diff options
author | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
commit | f294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch) | |
tree | e8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/gl_models.c |
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/gl_models.c')
-rw-r--r-- | source/gl_models.c | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/source/gl_models.c b/source/gl_models.c new file mode 100644 index 0000000..9626ebe --- /dev/null +++ b/source/gl_models.c @@ -0,0 +1,981 @@ +/* +Copyright (C) 2003-2006 Andrey Nazarov + +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. + +*/ + +#include "gl_local.h" + +#define Model_Malloc( size ) sys.HunkAlloc( &model->pool, size ) + +static model_t r_models[MAX_MODELS]; +static int r_numModels; + +static byte ll2byte[256][256]; + +static cvar_t *gl_override_models; + +static model_t *Model_Alloc( const char *name ) { + model_t *model; + int i; + + for( i = 0, model = r_models; i < r_numModels; i++, model++ ) { + if( !model->name[0] ) { + break; + } + } + + if( i == r_numModels ) { + if( r_numModels == MAX_MODELS ) { + Com_Error( ERR_DROP, "Model_Alloc: MAX_MODELS" ); + } + r_numModels++; + } + + strcpy( model->name, name ); + model->registration_sequence = registration_sequence; + model->type = MODEL_NULL; + + return model; +} + +static model_t *Model_Find( const char *name ) { + model_t *model; + aliasMesh_t *mesh; + int i, j; + + for( i = 0, model = r_models; i < r_numModels; i++, model++ ) { + if( !model->name[0] ) { + continue; + } + if( !Q_stricmp( model->name, name ) ) { + break; + } + } + + if( i == r_numModels ) { + return NULL; + } + + switch( model->type ) { + case MODEL_ALIAS: + for( i = 0; i < model->numMeshes; i++ ) { + mesh = &model->meshes[i]; + for( j = 0; j < mesh->numSkins; j++ ) { + mesh->skins[j]->registration_sequence = registration_sequence; + } + } + break; + case MODEL_SPRITE: + for( i = 0; i < model->numFrames; i++ ) { + model->sframes[i].image->registration_sequence = registration_sequence; + } + break; + default: + Com_Error( ERR_DROP, "Model_Find: bad model type: %d", model->type ); + break; + } + + model->registration_sequence = registration_sequence; + return model; +} + +static void Model_List_f( void ) { + int i; + model_t *model; + int bytes; + + Com_Printf( "------------------\n"); + bytes = 0; + + for( i = 0, model = r_models; i < r_numModels; i++, model++ ) { + if( !model->name[0] ) { + continue; + } + + Com_Printf( "%8i : %s\n", model->pool.cursize, model->name ); + bytes += model->pool.cursize; + } + Com_Printf( "Total resident: %i\n", bytes ); +} + +void Model_FreeUnused( void ) { + model_t *model; + int i; + + for( i = 0, model = r_models; i < r_numModels; i++, model++ ) { + if( !model->name[0] ) { + continue; + } + if( model->registration_sequence != registration_sequence ) { + sys.HunkFree( &model->pool ); + model->name[0] = 0; + } + } +} + +void Model_FreeAll( void ) { + model_t *model; + int i; + + for( i = 0, model = r_models; i < r_numModels; i++, model++ ) { + if( !model->name[0] ) { + continue; + } + + sys.HunkFree( &model->pool ); + model->name[0] = 0; + } + + r_numModels = 0; +} + +static qboolean Model_LoadMd2( model_t *model, const byte *rawdata, int length ) { + dmdl_t header; + daliasframe_t *src_frame; + dtrivertx_t *src_vert; + dtriangle_t *src_tri; + dstvert_t *src_tc; + aliasFrame_t *dst_frame; + aliasVert_t *dst_vert; + aliasMesh_t *dst_mesh; + uint32 *finalIndices; + tcoord_t *dst_tc; + int i, j, k; + uint16 remap[MAX_TRIANGLES*3]; + uint16 vertIndices[MAX_TRIANGLES*3]; + uint16 tcIndices[MAX_TRIANGLES*3]; + int numVerts, numIndices; + char skinname[MAX_QPATH]; + char *src_skin; + image_t *skin; + vec_t scaleS, scaleT; + int offset, val; + vec3_t mins, maxs; + const byte *rawend; + + if( length < sizeof( header ) ) { + Com_EPrintf( "%s has length < header length\n", model->name ); + return qfalse; + } + + /* byte swap the header */ + header = *( dmdl_t * )rawdata; + for( i = 0; i < sizeof( header )/4; i++ ) { + (( uint32 * )&header)[i] = LittleLong( (( uint32 * )&header)[i] ); + } + + if( header.ident != IDALIASHEADER ) { + Com_EPrintf( "%s is not an MD2 file\n", model->name ); + return qfalse; + } + + if( header.version != ALIAS_VERSION ) { + Com_EPrintf( "%s has bad version: %d != %d\n", + model->name, header.version, ALIAS_VERSION ); + return qfalse; + } + + if( header.num_frames < 1 ) { + Com_EPrintf( "%s has bad number of frames: %d\n", + model->name, header.num_frames ); + return qfalse; + } + if( header.num_frames > MAX_FRAMES ) { + Com_EPrintf( "%s has too many frames: %d > %d\n", + model->name, header.num_frames, MAX_FRAMES ); + return qfalse; + } + if( header.num_tris < 1 ) { + Com_EPrintf( "%s has bad number of triangles: %d\n", + model->name, header.num_tris ); + return qfalse; + } + if( header.num_tris > MAX_TRIANGLES ) { + Com_EPrintf( "%s has too many triangles: %d > %d\n", + model->name, header.num_tris, MAX_TRIANGLES ); + return qfalse; + } + if( ( uint32 )header.num_skins > MAX_MD2SKINS ) { + Com_EPrintf( "%s has too many skins: %d > %d\n", + model->name, header.num_skins, MAX_MD2SKINS ); + return qfalse; + } + + if( header.skinwidth <= 0 || header.skinheight <= 0 ) { + Com_EPrintf( "%s has bad skin dimensions: %d x %d\n", + model->name, header.skinwidth, header.skinheight ); + return qfalse; + } + + rawend = rawdata + length; + + model->type = MODEL_ALIAS; + sys.HunkBegin( &model->pool, 0x400000 ); + + /* load all triangle indices */ + src_tri = ( dtriangle_t * )( ( byte * )rawdata + header.ofs_tris ); + if( ( byte * )( src_tri + header.num_tris ) > rawend ) { + Com_EPrintf( "%s has bad triangles offset\n", model->name ); + goto fail; + } + for( i = 0; i < header.num_tris; i++ ) { + vertIndices[i*3+0] = LittleShort( src_tri->index_xyz[0] ); + vertIndices[i*3+1] = LittleShort( src_tri->index_xyz[1] ); + vertIndices[i*3+2] = LittleShort( src_tri->index_xyz[2] ); + + tcIndices[i*3+0] = LittleShort( src_tri->index_st[0] ); + tcIndices[i*3+1] = LittleShort( src_tri->index_st[1] ); + tcIndices[i*3+2] = LittleShort( src_tri->index_st[2] ); + + src_tri++; + } + + numIndices = header.num_tris * 3; + + model->meshes = Model_Malloc( sizeof( aliasMesh_t ) ); + model->numMeshes = 1; + + dst_mesh = model->meshes; + dst_mesh->indices = Model_Malloc( numIndices * sizeof( uint32 ) ); + dst_mesh->numTris = header.num_tris; + dst_mesh->numIndices = numIndices; + + for( i = 0; i < numIndices; i++ ) { + remap[i] = 0xFFFF; + } + + numVerts = 0; + src_tc = ( dstvert_t * )( ( byte * )rawdata + header.ofs_st ); + finalIndices = dst_mesh->indices; + for( i = 0; i < numIndices; i++ ) { + if( remap[i] != 0xFFFF ) { + continue; /* already remapped */ + } + + for( j = i + 1; j < numIndices; j++ ) { + if( vertIndices[i] == vertIndices[j] && + ( src_tc[tcIndices[i]].s == src_tc[tcIndices[j]].s && + src_tc[tcIndices[i]].t == src_tc[tcIndices[j]].t ) ) + { + /* duplicate vertex */ + remap[j] = i; + finalIndices[j] = numVerts; + } + } + + /* new vertex */ + remap[i] = i; + finalIndices[i] = numVerts++; + } + + dst_mesh->verts = Model_Malloc( numVerts * header.num_frames * sizeof( aliasVert_t ) ); + dst_mesh->tcoords = Model_Malloc( numVerts * sizeof( tcoord_t ) ); + dst_mesh->numVerts = numVerts; + + /* load all skins */ + src_skin = ( char * )rawdata + header.ofs_skins; + if( ( byte * )( src_skin + MAX_SKINNAME * header.num_skins ) > rawend ) { + Com_EPrintf( "%s has bad skins offset\n", model->name ); + goto fail; + } + for( i = 0; i < header.num_skins; i++ ) { + Q_strncpyz( skinname, src_skin, sizeof( skinname ) ); + skin = R_FindImage( skinname, it_skin ); + if( !skin ) { + skin = r_notexture; + } + dst_mesh->skins[i] = skin; + src_skin += MAX_SKINNAME; + } + dst_mesh->numSkins = header.num_skins; + + /* load all tcoords */ + src_tc = ( dstvert_t * )( ( byte * )rawdata + header.ofs_st ); + dst_tc = dst_mesh->tcoords; + skin = dst_mesh->skins[0]; + scaleS = 1.0f / header.skinwidth; + scaleT = 1.0f / header.skinheight; + for( i = 0; i < numIndices; i++ ) { + if( remap[i] == i ) { + dst_tc[ finalIndices[i] ].st[0] = LittleShort( src_tc[ tcIndices[i] ].s ) * scaleS; + dst_tc[ finalIndices[i] ].st[1] = LittleShort( src_tc[ tcIndices[i] ].t ) * scaleT; + } + } + + /* load all frames */ + model->frames = Model_Malloc( header.num_frames * sizeof( aliasFrame_t ) ); + model->numFrames = header.num_frames; + + offset = header.ofs_frames; + dst_frame = model->frames; + for( j = 0; j < header.num_frames; j++ ) { + src_frame = ( daliasframe_t * )( ( byte * )rawdata + offset ); + if( ( byte * )( src_frame + 1 ) > rawend ) { + Com_EPrintf( "%s has bad offset for frame %d\n", model->name, j ); + goto fail; + } + + LittleVector( src_frame->scale, dst_frame->scale ); + LittleVector( src_frame->translate, dst_frame->translate ); + + /* load frame vertices */ + ClearBounds( mins, maxs ); + for( i = 0; i < numIndices; i++ ) { + if( remap[i] == i ) { + src_vert = &src_frame->verts[ vertIndices[i] ]; + dst_vert = &dst_mesh->verts[ j * numVerts + finalIndices[i] ]; + + dst_vert->pos[0] = src_vert->v[0]; + dst_vert->pos[1] = src_vert->v[1]; + dst_vert->pos[2] = src_vert->v[2]; + k = src_vert->lightnormalindex; + if( k >= NUMVERTEXNORMALS ) { + Com_EPrintf( "%s has bad normal index\n", model->name ); + goto fail; + } + dst_vert->normalIndex = k; + + for ( k = 0; k < 3; k++ ) { + val = dst_vert->pos[k]; + if( val < mins[k] ) + mins[k] = val; + if( val > maxs[k] ) + maxs[k] = val; + } + } + } + + VectorVectorScale( mins, dst_frame->scale, mins ); + VectorVectorScale( maxs, dst_frame->scale, maxs ); + + dst_frame->radius = RadiusFromBounds( mins, maxs ); + + VectorAdd( mins, dst_frame->translate, dst_frame->bounds[0] ); + VectorAdd( maxs, dst_frame->translate, dst_frame->bounds[1] ); + + offset += header.framesize; + dst_frame++; + } + + sys.HunkEnd( &model->pool ); + + return qtrue; + +fail: + sys.HunkFree( &model->pool ); + return qfalse; +} + +static qboolean Model_LoadMd3( model_t *model, const byte *rawdata, int length ) { + dmd3header_t header; + uint32 offset; + dmd3frame_t *src_frame; + dmd3mesh_t *src_mesh; + dmd3vertex_t *src_vert; + dmd3coord_t *src_tc; + dmd3skin_t *src_skin; + uint32 *src_idx; + aliasFrame_t *dst_frame; + aliasMesh_t *dst_mesh; + aliasVert_t *dst_vert; + tcoord_t *dst_tc; + uint32 *dst_idx; + uint32 numVerts, numTris, numSkins; + uint32 totalVerts; + char skinname[MAX_QPATH]; + image_t *skin; + const byte *rawend; + int i, j; + + if( length < sizeof( header ) ) { + Com_EPrintf( "%s has length < header length\n", model->name ); + return qfalse; + } + + /* byte swap the header */ + header = *( dmd3header_t * )rawdata; + for( i = 0; i < sizeof( header )/4; i++ ) { + (( uint32 * )&header)[i] = LittleLong( (( uint32 * )&header)[i] ); + } + + if( header.ident != MD3_IDENT ) { + Com_EPrintf( "%s is not an MD3 file\n", model->name ); + return qfalse; + } + + if( header.version != MD3_VERSION ) { + Com_EPrintf( "%s has bad version: %d != %d\n", + model->name, header.version, MD3_VERSION ); + return qfalse; + } + + if( header.num_frames < 1 ) { + Com_EPrintf( "%s has bad number of frames: %d\n", + model->name, header.num_frames ); + return qfalse; + } + if( header.num_frames > MD3_MAX_FRAMES ) { + Com_EPrintf( "%s has too many frames: %d > %d\n", + model->name, header.num_frames, MD3_MAX_FRAMES ); + return qfalse; + } + + if( header.num_meshes < 1 || header.num_meshes > MD3_MAX_SURFACES ) { + Com_EPrintf( "%s has bad number of meshes\n", model->name ); + return qfalse; + } + + model->type = MODEL_ALIAS; + sys.HunkBegin( &model->pool, 0x400000 ); + model->numFrames = header.num_frames; + model->numMeshes = header.num_meshes; + model->meshes = Model_Malloc( sizeof( aliasMesh_t ) * header.num_meshes ); + model->frames = Model_Malloc( sizeof( aliasFrame_t ) * header.num_frames ); + + rawend = rawdata + length; + + /* load all frames */ + src_frame = ( dmd3frame_t * )( rawdata + header.ofs_frames ); + if( ( byte * )( src_frame + header.num_frames ) > rawend ) { + Com_EPrintf( "%s has bad frames offset\n", model->name ); + goto fail; + } + dst_frame = model->frames; + for( i = 0; i < header.num_frames; i++ ) { + LittleVector( src_frame->translate, dst_frame->translate ); + VectorSet( dst_frame->scale, MD3_XYZ_SCALE, MD3_XYZ_SCALE, MD3_XYZ_SCALE ); + + LittleVector( src_frame->mins, dst_frame->bounds[0] ); + LittleVector( src_frame->maxs, dst_frame->bounds[1] ); + dst_frame->radius = LittleFloat( src_frame->radius ); + + src_frame++; dst_frame++; + } + + /* load all meshes */ + src_mesh = ( dmd3mesh_t * )( rawdata + header.ofs_meshes ); + dst_mesh = model->meshes; + for( i = 0; i < header.num_meshes; i++ ) { + if( ( byte * )( src_mesh + 1 ) > rawend ) { + Com_EPrintf( "%s has bad offset for mesh %d\n", model->name, i ); + goto fail; + } + + numVerts = LittleLong( src_mesh->num_verts ); + numTris = LittleLong( src_mesh->num_tris ); + + if( numVerts < 3 || numVerts > TESS_MAX_VERTICES ) { + Com_EPrintf( "%s has bad number of vertices for mesh %d\n", model->name, i ); + goto fail; + } + if( numTris < 1 || numTris > TESS_MAX_INDICES / 3 ) { + Com_EPrintf( "%s has bad number of faces for mesh %d\n", model->name, i ); + goto fail; + } + + dst_mesh->numTris = numTris; + dst_mesh->numIndices = numTris * 3; + dst_mesh->numVerts = numVerts; + dst_mesh->verts = Model_Malloc( sizeof( aliasVert_t ) * numVerts * header.num_frames ); + dst_mesh->tcoords = Model_Malloc( sizeof( tcoord_t ) * numVerts ); + dst_mesh->indices = Model_Malloc( sizeof( uint32 ) * numTris * 3 ); + + /* load all skins */ + numSkins = LittleLong( src_mesh->num_skins ); + if( numSkins > MAX_MD2SKINS ) { + Com_EPrintf( "%s has bad number of skins for mesh %d\n", model->name, i ); + goto fail; + } + offset = LittleLong( src_mesh->ofs_skins ); + src_skin = ( dmd3skin_t * )( ( byte * )src_mesh + offset ); + if( ( byte * )( src_skin + numSkins ) > rawend ) { + Com_EPrintf( "%s has bad skins offset for mesh %d\n", model->name, i ); + goto fail; + } + for( j = 0; j < numSkins; j++ ) { + Q_strncpyz( skinname, src_skin->name, sizeof( skinname ) ); + skin = R_FindImage( skinname, it_skin ); + if( !skin ) { + skin = r_notexture; + } + dst_mesh->skins[j] = skin; + } + dst_mesh->numSkins = numSkins; + + /* load all vertices */ + totalVerts = numVerts * header.num_frames; + offset = LittleLong( src_mesh->ofs_verts ); + src_vert = ( dmd3vertex_t * )( ( byte * )src_mesh + offset ); + if( ( byte * )( src_vert + totalVerts ) > rawend ) { + Com_EPrintf( "%s has bad vertices offset for mesh %d\n", model->name, i ); + goto fail; + } + dst_vert = dst_mesh->verts; + for( j = 0; j < totalVerts; j++ ) { + dst_vert->pos[0] = LittleShort( src_vert->point[0] ); + dst_vert->pos[1] = LittleShort( src_vert->point[1] ); + dst_vert->pos[2] = LittleShort( src_vert->point[2] ); + + dst_vert->normalIndex = ll2byte[src_vert->norm[0]] + [src_vert->norm[1]]; + + src_vert++; dst_vert++; + } + + /* load all texture coords */ + offset = LittleLong( src_mesh->ofs_tcs ); + src_tc = ( dmd3coord_t * )( ( byte * )src_mesh + offset ); + if( ( byte * )( src_tc + numVerts ) > rawend ) { + Com_EPrintf( "%s has bad tcoords offset for mesh %d\n", model->name, i ); + goto fail; + } + dst_tc = dst_mesh->tcoords; + for( j = 0; j < numVerts; j++ ) { + dst_tc->st[0] = LittleFloat( src_tc->st[0] ); + dst_tc->st[1] = LittleFloat( src_tc->st[1] ); + src_tc++; dst_tc++; + } + + /* load all triangle indices */ + offset = LittleLong( src_mesh->ofs_indexes ); + src_idx = ( uint32 * )( ( byte * )src_mesh + offset ); + if( ( byte * )( src_idx + numTris * 3 ) > rawend ) { + Com_EPrintf( "%s has bad indices offset for mesh %d\n", model->name, i ); + goto fail; + } + dst_idx = dst_mesh->indices; + for( j = 0; j < numTris; j++ ) { + dst_idx[0] = LittleLong( src_idx[0] ); + dst_idx[1] = LittleLong( src_idx[1] ); + dst_idx[2] = LittleLong( src_idx[2] ); + if( dst_idx[0] >= numVerts || dst_idx[1] >= numVerts || dst_idx[2] >= numVerts ) { + Com_EPrintf( "%s has bad indices for triangle %d in mesh %d\n", model->name, j, i ); + goto fail; + } + src_idx += 3; dst_idx += 3; + } + + offset = LittleLong( src_mesh->meshsize ); + src_mesh = ( dmd3mesh_t * )( ( byte * )src_mesh + offset ); + dst_mesh++; + } + + sys.HunkEnd( &model->pool ); + + return qtrue; + +fail: + sys.HunkFree( &model->pool ); + return qfalse; +} + +#define Model_TempAlloc( ptr, size ) \ + do { ptr = ( void * )data; data += size; offset += size; } while( 0 ) + +static qboolean Model_WriteMd3( model_t *model, fileHandle_t f ) { + dmd3header_t *header; + uint32 offset, length; + byte *data; + dmd3frame_t *dst_frame; + dmd3mesh_t *dst_mesh; + dmd3vertex_t *dst_vert; + dmd3coord_t *dst_tc; + dmd3skin_t *dst_skin; + uint32 *dst_idx; + aliasFrame_t *src_frame; + aliasMesh_t *src_mesh; + aliasVert_t *src_vert; + tcoord_t *src_tc; + uint32 *src_idx; + uint32 totalVerts; + int i, j; + qboolean ret; + short pos[3]; + + // precalculate total length + length = sizeof( dmd3header_t ) + + model->numFrames * sizeof( dmd3frame_t ); + src_mesh = model->meshes; + for( i = 0; i < model->numMeshes; i++ ) { + length += sizeof( dmd3mesh_t ) + + src_mesh->numSkins * sizeof( dmd3skin_t ) + + src_mesh->numVerts * model->numFrames * sizeof( dmd3vertex_t ) + + src_mesh->numVerts * sizeof( dmd3coord_t ) + + src_mesh->numIndices * sizeof( uint32 ); + src_mesh++; + } + + data = fs.AllocTempMem( length ); + offset = 0; + + // write header + Model_TempAlloc( header, sizeof( dmd3header_t ) ); + header->ident = LittleLong( MD3_IDENT ); + header->version = LittleLong( MD3_VERSION ); + memset( header->filename, 0, sizeof( header->filename ) ); + header->flags = 0; + header->num_frames = LittleLong( model->numFrames ); + header->num_tags = 0; + header->num_meshes = LittleLong( model->numMeshes ); + header->num_skins = 0; + + // write all frames + header->ofs_frames = LittleLong( offset ); + src_frame = model->frames; + Model_TempAlloc( dst_frame, model->numFrames * sizeof( dmd3frame_t ) ); + for( i = 0; i < model->numFrames; i++ ) { + LittleVector( src_frame->translate, dst_frame->translate ); + + LittleVector( src_frame->bounds[0], dst_frame->mins ); + LittleVector( src_frame->bounds[1], dst_frame->maxs ); + dst_frame->radius = LittleFloat( src_frame->radius ); + + memset( dst_frame->creator, 0, sizeof( dst_frame->creator ) ); + + src_frame++; dst_frame++; + } + + // write all meshes + header->ofs_meshes = LittleLong( offset ); + src_mesh = model->meshes; + for( i = 0; i < model->numMeshes; i++ ) { + offset = 0; + Model_TempAlloc( dst_mesh, sizeof( dmd3mesh_t ) ); + dst_mesh->ident = LittleLong( MD3_IDENT ); + memset( dst_mesh->name, 0, sizeof( dst_mesh->name ) ); + dst_mesh->flags = 0; + dst_mesh->num_frames = LittleLong( model->numFrames ); + dst_mesh->num_skins = LittleLong( src_mesh->numSkins ); + dst_mesh->num_tris = LittleLong( src_mesh->numTris ); + dst_mesh->num_verts = LittleLong( src_mesh->numVerts ); + + // write all skins + dst_mesh->ofs_skins = LittleLong( offset ); + Model_TempAlloc( dst_skin, sizeof( dmd3skin_t ) * src_mesh->numSkins ); + for( j = 0; j < src_mesh->numSkins; j++ ) { + memcpy( dst_skin->name, src_mesh->skins[j]->name, MAX_QPATH ); + dst_skin->unused = 0; + dst_skin++; + } + + // write all vertices + dst_mesh->ofs_verts = LittleLong( offset ); + totalVerts = src_mesh->numVerts * model->numFrames; + src_vert = src_mesh->verts; + src_frame = model->frames; + Model_TempAlloc( dst_vert, sizeof( dmd3vertex_t ) * totalVerts ); + for( j = 0; j < totalVerts; j++ ) { + pos[0] = src_vert->pos[0] * src_frame->scale[0] / MD3_XYZ_SCALE; + pos[1] = src_vert->pos[1] * src_frame->scale[1] / MD3_XYZ_SCALE; + pos[2] = src_vert->pos[2] * src_frame->scale[2] / MD3_XYZ_SCALE; + + dst_vert->point[0] = LittleShort( pos[0] ); + dst_vert->point[1] = LittleShort( pos[1] ); + dst_vert->point[2] = LittleShort( pos[2] ); + + // TODO: calc normal + + src_vert++; dst_vert++; + } + + // write all texture coords + dst_mesh->ofs_tcs = LittleLong( offset ); + src_tc = src_mesh->tcoords; + Model_TempAlloc( dst_tc, sizeof( dmd3coord_t ) * src_mesh->numVerts ); + for( j = 0; j < src_mesh->numVerts; j++ ) { + dst_tc->st[0] = LittleFloat( src_tc->st[0] ); + dst_tc->st[1] = LittleFloat( src_tc->st[1] ); + src_tc++; dst_tc++; + } + + // write all triangle indices + dst_mesh->ofs_indexes = LittleLong( offset ); + src_idx = src_mesh->indices; + Model_TempAlloc( dst_idx, sizeof( uint32 ) * src_mesh->numIndices ); + for( j = 0; j < src_mesh->numTris; j++ ) { + dst_idx[0] = LittleLong( src_idx[0] ); + dst_idx[1] = LittleLong( src_idx[1] ); + dst_idx[2] = LittleLong( src_idx[2] ); + src_idx += 3; dst_idx += 3; + } + + dst_mesh->meshsize = LittleLong( offset ); + } + + header->ofs_tags = 0; + header->ofs_end = length; + + // write model to disk + ret = qtrue; + if( fs.Write( header, length, f ) != length ) { + ret = qfalse; + } + + fs.FreeFile( header ); + + return ret; +} + +static qboolean Model_LoadSp2( model_t *model, const byte *rawdata, int length ) { + dsprite_t *header; + dsprframe_t *src_frame; + spriteFrame_t *dst_frame; + int ident, version, numframes; + int i, width, height; + char buffer[MAX_SKINNAME]; + image_t *image; + + if( length < sizeof( *header ) ) { + Com_EPrintf( "%s has length < header length\n", model->name ); + return qfalse; + } + + /* byte swap the header */ + header = ( dsprite_t * )rawdata; + for( i = 0; i < sizeof( header )/4; i++ ) { + (( uint32 * )&header)[i] = LittleLong( (( uint32 * )&header)[i] ); + } + + ident = LittleLong( header->ident ); + version = LittleLong( header->version ); + numframes = LittleLong( header->numframes ); + + if( ident != IDSPRITEHEADER ) { + Com_EPrintf( "%s is not an sp2 file\n", model->name ); + return qfalse; + } + + if( version != SPRITE_VERSION ) { + Com_EPrintf( "%s has bad version: %d != %d\n", + model->name, version, ALIAS_VERSION ); + return qfalse; + } + + if( numframes < 1 ) { + Com_EPrintf( "%s has bad number of frames: %d\n", + model->name, numframes ); + return qfalse; + } + + if( numframes > MAX_MD2SKINS ) { + Com_EPrintf( "%s has too many frames: %d > %d\n", + model->name, numframes, MAX_MD2SKINS ); + return qfalse; + } + + model->type = MODEL_SPRITE; + sys.HunkBegin( &model->pool, 0x10000 ); + + model->sframes = Model_Malloc( sizeof( spriteFrame_t ) * numframes ); + model->numFrames = numframes; + + src_frame = header->frames; + dst_frame = model->sframes; + for( i = 0; i < numframes; i++ ) { + width = LittleLong( src_frame->width ); + height = LittleLong( src_frame->height ); + if( width <= 0 || height <= 0 ) { + Com_WPrintf( "%s has bad image dimensions for frame #%d: %d x %d\n", + model->name, width, height, i ); + width = 1; + height = 1; + } + dst_frame->width = width; + dst_frame->height = height; + dst_frame->x = LittleLong( src_frame->origin_x ); + dst_frame->y = LittleLong( src_frame->origin_y ); + + Q_strncpyz( buffer, src_frame->name, sizeof( buffer ) ); + image = R_FindImage( buffer, it_sprite ); + if( !image ) { + Com_DPrintf( "Couldn't find image '%s' for frame #%d for sprite '%s'\n", + buffer, i, model->name ); + image = r_notexture; + } + dst_frame->image = image; + + dst_frame++; src_frame++; + } + + sys.HunkEnd( &model->pool ); + + return qtrue; +} + +void GL_GetModelSize( qhandle_t hModel, vec3_t mins, vec3_t maxs ) { + VectorClear( mins ); + VectorClear( maxs ); +} + +qhandle_t GL_RegisterModel( const char *name ) { + int index, length, nameLength; + model_t *model; + byte *rawdata; + uint32 ident; + qboolean success; + char buffer[MAX_QPATH]; + + if( name[0] == '*' ) { + /* inline bsp model */ + index = atoi( name + 1 ); + if( index < 1 || index >= r_world.numSubmodels ) { + Com_Error( ERR_DROP, "GL_RegisterModel: bad inline model index: %d", index ); + } + return ~index; + } + + if( !strcmp( r_world.name, name ) ) { + /* TODO: change client code and remove this hack */ + return 0; + } + + nameLength = strlen( name ); + if( nameLength > MAX_QPATH - 1 ) { + Com_Error( ERR_DROP, "GL_RegisterModel: oversize name: %d chars", nameLength ); + } + + model = Model_Find( name ); + if( model ) { + goto finish; + } + + length = -1; + buffer[0] = 0; + if( gl_override_models->integer ) { + if( nameLength > 4 && !strcmp( name + nameLength - 4, ".md2" ) ) { + strcpy( buffer, name ); + buffer[nameLength - 1] = '3'; + } + length = fs.LoadFile( buffer, ( void ** )&rawdata ); + } + if( length == -1 ) { + length = fs.LoadFile( name, ( void ** )&rawdata ); + if( length == -1 ) { + Com_DPrintf( "Couldn't load %s\n", name ); + return 0; + } + } + + if( length < 4 ) { + Com_WPrintf( "%s has invalid length\n", name ); + return 0; + } + + model = Model_Alloc( name ); + + ident = LittleLong( *( uint32 * )rawdata ); + switch( ident ) { + case IDALIASHEADER: + success = Model_LoadMd2( model, rawdata, length ); + if( success && gl_override_models->integer > 1 ) { + fileHandle_t f; + + fs.FOpenFile( buffer, &f, FS_MODE_WRITE ); + if( f ) { + Model_WriteMd3( model, f ); + fs.FCloseFile( f ); + } + } + break; + case MD3_IDENT: + success = Model_LoadMd3( model, rawdata, length ); + break; + case IDSPRITEHEADER: + success = Model_LoadSp2( model, rawdata, length ); + break; + case IDBSPHEADER: + Com_WPrintf( "Loaded bsp model '%s' after the world\n", name ); + success = qfalse; + break; + default: + Com_WPrintf( "%s has unknown ident: %x\n", name, ident ); + success = qfalse; + break; + } + + fs.FreeFile( rawdata ); + + if( !success ) { + model->name[0] = 0; + return 0; + } + +finish: + index = ( model - r_models ) + 1; + return index; +} + +modelType_t *GL_ModelForHandle( qhandle_t hModel ) { + model_t *model; + bspSubmodel_t *submodel; + + if( !hModel ) { + return NULL; + } + + if( hModel & 0x80000000 ) { + hModel = ~hModel; + + if( hModel < 1 || hModel >= r_world.numSubmodels ) { + Com_Error( ERR_DROP, "GL_ModelForHandle: submodel %d out of range", hModel ); + } + + submodel = &r_world.submodels[hModel]; + return &submodel->type; + } + + if( hModel > r_numModels ) { + Com_Error( ERR_DROP, "GL_ModelForHandle: %d out of range", hModel ); + } + model = &r_models[ hModel - 1 ]; + if( !model->name[0] ) { + return NULL; + } + return &model->type; + +} + +void GL_InitModels( void ) { + float s[2], c[2]; + vec3_t normal; + int i, j; + + for( i = 0; i < 256; i++ ) { + for( j = 0; j < 256; j++ ) { + s[0] = sin( i / 255.0f ); + c[0] = cos( i / 255.0f ); + + s[1] = sin( j / 255.0f ); + c[1] = cos( j / 255.0f ); + + VectorSet( normal, s[0] * c[1], s[0] * s[1], c[0] ); + ll2byte[i][j] = DirToByte( normal ); + } + } + + gl_override_models = cvar.Get( "r_override_models", "0", + CVAR_ARCHIVE|CVAR_LATCHED ); + + + cmd.AddCommand( "modellist", Model_List_f ); +} + +void GL_ShutdownModels( void ) { + Model_FreeAll(); + cmd.RemoveCommand( "modellist" ); +} |