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

Revision 11252, 16.0 kB (checked in by mplatings, 5 years ago)

Fixed an infinite loop when loading a malformed file.

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