doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
tr_trisurf.cpp
Go to the documentation of this file.
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 
34 /*
35 ==============================================================================
36 
37 TRIANGLE MESH PROCESSING
38 
39 The functions in this file have no vertex / index count limits.
40 
41 Truly identical vertexes that match in position, normal, and texcoord can
42 be merged away.
43 
44 Vertexes that match in position and texcoord, but have distinct normals will
45 remain distinct for all purposes. This is usually a poor choice for models,
46 as adding a bevel face will not add any more vertexes, and will tend to
47 look better.
48 
49 Match in position and normal, but differ in texcoords are referenced together
50 for calculating tangent vectors for bump mapping.
51 Artists should take care to have identical texels in all maps (bump/diffuse/specular)
52 in this case
53 
54 Vertexes that only match in position are merged for shadow edge finding.
55 
56 Degenerate triangles.
57 
58 Overlapped triangles, even if normals or texcoords differ, must be removed.
59 for the silhoette based stencil shadow algorithm to function properly.
60 Is this true???
61 Is the overlapped triangle problem just an example of the trippled edge problem?
62 
63 Interpenetrating triangles are not currently clipped to surfaces.
64 Do they effect the shadows?
65 
66 if vertexes are intended to deform apart, make sure that no vertexes
67 are on top of each other in the base frame, or the sil edges may be
68 calculated incorrectly.
69 
70 We might be able to identify this from topology.
71 
72 Dangling edges are acceptable, but three way edges are not.
73 
74 Are any combinations of two way edges unacceptable, like one facing
75 the backside of the other?
76 
77 
78 Topology is determined by a collection of triangle indexes.
79 
80 The edge list can be built up from this, and stays valid even under
81 deformations.
82 
83 Somewhat non-intuitively, concave edges cannot be optimized away, or the
84 stencil shadow algorithm miscounts.
85 
86 Face normals are needed for generating shadow volumes and for calculating
87 the silhouette, but they will change with any deformation.
88 
89 Vertex normals and vertex tangents will change with each deformation,
90 but they may be able to be transformed instead of recalculated.
91 
92 bounding volume, both box and sphere will change with deformation.
93 
94 silhouette indexes
95 shade indexes
96 texture indexes
97 
98  shade indexes will only be > silhouette indexes if there is facet shading present
99 
100  lookups from texture to sil and texture to shade?
101 
102 The normal and tangent vector smoothing is simple averaging, no attempt is
103 made to better handle the cases where the distribution around the shared vertex
104 is highly uneven.
105 
106 
107  we may get degenerate triangles even with the uniquing and removal
108  if the vertexes have different texcoords.
109 
110 ==============================================================================
111 */
112 
113 // this shouldn't change anything, but previously renderbumped models seem to need it
114 #define USE_INVA
115 
116 // instead of using the texture T vector, cross the normal and S vector for an orthogonal axis
117 #define DERIVE_UNSMOOTHED_BITANGENT
118 
119 const int MAX_SIL_EDGES = 0x10000;
120 const int SILEDGE_HASH_SIZE = 1024;
121 
122 static int numSilEdges;
123 static silEdge_t * silEdges;
124 static idHashIndex silEdgeHash( SILEDGE_HASH_SIZE, MAX_SIL_EDGES );
125 static int numPlanes;
126 
127 static idBlockAlloc<srfTriangles_t, 1<<8> srfTrianglesAllocator;
128 
129 #ifdef USE_TRI_DATA_ALLOCATOR
130 static idDynamicBlockAlloc<idDrawVert, 1<<20, 1<<10> triVertexAllocator;
131 static idDynamicBlockAlloc<glIndex_t, 1<<18, 1<<10> triIndexAllocator;
132 static idDynamicBlockAlloc<shadowCache_t, 1<<18, 1<<10> triShadowVertexAllocator;
133 static idDynamicBlockAlloc<idPlane, 1<<17, 1<<10> triPlaneAllocator;
134 static idDynamicBlockAlloc<glIndex_t, 1<<17, 1<<10> triSilIndexAllocator;
135 static idDynamicBlockAlloc<silEdge_t, 1<<17, 1<<10> triSilEdgeAllocator;
136 static idDynamicBlockAlloc<dominantTri_t, 1<<16, 1<<10> triDominantTrisAllocator;
137 static idDynamicBlockAlloc<int, 1<<16, 1<<10> triMirroredVertAllocator;
138 static idDynamicBlockAlloc<int, 1<<16, 1<<10> triDupVertAllocator;
139 #else
140 static idDynamicAlloc<idDrawVert, 1<<20, 1<<10> triVertexAllocator;
141 static idDynamicAlloc<glIndex_t, 1<<18, 1<<10> triIndexAllocator;
142 static idDynamicAlloc<shadowCache_t, 1<<18, 1<<10> triShadowVertexAllocator;
143 static idDynamicAlloc<idPlane, 1<<17, 1<<10> triPlaneAllocator;
144 static idDynamicAlloc<glIndex_t, 1<<17, 1<<10> triSilIndexAllocator;
145 static idDynamicAlloc<silEdge_t, 1<<17, 1<<10> triSilEdgeAllocator;
146 static idDynamicAlloc<dominantTri_t, 1<<16, 1<<10> triDominantTrisAllocator;
147 static idDynamicAlloc<int, 1<<16, 1<<10> triMirroredVertAllocator;
148 static idDynamicAlloc<int, 1<<16, 1<<10> triDupVertAllocator;
149 #endif
150 
151 
152 /*
153 ===============
154 R_InitTriSurfData
155 ===============
156 */
157 void R_InitTriSurfData( void ) {
158  silEdges = (silEdge_t *)R_StaticAlloc( MAX_SIL_EDGES * sizeof( silEdges[0] ) );
159 
160  // initialize allocators for triangle surfaces
161  triVertexAllocator.Init();
162  triIndexAllocator.Init();
163  triShadowVertexAllocator.Init();
164  triPlaneAllocator.Init();
165  triSilIndexAllocator.Init();
166  triSilEdgeAllocator.Init();
167  triDominantTrisAllocator.Init();
168  triMirroredVertAllocator.Init();
169  triDupVertAllocator.Init();
170 
171  // never swap out triangle surfaces
172  triVertexAllocator.SetLockMemory( true );
173  triIndexAllocator.SetLockMemory( true );
174  triShadowVertexAllocator.SetLockMemory( true );
175  triPlaneAllocator.SetLockMemory( true );
176  triSilIndexAllocator.SetLockMemory( true );
177  triSilEdgeAllocator.SetLockMemory( true );
178  triDominantTrisAllocator.SetLockMemory( true );
179  triMirroredVertAllocator.SetLockMemory( true );
180  triDupVertAllocator.SetLockMemory( true );
181 }
182 
183 /*
184 ===============
185 R_ShutdownTriSurfData
186 ===============
187 */
188 void R_ShutdownTriSurfData( void ) {
189  R_StaticFree( silEdges );
190  silEdgeHash.Free();
191  srfTrianglesAllocator.Shutdown();
192  triVertexAllocator.Shutdown();
193  triIndexAllocator.Shutdown();
194  triShadowVertexAllocator.Shutdown();
195  triPlaneAllocator.Shutdown();
196  triSilIndexAllocator.Shutdown();
197  triSilEdgeAllocator.Shutdown();
198  triDominantTrisAllocator.Shutdown();
199  triMirroredVertAllocator.Shutdown();
200  triDupVertAllocator.Shutdown();
201 }
202 
203 /*
204 ===============
205 R_PurgeTriSurfData
206 ===============
207 */
209  // free deferred triangle surfaces
210  R_FreeDeferredTriSurfs( frame );
211 
212  // free empty base blocks
213  triVertexAllocator.FreeEmptyBaseBlocks();
214  triIndexAllocator.FreeEmptyBaseBlocks();
215  triShadowVertexAllocator.FreeEmptyBaseBlocks();
216  triPlaneAllocator.FreeEmptyBaseBlocks();
217  triSilIndexAllocator.FreeEmptyBaseBlocks();
218  triSilEdgeAllocator.FreeEmptyBaseBlocks();
219  triDominantTrisAllocator.FreeEmptyBaseBlocks();
220  triMirroredVertAllocator.FreeEmptyBaseBlocks();
221  triDupVertAllocator.FreeEmptyBaseBlocks();
222 }
223 
224 /*
225 ===============
226 R_ShowTriMemory_f
227 ===============
228 */
229 void R_ShowTriSurfMemory_f( const idCmdArgs &args ) {
230  common->Printf( "%6d kB in %d triangle surfaces\n",
231  ( srfTrianglesAllocator.GetAllocCount() * sizeof( srfTriangles_t ) ) >> 10,
232  srfTrianglesAllocator.GetAllocCount() );
233 
234  common->Printf( "%6d kB vertex memory (%d kB free in %d blocks, %d empty base blocks)\n",
235  triVertexAllocator.GetBaseBlockMemory() >> 10, triVertexAllocator.GetFreeBlockMemory() >> 10,
236  triVertexAllocator.GetNumFreeBlocks(), triVertexAllocator.GetNumEmptyBaseBlocks() );
237 
238  common->Printf( "%6d kB index memory (%d kB free in %d blocks, %d empty base blocks)\n",
239  triIndexAllocator.GetBaseBlockMemory() >> 10, triIndexAllocator.GetFreeBlockMemory() >> 10,
240  triIndexAllocator.GetNumFreeBlocks(), triIndexAllocator.GetNumEmptyBaseBlocks() );
241 
242  common->Printf( "%6d kB shadow vert memory (%d kB free in %d blocks, %d empty base blocks)\n",
243  triShadowVertexAllocator.GetBaseBlockMemory() >> 10, triShadowVertexAllocator.GetFreeBlockMemory() >> 10,
244  triShadowVertexAllocator.GetNumFreeBlocks(), triShadowVertexAllocator.GetNumEmptyBaseBlocks() );
245 
246  common->Printf( "%6d kB tri plane memory (%d kB free in %d blocks, %d empty base blocks)\n",
247  triPlaneAllocator.GetBaseBlockMemory() >> 10, triPlaneAllocator.GetFreeBlockMemory() >> 10,
248  triPlaneAllocator.GetNumFreeBlocks(), triPlaneAllocator.GetNumEmptyBaseBlocks() );
249 
250  common->Printf( "%6d kB sil index memory (%d kB free in %d blocks, %d empty base blocks)\n",
251  triSilIndexAllocator.GetBaseBlockMemory() >> 10, triSilIndexAllocator.GetFreeBlockMemory() >> 10,
252  triSilIndexAllocator.GetNumFreeBlocks(), triSilIndexAllocator.GetNumEmptyBaseBlocks() );
253 
254  common->Printf( "%6d kB sil edge memory (%d kB free in %d blocks, %d empty base blocks)\n",
255  triSilEdgeAllocator.GetBaseBlockMemory() >> 10, triSilEdgeAllocator.GetFreeBlockMemory() >> 10,
256  triSilEdgeAllocator.GetNumFreeBlocks(), triSilEdgeAllocator.GetNumEmptyBaseBlocks() );
257 
258  common->Printf( "%6d kB dominant tri memory (%d kB free in %d blocks, %d empty base blocks)\n",
259  triDominantTrisAllocator.GetBaseBlockMemory() >> 10, triDominantTrisAllocator.GetFreeBlockMemory() >> 10,
260  triDominantTrisAllocator.GetNumFreeBlocks(), triDominantTrisAllocator.GetNumEmptyBaseBlocks() );
261 
262  common->Printf( "%6d kB mirror vert memory (%d kB free in %d blocks, %d empty base blocks)\n",
263  triMirroredVertAllocator.GetBaseBlockMemory() >> 10, triMirroredVertAllocator.GetFreeBlockMemory() >> 10,
264  triMirroredVertAllocator.GetNumFreeBlocks(), triMirroredVertAllocator.GetNumEmptyBaseBlocks() );
265 
266  common->Printf( "%6d kB dup vert memory (%d kB free in %d blocks, %d empty base blocks)\n",
267  triDupVertAllocator.GetBaseBlockMemory() >> 10, triDupVertAllocator.GetFreeBlockMemory() >> 10,
268  triDupVertAllocator.GetNumFreeBlocks(), triDupVertAllocator.GetNumEmptyBaseBlocks() );
269 
270  common->Printf( "%6d kB total triangle memory\n",
271  ( srfTrianglesAllocator.GetAllocCount() * sizeof( srfTriangles_t ) +
272  triVertexAllocator.GetBaseBlockMemory() +
273  triIndexAllocator.GetBaseBlockMemory() +
274  triShadowVertexAllocator.GetBaseBlockMemory() +
275  triPlaneAllocator.GetBaseBlockMemory() +
276  triSilIndexAllocator.GetBaseBlockMemory() +
277  triSilEdgeAllocator.GetBaseBlockMemory() +
278  triDominantTrisAllocator.GetBaseBlockMemory() +
279  triMirroredVertAllocator.GetBaseBlockMemory() +
280  triDupVertAllocator.GetBaseBlockMemory() ) >> 10 );
281 }
282 
283 /*
284 =================
285 R_TriSurfMemory
286 
287 For memory profiling
288 =================
289 */
290 int R_TriSurfMemory( const srfTriangles_t *tri ) {
291  int total = 0;
292 
293  if ( !tri ) {
294  return total;
295  }
296 
297  // used as a flag in interations
298  if ( tri == LIGHT_TRIS_DEFERRED ) {
299  return total;
300  }
301 
302  if ( tri->shadowVertexes != NULL ) {
303  total += tri->numVerts * sizeof( tri->shadowVertexes[0] );
304  } else if ( tri->verts != NULL ) {
305  if ( tri->ambientSurface == NULL || tri->verts != tri->ambientSurface->verts ) {
306  total += tri->numVerts * sizeof( tri->verts[0] );
307  }
308  }
309  if ( tri->facePlanes != NULL ) {
310  total += tri->numIndexes / 3 * sizeof( tri->facePlanes[0] );
311  }
312  if ( tri->indexes != NULL ) {
313  if ( tri->ambientSurface == NULL || tri->indexes != tri->ambientSurface->indexes ) {
314  total += tri->numIndexes * sizeof( tri->indexes[0] );
315  }
316  }
317  if ( tri->silIndexes != NULL ) {
318  total += tri->numIndexes * sizeof( tri->silIndexes[0] );
319  }
320  if ( tri->silEdges != NULL ) {
321  total += tri->numSilEdges * sizeof( tri->silEdges[0] );
322  }
323  if ( tri->dominantTris != NULL ) {
324  total += tri->numVerts * sizeof( tri->dominantTris[0] );
325  }
326  if ( tri->mirroredVerts != NULL ) {
327  total += tri->numMirroredVerts * sizeof( tri->mirroredVerts[0] );
328  }
329  if ( tri->dupVerts != NULL ) {
330  total += tri->numDupVerts * sizeof( tri->dupVerts[0] );
331  }
332 
333  total += sizeof( *tri );
334 
335  return total;
336 }
337 
338 /*
339 ==============
340 R_FreeStaticTriSurfVertexCaches
341 ==============
342 */
344  if ( tri->ambientSurface == NULL ) {
345  // this is a real model surface
346  vertexCache.Free( tri->ambientCache );
347  tri->ambientCache = NULL;
348  } else {
349  // this is a light interaction surface that references
350  // a different ambient model surface
352  tri->lightingCache = NULL;
353  }
354  if ( tri->indexCache ) {
355  vertexCache.Free( tri->indexCache );
356  tri->indexCache = NULL;
357  }
358  if ( tri->shadowCache && ( tri->shadowVertexes != NULL || tri->verts != NULL ) ) {
359  // if we don't have tri->shadowVertexes, these are a reference to a
360  // shadowCache on the original surface, which a vertex program
361  // will take care of making unique for each light
362  vertexCache.Free( tri->shadowCache );
363  tri->shadowCache = NULL;
364  }
365 }
366 
367 /*
368 ==============
369 R_ReallyFreeStaticTriSurf
370 
371 This does the actual free
372 ==============
373 */
375  if ( !tri ) {
376  return;
377  }
378 
380 
381  if ( tri->verts != NULL ) {
382  // R_CreateLightTris points tri->verts at the verts of the ambient surface
383  if ( tri->ambientSurface == NULL || tri->verts != tri->ambientSurface->verts ) {
384  triVertexAllocator.Free( tri->verts );
385  }
386  }
387 
388  if ( !tri->deformedSurface ) {
389  if ( tri->indexes != NULL ) {
390  // if a surface is completely inside a light volume R_CreateLightTris points tri->indexes at the indexes of the ambient surface
391  if ( tri->ambientSurface == NULL || tri->indexes != tri->ambientSurface->indexes ) {
392  triIndexAllocator.Free( tri->indexes );
393  }
394  }
395  if ( tri->silIndexes != NULL ) {
396  triSilIndexAllocator.Free( tri->silIndexes );
397  }
398  if ( tri->silEdges != NULL ) {
399  triSilEdgeAllocator.Free( tri->silEdges );
400  }
401  if ( tri->dominantTris != NULL ) {
402  triDominantTrisAllocator.Free( tri->dominantTris );
403  }
404  if ( tri->mirroredVerts != NULL ) {
405  triMirroredVertAllocator.Free( tri->mirroredVerts );
406  }
407  if ( tri->dupVerts != NULL ) {
408  triDupVertAllocator.Free( tri->dupVerts );
409  }
410  }
411 
412  if ( tri->facePlanes != NULL ) {
413  triPlaneAllocator.Free( tri->facePlanes );
414  }
415 
416  if ( tri->shadowVertexes != NULL ) {
417  triShadowVertexAllocator.Free( tri->shadowVertexes );
418  }
419 
420 #ifdef _DEBUG
421  memset( tri, 0, sizeof( srfTriangles_t ) );
422 #endif
423 
424  srfTrianglesAllocator.Free( tri );
425 }
426 
427 /*
428 ==============
429 R_CheckStaticTriSurfMemory
430 ==============
431 */
433  if ( !tri ) {
434  return;
435  }
436 
437  if ( tri->verts != NULL ) {
438  // R_CreateLightTris points tri->verts at the verts of the ambient surface
439  if ( tri->ambientSurface == NULL || tri->verts != tri->ambientSurface->verts ) {
440  const char *error = triVertexAllocator.CheckMemory( tri->verts );
441  assert( error == NULL );
442  }
443  }
444 
445  if ( !tri->deformedSurface ) {
446  if ( tri->indexes != NULL ) {
447  // if a surface is completely inside a light volume R_CreateLightTris points tri->indexes at the indexes of the ambient surface
448  if ( tri->ambientSurface == NULL || tri->indexes != tri->ambientSurface->indexes ) {
449  const char *error = triIndexAllocator.CheckMemory( tri->indexes );
450  assert( error == NULL );
451  }
452  }
453  }
454 
455  if ( tri->shadowVertexes != NULL ) {
456  const char *error = triShadowVertexAllocator.CheckMemory( tri->shadowVertexes );
457  assert( error == NULL );
458  }
459 }
460 
461 /*
462 ==================
463 R_FreeDeferredTriSurfs
464 ==================
465 */
467  srfTriangles_t *tri, *next;
468 
469  if ( !frame ) {
470  return;
471  }
472 
473  for ( tri = frame->firstDeferredFreeTriSurf; tri; tri = next ) {
474  next = tri->nextDeferredFree;
476  }
477 
479  frame->lastDeferredFreeTriSurf = NULL;
480 }
481 
482 /*
483 ==============
484 R_FreeStaticTriSurf
485 
486 This will defer the free until the current frame has run through the back end.
487 ==============
488 */
490  frameData_t *frame;
491 
492  if ( !tri ) {
493  return;
494  }
495 
496  if ( tri->nextDeferredFree ) {
497  common->Error( "R_FreeStaticTriSurf: freed a freed triangle" );
498  }
499  frame = frameData;
500 
501  if ( !frame ) {
502  // command line utility, or rendering in editor preview mode ( force )
504  } else {
505 #ifdef ID_DEBUG_MEMORY
507 #endif
508  tri->nextDeferredFree = NULL;
509  if ( frame->lastDeferredFreeTriSurf ) {
511  } else {
512  frame->firstDeferredFreeTriSurf = tri;
513  }
514  frame->lastDeferredFreeTriSurf = tri;
515  }
516 }
517 
518 /*
519 ==============
520 R_AllocStaticTriSurf
521 ==============
522 */
524  srfTriangles_t *tris = srfTrianglesAllocator.Alloc();
525  memset( tris, 0, sizeof( srfTriangles_t ) );
526  return tris;
527 }
528 
529 /*
530 =================
531 R_CopyStaticTriSurf
532 
533 This only duplicates the indexes and verts, not any of the derived data.
534 =================
535 */
537  srfTriangles_t *newTri;
538 
539  newTri = R_AllocStaticTriSurf();
540  R_AllocStaticTriSurfVerts( newTri, tri->numVerts );
541  R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes );
542  newTri->numVerts = tri->numVerts;
543  newTri->numIndexes = tri->numIndexes;
544  memcpy( newTri->verts, tri->verts, tri->numVerts * sizeof( newTri->verts[0] ) );
545  memcpy( newTri->indexes, tri->indexes, tri->numIndexes * sizeof( newTri->indexes[0] ) );
546 
547  return newTri;
548 }
549 
550 /*
551 =================
552 R_AllocStaticTriSurfVerts
553 =================
554 */
555 void R_AllocStaticTriSurfVerts( srfTriangles_t *tri, int numVerts ) {
556  assert( tri->verts == NULL );
557  tri->verts = triVertexAllocator.Alloc( numVerts );
558 }
559 
560 /*
561 =================
562 R_AllocStaticTriSurfIndexes
563 =================
564 */
565 void R_AllocStaticTriSurfIndexes( srfTriangles_t *tri, int numIndexes ) {
566  assert( tri->indexes == NULL );
567  tri->indexes = triIndexAllocator.Alloc( numIndexes );
568 }
569 
570 /*
571 =================
572 R_AllocStaticTriSurfShadowVerts
573 =================
574 */
576  assert( tri->shadowVertexes == NULL );
577  tri->shadowVertexes = triShadowVertexAllocator.Alloc( numVerts );
578 }
579 
580 /*
581 =================
582 R_AllocStaticTriSurfPlanes
583 =================
584 */
585 void R_AllocStaticTriSurfPlanes( srfTriangles_t *tri, int numIndexes ) {
586  if ( tri->facePlanes ) {
587  triPlaneAllocator.Free( tri->facePlanes );
588  }
589  tri->facePlanes = triPlaneAllocator.Alloc( numIndexes / 3 );
590 }
591 
592 /*
593 =================
594 R_ResizeStaticTriSurfVerts
595 =================
596 */
597 void R_ResizeStaticTriSurfVerts( srfTriangles_t *tri, int numVerts ) {
598 #ifdef USE_TRI_DATA_ALLOCATOR
599  tri->verts = triVertexAllocator.Resize( tri->verts, numVerts );
600 #else
601  assert( false );
602 #endif
603 }
604 
605 /*
606 =================
607 R_ResizeStaticTriSurfIndexes
608 =================
609 */
610 void R_ResizeStaticTriSurfIndexes( srfTriangles_t *tri, int numIndexes ) {
611 #ifdef USE_TRI_DATA_ALLOCATOR
612  tri->indexes = triIndexAllocator.Resize( tri->indexes, numIndexes );
613 #else
614  assert( false );
615 #endif
616 }
617 
618 /*
619 =================
620 R_ResizeStaticTriSurfShadowVerts
621 =================
622 */
624 #ifdef USE_TRI_DATA_ALLOCATOR
625  tri->shadowVertexes = triShadowVertexAllocator.Resize( tri->shadowVertexes, numVerts );
626 #else
627  assert( false );
628 #endif
629 }
630 
631 /*
632 =================
633 R_ReferenceStaticTriSurfVerts
634 =================
635 */
637  tri->verts = reference->verts;
638 }
639 
640 /*
641 =================
642 R_ReferenceStaticTriSurfIndexes
643 =================
644 */
646  tri->indexes = reference->indexes;
647 }
648 
649 /*
650 =================
651 R_FreeStaticTriSurfSilIndexes
652 =================
653 */
655  triSilIndexAllocator.Free( tri->silIndexes );
656  tri->silIndexes = NULL;
657 }
658 
659 /*
660 ===============
661 R_RangeCheckIndexes
662 
663 Check for syntactically incorrect indexes, like out of range values.
664 Does not check for semantics, like degenerate triangles.
665 
666 No vertexes is acceptable if no indexes.
667 No indexes is acceptable.
668 More vertexes than are referenced by indexes are acceptable.
669 ===============
670 */
672  int i;
673 
674  if ( tri->numIndexes < 0 ) {
675  common->Error( "R_RangeCheckIndexes: numIndexes < 0" );
676  }
677  if ( tri->numVerts < 0 ) {
678  common->Error( "R_RangeCheckIndexes: numVerts < 0" );
679  }
680 
681  // must specify an integral number of triangles
682  if ( tri->numIndexes % 3 != 0 ) {
683  common->Error( "R_RangeCheckIndexes: numIndexes %% 3" );
684  }
685 
686  for ( i = 0 ; i < tri->numIndexes ; i++ ) {
687  if ( tri->indexes[i] < 0 || tri->indexes[i] >= tri->numVerts ) {
688  common->Error( "R_RangeCheckIndexes: index out of range" );
689  }
690  }
691 
692  // this should not be possible unless there are unused verts
693  if ( tri->numVerts > tri->numIndexes ) {
694  // FIXME: find the causes of these
695  // common->Printf( "R_RangeCheckIndexes: tri->numVerts > tri->numIndexes\n" );
696  }
697 }
698 
699 /*
700 =================
701 R_BoundTriSurf
702 =================
703 */
705  SIMDProcessor->MinMax( tri->bounds[0], tri->bounds[1], tri->verts, tri->numVerts );
706 }
707 
708 /*
709 =================
710 R_CreateSilRemap
711 =================
712 */
713 static int *R_CreateSilRemap( const srfTriangles_t *tri ) {
714  int c_removed, c_unique;
715  int *remap;
716  int i, j, hashKey;
717  const idDrawVert *v1, *v2;
718 
719  remap = (int *)R_ClearedStaticAlloc( tri->numVerts * sizeof( remap[0] ) );
720 
721  if ( !r_useSilRemap.GetBool() ) {
722  for ( i = 0 ; i < tri->numVerts ; i++ ) {
723  remap[i] = i;
724  }
725  return remap;
726  }
727 
728  idHashIndex hash( 1024, tri->numVerts );
729 
730  c_removed = 0;
731  c_unique = 0;
732  for ( i = 0 ; i < tri->numVerts ; i++ ) {
733  v1 = &tri->verts[i];
734 
735  // see if there is an earlier vert that it can map to
736  hashKey = hash.GenerateKey( v1->xyz );
737  for ( j = hash.First( hashKey ); j >= 0; j = hash.Next( j ) ) {
738  v2 = &tri->verts[j];
739  if ( v2->xyz[0] == v1->xyz[0]
740  && v2->xyz[1] == v1->xyz[1]
741  && v2->xyz[2] == v1->xyz[2] ) {
742  c_removed++;
743  remap[i] = j;
744  break;
745  }
746  }
747  if ( j < 0 ) {
748  c_unique++;
749  remap[i] = i;
750  hash.Add( hashKey, i );
751  }
752  }
753 
754  return remap;
755 }
756 
757 /*
758 =================
759 R_CreateSilIndexes
760 
761 Uniquing vertexes only on xyz before creating sil edges reduces
762 the edge count by about 20% on Q3 models
763 =================
764 */
766  int i;
767  int *remap;
768 
769  if ( tri->silIndexes ) {
770  triSilIndexAllocator.Free( tri->silIndexes );
771  tri->silIndexes = NULL;
772  }
773 
774  remap = R_CreateSilRemap( tri );
775 
776  // remap indexes to the first one
777  tri->silIndexes = triSilIndexAllocator.Alloc( tri->numIndexes );
778  for ( i = 0; i < tri->numIndexes; i++ ) {
779  tri->silIndexes[i] = remap[tri->indexes[i]];
780  }
781 
782  R_StaticFree( remap );
783 }
784 
785 /*
786 =====================
787 R_CreateDupVerts
788 =====================
789 */
791  int i;
792 
793  int *remap = (int *) _alloca16( tri->numVerts * sizeof( remap[0] ) );
794 
795  // initialize vertex remap in case there are unused verts
796  for ( i = 0; i < tri->numVerts; i++ ) {
797  remap[i] = i;
798  }
799 
800  // set the remap based on how the silhouette indexes are remapped
801  for ( i = 0; i < tri->numIndexes; i++ ) {
802  remap[tri->indexes[i]] = tri->silIndexes[i];
803  }
804 
805  // create duplicate vertex index based on the vertex remap
806  int * tempDupVerts = (int *) _alloca16( tri->numVerts * 2 * sizeof( tempDupVerts[0] ) );
807  tri->numDupVerts = 0;
808  for ( i = 0; i < tri->numVerts; i++ ) {
809  if ( remap[i] != i ) {
810  tempDupVerts[tri->numDupVerts*2+0] = i;
811  tempDupVerts[tri->numDupVerts*2+1] = remap[i];
812  tri->numDupVerts++;
813  }
814  }
815 
816  tri->dupVerts = triDupVertAllocator.Alloc( tri->numDupVerts * 2 );
817  memcpy( tri->dupVerts, tempDupVerts, tri->numDupVerts * 2 * sizeof( tri->dupVerts[0] ) );
818 }
819 
820 /*
821 =====================
822 R_DeriveFacePlanes
823 
824 Writes the facePlanes values, overwriting existing ones if present
825 =====================
826 */
828  idPlane * planes;
829 
830  if ( !tri->facePlanes ) {
832  }
833  planes = tri->facePlanes;
834 
835 #if 1
836 
837  SIMDProcessor->DeriveTriPlanes( planes, tri->verts, tri->numVerts, tri->indexes, tri->numIndexes );
838 
839 #else
840 
841  for ( int i = 0; i < tri->numIndexes; i+= 3, planes++ ) {
842  int i1, i2, i3;
843  idVec3 d1, d2, normal;
844  idVec3 *v1, *v2, *v3;
845 
846  i1 = tri->indexes[i + 0];
847  i2 = tri->indexes[i + 1];
848  i3 = tri->indexes[i + 2];
849 
850  v1 = &tri->verts[i1].xyz;
851  v2 = &tri->verts[i2].xyz;
852  v3 = &tri->verts[i3].xyz;
853 
854  d1[0] = v2->x - v1->x;
855  d1[1] = v2->y - v1->y;
856  d1[2] = v2->z - v1->z;
857 
858  d2[0] = v3->x - v1->x;
859  d2[1] = v3->y - v1->y;
860  d2[2] = v3->z - v1->z;
861 
862  normal[0] = d2.y * d1.z - d2.z * d1.y;
863  normal[1] = d2.z * d1.x - d2.x * d1.z;
864  normal[2] = d2.x * d1.y - d2.y * d1.x;
865 
866  float sqrLength, invLength;
867 
868  sqrLength = normal.x * normal.x + normal.y * normal.y + normal.z * normal.z;
869  invLength = idMath::RSqrt( sqrLength );
870 
871  (*planes)[0] = normal[0] * invLength;
872  (*planes)[1] = normal[1] * invLength;
873  (*planes)[2] = normal[2] * invLength;
874 
875  planes->FitThroughPoint( *v1 );
876  }
877 
878 #endif
879 
880  tri->facePlanesCalculated = true;
881 }
882 
883 /*
884 =====================
885 R_CreateVertexNormals
886 
887 Averages together the contributions of all faces that are
888 used by a vertex, creating drawVert->normal
889 =====================
890 */
892  int i, j;
893  const idPlane *planes;
894 
895  for ( i = 0 ; i < tri->numVerts ; i++ ) {
896  tri->verts[i].normal.Zero();
897  }
898 
899  if ( !tri->facePlanes || !tri->facePlanesCalculated ) {
900  R_DeriveFacePlanes( tri );
901  }
902  if ( !tri->silIndexes ) {
903  R_CreateSilIndexes( tri );
904  }
905  planes = tri->facePlanes;
906  for ( i = 0 ; i < tri->numIndexes ; i += 3, planes++ ) {
907  for ( j = 0 ; j < 3 ; j++ ) {
908  int index = tri->silIndexes[i+j];
909  tri->verts[index].normal += planes->Normal();
910  }
911  }
912 
913  // normalize and replicate from silIndexes to all indexes
914  for ( i = 0 ; i < tri->numIndexes ; i++ ) {
915  tri->verts[tri->indexes[i]].normal = tri->verts[tri->silIndexes[i]].normal;
916  tri->verts[tri->indexes[i]].normal.Normalize();
917  }
918 }
919 
920 /*
921 ===============
922 R_DefineEdge
923 ===============
924 */
925 static int c_duplicatedEdges, c_tripledEdges;
926 
927 static void R_DefineEdge( int v1, int v2, int planeNum ) {
928  int i, hashKey;
929 
930  // check for degenerate edge
931  if ( v1 == v2 ) {
932  return;
933  }
934  hashKey = silEdgeHash.GenerateKey( v1, v2 );
935  // search for a matching other side
936  for ( i = silEdgeHash.First( hashKey ); i >= 0 && i < MAX_SIL_EDGES; i = silEdgeHash.Next( i ) ) {
937  if ( silEdges[i].v1 == v1 && silEdges[i].v2 == v2 ) {
938  c_duplicatedEdges++;
939  // allow it to still create a new edge
940  continue;
941  }
942  if ( silEdges[i].v2 == v1 && silEdges[i].v1 == v2 ) {
943  if ( silEdges[i].p2 != numPlanes ) {
944  c_tripledEdges++;
945  // allow it to still create a new edge
946  continue;
947  }
948  // this is a matching back side
949  silEdges[i].p2 = planeNum;
950  return;
951  }
952 
953  }
954 
955  // define the new edge
956  if ( numSilEdges == MAX_SIL_EDGES ) {
957  common->DWarning( "MAX_SIL_EDGES" );
958  return;
959  }
960 
961  silEdgeHash.Add( hashKey, numSilEdges );
962 
963  silEdges[numSilEdges].p1 = planeNum;
964  silEdges[numSilEdges].p2 = numPlanes;
965  silEdges[numSilEdges].v1 = v1;
966  silEdges[numSilEdges].v2 = v2;
967 
968  numSilEdges++;
969 }
970 
971 /*
972 =================
973 SilEdgeSort
974 =================
975 */
976 static int SilEdgeSort( const void *a, const void *b ) {
977  if ( ((silEdge_t *)a)->p1 < ((silEdge_t *)b)->p1 ) {
978  return -1;
979  }
980  if ( ((silEdge_t *)a)->p1 > ((silEdge_t *)b)->p1 ) {
981  return 1;
982  }
983  if ( ((silEdge_t *)a)->p2 < ((silEdge_t *)b)->p2 ) {
984  return -1;
985  }
986  if ( ((silEdge_t *)a)->p2 > ((silEdge_t *)b)->p2 ) {
987  return 1;
988  }
989  return 0;
990 }
991 
992 /*
993 =================
994 R_IdentifySilEdges
995 
996 If the surface will not deform, coplanar edges (polygon interiors)
997 can never create silhouette plains, and can be omited
998 =================
999 */
1002 
1003 void R_IdentifySilEdges( srfTriangles_t *tri, bool omitCoplanarEdges ) {
1004  int i;
1005  int numTris;
1006  int shared, single;
1007 
1008  omitCoplanarEdges = false; // optimization doesn't work for some reason
1009 
1010  numTris = tri->numIndexes / 3;
1011 
1012  numSilEdges = 0;
1013  silEdgeHash.Clear();
1014  numPlanes = numTris;
1015 
1016  c_duplicatedEdges = 0;
1017  c_tripledEdges = 0;
1018 
1019  for ( i = 0 ; i < numTris ; i++ ) {
1020  int i1, i2, i3;
1021 
1022  i1 = tri->silIndexes[ i*3 + 0 ];
1023  i2 = tri->silIndexes[ i*3 + 1 ];
1024  i3 = tri->silIndexes[ i*3 + 2 ];
1025 
1026  // create the edges
1027  R_DefineEdge( i1, i2, i );
1028  R_DefineEdge( i2, i3, i );
1029  R_DefineEdge( i3, i1, i );
1030  }
1031 
1032  if ( c_duplicatedEdges || c_tripledEdges ) {
1033  common->DWarning( "%i duplicated edge directions, %i tripled edges", c_duplicatedEdges, c_tripledEdges );
1034  }
1035 
1036  // if we know that the vertexes aren't going
1037  // to deform, we can remove interior triangulation edges
1038  // on otherwise planar polygons.
1039  // I earlier believed that I could also remove concave
1040  // edges, because they are never silhouettes in the conventional sense,
1041  // but they are still needed to balance out all the true sil edges
1042  // for the shadow algorithm to function
1043  int c_coplanarCulled;
1044 
1045  c_coplanarCulled = 0;
1046  if ( omitCoplanarEdges ) {
1047  for ( i = 0 ; i < numSilEdges ; i++ ) {
1048  int i1, i2, i3;
1049  idPlane plane;
1050  int base;
1051  int j;
1052  float d;
1053 
1054  if ( silEdges[i].p2 == numPlanes ) { // the fake dangling edge
1055  continue;
1056  }
1057 
1058  base = silEdges[i].p1 * 3;
1059  i1 = tri->silIndexes[ base + 0 ];
1060  i2 = tri->silIndexes[ base + 1 ];
1061  i3 = tri->silIndexes[ base + 2 ];
1062 
1063  plane.FromPoints( tri->verts[i1].xyz, tri->verts[i2].xyz, tri->verts[i3].xyz );
1064 
1065  // check to see if points of second triangle are not coplanar
1066  base = silEdges[i].p2 * 3;
1067  for ( j = 0 ; j < 3 ; j++ ) {
1068  i1 = tri->silIndexes[ base + j ];
1069  d = plane.Distance( tri->verts[i1].xyz );
1070  if ( d != 0 ) { // even a small epsilon causes problems
1071  break;
1072  }
1073  }
1074 
1075  if ( j == 3 ) {
1076  // we can cull this sil edge
1077  memmove( &silEdges[i], &silEdges[i+1], (numSilEdges-i-1) * sizeof( silEdges[i] ) );
1078  c_coplanarCulled++;
1079  numSilEdges--;
1080  i--;
1081  }
1082  }
1083  if ( c_coplanarCulled ) {
1084  c_coplanarSilEdges += c_coplanarCulled;
1085 // common->Printf( "%i of %i sil edges coplanar culled\n", c_coplanarCulled,
1086 // c_coplanarCulled + numSilEdges );
1087  }
1088  }
1089  c_totalSilEdges += numSilEdges;
1090 
1091  // sort the sil edges based on plane number
1092  qsort( silEdges, numSilEdges, sizeof( silEdges[0] ), SilEdgeSort );
1093 
1094  // count up the distribution.
1095  // a perfectly built model should only have shared
1096  // edges, but most models will have some interpenetration
1097  // and dangling edges
1098  shared = 0;
1099  single = 0;
1100  for ( i = 0 ; i < numSilEdges ; i++ ) {
1101  if ( silEdges[i].p2 == numPlanes ) {
1102  single++;
1103  } else {
1104  shared++;
1105  }
1106  }
1107 
1108  if ( !single ) {
1109  tri->perfectHull = true;
1110  } else {
1111  tri->perfectHull = false;
1112  }
1113 
1114  tri->numSilEdges = numSilEdges;
1115  tri->silEdges = triSilEdgeAllocator.Alloc( numSilEdges );
1116  memcpy( tri->silEdges, silEdges, numSilEdges * sizeof( tri->silEdges[0] ) );
1117 }
1118 
1119 /*
1120 ===============
1121 R_FaceNegativePolarity
1122 
1123 Returns true if the texture polarity of the face is negative, false if it is positive or zero
1124 ===============
1125 */
1126 static bool R_FaceNegativePolarity( const srfTriangles_t *tri, int firstIndex ) {
1127  idDrawVert *a, *b, *c;
1128  float area;
1129  float d0[5], d1[5];
1130 
1131  a = tri->verts + tri->indexes[firstIndex + 0];
1132  b = tri->verts + tri->indexes[firstIndex + 1];
1133  c = tri->verts + tri->indexes[firstIndex + 2];
1134 
1135  d0[3] = b->st[0] - a->st[0];
1136  d0[4] = b->st[1] - a->st[1];
1137 
1138  d1[3] = c->st[0] - a->st[0];
1139  d1[4] = c->st[1] - a->st[1];
1140 
1141  area = d0[3] * d1[4] - d0[4] * d1[3];
1142  if ( area >= 0 ) {
1143  return false;
1144  }
1145  return true;
1146 }
1147 
1148 /*
1149 ==================
1150 R_DeriveFaceTangents
1151 ==================
1152 */
1153 typedef struct {
1154  idVec3 tangents[2];
1157 } faceTangents_t;
1158 
1159 static void R_DeriveFaceTangents( const srfTriangles_t *tri, faceTangents_t *faceTangents ) {
1160  int i;
1161  int c_textureDegenerateFaces;
1162  int c_positive, c_negative;
1163  faceTangents_t *ft;
1164  idDrawVert *a, *b, *c;
1165 
1166  //
1167  // calculate tangent vectors for each face in isolation
1168  //
1169  c_positive = 0;
1170  c_negative = 0;
1171  c_textureDegenerateFaces = 0;
1172  for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
1173  float area;
1174  idVec3 temp;
1175  float d0[5], d1[5];
1176 
1177  ft = &faceTangents[i/3];
1178 
1179  a = tri->verts + tri->indexes[i + 0];
1180  b = tri->verts + tri->indexes[i + 1];
1181  c = tri->verts + tri->indexes[i + 2];
1182 
1183  d0[0] = b->xyz[0] - a->xyz[0];
1184  d0[1] = b->xyz[1] - a->xyz[1];
1185  d0[2] = b->xyz[2] - a->xyz[2];
1186  d0[3] = b->st[0] - a->st[0];
1187  d0[4] = b->st[1] - a->st[1];
1188 
1189  d1[0] = c->xyz[0] - a->xyz[0];
1190  d1[1] = c->xyz[1] - a->xyz[1];
1191  d1[2] = c->xyz[2] - a->xyz[2];
1192  d1[3] = c->st[0] - a->st[0];
1193  d1[4] = c->st[1] - a->st[1];
1194 
1195  area = d0[3] * d1[4] - d0[4] * d1[3];
1196  if ( fabs( area ) < 1e-20f ) {
1197  ft->negativePolarity = false;
1198  ft->degenerate = true;
1199  ft->tangents[0].Zero();
1200  ft->tangents[1].Zero();
1201  c_textureDegenerateFaces++;
1202  continue;
1203  }
1204  if ( area > 0.0f ) {
1205  ft->negativePolarity = false;
1206  c_positive++;
1207  } else {
1208  ft->negativePolarity = true;
1209  c_negative++;
1210  }
1211  ft->degenerate = false;
1212 
1213 #ifdef USE_INVA
1214  float inva = area < 0.0f ? -1 : 1; // was = 1.0f / area;
1215 
1216  temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]) * inva;
1217  temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]) * inva;
1218  temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]) * inva;
1219  temp.Normalize();
1220  ft->tangents[0] = temp;
1221 
1222  temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]) * inva;
1223  temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]) * inva;
1224  temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]) * inva;
1225  temp.Normalize();
1226  ft->tangents[1] = temp;
1227 #else
1228  temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]);
1229  temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]);
1230  temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]);
1231  temp.Normalize();
1232  ft->tangents[0] = temp;
1233 
1234  temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]);
1235  temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]);
1236  temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]);
1237  temp.Normalize();
1238  ft->tangents[1] = temp;
1239 #endif
1240  }
1241 }
1242 
1243 
1244 
1245 /*
1246 ===================
1247 R_DuplicateMirroredVertexes
1248 
1249 Modifies the surface to bust apart any verts that are shared by both positive and
1250 negative texture polarities, so tangent space smoothing at the vertex doesn't
1251 degenerate.
1252 
1253 This will create some identical vertexes (which will eventually get different tangent
1254 vectors), so never optimize the resulting mesh, or it will get the mirrored edges back.
1255 
1256 Reallocates tri->verts and changes tri->indexes in place
1257 Silindexes are unchanged by this.
1258 
1259 sets mirroredVerts and mirroredVerts[]
1260 
1261 ===================
1262 */
1263 typedef struct {
1264  bool polarityUsed[2];
1266 } tangentVert_t;
1267 
1268 static void R_DuplicateMirroredVertexes( srfTriangles_t *tri ) {
1269  tangentVert_t *tverts, *vert;
1270  int i, j;
1271  int totalVerts;
1272  int numMirror;
1273 
1274  tverts = (tangentVert_t *)_alloca16( tri->numVerts * sizeof( *tverts ) );
1275  memset( tverts, 0, tri->numVerts * sizeof( *tverts ) );
1276 
1277  // determine texture polarity of each surface
1278 
1279  // mark each vert with the polarities it uses
1280  for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
1281  int polarity;
1282 
1283  polarity = R_FaceNegativePolarity( tri, i );
1284  for ( j = 0 ; j < 3 ; j++ ) {
1285  tverts[tri->indexes[i+j]].polarityUsed[ polarity ] = true;
1286  }
1287  }
1288 
1289  // now create new verts as needed
1290  totalVerts = tri->numVerts;
1291  for ( i = 0 ; i < tri->numVerts ; i++ ) {
1292  vert = &tverts[i];
1293  if ( vert->polarityUsed[0] && vert->polarityUsed[1] ) {
1294  vert->negativeRemap = totalVerts;
1295  totalVerts++;
1296  }
1297  }
1298 
1299  tri->numMirroredVerts = totalVerts - tri->numVerts;
1300 
1301  // now create the new list
1302  if ( totalVerts == tri->numVerts ) {
1303  tri->mirroredVerts = NULL;
1304  return;
1305  }
1306 
1307  tri->mirroredVerts = triMirroredVertAllocator.Alloc( tri->numMirroredVerts );
1308 
1309 #ifdef USE_TRI_DATA_ALLOCATOR
1310  tri->verts = triVertexAllocator.Resize( tri->verts, totalVerts );
1311 #else
1312  idDrawVert *oldVerts = tri->verts;
1313  R_AllocStaticTriSurfVerts( tri, totalVerts );
1314  memcpy( tri->verts, oldVerts, tri->numVerts * sizeof( tri->verts[0] ) );
1315  triVertexAllocator.Free( oldVerts );
1316 #endif
1317 
1318  // create the duplicates
1319  numMirror = 0;
1320  for ( i = 0 ; i < tri->numVerts ; i++ ) {
1321  j = tverts[i].negativeRemap;
1322  if ( j ) {
1323  tri->verts[j] = tri->verts[i];
1324  tri->mirroredVerts[numMirror] = i;
1325  numMirror++;
1326  }
1327  }
1328 
1329  tri->numVerts = totalVerts;
1330  // change the indexes
1331  for ( i = 0 ; i < tri->numIndexes ; i++ ) {
1332  if ( tverts[tri->indexes[i]].negativeRemap &&
1333  R_FaceNegativePolarity( tri, 3*(i/3) ) ) {
1334  tri->indexes[i] = tverts[tri->indexes[i]].negativeRemap;
1335  }
1336  }
1337 
1338  tri->numVerts = totalVerts;
1339 }
1340 
1341 /*
1342 =================
1343 R_DeriveTangentsWithoutNormals
1344 
1345 Build texture space tangents for bump mapping
1346 If a surface is deformed, this must be recalculated
1347 
1348 This assumes that any mirrored vertexes have already been duplicated, so
1349 any shared vertexes will have the tangent spaces smoothed across.
1350 
1351 Texture wrapping slightly complicates this, but as long as the normals
1352 are shared, and the tangent vectors are projected onto the normals, the
1353 separate vertexes should wind up with identical tangent spaces.
1354 
1355 mirroring a normalmap WILL cause a slightly visible seam unless the normals
1356 are completely flat around the edge's full bilerp support.
1357 
1358 Vertexes which are smooth shaded must have their tangent vectors
1359 in the same plane, which will allow a seamless
1360 rendering as long as the normal map is even on both sides of the
1361 seam.
1362 
1363 A smooth shaded surface may have multiple tangent vectors at a vertex
1364 due to texture seams or mirroring, but it should only have a single
1365 normal vector.
1366 
1367 Each triangle has a pair of tangent vectors in it's plane
1368 
1369 Should we consider having vertexes point at shared tangent spaces
1370 to save space or speed transforms?
1371 
1372 this version only handles bilateral symetry
1373 =================
1374 */
1376  int i, j;
1377  faceTangents_t *faceTangents;
1378  faceTangents_t *ft;
1379  idDrawVert *vert;
1380 
1381  faceTangents = (faceTangents_t *)_alloca16( sizeof(faceTangents[0]) * tri->numIndexes/3 );
1382  R_DeriveFaceTangents( tri, faceTangents );
1383 
1384  // clear the tangents
1385  for ( i = 0 ; i < tri->numVerts ; i++ ) {
1386  tri->verts[i].tangents[0].Zero();
1387  tri->verts[i].tangents[1].Zero();
1388  }
1389 
1390  // sum up the neighbors
1391  for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
1392  ft = &faceTangents[i/3];
1393 
1394  // for each vertex on this face
1395  for ( j = 0 ; j < 3 ; j++ ) {
1396  vert = &tri->verts[tri->indexes[i+j]];
1397 
1398  vert->tangents[0] += ft->tangents[0];
1399  vert->tangents[1] += ft->tangents[1];
1400  }
1401  }
1402 
1403 #if 0
1404  // sum up both sides of the mirrored verts
1405  // so the S vectors exactly mirror, and the T vectors are equal
1406  for ( i = 0 ; i < tri->numMirroredVerts ; i++ ) {
1407  idDrawVert *v1, *v2;
1408 
1409  v1 = &tri->verts[ tri->numVerts - tri->numMirroredVerts + i ];
1410  v2 = &tri->verts[ tri->mirroredVerts[i] ];
1411 
1412  v1->tangents[0] -= v2->tangents[0];
1413  v1->tangents[1] += v2->tangents[1];
1414 
1415  v2->tangents[0] = vec3_origin - v1->tangents[0];
1416  v2->tangents[1] = v1->tangents[1];
1417  }
1418 #endif
1419 
1420 
1421  // project the summed vectors onto the normal plane
1422  // and normalize. The tangent vectors will not necessarily
1423  // be orthogonal to each other, but they will be orthogonal
1424  // to the surface normal.
1425  for ( i = 0 ; i < tri->numVerts ; i++ ) {
1426  vert = &tri->verts[i];
1427  for ( j = 0 ; j < 2 ; j++ ) {
1428  float d;
1429 
1430  d = vert->tangents[j] * vert->normal;
1431  vert->tangents[j] = vert->tangents[j] - d * vert->normal;
1432  vert->tangents[j].Normalize();
1433  }
1434  }
1435 
1436  tri->tangentsCalculated = true;
1437 }
1438 
1439 static ID_INLINE void VectorNormalizeFast2( const idVec3 &v, idVec3 &out) {
1440  float ilength;
1441 
1442  ilength = idMath::RSqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] );
1443  out[0] = v[0] * ilength;
1444  out[1] = v[1] * ilength;
1445  out[2] = v[2] * ilength;
1446 }
1447 
1448 /*
1449 ===================
1450 R_BuildDominantTris
1451 
1452 Find the largest triangle that uses each vertex
1453 ===================
1454 */
1455 typedef struct {
1457  int faceNum;
1458 } indexSort_t;
1459 
1460 static int IndexSort( const void *a, const void *b ) {
1461  if ( ((indexSort_t *)a)->vertexNum < ((indexSort_t *)b)->vertexNum ) {
1462  return -1;
1463  }
1464  if ( ((indexSort_t *)a)->vertexNum > ((indexSort_t *)b)->vertexNum ) {
1465  return 1;
1466  }
1467  return 0;
1468 }
1469 
1471  int i, j;
1472  dominantTri_t *dt;
1473  indexSort_t *ind = (indexSort_t *)R_StaticAlloc( tri->numIndexes * sizeof( *ind ) );
1474 
1475  for ( i = 0; i < tri->numIndexes; i++ ) {
1476  ind[i].vertexNum = tri->indexes[i];
1477  ind[i].faceNum = i / 3;
1478  }
1479  qsort( ind, tri->numIndexes, sizeof( *ind ), IndexSort );
1480 
1481  tri->dominantTris = dt = triDominantTrisAllocator.Alloc( tri->numVerts );
1482  memset( dt, 0, tri->numVerts * sizeof( dt[0] ) );
1483 
1484  for ( i = 0; i < tri->numIndexes; i += j ) {
1485  float maxArea = 0;
1486  int vertNum = ind[i].vertexNum;
1487  for ( j = 0; i + j < tri->numIndexes && ind[i+j].vertexNum == vertNum; j++ ) {
1488  float d0[5], d1[5];
1489  idDrawVert *a, *b, *c;
1490  idVec3 normal, tangent, bitangent;
1491 
1492  int i1 = tri->indexes[ind[i+j].faceNum * 3 + 0];
1493  int i2 = tri->indexes[ind[i+j].faceNum * 3 + 1];
1494  int i3 = tri->indexes[ind[i+j].faceNum * 3 + 2];
1495 
1496  a = tri->verts + i1;
1497  b = tri->verts + i2;
1498  c = tri->verts + i3;
1499 
1500  d0[0] = b->xyz[0] - a->xyz[0];
1501  d0[1] = b->xyz[1] - a->xyz[1];
1502  d0[2] = b->xyz[2] - a->xyz[2];
1503  d0[3] = b->st[0] - a->st[0];
1504  d0[4] = b->st[1] - a->st[1];
1505 
1506  d1[0] = c->xyz[0] - a->xyz[0];
1507  d1[1] = c->xyz[1] - a->xyz[1];
1508  d1[2] = c->xyz[2] - a->xyz[2];
1509  d1[3] = c->st[0] - a->st[0];
1510  d1[4] = c->st[1] - a->st[1];
1511 
1512  normal[0] = ( d1[1] * d0[2] - d1[2] * d0[1] );
1513  normal[1] = ( d1[2] * d0[0] - d1[0] * d0[2] );
1514  normal[2] = ( d1[0] * d0[1] - d1[1] * d0[0] );
1515 
1516  float area = normal.Length();
1517 
1518  // if this is smaller than what we already have, skip it
1519  if ( area < maxArea ) {
1520  continue;
1521  }
1522  maxArea = area;
1523 
1524  if ( i1 == vertNum ) {
1525  dt[vertNum].v2 = i2;
1526  dt[vertNum].v3 = i3;
1527  } else if ( i2 == vertNum ) {
1528  dt[vertNum].v2 = i3;
1529  dt[vertNum].v3 = i1;
1530  } else {
1531  dt[vertNum].v2 = i1;
1532  dt[vertNum].v3 = i2;
1533  }
1534 
1535  float len = area;
1536  if ( len < 0.001f ) {
1537  len = 0.001f;
1538  }
1539  dt[vertNum].normalizationScale[2] = 1.0f / len; // normal
1540 
1541  // texture area
1542  area = d0[3] * d1[4] - d0[4] * d1[3];
1543 
1544  tangent[0] = ( d0[0] * d1[4] - d0[4] * d1[0] );
1545  tangent[1] = ( d0[1] * d1[4] - d0[4] * d1[1] );
1546  tangent[2] = ( d0[2] * d1[4] - d0[4] * d1[2] );
1547  len = tangent.Length();
1548  if ( len < 0.001f ) {
1549  len = 0.001f;
1550  }
1551  dt[vertNum].normalizationScale[0] = ( area > 0 ? 1 : -1 ) / len; // tangents[0]
1552 
1553  bitangent[0] = ( d0[3] * d1[0] - d0[0] * d1[3] );
1554  bitangent[1] = ( d0[3] * d1[1] - d0[1] * d1[3] );
1555  bitangent[2] = ( d0[3] * d1[2] - d0[2] * d1[3] );
1556  len = bitangent.Length();
1557  if ( len < 0.001f ) {
1558  len = 0.001f;
1559  }
1560 #ifdef DERIVE_UNSMOOTHED_BITANGENT
1561  dt[vertNum].normalizationScale[1] = ( area > 0 ? 1 : -1 );
1562 #else
1563  dt[vertNum].normalizationScale[1] = ( area > 0 ? 1 : -1 ) / len; // tangents[1]
1564 #endif
1565  }
1566  }
1567 
1568  R_StaticFree( ind );
1569 }
1570 
1571 /*
1572 ====================
1573 R_DeriveUnsmoothedTangents
1574 
1575 Uses the single largest area triangle for each vertex, instead of smoothing over all
1576 ====================
1577 */
1579  if ( tri->tangentsCalculated ) {
1580  return;
1581  }
1582 
1583 #if 1
1584 
1586 
1587 #else
1588 
1589  for ( int i = 0 ; i < tri->numVerts ; i++ ) {
1590  idVec3 temp;
1591  float d0[5], d1[5];
1592  idDrawVert *a, *b, *c;
1593  dominantTri_t *dt = &tri->dominantTris[i];
1594 
1595  a = tri->verts + i;
1596  b = tri->verts + dt->v2;
1597  c = tri->verts + dt->v3;
1598 
1599  d0[0] = b->xyz[0] - a->xyz[0];
1600  d0[1] = b->xyz[1] - a->xyz[1];
1601  d0[2] = b->xyz[2] - a->xyz[2];
1602  d0[3] = b->st[0] - a->st[0];
1603  d0[4] = b->st[1] - a->st[1];
1604 
1605  d1[0] = c->xyz[0] - a->xyz[0];
1606  d1[1] = c->xyz[1] - a->xyz[1];
1607  d1[2] = c->xyz[2] - a->xyz[2];
1608  d1[3] = c->st[0] - a->st[0];
1609  d1[4] = c->st[1] - a->st[1];
1610 
1611  a->normal[0] = dt->normalizationScale[2] * ( d1[1] * d0[2] - d1[2] * d0[1] );
1612  a->normal[1] = dt->normalizationScale[2] * ( d1[2] * d0[0] - d1[0] * d0[2] );
1613  a->normal[2] = dt->normalizationScale[2] * ( d1[0] * d0[1] - d1[1] * d0[0] );
1614 
1615  a->tangents[0][0] = dt->normalizationScale[0] * ( d0[0] * d1[4] - d0[4] * d1[0] );
1616  a->tangents[0][1] = dt->normalizationScale[0] * ( d0[1] * d1[4] - d0[4] * d1[1] );
1617  a->tangents[0][2] = dt->normalizationScale[0] * ( d0[2] * d1[4] - d0[4] * d1[2] );
1618 
1619 #ifdef DERIVE_UNSMOOTHED_BITANGENT
1620  // derive the bitangent for a completely orthogonal axis,
1621  // instead of using the texture T vector
1622  a->tangents[1][0] = dt->normalizationScale[1] * ( a->normal[2] * a->tangents[0][1] - a->normal[1] * a->tangents[0][2] );
1623  a->tangents[1][1] = dt->normalizationScale[1] * ( a->normal[0] * a->tangents[0][2] - a->normal[2] * a->tangents[0][0] );
1624  a->tangents[1][2] = dt->normalizationScale[1] * ( a->normal[1] * a->tangents[0][0] - a->normal[0] * a->tangents[0][1] );
1625 #else
1626  // calculate the bitangent from the texture T vector
1627  a->tangents[1][0] = dt->normalizationScale[1] * ( d0[3] * d1[0] - d0[0] * d1[3] );
1628  a->tangents[1][1] = dt->normalizationScale[1] * ( d0[3] * d1[1] - d0[1] * d1[3] );
1629  a->tangents[1][2] = dt->normalizationScale[1] * ( d0[3] * d1[2] - d0[2] * d1[3] );
1630 #endif
1631  }
1632 
1633 #endif
1634 
1635  tri->tangentsCalculated = true;
1636 }
1637 
1638 /*
1639 ==================
1640 R_DeriveTangents
1641 
1642 This is called once for static surfaces, and every frame for deforming surfaces
1643 
1644 Builds tangents, normals, and face planes
1645 ==================
1646 */
1647 void R_DeriveTangents( srfTriangles_t *tri, bool allocFacePlanes ) {
1648  int i;
1649  idPlane *planes;
1650 
1651  if ( tri->dominantTris != NULL ) {
1653  return;
1654  }
1655 
1656  if ( tri->tangentsCalculated ) {
1657  return;
1658  }
1659 
1660  tr.pc.c_tangentIndexes += tri->numIndexes;
1661 
1662  if ( !tri->facePlanes && allocFacePlanes ) {
1664  }
1665  planes = tri->facePlanes;
1666 
1667 #if 1
1668 
1669  if ( !planes ) {
1670  planes = (idPlane *)_alloca16( ( tri->numIndexes / 3 ) * sizeof( planes[0] ) );
1671  }
1672 
1673  SIMDProcessor->DeriveTangents( planes, tri->verts, tri->numVerts, tri->indexes, tri->numIndexes );
1674 
1675 #else
1676 
1677  for ( i = 0; i < tri->numVerts; i++ ) {
1678  tri->verts[i].normal.Zero();
1679  tri->verts[i].tangents[0].Zero();
1680  tri->verts[i].tangents[1].Zero();
1681  }
1682 
1683  for ( i = 0; i < tri->numIndexes; i += 3 ) {
1684  // make face tangents
1685  float d0[5], d1[5];
1686  idDrawVert *a, *b, *c;
1687  idVec3 temp, normal, tangents[2];
1688 
1689  a = tri->verts + tri->indexes[i + 0];
1690  b = tri->verts + tri->indexes[i + 1];
1691  c = tri->verts + tri->indexes[i + 2];
1692 
1693  d0[0] = b->xyz[0] - a->xyz[0];
1694  d0[1] = b->xyz[1] - a->xyz[1];
1695  d0[2] = b->xyz[2] - a->xyz[2];
1696  d0[3] = b->st[0] - a->st[0];
1697  d0[4] = b->st[1] - a->st[1];
1698 
1699  d1[0] = c->xyz[0] - a->xyz[0];
1700  d1[1] = c->xyz[1] - a->xyz[1];
1701  d1[2] = c->xyz[2] - a->xyz[2];
1702  d1[3] = c->st[0] - a->st[0];
1703  d1[4] = c->st[1] - a->st[1];
1704 
1705  // normal
1706  temp[0] = d1[1] * d0[2] - d1[2] * d0[1];
1707  temp[1] = d1[2] * d0[0] - d1[0] * d0[2];
1708  temp[2] = d1[0] * d0[1] - d1[1] * d0[0];
1709  VectorNormalizeFast2( temp, normal );
1710 
1711 #ifdef USE_INVA
1712  float area = d0[3] * d1[4] - d0[4] * d1[3];
1713  float inva = area < 0.0f ? -1 : 1; // was = 1.0f / area;
1714 
1715  temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]) * inva;
1716  temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]) * inva;
1717  temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]) * inva;
1718  VectorNormalizeFast2( temp, tangents[0] );
1719 
1720  temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]) * inva;
1721  temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]) * inva;
1722  temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]) * inva;
1723  VectorNormalizeFast2( temp, tangents[1] );
1724 #else
1725  temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]);
1726  temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]);
1727  temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]);
1728  VectorNormalizeFast2( temp, tangents[0] );
1729 
1730  temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]);
1731  temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]);
1732  temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]);
1733  VectorNormalizeFast2( temp, tangents[1] );
1734 #endif
1735 
1736  // sum up the tangents and normals for each vertex on this face
1737  for ( int j = 0 ; j < 3 ; j++ ) {
1738  vert = &tri->verts[tri->indexes[i+j]];
1739  vert->normal += normal;
1740  vert->tangents[0] += tangents[0];
1741  vert->tangents[1] += tangents[1];
1742  }
1743 
1744  if ( planes ) {
1745  planes->Normal() = normal;
1746  planes->FitThroughPoint( a->xyz );
1747  planes++;
1748  }
1749  }
1750 
1751 #endif
1752 
1753 #if 0
1754 
1755  if ( tri->silIndexes != NULL ) {
1756  for ( i = 0; i < tri->numVerts; i++ ) {
1757  tri->verts[i].normal.Zero();
1758  }
1759  for ( i = 0; i < tri->numIndexes; i++ ) {
1760  tri->verts[tri->silIndexes[i]].normal += planes[i/3].Normal();
1761  }
1762  for ( i = 0 ; i < tri->numIndexes ; i++ ) {
1763  tri->verts[tri->indexes[i]].normal = tri->verts[tri->silIndexes[i]].normal;
1764  }
1765  }
1766 
1767 #else
1768 
1769  int *dupVerts = tri->dupVerts;
1770  idDrawVert *verts = tri->verts;
1771 
1772  // add the normal of a duplicated vertex to the normal of the first vertex with the same XYZ
1773  for ( i = 0; i < tri->numDupVerts; i++ ) {
1774  verts[dupVerts[i*2+0]].normal += verts[dupVerts[i*2+1]].normal;
1775  }
1776 
1777  // copy vertex normals to duplicated vertices
1778  for ( i = 0; i < tri->numDupVerts; i++ ) {
1779  verts[dupVerts[i*2+1]].normal = verts[dupVerts[i*2+0]].normal;
1780  }
1781 
1782 #endif
1783 
1784 #if 0
1785  // sum up both sides of the mirrored verts
1786  // so the S vectors exactly mirror, and the T vectors are equal
1787  for ( i = 0 ; i < tri->numMirroredVerts ; i++ ) {
1788  idDrawVert *v1, *v2;
1789 
1790  v1 = &tri->verts[ tri->numVerts - tri->numMirroredVerts + i ];
1791  v2 = &tri->verts[ tri->mirroredVerts[i] ];
1792 
1793  v1->tangents[0] -= v2->tangents[0];
1794  v1->tangents[1] += v2->tangents[1];
1795 
1796  v2->tangents[0] = vec3_origin - v1->tangents[0];
1797  v2->tangents[1] = v1->tangents[1];
1798  }
1799 #endif
1800 
1801  // project the summed vectors onto the normal plane
1802  // and normalize. The tangent vectors will not necessarily
1803  // be orthogonal to each other, but they will be orthogonal
1804  // to the surface normal.
1805 #if 1
1806 
1808 
1809 #else
1810 
1811  for ( i = 0 ; i < tri->numVerts ; i++ ) {
1812  idDrawVert *vert = &tri->verts[i];
1813 
1814  VectorNormalizeFast2( vert->normal, vert->normal );
1815 
1816  // project the tangent vectors
1817  for ( int j = 0 ; j < 2 ; j++ ) {
1818  float d;
1819 
1820  d = vert->tangents[j] * vert->normal;
1821  vert->tangents[j] = vert->tangents[j] - d * vert->normal;
1822  VectorNormalizeFast2( vert->tangents[j], vert->tangents[j] );
1823  }
1824  }
1825 
1826 #endif
1827 
1828  tri->tangentsCalculated = true;
1829  tri->facePlanesCalculated = true;
1830 }
1831 
1832 /*
1833 =================
1834 R_RemoveDuplicatedTriangles
1835 
1836 silIndexes must have already been calculated
1837 
1838 silIndexes are used instead of indexes, because duplicated
1839 triangles could have different texture coordinates.
1840 =================
1841 */
1843  int c_removed;
1844  int i, j, r;
1845  int a, b, c;
1846 
1847  c_removed = 0;
1848 
1849  // check for completely duplicated triangles
1850  // any rotation of the triangle is still the same, but a mirroring
1851  // is considered different
1852  for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
1853  for ( r = 0 ; r < 3 ; r++ ) {
1854  a = tri->silIndexes[i+r];
1855  b = tri->silIndexes[i+(r+1)%3];
1856  c = tri->silIndexes[i+(r+2)%3];
1857  for ( j = i + 3 ; j < tri->numIndexes ; j+=3 ) {
1858  if ( tri->silIndexes[j] == a && tri->silIndexes[j+1] == b && tri->silIndexes[j+2] == c ) {
1859  c_removed++;
1860  memmove( tri->indexes + j, tri->indexes + j + 3, ( tri->numIndexes - j - 3 ) * sizeof( tri->indexes[0] ) );
1861  memmove( tri->silIndexes + j, tri->silIndexes + j + 3, ( tri->numIndexes - j - 3 ) * sizeof( tri->silIndexes[0] ) );
1862  tri->numIndexes -= 3;
1863  j -= 3;
1864  }
1865  }
1866  }
1867  }
1868 
1869  if ( c_removed ) {
1870  common->Printf( "removed %i duplicated triangles\n", c_removed );
1871  }
1872 
1873 }
1874 
1875 /*
1876 =================
1877 R_RemoveDegenerateTriangles
1878 
1879 silIndexes must have already been calculated
1880 =================
1881 */
1883  int c_removed;
1884  int i;
1885  int a, b, c;
1886 
1887  // check for completely degenerate triangles
1888  c_removed = 0;
1889  for ( i = 0; i < tri->numIndexes; i += 3 ) {
1890  a = tri->silIndexes[i];
1891  b = tri->silIndexes[i+1];
1892  c = tri->silIndexes[i+2];
1893  if ( a == b || a == c || b == c ) {
1894  c_removed++;
1895  memmove( tri->indexes + i, tri->indexes + i + 3, ( tri->numIndexes - i - 3 ) * sizeof( tri->indexes[0] ) );
1896  if ( tri->silIndexes ) {
1897  memmove( tri->silIndexes + i, tri->silIndexes + i + 3, ( tri->numIndexes - i - 3 ) * sizeof( tri->silIndexes[0] ) );
1898  }
1899  tri->numIndexes -= 3;
1900  i -= 3;
1901  }
1902  }
1903 
1904  // this doesn't free the memory used by the unused verts
1905 
1906  if ( c_removed ) {
1907  common->Printf( "removed %i degenerate triangles\n", c_removed );
1908  }
1909 }
1910 
1911 /*
1912 =================
1913 R_TestDegenerateTextureSpace
1914 =================
1915 */
1917  int c_degenerate;
1918  int i;
1919 
1920  // check for triangles with a degenerate texture space
1921  c_degenerate = 0;
1922  for ( i = 0; i < tri->numIndexes; i += 3 ) {
1923  const idDrawVert &a = tri->verts[tri->indexes[i+0]];
1924  const idDrawVert &b = tri->verts[tri->indexes[i+1]];
1925  const idDrawVert &c = tri->verts[tri->indexes[i+2]];
1926 
1927  if ( a.st == b.st || b.st == c.st || c.st == a.st ) {
1928  c_degenerate++;
1929  }
1930  }
1931 
1932  if ( c_degenerate ) {
1933 // common->Printf( "%d triangles with a degenerate texture space\n", c_degenerate );
1934  }
1935 }
1936 
1937 /*
1938 =================
1939 R_RemoveUnusedVerts
1940 =================
1941 */
1943  int i;
1944  int *mark;
1945  int index;
1946  int used;
1947 
1948  mark = (int *)R_ClearedStaticAlloc( tri->numVerts * sizeof( *mark ) );
1949 
1950  for ( i = 0 ; i < tri->numIndexes ; i++ ) {
1951  index = tri->indexes[i];
1952  if ( index < 0 || index >= tri->numVerts ) {
1953  common->Error( "R_RemoveUnusedVerts: bad index" );
1954  }
1955  mark[ index ] = 1;
1956 
1957  if ( tri->silIndexes ) {
1958  index = tri->silIndexes[i];
1959  if ( index < 0 || index >= tri->numVerts ) {
1960  common->Error( "R_RemoveUnusedVerts: bad index" );
1961  }
1962  mark[ index ] = 1;
1963  }
1964  }
1965 
1966  used = 0;
1967  for ( i = 0 ; i < tri->numVerts ; i++ ) {
1968  if ( !mark[i] ) {
1969  continue;
1970  }
1971  mark[i] = used + 1;
1972  used++;
1973  }
1974 
1975  if ( used != tri->numVerts ) {
1976  for ( i = 0 ; i < tri->numIndexes ; i++ ) {
1977  tri->indexes[i] = mark[ tri->indexes[i] ] - 1;
1978  if ( tri->silIndexes ) {
1979  tri->silIndexes[i] = mark[ tri->silIndexes[i] ] - 1;
1980  }
1981  }
1982  tri->numVerts = used;
1983 
1984  for ( i = 0 ; i < tri->numVerts ; i++ ) {
1985  index = mark[ i ];
1986  if ( !index ) {
1987  continue;
1988  }
1989  tri->verts[ index - 1 ] = tri->verts[i];
1990  }
1991 
1992  // this doesn't realloc the arrays to save the memory used by the unused verts
1993  }
1994 
1995  R_StaticFree( mark );
1996 }
1997 
1998 /*
1999 =================
2000 R_MergeSurfaceList
2001 
2002 Only deals with vertexes and indexes, not silhouettes, planes, etc.
2003 Does NOT perform a cleanup triangles, so there may be duplicated verts in the result.
2004 =================
2005 */
2006 srfTriangles_t *R_MergeSurfaceList( const srfTriangles_t **surfaces, int numSurfaces ) {
2007  srfTriangles_t *newTri;
2008  const srfTriangles_t *tri;
2009  int i, j;
2010  int totalVerts;
2011  int totalIndexes;
2012 
2013  totalVerts = 0;
2014  totalIndexes = 0;
2015  for ( i = 0 ; i < numSurfaces ; i++ ) {
2016  totalVerts += surfaces[i]->numVerts;
2017  totalIndexes += surfaces[i]->numIndexes;
2018  }
2019 
2020  newTri = R_AllocStaticTriSurf();
2021  newTri->numVerts = totalVerts;
2022  newTri->numIndexes = totalIndexes;
2023  R_AllocStaticTriSurfVerts( newTri, newTri->numVerts );
2024  R_AllocStaticTriSurfIndexes( newTri, newTri->numIndexes );
2025 
2026  totalVerts = 0;
2027  totalIndexes = 0;
2028  for ( i = 0 ; i < numSurfaces ; i++ ) {
2029  tri = surfaces[i];
2030  memcpy( newTri->verts + totalVerts, tri->verts, tri->numVerts * sizeof( *tri->verts ) );
2031  for ( j = 0 ; j < tri->numIndexes ; j++ ) {
2032  newTri->indexes[ totalIndexes + j ] = totalVerts + tri->indexes[j];
2033  }
2034  totalVerts += tri->numVerts;
2035  totalIndexes += tri->numIndexes;
2036  }
2037 
2038  return newTri;
2039 }
2040 
2041 /*
2042 =================
2043 R_MergeTriangles
2044 
2045 Only deals with vertexes and indexes, not silhouettes, planes, etc.
2046 Does NOT perform a cleanup triangles, so there may be duplicated verts in the result.
2047 =================
2048 */
2050  const srfTriangles_t *tris[2];
2051 
2052  tris[0] = tri1;
2053  tris[1] = tri2;
2054 
2055  return R_MergeSurfaceList( tris, 2 );
2056 }
2057 
2058 /*
2059 =================
2060 R_ReverseTriangles
2061 
2062 Lit two sided surfaces need to have the triangles actually duplicated,
2063 they can't just turn on two sided lighting, because the normal and tangents
2064 are wrong on the other sides.
2065 
2066 This should be called before R_CleanupTriangles
2067 =================
2068 */
2070  int i;
2071 
2072  // flip the normal on each vertex
2073  // If the surface is going to have generated normals, this won't matter,
2074  // but if it has explicit normals, this will keep it on the correct side
2075  for ( i = 0 ; i < tri->numVerts ; i++ ) {
2076  tri->verts[i].normal = vec3_origin - tri->verts[i].normal;
2077  }
2078 
2079  // flip the index order to make them back sided
2080  for ( i = 0 ; i < tri->numIndexes ; i+= 3 ) {
2081  glIndex_t temp;
2082 
2083  temp = tri->indexes[ i + 0 ];
2084  tri->indexes[ i + 0 ] = tri->indexes[ i + 1 ];
2085  tri->indexes[ i + 1 ] = temp;
2086  }
2087 }
2088 
2089 /*
2090 =================
2091 R_CleanupTriangles
2092 
2093 FIXME: allow createFlat and createSmooth normals, as well as explicit
2094 =================
2095 */
2096 void R_CleanupTriangles( srfTriangles_t *tri, bool createNormals, bool identifySilEdges, bool useUnsmoothedTangents ) {
2097  R_RangeCheckIndexes( tri );
2098 
2099  R_CreateSilIndexes( tri );
2100 
2101 // R_RemoveDuplicatedTriangles( tri ); // this may remove valid overlapped transparent triangles
2102 
2104 
2106 
2107 // R_RemoveUnusedVerts( tri );
2108 
2109  if ( identifySilEdges ) {
2110  R_IdentifySilEdges( tri, true ); // assume it is non-deformable, and omit coplanar edges
2111  }
2112 
2113  // bust vertexes that share a mirrored edge into separate vertexes
2114  R_DuplicateMirroredVertexes( tri );
2115 
2116  // optimize the index order (not working?)
2117 // R_OrderIndexes( tri->numIndexes, tri->indexes );
2118 
2119  R_CreateDupVerts( tri );
2120 
2121  R_BoundTriSurf( tri );
2122 
2123  if ( useUnsmoothedTangents ) {
2124  R_BuildDominantTris( tri );
2126  } else if ( !createNormals ) {
2127  R_DeriveFacePlanes( tri );
2129  } else {
2130  R_DeriveTangents( tri );
2131  }
2132 }
2133 
2134 /*
2135 ===================================================================================
2136 
2137 DEFORMED SURFACES
2138 
2139 ===================================================================================
2140 */
2141 
2142 /*
2143 ===================
2144 R_BuildDeformInfo
2145 ===================
2146 */
2147 deformInfo_t *R_BuildDeformInfo( int numVerts, const idDrawVert *verts, int numIndexes, const int *indexes, bool useUnsmoothedTangents ) {
2148  deformInfo_t *deform;
2149  srfTriangles_t tri;
2150  int i;
2151 
2152  memset( &tri, 0, sizeof( tri ) );
2153 
2154  tri.numVerts = numVerts;
2155  R_AllocStaticTriSurfVerts( &tri, tri.numVerts );
2156  SIMDProcessor->Memcpy( tri.verts, verts, tri.numVerts * sizeof( tri.verts[0] ) );
2157 
2158  tri.numIndexes = numIndexes;
2160 
2161  // don't memcpy, so we can change the index type from int to short without changing the interface
2162  for ( i = 0 ; i < tri.numIndexes ; i++ ) {
2163  tri.indexes[i] = indexes[i];
2164  }
2165 
2166  R_RangeCheckIndexes( &tri );
2167  R_CreateSilIndexes( &tri );
2168 
2169 // should we order the indexes here?
2170 
2171 // R_RemoveDuplicatedTriangles( &tri );
2172 // R_RemoveDegenerateTriangles( &tri );
2173 // R_RemoveUnusedVerts( &tri );
2174  R_IdentifySilEdges( &tri, false ); // we cannot remove coplanar edges, because
2175  // they can deform to silhouettes
2176 
2177  R_DuplicateMirroredVertexes( &tri ); // split mirror points into multiple points
2178 
2179  R_CreateDupVerts( &tri );
2180 
2181  if ( useUnsmoothedTangents ) {
2182  R_BuildDominantTris( &tri );
2183  }
2184 
2185  deform = (deformInfo_t *)R_ClearedStaticAlloc( sizeof( *deform ) );
2186 
2187  deform->numSourceVerts = numVerts;
2188  deform->numOutputVerts = tri.numVerts;
2189 
2190  deform->numIndexes = numIndexes;
2191  deform->indexes = tri.indexes;
2192 
2193  deform->silIndexes = tri.silIndexes;
2194 
2195  deform->numSilEdges = tri.numSilEdges;
2196  deform->silEdges = tri.silEdges;
2197 
2198  deform->dominantTris = tri.dominantTris;
2199 
2200  deform->numMirroredVerts = tri.numMirroredVerts;
2201  deform->mirroredVerts = tri.mirroredVerts;
2202 
2203  deform->numDupVerts = tri.numDupVerts;
2204  deform->dupVerts = tri.dupVerts;
2205 
2206  if ( tri.verts ) {
2207  triVertexAllocator.Free( tri.verts );
2208  }
2209 
2210  if ( tri.facePlanes ) {
2211  triPlaneAllocator.Free( tri.facePlanes );
2212  }
2213 
2214  return deform;
2215 }
2216 
2217 /*
2218 ===================
2219 R_FreeDeformInfo
2220 ===================
2221 */
2222 void R_FreeDeformInfo( deformInfo_t *deformInfo ) {
2223  if ( deformInfo->indexes != NULL ) {
2224  triIndexAllocator.Free( deformInfo->indexes );
2225  }
2226  if ( deformInfo->silIndexes != NULL ) {
2227  triSilIndexAllocator.Free( deformInfo->silIndexes );
2228  }
2229  if ( deformInfo->silEdges != NULL ) {
2230  triSilEdgeAllocator.Free( deformInfo->silEdges );
2231  }
2232  if ( deformInfo->dominantTris != NULL ) {
2233  triDominantTrisAllocator.Free( deformInfo->dominantTris );
2234  }
2235  if ( deformInfo->mirroredVerts != NULL ) {
2236  triMirroredVertAllocator.Free( deformInfo->mirroredVerts );
2237  }
2238  if ( deformInfo->dupVerts != NULL ) {
2239  triDupVertAllocator.Free( deformInfo->dupVerts );
2240  }
2241  R_StaticFree( deformInfo );
2242 }
2243 
2244 /*
2245 ===================
2246 R_DeformInfoMemoryUsed
2247 ===================
2248 */
2250  int total = 0;
2251 
2252  if ( deformInfo->indexes != NULL ) {
2253  total += deformInfo->numIndexes * sizeof( deformInfo->indexes[0] );
2254  }
2255  if ( deformInfo->silIndexes != NULL ) {
2256  total += deformInfo->numIndexes * sizeof( deformInfo->silIndexes[0] );
2257  }
2258  if ( deformInfo->silEdges != NULL ) {
2259  total += deformInfo->numSilEdges * sizeof( deformInfo->silEdges[0] );
2260  }
2261  if ( deformInfo->dominantTris != NULL ) {
2262  total += deformInfo->numSourceVerts * sizeof( deformInfo->dominantTris[0] );
2263  }
2264  if ( deformInfo->mirroredVerts != NULL ) {
2265  total += deformInfo->numMirroredVerts * sizeof( deformInfo->mirroredVerts[0] );
2266  }
2267  if ( deformInfo->dupVerts != NULL ) {
2268  total += deformInfo->numDupVerts * sizeof( deformInfo->dupVerts[0] );
2269  }
2270 
2271  total += sizeof( *deformInfo );
2272  return total;
2273 }
2274 
int R_TriSurfMemory(const srfTriangles_t *tri)
Definition: tr_trisurf.cpp:290
glIndex_t p1
Definition: Model.h:64
int GetNumFreeBlocks(void) const
Definition: Heap.h:268
void R_FreeDeferredTriSurfs(frameData_t *frame)
Definition: tr_trisurf.cpp:466
float Normalize(void)
Definition: Vector.h:646
assert(prefInfo.fullscreenBtn)
void R_FreeStaticTriSurf(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:489
bool FromPoints(const idVec3 &p1, const idVec3 &p2, const idVec3 &p3, bool fixDegenerate=true)
Definition: Plane.h:279
const idVec3 & Normal(void) const
Definition: Plane.h:239
int Next(const int index) const
Definition: HashIndex.h:247
void R_PurgeTriSurfData(frameData_t *frame)
Definition: tr_trisurf.cpp:208
void R_ReallyFreeStaticTriSurf(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:374
glIndex_t * silIndexes
Definition: Model.h:104
void R_ResizeStaticTriSurfShadowVerts(srfTriangles_t *tri, int numVerts)
Definition: tr_trisurf.cpp:623
int numVerts
Definition: Model.h:98
void R_CreateVertexNormals(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:891
void R_ReferenceStaticTriSurfVerts(srfTriangles_t *tri, const srfTriangles_t *reference)
Definition: tr_trisurf.cpp:636
const GLdouble * v
Definition: glext.h:2936
virtual void VPCALL DeriveTangents(idPlane *planes, idDrawVert *verts, const int numVerts, const int *indexes, const int numIndexes)=0
srfTriangles_t * R_CopyStaticTriSurf(const srfTriangles_t *tri)
Definition: tr_trisurf.cpp:536
type * Alloc(const int num)
Definition: Heap.h:303
void R_ReferenceStaticTriSurfIndexes(srfTriangles_t *tri, const srfTriangles_t *reference)
Definition: tr_trisurf.cpp:645
void R_RangeCheckIndexes(const srfTriangles_t *tri)
Definition: tr_trisurf.cpp:671
float Distance(const idVec3 &v) const
Definition: Plane.h:324
bool polarityUsed[2]
idVec3 xyz
Definition: DrawVert.h:42
void R_CleanupTriangles(srfTriangles_t *tri, bool createNormals, bool identifySilEdges, bool useUnsmoothedTangents)
float z
Definition: Vector.h:320
int numDupVerts
Definition: Model.h:109
void R_CheckStaticTriSurfMemory(const srfTriangles_t *tri)
Definition: tr_trisurf.cpp:432
idVec3 tangents[2]
Definition: DrawVert.h:45
int GetBaseBlockMemory(void) const
Definition: Heap.h:265
glIndex_t * indexes
Definition: tr_local.h:1526
void R_BuildDominantTris(srfTriangles_t *tri)
Definition: Vector.h:316
bool facePlanesCalculated
Definition: Model.h:93
int numMirroredVerts
Definition: tr_local.h:1522
performanceCounters_t pc
Definition: tr_local.h:788
glIndex_t p2
Definition: Model.h:64
bool perfectHull
Definition: Model.h:94
type * Resize(type *ptr, const int num)
Definition: Heap.h:314
silEdge_t * silEdges
Definition: Model.h:113
idPlane * facePlanes
Definition: Model.h:115
void Init(void)
Definition: Heap.h:294
void R_ResizeStaticTriSurfVerts(srfTriangles_t *tri, int numVerts)
Definition: tr_trisurf.cpp:597
frameData_t * frameData
Definition: tr_backend.cpp:34
bool tangentsCalculated
Definition: Model.h:92
glIndex_t v3
Definition: Model.h:70
glIndex_t * silIndexes
Definition: tr_local.h:1528
GLenum GLsizei len
Definition: glext.h:3472
float x
Definition: Vector.h:318
srfTriangles_t * R_MergeTriangles(const srfTriangles_t *tri1, const srfTriangles_t *tri2)
struct vertCache_s * ambientCache
Definition: Model.h:137
int i
Definition: process.py:33
void R_FreeStaticTriSurfSilIndexes(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:654
void R_DeriveTangents(srfTriangles_t *tri, bool allocFacePlanes)
void Free(void)
Definition: HashIndex.cpp:75
void SetLockMemory(bool lock)
Definition: Heap.h:256
int GetNumEmptyBaseBlocks(void) const
Definition: Heap.h:270
srfTriangles_t * R_AllocStaticTriSurf(void)
Definition: tr_trisurf.cpp:523
int First(const int key) const
Definition: HashIndex.h:238
struct srfTriangles_s * ambientSurface
Definition: Model.h:130
idVec3 tangents[2]
int * dupVerts
Definition: tr_local.h:1531
int numSilEdges
Definition: Model.h:112
void R_IdentifySilEdges(srfTriangles_t *tri, bool omitCoplanarEdges)
idVec2 st
Definition: DrawVert.h:43
GLfloat GLfloat GLfloat v2
Definition: glext.h:3608
void R_ReverseTriangles(srfTriangles_t *tri)
glIndex_t v2
Definition: Model.h:65
float normalizationScale[3]
Definition: Model.h:71
virtual void VPCALL MinMax(float &min, float &max, const float *src, const int count)=0
int numMirroredVerts
Definition: Model.h:106
int c_coplanarSilEdges
idCVar r_useSilRemap("r_useSilRemap","1", CVAR_RENDERER|CVAR_BOOL,"consider verts with the same XYZ, but different ST the same for shadows")
GLuint index
Definition: glext.h:3476
const GLubyte * c
Definition: glext.h:4677
srfTriangles_t * firstDeferredFreeTriSurf
Definition: tr_local.h:534
float Length(void) const
Definition: Vector.h:631
void R_AllocStaticTriSurfShadowVerts(srfTriangles_t *tri, int numVerts)
Definition: tr_trisurf.cpp:575
const int SILEDGE_HASH_SIZE
Definition: tr_trisurf.cpp:120
idVec3 vec3_origin(0.0f, 0.0f, 0.0f)
idBounds bounds
Definition: Model.h:87
type * Alloc(void)
Definition: Heap.h:197
virtual void VPCALL DeriveTriPlanes(idPlane *planes, const idDrawVert *verts, const int numVerts, const int *indexes, const int numIndexes)=0
virtual void virtual void virtual void DWarning(const char *fmt,...) id_attribute((format(printf
idCommon * common
Definition: Common.cpp:206
bool deformedSurface
Definition: Model.h:95
void R_AllocStaticTriSurfPlanes(srfTriangles_t *tri, int numIndexes)
Definition: tr_trisurf.cpp:585
#define NULL
Definition: Lib.h:88
void Shutdown(void)
Definition: Heap.h:224
int glIndex_t
Definition: Model.h:52
float y
Definition: Vector.h:319
const char * CheckMemory(const type *ptr) const
Definition: Heap.h:341
Definition: Plane.h:71
void R_RemoveDuplicatedTriangles(srfTriangles_t *tri)
#define LIGHT_TRIS_DEFERRED
Definition: Interaction.h:44
struct vertCache_s * lightingCache
Definition: Model.h:138
int GetAllocCount(void) const
Definition: Heap.h:165
srfTriangles_t * R_MergeSurfaceList(const srfTriangles_t **surfaces, int numSurfaces)
idVec3 normal
Definition: DrawVert.h:44
shadowCache_t * shadowVertexes
Definition: Model.h:127
struct vertCache_s * shadowCache
Definition: Model.h:139
GLubyte GLubyte GLubyte a
Definition: glext.h:4662
void R_AllocStaticTriSurfIndexes(srfTriangles_t *tri, int numIndexes)
Definition: tr_trisurf.cpp:565
virtual void Printf(const char *fmt,...) id_attribute((format(printf
int numSilEdges
Definition: tr_local.h:1533
const int MAX_SIL_EDGES
Definition: tr_trisurf.cpp:119
int numSourceVerts
Definition: tr_local.h:1515
GLfloat GLfloat v1
Definition: glext.h:3607
int * mirroredVerts
Definition: Model.h:107
void Free(type *ptr)
Definition: Heap.h:332
virtual void VPCALL DeriveUnsmoothedTangents(idDrawVert *verts, const dominantTri_s *dominantTris, const int numVerts)=0
virtual void VPCALL Memcpy(void *dst, const void *src, const int count)=0
void R_ShowTriSurfMemory_f(const idCmdArgs &args)
Definition: tr_trisurf.cpp:229
void Shutdown(void)
Definition: Heap.h:298
int GenerateKey(const char *string, bool caseSensitive=true) const
Definition: HashIndex.h:379
void R_FreeStaticTriSurfVertexCaches(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:343
GLubyte GLubyte b
Definition: glext.h:4662
void FreeEmptyBaseBlocks(void)
Definition: Heap.h:257
GLfloat GLfloat GLfloat GLfloat v3
Definition: glext.h:3609
GLdouble GLdouble GLdouble r
Definition: glext.h:2951
void R_DeriveTangentsWithoutNormals(srfTriangles_t *tri)
int R_DeformInfoMemoryUsed(deformInfo_t *deformInfo)
void R_TestDegenerateTextureSpace(srfTriangles_t *tri)
glIndex_t v2
Definition: Model.h:70
void R_RemoveDegenerateTriangles(srfTriangles_t *tri)
bool GetBool(void) const
Definition: CVarSystem.h:142
tuple f
Definition: idal.py:89
GLint GLint i2
Definition: qgl.h:261
int numDupVerts
Definition: tr_local.h:1530
glIndex_t v1
Definition: Model.h:65
void R_ShutdownTriSurfData(void)
Definition: tr_trisurf.cpp:188
void * R_ClearedStaticAlloc(int bytes)
Definition: tr_main.cpp:322
void * R_StaticAlloc(int bytes)
Definition: tr_main.cpp:301
int numIndexes
Definition: tr_local.h:1525
virtual void VPCALL NormalizeTangents(idDrawVert *verts, const int numVerts)=0
void R_FreeDeformInfo(deformInfo_t *deformInfo)
GLint i1
Definition: qgl.h:261
void R_CreateDupVerts(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:790
void R_ResizeStaticTriSurfIndexes(srfTriangles_t *tri, int numIndexes)
Definition: tr_trisurf.cpp:610
void R_InitTriSurfData(void)
Definition: tr_trisurf.cpp:157
glIndex_t * indexes
Definition: Model.h:102
idVertexCache vertexCache
Definition: VertexCache.cpp:41
dominantTri_t * dominantTris
Definition: tr_local.h:1536
idRenderSystemLocal tr
void Clear(void)
Definition: HashIndex.h:328
void R_DeriveFacePlanes(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:827
struct srfTriangles_s * nextDeferredFree
Definition: Model.h:133
deformInfo_t * R_BuildDeformInfo(int numVerts, const idDrawVert *verts, int numIndexes, const int *indexes, bool useUnsmoothedTangents)
void Free(type *element)
Definition: Heap.h:216
void R_CreateSilIndexes(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:765
void Add(const int key, const int index)
Definition: HashIndex.h:193
int GetFreeBlockMemory(void) const
Definition: Heap.h:269
silEdge_t * silEdges
Definition: tr_local.h:1534
int * dupVerts
Definition: Model.h:110
void R_AllocStaticTriSurfVerts(srfTriangles_t *tri, int numVerts)
Definition: tr_trisurf.cpp:555
GLint j
Definition: qgl.h:264
void R_DeriveUnsmoothedTangents(srfTriangles_t *tri)
void Free(vertCache_t *buffer)
int numIndexes
Definition: Model.h:101
int * mirroredVerts
Definition: tr_local.h:1523
srfTriangles_t * lastDeferredFreeTriSurf
Definition: tr_local.h:535
struct vertCache_s * indexCache
Definition: Model.h:136
struct srfTriangles_s srfTriangles_t
void R_StaticFree(void *data)
Definition: tr_main.cpp:335
virtual void Error(const char *fmt,...) id_attribute((format(printf
void Zero(void)
Definition: Vector.h:415
int c_totalSilEdges
idDrawVert * verts
Definition: Model.h:99
static float RSqrt(float x)
Definition: Math.h:241
int numOutputVerts
Definition: tr_local.h:1520
void R_RemoveUnusedVerts(srfTriangles_t *tri)
void FitThroughPoint(const idVec3 &p)
Definition: Plane.h:297
idSIMDProcessor * SIMDProcessor
Definition: Simd.cpp:43
void R_BoundTriSurf(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:704
dominantTri_t * dominantTris
Definition: Model.h:117