]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/model.c
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / tools / quake3 / q3map2 / model.c
1 /* -------------------------------------------------------------------------------
2
3    Copyright (C) 1999-2007 id Software, Inc. and contributors.
4    For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6    This file is part of GtkRadiant.
7
8    GtkRadiant is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    GtkRadiant is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with GtkRadiant; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
22    ----------------------------------------------------------------------------------
23
24    This code has been altered significantly from its original form, to support
25    several games based on the Quake III Arena engine, in the form of "Q3Map2."
26
27    ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define MODEL_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41 /*
42    PicoPrintFunc()
43    callback for picomodel.lib
44  */
45
46 void PicoPrintFunc( int level, const char *str ){
47         if ( str == NULL ) {
48                 return;
49         }
50         switch ( level )
51         {
52         case PICO_NORMAL:
53                 Sys_Printf( "%s\n", str );
54                 break;
55
56         case PICO_VERBOSE:
57                 Sys_FPrintf( SYS_VRB, "%s\n", str );
58                 break;
59
60         case PICO_WARNING:
61                 Sys_Printf( "WARNING: %s\n", str );
62                 break;
63
64         case PICO_ERROR:
65                 Sys_Printf( "ERROR: %s\n", str );
66                 break;
67
68         case PICO_FATAL:
69                 Error( "ERROR: %s\n", str );
70                 break;
71         }
72 }
73
74
75
76 /*
77    PicoLoadFileFunc()
78    callback for picomodel.lib
79  */
80
81 void PicoLoadFileFunc( char *name, byte **buffer, int *bufSize ){
82         *bufSize = vfsLoadFile( (const char*) name, (void**) buffer, 0 );
83 }
84
85
86
87 /*
88    FindModel() - ydnar
89    finds an existing picoModel and returns a pointer to the picoModel_t struct or NULL if not found
90  */
91
92 picoModel_t *FindModel( char *name, int frame ){
93         int i;
94
95
96         /* init */
97         if ( numPicoModels <= 0 ) {
98                 memset( picoModels, 0, sizeof( picoModels ) );
99         }
100
101         /* dummy check */
102         if ( name == NULL || name[ 0 ] == '\0' ) {
103                 return NULL;
104         }
105
106         /* search list */
107         for ( i = 0; i < MAX_MODELS; i++ )
108         {
109                 if ( picoModels[ i ] != NULL &&
110                          !strcmp( PicoGetModelName( picoModels[ i ] ), name ) &&
111                          PicoGetModelFrameNum( picoModels[ i ] ) == frame ) {
112                         return picoModels[ i ];
113                 }
114         }
115
116         /* no matching picoModel found */
117         return NULL;
118 }
119
120
121
122 /*
123    LoadModel() - ydnar
124    loads a picoModel and returns a pointer to the picoModel_t struct or NULL if not found
125  */
126
127 picoModel_t *LoadModel( char *name, int frame ){
128         int i;
129         picoModel_t     *model, **pm;
130
131
132         /* init */
133         if ( numPicoModels <= 0 ) {
134                 memset( picoModels, 0, sizeof( picoModels ) );
135         }
136
137         /* dummy check */
138         if ( name == NULL || name[ 0 ] == '\0' ) {
139                 return NULL;
140         }
141
142         /* try to find existing picoModel */
143         model = FindModel( name, frame );
144         if ( model != NULL ) {
145                 return model;
146         }
147
148         /* none found, so find first non-null picoModel */
149         pm = NULL;
150         for ( i = 0; i < MAX_MODELS; i++ )
151         {
152                 if ( picoModels[ i ] == NULL ) {
153                         pm = &picoModels[ i ];
154                         break;
155                 }
156         }
157
158         /* too many picoModels? */
159         if ( pm == NULL ) {
160                 Error( "MAX_MODELS (%d) exceeded, there are too many model files referenced by the map.", MAX_MODELS );
161         }
162
163         /* attempt to parse model */
164         *pm = PicoLoadModel( (char*) name, frame );
165
166         /* if loading failed, make a bogus model to silence the rest of the warnings */
167         if ( *pm == NULL ) {
168                 /* allocate a new model */
169                 *pm = PicoNewModel();
170                 if ( *pm == NULL ) {
171                         return NULL;
172                 }
173
174                 /* set data */
175                 PicoSetModelName( *pm, name );
176                 PicoSetModelFrameNum( *pm, frame );
177         }
178
179         /* debug code */
180         #if 0
181         {
182                 int numSurfaces, numVertexes;
183                 picoSurface_t   *ps;
184
185
186                 Sys_Printf( "Model %s\n", name );
187                 numSurfaces = PicoGetModelNumSurfaces( *pm );
188                 for ( i = 0; i < numSurfaces; i++ )
189                 {
190                         ps = PicoGetModelSurface( *pm, i );
191                         numVertexes = PicoGetSurfaceNumVertexes( ps );
192                         Sys_Printf( "Surface %d has %d vertexes\n", i, numVertexes );
193                 }
194         }
195         #endif
196
197         /* set count */
198         if ( *pm != NULL ) {
199                 numPicoModels++;
200         }
201
202         /* return the picoModel */
203         return *pm;
204 }
205
206
207
208 /*
209    InsertModel() - ydnar
210    adds a picomodel into the bsp
211  */
212
213 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 ){
214         int i, j, k, s, numSurfaces;
215         m4x4_t identity, nTransform;
216         picoModel_t         *model;
217         picoShader_t        *shader;
218         picoSurface_t       *surface;
219         shaderInfo_t        *si;
220         mapDrawSurface_t    *ds;
221         bspDrawVert_t       *dv;
222         char                *picoShaderName;
223         char shaderName[ MAX_QPATH ];
224         picoVec_t           *xyz, *normal, *st;
225         byte                *color;
226         picoIndex_t         *indexes;
227         remap_t             *rm, *glob;
228
229
230         /* get model */
231         model = LoadModel( name, frame );
232         if ( model == NULL ) {
233                 return;
234         }
235
236         /* handle null matrix */
237         if ( transform == NULL ) {
238                 m4x4_identity( identity );
239                 transform = identity;
240         }
241
242         /* hack: Stable-1_2 and trunk have differing row/column major matrix order
243            this transpose is necessary with Stable-1_2
244            uncomment the following line with old m4x4_t (non 1.3/spog_branch) code */
245         //%     m4x4_transpose( transform );
246
247         /* create transform matrix for normals */
248         memcpy( nTransform, transform, sizeof( m4x4_t ) );
249         if ( m4x4_invert( nTransform ) ) {
250                 Sys_FPrintf( SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n" );
251         }
252         m4x4_transpose( nTransform );
253
254         /* fix bogus lightmap scale */
255         if ( lightmapScale <= 0.0f ) {
256                 lightmapScale = 1.0f;
257         }
258
259         /* each surface on the model will become a new map drawsurface */
260         numSurfaces = PicoGetModelNumSurfaces( model );
261         //%     Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces );
262         for ( s = 0; s < numSurfaces; s++ )
263         {
264                 /* get surface */
265                 surface = PicoGetModelSurface( model, s );
266                 if ( surface == NULL ) {
267                         continue;
268                 }
269
270                 /* only handle triangle surfaces initially (fixme: support patches) */
271                 if ( PicoGetSurfaceType( surface ) != PICO_TRIANGLES ) {
272                         continue;
273                 }
274
275                 /* fix the surface's normals */
276                 PicoFixSurfaceNormals( surface );
277
278                 /* allocate a surface (ydnar: gs mods) */
279                 ds = AllocDrawSurface( SURFACE_TRIANGLES );
280                 ds->entityNum = eNum;
281                 ds->castShadows = castShadows;
282                 ds->recvShadows = recvShadows;
283
284                 /* get shader name */
285                 shader = PicoGetSurfaceShader( surface );
286                 if ( shader == NULL ) {
287                         picoShaderName = "";
288                 }
289                 else{
290                         picoShaderName = PicoGetShaderName( shader );
291                 }
292
293                 /* handle shader remapping */
294                 glob = NULL;
295                 for ( rm = remap; rm != NULL; rm = rm->next )
296                 {
297                         if ( rm->from[ 0 ] == '*' && rm->from[ 1 ] == '\0' ) {
298                                 glob = rm;
299                         }
300                         else if ( !Q_stricmp( picoShaderName, rm->from ) ) {
301                                 Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to );
302                                 picoShaderName = rm->to;
303                                 glob = NULL;
304                                 break;
305                         }
306                 }
307
308                 if ( glob != NULL ) {
309                         Sys_FPrintf( SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to );
310                         picoShaderName = glob->to;
311                 }
312
313                 /* shader renaming for sof2 */
314                 if ( renameModelShaders ) {
315                         strcpy( shaderName, picoShaderName );
316                         StripExtension( shaderName );
317                         if ( spawnFlags & 1 ) {
318                                 strcat( shaderName, "_RMG_BSP" );
319                         }
320                         else{
321                                 strcat( shaderName, "_BSP" );
322                         }
323                         si = ShaderInfoForShader( shaderName );
324                 }
325                 else{
326                         si = ShaderInfoForShader( picoShaderName );
327                 }
328
329                 /* set shader */
330                 ds->shaderInfo = si;
331
332                 /* set lightmap scale */
333                 ds->lightmapScale = lightmapScale;
334
335                 /* force to meta? */
336                 if ( ( si != NULL && si->forceMeta ) || ( spawnFlags & 4 ) ) { /* 3rd bit */
337                         ds->type = SURFACE_FORCED_META;
338                 }
339
340                 /* set particulars */
341                 ds->numVerts = PicoGetSurfaceNumVertexes( surface );
342                 ds->verts = safe_malloc( ds->numVerts * sizeof( ds->verts[ 0 ] ) );
343                 memset( ds->verts, 0, ds->numVerts * sizeof( ds->verts[ 0 ] ) );
344
345                 ds->numIndexes = PicoGetSurfaceNumIndexes( surface );
346                 ds->indexes = safe_malloc( ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );
347                 memset( ds->indexes, 0, ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );
348
349                 /* copy vertexes */
350                 for ( i = 0; i < ds->numVerts; i++ )
351                 {
352                         /* get vertex */
353                         dv = &ds->verts[ i ];
354
355                         /* xyz and normal */
356                         xyz = PicoGetSurfaceXYZ( surface, i );
357                         VectorCopy( xyz, dv->xyz );
358                         m4x4_transform_point( transform, dv->xyz );
359
360                         normal = PicoGetSurfaceNormal( surface, i );
361                         VectorCopy( normal, dv->normal );
362                         m4x4_transform_normal( nTransform, dv->normal );
363                         VectorNormalize( dv->normal, dv->normal );
364
365                         /* ydnar: tek-fu celshading support for flat shaded shit */
366                         if ( flat ) {
367                                 dv->st[ 0 ] = si->stFlat[ 0 ];
368                                 dv->st[ 1 ] = si->stFlat[ 1 ];
369                         }
370
371                         /* ydnar: gs mods: added support for explicit shader texcoord generation */
372                         else if ( si->tcGen ) {
373                                 /* project the texture */
374                                 dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], dv->xyz );
375                                 dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], dv->xyz );
376                         }
377
378                         /* normal texture coordinates */
379                         else
380                         {
381                                 st = PicoGetSurfaceST( surface, 0, i );
382                                 dv->st[ 0 ] = st[ 0 ];
383                                 dv->st[ 1 ] = st[ 1 ];
384                         }
385
386                         /* set lightmap/color bits */
387                         color = PicoGetSurfaceColor( surface, 0, i );
388                         for ( j = 0; j < MAX_LIGHTMAPS; j++ )
389                         {
390                                 dv->lightmap[ j ][ 0 ] = 0.0f;
391                                 dv->lightmap[ j ][ 1 ] = 0.0f;
392                                 dv->color[ j ][ 0 ] = color[ 0 ];
393                                 dv->color[ j ][ 1 ] = color[ 1 ];
394                                 dv->color[ j ][ 2 ] = color[ 2 ];
395                                 dv->color[ j ][ 3 ] = color[ 3 ];
396                         }
397                 }
398
399                 /* copy indexes */
400                 indexes = PicoGetSurfaceIndexes( surface, 0 );
401                 for ( i = 0; i < ds->numIndexes; i++ )
402                         ds->indexes[ i ] = indexes[ i ];
403
404                 /* set cel shader */
405                 ds->celShader = celShader;
406
407                 /* ydnar: giant hack land: generate clipping brushes for model triangles */
408                 if ( si->clipModel || ( spawnFlags & 2 ) ) { /* 2nd bit */
409                         vec3_t points[ 3 ], backs[ 3 ];
410                         vec4_t plane, reverse, pa, pb, pc;
411                         vec3_t nadir;
412
413
414                         /* temp hack */
415                         if ( !si->clipModel &&
416                                  ( ( si->compileFlags & C_TRANSLUCENT ) || !( si->compileFlags & C_SOLID ) ) ) {
417                                 continue;
418                         }
419
420                         /* overflow check */
421                         if ( ( nummapplanes + 64 ) >= ( MAX_MAP_PLANES >> 1 ) ) {
422                                 continue;
423                         }
424
425                         /* walk triangle list */
426                         for ( i = 0; i < ds->numIndexes; i += 3 )
427                         {
428                                 /* overflow hack */
429                                 if ( ( nummapplanes + 64 ) >= ( MAX_MAP_PLANES >> 1 ) ) {
430                                         Sys_Printf( "WARNING: MAX_MAP_PLANES (%d) hit generating clip brushes for model %s.\n",
431                                                                 MAX_MAP_PLANES, name );
432                                         break;
433                                 }
434
435                                 /* make points and back points */
436                                 for ( j = 0; j < 3; j++ )
437                                 {
438                                         /* get vertex */
439                                         dv = &ds->verts[ ds->indexes[ i + j ] ];
440
441                                         /* copy xyz */
442                                         VectorCopy( dv->xyz, points[ j ] );
443 #if !Q3MAP2_EXPERIMENTAL_MODEL_CLIPPING_FIX
444                                         // The code below is totally unneeded regardless of Q3MAP2_EXPERIMENTAL_MODEL_CLIPPING_FIX.
445                                         // backs is reinitialized further below.  However, the extra code does not hurt anything.
446                                         VectorCopy( dv->xyz, backs[ j ] );
447
448                                         /* find nearest axial to normal and push back points opposite */
449                                         /* note: this doesn't work as well as simply using the plane of the triangle, below */
450                                         for ( k = 0; k < 3; k++ )
451                                         {
452                                                 if ( fabs( dv->normal[ k ] ) > fabs( dv->normal[ ( k + 1 ) % 3 ] ) &&
453                                                          fabs( dv->normal[ k ] ) > fabs( dv->normal[ ( k + 2 ) % 3 ] ) ) {
454                                                         backs[ j ][ k ] += dv->normal[ k ] < 0.0f ? 64.0f : -64.0f;
455                                                         break;
456                                                 }
457                                         }
458 #endif
459                                 }
460
461                                 /* make plane for triangle */
462                                 if ( PlaneFromPoints( plane, points[ 0 ], points[ 1 ], points[ 2 ] ) ) {
463                                         /* regenerate back points */
464                                         for ( j = 0; j < 3; j++ )
465                                         {
466                                                 /* get vertex */
467                                                 dv = &ds->verts[ ds->indexes[ i + j ] ];
468
469                                                 /* copy xyz */
470                                                 VectorCopy( dv->xyz, backs[ j ] );
471
472                                                 /* find nearest axial to plane normal and push back points opposite */
473                                                 for ( k = 0; k < 3; k++ )
474                                                 {
475 #if Q3MAP2_EXPERIMENTAL_MODEL_CLIPPING_FIX
476                                                         if ( fabs( plane[ k ] ) >= fabs( plane[ ( k + 1 ) % 3 ] ) &&
477                                                                  fabs( plane[ k ] ) >= fabs( plane[ ( k + 2 ) % 3 ] ) )
478 #else
479                                                         // This code is broken for 45 degree angles where there
480                                                         // is no clear winner.
481                                                         if ( fabs( plane[ k ] ) > fabs( plane[ ( k + 1 ) % 3 ] ) &&
482                                                                  fabs( plane[ k ] ) > fabs( plane[ ( k + 2 ) % 3 ] ) )
483 #endif
484                                                         {
485                                                                 backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;
486                                                                 break;
487                                                         }
488                                                 }
489                                         }
490
491                                         /* make back plane */
492                                         VectorScale( plane, -1.0f, reverse );
493                                         reverse[ 3 ] = -( plane[ 3 ] - 1 );
494
495                                         /* make back pyramid point */
496                                         VectorCopy( points[ 0 ], nadir );
497                                         VectorAdd( nadir, points[ 1 ], nadir );
498                                         VectorAdd( nadir, points[ 2 ], nadir );
499                                         VectorScale( nadir, 0.3333333333333f, nadir );
500                                         VectorMA( nadir, -2.0f, plane, nadir );
501
502                                         /* make 3 more planes */
503                                         //%     if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], nadir ) &&
504                                         //%             PlaneFromPoints( pb, points[ 1 ], points[ 0 ], nadir ) &&
505                                         //%             PlaneFromPoints( pc, points[ 0 ], points[ 2 ], nadir ) )
506                                         if ( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], backs[ 1 ] ) &&
507                                                  PlaneFromPoints( pb, points[ 1 ], points[ 0 ], backs[ 0 ] ) &&
508                                                  PlaneFromPoints( pc, points[ 0 ], points[ 2 ], backs[ 2 ] ) ) {
509                                                 /* build a brush */
510                                                 buildBrush = AllocBrush( 48 );
511
512                                                 buildBrush->entityNum = mapEntityNum;
513                                                 buildBrush->original = buildBrush;
514                                                 buildBrush->contentShader = si;
515                                                 buildBrush->compileFlags = si->compileFlags;
516                                                 buildBrush->contentFlags = si->contentFlags;
517                                                 buildBrush->detail = qtrue;
518
519                                                 /* set up brush sides */
520                                                 buildBrush->numsides = 5;
521                                                 for ( j = 0; j < buildBrush->numsides; j++ )
522                                                         buildBrush->sides[ j ].shaderInfo = si;
523                                                 buildBrush->sides[ 0 ].planenum = FindFloatPlane( plane, plane[ 3 ], 3, points );
524                                                 buildBrush->sides[ 1 ].planenum = FindFloatPlane( pa, pa[ 3 ], 1, &points[ 2 ] );
525                                                 buildBrush->sides[ 2 ].planenum = FindFloatPlane( pb, pb[ 3 ], 1, &points[ 1 ] );
526                                                 buildBrush->sides[ 3 ].planenum = FindFloatPlane( pc, pc[ 3 ], 1, &points[ 0 ] );
527                                                 buildBrush->sides[ 4 ].planenum = FindFloatPlane( reverse, reverse[ 3 ], 3, points );
528
529                                                 /* add to entity */
530                                                 if ( CreateBrushWindings( buildBrush ) ) {
531                                                         AddBrushBevels();
532                                                         //%     EmitBrushes( buildBrush, NULL, NULL );
533                                                         buildBrush->next = entities[ mapEntityNum ].brushes;
534                                                         entities[ mapEntityNum ].brushes = buildBrush;
535                                                         entities[ mapEntityNum ].numBrushes++;
536                                                 }
537                                                 else{
538                                                         free( buildBrush );
539                                                 }
540                                         }
541                                 }
542                         }
543                 }
544         }
545 }
546
547
548
549 /*
550    AddTriangleModels()
551    adds misc_model surfaces to the bsp
552  */
553
554 void AddTriangleModels( entity_t *e ){
555         int num, frame, castShadows, recvShadows, spawnFlags;
556         entity_t        *e2;
557         const char      *targetName;
558         const char      *target, *model, *value;
559         char shader[ MAX_QPATH ];
560         shaderInfo_t    *celShader;
561         float temp, baseLightmapScale, lightmapScale;
562         vec3_t origin, scale, angles;
563         m4x4_t transform;
564         epair_t         *ep;
565         remap_t         *remap, *remap2;
566         char            *split;
567
568
569         /* note it */
570         Sys_FPrintf( SYS_VRB, "--- AddTriangleModels ---\n" );
571
572         /* get current brush entity targetname */
573         if ( e == entities ) {
574                 targetName = "";
575         }
576         else
577         {
578                 targetName = ValueForKey( e, "targetname" );
579
580                 /* misc_model entities target non-worldspawn brush model entities */
581                 if ( targetName[ 0 ] == '\0' ) {
582                         return;
583                 }
584         }
585
586         /* get lightmap scale */
587         baseLightmapScale = FloatForKey( e, "_lightmapscale" );
588         if ( baseLightmapScale <= 0.0f ) {
589                 baseLightmapScale = 0.0f;
590         }
591
592         /* walk the entity list */
593         for ( num = 1; num < numEntities; num++ )
594         {
595                 /* get e2 */
596                 e2 = &entities[ num ];
597
598                 /* convert misc_models into raw geometry */
599                 if ( Q_stricmp( "misc_model", ValueForKey( e2, "classname" ) ) ) {
600                         continue;
601                 }
602
603                 /* ydnar: added support for md3 models on non-worldspawn models */
604                 target = ValueForKey( e2, "target" );
605                 if ( strcmp( target, targetName ) ) {
606                         continue;
607                 }
608
609                 /* get model name */
610                 model = ValueForKey( e2, "model" );
611                 if ( model[ 0 ] == '\0' ) {
612                         Sys_Printf( "WARNING: misc_model at %i %i %i without a model key\n",
613                                                 (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
614                         continue;
615                 }
616
617                 /* get model frame */
618                 frame = IntForKey( e2, "_frame" );
619
620                 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
621                 if ( e == entities ) {
622                         castShadows = WORLDSPAWN_CAST_SHADOWS;
623                         recvShadows = WORLDSPAWN_RECV_SHADOWS;
624                 }
625
626                 /* other entities don't cast any shadows, but recv worldspawn shadows */
627                 else
628                 {
629                         castShadows = ENTITY_CAST_SHADOWS;
630                         recvShadows = ENTITY_RECV_SHADOWS;
631                 }
632
633                 /* get explicit shadow flags */
634                 GetEntityShadowFlags( e2, e, &castShadows, &recvShadows );
635
636                 /* get spawnflags */
637                 spawnFlags = IntForKey( e2, "spawnflags" );
638
639                 /* get origin */
640                 GetVectorForKey( e2, "origin", origin );
641                 VectorSubtract( origin, e->origin, origin );    /* offset by parent */
642
643                 /* get scale */
644                 scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = 1.0f;
645                 temp = FloatForKey( e2, "modelscale" );
646                 if ( temp != 0.0f ) {
647                         scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = temp;
648                 }
649                 value = ValueForKey( e2, "modelscale_vec" );
650                 if ( value[ 0 ] != '\0' ) {
651                         sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
652                 }
653
654                 /* get "angle" (yaw) or "angles" (pitch yaw roll) */
655                 angles[ 0 ] = angles[ 1 ] = angles[ 2 ] = 0.0f;
656                 angles[ 2 ] = FloatForKey( e2, "angle" );
657                 value = ValueForKey( e2, "angles" );
658                 if ( value[ 0 ] != '\0' ) {
659                         sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );
660                 }
661
662                 /* set transform matrix (thanks spog) */
663                 m4x4_identity( transform );
664                 m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );
665
666                 /* get shader remappings */
667                 remap = NULL;
668                 for ( ep = e2->epairs; ep != NULL; ep = ep->next )
669                 {
670                         /* look for keys prefixed with "_remap" */
671                         if ( ep->key != NULL && ep->value != NULL &&
672                                  ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' &&
673                                  !Q_strncasecmp( ep->key, "_remap", 6 ) ) {
674                                 /* create new remapping */
675                                 remap2 = remap;
676                                 remap = safe_malloc( sizeof( *remap ) );
677                                 remap->next = remap2;
678                                 strcpy( remap->from, ep->value );
679
680                                 /* split the string */
681                                 split = strchr( remap->from, ';' );
682                                 if ( split == NULL ) {
683                                         Sys_Printf( "WARNING: Shader _remap key found in misc_model without a ; character\n" );
684                                         free( remap );
685                                         remap = remap2;
686                                         continue;
687                                 }
688
689                                 /* store the split */
690                                 *split = '\0';
691                                 strcpy( remap->to, ( split + 1 ) );
692
693                                 /* note it */
694                                 //%     Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", remap->from, remap->to );
695                         }
696                 }
697
698                 /* ydnar: cel shader support */
699                 value = ValueForKey( e2, "_celshader" );
700                 if ( value[ 0 ] == '\0' ) {
701                         value = ValueForKey( &entities[ 0 ], "_celshader" );
702                 }
703                 if ( value[ 0 ] != '\0' ) {
704                         sprintf( shader, "textures/%s", value );
705                         celShader = ShaderInfoForShader( shader );
706                 }
707                 else{
708                         celShader = NULL;
709                 }
710
711                 /* get lightmap scale */
712                 lightmapScale = FloatForKey( e2, "_lightmapscale" );
713                 if ( lightmapScale <= 0.0f ) {
714                         lightmapScale = baseLightmapScale;
715                 }
716
717                 /* insert the model */
718                 InsertModel( (char*) model, frame, transform, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale );
719
720                 /* free shader remappings */
721                 while ( remap != NULL )
722                 {
723                         remap2 = remap->next;
724                         free( remap );
725                         remap = remap2;
726                 }
727         }
728 }