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:
Antonio Niño Díaz 2022-10-11 01:38:58 +01:00
parent 395e3672e1
commit 6e344141e3
31 changed files with 1203 additions and 2079 deletions

View File

@ -1,3 +0,0 @@
*.o
md2_to_bin
md2_to_bin.exe

View File

@ -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

View File

@ -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},

View File

@ -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];
}

View File

@ -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__

View File

@ -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;
}

View File

@ -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__

View File

@ -1,3 +0,0 @@
*.o
md2_to_nea
md2_to_nea.exe

View File

@ -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

View File

@ -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},

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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__

View File

@ -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;
}

View 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
View 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)

View File

@ -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.

View File

@ -1 +0,0 @@
java -jar NDSModelExporter.jar

View File

@ -1,3 +0,0 @@
*.o
dlfixer
dlfixer.exe

View File

@ -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

View File

@ -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;
}

View File

@ -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".

View File

@ -1 +0,0 @@
DLFixer.exe c

View File

@ -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
View 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

View File

@ -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.