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