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"
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"
41 #include "GeomReindex.h"
47 const size_t maxInfluences
= 4;
50 uint8 bones
[maxInfluences
];
51 float weights
[maxInfluences
];
53 VertexBlend defaultInfluences
= { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
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();
70 return FMVector3(vec
.x
/l
, vec
.y
/l
, vec
.z
/l
);
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();
90 memcpy(matrix
, transform
.Transposed().m
, sizeof(matrix
));
93 decomp_affine(matrix
, &parts
);
95 // Add prop point in game coordinates
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
},
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
));
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
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
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
;
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
);
308 if (! hasComplainedAboutNonexistentJoints
)
310 Log(LOG_WARNING
, "Vertexes influenced by nonexistent joint");
311 hasComplainedAboutNonexistentJoints
= true;
316 // Store into the VertexBlend
317 int boneId
= skeleton
.GetBoneID(joint
->GetName().c_str());
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());
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());
346 // unrecognised joint - it's probably just a prop point
347 // or something, so ignore it
351 FMMatrix44 bindPose
= skin
->GetJoint(i
)->GetBindPoseInverse().Inverted();
354 memcpy(matrix
, bindPose
.Transposed().m
, sizeof(matrix
));
355 // set matrix = bindPose^T, to match what decomp_affine wants
358 decomp_affine(matrix
, &parts
);
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());
382 // unrecognised joint name - ignore, same as before
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
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();
405 memcpy(matrix
, localTransform
.Transposed().m
, sizeof(matrix
));
408 decomp_affine(matrix
, &parts
);
410 // Add prop point to list
414 { parts
.t
.x
, parts
.t
.y
, parts
.t
.z
},
415 { parts
.q
.x
, parts
.q
.y
, parts
.q
.z
, parts
.q
.w
},
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
);
459 throw ColladaException("Unrecognised object type");
465 * Adds the default "root" prop-point.
467 static void AddDefaultPropPoints(std::vector
<PropPoint
>& propPoints
)
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
;
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
,
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();
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
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);
529 write(output
, boneWeights
[i
]);
531 write(output
, noBlend
);
535 write(output
, (uint32
)faceCount
);
536 for (size_t i
= 0; i
< indexCount
; ++i
)
538 write(output
, (uint16
)indices
[i
]);
542 write(output
, (uint32
)boneCount
);
543 for (size_t i
= 0; i
< boneCount
; ++i
)
545 output((char*)&boneTransforms
[i
], 7*4);
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");
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)
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
641 scaleMatrix
= DecomposeToScaleMatrix(transform
);
642 scaledTransform
= DecomposeToScaleMatrix(bindTransform
) * transform
;
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)
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
)
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];
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
718 void ColladaToPMD(const char* input
, OutputCB
& output
, std::string
& xmlErrors
)
720 PMDConvert::ColladaToPMD(input
, output
, xmlErrors
);