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