/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include using namespace osgShadow; OccluderGeometry::OccluderGeometry() { } OccluderGeometry::OccluderGeometry(const OccluderGeometry& oc, const osg::CopyOp& copyop): osg::Drawable(oc,copyop) { } class CollectOccludersVisitor : public osg::NodeVisitor { public: CollectOccludersVisitor(OccluderGeometry* oc, osg::Matrix* matrix, float ratio): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN), _oc(oc), _ratio(ratio) { if (matrix) pushMatrix(*matrix); } void apply(osg::Node& node) { if (node.getStateSet()) pushState(node.getStateSet()); traverse(node); if (node.getStateSet()) popState(); } void apply(osg::Transform& transform) { if (transform.getStateSet()) pushState(transform.getStateSet()); osg::Matrix matrix; if (!_matrixStack.empty()) matrix = _matrixStack.back(); transform.computeLocalToWorldMatrix(matrix,this); pushMatrix(matrix); traverse(transform); popMatrix(); if (transform.getStateSet()) popState(); } void apply(osg::Geode& geode) { if (geode.getStateSet()) pushState(geode.getStateSet()); for(unsigned int i=0; igetStateSet()) pushState(drawable->getStateSet()); apply(geode.getDrawable(i)); if (drawable->getStateSet()) popState(); } if (geode.getStateSet()) popState(); } void pushState(osg::StateSet* stateset) { osg::StateAttribute::GLModeValue prevBlendModeValue = _blendModeStack.empty() ? osg::StateAttribute::GLModeValue(osg::StateAttribute::INHERIT) : _blendModeStack.back(); osg::StateAttribute::GLModeValue newBlendModeValue = stateset->getMode(GL_BLEND); if (!(newBlendModeValue & osg::StateAttribute::PROTECTED) && (prevBlendModeValue & osg::StateAttribute::OVERRIDE) ) { newBlendModeValue = prevBlendModeValue; } _blendModeStack.push_back(newBlendModeValue); } void popState() { _blendModeStack.pop_back(); } void pushMatrix(osg::Matrix& matrix) { _matrixStack.push_back(matrix); } void popMatrix() { _matrixStack.pop_back(); } void apply(osg::Drawable* drawable) { osg::StateAttribute::GLModeValue blendModeValue = _blendModeStack.empty() ? osg::StateAttribute::GLModeValue(osg::StateAttribute::INHERIT) : _blendModeStack.back(); if (blendModeValue & osg::StateAttribute::ON) { // osg::notify(osg::NOTICE)<<"Ignoring transparent drawable."<processGeometry(drawable, (_matrixStack.empty() ? 0 : &_matrixStack.back()), _ratio); } protected: OccluderGeometry* _oc; typedef std::vector MatrixStack; typedef std::vector ModeStack; float _ratio; MatrixStack _matrixStack; ModeStack _blendModeStack; }; void OccluderGeometry::computeOccluderGeometry(osg::Node* subgraph, osg::Matrix* matrix, float sampleRatio) { osg::notify(osg::NOTICE)<<"computeOccluderGeometry(osg::Node* subgraph, float sampleRatio)"<tick(); CollectOccludersVisitor cov(this, matrix, sampleRatio); subgraph->accept(cov); setUpInternalStructures(); osg::Timer_t endTick = osg::Timer::instance()->tick(); osg::notify(osg::NOTICE)<<"done in "<delta_m(startTick, endTick)<<" ms"< VertexPointers; VertexPointers _vertexPointers; OccluderGeometry::Vec3List _tempoaryTriangleVertices; TriangleCollector():_matrix(0) { } void set(OccluderGeometry::Vec3List* vertices, OccluderGeometry::UIntList* triangleIndices, osg::Matrix* matrix) { _vertices = vertices; _triangleIndices = triangleIndices; _matrix = matrix; } // bool intersect(const Vec3& v1,const Vec3& v2,const Vec3& v3,float& r) inline void operator () (const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3, bool treatVertexDataAsTemporary) { if (treatVertexDataAsTemporary) { // osg::notify(osg::NOTICE)<<"Triangle temp ("< maxVertex) maxVertex = *itr; } unsigned int base = _vertices->size(); unsigned int numberNewVertices = _vertexPointers.empty() ? 0 : (maxVertex - minVertex) + 1; // osg::notify(osg::NOTICE)<<"base = "<accept(tc); tc.copyToLocalData(); #if 0 for(Vec3List::iterator vitr = _vertices.begin(); vitr != _vertices.end(); ++vitr) { osg::notify(osg::NOTICE)<<"v "<<*vitr<tick(); removeDuplicateVertices(); osg::Timer_t t1 = osg::Timer::instance()->tick(); removeNullTriangles(); osg::Timer_t t2 = osg::Timer::instance()->tick(); computeNormals(); osg::Timer_t t3 = osg::Timer::instance()->tick(); buildEdgeMaps(); osg::Timer_t t4 = osg::Timer::instance()->tick(); osg::notify(osg::NOTICE)<<"removeDuplicateVertices "<delta_m(t0,t1)<<" ms"<delta_m(t1,t2)<<" ms"<delta_m(t2,t3)<<" ms"<delta_m(t3,t4)<<" ms"<delta_m(t0,t4)<<" ms"<normalize(); } } void OccluderGeometry::buildEdgeMaps() { // osg::notify(osg::NOTICE)<<"OccluderGeometry::buildEdgeMaps()"< EdgeSet; EdgeSet edgeSet; unsigned int numTriangleErrors = 0; unsigned int triNo=0; for(UIntList::iterator titr = _triangleIndices.begin(); titr != _triangleIndices.end(); ++triNo) { GLuint p1 = *titr++; GLuint p2 = *titr++; GLuint p3 = *titr++; { Edge edge12(p1,p2); EdgeSet::iterator itr = edgeSet.find(edge12); if (itr == edgeSet.end()) { if (!edge12.addTriangle(triNo)) ++numTriangleErrors; edgeSet.insert(edge12); } else { if (!itr->addTriangle(triNo)) ++numTriangleErrors; } } { Edge edge23(p2,p3); EdgeSet::iterator itr = edgeSet.find(edge23); if (itr == edgeSet.end()) { if (!edge23.addTriangle(triNo)) ++numTriangleErrors; edgeSet.insert(edge23); } else { if (!itr->addTriangle(triNo)) ++numTriangleErrors; } } { Edge edge31(p3,p1); EdgeSet::iterator itr = edgeSet.find(edge31); if (itr == edgeSet.end()) { if (!edge31.addTriangle(triNo)) ++numTriangleErrors; edgeSet.insert(edge31); } else { if (!itr->addTriangle(triNo)) ++numTriangleErrors; } } } _edges.clear(); _edges.reserve(edgeSet.size()); unsigned int numEdgesWithNoTriangles = 0; unsigned int numEdgesWithOneTriangles = 0; unsigned int numEdgesWithTwoTriangles = 0; for(EdgeSet::iterator eitr = edgeSet.begin(); eitr != edgeSet.end(); ++eitr) { const Edge& edge = *eitr; osg::Vec3 pos(0.0,0.0,0.0); osg::Vec3 mid = (_vertices[edge._p1] + _vertices[edge._p2]) * 0.5f; unsigned int numTriangles = 0; if (edge._t1>=0) { ++numTriangles; GLuint p1 = _triangleIndices[edge._t1*3]; GLuint p2 = _triangleIndices[edge._t1*3+1]; GLuint p3 = _triangleIndices[edge._t1*3+2]; GLuint opposite = p1; if (p1 != edge._p1 && p1 != edge._p2) opposite = p1; else if (p2 != edge._p1 && p2 != edge._p2) opposite = p2; else if (p3 != edge._p1 && p3 != edge._p2) opposite = p3; pos = _vertices[opposite]; } if (edge._t2>=0) { ++numTriangles; GLuint p1 = _triangleIndices[edge._t2*3]; GLuint p2 = _triangleIndices[edge._t2*3+1]; GLuint p3 = _triangleIndices[edge._t2*3+2]; GLuint opposite = p1; if (p1 != edge._p1 && p1 != edge._p2) opposite = p1; else if (p2 != edge._p1 && p2 != edge._p2) opposite = p2; else if (p3 != edge._p1 && p3 != edge._p2) opposite = p3; pos += _vertices[opposite]; } switch(numTriangles) { case(0): ++numEdgesWithNoTriangles; edge._normal.set(0.0,0.0,0.0); osg::notify(osg::NOTICE)<<"Warning no triangles on edge."< 0.0) { silhouetteIndices.push_back(edge._p1); silhouetteIndices.push_back(edge._p2); } else { silhouetteIndices.push_back(edge._p2); silhouetteIndices.push_back(edge._p1); } } } } void OccluderGeometry::computeLightPositionSlihouetteEdges(const osg::Vec3& lightpos, UIntList& silhouetteIndices) const { silhouetteIndices.clear(); for(EdgeList::const_iterator eitr = _edges.begin(); eitr != _edges.end(); ++eitr) { const Edge& edge = *eitr; if (isLightPointSilhouetteEdge(lightpos,edge)) { const osg::Vec3& v1 = _vertices[edge._p1]; const osg::Vec3& v2 = _vertices[edge._p2]; osg::Vec3 normal = (v2-v1) ^ (v1-lightpos); if (normal * edge._normal > 0.0) { silhouetteIndices.push_back(edge._p1); silhouetteIndices.push_back(edge._p2); } else { silhouetteIndices.push_back(edge._p2); silhouetteIndices.push_back(edge._p1); } } } } void OccluderGeometry::computeShadowVolumeGeometry(const osg::Vec4& lightpos, ShadowVolumeGeometry& svg) const { // osg::Timer_t t0 = osg::Timer::instance()->tick(); ShadowVolumeGeometry::Vec3List& shadowVertices = svg.getVertices(); shadowVertices.clear(); ShadowVolumeGeometry::Vec3List& shadowNormals = svg.getNormals(); shadowNormals.clear(); // need to have some kind of handling of case when no planes exist. if (_boundingPolytope.getPlaneList().empty()) { osg::notify(osg::NOTICE)<<"Warning: no bounding polytope registered with OccluderGeometry."< pitr->dotProductNormal(lightdirection)) { basePlane = *pitr; } } // compute the silhouette edge UIntList silhouetteIndices; computeLightDirectionSlihouetteEdges(lightdirection, silhouetteIndices); osg::Vec3 offset( lightdirection*5.0f ); float directionScale = 1.0f / basePlane.dotProductNormal(lightdirection); for(UIntList::iterator itr = silhouetteIndices.begin(); itr != silhouetteIndices.end(); ) { const osg::Vec3& v1 = _vertices[*itr++]; const osg::Vec3& v2 = _vertices[*itr++]; float r1 = basePlane.distance(v1) * directionScale; float r2 = basePlane.distance(v2) * directionScale; osg::Vec3 v1_projected = v1 - (lightdirection * r1); osg::Vec3 v2_projected = v2 - (lightdirection * r2); shadowVertices.push_back( v1); shadowVertices.push_back( v1_projected); shadowVertices.push_back( v2_projected); shadowVertices.push_back( v2); osg::Vec3 normal = lightdirection ^ (v2-v1); normal.normalize(); shadowNormals.push_back(normal); shadowNormals.push_back(normal); shadowNormals.push_back(normal); shadowNormals.push_back(normal); } } else { // positional light osg::Vec3 lightposition( lightpos.x(), lightpos.y(), lightpos.z()); osg::Plane basePlane(0.0, 0.0, 1.0, 0.0); // osg::notify(osg::NOTICE)<<"Positional light"<delta_m(t0,t1)<<" ms"<disableAllVertexArrays(); renderInfo.getState()->setVertexPointer( 3, GL_FLOAT, 0, &(_vertices.front()) ); if (!_normals.empty()) { renderInfo.getState()->setNormalPointer( GL_FLOAT, 0, &(_normals.front()) ); } if (!_triangleIndices.empty()) { glDrawElements(GL_TRIANGLES, _triangleIndices.size(), GL_UNSIGNED_INT, &(_triangleIndices.front()) ); } } osg::BoundingBox OccluderGeometry::computeBound() const { osg::BoundingBox bb; for(Vec3List::const_iterator itr = _vertices.begin(); itr != _vertices.end(); ++itr) { bb.expandBy(*itr); } return bb; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // ShadowVolumeGeometry // ShadowVolumeGeometry::ShadowVolumeGeometry(): _drawMode(GEOMETRY) { } ShadowVolumeGeometry::ShadowVolumeGeometry(const ShadowVolumeGeometry& oc, const osg::CopyOp& copyop): osg::Drawable(oc,copyop) { } void ShadowVolumeGeometry::drawImplementation(osg::RenderInfo& renderInfo) const { if (_drawMode==GEOMETRY) { osg::State* state = renderInfo.getState(); state->disableAllVertexArrays(); state->setVertexPointer( 3, GL_FLOAT, 0, &(_vertices.front()) ); if (!_normals.empty()) { state->setNormalPointer( GL_FLOAT, 0, &(_normals.front()) ); } else { glNormal3f(0.0f, 0.0f, 0.0f); } glColor4f(0.5f, 1.0f, 1.0f, 1.0f); glDrawArrays( GL_QUADS, 0, _vertices.size() ); } else if (_drawMode==STENCIL_TWO_PASS) { osg::State* state = renderInfo.getState(); state->disableAllVertexArrays(); state->setVertexPointer( 3, GL_FLOAT, 0, &(_vertices.front()) ); // draw front faces of shadow volume glCullFace(GL_BACK); glStencilOp( GL_KEEP, GL_KEEP, GL_INCR); glDrawArrays( GL_QUADS, 0, _vertices.size() ); // draw back faces of shadow volume glCullFace(GL_FRONT); glStencilOp( GL_KEEP, GL_KEEP, GL_DECR); glDrawArrays( GL_QUADS, 0, _vertices.size() ); state->haveAppliedAttribute(osg::StateAttribute::CULLFACE); state->haveAppliedAttribute(osg::StateAttribute::STENCIL); } else // stencil two sided, note state all set up separately. { osg::State* state = renderInfo.getState(); state->disableAllVertexArrays(); state->setVertexPointer( 3, GL_FLOAT, 0, &(_vertices.front()) ); glDrawArrays( GL_QUADS, 0, _vertices.size() ); } } osg::BoundingBox ShadowVolumeGeometry::computeBound() const { osg::BoundingBox bb; for(Vec3List::const_iterator itr = _vertices.begin(); itr != _vertices.end(); ++itr) { bb.expandBy(*itr); } return bb; }