From fddaaf0d00bfb05a0e4630f0dd37771baebc7429 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 9 Apr 2009 14:25:14 +0000 Subject: [PATCH] From Ravi Mathur, "OK I have been away for a looong time, but still occasionally watching from a distance, and saw the bug people have reported about the DepthPartitionNode not handling scaled models properly. I believe this is now fixed ... I have attached the new DistanceAccumulator.cpp, along with a modified example file that uses a PositionAttitudeTransform to draw the Earth's orbit around the Sun." --- .../osgdepthpartition/DistanceAccumulator.cpp | 102 ++++++++++++------ .../osgdepthpartition/osgdepthpartition.cpp | 74 +++++++++++-- 2 files changed, 136 insertions(+), 40 deletions(-) diff --git a/examples/osgdepthpartition/DistanceAccumulator.cpp b/examples/osgdepthpartition/DistanceAccumulator.cpp index 29090b902..04beea12b 100644 --- a/examples/osgdepthpartition/DistanceAccumulator.cpp +++ b/examples/osgdepthpartition/DistanceAccumulator.cpp @@ -34,9 +34,13 @@ bool precedes(const DistanceAccumulator::DistancePair &a, else return false; } -/** Computes distance betwen a point and the viewpoint of a matrix */ +/** Computes distance (in z direction) betwen a point and the viewer's eye, + given by a view matrix */ double distance(const osg::Vec3 &coord, const osg::Matrix& matrix) { + // Here we are taking only the z coordinate of the point transformed + // by the matrix, ie coord*matrix. The negative sign is because we + // want to consider into the screen as INCREASING distance. return -( coord[0]*matrix(0,2) + coord[1]*matrix(1,2) + coord[2]*matrix(2,2) + matrix(3,2) ); } @@ -56,19 +60,19 @@ void CURRENT_CLASS::pushLocalFrustum() { osg::Matrix& currMatrix = _viewMatrices.back(); - // Compute the frustum in local space - osg::Polytope localFrustum; - localFrustum.setToUnitFrustum(false, false); - localFrustum.transformProvidingInverse(currMatrix*_projectionMatrices.back()); - _localFrusta.push_back(localFrustum); + // Compute the frustum in local space + osg::Polytope localFrustum; + localFrustum.setToUnitFrustum(false, false); + localFrustum.transformProvidingInverse(currMatrix*_projectionMatrices.back()); + _localFrusta.push_back(localFrustum); - // Compute new bounding box corners - bbCornerPair corner; - corner.second = (currMatrix(0,2)<=0?1:0) | - (currMatrix(1,2)<=0?2:0) | - (currMatrix(2,2)<=0?4:0); - corner.first = (~corner.second)&7; - _bbCorners.push_back(corner); + // Compute new bounding box corners + bbCornerPair corner; + corner.second = (currMatrix(0,2)<=0?1:0) | + (currMatrix(1,2)<=0?2:0) | + (currMatrix(2,2)<=0?4:0); + corner.first = (~corner.second)&7; + _bbCorners.push_back(corner); } void CURRENT_CLASS::pushDistancePair(double zNear, double zFar) @@ -99,19 +103,57 @@ bool CURRENT_CLASS::shouldContinueTraversal(osg::Node &node) // Allow traversal to continue if we haven't reached maximum depth. bool keepTraversing = (_currentDepth < _maxDepth); - const osg::BoundingSphere &bs = node.getBound(); + osg::BoundingSphere bs = node.getBound(); double zNear = 0.0, zFar = 0.0; - // Make sure bounding sphere is valid and within viewing volume + // Make sure bounding sphere is valid if(bs.valid()) { + // Make sure bounding sphere is within the viewing volume if(!_localFrusta.back().contains(bs)) keepTraversing = false; - else + + else // Compute near and far planes for this node { - // Compute near and far planes for this node - zNear = distance(bs._center, _viewMatrices.back()); - zFar = zNear + bs._radius; - zNear -= bs._radius; + // Since the view matrix could involve complex transformations, + // we need to determine a new BoundingSphere that would encompass + // the transformed BoundingSphere. + const osg::Matrix &l2w = _viewMatrices.back(); + + // Get the transformed x-axis of the BoundingSphere + osg::Vec3d newX = bs._center; + newX.x() += bs._radius; // Get X-edge of bounding sphere + newX = newX * l2w; + + // Get the transformed y-axis of the BoundingSphere + osg::Vec3d newY = bs._center; + newY.y() += bs._radius; // Get Y-edge of bounding sphere + newY = newY * l2w; + + // Get the transformed z-axis of the BoundingSphere + osg::Vec3d newZ = bs._center; + newZ.z() += bs._radius; // Get Z-edge of bounding sphere + newZ = newZ * l2w; + + // Get the transformed center of the BoundingSphere + bs._center = bs._center * l2w; + + // Compute lengths of transformed x, y, and z axes + double newXLen = (newX - bs._center).length(); + double newYLen = (newY - bs._center).length(); + double newZLen = (newZ - bs._center).length(); + + // The encompassing radius is the max of the transformed lengths + bs._radius = newXLen; + if(newYLen > bs._radius) bs._radius = newYLen; + if(newZLen > bs._radius) bs._radius = newZLen; + + // Now we can compute the near & far planes, noting that for + // complex view transformations (ie. involving scales) the new + // BoundingSphere may be bigger than the original one. + // Note that the negative sign on the bounding sphere center is + // because we want distance to increase INTO the screen. + zNear = -bs._center.z() - bs._radius; + zFar = -bs._center.z() + bs._radius; // If near/far ratio is big enough, then we don't need to keep // traversing children of this node. @@ -130,9 +172,9 @@ void CURRENT_CLASS::apply(osg::Node &node) if(shouldContinueTraversal(node)) { // Traverse this node - _currentDepth++; + ++_currentDepth; traverse(node); - _currentDepth--; + --_currentDepth; } } @@ -145,9 +187,9 @@ void CURRENT_CLASS::apply(osg::Projection &proj) pushLocalFrustum(); // Traverse the group - _currentDepth++; + ++_currentDepth; traverse(proj); - _currentDepth--; + --_currentDepth; // Reload original matrix and frustum _localFrusta.pop_back(); @@ -171,9 +213,9 @@ void CURRENT_CLASS::apply(osg::Transform &transform) pushLocalFrustum(); } - _currentDepth++; + ++_currentDepth; traverse(transform); - _currentDepth--; + --_currentDepth; if(pushMatrix) { @@ -195,7 +237,7 @@ void CURRENT_CLASS::apply(osg::Geode &geode) double zNear, zFar; // Handle each drawable in this geode - for(unsigned int i = 0; i < geode.getNumDrawables(); i++) + for(unsigned int i = 0; i < geode.getNumDrawables(); ++i) { drawable = geode.getDrawable(i); @@ -268,7 +310,7 @@ void CURRENT_CLASS::computeCameraPairs() // pairs (called combined pairs) will not overlap. PairList combinedPairs; DistancePair currPair = _distancePairs.front(); - for(i = _distancePairs.begin(); i != _distancePairs.end(); i++) + for(i = _distancePairs.begin(); i != _distancePairs.end(); ++i) { // Current distance pair does not overlap current combined pair, so // save the current combined pair and start a new one. @@ -290,7 +332,7 @@ void CURRENT_CLASS::computeCameraPairs() double currNearLimit, numSegs, new_ratio; double ratio_invlog = 1.0/log(_nearFarRatio); unsigned int temp; - for(i = combinedPairs.begin(); i != combinedPairs.end(); i++) + for(i = combinedPairs.begin(); i != combinedPairs.end(); ++i) { currPair = *i; // Save current view segment @@ -311,7 +353,7 @@ void CURRENT_CLASS::computeCameraPairs() } // See if the closest view segment can absorb other combined pairs - for(j = i+1; j != combinedPairs.end(); j++) + for(j = i+1; j != combinedPairs.end(); ++j) { // No other distance pairs can be included if(j->first < currNearLimit) break; diff --git a/examples/osgdepthpartition/osgdepthpartition.cpp b/examples/osgdepthpartition/osgdepthpartition.cpp index 6bfd0d544..89b54ed17 100644 --- a/examples/osgdepthpartition/osgdepthpartition.cpp +++ b/examples/osgdepthpartition/osgdepthpartition.cpp @@ -38,34 +38,88 @@ osg::Node* createScene() // Create the Earth, in blue osg::ShapeDrawable *earth_sd = new osg::ShapeDrawable; osg::Sphere* earth_sphere = new osg::Sphere; + earth_sphere->setName("EarthSphere"); earth_sphere->setRadius(r_earth); earth_sd->setShape(earth_sphere); earth_sd->setColor(osg::Vec4(0, 0, 1.0, 1.0)); - osg::Geode* earth = new osg::Geode; - earth->setName("earth"); - earth->addDrawable(earth_sd); - + osg::Geode* earth_geode = new osg::Geode; + earth_geode->setName("EarthGeode"); + earth_geode->addDrawable(earth_sd); + // Create the Sun, in yellow osg::ShapeDrawable *sun_sd = new osg::ShapeDrawable; osg::Sphere* sun_sphere = new osg::Sphere; + sun_sphere->setName("SunSphere"); sun_sphere->setRadius(r_sun); sun_sd->setShape(sun_sphere); sun_sd->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0)); osg::Geode* sun_geode = new osg::Geode; - sun_geode->setName("sun"); + sun_geode->setName("SunGeode"); sun_geode->addDrawable(sun_sd); // Move the sun behind the earth osg::PositionAttitudeTransform *pat = new osg::PositionAttitudeTransform; pat->setPosition(osg::Vec3d(0.0, AU, 0.0)); - - osg::Group* scene = new osg::Group; - scene->addChild(earth); - scene->addChild(pat); pat->addChild(sun_geode); + + osg::Geometry * unitCircle = new osg::Geometry(); + { + osg::Vec4Array * colours = new osg::Vec4Array(1); + (*colours)[0] = osg::Vec4d(1.0,1.0,1.0,1.0); + unitCircle->setColorArray(colours); + unitCircle->setColorBinding(osg::Geometry::BIND_OVERALL); + const unsigned int n_points = 1024; + osg::Vec3Array * coords = new osg::Vec3Array(n_points); + const double dx = 2.0*M_PI/n_points; + double s,c; + for (unsigned int j=0; jsetVertexArray(coords); + unitCircle->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF); + unitCircle->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP,0,n_points)); + } + osg::Geometry *axes = new osg::Geometry; + { + osg::Vec4Array *colours = new osg::Vec4Array(1); + (*colours)[0] = osg::Vec4d(1.0,0.0,0.0,1.0); + axes->setColorArray(colours); + axes->setColorBinding(osg::Geometry::BIND_OVERALL); + osg::Vec3Array *coords = new osg::Vec3Array(6); + (*coords)[0].set(osg::Vec3d(0.0, 0.0, 0.0)); + (*coords)[1].set(osg::Vec3d(0.5, 0.0, 0.0)); + (*coords)[2].set(osg::Vec3d(0.0, 0.0, 0.0)); + (*coords)[3].set(osg::Vec3d(0.0, 0.5, 0.0)); + (*coords)[4].set(osg::Vec3d(0.0, 0.0, 0.0)); + (*coords)[5].set(osg::Vec3d(0.0, 0.0, 0.5)); + axes->setVertexArray(coords); + axes->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF); + axes->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,6)); + } + + // Earth orbit + osg::Geode * earthOrbitGeode = new osg::Geode; + earthOrbitGeode->addDrawable(unitCircle); + earthOrbitGeode->addDrawable(axes); + earthOrbitGeode->setName("EarthOrbitGeode"); + + osg::PositionAttitudeTransform * earthOrbitPAT = new osg::PositionAttitudeTransform; + earthOrbitPAT->setScale(osg::Vec3d(AU,AU,AU)); + earthOrbitPAT->setPosition(osg::Vec3d(0.0, AU, 0.0)); + earthOrbitPAT->addChild(earthOrbitGeode); + earthOrbitPAT->setName("EarthOrbitPAT"); + + osg::Group* scene = new osg::Group; + scene->setName("SceneGroup"); + scene->addChild(earth_geode); + scene->addChild(pat); + scene->addChild(earthOrbitPAT); + return scene; } @@ -102,7 +156,7 @@ int main( int argc, char **argv ) if (needToSetHomePosition) { - viewer.getCameraManipulator()->setHomePosition(osg::Vec3d(0.0,-5.0*r_earth,0.0),osg::Vec3d(0.0,0.0,0.0),osg::Vec3d(0.0,0.0,1.0)); + viewer.getCameraManipulator()->setHomePosition(osg::Vec3d(0.0,-5.0*r_earth,0.0),osg::Vec3d(0.0,0.0,0.0),osg::Vec3d(0.0,0.0,1.0)); } // depth partion node is only supports single window/single threaded at present.