1
1
forked from 0ad/0ad

# Conversion of skeleton animations from COLLADA

This was SVN commit r4725.
This commit is contained in:
Ykkrosh 2006-12-26 22:43:09 +00:00
parent 24f06de815
commit b5403bcfc7
15 changed files with 548 additions and 236 deletions

View File

@ -0,0 +1,180 @@
#include "precompiled.h"
#include "CommonConvert.h"
#include "FCollada.h"
#include "FCDocument/FCDSceneNode.h"
#include "FCDocument/FCDSkinController.h"
#include <cassert>
/** Throws a ColladaException unless the value is true. */
#define REQUIRE(value, message) require_(__LINE__, value, "Assertion not satisfied", message)
/** Throws a ColladaException unless the status is successful. */
#define REQUIRE_SUCCESS(status) require_(__LINE__, status)
void require_(int line, bool value, const char* type, const char* message)
{
if (value) return;
char linestr[16];
sprintf(linestr, "%d", line);
throw ColladaException(std::string(type) + " (line " + linestr + "): " + message);
}
void require_(int line, const FUStatus& status)
{
require_(line, status, "FCollada error", status.GetErrorString());
}
/** Error handler for libxml2 */
void errorHandler(void* ctx, const char* msg, ...)
{
char buffer[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap);
buffer[sizeof(buffer)-1] = '\0';
va_end(ap);
*((std::string*)ctx) += buffer;
}
//////////////////////////////////////////////////////////////////////////
struct FoundInstance
{
FCDEntityInstance* instance;
FMMatrix44 transform;
};
/**
* Recursively finds all entities under the current node. If onlyMarked is
* set, only matches entities where the user-defined property was set to
* "export" in the modelling program.
*
* @param node root of subtree to search
* @param instances output - appends matching entities
* @param transform transform matrix of current subtree
* @param onlyMarked only match entities with "export" property
*/
static void FindInstances(FCDSceneNode* node, std::vector<FoundInstance>& instances, const FMMatrix44& transform, bool onlyMarked)
{
for (size_t i = 0; i < node->GetChildrenCount(); ++i)
{
FCDSceneNode* child = node->GetChild(i);
FindInstances(child, instances, transform * node->ToMatrix(), onlyMarked);
}
for (size_t i = 0; i < node->GetInstanceCount(); ++i)
{
if (onlyMarked)
{
if (node->GetNote() != "export")
continue;
}
// Only accept instances of appropriate types, and not e.g. lights
FCDEntity::Type type = node->GetInstance(i)->GetEntityType();
if (! (type == FCDEntity::GEOMETRY || type == FCDEntity::CONTROLLER))
continue;
FoundInstance f;
f.transform = transform * node->ToMatrix();
f.instance = node->GetInstance(i);
instances.push_back(f);
}
}
bool FindSingleInstance(FCDSceneNode* node, FCDEntityInstance*& instance, FMMatrix44& transform)
{
std::vector<FoundInstance> instances;
FindInstances(node, instances, FMMatrix44::Identity, true);
if (instances.size() > 1)
{
Log(LOG_ERROR, "Found too many export-marked objects");
return false;
}
if (instances.empty())
{
FindInstances(node, instances, FMMatrix44::Identity, false);
if (instances.size() > 1)
{
Log(LOG_ERROR, "Found too many possible objects to convert - try adding the 'export' property to disambiguate one");
return false;
}
if (instances.empty())
{
Log(LOG_ERROR, "Didn't find any objects in the scene");
return false;
}
}
assert(instances.size() == 1); // if we got this far
instance = instances[0].instance;
transform = instances[0].transform;
return true;
}
//////////////////////////////////////////////////////////////////////////
static bool ReverseSortWeight(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
{
return (a.weight > b.weight);
}
void SkinReduceInfluences(FCDSkinController* skin, size_t maxInfluenceCount, float minimumWeight)
{
FCDWeightedMatches& weightedMatches = skin->GetWeightedMatches();
for (FCDWeightedMatches::iterator itM = weightedMatches.begin(); itM != weightedMatches.end(); ++itM)
{
FCDJointWeightPairList& weights = (*itM);
FCDJointWeightPairList newWeights;
for (FCDJointWeightPairList::iterator itW = weights.begin(); itW != weights.end(); ++itW)
{
// If this joint already has an influence, just add the weight
// instead of adding a new influence
bool done = false;
for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
{
if (itW->jointIndex == itNW->jointIndex)
{
itNW->weight += itW->weight;
done = true;
break;
}
}
if (done)
continue;
// Not had this joint before, so add it
newWeights.push_back(*itW);
}
// Put highest-weighted influences at the front of the list
sort(newWeights.begin(), newWeights.end(), ReverseSortWeight);
// Limit the maximum number of influences
if (newWeights.size() > maxInfluenceCount)
newWeights.resize(maxInfluenceCount);
// Enforce the minimum weight per influence
while (!newWeights.empty() && newWeights.back().weight < minimumWeight)
newWeights.pop_back();
// Renormalise, so sum(weights)=1
float totalWeight = 0;
for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
totalWeight += itNW->weight;
for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
itNW->weight /= totalWeight;
// Copy new weights into the skin
weights = newWeights;
}
skin->SetDirtyFlag();
}

View File

@ -0,0 +1,77 @@
#ifndef COMMONCONVERT_H__
#define COMMONCONVERT_H__
#include <exception>
#include <string>
class FUStatus;
class FCDSceneNode;
class FCDEntityInstance;
class FMMatrix44;
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();
}
private:
std::string msg;
};
struct OutputCB
{
virtual void operator() (const char* data, unsigned int length)=0;
};
/** Throws a ColladaException unless the value is true. */
#define REQUIRE(value, message) require_(__LINE__, value, "Assertion not satisfied", message)
/** Throws a ColladaException unless the status is successful. */
#define REQUIRE_SUCCESS(status) require_(__LINE__, status)
void require_(int line, bool value, const char* type, const char* message);
void require_(int line, const FUStatus& status);
/** Outputs a structure, using sizeof to get the size. */
template<typename T> void write(OutputCB& output, const T& data)
{
output((char*)&data, sizeof(T));
}
/** Error handler for libxml2 */
void errorHandler(void* ctx, const char* msg, ...);
/**
* Tries to find a single suitable entity instance in the scene. Fails if there
* are none, or if there are too many and it's not clear which one should
* be converted.
*
* @param node root scene node to search under
* @param instance output - the found entity instance (if any)
* @param transform - the world-space transform of the found entity
*
* @return true if one was found
*/
bool FindSingleInstance(FCDSceneNode* node, FCDEntityInstance*& instance, FMMatrix44& transform);
/**
* Like FCDSkinController::ReduceInfluences but works correctly.
* Additionally, multiple influences for the same joint-vertex pair are
* collapsed into a single influence.
*/
void SkinReduceInfluences(FCDSkinController* skin, size_t maxInfluenceCount, float minimumWeight);
#endif // COMMONCONVERT_H__

