//------------------------------------------------------------------------------ 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; }
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] ) ); }
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; }
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))); }
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 ); }
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 ); }
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! }
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; }
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(); }
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 }
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! }
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; }