root/OpenSceneGraph/trunk/src/osgSim/Impostor.cpp @ 13041

Revision 13041, 14.7 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13#include <osgSim/Impostor>
14
15#include <algorithm>
16
17using namespace osg;
18using namespace osgSim;
19
20// use this cull callback to allow the camera to traverse the Impostor's children without
21// actuall having them assigned as children to the camea itself.  This make the camera a
22// decorator without ever directly being assigned to it.
23class ImpostorTraverseNodeCallback : public osg::NodeCallback
24{
25public:
26
27    ImpostorTraverseNodeCallback(osgSim::Impostor* node):_node(node) {}
28
29    virtual void operator()(osg::Node*, osg::NodeVisitor* nv)
30    {
31        _node->LOD::traverse(*nv);
32    }
33
34    osgSim::Impostor* _node;
35};
36
37Impostor::Impostor()
38{
39    _impostorThreshold = -1.0f;
40}
41
42
43ImpostorSprite* Impostor::findBestImpostorSprite(unsigned int contextID, const osg::Vec3& currLocalEyePoint) const
44{
45    ImpostorSpriteList& impostorSpriteList = _impostorSpriteListBuffer[contextID];
46
47    float min_distance2 = FLT_MAX;
48    ImpostorSprite* impostorSprite = NULL;
49    for(ImpostorSpriteList::iterator itr=impostorSpriteList.begin();
50        itr!=impostorSpriteList.end();
51        ++itr)
52    {
53        float distance2 = (currLocalEyePoint-(*itr)->getStoredLocalEyePoint()).length2();
54        if (distance2<min_distance2)
55        {
56            min_distance2 = distance2;
57            impostorSprite = itr->get();
58        }
59    }
60    return impostorSprite;
61}
62
63void Impostor::addImpostorSprite(unsigned int contextID, ImpostorSprite* is)
64{
65    if (is && is->getParent()!=this)
66    {
67        ImpostorSpriteList& impostorSpriteList = _impostorSpriteListBuffer[contextID];
68
69        // add it to my impostor list first, so it remains referenced
70        // when its reference in the previous_owner is removed.
71        impostorSpriteList.push_back(is);
72
73        if (is->getParent())
74        {
75            Impostor* previous_owner = is->getParent();
76            ImpostorSpriteList& isl = previous_owner->_impostorSpriteListBuffer[contextID];
77
78            // find and erase reference to is.
79            for(ImpostorSpriteList::iterator itr=isl.begin();
80                itr!=isl.end();
81                ++itr)
82            {
83                if ((*itr)==is)
84                {
85                    isl.erase(itr);
86                    break;
87                }
88            }
89        }
90        is->setParent(this);
91
92    }
93}
94
95osg::BoundingSphere Impostor::computeBound() const
96{
97    return LOD::computeBound();
98}
99
100inline osgUtil::CullVisitor::value_type distance(const osg::Vec3& coord,const osg::Matrix& matrix)
101{
102
103    //std::cout << "distance("<<coord<<", "<<matrix<<")"<<std::endl;
104
105    return -((osgUtil::CullVisitor::value_type)coord[0]*(osgUtil::CullVisitor::value_type)matrix(0,2)+(osgUtil::CullVisitor::value_type)coord[1]*(osgUtil::CullVisitor::value_type)matrix(1,2)+(osgUtil::CullVisitor::value_type)coord[2]*(osgUtil::CullVisitor::value_type)matrix(2,2)+matrix(3,2));
106}
107
108void Impostor::traverse(osg::NodeVisitor& nv)
109{
110    if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)
111    {
112        LOD::traverse(nv);
113        return;
114    }
115
116    osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
117    if (!cv)
118    {
119        LOD::traverse(nv);
120        return;
121    }
122
123
124    osg::Vec3 eyeLocal = nv.getEyePoint();
125    const BoundingSphere& bs = getBound();
126
127    unsigned int contextID = cv->getState() ? cv->getState()->getContextID() : 0;
128
129    float distance2 = (eyeLocal-bs.center()).length2();
130    float LODScale = cv->getLODScale();
131    if (!cv->getImpostorsActive() ||
132        distance2*LODScale*LODScale<osg::square(getImpostorThreshold()) ||
133        distance2<bs.radius2()*2.0f)
134    {
135        // outwith the impostor distance threshold therefore simple
136        // traverse the appropriate child of the LOD.
137        LOD::traverse(nv);
138    }
139    else
140    {
141
142        // within the impostor distance threshold therefore attempt
143        // to use impostor instead.
144
145        RefMatrix& matrix = *cv->getModelViewMatrix();
146
147        // search for the best fit ImpostorSprite;
148        ImpostorSprite* impostorSprite = findBestImpostorSprite(contextID,eyeLocal);
149
150        if (impostorSprite)
151        {
152            // impostor found, now check to see if it is good enough to use
153            float error = impostorSprite->calcPixelError(*(cv->getMVPW()));
154
155            if (error>cv->getImpostorPixelErrorThreshold())
156            {
157                // chosen impostor sprite pixel error is too great to use
158                // from this eye point, therefore invalidate it.
159                impostorSprite=NULL;
160            }
161        }
162
163
164// need to think about sprite reuse and support for multiple context's.
165
166        if (impostorSprite==NULL)
167        {
168            // no appropriate sprite has been found therefore need to create
169            // one for use
170
171            // create the impostor sprite.
172            impostorSprite = createImpostorSprite(cv);
173
174            //if (impostorSprite) impostorSprite->_color.set(0.0f,0.0f,1.0f,1.0f);
175
176        }
177        //else impostorSprite->_color.set(1.0f,1.0f,1.0f,1.0f);
178
179        if (impostorSprite)
180        {
181
182            // update frame number to show that impostor is in action.
183            impostorSprite->setLastFrameUsed(cv->getTraversalNumber());
184
185            if (cv->getComputeNearFarMode()) cv->updateCalculatedNearFar(matrix,*impostorSprite, false);
186
187            StateSet* stateset = impostorSprite->getStateSet();
188
189            if (stateset) cv->pushStateSet(stateset);
190
191            cv->addDrawableAndDepth(impostorSprite, &matrix, distance(getCenter(),matrix));
192
193            if (stateset) cv->popStateSet();
194
195
196        }
197        else
198        {
199           // no impostor has been selected or created so default to
200           // traversing the usual LOD selected child.
201            LOD::traverse(nv);
202        }
203
204    }
205}
206
207ImpostorSprite* Impostor::createImpostorSprite(osgUtil::CullVisitor* cv)
208{
209    unsigned int contextID = cv->getState() ? cv->getState()->getContextID() : 0;
210
211     osgSim::ImpostorSpriteManager* impostorSpriteManager = dynamic_cast<osgSim::ImpostorSpriteManager*>(cv->getUserData());
212     if (!impostorSpriteManager)
213     {
214          impostorSpriteManager = new osgSim::ImpostorSpriteManager;
215          cv->setUserData(impostorSpriteManager);
216     }
217
218
219    // default to true right now, will dertermine if perspective from the
220    // projection matrix...
221    bool isPerspectiveProjection = true;
222
223    const Matrix& matrix = *(cv->getModelViewMatrix());
224    const BoundingSphere& bs = getBound();
225    osg::Vec3 eye_local = cv->getEyeLocal();
226
227    if (!bs.valid())
228    {
229        OSG_WARN << "bb invalid"<<std::endl;
230        return NULL;
231    }
232
233    Vec3 center_local = bs.center();
234    Vec3 camera_up_local = cv->getUpLocal();
235    Vec3 lv_local = center_local-eye_local;
236
237    float distance_local = lv_local.length();
238    lv_local /= distance_local;
239
240    Vec3 sv_local = lv_local^camera_up_local;
241    sv_local.normalize();
242
243    Vec3 up_local = sv_local^lv_local;
244
245
246    float width = bs.radius();
247    if (isPerspectiveProjection)
248    {
249        // expand the width to account for projection onto sprite.
250        width *= (distance_local/sqrtf(distance_local*distance_local-bs.radius2()));
251    }
252
253    // scale up and side vectors to sprite width.
254    up_local *= width;
255    sv_local *= width;
256
257    // create the corners of the sprite.
258    Vec3 c00(center_local - sv_local - up_local);
259    Vec3 c10(center_local + sv_local - up_local);
260    Vec3 c01(center_local - sv_local + up_local);
261    Vec3 c11(center_local + sv_local + up_local);
262
263    // calc texture size for eye, bs.
264
265    // convert the corners of the sprite (in world coords) into their
266    // equivalent window coordinates by using the camera's project method.
267    const osg::Matrix& MVPW = *(cv->getMVPW());
268    Vec3 c00_win = c00 * MVPW;
269    Vec3 c11_win = c11 * MVPW;
270
271    // adjust texture size to be nearest power of 2.
272
273    float s  = c11_win.x()-c00_win.x();
274    float t  = c11_win.y()-c00_win.y();
275
276    // may need to reverse sign of width or height if a matrix has
277    // been applied which flips the orientation of this subgraph.
278    if (s<0.0f) s = -s;
279    if (t<0.0f) t = -t;
280
281    // bias value used to assist the rounding up or down of
282    // the texture dimensions to the nearest power of two.
283    // bias near 0.0 will almost always round down.
284    // bias near 1.0 will almost always round up.
285    float bias = 0.7f;
286
287    float sp2 = logf((float)s)/logf(2.0f);
288    float rounded_sp2 = floorf(sp2+bias);
289    int new_s = (int)(powf(2.0f,rounded_sp2));
290
291    float tp2 = logf((float)t)/logf(2.0f);
292    float rounded_tp2 = floorf(tp2+bias);
293    int new_t = (int)(powf(2.0f,rounded_tp2));
294
295    const osg::Viewport& viewport = *(cv->getViewport());
296
297    // if dimension is bigger than window divide it down.
298    while (new_s>viewport.width()) new_s /= 2;
299
300    // if dimension is bigger than window divide it down.
301    while (new_t>viewport.height()) new_t /= 2;
302
303    // create the impostor sprite.
304    ImpostorSprite* impostorSprite =
305        impostorSpriteManager->createOrReuseImpostorSprite(new_s,new_t,cv->getTraversalNumber()-cv->getNumberOfFrameToKeepImpostorSprites());
306
307    if (impostorSprite==NULL)
308    {
309        OSG_WARN<<"Warning: unable to create required impostor sprite."<<std::endl;
310        return NULL;
311    }
312
313    // update frame number to show that impostor is in action.
314    impostorSprite->setLastFrameUsed(cv->getTraversalNumber());
315
316
317    // have successfully created an impostor sprite so now need to
318    // add it into the impostor.
319    addImpostorSprite(contextID,impostorSprite);
320
321    if (cv->getDepthSortImpostorSprites())
322    {
323        // the depth sort bin should probably be user definable,
324        // will look into this later. RO July 2001.
325        StateSet* stateset = impostorSprite->getStateSet();
326        stateset->setRenderBinDetails(10,"DepthSortedBin");
327    }
328
329    osg::Texture2D* texture = impostorSprite->getTexture();
330
331    texture->setTextureSize(new_s, new_t);
332    texture->setInternalFormat(GL_RGBA);
333    texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
334    texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
335
336    // update frame number to show that impostor is in action.
337    impostorSprite->setLastFrameUsed(cv->getTraversalNumber());
338
339    Vec3* coords = impostorSprite->getCoords();
340    Vec2* texcoords = impostorSprite->getTexCoords();
341
342    coords[0] = c01;
343    texcoords[0].set(0.0f,1.0f);
344
345    coords[1] = c00;
346    texcoords[1].set(0.0f,0.0f);
347
348    coords[2] = c10;
349    texcoords[2].set(1.0f,0.0f);
350
351    coords[3] = c11;
352    texcoords[3].set(1.0f,1.0f);
353
354    impostorSprite->dirtyBound();
355
356    Vec3* controlcoords = impostorSprite->getControlCoords();
357
358    if (isPerspectiveProjection)
359    {
360        // deal with projection issue by moving the coorners of the quad
361        // towards the eye point.
362        float ratio = width/(center_local-eye_local).length();
363        float one_minus_ratio = 1.0f-ratio;
364        Vec3 eye_local_ratio = eye_local*ratio;
365
366        controlcoords[0] = coords[0]*one_minus_ratio + eye_local_ratio;
367        controlcoords[1] = coords[1]*one_minus_ratio + eye_local_ratio;
368        controlcoords[2] = coords[2]*one_minus_ratio + eye_local_ratio;
369        controlcoords[3] = coords[3]*one_minus_ratio + eye_local_ratio;
370    }
371    else
372    {
373        // project the control points forward towards the eyepoint,
374        // but since this an othographics projection this projection is
375        // parallel.
376        Vec3 dv = lv_local*width;
377
378        controlcoords[0] = coords[0]-dv;
379        controlcoords[1] = coords[1]-dv;
380        controlcoords[2] = coords[2]-dv;
381        controlcoords[3] = coords[3]-dv;
382    }
383
384    impostorSprite->setStoredLocalEyePoint(eye_local);
385
386    Vec3 eye_world(0.0,0.0,0.0);
387    Vec3 center_world = bs.center()*matrix;
388
389
390    osg::Camera* camera = impostorSprite->getCamera();
391    if (!camera)
392    {
393        camera = new osg::Camera;
394        impostorSprite->setCamera(camera);
395    }
396
397    camera->setCullCallback(new ImpostorTraverseNodeCallback(this));
398
399    osgUtil::RenderStage* previous_stage = cv->getRenderStage();
400
401    // set up the background color and clear mask.
402    osg::Vec4 clear_color = previous_stage->getClearColor();
403    clear_color[3] = 0.0f; // set thae alpha to zero.
404    camera->setClearColor(clear_color);
405    camera->setClearMask(previous_stage->getClearMask());
406
407
408// adjust camera left,right,up,down to fit (in world coords)
409
410    Vec3 near_local  ( center_local-lv_local*width );
411    Vec3 far_local   ( center_local+lv_local*width );
412    Vec3 top_local   ( center_local+up_local);
413    Vec3 right_local ( center_local+sv_local);
414
415    Vec3 near_world = near_local * matrix;
416    Vec3 far_world = far_local * matrix;
417    Vec3 top_world = top_local * matrix;
418    Vec3 right_world = right_local * matrix;
419
420    float znear = (near_world-eye_world).length();
421    float zfar  = (far_world-eye_world).length();
422
423    float top   = (top_world-center_world).length();
424    float right = (right_world-center_world).length();
425
426    znear *= 0.9f;
427    zfar *= 1.1f;
428
429    // set up projection.
430    if (isPerspectiveProjection)
431    {
432        // deal with projection issue move the top and right points
433        // onto the near plane.
434        float ratio = znear/(center_world-eye_world).length();
435        top *= ratio;
436        right *= ratio;
437        camera->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar);
438    }
439    else
440    {
441        camera->setProjectionMatrixAsOrtho(-right,right,-top,top,znear,zfar);
442    }
443
444    Vec3 rotate_from = bs.center()-eye_local;
445    Vec3 rotate_to   = cv-> getLookVectorLocal();
446
447    osg::Matrix rotate_matrix =
448        osg::Matrix::translate(-eye_local)*
449        osg::Matrix::rotate(rotate_from,rotate_to)*
450        osg::Matrix::translate(eye_local)*
451        *cv->getModelViewMatrix();
452
453    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
454    camera->setViewMatrix(rotate_matrix);
455
456    camera->setViewport(0,0,new_s,new_t);
457
458    // tell the camera to use OpenGL frame buffer object where supported.
459    camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::FRAME_BUFFER);
460
461    // set the camera to render before the main camera.
462    camera->setRenderOrder(osg::Camera::PRE_RENDER);
463
464    // attach the texture and use it as the color buffer.
465    camera->attach(osg::Camera::COLOR_BUFFER, texture);
466
467    // do the cull traversal on the subgraph
468    camera->accept(*cv);
469
470    return impostorSprite;
471
472}
Note: See TracBrowser for help on using the browser.