2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../../../idlib/precompiled.h"
36 T junction fixing never creates more xyz points, but
37 new vertexes will be created when different surfaces
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.
47 Snaping verts can drag some triangles backwards or collapse them to points,
48 which will cause them to be removed.
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
54 A case that causes recursive overflow with point to triangle fixing:
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
65 For a given source triangle
66 init the no-check list to hold the three triangle hashVerts
68 recursiveFixTriAgainstHash
70 recursiveFixTriAgainstHashVert_r
71 if hashVert is on the no-check list
73 if the hashVert should split the triangle
74 add to the no-check list
75 recursiveFixTriAgainstHash(a)
76 recursiveFixTriAgainstHash(b)
80 #define SNAP_FRACTIONS 32
81 //#define SNAP_FRACTIONS 8
82 //#define SNAP_FRACTIONS 1
84 #define VERTEX_EPSILON ( 1.0 / SNAP_FRACTIONS )
86 #define COLINEAR_EPSILON ( 1.8 * VERTEX_EPSILON )
90 typedef struct hashVert_s {
91 struct hashVert_s *next;
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];
106 Also modifies the original vert to the snapped value
109 struct hashVert_s *GetHashVert( idVec3 &v ) {
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 ) {
123 } else if ( block[i] >= HASH_BINS ) {
124 block[i] = HASH_BINS - 1;
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 ) {
132 if ( hv->iv[0] == iv[0] && hv->iv[1] == iv[1] && hv->iv[2] == iv[2] ) {
133 VectorCopy( hv->v, v );
137 for ( i = 0 ; i < 3 ; i++ ) {
139 d = hv->iv[i] - iv[i];
140 if ( d < -1 || d > 1 ) {
145 VectorCopy( hv->v, v );
152 hv = (hashVert_t *)Mem_Alloc( sizeof( *hv ) );
154 hv->next = hashVerts[block[0]][block[1]][block[2]];
155 hashVerts[block[0]][block[1]][block[2]] = hv;
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;
165 VectorCopy( hv->v, v );
177 Returns an inclusive bounding box of hash
178 bins that should hold the triangle
181 static void HashBlocksForTri( const mapTri_t *tri, int blocks[2][3] ) {
186 bounds.AddPoint( tri->v[0].xyz );
187 bounds.AddPoint( tri->v[1].xyz );
188 bounds.AddPoint( tri->v[2].xyz );
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 ) {
195 } else if ( blocks[0][i] >= HASH_BINS ) {
196 blocks[0][i] = HASH_BINS - 1;
199 blocks[1][i] = ( bounds[1][i] + 1.0 - hashBounds[0][i] ) / hashScale[i];
200 if ( blocks[1][i] < 0 ) {
202 } else if ( blocks[1][i] >= HASH_BINS ) {
203 blocks[1][i] = HASH_BINS - 1;
213 Removes triangles that are degenerated or flipped backwards
216 void HashTriangles( optimizeGroup_t *groupList ) {
220 optimizeGroup_t *group;
222 // clear the hash tables
223 memset( hashVerts, 0, sizeof( hashVerts ) );
228 // bound all the triangles to determine the bucket size
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 );
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;
244 hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
245 hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
246 if ( hashIntScale[i] < 1 ) {
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() ) {
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 );
269 The optimizer may add some more crossing verts
270 after t junction processing
273 void FreeTJunctionHash( void ) {
275 hashVert_t *hv, *next;
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 ) {
287 memset( hashVerts, 0, sizeof( hashVerts ) );
293 FixTriangleAgainstHashVert
295 Returns a list of two new mapTri if the hashVert is
296 on an edge of the given mapTri, otherwise returns NULL.
299 static mapTri_t *FixTriangleAgainstHashVert( const mapTri_t *a, const hashVert_t *hv ) {
301 const idDrawVert *v1, *v2, *v3;
306 mapTri_t *new1, *new2;
310 idPlane plane1, plane2;
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 ) {
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++ ) {
327 VectorSubtract( v2->xyz, v1->xyz, dir );
328 len = dir.Normalize();
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 ) {
337 // make sure it is on the line
338 VectorMA( v1->xyz, d, dir, temp );
339 VectorSubtract( temp, *v, temp );
341 if ( off <= -COLINEAR_EPSILON || off >= COLINEAR_EPSILON ) {
345 // take the x/y/z from the splitter,
346 // but interpolate everything else from the original tri
347 VectorCopy( *v, split.xyz );
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();
357 new1 = CopyMapTri( a );
358 new1->v[(i+1)%3] = split;
359 new1->hashVert[(i+1)%3] = hv;
362 new2 = CopyMapTri( a );
364 new2->hashVert[i] = hv;
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 );
370 d = DotProduct( plane1, plane2 );
372 // if the two split triangle's normals don't face the same way,
373 // it should not be split
390 FixTriangleAgainstHash
392 Potentially splits a triangle into a list of triangles based on tjunctions
395 static mapTri_t *FixTriangleAgainstHash( const mapTri_t *tri ) {
398 mapTri_t *test, *next;
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] ) {
412 fixed = CopyMapTri( tri );
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
423 for ( ; test ; test = next ) {
425 a = FixTriangleAgainstHashVert( test, hv );
427 // cut into two triangles
428 a->next->next = fixed;
450 int CountGroupListTris( const optimizeGroup_t *groupList ) {
454 for ( ; groupList ; groupList = groupList->nextGroup ) {
455 c += CountTriList( groupList->triList );
463 FixAreaGroupsTjunctions
466 void FixAreaGroupsTjunctions( optimizeGroup_t *groupList ) {
470 int startCount, endCount;
471 optimizeGroup_t *group;
473 if ( dmapGlobals.noTJunc ) {
481 startCount = CountGroupListTris( groupList );
483 if ( dmapGlobals.verbose ) {
484 common->Printf( "----- FixAreaGroupsTjunctions -----\n" );
485 common->Printf( "%6i triangles in\n", startCount );
488 HashTriangles( groupList );
490 for ( group = groupList ; group ; group = group->nextGroup ) {
491 // don't touch discrete surfaces
492 if ( group->material != NULL && group->material->IsDiscrete() ) {
497 for ( tri = group->triList ; tri ; tri = tri->next ) {
498 fixed = FixTriangleAgainstHash( tri );
499 newList = MergeTriLists( newList, fixed );
501 FreeTriList( group->triList );
502 group->triList = newList;
505 endCount = CountGroupListTris( groupList );
506 if ( dmapGlobals.verbose ) {
507 common->Printf( "%6i triangles out\n", endCount );
517 void FixEntityTjunctions( uEntity_t *e ) {
520 for ( i = 0 ; i < e->numAreas ; i++ ) {
521 FixAreaGroupsTjunctions( e->areas[i].groups );
531 void FixGlobalTjunctions( uEntity_t *e ) {
535 optimizeGroup_t *group;
538 common->Printf( "----- FixGlobalTjunctions -----\n" );
540 // clear the hash tables
541 memset( hashVerts, 0, sizeof( hashVerts ) );
546 // bound all the triangles to determine the bucket size
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 );
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;
564 hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
565 hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
566 if ( hashIntScale[i] < 1 ) {
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() ) {
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 );
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" ) ) {
596 const char *modelName = entity->mapEntity->epairs.GetString( "model" );
600 if ( !strstr( modelName, ".lwo" ) && !strstr( modelName, ".ase" ) && !strstr( modelName, ".ma" ) ) {
604 idRenderModel *model = renderModelManager->FindModel( modelName );
606 // common->Printf( "adding T junction verts for %s.\n", entity->mapEntity->epairs.GetString( "name" ) );
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();
619 idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" );
621 for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
622 const modelSurface_t *surface = model->Surface( i );
623 const srfTriangles_t *tri = surface->geometry;
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;
632 for ( int j = 0 ; j < tri->numVerts ; j += 3 ) {
633 idVec3 v = tri->verts[j].xyz * axis + origin;
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() ) {
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 );
655 FreeTriList( group->triList );
656 group->triList = newList;