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

Revision 13464, 18.7 kB (checked in by robert, 6 hours ago)

Added support for changing background colour of LineEdit? widget when focus changes

  • 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    KFbxSdkManager* m_pSdkManager;
93public:
94    explicit CleanUpFbx(KFbxSdkManager* 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(KFbxNode* pNode, std::set<const KFbxNode*>& fbxSkeletons)
106{
107    if (const KFbxGeometry* pMesh = KFbxCast<KFbxGeometry>(pNode->GetNodeAttribute()))
108    {
109        for (int i = 0; i < pMesh->GetDeformerCount(KFbxDeformer::eSKIN); ++i)
110        {
111            const KFbxSkin* pSkin = (const KFbxSkin*)pMesh->GetDeformer(i, KFbxDeformer::eSKIN);
112
113            for (int j = 0; j < pSkin->GetClusterCount(); ++j)
114            {
115                const KFbxNode* 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<KFbxNode*, osg::Node*>& nodeMap)
131{
132    std::set<std::string> nodeNames;
133    for (std::map<KFbxNode*, 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        KFbxNode* const fbxBone = it->first.first;
142        std::map<KFbxNode*, 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        KFbxSdkManager* pSdkManager = KFbxSdkManager::Create();
212
213        if (!pSdkManager)
214        {
215            return ReadResult::ERROR_IN_READING_FILE;
216        }
217
218        CleanUpFbx cleanUpFbx(pSdkManager);
219
220        pSdkManager->SetIOSettings(KFbxIOSettings::Create(pSdkManager, IOSROOT));
221
222        KFbxScene* pScene = KFbxScene::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        KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager, "");
232
233        if (!lImporter->Initialize(utf8filename.c_str(), -1, pSdkManager->GetIOSettings()))
234        {
235            return std::string(lImporter->GetLastErrorString());
236        }
237
238        if (!lImporter->IsFBX())
239        {
240            return ReadResult::ERROR_IN_READING_FILE;
241        }
242
243        for (int i = 0; KFbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i); i++)
244        {
245            lTakeInfo->mSelect = true;
246        }
247
248        if (!lImporter->Import(pScene))
249        {
250            return std::string(lImporter->GetLastErrorString());
251        }
252
253        //KFbxAxisSystem::OpenGL.ConvertScene(pScene);        // Doesn't work as expected. Still need to transform vertices.
254
255        if (KFbxNode* pNode = pScene->GetRootNode())
256        {
257            bool useFbxRoot = false;
258            bool lightmapTextures = false;
259            bool tessellatePolygons = false;
260            if (options)
261            {
262                std::istringstream iss(options->getOptionString());
263                std::string opt;
264                while (iss >> opt)
265                {
266                    if (opt == "UseFbxRoot")
267                    {
268                        useFbxRoot = true;
269                    }
270                    if (opt == "LightmapTextures")
271                    {
272                        lightmapTextures = true;
273                    }
274                    if (opt == "TessellatePolygons")
275                    {
276                        tessellatePolygons = true;
277                    }
278                }
279            }
280
281            bool bIsBone = false;
282            int nLightCount = 0;
283            osg::ref_ptr<Options> localOptions = NULL;
284            if (options)
285                localOptions = options->cloneOptions();
286            else
287                localOptions = new osgDB::Options();
288            localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_IMAGES);
289
290            std::string filePath = osgDB::getFilePath(filename);
291            FbxMaterialToOsgStateSet fbxMaterialToOsgStateSet(filePath, localOptions.get(), lightmapTextures);
292
293            std::set<const KFbxNode*> fbxSkeletons;
294            findLinkedFbxSkeletonNodes(pNode, fbxSkeletons);
295
296            OsgFbxReader::AuthoringTool authoringTool = OsgFbxReader::UNKNOWN;
297            if (KFbxDocumentInfo* pDocInfo = pScene->GetDocumentInfo())
298            {
299                struct ToolName
300                {
301                    const char* name;
302                    OsgFbxReader::AuthoringTool tool;
303                };
304
305                ToolName authoringTools[] = {
306                    {"OpenSceneGraph", OsgFbxReader::OPENSCENEGRAPH},
307                    {"3ds Max", OsgFbxReader::AUTODESK_3DSTUDIO_MAX}
308                };
309
310                fbxString appName = pDocInfo->LastSaved_ApplicationName.Get();
311
312                for (unsigned int i = 0; i < sizeof(authoringTools) / sizeof(authoringTools[0]); ++i)
313                {
314                    if (0 ==
315#ifdef WIN32
316                        _strnicmp
317#else
318                        strncasecmp
319#endif
320                        (appName, authoringTools[i].name, strlen(authoringTools[i].name)))
321                    {
322                        authoringTool = authoringTools[i].tool;
323                        break;
324                    }
325                }
326            }
327
328
329            OsgFbxReader reader(*pSdkManager,
330                *pScene,
331                fbxMaterialToOsgStateSet,
332                fbxSkeletons,
333                *localOptions,
334                authoringTool,
335                lightmapTextures,
336                tessellatePolygons);
337
338            ReadResult res = reader.readFbxNode(pNode, bIsBone, nLightCount);
339
340            if (res.success())
341            {
342                fbxMaterialToOsgStateSet.checkInvertTransparency();
343
344                resolveBindMatrices(*res.getNode(), reader.boneBindMatrices, reader.nodeMap);
345
346                osg::Node* osgNode = res.getNode();
347                for (int light = 0; light < nLightCount; ++light)
348                    osgNode->getOrCreateStateSet()->setMode(GL_LIGHT0 + light, osg::StateAttribute::ON);
349
350                osgNode->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON);
351                osgNode->getOrCreateStateSet()->setMode(GL_NORMALIZE,osg::StateAttribute::ON);
352
353                if (reader.pAnimationManager.valid())
354                {
355                    if (osgNode->getUpdateCallback())
356                    {
357                        osg::Group* osgGroup = new osg::Group;
358                        osgGroup->addChild(osgNode);
359                        osgNode = osgGroup;
360                    }
361
362                    //because the animations may be altered after registering
363                    reader.pAnimationManager->buildTargetReference();
364                    osgNode->setUpdateCallback(reader.pAnimationManager.get());
365                }
366
367                KFbxAxisSystem fbxAxis = pScene->GetGlobalSettings().GetAxisSystem();
368
369                if (fbxAxis != KFbxAxisSystem::OpenGL)
370                {
371                    int upSign;
372                    KFbxAxisSystem::eUpVector eUp = fbxAxis.GetUpVector(upSign);
373                    bool bLeftHanded = fbxAxis.GetCoorSystem() == KFbxAxisSystem::LeftHanded;
374                    float fSign = upSign < 0 ? -1.0f : 1.0f;
375                    float zScale = bLeftHanded ? -1.0f : 1.0f;
376
377                    osg::Matrix mat;
378                    switch (eUp)
379                    {
380                    case KFbxAxisSystem::XAxis:
381                        mat.set(0,fSign,0,0,-fSign,0,0,0,0,0,zScale,0,0,0,0,1);
382                        break;
383                    case KFbxAxisSystem::YAxis:
384                        mat.set(1,0,0,0,0,fSign,0,0,0,0,fSign*zScale,0,0,0,0,1);
385                        break;
386                    case KFbxAxisSystem::ZAxis:
387                        mat.set(1,0,0,0,0,0,-fSign*zScale,0,0,fSign,0,0,0,0,0,1);
388                        break;
389                    }
390
391                    osg::Transform* pTransformTemp = osgNode->asTransform();
392                    osg::MatrixTransform* pMatrixTransform = pTransformTemp ?
393                        pTransformTemp->asMatrixTransform() : NULL;
394                    if (pMatrixTransform)
395                    {
396                        pMatrixTransform->setMatrix(pMatrixTransform->getMatrix() * mat);
397                    }
398                    else
399                    {
400                        pMatrixTransform = new osg::MatrixTransform(mat);
401                        if (useFbxRoot && isBasicRootNode(*osgNode))
402                        {
403                            // If root node is a simple group, put all FBX elements under the OSG root
404                            osg::Group* osgGroup = osgNode->asGroup();
405                            for(unsigned int i = 0; i < osgGroup->getNumChildren(); ++i)
406                            {
407                                pMatrixTransform->addChild(osgGroup->getChild(i));
408                            }
409                            pMatrixTransform->setName(osgGroup->getName());
410                        }
411                        else
412                        {
413                            pMatrixTransform->addChild(osgNode);
414                        }
415                    }
416                    osgNode = pMatrixTransform;
417                }
418
419                osgNode->setName(filenameInit);
420                return osgNode;
421            }
422        }
423    }
424    catch (...)
425    {
426        OSG_WARN << "Exception thrown while importing \"" << filenameInit << '\"' << std::endl;
427    }
428
429    return ReadResult::ERROR_IN_READING_FILE;
430}
431
432osgDB::ReaderWriter::WriteResult ReaderWriterFBX::writeNode(
433    const osg::Node& node,
434    const std::string& filename,
435    const Options* options) const
436{
437    try
438    {
439        std::string ext = osgDB::getLowerCaseFileExtension(filename);
440        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
441
442        osg::ref_ptr<Options> localOptions = options ?
443            static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
444        localOptions->getDatabasePathList().push_front(osgDB::getFilePath(filename));
445
446        KFbxSdkManager* pSdkManager = KFbxSdkManager::Create();
447
448        if (!pSdkManager)
449        {
450            return WriteResult::ERROR_IN_WRITING_FILE;
451        }
452
453        CleanUpFbx cleanUpFbx(pSdkManager);
454
455        pSdkManager->SetIOSettings(KFbxIOSettings::Create(pSdkManager, IOSROOT));
456
457        bool useFbxRoot = false;
458        if (options)
459        {
460            std::istringstream iss(options->getOptionString());
461            std::string opt;
462            while (iss >> opt)
463            {
464                if (opt == "Embedded")
465                {
466                    pSdkManager->GetIOSettings()->SetBoolProp(EXP_FBX_EMBEDDED, true);
467                }
468                else if (opt == "UseFbxRoot")
469                {
470                    useFbxRoot = true;
471                }
472            }
473        }
474
475        KFbxScene* pScene = KFbxScene::Create(pSdkManager, "");
476        pluginfbx::WriterNodeVisitor writerNodeVisitor(pScene, pSdkManager, filename,
477            options, osgDB::getFilePath(node.getName().empty() ? filename : node.getName()));
478        if (useFbxRoot && isBasicRootNode(node))
479        {
480            // If root node is a simple group, put all elements under the FBX root
481            const osg::Group * osgGroup = node.asGroup();
482            for (unsigned int child = 0; child < osgGroup->getNumChildren(); ++child)
483            {
484                const_cast<osg::Node *>(osgGroup->getChild(child))->accept(writerNodeVisitor);
485            }
486        }
487        else {
488            // Normal scene
489            const_cast<osg::Node&>(node).accept(writerNodeVisitor);
490        }
491
492        KFbxDocumentInfo* pDocInfo = pScene->GetDocumentInfo();
493        bool needNewDocInfo = pDocInfo != NULL;
494        if (needNewDocInfo)
495        {
496            pDocInfo = KFbxDocumentInfo::Create(pSdkManager, "");
497        }
498        pDocInfo->LastSaved_ApplicationName.Set(fbxString("OpenSceneGraph"));
499        pDocInfo->LastSaved_ApplicationVersion.Set(fbxString(osgGetVersion()));
500        if (needNewDocInfo)
501        {
502            pScene->SetDocumentInfo(pDocInfo);
503        }
504
505        KFbxExporter* lExporter = KFbxExporter::Create(pSdkManager, "");
506        pScene->GetGlobalSettings().SetAxisSystem(KFbxAxisSystem::eOpenGL);
507
508        // Ensure the directory exists or else the FBX SDK will fail
509        if (!osgDB::makeDirectoryForFile(filename)) {
510            OSG_NOTICE << "Can't create directory for file '" << filename << "'. FBX SDK may fail creating the file." << std::endl;
511        }
512
513        // The FBX SDK interprets the filename as UTF-8
514#ifdef OSG_USE_UTF8_FILENAME
515        const std::string& utf8filename(filename);
516#else
517        std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
518#endif
519
520        if (!lExporter->Initialize(utf8filename.c_str()))
521        {
522            return std::string(lExporter->GetLastErrorString());
523        }
524        if (!lExporter->Export(pScene))
525        {
526            return std::string(lExporter->GetLastErrorString());
527        }
528
529        return WriteResult::FILE_SAVED;
530    }
531    catch (const std::string& s)
532    {
533        return s;
534    }
535    catch (const char* s)
536    {
537        return std::string(s);
538    }
539    catch (...)
540    {
541    }
542
543    return WriteResult::ERROR_IN_WRITING_FILE;
544}
545
546///////////////////////////////////////////////////////////////////////////
547// Add ourself to the Registry to instantiate the reader/writer.
548
549REGISTER_OSGPLUGIN(fbx, ReaderWriterFBX)
Note: See TracBrowser for help on using the browser.