From 6ec0e11b2a35a72c4633e087eb8140fb2fc0e2f5 Mon Sep 17 00:00:00 2001 From: Michael PLATINGS Date: Wed, 24 Feb 2010 10:25:50 +0000 Subject: [PATCH] --- src/osgPlugins/fbx/CMakeLists.txt | 4 + src/osgPlugins/fbx/ReaderWriterFBX.cpp | 311 ++++++++-- src/osgPlugins/fbx/ReaderWriterFBX.h | 10 +- src/osgPlugins/fbx/WriterNodeVisitor.cpp | 740 +++++++++++++++++++++++ src/osgPlugins/fbx/WriterNodeVisitor.h | 256 ++++++++ src/osgPlugins/fbx/fbxRAnimation.cpp | 119 +--- src/osgPlugins/fbx/fbxRAnimation.h | 5 - src/osgPlugins/fbx/fbxRMesh.cpp | 119 ++-- src/osgPlugins/fbx/fbxRMesh.h | 5 +- src/osgPlugins/fbx/fbxRNode.cpp | 383 +++++------- src/osgPlugins/fbx/fbxRNode.h | 6 +- 11 files changed, 1532 insertions(+), 426 deletions(-) create mode 100644 src/osgPlugins/fbx/WriterNodeVisitor.cpp create mode 100644 src/osgPlugins/fbx/WriterNodeVisitor.h diff --git a/src/osgPlugins/fbx/CMakeLists.txt b/src/osgPlugins/fbx/CMakeLists.txt index 858fa1822..b36e03f6f 100644 --- a/src/osgPlugins/fbx/CMakeLists.txt +++ b/src/osgPlugins/fbx/CMakeLists.txt @@ -7,6 +7,8 @@ SET(TARGET_SRC fbxRMesh.cpp fbxRNode.cpp ReaderWriterFBX.cpp + WriterNodeVisitor.cpp + fbxMaterialToOsgStateSet.cpp ) SET(TARGET_H @@ -16,6 +18,8 @@ SET(TARGET_H fbxRMesh.h fbxRNode.h ReaderWriterFBX.h + WriterNodeVisitor.h + fbxMaterialToOsgStateSet.h ) ADD_DEFINITIONS(-DKFBX_PLUGIN -DKFBX_FBXSDK -DKFBX_NODLL) diff --git a/src/osgPlugins/fbx/ReaderWriterFBX.cpp b/src/osgPlugins/fbx/ReaderWriterFBX.cpp index 612dbf0c4..291465ec8 100644 --- a/src/osgPlugins/fbx/ReaderWriterFBX.cpp +++ b/src/osgPlugins/fbx/ReaderWriterFBX.cpp @@ -4,18 +4,18 @@ #include #include #include +#include #include +#include #include #include #include #include - #include +#include #include -#include - #if defined(_MSC_VER) #pragma warning( disable : 4505 ) #endif @@ -23,20 +23,111 @@ #include "ReaderWriterFBX.h" #include "fbxRNode.h" +#include "fbxMaterialToOsgStateSet.h" +#include "WriterNodeVisitor.h" + + +/// Returns true if the given node is a basic root group with no special information. +/// Used in conjunction with UseFbxRoot option. +/// Identity transforms are considered as basic root nodes. +bool isBasicRootNode(const osg::Node& node) +{ + const osg::Group* osgGroup = node.asGroup(); + if (!osgGroup) + { + // Geodes & such are not basic root nodes + return false; + } + + // Test if we've got an empty transform (= a group!) + const osg::Transform* transform = osgGroup->asTransform(); + if (transform) + { + if (const osg::MatrixTransform* matrixTransform = transform->asMatrixTransform()) + { + if (!matrixTransform->getMatrix().isIdentity()) + { + // Non-identity matrix transform + return false; + } + } + else if (const osg::PositionAttitudeTransform* pat = transform->asPositionAttitudeTransform()) + { + if (pat->getPosition() != osg::Vec3d() || + pat->getAttitude() != osg::Quat() || + pat->getScale() != osg::Vec3d(1.0f, 1.0f, 1.0f) || + pat->getPivotPoint() != osg::Vec3d()) + { + // Non-identity position attribute transform + return false; + } + } + else + { + // Other transform (not identity or not predefined type) + return false; + } + } + + // Test the presence of a non-empty stateset + if (node.getStateSet()) + { + osg::ref_ptr emptyStateSet = new osg::StateSet; + if (node.getStateSet()->compare(*emptyStateSet, true) != 0) + { + return false; + } + } + + return true; +} + + +class CleanUpFbx +{ + KFbxSdkManager* m_pSdkManager; +public: + explicit CleanUpFbx(KFbxSdkManager* pSdkManager) : m_pSdkManager(pSdkManager) + {} + + ~CleanUpFbx() + { + KFbxIOSettings::IOSettingsRef().FreeIOSettings(); + m_pSdkManager->Destroy(); + } +}; + +class ConvertBindMatrixVisitor : public osg::NodeVisitor +{ +public: + ConvertBindMatrixVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {} + + virtual void apply(osg::MatrixTransform& node) + { + if (osgAnimation::Bone* bone = dynamic_cast(&node)) + { + bone->setInvBindMatrixInSkeletonSpace(osg::Matrix::inverse(bone->getMatrixInBoneSpace())); + if (const osgAnimation::Bone* parent = bone->getBoneParent()) + { + bone->setInvBindMatrixInSkeletonSpace(parent->getInvBindMatrixInSkeletonSpace() * bone->getInvBindMatrixInSkeletonSpace()); + } + } + + traverse(node); + } +}; osgDB::ReaderWriter::ReadResult -ReaderWriterFBX::readNode(const std::string& utf8filename, - const osgDB::ReaderWriter::Options* options) const +ReaderWriterFBX::readNode(const std::string& filenameInit, + const Options* options) const { - OpenThreads::ScopedLock lock(_serializerMutex); - try { - std::string ext(osgDB::getLowerCaseFileExtension(utf8filename)); - if(!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; + std::string ext(osgDB::getLowerCaseFileExtension(filenameInit)); + if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; - std::string fileName(osgDB::findDataFile(utf8filename, options)); - if( fileName.empty()) return ReadResult::FILE_NOT_FOUND; + std::string filename(osgDB::findDataFile(filenameInit, options)); + if (filename.empty()) return ReadResult::FILE_NOT_FOUND; KFbxSdkManager* pSdkManager = KFbxSdkManager::Create(); @@ -45,29 +136,23 @@ ReaderWriterFBX::readNode(const std::string& utf8filename, return ReadResult::ERROR_IN_READING_FILE; } - class CleanUpFbx - { - KFbxSdkManager* m_pSdkManager; - public: - CleanUpFbx(KFbxSdkManager* pSdkManager) : m_pSdkManager(pSdkManager) - {} + CleanUpFbx cleanUpFbx(pSdkManager); - ~CleanUpFbx() - { - KFbxIOSettings::IOSettingsRef().FreeIOSettings(); - m_pSdkManager->Destroy(); - } - } cleanUpFbx(pSdkManager); + KFbxScene* pScene = KFbxScene::Create(pSdkManager, ""); - KFbxScene* pScene = KFbxScene::Create(pSdkManager,""); + // The FBX SDK interprets the filename as UTF-8 +#ifdef OSG_USE_UTF8_FILENAME + const std::string& utf8filename(filename); +#else + std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename)); +#endif int fileFormat; if (!pSdkManager->GetIOPluginRegistry()->DetectFileFormat(utf8filename.c_str(), fileFormat)) { return ReadResult::FILE_NOT_HANDLED; } - - KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager,""); + KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager, ""); lImporter->SetFileFormat(fileFormat); if (!lImporter->Initialize(utf8filename.c_str())) @@ -80,7 +165,7 @@ ReaderWriterFBX::readNode(const std::string& utf8filename, return ReadResult::ERROR_IN_READING_FILE; } - for(int i = 0; i < lImporter->GetTakeCount(); i++) + for (int i = 0; i < lImporter->GetTakeCount(); i++) { KFbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i); @@ -92,18 +177,47 @@ ReaderWriterFBX::readNode(const std::string& utf8filename, return std::string(lImporter->GetLastErrorString()); } + //KFbxAxisSystem::OpenGL.ConvertScene(pScene); // Doesn't work as expected. Still need to transform vertices. + if (KFbxNode* pNode = pScene->GetRootNode()) { + bool useFbxRoot = false; + if (options) + { + std::istringstream iss(options->getOptionString()); + std::string opt; + while (iss >> opt) + { + if (opt == "UseFbxRoot") + { + useFbxRoot = true; + } + } + } + osg::ref_ptr pAnimationManager; bool bNeedSkeleton = false; int nLightCount = 0; + osg::ref_ptr localOptions = NULL; + if (options) + localOptions = options->cloneOptions(); + else + localOptions = new osgDB::Options(); + localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_IMAGES); + + std::string filePath = osgDB::getFilePath(filename); + FbxMaterialToOsgStateSet fbxMaterialToOsgStateSet(filePath, localOptions.get()); + ReadResult res = readFbxNode(*pSdkManager, pNode, pAnimationManager, - osgDB::getFilePath(fileName), bNeedSkeleton, nLightCount); + bNeedSkeleton, nLightCount, fbxMaterialToOsgStateSet, localOptions.get()); + if (res.success()) { osg::Node* osgNode = res.getNode(); if (bNeedSkeleton) { + ConvertBindMatrixVisitor convertBindMatrixVisitor; + osgNode->accept(convertBindMatrixVisitor); osgAnimation::Skeleton* osgSkeleton = new osgAnimation::Skeleton; osgSkeleton->setDefaultUpdateCallback(); osgSkeleton->addChild(osgNode); @@ -120,16 +234,16 @@ ReaderWriterFBX::readNode(const std::string& utf8filename, //because the animations may be altered after registering pAnimationManager->buildTargetReference(); - osgNode->setUpdateCallback(pAnimationManager.get()); } KFbxAxisSystem fbxAxis = pScene->GetGlobalSettings().GetAxisSystem(); - int upSign; - KFbxAxisSystem::eUpVector eUp = fbxAxis.GetUpVector(upSign); - bool bLeftHanded = fbxAxis.GetCoorSystem() == KFbxAxisSystem::LeftHanded; - if (eUp != KFbxAxisSystem::YAxis || upSign < 0 || bLeftHanded) + + if (fbxAxis != KFbxAxisSystem::OpenGL) { + int upSign; + KFbxAxisSystem::eUpVector eUp = fbxAxis.GetUpVector(upSign); + bool bLeftHanded = fbxAxis.GetCoorSystem() == KFbxAxisSystem::LeftHanded; float fSign = upSign < 0 ? -1.0f : 1.0f; float zScale = bLeftHanded ? -1.0f : 1.0f; @@ -146,11 +260,36 @@ ReaderWriterFBX::readNode(const std::string& utf8filename, mat.set(1,0,0,0,0,0,-fSign*zScale,0,0,fSign,0,0,0,0,0,1); break; } - osg::MatrixTransform* pTransform = new osg::MatrixTransform(mat); - pTransform->addChild(osgNode); - osgNode = pTransform; + + osg::Transform* pTransformTemp = osgNode->asTransform(); + osg::MatrixTransform* pMatrixTransform = pTransformTemp ? + pTransformTemp->asMatrixTransform() : NULL; + if (pMatrixTransform) + { + pMatrixTransform->setMatrix(pMatrixTransform->getMatrix() * mat); + } + else + { + pMatrixTransform = new osg::MatrixTransform(mat); + if (useFbxRoot && isBasicRootNode(*osgNode)) + { + // If root node is a simple group, put all FBX elements under the OSG root + osg::Group* osgGroup = osgNode->asGroup(); + for(unsigned int i = 0; i < osgGroup->getNumChildren(); ++i) + { + pMatrixTransform->addChild(osgGroup->getChild(i)); + } + pMatrixTransform->setName(osgGroup->getName()); + } + else + { + pMatrixTransform->addChild(osgNode); + } + } + osgNode = pMatrixTransform; } + osgNode->setName(filenameInit); return osgNode; } } @@ -162,6 +301,106 @@ ReaderWriterFBX::readNode(const std::string& utf8filename, return ReadResult::ERROR_IN_READING_FILE; } +osgDB::ReaderWriter::WriteResult ReaderWriterFBX::writeNode( + const osg::Node& node, + const std::string& filename, + const Options* options) const +{ + try + { + std::string ext = osgDB::getLowerCaseFileExtension(filename); + if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; + + osg::ref_ptr localOptions = options ? + static_cast(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; + localOptions->getDatabasePathList().push_front(osgDB::getFilePath(filename)); + + KFbxSdkManager* pSdkManager = KFbxSdkManager::Create(); + + if (!pSdkManager) + { + return WriteResult::ERROR_IN_WRITING_FILE; + } + + CleanUpFbx cleanUpFbx(pSdkManager); + + bool useFbxRoot = false; + if (options) + { + std::istringstream iss(options->getOptionString()); + std::string opt; + while (iss >> opt) + { + if (opt == "Embedded") + { + IOSREF.SetBoolProp(EXP_FBX_EMBEDDED, true); + if (KFbxIOSettings::IOSettingsRef().IsIOSettingsAllocated()) + KFbxIOSettings::IOSettingsRef().AllocateIOSettings(*pSdkManager); + } + else if (opt == "UseFbxRoot") + { + useFbxRoot = true; + } + } + } + + KFbxScene* pScene = KFbxScene::Create(pSdkManager, ""); + WriterNodeVisitor writerNodeVisitor(pScene, pSdkManager, filename, + options, osgDB::getFilePath(node.getName().empty() ? filename : node.getName())); + if (useFbxRoot && isBasicRootNode(node)) + { + // If root node is a simple group, put all elements under the FBX root + const osg::Group * osgGroup = node.asGroup(); + for(unsigned int child=0; childgetNumChildren(); ++child) { + const_cast(osgGroup->getChild(child))->accept(writerNodeVisitor); + } + } + else { + // Normal scene + const_cast(node).accept(writerNodeVisitor); + } + + KFbxExporter* lExporter = KFbxExporter::Create(pSdkManager, ""); + pScene->GetGlobalSettings().SetAxisSystem(KFbxAxisSystem::eOpenGL); + + // Ensure the directory exists or else the FBX SDK will fail + if (!osgDB::makeDirectoryForFile(filename)) { + osg::notify(osg::NOTICE) << "Can't create directory for file '" << filename << "'. FBX SDK may fail creating the file." << std::endl; + } + + // The FBX SDK interprets the filename as UTF-8 +#ifdef OSG_USE_UTF8_FILENAME + std::string utf8filename(filename); +#else + std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename)); +#endif + + if (!lExporter->Initialize(utf8filename.c_str())) + { + return std::string(lExporter->GetLastErrorString()); + } + if (!lExporter->Export(pScene)) + { + return std::string(lExporter->GetLastErrorString()); + } + + return WriteResult::FILE_SAVED; + } + catch (const std::string& s) + { + return s; + } + catch (const char* s) + { + return std::string(s); + } + catch (...) + { + } + + return WriteResult::ERROR_IN_WRITING_FILE; +} + /////////////////////////////////////////////////////////////////////////// // Add ourself to the Registry to instantiate the reader/writer. diff --git a/src/osgPlugins/fbx/ReaderWriterFBX.h b/src/osgPlugins/fbx/ReaderWriterFBX.h index 3055304a7..845999a97 100644 --- a/src/osgPlugins/fbx/ReaderWriterFBX.h +++ b/src/osgPlugins/fbx/ReaderWriterFBX.h @@ -1,7 +1,6 @@ #ifndef READERWRITERFBX_H #define READERWRITERFBX_H -#include #include /////////////////////////////////////////////////////////////////////////// @@ -14,15 +13,14 @@ public: ReaderWriterFBX() { supportsExtension("fbx", "FBX format"); + supportsOption("Embedded", "Embed textures in FBX file when writing"); + supportsOption("UseFbxRoot", "(Read/write option) If the source OSG root node is a simple group with no stateset, the writer will put its children directly under the FBX root, and vice-versa for reading"); } const char* className() const { return "FBX reader/writer"; } - /// The FBX SDK interprets the filename as UTF-8 - ReadResult readNode(const std::string& utf8filename, const Options*) const; - -private: - mutable OpenThreads::ReentrantMutex _serializerMutex; + virtual ReadResult readNode(const std::string& filename, const Options*) const; + virtual WriteResult writeNode(const osg::Node&, const std::string& filename, const Options*) const; }; /////////////////////////////////////////////////////////////////////////// diff --git a/src/osgPlugins/fbx/WriterNodeVisitor.cpp b/src/osgPlugins/fbx/WriterNodeVisitor.cpp new file mode 100644 index 000000000..d8e00564a --- /dev/null +++ b/src/osgPlugins/fbx/WriterNodeVisitor.cpp @@ -0,0 +1,740 @@ +// -*-c++-*- + +/* + * FBX writer for Open Scene Graph + * + * Copyright (C) 2009 + * + * Writing support added 2009 by Thibault Caporal and Sukender (Benoit Neil - http://sukender.free.fr) + * + * 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 +#include +#include +#include +#include +#include +#include +#include "WriterNodeVisitor.h" +#include + +/** 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(const osg::Geometry* geo, + ListTriangle& listTriangles, + unsigned int drawable_n, + unsigned int material) : + _hasNormalCoords(geo->getNormalArray() != NULL), + _hasTexCoords(geo->getTexCoordArray(0) != NULL), + _geo(geo), + _lastFaceIndex(0), + _listTriangles(listTriangles), + _drawable_n(drawable_n), + _material(material) + { + } + + unsigned int getNextFaceIndex() { return _lastFaceIndex; } + + virtual void setVertexArray(unsigned int, const osg::Vec2*) {} + + virtual void setVertexArray(unsigned int, const osg::Vec3*) {} + + 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: + template void drawElementsImplementation(GLenum mode, GLsizei count, const T* indices) + { + if (indices==0 || count==0) return; + + typedef const T* IndexPointer; + + switch (mode) + { + case GL_TRIANGLES: + { + IndexPointer ilast = indices + count; + for (IndexPointer iptr = indices; iptr < ilast; iptr+=3) + writeTriangle(iptr[0], iptr[1], iptr[2]); + + break; + } + case GL_TRIANGLE_STRIP: + { + IndexPointer iptr = indices; + for (GLsizei i = 2; i < count; ++i, ++iptr) + { + if (i & 1) writeTriangle(iptr[0], iptr[2], iptr[1]); + else writeTriangle(iptr[0], iptr[1], iptr[2]); + } + break; + } + case GL_QUADS: + { + IndexPointer iptr = indices; + for (GLsizei i = 3; i < count; i += 4, iptr += 4) + { + writeTriangle(iptr[0], iptr[1], iptr[2]); + writeTriangle(iptr[0], iptr[2], iptr[3]); + } + break; + } + case GL_QUAD_STRIP: + { + IndexPointer iptr = indices; + for (GLsizei i = 3; i < count; i += 2, iptr += 2) + { + writeTriangle(iptr[0], iptr[1], iptr[2]); + writeTriangle(iptr[1], iptr[3], iptr[2]); + } + break; + } + case GL_POLYGON: // treat polygons as GL_TRIANGLE_FAN + case GL_TRIANGLE_FAN: + { + IndexPointer iptr = indices; + unsigned int first = *iptr; + ++iptr; + for (GLsizei i = 2; i < count; ++i, ++iptr) + { + writeTriangle(first, iptr[0], iptr[1]); + } + break; + } + case GL_POINTS: + case GL_LINES: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + // Not handled + break; + default: + // uhm should never come to this point :) + break; + } + } + +private: + PrimitiveIndexWriter& operator = (const PrimitiveIndexWriter&) { return *this; } + + unsigned int _drawable_n; + ListTriangle& _listTriangles; + GLenum _modeCache; + std::vector _indexCache; + bool _hasNormalCoords, _hasTexCoords; + const osg::Geometry* _geo; + unsigned int _lastFaceIndex; + int _material; + KFbxMesh* _mesh; +}; + +void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count) +{ + unsigned int pos=first; + switch (mode) + { + case GL_TRIANGLES: + for (GLsizei i = 2; i < count; i += 3, pos += 3) + { + writeTriangle(pos, pos + 1, pos + 2); + } + break; + case GL_TRIANGLE_STRIP: + for (GLsizei i = 2; i < count; ++i, ++pos) + { + if (i & 1) writeTriangle(pos, pos + 2, pos + 1); + else writeTriangle(pos, pos + 1, pos + 2); + } + break; + case GL_QUADS: + for (GLsizei i = 3; i < count; i += 4, pos += 4) + { + writeTriangle(pos, pos + 1, pos + 2); + writeTriangle(pos, pos + 2, pos + 3); + } + break; + case GL_QUAD_STRIP: + for (GLsizei i = 3; i < count; i += 2, pos += 2) + { + writeTriangle(pos, pos + 1, pos + 2); + writeTriangle(pos + 1, pos + 3, pos + 2); + } + break; + case GL_POLYGON: // treat polygons as GL_TRIANGLE_FAN + case GL_TRIANGLE_FAN: + pos = first + 1; + for (GLsizei i = 2; i < count; ++i, ++pos) + { + writeTriangle(first, pos, pos+1); + } + break; + case GL_POINTS: + case GL_LINES: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + default: + osg::notify(osg::WARN) << "WriterNodeVisitor :: can't handle mode " << mode << std::endl; + break; + } +} + +// If 'to' is in a subdirectory of 'from' then this function returns the +// subpath. Otherwise it just returns the file name. +std::string getPathRelative(const std::string& from/*directory*/, + const std::string& to/*file path*/) +{ + + std::string::size_type slash = to.find_last_of('/'); + std::string::size_type backslash = to.find_last_of('\\'); + if (slash == std::string::npos) + { + if (backslash == std::string::npos) return to; + slash = backslash; + } + else if (backslash != std::string::npos && backslash > slash) + { + slash = backslash; + } + + if (from.empty() || from.length() > to.length()) + return osgDB::getSimpleFileName(to); + + std::string::const_iterator itTo = to.begin(); + for (std::string::const_iterator itFrom = from.begin(); + itFrom != from.end(); ++itFrom, ++itTo) + { + char a = tolower(*itFrom), b = tolower(*itTo); + if (a == '\\') a = '/'; + if (b == '\\') b = '/'; + if (a != b || itTo == to.begin() + slash + 1) + { + return osgDB::getSimpleFileName(to); + } + } + + while (itTo != to.end() && (*itTo == '\\' || *itTo == '/')) + { + ++itTo; + } + + return std::string(itTo, to.end()); +} + +//std::string testA = getPathRelative("C:\\a\\b", "C:\\a/b/d/f"); +//std::string testB = getPathRelative("C:\\a\\d", "C:\\a/b/d/f"); +//std::string testC = getPathRelative("C:\\ab", "C:\\a/b/d/f"); +//std::string testD = getPathRelative("a/d", "a/d"); + +WriterNodeVisitor::Material::Material(WriterNodeVisitor& writerNodeVisitor, + const std::string& srcDirectory, + const osg::StateSet* stateset, + const osg::Material* mat, + const osg::Texture* tex, + KFbxSdkManager* pSdkManager, + const std::string& directory, + ImageSet& imageSet, + ImageFilenameSet& imageFilenameSet, + unsigned int& lastGeneratedImageFileName, + int index) : + _index(index), + _fbxMaterial(NULL), + _fbxTexture(NULL), + _osgImage(NULL), + _directory(directory) +{ + osg::Vec4 diffuse(1,1,1,1), + ambient(0.2,0.2,0.2,1), + specular(0,0,0,1), + emission(1,1,1,1); + + float shininess(0); + float transparency(0); + + if (mat) + { + assert(stateset); + diffuse = mat->getDiffuse(osg::Material::FRONT); + ambient = mat->getAmbient(osg::Material::FRONT); + specular = mat->getSpecular(osg::Material::FRONT); + shininess = mat->getShininess(osg::Material::FRONT); + emission = mat->getEmission(osg::Material::FRONT); + transparency = 1 - diffuse.w(); + + const osg::StateAttribute* attribute = stateset->getAttribute(osg::StateAttribute::CULLFACE); + if (attribute) + { + assert(dynamic_cast(attribute)); + osg::CullFace::Mode mode = static_cast(attribute)->getMode(); + if (mode == osg::CullFace::FRONT) + { + osg::notify(osg::WARN) << "FBX Writer: Reversed face (culled FRONT) not supported yet." << std::endl; + } + else if (mode != osg::CullFace::BACK) + { + assert(mode == osg::CullFace::FRONT_AND_BACK); + osg::notify(osg::WARN) << "FBX Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl; + } + } + + _fbxMaterial = KFbxSurfacePhong::Create(pSdkManager, mat->getName().c_str()); + if (_fbxMaterial) + { + _fbxMaterial->GetDiffuseFactor().Set(1, true); + _fbxMaterial->GetDiffuseColor().Set(fbxDouble3( + diffuse.x(), + diffuse.y(), + diffuse.z())); + + _fbxMaterial->GetTransparencyFactor().Set(transparency); + + _fbxMaterial->GetAmbientColor().Set(fbxDouble3( + ambient.x(), + ambient.y(), + ambient.z())); + + _fbxMaterial->GetEmissiveColor().Set(fbxDouble3( + emission.x(), + emission.y(), + emission.z())); + + _fbxMaterial->GetSpecularColor().Set(fbxDouble3( + specular.x(), + specular.y(), + specular.z())); + + _fbxMaterial->GetShininess().Set(shininess); + } + } + if (tex && tex->getImage(0)) + { + _osgImage = tex->getImage(0); + + ImageSet::iterator it = imageSet.find(_osgImage); + + if (it == imageSet.end()) + { + std::string canonicalPath( osgDB::getRealPath(osgDB::convertFileNameToNativeStyle(_osgImage->getFileName())) ); + std::string destPath; + std::string relativePath; + if (canonicalPath.empty()) + { + static const unsigned int MAX_IMAGE_NUMBER = UINT_MAX-1; // -1 to allow doing +1 without an overflow + unsigned int imageNumber; + for (imageNumber=lastGeneratedImageFileName+1; imageNumbersecond.c_str()); + _fbxTexture->SetFileName(it->second.c_str()); + } +} + +int WriterNodeVisitor::processStateSet(const osg::StateSet* ss) +{ + //osg::notify(osg::ALWAYS) << "Trying Adding " << ss->getAttribute(osg::StateAttribute::MATERIAL)->getName() << std::endl; + MaterialMap::iterator itr = _materialMap.find(MaterialMap::key_type(ss)); + if (itr != _materialMap.end()) + { + if (itr->second.getIndex() < 0) + itr->second.setIndex(_lastMaterialIndex++); + return itr->second.getIndex(); + } + + const osg::Material* mat = dynamic_cast(ss->getAttribute(osg::StateAttribute::MATERIAL)); + const osg::Texture* tex = dynamic_cast(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE)); + + if (mat || tex) + { + int matNum = _lastMaterialIndex; + _materialMap.insert(MaterialMap::value_type(MaterialMap::key_type(ss), + Material(*this, _srcDirectory, ss, mat, tex, _pSdkManager, _directory, _imageSet, _imageFilenameSet, _lastGeneratedImageFileName, matNum))); + ++_lastMaterialIndex; + return matNum; + } + return -1; +} + +/** +* Add a vertex to the index and link him with the Triangle index and the drawable. +* \param index_vert is the map where the vertices are stored. +* \param index is the index of the vertices position in the vec3. +* \param drawable_n is the number of the drawable. +* \return the position of the vertex 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::setLayerTextureAndMaterial(KFbxMesh* mesh) +{ + KFbxLayerElementTexture* lTextureDiffuseLayer = KFbxLayerElementTexture::Create(mesh, "Diffuse"); + lTextureDiffuseLayer->SetMappingMode(KFbxLayerElement::eBY_POLYGON); + lTextureDiffuseLayer->SetReferenceMode(KFbxLayerElement::eINDEX_TO_DIRECT); + + KFbxLayerElementMaterial* lMaterialLayer = KFbxLayerElementMaterial::Create(mesh, "materialLayer"); + lMaterialLayer->SetMappingMode(KFbxLayerElement::eBY_POLYGON); + lMaterialLayer->SetReferenceMode(KFbxLayerElement::eINDEX_TO_DIRECT); + + lTextureDiffuseLayer->GetDirectArray().SetCount(_lastMaterialIndex); + lMaterialLayer->GetDirectArray().SetCount(_lastMaterialIndex); + for (MaterialMap::iterator it = _materialMap.begin(); it != _materialMap.end(); ++it) + { + if (it->second.getIndex() != -1) + { + KFbxSurfaceMaterial* lMaterial = it->second.getFbxMaterial(); + KFbxTexture* lTexture = it->second.getFbxTexture(); + lTextureDiffuseLayer->GetDirectArray().SetAt(it->second.getIndex(), lTexture); + lMaterialLayer->GetDirectArray().SetAt(it->second.getIndex(), lMaterial); + } + } + mesh->GetLayer(0)->SetMaterials(lMaterialLayer); + mesh->GetLayer(0)->SetDiffuseTextures(lTextureDiffuseLayer); +} + +void +WriterNodeVisitor::setControlPointAndNormalsAndUV(const osg::Geode& geo, + MapIndices& index_vert, + bool texcoords, + KFbxMesh* mesh) +{ + mesh->InitControlPoints(index_vert.size()); + KFbxLayerElementNormal* lLayerElementNormal= KFbxLayerElementNormal::Create(mesh, ""); + lLayerElementNormal->SetMappingMode(KFbxLayerElement::eBY_CONTROL_POINT); + lLayerElementNormal->SetReferenceMode(KFbxLayerElement::eDIRECT); + lLayerElementNormal->GetDirectArray().SetCount(index_vert.size()); + mesh->GetLayer(0)->SetNormals(lLayerElementNormal); + KFbxLayerElementUV* lUVDiffuseLayer = KFbxLayerElementUV::Create(mesh, "DiffuseUV"); + + if (texcoords) + { + lUVDiffuseLayer->SetMappingMode(KFbxLayerElement::eBY_CONTROL_POINT); + lUVDiffuseLayer->SetReferenceMode(KFbxLayerElement::eDIRECT); + lUVDiffuseLayer->GetDirectArray().SetCount(index_vert.size()); + mesh->GetLayer(0)->SetUVs(lUVDiffuseLayer, KFbxLayerElement::eDIFFUSE_TEXTURES); + } + + for (MapIndices::iterator it = index_vert.begin(); it != index_vert.end(); ++it) + { + const osg::Geometry* pGeometry = geo.getDrawable(it->first.second)->asGeometry(); + assert(pGeometry->getVertexArray()); + + if (pGeometry->getVertexArray()->getType() != osg::Array::Vec3ArrayType) + { + throw "Vertex array is not Vec3. Not implemented"; // TODO + } + + const osg::Vec3Array* vecs= static_cast(pGeometry->getVertexArray()); + if (vecs) + { + mesh->SetControlPointAt(*new KFbxVector4( + (*vecs)[it->first.first].x(), + (*vecs)[it->first.first].y(), + (*vecs)[it->first.first].z()), it->second); + } + + const osg::Vec3Array* pNormals = static_cast(pGeometry->getNormalArray()); + + if (pNormals) + { + switch (pGeometry->getNormalBinding()) + { + case osg::Geometry::BIND_PER_PRIMITIVE_SET: + case osg::Geometry::BIND_PER_PRIMITIVE: + lLayerElementNormal->GetDirectArray().SetAt(it->second, + KFbxVector4( + (*pNormals)[0].x(), + (*pNormals)[0].y(), + (*pNormals)[0].z(), 0)); + break; + + case osg::Geometry::BIND_PER_VERTEX: + lLayerElementNormal->GetDirectArray().SetAt(it->second, + KFbxVector4( + (*pNormals)[it->first.first].x(), + (*pNormals)[it->first.first].y(), + (*pNormals)[it->first.first].z(), 0)); + break; + } + } + + if (texcoords) + { + const osg::Vec2Array* vec2= static_cast(pGeometry->getTexCoordArray(0)); + if (vec2) + lUVDiffuseLayer->GetDirectArray().SetAt(it->second, KFbxVector2((*vec2)[it->first.first].x(), (*vec2)[it->first.first].y())); + } + } +} + +void WriterNodeVisitor::buildFaces(const osg::Geode& geo, + ListTriangle& listTriangles, + bool texcoords) +{ + MapIndices index_vert; + KFbxMesh* mesh = KFbxMesh::Create(_pSdkManager, geo.getName().c_str()); + _curFbxNode->AddNodeAttribute(mesh); + _curFbxNode->SetShadingMode(KFbxNode::eTEXTURE_SHADING); + KFbxLayer* lLayer = mesh->GetLayer(0); + if (lLayer == NULL) + { + mesh->CreateLayer(); + lLayer = mesh->GetLayer(0); + } + setLayerTextureAndMaterial(mesh); + lLayer->GetDiffuseTextures()->GetIndexArray().SetCount(listTriangles.size()); + lLayer->GetMaterials()->GetIndexArray().SetCount(listTriangles.size()); + + unsigned int i = 0; + for (ListTriangle::iterator it = listTriangles.begin(); it != listTriangles.end(); ++it, ++i) //Go through the triangle list to define meshs + { + if (it->first.material == -1) + { + mesh->BeginPolygon(); + } + else + { + mesh->BeginPolygon(i); + lLayer->GetDiffuseTextures()->GetIndexArray().SetAt(i, it->first.material); + lLayer->GetMaterials()->GetIndexArray().SetAt(i, it->first.material); + } + mesh->AddPolygon(getMeshIndexForGeometryIndex(index_vert, it->first.t1, it->second)); + mesh->AddPolygon(getMeshIndexForGeometryIndex(index_vert, it->first.t2, it->second)); + mesh->AddPolygon(getMeshIndexForGeometryIndex(index_vert, it->first.t3, it->second)); + mesh->EndPolygon(); + } + setControlPointAndNormalsAndUV(geo, index_vert, texcoords, mesh); +} + +void WriterNodeVisitor::createListTriangle(const 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 = static_cast(geo->getVertexArray()); + 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 = static_cast(geo->getTexCoordArray(0)); + 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 + { + const osg::PrimitiveSet* ps = geo->getPrimitiveSet(i); + PrimitiveIndexWriter pif(geo, listTriangles, drawable_n, material); + const_cast(ps)->accept(pif); + } +} + +void WriterNodeVisitor::apply(osg::Geode& node) +{ + KFbxNode* parent = _curFbxNode; + KFbxNode* nodeFBX = KFbxNode::Create(_pSdkManager, node.getName().empty() ? "DefaultName" : node.getName().c_str()); + _curFbxNode->AddChild(nodeFBX); + _curFbxNode = nodeFBX; + if (false) + { + KFbxProperty lProperty = KFbxProperty::Create(_curFbxNode, "nameGeode", DTString, "label2"); + std::ostringstream oss; + oss << node.getName().c_str() << ".metaData"; + lProperty.Set(new KString(oss.str().c_str()), eSTRING); + } + unsigned int count = node.getNumDrawables(); + ListTriangle listTriangles; + bool texcoords = false; + for (MaterialMap::iterator it = _materialMap.begin(); it != _materialMap.end(); ++it) + it->second.setIndex(-1); + _lastMaterialIndex = 0; + for (unsigned int i = 0; i < count; ++i) + { + const 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); + } + if (succeedLastApply()) + traverse(node); + _curFbxNode = parent; +} + +void WriterNodeVisitor::apply(osg::Group& node) +{ + KFbxNode* parent = _curFbxNode; + + KFbxNode* nodeFBX = KFbxNode::Create(_pSdkManager, node.getName().empty() ? "DefaultName" : node.getName().c_str()); + _curFbxNode->AddChild(nodeFBX); + _curFbxNode = nodeFBX; + if (false) + { + KFbxProperty lProperty = KFbxProperty::Create(_curFbxNode, "nameGeode", DTString, "label2"); + std::ostringstream oss; + oss << node.getName().c_str() << ".metaData"; + lProperty.Set(new KString(oss.str().c_str()), eSTRING); + } + traverse(node); + _curFbxNode = parent; +} + +void WriterNodeVisitor::apply(osg::MatrixTransform& node) +{ + KFbxNode* parent = _curFbxNode; + _curFbxNode = KFbxNode::Create(_pSdkManager, node.getName().empty() ? "DefaultName" : node.getName().c_str()); + parent->AddChild(_curFbxNode); + if (false) + { + KFbxProperty lProperty = KFbxProperty::Create(_curFbxNode, "nameGeode", DTString, "label2"); + std::ostringstream oss; + oss << node.getName().c_str() << ".metaData"; + lProperty.Set(new KString(oss.str().c_str()), eSTRING); + } + + const osg::Matrix& matrix = node.getMatrix(); + osg::Vec3d pos, scl; + osg::Quat rot, so; + + matrix.decompose(pos, rot, scl, so); + _curFbxNode->LclTranslation.Set(fbxDouble3(pos.x(), pos.y(), pos.z())); + _curFbxNode->LclScaling.Set(fbxDouble3(scl.x(), scl.y(), scl.z())); + + KFbxXMatrix mat; + + KFbxQuaternion q(rot.x(), rot.y(), rot.z(), rot.w()); + mat.SetQ(q); + KFbxVector4 vec4 = mat.GetR(); + + _curFbxNode->LclRotation.Set(fbxDouble3(vec4[0], vec4[1], vec4[2])); + + traverse(node); + _curFbxNode = parent; +} diff --git a/src/osgPlugins/fbx/WriterNodeVisitor.h b/src/osgPlugins/fbx/WriterNodeVisitor.h new file mode 100644 index 000000000..381c27ffb --- /dev/null +++ b/src/osgPlugins/fbx/WriterNodeVisitor.h @@ -0,0 +1,256 @@ +// -*-c++-*- + +/* + * FBX writer for Open Scene Graph + * + * Copyright (C) 2009 + * + * Writing support added 2009 by Thibault Caporal and Sukender (Benoit Neil - http://sukender.free.fr) + * + * 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/ + */ + +#ifndef _FBX_WRITER_NODE_VISITOR_HEADER__ +#define _FBX_WRITER_NODE_VISITOR_HEADER__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning( disable : 4505 ) +#endif +#include + +struct Triangle +{ + unsigned int t1; + unsigned int t2; + unsigned int t3; + int material; +}; + +typedef std::map, unsigned int> MapIndices; +typedef std::vector > ListTriangle; //the int is the drawable of the triangle + +///\author Capo (Thibault Caporal) +class WriterNodeVisitor: public osg::NodeVisitor +{ + public: + WriterNodeVisitor(KFbxScene* pScene, + KFbxSdkManager* pSdkManager, + const std::string& fileName, + const osgDB::ReaderWriter::Options* options, + const std::string& srcDirectory) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _pScene(pScene), + _pSdkManager(pSdkManager), + _currentStateSet(new osg::StateSet()), + _lastMaterialIndex(0), + _lastMeshIndex(0), + _lastGeneratedImageFileName(0), + _curFbxNode(pScene->GetRootNode()), + _options(options), + _succeedLastApply(true), + _directory(osgDB::getFilePath(fileName)), + _srcDirectory(srcDirectory) + {} + + ///Tell us if last Node succeed traversing. + bool succeedLastApply() const { return _succeedLastApply; } + + ///Set the flag _succeedLastApply to false. + void failedApply() { _succeedLastApply = false; } + + virtual void apply(osg::Geode& node); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + + void traverse (osg::Node& node) + { + pushStateSet(node.getStateSet()); + osg::NodeVisitor::traverse(node); + popStateSet(node.getStateSet()); + } + + void pushStateSet(const osg::StateSet* ss) + { + if (ss) + { + // Save our current stateset + _stateSetStack.push(_currentStateSet.get()); + + // merge with node stateset + _currentStateSet = static_cast( + _currentStateSet->clone(osg::CopyOp::SHALLOW_COPY)); + _currentStateSet->merge(*ss); + } + } + + + void popStateSet(const osg::StateSet* ss) + { + if (ss) + { + // restore the previous stateset + _currentStateSet = _stateSetStack.top(); + _stateSetStack.pop(); + } + } + + /// Copy the texture file in current path. + void copyTexture(); + typedef std::map ImageSet; + typedef std::set ImageFilenameSet; // Sub-optimal because strings are doubled (in ImageSet). Moreover, an unordered_set (= hashset) would be more efficient (Waiting for unordered_set to be included in C++ standard ;) ). + + ///\todo Add support for 2nd texture, opacity_map, bump_map, specular_map, shininess_map, self_illum_map, reflection_map. + class Material + { + public: + ///Create a KfbxMaterial and KfbxTexture from osg::Texture and osg::Material. + Material(WriterNodeVisitor& writerNodeVisitor, + const std::string& srcDirectory, + const osg::StateSet* stateset, + const osg::Material* mat, + const osg::Texture* tex, + KFbxSdkManager* pSdkManager, + const std::string& directory, + ImageSet& imageSet, + ImageFilenameSet& imageFilenameSet, + unsigned int& lastGeneratedImageFileName, + int index = -1); + + KFbxTexture* getFbxTexture() const + { + return _fbxTexture; + } + + KFbxSurfaceMaterial* getFbxMaterial() const + { + return _fbxMaterial; + } + + const osg::Image* getOsgImage() const + { + return _osgImage; + } + + const int getIndex() const + { + return _index; + } + + void setIndex(int index) + { + _index = index; + } + + private: + KFbxSurfacePhong* _fbxMaterial; + KFbxTexture* _fbxTexture; + int _index;///< Index in the Map + const osg::Image* _osgImage; + const std::string& _directory; + }; + + protected: + /// Compares StateSets. + ///\todo It may be useful to compare stack of pointers (see pushStateset()) in order to keep the same number of FBX materials when doing reading and then writing without further processing. + struct CompareStateSet + { + bool operator () (const osg::ref_ptr& ss1, const osg::ref_ptr& ss2) const + { + return *ss1 < *ss2; + } + }; + + private: + /** + * Fill the faces field of the mesh and call buildMesh(). + * \param geo is the geode which contains the vertices and faces. + * \param listTriangles contain all the mesh's faces. + * \param texcoords tell us if we have to handle texture coordinates. + */ + void buildFaces(const osg::Geode& geo, + ListTriangle& listTriangles, + bool texcoords); + + /// Set the layer for texture and Material in layer 0. + void setLayerTextureAndMaterial(KFbxMesh* mesh); + + /// Set Vertices, normals, and UVs + void setControlPointAndNormalsAndUV(const osg::Geode& geo, + MapIndices& index_vert, + bool texcoords, + KFbxMesh* fbxMesh); + + /** + * Add a vertex to the index and link him with the Triangle index and the drawable. + * \param index_vert is the map where the vertices are stored. + * \param index is the indices of the vertices position in the vec3. + * \param drawable_n is the number of the drawable. + * \return the position of the vertices in the final mesh. + */ + unsigned int getMeshIndexForGeometryIndex(MapIndices& index_vert, + unsigned int index, + unsigned int drawable_n); + + /** + * Create the list of faces from the geode. + * \param geo is the geode to study. + * \param listTriangles is the list to fill. + * \param texcoords tell us if we have to treat texture coord. + * \param drawable_n tell us which drawable we are building. + */ + void createListTriangle(const osg::Geometry* geo, + ListTriangle& listTriangles, + bool& texcoords, + unsigned int& drawable_n); + + ///Store the material of the stateset in the MaterialMap. + int processStateSet(const osg::StateSet* stateset); + + typedef std::stack > StateSetStack; + typedef std::map, Material, CompareStateSet> MaterialMap; + + ///We need this for every new Node we create. + KFbxSdkManager* _pSdkManager; + + ///Tell us if the last apply succeed, useful to stop going through the graph. + bool _succeedLastApply; + + ///The current directory. + std::string _directory; + + ///The Scene to save. + KFbxScene* _pScene; + + ///The current Fbx Node. + KFbxNode* _curFbxNode; + + ///The Stack of different stateSet. + StateSetStack _stateSetStack; + + ///The current stateSet. + osg::ref_ptr _currentStateSet; + + ///We store the fbx Materials and Textures in this map. + MaterialMap _materialMap; + ImageSet _imageSet; + ImageFilenameSet _imageFilenameSet; + unsigned int _lastGeneratedImageFileName; + unsigned int _lastMaterialIndex; + unsigned int _lastMeshIndex; + const osgDB::ReaderWriter::Options* _options; + const std::string _srcDirectory; +}; + +#endif // _FBX_WRITER_NODE_VISITOR_HEADER__ diff --git a/src/osgPlugins/fbx/fbxRAnimation.cpp b/src/osgPlugins/fbx/fbxRAnimation.cpp index 01bdeda63..1086a0348 100644 --- a/src/osgPlugins/fbx/fbxRAnimation.cpp +++ b/src/osgPlugins/fbx/fbxRAnimation.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #if defined(_MSC_VER) #pragma warning( disable : 4505 ) @@ -24,8 +23,7 @@ osg::Quat makeQuat(const osg::Vec3& radians, ERotationOrder fbxRotOrder) } void readKeys(KFCurve* curveX, KFCurve* curveY, KFCurve* curveZ, - float scalar, const osg::Vec3& baseValue, bool multiply, - std::vector >& keyFrameCntr) + std::vector >& keyFrameCntr, float scalar = 1.0f) { KFCurve* curves[3] = {curveX, curveY, curveZ}; @@ -58,36 +56,25 @@ void readKeys(KFCurve* curveX, KFCurve* curveY, KFCurve* curveZ, for (TimeSet::iterator it = times.begin(); it != times.end(); ++it) { float fTime = *it; - osg::Vec3 val(baseValue); + osg::Vec3 val; for (int i = 0; i < 3; ++i) { if (curveTimeMap[i].empty()) continue; TimeFloatMap::iterator lb = curveTimeMap[i].lower_bound(fTime); if (lb == curveTimeMap[i].end()) --lb; - if (multiply) - { - val[i] *= lb->second; - } - else - { - val[i] += lb->second; - } + val[i] = lb->second; } keyFrameCntr.push_back(osgAnimation::Vec3Keyframe(fTime, val)); } } osgAnimation::Channel* readFbxChannels(KFCurve* curveX, KFCurve* curveY, - KFCurve* curveZ, const char* targetName, const char* channelName, - float scalar, const osg::Vec3& baseValue, bool multiply) + KFCurve* curveZ, const char* targetName, const char* channelName) { - if (!curveX && !curveY && !curveZ) - { - return 0; - } - - if (!curveX->KeyGetCount() && !curveY->KeyGetCount() && !curveZ->KeyGetCount()) + if (!(curveX && curveX->KeyGetCount()) && + !(curveY && curveY->KeyGetCount()) && + !(curveZ && curveZ->KeyGetCount())) { return 0; } @@ -98,14 +85,14 @@ osgAnimation::Channel* readFbxChannels(KFCurve* curveX, KFCurve* curveY, pChannel->setTargetName(targetName); pChannel->setName(channelName); - readKeys(curveX, curveY, curveZ, scalar, baseValue, multiply, *pKeyFrameCntr); + readKeys(curveX, curveY, curveZ, *pKeyFrameCntr); return pChannel; } osgAnimation::Channel* readFbxChannels( KFbxTypedProperty& fbxProp, const char* pTakeName, - const char* targetName, const char* channelName, const osg::Vec3& baseValue, float scalar, bool multiply) + const char* targetName, const char* channelName) { if (!fbxProp.IsValid()) return 0; @@ -113,15 +100,16 @@ osgAnimation::Channel* readFbxChannels( fbxProp.GetKFCurve("X", pTakeName), fbxProp.GetKFCurve("Y", pTakeName), fbxProp.GetKFCurve("Z", pTakeName), - targetName, channelName, scalar, - baseValue * scalar, multiply); + targetName, channelName); } osgAnimation::Channel* readFbxChannelsQuat( KFCurve* curveX, KFCurve* curveY, KFCurve* curveZ, const char* targetName, - const osg::Quat& baseQuat, ERotationOrder rotOrder) + ERotationOrder rotOrder) { - if (!curveX && !curveY && !curveZ) + if (!(curveX && curveX->KeyGetCount()) && + !(curveY && curveY->KeyGetCount()) && + !(curveZ && curveZ->KeyGetCount())) { return 0; } @@ -131,7 +119,7 @@ osgAnimation::Channel* readFbxChannelsQuat( pChannel->setName("quaternion"); typedef std::vector > KeyFrameCntr; KeyFrameCntr eulerFrameCntr; - readKeys(curveX, curveY, curveZ, static_cast(osg::PI / 180.0), osg::Vec3(0,0,0), false, eulerFrameCntr); + readKeys(curveX, curveY, curveZ, eulerFrameCntr, static_cast(osg::PI / 180.0)); osgAnimation::QuatSphericalLinearSampler::KeyframeContainerType& quatFrameCntr = *pChannel->getOrCreateSampler()->getOrCreateKeyframeContainer(); @@ -142,7 +130,7 @@ osgAnimation::Channel* readFbxChannelsQuat( { const osg::Vec3& euler = it->getValue(); quatFrameCntr.push_back(osgAnimation::QuatKeyframe( - it->getTime(), makeQuat(euler, rotOrder) * baseQuat)); + it->getTime(), makeQuat(euler, rotOrder))); } return pChannel; @@ -152,7 +140,7 @@ osgAnimation::Animation* addChannels( osgAnimation::Channel* pTranslationChannel, osgAnimation::Channel* pRotationChannel, osgAnimation::Channel* pScaleChannel, - osg::ref_ptr &pAnimManager, + osg::ref_ptr& pAnimManager, const char* pTakeName) { if (pTranslationChannel || @@ -189,7 +177,7 @@ osgAnimation::Animation* addChannels( return 0; } -osgAnimation::Animation* readFbxBoneAnimation(KFbxNode* pNode, +osgAnimation::Animation* readFbxAnimation(KFbxNode* pNode, const char* pTakeName, const char* targetName, osg::ref_ptr& pAnimManager) { @@ -199,99 +187,36 @@ osgAnimation::Animation* readFbxBoneAnimation(KFbxNode* pNode, } ERotationOrder rotOrder = pNode->RotationOrder.IsValid() ? pNode->RotationOrder.Get() : eEULER_XYZ; - osg::Quat inverseRot; osgAnimation::Channel* pTranslationChannel = 0; osgAnimation::Channel* pRotationChannel = 0; if (pNode->LclRotation.IsValid()) { - inverseRot = makeQuat(pNode->LclRotation.Get(), rotOrder).inverse(); + fbxDouble3 fbxBaseValue = pNode->LclRotation.Get(); pRotationChannel = readFbxChannelsQuat( pNode->LclRotation.GetKFCurve(KFCURVENODE_R_X, pTakeName), pNode->LclRotation.GetKFCurve(KFCURVENODE_R_Y, pTakeName), pNode->LclRotation.GetKFCurve(KFCURVENODE_R_Z, pTakeName), - targetName, inverseRot, rotOrder); + targetName, rotOrder); } if (pNode->LclTranslation.IsValid()) { - fbxDouble3 fbxBaseValue = pNode->LclTranslation.Get(); - osg::Vec3 offsetTranslation( - -static_cast(fbxBaseValue[0]), - -static_cast(fbxBaseValue[1]), - -static_cast(fbxBaseValue[2])); - pTranslationChannel = readFbxChannels( pNode->LclTranslation.GetKFCurve(KFCURVENODE_T_X, pTakeName), pNode->LclTranslation.GetKFCurve(KFCURVENODE_T_Y, pTakeName), pNode->LclTranslation.GetKFCurve(KFCURVENODE_T_Z, pTakeName), - targetName, "position", 1.0f, offsetTranslation, false); - - if (pTranslationChannel) - { - osgAnimation::Vec3KeyframeContainer& keyFrameCntr = - dynamic_cast( - *pTranslationChannel->getSampler()->getKeyframeContainer()); - - for (int i = 0; i < keyFrameCntr.size(); ++i) - { - keyFrameCntr[i].setValue(inverseRot * keyFrameCntr[i].getValue()); - } - } + targetName, "translate"); } osgAnimation::Channel* pScaleChannel = readFbxChannels( - pNode->LclScaling, pTakeName, targetName, "scale", osg::Vec3(0,0,0), 1.0f, true); + pNode->LclScaling, pTakeName, targetName, "scale"); return addChannels(pTranslationChannel, pRotationChannel, pScaleChannel, pAnimManager, pTakeName); } -osgAnimation::Animation* readFbxAnimation(KFbxNode* pNode, - const char* pTakeName, const char* targetName, - osg::ref_ptr& pAnimManager) -{ - if (!pTakeName) return 0; - - osgAnimation::Channel* pTranslationChannel = readFbxChannels( - pNode->LclTranslation, pTakeName, targetName, "position", osg::Vec3(0,0,0), 1.0f, false); - - //TODO: This will break if there are rotations in more than one of - // Pre/Lcl/Post so really they should each get their own MatrixTransform. - fbxDouble3 fbxPreRot = pNode->PreRotation.Get(); - fbxDouble3 fbxPostRot = pNode->PostRotation.Get(); - osg::Vec3 eulerOffset( - static_cast(fbxPreRot[0] + fbxPostRot[0]), - static_cast(fbxPreRot[1] + fbxPostRot[1]), - static_cast(fbxPreRot[2] + fbxPostRot[2])); - - osgAnimation::Channel* pRotationChannel = readFbxChannels( - pNode->LclRotation, pTakeName, targetName, "euler", eulerOffset, static_cast(osg::PI / 180.0), false); - - osgAnimation::Channel* pScaleChannel = readFbxChannels( - pNode->LclScaling, pTakeName, targetName, "scale", osg::Vec3(1,1,1), 1.0f, true); - - return addChannels(pTranslationChannel, pRotationChannel, pScaleChannel, pAnimManager, pTakeName); -} - -std::string readFbxBoneAnimation(KFbxNode* pNode, - osg::ref_ptr& pAnimManager, - const char* targetName) -{ - std::string result; - for (int i = 1; i < pNode->GetTakeNodeCount(); ++i) - { - const char* pTakeName = pNode->GetTakeNodeName(i); - if (osgAnimation::Animation* pAnimation = readFbxBoneAnimation( - pNode, pTakeName, targetName, pAnimManager)) - { - result = targetName; - } - } - return result; -} - std::string readFbxAnimation(KFbxNode* pNode, osg::ref_ptr& pAnimManager, const char* targetName) diff --git a/src/osgPlugins/fbx/fbxRAnimation.h b/src/osgPlugins/fbx/fbxRAnimation.h index 60a9f152e..60dae7f4a 100644 --- a/src/osgPlugins/fbx/fbxRAnimation.h +++ b/src/osgPlugins/fbx/fbxRAnimation.h @@ -3,11 +3,6 @@ #include -std::string readFbxBoneAnimation( - FBXFILESDK_NAMESPACE::KFbxNode*, - osg::ref_ptr&, - const char* targetName); - std::string readFbxAnimation( FBXFILESDK_NAMESPACE::KFbxNode*, osg::ref_ptr&, diff --git a/src/osgPlugins/fbx/fbxRMesh.cpp b/src/osgPlugins/fbx/fbxRMesh.cpp index 8261dde42..953a87007 100644 --- a/src/osgPlugins/fbx/fbxRMesh.cpp +++ b/src/osgPlugins/fbx/fbxRMesh.cpp @@ -1,3 +1,6 @@ +#include +#include + #include #include #include @@ -132,9 +135,8 @@ FbxT getElement(const KFbxLayerElementTemplate* pLayerElement, typedef std::map > GeometryMap; osg::Geometry* getGeometry(osg::Geode* pGeode, GeometryMap& geometryMap, - const std::vector>& materialList, - const std::vector>& textureList, - GeometryType gt, unsigned mti, bool bNormal, bool bTexCoord, bool bColor) + std::vector& stateSetList, + GeometryType gt, unsigned int mti, bool bNormal, bool bTexCoord, bool bColor) { GeometryMap::iterator it = geometryMap.find(mti); @@ -144,13 +146,7 @@ osg::Geometry* getGeometry(osg::Geode* pGeode, GeometryMap& geometryMap, } osg::ref_ptr pGeometry; - if (gt == GEOMETRY_RIG) - { - osgAnimation::RigGeometry* pRig = new osgAnimation::RigGeometry; - pRig->setInfluenceMap(new osgAnimation::VertexInfluenceMap); - pGeometry = pRig; - } - else if (gt == GEOMETRY_MORPH) + if (gt == GEOMETRY_MORPH) { pGeometry = new osgAnimation::MorphGeometry; } @@ -164,17 +160,16 @@ osg::Geometry* getGeometry(osg::Geode* pGeode, GeometryMap& geometryMap, if (bTexCoord) pGeometry->setTexCoordData(0, osg::Geometry::ArrayData(new osg::Vec2Array, osg::Geometry::BIND_PER_VERTEX)); if (bColor) pGeometry->setColorData(osg::Geometry::ArrayData(new osg::Vec4Array, osg::Geometry::BIND_PER_VERTEX)); - if (mti < materialList.size()) + if (mti < stateSetList.size()) { - pGeometry->getOrCreateStateSet()->setAttributeAndModes(materialList[mti].get()); + const StateSetContent& ss = stateSetList[mti]; + if(ss.first) + pGeometry->getOrCreateStateSet()->setAttributeAndModes(ss.first); + if(ss.second) + pGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, ss.second); } - if (mti < textureList.size()) - { - pGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, textureList[mti].get()); - } - - geometryMap.insert(std::pair>(mti, pGeometry)); + geometryMap.insert(std::pair >(mti, pGeometry)); pGeode->addDrawable(pGeometry.get()); return pGeometry.get(); @@ -195,7 +190,7 @@ osgAnimation::VertexInfluence& getVertexInfluence( void addChannel( osgAnimation::Channel* pChannel, - osg::ref_ptr &pAnimManager, + osg::ref_ptr& pAnimManager, const char* pTakeName) { if (!pChannel) @@ -225,7 +220,7 @@ void addChannel( pAnimation->addChannel(pChannel); } -void readAnimation(KFbxNode* pNode, osg::Geode* pGeode, +void readAnimation(KFbxNode* pNode, const std::string& targetName, osg::ref_ptr& pAnimationManager, KFbxMesh* pMesh, int nShape) { @@ -233,18 +228,21 @@ void readAnimation(KFbxNode* pNode, osg::Geode* pGeode, { const char* pTakeName = pNode->GetTakeNodeName(i); - KFCurve* pCurve = pMesh->GetShapeChannel(nShape, true, pTakeName); - - osgAnimation::FloatLinearChannel* pChannel = new osgAnimation::FloatLinearChannel; - std::vector >& keyFrameCntr = *pChannel->getOrCreateSampler()->getOrCreateKeyframeContainer(); + KFCurve* pCurve = pMesh->GetShapeChannel(nShape, false, pTakeName); + if (!pCurve) + { + continue; + } int nKeys = pCurve->KeyGetCount(); if (!nKeys) { - float fValue = static_cast(pCurve->GetValue() * 0.01); - keyFrameCntr.push_back(osgAnimation::FloatKeyframe(0.0f,fValue)); + continue; } + osgAnimation::FloatLinearChannel* pChannel = new osgAnimation::FloatLinearChannel; + std::vector >& keyFrameCntr = *pChannel->getOrCreateSampler()->getOrCreateKeyframeContainer(); + for (int k = 0; k < nKeys; ++k) { KFCurveKey key = pCurve->KeyGet(k); @@ -253,16 +251,17 @@ void readAnimation(KFbxNode* pNode, osg::Geode* pGeode, keyFrameCntr.push_back(osgAnimation::FloatKeyframe(fTime,fValue)); } - pChannel->setTargetName(pGeode->getName()); - pChannel->setName(pMesh->GetShapeName(nShape)); + pChannel->setTargetName(targetName); + std::stringstream ss; + ss << nShape; + pChannel->setName(ss.str()); addChannel(pChannel, pAnimationManager, pTakeName); } } osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, osg::ref_ptr& pAnimationManager, - const std::vector>& materialList, - const std::vector>& textureList, + std::vector& stateSetList, const char* szName) { GeometryMap geometryMap; @@ -320,7 +319,7 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, int materialIndex = getPolygonIndex(pFbxMaterials, i); osg::Geometry* pGeometry = getGeometry(pGeode, geometryMap, - materialList, textureList, geomType, materialIndex, + stateSetList, geomType, materialIndex, pFbxNormals != 0, pFbxUVs != 0, pFbxColors != 0); osg::Vec3Array* pVertices = static_cast( @@ -346,7 +345,6 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, fbxToOsgVertMap.insert(FbxToOsgVertexMap::value_type(v1, GIPair(pGeometry, pVertices->size() + 1))); fbxToOsgVertMap.insert(FbxToOsgVertexMap::value_type(v2, GIPair(pGeometry, pVertices->size() + 2))); - pVertices->push_back(convertVec3(pFbxVertices[v0])); pVertices->push_back(convertVec3(pFbxVertices[v1])); pVertices->push_back(convertVec3(pFbxVertices[v2])); @@ -385,7 +383,16 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, for (int i = 0; i < pGeode->getNumDrawables(); ++i) { osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry(); - pGeometry->setName(pGeode->getName()); + if (pGeode->getNumDrawables() > 1) + { + std::stringstream ss; + ss << pGeode->getName() << " " << i + 1; + pGeometry->setName(ss.str()); + } + else + { + pGeometry->setName(pGeode->getName()); + } osg::DrawArrays* pDrawArrays = new osg::DrawArrays( GL_TRIANGLES, 0, pGeometry->getVertexArray()->getNumElements()); @@ -394,6 +401,26 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, if (geomType == GEOMETRY_RIG) { + typedef std::map, + osg::ref_ptr > GeometryRigGeometryMap; + GeometryRigGeometryMap old2newGeometryMap; + + for (int i = 0; i < pGeode->getNumDrawables(); ++i) + { + osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry(); + osgAnimation::RigGeometry* pRig = new osgAnimation::RigGeometry; + pRig->setSourceGeometry(pGeometry); + pRig->copyFrom(*pGeometry); + old2newGeometryMap.insert(GeometryRigGeometryMap::value_type( + pGeometry, pRig)); + pRig->setDataVariance(osg::Object::DYNAMIC); + pRig->setUseDisplayList( false ); + pGeode->setDrawable(i, pRig); + + pRig->setInfluenceMap(new osgAnimation::VertexInfluenceMap); + pGeometry = pRig; + } + for (int i = 0; i < nDeformerCount; ++i) { KFbxSkin* pSkin = (KFbxSkin*)fbxMesh->GetDeformer(i, KFbxDeformer::eSKIN); @@ -418,9 +445,13 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, it->first == fbxIndex; ++it) { GIPair gi = it->second; - osgAnimation::RigGeometry& rig = dynamic_cast(*gi.first); - osgAnimation::VertexInfluenceMap& vim = *rig.getInfluenceMap(); - osgAnimation::VertexInfluence& vi = getVertexInfluence(vim, pBone->GetName()); + osgAnimation::RigGeometry& rig = + dynamic_cast( + *old2newGeometryMap[gi.first]); + osgAnimation::VertexInfluenceMap& vim = + *rig.getInfluenceMap(); + osgAnimation::VertexInfluence& vi = + getVertexInfluence(vim, pBone->GetName()); vi.push_back(osgAnimation::VertexIndexWeight( gi.second, weight)); } @@ -430,15 +461,14 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, } else if (geomType == GEOMETRY_MORPH) { - pGeode->addUpdateCallback(new osgAnimation::UpdateMorph(pGeode->getName())); - - for (int i = 0; i < pGeode->getNumDrawables(); ++i) { osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry(); osgAnimation::MorphGeometry& morph = dynamic_cast(*pGeometry); + pGeode->addUpdateCallback(new osgAnimation::UpdateMorph(morph.getName())); + //read morph geometry for (int j = 0; j < nMorphShapeCount; ++j) { @@ -463,11 +493,9 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, } } pMorphTarget->setName(fbxMesh->GetShapeName(j)); - KFCurve* pCurve = fbxMesh->GetShapeChannel(j); - double defaultWeight = pCurve->GetValue() * 0.01; - morph.addMorphTarget(pMorphTarget, static_cast(defaultWeight)); + morph.addMorphTarget(pMorphTarget, 0.0f); - readAnimation(pNode, pGeode, pAnimationManager, fbxMesh, j); + readAnimation(pNode, morph.getName(), pAnimationManager, fbxMesh, j); } } @@ -534,8 +562,7 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, osgDB::ReaderWriter::ReadResult readFbxMesh(KFbxNode* pNode, osg::ref_ptr& pAnimationManager, - const std::vector>& materialList, - const std::vector>& textureList) + std::vector& stateSetList) { KFbxMesh* lMesh = dynamic_cast(pNode->GetNodeAttribute()); @@ -544,5 +571,5 @@ osgDB::ReaderWriter::ReadResult readFbxMesh(KFbxNode* pNode, return osgDB::ReaderWriter::ReadResult::ERROR_IN_READING_FILE; } - return readMesh(pNode, lMesh, pAnimationManager, materialList, textureList, pNode->GetName()); + return readMesh(pNode, lMesh, pAnimationManager, stateSetList, pNode->GetName()); } diff --git a/src/osgPlugins/fbx/fbxRMesh.h b/src/osgPlugins/fbx/fbxRMesh.h index 20a9d5c95..f2d73aa29 100644 --- a/src/osgPlugins/fbx/fbxRMesh.h +++ b/src/osgPlugins/fbx/fbxRMesh.h @@ -4,11 +4,10 @@ #include #include #include - +#include "fbxMaterialToOsgStateSet.h" osgDB::ReaderWriter::ReadResult readFbxMesh( FBXFILESDK_NAMESPACE::KFbxNode* pNode, osg::ref_ptr& pAnimationManager, - const std::vector>&, - const std::vector>&); + std::vector &); #endif diff --git a/src/osgPlugins/fbx/fbxRNode.cpp b/src/osgPlugins/fbx/fbxRNode.cpp index cd48f97ce..9ab9b04da 100644 --- a/src/osgPlugins/fbx/fbxRNode.cpp +++ b/src/osgPlugins/fbx/fbxRNode.cpp @@ -1,5 +1,8 @@ +#include #include +#include +#include #include #include #include @@ -9,8 +12,13 @@ #include #include +#include #include -#include +#include +#include +#include +#include +#include #if defined(_MSC_VER) #pragma warning( disable : 4505 ) @@ -21,140 +29,8 @@ #include "fbxRCamera.h" #include "fbxRLight.h" #include "fbxRMesh.h" - -template -class FbxToOsgMap -{ - std::map> m_map; -public: - ConvertFunc m_convertFunc; - - FbxToOsgMap(ConvertFunc convertFunc) : m_convertFunc(convertFunc) {} - - osg::ref_ptr Get(const FbxT* fbx) - { - if (!fbx) - return 0; - std::map>::iterator it = m_map.find(fbx); - if (it != m_map.end()) - { - return it->second; - } - osg::ref_ptr osgObj = m_convertFunc(fbx); - m_map.insert(std::pair>(fbx, osgObj)); - return osgObj; - } -}; - -struct GetOsgTexture -{ - const std::string& m_dir; - - GetOsgTexture(const std::string& dir) : m_dir(dir) {} - - static osg::Texture::WrapMode convertWrap(KFbxTexture::EWrapMode wrap) - { - return wrap == KFbxTexture::eREPEAT ? - osg::Texture2D::REPEAT : osg::Texture2D::CLAMP_TO_EDGE; - } - - osg::ref_ptr operator () (const KFbxTexture* fbx) - { - osg::Image* pImage; - if ((pImage = osgDB::readImageFile(osgDB::concatPaths(m_dir, fbx->GetRelativeFileName()))) || - (pImage = osgDB::readImageFile(osgDB::concatPaths(m_dir, fbx->GetFileName())))) - { - osg::ref_ptr pOsgTex = new osg::Texture2D; - - pOsgTex->setImage(pImage); - pOsgTex->setWrap(osg::Texture2D::WRAP_S, convertWrap(fbx->GetWrapModeU())); - pOsgTex->setWrap(osg::Texture2D::WRAP_T, convertWrap(fbx->GetWrapModeV())); - - return pOsgTex; - } - else - { - return 0; - } - } -}; - -struct GetOsgMaterial -{ - typedef FbxToOsgMap TextureMap; - TextureMap m_textureMap; - -public: - osg::ref_ptr m_pTexture; - - GetOsgMaterial(const std::string& dir) : m_textureMap(GetOsgTexture(dir)){} - - osg::ref_ptr operator () (const KFbxSurfaceMaterial* pFbxMat) - { - osg::ref_ptr pOsgMat = new osg::Material; - - const KFbxSurfaceLambert* pFbxLambert = dynamic_cast(pFbxMat); - - const KFbxProperty lProperty = pFbxMat->FindProperty(KFbxSurfaceMaterial::sDiffuse); - if(lProperty.IsValid()){ - int lNbTex = lProperty.GetSrcObjectCount(KFbxTexture::ClassId); - for (int lTextureIndex = 0; lTextureIndex < lNbTex; lTextureIndex++) - { - const KFbxTexture* lTexture = KFbxCast(lProperty.GetSrcObject(KFbxTexture::ClassId, lTextureIndex)); - if(lTexture) - { - m_pTexture = m_textureMap.Get(lTexture); - } - - //For now only allow 1 texture - break; - } - } - - if (pFbxLambert) - { - fbxDouble3 color = pFbxLambert->GetDiffuseColor().Get(); - double factor = pFbxLambert->GetDiffuseFactor().Get(); - pOsgMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4( - static_cast(color[0] * factor), - static_cast(color[1] * factor), - static_cast(color[2] * factor), - static_cast(1.0 - pFbxLambert->GetTransparencyFactor().Get()))); - - color = pFbxLambert->GetAmbientColor().Get(); - factor = pFbxLambert->GetAmbientFactor().Get(); - pOsgMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4( - static_cast(color[0] * factor), - static_cast(color[1] * factor), - static_cast(color[2] * factor), - 1.0f)); - - color = pFbxLambert->GetEmissiveColor().Get(); - factor = pFbxLambert->GetEmissiveFactor().Get(); - pOsgMat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4( - static_cast(color[0] * factor), - static_cast(color[1] * factor), - static_cast(color[2] * factor), - 1.0f)); - - if (const KFbxSurfacePhong* pFbxPhong = dynamic_cast(pFbxLambert)) - { - color = pFbxPhong->GetSpecularColor().Get(); - factor = pFbxPhong->GetSpecularFactor().Get(); - pOsgMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4( - static_cast(color[0] * factor), - static_cast(color[1] * factor), - static_cast(color[2] * factor), - 1.0f)); - - pOsgMat->setShininess(osg::Material::FRONT_AND_BACK, - static_cast(pFbxPhong->GetShininess().Get())); - } - } - - return pOsgMat; - } -}; +#include "fbxRNode.h" +#include "fbxMaterialToOsgStateSet.h" osg::Quat makeQuat(const fbxDouble3& degrees, ERotationOrder fbxRotOrder) { @@ -259,69 +135,8 @@ void makeLocalMatrix(const KFbxNode* pNode, osg::Matrix& m) -fbxSclPiv[2])); } -void getApproximateTransform(const KFbxNode* pNode, osg::Vec3& trans, osg::Quat& quat, osg::Vec3& scale) -{ - ERotationOrder fbxRotOrder = pNode->RotationOrder.Get(); - - fbxDouble3 fbxLclPos = pNode->LclTranslation.Get(); - //fbxDouble3 fbxRotOff = pNode->RotationOffset.Get(); - //fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get(); - fbxDouble3 fbxPreRot = pNode->PreRotation.Get(); - fbxDouble3 fbxLclRot = pNode->LclRotation.Get(); - fbxDouble3 fbxPostRot = pNode->PostRotation.Get(); - //fbxDouble3 fbxSclOff = pNode->ScalingOffset.Get(); - //fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get(); - fbxDouble3 fbxLclScl = pNode->LclScaling.Get(); - - trans.set( - static_cast(fbxLclPos[0]), - static_cast(fbxLclPos[1]), - static_cast(fbxLclPos[2])); - - quat = - makeQuat(fbxPostRot, fbxRotOrder) * - makeQuat(fbxLclRot, fbxRotOrder) * - makeQuat(fbxPreRot, fbxRotOrder); - - scale.set( - static_cast(fbxLclScl[0]), - static_cast(fbxLclScl[1]), - static_cast(fbxLclScl[2])); -} - -void getApproximateTransform(const KFbxNode* pNode, osg::Vec3& trans, osg::Vec3& euler, osg::Vec3& scale) -{ - //ERotationOrder fbxRotOrder = pNode->RotationOrder.Get(); - - fbxDouble3 fbxLclPos = pNode->LclTranslation.Get(); - //fbxDouble3 fbxRotOff = pNode->RotationOffset.Get(); - //fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get(); - fbxDouble3 fbxPreRot = pNode->PreRotation.Get(); - fbxDouble3 fbxLclRot = pNode->LclRotation.Get(); - fbxDouble3 fbxPostRot = pNode->PostRotation.Get(); - //fbxDouble3 fbxSclOff = pNode->ScalingOffset.Get(); - //fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get(); - fbxDouble3 fbxLclScl = pNode->LclScaling.Get(); - - trans.set( - static_cast(fbxLclPos[0]), - static_cast(fbxLclPos[1]), - static_cast(fbxLclPos[2])); - - //TODO: Convert each rotation to a quaternion, concatenate them and extract euler from that. - euler.set( - osg::DegreesToRadians(static_cast(fbxPreRot[0] + fbxLclRot[0] + fbxPostRot[0])), - osg::DegreesToRadians(static_cast(fbxPreRot[1] + fbxLclRot[1] + fbxPostRot[1])), - osg::DegreesToRadians(static_cast(fbxPreRot[2] + fbxLclRot[2] + fbxPostRot[2]))); - - scale.set( - static_cast(fbxLclScl[0]), - static_cast(fbxLclScl[1]), - static_cast(fbxLclScl[2])); -} - bool readBindPose(KFbxSdkManager& pManager, KFbxNode* pNode, - osgAnimation::Bone* osgBone) + osgAnimation::Bone* osgBone) { KArrayTemplate pPoseList; KArrayTemplate pIndex; @@ -332,10 +147,129 @@ bool readBindPose(KFbxSdkManager& pManager, KFbxNode* pNode, } const double* pMat = pPoseList[0]->GetMatrix(pIndex[0]); - osgBone->setBindMatrixInBoneSpace(osg::Matrix(pMat)); + osgBone->setMatrix(osg::Matrix(pMat)); return true; } +void readTranslationElement(KFbxTypedProperty& prop, + osgAnimation::UpdateMatrixTransform* pUpdate, + osg::Matrix& staticTransform) +{ + fbxDouble3 fbxPropValue = prop.Get(); + osg::Vec3d val( + fbxPropValue[0], + fbxPropValue[1], + fbxPropValue[2]); + + if (prop.GetKFCurve(KFCURVENODE_T_X) || + prop.GetKFCurve(KFCURVENODE_T_Y) || + prop.GetKFCurve(KFCURVENODE_T_Z)) + { + if (!staticTransform.isIdentity()) + { + pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform)); + staticTransform.makeIdentity(); + } + pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("translate", val)); + } + else + { + staticTransform.preMultTranslate(val); + } +} + +void readRotationElement(KFbxTypedProperty& prop, + ERotationOrder fbxRotOrder, + osgAnimation::UpdateMatrixTransform* pUpdate, + osg::Matrix& staticTransform) +{ + osg::Quat quat = makeQuat(prop.Get(), fbxRotOrder); + + if (prop.GetKFCurve(KFCURVENODE_R_X) || + prop.GetKFCurve(KFCURVENODE_R_Y) || + prop.GetKFCurve(KFCURVENODE_R_Z)) + { + if (!staticTransform.isIdentity()) + { + pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform)); + staticTransform.makeIdentity(); + } + pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedQuaternionElement("quaternion", quat)); + } + else + { + staticTransform.preMultRotate(quat); + } +} + +void readScaleElement(KFbxTypedProperty& prop, + osgAnimation::UpdateMatrixTransform* pUpdate, + osg::Matrix& staticTransform) +{ + fbxDouble3 fbxPropValue = prop.Get(); + osg::Vec3d val( + fbxPropValue[0], + fbxPropValue[1], + fbxPropValue[2]); + + if (prop.GetKFCurve(KFCURVENODE_S_X) || + prop.GetKFCurve(KFCURVENODE_S_Y) || + prop.GetKFCurve(KFCURVENODE_S_Z)) + { + if (!staticTransform.isIdentity()) + { + pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform)); + staticTransform.makeIdentity(); + } + pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedScaleElement("scale", val)); + } + else + { + staticTransform.preMultScale(val); + } +} + +void readUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform* pUpdate, KFbxNode* pNode) +{ + osg::Matrix staticTransform; + + readTranslationElement(pNode->LclTranslation, pUpdate, staticTransform); + + fbxDouble3 fbxRotOffset = pNode->RotationOffset.Get(); + fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get(); + staticTransform.preMultTranslate(osg::Vec3d( + fbxRotPiv[0] + fbxRotOffset[0], + fbxRotPiv[1] + fbxRotOffset[1], + fbxRotPiv[2] + fbxRotOffset[2])); + + ERotationOrder fbxRotOrder = pNode->RotationOrder.IsValid() ? pNode->RotationOrder.Get() : eEULER_XYZ; + + staticTransform.preMultRotate(makeQuat(pNode->PreRotation.Get(), fbxRotOrder)); + + readRotationElement(pNode->LclRotation, fbxRotOrder, pUpdate, staticTransform); + + staticTransform.preMultRotate(makeQuat(pNode->PostRotation.Get(), fbxRotOrder)); + + fbxDouble3 fbxSclOffset = pNode->ScalingOffset.Get(); + fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get(); + staticTransform.preMultTranslate(osg::Vec3d( + fbxSclOffset[0] + fbxSclPiv[0] - fbxRotPiv[0], + fbxSclOffset[1] + fbxSclPiv[1] - fbxRotPiv[1], + fbxSclOffset[2] + fbxSclPiv[2] - fbxRotPiv[2])); + + readScaleElement(pNode->LclScaling, pUpdate, staticTransform); + + staticTransform.preMultTranslate(osg::Vec3d( + -fbxSclPiv[0], + -fbxSclPiv[1], + -fbxSclPiv[2])); + + if (!staticTransform.isIdentity()) + { + pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform)); + } +} + osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode, const std::string& animName, const osg::Matrix& localMatrix, bool bNeedSkeleton) { @@ -344,7 +278,9 @@ osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode, osgAnimation::Bone* osgBone = new osgAnimation::Bone; osgBone->setDataVariance(osg::Object::DYNAMIC); osgBone->setName(pNode->GetName()); - osgBone->setDefaultUpdateCallback(animName); + osgAnimation::UpdateBone* pUpdate = new osgAnimation::UpdateBone(animName); + readUpdateMatrixTransform(pUpdate, pNode); + osgBone->setUpdateCallback(pUpdate); readBindPose(pSdkManager, pNode, osgBone); @@ -362,18 +298,14 @@ osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode, osg::MatrixTransform* pTransform = new osg::MatrixTransform(localMatrix); pTransform->setName(pNode->GetName()); + if (bAnimated) { - osgAnimation::UpdateTransform* pUpdate = new osgAnimation::UpdateTransform(animName); - - osg::Vec3 trans, rot, scale; - getApproximateTransform(pNode, trans, rot, scale); - - pUpdate->getPosition()->setValue(trans); - pUpdate->getEuler()->setValue(rot); - pUpdate->getScale()->setValue(scale); + osgAnimation::UpdateMatrixTransform* pUpdate = new osgAnimation::UpdateMatrixTransform(animName); + readUpdateMatrixTransform(pUpdate, pNode); pTransform->setUpdateCallback(pUpdate); } + return pTransform; } } @@ -381,7 +313,9 @@ osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode, osgDB::ReaderWriter::ReadResult readFbxNode( KFbxSdkManager& pSdkManager, KFbxNode* pNode, osg::ref_ptr& pAnimationManager, - const std::string& dir, bool& bNeedSkeleton, int& nLightCount) + bool& bNeedSkeleton, int& nLightCount, + FbxMaterialToOsgStateSet& fbxMaterialToOsgStateSet, + const osgDB::Options* options) { if (KFbxNodeAttribute* lNodeAttribute = pNode->GetNodeAttribute()) { @@ -404,17 +338,13 @@ osgDB::ReaderWriter::ReadResult readFbxNode( } unsigned nMaterials = pNode->GetMaterialCount(); - std::vector> materialList; - std::vector> textureList; - materialList.reserve(nMaterials); - - typedef FbxToOsgMap MaterialMap; - MaterialMap materialMap(dir); + std::vector stateSetList; for (unsigned i = 0; i < nMaterials; ++i) { - materialList.push_back(materialMap.Get(pNode->GetMaterial(i))); - textureList.push_back(materialMap.m_convertFunc.m_pTexture); + KFbxSurfaceMaterial* fbxMaterial = pNode->GetMaterial(i); + assert(fbxMaterial); + stateSetList.push_back(fbxMaterialToOsgStateSet.convert(fbxMaterial)); } osg::NodeList skeletal, children; @@ -432,8 +362,8 @@ osgDB::ReaderWriter::ReadResult readFbxNode( bool bChildNeedSkeleton = false; osgDB::ReaderWriter::ReadResult childResult = readFbxNode( - pSdkManager, pChildNode, pAnimationManager, dir, - bChildNeedSkeleton, nLightCount); + pSdkManager, pChildNode, pAnimationManager, + bChildNeedSkeleton, nLightCount, fbxMaterialToOsgStateSet, options); if (childResult.error()) { return childResult; @@ -452,17 +382,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode( } } - std::string animName; - - if (bNeedSkeleton) - { - animName = readFbxBoneAnimation(pNode, pAnimationManager, - pNode->GetName()); - } - else - { - animName = readFbxAnimation(pNode, pAnimationManager, pNode->GetName()); - } + std::string animName = readFbxAnimation(pNode, pAnimationManager, pNode->GetName()); osg::Matrix localMatrix; makeLocalMatrix(pNode, localMatrix); @@ -475,7 +395,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode( switch (lAttributeType) { case KFbxNodeAttribute::eUNIDENTIFIED: - if (children.size() + skeletal.size() == 1) + if (bLocalMatrixIdentity && children.size() + skeletal.size() == 1) { if (children.size() == 1) { @@ -490,7 +410,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode( case KFbxNodeAttribute::eMESH: { osgDB::ReaderWriter::ReadResult meshRes = readFbxMesh(pNode, - pAnimationManager, materialList, textureList); + pAnimationManager, stateSetList); if (meshRes.error()) { return meshRes; @@ -506,6 +426,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode( return osgDB::ReaderWriter::ReadResult(node); } osgGroup = createGroupNode(pSdkManager, pNode, animName, localMatrix, bNeedSkeleton); + assert(osgGroup->getStateSet() == NULL); osgGroup->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON); osgGroup->addChild(node); diff --git a/src/osgPlugins/fbx/fbxRNode.h b/src/osgPlugins/fbx/fbxRNode.h index a00ad2afe..225c8cd8a 100644 --- a/src/osgPlugins/fbx/fbxRNode.h +++ b/src/osgPlugins/fbx/fbxRNode.h @@ -1,6 +1,7 @@ #ifndef FBXRNODE_H #define FBXRNODE_H +#include "fbxMaterialToOsgStateSet.h" namespace osgAnimation { class AnimationManagerBase; @@ -10,8 +11,9 @@ osgDB::ReaderWriter::ReadResult readFbxNode( FBXFILESDK_NAMESPACE::KFbxSdkManager& pSdkManager, FBXFILESDK_NAMESPACE::KFbxNode* pNode, osg::ref_ptr& pAnimationManager, - const std::string& dir, bool& bNeedSkeleton, - int& nLightCount); + int& nLightCount, + FbxMaterialToOsgStateSet& fbxMaterialToOsgStateSet, + const osgDB::Options* options = NULL); #endif