summaryrefslogtreecommitdiff
path: root/src/gl_models.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gl_models.c')
-rw-r--r--src/gl_models.c457
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;
+}
+