1
0
forked from 0ad/0ad

# Incomplete improved support for XSI models/animations

This was SVN commit r4946.
This commit is contained in:
Ykkrosh 2007-03-09 13:40:28 +00:00
parent 8eec30c383
commit 781e630b2c
6 changed files with 432 additions and 134 deletions

View File

@ -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();
}
}

View File

@ -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__

View File

@ -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];
}
}
}

View File

@ -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);
}
};

View File

@ -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;
}

View File

@ -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>