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

Revision 11123, 33.5 kB (checked in by robert, 5 years ago)

From Sukender, "I tried a tiny change in is83() function and had no crash (under Windows). "osgconv cow.osg cow.3ds" exports a black cow and "osgconv lz.osg lz.3ds" exports tree(s) at (0,0,0)... I guess there are still things to do about non-zero-index textures and multiple instanciation of a node, but at least it doesn't crash."

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