shadowvolume.cxx

00001 // Shadow volume class
00002 //
00003 // Written by Harald JOHNSEN, started June 2005.
00004 //
00005 // Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public License as
00009 // published by the Free Software Foundation; either version 2 of the
00010 // License, or (at your option) any later version.
00011 //
00012 // This program is distributed in the hope that it will be useful, but
00013 // WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 // General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with this program; if not, write to the Free Software
00019 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020 //
00021 //
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #  include <simgear_config.h>
00025 #endif
00026 
00027 #include <plib/sg.h>
00028 #include <simgear/debug/logstream.hxx>
00029 #include <simgear/screen/extensions.hxx>
00030 #include <simgear/scene/model/animation.hxx>
00031 #include <simgear/scene/model/model.hxx>
00032 #include <simgear/environment/visual_enviro.hxx>
00033 #include SG_GLU_H
00034 
00035 #include "shadowvolume.hxx"
00036 
00037 /*
00038  geometry and edge list
00039  - traverse object graph until leaf to get geometry
00040  - what about transform and selection ?
00041         - use sub objects rather then objects
00042         - get local transform and selection
00043                 - range anim : ssgRangeSelector ( min, max ) => ssgSelector ( true/false )
00044                         => selection [0..n], kids [0..n]
00045                 - select/timed anim : ssgSelector ( true/false )
00046                         => isSelected( nkid )
00047                 - spin/rotate/trans/scale/... : ssgTransform ( matrix )
00048                         => getNetTransform
00049  - on new object :
00050         - for one object branch
00051                 - for each leaf
00052                         - save geometry
00053                         - save address in cache
00054  - when rendering object
00055         - for each leaf
00056                 - getNetTransform + object global rotation (ac) => transform for light position
00057                 - go up in tree and check isSelected( self )
00058  - generate connectivity each time the geometry change
00059         => using local light so connectivity never changes
00060  - generate active edge list when :
00061         - light moves
00062         - subpart moves (animation code)
00063         - ac rotate
00064                 => cache rotation matrix and generate edge list only if something change
00065                 => even if it changes, no need to do that every frame
00066 
00067  - static objects have static edge list if light does not move
00068 
00069  shadowing a scene
00070  - render full scene as normal
00071  - render occluder in stencil buffer with their shadow volumes
00072  - apply stencil to framebuffer (darkens shadowed parts)
00073 
00074         shadows using the alpha buffer
00075         http://wwwvis.informatik.uni-stuttgart.de/~roettger/html/Pages/shadows.html
00076 */
00077 
00078 // TODO
00079 //      - shadow for objects
00080 //              * aircraft
00081 //              * tile objects (from .stg)
00082 //              - ai objects
00083 //              - random objects => tie shadow geometry to lib objects and reuse them
00084 //      - zfail if camera inside shadow
00085 //      - queue geometry work if lot of objects
00086 //      * add a render property on/off (for aircraft, for scene objects, for ai)
00087 //      * add a render property in rendering dialog
00088 //      * filter : halo, light, shadow, disc, disk, flame, (exhaust), noshadow
00089 
00090 static int statSilhouette=0;
00091 static int statGeom=0;
00092 static int statObj=0;
00093 
00094 static SGShadowVolume *states;
00095 static glBlendEquationProc glBlendEquationPtr = NULL;
00096 #define GL_MIN_EXT                          0x8007
00097 #define GL_MAX_EXT                          0x8008
00098 
00099 
00100 SGShadowVolume::ShadowCaster::ShadowCaster( int _num_tri, ssgBranch * _geometry_leaf ) : 
00101         geometry_leaf ( _geometry_leaf ),
00102         scenery_object ( 0 ),
00103         first_select ( 0 ),
00104         frameNumber ( 0 ),
00105         indices ( 0 ),
00106         numTriangles ( 0 ),
00107         vertices ( 0 ),
00108         lastSilhouetteIndicesCount ( 0 )
00109 {
00110         int num_tri = _num_tri;
00111         numTriangles = num_tri;
00112         triangles = new triData[ num_tri ];
00113         indices = new int[1 + num_tri * 3];
00114         vertices = new sgVec4[1 + num_tri * 3];
00115         silhouetteEdgeIndices = new GLushort[(1+num_tri) * 3*3];
00116         indices [ num_tri * 3 ] = num_tri * 3;
00117         sgSetVec3(last_lightpos, 0.0, 0.0, 0.0);
00118         statGeom ++;
00119 
00120         ssgBranch *branch = (ssgBranch *) _geometry_leaf;
00121         while( branch && branch->getNumParents() > 0 ) {
00122                 if( branch->isAKindOf(ssgTypeSelector())) {
00123                         first_select = branch;
00124                         break;
00125                 }
00126                 if( sgCheckAnimationBranch( (ssgEntity *) branch ) )
00127                         if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1) {
00128                                 first_select = branch;
00129                                 break;
00130                         }
00131                 branch = branch->getParent(0);
00132         }
00133 }
00134 
00135 void SGShadowVolume::ShadowCaster::addLeaf( int & tri_idx, int & ind_idx, ssgLeaf *geometry_leaf ) {
00136         int num_tri = geometry_leaf->getNumTriangles();
00137         for(int i = 0; i < num_tri ; i ++ ) {
00138                 short v1, v2, v3;
00139                 sgVec3 a, b, c;
00140                 geometry_leaf->getTriangle( i, &v1, &v2, &v3 );
00141                 sgCopyVec3(a, geometry_leaf->getVertex(v1));
00142                 sgCopyVec3(b, geometry_leaf->getVertex(v2));
00143                 sgCopyVec3(c, geometry_leaf->getVertex(v3));
00144 
00145                 int p = tri_idx;
00146                 sgMakePlane ( triangles[p].planeEquations, a, b, c );
00147                 sgCopyVec3(vertices[ind_idx + v1], a);
00148                 sgCopyVec3(vertices[ind_idx + v2], b);
00149                 sgCopyVec3(vertices[ind_idx + v3], c);
00150                 vertices[ind_idx + v1][SG_W] = 1.0f;
00151                 vertices[ind_idx + v2][SG_W] = 1.0f;
00152                 vertices[ind_idx + v3][SG_W] = 1.0f;
00153                 indices[p*3] = ind_idx + v1;
00154                 indices[p*3+1] = ind_idx + v2;
00155                 indices[p*3+2] = ind_idx + v3;
00156 
00157                 tri_idx++;
00158         }
00159         if( num_tri == 0 )
00160                 return;
00161         isTranslucent |= geometry_leaf->isTranslucent() ? true : false;
00162         int num_ind = geometry_leaf->getNumVertices();
00163         ind_idx += num_ind;
00164 }
00165 
00166 SGShadowVolume::ShadowCaster::~ShadowCaster() {
00167         delete [] indices ;
00168         delete [] vertices ;
00169         delete [] triangles;
00170         delete [] silhouetteEdgeIndices;
00171 }
00172 
00173 
00174 
00175 bool SGShadowVolume::ShadowCaster::sameVertex(int edge1, int edge2) {
00176         if( edge1 == edge2)
00177                 return true;
00178         sgVec3 delta_v;
00179         sgSubVec3( delta_v, vertices[edge1], vertices[edge2]);
00180         if( delta_v[SG_X] != 0.0)       return false;
00181         if( delta_v[SG_Y] != 0.0)       return false;
00182         if( delta_v[SG_Z] != 0.0)       return false;
00183         return true;
00184 }
00185 
00186 
00187 //Calculate neighbour faces for each edge
00188 // caution, this is O(n2)
00189 void SGShadowVolume::ShadowCaster::SetConnectivity(void)
00190 {
00191         int edgeCount = 0;
00192 
00193         //set the neighbour indices to be -1
00194         for(int ii=0; ii<numTriangles; ++ii)
00195                 triangles[ii].neighbourIndices[0] = 
00196                 triangles[ii].neighbourIndices[1] = 
00197                 triangles[ii].neighbourIndices[2] = -1;
00198 
00199         //loop through triangles
00200         for(int i=0; i<numTriangles-1; ++i)
00201         {
00202                 //loop through edges on the first triangle
00203                 for(int edgeI=0; edgeI<3; ++edgeI)
00204                 {
00205                         //continue if this edge already has a neighbour set
00206                         if(triangles[i].neighbourIndices[ edgeI ]!=-1)
00207                                 continue;
00208 
00209                         //get the vertex indices on each edge
00210                         int edgeI1=indices[i*3+edgeI];
00211                         int edgeI2=indices[i*3+(edgeI == 2 ? 0 : edgeI+1)];
00212 
00213                         //loop through triangles with greater indices than this one
00214                         for(int j=i+1; j<numTriangles; ++j)
00215                         {
00216                                 //loop through edges on triangle j
00217                                 for(int edgeJ=0; edgeJ<3; ++edgeJ)
00218                                 {
00219                                         //continue if this edge already has a neighbour set
00220                                         if(triangles[j].neighbourIndices[ edgeJ ]!=-1) {
00221                                                 continue;
00222                                         }
00223                                         //get the vertex indices on each edge
00224                                         int edgeJ1=indices[j*3+edgeJ];
00225                                         int edgeJ2=indices[j*3+(edgeJ == 2 ? 0 : edgeJ+1)];
00226 
00227                                         //if these are the same (possibly reversed order), these faces are neighbours
00228 #if 0
00229                                         //no, we only use reverse order because same order means that
00230                                         //the triangle is wrongly oriented and that will cause shadow artifact
00231                                         if(     sameVertex(edgeI1, edgeJ1) && sameVertex(edgeI2, edgeJ2) ) {
00232                                                         // can happens with 'bad' geometry
00233 //                                                      printf("flipped triangle detected...check your model\n");
00234                                                 continue;
00235                                         }
00236 #endif
00237                                         if(     sameVertex(edgeI1, edgeJ2) && sameVertex(edgeI2, edgeJ1) )
00238                                         {
00239                                                 int edgeI3=indices[i*3+(edgeI == 0 ? 2 : edgeI-1)];
00240                                                 int edgeJ3=indices[j*3+(edgeJ == 0 ? 2 : edgeJ-1)];
00241                                                 if(     sameVertex(edgeI3, edgeJ3) ) {
00242                                                         // can happens with 'bad' geometry
00243 //                                                      printf("duplicated tri...check your model\n");
00244                                                         // exit loop
00245                                                         break;
00246                                                 }
00247                                                 triangles[i].neighbourIndices[edgeI]=j;
00248                                                 triangles[j].neighbourIndices[edgeJ]=i;
00249                                                 edgeCount ++;
00250                                                 // exit loop
00251                                                 j = numTriangles;
00252                                                 break;
00253                                         }
00254                                 }
00255                         }
00256                 }
00257         }
00258 //      printf("submodel has %d triangles and %d shared edges\n", numTriangles, edgeCount);
00259 }
00260 
00261 //calculate silhouette edges
00262 void SGShadowVolume::ShadowCaster::CalculateSilhouetteEdges(sgVec3 lightPosition)
00263 {
00264         //Calculate which faces face the light
00265         for(int i=0; i<numTriangles; ++i)
00266         {
00267                 if( sgDistToPlaneVec3 ( triangles[i].planeEquations, lightPosition ) > 0.0 )
00268                         triangles[i].isFacingLight=true;
00269                 else
00270                         triangles[i].isFacingLight=false;
00271         }
00272 
00273         //loop through faces
00274         int iEdgeIndices = 0;
00275         sgVec4 farCap = {-lightPosition[SG_X], -lightPosition[SG_Y], -lightPosition[SG_Z], 1.0f};
00276         sgCopyVec4( vertices[ numTriangles*3 ], farCap );
00277 
00278         for(int t=0; t < numTriangles; t++) {
00279                 int v = t * 3;
00280                 //if this face is not facing the light, not a silhouette edge
00281                 if(!triangles[t].isFacingLight)
00282                 {
00283                         triangles[t].isSilhouetteEdge[0]=false;
00284                         triangles[t].isSilhouetteEdge[1]=false;
00285                         triangles[t].isSilhouetteEdge[2]=false;
00286                         continue;
00287                 }
00288                 //loop through edges
00289                 for(int j = 0 ; j < 3 ; j++) {
00290                         //this face is facing the light
00291                         //if the neighbouring face is not facing the light, or there is no neighbouring face,
00292                         //then this is a silhouette edge
00293                         if(triangles[t].neighbourIndices[j]==-1 || 
00294                                 !triangles[triangles[t].neighbourIndices[j]].isFacingLight ) {
00295                                 triangles[t].isSilhouetteEdge[j]=true;
00296                                 silhouetteEdgeIndices[ iEdgeIndices++ ] = indices[v+(j == 2 ? 0 : j+1)];
00297                                 silhouetteEdgeIndices[ iEdgeIndices++ ] = indices[v+j];
00298                                 silhouetteEdgeIndices[ iEdgeIndices++ ] = numTriangles * 3;
00299                         }
00300                         else 
00301                                 triangles[t].isSilhouetteEdge[j]=false;
00302                 }
00303         }
00304         lastSilhouetteIndicesCount = iEdgeIndices;
00305 }
00306 
00307 void SGShadowVolume::ShadowCaster::DrawInfiniteShadowVolume(sgVec3 lightPosition, bool drawCaps)
00308 {
00309         glEnableClientState ( GL_VERTEX_ARRAY ) ;
00310         glVertexPointer ( 4, GL_FLOAT, 0, vertices ) ;
00311         glDrawElements ( GL_TRIANGLES, lastSilhouetteIndicesCount, GL_UNSIGNED_SHORT, silhouetteEdgeIndices ) ;
00312 
00313         //Draw caps if required
00314         if(drawCaps)
00315         {
00316                 glBegin(GL_TRIANGLES);
00317                 {
00318                         for(int i=0; i<numTriangles; ++i)
00319                         {
00320                                 if(triangles[i].isFacingLight) {
00321                                         int v = i*3;
00322                                         glVertex3fv( vertices[indices[v+0]] );
00323                                         glVertex3fv( vertices[indices[v+1]] );
00324                                         glVertex3fv( vertices[indices[v+2]] );
00325                                 }
00326                         }
00327                 }
00328                 glEnd();
00329         }
00330 }
00331 
00332 
00333 void SGShadowVolume::ShadowCaster::getNetTransform ( ssgBranch * branch, sgMat4 xform )
00334 {
00335         // one less matmult...
00336         bool first = true;
00337         while( branch && branch != lib_object ) {
00338                 if( branch->isA(ssgTypeTransform()) ) {
00339                         sgMat4 transform;
00340                         if( first ) {
00341                                 ((ssgTransform *) branch)->getTransform( xform );
00342                                 first = false;
00343                         } else {
00344                                 ((ssgTransform *) branch)->getTransform(transform);
00345                                 sgPostMultMat4 ( xform, transform ) ;
00346                         }
00347                 }
00348                 branch = branch->getParent( 0 );
00349         }
00350         if( first )
00351                 sgMakeIdentMat4 ( xform ) ;
00352 }
00353 
00354 // check the value of <select> and <range> animation
00355 // wich have been computed during the rendering
00356 bool SGShadowVolume::ShadowCaster::isSelected (  ssgBranch * branch, float dist ) {
00357         while( branch && branch != lib_object) {
00358                 if( sgCheckAnimationBranch( (ssgEntity *) branch ) ) {
00359                         if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1)
00360                                 if( ((SGShadowAnimation *) branch->getUserData())->get_condition_value() )
00361                                         return false;
00362                 }
00363                 // recompute range check since the value in the branch is shared by multiple objects
00364                 // we can only have the last value
00365                 if( branch->isA(ssgTypeRangeSelector()) )
00366                         if( dist >= ((ssgRangeSelector *) branch)->getRange(1) ||
00367                                 dist < ((ssgRangeSelector *) branch)->getRange(0))
00368                                 return false;
00369                 if( branch->isA(ssgTypeSelector()) )
00370                         if( !((ssgSelector *) branch)->isSelected(0) )
00371                                 return false;
00372                 branch = branch->getParent(0);
00373         }
00374         return true;
00375 }
00376 /*
00377         trans ----- personality ---- shared object
00378         perso1 *-----
00379                      \
00380                       *--------* shared object
00381                      /
00382         perso2 *-----
00383 */
00384 void SGShadowVolume::ShadowCaster::computeShadows(sgMat4 rotation, sgMat4 rotation_translation,
00385         OccluderType occluder_type) {
00386   
00387         // check the select and range ssgSelector node
00388         // object can't cast shadow if it is not visible
00389         sgVec4 trans;
00390         sgCopyVec4( trans, rotation_translation[3] );
00391         sgAddVec4( trans, states->CameraViewM[3] );
00392         float dist = sgLengthVec3( trans );
00393 
00394         if( first_select && ! isSelected( first_select, dist) )
00395                 return;
00396 
00397                 // get the transformations : this comes from animation code for example
00398                 // or static transf
00399         sgMat4 transf;
00400         sgVec3 lightPos;
00401         int deltaFrame = occluder_type == SGShadowVolume::occluderTypeAircraft ? 0 : 9;
00402         if( states->frameNumber - frameNumber > deltaFrame) {
00403                 sgMat4 transform ;
00404                 sgMat4 invTransform;
00405                 getNetTransform( (ssgBranch *) geometry_leaf, transform );
00406                 sgCopyMat4( last_transform, transform );
00407                 sgPostMultMat4( transform, rotation );
00408                 sgTransposeNegateMat4 ( invTransform, transform ); 
00409 
00410                 sgCopyVec3( lightPos, states->sunPos );
00411                 sgXformPnt3( lightPos, invTransform );
00412 
00413                 sgVec3 lightPosNorm;
00414                 sgNormaliseVec3( lightPosNorm, lightPos );
00415                 float deltaPos = sgAbs(lightPosNorm[0] - last_lightpos[0]) + 
00416                                 sgAbs(lightPosNorm[1] - last_lightpos[1]) +
00417                                 sgAbs(lightPosNorm[2] - last_lightpos[2]);
00418                 // if the geometry has rotated/moved enought then
00419                 // we need to recompute the silhouette
00420                 // but this computation does not need to be done each frame
00421                 if( deltaPos > 0.0 ) {
00422                         CalculateSilhouetteEdges( lightPos );
00423                         sgCopyVec3( last_lightpos, lightPosNorm );
00424                         frameNumber = states->frameNumber ;
00425                         statSilhouette ++;
00426                 }
00427         }
00428         sgCopyMat4( transf, last_transform );
00429         sgPostMultMat4( transf, rotation_translation );
00430         glLoadMatrixf ( (float *) states->CameraViewM ) ;
00431         glMultMatrixf( (float *) transf );
00432 
00433         if( states->shadowsDebug_enabled )
00434         {
00435                 glStencilFunc(GL_ALWAYS, 0, ~0);
00436                 glDisable( GL_CULL_FACE  );
00437                 glDisable( GL_DEPTH_TEST );
00438                 glDisable(GL_STENCIL_TEST);
00439                 glColorMask(1, 1, 1, 1);
00440                 glColor4f(0.0, 0.0, 1.0, 1.0);
00441                 glBegin(GL_LINES);
00442                 for(int i=0; i<numTriangles; ++i)
00443                 {
00444                         if(!triangles[i].isFacingLight)
00445                                 continue;
00446                         int v = i*3;
00447                         //Loop through edges on this face
00448                         sgVec3 vertex1, vertex2, vertex3;
00449                         sgCopyVec3(vertex1, vertices[indices[v+0]]);
00450                         sgCopyVec3(vertex2, vertices[indices[v+1]]);
00451                         sgCopyVec3(vertex3, vertices[indices[v+2]]);
00452 
00453                         if(triangles[i].isSilhouetteEdge[0]) {
00454                                 glVertex3fv(vertex2);
00455                                 glVertex3fv(vertex1);
00456                         }
00457                         if(triangles[i].isSilhouetteEdge[1]) {
00458                                 glVertex3fv(vertex2);
00459                                 glVertex3fv(vertex3);
00460                         }
00461                         if(triangles[i].isSilhouetteEdge[2]) {
00462                                 glVertex3fv(vertex3);
00463                                 glVertex3fv(vertex1);
00464                         }
00465                 }
00466                 glEnd();
00467                 glColorMask(0, 0, 0, 0);
00468                 glEnable( GL_CULL_FACE  );
00469                 glEnable( GL_DEPTH_TEST );
00470                 glEnable(GL_STENCIL_TEST);
00471         }
00472 
00473 
00474                 // TODO:compute intersection with near clip plane...
00475                 bool needZFail=false;
00476 
00477                 // GL_INCR_WRAP_EXT, GL_DECR_WRAP_EXT
00478 
00479                 if(needZFail)
00480                 {
00481                         //Increment stencil buffer for back face depth fail
00482                         glStencilFunc(GL_ALWAYS, 0, ~0);
00483                         glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
00484                         glCullFace(GL_FRONT);
00485 
00486                         DrawInfiniteShadowVolume( lightPos, true);
00487 
00488                         //Decrement stencil buffer for front face depth fail
00489                         glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
00490                         glCullFace(GL_BACK);
00491 
00492                         DrawInfiniteShadowVolume( lightPos, true);
00493                 }
00494                 else    //using zpass
00495                 {
00496                         //Increment stencil buffer for front face depth pass
00497                         if( states->use_alpha ) {
00498                                 glBlendEquationPtr( GL_FUNC_ADD );
00499                                 glBlendFunc( GL_ONE, GL_ONE );
00500                                 glColor4ub(1, 1, 1, 16);
00501                         } else {
00502                                 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
00503                                 glStencilFunc(GL_ALWAYS, 0, ~0);
00504                                 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
00505                         }
00506                         glCullFace(GL_BACK);
00507 
00508                         DrawInfiniteShadowVolume( lightPos, states->shadowsAC_transp_enabled & isTranslucent);
00509 
00510                         //Decrement stencil buffer for back face depth pass
00511                         if( states->use_alpha ) {
00512                                 glBlendEquationPtr( GL_FUNC_REVERSE_SUBTRACT );
00513                                 glBlendFunc( GL_ONE, GL_ONE );
00514                                 glColor4ub(1, 1, 1, 16);
00515                         } else {
00516                                 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
00517                         }
00518                         glCullFace(GL_FRONT);
00519 
00520                         DrawInfiniteShadowVolume( lightPos, states->shadowsAC_transp_enabled & isTranslucent);
00521                 }
00522 }
00523 
00524 
00525 void SGShadowVolume::SceneryObject::computeShadows(void) {
00526 
00527         // compute intersection with view frustum
00528         // use pending_object (pointer on obj transform) & tile transform
00529         // to get position
00530                 if( !scenery_object ) {
00531                         if( states->frameNumber - states->lastTraverseTreeFrame > 5 ) {
00532                                 find_trans();
00533                                 if( scenery_object )
00534                                         traverseTree( pending_object );
00535                                         states->lastTraverseTreeFrame = states->frameNumber;
00536                         }
00537                         return;
00538                 }
00539                 sgMat4 rotation, rotation_translation;
00540                 scenery_object->getNetTransform ( rotation_translation );
00541                 // split placement offset into rotation and offset
00542                 // rotation from model inside world
00543                 // we are not interested in translation since the light is very far away
00544                 // without translation we reduce the frequency of updates of the silouhette
00545                 sgCopyMat4( rotation, rotation_translation );
00546                 sgSetVec4( rotation[3], 0, 0, 0, 1);
00547 
00548                 ShadowCaster_list::iterator iShadowCaster;
00549                 for(iShadowCaster = parts.begin() ; iShadowCaster != parts.end() ; iShadowCaster ++ ) {
00550                         (*iShadowCaster)->computeShadows(rotation, rotation_translation, occluder_type);
00551                 }
00552 }
00553 
00554 static ssgCullResult cull_test ( ssgEntity *e, sgFrustum *f, sgMat4 m, int test_needed )
00555 {
00556   if ( ! test_needed )
00557     return SSG_INSIDE ;
00558 
00559   sgSphere tmp = *(e->getBSphere()) ;
00560 
00561   if ( tmp.isEmpty () )
00562     return SSG_OUTSIDE ;
00563 
00564   tmp . orthoXform ( m ) ;
00565   if( tmp.center[2] == 0.0 )
00566           return SSG_STRADDLE;
00567   // cull if too small on screen
00568   if ( tmp.radius / sgAbs(tmp.center[2]) < 1.0 / 40.0 )
00569     return SSG_OUTSIDE ;
00570 
00571   return (ssgCullResult) f -> contains ( &tmp ) ;
00572 }
00573 
00574 
00575 void SGShadowVolume::cull ( ssgBranch *b, sgFrustum *f, sgMat4 m, int test_needed )
00576 {
00577         int cull_result = cull_test ( (ssgEntity *) b, f, m, test_needed ) ;
00578 
00579         if ( cull_result == SSG_OUTSIDE )
00580                 return ;
00581         if( b->isA( ssgTypeTransform() ) ) {
00582 
00583                 SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( b );
00584                 if( iSceneryObject != sceneryObjects.end() ) {
00585                         SceneryObject *an_occluder = iSceneryObject->second;
00586                         if( shadowsTO_enabled && (an_occluder->occluder_type == occluderTypeTileObject) ||
00587                                 shadowsAI_enabled && (an_occluder->occluder_type == occluderTypeAI ) ||
00588                                 shadowsAC_enabled && (an_occluder->occluder_type == occluderTypeAircraft ) )
00589                                         an_occluder->computeShadows();
00590 
00591                         return;
00592                 }
00593                 sgMat4 tmp, transform ;
00594                 sgCopyMat4 ( tmp, m ) ;
00595                 ((ssgTransform *)b)->getTransform( transform );
00596                 sgPreMultMat4 ( tmp,  transform ) ;
00597                 glPushMatrix () ;
00598                 glLoadMatrixf ( (float *) tmp ) ;
00599                 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid() )
00600                         cull ( (ssgBranch *) e, f, tmp, cull_result != SSG_INSIDE ) ;
00601                 glPopMatrix () ;
00602         } else if( b->isAKindOf( ssgTypeSelector() ) ) {
00603                 int s = ((ssgSelector *) b)->getSelect() ;
00604                 if( b->isA( ssgTypeRangeSelector() ) ) {
00605                         float range = sgLengthVec3 ( m [ 3 ] ) ;
00606                         s = (range < ((ssgRangeSelector *) b)->getRange(1) &&
00607                                 range >= ((ssgRangeSelector *) b)->getRange(0) ) ? 1 : 0;
00608                 }
00609                 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid(), s >>= 1 )
00610                         if ( s & 1 )
00611                                 cull ( (ssgBranch *) e, f, m, cull_result != SSG_INSIDE ) ;
00612         } else if( b->isAKindOf( ssgTypeBranch() ) ) {
00613                 char *name = b->getName();
00614                 // quick exit for the hundreds of ground leafs
00615                 if( name && !strcmp(name, "LocalTerrain") )
00616                         return;
00617                 for ( ssgEntity *e = b->getKid ( 0 ) ; e != NULL ; e = b->getNextKid() )
00618                         if( ! e->isAKindOf( ssgTypeLeaf() ) )
00619                                 cull ( (ssgBranch *) e, f, m, cull_result != SSG_INSIDE ) ;
00620         }
00621 }
00622 
00623 /*
00624         1) starting at the root of a scene object we follow all branch to find vertex leaf.
00625         each vertex leaf will create a shadow sub object so that the shadow casted by this
00626         geometry can be correctly rendered according to select or transform nodes.
00627         2) models are not optimized for shadows because the geometry is cut by
00628                 - select / transform nodes
00629                 - material differences
00630         since we are not interested in the geometry material we try to merge same level
00631         leafs. this can divide the number of shadow sub models by a lot (reducing the cpu
00632         overhead for matrix computation) and at the end this will reduce the number of
00633         silouhette edge by a lot too.
00634 */
00635 static bool filterLeaf(ssgLeaf *this_kid) {
00636         const char *leaf_name = this_kid->getName();
00637 /*      ssgSimpleState *sst = (ssgSimpleState *) this_kid->getState();
00638         if( sst && sst->isTranslucent() )
00639                 return false;*/
00640         if( ! leaf_name )
00641                 return true;
00642         char lname[20];
00643         unsigned int l = 0;
00644         char *buff;
00645         for( buff = lname; *leaf_name && l < (sizeof( lname )-1); ++buff, l++ )
00646              *buff = tolower(*leaf_name++);
00647         *buff = 0;
00648         if( !strncmp(lname, "noshadow", 8) ) {
00649                 SG_LOG(SG_ALL, SG_WARN, "use of deprecated noshadow prefix in '"
00650                                 << this_kid->getName() << '\'');
00651                 return false;
00652         }
00653         return true;
00654 }
00655 void SGShadowVolume::SceneryObject::traverseTree(ssgBranch *branch) {
00656         int num_tri = 0;
00657         int num_leaf = 0;
00658 
00659         if( sgCheckAnimationBranch( (ssgEntity *) branch ) ) {
00660                 if( ((SGAnimation *) branch->getUserData())->get_animation_type() == 1)
00661                         if( ((SGShadowAnimation *) branch->getUserData())->get_condition_value() )
00662                                 return;
00663         }
00664 
00665         for(int i = 0 ; i < branch->getNumKids() ; i++) {
00666                 ssgEntity *this_kid = branch->getKid( i );
00667                 if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
00668                         if( filterLeaf( (ssgLeaf *) this_kid ) ) {
00669                                 num_tri += ((ssgLeaf *) this_kid)->getNumTriangles();
00670                                 num_leaf ++;
00671                         }
00672                 } else
00673                         traverseTree( (ssgBranch *) this_kid );
00674         }
00675         if( num_tri > 0) {
00676                 int tri_idx = 0;
00677                 int ind_idx = 0;
00678                 ShadowCaster *new_part = new ShadowCaster( num_tri, branch);
00679                 new_part->scenery_object = scenery_object;
00680                 new_part->lib_object = lib_object;
00681                 new_part->isTranslucent = false;
00682                 for(int i = 0 ; i < branch->getNumKids() ; i++) {
00683                         ssgEntity *this_kid = branch->getKid( i );
00684                         if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
00685                                 if( filterLeaf( (ssgLeaf *) this_kid ) )
00686                                         new_part->addLeaf( tri_idx, ind_idx, (ssgLeaf *) this_kid );
00687                         }
00688                 }
00689                 // only do that for aircraft
00690                 if( occluder_type != SGShadowVolume::occluderTypeAircraft )
00691                         new_part->isTranslucent = false;
00692                 new_part->SetConnectivity();
00693                 parts.push_back( new_part );
00694         }
00695 }
00696 
00697 void SGShadowVolume::SceneryObject::find_trans(void) {
00698         ssgBranch *branch = pending_object;
00699         // check the existence of the root node
00700         while( branch && branch->getNumParents() > 0 ) {
00701                 branch = branch->getParent(0);
00702         }
00703         // check if we are connected to the scene graph
00704         if( !branch->isA(ssgTypeRoot() ) )
00705                 return;
00706         scenery_object = pending_object;
00707 }
00708 
00709 SGShadowVolume::SceneryObject::SceneryObject(ssgBranch *_scenery_object, OccluderType _occluder_type) :
00710         scenery_object ( 0 ),
00711         pending_object ( _scenery_object ),
00712         occluder_type ( _occluder_type )
00713 {
00714         // queue objects, don't do all the work in the first frames because of
00715         // massive cpu power needed
00716         statObj++;
00717         if( occluder_type == SGShadowVolume::occluderTypeAircraft )
00718                 lib_object = _scenery_object;
00719         else
00720                 lib_object = (ssgBranch *) ((ssgBranch *)_scenery_object->getKid(0))->getKid(0);
00721 }
00722 
00723 SGShadowVolume::SceneryObject::~SceneryObject()
00724 {
00725         ShadowCaster_list::iterator iParts;
00726         for(iParts = parts.begin() ; iParts != parts.end(); iParts++ ) {
00727                 delete *iParts;
00728         }
00729         parts.clear();
00730 }
00731 
00732 void SGShadowVolume::computeShadows(void) {
00733 
00734         // intensity = ambient + lights
00735         // lights = sun_const * (sun_angle . normal)
00736         double dot_light = cos(sun_angle);
00737         // do nothing if sun is under horizon
00738         if( dot_light < 0.2 )
00739                 return;
00740 
00741         //Draw shadow volumes
00742         glPushAttrib(GL_ALL_ATTRIB_BITS);
00743         glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ;
00744         glDisableClientState ( GL_COLOR_ARRAY         ) ;
00745         glDisableClientState ( GL_NORMAL_ARRAY        ) ;
00746         glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
00747 
00748         if( use_alpha ) {
00749                 glColorMask(0, 0, 0, 1);
00750                 glClearColor(0.0, 0.0, 0.0, 0.0 );
00751                 glClear(GL_COLOR_BUFFER_BIT);
00752                 glDisable(GL_ALPHA);
00753                 glEnable(GL_BLEND);
00754         } else {
00755                 glClearStencil( 0 );
00756                 glClear(GL_STENCIL_BUFFER_BIT);
00757                 glColorMask(0, 0, 0, 0);
00758                 glEnable(GL_STENCIL_TEST);
00759                 glDisable(GL_ALPHA);
00760                 glDisable(GL_BLEND);
00761         }
00762         glDisable( GL_LIGHTING );
00763         glDisable( GL_FOG );
00764         glEnable( GL_CULL_FACE  );
00765 //      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00766     glPolygonOffset(0.0,2.0);
00767 //    glPolygonOffset(0.0,30.0);
00768     glEnable(GL_POLYGON_OFFSET_FILL);
00769 
00770         glShadeModel(GL_FLAT);
00771 
00772         glDepthMask( false );
00773         glEnable( GL_DEPTH_TEST );
00774         glDepthFunc(GL_LESS);
00775 
00776         {
00777                 float w, h;
00778                 sgFrustum frustum;
00779                 sgEnviro.getFOV( w, h );
00780                 frustum.setFOV( w, h );
00781                 frustum.setNearFar(0.1f, 5000.0f);
00782                 sgMat4 m;
00783                 ssgGetModelviewMatrix( m );
00784                 cull( ssg_root, &frustum, m, true);
00785         }
00786 
00787 
00788         glMatrixMode   ( GL_PROJECTION ) ;
00789         glPushMatrix   () ;
00790         glLoadIdentity () ;
00791         glOrtho        ( -100, 100, -100, 100, -1, 1 ) ;
00792         glMatrixMode   ( GL_MODELVIEW ) ;
00793         glPushMatrix   () ;
00794         glLoadIdentity () ;
00795 
00796         glDisable(GL_DEPTH_TEST);
00797         glDisable(GL_CULL_FACE);
00798 //      glBindTexture(GL_TEXTURE_2D, 0);
00799         glPolygonMode(GL_FRONT, GL_FILL);
00800         if( use_alpha ) {
00801                 // there is a flaw in the Roettger paper, this does not work for some geometry
00802                 // where we increment (multiply) the buffer a few times then we
00803                 // decrement (divide) a few times. Decrementing more then once will give a
00804                 // non shadowed parts with mask value < 0.25 because the incrementation was
00805                 // clamped
00806                 // Solution : don't use a start value as high as 0.25 but the smallest value
00807                 // posible ie 1/256. This is not a general solution, a stencil buffer will
00808                 // support 255 incrementation before clamping, the alpha mask only 7.
00809                 // => that still does not work with our geometry so use subtractive blend
00810 
00811                 // clamp mask = {0,16,32,64,...} => {0,16,16,16,...}
00812                 glBlendEquationPtr( GL_MIN_EXT );
00813                 glBlendFunc( GL_DST_COLOR, GL_ONE );
00814                 glColor4ub(1, 1, 1, 16);
00815                 glRectf(-100,-100,100,100);
00816                 // scale mask = {0,16} => {0,64}
00817                 glBlendEquationPtr( GL_FUNC_ADD );
00818                 glBlendFunc( GL_DST_COLOR, GL_ONE );
00819                 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
00820                 glRectf(-100,-100,100,100);
00821                 glRectf(-100,-100,100,100);
00822                 // negate mask => {0,64} => {255, 191}
00823                 glBlendFunc( GL_ONE_MINUS_DST_COLOR, GL_ZERO );
00824                 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
00825                 glRectf(-100,-100,100,100);
00826                 // apply mask
00827                 glColorMask(1, 1, 1, 1);
00828                 glBlendFunc( GL_ZERO, GL_DST_ALPHA );
00829                 glColor4f(1.0f, 0.5f, 0.2f, 1.0f);
00830                 glRectf(-100,-100,100,100);
00831         } else {
00832                 // now the stencil contains 0 for scenery in light and != 0 for parts in shadow
00833                 // draw a quad covering the screen, the stencil will be the mask
00834                 // we darken the shadowed parts
00835                 glColorMask(1, 1, 1, 1);
00836                 glStencilFunc(GL_NOTEQUAL, 0, ~0);
00837                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
00838                 glEnable(GL_STENCIL_TEST);
00839                 glEnable(GL_ALPHA);
00840                 glAlphaFunc(GL_GREATER, 0.0f);
00841                 glEnable(GL_BLEND);
00842                 glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
00843                 glColor4f(0.0, 0.0, 0.0, sgLerp(0.1, 0.3, dot_light) );
00844                 // fixed value is better, the previous line is surely wrong
00845                 glColor4f(0.0, 0.0, 0.0, 0.3 );
00846                 glRectf(-100,-100,100,100);
00847         }
00848         glMatrixMode   ( GL_PROJECTION ) ;
00849         glPopMatrix    () ;
00850         glMatrixMode   ( GL_MODELVIEW ) ;
00851         glPopMatrix    () ;
00852 
00853         glDisable(GL_STENCIL_TEST);
00854         glPopClientAttrib ( ) ;
00855         glPopAttrib();
00856 }
00857 
00858 SGShadowVolume::SGShadowVolume( ssgBranch *root ) : 
00859         shadows_enabled( false ),
00860         frameNumber( 0 ),
00861         lastTraverseTreeFrame ( 0 ),
00862         ssg_root( root ),
00863         shadows_rendered( false )
00864 {
00865         states = this;
00866 }
00867 
00868 SGShadowVolume::~SGShadowVolume() {
00869         SceneryObject_map::iterator iSceneryObject;
00870         for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); iSceneryObject++ ) {
00871                 delete iSceneryObject->second;
00872         }
00873         sceneryObjects.clear();
00874 }
00875 
00876 void SGShadowVolume::init(SGPropertyNode *sim_rendering_options) {
00877         shadows_enabled = true;
00878         sim_rendering = sim_rendering_options;
00879         GLint stencilBits = 0, alphaBits = 0;
00880         glGetIntegerv( GL_STENCIL_BITS, &stencilBits );
00881         glGetIntegerv( GL_ALPHA_BITS, &alphaBits );
00882         bool hasSubtractiveBlend = SGIsOpenGLExtensionSupported("GL_EXT_blend_subtract");
00883         bool hasMinMaxBlend = SGIsOpenGLExtensionSupported("GL_EXT_blend_minmax");
00884         if( hasSubtractiveBlend )
00885                 glBlendEquationPtr = (glBlendEquationProc ) SGLookupFunction("glBlendEquationEXT");
00886         canDoAlpha = (alphaBits >= 8) && hasSubtractiveBlend && hasMinMaxBlend;
00887         canDoStencil = (stencilBits >= 3);
00888         if( !canDoStencil )
00889                 if( canDoAlpha )
00890                         SG_LOG(SG_ALL, SG_WARN, "SGShadowVolume:no stencil buffer, using alpha buffer");
00891                 else
00892                         SG_LOG(SG_ALL, SG_WARN, "SGShadowVolume:no stencil buffer and no alpha buffer");
00893 }
00894 
00895 void SGShadowVolume::startOfFrame(void) {
00896 }
00897 void SGShadowVolume::deleteOccluderFromTile(ssgBranch *tile) {
00898         SceneryObject_map::iterator iSceneryObject;
00899         for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); ) {
00900                 SceneryObject_map::iterator iCurrent = iSceneryObject ++;
00901                 if( iCurrent->second->tile == tile ) {
00902                         delete iCurrent->second;
00903                         sceneryObjects.erase( iCurrent );
00904                 }
00905         }
00906 }
00907 
00908 void SGShadowVolume::deleteOccluder(ssgBranch *occluder) {
00909         // skip first node and go to first transform (placement)
00910         while( occluder && !occluder->isA(ssgTypeTransform()))
00911                 occluder = (ssgBranch *) occluder->getKid(0);
00912 
00913         // check if we allready know this object
00914         SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
00915         if( iSceneryObject != sceneryObjects.end() ) {
00916                 delete iSceneryObject->second;
00917                 sceneryObjects.erase( occluder );
00918         }
00919 }
00920 
00921 void SGShadowVolume::addOccluder(ssgBranch *occluder, OccluderType occluder_type, ssgBranch *tile) {
00922         // skip first node and go to first transform (placement)
00923         while( occluder && !occluder->isA(ssgTypeTransform()))
00924                 occluder = (ssgBranch *) occluder->getKid(0);
00925 
00926         // check if we allready know this object
00927         SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
00928         if( iSceneryObject == sceneryObjects.end() ) {
00929 //              printf("adding model %x %x\n", occluder, tile);
00930                 SceneryObject *entry = new SceneryObject( occluder, occluder_type );
00931                 entry->tile = tile;
00932                 sceneryObjects[ occluder ] = entry;
00933         }
00934 
00935 }
00936 
00937 void SGShadowVolume::setupShadows( double lon, double lat,
00938                 double gst, double SunRightAscension, double SunDeclination, double sunAngle) {
00939 
00940 #ifdef __APPLE__
00941         // FIXME: Find a better place for doing this.
00942         // At the time SGShadowVolume is called, OpenGL extensions and 
00943         // AlphaBits/StencilBits are not properly recognized on Macs. 
00944         // so I simply re-initialize it when called for the first time
00945         static bool re_initialized = false;
00946         if (!re_initialized) {
00947                 this->init(sim_rendering);
00948                 re_initialized = true;
00949         }
00950 #endif
00951         shadowsAC_enabled = sim_rendering->getBoolValue("shadows-ac", false);
00952         shadowsAC_transp_enabled = sim_rendering->getBoolValue("shadows-ac-transp", false);
00953         shadowsAI_enabled = sim_rendering->getBoolValue("shadows-ai", false);
00954         shadowsTO_enabled = sim_rendering->getBoolValue("shadows-to", false);
00955         shadowsDebug_enabled = sim_rendering->getBoolValue("shadows-debug", false);
00956         shadows_enabled = shadowsAC_enabled || shadowsAI_enabled || shadowsTO_enabled;
00957         shadows_enabled &= canDoAlpha || canDoStencil;
00958         use_alpha = ((!canDoStencil) || sim_rendering->getBoolValue("shadows-alpha", false)) &&
00959                 canDoAlpha;
00960 
00961         if( ! shadows_enabled )
00962                 return;
00963 
00964         shadows_rendered = false;
00965         sun_angle = sunAngle;
00966         {
00967         sgMat4 GST, RA, DEC;
00968         sgVec3 axis;
00969 
00970 
00971         sgSetVec3( axis, 0.0, 0.0, -1.0 );
00972         sgMakeRotMat4( GST, (gst) * 15.0, axis );
00973 
00974         sgSetVec3( axis, 0.0, 0.0, 1.0 );
00975         sgMakeRotMat4( RA, (SunRightAscension * SGD_RADIANS_TO_DEGREES) - 90.0, axis );
00976 
00977         sgSetVec3( axis, 1.0, 0.0, 0.0 );
00978         sgMakeRotMat4( DEC, SunDeclination * SGD_RADIANS_TO_DEGREES, axis );
00979 
00980         sgMat4 TRANSFORM;
00981         sgMakeIdentMat4( TRANSFORM );
00982         sgPreMultMat4( TRANSFORM, GST );
00983         sgPreMultMat4( TRANSFORM, RA );
00984         sgPreMultMat4( TRANSFORM, DEC );
00985         sgSetVec3( sunPos, 0.0, 9900000.0, 0.0);
00986         sgXformPnt3( sunPos, TRANSFORM );
00987         }
00988 
00989         ssgGetModelviewMatrix( CameraViewM );
00990 
00991 }
00992 
00993 void SGShadowVolume::endOfFrame(void) {
00994         if( ! shadows_enabled )
00995                 return;
00996         if( shadows_rendered )
00997                 return;
00998         glBindTexture(GL_TEXTURE_2D, 0);
00999         glBindTexture(GL_TEXTURE_1D, 0);
01000 
01001         glMatrixMode(GL_MODELVIEW);
01002         computeShadows();
01003         frameNumber ++;
01004         shadows_rendered = true;
01005 }
01006 
01007 int SGShadowVolume::ACpostTravCB( ssgEntity *entity, int traversal_mask ) {
01008         if( states->shadowsAC_transp_enabled && (SSGTRAV_CULL & traversal_mask) )
01009                 states->endOfFrame();
01010         return 0;
01011 }
01012 

Generated on Mon Dec 17 09:30:55 2007 for SimGear by  doxygen 1.5.1