Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
PMDConvert.cpp
Go to the documentation of this file.
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 
46 const size_t maxInfluences = 4;
48 {
51 };
52 VertexBlend defaultInfluences = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
53 
54 struct PropPoint
55 {
56  std::string name;
57  float translation[3];
58  float orientation[4];
59  uint8 bone;
60 };
61 
62 // Based on FMVector3::Normalize, but that function uses a static member
63 // FMVector3::XAxis which causes irritating linker errors. Rather than trying
64 // to make that XAxis work in a cross-platform way, just reimplement Normalize:
65 static FMVector3 FMVector3_Normalize(const FMVector3& vec)
66 {
67  float l = vec.Length();
68  if (l > 0.0f)
69  return FMVector3(vec.x/l, vec.y/l, vec.z/l);
70  else
71  return FMVector3(1.0f, 0.0f, 0.0f);
72 }
73 
74 static void AddStaticPropPoints(std::vector<PropPoint> &propPoints, const FMMatrix44& upAxisTransform, FCDSceneNode* node)
75 {
76  if (node->GetName().find("prop-") == 0 || node->GetName().find("prop_") == 0)
77  {
78  // Strip off the "prop-" from the name
79  std::string propPointName (node->GetName().substr(5));
80 
81  Log(LOG_INFO, "Adding prop point %s", propPointName.c_str());
82 
83  // CalculateWorldTransform applies transformations recursively for all parents of this node
84  // upAxisTransform transforms this node to right-handed Z_UP coordinates
85 
86  FMMatrix44 transform = upAxisTransform * node->CalculateWorldTransform();
87 
88  HMatrix matrix;
89  memcpy(matrix, transform.Transposed().m, sizeof(matrix));
90 
91  AffineParts parts;
92  decomp_affine(matrix, &parts);
93 
94  // Add prop point in game coordinates
95 
96  PropPoint p = {
97  propPointName,
98 
99  // Flip translation across the x-axis by swapping y and z
100  { parts.t.x, parts.t.z, parts.t.y },
101 
102  // To convert the quaternions: imagine you're using the axis/angle
103  // representation, then swap the y,z basis vectors and change the
104  // direction of rotation by negating the angle ( => negating sin(angle)
105  // => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)
106  // but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)
107  { parts.q.x, parts.q.z, parts.q.y, -parts.q.w },
108 
109  0xff
110  };
111  propPoints.push_back(p);
112  }
113 
114  // Search children for prop points
115  for (size_t i = 0; i < node->GetChildrenCount(); ++i)
116  AddStaticPropPoints(propPoints, upAxisTransform, node->GetChild(i));
117 }
118 
120 {
121 public:
122  /**
123  * Converts a COLLADA XML document into the PMD mesh format.
124  *
125  * @param input XML document to parse
126  * @param output callback for writing the PMD data; called lots of times
127  * with small strings
128  * @param xmlErrors output - errors reported by the XML parser
129  * @throws ColladaException on failure
130  */
131  static void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
132  {
133  CommonConvert converter(input, xmlErrors);
134 
135  if (converter.GetInstance().GetEntity()->GetType() == FCDEntity::GEOMETRY)
136  {
137  Log(LOG_INFO, "Found static geometry");
138 
139  FCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)converter.GetInstance().GetEntity());
140 
141  // Convert the geometry into a suitable form for the game
142  ReindexGeometry(polys);
143 
144  std::vector<VertexBlend> boneWeights; // unused
145  std::vector<BoneTransform> boneTransforms; // unused
146  std::vector<PropPoint> propPoints;
147 
148  // Get the raw vertex data
149 
150  FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
151  FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
152 
153  const uint32* indicesCombined = inputPosition->GetIndices();
154  size_t indicesCombinedCount = inputPosition->GetIndexCount();
155  // (ReindexGeometry guarantees position/normal/texcoord have the same indexes)
156 
157  FCDGeometrySource* sourcePosition = inputPosition->GetSource();
158  FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
159 
160  FCDGeometrySourceList texcoordSources;
161  polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
162 
163  float* dataPosition = sourcePosition->GetData();
164  float* dataNormal = sourceNormal ->GetData();
165  size_t vertexCount = sourcePosition->GetDataCount() / 3;
166  assert(sourcePosition->GetDataCount() == vertexCount*3);
167  assert(sourceNormal ->GetDataCount() == vertexCount*3);
168 
169  std::vector<float*> dataTexcoords;
170  for (size_t i = 0; i < texcoordSources.size(); ++i)
171  {
172  dataTexcoords.push_back(texcoordSources[i]->GetData());
173  }
174 
175  // Transform mesh coordinate system to game coordinates
176  // (doesn't modify prop points)
177 
178  TransformStaticModel(dataPosition, dataNormal, vertexCount, converter.GetEntityTransform(), converter.IsYUp());
179 
180  // Add static prop points
181  // which are empty child nodes of the main parent
182 
183  // Default prop points are already given in game coordinates
184  AddDefaultPropPoints(propPoints);
185 
186  // Calculate transform to convert from COLLADA-defined up_axis to Z-up because
187  // it's relatively straightforward to convert that to game coordinates
188  FMMatrix44 upAxisTransform = FMMatrix44_Identity;
189  if (converter.IsYUp())
190  {
191  // Prop points are rotated -90 degrees about the X-axis, reverse that rotation
192  // (do this once now because it's easier than messing with quaternions later)
193  upAxisTransform = FMMatrix44::XAxisRotationMatrix(1.57f);
194  }
195 
196  AddStaticPropPoints(propPoints, upAxisTransform, converter.GetInstance().GetParent());
197 
198  WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);
199  }
200  else if (converter.GetInstance().GetType() == FCDEntityInstance::CONTROLLER)
201  {
202  Log(LOG_INFO, "Found skinned geometry");
203 
204  FCDControllerInstance& controllerInstance = static_cast<FCDControllerInstance&>(converter.GetInstance());
205 
206  // (NB: GetType is deprecated and should be replaced with HasType,
207  // except that has irritating linker errors when using a DLL, so don't
208  // bother)
209 
210  assert(converter.GetInstance().GetEntity()->GetType() == FCDEntity::CONTROLLER); // assume this is always true?
211  FCDController* controller = static_cast<FCDController*>(converter.GetInstance().GetEntity());
212 
213  FCDSkinController* skin = controller->GetSkinController();
214  REQUIRE(skin != NULL, "is skin controller");
215 
216  FixSkeletonRoots(controllerInstance);
217 
218  // Data for joints is stored in two places - avoid overflows by limiting
219  // to the minimum of the two sizes, and warn if they're different (which
220  // happens in practice for slightly-broken meshes)
221  size_t jointCount = std::min(skin->GetJointCount(), controllerInstance.GetJointCount());
222  if (skin->GetJointCount() != controllerInstance.GetJointCount())
223  {
224  Log(LOG_WARNING, "Mismatched bone counts (skin has %d, skeleton has %d)",
225  skin->GetJointCount(), controllerInstance.GetJointCount());
226  for (size_t i = 0; i < skin->GetJointCount(); ++i)
227  Log(LOG_INFO, "Skin joint %d: %s", i, skin->GetJoint(i)->GetId().c_str());
228  for (size_t i = 0; i < controllerInstance.GetJointCount(); ++i)
229  Log(LOG_INFO, "Skeleton joint %d: %s", i, controllerInstance.GetJoint(i)->GetName().c_str());
230  }
231 
232  // Get the skinned mesh for this entity
233  FCDGeometry* baseGeometry = controller->GetBaseGeometry();
234  REQUIRE(baseGeometry != NULL, "controller has base geometry");
235  FCDGeometryPolygons* polys = GetPolysFromGeometry(baseGeometry);
236 
237  // Make sure it doesn't use more bones per vertex than the game can handle
238  SkinReduceInfluences(skin, maxInfluences, 0.001f);
239 
240  // Convert the geometry into a suitable form for the game
241  ReindexGeometry(polys, skin);
242 
243  const Skeleton& skeleton = FindSkeleton(controllerInstance);
244 
245  // Convert the bone influences into VertexBlend structures for the PMD:
246 
247  bool hasComplainedAboutNonexistentJoints = false; // because we want to emit a warning only once
248 
249  std::vector<VertexBlend> boneWeights; // one per vertex
250 
251  const FCDSkinControllerVertex* vertexInfluences = skin->GetVertexInfluences();
252  for (size_t i = 0; i < skin->GetInfluenceCount(); ++i)
253  {
254  VertexBlend influences = defaultInfluences;
255 
256  assert(vertexInfluences[i].GetPairCount() <= maxInfluences);
257  // guaranteed by ReduceInfluences; necessary for avoiding
258  // out-of-bounds writes to the VertexBlend
259 
260  if (vertexInfluences[i].GetPairCount() == 0)
261  {
262  // Blender exports some models with vertices that have no influences,
263  // which I've not found details about in the COLLADA spec, however,
264  // it seems to work OK to treat these vertices the same as if they
265  // were only influenced by the bind-shape matrix (see comment below),
266  // so we use the same special case here.
267  influences.bones[0] = (uint8)jointCount;
268  influences.weights[0] = 1.0f;
269  }
270 
271  for (size_t j = 0; j < vertexInfluences[i].GetPairCount(); ++j)
272  {
273  if (vertexInfluences[i].GetPair(j)->jointIndex == -1)
274  {
275  // This is a special case we must handle, according to the COLLADA spec:
276  // "An index of -1 into the array of joints refers to the bind shape"
277  //
278  // which basically means when skinning the vertex it's relative to the
279  // bind-shape transform instead of an animated bone. Since our skinning
280  // is in world space, we will have already applied the bind-shape transform,
281  // so we don't have to worry about that, though we DO have to apply the
282  // world space transform of the model for the indicated vertex.
283  //
284  // To indicate this special case, we use a bone ID set to the total number
285  // of bones in the model, which will have a special "bone matrix" reserved
286  // that contains the world space transform of the model during skinning.
287  // (see http://trac.wildfiregames.com/ticket/1012)
288  influences.bones[j] = (uint8)jointCount;
289  influences.weights[j] = vertexInfluences[i].GetPair(j)->weight;
290  }
291  else
292  {
293  // Check for less than 254 joints because we store them in a u8,
294  // 0xFF is a reserved value (no influence), and we reserve one slot
295  // for the above special case.
296  uint32 jointIdx = vertexInfluences[i].GetPair(j)->jointIndex;
297  REQUIRE(jointIdx < 0xFE, "sensible number of joints (<254)");
298 
299  // Find the joint on the skeleton, after checking it really exists
300  FCDSceneNode* joint = NULL;
301  if (jointIdx < controllerInstance.GetJointCount())
302  joint = controllerInstance.GetJoint(jointIdx);
303 
304  // Complain on error
305  if (! joint)
306  {
307  if (! hasComplainedAboutNonexistentJoints)
308  {
309  Log(LOG_WARNING, "Vertexes influenced by nonexistent joint");
310  hasComplainedAboutNonexistentJoints = true;
311  }
312  continue;
313  }
314 
315  // Store into the VertexBlend
316  int boneId = skeleton.GetBoneID(joint->GetName().c_str());
317  if (boneId < 0)
318  {
319  // The relevant joint does exist, but it's not a recognised
320  // bone in our chosen skeleton structure
321  Log(LOG_ERROR, "Vertex influenced by unrecognised bone '%s'", joint->GetName().c_str());
322  continue;
323  }
324 
325  influences.bones[j] = (uint8)boneId;
326  influences.weights[j] = vertexInfluences[i].GetPair(j)->weight;
327  }
328  }
329 
330  boneWeights.push_back(influences);
331  }
332 
333  // Convert the bind pose into BoneTransform structures for the PMD:
334 
335  BoneTransform boneDefault = { { 0, 0, 0 }, { 0, 0, 0, 1 } }; // identity transform
336  std::vector<BoneTransform> boneTransforms (skeleton.GetBoneCount(), boneDefault);
337 
338  for (size_t i = 0; i < jointCount; ++i)
339  {
340  FCDSceneNode* joint = controllerInstance.GetJoint(i);
341 
342  int boneId = skeleton.GetRealBoneID(joint->GetName().c_str());
343  if (boneId < 0)
344  {
345  // unrecognised joint - it's probably just a prop point
346  // or something, so ignore it
347  continue;
348  }
349 
350  FMMatrix44 bindPose = skin->GetJoint(i)->GetBindPoseInverse().Inverted();
351 
352  HMatrix matrix;
353  memcpy(matrix, bindPose.Transposed().m, sizeof(matrix));
354  // set matrix = bindPose^T, to match what decomp_affine wants
355 
356  AffineParts parts;
357  decomp_affine(matrix, &parts);
358 
359  BoneTransform b = {
360  { parts.t.x, parts.t.y, parts.t.z },
361  { parts.q.x, parts.q.y, parts.q.z, parts.q.w }
362  };
363 
364  boneTransforms[boneId] = b;
365  }
366 
367  // Construct the list of prop points.
368  // Currently takes all objects that are directly attached to a
369  // standard bone, and whose name begins with "prop-" or "prop_".
370 
371  std::vector<PropPoint> propPoints;
372  AddDefaultPropPoints(propPoints);
373 
374  for (size_t i = 0; i < jointCount; ++i)
375  {
376  FCDSceneNode* joint = controllerInstance.GetJoint(i);
377 
378  int boneId = skeleton.GetBoneID(joint->GetName().c_str());
379  if (boneId < 0)
380  {
381  // unrecognised joint name - ignore, same as before
382  continue;
383  }
384 
385  // Check all the objects attached to this bone
386  for (size_t j = 0; j < joint->GetChildrenCount(); ++j)
387  {
388  FCDSceneNode* child = joint->GetChild(j);
389  if (child->GetName().find("prop-") != 0 && child->GetName().find("prop_") != 0)
390  {
391  // doesn't begin with "prop-", so skip it
392  continue;
393  }
394  // Strip off the "prop-" from the name
395  std::string propPointName (child->GetName().substr(5));
396 
397  Log(LOG_INFO, "Adding prop point %s", propPointName.c_str());
398 
399  // Get translation and orientation of local transform
400 
401  FMMatrix44 localTransform = child->ToMatrix();
402 
403  HMatrix matrix;
404  memcpy(matrix, localTransform.Transposed().m, sizeof(matrix));
405 
406  AffineParts parts;
407  decomp_affine(matrix, &parts);
408 
409  // Add prop point to list
410 
411  PropPoint p = {
412  propPointName,
413  { parts.t.x, parts.t.y, parts.t.z },
414  { parts.q.x, parts.q.y, parts.q.z, parts.q.w },
415  (uint8)boneId
416  };
417  propPoints.push_back(p);
418  }
419  }
420 
421  // Get the raw vertex data
422 
423  FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
424  FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
425 
426  const uint32* indicesCombined = inputPosition->GetIndices();
427  size_t indicesCombinedCount = inputPosition->GetIndexCount();
428  // (ReindexGeometry guarantees position/normal/texcoord have the same indexes)
429 
430  FCDGeometrySource* sourcePosition = inputPosition->GetSource();
431  FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
432 
433  FCDGeometrySourceList texcoordSources;
434  polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
435 
436  float* dataPosition = sourcePosition->GetData();
437  float* dataNormal = sourceNormal ->GetData();
438  size_t vertexCount = sourcePosition->GetDataCount() / 3;
439  assert(sourcePosition->GetDataCount() == vertexCount*3);
440  assert(sourceNormal ->GetDataCount() == vertexCount*3);
441 
442  std::vector<float*> dataTexcoords;
443  for (size_t i = 0; i < texcoordSources.size(); ++i)
444  {
445  dataTexcoords.push_back(texcoordSources[i]->GetData());
446  }
447 
448  // Transform model coordinate system to game coordinates
449 
450  TransformSkinnedModel(dataPosition, dataNormal, vertexCount, boneTransforms, propPoints,
451  converter.GetEntityTransform(), skin->GetBindShapeTransform(),
452  converter.IsYUp(), converter.IsXSI());
453 
454  WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);
455  }
456  else
457  {
458  throw ColladaException("Unrecognised object type");
459  }
460 
461  }
462 
463  /**
464  * Adds the default "root" prop-point.
465  */
466  static void AddDefaultPropPoints(std::vector<PropPoint>& propPoints)
467  {
468  PropPoint root;
469  root.name = "root";
470  root.translation[0] = root.translation[1] = root.translation[2] = 0.0f;
471  root.orientation[0] = root.orientation[1] = root.orientation[2] = 0.0f;
472  root.orientation[3] = 1.0f;
473  root.bone = 0xFF;
474  propPoints.push_back(root);
475  }
476 
477  /**
478  * Writes the model data in the PMD format.
479  */
480  static void WritePMD(OutputCB& output,
481  const uint32* indices, size_t indexCount,
482  const float* position, const float* normal,
483  const std::vector<float*>& texcoords,
484  size_t vertexCount,
485  const std::vector<VertexBlend>& boneWeights, const std::vector<BoneTransform>& boneTransforms,
486  const std::vector<PropPoint>& propPoints)
487  {
488  static const VertexBlend noBlend = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };
489 
490  size_t faceCount = indexCount/3;
491  size_t boneCount = boneTransforms.size();
492  if (boneCount)
493  assert(boneWeights.size() == vertexCount);
494 
495  size_t propPointsSize = 0; // can't calculate this statically, so loop over all the prop points
496  for (size_t i = 0; i < propPoints.size(); ++i)
497  {
498  propPointsSize += 4 + propPoints[i].name.length();
499  propPointsSize += 3*4 + 4*4 + 1;
500  }
501 
502  output("PSMD", 4); // magic number
503  write(output, (uint32)4); // version number
504  write(output, (uint32)(
505  // for UVs, we add one uint32 (i.e. 4 bytes) per model that gives the number of
506  // texcoord sets in the model, plus 2 floats per new UV
507  // pair per vertex (i.e. 8 bytes * number of pairs * vertex count)
508  4 + 11*4*vertexCount + 4 + 8*texcoords.size()*vertexCount + // vertices
509  4 + 6*faceCount + // faces
510  4 + 7*4*boneCount + // bones
511  4 + propPointsSize // props
512  )); // data size
513 
514  // Vertex data
515  write<uint32>(output, (uint32)vertexCount);
516  write<uint32>(output, (uint32)texcoords.size()); // UV pairs per vertex
517  for (size_t i = 0; i < vertexCount; ++i)
518  {
519  output((char*)&position[i*3], 12);
520  output((char*)&normal [i*3], 12);
521 
522  for (size_t s = 0; s < texcoords.size(); ++s)
523  {
524  output((char*)&texcoords[s][i*2], 8);
525  }
526 
527  if (boneCount)
528  write(output, boneWeights[i]);
529  else
530  write(output, noBlend);
531  }
532 
533  // Face data
534  write(output, (uint32)faceCount);
535  for (size_t i = 0; i < indexCount; ++i)
536  {
537  write(output, (uint16)indices[i]);
538  }
539 
540  // Bones data
541  write(output, (uint32)boneCount);
542  for (size_t i = 0; i < boneCount; ++i)
543  {
544  output((char*)&boneTransforms[i], 7*4);
545  }
546 
547  // Prop points data
548  write(output, (uint32)propPoints.size());
549  for (size_t i = 0; i < propPoints.size(); ++i)
550  {
551  uint32 nameLen = (uint32)propPoints[i].name.length();
552  write(output, nameLen);
553  output(propPoints[i].name.c_str(), nameLen);
554  write(output, propPoints[i].translation);
555  write(output, propPoints[i].orientation);
556  write(output, propPoints[i].bone);
557  }
558  }
559 
560  static FCDGeometryPolygons* GetPolysFromGeometry(FCDGeometry* geom)
561  {
562  REQUIRE(geom->IsMesh(), "geometry is mesh");
563  FCDGeometryMesh* mesh = geom->GetMesh();
564 
565  if (! mesh->IsTriangles())
566  FCDGeometryPolygonsTools::Triangulate(mesh);
567 
568  REQUIRE(mesh->IsTriangles(), "mesh is made of triangles");
569  REQUIRE(mesh->GetPolygonsCount() == 1, "mesh has single set of polygons");
570  FCDGeometryPolygons* polys = mesh->GetPolygons(0);
571  REQUIRE(polys->FindInput(FUDaeGeometryInput::POSITION) != NULL, "mesh has vertex positions");
572  REQUIRE(polys->FindInput(FUDaeGeometryInput::NORMAL) != NULL, "mesh has vertex normals");
573  REQUIRE(polys->FindInput(FUDaeGeometryInput::TEXCOORD) != NULL, "mesh has vertex tex coords");
574  return polys;
575  }
576 
577  /**
578  * Applies world-space transform to vertex data and transforms Collada's right-handed
579  * Y-up / Z-up coordinates to the game's left-handed Y-up coordinate system
580  *
581  * TODO: Maybe we should use FCDocumentTools::StandardizeUpAxisAndLength in addition
582  * to this, so we'd only have one up-axis case to worry about, but it doesn't seem to
583  * correctly adjust the prop points in Y_UP models.
584  */
585  static void TransformStaticModel(float* position, float* normal, size_t vertexCount,
586  const FMMatrix44& transform, bool yUp)
587  {
588  for (size_t i = 0; i < vertexCount; ++i)
589  {
590  FMVector3 pos (&position[i*3], 0);
591  FMVector3 norm (&normal[i*3], 0);
592 
593  // Apply the scene-node transforms
594  pos = transform.TransformCoordinate(pos);
595  norm = FMVector3_Normalize(transform.TransformVector(norm));
596 
597  // Convert from right-handed Y_UP or Z_UP to the game's coordinate system (left-handed Y-up)
598 
599  if (yUp)
600  {
601  pos.z = -pos.z;
602  norm.z = -norm.z;
603  }
604  else
605  {
606  std::swap(pos.y, pos.z);
607  std::swap(norm.y, norm.z);
608  }
609 
610  // Copy back to array
611 
612  position[i*3] = pos.x;
613  position[i*3+1] = pos.y;
614  position[i*3+2] = pos.z;
615 
616  normal[i*3] = norm.x;
617  normal[i*3+1] = norm.y;
618  normal[i*3+2] = norm.z;
619  }
620  }
621 
622  /**
623  * Applies world-space transform to vertex data and transforms Collada's right-handed
624  * Y-up / Z-up coordinates to the game's left-handed Y-up coordinate system
625  *
626  * TODO: Maybe we should use FCDocumentTools::StandardizeUpAxisAndLength in addition
627  * to this, so we'd only have one up-axis case to worry about, but it doesn't seem to
628  * correctly adjust the prop points in Y_UP models.
629  */
630  static void TransformSkinnedModel(float* position, float* normal, size_t vertexCount,
631  std::vector<BoneTransform>& bones, std::vector<PropPoint>& propPoints,
632  const FMMatrix44& transform, const FMMatrix44& bindTransform, bool yUp, bool isXSI)
633  {
634  FMMatrix44 scaledTransform; // for vertexes
635  FMMatrix44 scaleMatrix; // for bones
636 
637  // HACK: see comment in PSAConvert::TransformVertices
638  if (isXSI)
639  {
640  scaleMatrix = DecomposeToScaleMatrix(transform);
641  scaledTransform = DecomposeToScaleMatrix(bindTransform) * transform;
642  }
643  else
644  {
645  scaleMatrix = FMMatrix44_Identity;
646  scaledTransform = bindTransform;
647  }
648 
649  // Update the vertex positions and normals
650  for (size_t i = 0; i < vertexCount; ++i)
651  {
652  FMVector3 pos (&position[i*3], 0);
653  FMVector3 norm (&normal[i*3], 0);
654 
655  // Apply the scene-node transforms
656  pos = scaledTransform.TransformCoordinate(pos);
657  norm = FMVector3_Normalize(scaledTransform.TransformVector(norm));
658 
659  // Convert from right-handed Y_UP or Z_UP to the game's coordinate system (left-handed Y-up)
660 
661  if (yUp)
662  {
663  pos.z = -pos.z;
664  norm.z = -norm.z;
665  }
666  else
667  {
668  std::swap(pos.y, pos.z);
669  std::swap(norm.y, norm.z);
670  }
671 
672  // and copy back into the original array
673 
674  position[i*3] = pos.x;
675  position[i*3+1] = pos.y;
676  position[i*3+2] = pos.z;
677 
678  normal[i*3] = norm.x;
679  normal[i*3+1] = norm.y;
680  normal[i*3+2] = norm.z;
681  }
682 
683  TransformBones(bones, scaleMatrix, yUp);
684 
685  // And do the same for prop points
686  for (size_t i = 0; i < propPoints.size(); ++i)
687  {
688  if (yUp)
689  {
690  propPoints[i].translation[0] = -propPoints[i].translation[0];
691  propPoints[i].orientation[0] = -propPoints[i].orientation[0];
692  propPoints[i].orientation[3] = -propPoints[i].orientation[3];
693  }
694  else
695  {
696  // Flip translation across the x-axis by swapping y and z
697  std::swap(propPoints[i].translation[1], propPoints[i].translation[2]);
698 
699  // To convert the quaternions: imagine you're using the axis/angle
700  // representation, then swap the y,z basis vectors and change the
701  // direction of rotation by negating the angle ( => negating sin(angle)
702  // => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)
703  // but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)
704  std::swap(propPoints[i].orientation[1], propPoints[i].orientation[2]);
705  propPoints[i].orientation[3] = -propPoints[i].orientation[3];
706  }
707  }
708 
709  }
710 };
711 
712 
713 // The above stuff is just in a class since I don't like having to bother
714 // with forward declarations of functions - but provide the plain function
715 // interface here:
716 
717 void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)
718 {
719  PMDConvert::ColladaToPMD(input, output, xmlErrors);
720 }
float x
Definition: Decompose.h:21
float weights[maxInfluences]
Definition: PMDConvert.cpp:50
static void TransformSkinnedModel(float *position, float *normal, size_t vertexCount, std::vector< BoneTransform > &bones, std::vector< PropPoint > &propPoints, const FMMatrix44 &transform, const FMMatrix44 &bindTransform, bool yUp, bool isXSI)
Applies world-space transform to vertex data and transforms Collada&#39;s right-handed Y-up / Z-up coordi...
Definition: PMDConvert.cpp:630
#define REQUIRE(value, message)
Throws a ColladaException unless the value is true.
float z
Definition: Decompose.h:21
tuple output
Definition: tests.py:116
HVect t
Definition: Decompose.h:26
Bone pose data.
void SkinReduceInfluences(FCDSkinController *skin, size_t maxInfluenceCount, float minimumWeight)
Like FCDSkinController::ReduceInfluences but works correctly.
float HMatrix[4][4]
Definition: Decompose.h:24
static void swap(UniqueRange &p1, UniqueRange &p2)
Definition: unique_range.h:176
float y
Definition: Decompose.h:21
float orientation[4]
Definition: PMDConvert.cpp:58
#define LOG_ERROR
static void AddDefaultPropPoints(std::vector< PropPoint > &propPoints)
Adds the default &quot;root&quot; prop-point.
Definition: PMDConvert.cpp:466
FMMatrix44 FMMatrix44_Identity
void ReindexGeometry(FCDGeometryPolygons *polys, FCDSkinController *skin)
void FixSkeletonRoots(FCDControllerInstance &controllerInstance)
Fixes some occasional problems with the skeleton root definitions in a controller.
const Skeleton & FindSkeleton(const FCDControllerInstance &controllerInstance)
Finds the skeleton definition which best matches the given controller.
static void ColladaToPMD(const char *input, OutputCB &output, std::string &xmlErrors)
Converts a COLLADA XML document into the PMD mesh format.
Definition: PMDConvert.cpp:131
VertexBlend defaultInfluences
Definition: PMDConvert.cpp:52
const size_t maxInfluences
Definition: PMDConvert.cpp:46
FMMatrix44 DecomposeToScaleMatrix(const FMMatrix44 &m)
Definition: Maths.cpp:35
#define LOG_INFO
static FCDGeometryPolygons * GetPolysFromGeometry(FCDGeometry *geom)
Definition: PMDConvert.cpp:560
void TransformBones(std::vector< BoneTransform > &bones, const FMMatrix44 &scaleTransform, bool yUp)
Performs the standard transformations on bones, applying a scale matrix and moving them into the game...
uint8 bone
Definition: PMDConvert.cpp:59
int GetBoneCount() const
Returns the number of bones in the standard-skeleton which this current skeleton is mapped onto...
int GetRealBoneID(const std::string &name) const
Similar to GetBoneID, but only returns a value for the most important (first) nonstandard-skeleton bo...
FCDEntityInstance & GetInstance()
static FMVector3 FMVector3_Normalize(const FMVector3 &vec)
Definition: PMDConvert.cpp:65
float w
Definition: Decompose.h:21
void decomp_affine(HMatrix A, AffineParts *parts)
Definition: Decompose.cpp:486
Wrapper for code shared between the PMD and PSA converters.
Definition: CommonConvert.h:97
void write(OutputCB &output, const T &data)
Outputs a structure, using sizeof to get the size.
static void WritePMD(OutputCB &output, const uint32 *indices, size_t indexCount, const float *position, const float *normal, const std::vector< float * > &texcoords, size_t vertexCount, const std::vector< VertexBlend > &boneWeights, const std::vector< BoneTransform > &boneTransforms, const std::vector< PropPoint > &propPoints)
Writes the model data in the PMD format.
Definition: PMDConvert.cpp:480
bool IsXSI() const
float translation[3]
Definition: PMDConvert.cpp:57
tuple input
Definition: tests.py:115
std::string name
Definition: PMDConvert.cpp:56
bool IsYUp() const
uint8 bones[maxInfluences]
Definition: PMDConvert.cpp:49
const FMMatrix44 & GetEntityTransform() const
static void AddStaticPropPoints(std::vector< PropPoint > &propPoints, const FMMatrix44 &upAxisTransform, FCDSceneNode *node)
Definition: PMDConvert.cpp:74
#define LOG_WARNING
int GetBoneID(const std::string &name) const
Returns the ID number of the standard-skeleton bone, which corresponds to the named nonstandard-skele...
void Log(int severity, const char *msg,...)
Definition: DLL.cpp:50
static void TransformStaticModel(float *position, float *normal, size_t vertexCount, const FMMatrix44 &transform, bool yUp)
Applies world-space transform to vertex data and transforms Collada&#39;s right-handed Y-up / Z-up coordi...
Definition: PMDConvert.cpp:585