goat3d

annotate src/goat3d_readxml.cc @ 88:7941e89798e5

selections
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 15 May 2014 06:52:01 +0300
parents 76dea247f75c
children 21319e71117f
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@19 21 #include "goat3d.h"
nuclear@19 22 #include "goat3d_impl.h"
nuclear@19 23 #include "tinyxml2.h"
nuclear@19 24 #include "log.h"
nuclear@19 25
nuclear@47 26 using namespace g3dimpl;
nuclear@19 27 using namespace tinyxml2;
nuclear@19 28
nuclear@19 29 static Material *read_material(Scene *scn, XMLElement *xml_mtl);
nuclear@19 30 static const char *read_material_attrib(MaterialAttrib *attr, XMLElement *xml_attr);
nuclear@19 31 static Mesh *read_mesh(Scene *scn, XMLElement *xml_mesh);
nuclear@75 32 static Node *read_node(Scene *scn, XMLElement *xml_node, std::map<Node*, std::string> &linkmap);
nuclear@75 33 static std::string get_name(XMLElement *node, int idx, const char *def_prefix);
nuclear@19 34
nuclear@19 35 bool Scene::loadxml(goat3d_io *io)
nuclear@19 36 {
nuclear@19 37 long bytes = io->seek(0, SEEK_END, io->cls);
nuclear@19 38 io->seek(0, SEEK_SET, io->cls);
nuclear@19 39
nuclear@19 40 char *buf = new char[bytes];
nuclear@19 41 if(io->read(buf, bytes, io->cls) < bytes) {
nuclear@19 42 logmsg(LOG_ERROR, "failed to read XML scene file\n");
nuclear@45 43 delete [] buf;
nuclear@19 44 return false;
nuclear@19 45 }
nuclear@19 46
nuclear@19 47 XMLDocument xml;
nuclear@19 48 XMLError err = xml.Parse(buf, bytes);
nuclear@19 49 if(err) {
nuclear@19 50 logmsg(LOG_ERROR, "failed to parse XML scene file: %s\n%s\n", xml.GetErrorStr1(),
nuclear@19 51 xml.GetErrorStr2());
nuclear@45 52 delete [] buf;
nuclear@19 53 return false;
nuclear@19 54 }
nuclear@19 55
nuclear@19 56 XMLElement *root = xml.RootElement();
nuclear@19 57 if(strcmp(root->Name(), "scene") != 0) {
nuclear@19 58 logmsg(LOG_ERROR, "invalid XML file, root node is not <scene>\n");
nuclear@45 59 delete [] buf;
nuclear@19 60 return false;
nuclear@19 61 }
nuclear@19 62
nuclear@19 63 XMLElement *elem;
nuclear@19 64
nuclear@19 65 // get all materials
nuclear@19 66 elem = root->FirstChildElement("mtl");
nuclear@19 67 while(elem) {
nuclear@19 68 Material *mtl = read_material(this, elem);
nuclear@19 69 if(mtl) {
nuclear@19 70 add_material(mtl);
nuclear@19 71 }
nuclear@19 72 elem = elem->NextSiblingElement("mtl");
nuclear@19 73 }
nuclear@19 74
nuclear@19 75 // get all meshes
nuclear@19 76 elem = root->FirstChildElement("mesh");
nuclear@19 77 while(elem) {
nuclear@19 78 Mesh *mesh = read_mesh(this, elem);
nuclear@19 79 if(mesh) {
nuclear@19 80 add_mesh(mesh);
nuclear@19 81 }
nuclear@19 82 elem = elem->NextSiblingElement("mesh");
nuclear@19 83 }
nuclear@19 84
nuclear@75 85 // get all nodes
nuclear@75 86 std::map<Node*, std::string> linkmap;
nuclear@75 87
nuclear@75 88 elem = root->FirstChildElement("node");
nuclear@75 89 while(elem) {
nuclear@75 90 Node *node = read_node(this, elem, linkmap);
nuclear@75 91 if(node) {
nuclear@75 92 add_node(node);
nuclear@75 93 }
nuclear@75 94 elem = elem->NextSiblingElement("node");
nuclear@75 95 }
nuclear@75 96
nuclear@75 97 // link up all the nodes in the hierarchy
nuclear@75 98 for(size_t i=0; i<nodes.size(); i++) {
nuclear@75 99 std::string parent_name = linkmap[nodes[i]];
nuclear@75 100 if(!parent_name.empty()) {
nuclear@75 101 Node *parent = get_node(parent_name.c_str());
nuclear@75 102 if(parent) {
nuclear@75 103 parent->add_child(nodes[i]);
nuclear@75 104 }
nuclear@75 105 }
nuclear@75 106 }
nuclear@75 107
nuclear@45 108 delete [] buf;
nuclear@45 109 return true;
nuclear@19 110 }
nuclear@19 111
nuclear@51 112 bool Scene::load_anim_xml(goat3d_io *io)
nuclear@51 113 {
nuclear@51 114 long bytes = io->seek(0, SEEK_END, io->cls);
nuclear@51 115 io->seek(0, SEEK_SET, io->cls);
nuclear@51 116
nuclear@51 117 char *buf = new char[bytes];
nuclear@51 118 if(io->read(buf, bytes, io->cls) < bytes) {
nuclear@51 119 logmsg(LOG_ERROR, "failed to read XML animation file\n");
nuclear@51 120 delete [] buf;
nuclear@51 121 return false;
nuclear@51 122 }
nuclear@51 123
nuclear@51 124 XMLDocument xml;
nuclear@51 125 XMLError err = xml.Parse(buf, bytes);
nuclear@51 126 if(err) {
nuclear@51 127 logmsg(LOG_ERROR, "failed to parse XML animation file: %s\n%s\n", xml.GetErrorStr1(),
nuclear@51 128 xml.GetErrorStr2());
nuclear@51 129 delete [] buf;
nuclear@51 130 return false;
nuclear@51 131 }
nuclear@51 132
nuclear@51 133 XMLElement *root = xml.RootElement();
nuclear@51 134 if(strcmp(root->Name(), "anim") != 0) {
nuclear@51 135 logmsg(LOG_ERROR, "invalid XML file, root node is not <anim>\n");
nuclear@51 136 delete [] buf;
nuclear@51 137 return false;
nuclear@51 138 }
nuclear@51 139
nuclear@51 140 XMLElement *elem;
nuclear@51 141
nuclear@51 142 elem = root->FirstChildElement();
nuclear@51 143 while(elem) {
nuclear@51 144 const char *elem_name = elem->Name();
nuclear@51 145
nuclear@51 146 if(strcmp(elem_name, "name") == 0) {
nuclear@51 147 } else if(strcmp(elem_name, "attr") == 0) {
nuclear@51 148 }
nuclear@51 149 elem = elem->NextSiblingElement();
nuclear@51 150 }
nuclear@51 151
nuclear@51 152 delete [] buf;
nuclear@51 153 return true;
nuclear@51 154 }
nuclear@19 155
nuclear@19 156 static Material *read_material(Scene *scn, XMLElement *xml_mtl)
nuclear@19 157 {
nuclear@19 158 Material *mtl = new Material;
nuclear@75 159 mtl->name = get_name(xml_mtl, scn->get_material_count(), "material");
nuclear@19 160
nuclear@19 161 // get all the material attributes in turn
nuclear@19 162 XMLElement *elem = xml_mtl->FirstChildElement("attr");
nuclear@19 163 while(elem) {
nuclear@19 164 MaterialAttrib attr;
nuclear@19 165 const char *name = read_material_attrib(&attr, elem);
nuclear@19 166 if(name) {
nuclear@19 167 (*mtl)[name] = attr;
nuclear@19 168 }
nuclear@19 169
nuclear@19 170 elem = elem->NextSiblingElement("attr");
nuclear@19 171 }
nuclear@19 172
nuclear@19 173 return mtl;
nuclear@19 174 }
nuclear@19 175
nuclear@19 176 static const char *read_material_attrib(MaterialAttrib *attr, XMLElement *xml_attr)
nuclear@19 177 {
nuclear@19 178 const char *name;
nuclear@19 179
nuclear@19 180 XMLElement *elem;
nuclear@19 181 if((elem = xml_attr->FirstChildElement("name"))) {
nuclear@19 182 if(!(name = elem->Attribute("string"))) {
nuclear@19 183 return 0;
nuclear@19 184 }
nuclear@19 185 }
nuclear@19 186
nuclear@19 187 if((elem = xml_attr->FirstChildElement("val"))) {
nuclear@19 188 if(elem->QueryFloatAttribute("float", &attr->value.x) != XML_NO_ERROR) {
nuclear@19 189 // try a float3
nuclear@19 190 const char *valstr = elem->Attribute("float3");
nuclear@19 191 if(!valstr || sscanf(valstr, "%f %f %f", &attr->value.x, &attr->value.y,
nuclear@19 192 &attr->value.z) != 3) {
nuclear@19 193 // try a float4
nuclear@19 194 valstr = elem->Attribute("float4");
nuclear@19 195 if(!valstr || sscanf(valstr, "%f %f %f %f", &attr->value.x, &attr->value.y,
nuclear@19 196 &attr->value.z, &attr->value.w) != 4) {
nuclear@19 197 // no valid val attribute found
nuclear@19 198 return 0;
nuclear@19 199 }
nuclear@19 200 }
nuclear@19 201 }
nuclear@19 202 }
nuclear@19 203
nuclear@19 204 if((elem = xml_attr->FirstChildElement("map"))) {
nuclear@19 205 const char *tex = elem->Attribute("string");
nuclear@19 206 if(tex) {
nuclear@19 207 attr->map = std::string(tex);
nuclear@19 208 }
nuclear@19 209 }
nuclear@19 210
nuclear@19 211 return name;
nuclear@19 212 }
nuclear@19 213
nuclear@19 214 static Mesh *read_mesh(Scene *scn, XMLElement *xml_mesh)
nuclear@19 215 {
nuclear@19 216 Mesh *mesh = new Mesh;
nuclear@75 217 mesh->name = get_name(xml_mesh, scn->get_mesh_count(), "mesh");
nuclear@19 218
nuclear@19 219 XMLElement *elem;
nuclear@19 220 if((elem = xml_mesh->FirstChildElement("material"))) {
nuclear@19 221 int idx;
nuclear@19 222 if(elem->QueryIntAttribute("int", &idx) == XML_NO_ERROR) {
nuclear@19 223 mesh->material = scn->get_material(idx);
nuclear@19 224 } else {
nuclear@19 225 // try string
nuclear@19 226 const char *mtlstr = elem->Attribute("string");
nuclear@19 227 if(mtlstr) {
nuclear@19 228 mesh->material = scn->get_material(mtlstr);
nuclear@19 229 }
nuclear@19 230 }
nuclear@19 231 }
nuclear@19 232
nuclear@19 233 /* reading mesh data from XML is not supported, only MESH_FILE can be used to
nuclear@19 234 * specify an external mesh file to be loaded
nuclear@19 235 */
nuclear@19 236
nuclear@19 237 if((elem = xml_mesh->FirstChildElement("file"))) {
nuclear@19 238 const char *fname = elem->Attribute("string");
nuclear@19 239 if(fname) {
nuclear@74 240 char *path = (char*)fname;
nuclear@74 241 if(scn->goat->search_path) {
nuclear@74 242 path = (char*)alloca(strlen(fname) + strlen(scn->goat->search_path) + 2);
nuclear@74 243 sprintf(path, "%s/%s", scn->goat->search_path, fname);
nuclear@74 244 }
nuclear@74 245 if(!mesh->load(path)) {
nuclear@19 246 delete mesh;
nuclear@19 247 return 0;
nuclear@19 248 }
nuclear@19 249 }
nuclear@19 250 }
nuclear@19 251
nuclear@19 252 return mesh;
nuclear@19 253 }
nuclear@19 254
nuclear@75 255 static Node *read_node(Scene *scn, XMLElement *xml_node, std::map<Node*, std::string> &linkmap)
nuclear@75 256 {
nuclear@75 257 Node *node = new Node;
nuclear@75 258 node->set_name(get_name(xml_node, scn->get_node_count(), "node").c_str());
nuclear@75 259
nuclear@75 260 XMLElement *elem;
nuclear@75 261 if((elem = xml_node->FirstChildElement("parent"))) {
nuclear@75 262 const char *pname = elem->Attribute("string");
nuclear@75 263 if(pname) {
nuclear@75 264 linkmap[node] = pname;
nuclear@75 265 }
nuclear@75 266 }
nuclear@75 267
nuclear@75 268 if((elem = xml_node->FirstChildElement("mesh"))) {
nuclear@75 269 Mesh *mesh = scn->get_mesh(elem->Attribute("string"));
nuclear@75 270 if(mesh) {
nuclear@75 271 node->set_object(mesh);
nuclear@75 272 }
nuclear@75 273 } else if((elem = xml_node->FirstChildElement("light"))) {
nuclear@75 274 Light *lt = scn->get_light(elem->Attribute("string"));
nuclear@75 275 if(lt) {
nuclear@75 276 node->set_object(lt);
nuclear@75 277 }
nuclear@75 278 } else if((elem = xml_node->FirstChildElement("camera"))) {
nuclear@75 279 Camera *cam = scn->get_camera(elem->Attribute("string"));
nuclear@75 280 if(cam) {
nuclear@75 281 node->set_object(cam);
nuclear@75 282 }
nuclear@75 283 }
nuclear@75 284
nuclear@75 285 float vec[4];
nuclear@75 286 if((elem = xml_node->FirstChildElement("pos"))) {
nuclear@75 287 const char *val = elem->Attribute("float3");
nuclear@75 288 if(val && sscanf(val, "%f %f %f", vec, vec + 1, vec + 2) == 3) {
nuclear@83 289 node->set_position(Vector3(vec[0], vec[1], vec[2]));
nuclear@75 290 } else {
nuclear@75 291 logmsg(LOG_ERROR, "node %s: invalid position tag\n", node->get_name());
nuclear@75 292 }
nuclear@75 293 }
nuclear@75 294 if((elem = xml_node->FirstChildElement("rot"))) {
nuclear@75 295 const char *val = elem->Attribute("float4");
nuclear@75 296 if(val && sscanf(val, "%f %f %f %f", vec, vec + 1, vec + 2, vec + 3) == 4) {
nuclear@75 297 node->set_rotation(Quaternion(vec[3], Vector3(vec[0], vec[1], vec[2])));
nuclear@75 298 } else {
nuclear@75 299 logmsg(LOG_ERROR, "node %s: invalid rotation tag\n", node->get_name());
nuclear@75 300 }
nuclear@75 301 }
nuclear@75 302 if((elem = xml_node->FirstChildElement("scale"))) {
nuclear@75 303 const char *val = elem->Attribute("float3");
nuclear@75 304 if(val && sscanf(val, "%f %f %f", vec, vec + 1, vec + 2) == 3) {
nuclear@75 305 node->set_scaling(Vector3(vec[0], vec[1], vec[2]));
nuclear@75 306 } else {
nuclear@75 307 logmsg(LOG_ERROR, "node %s: invalid scaling tag\n", node->get_name());
nuclear@75 308 }
nuclear@75 309 }
nuclear@75 310 if((elem = xml_node->FirstChildElement("pivot"))) {
nuclear@75 311 const char *val = elem->Attribute("float3");
nuclear@75 312 if(val && sscanf(val, "%f %f %f", vec, vec + 1, vec + 2) == 3) {
nuclear@75 313 node->set_pivot(Vector3(vec[0], vec[1], vec[2]));
nuclear@75 314 } else {
nuclear@75 315 logmsg(LOG_ERROR, "node %s: invalid pivot tag\n", node->get_name());
nuclear@75 316 }
nuclear@75 317 }
nuclear@75 318
nuclear@75 319 return node;
nuclear@75 320 }
nuclear@75 321
nuclear@75 322 static std::string get_name(XMLElement *node, int idx, const char *def_prefix)
nuclear@19 323 {
nuclear@19 324 char buf[64];
nuclear@19 325 const char *name = 0;
nuclear@19 326
nuclear@19 327 XMLElement *elem;
nuclear@19 328 if((elem = node->FirstChildElement("name"))) {
nuclear@19 329 name = elem->Attribute("string");
nuclear@19 330 }
nuclear@19 331
nuclear@19 332 if(!name) {
nuclear@75 333 sprintf(buf, "%s%04d", def_prefix, idx);
nuclear@19 334 name = buf;
nuclear@19 335 }
nuclear@19 336
nuclear@19 337 return std::string(name);
nuclear@19 338 }