void MinimalDrawBoundsShadowMap::ViewData::performBoundAnalysis( const osg::Camera& camera ) { if( !_projection.valid() ) return; osg::Camera::BufferAttachmentMap & bam = const_cast<osg::Camera&>( camera ).getBufferAttachmentMap(); #if ANALYSIS_DEPTH osg::Camera::Attachment & attachment = bam[ osg::Camera::DEPTH_BUFFER ]; #else osg::Camera::Attachment & attachment = bam[ osg::Camera::COLOR_BUFFER ]; #endif const osg::ref_ptr< osg::Image > image = attachment._image.get(); if( !image.valid() ) return; osg::Matrix m; m.invert( *_modellingSpaceToWorldPtr * camera.getViewMatrix() * camera.getProjectionMatrix() ); m.preMult( osg::Matrix::scale( osg::Vec3( 2.f, 2.f, 2.f ) ) * osg::Matrix::translate( osg::Vec3( -1.f, -1.f, -1.f ) ) ); osg::BoundingBox bb = scanImage( image.get(), m ); if( getDebugDraw() ) { ConvexPolyhedron p; p.setToBoundingBox( bb ); p.transform( *_modellingSpaceToWorldPtr, osg::Matrix::inverse( *_modellingSpaceToWorldPtr ) ); setDebugPolytope( "scan", p, osg::Vec4( 0,0,0,1 ), osg::Vec4( 0,0,0,0.1 ) ); } cutScenePolytope( *_modellingSpaceToWorldPtr, osg::Matrix::inverse( *_modellingSpaceToWorldPtr ), bb ); frameShadowCastingCamera( _mainCamera.get(), _camera.get() ); _projection->set( _camera->getProjectionMatrix( ) ); BaseClass::ViewData::_texgen->setPlanesFromMatrix( _camera->getProjectionMatrix() * osg::Matrix::translate(1.0,1.0,1.0) * osg::Matrix::scale(0.5,0.5,0.5) ); updateDebugGeometry( _mainCamera.get(), _camera.get() ); }
//---------------------------------------------------------------------------- ConvexClipper::ConvexClipper (const ConvexPolyhedron& rkPoly, Real fEpsilon) { m_fEpsilon = fEpsilon; const vector<Vector3>& rakPoint = rkPoly.GetPoints(); int iVQuantity = rkPoly.GetVQuantity(); m_akVertex.resize(iVQuantity); for (int iV = 0; iV < iVQuantity; iV++) m_akVertex[iV].m_kPoint = rakPoint[iV]; int iEQuantity = rkPoly.GetEQuantity(); m_akEdge.resize(iEQuantity); for (int iE = 0; iE < iEQuantity; iE++) { const MTEdge& rkE = rkPoly.GetEdge(iE); for (int i = 0; i < 2; i++) { m_akEdge[iE].m_aiVertex[i] = rkPoly.GetVLabel(rkE.GetVertex(i)); m_akEdge[iE].m_aiFace[i] = rkE.GetTriangle(i); } } int iTQuantity = rkPoly.GetTQuantity(); m_akFace.resize(iTQuantity); for (int iT = 0; iT < iTQuantity; iT++) { m_akFace[iT].m_kPlane = rkPoly.GetPlane(iT); const MTTriangle& rkT = rkPoly.GetTriangle(iT); for (int i = 0; i < 3; i++) m_akFace[iT].m_akEdge.insert(rkT.GetEdge(i)); } }
//---------------------------------------------------------------------------- bool ConvexPolyhedron::FindIntersection (const ConvexPolyhedron& rkPoly, ConvexPolyhedron& rkIntr) const { ConvexClipper kClipper(*this); const vector<Plane>& rakPlane = rkPoly.GetPlanes(); for (int i = 0; i < (int)rakPlane.size(); i++) { if ( kClipper.Clip(rakPlane[i]) == Plane::NEGATIVE_SIDE ) return false; } kClipper.Convert(rkIntr); return true; }
ConvexClipper<Real>::ConvexClipper (const ConvexPolyhedron<Real>& polyhedron, Real epsilon) : mEpsilon(epsilon) { const std::vector<Vector3<Real> >& points = polyhedron.GetPoints(); int numVertices = polyhedron.GetNumVertices(); mVertices.resize(numVertices); for (int v = 0; v < numVertices; ++v) { mVertices[v].Point = points[v]; } int numEdges = polyhedron.GetNumEdges(); mEdges.resize(numEdges); for (int e = 0; e < numEdges; ++e) { const MTEdge& edge = polyhedron.GetEdge(e); for (int i = 0; i < 2; ++i) { mEdges[e].Vertex[i] = polyhedron.GetVLabel(edge.GetVertex(i)); mEdges[e].Face[i] = edge.GetTriangle(i); } } int numTriangles = polyhedron.GetNumTriangles(); mFaces.resize(numTriangles); for (int t = 0; t < numTriangles; ++t) { mFaces[t].Plane = polyhedron.GetPlane(t); const MTTriangle& triangle = polyhedron.GetTriangle(t); for (int i = 0; i < 3; ++i) { mFaces[t].Edges.insert(triangle.GetEdge(i)); } } }
void ConvexClipper<Real>::Convert (ConvexPolyhedron<Real>& polyhedron) { // Get visible vertices. int numVertices = (int)mVertices.size(); std::vector<Vector3<Real> > points; int* vMap = new1<int>(numVertices); memset(vMap, 0xFF, numVertices*sizeof(int)); for (int v = 0; v < numVertices; ++v) { const Vertex& vertex = mVertices[v]; if (vertex.Visible) { vMap[v] = (int)points.size(); points.push_back(vertex.Point); } } std::vector<int> indices; std::vector<Plane3<Real> > planes; GetTriangles(indices, planes); // Reorder the indices. for (int c = 0; c < (int)indices.size(); ++c) { int oldC = indices[c]; assertion(0 <= oldC && oldC < numVertices, "Index out of range.\n"); int newC = vMap[oldC]; assertion(0 <= newC && newC < (int)points.size(), "Index out of range.\n"); indices[c] = newC; } delete1(vMap); polyhedron.Create(points, indices, planes); }
osg::BoundingBox MinimalShadowMap::ViewData::computeShadowReceivingCoarseBounds() { // Default slowest but most precise ShadowReceivingCoarseBoundAccuracy accuracy = DEFAULT_ACCURACY; MinimalShadowMap * msm = dynamic_cast< MinimalShadowMap* >( _st.get() ); if( msm ) accuracy = msm->getShadowReceivingCoarseBoundAccuracy(); if( accuracy == MinimalShadowMap::EMPTY_BOX ) { // One may skip coarse scene bounds computation if light is infinite. // Empty box will be intersected with view frustum so in the end // view frustum will be used as bounds approximation. // But if light is nondirectional and bounds come out too large // they may bring the effect of almost 180 deg perspective set // up for shadow camera. Such projection will significantly impact // precision of further math. return osg::BoundingBox(); } if( accuracy == MinimalShadowMap::BOUNDING_SPHERE ) { // faster but less precise rough scene bound computation // however if compute near far is active it may bring quite good result osg::Camera * camera = _cv->getRenderStage()->getCamera(); osg::Matrix m = camera->getViewMatrix() * _clampedProjection; ConvexPolyhedron frustum; frustum.setToUnitFrustum(); frustum.transform( osg::Matrix::inverse( m ), m ); osg::BoundingSphere bs =_st->getShadowedScene()->getBound(); osg::BoundingBox bb; bb.expandBy( bs ); osg::Polytope box; box.setToBoundingBox( bb ); frustum.cut( box ); // approximate sphere with octahedron. Ie first cut by box then // additionaly cut with the same box rotated 45, 45, 45 deg. box.transform( // rotate box around its center osg::Matrix::translate( -bs.center() ) * osg::Matrix::rotate( osg::PI_4, 0, 0, 1 ) * osg::Matrix::rotate( osg::PI_4, 1, 1, 0 ) * osg::Matrix::translate( bs.center() ) ); frustum.cut( box ); return frustum.computeBoundingBox( ); } if( accuracy == MinimalShadowMap::BOUNDING_BOX ) // Default { // more precise method but slower method // bound visitor traversal takes lot of time for complex scenes // (note that this adds to cull time) osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); cbbv.setTraversalMask(_st->getShadowedScene()->getCastsShadowTraversalMask()); _st->getShadowedScene()->osg::Group::traverse(cbbv); return cbbv.getBoundingBox(); } return osg::BoundingBox(); }
//---------------------------------------------------------------------------- void ConvexPolyhedron::CreateEggShape (const Vector3& rkCenter, Real fX0, Real fX1, Real fY0, Real fY1, Real fZ0, Real fZ1, int iMaxSteps, ConvexPolyhedron& rkEgg) { assert( fX0 > 0.0f && fX1 > 0.0f ); assert( fY0 > 0.0f && fY1 > 0.0f ); assert( fZ0 > 0.0f && fZ1 > 0.0f ); assert( iMaxSteps >= 0 ); // Start with an octahedron whose 6 vertices are (-x0,0,0), (x1,0,0), // (0,-y0,0), (0,y1,0), (0,0,-z0), (0,0,z1). The center point will be // added later. vector<Vector3> akPoint(6); akPoint[0] = Vector3(-fX0,0.0f,0.0f); akPoint[1] = Vector3(fX1,0.0f,0.0f); akPoint[2] = Vector3(0.0f,-fY0,0.0f); akPoint[3] = Vector3(0.0f,fY1,0.0f); akPoint[4] = Vector3(0.0f,0.0f,-fZ0); akPoint[5] = Vector3(0.0f,0.0f,fZ1); vector<int> aiConnect(24); aiConnect[ 0] = 1; aiConnect[ 1] = 3; aiConnect[ 2] = 5; aiConnect[ 3] = 3; aiConnect[ 4] = 0; aiConnect[ 5] = 5; aiConnect[ 6] = 0; aiConnect[ 7] = 2; aiConnect[ 8] = 5; aiConnect[ 9] = 2; aiConnect[10] = 1; aiConnect[11] = 5; aiConnect[12] = 3; aiConnect[13] = 1; aiConnect[14] = 4; aiConnect[15] = 0; aiConnect[16] = 3; aiConnect[17] = 4; aiConnect[18] = 2; aiConnect[19] = 0; aiConnect[20] = 4; aiConnect[21] = 1; aiConnect[22] = 2; aiConnect[23] = 4; rkEgg.InitialELabel() = 0; rkEgg.Create(akPoint,aiConnect); // Subdivide the triangles. The midpoints of the edges are computed. // The triangle is replaced by four sub triangles using the original 3 // vertices and the 3 new edge midpoints. int i; for (int iStep = 1; iStep <= iMaxSteps; iStep++) { int iVQuantity = rkEgg.GetVQuantity(); int iEQuantity = rkEgg.GetEQuantity(); int iTQuantity = rkEgg.GetTQuantity(); // compute lifted edge midpoints for (i = 0; i < iEQuantity; i++) { // get edge const MTEdge& rkE = rkEgg.GetEdge(i); int iV0 = rkEgg.GetVLabel(rkE.GetVertex(0)); int iV1 = rkEgg.GetVLabel(rkE.GetVertex(1)); // compute "lifted" centroid to points Vector3 kCen = rkEgg.Point(iV0)+rkEgg.Point(iV1); Real fXR = (kCen.x > 0.0f ? kCen.x/fX1 : kCen.x/fX0); Real fYR = (kCen.y > 0.0f ? kCen.y/fY1 : kCen.y/fY0); Real fZR = (kCen.z > 0.0f ? kCen.z/fZ1 : kCen.z/fZ0); kCen *= Math::InvSqrt(fXR*fXR+fYR*fYR+fZR*fZR); // Add the point to the array. Store the point index in the edge // label for support in adding new triangles. rkEgg.ELabel(i) = iVQuantity++; rkEgg.AddPoint(kCen); } // Add the new triangles and remove the old triangle. The removal // in slot i will cause the last added triangle to be moved to that // slot. This side effect will not interfere with the iteration // and removal of the triangles. for (i = 0; i < iTQuantity; i++) { const MTTriangle& rkT = rkEgg.GetTriangle(i); int iV0 = rkEgg.GetVLabel(rkT.GetVertex(0)); int iV1 = rkEgg.GetVLabel(rkT.GetVertex(1)); int iV2 = rkEgg.GetVLabel(rkT.GetVertex(2)); int iV01 = rkEgg.GetELabel(rkT.GetEdge(0)); int iV12 = rkEgg.GetELabel(rkT.GetEdge(1)); int iV20 = rkEgg.GetELabel(rkT.GetEdge(2)); rkEgg.Insert(iV0,iV01,iV20); rkEgg.Insert(iV01,iV1,iV12); rkEgg.Insert(iV20,iV12,iV2); rkEgg.Insert(iV01,iV12,iV20); rkEgg.Remove(iV0,iV1,iV2); } } // add center for (i = 0; i < (int)rkEgg.m_akPoint.size(); i++) rkEgg.m_akPoint[i] += rkCenter; rkEgg.UpdatePlanes(); }
void MinimalShadowMap::ViewData::frameShadowCastingCamera ( const osg::Camera* cameraMain, osg::Camera* cameraShadow, int pass ) { osg::Matrix mvp = cameraShadow->getViewMatrix() * cameraShadow->getProjectionMatrix(); ConvexPolyhedron polytope = _sceneReceivingShadowPolytope; std::vector<osg::Vec3d> points = _sceneReceivingShadowPolytopePoints; osg::BoundingBox bb = computeScenePolytopeBounds( mvp ); // projection was trimmed above, need to recompute mvp if( bb.valid() && *_minLightMarginPtr > 0 ) { // bb._max += osg::Vec3( 1, 1, 1 ); // bb._min -= osg::Vec3( 1, 1, 1 ); osg::Matrix transform = osg::Matrix::inverse( mvp ); // Code below was working only for directional lights ie when projection was ortho // osg::Vec3d normal = osg::Matrix::transform3x3( osg::Vec3d( 0,0,-1)., transfrom ); // So I replaced it with safer code working with spot lights as well osg::Vec3d normal = osg::Vec3d(0,0,-1) * transform - osg::Vec3d(0,0,1) * transform; normal.normalize(); _sceneReceivingShadowPolytope.extrude( normal * *_minLightMarginPtr ); // Zero pass does crude shadowed scene hull approximation. // Its important to cut it to coarse light frustum properly // at this stage. // If not cut and polytope extends beyond shadow projection clip // space (-1..1), it may get "twisted" by precisely adjusted shadow cam // projection in second pass. if ( pass == 0 && _frameShadowCastingCameraPasses > 1 ) { // Make sure extruded polytope does not extend beyond light frustum osg::Polytope lightFrustum; lightFrustum.setToUnitFrustum(); lightFrustum.transformProvidingInverse( mvp ); _sceneReceivingShadowPolytope.cut( lightFrustum ); } _sceneReceivingShadowPolytopePoints.clear(); _sceneReceivingShadowPolytope.getPoints ( _sceneReceivingShadowPolytopePoints ); bb = computeScenePolytopeBounds( mvp ); } setDebugPolytope( "extended", _sceneReceivingShadowPolytope, osg::Vec4( 1, 0.5, 0, 1 ), osg::Vec4( 1, 0.5, 0, 0.1 ) ); _sceneReceivingShadowPolytope = polytope; _sceneReceivingShadowPolytopePoints = points; // Warning: Trim light projection at near plane may remove shadowing // from objects outside of view space but still casting shadows into it. // I have not noticed this issue so I left mask at default: all bits set. if( bb.valid() ) trimProjection( cameraShadow->getProjectionMatrix(), bb, 1|2|4|8|16|32 ); ///// Debuging stuff ////////////////////////////////////////////////////////// setDebugPolytope( "scene", _sceneReceivingShadowPolytope, osg::Vec4(0,1,0,1) ); #if PRINT_SHADOW_TEXEL_TO_PIXEL_ERROR if( pass == 1 ) displayShadowTexelToPixelErrors ( cameraMain, cameraShadow, &_sceneReceivingShadowPolytope ); #endif if( pass == _frameShadowCastingCameraPasses - 1 ) { #if 1 { osg::Matrix mvp = cameraShadow->getViewMatrix() * cameraShadow->getProjectionMatrix(); ConvexPolyhedron frustum; frustum.setToUnitFrustum(); frustum.transform( osg::Matrix::inverse( mvp ), mvp ); setDebugPolytope( "shadowCamFrustum", frustum, osg::Vec4(0,0,1,1) ); } { osg::Matrix mvp = cameraMain->getViewMatrix() * cameraMain->getProjectionMatrix(); ConvexPolyhedron frustum; frustum.setToUnitFrustum(); frustum.transform( osg::Matrix::inverse( mvp ), mvp ); setDebugPolytope( "mainCamFrustum", frustum, osg::Vec4(1,1,1,1) ); } #endif std::string * filename = getDebugDump( ); if( filename && !filename->empty() ) { dump( *filename ); filename->clear(); } } }