root/OpenSceneGraph/trunk/src/osgPlugins/fbx/ReaderWriterFBX.cpp @ 11153

Revision 11153, 15.7 kB (checked in by mplatings, 5 years ago)

Fix for some FBX files with multiple meshes bound to a bone.

RevLine 
[10780]1#include <sstream>
2#include <memory>
[11139]3#include <cassert>
[10780]4
5#include <osg/Notify>
6#include <osg/MatrixTransform>
7#include <osg/Material>
[11109]8#include <osg/PositionAttitudeTransform>
[10780]9#include <osg/Texture2D>
[11109]10#include <osgDB/ConvertUTF>
[10780]11#include <osgDB/FileNameUtils>
12#include <osgDB/FileUtils>
13#include <osgDB/ReadFile>
14#include <osgDB/Registry>
15#include <osgAnimation/AnimationManagerBase>
[11109]16#include <osgAnimation/Bone>
[11153]17#include <osgAnimation/RigGeometry>
[10780]18#include <osgAnimation/Skeleton>
[11153]19#include <osgAnimation/VertexInfluence>
[10780]20
21#if defined(_MSC_VER)
22    #pragma warning( disable : 4505 )
23#endif
24#include <fbxsdk.h>
25
26#include "ReaderWriterFBX.h"
27#include "fbxRNode.h"
[11109]28#include "fbxMaterialToOsgStateSet.h"
29#include "WriterNodeVisitor.h"
[10780]30
[11109]31/// Returns true if the given node is a basic root group with no special information.
32/// Used in conjunction with UseFbxRoot option.
33/// Identity transforms are considered as basic root nodes.
34bool isBasicRootNode(const osg::Node& node)
35{
36    const osg::Group* osgGroup = node.asGroup();
37    if (!osgGroup)
38    {
39        // Geodes & such are not basic root nodes
40        return false;
41    }
42
43    // Test if we've got an empty transform (= a group!)
44    const osg::Transform* transform = osgGroup->asTransform();
45    if (transform)
46    {
47        if (const osg::MatrixTransform* matrixTransform = transform->asMatrixTransform())
48        {
49            if (!matrixTransform->getMatrix().isIdentity())
50            {
51                // Non-identity matrix transform
52                return false;
53            }
54        }
55        else if (const osg::PositionAttitudeTransform* pat = transform->asPositionAttitudeTransform())
56        {
57            if (pat->getPosition() != osg::Vec3d() ||
58                pat->getAttitude() != osg::Quat() ||
59                pat->getScale() != osg::Vec3d(1.0f, 1.0f, 1.0f) ||
60                pat->getPivotPoint() != osg::Vec3d())
61            {
62                // Non-identity position attribute transform
63                return false;
64            }
65        }
66        else
67        {
68            // Other transform (not identity or not predefined type)
69            return false;
70        }
71    }
72
73    // Test the presence of a non-empty stateset
74    if (node.getStateSet())
75    {
76        osg::ref_ptr<osg::StateSet> emptyStateSet = new osg::StateSet;
77        if (node.getStateSet()->compare(*emptyStateSet, true) != 0)
78        {
79            return false;
80        }
81    }
82
83    return true;
84}
85
86
87class CleanUpFbx
88{
89    KFbxSdkManager* m_pSdkManager;
90public:
91    explicit CleanUpFbx(KFbxSdkManager* pSdkManager) : m_pSdkManager(pSdkManager)
92    {}
93
94    ~CleanUpFbx()
95    {
96        KFbxIOSettings::IOSettingsRef().FreeIOSettings();
97        m_pSdkManager->Destroy();
98    }
99};
100
[11153]101void resolveBindMatrices(
102    osg::Node& root,
103    const BindMatrixMap& boneBindMatrices,
104    const std::map<KFbxNode*, osg::Node*>& nodeMap)
105{
106    std::set<std::string> nodeNames;
107    for (std::map<KFbxNode*, osg::Node*>::const_iterator it = nodeMap.begin(); it != nodeMap.end(); ++it)
108    {
109        nodeNames.insert(it->second->getName());
110    }
111
112    for (BindMatrixMap::const_iterator it = boneBindMatrices.begin();
113        it != boneBindMatrices.end();)
114    {
115        KFbxNode* const fbxBone = it->first.first;
116        std::map<KFbxNode*, osg::Node*>::const_iterator nodeIt = nodeMap.find(fbxBone);
117        if (nodeIt != nodeMap.end())
118        {
119            const osg::Matrix bindMatrix = it->second;
120            osgAnimation::Bone& osgBone = dynamic_cast<osgAnimation::Bone&>(*nodeIt->second);
121            osgBone.setInvBindMatrixInSkeletonSpace(bindMatrix);
122
123            ++it;
124            for (; it != boneBindMatrices.end() && it->first.first == fbxBone; ++it)
125            {
126                if (it->second != bindMatrix)
127                {
128                    std::string name;
129                    for (int i = 0;; ++i)
130                    {
131                        std::stringstream ss;
132                        ss << osgBone.getName() << '_' << i;
133                        name = ss.str();
134                        if (nodeNames.insert(name).second)
135                        {
136                            break;
137                        }
138                    }
139                    osgAnimation::Bone* newBone = new osgAnimation::Bone(name);
140                    newBone->setDefaultUpdateCallback();
141                    newBone->setInvBindMatrixInSkeletonSpace(it->second);
142                    osgBone.addChild(newBone);
143
144                    osgAnimation::RigGeometry* pRigGeometry = it->first.second;
145                   
146                    osgAnimation::VertexInfluenceMap* vertexInfluences = pRigGeometry->getInfluenceMap();
147
148                    osgAnimation::VertexInfluenceMap::iterator vimIt = vertexInfluences->find(osgBone.getName());
149                    if (vimIt != vertexInfluences->end())
150                    {
151                        osgAnimation::VertexInfluence vi;
152                        vi.swap(vimIt->second);
153                        vertexInfluences->erase(vimIt);
154                        osgAnimation::VertexInfluence& vi2 = (*vertexInfluences)[name];
155                        vi.swap(vi2);
156                        vi2.setName(name);
157                    }
158                    else
159                    {
160                        assert(0);
161                    }
162                }
163            }
164        }
165        else
166        {
167            assert(0);
168        }
169    }
170}
171
[10780]172osgDB::ReaderWriter::ReadResult
[11109]173ReaderWriterFBX::readNode(const std::string& filenameInit,
174                          const Options* options) const
[10780]175{
176    try
177    {
[11109]178        std::string ext(osgDB::getLowerCaseFileExtension(filenameInit));
179        if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
[10780]180
[11109]181        std::string filename(osgDB::findDataFile(filenameInit, options));
182        if (filename.empty()) return ReadResult::FILE_NOT_FOUND;
[10780]183
184        KFbxSdkManager* pSdkManager = KFbxSdkManager::Create();
185
186        if (!pSdkManager)
187        {
188            return ReadResult::ERROR_IN_READING_FILE;
189        }
190
[11109]191        CleanUpFbx cleanUpFbx(pSdkManager);
[10780]192
[11109]193        KFbxScene* pScene = KFbxScene::Create(pSdkManager, "");
[10780]194
[11109]195        // The FBX SDK interprets the filename as UTF-8
196#ifdef OSG_USE_UTF8_FILENAME
197        const std::string& utf8filename(filename);
198#else
[11141]199        std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
[11109]200#endif
[10780]201
202        int fileFormat;
203        if (!pSdkManager->GetIOPluginRegistry()->DetectFileFormat(utf8filename.c_str(), fileFormat))
204        {
205            return ReadResult::FILE_NOT_HANDLED;
206        }
[11109]207        KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager, "");
[10780]208
[11139]209        if (!lImporter->Initialize(utf8filename.c_str(), fileFormat))
[10780]210        {
211            return std::string(lImporter->GetLastErrorString());
212        }
213
214        if (!lImporter->IsFBX())
215        {
216            return ReadResult::ERROR_IN_READING_FILE;
217        }
218
[11109]219        for (int i = 0; i < lImporter->GetTakeCount(); i++)
[10780]220        {
221            KFbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i);
222
223            lTakeInfo->mSelect = true;
224        }
225
226        if (!lImporter->Import(pScene))
227        {
228            return std::string(lImporter->GetLastErrorString());
229        }
230
[11109]231        //KFbxAxisSystem::OpenGL.ConvertScene(pScene);        // Doesn't work as expected. Still need to transform vertices.
232
[10780]233        if (KFbxNode* pNode = pScene->GetRootNode())
234        {
[11141]235            pScene->SetCurrentTake(pScene->GetCurrentTakeName());
[11139]236
[11141]237            bool useFbxRoot = false;
[11109]238            if (options)
239            {
240                std::istringstream iss(options->getOptionString());
241                std::string opt;
242                while (iss >> opt)
243                {
244                    if (opt == "UseFbxRoot")
245                    {
246                        useFbxRoot = true;
247                    }
248                }
249            }
250
[10780]251            osg::ref_ptr<osgAnimation::AnimationManagerBase> pAnimationManager;
[11139]252            bool bIsBone = false;
[10780]253            int nLightCount = 0;
[11109]254            osg::ref_ptr<Options> localOptions = NULL;
255            if (options)
256                localOptions = options->cloneOptions();
257            else
258                localOptions = new osgDB::Options();
259            localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_IMAGES);
260
261            std::string filePath = osgDB::getFilePath(filename);
262            FbxMaterialToOsgStateSet fbxMaterialToOsgStateSet(filePath, localOptions.get());
[11139]263
[11141]264            std::map<KFbxNode*, osg::Node*> nodeMap;
[11153]265            BindMatrixMap boneBindMatrices;
[11141]266            std::map<KFbxNode*, osgAnimation::Skeleton*> skeletonMap;
[10780]267            ReadResult res = readFbxNode(*pSdkManager, pNode, pAnimationManager,
[11139]268                bIsBone, nLightCount, fbxMaterialToOsgStateSet, nodeMap,
[11141]269                boneBindMatrices, skeletonMap, localOptions.get());
[11109]270
[10780]271            if (res.success())
272            {
[11153]273                resolveBindMatrices(*res.getNode(), boneBindMatrices, nodeMap);
[11139]274
[11141]275                osg::Node* osgNode = res.getNode();
276                osgNode->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON);
277                osgNode->getOrCreateStateSet()->setMode(GL_NORMALIZE,osg::StateAttribute::ON);
[11139]278
[10780]279                if (pAnimationManager.valid())
280                {
281                    if (osgNode->getUpdateCallback())
282                    {
283                        osg::Group* osgGroup = new osg::Group;
284                        osgGroup->addChild(osgNode);
285                        osgNode = osgGroup;
286                    }
287
288                    //because the animations may be altered after registering
289                    pAnimationManager->buildTargetReference();
290                    osgNode->setUpdateCallback(pAnimationManager.get());
291                }
292
293                KFbxAxisSystem fbxAxis = pScene->GetGlobalSettings().GetAxisSystem();
[11109]294
295                if (fbxAxis != KFbxAxisSystem::OpenGL)
[10780]296                {
[11109]297                    int upSign;
298                    KFbxAxisSystem::eUpVector eUp = fbxAxis.GetUpVector(upSign);
299                    bool bLeftHanded = fbxAxis.GetCoorSystem() == KFbxAxisSystem::LeftHanded;
[10780]300                    float fSign = upSign < 0 ? -1.0f : 1.0f;
301                    float zScale = bLeftHanded ? -1.0f : 1.0f;
302
303                    osg::Matrix mat;
304                    switch (eUp)
305                    {
306                    case KFbxAxisSystem::XAxis:
307                        mat.set(0,fSign,0,0,-fSign,0,0,0,0,0,zScale,0,0,0,0,1);
308                        break;
309                    case KFbxAxisSystem::YAxis:
310                        mat.set(1,0,0,0,0,fSign,0,0,0,0,fSign*zScale,0,0,0,0,1);
311                        break;
312                    case KFbxAxisSystem::ZAxis:
313                        mat.set(1,0,0,0,0,0,-fSign*zScale,0,0,fSign,0,0,0,0,0,1);
314                        break;
315                    }
[11109]316
317                    osg::Transform* pTransformTemp = osgNode->asTransform();
318                    osg::MatrixTransform* pMatrixTransform = pTransformTemp ?
319                        pTransformTemp->asMatrixTransform() : NULL;
320                    if (pMatrixTransform)
321                    {
322                        pMatrixTransform->setMatrix(pMatrixTransform->getMatrix() * mat);
323                    }
324                    else
325                    {
326                        pMatrixTransform = new osg::MatrixTransform(mat);
327                        if (useFbxRoot && isBasicRootNode(*osgNode))
328                        {
329                            // If root node is a simple group, put all FBX elements under the OSG root
330                            osg::Group* osgGroup = osgNode->asGroup();
331                            for(unsigned int i = 0; i < osgGroup->getNumChildren(); ++i)
332                            {
333                                pMatrixTransform->addChild(osgGroup->getChild(i));
334                            }
335                            pMatrixTransform->setName(osgGroup->getName());
336                        }
337                        else
338                        {
339                            pMatrixTransform->addChild(osgNode);
340                        }
341                    }
342                    osgNode = pMatrixTransform;
[10780]343                }
344
[11109]345                osgNode->setName(filenameInit);
[10780]346                return osgNode;
347            }
348        }
349    }
350    catch (...)
351    {
[11141]352        osg::notify(osg::WARN) << "Exception thrown while importing \"" << filenameInit << '\"' << std::endl;
[10780]353    }
354
355    return ReadResult::ERROR_IN_READING_FILE;
356}
357
[11109]358osgDB::ReaderWriter::WriteResult ReaderWriterFBX::writeNode(
359    const osg::Node& node,
360    const std::string& filename,
361    const Options* options) const
362{
363    try
364    {
365        std::string ext = osgDB::getLowerCaseFileExtension(filename);
366        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
367
368        osg::ref_ptr<Options> localOptions = options ?
369            static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
370        localOptions->getDatabasePathList().push_front(osgDB::getFilePath(filename));
371
372        KFbxSdkManager* pSdkManager = KFbxSdkManager::Create();
373
374        if (!pSdkManager)
375        {
376            return WriteResult::ERROR_IN_WRITING_FILE;
377        }
378
379        CleanUpFbx cleanUpFbx(pSdkManager);
380
381        bool useFbxRoot = false;
382        if (options)
383        {
384            std::istringstream iss(options->getOptionString());
385            std::string opt;
386            while (iss >> opt)
387            {
388                if (opt == "Embedded")
389                {
390                    IOSREF.SetBoolProp(EXP_FBX_EMBEDDED, true);
391                    if (KFbxIOSettings::IOSettingsRef().IsIOSettingsAllocated())
392                        KFbxIOSettings::IOSettingsRef().AllocateIOSettings(*pSdkManager);
393                }
394                else if (opt == "UseFbxRoot")
395                {
396                    useFbxRoot = true;
397                }
398            }
399        }
400
401        KFbxScene* pScene = KFbxScene::Create(pSdkManager, "");
402        WriterNodeVisitor writerNodeVisitor(pScene, pSdkManager, filename,
403            options, osgDB::getFilePath(node.getName().empty() ? filename : node.getName()));
404        if (useFbxRoot && isBasicRootNode(node))
405        {
406            // If root node is a simple group, put all elements under the FBX root
407            const osg::Group * osgGroup = node.asGroup();
408            for(unsigned int child=0; child<osgGroup->getNumChildren(); ++child) {
409                const_cast<osg::Node *>(osgGroup->getChild(child))->accept(writerNodeVisitor);
410            }
411        }
412        else {
413            // Normal scene
414            const_cast<osg::Node&>(node).accept(writerNodeVisitor);
415        }
416
417        KFbxExporter* lExporter = KFbxExporter::Create(pSdkManager, "");
418        pScene->GetGlobalSettings().SetAxisSystem(KFbxAxisSystem::eOpenGL);
419
420        // Ensure the directory exists or else the FBX SDK will fail
421        if (!osgDB::makeDirectoryForFile(filename)) {
422            osg::notify(osg::NOTICE) << "Can't create directory for file '" << filename << "'. FBX SDK may fail creating the file." << std::endl;
423        }
424
425        // The FBX SDK interprets the filename as UTF-8
426#ifdef OSG_USE_UTF8_FILENAME
[11110]427        const std::string& utf8filename(filename);
[11109]428#else
[11141]429        std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
[11109]430#endif
431
432        if (!lExporter->Initialize(utf8filename.c_str()))
433        {
434            return std::string(lExporter->GetLastErrorString());
435        }
436        if (!lExporter->Export(pScene))
437        {
438            return std::string(lExporter->GetLastErrorString());
439        }
440
441        return WriteResult::FILE_SAVED;
442    }
443    catch (const std::string& s)
444    {
445        return s;
446    }
447    catch (const char* s)
448    {
449        return std::string(s);
450    }
451    catch (...)
452    {
453    }
454
455    return WriteResult::ERROR_IN_WRITING_FILE;
456}
457
[10780]458///////////////////////////////////////////////////////////////////////////
459// Add ourself to the Registry to instantiate the reader/writer.
460
461REGISTER_OSGPLUGIN(fbx, ReaderWriterFBX)
Note: See TracBrowser for help on using the browser.