2010-08-19 23:57:43 +02:00
|
|
|
/* Copyright (C) 2010 Wildfire Games.
|
2009-04-18 19:00:33 +02:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2006-12-06 01:06:05 +01:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2006-12-26 23:43:09 +01:00
|
|
|
#include "PMDConvert.h"
|
|
|
|
#include "CommonConvert.h"
|
2006-12-06 01:06:05 +01:00
|
|
|
|
|
|
|
#include "FCollada.h"
|
2007-03-03 03:20:57 +01:00
|
|
|
#include "FCDocument/FCDAsset.h"
|
2006-12-06 01:06:05 +01:00
|
|
|
#include "FCDocument/FCDocument.h"
|
2007-03-09 14:40:28 +01:00
|
|
|
#include "FCDocument/FCDocumentTools.h"
|
2006-12-12 03:11:24 +01:00
|
|
|
#include "FCDocument/FCDController.h"
|
2007-03-02 21:09:27 +01:00
|
|
|
#include "FCDocument/FCDControllerInstance.h"
|
2006-12-06 01:06:05 +01:00
|
|
|
#include "FCDocument/FCDGeometry.h"
|
|
|
|
#include "FCDocument/FCDGeometryMesh.h"
|
|
|
|
#include "FCDocument/FCDGeometryPolygons.h"
|
2008-08-25 00:22:25 +02:00
|
|
|
#include "FCDocument/FCDGeometryPolygonsInput.h"
|
2007-03-03 03:20:57 +01:00
|
|
|
#include "FCDocument/FCDGeometryPolygonsTools.h"
|
2006-12-06 01:06:05 +01:00
|
|
|
#include "FCDocument/FCDGeometrySource.h"
|
2006-12-12 03:11:24 +01:00
|
|
|
#include "FCDocument/FCDSceneNode.h"
|
|
|
|
#include "FCDocument/FCDSkinController.h"
|
|
|
|
|
|
|
|
#include "StdSkeletons.h"
|
|
|
|
#include "Decompose.h"
|
|
|
|
#include "Maths.h"
|
2006-12-26 00:42:53 +01:00
|
|
|
#include "GeomReindex.h"
|
2006-12-06 01:06:05 +01:00
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <vector>
|
|
|
|
|
2007-01-26 19:26:45 +01:00
|
|
|
const size_t maxInfluences = 4;
|
2006-12-06 01:06:05 +01:00
|
|
|
struct VertexBlend
|
|
|
|
{
|
2006-12-12 03:11:24 +01:00
|
|
|
uint8 bones[maxInfluences];
|
|
|
|
float weights[maxInfluences];
|
2006-12-06 01:06:05 +01:00
|
|
|
};
|
2006-12-12 03:11:24 +01:00
|
|
|
VertexBlend defaultInfluences = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2007-03-02 16:55:46 +01:00
|
|
|
struct PropPoint
|
|
|
|
{
|
|
|
|
std::string name;
|
|
|
|
float translation[3];
|
|
|
|
float orientation[4];
|
|
|
|
uint8 bone;
|
|
|
|
};
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2008-09-08 01:11:29 +02:00
|
|
|
// Based on FMVector3::Normalize, but that function uses a static member
|
|
|
|
// FMVector3::XAxis which causes irritating linker errors. Rather than trying
|
|
|
|
// to make that XAxis work in a cross-platform way, just reimplement Normalize:
|
|
|
|
static FMVector3 FMVector3_Normalize(const FMVector3& vec)
|
|
|
|
{
|
|
|
|
float l = vec.Length();
|
|
|
|
if (l > 0.0f)
|
|
|
|
return FMVector3(vec.x/l, vec.y/l, vec.z/l);
|
|
|
|
else
|
|
|
|
return FMVector3(1.0f, 0.0f, 0.0f);
|
|
|
|
}
|
2008-07-03 22:28:22 +02:00
|
|
|
|
2010-08-19 23:57:43 +02:00
|
|
|
static void AddStaticPropPoints(std::vector<PropPoint> &propPoints, FCDSceneNode* node)
|
|
|
|
{
|
2010-11-15 03:18:49 +01:00
|
|
|
if (node->GetName().find("prop-") == 0 || node->GetName().find("prop_") == 0)
|
2010-08-19 23:57:43 +02:00
|
|
|
{
|
|
|
|
// Strip off the "prop-" from the name
|
|
|
|
std::string propPointName (node->GetName().substr(5));
|
|
|
|
|
|
|
|
Log(LOG_INFO, "Adding prop point %s", propPointName.c_str());
|
|
|
|
|
|
|
|
// Get translation and orientation of the world transform
|
|
|
|
// (TODO: maybe this should be relative to the main mesh's transform?)
|
|
|
|
|
|
|
|
FMMatrix44 transform = node->CalculateWorldTransform();
|
|
|
|
|
|
|
|
HMatrix matrix;
|
|
|
|
memcpy(matrix, transform.Transposed().m, sizeof(matrix));
|
|
|
|
|
|
|
|
AffineParts parts;
|
|
|
|
decomp_affine(matrix, &parts);
|
|
|
|
|
|
|
|
// Add prop point to list (attached to no bone)
|
|
|
|
|
|
|
|
PropPoint p = {
|
|
|
|
propPointName,
|
|
|
|
{ parts.t.x, parts.t.y, parts.t.z },
|
|
|
|
{ parts.q.x, parts.q.y, parts.q.z, parts.q.w },
|
|
|
|
0xff
|
|
|
|
};
|
|
|
|
propPoints.push_back(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search children for prop points
|
|
|
|
for (size_t i = 0; i < node->GetChildrenCount(); ++i)
|
|
|
|
AddStaticPropPoints(propPoints, node->GetChild(i));
|
|
|
|
}
|
|
|
|
|
2006-12-26 23:43:09 +01:00
|
|
|
class PMDConvert
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
/**
|
2006-12-26 23:43:09 +01:00
|
|
|
* Converts a COLLADA XML document into the PMD mesh format.
|
2006-12-06 01:06:05 +01:00
|
|
|
*
|
|
|
|
* @param input XML document to parse
|
|
|
|
* @param output callback for writing the PMD data; called lots of times
|
|
|
|
* with small strings
|
|
|
|
* @param xmlErrors output - errors reported by the XML parser
|
|
|
|
* @throws ColladaException on failure
|
|
|
|
*/
|
2006-12-20 04:22:24 +01:00
|
|
|
static void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
2007-03-16 19:00:58 +01:00
|
|
|
CommonConvert converter(input, xmlErrors);
|
2007-03-03 03:20:57 +01:00
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
if (converter.GetInstance().GetEntity()->GetType() == FCDEntity::GEOMETRY)
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
|
|
|
Log(LOG_INFO, "Found static geometry");
|
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
FCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)converter.GetInstance().GetEntity());
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
// Convert the geometry into a suitable form for the game
|
|
|
|
ReindexGeometry(polys);
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
|
|
|
|
FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
|
|
|
|
FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD);
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
const uint32* indicesCombined = inputPosition->GetIndices();
|
|
|
|
size_t indicesCombinedCount = inputPosition->GetIndexCount();
|
|
|
|
// (ReindexGeometry guarantees position/normal/texcoord have the same indexes)
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
FCDGeometrySource* sourcePosition = inputPosition->GetSource();
|
|
|
|
FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
|
|
|
|
FCDGeometrySource* sourceTexcoord = inputTexcoord->GetSource();
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
float* dataPosition = sourcePosition->GetData();
|
|
|
|
float* dataNormal = sourceNormal ->GetData();
|
|
|
|
float* dataTexcoord = sourceTexcoord->GetData();
|
|
|
|
size_t vertexCount = sourcePosition->GetDataCount() / 3;
|
|
|
|
assert(sourcePosition->GetDataCount() == vertexCount*3);
|
|
|
|
assert(sourceNormal ->GetDataCount() == vertexCount*3);
|
|
|
|
assert(sourceTexcoord->GetDataCount() == vertexCount*2);
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
TransformVertices(dataPosition, dataNormal, vertexCount, converter.GetEntityTransform(), converter.IsYUp());
|
2006-12-26 00:42:53 +01:00
|
|
|
|
|
|
|
std::vector<VertexBlend> boneWeights;
|
|
|
|
std::vector<BoneTransform> boneTransforms;
|
2007-03-02 16:55:46 +01:00
|
|
|
std::vector<PropPoint> propPoints;
|
2008-09-10 00:30:14 +02:00
|
|
|
AddDefaultPropPoints(propPoints);
|
2006-12-26 00:42:53 +01:00
|
|
|
|
2010-08-19 23:57:43 +02:00
|
|
|
AddStaticPropPoints(propPoints, converter.GetInstance().GetParent());
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoord, vertexCount, boneWeights, boneTransforms, propPoints);
|
2006-12-12 03:11:24 +01:00
|
|
|
}
|
2007-03-16 19:00:58 +01:00
|
|
|
else if (converter.GetInstance().GetType() == FCDEntityInstance::CONTROLLER)
|
2006-12-12 03:11:24 +01:00
|
|
|
{
|
2007-03-02 16:55:46 +01:00
|
|
|
Log(LOG_INFO, "Found skinned geometry");
|
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
FCDControllerInstance& controllerInstance = static_cast<FCDControllerInstance&>(converter.GetInstance());
|
2007-03-01 01:24:34 +01:00
|
|
|
|
|
|
|
// (NB: GetType is deprecated and should be replaced with HasType,
|
|
|
|
// except that has irritating linker errors when using a DLL, so don't
|
|
|
|
// bother)
|
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
assert(converter.GetInstance().GetEntity()->GetType() == FCDEntity::CONTROLLER); // assume this is always true?
|
|
|
|
FCDController* controller = static_cast<FCDController*>(converter.GetInstance().GetEntity());
|
2006-12-12 03:11:24 +01:00
|
|
|
|
|
|
|
FCDSkinController* skin = controller->GetSkinController();
|
2007-03-01 01:24:34 +01:00
|
|
|
REQUIRE(skin != NULL, "is skin controller");
|
|
|
|
|
2007-03-09 14:40:28 +01:00
|
|
|
FixSkeletonRoots(controllerInstance);
|
|
|
|
|
2007-03-01 01:24:34 +01:00
|
|
|
// 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)
|
2007-03-16 19:00:58 +01:00
|
|
|
size_t jointCount = std::min(skin->GetJointCount(), controllerInstance.GetJointCount());
|
|
|
|
if (skin->GetJointCount() != controllerInstance.GetJointCount())
|
|
|
|
{
|
|
|
|
Log(LOG_WARNING, "Mismatched bone counts (skin has %d, skeleton has %d)",
|
|
|
|
skin->GetJointCount(), controllerInstance.GetJointCount());
|
2008-08-25 00:22:25 +02:00
|
|
|
for (size_t i = 0; i < skin->GetJointCount(); ++i)
|
2009-11-06 11:59:10 +01:00
|
|
|
Log(LOG_INFO, "Skin joint %d: %s", i, skin->GetJoint(i)->GetId().c_str());
|
2008-08-25 00:22:25 +02:00
|
|
|
for (size_t i = 0; i < controllerInstance.GetJointCount(); ++i)
|
2009-11-06 11:59:10 +01:00
|
|
|
Log(LOG_INFO, "Skeleton joint %d: %s", i, controllerInstance.GetJoint(i)->GetName().c_str());
|
2007-03-16 19:00:58 +01:00
|
|
|
}
|
2006-12-12 03:11:24 +01:00
|
|
|
|
|
|
|
// Get the skinned mesh for this entity
|
2007-03-16 19:00:58 +01:00
|
|
|
FCDGeometry* baseGeometry = controller->GetBaseGeometry();
|
|
|
|
REQUIRE(baseGeometry != NULL, "controller has base geometry");
|
|
|
|
FCDGeometryPolygons* polys = GetPolysFromGeometry(baseGeometry);
|
2006-12-12 03:11:24 +01:00
|
|
|
|
|
|
|
// Make sure it doesn't use more bones per vertex than the game can handle
|
2006-12-15 17:09:30 +01:00
|
|
|
SkinReduceInfluences(skin, maxInfluences, 0.001f);
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
// Convert the geometry into a suitable form for the game
|
|
|
|
ReindexGeometry(polys, skin);
|
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
const Skeleton& skeleton = FindSkeleton(controllerInstance);
|
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
// Convert the bone influences into VertexBlend structures for the PMD:
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2007-03-02 16:55:46 +01:00
|
|
|
bool hasComplainedAboutNonexistentJoints = false; // because we want to emit a warning only once
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
std::vector<VertexBlend> boneWeights; // one per vertex
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
const FCDSkinControllerVertex* vertexInfluences = skin->GetVertexInfluences();
|
|
|
|
for (size_t i = 0; i < skin->GetInfluenceCount(); ++i)
|
2006-12-12 03:11:24 +01:00
|
|
|
{
|
|
|
|
VertexBlend influences = defaultInfluences;
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
assert(vertexInfluences[i].GetPairCount() <= maxInfluences);
|
2007-03-02 16:55:46 +01:00
|
|
|
// guaranteed by ReduceInfluences; necessary for avoiding
|
|
|
|
// out-of-bounds writes to the VertexBlend
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
for (size_t j = 0; j < vertexInfluences[i].GetPairCount(); ++j)
|
2006-12-12 03:11:24 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
uint32 jointIdx = vertexInfluences[i].GetPair(j)->jointIndex;
|
|
|
|
REQUIRE(jointIdx <= 0xFF, "sensible number of joints (<256)"); // because we only have a u8 to store them in
|
2007-03-01 01:24:34 +01:00
|
|
|
|
|
|
|
// Find the joint on the skeleton, after checking it really exists
|
|
|
|
FCDSceneNode* joint = NULL;
|
2007-03-16 19:00:58 +01:00
|
|
|
if (jointIdx < controllerInstance.GetJointCount())
|
|
|
|
joint = controllerInstance.GetJoint(jointIdx);
|
2007-03-01 01:24:34 +01:00
|
|
|
|
2007-03-02 16:55:46 +01:00
|
|
|
// Complain on error
|
2006-12-12 03:11:24 +01:00
|
|
|
if (! joint)
|
|
|
|
{
|
2006-12-26 00:42:53 +01:00
|
|
|
if (! hasComplainedAboutNonexistentJoints)
|
|
|
|
{
|
|
|
|
Log(LOG_WARNING, "Vertexes influenced by nonexistent joint");
|
|
|
|
hasComplainedAboutNonexistentJoints = true;
|
|
|
|
}
|
2006-12-12 03:11:24 +01:00
|
|
|
continue;
|
|
|
|
}
|
2007-03-02 16:55:46 +01:00
|
|
|
|
|
|
|
// Store into the VertexBlend
|
2008-08-25 00:22:25 +02:00
|
|
|
int boneId = skeleton.GetBoneID(joint->GetName().c_str());
|
2007-03-16 19:00:58 +01:00
|
|
|
if (boneId < 0)
|
|
|
|
{
|
|
|
|
// The relevant joint does exist, but it's not a recognised
|
|
|
|
// bone in our chosen skeleton structure
|
2009-11-06 11:59:10 +01:00
|
|
|
Log(LOG_ERROR, "Vertex influenced by unrecognised bone '%s'", joint->GetName().c_str());
|
2007-03-16 19:00:58 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2006-12-12 03:11:24 +01:00
|
|
|
influences.bones[j] = (uint8)boneId;
|
2008-08-25 00:22:25 +02:00
|
|
|
influences.weights[j] = vertexInfluences[i].GetPair(j)->weight;
|
2006-12-12 03:11:24 +01:00
|
|
|
}
|
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
boneWeights.push_back(influences);
|
2006-12-06 01:06:05 +01:00
|
|
|
}
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2007-03-02 16:55:46 +01:00
|
|
|
// Convert the bind pose into BoneTransform structures for the PMD:
|
|
|
|
|
|
|
|
BoneTransform boneDefault = { { 0, 0, 0 }, { 0, 0, 0, 1 } }; // identity transform
|
2007-03-16 19:00:58 +01:00
|
|
|
std::vector<BoneTransform> boneTransforms (skeleton.GetBoneCount(), boneDefault);
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2007-03-01 01:24:34 +01:00
|
|
|
for (size_t i = 0; i < jointCount; ++i)
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
2007-03-16 19:00:58 +01:00
|
|
|
FCDSceneNode* joint = controllerInstance.GetJoint(i);
|
2007-03-02 21:09:27 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
int boneId = skeleton.GetRealBoneID(joint->GetName().c_str());
|
2007-03-02 21:09:27 +01:00
|
|
|
if (boneId < 0)
|
|
|
|
{
|
|
|
|
// unrecognised joint - it's probably just a prop point
|
|
|
|
// or something, so ignore it
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
FMMatrix44 bindPose = skin->GetJoint(i)->GetBindPoseInverse().Inverted();
|
2006-12-12 03:11:24 +01:00
|
|
|
|
|
|
|
HMatrix matrix;
|
|
|
|
memcpy(matrix, bindPose.Transposed().m, sizeof(matrix));
|
2007-03-02 16:55:46 +01:00
|
|
|
// set matrix = bindPose^T, to match what decomp_affine wants
|
2006-12-12 03:11:24 +01:00
|
|
|
|
|
|
|
AffineParts parts;
|
|
|
|
decomp_affine(matrix, &parts);
|
|
|
|
|
|
|
|
BoneTransform b = {
|
|
|
|
{ parts.t.x, parts.t.y, parts.t.z },
|
2006-12-15 17:09:30 +01:00
|
|
|
{ parts.q.x, parts.q.y, parts.q.z, parts.q.w }
|
2006-12-12 03:11:24 +01:00
|
|
|
};
|
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
boneTransforms[boneId] = b;
|
2006-12-06 01:06:05 +01:00
|
|
|
}
|
|
|
|
|
2007-03-19 17:12:53 +01:00
|
|
|
// Construct the list of prop points.
|
|
|
|
// Currently takes all objects that are directly attached to a
|
2010-11-15 03:18:49 +01:00
|
|
|
// standard bone, and whose name begins with "prop-" or "prop_".
|
2007-03-02 16:55:46 +01:00
|
|
|
|
|
|
|
std::vector<PropPoint> propPoints;
|
2008-09-10 00:30:14 +02:00
|
|
|
AddDefaultPropPoints(propPoints);
|
2007-03-02 16:55:46 +01:00
|
|
|
|
|
|
|
for (size_t i = 0; i < jointCount; ++i)
|
|
|
|
{
|
2007-03-16 19:00:58 +01:00
|
|
|
FCDSceneNode* joint = controllerInstance.GetJoint(i);
|
2007-03-02 16:55:46 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
int boneId = skeleton.GetBoneID(joint->GetName().c_str());
|
2007-03-02 16:55:46 +01:00
|
|
|
if (boneId < 0)
|
|
|
|
{
|
2007-03-02 21:09:27 +01:00
|
|
|
// unrecognised joint name - ignore, same as before
|
2007-03-02 16:55:46 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-03-19 17:12:53 +01:00
|
|
|
// Check all the objects attached to this bone
|
2007-03-02 16:55:46 +01:00
|
|
|
for (size_t j = 0; j < joint->GetChildrenCount(); ++j)
|
|
|
|
{
|
|
|
|
FCDSceneNode* child = joint->GetChild(j);
|
2010-11-15 03:18:49 +01:00
|
|
|
if (child->GetName().find("prop-") != 0 && child->GetName().find("prop_") != 0)
|
2007-03-02 16:55:46 +01:00
|
|
|
{
|
2007-03-19 17:12:53 +01:00
|
|
|
// doesn't begin with "prop-", so skip it
|
2007-03-02 16:55:46 +01:00
|
|
|
continue;
|
|
|
|
}
|
2007-03-19 17:12:53 +01:00
|
|
|
// Strip off the "prop-" from the name
|
2008-09-17 17:55:04 +02:00
|
|
|
std::string propPointName (child->GetName().substr(5));
|
2007-03-02 16:55:46 +01:00
|
|
|
|
2009-11-06 11:59:10 +01:00
|
|
|
Log(LOG_INFO, "Adding prop point %s", propPointName.c_str());
|
2007-03-02 16:55:46 +01:00
|
|
|
|
|
|
|
// Get translation and orientation of local transform
|
|
|
|
|
|
|
|
FMMatrix44 localTransform = child->ToMatrix();
|
|
|
|
|
|
|
|
HMatrix matrix;
|
|
|
|
memcpy(matrix, localTransform.Transposed().m, sizeof(matrix));
|
|
|
|
|
|
|
|
AffineParts parts;
|
|
|
|
decomp_affine(matrix, &parts);
|
|
|
|
|
|
|
|
// Add prop point to list
|
|
|
|
|
|
|
|
PropPoint p = {
|
2007-03-19 17:12:53 +01:00
|
|
|
propPointName,
|
2007-03-02 16:55:46 +01:00
|
|
|
{ parts.t.x, parts.t.y, parts.t.z },
|
|
|
|
{ parts.q.x, parts.q.y, parts.q.z, parts.q.w },
|
|
|
|
(uint8)boneId
|
|
|
|
};
|
|
|
|
propPoints.push_back(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the raw vertex data
|
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
|
|
|
|
FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
|
|
|
|
FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD);
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
|
|
|
|
const uint32* indicesCombined = inputPosition->GetIndices();
|
|
|
|
size_t indicesCombinedCount = inputPosition->GetIndexCount();
|
|
|
|
// (ReindexGeometry guarantees position/normal/texcoord have the same indexes)
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2006-12-26 00:42:53 +01:00
|
|
|
FCDGeometrySource* sourcePosition = inputPosition->GetSource();
|
|
|
|
FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
|
|
|
|
FCDGeometrySource* sourceTexcoord = inputTexcoord->GetSource();
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
float* dataPosition = sourcePosition->GetData();
|
|
|
|
float* dataNormal = sourceNormal ->GetData();
|
|
|
|
float* dataTexcoord = sourceTexcoord->GetData();
|
|
|
|
size_t vertexCount = sourcePosition->GetDataCount() / 3;
|
|
|
|
assert(sourcePosition->GetDataCount() == vertexCount*3);
|
|
|
|
assert(sourceNormal ->GetDataCount() == vertexCount*3);
|
|
|
|
assert(sourceTexcoord->GetDataCount() == vertexCount*2);
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
TransformVertices(dataPosition, dataNormal, vertexCount, boneTransforms, propPoints,
|
2007-03-16 19:00:58 +01:00
|
|
|
converter.GetEntityTransform(), skin->GetBindShapeTransform(),
|
|
|
|
converter.IsYUp(), converter.IsXSI());
|
2006-12-26 00:42:53 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoord, vertexCount, boneWeights, boneTransforms, propPoints);
|
2006-12-06 01:06:05 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw ColladaException("Unrecognised object type");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2008-09-10 00:30:14 +02:00
|
|
|
/**
|
|
|
|
* Adds the default "root" prop-point.
|
|
|
|
*/
|
|
|
|
static void AddDefaultPropPoints(std::vector<PropPoint>& propPoints)
|
|
|
|
{
|
|
|
|
PropPoint root;
|
|
|
|
root.name = "root";
|
|
|
|
root.translation[0] = root.translation[1] = root.translation[2] = 0.0f;
|
|
|
|
root.orientation[0] = root.orientation[1] = root.orientation[2] = 0.0f;
|
|
|
|
root.orientation[3] = 1.0f;
|
|
|
|
root.bone = 0xFF;
|
|
|
|
propPoints.push_back(root);
|
|
|
|
}
|
|
|
|
|
2006-12-06 01:06:05 +01:00
|
|
|
/**
|
|
|
|
* Writes the model data in the PMD format.
|
|
|
|
*/
|
2006-12-20 04:22:24 +01:00
|
|
|
static void WritePMD(OutputCB& output,
|
2008-08-25 00:22:25 +02:00
|
|
|
const uint32* indices, size_t indexCount,
|
|
|
|
const float* position, const float* normal, const float* texcoord, size_t vertexCount,
|
2007-03-02 16:55:46 +01:00
|
|
|
const std::vector<VertexBlend>& boneWeights, const std::vector<BoneTransform>& boneTransforms,
|
|
|
|
const std::vector<PropPoint>& propPoints)
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
2006-12-06 18:39:49 +01:00
|
|
|
static const VertexBlend noBlend = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
size_t faceCount = indexCount/3;
|
2006-12-26 00:42:53 +01:00
|
|
|
size_t boneCount = boneTransforms.size();
|
|
|
|
if (boneCount)
|
|
|
|
assert(boneWeights.size() == vertexCount);
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2007-03-02 16:55:46 +01:00
|
|
|
size_t propPointsSize = 0; // can't calculate this statically, so loop over all the prop points
|
|
|
|
for (size_t i = 0; i < propPoints.size(); ++i)
|
|
|
|
{
|
|
|
|
propPointsSize += 4 + propPoints[i].name.length();
|
|
|
|
propPointsSize += 3*4 + 4*4 + 1;
|
|
|
|
}
|
|
|
|
|
2006-12-06 01:06:05 +01:00
|
|
|
output("PSMD", 4); // magic number
|
2007-03-02 16:55:46 +01:00
|
|
|
write(output, (uint32)3); // version number
|
|
|
|
write(output, (uint32)(
|
2006-12-06 01:06:05 +01:00
|
|
|
4 + 13*4*vertexCount + // vertices
|
2006-12-26 00:42:53 +01:00
|
|
|
4 + 6*faceCount + // faces
|
2006-12-06 01:06:05 +01:00
|
|
|
4 + 7*4*boneCount + // bones
|
2007-03-02 16:55:46 +01:00
|
|
|
4 + propPointsSize // props
|
2006-12-06 01:06:05 +01:00
|
|
|
)); // data size
|
|
|
|
|
|
|
|
// Vertex data
|
|
|
|
write<uint32>(output, (uint32)vertexCount);
|
|
|
|
for (size_t i = 0; i < vertexCount; ++i)
|
|
|
|
{
|
|
|
|
output((char*)&position[i*3], 12);
|
|
|
|
output((char*)&normal [i*3], 12);
|
|
|
|
output((char*)&texcoord[i*2], 8);
|
2006-12-26 00:42:53 +01:00
|
|
|
if (boneCount)
|
2006-12-06 01:06:05 +01:00
|
|
|
write(output, boneWeights[i]);
|
|
|
|
else
|
|
|
|
write(output, noBlend);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Face data
|
2007-03-02 16:55:46 +01:00
|
|
|
write(output, (uint32)faceCount);
|
2008-08-25 00:22:25 +02:00
|
|
|
for (size_t i = 0; i < indexCount; ++i)
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
2006-12-26 00:42:53 +01:00
|
|
|
write(output, (uint16)indices[i]);
|
2006-12-06 01:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bones data
|
2007-03-02 16:55:46 +01:00
|
|
|
write(output, (uint32)boneCount);
|
2006-12-06 01:06:05 +01:00
|
|
|
for (size_t i = 0; i < boneCount; ++i)
|
|
|
|
{
|
2006-12-12 03:11:24 +01:00
|
|
|
output((char*)&boneTransforms[i], 7*4);
|
2006-12-06 01:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Prop points data
|
2007-03-02 16:55:46 +01:00
|
|
|
write(output, (uint32)propPoints.size());
|
|
|
|
for (size_t i = 0; i < propPoints.size(); ++i)
|
|
|
|
{
|
|
|
|
uint32 nameLen = (uint32)propPoints[i].name.length();
|
|
|
|
write(output, nameLen);
|
2009-11-04 16:29:28 +01:00
|
|
|
output(propPoints[i].name.c_str(), nameLen);
|
2007-03-02 16:55:46 +01:00
|
|
|
write(output, propPoints[i].translation);
|
|
|
|
write(output, propPoints[i].orientation);
|
|
|
|
write(output, propPoints[i].bone);
|
|
|
|
}
|
2006-12-06 01:06:05 +01:00
|
|
|
}
|
|
|
|
|
2006-12-12 03:11:24 +01:00
|
|
|
static FCDGeometryPolygons* GetPolysFromGeometry(FCDGeometry* geom)
|
|
|
|
{
|
|
|
|
REQUIRE(geom->IsMesh(), "geometry is mesh");
|
|
|
|
FCDGeometryMesh* mesh = geom->GetMesh();
|
2007-03-03 03:20:57 +01:00
|
|
|
|
2010-08-19 23:57:43 +02:00
|
|
|
if (! mesh->IsTriangles())
|
|
|
|
FCDGeometryPolygonsTools::Triangulate(mesh);
|
2007-03-03 03:20:57 +01:00
|
|
|
|
2006-12-12 03:11:24 +01:00
|
|
|
REQUIRE(mesh->IsTriangles(), "mesh is made of triangles");
|
|
|
|
REQUIRE(mesh->GetPolygonsCount() == 1, "mesh has single set of polygons");
|
2007-03-03 03:20:57 +01:00
|
|
|
FCDGeometryPolygons* polys = mesh->GetPolygons(0);
|
2008-08-25 00:22:25 +02:00
|
|
|
REQUIRE(polys->FindInput(FUDaeGeometryInput::POSITION) != NULL, "mesh has vertex positions");
|
|
|
|
REQUIRE(polys->FindInput(FUDaeGeometryInput::NORMAL) != NULL, "mesh has vertex normals");
|
|
|
|
REQUIRE(polys->FindInput(FUDaeGeometryInput::TEXCOORD) != NULL, "mesh has vertex tex coords");
|
2007-03-03 03:20:57 +01:00
|
|
|
return polys;
|
2006-12-12 03:11:24 +01:00
|
|
|
}
|
|
|
|
|
2006-12-06 01:06:05 +01:00
|
|
|
/**
|
|
|
|
* Applies world-space transform to vertex data, and flips into other-handed
|
|
|
|
* coordinate space.
|
|
|
|
*/
|
2008-08-25 00:22:25 +02:00
|
|
|
static void TransformVertices(float* position, float* normal, size_t vertexCount,
|
2007-03-03 03:20:57 +01:00
|
|
|
const FMMatrix44& transform, bool yUp)
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
for (size_t i = 0; i < vertexCount; ++i)
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
FMVector3 pos (&position[i*3], 0);
|
|
|
|
FMVector3 norm (&normal[i*3], 0);
|
2006-12-06 01:06:05 +01:00
|
|
|
|
|
|
|
// Apply the scene-node transforms
|
|
|
|
pos = transform.TransformCoordinate(pos);
|
2008-09-08 01:11:29 +02:00
|
|
|
norm = FMVector3_Normalize(transform.TransformVector(norm));
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2007-03-03 03:20:57 +01:00
|
|
|
// Convert from Y_UP or Z_UP to the game's coordinate system
|
|
|
|
|
|
|
|
if (yUp)
|
|
|
|
{
|
|
|
|
pos.z = -pos.z;
|
|
|
|
norm.z = -norm.z;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::swap(pos.y, pos.z);
|
|
|
|
std::swap(norm.y, norm.z);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy back to array
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
position[i*3] = pos.x;
|
|
|
|
position[i*3+1] = pos.y;
|
|
|
|
position[i*3+2] = pos.z;
|
2006-12-06 01:06:05 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
normal[i*3] = norm.x;
|
|
|
|
normal[i*3+1] = norm.y;
|
|
|
|
normal[i*3+2] = norm.z;
|
2006-12-06 01:06:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
static void TransformVertices(float* position, float* normal, size_t vertexCount,
|
2007-03-02 16:55:46 +01:00
|
|
|
std::vector<BoneTransform>& bones, std::vector<PropPoint>& propPoints,
|
2007-03-16 19:00:58 +01:00
|
|
|
const FMMatrix44& transform, const FMMatrix44& bindTransform, bool yUp, bool isXSI)
|
2006-12-12 03:11:24 +01:00
|
|
|
{
|
2007-03-16 19:00:58 +01:00
|
|
|
FMMatrix44 scaledTransform; // for vertexes
|
|
|
|
FMMatrix44 scaleMatrix; // for bones
|
|
|
|
|
|
|
|
// HACK: see comment in PSAConvert::TransformVertices
|
|
|
|
if (isXSI)
|
|
|
|
{
|
|
|
|
scaleMatrix = DecomposeToScaleMatrix(transform);
|
|
|
|
scaledTransform = DecomposeToScaleMatrix(bindTransform) * transform;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-03-20 02:06:34 +01:00
|
|
|
scaleMatrix = FMMatrix44_Identity;
|
2007-03-16 19:00:58 +01:00
|
|
|
scaledTransform = bindTransform;
|
|
|
|
}
|
|
|
|
|
2007-03-02 16:55:46 +01:00
|
|
|
// Update the vertex positions and normals
|
2008-08-25 00:22:25 +02:00
|
|
|
for (size_t i = 0; i < vertexCount; ++i)
|
2006-12-12 03:11:24 +01:00
|
|
|
{
|
2007-03-02 16:55:46 +01:00
|
|
|
FMVector3 pos (&position[i*3], 0);
|
|
|
|
FMVector3 norm (&normal[i*3], 0);
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2006-12-15 17:09:30 +01:00
|
|
|
// Apply the scene-node transforms
|
2007-03-16 19:00:58 +01:00
|
|
|
pos = scaledTransform.TransformCoordinate(pos);
|
2008-09-08 01:11:29 +02:00
|
|
|
norm = FMVector3_Normalize(scaledTransform.TransformVector(norm));
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2007-03-09 14:40:28 +01:00
|
|
|
// Convert from Y_UP or Z_UP to the game's coordinate system
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2007-03-09 14:40:28 +01:00
|
|
|
if (yUp)
|
|
|
|
{
|
2007-03-16 19:00:58 +01:00
|
|
|
pos.z = -pos.z;
|
|
|
|
norm.z = -norm.z;
|
2007-03-09 14:40:28 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::swap(pos.y, pos.z);
|
|
|
|
std::swap(norm.y, norm.z);
|
|
|
|
}
|
2006-12-12 03:11:24 +01:00
|
|
|
|
|
|
|
// and copy back into the original array
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
position[i*3] = pos.x;
|
2007-03-02 16:55:46 +01:00
|
|
|
position[i*3+1] = pos.y;
|
|
|
|
position[i*3+2] = pos.z;
|
2006-12-12 03:11:24 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
normal[i*3] = norm.x;
|
2007-03-02 16:55:46 +01:00
|
|
|
normal[i*3+1] = norm.y;
|
|
|
|
normal[i*3+2] = norm.z;
|
2006-12-12 03:11:24 +01:00
|
|
|
}
|
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
TransformBones(bones, scaleMatrix, yUp);
|
2007-03-02 16:55:46 +01:00
|
|
|
|
|
|
|
// And do the same for prop points
|
|
|
|
for (size_t i = 0; i < propPoints.size(); ++i)
|
|
|
|
{
|
2007-03-09 14:40:28 +01:00
|
|
|
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];
|
|
|
|
}
|
2007-03-02 16:55:46 +01:00
|
|
|
}
|
|
|
|
|
2006-12-12 03:11:24 +01:00
|
|
|
}
|
2006-12-06 01:06:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 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:
|
|
|
|
|
2006-12-20 04:22:24 +01:00
|
|
|
void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
|
2006-12-06 01:06:05 +01:00
|
|
|
{
|
2006-12-26 23:43:09 +01:00
|
|
|
PMDConvert::ColladaToPMD(input, output, xmlErrors);
|
2006-12-06 01:06:05 +01:00
|
|
|
}
|