goat3d

annotate src/goat3d_readxml.cc @ 96:20b04b4edad4

[goat3d] removed C++11-ism from libgoat3d
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 19 May 2014 06:46:30 +0300
parents da100bf13f7f
children
rev   line source
nuclear@54 1 /*
nuclear@54 2 goat3d - 3D scene, character, and animation file format library.
nuclear@54 3 Copyright (C) 2013-2014 John Tsiombikas <nuclear@member.fsf.org>
nuclear@54 4
nuclear@54 5 This program is free software: you can redistribute it and/or modify
nuclear@54 6 it under the terms of the GNU Lesser General Public License as published by
nuclear@54 7 the Free Software Foundation, either version 3 of the License, or
nuclear@54 8 (at your option) any later version.
nuclear@54 9
nuclear@54 10 This program is distributed in the hope that it will be useful,
nuclear@54 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
nuclear@54 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
nuclear@54 13 GNU Lesser General Public License for more details.
nuclear@54 14
nuclear@54 15 You should have received a copy of the GNU Lesser General Public License
nuclear@54 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
nuclear@54 17 */
nuclear@19 18 #include <stdio.h>
nuclear@75 19 #include <map>
nuclear@75 20 #include <string>
nuclear@95 21 #include <algorithm>
nuclear@19 22 #include "goat3d.h"
nuclear@19 23 #include "goat3d_impl.h"
nuclear@19 24 #include "tinyxml2.h"
nuclear@19 25 #include "log.h"
nuclear@19 26
nuclear@47 27 using namespace g3dimpl;
nuclear@19 28 using namespace tinyxml2;
nuclear@19 29
nuclear@95 30 struct Key {
nuclear@95 31 long tm;
nuclear@95 32 Vector4 val;
nuclear@95 33 };
nuclear@95 34
nuclear@95 35 static bool read_track(Scene *scn, XMLElement *xml_track, int *anim_idx);
nuclear@95 36 static Key read_key(XMLElement *xml_key);
nuclear@95 37
nuclear@19 38 static Material *read_material(Scene *scn, XMLElement *xml_mtl);
nuclear@19 39 static const char *read_material_attrib(MaterialAttrib *attr, XMLElement *xml_attr);
nuclear@19 40 static Mesh *read_mesh(Scene *scn, XMLElement *xml_mesh);
nuclear@75 41 static Node *read_node(Scene *scn, XMLElement *xml_node, std::map<Node*, std::string> &linkmap);
nuclear@75 42 static std::string get_name(XMLElement *node, int idx, const char *def_prefix);
nuclear@19 43
nuclear@19 44 bool Scene::loadxml(goat3d_io *io)
nuclear@19 45 {
nuclear@19 46 long bytes = io->seek(0, SEEK_END, io->cls);
nuclear@19 47 io->seek(0, SEEK_SET, io->cls);
nuclear@19 48
nuclear@19 49 char *buf = new char[bytes];
nuclear@19 50 if(io->read(buf, bytes, io->cls) < bytes) {
nuclear@19 51 logmsg(LOG_ERROR, "failed to read XML scene file\n");
nuclear@45 52 delete [] buf;
nuclear@19 53 return false;
nuclear@19 54 }
nuclear@19 55
nuclear@19 56 XMLDocument xml;
nuclear@19 57 XMLError err = xml.Parse(buf, bytes);
nuclear@19 58 if(err) {
nuclear@19 59 logmsg(LOG_ERROR, "failed to parse XML scene file: %s\n%s\n", xml.GetErrorStr1(),
nuclear@19 60 xml.GetErrorStr2());
nuclear@45 61 delete [] buf;
nuclear@19 62 return false;
nuclear@19 63 }
nuclear@19 64
nuclear@19 65 XMLElement *root = xml.RootElement();
nuclear@19 66 if(strcmp(root->Name(), "scene") != 0) {
nuclear@19 67 logmsg(LOG_ERROR, "invalid XML file, root node is not <scene>\n");
nuclear@45 68 delete [] buf;
nuclear@19 69 return false;
nuclear@19 70 }
nuclear@19 71
nuclear@19 72 XMLElement *elem;
nuclear@19 73
nuclear@19 74 // get all materials
nuclear@19 75 elem = root->FirstChildElement("mtl");
nuclear@19 76 while(elem) {
nuclear@19 77 Material *mtl = read_material(this, elem);
nuclear@19 78 if(mtl) {
nuclear@19 79 add_material(mtl);
nuclear@19 80 }
nuclear@19 81 elem = elem->NextSiblingElement("mtl");
nuclear@19 82 }
nuclear@19 83
nuclear@19 84 // get all meshes
nuclear@19 85 elem = root->FirstChildElement("mesh");
nuclear@19 86 while(elem) {
nuclear@19 87 Mesh *mesh = read_mesh(this, elem);
nuclear@19 88 if(mesh) {
nuclear@19 89 add_mesh(mesh);
nuclear@19 90 }
nuclear@19 91 elem = elem->NextSiblingElement("mesh");
nuclear@19 92 }
nuclear@19 93
nuclear@75 94 // get all nodes
nuclear@75 95 std::map<Node*, std::string> linkmap;
nuclear@75 96
nuclear@75 97 elem = root->FirstChildElement("node");
nuclear@75 98 while(elem) {
nuclear@75 99 Node *node = read_node(this, elem, linkmap);
nuclear@75 100 if(node) {
nuclear@75 101 add_node(node);
nuclear@75 102 }
nuclear@75 103 elem = elem->NextSiblingElement("node");
nuclear@75 104 }
nuclear@75 105
nuclear@75 106 // link up all the nodes in the hierarchy
nuclear@75 107 for(size_t i=0; i<nodes.size(); i++) {
nuclear@75 108 std::string parent_name = linkmap[nodes[i]];
nuclear@75 109 if(!parent_name.empty()) {
nuclear@75 110 Node *parent = get_node(parent_name.c_str());
nuclear@75 111 if(parent) {
nuclear@75 112 parent->add_child(nodes[i]);
nuclear@75 113 }
nuclear@75 114 }
nuclear@75 115 }
nuclear@75 116
nuclear@45 117 delete [] buf;
nuclear@45 118 return true;
nuclear@19 119 }
nuclear@19 120
nuclear@51 121 bool Scene::load_anim_xml(goat3d_io *io)
nuclear@51 122 {
nuclear@51 123 long bytes = io->seek(0, SEEK_END, io->cls);
nuclear@51 124 io->seek(0, SEEK_SET, io->cls);
nuclear@51 125
nuclear@51 126 char *buf = new char[bytes];
nuclear@51 127 if(io->read(buf, bytes, io->cls) < bytes) {
nuclear@51 128 logmsg(LOG_ERROR, "failed to read XML animation file\n");
nuclear@51 129 delete [] buf;
nuclear@51 130 return false;
nuclear@51 131 }
nuclear@51 132
nuclear@51 133 XMLDocument xml;
nuclear@51 134 XMLError err = xml.Parse(buf, bytes);
nuclear@51 135 if(err) {
nuclear@51 136 logmsg(LOG_ERROR, "failed to parse XML animation file: %s\n%s\n", xml.GetErrorStr1(),
nuclear@51 137 xml.GetErrorStr2());
nuclear@51 138 delete [] buf;
nuclear@51 139 return false;
nuclear@51 140 }
nuclear@51 141
nuclear@51 142 XMLElement *root = xml.RootElement();
nuclear@51 143 if(strcmp(root->Name(), "anim") != 0) {
nuclear@95 144 logmsg(LOG_ERROR, "%s: root node is not <anim>\n", __FUNCTION__);
nuclear@51 145 delete [] buf;
nuclear@51 146 return false;
nuclear@51 147 }
nuclear@51 148
nuclear@95 149 int anim_idx = -1;
nuclear@95 150 XMLElement *elem = root->FirstChildElement();
nuclear@51 151 while(elem) {
nuclear@51 152 const char *elem_name = elem->Name();
nuclear@51 153
nuclear@95 154 if(strcmp(elem_name, "track") != 0) {
nuclear@95 155 logmsg(LOG_ERROR, "%s: only <track>s allowed in <anim>\n", __FUNCTION__);
nuclear@95 156 delete [] buf;
nuclear@95 157 return false;
nuclear@95 158 }
nuclear@95 159
nuclear@95 160 if(!read_track(this, elem, &anim_idx)) {
nuclear@95 161 delete [] buf;
nuclear@95 162 return false;
nuclear@51 163 }
nuclear@51 164 elem = elem->NextSiblingElement();
nuclear@51 165 }
nuclear@51 166
nuclear@95 167 if(anim_idx == -1) {
nuclear@95 168 logmsg(LOG_INFO, "%s: WARNING animation affected 0 nodes\n", __FUNCTION__);
nuclear@95 169 }
nuclear@95 170
nuclear@51 171 delete [] buf;
nuclear@51 172 return true;
nuclear@51 173 }
nuclear@19 174
nuclear@95 175 static bool read_track(Scene *scn, XMLElement *xml_track, int *anim_idx)
nuclear@95 176 {
nuclear@95 177 Node *node = 0;
nuclear@95 178 int type = -1;
nuclear@95 179 std::vector<Key> keys;
nuclear@95 180
nuclear@95 181 XMLElement *elem = xml_track->FirstChildElement();
nuclear@95 182 while(elem) {
nuclear@95 183 const char *elem_name = elem->Name();
nuclear@95 184
nuclear@95 185 if(strcmp(elem_name, "node") == 0) {
nuclear@95 186 const char *name = elem->Attribute("string");
nuclear@95 187 if(!name || !(node = scn->get_node(name))) {
nuclear@95 188 logmsg(LOG_ERROR, "%s: invalid track node: %s\n", __FUNCTION__, name);
nuclear@95 189 return false;
nuclear@95 190 }
nuclear@95 191
nuclear@95 192 } else if(strcmp(elem_name, "attr") == 0) {
nuclear@95 193 const char *str = elem->Attribute("string");
nuclear@95 194 if(str && strcmp(str, "position") == 0) {
nuclear@95 195 type = XFormNode::POSITION_TRACK;
nuclear@95 196 } else if(str && strcmp(str, "rotation") == 0) {
nuclear@95 197 type = XFormNode::ROTATION_TRACK;
nuclear@95 198 } else if(str && strcmp(str, "scaling") == 0) {
nuclear@95 199 type = XFormNode::SCALING_TRACK;
nuclear@95 200 } else {
nuclear@95 201 logmsg(LOG_ERROR, "%s: invalid track attribute specifier: %s\n", __FUNCTION__, str);
nuclear@95 202 return false;
nuclear@95 203 }
nuclear@95 204
nuclear@95 205 } else if(strcmp(elem_name, "key") == 0) {
nuclear@95 206 Key key = read_key(elem);
nuclear@95 207 if(key.tm == LONG_MIN) {
nuclear@95 208 return false; // logging in read_key
nuclear@95 209 }
nuclear@95 210 keys.push_back(key);
nuclear@95 211
nuclear@95 212 } else {
nuclear@95 213 logmsg(LOG_ERROR, "%s: unexpected element <%s> in <track>\n", __FUNCTION__, elem_name);
nuclear@95 214 return false;
nuclear@95 215 }
nuclear@95 216 elem = elem->NextSiblingElement();
nuclear@95 217 }
nuclear@95 218
nuclear@95 219 if(!node) {
nuclear@95 220 logmsg(LOG_ERROR, "%s: invalid track, missing node reference\n", __FUNCTION__);
nuclear@95 221 return false;
nuclear@95 222 }
nuclear@95 223 if(type == -1) {
nuclear@95 224 logmsg(LOG_ERROR, "%s: invalid track, missing attribute specifier\n", __FUNCTION__);
nuclear@95 225 return false;
nuclear@95 226 }
nuclear@95 227
nuclear@95 228 if(*anim_idx == -1) {
nuclear@95 229 // this is the first node we encounter. add a new animation and activate it
nuclear@95 230 XFormNode *root = node->get_root();
nuclear@95 231 *anim_idx = root->get_animation_count() + 1;
nuclear@95 232
nuclear@95 233 char name[64];
nuclear@95 234 sprintf(name, "anim%03d\n", *anim_idx);
nuclear@95 235 root->add_animation(name);
nuclear@95 236 root->use_animation(*anim_idx);
nuclear@95 237 } else {
nuclear@95 238 // make sure this node hierarchy already has this animation, otherwise add it
nuclear@95 239 XFormNode *root = node->get_root();
nuclear@95 240 if(root->get_active_animation_index() != *anim_idx) {
nuclear@95 241 char name[64];
nuclear@95 242 sprintf(name, "anim%03d\n", *anim_idx);
nuclear@95 243 root->add_animation(name);
nuclear@95 244 root->use_animation(*anim_idx);
nuclear@95 245 }
nuclear@95 246 }
nuclear@95 247
nuclear@96 248 for(size_t i=0; i<keys.size(); i++) {
nuclear@95 249 switch(type) {
nuclear@95 250 case XFormNode::POSITION_TRACK:
nuclear@96 251 node->set_position(Vector3(keys[i].val.x, keys[i].val.y, keys[i].val.z), keys[i].tm);
nuclear@95 252 break;
nuclear@95 253 case XFormNode::ROTATION_TRACK:
nuclear@96 254 node->set_rotation(Quaternion(keys[i].val.w, keys[i].val.x, keys[i].val.y, keys[i].val.z), keys[i].tm);
nuclear@95 255 break;
nuclear@95 256 case XFormNode::SCALING_TRACK:
nuclear@96 257 node->set_scaling(Vector3(keys[i].val.x, keys[i].val.y, keys[i].val.z), keys[i].tm);
nuclear@95 258 }
nuclear@95 259 }
nuclear@95 260 return true;
nuclear@95 261 }
nuclear@95 262
nuclear@95 263 static Key read_key(XMLElement *xml_key)
nuclear@95 264 {
nuclear@95 265 Key key;
nuclear@95 266 key.tm = LONG_MIN; // initialize to invalid time
nuclear@95 267
nuclear@95 268 XMLElement *xml_time = xml_key->FirstChildElement("time");
nuclear@95 269 XMLElement *xml_value = xml_key->FirstChildElement("value");
nuclear@95 270
nuclear@95 271 if(!xml_time || !xml_value) {
nuclear@95 272 logmsg(LOG_ERROR, "%s: invalid key, missing either <time> or <value> elements\n", __FUNCTION__);
nuclear@95 273 return key;
nuclear@95 274 }
nuclear@95 275
nuclear@95 276 int ival;
nuclear@95 277 if(xml_time->QueryIntAttribute("int", &ival) != XML_NO_ERROR) {
nuclear@95 278 logmsg(LOG_ERROR, "%s: invalid time element in <key>\n", __FUNCTION__);
nuclear@95 279 return key;
nuclear@95 280 }
nuclear@95 281
nuclear@95 282 const char *vstr;
nuclear@95 283 if((vstr = xml_value->Attribute("float3"))) {
nuclear@95 284 if(sscanf(vstr, "%f %f %f", &key.val.x, &key.val.y, &key.val.z) != 3) {
nuclear@95 285 logmsg(LOG_ERROR, "%s: invalid float3 value element in <key>: %s\n", __FUNCTION__, vstr);
nuclear@95 286 return key;
nuclear@95 287 }
nuclear@95 288 } else if((vstr = xml_value->Attribute("float4"))) {
nuclear@95 289 if(sscanf(vstr, "%f %f %f %f", &key.val.x, &key.val.y, &key.val.z, &key.val.w) != 4) {
nuclear@95 290 logmsg(LOG_ERROR, "%s: invalid float4 value element in <key>: %s\n", __FUNCTION__, vstr);
nuclear@95 291 return key;
nuclear@95 292 }
nuclear@95 293 } else {
nuclear@95 294 logmsg(LOG_ERROR, "%s: invalid value element in <key>: missing float3 or float4 attributes\n", __FUNCTION__);
nuclear@95 295 return key;
nuclear@95 296 }
nuclear@95 297
nuclear@95 298 key.tm = ival;
nuclear@95 299 return key;
nuclear@95 300 }
nuclear@95 301
nuclear@19 302 static Material *read_material(Scene *scn, XMLElement *xml_mtl)
nuclear@19 303 {
nuclear@19 304 Material *mtl = new Material;
nuclear@75 305 mtl->name = get_name(xml_mtl, scn->get_material_count(), "material");
nuclear@19 306
nuclear@19 307 // get all the material attributes in turn
nuclear@19 308 XMLElement *elem = xml_mtl->FirstChildElement("attr");
nuclear@19 309 while(elem) {
nuclear@19 310 MaterialAttrib attr;
nuclear@19 311 const char *name = read_material_attrib(&attr, elem);
nuclear@19 312 if(name) {
nuclear@19 313 (*mtl)[name] = attr;
nuclear@19 314 }
nuclear@19 315
nuclear@19 316 elem = elem->NextSiblingElement("attr");
nuclear@19 317 }
nuclear@19 318
nuclear@19 319 return mtl;
nuclear@19 320 }
nuclear@19 321
nuclear@19 322 static const char *read_material_attrib(MaterialAttrib *attr, XMLElement *xml_attr)
nuclear@19 323 {
nuclear@94 324 const char *name = 0;
nuclear@19 325
nuclear@19 326 XMLElement *elem;
nuclear@19 327 if((elem = xml_attr->FirstChildElement("name"))) {
nuclear@19 328 if(!(name = elem->Attribute("string"))) {
nuclear@19 329 return 0;
nuclear@19 330 }
nuclear@19 331 }
nuclear@19 332
nuclear@19 333 if((elem = xml_attr->FirstChildElement("val"))) {
nuclear@19 334 if(elem->QueryFloatAttribute("float", &attr->value.x) != XML_NO_ERROR) {
nuclear@19 335 // try a float3
nuclear@19 336 const char *valstr = elem->Attribute("float3");
nuclear@19 337 if(!valstr || sscanf(valstr, "%f %f %f", &attr->value.x, &attr->value.y,
nuclear@19 338 &attr->value.z) != 3) {
nuclear@19 339 // try a float4
nuclear@19 340 valstr = elem->Attribute("float4");
nuclear@19 341 if(!valstr || sscanf(valstr, "%f %f %f %f", &attr->value.x, &attr->value.y,
nuclear@19 342 &attr->value.z, &attr->value.w) != 4) {
nuclear@19 343 // no valid val attribute found
nuclear@19 344 return 0;
nuclear@19 345 }
nuclear@19 346 }
nuclear@19 347 }
nuclear@19 348 }
nuclear@19 349
nuclear@19 350 if((elem = xml_attr->FirstChildElement("map"))) {
nuclear@19 351 const char *tex = elem->Attribute("string");
nuclear@19 352 if(tex) {
nuclear@19 353 attr->map = std::string(tex);
nuclear@19 354 }
nuclear@19 355 }
nuclear@19 356
nuclear@19 357 return name;
nuclear@19 358 }
nuclear@19 359
nuclear@19 360 static Mesh *read_mesh(Scene *scn, XMLElement *xml_mesh)
nuclear@19 361 {
nuclear@19 362 Mesh *mesh = new Mesh;
nuclear@75 363 mesh->name = get_name(xml_mesh, scn->get_mesh_count(), "mesh");
nuclear@19 364
nuclear@19 365 XMLElement *elem;
nuclear@19 366 if((elem = xml_mesh->FirstChildElement("material"))) {
nuclear@19 367 int idx;
nuclear@19 368 if(elem->QueryIntAttribute("int", &idx) == XML_NO_ERROR) {
nuclear@19 369 mesh->material = scn->get_material(idx);
nuclear@19 370 } else {
nuclear@19 371 // try string
nuclear@19 372 const char *mtlstr = elem->Attribute("string");
nuclear@19 373 if(mtlstr) {
nuclear@19 374 mesh->material = scn->get_material(mtlstr);
nuclear@19 375 }
nuclear@19 376 }
nuclear@19 377 }
nuclear@19 378
nuclear@19 379 /* reading mesh data from XML is not supported, only MESH_FILE can be used to
nuclear@19 380 * specify an external mesh file to be loaded
nuclear@19 381 */
nuclear@19 382
nuclear@19 383 if((elem = xml_mesh->FirstChildElement("file"))) {
nuclear@19 384 const char *fname = elem->Attribute("string");
nuclear@19 385 if(fname) {
nuclear@74 386 char *path = (char*)fname;
nuclear@74 387 if(scn->goat->search_path) {
nuclear@74 388 path = (char*)alloca(strlen(fname) + strlen(scn->goat->search_path) + 2);
nuclear@74 389 sprintf(path, "%s/%s", scn->goat->search_path, fname);
nuclear@74 390 }
nuclear@74 391 if(!mesh->load(path)) {
nuclear@19 392 delete mesh;
nuclear@19 393 return 0;
nuclear@19 394 }
nuclear@19 395 }
nuclear@19 396 }
nuclear@19 397
nuclear@19 398 return mesh;
nuclear@19 399 }
nuclear@19 400
nuclear@75 401 static Node *read_node(Scene *scn, XMLElement *xml_node, std::map<Node*, std::string> &linkmap)
nuclear@75 402 {
nuclear@75 403 Node *node = new Node;
nuclear@75 404 node->set_name(get_name(xml_node, scn->get_node_count(), "node").c_str());
nuclear@75 405
nuclear@75 406 XMLElement *elem;
nuclear@75 407 if((elem = xml_node->FirstChildElement("parent"))) {
nuclear@75 408 const char *pname = elem->Attribute("string");
nuclear@75 409 if(pname) {
nuclear@75 410 linkmap[node] = pname;
nuclear@75 411 }
nuclear@75 412 }
nuclear@75 413
nuclear@75 414 if((elem = xml_node->FirstChildElement("mesh"))) {
nuclear@75 415 Mesh *mesh = scn->get_mesh(elem->Attribute("string"));
nuclear@75 416 if(mesh) {
nuclear@75 417 node->set_object(mesh);
nuclear@75 418 }
nuclear@75 419 } else if((elem = xml_node->FirstChildElement("light"))) {
nuclear@75 420 Light *lt = scn->get_light(elem->Attribute("string"));
nuclear@75 421 if(lt) {
nuclear@75 422 node->set_object(lt);
nuclear@75 423 }
nuclear@75 424 } else if((elem = xml_node->FirstChildElement("camera"))) {
nuclear@75 425 Camera *cam = scn->get_camera(elem->Attribute("string"));
nuclear@75 426 if(cam) {
nuclear@75 427 node->set_object(cam);
nuclear@75 428 }
nuclear@75 429 }
nuclear@75 430
nuclear@75 431 float vec[4];
nuclear@75 432 if((elem = xml_node->FirstChildElement("pos"))) {
nuclear@75 433 const char *val = elem->Attribute("float3");
nuclear@75 434 if(val && sscanf(val, "%f %f %f", vec, vec + 1, vec + 2) == 3) {
nuclear@83 435 node->set_position(Vector3(vec[0], vec[1], vec[2]));
nuclear@75 436 } else {
nuclear@75 437 logmsg(LOG_ERROR, "node %s: invalid position tag\n", node->get_name());
nuclear@75 438 }
nuclear@75 439 }
nuclear@75 440 if((elem = xml_node->FirstChildElement("rot"))) {
nuclear@75 441 const char *val = elem->Attribute("float4");
nuclear@75 442 if(val && sscanf(val, "%f %f %f %f", vec, vec + 1, vec + 2, vec + 3) == 4) {
nuclear@75 443 node->set_rotation(Quaternion(vec[3], Vector3(vec[0], vec[1], vec[2])));
nuclear@75 444 } else {
nuclear@75 445 logmsg(LOG_ERROR, "node %s: invalid rotation tag\n", node->get_name());
nuclear@75 446 }
nuclear@75 447 }
nuclear@75 448 if((elem = xml_node->FirstChildElement("scale"))) {
nuclear@75 449 const char *val = elem->Attribute("float3");
nuclear@75 450 if(val && sscanf(val, "%f %f %f", vec, vec + 1, vec + 2) == 3) {
nuclear@75 451 node->set_scaling(Vector3(vec[0], vec[1], vec[2]));
nuclear@75 452 } else {
nuclear@75 453 logmsg(LOG_ERROR, "node %s: invalid scaling tag\n", node->get_name());
nuclear@75 454 }
nuclear@75 455 }
nuclear@75 456 if((elem = xml_node->FirstChildElement("pivot"))) {
nuclear@75 457 const char *val = elem->Attribute("float3");
nuclear@75 458 if(val && sscanf(val, "%f %f %f", vec, vec + 1, vec + 2) == 3) {
nuclear@75 459 node->set_pivot(Vector3(vec[0], vec[1], vec[2]));
nuclear@75 460 } else {
nuclear@75 461 logmsg(LOG_ERROR, "node %s: invalid pivot tag\n", node->get_name());
nuclear@75 462 }
nuclear@75 463 }
nuclear@75 464
nuclear@75 465 return node;
nuclear@75 466 }
nuclear@75 467
nuclear@75 468 static std::string get_name(XMLElement *node, int idx, const char *def_prefix)
nuclear@19 469 {
nuclear@19 470 char buf[64];
nuclear@19 471 const char *name = 0;
nuclear@19 472
nuclear@19 473 XMLElement *elem;
nuclear@19 474 if((elem = node->FirstChildElement("name"))) {
nuclear@19 475 name = elem->Attribute("string");
nuclear@19 476 }
nuclear@19 477
nuclear@19 478 if(!name) {
nuclear@75 479 sprintf(buf, "%s%04d", def_prefix, idx);
nuclear@19 480 name = buf;
nuclear@19 481 }
nuclear@19 482
nuclear@19 483 return std::string(name);
nuclear@19 484 }