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

Revision 10853, 31.7 kB (checked in by robert, 5 years ago)

From Sukender,
"Here is our freshly baked 3DS reader/writer (named 'v0.5' to differentiate from previous one). Changes are against trunk rev. 10819.
Short changelog (from rev 10819):
- Added 3DS writer
- Sync'd with latest lib3DS
- Added options, especially "flattenMatrixTransforms" to get the "old" behaviour (else the reader correctly maps to OSG the transforms from the 3DS file).

What should be done:
- Check with pivot points, with and without "flattenMatrixTransforms" option.
- We ran tests on it, but we can never be 100% sure there is no bug. Testing from the community would of course be helpful."

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