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