LCOV - code coverage report
Current view: top level - source/collada - PMDConvert.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 117 275 42.5 %
Date: 2023-01-19 00:18:29 Functions: 11 13 84.6 %

          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 : }

Generated by: LCOV version 1.13