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

Revision 11109, 13.5 kB (checked in by mplatings, 5 years ago)
Line 
1#include <sstream>
2#include <memory>
3
4#include <osg/Notify>
5#include <osg/MatrixTransform>
6#include <osg/Material>
7#include <osg/PositionAttitudeTransform>
8#include <osg/Texture2D>
9#include <osgDB/ConvertUTF>
10
11#include <osgDB/FileNameUtils>
12#include <osgDB/FileUtils>
13#include <osgDB/ReadFile>
14#include <osgDB/Registry>
15#include <osgAnimation/AnimationManagerBase>
16#include <osgAnimation/Bone>
17#include <osgAnimation/Skeleton>
18
19#if defined(_MSC_VER)
20    #pragma warning( disable : 4505 )
21#endif
22#include <fbxsdk.h>
23
24#include "ReaderWriterFBX.h"
25#include "fbxRNode.h"
26#include "fbxMaterialToOsgStateSet.h"
27#include "WriterNodeVisitor.h"
28
29
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
100class ConvertBindMatrixVisitor : public osg::NodeVisitor
101{
102public:
103    ConvertBindMatrixVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}
104
105    virtual void apply(osg::MatrixTransform& node)
106    {
107        if (osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(&node))
108        {
109            bone->setInvBindMatrixInSkeletonSpace(osg::Matrix::inverse(bone->getMatrixInBoneSpace()));
110            if (const osgAnimation::Bone* parent = bone->getBoneParent())
111            {
112                bone->setInvBindMatrixInSkeletonSpace(parent->getInvBindMatrixInSkeletonSpace() * bone->getInvBindMatrixInSkeletonSpace());
113            }
114        }
115
116        traverse(node);
117    }
118};
119
120osgDB::ReaderWriter::ReadResult
121ReaderWriterFBX::readNode(const std::string& filenameInit,
122                          const Options* options) const
123{
124    try
125    {
126        std::string ext(osgDB::getLowerCaseFileExtension(filenameInit));
127        if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
128
129        std::string filename(osgDB::findDataFile(filenameInit, options));
130        if (filename.empty()) return ReadResult::FILE_NOT_FOUND;
131
132        KFbxSdkManager* pSdkManager = KFbxSdkManager::Create();
133
134        if (!pSdkManager)
135        {
136            return ReadResult::ERROR_IN_READING_FILE;
137        }
138
139        CleanUpFbx cleanUpFbx(pSdkManager);
140
141        KFbxScene* pScene = KFbxScene::Create(pSdkManager, "");
142
143        // The FBX SDK interprets the filename as UTF-8
144#ifdef OSG_USE_UTF8_FILENAME
145        const std::string& utf8filename(filename);
146#else
147        std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
148#endif
149
150        int fileFormat;
151        if (!pSdkManager->GetIOPluginRegistry()->DetectFileFormat(utf8filename.c_str(), fileFormat))
152        {
153            return ReadResult::FILE_NOT_HANDLED;
154        }
155        KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager, "");
156        lImporter->SetFileFormat(fileFormat);
157
158        if (!lImporter->Initialize(utf8filename.c_str()))
159        {
160            return std::string(lImporter->GetLastErrorString());
161        }
162
163        if (!lImporter->IsFBX())
164        {
165            return ReadResult::ERROR_IN_READING_FILE;
166        }
167
168        for (int i = 0; i < lImporter->GetTakeCount(); i++)
169        {
170            KFbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i);
171
172            lTakeInfo->mSelect = true;
173        }
174
175        if (!lImporter->Import(pScene))
176        {
177            return std::string(lImporter->GetLastErrorString());
178        }
179
180        //KFbxAxisSystem::OpenGL.ConvertScene(pScene);        // Doesn't work as expected. Still need to transform vertices.
181
182        if (KFbxNode* pNode = pScene->GetRootNode())
183        {
184            bool useFbxRoot = false;
185            if (options)
186            {
187                std::istringstream iss(options->getOptionString());
188                std::string opt;
189                while (iss >> opt)
190                {
191                    if (opt == "UseFbxRoot")
192                    {
193                        useFbxRoot = true;
194                    }
195                }
196            }
197
198            osg::ref_ptr<osgAnimation::AnimationManagerBase> pAnimationManager;
199            bool bNeedSkeleton = false;
200            int nLightCount = 0;
201            osg::ref_ptr<Options> localOptions = NULL;
202            if (options)
203                localOptions = options->cloneOptions();
204            else
205                localOptions = new osgDB::Options();
206            localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_IMAGES);
207
208            std::string filePath = osgDB::getFilePath(filename);
209            FbxMaterialToOsgStateSet fbxMaterialToOsgStateSet(filePath, localOptions.get());
210           
211            ReadResult res = readFbxNode(*pSdkManager, pNode, pAnimationManager,
212                bNeedSkeleton, nLightCount, fbxMaterialToOsgStateSet, localOptions.get());
213
214            if (res.success())
215            {
216                osg::Node* osgNode = res.getNode();
217                if (bNeedSkeleton)
218                {
219                    ConvertBindMatrixVisitor convertBindMatrixVisitor;
220                    osgNode->accept(convertBindMatrixVisitor);
221                    osgAnimation::Skeleton* osgSkeleton = new osgAnimation::Skeleton;
222                    osgSkeleton->setDefaultUpdateCallback();
223                    osgSkeleton->addChild(osgNode);
224                    osgNode = osgSkeleton;
225                }
226                if (pAnimationManager.valid())
227                {
228                    if (osgNode->getUpdateCallback())
229                    {
230                        osg::Group* osgGroup = new osg::Group;
231                        osgGroup->addChild(osgNode);
232                        osgNode = osgGroup;
233                    }
234
235                    //because the animations may be altered after registering
236                    pAnimationManager->buildTargetReference();
237                    osgNode->setUpdateCallback(pAnimationManager.get());
238                }
239
240                KFbxAxisSystem fbxAxis = pScene->GetGlobalSettings().GetAxisSystem();
241
242                if (fbxAxis != KFbxAxisSystem::OpenGL)
243                {
244                    int upSign;
245                    KFbxAxisSystem::eUpVector eUp = fbxAxis.GetUpVector(upSign);
246                    bool bLeftHanded = fbxAxis.GetCoorSystem() == KFbxAxisSystem::LeftHanded;
247                    float fSign = upSign < 0 ? -1.0f : 1.0f;
248                    float zScale = bLeftHanded ? -1.0f : 1.0f;
249
250                    osg::Matrix mat;
251                    switch (eUp)
252                    {
253                    case KFbxAxisSystem::XAxis:
254                        mat.set(0,fSign,0,0,-fSign,0,0,0,0,0,zScale,0,0,0,0,1);
255                        break;
256                    case KFbxAxisSystem::YAxis:
257                        mat.set(1,0,0,0,0,fSign,0,0,0,0,fSign*zScale,0,0,0,0,1);
258                        break;
259                    case KFbxAxisSystem::ZAxis:
260                        mat.set(1,0,0,0,0,0,-fSign*zScale,0,0,fSign,0,0,0,0,0,1);
261                        break;
262                    }
263
264                    osg::Transform* pTransformTemp = osgNode->asTransform();
265                    osg::MatrixTransform* pMatrixTransform = pTransformTemp ?
266                        pTransformTemp->asMatrixTransform() : NULL;
267                    if (pMatrixTransform)
268                    {
269                        pMatrixTransform->setMatrix(pMatrixTransform->getMatrix() * mat);
270                    }
271                    else
272                    {
273                        pMatrixTransform = new osg::MatrixTransform(mat);
274                        if (useFbxRoot && isBasicRootNode(*osgNode))
275                        {
276                            // If root node is a simple group, put all FBX elements under the OSG root
277                            osg::Group* osgGroup = osgNode->asGroup();
278                            for(unsigned int i = 0; i < osgGroup->getNumChildren(); ++i)
279                            {
280                                pMatrixTransform->addChild(osgGroup->getChild(i));
281                            }
282                            pMatrixTransform->setName(osgGroup->getName());
283                        }
284                        else
285                        {
286                            pMatrixTransform->addChild(osgNode);
287                        }
288                    }
289                    osgNode = pMatrixTransform;
290                }
291
292                osgNode->setName(filenameInit);
293                return osgNode;
294            }
295        }
296    }
297    catch (...)
298    {
299    }
300
301    return ReadResult::ERROR_IN_READING_FILE;
302}
303
304osgDB::ReaderWriter::WriteResult ReaderWriterFBX::writeNode(
305    const osg::Node& node,
306    const std::string& filename,
307    const Options* options) const
308{
309    try
310    {
311        std::string ext = osgDB::getLowerCaseFileExtension(filename);
312        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
313
314        osg::ref_ptr<Options> localOptions = options ?
315            static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
316        localOptions->getDatabasePathList().push_front(osgDB::getFilePath(filename));
317
318        KFbxSdkManager* pSdkManager = KFbxSdkManager::Create();
319
320        if (!pSdkManager)
321        {
322            return WriteResult::ERROR_IN_WRITING_FILE;
323        }
324
325        CleanUpFbx cleanUpFbx(pSdkManager);
326
327        bool useFbxRoot = false;
328        if (options)
329        {
330            std::istringstream iss(options->getOptionString());
331            std::string opt;
332            while (iss >> opt)
333            {
334                if (opt == "Embedded")
335                {
336                    IOSREF.SetBoolProp(EXP_FBX_EMBEDDED, true);
337                    if (KFbxIOSettings::IOSettingsRef().IsIOSettingsAllocated())
338                        KFbxIOSettings::IOSettingsRef().AllocateIOSettings(*pSdkManager);
339                }
340                else if (opt == "UseFbxRoot")
341                {
342                    useFbxRoot = true;
343                }
344            }
345        }
346
347        KFbxScene* pScene = KFbxScene::Create(pSdkManager, "");
348        WriterNodeVisitor writerNodeVisitor(pScene, pSdkManager, filename,
349            options, osgDB::getFilePath(node.getName().empty() ? filename : node.getName()));
350        if (useFbxRoot && isBasicRootNode(node))
351        {
352            // If root node is a simple group, put all elements under the FBX root
353            const osg::Group * osgGroup = node.asGroup();
354            for(unsigned int child=0; child<osgGroup->getNumChildren(); ++child) {
355                const_cast<osg::Node *>(osgGroup->getChild(child))->accept(writerNodeVisitor);
356            }
357        }
358        else {
359            // Normal scene
360            const_cast<osg::Node&>(node).accept(writerNodeVisitor);
361        }
362
363        KFbxExporter* lExporter = KFbxExporter::Create(pSdkManager, "");
364        pScene->GetGlobalSettings().SetAxisSystem(KFbxAxisSystem::eOpenGL);
365
366        // Ensure the directory exists or else the FBX SDK will fail
367        if (!osgDB::makeDirectoryForFile(filename)) {
368            osg::notify(osg::NOTICE) << "Can't create directory for file '" << filename << "'. FBX SDK may fail creating the file." << std::endl;
369        }
370
371        // The FBX SDK interprets the filename as UTF-8
372#ifdef OSG_USE_UTF8_FILENAME
373        std::string utf8filename(filename);
374#else
375        std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
376#endif
377
378        if (!lExporter->Initialize(utf8filename.c_str()))
379        {
380            return std::string(lExporter->GetLastErrorString());
381        }
382        if (!lExporter->Export(pScene))
383        {
384            return std::string(lExporter->GetLastErrorString());
385        }
386
387        return WriteResult::FILE_SAVED;
388    }
389    catch (const std::string& s)
390    {
391        return s;
392    }
393    catch (const char* s)
394    {
395        return std::string(s);
396    }
397    catch (...)
398    {
399    }
400
401    return WriteResult::ERROR_IN_WRITING_FILE;
402}
403
404///////////////////////////////////////////////////////////////////////////
405// Add ourself to the Registry to instantiate the reader/writer.
406
407REGISTER_OSGPLUGIN(fbx, ReaderWriterFBX)
Note: See TracBrowser for help on using the browser.