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

Revision 11194, 35.9 kB (checked in by robert, 4 years ago)

From Sukender, "I've fixed positions for 3DS writer. Points in 3DS must be in world coordinates and I added what was missing.
And by refactoring a bit of code, I may have fixed some StateSets? related bugs (was ignoring StateSets? for osg::Groups).
I also added support for Billboard's points, so now "osgconv lz.osg lz.3ds" has an acceptable output. However, there is no rotation depending on billboards' axis, hence the notice "Warning: 3DS writer is incomplete for Billboards (rotation not implemented).". You may want to remove this notice (or lower the notify severity) if you feel 3DS doesn't have to handle such rotations.
The attached archive contains 3 files from 3DS plugin, against rev. 11162.

Please note there is still the textures issue for cow.osg. I guess it's because it's not a "flat, dummy and standard" texture in slot 0... That is to say the only thing the writer can handle at the moment. I guess I won't address this soon.
"
and

"I've detected and fixed another bug in 3DS writer: support for automatic splitting of meshes having >65k faces/points was buggy (was deleting faces).
Here is my four 3DS modified files (in a ZIP), against rev. 11193, including previous fixes AND Stephan's fix about relative filenames."

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