mirror of
https://github.com/AntonioND/nitro-engine.git
synced 2025-06-18 16:45:33 -04:00
tools: Remove old tools and integrate md5_to_dsma
MD2 is a very awkward format to use, with few tools that can read it or write it. Relying on it is a bad idea. The replacements are: - OBJ: Extremely common, but it only supports static models. - MD5: Very easy to read, supports skeletal animations, reasonably good support in Blender. For more information, check its repository: https://github.com/AntonioND/dsma-library
This commit is contained in:
parent
395e3672e1
commit
6e344141e3
3
tools/md2_to_bin/.gitignore
vendored
3
tools/md2_to_bin/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
*.o
|
||||
md2_to_bin
|
||||
md2_to_bin.exe
|
@ -1,38 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Copyright (c) 2008-2011, 2019, Antonio Niño Díaz
|
||||
|
||||
# Variables
|
||||
|
||||
NAME := md2_to_bin
|
||||
# Either leave the extension empty or assign .exe to it for Windows
|
||||
EXT :=
|
||||
|
||||
CFLAGS := -g -Wall
|
||||
RM := rm -rf
|
||||
|
||||
# Rules to build the binary
|
||||
|
||||
all: $(NAME)$(EXT)
|
||||
|
||||
OBJS := md2_to_bin.o dlmaker.o
|
||||
|
||||
$(NAME)$(EXT): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
# Target used to remove all files generated by other Makefile targets
|
||||
|
||||
clean:
|
||||
$(RM) $(NAME) $(NAME).exe $(OBJS)
|
||||
|
||||
# Targets to cross-compile Windows binaries from Linux. Not used to compile
|
||||
# natively from Windows.
|
||||
|
||||
mingw32:
|
||||
make CC=i686-w64-mingw32-gcc EXT=.exe
|
||||
|
||||
mingw64:
|
||||
make CC=x86_64-w64-mingw32-gcc EXT=.exe
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
{-0.525731, 0.000000, 0.850651},
|
||||
{-0.442863, 0.238856, 0.864188},
|
||||
{-0.295242, 0.000000, 0.955423},
|
||||
{-0.309017, 0.500000, 0.809017},
|
||||
{-0.162460, 0.262866, 0.951056},
|
||||
{0.000000, 0.000000, 1.000000},
|
||||
{0.000000, 0.850651, 0.525731},
|
||||
{-0.147621, 0.716567, 0.681718},
|
||||
{0.147621, 0.716567, 0.681718},
|
||||
{0.000000, 0.525731, 0.850651},
|
||||
{0.309017, 0.500000, 0.809017},
|
||||
{0.525731, 0.000000, 0.850651},
|
||||
{0.295242, 0.000000, 0.955423},
|
||||
{0.442863, 0.238856, 0.864188},
|
||||
{0.162460, 0.262866, 0.951056},
|
||||
{-0.681718, 0.147621, 0.716567},
|
||||
{-0.809017, 0.309017, 0.500000},
|
||||
{-0.587785, 0.425325, 0.688191},
|
||||
{-0.850651, 0.525731, 0.000000},
|
||||
{-0.864188, 0.442863, 0.238856},
|
||||
{-0.716567, 0.681718, 0.147621},
|
||||
{-0.688191, 0.587785, 0.425325},
|
||||
{-0.500000, 0.809017, 0.309017},
|
||||
{-0.238856, 0.864188, 0.442863},
|
||||
{-0.425325, 0.688191, 0.587785},
|
||||
{-0.716567, 0.681718, -0.147621},
|
||||
{-0.500000, 0.809017, -0.309017},
|
||||
{-0.525731, 0.850651, 0.000000},
|
||||
{0.000000, 0.850651, -0.525731},
|
||||
{-0.238856, 0.864188, -0.442863},
|
||||
{0.000000, 0.955423, -0.295242},
|
||||
{-0.262866, 0.951056, -0.162460},
|
||||
{0.000000, 1.000000, 0.000000},
|
||||
{0.000000, 0.955423, 0.295242},
|
||||
{-0.262866, 0.951056, 0.162460},
|
||||
{0.238856, 0.864188, 0.442863},
|
||||
{0.262866, 0.951056, 0.162460},
|
||||
{0.500000, 0.809017, 0.309017},
|
||||
{0.238856, 0.864188, -0.442863},
|
||||
{0.262866, 0.951056, -0.162460},
|
||||
{0.500000, 0.809017, -0.309017},
|
||||
{0.850651, 0.525731, 0.000000},
|
||||
{0.716567, 0.681718, 0.147621},
|
||||
{0.716567, 0.681718, -0.147621},
|
||||
{0.525731, 0.850651, 0.000000},
|
||||
{0.425325, 0.688191, 0.587785},
|
||||
{0.864188, 0.442863, 0.238856},
|
||||
{0.688191, 0.587785, 0.425325},
|
||||
{0.809017, 0.309017, 0.500000},
|
||||
{0.681718, 0.147621, 0.716567},
|
||||
{0.587785, 0.425325, 0.688191},
|
||||
{0.955423, 0.295242, 0.000000},
|
||||
{1.000000, 0.000000, 0.000000},
|
||||
{0.951056, 0.162460, 0.262866},
|
||||
{0.850651, -0.525731, 0.000000},
|
||||
{0.955423, -0.295242, 0.000000},
|
||||
{0.864188, -0.442863, 0.238856},
|
||||
{0.951056, -0.162460, 0.262866},
|
||||
{0.809017, -0.309017, 0.500000},
|
||||
{0.681718, -0.147621, 0.716567},
|
||||
{0.850651, 0.000000, 0.525731},
|
||||
{0.864188, 0.442863, -0.238856},
|
||||
{0.809017, 0.309017, -0.500000},
|
||||
{0.951056, 0.162460, -0.262866},
|
||||
{0.525731, 0.000000, -0.850651},
|
||||
{0.681718, 0.147621, -0.716567},
|
||||
{0.681718, -0.147621, -0.716567},
|
||||
{0.850651, 0.000000, -0.525731},
|
||||
{0.809017, -0.309017, -0.500000},
|
||||
{0.864188, -0.442863, -0.238856},
|
||||
{0.951056, -0.162460, -0.262866},
|
||||
{0.147621, 0.716567, -0.681718},
|
||||
{0.309017, 0.500000, -0.809017},
|
||||
{0.425325, 0.688191, -0.587785},
|
||||
{0.442863, 0.238856, -0.864188},
|
||||
{0.587785, 0.425325, -0.688191},
|
||||
{0.688191, 0.587785, -0.425325},
|
||||
{-0.147621, 0.716567, -0.681718},
|
||||
{-0.309017, 0.500000, -0.809017},
|
||||
{0.000000, 0.525731, -0.850651},
|
||||
{-0.525731, 0.000000, -0.850651},
|
||||
{-0.442863, 0.238856, -0.864188},
|
||||
{-0.295242, 0.000000, -0.955423},
|
||||
{-0.162460, 0.262866, -0.951056},
|
||||
{0.000000, 0.000000, -1.000000},
|
||||
{0.295242, 0.000000, -0.955423},
|
||||
{0.162460, 0.262866, -0.951056},
|
||||
{-0.442863, -0.238856, -0.864188},
|
||||
{-0.309017, -0.500000, -0.809017},
|
||||
{-0.162460, -0.262866, -0.951056},
|
||||
{0.000000, -0.850651, -0.525731},
|
||||
{-0.147621, -0.716567, -0.681718},
|
||||
{0.147621, -0.716567, -0.681718},
|
||||
{0.000000, -0.525731, -0.850651},
|
||||
{0.309017, -0.500000, -0.809017},
|
||||
{0.442863, -0.238856, -0.864188},
|
||||
{0.162460, -0.262866, -0.951056},
|
||||
{0.238856, -0.864188, -0.442863},
|
||||
{0.500000, -0.809017, -0.309017},
|
||||
{0.425325, -0.688191, -0.587785},
|
||||
{0.716567, -0.681718, -0.147621},
|
||||
{0.688191, -0.587785, -0.425325},
|
||||
{0.587785, -0.425325, -0.688191},
|
||||
{0.000000, -0.955423, -0.295242},
|
||||
{0.000000, -1.000000, 0.000000},
|
||||
{0.262866, -0.951056, -0.162460},
|
||||
{0.000000, -0.850651, 0.525731},
|
||||
{0.000000, -0.955423, 0.295242},
|
||||
{0.238856, -0.864188, 0.442863},
|
||||
{0.262866, -0.951056, 0.162460},
|
||||
{0.500000, -0.809017, 0.309017},
|
||||
{0.716567, -0.681718, 0.147621},
|
||||
{0.525731, -0.850651, 0.000000},
|
||||
{-0.238856, -0.864188, -0.442863},
|
||||
{-0.500000, -0.809017, -0.309017},
|
||||
{-0.262866, -0.951056, -0.162460},
|
||||
{-0.850651, -0.525731, 0.000000},
|
||||
{-0.716567, -0.681718, -0.147621},
|
||||
{-0.716567, -0.681718, 0.147621},
|
||||
{-0.525731, -0.850651, 0.000000},
|
||||
{-0.500000, -0.809017, 0.309017},
|
||||
{-0.238856, -0.864188, 0.442863},
|
||||
{-0.262866, -0.951056, 0.162460},
|
||||
{-0.864188, -0.442863, 0.238856},
|
||||
{-0.809017, -0.309017, 0.500000},
|
||||
{-0.688191, -0.587785, 0.425325},
|
||||
{-0.681718, -0.147621, 0.716567},
|
||||
{-0.442863, -0.238856, 0.864188},
|
||||
{-0.587785, -0.425325, 0.688191},
|
||||
{-0.309017, -0.500000, 0.809017},
|
||||
{-0.147621, -0.716567, 0.681718},
|
||||
{-0.425325, -0.688191, 0.587785},
|
||||
{-0.162460, -0.262866, 0.951056},
|
||||
{0.442863, -0.238856, 0.864188},
|
||||
{0.162460, -0.262866, 0.951056},
|
||||
{0.309017, -0.500000, 0.809017},
|
||||
{0.147621, -0.716567, 0.681718},
|
||||
{0.000000, -0.525731, 0.850651},
|
||||
{0.425325, -0.688191, 0.587785},
|
||||
{0.587785, -0.425325, 0.688191},
|
||||
{0.688191, -0.587785, 0.425325},
|
||||
{-0.955423, 0.295242, 0.000000},
|
||||
{-0.951056, 0.162460, 0.262866},
|
||||
{-1.000000, 0.000000, 0.000000},
|
||||
{-0.850651, 0.000000, 0.525731},
|
||||
{-0.955423, -0.295242, 0.000000},
|
||||
{-0.951056, -0.162460, 0.262866},
|
||||
{-0.864188, 0.442863, -0.238856},
|
||||
{-0.951056, 0.162460, -0.262866},
|
||||
{-0.809017, 0.309017, -0.500000},
|
||||
{-0.864188, -0.442863, -0.238856},
|
||||
{-0.951056, -0.162460, -0.262866},
|
||||
{-0.809017, -0.309017, -0.500000},
|
||||
{-0.681718, 0.147621, -0.716567},
|
||||
{-0.681718, -0.147621, -0.716567},
|
||||
{-0.850651, 0.000000, -0.525731},
|
||||
{-0.688191, 0.587785, -0.425325},
|
||||
{-0.587785, 0.425325, -0.688191},
|
||||
{-0.425325, 0.688191, -0.587785},
|
||||
{-0.425325, -0.688191, -0.587785},
|
||||
{-0.587785, -0.425325, -0.688191},
|
||||
{-0.688191, -0.587785, -0.425325},
|
@ -1,103 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, 2022, Antonio Niño Díaz
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dlmaker.h"
|
||||
#include "nds.h"
|
||||
|
||||
uint32_t DLSize[1024];
|
||||
uint32_t *DLPointer[1024];
|
||||
int DLcount = 0;
|
||||
|
||||
uint8_t command[4];
|
||||
int commands;
|
||||
|
||||
uint32_t param[8];
|
||||
int params;
|
||||
|
||||
void NewDL(void)
|
||||
{
|
||||
memset(command, 0, sizeof(command));
|
||||
memset(param, 0, sizeof(param));
|
||||
commands = 0;
|
||||
params = 0;
|
||||
|
||||
DLPointer[DLcount] = (uint32_t *)malloc(DEFAULT_DL_SIZE);
|
||||
DLSize[DLcount] = 1;
|
||||
}
|
||||
|
||||
void NewCommandDL(int id)
|
||||
{
|
||||
command[commands] = id;
|
||||
commands++;
|
||||
|
||||
if (commands == 4) {
|
||||
// Save data to display list
|
||||
commands = 0;
|
||||
uint32_t temp;
|
||||
temp =
|
||||
COMMAND_PACK(command[0], command[1], command[2],
|
||||
command[3]);
|
||||
command[0] = command[1] = command[2] = command[3] = 0;
|
||||
uint32_t *pointer =
|
||||
&((DLPointer[DLcount])[DLSize[DLcount]]);
|
||||
// Save commands
|
||||
*pointer = temp;
|
||||
DLSize[DLcount]++;
|
||||
if (param > 0) {
|
||||
pointer = &((DLPointer[DLcount])[DLSize[DLcount]]);
|
||||
int a;
|
||||
for (a = 0; a < params; a++) {
|
||||
// Save commands
|
||||
pointer[a] = param[a];
|
||||
DLSize[DLcount]++;
|
||||
}
|
||||
}
|
||||
param[0] = param[1] = param[2] = param[3] = 0;
|
||||
param[4] = param[5] = param[6] = param[7] = 0;
|
||||
params = 0;
|
||||
|
||||
if (DLSize[DLcount] >= DEFAULT_DL_SIZE) {
|
||||
printf("\n\nDisplay list buffer overflow.\n\n");
|
||||
// TODO: Exit in a better way.
|
||||
while (1) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NewParamDL(uint32_t param_)
|
||||
{
|
||||
param[params] = param_;
|
||||
params++;
|
||||
}
|
||||
|
||||
void FinishDL(void)
|
||||
{
|
||||
if (commands > 0) {
|
||||
// Add NOP commands to fill packed commands
|
||||
while (1) {
|
||||
NewCommandDL(ID_NOP);
|
||||
if (commands == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save DL real size in 4 bytes packs
|
||||
*DLPointer[DLcount] = DLSize[DLcount] - 1;
|
||||
DLcount++;
|
||||
}
|
||||
|
||||
int GetDLSize(int num)
|
||||
{
|
||||
return DLSize[num];
|
||||
}
|
||||
|
||||
uint32_t *GetDLPointer(int num)
|
||||
{
|
||||
return DLPointer[num];
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, 2022, Antonio Niño Díaz
|
||||
|
||||
#ifndef DLMAKER_H__
|
||||
#define DLMAKER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEFAULT_DL_SIZE (512 * 1024)
|
||||
|
||||
void NewDL(void);
|
||||
void NewCommandDL(int id);
|
||||
void NewParamDL(uint32_t param_);
|
||||
void FinishDL(void);
|
||||
|
||||
int GetDLSize(int num);
|
||||
uint32_t *GetDLPointer(int num);
|
||||
|
||||
#endif // DLMAKER_H__
|
@ -1,359 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, Antonio Niño Díaz
|
||||
|
||||
// I used the information in this web to make the converter:
|
||||
// http://tfc.duke.free.fr/coding/md2-specs-en.html
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nds.h"
|
||||
#include "dlmaker.h"
|
||||
|
||||
#define MAX_PATHLEN 1024
|
||||
|
||||
#define absf(x) (((x) > 0) ? (x) : -(x))
|
||||
|
||||
typedef float vec3_t[3];
|
||||
|
||||
typedef struct {
|
||||
int32_t ident;
|
||||
int32_t version;
|
||||
|
||||
int32_t skinwidth;
|
||||
int32_t skinheight;
|
||||
int32_t framesize;
|
||||
int32_t num_skins;
|
||||
int32_t num_vertices;
|
||||
int32_t num_st;
|
||||
int32_t num_tris;
|
||||
int32_t num_glcmds;
|
||||
int32_t num_frames;
|
||||
int32_t offset_skins;
|
||||
int32_t offset_st;
|
||||
int32_t offset_tris;
|
||||
int32_t offset_frames;
|
||||
int32_t offset_glcmds;
|
||||
int32_t offset_end;
|
||||
} md2_header_t;
|
||||
|
||||
typedef struct {
|
||||
int16_t s;
|
||||
int16_t t;
|
||||
} md2_texCoord_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t vertex[3];
|
||||
uint16_t st[3];
|
||||
} md2_triangle_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t v[3];
|
||||
uint8_t normalIndex;
|
||||
} md2_vertex_t;
|
||||
|
||||
typedef struct {
|
||||
vec3_t scale;
|
||||
vec3_t translate;
|
||||
char name[16];
|
||||
md2_vertex_t *verts;
|
||||
} md2_frame_t;
|
||||
|
||||
typedef struct {
|
||||
float s;
|
||||
float t;
|
||||
int32_t index;
|
||||
} md2_glcmd_t;
|
||||
|
||||
float anorms[162][3] = {
|
||||
#include "anorms.h"
|
||||
};
|
||||
|
||||
void PrintUsage(void)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" md2_to_bin [input.md2] [output.bin] <[float scale]>\n");
|
||||
printf(" <[float translate x] [float translate x] [float translate x]>\n");
|
||||
}
|
||||
|
||||
int16_t floattovtx10(float n)
|
||||
{
|
||||
int16_t value = ((int16_t) ((n) * (1 << 6))) & 0x3FF;
|
||||
if (n < 0)
|
||||
value |= (1 << 10);
|
||||
else
|
||||
value &= 0x1FF;
|
||||
return value;
|
||||
}
|
||||
|
||||
int IsValidSize(int size)
|
||||
{
|
||||
return (size == 8 || size == 16 || size == 32 || size == 64 ||
|
||||
size == 128 || size == 256 || size == 512 || size == 1024);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("md2_to_bin v2.1\n");
|
||||
printf("\n");
|
||||
printf("Copyright (c) 2008-2011, 2019 Antonio Nino Diaz\n");
|
||||
printf("\n");
|
||||
|
||||
// Default values
|
||||
float global_scale = 1;
|
||||
float global_translation[3] = { 0, 0, 0 };
|
||||
|
||||
switch (argc) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
// Not enough arguments
|
||||
PrintUsage();
|
||||
return -1;
|
||||
case 3:
|
||||
// Use default modifications
|
||||
break;
|
||||
case 4:
|
||||
// Use default translation and custom scale
|
||||
global_scale = atof(argv[3]);
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
// Custom translation, not enough
|
||||
printf("You must set 3 coordinates for translation, not less.");
|
||||
PrintUsage();
|
||||
return -1;
|
||||
case 7:
|
||||
// Custom translation + scale
|
||||
global_scale = atof(argv[3]);
|
||||
global_translation[0] = atof(argv[4]);
|
||||
global_translation[1] = atof(argv[5]);
|
||||
global_translation[2] = atof(argv[6]);
|
||||
break;
|
||||
default:
|
||||
// Too many arguments
|
||||
PrintUsage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
char inputfilepath[MAX_PATHLEN];
|
||||
char outputfilepath[MAX_PATHLEN];
|
||||
|
||||
strcpy(inputfilepath, argv[1]);
|
||||
strcpy(outputfilepath, argv[2]);
|
||||
|
||||
if (global_scale == 0) {
|
||||
printf("\nScale can't be 0!!");
|
||||
PrintUsage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *md2data;
|
||||
|
||||
printf("\n");
|
||||
printf("Scale: %f\n", global_scale);
|
||||
printf("Translate: %f, %f, %f\n", global_translation[0],
|
||||
global_translation[1], global_translation[2]);
|
||||
|
||||
printf("\n");
|
||||
printf("Loading MD2 model...\n");
|
||||
|
||||
FILE *datafile = fopen(inputfilepath, "r");
|
||||
if (datafile != NULL)
|
||||
{
|
||||
fseek(datafile, 0, SEEK_END);
|
||||
long int size_ = ftell(datafile);
|
||||
rewind(datafile);
|
||||
md2data = (char *)malloc(sizeof(char) * size_);
|
||||
fread(md2data, 1, size_, datafile);
|
||||
fclose(datafile);
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose(datafile);
|
||||
printf("\n\nCouldn't open %s!!\n\n", inputfilepath);
|
||||
PrintUsage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
md2_header_t *header = (md2_header_t *) md2data;
|
||||
|
||||
if ((header->ident != 844121161) || (header->version != 8))
|
||||
{
|
||||
printf("\n\nWrong file type or version!!\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int t_w = header->skinwidth, t_h = header->skinheight;
|
||||
|
||||
if (t_w > 1024 || t_h > 1024)
|
||||
{
|
||||
printf("\n\nTexture too big!!\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!IsValidSize(t_w) || !IsValidSize(t_h))
|
||||
{
|
||||
printf("\nWrong texture size. It must be a power of 2.\n");
|
||||
printf("\nAlthough the model uses an invalid texture size, it will be converted.\n");
|
||||
printf("\nResize the texture to nearest valid size.\n\n");
|
||||
}
|
||||
|
||||
while (!IsValidSize(t_w))
|
||||
t_w++;
|
||||
|
||||
while (!IsValidSize(t_h))
|
||||
t_h++;
|
||||
|
||||
int num_tris = header->num_tris;
|
||||
|
||||
md2_frame_t *frame = NULL;
|
||||
md2_texCoord_t *texcoord = (md2_texCoord_t *)((uintptr_t) header->offset_st
|
||||
+ (uintptr_t)header);
|
||||
md2_triangle_t *triangle = (md2_triangle_t *)((uintptr_t) header->offset_tris
|
||||
+ (uintptr_t)header);
|
||||
|
||||
// Current vertex
|
||||
md2_vertex_t *vtx;
|
||||
|
||||
printf("\nMD2 texture size: %dx%d", t_w, t_h);
|
||||
|
||||
printf("\nCreating display list...\n");
|
||||
|
||||
float bigvalue = 0;
|
||||
|
||||
// Everything ready, let's "draw" the display list
|
||||
frame = (md2_frame_t *)((uintptr_t)header->offset_frames + (uintptr_t)header);
|
||||
|
||||
NewDL();
|
||||
|
||||
// Send GL_TRIANGLES command
|
||||
NewParamDL(0);
|
||||
NewCommandDL(ID_BEGIN);
|
||||
|
||||
// Keep track of the last command to avoid useless repetition
|
||||
int olds_ = -1, oldt_ = -1;
|
||||
int oldnormindex = -1;
|
||||
|
||||
int vtxcount = 0;
|
||||
|
||||
for (int t = 0; t < num_tris; t++)
|
||||
{
|
||||
for (int v = 0; v < 3; v++)
|
||||
{
|
||||
vtx = (md2_vertex_t *) ((uintptr_t) (&(frame->verts)));
|
||||
vtx = &vtx[triangle[t].vertex[v]];
|
||||
|
||||
// Texture coordinates
|
||||
int16_t s_ = texcoord[triangle[t].st[v]].s;
|
||||
int16_t t_ = texcoord[triangle[t].st[v]].t;
|
||||
if (olds_ != s_ || oldt_ != t_) {
|
||||
olds_ = s_;
|
||||
oldt_ = t_;
|
||||
|
||||
// This is used to scale UVs if using a texture
|
||||
// size unsupported by DS
|
||||
s_ = (int16_t)((float)(s_ * t_w) / (float)header->skinwidth);
|
||||
t_ = (int16_t)((float)(t_ * t_h) / (float)header->skinheight);
|
||||
NewParamDL(TEXTURE_PACK(s_ << 4, t_ << 4)); // (t_h-t_)<<4));
|
||||
NewCommandDL(ID_TEX_COORD);
|
||||
}
|
||||
|
||||
// Normal
|
||||
float norm[3];
|
||||
if (oldnormindex != vtx->normalIndex)
|
||||
{
|
||||
oldnormindex = vtx->normalIndex;
|
||||
for (int b = 0; b < 3; b++)
|
||||
norm[b] = anorms[vtx->normalIndex][b];
|
||||
NewParamDL(NORMAL_PACK(floattov10(norm[0]), floattov10(norm[1]),
|
||||
floattov10(norm[2])));
|
||||
NewCommandDL(ID_NORMAL);
|
||||
}
|
||||
|
||||
// Vertex
|
||||
float _v[3];
|
||||
int vtx10 = false;
|
||||
for (int a = 0; a < 3; a++)
|
||||
{
|
||||
vtxcount++;
|
||||
_v[a] = ((float)frame->scale[a] * (float)(vtx->v[a]))
|
||||
+ (float)frame->translate[a];
|
||||
_v[a] += global_translation[a];
|
||||
_v[a] *= global_scale;
|
||||
|
||||
// 7.9997 for VTX_16, 7.98 for VTX_10
|
||||
if ((absf(_v[a]) > 7.9997f) && (absf(bigvalue) < absf(_v[a])))
|
||||
bigvalue = _v[a];
|
||||
|
||||
// Test to see if it is more accurate to use
|
||||
// VTX_10 or VTX_16
|
||||
if (absf(_v[a]) < 7.98f)
|
||||
{
|
||||
float aux = 1.0f / 64.0f;
|
||||
float tempvtx = _v[a] / aux;
|
||||
tempvtx = absf(tempvtx - (float)(int)tempvtx);
|
||||
// Arbitrary threshold
|
||||
aux = (float)0.2;
|
||||
if ((tempvtx < aux) || (tempvtx > (1.0f - aux)))
|
||||
vtx10 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (vtx10)
|
||||
{
|
||||
NewParamDL(VERTEX_10_PACK(floattovtx10(_v[0]),
|
||||
floattovtx10(_v[2]), floattovtx10(_v[1])));
|
||||
NewCommandDL(ID_VERTEX10);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewParamDL((floattov16(_v[2]) << 16) | (floattov16(_v[0]) & 0xFFFF));
|
||||
NewParamDL((floattov16(_v[1]) & 0xFFFF));
|
||||
NewCommandDL(ID_VERTEX16);
|
||||
}
|
||||
}
|
||||
}
|
||||
FinishDL();
|
||||
|
||||
if (absf(bigvalue) > 0)
|
||||
{
|
||||
printf("\nModel too big for DS! Scale it down.\n");
|
||||
printf("\nDS max. allowed value: +/-7,9997\nModel max. detected value: %f\n\n",
|
||||
bigvalue);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vtxcount > 6144)
|
||||
{
|
||||
printf("\nModel has too many vertices!\n");
|
||||
printf("\nDS can only render 6144 vertices per frame.\nYour model has %d vertices.\n",
|
||||
vtxcount);
|
||||
}
|
||||
|
||||
printf("\nCreating BIN file...\n");
|
||||
|
||||
// Now, let's save them into a BIN file.
|
||||
FILE *file = fopen(outputfilepath, "wb+");
|
||||
if (file == NULL)
|
||||
{
|
||||
printf("\nCouldn't create %s file!", outputfilepath);
|
||||
return -1;
|
||||
}
|
||||
fwrite((int *)GetDLPointer(0), GetDLSize(0) * sizeof(unsigned int), 1, file);
|
||||
fclose(file);
|
||||
|
||||
FILE *test = fopen(outputfilepath, "rb");
|
||||
fseek(test, 0, SEEK_END);
|
||||
long int size = ftell(test);
|
||||
fclose(test);
|
||||
printf("\nBIN file size: %ld bytes\n", size);
|
||||
|
||||
printf("\nReady!\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, Antonio Niño Díaz
|
||||
|
||||
#ifndef NDS_H__
|
||||
#define NDS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//From libnds/gbatek
|
||||
|
||||
static inline int32_t floattof32(float n)
|
||||
{
|
||||
return (int32_t)(n * (1 << 12));
|
||||
}
|
||||
|
||||
static inline int16_t floattov16(float n)
|
||||
{
|
||||
return (int16_t)(n * (1 << 12));
|
||||
}
|
||||
|
||||
static inline int16_t floattov10(float n)
|
||||
{
|
||||
if (n > 0.998)
|
||||
return 0x1FF;
|
||||
if (n < -0.998)
|
||||
return 0x3FF;
|
||||
return (int16_t)(n * (1 << 9));
|
||||
}
|
||||
|
||||
#define VERTEX_10_PACK(x,y,z) \
|
||||
(((x) & 0x3FF) | (((y) & 0x3FF) << 10) | (((z) & 0x3FF) << 20))
|
||||
|
||||
#define NORMAL_PACK(x,y,z) \
|
||||
(((x) & 0x3FF) | (((y) & 0x3FF) << 10) | ((z) << 20))
|
||||
|
||||
#define TEXTURE_PACK(u,v) \
|
||||
(((u) & 0xFFFF) | ((v) << 16))
|
||||
|
||||
#define COMMAND_PACK(c1, c2, c3, c4) \
|
||||
(((c4) << 24) | ((c3) << 16) | ((c2) << 8) | (c1))
|
||||
|
||||
#define ID_NOP 0x00
|
||||
#define ID_VERTEX16 0x23
|
||||
#define ID_VERTEX10 0x24
|
||||
#define ID_TEX_COORD 0x22
|
||||
#define ID_NORMAL 0x21
|
||||
#define ID_BEGIN 0x40
|
||||
|
||||
#endif // NDS_H__
|
3
tools/md2_to_nea/.gitignore
vendored
3
tools/md2_to_nea/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
*.o
|
||||
md2_to_nea
|
||||
md2_to_nea.exe
|
@ -1,38 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Copyright (c) 2008-2011, 2019, Antonio Niño Díaz
|
||||
|
||||
# Variables
|
||||
|
||||
NAME := md2_to_nea
|
||||
# Either leave the extension empty or assign .exe to it for Windows
|
||||
EXT :=
|
||||
|
||||
CFLAGS := -g -Wall
|
||||
RM := rm -rf
|
||||
|
||||
# Rules to build the binary
|
||||
|
||||
all: $(NAME)$(EXT)
|
||||
|
||||
OBJS := md2_to_nea.o dynamic_list.o framemaker.o
|
||||
|
||||
$(NAME)$(EXT): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
# Target used to remove all files generated by other Makefile targets
|
||||
|
||||
clean:
|
||||
$(RM) $(NAME) $(NAME).exe $(OBJS)
|
||||
|
||||
# Targets to cross-compile Windows binaries from Linux. Not used to compile
|
||||
# natively from Windows.
|
||||
|
||||
mingw32:
|
||||
make CC=i686-w64-mingw32-gcc EXT=.exe
|
||||
|
||||
mingw64:
|
||||
make CC=x86_64-w64-mingw32-gcc EXT=.exe
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
{-0.525731, 0.000000, 0.850651},
|
||||
{-0.442863, 0.238856, 0.864188},
|
||||
{-0.295242, 0.000000, 0.955423},
|
||||
{-0.309017, 0.500000, 0.809017},
|
||||
{-0.162460, 0.262866, 0.951056},
|
||||
{0.000000, 0.000000, 1.000000},
|
||||
{0.000000, 0.850651, 0.525731},
|
||||
{-0.147621, 0.716567, 0.681718},
|
||||
{0.147621, 0.716567, 0.681718},
|
||||
{0.000000, 0.525731, 0.850651},
|
||||
{0.309017, 0.500000, 0.809017},
|
||||
{0.525731, 0.000000, 0.850651},
|
||||
{0.295242, 0.000000, 0.955423},
|
||||
{0.442863, 0.238856, 0.864188},
|
||||
{0.162460, 0.262866, 0.951056},
|
||||
{-0.681718, 0.147621, 0.716567},
|
||||
{-0.809017, 0.309017, 0.500000},
|
||||
{-0.587785, 0.425325, 0.688191},
|
||||
{-0.850651, 0.525731, 0.000000},
|
||||
{-0.864188, 0.442863, 0.238856},
|
||||
{-0.716567, 0.681718, 0.147621},
|
||||
{-0.688191, 0.587785, 0.425325},
|
||||
{-0.500000, 0.809017, 0.309017},
|
||||
{-0.238856, 0.864188, 0.442863},
|
||||
{-0.425325, 0.688191, 0.587785},
|
||||
{-0.716567, 0.681718, -0.147621},
|
||||
{-0.500000, 0.809017, -0.309017},
|
||||
{-0.525731, 0.850651, 0.000000},
|
||||
{0.000000, 0.850651, -0.525731},
|
||||
{-0.238856, 0.864188, -0.442863},
|
||||
{0.000000, 0.955423, -0.295242},
|
||||
{-0.262866, 0.951056, -0.162460},
|
||||
{0.000000, 1.000000, 0.000000},
|
||||
{0.000000, 0.955423, 0.295242},
|
||||
{-0.262866, 0.951056, 0.162460},
|
||||
{0.238856, 0.864188, 0.442863},
|
||||
{0.262866, 0.951056, 0.162460},
|
||||
{0.500000, 0.809017, 0.309017},
|
||||
{0.238856, 0.864188, -0.442863},
|
||||
{0.262866, 0.951056, -0.162460},
|
||||
{0.500000, 0.809017, -0.309017},
|
||||
{0.850651, 0.525731, 0.000000},
|
||||
{0.716567, 0.681718, 0.147621},
|
||||
{0.716567, 0.681718, -0.147621},
|
||||
{0.525731, 0.850651, 0.000000},
|
||||
{0.425325, 0.688191, 0.587785},
|
||||
{0.864188, 0.442863, 0.238856},
|
||||
{0.688191, 0.587785, 0.425325},
|
||||
{0.809017, 0.309017, 0.500000},
|
||||
{0.681718, 0.147621, 0.716567},
|
||||
{0.587785, 0.425325, 0.688191},
|
||||
{0.955423, 0.295242, 0.000000},
|
||||
{1.000000, 0.000000, 0.000000},
|
||||
{0.951056, 0.162460, 0.262866},
|
||||
{0.850651, -0.525731, 0.000000},
|
||||
{0.955423, -0.295242, 0.000000},
|
||||
{0.864188, -0.442863, 0.238856},
|
||||
{0.951056, -0.162460, 0.262866},
|
||||
{0.809017, -0.309017, 0.500000},
|
||||
{0.681718, -0.147621, 0.716567},
|
||||
{0.850651, 0.000000, 0.525731},
|
||||
{0.864188, 0.442863, -0.238856},
|
||||
{0.809017, 0.309017, -0.500000},
|
||||
{0.951056, 0.162460, -0.262866},
|
||||
{0.525731, 0.000000, -0.850651},
|
||||
{0.681718, 0.147621, -0.716567},
|
||||
{0.681718, -0.147621, -0.716567},
|
||||
{0.850651, 0.000000, -0.525731},
|
||||
{0.809017, -0.309017, -0.500000},
|
||||
{0.864188, -0.442863, -0.238856},
|
||||
{0.951056, -0.162460, -0.262866},
|
||||
{0.147621, 0.716567, -0.681718},
|
||||
{0.309017, 0.500000, -0.809017},
|
||||
{0.425325, 0.688191, -0.587785},
|
||||
{0.442863, 0.238856, -0.864188},
|
||||
{0.587785, 0.425325, -0.688191},
|
||||
{0.688191, 0.587785, -0.425325},
|
||||
{-0.147621, 0.716567, -0.681718},
|
||||
{-0.309017, 0.500000, -0.809017},
|
||||
{0.000000, 0.525731, -0.850651},
|
||||
{-0.525731, 0.000000, -0.850651},
|
||||
{-0.442863, 0.238856, -0.864188},
|
||||
{-0.295242, 0.000000, -0.955423},
|
||||
{-0.162460, 0.262866, -0.951056},
|
||||
{0.000000, 0.000000, -1.000000},
|
||||
{0.295242, 0.000000, -0.955423},
|
||||
{0.162460, 0.262866, -0.951056},
|
||||
{-0.442863, -0.238856, -0.864188},
|
||||
{-0.309017, -0.500000, -0.809017},
|
||||
{-0.162460, -0.262866, -0.951056},
|
||||
{0.000000, -0.850651, -0.525731},
|
||||
{-0.147621, -0.716567, -0.681718},
|
||||
{0.147621, -0.716567, -0.681718},
|
||||
{0.000000, -0.525731, -0.850651},
|
||||
{0.309017, -0.500000, -0.809017},
|
||||
{0.442863, -0.238856, -0.864188},
|
||||
{0.162460, -0.262866, -0.951056},
|
||||
{0.238856, -0.864188, -0.442863},
|
||||
{0.500000, -0.809017, -0.309017},
|
||||
{0.425325, -0.688191, -0.587785},
|
||||
{0.716567, -0.681718, -0.147621},
|
||||
{0.688191, -0.587785, -0.425325},
|
||||
{0.587785, -0.425325, -0.688191},
|
||||
{0.000000, -0.955423, -0.295242},
|
||||
{0.000000, -1.000000, 0.000000},
|
||||
{0.262866, -0.951056, -0.162460},
|
||||
{0.000000, -0.850651, 0.525731},
|
||||
{0.000000, -0.955423, 0.295242},
|
||||
{0.238856, -0.864188, 0.442863},
|
||||
{0.262866, -0.951056, 0.162460},
|
||||
{0.500000, -0.809017, 0.309017},
|
||||
{0.716567, -0.681718, 0.147621},
|
||||
{0.525731, -0.850651, 0.000000},
|
||||
{-0.238856, -0.864188, -0.442863},
|
||||
{-0.500000, -0.809017, -0.309017},
|
||||
{-0.262866, -0.951056, -0.162460},
|
||||
{-0.850651, -0.525731, 0.000000},
|
||||
{-0.716567, -0.681718, -0.147621},
|
||||
{-0.716567, -0.681718, 0.147621},
|
||||
{-0.525731, -0.850651, 0.000000},
|
||||
{-0.500000, -0.809017, 0.309017},
|
||||
{-0.238856, -0.864188, 0.442863},
|
||||
{-0.262866, -0.951056, 0.162460},
|
||||
{-0.864188, -0.442863, 0.238856},
|
||||
{-0.809017, -0.309017, 0.500000},
|
||||
{-0.688191, -0.587785, 0.425325},
|
||||
{-0.681718, -0.147621, 0.716567},
|
||||
{-0.442863, -0.238856, 0.864188},
|
||||
{-0.587785, -0.425325, 0.688191},
|
||||
{-0.309017, -0.500000, 0.809017},
|
||||
{-0.147621, -0.716567, 0.681718},
|
||||
{-0.425325, -0.688191, 0.587785},
|
||||
{-0.162460, -0.262866, 0.951056},
|
||||
{0.442863, -0.238856, 0.864188},
|
||||
{0.162460, -0.262866, 0.951056},
|
||||
{0.309017, -0.500000, 0.809017},
|
||||
{0.147621, -0.716567, 0.681718},
|
||||
{0.000000, -0.525731, 0.850651},
|
||||
{0.425325, -0.688191, 0.587785},
|
||||
{0.587785, -0.425325, 0.688191},
|
||||
{0.688191, -0.587785, 0.425325},
|
||||
{-0.955423, 0.295242, 0.000000},
|
||||
{-0.951056, 0.162460, 0.262866},
|
||||
{-1.000000, 0.000000, 0.000000},
|
||||
{-0.850651, 0.000000, 0.525731},
|
||||
{-0.955423, -0.295242, 0.000000},
|
||||
{-0.951056, -0.162460, 0.262866},
|
||||
{-0.864188, 0.442863, -0.238856},
|
||||
{-0.951056, 0.162460, -0.262866},
|
||||
{-0.809017, 0.309017, -0.500000},
|
||||
{-0.864188, -0.442863, -0.238856},
|
||||
{-0.951056, -0.162460, -0.262866},
|
||||
{-0.809017, -0.309017, -0.500000},
|
||||
{-0.681718, 0.147621, -0.716567},
|
||||
{-0.681718, -0.147621, -0.716567},
|
||||
{-0.850651, 0.000000, -0.525731},
|
||||
{-0.688191, 0.587785, -0.425325},
|
||||
{-0.587785, 0.425325, -0.688191},
|
||||
{-0.425325, 0.688191, -0.587785},
|
||||
{-0.425325, -0.688191, -0.587785},
|
||||
{-0.587785, -0.425325, -0.688191},
|
||||
{-0.688191, -0.587785, -0.425325},
|
@ -1,122 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, 2022 Antonio Niño Díaz
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dynamic_list.h"
|
||||
|
||||
void DynamicListNew(DynamicList **list)
|
||||
{
|
||||
*list = (DynamicList *)malloc(sizeof(DynamicList));
|
||||
(*list)->previous = NULL;
|
||||
(*list)->data = 0;
|
||||
(*list)->next = NULL;
|
||||
}
|
||||
|
||||
int DynamicListNewElement(DynamicList *list)
|
||||
{
|
||||
DynamicList *element_search = list;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (element_search->next == NULL)
|
||||
break;
|
||||
|
||||
element_search = (DynamicList *)element_search->next;
|
||||
}
|
||||
|
||||
DynamicList *new_element;
|
||||
new_element = (DynamicList *)malloc(sizeof(DynamicList));
|
||||
element_search->next = new_element;
|
||||
|
||||
new_element->previous = element_search;
|
||||
new_element->data = 0;
|
||||
new_element->next = NULL;
|
||||
|
||||
return DynamicListLenghtGet(list) - 1;
|
||||
}
|
||||
|
||||
void DynamicListElementSet(DynamicList *list, int index, uint64_t value)
|
||||
{
|
||||
int count = 0;
|
||||
DynamicList *element_search = list;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (count == index)
|
||||
break;
|
||||
element_search = (DynamicList *)element_search->next;
|
||||
count++;
|
||||
}
|
||||
|
||||
element_search->data = value;
|
||||
}
|
||||
|
||||
uint64_t DynamicListElementGet(DynamicList *list, int index)
|
||||
{
|
||||
int count = 0;
|
||||
DynamicList *element_search = list;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (count == index)
|
||||
break;
|
||||
element_search = (DynamicList *)element_search->next;
|
||||
count++;
|
||||
}
|
||||
|
||||
return element_search->data;
|
||||
}
|
||||
|
||||
int DynamicListLenghtGet(DynamicList *list)
|
||||
{
|
||||
int count = 0;
|
||||
DynamicList *element_search = list;
|
||||
|
||||
while (1)
|
||||
{
|
||||
count++;
|
||||
element_search = (DynamicList *)element_search->next;
|
||||
if (element_search == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int DynamicListGetIndex(DynamicList *list, uint64_t value)
|
||||
{
|
||||
int count = 0;
|
||||
DynamicList *element_search = list;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (element_search->data == value)
|
||||
return count;
|
||||
count++;
|
||||
element_search = (DynamicList *)element_search->next;
|
||||
if (element_search == NULL)
|
||||
break; //No luck...
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void DynamicListDelete(DynamicList *list)
|
||||
{
|
||||
DynamicList *element_search;
|
||||
DynamicList *element_current = list;
|
||||
|
||||
while (1)
|
||||
{
|
||||
element_search = (DynamicList *)element_current->next;
|
||||
free(element_current);
|
||||
if (element_search == NULL)
|
||||
break;
|
||||
element_current = element_search;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, 2022, Antonio Niño Díaz
|
||||
|
||||
#ifndef _DYNAMICLIST_H_
|
||||
#define _DYNAMICLIST_H_
|
||||
|
||||
typedef struct {
|
||||
void *previous;
|
||||
uint64_t data;
|
||||
void *next;
|
||||
} DynamicList;
|
||||
|
||||
void DynamicListNew(DynamicList **list);
|
||||
int DynamicListNewElement(DynamicList *list);
|
||||
void DynamicListElementSet(DynamicList *list, int index, uint64_t value);
|
||||
uint64_t DynamicListElementGet(DynamicList *list, int index);
|
||||
int DynamicListLenghtGet(DynamicList *list);
|
||||
int DynamicListGetIndex(DynamicList *list, uint64_t value);
|
||||
void DynamicListDelete(DynamicList *list);
|
||||
|
||||
#endif
|
@ -1,150 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, 2022, Antonio Niño Díaz
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dynamic_list.h"
|
||||
#include "framemaker.h"
|
||||
|
||||
DynamicList *vertices_list;
|
||||
DynamicList *normal_list;
|
||||
DynamicList *texcoords_list;
|
||||
|
||||
uint32_t FrameSize[1024];
|
||||
uint16_t *FramePointer[1024];
|
||||
int framecount = -1; // ------.
|
||||
// |
|
||||
void NewFrame(void) // |
|
||||
{ // |
|
||||
framecount++; // <----' For first frame
|
||||
|
||||
FramePointer[framecount] = (uint16_t *)malloc(DEFAULT_FRAME_SIZE);
|
||||
if (FramePointer[framecount] == NULL)
|
||||
{
|
||||
printf("\n\nCouldn't allocate memory.\n\n");
|
||||
while (1); //TODO: Exit in a better way.
|
||||
}
|
||||
FrameSize[framecount] = 0;
|
||||
}
|
||||
|
||||
void NewFrameData(uint16_t data)
|
||||
{
|
||||
(FramePointer[framecount])[FrameSize[framecount]] = data;
|
||||
FrameSize[framecount]++;
|
||||
|
||||
if (FrameSize[framecount] >= DEFAULT_FRAME_SIZE)
|
||||
{
|
||||
printf("\n\nFrame commands buffer overflow.\n\n");
|
||||
while (1); //TODO: Exit in a better way.
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetFrameSize(int num)
|
||||
{
|
||||
return FrameSize[num];
|
||||
}
|
||||
|
||||
uint16_t *GetFramePointer(int num)
|
||||
{
|
||||
return FramePointer[num];
|
||||
}
|
||||
|
||||
void InitDynamicLists(void)
|
||||
{
|
||||
DynamicListNew(&vertices_list);
|
||||
DynamicListNew(&normal_list);
|
||||
DynamicListNew(&texcoords_list);
|
||||
}
|
||||
|
||||
void EndDynamicLists(void)
|
||||
{
|
||||
DynamicListDelete(vertices_list);
|
||||
DynamicListDelete(normal_list);
|
||||
DynamicListDelete(texcoords_list);
|
||||
}
|
||||
|
||||
int AddVertex(uint16_t x, uint16_t y, uint16_t z)
|
||||
{
|
||||
int64_t value = ((int64_t)x << 32) | ((int64_t)y << 16) | (int64_t)z;
|
||||
|
||||
int result = DynamicListGetIndex(vertices_list, value);
|
||||
if (result != -1)
|
||||
return result;
|
||||
|
||||
int index = DynamicListNewElement(vertices_list);
|
||||
DynamicListElementSet(vertices_list, index, value);
|
||||
return index;
|
||||
}
|
||||
|
||||
void GetVertex(int index, uint16_t *x, uint16_t *y, uint16_t *z)
|
||||
{
|
||||
uint64_t temp = DynamicListElementGet(vertices_list, index);
|
||||
*x = (uint16_t)((temp >> 32) & 0xFFFF);
|
||||
*y = (uint16_t)((temp >> 16) & 0xFFFF);
|
||||
*z = (uint16_t)(temp & 0xFFFF);
|
||||
}
|
||||
|
||||
int AddNormal(uint16_t x, uint16_t y, uint16_t z)
|
||||
{
|
||||
uint64_t value = ((uint64_t)x << 32) | ((uint64_t)y << 16) | (uint64_t)z;
|
||||
|
||||
int result = DynamicListGetIndex(normal_list, value);
|
||||
|
||||
if (result != -1)
|
||||
return result;
|
||||
|
||||
int index = DynamicListNewElement(normal_list);
|
||||
|
||||
DynamicListElementSet(normal_list, index, value);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void GetNormal(int index, uint16_t *x, uint16_t *y, uint16_t *z)
|
||||
{
|
||||
uint64_t temp = DynamicListElementGet(normal_list, index);
|
||||
*x = (uint16_t)((temp >> 32) & 0xFFFF);
|
||||
*y = (uint16_t)((temp >> 16) & 0xFFFF);
|
||||
*z = (uint16_t)(temp & 0xFFFF);
|
||||
}
|
||||
|
||||
int AddTexCoord(uint16_t u, uint16_t v)
|
||||
{
|
||||
int32_t value = ((int32_t)u << 16) | (int32_t)v;
|
||||
|
||||
int result = DynamicListGetIndex(texcoords_list, value);
|
||||
|
||||
if (result != -1)
|
||||
return result;
|
||||
|
||||
int index = DynamicListNewElement(texcoords_list);
|
||||
|
||||
DynamicListElementSet(texcoords_list, index, value);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void GetTexCoord(int index, uint16_t *u, uint16_t *v)
|
||||
{
|
||||
uint32_t temp = DynamicListElementGet(texcoords_list, index);
|
||||
*u = (uint16_t)((temp >> 16) & 0xFFFF);
|
||||
*v = (uint16_t)(temp & 0xFFFF);
|
||||
}
|
||||
|
||||
int GetVerticesNumber(void)
|
||||
{
|
||||
return DynamicListLenghtGet(vertices_list);
|
||||
}
|
||||
|
||||
int GetNormalNumber(void)
|
||||
{
|
||||
return DynamicListLenghtGet(normal_list);
|
||||
}
|
||||
|
||||
int GetTexcoordsNumber(void)
|
||||
{
|
||||
return DynamicListLenghtGet(texcoords_list);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, 2022, Antonio Niño Díaz
|
||||
|
||||
#ifndef FRAMEMAKER_H__
|
||||
#define FRAMEMAKER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEFAULT_FRAME_SIZE (512 * 1024)
|
||||
|
||||
void NewFrame(void);
|
||||
void NewFrameData(uint16_t data);
|
||||
|
||||
unsigned int GetFrameSize(int num);
|
||||
uint16_t *GetFramePointer(int num);
|
||||
|
||||
void InitDynamicLists(void);
|
||||
void EndDynamicLists(void);
|
||||
|
||||
int AddVertex(uint16_t x, uint16_t y, uint16_t z);
|
||||
int AddNormal(uint16_t x, uint16_t y, uint16_t z);
|
||||
int AddTexCoord(uint16_t u, uint16_t v);
|
||||
|
||||
int GetVerticesNumber(void);
|
||||
int GetNormalNumber(void);
|
||||
int GetTexcoordsNumber(void);
|
||||
|
||||
void GetVertex(int index, uint16_t *x, uint16_t *y, uint16_t *z);
|
||||
void GetNormal(int index, uint16_t *x, uint16_t *y, uint16_t *z);
|
||||
void GetTexCoord(int index, uint16_t *u, uint16_t *v);
|
||||
|
||||
#endif // FRAMEMAKER_H__
|
@ -1,426 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, 2022, Antonio Niño Díaz
|
||||
|
||||
// I used the information in this web to make the converter:
|
||||
// http://tfc.duke.free.fr/coding/md2-specs-en.html
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dynamic_list.h"
|
||||
#include "framemaker.h"
|
||||
|
||||
#define MAX_PATHLEN 1024
|
||||
|
||||
static inline int32_t floattof32(float n)
|
||||
{
|
||||
return (int32_t)(n * (1 << 12));
|
||||
}
|
||||
|
||||
static inline int16_t floattov16(float n)
|
||||
{
|
||||
return (int16_t)(n * (1 << 12));
|
||||
}
|
||||
|
||||
static inline int16_t floattov10(float n)
|
||||
{
|
||||
if (n > 0.998)
|
||||
return 0x7FFF;
|
||||
if (n < -0.998)
|
||||
return 0xFFFF;
|
||||
return (int16_t)(n * (1 << 9));
|
||||
}
|
||||
|
||||
#define absf(x) (((x) > 0) ? (x) : -(x))
|
||||
|
||||
//----------------------------------------------------
|
||||
// MD2 structs
|
||||
//----------------------------------------------------
|
||||
|
||||
typedef float vec3_t[3];
|
||||
|
||||
typedef struct {
|
||||
int32_t ident;
|
||||
int32_t version;
|
||||
|
||||
int32_t skinwidth;
|
||||
int32_t skinheight;
|
||||
int32_t framesize;
|
||||
int32_t num_skins;
|
||||
int32_t num_vertices;
|
||||
int32_t num_st;
|
||||
int32_t num_tris;
|
||||
int32_t num_glcmds;
|
||||
int32_t num_frames;
|
||||
int32_t offset_skins;
|
||||
int32_t offset_st;
|
||||
int32_t offset_tris;
|
||||
int32_t offset_frames;
|
||||
int32_t offset_glcmds;
|
||||
int32_t offset_end;
|
||||
} md2_header_t;
|
||||
|
||||
typedef struct {
|
||||
int16_t s;
|
||||
int16_t t;
|
||||
} md2_texCoord_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t vertex[3];
|
||||
uint16_t st[3];
|
||||
} md2_triangle_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t v[3];
|
||||
uint8_t normalIndex;
|
||||
} md2_vertex_t;
|
||||
|
||||
typedef struct {
|
||||
vec3_t scale;
|
||||
vec3_t translate;
|
||||
char name[16];
|
||||
md2_vertex_t *verts;
|
||||
} md2_frame_t;
|
||||
|
||||
typedef struct {
|
||||
float s;
|
||||
float t;
|
||||
int32_t index;
|
||||
} md2_glcmd_t;
|
||||
|
||||
float anorms[162][3] = {
|
||||
#include "anorms.h"
|
||||
};
|
||||
|
||||
//----------------------------------------------------
|
||||
// Converted structs
|
||||
//----------------------------------------------------
|
||||
|
||||
//Frame data -> texture, normal, vertex, texture...
|
||||
|
||||
typedef struct {
|
||||
int32_t magic;
|
||||
int32_t version;
|
||||
|
||||
int32_t num_frames;
|
||||
int32_t num_vertices;
|
||||
|
||||
int32_t offset_frames;
|
||||
int32_t offset_vtx;
|
||||
int32_t offset_norm;
|
||||
int32_t offset_st;
|
||||
|
||||
} ds_header_t;
|
||||
|
||||
typedef int16_t ds_vec3_t[3];
|
||||
|
||||
typedef int16_t ds_st_t[2];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
|
||||
void PrintUsage(void)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" md2_to_nea [input.md2] [output.nea] ([float scale])\n");
|
||||
printf(" ([float translate x] [float translate x] [float translate x])\n");
|
||||
}
|
||||
|
||||
int IsValidSize(int size)
|
||||
{
|
||||
return (size == 8 || size == 16 || size == 32 || size == 64 ||
|
||||
size == 128 || size == 256 || size == 512 || size == 1024);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("md2_to_nea v2.1\n");
|
||||
printf("\n");
|
||||
printf("Copyright (c) 2008-2011, 2019 Antonio Nino Diaz\n");
|
||||
printf("\n");
|
||||
|
||||
// DEFAULT VALUES
|
||||
float general_scale = 1;
|
||||
float general_trans[3] = { 0, 0, 0 };
|
||||
|
||||
switch (argc) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
// Not enough
|
||||
PrintUsage();
|
||||
return -1;
|
||||
case 3:
|
||||
// Use default modifications
|
||||
break;
|
||||
case 4:
|
||||
// Use default translation and custom scale
|
||||
general_scale = atof(argv[3]);
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
// Custom translation, not enough
|
||||
printf("You must set 3 coordinates for translation, not less.");
|
||||
PrintUsage();
|
||||
return -1;
|
||||
case 7:
|
||||
// Custom translation + scale
|
||||
general_scale = atof(argv[3]);
|
||||
general_trans[0] = atof(argv[4]);
|
||||
general_trans[1] = atof(argv[5]);
|
||||
general_trans[2] = atof(argv[6]);
|
||||
break;
|
||||
default:
|
||||
// The rest...
|
||||
PrintUsage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
char inputfilepath[MAX_PATHLEN];
|
||||
char outputfilepath[MAX_PATHLEN];
|
||||
|
||||
strcpy(inputfilepath, argv[1]);
|
||||
strcpy(outputfilepath, argv[2]);
|
||||
|
||||
if (general_scale == 0)
|
||||
{
|
||||
printf("\nScale can't be 0!!");
|
||||
PrintUsage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\nScale: %f\n", general_scale);
|
||||
printf("Translate: %f, %f, %f\n", general_trans[0], general_trans[1],
|
||||
general_trans[2]);
|
||||
|
||||
char *md2data;
|
||||
|
||||
printf("\nLoading MD2 model...\n");
|
||||
|
||||
FILE *datafile = fopen(inputfilepath, "r");
|
||||
if (datafile != NULL)
|
||||
{
|
||||
fseek(datafile, 0, SEEK_END);
|
||||
long int size = ftell(datafile);
|
||||
rewind(datafile);
|
||||
md2data = (char *)malloc(sizeof(char) * size);
|
||||
fread(md2data, 1, size, datafile);
|
||||
fclose(datafile);
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose(datafile);
|
||||
printf("\n\nCouldn't open %s!!\n\n", inputfilepath);
|
||||
PrintUsage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
md2_header_t *header = (md2_header_t *) md2data;
|
||||
|
||||
if ((header->ident != 844121161) || (header->version != 8))
|
||||
{
|
||||
printf("\n\nWrong file type or version!!\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t t_w = header->skinwidth;
|
||||
int32_t t_h = header->skinheight;
|
||||
|
||||
if (t_w > 1024 || t_h > 1024)
|
||||
{
|
||||
printf("\n\nTexture too big!!\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!IsValidSize(t_w) || !IsValidSize(t_h))
|
||||
{
|
||||
printf("\nWrong texture size. Must be power of 2.\n");
|
||||
printf("\nAlthough the model uses an invalid texture size, it will be converted.\n");
|
||||
printf("\nResize the texture to nearest valid size.\n\n");
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (IsValidSize(t_w))
|
||||
break;
|
||||
t_w++;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
if (IsValidSize(t_h))
|
||||
break;
|
||||
t_h++;
|
||||
}
|
||||
|
||||
int num_tris = header->num_tris;
|
||||
|
||||
md2_frame_t *frame = NULL;
|
||||
md2_texCoord_t *texcoord =
|
||||
(md2_texCoord_t *) ((uintptr_t)header->offset_st + (uintptr_t)header);
|
||||
md2_triangle_t *triangle =
|
||||
(md2_triangle_t *) ((uintptr_t)header->offset_tris + (uintptr_t)header);
|
||||
|
||||
// Current vertex
|
||||
md2_vertex_t *vtx;
|
||||
|
||||
printf("\nMD2 Information:\n\n Number of frames: %d\n Texture size: %dx%d",
|
||||
header->num_frames, t_w, t_h);
|
||||
|
||||
printf("\nCreating lists of commands...\n");
|
||||
|
||||
float bigvalue = 0;
|
||||
int32_t maxvtxnum = 0;
|
||||
|
||||
InitDynamicLists();
|
||||
|
||||
// Everything ready, let's "draw" all frames
|
||||
for (int n = 0; n < header->num_frames; n++)
|
||||
{
|
||||
frame = (md2_frame_t *) ((uintptr_t)header->offset_frames +
|
||||
(uintptr_t)header + (header->framesize * n));
|
||||
|
||||
NewFrame();
|
||||
|
||||
int32_t vtxcount = 0;
|
||||
|
||||
for (int32_t t = 0; t < num_tris; t++) {
|
||||
for (int32_t v = 0; v < 3; v++) {
|
||||
vtx = (md2_vertex_t *) ((uintptr_t)(&(frame->verts)));
|
||||
vtx = &vtx[triangle[t].vertex[v]];
|
||||
|
||||
// Texture
|
||||
int16_t s_ = texcoord[triangle[t].st[v]].s;
|
||||
int16_t t_ = texcoord[triangle[t].st[v]].t;
|
||||
|
||||
// This is used to change UVs if using a texture size unsupported by DS
|
||||
s_ = (int32_t)((float)(s_ * t_w) / (float)header->skinwidth);
|
||||
t_ = (int32_t)((float)(t_ * t_h) / (float)header->skinheight);
|
||||
NewFrameData(AddTexCoord(s_ << 4, t_ << 4)); // (t_h-t_)<<4));
|
||||
|
||||
// Normal
|
||||
uint16_t norm[3];
|
||||
for (int32_t b = 0; b < 3; b++)
|
||||
norm[b] = floattov10(anorms[vtx->normalIndex][b]);
|
||||
NewFrameData(AddNormal(norm[0], norm[1], norm[2]));
|
||||
|
||||
// Vertex
|
||||
vtxcount++;
|
||||
float _v[3];
|
||||
for (int32_t a = 0; a < 3; a++) {
|
||||
_v[a] = ((float)frame->scale[a] * (float)(vtx->v[a])) +
|
||||
(float)frame->translate[a];
|
||||
_v[a] += general_trans[a];
|
||||
_v[a] *= general_scale;
|
||||
|
||||
if ((absf(_v[a]) > (float)7.9997) && (absf(bigvalue) < absf(_v[a])))
|
||||
bigvalue = _v[a];
|
||||
}
|
||||
|
||||
NewFrameData(AddVertex(floattov16(_v[0]), floattov16(_v[2]),
|
||||
floattov16(_v[1])));
|
||||
}
|
||||
}
|
||||
|
||||
if (maxvtxnum < vtxcount)
|
||||
maxvtxnum = vtxcount;
|
||||
}
|
||||
|
||||
if (absf(bigvalue) > 0)
|
||||
{
|
||||
printf("\nModel too big for DS! Scale it down.\n");
|
||||
printf("\nDS max. allowed value: +/-7,9997\nModel max. detected value: %f\n",
|
||||
bigvalue);
|
||||
}
|
||||
|
||||
if (maxvtxnum > 6144)
|
||||
{
|
||||
printf("\nModel has too many vertices!\n");
|
||||
printf("\nDS can only render 6144 vertices per frame.\nYour model has %d vertices.\n",
|
||||
maxvtxnum);
|
||||
}
|
||||
|
||||
printf("\nCreating NEA file...\n");
|
||||
|
||||
// Now, let's save them into a NEA file.
|
||||
FILE *file = fopen(outputfilepath, "wb+");
|
||||
if (file == NULL)
|
||||
{
|
||||
printf("\nCouldn't create %s file!", outputfilepath);
|
||||
EndDynamicLists();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ds_header_t temp_header;
|
||||
temp_header.magic = 1296123214; // 'NEAM'
|
||||
temp_header.version = 2;
|
||||
temp_header.num_frames = header->num_frames;
|
||||
temp_header.num_vertices = num_tris * 3;
|
||||
printf("\nNumber of vertices: %d - Each frame: %d\n",
|
||||
GetVerticesNumber(), temp_header.num_vertices);
|
||||
printf("Number of normals: %d\n", GetNormalNumber());
|
||||
printf("Number of texture coordinates: %d\n", GetTexcoordsNumber());
|
||||
printf("Number of frames: %d\n", temp_header.num_frames);
|
||||
temp_header.offset_norm = sizeof(ds_header_t);
|
||||
temp_header.offset_st = temp_header.offset_norm +
|
||||
(sizeof(ds_vec3_t) * GetNormalNumber());
|
||||
temp_header.offset_vtx = temp_header.offset_st +
|
||||
(sizeof(ds_st_t) * GetTexcoordsNumber());
|
||||
temp_header.offset_frames = temp_header.offset_vtx +
|
||||
(sizeof(ds_vec3_t) * GetVerticesNumber());
|
||||
fwrite(&temp_header, sizeof(ds_header_t), 1, file);
|
||||
|
||||
// Normals...
|
||||
ds_vec3_t temp_vector;
|
||||
int32_t number = GetNormalNumber();
|
||||
for (int32_t i = 0; i < number; i++)
|
||||
{
|
||||
GetNormal(i, (uint16_t *)&temp_vector[0], (uint16_t *)&temp_vector[1],
|
||||
(uint16_t *)&temp_vector[2]);
|
||||
fwrite(&temp_vector, sizeof(ds_vec3_t), 1, file);
|
||||
}
|
||||
|
||||
// Texcoords
|
||||
ds_st_t temp_texcoord;
|
||||
number = GetTexcoordsNumber();
|
||||
for (int32_t i = 0; i < number; i++)
|
||||
{
|
||||
GetTexCoord(i, (uint16_t *)&temp_texcoord[0],
|
||||
(uint16_t *)&temp_texcoord[1]);
|
||||
fwrite(&temp_texcoord, sizeof(ds_st_t), 1, file);
|
||||
}
|
||||
|
||||
// Vertices
|
||||
number = GetVerticesNumber();
|
||||
for (int32_t i = 0; i < number; i++)
|
||||
{
|
||||
GetVertex(i, (uint16_t *)&temp_vector[0], (uint16_t *)&temp_vector[1],
|
||||
(uint16_t *)&temp_vector[2]);
|
||||
fwrite(&temp_vector, sizeof(ds_vec3_t), 1, file);
|
||||
}
|
||||
|
||||
printf("\nSize of a frame: %ld\n",
|
||||
(long int)(GetFrameSize(0) * sizeof(uint16_t)));
|
||||
|
||||
// Frames
|
||||
for (int32_t i_ = 0; i_ < header->num_frames; i_++)
|
||||
{
|
||||
fwrite((int *)GetFramePointer(i_), 1,
|
||||
GetFrameSize(i_) * sizeof(uint16_t), file);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
FILE *test = fopen(outputfilepath, "rb");
|
||||
fseek(test, 0, SEEK_END);
|
||||
long int size = ftell(test);
|
||||
fclose(test);
|
||||
|
||||
printf("\nNEA file size: %ld bytes", size);
|
||||
printf("\n\nReady!\n\n");
|
||||
|
||||
EndDynamicLists();
|
||||
|
||||
return 0;
|
||||
}
|
324
tools/md5_to_dsma/display_list.py
Normal file
324
tools/md5_to_dsma/display_list.py
Normal file
@ -0,0 +1,324 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2022 Antonio Niño Díaz <antonio_nd@outlook.com>
|
||||
|
||||
def float_to_v16(val):
|
||||
res = int(val * (1 << 12))
|
||||
if res < -0x8000:
|
||||
raise OverflowError(f"{val} too small for v16: {res:#04x}")
|
||||
if res > 0x7FFF:
|
||||
raise OverflowError(f"{val} too big for v16: {res:#04x}")
|
||||
if res < 0:
|
||||
res = 0x10000 + res
|
||||
return res
|
||||
|
||||
def float_to_f32(val):
|
||||
res = int(val * (1 << 12))
|
||||
if res < -0x80000000:
|
||||
raise OverflowError(f"{val} too small for f32: {res:#08x}")
|
||||
if res > 0x7FFFFFFF:
|
||||
raise OverflowError(f"{val} too big for f32: {res:#08x}")
|
||||
if res < 0:
|
||||
res = 0x100000000 + res
|
||||
return res
|
||||
|
||||
def v16_to_float(val):
|
||||
return val / (1 << 12)
|
||||
|
||||
def float_to_v10(val):
|
||||
res = int(val * (1 << 6))
|
||||
if res < -0x200:
|
||||
raise OverflowError(f"{val} too small for v10: {res:#03x}")
|
||||
if res > 0x1FF:
|
||||
raise OverflowError(f"{val} too big for v10: {res:#03x}")
|
||||
if res < 0:
|
||||
res = 0x400 + res
|
||||
return res
|
||||
|
||||
def v10_to_float(val):
|
||||
return val / (1 << 6)
|
||||
|
||||
def float_to_diff10(val):
|
||||
res = int(val * (1 << 9))
|
||||
if res < -0x200:
|
||||
raise OverflowError(f"{val} too small for diff10: {res:#03x}")
|
||||
if res > 0x1FF:
|
||||
raise OverflowError(f"{val} too big for diff10: {res:#03x}")
|
||||
if res < 0:
|
||||
res = 0x400 + res
|
||||
return res
|
||||
|
||||
def diff10_to_float(val):
|
||||
return val / (1 << 9)
|
||||
|
||||
def float_to_t16(val):
|
||||
res = int(val * (1 << 4))
|
||||
if res < -0x8000:
|
||||
raise OverflowError(f"{val} too small for t16: {res:#04x}")
|
||||
if res > 0x7FFF:
|
||||
raise OverflowError(f"{val} too big for t16: {res:#04x}")
|
||||
if res < 0:
|
||||
res = 0x10000 + res
|
||||
return res
|
||||
|
||||
def float_to_n10(val):
|
||||
res = int(val * (1 << 9))
|
||||
if res < -0x200:
|
||||
#raise OverflowError(f"{val} too small for n10: {res:#03x}")
|
||||
res = -0x200
|
||||
if res > 0x1FF:
|
||||
#raise OverflowError(f"{val} too big for n10: {res:#03x}")
|
||||
res = 0x1FF
|
||||
if res < 0:
|
||||
res = 0x400 + res
|
||||
return res
|
||||
|
||||
def command_name_to_id(name):
|
||||
commands = {
|
||||
"NOP": 0x00, # (0) No Operation (for padding packed GXFIFO commands)
|
||||
"MTX_MODE": 0x10, # (1) Set Matrix Mode
|
||||
"MTX_PUSH": 0x11, # (0) Push Current Matrix on Stack
|
||||
"MTX_POP": 0x12, # (1) Pop Current Matrix from Stack
|
||||
"MTX_STORE": 0x13, # (1) Store Current Matrix on Stack
|
||||
"MTX_RESTORE": 0x14, # (1) Restore Current Matrix from Stack
|
||||
"MTX_IDENTITY": 0x15, # (0) Load Unit Matrix to Current Matrix
|
||||
"MTX_LOAD_4x4": 0x16, # (16) Load 4x4 Matrix to Current Matrix
|
||||
"MTX_LOAD_4x3": 0x17, # (12) Load 4x3 Matrix to Current Matrix
|
||||
"MTX_MULT_4x4": 0x18, # (16) Multiply Current Matrix by 4x4 Matrix
|
||||
"MTX_MULT_4x3": 0x19, # (12) Multiply Current Matrix by 4x3 Matrix
|
||||
"MTX_MULT_3x3": 0x1A, # (9) Multiply Current Matrix by 3x3 Matrix
|
||||
"MTX_SCALE": 0x1B, # (3) Multiply Current Matrix by Scale Matrix
|
||||
"MTX_TRANS": 0x1C, # (3) Mult. Curr. Matrix by Translation Matrix
|
||||
"COLOR": 0x20, # (1) Directly Set Vertex Color
|
||||
"NORMAL": 0x21, # (1) Set Normal Vector
|
||||
"TEXCOORD": 0x22, # (1) Set Texture Coordinates
|
||||
"VTX_16": 0x23, # (2) Set Vertex XYZ Coordinates
|
||||
"VTX_10": 0x24, # (1) Set Vertex XYZ Coordinates
|
||||
"VTX_XY": 0x25, # (1) Set Vertex XY Coordinates
|
||||
"VTX_XZ": 0x26, # (1) Set Vertex XZ Coordinates
|
||||
"VTX_YZ": 0x27, # (1) Set Vertex YZ Coordinates
|
||||
"VTX_DIFF": 0x28, # (1) Set Relative Vertex Coordinates
|
||||
"POLYGON_ATTR": 0x29, # (1) Set Polygon Attributes
|
||||
"TEXIMAGE_PARAM": 0x2A, # (1) Set Texture Parameters
|
||||
"PLTT_BASE": 0x2B, # (1) Set Texture Palette Base Address
|
||||
"DIF_AMB": 0x30, # (1) MaterialColor0 # Diffuse/Ambient Reflect.
|
||||
"SPE_EMI": 0x31, # (1) MaterialColor1 # Specular Ref. & Emission
|
||||
"LIGHT_VECTOR": 0x32, # (1) Set Light's Directional Vector
|
||||
"LIGHT_COLOR": 0x33, # (1) Set Light Color
|
||||
"SHININESS": 0x34, # (32) Specular Reflection Shininess Table
|
||||
"BEGIN_VTXS": 0x40, # (1) Start of Vertex List
|
||||
"END_VTXS": 0x41, # (0) End of Vertex List
|
||||
"SWAP_BUFFERS": 0x50, # (1) Swap Rendering Engine Buffer
|
||||
"VIEWPORT": 0x60, # (1) Set Viewport
|
||||
"BOX_TEST": 0x70, # (3) Test if Cuboid Sits inside View Volume
|
||||
"POS_TEST": 0x71, # (2) Set Position Coordinates for Test
|
||||
"VEC_TEST": 0x72, # (1) Set Directional Vector for Test
|
||||
}
|
||||
return commands[name]
|
||||
|
||||
def poly_type_to_id(name):
|
||||
types = {
|
||||
"triangles": 0,
|
||||
"quads": 1,
|
||||
"triangle_strip": 2,
|
||||
"quad_strip": 3,
|
||||
}
|
||||
return types[name]
|
||||
|
||||
def error(x1, x2, y1, y2, z1, z2):
|
||||
return (abs(x1 - x2) ** 2) + (abs(y1 - y2) ** 2) + (abs(z1 - z2) ** 2)
|
||||
|
||||
class DisplayList():
|
||||
|
||||
def __init__(self):
|
||||
self.commands = []
|
||||
self.parameters = []
|
||||
self.vtx_last = None
|
||||
self.texcoord_last = None
|
||||
self.normal_last = None
|
||||
self.begin_vtx_last = None
|
||||
|
||||
self.display_list = []
|
||||
|
||||
def add_command(self, command, *args):
|
||||
self.commands.append(command)
|
||||
if len(args) > 0:
|
||||
self.parameters.extend(args)
|
||||
|
||||
if len(self.commands) == 4:
|
||||
header = self.commands[0] | self.commands[1] << 8 | \
|
||||
self.commands[2] << 16 | self.commands[3] << 24
|
||||
|
||||
self.display_list.append(header)
|
||||
self.display_list.extend(self.parameters)
|
||||
|
||||
self.commands = []
|
||||
self.parameters = []
|
||||
|
||||
def finalize(self):
|
||||
# If there are pending commands, add NOPs to complete the display list
|
||||
if len(self.commands) > 0:
|
||||
padding = 4 - len(self.commands)
|
||||
for i in range(padding):
|
||||
self.nop()
|
||||
|
||||
# Prepend size to the list
|
||||
self.display_list.insert(0, len(self.display_list))
|
||||
|
||||
def save_to_file(self, path):
|
||||
with open(path, "wb") as f:
|
||||
for u32 in self.display_list:
|
||||
b = [u32 & 0xFF, \
|
||||
(u32 >> 8) & 0xFF, \
|
||||
(u32 >> 16) & 0xFF, \
|
||||
(u32 >> 24) & 0xFF]
|
||||
f.write(bytearray(b))
|
||||
|
||||
def nop(self):
|
||||
self.add_command(command_name_to_id("NOP"))
|
||||
|
||||
def mtx_push(self):
|
||||
self.add_command(command_name_to_id("MTX_PUSH"))
|
||||
|
||||
def mtx_pop(self, index):
|
||||
self.add_command(command_name_to_id("MTX_POP"), index)
|
||||
|
||||
def mtx_restore(self, index):
|
||||
self.add_command(command_name_to_id("MTX_RESTORE"), index)
|
||||
|
||||
def mtx_load_4x3(self, m):
|
||||
fixed_m = [float_to_f32(v) for v in m]
|
||||
self.add_command(command_name_to_id("MTX_LOAD_4x3"), *fixed_m)
|
||||
|
||||
def mtx_mult_4x3(self, m):
|
||||
fixed_m = [float_to_f32(v) for v in m]
|
||||
self.add_command(command_name_to_id("MTX_MULT_4x3"), *fixed_m)
|
||||
|
||||
def color(self, r, g, b):
|
||||
arg = int(r * 31) | (int(g * 31) << 5) | (int(b * 31) << 10)
|
||||
self.add_command(command_name_to_id("COLOR"), arg)
|
||||
|
||||
def normal(self, x, y, z):
|
||||
# Skip if it's the same normal
|
||||
if self.normal_last is not None:
|
||||
if self.normal_last[0] == x and self.normal_last[1] == y and \
|
||||
self.normal_last[2] == z:
|
||||
return
|
||||
|
||||
arg = float_to_n10(x) | (float_to_n10(y) << 10) | float_to_n10(z) << 20
|
||||
self.add_command(command_name_to_id("NORMAL"), arg)
|
||||
self.normal_last = (x, y, z)
|
||||
|
||||
def texcoord(self, u, v):
|
||||
# Skip if it's the same texcoord
|
||||
if self.texcoord_last is not None:
|
||||
if self.texcoord_last[0] == u and self.texcoord_last[1] == v:
|
||||
return
|
||||
|
||||
arg = float_to_t16(u) | (float_to_t16(v) << 16)
|
||||
self.add_command(command_name_to_id("TEXCOORD"), arg)
|
||||
self.texcoord_last = (u, v)
|
||||
|
||||
def vtx_16(self, x, y, z):
|
||||
args = [float_to_v16(x) | (float_to_v16(y) << 16), float_to_v16(z)]
|
||||
self.add_command(command_name_to_id("VTX_16"), *args)
|
||||
self.vtx_last = (x, y, z)
|
||||
|
||||
def vtx_10(self, x, y, z):
|
||||
arg = float_to_v10(x) | (float_to_v10(y) << 10) | float_to_v10(z) << 20
|
||||
self.add_command(command_name_to_id("VTX_10"), arg)
|
||||
self.vtx_last = (x, y, z)
|
||||
|
||||
def vtx_xy(self, x, y):
|
||||
arg = float_to_v16(x) | (float_to_v16(y) << 16)
|
||||
self.add_command(command_name_to_id("VTX_XY"), arg)
|
||||
self.vtx_last = (x, y, self.vtx_last[2])
|
||||
|
||||
def vtx_xz(self, x, z):
|
||||
arg = float_to_v16(x) | (float_to_v16(z) << 16)
|
||||
self.add_command(command_name_to_id("VTX_XZ"), arg)
|
||||
self.vtx_last = (x, self.vtx_last[1], z)
|
||||
|
||||
def vtx_yz(self, y, z):
|
||||
arg = float_to_v16(y) | (float_to_v16(z) << 16)
|
||||
self.add_command(command_name_to_id("VTX_YZ"), arg)
|
||||
self.vtx_last = (self.vtx_last[0], y, z)
|
||||
|
||||
def vtx_diff(self, x, y, z):
|
||||
arg = float_to_diff10(x - self.vtx_last[0]) | \
|
||||
(float_to_diff10(y - self.vtx_last[1]) << 10) | \
|
||||
(float_to_diff10(z - self.vtx_last[2]) << 20)
|
||||
self.add_command(command_name_to_id("VTX_DIFF"), arg)
|
||||
self.vtx_last = (x, y, z)
|
||||
|
||||
def vtx(self, x, y, z):
|
||||
"""
|
||||
Picks the best vtx command based on the previous vertex and the error of
|
||||
the conversion.
|
||||
"""
|
||||
# Allow {vtx_xy, vtx_yz, vtx_xz, vtx_diff} if there is a previous vertex
|
||||
allow_diff = self.vtx_last is not None
|
||||
|
||||
# First, check if any of the coordinates is exactly the same as the
|
||||
# previous command. We can trivially use vtx_xy, vtx_xz, vtx_yz because
|
||||
# they have the min possible size and the max possible accuracy
|
||||
if allow_diff:
|
||||
if float_to_v16(self.vtx_last[0]) == float_to_v16(x):
|
||||
self.vtx_yz(y, z)
|
||||
return
|
||||
elif float_to_v16(self.vtx_last[1]) == float_to_v16(y):
|
||||
self.vtx_xz(x, z)
|
||||
return
|
||||
elif float_to_v16(self.vtx_last[2]) == float_to_v16(z):
|
||||
self.vtx_xy(x, y)
|
||||
return
|
||||
|
||||
# If not, there are three options: vtx_16, vtx_10, vtx_diff. Pick the
|
||||
# one with the lowest error.
|
||||
|
||||
# TODO: Maybe use vtx_diff, but this may cause accuracy issues if it is
|
||||
# used several times in a row.
|
||||
|
||||
error_vtx_16 = error(v16_to_float(float_to_v16(x)), x,
|
||||
v16_to_float(float_to_v16(y)), y,
|
||||
v16_to_float(float_to_v16(z)), z)
|
||||
|
||||
error_vtx_10 = error(v10_to_float(float_to_v10(x)), x,
|
||||
v10_to_float(float_to_v10(y)), y,
|
||||
v10_to_float(float_to_v10(z)), z)
|
||||
|
||||
if error_vtx_10 <= error_vtx_16:
|
||||
self.vtx_10(x, y, z)
|
||||
else:
|
||||
self.vtx_16(x, y, z)
|
||||
|
||||
return
|
||||
|
||||
def begin_vtxs(self, poly_type):
|
||||
self.add_command(command_name_to_id("BEGIN_VTXS"), poly_type_to_id(poly_type))
|
||||
self.begin_vtx_last = poly_type
|
||||
|
||||
def end_vtxs(self):
|
||||
self.add_command(command_name_to_id("END_VTXS"))
|
||||
self.begin_vtx_last = None
|
||||
|
||||
def switch_vtxs(self, poly_type):
|
||||
"""Sends a new BEGIN_VTXS if the polygon type has changed."""
|
||||
if self.begin_vtx_last != poly_type:
|
||||
if self.begin_vtx_last is not None:
|
||||
self.end_vtxs()
|
||||
self.begin_vtxs(poly_type)
|
||||
|
||||
if __name__ == "__main__":
|
||||
dl = DisplayList()
|
||||
dl.begin_vtxs("triangles")
|
||||
dl.color(1.0, 0, 0)
|
||||
dl.vtx_16(1.0, -1.0, 0)
|
||||
dl.color(0, 1.0, 0)
|
||||
dl.vtx_10(1.0, 1.0, 0)
|
||||
dl.color(0, 0, 1.0)
|
||||
dl.vtx_xy(-1.0, -1.0)
|
||||
dl.end_vtxs()
|
||||
dl.finalize()
|
||||
print(', '.join([hex(i) for i in dl.display_list]))
|
||||
dl.save_to_file("test.bin")
|
859
tools/md5_to_dsma/md5_to_dsma.py
Executable file
859
tools/md5_to_dsma/md5_to_dsma.py
Executable file
@ -0,0 +1,859 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2022 Antonio Niño Díaz <antonio_nd@outlook.com>
|
||||
|
||||
import os
|
||||
|
||||
from collections import namedtuple
|
||||
from math import sqrt
|
||||
|
||||
from display_list import DisplayList, float_to_f32
|
||||
|
||||
class MD5FormatError(Exception):
|
||||
pass
|
||||
|
||||
VALID_TEXTURE_SIZES = [8, 16, 32, 64, 128, 256, 512, 1024]
|
||||
|
||||
def is_valid_texture_size(size):
|
||||
return size in VALID_TEXTURE_SIZES
|
||||
|
||||
def assert_num_args(cmd, real, expected, tokens):
|
||||
if real != expected:
|
||||
raise MD5FormatError(f"Unexpected nargs for '{cmd}' ({real} != {expected}): {tokens}")
|
||||
|
||||
class Quaternion():
|
||||
def __init__(self, w, x, y, z):
|
||||
self.w = w
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
def to_v3(self):
|
||||
return Vector(self.x, self.y, self.z)
|
||||
|
||||
def complement(self):
|
||||
return Quaternion(self.w, -self.x, -self.y, -self.z)
|
||||
|
||||
def normalize(self):
|
||||
mag = sqrt((self.w ** 2) + (self.x ** 2) + (self.y ** 2) + (self.z ** 2))
|
||||
return Quaternion(self.w / mag, self.x /mag, self.y / mag, self.z / mag)
|
||||
|
||||
def mul(self, other):
|
||||
w = (self.w * other.w) - (self.x * other.x) - (self.y * other.y) - (self.z * other.z)
|
||||
x = (self.x * other.w) + (self.w * other.x) + (self.y * other.z) - (self.z * other.y)
|
||||
y = (self.y * other.w) + (self.w * other.y) + (self.z * other.x) - (self.x * other.z)
|
||||
z = (self.z * other.w) + (self.w * other.z) + (self.x * other.y) - (self.y * other.x)
|
||||
return Quaternion(w, x, y, z)
|
||||
|
||||
def quaternion_fill_incomplete_w(v):
|
||||
"""
|
||||
This expands an incomplete quaternion, not a regular vector. This is
|
||||
needed if a quaternion is stored as the components x, y and z and it is
|
||||
expected that the code will fill the value of w.
|
||||
"""
|
||||
t = 1.0 - (v[0] * v[0]) - (v[1] * v[1]) - (v[2] * v[2])
|
||||
if t < 0:
|
||||
w = 0
|
||||
else:
|
||||
w = -sqrt(t)
|
||||
return Quaternion(w, v[0], v[1], v[2])
|
||||
|
||||
class Vector():
|
||||
def __init__(self, x, y, z):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
def to_q(self):
|
||||
return Quaternion(0, self.x, self.y, self.z)
|
||||
|
||||
def length(self):
|
||||
return sqrt((self.x ** 2) + (self.y ** 2) + (self.z ** 2))
|
||||
|
||||
def normalize(self):
|
||||
mag = self.length()
|
||||
return Vector(self.x / mag, self.y / mag, self.z / mag)
|
||||
|
||||
def add(self, other):
|
||||
return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||
|
||||
def sub(self, other):
|
||||
return Vector(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
|
||||
def cross(self, other):
|
||||
x = (self.y * other.z) - (other.y * self.z)
|
||||
y = (self.z * other.x) - (other.z * self.x)
|
||||
z = (self.x * other.y) - (other.x * self.y)
|
||||
return Vector(x, y, z)
|
||||
|
||||
def mul_m4x3(self, m):
|
||||
x = (self.x * m[0][0]) + (self.y * m[0][1]) + (self.z * m[0][2]) + (m[0][3] * 1)
|
||||
y = (self.x * m[1][0]) + (self.y * m[1][1]) + (self.z * m[1][2]) + (m[1][3] * 1)
|
||||
z = (self.x * m[2][0]) + (self.y * m[2][1]) + (self.z * m[2][2]) + (m[2][3] * 1)
|
||||
return Vector(x, y, z)
|
||||
|
||||
def joint_info_to_m4x3(q, trans):
|
||||
"""
|
||||
This generates a 4x3 matrix that represents a rotation and a translation.
|
||||
q is a Quaternion with a orientation, trans is a Vector with a translation.
|
||||
"""
|
||||
wx = 2 * q.w * q.x
|
||||
wy = 2 * q.w * q.y
|
||||
wz = 2 * q.w * q.z
|
||||
x2 = 2 * q.x * q.x
|
||||
xy = 2 * q.x * q.y
|
||||
xz = 2 * q.x * q.z
|
||||
y2 = 2 * q.y * q.y
|
||||
yz = 2 * q.y * q.z
|
||||
z2 = 2 * q.z * q.z
|
||||
|
||||
return [[1 - y2 - z2, xy - wz, xz + wy, trans.x],
|
||||
[ xy + wz, 1 - x2 - z2, yz - wx, trans.y],
|
||||
[ xz - wy, yz + wx, 1 - x2 - y2, trans.z]]
|
||||
|
||||
def parse_md5mesh(input_file):
|
||||
Joint = namedtuple("Joint", "name parent pos orient")
|
||||
Vert = namedtuple("Vert", "st startWeight countWeight")
|
||||
Weight = namedtuple("Weight", "joint bias pos")
|
||||
Mesh = namedtuple("Mesh", "numverts verts numtris tris numweights weights")
|
||||
|
||||
joints = []
|
||||
meshes = []
|
||||
|
||||
with open(input_file, 'r') as md5mesh_file:
|
||||
|
||||
numJoints = None
|
||||
numMeshes = None
|
||||
|
||||
# This can have three values:
|
||||
# - "root": Parsing commands in the md5mesh outside of any group
|
||||
# - "joints": Inside a "joints" node.
|
||||
# - "mesh": Inside a "mesh" node.
|
||||
mode = "root"
|
||||
|
||||
# Temporary variables used to store mesh information before packing it
|
||||
numverts = None
|
||||
verts = None
|
||||
numtris = None
|
||||
tris = None
|
||||
numweights = None
|
||||
weights = None
|
||||
|
||||
for line in md5mesh_file:
|
||||
# Remove comments
|
||||
line = line.split('//')[0]
|
||||
|
||||
# Parse line
|
||||
tokens = line.split()
|
||||
|
||||
if len(tokens) == 0: # Empty line
|
||||
continue
|
||||
|
||||
cmd = tokens[0]
|
||||
tokens = tokens[1:]
|
||||
nargs = len(tokens)
|
||||
|
||||
if mode == "root":
|
||||
if cmd == 'MD5Version':
|
||||
assert_num_args('MD5Version', nargs, 1, tokens)
|
||||
version = int(tokens[0])
|
||||
if version != 10:
|
||||
raise MD5FormatError(f"Invalid 'MD5Version': {version} != 10")
|
||||
|
||||
elif cmd == 'commandline':
|
||||
# Ignore this
|
||||
pass
|
||||
|
||||
elif cmd == 'numJoints':
|
||||
assert_num_args('numJoints', nargs, 1, tokens)
|
||||
numJoints = int(tokens[0])
|
||||
if numJoints == 0:
|
||||
raise MD5FormatError(f"'numJoints' is 0")
|
||||
|
||||
elif cmd == 'numMeshes':
|
||||
assert_num_args('numMeshes', nargs, 1, tokens)
|
||||
numMeshes = int(tokens[0])
|
||||
if numMeshes == 0:
|
||||
raise MD5FormatError(f"'numMeshes' is 0")
|
||||
|
||||
elif cmd == 'joints':
|
||||
assert_num_args('joints', nargs, 1, tokens)
|
||||
if tokens[0] != '{':
|
||||
raise MD5FormatError(f"Unexpected token for 'joints': {tokens}")
|
||||
if numJoints is None:
|
||||
raise MD5FormatError("'joints' command before 'numJoints'")
|
||||
mode = "joints"
|
||||
|
||||
elif cmd == 'mesh':
|
||||
assert_num_args('mesh', nargs, 1, tokens)
|
||||
if tokens[0] != '{':
|
||||
raise MD5FormatError(f"Unexpected token for 'mesh': {tokens}")
|
||||
if numMeshes is None:
|
||||
raise MD5FormatError("'mesh' command before 'numMeshes'")
|
||||
mode = "mesh"
|
||||
|
||||
else:
|
||||
print(f"Ignored unsupported command: {cmd} {tokens}")
|
||||
|
||||
elif mode == "joints":
|
||||
if cmd == '}':
|
||||
if nargs > 0:
|
||||
raise MD5FormatError(f"Unexpected tokens after 'joints {{}}': {tokens}")
|
||||
mode = "root"
|
||||
else:
|
||||
_, name, line = line.split('"')
|
||||
tokens = line.strip().split(" ")
|
||||
nargs = len(tokens)
|
||||
|
||||
assert_num_args('joint entry', nargs, 11, tokens)
|
||||
|
||||
parent = int(tokens[0])
|
||||
|
||||
if tokens[1] != '(':
|
||||
raise MD5FormatError(f"Unexpected token 1 for joint': {tokens}")
|
||||
pos = Vector(float(tokens[2]), float(tokens[3]), float(tokens[4]))
|
||||
if tokens[5] != ')':
|
||||
raise MD5FormatError(f"Unexpected token 5 for joint': {tokens}")
|
||||
|
||||
if tokens[6] != '(':
|
||||
raise MD5FormatError(f"Unexpected token 6 for joint': {tokens}")
|
||||
orient = (float(tokens[7]), float(tokens[8]), float(tokens[9]))
|
||||
q_orient = quaternion_fill_incomplete_w(orient)
|
||||
if tokens[10] != ')':
|
||||
raise MD5FormatError(f"Unexpected token 10 for joint': {tokens}")
|
||||
|
||||
joints.append(Joint(name, parent, pos, q_orient))
|
||||
|
||||
elif mode == "mesh":
|
||||
if cmd == '}':
|
||||
if nargs != 0:
|
||||
raise MD5FormatError(f"Unexpected tokens after 'mesh {{}}': {tokens}")
|
||||
mode = "root"
|
||||
|
||||
meshes.append(Mesh(numverts, verts, numtris, tris, numweights, weights))
|
||||
|
||||
numverts = None
|
||||
verts = None
|
||||
numtris = None
|
||||
tris = None
|
||||
numweights = None
|
||||
weights = None
|
||||
|
||||
elif cmd == 'shader':
|
||||
# Ignore this
|
||||
pass
|
||||
|
||||
elif cmd == 'numverts':
|
||||
assert_num_args('numverts', nargs, 1, tokens)
|
||||
numverts = int(tokens[0])
|
||||
verts = [None] * numverts
|
||||
|
||||
elif cmd == 'vert':
|
||||
assert_num_args('vert', nargs, 7, tokens)
|
||||
if numverts is None:
|
||||
raise MD5FormatError("'vert' command before 'numverts'")
|
||||
|
||||
index = int(tokens[0])
|
||||
|
||||
if tokens[1] != '(':
|
||||
raise MD5FormatError(f"Unexpected token 1 for vert': {tokens}")
|
||||
st = (float(tokens[2]), float(tokens[3]))
|
||||
if tokens[4] != ')':
|
||||
raise MD5FormatError(f"Unexpected token 4 for vert': {tokens}")
|
||||
|
||||
startWeight = int(tokens[5])
|
||||
countWeight = int(tokens[6])
|
||||
|
||||
if countWeight != 1:
|
||||
raise MD5FormatError(
|
||||
f"Vertex with {countWeight} weights detected, but this tool "
|
||||
"only supports vertices with one weight. Ensure that all your "
|
||||
"vertices are assigned exactly one weight with a bias of 1.0."
|
||||
)
|
||||
|
||||
verts[index] = Vert(st, startWeight, countWeight)
|
||||
|
||||
elif cmd == 'numtris':
|
||||
assert_num_args('numtris', nargs, 1, tokens)
|
||||
numtris = int(tokens[0])
|
||||
tris = [None] * numtris
|
||||
|
||||
elif cmd == 'tri':
|
||||
assert_num_args('tri', nargs, 4, tokens)
|
||||
if numtris is None:
|
||||
raise MD5FormatError("'tri' command before 'numtris'")
|
||||
|
||||
index = int(tokens[0])
|
||||
# Reverse order so that they face the right direction
|
||||
vertIndices = (int(tokens[3]), int(tokens[2]), int(tokens[1]))
|
||||
|
||||
tris[index] = vertIndices
|
||||
|
||||
elif cmd == 'numweights':
|
||||
assert_num_args('numweights', nargs, 1, tokens)
|
||||
numweights = int(tokens[0])
|
||||
weights = [None] * numweights
|
||||
|
||||
elif cmd == 'weight':
|
||||
assert_num_args('weight', nargs, 8, tokens)
|
||||
if numverts is None:
|
||||
raise MD5FormatError("'weight' command before 'numweights'")
|
||||
|
||||
index = int(tokens[0])
|
||||
jointIndex = int(tokens[1])
|
||||
bias = float(tokens[2])
|
||||
|
||||
if bias != 1.0:
|
||||
raise MD5FormatError(
|
||||
f"Weight with bias {bias} detected, but this tool only"
|
||||
"supports weights with bias equal to 1.0. Ensure that all"
|
||||
"your vertices are assigned exactly one weight with a"
|
||||
"bias of 1.0."
|
||||
)
|
||||
|
||||
if tokens[3] != '(':
|
||||
raise MD5FormatError(f"Unexpected token 3 for weight': {tokens}")
|
||||
pos = Vector(float(tokens[4]), float(tokens[5]), float(tokens[6]))
|
||||
if tokens[7] != ')':
|
||||
raise MD5FormatError(f"Unexpected token 7 for weight': {tokens}")
|
||||
|
||||
weights[index] = Weight(jointIndex, bias, pos)
|
||||
|
||||
else:
|
||||
print(f"Ignored unsupported command: {cmd} {tokens}")
|
||||
|
||||
if mode != "root":
|
||||
raise MD5FormatError("Unexpected end of file (expected '}')")
|
||||
|
||||
realJoints = len(joints)
|
||||
if numJoints != realJoints:
|
||||
raise MD5FormatError(f"Incorrect number of joints: {numJoints} != {realJoints}")
|
||||
|
||||
realMeshes = len(meshes)
|
||||
if numJoints != realJoints:
|
||||
raise MD5FormatError(f"Incorrect number of joints: {numJoints} != {realJoints}")
|
||||
|
||||
return (joints, meshes)
|
||||
|
||||
def parse_md5anim(input_file):
|
||||
Joint = namedtuple("Joint", "name parent pos orient")
|
||||
|
||||
joints = []
|
||||
frames = []
|
||||
|
||||
with open(input_file, 'r') as md5anim_file:
|
||||
|
||||
numFrames = None
|
||||
numJoints = None
|
||||
|
||||
baseframe = []
|
||||
hierarchy = []
|
||||
|
||||
# This can have three values:
|
||||
# - "root": Parsing commands in the md5mesh outside of any group
|
||||
# - "hierarchy": Inside a "hierarchy" node.
|
||||
# - "bounds": Inside a "bounds" node.
|
||||
# - "baseframe": Inside a "baseframe" node.
|
||||
# - "frame": Inside a "frame" node.
|
||||
mode = "root"
|
||||
|
||||
frame_index = None
|
||||
|
||||
for line in md5anim_file:
|
||||
# Remove comments
|
||||
line = line.split('//')[0]
|
||||
|
||||
# Parse line
|
||||
tokens = line.split()
|
||||
|
||||
if len(tokens) == 0: # Empty line
|
||||
continue
|
||||
|
||||
cmd = tokens[0]
|
||||
tokens = tokens[1:]
|
||||
nargs = len(tokens)
|
||||
|
||||
if mode == "root":
|
||||
if cmd == 'MD5Version':
|
||||
assert_num_args('MD5Version', nargs, 1, tokens)
|
||||
version = int(tokens[0])
|
||||
if version != 10:
|
||||
raise MD5FormatError(f"Invalid 'MD5Version': {version} != 10")
|
||||
|
||||
elif cmd == 'commandline':
|
||||
# Ignore this
|
||||
pass
|
||||
|
||||
elif cmd == 'numFrames':
|
||||
assert_num_args('numFrames', nargs, 1, tokens)
|
||||
numFrames = int(tokens[0])
|
||||
if numFrames == 0:
|
||||
raise MD5FormatError(f"'numFrames' is 0")
|
||||
frames = [None] * numFrames
|
||||
|
||||
elif cmd == 'numJoints':
|
||||
assert_num_args('numJoints', nargs, 1, tokens)
|
||||
numJoints = int(tokens[0])
|
||||
if numJoints == 0:
|
||||
raise MD5FormatError(f"'numJoints' is 0")
|
||||
|
||||
elif cmd == 'frameRate':
|
||||
# Ignore this
|
||||
pass
|
||||
|
||||
elif cmd == 'numAnimatedComponents':
|
||||
# Ignore this
|
||||
pass
|
||||
|
||||
elif cmd == 'hierarchy':
|
||||
assert_num_args('hierarchy', nargs, 1, tokens)
|
||||
if tokens[0] != '{':
|
||||
raise MD5FormatError(f"Unexpected token for 'hierarchy': {tokens}")
|
||||
mode = "hierarchy"
|
||||
|
||||
elif cmd == 'bounds':
|
||||
assert_num_args('bounds', nargs, 1, tokens)
|
||||
if tokens[0] != '{':
|
||||
raise MD5FormatError(f"Unexpected token for 'bounds': {tokens}")
|
||||
mode = "bounds"
|
||||
|
||||
elif cmd == 'baseframe':
|
||||
assert_num_args('baseframe', nargs, 1, tokens)
|
||||
if tokens[0] != '{':
|
||||
raise MD5FormatError(f"Unexpected token for 'baseframe': {tokens}")
|
||||
mode = "baseframe"
|
||||
|
||||
elif cmd == 'frame':
|
||||
assert_num_args('frame', nargs, 2, tokens)
|
||||
frame_index = int(tokens[0])
|
||||
|
||||
if tokens[1] != '{':
|
||||
raise MD5FormatError(f"Unexpected token for 'frame': {tokens}")
|
||||
if numFrames is None:
|
||||
raise MD5FormatError("'frame' command before 'numFrames'")
|
||||
mode = "frame"
|
||||
joints = []
|
||||
|
||||
else:
|
||||
print(f"Ignored unsupported command: {cmd} {tokens}")
|
||||
|
||||
elif mode == "hierarchy":
|
||||
if cmd == '}':
|
||||
if nargs > 0:
|
||||
raise MD5FormatError(f"Unexpected tokens after 'hierarchy {{}}': {tokens}")
|
||||
mode = "root"
|
||||
else:
|
||||
_, name, line = line.split('"')
|
||||
tokens = line.strip().split(" ")
|
||||
nargs = len(tokens)
|
||||
|
||||
assert_num_args('hierarchy entry', nargs, 3, tokens)
|
||||
|
||||
parent_index = int(tokens[0])
|
||||
flags = int(tokens[1])
|
||||
if flags != 63:
|
||||
raise MD5FormatError(f"Unexpected flags in hierarchy: {flags}")
|
||||
frame_data_index = int(tokens[2])
|
||||
|
||||
hierarchy.append(parent_index)
|
||||
|
||||
elif mode == "bounds":
|
||||
if cmd == '}':
|
||||
if nargs > 0:
|
||||
raise MD5FormatError(f"Unexpected tokens after 'bounds {{}}': {tokens}")
|
||||
mode = "root"
|
||||
else:
|
||||
# Ignore everything else
|
||||
pass
|
||||
|
||||
elif mode == "baseframe":
|
||||
if cmd == '}':
|
||||
if nargs > 0:
|
||||
raise MD5FormatError(f"Unexpected tokens after 'baseframe {{}}': {tokens}")
|
||||
mode = "root"
|
||||
else:
|
||||
values = line.strip().split()
|
||||
assert_num_args('baseframe joint', len(values), 10, values)
|
||||
|
||||
if values[0] != '(':
|
||||
raise MD5FormatError(f"Unexpected token 0 for baseframe': {values}")
|
||||
pos = Vector(float(values[1]), float(values[2]), float(values[3]))
|
||||
if values[4] != ')':
|
||||
raise MD5FormatError(f"Unexpected token 4 for baseframe': {values}")
|
||||
|
||||
if values[5] != '(':
|
||||
raise MD5FormatError(f"Unexpected token 5 for baseframe': {values}")
|
||||
orient = (float(values[6]), float(values[7]), float(values[8]))
|
||||
q_orient = quaternion_fill_incomplete_w(orient)
|
||||
if values[9] != ')':
|
||||
raise MD5FormatError(f"Unexpected token 9 for baseframe': {values}")
|
||||
|
||||
baseframe.append(Joint("", -1, pos, q_orient))
|
||||
|
||||
elif mode == "frame":
|
||||
if cmd == '}':
|
||||
if nargs > 0:
|
||||
raise MD5FormatError(f"Unexpected tokens after 'frame {{}}': {tokens}")
|
||||
mode = "root"
|
||||
|
||||
# Now that the frame has been read, process the real
|
||||
# positions and orientations of the bones before storing
|
||||
# them.
|
||||
|
||||
transformed_joints = []
|
||||
|
||||
for joint, parent_index in zip(joints, hierarchy):
|
||||
if parent_index == -1:
|
||||
# Root bone
|
||||
transformed_joints.append(joint)
|
||||
else:
|
||||
parent_pos = transformed_joints[parent_index].pos
|
||||
parent_orient = transformed_joints[parent_index].orient
|
||||
|
||||
this_pos = joint.pos
|
||||
this_orient = joint.orient
|
||||
|
||||
q = parent_orient
|
||||
qt = q.complement()
|
||||
q_pos_delta = q.mul(this_pos.to_q()).mul(qt)
|
||||
pos_delta = q_pos_delta.to_v3()
|
||||
|
||||
pos = parent_pos.add(pos_delta)
|
||||
orient = parent_orient.mul(this_orient).normalize()
|
||||
|
||||
transformed_joints.append(Joint("", -1, pos, orient))
|
||||
|
||||
frames[frame_index] = transformed_joints
|
||||
else:
|
||||
values = line.strip().split()
|
||||
assert_num_args('frame joint', len(values), 6, values)
|
||||
|
||||
pos = Vector(float(values[0]), float(values[1]), float(values[2]))
|
||||
|
||||
orient = (float(values[3]), float(values[4]), float(values[5]))
|
||||
q_orient = quaternion_fill_incomplete_w(orient)
|
||||
|
||||
joints.append(Joint("", -1, pos, q_orient))
|
||||
|
||||
if mode != "root":
|
||||
raise MD5FormatError("Unexpected end of file (expected '}')")
|
||||
|
||||
realJoints = len(joints)
|
||||
if numJoints != realJoints:
|
||||
raise MD5FormatError(f"Incorrect number of joints: {numJoints} != {realJoints}")
|
||||
|
||||
realFrames = len(frames)
|
||||
if numFrames != realFrames:
|
||||
raise MD5FormatError(f"Incorrect number of frames: {numFrames} != {realFrames}")
|
||||
|
||||
return frames
|
||||
|
||||
def save_animation(frames, output_file, blender_fix):
|
||||
|
||||
version = 1
|
||||
num_frames = len(frames)
|
||||
num_bones = len(frames[0])
|
||||
|
||||
u32_array = [version, num_frames, num_bones]
|
||||
|
||||
for joints in frames:
|
||||
if num_bones != len(joints):
|
||||
raise MD5FormatError("Different number of bones across frames")
|
||||
|
||||
for joint in joints:
|
||||
this_pos = joint.pos
|
||||
this_orient = joint.orient
|
||||
|
||||
if blender_fix:
|
||||
# It is needed to rotate all bones because all bones have
|
||||
# absolute transformations. Rotate orientation and position by
|
||||
# -90 degrees on the X axis.
|
||||
q_rot = Quaternion(0.7071068, -0.7071068, 0, 0)
|
||||
this_orient = q_rot.mul(this_orient)
|
||||
this_pos = Vector(this_pos.x, this_pos.z, -this_pos.y)
|
||||
|
||||
pos = [float_to_f32(this_pos.x), float_to_f32(this_pos.y),
|
||||
float_to_f32(this_pos.z)]
|
||||
orient = [float_to_f32(this_orient.w), float_to_f32(this_orient.x),
|
||||
float_to_f32(this_orient.y), float_to_f32(this_orient.z)]
|
||||
|
||||
u32_array.extend(pos)
|
||||
u32_array.extend(orient)
|
||||
|
||||
with open(output_file, "wb") as f:
|
||||
for u32 in u32_array:
|
||||
b = [u32 & 0xFF, \
|
||||
(u32 >> 8) & 0xFF, \
|
||||
(u32 >> 16) & 0xFF, \
|
||||
(u32 >> 24) & 0xFF]
|
||||
f.write(bytearray(b))
|
||||
|
||||
def convert_md5mesh(model_file, name, output_folder, texture_size,
|
||||
draw_normal_polygons, suffix, blender_fix,
|
||||
export_base_pose):
|
||||
|
||||
print(f"Converting model: {model_file}")
|
||||
|
||||
# Parse md5mesh file
|
||||
joints, meshes = parse_md5mesh(model_file)
|
||||
|
||||
print(f"Loaded {len(joints)} joint(s) and {len(meshes)} mesh(es).")
|
||||
|
||||
if len(meshes) > 1:
|
||||
print("WARNING: More than one mesh found. All meshes will share the same "
|
||||
"texture. If you want them to have different textures, you must use "
|
||||
"multiple .md5mesh files.")
|
||||
|
||||
if export_base_pose:
|
||||
print("Converting base pose...")
|
||||
|
||||
save_animation([joints],
|
||||
os.path.join(output_folder, f"{name}.dsa{suffix}"),
|
||||
blender_fix)
|
||||
|
||||
print("Converting meshes...")
|
||||
|
||||
# Display list shared between all meshes
|
||||
dl = DisplayList()
|
||||
dl.switch_vtxs("triangles")
|
||||
|
||||
base_matrix = 30 - len(joints) + 1
|
||||
last_joint_index = None
|
||||
|
||||
for mesh in meshes:
|
||||
print(f" Vertices: {mesh.numverts}")
|
||||
print(f" Tris: {mesh.numtris}")
|
||||
print(f" Weights: {mesh.numweights}")
|
||||
|
||||
print(" Generating per-triangle normals...")
|
||||
|
||||
tri_normal = []
|
||||
for tri in mesh.tris:
|
||||
verts = [mesh.verts[i] for i in tri]
|
||||
weights = [mesh.weights[v.startWeight] for v in verts]
|
||||
|
||||
vtx = []
|
||||
for vert, weight in zip(verts, weights):
|
||||
joint = joints[weight.joint]
|
||||
m = joint_info_to_m4x3(joint.orient, joint.pos)
|
||||
final = weight.pos.mul_m4x3(m)
|
||||
vtx.append(final)
|
||||
|
||||
a = vtx[0].sub(vtx[1])
|
||||
b = vtx[1].sub(vtx[2])
|
||||
|
||||
n = a.cross(b)
|
||||
|
||||
if n.length() > 0:
|
||||
n = n.normalize()
|
||||
tri_normal.append(n)
|
||||
else:
|
||||
tri_normal.append(Vector(0, 0, 0))
|
||||
|
||||
print(" Generating display list...")
|
||||
|
||||
for tri, norm in zip(mesh.tris, tri_normal):
|
||||
verts = [mesh.verts[i] for i in tri]
|
||||
weights = [mesh.weights[v.startWeight] for v in verts]
|
||||
|
||||
finals = []
|
||||
|
||||
for vert, weight in zip(verts, weights):
|
||||
|
||||
# Texture
|
||||
# -------
|
||||
|
||||
st = vert.st
|
||||
|
||||
u = st[0] * texture_size[0]
|
||||
v = st[1] * texture_size[1]
|
||||
dl.texcoord(u, v)
|
||||
|
||||
# Vertex and normal
|
||||
# -----------------
|
||||
|
||||
# Load joint matrix. When drawing normal polygons it has to be
|
||||
# loaded every time, because drawing the normal restores the
|
||||
# original matrix.
|
||||
|
||||
joint_index = weight.joint
|
||||
if draw_normal_polygons or joint_index != last_joint_index:
|
||||
dl.mtx_restore(base_matrix + joint_index)
|
||||
last_joint_index = joint_index
|
||||
|
||||
# Calculate normal in joint space
|
||||
|
||||
joint = joints[joint_index]
|
||||
|
||||
q = joint.orient
|
||||
qt = q.complement()
|
||||
n = norm.to_q()
|
||||
|
||||
# Transform by the inverted quaternion
|
||||
n = qt.mul(n).mul(q).to_v3()
|
||||
if n.length() > 0:
|
||||
n = n.normalize()
|
||||
dl.normal(n.x, n.y, n.z)
|
||||
|
||||
# The vertex is already in joint space
|
||||
|
||||
dl.vtx(weight.pos.x, weight.pos.y, weight.pos.z)
|
||||
|
||||
if draw_normal_polygons:
|
||||
# Calculate actual location of the vertex so that the
|
||||
# vertices of the triangle can be averaged as origin of the
|
||||
# normal polygon.
|
||||
q = joint.orient
|
||||
qt = q.complement()
|
||||
v = weight.pos.to_q()
|
||||
|
||||
delta = q.mul(v).mul(qt).to_v3()
|
||||
|
||||
final = joint.pos.add(delta)
|
||||
finals.append(final)
|
||||
|
||||
if draw_normal_polygons:
|
||||
|
||||
# Don't use any of the joint transformation matrices
|
||||
dl.mtx_restore(1)
|
||||
|
||||
vert_avg = Vector(
|
||||
(finals[0].x + finals[1].x + finals[2].x) / 3,
|
||||
(finals[0].y + finals[1].y + finals[2].y) / 3,
|
||||
(finals[0].z + finals[1].z + finals[2].z) / 3
|
||||
)
|
||||
|
||||
vert_avg_end = vert_avg.add(norm)
|
||||
|
||||
dl.texcoord(0, 0)
|
||||
|
||||
dl.color(1, 0, 0)
|
||||
dl.vtx(vert_avg.x + 0.1, vert_avg.y, vert_avg.z)
|
||||
dl.vtx(vert_avg.x, vert_avg.y, vert_avg.z)
|
||||
dl.color(0, 1, 0)
|
||||
dl.vtx(vert_avg_end.x, vert_avg_end.y, vert_avg_end.z)
|
||||
|
||||
dl.color(1, 0, 0)
|
||||
dl.vtx(vert_avg.x, vert_avg.y, vert_avg.z)
|
||||
dl.vtx(vert_avg.x, vert_avg.y + 0.1, vert_avg.z)
|
||||
dl.color(0, 1, 0)
|
||||
dl.vtx(vert_avg_end.x, vert_avg_end.y, vert_avg_end.z)
|
||||
|
||||
dl.color(1, 0, 0)
|
||||
dl.vtx(vert_avg.x, vert_avg.y, vert_avg.z)
|
||||
dl.vtx(vert_avg.x, vert_avg.y, vert_avg.z + 0.1)
|
||||
dl.color(0, 1, 0)
|
||||
dl.vtx(vert_avg_end.x, vert_avg_end.y, vert_avg_end.z)
|
||||
|
||||
dl.end_vtxs()
|
||||
dl.finalize()
|
||||
|
||||
dl.save_to_file(os.path.join(output_folder, f"{name}.dsm{suffix}"))
|
||||
|
||||
|
||||
def convert_md5anim(name, output_folder, anim_file, skip_frames, suffix,
|
||||
blender_fix):
|
||||
|
||||
print(f"Converting animation: {anim_file}")
|
||||
|
||||
frames = parse_md5anim(anim_file)
|
||||
|
||||
# Create name of animation based on file name
|
||||
file_basename = os.path.basename(anim_file).replace(".md5anim", "")
|
||||
anim_name = file_basename.replace(".", "_").lower()
|
||||
|
||||
frames = frames[::skip_frames+1]
|
||||
save_animation(frames, os.path.join(output_folder,
|
||||
f"{name}_{anim_name}.dsa{suffix}"), blender_fix)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
print("md5_to_dsma v0.1.0")
|
||||
print("Copyright (c) 2022 Antonio Niño Díaz <antonio_nd@outlook.com>")
|
||||
print("All rights reserved")
|
||||
print("")
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Converts md5mesh and md5anim files into DSM and DSA files.')
|
||||
|
||||
# Required arguments
|
||||
parser.add_argument("--name", required=True,
|
||||
help="model name to be used in output files")
|
||||
parser.add_argument("--output", required=True,
|
||||
help="output folder")
|
||||
|
||||
# Optional arguments
|
||||
parser.add_argument("--model", required=False, type=str, default=None,
|
||||
help="input md5mesh file")
|
||||
parser.add_argument("--texture", required=False, type=int, default=[],
|
||||
nargs="+", action="extend",
|
||||
help="texture width and height (e.g. '--texture 32 64')")
|
||||
parser.add_argument("--anims", required=False, type=str, default=[],
|
||||
nargs="+", action="extend",
|
||||
help="list of md5anim files to convert")
|
||||
parser.add_argument("--bin", required=False,
|
||||
action='store_true',
|
||||
help="add '.bin' to the name of the output files")
|
||||
parser.add_argument("--blender-fix", required=False,
|
||||
action='store_true',
|
||||
help="rotate model -90 degrees on X axis to match Blender's orientation")
|
||||
parser.add_argument("--export-base-pose", required=False,
|
||||
action='store_true',
|
||||
help="export base pose of a md5mesh as a DSA file")
|
||||
parser.add_argument("--skip-frames", required=False,
|
||||
default=0, type=int,
|
||||
help="number of frames to skip in an animation (0 = export all, 1 = export half, 2 = export 33%, etc)")
|
||||
parser.add_argument("--draw-normal-polygons", required=False,
|
||||
action='store_true',
|
||||
help="draw polygons with the shape of normals for debugging")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.model is not None:
|
||||
if len(args.texture) != 2:
|
||||
print("Please, provide exactly 2 values to the --texture argument")
|
||||
sys.exit(1)
|
||||
|
||||
if not is_valid_texture_size(args.texture[0]):
|
||||
print(f"Invalid texture width. Valid values: {VALID_TEXTURE_SIZES}")
|
||||
sys.exit(1)
|
||||
|
||||
if not is_valid_texture_size(args.texture[1]):
|
||||
print(f"Invalid texture height. Valid values: {VALID_TEXTURE_SIZES}")
|
||||
sys.exit(1)
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
os.makedirs(args.output, exist_ok=True)
|
||||
|
||||
# Add '.bin' to the name of the files if requested
|
||||
suffix = ".bin" if args.bin else ""
|
||||
|
||||
try:
|
||||
if args.model is not None:
|
||||
convert_md5mesh(args.model, args.name, args.output, args.texture,
|
||||
args.draw_normal_polygons, suffix, args.blender_fix,
|
||||
args.export_base_pose)
|
||||
|
||||
for anim_file in args.anims:
|
||||
convert_md5anim(args.name, args.output, anim_file, args.skip_frames,
|
||||
suffix, args.blender_fix)
|
||||
|
||||
except BaseException as e:
|
||||
print("ERROR: " + str(e))
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
except MD5FormatError as e:
|
||||
print("ERROR: Invalid MD5 file: " + str(e))
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
print("Done!")
|
||||
|
||||
sys.exit(0)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,22 +0,0 @@
|
||||
// NDSModelExporter by Kasikiare 2008;
|
||||
// Using bmp2bin (libNDS - http://www.devkitpro.org)
|
||||
// Irrlicht OpenSource Library (irrlicht.sourceforge.net)
|
||||
// and NDS_Mesh_Converter (by PadrinatoR).
|
||||
//
|
||||
// Greetings to Dark Knight Ez for DisplayList library. :D
|
||||
|
||||
1.Meshes and Textures/Images to convert must be placed in the root directory
|
||||
of the aplication. Ex.:
|
||||
|
||||
C:\NDS Programs\NDSModelExporter\
|
||||
|
||||
2.Result files will be writed on the root directory of the aplication.
|
||||
3.The output names will have the following format
|
||||
|
||||
<OriginalName>_<OriginalFormat>.bin
|
||||
|
||||
4.Green colored textFields in Textures Tab/Panel show that the bmp2bin.exe
|
||||
was correctly launched but not that the output file is correct. Check result
|
||||
sizes.
|
||||
|
||||
5.The "Convert" button takes effect only over the Tab that is been shown.
|
@ -1 +0,0 @@
|
||||
java -jar NDSModelExporter.jar
|
3
tools/nds_model_exporter/dlfixer/.gitignore
vendored
3
tools/nds_model_exporter/dlfixer/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
*.o
|
||||
dlfixer
|
||||
dlfixer.exe
|
@ -1,38 +0,0 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2008-2011, 2019, Antonio Niño Díaz
|
||||
|
||||
# Variables
|
||||
|
||||
NAME := dlfixer
|
||||
# Either leave the extension empty or assign .exe to it for Windows
|
||||
EXT :=
|
||||
|
||||
CFLAGS := -g -Wall
|
||||
RM := rm -rf
|
||||
|
||||
# Rules to build the binary
|
||||
|
||||
all: $(NAME)$(EXT)
|
||||
|
||||
OBJS := dlfixer.o
|
||||
|
||||
$(NAME)$(EXT): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
# Target used to remove all files generated by other Makefile targets
|
||||
|
||||
clean:
|
||||
$(RM) $(NAME) $(NAME).exe $(OBJS)
|
||||
|
||||
# Targets to cross-compile Windows binaries from Linux. Not used to compile
|
||||
# natively from Windows.
|
||||
|
||||
mingw32:
|
||||
make CC=i686-w64-mingw32-gcc EXT=.exe
|
||||
|
||||
mingw64:
|
||||
make CC=x86_64-w64-mingw32-gcc EXT=.exe
|
@ -1,254 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2008-2011, 2019, Antonio Niño Díaz
|
||||
|
||||
// Display List Fixer v0.3
|
||||
//
|
||||
// It removes color commands from a Display list to make lights work.
|
||||
//
|
||||
// Designed for NDS Mesh Converter by PadrinatoR
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
unsigned char COMMAND_ARGUMENTS[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, //0h
|
||||
0, 0, 0, 0, 0, 0, 0, 0, //8h
|
||||
1, 0, 1, 1, 1, 0, 16, 12, //10h
|
||||
16, 12, 9, 3, 3, 0, 0, 0, //18h
|
||||
1, 1, 1, 2, 1, 1, 1, 1, //20h
|
||||
1, 1, 1, 1, 0, 0, 0, 0, //28h
|
||||
1, 1, 1, 1, 32, 0, 0, 0, //30h
|
||||
0, 0, 0, 0, 0, 0, 0, 0, //38h
|
||||
1, 0, 0, 0, 0, 0, 0, 0, //40h
|
||||
0, 0, 0, 0, 0, 0, 0, 0, //48h
|
||||
1, 0, 0, 0, 0, 0, 0, 0, //50h
|
||||
0, 0, 0, 0, 0, 0, 0, 0, //58h
|
||||
1, 0, 0, 0, 0, 0, 0, 0, //60h
|
||||
0, 0, 0, 0, 0, 0, 0, 0, //68h
|
||||
3, 2, 1 //70h
|
||||
};
|
||||
|
||||
unsigned char Command_Arguments[4];
|
||||
int Arguments;
|
||||
unsigned char Command[4];
|
||||
unsigned int FILE_SIZE;
|
||||
void *Data_Buffer;
|
||||
unsigned int *Pointer;
|
||||
|
||||
// Command ID to remove from the display list
|
||||
int Target_Command;
|
||||
|
||||
void Divide_Commands()
|
||||
{
|
||||
Command[0] = ((unsigned int)*Pointer >> 0) & 0xFF;
|
||||
Command[1] = ((unsigned int)*Pointer >> 8) & 0xFF;
|
||||
Command[2] = ((unsigned int)*Pointer >> 16) & 0xFF;
|
||||
Command[3] = ((unsigned int)*Pointer >> 24) & 0xFF;
|
||||
|
||||
Command_Arguments[0] = COMMAND_ARGUMENTS[Command[0]];
|
||||
Command_Arguments[1] = COMMAND_ARGUMENTS[Command[1]];
|
||||
Command_Arguments[2] = COMMAND_ARGUMENTS[Command[2]];
|
||||
Command_Arguments[3] = COMMAND_ARGUMENTS[Command[3]];
|
||||
|
||||
Arguments =
|
||||
Command_Arguments[0] + Command_Arguments[1] + Command_Arguments[2] +
|
||||
Command_Arguments[3];
|
||||
}
|
||||
|
||||
void Debug()
|
||||
{
|
||||
int size = (unsigned int)*Pointer;
|
||||
Pointer++;
|
||||
|
||||
while (size > 0) {
|
||||
// Divide commands
|
||||
Command[0] = ((unsigned int)*Pointer >> 0) & 0xFF;
|
||||
Command[1] = ((unsigned int)*Pointer >> 8) & 0xFF;
|
||||
Command[2] = ((unsigned int)*Pointer >> 16) & 0xFF;
|
||||
Command[3] = ((unsigned int)*Pointer >> 24) & 0xFF;
|
||||
|
||||
printf("\nCommands: %02x, %02x, %02x, %02x (%08x)\n",
|
||||
Command[0], Command[1], Command[2], Command[3],
|
||||
*Pointer);
|
||||
|
||||
Command_Arguments[0] = COMMAND_ARGUMENTS[Command[0]];
|
||||
Command_Arguments[1] = COMMAND_ARGUMENTS[Command[1]];
|
||||
Command_Arguments[2] = COMMAND_ARGUMENTS[Command[2]];
|
||||
Command_Arguments[3] = COMMAND_ARGUMENTS[Command[3]];
|
||||
Arguments =
|
||||
Command_Arguments[0] + Command_Arguments[1] +
|
||||
Command_Arguments[2] + Command_Arguments[3];
|
||||
|
||||
printf("Argument Number: %02d, %02d, %02d, %02d (%d)\n",
|
||||
Command_Arguments[0], Command_Arguments[1],
|
||||
Command_Arguments[2], Command_Arguments[3], Arguments);
|
||||
|
||||
Pointer++;
|
||||
size--;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (Command[i] == Target_Command)
|
||||
printf("\nColor detected!\n");
|
||||
|
||||
Pointer += Command_Arguments[i];
|
||||
size -= Command_Arguments[i];
|
||||
}
|
||||
}
|
||||
free(Data_Buffer);
|
||||
printf("\n\nEnd!\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FILE *ModelFile;
|
||||
unsigned int *Out_Pointer;
|
||||
unsigned int *Output_File;
|
||||
|
||||
printf("dlfixer v1.0\n");
|
||||
printf("\n");
|
||||
printf("Copyright (c) 2008-2011, 2019 Antonio Nino Diaz\n");
|
||||
printf("\n");
|
||||
|
||||
// Load file
|
||||
ModelFile = fopen("model.bin", "rb+");
|
||||
if (ModelFile == NULL) {
|
||||
printf("model.bin not found!\n");
|
||||
return (-1);
|
||||
}
|
||||
fseek(ModelFile, 0, SEEK_END);
|
||||
FILE_SIZE = ftell(ModelFile);
|
||||
rewind(ModelFile);
|
||||
Data_Buffer = (char *)malloc(sizeof(char) * FILE_SIZE);
|
||||
fread(Data_Buffer, 1, FILE_SIZE, ModelFile);
|
||||
fclose(ModelFile);
|
||||
|
||||
Pointer = (unsigned int *)Data_Buffer;
|
||||
|
||||
if (argc > 1) {
|
||||
if ((strcmp((char *)argv[1], "d") * strcmp((char *)argv[1], "D")) == 0) {
|
||||
printf("\n\nDebug Mode\n----------\n");
|
||||
Target_Command = 0x20;
|
||||
Debug();
|
||||
} else if ((strcmp((char *)argv[1], "c") * strcmp((char *)argv[1], "C")) == 0) {
|
||||
printf("\n\nRemove Color\n----------\n");
|
||||
Target_Command = 0x20;
|
||||
} else if ((strcmp((char *)argv[1], "n") * strcmp((char *)argv[1], "N")) == 0) {
|
||||
printf("\n\nRemove Normals\n----------\n");
|
||||
Target_Command = 0x21;
|
||||
} else {
|
||||
printf
|
||||
("\n\nUse: program.exe [c/n] (Remove color/normals).\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Out_Pointer = Output_File = (unsigned int *)malloc(FILE_SIZE + 2);
|
||||
|
||||
FILE_SIZE = 0;
|
||||
int size = (unsigned int)*Pointer;
|
||||
Pointer++;
|
||||
Out_Pointer++;
|
||||
|
||||
while (size > 0) {
|
||||
Divide_Commands();
|
||||
|
||||
bool target_detected = false;
|
||||
bool slotdetected[4];
|
||||
|
||||
// Check commands
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (Command[i] == Target_Command) {
|
||||
target_detected = true;
|
||||
slotdetected[i] = true;
|
||||
} else
|
||||
slotdetected[i] = false;
|
||||
}
|
||||
|
||||
// Fix
|
||||
if (target_detected) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (Command[i] != Target_Command)
|
||||
continue;
|
||||
|
||||
if (i == 3) {
|
||||
Command[3] = 0;
|
||||
} else {
|
||||
for (int j = i; j < 3; j++) {
|
||||
Command[j] = Command[j + 1];
|
||||
}
|
||||
Command[3] = 0;
|
||||
}
|
||||
}
|
||||
unsigned int newcommand =
|
||||
(Command[3] << 24) + (Command[2] << 16) +
|
||||
(Command[1] << 8) + (Command[0]);
|
||||
|
||||
*Out_Pointer = newcommand;
|
||||
Out_Pointer++;
|
||||
FILE_SIZE++;
|
||||
Pointer++;
|
||||
size--;
|
||||
// Save arguments
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (slotdetected[i] == false) {
|
||||
if (Command_Arguments[i] == 0)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < Command_Arguments[i]; j++) {
|
||||
*Out_Pointer = (unsigned int)*Pointer;
|
||||
Out_Pointer++;
|
||||
FILE_SIZE++;
|
||||
Pointer++;
|
||||
size--;
|
||||
}
|
||||
} else {
|
||||
// FIFO_COLOR has always one argument
|
||||
Pointer++;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*Out_Pointer = (unsigned int)*Pointer;
|
||||
Out_Pointer++;
|
||||
FILE_SIZE++;
|
||||
Pointer++;
|
||||
size--;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (Command_Arguments[i] == 0)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < Command_Arguments[i]; j++) {
|
||||
*Out_Pointer = (unsigned int)*Pointer;
|
||||
Out_Pointer++;
|
||||
FILE_SIZE++;
|
||||
Pointer++;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*Output_File = FILE_SIZE;
|
||||
|
||||
// Create new file
|
||||
ModelFile = fopen("model_out.bin", "wb+");
|
||||
if (ModelFile == NULL) {
|
||||
printf("model_out.bin couldn't be created!\n");
|
||||
free(Data_Buffer);
|
||||
free((void *)Output_File);
|
||||
return (-1);
|
||||
}
|
||||
fwrite(Output_File, 4, FILE_SIZE + 1, ModelFile);
|
||||
fclose(ModelFile);
|
||||
|
||||
free(Data_Buffer);
|
||||
free((void *)Output_File);
|
||||
|
||||
printf("Done!\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
After converting any model with NDS Mesh Converter rename it to "model.bin".
|
||||
Then execute RemoveColor.bat. This will fix it so that it is affected by lights.
|
||||
The fixed model will be output to "model_out.bin".
|
@ -1 +0,0 @@
|
||||
DLFixer.exe c
|
@ -1,7 +0,0 @@
|
||||
NDSModelExporter is a tool not created as part of Nitro Engine, and it is
|
||||
provided here for conveniency. It supports more formats than MD2, but it exports
|
||||
models with color commands included. This means that normal commands are
|
||||
overriden and lights don't work on the models exported by this converter.
|
||||
|
||||
In order to make them work, you need to pass them on to dlfixer so that it
|
||||
removes the color commands.
|
20
tools/readme.rst
Normal file
20
tools/readme.rst
Normal file
@ -0,0 +1,20 @@
|
||||
Nitro Engine Tools
|
||||
==================
|
||||
|
||||
The following tools are used to export models created on the PC to the NDS:
|
||||
|
||||
- **Nitro_Texture_Converter**
|
||||
|
||||
It converts any PNG into any DS texture format (except compressed format).
|
||||
It uses the alpha channel (if any) for texture transparency.
|
||||
|
||||
- **obj2dl**
|
||||
|
||||
Converts a Wavefront OBJ file into a NDS display list.
|
||||
|
||||
- **md5_to_dsma**
|
||||
|
||||
Converts MD5 models with skeletal animation (md5mesh and md5anim files) into a
|
||||
format that allows them to be displayed on the NDS efficiently.
|
||||
|
||||
https://github.com/AntonioND/dsma-library
|
@ -1,21 +0,0 @@
|
||||
Those tools are used to export models created on the PC to the NDS:
|
||||
|
||||
Made for Nitro Engine:
|
||||
|
||||
- Nitro_Texture_Converter:
|
||||
It converts any PNG into any DS texture format (except compressed format).
|
||||
It uses the alpha channel (if any) for texture transparency.
|
||||
|
||||
- MD2_2_BIN:
|
||||
Exports the first frame of an MD2 model to a NDS display list. This is more
|
||||
optimized than NDS_Model_Exporter.
|
||||
|
||||
- MD2_2_NEA:
|
||||
Exports every frame of an MD2 model to a NEA file that can be used by Nitro
|
||||
Engine.
|
||||
|
||||
Made by others:
|
||||
|
||||
- NDS_Model_Exporter:
|
||||
Windows only! It exports models in some formats to NDS display lists. It can
|
||||
export textures too, but only to RGB format.
|
Loading…
Reference in New Issue
Block a user