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   transform->ref();
00051   nScTr = 0;
00052   nSVTr = 0;  
00053   aLight = shadowlight->getLight();
00054 
00055   objectModel = NULL;
00056   trMatrix = NULL;
00057   caster = NULL;
00058 
00059   if ( casterName >= 0 ) // find caster node
00060   {
00061     // caster node name
00062     SbString index(casterName);
00063     SbString name = "Caster_";
00064     name += index;    
00065 
00066     // get and set actual information from caster
00067     caster = (SoShadowCaster *)SoNode::getByName(name);
00068     ONDEBUG( printf("SV::constructor caster name (%d): %d\n", casterName, caster->getCasterName()) );
00069   }
00070 
00071 }
00072 
00073 
00077 SoShadowVolume::~SoShadowVolume()
00078 {
00079   transform->unref();
00080 }
00081 
00082 
00086 void SoShadowVolume::init()
00087 { 
00088   // transformation used for directional light
00089   // has no sense in other cases 
00090   transform->translation.setValue(0.0f, 0.0f, 0.0f);
00091   transform->rotation.setValue(0.0f, 0.0f, 0.0f, 1.0f);
00092   transform->scaleFactor.setValue(1.0f, 1.0f, 1.0f);
00093   transform->scaleOrientation.setValue(0.0f, 0.0f, 0.0f, 1.0f);
00094   transform->center.setValue(0.0f, 0.0f, 0.0f);
00095   this->addChild(transform);
00096 
00097   // shadow volumes material
00098   SoMaterial *material = new SoMaterial;
00099   material->ambientColor.setValue(svColor);
00100   material->diffuseColor.setValue(svColor);
00101 //  if ( ! settings.dropShadows )    material->transparency.setValue(0.1f);
00102   this->addChild(material);
00103 
00104   // shadow shape hints
00105   SoShapeHints * hints = new SoShapeHints;
00106   hints->shapeType = SoShapeHintsElement::SOLID;
00107   hints->vertexOrdering = SoShapeHintsElement::COUNTERCLOCKWISE;
00108   this->addChild(hints);
00109 
00110   // normal binding
00111   SoNormalBinding *nbind = new SoNormalBinding;
00112   nbind->value.setValue(SoNormalBinding::PER_FACE_INDEXED);
00113   this->addChild(nbind);
00114 }
00115 
00116 
00122 int SoShadowVolume::getCasterName()
00123 {
00124   return this->casterName;
00125 }
00126 
00127 
00131 void SoShadowVolume::create()
00132 {
00133   ONDEBUG( printf("+++SoShadowVolume::create()\n") );
00134 
00135   if ( aLight != NULL ) 
00136   {         
00137     // check light status
00138     SbBool lightON = TRUE;  
00139     aLight->on.enableNotify(FALSE);  
00140     lightON = this->aLight->on.getValue();
00141     aLight->on.enableNotify(TRUE);
00142 
00143     ONDEBUG(
00144       if ( lightON )
00145        printf("+++SoShadowVolume::light is ON\n");
00146       else
00147         printf("+++SoShadowVolume::light is OFF\n");
00148     );
00149 
00150     // optimalisation for directional light
00151     // no need to recompute shadow volumes when object has moved
00152     // just compute translation vector and add it to the SV's transformation node
00153     ONDEBUG( printf("caster name: %d\n",casterName) );
00154     SbVec3f translation(0.0f, 0.0f, 0.0f);
00155     bool dirOpt = FALSE; // can be directional light optimalisation used?
00156 
00157     if ( caster != NULL ) // find parent caster node, if object changes
00158     {
00159       trMatrix = caster->getMatrix();
00160       objectModel = caster->getObjectModel();
00161 
00162       if ( caster->moved() )
00163       {
00164         ONDEBUG( printf("CASTER MOVEMENT: \n") );
00165         if (   aLight->getTypeId() == SoDirectionalLight::getClassTypeId() 
00166             || aLight->getTypeId() == SoDirectionalLightManip::getClassTypeId()
00167            )
00168         {
00169           ONDEBUG( printf("dir. light optimalisation \n") );
00170           SbVec3f pointOld(1.0f, 1.0f, 1.0f);
00171           SbVec3f pointNew(1.0f, 1.0f, 1.0f);
00172           caster->trMatrixOld.multVecMatrix(pointOld, pointOld);
00173           caster->trMatrix.multVecMatrix(pointNew, pointNew);
00174           translation = pointNew - pointOld;
00175           dirOpt = TRUE;
00176         }      
00177         caster->setMoved(FALSE);
00178       }
00179     }
00180   
00181 
00182     if ( !dirOpt )
00183     {
00184       // delete old shadow volume's coordinates etc.; clear the volume node
00185       int x = this->getNumChildren();
00186       ONDEBUG( printf("volume had %d children\n",x) );
00187       this->removeAllChildren();      
00188       nScTr = 0;
00189       nSVTr = 0;  
00190     }
00191     else
00192     {
00193       // add the translation
00194       transform->translation.setValue(transform->translation.getValue() + translation);
00195     }
00196 
00197 
00198     if ( lightON && !dirOpt ) // re-compute shadow volume only if the light is turned ON
00199                               // and it is not a directional light (no optimalisation)
00200     {
00201       this->init();
00202 
00203       // determine algorithm
00204       this->shadowlight->getManager()->method2algorithm();
00205  
00206       int itn = 0; // index to normal indices array
00207       int j = 2; // actual triangle in the strip set, vertices: j-2, j-1 and j
00208       int itmat = 0;
00209       float vysl = 0.0f;
00210 
00211       SbVec3f normal;
00212       // Debug: Check if we are in the range
00213       int b = this->objectModel->modelIfs->normalIndex.getNum();
00214 //      this->objectModel->modelIfs->materialIndex.setNum(0); // ??? co to je?
00215 
00216       // Create a shadow volume for each light-facing triangle
00217       // Goes over all triangles in the triangleStripSet
00218       while ( j < objectModel->modelIfs->coordIndex.getNum() ) {
00219 
00220         while ( objectModel->modelIfs->coordIndex[j] != -1 ) {
00221 
00222           if ( b < itn ) {
00223             ONDEBUG( fprintf(stderr,"Normal index out of range.\n\n") );
00224             exit(1);
00225           }
00226 
00227           // Get the normal vector
00228           int nindex = objectModel->modelIfs->normalIndex[itn];
00229           normal = objectModel->modelNormal->vector[nindex];            
00230 
00231           // Transformation of a vertex and normal vector to global coordinates
00232           SbVec3f x(objectModel->modelCoord3->point[objectModel->modelIfs->coordIndex[j-2]]);
00233     //            printf("X: (%f %f %f) \n",x[0],x[1],x[2]);
00234           SbVec3f normEnd(x+1*normal);  // endpoint of the normal vector
00235 
00236           // Vertex
00237           trMatrix->multVecMatrix(x, x);
00238           // New normal vector
00239           trMatrix->multVecMatrix(normEnd, normEnd);
00240           normal = normEnd - x;
00241 
00242           if ( normal.length() > .0f ) {
00243             normal.normalize();
00244           } else {
00245             ONDEBUG(printf("Error: Length of the normal is 0.\n"));
00246           }
00247 
00248           // lights: 
00249           // according the light type compute light location and light direction vector
00250           SbVec3f lightLocation;
00251           SbVec3f lightDirection;
00252           enum TLightTypes lightType;
00253           char * typ = "";
00254       
00255           if (aLight->getTypeId() == SoDirectionalLight::getClassTypeId() )
00256           {
00257             lightType = TSoDirectionalLight;
00258             typ = "TSoDirectionalLight";
00259             SoDirectionalLight * light = (SoDirectionalLight *)aLight;
00260             lightDirection = light->direction.getValue();
00261           }
00262           else if (aLight->getTypeId() == SoDirectionalLightManip::getClassTypeId() )
00263           {
00264             lightType = TSoDirectionalLightManip;
00265             typ = "TSoDirectionalLightManip";
00266             SoDirectionalLightManip * light = (SoDirectionalLightManip *)aLight;
00267             lightDirection = light->direction.getValue();
00268           }
00269           else if (aLight->getTypeId() == SoPointLight::getClassTypeId() )
00270           {
00271             lightType = TSoPointLight;
00272             typ = "TSoPointLight";
00273             SoPointLight * light = (SoPointLight *)aLight;
00274             lightLocation = light->location.getValue();
00275           }
00276           else if (aLight->getTypeId() == SoPointLightManip::getClassTypeId() )
00277           {
00278             lightType = TSoPointLightManip;
00279             typ = "TSoPointLightManip";
00280             SoPointLightManip * light = (SoPointLightManip *)(aLight);
00281             lightLocation = light->location.getValue();
00282           }
00283           else if (aLight->getTypeId() == SoSpotLight::getClassTypeId() )
00284           {
00285             lightType = TSoSpotLight;
00286             typ = "TSoSpotLight";
00287             SoSpotLight * light = (SoSpotLight *)(aLight);
00288             lightLocation = light->location.getValue();
00289           }
00290           else if (aLight->getTypeId() == SoSpotLightManip::getClassTypeId() )
00291           {
00292             lightType = TSoSpotLightManip;
00293             typ = "TSoSpotLightManip";
00294             SoSpotLightManip * light = (SoSpotLightManip *)aLight;
00295             lightLocation = light->location.getValue();
00296           }
00297           else 
00298           {
00299             lightType = TUnknownLightType;
00300             typ = "TUnknownLightType";
00301           }
00302 
00303           // light direction vector. it's a vector from the vertex 
00304           // to light source (point and spot light) or light direction
00305           // (directional light)
00306           SbVec3f lv;
00307           if (lightType == TSoDirectionalLight)
00308           {
00309             lv = lightDirection;
00310           }
00311           else if (lightType == TSoDirectionalLightManip)
00312           {                
00313             lv = lightDirection;
00314           }
00315           else if (lightType == TSoPointLight)
00316           {
00317             lv = (lightLocation-x);
00318           }
00319           else if (lightType == TSoPointLightManip)
00320           {                
00321             lv = (lightLocation-x);
00322           }
00323           else if (lightType == TSoSpotLight)
00324           {                
00325             lv = (lightLocation-x);
00326           }
00327           else if (lightType == TSoSpotLightManip)
00328           {
00329             lv = (lightLocation-x);
00330           }          
00331 
00332           // Dot product of normal and light direction vector
00333           // to determine if the triangle is light facing or not
00334           vysl = normal.dot(lv);
00335     //            printf("SV: vysl = %f:\n", vysl); 
00336           if ( vysl <= 0.0f ) {
00337             // Not facing light
00338             int n = objectModel->modelIfs->materialIndex.getNum();
00339             objectModel->modelIfs->materialIndex.set1Value(n,0);
00340             objectModel->modelIfs->materialIndex.setNum(n+1);
00341     //              printf("SV NOT facing\n");
00342           } else {
00343             // Is light-facing, part of the silhouette
00344             this->nScTr++;
00345     //              printf("SV facing: %d\n",this->nScTr++);
00346 
00347             int n = objectModel->modelIfs->materialIndex.getNum();
00348             objectModel->modelIfs->materialIndex.set1Value(n,1);
00349             objectModel->modelIfs->materialIndex.setNum(n+1);
00350 
00351             // Create envelope of the shadow volume
00352 
00353             // Coords
00354             SoCoordinate3 *envCoords = new SoCoordinate3;
00355             SoNormal * envNormals = new SoNormal;
00356 
00357             envCoords->point.setNum(2*3+2); // 8 = 2 * #edges + 2 (closed)
00358             envNormals->vector.setNum(2*3+2);
00359             this->nSVTr += 6; // #tr of SV envelope
00360 
00361             SbVec3f *c = envCoords->point.startEditing();
00362 
00363             int k = 0;
00364 
00365             // compute verteces of the shadow volume
00366             for ( k=3; k >= 0; k-- )
00367             {
00368               // To global coordinates
00369               int t = j-k%3;
00370               SbVec3f xx(objectModel->modelCoord3->point[objectModel->modelIfs->coordIndex[t]]);
00371               trMatrix->multVecMatrix(xx, xx);
00372 
00373               SbVec3f l2pVec; // light dir. vector, depends on light type
00374               if (lightType != TSoDirectionalLight && lightType != TSoDirectionalLightManip)
00375               {
00376                 l2pVec = xx - lightLocation;
00377               }
00378               else
00379               {
00380                 l2pVec = lightDirection;
00381               }
00382          
00383               // point of the far cap, comp. by using light direction vector and a triangle vertex
00384               SbVec3f xxFar(farPointNew(l2pVec, xx, this->shadowlight->getManager()->readSettings()->fDepth));
00385               *(c++) = xx;
00386               *(c++) = xxFar;  // Far point
00387             }
00388             envCoords->point.finishEditing();
00389 
00390             // Add nodes to scene graph
00391             this->addChild(envCoords);
00392         
00393             SoIndexedTriangleStripSet * envStrip = new SoIndexedTriangleStripSet;
00394             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};
00395             // for directional light indices must be exact contrariwise than for point & spot light
00396             // do not know why
00397             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};
00398             if (lightType == TSoDirectionalLight || lightType == TSoDirectionalLightManip)
00399               envStrip->coordIndex.setValues(0,24,indecesDir);
00400             else
00401               envStrip->coordIndex.setValues(0,24,indeces);
00402             this->addChild(envStrip);
00403 
00404             // add envelope normals
00405             SbVec3f env1Normal;
00406 
00407             env1Normal = this->faceNormal(envCoords->point[0], envCoords->point[1], envCoords->point[2]);
00408             envNormals->vector.set1Value(0, env1Normal);
00409             envStrip->normalIndex.set1Value(0,0);
00410             //
00411             env1Normal = this->faceNormal(envCoords->point[2], envCoords->point[1], envCoords->point[3]);
00412             envNormals->vector.set1Value(1, env1Normal);
00413             envStrip->normalIndex.set1Value(1,1);
00414             //
00415             env1Normal = this->faceNormal(envCoords->point[2], envCoords->point[3], envCoords->point[4]);
00416             envNormals->vector.set1Value(2, env1Normal);
00417             envStrip->normalIndex.set1Value(2,2);
00418             //
00419             env1Normal = this->faceNormal(envCoords->point[4], envCoords->point[3], envCoords->point[5]);
00420             envNormals->vector.set1Value(3, env1Normal);
00421             envStrip->normalIndex.set1Value(3,3);
00422             //
00423             env1Normal = this->faceNormal(envCoords->point[4], envCoords->point[5], envCoords->point[6]);
00424             envNormals->vector.set1Value(4, env1Normal);
00425             envStrip->normalIndex.set1Value(4,4);
00426             //
00427             env1Normal = this->faceNormal(envCoords->point[6], envCoords->point[5], envCoords->point[7]);
00428             envNormals->vector.set1Value(5, env1Normal);
00429             envStrip->normalIndex.set1Value(5,5);
00430 
00431             // TODO: determine algorithm
00432   //          this->getManager()->method2algorithm();
00433 
00434             // cap volumes only if zfail used
00435             if ( *this->shadowlight->getManager()->getAlgorithm() == zfailAlg )
00436             // if ( this->getManager()->getSettings()->method == zfail )
00437             {     
00438               // Near & far cap of the volume
00439               SoIndexedTriangleStripSet * ncapStrip = new SoIndexedTriangleStripSet;
00440               int32_t ncapIndeces[4] = {0,2,4,-1};
00441               // dir. light
00442               int32_t ncapIndecesDir[4] = {0,4,2,-1};
00443               if (lightType == TSoDirectionalLight || lightType == TSoDirectionalLightManip)
00444                 ncapStrip->coordIndex.setValues(0,4,ncapIndecesDir);
00445               else
00446                 ncapStrip->coordIndex.setValues(0,4,ncapIndeces);
00447               this->addChild(ncapStrip);
00448 
00449               // normal up
00450               env1Normal = this->faceNormal(envCoords->point[0], envCoords->point[2], envCoords->point[4]);
00451               envNormals->vector.set1Value(6, env1Normal);
00452               ncapStrip->normalIndex.set1Value(6,6);
00453 
00454               SoIndexedTriangleStripSet * fcapStrip = new SoIndexedTriangleStripSet;
00455               int32_t fcapIndeces[4] = {1,5,3,-1}; // 1,3,5
00456               // dir. light
00457               int32_t fcapIndecesDir[4] = {1,3,5,-1};
00458               if (lightType == TSoDirectionalLight || lightType == TSoDirectionalLightManip)
00459                 fcapStrip->coordIndex.setValues(0,4,fcapIndecesDir);
00460               else
00461                 fcapStrip->coordIndex.setValues(0,4,fcapIndeces);
00462               this->addChild(fcapStrip);
00463 
00464               // normal down
00465               env1Normal = this->faceNormal(envCoords->point[1], envCoords->point[5], envCoords->point[3]);
00466               envNormals->vector.set1Value(7, env1Normal);
00467               fcapStrip->normalIndex.set1Value(7,7);
00468               //
00469               this->nSVTr += 2; // #tr of SV near and far cap
00470             }
00471           }
00472 
00473           // End of a strip, move iterators
00474           itn++;
00475           j++;
00476 
00477         } // while
00478 
00479         j += 3;  // end of strip (-1) => move to next one
00480 
00481       } // while coords
00482       ONDEBUG( printf("Shadow volumes of the object created. They consists of %d shadow casting tr.\n", this->nScTr) );
00483     } // end if light is ON
00484 
00485   } // aLight not NULL
00486   else
00487   {
00488     printf("light is NULL\n");
00489   }
00490 }
00491 
00492 
00498 void SoShadowVolume::writeShadowVolume(const char *filename)
00499 {
00500   SoOutput output;
00501 
00502   if (this->getChildren()->getLength() != 0) {
00503     output.openFile(filename);
00504     SoWriteAction * writer = new SoWriteAction(&output);
00505     writer->apply(this);
00506     delete writer;
00507     ONDEBUG( fprintf(stdout,"Shadows' scene graph was written in '%s'.\n\n", filename) );
00508   } else {
00509     ONDEBUG( fprintf(stderr,"Error: Could not write shadows' scene graph.\nShadow volumes have not been generated yet.\n\n") );
00510   }
00511 }
00512 
00513 
00522 /*
00523 SbVec3f SoShadowVolume::farPoint(SbVec3f lightPos, SbVec3f point)
00524 {
00525   SbVec3f farPoint(point);
00526   SbVec3f l2pVec(point-lightPos);
00527   l2pVec.normalize();
00528 
00529   float farDepth2 = 150.f;
00530 
00531   farPoint = point + (farDepth2 * l2pVec);
00532 
00533   return farPoint;
00534 }
00535 */
00536 
00537 
00549 SbVec3f SoShadowVolume::farPointNew(SbVec3f lightVec, SbVec3f point, float farDepth)
00550 {
00551   SbVec3f farPoint(point);
00552   lightVec.normalize();
00553   farPoint = point + farDepth * lightVec;
00554 
00555   return farPoint;
00556 }
00557 
00558 
00570 SbVec3f SoShadowVolume::faceNormal(const SbVec3f p1, const SbVec3f p2, const SbVec3f p3)
00571 {
00572   SbVec3f vector1, vector2;
00573 
00574   vector1 = p2 - p1;
00575   vector2 = p3 - p2;
00576 
00577   if ( vector1.length() <= .0f ) {
00578     ONDEBUG(printf("Error: Length of a vector1 is 0. Correcting...\n"));
00579   }
00580   if ( vector2.length() <= .0f ) {
00581     ONDEBUG(printf("Error: Length of a vector2 is 0. Correcting...\n"));
00582   }
00583 
00584   SbVec3f normal = vector1.cross(vector2);
00585   if ( normal.length() > .0f ) {
00586     normal.normalize();
00587   } else {
00588     ONDEBUG(printf("Error: FaceNormal: Length of the normal is 0.\n"));
00589   }
00590 
00591   return normal;
00592 }

Generated on Wed May 17 08:24:45 2006 for Shadow Engine by  doxygen 1.4.6-NO