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