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

Revision 11203, 37.6 kB (checked in by robert, 5 years ago)

From Sukender, "I had some conflicts when updating but they seem resolved now. Attached files (four files) are against rev. 11200. Changelog:
- Replaced exceptions with assert() or OSG_NOTIFY
- Replaced osg::notify() with OSG_NOTIFY
- Changed braces and tabs to fit OSG coding convention
- Cleaned a few things in code (names, added deallocations upon error)"

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