[Gameplay] Reduce loom cost
[0ad.git] / source / collada / PMDConvert.cpp
blobbb55df0488d4a070ae947295841371f63dba2889
1 /* Copyright (C) 2012 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "PMDConvert.h"
21 #include "CommonConvert.h"
23 #include "FCollada.h"
24 #include "FCDocument/FCDAsset.h"
25 #include "FCDocument/FCDocument.h"
26 #include "FCDocument/FCDocumentTools.h"
27 #include "FCDocument/FCDController.h"
28 #include "FCDocument/FCDControllerInstance.h"
29 #include "FCDocument/FCDGeometry.h"
30 #include "FCDocument/FCDGeometryMesh.h"
31 #include "FCDocument/FCDGeometryPolygons.h"
32 #include "FCDocument/FCDGeometryPolygonsInput.h"
33 #include "FCDocument/FCDGeometryPolygonsTools.h"
34 #include "FCDocument/FCDGeometrySource.h"
35 #include "FCDocument/FCDSceneNode.h"
36 #include "FCDocument/FCDSkinController.h"
38 #include "StdSkeletons.h"
39 #include "Decompose.h"
40 #include "Maths.h"
41 #include "GeomReindex.h"
43 #include <cassert>
44 #include <vector>
45 #include <algorithm>
47 const size_t maxInfluences = 4;
48 struct VertexBlend
50 uint8 bones[maxInfluences];
51 float weights[maxInfluences];
53 VertexBlend defaultInfluences = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
55 struct PropPoint
57 std::string name;
58 float translation[3];
59 float orientation[4];
60 uint8 bone;
63 // Based on FMVector3::Normalize, but that function uses a static member
64 // FMVector3::XAxis which causes irritating linker errors. Rather than trying
65 // to make that XAxis work in a cross-platform way, just reimplement Normalize:
66 static FMVector3 FMVector3_Normalize(const FMVector3& vec)
68 float l = vec.Length();
69 if (l > 0.0f)
70 return FMVector3(vec.x/l, vec.y/l, vec.z/l);
71 else
72 return FMVector3(1.0f, 0.0f, 0.0f);
75 static void AddStaticPropPoints(std::vector<PropPoint> &propPoints, const FMMatrix44& upAxisTransform, FCDSceneNode* node)
77 if (node->GetName().find("prop-") == 0 || node->GetName().find("prop_") == 0)
79 // Strip off the "prop-" from the name
80 std::string propPointName (node->GetName().substr(5));
82 Log(LOG_INFO, "Adding prop point %s", propPointName.c_str());
84 // CalculateWorldTransform applies transformations recursively for all parents of this node
85 // upAxisTransform transforms this node to right-handed Z_UP coordinates
87 FMMatrix44 transform = upAxisTransform * node->CalculateWorldTransform();
89 HMatrix matrix;
90 memcpy(matrix, transform.Transposed().m, sizeof(matrix));
92 AffineParts parts;
93 decomp_affine(matrix, &parts);
95 // Add prop point in game coordinates
97 PropPoint p = {
98 propPointName,
100 // Flip translation across the x-axis by swapping y and z
101 { parts.t.x, parts.t.z, parts.t.y },
103 // To convert the quaternions: imagine you're using the axis/angle
104 // representation, then swap the y,z basis vectors and change the
105 // direction of rotation by negating the angle ( => negating sin(angle)
106 // => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)
107 // but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)
108 { parts.q.x, parts.q.z, parts.q.y, -parts.q.w },
110 0xff
112 propPoints.push_back(p);
115 // Search children for prop points
116 for (size_t i = 0; i < node->GetChildrenCount(); ++i)
117 AddStaticPropPoints(propPoints, upAxisTransform, node->GetChild(i));
120 class PMDConvert
122 public:
124 * Converts a COLLADA XML document into the PMD mesh format.
126 * @param input XML document to parse
127 * @param output callback for writing the PMD data; called lots of times
128 * with small strings
129 * @param xmlErrors output - errors reported by the XML parser
130 * @throws ColladaException on failure
132 static void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
134 CommonConvert converter(input, xmlErrors);
136 if (converter.GetInstance().GetEntity()->GetType() == FCDEntity::GEOMETRY)
138 Log(LOG_INFO, "Found static geometry");
140 FCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)converter.GetInstance().GetEntity());
142 // Convert the geometry into a suitable form for the game
143 ReindexGeometry(polys);
145 std::vector<VertexBlend> boneWeights; // unused
146 std::vector<BoneTransform> boneTransforms; // unused
147 std::vector<PropPoint> propPoints;
149 // Get the raw vertex data
151 FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
152 FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
154 const uint32* indicesCombined = inputPosition->GetIndices();
155 size_t indicesCombinedCount = inputPosition->GetIndexCount();
156 // (ReindexGeometry guarantees position/normal/texcoord have the same indexes)
158 FCDGeometrySource* sourcePosition = inputPosition->GetSource();
159 FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
161 FCDGeometrySourceList texcoordSources;
162 polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
164 float* dataPosition = sourcePosition->GetData();
165 float* dataNormal = sourceNormal ->GetData();
166 size_t vertexCount = sourcePosition->GetDataCount() / 3;
167 assert(sourcePosition->GetDataCount() == vertexCount*3);
168 assert(sourceNormal ->GetDataCount() == vertexCount*3);
170 std::vector<float*> dataTexcoords;
171 for (size_t i = 0; i < texcoordSources.size(); ++i)
173 dataTexcoords.push_back(texcoordSources[i]->GetData());
176 // Transform mesh coordinate system to game coordinates
177 // (doesn't modify prop points)
179 TransformStaticModel(dataPosition, dataNormal, vertexCount, converter.GetEntityTransform(), converter.IsYUp());
181 // Add static prop points
182 // which are empty child nodes of the main parent
184 // Default prop points are already given in game coordinates
185 AddDefaultPropPoints(propPoints);
187 // Calculate transform to convert from COLLADA-defined up_axis to Z-up because
188 // it's relatively straightforward to convert that to game coordinates
189 FMMatrix44 upAxisTransform = FMMatrix44_Identity;
190 if (converter.IsYUp())
192 // Prop points are rotated -90 degrees about the X-axis, reverse that rotation
193 // (do this once now because it's easier than messing with quaternions later)
194 upAxisTransform = FMMatrix44::XAxisRotationMatrix(1.57f);
197 AddStaticPropPoints(propPoints, upAxisTransform, converter.GetInstance().GetParent());
199 WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);
201 else if (converter.GetInstance().GetType() == FCDEntityInstance::CONTROLLER)
203 Log(LOG_INFO, "Found skinned geometry");
205 FCDControllerInstance& controllerInstance = static_cast<FCDControllerInstance&>(converter.GetInstance());
207 // (NB: GetType is deprecated and should be replaced with HasType,
208 // except that has irritating linker errors when using a DLL, so don't
209 // bother)
211 assert(converter.GetInstance().GetEntity()->GetType() == FCDEntity::CONTROLLER); // assume this is always true?
212 FCDController* controller = static_cast<FCDController*>(converter.GetInstance().GetEntity());
214 FCDSkinController* skin = controller->GetSkinController();
215 REQUIRE(skin != NULL, "is skin controller");
217 FixSkeletonRoots(controllerInstance);
219 // Data for joints is stored in two places - avoid overflows by limiting
220 // to the minimum of the two sizes, and warn if they're different (which
221 // happens in practice for slightly-broken meshes)
222 size_t jointCount = std::min(skin->GetJointCount(), controllerInstance.GetJointCount());
223 if (skin->GetJointCount() != controllerInstance.GetJointCount())
225 Log(LOG_WARNING, "Mismatched bone counts (skin has %d, skeleton has %d)",
226 skin->GetJointCount(), controllerInstance.GetJointCount());
227 for (size_t i = 0; i < skin->GetJointCount(); ++i)
228 Log(LOG_INFO, "Skin joint %d: %s", i, skin->GetJoint(i)->GetId().c_str());
229 for (size_t i = 0; i < controllerInstance.GetJointCount(); ++i)
230 Log(LOG_INFO, "Skeleton joint %d: %s", i, controllerInstance.GetJoint(i)->GetName().c_str());
233 // Get the skinned mesh for this entity
234 FCDGeometry* baseGeometry = controller->GetBaseGeometry();
235 REQUIRE(baseGeometry != NULL, "controller has base geometry");
236 FCDGeometryPolygons* polys = GetPolysFromGeometry(baseGeometry);
238 // Make sure it doesn't use more bones per vertex than the game can handle
239 SkinReduceInfluences(skin, maxInfluences, 0.001f);
241 // Convert the geometry into a suitable form for the game
242 ReindexGeometry(polys, skin);
244 const Skeleton& skeleton = FindSkeleton(controllerInstance);
246 // Convert the bone influences into VertexBlend structures for the PMD:
248 bool hasComplainedAboutNonexistentJoints = false; // because we want to emit a warning only once
250 std::vector<VertexBlend> boneWeights; // one per vertex
252 const FCDSkinControllerVertex* vertexInfluences = skin->GetVertexInfluences();
253 for (size_t i = 0; i < skin->GetInfluenceCount(); ++i)
255 VertexBlend influences = defaultInfluences;
257 assert(vertexInfluences[i].GetPairCount() <= maxInfluences);
258 // guaranteed by ReduceInfluences; necessary for avoiding
259 // out-of-bounds writes to the VertexBlend
261 if (vertexInfluences[i].GetPairCount() == 0)
263 // Blender exports some models with vertices that have no influences,
264 // which I've not found details about in the COLLADA spec, however,
265 // it seems to work OK to treat these vertices the same as if they
266 // were only influenced by the bind-shape matrix (see comment below),
267 // so we use the same special case here.
268 influences.bones[0] = (uint8)jointCount;
269 influences.weights[0] = 1.0f;
272 for (size_t j = 0; j < vertexInfluences[i].GetPairCount(); ++j)
274 if (vertexInfluences[i].GetPair(j)->jointIndex == -1)
276 // This is a special case we must handle, according to the COLLADA spec:
277 // "An index of -1 into the array of joints refers to the bind shape"
279 // which basically means when skinning the vertex it's relative to the
280 // bind-shape transform instead of an animated bone. Since our skinning
281 // is in world space, we will have already applied the bind-shape transform,
282 // so we don't have to worry about that, though we DO have to apply the
283 // world space transform of the model for the indicated vertex.
285 // To indicate this special case, we use a bone ID set to the total number
286 // of bones in the model, which will have a special "bone matrix" reserved
287 // that contains the world space transform of the model during skinning.
288 // (see http://trac.wildfiregames.com/ticket/1012)
289 influences.bones[j] = (uint8)jointCount;
290 influences.weights[j] = vertexInfluences[i].GetPair(j)->weight;
292 else
294 // Check for less than 254 joints because we store them in a u8,
295 // 0xFF is a reserved value (no influence), and we reserve one slot
296 // for the above special case.
297 uint32 jointIdx = vertexInfluences[i].GetPair(j)->jointIndex;
298 REQUIRE(jointIdx < 0xFE, "sensible number of joints (<254)");
300 // Find the joint on the skeleton, after checking it really exists
301 FCDSceneNode* joint = NULL;
302 if (jointIdx < controllerInstance.GetJointCount())
303 joint = controllerInstance.GetJoint(jointIdx);
305 // Complain on error
306 if (! joint)
308 if (! hasComplainedAboutNonexistentJoints)
310 Log(LOG_WARNING, "Vertexes influenced by nonexistent joint");
311 hasComplainedAboutNonexistentJoints = true;
313 continue;
316 // Store into the VertexBlend
317 int boneId = skeleton.GetBoneID(joint->GetName().c_str());
318 if (boneId < 0)
320 // The relevant joint does exist, but it's not a recognised
321 // bone in our chosen skeleton structure
322 Log(LOG_ERROR, "Vertex influenced by unrecognised bone '%s'", joint->GetName().c_str());
323 continue;
326 influences.bones[j] = (uint8)boneId;
327 influences.weights[j] = vertexInfluences[i].GetPair(j)->weight;
331 boneWeights.push_back(influences);
334 // Convert the bind pose into BoneTransform structures for the PMD:
336 BoneTransform boneDefault = { { 0, 0, 0 }, { 0, 0, 0, 1 } }; // identity transform
337 std::vector<BoneTransform> boneTransforms (skeleton.GetBoneCount(), boneDefault);
339 for (size_t i = 0; i < jointCount; ++i)
341 FCDSceneNode* joint = controllerInstance.GetJoint(i);
343 int boneId = skeleton.GetRealBoneID(joint->GetName().c_str());
344 if (boneId < 0)
346 // unrecognised joint - it's probably just a prop point
347 // or something, so ignore it
348 continue;
351 FMMatrix44 bindPose = skin->GetJoint(i)->GetBindPoseInverse().Inverted();
353 HMatrix matrix;
354 memcpy(matrix, bindPose.Transposed().m, sizeof(matrix));
355 // set matrix = bindPose^T, to match what decomp_affine wants
357 AffineParts parts;
358 decomp_affine(matrix, &parts);
360 BoneTransform b = {
361 { parts.t.x, parts.t.y, parts.t.z },
362 { parts.q.x, parts.q.y, parts.q.z, parts.q.w }
365 boneTransforms[boneId] = b;
368 // Construct the list of prop points.
369 // Currently takes all objects that are directly attached to a
370 // standard bone, and whose name begins with "prop-" or "prop_".
372 std::vector<PropPoint> propPoints;
373 AddDefaultPropPoints(propPoints);
375 for (size_t i = 0; i < jointCount; ++i)
377 FCDSceneNode* joint = controllerInstance.GetJoint(i);
379 int boneId = skeleton.GetBoneID(joint->GetName().c_str());
380 if (boneId < 0)
382 // unrecognised joint name - ignore, same as before
383 continue;
386 // Check all the objects attached to this bone
387 for (size_t j = 0; j < joint->GetChildrenCount(); ++j)
389 FCDSceneNode* child = joint->GetChild(j);
390 if (child->GetName().find("prop-") != 0 && child->GetName().find("prop_") != 0)
392 // doesn't begin with "prop-", so skip it
393 continue;
395 // Strip off the "prop-" from the name
396 std::string propPointName (child->GetName().substr(5));
398 Log(LOG_INFO, "Adding prop point %s", propPointName.c_str());
400 // Get translation and orientation of local transform
402 FMMatrix44 localTransform = child->ToMatrix();
404 HMatrix matrix;
405 memcpy(matrix, localTransform.Transposed().m, sizeof(matrix));
407 AffineParts parts;
408 decomp_affine(matrix, &parts);
410 // Add prop point to list
412 PropPoint p = {
413 propPointName,
414 { parts.t.x, parts.t.y, parts.t.z },
415 { parts.q.x, parts.q.y, parts.q.z, parts.q.w },
416 (uint8)boneId
418 propPoints.push_back(p);
422 // Get the raw vertex data
424 FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
425 FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
427 const uint32* indicesCombined = inputPosition->GetIndices();
428 size_t indicesCombinedCount = inputPosition->GetIndexCount();
429 // (ReindexGeometry guarantees position/normal/texcoord have the same indexes)
431 FCDGeometrySource* sourcePosition = inputPosition->GetSource();
432 FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
434 FCDGeometrySourceList texcoordSources;
435 polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
437 float* dataPosition = sourcePosition->GetData();
438 float* dataNormal = sourceNormal ->GetData();
439 size_t vertexCount = sourcePosition->GetDataCount() / 3;
440 assert(sourcePosition->GetDataCount() == vertexCount*3);
441 assert(sourceNormal ->GetDataCount() == vertexCount*3);
443 std::vector<float*> dataTexcoords;
444 for (size_t i = 0; i < texcoordSources.size(); ++i)
446 dataTexcoords.push_back(texcoordSources[i]->GetData());
449 // Transform model coordinate system to game coordinates
451 TransformSkinnedModel(dataPosition, dataNormal, vertexCount, boneTransforms, propPoints,
452 converter.GetEntityTransform(), skin->GetBindShapeTransform(),
453 converter.IsYUp(), converter.IsXSI());
455 WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);
457 else
459 throw ColladaException("Unrecognised object type");
465 * Adds the default "root" prop-point.
467 static void AddDefaultPropPoints(std::vector<PropPoint>& propPoints)
469 PropPoint root;
470 root.name = "root";
471 root.translation[0] = root.translation[1] = root.translation[2] = 0.0f;
472 root.orientation[0] = root.orientation[1] = root.orientation[2] = 0.0f;
473 root.orientation[3] = 1.0f;
474 root.bone = 0xFF;
475 propPoints.push_back(root);
479 * Writes the model data in the PMD format.
481 static void WritePMD(OutputCB& output,
482 const uint32* indices, size_t indexCount,
483 const float* position, const float* normal,
484 const std::vector<float*>& texcoords,
485 size_t vertexCount,
486 const std::vector<VertexBlend>& boneWeights, const std::vector<BoneTransform>& boneTransforms,
487 const std::vector<PropPoint>& propPoints)
489 static const VertexBlend noBlend = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
491 size_t faceCount = indexCount/3;
492 size_t boneCount = boneTransforms.size();
493 if (boneCount)
494 assert(boneWeights.size() == vertexCount);
496 size_t propPointsSize = 0; // can't calculate this statically, so loop over all the prop points
497 for (size_t i = 0; i < propPoints.size(); ++i)
499 propPointsSize += 4 + propPoints[i].name.length();
500 propPointsSize += 3*4 + 4*4 + 1;
503 output("PSMD", 4); // magic number
504 write(output, (uint32)4); // version number
505 write(output, (uint32)(
506 // for UVs, we add one uint32 (i.e. 4 bytes) per model that gives the number of
507 // texcoord sets in the model, plus 2 floats per new UV
508 // pair per vertex (i.e. 8 bytes * number of pairs * vertex count)
509 4 + 11*4*vertexCount + 4 + 8*texcoords.size()*vertexCount + // vertices
510 4 + 6*faceCount + // faces
511 4 + 7*4*boneCount + // bones
512 4 + propPointsSize // props
513 )); // data size
515 // Vertex data
516 write<uint32>(output, (uint32)vertexCount);
517 write<uint32>(output, (uint32)texcoords.size()); // UV pairs per vertex
518 for (size_t i = 0; i < vertexCount; ++i)
520 output((char*)&position[i*3], 12);
521 output((char*)&normal [i*3], 12);
523 for (size_t s = 0; s < texcoords.size(); ++s)
525 output((char*)&texcoords[s][i*2], 8);
528 if (boneCount)
529 write(output, boneWeights[i]);
530 else
531 write(output, noBlend);
534 // Face data
535 write(output, (uint32)faceCount);
536 for (size_t i = 0; i < indexCount; ++i)
538 write(output, (uint16)indices[i]);
541 // Bones data
542 write(output, (uint32)boneCount);
543 for (size_t i = 0; i < boneCount; ++i)
545 output((char*)&boneTransforms[i], 7*4);
548 // Prop points data
549 write(output, (uint32)propPoints.size());
550 for (size_t i = 0; i < propPoints.size(); ++i)
552 uint32 nameLen = (uint32)propPoints[i].name.length();
553 write(output, nameLen);
554 output(propPoints[i].name.c_str(), nameLen);
555 write(output, propPoints[i].translation);
556 write(output, propPoints[i].orientation);
557 write(output, propPoints[i].bone);
561 static FCDGeometryPolygons* GetPolysFromGeometry(FCDGeometry* geom)
563 REQUIRE(geom->IsMesh(), "geometry is mesh");
564 FCDGeometryMesh* mesh = geom->GetMesh();
566 if (! mesh->IsTriangles())
567 FCDGeometryPolygonsTools::Triangulate(mesh);
569 REQUIRE(mesh->IsTriangles(), "mesh is made of triangles");
570 REQUIRE(mesh->GetPolygonsCount() == 1, "mesh has single set of polygons");
571 FCDGeometryPolygons* polys = mesh->GetPolygons(0);
572 REQUIRE(polys->FindInput(FUDaeGeometryInput::POSITION) != NULL, "mesh has vertex positions");
573 REQUIRE(polys->FindInput(FUDaeGeometryInput::NORMAL) != NULL, "mesh has vertex normals");
574 REQUIRE(polys->FindInput(FUDaeGeometryInput::TEXCOORD) != NULL, "mesh has vertex tex coords");
575 return polys;
579 * Applies world-space transform to vertex data and transforms Collada's right-handed
580 * Y-up / Z-up coordinates to the game's left-handed Y-up coordinate system
582 * TODO: Maybe we should use FCDocumentTools::StandardizeUpAxisAndLength in addition
583 * to this, so we'd only have one up-axis case to worry about, but it doesn't seem to
584 * correctly adjust the prop points in Y_UP models.
586 static void TransformStaticModel(float* position, float* normal, size_t vertexCount,
587 const FMMatrix44& transform, bool yUp)
589 for (size_t i = 0; i < vertexCount; ++i)
591 FMVector3 pos (&position[i*3], 0);
592 FMVector3 norm (&normal[i*3], 0);
594 // Apply the scene-node transforms
595 pos = transform.TransformCoordinate(pos);
596 norm = FMVector3_Normalize(transform.TransformVector(norm));
598 // Convert from right-handed Y_UP or Z_UP to the game's coordinate system (left-handed Y-up)
600 if (yUp)
602 pos.z = -pos.z;
603 norm.z = -norm.z;
605 else
607 std::swap(pos.y, pos.z);
608 std::swap(norm.y, norm.z);
611 // Copy back to array
613 position[i*3] = pos.x;
614 position[i*3+1] = pos.y;
615 position[i*3+2] = pos.z;
617 normal[i*3] = norm.x;
618 normal[i*3+1] = norm.y;
619 normal[i*3+2] = norm.z;
624 * Applies world-space transform to vertex data and transforms Collada's right-handed
625 * Y-up / Z-up coordinates to the game's left-handed Y-up coordinate system
627 * TODO: Maybe we should use FCDocumentTools::StandardizeUpAxisAndLength in addition
628 * to this, so we'd only have one up-axis case to worry about, but it doesn't seem to
629 * correctly adjust the prop points in Y_UP models.
631 static void TransformSkinnedModel(float* position, float* normal, size_t vertexCount,
632 std::vector<BoneTransform>& bones, std::vector<PropPoint>& propPoints,
633 const FMMatrix44& transform, const FMMatrix44& bindTransform, bool yUp, bool isXSI)
635 FMMatrix44 scaledTransform; // for vertexes
636 FMMatrix44 scaleMatrix; // for bones
638 // HACK: see comment in PSAConvert::TransformVertices
639 if (isXSI)
641 scaleMatrix = DecomposeToScaleMatrix(transform);
642 scaledTransform = DecomposeToScaleMatrix(bindTransform) * transform;
644 else
646 scaleMatrix = FMMatrix44_Identity;
647 scaledTransform = bindTransform;
650 // Update the vertex positions and normals
651 for (size_t i = 0; i < vertexCount; ++i)
653 FMVector3 pos (&position[i*3], 0);
654 FMVector3 norm (&normal[i*3], 0);
656 // Apply the scene-node transforms
657 pos = scaledTransform.TransformCoordinate(pos);
658 norm = FMVector3_Normalize(scaledTransform.TransformVector(norm));
660 // Convert from right-handed Y_UP or Z_UP to the game's coordinate system (left-handed Y-up)
662 if (yUp)
664 pos.z = -pos.z;
665 norm.z = -norm.z;
667 else
669 std::swap(pos.y, pos.z);
670 std::swap(norm.y, norm.z);
673 // and copy back into the original array
675 position[i*3] = pos.x;
676 position[i*3+1] = pos.y;
677 position[i*3+2] = pos.z;
679 normal[i*3] = norm.x;
680 normal[i*3+1] = norm.y;
681 normal[i*3+2] = norm.z;
684 TransformBones(bones, scaleMatrix, yUp);
686 // And do the same for prop points
687 for (size_t i = 0; i < propPoints.size(); ++i)
689 if (yUp)
691 propPoints[i].translation[0] = -propPoints[i].translation[0];
692 propPoints[i].orientation[0] = -propPoints[i].orientation[0];
693 propPoints[i].orientation[3] = -propPoints[i].orientation[3];
695 else
697 // Flip translation across the x-axis by swapping y and z
698 std::swap(propPoints[i].translation[1], propPoints[i].translation[2]);
700 // To convert the quaternions: imagine you're using the axis/angle
701 // representation, then swap the y,z basis vectors and change the
702 // direction of rotation by negating the angle ( => negating sin(angle)
703 // => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)
704 // but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)
705 std::swap(propPoints[i].orientation[1], propPoints[i].orientation[2]);
706 propPoints[i].orientation[3] = -propPoints[i].orientation[3];
714 // The above stuff is just in a class since I don't like having to bother
715 // with forward declarations of functions - but provide the plain function
716 // interface here:
718 void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
720 PMDConvert::ColladaToPMD(input, output, xmlErrors);