LCOV - code coverage report
Current view: top level - source/collada - StdSkeletons.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 27 98 27.6 %
Date: 2023-01-19 00:18:29 Functions: 6 20 30.0 %

          Line data    Source code
       1             : /* Copyright (C) 2021 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 "libxml/parser.h"
      21             : #include "libxml/xmlerror.h"
      22             : 
      23             : #include "StdSkeletons.h"
      24             : 
      25             : #include "CommonConvert.h"
      26             : 
      27             : #include "FUtils/FUXmlParser.h"
      28             : 
      29             : #include <map>
      30             : 
      31             : namespace
      32             : {
      33           2 :     struct SkeletonMap : public std::map<std::string, const Skeleton*>
      34             :     {
      35           2 :         ~SkeletonMap()
      36           2 :         {
      37           2 :             for (iterator it = begin(); it != end(); ++it)
      38           0 :                 delete it->second;
      39           2 :         }
      40             :     };
      41             : 
      42           1 :     SkeletonMap g_StandardSkeletons;
      43           1 :     SkeletonMap g_MappedSkeletons;
      44             : 
      45           0 :     struct Bone
      46             :     {
      47             :         std::string parent;
      48             :         std::string name;
      49             :         int targetId;
      50             :         int realTargetId;
      51             :     };
      52             : }
      53             : 
      54           0 : struct Skeleton_impl
      55             : {
      56             :     std::string title;
      57             :     std::vector<Bone> bones;
      58             :     const Skeleton* target;
      59             : };
      60             : 
      61           0 : Skeleton::Skeleton() : m(new Skeleton_impl) { }
      62           0 : Skeleton::~Skeleton() { }
      63             : 
      64           0 : const Skeleton* Skeleton::FindSkeleton(const std::string& name)
      65             : {
      66           0 :     return g_MappedSkeletons[name];
      67             : }
      68             : 
      69           0 : int Skeleton::GetBoneID(const std::string& name) const
      70             : {
      71           0 :     for (size_t i = 0; i < m->bones.size(); ++i)
      72           0 :         if (m->bones[i].name == name)
      73           0 :             return m->bones[i].targetId;
      74           0 :     return -1;
      75             : }
      76             : 
      77           0 : int Skeleton::GetRealBoneID(const std::string& name) const
      78             : {
      79           0 :     for (size_t i = 0; i < m->bones.size(); ++i)
      80           0 :         if (m->bones[i].name == name)
      81           0 :             return m->bones[i].realTargetId;
      82           0 :     return -1;
      83             : }
      84             : 
      85           0 : int Skeleton::GetBoneCount() const
      86             : {
      87           0 :     return (int)m->target->m->bones.size();
      88             : }
      89             : 
      90             : namespace
      91             : {
      92           0 :     bool AlreadyUsedTargetBone(const std::vector<Bone>& bones, int targetId)
      93             :     {
      94           0 :         for (size_t i = 0; i < bones.size(); ++i)
      95           0 :             if (bones[i].targetId == targetId)
      96           0 :                 return true;
      97           0 :         return false;
      98             :     }
      99             : 
     100             :     // Recursive helper function used by LoadSkeletonData
     101           0 :     void LoadSkeletonBones(xmlNode* parent, std::vector<Bone>& bones, const Skeleton* targetSkeleton, const std::string& targetName)
     102             :     {
     103           0 :         xmlNodeList boneNodes;
     104           0 :         FUXmlParser::FindChildrenByType(parent, "bone", boneNodes);
     105           0 :         for (xmlNodeList::iterator boneNode = boneNodes.begin(); boneNode != boneNodes.end(); ++boneNode)
     106             :         {
     107           0 :             std::string name (FUXmlParser::ReadNodeProperty(*boneNode, "name"));
     108             : 
     109           0 :             Bone b;
     110           0 :             b.name = name;
     111             : 
     112           0 :             std::string newTargetName = targetName;
     113             : 
     114           0 :             if (targetSkeleton)
     115             :             {
     116           0 :                 xmlNode* targetNode = FUXmlParser::FindChildByType(*boneNode, "target");
     117           0 :                 if (targetNode)
     118           0 :                     newTargetName = FUXmlParser::ReadNodeContentFull(targetNode);
     119             :                 // else fall back to the parent node's target
     120             : 
     121           0 :                 b.targetId = targetSkeleton->GetBoneID(newTargetName);
     122           0 :                 REQUIRE(b.targetId != -1, "skeleton bone target matches some standard_skeleton bone name");
     123             : 
     124           0 :                 if (AlreadyUsedTargetBone(bones, b.targetId))
     125           0 :                     b.realTargetId = -1;
     126             :                 else
     127           0 :                     b.realTargetId = b.targetId;
     128             :             }
     129             :             else
     130             :             {
     131             :                 // No target - this is a standard skeleton
     132             : 
     133           0 :                 b.targetId = (int)bones.size();
     134           0 :                 b.realTargetId = b.targetId;
     135             :             }
     136             : 
     137           0 :             bones.push_back(b);
     138             : 
     139           0 :             LoadSkeletonBones(*boneNode, bones, targetSkeleton, newTargetName);
     140             :         }
     141           0 :     }
     142             : 
     143           5 :     void LoadSkeletonData(xmlNode* root)
     144             :     {
     145          10 :         xmlNodeList skeletonNodes;
     146           5 :         FUXmlParser::FindChildrenByType(root, "standard_skeleton", skeletonNodes);
     147           5 :         FUXmlParser::FindChildrenByType(root, "skeleton", skeletonNodes);
     148           5 :         for (xmlNodeList::iterator skeletonNode = skeletonNodes.begin();
     149           5 :             skeletonNode != skeletonNodes.end(); ++skeletonNode)
     150             :         {
     151           0 :             std::unique_ptr<Skeleton> skeleton = std::make_unique<Skeleton>();
     152             : 
     153           0 :             std::string title (FUXmlParser::ReadNodeProperty(*skeletonNode, "title"));
     154             : 
     155           0 :             skeleton->m->title = title;
     156             : 
     157           0 :             if (IsEquivalent((*skeletonNode)->name, "standard_skeleton"))
     158             :             {
     159           0 :                 skeleton->m->target = NULL;
     160             : 
     161           0 :                 LoadSkeletonBones(*skeletonNode, skeleton->m->bones, NULL, "");
     162             : 
     163           0 :                 std::string id (FUXmlParser::ReadNodeProperty(*skeletonNode, "id"));
     164           0 :                 REQUIRE(! id.empty(), "standard_skeleton has id");
     165             : 
     166           0 :                 g_StandardSkeletons[id] = skeleton.release();
     167             :             }
     168             :             else
     169             :             {
     170             :                 // Non-standard skeletons need to choose a standard skeleton
     171             :                 // as their target to be mapped onto
     172             : 
     173           0 :                 std::string target (FUXmlParser::ReadNodeProperty(*skeletonNode, "target"));
     174           0 :                 const Skeleton* targetSkeleton = g_StandardSkeletons[target];
     175           0 :                 REQUIRE(targetSkeleton != NULL, "skeleton target matches some standard_skeleton id");
     176             : 
     177           0 :                 skeleton->m->target = targetSkeleton;
     178             : 
     179           0 :                 LoadSkeletonBones(*skeletonNode, skeleton->m->bones, targetSkeleton, "");
     180             : 
     181             :                 // Currently the only supported identifier is a precise name match,
     182             :                 // so just look for that
     183           0 :                 xmlNode* identifier = FUXmlParser::FindChildByType(*skeletonNode, "identifier");
     184           0 :                 REQUIRE(identifier != NULL, "skeleton has <identifier>");
     185           0 :                 xmlNode* identRoot = FUXmlParser::FindChildByType(identifier, "root");
     186           0 :                 REQUIRE(identRoot != NULL, "skeleton identifier has <root>");
     187           0 :                 std::string identRootName (FUXmlParser::ReadNodeContentFull(identRoot));
     188             : 
     189           0 :                 g_MappedSkeletons[identRootName] = skeleton.release();
     190             :             }
     191             :         }
     192           5 :     }
     193             : }
     194             : 
     195             : void errorHandler(void* ctx, const char* msg, ...);
     196             : 
     197           6 : void Skeleton::LoadSkeletonDataFromXml(const char* xmlData, size_t xmlLength, std::string& xmlErrors)
     198             : {
     199           6 :     xmlDoc* doc = NULL;
     200             :     try
     201             :     {
     202           6 :         xmlSetGenericErrorFunc(&xmlErrors, &errorHandler);
     203           6 :         doc = xmlParseMemory(xmlData, (int)xmlLength);
     204           6 :         if (doc)
     205             :         {
     206           5 :             xmlNode* root = xmlDocGetRootElement(doc);
     207           5 :             LoadSkeletonData(root);
     208           5 :             xmlFreeDoc(doc);
     209           5 :             doc = NULL;
     210             :         }
     211           6 :         xmlSetGenericErrorFunc(NULL, NULL);
     212             :     }
     213           0 :     catch (const ColladaException&)
     214             :     {
     215           0 :         if (doc)
     216           0 :             xmlFreeDoc(doc);
     217           0 :         xmlSetGenericErrorFunc(NULL, NULL);
     218           0 :         throw;
     219             :     }
     220             : 
     221           6 :     if (! xmlErrors.empty())
     222           1 :         throw ColladaException("XML parsing failed");
     223           8 : }

Generated by: LCOV version 1.13