# Incomplete improved support for XSI models/animations
This was SVN commit r4946.
This commit is contained in:
parent
8eec30c383
commit
781e630b2c
@ -5,6 +5,9 @@
|
||||
#include "FCollada.h"
|
||||
#include "FCDocument/FCDSceneNode.h"
|
||||
#include "FCDocument/FCDSkinController.h"
|
||||
#include "FUtils/FUDaeSyntax.h"
|
||||
#include "FUtils/FUFileManager.h"
|
||||
#include "FUtils/FUXmlParser.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
@ -64,11 +67,76 @@ void FColladaErrorHandler::OnError(FUError::Level errorLevel, uint32 errorCode,
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FColladaDocument::LoadFromText(const char *text)
|
||||
{
|
||||
document.reset(FCollada::NewTopDocument());
|
||||
|
||||
FUFileManager* fileManager = document->GetFileManager();
|
||||
const char* basePath = "";
|
||||
|
||||
// Mostly copied from FCDocument::LoadFromText
|
||||
|
||||
bool status = true;
|
||||
|
||||
// Push the given path unto the file manager's stack
|
||||
fileManager->PushRootPath(basePath);
|
||||
|
||||
// Parse the document into a XML tree
|
||||
xmlDoc* daeDocument = xmlParseDoc((const xmlChar*)text);
|
||||
if (daeDocument != NULL)
|
||||
{
|
||||
xmlNode *rootNode = xmlDocGetRootElement(daeDocument);
|
||||
|
||||
// Read in the whole document from the root node
|
||||
status &= (document->LoadDocumentFromXML(rootNode));
|
||||
|
||||
// HACK (sort of): read in <extra> from the root, because FCollada
|
||||
// doesn't let us do that
|
||||
ReadExtras(rootNode);
|
||||
|
||||
// Free the XML document
|
||||
xmlFreeDoc(daeDocument);
|
||||
}
|
||||
else
|
||||
{
|
||||
FUError::Error(FUError::ERROR, FUError::ERROR_MALFORMED_XML);
|
||||
status = false;
|
||||
}
|
||||
|
||||
// Clean-up the XML reader
|
||||
xmlCleanupParser();
|
||||
|
||||
// Restore the original OS current folder
|
||||
fileManager->PopRootPath();
|
||||
|
||||
if (status)
|
||||
FUError::Error(FUError::DEBUG, FUError::DEBUG_LOAD_SUCCESSFUL);
|
||||
|
||||
REQUIRE_SUCCESS(status);
|
||||
}
|
||||
|
||||
void FColladaDocument::ReadExtras(xmlNode* colladaNode)
|
||||
{
|
||||
if (! IsEquivalent(colladaNode->name, DAE_COLLADA_ELEMENT))
|
||||
return;
|
||||
|
||||
extra.reset(new FCDExtra(document.get()));
|
||||
|
||||
xmlNodeList extraNodes;
|
||||
FUXmlParser::FindChildrenByType(colladaNode, DAE_EXTRA_ELEMENT, extraNodes);
|
||||
for (xmlNodeList::iterator it = extraNodes.begin(); it != extraNodes.end(); ++it)
|
||||
{
|
||||
xmlNode* extraNode = (*it);
|
||||
extra->LoadFromXML(extraNode);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// These don't get exported properly from FCollada (3.02, DLL), so define them
|
||||
// here instead of fixing it correctly.
|
||||
// HACK: These don't get exported properly from FCollada (3.02, DLL), so define
|
||||
// them here instead of fixing it correctly.
|
||||
const FMVector3 FMVector3::XAxis(1.0f, 0.0f, 0.0f);
|
||||
static float identity[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
|
||||
FMMatrix44 FMMatrix44::Identity(identity);
|
||||
@ -79,6 +147,45 @@ struct FoundInstance
|
||||
FMMatrix44 transform;
|
||||
};
|
||||
|
||||
static bool IsVisible_XSI(FCDSceneNode* node, bool& visible)
|
||||
{
|
||||
// Look for <extra><technique profile="XSI"><SI_Visibility><xsi_param sid="visibility">
|
||||
|
||||
FCDExtra* extra = node->GetExtra();
|
||||
if (! extra) return false;
|
||||
|
||||
FCDEType* type = extra->GetDefaultType();
|
||||
if (! type) return false;
|
||||
|
||||
FCDETechnique* technique = type->FindTechnique("XSI");
|
||||
if (! technique) return false;
|
||||
|
||||
FCDENode* visibility1 = technique->FindChildNode("SI_Visibility");
|
||||
if (! visibility1) return false;
|
||||
|
||||
FCDENode* visibility2 = visibility1->FindChildNode("xsi_param");
|
||||
if (! visibility2) return false;
|
||||
|
||||
if (IsEquivalent(visibility2->GetContent(), "TRUE"))
|
||||
visible = true;
|
||||
else if (IsEquivalent(visibility2->GetContent(), "FALSE"))
|
||||
visible = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsVisible(FCDSceneNode* node)
|
||||
{
|
||||
bool visible;
|
||||
|
||||
// Try the XSI visibility property
|
||||
if (IsVisible_XSI(node, visible))
|
||||
return visible;
|
||||
|
||||
// Else fall back to the FCollada-specific setting
|
||||
visible = (node->GetVisibility() != 0.0);
|
||||
return visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively finds all entities under the current node. If onlyMarked is
|
||||
* set, only matches entities where the user-defined property was set to
|
||||
@ -110,10 +217,15 @@ static void FindInstances(FCDSceneNode* node, std::vector<FoundInstance>& instan
|
||||
if (! (type == FCDEntity::GEOMETRY || type == FCDEntity::CONTROLLER))
|
||||
continue;
|
||||
|
||||
// Ignore invisible objects, because presumably nobody wanted to export them
|
||||
if (! IsVisible(node))
|
||||
continue;
|
||||
|
||||
FoundInstance f;
|
||||
f.transform = transform * node->ToMatrix();
|
||||
f.instance = node->GetInstance(i);
|
||||
instances.push_back(f);
|
||||
Log(LOG_INFO, "Found convertible object '%s'", node->GetName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,3 +321,19 @@ void SkinReduceInfluences(FCDSkinController* skin, size_t maxInfluenceCount, flo
|
||||
|
||||
skin->SetDirtyFlag();
|
||||
}
|
||||
|
||||
|
||||
void FixSkeletonRoots(FCDControllerInstance* controllerInstance)
|
||||
{
|
||||
// HACK: The XSI exporter doesn't do a <skeleton> and FCollada doesn't
|
||||
// seem to know where else to look, so just guess that it's somewhere
|
||||
// under Scene_Root
|
||||
if (controllerInstance->GetSkeletonRoots().empty())
|
||||
{
|
||||
// HACK (evil): SetSkeletonRoot is declared but not defined, and there's
|
||||
// no other proper way to modify the skeleton-roots list, so cheat horribly
|
||||
FUUriList& uriList = const_cast<FUUriList&>(controllerInstance->GetSkeletonRoots());
|
||||
uriList.push_back(FUUri("Scene_Root"));
|
||||
controllerInstance->LinkImport();
|
||||
}
|
||||
}
|
||||
|
@ -13,19 +13,9 @@ class FCDSkinController;
|
||||
class ColladaException : public std::exception
|
||||
{
|
||||
public:
|
||||
ColladaException(const std::string& msg)
|
||||
: msg(msg)
|
||||
{
|
||||
}
|
||||
|
||||
~ColladaException() throw()
|
||||
{
|
||||
}
|
||||
|
||||
virtual const char* what() const throw()
|
||||
{
|
||||
return msg.c_str();
|
||||
}
|
||||
ColladaException(const std::string& msg) : msg(msg) { }
|
||||
~ColladaException() throw() { }
|
||||
virtual const char* what() const throw() { return msg.c_str(); }
|
||||
private:
|
||||
std::string msg;
|
||||
};
|
||||
@ -35,6 +25,10 @@ struct OutputCB
|
||||
virtual void operator() (const char* data, unsigned int length)=0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Standard error handler - logs FCollada messages using Log(), and also
|
||||
* maintains a list of XML parser errors.
|
||||
*/
|
||||
class FColladaErrorHandler
|
||||
{
|
||||
public:
|
||||
@ -48,6 +42,31 @@ private:
|
||||
void operator=(FColladaErrorHandler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Standard document loader. Based on FCDocument::LoadFromText, but allows
|
||||
* access to <extra> nodes at the document level (i.e. directly in <COLLADA>).
|
||||
*/
|
||||
class FColladaDocument
|
||||
{
|
||||
public:
|
||||
/** Loads the document from the given XML string. Should be the first function
|
||||
* called on this object, and should only be called once.
|
||||
* @throws ColladaException if unable to load.
|
||||
*/
|
||||
void LoadFromText(const char* text);
|
||||
|
||||
/** Returns the FCDocument that was loaded. */
|
||||
FCDocument* GetDocument() const { return document.get(); }
|
||||
|
||||
/** Returns the <extra> data from the <COLLADA> element. */
|
||||
FCDExtra* GetExtra() const { return extra.get(); }
|
||||
|
||||
private:
|
||||
void ReadExtras(xmlNode* colladaNode);
|
||||
std::auto_ptr<FCDocument> document;
|
||||
std::auto_ptr<FCDExtra> extra;
|
||||
};
|
||||
|
||||
/** Throws a ColladaException unless the value is true. */
|
||||
#define REQUIRE(value, message) require_(__LINE__, value, "Assertion not satisfied", message)
|
||||
|
||||
@ -87,4 +106,11 @@ bool FindSingleInstance(FCDSceneNode* node, FCDEntityInstance*& instance, FMMatr
|
||||
*/
|
||||
void SkinReduceInfluences(FCDSkinController* skin, size_t maxInfluenceCount, float minimumWeight);
|
||||
|
||||
/**
|
||||
* Fixes some occasional problems with the skeleton root definitions in a
|
||||
* controller. (In particular, it's needed for models exported from XSI.)
|
||||
* Should be called before extracting any joint information from the controller.
|
||||
*/
|
||||
void FixSkeletonRoots(FCDControllerInstance* controllerInstance);
|
||||
|
||||
#endif // COMMONCONVERT_H__
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "FCollada.h"
|
||||
#include "FCDocument/FCDAsset.h"
|
||||
#include "FCDocument/FCDocument.h"
|
||||
#include "FCDocument/FCDocumentTools.h"
|
||||
#include "FCDocument/FCDController.h"
|
||||
#include "FCDocument/FCDControllerInstance.h"
|
||||
#include "FCDocument/FCDGeometry.h"
|
||||
@ -62,10 +63,10 @@ public:
|
||||
{
|
||||
FColladaErrorHandler err (xmlErrors);
|
||||
|
||||
std::auto_ptr<FCDocument> doc (FCollada::NewTopDocument());
|
||||
REQUIRE_SUCCESS(doc->LoadFromText("", input));
|
||||
FColladaDocument doc;
|
||||
doc.LoadFromText(input);
|
||||
|
||||
FCDSceneNode* root = doc->GetVisualSceneRoot();
|
||||
FCDSceneNode* root = doc.GetDocument()->GetVisualSceneRoot();
|
||||
REQUIRE(root != NULL, "has root object");
|
||||
|
||||
// Find the instance to convert
|
||||
@ -77,7 +78,8 @@ public:
|
||||
assert(instance);
|
||||
Log(LOG_INFO, "Converting '%s'", instance->GetEntity()->GetName().c_str());
|
||||
|
||||
FMVector3 upAxis = doc->GetAsset()->GetUpAxis();
|
||||
// StandardizeUpAxisAndLength completely mangles the skeletons, so don't use it
|
||||
FMVector3 upAxis = doc.GetDocument()->GetAsset()->GetUpAxis();
|
||||
bool yUp = (upAxis.y != 0); // assume either Y_UP or Z_UP (TODO: does anyone ever do X_UP?)
|
||||
|
||||
if (instance->GetEntity()->GetType() == FCDEntity::GEOMETRY)
|
||||
@ -127,12 +129,14 @@ public:
|
||||
FCDSkinController* skin = controller->GetSkinController();
|
||||
REQUIRE(skin != NULL, "is skin controller");
|
||||
|
||||
FixSkeletonRoots(controllerInstance);
|
||||
|
||||
// Data for joints is stored in two places - avoid overflows by limiting
|
||||
// to the minimum of the two sizes, and warn if they're different (which
|
||||
// happens in practice for slightly-broken meshes)
|
||||
size_t jointCount = std::min(skin->GetJointCount(), controllerInstance->GetJointCount());
|
||||
if (skin->GetJointCount() != controllerInstance->GetJointCount())
|
||||
Log(LOG_WARNING, "Mismatched bone counts");
|
||||
Log(LOG_WARNING, "Mismatched bone counts (skin has %d, skeleton has %d)", skin->GetJointCount(), controllerInstance->GetJointCount());
|
||||
|
||||
// Get the skinned mesh for this entity
|
||||
FCDEntity* baseTarget = controller->GetBaseTarget();
|
||||
@ -295,7 +299,7 @@ public:
|
||||
FloatList& dataNormal = sourceNormal ->GetSourceData();
|
||||
FloatList& dataTexcoord = sourceTexcoord->GetSourceData();
|
||||
|
||||
TransformVertices(dataPosition, dataNormal, boneTransforms, propPoints, transform);
|
||||
TransformVertices(dataPosition, dataNormal, boneTransforms, propPoints, transform, yUp);
|
||||
|
||||
WritePMD(output, *indicesCombined, dataPosition, dataNormal, dataTexcoord, boneWeights, boneTransforms, propPoints);
|
||||
}
|
||||
@ -440,7 +444,7 @@ public:
|
||||
|
||||
static void TransformVertices(FloatList& position, FloatList& normal,
|
||||
std::vector<BoneTransform>& bones, std::vector<PropPoint>& propPoints,
|
||||
const FMMatrix44& transform)
|
||||
const FMMatrix44& transform, bool yUp)
|
||||
{
|
||||
// Update the vertex positions and normals
|
||||
assert(position.size() == normal.size());
|
||||
@ -453,11 +457,18 @@ public:
|
||||
pos = transform.TransformCoordinate(pos);
|
||||
norm = transform.TransformVector(norm).Normalize();
|
||||
|
||||
// Switch from Max's coordinate system into the game's:
|
||||
// (TODO: handle Y_UP mode too)
|
||||
// Convert from Y_UP or Z_UP to the game's coordinate system
|
||||
|
||||
std::swap(pos.y, pos.z);
|
||||
std::swap(norm.y, norm.z);
|
||||
if (yUp)
|
||||
{
|
||||
pos.x = -pos.x;
|
||||
norm.x = -norm.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::swap(pos.y, pos.z);
|
||||
std::swap(norm.y, norm.z);
|
||||
}
|
||||
|
||||
// and copy back into the original array
|
||||
|
||||
@ -475,24 +486,45 @@ public:
|
||||
// applied to the skeleton.
|
||||
for (size_t i = 0; i < bones.size(); ++i)
|
||||
{
|
||||
// Convert bone translations from xyz into xzy axes:
|
||||
std::swap(bones[i].translation[1], bones[i].translation[2]);
|
||||
if (yUp)
|
||||
{
|
||||
// TODO: this is all just guesses which seem to work for data
|
||||
// exported from XSI, rather than having been properly thought
|
||||
// through
|
||||
bones[i].translation[0] = -bones[i].translation[0];
|
||||
bones[i].orientation[0] = -bones[i].orientation[0];
|
||||
bones[i].orientation[3] = -bones[i].orientation[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert bone translations from xyz into xzy axes:
|
||||
std::swap(bones[i].translation[1], bones[i].translation[2]);
|
||||
|
||||
// To convert the quaternions: imagine you're using the axis/angle
|
||||
// representation, then swap the y,z basis vectors and change the
|
||||
// direction of rotation by negating the angle ( => negating sin(angle)
|
||||
// => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)
|
||||
// but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)
|
||||
std::swap(bones[i].orientation[1], bones[i].orientation[2]);
|
||||
bones[i].orientation[3] = -bones[i].orientation[3];
|
||||
// To convert the quaternions: imagine you're using the axis/angle
|
||||
// representation, then swap the y,z basis vectors and change the
|
||||
// direction of rotation by negating the angle ( => negating sin(angle)
|
||||
// => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)
|
||||
// but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)
|
||||
std::swap(bones[i].orientation[1], bones[i].orientation[2]);
|
||||
bones[i].orientation[3] = -bones[i].orientation[3];
|
||||
}
|
||||
}
|
||||
|
||||
// And do the same for prop points
|
||||
for (size_t i = 0; i < propPoints.size(); ++i)
|
||||
{
|
||||
std::swap(propPoints[i].translation[1], propPoints[i].translation[2]);
|
||||
std::swap(propPoints[i].orientation[1], propPoints[i].orientation[2]);
|
||||
propPoints[i].orientation[3] = -propPoints[i].orientation[3];
|
||||
if (yUp)
|
||||
{
|
||||
propPoints[i].translation[0] = -propPoints[i].translation[0];
|
||||
propPoints[i].orientation[0] = -propPoints[i].orientation[0];
|
||||
propPoints[i].orientation[3] = -propPoints[i].orientation[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::swap(propPoints[i].translation[1], propPoints[i].translation[2]);
|
||||
std::swap(propPoints[i].orientation[1], propPoints[i].orientation[2]);
|
||||
propPoints[i].orientation[3] = -propPoints[i].orientation[3];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "FCollada.h"
|
||||
#include "FCDocument/FCDocument.h"
|
||||
#include "FCDocument/FCDocumentTools.h"
|
||||
#include "FCDocument/FCDAnimated.h"
|
||||
#include "FCDocument/FCDAnimationCurve.h"
|
||||
#include "FCDocument/FCDController.h"
|
||||
@ -47,10 +48,10 @@ public:
|
||||
{
|
||||
FColladaErrorHandler err (xmlErrors);
|
||||
|
||||
std::auto_ptr<FCDocument> doc (FCollada::NewTopDocument());
|
||||
REQUIRE_SUCCESS(doc->LoadFromText("", input));
|
||||
FColladaDocument doc;
|
||||
doc.LoadFromText(input);
|
||||
|
||||
FCDSceneNode* root = doc->GetVisualSceneRoot();
|
||||
FCDSceneNode* root = doc.GetDocument()->GetVisualSceneRoot();
|
||||
REQUIRE(root != NULL, "has root object");
|
||||
|
||||
// Find the instance to convert
|
||||
@ -62,69 +63,21 @@ public:
|
||||
assert(instance);
|
||||
Log(LOG_INFO, "Converting '%s'", instance->GetEntity()->GetName().c_str());
|
||||
|
||||
FMVector3 upAxis = doc.GetDocument()->GetAsset()->GetUpAxis();
|
||||
bool yUp = (upAxis.y != 0); // assume either Y_UP or Z_UP (TODO: does anyone ever do X_UP?)
|
||||
|
||||
if (instance->GetType() == FCDEntityInstance::CONTROLLER)
|
||||
{
|
||||
FCDControllerInstance* controllerInstance = (FCDControllerInstance*)instance;
|
||||
|
||||
FixSkeletonRoots(controllerInstance);
|
||||
|
||||
float frameLength = 1.f / 30.f; // currently we always want to create PMDs at fixed 30fps
|
||||
|
||||
// Find the extents of the animation:
|
||||
|
||||
float timeStart, timeEnd;
|
||||
|
||||
// FCollada tools export <extra> info in the scene to specify the start
|
||||
// and end times.
|
||||
// If that isn't available, we have to search for the earliest and latest
|
||||
// keyframes on any of the bones.
|
||||
if (doc->HasStartTime() && doc->HasEndTime())
|
||||
{
|
||||
timeStart = doc->GetStartTime();
|
||||
timeEnd = doc->GetEndTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
timeStart = std::numeric_limits<float>::max();
|
||||
timeEnd = -std::numeric_limits<float>::max();
|
||||
for (size_t i = 0; i < controllerInstance->GetJointCount(); ++i)
|
||||
{
|
||||
FCDSceneNode* joint = controllerInstance->GetJoint(i);
|
||||
REQUIRE(joint != NULL, "joint exists");
|
||||
|
||||
int boneId = StdSkeletons::FindStandardBoneID(joint->GetName());
|
||||
if (boneId < 0)
|
||||
{
|
||||
// unrecognised joint - it's probably just a prop point
|
||||
// or something, so ignore it
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip unanimated joints
|
||||
if (joint->GetTransformCount() == 0)
|
||||
continue;
|
||||
|
||||
REQUIRE(joint->GetTransformCount() == 1, "joint has single transform");
|
||||
|
||||
FCDTransform* transform = joint->GetTransform(0);
|
||||
|
||||
// Skip unanimated joints (TODO: Which of these happens in practice?)
|
||||
if (! transform->IsAnimated())
|
||||
continue;
|
||||
|
||||
// Iterate over all curves
|
||||
FCDAnimated* anim = transform->GetAnimated();
|
||||
FCDAnimationCurveListList& curvesList = anim->GetCurves();
|
||||
for (size_t j = 0; j < curvesList.size(); ++j)
|
||||
{
|
||||
FCDAnimationCurveList& curves = curvesList[j];
|
||||
for (size_t k = 0; k < curves.size(); ++k)
|
||||
{
|
||||
FCDAnimationCurve* curve = curves[k];
|
||||
timeStart = std::min(timeStart, curve->GetKeys().front());
|
||||
timeEnd = std::max(timeEnd, curve->GetKeys().back());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GetAnimationRange(doc, controllerInstance, timeStart, timeEnd);
|
||||
|
||||
// Count frames; don't include the last keyframe
|
||||
size_t frameCount = (size_t)((timeEnd - timeStart) / frameLength - 0.5f);
|
||||
@ -142,36 +95,9 @@ public:
|
||||
std::vector<BoneTransform> frameBoneTransforms (boneCount, boneDefault);
|
||||
|
||||
// Move the model into the new animated pose
|
||||
for (size_t i = 0; i < controllerInstance->GetJointCount(); ++i)
|
||||
{
|
||||
FCDSceneNode* joint = controllerInstance->GetJoint(i);
|
||||
|
||||
int boneId = StdSkeletons::FindStandardBoneID(joint->GetName());
|
||||
if (boneId < 0)
|
||||
continue; // not a recognised bone - ignore it, same as before
|
||||
|
||||
FCDTransform* transform = joint->GetTransform(0);
|
||||
FCDAnimated* anim = transform->GetAnimated();
|
||||
anim->Evaluate(time);
|
||||
}
|
||||
// As well as the joints, we need to update all the ancestors
|
||||
// of the skeleton (e.g. the Bip01 node, since the skeleton is
|
||||
// hanging off Bip01-Pelvis instead).
|
||||
// So choose an arbitrary joint, which is hopefully actually the
|
||||
// top-most joint but it doesn't really matter, and evaluate all
|
||||
// its ancestors;
|
||||
if (controllerInstance->GetJointCount() >= 1)
|
||||
{
|
||||
FCDSceneNode* node = controllerInstance->GetJoint(0);
|
||||
while (node->GetParentCount() == 1) // (I guess this should be true in sensible models)
|
||||
{
|
||||
node = node->GetParent();
|
||||
if (node->IsJoint() && node->GetTransformCount() == 1)
|
||||
node->GetTransform(0)->GetAnimated()->Evaluate(time);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
// (We can't tell exactly which nodes should be animated, so
|
||||
// just update the entire world recursively)
|
||||
EvaluateAnimations(root, time);
|
||||
|
||||
// Convert the pose into the form require by the game
|
||||
for (size_t i = 0; i < controllerInstance->GetJointCount(); ++i)
|
||||
@ -204,7 +130,7 @@ public:
|
||||
}
|
||||
|
||||
// Convert into game's coordinate space
|
||||
TransformVertices(boneTransforms);
|
||||
TransformVertices(boneTransforms, yUp);
|
||||
|
||||
// Write out the file
|
||||
WritePSA(output, frameCount, boneCount, boneTransforms);
|
||||
@ -244,16 +170,141 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void TransformVertices(std::vector<BoneTransform>& bones)
|
||||
static void TransformVertices(std::vector<BoneTransform>& bones, bool yUp)
|
||||
{
|
||||
// (See PMDConvert.cpp for explanatory comments)
|
||||
for (size_t i = 0; i < bones.size(); ++i)
|
||||
{
|
||||
std::swap(bones[i].translation[1], bones[i].translation[2]);
|
||||
std::swap(bones[i].orientation[1], bones[i].orientation[2]);
|
||||
bones[i].orientation[3] = -bones[i].orientation[3];
|
||||
if (yUp)
|
||||
{
|
||||
bones[i].translation[0] = -bones[i].translation[0];
|
||||
bones[i].orientation[0] = -bones[i].orientation[0];
|
||||
bones[i].orientation[3] = -bones[i].orientation[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::swap(bones[i].translation[1], bones[i].translation[2]);
|
||||
std::swap(bones[i].orientation[1], bones[i].orientation[2]);
|
||||
bones[i].orientation[3] = -bones[i].orientation[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GetAnimationRange(FColladaDocument& doc, FCDControllerInstance* controllerInstance,
|
||||
float& timeStart, float& timeEnd)
|
||||
{
|
||||
// FCollada tools export <extra> info in the scene to specify the start
|
||||
// and end times.
|
||||
// If that isn't available, we have to search for the earliest and latest
|
||||
// keyframes on any of the bones.
|
||||
if (doc.GetDocument()->HasStartTime() && doc.GetDocument()->HasEndTime())
|
||||
{
|
||||
timeStart = doc.GetDocument()->GetStartTime();
|
||||
timeEnd = doc.GetDocument()->GetEndTime();
|
||||
return;
|
||||
}
|
||||
|
||||
// XSI exports relevant information in
|
||||
// <extra><technique profile="XSI"><SI_Scene><xsi_param sid="start">
|
||||
// (and 'end' and 'frameRate') so use those
|
||||
if (GetAnimationRange_XSI(doc, timeStart, timeEnd))
|
||||
return;
|
||||
|
||||
timeStart = std::numeric_limits<float>::max();
|
||||
timeEnd = -std::numeric_limits<float>::max();
|
||||
for (size_t i = 0; i < controllerInstance->GetJointCount(); ++i)
|
||||
{
|
||||
FCDSceneNode* joint = controllerInstance->GetJoint(i);
|
||||
REQUIRE(joint != NULL, "joint exists");
|
||||
|
||||
int boneId = StdSkeletons::FindStandardBoneID(joint->GetName());
|
||||
if (boneId < 0)
|
||||
{
|
||||
// unrecognised joint - it's probably just a prop point
|
||||
// or something, so ignore it
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip unanimated joints
|
||||
if (joint->GetTransformCount() == 0)
|
||||
continue;
|
||||
|
||||
for (size_t j = 0; j < joint->GetTransformCount(); ++j)
|
||||
{
|
||||
FCDTransform* transform = joint->GetTransform(j);
|
||||
|
||||
if (! transform->IsAnimated())
|
||||
continue;
|
||||
|
||||
// Iterate over all curves
|
||||
FCDAnimated* anim = transform->GetAnimated();
|
||||
FCDAnimationCurveListList& curvesList = anim->GetCurves();
|
||||
for (size_t j = 0; j < curvesList.size(); ++j)
|
||||
{
|
||||
FCDAnimationCurveList& curves = curvesList[j];
|
||||
for (size_t k = 0; k < curves.size(); ++k)
|
||||
{
|
||||
FCDAnimationCurve* curve = curves[k];
|
||||
timeStart = std::min(timeStart, curve->GetKeys().front());
|
||||
timeEnd = std::max(timeEnd, curve->GetKeys().back());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetAnimationRange_XSI(FColladaDocument& doc, float& timeStart, float& timeEnd)
|
||||
{
|
||||
FCDExtra* extra = doc.GetExtra();
|
||||
if (! extra) return false;
|
||||
|
||||
FCDEType* type = extra->GetDefaultType();
|
||||
if (! type) return false;
|
||||
|
||||
FCDETechnique* technique = type->FindTechnique("XSI");
|
||||
if (! technique) return false;
|
||||
|
||||
FCDENode* scene = technique->FindChildNode("SI_Scene");
|
||||
if (! scene) return false;
|
||||
|
||||
float start = FLT_MAX, end = -FLT_MAX, framerate = 0.f;
|
||||
|
||||
FCDENodeList paramNodes;
|
||||
scene->FindChildrenNodes("xsi_param", paramNodes);
|
||||
for (FCDENodeList::iterator it = paramNodes.begin(); it != paramNodes.end(); ++it)
|
||||
{
|
||||
if ((*it)->ReadAttribute("sid") == "start")
|
||||
start = FUStringConversion::ToFloat((*it)->GetContent());
|
||||
else if ((*it)->ReadAttribute("sid") == "end")
|
||||
end = FUStringConversion::ToFloat((*it)->GetContent());
|
||||
else if ((*it)->ReadAttribute("sid") == "frameRate")
|
||||
framerate = FUStringConversion::ToFloat((*it)->GetContent());
|
||||
}
|
||||
|
||||
if (framerate != 0.f && start != FLT_MAX && end != -FLT_MAX)
|
||||
{
|
||||
timeStart = start / framerate;
|
||||
timeEnd = end / framerate;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void EvaluateAnimations(FCDSceneNode* node, float time)
|
||||
{
|
||||
for (size_t i = 0; i < node->GetTransformCount(); ++i)
|
||||
{
|
||||
FCDTransform* transform = node->GetTransform(i);
|
||||
FCDAnimated* anim = transform->GetAnimated();
|
||||
if (anim)
|
||||
anim->Evaluate(time);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node->GetChildrenCount(); ++i)
|
||||
EvaluateAnimations(node->GetChild(i), time);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* standardBoneNames[] = {
|
||||
const char* standardBoneNames0[] = {
|
||||
/* */ "Bip01",
|
||||
/* */ "Bip01_Pelvis",
|
||||
/* */ "Bip01_Spine",
|
||||
@ -36,8 +36,65 @@ namespace
|
||||
/* */ "Bip01_R_Toe0Nub",
|
||||
// (the above comments just stop the indentation being dropped by
|
||||
// automatic code-formatting things...)
|
||||
// NULL
|
||||
// };
|
||||
// (TODO (important): do this stuff properly)
|
||||
// const char* standardBoneNames1[] = {
|
||||
/* */ "Biped_GlobalSRT",
|
||||
/* */ "Biped_Spineroot",
|
||||
/* */ "Biped_Spine01",
|
||||
/* */ "Biped_Spine02",
|
||||
/* */ "Biped_Spine03",
|
||||
/* */ "Biped_Spineeffector",
|
||||
/* */ "Biped_Lshoulderroot",
|
||||
/* */ "Biped_Lshoulder",
|
||||
/* */ "Biped_Lshouldereffector",
|
||||
/* */ "Biped_Larmroot",
|
||||
/* */ "Biped_Lbicept",
|
||||
/* */ "Biped_Lforearm",
|
||||
/* */ "Biped_Larmupvector",
|
||||
/* */ "Biped_Rshoulderroot",
|
||||
/* */ "Biped_Rshoulder",
|
||||
/* */ "Biped_Rshouldereffector",
|
||||
/* */ "Biped_Rarmroot",
|
||||
/* */ "Biped_Rbicept",
|
||||
/* */ "Biped_Rforearm",
|
||||
/* */ "Biped_Rarmupvector",
|
||||
/* */ "Biped_neckroot",
|
||||
/* */ "Biped_neck",
|
||||
/* */ "Biped_head",
|
||||
/* */ "Biped_headeffector",
|
||||
/* */ "Biped_Llegroot",
|
||||
/* */ "Biped_Lthigh",
|
||||
/* */ "Biped_Lshin",
|
||||
/* */ "Biped_Rlegroot",
|
||||
/* */ "Biped_Rthigh",
|
||||
/* */ "Biped_Rshin",
|
||||
/* */ "Biped_Llegupvector",
|
||||
/* */ "Biped_Rlegupvector",
|
||||
/* */ "Biped_Larmeffector",
|
||||
/* */ "Biped_Lhandroot",
|
||||
/* */ "Biped_Lhand",
|
||||
/* */ "Biped_Lfingers",
|
||||
/* */ "Biped_Lhandeffector",
|
||||
/* */ "Biped_Llegeffector",
|
||||
/* */ "Biped_Lfooteffector",
|
||||
/* */ "Biped_Lfoot",
|
||||
/* */ "Biped_Ltoe",
|
||||
/* */ "Biped_Ltoeeffector",
|
||||
/* */ "Biped_Rarmeffector",
|
||||
/* */ "Biped_Rhandroot",
|
||||
/* */ "Biped_Rhand",
|
||||
/* */ "Biped_Rfingers",
|
||||
/* */ "Biped_Rhandeffector",
|
||||
/* */ "Biped_Rlegeffector",
|
||||
/* */ "Biped_Rfootroot",
|
||||
/* */ "Biped_Rfoot",
|
||||
/* */ "Biped_Rtoe",
|
||||
/* */ "Biped_Rtoeeffector",
|
||||
NULL
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace StdSkeletons
|
||||
@ -45,15 +102,15 @@ namespace StdSkeletons
|
||||
int GetBoneCount()
|
||||
{
|
||||
int i = 0;
|
||||
while (standardBoneNames[i] != NULL)
|
||||
while (standardBoneNames0[i] != NULL)
|
||||
++i;
|
||||
return i;
|
||||
}
|
||||
|
||||
int FindStandardBoneID(const std::string& name)
|
||||
{
|
||||
for (int i = 0; standardBoneNames[i] != NULL; ++i)
|
||||
if (standardBoneNames[i] == name)
|
||||
for (int i = 0; standardBoneNames0[i] != NULL; ++i)
|
||||
if (standardBoneNames0[i] == name)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ extern void Log(int severity, const char* fmt, ...);
|
||||
#include "FCollada.h"
|
||||
#include "FCDocument/FCDAsset.h"
|
||||
#include "FCDocument/FCDocument.h"
|
||||
#include "FCDocument/FCDocumentTools.h"
|
||||
#include "FCDocument/FCDAnimated.h"
|
||||
#include "FCDocument/FCDAnimationCurve.h"
|
||||
#include "FCDocument/FCDController.h"
|
||||
@ -27,6 +28,9 @@ extern void Log(int severity, const char* fmt, ...);
|
||||
#include "FCDocument/FCDGeometrySource.h"
|
||||
#include "FCDocument/FCDSceneNode.h"
|
||||
#include "FCDocument/FCDSkinController.h"
|
||||
#include "FUtils/FUDaeSyntax.h"
|
||||
#include "FUtils/FUFileManager.h"
|
||||
#include "FUtils/FUXmlParser.h"
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user