]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/Model.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / renderer / Model.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "tr_local.h"
33 #include "Model_local.h"
34 #include "Model_ase.h"
35 #include "Model_lwo.h"
36 #include "Model_ma.h"
37
38 idCVar idRenderModelStatic::r_mergeModelSurfaces( "r_mergeModelSurfaces", "1", CVAR_BOOL|CVAR_RENDERER, "combine model surfaces with the same material" );
39 idCVar idRenderModelStatic::r_slopVertex( "r_slopVertex", "0.01", CVAR_RENDERER, "merge xyz coordinates this far apart" );
40 idCVar idRenderModelStatic::r_slopTexCoord( "r_slopTexCoord", "0.001", CVAR_RENDERER, "merge texture coordinates this far apart" );
41 idCVar idRenderModelStatic::r_slopNormal( "r_slopNormal", "0.02", CVAR_RENDERER, "merge normals that dot less than this" );
42
43 /*
44 ================
45 idRenderModelStatic::idRenderModelStatic
46 ================
47 */
48 idRenderModelStatic::idRenderModelStatic() {
49         name = "<undefined>";
50         bounds.Clear();
51         lastModifiedFrame = 0;
52         lastArchivedFrame = 0;
53         overlaysAdded = 0;
54         shadowHull = NULL;
55         isStaticWorldModel = false;
56         defaulted = false;
57         purged = false;
58         fastLoad = false;
59         reloadable = true;
60         levelLoadReferenced = false;
61         timeStamp = 0;
62 }
63
64 /*
65 ================
66 idRenderModelStatic::~idRenderModelStatic
67 ================
68 */
69 idRenderModelStatic::~idRenderModelStatic() {
70         PurgeModel();
71 }
72
73 /*
74 ==============
75 idRenderModelStatic::Print
76 ==============
77 */
78 void idRenderModelStatic::Print() const {
79         common->Printf( "%s\n", name.c_str() );
80         common->Printf( "Static model.\n" );
81         common->Printf( "bounds: (%f %f %f) to (%f %f %f)\n", 
82                 bounds[0][0], bounds[0][1], bounds[0][2], 
83                 bounds[1][0], bounds[1][1], bounds[1][2] );
84
85         common->Printf( "    verts  tris material\n" );
86         for ( int i = 0 ; i < NumSurfaces() ; i++ ) {
87                 const modelSurface_t    *surf = Surface( i );
88
89                 srfTriangles_t *tri = surf->geometry;
90                 const idMaterial *material = surf->shader;
91                 
92                 if ( !tri ) {
93                         common->Printf( "%2i: %s, NULL surface geometry\n", i, material->GetName() );
94                         continue;
95                 }
96
97                 common->Printf( "%2i: %5i %5i %s", i, tri->numVerts, tri->numIndexes / 3, material->GetName() );
98                 if ( tri->generateNormals ) {
99                         common->Printf( " (smoothed)\n" );
100                 } else {
101                         common->Printf( "\n" );
102                 }
103         }       
104 }
105
106 /*
107 ==============
108 idRenderModelStatic::Memory
109 ==============
110 */
111 int idRenderModelStatic::Memory() const {
112         int     totalBytes = 0;
113
114         totalBytes += sizeof( *this );
115         totalBytes += name.DynamicMemoryUsed();
116         totalBytes += surfaces.MemoryUsed();
117
118         if ( shadowHull ) {
119                 totalBytes += R_TriSurfMemory( shadowHull );
120         }
121
122         for ( int j = 0 ; j < NumSurfaces() ; j++ ) {
123                 const modelSurface_t    *surf = Surface( j );
124                 if ( !surf->geometry ) {
125                         continue;
126                 }
127                 totalBytes += R_TriSurfMemory( surf->geometry );
128         }
129
130         return totalBytes;
131 }
132
133 /*
134 ==============
135 idRenderModelStatic::List
136 ==============
137 */
138 void idRenderModelStatic::List() const {
139         int     totalTris = 0;
140         int     totalVerts = 0;
141         int     totalBytes = 0;
142
143         totalBytes = Memory();
144
145         char    closed = 'C';
146         for ( int j = 0 ; j < NumSurfaces() ; j++ ) {
147                 const modelSurface_t    *surf = Surface( j );
148                 if ( !surf->geometry ) {
149                         continue;
150                 }
151                 if ( !surf->geometry->perfectHull ) {
152                         closed = ' ';
153                 }
154                 totalTris += surf->geometry->numIndexes / 3;
155                 totalVerts += surf->geometry->numVerts;
156         }
157         common->Printf( "%c%4ik %3i %4i %4i %s", closed, totalBytes/1024, NumSurfaces(), totalVerts, totalTris, Name() );
158
159         if ( IsDynamicModel() == DM_CACHED ) {
160                 common->Printf( " (DM_CACHED)" );
161         }
162         if ( IsDynamicModel() == DM_CONTINUOUS ) {
163                 common->Printf( " (DM_CONTINUOUS)" );
164         }
165         if ( defaulted ) {
166                 common->Printf( " (DEFAULTED)" );
167         }
168         if ( bounds[0][0] >= bounds[1][0] ) {
169                 common->Printf( " (EMPTY BOUNDS)" );
170         }
171         if ( bounds[1][0] - bounds[0][0] > 100000 ) {
172                 common->Printf( " (HUGE BOUNDS)" );
173         }
174
175         common->Printf( "\n" );
176 }
177
178 /*
179 ================
180 idRenderModelStatic::IsDefaultModel
181 ================
182 */
183 bool idRenderModelStatic::IsDefaultModel() const {
184         return defaulted;
185 }
186
187 /*
188 ================
189 AddCubeFace
190 ================
191 */
192 static void AddCubeFace( srfTriangles_t *tri, idVec3 v1, idVec3 v2, idVec3 v3, idVec3 v4 ) {
193         tri->verts[tri->numVerts+0].Clear();
194         tri->verts[tri->numVerts+0].xyz = v1 * 8;
195         tri->verts[tri->numVerts+0].st[0] = 0;
196         tri->verts[tri->numVerts+0].st[1] = 0;
197
198         tri->verts[tri->numVerts+1].Clear();
199         tri->verts[tri->numVerts+1].xyz = v2 * 8;
200         tri->verts[tri->numVerts+1].st[0] = 1;
201         tri->verts[tri->numVerts+1].st[1] = 0;
202
203         tri->verts[tri->numVerts+2].Clear();
204         tri->verts[tri->numVerts+2].xyz = v3 * 8;
205         tri->verts[tri->numVerts+2].st[0] = 1;
206         tri->verts[tri->numVerts+2].st[1] = 1;
207
208         tri->verts[tri->numVerts+3].Clear();
209         tri->verts[tri->numVerts+3].xyz = v4 * 8;
210         tri->verts[tri->numVerts+3].st[0] = 0;
211         tri->verts[tri->numVerts+3].st[1] = 1;
212
213         tri->indexes[tri->numIndexes+0] = tri->numVerts + 0;
214         tri->indexes[tri->numIndexes+1] = tri->numVerts + 1;
215         tri->indexes[tri->numIndexes+2] = tri->numVerts + 2;
216         tri->indexes[tri->numIndexes+3] = tri->numVerts + 0;
217         tri->indexes[tri->numIndexes+4] = tri->numVerts + 2;
218         tri->indexes[tri->numIndexes+5] = tri->numVerts + 3;
219
220         tri->numVerts += 4;
221         tri->numIndexes += 6;
222 }
223
224 /*
225 ================
226 idRenderModelStatic::MakeDefaultModel
227 ================
228 */
229 void idRenderModelStatic::MakeDefaultModel() {
230
231         defaulted = true;
232
233         // throw out any surfaces we already have
234         PurgeModel();
235
236         // create one new surface
237         modelSurface_t  surf;
238
239         srfTriangles_t *tri = R_AllocStaticTriSurf();
240
241         surf.shader = tr.defaultMaterial;
242         surf.geometry = tri;
243
244         R_AllocStaticTriSurfVerts( tri, 24 );
245         R_AllocStaticTriSurfIndexes( tri, 36 );
246
247         AddCubeFace( tri, idVec3(-1, 1, 1), idVec3(1, 1, 1), idVec3(1, -1, 1), idVec3(-1, -1, 1) );
248         AddCubeFace( tri, idVec3(-1, 1, -1), idVec3(-1, -1, -1), idVec3(1, -1, -1), idVec3(1, 1, -1) );
249
250         AddCubeFace( tri, idVec3(1, -1, 1), idVec3(1, 1, 1), idVec3(1, 1, -1), idVec3(1, -1, -1) );
251         AddCubeFace( tri, idVec3(-1, -1, 1), idVec3(-1, -1, -1), idVec3(-1, 1, -1), idVec3(-1, 1, 1) );
252
253         AddCubeFace( tri, idVec3(-1, -1, 1), idVec3(1, -1, 1), idVec3(1, -1, -1), idVec3(-1, -1, -1) );
254         AddCubeFace( tri, idVec3(-1, 1, 1), idVec3(-1, 1, -1), idVec3(1, 1, -1), idVec3(1, 1, 1) );
255
256         tri->generateNormals = true;
257
258         AddSurface( surf );
259         FinishSurfaces();
260 }
261
262 /*
263 ================
264 idRenderModelStatic::PartialInitFromFile
265 ================
266 */
267 void idRenderModelStatic::PartialInitFromFile( const char *fileName ) {
268         fastLoad = true;
269         InitFromFile( fileName );
270 }
271
272 /*
273 ================
274 idRenderModelStatic::InitFromFile
275 ================
276 */
277 void idRenderModelStatic::InitFromFile( const char *fileName ) {
278         bool loaded;
279         idStr extension;
280
281         InitEmpty( fileName );
282
283         // FIXME: load new .proc map format
284
285         name.ExtractFileExtension( extension );
286
287         if ( extension.Icmp( "ase" ) == 0 ) {
288                 loaded          = LoadASE( name );
289                 reloadable      = true;
290         } else if ( extension.Icmp( "lwo" ) == 0 ) {
291                 loaded          = LoadLWO( name );
292                 reloadable      = true;
293         } else if ( extension.Icmp( "flt" ) == 0 ) {
294                 loaded          = LoadFLT( name );
295                 reloadable      = true;
296         } else if ( extension.Icmp( "ma" ) == 0 ) {
297                 loaded          = LoadMA( name );
298                 reloadable      = true;
299         } else {
300                 common->Warning( "idRenderModelStatic::InitFromFile: unknown type for model: \'%s\'", name.c_str() );
301                 loaded          = false;
302         }
303
304         if ( !loaded ) {
305                 common->Warning( "Couldn't load model: '%s'", name.c_str() );
306                 MakeDefaultModel();
307                 return;
308         }
309
310         // it is now available for use
311         purged = false;
312
313         // create the bounds for culling and dynamic surface creation
314         FinishSurfaces();
315 }
316
317 /*
318 ================
319 idRenderModelStatic::LoadModel
320 ================
321 */
322 void idRenderModelStatic::LoadModel() {
323         PurgeModel();
324         InitFromFile( name );
325 }
326
327 /*
328 ================
329 idRenderModelStatic::InitEmpty
330 ================
331 */
332 void idRenderModelStatic::InitEmpty( const char *fileName ) {
333         // model names of the form _area* are static parts of the
334         // world, and have already been considered for optimized shadows
335         // other model names are inline entity models, and need to be
336         // shadowed normally
337         if ( !idStr::Cmpn( fileName, "_area", 5 ) ) {
338                 isStaticWorldModel = true;
339         } else {
340                 isStaticWorldModel = false;
341         }
342
343         name = fileName;
344         reloadable = false;     // if it didn't come from a file, we can't reload it
345         PurgeModel();
346         purged = false;
347         bounds.Zero();
348 }
349
350 /*
351 ================
352 idRenderModelStatic::AddSurface
353 ================
354 */
355 void idRenderModelStatic::AddSurface( modelSurface_t surface ) {
356         surfaces.Append( surface );
357         if ( surface.geometry ) {
358                 bounds += surface.geometry->bounds;
359         }
360 }
361
362 /*
363 ================
364 idRenderModelStatic::Name
365 ================
366 */
367 const char *idRenderModelStatic::Name() const {
368         return name;
369 }
370
371 /*
372 ================
373 idRenderModelStatic::Timestamp
374 ================
375 */
376 ID_TIME_T idRenderModelStatic::Timestamp() const {
377         return timeStamp;
378 }
379
380 /*
381 ================
382 idRenderModelStatic::NumSurfaces
383 ================
384 */
385 int idRenderModelStatic::NumSurfaces() const {
386         return surfaces.Num();
387 }
388
389 /*
390 ================
391 idRenderModelStatic::NumBaseSurfaces
392 ================
393 */
394 int idRenderModelStatic::NumBaseSurfaces() const {
395         return surfaces.Num() - overlaysAdded;
396 }
397
398 /*
399 ================
400 idRenderModelStatic::Surface
401 ================
402 */
403 const modelSurface_t *idRenderModelStatic::Surface( int surfaceNum ) const {
404         return &surfaces[surfaceNum];
405 }
406
407 /*
408 ================
409 idRenderModelStatic::AllocSurfaceTriangles
410 ================
411 */
412 srfTriangles_t *idRenderModelStatic::AllocSurfaceTriangles( int numVerts, int numIndexes ) const {
413         srfTriangles_t *tri = R_AllocStaticTriSurf();
414         R_AllocStaticTriSurfVerts( tri, numVerts );
415         R_AllocStaticTriSurfIndexes( tri, numIndexes );
416         return tri;
417 }
418
419 /*
420 ================
421 idRenderModelStatic::FreeSurfaceTriangles
422 ================
423 */
424 void idRenderModelStatic::FreeSurfaceTriangles( srfTriangles_t *tris ) const {
425         R_FreeStaticTriSurf( tris );
426 }
427
428 /*
429 ================
430 idRenderModelStatic::ShadowHull
431 ================
432 */
433 srfTriangles_t *idRenderModelStatic::ShadowHull() const {
434         return shadowHull;
435 }
436
437 /*
438 ================
439 idRenderModelStatic::IsStaticWorldModel
440 ================
441 */
442 bool idRenderModelStatic::IsStaticWorldModel() const {
443         return isStaticWorldModel;
444 }
445
446 /*
447 ================
448 idRenderModelStatic::IsDynamicModel
449 ================
450 */
451 dynamicModel_t idRenderModelStatic::IsDynamicModel() const {
452         // dynamic subclasses will override this
453         return DM_STATIC;
454 }
455
456 /*
457 ================
458 idRenderModelStatic::IsReloadable
459 ================
460 */
461 bool idRenderModelStatic::IsReloadable() const {
462         return reloadable;
463 }
464
465 /*
466 ================
467 idRenderModelStatic::Bounds
468 ================
469 */
470 idBounds idRenderModelStatic::Bounds( const struct renderEntity_s *mdef ) const {
471         return bounds;
472 }
473
474 /*
475 ================
476 idRenderModelStatic::DepthHack
477 ================
478 */
479 float idRenderModelStatic::DepthHack() const {
480         return 0.0f;
481 }
482
483 /*
484 ================
485 idRenderModelStatic::InstantiateDynamicModel
486 ================
487 */
488 idRenderModel *idRenderModelStatic::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
489         if ( cachedModel ) {
490                 delete cachedModel;
491                 cachedModel = NULL;
492         }
493         common->Error( "InstantiateDynamicModel called on static model '%s'", name.c_str() );
494         return NULL;
495 }
496
497 /*
498 ================
499 idRenderModelStatic::NumJoints
500 ================
501 */
502 int idRenderModelStatic::NumJoints( void ) const {
503         return 0;
504 }
505
506 /*
507 ================
508 idRenderModelStatic::GetJoints
509 ================
510 */
511 const idMD5Joint *idRenderModelStatic::GetJoints( void ) const {
512         return NULL;
513 }
514
515 /*
516 ================
517 idRenderModelStatic::GetJointHandle
518 ================
519 */
520 jointHandle_t idRenderModelStatic::GetJointHandle( const char *name ) const {
521         return INVALID_JOINT;
522 }
523
524 /*
525 ================
526 idRenderModelStatic::GetJointName
527 ================
528 */
529 const char * idRenderModelStatic::GetJointName( jointHandle_t handle ) const {
530         return "";
531 }
532
533 /*
534 ================
535 idRenderModelStatic::GetDefaultPose
536 ================
537 */
538 const idJointQuat *idRenderModelStatic::GetDefaultPose( void ) const {
539         return NULL;
540 }
541
542 /*
543 ================
544 idRenderModelStatic::NearestJoint
545 ================
546 */
547 int idRenderModelStatic::NearestJoint( int surfaceNum, int a, int b, int c ) const {
548         return INVALID_JOINT;
549 }
550
551
552 //=====================================================================
553
554
555 /*
556 ================
557 idRenderModelStatic::FinishSurfaces
558
559 The mergeShadows option allows surfaces with different textures to share
560 silhouette edges for shadow calculation, instead of leaving shared edges
561 hanging.
562
563 If any of the original shaders have the noSelfShadow flag set, the surfaces
564 can't be merged, because they will need to be drawn in different order.
565
566 If there is only one surface, a separate merged surface won't be generated.
567
568 A model with multiple surfaces can't later have a skinned shader change the
569 state of the noSelfShadow flag.
570
571 -----------------
572
573 Creates mirrored copies of two sided surfaces with normal maps, which would
574 otherwise light funny.
575
576 Extends the bounds of deformed surfaces so they don't cull incorrectly at screen edges.
577
578 ================
579 */
580 void idRenderModelStatic::FinishSurfaces() {
581         int                     i;
582         int                     totalVerts, totalIndexes;
583
584         purged = false;
585
586         // make sure we don't have a huge bounds even if we don't finish everything
587         bounds.Zero();
588
589         if ( surfaces.Num() == 0 ) {
590                 return;
591         }
592
593         // renderBump doesn't care about most of this
594         if ( fastLoad ) {
595                 bounds.Zero();
596                 for ( i = 0 ; i < surfaces.Num() ; i++ ) {
597                         const modelSurface_t    *surf = &surfaces[i];
598
599                         R_BoundTriSurf( surf->geometry );
600                         bounds.AddBounds( surf->geometry->bounds );
601                 }
602
603                 return;
604         }
605
606         // cleanup all the final surfaces, but don't create sil edges
607         totalVerts = 0;
608         totalIndexes = 0;
609
610         // decide if we are going to merge all the surfaces into one shadower
611         int     numOriginalSurfaces = surfaces.Num();
612
613         // make sure there aren't any NULL shaders or geometry
614         for ( i = 0 ; i < numOriginalSurfaces ; i++ ) {
615                 const modelSurface_t    *surf = &surfaces[i];
616
617                 if ( surf->geometry == NULL || surf->shader == NULL ) {
618                         MakeDefaultModel();
619                         common->Error( "Model %s, surface %i had NULL geometry", name.c_str(), i );
620                 }
621                 if ( surf->shader == NULL ) {
622                         MakeDefaultModel();
623                         common->Error( "Model %s, surface %i had NULL shader", name.c_str(), i );
624                 }
625         }
626
627         // duplicate and reverse triangles for two sided bump mapped surfaces
628         // note that this won't catch surfaces that have their shaders dynamically
629         // changed, and won't work with animated models.
630         // It is better to create completely separate surfaces, rather than
631         // add vertexes and indexes to the existing surface, because the
632         // tangent generation wouldn't like the acute shared edges
633         for ( i = 0 ; i < numOriginalSurfaces ; i++ ) {
634                 const modelSurface_t    *surf = &surfaces[i];
635
636                 if ( surf->shader->ShouldCreateBackSides() ) {
637                         srfTriangles_t *newTri;
638
639                         newTri = R_CopyStaticTriSurf( surf->geometry );
640                         R_ReverseTriangles( newTri );
641
642                         modelSurface_t  newSurf;
643
644                         newSurf.shader = surf->shader;
645                         newSurf.geometry = newTri;
646
647                         AddSurface( newSurf );
648                 }
649         }
650
651         // clean the surfaces
652         for ( i = 0 ; i < surfaces.Num() ; i++ ) {
653                 const modelSurface_t    *surf = &surfaces[i];
654
655                 R_CleanupTriangles( surf->geometry, surf->geometry->generateNormals, true, surf->shader->UseUnsmoothedTangents() );
656                 if ( surf->shader->SurfaceCastsShadow() ) {
657                         totalVerts += surf->geometry->numVerts;
658                         totalIndexes += surf->geometry->numIndexes;
659                 }
660         }
661
662         // add up the total surface area for development information
663         for ( i = 0 ; i < surfaces.Num() ; i++ ) {
664                 const modelSurface_t    *surf = &surfaces[i];
665                 srfTriangles_t  *tri = surf->geometry;
666
667                 for ( int j = 0 ; j < tri->numIndexes ; j += 3 ) {
668                         float   area = idWinding::TriangleArea( tri->verts[tri->indexes[j]].xyz,
669                                  tri->verts[tri->indexes[j+1]].xyz,  tri->verts[tri->indexes[j+2]].xyz );
670                         const_cast<idMaterial *>(surf->shader)->AddToSurfaceArea( area );
671                 }
672         }
673
674         // calculate the bounds
675         if ( surfaces.Num() == 0 ) {
676                 bounds.Zero();
677         } else {
678                 bounds.Clear();
679                 for ( i = 0 ; i < surfaces.Num() ; i++ ) {
680                         modelSurface_t  *surf = &surfaces[i];
681
682                         // if the surface has a deformation, increase the bounds
683                         // the amount here is somewhat arbitrary, designed to handle
684                         // autosprites and flares, but could be done better with exact
685                         // deformation information.
686                         // Note that this doesn't handle deformations that are skinned in
687                         // at run time...
688                         if ( surf->shader->Deform() != DFRM_NONE ) {
689                                 srfTriangles_t  *tri = surf->geometry;
690                                 idVec3  mid = ( tri->bounds[1] + tri->bounds[0] ) * 0.5f;
691                                 float   radius = ( tri->bounds[0] - mid ).Length();
692                                 radius += 20.0f;
693
694                                 tri->bounds[0][0] = mid[0] - radius;
695                                 tri->bounds[0][1] = mid[1] - radius;
696                                 tri->bounds[0][2] = mid[2] - radius;
697
698                                 tri->bounds[1][0] = mid[0] + radius;
699                                 tri->bounds[1][1] = mid[1] + radius;
700                                 tri->bounds[1][2] = mid[2] + radius;
701                         }
702
703                         // add to the model bounds
704                         bounds.AddBounds( surf->geometry->bounds );
705
706                 }
707         }
708 }
709
710 /*
711 =================
712 idRenderModelStatic::ConvertASEToModelSurfaces
713 =================
714 */
715 typedef struct matchVert_s {
716         struct matchVert_s      *next;
717         int             v, tv;
718         byte    color[4];
719         idVec3  normal;
720 } matchVert_t;
721
722 bool idRenderModelStatic::ConvertASEToModelSurfaces( const struct aseModel_s *ase ) {
723         aseObject_t *   object;
724         aseMesh_t *             mesh;
725         aseMaterial_t * material;
726         const idMaterial *im1, *im2;
727         srfTriangles_t *tri;
728         int                             objectNum;
729         int                             i, j, k;
730         int                             v, tv;
731         int *                   vRemap;
732         int *                   tvRemap;
733         matchVert_t *   mvTable;        // all of the match verts
734         matchVert_t **  mvHash;         // points inside mvTable for each xyz index
735         matchVert_t *   lastmv;
736         matchVert_t *   mv;
737         idVec3                  normal;
738         float                   uOffset, vOffset, textureSin, textureCos;
739         float                   uTiling, vTiling;
740         int *                   mergeTo;
741         byte *                  color;
742         static byte     identityColor[4] = { 255, 255, 255, 255 };
743         modelSurface_t  surf, *modelSurf;
744
745         if ( !ase ) {
746                 return false;
747         }
748         if ( ase->objects.Num() < 1 ) {
749                 return false;
750         }
751
752         timeStamp = ase->timeStamp;
753
754         // the modeling programs can save out multiple surfaces with a common
755         // material, but we would like to mege them together where possible
756         // meaning that this->NumSurfaces() <= ase->objects.currentElements
757         mergeTo = (int *)_alloca( ase->objects.Num() * sizeof( *mergeTo ) ); 
758         surf.geometry = NULL;
759         if ( ase->materials.Num() == 0 ) {
760                 // if we don't have any materials, dump everything into a single surface
761                 surf.shader = tr.defaultMaterial;
762                 surf.id = 0;
763                 this->AddSurface( surf );
764                 for ( i = 0 ; i < ase->objects.Num() ; i++ ) { 
765                         mergeTo[i] = 0;
766                 }
767         } else if ( !r_mergeModelSurfaces.GetBool() ) {
768                 // don't merge any
769                 for ( i = 0 ; i < ase->objects.Num() ; i++ ) { 
770                         mergeTo[i] = i;
771                         object = ase->objects[i];
772                         material = ase->materials[object->materialRef];
773                         surf.shader = declManager->FindMaterial( material->name );
774                         surf.id = this->NumSurfaces();
775                         this->AddSurface( surf );
776                 }
777         } else {
778                 // search for material matches
779                 for ( i = 0 ; i < ase->objects.Num() ; i++ ) { 
780                         object = ase->objects[i];
781                         material = ase->materials[object->materialRef];
782                         im1 = declManager->FindMaterial( material->name );
783                         if ( im1->IsDiscrete() ) {
784                                 // flares, autosprites, etc
785                                 j = this->NumSurfaces();
786                         } else {
787                                 for ( j = 0 ; j < this->NumSurfaces() ; j++ ) {
788                                         modelSurf = &this->surfaces[j];
789                                         im2 = modelSurf->shader;
790                                         if ( im1 == im2 ) {
791                                                 // merge this
792                                                 mergeTo[i] = j;
793                                                 break;
794                                         }
795                                 }
796                         }
797                         if ( j == this->NumSurfaces() ) {
798                                 // didn't merge
799                                 mergeTo[i] = j;
800                                 surf.shader = im1;
801                                 surf.id = this->NumSurfaces();
802                                 this->AddSurface( surf );
803                         }
804                 }
805         }
806
807         idVectorSubset<idVec3, 3> vertexSubset;
808         idVectorSubset<idVec2, 2> texCoordSubset;
809
810         // build the surfaces
811         for ( objectNum = 0 ; objectNum < ase->objects.Num() ; objectNum++ ) {
812                 object = ase->objects[objectNum];
813                 mesh = &object->mesh;
814                 material = ase->materials[object->materialRef];
815                 im1 = declManager->FindMaterial( material->name );
816
817                 bool normalsParsed = mesh->normalsParsed;
818
819                 // completely ignore any explict normals on surfaces with a renderbump command
820                 // which will guarantee the best contours and least vertexes.
821                 const char *rb = im1->GetRenderBump();
822                 if ( rb && rb[0] ) {
823                         normalsParsed = false;
824                 }
825
826                 // It seems like the tools our artists are using often generate
827                 // verts and texcoords slightly separated that should be merged
828                 // note that we really should combine the surfaces with common materials
829                 // before doing this operation, because we can miss a slop combination
830                 // if they are in different surfaces
831
832                 vRemap = (int *)R_StaticAlloc( mesh->numVertexes * sizeof( vRemap[0] ) );
833
834                 if ( fastLoad ) {
835                         // renderbump doesn't care about vertex count
836                         for ( j = 0; j < mesh->numVertexes; j++ ) {
837                                 vRemap[j] = j;
838                         }
839                 } else {
840                         float vertexEpsilon = r_slopVertex.GetFloat();
841                         float expand = 2 * 32 * vertexEpsilon;
842                         idVec3 mins, maxs;
843
844                         SIMDProcessor->MinMax( mins, maxs, mesh->vertexes, mesh->numVertexes );
845                         mins -= idVec3( expand, expand, expand );
846                         maxs += idVec3( expand, expand, expand );
847                         vertexSubset.Init( mins, maxs, 32, 1024 );
848                         for ( j = 0; j < mesh->numVertexes; j++ ) {
849                                 vRemap[j] = vertexSubset.FindVector( mesh->vertexes, j, vertexEpsilon );
850                         }
851                 }
852
853                 tvRemap = (int *)R_StaticAlloc( mesh->numTVertexes * sizeof( tvRemap[0] ) );
854
855                 if ( fastLoad ) {
856                         // renderbump doesn't care about vertex count
857                         for ( j = 0; j < mesh->numTVertexes; j++ ) {
858                                 tvRemap[j] = j;
859                         }
860                 } else {
861                         float texCoordEpsilon = r_slopTexCoord.GetFloat();
862                         float expand = 2 * 32 * texCoordEpsilon;
863                         idVec2 mins, maxs;
864
865                         SIMDProcessor->MinMax( mins, maxs, mesh->tvertexes, mesh->numTVertexes );
866                         mins -= idVec2( expand, expand );
867                         maxs += idVec2( expand, expand );
868                         texCoordSubset.Init( mins, maxs, 32, 1024 );
869                         for ( j = 0; j < mesh->numTVertexes; j++ ) {
870                                 tvRemap[j] = texCoordSubset.FindVector( mesh->tvertexes, j, texCoordEpsilon );
871                         }
872                 }
873
874                 // we need to find out how many unique vertex / texcoord combinations
875                 // there are, because ASE tracks them separately but we need them unified
876
877                 // the maximum possible number of combined vertexes is the number of indexes
878                 mvTable = (matchVert_t *)R_ClearedStaticAlloc( mesh->numFaces * 3 * sizeof( mvTable[0] ) );
879
880                 // we will have a hash chain based on the xyz values
881                 mvHash = (matchVert_t **)R_ClearedStaticAlloc( mesh->numVertexes * sizeof( mvHash[0] ) );
882
883                 // allocate triangle surface
884                 tri = R_AllocStaticTriSurf();
885                 tri->numVerts = 0;
886                 tri->numIndexes = 0;
887                 R_AllocStaticTriSurfIndexes( tri, mesh->numFaces * 3 );
888                 tri->generateNormals = !normalsParsed;
889
890                 // init default normal, color and tex coord index
891                 normal.Zero();
892                 color = identityColor;
893                 tv = 0;
894
895                 // find all the unique combinations
896                 float normalEpsilon = 1.0f - r_slopNormal.GetFloat();
897                 for ( j = 0; j < mesh->numFaces; j++ ) {
898                         for ( k = 0; k < 3; k++ ) {
899                                 v = mesh->faces[j].vertexNum[k];
900
901                                 if ( v < 0 || v >= mesh->numVertexes ) {
902                                         common->Error( "ConvertASEToModelSurfaces: bad vertex index in ASE file %s", name.c_str() );
903                                 }
904
905                                 // collapse the position if it was slightly offset 
906                                 v = vRemap[v];
907
908                                 // we may or may not have texcoords to compare
909                                 if ( mesh->numTVFaces == mesh->numFaces && mesh->numTVertexes != 0 ) {
910                                         tv = mesh->faces[j].tVertexNum[k];
911                                         if ( tv < 0 || tv >= mesh->numTVertexes ) {
912                                                 common->Error( "ConvertASEToModelSurfaces: bad tex coord index in ASE file %s", name.c_str() );
913                                         }
914                                         // collapse the tex coord if it was slightly offset
915                                         tv = tvRemap[tv];
916                                 }
917
918                                 // we may or may not have normals to compare
919                                 if ( normalsParsed ) {
920                                         normal = mesh->faces[j].vertexNormals[k];
921                                 }
922
923                                 // we may or may not have colors to compare
924                                 if ( mesh->colorsParsed ) {
925                                         color = mesh->faces[j].vertexColors[k];
926                                 }
927
928                                 // find a matching vert
929                                 for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
930                                         if ( mv->tv != tv ) {
931                                                 continue;
932                                         }
933                                         if ( *(unsigned *)mv->color != *(unsigned *)color ) {
934                                                 continue;
935                                         }
936                                         if ( !normalsParsed ) {
937                                                 // if we are going to create the normals, just
938                                                 // matching texcoords is enough
939                                                 break;
940                                         }
941                                         if ( mv->normal * normal > normalEpsilon ) {
942                                                 break;          // we already have this one
943                                         }
944                                 }
945                                 if ( !mv ) {
946                                         // allocate a new match vert and link to hash chain
947                                         mv = &mvTable[ tri->numVerts ];
948                                         mv->v = v;
949                                         mv->tv = tv;
950                                         mv->normal = normal;
951                                         *(unsigned *)mv->color = *(unsigned *)color;
952                                         mv->next = NULL;
953                                         if ( lastmv ) {
954                                                 lastmv->next = mv;
955                                         } else {
956                                                 mvHash[v] = mv;
957                                         }
958                                         tri->numVerts++;
959                                 }
960
961                                 tri->indexes[tri->numIndexes] = mv - mvTable;
962                                 tri->numIndexes++;
963                         }
964                 }
965
966                 // allocate space for the indexes and copy them
967                 if ( tri->numIndexes > mesh->numFaces * 3 ) {
968                         common->FatalError( "ConvertASEToModelSurfaces: index miscount in ASE file %s", name.c_str() );
969                 }
970                 if ( tri->numVerts > mesh->numFaces * 3 ) {
971                         common->FatalError( "ConvertASEToModelSurfaces: vertex miscount in ASE file %s", name.c_str() );
972                 }
973
974                 // an ASE allows the texture coordinates to be scaled, translated, and rotated
975                 if ( ase->materials.Num() == 0 ) {
976                         uOffset = vOffset = 0.0f;
977                         uTiling = vTiling = 1.0f;
978                         textureSin = 0.0f;
979                         textureCos = 1.0f;
980                 } else {
981                         material = ase->materials[object->materialRef];
982                         uOffset = -material->uOffset;
983                         vOffset = material->vOffset;
984                         uTiling = material->uTiling;
985                         vTiling = material->vTiling;
986                         textureSin = idMath::Sin( material->angle );
987                         textureCos = idMath::Cos( material->angle );
988                 }
989
990                 // now allocate and generate the combined vertexes
991                 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
992                 for ( j = 0; j < tri->numVerts; j++ ) {
993                         mv = &mvTable[j];
994                         tri->verts[ j ].Clear();
995                         tri->verts[ j ].xyz = mesh->vertexes[ mv->v ];
996                         tri->verts[ j ].normal = mv->normal;
997                         *(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
998                         if ( mesh->numTVFaces == mesh->numFaces && mesh->numTVertexes != 0 ) {
999                                 const idVec2 &tv = mesh->tvertexes[ mv->tv ];
1000                                 float u = tv.x * uTiling + uOffset;
1001                                 float v = tv.y * vTiling + vOffset;
1002                                 tri->verts[ j ].st[0] = u * textureCos + v * textureSin;
1003                                 tri->verts[ j ].st[1] = u * -textureSin + v * textureCos;
1004                         }
1005                 }
1006
1007                 R_StaticFree( mvTable );
1008                 R_StaticFree( mvHash );
1009                 R_StaticFree( tvRemap );
1010                 R_StaticFree( vRemap );
1011
1012                 // see if we need to merge with a previous surface of the same material
1013                 modelSurf = &this->surfaces[mergeTo[ objectNum ]];
1014                 srfTriangles_t  *mergeTri = modelSurf->geometry;
1015                 if ( !mergeTri ) {
1016                         modelSurf->geometry = tri;
1017                 } else {
1018                         modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
1019                         R_FreeStaticTriSurf( tri );
1020                         R_FreeStaticTriSurf( mergeTri );
1021                 }
1022         }
1023
1024         return true;
1025 }
1026
1027 /*
1028 =================
1029 idRenderModelStatic::ConvertLWOToModelSurfaces
1030 =================
1031 */
1032 bool idRenderModelStatic::ConvertLWOToModelSurfaces( const struct st_lwObject *lwo ) {
1033         const idMaterial *im1, *im2;
1034         srfTriangles_t  *tri;
1035         lwSurface *             lwoSurf;
1036         int                             numTVertexes;
1037         int                             i, j, k;
1038         int                             v, tv;
1039         idVec3 *                vList;
1040         int *                   vRemap;
1041         idVec2 *                tvList;
1042         int *                   tvRemap;
1043         matchVert_t *   mvTable;        // all of the match verts
1044         matchVert_t **  mvHash;         // points inside mvTable for each xyz index
1045         matchVert_t *   lastmv;
1046         matchVert_t *   mv;
1047         idVec3                  normal;
1048         int *                   mergeTo;
1049         byte                    color[4];
1050         modelSurface_t  surf, *modelSurf;
1051
1052         if ( !lwo ) {
1053                 return false;
1054         }
1055         if ( lwo->surf == NULL ) {
1056                 return false;
1057         }
1058
1059         timeStamp = lwo->timeStamp;
1060
1061         // count the number of surfaces
1062         i = 0;
1063         for ( lwoSurf = lwo->surf; lwoSurf; lwoSurf = lwoSurf->next ) {
1064                 i++;
1065         }
1066
1067         // the modeling programs can save out multiple surfaces with a common
1068         // material, but we would like to merge them together where possible
1069         mergeTo = (int *)_alloca( i * sizeof( mergeTo[0] ) ); 
1070         memset( &surf, 0, sizeof( surf ) );
1071
1072         if ( !r_mergeModelSurfaces.GetBool() ) {
1073                 // don't merge any
1074                 for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
1075                         mergeTo[i] = i;
1076                         surf.shader = declManager->FindMaterial( lwoSurf->name );
1077                         surf.id = this->NumSurfaces();
1078                         this->AddSurface( surf );
1079                 }
1080         } else {
1081                 // search for material matches
1082                 for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
1083                         im1 = declManager->FindMaterial( lwoSurf->name );
1084                         if ( im1->IsDiscrete() ) {
1085                                 // flares, autosprites, etc
1086                                 j = this->NumSurfaces();
1087                         } else {
1088                                 for ( j = 0 ; j < this->NumSurfaces() ; j++ ) {
1089                                         modelSurf = &this->surfaces[j];
1090                                         im2 = modelSurf->shader;
1091                                         if ( im1 == im2 ) {
1092                                                 // merge this
1093                                                 mergeTo[i] = j;
1094                                                 break;
1095                                         }
1096                                 }
1097                         }
1098                         if ( j == this->NumSurfaces() ) {
1099                                 // didn't merge
1100                                 mergeTo[i] = j;
1101                                 surf.shader = im1;
1102                                 surf.id = this->NumSurfaces();
1103                                 this->AddSurface( surf );
1104                         }
1105                 }
1106         }
1107
1108         idVectorSubset<idVec3, 3> vertexSubset;
1109         idVectorSubset<idVec2, 2> texCoordSubset;
1110
1111         // we only ever use the first layer
1112         lwLayer *layer = lwo->layer;
1113
1114         // vertex positions
1115         if ( layer->point.count <= 0 ) {
1116                 common->Warning( "ConvertLWOToModelSurfaces: model \'%s\' has bad or missing vertex data", name.c_str() );
1117                 return false;
1118         }
1119
1120         vList = (idVec3 *)R_StaticAlloc( layer->point.count * sizeof( vList[0] ) );
1121         for ( j = 0; j < layer->point.count; j++ ) {
1122                 vList[j].x = layer->point.pt[j].pos[0];
1123                 vList[j].y = layer->point.pt[j].pos[2];
1124                 vList[j].z = layer->point.pt[j].pos[1];
1125         }
1126
1127         // vertex texture coords
1128         numTVertexes = 0;
1129
1130         if ( layer->nvmaps ) {
1131                 for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
1132                         if ( vm->type == LWID_('T','X','U','V') ) {
1133                                 numTVertexes += vm->nverts;
1134                         }
1135                 }
1136         }
1137
1138         if ( numTVertexes ) {
1139                 tvList = (idVec2 *)Mem_Alloc( numTVertexes * sizeof( tvList[0] ) );
1140                 int offset = 0;
1141                 for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
1142                         if ( vm->type == LWID_('T','X','U','V') ) {
1143                                 vm->offset = offset;
1144                                 for ( k = 0; k < vm->nverts; k++ ) {
1145                                         tvList[k + offset].x = vm->val[k][0];
1146                                         tvList[k + offset].y = 1.0f - vm->val[k][1];    // invert the t
1147                                 }
1148                                 offset += vm->nverts;
1149                         }
1150                 }
1151         } else {
1152                 common->Warning( "ConvertLWOToModelSurfaces: model \'%s\' has bad or missing uv data", name.c_str() );
1153                 numTVertexes = 1;
1154                 tvList = (idVec2 *)Mem_ClearedAlloc( numTVertexes * sizeof( tvList[0] ) );
1155         }
1156
1157         // It seems like the tools our artists are using often generate
1158         // verts and texcoords slightly separated that should be merged
1159         // note that we really should combine the surfaces with common materials
1160         // before doing this operation, because we can miss a slop combination
1161         // if they are in different surfaces
1162
1163         vRemap = (int *)R_StaticAlloc( layer->point.count * sizeof( vRemap[0] ) );
1164
1165         if ( fastLoad ) {
1166                 // renderbump doesn't care about vertex count
1167                 for ( j = 0; j < layer->point.count; j++ ) {
1168                         vRemap[j] = j;
1169                 }
1170         } else {
1171                 float vertexEpsilon = r_slopVertex.GetFloat();
1172                 float expand = 2 * 32 * vertexEpsilon;
1173                 idVec3 mins, maxs;
1174
1175                 SIMDProcessor->MinMax( mins, maxs, vList, layer->point.count );
1176                 mins -= idVec3( expand, expand, expand );
1177                 maxs += idVec3( expand, expand, expand );
1178                 vertexSubset.Init( mins, maxs, 32, 1024 );
1179                 for ( j = 0; j < layer->point.count; j++ ) {
1180                         vRemap[j] = vertexSubset.FindVector( vList, j, vertexEpsilon );
1181                 }
1182         }
1183
1184         tvRemap = (int *)R_StaticAlloc( numTVertexes * sizeof( tvRemap[0] ) );
1185
1186         if ( fastLoad ) {
1187                 // renderbump doesn't care about vertex count
1188                 for ( j = 0; j < numTVertexes; j++ ) {
1189                         tvRemap[j] = j;
1190                 }
1191         } else {
1192                 float texCoordEpsilon = r_slopTexCoord.GetFloat();
1193                 float expand = 2 * 32 * texCoordEpsilon;
1194                 idVec2 mins, maxs;
1195
1196                 SIMDProcessor->MinMax( mins, maxs, tvList, numTVertexes );
1197                 mins -= idVec2( expand, expand );
1198                 maxs += idVec2( expand, expand );
1199                 texCoordSubset.Init( mins, maxs, 32, 1024 );
1200                 for ( j = 0; j < numTVertexes; j++ ) {
1201                         tvRemap[j] = texCoordSubset.FindVector( tvList, j, texCoordEpsilon );
1202                 }
1203         }
1204
1205         // build the surfaces
1206         for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
1207                 im1 = declManager->FindMaterial( lwoSurf->name );
1208
1209                 bool normalsParsed = true;
1210
1211                 // completely ignore any explict normals on surfaces with a renderbump command
1212                 // which will guarantee the best contours and least vertexes.
1213                 const char *rb = im1->GetRenderBump();
1214                 if ( rb && rb[0] ) {
1215                         normalsParsed = false;
1216                 }
1217
1218                 // we need to find out how many unique vertex / texcoord combinations there are
1219
1220                 // the maximum possible number of combined vertexes is the number of indexes
1221                 mvTable = (matchVert_t *)R_ClearedStaticAlloc( layer->polygon.count * 3 * sizeof( mvTable[0] ) );
1222
1223                 // we will have a hash chain based on the xyz values
1224                 mvHash = (matchVert_t **)R_ClearedStaticAlloc( layer->point.count * sizeof( mvHash[0] ) );
1225
1226                 // allocate triangle surface
1227                 tri = R_AllocStaticTriSurf();
1228                 tri->numVerts = 0;
1229                 tri->numIndexes = 0;
1230                 R_AllocStaticTriSurfIndexes( tri, layer->polygon.count * 3 );
1231                 tri->generateNormals = !normalsParsed;
1232
1233                 // find all the unique combinations
1234                 float   normalEpsilon;
1235                 if ( fastLoad ) {
1236                         normalEpsilon = 1.0f;   // don't merge unless completely exact
1237                 } else {
1238                         normalEpsilon = 1.0f - r_slopNormal.GetFloat();
1239                 }
1240                 for ( j = 0; j < layer->polygon.count; j++ ) {
1241                         lwPolygon *poly = &layer->polygon.pol[j];
1242
1243                         if ( poly->surf != lwoSurf ) {
1244                                 continue;
1245                         }
1246
1247                         if ( poly->nverts != 3 ) {
1248                                 common->Warning( "ConvertLWOToModelSurfaces: model %s has too many verts for a poly! Make sure you triplet it down", name.c_str() );
1249                                 continue;
1250                         }
1251
1252                         for ( k = 0; k < 3; k++ ) {
1253
1254                                 v = vRemap[poly->v[k].index];
1255
1256                                 normal.x = poly->v[k].norm[0];
1257                                 normal.y = poly->v[k].norm[2];
1258                                 normal.z = poly->v[k].norm[1];
1259
1260                                 // LWO models aren't all that pretty when it comes down to the floating point values they store
1261                                 normal.FixDegenerateNormal();
1262
1263                                 tv = 0;
1264
1265                                 color[0] = lwoSurf->color.rgb[0] * 255;
1266                                 color[1] = lwoSurf->color.rgb[1] * 255;
1267                                 color[2] = lwoSurf->color.rgb[2] * 255;
1268                                 color[3] = 255;
1269
1270                                 // first set attributes from the vertex
1271                                 lwPoint *pt = &layer->point.pt[poly->v[k].index];
1272                                 int nvm;
1273                                 for ( nvm = 0; nvm < pt->nvmaps; nvm++ ) {
1274                                         lwVMapPt *vm = &pt->vm[nvm];
1275
1276                                         if ( vm->vmap->type == LWID_('T','X','U','V') ) {
1277                                                 tv = tvRemap[vm->index + vm->vmap->offset];
1278                                         }
1279                                         if ( vm->vmap->type == LWID_('R','G','B','A') ) {
1280                                                 for ( int chan = 0; chan < 4; chan++ ) {
1281                                                         color[chan] = 255 * vm->vmap->val[vm->index][chan];
1282                                                 }
1283                                         }
1284                                 }
1285
1286                                 // then override with polygon attributes
1287                                 for ( nvm = 0; nvm < poly->v[k].nvmaps; nvm++ ) {
1288                                         lwVMapPt *vm = &poly->v[k].vm[nvm];
1289
1290                                         if ( vm->vmap->type == LWID_('T','X','U','V') ) {
1291                                                 tv = tvRemap[vm->index + vm->vmap->offset];
1292                                         }
1293                                         if ( vm->vmap->type == LWID_('R','G','B','A') ) {
1294                                                 for ( int chan = 0; chan < 4; chan++ ) {
1295                                                         color[chan] = 255 * vm->vmap->val[vm->index][chan];
1296                                                 }
1297                                         }
1298                                 }
1299
1300                                 // find a matching vert
1301                                 for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
1302                                         if ( mv->tv != tv ) {
1303                                                 continue;
1304                                         }
1305                                         if ( *(unsigned *)mv->color != *(unsigned *)color ) {
1306                                                 continue;
1307                                         }
1308                                         if ( !normalsParsed ) {
1309                                                 // if we are going to create the normals, just
1310                                                 // matching texcoords is enough
1311                                                 break;
1312                                         }
1313                                         if ( mv->normal * normal > normalEpsilon ) {
1314                                                 break;          // we already have this one
1315                                         }
1316                                 }
1317                                 if ( !mv ) {
1318                                         // allocate a new match vert and link to hash chain
1319                                         mv = &mvTable[ tri->numVerts ];
1320                                         mv->v = v;
1321                                         mv->tv = tv;
1322                                         mv->normal = normal;
1323                                         *(unsigned *)mv->color = *(unsigned *)color;
1324                                         mv->next = NULL;
1325                                         if ( lastmv ) {
1326                                                 lastmv->next = mv;
1327                                         } else {
1328                                                 mvHash[v] = mv;
1329                                         }
1330                                         tri->numVerts++;
1331                                 }
1332
1333                                 tri->indexes[tri->numIndexes] = mv - mvTable;
1334                                 tri->numIndexes++;
1335                         }
1336                 }
1337
1338                 // allocate space for the indexes and copy them
1339                 if ( tri->numIndexes > layer->polygon.count * 3 ) {
1340                         common->FatalError( "ConvertLWOToModelSurfaces: index miscount in LWO file %s", name.c_str() );
1341                 }
1342                 if ( tri->numVerts > layer->polygon.count * 3 ) {
1343                         common->FatalError( "ConvertLWOToModelSurfaces: vertex miscount in LWO file %s", name.c_str() );
1344                 }
1345
1346                 // now allocate and generate the combined vertexes
1347                 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
1348                 for ( j = 0; j < tri->numVerts; j++ ) {
1349                         mv = &mvTable[j];
1350                         tri->verts[ j ].Clear();
1351                         tri->verts[ j ].xyz = vList[ mv->v ];
1352                         tri->verts[ j ].st = tvList[ mv->tv ];
1353                         tri->verts[ j ].normal = mv->normal;
1354                         *(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
1355                 }
1356
1357                 R_StaticFree( mvTable );
1358                 R_StaticFree( mvHash );
1359
1360                 // see if we need to merge with a previous surface of the same material
1361                 modelSurf = &this->surfaces[mergeTo[ i ]];
1362                 srfTriangles_t  *mergeTri = modelSurf->geometry;
1363                 if ( !mergeTri ) {
1364                         modelSurf->geometry = tri;
1365                 } else {
1366                         modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
1367                         R_FreeStaticTriSurf( tri );
1368                         R_FreeStaticTriSurf( mergeTri );
1369                 }
1370         }
1371
1372         R_StaticFree( tvRemap );
1373         R_StaticFree( vRemap );
1374         R_StaticFree( tvList );
1375         R_StaticFree( vList );
1376
1377         return true;
1378 }
1379
1380 /*
1381 =================
1382 idRenderModelStatic::ConvertLWOToASE
1383 =================
1384 */
1385 struct aseModel_s *idRenderModelStatic::ConvertLWOToASE( const struct st_lwObject *obj, const char *fileName ) {
1386         int j, k;
1387         aseModel_t *ase;
1388
1389         if ( !obj ) {
1390                 return NULL;
1391         }
1392
1393         // NOTE: using new operator because aseModel_t contains idList class objects
1394         ase = new aseModel_t;
1395         ase->timeStamp = obj->timeStamp;
1396         ase->objects.Resize( obj->nlayers, obj->nlayers );
1397
1398         int materialRef = 0;
1399
1400         for ( lwSurface *surf = obj->surf; surf; surf = surf->next ) {
1401
1402                 aseMaterial_t *mat = (aseMaterial_t *)Mem_ClearedAlloc( sizeof( *mat ) );
1403                 strcpy( mat->name, surf->name );
1404                 mat->uTiling = mat->vTiling = 1;
1405                 mat->angle = mat->uOffset = mat->vOffset = 0;
1406                 ase->materials.Append( mat );
1407
1408                 lwLayer *layer = obj->layer;
1409
1410                 aseObject_t *object = (aseObject_t *)Mem_ClearedAlloc( sizeof( *object ) );
1411                 object->materialRef = materialRef++;
1412
1413                 aseMesh_t *mesh = &object->mesh;
1414                 ase->objects.Append( object );
1415
1416                 mesh->numFaces = layer->polygon.count;
1417                 mesh->numTVFaces = mesh->numFaces;
1418                 mesh->faces = (aseFace_t *)Mem_Alloc( mesh->numFaces  * sizeof( mesh->faces[0] ) );
1419
1420                 mesh->numVertexes = layer->point.count;
1421                 mesh->vertexes = (idVec3 *)Mem_Alloc( mesh->numVertexes * sizeof( mesh->vertexes[0] ) );
1422
1423                 // vertex positions
1424                 if ( layer->point.count <= 0 ) {
1425                         common->Warning( "ConvertLWOToASE: model \'%s\' has bad or missing vertex data", name.c_str() );
1426                 }
1427
1428                 for ( j = 0; j < layer->point.count; j++ ) {
1429                         mesh->vertexes[j].x = layer->point.pt[j].pos[0];
1430                         mesh->vertexes[j].y = layer->point.pt[j].pos[2];
1431                         mesh->vertexes[j].z = layer->point.pt[j].pos[1];
1432                 }
1433
1434                 // vertex texture coords
1435                 mesh->numTVertexes = 0;
1436
1437                 if ( layer->nvmaps ) {
1438                         for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
1439                                 if ( vm->type == LWID_('T','X','U','V') ) {
1440                                         mesh->numTVertexes += vm->nverts;
1441                                 }
1442                         }
1443                 }
1444
1445                 if ( mesh->numTVertexes ) {
1446                         mesh->tvertexes = (idVec2 *)Mem_Alloc( mesh->numTVertexes * sizeof( mesh->tvertexes[0] ) );
1447                         int offset = 0;
1448                         for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
1449                                 if ( vm->type == LWID_('T','X','U','V') ) {
1450                                         vm->offset = offset;
1451                                         for ( k = 0; k < vm->nverts; k++ ) {
1452                                                 mesh->tvertexes[k + offset].x = vm->val[k][0];
1453                                                 mesh->tvertexes[k + offset].y = 1.0f - vm->val[k][1];   // invert the t
1454                                         }
1455                                         offset += vm->nverts;
1456                                 }
1457                         }
1458                 } else {
1459                         common->Warning( "ConvertLWOToASE: model \'%s\' has bad or missing uv data", fileName );
1460                         mesh->numTVertexes = 1;
1461                         mesh->tvertexes = (idVec2 *)Mem_ClearedAlloc( mesh->numTVertexes * sizeof( mesh->tvertexes[0] ) );
1462                 }
1463
1464                 mesh->normalsParsed = true;
1465                 mesh->colorsParsed = true;      // because we are falling back to the surface color
1466
1467                 // triangles
1468                 int faceIndex = 0;
1469                 for ( j = 0; j < layer->polygon.count; j++ ) {
1470                         lwPolygon *poly = &layer->polygon.pol[j];
1471
1472                         if ( poly->surf != surf ) {
1473                                 continue;
1474                         }
1475
1476                         if ( poly->nverts != 3 ) {
1477                                 common->Warning( "ConvertLWOToASE: model %s has too many verts for a poly! Make sure you triplet it down", fileName );
1478                                 continue;
1479                         }
1480         
1481                         mesh->faces[faceIndex].faceNormal.x = poly->norm[0];
1482                         mesh->faces[faceIndex].faceNormal.y = poly->norm[2];
1483                         mesh->faces[faceIndex].faceNormal.z = poly->norm[1];
1484
1485                         for ( k = 0; k < 3; k++ ) {
1486
1487                                 mesh->faces[faceIndex].vertexNum[k] = poly->v[k].index;
1488
1489                                 mesh->faces[faceIndex].vertexNormals[k].x = poly->v[k].norm[0];
1490                                 mesh->faces[faceIndex].vertexNormals[k].y = poly->v[k].norm[2];
1491                                 mesh->faces[faceIndex].vertexNormals[k].z = poly->v[k].norm[1];
1492
1493                                 // complete fallbacks
1494                                 mesh->faces[faceIndex].tVertexNum[k] = 0;
1495
1496                                 mesh->faces[faceIndex].vertexColors[k][0] = surf->color.rgb[0] * 255;
1497                                 mesh->faces[faceIndex].vertexColors[k][1] = surf->color.rgb[1] * 255;
1498                                 mesh->faces[faceIndex].vertexColors[k][2] = surf->color.rgb[2] * 255;
1499                                 mesh->faces[faceIndex].vertexColors[k][3] = 255;
1500
1501                                 // first set attributes from the vertex
1502                                 lwPoint *pt = &layer->point.pt[poly->v[k].index];
1503                                 int nvm;
1504                                 for ( nvm = 0; nvm < pt->nvmaps; nvm++ ) {
1505                                         lwVMapPt *vm = &pt->vm[nvm];
1506
1507                                         if ( vm->vmap->type == LWID_('T','X','U','V') ) {
1508                                                 mesh->faces[faceIndex].tVertexNum[k] = vm->index + vm->vmap->offset;
1509                                         }
1510                                         if ( vm->vmap->type == LWID_('R','G','B','A') ) {
1511                                                 for ( int chan = 0; chan < 4; chan++ ) {
1512                                                         mesh->faces[faceIndex].vertexColors[k][chan] = 255 * vm->vmap->val[vm->index][chan];
1513                                                 }
1514                                         }
1515                                 }
1516
1517                                 // then override with polygon attributes
1518                                 for ( nvm = 0; nvm < poly->v[k].nvmaps; nvm++ ) {
1519                                         lwVMapPt *vm = &poly->v[k].vm[nvm];
1520
1521                                         if ( vm->vmap->type == LWID_('T','X','U','V') ) {
1522                                                 mesh->faces[faceIndex].tVertexNum[k] = vm->index + vm->vmap->offset;
1523                                         }
1524                                         if ( vm->vmap->type == LWID_('R','G','B','A') ) {
1525                                                 for ( int chan = 0; chan < 4; chan++ ) {
1526                                                         mesh->faces[faceIndex].vertexColors[k][chan] = 255 * vm->vmap->val[vm->index][chan];
1527                                                 }
1528                                         }
1529                                 }
1530                         }
1531
1532                         faceIndex++;
1533                 }
1534
1535                 mesh->numFaces = faceIndex;
1536                 mesh->numTVFaces = faceIndex;
1537
1538                 aseFace_t *newFaces = ( aseFace_t* )Mem_Alloc( mesh->numFaces * sizeof ( mesh->faces[0] ) );
1539                 memcpy( newFaces, mesh->faces, sizeof( mesh->faces[0] ) * mesh->numFaces );
1540                 Mem_Free( mesh->faces );
1541                 mesh->faces = newFaces;
1542         }
1543
1544         return ase;
1545 }
1546
1547 /*
1548 =================
1549 idRenderModelStatic::ConvertMAToModelSurfaces
1550 =================
1551 */
1552 bool idRenderModelStatic::ConvertMAToModelSurfaces (const struct maModel_s *ma ) {
1553
1554         maObject_t *    object;
1555         maMesh_t *              mesh;
1556         maMaterial_t *  material;
1557         
1558         const idMaterial *im1, *im2;
1559         srfTriangles_t *tri;
1560         int                             objectNum;
1561         int                             i, j, k;
1562         int                             v, tv;
1563         int *                   vRemap;
1564         int *                   tvRemap;
1565         matchVert_t *   mvTable;        // all of the match verts
1566         matchVert_t **  mvHash;         // points inside mvTable for each xyz index
1567         matchVert_t *   lastmv;
1568         matchVert_t *   mv;
1569         idVec3                  normal;
1570         float                   uOffset, vOffset, textureSin, textureCos;
1571         float                   uTiling, vTiling;
1572         int *                   mergeTo;
1573         byte *                  color;
1574         static byte     identityColor[4] = { 255, 255, 255, 255 };
1575         modelSurface_t  surf, *modelSurf;
1576
1577         if ( !ma ) {
1578                 return false;
1579         }
1580         if ( ma->objects.Num() < 1 ) {
1581                 return false;
1582         }
1583
1584         timeStamp = ma->timeStamp;
1585
1586         // the modeling programs can save out multiple surfaces with a common
1587         // material, but we would like to mege them together where possible
1588         // meaning that this->NumSurfaces() <= ma->objects.currentElements
1589         mergeTo = (int *)_alloca( ma->objects.Num() * sizeof( *mergeTo ) ); 
1590
1591         surf.geometry = NULL;
1592         if ( ma->materials.Num() == 0 ) {
1593                 // if we don't have any materials, dump everything into a single surface
1594                 surf.shader = tr.defaultMaterial;
1595                 surf.id = 0;
1596                 this->AddSurface( surf );
1597                 for ( i = 0 ; i < ma->objects.Num() ; i++ ) { 
1598                         mergeTo[i] = 0;
1599                 }
1600         } else if ( !r_mergeModelSurfaces.GetBool() ) {
1601                 // don't merge any
1602                 for ( i = 0 ; i < ma->objects.Num() ; i++ ) { 
1603                         mergeTo[i] = i;
1604                         object = ma->objects[i];
1605                         if(object->materialRef >= 0) {
1606                                 material = ma->materials[object->materialRef];
1607                                 surf.shader = declManager->FindMaterial( material->name );
1608                         } else {
1609                                 surf.shader = tr.defaultMaterial;
1610                         }
1611                         surf.id = this->NumSurfaces();
1612                         this->AddSurface( surf );
1613                 }
1614         } else {
1615                 // search for material matches
1616                 for ( i = 0 ; i < ma->objects.Num() ; i++ ) { 
1617                         object = ma->objects[i];
1618                         if(object->materialRef >= 0) {
1619                                 material = ma->materials[object->materialRef];
1620                                 im1 = declManager->FindMaterial( material->name );
1621                         } else {
1622                                 im1 = tr.defaultMaterial;
1623                         }
1624                         if ( im1->IsDiscrete() ) {
1625                                 // flares, autosprites, etc
1626                                 j = this->NumSurfaces();
1627                         } else {
1628                                 for ( j = 0 ; j < this->NumSurfaces() ; j++ ) {
1629                                         modelSurf = &this->surfaces[j];
1630                                         im2 = modelSurf->shader;
1631                                         if ( im1 == im2 ) {
1632                                                 // merge this
1633                                                 mergeTo[i] = j;
1634                                                 break;
1635                                         }
1636                                 }
1637                         }
1638                         if ( j == this->NumSurfaces() ) {
1639                                 // didn't merge
1640                                 mergeTo[i] = j;
1641                                 surf.shader = im1;
1642                                 surf.id = this->NumSurfaces();
1643                                 this->AddSurface( surf );
1644                         }
1645                 }
1646         }
1647
1648         idVectorSubset<idVec3, 3> vertexSubset;
1649         idVectorSubset<idVec2, 2> texCoordSubset;
1650
1651         // build the surfaces
1652         for ( objectNum = 0 ; objectNum < ma->objects.Num() ; objectNum++ ) {
1653                 object = ma->objects[objectNum];
1654                 mesh = &object->mesh;
1655                 if(object->materialRef >= 0) {
1656                         material = ma->materials[object->materialRef];
1657                         im1 = declManager->FindMaterial( material->name );
1658                 } else {
1659                         im1 = tr.defaultMaterial;
1660                 }
1661
1662                 bool normalsParsed = mesh->normalsParsed;
1663                 
1664                 // completely ignore any explict normals on surfaces with a renderbump command
1665                 // which will guarantee the best contours and least vertexes.
1666                 const char *rb = im1->GetRenderBump();
1667                 if ( rb && rb[0] ) {
1668                         normalsParsed = false;
1669                 }
1670
1671                 // It seems like the tools our artists are using often generate
1672                 // verts and texcoords slightly separated that should be merged
1673                 // note that we really should combine the surfaces with common materials
1674                 // before doing this operation, because we can miss a slop combination
1675                 // if they are in different surfaces
1676
1677                 vRemap = (int *)R_StaticAlloc( mesh->numVertexes * sizeof( vRemap[0] ) );
1678
1679                 if ( fastLoad ) {
1680                         // renderbump doesn't care about vertex count
1681                         for ( j = 0; j < mesh->numVertexes; j++ ) {
1682                                 vRemap[j] = j;
1683                         }
1684                 } else {
1685                         float vertexEpsilon = r_slopVertex.GetFloat();
1686                         float expand = 2 * 32 * vertexEpsilon;
1687                         idVec3 mins, maxs;
1688
1689                         SIMDProcessor->MinMax( mins, maxs, mesh->vertexes, mesh->numVertexes );
1690                         mins -= idVec3( expand, expand, expand );
1691                         maxs += idVec3( expand, expand, expand );
1692                         vertexSubset.Init( mins, maxs, 32, 1024 );
1693                         for ( j = 0; j < mesh->numVertexes; j++ ) {
1694                                 vRemap[j] = vertexSubset.FindVector( mesh->vertexes, j, vertexEpsilon );
1695                         }
1696                 }
1697
1698                 tvRemap = (int *)R_StaticAlloc( mesh->numTVertexes * sizeof( tvRemap[0] ) );
1699
1700                 if ( fastLoad ) {
1701                         // renderbump doesn't care about vertex count
1702                         for ( j = 0; j < mesh->numTVertexes; j++ ) {
1703                                 tvRemap[j] = j;
1704                         }
1705                 } else {
1706                         float texCoordEpsilon = r_slopTexCoord.GetFloat();
1707                         float expand = 2 * 32 * texCoordEpsilon;
1708                         idVec2 mins, maxs;
1709
1710                         SIMDProcessor->MinMax( mins, maxs, mesh->tvertexes, mesh->numTVertexes );
1711                         mins -= idVec2( expand, expand );
1712                         maxs += idVec2( expand, expand );
1713                         texCoordSubset.Init( mins, maxs, 32, 1024 );
1714                         for ( j = 0; j < mesh->numTVertexes; j++ ) {
1715                                 tvRemap[j] = texCoordSubset.FindVector( mesh->tvertexes, j, texCoordEpsilon );
1716                         }
1717                 }
1718
1719                 // we need to find out how many unique vertex / texcoord / color combinations
1720                 // there are, because MA tracks them separately but we need them unified
1721
1722                 // the maximum possible number of combined vertexes is the number of indexes
1723                 mvTable = (matchVert_t *)R_ClearedStaticAlloc( mesh->numFaces * 3 * sizeof( mvTable[0] ) );
1724
1725                 // we will have a hash chain based on the xyz values
1726                 mvHash = (matchVert_t **)R_ClearedStaticAlloc( mesh->numVertexes * sizeof( mvHash[0] ) );
1727
1728                 // allocate triangle surface
1729                 tri = R_AllocStaticTriSurf();
1730                 tri->numVerts = 0;
1731                 tri->numIndexes = 0;
1732                 R_AllocStaticTriSurfIndexes( tri, mesh->numFaces * 3 );
1733                 tri->generateNormals = !normalsParsed;
1734
1735                 // init default normal, color and tex coord index
1736                 normal.Zero();
1737                 color = identityColor;
1738                 tv = 0;
1739
1740                 // find all the unique combinations
1741                 float normalEpsilon = 1.0f - r_slopNormal.GetFloat();
1742                 for ( j = 0; j < mesh->numFaces; j++ ) {
1743                         for ( k = 0; k < 3; k++ ) {
1744                                 v = mesh->faces[j].vertexNum[k];
1745
1746                                 if ( v < 0 || v >= mesh->numVertexes ) {
1747                                         common->Error( "ConvertMAToModelSurfaces: bad vertex index in MA file %s", name.c_str() );
1748                                 }
1749
1750                                 // collapse the position if it was slightly offset 
1751                                 v = vRemap[v];
1752
1753                                 // we may or may not have texcoords to compare
1754                                 if ( mesh->numTVertexes != 0 ) {
1755                                         tv = mesh->faces[j].tVertexNum[k];
1756                                         if ( tv < 0 || tv >= mesh->numTVertexes ) {
1757                                                 common->Error( "ConvertMAToModelSurfaces: bad tex coord index in MA file %s", name.c_str() );
1758                                         }
1759                                         // collapse the tex coord if it was slightly offset
1760                                         tv = tvRemap[tv];
1761                                 }
1762
1763                                 // we may or may not have normals to compare
1764                                 if ( normalsParsed ) {
1765                                         normal = mesh->faces[j].vertexNormals[k];
1766                                 }
1767
1768                                 //BSM: Todo: Fix the vertex colors
1769                                 // we may or may not have colors to compare
1770                                 if ( mesh->faces[j].vertexColors[k] != -1 && mesh->faces[j].vertexColors[k] != -999 ) {
1771
1772                                         color = &mesh->colors[mesh->faces[j].vertexColors[k]*4];
1773                                 }
1774
1775                                 // find a matching vert
1776                                 for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
1777                                         if ( mv->tv != tv ) {
1778                                                 continue;
1779                                         }
1780                                         if ( *(unsigned *)mv->color != *(unsigned *)color ) {
1781                                                 continue;
1782                                         }
1783                                         if ( !normalsParsed ) {
1784                                                 // if we are going to create the normals, just
1785                                                 // matching texcoords is enough
1786                                                 break;
1787                                         }
1788                                         if ( mv->normal * normal > normalEpsilon ) {
1789                                                 break;          // we already have this one
1790                                         }
1791                                 }
1792                                 if ( !mv ) {
1793                                         // allocate a new match vert and link to hash chain
1794                                         mv = &mvTable[ tri->numVerts ];
1795                                         mv->v = v;
1796                                         mv->tv = tv;
1797                                         mv->normal = normal;
1798                                         *(unsigned *)mv->color = *(unsigned *)color;
1799                                         mv->next = NULL;
1800                                         if ( lastmv ) {
1801                                                 lastmv->next = mv;
1802                                         } else {
1803                                                 mvHash[v] = mv;
1804                                         }
1805                                         tri->numVerts++;
1806                                 }
1807
1808                                 tri->indexes[tri->numIndexes] = mv - mvTable;
1809                                 tri->numIndexes++;
1810                         }
1811                 }
1812
1813                 // allocate space for the indexes and copy them
1814                 if ( tri->numIndexes > mesh->numFaces * 3 ) {
1815                         common->FatalError( "ConvertMAToModelSurfaces: index miscount in MA file %s", name.c_str() );
1816                 }
1817                 if ( tri->numVerts > mesh->numFaces * 3 ) {
1818                         common->FatalError( "ConvertMAToModelSurfaces: vertex miscount in MA file %s", name.c_str() );
1819                 }
1820
1821                 // an MA allows the texture coordinates to be scaled, translated, and rotated
1822                 //BSM: Todo: Does Maya support this and if so how
1823                 //if ( ase->materials.Num() == 0 ) {
1824                         uOffset = vOffset = 0.0f;
1825                         uTiling = vTiling = 1.0f;
1826                         textureSin = 0.0f;
1827                         textureCos = 1.0f;
1828                 //} else {
1829                 //      material = ase->materials[object->materialRef];
1830                 //      uOffset = -material->uOffset;
1831                 //      vOffset = material->vOffset;
1832                 //      uTiling = material->uTiling;
1833                 //      vTiling = material->vTiling;
1834                 //      textureSin = idMath::Sin( material->angle );
1835                 //      textureCos = idMath::Cos( material->angle );
1836                 //}
1837
1838                 // now allocate and generate the combined vertexes
1839                 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
1840                 for ( j = 0; j < tri->numVerts; j++ ) {
1841                         mv = &mvTable[j];
1842                         tri->verts[ j ].Clear();
1843                         tri->verts[ j ].xyz = mesh->vertexes[ mv->v ];
1844                         tri->verts[ j ].normal = mv->normal;
1845                         *(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
1846                         if ( mesh->numTVertexes != 0 ) {
1847                                 const idVec2 &tv = mesh->tvertexes[ mv->tv ];
1848                                 float u = tv.x * uTiling + uOffset;
1849                                 float v = tv.y * vTiling + vOffset;
1850                                 tri->verts[ j ].st[0] = u * textureCos + v * textureSin;
1851                                 tri->verts[ j ].st[1] = u * -textureSin + v * textureCos;
1852                         }
1853                 }
1854
1855                 R_StaticFree( mvTable );
1856                 R_StaticFree( mvHash );
1857                 R_StaticFree( tvRemap );
1858                 R_StaticFree( vRemap );
1859
1860                 // see if we need to merge with a previous surface of the same material
1861                 modelSurf = &this->surfaces[mergeTo[ objectNum ]];
1862                 srfTriangles_t  *mergeTri = modelSurf->geometry;
1863                 if ( !mergeTri ) {
1864                         modelSurf->geometry = tri;
1865                 } else {
1866                         modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
1867                         R_FreeStaticTriSurf( tri );
1868                         R_FreeStaticTriSurf( mergeTri );
1869                 }
1870         }
1871
1872         return true;
1873 }
1874
1875 /*
1876 =================
1877 idRenderModelStatic::LoadASE
1878 =================
1879 */
1880 bool idRenderModelStatic::LoadASE( const char *fileName ) {
1881         aseModel_t *ase;
1882
1883         ase = ASE_Load( fileName );
1884         if ( ase == NULL ) {
1885                 return false;
1886         }
1887
1888         ConvertASEToModelSurfaces( ase );
1889
1890         ASE_Free( ase );
1891
1892         return true;
1893 }
1894
1895 /*
1896 =================
1897 idRenderModelStatic::LoadLWO
1898 =================
1899 */
1900 bool idRenderModelStatic::LoadLWO( const char *fileName ) {
1901         unsigned int failID;
1902         int failPos;
1903         lwObject *lwo;
1904
1905         lwo = lwGetObject( fileName, &failID, &failPos );
1906         if ( lwo == NULL ) {
1907                 return false;
1908         }
1909
1910         ConvertLWOToModelSurfaces( lwo );
1911
1912         lwFreeObject( lwo );
1913
1914         return true;
1915 }
1916
1917 /*
1918 =================
1919 idRenderModelStatic::LoadMA
1920 =================
1921 */
1922 bool idRenderModelStatic::LoadMA( const char *fileName ) {
1923         maModel_t *ma;
1924
1925         ma = MA_Load( fileName );
1926         if ( ma == NULL ) {
1927                 return false;
1928         }
1929
1930         ConvertMAToModelSurfaces( ma );
1931
1932         MA_Free( ma );
1933
1934         return true;
1935 }
1936
1937 /*
1938 =================
1939 idRenderModelStatic::LoadFLT
1940
1941 USGS height map data for megaTexture experiments
1942 =================
1943 */
1944 bool idRenderModelStatic::LoadFLT( const char *fileName ) {
1945         float   *data;
1946         int             len;
1947
1948         len = fileSystem->ReadFile( fileName, (void **)&data );
1949         if ( len <= 0 ) {
1950                 return false;
1951         }
1952         int     size = sqrt( len / 4.0f );
1953
1954         // bound the altitudes
1955         float min = 9999999;
1956         float max = -9999999;
1957         for ( int i = 0 ; i < len/4 ; i++ ) {
1958         data[i] = BigFloat( data[i] );
1959         if ( data[i] == -9999 ) {
1960                 data[i] = 0;            // unscanned areas
1961         }
1962
1963                 if ( data[i] < min ) {
1964                         min = data[i];
1965                 }
1966                 if ( data[i] > max ) {
1967                         max = data[i];
1968                 }
1969         }
1970 #if 1
1971         // write out a gray scale height map
1972         byte    *image = (byte *)R_StaticAlloc( len );
1973         byte    *image_p = image;
1974         for ( int i = 0 ; i < len/4 ; i++ ) {
1975                 float v = ( data[i] - min ) / ( max - min );
1976                 image_p[0] =
1977                 image_p[1] =
1978                 image_p[2] = v * 255;
1979                 image_p[3] = 255;
1980                 image_p += 4;
1981         }
1982         idStr   tgaName = fileName;
1983         tgaName.StripFileExtension();
1984         tgaName += ".tga";
1985         R_WriteTGA( tgaName.c_str(), image, size, size, false );
1986         R_StaticFree( image );
1987 //return false;
1988 #endif
1989
1990         // find the island above sea level
1991         int     minX, maxX, minY, maxY;
1992         {
1993                 int     i;      
1994                 for ( minX = 0 ; minX < size ; minX++ ) {
1995                         for ( i = 0 ; i < size ; i++ ) {
1996                                 if ( data[i*size + minX] > 1.0 ) {
1997                                         break;
1998                                 }
1999                         }
2000                         if ( i != size ) {
2001                                 break;
2002                         }
2003                 }
2004
2005                 for ( maxX = size-1 ; maxX > 0 ; maxX-- ) {
2006                         for ( i = 0 ; i < size ; i++ ) {
2007                                 if ( data[i*size + maxX] > 1.0 ) {
2008                                         break;
2009                                 }
2010                         }
2011                         if ( i != size ) {
2012                                 break;
2013                         }
2014                 }
2015
2016                 for ( minY = 0 ; minY < size ; minY++ ) {
2017                         for ( i = 0 ; i < size ; i++ ) {
2018                                 if ( data[minY*size + i] > 1.0 ) {
2019                                         break;
2020                                 }
2021                         }
2022                         if ( i != size ) {
2023                                 break;
2024                         }
2025                 }
2026
2027                 for ( maxY = size-1 ; maxY < size ; maxY-- ) {
2028                         for ( i = 0 ; i < size ; i++ ) {
2029                                 if ( data[maxY*size + i] > 1.0 ) {
2030                                         break;
2031                                 }
2032                         }
2033                         if ( i != size ) {
2034                                 break;
2035                         }
2036                 }
2037         }
2038
2039         int     width = maxX - minX + 1;
2040         int height = maxY - minY + 1;
2041
2042 //width /= 2;
2043         // allocate triangle surface
2044         srfTriangles_t *tri = R_AllocStaticTriSurf();
2045         tri->numVerts = width * height;
2046         tri->numIndexes = (width-1) * (height-1) * 6;
2047
2048         fastLoad = true;                // don't do all the sil processing
2049
2050         R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
2051         R_AllocStaticTriSurfVerts( tri, tri->numVerts );
2052
2053         for ( int i = 0 ; i < height ; i++ ) {
2054                 for ( int j = 0; j < width ; j++ ) {
2055                         int             v = i * width + j;
2056                         tri->verts[ v ].Clear();
2057                         tri->verts[ v ].xyz[0] = j * 10;        // each sample is 10 meters
2058                         tri->verts[ v ].xyz[1] = -i * 10;
2059                         tri->verts[ v ].xyz[2] = data[(minY+i)*size+minX+j];    // height is in meters
2060                         tri->verts[ v ].st[0] = (float) j / (width-1);
2061                         tri->verts[ v ].st[1] = 1.0 - ( (float) i / (height-1) );
2062                 }
2063         }
2064
2065         for ( int i = 0 ; i < height-1 ; i++ ) {
2066                 for ( int j = 0; j < width-1 ; j++ ) {
2067                         int     v = ( i * (width-1) + j ) * 6;
2068 #if 0
2069                         tri->indexes[ v + 0 ] = i * width + j;
2070                         tri->indexes[ v + 1 ] = (i+1) * width + j;
2071                         tri->indexes[ v + 2 ] = (i+1) * width + j + 1;
2072                         tri->indexes[ v + 3 ] = i * width + j;
2073                         tri->indexes[ v + 4 ] = (i+1) * width + j + 1;
2074                         tri->indexes[ v + 5 ] = i * width + j + 1;
2075 #else
2076                         tri->indexes[ v + 0 ] = i * width + j;
2077                         tri->indexes[ v + 1 ] = i * width + j + 1;
2078                         tri->indexes[ v + 2 ] = (i+1) * width + j + 1;
2079                         tri->indexes[ v + 3 ] = i * width + j;
2080                         tri->indexes[ v + 4 ] = (i+1) * width + j + 1;
2081                         tri->indexes[ v + 5 ] = (i+1) * width + j;
2082 #endif
2083                 }
2084         }
2085
2086         fileSystem->FreeFile( data );
2087
2088         modelSurface_t  surface;
2089
2090         surface.geometry = tri;
2091         surface.id = 0;
2092         surface.shader = tr.defaultMaterial; // declManager->FindMaterial( "shaderDemos/megaTexture" );
2093
2094         this->AddSurface( surface );
2095
2096         return true;
2097 }
2098
2099
2100 //=============================================================================
2101
2102 /*
2103 ================
2104 idRenderModelStatic::PurgeModel
2105 ================
2106 */
2107 void idRenderModelStatic::PurgeModel() {
2108         int             i;
2109         modelSurface_t  *surf;
2110
2111         for ( i = 0 ; i < surfaces.Num() ; i++ ) {
2112                 surf = &surfaces[i];
2113
2114                 if ( surf->geometry ) {
2115                         R_FreeStaticTriSurf( surf->geometry );
2116                 }
2117         }
2118         surfaces.Clear();
2119
2120         purged = true;
2121 }
2122
2123 /*
2124 ==============
2125 idRenderModelStatic::FreeVertexCache
2126
2127 We are about to restart the vertex cache, so dump everything
2128 ==============
2129 */
2130 void idRenderModelStatic::FreeVertexCache( void ) {
2131         for ( int j = 0 ; j < surfaces.Num() ; j++ ) {
2132                 srfTriangles_t *tri = surfaces[j].geometry;
2133                 if ( !tri ) {
2134                         continue;
2135                 }
2136                 if ( tri->ambientCache ) {
2137                         vertexCache.Free( tri->ambientCache );
2138                         tri->ambientCache = NULL;
2139                 }
2140                 // static shadows may be present
2141                 if ( tri->shadowCache ) {
2142                         vertexCache.Free( tri->shadowCache );
2143                         tri->shadowCache = NULL;
2144                 }
2145         }
2146 }
2147
2148 /*
2149 ================
2150 idRenderModelStatic::ReadFromDemoFile
2151 ================
2152 */
2153 void idRenderModelStatic::ReadFromDemoFile( class idDemoFile *f ) {
2154         PurgeModel();
2155
2156         InitEmpty( f->ReadHashString() );
2157
2158         int i, j, numSurfaces;
2159         f->ReadInt( numSurfaces );
2160         
2161         for ( i = 0 ; i < numSurfaces ; i++ ) {
2162                 modelSurface_t  surf;
2163                 
2164                 surf.shader = declManager->FindMaterial( f->ReadHashString() );
2165                 
2166                 srfTriangles_t  *tri = R_AllocStaticTriSurf();
2167                 
2168                 f->ReadInt( tri->numIndexes );
2169                 R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
2170                 for ( j = 0; j < tri->numIndexes; ++j )
2171                         f->ReadInt( (int&)tri->indexes[j] );
2172                 
2173                 f->ReadInt( tri->numVerts );
2174                 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
2175                 for ( j = 0; j < tri->numVerts; ++j ) {
2176                         f->ReadVec3( tri->verts[j].xyz );
2177                         f->ReadVec2( tri->verts[j].st );
2178                         f->ReadVec3( tri->verts[j].normal );
2179                         f->ReadVec3( tri->verts[j].tangents[0] );
2180                         f->ReadVec3( tri->verts[j].tangents[1] );
2181                         f->ReadUnsignedChar( tri->verts[j].color[0] );
2182                         f->ReadUnsignedChar( tri->verts[j].color[1] );
2183                         f->ReadUnsignedChar( tri->verts[j].color[2] );
2184                         f->ReadUnsignedChar( tri->verts[j].color[3] );
2185                 }
2186                 
2187                 surf.geometry = tri;
2188                 
2189                 this->AddSurface( surf );
2190         }
2191         this->FinishSurfaces();
2192 }
2193
2194 /*
2195 ================
2196 idRenderModelStatic::WriteToDemoFile
2197 ================
2198 */
2199 void idRenderModelStatic::WriteToDemoFile( class idDemoFile *f ) {
2200         int     data[1];
2201
2202         // note that it has been updated
2203         lastArchivedFrame = tr.frameCount;
2204
2205         data[0] = DC_DEFINE_MODEL;
2206         f->WriteInt( data[0] );
2207         f->WriteHashString( this->Name() );
2208
2209         int i, j, iData = surfaces.Num();
2210         f->WriteInt( iData );
2211
2212         for ( i = 0 ; i < surfaces.Num() ; i++ ) {
2213                 const modelSurface_t    *surf = &surfaces[i];
2214                 
2215                 f->WriteHashString( surf->shader->GetName() );
2216                 
2217                 srfTriangles_t *tri = surf->geometry;
2218                 f->WriteInt( tri->numIndexes );
2219                 for ( j = 0; j < tri->numIndexes; ++j )
2220                         f->WriteInt( (int&)tri->indexes[j] );
2221                 f->WriteInt( tri->numVerts );
2222                 for ( j = 0; j < tri->numVerts; ++j ) {
2223                         f->WriteVec3( tri->verts[j].xyz );
2224                         f->WriteVec2( tri->verts[j].st );
2225                         f->WriteVec3( tri->verts[j].normal );
2226                         f->WriteVec3( tri->verts[j].tangents[0] );
2227                         f->WriteVec3( tri->verts[j].tangents[1] );
2228                         f->WriteUnsignedChar( tri->verts[j].color[0] );
2229                         f->WriteUnsignedChar( tri->verts[j].color[1] );
2230                         f->WriteUnsignedChar( tri->verts[j].color[2] );
2231                         f->WriteUnsignedChar( tri->verts[j].color[3] );
2232                 }
2233         }
2234 }
2235
2236 /*
2237 ================
2238 idRenderModelStatic::IsLoaded
2239 ================
2240 */
2241 bool idRenderModelStatic::IsLoaded( void ) {
2242         return !purged;
2243 }
2244
2245 /*
2246 ================
2247 idRenderModelStatic::SetLevelLoadReferenced
2248 ================
2249 */
2250 void idRenderModelStatic::SetLevelLoadReferenced( bool referenced ) {
2251         levelLoadReferenced = referenced;
2252 }
2253
2254 /*
2255 ================
2256 idRenderModelStatic::IsLevelLoadReferenced
2257 ================
2258 */
2259 bool idRenderModelStatic::IsLevelLoadReferenced( void ) {
2260         return levelLoadReferenced;
2261 }
2262
2263 /*
2264 =================
2265 idRenderModelStatic::TouchData
2266 =================
2267 */
2268 void idRenderModelStatic::TouchData( void ) {
2269         for ( int i = 0 ; i < surfaces.Num() ; i++ ) {
2270                 const modelSurface_t    *surf = &surfaces[i];
2271
2272                 // re-find the material to make sure it gets added to the
2273                 // level keep list
2274                 declManager->FindMaterial( surf->shader->GetName() );
2275         }
2276 }
2277
2278 /*
2279 =================
2280 idRenderModelStatic::DeleteSurfaceWithId
2281 =================
2282 */
2283 bool idRenderModelStatic::DeleteSurfaceWithId( int id ) {
2284         int i;
2285
2286         for ( i = 0; i < surfaces.Num(); i++ ) {
2287                 if ( surfaces[i].id == id ) {
2288                         R_FreeStaticTriSurf( surfaces[i].geometry );
2289                         surfaces.RemoveIndex( i );
2290                         return true;
2291                 }
2292         }
2293         return false;
2294 }
2295
2296 /*
2297 =================
2298 idRenderModelStatic::DeleteSurfacesWithNegativeId
2299 =================
2300 */
2301 void idRenderModelStatic::DeleteSurfacesWithNegativeId( void ) {
2302         int i;
2303
2304         for ( i = 0; i < surfaces.Num(); i++ ) {
2305                 if ( surfaces[i].id < 0 ) {
2306                         R_FreeStaticTriSurf( surfaces[i].geometry );
2307                         surfaces.RemoveIndex( i );
2308                         i--;
2309                 }
2310         }
2311 }
2312
2313 /*
2314 =================
2315 idRenderModelStatic::FindSurfaceWithId
2316 =================
2317 */
2318 bool idRenderModelStatic::FindSurfaceWithId( int id, int &surfaceNum ) {
2319         int i;
2320
2321         for ( i = 0; i < surfaces.Num(); i++ ) {
2322                 if ( surfaces[i].id == id ) {
2323                         surfaceNum = i;
2324                         return true;
2325                 }
2326         }
2327         return false;
2328 }