Created
March 18, 2009 22:21
-
-
Save jamesu/81445 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Hey there, | |
I writ this code a few months ago, in order to familiarize myself with the quake3 code. Rather than let it rot away in the code dungeon, i thought you guy's might find some use for it. | |
The following modification allows Quake3 to load Quake1 MDL's and Quake2 MD2's. This is done at runtime via a step-by-step in-memory conversion process, which converts the source models into Quake3 MD3's. | |
I got the MD2 conversion going on pretty well - successfully loaded in some Quake2 models complete with original textures, although my testing has been limited as i am not too familiar with the game code (which would allow me to try out replacing actual player models). | |
The MDL converter does not work as well (does not convert skins and has scaling issues), but i was still able to display the Quake1 player model in-game as a powerup. | |
Neither converters convert tag's since they do not exist in the MDL or MD2 formats (unless i missed something obvious). However, the code is quite straight forward, so i have no doubt that someone would be able to hack support in for any missing features. | |
Due to the structure of both formats, i had to write a converter to remap all of the triangle and vertex/texcoord references into one continuous list for the md3 surfaces. Depending on the amount of triangles in your source meshes, there may be more than one md3 surface generated. | |
Enough with the details, here is the code. | |
*/ | |
//!! In code/qcommon/qfiles.h, just after " } TargaHeader;", add the following: | |
/* | |
============================================================================== | |
.MDL triangle model file format | |
============================================================================== | |
*/ | |
#define MDL_IDENT (('O'<<24)+('P'<<16)+('D'<<8)+'I') | |
#define MDL_VERSION 6 | |
#define MDL_ONSEAM 0x0020 | |
#define MDL_DT_FACES_FRONT 0x0010 | |
#define MDL_MAX_SKINS 32 | |
#define MDL_MAXVERTS 1024 | |
#define MDL_MAXFRAMES 256 | |
#define MDL_MAXTRIS 2048 | |
#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) | |
typedef enum { MDL_SINGLE=0, MDL_GROUP } mdlFrametype_t; | |
typedef enum { MDL_SKIN_SINGLE=0, MDL_SKIN_GROUP } mdlSkintype_t; | |
typedef enum {ST_SYNC=0, ST_RAND } mdlSynctype_t; | |
// structs below here are figured out | |
typedef struct | |
{ | |
mdlSkintype_t type; | |
void *pcachespot; | |
int skin; | |
} mdlSkinDesc_t; | |
typedef struct | |
{ | |
int numskins; | |
int intervals; | |
mdlSkinDesc_t skindescs[1]; | |
} mdlSkingroup_t; | |
// NOTE: the following could be typedef type instead? | |
typedef struct { | |
int numskins; | |
} dmdlSkingroup_t; | |
typedef struct { | |
float interval; | |
} dmdlInterval_t; | |
typedef struct { | |
float interval; | |
} dmdlSkininterval_t; | |
typedef struct { | |
mdlFrametype_t type; | |
} dmdlFrametype_t; | |
typedef struct { | |
mdlSkintype_t type; | |
} dmdlSkintype_t; | |
// NOTE: later versions have these as shorts, then floats | |
typedef struct { | |
int onseam; | |
int s; | |
int t; | |
} mdlSt_t; // | |
typedef struct | |
{ | |
byte v[3]; | |
byte lightnormalindex; | |
} mdlTrivertx_t; | |
typedef struct { | |
int numframes; | |
mdlTrivertx_t bboxmin; // lightnormal isn't used | |
mdlTrivertx_t bboxmax; // lightnormal isn't used | |
} mdlGroup_t; | |
typedef struct { | |
mdlTrivertx_t bboxmin; // lightnormal isn't used | |
mdlTrivertx_t bboxmax; // lightnormal isn't used | |
char name[16]; // frame name from grabbing | |
} mdlFrame_t; | |
typedef struct { | |
int facesfront; | |
int vertindex[3]; | |
} mdlTriangle_t; // | |
typedef struct { | |
int ident; | |
int version; | |
vec3_t scale; | |
vec3_t scale_origin; | |
float boundingradius; | |
vec3_t eyeposition; | |
int numskins; | |
int skinwidth; | |
int skinheight; | |
int numverts; | |
int numtris; | |
int numframes; | |
mdlSynctype_t synctype; | |
int flags; | |
float size; | |
} mdlHeader_t; // | |
/* | |
======================================================================== | |
.MD2 triangle model file format | |
======================================================================== | |
*/ | |
#define MD2_IDENT (('2'<<24)+('P'<<16)+('D'<<8)+'I') | |
#define MD2_VERSION 8 | |
#define MD2_MAX_TRIANGLES 4096 | |
#define MD2_MAX_VERTS 2048 | |
#define MD2_MAX_FRAMES 512 | |
#define MD2_MAX_SKINS 32 | |
#define MD2_MAX_SKINNAME 64 | |
typedef struct | |
{ | |
short s; | |
short t; | |
} md2St_t; | |
typedef struct | |
{ | |
short index_xyz[3]; | |
short index_st[3]; | |
} md2Triangle_t; | |
typedef struct | |
{ | |
byte v[3]; // scaled byte to fit in frame mins/maxs | |
byte lightnormalindex; | |
} md2Trivertx_t; | |
#define MD2_TRIVERTX_V0 0 | |
#define MD2_TRIVERTX_V1 1 | |
#define MD2_TRIVERTX_V2 2 | |
#define MD2_TRIVERTX_LNI 3 | |
#define MD2_TRIVERTX_SIZE 4 | |
typedef struct | |
{ | |
float scale[3]; // multiply byte verts by this | |
float translate[3]; // then add this | |
char name[16]; // frame name from grabbing | |
md2Trivertx_t verts[1]; // variable sized | |
} md2Frame_t; | |
// the glcmd format: | |
// a positive integer starts a tristrip command, followed by that many | |
// vertex structures. | |
// a negative integer starts a trifan command, followed by -x vertexes | |
// a zero indicates the end of the command list. | |
// a vertex consists of a floating point s, a floating point t, | |
// and an integer vertex index. | |
typedef struct | |
{ | |
int ident; | |
int version; | |
int skinwidth; | |
int skinheight; | |
int framesize; // byte size of each frame | |
int numSkins; | |
int numVerts; | |
int numSt; // greater than num_xyz for seams | |
int numTriangles; | |
int numGLcmds; // dwords in strip/fan command list | |
int numFrames; | |
int ofsSkins; // each skin is a MAX_SKINNAME string | |
int ofsSt; // byte offset from start for stverts | |
int ofsTris; // offset for dtriangles | |
int ofsFrames; // offset for first frame | |
int ofsGLcmds; | |
int ofsEnd; // end of file | |
} md2Header_t; | |
//!! In code/renderer/tr_model.c, add the following after "#define LL(x) x=LittleLong(x)": | |
#define LS(x) x=LittleShort(x) | |
#define LF(x) x=LittleFloat(x) | |
static qboolean R_LoadMDL (model_t *mod, int lod, void *buffer, const char *name ); | |
static qboolean R_LoadMD2 (model_t *mod, int lod, void *buffer, const char *name ); | |
//!! We also need to fix RE_RegisterModel in order to allow for the MDl/MD2 files to be loaded. | |
//!! just above "if ( lod != 0 ) {", add the following: | |
// Use lod's only for md3's | |
if ( lod == 1) | |
{ | |
const char *ext = strrchr(filename, '.'); | |
if (ext && strcmp(ext, ".md3")) | |
break; | |
} | |
// Hook in lod names | |
//!! Then replace everything between "ident = LittleLong(*(unsigned *)buf);" and "ri.FS_FreeFile (buf);" with the following: | |
switch (ident) | |
{ | |
case MD3_IDENT: | |
loaded = R_LoadMD3( mod, lod, buf, name ); | |
break; | |
case MD4_IDENT: | |
loaded = R_LoadMD4( mod, buf, name ); | |
break; | |
case MD2_IDENT: | |
loaded = R_LoadMD2( mod, lod, buf, name ); | |
break; | |
case MDL_IDENT: | |
loaded = R_LoadMDL( mod, lod, buf, name); | |
break; | |
default: | |
ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", name); | |
goto fail; | |
} | |
//!! In the same file, we can now add in the model loading functions, as follows: | |
// JamesU - MDL/MD2 loading code | |
/* | |
================= | |
Alias model vertex map | |
Structures hold a list of triangle indices pointing to originals, grouped by surfaces for MD3 models. | |
Functions build this list from a set of MD2 triangle indices. | |
NOTE: | |
Structures and the routines could be optimized to minimize memory usage. | |
An initialized vertex map requires around 87kb of temporary memory (or 562kb with 32 surfaces). | |
================= | |
*/ | |
#define ALIAS_VERTEXMAP_MAX_SURFACES 5 // Realistic maximum surfaces we are going to generate from an MD2 | |
typedef struct { | |
short verts[SHADER_MAX_VERTEXES]; // pointers to vertex_map (no duplicates) | |
short inds[SHADER_MAX_INDEXES]; // pointers to verts (in triangles) | |
short nverts; // number of verts | |
short ninds; // number of inds | |
} aliasSurfaceVerts_t; | |
typedef struct { | |
short vertex_map[ALIAS_VERTEXMAP_MAX_SURFACES * SHADER_MAX_VERTEXES]; // map of new texcoords -> old vertex | |
short vertex_st[ALIAS_VERTEXMAP_MAX_SURFACES * SHADER_MAX_VERTEXES]; // map of new texcoords -> old texcoord | |
aliasSurfaceVerts_t new_surfaces[ALIAS_VERTEXMAP_MAX_SURFACES]; // list of surfaces with split vertex lists | |
short nverts; // # of vertexes used in vertex_map and new_st arrays | |
short nsurfs; // # of indices used in surface_split | |
} aliasVertexmap_t; | |
qboolean R_VMAPAddExisting(aliasVertexmap_t *vmap, short nvtx_new) { | |
aliasSurfaceVerts_t *surf = &vmap->new_surfaces[vmap->nsurfs]; | |
aliasSurfaceVerts_t *splitsurf = NULL; | |
int surf_idx; // index of vertex to add to inds list | |
// Check vertexes | |
for (surf_idx=0; surf_idx<surf->nverts; surf_idx++) | |
{ | |
if (surf->verts[surf_idx] == nvtx_new) | |
break; | |
} | |
if (surf_idx == surf->nverts) | |
{ | |
// New vertex referenced | |
if (surf->nverts == SHADER_MAX_VERTEXES) | |
{ | |
if (vmap->nsurfs == ALIAS_VERTEXMAP_MAX_SURFACES) | |
surf = NULL; | |
else | |
{ | |
// fix splits | |
int splitat = surf->ninds % 3; | |
if (splitat != 0) | |
{ | |
splitsurf = surf; | |
splitsurf->ninds -= splitat; | |
surf++; | |
vmap->nsurfs++; | |
surf->ninds = splitat; | |
surf->nverts = 1; | |
surf_idx = 0; | |
if (splitat == 1) | |
{ | |
surf->inds[0] = 0; | |
surf->verts[0] = splitsurf->verts[splitsurf->inds[splitsurf->ninds]]; | |
surf->nverts = splitat; | |
} | |
else | |
{ | |
surf->inds[0] = 0; | |
surf->verts[0] = splitsurf->verts[splitsurf->inds[splitsurf->ninds]]; | |
if (surf->inds[0] != surf->inds[1]) { | |
// Extra unique vertex | |
surf->verts[surf->nverts++] = splitsurf->verts[splitsurf->inds[splitsurf->ninds+1]]; // another vertex | |
surf->nverts++; | |
surf->inds[1] = 1; | |
} | |
else | |
surf->inds[1] = 0; | |
} | |
surf_idx = surf->nverts; | |
} | |
else | |
{ | |
// regular new surface | |
surf++; | |
vmap->nsurfs++; | |
surf->nverts = 1; | |
surf->verts[0] = nvtx_new; | |
surf->ninds = 0; | |
surf_idx = 0; | |
} | |
} | |
} | |
else if (surf != NULL) | |
{ | |
// Add vertex (existing surface) | |
surf_idx = surf->nverts; | |
surf->verts[surf->nverts++] = nvtx_new; | |
} | |
} | |
// Check indices (no splits here since they are divisible by 3) | |
if (surf != NULL && surf->ninds == SHADER_MAX_INDEXES) | |
{ | |
if (vmap->nsurfs == ALIAS_VERTEXMAP_MAX_SURFACES) | |
surf = NULL; | |
else | |
{ | |
// New surface, add vertex | |
surf++; | |
vmap->nsurfs++; | |
surf->nverts = 1; | |
surf->verts[0] = nvtx_new; | |
surf->ninds = 0; | |
surf_idx = 0; | |
} | |
} | |
if (surf == NULL) | |
{ | |
ri.Error (ERR_DROP, "R_LoadMD2: model needs too many surfaces\n"); | |
return qfalse; | |
} | |
surf->inds[surf->ninds++] = surf_idx; | |
return qtrue; | |
} | |
qboolean R_VMAPAddMapping(aliasVertexmap_t *vmap, int nvtx, int ntx) { | |
// Add stuff to vertex map | |
vmap->vertex_map[vmap->nverts] = nvtx; | |
vmap->vertex_st[vmap->nverts] = ntx; | |
if (!R_VMAPAddExisting(vmap, vmap->nverts)) | |
return qfalse; | |
// check for illegal immigrants | |
if (vmap->nverts != ALIAS_VERTEXMAP_MAX_SURFACES * SHADER_MAX_VERTEXES) | |
vmap->nverts++; | |
else | |
return qfalse; | |
return qtrue; | |
} | |
qboolean R_VMAPAddVertexIndex(aliasVertexmap_t *vmap, short nvtx, short ntxc) { | |
int i; | |
// Find vertex idx in list | |
for (i=0; i<vmap->nverts; i++) | |
{ | |
if (vmap->vertex_map[i] == nvtx) | |
{ | |
// Vertex in map, check the texcoord | |
if (vmap->vertex_st[i] == ntxc) | |
// Yay, add indicie | |
return R_VMAPAddExisting(vmap, i); | |
else | |
// Boo, add duplicate vertex with new texcoord | |
return R_VMAPAddMapping(vmap, nvtx, ntxc); | |
} | |
} | |
// All else failed, add new mapping | |
return R_VMAPAddMapping(vmap, nvtx, ntxc); | |
} | |
static qboolean R_CreateMDLVertexMap(aliasVertexmap_t *vmap, int numTriangles, short numVerts, mdlTriangle_t *start, mdlSt_t *stverts) { | |
int i, y; | |
qboolean result; | |
// | |
// Create a vertex index map to provide duplicated texture coords | |
// | |
// This is slightly different to the Q2 version, as the ST list is treated as twice as big, to account for mirror st coords. | |
vmap->nverts = 0; | |
vmap->nsurfs = 0; | |
vmap->new_surfaces[0].ninds = 0; | |
vmap->new_surfaces[0].nverts = 0; | |
// Begin the iteration through the triangles | |
for (i=0 ; i<numTriangles ; i++) | |
{ | |
int facesfront = LittleLong(start[i].facesfront); | |
for (y=0; y<3; y++) | |
{ | |
// NOTE: mdl indices are 32bit, whereas the vertex map indices are 16bit | |
// although it is unlikely there will be 65536+ indices in an mdl! | |
int vidx = LittleLong(start[i].vertindex[y]); | |
if (!facesfront && stverts[vidx].onseam) | |
result = R_VMAPAddVertexIndex(vmap, (short)vidx, (short)vidx + numVerts); | |
else | |
result = R_VMAPAddVertexIndex(vmap, (short)vidx, (short)vidx); | |
} | |
if (result == qfalse) return qfalse; | |
} | |
return result; | |
} | |
static qboolean R_CreateMD2VertexMap(aliasVertexmap_t *vmap, int numTriangles, md2Triangle_t *start) { | |
int i; | |
// | |
// Create a vertex index map to provide duplicated texture coords | |
// | |
vmap->nverts = 0; | |
vmap->nsurfs = 0; | |
vmap->new_surfaces[0].ninds = 0; | |
vmap->new_surfaces[0].nverts = 0; | |
// Begin the iteration through the triangles | |
for (i=0 ; i<numTriangles ; i++) | |
{ | |
if (!( | |
R_VMAPAddVertexIndex(vmap, LittleShort(start[i].index_xyz[0]), LittleShort(start[i].index_st[0])) && | |
R_VMAPAddVertexIndex(vmap, LittleShort(start[i].index_xyz[1]), LittleShort(start[i].index_st[1])) && | |
R_VMAPAddVertexIndex(vmap, LittleShort(start[i].index_xyz[2]), LittleShort(start[i].index_st[2])) | |
)) | |
return qfalse; | |
} | |
return qtrue; | |
} | |
int R_CalculateMD3VertexMapSurfaceSize(aliasVertexmap_t *vmap, int numFrames) { | |
int calcSize; | |
int i; | |
aliasSurfaceVerts_t *surf; | |
calcSize = vmap->nsurfs * sizeof(md3Surface_t); | |
for (i=0; i<vmap->nsurfs; i++) | |
{ | |
surf = &vmap->new_surfaces[i]; | |
calcSize += sizeof(md3Shader_t); | |
calcSize += (surf->ninds / 3) * sizeof(md3Triangle_t); | |
calcSize += (surf->nverts) * sizeof(md3St_t); | |
calcSize += (surf->nverts) * numFrames * sizeof(md3XyzNormal_t); | |
ri.Printf( PRINT_ALL, "DBG: MD3[%d] mapped to %d triangles, %d vert/texcoords, %d frames\n", i, surf->ninds / 3, surf->nverts, numFrames); | |
} | |
return calcSize; | |
} | |
// #define MDL_CALCULATE_BOUNDS // Force calculate bounds in MDL | |
/* | |
================= | |
R_ProcessMDLSkins | |
================= | |
*/ | |
void *R_ProcessMDLSkins(mdlHeader_t *pheader, dmdlSkintype_t *pskintype) | |
{ | |
int i, j; | |
char name[32]; | |
int s; | |
byte *skin; | |
dmdlSkingroup_t *pinskingroup; | |
int groupskins; | |
dmdlSkininterval_t *pinskinintervals; | |
skin = (byte *)(pskintype + 1); | |
if (pheader->numskins < 1 || pheader->numskins > MDL_MAX_SKINS) { | |
ri.Error(ERR_DROP, "R_LoadMDL: Invalid # of skins: %d\n", pheader->numskins); | |
return NULL; | |
} | |
s = pheader->skinwidth * pheader->skinheight; | |
for (i=0 ; i<pheader->numskins ; i++) | |
{ | |
if (pskintype->type == MDL_SKIN_SINGLE) { | |
//Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight ); | |
// NOTE: could dump these textures so shaders can be generated | |
//Com_sprintf (name, "%s_%i", loadmodel->name, i); | |
//pheader->skinheight, (byte *)(pskintype + 1), true, false); | |
pskintype = (dmdlSkintype_t *)(skin + s); | |
} else { | |
// animating skin group. yuck. | |
pskintype++; | |
pinskingroup = (dmdlSkingroup_t *)pskintype; | |
groupskins = LittleLong (pinskingroup->numskins); | |
pinskinintervals = (dmdlSkininterval_t *)(pinskingroup + 1); | |
pskintype = (void *)(pinskinintervals + groupskins); | |
for (j=0 ; j<groupskins ; j++) | |
{ | |
//Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight ); | |
// NOTE: could dump these textures so shaders can be generated | |
//sprintf (name, "%s_%i_%i", loadmodel->name, i,j); | |
//pheader->gl_texturenum[i][j&3] = | |
//GL_LoadTexture (name, pheader->skinwidth, | |
//pheader->skinheight, (byte *)(pskintype), true, false); | |
pskintype = (dmdlSkintype_t *)((byte *)(pskintype) + s); | |
} | |
//k = j; | |
//for (/* */; j < 4; j++) | |
// pheader->gl_texturenum[i][j&3] = | |
// pheader->gl_texturenum[i][j - k]; | |
} | |
} | |
return (void *)pskintype; | |
} | |
int R_CalculateMDLFrameNumber(mdlHeader_t *pinmodel, dmdlFrametype_t *pinframe) | |
{ | |
int i, tmp, count; | |
dmdlFrametype_t *pinframetype; | |
count = 0; | |
pinframetype = pinframe; | |
for (i=0; i<pinmodel->numframes; i++) | |
{ | |
tmp = LittleLong(pinframetype->type); | |
if (pinframetype->type == MDL_SINGLE) | |
{ | |
// Single set of frame data | |
mdlFrame_t *frame = (mdlFrame_t*)(pinframetype + 1); | |
count++; | |
pinframetype = (dmdlFrametype_t *) (((mdlTrivertx_t*)(frame + 1)) + pinmodel->numverts); | |
} | |
else | |
{ | |
// Multiple sets of frame data (sharing same bounds, name, etc) | |
mdlGroup_t *pingroup = (mdlGroup_t *)(pinframetype + 1); | |
dmdlInterval_t *pininterval; | |
mdlTrivertx_t *pinvert; | |
tmp = LittleLong(pingroup->numframes); | |
// Intervals (important?) | |
pininterval = (dmdlInterval_t *)(pingroup + 1); | |
pininterval += tmp; | |
// Get frame pointer | |
// [header] verts [header] verts... | |
pinvert = (mdlTrivertx_t *)((mdlFrame_t*)pininterval + 1); | |
while (tmp != 0) | |
{ | |
pinvert = (mdlTrivertx_t *)(((mdlFrame_t *)pinvert + 1) + pinmodel->numverts); | |
count++; | |
tmp--; | |
} | |
} | |
} | |
ri.Printf( PRINT_WARNING, "DBG: %d frames found in MDL\n", count); | |
return count; | |
} | |
/* | |
================= | |
R_LoadMDL | |
================= | |
*/ | |
static qboolean R_LoadMDL (model_t *mod, int lod, void *buffer, const char *name ) { | |
int i, j, y; // cabin crew | |
// Inbound flight 80 from medieval village | |
mdlHeader_t *pinmodel; | |
mdlSt_t *pinst; | |
mdlTriangle_t *pintri; | |
dmdlFrametype_t *pinframetype; | |
mdlTrivertx_t *pinvert; | |
// Outbound flight 101 to fragville | |
md3Header_t *poutmodel; | |
md3St_t *poutst; | |
md3Surface_t *psurfaces; | |
md3Frame_t *poutframe; | |
md3Triangle_t *pouttri; | |
md3XyzNormal_t *poutverts; | |
// Stowaway spy | |
int tmp; | |
// Airport security | |
aliasVertexmap_t *vmap; | |
// Version check - very important! | |
pinmodel = (mdlHeader_t *)buffer; | |
ri.Printf( PRINT_ALL, "DBG: Loading an MDL (q1)\n" ); | |
tmp = LittleLong(pinmodel->version); | |
if (tmp != MDL_VERSION) { | |
ri.Printf( PRINT_WARNING, "R_LoadMDL: %s has wrong version number (%i should be %i)\n", | |
mod->name, tmp, MDL_VERSION); | |
return qfalse; | |
} | |
mod->type = MOD_MESH; | |
// Byteswap header fields | |
LL(pinmodel->flags); | |
LF(pinmodel->boundingradius); | |
LL(pinmodel->numskins); | |
LL(pinmodel->skinwidth); | |
LL(pinmodel->skinheight); | |
pinmodel->size = LittleFloat(pinmodel->size) * ALIAS_BASE_SIZE_RATIO; | |
LL(pinmodel->synctype); | |
LL(pinmodel->numverts); | |
LL(pinmodel->numtris); | |
LL(pinmodel->numframes); | |
for (i=0 ; i<3 ; i++) { | |
LF(pinmodel->scale[i]); | |
LF(pinmodel->scale_origin[i]); | |
LF(pinmodel->eyeposition[i]); | |
} | |
// Quick overview of MDL format: | |
// Header | |
// Skins[numskins] | |
// ST verts[numverts] | |
// Triangles[numtris] | |
// Frame Data[numframes] | |
// * Single (like MD2) OR | |
// * Grouped (each frame contains multiple references to vertex data, | |
// perhaps to save the need for multiple frame headers) | |
// Load skins, calculate st and triangle pointers | |
pinst = (mdlSt_t*) R_ProcessMDLSkins(pinmodel, (dmdlSkintype_t*)&pinmodel[1]); | |
if (pinst == NULL) | |
return qfalse; | |
pintri = (mdlTriangle_t*) (pinst + pinmodel->numverts); | |
pinframetype = (dmdlFrametype_t *)(pintri + pinmodel->numtris); | |
// Create vertex map which will tell us about size requirements | |
vmap = ri.Hunk_AllocateTempMemory(sizeof(aliasVertexmap_t)); | |
if (vmap == NULL) | |
{ | |
ri.Error (ERR_DROP, "R_LoadMDL: not enough temporory memory to convert %s (%d bytes required)\n", mod->name, sizeof(aliasVertexmap_t)); | |
return qfalse; | |
} | |
if (!R_CreateMDLVertexMap(vmap, pinmodel->numtris, pinmodel->numverts, pintri, pinst)) | |
{ | |
ri.Error (ERR_DROP, "R_LoadMDL: failed to generate vertex map whilst error converting %s\n", mod->name); | |
ri.Hunk_FreeTempMemory(vmap); | |
return qfalse; | |
} | |
if (vmap->new_surfaces[vmap->nsurfs].ninds != 0) vmap->nsurfs++; // Close last surface | |
ri.Printf( PRINT_ALL, "DBG: MDL mapped to %d verts using %d surfaces from %d tris\n", vmap->nverts, vmap->nsurfs, pinmodel->numtris ); | |
// Calculate size of model as an MD3, and Allocate hunk | |
// TODO: need to go through frames one time to determine max frames | |
tmp = sizeof(md3Header_t) + | |
(R_CalculateMDLFrameNumber(pinmodel, pinframetype) * sizeof(md3Frame_t)) + // Frame list | |
0 + // Tag list | |
R_CalculateMD3VertexMapSurfaceSize(vmap, pinmodel->numframes) // Surface list | |
; | |
mod->dataSize += tmp; | |
mod->md3[lod] = ri.Hunk_Alloc( tmp, h_low ); | |
poutmodel = mod->md3[lod]; // so we don't have to use mod->md3[lod] every time | |
poutmodel->numSurfaces = vmap->nsurfs; | |
// Byte swap and convert the header fields | |
poutmodel->ident = MD3_IDENT; | |
poutmodel->version = MD3_VERSION; | |
poutmodel->flags = 0; // TODO | |
poutmodel->name[0] = '\0'; // TODO | |
// Background check | |
if ( pinmodel->numframes <= 0 ) { | |
ri.Error (ERR_DROP, "R_LoadMDL: %s has no frames\n", mod->name); | |
ri.Hunk_FreeTempMemory(vmap); | |
return qfalse; | |
} | |
// | |
// Start with the frames | |
// Since MDL frames can be grouped, so we need to be careful | |
// | |
poutmodel->numFrames = 0; | |
poutmodel->ofsFrames = sizeof(md3Header_t); | |
poutframe = (md3Frame_t *) ((byte *)poutmodel | |
+ poutmodel->ofsFrames); | |
mdlTrivertx_t *poses[MDL_MAXFRAMES]; // Useful reference for vertex frames | |
for (i=0; i<pinmodel->numframes; i++) | |
{ | |
tmp = LittleLong(pinframetype->type); | |
if (tmp == MDL_SINGLE) | |
{ | |
// Single set of frame data | |
mdlFrame_t *frame = (mdlFrame_t*)(pinframetype + 1); | |
Com_Memcpy(poutframe->name, frame->name, 16); | |
#ifndef MDL_CALCULATE_BOUNDS | |
// Bounds | |
for (y=0; y<3; y++) | |
{ | |
poutframe->bounds[0][y] = frame->bboxmin.v[y]; | |
poutframe->bounds[1][y] = frame->bboxmax.v[y]; // NOTE: typo assignment (min instead of max) in original source | |
} | |
#else | |
// Bounds | |
for (y=0 ; y<3 ; y++) | |
poutframe->bounds[0][y] = 10e30; // min | |
for (y=0 ; y<3 ; y++) | |
poutframe->bounds[1][y] = -10e30; // max | |
#endif | |
// Get frame pointer | |
pinvert = (mdlTrivertx_t*)(frame + 1); | |
poses[poutmodel->numFrames] = pinvert; | |
poutmodel->numFrames++; | |
pinframetype = (dmdlFrametype_t *) (pinvert + pinmodel->numverts); | |
#ifndef MDL_CALCULATE_BOUNDS | |
// Calculate center | |
// ((min - max) / 2) + max | |
poutframe->localOrigin[0] = ((poutframe->bounds[0][0] - poutframe->bounds[1][0])/2.f) + poutframe->bounds[1][0]; | |
poutframe->localOrigin[1] = ((poutframe->bounds[0][1] - poutframe->bounds[1][1])/2.f) + poutframe->bounds[1][1]; | |
poutframe->localOrigin[2] = ((poutframe->bounds[0][2] - poutframe->bounds[1][2])/2.f) + poutframe->bounds[1][2]; | |
// Calculate radius | |
// A bit inaccurate, but not much we can check here | |
poutframe->radius = RadiusFromBounds(poutframe->bounds[0], poutframe->bounds[1]); | |
#endif | |
poutframe++; | |
} | |
else | |
{ | |
// Multiple sets of frame data (sharing same bounds, name, etc) | |
mdlGroup_t *pingroup = (mdlGroup_t *)(pinframetype + 1); | |
dmdlInterval_t *pininterval; | |
y = LittleLong(pingroup->numframes); | |
#ifndef MDL_CALCULATE_BOUNDS | |
// Bounds | |
for (j=0; j<3; j++) | |
{ | |
poutframe->bounds[0][j] = pingroup->bboxmin.v[j]; | |
poutframe->bounds[1][j] = pingroup->bboxmax.v[j]; | |
} | |
// Calculate center | |
// ((min - max) / 2) + max | |
poutframe->localOrigin[0] = ((poutframe->bounds[0][0] - poutframe->bounds[1][0])/2.f) + poutframe->bounds[1][0]; | |
poutframe->localOrigin[1] = ((poutframe->bounds[0][1] - poutframe->bounds[1][1])/2.f) + poutframe->bounds[1][1]; | |
poutframe->localOrigin[2] = ((poutframe->bounds[0][2] - poutframe->bounds[1][2])/2.f) + poutframe->bounds[1][2]; | |
// Calculate radius | |
// A bit inaccurate, but not much we can check here | |
poutframe->radius = RadiusFromBounds(poutframe->bounds[0], poutframe->bounds[1]); | |
#else | |
// Bounds | |
for (y=0 ; y<3 ; y++) | |
poutframe->bounds[0][y] = 10e30; // min | |
for (y=0 ; y<3 ; y++) | |
poutframe->bounds[1][y] = -10e30; // max | |
#endif | |
// Intervals (important?) | |
pininterval = (dmdlInterval_t *)(pingroup + 1); | |
pininterval += y; | |
// Get frame pointer | |
// [header] verts [header] verts... | |
// NOTE: seems horrifically redundant - only advantage is shared bounds and no repeating frame type, | |
// as well as a sequence-like grouping mechanizm | |
pinvert = (mdlTrivertx_t *)((mdlFrame_t*)pininterval + 1); | |
while (y != 0) | |
{ | |
poses[poutmodel->numFrames] = pinvert; | |
pinvert = (mdlTrivertx_t *)(((mdlFrame_t *)pinvert + 1) + pinmodel->numverts); | |
poutmodel->numFrames++; | |
Com_Memcpy(poutframe, poutframe-1, sizeof(md3Frame_t)); // prev -> next | |
poutframe++; | |
y--; | |
} | |
} | |
} | |
// | |
// Tags | |
// TODO: add a tag for eyes (eyeposition[])? | |
// | |
poutmodel->numTags = 0; | |
poutmodel->ofsTags = poutmodel->ofsFrames + (poutmodel->numFrames * sizeof(md3Frame_t)); | |
// | |
// Surfaces | |
// | |
poutmodel->ofsSurfaces = poutmodel->ofsTags + (poutmodel->numTags * sizeof(md3Tag_t)); | |
psurfaces = (md3Surface_t *) ((byte *)poutmodel | |
+ poutmodel->ofsSurfaces); | |
poutmodel->ofsEnd = poutmodel->ofsSurfaces; | |
for(i = 0; i<poutmodel->numSurfaces; i++) | |
{ | |
aliasSurfaceVerts_t *surf = &vmap->new_surfaces[i]; | |
md3Shader_t *pshader; | |
psurfaces[i].ident = SF_MD3; | |
Com_sprintf(psurfaces[i].name, MAX_QPATH, "mdlsurf%d", i); | |
psurfaces[i].flags = 0; | |
psurfaces[i].numFrames = poutmodel->numFrames; | |
psurfaces[i].numShaders = 1; | |
psurfaces[i].numVerts = surf->nverts; | |
psurfaces[i].numTriangles = surf->ninds / 3; | |
// | |
// Convert the surface | |
// | |
// Surface Shader | |
psurfaces[i].ofsShaders = sizeof(md3Surface_t); | |
pshader = (md3Shader_t *) ((byte *)&psurfaces[i] + psurfaces[i].ofsShaders); | |
Com_sprintf(pshader->name, MAX_QPATH, "default"); // TODO | |
shader_t *sh; | |
sh = R_FindShader( pshader->name, LIGHTMAP_NONE, qtrue ); | |
if ( sh->defaultShader ) { | |
pshader->shaderIndex = 0; | |
} else { | |
pshader->shaderIndex = sh->index; | |
} | |
// Surface Triangles | |
psurfaces[i].ofsTriangles = psurfaces[i].ofsShaders + sizeof(md3Shader_t); | |
pouttri = (md3Triangle_t *) ((byte *)&psurfaces[i] + psurfaces[i].ofsTriangles); | |
for (j=0; j<surf->ninds; j+=3) | |
{ | |
pouttri->indexes[0] = surf->inds[j]; | |
pouttri->indexes[1] = surf->inds[j+1]; | |
pouttri->indexes[2] = surf->inds[j+2]; | |
pouttri++; | |
}; | |
// Surface Texcoords | |
psurfaces[i].ofsSt = psurfaces[i].ofsTriangles + (psurfaces[i].numTriangles * sizeof(md3Triangle_t)); | |
poutst = (md3St_t *) ((byte *)&psurfaces[i] + psurfaces[i].ofsSt); | |
for (j=0; j<surf->nverts; j++) | |
{ | |
tmp = vmap->vertex_st[surf->verts[j]]; // Need to map new verts -> old verts | |
// verts -> vertex_map,st_map -> frame[].pinvert, pinst | |
if (tmp >= pinmodel->numverts) { | |
tmp -= pinmodel->numverts; | |
poutst->st[0] = ((LittleLong(pinst[tmp].s) + 0.5) / pinmodel->skinwidth) + (pinmodel->skinwidth / 2); | |
} | |
else | |
poutst->st[0] = (LittleLong(pinst[tmp].s) + 0.5) / pinmodel->skinwidth; | |
poutst->st[1] = (LittleLong(pinst[tmp].t) + 0.5) / pinmodel->skinheight; | |
poutst++; | |
} | |
// Surface Vertexes[frames] | |
psurfaces[i].ofsXyzNormals = psurfaces[i].ofsSt + (psurfaces[i].numVerts * sizeof(md3St_t)); | |
poutverts = (md3XyzNormal_t *) ((byte *)&psurfaces[i] + psurfaces[i].ofsXyzNormals); | |
poutframe = (md3Frame_t *) ((byte *)poutmodel | |
+ poutmodel->ofsFrames); | |
for (j=0; j<poutmodel->numFrames; j++) | |
{ | |
pinvert = poses[j]; | |
// Convert each vertex into an MD3 representation | |
for (y=0; y<surf->nverts; y++) | |
{ | |
tmp = vmap->vertex_map[surf->verts[y]]; // Need to map new verts -> old verts | |
float base[3]; | |
// TODO: fix origin | |
base[0] = ((float)pinvert[tmp].v[0] * pinmodel->scale[0]) + pinmodel->scale_origin[0]; | |
base[1] = ((float)pinvert[tmp].v[1] * pinmodel->scale[1]) + pinmodel->scale_origin[1]; | |
base[2] = ((float)pinvert[tmp].v[2] * pinmodel->scale[2]) + pinmodel->scale_origin[2]; | |
poutverts->normal = pinvert[tmp].lightnormalindex; | |
#ifdef MDL_CALCULATE_BOUNDS | |
// Expand bounds | |
for (tmp=0; tmp<3; tmp++) | |
if (base[tmp] < poutframe->bounds[0][tmp]) poutframe->bounds[0][tmp] = base[tmp]; | |
for (tmp=0; tmp<3; tmp++) | |
if (base[tmp] > poutframe->bounds[1][tmp]) poutframe->bounds[1][tmp] = base[tmp]; | |
#endif | |
// This should give an appropriate MD3 vertex | |
poutverts->xyz[0] = base[0] / MD3_XYZ_SCALE; | |
poutverts->xyz[1] = base[1] / MD3_XYZ_SCALE; | |
poutverts->xyz[2] = base[2] / MD3_XYZ_SCALE; | |
poutverts++; | |
} | |
#ifdef MDL_CALCULATE_BOUNDS | |
// Calculate center | |
// ((min - max) / 2) + max | |
poutframe->localOrigin[0] = ((poutframe->bounds[0][0] - poutframe->bounds[1][0])/2.f) + poutframe->bounds[1][0]; | |
poutframe->localOrigin[1] = ((poutframe->bounds[0][1] - poutframe->bounds[1][1])/2.f) + poutframe->bounds[1][1]; | |
poutframe->localOrigin[2] = ((poutframe->bounds[0][2] - poutframe->bounds[1][2])/2.f) + poutframe->bounds[1][2]; | |
// Calculate radius | |
// A bit inaccurate, but its better than checking every single vertex using sqrt | |
poutframe->radius = RadiusFromBounds(poutframe->bounds[0], poutframe->bounds[1]); | |
#endif | |
// Next please | |
poutframe++; | |
} | |
psurfaces[i].ofsEnd = psurfaces[i].ofsXyzNormals + (psurfaces[i].numVerts * psurfaces[i].numFrames * sizeof(md3XyzNormal_t)); | |
poutmodel->ofsEnd += psurfaces[i].ofsEnd; | |
} | |
ri.Hunk_FreeTempMemory(vmap); | |
// Test MDL dump | |
FILE *fp = fopen("/Users/jamesu/debug_model.md3", "wb"); | |
fwrite(poutmodel, poutmodel->ofsEnd, 1, fp); | |
fclose(fp); | |
return qtrue; | |
} | |
/* | |
================= | |
R_LoadMD2 | |
================= | |
*/ | |
static qboolean R_LoadMD2 (model_t *mod, int lod, void *buffer, const char *mod_name ) { | |
int i, j, y; // cabin crew | |
// Inbound flight 102 from stroggos | |
md2Header_t *pinmodel; | |
md2St_t *pinst; | |
md2Frame_t *pinframe; | |
md2Trivertx_t *pinvert; // base triangle data and normal | |
// Outbound flight 106 to fragville | |
md3Header_t *poutmodel; | |
md3St_t *poutst; | |
md3Surface_t *psurfaces; | |
md3Frame_t *poutframe; | |
md3Triangle_t *pouttri; | |
md3XyzNormal_t *poutverts; | |
// Stowaway spy | |
int tmp; | |
// Airport security | |
aliasVertexmap_t *vmap; | |
// Version check - very important! | |
pinmodel = (md2Header_t *)buffer; | |
ri.Printf( PRINT_ALL, "DBG: Loading an MD2\n" ); | |
tmp = LittleLong(pinmodel->version); | |
if (tmp != MD2_VERSION) { | |
ri.Printf( PRINT_WARNING, "R_LoadMD2: %s has wrong version number (%i should be %i)\n", | |
mod->name, tmp, MD2_VERSION); | |
return qfalse; | |
} | |
mod->type = MOD_MESH; | |
// Byteswap header fields | |
for (i=0 ; i<sizeof(md2Header_t)/4 ; i++) | |
((int *)pinmodel)[i] = LittleLong (((int *)pinmodel)[i]); | |
// Create vertex map which will tell us about size requirements | |
vmap = ri.Hunk_AllocateTempMemory(sizeof(aliasVertexmap_t)); | |
if (vmap == NULL) | |
{ | |
ri.Error (ERR_DROP, "R_LoadMD2: not enough temporory memory to convert %s (%d bytes required)\n", mod->name, sizeof(aliasVertexmap_t)); | |
return qfalse; | |
} | |
if (!R_CreateMD2VertexMap(vmap, pinmodel->numTriangles, (md2Triangle_t *) ((byte *)pinmodel + pinmodel->ofsTris))) | |
{ | |
ri.Error (ERR_DROP, "R_LoadMD2: failed to generate vertex map whilst error converting %s\n", mod->name); | |
ri.Hunk_FreeTempMemory(vmap); | |
return qfalse; | |
} | |
if (vmap->new_surfaces[vmap->nsurfs].ninds != 0) vmap->nsurfs++; // Close last surface | |
ri.Printf( PRINT_ALL, "DBG: MD2 mapped to %d verts using %d surfaces from %d tris\n", vmap->nverts, vmap->nsurfs, pinmodel->numTriangles ); | |
// Calculate size of model as an MD3, and Allocate hunk | |
tmp = sizeof(md3Header_t) + | |
(pinmodel->numFrames * sizeof(md3Frame_t)) + // Frame list | |
0 + // Tag list | |
R_CalculateMD3VertexMapSurfaceSize(vmap, pinmodel->numFrames) // Surface list | |
; | |
mod->dataSize += tmp; | |
mod->md3[lod] = ri.Hunk_Alloc( tmp, h_low ); | |
poutmodel = mod->md3[lod]; // so we don't have to use mod->md3[lod] every time | |
poutmodel->numSurfaces = vmap->nsurfs; | |
// Byte swap and convert the header fields | |
poutmodel->ident = MD3_IDENT; | |
poutmodel->version = MD3_VERSION; | |
poutmodel->flags = 0; // TODO | |
poutmodel->name[0] = '\0'; // TODO | |
// Background check | |
if ( pinmodel->numFrames <= 0 ) { | |
ri.Error (ERR_DROP, "R_LoadMD2: %s has no frames\n", mod->name); | |
ri.Hunk_FreeTempMemory(vmap); | |
return qfalse; | |
} | |
// | |
// Start with the frames (bounds and radius calculated later) | |
// | |
poutmodel->numFrames = pinmodel->numFrames; | |
poutmodel->ofsFrames = sizeof(md3Header_t); | |
pinframe = (md2Frame_t *) ((byte *)pinmodel | |
+ pinmodel->ofsFrames); | |
poutframe = (md3Frame_t *) ((byte *)poutmodel | |
+ poutmodel->ofsFrames); | |
for (i=0; i<pinmodel->numFrames ; i++) | |
{ | |
pinframe = (md2Frame_t *) ((byte *)pinmodel | |
+ pinmodel->ofsFrames + (pinmodel->framesize * i)); | |
Com_Memcpy(poutframe->name, pinframe->name, 16); | |
for (j=0 ; j<3 ; j++) | |
{ | |
LF(pinframe->scale[j]); | |
LF(pinframe->translate[j]); | |
} | |
// Bounds | |
for (j=0 ; j<3 ; j++) | |
poutframe->bounds[0][j] = 10e30; // min | |
for (j=0 ; j<3 ; j++) | |
poutframe->bounds[1][j] = -10e30; // max | |
poutframe->radius = 0; | |
poutframe++; | |
} | |
// | |
// Tags | |
// | |
poutmodel->numTags = 0; | |
poutmodel->ofsTags = poutmodel->ofsFrames + (poutmodel->numFrames * sizeof(md3Frame_t)); | |
// | |
// Surfaces | |
// | |
poutmodel->ofsSurfaces = poutmodel->ofsTags + (poutmodel->numTags * sizeof(md3Tag_t)); | |
psurfaces = (md3Surface_t *) ((byte *)poutmodel | |
+ poutmodel->ofsSurfaces); | |
poutmodel->ofsEnd = poutmodel->ofsSurfaces; | |
for(i = 0; i<poutmodel->numSurfaces; i++) | |
{ | |
aliasSurfaceVerts_t *surf = &vmap->new_surfaces[i]; | |
psurfaces[i].ident = SF_MD3; | |
Com_sprintf(psurfaces[i].name, MAX_QPATH, "md2surf%d", i); | |
psurfaces[i].flags = 0; | |
psurfaces[i].numFrames = poutmodel->numFrames; | |
psurfaces[i].numShaders = 1; | |
psurfaces[i].numVerts = surf->nverts; | |
psurfaces[i].numTriangles = surf->ninds / 3; | |
// | |
// Convert the surface | |
// | |
// Surface Shader | |
psurfaces[i].ofsShaders = sizeof(md3Surface_t); | |
md3Shader_t *pshader = (md3Shader_t *) ((byte *)&psurfaces[i] + psurfaces[i].ofsShaders); | |
if (pinmodel->numSkins == 0) | |
Com_sprintf(pshader->name, MAX_QPATH, "default"); | |
else | |
Com_Memcpy(pshader->name, (byte*)pinmodel + pinmodel->ofsSkins, MAX_QPATH); | |
shader_t *sh; | |
sh = R_FindShader( pshader->name, LIGHTMAP_NONE, qtrue ); | |
if ( sh->defaultShader ) { | |
pshader->shaderIndex = 0; | |
} else { | |
pshader->shaderIndex = sh->index; | |
} | |
// Surface Triangles | |
psurfaces[i].ofsTriangles = psurfaces[i].ofsShaders + sizeof(md3Shader_t); | |
pouttri = (md3Triangle_t *) ((byte *)&psurfaces[i] + psurfaces[i].ofsTriangles); | |
for (j=0; j<surf->ninds; j+=3) | |
{ | |
pouttri->indexes[0] = surf->inds[j]; | |
pouttri->indexes[1] = surf->inds[j+1]; | |
pouttri->indexes[2] = surf->inds[j+2]; | |
pouttri++; | |
}; | |
// Surface Texcoords | |
psurfaces[i].ofsSt = psurfaces[i].ofsTriangles + (psurfaces[i].numTriangles * sizeof(md3Triangle_t)); | |
pinst = (md2St_t *) ((byte *)pinmodel + pinmodel->ofsSt); | |
poutst = (md3St_t *) ((byte *)&psurfaces[i] + psurfaces[i].ofsSt); | |
for (j=0; j<surf->nverts; j++) | |
{ | |
tmp = vmap->vertex_st[surf->verts[j]]; // Need to map new verts -> old verts | |
// verts -> vertex_map,st_map -> frame[].pinvert, pinst | |
poutst->st[0] = (float)LittleShort(pinst[tmp].s) / pinmodel->skinwidth; | |
poutst->st[1] = (float)LittleShort(pinst[tmp].t) / pinmodel->skinheight; | |
poutst++; | |
} | |
// Surface Vertexes[frames] | |
psurfaces[i].ofsXyzNormals = psurfaces[i].ofsSt + (psurfaces[i].numVerts * sizeof(md3St_t)); | |
poutverts = (md3XyzNormal_t *) ((byte *)&psurfaces[i] + psurfaces[i].ofsXyzNormals); | |
poutframe = (md3Frame_t *) ((byte *)poutmodel | |
+ poutmodel->ofsFrames); | |
for (j=0; j<poutmodel->numFrames; j++) | |
{ | |
//if (j == 0) fp = fopen("/Users/jamesu/import_md2_verts.txt", "w"); | |
pinframe = (md2Frame_t *) ((byte *)pinmodel | |
+ pinmodel->ofsFrames + (pinmodel->framesize * j)); | |
pinvert = pinframe->verts; | |
// Convert each vertex into an MD3 representation | |
for (y=0; y<surf->nverts; y++) | |
{ | |
tmp = vmap->vertex_map[surf->verts[y]]; // Need to map new verts -> old verts | |
float base[3]; | |
base[0] = ((float)pinvert[tmp].v[0] * pinframe->scale[0]) + pinframe->translate[0]; | |
base[1] = ((float)pinvert[tmp].v[1] * pinframe->scale[1]) + pinframe->translate[1]; | |
base[2] = ((float)pinvert[tmp].v[2] * pinframe->scale[2]) + pinframe->translate[2]; | |
poutverts->normal = pinvert[tmp].lightnormalindex; | |
// Expand bounds | |
for (tmp=0; tmp<3; tmp++) | |
if (base[tmp] < poutframe->bounds[0][tmp]) poutframe->bounds[0][tmp] = base[tmp]; | |
for (tmp=0; tmp<3; tmp++) | |
if (base[tmp] > poutframe->bounds[1][tmp]) poutframe->bounds[1][tmp] = base[tmp]; | |
// This should give an appropriate MD3 vertex | |
poutverts->xyz[0] = base[0] / MD3_XYZ_SCALE; | |
poutverts->xyz[1] = base[1] / MD3_XYZ_SCALE; | |
poutverts->xyz[2] = base[2] / MD3_XYZ_SCALE; | |
poutverts++; | |
} | |
// Calculate center | |
// ((min - max) / 2) + max | |
poutframe->localOrigin[0] = ((poutframe->bounds[0][0] - poutframe->bounds[1][0])/2.f) + poutframe->bounds[1][0]; | |
poutframe->localOrigin[1] = ((poutframe->bounds[0][1] - poutframe->bounds[1][1])/2.f) + poutframe->bounds[1][1]; | |
poutframe->localOrigin[2] = ((poutframe->bounds[0][2] - poutframe->bounds[1][2])/2.f) + poutframe->bounds[1][2]; | |
// Calculate radius | |
// A bit inaccurate, but its better than checking every single vertex using sqrt | |
poutframe->radius = RadiusFromBounds(poutframe->bounds[0], poutframe->bounds[1]); | |
// Next please | |
pinframe += pinmodel->framesize; | |
poutframe++; | |
} | |
psurfaces[i].ofsEnd = psurfaces[i].ofsXyzNormals + (psurfaces[i].numVerts * psurfaces[i].numFrames * sizeof(md3XyzNormal_t)); | |
poutmodel->ofsEnd += psurfaces[i].ofsEnd; | |
} | |
ri.Hunk_FreeTempMemory(vmap); | |
// Test MD2 dump | |
//FILE *fp = fopen("/Users/jamesu/debug_md2model.md3", "wb"); | |
//fwrite(poutmodel, poutmodel->ofsEnd, 1, fp); | |
//fclose(fp); | |
return qtrue; | |
} | |
// JamesU - End of MDL/MD2 loading code | |
//!! Enjoy. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment