1 /*
2 PLIB - A Suite of Portable Game Libraries
3 Copyright (C) 1998,2002 Steve Baker
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 For further information visit http://plib.sourceforge.net
20
21 $Id: ssgOptimiser.cxx 1999 2005-01-09 13:10:08Z bram $
22 */
23
24
25 #include "ssgLocal.h"
26
27 #include "ssgVertSplitter.h"
28
29 static float optimise_vtol [3] =
30 {
31 0.001f, /* DISTANCE_SLOP = One millimeter */
32 0.04f, /* COLOUR_SLOP = Four percent */
33 0.004f, /* TEXCOORD_SLOP = One texel on a 256 map */
34 } ;
35
36 static float* current_vtol = 0 ;
37
38 #define DISTANCE_SLOP current_vtol[0]
39 #define COLOUR_SLOP current_vtol[1]
40 #define TEXCOORD_SLOP current_vtol[2]
41
frac(float x)42 inline float frac ( float x )
43 {
44 return x - (float) floor(x) ;
45 }
46
47 struct OptVertex
48 {
49 sgVec3 vertex ;
50 sgVec3 normal ;
51 sgVec2 texcoord ;
52 sgVec4 colour ;
53 int counter ;
54
printOptVertex55 void print () { ulSetError ( UL_DEBUG, "%d:(%g,%g,%g):(%g,%g):(%g,%g,%g,%g):(%g,%g,%g)",
56 counter,
57 vertex[0],vertex[1],vertex[2],
58 texcoord[0],texcoord[1],
59 colour[0],colour[1],colour[2],colour[3],
60 normal[0],normal[1],normal[2] ) ; }
61
OptVertexOptVertex62 OptVertex ( sgVec3 v, sgVec2 t, sgVec4 c )
63 {
64 sgCopyVec3 ( vertex , v ) ;
65 sgCopyVec2 ( texcoord, t ) ;
66 sgCopyVec4 ( colour , c ) ;
67 sgSetVec3 ( normal , 0.0f, 0.0f, 0.0f ) ;
68 counter = 1 ;
69 }
70
OptVertexOptVertex71 OptVertex ( OptVertex* cloneFrom )
72 {
73 sgCopyVec3 ( vertex , cloneFrom -> vertex ) ;
74 sgCopyVec2 ( texcoord, cloneFrom -> texcoord ) ;
75 sgCopyVec4 ( colour , cloneFrom -> colour ) ;
76 sgSetVec3 ( normal , 0.0f, 0.0f, 0.0f ) ;
77 counter = 1 ;
78 }
79
equalOptVertex80 int equal ( sgVec3 v, sgVec2 t, sgVec4 c, int tex_frac )
81 {
82 if ( ! sgCompareVec3 ( vertex , v, DISTANCE_SLOP ) ||
83 ! sgCompareVec4 ( colour , c, COLOUR_SLOP ) )
84 return FALSE ;
85
86 if ( ! tex_frac )
87 return sgCompareVec2 ( texcoord, t, TEXCOORD_SLOP ) ;
88
89 return ( fabs ( frac ( texcoord[0] ) - frac ( t[0] ) ) <= TEXCOORD_SLOP &&
90 fabs ( frac ( texcoord[1] ) - frac ( t[1] ) ) <= TEXCOORD_SLOP ) ;
91 }
92
bumpOptVertex93 void bump () { counter++ ; }
dentOptVertex94 void dent () { counter-- ; }
getCountOptVertex95 int getCount () { return counter ; }
96 } ;
97
98
99 #define MAX_OPT_VERTEX_LIST 10000
100
101 class OptVertexList
102 {
103 public:
104 short vnum, tnum ;
105 OptVertex **vlist ;
106 short *tlist ;
107 ssgState *state ;
108 int cullface ;
109
OptVertexList(ssgState * s,int cf)110 OptVertexList ( ssgState *s, int cf )
111 {
112 /*
113 Have to dynamically allocate these to get
114 around Mac's CodeWarrior restriction on
115 32Kb as max structure size.
116 */
117
118 vlist = new OptVertex* [ MAX_OPT_VERTEX_LIST ] ;
119 tlist = new short [ MAX_OPT_VERTEX_LIST * 3 ] ;
120 state = s ;
121 if (state != NULL) state -> ref(); //~T.G.
122 cullface = cf ;
123 vnum = tnum = 0 ;
124 }
125
~OptVertexList()126 ~OptVertexList ()
127 {
128 for ( int i = 0 ; i < vnum ; i++ )
129 delete vlist [ i ] ;
130
131 delete [] vlist ;
132 delete [] tlist ;
133
134 if (state != NULL) ssgDeRefDelete(state); //~T.G.
135 }
136
137 short find ( sgVec3 v, sgVec2 t, sgVec4 c, int tex_fraction_only = FALSE ) ;
138
139 short add ( sgVec3 v1, sgVec2 t1, sgVec4 c1,
140 sgVec3 v2, sgVec2 t2, sgVec4 c2,
141 sgVec3 v3, sgVec2 t3, sgVec4 c3 ) ;
142 short add ( sgVec3 v, sgVec2 t, sgVec4 c ) ;
143 short add ( short v1, short v2, short v3 ) ;
144 void add ( ssgLeaf *l ) ;
145
146 void makeNormals () ;
147
print()148 void print ()
149 {
150 ulSetError ( UL_DEBUG, "LIST: %d unique vertices and %d triangles",
151 vnum, tnum ) ;
152 }
153
154 void follow ( int tri, int v1, int v2, int backwards, int *len, short *list, short *next ) ;
155
getLeastConnected(short * t,short * v)156 int getLeastConnected ( short *t, short *v )
157 {
158 int least = 32767 ;
159 *v = 0 ;
160
161 /* Find the least connected vertex that *is* connected */
162
163 int i ;
164
165 for ( i = 0 ; i < vnum ; i++ )
166 {
167 int c = vlist [ i ] -> getCount () ;
168
169 if ( c > 0 && c < least )
170 {
171 least = c ;
172 *v = i ;
173 }
174 }
175
176 if ( least == 32767 ) /* Didn't find an unused vertex - so punt. */
177 return FALSE ;
178
179 least = 32767 ;
180 *t = 32767 ;
181
182 for ( i = 0 ; i < tnum ; i++ )
183 if ( tlist[i*3+0] == *v || tlist[i*3+1] == *v || tlist[i*3+2] == *v )
184 {
185 int c = vlist [ tlist[i*3+0] ] -> getCount () +
186 vlist [ tlist[i*3+1] ] -> getCount () +
187 vlist [ tlist[i*3+2] ] -> getCount () ;
188
189 if ( c < least )
190 {
191 least = c ;
192 *t = i ;
193 }
194 }
195
196 if ( least == 32767 ) /* Didn't find an unused vertex - so punt. */
197 return FALSE ;
198
199 return TRUE ; /* Got it! */
200 }
201
sub(short t)202 void sub ( short t )
203 {
204 vlist [ tlist[t*3+0] ] -> dent () ;
205 vlist [ tlist[t*3+1] ] -> dent () ;
206 vlist [ tlist[t*3+2] ] -> dent () ;
207
208 tlist[t*3+0] = -1 ;
209 tlist[t*3+1] = -1 ;
210 tlist[t*3+2] = -1 ;
211 }
212 } ;
213
214
add(sgVec3 v1,sgVec2 t1,sgVec4 c1,sgVec3 v2,sgVec2 t2,sgVec4 c2,sgVec3 v3,sgVec2 t3,sgVec4 c3)215 short OptVertexList::add ( sgVec3 v1, sgVec2 t1, sgVec4 c1,
216 sgVec3 v2, sgVec2 t2, sgVec4 c2,
217 sgVec3 v3, sgVec2 t3, sgVec4 c3 )
218 {
219 // If possible, this routine moves the texturecoordinates of
220 // all three vertices of a Tria so that one needs less vertices.
221 // This doesn't affect looks, but enhances the speed a bit.
222 // This is not possible, if warpu or wrapv is FALSE
223
224 int bWrapsInBothDirections = FALSE;
225 short vi1, vi2, vi3;
226 /*
227 if ( state->isAKindOf( ssgTypeSimpleState() ) )
228 bWrapsInBothDirections =
229 ( (((ssgSimpleState *)state)->getWrapU()) &&
230 (((ssgSimpleState *)state)->getWrapV()) );
231 */
232 if (!bWrapsInBothDirections)
233 {
234 /* Find which (if any) of the vertices are a match for one in the list */
235
236 vi1 = add ( v1, t1, c1 ) ;
237 vi2 = add ( v2, t2, c2 ) ;
238 vi3 = add ( v3, t3, c3 ) ;
239 }
240 else
241 {
242 /*
243 Sharing vertices is tricky because of texture coordinates
244 that have the same all-important fractional part - but
245 differ in their integer parts.
246 */
247
248 sgVec2 adjust ;
249
250 /* Find which (if any) of the vertices are a match for one in the list */
251
252 vi1 = find ( v1, t1, c1, TRUE ) ;
253 vi2 = find ( v2, t2, c2, TRUE ) ;
254 vi3 = find ( v3, t3, c3, TRUE ) ;
255
256 /* Compute texture offset coordinates (if needed) to make everything match */
257
258 if ( vi1 >= 0 )
259 sgSubVec2 ( adjust, t1, vlist[vi1]->texcoord ) ;
260 else
261 if ( vi2 >= 0 )
262 sgSubVec2 ( adjust, t2, vlist[vi2]->texcoord ) ;
263 else
264 if ( vi3 >= 0 )
265 sgSubVec2 ( adjust, t3, vlist[vi3]->texcoord ) ;
266 else
267 {
268 /*
269 OK, there was no match - so just remove
270 any large numbers from the texture coords
271 */
272
273 adjust [ 0 ] = (float) floor ( t1[0] ) ;
274 adjust [ 1 ] = (float) floor ( t1[1] ) ;
275 }
276
277 /*
278 Now adjust the texture coordinates and add them into the list
279 */
280 sgVec2 tmp ;
281 sgSubVec2 ( tmp, t1, adjust ) ; vi1 = add ( v1, tmp, c1 ) ;
282 sgSubVec2 ( tmp, t2, adjust ) ; vi2 = add ( v2, tmp, c2 ) ;
283 sgSubVec2 ( tmp, t3, adjust ) ; vi3 = add ( v3, tmp, c3 ) ;
284 }
285 return add ( vi1, vi2, vi3 ) ;
286 }
287
288
add(short v1,short v2,short v3)289 short OptVertexList::add ( short v1, short v2, short v3 )
290 {
291 if ( v1 == v2 || v2 == v3 || v3 == v1 ) /* Toss degenerate triangles */
292 {
293 vlist [ v1 ] -> dent () ; /* Un-reference their vertices */
294 vlist [ v2 ] -> dent () ;
295 vlist [ v3 ] -> dent () ;
296 return -1 ;
297 }
298
299 tlist [ tnum*3+ 0 ] = v1 ;
300 tlist [ tnum*3+ 1 ] = v2 ;
301 tlist [ tnum*3+ 2 ] = v3 ;
302
303 return tnum++ ;
304 }
305
306 //
307 // Splits vertices across "sharp" edges to improve the normal
308 // direction.
309 //
makeNormals()310 void OptVertexList::makeNormals ()
311 {
312 int i, j ;
313 ssgVertSplitter vs ( vnum, tnum ) ;
314
315 // Copy in the vertex and triangle data
316 for ( i = 0 ; i < vnum ; i ++ )
317 sgCopyVec3 ( vs.vert ( i ), vlist[ i ] -> vertex ) ;
318
319 for ( i = 0 ; i < tnum ; i ++ ) {
320 short* t = tlist + 3 * i ;
321 vs.setTri ( i , t[0], t[1], t[2] );
322 }
323
324 // Run the algorithm. Get the "sharp" angle from somewhere
325 // intelligent, instead of hardcoding it.
326 vs.splitAndCalcNormals () ;
327
328 // Generate the new vertices by cloning their originals
329 int newVerts = vs.newVerts () ;
330 if ( vnum + newVerts > MAX_OPT_VERTEX_LIST )
331 return; // Oh well. No harm done, at least.
332
333 for ( i = 0 ; i < newVerts ; i++ ) {
334 OptVertex *ov = vlist[ vs.origVert ( i ) ] ;
335 vlist[ i + vnum ] = new OptVertex ( ov );
336 }
337 vnum += newVerts;
338
339 // Copy out the new, optimized normal data
340 for(i=0; i<vnum; i++)
341 sgCopyVec3(vlist[i]->normal, vs.norm(i));
342
343 // Zero out the reference counts for the vertices; they will have
344 // changed during the split and will be recalculated below. This
345 // is an ugly hack -- a more elegant solution would be to do the
346 // bump()/dent() calls inside the ssgVertSplitter code as each vertex
347 // is duplicated.
348 for ( i = 0 ; i < vnum ; i++ )
349 while ( vlist [ i ] -> getCount () )
350 vlist [ i ] -> dent () ;
351
352 // Copy out the possibly changed vertices for the triangles
353 for (i = 0 ; i < tnum ; i++ ) {
354 short* oldtri = tlist + 3 * i ;
355 int* newtri = vs.getTri ( i ) ;
356 for ( j = 0 ; j < 3 ; j++ ) {
357 oldtri [ j ] = newtri [ j ] ;
358 vlist[ newtri [ j ] ] -> bump ();
359 }
360 }
361 }
362
find(sgVec3 v,sgVec2 t,sgVec4 c,int tex_frac)363 short OptVertexList::find ( sgVec3 v, sgVec2 t, sgVec4 c, int tex_frac )
364 {
365 for ( short i = 0 ; i < vnum ; i++ )
366 {
367 if ( vlist[i] -> equal ( v, t, c, tex_frac ) )
368 return i ;
369 }
370 return -1 ;
371 }
372
add(sgVec3 v,sgVec2 t,sgVec4 c)373 short OptVertexList::add ( sgVec3 v, sgVec2 t, sgVec4 c )
374 {
375 short i = find ( v, t, c, FALSE ) ;
376
377 if ( i >= 0 )
378 {
379 vlist [ i ] -> bump () ;
380 return i ;
381 }
382
383 vlist [ vnum ] = new OptVertex ( v, t, c ) ;
384 return vnum++ ;
385 }
386
add(ssgLeaf * l)387 void OptVertexList::add ( ssgLeaf *l )
388 {
389 int j ;
390
391 for ( j = 0 ; j < l -> getNumTriangles () ; j ++ )
392 {
393 short v1, v2, v3 ;
394 l -> getTriangle ( j, &v1, &v2, &v3 ) ;
395 add ( l->getVertex ( v1 ), l->getTexCoord ( v1 ), l->getColour ( v1 ),
396 l->getVertex ( v2 ), l->getTexCoord ( v2 ), l->getColour ( v2 ),
397 l->getVertex ( v3 ), l->getTexCoord ( v3 ), l->getColour ( v3 ) ) ;
398 }
399 }
400
401
follow(int tri,int v1,int v2,int backwards,int * len,short * new_vlist,short * new_vc)402 void OptVertexList::follow ( int tri, int v1, int v2, int backwards, int *len,
403 short *new_vlist, short *new_vc )
404 {
405 /* WARNING - RECURSIVE !! */
406
407 v1 = tlist [ tri*3+ v1 ] ;
408 v2 = tlist [ tri*3+ v2 ] ;
409
410 /*
411 This triangle's work is done - dump it.
412 */
413
414 (*len)++ ;
415 sub ( tri ) ;
416
417 /*
418 If the exit edge vertices don't *both* have a reference
419 then we are done.
420 */
421
422 if ( vlist [ v1 ] -> getCount () <= 0 ||
423 vlist [ v2 ] -> getCount () <= 0 )
424 return ;
425
426 /*
427 Search for a polygon that shares that edge in the correct
428 direction - and follow it.
429 */
430
431 for ( int i = 0 ; i < tnum ; i++ )
432 {
433 if ( tlist [ i*3+ 0 ] < 0 ) /* Deleted triangle */
434 continue ;
435
436 if ( backwards )
437 {
438 /* If the previous polygon was backwards */
439
440 if ( tlist [ i*3+ 0 ] == v1 && tlist [ i*3+ 2 ] == v2 )
441 {
442 new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 1 ] ;
443 follow ( i, 0, 1, !backwards, len, new_vlist, new_vc ) ;
444 return ;
445 }
446 else
447 if ( tlist [ i*3+ 1 ] == v1 && tlist [ i*3+ 0 ] == v2 )
448 {
449 new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 2 ] ;
450 follow ( i, 1, 2, !backwards, len, new_vlist, new_vc ) ;
451 return ;
452 }
453 else
454 if ( tlist [ i*3+ 2 ] == v1 && tlist [ i*3+ 1 ] == v2 )
455 {
456 new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 0 ] ;
457 follow ( i, 2, 0, !backwards, len, new_vlist, new_vc ) ;
458 return ;
459 }
460 }
461 else
462 {
463 /* If the previous polygon was forwards... */
464
465 if ( tlist [ i*3+ 0 ] == v1 && tlist [ i*3+ 2 ] == v2 )
466 {
467 new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 1 ] ;
468 follow ( i, 1, 2, !backwards, len, new_vlist, new_vc ) ;
469 return ;
470 }
471 else
472 if ( tlist [ i*3+ 1 ] == v1 && tlist [ i*3+ 0 ] == v2 )
473 {
474 new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 2 ] ;
475 follow ( i, 2, 0, !backwards, len, new_vlist, new_vc ) ;
476 return ;
477 }
478 else
479 if ( tlist [ i*3+ 2 ] == v1 && tlist [ i*3+ 1 ] == v2 )
480 {
481 new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 0 ] ;
482 follow ( i, 0, 1, !backwards, len, new_vlist, new_vc ) ;
483 return ;
484 }
485 }
486 }
487 }
488
489
build_leaf_list(ssgEntity * ent,ssgLeaf ** leaf_list=0)490 static ssgLeaf** build_leaf_list ( ssgEntity *ent, ssgLeaf** leaf_list=0 )
491 {
492 enum { MAX_LEAF_COUNT = 10000 } ;
493 static int leaf_count ;
494
495 if ( leaf_list == NULL )
496 {
497 leaf_list = new ssgLeaf* [ MAX_LEAF_COUNT+1 ] ;
498 leaf_count = 0 ;
499 leaf_list [ leaf_count ] = NULL ;
500 }
501
502 if ( ent -> isAKindOf ( ssgTypeBranch () ) )
503 {
504 ssgBranch *b_ent = (ssgBranch *) ent ;
505 for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ;
506 k = b_ent -> getNextKid () )
507 {
508 build_leaf_list ( k, leaf_list ) ;
509 }
510 }
511 else if ( ent -> isAKindOf ( ssgTypeLeaf () ) )
512 {
513 ssgLeaf *l = (ssgLeaf *) ent ;
514
515 bool found = false ;
516 for ( int i = 0 ; leaf_list [ i ] != NULL ; i ++ )
517 {
518 if ( leaf_list [ i ] == l )
519 {
520 found = true ;
521 break ;
522 }
523 }
524
525 if ( !found && leaf_count < MAX_LEAF_COUNT )
526 {
527 leaf_list [ leaf_count ++ ] = l ;
528 leaf_list [ leaf_count ] = NULL ;
529 }
530 }
531
532 return leaf_list ;
533 }
534
535 /*
536 * NAME
537 * ssgArrayTool
538 *
539 * DESCRIPTION
540 * Process the graph and convert all leaf entities into vertex arrays.
541 *
542 * Each vertex is described by a single index (instead of different
543 * indices into the v, vc, vt-arrays). This may introduce new redundant
544 * vertex data into the data set, which may seem silly. However, when
545 * rendering through OpenGL, it is in most cases the optimal solution
546 * to use indexed vertex arrays, which only have ONE index for all
547 * the vertex data.
548 *
549 * INPUTS
550 *
551 * ent - the entity to process
552 *
553 * vtol - an array of 3 floats used to specify tolerances
554 * vtol[0] - tolerance value for vertices
555 * vtol[1] - tolerance for colours
556 * vtol[2] - tolerance for texture coordinates
557 *
558 * make_normals - if true then averaged vertex normals are computed
559 */
ssgArrayTool(ssgEntity * ent,float * vtol,bool make_normals)560 void ssgArrayTool ( ssgEntity *ent, float* vtol, bool make_normals )
561 {
562 current_vtol = vtol? vtol: optimise_vtol ;
563
564 ssgLeaf** leaf_list = build_leaf_list ( ent ) ;
565 for ( int i = 0 ; leaf_list [ i ] != NULL ; i ++ )
566 {
567 ssgLeaf *l = leaf_list [ i ] ;
568 ssgState *s = l -> getState() ;
569 int cf = l -> getCullFace() ;
570
571 OptVertexList list ( s, cf ) ;
572 list . add ( l ) ;
573
574 if ( list . tnum > 0 ) /* If all the triangles are degenerate maybe */
575 {
576 ssgIndexArray *new_index = new ssgIndexArray ( list . tnum * 3 ) ;
577 ssgVertexArray *new_coords = new ssgVertexArray ( list . vnum ) ;
578 ssgTexCoordArray *new_texcoords = new ssgTexCoordArray ( list . vnum ) ;
579 ssgColourArray *new_colours = new ssgColourArray ( list . vnum ) ;
580 ssgNormalArray *new_normals = 0 ;
581
582 if ( make_normals )
583 {
584 list . makeNormals () ;
585 new_normals = new ssgNormalArray ( list . vnum ) ;
586 }
587
588 for ( int t = 0 ; t < list . tnum ; t++ )
589 {
590 new_index -> add ( list . tlist [ t*3+ 0 ] ) ;
591 new_index -> add ( list . tlist [ t*3+ 1 ] ) ;
592 new_index -> add ( list . tlist [ t*3+ 2 ] ) ;
593 }
594
595 for ( int v = 0 ; v < list . vnum ; v++ )
596 {
597 new_coords -> add ( list . vlist[ v ]->vertex ) ;
598 new_texcoords-> add ( list . vlist[ v ]->texcoord ) ;
599 new_colours -> add ( list . vlist[ v ]->colour ) ;
600 if ( make_normals )
601 new_normals -> add ( list . vlist[ v ]->normal ) ;
602 }
603
604 ssgVtxArray *new_varray = new ssgVtxArray ( GL_TRIANGLES,
605 new_coords, new_normals, new_texcoords, new_colours, new_index ) ;
606 new_varray -> setState ( list.state ) ;
607 new_varray -> setCullFace ( list.cullface ) ;
608
609 ssgBranch *p ;
610
611 /*
612 Add the new leaf
613 */
614 for ( p = l -> getParent ( 0 ) ; p != NULL ;
615 p = l -> getNextParent () )
616 p -> addKid ( new_varray ) ;
617
618 /*
619 Remove the old leaf
620 */
621 for ( p = new_varray -> getParent ( 0 ) ; p != NULL ;
622 p = new_varray -> getNextParent () )
623 p -> removeKid ( l ) ;
624 }
625 }
626 delete[] leaf_list ;
627
628 ent -> recalcBSphere () ;
629 }
630
ssgStripify(ssgEntity * ent)631 void ssgStripify ( ssgEntity *ent )
632 {
633 current_vtol = optimise_vtol ;
634
635 /*
636 Walk down until we find a leaf node, then
637 back up one level, collect all the ssgVtxTables
638 into one big heap and triangulate them.
639 */
640
641 if ( ent -> isAKindOf ( ssgTypeLeaf () ) )
642 return ;
643
644 ssgBranch *b_ent = (ssgBranch *) ent ;
645
646 /*
647 Count number of unique materials (and cull-facedness)
648 - make a list of them. Recursively stripify non-leaf nodes.
649 */
650
651 int stot = 0 ;
652 ssgState **slist = new ssgState *[ b_ent -> getNumKids () ] ;
653 int *cflist = new int [ b_ent -> getNumKids () ] ;
654
655 for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ;
656 k = b_ent -> getNextKid () )
657 {
658 if ( k -> isAKindOf ( ssgTypeVtxTable () ) )
659 {
660 GLenum thisType = ((ssgVtxTable *)k)->getPrimitiveType ();
661 if ((thisType != GL_POINTS) && (thisType != GL_LINES) &&
662 (thisType != GL_LINE_STRIP) && (thisType != GL_LINE_LOOP))
663 {
664 int i ;
665 ssgState *s = ((ssgLeaf *) k ) -> getState() ;
666 int c = ((ssgLeaf *) k ) -> getCullFace() ;
667
668 for ( i = 0 ; i < stot ; i++ )
669 if ( s == slist [ i ] && c == cflist [ i ] )
670 break ;
671
672 if ( i >= stot )
673 {
674 slist [ i ] = s ;
675 cflist [ i ] = c ;
676 stot++ ;
677 }
678 }
679 }
680 else
681 if ( k -> isAKindOf ( ssgTypeBranch () ) )
682 ssgStripify ( k ) ;
683 }
684
685 /*
686 Now, for each unique state, grab all the VtxTable leaf nodes
687 and smoosh them into one.
688 */
689
690 for ( int i = 0 ; i < stot ; i++ )
691 {
692 /*
693 Put it into a triangle-oriented structure and
694 then do stripifying and average normal generation.
695
696 Ick!
697 */
698
699 OptVertexList list ( slist [ i ], cflist [ i ] ) ;
700
701 ssgEntity *k = b_ent -> getKid ( 0 ) ;
702
703 while ( k != NULL )
704 {
705 if ( k -> isAKindOf ( ssgTypeVtxTable () ) &&
706 ((ssgLeaf *) k ) -> getState() == slist [ i ] &&
707 ((ssgLeaf *) k ) -> getCullFace() == cflist [ i ] )
708 {
709 GLenum thisType = ((ssgVtxTable *)k)->getPrimitiveType ();
710 if ((thisType != GL_POINTS) && (thisType != GL_LINES) &&
711 (thisType != GL_LINE_STRIP) && (thisType != GL_LINE_LOOP))
712 { list . add ( (ssgVtxTable *) k ) ;
713 b_ent -> removeKid ( k ) ;
714 k = b_ent -> getKid ( 0 ) ;
715 }
716 else
717 k = b_ent -> getNextKid () ;
718 }
719 else
720 k = b_ent -> getNextKid () ;
721 }
722
723 if ( list . tnum == 0 ) /* If all the triangles are degenerate maybe */
724 continue ;
725
726 /*
727 So, now we have all the important information sucked out of
728 all those nodes and safely tucked away in the OptVertexList
729
730 Let's take this opportunity to compute vertex normals.
731 */
732
733 list . makeNormals () ;
734
735 /*
736 Find the least connected triangle.
737 Use it as the starting point.
738 */
739
740 short tleast, nleast ;
741
742 while ( list . getLeastConnected ( & tleast, & nleast ) )
743 {
744 /* OK, we have our starting point - follow where it
745 leads - but which way to start? We need two vertices
746 with at least two references - and not the least
747 referenced vertex please. */
748
749 short *new_vlist = new short [ list.tnum * 3 ] ;
750 short new_vc = 0 ;
751
752 int striplength = 0 ;
753
754 if ( nleast == list.tlist[tleast*3+0] )
755 {
756 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 0 ] ;
757 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 1 ] ;
758 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 2 ] ;
759 list . follow ( tleast, 1, 2, FALSE, &striplength, new_vlist, & new_vc ) ;
760 }
761 else
762 if ( nleast == list.tlist[tleast*3+1] )
763 {
764 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 1 ] ;
765 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 2 ] ;
766 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 0 ] ;
767 list . follow ( tleast, 2, 0, FALSE, &striplength, new_vlist, & new_vc ) ;
768 }
769 else
770 if ( nleast == list.tlist[tleast*3+2] )
771 {
772 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 2 ] ;
773 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 0 ] ;
774 new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 1 ] ;
775 list . follow ( tleast, 0, 1, FALSE, &striplength, new_vlist, & new_vc ) ;
776 }
777 else
778 ulSetError ( UL_WARNING, "Tleast doesn't contain nleast!" ) ;
779
780 ssgVertexArray *new_coords = new ssgVertexArray ( new_vc ) ;
781 ssgNormalArray *new_normals = new ssgNormalArray ( new_vc ) ;
782 ssgTexCoordArray *new_texcoords = new ssgTexCoordArray ( new_vc ) ;
783 ssgColourArray *new_colours = new ssgColourArray ( new_vc ) ;
784
785 for ( int m = 0 ; m < new_vc ; m++ )
786 {
787 new_coords -> add ( list.vlist[new_vlist[m]]->vertex ) ;
788 new_normals -> add ( list.vlist[new_vlist[m]]->normal ) ;
789 new_texcoords-> add ( list.vlist[new_vlist[m]]->texcoord ) ;
790 new_colours -> add ( list.vlist[new_vlist[m]]->colour ) ;
791 }
792
793 delete [] new_vlist ;
794
795 ssgVtxTable *new_vtable = new ssgVtxTable ( GL_TRIANGLE_STRIP,
796 new_coords, new_normals, new_texcoords, new_colours ) ;
797 new_vtable -> setState ( list.state ) ;
798 new_vtable -> setCullFace ( list.cullface ) ;
799
800 b_ent -> addKid ( new_vtable ) ;
801 }
802 }
803
804 delete [] slist ;
805 delete [] cflist ;
806 }
807
808
809 /*
810 These routines are essentially non-realtime tree optimisations.
811 */
812
safe_replace_kid(ssgBranch * parent,ssgEntity * old_kid,ssgEntity * new_kid)813 static void safe_replace_kid ( ssgBranch *parent, ssgEntity *old_kid, ssgEntity *new_kid )
814 {
815 /*
816 Replace old_kid by new_kid in a "safe" manner.
817 new_kid may be null, in which case old_kid is removed.
818 If parent is null then loop over all parents of old_kid.
819 */
820
821 if ( old_kid == new_kid )
822 return ;
823
824 if ( parent == NULL )
825 {
826 int n = old_kid -> getNumParents () ;
827 while ( n-- > 0 )
828 safe_replace_kid ( old_kid -> getParent ( 0 ), old_kid, new_kid ) ;
829 return ;
830 }
831
832 // assert ( parent -> searchForKid ( old_kid ) >= 0 ) ;
833
834 if ( new_kid == NULL )
835 {
836 if ( parent -> isAKindOf ( ssgTypeSelector () ) )
837 {
838 /* cannot remove kids from selectors */
839 parent -> replaceKid ( old_kid, new ssgInvisible ) ;
840 }
841 else
842 {
843 parent -> removeKid ( old_kid ) ;
844 }
845 }
846 else
847 {
848 parent -> replaceKid ( old_kid, new_kid ) ;
849 }
850 }
851
strip(ssgEntity * ent)852 static void strip ( ssgEntity *ent )
853 {
854 /*
855 Strip off all branches with no kids - and snip out all
856 simple branches with just one kid.
857 A node with user data is always left unchanged.
858 */
859
860 if ( ! ent -> isAKindOf ( ssgTypeBranch () ) )
861 return ;
862
863 ssgBranch *b_ent = (ssgBranch *) ent ;
864
865 for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ;
866 k = b_ent -> getNextKid () )
867 strip ( k ) ;
868
869 switch ( b_ent -> getNumKids () )
870 {
871 case 0:
872 if ( b_ent -> getUserData() == NULL && b_ent -> getName () == NULL )
873 safe_replace_kid ( NULL, b_ent, NULL ) ;
874 break;
875
876 case 1:
877 if ( b_ent -> isA ( ssgTypeBranch () ) &&
878 b_ent -> getUserData () == NULL )
879 {
880 ssgEntity *k = b_ent -> getKid ( 0 ) ;
881 if ( b_ent -> getName () != NULL && k -> getName () != NULL )
882 break;
883 if ( b_ent -> getName () != NULL )
884 k -> setName ( b_ent -> getName () ) ;
885
886 safe_replace_kid ( NULL, b_ent, k ) ;
887 }
888 else if ( ! b_ent -> isAKindOf ( ssgTypeSelector () ) &&
889 b_ent -> getKid ( 0 ) -> isA ( ssgTypeBranch () ) &&
890 b_ent -> getKid ( 0 ) -> getUserData () == NULL )
891 {
892 ssgBranch *b_kid = (ssgBranch *) b_ent -> getKid ( 0 ) ;
893 for ( ssgEntity *k = b_kid -> getKid ( 0 ) ; k != NULL ;
894 k = b_kid -> getNextKid () )
895 b_ent -> addKid ( k ) ;
896 b_ent -> removeKid ( b_kid ) ;
897 b_ent -> recalcBSphere () ;
898 }
899 break;
900
901 default:
902 if ( b_ent -> isDirtyBSphere () )
903 b_ent -> recalcBSphere () ;
904 }
905 }
906
flatten(ssgBranch * parent,ssgEntity * ent,sgMat4 mat)907 void flatten ( ssgBranch *parent, ssgEntity *ent, sgMat4 mat )
908 {
909 /*
910 Move all transforms down to the leaf nodes and
911 then multiply them out. You need to strip() the
912 tree after calling this.
913 */
914
915 sgMat4 mat2 ;
916
917 /*
918 The following nodes may (currently) not be flattened:
919 - ssgCutout,
920 - ssgRangeSelector, and
921 - ssgTransform with user data.
922 */
923 if ( ent -> isAKindOf ( ssgTypeCutout () ) ||
924 ent -> isAKindOf ( ssgTypeRangeSelector () ) ||
925 ( ent -> isA ( ssgTypeTransform () ) &&
926 ent -> getUserData () != NULL ) )
927 {
928 /* Insert a transform node if needed. */
929 if ( mat != NULL ) {
930 ssgTransform *tr = new ssgTransform ;
931 tr -> setTransform ( mat ) ;
932 tr -> addKid ( ent ) ;
933 safe_replace_kid ( parent, ent, tr ) ;
934 }
935
936 /* Traverse as usual. */
937 if ( ent -> isAKindOf ( ssgTypeBranch () ) )
938 {
939 ssgBranch *b_ent = (ssgBranch *) ent ;
940 for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ;
941 k = b_ent -> getNextKid () )
942 flatten ( b_ent, k, NULL ) ;
943 }
944
945 return ;
946 }
947
948 /*
949 Clone the node if needed (there is no need to clone it recursively,
950 especially not past unflattable nodes).
951 */
952 if ( ent -> getRef () > 1 && mat != NULL )
953 {
954 ssgEntity *clone = (ssgEntity *) ent -> clone ( SSG_CLONE_GEOMETRY |
955 SSG_CLONE_USERDATA ) ;
956 safe_replace_kid ( parent, ent, clone ) ;
957 ent = clone ;
958 }
959
960 /*
961 Apply the transformation on leaf nodes.
962 */
963 if ( ent -> isAKindOf ( ssgTypeLeaf () ) )
964 {
965 if ( mat != NULL )
966 ((ssgLeaf *) ent) -> transform ( mat ) ;
967 return ;
968 }
969
970 /*
971 Replace transform nodes with simple branches.
972 */
973 if ( ent -> isAKindOf ( ssgTypeTransform () ) )
974 {
975 ssgTransform *t_ent = (ssgTransform *) ent ;
976
977 t_ent -> getTransform ( mat2 ) ;
978 if ( mat != NULL )
979 sgPostMultMat4 ( mat2, mat ) ;
980
981 mat = sgClassifyMat4 ( mat2 ) != 0 ? mat2 : NULL ;
982
983 ssgBranch *br = new ssgBranch ;
984 /*
985 FIXME! It would have been very neat to do:
986 br -> copy_from ( t_ent, 0 ) ;
987 */
988 br -> setName ( t_ent -> getName () ) ;
989 for ( ssgEntity *k = t_ent -> getKid ( 0 ) ; k != NULL ;
990 k = t_ent -> getNextKid () )
991 br -> addKid ( k ) ;
992 t_ent -> removeAllKids () ;
993
994 safe_replace_kid ( NULL, ent, br ) ;
995 ent = br ;
996 }
997
998 /*
999 Finally traverse the kids.
1000 */
1001 if ( ent -> isAKindOf ( ssgTypeBranch () ) )
1002 {
1003 ssgBranch *b_ent = (ssgBranch *) ent ;
1004 for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ;
1005 k = b_ent -> getNextKid () )
1006 flatten ( b_ent, k, mat ) ;
1007 }
1008
1009 }
1010
ssgFlatten(ssgEntity * ent)1011 void ssgFlatten ( ssgEntity *ent )
1012 {
1013 if ( ! ent -> isAKindOf ( ssgTypeBranch () ) )
1014 return ;
1015
1016 ssgBranch *b_ent = (ssgBranch *) ent ;
1017 sgVec4 *mat = NULL ;
1018 sgMat4 xform, ident ;
1019
1020 /*
1021 If the top level node is a ssgTransform, then do not replace it;
1022 instead load an identity transform and multiply out the matrix.
1023 */
1024
1025 if ( b_ent -> isA ( ssgTypeTransform () ) &&
1026 b_ent -> getUserData () == NULL )
1027 {
1028 sgMakeIdentMat4 ( ident ) ;
1029 ((ssgTransform *) b_ent) -> getTransform ( xform ) ;
1030 ((ssgTransform *) b_ent) -> setTransform ( ident ) ;
1031 mat = xform ;
1032 }
1033
1034 /*
1035 Since the top level node may not be removed, loop over the kids.
1036 Done in two passes because *kid* may be removed.
1037 */
1038 ssgEntity *kid;
1039
1040 for ( kid = b_ent -> getKid ( 0 ) ; kid != NULL ;
1041 kid = b_ent -> getNextKid () )
1042 flatten ( b_ent, kid, mat ) ;
1043
1044 for ( kid = b_ent -> getKid ( 0 ) ; kid != NULL ;
1045 kid = b_ent -> getNextKid () )
1046 strip ( kid ) ;
1047
1048 if ( b_ent -> isDirtyBSphere () )
1049 b_ent -> recalcBSphere () ;
1050 }
1051
1052
1053 /*
1054 * NAME
1055 * ssgTransTool
1056 *
1057 * DESCRIPTION
1058 * Apply a transform (translate, rotate, scale) to all verticies of a graph.
1059 *
1060 * INPUTS
1061 * ent -- the entity to process
1062 * trans -- transform
1063 */
ssgTransTool(ssgEntity * ent,const sgMat4 trans)1064 void ssgTransTool ( ssgEntity *ent, const sgMat4 trans )
1065 {
1066 if ( ent -> isAKindOf ( ssgTypeLeaf () ) )
1067 {
1068 ((ssgLeaf *) ent) -> transform ( trans ) ;
1069 return ;
1070 }
1071
1072 if ( ! ent -> isAKindOf ( ssgTypeBranch () ) )
1073 return ;
1074
1075 ssgBranch *b_ent = (ssgBranch *) ent ;
1076 sgMat4 mat, ident, xform ;
1077
1078 sgCopyMat4 ( mat, trans ) ;
1079
1080 if ( b_ent -> isA ( ssgTypeTransform () ) &&
1081 b_ent -> getUserData () == NULL )
1082 {
1083 sgMakeIdentMat4 ( ident ) ;
1084 ((ssgTransform *) b_ent) -> getTransform ( xform ) ;
1085 ((ssgTransform *) b_ent) -> setTransform ( ident ) ;
1086 sgPreMultMat4 ( mat, xform ) ;
1087 }
1088
1089 else if ( b_ent -> isAKindOf ( ssgTypeTransform () ) ||
1090 b_ent -> isAKindOf ( ssgTypeCutout () ) ||
1091 b_ent -> isAKindOf ( ssgTypeRangeSelector () ) )
1092 {
1093 ulSetError ( UL_WARNING,
1094 "ssgTransTool: Cannot handle this kind of node at top level." ) ;
1095 return ;
1096 }
1097
1098 ssgEntity *kid;
1099 for ( kid = b_ent -> getKid ( 0 ) ; kid != NULL ;
1100 kid = b_ent -> getNextKid () )
1101 flatten ( b_ent, kid, mat ) ;
1102
1103 for ( kid = b_ent -> getKid ( 0 ) ; kid != NULL ;
1104 kid = b_ent -> getNextKid () )
1105 strip ( kid ) ;
1106
1107 b_ent -> recalcBSphere () ;
1108 }
1109