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>
29 #include <simgear/props/props.hxx>
30 #include <simgear/debug/logstream.hxx>
31 #include <simgear/screen/extensions.hxx>
32 #include <simgear/scene/model/animation.hxx>
33 #include <simgear/scene/model/model.hxx>
34 #include <simgear/environment/visual_enviro.hxx>
37 #include "shadowvolume.hxx"
40 geometry and edge list
41 - traverse object graph until leaf to get geometry
42 - what about transform and selection ?
43 - use sub objects rather then objects
44 - get local transform and selection
45 - range anim : ssgRangeSelector ( min, max ) => ssgSelector ( true/false )
46 => selection [0..n], kids [0..n]
47 - select/timed anim : ssgSelector ( true/false )
49 - spin/rotate/trans/scale/... : ssgTransform ( matrix )
52 - for one object branch
55 - save address in cache
56 - when rendering object
58 - getNetTransform + object global rotation (ac) => transform for light position
59 - go up in tree and check isSelected( self )
60 - generate connectivity each time the geometry change
61 => using local light so connectivity never changes
62 - generate active edge list when :
64 - subpart moves (animation code)
66 => cache rotation matrix and generate edge list only if something change
67 => even if it changes, no need to do that every frame
69 - static objects have static edge list if light does not move
72 - render full scene as normal
73 - render occluder in stencil buffer with their shadow volumes
74 - apply stencil to framebuffer (darkens shadowed parts)
76 shadows using the alpha buffer
77 http://wwwvis.informatik.uni-stuttgart.de/~roettger/html/Pages/shadows.html
81 // - shadow for objects
83 // * tile objects (from .stg)
85 // - random objects => tie shadow geometry to lib objects and reuse them
86 // - zfail if camera inside shadow
87 // - queue geometry work if lot of objects
88 // * add a render property on/off (for aircraft, for scene objects, for ai)
89 // * add a render property in rendering dialog
90 // * filter : halo, light, shadow, disc, disk, flame, (exhaust), noshadow
92 static int statSilhouette=0;
93 static int statGeom=0;
96 static SGShadowVolume *states;
97 static glBlendEquationProc glBlendEquationPtr = NULL;
98 #define GL_MIN_EXT 0x8007
99 #define GL_MAX_EXT 0x8008
102 SGShadowVolume::ShadowCaster::ShadowCaster( int _num_tri, ssgBranch * _geometry_leaf ) :
103 geometry_leaf ( _geometry_leaf ),
104 scenery_object ( 0 ),
110 lastSilhouetteIndicesCount ( 0 )
112 int num_tri = _num_tri;
113 numTriangles = num_tri;
114 triangles = new triData[ num_tri ];
115 indices = new int[1 + num_tri * 3];
116 vertices = new sgVec4[1 + num_tri * 3];
117 silhouetteEdgeIndices = new GLushort[(1+num_tri) * 3*3];
118 indices [ num_tri * 3 ] = num_tri * 3;
119 sgSetVec3(last_lightpos, 0.0, 0.0, 0.0);
122 ssgBranch *branch = (ssgBranch *) _geometry_leaf;
123 while( branch && branch->getNumParents() > 0 ) {
124 if( branch->isAKindOf(ssgTypeSelector())) {
125 first_select = branch;
128 if( sgCheckAnimationBranch( (ssgEntity *) branch ) )
129 if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1) {
130 first_select = branch;
133 branch = branch->getParent(0);
137 void SGShadowVolume::ShadowCaster::addLeaf( int & tri_idx, int & ind_idx, ssgLeaf *geometry_leaf ) {
138 int num_tri = geometry_leaf->getNumTriangles();
139 for(int i = 0; i < num_tri ; i ++ ) {
142 geometry_leaf->getTriangle( i, &v1, &v2, &v3 );
143 sgCopyVec3(a, geometry_leaf->getVertex(v1));
144 sgCopyVec3(b, geometry_leaf->getVertex(v2));
145 sgCopyVec3(c, geometry_leaf->getVertex(v3));
148 sgMakePlane ( triangles[p].planeEquations, a, b, c );
149 sgCopyVec3(vertices[ind_idx + v1], a);
150 sgCopyVec3(vertices[ind_idx + v2], b);
151 sgCopyVec3(vertices[ind_idx + v3], c);
152 vertices[ind_idx + v1][SG_W] = 1.0f;
153 vertices[ind_idx + v2][SG_W] = 1.0f;
154 vertices[ind_idx + v3][SG_W] = 1.0f;
155 indices[p*3] = ind_idx + v1;
156 indices[p*3+1] = ind_idx + v2;
157 indices[p*3+2] = ind_idx + v3;
163 isTranslucent |= geometry_leaf->isTranslucent() ? true : false;
164 int num_ind = geometry_leaf->getNumVertices();
168 SGShadowVolume::ShadowCaster::~ShadowCaster() {
172 delete [] silhouetteEdgeIndices;
177 bool SGShadowVolume::ShadowCaster::sameVertex(int edge1, int edge2) {
181 sgSubVec3( delta_v, vertices[edge1], vertices[edge2]);
182 if( delta_v[SG_X] != 0.0) return false;
183 if( delta_v[SG_Y] != 0.0) return false;
184 if( delta_v[SG_Z] != 0.0) return false;
189 //Calculate neighbour faces for each edge
190 // caution, this is O(n2)
191 void SGShadowVolume::ShadowCaster::SetConnectivity(void)
195 //set the neighbour indices to be -1
196 for(int ii=0; ii<numTriangles; ++ii)
197 triangles[ii].neighbourIndices[0] =
198 triangles[ii].neighbourIndices[1] =
199 triangles[ii].neighbourIndices[2] = -1;
201 //loop through triangles
202 for(int i=0; i<numTriangles-1; ++i)
204 //loop through edges on the first triangle
205 for(int edgeI=0; edgeI<3; ++edgeI)
207 //continue if this edge already has a neighbour set
208 if(triangles[i].neighbourIndices[ edgeI ]!=-1)
211 //get the vertex indices on each edge
212 int edgeI1=indices[i*3+edgeI];
213 int edgeI2=indices[i*3+(edgeI == 2 ? 0 : edgeI+1)];
215 //loop through triangles with greater indices than this one
216 for(int j=i+1; j<numTriangles; ++j)
218 //loop through edges on triangle j
219 for(int edgeJ=0; edgeJ<3; ++edgeJ)
221 //continue if this edge already has a neighbour set
222 if(triangles[j].neighbourIndices[ edgeJ ]!=-1) {
225 //get the vertex indices on each edge
226 int edgeJ1=indices[j*3+edgeJ];
227 int edgeJ2=indices[j*3+(edgeJ == 2 ? 0 : edgeJ+1)];
229 //if these are the same (possibly reversed order), these faces are neighbours
231 //no, we only use reverse order because same order means that
232 //the triangle is wrongly oriented and that will cause shadow artifact
233 if( sameVertex(edgeI1, edgeJ1) && sameVertex(edgeI2, edgeJ2) ) {
234 // can happens with 'bad' geometry
235 // printf("flipped triangle detected...check your model\n");
239 if( sameVertex(edgeI1, edgeJ2) && sameVertex(edgeI2, edgeJ1) )
241 int edgeI3=indices[i*3+(edgeI == 0 ? 2 : edgeI-1)];
242 int edgeJ3=indices[j*3+(edgeJ == 0 ? 2 : edgeJ-1)];
243 if( sameVertex(edgeI3, edgeJ3) ) {
244 // can happens with 'bad' geometry
245 // printf("duplicated tri...check your model\n");
249 triangles[i].neighbourIndices[edgeI]=j;
250 triangles[j].neighbourIndices[edgeJ]=i;
260 // printf("submodel has %d triangles and %d shared edges\n", numTriangles, edgeCount);
263 //calculate silhouette edges
264 void SGShadowVolume::ShadowCaster::CalculateSilhouetteEdges(sgVec3 lightPosition)
266 //Calculate which faces face the light
267 for(int i=0; i<numTriangles; ++i)
269 if( sgDistToPlaneVec3 ( triangles[i].planeEquations, lightPosition ) > 0.0 )
270 triangles[i].isFacingLight=true;
272 triangles[i].isFacingLight=false;
276 int iEdgeIndices = 0;
277 sgVec4 farCap = {-lightPosition[SG_X], -lightPosition[SG_Y], -lightPosition[SG_Z], 1.0f};
278 sgCopyVec4( vertices[ numTriangles*3 ], farCap );
280 for(int t=0; t < numTriangles; t++) {
282 //if this face is not facing the light, not a silhouette edge
283 if(!triangles[t].isFacingLight)
285 triangles[t].isSilhouetteEdge[0]=false;
286 triangles[t].isSilhouetteEdge[1]=false;
287 triangles[t].isSilhouetteEdge[2]=false;
291 for(int j = 0 ; j < 3 ; j++) {
292 //this face is facing the light
293 //if the neighbouring face is not facing the light, or there is no neighbouring face,
294 //then this is a silhouette edge
295 if(triangles[t].neighbourIndices[j]==-1 ||
296 !triangles[triangles[t].neighbourIndices[j]].isFacingLight ) {
297 triangles[t].isSilhouetteEdge[j]=true;
298 silhouetteEdgeIndices[ iEdgeIndices++ ] = indices[v+(j == 2 ? 0 : j+1)];
299 silhouetteEdgeIndices[ iEdgeIndices++ ] = indices[v+j];
300 silhouetteEdgeIndices[ iEdgeIndices++ ] = numTriangles * 3;
303 triangles[t].isSilhouetteEdge[j]=false;
306 lastSilhouetteIndicesCount = iEdgeIndices;
309 void SGShadowVolume::ShadowCaster::DrawInfiniteShadowVolume(sgVec3 lightPosition, bool drawCaps)
311 glEnableClientState ( GL_VERTEX_ARRAY ) ;
312 glVertexPointer ( 4, GL_FLOAT, 0, vertices ) ;
313 glDrawElements ( GL_TRIANGLES, lastSilhouetteIndicesCount, GL_UNSIGNED_SHORT, silhouetteEdgeIndices ) ;
315 //Draw caps if required
318 glBegin(GL_TRIANGLES);
320 for(int i=0; i<numTriangles; ++i)
322 if(triangles[i].isFacingLight) {
324 glVertex3fv( vertices[indices[v+0]] );
325 glVertex3fv( vertices[indices[v+1]] );
326 glVertex3fv( vertices[indices[v+2]] );
335 void SGShadowVolume::ShadowCaster::getNetTransform ( ssgBranch * branch, sgMat4 xform )
337 // one less matmult...
339 while( branch && branch != lib_object ) {
340 if( branch->isA(ssgTypeTransform()) ) {
343 ((ssgTransform *) branch)->getTransform( xform );
346 ((ssgTransform *) branch)->getTransform(transform);
347 sgPostMultMat4 ( xform, transform ) ;
350 branch = branch->getParent( 0 );
353 sgMakeIdentMat4 ( xform ) ;
356 // check the value of <select> and <range> animation
357 // wich have been computed during the rendering
358 bool SGShadowVolume::ShadowCaster::isSelected ( ssgBranch * branch, float dist ) {
359 while( branch && branch != lib_object) {
360 if( sgCheckAnimationBranch( (ssgEntity *) branch ) ) {
361 if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1)
362 if( ((SGShadowAnimation *) branch->getUserData())->get_condition_value() )
365 // recompute range check since the value in the branch is shared by multiple objects
366 // we can only have the last value
367 if( branch->isA(ssgTypeRangeSelector()) )
368 if( dist >= ((ssgRangeSelector *) branch)->getRange(1) ||
369 dist < ((ssgRangeSelector *) branch)->getRange(0))
371 if( branch->isA(ssgTypeSelector()) )
372 if( !((ssgSelector *) branch)->isSelected(0) )
374 branch = branch->getParent(0);
379 trans ----- personality ---- shared object
382 *--------* shared object
386 void SGShadowVolume::ShadowCaster::computeShadows(sgMat4 rotation, sgMat4 rotation_translation,
387 OccluderType occluder_type) {
389 // check the select and range ssgSelector node
390 // object can't cast shadow if it is not visible
392 sgCopyVec4( trans, rotation_translation[3] );
393 sgAddVec4( trans, states->CameraViewM[3] );
394 float dist = sgLengthVec3( trans );
396 if( first_select && ! isSelected( first_select, dist) )
399 // get the transformations : this comes from animation code for example
403 int deltaFrame = occluder_type == SGShadowVolume::occluderTypeAircraft ? 0 : 9;
404 if( states->frameNumber - frameNumber > deltaFrame) {
407 getNetTransform( (ssgBranch *) geometry_leaf, transform );
408 sgCopyMat4( last_transform, transform );
409 sgPostMultMat4( transform, rotation );
410 sgTransposeNegateMat4 ( invTransform, transform );
412 sgCopyVec3( lightPos, states->sunPos );
413 sgXformPnt3( lightPos, invTransform );
416 sgNormaliseVec3( lightPosNorm, lightPos );
417 float deltaPos = sgAbs(lightPosNorm[0] - last_lightpos[0]) +
418 sgAbs(lightPosNorm[1] - last_lightpos[1]) +
419 sgAbs(lightPosNorm[2] - last_lightpos[2]);
420 // if the geometry has rotated/moved enought then
421 // we need to recompute the silhouette
422 // but this computation does not need to be done each frame
423 if( deltaPos > 0.0 ) {
424 CalculateSilhouetteEdges( lightPos );
425 sgCopyVec3( last_lightpos, lightPosNorm );
426 frameNumber = states->frameNumber ;
430 sgCopyMat4( transf, last_transform );
431 sgPostMultMat4( transf, rotation_translation );
432 glLoadMatrixf ( (float *) states->CameraViewM ) ;
433 glMultMatrixf( (float *) transf );
435 if( states->shadowsDebug_enabled )
437 glStencilFunc(GL_ALWAYS, 0, ~0);
438 glDisable( GL_CULL_FACE );
439 glDisable( GL_DEPTH_TEST );
440 glDisable(GL_STENCIL_TEST);
441 glColorMask(1, 1, 1, 1);
442 glColor4f(0.0, 0.0, 1.0, 1.0);
444 for(int i=0; i<numTriangles; ++i)
446 if(!triangles[i].isFacingLight)
449 //Loop through edges on this face
450 sgVec3 vertex1, vertex2, vertex3;
451 sgCopyVec3(vertex1, vertices[indices[v+0]]);
452 sgCopyVec3(vertex2, vertices[indices[v+1]]);
453 sgCopyVec3(vertex3, vertices[indices[v+2]]);
455 if(triangles[i].isSilhouetteEdge[0]) {
456 glVertex3fv(vertex2);
457 glVertex3fv(vertex1);
459 if(triangles[i].isSilhouetteEdge[1]) {
460 glVertex3fv(vertex2);
461 glVertex3fv(vertex3);
463 if(triangles[i].isSilhouetteEdge[2]) {
464 glVertex3fv(vertex3);
465 glVertex3fv(vertex1);
469 glColorMask(0, 0, 0, 0);
470 glEnable( GL_CULL_FACE );
471 glEnable( GL_DEPTH_TEST );
472 glEnable(GL_STENCIL_TEST);
476 // TODO:compute intersection with near clip plane...
477 bool needZFail=false;
479 // GL_INCR_WRAP_EXT, GL_DECR_WRAP_EXT
483 //Increment stencil buffer for back face depth fail
484 glStencilFunc(GL_ALWAYS, 0, ~0);
485 glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
486 glCullFace(GL_FRONT);
488 DrawInfiniteShadowVolume( lightPos, true);
490 //Decrement stencil buffer for front face depth fail
491 glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
494 DrawInfiniteShadowVolume( lightPos, true);
498 //Increment stencil buffer for front face depth pass
499 if( states->use_alpha ) {
500 glBlendEquationPtr( GL_FUNC_ADD );
501 glBlendFunc( GL_ONE, GL_ONE );
502 glColor4ub(1, 1, 1, 16);
504 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
505 glStencilFunc(GL_ALWAYS, 0, ~0);
506 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
510 DrawInfiniteShadowVolume( lightPos, states->shadowsAC_transp_enabled & isTranslucent);
512 //Decrement stencil buffer for back face depth pass
513 if( states->use_alpha ) {
514 glBlendEquationPtr( GL_FUNC_REVERSE_SUBTRACT );
515 glBlendFunc( GL_ONE, GL_ONE );
516 glColor4ub(1, 1, 1, 16);
518 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
520 glCullFace(GL_FRONT);
522 DrawInfiniteShadowVolume( lightPos, states->shadowsAC_transp_enabled & isTranslucent);
527 void SGShadowVolume::SceneryObject::computeShadows(void) {
529 // compute intersection with view frustum
530 // use pending_object (pointer on obj transform) & tile transform
532 if( !scenery_object ) {
533 if( states->frameNumber - states->lastTraverseTreeFrame > 5 ) {
536 traverseTree( pending_object );
537 states->lastTraverseTreeFrame = states->frameNumber;
541 sgMat4 rotation, rotation_translation;
542 scenery_object->getNetTransform ( rotation_translation );
543 // split placement offset into rotation and offset
544 // rotation from model inside world
545 // we are not interested in translation since the light is very far away
546 // without translation we reduce the frequency of updates of the silouhette
547 sgCopyMat4( rotation, rotation_translation );
548 sgSetVec4( rotation[3], 0, 0, 0, 1);
550 ShadowCaster_list::iterator iShadowCaster;
551 for(iShadowCaster = parts.begin() ; iShadowCaster != parts.end() ; iShadowCaster ++ ) {
552 (*iShadowCaster)->computeShadows(rotation, rotation_translation, occluder_type);
556 static ssgCullResult cull_test ( ssgEntity *e, sgFrustum *f, sgMat4 m, int test_needed )
561 sgSphere tmp = *(e->getBSphere()) ;
563 if ( tmp.isEmpty () )
566 tmp . orthoXform ( m ) ;
567 if( tmp.center[2] == 0.0 )
569 // cull if too small on screen
570 if ( tmp.radius / sgAbs(tmp.center[2]) < 1.0 / 40.0 )
573 return (ssgCullResult) f -> contains ( &tmp ) ;
577 void SGShadowVolume::cull ( ssgBranch *b, sgFrustum *f, sgMat4 m, int test_needed )
579 int cull_result = cull_test ( (ssgEntity *) b, f, m, test_needed ) ;
581 if ( cull_result == SSG_OUTSIDE )
583 if( b->isA( ssgTypeTransform() ) ) {
585 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( b );
586 if( iSceneryObject != sceneryObjects.end() ) {
587 SceneryObject *an_occluder = iSceneryObject->second;
588 if( shadowsTO_enabled && (an_occluder->occluder_type == occluderTypeTileObject) ||
589 shadowsAI_enabled && (an_occluder->occluder_type == occluderTypeAI ) ||
590 shadowsAC_enabled && (an_occluder->occluder_type == occluderTypeAircraft ) )
591 an_occluder->computeShadows();
595 sgMat4 tmp, transform ;
596 sgCopyMat4 ( tmp, m ) ;
597 ((ssgTransform *)b)->getTransform( transform );
598 sgPreMultMat4 ( tmp, transform ) ;
600 glLoadMatrixf ( (float *) tmp ) ;
601 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid() )
602 cull ( (ssgBranch *) e, f, tmp, cull_result != SSG_INSIDE ) ;
604 } else if( b->isAKindOf( ssgTypeSelector() ) ) {
605 int s = ((ssgSelector *) b)->getSelect() ;
606 if( b->isA( ssgTypeRangeSelector() ) ) {
607 float range = sgLengthVec3 ( m [ 3 ] ) ;
608 s = (range < ((ssgRangeSelector *) b)->getRange(1) &&
609 range >= ((ssgRangeSelector *) b)->getRange(0) ) ? 1 : 0;
611 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid(), s >>= 1 )
613 cull ( (ssgBranch *) e, f, m, cull_result != SSG_INSIDE ) ;
614 } else if( b->isAKindOf( ssgTypeBranch() ) ) {
615 char *name = b->getName();
616 // quick exit for the hundreds of ground leafs
617 if( name && !strcmp(name, "LocalTerrain") )
619 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid() )
620 if( ! e->isAKindOf( ssgTypeLeaf() ) )
621 cull ( (ssgBranch *) e, f, m, cull_result != SSG_INSIDE ) ;
626 1) starting at the root of a scene object we follow all branch to find vertex leaf.
627 each vertex leaf will create a shadow sub object so that the shadow casted by this
628 geometry can be correctly rendered according to select or transform nodes.
629 2) models are not optimized for shadows because the geometry is cut by
630 - select / transform nodes
631 - material differences
632 since we are not interested in the geometry material we try to merge same level
633 leafs. this can divide the number of shadow sub models by a lot (reducing the cpu
634 overhead for matrix computation) and at the end this will reduce the number of
635 silouhette edge by a lot too.
637 static bool filterLeaf(ssgLeaf *this_kid) {
638 const char *leaf_name = this_kid->getName();
639 /* ssgSimpleState *sst = (ssgSimpleState *) this_kid->getState();
640 if( sst && sst->isTranslucent() )
647 for( buff = lname; *leaf_name && l < (sizeof( lname )-1); ++buff, l++ )
648 *buff = tolower(*leaf_name++);
650 if( !strncmp(lname, "noshadow", 8) )
654 void SGShadowVolume::SceneryObject::traverseTree(ssgBranch *branch) {
658 if( sgCheckAnimationBranch( (ssgEntity *) branch ) ) {
659 if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1)
660 if( ((SGShadowAnimation *) branch->getUserData())->get_condition_value() )
664 for(int i = 0 ; i < branch->getNumKids() ; i++) {
665 ssgEntity *this_kid = branch->getKid( i );
666 if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
667 if( filterLeaf( (ssgLeaf *) this_kid ) ) {
668 num_tri += ((ssgLeaf *) this_kid)->getNumTriangles();
672 traverseTree( (ssgBranch *) this_kid );
677 ShadowCaster *new_part = new ShadowCaster( num_tri, branch);
678 new_part->scenery_object = scenery_object;
679 new_part->lib_object = lib_object;
680 new_part->isTranslucent = false;
681 for(int i = 0 ; i < branch->getNumKids() ; i++) {
682 ssgEntity *this_kid = branch->getKid( i );
683 if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
684 if( filterLeaf( (ssgLeaf *) this_kid ) )
685 new_part->addLeaf( tri_idx, ind_idx, (ssgLeaf *) this_kid );
688 // only do that for aircraft
689 if( occluder_type != SGShadowVolume::occluderTypeAircraft )
690 new_part->isTranslucent = false;
691 new_part->SetConnectivity();
692 parts.push_back( new_part );
696 void SGShadowVolume::SceneryObject::find_trans(void) {
697 ssgBranch *branch = pending_object;
698 // check the existence of the root node
699 while( branch && branch->getNumParents() > 0 ) {
700 branch = branch->getParent(0);
702 // check if we are connected to the scene graph
703 if( !branch->isA(ssgTypeRoot() ) )
705 scenery_object = pending_object;
708 SGShadowVolume::SceneryObject::SceneryObject(ssgBranch *_scenery_object, OccluderType _occluder_type) :
709 scenery_object ( 0 ),
710 pending_object ( _scenery_object ),
711 occluder_type ( _occluder_type )
713 // queue objects, don't do all the work in the first frames because of
714 // massive cpu power needed
716 if( occluder_type == SGShadowVolume::occluderTypeAircraft )
717 lib_object = _scenery_object;
719 lib_object = (ssgBranch *) ((ssgBranch *)_scenery_object->getKid(0))->getKid(0);
722 SGShadowVolume::SceneryObject::~SceneryObject()
724 ShadowCaster_list::iterator iParts;
725 for(iParts = parts.begin() ; iParts != parts.end(); iParts++ ) {
731 void SGShadowVolume::computeShadows(void) {
733 // intensity = ambient + lights
734 // lights = sun_const * (sun_angle . normal)
735 double dot_light = cos(sun_angle);
736 // do nothing if sun is under horizon
737 if( dot_light < 0.2 )
740 //Draw shadow volumes
741 glPushAttrib(GL_ALL_ATTRIB_BITS);
742 glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ;
743 glDisableClientState ( GL_COLOR_ARRAY ) ;
744 glDisableClientState ( GL_NORMAL_ARRAY ) ;
745 glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
748 glColorMask(0, 0, 0, 1);
749 glClearColor(0.0, 0.0, 0.0, 0.0 );
750 glClear(GL_COLOR_BUFFER_BIT);
755 glClear(GL_STENCIL_BUFFER_BIT);
756 glColorMask(0, 0, 0, 0);
757 glEnable(GL_STENCIL_TEST);
761 glDisable( GL_LIGHTING );
763 glEnable( GL_CULL_FACE );
764 // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
765 glPolygonOffset(0.0,2.0);
766 // glPolygonOffset(0.0,30.0);
767 glEnable(GL_POLYGON_OFFSET_FILL);
769 glShadeModel(GL_FLAT);
771 glDepthMask( false );
772 glEnable( GL_DEPTH_TEST );
773 glDepthFunc(GL_LESS);
778 sgEnviro.getFOV( w, h );
779 frustum.setFOV( w, h );
780 frustum.setNearFar(0.1f, 5000.0f);
782 ssgGetModelviewMatrix( m );
783 cull( ssg_root, &frustum, m, true);
787 glMatrixMode ( GL_PROJECTION ) ;
790 glOrtho ( -100, 100, -100, 100, -1, 1 ) ;
791 glMatrixMode ( GL_MODELVIEW ) ;
795 glDisable(GL_DEPTH_TEST);
796 glDisable(GL_CULL_FACE);
797 // glBindTexture(GL_TEXTURE_2D, 0);
798 glPolygonMode(GL_FRONT, GL_FILL);
800 // there is a flaw in the Roettger paper, this does not work for some geometry
801 // where we increment (multiply) the buffer a few times then we
802 // decrement (divide) a few times. Decrementing more then once will give a
803 // non shadowed parts with mask value < 0.25 because the incrementation was
805 // Solution : don't use a start value as high as 0.25 but the smallest value
806 // posible ie 1/256. This is not a general solution, a stencil buffer will
807 // support 255 incrementation before clamping, the alpha mask only 7.
808 // => that still does not work with our geometry so use subtractive blend
810 // clamp mask = {0,16,32,64,...} => {0,16,16,16,...}
811 glBlendEquationPtr( GL_MIN_EXT );
812 glBlendFunc( GL_DST_COLOR, GL_ONE );
813 glColor4ub(1, 1, 1, 16);
814 glRectf(-100,-100,100,100);
815 // scale mask = {0,16} => {0,64}
816 glBlendEquationPtr( GL_FUNC_ADD );
817 glBlendFunc( GL_DST_COLOR, GL_ONE );
818 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
819 glRectf(-100,-100,100,100);
820 glRectf(-100,-100,100,100);
821 // negate mask => {0,64} => {255, 191}
822 glBlendFunc( GL_ONE_MINUS_DST_COLOR, GL_ZERO );
823 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
824 glRectf(-100,-100,100,100);
826 glColorMask(1, 1, 1, 1);
827 glBlendFunc( GL_ZERO, GL_DST_ALPHA );
828 glColor4f(1.0f, 0.5f, 0.2f, 1.0f);
829 glRectf(-100,-100,100,100);
831 // now the stencil contains 0 for scenery in light and != 0 for parts in shadow
832 // draw a quad covering the screen, the stencil will be the mask
833 // we darken the shadowed parts
834 glColorMask(1, 1, 1, 1);
835 glStencilFunc(GL_NOTEQUAL, 0, ~0);
836 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
837 glEnable(GL_STENCIL_TEST);
839 glAlphaFunc(GL_GREATER, 0.0f);
841 glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
842 glColor4f(0.0, 0.0, 0.0, sgLerp(0.1, 0.3, dot_light) );
843 // fixed value is better, the previous line is surely wrong
844 glColor4f(0.0, 0.0, 0.0, 0.3 );
845 glRectf(-100,-100,100,100);
847 glMatrixMode ( GL_PROJECTION ) ;
849 glMatrixMode ( GL_MODELVIEW ) ;
852 glDisable(GL_STENCIL_TEST);
853 glPopClientAttrib ( ) ;
857 SGShadowVolume::SGShadowVolume( ssgBranch *root ) :
858 shadows_enabled( false ),
860 lastTraverseTreeFrame ( 0 ),
862 shadows_rendered( false )
867 SGShadowVolume::~SGShadowVolume() {
868 SceneryObject_map::iterator iSceneryObject;
869 for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); iSceneryObject++ ) {
870 delete iSceneryObject->second;
872 sceneryObjects.clear();
875 void SGShadowVolume::init(SGPropertyNode *sim_rendering_options) {
876 shadows_enabled = true;
877 sim_rendering = sim_rendering_options;
878 GLint stencilBits = 0, alphaBits = 0;
879 glGetIntegerv( GL_STENCIL_BITS, &stencilBits );
880 glGetIntegerv( GL_ALPHA_BITS, &alphaBits );
881 bool hasSubtractiveBlend = SGIsOpenGLExtensionSupported("GL_EXT_blend_subtract");
882 bool hasMinMaxBlend = SGIsOpenGLExtensionSupported("GL_EXT_blend_minmax");
883 if( hasSubtractiveBlend )
884 glBlendEquationPtr = (glBlendEquationProc ) SGLookupFunction("glBlendEquationEXT");
885 canDoAlpha = (alphaBits >= 8) && hasSubtractiveBlend && hasMinMaxBlend;
886 canDoStencil = (stencilBits >= 3);
889 SG_LOG(SG_ALL, SG_WARN, "SGShadowVolume:no stencil buffer, using alpha buffer");
891 SG_LOG(SG_ALL, SG_WARN, "SGShadowVolume:no stencil buffer and no alpha buffer");
894 void SGShadowVolume::startOfFrame(void) {
896 void SGShadowVolume::deleteOccluderFromTile(ssgBranch *tile) {
897 SceneryObject_map::iterator iSceneryObject;
898 for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); ) {
899 SceneryObject_map::iterator iCurrent = iSceneryObject ++;
900 if( iCurrent->second->tile == tile ) {
901 delete iCurrent->second;
902 sceneryObjects.erase( iCurrent );
907 void SGShadowVolume::deleteOccluder(ssgBranch *occluder) {
908 // skip first node and go to first transform (placement)
909 while( occluder && !occluder->isA(ssgTypeTransform()))
910 occluder = (ssgBranch *) occluder->getKid(0);
912 // check if we allready know this object
913 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
914 if( iSceneryObject != sceneryObjects.end() ) {
915 delete iSceneryObject->second;
916 sceneryObjects.erase( occluder );
920 void SGShadowVolume::addOccluder(ssgBranch *occluder, OccluderType occluder_type, ssgBranch *tile) {
921 // skip first node and go to first transform (placement)
922 while( occluder && !occluder->isA(ssgTypeTransform()))
923 occluder = (ssgBranch *) occluder->getKid(0);
925 // check if we allready know this object
926 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
927 if( iSceneryObject == sceneryObjects.end() ) {
928 // printf("adding model %x %x\n", occluder, tile);
929 SceneryObject *entry = new SceneryObject( occluder, occluder_type );
931 sceneryObjects[ occluder ] = entry;
936 void SGShadowVolume::setupShadows( double lon, double lat,
937 double gst, double SunRightAscension, double SunDeclination, double sunAngle) {
939 shadowsAC_enabled = sim_rendering->getBoolValue("shadows-ac", false);
940 shadowsAC_transp_enabled = sim_rendering->getBoolValue("shadows-ac-transp", false);
941 shadowsAI_enabled = sim_rendering->getBoolValue("shadows-ai", false);
942 shadowsTO_enabled = sim_rendering->getBoolValue("shadows-to", false);
943 shadowsDebug_enabled = sim_rendering->getBoolValue("shadows-debug", false);
944 shadows_enabled = shadowsAC_enabled || shadowsAI_enabled || shadowsTO_enabled;
945 shadows_enabled &= canDoAlpha || canDoStencil;
946 use_alpha = ((!canDoStencil) || sim_rendering->getBoolValue("shadows-alpha", false)) &&
949 if( ! shadows_enabled )
952 shadows_rendered = false;
953 sun_angle = sunAngle;
959 sgSetVec3( axis, 0.0, 0.0, -1.0 );
960 sgMakeRotMat4( GST, (gst) * 15.0, axis );
962 sgSetVec3( axis, 0.0, 0.0, 1.0 );
963 sgMakeRotMat4( RA, (SunRightAscension * SGD_RADIANS_TO_DEGREES) - 90.0, axis );
965 sgSetVec3( axis, 1.0, 0.0, 0.0 );
966 sgMakeRotMat4( DEC, SunDeclination * SGD_RADIANS_TO_DEGREES, axis );
969 sgMakeIdentMat4( TRANSFORM );
970 sgPreMultMat4( TRANSFORM, GST );
971 sgPreMultMat4( TRANSFORM, RA );
972 sgPreMultMat4( TRANSFORM, DEC );
973 sgSetVec3( sunPos, 0.0, 9900000.0, 0.0);
974 sgXformPnt3( sunPos, TRANSFORM );
977 ssgGetModelviewMatrix( CameraViewM );
981 void SGShadowVolume::endOfFrame(void) {
982 if( ! shadows_enabled )
984 if( shadows_rendered )
986 glBindTexture(GL_TEXTURE_2D, 0);
987 glBindTexture(GL_TEXTURE_1D, 0);
989 glMatrixMode(GL_MODELVIEW);
992 shadows_rendered = true;
995 int SGShadowVolume::ACpostTravCB( ssgEntity *entity, int traversal_mask ) {
996 if( states->shadowsAC_transp_enabled && (SSGTRAV_CULL & traversal_mask) )
997 states->endOfFrame();