Line data Source code
1 : /* Copyright (C) 2012 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
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.
8 : *
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.
13 : *
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/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "PMDConvert.h"
21 : #include "CommonConvert.h"
22 :
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"
37 :
38 : #include "StdSkeletons.h"
39 : #include "Decompose.h"
40 : #include "Maths.h"
41 : #include "GeomReindex.h"
42 :
43 : #include <cassert>
44 : #include <vector>
45 : #include <algorithm>
46 :
47 : const size_t maxInfluences = 4;
48 : struct VertexBlend
49 : {
50 : uint8 bones[maxInfluences];
51 : float weights[maxInfluences];
52 : };
53 : VertexBlend defaultInfluences = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
54 :
55 16 : struct PropPoint
56 : {
57 : std::string name;
58 : float translation[3];
59 : float orientation[4];
60 : uint8 bone;
61 : };
62 :
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 357 : static FMVector3 FMVector3_Normalize(const FMVector3& vec)
67 : {
68 357 : float l = vec.Length();
69 357 : if (l > 0.0f)
70 357 : return FMVector3(vec.x/l, vec.y/l, vec.z/l);
71 : else
72 0 : return FMVector3(1.0f, 0.0f, 0.0f);
73 : }
74 :
75 4 : static void AddStaticPropPoints(std::vector<PropPoint> &propPoints, const FMMatrix44& upAxisTransform, FCDSceneNode* node)
76 : {
77 4 : if (node->GetName().find("prop-") == 0 || node->GetName().find("prop_") == 0)
78 : {
79 : // Strip off the "prop-" from the name
80 0 : std::string propPointName (node->GetName().substr(5));
81 :
82 0 : Log(LOG_INFO, "Adding prop point %s", propPointName.c_str());
83 :
84 : // CalculateWorldTransform applies transformations recursively for all parents of this node
85 : // upAxisTransform transforms this node to right-handed Z_UP coordinates
86 :
87 0 : FMMatrix44 transform = upAxisTransform * node->CalculateWorldTransform();
88 :
89 : HMatrix matrix;
90 0 : memcpy(matrix, transform.Transposed().m, sizeof(matrix));
91 :
92 : AffineParts parts;
93 0 : decomp_affine(matrix, &parts);
94 :
95 : // Add prop point in game coordinates
96 :
97 0 : PropPoint p = {
98 : propPointName,
99 :
100 : // Flip translation across the x-axis by swapping y and z
101 0 : { parts.t.x, parts.t.z, parts.t.y },
102 :
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 0 : { parts.q.x, parts.q.z, parts.q.y, -parts.q.w },
109 :
110 : 0xff
111 0 : };
112 0 : propPoints.push_back(p);
113 : }
114 :
115 : // Search children for prop points
116 4 : for (size_t i = 0; i < node->GetChildrenCount(); ++i)
117 0 : AddStaticPropPoints(propPoints, upAxisTransform, node->GetChild(i));
118 4 : }
119 :
120 : class PMDConvert
121 : {
122 : public:
123 : /**
124 : * Converts a COLLADA XML document into the PMD mesh format.
125 : *
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
131 : */
132 5 : static void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
133 : {
134 9 : CommonConvert converter(input, xmlErrors);
135 :
136 4 : if (converter.GetInstance().GetEntity()->GetType() == FCDEntity::GEOMETRY)
137 : {
138 4 : Log(LOG_INFO, "Found static geometry");
139 :
140 4 : FCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)converter.GetInstance().GetEntity());
141 :
142 : // Convert the geometry into a suitable form for the game
143 4 : ReindexGeometry(polys);
144 :
145 8 : std::vector<VertexBlend> boneWeights; // unused
146 8 : std::vector<BoneTransform> boneTransforms; // unused
147 8 : std::vector<PropPoint> propPoints;
148 :
149 : // Get the raw vertex data
150 :
151 4 : FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
152 4 : FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
153 :
154 4 : const uint32* indicesCombined = inputPosition->GetIndices();
155 4 : size_t indicesCombinedCount = inputPosition->GetIndexCount();
156 : // (ReindexGeometry guarantees position/normal/texcoord have the same indexes)
157 :
158 4 : FCDGeometrySource* sourcePosition = inputPosition->GetSource();
159 4 : FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
160 :
161 8 : FCDGeometrySourceList texcoordSources;
162 4 : polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
163 :
164 4 : float* dataPosition = sourcePosition->GetData();
165 4 : float* dataNormal = sourceNormal ->GetData();
166 4 : size_t vertexCount = sourcePosition->GetDataCount() / 3;
167 4 : assert(sourcePosition->GetDataCount() == vertexCount*3);
168 4 : assert(sourceNormal ->GetDataCount() == vertexCount*3);
169 :
170 8 : std::vector<float*> dataTexcoords;
171 8 : for (size_t i = 0; i < texcoordSources.size(); ++i)
172 : {
173 4 : dataTexcoords.push_back(texcoordSources[i]->GetData());
174 : }
175 :
176 : // Transform mesh coordinate system to game coordinates
177 : // (doesn't modify prop points)
178 :
179 4 : TransformStaticModel(dataPosition, dataNormal, vertexCount, converter.GetEntityTransform(), converter.IsYUp());
180 :
181 : // Add static prop points
182 : // which are empty child nodes of the main parent
183 :
184 : // Default prop points are already given in game coordinates
185 4 : AddDefaultPropPoints(propPoints);
186 :
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 4 : FMMatrix44 upAxisTransform = FMMatrix44_Identity;
190 4 : if (converter.IsYUp())
191 : {
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 0 : upAxisTransform = FMMatrix44::XAxisRotationMatrix(1.57f);
195 : }
196 :
197 4 : AddStaticPropPoints(propPoints, upAxisTransform, converter.GetInstance().GetParent());
198 :
199 4 : WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);
200 : }
201 0 : else if (converter.GetInstance().GetType() == FCDEntityInstance::CONTROLLER)
202 : {
203 0 : Log(LOG_INFO, "Found skinned geometry");
204 :
205 0 : FCDControllerInstance& controllerInstance = static_cast<FCDControllerInstance&>(converter.GetInstance());
206 :
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)
210 :
211 0 : assert(converter.GetInstance().GetEntity()->GetType() == FCDEntity::CONTROLLER); // assume this is always true?
212 0 : FCDController* controller = static_cast<FCDController*>(converter.GetInstance().GetEntity());
213 :
214 0 : FCDSkinController* skin = controller->GetSkinController();
215 0 : REQUIRE(skin != NULL, "is skin controller");
216 :
217 0 : FixSkeletonRoots(controllerInstance);
218 :
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 0 : size_t jointCount = std::min(skin->GetJointCount(), controllerInstance.GetJointCount());
223 0 : if (skin->GetJointCount() != controllerInstance.GetJointCount())
224 : {
225 0 : Log(LOG_WARNING, "Mismatched bone counts (skin has %d, skeleton has %d)",
226 : skin->GetJointCount(), controllerInstance.GetJointCount());
227 0 : for (size_t i = 0; i < skin->GetJointCount(); ++i)
228 0 : Log(LOG_INFO, "Skin joint %d: %s", i, skin->GetJoint(i)->GetId().c_str());
229 0 : for (size_t i = 0; i < controllerInstance.GetJointCount(); ++i)
230 0 : Log(LOG_INFO, "Skeleton joint %d: %s", i, controllerInstance.GetJoint(i)->GetName().c_str());
231 : }
232 :
233 : // Get the skinned mesh for this entity
234 0 : FCDGeometry* baseGeometry = controller->GetBaseGeometry();
235 0 : REQUIRE(baseGeometry != NULL, "controller has base geometry");
236 0 : FCDGeometryPolygons* polys = GetPolysFromGeometry(baseGeometry);
237 :
238 : // Make sure it doesn't use more bones per vertex than the game can handle
239 0 : SkinReduceInfluences(skin, maxInfluences, 0.001f);
240 :
241 : // Convert the geometry into a suitable form for the game
242 0 : ReindexGeometry(polys, skin);
243 :
244 0 : const Skeleton& skeleton = FindSkeleton(controllerInstance);
245 :
246 : // Convert the bone influences into VertexBlend structures for the PMD:
247 :
248 0 : bool hasComplainedAboutNonexistentJoints = false; // because we want to emit a warning only once
249 :
250 0 : std::vector<VertexBlend> boneWeights; // one per vertex
251 :
252 0 : const FCDSkinControllerVertex* vertexInfluences = skin->GetVertexInfluences();
253 0 : for (size_t i = 0; i < skin->GetInfluenceCount(); ++i)
254 : {
255 0 : VertexBlend influences = defaultInfluences;
256 :
257 0 : assert(vertexInfluences[i].GetPairCount() <= maxInfluences);
258 : // guaranteed by ReduceInfluences; necessary for avoiding
259 : // out-of-bounds writes to the VertexBlend
260 :
261 0 : if (vertexInfluences[i].GetPairCount() == 0)
262 : {
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 0 : influences.bones[0] = (uint8)jointCount;
269 0 : influences.weights[0] = 1.0f;
270 : }
271 :
272 0 : for (size_t j = 0; j < vertexInfluences[i].GetPairCount(); ++j)
273 : {
274 0 : if (vertexInfluences[i].GetPair(j)->jointIndex == -1)
275 : {
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"
278 : //
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.
284 : //
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 0 : influences.bones[j] = (uint8)jointCount;
290 0 : influences.weights[j] = vertexInfluences[i].GetPair(j)->weight;
291 : }
292 : else
293 : {
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 0 : uint32 jointIdx = vertexInfluences[i].GetPair(j)->jointIndex;
298 0 : REQUIRE(jointIdx < 0xFE, "sensible number of joints (<254)");
299 :
300 : // Find the joint on the skeleton, after checking it really exists
301 0 : FCDSceneNode* joint = NULL;
302 0 : if (jointIdx < controllerInstance.GetJointCount())
303 0 : joint = controllerInstance.GetJoint(jointIdx);
304 :
305 : // Complain on error
306 0 : if (! joint)
307 : {
308 0 : if (! hasComplainedAboutNonexistentJoints)
309 : {
310 0 : Log(LOG_WARNING, "Vertexes influenced by nonexistent joint");
311 0 : hasComplainedAboutNonexistentJoints = true;
312 : }
313 0 : continue;
314 : }
315 :
316 : // Store into the VertexBlend
317 0 : int boneId = skeleton.GetBoneID(joint->GetName().c_str());
318 0 : if (boneId < 0)
319 : {
320 : // The relevant joint does exist, but it's not a recognised
321 : // bone in our chosen skeleton structure
322 0 : Log(LOG_ERROR, "Vertex influenced by unrecognised bone '%s'", joint->GetName().c_str());
323 0 : continue;
324 : }
325 :
326 0 : influences.bones[j] = (uint8)boneId;
327 0 : influences.weights[j] = vertexInfluences[i].GetPair(j)->weight;
328 : }
329 : }
330 :
331 0 : boneWeights.push_back(influences);
332 : }
333 :
334 : // Convert the bind pose into BoneTransform structures for the PMD:
335 :
336 0 : BoneTransform boneDefault = { { 0, 0, 0 }, { 0, 0, 0, 1 } }; // identity transform
337 0 : std::vector<BoneTransform> boneTransforms (skeleton.GetBoneCount(), boneDefault);
338 :
339 0 : for (size_t i = 0; i < jointCount; ++i)
340 : {
341 0 : FCDSceneNode* joint = controllerInstance.GetJoint(i);
342 :
343 0 : int boneId = skeleton.GetRealBoneID(joint->GetName().c_str());
344 0 : if (boneId < 0)
345 : {
346 : // unrecognised joint - it's probably just a prop point
347 : // or something, so ignore it
348 0 : continue;
349 : }
350 :
351 0 : FMMatrix44 bindPose = skin->GetJoint(i)->GetBindPoseInverse().Inverted();
352 :
353 : HMatrix matrix;
354 0 : memcpy(matrix, bindPose.Transposed().m, sizeof(matrix));
355 : // set matrix = bindPose^T, to match what decomp_affine wants
356 :
357 : AffineParts parts;
358 0 : decomp_affine(matrix, &parts);
359 :
360 : BoneTransform b = {
361 0 : { parts.t.x, parts.t.y, parts.t.z },
362 0 : { parts.q.x, parts.q.y, parts.q.z, parts.q.w }
363 0 : };
364 :
365 0 : boneTransforms[boneId] = b;
366 : }
367 :
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_".
371 :
372 0 : std::vector<PropPoint> propPoints;
373 0 : AddDefaultPropPoints(propPoints);
374 :
375 0 : for (size_t i = 0; i < jointCount; ++i)
376 : {
377 0 : FCDSceneNode* joint = controllerInstance.GetJoint(i);
378 :
379 0 : int boneId = skeleton.GetBoneID(joint->GetName().c_str());
380 0 : if (boneId < 0)
381 : {
382 : // unrecognised joint name - ignore, same as before
383 0 : continue;
384 : }
385 :
386 : // Check all the objects attached to this bone
387 0 : for (size_t j = 0; j < joint->GetChildrenCount(); ++j)
388 : {
389 0 : FCDSceneNode* child = joint->GetChild(j);
390 0 : if (child->GetName().find("prop-") != 0 && child->GetName().find("prop_") != 0)
391 : {
392 : // doesn't begin with "prop-", so skip it
393 0 : continue;
394 : }
395 : // Strip off the "prop-" from the name
396 0 : std::string propPointName (child->GetName().substr(5));
397 :
398 0 : Log(LOG_INFO, "Adding prop point %s", propPointName.c_str());
399 :
400 : // Get translation and orientation of local transform
401 :
402 0 : FMMatrix44 localTransform = child->ToMatrix();
403 :
404 : HMatrix matrix;
405 0 : memcpy(matrix, localTransform.Transposed().m, sizeof(matrix));
406 :
407 : AffineParts parts;
408 0 : decomp_affine(matrix, &parts);
409 :
410 : // Add prop point to list
411 :
412 : PropPoint p = {
413 : propPointName,
414 0 : { parts.t.x, parts.t.y, parts.t.z },
415 0 : { parts.q.x, parts.q.y, parts.q.z, parts.q.w },
416 : (uint8)boneId
417 0 : };
418 0 : propPoints.push_back(p);
419 : }
420 : }
421 :
422 : // Get the raw vertex data
423 :
424 0 : FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
425 0 : FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
426 :
427 0 : const uint32* indicesCombined = inputPosition->GetIndices();
428 0 : size_t indicesCombinedCount = inputPosition->GetIndexCount();
429 : // (ReindexGeometry guarantees position/normal/texcoord have the same indexes)
430 :
431 0 : FCDGeometrySource* sourcePosition = inputPosition->GetSource();
432 0 : FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
433 :
434 0 : FCDGeometrySourceList texcoordSources;
435 0 : polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
436 :
437 0 : float* dataPosition = sourcePosition->GetData();
438 0 : float* dataNormal = sourceNormal ->GetData();
439 0 : size_t vertexCount = sourcePosition->GetDataCount() / 3;
440 0 : assert(sourcePosition->GetDataCount() == vertexCount*3);
441 0 : assert(sourceNormal ->GetDataCount() == vertexCount*3);
442 :
443 0 : std::vector<float*> dataTexcoords;
444 0 : for (size_t i = 0; i < texcoordSources.size(); ++i)
445 : {
446 0 : dataTexcoords.push_back(texcoordSources[i]->GetData());
447 : }
448 :
449 : // Transform model coordinate system to game coordinates
450 :
451 0 : TransformSkinnedModel(dataPosition, dataNormal, vertexCount, boneTransforms, propPoints,
452 : converter.GetEntityTransform(), skin->GetBindShapeTransform(),
453 0 : converter.IsYUp(), converter.IsXSI());
454 :
455 0 : WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);
456 : }
457 : else
458 : {
459 0 : throw ColladaException("Unrecognised object type");
460 : }
461 :
462 4 : }
463 :
464 : /**
465 : * Adds the default "root" prop-point.
466 : */
467 4 : static void AddDefaultPropPoints(std::vector<PropPoint>& propPoints)
468 : {
469 8 : PropPoint root;
470 4 : root.name = "root";
471 4 : root.translation[0] = root.translation[1] = root.translation[2] = 0.0f;
472 4 : root.orientation[0] = root.orientation[1] = root.orientation[2] = 0.0f;
473 4 : root.orientation[3] = 1.0f;
474 4 : root.bone = 0xFF;
475 4 : propPoints.push_back(root);
476 4 : }
477 :
478 : /**
479 : * Writes the model data in the PMD format.
480 : */
481 4 : 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)
488 : {
489 : static const VertexBlend noBlend = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
490 :
491 4 : size_t faceCount = indexCount/3;
492 4 : size_t boneCount = boneTransforms.size();
493 4 : if (boneCount)
494 0 : assert(boneWeights.size() == vertexCount);
495 :
496 4 : size_t propPointsSize = 0; // can't calculate this statically, so loop over all the prop points
497 8 : for (size_t i = 0; i < propPoints.size(); ++i)
498 : {
499 4 : propPointsSize += 4 + propPoints[i].name.length();
500 4 : propPointsSize += 3*4 + 4*4 + 1;
501 : }
502 :
503 4 : output("PSMD", 4); // magic number
504 4 : write(output, (uint32)4); // version number
505 4 : 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 : 4 + 11*4*vertexCount + 4 + 8*texcoords.size()*vertexCount + // vertices
510 : 4 + 6*faceCount + // faces
511 4 : 4 + 7*4*boneCount + // bones
512 4 : 4 + propPointsSize // props
513 : )); // data size
514 :
515 : // Vertex data
516 4 : write<uint32>(output, (uint32)vertexCount);
517 4 : write<uint32>(output, (uint32)texcoords.size()); // UV pairs per vertex
518 361 : for (size_t i = 0; i < vertexCount; ++i)
519 : {
520 357 : output((char*)&position[i*3], 12);
521 357 : output((char*)&normal [i*3], 12);
522 :
523 714 : for (size_t s = 0; s < texcoords.size(); ++s)
524 : {
525 357 : output((char*)&texcoords[s][i*2], 8);
526 : }
527 :
528 357 : if (boneCount)
529 0 : write(output, boneWeights[i]);
530 : else
531 357 : write(output, noBlend);
532 : }
533 :
534 : // Face data
535 4 : write(output, (uint32)faceCount);
536 1006 : for (size_t i = 0; i < indexCount; ++i)
537 : {
538 1002 : write(output, (uint16)indices[i]);
539 : }
540 :
541 : // Bones data
542 4 : write(output, (uint32)boneCount);
543 4 : for (size_t i = 0; i < boneCount; ++i)
544 : {
545 0 : output((char*)&boneTransforms[i], 7*4);
546 : }
547 :
548 : // Prop points data
549 4 : write(output, (uint32)propPoints.size());
550 8 : for (size_t i = 0; i < propPoints.size(); ++i)
551 : {
552 4 : uint32 nameLen = (uint32)propPoints[i].name.length();
553 4 : write(output, nameLen);
554 4 : output(propPoints[i].name.c_str(), nameLen);
555 4 : write(output, propPoints[i].translation);
556 4 : write(output, propPoints[i].orientation);
557 4 : write(output, propPoints[i].bone);
558 : }
559 4 : }
560 :
561 4 : static FCDGeometryPolygons* GetPolysFromGeometry(FCDGeometry* geom)
562 : {
563 4 : REQUIRE(geom->IsMesh(), "geometry is mesh");
564 4 : FCDGeometryMesh* mesh = geom->GetMesh();
565 :
566 4 : if (! mesh->IsTriangles())
567 0 : FCDGeometryPolygonsTools::Triangulate(mesh);
568 :
569 4 : REQUIRE(mesh->IsTriangles(), "mesh is made of triangles");
570 4 : REQUIRE(mesh->GetPolygonsCount() == 1, "mesh has single set of polygons");
571 4 : FCDGeometryPolygons* polys = mesh->GetPolygons(0);
572 4 : REQUIRE(polys->FindInput(FUDaeGeometryInput::POSITION) != NULL, "mesh has vertex positions");
573 4 : REQUIRE(polys->FindInput(FUDaeGeometryInput::NORMAL) != NULL, "mesh has vertex normals");
574 4 : REQUIRE(polys->FindInput(FUDaeGeometryInput::TEXCOORD) != NULL, "mesh has vertex tex coords");
575 4 : return polys;
576 : }
577 :
578 : /**
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
581 : *
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.
585 : */
586 4 : static void TransformStaticModel(float* position, float* normal, size_t vertexCount,
587 : const FMMatrix44& transform, bool yUp)
588 : {
589 361 : for (size_t i = 0; i < vertexCount; ++i)
590 : {
591 357 : FMVector3 pos (&position[i*3], 0);
592 357 : FMVector3 norm (&normal[i*3], 0);
593 :
594 : // Apply the scene-node transforms
595 357 : pos = transform.TransformCoordinate(pos);
596 357 : norm = FMVector3_Normalize(transform.TransformVector(norm));
597 :
598 : // Convert from right-handed Y_UP or Z_UP to the game's coordinate system (left-handed Y-up)
599 :
600 357 : if (yUp)
601 : {
602 0 : pos.z = -pos.z;
603 0 : norm.z = -norm.z;
604 : }
605 : else
606 : {
607 357 : std::swap(pos.y, pos.z);
608 357 : std::swap(norm.y, norm.z);
609 : }
610 :
611 : // Copy back to array
612 :
613 357 : position[i*3] = pos.x;
614 357 : position[i*3+1] = pos.y;
615 357 : position[i*3+2] = pos.z;
616 :
617 357 : normal[i*3] = norm.x;
618 357 : normal[i*3+1] = norm.y;
619 357 : normal[i*3+2] = norm.z;
620 : }
621 4 : }
622 :
623 : /**
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
626 : *
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.
630 : */
631 0 : 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)
634 : {
635 0 : FMMatrix44 scaledTransform; // for vertexes
636 0 : FMMatrix44 scaleMatrix; // for bones
637 :
638 : // HACK: see comment in PSAConvert::TransformVertices
639 0 : if (isXSI)
640 : {
641 0 : scaleMatrix = DecomposeToScaleMatrix(transform);
642 0 : scaledTransform = DecomposeToScaleMatrix(bindTransform) * transform;
643 : }
644 : else
645 : {
646 0 : scaleMatrix = FMMatrix44_Identity;
647 0 : scaledTransform = bindTransform;
648 : }
649 :
650 : // Update the vertex positions and normals
651 0 : for (size_t i = 0; i < vertexCount; ++i)
652 : {
653 0 : FMVector3 pos (&position[i*3], 0);
654 0 : FMVector3 norm (&normal[i*3], 0);
655 :
656 : // Apply the scene-node transforms
657 0 : pos = scaledTransform.TransformCoordinate(pos);
658 0 : norm = FMVector3_Normalize(scaledTransform.TransformVector(norm));
659 :
660 : // Convert from right-handed Y_UP or Z_UP to the game's coordinate system (left-handed Y-up)
661 :
662 0 : if (yUp)
663 : {
664 0 : pos.z = -pos.z;
665 0 : norm.z = -norm.z;
666 : }
667 : else
668 : {
669 0 : std::swap(pos.y, pos.z);
670 0 : std::swap(norm.y, norm.z);
671 : }
672 :
673 : // and copy back into the original array
674 :
675 0 : position[i*3] = pos.x;
676 0 : position[i*3+1] = pos.y;
677 0 : position[i*3+2] = pos.z;
678 :
679 0 : normal[i*3] = norm.x;
680 0 : normal[i*3+1] = norm.y;
681 0 : normal[i*3+2] = norm.z;
682 : }
683 :
684 0 : TransformBones(bones, scaleMatrix, yUp);
685 :
686 : // And do the same for prop points
687 0 : for (size_t i = 0; i < propPoints.size(); ++i)
688 : {
689 0 : if (yUp)
690 : {
691 0 : propPoints[i].translation[0] = -propPoints[i].translation[0];
692 0 : propPoints[i].orientation[0] = -propPoints[i].orientation[0];
693 0 : propPoints[i].orientation[3] = -propPoints[i].orientation[3];
694 : }
695 : else
696 : {
697 : // Flip translation across the x-axis by swapping y and z
698 0 : std::swap(propPoints[i].translation[1], propPoints[i].translation[2]);
699 :
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 0 : std::swap(propPoints[i].orientation[1], propPoints[i].orientation[2]);
706 0 : propPoints[i].orientation[3] = -propPoints[i].orientation[3];
707 : }
708 : }
709 :
710 0 : }
711 : };
712 :
713 :
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:
717 :
718 5 : void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
719 : {
720 5 : PMDConvert::ColladaToPMD(input, output, xmlErrors);
721 4 : }
|