]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/compilers/dmap/tritjunction.cpp
hello world
[icculus/iodoom3.git] / neo / tools / compilers / dmap / tritjunction.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "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 */
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 */
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 */
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 */
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 */
299 static mapTri_t *FixTriangleAgainstHashVert( const mapTri_t *a, const hashVert_t *hv ) {
300         int                     i;
301         const idDrawVert        *v1, *v2, *v3;
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         // we probably should find the edge that the vertex is closest to.
321         // it is possible to be < 1 unit away from multiple
322         // edges, but we only want to split by one of them
323         for ( i = 0 ; i < 3 ; i++ ) {
324                 v1 = &a->v[i];
325                 v2 = &a->v[(i+1)%3];
326                 v3 = &a->v[(i+2)%3];
327                 VectorSubtract( v2->xyz, v1->xyz, dir );
328                 len = dir.Normalize();
329
330                 // if it is close to one of the edge vertexes, skip it
331                 VectorSubtract( *v, v1->xyz, temp );
332                 d = DotProduct( temp, dir );
333                 if ( d <= 0 || d >= len ) {
334                         continue;
335                 }
336
337                 // make sure it is on the line
338                 VectorMA( v1->xyz, d, dir, temp );
339                 VectorSubtract( temp, *v, temp );
340                 off = temp.Length();
341                 if ( off <= -COLINEAR_EPSILON || off >= COLINEAR_EPSILON ) {
342                         continue;
343                 }
344
345                 // take the x/y/z from the splitter,
346                 // but interpolate everything else from the original tri
347                 VectorCopy( *v, split.xyz );
348                 frac = d / len;
349                 split.st[0] = v1->st[0] + frac * ( v2->st[0] - v1->st[0] );
350                 split.st[1] = v1->st[1] + frac * ( v2->st[1] - v1->st[1] );
351                 split.normal[0] = v1->normal[0] + frac * ( v2->normal[0] - v1->normal[0] );
352                 split.normal[1] = v1->normal[1] + frac * ( v2->normal[1] - v1->normal[1] );
353                 split.normal[2] = v1->normal[2] + frac * ( v2->normal[2] - v1->normal[2] );
354                 split.normal.Normalize();
355
356                 // split the tri
357                 new1 = CopyMapTri( a );
358                 new1->v[(i+1)%3] = split;
359                 new1->hashVert[(i+1)%3] = hv;
360                 new1->next = NULL;
361
362                 new2 = CopyMapTri( a );
363                 new2->v[i] = split;
364                 new2->hashVert[i] = hv;
365                 new2->next = new1;
366
367                 plane1.FromPoints( new1->hashVert[0]->v, new1->hashVert[1]->v, new1->hashVert[2]->v );
368                 plane2.FromPoints( new2->hashVert[0]->v, new2->hashVert[1]->v, new2->hashVert[2]->v );
369
370                 d = DotProduct( plane1, plane2 );
371
372                 // if the two split triangle's normals don't face the same way,
373                 // it should not be split
374                 if ( d <= 0 ) {
375                         FreeTriList( new2 );
376                         continue;
377                 }
378
379                 return new2;
380         }
381
382
383         return NULL;
384 }
385
386
387
388 /*
389 ==================
390 FixTriangleAgainstHash
391
392 Potentially splits a triangle into a list of triangles based on tjunctions
393 ==================
394 */
395 static mapTri_t *FixTriangleAgainstHash( const mapTri_t *tri ) {
396         mapTri_t                *fixed;
397         mapTri_t                *a;
398         mapTri_t                *test, *next;
399         int                             blocks[2][3];
400         int                             i, j, k;
401         hashVert_t              *hv;
402
403         // if this triangle is degenerate after point snapping,
404         // do nothing (this shouldn't happen, because they should
405         // be removed as they are hashed)
406         if ( tri->hashVert[0] == tri->hashVert[1]
407                 || tri->hashVert[0] == tri->hashVert[2]
408                 || tri->hashVert[1] == tri->hashVert[2] ) {
409                 return NULL;
410         }
411
412         fixed = CopyMapTri( tri );
413         fixed->next = NULL;
414
415         HashBlocksForTri( tri, blocks );
416         for ( i = blocks[0][0] ; i <= blocks[1][0] ; i++ ) {
417                 for ( j = blocks[0][1] ; j <= blocks[1][1] ; j++ ) {
418                         for ( k = blocks[0][2] ; k <= blocks[1][2] ; k++ ) {
419                                 for ( hv = hashVerts[i][j][k] ; hv ; hv = hv->next ) {
420                                         // fix all triangles in the list against this point
421                                         test = fixed;
422                                         fixed = NULL;
423                                         for ( ; test ; test = next ) {
424                                                 next = test->next;
425                                                 a = FixTriangleAgainstHashVert( test, hv );
426                                                 if ( a ) {
427                                                         // cut into two triangles
428                                                         a->next->next = fixed;
429                                                         fixed = a;
430                                                         FreeTri( test );
431                                                 } else {
432                                                         test->next = fixed;
433                                                         fixed = test;
434                                                 }
435                                         }
436                                 }
437                         }
438                 }
439         }
440
441         return fixed;
442 }
443
444
445 /*
446 ==================
447 CountGroupListTris
448 ==================
449 */
450 int CountGroupListTris( const optimizeGroup_t *groupList ) {
451         int             c;
452
453         c = 0;
454         for ( ; groupList ; groupList = groupList->nextGroup ) {
455                 c += CountTriList( groupList->triList );
456         }
457
458         return c;
459 }
460
461 /*
462 ==================
463 FixAreaGroupsTjunctions
464 ==================
465 */
466 void    FixAreaGroupsTjunctions( optimizeGroup_t *groupList ) {
467         const mapTri_t  *tri;
468         mapTri_t                *newList;
469         mapTri_t                *fixed;
470         int                             startCount, endCount;
471         optimizeGroup_t *group;
472
473         if ( dmapGlobals.noTJunc ) {
474                 return;
475         }
476
477         if ( !groupList ) {
478                 return;
479         }
480
481         startCount = CountGroupListTris( groupList );
482
483         if ( dmapGlobals.verbose ) {
484                 common->Printf( "----- FixAreaGroupsTjunctions -----\n" );
485                 common->Printf( "%6i triangles in\n", startCount );
486         }
487
488         HashTriangles( groupList );
489
490         for ( group = groupList ; group ; group = group->nextGroup ) {
491                 // don't touch discrete surfaces
492                 if ( group->material != NULL && group->material->IsDiscrete() ) {
493                         continue;
494                 }
495
496                 newList = NULL;
497                 for ( tri = group->triList ; tri ; tri = tri->next ) {
498                         fixed = FixTriangleAgainstHash( tri );
499                         newList = MergeTriLists( newList, fixed );
500                 }
501                 FreeTriList( group->triList );
502                 group->triList = newList;
503         }
504
505         endCount = CountGroupListTris( groupList );
506         if ( dmapGlobals.verbose ) {
507                 common->Printf( "%6i triangles out\n", endCount );
508         }
509 }
510
511
512 /*
513 ==================
514 FixEntityTjunctions
515 ==================
516 */
517 void    FixEntityTjunctions( uEntity_t *e ) {
518         int             i;
519
520         for ( i = 0 ; i < e->numAreas ; i++ ) {
521                 FixAreaGroupsTjunctions( e->areas[i].groups );
522                 FreeTJunctionHash();
523         }
524 }
525
526 /*
527 ==================
528 FixGlobalTjunctions
529 ==================
530 */
531 void    FixGlobalTjunctions( uEntity_t *e ) {
532         mapTri_t        *a;
533         int                     vert;
534         int                     i;
535         optimizeGroup_t *group;
536         int                     areaNum;
537
538         common->Printf( "----- FixGlobalTjunctions -----\n" );
539
540         // clear the hash tables
541         memset( hashVerts, 0, sizeof( hashVerts ) );
542
543         numHashVerts = 0;
544         numTotalVerts = 0;
545
546         // bound all the triangles to determine the bucket size
547         hashBounds.Clear();
548         for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
549                 for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
550                         for ( a = group->triList ; a ; a = a->next ) {
551                                 hashBounds.AddPoint( a->v[0].xyz );
552                                 hashBounds.AddPoint( a->v[1].xyz );
553                                 hashBounds.AddPoint( a->v[2].xyz );
554                         }
555                 }
556         }
557
558         // spread the bounds so it will never have a zero size
559         for ( i = 0 ; i < 3 ; i++ ) {
560                 hashBounds[0][i] = floor( hashBounds[0][i] - 1 );
561                 hashBounds[1][i] = ceil( hashBounds[1][i] + 1 );
562                 hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS;
563
564                 hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
565                 hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
566                 if ( hashIntScale[i] < 1 ) {
567                         hashIntScale[i] = 1;
568                 }
569         }
570
571         // add all the points to the hash buckets
572         for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
573                 for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
574                         // don't touch discrete surfaces
575                         if ( group->material != NULL && group->material->IsDiscrete() ) {
576                                 continue;
577                         }
578
579                         for ( a = group->triList ; a ; a = a->next ) {
580                                 for ( vert = 0 ; vert < 3 ; vert++ ) {
581                                         a->hashVert[vert] = GetHashVert( a->v[vert].xyz );
582                                 }
583                         }
584                 }
585         }
586
587         // add all the func_static model vertexes to the hash buckets
588         // optionally inline some of the func_static models
589         if ( dmapGlobals.entityNum == 0 ) {
590                 for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) {
591                         uEntity_t *entity = &dmapGlobals.uEntities[eNum];
592                         const char *className = entity->mapEntity->epairs.GetString( "classname" );
593                         if ( idStr::Icmp( className, "func_static" ) ) {
594                                 continue;
595                         }
596                         const char *modelName = entity->mapEntity->epairs.GetString( "model" );
597                         if ( !modelName ) {
598                                 continue;
599                         }
600                         if ( !strstr( modelName, ".lwo" ) && !strstr( modelName, ".ase" ) && !strstr( modelName, ".ma" ) ) {
601                                 continue;
602                         }
603
604                         idRenderModel   *model = renderModelManager->FindModel( modelName );
605
606 //                      common->Printf( "adding T junction verts for %s.\n", entity->mapEntity->epairs.GetString( "name" ) );
607
608                         idMat3  axis;
609                         // get the rotation matrix in either full form, or single angle form
610                         if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
611                                 float angle = entity->mapEntity->epairs.GetFloat( "angle" );
612                                 if ( angle != 0.0f ) {
613                                         axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
614                                 } else {
615                                         axis.Identity();
616                                 }
617                         }               
618
619                         idVec3  origin = entity->mapEntity->epairs.GetVector( "origin" );
620
621                         for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
622                                 const modelSurface_t *surface = model->Surface( i );
623                                 const srfTriangles_t *tri = surface->geometry;
624
625                                 mapTri_t        mapTri;
626                                 memset( &mapTri, 0, sizeof( mapTri ) );
627                                 mapTri.material = surface->shader;
628                                 // don't let discretes (autosprites, etc) merge together
629                                 if ( mapTri.material->IsDiscrete() ) {
630                                         mapTri.mergeGroup = (void *)surface;
631                                 }
632                                 for ( int j = 0 ; j < tri->numVerts ; j += 3 ) {
633                                         idVec3 v = tri->verts[j].xyz * axis + origin;
634                                         GetHashVert( v );
635                                 }
636                         }
637                 }
638         }
639
640
641
642         // now fix each area
643         for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
644                 for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
645                         // don't touch discrete surfaces
646                         if ( group->material != NULL && group->material->IsDiscrete() ) {
647                                 continue;
648                         }
649
650                         mapTri_t *newList = NULL;
651                         for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) {
652                                 mapTri_t *fixed = FixTriangleAgainstHash( tri );
653                                 newList = MergeTriLists( newList, fixed );
654                         }
655                         FreeTriList( group->triList );
656                         group->triList = newList;
657                 }
658         }
659
660
661         // done
662         FreeTJunctionHash();
663 }