Angus Dorbie (dorbie++at++bitch.reading.sgi.com)
Mon, 11 Mar 1996 09:02:57 +0100
Here's code which querrys the scene graph to vertex level & uses
the info to add a cull callback. It doesn't handle indexed
geosets because I was too lazy to add this. Actually this is a
problem because under some conditions the loader gave me indexed
information. In my case these were only individual triangles so
my dangerous solution was to ignore the fact that there indexed
so beware.
/* generate radial cull information based on vertex information */
void Modify_Tree_Cull(pfNode *node, int depth)
{
int i, j;
if( pfIsOfType(node, pfGetGeodeClassType()) )
{
QuerryGSets((pfGeode*)node);
}
else if( pfIsOfType(node, pfGetGroupClassType()) )
{
/*
* Recurse on children
*/
for(i = 0; i < pfGetNumChildren(node); i++)
{
/*
for(j = 0; j < depth; j++)
printf("######");
printf(" %d\n", i);
*/
Modify_Tree_Cull( pfGetChild(node, i) , depth +1);
}
}
}
/* generates angles subtended with origin for radial culling */
/* only supports angles less than 180 degrees, because it assumes */
/* the angle subtends at least first two vertices, some assumption */
/* of this nature is required because we don't consider vertex */
/* connectivity */
static int QuerryGSets(pfGeode *geode)
{
int i, j, ng, nv, extends;
pfVec3 *cull_data;
pfVec3 *coords;
void *coordaddr;
ushort *indices;
pfGeoSet *gset;
float angle_centre, angle_low, angle_high, vangle, tmp_cent;
float delta, delta2;
angle_centre = angle_low = angle_high = -1.0f;
extends = FALSE;
ng = pfGetNumGSets( geode);
printf("found a geode with %d gsets\n",ng);
if(ng > 0)
{
for(i = 0; i < ng; i++)
{
gset = pfGetGSet( geode, i);
pfGetGSetAttrLists( gset, PFGS_COORD3, &coordaddr, &indices);
coords = (pfVec3 *) coordaddr;
if(!indices | TRUE) /* risk it with indexed stuff too */
{
nv = getNumVerts(gset);
printf(" with %d verts\n",nv);
/* determine radial information from vertices */
for(j=0; j<nv; j++)
{
if((*(coords+j))[PF_X] != 0.0f && (*(coords+j))[PF_Y] != 0.0f)
{
/* produce angle based on vertex */
vangle = pfArcTan2( (*(coords+j))[PF_Y], (*(coords+j))[PF_X] );
vangle -= 90.0f;
while(vangle < 0.0f)
vangle += 360.0f;
if(angle_low < 0.0f) /* first entry */
angle_centre = angle_low = angle_high = vangle;
else /* additional entry */
{
/* check to see if new angle extends beyond 180 degrees */
delta = fabsf(angle_centre - angle_low);
if(delta > 180.0f)
delta = 360.0f - delta;
tmp_cent = angle_centre + 180.0f;
if(tmp_cent > 360.0f)
tmp_cent -= 360.0f;
delta2 = fabsf(tmp_cent - vangle);
if(delta2 > 180.0f)
delta2 = 360.0f - delta2;
if(delta2 < delta)
{
printf("Radial culling aborted, > 180 degrees\n");
return(FALSE);
}
/* check to see if new angle extends segment */
if( ((vangle > angle_low && vangle < angle_high) !=
(angle_centre > angle_low && angle_centre < angle_high)) ||
(angle_low == angle_high) )
{
/* extend nearest boundary */
delta = fabsf(angle_high - vangle);
if(delta > 180.0f)
delta = 360.0f - delta;
delta2 = fabsf(angle_low - vangle);
if(delta2 > 180.0f)
delta2 = 360.0f - delta2;
if( delta < delta2 )
angle_high = vangle;
else
angle_low = vangle;
/* check minmax order */
if(angle_high < angle_low)
{
vangle = angle_high;
angle_high = angle_low;
angle_low = vangle;
}
/* determine centre position */
angle_centre = angle_low + .5f * (angle_high - angle_low);
/* move 180 degrees if in trouble */
if( (angle_high - angle_centre) > 90.0f)
angle_centre += 180.0f;
/* normalise */
if(angle_centre > 360.0f)
angle_centre -= 360.0f;
}
}
}
}
}
else
{
printf("Indexed GeoSet!!!!\n");
}
}
}
/* now add a cull callback with the relevant data */
cull_data = (pfVec3 *)pfMalloc(sizeof(pfVec3), pfGetSharedArena());
(*cull_data)[0] = angle_centre;
delta = fabsf(angle_centre - angle_low);
if(delta > 180.0f)
delta = 360 - delta;
(*cull_data)[1] = delta;
printf(" Radially culling geode with %.3f, %.3f, %.3f, (%.3f)\n",
angle_low, angle_centre, angle_high, delta);
pfNodeTravData(geode, PFTRAV_CULL, (void *)cull_data);
pfNodeTravFuncs(geode, PFTRAV_CULL, _Radial_Cull, NULL);
return(TRUE);
}
static int getNumVerts(pfGeoSet *gset)
{
int i, nprims, nverts;
int *lengths;
int sumLengths = 0;
nprims = pfGetGSetNumPrims( gset );
lengths = pfGetGSetPrimLengths( gset );
if(lengths)
{
for(i = 0; i<nprims; i++)
{
sumLengths += *(lengths + i);
}
}
switch (pfGetGSetPrimType( gset ) )
{
case PFGS_POINTS:
printf("PFGS_POINTS");
nverts = nprims;
break;
case PFGS_LINES:
printf("PFGS_LINES");
nverts = nprims * 2;
break;
case PFGS_LINESTRIPS:
printf("PFGS_LINESTRIPS");
nverts = sumLengths;
break;
case PFGS_FLAT_LINESTRIPS:
printf("PFGS_FLAT_LINESTRIPS");
nverts = sumLengths;
break;
case PFGS_TRIS:
printf("PFGS_TRIS");
nverts = nprims * 3;
break;
case PFGS_QUADS:
printf("PFGS_QUADS");
nverts = nprims * 4;
break;
case PFGS_TRISTRIPS:
printf("PFGS_TRISTRIPS");
nverts = sumLengths;
break;
case PFGS_FLAT_TRISTRIPS:
printf("PFGS_FLAT_TRISTRIPS");
nverts = sumLengths;
break;
default:
printf("Primitive type Unknown: %d ", pfGetGSetPrimType( gset ));
nverts = 0;
}
return nverts;
}
/* tests for object in view first */
static int _Radial_Cull(pfTraverser *trav, void *data)
{
pfChannel *chan;
pfVec3 _xyz, _hpr;
float separation;
/* values calculated by pre-cull */
extern float Chan_heading, Chan_HalfWidth;
/* things are symmetrical so find angle & compare against width */
separation = fabsf( (*(pfVec3 *)data)[0] - Chan_heading);
if(separation > 180.0f)
separation = 360.0f - separation;
if(separation > (Chan_HalfWidth + (*(pfVec3 *)data)[1]) )
{
return(PFTRAV_PRUNE);
}
else
{
return(PFTRAV_CONT);
}
}
-- Angus Dorbie, Silicon Graphics Ltd, UK dorbie++at++reading.sgi.com
This archive was generated by hypermail 2.0b2 on Mon Aug 10 1998 - 17:52:32 PDT