View File

@ -1,34 +0,0 @@
#ifndef CONVERTER_H__
#define CONVERTER_H__
#include <exception>
#include <string>
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();
}
private:
std::string msg;
};
struct OutputCB
{
virtual void operator() (const char* data, unsigned int length)=0;
};
void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors);
#endif // CONVERTER_H__

View File

@ -1,6 +1,8 @@
#include "precompiled.h"
#include "Converter.h"
#include "CommonConvert.h"
#include "PMDConvert.h"
#include "PSAConvert.h"
#include <cstdarg>
#include <cassert>
@ -76,15 +78,15 @@ struct BufferedOutputCallback : public OutputCB
}
};
int convert_dae_to_pmd(const char* dae, OutputFn pmd_writer, void* cb_data)
int convert_dae_to_whatever(const char* dae, OutputFn writer, void* cb_data, void(*conv)(const char*, OutputCB&, std::string&))
{
Log(LOG_INFO, "Starting conversion");
std::string xmlErrors;
BufferedOutputCallback cb(pmd_writer, cb_data);
BufferedOutputCallback cb(writer, cb_data);
try
{
ColladaToPMD(dae, cb, xmlErrors);
conv(dae, cb, xmlErrors);
}
catch (ColladaException e)
{
@ -105,3 +107,13 @@ int convert_dae_to_pmd(const char* dae, OutputFn pmd_writer, void* cb_data)
return 0;
}
int convert_dae_to_pmd(const char* dae, OutputFn pmd_writer, void* cb_data)
{
return convert_dae_to_whatever(dae, pmd_writer, cb_data, ColladaToPMD);
}
int convert_dae_to_psa(const char* dae, OutputFn psa_writer, void* cb_data)
{
return convert_dae_to_whatever(dae, psa_writer, cb_data, ColladaToPSA);
}

View File

@ -27,6 +27,7 @@ typedef void (*OutputFn) (void* cb_data, const char* data, unsigned int length);
EXPORT void set_logger(LogFn logger);
EXPORT int convert_dae_to_pmd(const char* dae, OutputFn pmd_writer, void* cb_data);
EXPORT int convert_dae_to_psa(const char* dae, OutputFn psa_writer, void* cb_data);
#ifdef __cplusplus
};

