This commit is contained in:
Michael PLATINGS
2010-02-24 10:25:50 +00:00
parent 4f72d66cbe
commit 6ec0e11b2a
11 changed files with 1532 additions and 426 deletions

View File

@@ -7,6 +7,8 @@ SET(TARGET_SRC
fbxRMesh.cpp
fbxRNode.cpp
ReaderWriterFBX.cpp
WriterNodeVisitor.cpp
fbxMaterialToOsgStateSet.cpp
)
SET(TARGET_H
@@ -16,6 +18,8 @@ SET(TARGET_H
fbxRMesh.h
fbxRNode.h
ReaderWriterFBX.h
WriterNodeVisitor.h
fbxMaterialToOsgStateSet.h
)
ADD_DEFINITIONS(-DKFBX_PLUGIN -DKFBX_FBXSDK -DKFBX_NODLL)

View File

@@ -4,18 +4,18 @@
#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/Material>
#include <osg/PositionAttitudeTransform>
#include <osg/Texture2D>
#include <osgDB/ConvertUTF>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/ReadFile>
#include <osgDB/Registry>
#include <osgAnimation/AnimationManagerBase>
#include <osgAnimation/Bone>
#include <osgAnimation/Skeleton>
#include <OpenThreads/ScopedLock>
#if defined(_MSC_VER)
#pragma warning( disable : 4505 )
#endif
@@ -23,20 +23,111 @@
#include "ReaderWriterFBX.h"
#include "fbxRNode.h"
#include "fbxMaterialToOsgStateSet.h"
#include "WriterNodeVisitor.h"
/// Returns true if the given node is a basic root group with no special information.
/// Used in conjunction with UseFbxRoot option.
/// Identity transforms are considered as basic root nodes.
bool isBasicRootNode(const osg::Node& node)
{
const osg::Group* osgGroup = node.asGroup();
if (!osgGroup)
{
// Geodes & such are not basic root nodes
return false;
}
// Test if we've got an empty transform (= a group!)
const osg::Transform* transform = osgGroup->asTransform();
if (transform)
{
if (const osg::MatrixTransform* matrixTransform = transform->asMatrixTransform())
{
if (!matrixTransform->getMatrix().isIdentity())
{
// Non-identity matrix transform
return false;
}
}
else if (const osg::PositionAttitudeTransform* pat = transform->asPositionAttitudeTransform())
{
if (pat->getPosition() != osg::Vec3d() ||
pat->getAttitude() != osg::Quat() ||
pat->getScale() != osg::Vec3d(1.0f, 1.0f, 1.0f) ||
pat->getPivotPoint() != osg::Vec3d())
{
// Non-identity position attribute transform
return false;
}
}
else
{
// Other transform (not identity or not predefined type)
return false;
}
}
// Test the presence of a non-empty stateset
if (node.getStateSet())
{
osg::ref_ptr<osg::StateSet> emptyStateSet = new osg::StateSet;
if (node.getStateSet()->compare(*emptyStateSet, true) != 0)
{
return false;
}
}
return true;
}
class CleanUpFbx
{
KFbxSdkManager* m_pSdkManager;
public:
explicit CleanUpFbx(KFbxSdkManager* pSdkManager) : m_pSdkManager(pSdkManager)
{}
~CleanUpFbx()
{
KFbxIOSettings::IOSettingsRef().FreeIOSettings();
m_pSdkManager->Destroy();
}
};
class ConvertBindMatrixVisitor : public osg::NodeVisitor
{
public:
ConvertBindMatrixVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}
virtual void apply(osg::MatrixTransform& node)
{
if (osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(&node))
{
bone->setInvBindMatrixInSkeletonSpace(osg::Matrix::inverse(bone->getMatrixInBoneSpace()));
if (const osgAnimation::Bone* parent = bone->getBoneParent())
{
bone->setInvBindMatrixInSkeletonSpace(parent->getInvBindMatrixInSkeletonSpace() * bone->getInvBindMatrixInSkeletonSpace());
}
}
traverse(node);
}
};
osgDB::ReaderWriter::ReadResult
ReaderWriterFBX::readNode(const std::string& utf8filename,
const osgDB::ReaderWriter::Options* options) const
ReaderWriterFBX::readNode(const std::string& filenameInit,
const Options* options) const
{
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(_serializerMutex);
try
{
std::string ext(osgDB::getLowerCaseFileExtension(utf8filename));
if(!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string ext(osgDB::getLowerCaseFileExtension(filenameInit));
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string fileName(osgDB::findDataFile(utf8filename, options));
if( fileName.empty()) return ReadResult::FILE_NOT_FOUND;
std::string filename(osgDB::findDataFile(filenameInit, options));
if (filename.empty()) return ReadResult::FILE_NOT_FOUND;
KFbxSdkManager* pSdkManager = KFbxSdkManager::Create();
@@ -45,29 +136,23 @@ ReaderWriterFBX::readNode(const std::string& utf8filename,
return ReadResult::ERROR_IN_READING_FILE;
}
class CleanUpFbx
{
KFbxSdkManager* m_pSdkManager;
public:
CleanUpFbx(KFbxSdkManager* pSdkManager) : m_pSdkManager(pSdkManager)
{}
CleanUpFbx cleanUpFbx(pSdkManager);
~CleanUpFbx()
{
KFbxIOSettings::IOSettingsRef().FreeIOSettings();
m_pSdkManager->Destroy();
}
} cleanUpFbx(pSdkManager);
KFbxScene* pScene = KFbxScene::Create(pSdkManager, "");
KFbxScene* pScene = KFbxScene::Create(pSdkManager,"");
// The FBX SDK interprets the filename as UTF-8
#ifdef OSG_USE_UTF8_FILENAME
const std::string& utf8filename(filename);
#else
std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
#endif
int fileFormat;
if (!pSdkManager->GetIOPluginRegistry()->DetectFileFormat(utf8filename.c_str(), fileFormat))
{
return ReadResult::FILE_NOT_HANDLED;
}
KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager,"");
KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager, "");
lImporter->SetFileFormat(fileFormat);
if (!lImporter->Initialize(utf8filename.c_str()))
@@ -80,7 +165,7 @@ ReaderWriterFBX::readNode(const std::string& utf8filename,
return ReadResult::ERROR_IN_READING_FILE;
}
for(int i = 0; i < lImporter->GetTakeCount(); i++)
for (int i = 0; i < lImporter->GetTakeCount(); i++)
{
KFbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i);
@@ -92,18 +177,47 @@ ReaderWriterFBX::readNode(const std::string& utf8filename,
return std::string(lImporter->GetLastErrorString());
}
//KFbxAxisSystem::OpenGL.ConvertScene(pScene); // Doesn't work as expected. Still need to transform vertices.
if (KFbxNode* pNode = pScene->GetRootNode())
{
bool useFbxRoot = false;
if (options)
{
std::istringstream iss(options->getOptionString());
std::string opt;
while (iss >> opt)
{
if (opt == "UseFbxRoot")
{
useFbxRoot = true;
}
}
}
osg::ref_ptr<osgAnimation::AnimationManagerBase> pAnimationManager;
bool bNeedSkeleton = false;
int nLightCount = 0;
osg::ref_ptr<Options> localOptions = NULL;
if (options)
localOptions = options->cloneOptions();
else
localOptions = new osgDB::Options();
localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_IMAGES);
std::string filePath = osgDB::getFilePath(filename);
FbxMaterialToOsgStateSet fbxMaterialToOsgStateSet(filePath, localOptions.get());
ReadResult res = readFbxNode(*pSdkManager, pNode, pAnimationManager,
osgDB::getFilePath(fileName), bNeedSkeleton, nLightCount);
bNeedSkeleton, nLightCount, fbxMaterialToOsgStateSet, localOptions.get());
if (res.success())
{
osg::Node* osgNode = res.getNode();
if (bNeedSkeleton)
{
ConvertBindMatrixVisitor convertBindMatrixVisitor;
osgNode->accept(convertBindMatrixVisitor);
osgAnimation::Skeleton* osgSkeleton = new osgAnimation::Skeleton;
osgSkeleton->setDefaultUpdateCallback();
osgSkeleton->addChild(osgNode);
@@ -120,16 +234,16 @@ ReaderWriterFBX::readNode(const std::string& utf8filename,
//because the animations may be altered after registering
pAnimationManager->buildTargetReference();
osgNode->setUpdateCallback(pAnimationManager.get());
}
KFbxAxisSystem fbxAxis = pScene->GetGlobalSettings().GetAxisSystem();
int upSign;
KFbxAxisSystem::eUpVector eUp = fbxAxis.GetUpVector(upSign);
bool bLeftHanded = fbxAxis.GetCoorSystem() == KFbxAxisSystem::LeftHanded;
if (eUp != KFbxAxisSystem::YAxis || upSign < 0 || bLeftHanded)
if (fbxAxis != KFbxAxisSystem::OpenGL)
{
int upSign;
KFbxAxisSystem::eUpVector eUp = fbxAxis.GetUpVector(upSign);
bool bLeftHanded = fbxAxis.GetCoorSystem() == KFbxAxisSystem::LeftHanded;
float fSign = upSign < 0 ? -1.0f : 1.0f;
float zScale = bLeftHanded ? -1.0f : 1.0f;
@@ -146,11 +260,36 @@ ReaderWriterFBX::readNode(const std::string& utf8filename,
mat.set(1,0,0,0,0,0,-fSign*zScale,0,0,fSign,0,0,0,0,0,1);
break;
}
osg::MatrixTransform* pTransform = new osg::MatrixTransform(mat);
pTransform->addChild(osgNode);
osgNode = pTransform;
osg::Transform* pTransformTemp = osgNode->asTransform();
osg::MatrixTransform* pMatrixTransform = pTransformTemp ?
pTransformTemp->asMatrixTransform() : NULL;
if (pMatrixTransform)
{
pMatrixTransform->setMatrix(pMatrixTransform->getMatrix() * mat);
}
else
{
pMatrixTransform = new osg::MatrixTransform(mat);
if (useFbxRoot && isBasicRootNode(*osgNode))
{
// If root node is a simple group, put all FBX elements under the OSG root
osg::Group* osgGroup = osgNode->asGroup();
for(unsigned int i = 0; i < osgGroup->getNumChildren(); ++i)
{
pMatrixTransform->addChild(osgGroup->getChild(i));
}
pMatrixTransform->setName(osgGroup->getName());
}
else
{
pMatrixTransform->addChild(osgNode);
}
}
osgNode = pMatrixTransform;
}
osgNode->setName(filenameInit);
return osgNode;
}
}
@@ -162,6 +301,106 @@ ReaderWriterFBX::readNode(const std::string& utf8filename,
return ReadResult::ERROR_IN_READING_FILE;
}
osgDB::ReaderWriter::WriteResult ReaderWriterFBX::writeNode(
const osg::Node& node,
const std::string& filename,
const Options* options) const
{
try
{
std::string ext = osgDB::getLowerCaseFileExtension(filename);
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
osg::ref_ptr<Options> localOptions = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
localOptions->getDatabasePathList().push_front(osgDB::getFilePath(filename));
KFbxSdkManager* pSdkManager = KFbxSdkManager::Create();
if (!pSdkManager)
{
return WriteResult::ERROR_IN_WRITING_FILE;
}
CleanUpFbx cleanUpFbx(pSdkManager);
bool useFbxRoot = false;
if (options)
{
std::istringstream iss(options->getOptionString());
std::string opt;
while (iss >> opt)
{
if (opt == "Embedded")
{
IOSREF.SetBoolProp(EXP_FBX_EMBEDDED, true);
if (KFbxIOSettings::IOSettingsRef().IsIOSettingsAllocated())
KFbxIOSettings::IOSettingsRef().AllocateIOSettings(*pSdkManager);
}
else if (opt == "UseFbxRoot")
{
useFbxRoot = true;
}
}
}
KFbxScene* pScene = KFbxScene::Create(pSdkManager, "");
WriterNodeVisitor writerNodeVisitor(pScene, pSdkManager, filename,
options, osgDB::getFilePath(node.getName().empty() ? filename : node.getName()));
if (useFbxRoot && isBasicRootNode(node))
{
// If root node is a simple group, put all elements under the FBX root
const osg::Group * osgGroup = node.asGroup();
for(unsigned int child=0; child<osgGroup->getNumChildren(); ++child) {
const_cast<osg::Node *>(osgGroup->getChild(child))->accept(writerNodeVisitor);
}
}
else {
// Normal scene
const_cast<osg::Node&>(node).accept(writerNodeVisitor);
}
KFbxExporter* lExporter = KFbxExporter::Create(pSdkManager, "");
pScene->GetGlobalSettings().SetAxisSystem(KFbxAxisSystem::eOpenGL);
// Ensure the directory exists or else the FBX SDK will fail
if (!osgDB::makeDirectoryForFile(filename)) {
osg::notify(osg::NOTICE) << "Can't create directory for file '" << filename << "'. FBX SDK may fail creating the file." << std::endl;
}
// The FBX SDK interprets the filename as UTF-8
#ifdef OSG_USE_UTF8_FILENAME
std::string utf8filename(filename);
#else
std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
#endif
if (!lExporter->Initialize(utf8filename.c_str()))
{
return std::string(lExporter->GetLastErrorString());
}
if (!lExporter->Export(pScene))
{
return std::string(lExporter->GetLastErrorString());
}
return WriteResult::FILE_SAVED;
}
catch (const std::string& s)
{
return s;
}
catch (const char* s)
{
return std::string(s);
}
catch (...)
{
}
return WriteResult::ERROR_IN_WRITING_FILE;
}
///////////////////////////////////////////////////////////////////////////
// Add ourself to the Registry to instantiate the reader/writer.

