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, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
25 #include <simgear/props/props.hxx>
26 #include <simgear/debug/logstream.hxx>
27 #include <simgear/screen/extensions.hxx>
28 #include <simgear/scene/model/animation.hxx>
29 #include <simgear/scene/model/model.hxx>
32 #include "shadowvolume.hxx"
35 geometry and edge list
36 - traverse object graph until leaf to get geometry
37 - what about transform and selection ?
38 - use sub objects rather then objects
39 - get local transform and selection
40 - range anim : ssgRangeSelector ( min, max ) => ssgSelector ( true/false )
41 => selection [0..n], kids [0..n]
42 - select/timed anim : ssgSelector ( true/false )
44 - spin/rotate/trans/scale/... : ssgTransform ( matrix )
47 - for one object branch
50 - save address in cache
51 - when rendering object
53 - getNetTransform + object global rotation (ac) => transform for light position
54 - go up in tree and check isSelected( self )
55 - generate connectivity each time the geometry change
56 => using local light so connectivity never changes
57 - generate active edge list when :
59 - subpart moves (animation code)
61 => cache rotation matrix and generate edge list only if something change
62 => even if it changes, no need to do that every frame
64 - static objects have static edge list if light does not move
67 - render full scene as normal
68 - render occluder in stencil buffer with their shadow volumes
69 - apply stencil to framebuffer (darkens shadowed parts)
71 shadows using the alpha buffer
72 http://wwwvis.informatik.uni-stuttgart.de/~roettger/html/Pages/shadows.html
76 // - shadow for objects
78 // * tile objects (from .stg)
80 // - random objects => tie shadow geometry to lib objects and reuse them
81 // - zfail if camera inside shadow
82 // - queue geometry work if lot of objects
83 // * add a render property on/off (for aircraft, for scene objects, for ai)
84 // * add a render property in rendering dialog
85 // * filter : halo, light, shadow, disc, disk, flame, (exhaust), noshadow
87 static int statSilhouette=0;
88 static int statGeom=0;
91 static SGShadowVolume *states;
92 static glBlendEquationProc glBlendEquation = NULL;
93 #define GL_MIN_EXT 0x8007
94 #define GL_MAX_EXT 0x8008
97 SGShadowVolume::ShadowCaster::ShadowCaster( int _num_tri, ssgBranch * _geometry_leaf ) :
98 geometry_leaf ( _geometry_leaf ),
105 lastSilhouetteIndicesCount ( 0 )
107 int num_tri = _num_tri;
108 numTriangles = num_tri;
109 triangles = new triData[ num_tri ];
110 indices = new int[1 + num_tri * 3];
111 vertices = new sgVec4[1 + num_tri * 3];
112 silhouetteEdgeIndices = new GLushort[(1+num_tri) * 3*3];
113 indices [ num_tri * 3 ] = num_tri * 3;
114 sgSetVec3(last_lightpos, 0.0, 0.0, 0.0);
117 ssgBranch *branch = (ssgBranch *) _geometry_leaf;
118 while( branch && branch->getNumParents() > 0 ) {
119 if( !first_select && branch->isA(ssgTypeSelector())) {
120 first_select = branch;
123 branch = branch->getParent(0);
127 void SGShadowVolume::ShadowCaster::addLeaf( int & tri_idx, int & ind_idx, ssgLeaf *geometry_leaf ) {
128 int num_tri = geometry_leaf->getNumTriangles();
129 for(int i = 0; i < num_tri ; i ++ ) {
132 geometry_leaf->getTriangle( i, &v1, &v2, &v3 );
133 sgCopyVec3(a, geometry_leaf->getVertex(v1));
134 sgCopyVec3(b, geometry_leaf->getVertex(v2));
135 sgCopyVec3(c, geometry_leaf->getVertex(v3));
138 sgMakePlane ( triangles[p].planeEquations, a, b, c );
139 sgCopyVec3(vertices[ind_idx + v1], a);
140 sgCopyVec3(vertices[ind_idx + v2], b);
141 sgCopyVec3(vertices[ind_idx + v3], c);
142 vertices[ind_idx + v1][SG_W] = 1.0f;
143 vertices[ind_idx + v2][SG_W] = 1.0f;
144 vertices[ind_idx + v3][SG_W] = 1.0f;
145 indices[p*3] = ind_idx + v1;
146 indices[p*3+1] = ind_idx + v2;
147 indices[p*3+2] = ind_idx + v3;
153 int num_ind = geometry_leaf->getNumVertices();
157 SGShadowVolume::ShadowCaster::~ShadowCaster() {
161 delete [] silhouetteEdgeIndices;
166 bool SGShadowVolume::ShadowCaster::sameVertex(int edge1, int edge2) {
170 sgSubVec3( delta_v, vertices[edge1], vertices[edge2]);
171 if( delta_v[SG_X] != 0.0) return false;
172 if( delta_v[SG_Y] != 0.0) return false;
173 if( delta_v[SG_Z] != 0.0) return false;
178 //Calculate neighbour faces for each edge
179 // caution, this is O(n2)
180 void SGShadowVolume::ShadowCaster::SetConnectivity(void)
184 //set the neighbour indices to be -1
185 for(int ii=0; ii<numTriangles; ++ii)
186 triangles[ii].neighbourIndices[0] =
187 triangles[ii].neighbourIndices[1] =
188 triangles[ii].neighbourIndices[2] = -1;
190 //loop through triangles
191 for(int i=0; i<numTriangles-1; ++i)
193 //loop through edges on the first triangle
194 for(int edgeI=0; edgeI<3; ++edgeI)
196 //continue if this edge already has a neighbour set
197 if(triangles[i].neighbourIndices[ edgeI ]!=-1)
200 //get the vertex indices on each edge
201 int edgeI1=indices[i*3+edgeI];
202 int edgeI2=indices[i*3+(edgeI == 2 ? 0 : edgeI+1)];
204 //loop through triangles with greater indices than this one
205 for(int j=i+1; j<numTriangles; ++j)
207 //loop through edges on triangle j
208 for(int edgeJ=0; edgeJ<3; ++edgeJ)
210 //continue if this edge already has a neighbour set
211 if(triangles[j].neighbourIndices[ edgeJ ]!=-1) {
214 //get the vertex indices on each edge
215 int edgeJ1=indices[j*3+edgeJ];
216 int edgeJ2=indices[j*3+(edgeJ == 2 ? 0 : edgeJ+1)];
218 //if these are the same (possibly reversed order), these faces are neighbours
220 //no, we only use reverse order because same order means that
221 //the triangle is wrongly oriented and that will cause shadow artifact
222 if( sameVertex(edgeI1, edgeJ1) && sameVertex(edgeI2, edgeJ2) ) {
223 // can happens with 'bad' geometry
224 // printf("flipped triangle detected...check your model\n");
228 if( sameVertex(edgeI1, edgeJ2) && sameVertex(edgeI2, edgeJ1) )
230 int edgeI3=indices[i*3+(edgeI == 0 ? 2 : edgeI-1)];
231 int edgeJ3=indices[j*3+(edgeJ == 0 ? 2 : edgeJ-1)];
232 if( sameVertex(edgeI3, edgeJ3) ) {
233 // can happens with 'bad' geometry
234 // printf("duplicated tri...check your model\n");
238 triangles[i].neighbourIndices[edgeI]=j;
239 triangles[j].neighbourIndices[edgeJ]=i;
249 // printf("submodel has %d triangles and %d shared edges\n", numTriangles, edgeCount);
252 //calculate silhouette edges
253 void SGShadowVolume::ShadowCaster::CalculateSilhouetteEdges(sgVec3 lightPosition)
255 //Calculate which faces face the light
256 for(int i=0; i<numTriangles; ++i)
258 if( sgDistToPlaneVec3 ( triangles[i].planeEquations, lightPosition ) > 0.0 )
259 triangles[i].isFacingLight=true;
261 triangles[i].isFacingLight=false;
265 int iEdgeIndices = 0;
266 sgVec4 farCap = {-lightPosition[SG_X], -lightPosition[SG_Y], -lightPosition[SG_Z], 1.0f};
267 sgCopyVec4( vertices[ numTriangles*3 ], farCap );
269 for(int t=0; t < numTriangles; t++) {
271 //if this face is not facing the light, not a silhouette edge
272 if(!triangles[t].isFacingLight)
274 triangles[t].isSilhouetteEdge[0]=false;
275 triangles[t].isSilhouetteEdge[1]=false;
276 triangles[t].isSilhouetteEdge[2]=false;
280 for(int j = 0 ; j < 3 ; j++) {
281 //this face is facing the light
282 //if the neighbouring face is not facing the light, or there is no neighbouring face,
283 //then this is a silhouette edge
284 if(triangles[t].neighbourIndices[j]==-1 ||
285 !triangles[triangles[t].neighbourIndices[j]].isFacingLight ) {
286 triangles[t].isSilhouetteEdge[j]=true;
287 silhouetteEdgeIndices[ iEdgeIndices++ ] = indices[v+(j == 2 ? 0 : j+1)];
288 silhouetteEdgeIndices[ iEdgeIndices++ ] = indices[v+j];
289 silhouetteEdgeIndices[ iEdgeIndices++ ] = numTriangles * 3;
292 triangles[t].isSilhouetteEdge[j]=false;
295 lastSilhouetteIndicesCount = iEdgeIndices;
298 void SGShadowVolume::ShadowCaster::DrawInfiniteShadowVolume(sgVec3 lightPosition, bool drawCaps)
300 glEnableClientState ( GL_VERTEX_ARRAY ) ;
301 glVertexPointer ( 4, GL_FLOAT, 0, vertices ) ;
302 glDrawElements ( GL_TRIANGLES, lastSilhouetteIndicesCount, GL_UNSIGNED_SHORT, silhouetteEdgeIndices ) ;
304 //Draw caps if required
307 glBegin(GL_TRIANGLES);
309 for(int i=0; i<numTriangles; ++i)
311 if(triangles[i].isFacingLight) {
313 glVertex3fv( vertices[indices[v+0]] );
314 glVertex3fv( vertices[indices[v+1]] );
315 glVertex3fv( vertices[indices[v+2]] );
324 void SGShadowVolume::ShadowCaster::getNetTransform ( ssgBranch * branch, sgMat4 xform )
326 // one less matmult...
328 while( branch && branch != lib_object ) {
329 if( branch->isA(ssgTypeTransform()) ) {
332 ((ssgTransform *) branch)->getTransform( xform );
335 ((ssgTransform *) branch)->getTransform(transform);
336 sgPostMultMat4 ( xform, transform ) ;
339 branch = branch->getParent( 0 );
342 sgMakeIdentMat4 ( xform ) ;
345 // check the value of <select> and <range> animation
346 // wich have been computed during the rendering
347 bool SGShadowVolume::ShadowCaster::isSelected ( ssgBranch * branch ) {
348 while( branch && branch != lib_object) {
349 if( branch->isA(ssgTypeSelector()) )
350 if( !((ssgSelector *) branch)->isSelected(0) )
352 branch = branch->getParent(0);
357 trans ----- personality ---- shared object
360 *--------* shared object
364 void SGShadowVolume::ShadowCaster::computeShadows(sgMat4 rotation, sgMat4 rotation_translation) {
366 // check the select and range ssgSelector node
367 // object can't cast shadow if it is not visible
369 if( first_select && ! isSelected( first_select) )
374 // get the transformations : this comes from animation code for example
376 getNetTransform( (ssgBranch *) geometry_leaf, transform );
378 sgCopyMat4( transf, transform );
379 sgPostMultMat4( transf, rotation_translation );
380 sgPostMultMat4( transform, rotation );
381 sgTransposeNegateMat4 ( invTransform, transform );
383 glLoadMatrixf ( (float *) states->CameraViewM ) ;
384 glMultMatrixf( (float *) transf );
387 sgCopyVec3( lightPos, states->sunPos );
388 sgXformPnt3( lightPos, invTransform );
391 sgNormaliseVec3( lightPosNorm, lightPos );
392 float deltaPos = sgAbs(lightPosNorm[0] - last_lightpos[0]) +
393 sgAbs(lightPosNorm[1] - last_lightpos[1]) +
394 sgAbs(lightPosNorm[2] - last_lightpos[2]);
395 // if the geometry has rotated/moved enought then
396 // we need to recompute the silhouette
397 // but this computation does not need to be done each frame
398 if( (deltaPos > 0.0) && ( states->frameNumber - frameNumber > 4)) {
399 CalculateSilhouetteEdges( lightPos );
400 sgCopyVec3( last_lightpos, lightPosNorm );
401 frameNumber = states->frameNumber ;
405 if( states->shadowsDebug_enabled )
407 glStencilFunc(GL_ALWAYS, 0, ~0);
408 glDisable( GL_CULL_FACE );
409 glDisable( GL_DEPTH_TEST );
410 glDisable(GL_STENCIL_TEST);
411 glColorMask(1, 1, 1, 1);
412 glColor4f(0.0, 0.0, 1.0, 1.0);
414 for(int i=0; i<numTriangles; ++i)
416 if(!triangles[i].isFacingLight)
419 //Loop through edges on this face
420 sgVec3 vertex1, vertex2, vertex3;
421 sgCopyVec3(vertex1, vertices[indices[v+0]]);
422 sgCopyVec3(vertex2, vertices[indices[v+1]]);
423 sgCopyVec3(vertex3, vertices[indices[v+2]]);
425 if(triangles[i].isSilhouetteEdge[0]) {
426 glVertex3fv(vertex2);
427 glVertex3fv(vertex1);
429 if(triangles[i].isSilhouetteEdge[1]) {
430 glVertex3fv(vertex2);
431 glVertex3fv(vertex3);
433 if(triangles[i].isSilhouetteEdge[2]) {
434 glVertex3fv(vertex3);
435 glVertex3fv(vertex1);
439 glColorMask(0, 0, 0, 0);
440 glEnable( GL_CULL_FACE );
441 glEnable( GL_DEPTH_TEST );
442 glEnable(GL_STENCIL_TEST);
446 // TODO:compute intersection with near clip plane...
447 bool needZFail=false;
449 // GL_INCR_WRAP_EXT, GL_DECR_WRAP_EXT
453 //Increment stencil buffer for back face depth fail
454 glStencilFunc(GL_ALWAYS, 0, ~0);
455 glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
456 glCullFace(GL_FRONT);
458 DrawInfiniteShadowVolume( lightPos, true);
460 //Decrement stencil buffer for front face depth fail
461 glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
464 DrawInfiniteShadowVolume( lightPos, true);
468 //Increment stencil buffer for front face depth pass
469 if( states->use_alpha ) {
470 glBlendEquation( GL_FUNC_ADD );
471 glBlendFunc( GL_ONE, GL_ONE );
472 glColor4ub(1, 1, 1, 16);
474 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
475 glStencilFunc(GL_ALWAYS, 0, ~0);
476 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
480 DrawInfiniteShadowVolume( lightPos, false);
482 //Decrement stencil buffer for back face depth pass
483 if( states->use_alpha ) {
484 glBlendEquation( GL_FUNC_REVERSE_SUBTRACT );
485 glBlendFunc( GL_ONE, GL_ONE );
486 glColor4ub(1, 1, 1, 16);
488 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
490 glCullFace(GL_FRONT);
492 DrawInfiniteShadowVolume( lightPos, false);
497 void SGShadowVolume::SceneryObject::computeShadows(void) {
499 bool intersect = true;
500 // compute intersection with view frustum
501 // use pending_object (pointer on obj transform) & tile transform
503 sgMat4 position, CamXpos;
504 sgFrustum *f = ssgGetFrustum();
505 pending_object->getParent(0)->getNetTransform( position );
506 sgCopyMat4 ( CamXpos, states->CameraViewM ) ;
507 sgPreMultMat4 ( CamXpos, position ) ;
509 sgSphere tmp = *(pending_object->getBSphere()) ;
510 if ( tmp.isEmpty () )
514 float max_dist = 5000.0f;
515 tmp . orthoXform ( CamXpos ) ;
517 if ( -tmp.center[2] - tmp.radius > max_dist )
519 else if( tmp.center[2] == 0.0 )
521 // cull if too small on screen
522 else if ( tmp.radius / sgAbs(tmp.center[2]) < 1.0 / 40.0 )
525 intersect = SSG_OUTSIDE != (ssgCullResult) f -> contains ( &tmp );
529 if( !scenery_object ) {
530 if( states->frameNumber - states->lastTraverseTreeFrame > 5 ) {
533 traverseTree( pending_object );
534 states->lastTraverseTreeFrame = states->frameNumber;
538 sgMat4 rotation, rotation_translation;
539 scenery_object->getNetTransform ( rotation_translation );
540 // split placement offset into rotation and offset
541 // rotation from model inside world
542 // we are not interested in translation since the light is very far away
543 // without translation we reduce the frequency of updates of the silouhette
544 sgCopyMat4( rotation, rotation_translation );
545 sgSetVec4( rotation[3], 0, 0, 0, 1);
547 ShadowCaster_list::iterator iShadowCaster;
548 for(iShadowCaster = parts.begin() ; iShadowCaster != parts.end() ; iShadowCaster ++ ) {
549 (*iShadowCaster)->computeShadows(rotation, rotation_translation);
555 1) starting at the root of a scene object we follow all branch to find vertex leaf.
556 each vertex leaf will create a shadow sub object so that the shadow casted by this
557 geometry can be correctly rendered according to select or transform nodes.
558 2) models are not optimized for shadows because the geometry is cut by
559 - select / transform nodes
560 - material differences
561 since we are not interested in the geometry material we try to merge same level
562 leafs. this can divide the number of shadow sub models by a lot (reducing the cpu
563 overhead for matrix computation) and at the end this will reduce the number of
564 silouhette edge by a lot too.
566 static bool filterName(const char *leaf_name) {
572 for( buff = lname; *leaf_name && l < (sizeof( lname )-1); ++buff, l++ )
573 *buff = tolower(*leaf_name++);
575 if( !strncmp(lname, "noshadow", 8) )
579 void SGShadowVolume::SceneryObject::traverseTree(ssgBranch *branch) {
583 if( sgCheckAnimationBranch( (ssgEntity *) branch ) ) {
584 if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1)
588 for(int i = 0 ; i < branch->getNumKids() ; i++) {
589 ssgEntity *this_kid = branch->getKid( i );
590 if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
591 if( filterName( this_kid->getName()) ) {
592 num_tri += ((ssgLeaf *) this_kid)->getNumTriangles();
596 traverseTree( (ssgBranch *) this_kid );
601 ShadowCaster *new_part = new ShadowCaster( num_tri, branch);
602 new_part->scenery_object = scenery_object;
603 new_part->lib_object = lib_object;
604 for(int i = 0 ; i < branch->getNumKids() ; i++) {
605 ssgEntity *this_kid = branch->getKid( i );
606 if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
607 if( filterName( this_kid->getName()) )
608 new_part->addLeaf( tri_idx, ind_idx, (ssgLeaf *) this_kid );
611 new_part->SetConnectivity();
612 parts.push_back( new_part );
616 void SGShadowVolume::SceneryObject::find_trans(void) {
617 ssgBranch *branch = pending_object;
618 ssgBranch *prev_branch = pending_object;
619 // check the existence of the root node
620 while( branch && branch->getNumParents() > 0 ) {
621 prev_branch = branch;
622 branch = branch->getParent(0);
624 // check if we are connected to the scene graph
625 if( !branch->isA(ssgTypeRoot() ) )
627 scenery_object = pending_object;
630 SGShadowVolume::SceneryObject::SceneryObject(ssgBranch *_scenery_object, OccluderType _occluder_type) :
631 pending_object ( _scenery_object ),
632 occluder_type ( _occluder_type ),
635 // queue objects, don't do all the work in the first frames because of
636 // massive cpu power needed
638 if( occluder_type == SGShadowVolume::occluderTypeAircraft )
639 lib_object = _scenery_object;
641 lib_object = (ssgBranch *) ((ssgBranch *)_scenery_object->getKid(0))->getKid(0);
644 SGShadowVolume::SceneryObject::~SceneryObject()
646 ShadowCaster_list::iterator iParts;
647 for(iParts = parts.begin() ; iParts != parts.end(); iParts++ ) {
653 void SGShadowVolume::computeShadows(void) {
655 // intensity = ambient + lights
656 // lights = sun_const * (sun_angle . normal)
657 double dot_light = cos(sun_angle);
658 // do nothing if sun is under horizon
659 if( dot_light < 0.2 )
662 //Draw shadow volumes
663 glPushAttrib(GL_ALL_ATTRIB_BITS);
664 glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ;
665 glDisableClientState ( GL_COLOR_ARRAY ) ;
666 glDisableClientState ( GL_NORMAL_ARRAY ) ;
667 glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
670 glColorMask(0, 0, 0, 1);
671 glClearColor(0.0, 0.0, 0.0, 0.0 );
672 glClear(GL_COLOR_BUFFER_BIT);
677 glClear(GL_STENCIL_BUFFER_BIT);
678 glColorMask(0, 0, 0, 0);
679 glEnable(GL_STENCIL_TEST);
683 glDisable( GL_LIGHTING );
685 glEnable( GL_CULL_FACE );
686 // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
687 // glPolygonOffset(0.0,5.0);
688 glPolygonOffset(0.0,30.0);
689 glEnable(GL_POLYGON_OFFSET_FILL);
691 glShadeModel(GL_FLAT);
693 glDepthMask( false );
694 glEnable( GL_DEPTH_TEST );
695 glDepthFunc(GL_LESS);
697 SceneryObject_map::iterator iSceneryObject;
698 // compute shadows for each objects
699 for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); iSceneryObject++ ) {
700 SceneryObject *an_occluder = iSceneryObject->second;
701 if( shadowsTO_enabled && (an_occluder->occluder_type == occluderTypeTileObject) ||
702 shadowsAI_enabled && (an_occluder->occluder_type == occluderTypeAI ) ||
703 shadowsAC_enabled && (an_occluder->occluder_type == occluderTypeAircraft ) )
704 an_occluder->computeShadows();
708 glMatrixMode ( GL_PROJECTION ) ;
711 glOrtho ( -100, 100, -100, 100, -1, 1 ) ;
712 glMatrixMode ( GL_MODELVIEW ) ;
716 glDisable(GL_DEPTH_TEST);
717 glDisable(GL_CULL_FACE);
718 // glBindTexture(GL_TEXTURE_2D, 0);
719 glPolygonMode(GL_FRONT, GL_FILL);
721 // there is a flaw in the Roettger paper, this does not work for some geometry
722 // where we increment (multiply) the buffer a few times then we
723 // decrement (divide) a few times. Decrementing more then once will give a
724 // non shadowed parts with mask value < 0.25 because the incrementation was
726 // Solution : don't use a start value as high as 0.25 but the smallest value
727 // posible ie 1/256. This is not a general solution, a stencil buffer will
728 // support 255 incrementation before clamping, the alpha mask only 7.
729 // => that still does not work with our geometry so use subtractive blend
731 // clamp mask = {0,16,32,64,...} => {0,16,16,16,...}
732 glBlendEquation( GL_MIN_EXT );
733 glBlendFunc( GL_DST_COLOR, GL_ONE );
734 glColor4ub(1, 1, 1, 16);
735 glRectf(-100,-100,100,100);
736 // scale mask = {0,16} => {0,64}
737 glBlendEquation( GL_FUNC_ADD );
738 glBlendFunc( GL_DST_COLOR, GL_ONE );
739 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
740 glRectf(-100,-100,100,100);
741 glRectf(-100,-100,100,100);
742 // negate mask => {0,64} => {255, 191}
743 glBlendFunc( GL_ONE_MINUS_DST_COLOR, GL_ZERO );
744 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
745 glRectf(-100,-100,100,100);
747 glColorMask(1, 1, 1, 1);
748 glBlendFunc( GL_ZERO, GL_DST_ALPHA );
749 glColor4f(1.0f, 0.5f, 0.2f, 1.0f);
750 glRectf(-100,-100,100,100);
752 // now the stencil contains 0 for scenery in light and != 0 for parts in shadow
753 // draw a quad covering the screen, the stencil will be the mask
754 // we darken the shadowed parts
755 glColorMask(1, 1, 1, 1);
756 glStencilFunc(GL_NOTEQUAL, 0, ~0);
757 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
758 glEnable(GL_STENCIL_TEST);
760 glAlphaFunc(GL_GREATER, 0.0f);
762 glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
763 glColor4f(0.0, 0.0, 0.0, sgLerp(0.1, 0.3, dot_light) );
764 // fixed value is better, the previous line is surely wrong
765 glColor4f(0.0, 0.0, 0.0, 0.3 );
766 glRectf(-100,-100,100,100);
768 glMatrixMode ( GL_PROJECTION ) ;
770 glMatrixMode ( GL_MODELVIEW ) ;
773 glDisable(GL_STENCIL_TEST);
774 glPopClientAttrib ( ) ;
778 SGShadowVolume::SGShadowVolume() :
780 shadows_enabled( false ),
782 lastTraverseTreeFrame ( 0 )
787 SGShadowVolume::~SGShadowVolume() {
788 SceneryObject_map::iterator iSceneryObject;
789 for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); ) {
790 delete iSceneryObject->second;
791 iSceneryObject = sceneryObjects.erase( iSceneryObject );
795 void SGShadowVolume::init(SGPropertyNode *sim_rendering_options) {
797 shadows_enabled = true;
798 sim_rendering = sim_rendering_options;
799 int stencilBits = 0, alphaBits = 0;
800 glGetIntegerv( GL_STENCIL_BITS, &stencilBits );
801 glGetIntegerv( GL_ALPHA_BITS, &alphaBits );
802 bool hasSubtractiveBlend = SGIsOpenGLExtensionSupported("GL_EXT_blend_subtract");
803 bool hasMinMaxBlend = SGIsOpenGLExtensionSupported("GL_EXT_blend_minmax");
804 if( hasSubtractiveBlend )
805 glBlendEquation = (glBlendEquationProc ) SGLookupFunction("glBlendEquationEXT");
806 canDoAlpha = (alphaBits >= 8) && hasSubtractiveBlend && hasMinMaxBlend;
807 canDoStencil = (stencilBits >= 3);
810 SG_LOG(SG_ALL, SG_WARN, "SGShadowVolume:no stencil buffer, using alpha buffer");
812 SG_LOG(SG_ALL, SG_WARN, "SGShadowVolume:no stencil buffer and no alpha buffer");
815 void SGShadowVolume::startOfFrame(void) {
817 void SGShadowVolume::deleteOccluderFromTile(ssgBranch *tile) {
818 SceneryObject_map::iterator iSceneryObject;
819 for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); ) {
820 if( iSceneryObject->second->tile == tile ) {
821 delete iSceneryObject->second;
822 iSceneryObject = sceneryObjects.erase( iSceneryObject );
829 void SGShadowVolume::deleteOccluder(ssgBranch *occluder) {
830 ssgBranch *branch = occluder;
831 // skip first node and go to first transform (placement)
832 while( occluder && !occluder->isA(ssgTypeTransform()))
833 occluder = (ssgBranch *) occluder->getKid(0);
835 // check if we allready know this object
836 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
837 if( iSceneryObject != sceneryObjects.end() ) {
838 delete iSceneryObject->second;
839 sceneryObjects.erase( occluder );
843 void SGShadowVolume::addOccluder(ssgBranch *occluder, OccluderType occluder_type, ssgBranch *tile) {
845 ssgBranch *branch = occluder;
847 // skip first node and go to first transform (placement)
848 while( occluder && !occluder->isA(ssgTypeTransform()))
849 occluder = (ssgBranch *) occluder->getKid(0);
851 // check if we allready know this object
852 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
853 if( iSceneryObject == sceneryObjects.end() ) {
854 // printf("adding model %x %x\n", occluder, tile);
855 SceneryObject *entry = new SceneryObject( occluder, occluder_type );
857 sceneryObjects[ occluder ] = entry;
862 void SGShadowVolume::setupShadows( double lon, double lat,
863 double gst, double SunRightAscension, double SunDeclination, double sunAngle) {
865 shadowsAC_enabled = sim_rendering->getBoolValue("shadows-ac", false);
866 shadowsAI_enabled = sim_rendering->getBoolValue("shadows-ai", false);
867 shadowsTO_enabled = sim_rendering->getBoolValue("shadows-to", false);
868 shadowsDebug_enabled = sim_rendering->getBoolValue("shadows-debug", false);
869 // shadows_enabled = sim_rendering->getBoolValue("shadows", false);
870 shadows_enabled = shadowsAC_enabled || shadowsAI_enabled || shadowsTO_enabled;
871 shadows_enabled &= canDoAlpha || canDoStencil;
872 use_alpha = ((!canDoStencil) || sim_rendering->getBoolValue("shadows-alpha", false)) &&
875 if( ! shadows_enabled )
879 sun_angle = sunAngle;
884 sgSetVec3( axis, 0.0, 0.0, 1.0 );
885 sgMakeRotMat4( LON, lon, axis );
887 sgSetVec3( axis, 0.0, 1.0, 0.0 );
888 sgMakeRotMat4( LAT, 90.0 - lat, axis );
892 sgMakeIdentMat4 ( TRANSFORM );
893 sgPreMultMat4( TRANSFORM, LON );
894 sgPreMultMat4( TRANSFORM, LAT );
897 sgSetCoord( &pos, TRANSFORM );
899 sgMakeCoordMat4( view_angle, &pos );
906 sgSetVec3( axis, 0.0, 0.0, -1.0 );
907 sgMakeRotMat4( GST, (gst) * 15.0, axis );
909 sgSetVec3( axis, 0.0, 0.0, 1.0 );
910 sgMakeRotMat4( RA, (SunRightAscension * SGD_RADIANS_TO_DEGREES) - 90.0, axis );
912 sgSetVec3( axis, 1.0, 0.0, 0.0 );
913 sgMakeRotMat4( DEC, SunDeclination * SGD_RADIANS_TO_DEGREES, axis );
915 sgInvertMat4( invViewAngle, view_angle);
918 sgMakeIdentMat4( TRANSFORM );
919 sgPreMultMat4( TRANSFORM, GST );
920 sgPreMultMat4( TRANSFORM, RA );
921 sgPreMultMat4( TRANSFORM, DEC );
922 sgSetVec3( sunPos, 0.0, 9900000.0, 0.0);
923 sgXformPnt3( sunPos, TRANSFORM );
926 ssgGetModelviewMatrix( CameraViewM );
930 void SGShadowVolume::endOfFrame(void) {
931 if( ! shadows_enabled )
933 glBindTexture(GL_TEXTURE_2D, 0);
934 glBindTexture(GL_TEXTURE_1D, 0);
936 glMatrixMode(GL_MODELVIEW);