root/OpenSceneGraph/trunk/examples/osgprerender/osgprerender.cpp @ 14152

Revision 13574, 17.8 kB (checked in by robert, 12 hours ago)

Changed the osgUI behaviour so that events are set to be handled by Widgets that have focus even if they don't directly use them.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* OpenSceneGraph example, osgprerender.
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 <osg/GLExtensions>
20#include <osg/Node>
21#include <osg/Geometry>
22#include <osg/Notify>
23#include <osg/MatrixTransform>
24#include <osg/Texture2D>
25#include <osg/TextureRectangle>
26#include <osg/Stencil>
27#include <osg/ColorMask>
28#include <osg/Depth>
29#include <osg/Billboard>
30#include <osg/Material>
31#include <osg/AnimationPath>
32
33#include <osgGA/TrackballManipulator>
34#include <osgGA/FlightManipulator>
35#include <osgGA/DriveManipulator>
36
37#include <osgUtil/SmoothingVisitor>
38
39#include <osgDB/Registry>
40#include <osgDB/ReadFile>
41#include <osgDB/WriteFile>
42
43#include <osgViewer/Viewer>
44#include <osgViewer/ViewerEventHandlers>
45
46#include <iostream>
47
48// call back which creates a deformation field to oscillate the model.
49class MyGeometryCallback :
50    public osg::Drawable::UpdateCallback,
51    public osg::Drawable::AttributeFunctor
52{
53    public:
54
55        MyGeometryCallback(const osg::Vec3& o,
56                           const osg::Vec3& x,const osg::Vec3& y,const osg::Vec3& z,
57                           double period,double xphase,double amplitude):
58            _firstCall(true),
59            _startTime(0.0),
60            _time(0.0),
61            _period(period),
62            _xphase(xphase),
63            _amplitude(amplitude),
64            _origin(o),
65            _xAxis(x),
66            _yAxis(y),
67            _zAxis(z) {}
68
69        virtual void update(osg::NodeVisitor* nv,osg::Drawable* drawable)
70        {
71            // OpenThreads::Thread::microSleep( 1000 );
72
73            const osg::FrameStamp* fs = nv->getFrameStamp();
74            double simulationTime = fs->getSimulationTime();
75            if (_firstCall)
76            {
77                _firstCall = false;
78                _startTime = simulationTime;
79            }
80
81            _time = simulationTime-_startTime;
82
83            drawable->accept(*this);
84            drawable->dirtyBound();
85
86            osg::Geometry* geometry = dynamic_cast<osg::Geometry*>(drawable);
87            if (geometry)
88            {
89                osgUtil::SmoothingVisitor::smooth(*geometry);
90            }
91
92        }
93
94        virtual void apply(osg::Drawable::AttributeType type,unsigned int count,osg::Vec3* begin)
95        {
96            if (type == osg::Drawable::VERTICES)
97            {
98                const float TwoPI=2.0f*osg::PI;
99                const float phase = -_time/_period;
100
101                osg::Vec3* end = begin+count;
102                for (osg::Vec3* itr=begin;itr<end;++itr)
103                {
104                    osg::Vec3 dv(*itr-_origin);
105                    osg::Vec3 local(dv*_xAxis,dv*_yAxis,dv*_zAxis);
106
107                    local.z() = local.x()*_amplitude*
108                                sinf(TwoPI*(phase+local.x()*_xphase));
109
110                    (*itr) = _origin +
111                             _xAxis*local.x()+
112                             _yAxis*local.y()+
113                             _zAxis*local.z();
114                }
115            }
116        }
117
118        bool    _firstCall;
119
120        double  _startTime;
121        double  _time;
122
123        double  _period;
124        double  _xphase;
125        float   _amplitude;
126
127        osg::Vec3   _origin;
128        osg::Vec3   _xAxis;
129        osg::Vec3   _yAxis;
130        osg::Vec3   _zAxis;
131
132};
133
134struct MyCameraPostDrawCallback : public osg::Camera::DrawCallback
135{
136    MyCameraPostDrawCallback(osg::Image* image):
137        _image(image)
138    {
139    }
140
141    virtual void operator () (const osg::Camera& /*camera*/) const
142    {
143        if (_image && _image->getPixelFormat()==GL_RGBA && _image->getDataType()==GL_UNSIGNED_BYTE)
144        {
145            // we'll pick out the center 1/2 of the whole image,
146            int column_start = _image->s()/4;
147            int column_end = 3*column_start;
148
149            int row_start = _image->t()/4;
150            int row_end = 3*row_start;
151
152
153            // and then invert these pixels
154            for(int r=row_start; r<row_end; ++r)
155            {
156                unsigned char* data = _image->data(column_start, r);
157                for(int c=column_start; c<column_end; ++c)
158                {
159                    (*data) = 255-(*data); ++data;
160                    (*data) = 255-(*data); ++data;
161                    (*data) = 255-(*data); ++data;
162                    (*data) = 255; ++data;
163                }
164            }
165
166
167            // dirty the image (increments the modified count) so that any textures
168            // using the image can be informed that they need to update.
169            _image->dirty();
170        }
171        else if (_image && _image->getPixelFormat()==GL_RGBA && _image->getDataType()==GL_FLOAT)
172        {
173            // we'll pick out the center 1/2 of the whole image,
174            int column_start = _image->s()/4;
175            int column_end = 3*column_start;
176
177            int row_start = _image->t()/4;
178            int row_end = 3*row_start;
179
180            // and then invert these pixels
181            for(int r=row_start; r<row_end; ++r)
182            {
183                float* data = (float*)_image->data(column_start, r);
184                for(int c=column_start; c<column_end; ++c)
185                {
186                    (*data) = 1.0f-(*data); ++data;
187                    (*data) = 1.0f-(*data); ++data;
188                    (*data) = 1.0f-(*data); ++data;
189                    (*data) = 1.0f; ++data;
190                }
191            }
192
193            // dirty the image (increments the modified count) so that any textures
194            // using the image can be informed that they need to update.
195            _image->dirty();
196        }
197
198    }
199
200    osg::Image* _image;
201};
202
203
204osg::Node* createPreRenderSubGraph(osg::Node* subgraph,
205                                   unsigned tex_width, unsigned tex_height,
206                                   osg::Camera::RenderTargetImplementation renderImplementation,
207                                   bool useImage, bool useTextureRectangle, bool useHDR,
208                                   unsigned int samples, unsigned int colorSamples)
209{
210    if (!subgraph) return 0;
211
212    // create a group to contain the flag and the pre rendering camera.
213    osg::Group* parent = new osg::Group;
214
215    // texture to render to and to use for rendering of flag.
216    osg::Texture* texture = 0;
217    if (useTextureRectangle)
218    {
219        osg::TextureRectangle* textureRect = new osg::TextureRectangle;
220        textureRect->setTextureSize(tex_width, tex_height);
221        textureRect->setInternalFormat(GL_RGBA);
222        textureRect->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
223        textureRect->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
224
225        texture = textureRect;
226    }
227    else
228    {
229        osg::Texture2D* texture2D = new osg::Texture2D;
230        texture2D->setTextureSize(tex_width, tex_height);
231        texture2D->setInternalFormat(GL_RGBA);
232        texture2D->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
233        texture2D->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
234
235        texture = texture2D;
236    }
237
238    if (useHDR)
239    {
240        texture->setInternalFormat(GL_RGBA16F_ARB);
241        texture->setSourceFormat(GL_RGBA);
242        texture->setSourceType(GL_FLOAT);
243    }
244
245    // first create the geometry of the flag of which to view.
246    {
247        // create the to visualize.
248        osg::Geometry* polyGeom = new osg::Geometry();
249
250        polyGeom->setName( "PolyGeom" );
251
252        polyGeom->setDataVariance( osg::Object::DYNAMIC );
253        polyGeom->setSupportsDisplayList(false);
254
255        osg::Vec3 origin(0.0f,0.0f,0.0f);
256        osg::Vec3 xAxis(1.0f,0.0f,0.0f);
257        osg::Vec3 yAxis(0.0f,0.0f,1.0f);
258        osg::Vec3 zAxis(0.0f,-1.0f,0.0f);
259        float height = 100.0f;
260        float width = 200.0f;
261        int noSteps = 20;
262
263        osg::Vec3Array* vertices = new osg::Vec3Array;
264        osg::Vec3 bottom = origin;
265        osg::Vec3 top = origin; top.z()+= height;
266        osg::Vec3 dv = xAxis*(width/((float)(noSteps-1)));
267
268        osg::Vec2Array* texcoords = new osg::Vec2Array;
269
270        // note, when we use TextureRectangle we have to scale the tex coords up to compensate.
271        osg::Vec2 bottom_texcoord(0.0f,0.0f);
272        osg::Vec2 top_texcoord(0.0f, useTextureRectangle ? tex_height : 1.0f);
273        osg::Vec2 dv_texcoord((useTextureRectangle ? tex_width : 1.0f)/(float)(noSteps-1),0.0f);
274
275        for(int i=0;i<noSteps;++i)
276        {
277            vertices->push_back(top);
278            vertices->push_back(bottom);
279            top+=dv;
280            bottom+=dv;
281
282            texcoords->push_back(top_texcoord);
283            texcoords->push_back(bottom_texcoord);
284            top_texcoord+=dv_texcoord;
285            bottom_texcoord+=dv_texcoord;
286        }
287
288
289        // pass the created vertex array to the points geometry object.
290        polyGeom->setVertexArray(vertices);
291
292        polyGeom->setTexCoordArray(0,texcoords);
293
294        osg::Vec4Array* colors = new osg::Vec4Array;
295        colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
296        polyGeom->setColorArray(colors, osg::Array::BIND_OVERALL);
297
298        polyGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,0,vertices->size()));
299
300        // new we need to add the texture to the Drawable, we do so by creating a
301        // StateSet to contain the Texture StateAttribute.
302        osg::StateSet* stateset = new osg::StateSet;
303
304        stateset->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON);
305
306        polyGeom->setStateSet(stateset);
307
308        polyGeom->setUpdateCallback(new MyGeometryCallback(origin,xAxis,yAxis,zAxis,1.0,1.0/width,0.2f));
309
310        osg::Geode* geode = new osg::Geode();
311        geode->addDrawable(polyGeom);
312
313        parent->addChild(geode);
314
315    }
316
317
318    // then create the camera node to do the render to texture
319    {
320        osg::Camera* camera = new osg::Camera;
321
322        // set up the background color and clear mask.
323        camera->setClearColor(osg::Vec4(0.1f,0.1f,0.3f,1.0f));
324        camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
325
326        const osg::BoundingSphere& bs = subgraph->getBound();
327        if (!bs.valid())
328        {
329            return subgraph;
330        }
331
332        float znear = 1.0f*bs.radius();
333        float zfar  = 3.0f*bs.radius();
334
335        // 2:1 aspect ratio as per flag geometry below.
336        float proj_top   = 0.25f*znear;
337        float proj_right = 0.5f*znear;
338
339        znear *= 0.9f;
340        zfar *= 1.1f;
341
342        // set up projection.
343        camera->setProjectionMatrixAsFrustum(-proj_right,proj_right,-proj_top,proj_top,znear,zfar);
344
345        // set view
346        camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
347        camera->setViewMatrixAsLookAt(bs.center()-osg::Vec3(0.0f,2.0f,0.0f)*bs.radius(),bs.center(),osg::Vec3(0.0f,0.0f,1.0f));
348
349        // set viewport
350        camera->setViewport(0,0,tex_width,tex_height);
351
352        // set the camera to render before the main camera.
353        camera->setRenderOrder(osg::Camera::PRE_RENDER);
354
355        // tell the camera to use OpenGL frame buffer object where supported.
356        camera->setRenderTargetImplementation(renderImplementation);
357
358
359        if (useImage)
360        {
361            osg::Image* image = new osg::Image;
362            //image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
363            image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT);
364
365            // attach the image so its copied on each frame.
366            camera->attach(osg::Camera::COLOR_BUFFER, image,
367                           samples, colorSamples);
368
369            camera->setPostDrawCallback(new MyCameraPostDrawCallback(image));
370
371            // Rather than attach the texture directly to illustrate the texture's ability to
372            // detect an image update and to subload the image onto the texture.  You needn't
373            // do this when using an Image for copying to, as a separate camera->attach(..)
374            // would suffice as well, but we'll do it the long way round here just for demonstration
375            // purposes (long way round meaning we'll need to copy image to main memory, then
376            // copy it back to the graphics card to the texture in one frame).
377            // The long way round allows us to manually modify the copied image via the callback
378            // and then let this modified image by reloaded back.
379            texture->setImage(0, image);
380        }
381        else
382        {
383            // attach the texture and use it as the color buffer.
384            camera->attach(osg::Camera::COLOR_BUFFER, texture,
385                           0, 0, false,
386                           samples, colorSamples);
387        }
388
389
390        // add subgraph to render
391        camera->addChild(subgraph);
392
393        parent->addChild(camera);
394
395    }
396
397    return parent;
398}
399
400int main( int argc, char **argv )
401{
402    // use an ArgumentParser object to manage the program arguments.
403    osg::ArgumentParser arguments(&argc,argv);
404
405    // set up the usage document, in case we need to print out how to use this program.
406    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates pre rendering of scene to a texture, and then apply this texture to geometry.");
407    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
408    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
409    arguments.getApplicationUsage()->addCommandLineOption("--fbo","Use Frame Buffer Object for render to texture, where supported.");
410    arguments.getApplicationUsage()->addCommandLineOption("--fb","Use FrameBuffer for render to texture.");
411    arguments.getApplicationUsage()->addCommandLineOption("--pbuffer","Use Pixel Buffer for render to texture, where supported.");
412    arguments.getApplicationUsage()->addCommandLineOption("--window","Use a separate Window for render to texture.");
413    arguments.getApplicationUsage()->addCommandLineOption("--width","Set the width of the render to texture.");
414    arguments.getApplicationUsage()->addCommandLineOption("--height","Set the height of the render to texture.");
415    arguments.getApplicationUsage()->addCommandLineOption("--image","Render to an image, then apply a post draw callback to it, and use this image to update a texture.");
416    arguments.getApplicationUsage()->addCommandLineOption("--texture-rectangle","Use osg::TextureRectangle for doing the render to texture to.");
417
418    // construct the viewer.
419    osgViewer::Viewer viewer(arguments);
420
421    // add stats
422    viewer.addEventHandler( new osgViewer::StatsHandler() );
423
424    // add the record camera path handler
425    viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);
426
427    // add the threading handler
428    viewer.addEventHandler( new osgViewer::ThreadingHandler() );
429
430    // if user request help write it out to cout.
431    if (arguments.read("-h") || arguments.read("--help"))
432    {
433        arguments.getApplicationUsage()->write(std::cout);
434        return 1;
435    }
436
437    unsigned int tex_width = 1024;
438    unsigned int tex_height = 512;
439    unsigned int samples = 0;
440    unsigned int colorSamples = 0;
441
442    while (arguments.read("--width", tex_width)) {}
443    while (arguments.read("--height", tex_height)) {}
444
445    osg::Camera::RenderTargetImplementation renderImplementation = osg::Camera::FRAME_BUFFER_OBJECT;
446
447    while (arguments.read("--fbo")) { renderImplementation = osg::Camera::FRAME_BUFFER_OBJECT; }
448    while (arguments.read("--pbuffer")) { renderImplementation = osg::Camera::PIXEL_BUFFER; }
449    while (arguments.read("--pbuffer-rtt")) { renderImplementation = osg::Camera::PIXEL_BUFFER_RTT; }
450    while (arguments.read("--fb")) { renderImplementation = osg::Camera::FRAME_BUFFER; }
451    while (arguments.read("--window")) { renderImplementation = osg::Camera::SEPERATE_WINDOW; }
452    while (arguments.read("--fbo-samples", samples)) {}
453    while (arguments.read("--color-samples", colorSamples)) {}
454
455    bool useImage = false;
456    while (arguments.read("--image")) { useImage = true; }
457
458    bool useTextureRectangle = false;
459    while (arguments.read("--texture-rectangle")) { useTextureRectangle = true; }
460
461    bool useHDR = false;
462    while (arguments.read("--hdr")) { useHDR = true; }
463
464
465    // load the nodes from the commandline arguments.
466    osg::Node* loadedModel = osgDB::readNodeFiles(arguments);
467
468    // if not loaded assume no arguments passed in, try use default mode instead.
469    if (!loadedModel) loadedModel = osgDB::readNodeFile("cessna.osgt");
470
471    if (!loadedModel)
472    {
473        return 1;
474    }
475
476    // create a transform to spin the model.
477    osg::MatrixTransform* loadedModelTransform = new osg::MatrixTransform;
478    loadedModelTransform->addChild(loadedModel);
479
480    osg::NodeCallback* nc = new osg::AnimationPathCallback(loadedModelTransform->getBound().center(),osg::Vec3(0.0f,0.0f,1.0f),osg::inDegrees(45.0f));
481    loadedModelTransform->setUpdateCallback(nc);
482
483    osg::Group* rootNode = new osg::Group();
484    rootNode->addChild(createPreRenderSubGraph(loadedModelTransform,tex_width,tex_height, renderImplementation, useImage, useTextureRectangle, useHDR, samples, colorSamples));
485
486    //osgDB::writeNodeFile(*rootNode, "test.osgb");
487
488    // add model to the viewer.
489    viewer.setSceneData( rootNode );
490
491    return viewer.run();
492}
Note: See TracBrowser for help on using the browser.