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 "sys/platform.h"
30 #include "renderer/ModelManager.h"
31
32 #include "tools/compilers/dmap/dmap.h"
33
34 /*
35
36 T junction fixing never creates more xyz points, but
37 new vertexes will be created when different surfaces
38 cause a fix
39
40 The vertex cleaning accomplishes two goals: removing extranious low order
41 bits to avoid numbers like 1.000001233, and grouping nearby vertexes
42 together. Straight truncation accomplishes the first foal, but two vertexes
43 only a tiny epsilon apart could still be spread to different snap points.
44 To avoid this, we allow the merge test to group points together that
45 snapped to neighboring integer coordinates.
46
47 Snaping verts can drag some triangles backwards or collapse them to points,
48 which will cause them to be removed.
49
50
51 When snapping to ints, a point can move a maximum of sqrt(3)/2 distance
52 Two points that were an epsilon apart can then become sqrt(3) apart
53
54 A case that causes recursive overflow with point to triangle fixing:
55
56 A
57 C D
58 B
59
60 Triangle ABC tests against point D and splits into triangles ADC and DBC
61 Triangle DBC then tests against point A again and splits into ABC and ADB
62 infinite recursive loop
63
64
65 For a given source triangle
66 init the no-check list to hold the three triangle hashVerts
67
68 recursiveFixTriAgainstHash
69
70 recursiveFixTriAgainstHashVert_r
71 if hashVert is on the no-check list
72 exit
73 if the hashVert should split the triangle
74 add to the no-check list
75 recursiveFixTriAgainstHash(a)
76 recursiveFixTriAgainstHash(b)
77
78 */
79
80 #define SNAP_FRACTIONS 32
81 //#define SNAP_FRACTIONS 8
82 //#define SNAP_FRACTIONS 1
83
84 #define VERTEX_EPSILON ( 1.0 / SNAP_FRACTIONS )
85
86 #define COLINEAR_EPSILON ( 1.8 * VERTEX_EPSILON )
87
88 #define HASH_BINS 16
89
90 typedef struct hashVert_s {
91 struct hashVert_s *next;
92 idVec3 v;
93 int iv[3];
94 } hashVert_t;
95
96 static idBounds hashBounds;
97 static idVec3 hashScale;
98 static hashVert_t *hashVerts[HASH_BINS][HASH_BINS][HASH_BINS];
99 static int numHashVerts, numTotalVerts;
100 static int hashIntMins[3], hashIntScale[3];
101
102 /*
103 ===============
104 GetHashVert
105
106 Also modifies the original vert to the snapped value
107 ===============
108 */
GetHashVert(idVec3 & v)109 struct hashVert_s *GetHashVert( idVec3 &v ) {
110 int iv[3];
111 int block[3];
112 int i;
113 hashVert_t *hv;
114
115 numTotalVerts++;
116
117 // snap the vert to integral values
118 for ( i = 0 ; i < 3 ; i++ ) {
119 iv[i] = floor( ( v[i] + 0.5/SNAP_FRACTIONS ) * SNAP_FRACTIONS );
120 block[i] = ( iv[i] - hashIntMins[i] ) / hashIntScale[i];
121 if ( block[i] < 0 ) {
122 block[i] = 0;
123 } else if ( block[i] >= HASH_BINS ) {
124 block[i] = HASH_BINS - 1;
125 }
126 }
127
128 // see if a vertex near enough already exists
129 // this could still fail to find a near neighbor right at the hash block boundary
130 for ( hv = hashVerts[block[0]][block[1]][block[2]] ; hv ; hv = hv->next ) {
131 #if 0
132 if ( hv->iv[0] == iv[0] && hv->iv[1] == iv[1] && hv->iv[2] == iv[2] ) {
133 VectorCopy( hv->v, v );
134 return hv;
135 }
136 #else
137 for ( i = 0 ; i < 3 ; i++ ) {
138 int d;
139 d = hv->iv[i] - iv[i];
140 if ( d < -1 || d > 1 ) {
141 break;
142 }
143 }
144 if ( i == 3 ) {
145 VectorCopy( hv->v, v );
146 return hv;
147 }
148 #endif
149 }
150
151 // create a new one
152 hv = (hashVert_t *)Mem_Alloc( sizeof( *hv ) );
153
154 hv->next = hashVerts[block[0]][block[1]][block[2]];
155 hashVerts[block[0]][block[1]][block[2]] = hv;
156
157 hv->iv[0] = iv[0];
158 hv->iv[1] = iv[1];
159 hv->iv[2] = iv[2];
160
161 hv->v[0] = (float)iv[0] / SNAP_FRACTIONS;
162 hv->v[1] = (float)iv[1] / SNAP_FRACTIONS;
163 hv->v[2] = (float)iv[2] / SNAP_FRACTIONS;
164
165 VectorCopy( hv->v, v );
166
167 numHashVerts++;
168
169 return hv;
170 }
171
172
173 /*
174 ==================
175 HashBlocksForTri
176
177 Returns an inclusive bounding box of hash
178 bins that should hold the triangle
179 ==================
180 */
HashBlocksForTri(const mapTri_t * tri,int blocks[2][3])181 static void HashBlocksForTri( const mapTri_t *tri, int blocks[2][3] ) {
182 idBounds bounds;
183 int i;
184
185 bounds.Clear();
186 bounds.AddPoint( tri->v[0].xyz );
187 bounds.AddPoint( tri->v[1].xyz );
188 bounds.AddPoint( tri->v[2].xyz );
189
190 // add a 1.0 slop margin on each side
191 for ( i = 0 ; i < 3 ; i++ ) {
192 blocks[0][i] = ( bounds[0][i] - 1.0 - hashBounds[0][i] ) / hashScale[i];
193 if ( blocks[0][i] < 0 ) {
194 blocks[0][i] = 0;
195 } else if ( blocks[0][i] >= HASH_BINS ) {
196 blocks[0][i] = HASH_BINS - 1;
197 }
198
199 blocks[1][i] = ( bounds[1][i] + 1.0 - hashBounds[0][i] ) / hashScale[i];
200 if ( blocks[1][i] < 0 ) {
201 blocks[1][i] = 0;
202 } else if ( blocks[1][i] >= HASH_BINS ) {
203 blocks[1][i] = HASH_BINS - 1;
204 }
205 }
206 }
207
208
209 /*
210 =================
211 HashTriangles
212
213 Removes triangles that are degenerated or flipped backwards
214 =================
215 */
HashTriangles(optimizeGroup_t * groupList)216 void HashTriangles( optimizeGroup_t *groupList ) {
217 mapTri_t *a;
218 int vert;
219 int i;
220 optimizeGroup_t *group;
221
222 // clear the hash tables
223 memset( hashVerts, 0, sizeof( hashVerts ) );
224
225 numHashVerts = 0;
226 numTotalVerts = 0;
227
228 // bound all the triangles to determine the bucket size
229 hashBounds.Clear();
230 for ( group = groupList ; group ; group = group->nextGroup ) {
231 for ( a = group->triList ; a ; a = a->next ) {
232 hashBounds.AddPoint( a->v[0].xyz );
233 hashBounds.AddPoint( a->v[1].xyz );
234 hashBounds.AddPoint( a->v[2].xyz );
235 }
236 }
237
238 // spread the bounds so it will never have a zero size
239 for ( i = 0 ; i < 3 ; i++ ) {
240 hashBounds[0][i] = floor( hashBounds[0][i] - 1 );
241 hashBounds[1][i] = ceil( hashBounds[1][i] + 1 );
242 hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS;
243
244 hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
245 hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
246 if ( hashIntScale[i] < 1 ) {
247 hashIntScale[i] = 1;
248 }
249 }
250
251 // add all the points to the hash buckets
252 for ( group = groupList ; group ; group = group->nextGroup ) {
253 // don't create tjunctions against discrete surfaces (blood decals, etc)
254 if ( group->material != NULL && group->material->IsDiscrete() ) {
255 continue;
256 }
257 for ( a = group->triList ; a ; a = a->next ) {
258 for ( vert = 0 ; vert < 3 ; vert++ ) {
259 a->hashVert[vert] = GetHashVert( a->v[vert].xyz );
260 }
261 }
262 }
263 }
264
265 /*
266 =================
267 FreeTJunctionHash
268
269 The optimizer may add some more crossing verts
270 after t junction processing
271 =================
272 */
FreeTJunctionHash(void)273 void FreeTJunctionHash( void ) {
274 int i, j, k;
275 hashVert_t *hv, *next;
276
277 for ( i = 0 ; i < HASH_BINS ; i++ ) {
278 for ( j = 0 ; j < HASH_BINS ; j++ ) {
279 for ( k = 0 ; k < HASH_BINS ; k++ ) {
280 for ( hv = hashVerts[i][j][k] ; hv ; hv = next ) {
281 next = hv->next;
282 Mem_Free( hv );
283 }
284 }
285 }
286 }
287 memset( hashVerts, 0, sizeof( hashVerts ) );
288 }
289
290
291 /*
292 ==================
293 FixTriangleAgainstHashVert
294
295 Returns a list of two new mapTri if the hashVert is
296 on an edge of the given mapTri, otherwise returns NULL.
297 ==================
298 */
FixTriangleAgainstHashVert(const mapTri_t * a,const hashVert_t * hv)299 static mapTri_t *FixTriangleAgainstHashVert( const mapTri_t *a, const hashVert_t *hv ) {
300 int i;
301 const idDrawVert *v1, *v2;
302 idDrawVert split;
303 idVec3 dir;
304 float len;
305 float frac;
306 mapTri_t *new1, *new2;
307 idVec3 temp;
308 float d, off;
309 const idVec3 *v;
310 idPlane plane1, plane2;
311
312 v = &hv->v;
313
314 // if the triangle already has this hashVert as a vert,
315 // it can't be split by it
316 if ( a->hashVert[0] == hv || a->hashVert[1] == hv || a->hashVert[2] == hv ) {
317 return NULL;
318 }
319
320 split.Clear();
321
322 // we probably should find the edge that the vertex is closest to.
323 // it is possible to be < 1 unit away from multiple
324 // edges, but we only want to split by one of them
325 for ( i = 0 ; i < 3 ; i++ ) {
326 v1 = &a->v[i];
327 v2 = &a->v[(i+1)%3];
328 VectorSubtract( v2->xyz, v1->xyz, dir );
329 len = dir.Normalize();
330
331 // if it is close to one of the edge vertexes, skip it
332 VectorSubtract( *v, v1->xyz, temp );
333 d = DotProduct( temp, dir );
334 if ( d <= 0 || d >= len ) {
335 continue;
336 }
337
338 // make sure it is on the line
339 VectorMA( v1->xyz, d, dir, temp );
340 VectorSubtract( temp, *v, temp );
341 off = temp.Length();
342 if ( off <= -COLINEAR_EPSILON || off >= COLINEAR_EPSILON ) {
343 continue;
344 }
345
346 // take the x/y/z from the splitter,
347 // but interpolate everything else from the original tri
348 VectorCopy( *v, split.xyz );
349 frac = d / len;
350 split.st[0] = v1->st[0] + frac * ( v2->st[0] - v1->st[0] );
351 split.st[1] = v1->st[1] + frac * ( v2->st[1] - v1->st[1] );
352 split.normal[0] = v1->normal[0] + frac * ( v2->normal[0] - v1->normal[0] );
353 split.normal[1] = v1->normal[1] + frac * ( v2->normal[1] - v1->normal[1] );
354 split.normal[2] = v1->normal[2] + frac * ( v2->normal[2] - v1->normal[2] );
355 split.normal.Normalize();
356
357 // split the tri
358 new1 = CopyMapTri( a );
359 new1->v[(i+1)%3] = split;
360 new1->hashVert[(i+1)%3] = hv;
361 new1->next = NULL;
362
363 new2 = CopyMapTri( a );
364 new2->v[i] = split;
365 new2->hashVert[i] = hv;
366 new2->next = new1;
367
368 plane1.FromPoints( new1->hashVert[0]->v, new1->hashVert[1]->v, new1->hashVert[2]->v );
369 plane2.FromPoints( new2->hashVert[0]->v, new2->hashVert[1]->v, new2->hashVert[2]->v );
370
371 d = DotProduct( plane1, plane2 );
372
373 // if the two split triangle's normals don't face the same way,
374 // it should not be split
375 if ( d <= 0 ) {
376 FreeTriList( new2 );
377 continue;
378 }
379
380 return new2;
381 }
382
383
384 return NULL;
385 }
386
387
388
389 /*
390 ==================
391 FixTriangleAgainstHash
392
393 Potentially splits a triangle into a list of triangles based on tjunctions
394 ==================
395 */
FixTriangleAgainstHash(const mapTri_t * tri)396 static mapTri_t *FixTriangleAgainstHash( const mapTri_t *tri ) {
397 mapTri_t *fixed;
398 mapTri_t *a;
399 mapTri_t *test, *next;
400 int blocks[2][3];
401 int i, j, k;
402 hashVert_t *hv;
403
404 // if this triangle is degenerate after point snapping,
405 // do nothing (this shouldn't happen, because they should
406 // be removed as they are hashed)
407 if ( tri->hashVert[0] == tri->hashVert[1]
408 || tri->hashVert[0] == tri->hashVert[2]
409 || tri->hashVert[1] == tri->hashVert[2] ) {
410 return NULL;
411 }
412
413 fixed = CopyMapTri( tri );
414 fixed->next = NULL;
415
416 HashBlocksForTri( tri, blocks );
417 for ( i = blocks[0][0] ; i <= blocks[1][0] ; i++ ) {
418 for ( j = blocks[0][1] ; j <= blocks[1][1] ; j++ ) {
419 for ( k = blocks[0][2] ; k <= blocks[1][2] ; k++ ) {
420 for ( hv = hashVerts[i][j][k] ; hv ; hv = hv->next ) {
421 // fix all triangles in the list against this point
422 test = fixed;
423 fixed = NULL;
424 for ( ; test ; test = next ) {
425 next = test->next;
426 a = FixTriangleAgainstHashVert( test, hv );
427 if ( a ) {
428 // cut into two triangles
429 a->next->next = fixed;
430 fixed = a;
431 FreeTri( test );
432 } else {
433 test->next = fixed;
434 fixed = test;
435 }
436 }
437 }
438 }
439 }
440 }
441
442 return fixed;
443 }
444
445
446 /*
447 ==================
448 CountGroupListTris
449 ==================
450 */
CountGroupListTris(const optimizeGroup_t * groupList)451 int CountGroupListTris( const optimizeGroup_t *groupList ) {
452 int c;
453
454 c = 0;
455 for ( ; groupList ; groupList = groupList->nextGroup ) {
456 c += CountTriList( groupList->triList );
457 }
458
459 return c;
460 }
461
462 /*
463 ==================
464 FixAreaGroupsTjunctions
465 ==================
466 */
FixAreaGroupsTjunctions(optimizeGroup_t * groupList)467 void FixAreaGroupsTjunctions( optimizeGroup_t *groupList ) {
468 const mapTri_t *tri;
469 mapTri_t *newList;
470 mapTri_t *fixed;
471 int startCount, endCount;
472 optimizeGroup_t *group;
473
474 if ( dmapGlobals.noTJunc ) {
475 return;
476 }
477
478 if ( !groupList ) {
479 return;
480 }
481
482 startCount = CountGroupListTris( groupList );
483
484 if ( dmapGlobals.verbose ) {
485 common->Printf( "----- FixAreaGroupsTjunctions -----\n" );
486 common->Printf( "%6i triangles in\n", startCount );
487 }
488
489 HashTriangles( groupList );
490
491 for ( group = groupList ; group ; group = group->nextGroup ) {
492 // don't touch discrete surfaces
493 if ( group->material != NULL && group->material->IsDiscrete() ) {
494 continue;
495 }
496
497 newList = NULL;
498 for ( tri = group->triList ; tri ; tri = tri->next ) {
499 fixed = FixTriangleAgainstHash( tri );
500 newList = MergeTriLists( newList, fixed );
501 }
502 FreeTriList( group->triList );
503 group->triList = newList;
504 }
505
506 endCount = CountGroupListTris( groupList );
507 if ( dmapGlobals.verbose ) {
508 common->Printf( "%6i triangles out\n", endCount );
509 }
510 }
511
512
513 /*
514 ==================
515 FixEntityTjunctions
516 ==================
517 */
FixEntityTjunctions(uEntity_t * e)518 void FixEntityTjunctions( uEntity_t *e ) {
519 int i;
520
521 for ( i = 0 ; i < e->numAreas ; i++ ) {
522 FixAreaGroupsTjunctions( e->areas[i].groups );
523 FreeTJunctionHash();
524 }
525 }
526
527 /*
528 ==================
529 FixGlobalTjunctions
530 ==================
531 */
FixGlobalTjunctions(uEntity_t * e)532 void FixGlobalTjunctions( uEntity_t *e ) {
533 mapTri_t *a;
534 int vert;
535 int i;
536 optimizeGroup_t *group;
537 int areaNum;
538
539 common->Printf( "----- FixGlobalTjunctions -----\n" );
540
541 // clear the hash tables
542 memset( hashVerts, 0, sizeof( hashVerts ) );
543
544 numHashVerts = 0;
545 numTotalVerts = 0;
546
547 // bound all the triangles to determine the bucket size
548 hashBounds.Clear();
549 for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
550 for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
551 for ( a = group->triList ; a ; a = a->next ) {
552 hashBounds.AddPoint( a->v[0].xyz );
553 hashBounds.AddPoint( a->v[1].xyz );
554 hashBounds.AddPoint( a->v[2].xyz );
555 }
556 }
557 }
558
559 // spread the bounds so it will never have a zero size
560 for ( i = 0 ; i < 3 ; i++ ) {
561 hashBounds[0][i] = floor( hashBounds[0][i] - 1 );
562 hashBounds[1][i] = ceil( hashBounds[1][i] + 1 );
563 hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS;
564
565 hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
566 hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
567 if ( hashIntScale[i] < 1 ) {
568 hashIntScale[i] = 1;
569 }
570 }
571
572 // add all the points to the hash buckets
573 for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
574 for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
575 // don't touch discrete surfaces
576 if ( group->material != NULL && group->material->IsDiscrete() ) {
577 continue;
578 }
579
580 for ( a = group->triList ; a ; a = a->next ) {
581 for ( vert = 0 ; vert < 3 ; vert++ ) {
582 a->hashVert[vert] = GetHashVert( a->v[vert].xyz );
583 }
584 }
585 }
586 }
587
588 // add all the func_static model vertexes to the hash buckets
589 // optionally inline some of the func_static models
590 if ( dmapGlobals.entityNum == 0 ) {
591 for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) {
592 uEntity_t *entity = &dmapGlobals.uEntities[eNum];
593 const char *className = entity->mapEntity->epairs.GetString( "classname" );
594 if ( idStr::Icmp( className, "func_static" ) ) {
595 continue;
596 }
597 const char *modelName = entity->mapEntity->epairs.GetString( "model" );
598 if ( !modelName ) {
599 continue;
600 }
601 if ( !strstr( modelName, ".lwo" ) && !strstr( modelName, ".ase" ) && !strstr( modelName, ".ma" ) ) {
602 continue;
603 }
604
605 idRenderModel *model = renderModelManager->FindModel( modelName );
606
607 // common->Printf( "adding T junction verts for %s.\n", entity->mapEntity->epairs.GetString( "name" ) );
608
609 idMat3 axis;
610 // get the rotation matrix in either full form, or single angle form
611 if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
612 float angle = entity->mapEntity->epairs.GetFloat( "angle" );
613 if ( angle != 0.0f ) {
614 axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
615 } else {
616 axis.Identity();
617 }
618 }
619
620 idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" );
621
622 for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
623 const modelSurface_t *surface = model->Surface( i );
624 const srfTriangles_t *tri = surface->geometry;
625
626 mapTri_t mapTri;
627 memset( &mapTri, 0, sizeof( mapTri ) );
628 mapTri.material = surface->shader;
629 // don't let discretes (autosprites, etc) merge together
630 if ( mapTri.material->IsDiscrete() ) {
631 mapTri.mergeGroup = (void *)surface;
632 }
633 for ( int j = 0 ; j < tri->numVerts ; j += 3 ) {
634 idVec3 v = tri->verts[j].xyz * axis + origin;
635 GetHashVert( v );
636 }
637 }
638 }
639 }
640
641
642
643 // now fix each area
644 for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
645 for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
646 // don't touch discrete surfaces
647 if ( group->material != NULL && group->material->IsDiscrete() ) {
648 continue;
649 }
650
651 mapTri_t *newList = NULL;
652 for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) {
653 mapTri_t *fixed = FixTriangleAgainstHash( tri );
654 newList = MergeTriLists( newList, fixed );
655 }
656 FreeTriList( group->triList );
657 group->triList = newList;
658 }
659 }
660
661
662 // done
663 FreeTJunctionHash();
664 }
665