root/OpenSceneGraph/trunk/src/osgPlugins/obj/OBJWriterNodeVisitor.cpp @ 13041

Revision 13041, 17.4 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1// -*-c++-*-
2
3/*
4 * Wavefront OBJ loader for Open Scene Graph
5 *
6 * Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
7 *
8 * Modified by Robert Osfield to support per Drawable coord, normal and
9 * texture coord arrays, bug fixes, and support for texture mapping.
10 *
11 * Writing support added 2007 by Stephan Huber, http://digitalmind.de,
12 * some ideas taken from the dae-plugin
13 *
14 * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
15 * real-time rendering of large 3D photo-realistic models.
16 * The OSG homepage is http://www.openscenegraph.org/
17 */
18
19#include <osg/io_utils>
20#include "OBJWriterNodeVisitor.h"
21
22
23
24/** writes all values of an array out to a stream, applies a matrix beforehand if necessary */
25class ValueVisitor : public osg::ValueVisitor {
26    public:
27        ValueVisitor(std::ostream& fout, const osg::Matrix& m = osg::Matrix::identity(), bool isNormal = false) :
28            osg::ValueVisitor(),
29            _fout(fout),
30            _m(m),
31            _isNormal(isNormal)
32        {
33            _applyMatrix = (_m != osg::Matrix::identity());
34            if (_isNormal) _origin = osg::Vec3(0,0,0) * _m;
35        }
36
37        virtual void apply (osg::Vec2 & inv)
38        {
39            _fout << inv[0] << ' ' << inv[1];
40        }
41
42        virtual void apply (osg::Vec3 & inv)
43        {
44            osg::Vec3 v(inv);
45            if (_applyMatrix)  v = (_isNormal) ? (v * _m) - _origin : v * _m;
46            _fout << v[0] << ' ' << v[1] << ' ' << v[2];
47        }
48
49        virtual void apply (osg::Vec2b & inv)
50        {
51            _fout << inv[0] << ' ' << inv[1];
52        }
53
54        virtual void apply (osg::Vec3b & inv)
55        {
56            osg::Vec3 v(inv[0], inv[1], inv[2]);
57            if (_applyMatrix)  v = (_isNormal) ? (v * _m) - _origin : v * _m;
58            _fout << v[0] << ' ' << v[1] << ' ' << v[2];
59        }
60
61        virtual void apply (osg::Vec2s & inv)
62        {
63            _fout << inv[0] << ' ' << inv[1];
64        }
65
66        virtual void apply (osg::Vec3s & inv)
67        {
68            osg::Vec3 v(inv[0], inv[1], inv[2]);
69            if (_applyMatrix)  v = (_isNormal) ? (v * _m) - _origin : v * _m;
70            _fout << v[0] << ' ' << v[1] << ' ' << v[2];
71        }
72    private:
73
74        ValueVisitor& operator = (const ValueVisitor&) { return *this; }
75
76        std::ostream&    _fout;
77        osg::Matrix        _m;
78        bool            _applyMatrix, _isNormal;
79        osg::Vec3        _origin;
80};
81
82/** writes all primitives of a primitive-set out to a stream, decomposes quads to triangles, line-strips to lines etc */
83class ObjPrimitiveIndexWriter : public osg::PrimitiveIndexFunctor {
84
85    public:
86        ObjPrimitiveIndexWriter(std::ostream& fout,osg::Geometry* geo, unsigned int normalIndex, unsigned int lastVertexIndex, unsigned int  lastNormalIndex, unsigned int lastTexIndex) :
87            osg::PrimitiveIndexFunctor(),
88            _fout(fout),
89            _lastVertexIndex(lastVertexIndex),
90            _lastNormalIndex(lastNormalIndex),
91            _lastTexIndex(lastTexIndex),
92            _hasNormalCoords(geo->getNormalArray() != NULL),
93            _hasTexCoords(geo->getTexCoordArray(0) != NULL),
94            _geo(geo),
95            _normalIndex(normalIndex)
96        {
97        }
98
99        virtual void setVertexArray(unsigned int,const osg::Vec2*) {}
100
101        virtual void setVertexArray(unsigned int ,const osg::Vec3* ) {}
102
103        virtual void setVertexArray(unsigned int,const osg::Vec4* ) {}
104
105        virtual void setVertexArray(unsigned int,const osg::Vec2d*) {}
106
107        virtual void setVertexArray(unsigned int ,const osg::Vec3d* ) {}
108
109        virtual void setVertexArray(unsigned int,const osg::Vec4d* ) {}
110
111        void write(unsigned int i)
112        {
113            _fout << (i + _lastVertexIndex) << "/";
114
115            if (_hasTexCoords || _hasNormalCoords)
116            {
117                if (_hasTexCoords)
118                    _fout << (i + _lastTexIndex);
119                _fout << "/";
120                if (_hasNormalCoords)
121                {
122                    if (_geo->getNormalBinding() == osg::Geometry::BIND_PER_VERTEX)
123                        _fout << (i+_lastNormalIndex);
124                    else
125                        _fout << (_normalIndex + _lastNormalIndex);
126                }
127            }
128            _fout << " ";
129        }
130
131        // operator for triangles
132        void writeTriangle(unsigned int i1, unsigned int i2, unsigned int i3)
133         {
134            _fout << "f ";
135            write(i1);
136            write(i2);
137            write(i3);
138            _fout << std::endl;
139            // not sure if this is correct?
140            if(_geo->getNormalBinding() && _geo->getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE) ++_normalIndex;
141        }
142
143        // operator for lines
144        void writeLine(unsigned int i1, unsigned int i2)
145        {
146            _fout << "l ";
147            write(i1);
148            write(i2);
149            _fout << std::endl;
150            // not sure if this is correct?
151            if(_geo->getNormalBinding() && _geo->getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE) ++_normalIndex;
152        }
153
154        // operator for points
155        void writePoint(unsigned int i1)
156        {
157            _fout << "p ";
158            write(i1);
159            _fout << std::endl;
160            // not sure if this is correct?
161            if(_geo->getNormalBinding() && _geo->getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE) ++_normalIndex;
162        }
163
164        virtual void begin(GLenum mode)
165        {
166            _modeCache = mode;
167            _indexCache.clear();
168        }
169
170        virtual void vertex(unsigned int vert)
171        {
172            _indexCache.push_back(vert);
173        }
174
175        virtual void end()
176        {
177            if (!_indexCache.empty())
178            {
179                drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
180            }
181        }
182
183        virtual void drawArrays(GLenum mode,GLint first,GLsizei count);
184
185        virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices)
186        {
187            drawElementsImplementation<GLubyte>(mode, count, indices);
188        }
189        virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices)
190        {
191            drawElementsImplementation<GLushort>(mode, count, indices);
192        }
193
194        virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices)
195        {
196            drawElementsImplementation<GLuint>(mode, count, indices);
197        }
198
199    protected:
200
201        template<typename T>void drawElementsImplementation(GLenum mode, GLsizei count, const T* indices)
202        {
203            if (indices==0 || count==0) return;
204
205            typedef const T* IndexPointer;
206
207            switch(mode)
208            {
209                case(GL_TRIANGLES):
210                {
211                    IndexPointer ilast = &indices[count];
212                    for(IndexPointer  iptr=indices;iptr<ilast;iptr+=3)
213                        writeTriangle(*iptr,*(iptr+1),*(iptr+2));
214
215                    break;
216                }
217                case(GL_TRIANGLE_STRIP):
218                {
219                    IndexPointer iptr = indices;
220                    for(GLsizei i=2;i<count;++i,++iptr)
221                    {
222                        if ((i%2)) writeTriangle(*(iptr),*(iptr+2),*(iptr+1));
223                        else       writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
224                    }
225                    break;
226                }
227                case(GL_QUADS):
228                {
229                    IndexPointer iptr = indices;
230                    for(GLsizei i=3;i<count;i+=4,iptr+=4)
231                    {
232                        writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
233                        writeTriangle(*(iptr),*(iptr+2),*(iptr+3));
234                    }
235                    break;
236                }
237                case(GL_QUAD_STRIP):
238                {
239                    IndexPointer iptr = indices;
240                    for(GLsizei i=3;i<count;i+=2,iptr+=2)
241                    {
242                        writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
243                        writeTriangle(*(iptr+1),*(iptr+3),*(iptr+2));
244                    }
245                    break;
246                }
247                case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
248                case(GL_TRIANGLE_FAN):
249                {
250                    IndexPointer iptr = indices;
251                    unsigned int first = *iptr;
252                    ++iptr;
253                    for(GLsizei i=2;i<count;++i,++iptr)
254                    {
255                        writeTriangle(first,*(iptr),*(iptr+1));
256                    }
257                    break;
258                }
259                case(GL_POINTS):
260                {
261                    IndexPointer ilast = &indices[count];
262                    for(IndexPointer  iptr=indices;iptr<ilast;++iptr)
263
264                    {
265                        writePoint(*iptr);
266                    }
267                    break;
268                }
269
270                case(GL_LINES):
271                {
272                    IndexPointer ilast = &indices[count];
273                    for(IndexPointer  iptr=indices;iptr<ilast;iptr+=2)
274                    {
275                        writeLine(*iptr, *(iptr+1));
276                    }
277                    break;
278                }
279                case(GL_LINE_STRIP):
280                {
281
282                    IndexPointer ilast = &indices[count];
283                    for(IndexPointer  iptr=indices+1;iptr<ilast;iptr+=2)
284
285                    {
286                        writeLine(*(iptr-1), *iptr);
287                    }
288                    break;
289                }
290                case(GL_LINE_LOOP):
291                {
292                    IndexPointer ilast = &indices[count];
293                    for(IndexPointer  iptr=indices+1;iptr<ilast;iptr+=2)
294                    {
295                        writeLine(*(iptr-1), *iptr);
296                    }
297                    writeLine(*ilast, *indices);
298                    break;
299                }
300
301                default:
302                    // uhm should never come to this point :)
303                    break;
304            }
305        }
306
307    private:
308
309        ObjPrimitiveIndexWriter& operator = (const ObjPrimitiveIndexWriter&) { return *this; }
310
311        std::ostream&         _fout;
312        GLenum               _modeCache;
313        std::vector<GLuint>  _indexCache;
314        unsigned int         _lastVertexIndex, _lastNormalIndex, _lastTexIndex;
315        bool                 _hasNormalCoords, _hasTexCoords;
316        osg::Geometry*         _geo;
317        unsigned int         _normalIndex;
318};
319
320
321void ObjPrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count)
322{
323    switch(mode)
324    {
325        case(GL_TRIANGLES):
326        {
327            unsigned int pos=first;
328            for(GLsizei i=2;i<count;i+=3,pos+=3)
329            {
330                writeTriangle(pos,pos+1,pos+2);
331            }
332            break;
333        }
334        case(GL_TRIANGLE_STRIP):
335         {
336            unsigned int pos=first;
337            for(GLsizei i=2;i<count;++i,++pos)
338            {
339                if ((i%2)) writeTriangle(pos,pos+2,pos+1);
340                else       writeTriangle(pos,pos+1,pos+2);
341            }
342            break;
343        }
344        case(GL_QUADS):
345        {
346            unsigned int pos=first;
347            for(GLsizei i=3;i<count;i+=4,pos+=4)
348            {
349                writeTriangle(pos,pos+1,pos+2);
350                writeTriangle(pos,pos+2,pos+3);
351            }
352            break;
353        }
354        case(GL_QUAD_STRIP):
355        {
356            unsigned int pos=first;
357            for(GLsizei i=3;i<count;i+=2,pos+=2)
358            {
359                writeTriangle(pos,pos+1,pos+2);
360                writeTriangle(pos+1,pos+3,pos+2);
361            }
362            break;
363        }
364        case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
365        case(GL_TRIANGLE_FAN):
366        {
367            unsigned int pos=first+1;
368            for(GLsizei i=2;i<count;++i,++pos)
369            {
370                writeTriangle(first,pos,pos+1);
371            }
372            break;
373        }
374        case(GL_POINTS):
375        {
376
377            for(GLsizei i=0;i<count;++i)
378            {
379                writePoint(i);
380            }
381            break;
382        }
383
384        case(GL_LINES):
385        {
386            for(GLsizei i=0;i<count;i+=2)
387            {
388                writeLine(i, i+1);
389            }
390            break;
391        }
392        case(GL_LINE_STRIP):
393        {
394            for(GLsizei i=1;i<count;++i)
395            {
396                writeLine(i-1, i);
397            }
398            break;
399        }
400        case(GL_LINE_LOOP):
401        {
402            for(GLsizei i=1;i<count;++i)
403            {
404                writeLine(i-1, i);
405            }
406            writeLine(count-1, 0);
407            break;
408        }
409        default:
410            OSG_WARN << "OBJWriterNodeVisitor :: can't handle mode " << mode << std::endl;
411            break;
412    }
413}
414
415
416OBJWriterNodeVisitor::OBJMaterial::OBJMaterial(osg::Material* mat, osg::Texture* tex) :
417    diffuse(1,1,1,1),
418    ambient(0.2,0.2,0.2,1),
419    specular(0,0,0,1),
420    image("")
421{
422    static unsigned int s_objmaterial_id = 0;
423    ++s_objmaterial_id;
424    std::stringstream ss;
425    ss << "material_" << s_objmaterial_id;
426    name = ss.str();
427
428    if (mat) {
429        diffuse = mat->getDiffuse(osg::Material::FRONT);
430        ambient = mat->getAmbient(osg::Material::FRONT);
431        specular = mat->getSpecular(osg::Material::FRONT);
432    }
433
434    if (tex) {
435        osg::Image* img = tex->getImage(0);
436        if ((img) && (!img->getFileName().empty()))
437            image = img->getFileName();
438
439    }
440
441}
442
443std::ostream& operator<<(std::ostream& fout, const OBJWriterNodeVisitor::OBJMaterial& mat) {
444
445    fout << "newmtl " << mat.name << std::endl;
446    fout << "       " << "Ka " << mat.ambient << std::endl;
447    fout << "       " << "Kd " << mat.diffuse << std::endl;
448    fout << "       " << "Ks " << mat.specular << std::endl;
449
450    if(!mat.image.empty())
451        fout << "       " << "map_Kd " << mat.image << std::endl;
452
453    return fout;
454
455}
456
457void OBJWriterNodeVisitor::writeMaterials(std::ostream& fout)
458{
459    for(MaterialMap::iterator i = _materialMap.begin(); i != _materialMap.end(); ++i)
460    {
461        fout << (*i).second << std::endl;
462    }
463}
464
465
466std::string OBJWriterNodeVisitor::getUniqueName(const std::string& defaultvalue) {
467
468    std::string name = "";
469    for(std::list<std::string>::iterator i = _nameStack.begin(); i != _nameStack.end(); ++i) {
470        if (!name.empty()) name+="_";
471        name += (*i);
472    }
473
474    if (!defaultvalue.empty())
475        name += "_" +defaultvalue;
476
477    if (_nameMap.find(name) == _nameMap.end())
478        _nameMap.insert(std::make_pair(name, 0u));
479
480    std::stringstream ss;
481    ss << name << "_" << _nameMap[name];
482    ++(_nameMap[name]);
483    return ss.str();
484
485}
486
487void OBJWriterNodeVisitor::processArray(const std::string& key, osg::Array* array, const osg::Matrix& m, bool isNormal)
488{
489    if (array == NULL)
490        return;
491
492    ValueVisitor vv(_fout, m, isNormal);
493    _fout << std::endl;
494    for(unsigned int i = 0; i < array->getNumElements(); ++i) {
495        _fout << key << ' ';
496        array->accept(i, vv);
497        _fout << std::endl;
498    }
499
500    _fout << "# " << array->getNumElements() << " elements written" << std::endl;
501
502}
503
504void OBJWriterNodeVisitor::processStateSet(osg::StateSet* ss)
505{
506    if (_materialMap.find(ss) != _materialMap.end()) {
507        _fout << "usemtl " << _materialMap[ss].name << std::endl;
508        return;
509    }
510
511    osg::Material* mat = dynamic_cast<osg::Material*>(ss->getAttribute(osg::StateAttribute::MATERIAL));
512    osg::Texture* tex = dynamic_cast<osg::Texture*>(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
513
514    if (mat || tex)
515    {
516        _materialMap.insert(std::make_pair(osg::ref_ptr<osg::StateSet>(ss), OBJMaterial(mat, tex)));
517        _fout << "usemtl " << _materialMap[ss].name << std::endl;
518    }
519
520}
521
522
523void OBJWriterNodeVisitor::processGeometry(osg::Geometry* geo, osg::Matrix& m) {
524    _fout << std::endl;
525    _fout << "o " << getUniqueName( geo->getName().empty() ? geo->className() : geo->getName() ) << std::endl;
526
527    processStateSet(_currentStateSet.get());
528
529    processArray("v", geo->getVertexArray(), m, false);
530    processArray("vn", geo->getNormalArray(), m, true);
531    processArray("vt", geo->getTexCoordArray(0)); // we support only tex-unit 0
532    unsigned int normalIndex = 0;
533    for(unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i)
534    {
535        osg::PrimitiveSet* ps = geo->getPrimitiveSet(i);
536
537        ObjPrimitiveIndexWriter pif(_fout, geo, normalIndex, _lastVertexIndex, _lastNormalIndex, _lastTexIndex);
538        ps->accept(pif);
539
540        if(geo->getNormalArray() && geo->getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET)
541            ++normalIndex;
542    }
543    if (geo->getVertexArray())
544        _lastVertexIndex += geo->getVertexArray()->getNumElements();
545    if (geo->getNormalArray())
546        _lastNormalIndex += geo->getNormalArray()->getNumElements();
547    if(geo->getTexCoordArray(0))
548        _lastTexIndex += geo->getTexCoordArray(0)->getNumElements();
549
550}
551
552void OBJWriterNodeVisitor::apply( osg::Geode &node )
553{
554
555    pushStateSet(node.getStateSet());
556    _nameStack.push_back(node.getName());
557    osg::Matrix m = osg::computeLocalToWorld(getNodePath());
558    unsigned int count = node.getNumDrawables();
559    for ( unsigned int i = 0; i < count; i++ )
560    {
561        osg::Geometry *g = node.getDrawable( i )->asGeometry();
562        if ( g != NULL )
563        {
564            pushStateSet(g->getStateSet());
565
566            processGeometry(g,m);
567
568            popStateSet(g->getStateSet());
569        }
570    }
571
572
573    popStateSet(node.getStateSet());
574    _nameStack.pop_back();
575}
576
577
Note: See TracBrowser for help on using the browser.