1 /* OpenSceneGraph example, osgvertexattributes.
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 <osgUtil/ShaderGen>
20 #include <osgDB/ReadFile>
21 #include <osgDB/WriteFile>
22 #include <osgViewer/Viewer>
23 #include <osgViewer/ViewerEventHandlers>
24 #include <osgGA/TrackballManipulator>
25 
26 class ConvertToVertexAttibArrays : public osg::NodeVisitor
27 {
28     public:
29 
30         typedef std::pair<unsigned int, std::string> AttributeAlias;
31 
ConvertToVertexAttibArrays()32         ConvertToVertexAttibArrays():
33             osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
34         {
35             _manualVertexAliasing = false;
36 
37             // mappings taken from http://www.opengl.org/registry/specs/NV/vertex_program.txt
38             _vertexAlias = AttributeAlias(0, "osg_Vertex");
39             _normalAlias = AttributeAlias(2, "osg_Normal");
40             _colorAlias = AttributeAlias(3, "osg_Color");
41             _secondaryColorAlias = AttributeAlias(4, "osg_SecondaryColor");
42             _fogCoordAlias = AttributeAlias(5, "osg_FogCoord");
43             _texCoordAlias[0] = AttributeAlias(8, "osg_MultiTexCoord0");
44             _texCoordAlias[1] = AttributeAlias(9, "osg_MultiTexCoord1");
45             _texCoordAlias[2] = AttributeAlias(10, "osg_MultiTexCoord2");
46             _texCoordAlias[3] = AttributeAlias(11, "osg_MultiTexCoord3");
47             _texCoordAlias[4] = AttributeAlias(12, "osg_MultiTexCoord4");
48             _texCoordAlias[5] = AttributeAlias(13, "osg_MultiTexCoord5");
49             _texCoordAlias[6] = AttributeAlias(14, "osg_MultiTexCoord6");
50             _texCoordAlias[7] = AttributeAlias(15, "osg_MultiTexCoord7");
51         }
52 
bindAttribute(osg::Program & program,const AttributeAlias & alias)53         void bindAttribute(osg::Program& program, const AttributeAlias& alias)
54         {
55                 program.addBindAttribLocation(alias.second, alias.first);
56         }
57 
replaceAndBindAttrib(osg::Program & program,std::string & source,const std::string & originalStr,const AttributeAlias & alias,const std::string & declarationPrefix)58         void replaceAndBindAttrib(osg::Program& program, std::string& source, const std::string& originalStr, const AttributeAlias& alias, const std::string& declarationPrefix)
59         {
60             if (replace(source, originalStr, alias.second))
61             {
62                 source.insert(0, declarationPrefix + alias.second + std::string(";\n"));
63                 if (_manualVertexAliasing) bindAttribute(program, alias);
64             }
65         }
66 
replaceBuiltInUniform(std::string & source,const std::string & originalStr,const std::string & newStr,const std::string & declarationPrefix)67         void replaceBuiltInUniform(std::string& source, const std::string& originalStr, const std::string& newStr, const std::string& declarationPrefix)
68         {
69             if (replace(source, originalStr, newStr))
70             {
71                 source.insert(0, declarationPrefix + newStr + std::string(";\n"));
72             }
73         }
74 
convertVertexShader(osg::Program & program,osg::Shader & shader)75         void convertVertexShader(osg::Program& program, osg::Shader& shader)
76         {
77             std::string source = shader.getShaderSource();
78 
79             // replace ftransform as it only works with built-ins
80             replace(source, "ftransform()", "gl_ModelViewProjectionMatrix * gl_Vertex");
81 
82 #if 1
83             replaceAndBindAttrib(program, source, "gl_Normal", _normalAlias, "attribute vec3 ");
84             replaceAndBindAttrib(program, source, "gl_Vertex", _vertexAlias, "attribute vec4 ");
85             replaceAndBindAttrib(program, source, "gl_Color", _colorAlias, "attribute vec4 ");
86             replaceAndBindAttrib(program, source, "gl_SecondaryColor", _secondaryColorAlias, "attribute vec4 ");
87             replaceAndBindAttrib(program, source, "gl_FogCoord", _fogCoordAlias, "attribute float ");
88 
89             replaceAndBindAttrib(program, source, "gl_MultiTexCoord0", _texCoordAlias[0], "attribute vec4 ");
90             replaceAndBindAttrib(program, source, "gl_MultiTexCoord1", _texCoordAlias[1], "attribute vec4 ");
91             replaceAndBindAttrib(program, source, "gl_MultiTexCoord2", _texCoordAlias[2], "attribute vec4 ");
92             replaceAndBindAttrib(program, source, "gl_MultiTexCoord3", _texCoordAlias[3], "attribute vec4 ");
93             replaceAndBindAttrib(program, source, "gl_MultiTexCoord4", _texCoordAlias[4], "attribute vec4 ");
94             replaceAndBindAttrib(program, source, "gl_MultiTexCoord5", _texCoordAlias[5], "attribute vec4 ");
95             replaceAndBindAttrib(program, source, "gl_MultiTexCoord6", _texCoordAlias[6], "attribute vec4 ");
96             replaceAndBindAttrib(program, source, "gl_MultiTexCoord7", _texCoordAlias[7], "attribute vec4 ");
97 #endif
98 
99 #if 1
100             // replace built in uniform
101             replaceBuiltInUniform(source, "gl_ModelViewMatrix", "osg_ModelViewMatrix", "uniform mat4 ");
102             replaceBuiltInUniform(source, "gl_ModelViewProjectionMatrix", "osg_ModelViewProjectionMatrix", "uniform mat4 ");
103             replaceBuiltInUniform(source, "gl_ProjectionMatrix", "osg_ProjectionMatrix", "uniform mat4 ");
104 #endif
105             shader.setShaderSource(source);
106         }
107 
convertFragmentShader(osg::Program &,osg::Shader &)108         void convertFragmentShader(osg::Program& /*program*/, osg::Shader& /*shader*/)
109         {
110         }
111 
reset()112         virtual void reset()
113         {
114             _visited.clear();
115         }
116 
apply(osg::Node & node)117         void apply(osg::Node& node)
118         {
119             if (_visited.count(&node)!=0) return;
120             _visited.insert(&node);
121 
122             if (node.getStateSet()) apply(*(node.getStateSet()));
123             traverse(node);
124         }
125 
apply(osg::Geode & geode)126         void apply(osg::Geode& geode)
127         {
128             if (_visited.count(&geode)!=0) return;
129             _visited.insert(&geode);
130 
131             if (geode.getStateSet()) apply(*(geode.getStateSet()));
132 
133             for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
134             {
135                 if (geode.getDrawable(i)->getStateSet()) apply(*(geode.getDrawable(i)->getStateSet()));
136 
137                 osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
138                 if (geom) apply(*geom);
139             }
140         }
141 
replace(std::string & str,const std::string & original_phrase,const std::string & new_phrase)142         bool replace(std::string& str, const std::string& original_phrase, const std::string& new_phrase)
143         {
144             bool replacedStr = false;
145             std::string::size_type pos = 0;
146             while((pos=str.find(original_phrase, pos))!=std::string::npos)
147             {
148                 std::string::size_type endOfPhrasePos = pos+original_phrase.size();
149                 if (endOfPhrasePos<str.size())
150                 {
151                     char c = str[endOfPhrasePos];
152                     if ((c>='0' && c<='9') ||
153                         (c>='a' && c<='z') ||
154                         (c>='A' && c<='Z'))
155                     {
156                         pos = endOfPhrasePos;
157                         continue;
158                     }
159                 }
160 
161                 replacedStr = true;
162                 str.replace(pos, original_phrase.size(), new_phrase);
163             }
164             return replacedStr;
165         }
166 
apply(osg::Program & program,osg::Shader & shader)167         void apply(osg::Program& program, osg::Shader& shader)
168         {
169              if (_visited.count(&shader)!=0) return;
170             _visited.insert(&shader);
171 
172             osg::notify(osg::NOTICE)<<"Shader "<<shader.getTypename()<<" ----before-----------"<<std::endl;
173             osg::notify(osg::NOTICE)<<shader.getShaderSource()<<std::endl;
174 
175             if (shader.getType()==osg::Shader::VERTEX) convertVertexShader(program, shader);
176             else if (shader.getType()==osg::Shader::FRAGMENT) convertFragmentShader(program, shader);
177 
178             osg::notify(osg::NOTICE)<<"--after-----------"<<std::endl;
179             osg::notify(osg::NOTICE)<<shader.getShaderSource()<<std::endl;
180             osg::notify(osg::NOTICE)<<"---------------------"<<std::endl;
181         }
182 
apply(osg::StateSet & stateset)183         void apply(osg::StateSet& stateset)
184         {
185              if (_visited.count(&stateset)!=0) return;
186             _visited.insert(&stateset);
187 
188             return;
189 
190             osg::notify(osg::NOTICE)<<"Found stateset "<<&stateset<<std::endl;
191             osg::Program* program = dynamic_cast<osg::Program*>(stateset.getAttribute(osg::StateAttribute::PROGRAM));
192             if (program)
193             {
194                 osg::notify(osg::NOTICE)<<"   Found Program "<<program<<std::endl;
195                 for(unsigned int i=0; i<program->getNumShaders(); ++i)
196                 {
197                     apply(*program, *(program->getShader(i)));
198                 }
199 
200             }
201        }
202 
apply(osg::Geometry & geom)203         void apply(osg::Geometry& geom)
204         {
205             geom.setUseDisplayList(false);
206 
207             if (!_manualVertexAliasing) return;
208 
209             osg::notify(osg::NOTICE)<<"Found geometry "<<&geom<<std::endl;
210             if (geom.getVertexArray())
211             {
212                 setVertexAttrib(geom, _vertexAlias, geom.getVertexArray(), false, osg::Array::BIND_PER_VERTEX);
213                 geom.setVertexArray(0);
214             }
215 
216             if (geom.getNormalArray())
217             {
218                 setVertexAttrib(geom, _normalAlias, geom.getNormalArray(), true);
219                 geom.setNormalArray(0);
220             }
221 
222             if (geom.getColorArray())
223             {
224                 setVertexAttrib(geom, _colorAlias, geom.getColorArray(), false);
225                 geom.setColorArray(0);
226             }
227 
228             if (geom.getSecondaryColorArray())
229             {
230                 setVertexAttrib(geom, _secondaryColorAlias, geom.getSecondaryColorArray(), false);
231                 geom.setSecondaryColorArray(0);
232             }
233 
234             if (geom.getFogCoordArray())
235             {
236                 // should we normalize the FogCoord array? Don't think so...
237                 setVertexAttrib(geom, _fogCoordAlias, geom.getFogCoordArray(), false);
238                 geom.setFogCoordArray(0);
239             }
240 
241             unsigned int maxNumTexCoords = geom.getNumTexCoordArrays();
242             if (maxNumTexCoords>8)
243             {
244                 osg::notify(osg::NOTICE)<<"Warning: Ignoring "<<maxNumTexCoords-8<<" texture coordinate arrays, only 8 are currently supported in vertex attribute conversion code."<<std::endl;
245                 maxNumTexCoords = 8;
246             }
247             for(unsigned int i=0; i<maxNumTexCoords; ++i)
248             {
249                 if (geom.getTexCoordArray(i))
250                 {
251                     setVertexAttrib(geom, _texCoordAlias[i], geom.getTexCoordArray(i), false, osg::Array::BIND_PER_VERTEX);
252                     geom.setTexCoordArray(i,0);
253                 }
254                 else
255                 {
256                     osg::notify(osg::NOTICE)<<"Found empty TexCoordArray("<<i<<")"<<std::endl;
257                 }
258             }
259         }
260 
setVertexAttrib(osg::Geometry & geom,const AttributeAlias & alias,osg::Array * array,bool normalize,osg::Array::Binding binding=osg::Array::BIND_UNDEFINED)261         void setVertexAttrib(osg::Geometry& geom, const AttributeAlias& alias, osg::Array* array, bool normalize, osg::Array::Binding binding = osg::Array::BIND_UNDEFINED)
262         {
263             unsigned int index = alias.first;
264             const std::string& name = alias.second;
265             array->setName(name);
266             if (binding!=osg::Array::BIND_UNDEFINED) array->setBinding(binding);
267             array->setNormalize(normalize);
268             geom.setVertexAttribArray(index, array);
269 
270             osg::notify(osg::NOTICE)<<"   vertex attrib("<<name<<", index="<<index<<", normalize="<<normalize<<" binding="<<binding<<")"<<std::endl;
271         }
272 
273 
274         typedef std::set<osg::Object*> Visited;
275         Visited         _visited;
276 
277         bool           _manualVertexAliasing;
278         AttributeAlias _vertexAlias;
279         AttributeAlias _normalAlias;
280         AttributeAlias _colorAlias;
281         AttributeAlias _secondaryColorAlias;
282         AttributeAlias _fogCoordAlias;
283         AttributeAlias _texCoordAlias[8];
284 };
285 
createSimpleTestModel()286 osg::Node* createSimpleTestModel()
287 {
288     osg::Group* group = new osg::Group;
289 
290     osg::Geode* geode = new osg::Geode;
291     group->addChild(geode);
292 
293     osg::Geometry* geometry = new osg::Geometry;
294     geode->addDrawable(geometry);
295 
296     osg::Vec3Array* vertices = new osg::Vec3Array;
297     vertices->push_back(osg::Vec3(0.0,0.0,0.0));
298     vertices->push_back(osg::Vec3(0.0,0.0,1.0));
299     vertices->push_back(osg::Vec3(1.0,0.0,0.0));
300     vertices->push_back(osg::Vec3(1.0,0.0,1.0));
301     geometry->setVertexArray(vertices);
302 
303     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
304 
305     char vertexShaderSource[] =
306        "void main(void)\n"
307        "{\n"
308        "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
309        "}\n";
310 
311     char fragmentShaderSource[] =
312         "void main(void)\n"
313         "{\n"
314         "    gl_FragColor = vec4(1.0,1.0,0.0,1.0); \n"
315         "}\n";
316 
317     osg::Program* program = new osg::Program;
318     program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSource));
319     program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource));
320 
321     geometry->getOrCreateStateSet()->setAttribute(program);
322 
323     return group;
324 }
325 
createSimpleTextureTestModel()326 osg::Node* createSimpleTextureTestModel()
327 {
328     osg::Group* group = new osg::Group;
329 
330     osg::Geode* geode = new osg::Geode;
331     group->addChild(geode);
332 
333     osg::Geometry* geometry = new osg::Geometry;
334     geode->addDrawable(geometry);
335 
336     osg::Vec3Array* vertices = new osg::Vec3Array;
337     vertices->push_back(osg::Vec3(0.0,0.0,0.0));
338     vertices->push_back(osg::Vec3(0.0,0.0,1.0));
339     vertices->push_back(osg::Vec3(1.0,0.0,0.0));
340     vertices->push_back(osg::Vec3(1.0,0.0,1.0));
341     geometry->setVertexArray(vertices);
342 
343     osg::Vec2Array* texcoords = new osg::Vec2Array;
344     texcoords->push_back(osg::Vec2(0.0,0.0));
345     texcoords->push_back(osg::Vec2(0.0,1.0));
346     texcoords->push_back(osg::Vec2(1.0,0.0));
347     texcoords->push_back(osg::Vec2(1.0,1.0));
348     geometry->setTexCoordArray(0, texcoords);
349 
350     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
351 
352     char vertexShaderSource[] =
353        "varying vec2 texCoord;\n"
354        "void main(void)\n"
355        "{\n"
356        "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
357        "    texCoord = gl_MultiTexCoord0.xy;\n"
358        "}\n";
359 
360     char fragmentShaderSource[] =
361         "varying vec2 texCoord;\n"
362         "uniform sampler2D baseTexture;\n"
363         "void main(void)\n"
364         "{\n"
365         "    gl_FragColor = texture2D(baseTexture, texCoord); \n"
366         "}\n";
367 
368     osg::Program* program = new osg::Program;
369     program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSource));
370     program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource));
371 
372     osg::StateSet* stateset = geometry->getOrCreateStateSet();
373     stateset->setAttribute(program);
374 
375     osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile("Images/lz.rgb");
376     osg::Texture2D* texture = new osg::Texture2D(image);
377     texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
378     texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
379     stateset->setTextureAttribute(0, texture);
380 
381     osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
382     stateset->addUniform(baseTextureSampler);
383 
384     return group;
385 }
386 
main(int argc,char * argv[])387 int main(int argc, char *argv[])
388 {
389     // use an ArgumentParser object to manage the program arguments.
390     osg::ArgumentParser arguments(&argc,argv);
391 
392     // construct the viewer.
393     osgViewer::Viewer viewer(arguments);
394 
395     std::string outputFileName;
396     while (arguments.read("-o",outputFileName)) {}
397 
398     osg::ref_ptr<osg::Node> loadedModel;
399 
400     bool runConvertToVertexAttributes = false;
401     if (arguments.read("--simple") || arguments.read("--s"))
402     {
403         loadedModel = createSimpleTestModel();
404     }
405     else if (arguments.read("--texture") || arguments.read("-t"))
406     {
407         loadedModel = createSimpleTextureTestModel();
408     }
409     else
410     {
411         bool runShaderGen = true;
412         while (arguments.read("--shader-gen")) { runShaderGen = true; }
413         while (arguments.read("--no-shader-gen")) { runShaderGen = false; }
414 
415         while (arguments.read("--vertex-attrib")) { runConvertToVertexAttributes = true; }
416         while (arguments.read("--no-vertex-attrib")) { runConvertToVertexAttributes = false; }
417 
418         loadedModel = osgDB::readRefNodeFiles(arguments);
419         if (!loadedModel.get())
420         {
421             osg::notify(osg::NOTICE)<<"No model loaded, please specify a model filename."<<std::endl;
422             return 1;
423         }
424 
425         if (runShaderGen)
426         {
427             // convert fixed function pipeline to shaders
428             osgUtil::ShaderGenVisitor sgv;
429             loadedModel->accept(sgv);
430         }
431 
432         if (runConvertToVertexAttributes)
433         {
434             // find any conventional vertex, colour, normal and tex coords arrays and convert to vertex attributes
435             ConvertToVertexAttibArrays ctvaa;
436             loadedModel->accept(ctvaa);
437         }
438     }
439 
440     if (!loadedModel) return 1;
441 
442     if (!outputFileName.empty())
443     {
444         osgDB::writeNodeFile(*loadedModel, outputFileName);
445         return 0;
446     }
447 
448     // add a viewport to the viewer and attach the scene graph.
449     viewer.setSceneData(loadedModel);
450 
451     viewer.setCameraManipulator(new osgGA::TrackballManipulator());
452 
453     // add the stats handler
454     viewer.addEventHandler(new osgViewer::StatsHandler);
455 
456     viewer.realize();
457 
458 
459     if (runConvertToVertexAttributes)
460     {
461         // switch on the uniforms that track the modelview and projection matrices
462         osgViewer::Viewer::Windows windows;
463         viewer.getWindows(windows);
464         for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
465             itr != windows.end();
466             ++itr)
467         {
468             (*itr)->getState()->setUseModelViewAndProjectionUniforms(true);
469             (*itr)->getState()->setUseVertexAttributeAliasing(true);
470         }
471     }
472 
473     return viewer.run();
474 }
475