nuclear@54: /* nuclear@54: goat3d - 3D scene, character, and animation file format library. nuclear@54: Copyright (C) 2013-2014 John Tsiombikas nuclear@54: nuclear@54: This program is free software: you can redistribute it and/or modify nuclear@54: it under the terms of the GNU Lesser General Public License as published by nuclear@54: the Free Software Foundation, either version 3 of the License, or nuclear@54: (at your option) any later version. nuclear@54: nuclear@54: This program is distributed in the hope that it will be useful, nuclear@54: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@54: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@54: GNU Lesser General Public License for more details. nuclear@54: nuclear@54: You should have received a copy of the GNU Lesser General Public License nuclear@54: along with this program. If not, see . nuclear@54: */ nuclear@19: #include nuclear@19: #include "goat3d.h" nuclear@19: #include "goat3d_impl.h" nuclear@19: #include "tinyxml2.h" nuclear@19: #include "log.h" nuclear@19: nuclear@47: using namespace g3dimpl; nuclear@19: using namespace tinyxml2; nuclear@19: nuclear@19: static Material *read_material(Scene *scn, XMLElement *xml_mtl); nuclear@19: static const char *read_material_attrib(MaterialAttrib *attr, XMLElement *xml_attr); nuclear@19: static Mesh *read_mesh(Scene *scn, XMLElement *xml_mesh); nuclear@19: static std::string get_name(XMLElement *node, int idx); nuclear@19: nuclear@19: bool Scene::loadxml(goat3d_io *io) nuclear@19: { nuclear@19: long bytes = io->seek(0, SEEK_END, io->cls); nuclear@19: io->seek(0, SEEK_SET, io->cls); nuclear@19: nuclear@19: char *buf = new char[bytes]; nuclear@19: if(io->read(buf, bytes, io->cls) < bytes) { nuclear@19: logmsg(LOG_ERROR, "failed to read XML scene file\n"); nuclear@45: delete [] buf; nuclear@19: return false; nuclear@19: } nuclear@19: nuclear@19: XMLDocument xml; nuclear@19: XMLError err = xml.Parse(buf, bytes); nuclear@19: if(err) { nuclear@19: logmsg(LOG_ERROR, "failed to parse XML scene file: %s\n%s\n", xml.GetErrorStr1(), nuclear@19: xml.GetErrorStr2()); nuclear@45: delete [] buf; nuclear@19: return false; nuclear@19: } nuclear@19: nuclear@19: XMLElement *root = xml.RootElement(); nuclear@19: if(strcmp(root->Name(), "scene") != 0) { nuclear@19: logmsg(LOG_ERROR, "invalid XML file, root node is not \n"); nuclear@45: delete [] buf; nuclear@19: return false; nuclear@19: } nuclear@19: nuclear@19: XMLElement *elem; nuclear@19: nuclear@19: // get all materials nuclear@19: elem = root->FirstChildElement("mtl"); nuclear@19: while(elem) { nuclear@19: Material *mtl = read_material(this, elem); nuclear@19: if(mtl) { nuclear@19: add_material(mtl); nuclear@19: } nuclear@19: elem = elem->NextSiblingElement("mtl"); nuclear@19: } nuclear@19: nuclear@19: // get all meshes nuclear@19: elem = root->FirstChildElement("mesh"); nuclear@19: while(elem) { nuclear@19: Mesh *mesh = read_mesh(this, elem); nuclear@19: if(mesh) { nuclear@19: add_mesh(mesh); nuclear@19: } nuclear@19: elem = elem->NextSiblingElement("mesh"); nuclear@19: } nuclear@19: nuclear@45: delete [] buf; nuclear@45: return true; nuclear@19: } nuclear@19: nuclear@51: bool Scene::load_anim_xml(goat3d_io *io) nuclear@51: { nuclear@51: long bytes = io->seek(0, SEEK_END, io->cls); nuclear@51: io->seek(0, SEEK_SET, io->cls); nuclear@51: nuclear@51: char *buf = new char[bytes]; nuclear@51: if(io->read(buf, bytes, io->cls) < bytes) { nuclear@51: logmsg(LOG_ERROR, "failed to read XML animation file\n"); nuclear@51: delete [] buf; nuclear@51: return false; nuclear@51: } nuclear@51: nuclear@51: XMLDocument xml; nuclear@51: XMLError err = xml.Parse(buf, bytes); nuclear@51: if(err) { nuclear@51: logmsg(LOG_ERROR, "failed to parse XML animation file: %s\n%s\n", xml.GetErrorStr1(), nuclear@51: xml.GetErrorStr2()); nuclear@51: delete [] buf; nuclear@51: return false; nuclear@51: } nuclear@51: nuclear@51: XMLElement *root = xml.RootElement(); nuclear@51: if(strcmp(root->Name(), "anim") != 0) { nuclear@51: logmsg(LOG_ERROR, "invalid XML file, root node is not \n"); nuclear@51: delete [] buf; nuclear@51: return false; nuclear@51: } nuclear@51: nuclear@51: XMLElement *elem; nuclear@51: nuclear@51: elem = root->FirstChildElement(); nuclear@51: while(elem) { nuclear@51: const char *elem_name = elem->Name(); nuclear@51: nuclear@51: if(strcmp(elem_name, "name") == 0) { nuclear@51: } else if(strcmp(elem_name, "attr") == 0) { nuclear@51: } nuclear@51: elem = elem->NextSiblingElement(); nuclear@51: } nuclear@51: nuclear@51: delete [] buf; nuclear@51: return true; nuclear@51: } nuclear@19: nuclear@19: static Material *read_material(Scene *scn, XMLElement *xml_mtl) nuclear@19: { nuclear@19: Material *mtl = new Material; nuclear@19: mtl->name = get_name(xml_mtl, scn->get_material_count()); nuclear@19: nuclear@19: // get all the material attributes in turn nuclear@19: XMLElement *elem = xml_mtl->FirstChildElement("attr"); nuclear@19: while(elem) { nuclear@19: MaterialAttrib attr; nuclear@19: const char *name = read_material_attrib(&attr, elem); nuclear@19: if(name) { nuclear@19: (*mtl)[name] = attr; nuclear@19: } nuclear@19: nuclear@19: elem = elem->NextSiblingElement("attr"); nuclear@19: } nuclear@19: nuclear@19: return mtl; nuclear@19: } nuclear@19: nuclear@19: static const char *read_material_attrib(MaterialAttrib *attr, XMLElement *xml_attr) nuclear@19: { nuclear@19: const char *name; nuclear@19: nuclear@19: XMLElement *elem; nuclear@19: if((elem = xml_attr->FirstChildElement("name"))) { nuclear@19: if(!(name = elem->Attribute("string"))) { nuclear@19: return 0; nuclear@19: } nuclear@19: } nuclear@19: nuclear@19: if((elem = xml_attr->FirstChildElement("val"))) { nuclear@19: if(elem->QueryFloatAttribute("float", &attr->value.x) != XML_NO_ERROR) { nuclear@19: // try a float3 nuclear@19: const char *valstr = elem->Attribute("float3"); nuclear@19: if(!valstr || sscanf(valstr, "%f %f %f", &attr->value.x, &attr->value.y, nuclear@19: &attr->value.z) != 3) { nuclear@19: // try a float4 nuclear@19: valstr = elem->Attribute("float4"); nuclear@19: if(!valstr || sscanf(valstr, "%f %f %f %f", &attr->value.x, &attr->value.y, nuclear@19: &attr->value.z, &attr->value.w) != 4) { nuclear@19: // no valid val attribute found nuclear@19: return 0; nuclear@19: } nuclear@19: } nuclear@19: } nuclear@19: } nuclear@19: nuclear@19: if((elem = xml_attr->FirstChildElement("map"))) { nuclear@19: const char *tex = elem->Attribute("string"); nuclear@19: if(tex) { nuclear@19: attr->map = std::string(tex); nuclear@19: } nuclear@19: } nuclear@19: nuclear@19: return name; nuclear@19: } nuclear@19: nuclear@19: static Mesh *read_mesh(Scene *scn, XMLElement *xml_mesh) nuclear@19: { nuclear@19: Mesh *mesh = new Mesh; nuclear@19: mesh->name = get_name(xml_mesh, scn->get_mesh_count()); nuclear@19: nuclear@19: XMLElement *elem; nuclear@19: if((elem = xml_mesh->FirstChildElement("material"))) { nuclear@19: int idx; nuclear@19: if(elem->QueryIntAttribute("int", &idx) == XML_NO_ERROR) { nuclear@19: mesh->material = scn->get_material(idx); nuclear@19: } else { nuclear@19: // try string nuclear@19: const char *mtlstr = elem->Attribute("string"); nuclear@19: if(mtlstr) { nuclear@19: mesh->material = scn->get_material(mtlstr); nuclear@19: } nuclear@19: } nuclear@19: } nuclear@19: nuclear@19: /* reading mesh data from XML is not supported, only MESH_FILE can be used to nuclear@19: * specify an external mesh file to be loaded nuclear@19: */ nuclear@19: nuclear@19: if((elem = xml_mesh->FirstChildElement("file"))) { nuclear@19: const char *fname = elem->Attribute("string"); nuclear@19: if(fname) { nuclear@19: if(!mesh->load(fname)) { nuclear@19: delete mesh; nuclear@19: return 0; nuclear@19: } nuclear@19: } nuclear@19: } nuclear@19: nuclear@19: return mesh; nuclear@19: } nuclear@19: nuclear@19: static std::string get_name(XMLElement *node, int idx) nuclear@19: { nuclear@19: char buf[64]; nuclear@19: const char *name = 0; nuclear@19: nuclear@19: XMLElement *elem; nuclear@19: if((elem = node->FirstChildElement("name"))) { nuclear@19: name = elem->Attribute("string"); nuclear@19: } nuclear@19: nuclear@19: if(!name) { nuclear@19: sprintf(buf, "mesh%04d", idx); nuclear@19: name = buf; nuclear@19: } nuclear@19: nuclear@19: return std::string(name); nuclear@19: }