SoShadowVolume.cpp

Go to the documentation of this file.
00001 
00007 /* ---------------------------------------------------------------------------
00008  * Authors:      Tomáš Burian (tburian _AT centrum.cz)            
00009  * Thanks to:    PCJohn (peciva _AT fit.vutbr.cz)
00010  * Contributors: 
00011  *
00012  * THIS SOFTWARE IS NOT COPYRIGHTED
00013  *
00014  * This source code is offered for use in the public domain.
00015  * You may use, modify or distribute it freely.
00016  *
00017  * This source code is distributed in the hope that it will be useful but
00018  * WITHOUT ANY WARRANTY.  ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
00019  * DISCLAIMED.  This includes but is not limited to warranties of
00020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00021  *
00022  * If you find the source code useful, authors will kindly welcome
00023  * if you give them credit and keep their names with their source code,
00024  * but do not feel to be forced to do so.
00025  * ---------------------------------------------------------------------------
00026  */
00027 
00028 //----------------------------------------------------------------------------
00029 // INCLUDEs
00030 //----------------------------------------------------------------------------
00031 
00032 #include "SoShadowVolume.h"
00033 
00034 //----------------------------------------------------------------------------
00035 // IMPLEMENTATION
00036 //----------------------------------------------------------------------------
00037 
00044 SoShadowVolume::SoShadowVolume(SoShadowLight *l, int cn)
00045     : shadowlight(l),
00046       casterName(cn)
00047 {
00048 
00049   transform = new SoTransform();
00050   nScTr = 0;
00051   nSVTr = 0;  
00052   aLight = shadowlight->getLight();
00053 
00054   objectModel = NULL;
00055   trMatrix = NULL;
00056   caster = NULL;
00057 
00058   if ( casterName >= 0 ) // find caster node
00059   {
00060     // caster node name
00061     SbString index(casterName);
00062     SbString name = "Caster_";
00063     name += index;    
00064 
00065     // get and set actual information from caster
00066     caster = (SoShadowCaster *)SoNode::getByName(name);
00067     ONDEBUG( printf("SV::constructor caster name (%d): %d\n", casterName, caster->getCasterName()) );
00068   }
00069 
00070   initNode = NULL;
00071   volumeNode = NULL;
00072 
00073   this->init();
00074 }
00075 
00076 
00080 SoShadowVolume::~SoShadowVolume()
00081 {
00082   this->removeAllChildren();
00083 }
00084 
00085 
00089 void SoShadowVolume::init()
00090 { 
00091   this->initNode = new SoGroup;
00092   this->addChild(initNode);
00093   this->volumeNode = new SoGroup;      
00094   this->addChild(volumeNode);
00095 
00096   // transformation used for directional light
00097   // has no sense in other cases 
00098   transform->translation.setValue(0.0f, 0.0f, 0.0f);
00099   transform->rotation.setValue(0.0f, 0.0f, 0.0f, 1.0f);
00100   transform->scaleFactor.setValue(1.0f, 1.0f, 1.0f);
00101   transform->scaleOrientation.setValue(0.0f, 0.0f, 0.0f, 1.0f);
00102   transform->center.setValue(0.0f, 0.0f, 0.0f);
00103   this->initNode->addChild(transform);
00104 
00105   // shadow volumes material
00106   SoMaterial *material = new SoMaterial;
00107   material->ambientColor.setValue(svColor);
00108   material->diffuseColor.setValue(svColor);
00109 //  if ( ! settings.dropShadows )    material->transparency.setValue(0.1f);
00110   this->initNode->addChild(material);
00111 
00112   // shadow shape hints
00113   SoShapeHints * hints = new SoShapeHints;
00114   hints->shapeType = SoShapeHintsElement::SOLID;
00115   hints->vertexOrdering = SoShapeHintsElement::COUNTERCLOCKWISE;
00116   this->initNode->addChild(hints);
00117 
00118   // normal binding
00119   SoNormalBinding *nbind = new SoNormalBinding;
00120   nbind->value.setValue(SoNormalBinding::PER_FACE_INDEXED);
00121   this->initNode->addChild(nbind);
00122 }
00123 
00124 
00130 int SoShadowVolume::getCasterName()
00131 {
00132   return this->casterName;
00133 }
00134 
00135 
00139 void SoShadowVolume::create()
00140 {
00141   ONDEBUG( printf("+++SoShadowVolume::create()\n") );
00142 
00143   if ( aLight != NULL ) 
00144   {         
00145     // check light status
00146     SbBool lightON = TRUE;  
00147     aLight->on.enableNotify(FALSE);  
00148     lightON = this->aLight->on.getValue();
00149     aLight->on.enableNotify(TRUE);
00150 
00151     ONDEBUG(
00152       if ( lightON )
00153        printf("+++SoShadowVolume::light is ON\n");
00154       else
00155         printf("+++SoShadowVolume::light is OFF\n");
00156     );
00157 
00158     // optimalisation for directional light
00159     // no need to recompute shadow volumes when object has moved
00160     // just compute translation vector and add it to the SV's transformation node
00161     ONDEBUG( printf("caster name: %d\n",casterName) );
00162     SbVec3f translation(0.0f, 0.0f, 0.0f);
00163     bool dirOpt = FALSE; // can be directional light optimalisation used?
00164 
00165     if ( caster != NULL ) // find parent caster node, if object changes
00166     {
00167       trMatrix = caster->getMatrix();
00168       objectModel = caster->getObjectModel();
00169 
00170       if ( caster->moved() )
00171       {
00172         ONDEBUG( printf("CASTER MOVEMENT: \n") );
00173         if (   aLight->getTypeId() == SoDirectionalLight::getClassTypeId() 
00174             || aLight->getTypeId() == SoDirectionalLightManip::getClassTypeId()
00175            )
00176         {
00177           ONDEBUG( printf("dir. light optimalisation \n") );
00178           SbVec3f pointOld(1.0f, 1.0f, 1.0f);
00179           SbVec3f pointNew(1.0f, 1.0f, 1.0f);
00180           caster->trMatrixOld.multVecMatrix(pointOld, pointOld);
00181           caster->trMatrix.multVecMatrix(pointNew, pointNew);
00182           translation = pointNew - pointOld;
00183           dirOpt = TRUE;
00184         }      
00185         caster->setMoved(FALSE);
00186       }
00187     }
00188   
00189 
00190     if ( !dirOpt )
00191     {
00192       // delete old shadow volume's coordinates etc.; clear the volume node      
00193       ONDEBUG( 
00194         int x = this->volumeNode->getNumChildren();
00195         printf("volume had %d children\n",x);
00196       );
00197 
00198       // delete all shadow volumes under this node
00199       this->volumeNode->removeAllChildren();                     
00200 
00201       nScTr = 0;
00202       nSVTr = 0;  
00203 
00204       transform->translation.setValue(0.0f, 0.0f, 0.0f);
00205     }
00206 
00207     else
00208     {
00209       // add the translation
00210       transform->translation.setValue(transform->translation.getValue() + translation);
00211     }
00212 
00213     if ( lightON && !dirOpt ) // re-compute shadow volume only if the light is turned ON
00214                               // and it is not a directional light (no optimalisation)
00215     {
00216       //this->init();
00217 
00218       // determine algorithm
00219       this->shadowlight->getManager()->method2algorithm();
00220  
00221       int itn = 0; // index to normal indices array
00222       int j = 2; // actual triangle in the strip set, vertices: j-2, j-1 and j
00223       int itmat = 0;
00224       float vysl = 0.0f;
00225 
00226       SbVec3f normal;
00227       // Debug: Check if we are in the range
00228       int b = this->objectModel->modelIfs->normalIndex.getNum();
00229 
00230       // Create a shadow volume for each light-facing triangle
00231       // Goes over all triangles in the triangleStripSet
00232       while ( j < objectModel->modelIfs->coordIndex.getNum() ) {
00233 
00234         while ( objectModel->modelIfs->coordIndex[j] != -1 ) {
00235 
00236           if ( b < itn ) {
00237             ONDEBUG( fprintf(stderr,"Normal index out of range.\n\n") );
00238             exit(1);
00239           }
00240 
00241           // Get the normal vector
00242           int nindex = objectModel->modelIfs->normalIndex[itn];
00243           normal = objectModel->modelNormal->vector[nindex];            
00244 
00245           // Transformation of a vertex and normal vector to global coordinates
00246           SbVec3f x(objectModel->modelCoord3->point[objectModel->modelIfs->coordIndex[j-2]]);
00247           SbVec3f normEnd(x+1*normal);  // endpoint of the normal vector
00248 
00249           // Vertex
00250           trMatrix->multVecMatrix(x, x);
00251           // New normal vector
00252           trMatrix->multVecMatrix(normEnd, normEnd);
00253           normal = normEnd - x;
00254 
00255           if ( normal.length() > .0f ) {
00256             normal.normalize();
00257           } else {
00258             ONDEBUG(printf("Error: Length of the normal is 0.\n"));
00259           }
00260 
00261           // lights: 
00262           // according the light type compute light location and light direction vector
00263           SbVec3f lightLocation;
00264           SbVec3f lightDirection;
00265           enum TLightTypes lightType;
00266           char * typ = "";
00267       
00268           if (aLight->getTypeId() == SoDirectionalLight::getClassTypeId() )
00269           {
00270             lightType = TSoDirectionalLight;
00271             typ = "TSoDirectionalLight";
00272             SoDirectionalLight * light = (SoDirectionalLight *)aLight;
00273             lightDirection = light->direction.getValue();
00274           }
00275           else if (aLight->getTypeId() == SoDirectionalLightManip::getClassTypeId() )
00276           {
00277             lightType = TSoDirectionalLightManip;
00278             typ = "TSoDirectionalLightManip";
00279             SoDirectionalLightManip * light = (SoDirectionalLightManip *)aLight;
00280             lightDirection = light->direction.getValue();
00281           }
00282           else if (aLight->getTypeId() == SoPointLight::getClassTypeId() )
00283           {
00284             lightType = TSoPointLight;
00285             typ = "TSoPointLight";
00286             SoPointLight * light = (SoPointLight *)aLight;
00287             lightLocation = light->location.getValue();
00288           }
00289           else if (aLight->getTypeId() == SoPointLightManip::getClassTypeId() )
00290           {
00291             lightType = TSoPointLightManip;
00292             typ = "TSoPointLightManip";
00293             SoPointLightManip * light = (SoPointLightManip *)(aLight);
00294             lightLocation = light->location.getValue();
00295           }
00296           else if (aLight->getTypeId() == SoSpotLight::getClassTypeId() )
00297           {
00298             lightType = TSoSpotLight;
00299             typ = "TSoSpotLight";
00300             SoSpotLight * light = (SoSpotLight *)(aLight);
00301             lightLocation = light->location.getValue();
00302           }
00303           else if (aLight->getTypeId() == SoSpotLightManip::getClassTypeId() )
00304           {
00305             lightType = TSoSpotLightManip;
00306             typ = "TSoSpotLightManip";
00307             SoSpotLightManip * light = (SoSpotLightManip *)aLight;
00308             lightLocation = light->location.getValue();
00309           }
00310           else 
00311           {
00312             lightType = TUnknownLightType;
00313             typ = "TUnknownLightType";
00314           }
00315 
00316           // light direction vector. it's a vector from the vertex 
00317           // to light source (point and spot light) or light direction
00318           // (directional light)
00319           SbVec3f lv;
00320           if (lightType == TSoDirectionalLight)
00321           {
00322             lv = lightDirection;
00323           }
00324           else if (lightType == TSoDirectionalLightManip)
00325           {                
00326             lv = lightDirection;
00327           }
00328           else if (lightType == TSoPointLight)
00329           {
00330             lv = (lightLocation-x);
00331           }
00332           else if (lightType == TSoPointLightManip)
00333           {                
00334             lv = (lightLocation-x);
00335           }
00336           else if (lightType == TSoSpotLight)
00337           {                
00338             lv = (lightLocation-x);
00339           }
00340           else if (lightType == TSoSpotLightManip)
00341           {
00342             lv = (lightLocation-x);
00343           }          
00344 
00345           // Dot product of normal and light direction vector
00346           // to determine if the triangle is light facing or not
00347           vysl = normal.dot(lv);
00348           if ( vysl <= 0.0f )
00349           {
00350             // Not facing light
00351             int n = objectModel->modelIfs->materialIndex.getNum();
00352             objectModel->modelIfs->materialIndex.set1Value(n,0);
00353             objectModel->modelIfs->materialIndex.setNum(n+1);
00354           } 
00355           else 
00356           {
00357             // Is light-facing, part of the silhouette
00358             this->nScTr++;
00359             int n = objectModel->modelIfs->materialIndex.getNum();
00360             objectModel->modelIfs->materialIndex.set1Value(n,1);
00361             objectModel->modelIfs->materialIndex.setNum(n+1);
00362 
00363             // Create envelope of the shadow volume
00364 
00365             SoCoordinate3 *envCoords = new SoCoordinate3;
00366             SoIndexedTriangleStripSet * envStrip = new SoIndexedTriangleStripSet;
00367             // Note: normals are computed automatically by Coin according to the 
00368             // SoNormalBinding::PER_FACE_INDEXED from the coordinates
00369 
00370             envCoords->point.setNum(2*3+2); // 8 = 2 * #edges + 2 (closed)
00371             this->nSVTr += 6; // #tr of SV envelope
00372 
00373             SbVec3f *c = envCoords->point.startEditing();
00374 
00375             int k = 0;
00376 
00377             // compute verteces of the shadow volume
00378             for ( k=3; k >= 0; k-- )
00379             {
00380               // To global coordinates
00381               int t = j-k%3;
00382               SbVec3f xx(objectModel->modelCoord3->point[objectModel->modelIfs->coordIndex[t]]);
00383               trMatrix->multVecMatrix(xx, xx);
00384 
00385               SbVec3f l2pVec; // light dir. vector, depends on light type
00386               if (lightType != TSoDirectionalLight && lightType != TSoDirectionalLightManip)
00387               {
00388                 l2pVec = xx - lightLocation;
00389               }
00390               else
00391               {
00392                 l2pVec = lightDirection;
00393               }
00394          
00395               // point of the far cap, comp. by using light direction vector and a triangle vertex
00396               SbVec3f xxFar(farPointNew(l2pVec, xx, this->shadowlight->getManager()->readSettings()->fDepth));
00397               *(c++) = xx;
00398               *(c++) = xxFar;  // Far point
00399             }
00400             envCoords->point.finishEditing();
00401 
00402             // Add nodes to scene graph
00403             this->volumeNode->addChild(envCoords);
00404         
00405             // puvodni SoIndexedTriangleStripSet * envStrip = new SoIndexedTriangleStripSet;
00406             int32_t indeces[24] = {0,1,2,-1,2,1,3,-1, 2,3,4,-1,4,3,5,-1, 4,5,6,-1,6,5,7,-1};
00407             // for directional light indices must be exact contrariwise than for point & spot light
00408             // do not know why
00409             int32_t indecesDir[24] = {1,0,2,-1, 1,2,3,-1, 2,4,3,-1, 3,4,5,-1, 4,6,5,-1, 6,7,5,-1};
00410             if (lightType == TSoDirectionalLight || lightType == TSoDirectionalLightManip)
00411               envStrip->coordIndex.setValues(0,24,indecesDir);
00412             else
00413               envStrip->coordIndex.setValues(0,24,indeces);
00414             this->volumeNode->addChild(envStrip);
00415 
00416             // TODO: determine algorithm
00417             // this->getManager()->method2algorithm();
00418 
00419             // cap volumes only if zfail used
00420             if ( *this->shadowlight->getManager()->getAlgorithm() == zfailAlg )
00421             // if ( this->getManager()->getSettings()->method == zfail )
00422             {     
00423               // Near & far cap of the volume
00424               SoIndexedTriangleStripSet * ncapStrip = new SoIndexedTriangleStripSet;
00425               int32_t ncapIndeces[4] = {0,2,4,-1};
00426               // dir. light
00427               int32_t ncapIndecesDir[4] = {0,4,2,-1};
00428               if (lightType == TSoDirectionalLight || lightType == TSoDirectionalLightManip)
00429                 ncapStrip->coordIndex.setValues(0,4,ncapIndecesDir);
00430               else
00431                 ncapStrip->coordIndex.setValues(0,4,ncapIndeces);
00432               this->volumeNode->addChild(ncapStrip);
00433 
00434               SoIndexedTriangleStripSet * fcapStrip = new SoIndexedTriangleStripSet;
00435               int32_t fcapIndeces[4] = {1,5,3,-1}; // 1,3,5
00436               // dir. light
00437               int32_t fcapIndecesDir[4] = {1,3,5,-1};
00438               if (lightType == TSoDirectionalLight || lightType == TSoDirectionalLightManip)
00439                 fcapStrip->coordIndex.setValues(0,4,fcapIndecesDir);
00440               else
00441                 fcapStrip->coordIndex.setValues(0,4,fcapIndeces);
00442               this->volumeNode->addChild(fcapStrip);
00443 
00444               //
00445               this->nSVTr += 2; // #tr of SV near and far cap
00446             }
00447 
00448 
00449           }
00450           // End of a strip, move iterators
00451           itn++;
00452           j++;
00453 
00454         } // while
00455 
00456         j += 3;  // end of strip (-1) => move to next one
00457 
00458       } // while coords
00459       ONDEBUG( printf("Shadow volumes of the object created. They consists of %d shadow casting tr.\n", this->nScTr) );
00460     } // end if light is ON
00461 
00462   } // aLight not NULL
00463   else
00464   {
00465     printf("light is NULL\n");
00466   }
00467 }
00468 
00469 
00475 void SoShadowVolume::writeShadowVolume(const char *filename)
00476 {
00477   SoOutput output;
00478 
00479   if (this->getChildren()->getLength() != 0) {
00480     output.openFile(filename);
00481     SoWriteAction * writer = new SoWriteAction(&output);
00482     writer->apply(this);
00483     delete writer;
00484     ONDEBUG( fprintf(stdout,"Shadows' scene graph was written in '%s'.\n\n", filename) );
00485   } else {
00486     ONDEBUG( fprintf(stderr,"Error: Could not write shadows' scene graph.\nShadow volumes have not been generated yet.\n\n") );
00487   }
00488 }
00489 
00490 
00499 /*
00500 SbVec3f SoShadowVolume::farPoint(SbVec3f lightPos, SbVec3f point)
00501 {
00502   SbVec3f farPoint(point);
00503   SbVec3f l2pVec(point-lightPos);
00504   l2pVec.normalize();
00505 
00506   float farDepth2 = 150.f;
00507 
00508   farPoint = point + (farDepth2 * l2pVec);
00509 
00510   return farPoint;
00511 }
00512 */
00513 
00514 
00526 SbVec3f SoShadowVolume::farPointNew(SbVec3f lightVec, SbVec3f point, float farDepth)
00527 {
00528   SbVec3f farPoint(point);
00529   lightVec.normalize();
00530   farPoint = point + farDepth * lightVec;
00531 
00532   return farPoint;
00533 }
00534 
00535 
00547 SbVec3f SoShadowVolume::faceNormal(const SbVec3f p1, const SbVec3f p2, const SbVec3f p3)
00548 {
00549   SbVec3f vector1, vector2;
00550 
00551   vector1 = p2 - p1;
00552   vector2 = p3 - p2;
00553 
00554   if ( vector1.length() <= .0f ) {
00555     ONDEBUG(printf("Error: Length of a vector1 is 0. Correcting...\n"));
00556   }
00557   if ( vector2.length() <= .0f ) {
00558     ONDEBUG(printf("Error: Length of a vector2 is 0. Correcting...\n"));
00559   }
00560 
00561   SbVec3f normal = vector1.cross(vector2);
00562   if ( normal.length() > .0f ) {
00563     normal.normalize();
00564   } else {
00565     ONDEBUG(printf("Error: FaceNormal: Length of the normal is 0.\n"));
00566   }
00567 
00568   return normal;
00569 }

Generated on Wed May 17 17:27:52 2006 for Shadow Engine by  doxygen 1.4.6-NO