1 //info : osgSSBO example,testing ShaderStorageBufferObjects ,Markus Hein,  2014, osg-3.2.1
2 //required hardware and driver must support GL >=  GL 4.3 or GL ES 3.1 (GL ES not tested, would be nice if someone will test it on a small device)
3 
4 //testing osg support for Shader Storage Buffer Objects
5 
6 //version: "first take" from last night session..
7 
8 
9 
10 #include <osg/StateAttributeCallback>
11 #include <osg/Texture2D>
12 #include <osg/Geometry>
13 #include <osg/DispatchCompute>
14 #include <osgDB/ReadFile>
15 #include <osgGA/StateSetManipulator>
16 #include <osgViewer/Viewer>
17 #include <osgViewer/ViewerEventHandlers>
18 
19 #include <osg/Node>
20 #include <osg/PositionAttitudeTransform>
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/Depth>
28 #include <osg/Billboard>
29 #include <osg/Material>
30 #include <osg/AnimationPath>
31 
32 #include <osgGA/TrackballManipulator>
33 #include <osgGA/FlightManipulator>
34 #include <osgGA/DriveManipulator>
35 
36 #include <osgUtil/SmoothingVisitor>
37 
38 #include <osgDB/Registry>
39 #include <osgDB/ReadFile>
40 #include <osgDB/WriteFile>
41 
42 #include <osgViewer/Viewer>
43 #include <osgViewer/ViewerEventHandlers>
44 #include <osgViewer/Renderer>
45 
46 
47 #include <osg/Array>
48 #include <osg/BoundingSphere>
49 #include <osg/BufferIndexBinding>
50 #include <osg/BufferObject>
51 #include <osg/Group>
52 #include <osg/Math>
53 #include <osg/MatrixTransform>
54 #include <osg/Program>
55 #include <osg/Shader>
56 #include <osg/Drawable>
57 #include <osg/CopyOp>
58 #include <osg/State>
59 #include <osg/Matrix>
60 #include <osg/ShapeDrawable>
61 #include <osg/GL>
62 #include <osg/StateSet>
63 #include <osg/Texture2D>
64 #include <osg/BlendFunc>
65 #include <osg/TexEnv>
66 #include <osg/Material>
67 #include <osg/PointSprite>
68 #include <osg/Program>
69 #include <osg/Notify>
70 #include <osg/Point>
71 #include <osg/io_utils>
72 #include <osg/VertexProgram>
73 
74 #include <osgText/Font>
75 #include <osgText/Text>
76 
77 
78 #include <osgDB/ReadFile>
79 #include <osgDB/WriteFile>
80 #include <osgDB/FileNameUtils>
81 #include <osgUtil/Optimizer>
82 #include <iostream>
83 #include <typeinfo>
84 
85 
86 using namespace osg;
87 
88 
89 //todo .. #define COMPUTATION_IN_SEPARATE_THREAD
90 
91 #define WORK_GROUP_SIZE 16
92 
93 
94 #define PRERENDER_ANTIALIASINGMULTISAMPLES 16
95 #define PRERENDER_HIGH_QUALITY_ANTIALIASING
96 #define PRERENDER_WIDTH 1920
97 #define PRERENDER_HEIGHT 1080
98 
99 
100 #define SUB_PLACEMENT_OFFSET_HORIZONTAL  0.5
101 #define SUB_PLACEMENT_OFFSET_VERTICAL  0.5
102 
103 enum BufferOffset
104 {
105     POSITION_NOW_OFFSET,
106     POSITION_OLD_OFFSET,
107     POSITION_INIT_OFFSET,
108 
109     VELOCITY_NOW_OFFSET,
110     VELOCITY_OLD_OFFSET,
111     VELOCITY_INIT_OFFSET,
112 
113     ACCELERATION_OFFSET,
114     PROPERTIES_OFFSET,
115 
116     OFFSET_END
117 };
118 
119 
120 const int __numDataValuesPerChannel = OFFSET_END;
121 const int __numChannels = 4;
122 
123 //512x512x4x7 = 7.340.032 floats in SSBO on GPU
124 const int NUM_ELEMENTS_X = 512;
125 const int NUM_ELEMENTS_Y = 512;
126 
random(float min,float max)127 float random(float min, float max) { return min + (max - min)*(float)rand() / (float)RAND_MAX; }
128 
129 
130 enum Channel
131 {
132     RED_CHANNEL,
133     GREEN_CHANNEL,
134     BLUE_CHANNEL,
135     ALPHA_CHANNEL,
136     RGB_CHANNEL,
137     RGBA_CHANNEL
138 };
139 
140 
141 
142 class ShaderStorageBufferCallback : public osg::StateAttributeCallback
143 {
144 public:
operator ()(osg::StateAttribute *,osg::NodeVisitor *)145     void operator() (osg::StateAttribute* /*attr*/, osg::NodeVisitor* /*nv*/)
146     {
147         //if you need to process the data in your app-code , better leaving it on GPU and processing there, uploading per frame will make it slow
148 #if 0
149         osg::ShaderStorageBufferBinding* ssbb = static_cast<osg::ShaderStorageBufferBinding*>(attr);
150         osg::ShaderStorageBufferObject* ssbo
151             = static_cast<osg::ShaderStorageBufferObject*>(ssbb->getBufferObject());
152 
153         osg::FloatArray* array = static_cast<osg::FloatArray*>(ssbo->getBufferData(0));
154 
155         float someValue = array->at(0);
156         //std::cout << "someValue now: " << someValue << std::endl;
157         //data transfer performance test
158         //    array->dirty();
159 #endif
160     }
161 };
162 
163 
164 //do not forget to set OSG_FILE_PATH to default OSG-Data and make sure the new shaders are copied there under"shaders"
165 class ComputeNode : public osg::PositionAttitudeTransform
166 {
167 
168 public:
169 
170     osg::ref_ptr<osg::DispatchCompute>                _DispatchCompute;
171     osg::ref_ptr<osg::Program>                        _computeProgram;
172     osg::ref_ptr<osg::Shader>                        _computeShader;        //compute and write position data in SSBO
173 
174     osg::ref_ptr<osg::Shader>                        _vertexShader;        //reading position data from SSBO (OBS!: make sure glMemoryBuffer() is syncing this)
175     osg::ref_ptr<osg::Shader>                        _geometryShader;    //building a quad looking to the camera
176     osg::ref_ptr<osg::Shader>                        _fragmentShader;    //use false-colors etc. for making your data visible
177 
178     osg::ref_ptr<osg::Node>                            _helperNode;        // coordinate system node
179 
180     ref_ptr<osg::ShaderStorageBufferObject>            _ssbo;
181     ref_ptr<osg::ShaderStorageBufferBinding>        _ssbb;
182 
183     GLfloat*                                        _data;        // some data we upload to GPU, initialised with random values
184     osg::ref_ptr<FloatArray>                        _dataArray; //
185 
186     osg::ref_ptr<osg::Group>                        _computationResultsRenderGroup;
187     osg::ref_ptr<osg::Program>                        _computationResultsRenderProgram;
188     osg::ref_ptr<osg::StateSet>                        _computationResultsRenderStateSet;
189 
190 
191     std::string                                        _computeShaderSourcePath;
192     std::string                                        _vertexShaderSourcePath;
193     std::string                                        _geometryShaderSourcePath;
194     std::string                                        _fragmentShaderSourcePath;
195 
196 
197     void                addHelperGeometry();
198     void                addDataMonitor(osg::Vec3 placement, osg::Vec3 relativePlacement, float scale, Channel channel, BufferOffset shaderBufferOffset, std::string labelcaption, float minDataRange, float maxDataRange);
199     void                addComputationResultsRenderTree();
200     void                initComputingSetup();
201 
ComputeNode()202     ComputeNode()
203     {
204         _computeShaderSourcePath = "shaders/osgssboComputeShader.cs";
205         _vertexShaderSourcePath = "shaders/osgssboVertexShader.vs";
206         _geometryShaderSourcePath = "shaders/osgssboGeometryShader.gs";
207         _fragmentShaderSourcePath = "shaders/osgssboFragmentShader.fs";
208 	_DispatchCompute=new osg::DispatchCompute();
209         addChild(_DispatchCompute);
210     }
211 
212 };
213 
214 
215 class ComputeNodeUpdateCallback : public osg::NodeCallback
216 {
217 public:
218 
219     ComputeNode* _computeNode;
220     osg::Timer_t _prevShaderUpdateTime;
221     osg::Timer _timer;
222 
ComputeNodeUpdateCallback()223     ComputeNodeUpdateCallback(){}
224 
ComputeNodeUpdateCallback(ComputeNode * computeNode)225     ComputeNodeUpdateCallback(ComputeNode* computeNode)
226     {
227         _computeNode = computeNode;
228         _prevShaderUpdateTime = 0;
229     }
230 
operator ()(osg::Node * node,osg::NodeVisitor * nv)231     virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
232     {
233         osg::Timer_t currTime = _timer.tick();
234 
235         if (_timer.delta_s(_prevShaderUpdateTime, currTime) > 1.0) //one  second interval for shader-changed-do-reload check
236         {
237             osg::ref_ptr<osg::Shader> reloadedshader;
238             std::string runningSource;
239             std::string reloadedstring;
240 
241             if (_computeNode->_computeShader.valid())
242             {
243                 runningSource = _computeNode->_computeShader->getShaderSource();
244                 reloadedshader = osgDB::readRefShaderFile(osg::Shader::COMPUTE, _computeNode->_computeShaderSourcePath);
245 
246                 reloadedstring = reloadedshader->getShaderSource();
247                 if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str()))
248                 {
249                     _computeNode->_computeProgram->removeShader(_computeNode->_computeShader.get());
250                     _computeNode->_computeShader = reloadedshader.get();
251                     _computeNode->_computeProgram->addShader(_computeNode->_computeShader.get());
252                 }
253             }
254 
255             if (_computeNode->_vertexShader.valid())
256             {
257 
258                 runningSource = _computeNode->_vertexShader->getShaderSource();
259                 reloadedshader = osgDB::readRefShaderFile(osg::Shader::VERTEX, _computeNode->_vertexShaderSourcePath);
260 
261                 reloadedstring = reloadedshader->getShaderSource();
262                 if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str()))
263                 {
264                     _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_vertexShader.get());
265                     _computeNode->_vertexShader = reloadedshader.get();
266                     _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_vertexShader.get());
267                 }
268             }
269 
270 
271 
272             if (_computeNode->_geometryShader.valid())
273             {
274                 runningSource = _computeNode->_geometryShader->getShaderSource();
275                 reloadedshader = osgDB::readRefShaderFile(osg::Shader::GEOMETRY, _computeNode->_geometryShaderSourcePath);
276 
277                 reloadedstring = reloadedshader->getShaderSource();
278                 if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str()))
279                 {
280                     _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_geometryShader.get());
281                     _computeNode->_geometryShader = reloadedshader.get();
282                     _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_geometryShader.get());
283                 }
284             }
285 
286             if (_computeNode->_fragmentShader.valid())
287             {
288                 runningSource = _computeNode->_fragmentShader->getShaderSource();
289                 reloadedshader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, _computeNode->_fragmentShaderSourcePath);
290 
291                 reloadedstring = reloadedshader->getShaderSource();
292                 if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str()))
293                 {
294                     _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_fragmentShader.get());
295                     _computeNode->_fragmentShader = reloadedshader.get();
296                     _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_fragmentShader.get());
297                 }
298             }
299 
300 
301             _prevShaderUpdateTime = _timer.tick();
302         }
303 
304         traverse(node, nv);
305 
306     }
307 };
308 
309 //set  OSG_FILE_PATH for loading axes.osgt
addHelperGeometry()310 void ComputeNode::addHelperGeometry()
311 {
312     _helperNode = osgDB::readRefNodeFile("axes.osgt");
313 
314     if (_helperNode.valid())
315     {
316         addChild(_helperNode.get());
317     }
318 
319     //osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform;
320     //pat->setPosition(osg::Vec3(0.5, 0, 0.5));
321     //osg::Geode *sphereGeode = new osg::Geode;
322     //float radius = 0.5f;
323     //osg::TessellationHints* hints = new osg::TessellationHints;
324     //hints->setDetailRatio(0.9f);
325     //osg::ShapeDrawable* sphere = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f, 0.0f, 0.0f), radius), hints);
326     //sphereGeode->addDrawable(sphere);
327     //sphere->setColor(osg::Vec4(0, 1, 0, 0.1));
328     //osg::StateSet* stateset = sphereGeode->getOrCreateStateSet();
329     //osg::BlendFunc *blend = new osg::BlendFunc;
330     //blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
331     //stateset->setAttributeAndModes(blend, osg::StateAttribute::ON);
332     //pat->addChild(sphereGeode);
333     //addChild(pat);
334 }
335 
336 
337 
338 
339 
340 
addDataMonitor(osg::Vec3 placement,osg::Vec3 relativePlacement,float scale,Channel colorchannel,BufferOffset shaderStorageBufferOffset,std::string labelCaption,float minDataRange,float maxDataRange)341 void ComputeNode::addDataMonitor(osg::Vec3 placement, osg::Vec3 relativePlacement, float scale, Channel colorchannel, BufferOffset shaderStorageBufferOffset, std::string labelCaption, float minDataRange, float maxDataRange)
342 {
343     osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform;
344     pat->setPosition(relativePlacement);
345     addChild(pat);
346     osg::Geometry* geom;
347 
348     if (NUM_ELEMENTS_X >= NUM_ELEMENTS_Y)
349     {
350         float ratio = (float)((float)NUM_ELEMENTS_Y / (float)NUM_ELEMENTS_X);
351         geom = osg::createTexturedQuadGeometry(placement, osg::Vec3(1.0f*scale, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, ratio*1.0f*scale));
352     }
353     else
354     {
355         float ratio = (float)((float)NUM_ELEMENTS_X / (float)NUM_ELEMENTS_Y);
356         geom = osg::createTexturedQuadGeometry(placement, osg::Vec3(ratio*1.0f*scale, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, 1.0f*scale));
357 
358     }
359 
360     geom->setVertexAttribArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);
361 
362     osg::ref_ptr<osg::Geode> quad = new osg::Geode;
363     quad->addDrawable(geom);
364     quad->setStateSet(getOrCreateStateSet());
365     pat->addChild(quad.get());
366 
367     static const char* vertexShaderSrcChannelMonitor = {
368 
369         "#version 430  \n"
370 
371         "uniform int numRows;\n"
372         "uniform int numCols;\n"
373         "uniform float osg_FrameTime;\n"
374         "uniform mat4 osg_ProjectionMatrix;\n"
375         "uniform mat4 osg_ModelViewMatrix;\n"
376         "out vec2 texCoordFromVertexShader;\n"
377         "struct particle{ float    x; float y; float z; float w;};"
378         "layout (location = 0) in vec3 vertexpos;\n"
379         "attribute vec2 tex_coords;\n"
380         "void main() {\n"
381         "texCoordFromVertexShader.xy = tex_coords.xy;  gl_Position = ( osg_ProjectionMatrix * osg_ModelViewMatrix * vec4(vertexpos.x,vertexpos.y,vertexpos.z,1) ); \n"
382         "}\n"
383     };
384 
385 
386 
387 
388     std::stringstream fragmentshaderstringstreamChannelMonitor;
389     fragmentshaderstringstreamChannelMonitor << "#version 430\n";
390     fragmentshaderstringstreamChannelMonitor << "uniform int numRows;\n";
391     fragmentshaderstringstreamChannelMonitor << "uniform int numCols;\n";
392     fragmentshaderstringstreamChannelMonitor << "uniform float dataRangeMin;\n";
393     fragmentshaderstringstreamChannelMonitor << "uniform float dataRangeMax;\n";
394     fragmentshaderstringstreamChannelMonitor << "in vec2 texCoordFromVertexShader;\n";
395     fragmentshaderstringstreamChannelMonitor << "struct particle{ float    x; float y; float z; float w;};";
396     fragmentshaderstringstreamChannelMonitor << "layout(std140, binding=0) coherent buffer particles{particle p[];}; ";
397     fragmentshaderstringstreamChannelMonitor << "\n";
398     fragmentshaderstringstreamChannelMonitor << "void main(void)\n";
399     fragmentshaderstringstreamChannelMonitor << "{\n";
400     fragmentshaderstringstreamChannelMonitor << "ivec2 storePos = ivec2(numRows*texCoordFromVertexShader.x, numCols*texCoordFromVertexShader.y);  particle particleData = p[" << shaderStorageBufferOffset * NUM_ELEMENTS_X*NUM_ELEMENTS_Y << " + (storePos.x*numRows + storePos.y)]; ";
401 
402     //fragmentshaderstringstreamChannelMonitor << " memoryBarrierBuffer(); \n";
403     fragmentshaderstringstreamChannelMonitor << " float dataRangeMultiplier = 1.0 / abs(dataRangeMax - dataRangeMin); \n";
404 
405     switch (colorchannel)
406     {
407     case RED_CHANNEL:
408     {
409         fragmentshaderstringstreamChannelMonitor << "   vec4 color; color.x =  0.5+dataRangeMultiplier*particleData.x; color.y =0.0; color.z = 0.0; color.w = 1.0; gl_FragColor = color;\n";
410 
411         break;
412     }
413     case GREEN_CHANNEL:
414     {
415         fragmentshaderstringstreamChannelMonitor << "   vec4 color; color.x = 0.0; color.y = 0.5+dataRangeMultiplier*particleData.y; color.z = 0.0; color.w = 1.0; gl_FragColor = color;\n";
416         break;
417     }
418     case BLUE_CHANNEL:
419     {
420         fragmentshaderstringstreamChannelMonitor << "   vec4 color; color.x = 0.0; color.y = 0.0; color.z = 0.5+dataRangeMultiplier*particleData.z;  color.w = 0.0 ; gl_FragColor = color;\n";
421         break;
422     }
423     case ALPHA_CHANNEL:
424     {
425         fragmentshaderstringstreamChannelMonitor << "   vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.w; color.y = 0.5+dataRangeMultiplier*particleData.w; color.z = 0.5+dataRangeMultiplier*particleData.w; color.w = 0.5+0.5*particleData.w; gl_FragColor = color;\n";
426         break;
427     }
428 
429     case RGB_CHANNEL:
430     {
431         fragmentshaderstringstreamChannelMonitor << "   vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.x; color.y = 0.5+dataRangeMultiplier*particleData.y; color.z = 0.5+dataRangeMultiplier*particleData.z; color.w = 1.0; gl_FragColor = color;\n";
432         break;
433     }
434 
435     case RGBA_CHANNEL:
436     {
437         fragmentshaderstringstreamChannelMonitor << "   vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.x; color.y = 0.5+dataRangeMultiplier*particleData.y; color.z = 0.5+dataRangeMultiplier*particleData.z; color.w = 0.5+0.5*particleData.w; gl_FragColor = color;\n";
438         break;
439     }
440 
441     }
442 
443     fragmentshaderstringstreamChannelMonitor << "}\n";
444 
445 
446 
447 
448     osg::Program * program = new osg::Program;
449     program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSrcChannelMonitor));
450     program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentshaderstringstreamChannelMonitor.str().c_str()));
451     program->addBindAttribLocation("tex_coords", 1);
452 
453     osg::StateSet* ss = geom->getOrCreateStateSet();
454     ss->setAttributeAndModes(program, osg::StateAttribute::ON);
455     ss->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_X));
456     ss->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_Y));
457 
458     ss->addUniform(new osg::Uniform("dataRangeMin", (float)minDataRange));
459     ss->addUniform(new osg::Uniform("dataRangeMax", (float)maxDataRange));
460 
461 
462     ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
463 
464     //add a label
465     osg::ref_ptr<osgText::Text> text = new osgText::Text;
466     osg::ref_ptr<osgText::Font> font = osgText::readRefFontFile("fonts/arial.ttf");
467     text->setFont(font);
468     text->setColor(osg::Vec4(1, 1, 1, 1));
469     text->setCharacterSize(0.1*scale);
470     text->setPosition(placement + osg::Vec3(0.05, 0.05, 0));
471     pat->setName(labelCaption);
472     text->setText(pat->getName());
473     text->setBackdropType(osgText::Text::OUTLINE);
474     text->setBackdropOffset(0.05f);
475     text->setBackdropColor(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
476 
477     quad->addDrawable(text);
478 
479     pat->addChild(quad.get());
480 
481 }
482 
483 //compute texture image , taken from osgspotlight
createSpotLightImage(const osg::Vec4 & centerColour,const osg::Vec4 & backgroudColour,unsigned int size,float power)484 osg::Image* createSpotLightImage(const osg::Vec4& centerColour, const osg::Vec4& backgroudColour, unsigned int size, float power)
485 {
486     osg::Image* image = new osg::Image;
487     image->allocateImage(size, size, 1,
488         GL_RGBA, GL_UNSIGNED_BYTE);
489 
490 
491     float mid = (float(size) - 1)*0.5f;
492     float div = 2.0f / float(size);
493     for (unsigned int r = 0; r < size; ++r)
494     {
495         unsigned char* ptr = image->data(0, r, 0);
496         for (unsigned int c = 0; c < size; ++c)
497         {
498             float dx = (float(c) - mid)*div;
499             float dy = (float(r) - mid)*div;
500             float pr = powf(1.0f - sqrtf(dx*dx + dy*dy), power);
501             if (pr < 0.0f) pr = 0.0f;
502             osg::Vec4 color = centerColour*pr + backgroudColour*(1.0f - pr);
503             *ptr++ = (unsigned char)((color[0])*255.0f);
504             *ptr++ = (unsigned char)((color[1])*255.0f);
505             *ptr++ = (unsigned char)((color[2])*255.0f);
506             *ptr++ = (unsigned char)((color[3])*255.0f);
507         }
508     }
509     return image;
510 }
511 
512 
addComputationResultsRenderTree()513 void ComputeNode::addComputationResultsRenderTree()
514 {
515 
516     _computationResultsRenderProgram = new osg::Program;
517 
518     _vertexShader = osgDB::readRefShaderFile(osg::Shader::VERTEX, _vertexShaderSourcePath);
519     _computationResultsRenderProgram->addShader(_vertexShader.get());
520 
521     _geometryShader = osgDB::readRefShaderFile(osg::Shader::GEOMETRY, _geometryShaderSourcePath);
522     _computationResultsRenderProgram->addShader(_geometryShader.get());
523 
524     _fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, _fragmentShaderSourcePath);
525     _computationResultsRenderProgram->addShader(_fragmentShader.get());
526 
527 
528     _computationResultsRenderProgram->addBindAttribLocation("tex_coords", 1);
529 
530     _computationResultsRenderGroup = new osg::Group;
531     _computationResultsRenderGroup->setDataVariance(osg::Object::DYNAMIC);
532     _computationResultsRenderStateSet = _computationResultsRenderGroup->getOrCreateStateSet();
533     _computationResultsRenderStateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
534 
535     osg::PointSprite *sprite = new osg::PointSprite;
536     int texture_unit = 0;
537     _computationResultsRenderStateSet->setTextureAttributeAndModes(texture_unit, sprite, osg::StateAttribute::ON);
538     _computationResultsRenderStateSet->setAttributeAndModes(_computationResultsRenderProgram.get(), osg::StateAttribute::ON);
539     _computationResultsRenderStateSet->addUniform(new osg::Uniform("particleTexture", texture_unit));
540     _computationResultsRenderStateSet->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_X));
541     _computationResultsRenderStateSet->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_Y));
542 
543 
544     _computationResultsRenderStateSet->setMode(GL_POINT_SMOOTH, osg::StateAttribute::ON);
545     _computationResultsRenderStateSet->setMode(GL_VERTEX_PROGRAM_POINT_SIZE_ARB, osg::StateAttribute::ON);
546     _computationResultsRenderStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
547     _computationResultsRenderStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
548 
549     osg::Texture2D *tex = new osg::Texture2D();
550 
551     osg::Image* particleImage = createSpotLightImage(osg::Vec4(1, 0, 0, 1), osg::Vec4(0.5, 0, 0, 0.0), 32, 0.7);
552     if (particleImage)
553     {
554         tex->setImage(particleImage);
555     }
556     _computationResultsRenderStateSet->setTextureAttributeAndModes(texture_unit, tex, osg::StateAttribute::ON);
557 
558 
559     osg::BlendFunc *blend = new osg::BlendFunc;
560     if (false) //emissive particles
561     {
562         blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE);
563     }
564     else
565     {
566         blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
567     }
568 
569     _computationResultsRenderStateSet->setAttributeAndModes(blend, osg::StateAttribute::ON);
570 
571 
572     osg::Depth* depth = new osg::Depth;
573     depth->setRange(0.0f, 0.0f);
574     depth->setFunction(osg::Depth::ALWAYS);
575     depth->setWriteMask(false);
576     depth->setFunction(osg::Depth::ALWAYS);
577 
578     _computationResultsRenderStateSet->setAttributeAndModes(depth, osg::StateAttribute::OFF);
579 
580 
581     osg::Geode* particleGeode = new osg::Geode;
582     unsigned int numVertices = NUM_ELEMENTS_X*NUM_ELEMENTS_Y;
583 
584     osg::Geometry* particleGeometry = new osg::Geometry;
585     particleGeometry->setUseDisplayList(false);
586     particleGeometry->setUseVertexBufferObjects(true);
587 
588     osg::Vec3Array* vertexarray = new osg::Vec3Array;
589     osg::Vec2Array* tcoords = new osg::Vec2Array;
590 
591     osg::Vec2 bottom_texcoord(0.0f, 0.0f);
592 
593     osg::Vec2 dx_texcoord(1.0f / (float)(NUM_ELEMENTS_X), 0.0f);
594     osg::Vec2 dy_texcoord(0.0f, 1.0f / (float)(NUM_ELEMENTS_Y));
595 
596 
597 
598     for (int i = 0; i < NUM_ELEMENTS_X; i++)
599     {
600         osg::Vec2 texcoord = bottom_texcoord + dy_texcoord*(float)i;
601 
602         for (int j = 0; j < NUM_ELEMENTS_Y; j++)
603         {
604             vertexarray->push_back(osg::Vec3(texcoord.x(), texcoord.y(), 0.0));
605             tcoords->push_back(osg::Vec2(texcoord.x(), texcoord.y()));
606             texcoord += dx_texcoord;
607         }
608     }
609 
610     particleGeometry->setVertexArray(vertexarray);
611     particleGeometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, numVertices));
612     particleGeometry->setTexCoordArray(0, tcoords);
613     //this glMemoryBarrier thing... not sure if we could better do instanced drawing?  all the data is in Shader Storage Buffer..
614     particleGeometry->setVertexAttribArray(1, particleGeometry->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);
615 
616     _computationResultsRenderGroup->addChild(particleGeode);
617     particleGeode->addDrawable(particleGeometry);
618 
619     addChild(_computationResultsRenderGroup.get());
620 
621 }
622 
623 
initComputingSetup()624 void ComputeNode::initComputingSetup()
625 {
626 
627     _computeProgram = new osg::Program;
628     _DispatchCompute->setComputeGroups((NUM_ELEMENTS_X / WORK_GROUP_SIZE) <= 1 ? 1 : (NUM_ELEMENTS_X / WORK_GROUP_SIZE), (NUM_ELEMENTS_Y / WORK_GROUP_SIZE) <= 1 ? 1 : (NUM_ELEMENTS_Y / WORK_GROUP_SIZE), 1);
629     _computeShader = osgDB::readRefShaderFile(osg::Shader::COMPUTE, _computeShaderSourcePath);
630     _computeProgram->addShader(_computeShader.get());
631 
632     setDataVariance(osg::Object::DYNAMIC);
633     osg::StateSet* statesetComputation = getOrCreateStateSet();
634     statesetComputation->setAttributeAndModes(_computeProgram.get());
635     statesetComputation->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_X));
636     statesetComputation->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_Y));
637     statesetComputation->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
638 
639     //blocksize
640     int  numParticles = NUM_ELEMENTS_X * NUM_ELEMENTS_Y;
641     const unsigned blockSize = numParticles * __numChannels * __numDataValuesPerChannel* sizeof(GLfloat);
642 
643     //init all the particle data array
644     int idx = 0;
645     _data = new GLfloat[NUM_ELEMENTS_X  * NUM_ELEMENTS_Y  * __numChannels * __numDataValuesPerChannel];
646     _dataArray = new FloatArray;
647 
648     //init the data array  somehow, this way all is stored in one BufferObject. maybe better using multiple buffers instead? not sure what is faster and better for threading
649     for (int d = 0; d < __numDataValuesPerChannel; ++d)
650     {
651         for (int i = 0; i < NUM_ELEMENTS_X; ++i)
652         {
653 
654             for (int j = 0; j < NUM_ELEMENTS_Y; ++j)
655             {
656 
657                 for (int k = 0; k < __numChannels; ++k)
658                 {
659                     switch (k)
660                     {
661 
662                     case (RED_CHANNEL) :
663                     {
664                         if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position
665                         {
666                             *_data = random(0.25, 0.75);
667                         }
668                         if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity
669                         {
670                             *_data = random(-2.4, 2.4);
671                         }
672                         if (d == ACCELERATION_OFFSET) //acceleration
673                         {
674                             *_data = random(-3.0, 3.0);
675                         }
676 
677                         if (d == PROPERTIES_OFFSET) //property particle mass (compute shader is computing sphere mass from radius instead)
678                         {
679                             *_data = random(0.2, 15.0);
680                         }
681 
682                         break;
683                     }
684 
685                     case (GREEN_CHANNEL) :
686                     {
687                         if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position
688                         {
689                             *_data = random(0.25, 0.75);
690                         }
691                         if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity
692                         {
693                             *_data = random(-2.4, 2.4);
694                         }
695 
696                         if (d == ACCELERATION_OFFSET)//acceleration
697                         {
698                             *_data = random(-3.0, 3.0);
699                         }
700                         if (d == PROPERTIES_OFFSET) //property particle radius
701                         {
702                             *_data = random(0.07, 0.219);
703                         }
704 
705                         break;
706                     }
707 
708                     case (BLUE_CHANNEL) :
709                     {
710                         if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position
711                         {
712                             *_data = random(0.25, 0.75);
713                         }
714                         if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity
715                         {
716                             *_data = random(-2.4, 2.4);
717                         }
718 
719                         if (d == ACCELERATION_OFFSET)//acceleration
720                         {
721                             *_data = random(-3.0, 3.0);
722                         }
723 
724 
725                         if (d == PROPERTIES_OFFSET)  //place for some other property
726                         {
727                             *_data = random(0.0, 0.0);
728                         }
729 
730                         break;
731                     }
732 
733                     case (ALPHA_CHANNEL) :
734                     {
735                         if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position
736                         {
737                             *_data = random(1.0, 1.0);
738                         }
739                         if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity
740                         {
741                             *_data = random(-2.4, 2.4);
742                         }
743 
744                         if (d == ACCELERATION_OFFSET) //acceleration
745                         {
746                             //*_data = random(1.0, 1.0);
747                             *_data = random(0.0, 0.0);
748                         }
749 
750                         if (d == PROPERTIES_OFFSET) //place for some other property
751                         {
752                             *_data = random(0.3, 0.3);
753                         }
754 
755                         break;
756                     }
757 
758 
759 
760                     }
761                     _dataArray->push_back(*_data);
762                     _data++;
763                     idx++;
764                 }
765             }
766         }
767     }
768 
769     _ssbo = new osg::ShaderStorageBufferObject;
770     _dataArray->setBufferObject(_ssbo.get());
771 
772 
773     _ssbb = new osg::ShaderStorageBufferBinding(0, _dataArray.get(), 0, blockSize);
774     statesetComputation->setAttributeAndModes(_ssbb.get(), osg::StateAttribute::ON);
775 
776 
777     //option, do something useful with data or test the transfer speed
778     //_ssbb->setUpdateCallback(new ShaderStorageBufferCallback);
779 
780     //adding a quad , visualizing data in buffer
781     addDataMonitor(osg::Vec3(0, -1, 0), osg::Vec3(SUB_PLACEMENT_OFFSET_HORIZONTAL * 0, -SUB_PLACEMENT_OFFSET_VERTICAL * -2.0, SUB_PLACEMENT_OFFSET_HORIZONTAL * 0), 1.0, RGB_CHANNEL, POSITION_NOW_OFFSET, "X,Y,Z - PositionNow", -1.0, 1.0);
782 
783     //the coord from default dataset
784     addHelperGeometry();
785 
786 
787     addComputationResultsRenderTree();
788 
789 }
790 
791 
792 //taken from osgdistorsion example for getting it nice on screen with antialiasing
createPrerenderSubgraph(osg::Node * subgraph,const osg::Vec4 & clearColour)793 osg::Node* createPrerenderSubgraph(osg::Node* subgraph, const osg::Vec4& clearColour)
794 {
795     osg::Group* prerenderNode = new osg::Group;
796 
797     unsigned int tex_width = PRERENDER_WIDTH;
798     unsigned int tex_height = PRERENDER_HEIGHT;
799 
800     osg::Texture2D* texture = new osg::Texture2D;
801     texture->setTextureSize(tex_width, tex_height);
802     texture->setInternalFormat(GL_RGBA);
803     texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
804     texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
805 
806     {
807         osg::Camera* prerenderCamera = new osg::Camera;
808         prerenderCamera->setClearColor(clearColour);
809         prerenderCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
810         prerenderCamera->setReferenceFrame(osg::Transform::RELATIVE_RF);
811         prerenderCamera->setProjectionMatrix(osg::Matrixd::identity());
812         prerenderCamera->setViewMatrix(osg::Matrixd::identity());
813         prerenderCamera->setViewport(0, 0, tex_width, tex_height);
814         prerenderCamera->setRenderOrder(osg::Camera::PRE_RENDER);
815         prerenderCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
816         prerenderCamera->attach(osg::Camera::COLOR_BUFFER0, texture, 0, 0, false, PRERENDER_ANTIALIASINGMULTISAMPLES, PRERENDER_ANTIALIASINGMULTISAMPLES);
817         prerenderCamera->addChild(subgraph);
818         prerenderNode->addChild(prerenderCamera);
819 
820     }
821 
822     {
823         osg::Geometry* polyGeom = new osg::Geometry();
824 
825         polyGeom->setSupportsDisplayList(false);
826 
827         osg::Vec3 origin(0.0f, 0.0f, 0.0f);
828         osg::Vec3 xAxis(1.0f, 0.0f, 0.0f);
829         osg::Vec3 yAxis(0.0f, 1.0f, 0.0f);
830 
831         float height = 1024.0f;
832         float width = 1280.0f;
833         int noSteps = 3;
834 
835         osg::Vec3Array* vertices = new osg::Vec3Array;
836         osg::Vec2Array* texcoords = new osg::Vec2Array;
837         osg::Vec4Array* colors = new osg::Vec4Array;
838 
839         osg::Vec3 bottom = origin;
840         osg::Vec3 dx = xAxis*(width / ((float)(noSteps - 1)));
841         osg::Vec3 dy = yAxis*(height / ((float)(noSteps - 1)));
842 
843         osg::Vec2 bottom_texcoord(0.0f, 0.0f);
844         osg::Vec2 dx_texcoord(1.0f / (float)(noSteps - 1), 0.0f);
845         osg::Vec2 dy_texcoord(0.0f, 1.0f / (float)(noSteps - 1));
846 
847         int i, j;
848         for (i = 0; i < noSteps; ++i)
849         {
850             osg::Vec3 cursor = bottom + dy*(float)i;
851             osg::Vec2 texcoord = bottom_texcoord + dy_texcoord*(float)i;
852             for (j = 0; j < noSteps; ++j)
853             {
854                 vertices->push_back(cursor);
855                 texcoords->push_back(osg::Vec2((sin(texcoord.x()*osg::PI - osg::PI*0.5) + 1.0f)*0.5f, (sin(texcoord.y()*osg::PI - osg::PI*0.5) + 1.0f)*0.5f));
856                 colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
857                 cursor += dx;
858                 texcoord += dx_texcoord;
859             }
860         }
861 
862         polyGeom->setVertexArray(vertices);
863         polyGeom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
864         polyGeom->setTexCoordArray(0, texcoords);
865 
866         for (i = 0; i < noSteps - 1; ++i)
867         {
868             osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::QUAD_STRIP);
869             for (j = 0; j < noSteps; ++j)
870             {
871                 elements->push_back(j + (i + 1)*noSteps);
872                 elements->push_back(j + (i)*noSteps);
873             }
874             polyGeom->addPrimitiveSet(elements);
875         }
876 
877         osg::StateSet* stateset = polyGeom->getOrCreateStateSet();
878         stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
879         stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
880 
881         osg::Geode* geode = new osg::Geode();
882         geode->addDrawable(polyGeom);
883 
884         osg::Camera* nestedRenderCamera = new osg::Camera;
885         nestedRenderCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
886         nestedRenderCamera->setViewMatrix(osg::Matrix::identity());
887         nestedRenderCamera->setProjectionMatrixAsOrtho2D(0, 1280, 0, 1024);
888         nestedRenderCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
889         nestedRenderCamera->addChild(geode);
890 
891         prerenderNode->addChild(nestedRenderCamera);
892     }
893 
894     return prerenderNode;
895 }
896 
897 
898 
899 
main(int argc,char ** argv)900 int main(int argc, char** argv)
901 {
902     osg::ArgumentParser arguments(&argc, argv);
903 
904     osgViewer::Viewer viewer;
905 
906     osg::ref_ptr<osg::Group> scene = new osg::Group;
907 
908 
909     viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
910     viewer.addEventHandler(new osgViewer::StatsHandler);
911     viewer.addEventHandler(new osgViewer::WindowSizeHandler);
912     viewer.addEventHandler(new osgViewer::ThreadingHandler);
913     viewer.getCamera()->setProjectionMatrixAsPerspective(60.0f, 1.33333, 0.01, 100.0);
914     viewer.setCameraManipulator(new osgGA::TrackballManipulator());
915 
916     viewer.setUpViewInWindow(11, 11, 800 + 11, 600 + 11);
917     //viewer.setUpViewOnSingleScreen(0); // !!
918 
919     viewer.getCamera()->setClearColor(osg::Vec4(0.3, 0.3, 0.3, 1.0));
920     viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);// we can play with threading models later
921 
922     osg::ref_ptr<ComputeNode> computeNode = new ComputeNode();
923     computeNode->setPosition(osg::Vec3(0, 0, 0));
924     computeNode->setUpdateCallback(new ComputeNodeUpdateCallback(computeNode.get())); // on-the-fly reloading the shaders if shader source on disk is changed
925     computeNode->initComputingSetup();
926 
927 
928 
929     scene->addChild(computeNode.get());
930     scene->addChild(computeNode->_computationResultsRenderGroup.get());
931 
932 
933 #ifdef PRERENDER_HIGH_QUALITY_ANTIALIASING
934     viewer.setSceneData(createPrerenderSubgraph(scene.get(), osg::Vec4(0.3, 0.4, 0.6, 1)));
935 #else
936     viewer.setSceneData(scene.get());
937 #endif
938 
939     viewer.realize();
940 
941     viewer.getCamera()->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true);
942 
943     viewer.run();
944 
945     return 1;
946 }
947