View File

@@ -1,7 +1,6 @@
#ifndef READERWRITERFBX_H
#define READERWRITERFBX_H
#include <OpenThreads/ReentrantMutex>
#include <osgDB/ReaderWriter>
///////////////////////////////////////////////////////////////////////////
@@ -14,15 +13,14 @@ public:
ReaderWriterFBX()
{
supportsExtension("fbx", "FBX format");
supportsOption("Embedded", "Embed textures in FBX file when writing");
supportsOption("UseFbxRoot", "(Read/write option) If the source OSG root node is a simple group with no stateset, the writer will put its children directly under the FBX root, and vice-versa for reading");
}
const char* className() const { return "FBX reader/writer"; }
/// The FBX SDK interprets the filename as UTF-8
ReadResult readNode(const std::string& utf8filename, const Options*) const;
private:
mutable OpenThreads::ReentrantMutex _serializerMutex;
virtual ReadResult readNode(const std::string& filename, const Options*) const;
virtual WriteResult writeNode(const osg::Node&, const std::string& filename, const Options*) const;
};
///////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,740 @@
// -*-c++-*-
/*
* FBX writer for Open Scene Graph
*
* Copyright (C) 2009
*
* Writing support added 2009 by Thibault Caporal and Sukender (Benoit Neil - http://sukender.free.fr)
*
* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
* real-time rendering of large 3D photo-realistic models.
* The OSG homepage is http://www.openscenegraph.org/
*/
#include <cassert>
#include <sstream>
#include <map>
#include <osg/CullFace>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/NodeVisitor>
#include <osg/PrimitiveSet>
#include <osgDB/FileUtils>
#include <osgDB/WriteFile>
#include "WriterNodeVisitor.h"
#include <limits.h>
/** writes all primitives of a primitive-set out to a stream, decomposes quads to triangles, line-strips to lines etc */
class PrimitiveIndexWriter : public osg::PrimitiveIndexFunctor
{
public:
PrimitiveIndexWriter(const osg::Geometry* geo,
ListTriangle& listTriangles,
unsigned int drawable_n,
unsigned int material) :
_hasNormalCoords(geo->getNormalArray() != NULL),
_hasTexCoords(geo->getTexCoordArray(0) != NULL),
_geo(geo),
_lastFaceIndex(0),
_listTriangles(listTriangles),
_drawable_n(drawable_n),
_material(material)
{
}
unsigned int getNextFaceIndex() { return _lastFaceIndex; }
virtual void setVertexArray(unsigned int, const osg::Vec2*) {}
virtual void setVertexArray(unsigned int, const osg::Vec3*) {}
virtual void setVertexArray(unsigned int, const osg::Vec4*) {}
virtual void setVertexArray(unsigned int, const osg::Vec2d*) {}
virtual void setVertexArray(unsigned int, const osg::Vec3d*) {}
virtual void setVertexArray(unsigned int, const osg::Vec4d*) {}
// operator for triangles
void writeTriangle(unsigned int i1, unsigned int i2, unsigned int i3)
{
Triangle triangle;
triangle.t1 = i1;
triangle.t2 = i2;
triangle.t3 = i3;
triangle.material = _material;
_listTriangles.push_back(std::make_pair(triangle, _drawable_n));
}
virtual void begin(GLenum mode)
{
_modeCache = mode;
_indexCache.clear();
}
virtual void vertex(unsigned int vert)
{
_indexCache.push_back(vert);
}
virtual void end()
{
if (!_indexCache.empty())
{
drawElements(_modeCache, _indexCache.size(), &_indexCache.front());
}
}
virtual void drawArrays(GLenum mode, GLint first, GLsizei count);
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices)
{
drawElementsImplementation<GLubyte>(mode, count, indices);
}
virtual void drawElements(GLenum mode, GLsizei count, const GLushort* indices)
{
drawElementsImplementation<GLushort>(mode, count, indices);
}
virtual void drawElements(GLenum mode, GLsizei count, const GLuint* indices)
{
drawElementsImplementation<GLuint>(mode, count, indices);
}
protected:
template <typename T> void drawElementsImplementation(GLenum mode, GLsizei count, const T* indices)
{
if (indices==0 || count==0) return;
typedef const T* IndexPointer;
switch (mode)
{
case GL_TRIANGLES:
{
IndexPointer ilast = indices + count;
for (IndexPointer iptr = indices; iptr < ilast; iptr+=3)
writeTriangle(iptr[0], iptr[1], iptr[2]);
break;
}
case GL_TRIANGLE_STRIP:
{
IndexPointer iptr = indices;
for (GLsizei i = 2; i < count; ++i, ++iptr)
{
if (i & 1) writeTriangle(iptr[0], iptr[2], iptr[1]);
else writeTriangle(iptr[0], iptr[1], iptr[2]);
}
break;
}
case GL_QUADS:
{
IndexPointer iptr = indices;
for (GLsizei i = 3; i < count; i += 4, iptr += 4)
{
writeTriangle(iptr[0], iptr[1], iptr[2]);
writeTriangle(iptr[0], iptr[2], iptr[3]);
}
break;
}
case GL_QUAD_STRIP:
{
IndexPointer iptr = indices;
for (GLsizei i = 3; i < count; i += 2, iptr += 2)
{
writeTriangle(iptr[0], iptr[1], iptr[2]);
writeTriangle(iptr[1], iptr[3], iptr[2]);
}
break;
}
case GL_POLYGON: // treat polygons as GL_TRIANGLE_FAN
case GL_TRIANGLE_FAN:
{
IndexPointer iptr = indices;
unsigned int first = *iptr;
++iptr;
for (GLsizei i = 2; i < count; ++i, ++iptr)
{
writeTriangle(first, iptr[0], iptr[1]);
}
break;
}
case GL_POINTS:
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
// Not handled
break;
default:
// uhm should never come to this point :)
break;
}
}
private:
PrimitiveIndexWriter& operator = (const PrimitiveIndexWriter&) { return *this; }
unsigned int _drawable_n;
ListTriangle& _listTriangles;
GLenum _modeCache;
std::vector<GLuint> _indexCache;
bool _hasNormalCoords, _hasTexCoords;
const osg::Geometry* _geo;
unsigned int _lastFaceIndex;
int _material;
KFbxMesh* _mesh;
};
void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count)
{
unsigned int pos=first;
switch (mode)
{
case GL_TRIANGLES:
for (GLsizei i = 2; i < count; i += 3, pos += 3)
{
writeTriangle(pos, pos + 1, pos + 2);
}
break;
case GL_TRIANGLE_STRIP:
for (GLsizei i = 2; i < count; ++i, ++pos)
{
if (i & 1) writeTriangle(pos, pos + 2, pos + 1);
else writeTriangle(pos, pos + 1, pos + 2);
}
break;
case GL_QUADS:
for (GLsizei i = 3; i < count; i += 4, pos += 4)
{
writeTriangle(pos, pos + 1, pos + 2);
writeTriangle(pos, pos + 2, pos + 3);
}
break;
case GL_QUAD_STRIP:
for (GLsizei i = 3; i < count; i += 2, pos += 2)
{
writeTriangle(pos, pos + 1, pos + 2);
writeTriangle(pos + 1, pos + 3, pos + 2);
}
break;
case GL_POLYGON: // treat polygons as GL_TRIANGLE_FAN
case GL_TRIANGLE_FAN:
pos = first + 1;
for (GLsizei i = 2; i < count; ++i, ++pos)
{
writeTriangle(first, pos, pos+1);
}
break;
case GL_POINTS:
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
default:
osg::notify(osg::WARN) << "WriterNodeVisitor :: can't handle mode " << mode << std::endl;
break;
}
}
// If 'to' is in a subdirectory of 'from' then this function returns the
// subpath. Otherwise it just returns the file name.
std::string getPathRelative(const std::string& from/*directory*/,
const std::string& to/*file path*/)
{
std::string::size_type slash = to.find_last_of('/');
std::string::size_type backslash = to.find_last_of('\\');
if (slash == std::string::npos)
{
if (backslash == std::string::npos) return to;
slash = backslash;
}
else if (backslash != std::string::npos && backslash > slash)
{
slash = backslash;
}
if (from.empty() || from.length() > to.length())
return osgDB::getSimpleFileName(to);
std::string::const_iterator itTo = to.begin();
for (std::string::const_iterator itFrom = from.begin();
itFrom != from.end(); ++itFrom, ++itTo)
{
char a = tolower(*itFrom), b = tolower(*itTo);
if (a == '\\') a = '/';
if (b == '\\') b = '/';
if (a != b || itTo == to.begin() + slash + 1)
{
return osgDB::getSimpleFileName(to);
}
}
while (itTo != to.end() && (*itTo == '\\' || *itTo == '/'))
{
++itTo;
}
return std::string(itTo, to.end());
}
//std::string testA = getPathRelative("C:\\a\\b", "C:\\a/b/d/f");
//std::string testB = getPathRelative("C:\\a\\d", "C:\\a/b/d/f");
//std::string testC = getPathRelative("C:\\ab", "C:\\a/b/d/f");
//std::string testD = getPathRelative("a/d", "a/d");
WriterNodeVisitor::Material::Material(WriterNodeVisitor& writerNodeVisitor,
const std::string& srcDirectory,
const osg::StateSet* stateset,
const osg::Material* mat,
const osg::Texture* tex,
KFbxSdkManager* pSdkManager,
const std::string& directory,
ImageSet& imageSet,
ImageFilenameSet& imageFilenameSet,
unsigned int& lastGeneratedImageFileName,
int index) :
_index(index),
_fbxMaterial(NULL),
_fbxTexture(NULL),
_osgImage(NULL),
_directory(directory)
{
osg::Vec4 diffuse(1,1,1,1),
ambient(0.2,0.2,0.2,1),
specular(0,0,0,1),
emission(1,1,1,1);
float shininess(0);
float transparency(0);
if (mat)
{
assert(stateset);
diffuse = mat->getDiffuse(osg::Material::FRONT);
ambient = mat->getAmbient(osg::Material::FRONT);
specular = mat->getSpecular(osg::Material::FRONT);
shininess = mat->getShininess(osg::Material::FRONT);
emission = mat->getEmission(osg::Material::FRONT);
transparency = 1 - diffuse.w();
const osg::StateAttribute* attribute = stateset->getAttribute(osg::StateAttribute::CULLFACE);
if (attribute)
{
assert(dynamic_cast<const osg::CullFace*>(attribute));
osg::CullFace::Mode mode = static_cast<const osg::CullFace*>(attribute)->getMode();
if (mode == osg::CullFace::FRONT)
{
osg::notify(osg::WARN) << "FBX Writer: Reversed face (culled FRONT) not supported yet." << std::endl;
}
else if (mode != osg::CullFace::BACK)
{
assert(mode == osg::CullFace::FRONT_AND_BACK);
osg::notify(osg::WARN) << "FBX Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl;
}
}
_fbxMaterial = KFbxSurfacePhong::Create(pSdkManager, mat->getName().c_str());
if (_fbxMaterial)
{
_fbxMaterial->GetDiffuseFactor().Set(1, true);
_fbxMaterial->GetDiffuseColor().Set(fbxDouble3(
diffuse.x(),
diffuse.y(),
diffuse.z()));
_fbxMaterial->GetTransparencyFactor().Set(transparency);
_fbxMaterial->GetAmbientColor().Set(fbxDouble3(
ambient.x(),
ambient.y(),
ambient.z()));
_fbxMaterial->GetEmissiveColor().Set(fbxDouble3(
emission.x(),
emission.y(),
emission.z()));
_fbxMaterial->GetSpecularColor().Set(fbxDouble3(
specular.x(),
specular.y(),
specular.z()));
_fbxMaterial->GetShininess().Set(shininess);
}
}
if (tex && tex->getImage(0))
{
_osgImage = tex->getImage(0);
ImageSet::iterator it = imageSet.find(_osgImage);
if (it == imageSet.end())
{
std::string canonicalPath( osgDB::getRealPath(osgDB::convertFileNameToNativeStyle(_osgImage->getFileName())) );
std::string destPath;
std::string relativePath;
if (canonicalPath.empty())
{
static const unsigned int MAX_IMAGE_NUMBER = UINT_MAX-1; // -1 to allow doing +1 without an overflow
unsigned int imageNumber;
for (imageNumber=lastGeneratedImageFileName+1; imageNumber<MAX_IMAGE_NUMBER; ++imageNumber)
{
std::ostringstream oss;
oss << "Image_" << imageNumber << ".tga";
relativePath = oss.str();
destPath = osgDB::concatPaths(_directory, relativePath);
if (imageFilenameSet.find(destPath) != imageFilenameSet.end()) break;
}
lastGeneratedImageFileName = imageNumber;
osgDB::writeImageFile(*_osgImage, destPath);
}
else
{
relativePath = getPathRelative(srcDirectory, canonicalPath);
destPath = osgDB::getRealPath(osgDB::convertFileNameToNativeStyle( osgDB::concatPaths(_directory, relativePath) ));
if (destPath != canonicalPath)
{
osgDB::writeImageFile(*_osgImage, destPath);
}
}
assert(!destPath.empty()); // Else the implementation is to be fixed
assert(!relativePath.empty()); // ditto
it = imageSet.insert(ImageSet::value_type(_osgImage, relativePath)).first;
imageFilenameSet.insert(destPath);
}
_fbxTexture = KFbxTexture::Create(pSdkManager, it->second.c_str());
_fbxTexture->SetFileName(it->second.c_str());
}
}
int WriterNodeVisitor::processStateSet(const osg::StateSet* ss)
{
//osg::notify(osg::ALWAYS) << "Trying Adding " << ss->getAttribute(osg::StateAttribute::MATERIAL)->getName() << std::endl;
MaterialMap::iterator itr = _materialMap.find(MaterialMap::key_type(ss));
if (itr != _materialMap.end())
{
if (itr->second.getIndex() < 0)
itr->second.setIndex(_lastMaterialIndex++);
return itr->second.getIndex();
}
const osg::Material* mat = dynamic_cast<const osg::Material*>(ss->getAttribute(osg::StateAttribute::MATERIAL));
const osg::Texture* tex = dynamic_cast<const osg::Texture*>(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
if (mat || tex)
{
int matNum = _lastMaterialIndex;
_materialMap.insert(MaterialMap::value_type(MaterialMap::key_type(ss),
Material(*this, _srcDirectory, ss, mat, tex, _pSdkManager, _directory, _imageSet, _imageFilenameSet, _lastGeneratedImageFileName, matNum)));
++_lastMaterialIndex;
return matNum;
}
return -1;
}
/**
* Add a vertex to the index and link him with the Triangle index and the drawable.
* \param index_vert is the map where the vertices are stored.
* \param index is the index of the vertices position in the vec3.
* \param drawable_n is the number of the drawable.
* \return the position of the vertex in the final mesh.
*/
unsigned int
WriterNodeVisitor::getMeshIndexForGeometryIndex(MapIndices& index_vert,
unsigned int index,
unsigned int drawable_n)
{
MapIndices::iterator itIndex = index_vert.find(std::make_pair(index, drawable_n));
if (itIndex == index_vert.end())
{
unsigned int indexMesh = index_vert.size();
index_vert.insert(std::make_pair(std::make_pair(index, drawable_n), indexMesh));
return indexMesh;
}
return itIndex->second;
}
void
WriterNodeVisitor::setLayerTextureAndMaterial(KFbxMesh* mesh)
{
KFbxLayerElementTexture* lTextureDiffuseLayer = KFbxLayerElementTexture::Create(mesh, "Diffuse");
lTextureDiffuseLayer->SetMappingMode(KFbxLayerElement::eBY_POLYGON);
lTextureDiffuseLayer->SetReferenceMode(KFbxLayerElement::eINDEX_TO_DIRECT);
KFbxLayerElementMaterial* lMaterialLayer = KFbxLayerElementMaterial::Create(mesh, "materialLayer");
lMaterialLayer->SetMappingMode(KFbxLayerElement::eBY_POLYGON);
lMaterialLayer->SetReferenceMode(KFbxLayerElement::eINDEX_TO_DIRECT);
lTextureDiffuseLayer->GetDirectArray().SetCount(_lastMaterialIndex);
lMaterialLayer->GetDirectArray().SetCount(_lastMaterialIndex);
for (MaterialMap::iterator it = _materialMap.begin(); it != _materialMap.end(); ++it)
{
if (it->second.getIndex() != -1)
{
KFbxSurfaceMaterial* lMaterial = it->second.getFbxMaterial();
KFbxTexture* lTexture = it->second.getFbxTexture();
lTextureDiffuseLayer->GetDirectArray().SetAt(it->second.getIndex(), lTexture);
lMaterialLayer->GetDirectArray().SetAt(it->second.getIndex(), lMaterial);
}
}
mesh->GetLayer(0)->SetMaterials(lMaterialLayer);
mesh->GetLayer(0)->SetDiffuseTextures(lTextureDiffuseLayer);
}
void
WriterNodeVisitor::setControlPointAndNormalsAndUV(const osg::Geode& geo,
MapIndices& index_vert,
bool texcoords,
KFbxMesh* mesh)
{
mesh->InitControlPoints(index_vert.size());
KFbxLayerElementNormal* lLayerElementNormal= KFbxLayerElementNormal::Create(mesh, "");
lLayerElementNormal->SetMappingMode(KFbxLayerElement::eBY_CONTROL_POINT);
lLayerElementNormal->SetReferenceMode(KFbxLayerElement::eDIRECT);
lLayerElementNormal->GetDirectArray().SetCount(index_vert.size());
mesh->GetLayer(0)->SetNormals(lLayerElementNormal);
KFbxLayerElementUV* lUVDiffuseLayer = KFbxLayerElementUV::Create(mesh, "DiffuseUV");
if (texcoords)
{
lUVDiffuseLayer->SetMappingMode(KFbxLayerElement::eBY_CONTROL_POINT);
lUVDiffuseLayer->SetReferenceMode(KFbxLayerElement::eDIRECT);
lUVDiffuseLayer->GetDirectArray().SetCount(index_vert.size());
mesh->GetLayer(0)->SetUVs(lUVDiffuseLayer, KFbxLayerElement::eDIFFUSE_TEXTURES);
}
for (MapIndices::iterator it = index_vert.begin(); it != index_vert.end(); ++it)
{
const osg::Geometry* pGeometry = geo.getDrawable(it->first.second)->asGeometry();
assert(pGeometry->getVertexArray());
if (pGeometry->getVertexArray()->getType() != osg::Array::Vec3ArrayType)
{
throw "Vertex array is not Vec3. Not implemented"; // TODO
}
const osg::Vec3Array* vecs= static_cast<const osg::Vec3Array*>(pGeometry->getVertexArray());
if (vecs)
{
mesh->SetControlPointAt(*new KFbxVector4(
(*vecs)[it->first.first].x(),
(*vecs)[it->first.first].y(),
(*vecs)[it->first.first].z()), it->second);
}
const osg::Vec3Array* pNormals = static_cast<const osg::Vec3Array*>(pGeometry->getNormalArray());
if (pNormals)
{
switch (pGeometry->getNormalBinding())
{
case osg::Geometry::BIND_PER_PRIMITIVE_SET:
case osg::Geometry::BIND_PER_PRIMITIVE:
lLayerElementNormal->GetDirectArray().SetAt(it->second,
KFbxVector4(
(*pNormals)[0].x(),
(*pNormals)[0].y(),
(*pNormals)[0].z(), 0));
break;
case osg::Geometry::BIND_PER_VERTEX:
lLayerElementNormal->GetDirectArray().SetAt(it->second,
KFbxVector4(
(*pNormals)[it->first.first].x(),
(*pNormals)[it->first.first].y(),
(*pNormals)[it->first.first].z(), 0));
break;
}
}
if (texcoords)
{
const osg::Vec2Array* vec2= static_cast<const osg::Vec2Array*>(pGeometry->getTexCoordArray(0));
if (vec2)
lUVDiffuseLayer->GetDirectArray().SetAt(it->second, KFbxVector2((*vec2)[it->first.first].x(), (*vec2)[it->first.first].y()));
}
}
}
void WriterNodeVisitor::buildFaces(const osg::Geode& geo,
ListTriangle& listTriangles,
bool texcoords)
{
MapIndices index_vert;
KFbxMesh* mesh = KFbxMesh::Create(_pSdkManager, geo.getName().c_str());
_curFbxNode->AddNodeAttribute(mesh);
_curFbxNode->SetShadingMode(KFbxNode::eTEXTURE_SHADING);
KFbxLayer* lLayer = mesh->GetLayer(0);
if (lLayer == NULL)
{
mesh->CreateLayer();
lLayer = mesh->GetLayer(0);
}
setLayerTextureAndMaterial(mesh);
lLayer->GetDiffuseTextures()->GetIndexArray().SetCount(listTriangles.size());
lLayer->GetMaterials()->GetIndexArray().SetCount(listTriangles.size());
unsigned int i = 0;
for (ListTriangle::iterator it = listTriangles.begin(); it != listTriangles.end(); ++it, ++i) //Go through the triangle list to define meshs
{
if (it->first.material == -1)
{
mesh->BeginPolygon();
}
else
{
mesh->BeginPolygon(i);
lLayer->GetDiffuseTextures()->GetIndexArray().SetAt(i, it->first.material);
lLayer->GetMaterials()->GetIndexArray().SetAt(i, it->first.material);
}
mesh->AddPolygon(getMeshIndexForGeometryIndex(index_vert, it->first.t1, it->second));
mesh->AddPolygon(getMeshIndexForGeometryIndex(index_vert, it->first.t2, it->second));
mesh->AddPolygon(getMeshIndexForGeometryIndex(index_vert, it->first.t3, it->second));
mesh->EndPolygon();
}
setControlPointAndNormalsAndUV(geo, index_vert, texcoords, mesh);
}
void WriterNodeVisitor::createListTriangle(const osg::Geometry* geo,
ListTriangle& listTriangles,
bool& texcoords,
unsigned int& drawable_n)
{
unsigned int nbVertices = 0;
{
if (geo->getVertexArray() && geo->getVertexArray()->getType() != osg::Array::Vec3ArrayType)
{
throw "Vertex array is not Vec3. Not implemented"; // TODO
}
const osg::Vec3Array* vecs = static_cast<const osg::Vec3Array*>(geo->getVertexArray());
if (vecs)
{
nbVertices = geo->getVertexArray()->getNumElements();
// Texture coords
if (geo->getTexCoordArray(0) && geo->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType)
{
throw "Texture coords array is not Vec2. Not implemented"; // TODO
}
const osg::Vec2Array* texvecs = static_cast<const osg::Vec2Array*>(geo->getTexCoordArray(0));
if (texvecs)
{
unsigned int nb = geo->getTexCoordArray(0)->getNumElements();
if (nb != geo->getVertexArray()->getNumElements())
{
throw "There are more/less texture coords than vertices!";
}
texcoords = true;
}
}
}
if (nbVertices==0) return;
int material = processStateSet(_currentStateSet.get());
for (unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i) //Fill the Triangle List
{
const osg::PrimitiveSet* ps = geo->getPrimitiveSet(i);
PrimitiveIndexWriter pif(geo, listTriangles, drawable_n, material);
const_cast<osg::PrimitiveSet*>(ps)->accept(pif);
}
}
void WriterNodeVisitor::apply(osg::Geode& node)
{
KFbxNode* parent = _curFbxNode;
KFbxNode* nodeFBX = KFbxNode::Create(_pSdkManager, node.getName().empty() ? "DefaultName" : node.getName().c_str());
_curFbxNode->AddChild(nodeFBX);
_curFbxNode = nodeFBX;
if (false)
{
KFbxProperty lProperty = KFbxProperty::Create(_curFbxNode, "nameGeode", DTString, "label2");
std::ostringstream oss;
oss << node.getName().c_str() << ".metaData";
lProperty.Set(new KString(oss.str().c_str()), eSTRING);
}
unsigned int count = node.getNumDrawables();
ListTriangle listTriangles;
bool texcoords = false;
for (MaterialMap::iterator it = _materialMap.begin(); it != _materialMap.end(); ++it)
it->second.setIndex(-1);
_lastMaterialIndex = 0;
for (unsigned int i = 0; i < count; ++i)
{
const osg::Geometry* g = node.getDrawable(i)->asGeometry();
if (g != NULL)
{
pushStateSet(g->getStateSet());
createListTriangle(g, listTriangles, texcoords, i);
popStateSet(g->getStateSet());
}
}
if (count > 0)
{
buildFaces(node, listTriangles, texcoords);
}
if (succeedLastApply())
traverse(node);
_curFbxNode = parent;
}
void WriterNodeVisitor::apply(osg::Group& node)
{
KFbxNode* parent = _curFbxNode;
KFbxNode* nodeFBX = KFbxNode::Create(_pSdkManager, node.getName().empty() ? "DefaultName" : node.getName().c_str());
_curFbxNode->AddChild(nodeFBX);
_curFbxNode = nodeFBX;
if (false)
{
KFbxProperty lProperty = KFbxProperty::Create(_curFbxNode, "nameGeode", DTString, "label2");
std::ostringstream oss;
oss << node.getName().c_str() << ".metaData";
lProperty.Set(new KString(oss.str().c_str()), eSTRING);
}
traverse(node);
_curFbxNode = parent;
}
void WriterNodeVisitor::apply(osg::MatrixTransform& node)
{
KFbxNode* parent = _curFbxNode;
_curFbxNode = KFbxNode::Create(_pSdkManager, node.getName().empty() ? "DefaultName" : node.getName().c_str());
parent->AddChild(_curFbxNode);
if (false)
{
KFbxProperty lProperty = KFbxProperty::Create(_curFbxNode, "nameGeode", DTString, "label2");
std::ostringstream oss;
oss << node.getName().c_str() << ".metaData";
lProperty.Set(new KString(oss.str().c_str()), eSTRING);
}
const osg::Matrix& matrix = node.getMatrix();
osg::Vec3d pos, scl;
osg::Quat rot, so;
matrix.decompose(pos, rot, scl, so);
_curFbxNode->LclTranslation.Set(fbxDouble3(pos.x(), pos.y(), pos.z()));
_curFbxNode->LclScaling.Set(fbxDouble3(scl.x(), scl.y(), scl.z()));
KFbxXMatrix mat;
KFbxQuaternion q(rot.x(), rot.y(), rot.z(), rot.w());
mat.SetQ(q);
KFbxVector4 vec4 = mat.GetR();
_curFbxNode->LclRotation.Set(fbxDouble3(vec4[0], vec4[1], vec4[2]));
traverse(node);
_curFbxNode = parent;
}

View File

@@ -0,0 +1,256 @@
// -*-c++-*-
/*
* FBX writer for Open Scene Graph
*
* Copyright (C) 2009
*
* Writing support added 2009 by Thibault Caporal and Sukender (Benoit Neil - http://sukender.free.fr)
*
* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
* real-time rendering of large 3D photo-realistic models.
* The OSG homepage is http://www.openscenegraph.org/
*/
#ifndef _FBX_WRITER_NODE_VISITOR_HEADER__
#define _FBX_WRITER_NODE_VISITOR_HEADER__
#include <map>
#include <set>
#include <stack>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/NodeVisitor>
#include <osg/PrimitiveSet>
#include <osgDB/FileNameUtils>
#include <osgDB/ReaderWriter>
#if defined(_MSC_VER)
#pragma warning( disable : 4505 )
#endif
#include <fbxsdk.h>
struct Triangle
{
unsigned int t1;
unsigned int t2;
unsigned int t3;
int material;
};
typedef std::map<std::pair<unsigned int, unsigned int>, unsigned int> MapIndices;
typedef std::vector<std::pair<Triangle, int> > ListTriangle; //the int is the drawable of the triangle
///\author Capo (Thibault Caporal)
class WriterNodeVisitor: public osg::NodeVisitor
{
public:
WriterNodeVisitor(KFbxScene* pScene,
KFbxSdkManager* pSdkManager,
const std::string& fileName,
const osgDB::ReaderWriter::Options* options,
const std::string& srcDirectory) :
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_pScene(pScene),
_pSdkManager(pSdkManager),
_currentStateSet(new osg::StateSet()),
_lastMaterialIndex(0),
_lastMeshIndex(0),
_lastGeneratedImageFileName(0),
_curFbxNode(pScene->GetRootNode()),
_options(options),
_succeedLastApply(true),
_directory(osgDB::getFilePath(fileName)),
_srcDirectory(srcDirectory)
{}
///Tell us if last Node succeed traversing.
bool succeedLastApply() const { return _succeedLastApply; }
///Set the flag _succeedLastApply to false.
void failedApply() { _succeedLastApply = false; }
virtual void apply(osg::Geode& node);
virtual void apply(osg::Group& node);
virtual void apply(osg::MatrixTransform& node);
void traverse (osg::Node& node)
{
pushStateSet(node.getStateSet());
osg::NodeVisitor::traverse(node);
popStateSet(node.getStateSet());
}
void pushStateSet(const osg::StateSet* ss)
{
if (ss)
{
// Save our current stateset
_stateSetStack.push(_currentStateSet.get());
// merge with node stateset
_currentStateSet = static_cast<osg::StateSet*>(
_currentStateSet->clone(osg::CopyOp::SHALLOW_COPY));
_currentStateSet->merge(*ss);
}
}
void popStateSet(const osg::StateSet* ss)
{
if (ss)
{
// restore the previous stateset
_currentStateSet = _stateSetStack.top();
_stateSetStack.pop();
}
}
/// Copy the texture file in current path.
void copyTexture();
typedef std::map<const osg::Image*, std::string> ImageSet;
typedef std::set<std::string> ImageFilenameSet; // Sub-optimal because strings are doubled (in ImageSet). Moreover, an unordered_set (= hashset) would be more efficient (Waiting for unordered_set to be included in C++ standard ;) ).
///\todo Add support for 2nd texture, opacity_map, bump_map, specular_map, shininess_map, self_illum_map, reflection_map.
class Material
{
public:
///Create a KfbxMaterial and KfbxTexture from osg::Texture and osg::Material.
Material(WriterNodeVisitor& writerNodeVisitor,
const std::string& srcDirectory,
const osg::StateSet* stateset,
const osg::Material* mat,
const osg::Texture* tex,
KFbxSdkManager* pSdkManager,
const std::string& directory,
ImageSet& imageSet,
ImageFilenameSet& imageFilenameSet,
unsigned int& lastGeneratedImageFileName,
int index = -1);
KFbxTexture* getFbxTexture() const
{
return _fbxTexture;
}
KFbxSurfaceMaterial* getFbxMaterial() const
{
return _fbxMaterial;
}
const osg::Image* getOsgImage() const
{
return _osgImage;
}
const int getIndex() const
{
return _index;
}
void setIndex(int index)
{
_index = index;
}
private:
KFbxSurfacePhong* _fbxMaterial;
KFbxTexture* _fbxTexture;
int _index;///< Index in the Map
const osg::Image* _osgImage;
const std::string& _directory;
};
protected:
/// Compares StateSets.
///\todo It may be useful to compare stack of pointers (see pushStateset()) in order to keep the same number of FBX materials when doing reading and then writing without further processing.
struct CompareStateSet
{
bool operator () (const osg::ref_ptr<const osg::StateSet>& ss1, const osg::ref_ptr<const osg::StateSet>& ss2) const
{
return *ss1 < *ss2;
}
};
private:
/**
* Fill the faces field of the mesh and call buildMesh().
* \param geo is the geode which contains the vertices and faces.
* \param listTriangles contain all the mesh's faces.
* \param texcoords tell us if we have to handle texture coordinates.
*/
void buildFaces(const osg::Geode& geo,
ListTriangle& listTriangles,
bool texcoords);
/// Set the layer for texture and Material in layer 0.
void setLayerTextureAndMaterial(KFbxMesh* mesh);
/// Set Vertices, normals, and UVs
void setControlPointAndNormalsAndUV(const osg::Geode& geo,
MapIndices& index_vert,
bool texcoords,
KFbxMesh* fbxMesh);
/**
* Add a vertex to the index and link him with the Triangle index and the drawable.
* \param index_vert is the map where the vertices are stored.
* \param index is the indices of the vertices position in the vec3.
* \param drawable_n is the number of the drawable.
* \return the position of the vertices in the final mesh.
*/
unsigned int getMeshIndexForGeometryIndex(MapIndices& index_vert,
unsigned int index,
unsigned int drawable_n);
/**
* Create the list of faces from the geode.
* \param geo is the geode to study.
* \param listTriangles is the list to fill.
* \param texcoords tell us if we have to treat texture coord.
* \param drawable_n tell us which drawable we are building.
*/
void createListTriangle(const osg::Geometry* geo,
ListTriangle& listTriangles,
bool& texcoords,
unsigned int& drawable_n);
///Store the material of the stateset in the MaterialMap.
int processStateSet(const osg::StateSet* stateset);
typedef std::stack<osg::ref_ptr<osg::StateSet> > StateSetStack;
typedef std::map<osg::ref_ptr<const osg::StateSet>, Material, CompareStateSet> MaterialMap;
///We need this for every new Node we create.
KFbxSdkManager* _pSdkManager;
///Tell us if the last apply succeed, useful to stop going through the graph.
bool _succeedLastApply;
///The current directory.
std::string _directory;
///The Scene to save.
KFbxScene* _pScene;
///The current Fbx Node.
KFbxNode* _curFbxNode;
///The Stack of different stateSet.
StateSetStack _stateSetStack;
///The current stateSet.
osg::ref_ptr<osg::StateSet> _currentStateSet;
///We store the fbx Materials and Textures in this map.
MaterialMap _materialMap;
ImageSet _imageSet;
ImageFilenameSet _imageFilenameSet;
unsigned int _lastGeneratedImageFileName;
unsigned int _lastMaterialIndex;
unsigned int _lastMeshIndex;
const osgDB::ReaderWriter::Options* _options;
const std::string _srcDirectory;
};
#endif // _FBX_WRITER_NODE_VISITOR_HEADER__

View File

@@ -4,7 +4,6 @@
#include <osgAnimation/BasicAnimationManager>
#include <osgAnimation/Channel>
#include <osgAnimation/Sampler>
#include <osgAnimation/UpdateCallback>
#if defined(_MSC_VER)
#pragma warning( disable : 4505 )
@@ -24,8 +23,7 @@ osg::Quat makeQuat(const osg::Vec3& radians, ERotationOrder fbxRotOrder)
}
void readKeys(KFCurve* curveX, KFCurve* curveY, KFCurve* curveZ,
float scalar, const osg::Vec3& baseValue, bool multiply,
std::vector<osgAnimation::TemplateKeyframe<osg::Vec3> >& keyFrameCntr)
std::vector<osgAnimation::TemplateKeyframe<osg::Vec3> >& keyFrameCntr, float scalar = 1.0f)
{
KFCurve* curves[3] = {curveX, curveY, curveZ};
@@ -58,36 +56,25 @@ void readKeys(KFCurve* curveX, KFCurve* curveY, KFCurve* curveZ,
for (TimeSet::iterator it = times.begin(); it != times.end(); ++it)
{
float fTime = *it;
osg::Vec3 val(baseValue);
osg::Vec3 val;
for (int i = 0; i < 3; ++i)
{
if (curveTimeMap[i].empty()) continue;
TimeFloatMap::iterator lb = curveTimeMap[i].lower_bound(fTime);
if (lb == curveTimeMap[i].end()) --lb;
if (multiply)
{
val[i] *= lb->second;
}
else
{
val[i] += lb->second;
}
val[i] = lb->second;
}
keyFrameCntr.push_back(osgAnimation::Vec3Keyframe(fTime, val));
}
}
osgAnimation::Channel* readFbxChannels(KFCurve* curveX, KFCurve* curveY,
KFCurve* curveZ, const char* targetName, const char* channelName,
float scalar, const osg::Vec3& baseValue, bool multiply)
KFCurve* curveZ, const char* targetName, const char* channelName)
{
if (!curveX && !curveY && !curveZ)
{
return 0;
}
if (!curveX->KeyGetCount() && !curveY->KeyGetCount() && !curveZ->KeyGetCount())
if (!(curveX && curveX->KeyGetCount()) &&
!(curveY && curveY->KeyGetCount()) &&
!(curveZ && curveZ->KeyGetCount()))
{
return 0;
}
@@ -98,14 +85,14 @@ osgAnimation::Channel* readFbxChannels(KFCurve* curveX, KFCurve* curveY,
pChannel->setTargetName(targetName);
pChannel->setName(channelName);
readKeys(curveX, curveY, curveZ, scalar, baseValue, multiply, *pKeyFrameCntr);
readKeys(curveX, curveY, curveZ, *pKeyFrameCntr);
return pChannel;
}
osgAnimation::Channel* readFbxChannels(
KFbxTypedProperty<fbxDouble3>& fbxProp, const char* pTakeName,
const char* targetName, const char* channelName, const osg::Vec3& baseValue, float scalar, bool multiply)
const char* targetName, const char* channelName)
{
if (!fbxProp.IsValid()) return 0;
@@ -113,15 +100,16 @@ osgAnimation::Channel* readFbxChannels(
fbxProp.GetKFCurve("X", pTakeName),
fbxProp.GetKFCurve("Y", pTakeName),
fbxProp.GetKFCurve("Z", pTakeName),
targetName, channelName, scalar,
baseValue * scalar, multiply);
targetName, channelName);
}
osgAnimation::Channel* readFbxChannelsQuat(
KFCurve* curveX, KFCurve* curveY, KFCurve* curveZ, const char* targetName,
const osg::Quat& baseQuat, ERotationOrder rotOrder)
ERotationOrder rotOrder)
{
if (!curveX && !curveY && !curveZ)
if (!(curveX && curveX->KeyGetCount()) &&
!(curveY && curveY->KeyGetCount()) &&
!(curveZ && curveZ->KeyGetCount()))
{
return 0;
}
@@ -131,7 +119,7 @@ osgAnimation::Channel* readFbxChannelsQuat(
pChannel->setName("quaternion");
typedef std::vector<osgAnimation::TemplateKeyframe<osg::Vec3> > KeyFrameCntr;
KeyFrameCntr eulerFrameCntr;
readKeys(curveX, curveY, curveZ, static_cast<float>(osg::PI / 180.0), osg::Vec3(0,0,0), false, eulerFrameCntr);
readKeys(curveX, curveY, curveZ, eulerFrameCntr, static_cast<float>(osg::PI / 180.0));
osgAnimation::QuatSphericalLinearSampler::KeyframeContainerType& quatFrameCntr =
*pChannel->getOrCreateSampler()->getOrCreateKeyframeContainer();
@@ -142,7 +130,7 @@ osgAnimation::Channel* readFbxChannelsQuat(
{
const osg::Vec3& euler = it->getValue();
quatFrameCntr.push_back(osgAnimation::QuatKeyframe(
it->getTime(), makeQuat(euler, rotOrder) * baseQuat));
it->getTime(), makeQuat(euler, rotOrder)));
}
return pChannel;
@@ -152,7 +140,7 @@ osgAnimation::Animation* addChannels(
osgAnimation::Channel* pTranslationChannel,
osgAnimation::Channel* pRotationChannel,
osgAnimation::Channel* pScaleChannel,
osg::ref_ptr<osgAnimation::AnimationManagerBase> &pAnimManager,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimManager,
const char* pTakeName)
{
if (pTranslationChannel ||
@@ -189,7 +177,7 @@ osgAnimation::Animation* addChannels(
return 0;
}
osgAnimation::Animation* readFbxBoneAnimation(KFbxNode* pNode,
osgAnimation::Animation* readFbxAnimation(KFbxNode* pNode,
const char* pTakeName, const char* targetName,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimManager)
{
@@ -199,99 +187,36 @@ osgAnimation::Animation* readFbxBoneAnimation(KFbxNode* pNode,
}
ERotationOrder rotOrder = pNode->RotationOrder.IsValid() ? pNode->RotationOrder.Get() : eEULER_XYZ;
osg::Quat inverseRot;
osgAnimation::Channel* pTranslationChannel = 0;
osgAnimation::Channel* pRotationChannel = 0;
if (pNode->LclRotation.IsValid())
{
inverseRot = makeQuat(pNode->LclRotation.Get(), rotOrder).inverse();
fbxDouble3 fbxBaseValue = pNode->LclRotation.Get();
pRotationChannel = readFbxChannelsQuat(
pNode->LclRotation.GetKFCurve(KFCURVENODE_R_X, pTakeName),
pNode->LclRotation.GetKFCurve(KFCURVENODE_R_Y, pTakeName),
pNode->LclRotation.GetKFCurve(KFCURVENODE_R_Z, pTakeName),
targetName, inverseRot, rotOrder);
targetName, rotOrder);
}
if (pNode->LclTranslation.IsValid())
{
fbxDouble3 fbxBaseValue = pNode->LclTranslation.Get();
osg::Vec3 offsetTranslation(
-static_cast<float>(fbxBaseValue[0]),
-static_cast<float>(fbxBaseValue[1]),
-static_cast<float>(fbxBaseValue[2]));
pTranslationChannel = readFbxChannels(
pNode->LclTranslation.GetKFCurve(KFCURVENODE_T_X, pTakeName),
pNode->LclTranslation.GetKFCurve(KFCURVENODE_T_Y, pTakeName),
pNode->LclTranslation.GetKFCurve(KFCURVENODE_T_Z, pTakeName),
targetName, "position", 1.0f, offsetTranslation, false);
if (pTranslationChannel)
{
osgAnimation::Vec3KeyframeContainer& keyFrameCntr =
dynamic_cast<osgAnimation::Vec3KeyframeContainer&>(
*pTranslationChannel->getSampler()->getKeyframeContainer());
for (int i = 0; i < keyFrameCntr.size(); ++i)
{
keyFrameCntr[i].setValue(inverseRot * keyFrameCntr[i].getValue());
}
}
targetName, "translate");
}
osgAnimation::Channel* pScaleChannel = readFbxChannels(
pNode->LclScaling, pTakeName, targetName, "scale", osg::Vec3(0,0,0), 1.0f, true);
pNode->LclScaling, pTakeName, targetName, "scale");
return addChannels(pTranslationChannel, pRotationChannel, pScaleChannel, pAnimManager, pTakeName);
}
osgAnimation::Animation* readFbxAnimation(KFbxNode* pNode,
const char* pTakeName, const char* targetName,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimManager)
{
if (!pTakeName) return 0;
osgAnimation::Channel* pTranslationChannel = readFbxChannels(
pNode->LclTranslation, pTakeName, targetName, "position", osg::Vec3(0,0,0), 1.0f, false);
//TODO: This will break if there are rotations in more than one of
// Pre/Lcl/Post so really they should each get their own MatrixTransform.
fbxDouble3 fbxPreRot = pNode->PreRotation.Get();
fbxDouble3 fbxPostRot = pNode->PostRotation.Get();
osg::Vec3 eulerOffset(
static_cast<float>(fbxPreRot[0] + fbxPostRot[0]),
static_cast<float>(fbxPreRot[1] + fbxPostRot[1]),
static_cast<float>(fbxPreRot[2] + fbxPostRot[2]));
osgAnimation::Channel* pRotationChannel = readFbxChannels(
pNode->LclRotation, pTakeName, targetName, "euler", eulerOffset, static_cast<float>(osg::PI / 180.0), false);
osgAnimation::Channel* pScaleChannel = readFbxChannels(
pNode->LclScaling, pTakeName, targetName, "scale", osg::Vec3(1,1,1), 1.0f, true);
return addChannels(pTranslationChannel, pRotationChannel, pScaleChannel, pAnimManager, pTakeName);
}
std::string readFbxBoneAnimation(KFbxNode* pNode,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimManager,
const char* targetName)
{
std::string result;
for (int i = 1; i < pNode->GetTakeNodeCount(); ++i)
{
const char* pTakeName = pNode->GetTakeNodeName(i);
if (osgAnimation::Animation* pAnimation = readFbxBoneAnimation(
pNode, pTakeName, targetName, pAnimManager))
{
result = targetName;
}
}
return result;
}
std::string readFbxAnimation(KFbxNode* pNode,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimManager,
const char* targetName)

View File

@@ -3,11 +3,6 @@
#include <fbxfilesdk/fbxfilesdk_def.h>
std::string readFbxBoneAnimation(
FBXFILESDK_NAMESPACE::KFbxNode*,
osg::ref_ptr<osgAnimation::AnimationManagerBase>&,
const char* targetName);
std::string readFbxAnimation(
FBXFILESDK_NAMESPACE::KFbxNode*,
osg::ref_ptr<osgAnimation::AnimationManagerBase>&,

View File

@@ -1,3 +1,6 @@
#include <cassert>
#include <sstream>
#include <osg/Geode>
#include <osg/Image>
#include <osg/MatrixTransform>
@@ -132,9 +135,8 @@ FbxT getElement(const KFbxLayerElementTemplate<FbxT>* pLayerElement,
typedef std::map<unsigned, osg::ref_ptr<osg::Geometry> > GeometryMap;
osg::Geometry* getGeometry(osg::Geode* pGeode, GeometryMap& geometryMap,
const std::vector<osg::ref_ptr<osg::Material>>& materialList,
const std::vector<osg::ref_ptr<osg::Texture>>& textureList,
GeometryType gt, unsigned mti, bool bNormal, bool bTexCoord, bool bColor)
std::vector<StateSetContent>& stateSetList,
GeometryType gt, unsigned int mti, bool bNormal, bool bTexCoord, bool bColor)
{
GeometryMap::iterator it = geometryMap.find(mti);
@@ -144,13 +146,7 @@ osg::Geometry* getGeometry(osg::Geode* pGeode, GeometryMap& geometryMap,
}
osg::ref_ptr<osg::Geometry> pGeometry;
if (gt == GEOMETRY_RIG)
{
osgAnimation::RigGeometry* pRig = new osgAnimation::RigGeometry;
pRig->setInfluenceMap(new osgAnimation::VertexInfluenceMap);
pGeometry = pRig;
}
else if (gt == GEOMETRY_MORPH)
if (gt == GEOMETRY_MORPH)
{
pGeometry = new osgAnimation::MorphGeometry;
}
@@ -164,17 +160,16 @@ osg::Geometry* getGeometry(osg::Geode* pGeode, GeometryMap& geometryMap,
if (bTexCoord) pGeometry->setTexCoordData(0, osg::Geometry::ArrayData(new osg::Vec2Array, osg::Geometry::BIND_PER_VERTEX));
if (bColor) pGeometry->setColorData(osg::Geometry::ArrayData(new osg::Vec4Array, osg::Geometry::BIND_PER_VERTEX));
if (mti < materialList.size())
if (mti < stateSetList.size())
{
pGeometry->getOrCreateStateSet()->setAttributeAndModes(materialList[mti].get());
const StateSetContent& ss = stateSetList[mti];
if(ss.first)
pGeometry->getOrCreateStateSet()->setAttributeAndModes(ss.first);
if(ss.second)
pGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, ss.second);
}
if (mti < textureList.size())
{
pGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, textureList[mti].get());
}
geometryMap.insert(std::pair<unsigned, osg::ref_ptr<osg::Geometry>>(mti, pGeometry));
geometryMap.insert(std::pair<unsigned, osg::ref_ptr<osg::Geometry> >(mti, pGeometry));
pGeode->addDrawable(pGeometry.get());
return pGeometry.get();
@@ -195,7 +190,7 @@ osgAnimation::VertexInfluence& getVertexInfluence(
void addChannel(
osgAnimation::Channel* pChannel,
osg::ref_ptr<osgAnimation::AnimationManagerBase> &pAnimManager,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimManager,
const char* pTakeName)
{
if (!pChannel)
@@ -225,7 +220,7 @@ void addChannel(
pAnimation->addChannel(pChannel);
}
void readAnimation(KFbxNode* pNode, osg::Geode* pGeode,
void readAnimation(KFbxNode* pNode, const std::string& targetName,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimationManager,
KFbxMesh* pMesh, int nShape)
{
@@ -233,18 +228,21 @@ void readAnimation(KFbxNode* pNode, osg::Geode* pGeode,
{
const char* pTakeName = pNode->GetTakeNodeName(i);
KFCurve* pCurve = pMesh->GetShapeChannel(nShape, true, pTakeName);
osgAnimation::FloatLinearChannel* pChannel = new osgAnimation::FloatLinearChannel;
std::vector<osgAnimation::TemplateKeyframe<float> >& keyFrameCntr = *pChannel->getOrCreateSampler()->getOrCreateKeyframeContainer();
KFCurve* pCurve = pMesh->GetShapeChannel(nShape, false, pTakeName);
if (!pCurve)
{
continue;
}
int nKeys = pCurve->KeyGetCount();
if (!nKeys)
{
float fValue = static_cast<float>(pCurve->GetValue() * 0.01);
keyFrameCntr.push_back(osgAnimation::FloatKeyframe(0.0f,fValue));
continue;
}
osgAnimation::FloatLinearChannel* pChannel = new osgAnimation::FloatLinearChannel;
std::vector<osgAnimation::TemplateKeyframe<float> >& keyFrameCntr = *pChannel->getOrCreateSampler()->getOrCreateKeyframeContainer();
for (int k = 0; k < nKeys; ++k)
{
KFCurveKey key = pCurve->KeyGet(k);
@@ -253,16 +251,17 @@ void readAnimation(KFbxNode* pNode, osg::Geode* pGeode,
keyFrameCntr.push_back(osgAnimation::FloatKeyframe(fTime,fValue));
}
pChannel->setTargetName(pGeode->getName());
pChannel->setName(pMesh->GetShapeName(nShape));
pChannel->setTargetName(targetName);
std::stringstream ss;
ss << nShape;
pChannel->setName(ss.str());
addChannel(pChannel, pAnimationManager, pTakeName);
}
}
osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimationManager,
const std::vector<osg::ref_ptr<osg::Material>>& materialList,
const std::vector<osg::ref_ptr<osg::Texture>>& textureList,
std::vector<StateSetContent>& stateSetList,
const char* szName)
{
GeometryMap geometryMap;
@@ -320,7 +319,7 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
int materialIndex = getPolygonIndex(pFbxMaterials, i);
osg::Geometry* pGeometry = getGeometry(pGeode, geometryMap,
materialList, textureList, geomType, materialIndex,
stateSetList, geomType, materialIndex,
pFbxNormals != 0, pFbxUVs != 0, pFbxColors != 0);
osg::Vec3Array* pVertices = static_cast<osg::Vec3Array*>(
@@ -346,7 +345,6 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
fbxToOsgVertMap.insert(FbxToOsgVertexMap::value_type(v1, GIPair(pGeometry, pVertices->size() + 1)));
fbxToOsgVertMap.insert(FbxToOsgVertexMap::value_type(v2, GIPair(pGeometry, pVertices->size() + 2)));
pVertices->push_back(convertVec3(pFbxVertices[v0]));
pVertices->push_back(convertVec3(pFbxVertices[v1]));
pVertices->push_back(convertVec3(pFbxVertices[v2]));
@@ -385,7 +383,16 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
for (int i = 0; i < pGeode->getNumDrawables(); ++i)
{
osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry();
pGeometry->setName(pGeode->getName());
if (pGeode->getNumDrawables() > 1)
{
std::stringstream ss;
ss << pGeode->getName() << " " << i + 1;
pGeometry->setName(ss.str());
}
else
{
pGeometry->setName(pGeode->getName());
}
osg::DrawArrays* pDrawArrays = new osg::DrawArrays(
GL_TRIANGLES, 0, pGeometry->getVertexArray()->getNumElements());
@@ -394,6 +401,26 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
if (geomType == GEOMETRY_RIG)
{
typedef std::map<osg::ref_ptr<osg::Geometry>,
osg::ref_ptr<osgAnimation::RigGeometry> > GeometryRigGeometryMap;
GeometryRigGeometryMap old2newGeometryMap;
for (int i = 0; i < pGeode->getNumDrawables(); ++i)
{
osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry();
osgAnimation::RigGeometry* pRig = new osgAnimation::RigGeometry;
pRig->setSourceGeometry(pGeometry);
pRig->copyFrom(*pGeometry);
old2newGeometryMap.insert(GeometryRigGeometryMap::value_type(
pGeometry, pRig));
pRig->setDataVariance(osg::Object::DYNAMIC);
pRig->setUseDisplayList( false );
pGeode->setDrawable(i, pRig);
pRig->setInfluenceMap(new osgAnimation::VertexInfluenceMap);
pGeometry = pRig;
}
for (int i = 0; i < nDeformerCount; ++i)
{
KFbxSkin* pSkin = (KFbxSkin*)fbxMesh->GetDeformer(i, KFbxDeformer::eSKIN);
@@ -418,9 +445,13 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
it->first == fbxIndex; ++it)
{
GIPair gi = it->second;
osgAnimation::RigGeometry& rig = dynamic_cast<osgAnimation::RigGeometry&>(*gi.first);
osgAnimation::VertexInfluenceMap& vim = *rig.getInfluenceMap();
osgAnimation::VertexInfluence& vi = getVertexInfluence(vim, pBone->GetName());
osgAnimation::RigGeometry& rig =
dynamic_cast<osgAnimation::RigGeometry&>(
*old2newGeometryMap[gi.first]);
osgAnimation::VertexInfluenceMap& vim =
*rig.getInfluenceMap();
osgAnimation::VertexInfluence& vi =
getVertexInfluence(vim, pBone->GetName());
vi.push_back(osgAnimation::VertexIndexWeight(
gi.second, weight));
}
@@ -430,15 +461,14 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
}
else if (geomType == GEOMETRY_MORPH)
{
pGeode->addUpdateCallback(new osgAnimation::UpdateMorph(pGeode->getName()));
for (int i = 0; i < pGeode->getNumDrawables(); ++i)
{
osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry();
osgAnimation::MorphGeometry& morph = dynamic_cast<osgAnimation::MorphGeometry&>(*pGeometry);
pGeode->addUpdateCallback(new osgAnimation::UpdateMorph(morph.getName()));
//read morph geometry
for (int j = 0; j < nMorphShapeCount; ++j)
{
@@ -463,11 +493,9 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
}
}
pMorphTarget->setName(fbxMesh->GetShapeName(j));
KFCurve* pCurve = fbxMesh->GetShapeChannel(j);
double defaultWeight = pCurve->GetValue() * 0.01;
morph.addMorphTarget(pMorphTarget, static_cast<float>(defaultWeight));
morph.addMorphTarget(pMorphTarget, 0.0f);
readAnimation(pNode, pGeode, pAnimationManager, fbxMesh, j);
readAnimation(pNode, morph.getName(), pAnimationManager, fbxMesh, j);
}
}
@@ -534,8 +562,7 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh,
osgDB::ReaderWriter::ReadResult readFbxMesh(KFbxNode* pNode,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimationManager,
const std::vector<osg::ref_ptr<osg::Material>>& materialList,
const std::vector<osg::ref_ptr<osg::Texture>>& textureList)
std::vector<StateSetContent>& stateSetList)
{
KFbxMesh* lMesh = dynamic_cast<KFbxMesh*>(pNode->GetNodeAttribute());
@@ -544,5 +571,5 @@ osgDB::ReaderWriter::ReadResult readFbxMesh(KFbxNode* pNode,
return osgDB::ReaderWriter::ReadResult::ERROR_IN_READING_FILE;
}
return readMesh(pNode, lMesh, pAnimationManager, materialList, textureList, pNode->GetName());
return readMesh(pNode, lMesh, pAnimationManager, stateSetList, pNode->GetName());
}

View File

@@ -4,11 +4,10 @@
#include <fbxfilesdk/fbxfilesdk_def.h>
#include <osgDB/ReaderWriter>
#include <osg/Material>
#include "fbxMaterialToOsgStateSet.h"
osgDB::ReaderWriter::ReadResult readFbxMesh(
FBXFILESDK_NAMESPACE::KFbxNode* pNode,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimationManager,
const std::vector<osg::ref_ptr<osg::Material>>&,
const std::vector<osg::ref_ptr<osg::Texture>>&);
std::vector<StateSetContent> &);
#endif

View File

@@ -1,5 +1,8 @@
#include <cassert>
#include <memory>
#include <sstream>
#include <osg/io_utils>
#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/Material>
@@ -9,8 +12,13 @@
#include <osgDB/ReadFile>
#include <osgAnimation/AnimationManagerBase>
#include <osgAnimation/Bone>
#include <osgAnimation/Skeleton>
#include <osgAnimation/UpdateCallback>
#include <osgAnimation/StackedMatrixElement>
#include <osgAnimation/StackedQuaternionElement>
#include <osgAnimation/StackedScaleElement>
#include <osgAnimation/StackedTranslateElement>
#include <osgAnimation/UpdateBone>
#if defined(_MSC_VER)
#pragma warning( disable : 4505 )
@@ -21,140 +29,8 @@
#include "fbxRCamera.h"
#include "fbxRLight.h"
#include "fbxRMesh.h"
template <typename FbxT, typename OsgT, typename ConvertFunc>
class FbxToOsgMap
{
std::map<const FbxT*, osg::ref_ptr<OsgT>> m_map;
public:
ConvertFunc m_convertFunc;
FbxToOsgMap(ConvertFunc convertFunc) : m_convertFunc(convertFunc) {}
osg::ref_ptr<OsgT> Get(const FbxT* fbx)
{
if (!fbx)
return 0;
std::map<const FbxT*, osg::ref_ptr<OsgT>>::iterator it = m_map.find(fbx);
if (it != m_map.end())
{
return it->second;
}
osg::ref_ptr<OsgT> osgObj = m_convertFunc(fbx);
m_map.insert(std::pair<const FbxT*, osg::ref_ptr<OsgT>>(fbx, osgObj));
return osgObj;
}
};
struct GetOsgTexture
{
const std::string& m_dir;
GetOsgTexture(const std::string& dir) : m_dir(dir) {}
static osg::Texture::WrapMode convertWrap(KFbxTexture::EWrapMode wrap)
{
return wrap == KFbxTexture::eREPEAT ?
osg::Texture2D::REPEAT : osg::Texture2D::CLAMP_TO_EDGE;
}
osg::ref_ptr<osg::Texture2D> operator () (const KFbxTexture* fbx)
{
osg::Image* pImage;
if ((pImage = osgDB::readImageFile(osgDB::concatPaths(m_dir, fbx->GetRelativeFileName()))) ||
(pImage = osgDB::readImageFile(osgDB::concatPaths(m_dir, fbx->GetFileName()))))
{
osg::ref_ptr<osg::Texture2D> pOsgTex = new osg::Texture2D;
pOsgTex->setImage(pImage);
pOsgTex->setWrap(osg::Texture2D::WRAP_S, convertWrap(fbx->GetWrapModeU()));
pOsgTex->setWrap(osg::Texture2D::WRAP_T, convertWrap(fbx->GetWrapModeV()));
return pOsgTex;
}
else
{
return 0;
}
}
};
struct GetOsgMaterial
{
typedef FbxToOsgMap<KFbxTexture, osg::Texture2D, GetOsgTexture> TextureMap;
TextureMap m_textureMap;
public:
osg::ref_ptr<osg::Texture2D> m_pTexture;
GetOsgMaterial(const std::string& dir) : m_textureMap(GetOsgTexture(dir)){}
osg::ref_ptr<osg::Material> operator () (const KFbxSurfaceMaterial* pFbxMat)
{
osg::ref_ptr<osg::Material> pOsgMat = new osg::Material;
const KFbxSurfaceLambert* pFbxLambert = dynamic_cast<const KFbxSurfaceLambert*>(pFbxMat);
const KFbxProperty lProperty = pFbxMat->FindProperty(KFbxSurfaceMaterial::sDiffuse);
if(lProperty.IsValid()){
int lNbTex = lProperty.GetSrcObjectCount(KFbxTexture::ClassId);
for (int lTextureIndex = 0; lTextureIndex < lNbTex; lTextureIndex++)
{
const KFbxTexture* lTexture = KFbxCast<KFbxTexture>(lProperty.GetSrcObject(KFbxTexture::ClassId, lTextureIndex));
if(lTexture)
{
m_pTexture = m_textureMap.Get(lTexture);
}
//For now only allow 1 texture
break;
}
}
if (pFbxLambert)
{
fbxDouble3 color = pFbxLambert->GetDiffuseColor().Get();
double factor = pFbxLambert->GetDiffuseFactor().Get();
pOsgMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(
static_cast<float>(color[0] * factor),
static_cast<float>(color[1] * factor),
static_cast<float>(color[2] * factor),
static_cast<float>(1.0 - pFbxLambert->GetTransparencyFactor().Get())));
color = pFbxLambert->GetAmbientColor().Get();
factor = pFbxLambert->GetAmbientFactor().Get();
pOsgMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(
static_cast<float>(color[0] * factor),
static_cast<float>(color[1] * factor),
static_cast<float>(color[2] * factor),
1.0f));
color = pFbxLambert->GetEmissiveColor().Get();
factor = pFbxLambert->GetEmissiveFactor().Get();
pOsgMat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4(
static_cast<float>(color[0] * factor),
static_cast<float>(color[1] * factor),
static_cast<float>(color[2] * factor),
1.0f));
if (const KFbxSurfacePhong* pFbxPhong = dynamic_cast<const KFbxSurfacePhong*>(pFbxLambert))
{
color = pFbxPhong->GetSpecularColor().Get();
factor = pFbxPhong->GetSpecularFactor().Get();
pOsgMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(
static_cast<float>(color[0] * factor),
static_cast<float>(color[1] * factor),
static_cast<float>(color[2] * factor),
1.0f));
pOsgMat->setShininess(osg::Material::FRONT_AND_BACK,
static_cast<float>(pFbxPhong->GetShininess().Get()));
}
}
return pOsgMat;
}
};
#include "fbxRNode.h"
#include "fbxMaterialToOsgStateSet.h"
osg::Quat makeQuat(const fbxDouble3& degrees, ERotationOrder fbxRotOrder)
{
@@ -259,69 +135,8 @@ void makeLocalMatrix(const KFbxNode* pNode, osg::Matrix& m)
-fbxSclPiv[2]));
}
void getApproximateTransform(const KFbxNode* pNode, osg::Vec3& trans, osg::Quat& quat, osg::Vec3& scale)
{
ERotationOrder fbxRotOrder = pNode->RotationOrder.Get();
fbxDouble3 fbxLclPos = pNode->LclTranslation.Get();
//fbxDouble3 fbxRotOff = pNode->RotationOffset.Get();
//fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get();
fbxDouble3 fbxPreRot = pNode->PreRotation.Get();
fbxDouble3 fbxLclRot = pNode->LclRotation.Get();
fbxDouble3 fbxPostRot = pNode->PostRotation.Get();
//fbxDouble3 fbxSclOff = pNode->ScalingOffset.Get();
//fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get();
fbxDouble3 fbxLclScl = pNode->LclScaling.Get();
trans.set(
static_cast<float>(fbxLclPos[0]),
static_cast<float>(fbxLclPos[1]),
static_cast<float>(fbxLclPos[2]));
quat =
makeQuat(fbxPostRot, fbxRotOrder) *
makeQuat(fbxLclRot, fbxRotOrder) *
makeQuat(fbxPreRot, fbxRotOrder);
scale.set(
static_cast<float>(fbxLclScl[0]),
static_cast<float>(fbxLclScl[1]),
static_cast<float>(fbxLclScl[2]));
}
void getApproximateTransform(const KFbxNode* pNode, osg::Vec3& trans, osg::Vec3& euler, osg::Vec3& scale)
{
//ERotationOrder fbxRotOrder = pNode->RotationOrder.Get();
fbxDouble3 fbxLclPos = pNode->LclTranslation.Get();
//fbxDouble3 fbxRotOff = pNode->RotationOffset.Get();
//fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get();
fbxDouble3 fbxPreRot = pNode->PreRotation.Get();
fbxDouble3 fbxLclRot = pNode->LclRotation.Get();
fbxDouble3 fbxPostRot = pNode->PostRotation.Get();
//fbxDouble3 fbxSclOff = pNode->ScalingOffset.Get();
//fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get();
fbxDouble3 fbxLclScl = pNode->LclScaling.Get();
trans.set(
static_cast<float>(fbxLclPos[0]),
static_cast<float>(fbxLclPos[1]),
static_cast<float>(fbxLclPos[2]));
//TODO: Convert each rotation to a quaternion, concatenate them and extract euler from that.
euler.set(
osg::DegreesToRadians(static_cast<float>(fbxPreRot[0] + fbxLclRot[0] + fbxPostRot[0])),
osg::DegreesToRadians(static_cast<float>(fbxPreRot[1] + fbxLclRot[1] + fbxPostRot[1])),
osg::DegreesToRadians(static_cast<float>(fbxPreRot[2] + fbxLclRot[2] + fbxPostRot[2])));
scale.set(
static_cast<float>(fbxLclScl[0]),
static_cast<float>(fbxLclScl[1]),
static_cast<float>(fbxLclScl[2]));
}
bool readBindPose(KFbxSdkManager& pManager, KFbxNode* pNode,
osgAnimation::Bone* osgBone)
osgAnimation::Bone* osgBone)
{
KArrayTemplate<KFbxPose*> pPoseList;
KArrayTemplate<int> pIndex;
@@ -332,10 +147,129 @@ bool readBindPose(KFbxSdkManager& pManager, KFbxNode* pNode,
}
const double* pMat = pPoseList[0]->GetMatrix(pIndex[0]);
osgBone->setBindMatrixInBoneSpace(osg::Matrix(pMat));
osgBone->setMatrix(osg::Matrix(pMat));
return true;
}
void readTranslationElement(KFbxTypedProperty<fbxDouble3>& prop,
osgAnimation::UpdateMatrixTransform* pUpdate,
osg::Matrix& staticTransform)
{
fbxDouble3 fbxPropValue = prop.Get();
osg::Vec3d val(
fbxPropValue[0],
fbxPropValue[1],
fbxPropValue[2]);
if (prop.GetKFCurve(KFCURVENODE_T_X) ||
prop.GetKFCurve(KFCURVENODE_T_Y) ||
prop.GetKFCurve(KFCURVENODE_T_Z))
{
if (!staticTransform.isIdentity())
{
pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform));
staticTransform.makeIdentity();
}
pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("translate", val));
}
else
{
staticTransform.preMultTranslate(val);
}
}
void readRotationElement(KFbxTypedProperty<fbxDouble3>& prop,
ERotationOrder fbxRotOrder,
osgAnimation::UpdateMatrixTransform* pUpdate,
osg::Matrix& staticTransform)
{
osg::Quat quat = makeQuat(prop.Get(), fbxRotOrder);
if (prop.GetKFCurve(KFCURVENODE_R_X) ||
prop.GetKFCurve(KFCURVENODE_R_Y) ||
prop.GetKFCurve(KFCURVENODE_R_Z))
{
if (!staticTransform.isIdentity())
{
pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform));
staticTransform.makeIdentity();
}
pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedQuaternionElement("quaternion", quat));
}
else
{
staticTransform.preMultRotate(quat);
}
}
void readScaleElement(KFbxTypedProperty<fbxDouble3>& prop,
osgAnimation::UpdateMatrixTransform* pUpdate,
osg::Matrix& staticTransform)
{
fbxDouble3 fbxPropValue = prop.Get();
osg::Vec3d val(
fbxPropValue[0],
fbxPropValue[1],
fbxPropValue[2]);
if (prop.GetKFCurve(KFCURVENODE_S_X) ||
prop.GetKFCurve(KFCURVENODE_S_Y) ||
prop.GetKFCurve(KFCURVENODE_S_Z))
{
if (!staticTransform.isIdentity())
{
pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform));
staticTransform.makeIdentity();
}
pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedScaleElement("scale", val));
}
else
{
staticTransform.preMultScale(val);
}
}
void readUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform* pUpdate, KFbxNode* pNode)
{
osg::Matrix staticTransform;
readTranslationElement(pNode->LclTranslation, pUpdate, staticTransform);
fbxDouble3 fbxRotOffset = pNode->RotationOffset.Get();
fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get();
staticTransform.preMultTranslate(osg::Vec3d(
fbxRotPiv[0] + fbxRotOffset[0],
fbxRotPiv[1] + fbxRotOffset[1],
fbxRotPiv[2] + fbxRotOffset[2]));
ERotationOrder fbxRotOrder = pNode->RotationOrder.IsValid() ? pNode->RotationOrder.Get() : eEULER_XYZ;
staticTransform.preMultRotate(makeQuat(pNode->PreRotation.Get(), fbxRotOrder));
readRotationElement(pNode->LclRotation, fbxRotOrder, pUpdate, staticTransform);
staticTransform.preMultRotate(makeQuat(pNode->PostRotation.Get(), fbxRotOrder));
fbxDouble3 fbxSclOffset = pNode->ScalingOffset.Get();
fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get();
staticTransform.preMultTranslate(osg::Vec3d(
fbxSclOffset[0] + fbxSclPiv[0] - fbxRotPiv[0],
fbxSclOffset[1] + fbxSclPiv[1] - fbxRotPiv[1],
fbxSclOffset[2] + fbxSclPiv[2] - fbxRotPiv[2]));
readScaleElement(pNode->LclScaling, pUpdate, staticTransform);
staticTransform.preMultTranslate(osg::Vec3d(
-fbxSclPiv[0],
-fbxSclPiv[1],
-fbxSclPiv[2]));
if (!staticTransform.isIdentity())
{
pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform));
}
}
osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode,
const std::string& animName, const osg::Matrix& localMatrix, bool bNeedSkeleton)
{
@@ -344,7 +278,9 @@ osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode,
osgAnimation::Bone* osgBone = new osgAnimation::Bone;
osgBone->setDataVariance(osg::Object::DYNAMIC);
osgBone->setName(pNode->GetName());
osgBone->setDefaultUpdateCallback(animName);
osgAnimation::UpdateBone* pUpdate = new osgAnimation::UpdateBone(animName);
readUpdateMatrixTransform(pUpdate, pNode);
osgBone->setUpdateCallback(pUpdate);
readBindPose(pSdkManager, pNode, osgBone);
@@ -362,18 +298,14 @@ osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode,
osg::MatrixTransform* pTransform = new osg::MatrixTransform(localMatrix);
pTransform->setName(pNode->GetName());
if (bAnimated)
{
osgAnimation::UpdateTransform* pUpdate = new osgAnimation::UpdateTransform(animName);
osg::Vec3 trans, rot, scale;
getApproximateTransform(pNode, trans, rot, scale);
pUpdate->getPosition()->setValue(trans);
pUpdate->getEuler()->setValue(rot);
pUpdate->getScale()->setValue(scale);
osgAnimation::UpdateMatrixTransform* pUpdate = new osgAnimation::UpdateMatrixTransform(animName);
readUpdateMatrixTransform(pUpdate, pNode);
pTransform->setUpdateCallback(pUpdate);
}
return pTransform;
}
}
@@ -381,7 +313,9 @@ osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode,
osgDB::ReaderWriter::ReadResult readFbxNode(
KFbxSdkManager& pSdkManager, KFbxNode* pNode,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimationManager,
const std::string& dir, bool& bNeedSkeleton, int& nLightCount)
bool& bNeedSkeleton, int& nLightCount,
FbxMaterialToOsgStateSet& fbxMaterialToOsgStateSet,
const osgDB::Options* options)
{
if (KFbxNodeAttribute* lNodeAttribute = pNode->GetNodeAttribute())
{
@@ -404,17 +338,13 @@ osgDB::ReaderWriter::ReadResult readFbxNode(
}
unsigned nMaterials = pNode->GetMaterialCount();
std::vector<osg::ref_ptr<osg::Material>> materialList;
std::vector<osg::ref_ptr<osg::Texture>> textureList;
materialList.reserve(nMaterials);
typedef FbxToOsgMap<KFbxSurfaceMaterial, osg::Material, GetOsgMaterial> MaterialMap;
MaterialMap materialMap(dir);
std::vector<StateSetContent > stateSetList;
for (unsigned i = 0; i < nMaterials; ++i)
{
materialList.push_back(materialMap.Get(pNode->GetMaterial(i)));
textureList.push_back(materialMap.m_convertFunc.m_pTexture);
KFbxSurfaceMaterial* fbxMaterial = pNode->GetMaterial(i);
assert(fbxMaterial);
stateSetList.push_back(fbxMaterialToOsgStateSet.convert(fbxMaterial));
}
osg::NodeList skeletal, children;
@@ -432,8 +362,8 @@ osgDB::ReaderWriter::ReadResult readFbxNode(
bool bChildNeedSkeleton = false;
osgDB::ReaderWriter::ReadResult childResult = readFbxNode(
pSdkManager, pChildNode, pAnimationManager, dir,
bChildNeedSkeleton, nLightCount);
pSdkManager, pChildNode, pAnimationManager,
bChildNeedSkeleton, nLightCount, fbxMaterialToOsgStateSet, options);
if (childResult.error())
{
return childResult;
@@ -452,17 +382,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode(
}
}
std::string animName;
if (bNeedSkeleton)
{
animName = readFbxBoneAnimation(pNode, pAnimationManager,
pNode->GetName());
}
else
{
animName = readFbxAnimation(pNode, pAnimationManager, pNode->GetName());
}
std::string animName = readFbxAnimation(pNode, pAnimationManager, pNode->GetName());
osg::Matrix localMatrix;
makeLocalMatrix(pNode, localMatrix);
@@ -475,7 +395,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode(
switch (lAttributeType)
{
case KFbxNodeAttribute::eUNIDENTIFIED:
if (children.size() + skeletal.size() == 1)
if (bLocalMatrixIdentity && children.size() + skeletal.size() == 1)
{
if (children.size() == 1)
{
@@ -490,7 +410,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode(
case KFbxNodeAttribute::eMESH:
{
osgDB::ReaderWriter::ReadResult meshRes = readFbxMesh(pNode,
pAnimationManager, materialList, textureList);
pAnimationManager, stateSetList);
if (meshRes.error())
{
return meshRes;
@@ -506,6 +426,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode(
return osgDB::ReaderWriter::ReadResult(node);
}
osgGroup = createGroupNode(pSdkManager, pNode, animName, localMatrix, bNeedSkeleton);
assert(osgGroup->getStateSet() == NULL);
osgGroup->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON);
osgGroup->addChild(node);

View File

@@ -1,6 +1,7 @@
#ifndef FBXRNODE_H
#define FBXRNODE_H
#include "fbxMaterialToOsgStateSet.h"
namespace osgAnimation
{
class AnimationManagerBase;
@@ -10,8 +11,9 @@ osgDB::ReaderWriter::ReadResult readFbxNode(
FBXFILESDK_NAMESPACE::KFbxSdkManager& pSdkManager,
FBXFILESDK_NAMESPACE::KFbxNode* pNode,
osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimationManager,
const std::string& dir,
bool& bNeedSkeleton,
int& nLightCount);
int& nLightCount,
FbxMaterialToOsgStateSet& fbxMaterialToOsgStateSet,
const osgDB::Options* options = NULL);
#endif