//------------------------------------------------------------------------------
void ShadowVolumeBSP::buildPolyVolume(SVPoly * poly, LightInfo * light)
{
    if(light->getType() != LightInfo::Vector)
        return;

    // build the poly
    Point3F pointOffset = light->getDirection() * 10.f;

    // create the shadow volume
    mShadowVolumes.increment();
    SVNode ** traverse = &mShadowVolumes.last();
    U32 shadowVolumeIndex = mShadowVolumes.size() - 1;

    for(U32 i = 0; i < poly->mWindingCount; i++)
    {
        U32 j = (i + 1) % poly->mWindingCount;
        if(poly->mWinding[i] == poly->mWinding[j])
            continue;

        (*traverse) = createNode();
        Point3F & a = poly->mWinding[i];
        Point3F & b = poly->mWinding[j];
        Point3F c = b + pointOffset;

        (*traverse)->mPlaneIndex = insertPlane(PlaneF(a,b,c));
        (*traverse)->mShadowVolume = shadowVolumeIndex;

        traverse = &(*traverse)->mFront;
    }

    // do the poly node
    (*traverse) = createNode();
    (*traverse)->mPlaneIndex = insertPlane(poly->mPlane);
    (*traverse)->mShadowVolume = poly->mShadowVolume = shadowVolumeIndex;
}
Esempio n. 2
0
bool GroundPlane::onAdd()
{
   if( !Parent::onAdd() )
      return false;

   if( isClientObject() )
      _updateMaterial();
      
   if( mSquareSize < sMIN_SQUARE_SIZE )
   {
      Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
      mSquareSize = sMIN_SQUARE_SIZE;
   }

   Parent::setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
   Parent::setTransform( MatrixF::Identity );
   setGlobalBounds();
   resetWorldBox();

   addToScene();

   if ( PHYSICSMGR )
   {
      PhysicsCollision *colShape = PHYSICSMGR->createCollision();
      colShape->addPlane( PlaneF( Point3F::Zero, Point3F( 0, 0, 1 ) ) ); 

      PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
      mPhysicsRep = PHYSICSMGR->createBody();
      mPhysicsRep->init( colShape, 0, 0, this, world );
   }

   return true;
}
void OptimizedPolyList::plane(U32 v1, U32 v2, U32 v3)
{
   /*
   AssertFatal(v1 < mPoints.size() && v2 < mPoints.size() && v3 < mPoints.size(),
      "OptimizedPolyList::plane(): Vertex indices are larger than vertex list size");

   mPolyList.last().plane = addPlane(PlaneF(mPoints[v1], mPoints[v2], mPoints[v3]));
   */

   mPolyList.last().plane = addPlane( PlaneF( mPoints[mVertexList[v1].vertIdx], mPoints[mVertexList[v2].vertIdx], mPoints[mVertexList[v3].vertIdx] ) );
}
Esempio n. 4
0
bool ConvexShape::buildPolyList( PolyListContext context, AbstractPolyList *plist, const Box3F &box, const SphereF &sphere )
{
   if ( mGeometry.points.empty() )	
      return false;

   // If we're exporting deal with that first.
   if ( context == PLC_Export )
   {
      AssertFatal( dynamic_cast<OptimizedPolyList*>( plist ), "ConvexShape::buildPolyList - Bad polylist for export!" );
      _export( (OptimizedPolyList*)plist, box, sphere );
      return true;
   }

   plist->setTransform( &mObjToWorld, mObjScale );
   plist->setObject( this );


   // Add points...

   const Vector< Point3F > pointList = mGeometry.points;

   S32 base = plist->addPoint( pointList[0] );

   for ( S32 i = 1; i < pointList.size(); i++ )	
      plist->addPoint( pointList[i] );


   // Add Surfaces...

   const Vector< ConvexShape::Face > faceList = mGeometry.faces;

   for ( S32 i = 0; i < faceList.size(); i++ )
   {
      const ConvexShape::Face &face = faceList[i];		

      plist->begin( 0, i );

      plist->plane( PlaneF( face.centroid, face.normal ) );

      for ( S32 j = 0; j < face.triangles.size(); j++ )
      {
         plist->vertex( base + face.points[ face.triangles[j].p0 ] );
         plist->vertex( base + face.points[ face.triangles[j].p1 ] );
         plist->vertex( base + face.points[ face.triangles[j].p2 ] );
      }      

      plist->end();
   }

   return true;
}
Esempio n. 5
0
 virtual void SetUp()
 {
    // Build planes for a unit cube centered at the origin.
    // Note that the normals must be facing inwards.
    planes.push_back(PlaneF(Point3F(-0.5f,  0.f,   0.f ), Point3F( 1.f,  0.f,  0.f)));
    planes.push_back(PlaneF(Point3F( 0.5f,  0.f,   0.f ), Point3F(-1.f,  0.f,  0.f)));
    planes.push_back(PlaneF(Point3F( 0.f,  -0.5f,  0.f ), Point3F( 0.f,  1.f,  0.f)));
    planes.push_back(PlaneF(Point3F( 0.f,   0.5f,  0.f ), Point3F( 0.f, -1.f,  0.f)));
    planes.push_back(PlaneF(Point3F( 0.f,   0.f,  -0.5f), Point3F( 0.f,  0.f,  1.f)));
    planes.push_back(PlaneF(Point3F( 0.f,   0.f,   0.5f), Point3F( 0.f,  0.f, -1.f)));
 }
