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

Revision 5757, 10.6 kB (checked in by robert, 7 years ago)

Renamed osg::CameraNode? to osg::Camera, cleaned up osg::View.

Added beginnings of new osgViewer::Scene,View,Viewer,CompositeViewer? and GraphicsWindowProxy? files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include "DistanceAccumulator.h"
2#include <osg/Geode>
3#include <osg/Transform>
4#include <osg/Projection>
5#include <algorithm>
6#include <math.h>
7
8/** Function that sees whether one DistancePair should come before another in
9    an sorted list. Used to sort the vector of DistancePairs. */
10bool precedes(const DistanceAccumulator::DistancePair &a,
11              const DistanceAccumulator::DistancePair &b)
12{
13    // This results in sorting in order of descending far distances
14    if(a.second > b.second) return true;
15    else return false;
16}
17
18/** Computes distance betwen a point and the viewpoint of a matrix */
19double distance(const osg::Vec3 &coord, const osg::Matrix& matrix)
20{
21    return -( coord[0]*matrix(0,2) + coord[1]*matrix(1,2) +
22              coord[2]*matrix(2,2) + matrix(3,2) );
23}
24
25#define CURRENT_CLASS DistanceAccumulator
26CURRENT_CLASS::CURRENT_CLASS()
27    : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN),
28      _nearFarRatio(0.0005), _maxDepth(UINT_MAX)
29{
30    setMatrices(osg::Matrix::identity(), osg::Matrix::identity());
31    reset();
32}
33
34CURRENT_CLASS::~CURRENT_CLASS() {}
35
36void CURRENT_CLASS::pushLocalFrustum()
37{
38    osg::Matrix& currMatrix = _viewMatrices.back();
39
40        // Compute the frustum in local space
41        osg::Polytope localFrustum;
42        localFrustum.setToUnitFrustum(false, false);
43        localFrustum.transformProvidingInverse(currMatrix*_projectionMatrices.back());
44        _localFrusta.push_back(localFrustum);
45
46        // Compute new bounding box corners
47        bbCornerPair corner;
48        corner.second = (currMatrix(0,2)<=0?1:0) |
49                        (currMatrix(1,2)<=0?2:0) |
50                        (currMatrix(2,2)<=0?4:0);
51        corner.first = (~corner.second)&7;
52        _bbCorners.push_back(corner);
53}
54
55void CURRENT_CLASS::pushDistancePair(double zNear, double zFar)
56{
57    if(zFar > 0.0) // Make sure some of drawable is visible
58    {
59      // Make sure near plane is in front of viewpoint.
60      if(zNear <= 0.0)
61      {
62        zNear = zFar*_nearFarRatio;
63        if(zNear >= 1.0) zNear = 1.0; // 1.0 limit chosen arbitrarily!
64      }
65
66      // Add distance pair for current drawable
67      _distancePairs.push_back(DistancePair(zNear, zFar));
68
69      // Override the current nearest/farthest planes if necessary
70      if(zNear < _limits.first) _limits.first = zNear;
71      if(zFar > _limits.second) _limits.second = zFar;
72    }
73}
74
75/** Return true if the node should be traversed, and false if the bounding sphere
76    of the node is small enough to be rendered by one Camera. If the latter
77    is true, then store the node's near & far plane distances. */
78bool CURRENT_CLASS::shouldContinueTraversal(osg::Node &node)
79{
80    // Allow traversal to continue if we haven't reached maximum depth.
81    bool keepTraversing = (_currentDepth < _maxDepth);
82
83    const osg::BoundingSphere &bs = node.getBound();
84    double zNear = 0.0, zFar = 0.0;
85
86    // Make sure bounding sphere is valid and within viewing volume
87    if(bs.valid())
88    {
89      if(!_localFrusta.back().contains(bs)) keepTraversing = false;
90      else
91      {
92        // Compute near and far planes for this node
93        zNear = distance(bs._center, _viewMatrices.back());
94        zFar = zNear + bs._radius;
95        zNear -= bs._radius;
96
97        // If near/far ratio is big enough, then we don't need to keep
98        // traversing children of this node.
99        if(zNear >= zFar*_nearFarRatio) keepTraversing = false;
100      }
101    }
102
103    // If traversal should stop, then store this node's (near,far) pair
104    if(!keepTraversing) pushDistancePair(zNear, zFar);
105
106    return keepTraversing;
107}
108
109void CURRENT_CLASS::apply(osg::Node &node)
110{
111    if(shouldContinueTraversal(node))
112    {
113      // Traverse this node
114      _currentDepth++;
115      traverse(node);
116      _currentDepth--;
117    }
118}
119
120void CURRENT_CLASS::apply(osg::Projection &proj)
121{
122    if(shouldContinueTraversal(proj))
123    {
124      // Push the new projection matrix view frustum
125      _projectionMatrices.push_back(proj.getMatrix());
126      pushLocalFrustum();
127
128      // Traverse the group
129      _currentDepth++;
130      traverse(proj);
131      _currentDepth--;
132
133      // Reload original matrix and frustum
134      _localFrusta.pop_back();
135      _bbCorners.pop_back();
136      _projectionMatrices.pop_back();
137    }
138}
139
140void CURRENT_CLASS::apply(osg::Transform &transform)
141{
142    if(shouldContinueTraversal(transform))
143    {
144      // Compute transform for current node
145      osg::Matrix currMatrix = _viewMatrices.back();
146      bool pushMatrix = transform.computeLocalToWorldMatrix(currMatrix, this);
147
148      if(pushMatrix)
149      {
150        // Store the new modelview matrix and view frustum
151        _viewMatrices.push_back(currMatrix);
152        pushLocalFrustum();
153      }
154
155      _currentDepth++;
156      traverse(transform);
157      _currentDepth--;
158
159      if(pushMatrix)
160      {
161        // Restore the old modelview matrix and view frustum
162        _localFrusta.pop_back();
163        _bbCorners.pop_back();
164        _viewMatrices.pop_back();
165      }
166    }
167}
168
169void CURRENT_CLASS::apply(osg::Geode &geode)
170{
171    // Contained drawables will only be individually considered if we are
172    // allowed to continue traversing.
173    if(shouldContinueTraversal(geode))
174    {
175      osg::Drawable *drawable;
176      double zNear, zFar;
177
178      // Handle each drawable in this geode
179      for(unsigned int i = 0; i < geode.getNumDrawables(); i++)
180      {
181        drawable = geode.getDrawable(i);
182
183        const osg::BoundingBox &bb = drawable->getBound();
184        if(bb.valid())
185        {
186          // Make sure drawable will be visible in the scene
187          if(!_localFrusta.back().contains(bb)) continue;
188
189          // Compute near/far distances for current drawable
190          zNear = distance(bb.corner(_bbCorners.back().first),
191                               _viewMatrices.back());
192          zFar = distance(bb.corner(_bbCorners.back().second),
193                          _viewMatrices.back());
194          if(zNear > zFar) std::swap(zNear, zFar);
195          pushDistancePair(zNear, zFar);
196        }
197      }
198    }
199}
200
201void CURRENT_CLASS::setMatrices(const osg::Matrix &modelview,
202                                const osg::Matrix &projection)
203{
204    _modelview = modelview;
205    _projection = projection;
206}
207
208void CURRENT_CLASS::reset()
209{
210    // Clear vectors & values
211    _distancePairs.clear();
212    _cameraPairs.clear();
213    _limits.first = DBL_MAX;
214    _limits.second = 0.0;
215    _currentDepth = 0;
216
217    // Initial transform matrix is the modelview matrix
218    _viewMatrices.clear();
219    _viewMatrices.push_back(_modelview);
220
221    // Set the initial projection matrix
222    _projectionMatrices.clear();
223    _projectionMatrices.push_back(_projection);
224
225    // Create a frustum without near/far planes, for cull computations
226    _localFrusta.clear();
227    _bbCorners.clear();
228    pushLocalFrustum();
229}
230
231void CURRENT_CLASS::computeCameraPairs()
232{
233    // Nothing in the scene, so no cameras needed
234    if(_distancePairs.empty()) return;
235
236    // Entire scene can be handled by just one camera
237    if(_limits.first >= _limits.second*_nearFarRatio)
238    {
239      _cameraPairs.push_back(_limits);
240      return;
241    }
242
243    PairList::iterator i,j;
244
245    // Sort the list of distance pairs by descending far distance
246    std::sort(_distancePairs.begin(), _distancePairs.end(), precedes);
247
248    // Combine overlapping distance pairs. The resulting set of distance
249    // pairs (called combined pairs) will not overlap.
250    PairList combinedPairs;
251    DistancePair currPair = _distancePairs.front();
252    for(i = _distancePairs.begin(); i != _distancePairs.end(); i++)
253    {
254      // Current distance pair does not overlap current combined pair, so
255      // save the current combined pair and start a new one.
256      if(i->second < 0.99*currPair.first)
257      {
258        combinedPairs.push_back(currPair);
259        currPair = *i;
260      }
261
262      // Current distance pair overlaps current combined pair, so expand
263      // current combined pair to encompass distance pair.
264      else
265        currPair.first = std::min(i->first, currPair.first);
266    }
267    combinedPairs.push_back(currPair); // Add last pair
268
269    // Compute the (near,far) distance pairs for each camera.
270    // Each of these distance pairs is called a "view segment".
271    double currNearLimit, numSegs, new_ratio;
272    double ratio_invlog = 1.0/log(_nearFarRatio);
273    unsigned int temp;
274    for(i = combinedPairs.begin(); i != combinedPairs.end(); i++)
275    {
276      currPair = *i; // Save current view segment
277
278      // Compute the fractional number of view segments needed to span
279      // the current combined distance pair.
280      currNearLimit = currPair.second*_nearFarRatio;
281      if(currPair.first >= currNearLimit) numSegs = 1.0;
282      else 
283      {
284        numSegs = log(currPair.first/currPair.second)*ratio_invlog;
285
286        // Compute the near plane of the last view segment
287        //currNearLimit *= pow(_nearFarRatio, -floor(-numSegs) - 1);
288        for(temp = (unsigned int)(-floor(-numSegs)); temp > 1; temp--)
289        {
290          currNearLimit *= _nearFarRatio;
291        }
292      }
293
294      // See if the closest view segment can absorb other combined pairs
295      for(j = i+1; j != combinedPairs.end(); j++)
296      {
297        // No other distance pairs can be included
298        if(j->first < currNearLimit) break;
299      }
300
301      // If we did absorb another combined distance pair, recompute the
302      // number of required view segments.
303      if(i != j-1)
304      {
305        i = j-1;
306        currPair.first = i->first;
307        if(currPair.first >= currPair.second*_nearFarRatio) numSegs = 1.0;
308        else numSegs = log(currPair.first/currPair.second)*ratio_invlog;
309      }
310
311      /* Compute an integer number of segments by rounding the fractional
312         number of segments according to how many segments there are.
313         In general, the more segments there are, the more likely that the
314         integer number of segments will be rounded down.
315         The purpose of this is to try to minimize the number of view segments
316         that are used to render any section of the scene without violating
317         the specified _nearFarRatio by too much. */
318      if(numSegs < 10.0) numSegs = floor(numSegs + 1.0 - 0.1*floor(numSegs));
319      else numSegs = floor(numSegs);
320
321
322      // Compute the near/far ratio that will be used for each view segment
323      // in this section of the scene.
324      new_ratio = pow(currPair.first/currPair.second, 1.0/numSegs);
325
326      // Add numSegs new view segments to the camera pairs list
327      for(temp = (unsigned int)numSegs; temp > 0; temp--)
328      {
329        currPair.first = currPair.second*new_ratio;
330        _cameraPairs.push_back(currPair);
331        currPair.second = currPair.first;
332      }
333    }
334}
335
336void CURRENT_CLASS::setNearFarRatio(double ratio)
337{
338    if(ratio <= 0.0 || ratio >= 1.0) return;
339    _nearFarRatio = ratio;
340}
341#undef CURRENT_CLASS
Note: See TracBrowser for help on using the browser.