View File

@ -2,6 +2,12 @@
#include "GeomReindex.h"
#include "FCollada.h"
#include "FCDocument/FCDEntity.h"
#include "FCDocument/FCDGeometryPolygons.h"
#include "FCDocument/FCDGeometrySource.h"
#include "FCDocument/FCDSkinController.h"
#include <cassert>
struct VertexData

View File

@ -1 +1,9 @@
void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin = NULL);
#ifndef GEOMREINDEX_H__
#define GEOMREINDEX_H__
class FCDGeometryPolygons;
class FCDSkinController;
void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin = 0);
#endif // GEOMREINDEX_H__

View File

@ -2,6 +2,8 @@
#include "Maths.h"
#include "FCollada.h"
FMMatrix44 operator+ (const FMMatrix44& a, const FMMatrix44& b)
{
FMMatrix44 r;

View File

@ -1,6 +1,8 @@
#ifndef MATHS_H__
#define MATHS_H__
class FMMatrix44;
extern FMMatrix44 operator+ (const FMMatrix44& a, const FMMatrix44& b);
extern FMMatrix44 operator/ (const FMMatrix44& a, const float b);
extern FMMatrix44 QuatToMatrix(float x, float y, float z, float w);

View File

@ -1,6 +1,7 @@
#include "precompiled.h"
#include "Converter.h"
#include "PMDConvert.h"
#include "CommonConvert.h"
#include "FCollada.h"
#include "FCDocument/FCDocument.h"
@ -20,30 +21,6 @@
#include <cassert>
#include <vector>
/** Throws a ColladaException unless the value is true. */
#define REQUIRE(value, message) require_(__LINE__, value, "Assertion not satisfied", message)
/** Throws a ColladaException unless the status is successful. */
#define REQUIRE_SUCCESS(status) require_(__LINE__, status)
void require_(int line, bool value, const char* type, const char* message)
{
if (value) return;
char linestr[16];
sprintf(linestr, "%d", line);
throw ColladaException(std::string(type) + " (line " + linestr + "): " + message);
}
void require_(int line, const FUStatus& status)
{
require_(line, status, "FCollada error", status.GetErrorString());
}
/** Outputs a structure, using sizeof to get the size. */
template<typename T> void write(OutputCB& output, const T& data)
{
output((char*)&data, sizeof(T));
}
const int maxInfluences = 4;
struct VertexBlend
{
@ -59,24 +36,11 @@ struct BoneTransform
};
/** Error handler for libxml2 */
void errorHandler(void* ctx, const char* msg, ...)
{
char buffer[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap);
buffer[sizeof(buffer)-1] = '\0';
va_end(ap);
*((std::string*)ctx) += buffer;
}
class Converter
class PMDConvert
{
public:
/**
* Converts a COLLADA XML document into the PMD format.
* Converts a COLLADA XML document into the PMD mesh format.
*
* @param input XML document to parse
* @param output callback for writing the PMD data; called lots of times
@ -385,162 +349,6 @@ public:
bones[i].orientation[3] = -bones[i].orientation[3];
}
}
//////////////////////////////////////////////////////////////////////////
struct FoundInstance
{
FCDEntityInstance* instance;
FMMatrix44 transform;
};
/**
* Tries to find a single suitable entity instance in the scene. Fails if there
* are none, or if there are too many and it's not clear which one should
* be converted.
*
* @param node root scene node to search under
* @param instance output - the found entity instance (if any)
* @param transform - the world-space transform of the found entity
*
* @return true if one was found
*/
static bool FindSingleInstance(FCDSceneNode* node, FCDEntityInstance*& instance, FMMatrix44& transform)
{
std::vector<FoundInstance> instances;
FindInstances(node, instances, FMMatrix44::Identity, true);
if (instances.size() > 1)
{
Log(LOG_ERROR, "Found too many export-marked objects");
return false;
}
if (instances.empty())
{
FindInstances(node, instances, FMMatrix44::Identity, false);
if (instances.size() > 1)
{
Log(LOG_ERROR, "Found too many possible objects to convert - try adding the 'export' property to disambiguate one");
return false;
}
if (instances.empty())
{
Log(LOG_ERROR, "Didn't find any objects in the scene");
return false;
}
}
assert(instances.size() == 1); // if we got this far
instance = instances[0].instance;
transform = instances[0].transform;
return true;
}
/**
* Recursively finds all entities under the current node. If onlyMarked is
* set, only matches entities where the user-defined property was set to
* "export" in the modelling program.
*
* @param node root of subtree to search
* @param instances output - appends matching entities
* @param transform transform matrix of current subtree
* @param onlyMarked only match entities with "export" property
*/
static void FindInstances(FCDSceneNode* node, std::vector<FoundInstance>& instances, const FMMatrix44& transform, bool onlyMarked)
{
for (size_t i = 0; i < node->GetChildrenCount(); ++i)
{
FCDSceneNode* child = node->GetChild(i);
FindInstances(child, instances, transform * node->ToMatrix(), onlyMarked);
}
for (size_t i = 0; i < node->GetInstanceCount(); ++i)
{
if (onlyMarked)
{
if (node->GetNote() != "export")
continue;
}
// Only accept instances of appropriate types, and not e.g. lights
FCDEntity::Type type = node->GetInstance(i)->GetEntityType();
if (! (type == FCDEntity::GEOMETRY || type == FCDEntity::CONTROLLER))
continue;
FoundInstance f;
f.transform = transform * node->ToMatrix();
f.instance = node->GetInstance(i);
instances.push_back(f);
}
}
//////////////////////////////////////////////////////////////////////////
static bool ReverseSortWeight(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
{
return (a.weight > b.weight);
}
/**
* Like FCDSkinController::ReduceInfluences but works correctly.
* Additionally, multiple influences for the same joint-vertex pair are
* collapsed into a single influence.
*/
static void SkinReduceInfluences(FCDSkinController* skin, uint32 maxInfluenceCount, float minimumWeight)
{
FCDWeightedMatches& weightedMatches = skin->GetWeightedMatches();
for (FCDWeightedMatches::iterator itM = weightedMatches.begin(); itM != weightedMatches.end(); ++itM)
{
FCDJointWeightPairList& weights = (*itM);
FCDJointWeightPairList newWeights;
for (FCDJointWeightPairList::iterator itW = weights.begin(); itW != weights.end(); ++itW)
{
// If this joint already has an influence, just add the weight
// instead of adding a new influence
bool done = false;
for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
{
if (itW->jointIndex == itNW->jointIndex)
{
itNW->weight += itW->weight;
done = true;
break;
}
}
if (done)
continue;
// Not had this joint before, so add it
newWeights.push_back(*itW);
}
// Put highest-weighted influences at the front of the list
sort(newWeights.begin(), newWeights.end(), ReverseSortWeight);
// Limit the maximum number of influences
if (newWeights.size() > maxInfluenceCount)
newWeights.resize(maxInfluenceCount);
// Enforce the minimum weight per influence
while (!newWeights.empty() && newWeights.back().weight < minimumWeight)
newWeights.pop_back();
// Renormalise, so sum(weights)=1
float totalWeight = 0;
for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
totalWeight += itNW->weight;
for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
itNW->weight /= totalWeight;
// Copy new weights into the skin
weights = newWeights;
}
skin->SetDirtyFlag();
}
};
@ -550,5 +358,5 @@ public:
void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
{
Converter::ColladaToPMD(input, output, xmlErrors);
PMDConvert::ColladaToPMD(input, output, xmlErrors);
}

View File

@ -0,0 +1,10 @@
#ifndef PMDCONVERT_H__
#define PMDCONVERT_H__
#include <string>
struct OutputCB;
void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors);
#endif // PMDCONVERT_H__

View File

@ -0,0 +1,220 @@
#include "precompiled.h"
#include "PSAConvert.h"
#include "CommonConvert.h"
#include "FCollada.h"
#include "FCDocument/FCDocument.h"
#include "FCDocument/FCDAnimated.h"
#include "FCDocument/FCDAnimationCurve.h"
#include "FCDocument/FCDController.h"
#include "FCDocument/FCDControllerInstance.h"
#include "FCDocument/FCDGeometry.h"
#include "FCDocument/FCDGeometryMesh.h"
#include "FCDocument/FCDGeometryPolygons.h"
#include "FCDocument/FCDGeometrySource.h"
#include "FCDocument/FCDSceneNode.h"
#include "FCDocument/FCDSkinController.h"
#include "StdSkeletons.h"
#include "Decompose.h"
#include "Maths.h"
#include "GeomReindex.h"
#include <cassert>
#include <vector>
struct BoneTransform
{
float translation[3];
float orientation[4];
};
class PSAConvert
{
public:
/**
* Converts a COLLADA XML document into the PSA animation format.
*
* @param input XML document to parse
* @param output callback for writing the PSA data; called lots of times
* with small strings
* @param xmlErrors output - errors reported by the XML parser
* @throws ColladaException on failure
*/
static void ColladaToPSA(const char* input, OutputCB& output, std::string& xmlErrors)
{
FUStatus ret;
// Grab all the error output from libxml2. Be careful to never use
// libxml2 outside this function without having first set/reset the
// errorfunc (since xmlErrors won't be valid any more).
xmlSetGenericErrorFunc(&xmlErrors, &errorHandler);
std::auto_ptr<FCDocument> doc (FCollada::NewTopDocument());
REQUIRE_SUCCESS(doc->LoadFromText("", input));
FCDSceneNode* root = doc->GetVisualSceneRoot();
// Find the instance to convert
FCDEntityInstance* instance;
FMMatrix44 entityTransform;
if (! FindSingleInstance(root, instance, entityTransform))
throw ColladaException("Couldn't find object to convert");
assert(instance);
Log(LOG_INFO, "Converting '%s'", instance->GetEntity()->GetName().c_str());
if (instance->GetEntity()->GetType() == FCDEntity::CONTROLLER)
{
FCDController* controller = (FCDController*)instance->GetEntity();
REQUIRE(controller->HasSkinController(), "has skin controller");
FCDSkinController* skin = controller->GetSkinController();
// Find the first and last times which have animations
float timeStart = FLT_MAX, timeEnd = -FLT_MAX;
for (size_t i = 0; i < skin->GetJointCount(); ++i)
{
FCDJointMatrixPair* joint = skin->GetJoint(i);
REQUIRE(joint->joint != NULL, "joint exists");
// Skip unanimated joints
if (joint->joint->GetTransformCount() == 0)
continue;
REQUIRE(joint->joint->GetTransformCount() == 1, "joint has single transform");
FCDTransform* transform = joint->joint->GetTransform(0);
// Skip unanimated joints again. (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());
}
}
}
float frameLength = 1.f / 30.f;
// Count frames; don't include the last keyframe
size_t frameCount = (size_t)((timeEnd - timeStart) / frameLength - 0.5f);
size_t boneCount = StdSkeletons::GetBoneCount();
std::vector<BoneTransform> boneTransforms;
for (size_t frame = 0; frame < frameCount; ++frame)
{
float time = timeStart + frameLength * frame;
BoneTransform boneDefault = { { 0, 0, 0 }, { 0, 0, 0, 1 } };
std::vector<BoneTransform> frameBoneTransforms (boneCount, boneDefault);
// Move the model into the new animated pose
for (size_t i = 0; i < skin->GetJointCount(); ++i)
{
FCDTransform* transform = skin->GetJoint(i)->joint->GetTransform(0);
FCDAnimated* anim = transform->GetAnimated();
anim->Evaluate(time);
}
// Convert the pose into the form require by the game
for (size_t i = 0; i < skin->GetJointCount(); ++i)
{
FCDSceneNode* jointNode = skin->GetJoint(i)->joint;
FMMatrix44 worldTransform = jointNode->CalculateWorldTransform();
HMatrix matrix;
memcpy(matrix, worldTransform.Transposed().m, sizeof(matrix));
AffineParts parts;
decomp_affine(matrix, &parts);
BoneTransform b = {
{ parts.t.x, parts.t.y, parts.t.z },
{ parts.q.x, parts.q.y, parts.q.z, parts.q.w }
};
int boneId = StdSkeletons::FindStandardBoneID(jointNode->GetName());
REQUIRE(boneId >= 0, "recognised bone name");
frameBoneTransforms[boneId] = b;
}
// Push frameBoneTransforms onto the back of boneTransforms
copy(frameBoneTransforms.begin(), frameBoneTransforms.end(), inserter(boneTransforms, boneTransforms.end()));
}
// Convert into game's coordinate space
TransformVertices(boneTransforms);
// Write out the file
WritePSA(output, frameCount, boneCount, boneTransforms);
}
else
{
throw ColladaException("Unrecognised object type");
}
}
/**
* Writes the animation data in the PSA format.
*/
static void WritePSA(OutputCB& output, size_t frameCount, size_t boneCount, const std::vector<BoneTransform>& boneTransforms)
{
output("PSSA", 4); // magic number
write<uint32>(output, 1); // version number
write<uint32>(output, (uint32)(
4 + 0 + // name
4 + // frameLength
4 + 4 + // numBones, numFrames
7*4*boneCount*frameCount // boneStates
)); // data size
// Name
write<uint32>(output, 0);
// Frame length
write<float>(output, 1000/30.f);
write<uint32>(output, (uint32)boneCount);
write<uint32>(output, (uint32)frameCount);
for (size_t i = 0; i < boneCount*frameCount; ++i)
{
output((char*)&boneTransforms[i], 7*4);
}
}
static void TransformVertices(std::vector<BoneTransform>& bones)
{
// (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];
}
}
};
// The above stuff is just in a class since I don't like having to bother
// with forward declarations of functions - but provide the plain function
// interface here:
void ColladaToPSA(const char* input, OutputCB& output, std::string& xmlErrors)
{
PSAConvert::ColladaToPSA(input, output, xmlErrors);
}

View File

@ -0,0 +1,10 @@
#ifndef PSACONVERT_H__
#define PSACONVERT_H__
#include <string>
struct OutputCB;
void ColladaToPSA(const char* input, OutputCB& output, std::string& xmlErrors);
#endif // PSACONVERT_H__

View File

@ -1,6 +1,8 @@
#ifndef STDSKELETONS_H__
#define STDSKELETONS_H__
#include <string>
namespace StdSkeletons
{
extern int GetBoneCount();

View File

@ -11,6 +11,7 @@ extern void Log(int severity, const char* fmt, ...);
#include "FCollada.h"
#include "FCDocument/FCDocument.h"
#include "FCDocument/FCDAnimated.h"
#include "FCDocument/FCDController.h"
#include "FCDocument/FCDGeometry.h"
#include "FCDocument/FCDGeometryMesh.h"
@ -18,3 +19,10 @@ extern void Log(int severity, const char* fmt, ...);
#include "FCDocument/FCDGeometrySource.h"
#include "FCDocument/FCDSceneNode.h"
#include "FCDocument/FCDSkinController.h"
#include <cassert>
#include <string>
// FCollada pollutes the global namespace by defining these
// to std::{min,max}, so undo its macros
#undef min
#undef max