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

Revision 13502, 16.9 kB (checked in by robert, 15 hours ago)

Release OpenSceneGraph-3.3.3

  • 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        }
140
141        // operator for lines
142        void writeLine(unsigned int i1, unsigned int i2)
143        {
144            _fout << "l ";
145            write(i1);
146            write(i2);
147            _fout << std::endl;
148        }
149
150        // operator for points
151        void writePoint(unsigned int i1)
152        {
153            _fout << "p ";
154            write(i1);
155            _fout << std::endl;
156        }
157
158        virtual void begin(GLenum mode)
159        {
160            _modeCache = mode;
161            _indexCache.clear();
162        }
163
164        virtual void vertex(unsigned int vert)
165        {
166            _indexCache.push_back(vert);
167        }
168
169        virtual void end()
170        {
171            if (!_indexCache.empty())
172            {
173                drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
174            }
175        }
176
177        virtual void drawArrays(GLenum mode,GLint first,GLsizei count);
178
179        virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices)
180        {
181            drawElementsImplementation<GLubyte>(mode, count, indices);
182        }
183        virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices)
184        {
185            drawElementsImplementation<GLushort>(mode, count, indices);
186        }
187
188        virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices)
189        {
190            drawElementsImplementation<GLuint>(mode, count, indices);
191        }
192
193    protected:
194
195        template<typename T>void drawElementsImplementation(GLenum mode, GLsizei count, const T* indices)
196        {
197            if (indices==0 || count==0) return;
198
199            typedef const T* IndexPointer;
200
201            switch(mode)
202            {
203                case(GL_TRIANGLES):
204                {
205                    IndexPointer ilast = &indices[count];
206                    for(IndexPointer  iptr=indices;iptr<ilast;iptr+=3)
207                        writeTriangle(*iptr,*(iptr+1),*(iptr+2));
208
209                    break;
210                }
211                case(GL_TRIANGLE_STRIP):
212                {
213                    IndexPointer iptr = indices;
214                    for(GLsizei i=2;i<count;++i,++iptr)
215                    {
216                        if ((i%2)) writeTriangle(*(iptr),*(iptr+2),*(iptr+1));
217                        else       writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
218                    }
219                    break;
220                }
221                case(GL_QUADS):
222                {
223                    IndexPointer iptr = indices;
224                    for(GLsizei i=3;i<count;i+=4,iptr+=4)
225                    {
226                        writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
227                        writeTriangle(*(iptr),*(iptr+2),*(iptr+3));
228                    }
229                    break;
230                }
231                case(GL_QUAD_STRIP):
232                {
233                    IndexPointer iptr = indices;
234                    for(GLsizei i=3;i<count;i+=2,iptr+=2)
235                    {
236                        writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
237                        writeTriangle(*(iptr+1),*(iptr+3),*(iptr+2));
238                    }
239                    break;
240                }
241                case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
242                case(GL_TRIANGLE_FAN):
243                {
244                    IndexPointer iptr = indices;
245                    unsigned int first = *iptr;
246                    ++iptr;
247                    for(GLsizei i=2;i<count;++i,++iptr)
248                    {
249                        writeTriangle(first,*(iptr),*(iptr+1));
250                    }
251                    break;
252                }
253                case(GL_POINTS):
254                {
255                    IndexPointer ilast = &indices[count];
256                    for(IndexPointer  iptr=indices;iptr<ilast;++iptr)
257
258                    {
259                        writePoint(*iptr);
260                    }
261                    break;
262                }
263
264                case(GL_LINES):
265                {
266                    IndexPointer ilast = &indices[count];
267                    for(IndexPointer  iptr=indices;iptr<ilast;iptr+=2)
268                    {
269                        writeLine(*iptr, *(iptr+1));
270                    }
271                    break;
272                }
273                case(GL_LINE_STRIP):
274                {
275
276                    IndexPointer ilast = &indices[count];
277                    for(IndexPointer  iptr=indices+1;iptr<ilast;iptr+=2)
278
279                    {
280                        writeLine(*(iptr-1), *iptr);
281                    }
282                    break;
283                }
284                case(GL_LINE_LOOP):
285                {
286                    IndexPointer ilast = &indices[count];
287                    for(IndexPointer  iptr=indices+1;iptr<ilast;iptr+=2)
288                    {
289                        writeLine(*(iptr-1), *iptr);
290                    }
291                    writeLine(*ilast, *indices);
292                    break;
293                }
294
295                default:
296                    // uhm should never come to this point :)
297                    break;
298            }
299        }
300
301    private:
302
303        ObjPrimitiveIndexWriter& operator = (const ObjPrimitiveIndexWriter&) { return *this; }
304
305        std::ostream&         _fout;
306        GLenum               _modeCache;
307        std::vector<GLuint>  _indexCache;
308        unsigned int         _lastVertexIndex, _lastNormalIndex, _lastTexIndex;
309        bool                 _hasNormalCoords, _hasTexCoords;
310        osg::Geometry*         _geo;
311        unsigned int         _normalIndex;
312};
313
314
315void ObjPrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count)
316{
317    switch(mode)
318    {
319        case(GL_TRIANGLES):
320        {
321            unsigned int pos=first;
322            for(GLsizei i=2;i<count;i+=3,pos+=3)
323            {
324                writeTriangle(pos,pos+1,pos+2);
325            }
326            break;
327        }
328        case(GL_TRIANGLE_STRIP):
329         {
330            unsigned int pos=first;
331            for(GLsizei i=2;i<count;++i,++pos)
332            {
333                if ((i%2)) writeTriangle(pos,pos+2,pos+1);
334                else       writeTriangle(pos,pos+1,pos+2);
335            }
336            break;
337        }
338        case(GL_QUADS):
339        {
340            unsigned int pos=first;
341            for(GLsizei i=3;i<count;i+=4,pos+=4)
342            {
343                writeTriangle(pos,pos+1,pos+2);
344                writeTriangle(pos,pos+2,pos+3);
345            }
346            break;
347        }
348        case(GL_QUAD_STRIP):
349        {
350            unsigned int pos=first;
351            for(GLsizei i=3;i<count;i+=2,pos+=2)
352            {
353                writeTriangle(pos,pos+1,pos+2);
354                writeTriangle(pos+1,pos+3,pos+2);
355            }
356            break;
357        }
358        case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
359        case(GL_TRIANGLE_FAN):
360        {
361            unsigned int pos=first+1;
362            for(GLsizei i=2;i<count;++i,++pos)
363            {
364                writeTriangle(first,pos,pos+1);
365            }
366            break;
367        }
368        case(GL_POINTS):
369        {
370
371            for(GLsizei i=0;i<count;++i)
372            {
373                writePoint(i);
374            }
375            break;
376        }
377
378        case(GL_LINES):
379        {
380            for(GLsizei i=0;i<count;i+=2)
381            {
382                writeLine(i, i+1);
383            }
384            break;
385        }
386        case(GL_LINE_STRIP):
387        {
388            for(GLsizei i=1;i<count;++i)
389            {
390                writeLine(i-1, i);
391            }
392            break;
393        }
394        case(GL_LINE_LOOP):
395        {
396            for(GLsizei i=1;i<count;++i)
397            {
398                writeLine(i-1, i);
399            }
400            writeLine(count-1, 0);
401            break;
402        }
403        default:
404            OSG_WARN << "OBJWriterNodeVisitor :: can't handle mode " << mode << std::endl;
405            break;
406    }
407}
408
409
410OBJWriterNodeVisitor::OBJMaterial::OBJMaterial(osg::Material* mat, osg::Texture* tex) :
411    diffuse(1,1,1,1),
412    ambient(0.2,0.2,0.2,1),
413    specular(0,0,0,1),
414    image("")
415{
416    static unsigned int s_objmaterial_id = 0;
417    ++s_objmaterial_id;
418    std::stringstream ss;
419    ss << "material_" << s_objmaterial_id;
420    name = ss.str();
421
422    if (mat) {
423        diffuse = mat->getDiffuse(osg::Material::FRONT);
424        ambient = mat->getAmbient(osg::Material::FRONT);
425        specular = mat->getSpecular(osg::Material::FRONT);
426    }
427
428    if (tex) {
429        osg::Image* img = tex->getImage(0);
430        if ((img) && (!img->getFileName().empty()))
431            image = img->getFileName();
432
433    }
434
435}
436
437std::ostream& operator<<(std::ostream& fout, const OBJWriterNodeVisitor::OBJMaterial& mat) {
438
439    fout << "newmtl " << mat.name << std::endl;
440    fout << "       " << "Ka " << mat.ambient << std::endl;
441    fout << "       " << "Kd " << mat.diffuse << std::endl;
442    fout << "       " << "Ks " << mat.specular << std::endl;
443
444    if(!mat.image.empty())
445        fout << "       " << "map_Kd " << mat.image << std::endl;
446
447    return fout;
448
449}
450
451void OBJWriterNodeVisitor::writeMaterials(std::ostream& fout)
452{
453    for(MaterialMap::iterator i = _materialMap.begin(); i != _materialMap.end(); ++i)
454    {
455        fout << (*i).second << std::endl;
456    }
457}
458
459
460std::string OBJWriterNodeVisitor::getUniqueName(const std::string& defaultvalue) {
461
462    std::string name = "";
463    for(std::list<std::string>::iterator i = _nameStack.begin(); i != _nameStack.end(); ++i) {
464        if (!name.empty()) name+="_";
465        name += (*i);
466    }
467
468    if (!defaultvalue.empty())
469        name += "_" +defaultvalue;
470
471    if (_nameMap.find(name) == _nameMap.end())
472        _nameMap.insert(std::make_pair(name, 0u));
473
474    std::stringstream ss;
475    ss << name << "_" << _nameMap[name];
476    ++(_nameMap[name]);
477    return ss.str();
478
479}
480
481void OBJWriterNodeVisitor::processArray(const std::string& key, osg::Array* array, const osg::Matrix& m, bool isNormal)
482{
483    if (array == NULL)
484        return;
485
486    ValueVisitor vv(_fout, m, isNormal);
487    _fout << std::endl;
488    for(unsigned int i = 0; i < array->getNumElements(); ++i) {
489        _fout << key << ' ';
490        array->accept(i, vv);
491        _fout << std::endl;
492    }
493
494    _fout << "# " << array->getNumElements() << " elements written" << std::endl;
495
496}
497
498void OBJWriterNodeVisitor::processStateSet(osg::StateSet* ss)
499{
500    if (_materialMap.find(ss) != _materialMap.end()) {
501        _fout << "usemtl " << _materialMap[ss].name << std::endl;
502        return;
503    }
504
505    osg::Material* mat = dynamic_cast<osg::Material*>(ss->getAttribute(osg::StateAttribute::MATERIAL));
506    osg::Texture* tex = dynamic_cast<osg::Texture*>(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
507
508    if (mat || tex)
509    {
510        _materialMap.insert(std::make_pair(osg::ref_ptr<osg::StateSet>(ss), OBJMaterial(mat, tex)));
511        _fout << "usemtl " << _materialMap[ss].name << std::endl;
512    }
513
514}
515
516
517void OBJWriterNodeVisitor::processGeometry(osg::Geometry* geo, osg::Matrix& m) {
518    _fout << std::endl;
519    _fout << "o " << getUniqueName( geo->getName().empty() ? geo->className() : geo->getName() ) << std::endl;
520
521    if (geo->containsDeprecatedData()) geo->fixDeprecatedData();
522   
523    processStateSet(_currentStateSet.get());
524
525    processArray("v", geo->getVertexArray(), m, false);
526    processArray("vn", geo->getNormalArray(), m, true);
527    processArray("vt", geo->getTexCoordArray(0)); // we support only tex-unit 0
528    unsigned int normalIndex = 0;
529    for(unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i)
530    {
531        osg::PrimitiveSet* ps = geo->getPrimitiveSet(i);
532
533        ObjPrimitiveIndexWriter pif(_fout, geo, normalIndex, _lastVertexIndex, _lastNormalIndex, _lastTexIndex);
534        ps->accept(pif);
535
536        if(geo->getNormalArray() && geo->getNormalBinding() == osg::Geometry::BIND_PER_PRIMITIVE_SET)
537            ++normalIndex;
538    }
539    if (geo->getVertexArray())
540        _lastVertexIndex += geo->getVertexArray()->getNumElements();
541    if (geo->getNormalArray())
542        _lastNormalIndex += geo->getNormalArray()->getNumElements();
543    if(geo->getTexCoordArray(0))
544        _lastTexIndex += geo->getTexCoordArray(0)->getNumElements();
545
546}
547
548void OBJWriterNodeVisitor::apply( osg::Geode &node )
549{
550
551    pushStateSet(node.getStateSet());
552    _nameStack.push_back(node.getName());
553    osg::Matrix m = osg::computeLocalToWorld(getNodePath());
554    unsigned int count = node.getNumDrawables();
555    for ( unsigned int i = 0; i < count; i++ )
556    {
557        osg::Geometry *g = node.getDrawable( i )->asGeometry();
558        if ( g != NULL )
559        {
560            pushStateSet(g->getStateSet());
561
562            processGeometry(g,m);
563
564            popStateSet(g->getStateSet());
565        }
566    }
567
568
569    popStateSet(node.getStateSet());
570    _nameStack.pop_back();
571}
572
573
Note: See TracBrowser for help on using the browser.