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