3 // Written by Harald JOHNSEN, started June 2005.
5 // Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 # include <simgear_config.h>
28 #include <simgear/debug/logstream.hxx>
29 #include <simgear/screen/extensions.hxx>
30 #include <simgear/scene/model/animation.hxx>
31 #include <simgear/scene/model/model.hxx>
32 #include <simgear/environment/visual_enviro.hxx>
35 #include "shadowvolume.hxx"
38 geometry and edge list
39 - traverse object graph until leaf to get geometry
40 - what about transform and selection ?
41 - use sub objects rather then objects
42 - get local transform and selection
43 - range anim : ssgRangeSelector ( min, max ) => ssgSelector ( true/false )
44 => selection [0..n], kids [0..n]
45 - select/timed anim : ssgSelector ( true/false )
47 - spin/rotate/trans/scale/... : ssgTransform ( matrix )
50 - for one object branch
53 - save address in cache
54 - when rendering object
56 - getNetTransform + object global rotation (ac) => transform for light position
57 - go up in tree and check isSelected( self )
58 - generate connectivity each time the geometry change
59 => using local light so connectivity never changes
60 - generate active edge list when :
62 - subpart moves (animation code)
64 => cache rotation matrix and generate edge list only if something change
65 => even if it changes, no need to do that every frame
67 - static objects have static edge list if light does not move
70 - render full scene as normal
71 - render occluder in stencil buffer with their shadow volumes
72 - apply stencil to framebuffer (darkens shadowed parts)
74 shadows using the alpha buffer
75 http://wwwvis.informatik.uni-stuttgart.de/~roettger/html/Pages/shadows.html
79 // - shadow for objects
81 // * tile objects (from .stg)
83 // - random objects => tie shadow geometry to lib objects and reuse them
84 // - zfail if camera inside shadow
85 // - queue geometry work if lot of objects
86 // * add a render property on/off (for aircraft, for scene objects, for ai)
87 // * add a render property in rendering dialog
88 // * filter : halo, light, shadow, disc, disk, flame, (exhaust), noshadow
90 static int statSilhouette=0;
91 static int statGeom=0;
94 static SGShadowVolume *states;
95 static glBlendEquationProc glBlendEquationPtr = NULL;
96 #define GL_MIN_EXT 0x8007
97 #define GL_MAX_EXT 0x8008
100 SGShadowVolume::ShadowCaster::ShadowCaster( int _num_tri, ssgBranch * _geometry_leaf ) :
101 geometry_leaf ( _geometry_leaf ),
102 scenery_object ( 0 ),
108 lastSilhouetteIndicesCount ( 0 )
110 int num_tri = _num_tri;
111 numTriangles = num_tri;
112 triangles = new triData[ num_tri ];
113 indices = new int[1 + num_tri * 3];
114 vertices = new sgVec4[1 + num_tri * 3];
115 silhouetteEdgeIndices = new GLushort[(1+num_tri) * 3*3];
116 indices [ num_tri * 3 ] = num_tri * 3;
117 sgSetVec3(last_lightpos, 0.0, 0.0, 0.0);
120 ssgBranch *branch = (ssgBranch *) _geometry_leaf;
121 while( branch && branch->getNumParents() > 0 ) {
122 if( branch->isAKindOf(ssgTypeSelector())) {
123 first_select = branch;
126 if( sgCheckAnimationBranch( (ssgEntity *) branch ) )
127 if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1) {
128 first_select = branch;
131 branch = branch->getParent(0);
135 void SGShadowVolume::ShadowCaster::addLeaf( int & tri_idx, int & ind_idx, ssgLeaf *geometry_leaf ) {
136 int num_tri = geometry_leaf->getNumTriangles();
137 for(int i = 0; i < num_tri ; i ++ ) {
140 geometry_leaf->getTriangle( i, &v1, &v2, &v3 );
141 sgCopyVec3(a, geometry_leaf->getVertex(v1));
142 sgCopyVec3(b, geometry_leaf->getVertex(v2));
143 sgCopyVec3(c, geometry_leaf->getVertex(v3));
146 sgMakePlane ( triangles[p].planeEquations, a, b, c );
147 sgCopyVec3(vertices[ind_idx + v1], a);
148 sgCopyVec3(vertices[ind_idx + v2], b);
149 sgCopyVec3(vertices[ind_idx + v3], c);
150 vertices[ind_idx + v1][SG_W] = 1.0f;
151 vertices[ind_idx + v2][SG_W] = 1.0f;
152 vertices[ind_idx + v3][SG_W] = 1.0f;
153 indices[p*3] = ind_idx + v1;
154 indices[p*3+1] = ind_idx + v2;
155 indices[p*3+2] = ind_idx + v3;
161 isTranslucent |= geometry_leaf->isTranslucent() ? true : false;
162 int num_ind = geometry_leaf->getNumVertices();
166 SGShadowVolume::ShadowCaster::~ShadowCaster() {
170 delete [] silhouetteEdgeIndices;
175 bool SGShadowVolume::ShadowCaster::sameVertex(int edge1, int edge2) {
179 sgSubVec3( delta_v, vertices[edge1], vertices[edge2]);
180 if( delta_v[SG_X] != 0.0) return false;
181 if( delta_v[SG_Y] != 0.0) return false;
182 if( delta_v[SG_Z] != 0.0) return false;
187 //Calculate neighbour faces for each edge
188 // caution, this is O(n2)
189 void SGShadowVolume::ShadowCaster::SetConnectivity(void)
193 //set the neighbour indices to be -1
194 for(int ii=0; ii<numTriangles; ++ii)
195 triangles[ii].neighbourIndices[0] =
196 triangles[ii].neighbourIndices[1] =
197 triangles[ii].neighbourIndices[2] = -1;
199 //loop through triangles
200 for(int i=0; i<numTriangles-1; ++i)
202 //loop through edges on the first triangle
203 for(int edgeI=0; edgeI<3; ++edgeI)
205 //continue if this edge already has a neighbour set
206 if(triangles[i].neighbourIndices[ edgeI ]!=-1)
209 //get the vertex indices on each edge
210 int edgeI1=indices[i*3+edgeI];
211 int edgeI2=indices[i*3+(edgeI == 2 ? 0 : edgeI+1)];
213 //loop through triangles with greater indices than this one
214 for(int j=i+1; j<numTriangles; ++j)
216 //loop through edges on triangle j
217 for(int edgeJ=0; edgeJ<3; ++edgeJ)
219 //continue if this edge already has a neighbour set
220 if(triangles[j].neighbourIndices[ edgeJ ]!=-1) {
223 //get the vertex indices on each edge
224 int edgeJ1=indices[j*3+edgeJ];
225 int edgeJ2=indices[j*3+(edgeJ == 2 ? 0 : edgeJ+1)];
227 //if these are the same (possibly reversed order), these faces are neighbours
229 //no, we only use reverse order because same order means that
230 //the triangle is wrongly oriented and that will cause shadow artifact
231 if( sameVertex(edgeI1, edgeJ1) && sameVertex(edgeI2, edgeJ2) ) {
232 // can happens with 'bad' geometry
233 // printf("flipped triangle detected...check your model\n");
237 if( sameVertex(edgeI1, edgeJ2) && sameVertex(edgeI2, edgeJ1) )
239 int edgeI3=indices[i*3+(edgeI == 0 ? 2 : edgeI-1)];
240 int edgeJ3=indices[j*3+(edgeJ == 0 ? 2 : edgeJ-1)];
241 if( sameVertex(edgeI3, edgeJ3) ) {
242 // can happens with 'bad' geometry
243 // printf("duplicated tri...check your model\n");
247 triangles[i].neighbourIndices[edgeI]=j;
248 triangles[j].neighbourIndices[edgeJ]=i;
258 // printf("submodel has %d triangles and %d shared edges\n", numTriangles, edgeCount);
261 //calculate silhouette edges
262 void SGShadowVolume::ShadowCaster::CalculateSilhouetteEdges(sgVec3 lightPosition)
264 //Calculate which faces face the light
265 for(int i=0; i<numTriangles; ++i)
267 if( sgDistToPlaneVec3 ( triangles[i].planeEquations, lightPosition ) > 0.0 )
268 triangles[i].isFacingLight=true;
270 triangles[i].isFacingLight=false;
274 int iEdgeIndices = 0;
275 sgVec4 farCap = {-lightPosition[SG_X], -lightPosition[SG_Y], -lightPosition[SG_Z], 1.0f};
276 sgCopyVec4( vertices[ numTriangles*3 ], farCap );
278 for(int t=0; t < numTriangles; t++) {
280 //if this face is not facing the light, not a silhouette edge
281 if(!triangles[t].isFacingLight)
283 triangles[t].isSilhouetteEdge[0]=false;
284 triangles[t].isSilhouetteEdge[1]=false;
285 triangles[t].isSilhouetteEdge[2]=false;
289 for(int j = 0 ; j < 3 ; j++) {
290 //this face is facing the light
291 //if the neighbouring face is not facing the light, or there is no neighbouring face,
292 //then this is a silhouette edge
293 if(triangles[t].neighbourIndices[j]==-1 ||
294 !triangles[triangles[t].neighbourIndices[j]].isFacingLight ) {
295 triangles[t].isSilhouetteEdge[j]=true;
296 silhouetteEdgeIndices[ iEdgeIndices++ ] = indices[v+(j == 2 ? 0 : j+1)];
297 silhouetteEdgeIndices[ iEdgeIndices++ ] = indices[v+j];
298 silhouetteEdgeIndices[ iEdgeIndices++ ] = numTriangles * 3;
301 triangles[t].isSilhouetteEdge[j]=false;
304 lastSilhouetteIndicesCount = iEdgeIndices;
307 void SGShadowVolume::ShadowCaster::DrawInfiniteShadowVolume(sgVec3 lightPosition, bool drawCaps)
309 glEnableClientState ( GL_VERTEX_ARRAY ) ;
310 glVertexPointer ( 4, GL_FLOAT, 0, vertices ) ;
311 glDrawElements ( GL_TRIANGLES, lastSilhouetteIndicesCount, GL_UNSIGNED_SHORT, silhouetteEdgeIndices ) ;
313 //Draw caps if required
316 glBegin(GL_TRIANGLES);
318 for(int i=0; i<numTriangles; ++i)
320 if(triangles[i].isFacingLight) {
322 glVertex3fv( vertices[indices[v+0]] );
323 glVertex3fv( vertices[indices[v+1]] );
324 glVertex3fv( vertices[indices[v+2]] );
333 void SGShadowVolume::ShadowCaster::getNetTransform ( ssgBranch * branch, sgMat4 xform )
335 // one less matmult...
337 while( branch && branch != lib_object ) {
338 if( branch->isA(ssgTypeTransform()) ) {
341 ((ssgTransform *) branch)->getTransform( xform );
344 ((ssgTransform *) branch)->getTransform(transform);
345 sgPostMultMat4 ( xform, transform ) ;
348 branch = branch->getParent( 0 );
351 sgMakeIdentMat4 ( xform ) ;
354 // check the value of <select> and <range> animation
355 // wich have been computed during the rendering
356 bool SGShadowVolume::ShadowCaster::isSelected ( ssgBranch * branch, float dist ) {
357 while( branch && branch != lib_object) {
358 if( sgCheckAnimationBranch( (ssgEntity *) branch ) ) {
359 if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1)
360 if( ((SGShadowAnimation *) branch->getUserData())->get_condition_value() )
363 // recompute range check since the value in the branch is shared by multiple objects
364 // we can only have the last value
365 if( branch->isA(ssgTypeRangeSelector()) )
366 if( dist >= ((ssgRangeSelector *) branch)->getRange(1) ||
367 dist < ((ssgRangeSelector *) branch)->getRange(0))
369 if( branch->isA(ssgTypeSelector()) )
370 if( !((ssgSelector *) branch)->isSelected(0) )
372 branch = branch->getParent(0);
377 trans ----- personality ---- shared object
380 *--------* shared object
384 void SGShadowVolume::ShadowCaster::computeShadows(sgMat4 rotation, sgMat4 rotation_translation,
385 OccluderType occluder_type) {
387 // check the select and range ssgSelector node
388 // object can't cast shadow if it is not visible
390 sgCopyVec4( trans, rotation_translation[3] );
391 sgAddVec4( trans, states->CameraViewM[3] );
392 float dist = sgLengthVec3( trans );
394 if( first_select && ! isSelected( first_select, dist) )
397 // get the transformations : this comes from animation code for example
401 int deltaFrame = occluder_type == SGShadowVolume::occluderTypeAircraft ? 0 : 9;
402 if( states->frameNumber - frameNumber > deltaFrame) {
405 getNetTransform( (ssgBranch *) geometry_leaf, transform );
406 sgCopyMat4( last_transform, transform );
407 sgPostMultMat4( transform, rotation );
408 sgTransposeNegateMat4 ( invTransform, transform );
410 sgCopyVec3( lightPos, states->sunPos );
411 sgXformPnt3( lightPos, invTransform );
414 sgNormaliseVec3( lightPosNorm, lightPos );
415 float deltaPos = sgAbs(lightPosNorm[0] - last_lightpos[0]) +
416 sgAbs(lightPosNorm[1] - last_lightpos[1]) +
417 sgAbs(lightPosNorm[2] - last_lightpos[2]);
418 // if the geometry has rotated/moved enought then
419 // we need to recompute the silhouette
420 // but this computation does not need to be done each frame
421 if( deltaPos > 0.0 ) {
422 CalculateSilhouetteEdges( lightPos );
423 sgCopyVec3( last_lightpos, lightPosNorm );
424 frameNumber = states->frameNumber ;
428 sgCopyMat4( transf, last_transform );
429 sgPostMultMat4( transf, rotation_translation );
430 glLoadMatrixf ( (float *) states->CameraViewM ) ;
431 glMultMatrixf( (float *) transf );
433 if( states->shadowsDebug_enabled )
435 glStencilFunc(GL_ALWAYS, 0, ~0);
436 glDisable( GL_CULL_FACE );
437 glDisable( GL_DEPTH_TEST );
438 glDisable(GL_STENCIL_TEST);
439 glColorMask(1, 1, 1, 1);
440 glColor4f(0.0, 0.0, 1.0, 1.0);
442 for(int i=0; i<numTriangles; ++i)
444 if(!triangles[i].isFacingLight)
447 //Loop through edges on this face
448 sgVec3 vertex1, vertex2, vertex3;
449 sgCopyVec3(vertex1, vertices[indices[v+0]]);
450 sgCopyVec3(vertex2, vertices[indices[v+1]]);
451 sgCopyVec3(vertex3, vertices[indices[v+2]]);
453 if(triangles[i].isSilhouetteEdge[0]) {
454 glVertex3fv(vertex2);
455 glVertex3fv(vertex1);
457 if(triangles[i].isSilhouetteEdge[1]) {
458 glVertex3fv(vertex2);
459 glVertex3fv(vertex3);
461 if(triangles[i].isSilhouetteEdge[2]) {
462 glVertex3fv(vertex3);
463 glVertex3fv(vertex1);
467 glColorMask(0, 0, 0, 0);
468 glEnable( GL_CULL_FACE );
469 glEnable( GL_DEPTH_TEST );
470 glEnable(GL_STENCIL_TEST);
474 // TODO:compute intersection with near clip plane...
475 bool needZFail=false;
477 // GL_INCR_WRAP_EXT, GL_DECR_WRAP_EXT
481 //Increment stencil buffer for back face depth fail
482 glStencilFunc(GL_ALWAYS, 0, ~0);
483 glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
484 glCullFace(GL_FRONT);
486 DrawInfiniteShadowVolume( lightPos, true);
488 //Decrement stencil buffer for front face depth fail
489 glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
492 DrawInfiniteShadowVolume( lightPos, true);
496 //Increment stencil buffer for front face depth pass
497 if( states->use_alpha ) {
498 glBlendEquationPtr( GL_FUNC_ADD );
499 glBlendFunc( GL_ONE, GL_ONE );
500 glColor4ub(1, 1, 1, 16);
502 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
503 glStencilFunc(GL_ALWAYS, 0, ~0);
504 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
508 DrawInfiniteShadowVolume( lightPos, states->shadowsAC_transp_enabled & isTranslucent);
510 //Decrement stencil buffer for back face depth pass
511 if( states->use_alpha ) {
512 glBlendEquationPtr( GL_FUNC_REVERSE_SUBTRACT );
513 glBlendFunc( GL_ONE, GL_ONE );
514 glColor4ub(1, 1, 1, 16);
516 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
518 glCullFace(GL_FRONT);
520 DrawInfiniteShadowVolume( lightPos, states->shadowsAC_transp_enabled & isTranslucent);
525 void SGShadowVolume::SceneryObject::computeShadows(void) {
527 // compute intersection with view frustum
528 // use pending_object (pointer on obj transform) & tile transform
530 if( !scenery_object ) {
531 if( states->frameNumber - states->lastTraverseTreeFrame > 5 ) {
534 traverseTree( pending_object );
535 states->lastTraverseTreeFrame = states->frameNumber;
539 sgMat4 rotation, rotation_translation;
540 scenery_object->getNetTransform ( rotation_translation );
541 // split placement offset into rotation and offset
542 // rotation from model inside world
543 // we are not interested in translation since the light is very far away
544 // without translation we reduce the frequency of updates of the silouhette
545 sgCopyMat4( rotation, rotation_translation );
546 sgSetVec4( rotation[3], 0, 0, 0, 1);
548 ShadowCaster_list::iterator iShadowCaster;
549 for(iShadowCaster = parts.begin() ; iShadowCaster != parts.end() ; iShadowCaster ++ ) {
550 (*iShadowCaster)->computeShadows(rotation, rotation_translation, occluder_type);
554 static ssgCullResult cull_test ( ssgEntity *e, sgFrustum *f, sgMat4 m, int test_needed )
559 sgSphere tmp = *(e->getBSphere()) ;
561 if ( tmp.isEmpty () )
564 tmp . orthoXform ( m ) ;
565 if( tmp.center[2] == 0.0 )
567 // cull if too small on screen
568 if ( tmp.radius / sgAbs(tmp.center[2]) < 1.0 / 40.0 )
571 return (ssgCullResult) f -> contains ( &tmp ) ;
575 void SGShadowVolume::cull ( ssgBranch *b, sgFrustum *f, sgMat4 m, int test_needed )
577 int cull_result = cull_test ( (ssgEntity *) b, f, m, test_needed ) ;
579 if ( cull_result == SSG_OUTSIDE )
581 if( b->isA( ssgTypeTransform() ) ) {
583 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( b );
584 if( iSceneryObject != sceneryObjects.end() ) {
585 SceneryObject *an_occluder = iSceneryObject->second;
586 if( shadowsTO_enabled && (an_occluder->occluder_type == occluderTypeTileObject) ||
587 shadowsAI_enabled && (an_occluder->occluder_type == occluderTypeAI ) ||
588 shadowsAC_enabled && (an_occluder->occluder_type == occluderTypeAircraft ) )
589 an_occluder->computeShadows();
593 sgMat4 tmp, transform ;
594 sgCopyMat4 ( tmp, m ) ;
595 ((ssgTransform *)b)->getTransform( transform );
596 sgPreMultMat4 ( tmp, transform ) ;
598 glLoadMatrixf ( (float *) tmp ) ;
599 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid() )
600 cull ( (ssgBranch *) e, f, tmp, cull_result != SSG_INSIDE ) ;
602 } else if( b->isAKindOf( ssgTypeSelector() ) ) {
603 int s = ((ssgSelector *) b)->getSelect() ;
604 if( b->isA( ssgTypeRangeSelector() ) ) {
605 float range = sgLengthVec3 ( m [ 3 ] ) ;
606 s = (range < ((ssgRangeSelector *) b)->getRange(1) &&
607 range >= ((ssgRangeSelector *) b)->getRange(0) ) ? 1 : 0;
609 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid(), s >>= 1 )
611 cull ( (ssgBranch *) e, f, m, cull_result != SSG_INSIDE ) ;
612 } else if( b->isAKindOf( ssgTypeBranch() ) ) {
613 char *name = b->getName();
614 // quick exit for the hundreds of ground leafs
615 if( name && !strcmp(name, "LocalTerrain") )
617 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid() )
618 if( ! e->isAKindOf( ssgTypeLeaf() ) )
619 cull ( (ssgBranch *) e, f, m, cull_result != SSG_INSIDE ) ;
624 1) starting at the root of a scene object we follow all branch to find vertex leaf.
625 each vertex leaf will create a shadow sub object so that the shadow casted by this
626 geometry can be correctly rendered according to select or transform nodes.
627 2) models are not optimized for shadows because the geometry is cut by
628 - select / transform nodes
629 - material differences
630 since we are not interested in the geometry material we try to merge same level
631 leafs. this can divide the number of shadow sub models by a lot (reducing the cpu
632 overhead for matrix computation) and at the end this will reduce the number of
633 silouhette edge by a lot too.
635 static bool filterLeaf(ssgLeaf *this_kid) {
636 const char *leaf_name = this_kid->getName();
637 /* ssgSimpleState *sst = (ssgSimpleState *) this_kid->getState();
638 if( sst && sst->isTranslucent() )
645 for( buff = lname; *leaf_name && l < (sizeof( lname )-1); ++buff, l++ )
646 *buff = tolower(*leaf_name++);
648 if( !strncmp(lname, "noshadow", 8) )
652 void SGShadowVolume::SceneryObject::traverseTree(ssgBranch *branch) {
656 if( sgCheckAnimationBranch( (ssgEntity *) branch ) ) {
657 if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1)
658 if( ((SGShadowAnimation *) branch->getUserData())->get_condition_value() )
662 for(int i = 0 ; i < branch->getNumKids() ; i++) {
663 ssgEntity *this_kid = branch->getKid( i );
664 if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
665 if( filterLeaf( (ssgLeaf *) this_kid ) ) {
666 num_tri += ((ssgLeaf *) this_kid)->getNumTriangles();
670 traverseTree( (ssgBranch *) this_kid );
675 ShadowCaster *new_part = new ShadowCaster( num_tri, branch);
676 new_part->scenery_object = scenery_object;
677 new_part->lib_object = lib_object;
678 new_part->isTranslucent = false;
679 for(int i = 0 ; i < branch->getNumKids() ; i++) {
680 ssgEntity *this_kid = branch->getKid( i );
681 if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
682 if( filterLeaf( (ssgLeaf *) this_kid ) )
683 new_part->addLeaf( tri_idx, ind_idx, (ssgLeaf *) this_kid );
686 // only do that for aircraft
687 if( occluder_type != SGShadowVolume::occluderTypeAircraft )
688 new_part->isTranslucent = false;
689 new_part->SetConnectivity();
690 parts.push_back( new_part );
694 void SGShadowVolume::SceneryObject::find_trans(void) {
695 ssgBranch *branch = pending_object;
696 // check the existence of the root node
697 while( branch && branch->getNumParents() > 0 ) {
698 branch = branch->getParent(0);
700 // check if we are connected to the scene graph
701 if( !branch->isA(ssgTypeRoot() ) )
703 scenery_object = pending_object;
706 SGShadowVolume::SceneryObject::SceneryObject(ssgBranch *_scenery_object, OccluderType _occluder_type) :
707 scenery_object ( 0 ),
708 pending_object ( _scenery_object ),
709 occluder_type ( _occluder_type )
711 // queue objects, don't do all the work in the first frames because of
712 // massive cpu power needed
714 if( occluder_type == SGShadowVolume::occluderTypeAircraft )
715 lib_object = _scenery_object;
717 lib_object = (ssgBranch *) ((ssgBranch *)_scenery_object->getKid(0))->getKid(0);
720 SGShadowVolume::SceneryObject::~SceneryObject()
722 ShadowCaster_list::iterator iParts;
723 for(iParts = parts.begin() ; iParts != parts.end(); iParts++ ) {
729 void SGShadowVolume::computeShadows(void) {
731 // intensity = ambient + lights
732 // lights = sun_const * (sun_angle . normal)
733 double dot_light = cos(sun_angle);
734 // do nothing if sun is under horizon
735 if( dot_light < 0.2 )
738 //Draw shadow volumes
739 glPushAttrib(GL_ALL_ATTRIB_BITS);
740 glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ;
741 glDisableClientState ( GL_COLOR_ARRAY ) ;
742 glDisableClientState ( GL_NORMAL_ARRAY ) ;
743 glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
746 glColorMask(0, 0, 0, 1);
747 glClearColor(0.0, 0.0, 0.0, 0.0 );
748 glClear(GL_COLOR_BUFFER_BIT);
753 glClear(GL_STENCIL_BUFFER_BIT);
754 glColorMask(0, 0, 0, 0);
755 glEnable(GL_STENCIL_TEST);
759 glDisable( GL_LIGHTING );
761 glEnable( GL_CULL_FACE );
762 // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
763 glPolygonOffset(0.0,2.0);
764 // glPolygonOffset(0.0,30.0);
765 glEnable(GL_POLYGON_OFFSET_FILL);
767 glShadeModel(GL_FLAT);
769 glDepthMask( false );
770 glEnable( GL_DEPTH_TEST );
771 glDepthFunc(GL_LESS);
776 sgEnviro.getFOV( w, h );
777 frustum.setFOV( w, h );
778 frustum.setNearFar(0.1f, 5000.0f);
780 ssgGetModelviewMatrix( m );
781 cull( ssg_root, &frustum, m, true);
785 glMatrixMode ( GL_PROJECTION ) ;
788 glOrtho ( -100, 100, -100, 100, -1, 1 ) ;
789 glMatrixMode ( GL_MODELVIEW ) ;
793 glDisable(GL_DEPTH_TEST);
794 glDisable(GL_CULL_FACE);
795 // glBindTexture(GL_TEXTURE_2D, 0);
796 glPolygonMode(GL_FRONT, GL_FILL);
798 // there is a flaw in the Roettger paper, this does not work for some geometry
799 // where we increment (multiply) the buffer a few times then we
800 // decrement (divide) a few times. Decrementing more then once will give a
801 // non shadowed parts with mask value < 0.25 because the incrementation was
803 // Solution : don't use a start value as high as 0.25 but the smallest value
804 // posible ie 1/256. This is not a general solution, a stencil buffer will
805 // support 255 incrementation before clamping, the alpha mask only 7.
806 // => that still does not work with our geometry so use subtractive blend
808 // clamp mask = {0,16,32,64,...} => {0,16,16,16,...}
809 glBlendEquationPtr( GL_MIN_EXT );
810 glBlendFunc( GL_DST_COLOR, GL_ONE );
811 glColor4ub(1, 1, 1, 16);
812 glRectf(-100,-100,100,100);
813 // scale mask = {0,16} => {0,64}
814 glBlendEquationPtr( GL_FUNC_ADD );
815 glBlendFunc( GL_DST_COLOR, GL_ONE );
816 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
817 glRectf(-100,-100,100,100);
818 glRectf(-100,-100,100,100);
819 // negate mask => {0,64} => {255, 191}
820 glBlendFunc( GL_ONE_MINUS_DST_COLOR, GL_ZERO );
821 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
822 glRectf(-100,-100,100,100);
824 glColorMask(1, 1, 1, 1);
825 glBlendFunc( GL_ZERO, GL_DST_ALPHA );
826 glColor4f(1.0f, 0.5f, 0.2f, 1.0f);
827 glRectf(-100,-100,100,100);
829 // now the stencil contains 0 for scenery in light and != 0 for parts in shadow
830 // draw a quad covering the screen, the stencil will be the mask
831 // we darken the shadowed parts
832 glColorMask(1, 1, 1, 1);
833 glStencilFunc(GL_NOTEQUAL, 0, ~0);
834 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
835 glEnable(GL_STENCIL_TEST);
837 glAlphaFunc(GL_GREATER, 0.0f);
839 glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
840 glColor4f(0.0, 0.0, 0.0, sgLerp(0.1, 0.3, dot_light) );
841 // fixed value is better, the previous line is surely wrong
842 glColor4f(0.0, 0.0, 0.0, 0.3 );
843 glRectf(-100,-100,100,100);
845 glMatrixMode ( GL_PROJECTION ) ;
847 glMatrixMode ( GL_MODELVIEW ) ;
850 glDisable(GL_STENCIL_TEST);
851 glPopClientAttrib ( ) ;
855 SGShadowVolume::SGShadowVolume( ssgBranch *root ) :
856 shadows_enabled( false ),
858 lastTraverseTreeFrame ( 0 ),
860 shadows_rendered( false )
865 SGShadowVolume::~SGShadowVolume() {
866 SceneryObject_map::iterator iSceneryObject;
867 for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); iSceneryObject++ ) {
868 delete iSceneryObject->second;
870 sceneryObjects.clear();
873 void SGShadowVolume::init(SGPropertyNode *sim_rendering_options) {
874 shadows_enabled = true;
875 sim_rendering = sim_rendering_options;
876 GLint stencilBits = 0, alphaBits = 0;
877 glGetIntegerv( GL_STENCIL_BITS, &stencilBits );
878 glGetIntegerv( GL_ALPHA_BITS, &alphaBits );
879 bool hasSubtractiveBlend = SGIsOpenGLExtensionSupported("GL_EXT_blend_subtract");
880 bool hasMinMaxBlend = SGIsOpenGLExtensionSupported("GL_EXT_blend_minmax");
881 if( hasSubtractiveBlend )
882 glBlendEquationPtr = (glBlendEquationProc ) SGLookupFunction("glBlendEquationEXT");
883 canDoAlpha = (alphaBits >= 8) && hasSubtractiveBlend && hasMinMaxBlend;
884 canDoStencil = (stencilBits >= 3);
887 SG_LOG(SG_ALL, SG_WARN, "SGShadowVolume:no stencil buffer, using alpha buffer");
889 SG_LOG(SG_ALL, SG_WARN, "SGShadowVolume:no stencil buffer and no alpha buffer");
892 void SGShadowVolume::startOfFrame(void) {
894 void SGShadowVolume::deleteOccluderFromTile(ssgBranch *tile) {
895 SceneryObject_map::iterator iSceneryObject;
896 for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); ) {
897 SceneryObject_map::iterator iCurrent = iSceneryObject ++;
898 if( iCurrent->second->tile == tile ) {
899 delete iCurrent->second;
900 sceneryObjects.erase( iCurrent );
905 void SGShadowVolume::deleteOccluder(ssgBranch *occluder) {
906 // skip first node and go to first transform (placement)
907 while( occluder && !occluder->isA(ssgTypeTransform()))
908 occluder = (ssgBranch *) occluder->getKid(0);
910 // check if we allready know this object
911 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
912 if( iSceneryObject != sceneryObjects.end() ) {
913 delete iSceneryObject->second;
914 sceneryObjects.erase( occluder );
918 void SGShadowVolume::addOccluder(ssgBranch *occluder, OccluderType occluder_type, ssgBranch *tile) {
919 // skip first node and go to first transform (placement)
920 while( occluder && !occluder->isA(ssgTypeTransform()))
921 occluder = (ssgBranch *) occluder->getKid(0);
923 // check if we allready know this object
924 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
925 if( iSceneryObject == sceneryObjects.end() ) {
926 // printf("adding model %x %x\n", occluder, tile);
927 SceneryObject *entry = new SceneryObject( occluder, occluder_type );
929 sceneryObjects[ occluder ] = entry;
934 void SGShadowVolume::setupShadows( double lon, double lat,
935 double gst, double SunRightAscension, double SunDeclination, double sunAngle) {
937 shadowsAC_enabled = sim_rendering->getBoolValue("shadows-ac", false);
938 shadowsAC_transp_enabled = sim_rendering->getBoolValue("shadows-ac-transp", false);
939 shadowsAI_enabled = sim_rendering->getBoolValue("shadows-ai", false);
940 shadowsTO_enabled = sim_rendering->getBoolValue("shadows-to", false);
941 shadowsDebug_enabled = sim_rendering->getBoolValue("shadows-debug", false);
942 shadows_enabled = shadowsAC_enabled || shadowsAI_enabled || shadowsTO_enabled;
943 shadows_enabled &= canDoAlpha || canDoStencil;
944 use_alpha = ((!canDoStencil) || sim_rendering->getBoolValue("shadows-alpha", false)) &&
947 if( ! shadows_enabled )
950 shadows_rendered = false;
951 sun_angle = sunAngle;
957 sgSetVec3( axis, 0.0, 0.0, -1.0 );
958 sgMakeRotMat4( GST, (gst) * 15.0, axis );
960 sgSetVec3( axis, 0.0, 0.0, 1.0 );
961 sgMakeRotMat4( RA, (SunRightAscension * SGD_RADIANS_TO_DEGREES) - 90.0, axis );
963 sgSetVec3( axis, 1.0, 0.0, 0.0 );
964 sgMakeRotMat4( DEC, SunDeclination * SGD_RADIANS_TO_DEGREES, axis );
967 sgMakeIdentMat4( TRANSFORM );
968 sgPreMultMat4( TRANSFORM, GST );
969 sgPreMultMat4( TRANSFORM, RA );
970 sgPreMultMat4( TRANSFORM, DEC );
971 sgSetVec3( sunPos, 0.0, 9900000.0, 0.0);
972 sgXformPnt3( sunPos, TRANSFORM );
975 ssgGetModelviewMatrix( CameraViewM );
979 void SGShadowVolume::endOfFrame(void) {
980 if( ! shadows_enabled )
982 if( shadows_rendered )
984 glBindTexture(GL_TEXTURE_2D, 0);
985 glBindTexture(GL_TEXTURE_1D, 0);
987 glMatrixMode(GL_MODELVIEW);
990 shadows_rendered = true;
993 int SGShadowVolume::ACpostTravCB( ssgEntity *entity, int traversal_mask ) {
994 if( states->shadowsAC_transp_enabled && (SSGTRAV_CULL & traversal_mask) )
995 states->endOfFrame();