Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
StdSkeletons.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2009 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  struct SkeletonMap : public std::map<std::string, const Skeleton*>
34  {
36  {
37  for (iterator it = begin(); it != end(); ++it)
38  delete it->second;
39  }
40  };
41 
44 
45  struct Bone
46  {
47  std::string parent;
48  std::string name;
49  int targetId;
51  };
52 }
53 
55 {
56  std::string title;
57  std::vector<Bone> bones;
58  const Skeleton* target;
59 };
60 
63 
64 const Skeleton* Skeleton::FindSkeleton(const std::string& name)
65 {
66  return g_MappedSkeletons[name];
67 }
68 
69 int Skeleton::GetBoneID(const std::string& name) const
70 {
71  for (size_t i = 0; i < m->bones.size(); ++i)
72  if (m->bones[i].name == name)
73  return m->bones[i].targetId;
74  return -1;
75 }
76 
77 int Skeleton::GetRealBoneID(const std::string& name) const
78 {
79  for (size_t i = 0; i < m->bones.size(); ++i)
80  if (m->bones[i].name == name)
81  return m->bones[i].realTargetId;
82  return -1;
83 }
84 
86 {
87  return (int)m->target->m->bones.size();
88 }
89 
90 namespace
91 {
92  bool AlreadyUsedTargetBone(const std::vector<Bone>& bones, int targetId)
93  {
94  for (size_t i = 0; i < bones.size(); ++i)
95  if (bones[i].targetId == targetId)
96  return true;
97  return false;
98  }
99 
100  // Recursive helper function used by LoadSkeletonData
101  void LoadSkeletonBones(xmlNode* parent, std::vector<Bone>& bones, const Skeleton* targetSkeleton, const std::string& targetName)
102  {
103  xmlNodeList boneNodes;
104  FUXmlParser::FindChildrenByType(parent, "bone", boneNodes);
105  for (xmlNodeList::iterator boneNode = boneNodes.begin(); boneNode != boneNodes.end(); ++boneNode)
106  {
107  std::string name (FUXmlParser::ReadNodeProperty(*boneNode, "name"));
108 
109  Bone b;
110  b.name = name;
111 
112  std::string newTargetName = targetName;
113 
114  if (targetSkeleton)
115  {
116  xmlNode* targetNode = FUXmlParser::FindChildByType(*boneNode, "target");
117  if (targetNode)
118  newTargetName = FUXmlParser::ReadNodeContentFull(targetNode);
119  // else fall back to the parent node's target
120 
121  b.targetId = targetSkeleton->GetBoneID(newTargetName);
122  REQUIRE(b.targetId != -1, "skeleton bone target matches some standard_skeleton bone name");
123 
124  if (AlreadyUsedTargetBone(bones, b.targetId))
125  b.realTargetId = -1;
126  else
127  b.realTargetId = b.targetId;
128  }
129  else
130  {
131  // No target - this is a standard skeleton
132 
133  b.targetId = (int)bones.size();
134  b.realTargetId = b.targetId;
135  }
136 
137  bones.push_back(b);
138 
139  LoadSkeletonBones(*boneNode, bones, targetSkeleton, newTargetName);
140  }
141  }
142 
143  void LoadSkeletonData(xmlNode* root)
144  {
145  xmlNodeList skeletonNodes;
146  FUXmlParser::FindChildrenByType(root, "standard_skeleton", skeletonNodes);
147  FUXmlParser::FindChildrenByType(root, "skeleton", skeletonNodes);
148  for (xmlNodeList::iterator skeletonNode = skeletonNodes.begin();
149  skeletonNode != skeletonNodes.end(); ++skeletonNode)
150  {
151  std::auto_ptr<Skeleton> skeleton (new Skeleton());
152 
153  std::string title (FUXmlParser::ReadNodeProperty(*skeletonNode, "title"));
154 
155  skeleton->m->title = title;
156 
157  if (IsEquivalent((*skeletonNode)->name, "standard_skeleton"))
158  {
159  skeleton->m->target = NULL;
160 
161  LoadSkeletonBones(*skeletonNode, skeleton->m->bones, NULL, "");
162 
163  std::string id (FUXmlParser::ReadNodeProperty(*skeletonNode, "id"));
164  REQUIRE(! id.empty(), "standard_skeleton has id");
165 
166  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  std::string target (FUXmlParser::ReadNodeProperty(*skeletonNode, "target"));
174  const Skeleton* targetSkeleton = g_StandardSkeletons[target];
175  REQUIRE(targetSkeleton != NULL, "skeleton target matches some standard_skeleton id");
176 
177  skeleton->m->target = targetSkeleton;
178 
179  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  xmlNode* identifier = FUXmlParser::FindChildByType(*skeletonNode, "identifier");
184  REQUIRE(identifier != NULL, "skeleton has <identifier>");
185  xmlNode* identRoot = FUXmlParser::FindChildByType(identifier, "root");
186  REQUIRE(identRoot != NULL, "skeleton identifier has <root>");
187  std::string identRootName (FUXmlParser::ReadNodeContentFull(identRoot));
188 
189  g_MappedSkeletons[identRootName] = skeleton.release();
190  }
191  }
192  }
193 }
194 
195 void errorHandler(void* ctx, const char* msg, ...);
196 
197 void Skeleton::LoadSkeletonDataFromXml(const char* xmlData, size_t xmlLength, std::string& xmlErrors)
198 {
199  xmlDoc* doc = NULL;
200  try
201  {
202  xmlSetGenericErrorFunc(&xmlErrors, &errorHandler);
203  doc = xmlParseMemory(xmlData, (int)xmlLength);
204  if (doc)
205  {
206  xmlNode* root = xmlDocGetRootElement(doc);
207  LoadSkeletonData(root);
208  xmlFreeDoc(doc);
209  doc = NULL;
210  }
211  xmlSetGenericErrorFunc(NULL, NULL);
212  }
213  catch (const ColladaException&)
214  {
215  if (doc)
216  xmlFreeDoc(doc);
217  xmlSetGenericErrorFunc(NULL, NULL);
218  throw;
219  }
220 
221  if (! xmlErrors.empty())
222  throw ColladaException("XML parsing failed");
223 }
static const Skeleton * FindSkeleton(const std::string &rootBoneName)
Tries to find a skeleton that matches the given root bone name.
#define REQUIRE(value, message)
Throws a ColladaException unless the value is true.
std::vector< Bone > bones
bool AlreadyUsedTargetBone(const std::vector< Bone > &bones, int targetId)
std::auto_ptr< Skeleton_impl > m
Definition: StdSkeletons.h:96
void LoadSkeletonBones(xmlNode *parent, std::vector< Bone > &bones, const Skeleton *targetSkeleton, const std::string &targetName)
std::string title
const Skeleton * target
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...
static void LoadSkeletonDataFromXml(const char *xmlData, size_t xmlLength, std::string &xmlErrors)
Initialises the global state with skeleton data loaded from the given XML data.
struct _xmlDoc xmlDoc
Definition: Xeromyces.h:39
void errorHandler(void *ctx, const char *msg,...)
Error handler for libxml2.
Skeleton()
Default constructor - don&#39;t use; use FindSkeleton instead.
int GetBoneID(const std::string &name) const
Returns the ID number of the standard-skeleton bone, which corresponds to the named nonstandard-skele...