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

Revision 13466, 18.9 kB (checked in by robert, 13 days ago)

Moved widgets from VolumeEditorWidget? to TransferFunctionWidget?, and widget utilities into WidgetUtils?.

  • Property svn:eol-style set to native
Line 
1#include <sstream>
2#include <memory>
3#ifndef WIN32
4#include <strings.h>//for strncasecmp
5#endif
6
7#include <osg/Notify>
8#include <osg/MatrixTransform>
9#include <osg/Material>
10#include <osg/PositionAttitudeTransform>
11#include <osg/Texture2D>
12#include <osg/Version>
13#include <osgDB/ConvertUTF>
14#include <osgDB/FileNameUtils>
15#include <osgDB/FileUtils>
16#include <osgDB/ReadFile>
17#include <osgDB/Registry>
18#include <osgAnimation/AnimationManagerBase>
19#include <osgAnimation/Bone>
20#include <osgAnimation/RigGeometry>
21#include <osgAnimation/Skeleton>
22#include <osgAnimation/VertexInfluence>
23
24#if defined(_MSC_VER)
25    #pragma warning( disable : 4505 )
26    #pragma warning( default : 4996 )
27#endif
28#include <fbxsdk.h>
29
30#include "ReaderWriterFBX.h"
31#include "fbxReader.h"
32#include "WriterNodeVisitor.h"
33
34/// Returns true if the given node is a basic root group with no special information.
35/// Used in conjunction with UseFbxRoot option.
36/// Identity transforms are considered as basic root nodes.
37bool isBasicRootNode(const osg::Node& node)
38{
39    const osg::Group* osgGroup = node.asGroup();
40    if (!osgGroup)
41    {
42        // Geodes & such are not basic root nodes
43        return false;
44    }
45
46    // Test if we've got an empty transform (= a group!)
47    const osg::Transform* transform = osgGroup->asTransform();
48    if (transform)
49    {
50        if (const osg::MatrixTransform* matrixTransform = transform->asMatrixTransform())
51        {
52            if (!matrixTransform->getMatrix().isIdentity())
53            {
54                // Non-identity matrix transform
55                return false;
56            }
57        }
58        else if (const osg::PositionAttitudeTransform* pat = transform->asPositionAttitudeTransform())
59        {
60            if (pat->getPosition() != osg::Vec3d() ||
61                pat->getAttitude() != osg::Quat() ||
62                pat->getScale() != osg::Vec3d(1.0f, 1.0f, 1.0f) ||
63                pat->getPivotPoint() != osg::Vec3d())
64            {
65                // Non-identity position attribute transform
66                return false;
67            }
68        }
69        else
70        {
71            // Other transform (not identity or not predefined type)
72            return false;
73        }
74    }
75
76    // Test the presence of a non-empty stateset
77    if (node.getStateSet())
78    {
79        osg::ref_ptr<osg::StateSet> emptyStateSet = new osg::StateSet;
80        if (node.getStateSet()->compare(*emptyStateSet, true) != 0)
81        {
82            return false;
83        }
84    }
85
86    return true;
87}
88
89
90class CleanUpFbx
91{
92    FbxManager* m_pSdkManager;
93public:
94    explicit CleanUpFbx(FbxManager* pSdkManager) : m_pSdkManager(pSdkManager)
95    {}
96
97    ~CleanUpFbx()
98    {
99        m_pSdkManager->Destroy();
100    }
101};
102
103//Some files don't correctly mark their skeleton nodes, so this function infers
104//them from the nodes that skin deformers linked to.
105void findLinkedFbxSkeletonNodes(FbxNode* pNode, std::set<const FbxNode*>& fbxSkeletons)
106{
107    if (const FbxGeometry* pMesh = FbxCast<FbxGeometry>(pNode->GetNodeAttribute()))
108    {
109        for (int i = 0; i < pMesh->GetDeformerCount(FbxDeformer::eSkin); ++i)
110        {
111            const FbxSkin* pSkin = (const FbxSkin*)pMesh->GetDeformer(i, FbxDeformer::eSkin);
112
113            for (int j = 0; j < pSkin->GetClusterCount(); ++j)
114            {
115                const FbxNode* pSkeleton = pSkin->GetCluster(j)->GetLink();
116                fbxSkeletons.insert(pSkeleton);
117            }
118        }
119    }
120
121    for (int i = 0; i < pNode->GetChildCount(); ++i)
122    {
123        findLinkedFbxSkeletonNodes(pNode->GetChild(i), fbxSkeletons);
124    }
125}
126
127void resolveBindMatrices(
128    osg::Node& root,
129    const BindMatrixMap& boneBindMatrices,
130    const std::map<FbxNode*, osg::Node*>& nodeMap)
131{
132    std::set<std::string> nodeNames;
133    for (std::map<FbxNode*, osg::Node*>::const_iterator it = nodeMap.begin(); it != nodeMap.end(); ++it)
134    {
135        nodeNames.insert(it->second->getName());
136    }
137
138    for (BindMatrixMap::const_iterator it = boneBindMatrices.begin();
139        it != boneBindMatrices.end();)
140    {
141        FbxNode* const fbxBone = it->first.first;
142        std::map<FbxNode*, osg::Node*>::const_iterator nodeIt = nodeMap.find(fbxBone);
143        if (nodeIt != nodeMap.end())
144        {
145            const osg::Matrix bindMatrix = it->second;
146            osgAnimation::Bone& osgBone = dynamic_cast<osgAnimation::Bone&>(*nodeIt->second);
147            osgBone.setInvBindMatrixInSkeletonSpace(bindMatrix);
148
149            ++it;
150            for (; it != boneBindMatrices.end() && it->first.first == fbxBone; ++it)
151            {
152                if (it->second != bindMatrix)
153                {
154                    std::string name;
155                    for (int i = 0;; ++i)
156                    {
157                        std::stringstream ss;
158                        ss << osgBone.getName() << '_' << i;
159                        name = ss.str();
160                        if (nodeNames.insert(name).second)
161                        {
162                            break;
163                        }
164                    }
165                    osgAnimation::Bone* newBone = new osgAnimation::Bone(name);
166                    newBone->setDefaultUpdateCallback();
167                    newBone->setInvBindMatrixInSkeletonSpace(it->second);
168                    osgBone.addChild(newBone);
169
170                    osgAnimation::RigGeometry* pRigGeometry = it->first.second;
171
172                    osgAnimation::VertexInfluenceMap* vertexInfluences = pRigGeometry->getInfluenceMap();
173
174                    osgAnimation::VertexInfluenceMap::iterator vimIt = vertexInfluences->find(osgBone.getName());
175                    if (vimIt != vertexInfluences->end())
176                    {
177                        osgAnimation::VertexInfluence vi;
178                        vi.swap(vimIt->second);
179                        vertexInfluences->erase(vimIt);
180                        osgAnimation::VertexInfluence& vi2 = (*vertexInfluences)[name];
181                        vi.swap(vi2);
182                        vi2.setName(name);
183                    }
184                    else
185                    {
186                        OSG_WARN << "No vertex influences found for \"" << osgBone.getName() << "\"" << std::endl;
187                    }
188                }
189            }
190        }
191        else
192        {
193            OSG_WARN << "No bone found for \"" << fbxBone->GetName() << "\"" << std::endl;
194            ++it;
195        }
196    }
197}
198
199osgDB::ReaderWriter::ReadResult
200ReaderWriterFBX::readNode(const std::string& filenameInit,
201                          const Options* options) const
202{
203    try
204    {
205        std::string ext(osgDB::getLowerCaseFileExtension(filenameInit));
206        if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
207
208        std::string filename(osgDB::findDataFile(filenameInit, options));
209        if (filename.empty()) return ReadResult::FILE_NOT_FOUND;
210
211        FbxManager* pSdkManager = FbxManager::Create();
212
213        if (!pSdkManager)
214        {
215            return ReadResult::ERROR_IN_READING_FILE;
216        }
217
218        CleanUpFbx cleanUpFbx(pSdkManager);
219
220        pSdkManager->SetIOSettings(FbxIOSettings::Create(pSdkManager, IOSROOT));
221
222        FbxScene* pScene = FbxScene::Create(pSdkManager, "");
223
224        // The FBX SDK interprets the filename as UTF-8
225#ifdef OSG_USE_UTF8_FILENAME
226        const std::string& utf8filename(filename);
227#else
228        std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
229#endif
230
231        FbxImporter* lImporter = FbxImporter::Create(pSdkManager, "");
232
233        if (!lImporter->Initialize(utf8filename.c_str(), -1, pSdkManager->GetIOSettings()))
234        {
235#if FBXSDK_VERSION_MAJOR < 2014
236            return std::string(lImporter->GetLastErrorString());
237#else
238            return std::string(lImporter->GetStatus().GetErrorString());
239#endif
240        }
241
242        if (!lImporter->IsFBX())
243        {
244            return ReadResult::ERROR_IN_READING_FILE;
245        }
246
247        for (int i = 0; FbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i); i++)
248        {
249            lTakeInfo->mSelect = true;
250        }
251
252        if (!lImporter->Import(pScene))
253        {
254#if FBXSDK_VERSION_MAJOR < 2014
255            return std::string(lImporter->GetLastErrorString());
256#else 
257            return std::string(lImporter->GetStatus().GetErrorString());
258#endif
259        }
260
261        //FbxAxisSystem::OpenGL.ConvertScene(pScene);        // Doesn't work as expected. Still need to transform vertices.
262
263        if (FbxNode* pNode = pScene->GetRootNode())
264        {
265            bool useFbxRoot = false;
266            bool lightmapTextures = false;
267            bool tessellatePolygons = false;
268            if (options)
269            {
270                std::istringstream iss(options->getOptionString());
271                std::string opt;
272                while (iss >> opt)
273                {
274                    if (opt == "UseFbxRoot")
275                    {
276                        useFbxRoot = true;
277                    }
278                    if (opt == "LightmapTextures")
279                    {
280                        lightmapTextures = true;
281                    }
282                    if (opt == "TessellatePolygons")
283                    {
284                        tessellatePolygons = true;
285                    }
286                }
287            }
288
289            bool bIsBone = false;
290            int nLightCount = 0;
291            osg::ref_ptr<Options> localOptions = NULL;
292            if (options)
293                localOptions = options->cloneOptions();
294            else
295                localOptions = new osgDB::Options();
296            localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_IMAGES);
297
298            std::string filePath = osgDB::getFilePath(filename);
299            FbxMaterialToOsgStateSet fbxMaterialToOsgStateSet(filePath, localOptions.get(), lightmapTextures);
300
301            std::set<const FbxNode*> fbxSkeletons;
302            findLinkedFbxSkeletonNodes(pNode, fbxSkeletons);
303
304            OsgFbxReader::AuthoringTool authoringTool = OsgFbxReader::UNKNOWN;
305            if (FbxDocumentInfo* pDocInfo = pScene->GetDocumentInfo())
306            {
307                struct ToolName
308                {
309                    const char* name;
310                    OsgFbxReader::AuthoringTool tool;
311                };
312
313                ToolName authoringTools[] = {
314                    {"OpenSceneGraph", OsgFbxReader::OPENSCENEGRAPH},
315                    {"3ds Max", OsgFbxReader::AUTODESK_3DSTUDIO_MAX}
316                };
317
318                FbxString appName = pDocInfo->LastSaved_ApplicationName.Get();
319
320                for (unsigned int i = 0; i < sizeof(authoringTools) / sizeof(authoringTools[0]); ++i)
321                {
322                    if (0 ==
323#ifdef WIN32
324                        _strnicmp
325#else
326                        strncasecmp
327#endif
328                        (appName, authoringTools[i].name, strlen(authoringTools[i].name)))
329                    {
330                        authoringTool = authoringTools[i].tool;
331                        break;
332                    }
333                }
334            }
335
336
337            OsgFbxReader reader(*pSdkManager,
338                *pScene,
339                fbxMaterialToOsgStateSet,
340                fbxSkeletons,
341                *localOptions,
342                authoringTool,
343                lightmapTextures,
344                tessellatePolygons);
345
346            ReadResult res = reader.readFbxNode(pNode, bIsBone, nLightCount);
347
348            if (res.success())
349            {
350                fbxMaterialToOsgStateSet.checkInvertTransparency();
351
352                resolveBindMatrices(*res.getNode(), reader.boneBindMatrices, reader.nodeMap);
353
354                osg::Node* osgNode = res.getNode();
355                osgNode->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON);
356                osgNode->getOrCreateStateSet()->setMode(GL_NORMALIZE,osg::StateAttribute::ON);
357
358                if (reader.pAnimationManager.valid())
359                {
360                    if (osgNode->getUpdateCallback())
361                    {
362                        osg::Group* osgGroup = new osg::Group;
363                        osgGroup->addChild(osgNode);
364                        osgNode = osgGroup;
365                    }
366
367                    //because the animations may be altered after registering
368                    reader.pAnimationManager->buildTargetReference();
369                    osgNode->setUpdateCallback(reader.pAnimationManager.get());
370                }
371
372                FbxAxisSystem fbxAxis = pScene->GetGlobalSettings().GetAxisSystem();
373
374                if (fbxAxis != FbxAxisSystem::OpenGL)
375                {
376                    int upSign;
377                    FbxAxisSystem::EUpVector eUp = fbxAxis.GetUpVector(upSign);
378                    bool bLeftHanded = fbxAxis.GetCoorSystem() == FbxAxisSystem::eLeftHanded;
379                    float fSign = upSign < 0 ? -1.0f : 1.0f;
380                    float zScale = bLeftHanded ? -1.0f : 1.0f;
381
382                    osg::Matrix mat;
383                    switch (eUp)
384                    {
385                    case FbxAxisSystem::eXAxis:
386                        mat.set(0,fSign,0,0,-fSign,0,0,0,0,0,zScale,0,0,0,0,1);
387                        break;
388                    case FbxAxisSystem::eYAxis:
389                        mat.set(1,0,0,0,0,fSign,0,0,0,0,fSign*zScale,0,0,0,0,1);
390                        break;
391                    case FbxAxisSystem::eZAxis:
392                        mat.set(1,0,0,0,0,0,-fSign*zScale,0,0,fSign,0,0,0,0,0,1);
393                        break;
394                    }
395
396                    osg::Transform* pTransformTemp = osgNode->asTransform();
397                    osg::MatrixTransform* pMatrixTransform = pTransformTemp ?
398                        pTransformTemp->asMatrixTransform() : NULL;
399                    if (pMatrixTransform)
400                    {
401                        pMatrixTransform->setMatrix(pMatrixTransform->getMatrix() * mat);
402                    }
403                    else
404                    {
405                        pMatrixTransform = new osg::MatrixTransform(mat);
406                        if (useFbxRoot && isBasicRootNode(*osgNode))
407                        {
408                            // If root node is a simple group, put all FBX elements under the OSG root
409                            osg::Group* osgGroup = osgNode->asGroup();
410                            for(unsigned int i = 0; i < osgGroup->getNumChildren(); ++i)
411                            {
412                                pMatrixTransform->addChild(osgGroup->getChild(i));
413                            }
414                            pMatrixTransform->setName(osgGroup->getName());
415                        }
416                        else
417                        {
418                            pMatrixTransform->addChild(osgNode);
419                        }
420                    }
421                    osgNode = pMatrixTransform;
422                }
423
424                osgNode->setName(filenameInit);
425                return osgNode;
426            }
427        }
428    }
429    catch (...)
430    {
431        OSG_WARN << "Exception thrown while importing \"" << filenameInit << '\"' << std::endl;
432    }
433
434    return ReadResult::ERROR_IN_READING_FILE;
435}
436
437osgDB::ReaderWriter::WriteResult ReaderWriterFBX::writeNode(
438    const osg::Node& node,
439    const std::string& filename,
440    const Options* options) const
441{
442    try
443    {
444        std::string ext = osgDB::getLowerCaseFileExtension(filename);
445        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
446
447        osg::ref_ptr<Options> localOptions = options ?
448            static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
449        localOptions->getDatabasePathList().push_front(osgDB::getFilePath(filename));
450
451        FbxManager* pSdkManager = FbxManager::Create();
452
453        if (!pSdkManager)
454        {
455            return WriteResult::ERROR_IN_WRITING_FILE;
456        }
457
458        CleanUpFbx cleanUpFbx(pSdkManager);
459
460        pSdkManager->SetIOSettings(FbxIOSettings::Create(pSdkManager, IOSROOT));
461
462        bool useFbxRoot = false;
463        if (options)
464        {
465            std::istringstream iss(options->getOptionString());
466            std::string opt;
467            while (iss >> opt)
468            {
469                if (opt == "Embedded")
470                {
471                    pSdkManager->GetIOSettings()->SetBoolProp(EXP_FBX_EMBEDDED, true);
472                }
473                else if (opt == "UseFbxRoot")
474                {
475                    useFbxRoot = true;
476                }
477            }
478        }
479
480        FbxScene* pScene = FbxScene::Create(pSdkManager, "");
481        pluginfbx::WriterNodeVisitor writerNodeVisitor(pScene, pSdkManager, filename,
482            options, osgDB::getFilePath(node.getName().empty() ? filename : node.getName()));
483        if (useFbxRoot && isBasicRootNode(node))
484        {
485            // If root node is a simple group, put all elements under the FBX root
486            const osg::Group * osgGroup = node.asGroup();
487            for (unsigned int child = 0; child < osgGroup->getNumChildren(); ++child)
488            {
489                const_cast<osg::Node *>(osgGroup->getChild(child))->accept(writerNodeVisitor);
490            }
491        }
492        else {
493            // Normal scene
494            const_cast<osg::Node&>(node).accept(writerNodeVisitor);
495        }
496
497        FbxDocumentInfo* pDocInfo = pScene->GetDocumentInfo();
498        bool needNewDocInfo = pDocInfo != NULL;
499        if (needNewDocInfo)
500        {
501            pDocInfo = FbxDocumentInfo::Create(pSdkManager, "");
502        }
503        pDocInfo->LastSaved_ApplicationName.Set(FbxString("OpenSceneGraph"));
504        pDocInfo->LastSaved_ApplicationVersion.Set(FbxString(osgGetVersion()));
505        if (needNewDocInfo)
506        {
507            pScene->SetDocumentInfo(pDocInfo);
508        }
509
510        FbxExporter* lExporter = FbxExporter::Create(pSdkManager, "");
511        pScene->GetGlobalSettings().SetAxisSystem(FbxAxisSystem::eOpenGL);
512
513        // Ensure the directory exists or else the FBX SDK will fail
514        if (!osgDB::makeDirectoryForFile(filename)) {
515            OSG_NOTICE << "Can't create directory for file '" << filename << "'. FBX SDK may fail creating the file." << std::endl;
516        }
517
518        // The FBX SDK interprets the filename as UTF-8
519#ifdef OSG_USE_UTF8_FILENAME
520        const std::string& utf8filename(filename);
521#else
522        std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
523#endif
524
525        if (!lExporter->Initialize(utf8filename.c_str()))
526        {
527#if FBXSDK_VERSION_MAJOR < 2014
528            return std::string(lExporter->GetLastErrorString());
529#else
530            return std::string(lExporter->GetStatus().GetErrorString());
531#endif
532        }
533        if (!lExporter->Export(pScene))
534        {
535#if FBXSDK_VERSION_MAJOR < 2014
536            return std::string(lExporter->GetLastErrorString());
537#else
538            return std::string(lExporter->GetStatus().GetErrorString());
539#endif
540        }
541
542        return WriteResult::FILE_SAVED;
543    }
544    catch (const std::string& s)
545    {
546        return s;
547    }
548    catch (const char* s)
549    {
550        return std::string(s);
551    }
552    catch (...)
553    {
554    }
555
556    return WriteResult::ERROR_IN_WRITING_FILE;
557}
558
559///////////////////////////////////////////////////////////////////////////
560// Add ourself to the Registry to instantiate the reader/writer.
561
562REGISTER_OSGPLUGIN(fbx, ReaderWriterFBX)
Note: See TracBrowser for help on using the browser.