root/OpenSceneGraph/trunk/src/osgPlugins/3ds/WriterNodeVisitor.cpp @ 10932

Revision 10932, 30.6 kB (checked in by robert, 5 years ago)

From Sukender, "ReaderWriter?3DS.cpp fixes:
- Fixed creation of useless intermediate nodes

WriterNodeVisitor?.cpp fixes:
- Fixed naming of textures (path and extension)
"

Note from Robert Osfield, this submission also came with changes to use of ref_ptr<> and removal of delete[]'s, but these were not merged as they didn't actually fix any memory leaks, and in once instance introduced one.

RevLine 
[10932]1// -*-c++-*-
2
3/*
4 * 3DS reader/writer for Open Scene Graph
5 *
6 * Copyright (C) ???
7 *
8 * Writing support added 2007 by Sukender (Benoit Neil), http://sukender.free.fr,
9 * strongly inspired by the OBJ writer object by Stephan Huber
10 *
11 * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
12 * real-time rendering of large 3D photo-realistic models.
13 * The OSG homepage is http://www.openscenegraph.org/
14 */
15
16#include <osg/io_utils>
17#include <osg/CullFace>
18#include <osgDB/WriteFile>
19
20#include "WriterNodeVisitor.h"
21#include <assert.h>
22#include <string.h>
23
24
25void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix)
26{
27    for(int row=0; row<4; ++row) {
28        lib3ds_matrix[row][0] = osg_matrix.ptr()[row*4+0];
29        lib3ds_matrix[row][1] = osg_matrix.ptr()[row*4+1];
30        lib3ds_matrix[row][2] = osg_matrix.ptr()[row*4+2];
31        lib3ds_matrix[row][3] = osg_matrix.ptr()[row*4+3];
32    }
33}
34
35inline void copyOsgVectorToLib3dsVector(Lib3dsVector lib3ds_vector, const osg::Vec3f& osg_vector) {
36    lib3ds_vector[0] = osg_vector[0];
37    lib3ds_vector[1] = osg_vector[1];
38    lib3ds_vector[2] = osg_vector[2];
39}
40inline void copyOsgVectorToLib3dsVector(Lib3dsVector lib3ds_vector, const osg::Vec3d& osg_vector) {
41    lib3ds_vector[0] = osg_vector[0];
42    lib3ds_vector[1] = osg_vector[1];
43    lib3ds_vector[2] = osg_vector[2];
44}
45
46inline void copyOsgColorToLib3dsColor(Lib3dsVector lib3ds_vector, const osg::Vec4f& osg_vector) {
47    lib3ds_vector[0] = osg_vector[0];
48    lib3ds_vector[1] = osg_vector[1];
49    lib3ds_vector[2] = osg_vector[2];
50}
51inline void copyOsgColorToLib3dsColor(Lib3dsVector lib3ds_vector, const osg::Vec4d& osg_vector) {
52    lib3ds_vector[0] = osg_vector[0];
53    lib3ds_vector[1] = osg_vector[1];
54    lib3ds_vector[2] = osg_vector[2];
55}
56
57inline void copyOsgQuatToLib3dsQuat(float lib3ds_vector[4], const osg::Quat& osg_quat) {
58    //lib3ds_vector[0] = osg_quat[3];        // Not sure
59    //lib3ds_vector[1] = osg_quat[0];
60    //lib3ds_vector[2] = osg_quat[1];
61    //lib3ds_vector[3] = osg_quat[2];
62    // 3DS seems to store (angle in radians, axis_x, axis_y, axis_z), but it works with (axis_x, axis_y, axis_z, -angle in radians)!
63    osg::Quat::value_type angle, x, y, z;
64    osg_quat.getRotate(angle, x, y, z);
65    lib3ds_vector[0] = static_cast<float>(x);
66    lib3ds_vector[1] = static_cast<float>(y);
67    lib3ds_vector[2] = static_cast<float>(z);
68    lib3ds_vector[3] = static_cast<float>(-angle);
69}
70
71std::string getFileName(const std::string & path) {
72    unsigned int slashPos = path.find_last_of("/\\");
73    if (slashPos == std::string::npos) return path;
74    return path.substr(slashPos+1);
75}
76
77
78/// Checks if a filename (\b not path) is 8.3 (an empty name is never 8.3, and a path is never 8.3).
79bool is83(const std::string & s) {
80    // 012345678901
81    // ABCDEFGH.ABC
82    if (s.find_first_of("/\\") != std::string::npos) return false;            // It should not be a path, but a filename
83    unsigned int len = s.length();
84    if (len > 12 || len == 0) return false;
85    unsigned int pointPos = s.rfind('.');
86    if (pointPos == std::string::npos) return len <= 8;        // Without point
87    // With point
88    if (pointPos > 8) return false;
89    if (len-1 - pointPos > 3) return false;
90    return true;
91}
92
93/// Tests if the given string is a path supported by 3DS format (8.3, 63 chars max).
94bool is3DSpath(const std::string & s) {
95    unsigned int len = s.length();
96    if (len >= 64 || len == 0) return false;
97
98    unsigned int tokenBegin = 0;
99    for (unsigned int tokenEnd=0; tokenEnd != std::string::npos; tokenBegin = tokenEnd+1) {
100        tokenEnd = s.find_first_of("/\\", tokenBegin);
101        if ( !is83(s.substr(tokenBegin, tokenEnd-tokenBegin-1)) ) return false;
102    }
103    return true;
104}
105
106
107
108/** writes all primitives of a primitive-set out to a stream, decomposes quads to triangles, line-strips to lines etc */
109class PrimitiveIndexWriter : public osg::PrimitiveIndexFunctor {
110public:
111      PrimitiveIndexWriter(osg::Geometry  *    geo,
112                           ListTriangle  &    listTriangles,
113                           unsigned int        drawable_n,
114                           unsigned int        material) :
115          osg::PrimitiveIndexFunctor(),
116          _drawable_n(drawable_n),
117          _listTriangles(listTriangles),
118          _hasNormalCoords(geo->getNormalArray() != NULL),
119          _hasTexCoords(geo->getTexCoordArray(0) != NULL),
120          _geo(geo),
121          _lastFaceIndex(0),
122          _material(material)
123      {
124      }
125
126      unsigned int getNextFaceIndex() { return _lastFaceIndex; }
127
128      virtual void setVertexArray(unsigned int,const osg::Vec2*) {}
129
130      virtual void setVertexArray(unsigned int count,const osg::Vec3* vecs) {}
131
132      virtual void setVertexArray(unsigned int,const osg::Vec4* ) {}
133
134      virtual void setVertexArray(unsigned int,const osg::Vec2d*) {}
135
136      virtual void setVertexArray(unsigned int ,const osg::Vec3d* ) {}
137      virtual void setVertexArray(unsigned int,const osg::Vec4d* ) {}
138
139
140      // operator for triangles
141      void writeTriangle(unsigned int i1, unsigned int i2, unsigned int i3)
142      {
143          Triangle triangle;
144          triangle.t1 = i1;
145          triangle.t2 = i2;
146          triangle.t3 = i3;
147          triangle.material = _material;
148          _listTriangles.push_back(std::make_pair(triangle, _drawable_n));
149      }
150      virtual void begin(GLenum mode)
151      {
152          _modeCache = mode;
153          _indexCache.clear();
154      }
155
156      virtual void vertex(unsigned int vert)
157      {
158          _indexCache.push_back(vert);
159      }
160
161      virtual void end()
162      {
163          if (!_indexCache.empty())
164          {
165              drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
166          }
167      }
168
169      virtual void drawArrays(GLenum mode,GLint first,GLsizei count);
170
171      virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices)
172      {
173          drawElementsImplementation<GLubyte>(mode, count, indices);
174      }
175      virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices)
176      {
177          drawElementsImplementation<GLushort>(mode, count, indices);
178      }
179
180      virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices)
181      {
182          drawElementsImplementation<GLuint>(mode, count, indices);
183      }
184
185protected:
186
187    template<typename T>void drawElementsImplementation(GLenum mode, GLsizei count, const T* indices)
188    {
189        if (indices==0 || count==0) return;
190
191        typedef const T* IndexPointer;
192
193        switch(mode)
194        {
195        case(GL_TRIANGLES):
196            {
197                //lib3ds_mesh_resize_faces(_mesh, _lastFaceIndex + count / 3);
198                IndexPointer ilast = &indices[count];
199                for(IndexPointer  iptr=indices;iptr<ilast;iptr+=3)
200                    writeTriangle(*iptr,*(iptr+1),*(iptr+2));
201
202                break;
203            }
204        case(GL_TRIANGLE_STRIP):
205            {
206                //lib3ds_mesh_resize_faces(_mesh, _lastFaceIndex + count -2);
207                IndexPointer iptr = indices;
208                for(GLsizei i=2;i<count;++i,++iptr)
209                {
210                    if ((i%2)) writeTriangle(*(iptr),*(iptr+2),*(iptr+1));
211                    else       writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
212                }
213                break;
214            }
215        case(GL_QUADS):
216            {
217                //lib3ds_mesh_resize_faces(_mesh, _lastFaceIndex + count /2);        // count/4*2
218                IndexPointer iptr = indices;
219                for(GLsizei i=3;i<count;i+=4,iptr+=4)
220                {
221                    writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
222                    writeTriangle(*(iptr),*(iptr+2),*(iptr+3));
223                }
224                break;
225            }
226        case(GL_QUAD_STRIP):
227            {
228                //lib3ds_mesh_resize_faces(_mesh, _lastFaceIndex + (count / 2 -1)*2);
229                IndexPointer iptr = indices;
230                for(GLsizei i=3;i<count;i+=2,iptr+=2)
231                {
232                    writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
233                    writeTriangle(*(iptr+1),*(iptr+3),*(iptr+2));
234                }
235                break;
236            }
237        case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
238        case(GL_TRIANGLE_FAN):
239            {
240                //lib3ds_mesh_resize_faces(_mesh, _lastFaceIndex + count -2);
241                IndexPointer iptr = indices;
242                unsigned int first = *iptr;
243                ++iptr;
244                for(GLsizei i=2;i<count;++i,++iptr)
245                {
246                    writeTriangle(first,*(iptr),*(iptr+1));
247                }
248                break;
249            }
250        case(GL_POINTS):
251        case(GL_LINES):
252        case(GL_LINE_STRIP):
253        case(GL_LINE_LOOP):
254            // Not handled
255            break;
256
257        default:
258            // uhm should never come to this point :)
259            break;
260        }
261    }
262
263private:
264
265    PrimitiveIndexWriter& operator = (const PrimitiveIndexWriter&) { return *this; }
266
267    unsigned int         _drawable_n;
268    ListTriangle    &     _listTriangles;
269    GLenum               _modeCache;
270    std::vector<GLuint>  _indexCache;
271    bool                 _hasNormalCoords, _hasTexCoords;
272    osg::Geometry*       _geo;
273    unsigned int         _lastFaceIndex;
274    unsigned int         _material;
275};
276
277
278void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count)
279{
280    switch(mode)
281    {
282    case(GL_TRIANGLES):
283        {
284            unsigned int pos=first;
285            for(GLsizei i=2;i<count;i+=3,pos+=3)
286            {
287                writeTriangle(pos,pos+1,pos+2);
288            }
289            break;
290        }
291    case(GL_TRIANGLE_STRIP):
292        {
293            unsigned int pos=first;
294            for(GLsizei i=2;i<count;++i,++pos)
295            {
296                if ((i%2)) writeTriangle(pos,pos+2,pos+1);
297                else       writeTriangle(pos,pos+1,pos+2);
298            }
299            break;
300        }
301    case(GL_QUADS):
302        {
303            unsigned int pos=first;
304            for(GLsizei i=3;i<count;i+=4,pos+=4)
305            {
306                writeTriangle(pos,pos+1,pos+2);
307                writeTriangle(pos,pos+2,pos+3);
308            }
309            break;
310        }
311    case(GL_QUAD_STRIP):
312        {
313            unsigned int pos=first;
314            for(GLsizei i=3;i<count;i+=2,pos+=2)
315            {
316                writeTriangle(pos,pos+1,pos+2);
317                writeTriangle(pos+1,pos+3,pos+2);
318            }
319            break;
320        }
321    case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
322    case(GL_TRIANGLE_FAN):
323        {
324            unsigned int pos=first+1;
325            for(GLsizei i=2;i<count;++i,++pos)
326            {
327                writeTriangle(first,pos,pos+1);
328            }
329            break;
330        }
331    case(GL_POINTS):
332    case(GL_LINES):
333    case(GL_LINE_STRIP):
334    case(GL_LINE_LOOP):
335        //break;
336    default:
337        osg::notify(osg::WARN) << "WriterNodeVisitor :: can't handle mode " << mode << std::endl;
338        break;
339    }
340}
341
342
343
344WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg::StateSet * stateset, osg::Material* mat, osg::Texture* tex, int index) :
345    index(index),
346    diffuse(1,1,1,1),
347    ambient(0.2,0.2,0.2,1),
348    specular(0,0,0,1),
349    shininess(0),
350    transparency(0),
351    double_sided(false),
352    image(NULL),
353    texture_transparency(false),
354    texture_no_tile(false)
355{
356    //static unsigned int s_objmaterial_id = 0;
357    //++s_objmaterial_id;
358    if (mat) {
359        assert(stateset);
360        diffuse = mat->getDiffuse(osg::Material::FRONT);
361        ambient = mat->getAmbient(osg::Material::FRONT);
362        specular = mat->getSpecular(osg::Material::FRONT);
363        shininess = mat->getShininess(osg::Material::FRONT);
364        transparency = 1-diffuse.w();
365        name = writerNodeVisitor.getUniqueName(mat->getName(),"mat");
366        osg::StateAttribute * attribute = stateset->getAttribute(osg::StateAttribute::CULLFACE);
367        if (!attribute) {
368            double_sided = true;
369        } else {
370            assert(dynamic_cast<osg::CullFace *>(attribute));
371            osg::CullFace::Mode mode = static_cast<osg::CullFace *>(attribute)->getMode();
372            if (mode == osg::CullFace::BACK) double_sided = false;
373            else if (mode == osg::CullFace::FRONT) {
374                osg::notify(osg::WARN) << "3DS Writer: Reversed face (culled FRONT) not supported yet." << std::endl;
375                double_sided = false;
376            }
377            else {
378                assert(mode == osg::CullFace::FRONT_AND_BACK);
379                osg::notify(osg::WARN) << "3DS Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl;
380                double_sided = false;
381            }
382        }
383    }
384    if (tex) {
385        osg::Image* img = tex->getImage(0);
386        if(img)
387        {
388            texture_transparency = (stateset->getMode(GL_BLEND) == osg::StateAttribute::ON);
389            texture_no_tile = (tex->getWrap(osg::Texture2D::WRAP_S) == osg::Texture2D::CLAMP);
390            image = img;
391        }
392    }
393
394    if (name.empty()) {
395        std::stringstream ss;
396        ss << "m" << index;
397        name = ss.str();
398    }
399}
400
401
402std::string
403getPathRelative(const std::string & srcBad,
404                const std::string & dstBad)
405{
406    if(srcBad.empty())
407        return osgDB::getSimpleFileName(dstBad);
408    const std::string & src = osgDB::convertFileNameToNativeStyle(srcBad);
409    const std::string & dst = osgDB::convertFileNameToNativeStyle(dstBad);
410    std::string::const_iterator itDst = dst.begin();
411    std::string::const_iterator itSrc = src.begin();
412
413    std::string result = "";
414
415    while(itDst != dst.end())
416    {
417        if (itSrc != src.end() && *itDst == *itSrc)
418            ++itSrc;
419        else if (!result.empty() || *itDst != '\\'
420            result += *itDst;
421        ++itDst;
422    }
423    if (itSrc != src.end())
424        result = osgDB::getSimpleFileName(dst);
425    return result;
426}
427
428void WriterNodeVisitor::writeMaterials()
429{
430    unsigned int nbMat = _materialMap.size();
431    lib3ds_file_reserve_materials(file3ds, nbMat, 1);
432    // Ugly thing: it seems lib3ds_file_insert_material() doesn't support insertion in a random order (else materials are not assigned the right way)
433    for (unsigned int iMat=0; iMat<nbMat; ++iMat)
434    {
435        bool found = false;
436        for(MaterialMap::iterator itr = _materialMap.begin(); itr != _materialMap.end(); ++itr)
437        {
438            const Material & mat = itr->second;
439            if (mat.index != static_cast<int>(iMat)) continue;        // Ugly thing (2)
440            found = true;
441
442            assert(mat.index>=0 && mat.index < static_cast<int>(_materialMap.size()));
443            Lib3dsMaterial * mat3ds = lib3ds_material_new(getFileName(mat.name).c_str());
444            copyOsgColorToLib3dsColor(mat3ds->ambient,  mat.ambient);
445            copyOsgColorToLib3dsColor(mat3ds->diffuse,  mat.diffuse);
446            copyOsgColorToLib3dsColor(mat3ds->specular, mat.specular);
447            mat3ds->shininess = mat.shininess;
448            mat3ds->transparency = mat.transparency;
449            mat3ds->two_sided = mat.double_sided ? 1 : 0;
450            if (mat.image)
451            {
452                Lib3dsTextureMap & tex = mat3ds->texture1_map;
453                std::string path;
454                if(mat.image->getFileName().empty())
455                {
456                    std::ostringstream oss;
457                    oss << "Image_" << _imageCount++ << ".rgb";
458                    path = oss.str();
459                }
460                else {
461                    path = getPathRelative(_srcDirectory, mat.image->getFileName());
462                }
463                if(!is3DSpath(path)) {
464                    path = getUniqueName(path, "", true);
465                    //path = osgDB::getSimpleFileName(path);
466                }
467
468                strcpy(tex.name, path.c_str());
469                path = osgDB::concatPaths(_directory, path);
470                osgDB::makeDirectoryForFile(path);
471
472                //if (mat.image->valid()) osgDB::writeImageFile(*(mat.image), path);
473                osgDB::writeImageFile(*(mat.image), path);
474                if (mat.texture_transparency) tex.flags |= LIB3DS_TEXTURE_ALPHA_SOURCE;
475                if (mat.texture_no_tile) tex.flags |= LIB3DS_TEXTURE_NO_TILE;
476            }
477            if (!suceedLastApply())
478                return;
479            lib3ds_file_insert_material(file3ds, mat3ds, itr->second.index);
480            break;        // Ugly thing (3)
481        }
482        if (!found) throw "Implementation error";                // Ugly thing (4)
483    }
484}
485
486
487std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, const std::string & _defaultPrefix, bool nameIsPath) {
488    if (_defaultPrefix.length()>=4) throw "Default prefix is too long";            // Arbitrarily defined to 3 chars. You can modify this, but you may have to change the code so that finding a number is okay, even when changing the default prefix length.
489
490    // Tests if default name is valid and unique
491    bool defaultIs83 = is83(_defaultValue);
492    bool defaultIsValid = nameIsPath ? is3DSpath(_defaultValue) : defaultIs83;
493    if (defaultIsValid && _nameMap.find(_defaultValue) == _nameMap.end()) {
494        _nameMap.insert(_defaultValue);
495        return _defaultValue;
496    }
497
498    // Handling of paths is not well done yet. Defaulting to something very simple.
499    // We should actually ensure each component is 8 chars long, and final filename is 8.3, and total is <64 chars.
500    std::string defaultValue(nameIsPath ? osgDB::getSimpleFileName(_defaultValue) : _defaultValue);
501    std::string ext(nameIsPath ? osgDB::getFileExtensionIncludingDot(_defaultValue).substr(0, std::min<unsigned int>(_defaultValue.size(), 4)) : "");        // 4 chars = dot + 3 chars
502
503    std::string defaultPrefix(_defaultPrefix.empty() ? "_" : _defaultPrefix);
504
505    unsigned int max_val = 0;
506    std::string truncDefaultValue = "";
507    for (unsigned int i = 0; i < std::min<unsigned int>(defaultValue.size(), 4); ++i)
508    {
509        if (defaultValue[i] == '.')
510        {
511            truncDefaultValue = defaultValue.substr(0, i);
512            break;
513        }
514    }
515    if (truncDefaultValue.empty())
516        truncDefaultValue = defaultValue.substr(0, std::min<unsigned int>(defaultValue.size(), 4));
517    std::map<std::string, unsigned int>::iterator pairPrefix;
518    defaultIs83 = is83(truncDefaultValue);
519    if (defaultIs83)
520    {
521        max_val = static_cast<unsigned int>(pow(10., 8. - truncDefaultValue.length() - 1)) -1;        // defaultPrefix.length()-1 because we add an underscore ("_")
522        pairPrefix = _mapPrefix.find(truncDefaultValue);
523    } 
524
525    if (defaultIs83 && (_mapPrefix.end() == pairPrefix || pairPrefix->second <= max_val))
526    {
527        defaultPrefix = truncDefaultValue;
528    }
529    else
530    {
531        max_val = static_cast<unsigned int>(pow(10., 8. - defaultPrefix.length() - 1)) -  1;        // defaultPrefix.length()-1 because we add an underscore ("_")
532        pairPrefix = _mapPrefix.find(defaultPrefix);
533    }
534
535    unsigned int searchStart = 0;
536    if (pairPrefix != _mapPrefix.end())
537        searchStart = pairPrefix->second;
538
539    for(unsigned int i = searchStart; i <= max_val; ++i) {
540        std::stringstream ss;
541        ss << defaultPrefix << "_" << i;
542        const std::string & res = ss.str();
543        if (_nameMap.find(res) == _nameMap.end()) {
544            if (pairPrefix != _mapPrefix.end())
545            {
546                pairPrefix->second = i + 1;
547            }
548            else
549            {
550                _mapPrefix.insert(std::make_pair(defaultPrefix, i + 1));
551            }
552            _nameMap.insert(res);
553            return res + ext;
554        }
555    }
556    if (defaultPrefix == "_") _lastGeneratedNumberedName = max_val;
557    throw "No more names available! Is default prefix too long?";
558}
559
560int WriterNodeVisitor::processStateSet(osg::StateSet* ss)
561{
562    MaterialMap::const_iterator itr = _materialMap.find(ss);
563    if (itr != _materialMap.end()) {
564        assert(itr->second.index>=0);
565        return itr->second.index;
566    }
567
568    osg::Material* mat = dynamic_cast<osg::Material*>(ss->getAttribute(osg::StateAttribute::MATERIAL));
569    osg::Texture* tex = dynamic_cast<osg::Texture*>(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
570
571    if (mat || tex)
572    {
573        int matNum = _lastMaterialIndex;
574        _materialMap.insert(std::make_pair(osg::ref_ptr<osg::StateSet>(ss), Material(*this, ss, mat, tex, matNum) ));
575        ++_lastMaterialIndex;
576        return matNum;
577    }
578    return -1;
579}
580
581/**
582*  Add a vertice to the index and link him with the Triangle index and the drawable.
583*  \param index_vert is the map where the vertice are stored.
584*  \param index is the indice of the vertice's position in the vec3.
585*  \param drawable_n is the number of the drawable.
586*  \return the position of the vertice in the final mesh.
587*/
588unsigned int
589WriterNodeVisitor::getMeshIndexForGeometryIndex(MapIndices & index_vert,
590                                                unsigned int index,
591                                                unsigned int drawable_n)
592{
593    MapIndices::iterator itIndex = index_vert.find(std::make_pair(index, drawable_n));
594    if (itIndex == index_vert.end()) {
595        unsigned int indexMesh = index_vert.size();
596        index_vert.insert(std::make_pair(std::make_pair(index, drawable_n), indexMesh));
597        return indexMesh;
598    }
599    return itIndex->second;
600}
601
602
603void 
604WriterNodeVisitor::buildMesh(osg::Geode                  &    geo,
605                             MapIndices                  &    index_vert,
606                             bool                        texcoords,
607                             Lib3dsMesh                  *    mesh)
608{
609    osg::notify(osg::DEBUG_INFO) << "Building Mesh" << std::endl;
610
611    if (!mesh) throw "Allocation error";        // TODO
612
613    lib3ds_mesh_resize_vertices(mesh, index_vert.size(), texcoords ? 1 : 0, 0);
614    // Write points
615
616    for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end();++it)
617    {
618        osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry();
619        assert(g->getVertexArray());
620        if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType)
621            throw "Vertex array is not Vec3. Not implemented";        // TODO
622        const osg::Vec3Array & vecs= *static_cast<osg::Vec3Array *>(g->getVertexArray());
623        copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]);
624    }
625
626    // Write texture coords (Texture 0 only)
627    if (texcoords)
628    {
629        for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end(); ++it)
630        {
631            osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry();
632            osg::Array * array = g->getTexCoordArray(0);
633            if(array)
634            {
635                if (g->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType)
636                    throw "Texture coords array is not Vec2. Not implemented";        // TODO
637                const osg::Vec2Array & vecs= *static_cast<osg::Vec2Array *>(array);
638                mesh->texcos[it->second][0] = vecs[it->first.first][0];
639                mesh->texcos[it->second][1] = vecs[it->first.first][1];
640            }
641        }
642    }
643    lib3ds_file_insert_mesh(file3ds, mesh, _lastMeshIndex);
644    ++_lastMeshIndex;
645
646    Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(mesh, mesh->name, NULL, NULL, NULL);
647    lib3ds_file_append_node(file3ds, reinterpret_cast<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(_cur3dsNode));
648}
649
650unsigned int 
651WriterNodeVisitor::calcVertices(osg::Geode & geo)
652{
653    unsigned int numVertice = 0;
654    for (unsigned int i = 0; i < geo.getNumDrawables(); ++i)
655    {
656        osg::Geometry *g = geo.getDrawable( i )->asGeometry();
657        assert(g->getVertexArray());
658        if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType)
659            throw "Vertex array is not Vec3. Not implemented";        // TODO
660        const osg::Vec3Array & vecs= *static_cast<osg::Vec3Array *>(g->getVertexArray());
661        numVertice += vecs.getNumElements();
662    }
663    return numVertice;
664}
665
666
667void
668WriterNodeVisitor::buildFaces(osg::Geode     &    geo,
669                              ListTriangle   &    listTriangles,
670                              bool                texcoords)
671{
672    MapIndices index_vert;
673    unsigned int nbFace = 0;
674    Lib3dsMesh *mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str() );
675    unsigned int nbTriangles = listTriangles.size();
676
677    lib3ds_mesh_resize_faces(mesh, nbTriangles);
678
679    unsigned int nbVertices = calcVertices(geo);
680    if (listTriangles.size() >= MAX_FACES-2 ||
681       ((nbVertices) >= MAX_VERTICES-2))
682    {
683        osg::notify(osg::ALWAYS) << "Sorting elements..." << std::endl;
684        WriterCompareTriangle cmp(geo, nbVertices);
685        std::sort(listTriangles.begin(), listTriangles.end(), cmp);
686    }
687
688    for (ListTriangle::iterator it = listTriangles.begin(); it != listTriangles.end(); ++it) //Go through the triangle list to define meshs
689    {
690        // Using -2 due to the fact that we treat 3 faces in one time (=the algorithm may overrun the limit by 2).
691        if ((index_vert.size() >= MAX_VERTICES-2 ||        // If mesh is full
692            nbFace >= MAX_FACES-2))
693        {
694            // Finnishing mesh
695            lib3ds_mesh_resize_faces(mesh, nbFace);
696            buildMesh(geo, index_vert, texcoords, mesh);
697
698            // Creating a new mesh
699            index_vert.clear();
700            mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str());
701            nbTriangles -= nbFace;
702            nbFace = 0;
703            lib3ds_mesh_resize_faces(mesh, nbTriangles);
704        }
705        Lib3dsFace & face = mesh->faces[nbFace++];
706        face.index[0] = getMeshIndexForGeometryIndex(index_vert, it->first.t1, it->second);
707        face.index[1] = getMeshIndexForGeometryIndex(index_vert, it->first.t2, it->second);
708        face.index[2] = getMeshIndexForGeometryIndex(index_vert, it->first.t3, it->second);
709        face.material = it->first.material;
710    }
711    buildMesh(geo, index_vert, texcoords, mesh); //When a Mesh is completed without restriction of vertices number
712}
713
714void 
715WriterNodeVisitor::createListTriangle(osg::Geometry    *    geo,
716                                      ListTriangle    &    listTriangles,
717                                      bool            &    texcoords,
718                                      unsigned int    &   drawable_n)
719{
720    unsigned int nbVertices = 0;
721    {
722        if (geo->getVertexArray() && geo->getVertexArray()->getType() != osg::Array::Vec3ArrayType)
723            throw "Vertex array is not Vec3. Not implemented";        // TODO
724        const osg::Vec3Array * vecs = geo->getVertexArray() ? static_cast<osg::Vec3Array *>(geo->getVertexArray()) : NULL;
725        if (vecs)
726        {
727            nbVertices = geo->getVertexArray()->getNumElements();
728            // Texture coords
729            if (geo->getTexCoordArray(0) && geo->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType)
730                throw "Texture coords array is not Vec2. Not implemented";        // TODO
731            const osg::Vec2Array * texvecs = geo->getTexCoordArray(0) ? static_cast<osg::Vec2Array *>(geo->getTexCoordArray(0)) : NULL;
732            if (texvecs)
733            {
734                unsigned int nb = geo->getTexCoordArray(0)->getNumElements();
735                if (nb != geo->getVertexArray()->getNumElements()) throw "There are more/less texture coords than vertices!";
736                texcoords = true;
737            }
738        }
739    }
740
741    if (nbVertices==0) return;
742
743    int material = processStateSet(_currentStateSet.get());   
744
745    for(unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i) //Fill the Triangle List
746    {
747        osg::PrimitiveSet* ps = geo->getPrimitiveSet(i);
748        PrimitiveIndexWriter pif(geo, listTriangles, drawable_n, material);
749        ps->accept(pif);
750    }
751}
752
753bool WriterNodeVisitor::suceedLastApply() const
754{
755    return _suceedLastApply;
756}
757
758void WriterNodeVisitor::failedApply()
759{
760    _suceedLastApply = false;
761    osg::notify(osg::NOTICE) << "Error going through node" << std::endl;
762}
763
764void WriterNodeVisitor::apply( osg::Geode &node )
765{
766    pushStateSet(node.getStateSet());
767    //_nameStack.push_back(node.getName());
768    //osg::Matrix m = osg::computeLocalToWorld(getNodePath());
769    unsigned int count = node.getNumDrawables();
770    ListTriangle listTriangles;
771    bool texcoords = false;
772    for ( unsigned int i = 0; i < count; i++ )
773    {
774        osg::Geometry *g = node.getDrawable( i )->asGeometry();
775        if ( g != NULL )
776        {
777            pushStateSet(g->getStateSet());
778            createListTriangle(g, listTriangles, texcoords, i);
779            popStateSet(g->getStateSet());
780        }
781    }
782    if (count > 0)
783    {
784        buildFaces(node, listTriangles, texcoords);
785    }
786    popStateSet(node.getStateSet());
787    //_nameStack.pop_back();
788    if (suceedLastApply())
789        traverse(node);
790}
791
792void WriterNodeVisitor::apply(osg::Group &node)
793{
794    Lib3dsMeshInstanceNode * parent = _cur3dsNode;
795    Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : getFileName(node.getName()), "grp").c_str(), NULL, NULL, NULL);
796    lib3ds_file_append_node(file3ds, reinterpret_cast<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(parent));
797    _cur3dsNode = node3ds;
798    if (suceedLastApply())
799        traverse(node);
800    _cur3dsNode = parent;
801}
802
803void WriterNodeVisitor::apply(osg::MatrixTransform &node)
804{
805    Lib3dsMeshInstanceNode * parent = _cur3dsNode;
806
807    const osg::Matrix & m = node.getMatrix();
808    //const osg::Matrix m( osg::computeWorldToLocal(getNodePath()) );        // [NEEDS TESTING!] 3DS matrices always contain world to local transformation (not local transform; ie. from parent)
809
810    // Transform data used to be given to lib3ds_node_new_mesh_instance(), but it seems buggy (pivot problem? bug in conversion?).
811    float pos[3];
812    float scl[3];
813    float rot[4];
814    osg::Vec3 osgScl, osgPos;
815    osg::Quat osgRot, osgSo;
816    m.decompose(osgPos, osgRot, osgScl, osgSo);
817    copyOsgVectorToLib3dsVector(pos, osgPos);
818    copyOsgVectorToLib3dsVector(scl, osgScl);
819    copyOsgQuatToLib3dsQuat(rot, osgRot);
820    Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance
821        (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), pos, scl, rot);
822
823    //// Create a mesh instance with no transform and then copy the matrix (doesn't work)
824    //Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance
825    //    (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), NULL, NULL, NULL);
826    //    copyOsgMatrixToLib3dsMatrix(node3ds->base.matrix, m);
827
828    lib3ds_file_append_node(file3ds, reinterpret_cast<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(parent));
829    _cur3dsNode = node3ds;
830    if (suceedLastApply())
831        traverse(node);
832    _cur3dsNode = parent;
833}
Note: See TracBrowser for help on using the browser.