diff options
Diffstat (limited to 'src/gl_models.c')
-rw-r--r-- | src/gl_models.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/src/gl_models.c b/src/gl_models.c new file mode 100644 index 0000000..264495e --- /dev/null +++ b/src/gl_models.c @@ -0,0 +1,457 @@ +/* +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" +#include "d_md2.h" +#include "d_md3.h" +#include "d_sp2.h" + +qerror_t MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) { + dmd2header_t header; + dmd2frame_t *src_frame; + dmd2trivertx_t *src_vert; + dmd2triangle_t *src_tri; + dmd2stvert_t *src_tc; + maliasframe_t *dst_frame; + maliasvert_t *dst_vert; + maliasmesh_t *dst_mesh; + uint32_t *finalIndices; + maliastc_t *dst_tc; + int i, j, k; + uint16_t remap[MD2_MAX_TRIANGLES*3]; + uint16_t vertIndices[MD2_MAX_TRIANGLES*3]; + uint16_t tcIndices[MD2_MAX_TRIANGLES*3]; + int numverts, numindices; + char skinname[MAX_QPATH]; + char *src_skin; + image_t *skin; + vec_t scaleS, scaleT; + int val; + vec3_t mins, maxs; + qerror_t ret; + + if( length < sizeof( header ) ) { + return Q_ERR_FILE_TOO_SMALL; + } + + // byte swap the header + header = *( dmd2header_t * )rawdata; + for( i = 0; i < sizeof( header )/4; i++ ) { + (( uint32_t * )&header)[i] = LittleLong( (( uint32_t * )&header)[i] ); + } + + // validate the header + ret = MOD_ValidateMD2( model, &header, length ); + if( ret ) { + return ret; + } + + Hunk_Begin( &model->pool, 0x400000 ); + + // load all triangle indices + src_tri = ( dmd2triangle_t * )( ( byte * )rawdata + header.ofs_tris ); + for( i = 0; i < header.num_tris; i++ ) { + for( j = 0; j < 3; j++ ) { + uint16_t idx_xyz = LittleShort( src_tri->index_xyz[j] ); + uint16_t idx_st = LittleShort( src_tri->index_st[j] ); + + if( idx_xyz >= header.num_xyz || idx_st >= header.num_st ) { + ret = Q_ERR_BAD_INDEX; + goto fail; + } + + vertIndices[i*3+j] = idx_xyz; + tcIndices[i*3+j] = idx_st; + } + src_tri++; + } + + numindices = header.num_tris * 3; + + model->meshes = Model_Malloc( sizeof( maliasmesh_t ) ); + model->nummeshes = 1; + + dst_mesh = model->meshes; + dst_mesh->indices = Model_Malloc( numindices * sizeof( uint32_t ) ); + dst_mesh->numtris = header.num_tris; + dst_mesh->numindices = numindices; + + for( i = 0; i < numindices; i++ ) { + remap[i] = 0xFFFF; + } + + numverts = 0; + src_tc = ( dmd2stvert_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( maliasvert_t ) ); + dst_mesh->tcoords = Model_Malloc( numverts * sizeof( maliastc_t ) ); + dst_mesh->numverts = numverts; + + // load all skins + src_skin = ( char * )rawdata + header.ofs_skins; + for( i = 0; i < header.num_skins; i++ ) { + Q_strlcpy( skinname, src_skin, sizeof( skinname ) ); + skin = IMG_Find( skinname, it_skin ); + if( !skin ) { + skin = r_notexture; + } + dst_mesh->skins[i] = skin; + src_skin += MD2_MAX_SKINNAME; + } + dst_mesh->numskins = header.num_skins; + + // load all tcoords + src_tc = ( dmd2stvert_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 ) { + float s = ( signed short )LittleShort( src_tc[ tcIndices[i] ].s ); + float t = ( signed short )LittleShort( src_tc[ tcIndices[i] ].t ); + + dst_tc[ finalIndices[i] ].st[0] = s * scaleS; + dst_tc[ finalIndices[i] ].st[1] = t * scaleT; + } + } + + // load all frames + model->frames = Model_Malloc( header.num_frames * sizeof( maliasframe_t ) ); + model->numframes = header.num_frames; + + src_frame = ( dmd2frame_t * )( ( byte * )rawdata + header.ofs_frames ); + dst_frame = model->frames; + for( j = 0; j < header.num_frames; j++ ) { + 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 ) { + ret = Q_ERR_BAD_INDEX; + 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] ); + + src_frame = ( dmd2frame_t * )( ( byte * )src_frame + header.framesize ); + dst_frame++; + } + + Hunk_End( &model->pool ); + return Q_ERR_SUCCESS; + +fail: + Hunk_Free( &model->pool ); + return ret; +} + +#if USE_MD3 +static byte ll2byte[256][256]; +static qboolean ll2byte_inited; + +static void ll2byte_init( 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 ); + } + } +} + +qerror_t MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) { + dmd3header_t header; + uint32_t offset; + dmd3frame_t *src_frame; + dmd3mesh_t *src_mesh; + dmd3vertex_t *src_vert; + dmd3coord_t *src_tc; + dmd3skin_t *src_skin; + uint32_t *src_idx; + maliasframe_t *dst_frame; + maliasmesh_t *dst_mesh; + maliasvert_t *dst_vert; + maliastc_t *dst_tc; + uint32_t *dst_idx; + uint32_t numverts, numtris, numskins; + uint32_t totalVerts; + char skinname[MAX_QPATH]; + image_t *skin; + const byte *rawend; + int i, j; + qerror_t ret; + + if( length < sizeof( header ) ) { + return Q_ERR_FILE_TOO_SMALL; + } + + // byte swap the header + header = *( dmd3header_t * )rawdata; + for( i = 0; i < sizeof( header )/4; i++ ) { + (( uint32_t * )&header)[i] = LittleLong( (( uint32_t * )&header)[i] ); + } + + if( header.ident != MD3_IDENT ) + return Q_ERR_UNKNOWN_FORMAT; + if( header.version != MD3_VERSION ) + return Q_ERR_UNKNOWN_FORMAT; + if( header.num_frames < 1 ) + return Q_ERR_TOO_FEW; + if( header.num_frames > MD3_MAX_FRAMES ) + return Q_ERR_TOO_MANY; + if( header.num_meshes < 1 ) + return Q_ERR_TOO_FEW; + if( header.num_meshes > MD3_MAX_MESHES ) + return Q_ERR_TOO_MANY; + + Hunk_Begin( &model->pool, 0x400000 ); + model->numframes = header.num_frames; + model->nummeshes = header.num_meshes; + model->meshes = Model_Malloc( sizeof( maliasmesh_t ) * header.num_meshes ); + model->frames = Model_Malloc( sizeof( maliasframe_t ) * header.num_frames ); + + rawend = ( byte * )rawdata + length; + + // load all frames + src_frame = ( dmd3frame_t * )( ( byte * )rawdata + header.ofs_frames ); + if( ( byte * )( src_frame + header.num_frames ) > rawend ) { + ret = Q_ERR_BAD_EXTENT; + 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++; + } + + if( !ll2byte_inited ) { + ll2byte_init(); + ll2byte_inited = qtrue; + } + + // load all meshes + src_mesh = ( dmd3mesh_t * )( ( byte * )rawdata + header.ofs_meshes ); + dst_mesh = model->meshes; + for( i = 0; i < header.num_meshes; i++ ) { + if( ( byte * )( src_mesh + 1 ) > rawend ) { + ret = Q_ERR_BAD_EXTENT; + goto fail; + } + + numverts = LittleLong( src_mesh->num_verts ); + if( numverts < 3 ) { + ret = Q_ERR_TOO_FEW; + goto fail; + } + if( numverts > TESS_MAX_VERTICES ) { + ret = Q_ERR_TOO_MANY; + goto fail; + } + + numtris = LittleLong( src_mesh->num_tris ); + if( numtris < 1 ) { + ret = Q_ERR_TOO_FEW; + goto fail; + } + if( numtris > TESS_MAX_INDICES / 3 ) { + ret = Q_ERR_TOO_MANY; + goto fail; + } + + dst_mesh->numtris = numtris; + dst_mesh->numindices = numtris * 3; + dst_mesh->numverts = numverts; + dst_mesh->verts = Model_Malloc( sizeof( maliasvert_t ) * numverts * header.num_frames ); + dst_mesh->tcoords = Model_Malloc( sizeof( maliastc_t ) * numverts ); + dst_mesh->indices = Model_Malloc( sizeof( uint32_t ) * numtris * 3 ); + + // load all skins + numskins = LittleLong( src_mesh->num_skins ); + if( numskins > MAX_ALIAS_SKINS ) { + ret = Q_ERR_TOO_MANY; + goto fail; + } + offset = LittleLong( src_mesh->ofs_skins ); + src_skin = ( dmd3skin_t * )( ( byte * )src_mesh + offset ); + if( ( byte * )( src_skin + numskins ) > rawend ) { + ret = Q_ERR_BAD_EXTENT; + goto fail; + } + for( j = 0; j < numskins; j++ ) { + Q_strlcpy( skinname, src_skin->name, sizeof( skinname ) ); + skin = IMG_Find( 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 ) { + ret = Q_ERR_BAD_EXTENT; + goto fail; + } + dst_vert = dst_mesh->verts; + for( j = 0; j < totalVerts; j++ ) { + dst_vert->pos[0] = ( signed short )LittleShort( src_vert->point[0] ); + dst_vert->pos[1] = ( signed short )LittleShort( src_vert->point[1] ); + dst_vert->pos[2] = ( signed short )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 ) { + ret = Q_ERR_BAD_EXTENT; + 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_t * )( ( byte * )src_mesh + offset ); + if( ( byte * )( src_idx + numtris * 3 ) > rawend ) { + ret = Q_ERR_BAD_EXTENT; + 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 ) { + ret = Q_ERR_BAD_INDEX; + goto fail; + } + src_idx += 3; dst_idx += 3; + } + + offset = LittleLong( src_mesh->meshsize ); + src_mesh = ( dmd3mesh_t * )( ( byte * )src_mesh + offset ); + dst_mesh++; + } + + Hunk_End( &model->pool ); + return Q_ERR_SUCCESS; + +fail: + Hunk_Free( &model->pool ); + return ret; +} +#endif + +void MOD_Reference( model_t *model ) { + int i, j; + + if( model->frames ) { + for( i = 0; i < model->nummeshes; i++ ) { + maliasmesh_t *mesh = &model->meshes[i]; + for( j = 0; j < mesh->numskins; j++ ) { + mesh->skins[j]->registration_sequence = registration_sequence; + } + } + } else if( model->spriteframes ) { + for( i = 0; i < model->numframes; i++ ) { + model->spriteframes[i].image->registration_sequence = registration_sequence; + } + } else { + Com_Error( ERR_FATAL, "%s: bad model type", __func__ ); + } + + model->registration_sequence = registration_sequence; +} + |