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

Revision 6941, 11.6 kB (checked in by robert, 7 years ago)

From Martin Lavery and Robert Osfield, Updated examples to use a variation of the MIT License

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