]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/model.c
set eol-style
[xonotic/netradiant.git] / tools / quake3 / q3map2 / model.c
index 15192e2a72f032c1a8c6d4afdb97b7be992fbeae..f303df75a7972d9d81467868b84e385823e6e518 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-\r
-----------------------------------------------------------------------------------\r
-\r
-This code has been altered significantly from its original form, to support\r
-several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-\r
-\r
-/* marker */\r
-#define MODEL_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-/* \r
-PicoPrintFunc()\r
-callback for picomodel.lib\r
-*/\r
-\r
-void PicoPrintFunc( int level, const char *str )\r
-{\r
-       if( str == NULL )\r
-               return;\r
-       switch( level )\r
-       {\r
-               case PICO_NORMAL:\r
-                       Sys_Printf( "%s\n", str );\r
-                       break;\r
-               \r
-               case PICO_VERBOSE:\r
-                       Sys_FPrintf( SYS_VRB, "%s\n", str );\r
-                       break;\r
-               \r
-               case PICO_WARNING:\r
-                       Sys_Printf( "WARNING: %s\n", str );\r
-                       break;\r
-               \r
-               case PICO_ERROR:\r
-                       Sys_Printf( "ERROR: %s\n", str );\r
-                       break;\r
-               \r
-               case PICO_FATAL:\r
-                       Error( "ERROR: %s\n", str );\r
-                       break;\r
-       }\r
-}\r
-\r
-\r
-\r
-/* \r
-PicoLoadFileFunc()\r
-callback for picomodel.lib\r
-*/\r
-\r
-void PicoLoadFileFunc( char *name, byte **buffer, int *bufSize )\r
-{\r
-       *bufSize = vfsLoadFile( (const char*) name, (void**) buffer, 0 );\r
-}\r
-\r
-\r
-\r
-/*\r
-FindModel() - ydnar\r
-finds an existing picoModel and returns a pointer to the picoModel_t struct or NULL if not found\r
-*/\r
-\r
-picoModel_t *FindModel( char *name, int frame )\r
-{\r
-       int                     i;\r
-       \r
-       \r
-       /* init */\r
-       if( numPicoModels <= 0 )\r
-               memset( picoModels, 0, sizeof( picoModels ) );\r
-       \r
-       /* dummy check */\r
-       if( name == NULL || name[ 0 ] == '\0' )\r
-               return NULL;\r
-       \r
-       /* search list */\r
-       for( i = 0; i < MAX_MODELS; i++ )\r
-       {\r
-               if( picoModels[ i ] != NULL &&\r
-                       !strcmp( PicoGetModelName( picoModels[ i ] ), name ) &&\r
-                       PicoGetModelFrameNum( picoModels[ i ] ) == frame )\r
-                       return picoModels[ i ];\r
-       }\r
-       \r
-       /* no matching picoModel found */\r
-       return NULL;\r
-}\r
-\r
-\r
-\r
-/*\r
-LoadModel() - ydnar\r
-loads a picoModel and returns a pointer to the picoModel_t struct or NULL if not found\r
-*/\r
-\r
-picoModel_t *LoadModel( char *name, int frame )\r
-{\r
-       int                             i;\r
-       picoModel_t             *model, **pm;\r
-       \r
-       \r
-       /* init */\r
-       if( numPicoModels <= 0 )\r
-               memset( picoModels, 0, sizeof( picoModels ) );\r
-       \r
-       /* dummy check */\r
-       if( name == NULL || name[ 0 ] == '\0' )\r
-               return NULL;\r
-       \r
-       /* try to find existing picoModel */\r
-       model = FindModel( name, frame );\r
-       if( model != NULL )\r
-               return model;\r
-       \r
-       /* none found, so find first non-null picoModel */\r
-       pm = NULL;\r
-       for( i = 0; i < MAX_MODELS; i++ )\r
-       {\r
-               if( picoModels[ i ] == NULL )\r
-               {\r
-                       pm = &picoModels[ i ];\r
-                       break;\r
-               }\r
-       }\r
-       \r
-       /* too many picoModels? */\r
-       if( pm == NULL )\r
-               Error( "MAX_MODELS (%d) exceeded, there are too many model files referenced by the map.", MAX_MODELS );\r
-       \r
-       /* attempt to parse model */\r
-       *pm = PicoLoadModel( (char*) name, frame );\r
-       \r
-       /* if loading failed, make a bogus model to silence the rest of the warnings */\r
-       if( *pm == NULL )\r
-       {\r
-               /* allocate a new model */\r
-               *pm = PicoNewModel();\r
-               if( *pm == NULL )\r
-                       return NULL;\r
-               \r
-               /* set data */\r
-               PicoSetModelName( *pm, name );\r
-               PicoSetModelFrameNum( *pm, frame );\r
-       }\r
-       \r
-       /* debug code */\r
-       #if 0\r
-       {\r
-               int                             numSurfaces, numVertexes;\r
-               picoSurface_t   *ps;\r
-               \r
-               \r
-               Sys_Printf( "Model %s\n", name );\r
-               numSurfaces = PicoGetModelNumSurfaces( *pm );\r
-               for( i = 0; i < numSurfaces; i++ )\r
-               {\r
-                       ps = PicoGetModelSurface( *pm, i );\r
-                       numVertexes = PicoGetSurfaceNumVertexes( ps );\r
-                       Sys_Printf( "Surface %d has %d vertexes\n", i, numVertexes );\r
-               }\r
-       }\r
-       #endif\r
-       \r
-       /* set count */\r
-       if( *pm != NULL )\r
-               numPicoModels++;\r
-       \r
-       /* return the picoModel */\r
-       return *pm;\r
-}\r
-\r
-\r
-\r
-/*\r
-InsertModel() - ydnar\r
-adds a picomodel into the bsp\r
-*/\r
-\r
-void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shaderInfo_t *celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale )\r
-{\r
-       int                                     i, j, k, s, numSurfaces;\r
-       m4x4_t                          identity, nTransform;\r
-       picoModel_t                     *model;\r
-       picoShader_t            *shader;\r
-       picoSurface_t           *surface;\r
-       shaderInfo_t            *si;\r
-       mapDrawSurface_t        *ds;\r
-       bspDrawVert_t           *dv;\r
-       char                            *picoShaderName;\r
-       char                            shaderName[ MAX_QPATH ];\r
-       picoVec_t                       *xyz, *normal, *st;\r
-       byte                            *color;\r
-       picoIndex_t                     *indexes;\r
-       remap_t                         *rm, *glob;\r
-       \r
-       \r
-       /* get model */\r
-       model = LoadModel( name, frame );\r
-       if( model == NULL )\r
-               return;\r
-       \r
-       /* handle null matrix */\r
-       if( transform == NULL )\r
-       {\r
-               m4x4_identity( identity );\r
-               transform = identity;\r
-       }\r
-       \r
-       /* hack: Stable-1_2 and trunk have differing row/column major matrix order\r
-          this transpose is necessary with Stable-1_2\r
-          uncomment the following line with old m4x4_t (non 1.3/spog_branch) code */\r
-       //%     m4x4_transpose( transform );\r
-       \r
-       /* create transform matrix for normals */\r
-       memcpy( nTransform, transform, sizeof( m4x4_t ) );\r
-       if( m4x4_invert( nTransform ) )\r
-               Sys_FPrintf( SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n" );\r
-       m4x4_transpose( nTransform );\r
-       \r
-       /* fix bogus lightmap scale */\r
-       if( lightmapScale <= 0.0f )\r
-               lightmapScale = 1.0f;\r
-       \r
-       /* each surface on the model will become a new map drawsurface */\r
-       numSurfaces = PicoGetModelNumSurfaces( model );\r
-       //%     Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces );\r
-       for( s = 0; s < numSurfaces; s++ )\r
-       {\r
-               /* get surface */\r
-               surface = PicoGetModelSurface( model, s );\r
-               if( surface == NULL )\r
-                       continue;\r
-               \r
-               /* only handle triangle surfaces initially (fixme: support patches) */\r
-               if( PicoGetSurfaceType( surface ) != PICO_TRIANGLES )\r
-                       continue;\r
-               \r
-               /* fix the surface's normals */\r
-               PicoFixSurfaceNormals( surface );\r
-               \r
-               /* allocate a surface (ydnar: gs mods) */\r
-               ds = AllocDrawSurface( SURFACE_TRIANGLES );\r
-               ds->entityNum = eNum;\r
-               ds->castShadows = castShadows;\r
-               ds->recvShadows = recvShadows;\r
-               \r
-               /* get shader name */\r
-        shader = PicoGetSurfaceShader( surface );\r
-               if( shader == NULL )\r
-                       picoShaderName = "";\r
-               else\r
-                       picoShaderName = PicoGetShaderName( shader );\r
-               \r
-               /* handle shader remapping */\r
-               glob = NULL;\r
-               for( rm = remap; rm != NULL; rm = rm->next )\r
-               {\r
-                       if( rm->from[ 0 ] == '*' && rm->from[ 1 ] == '\0' )\r
-                               glob = rm;\r
-                       else if( !Q_stricmp( picoShaderName, rm->from ) )\r
-                       {\r
-                               Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to );\r
-                               picoShaderName = rm->to;\r
-                               glob = NULL;\r
-                               break;\r
-                       }\r
-               }\r
-               \r
-               if( glob != NULL )\r
-               {\r
-                       Sys_FPrintf( SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to );\r
-                       picoShaderName = glob->to;\r
-               }\r
-               \r
-               /* shader renaming for sof2 */\r
-               if( renameModelShaders )\r
-               {\r
-                       strcpy( shaderName, picoShaderName );\r
-                       StripExtension( shaderName );\r
-                       if( spawnFlags & 1 )\r
-                               strcat( shaderName, "_RMG_BSP" );\r
-                       else\r
-                               strcat( shaderName, "_BSP" );\r
-                       si = ShaderInfoForShader( shaderName );\r
-               }\r
-               else\r
-                       si = ShaderInfoForShader( picoShaderName );\r
-               \r
-               /* set shader */\r
-               ds->shaderInfo = si;\r
-               \r
-               /* set lightmap scale */\r
-               ds->lightmapScale = lightmapScale;\r
-               \r
-               /* force to meta? */\r
-               if( si != NULL && si->forceMeta )\r
-                       ds->type = SURFACE_FORCED_META;\r
-               \r
-               /* set particulars */\r
-               ds->numVerts = PicoGetSurfaceNumVertexes( surface );\r
-               ds->verts = safe_malloc( ds->numVerts * sizeof( ds->verts[ 0 ] ) );\r
-               memset( ds->verts, 0, ds->numVerts * sizeof( ds->verts[ 0 ] ) );\r
-               \r
-               ds->numIndexes = PicoGetSurfaceNumIndexes( surface );\r
-               ds->indexes = safe_malloc( ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );\r
-               memset( ds->indexes, 0, ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );\r
-               \r
-               /* copy vertexes */\r
-               for( i = 0; i < ds->numVerts; i++ )\r
-               {\r
-                       /* get vertex */\r
-                       dv = &ds->verts[ i ];\r
-                       \r
-                       /* xyz and normal */\r
-                       xyz = PicoGetSurfaceXYZ( surface, i );\r
-                       VectorCopy( xyz, dv->xyz );\r
-                       m4x4_transform_point( transform, dv->xyz );\r
-                       \r
-                       normal = PicoGetSurfaceNormal( surface, i );\r
-                       VectorCopy( normal, dv->normal );\r
-                       m4x4_transform_normal( nTransform, dv->normal );\r
-                       VectorNormalize( dv->normal, dv->normal );\r
-\r
-                       /* ydnar: tek-fu celshading support for flat shaded shit */\r
-                       if( flat )\r
-                       {\r
-                               dv->st[ 0 ] = si->stFlat[ 0 ];\r
-                               dv->st[ 1 ] = si->stFlat[ 1 ];\r
-                       }\r
-                       \r
-                       /* ydnar: gs mods: added support for explicit shader texcoord generation */\r
-                       else if( si->tcGen )\r
-                       {\r
-                               /* project the texture */\r
-                               dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], dv->xyz );\r
-                               dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], dv->xyz );\r
-                       }\r
-                       \r
-                       /* normal texture coordinates */\r
-                       {\r
-                               st = PicoGetSurfaceST( surface, 0, i );\r
-                               dv->st[ 0 ] = st[ 0 ];\r
-                               dv->st[ 1 ] = st[ 1 ];\r
-                       }\r
-                       \r
-                       /* set lightmap/color bits */\r
-                       color = PicoGetSurfaceColor( surface, 0, i );\r
-                       for( j = 0; j < MAX_LIGHTMAPS; j++ )\r
-                       {\r
-                               dv->lightmap[ j ][ 0 ] = 0.0f;\r
-                               dv->lightmap[ j ][ 1 ] = 0.0f;\r
-                               dv->color[ j ][ 0 ] = color[ 0 ];\r
-                               dv->color[ j ][ 1 ] = color[ 1 ];\r
-                               dv->color[ j ][ 2 ] = color[ 2 ];\r
-                               dv->color[ j ][ 3 ] = color[ 3 ];\r
-                       }\r
-               }\r
-               \r
-               /* copy indexes */\r
-               indexes = PicoGetSurfaceIndexes( surface, 0 );\r
-               for( i = 0; i < ds->numIndexes; i++ )\r
-                       ds->indexes[ i ] = indexes[ i ];\r
-               \r
-               /* set cel shader */\r
-               ds->celShader = celShader;\r
-               \r
-               /* finish surface */\r
-               FinishSurface( ds );\r
-               \r
-               /* ydnar: giant hack land: generate clipping brushes for model triangles */\r
-               if( si->clipModel || (spawnFlags & 2) ) /* 2nd bit */\r
-               {\r
-                       vec3_t          points[ 3 ], backs[ 3 ];\r
-                       vec4_t          plane, reverse, pa, pb, pc;\r
-                       vec3_t          nadir;\r
-                       \r
-                       \r
-                       /* temp hack */\r
-                       if( (si->compileFlags & C_TRANSLUCENT) || !(si->compileFlags & C_SOLID) )\r
-                               continue;\r
-                       \r
-                       /* overflow check */\r
-                       if( (nummapplanes + 64) >= (MAX_MAP_PLANES >> 1) )\r
-                               continue;\r
-                       \r
-                       /* walk triangle list */\r
-                       for( i = 0; i < ds->numIndexes; i += 3 )\r
-                       {\r
-                               /* overflow hack */\r
-                               if( (nummapplanes + 64) >= (MAX_MAP_PLANES >> 1) )\r
-                               {\r
-                                       Sys_Printf( "WARNING: MAX_MAP_PLANES (%d) hit generating clip brushes for model %s.\n",\r
-                                               MAX_MAP_PLANES, name );\r
-                                       break;\r
-                               }\r
-                               \r
-                               /* make points and back points */\r
-                               for( j = 0; j < 3; j++ )\r
-                               {\r
-                                       /* get vertex */\r
-                                       dv = &ds->verts[ ds->indexes[ i + j ] ];\r
-                                       \r
-                                       /* copy xyz */\r
-                                       VectorCopy( dv->xyz, points[ j ] );\r
-                                       VectorCopy( dv->xyz, backs[ j ] );\r
-                                       \r
-                                       /* find nearest axial to normal and push back points opposite */\r
-                                       /* note: this doesn't work as well as simply using the plane of the triangle, below */\r
-                                       for( k = 0; k < 3; k++ )\r
-                                       {\r
-                                               if( fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 1) % 3 ] ) &&\r
-                                                       fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 2) % 3 ] ) )\r
-                                               {\r
-                                                       backs[ j ][ k ] += dv->normal[ k ] < 0.0f ? 64.0f : -64.0f;\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-                               }\r
-                               \r
-                               /* make plane for triangle */\r
-                               if( PlaneFromPoints( plane, points[ 0 ], points[ 1 ], points[ 2 ] ) )\r
-                               {\r
-                                       /* regenerate back points */\r
-                                       for( j = 0; j < 3; j++ )\r
-                                       {\r
-                                               /* get vertex */\r
-                                               dv = &ds->verts[ ds->indexes[ i + j ] ];\r
-                                               \r
-                                               /* copy xyz */\r
-                                               VectorCopy( dv->xyz, backs[ j ] );\r
-                                               \r
-                                               /* find nearest axial to plane normal and push back points opposite */\r
-                                               for( k = 0; k < 3; k++ )\r
-                                               {\r
-                                                       if( fabs( plane[ k ] ) > fabs( plane[ (k + 1) % 3 ] ) &&\r
-                                                               fabs( plane[ k ] ) > fabs( plane[ (k + 2) % 3 ] ) )\r
-                                                       {\r
-                                                               backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;\r
-                                                               break;\r
-                                                       }\r
-                                               }\r
-                                       }\r
-                                       \r
-                                       /* make back plane */\r
-                                       VectorScale( plane, -1.0f, reverse );\r
-                                       reverse[ 3 ] = -(plane[ 3 ] - 1);\r
-                                       \r
-                                       /* make back pyramid point */\r
-                                       VectorCopy( points[ 0 ], nadir );\r
-                                       VectorAdd( nadir, points[ 1 ], nadir );\r
-                                       VectorAdd( nadir, points[ 2 ], nadir );\r
-                                       VectorScale( nadir, 0.3333333333333f, nadir );\r
-                                       VectorMA( nadir, -2.0f, plane, nadir );\r
-                                       \r
-                                       /* make 3 more planes */\r
-                                       //%     if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], nadir ) &&\r
-                                       //%             PlaneFromPoints( pb, points[ 1 ], points[ 0 ], nadir ) &&\r
-                                       //%             PlaneFromPoints( pc, points[ 0 ], points[ 2 ], nadir ) )\r
-                                       if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], backs[ 1 ] ) &&\r
-                                               PlaneFromPoints( pb, points[ 1 ], points[ 0 ], backs[ 0 ] ) &&\r
-                                               PlaneFromPoints( pc, points[ 0 ], points[ 2 ], backs[ 2 ] ) )\r
-                                       {\r
-                                               /* build a brush */\r
-                                               buildBrush = AllocBrush( 48 );\r
-                                               \r
-                                               buildBrush->entityNum = mapEntityNum;\r
-                                               buildBrush->original = buildBrush;\r
-                                               buildBrush->contentShader = si;\r
-                                               buildBrush->compileFlags = si->compileFlags;\r
-                                               buildBrush->contentFlags = si->contentFlags;\r
-                                               buildBrush->detail = qtrue;\r
-                                               \r
-                                               /* set up brush sides */\r
-                                               buildBrush->numsides = 5;\r
-                                               for( j = 0; j < buildBrush->numsides; j++ )\r
-                                                       buildBrush->sides[ j ].shaderInfo = si;\r
-                                               buildBrush->sides[ 0 ].planenum = FindFloatPlane( plane, plane[ 3 ], 3, points );\r
-                                               buildBrush->sides[ 1 ].planenum = FindFloatPlane( pa, pa[ 3 ], 1, &points[ 2 ] );\r
-                                               buildBrush->sides[ 2 ].planenum = FindFloatPlane( pb, pb[ 3 ], 1, &points[ 1 ] );\r
-                                               buildBrush->sides[ 3 ].planenum = FindFloatPlane( pc, pc[ 3 ], 1, &points[ 0 ] );\r
-                                               buildBrush->sides[ 4 ].planenum = FindFloatPlane( reverse, reverse[ 3 ], 3, points );\r
-                                               \r
-                                               /* add to entity */\r
-                                               if( CreateBrushWindings( buildBrush ) )\r
-                                               {\r
-                                                       AddBrushBevels();\r
-                                                       //%     EmitBrushes( buildBrush, NULL, NULL );\r
-                                                       buildBrush->next = entities[ mapEntityNum ].brushes;\r
-                                                       entities[ mapEntityNum ].brushes = buildBrush;\r
-                                                       entities[ mapEntityNum ].numBrushes++;\r
-                                               }\r
-                                               else\r
-                                                       free( buildBrush );\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-AddTriangleModels()\r
-adds misc_model surfaces to the bsp\r
-*/\r
-\r
-void AddTriangleModels( entity_t *e )\r
-{\r
-       int                             num, frame, castShadows, recvShadows, spawnFlags;\r
-       entity_t                *e2;\r
-       const char              *targetName;\r
-       const char              *target, *model, *value;\r
-       char                    shader[ MAX_QPATH ];\r
-       shaderInfo_t    *celShader;\r
-       float                   temp, baseLightmapScale, lightmapScale;\r
-       vec3_t                  origin, scale, angles;\r
-       m4x4_t                  transform;\r
-       epair_t                 *ep;\r
-       remap_t                 *remap, *remap2;\r
-       char                    *split;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- AddTriangleModels ---\n" );\r
-       \r
-       /* get current brush entity targetname */\r
-       if( e == entities )\r
-               targetName = "";\r
-       else\r
-       {\r
-               targetName = ValueForKey( e, "targetname" );\r
-       \r
-               /* misc_model entities target non-worldspawn brush model entities */\r
-               if( targetName[ 0 ] == '\0' )\r
-                       return;\r
-       }\r
-       \r
-       /* get lightmap scale */\r
-       baseLightmapScale = FloatForKey( e, "_lightmapscale" );\r
-       if( baseLightmapScale <= 0.0f )\r
-               baseLightmapScale = 0.0f;\r
-       \r
-       /* walk the entity list */\r
-       for( num = 1; num < numEntities; num++ )\r
-       {\r
-               /* get e2 */\r
-               e2 = &entities[ num ];\r
-               \r
-               /* convert misc_models into raw geometry */\r
-               if( Q_stricmp( "misc_model", ValueForKey( e2, "classname" ) ) )\r
-                       continue;\r
-\r
-               /* ydnar: added support for md3 models on non-worldspawn models */\r
-               target = ValueForKey( e2, "target" );\r
-               if( strcmp( target, targetName ) )\r
-                       continue;\r
-               \r
-               /* get model name */\r
-               model = ValueForKey( e2, "model" );\r
-               if( model[ 0 ] == '\0' )\r
-               {\r
-                       Sys_Printf( "WARNING: misc_model at %i %i %i without a model key\n",\r
-                               (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );\r
-                       continue;\r
-               }\r
-               \r
-               /* get model frame */\r
-               frame = IntForKey( e2, "_frame" );\r
-               \r
-               /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */\r
-               if( e == entities )\r
-               {\r
-                       castShadows = WORLDSPAWN_CAST_SHADOWS;\r
-                       recvShadows = WORLDSPAWN_RECV_SHADOWS;\r
-               }\r
-               \r
-               /* other entities don't cast any shadows, but recv worldspawn shadows */\r
-               else\r
-               {\r
-                       castShadows = ENTITY_CAST_SHADOWS;\r
-                       recvShadows = ENTITY_RECV_SHADOWS;\r
-               }\r
-               \r
-               /* get explicit shadow flags */\r
-               GetEntityShadowFlags( e2, e, &castShadows, &recvShadows );\r
-               \r
-               /* get spawnflags */\r
-               spawnFlags = IntForKey( e2, "spawnflags" );\r
-               \r
-               /* get origin */\r
-               GetVectorForKey( e2, "origin", origin );\r
-               VectorSubtract( origin, e->origin, origin );    /* offset by parent */\r
-               \r
-               /* get scale */\r
-               scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = 1.0f;\r
-               temp = FloatForKey( e2, "modelscale" );\r
-               if( temp != 0.0f )\r
-                       scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = temp;\r
-               value = ValueForKey( e2, "modelscale_vec" );\r
-               if( value[ 0 ] != '\0' )\r
-                       sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );\r
-               \r
-               /* get "angle" (yaw) or "angles" (pitch yaw roll) */\r
-               angles[ 0 ] = angles[ 1 ] = angles[ 2 ] = 0.0f;\r
-               angles[ 2 ] = FloatForKey( e2, "angle" );\r
-               value = ValueForKey( e2, "angles" );\r
-               if( value[ 0 ] != '\0' )\r
-                       sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );\r
-               \r
-               /* set transform matrix (thanks spog) */\r
-               m4x4_identity( transform );\r
-               m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );\r
-               \r
-               /* get shader remappings */\r
-               remap = NULL;\r
-               for( ep = e2->epairs; ep != NULL; ep = ep->next )\r
-               {\r
-                       /* look for keys prefixed with "_remap" */\r
-                       if( ep->key != NULL && ep->value != NULL &&\r
-                               ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' &&\r
-                               !Q_strncasecmp( ep->key, "_remap", 6 ) )\r
-                       {\r
-                               /* create new remapping */\r
-                               remap2 = remap;\r
-                               remap = safe_malloc( sizeof( *remap ) );\r
-                               remap->next = remap2;\r
-                               strcpy( remap->from, ep->value );\r
-                               \r
-                               /* split the string */\r
-                               split = strchr( remap->from, ';' );\r
-                               if( split == NULL )\r
-                               {\r
-                                       Sys_Printf( "WARNING: Shader _remap key found in misc_model without a ; character\n" );\r
-                                       free( remap );\r
-                                       remap = remap2;\r
-                                       continue;\r
-                               }\r
-                               \r
-                               /* store the split */\r
-                               *split = '\0';\r
-                               strcpy( remap->to, (split + 1) );\r
-                               \r
-                               /* note it */\r
-                               //%     Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", remap->from, remap->to );\r
-                       }\r
-               }\r
-               \r
-               /* ydnar: cel shader support */\r
-               value = ValueForKey( e2, "_celshader" );\r
-               if( value[ 0 ] == '\0' )\r
-                       value = ValueForKey( &entities[ 0 ], "_celshader" );\r
-               if( value[ 0 ] != '\0' )\r
-               {\r
-                       sprintf( shader, "textures/%s", value );\r
-                       celShader = ShaderInfoForShader( shader );\r
-               }\r
-               else\r
-                       celShader = NULL;\r
-               \r
-               /* get lightmap scale */\r
-               lightmapScale = FloatForKey( e2, "_lightmapscale" );\r
-               if( lightmapScale <= 0.0f )\r
-                       lightmapScale = baseLightmapScale;\r
-               \r
-               /* insert the model */\r
-               InsertModel( (char*) model, frame, transform, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale );\r
-               \r
-               /* free shader remappings */\r
-               while( remap != NULL )\r
-               {\r
-                       remap2 = remap->next;\r
-                       free( remap );\r
-                       remap = remap2;\r
-               }\r
-       }\r
-}\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant 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.
+
+GtkRadiant 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 GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+----------------------------------------------------------------------------------
+
+This code has been altered significantly from its original form, to support
+several games based on the Quake III Arena engine, in the form of "Q3Map2."
+
+------------------------------------------------------------------------------- */
+
+
+
+/* marker */
+#define MODEL_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+
+
+
+/* 
+PicoPrintFunc()
+callback for picomodel.lib
+*/
+
+void PicoPrintFunc( int level, const char *str )
+{
+       if( str == NULL )
+               return;
+       switch( level )
+       {
+               case PICO_NORMAL:
+                       Sys_Printf( "%s\n", str );
+                       break;
+               
+               case PICO_VERBOSE:
+                       Sys_FPrintf( SYS_VRB, "%s\n", str );
+                       break;
+               
+               case PICO_WARNING:
+                       Sys_Printf( "WARNING: %s\n", str );
+                       break;
+               
+               case PICO_ERROR:
+                       Sys_Printf( "ERROR: %s\n", str );
+                       break;
+               
+               case PICO_FATAL:
+                       Error( "ERROR: %s\n", str );
+                       break;
+       }
+}
+
+
+
+/* 
+PicoLoadFileFunc()
+callback for picomodel.lib
+*/
+
+void PicoLoadFileFunc( char *name, byte **buffer, int *bufSize )
+{
+       *bufSize = vfsLoadFile( (const char*) name, (void**) buffer, 0 );
+}
+
+
+
+/*
+FindModel() - ydnar
+finds an existing picoModel and returns a pointer to the picoModel_t struct or NULL if not found
+*/
+
+picoModel_t *FindModel( char *name, int frame )
+{
+       int                     i;
+       
+       
+       /* init */
+       if( numPicoModels <= 0 )
+               memset( picoModels, 0, sizeof( picoModels ) );
+       
+       /* dummy check */
+       if( name == NULL || name[ 0 ] == '\0' )
+               return NULL;
+       
+       /* search list */
+       for( i = 0; i < MAX_MODELS; i++ )
+       {
+               if( picoModels[ i ] != NULL &&
+                       !strcmp( PicoGetModelName( picoModels[ i ] ), name ) &&
+                       PicoGetModelFrameNum( picoModels[ i ] ) == frame )
+                       return picoModels[ i ];
+       }
+       
+       /* no matching picoModel found */
+       return NULL;
+}
+
+
+
+/*
+LoadModel() - ydnar
+loads a picoModel and returns a pointer to the picoModel_t struct or NULL if not found
+*/
+
+picoModel_t *LoadModel( char *name, int frame )
+{
+       int                             i;
+       picoModel_t             *model, **pm;
+       
+       
+       /* init */
+       if( numPicoModels <= 0 )
+               memset( picoModels, 0, sizeof( picoModels ) );
+       
+       /* dummy check */
+       if( name == NULL || name[ 0 ] == '\0' )
+               return NULL;
+       
+       /* try to find existing picoModel */
+       model = FindModel( name, frame );
+       if( model != NULL )
+               return model;
+       
+       /* none found, so find first non-null picoModel */
+       pm = NULL;
+       for( i = 0; i < MAX_MODELS; i++ )
+       {
+               if( picoModels[ i ] == NULL )
+               {
+                       pm = &picoModels[ i ];
+                       break;
+               }
+       }
+       
+       /* too many picoModels? */
+       if( pm == NULL )
+               Error( "MAX_MODELS (%d) exceeded, there are too many model files referenced by the map.", MAX_MODELS );
+       
+       /* attempt to parse model */
+       *pm = PicoLoadModel( (char*) name, frame );
+       
+       /* if loading failed, make a bogus model to silence the rest of the warnings */
+       if( *pm == NULL )
+       {
+               /* allocate a new model */
+               *pm = PicoNewModel();
+               if( *pm == NULL )
+                       return NULL;
+               
+               /* set data */
+               PicoSetModelName( *pm, name );
+               PicoSetModelFrameNum( *pm, frame );
+       }
+       
+       /* debug code */
+       #if 0
+       {
+               int                             numSurfaces, numVertexes;
+               picoSurface_t   *ps;
+               
+               
+               Sys_Printf( "Model %s\n", name );
+               numSurfaces = PicoGetModelNumSurfaces( *pm );
+               for( i = 0; i < numSurfaces; i++ )
+               {
+                       ps = PicoGetModelSurface( *pm, i );
+                       numVertexes = PicoGetSurfaceNumVertexes( ps );
+                       Sys_Printf( "Surface %d has %d vertexes\n", i, numVertexes );
+               }
+       }
+       #endif
+       
+       /* set count */
+       if( *pm != NULL )
+               numPicoModels++;
+       
+       /* return the picoModel */
+       return *pm;
+}
+
+
+
+/*
+InsertModel() - ydnar
+adds a picomodel into the bsp
+*/
+
+void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shaderInfo_t *celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale )
+{
+       int                                     i, j, k, s, numSurfaces;
+       m4x4_t                          identity, nTransform;
+       picoModel_t                     *model;
+       picoShader_t            *shader;
+       picoSurface_t           *surface;
+       shaderInfo_t            *si;
+       mapDrawSurface_t        *ds;
+       bspDrawVert_t           *dv;
+       char                            *picoShaderName;
+       char                            shaderName[ MAX_QPATH ];
+       picoVec_t                       *xyz, *normal, *st;
+       byte                            *color;
+       picoIndex_t                     *indexes;
+       remap_t                         *rm, *glob;
+       
+       
+       /* get model */
+       model = LoadModel( name, frame );
+       if( model == NULL )
+               return;
+       
+       /* handle null matrix */
+       if( transform == NULL )
+       {
+               m4x4_identity( identity );
+               transform = identity;
+       }
+       
+       /* hack: Stable-1_2 and trunk have differing row/column major matrix order
+          this transpose is necessary with Stable-1_2
+          uncomment the following line with old m4x4_t (non 1.3/spog_branch) code */
+       //%     m4x4_transpose( transform );
+       
+       /* create transform matrix for normals */
+       memcpy( nTransform, transform, sizeof( m4x4_t ) );
+       if( m4x4_invert( nTransform ) )
+               Sys_FPrintf( SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n" );
+       m4x4_transpose( nTransform );
+       
+       /* fix bogus lightmap scale */
+       if( lightmapScale <= 0.0f )
+               lightmapScale = 1.0f;
+       
+       /* each surface on the model will become a new map drawsurface */
+       numSurfaces = PicoGetModelNumSurfaces( model );
+       //%     Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces );
+       for( s = 0; s < numSurfaces; s++ )
+       {
+               /* get surface */
+               surface = PicoGetModelSurface( model, s );
+               if( surface == NULL )
+                       continue;
+               
+               /* only handle triangle surfaces initially (fixme: support patches) */
+               if( PicoGetSurfaceType( surface ) != PICO_TRIANGLES )
+                       continue;
+               
+               /* fix the surface's normals */
+               PicoFixSurfaceNormals( surface );
+               
+               /* allocate a surface (ydnar: gs mods) */
+               ds = AllocDrawSurface( SURFACE_TRIANGLES );
+               ds->entityNum = eNum;
+               ds->castShadows = castShadows;
+               ds->recvShadows = recvShadows;
+               
+               /* get shader name */
+        shader = PicoGetSurfaceShader( surface );
+               if( shader == NULL )
+                       picoShaderName = "";
+               else
+                       picoShaderName = PicoGetShaderName( shader );
+               
+               /* handle shader remapping */
+               glob = NULL;
+               for( rm = remap; rm != NULL; rm = rm->next )
+               {
+                       if( rm->from[ 0 ] == '*' && rm->from[ 1 ] == '\0' )
+                               glob = rm;
+                       else if( !Q_stricmp( picoShaderName, rm->from ) )
+                       {
+                               Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to );
+                               picoShaderName = rm->to;
+                               glob = NULL;
+                               break;
+                       }
+               }
+               
+               if( glob != NULL )
+               {
+                       Sys_FPrintf( SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to );
+                       picoShaderName = glob->to;
+               }
+               
+               /* shader renaming for sof2 */
+               if( renameModelShaders )
+               {
+                       strcpy( shaderName, picoShaderName );
+                       StripExtension( shaderName );
+                       if( spawnFlags & 1 )
+                               strcat( shaderName, "_RMG_BSP" );
+                       else
+                               strcat( shaderName, "_BSP" );
+                       si = ShaderInfoForShader( shaderName );
+               }
+               else
+                       si = ShaderInfoForShader( picoShaderName );
+               
+               /* set shader */
+               ds->shaderInfo = si;
+               
+               /* set lightmap scale */
+               ds->lightmapScale = lightmapScale;
+               
+               /* force to meta? */
+               if( si != NULL && si->forceMeta )
+                       ds->type = SURFACE_FORCED_META;
+               
+               /* set particulars */
+               ds->numVerts = PicoGetSurfaceNumVertexes( surface );
+               ds->verts = safe_malloc( ds->numVerts * sizeof( ds->verts[ 0 ] ) );
+               memset( ds->verts, 0, ds->numVerts * sizeof( ds->verts[ 0 ] ) );
+               
+               ds->numIndexes = PicoGetSurfaceNumIndexes( surface );
+               ds->indexes = safe_malloc( ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );
+               memset( ds->indexes, 0, ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );
+               
+               /* copy vertexes */
+               for( i = 0; i < ds->numVerts; i++ )
+               {
+                       /* get vertex */
+                       dv = &ds->verts[ i ];
+                       
+                       /* xyz and normal */
+                       xyz = PicoGetSurfaceXYZ( surface, i );
+                       VectorCopy( xyz, dv->xyz );
+                       m4x4_transform_point( transform, dv->xyz );
+                       
+                       normal = PicoGetSurfaceNormal( surface, i );
+                       VectorCopy( normal, dv->normal );
+                       m4x4_transform_normal( nTransform, dv->normal );
+                       VectorNormalize( dv->normal, dv->normal );
+
+                       /* ydnar: tek-fu celshading support for flat shaded shit */
+                       if( flat )
+                       {
+                               dv->st[ 0 ] = si->stFlat[ 0 ];
+                               dv->st[ 1 ] = si->stFlat[ 1 ];
+                       }
+                       
+                       /* ydnar: gs mods: added support for explicit shader texcoord generation */
+                       else if( si->tcGen )
+                       {
+                               /* project the texture */
+                               dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], dv->xyz );
+                               dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], dv->xyz );
+                       }
+                       
+                       /* normal texture coordinates */
+                       {
+                               st = PicoGetSurfaceST( surface, 0, i );
+                               dv->st[ 0 ] = st[ 0 ];
+                               dv->st[ 1 ] = st[ 1 ];
+                       }
+                       
+                       /* set lightmap/color bits */
+                       color = PicoGetSurfaceColor( surface, 0, i );
+                       for( j = 0; j < MAX_LIGHTMAPS; j++ )
+                       {
+                               dv->lightmap[ j ][ 0 ] = 0.0f;
+                               dv->lightmap[ j ][ 1 ] = 0.0f;
+                               dv->color[ j ][ 0 ] = color[ 0 ];
+                               dv->color[ j ][ 1 ] = color[ 1 ];
+                               dv->color[ j ][ 2 ] = color[ 2 ];
+                               dv->color[ j ][ 3 ] = color[ 3 ];
+                       }
+               }
+               
+               /* copy indexes */
+               indexes = PicoGetSurfaceIndexes( surface, 0 );
+               for( i = 0; i < ds->numIndexes; i++ )
+                       ds->indexes[ i ] = indexes[ i ];
+               
+               /* set cel shader */
+               ds->celShader = celShader;
+               
+               /* finish surface */
+               FinishSurface( ds );
+               
+               /* ydnar: giant hack land: generate clipping brushes for model triangles */
+               if( si->clipModel || (spawnFlags & 2) ) /* 2nd bit */
+               {
+                       vec3_t          points[ 3 ], backs[ 3 ];
+                       vec4_t          plane, reverse, pa, pb, pc;
+                       vec3_t          nadir;
+                       
+                       
+                       /* temp hack */
+                       if( (si->compileFlags & C_TRANSLUCENT) || !(si->compileFlags & C_SOLID) )
+                               continue;
+                       
+                       /* overflow check */
+                       if( (nummapplanes + 64) >= (MAX_MAP_PLANES >> 1) )
+                               continue;
+                       
+                       /* walk triangle list */
+                       for( i = 0; i < ds->numIndexes; i += 3 )
+                       {
+                               /* overflow hack */
+                               if( (nummapplanes + 64) >= (MAX_MAP_PLANES >> 1) )
+                               {
+                                       Sys_Printf( "WARNING: MAX_MAP_PLANES (%d) hit generating clip brushes for model %s.\n",
+                                               MAX_MAP_PLANES, name );
+                                       break;
+                               }
+                               
+                               /* make points and back points */
+                               for( j = 0; j < 3; j++ )
+                               {
+                                       /* get vertex */
+                                       dv = &ds->verts[ ds->indexes[ i + j ] ];
+                                       
+                                       /* copy xyz */
+                                       VectorCopy( dv->xyz, points[ j ] );
+                                       VectorCopy( dv->xyz, backs[ j ] );
+                                       
+                                       /* find nearest axial to normal and push back points opposite */
+                                       /* note: this doesn't work as well as simply using the plane of the triangle, below */
+                                       for( k = 0; k < 3; k++ )
+                                       {
+                                               if( fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 1) % 3 ] ) &&
+                                                       fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 2) % 3 ] ) )
+                                               {
+                                                       backs[ j ][ k ] += dv->normal[ k ] < 0.0f ? 64.0f : -64.0f;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               
+                               /* make plane for triangle */
+                               if( PlaneFromPoints( plane, points[ 0 ], points[ 1 ], points[ 2 ] ) )
+                               {
+                                       /* regenerate back points */
+                                       for( j = 0; j < 3; j++ )
+                                       {
+                                               /* get vertex */
+                                               dv = &ds->verts[ ds->indexes[ i + j ] ];
+                                               
+                                               /* copy xyz */
+                                               VectorCopy( dv->xyz, backs[ j ] );
+                                               
+                                               /* find nearest axial to plane normal and push back points opposite */
+                                               for( k = 0; k < 3; k++ )
+                                               {
+                                                       if( fabs( plane[ k ] ) > fabs( plane[ (k + 1) % 3 ] ) &&
+                                                               fabs( plane[ k ] ) > fabs( plane[ (k + 2) % 3 ] ) )
+                                                       {
+                                                               backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       
+                                       /* make back plane */
+                                       VectorScale( plane, -1.0f, reverse );
+                                       reverse[ 3 ] = -(plane[ 3 ] - 1);
+                                       
+                                       /* make back pyramid point */
+                                       VectorCopy( points[ 0 ], nadir );
+                                       VectorAdd( nadir, points[ 1 ], nadir );
+                                       VectorAdd( nadir, points[ 2 ], nadir );
+                                       VectorScale( nadir, 0.3333333333333f, nadir );
+                                       VectorMA( nadir, -2.0f, plane, nadir );
+                                       
+                                       /* make 3 more planes */
+                                       //%     if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], nadir ) &&
+                                       //%             PlaneFromPoints( pb, points[ 1 ], points[ 0 ], nadir ) &&
+                                       //%             PlaneFromPoints( pc, points[ 0 ], points[ 2 ], nadir ) )
+                                       if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], backs[ 1 ] ) &&
+                                               PlaneFromPoints( pb, points[ 1 ], points[ 0 ], backs[ 0 ] ) &&
+                                               PlaneFromPoints( pc, points[ 0 ], points[ 2 ], backs[ 2 ] ) )
+                                       {
+                                               /* build a brush */
+                                               buildBrush = AllocBrush( 48 );
+                                               
+                                               buildBrush->entityNum = mapEntityNum;
+                                               buildBrush->original = buildBrush;
+                                               buildBrush->contentShader = si;
+                                               buildBrush->compileFlags = si->compileFlags;
+                                               buildBrush->contentFlags = si->contentFlags;
+                                               buildBrush->detail = qtrue;
+                                               
+                                               /* set up brush sides */
+                                               buildBrush->numsides = 5;
+                                               for( j = 0; j < buildBrush->numsides; j++ )
+                                                       buildBrush->sides[ j ].shaderInfo = si;
+                                               buildBrush->sides[ 0 ].planenum = FindFloatPlane( plane, plane[ 3 ], 3, points );
+                                               buildBrush->sides[ 1 ].planenum = FindFloatPlane( pa, pa[ 3 ], 1, &points[ 2 ] );
+                                               buildBrush->sides[ 2 ].planenum = FindFloatPlane( pb, pb[ 3 ], 1, &points[ 1 ] );
+                                               buildBrush->sides[ 3 ].planenum = FindFloatPlane( pc, pc[ 3 ], 1, &points[ 0 ] );
+                                               buildBrush->sides[ 4 ].planenum = FindFloatPlane( reverse, reverse[ 3 ], 3, points );
+                                               
+                                               /* add to entity */
+                                               if( CreateBrushWindings( buildBrush ) )
+                                               {
+                                                       AddBrushBevels();
+                                                       //%     EmitBrushes( buildBrush, NULL, NULL );
+                                                       buildBrush->next = entities[ mapEntityNum ].brushes;
+                                                       entities[ mapEntityNum ].brushes = buildBrush;
+                                                       entities[ mapEntityNum ].numBrushes++;
+                                               }
+                                               else
+                                                       free( buildBrush );
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+
+
+/*
+AddTriangleModels()
+adds misc_model surfaces to the bsp
+*/
+
+void AddTriangleModels( entity_t *e )
+{
+       int                             num, frame, castShadows, recvShadows, spawnFlags;
+       entity_t                *e2;
+       const char              *targetName;
+       const char              *target, *model, *value;
+       char                    shader[ MAX_QPATH ];
+       shaderInfo_t    *celShader;
+       float                   temp, baseLightmapScale, lightmapScale;
+       vec3_t                  origin, scale, angles;
+       m4x4_t                  transform;
+       epair_t                 *ep;
+       remap_t                 *remap, *remap2;
+       char                    *split;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- AddTriangleModels ---\n" );
+       
+       /* get current brush entity targetname */
+       if( e == entities )
+               targetName = "";
+       else
+       {
+               targetName = ValueForKey( e, "targetname" );
+       
+               /* misc_model entities target non-worldspawn brush model entities */
+               if( targetName[ 0 ] == '\0' )
+                       return;
+       }
+       
+       /* get lightmap scale */
+       baseLightmapScale = FloatForKey( e, "_lightmapscale" );
+       if( baseLightmapScale <= 0.0f )
+               baseLightmapScale = 0.0f;
+       
+       /* walk the entity list */
+       for( num = 1; num < numEntities; num++ )
+       {
+               /* get e2 */
+               e2 = &entities[ num ];
+               
+               /* convert misc_models into raw geometry */
+               if( Q_stricmp( "misc_model", ValueForKey( e2, "classname" ) ) )
+                       continue;
+
+               /* ydnar: added support for md3 models on non-worldspawn models */
+               target = ValueForKey( e2, "target" );
+               if( strcmp( target, targetName ) )
+                       continue;
+               
+               /* get model name */
+               model = ValueForKey( e2, "model" );
+               if( model[ 0 ] == '\0' )
+               {
+                       Sys_Printf( "WARNING: misc_model at %i %i %i without a model key\n",
+                               (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
+                       continue;
+               }
+               
+               /* get model frame */
+               frame = IntForKey( e2, "_frame" );
+               
+               /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
+               if( e == entities )
+               {
+                       castShadows = WORLDSPAWN_CAST_SHADOWS;
+                       recvShadows = WORLDSPAWN_RECV_SHADOWS;
+               }
+               
+               /* other entities don't cast any shadows, but recv worldspawn shadows */
+               else
+               {
+                       castShadows = ENTITY_CAST_SHADOWS;
+                       recvShadows = ENTITY_RECV_SHADOWS;
+               }
+               
+               /* get explicit shadow flags */
+               GetEntityShadowFlags( e2, e, &castShadows, &recvShadows );
+               
+               /* get spawnflags */
+               spawnFlags = IntForKey( e2, "spawnflags" );
+               
+               /* get origin */
+               GetVectorForKey( e2, "origin", origin );
+               VectorSubtract( origin, e->origin, origin );    /* offset by parent */
+               
+               /* get scale */
+               scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = 1.0f;
+               temp = FloatForKey( e2, "modelscale" );
+               if( temp != 0.0f )
+                       scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = temp;
+               value = ValueForKey( e2, "modelscale_vec" );
+               if( value[ 0 ] != '\0' )
+                       sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
+               
+               /* get "angle" (yaw) or "angles" (pitch yaw roll) */
+               angles[ 0 ] = angles[ 1 ] = angles[ 2 ] = 0.0f;
+               angles[ 2 ] = FloatForKey( e2, "angle" );
+               value = ValueForKey( e2, "angles" );
+               if( value[ 0 ] != '\0' )
+                       sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );
+               
+               /* set transform matrix (thanks spog) */
+               m4x4_identity( transform );
+               m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );
+               
+               /* get shader remappings */
+               remap = NULL;
+               for( ep = e2->epairs; ep != NULL; ep = ep->next )
+               {
+                       /* look for keys prefixed with "_remap" */
+                       if( ep->key != NULL && ep->value != NULL &&
+                               ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' &&
+                               !Q_strncasecmp( ep->key, "_remap", 6 ) )
+                       {
+                               /* create new remapping */
+                               remap2 = remap;
+                               remap = safe_malloc( sizeof( *remap ) );
+                               remap->next = remap2;
+                               strcpy( remap->from, ep->value );
+                               
+                               /* split the string */
+                               split = strchr( remap->from, ';' );
+                               if( split == NULL )
+                               {
+                                       Sys_Printf( "WARNING: Shader _remap key found in misc_model without a ; character\n" );
+                                       free( remap );
+                                       remap = remap2;
+                                       continue;
+                               }
+                               
+                               /* store the split */
+                               *split = '\0';
+                               strcpy( remap->to, (split + 1) );
+                               
+                               /* note it */
+                               //%     Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", remap->from, remap->to );
+                       }
+               }
+               
+               /* ydnar: cel shader support */
+               value = ValueForKey( e2, "_celshader" );
+               if( value[ 0 ] == '\0' )
+                       value = ValueForKey( &entities[ 0 ], "_celshader" );
+               if( value[ 0 ] != '\0' )
+               {
+                       sprintf( shader, "textures/%s", value );
+                       celShader = ShaderInfoForShader( shader );
+               }
+               else
+                       celShader = NULL;
+               
+               /* get lightmap scale */
+               lightmapScale = FloatForKey( e2, "_lightmapscale" );
+               if( lightmapScale <= 0.0f )
+                       lightmapScale = baseLightmapScale;
+               
+               /* insert the model */
+               InsertModel( (char*) model, frame, transform, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale );
+               
+               /* free shader remappings */
+               while( remap != NULL )
+               {
+                       remap2 = remap->next;
+                       free( remap );
+                       remap = remap2;
+               }
+       }
+}