diff --git a/src/osgPlugins/3ds/ReaderWriter3DS.cpp b/src/osgPlugins/3ds/ReaderWriter3DS.cpp index 62081b4b8..7227d39f2 100644 --- a/src/osgPlugins/3ds/ReaderWriter3DS.cpp +++ b/src/osgPlugins/3ds/ReaderWriter3DS.cpp @@ -348,9 +348,6 @@ osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap, for (unsigned int i=0; infaces; ++i) { if (mesh->faces[i].material>=0) { - //std::stringstream materialStr; - //materialStr << mesh->faces[i].material; - //materialFaceMap[materialStr.str()].push_back(i); materialFaceMap[mesh->faces[i].material].push_back(i); } else @@ -453,7 +450,8 @@ osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,L } - if (node->childs != NULL || pivoted || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) { + //if (node->childs != NULL || pivoted || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) { + if (node->childs != NULL || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) { if (isOsgNodeMatrixIdentity || noMatrixTransforms) { group = new osg::Group; } else { @@ -888,24 +886,8 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap * osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_TEXTURE_IGNORE_ALPHA)<< std::endl; osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_RGB_TINT "<<((texture->flags)&LIB3DS_TEXTURE_RGB_TINT)<< std::endl; - bool noTexture = false; - if (options) - { - std::istringstream iss(options->getOptionString()); - std::string opt; - while (iss >> opt) - { - if (opt == "noTexture") - noTexture = true; - } - } - - osg::ref_ptr osg_image = NULL; - if(noTexture) - osg_image = new osg::Image(); - else - osg_image = osgDB::readRefImageFile(fileName.c_str(), options); //Absolute Path - if (!osg_image) + osg::ref_ptr osg_image = osgDB::readRefImageFile(fileName.c_str(), options); //Absolute Path + if (!osg_image.valid()) { osg::notify(osg::NOTICE) << "Warning: Cannot create texture "<name<< std::endl; return NULL; diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.cpp b/src/osgPlugins/3ds/WriterNodeVisitor.cpp index 0ce6ce592..f07341601 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.cpp +++ b/src/osgPlugins/3ds/WriterNodeVisitor.cpp @@ -1,843 +1,833 @@ -// -*-c++-*- - -/* - * 3DS reader/writer for Open Scene Graph - * - * Copyright (C) ??? - * - * Writing support added 2007 by Sukender (Benoit Neil), http://sukender.free.fr, - * strongly inspired by the OBJ writer object by Stephan Huber - * - * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for - * real-time rendering of large 3D photo-realistic models. - * The OSG homepage is http://www.openscenegraph.org/ - */ - -#include -#include -#include "WriterNodeVisitor.h" -#include -#include - - -void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix) -{ - for(int row=0; row<4; ++row) { - lib3ds_matrix[row][0] = osg_matrix.ptr()[row*4+0]; - lib3ds_matrix[row][1] = osg_matrix.ptr()[row*4+1]; - lib3ds_matrix[row][2] = osg_matrix.ptr()[row*4+2]; - lib3ds_matrix[row][3] = osg_matrix.ptr()[row*4+3]; - } -} - -inline void copyOsgVectorToLib3dsVector(Lib3dsVector lib3ds_vector, const osg::Vec3f& osg_vector) { - lib3ds_vector[0] = osg_vector[0]; - lib3ds_vector[1] = osg_vector[1]; - lib3ds_vector[2] = osg_vector[2]; -} -inline void copyOsgVectorToLib3dsVector(Lib3dsVector lib3ds_vector, const osg::Vec3d& osg_vector) { - lib3ds_vector[0] = osg_vector[0]; - lib3ds_vector[1] = osg_vector[1]; - lib3ds_vector[2] = osg_vector[2]; -} - -inline void copyOsgColorToLib3dsColor(Lib3dsVector lib3ds_vector, const osg::Vec4f& osg_vector) { - lib3ds_vector[0] = osg_vector[0]; - lib3ds_vector[1] = osg_vector[1]; - lib3ds_vector[2] = osg_vector[2]; -} -inline void copyOsgColorToLib3dsColor(Lib3dsVector lib3ds_vector, const osg::Vec4d& osg_vector) { - lib3ds_vector[0] = osg_vector[0]; - lib3ds_vector[1] = osg_vector[1]; - lib3ds_vector[2] = osg_vector[2]; -} - -inline void copyOsgQuatToLib3dsQuat(float lib3ds_vector[4], const osg::Quat& osg_quat) { - //lib3ds_vector[0] = osg_quat[3]; // Not sure - //lib3ds_vector[1] = osg_quat[0]; - //lib3ds_vector[2] = osg_quat[1]; - //lib3ds_vector[3] = osg_quat[2]; - // 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)! - osg::Quat::value_type angle, x, y, z; - osg_quat.getRotate(angle, x, y, z); - lib3ds_vector[0] = static_cast(x); - lib3ds_vector[1] = static_cast(y); - lib3ds_vector[2] = static_cast(z); - lib3ds_vector[3] = static_cast(-angle); -} - -std::string getFileName(const std::string & path) { - unsigned int slashPos = path.find_last_of("/\\"); - if (slashPos == std::string::npos) return path; - return path.substr(slashPos+1); -} - - -/// Checks if a filename (\b not path) is 8.3 (an empty name is never 8.3, and a path is never 8.3). -bool is83(const std::string & s) { - // 012345678901 - // ABCDEFGH.ABC - if (s.find_first_of("/\\") != std::string::npos) return false; // It should not be a path, but a filename - unsigned int len = s.length(); - if (len > 12 || len == 0) return false; - unsigned int pointPos = s.rfind('.'); - if (pointPos == std::string::npos) return len <= 8; // Without point - // With point - if (pointPos > 8) return false; - if (len-1 - pointPos > 3) return false; - return true; -} - -/// Tests if the given string is a path supported by 3DS format (8.3, 63 chars max). -bool is3DSpath(const std::string & s) { - unsigned int len = s.length(); - if (len >= 64 || len == 0) return false; - - unsigned int tokenBegin = 0; - for (unsigned int tokenEnd=0; tokenEnd != std::string::npos; tokenBegin = tokenEnd+1) { - tokenEnd = s.find_first_of("/\\", tokenBegin); - if ( !is83(s.substr(tokenBegin, tokenEnd-tokenBegin-1)) ) return false; - } - return true; -} - - - -/** writes all primitives of a primitive-set out to a stream, decomposes quads to triangles, line-strips to lines etc */ -class PrimitiveIndexWriter : public osg::PrimitiveIndexFunctor { -public: - PrimitiveIndexWriter(osg::Geometry * geo, - ListTriangle & listTriangles, - unsigned int drawable_n, - unsigned int material) : - osg::PrimitiveIndexFunctor(), - _drawable_n(drawable_n), - _listTriangles(listTriangles), - _hasNormalCoords(geo->getNormalArray() != NULL), - _hasTexCoords(geo->getTexCoordArray(0) != NULL), - _geo(geo), - _lastFaceIndex(0), - _material(material) - { - } - - unsigned int getNextFaceIndex() { return _lastFaceIndex; } - - virtual void setVertexArray(unsigned int,const osg::Vec2*) {} - - virtual void setVertexArray(unsigned int count,const osg::Vec3* vecs) {} - - virtual void setVertexArray(unsigned int,const osg::Vec4* ) {} - - virtual void setVertexArray(unsigned int,const osg::Vec2d*) {} - - virtual void setVertexArray(unsigned int ,const osg::Vec3d* ) {} - virtual void setVertexArray(unsigned int,const osg::Vec4d* ) {} - - - // operator for triangles - void writeTriangle(unsigned int i1, unsigned int i2, unsigned int i3) - { - Triangle triangle; - triangle.t1 = i1; - triangle.t2 = i2; - triangle.t3 = i3; - triangle.material = _material; - _listTriangles.push_back(std::make_pair(triangle, _drawable_n)); - } - virtual void begin(GLenum mode) - { - _modeCache = mode; - _indexCache.clear(); - } - - virtual void vertex(unsigned int vert) - { - _indexCache.push_back(vert); - } - - virtual void end() - { - if (!_indexCache.empty()) - { - drawElements(_modeCache,_indexCache.size(),&_indexCache.front()); - } - } - - virtual void drawArrays(GLenum mode,GLint first,GLsizei count); - - virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices) - { - drawElementsImplementation(mode, count, indices); - } - virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) - { - drawElementsImplementation(mode, count, indices); - } - - virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) - { - drawElementsImplementation(mode, count, indices); - } - -protected: - - templatevoid drawElementsImplementation(GLenum mode, GLsizei count, const T* indices) - { - if (indices==0 || count==0) return; - - typedef const T* IndexPointer; - - switch(mode) - { - case(GL_TRIANGLES): - { - //lib3ds_mesh_resize_faces(_mesh, _lastFaceIndex + count / 3); - IndexPointer ilast = &indices[count]; - for(IndexPointer iptr=indices;iptr _indexCache; - bool _hasNormalCoords, _hasTexCoords; - osg::Geometry* _geo; - unsigned int _lastFaceIndex; - unsigned int _material; -}; - - -void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count) -{ - switch(mode) - { - case(GL_TRIANGLES): - { - unsigned int pos=first; - for(GLsizei i=2;igetDiffuse(osg::Material::FRONT); - ambient = mat->getAmbient(osg::Material::FRONT); - specular = mat->getSpecular(osg::Material::FRONT); - shininess = mat->getShininess(osg::Material::FRONT); - transparency = 1-diffuse.w(); - name = writerNodeVisitor.getUniqueName(mat->getName(),"mat"); - osg::StateAttribute * attribute = stateset->getAttribute(osg::StateAttribute::CULLFACE); - if (!attribute) { - double_sided = true; - } else { - assert(dynamic_cast(attribute)); - osg::CullFace::Mode mode = static_cast(attribute)->getMode(); - if (mode == osg::CullFace::BACK) double_sided = false; - else if (mode == osg::CullFace::FRONT) { - osg::notify(osg::WARN) << "3DS Writer: Reversed face (culled FRONT) not supported yet." << std::endl; - double_sided = false; - } - else { - assert(mode == osg::CullFace::FRONT_AND_BACK); - osg::notify(osg::WARN) << "3DS Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl; - double_sided = false; - } - } - } - if (tex) { - osg::Image* img = tex->getImage(0); - if(img) - { - texture_transparency = (stateset->getMode(GL_BLEND) == osg::StateAttribute::ON); - texture_no_tile = (tex->getWrap(osg::Texture2D::WRAP_S) == osg::Texture2D::CLAMP); - image = img; - } - } - - if (name.empty()) { - std::stringstream ss; - ss << "m" << index; - name = ss.str(); - } -} - - -/// Creates a unique 3DS name for a given texture, and copies the image file to disk at an appropriate location if necessary. -//std::string WriterNodeVisitor::export3DSTexture(const osg::Image * image, const std::string & texName) { -// std::string extension = osgDB::getFileExtension(texName); -// if (is83(texName)) -// { -// std::string newName = texName; -// if (extension.empty()) -// newName += ".rgb"; -// if (osgDB::Registry::instance()->writeImage(*image, osgDB::concatPaths(_directory, newName), options).status() == -// osgDB::ReaderWriter::WriteResult::ERROR_IN_WRITING_FILE) -// failedApply(); -// return texName; -// } -// if (extension.empty()) -// { -// extension = "rgb"; -// } -// std::string newName = getUniqueName(texName, "tex"); -// newName += "." + extension; -// osgDB::Registry::instance()->writeImage(*image, osgDB::concatPaths(_directory, newName), options); -// return newName; -//} - -std::string -getPathRelative(const std::string & srcBad, - const std::string & dstBad) -{ - const std::string & src = osgDB::convertFileNameToNativeStyle(srcBad); - const std::string & dst = osgDB::convertFileNameToNativeStyle(dstBad); - std::string::const_iterator itDst = dst.begin(); - std::string::const_iterator itSrc = src.begin(); - - std::string result = ""; - - while(itDst != dst.end()) - { - if (itSrc != src.end() && *itDst == *itSrc) - ++itSrc; - else if (!result.empty() || *itDst != '\\') - result += *itDst; - ++itDst; - } - if (itSrc != src.end() || !is3DSpath(result)) - result = osgDB::getSimpleFileName(dst); - return result; -} - -void WriterNodeVisitor::writeMaterials() -{ - unsigned int nbMat = _materialMap.size(); - lib3ds_file_reserve_materials(file3ds, nbMat, 1); - // Ugly thing: it seems lib3ds_file_insert_material() doesn't support insertion in a random order (else materials are not assigned the right way) - for (unsigned int iMat=0; iMatsecond; - if (mat.index != static_cast(iMat)) continue; // Ugly thing (2) - found = true; - - assert(mat.index>=0 && mat.index < static_cast(_materialMap.size())); - Lib3dsMaterial * mat3ds = lib3ds_material_new(getFileName(mat.name).c_str()); - copyOsgColorToLib3dsColor(mat3ds->ambient, mat.ambient); - copyOsgColorToLib3dsColor(mat3ds->diffuse, mat.diffuse); - copyOsgColorToLib3dsColor(mat3ds->specular, mat.specular); - mat3ds->shininess = mat.shininess; - mat3ds->transparency = mat.transparency; - mat3ds->two_sided = mat.double_sided ? 1 : 0; - if (mat.image) - { - Lib3dsTextureMap & tex = mat3ds->texture1_map; - std::string path; - if(mat.image->getFileName().empty()) - { - std::ostringstream oss; - oss << "Image_" << _imageCount++ << ".rgb"; - path = oss.str(); - } - else - path = getPathRelative(_srcDirectory, mat.image->getFileName()); - // if(!is3DSpath(path)) - path = osgDB::getSimpleFileName(path); - - strcpy(tex.name, path.c_str()); - path = osgDB::concatPaths(_directory, path); - osgDB::makeDirectoryForFile(path); - - if (mat.image && mat.image->data()) osgDB::Registry::instance()->writeImage(*(mat.image), path, NULL); - if (mat.texture_transparency) tex.flags |= LIB3DS_TEXTURE_ALPHA_SOURCE; - if (mat.texture_no_tile) tex.flags |= LIB3DS_TEXTURE_NO_TILE; - } - if (!suceedLastApply()) - return; - lib3ds_file_insert_material(file3ds, mat3ds, itr->second.index); - break; // Ugly thing (3) - } - if (!found) throw "Implementation error"; // Ugly thing (4) - } -} - - -std::string WriterNodeVisitor::getUniqueName(const std::string& defaultValue, const std::string & _defaultPrefix, bool nameIsPath) { - if (_defaultPrefix.length()>=4) throw "Default prefix is too long"; // Arbitrarily defined to 3 chars. You can modify this, but you may have to change the code so that finding a number is okay, even when changing the default prefix length. - - // Tests if default name is valid and unique - bool defaultIs83 = is83(defaultValue); - bool defaultIsValid = nameIsPath ? is3DSpath(defaultValue) : defaultIs83; - if (defaultIsValid && _nameMap.find(defaultValue) == _nameMap.end()) { - _nameMap.insert(defaultValue); - return defaultValue; - } - - std::string defaultPrefix(_defaultPrefix.empty() ? "_" : _defaultPrefix); - - unsigned int max_val = 0; - std::string truncDefaultValue = ""; - for (unsigned int i = 0; i < std::min(defaultValue.size(), 4); ++i) - { - if (defaultValue[i] == '.') - { - truncDefaultValue = defaultValue.substr(0, i); - break; - } - } - if (truncDefaultValue.empty()) - truncDefaultValue = defaultValue.substr(0, std::min(defaultValue.size(), 4)); - std::map::iterator pairPrefix; - defaultIs83 = is83(truncDefaultValue); - if (defaultIs83) - { - max_val = static_cast(pow(10., 8. - truncDefaultValue.length() - 1)) -1; // defaultPrefix.length()-1 because we add an underscore ("_") - pairPrefix = _mapPrefix.find(truncDefaultValue); - } - - if (defaultIs83 && (_mapPrefix.end() == pairPrefix || pairPrefix->second <= max_val)) - { - defaultPrefix = truncDefaultValue; - } - else - { - max_val = static_cast(pow(10., 8. - defaultPrefix.length() - 1)) - 1; // defaultPrefix.length()-1 because we add an underscore ("_") - pairPrefix = _mapPrefix.find(defaultPrefix); - } - - unsigned int searchStart = 0; - if (pairPrefix != _mapPrefix.end()) - searchStart = pairPrefix->second; - - for(unsigned int i = searchStart; i <= max_val; ++i) { - std::stringstream ss; - ss << defaultPrefix << "_" << i; - const std::string & res = ss.str(); - if (_nameMap.find(res) == _nameMap.end()) { - if (pairPrefix != _mapPrefix.end()) - { - pairPrefix->second = i + 1; - } - else - { - _mapPrefix.insert(std::make_pair(defaultPrefix, i + 1)); - } - _nameMap.insert(res); - return res; - } - } - if (defaultPrefix == "_") _lastGeneratedNumberedName = max_val; - throw "No more names available! Is default prefix too long?"; -} - -int WriterNodeVisitor::processStateSet(osg::StateSet* ss) -{ - MaterialMap::const_iterator itr = _materialMap.find(ss); - if (itr != _materialMap.end()) { - assert(itr->second.index>=0); - return itr->second.index; - } - - osg::Material* mat = dynamic_cast(ss->getAttribute(osg::StateAttribute::MATERIAL)); - osg::Texture* tex = dynamic_cast(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE)); - - if (mat || tex) - { - int matNum = _lastMaterialIndex; - _materialMap.insert(std::make_pair(osg::ref_ptr(ss), Material(*this, ss, mat, tex, matNum) )); - ++_lastMaterialIndex; - return matNum; - } - return -1; -} - -/** -* Add a vertice to the index and link him with the Triangle index and the drawable. -* \param index_vert is the map where the vertice are stored. -* \param index is the indice of the vertice's position in the vec3. -* \param drawable_n is the number of the drawable. -* \return the position of the vertice in the final mesh. -*/ -unsigned int -WriterNodeVisitor::getMeshIndexForGeometryIndex(MapIndices & index_vert, - unsigned int index, - unsigned int drawable_n) -{ - MapIndices::iterator itIndex = index_vert.find(std::make_pair(index, drawable_n)); - if (itIndex == index_vert.end()) { - unsigned int indexMesh = index_vert.size(); - index_vert.insert(std::make_pair(std::make_pair(index, drawable_n), indexMesh)); - return indexMesh; - } - return itIndex->second; -} - - -void -WriterNodeVisitor::buildMesh(osg::Geode & geo, - MapIndices & index_vert, - bool texcoords, - Lib3dsMesh * mesh) -{ - osg::notify(osg::DEBUG_INFO) << "Building Mesh" << std::endl; - - if (!mesh) throw "Allocation error"; // TODO - - lib3ds_mesh_resize_vertices(mesh, index_vert.size(), texcoords ? 1 : 0, 0); - // Write points - - for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end();++it) - { - osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry(); - assert(g->getVertexArray()); - if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType) - throw "Vertex array is not Vec3. Not implemented"; // TODO - const osg::Vec3Array & vecs= *static_cast(g->getVertexArray()); - copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]); - } - - // Write texture coords (Texture 0 only) - if (texcoords) - { - for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end(); ++it) - { - osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry(); - osg::Array * array = g->getTexCoordArray(0); - if(array) - { - if (g->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType) - throw "Texture coords array is not Vec2. Not implemented"; // TODO - const osg::Vec2Array & vecs= *static_cast(array); - mesh->texcos[it->second][0] = vecs[it->first.first][0]; - mesh->texcos[it->second][1] = vecs[it->first.first][1]; - } - } - } - lib3ds_file_insert_mesh(file3ds, mesh, _lastMeshIndex); - ++_lastMeshIndex; - - Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(mesh, mesh->name, NULL, NULL, NULL); - lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(_cur3dsNode)); -} - -unsigned int -WriterNodeVisitor::calcVertices(osg::Geode & geo) -{ - unsigned int numVertice = 0; - for (unsigned int i = 0; i < geo.getNumDrawables(); ++i) - { - osg::Geometry *g = geo.getDrawable( i )->asGeometry(); - assert(g->getVertexArray()); - if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType) - throw "Vertex array is not Vec3. Not implemented"; // TODO - const osg::Vec3Array & vecs= *static_cast(g->getVertexArray()); - numVertice += vecs.getNumElements(); - } - return numVertice; -} - - -void -WriterNodeVisitor::buildFaces(osg::Geode & geo, - ListTriangle & listTriangles, - bool texcoords) -{ - MapIndices index_vert; - unsigned int nbFace = 0; - Lib3dsMesh *mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str() ); - unsigned int nbTriangles = listTriangles.size(); - - lib3ds_mesh_resize_faces(mesh, nbTriangles); - - unsigned int nbVertices = calcVertices(geo); - if (listTriangles.size() >= MAX_FACES-2 || - ((nbVertices) >= MAX_VERTICES-2)) - { - osg::notify(osg::ALWAYS) << "Sorting elements..." << std::endl; - WriterCompareTriangle cmp(geo, nbVertices); - std::sort(listTriangles.begin(), listTriangles.end(), cmp); - } - - for (ListTriangle::iterator it = listTriangles.begin(); it != listTriangles.end(); ++it) //Go through the triangle list to define meshs - { - // Using -2 due to the fact that we treat 3 faces in one time (=the algorithm may overrun the limit by 2). - if ((index_vert.size() >= MAX_VERTICES-2 || // If mesh is full - nbFace >= MAX_FACES-2)) - { - // Finnishing mesh - lib3ds_mesh_resize_faces(mesh, nbFace); - buildMesh(geo, index_vert, texcoords, mesh); - - // Creating a new mesh - index_vert.clear(); - mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str()); - nbTriangles -= nbFace; - nbFace = 0; - lib3ds_mesh_resize_faces(mesh, nbTriangles); - } - Lib3dsFace & face = mesh->faces[nbFace++]; - face.index[0] = getMeshIndexForGeometryIndex(index_vert, it->first.t1, it->second); - face.index[1] = getMeshIndexForGeometryIndex(index_vert, it->first.t2, it->second); - face.index[2] = getMeshIndexForGeometryIndex(index_vert, it->first.t3, it->second); - face.material = it->first.material; - } - buildMesh(geo, index_vert, texcoords, mesh); //When a Mesh is completed without restriction of vertices number -} - -void -WriterNodeVisitor::createListTriangle(osg::Geometry * geo, - ListTriangle & listTriangles, - bool & texcoords, - unsigned int & drawable_n) -{ - unsigned int nbVertices = 0; - { - if (geo->getVertexArray() && geo->getVertexArray()->getType() != osg::Array::Vec3ArrayType) - throw "Vertex array is not Vec3. Not implemented"; // TODO - const osg::Vec3Array * vecs = geo->getVertexArray() ? static_cast(geo->getVertexArray()) : NULL; - if (vecs) - { - nbVertices = geo->getVertexArray()->getNumElements(); - // Texture coords - if (geo->getTexCoordArray(0) && geo->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType) - throw "Texture coords array is not Vec2. Not implemented"; // TODO - const osg::Vec2Array * texvecs = geo->getTexCoordArray(0) ? static_cast(geo->getTexCoordArray(0)) : NULL; - if (texvecs) - { - unsigned int nb = geo->getTexCoordArray(0)->getNumElements(); - if (nb != geo->getVertexArray()->getNumElements()) throw "There are more/less texture coords than vertices!"; - texcoords = true; - } - } - } - - if (nbVertices==0) return; - - int material = processStateSet(_currentStateSet.get()); - - for(unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i) //Fill the Triangle List - { - osg::PrimitiveSet* ps = geo->getPrimitiveSet(i); - PrimitiveIndexWriter pif(geo, listTriangles, drawable_n, material); - ps->accept(pif); - } -} - -bool WriterNodeVisitor::suceedLastApply() const -{ - return _suceedLastApply; -} - -void WriterNodeVisitor::failedApply() -{ - _suceedLastApply = false; - osg::notify(osg::NOTICE) << "Error going through node" << std::endl; -} - -void WriterNodeVisitor::apply( osg::Geode &node ) -{ - pushStateSet(node.getStateSet()); - //_nameStack.push_back(node.getName()); - //osg::Matrix m = osg::computeLocalToWorld(getNodePath()); - unsigned int count = node.getNumDrawables(); - ListTriangle listTriangles; - bool texcoords = false; - for ( unsigned int i = 0; i < count; i++ ) - { - osg::Geometry *g = node.getDrawable( i )->asGeometry(); - if ( g != NULL ) - { - pushStateSet(g->getStateSet()); - createListTriangle(g, listTriangles, texcoords, i); - popStateSet(g->getStateSet()); - } - } - if (count > 0) - { - buildFaces(node, listTriangles, texcoords); - } - popStateSet(node.getStateSet()); - //_nameStack.pop_back(); - if (suceedLastApply()) - traverse(node); -} - -void WriterNodeVisitor::apply(osg::Group &node) -{ - Lib3dsMeshInstanceNode * parent = _cur3dsNode; - Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : getFileName(node.getName()), "grp").c_str(), NULL, NULL, NULL); - lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); - _cur3dsNode = node3ds; - if (suceedLastApply()) - traverse(node); - _cur3dsNode = parent; -} - -void WriterNodeVisitor::apply(osg::MatrixTransform &node) -{ - Lib3dsMeshInstanceNode * parent = _cur3dsNode; - - const osg::Matrix & m = node.getMatrix(); - //const osg::Matrix m( osg::computeWorldToLocal(getNodePath()) ); // [NEEDS TESTING!] 3DS matrices always contain world to local transformation (not local transform; ie. from parent) - - // Transform data used to be given to lib3ds_node_new_mesh_instance(), but it seems buggy (pivot problem? bug in conversion?). - float pos[3]; - float scl[3]; - float rot[4]; - osg::Vec3 osgScl, osgPos; - osg::Quat osgRot, osgSo; - m.decompose(osgPos, osgRot, osgScl, osgSo); - copyOsgVectorToLib3dsVector(pos, osgPos); - copyOsgVectorToLib3dsVector(scl, osgScl); - copyOsgQuatToLib3dsQuat(rot, osgRot); - Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance - (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), pos, scl, rot); - - //// Create a mesh instance with no transform and then copy the matrix (doesn't work) - //Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance - // (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), NULL, NULL, NULL); - // copyOsgMatrixToLib3dsMatrix(node3ds->base.matrix, m); - - lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); - _cur3dsNode = node3ds; - if (suceedLastApply()) - traverse(node); - _cur3dsNode = parent; -} +// -*-c++-*- + +/* + * 3DS reader/writer for Open Scene Graph + * + * Copyright (C) ??? + * + * Writing support added 2007 by Sukender (Benoit Neil), http://sukender.free.fr, + * strongly inspired by the OBJ writer object by Stephan Huber + * + * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for + * real-time rendering of large 3D photo-realistic models. + * The OSG homepage is http://www.openscenegraph.org/ + */ + +#include +#include +#include + +#include "WriterNodeVisitor.h" +#include +#include + + +void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix) +{ + for(int row=0; row<4; ++row) { + lib3ds_matrix[row][0] = osg_matrix.ptr()[row*4+0]; + lib3ds_matrix[row][1] = osg_matrix.ptr()[row*4+1]; + lib3ds_matrix[row][2] = osg_matrix.ptr()[row*4+2]; + lib3ds_matrix[row][3] = osg_matrix.ptr()[row*4+3]; + } +} + +inline void copyOsgVectorToLib3dsVector(Lib3dsVector lib3ds_vector, const osg::Vec3f& osg_vector) { + lib3ds_vector[0] = osg_vector[0]; + lib3ds_vector[1] = osg_vector[1]; + lib3ds_vector[2] = osg_vector[2]; +} +inline void copyOsgVectorToLib3dsVector(Lib3dsVector lib3ds_vector, const osg::Vec3d& osg_vector) { + lib3ds_vector[0] = osg_vector[0]; + lib3ds_vector[1] = osg_vector[1]; + lib3ds_vector[2] = osg_vector[2]; +} + +inline void copyOsgColorToLib3dsColor(Lib3dsVector lib3ds_vector, const osg::Vec4f& osg_vector) { + lib3ds_vector[0] = osg_vector[0]; + lib3ds_vector[1] = osg_vector[1]; + lib3ds_vector[2] = osg_vector[2]; +} +inline void copyOsgColorToLib3dsColor(Lib3dsVector lib3ds_vector, const osg::Vec4d& osg_vector) { + lib3ds_vector[0] = osg_vector[0]; + lib3ds_vector[1] = osg_vector[1]; + lib3ds_vector[2] = osg_vector[2]; +} + +inline void copyOsgQuatToLib3dsQuat(float lib3ds_vector[4], const osg::Quat& osg_quat) { + //lib3ds_vector[0] = osg_quat[3]; // Not sure + //lib3ds_vector[1] = osg_quat[0]; + //lib3ds_vector[2] = osg_quat[1]; + //lib3ds_vector[3] = osg_quat[2]; + // 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)! + osg::Quat::value_type angle, x, y, z; + osg_quat.getRotate(angle, x, y, z); + lib3ds_vector[0] = static_cast(x); + lib3ds_vector[1] = static_cast(y); + lib3ds_vector[2] = static_cast(z); + lib3ds_vector[3] = static_cast(-angle); +} + +std::string getFileName(const std::string & path) { + unsigned int slashPos = path.find_last_of("/\\"); + if (slashPos == std::string::npos) return path; + return path.substr(slashPos+1); +} + + +/// Checks if a filename (\b not path) is 8.3 (an empty name is never 8.3, and a path is never 8.3). +bool is83(const std::string & s) { + // 012345678901 + // ABCDEFGH.ABC + if (s.find_first_of("/\\") != std::string::npos) return false; // It should not be a path, but a filename + unsigned int len = s.length(); + if (len > 12 || len == 0) return false; + unsigned int pointPos = s.rfind('.'); + if (pointPos == std::string::npos) return len <= 8; // Without point + // With point + if (pointPos > 8) return false; + if (len-1 - pointPos > 3) return false; + return true; +} + +/// Tests if the given string is a path supported by 3DS format (8.3, 63 chars max). +bool is3DSpath(const std::string & s) { + unsigned int len = s.length(); + if (len >= 64 || len == 0) return false; + + unsigned int tokenBegin = 0; + for (unsigned int tokenEnd=0; tokenEnd != std::string::npos; tokenBegin = tokenEnd+1) { + tokenEnd = s.find_first_of("/\\", tokenBegin); + if ( !is83(s.substr(tokenBegin, tokenEnd-tokenBegin-1)) ) return false; + } + return true; +} + + + +/** writes all primitives of a primitive-set out to a stream, decomposes quads to triangles, line-strips to lines etc */ +class PrimitiveIndexWriter : public osg::PrimitiveIndexFunctor { +public: + PrimitiveIndexWriter(osg::Geometry * geo, + ListTriangle & listTriangles, + unsigned int drawable_n, + unsigned int material) : + osg::PrimitiveIndexFunctor(), + _drawable_n(drawable_n), + _listTriangles(listTriangles), + _hasNormalCoords(geo->getNormalArray() != NULL), + _hasTexCoords(geo->getTexCoordArray(0) != NULL), + _geo(geo), + _lastFaceIndex(0), + _material(material) + { + } + + unsigned int getNextFaceIndex() { return _lastFaceIndex; } + + virtual void setVertexArray(unsigned int,const osg::Vec2*) {} + + virtual void setVertexArray(unsigned int count,const osg::Vec3* vecs) {} + + virtual void setVertexArray(unsigned int,const osg::Vec4* ) {} + + virtual void setVertexArray(unsigned int,const osg::Vec2d*) {} + + virtual void setVertexArray(unsigned int ,const osg::Vec3d* ) {} + virtual void setVertexArray(unsigned int,const osg::Vec4d* ) {} + + + // operator for triangles + void writeTriangle(unsigned int i1, unsigned int i2, unsigned int i3) + { + Triangle triangle; + triangle.t1 = i1; + triangle.t2 = i2; + triangle.t3 = i3; + triangle.material = _material; + _listTriangles.push_back(std::make_pair(triangle, _drawable_n)); + } + virtual void begin(GLenum mode) + { + _modeCache = mode; + _indexCache.clear(); + } + + virtual void vertex(unsigned int vert) + { + _indexCache.push_back(vert); + } + + virtual void end() + { + if (!_indexCache.empty()) + { + drawElements(_modeCache,_indexCache.size(),&_indexCache.front()); + } + } + + virtual void drawArrays(GLenum mode,GLint first,GLsizei count); + + virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices) + { + drawElementsImplementation(mode, count, indices); + } + virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) + { + drawElementsImplementation(mode, count, indices); + } + + virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) + { + drawElementsImplementation(mode, count, indices); + } + +protected: + + templatevoid drawElementsImplementation(GLenum mode, GLsizei count, const T* indices) + { + if (indices==0 || count==0) return; + + typedef const T* IndexPointer; + + switch(mode) + { + case(GL_TRIANGLES): + { + //lib3ds_mesh_resize_faces(_mesh, _lastFaceIndex + count / 3); + IndexPointer ilast = &indices[count]; + for(IndexPointer iptr=indices;iptr _indexCache; + bool _hasNormalCoords, _hasTexCoords; + osg::Geometry* _geo; + unsigned int _lastFaceIndex; + unsigned int _material; +}; + + +void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count) +{ + switch(mode) + { + case(GL_TRIANGLES): + { + unsigned int pos=first; + for(GLsizei i=2;igetDiffuse(osg::Material::FRONT); + ambient = mat->getAmbient(osg::Material::FRONT); + specular = mat->getSpecular(osg::Material::FRONT); + shininess = mat->getShininess(osg::Material::FRONT); + transparency = 1-diffuse.w(); + name = writerNodeVisitor.getUniqueName(mat->getName(),"mat"); + osg::StateAttribute * attribute = stateset->getAttribute(osg::StateAttribute::CULLFACE); + if (!attribute) { + double_sided = true; + } else { + assert(dynamic_cast(attribute)); + osg::CullFace::Mode mode = static_cast(attribute)->getMode(); + if (mode == osg::CullFace::BACK) double_sided = false; + else if (mode == osg::CullFace::FRONT) { + osg::notify(osg::WARN) << "3DS Writer: Reversed face (culled FRONT) not supported yet." << std::endl; + double_sided = false; + } + else { + assert(mode == osg::CullFace::FRONT_AND_BACK); + osg::notify(osg::WARN) << "3DS Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl; + double_sided = false; + } + } + } + if (tex) { + osg::Image* img = tex->getImage(0); + if(img) + { + texture_transparency = (stateset->getMode(GL_BLEND) == osg::StateAttribute::ON); + texture_no_tile = (tex->getWrap(osg::Texture2D::WRAP_S) == osg::Texture2D::CLAMP); + image = img; + } + } + + if (name.empty()) { + std::stringstream ss; + ss << "m" << index; + name = ss.str(); + } +} + + +std::string +getPathRelative(const std::string & srcBad, + const std::string & dstBad) +{ + if(srcBad.empty()) + return osgDB::getSimpleFileName(dstBad); + const std::string & src = osgDB::convertFileNameToNativeStyle(srcBad); + const std::string & dst = osgDB::convertFileNameToNativeStyle(dstBad); + std::string::const_iterator itDst = dst.begin(); + std::string::const_iterator itSrc = src.begin(); + + std::string result = ""; + + while(itDst != dst.end()) + { + if (itSrc != src.end() && *itDst == *itSrc) + ++itSrc; + else if (!result.empty() || *itDst != '\\') + result += *itDst; + ++itDst; + } + if (itSrc != src.end()) + result = osgDB::getSimpleFileName(dst); + return result; +} + +void WriterNodeVisitor::writeMaterials() +{ + unsigned int nbMat = _materialMap.size(); + lib3ds_file_reserve_materials(file3ds, nbMat, 1); + // Ugly thing: it seems lib3ds_file_insert_material() doesn't support insertion in a random order (else materials are not assigned the right way) + for (unsigned int iMat=0; iMatsecond; + if (mat.index != static_cast(iMat)) continue; // Ugly thing (2) + found = true; + + assert(mat.index>=0 && mat.index < static_cast(_materialMap.size())); + Lib3dsMaterial * mat3ds = lib3ds_material_new(getFileName(mat.name).c_str()); + copyOsgColorToLib3dsColor(mat3ds->ambient, mat.ambient); + copyOsgColorToLib3dsColor(mat3ds->diffuse, mat.diffuse); + copyOsgColorToLib3dsColor(mat3ds->specular, mat.specular); + mat3ds->shininess = mat.shininess; + mat3ds->transparency = mat.transparency; + mat3ds->two_sided = mat.double_sided ? 1 : 0; + if (mat.image) + { + Lib3dsTextureMap & tex = mat3ds->texture1_map; + std::string path; + if(mat.image->getFileName().empty()) + { + std::ostringstream oss; + oss << "Image_" << _imageCount++ << ".rgb"; + path = oss.str(); + } + else { + path = getPathRelative(_srcDirectory, mat.image->getFileName()); + } + if(!is3DSpath(path)) { + path = getUniqueName(path, "", true); + //path = osgDB::getSimpleFileName(path); + } + + strcpy(tex.name, path.c_str()); + path = osgDB::concatPaths(_directory, path); + osgDB::makeDirectoryForFile(path); + + //if (mat.image->valid()) osgDB::writeImageFile(*(mat.image), path); + osgDB::writeImageFile(*(mat.image), path); + if (mat.texture_transparency) tex.flags |= LIB3DS_TEXTURE_ALPHA_SOURCE; + if (mat.texture_no_tile) tex.flags |= LIB3DS_TEXTURE_NO_TILE; + } + if (!suceedLastApply()) + return; + lib3ds_file_insert_material(file3ds, mat3ds, itr->second.index); + break; // Ugly thing (3) + } + if (!found) throw "Implementation error"; // Ugly thing (4) + } +} + + +std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, const std::string & _defaultPrefix, bool nameIsPath) { + if (_defaultPrefix.length()>=4) throw "Default prefix is too long"; // Arbitrarily defined to 3 chars. You can modify this, but you may have to change the code so that finding a number is okay, even when changing the default prefix length. + + // Tests if default name is valid and unique + bool defaultIs83 = is83(_defaultValue); + bool defaultIsValid = nameIsPath ? is3DSpath(_defaultValue) : defaultIs83; + if (defaultIsValid && _nameMap.find(_defaultValue) == _nameMap.end()) { + _nameMap.insert(_defaultValue); + return _defaultValue; + } + + // Handling of paths is not well done yet. Defaulting to something very simple. + // We should actually ensure each component is 8 chars long, and final filename is 8.3, and total is <64 chars. + std::string defaultValue(nameIsPath ? osgDB::getSimpleFileName(_defaultValue) : _defaultValue); + std::string ext(nameIsPath ? osgDB::getFileExtensionIncludingDot(_defaultValue).substr(0, std::min(_defaultValue.size(), 4)) : ""); // 4 chars = dot + 3 chars + + std::string defaultPrefix(_defaultPrefix.empty() ? "_" : _defaultPrefix); + + unsigned int max_val = 0; + std::string truncDefaultValue = ""; + for (unsigned int i = 0; i < std::min(defaultValue.size(), 4); ++i) + { + if (defaultValue[i] == '.') + { + truncDefaultValue = defaultValue.substr(0, i); + break; + } + } + if (truncDefaultValue.empty()) + truncDefaultValue = defaultValue.substr(0, std::min(defaultValue.size(), 4)); + std::map::iterator pairPrefix; + defaultIs83 = is83(truncDefaultValue); + if (defaultIs83) + { + max_val = static_cast(pow(10., 8. - truncDefaultValue.length() - 1)) -1; // defaultPrefix.length()-1 because we add an underscore ("_") + pairPrefix = _mapPrefix.find(truncDefaultValue); + } + + if (defaultIs83 && (_mapPrefix.end() == pairPrefix || pairPrefix->second <= max_val)) + { + defaultPrefix = truncDefaultValue; + } + else + { + max_val = static_cast(pow(10., 8. - defaultPrefix.length() - 1)) - 1; // defaultPrefix.length()-1 because we add an underscore ("_") + pairPrefix = _mapPrefix.find(defaultPrefix); + } + + unsigned int searchStart = 0; + if (pairPrefix != _mapPrefix.end()) + searchStart = pairPrefix->second; + + for(unsigned int i = searchStart; i <= max_val; ++i) { + std::stringstream ss; + ss << defaultPrefix << "_" << i; + const std::string & res = ss.str(); + if (_nameMap.find(res) == _nameMap.end()) { + if (pairPrefix != _mapPrefix.end()) + { + pairPrefix->second = i + 1; + } + else + { + _mapPrefix.insert(std::make_pair(defaultPrefix, i + 1)); + } + _nameMap.insert(res); + return res + ext; + } + } + if (defaultPrefix == "_") _lastGeneratedNumberedName = max_val; + throw "No more names available! Is default prefix too long?"; +} + +int WriterNodeVisitor::processStateSet(osg::StateSet* ss) +{ + MaterialMap::const_iterator itr = _materialMap.find(ss); + if (itr != _materialMap.end()) { + assert(itr->second.index>=0); + return itr->second.index; + } + + osg::Material* mat = dynamic_cast(ss->getAttribute(osg::StateAttribute::MATERIAL)); + osg::Texture* tex = dynamic_cast(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE)); + + if (mat || tex) + { + int matNum = _lastMaterialIndex; + _materialMap.insert(std::make_pair(osg::ref_ptr(ss), Material(*this, ss, mat, tex, matNum) )); + ++_lastMaterialIndex; + return matNum; + } + return -1; +} + +/** +* Add a vertice to the index and link him with the Triangle index and the drawable. +* \param index_vert is the map where the vertice are stored. +* \param index is the indice of the vertice's position in the vec3. +* \param drawable_n is the number of the drawable. +* \return the position of the vertice in the final mesh. +*/ +unsigned int +WriterNodeVisitor::getMeshIndexForGeometryIndex(MapIndices & index_vert, + unsigned int index, + unsigned int drawable_n) +{ + MapIndices::iterator itIndex = index_vert.find(std::make_pair(index, drawable_n)); + if (itIndex == index_vert.end()) { + unsigned int indexMesh = index_vert.size(); + index_vert.insert(std::make_pair(std::make_pair(index, drawable_n), indexMesh)); + return indexMesh; + } + return itIndex->second; +} + + +void +WriterNodeVisitor::buildMesh(osg::Geode & geo, + MapIndices & index_vert, + bool texcoords, + Lib3dsMesh * mesh) +{ + osg::notify(osg::DEBUG_INFO) << "Building Mesh" << std::endl; + + if (!mesh) throw "Allocation error"; // TODO + + lib3ds_mesh_resize_vertices(mesh, index_vert.size(), texcoords ? 1 : 0, 0); + // Write points + + for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end();++it) + { + osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry(); + assert(g->getVertexArray()); + if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType) + throw "Vertex array is not Vec3. Not implemented"; // TODO + const osg::Vec3Array & vecs= *static_cast(g->getVertexArray()); + copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]); + } + + // Write texture coords (Texture 0 only) + if (texcoords) + { + for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end(); ++it) + { + osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry(); + osg::Array * array = g->getTexCoordArray(0); + if(array) + { + if (g->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType) + throw "Texture coords array is not Vec2. Not implemented"; // TODO + const osg::Vec2Array & vecs= *static_cast(array); + mesh->texcos[it->second][0] = vecs[it->first.first][0]; + mesh->texcos[it->second][1] = vecs[it->first.first][1]; + } + } + } + lib3ds_file_insert_mesh(file3ds, mesh, _lastMeshIndex); + ++_lastMeshIndex; + + Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(mesh, mesh->name, NULL, NULL, NULL); + lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(_cur3dsNode)); +} + +unsigned int +WriterNodeVisitor::calcVertices(osg::Geode & geo) +{ + unsigned int numVertice = 0; + for (unsigned int i = 0; i < geo.getNumDrawables(); ++i) + { + osg::Geometry *g = geo.getDrawable( i )->asGeometry(); + assert(g->getVertexArray()); + if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType) + throw "Vertex array is not Vec3. Not implemented"; // TODO + const osg::Vec3Array & vecs= *static_cast(g->getVertexArray()); + numVertice += vecs.getNumElements(); + } + return numVertice; +} + + +void +WriterNodeVisitor::buildFaces(osg::Geode & geo, + ListTriangle & listTriangles, + bool texcoords) +{ + MapIndices index_vert; + unsigned int nbFace = 0; + Lib3dsMesh *mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str() ); + unsigned int nbTriangles = listTriangles.size(); + + lib3ds_mesh_resize_faces(mesh, nbTriangles); + + unsigned int nbVertices = calcVertices(geo); + if (listTriangles.size() >= MAX_FACES-2 || + ((nbVertices) >= MAX_VERTICES-2)) + { + osg::notify(osg::ALWAYS) << "Sorting elements..." << std::endl; + WriterCompareTriangle cmp(geo, nbVertices); + std::sort(listTriangles.begin(), listTriangles.end(), cmp); + } + + for (ListTriangle::iterator it = listTriangles.begin(); it != listTriangles.end(); ++it) //Go through the triangle list to define meshs + { + // Using -2 due to the fact that we treat 3 faces in one time (=the algorithm may overrun the limit by 2). + if ((index_vert.size() >= MAX_VERTICES-2 || // If mesh is full + nbFace >= MAX_FACES-2)) + { + // Finnishing mesh + lib3ds_mesh_resize_faces(mesh, nbFace); + buildMesh(geo, index_vert, texcoords, mesh); + + // Creating a new mesh + index_vert.clear(); + mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str()); + nbTriangles -= nbFace; + nbFace = 0; + lib3ds_mesh_resize_faces(mesh, nbTriangles); + } + Lib3dsFace & face = mesh->faces[nbFace++]; + face.index[0] = getMeshIndexForGeometryIndex(index_vert, it->first.t1, it->second); + face.index[1] = getMeshIndexForGeometryIndex(index_vert, it->first.t2, it->second); + face.index[2] = getMeshIndexForGeometryIndex(index_vert, it->first.t3, it->second); + face.material = it->first.material; + } + buildMesh(geo, index_vert, texcoords, mesh); //When a Mesh is completed without restriction of vertices number +} + +void +WriterNodeVisitor::createListTriangle(osg::Geometry * geo, + ListTriangle & listTriangles, + bool & texcoords, + unsigned int & drawable_n) +{ + unsigned int nbVertices = 0; + { + if (geo->getVertexArray() && geo->getVertexArray()->getType() != osg::Array::Vec3ArrayType) + throw "Vertex array is not Vec3. Not implemented"; // TODO + const osg::Vec3Array * vecs = geo->getVertexArray() ? static_cast(geo->getVertexArray()) : NULL; + if (vecs) + { + nbVertices = geo->getVertexArray()->getNumElements(); + // Texture coords + if (geo->getTexCoordArray(0) && geo->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType) + throw "Texture coords array is not Vec2. Not implemented"; // TODO + const osg::Vec2Array * texvecs = geo->getTexCoordArray(0) ? static_cast(geo->getTexCoordArray(0)) : NULL; + if (texvecs) + { + unsigned int nb = geo->getTexCoordArray(0)->getNumElements(); + if (nb != geo->getVertexArray()->getNumElements()) throw "There are more/less texture coords than vertices!"; + texcoords = true; + } + } + } + + if (nbVertices==0) return; + + int material = processStateSet(_currentStateSet.get()); + + for(unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i) //Fill the Triangle List + { + osg::PrimitiveSet* ps = geo->getPrimitiveSet(i); + PrimitiveIndexWriter pif(geo, listTriangles, drawable_n, material); + ps->accept(pif); + } +} + +bool WriterNodeVisitor::suceedLastApply() const +{ + return _suceedLastApply; +} + +void WriterNodeVisitor::failedApply() +{ + _suceedLastApply = false; + osg::notify(osg::NOTICE) << "Error going through node" << std::endl; +} + +void WriterNodeVisitor::apply( osg::Geode &node ) +{ + pushStateSet(node.getStateSet()); + //_nameStack.push_back(node.getName()); + //osg::Matrix m = osg::computeLocalToWorld(getNodePath()); + unsigned int count = node.getNumDrawables(); + ListTriangle listTriangles; + bool texcoords = false; + for ( unsigned int i = 0; i < count; i++ ) + { + osg::Geometry *g = node.getDrawable( i )->asGeometry(); + if ( g != NULL ) + { + pushStateSet(g->getStateSet()); + createListTriangle(g, listTriangles, texcoords, i); + popStateSet(g->getStateSet()); + } + } + if (count > 0) + { + buildFaces(node, listTriangles, texcoords); + } + popStateSet(node.getStateSet()); + //_nameStack.pop_back(); + if (suceedLastApply()) + traverse(node); +} + +void WriterNodeVisitor::apply(osg::Group &node) +{ + Lib3dsMeshInstanceNode * parent = _cur3dsNode; + Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : getFileName(node.getName()), "grp").c_str(), NULL, NULL, NULL); + lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); + _cur3dsNode = node3ds; + if (suceedLastApply()) + traverse(node); + _cur3dsNode = parent; +} + +void WriterNodeVisitor::apply(osg::MatrixTransform &node) +{ + Lib3dsMeshInstanceNode * parent = _cur3dsNode; + + const osg::Matrix & m = node.getMatrix(); + //const osg::Matrix m( osg::computeWorldToLocal(getNodePath()) ); // [NEEDS TESTING!] 3DS matrices always contain world to local transformation (not local transform; ie. from parent) + + // Transform data used to be given to lib3ds_node_new_mesh_instance(), but it seems buggy (pivot problem? bug in conversion?). + float pos[3]; + float scl[3]; + float rot[4]; + osg::Vec3 osgScl, osgPos; + osg::Quat osgRot, osgSo; + m.decompose(osgPos, osgRot, osgScl, osgSo); + copyOsgVectorToLib3dsVector(pos, osgPos); + copyOsgVectorToLib3dsVector(scl, osgScl); + copyOsgQuatToLib3dsQuat(rot, osgRot); + Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance + (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), pos, scl, rot); + + //// Create a mesh instance with no transform and then copy the matrix (doesn't work) + //Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance + // (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), NULL, NULL, NULL); + // copyOsgMatrixToLib3dsMatrix(node3ds->base.matrix, m); + + lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); + _cur3dsNode = node3ds; + if (suceedLastApply()) + traverse(node); + _cur3dsNode = parent; +}