forked from 0ad/0ad
Semi-broken skeletally-animated model COLLADA->PMD converter.
This was SVN commit r4691.
This commit is contained in:
parent
207d54e825
commit
5d8bef5f6e
@ -4,17 +4,23 @@
|
||||
|
||||
#include "FCollada.h"
|
||||
#include "FCDocument/FCDocument.h"
|
||||
#include "FCDocument/FCDSceneNode.h"
|
||||
#include "FCDocument/FCDController.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 <cassert>
|
||||
#include <vector>
|
||||
|
||||
/** Throws a ColladaException unless the value is true. */
|
||||
#define REQUIRE(value, message) require_(__LINE__, value, "Failed assertion", message)
|
||||
#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)
|
||||
@ -37,16 +43,19 @@ template<typename T> void write(OutputFn output, const T& data)
|
||||
output((char*)&data, sizeof(T));
|
||||
}
|
||||
|
||||
const int maxInfluences = 4;
|
||||
struct VertexBlend
|
||||
{
|
||||
uint8 bones[4];
|
||||
float weights[4];
|
||||
uint8 bones[maxInfluences];
|
||||
float weights[maxInfluences];
|
||||
};
|
||||
VertexBlend defaultInfluences = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
|
||||
|
||||
struct BoneTransform
|
||||
{
|
||||
float translation[3];
|
||||
float orientation[4];
|
||||
FMMatrix44 matrix; // not output in the PMD, but useful for calculating bone transforms
|
||||
};
|
||||
|
||||
|
||||
@ -82,7 +91,7 @@ public:
|
||||
// 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);
|
||||
xmlSetGenericErrorFunc(&xmlErrors, &errorHandler);
|
||||
|
||||
std::auto_ptr<FCDocument> doc (FCollada::NewTopDocument());
|
||||
REQUIRE_SUCCESS(doc->LoadFromText("", input));
|
||||
@ -96,42 +105,135 @@ public:
|
||||
throw ColladaException("Couldn't find object to convert");
|
||||
|
||||
assert(instance);
|
||||
Log(LOG_INFO, "Converting '%s'", instance->GetEntity()->GetName().c_str());
|
||||
Log(LOG_INFO, "Converting '%s'", instance->GetEntity()->GetName().c_str());
|
||||
|
||||
if (instance->GetEntity()->GetType() == FCDEntity::GEOMETRY)
|
||||
{
|
||||
Log(LOG_INFO, "Found static geometry");
|
||||
|
||||
FCDGeometry* geom = (FCDGeometry*)instance->GetEntity();
|
||||
REQUIRE(geom->IsMesh(), "geometry is mesh");
|
||||
FCDGeometryMesh* mesh = geom->GetMesh();
|
||||
REQUIRE(mesh->IsTriangles(), "mesh is made of triangles");
|
||||
REQUIRE(mesh->GetPolygonsCount() == 1, "mesh has single set of polygons");
|
||||
FCDGeometryPolygons* polys = mesh->GetPolygons(0);
|
||||
FCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)instance->GetEntity());
|
||||
|
||||
size_t vertices = polys->GetFaceVertexCount();
|
||||
FloatList position, normal, texcoord;
|
||||
|
||||
DeindexInput(polys, FUDaeGeometryInput::POSITION, position, 3);
|
||||
DeindexInput(polys, FUDaeGeometryInput::NORMAL, normal, 3);
|
||||
|
||||
if (polys->FindInput(FUDaeGeometryInput::TEXCOORD))
|
||||
{
|
||||
DeindexInput(polys, FUDaeGeometryInput::TEXCOORD, texcoord, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Accept untextured models
|
||||
else // Accept untextured models
|
||||
texcoord.resize(vertices*2, 0.f);
|
||||
}
|
||||
|
||||
assert(position.size() == vertices*3);
|
||||
assert(normal.size() == vertices*3);
|
||||
assert(texcoord.size() == vertices*2);
|
||||
|
||||
TransformVertices(position, normal, transform);
|
||||
TransformVertices(position, normal, transform);
|
||||
// TODO: optimise at least enough to merge identical vertices
|
||||
|
||||
WritePMD(output, vertices, 0, &position[0], &normal[0], &texcoord[0], NULL, NULL);
|
||||
|
||||
WritePMD(output, vertices, 0, &position[0], &normal[0], &texcoord[0], NULL, NULL);
|
||||
}
|
||||
else if (instance->GetEntity()->GetType() == FCDEntity::CONTROLLER)
|
||||
{
|
||||
FCDController* controller = (FCDController*)instance->GetEntity();
|
||||
|
||||
REQUIRE(controller->HasSkinController(), "has skin controller");
|
||||
FCDSkinController* skin = controller->GetSkinController();
|
||||
|
||||
// Get the skinned mesh for this entity
|
||||
FCDEntity* baseTarget = controller->GetBaseTarget();
|
||||
REQUIRE(baseTarget->GetType() == FCDEntity::GEOMETRY, "base target is geometry");
|
||||
FCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)baseTarget);
|
||||
|
||||
// Make sure it doesn't use more bones per vertex than the game can handle
|
||||
// SkinReduceInfluences(skin, maxInfluences, 0.001f);
|
||||
|
||||
// XXX The game is broken if there's >1 influence per vertex. Until
|
||||
// that's fixed, just limit it to 1 and put up with the ugly blending...
|
||||
SkinReduceInfluences(skin, 1, 0.001f);
|
||||
|
||||
// Convert the bone influences into VertexBlend structures for the PMD
|
||||
|
||||
std::vector<VertexBlend> vertexBlends; // one per vertex
|
||||
|
||||
const FCDWeightedMatches& boneWeights = skin->GetVertexInfluences();
|
||||
for (size_t i = 0; i < boneWeights.size(); ++i)
|
||||
{
|
||||
VertexBlend influences = defaultInfluences;
|
||||
|
||||
assert(boneWeights[i].size() <= maxInfluences); // guaranteed by ReduceInfluences
|
||||
for (size_t j = 0; j < boneWeights[i].size(); ++j)
|
||||
{
|
||||
uint32 jointIdx = boneWeights[i][j].jointIndex;
|
||||
REQUIRE(jointIdx <= 0xFF, "sensible number of joints");
|
||||
FCDSceneNode* joint = skin->GetJoint(jointIdx)->joint;
|
||||
if (! joint)
|
||||
{
|
||||
Log(LOG_WARNING, "Vertexes influenced by nonexistent joint");
|
||||
continue;
|
||||
}
|
||||
int boneId = StdSkeletons::FindStandardBoneID(joint->GetName());
|
||||
REQUIRE(boneId >= 0, "recognised bone name");
|
||||
influences.bones[j] = (uint8)boneId;
|
||||
influences.weights[j] = boneWeights[i][j].weight;
|
||||
}
|
||||
|
||||
vertexBlends.push_back(influences);
|
||||
}
|
||||
|
||||
BoneTransform boneDefault = { { 0, 0, 0 }, { 0, 0, 0, 1 } };
|
||||
std::vector<BoneTransform> bones (StdSkeletons::GetBoneCount(), boneDefault);
|
||||
|
||||
transform = skin->GetBindShapeTransform();
|
||||
|
||||
for (size_t i = 0; i < skin->GetJointCount(); ++i)
|
||||
{
|
||||
FCDJointMatrixPair* joint = skin->GetJoint(i);
|
||||
|
||||
if (! joint->joint)
|
||||
{
|
||||
Log(LOG_WARNING, "Skin has nonexistent joint");
|
||||
continue;
|
||||
}
|
||||
|
||||
FMMatrix44 bindPose = joint->invertedBindPose.Inverted();
|
||||
|
||||
HMatrix matrix;
|
||||
memcpy(matrix, bindPose.Transposed().m, sizeof(matrix));
|
||||
// matrix = bindPose^T, to match what decomp_affine wants
|
||||
|
||||
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 },
|
||||
bindPose
|
||||
};
|
||||
|
||||
int boneId = StdSkeletons::FindStandardBoneID(joint->joint->GetName());
|
||||
REQUIRE(boneId >= 0, "recognised bone name");
|
||||
bones[boneId] = b;
|
||||
}
|
||||
|
||||
size_t vertices = polys->GetFaceVertexCount();
|
||||
FloatList position, normal, texcoord;
|
||||
std::vector<VertexBlend> blends;
|
||||
DeindexInput(polys, FUDaeGeometryInput::POSITION, position, 3);
|
||||
DeindexInput(polys, vertexBlends, blends);
|
||||
DeindexInput(polys, FUDaeGeometryInput::NORMAL, normal, 3);
|
||||
if (polys->FindInput(FUDaeGeometryInput::TEXCOORD))
|
||||
DeindexInput(polys, FUDaeGeometryInput::TEXCOORD, texcoord, 2);
|
||||
else // Accept untextured models
|
||||
texcoord.resize(vertices*2, 0.f);
|
||||
|
||||
assert(position.size() == vertices*3);
|
||||
assert(normal.size() == vertices*3);
|
||||
assert(texcoord.size() == vertices*2);
|
||||
|
||||
TransformVertices(position, normal, blends, bones, transform);
|
||||
|
||||
WritePMD(output, vertices, bones.size(), &position[0], &normal[0], &texcoord[0], &blends[0], &bones[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -189,24 +291,33 @@ public:
|
||||
write<uint32>(output, (uint32)boneCount);
|
||||
for (size_t i = 0; i < boneCount; ++i)
|
||||
{
|
||||
write(output, boneTransforms[i]);
|
||||
output((char*)&boneTransforms[i], 7*4);
|
||||
}
|
||||
|
||||
// Prop points data
|
||||
write<uint32>(output, 0);
|
||||
}
|
||||
|
||||
static FCDGeometryPolygons* GetPolysFromGeometry(FCDGeometry* geom)
|
||||
{
|
||||
REQUIRE(geom->IsMesh(), "geometry is mesh");
|
||||
FCDGeometryMesh* mesh = geom->GetMesh();
|
||||
REQUIRE(mesh->IsTriangles(), "mesh is made of triangles");
|
||||
REQUIRE(mesh->GetPolygonsCount() == 1, "mesh has single set of polygons");
|
||||
return mesh->GetPolygons(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from value-array plus indexes-into-array-per-vertex, into
|
||||
* values-per-vertex (because that's what PMD wants).
|
||||
*/
|
||||
static void DeindexInput(FCDGeometryPolygons* polys, FUDaeGeometryInput::Semantic semantic, FloatList& out, size_t outStride)
|
||||
static void DeindexInput(const FCDGeometryPolygons* polys, FUDaeGeometryInput::Semantic semantic, FloatList& out, size_t outStride)
|
||||
{
|
||||
FCDGeometryPolygonsInput* input = polys->FindInput(semantic);
|
||||
UInt32List* indices = polys->FindIndices(input);
|
||||
const FCDGeometryPolygonsInput* input = polys->FindInput(semantic);
|
||||
const UInt32List* indices = polys->FindIndices(input);
|
||||
REQUIRE(input && indices, "has expected polygon input");
|
||||
FCDGeometrySource* source = input->GetSource();
|
||||
FloatList& data = source->GetSourceData();
|
||||
const FCDGeometrySource* source = input->GetSource();
|
||||
const FloatList& data = source->GetSourceData();
|
||||
size_t stride = source->GetSourceStride();
|
||||
|
||||
for (size_t i = 0; i < indices->size(); ++i)
|
||||
@ -214,6 +325,18 @@ public:
|
||||
out.push_back(data[(*indices)[i]*stride + j]);
|
||||
}
|
||||
|
||||
static void DeindexInput(const FCDGeometryPolygons* polys, const std::vector<VertexBlend>& inBlends, std::vector<VertexBlend>& outBlends)
|
||||
{
|
||||
assert(outBlends.empty());
|
||||
|
||||
const FCDGeometryPolygonsInput* input = polys->FindInput(FUDaeGeometryInput::POSITION);
|
||||
const UInt32List* indices = polys->FindIndices(input);
|
||||
|
||||
for (size_t i = 0; i < indices->size(); ++i)
|
||||
outBlends.push_back(inBlends[(*indices)[i]]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies world-space transform to vertex data, and flips into other-handed
|
||||
* coordinate space.
|
||||
@ -241,6 +364,86 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void TransformVertices(FloatList& position, FloatList& normal, std::vector<VertexBlend>& blends, std::vector<BoneTransform>& bones, const FMMatrix44& transform)
|
||||
{
|
||||
for (size_t vtxId = 0; vtxId < position.size()/3; ++vtxId)
|
||||
{
|
||||
// Skinned vertices need to be transformed by the inverse of their
|
||||
// rest states:
|
||||
|
||||
float zero16[16] = {0};
|
||||
FMMatrix44 bindPoseTransform (zero16);
|
||||
|
||||
// Calculate the weighted sum of influence matrices
|
||||
for (size_t j = 0; j < maxInfluences; ++j)
|
||||
{
|
||||
// Ignore unused bone influences
|
||||
if (blends[vtxId].bones[j] == 0xff)
|
||||
continue;
|
||||
|
||||
float weight = blends[vtxId].weights[j];
|
||||
const BoneTransform& b = bones[blends[vtxId].bones[j]];
|
||||
|
||||
// The transformation matrix could be reconstructed with:
|
||||
/*
|
||||
FMMatrix44 R = QuatToMatrix(b.orientation[0], b.orientation[1], b.orientation[2], b.orientation[3]);
|
||||
FMMatrix44 T = FMMatrix44::TranslationMatrix(FMVector3(b.translation, 0));
|
||||
FMMatrix44 boneMatrix = T * R;
|
||||
*/
|
||||
// but since we generated orientation/translation from a matrix,
|
||||
// just use that matrix directly:
|
||||
FMMatrix44 boneMatrix = b.matrix;
|
||||
|
||||
bindPoseTransform = bindPoseTransform + weight * boneMatrix;
|
||||
}
|
||||
|
||||
FMVector3 pos (&position[vtxId*3], 0);
|
||||
FMVector3 norm (&normal[vtxId*3], 0);
|
||||
|
||||
// Apply the scene-node transforms first
|
||||
pos = transform.TransformCoordinate(pos);
|
||||
norm = transform.TransformVector(norm).Normalize();
|
||||
|
||||
// Apply the inverse bind pose transform, so the model will display
|
||||
// correctly after it's been transform back again
|
||||
FMMatrix44 bindPoseTransformInverse = bindPoseTransform.Inverted();
|
||||
pos = bindPoseTransformInverse.TransformCoordinate(pos);
|
||||
norm = bindPoseTransformInverse.TransformVector(norm);
|
||||
|
||||
// Switch from Max's coordinate system into the game's:
|
||||
|
||||
std::swap(pos.y, pos.z);
|
||||
std::swap(norm.y, norm.z);
|
||||
|
||||
// and copy back into the original array
|
||||
|
||||
position[vtxId*3+0] = pos.x;
|
||||
position[vtxId*3+1] = pos.y;
|
||||
position[vtxId*3+2] = pos.z;
|
||||
|
||||
normal[vtxId*3+0] = norm.x;
|
||||
normal[vtxId*3+1] = norm.y;
|
||||
normal[vtxId*3+2] = norm.z;
|
||||
}
|
||||
|
||||
// We also need to change the bone rest states into the new coordinate
|
||||
// system, so it'll look correct when displayed without any animation
|
||||
// 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]);
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct FoundInstance
|
||||
@ -317,12 +520,85 @@ public:
|
||||
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();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
48
source/collada/Maths.cpp
Normal file
48
source/collada/Maths.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "Maths.h"
|
||||
|
||||
FMMatrix44 operator+ (const FMMatrix44& a, const FMMatrix44& b)
|
||||
{
|
||||
FMMatrix44 r;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
r[x][y] = a[x][y] + b[x][y];
|
||||
return r;
|
||||
}
|
||||
|
||||
FMMatrix44 operator/ (const FMMatrix44& a, const float b)
|
||||
{
|
||||
FMMatrix44 r;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
r[x][y] = a[x][y] / b;
|
||||
return r;
|
||||
}
|
||||
|
||||
FMMatrix44 QuatToMatrix(float x, float y, float z, float w)
|
||||
{
|
||||
FMMatrix44 r;
|
||||
|
||||
r[0][0] = 1.0f - (y*y*2 + z*z*2);
|
||||
r[1][0] = x*y*2 - w*z*2;
|
||||
r[2][0] = x*z*2 + w*y*2;
|
||||
r[3][0] = 0;
|
||||
|
||||
r[0][1] = x*y*2 + w*z*2;
|
||||
r[1][1] = 1.0f - (x*x*2 + z*z*2);
|
||||
r[2][1] = y*z*2 - w*x*2;
|
||||
r[3][1] = 0;
|
||||
|
||||
r[0][2] = x*z*2 - w*y*2;
|
||||
r[1][2] = y*z*2 + w*x*2;
|
||||
r[2][2] = 1.0f - (x*x*2 + y*y*2);
|
||||
r[3][2] = 0;
|
||||
|
||||
r[0][3] = 0;
|
||||
r[1][3] = 0;
|
||||
r[2][3] = 0;
|
||||
r[3][3] = 1;
|
||||
|
||||
return r;
|
||||
}
|
8
source/collada/Maths.h
Normal file
8
source/collada/Maths.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef MATHS_H__
|
||||
#define MATHS_H__
|
||||
|
||||
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);
|
||||
|
||||
#endif // MATHS_H__
|
60
source/collada/StdSkeletons.cpp
Normal file
60
source/collada/StdSkeletons.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "StdSkeletons.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* standardBoneNames[] = {
|
||||
/* */ "Bip01",
|
||||
/* */ "Bip01_Pelvis",
|
||||
/* */ "Bip01_Spine",
|
||||
/* */ "Bip01_Spine1",
|
||||
/* */ "Bip01_Neck",
|
||||
/* */ "Bip01_Head",
|
||||
/* */ "Bip01_HeadNub",
|
||||
/* */ "Bip01_L_Clavicle",
|
||||
/* */ "Bip01_L_UpperArm",
|
||||
/* */ "Bip01_L_Forearm",
|
||||
/* */ "Bip01_L_Hand",
|
||||
/* */ "Bip01_L_Finger0",
|
||||
/* */ "Bip01_L_Finger0Nub",
|
||||
/* */ "Bip01_R_Clavicle",
|
||||
/* */ "Bip01_R_UpperArm",
|
||||
/* */ "Bip01_R_Forearm",
|
||||
/* */ "Bip01_R_Hand",
|
||||
/* */ "Bip01_R_Finger0",
|
||||
/* */ "Bip01_R_Finger0Nub",
|
||||
/* */ "Bip01_L_Thigh",
|
||||
/* */ "Bip01_L_Calf",
|
||||
/* */ "Bip01_L_Foot",
|
||||
/* */ "Bip01_L_Toe0",
|
||||
/* */ "Bip01_L_Toe0Nub",
|
||||
/* */ "Bip01_R_Thigh",
|
||||
/* */ "Bip01_R_Calf",
|
||||
/* */ "Bip01_R_Foot",
|
||||
/* */ "Bip01_R_Toe0",
|
||||
/* */ "Bip01_R_Toe0Nub",
|
||||
// (the above comments just stop the indentation being dropped by
|
||||
// automatic code-formatting things...)
|
||||
NULL
|
||||
};
|
||||
}
|
||||
|
||||
namespace StdSkeletons
|
||||
{
|
||||
int GetBoneCount()
|
||||
{
|
||||
int i = 0;
|
||||
while (standardBoneNames[i] != NULL)
|
||||
++i;
|
||||
return i;
|
||||
}
|
||||
|
||||
int FindStandardBoneID(const std::string& name)
|
||||
{
|
||||
for (int i = 0; standardBoneNames[i] != NULL; ++i)
|
||||
if (standardBoneNames[i] == name)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
}
|
10
source/collada/StdSkeletons.h
Normal file
10
source/collada/StdSkeletons.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef STDSKELETONS_H__
|
||||
#define STDSKELETONS_H__
|
||||
|
||||
namespace StdSkeletons
|
||||
{
|
||||
extern int GetBoneCount();
|
||||
extern int FindStandardBoneID(const std::string& name);
|
||||
}
|
||||
|
||||
#endif // STDSKELETONS_H__
|
@ -11,8 +11,10 @@ extern void Log(int severity, const char* fmt, ...);
|
||||
|
||||
#include "FCollada.h"
|
||||
#include "FCDocument/FCDocument.h"
|
||||
#include "FCDocument/FCDSceneNode.h"
|
||||
#include "FCDocument/FCDController.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"
|
||||
|
@ -7,7 +7,7 @@ binaries = '../../../binaries'
|
||||
|
||||
dll_filename = {
|
||||
'posix': './libCollada_dbg.so',
|
||||
'nt': 'Collada.dll',
|
||||
'nt': 'Collada_dbg.dll',
|
||||
}[os.name]
|
||||
|
||||
library = cdll.LoadLibrary('%s/system/%s' % (binaries, dll_filename))
|
||||
@ -42,6 +42,19 @@ def clean_dir(path):
|
||||
os.makedirs(path)
|
||||
except OSError:
|
||||
pass # (ignore errors if it already exists)
|
||||
|
||||
def create_actor(mesh, texture, idleanim, corpseanim, gatheranim):
|
||||
actor = ET.Element('actor', version='1')
|
||||
ET.SubElement(actor, 'castshadow')
|
||||
group = ET.SubElement(actor, 'group')
|
||||
variant = ET.SubElement(group, 'variant', frequency='100', name='Base')
|
||||
ET.SubElement(variant, 'mesh').text = mesh+'.pmd'
|
||||
ET.SubElement(variant, 'texture').text = texture+'.dds'
|
||||
animations = ET.SubElement(variant, 'animations')
|
||||
ET.SubElement(animations, 'animation', file=idleanim+'.psa', name='Idle', speed='100')
|
||||
ET.SubElement(animations, 'animation', file=corpseanim+'.psa', name='Corpse', speed='100')
|
||||
ET.SubElement(animations, 'animation', file=gatheranim+'.psa', name='Build', speed='100')
|
||||
return ET.tostring(actor)
|
||||
|
||||
################################
|
||||
|
||||
@ -51,7 +64,8 @@ test_mod = binaries + '/data/mods/_test.collada'
|
||||
clean_dir(test_mod + '/art/meshes')
|
||||
clean_dir(test_mod + '/art/actors')
|
||||
|
||||
for test_file in ['cube', 'jav2']:
|
||||
for test_file in ['cube', 'jav2', 'teapot_basic', 'teapot_skin', 'plane_skin', 'dude_skin']:
|
||||
#for test_file in ['dude_skin']:
|
||||
input_filename = '%s/%s.dae' % (test_data, test_file)
|
||||
output_filename = '%s/art/meshes/%s.pmd' % (test_mod, test_file)
|
||||
|
||||
@ -59,10 +73,5 @@ for test_file in ['cube', 'jav2']:
|
||||
output = convert_dae_to_pmd(input)
|
||||
open(output_filename, 'wb').write(output)
|
||||
|
||||
actor = ET.Element('actor', version='1')
|
||||
group = ET.SubElement(actor, 'group')
|
||||
variant = ET.SubElement(group, 'variant', frequency='100', name='Base')
|
||||
ET.SubElement(variant, 'mesh').text = test_file+'.pmd'
|
||||
ET.SubElement(variant, 'texture').text = 'male.dds'
|
||||
|
||||
open('%s/art/actors/%s.xml' % (test_mod, test_file), 'w').write(ET.tostring(actor))
|
||||
xml = create_actor(test_file, 'male', 'dudeidle', 'dudecorpse', 'dudechop')
|
||||
open('%s/art/actors/%s.xml' % (test_mod, test_file), 'w').write(xml)
|
||||
|
@ -108,22 +108,26 @@ bool CModel::InitModel(CModelDefPtr modeldef)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SkinPoint: skin the given point using the given blend and matrix data
|
||||
static CVector3D SkinPoint(const CVector3D& pos,const SVertexBlend& blend,
|
||||
static CVector3D SkinPoint(const CVector3D& pos, const SVertexBlend& blend,
|
||||
const CMatrix3D* bonestates)
|
||||
{
|
||||
CVector3D result,tmp;
|
||||
|
||||
// must have at least one valid bone if we're using SkinPoint
|
||||
debug_assert(blend.m_Bone[0]!=0xff);
|
||||
if (blend.m_Bone[0] == 0xff)
|
||||
{
|
||||
ONCE( debug_warn("SkinPoint called for vertex with no bone weights") );
|
||||
return CVector3D(0, 0, 0);
|
||||
}
|
||||
|
||||
const CMatrix3D& m=bonestates[blend.m_Bone[0]];
|
||||
m.Transform(pos,result);
|
||||
result*=blend.m_Weight[0];
|
||||
const CMatrix3D& m = bonestates[blend.m_Bone[0]];
|
||||
m.Transform(pos, result);
|
||||
result *= blend.m_Weight[0];
|
||||
|
||||
for (int i=1;i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff;i++) {
|
||||
const CMatrix3D& m=bonestates[blend.m_Bone[i]];
|
||||
m.Transform(pos,tmp);
|
||||
result+=tmp*blend.m_Weight[i];
|
||||
for (int i = 1; i < SVertexBlend::SIZE && blend.m_Bone[i] != 0xff; i++) {
|
||||
const CMatrix3D& m = bonestates[blend.m_Bone[i]];
|
||||
m.Transform(pos, tmp);
|
||||
result += tmp*blend.m_Weight[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -39,7 +39,12 @@ static void SkinPoint(const SModelVertex& vertex,const CMatrix3D* matrices,CVect
|
||||
const SVertexBlend& blend=vertex.m_Blend;
|
||||
|
||||
// must have at least one valid bone if we're using SkinPoint
|
||||
debug_assert(blend.m_Bone[0]!=0xff);
|
||||
if (blend.m_Bone[0] == 0xff)
|
||||
{
|
||||
// (CModel should have already complained about this)
|
||||
result = CVector3D(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const CMatrix3D& m=matrices[blend.m_Bone[0]];
|
||||
m.Transform(vertex.m_Coords,result);
|
||||
@ -60,7 +65,12 @@ static void SkinNormal(const SModelVertex& vertex, const CMatrix3D* invtranspmat
|
||||
const SVertexBlend& blend=vertex.m_Blend;
|
||||
|
||||
// must have at least one valid bone if we're using SkinNormal
|
||||
debug_assert(blend.m_Bone[0]!=0xff);
|
||||
if (blend.m_Bone[0] == 0xff)
|
||||
{
|
||||
// (CModel should have already complained about this)
|
||||
result = CVector3D(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const CMatrix3D& m = invtranspmatrices[blend.m_Bone[0]];
|
||||
m.Rotate(vertex.m_Norm, result);
|
||||
|
Loading…
Reference in New Issue
Block a user