summaryrefslogtreecommitdiff
path: root/source/r_models.c
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2008-08-16 10:19:42 +0000
committerAndrey Nazarov <skuller@skuller.net>2008-08-16 10:19:42 +0000
commit1526e22e4ff29153e9c127081e8ea8d9e2f33b8c (patch)
treeb361766433d4a7b4a111865afd52803e2bbf7754 /source/r_models.c
parente826e5f176f21cd18b3bbc22887a266835ada57c (diff)
Split some monolithic include files into smaller ones.
Use single BSP models cache for refresh and collision subsystems. Refresh libraries may not longer be dynamically loaded. Made gi.TagMalloc use separate tag namespace to avoid conflicts with engine reserverd tags. Fixed listing order of MVD channels in chooser menu. A lot of misc changes... MSVC build is definitely broken now.
Diffstat (limited to 'source/r_models.c')
-rw-r--r--source/r_models.c418
1 files changed, 418 insertions, 0 deletions
diff --git a/source/r_models.c b/source/r_models.c
new file mode 100644
index 0000000..795721e
--- /dev/null
+++ b/source/r_models.c
@@ -0,0 +1,418 @@
+/*
+Copyright (C) 2008 Andrey Nazarov
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "com_local.h"
+#include "files.h"
+#include "sys_public.h"
+#include "q_list.h"
+#include "d_md2.h"
+#if USE_MD3
+#include "d_md3.h"
+#endif
+#include "d_sp2.h"
+#include "r_shared.h"
+#include "r_models.h"
+
+static model_t r_models[MAX_MODELS];
+static int r_numModels;
+
+#if USE_MD3
+static cvar_t *r_override_models;
+#endif
+
+static model_t *MOD_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;
+
+ return model;
+}
+
+static model_t *MOD_Find( const char *name ) {
+ model_t *model;
+ int i;
+
+ for( i = 0, model = r_models; i < r_numModels; i++, model++ ) {
+ if( !model->name[0] ) {
+ continue;
+ }
+ if( !FS_pathcmp( model->name, name ) ) {
+ return model;
+ }
+ }
+
+ return NULL;
+}
+
+static void MOD_List_f( void ) {
+ int i, count;
+ model_t *model;
+ size_t bytes;
+
+ Com_Printf( "------------------\n");
+ bytes = count = 0;
+
+ for( i = 0, model = r_models; i < r_numModels; i++, model++ ) {
+ if( !model->name[0] ) {
+ continue;
+ }
+ Com_Printf( "%8"PRIz" : %s\n", model->pool.mapped, model->name );
+ bytes += model->pool.mapped;
+ count++;
+ }
+ Com_Printf( "Total models: %d (out of %d slots)\n", count, r_numModels );
+ Com_Printf( "Total resident: %"PRIz"\n", bytes );
+}
+
+void MOD_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 ) {
+ // make sure it is paged in
+ Com_PageInMemory( model->pool.base, model->pool.cursize );
+ } else {
+ // don't need this model
+ Hunk_Free( &model->pool );
+ memset( model, 0, sizeof( *model ) );
+ }
+ }
+}
+
+void MOD_FreeAll( void ) {
+ model_t *model;
+ int i;
+
+ for( i = 0, model = r_models; i < r_numModels; i++, model++ ) {
+ if( !model->name[0] ) {
+ continue;
+ }
+
+ Hunk_Free( &model->pool );
+ memset( model, 0, sizeof( *model ) );
+ }
+
+ r_numModels = 0;
+}
+
+qboolean MOD_ValidateMD2( model_t *model, dmd2header_t *header, size_t length ) {
+ size_t end;
+
+ // check ident and version
+ if( header->ident != MD2_IDENT ) {
+ Com_WPrintf( "%s is not an MD2 file\n", model->name );
+ return qfalse;
+ }
+ if( header->version != MD2_VERSION ) {
+ Com_WPrintf( "%s has bad version: %d instead of %d\n",
+ model->name, header->version, MD2_VERSION );
+ return qfalse;
+ }
+
+ // check triangles
+ if( header->num_tris < 1 || header->num_tris > MD2_MAX_TRIANGLES ) {
+ Com_WPrintf( "%s has bad number of triangles\n", model->name );
+ return qfalse;
+ }
+ end = header->ofs_tris + sizeof( dmd2triangle_t ) * header->num_tris;
+ if( header->ofs_tris < sizeof( header ) || end < header->ofs_tris || end > length ) {
+ Com_WPrintf( "%s has bad triangles offset\n", model->name );
+ return qfalse;
+ }
+
+ // check st
+ if( header->num_st < 3 || header->num_st > MD2_MAX_VERTS ) {
+ Com_WPrintf( "%s has bad number of st\n", model->name );
+ return qfalse;
+ }
+ end = header->ofs_st + sizeof( dmd2stvert_t ) * header->num_st;
+ if( header->ofs_st < sizeof( header ) || end < header->ofs_st || end > length ) {
+ Com_WPrintf( "%s has bad st offset\n", model->name );
+ return qfalse;
+ }
+
+ // check xyz and frames
+ if( header->num_xyz < 3 || header->num_xyz > MD2_MAX_VERTS ) {
+ Com_WPrintf( "%s has bad number of xyz\n", model->name );
+ return qfalse;
+ }
+ if( header->num_frames < 1 || header->num_frames > MD2_MAX_FRAMES ) {
+ Com_WPrintf( "%s has bad number of frames\n", model->name );
+ return qfalse;
+ }
+ end = sizeof( dmd2frame_t ) + ( header->num_xyz - 1 ) * sizeof( dmd2trivertx_t );
+ if( header->framesize < end || header->framesize > MD2_MAX_FRAMESIZE ) {
+ Com_WPrintf( "%s has bad frame size\n", model->name );
+ return qfalse;
+ }
+ end = header->ofs_frames + header->framesize * header->num_frames;
+ if( header->ofs_frames < sizeof( header ) || end < header->ofs_frames || end > length ) {
+ Com_WPrintf( "%s has bad frames offset\n", model->name );
+ return qfalse;
+ }
+
+ // check skins
+ if( header->num_skins > MAX_ALIAS_SKINS ) {
+ Com_WPrintf( "%s has bad number of skins\n", model->name );
+ return qfalse;
+ }
+ if( header->num_skins ) {
+ end = header->ofs_skins + MD2_MAX_SKINNAME * header->num_skins;
+ if( header->ofs_skins < sizeof( header ) || end < header->ofs_skins || end > length ) {
+ Com_WPrintf( "%s has bad skins offset\n", model->name );
+ return qfalse;
+ }
+ }
+ if( header->skinwidth < 1 || header->skinwidth > MD2_MAX_SKINWIDTH ) {
+ Com_WPrintf( "%s has bad skin width\n", model->name );
+ return qfalse;
+ }
+ if( header->skinheight < 1 || header->skinheight > MD2_MAX_SKINHEIGHT ) {
+ Com_WPrintf( "%s has bad skin height\n", model->name );
+ return qfalse;
+ }
+ return qtrue;
+}
+
+static qboolean MOD_LoadSP2( model_t *model, const void *rawdata, size_t length ) {
+ dsp2header_t header;
+ dsp2frame_t *src_frame;
+ mspriteframe_t *dst_frame;
+ unsigned w, h, x, y;
+ char buffer[SP2_MAX_FRAMENAME];
+ image_t *image;
+ int i;
+
+ if( length < sizeof( header ) ) {
+ Com_EPrintf( "%s is too small\n", model->name );
+ return qfalse;
+ }
+
+ // byte swap the header
+ header = *( dsp2header_t * )rawdata;
+ for( i = 0; i < sizeof( header )/4; i++ ) {
+ (( uint32_t * )&header)[i] = LittleLong( (( uint32_t * )&header)[i] );
+ }
+
+ if( header.ident != SP2_IDENT ) {
+ Com_EPrintf( "%s is not an SP2 file\n", model->name );
+ return qfalse;
+ }
+ if( header.version != SP2_VERSION ) {
+ Com_EPrintf( "%s has bad version: %d instead of %d\n",
+ model->name, header.version, SP2_VERSION );
+ return qfalse;
+ }
+ if( header.numframes < 1 || header.numframes > SP2_MAX_FRAMES ) {
+ Com_EPrintf( "%s has bad number of frames: %d\n",
+ model->name, header.numframes );
+ return qfalse;
+ }
+ if( sizeof( dsp2header_t ) + sizeof( dsp2frame_t ) * header.numframes > length ) {
+ Com_EPrintf( "%s has frames out of bounds\n", model->name );
+ return qfalse;
+ }
+
+ Hunk_Begin( &model->pool, 0x10000 );
+
+ model->spriteframes = Model_Malloc( sizeof( mspriteframe_t ) * header.numframes );
+ model->numframes = header.numframes;
+
+ src_frame = ( dsp2frame_t * )( ( byte * )rawdata + sizeof( dsp2header_t ) );
+ dst_frame = model->spriteframes;
+ for( i = 0; i < header.numframes; i++ ) {
+ w = LittleLong( src_frame->width );
+ h = LittleLong( src_frame->height );
+ if( w < 1 || h < 1 || w > MAX_TEXTURE_SIZE || h > MAX_TEXTURE_SIZE ) {
+ Com_WPrintf( "%s has bad frame dimensions\n", model->name );
+ w = 1;
+ h = 1;
+ }
+ dst_frame->width = w;
+ dst_frame->height = h;
+
+ // FIXME: are these signed?
+ x = LittleLong( src_frame->origin_x );
+ y = LittleLong( src_frame->origin_y );
+ if( x > 8192 || y > 8192 ) {
+ Com_WPrintf( "%s has bad frame origin\n", model->name );
+ x = y = 0;
+ }
+ dst_frame->origin_x = x;
+ dst_frame->origin_y = y;
+
+ memcpy( buffer, src_frame->name, sizeof( buffer ) );
+ buffer[sizeof( buffer ) - 1] = 0;
+ image = IMG_Find( buffer, it_sprite );
+ if( !image ) {
+ image = r_notexture;
+ }
+ dst_frame->image = image;
+
+ src_frame++;
+ dst_frame++;
+ }
+
+ Hunk_End( &model->pool );
+
+ return qtrue;
+}
+
+
+qhandle_t R_RegisterModel( const char *name ) {
+ int index;
+ size_t namelen, filelen;
+ model_t *model;
+ byte *rawdata;
+ uint32_t ident;
+ qboolean success;
+
+ if( name[0] == '*' ) {
+ // inline bsp model
+ index = atoi( name + 1 );
+ return ~index;
+ }
+
+ namelen = strlen( name );
+ if( namelen >= MAX_QPATH ) {
+ Com_Error( ERR_DROP, "%s: oversize name", __func__ );
+ }
+
+ model = MOD_Find( name );
+ if( model ) {
+ MOD_Reference( model );
+ goto finish;
+ }
+
+ filelen = 0;
+ rawdata = NULL;
+
+#if USE_MD3
+ if( r_override_models->integer ) {
+ char buffer[MAX_QPATH];
+
+ if( namelen > 4 && !Q_stricmp( name + namelen - 4, ".md2" ) ) {
+ memcpy( buffer, name, namelen + 1 );
+ buffer[namelen - 1] = '3';
+ filelen = FS_LoadFile( buffer, ( void ** )&rawdata );
+ }
+ }
+ if( !rawdata )
+#endif
+ {
+ filelen = FS_LoadFile( name, ( void ** )&rawdata );
+ if( !rawdata ) {
+ Com_DPrintf( "Couldn't load %s\n", name );
+ return 0;
+ }
+ }
+
+ if( filelen < 4 ) {
+ Com_WPrintf( "%s: %s: file too short\n", __func__, name );
+ return 0;
+ }
+
+ model = MOD_Alloc( name );
+
+ ident = LittleLong( *( uint32_t * )rawdata );
+ switch( ident ) {
+ case MD2_IDENT:
+ success = MOD_LoadMD2( model, rawdata, filelen );
+ break;
+#if USE_MD3
+ case MD3_IDENT:
+ success = MOD_LoadMD3( model, rawdata, filelen );
+ break;
+#endif
+ case SP2_IDENT:
+ success = MOD_LoadSP2( model, rawdata, filelen );
+ break;
+ default:
+ Com_WPrintf( "%s: %s: unknown ident: %x\n", __func__, name, ident );
+ success = qfalse;
+ break;
+ }
+
+ FS_FreeFile( rawdata );
+
+ if( !success ) {
+ memset( model, 0, sizeof( *model ) );
+ return 0;
+ }
+
+finish:
+ index = ( model - r_models ) + 1;
+ return index;
+}
+
+model_t *MOD_ForHandle( qhandle_t h ) {
+ model_t *model;
+
+ if( !h ) {
+ return NULL;
+ }
+ if( h < 0 || h > r_numModels ) {
+ Com_Error( ERR_DROP, "%s: %d out of range", __func__, h );
+ }
+ model = &r_models[ h - 1 ];
+ if( !model->name[0] ) {
+ return NULL;
+ }
+ return model;
+}
+
+void MOD_Init( void ) {
+ if( r_numModels ) {
+ Com_Error( ERR_FATAL, "%s: %d models not freed", __func__, r_numModels );
+ }
+
+#if USE_MD3
+ r_override_models = Cvar_Get( "r_override_models", "0",
+ CVAR_ARCHIVE|CVAR_FILES );
+#endif
+
+ Cmd_AddCommand( "modellist", MOD_List_f );
+}
+
+void MOD_Shutdown( void ) {
+ MOD_FreeAll();
+ Cmd_RemoveCommand( "modellist" );
+}
+