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.
49 class MyGeometryCallback :
50     public osg::Drawable::UpdateCallback,
51     public osg::Drawable::AttributeFunctor
52 {
53     public:
54 
MyGeometryCallback(const osg::Vec3 & o,const osg::Vec3 & x,const osg::Vec3 & y,const osg::Vec3 & z,double period,double xphase,double amplitude)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 
update(osg::NodeVisitor * nv,osg::Drawable * drawable)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 
apply(osg::Drawable::AttributeType type,unsigned int count,osg::Vec3 * begin)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 
134 struct MyCameraPostDrawCallback : public osg::Camera::DrawCallback
135 {
MyCameraPostDrawCallbackMyCameraPostDrawCallback136     MyCameraPostDrawCallback(osg::Image* image):
137         _image(image)
138     {
139     }
140 
operator ()MyCameraPostDrawCallback141     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 
createPreRenderSubGraph(osg::Node * subgraph,unsigned tex_width,unsigned tex_height,osg::Camera::RenderTargetImplementation renderImplementation,bool useImage,bool useTextureRectangle,bool useHDR,unsigned int samples,unsigned int colorSamples)204 osg::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 
main(int argc,char ** argv)400 int 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::SEPARATE_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::ref_ptr<osg::Node> loadedModel = osgDB::readRefNodeFiles(arguments);
467 
468     // if not loaded assume no arguments passed in, try use default mode instead.
469     if (!loadedModel) loadedModel = osgDB::readRefNodeFile("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 }
493