Esempio n. 6
0
void ConvexShape::_export( OptimizedPolyList *plist, const Box3F &box, const SphereF &sphere )
{
   BaseMatInstance *matInst = mMaterialInst;
   if ( isServerObject() && getClientObject() )
      matInst = dynamic_cast<ConvexShape*>(getClientObject())->mMaterialInst;
   
   MatrixF saveMat;
   Point3F saveScale;
   plist->getTransform( &saveMat, &saveScale );

   plist->setTransform( &mObjToWorld, mObjScale );
   plist->setObject( this );   

   const Vector< ConvexShape::Face > faceList = mGeometry.faces;
   const Vector< Point3F > &pointList = mGeometry.points;

   for ( S32 i = 0; i < faceList.size(); i++ )
   {
      const ConvexShape::Face &face = faceList[i];		

      plist->begin( matInst, i, OptimizedPolyList::TriangleList );

      plist->plane( PlaneF( face.centroid, face.normal ) );

      for ( S32 j = 0; j < face.triangles.size(); j++ )
      {                  
         for ( S32 k = 0; k < 3; k++ )         
         {
            U32 vertId = face.triangles[j][k];
            plist->vertex( pointList[ face.points[ vertId ] ], face.normal, face.texcoords[ vertId ] );         
         }
      }      

      plist->end();
   }

   plist->setTransform( &saveMat, saveScale );
}
Esempio n. 7
0
void DecalRoad::_captureVerts()
{  
   PROFILE_SCOPE( DecalRoad_captureVerts );

   //Con::warnf( "%s - captureVerts", isServerObject() ? "server" : "client" );

   if ( isServerObject() )
   {
      //Con::errorf( "DecalRoad::_captureVerts - called on the server side!" );
      return;
   }

   if ( mEdges.size() == 0 )
      return;

   //
   // Construct ClippedPolyList objects for each pair
   // of roadEdges.
   // Use them to capture Terrain verts.
   //
   SphereF sphere;
   RoadEdge *edge = NULL;
   RoadEdge *nextEdge = NULL;

   mTriangleCount = 0;
   mVertCount = 0;

   Vector<ClippedPolyList> clipperList;

   for ( U32 i = 0; i < mEdges.size() - 1; i++ )
   {      
      Box3F box;
      edge = &mEdges[i];
      nextEdge = &mEdges[i+1];

      box.minExtents = edge->p1;
      box.maxExtents = edge->p1;
      box.extend( edge->p0 );
      box.extend( edge->p2 );
      box.extend( nextEdge->p0 );
      box.extend( nextEdge->p1 );
      box.extend( nextEdge->p2 );
      box.minExtents.z -= 5.0f;
      box.maxExtents.z += 5.0f;

      sphere.center = ( nextEdge->p1 + edge->p1 ) * 0.5f;
      sphere.radius = 100.0f; // NOTE: no idea how to calculate this

      ClippedPolyList clipper;
      clipper.mNormal.set(0.0f, 0.0f, 0.0f);
      VectorF n;
      PlaneF plane0, plane1;

      // Construct Back Plane
      n = edge->p2 - edge->p0;
      n.normalize();
      n = mCross( n, edge->uvec );      
      plane0.set( edge->p0, n );   
      clipper.mPlaneList.push_back( plane0 );

      // Construct Front Plane
      n = nextEdge->p2 - nextEdge->p0;
      n.normalize();
      n = -mCross( edge->uvec, n );
      plane1.set( nextEdge->p0, -n );
      //clipper.mPlaneList.push_back( plane1 );

      // Test if / where the planes intersect.
      bool discardLeft = false;
      bool discardRight = false;
      Point3F iPos; 
      VectorF iDir;

      if ( plane0.intersect( plane1, iPos, iDir ) )
      {                  
         Point2F iPos2F( iPos.x, iPos.y );
         Point2F cPos2F( edge->p1.x, edge->p1.y );
         Point2F rVec2F( edge->rvec.x, edge->rvec.y );

         Point2F iVec2F = iPos2F - cPos2F;
         F32 iLen = iVec2F.len();
         iVec2F.normalize();

         if ( iLen < edge->width * 0.5f )
         {
            F32 dot = mDot( rVec2F, iVec2F );

            // The clipping planes intersected on the right side,
            // discard the right side clipping plane.
            if ( dot > 0.0f )
               discardRight = true;         
            // The clipping planes intersected on the left side,
            // discard the left side clipping plane.
            else
               discardLeft = true;
         }
      }

      // Left Plane
      if ( !discardLeft )
      {
         n = ( nextEdge->p0 - edge->p0 );
         n.normalize();
         n = mCross( edge->uvec, n );
         clipper.mPlaneList.push_back( PlaneF(edge->p0, n) );
      }
      else
      {
         nextEdge->p0 = edge->p0;         
      }

      // Right Plane
      if ( !discardRight )
      {
         n = ( nextEdge->p2 - edge->p2 );
         n.normalize();            
         n = -mCross( n, edge->uvec );
         clipper.mPlaneList.push_back( PlaneF(edge->p2, -n) );
      }
      else
      {
         nextEdge->p2 = edge->p2;         
      }

      n = nextEdge->p2 - nextEdge->p0;
      n.normalize();
      n = -mCross( edge->uvec, n );
      plane1.set( nextEdge->p0, -n );
      clipper.mPlaneList.push_back( plane1 );

      // We have constructed the clipping planes,
      // now grab/clip the terrain geometry
      getContainer()->buildPolyList( PLC_Decal, box, TerrainObjectType, &clipper );         
      clipper.cullUnusedVerts();
      clipper.triangulate();
      clipper.generateNormals();

      // If we got something, add it to the ClippedPolyList Vector
      if ( !clipper.isEmpty() && !( smDiscardAll && ( discardRight || discardLeft ) ) )
      {
         clipperList.push_back( clipper );       
      
         mVertCount += clipper.mVertexList.size();
         mTriangleCount += clipper.mPolyList.size();
      }
   }

   //
   // Set the roadEdge height to be flush with terrain
   // This is not really necessary but makes the debug spline rendering better.
   //
   for ( U32 i = 0; i < mEdges.size() - 1; i++ )
   {            
      edge = &mEdges[i];      
      
      _getTerrainHeight( edge->p0.x, edge->p0.y, edge->p0.z );

      _getTerrainHeight( edge->p2.x, edge->p2.y, edge->p2.z );
   }

   //
   // Allocate the RoadBatch(s)   
   //

   // If we captured no verts, then we can return here without
   // allocating any RoadBatches or the Vert/Index Buffers.
   // PreprenderImage will not allocate a render instance while
   // mBatches.size() is zero.
   U32 numClippers = clipperList.size();
   if ( numClippers == 0 )
      return;

   mBatches.clear();

   // Allocate the VertexBuffer and PrimitiveBuffer
   mVB.set( GFX, mVertCount, GFXBufferTypeStatic );
   mPB.set( GFX, mTriangleCount * 3, 0, GFXBufferTypeStatic );

   // Lock the VertexBuffer
   GFXVertexPNTBT *vertPtr = mVB.lock();
   if(!vertPtr) return;
   U32 vertIdx = 0;

   //
   // Fill the VertexBuffer and vertex data for the RoadBatches
   // Loop through the ClippedPolyList Vector   
   //
   RoadBatch *batch = NULL;
   F32 texStart = 0.0f;
   F32 texEnd;
   
   for ( U32 i = 0; i < clipperList.size(); i++ )
   {   
      ClippedPolyList *clipper = &clipperList[i];
      RoadEdge &edge = mEdges[i];
      RoadEdge &nextEdge = mEdges[i+1];      

      VectorF segFvec = nextEdge.p1 - edge.p1;
      F32 segLen = segFvec.len();
      segFvec.normalize();

      F32 texLen = segLen / mTextureLength;
      texEnd = texStart + texLen;

      BiQuadToSqr quadToSquare(  Point2F( edge.p0.x, edge.p0.y ), 
                                 Point2F( edge.p2.x, edge.p2.y ), 
                                 Point2F( nextEdge.p2.x, nextEdge.p2.y ), 
                                 Point2F( nextEdge.p0.x, nextEdge.p0.y ) );  

      //
      if ( i % mSegmentsPerBatch == 0 )
      {
         mBatches.increment();
         batch = &mBatches.last();

         batch->bounds.minExtents = clipper->mVertexList[0].point;
         batch->bounds.maxExtents = clipper->mVertexList[0].point;
         batch->startVert = vertIdx;
      }

      // Loop through each ClippedPolyList        
      for ( U32 j = 0; j < clipper->mVertexList.size(); j++ )
      {
         // Add each vert to the VertexBuffer
         Point3F pos = clipper->mVertexList[j].point;
         vertPtr[vertIdx].point = pos;
         vertPtr[vertIdx].normal = clipper->mNormalList[j];                     
                  
         Point2F uv = quadToSquare.transform( Point2F(pos.x,pos.y) );
         vertPtr[vertIdx].texCoord.x = uv.x;
         vertPtr[vertIdx].texCoord.y = -(( texEnd - texStart ) * uv.y + texStart);   

         vertPtr[vertIdx].tangent = mCross( segFvec, clipper->mNormalList[j] );
         vertPtr[vertIdx].binormal = segFvec;

         vertIdx++;

         // Expand the RoadBatch bounds to contain this vertex   
         batch->bounds.extend( pos );
      }    

      batch->endVert = vertIdx - 1;

      texStart = texEnd;
   }   

   // Unlock the VertexBuffer, we are done filling it.
   mVB.unlock();

   // Lock the PrimitiveBuffer
   U16 *idxBuff;
   mPB.lock(&idxBuff);     
   U32 curIdx = 0;
   U16 vertOffset = 0;   
   batch = NULL;
   S32 batchIdx = -1;

   // Fill the PrimitiveBuffer   
   // Loop through each ClippedPolyList in the Vector
   for ( U32 i = 0; i < clipperList.size(); i++ )
   {      
      ClippedPolyList *clipper = &clipperList[i];

      if ( i % mSegmentsPerBatch == 0 )
      {
         batchIdx++;
         batch = &mBatches[batchIdx];
         batch->startIndex = curIdx;         
      }        

      for ( U32 j = 0; j < clipper->mPolyList.size(); j++ )
      {
         // Write indices for each Poly
         ClippedPolyList::Poly *poly = &clipper->mPolyList[j];                  

         AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" );

         idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart] + vertOffset;         
         curIdx++;
         idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart + 1] + vertOffset;            
         curIdx++;
         idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart + 2] + vertOffset;                
         curIdx++;
      } 

      batch->endIndex = curIdx - 1;

      vertOffset += clipper->mVertexList.size();
   }

   // Unlock the PrimitiveBuffer, we are done filling it.
   mPB.unlock();

   // Generate the object/world bounds
   // Is the union of all batch bounding boxes.

   Box3F box;
   for ( U32 i = 0; i < mBatches.size(); i++ )
   {
      const RoadBatch &batch = mBatches[i];

      if ( i == 0 )      
         box = batch.bounds;               
      else      
         box.intersect( batch.bounds );               
   }

   Point3F pos = getPosition();

   mWorldBox = box;
   resetObjectBox();

   // Make sure we are in the correct bins given our world box.
   if( getSceneManager() != NULL )
      getSceneManager()->notifyObjectDirty( this );
}
Esempio n. 8
0
void AtlasConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf)
{
   cf->material = 0;
   cf->object = mObject;

   // For a tetrahedron this is pretty easy.

   //    points...
   S32 firstVert = cf->mVertexList.size();
   cf->mVertexList.increment(); mat.mulP(point[0], &cf->mVertexList.last());
   cf->mVertexList.increment(); mat.mulP(point[1], &cf->mVertexList.last());
   cf->mVertexList.increment(); mat.mulP(point[2], &cf->mVertexList.last());
   cf->mVertexList.increment(); mat.mulP(point[3], &cf->mVertexList.last());

   //    edges...
   cf->mEdgeList.increment(); 
   cf->mEdgeList.last().vertex[0] = firstVert+0;
   cf->mEdgeList.last().vertex[1] = firstVert+1;

   cf->mEdgeList.increment(); 
   cf->mEdgeList.last().vertex[0] = firstVert+1;
   cf->mEdgeList.last().vertex[1] = firstVert+2;

   cf->mEdgeList.increment(); 
   cf->mEdgeList.last().vertex[0] = firstVert+2;
   cf->mEdgeList.last().vertex[1] = firstVert+0;

   cf->mEdgeList.increment(); 
   cf->mEdgeList.last().vertex[0] = firstVert+3;
   cf->mEdgeList.last().vertex[1] = firstVert+0;

   cf->mEdgeList.increment(); 
   cf->mEdgeList.last().vertex[0] = firstVert+3;
   cf->mEdgeList.last().vertex[1] = firstVert+1;

   cf->mEdgeList.increment(); 
   cf->mEdgeList.last().vertex[0] = firstVert+3;
   cf->mEdgeList.last().vertex[1] = firstVert+2;

   //    triangles...
   cf->mFaceList.increment();
   cf->mFaceList.last().normal;
   mat.mulV(normal, &cf->mFaceList.last().normal);
   cf->mFaceList.last().vertex[0] = firstVert+0;
   cf->mFaceList.last().vertex[1] = firstVert+1;
   cf->mFaceList.last().vertex[2] = firstVert+2;

   cf->mFaceList.increment();
   mat.mulV(PlaneF(point[0], point[1], point[3]), &cf->mFaceList.last().normal);
   cf->mFaceList.last().vertex[0] = firstVert+3;
   cf->mFaceList.last().vertex[1] = firstVert+0;
   cf->mFaceList.last().vertex[2] = firstVert+1;

   cf->mFaceList.increment();
   mat.mulV(PlaneF(point[1], point[2], point[3]), &cf->mFaceList.last().normal);
   cf->mFaceList.last().vertex[0] = firstVert+3;
   cf->mFaceList.last().vertex[1] = firstVert+1;
   cf->mFaceList.last().vertex[2] = firstVert+2;

   cf->mFaceList.increment();
   mat.mulV(PlaneF(point[2], point[0], point[3]), &cf->mFaceList.last().normal);
   cf->mFaceList.last().vertex[0] = firstVert+3;
   cf->mFaceList.last().vertex[1] = firstVert+0;
   cf->mFaceList.last().vertex[2] = firstVert+2;

   // All done!
}
Esempio n. 9
0
bool SceneCullingState::createCullingVolume( const Point3F* vertices, U32 numVertices, SceneCullingVolume::Type type, SceneCullingVolume& outVolume )
{
   const Point3F& viewPos = getCameraState().getViewPosition();
   const Point3F& viewDir = getCameraState().getViewDirection();
   const bool isOrtho = getCullingFrustum().isOrtho();

   //TODO: check if we need to handle penetration of the near plane for occluders specially

   // Allocate space for the clipping planes we generate.  Assume the worst case
   // of every edge generating a plane and, for includers, all edges meeting at
   // steep angles so we need to insert extra planes (the latter is not possible,
   // of course, but it makes things less complicated here).  For occluders, add
   // an extra plane for the near cap.

   const U32 maxPlanes = ( type == SceneCullingVolume::Occluder ? numVertices + 1 : numVertices * 2 );
   PlaneF* planes = allocateData< PlaneF >( maxPlanes );

   // Keep track of the world-space bounds of the polygon.  We use this later
   // to derive some metrics.

   Box3F wsPolyBounds;

   wsPolyBounds.minExtents = Point3F( TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX );
   wsPolyBounds.maxExtents = Point3F( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN );

   // For occluders, also keep track of the nearest, and two farthest silhouette points.  We use
   // this later to construct a near capping plane.
   F32 minVertexDistanceSquared = TypeTraits< F32 >::MAX;
   U32 leastDistantVert = 0;

   F32 maxVertexDistancesSquared[ 2 ] = { TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN };
   U32 mostDistantVertices[ 2 ] = { 0, 0 };

   // Generate the extrusion volume.  For orthographic projections, extrude
   // parallel to the view direction whereas for parallel projections, extrude
   // from the viewpoint.

   U32 numPlanes = 0;
   U32 lastVertex = numVertices - 1;
   bool invert = false;

   for( U32 i = 0; i < numVertices; lastVertex = i, ++ i )
   {
      AssertFatal( numPlanes < maxPlanes, "SceneCullingState::createCullingVolume - Did not allocate enough planes!" );

      const Point3F& v1 = vertices[ i ];
      const Point3F& v2 = vertices[ lastVertex ];

      // Keep track of bounds.

      wsPolyBounds.minExtents.setMin( v1 );
      wsPolyBounds.maxExtents.setMax( v1 );

      // Skip the edge if it's length is really short.

      const Point3F edgeVector = v2 - v1;
      const F32 edgeVectorLenSquared = edgeVector.lenSquared();
      if( edgeVectorLenSquared < 0.025f )
         continue;

      //TODO: might need to do additional checks here for non-planar polygons used by occluders
      //TODO: test for colinearity of edge vector with view vector (occluders only)

      // Create a plane for the edge.

      if( isOrtho )
      {
         // Compute a plane through the two edge vertices and one
         // of the vertices extended along the view direction.

         if( !invert )
            planes[ numPlanes ] = PlaneF( v1, v1 + viewDir, v2 );
         else
            planes[ numPlanes ] = PlaneF( v2, v1 + viewDir, v1 );
      }
      else
      {
         // Compute a plane going through the viewpoint and the two
         // edge vertices.

         if( !invert )
            planes[ numPlanes ] = PlaneF( v1, viewPos, v2 );
         else
            planes[ numPlanes ] = PlaneF( v2, viewPos, v1 );
      }

      numPlanes ++;

      // If this is the first plane that we have created, find out whether
      // the vertex ordering is giving us the plane orientations that we want
      // (facing inside).  If not, invert vertex order from now on.

      if( numPlanes == 1 )
      {
         Point3F center( 0, 0, 0 );
         for( U32 n = 0; n < numVertices; ++ n )
            center += vertices[n];
         center /= numVertices;

         if( planes[numPlanes - 1].whichSide( center ) == PlaneF::Back )
         {
            invert = true;
            planes[ numPlanes - 1 ].invert();
         }
      }

      // For occluders, keep tabs of the nearest, and two farthest vertices.

      if( type == SceneCullingVolume::Occluder )
      {
         const F32 distSquared = ( v1 - viewPos ).lenSquared();
         if( distSquared < minVertexDistanceSquared )
         {
            minVertexDistanceSquared = distSquared;
            leastDistantVert = i;
         }
         if( distSquared > maxVertexDistancesSquared[ 0 ] )
         {
            // Move 0 to 1.
            maxVertexDistancesSquared[ 1 ] = maxVertexDistancesSquared[ 0 ];
            mostDistantVertices[ 1 ] = mostDistantVertices[ 0 ];

            // Replace 0.
            maxVertexDistancesSquared[ 0 ] = distSquared;
            mostDistantVertices[ 0 ] = i;
         }
         else if( distSquared > maxVertexDistancesSquared[ 1 ] )
         {
            // Replace 1.
            maxVertexDistancesSquared[ 1 ] = distSquared;
            mostDistantVertices[ 1 ] = i;
         }
      }
   }

   // If the extrusion produced no useful result, abort.

   if( numPlanes < 3 )
      return false;

   // For includers, test the angle of the edges at the current vertex.
   // If too steep, add an extra plane to improve culling efficiency.

   if( false )//type == SceneCullingVolume::Includer )
   {
      const U32 numOriginalPlanes = numPlanes;
      U32 lastPlaneIndex = numPlanes - 1;

      for( U32 i = 0; i < numOriginalPlanes; lastPlaneIndex = i, ++ i )
      {
         const PlaneF& currentPlane = planes[ i ];
         const PlaneF& lastPlane = planes[ lastPlaneIndex ];

         // Compute the cosine of the angle between the two plane normals.

         const F32 cosAngle = mFabs( mDot( currentPlane, lastPlane ) );

         // The planes meet at increasingly steep angles the more they point
         // in opposite directions, i.e the closer the angle of their normals
         // is to 180 degrees.  Skip any two planes that don't get near that.

         if( cosAngle > 0.1f )
            continue;

         //TODO

         const Point3F addNormals = currentPlane + lastPlane;
         const Point3F crossNormals = mCross( currentPlane, lastPlane );

         Point3F newNormal = currentPlane + lastPlane;//addNormals - mDot( addNormals, crossNormals ) * crossNormals;

         //

         planes[ numPlanes ] = PlaneF( currentPlane.getPosition(), newNormal );
         numPlanes ++;
      }
   }

   // Compute the metrics of the culling volume in relation to the view frustum.
   //
   // For this, we are short-circuiting things slightly.  The correct way (other than doing
   // full screen projections) would be to transform all the polygon points into camera
   // space, lay an AABB around those points, and then find the X and Z extents on the near plane.
   //
   // However, while not as accurate, a faster way is to just project the axial vectors
   // of the bounding box onto both the camera right and up vector.  This gives us a rough
   // estimate of the camera-space size of the polygon we're looking at.
   
   const MatrixF& cameraTransform = getCameraState().getViewWorldMatrix();
   const Point3F cameraRight = cameraTransform.getRightVector();
   const Point3F cameraUp = cameraTransform.getUpVector();

   const Point3F wsPolyBoundsExtents = wsPolyBounds.getExtents();
   
   F32 widthEstimate =
      getMax( mFabs( wsPolyBoundsExtents.x * cameraRight.x ),
         getMax( mFabs( wsPolyBoundsExtents.y * cameraRight.y ),
                 mFabs( wsPolyBoundsExtents.z * cameraRight.z ) ) );

   F32 heightEstimate =
      getMax( mFabs( wsPolyBoundsExtents.x * cameraUp.x ),
         getMax( mFabs( wsPolyBoundsExtents.y * cameraUp.y ),
                 mFabs( wsPolyBoundsExtents.z * cameraUp.z ) ) );

   // If the current camera is a perspective one, divide the two estimates
   // by the distance of the nearest bounding box vertex to the camera
   // to account for perspective distortion.

   if( !isOrtho )
   {
      const Point3F nearestVertex = wsPolyBounds.computeVertex(
         Box3F::getPointIndexFromOctant( - viewDir )
      );

      const F32 distance = ( nearestVertex - viewPos ).len();

      widthEstimate /= distance;
      heightEstimate /= distance;
   }

   // If we are creating an occluder, check to see if the estimates fit
   // our minimum requirements.

   if( type == SceneCullingVolume::Occluder )
   {
      const F32 widthEstimatePercentage = widthEstimate / getCullingFrustum().getWidth();
      const F32 heightEstimatePercentage = heightEstimate / getCullingFrustum().getHeight();

      if( widthEstimatePercentage < smOccluderMinWidthPercentage ||
          heightEstimatePercentage < smOccluderMinHeightPercentage )
         return false; // Reject.
   }

   // Use the area estimate as the volume's sort point.

   const F32 sortPoint = widthEstimate * heightEstimate;

   // Finally, if it's an occluder, compute a near cap.  The near cap prevents objects
   // in front of the occluder from testing positive.  The same could be achieved by
   // manually comparing distances before testing objects but since that would amount
   // to the same checks the plane/AABB tests do, it's easier to just add another plane.
   // Additionally, it gives the benefit of being able to create more precise culling
   // results by angling the plane.

   //NOTE: Could consider adding a near cap for includers too when generating a volume
   //  for the outdoor zone as that may prevent quite a bit of space from being included.
   //  However, given that this space will most likely just be filled with interior
   //  stuff anyway, it's probably not worth it.

   if( type == SceneCullingVolume::Occluder )
   {
      const U32 nearCapIndex = numPlanes;
      planes[ nearCapIndex ] = PlaneF(
         vertices[ mostDistantVertices[ 0 ] ],
         vertices[ mostDistantVertices[ 1 ] ],
         vertices[ leastDistantVert ] );

      // Invert the plane, if necessary.
      if( planes[ nearCapIndex ].whichSide( viewPos ) == PlaneF::Front )
         planes[ nearCapIndex ].invert();

      numPlanes ++;
   }

   // Create the volume from the planes.

   outVolume = SceneCullingVolume(
      type,
      PlaneSetF( planes, numPlanes )
   );
   outVolume.setSortPoint( sortPoint );

   // Done.

   return true;
}
Esempio n. 10
0
void ConvexShape::_updateGeometry( bool updateCollision )
{
   mPlanes.clear();

   for ( S32 i = 0; i < mSurfaces.size(); i++ )   
      mPlanes.push_back( PlaneF( mSurfaces[i].getPosition(), mSurfaces[i].getUpVector() ) );

	Vector< Point3F > tangents;
	for ( S32 i = 0; i < mSurfaces.size(); i++ )
		tangents.push_back( mSurfaces[i].getRightVector() );
   
   mGeometry.generate( mPlanes, tangents );

   AssertFatal( mGeometry.faces.size() <= mSurfaces.size(), "Got more faces than planes?" );

   const Vector< ConvexShape::Face > &faceList = mGeometry.faces;
   const Vector< Point3F > &pointList = mGeometry.points;

   // Reset our surface center points.

   for ( S32 i = 0; i < faceList.size(); i++ )
		mSurfaces[ faceList[i].id ].setPosition( faceList[i].centroid );

   mPlanes.clear();

   for ( S32 i = 0; i < mSurfaces.size(); i++ )   
      mPlanes.push_back( PlaneF( mSurfaces[i].getPosition(), mSurfaces[i].getUpVector() ) );

   // Update bounding box.   
   updateBounds( false );

	mVertexBuffer = NULL;
	mPrimitiveBuffer = NULL;
	mVertCount = 0;
	mPrimCount = 0;

   if ( updateCollision )
      _updateCollision();

   // Server does not need to generate vertex/prim buffers.
   if ( isServerObject() )
      return;

   if ( faceList.empty() )   
      return;


	// Get total vert and prim count.

	for ( S32 i = 0; i < faceList.size(); i++ )	
	{
		U32 count = faceList[i].triangles.size();
		mPrimCount += count;
		mVertCount += count * 3;		
	}

	// Allocate VB and copy in data.

	mVertexBuffer.set( GFX, mVertCount, GFXBufferTypeStatic );
	VertexType *pVert = mVertexBuffer.lock();

	for ( S32 i = 0; i < faceList.size(); i++ )
	{
		const ConvexShape::Face &face = faceList[i];
		const Vector< U32 > &facePntMap = face.points;
		const Vector< ConvexShape::Triangle > &triangles = face.triangles;
		const ColorI &faceColor = sgConvexFaceColors[ i % sgConvexFaceColorCount ];

		const Point3F binormal = mCross( face.normal, face.tangent );

		for ( S32 j = 0; j < triangles.size(); j++ )
		{
			for ( S32 k = 0; k < 3; k++ )
			{
				pVert->normal = face.normal;
				pVert->tangent = face.tangent;
				pVert->color = faceColor;			
				pVert->point = pointList[ facePntMap[ triangles[j][k] ] ];
				pVert->texCoord = face.texcoords[ triangles[j][k] ];

				pVert++;
			}
		}		
	}	

	mVertexBuffer.unlock();

	// Allocate PB

   mPrimitiveBuffer.set( GFX, mPrimCount * 3, mPrimCount, GFXBufferTypeStatic );

   U16 *pIndex;
   mPrimitiveBuffer.lock( &pIndex );

   for ( U16 i = 0; i < mPrimCount * 3; i++ )
   {
      *pIndex = i;
      pIndex++;
   }

   mPrimitiveBuffer.unlock();
}
Esempio n. 11
0
void PSSMLightShadowMap::_calcPlanesCullForShadowCasters(Vector< Vector<PlaneF> > &out, const Frustum &viewFrustum, const Point3F &_ligthDir)
{

#define ENABLE_CULL_ASSERT

   PROFILE_SCOPE(PSSMLightShadowMap_render_getCullFrustrum);

   Point3F ligthDir = _ligthDir;
   PlaneF lightFarPlane, lightNearPlane;
   MatrixF lightFarPlaneMat(true);
   MatrixF invLightFarPlaneMat(true);

   // init data
   {
      ligthDir.normalize();
      Point3F viewDir = viewFrustum.getTransform().getForwardVector();
      viewDir.normalize();
      const Point3F viewPosition = viewFrustum.getPosition();
      const F32 viewDistance = viewFrustum.getBounds().len();
      lightNearPlane = PlaneF(viewPosition + (viewDistance * -ligthDir), ligthDir);

      const Point3F lightFarPlanePos = viewPosition + (viewDistance * ligthDir);
      lightFarPlane = PlaneF(lightFarPlanePos, -ligthDir);

      lightFarPlaneMat = MathUtils::createOrientFromDir(-ligthDir);
      lightFarPlaneMat.setPosition(lightFarPlanePos);
      lightFarPlaneMat.invertTo(&invLightFarPlaneMat);
   }

   Vector<Point2F> projVertices;

   //project all frustum vertices into plane
   // all vertices are 2d and local to far plane
   projVertices.setSize(8);
   for (int i = 0; i < 8; ++i) //
   {
      const Point3F &point = viewFrustum.getPoints()[i];
#ifdef ENABLE_CULL_ASSERT
      AssertFatal( PlaneF::Front == lightNearPlane.whichSide(point), "" );
      AssertFatal( PlaneF::Front == lightFarPlane.whichSide(point), "" );
#endif

      Point3F localPoint(lightFarPlane.project(point));
      invLightFarPlaneMat.mulP(localPoint);
      projVertices[i] = Point2F(localPoint.x, localPoint.z);
   }

   //create hull arround projected proints
   Vector<Point2F> hullVerts;
   MathUtils::mBuildHull2D(projVertices, hullVerts);

   Vector<PlaneF> planes;
   planes.push_back(lightNearPlane);
   planes.push_back(lightFarPlane);

   //build planes
   for (int i = 0; i < (hullVerts.size() - 1); ++i)
   {
      Point2F pos2D = (hullVerts[i] + hullVerts[i + 1]) / 2;
      Point3F pos3D(pos2D.x, 0, pos2D.y);

      Point3F pos3DA(hullVerts[i].x, 0, hullVerts[i].y);
      Point3F pos3DB(hullVerts[i + 1].x, 0, hullVerts[i + 1].y);

      // move hull points to 3d space
      lightFarPlaneMat.mulP(pos3D);
      lightFarPlaneMat.mulP(pos3DA);
      lightFarPlaneMat.mulP(pos3DB);

      PlaneF plane(pos3D, MathUtils::mTriangleNormal(pos3DB, pos3DA, (pos3DA - ligthDir)));
      planes.push_back(plane);
   }

   //recalculate planes for each splits
   for (int split = 0; split < mNumSplits; ++split)
   {
      Frustum subFrustum(viewFrustum);
      subFrustum.cropNearFar(mSplitDist[split], mSplitDist[split + 1]);
      subFrustum.setFarDist(getMin(subFrustum.getFarDist()*2.5f, viewFrustum.getFarDist()));
      subFrustum.update();

      Vector<PlaneF> subPlanes = planes;

      for (int planeIdx = 0; planeIdx < subPlanes.size(); ++planeIdx)
      {
         PlaneF &plane = subPlanes[planeIdx];
         F32 minDist = 0;

         //calculate near vertex distance
         for (int vertexIdx = 0; vertexIdx < 8; ++vertexIdx)
         {
            Point3F point = subFrustum.getPoints()[vertexIdx];
            minDist = getMin(plane.distToPlane(point), minDist);
         }

         // move plane to near vertex
         Point3F newPos = plane.getPosition() + (plane.getNormal() * minDist);
         plane = PlaneF(newPos, plane.getNormal());

#ifdef ENABLE_CULL_ASSERT
         for(int x = 0; x < 8; ++x)
         {
            AssertFatal( PlaneF::Back != plane.whichSide( subFrustum.getPoints()[x] ), "");
         }
#endif
      }

      out.push_back(subPlanes);
   }

#undef ENABLE_CULL_ASSERT

}
Esempio n. 12
0
void TSStaticPolysoupConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf)
{
   cf->material = 0;
   cf->object = mObject;

   // For a tetrahedron this is pretty easy... first
   // convert everything into world space.
   Point3F tverts[4];
   mat.mulP(verts[0], &tverts[0]);
   mat.mulP(verts[1], &tverts[1]);
   mat.mulP(verts[2], &tverts[2]);
   mat.mulP(verts[3], &tverts[3]);

   // points...
   S32 firstVert = cf->mVertexList.size();
   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0];
   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1];
   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2];
   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3];

   //    edges...
   cf->mEdgeList.increment();
   cf->mEdgeList.last().vertex[0] = firstVert+0;
   cf->mEdgeList.last().vertex[1] = firstVert+1;

   cf->mEdgeList.increment();
   cf->mEdgeList.last().vertex[0] = firstVert+1;
   cf->mEdgeList.last().vertex[1] = firstVert+2;

   cf->mEdgeList.increment();
   cf->mEdgeList.last().vertex[0] = firstVert+2;
   cf->mEdgeList.last().vertex[1] = firstVert+0;

   cf->mEdgeList.increment();
   cf->mEdgeList.last().vertex[0] = firstVert+3;
   cf->mEdgeList.last().vertex[1] = firstVert+0;

   cf->mEdgeList.increment();
   cf->mEdgeList.last().vertex[0] = firstVert+3;
   cf->mEdgeList.last().vertex[1] = firstVert+1;

   cf->mEdgeList.increment();
   cf->mEdgeList.last().vertex[0] = firstVert+3;
   cf->mEdgeList.last().vertex[1] = firstVert+2;

   //    triangles...
   cf->mFaceList.increment();
   cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]);
   cf->mFaceList.last().vertex[0] = firstVert+2;
   cf->mFaceList.last().vertex[1] = firstVert+1;
   cf->mFaceList.last().vertex[2] = firstVert+0;

   cf->mFaceList.increment();
   cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]);
   cf->mFaceList.last().vertex[0] = firstVert+1;
   cf->mFaceList.last().vertex[1] = firstVert+0;
   cf->mFaceList.last().vertex[2] = firstVert+3;

   cf->mFaceList.increment();
   cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]);
   cf->mFaceList.last().vertex[0] = firstVert+2;
   cf->mFaceList.last().vertex[1] = firstVert+1;
   cf->mFaceList.last().vertex[2] = firstVert+3;

   cf->mFaceList.increment();
   cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]);
   cf->mFaceList.last().vertex[0] = firstVert+0;
   cf->mFaceList.last().vertex[1] = firstVert+2;
   cf->mFaceList.last().vertex[2] = firstVert+3;

   // All done!
}
Esempio n. 13
0
void Portal::_updateGeometry()
{
   const F32 boxHalfWidth = getScale().x * 0.5f;
   const F32 boxHalfHeight = getScale().z * 0.5f;

   const Point3F center = getTransform().getPosition();
   const Point3F up = getTransform().getUpVector() * boxHalfHeight;
   const Point3F right = getTransform().getRightVector() * boxHalfWidth;

   // Update the portal polygon and plane.

   if( mIsBox )
   {
      // It's a box so the portal polygon is a rectangle.
      // Simply compute the corner points by stepping from the
      // center to the corners using the up and right vector.

      mPortalPolygonWS.setSize( 4 );

      mPortalPolygonWS[ 0 ] = center + right - up; // bottom right
      mPortalPolygonWS[ 1 ] = center - right - up; // bottom left
      mPortalPolygonWS[ 2 ] = center - right + up; // top left
      mPortalPolygonWS[ 3 ] = center + right + up; // top right

      // Update the plane by going through three of the points.

      mPortalPlane = PlaneF(
         mPortalPolygonWS[ 0 ],
         mPortalPolygonWS[ 1 ],
         mPortalPolygonWS[ 2 ]
      );
   }
   else
   {
      // It's not necessarily a box so we must use the general
      // routine.

      // Update the portal plane by building a plane that cuts the
      // OBB in half vertically along its Y axis.

      mPortalPlane = PlaneF(
         center + right - up,
         center - right - up,
         center - right + up
      );

      // Slice the polyhedron along the same plane in object space.

      const PlaneF slicePlane = PlaneF( Point3F::Zero, Point3F( 0.f, 1.f, 0.f ) );

      mPortalPolygonWS.setSize( mPolyhedron.getNumEdges() );
      U32 numPoints = mPolyhedron.constructIntersection( slicePlane, mPortalPolygonWS.address(), mPortalPolygonWS.size() );
      mPortalPolygonWS.setSize( numPoints );

      // Transform the polygon to world space.

      for( U32 i = 0; i < numPoints; ++ i )
      {
         mPortalPolygonWS[ i ].convolve( getScale() );
         mObjToWorld.mulP( mPortalPolygonWS[ i ] );
      }
   }

   mIsGeometryDirty = false;
}