root/OpenSceneGraph/trunk/examples/osgdepthpartition/DistanceAccumulator.cpp @ 10001

Revision 10001, 13.4 kB (checked in by robert, 6 years ago)

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."

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* OpenSceneGraph example, osgdepthpartion.
2*
3*  Permission is hereby granted, free of charge, to any person obtaining a copy
4*  of this software and associated documentation files (the "Software"), to deal
5*  in the Software without restriction, including without limitation the rights
6*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7*  copies of the Software, and to permit persons to whom the Software is
8*  furnished to do so, subject to the following conditions:
9*
10*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16*  THE SOFTWARE.
17*/
18
19#include "DistanceAccumulator.h"
20#include <osg/Geode>
21#include <osg/Transform>
22#include <osg/Projection>
23#include <algorithm>
24#include <math.h>
25#include <limits.h>
26
27/** Function that sees whether one DistancePair should come before another in
28    an sorted list. Used to sort the vector of DistancePairs. */
29bool precedes(const DistanceAccumulator::DistancePair &a,
30              const DistanceAccumulator::DistancePair &b)
31{
32    // This results in sorting in order of descending far distances
33    if(a.second > b.second) return true;
34    else return false;
35}
36
37/** Computes distance (in z direction) betwen a point and the viewer's eye,
38    given by a view matrix */
39double distance(const osg::Vec3 &coord, const osg::Matrix& matrix)
40{
41    // Here we are taking only the z coordinate of the point transformed
42    // by the matrix, ie coord*matrix. The negative sign is because we
43    // want to consider into the screen as INCREASING distance.
44    return -( coord[0]*matrix(0,2) + coord[1]*matrix(1,2) +
45              coord[2]*matrix(2,2) + matrix(3,2) );
46}
47
48#define CURRENT_CLASS DistanceAccumulator
49CURRENT_CLASS::CURRENT_CLASS()
50    : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN),
51      _nearFarRatio(0.0005), _maxDepth(UINT_MAX)
52{
53    setMatrices(osg::Matrix::identity(), osg::Matrix::identity());
54    reset();
55}
56
57CURRENT_CLASS::~CURRENT_CLASS() {}
58
59void CURRENT_CLASS::pushLocalFrustum()
60{
61    osg::Matrix& currMatrix = _viewMatrices.back();
62
63    // Compute the frustum in local space
64    osg::Polytope localFrustum;
65    localFrustum.setToUnitFrustum(false, false);
66    localFrustum.transformProvidingInverse(currMatrix*_projectionMatrices.back());
67    _localFrusta.push_back(localFrustum);
68
69    // Compute new bounding box corners
70    bbCornerPair corner;
71    corner.second = (currMatrix(0,2)<=0?1:0) |
72                    (currMatrix(1,2)<=0?2:0) |
73                    (currMatrix(2,2)<=0?4:0);
74    corner.first = (~corner.second)&7;
75    _bbCorners.push_back(corner);
76}
77
78void CURRENT_CLASS::pushDistancePair(double zNear, double zFar)
79{
80    if(zFar > 0.0) // Make sure some of drawable is visible
81    {
82      // Make sure near plane is in front of viewpoint.
83      if(zNear <= 0.0)
84      {
85        zNear = zFar*_nearFarRatio;
86        if(zNear >= 1.0) zNear = 1.0; // 1.0 limit chosen arbitrarily!
87      }
88
89      // Add distance pair for current drawable
90      _distancePairs.push_back(DistancePair(zNear, zFar));
91
92      // Override the current nearest/farthest planes if necessary
93      if(zNear < _limits.first) _limits.first = zNear;
94      if(zFar > _limits.second) _limits.second = zFar;
95    }
96}
97
98/** Return true if the node should be traversed, and false if the bounding sphere
99    of the node is small enough to be rendered by one Camera. If the latter
100    is true, then store the node's near & far plane distances. */
101bool CURRENT_CLASS::shouldContinueTraversal(osg::Node &node)
102{
103    // Allow traversal to continue if we haven't reached maximum depth.
104    bool keepTraversing = (_currentDepth < _maxDepth);
105
106    osg::BoundingSphere bs = node.getBound();
107    double zNear = 0.0, zFar = 0.0;
108
109    // Make sure bounding sphere is valid
110    if(bs.valid())
111    {
112      // Make sure bounding sphere is within the viewing volume
113      if(!_localFrusta.back().contains(bs)) keepTraversing = false;
114
115      else // Compute near and far planes for this node
116      {
117        // Since the view matrix could involve complex transformations,
118        // we need to determine a new BoundingSphere that would encompass
119        // the transformed BoundingSphere.
120        const osg::Matrix &l2w = _viewMatrices.back();
121
122        // Get the transformed x-axis of the BoundingSphere
123        osg::Vec3d newX = bs._center;
124        newX.x() += bs._radius; // Get X-edge of bounding sphere
125        newX = newX * l2w;
126
127        // Get the transformed y-axis of the BoundingSphere
128        osg::Vec3d newY = bs._center;
129        newY.y() += bs._radius; // Get Y-edge of bounding sphere
130        newY = newY * l2w;
131
132        // Get the transformed z-axis of the BoundingSphere
133        osg::Vec3d newZ = bs._center;
134        newZ.z() += bs._radius; // Get Z-edge of bounding sphere
135        newZ = newZ * l2w;
136
137        // Get the transformed center of the BoundingSphere
138        bs._center = bs._center * l2w;
139
140        // Compute lengths of transformed x, y, and z axes
141        double newXLen = (newX - bs._center).length();
142        double newYLen = (newY - bs._center).length();
143        double newZLen = (newZ - bs._center).length();
144
145        // The encompassing radius is the max of the transformed lengths
146        bs._radius = newXLen;
147        if(newYLen > bs._radius) bs._radius = newYLen;
148        if(newZLen > bs._radius) bs._radius = newZLen;
149
150        // Now we can compute the near & far planes, noting that for
151        // complex view transformations (ie. involving scales) the new
152        // BoundingSphere may be bigger than the original one.
153        // Note that the negative sign on the bounding sphere center is
154        // because we want distance to increase INTO the screen.
155        zNear = -bs._center.z() - bs._radius;
156        zFar = -bs._center.z() + bs._radius;
157
158        // If near/far ratio is big enough, then we don't need to keep
159        // traversing children of this node.
160        if(zNear >= zFar*_nearFarRatio) keepTraversing = false;
161      }
162    }
163
164    // If traversal should stop, then store this node's (near,far) pair
165    if(!keepTraversing) pushDistancePair(zNear, zFar);
166
167    return keepTraversing;
168}
169
170void CURRENT_CLASS::apply(osg::Node &node)
171{
172    if(shouldContinueTraversal(node))
173    {
174      // Traverse this node
175      ++_currentDepth;
176      traverse(node);
177      --_currentDepth;
178    }
179}
180
181void CURRENT_CLASS::apply(osg::Projection &proj)
182{
183    if(shouldContinueTraversal(proj))
184    {
185      // Push the new projection matrix view frustum
186      _projectionMatrices.push_back(proj.getMatrix());
187      pushLocalFrustum();
188
189      // Traverse the group
190      ++_currentDepth;
191      traverse(proj);
192      --_currentDepth;
193
194      // Reload original matrix and frustum
195      _localFrusta.pop_back();
196      _bbCorners.pop_back();
197      _projectionMatrices.pop_back();
198    }
199}
200
201void CURRENT_CLASS::apply(osg::Transform &transform)
202{
203    if(shouldContinueTraversal(transform))
204    {
205      // Compute transform for current node
206      osg::Matrix currMatrix = _viewMatrices.back();
207      bool pushMatrix = transform.computeLocalToWorldMatrix(currMatrix, this);
208
209      if(pushMatrix)
210      {
211        // Store the new modelview matrix and view frustum
212        _viewMatrices.push_back(currMatrix);
213        pushLocalFrustum();
214      }
215
216      ++_currentDepth;
217      traverse(transform);
218      --_currentDepth;
219
220      if(pushMatrix)
221      {
222        // Restore the old modelview matrix and view frustum
223        _localFrusta.pop_back();
224        _bbCorners.pop_back();
225        _viewMatrices.pop_back();
226      }
227    }
228}
229
230void CURRENT_CLASS::apply(osg::Geode &geode)
231{
232    // Contained drawables will only be individually considered if we are
233    // allowed to continue traversing.
234    if(shouldContinueTraversal(geode))
235    {
236      osg::Drawable *drawable;
237      double zNear, zFar;
238
239      // Handle each drawable in this geode
240      for(unsigned int i = 0; i < geode.getNumDrawables(); ++i)
241      {
242        drawable = geode.getDrawable(i);
243
244        const osg::BoundingBox &bb = drawable->getBound();
245        if(bb.valid())
246        {
247          // Make sure drawable will be visible in the scene
248          if(!_localFrusta.back().contains(bb)) continue;
249
250          // Compute near/far distances for current drawable
251          zNear = distance(bb.corner(_bbCorners.back().first),
252                               _viewMatrices.back());
253          zFar = distance(bb.corner(_bbCorners.back().second),
254                          _viewMatrices.back());
255          if(zNear > zFar) std::swap(zNear, zFar);
256          pushDistancePair(zNear, zFar);
257        }
258      }
259    }
260}
261
262void CURRENT_CLASS::setMatrices(const osg::Matrix &modelview,
263                                const osg::Matrix &projection)
264{
265    _modelview = modelview;
266    _projection = projection;
267}
268
269void CURRENT_CLASS::reset()
270{
271    // Clear vectors & values
272    _distancePairs.clear();
273    _cameraPairs.clear();
274    _limits.first = DBL_MAX;
275    _limits.second = 0.0;
276    _currentDepth = 0;
277
278    // Initial transform matrix is the modelview matrix
279    _viewMatrices.clear();
280    _viewMatrices.push_back(_modelview);
281
282    // Set the initial projection matrix
283    _projectionMatrices.clear();
284    _projectionMatrices.push_back(_projection);
285
286    // Create a frustum without near/far planes, for cull computations
287    _localFrusta.clear();
288    _bbCorners.clear();
289    pushLocalFrustum();
290}
291
292void CURRENT_CLASS::computeCameraPairs()
293{
294    // Nothing in the scene, so no cameras needed
295    if(_distancePairs.empty()) return;
296
297    // Entire scene can be handled by just one camera
298    if(_limits.first >= _limits.second*_nearFarRatio)
299    {
300      _cameraPairs.push_back(_limits);
301      return;
302    }
303
304    PairList::iterator i,j;
305
306    // Sort the list of distance pairs by descending far distance
307    std::sort(_distancePairs.begin(), _distancePairs.end(), precedes);
308
309    // Combine overlapping distance pairs. The resulting set of distance
310    // pairs (called combined pairs) will not overlap.
311    PairList combinedPairs;
312    DistancePair currPair = _distancePairs.front();
313    for(i = _distancePairs.begin(); i != _distancePairs.end(); ++i)
314    {
315      // Current distance pair does not overlap current combined pair, so
316      // save the current combined pair and start a new one.
317      if(i->second < 0.99*currPair.first)
318      {
319        combinedPairs.push_back(currPair);
320        currPair = *i;
321      }
322
323      // Current distance pair overlaps current combined pair, so expand
324      // current combined pair to encompass distance pair.
325      else
326        currPair.first = std::min(i->first, currPair.first);
327    }
328    combinedPairs.push_back(currPair); // Add last pair
329
330    // Compute the (near,far) distance pairs for each camera.
331    // Each of these distance pairs is called a "view segment".
332    double currNearLimit, numSegs, new_ratio;
333    double ratio_invlog = 1.0/log(_nearFarRatio);
334    unsigned int temp;
335    for(i = combinedPairs.begin(); i != combinedPairs.end(); ++i)
336    {
337      currPair = *i; // Save current view segment
338
339      // Compute the fractional number of view segments needed to span
340      // the current combined distance pair.
341      currNearLimit = currPair.second*_nearFarRatio;
342      if(currPair.first >= currNearLimit) numSegs = 1.0;
343      else 
344      {
345        numSegs = log(currPair.first/currPair.second)*ratio_invlog;
346
347        // Compute the near plane of the last view segment
348        //currNearLimit *= pow(_nearFarRatio, -floor(-numSegs) - 1);
349        for(temp = (unsigned int)(-floor(-numSegs)); temp > 1; temp--)
350        {
351          currNearLimit *= _nearFarRatio;
352        }
353      }
354
355      // See if the closest view segment can absorb other combined pairs
356      for(j = i+1; j != combinedPairs.end(); ++j)
357      {
358        // No other distance pairs can be included
359        if(j->first < currNearLimit) break;
360      }
361
362      // If we did absorb another combined distance pair, recompute the
363      // number of required view segments.
364      if(i != j-1)
365      {
366        i = j-1;
367        currPair.first = i->first;
368        if(currPair.first >= currPair.second*_nearFarRatio) numSegs = 1.0;
369        else numSegs = log(currPair.first/currPair.second)*ratio_invlog;
370      }
371
372      /* Compute an integer number of segments by rounding the fractional
373         number of segments according to how many segments there are.
374         In general, the more segments there are, the more likely that the
375         integer number of segments will be rounded down.
376         The purpose of this is to try to minimize the number of view segments
377         that are used to render any section of the scene without violating
378         the specified _nearFarRatio by too much. */
379      if(numSegs < 10.0) numSegs = floor(numSegs + 1.0 - 0.1*floor(numSegs));
380      else numSegs = floor(numSegs);
381
382
383      // Compute the near/far ratio that will be used for each view segment
384      // in this section of the scene.
385      new_ratio = pow(currPair.first/currPair.second, 1.0/numSegs);
386
387      // Add numSegs new view segments to the camera pairs list
388      for(temp = (unsigned int)numSegs; temp > 0; temp--)
389      {
390        currPair.first = currPair.second*new_ratio;
391        _cameraPairs.push_back(currPair);
392        currPair.second = currPair.first;
393      }
394    }
395}
396
397void CURRENT_CLASS::setNearFarRatio(double ratio)
398{
399    if(ratio <= 0.0 || ratio >= 1.0) return;
400    _nearFarRatio = ratio;
401}
402#undef CURRENT_CLASS
Note: See TracBrowser for help on using the browser.