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@19
|
19 #include "goat3d.h"
|
nuclear@19
|
20 #include "goat3d_impl.h"
|
nuclear@19
|
21 #include "tinyxml2.h"
|
nuclear@19
|
22 #include "log.h"
|
nuclear@19
|
23
|
nuclear@47
|
24 using namespace g3dimpl;
|
nuclear@19
|
25 using namespace tinyxml2;
|
nuclear@19
|
26
|
nuclear@19
|
27 static Material *read_material(Scene *scn, XMLElement *xml_mtl);
|
nuclear@19
|
28 static const char *read_material_attrib(MaterialAttrib *attr, XMLElement *xml_attr);
|
nuclear@19
|
29 static Mesh *read_mesh(Scene *scn, XMLElement *xml_mesh);
|
nuclear@19
|
30 static std::string get_name(XMLElement *node, int idx);
|
nuclear@19
|
31
|
nuclear@19
|
32 bool Scene::loadxml(goat3d_io *io)
|
nuclear@19
|
33 {
|
nuclear@19
|
34 long bytes = io->seek(0, SEEK_END, io->cls);
|
nuclear@19
|
35 io->seek(0, SEEK_SET, io->cls);
|
nuclear@19
|
36
|
nuclear@19
|
37 char *buf = new char[bytes];
|
nuclear@19
|
38 if(io->read(buf, bytes, io->cls) < bytes) {
|
nuclear@19
|
39 logmsg(LOG_ERROR, "failed to read XML scene file\n");
|
nuclear@45
|
40 delete [] buf;
|
nuclear@19
|
41 return false;
|
nuclear@19
|
42 }
|
nuclear@19
|
43
|
nuclear@19
|
44 XMLDocument xml;
|
nuclear@19
|
45 XMLError err = xml.Parse(buf, bytes);
|
nuclear@19
|
46 if(err) {
|
nuclear@19
|
47 logmsg(LOG_ERROR, "failed to parse XML scene file: %s\n%s\n", xml.GetErrorStr1(),
|
nuclear@19
|
48 xml.GetErrorStr2());
|
nuclear@45
|
49 delete [] buf;
|
nuclear@19
|
50 return false;
|
nuclear@19
|
51 }
|
nuclear@19
|
52
|
nuclear@19
|
53 XMLElement *root = xml.RootElement();
|
nuclear@19
|
54 if(strcmp(root->Name(), "scene") != 0) {
|
nuclear@19
|
55 logmsg(LOG_ERROR, "invalid XML file, root node is not <scene>\n");
|
nuclear@45
|
56 delete [] buf;
|
nuclear@19
|
57 return false;
|
nuclear@19
|
58 }
|
nuclear@19
|
59
|
nuclear@19
|
60 XMLElement *elem;
|
nuclear@19
|
61
|
nuclear@19
|
62 // get all materials
|
nuclear@19
|
63 elem = root->FirstChildElement("mtl");
|
nuclear@19
|
64 while(elem) {
|
nuclear@19
|
65 Material *mtl = read_material(this, elem);
|
nuclear@19
|
66 if(mtl) {
|
nuclear@19
|
67 add_material(mtl);
|
nuclear@19
|
68 }
|
nuclear@19
|
69 elem = elem->NextSiblingElement("mtl");
|
nuclear@19
|
70 }
|
nuclear@19
|
71
|
nuclear@19
|
72 // get all meshes
|
nuclear@19
|
73 elem = root->FirstChildElement("mesh");
|
nuclear@19
|
74 while(elem) {
|
nuclear@19
|
75 Mesh *mesh = read_mesh(this, elem);
|
nuclear@19
|
76 if(mesh) {
|
nuclear@19
|
77 add_mesh(mesh);
|
nuclear@19
|
78 }
|
nuclear@19
|
79 elem = elem->NextSiblingElement("mesh");
|
nuclear@19
|
80 }
|
nuclear@19
|
81
|
nuclear@45
|
82 delete [] buf;
|
nuclear@45
|
83 return true;
|
nuclear@19
|
84 }
|
nuclear@19
|
85
|
nuclear@51
|
86 bool Scene::load_anim_xml(goat3d_io *io)
|
nuclear@51
|
87 {
|
nuclear@51
|
88 long bytes = io->seek(0, SEEK_END, io->cls);
|
nuclear@51
|
89 io->seek(0, SEEK_SET, io->cls);
|
nuclear@51
|
90
|
nuclear@51
|
91 char *buf = new char[bytes];
|
nuclear@51
|
92 if(io->read(buf, bytes, io->cls) < bytes) {
|
nuclear@51
|
93 logmsg(LOG_ERROR, "failed to read XML animation file\n");
|
nuclear@51
|
94 delete [] buf;
|
nuclear@51
|
95 return false;
|
nuclear@51
|
96 }
|
nuclear@51
|
97
|
nuclear@51
|
98 XMLDocument xml;
|
nuclear@51
|
99 XMLError err = xml.Parse(buf, bytes);
|
nuclear@51
|
100 if(err) {
|
nuclear@51
|
101 logmsg(LOG_ERROR, "failed to parse XML animation file: %s\n%s\n", xml.GetErrorStr1(),
|
nuclear@51
|
102 xml.GetErrorStr2());
|
nuclear@51
|
103 delete [] buf;
|
nuclear@51
|
104 return false;
|
nuclear@51
|
105 }
|
nuclear@51
|
106
|
nuclear@51
|
107 XMLElement *root = xml.RootElement();
|
nuclear@51
|
108 if(strcmp(root->Name(), "anim") != 0) {
|
nuclear@51
|
109 logmsg(LOG_ERROR, "invalid XML file, root node is not <anim>\n");
|
nuclear@51
|
110 delete [] buf;
|
nuclear@51
|
111 return false;
|
nuclear@51
|
112 }
|
nuclear@51
|
113
|
nuclear@51
|
114 XMLElement *elem;
|
nuclear@51
|
115
|
nuclear@51
|
116 elem = root->FirstChildElement();
|
nuclear@51
|
117 while(elem) {
|
nuclear@51
|
118 const char *elem_name = elem->Name();
|
nuclear@51
|
119
|
nuclear@51
|
120 if(strcmp(elem_name, "name") == 0) {
|
nuclear@51
|
121 } else if(strcmp(elem_name, "attr") == 0) {
|
nuclear@51
|
122 }
|
nuclear@51
|
123 elem = elem->NextSiblingElement();
|
nuclear@51
|
124 }
|
nuclear@51
|
125
|
nuclear@51
|
126 delete [] buf;
|
nuclear@51
|
127 return true;
|
nuclear@51
|
128 }
|
nuclear@19
|
129
|
nuclear@19
|
130 static Material *read_material(Scene *scn, XMLElement *xml_mtl)
|
nuclear@19
|
131 {
|
nuclear@19
|
132 Material *mtl = new Material;
|
nuclear@19
|
133 mtl->name = get_name(xml_mtl, scn->get_material_count());
|
nuclear@19
|
134
|
nuclear@19
|
135 // get all the material attributes in turn
|
nuclear@19
|
136 XMLElement *elem = xml_mtl->FirstChildElement("attr");
|
nuclear@19
|
137 while(elem) {
|
nuclear@19
|
138 MaterialAttrib attr;
|
nuclear@19
|
139 const char *name = read_material_attrib(&attr, elem);
|
nuclear@19
|
140 if(name) {
|
nuclear@19
|
141 (*mtl)[name] = attr;
|
nuclear@19
|
142 }
|
nuclear@19
|
143
|
nuclear@19
|
144 elem = elem->NextSiblingElement("attr");
|
nuclear@19
|
145 }
|
nuclear@19
|
146
|
nuclear@19
|
147 return mtl;
|
nuclear@19
|
148 }
|
nuclear@19
|
149
|
nuclear@19
|
150 static const char *read_material_attrib(MaterialAttrib *attr, XMLElement *xml_attr)
|
nuclear@19
|
151 {
|
nuclear@19
|
152 const char *name;
|
nuclear@19
|
153
|
nuclear@19
|
154 XMLElement *elem;
|
nuclear@19
|
155 if((elem = xml_attr->FirstChildElement("name"))) {
|
nuclear@19
|
156 if(!(name = elem->Attribute("string"))) {
|
nuclear@19
|
157 return 0;
|
nuclear@19
|
158 }
|
nuclear@19
|
159 }
|
nuclear@19
|
160
|
nuclear@19
|
161 if((elem = xml_attr->FirstChildElement("val"))) {
|
nuclear@19
|
162 if(elem->QueryFloatAttribute("float", &attr->value.x) != XML_NO_ERROR) {
|
nuclear@19
|
163 // try a float3
|
nuclear@19
|
164 const char *valstr = elem->Attribute("float3");
|
nuclear@19
|
165 if(!valstr || sscanf(valstr, "%f %f %f", &attr->value.x, &attr->value.y,
|
nuclear@19
|
166 &attr->value.z) != 3) {
|
nuclear@19
|
167 // try a float4
|
nuclear@19
|
168 valstr = elem->Attribute("float4");
|
nuclear@19
|
169 if(!valstr || sscanf(valstr, "%f %f %f %f", &attr->value.x, &attr->value.y,
|
nuclear@19
|
170 &attr->value.z, &attr->value.w) != 4) {
|
nuclear@19
|
171 // no valid val attribute found
|
nuclear@19
|
172 return 0;
|
nuclear@19
|
173 }
|
nuclear@19
|
174 }
|
nuclear@19
|
175 }
|
nuclear@19
|
176 }
|
nuclear@19
|
177
|
nuclear@19
|
178 if((elem = xml_attr->FirstChildElement("map"))) {
|
nuclear@19
|
179 const char *tex = elem->Attribute("string");
|
nuclear@19
|
180 if(tex) {
|
nuclear@19
|
181 attr->map = std::string(tex);
|
nuclear@19
|
182 }
|
nuclear@19
|
183 }
|
nuclear@19
|
184
|
nuclear@19
|
185 return name;
|
nuclear@19
|
186 }
|
nuclear@19
|
187
|
nuclear@19
|
188 static Mesh *read_mesh(Scene *scn, XMLElement *xml_mesh)
|
nuclear@19
|
189 {
|
nuclear@19
|
190 Mesh *mesh = new Mesh;
|
nuclear@19
|
191 mesh->name = get_name(xml_mesh, scn->get_mesh_count());
|
nuclear@19
|
192
|
nuclear@19
|
193 XMLElement *elem;
|
nuclear@19
|
194 if((elem = xml_mesh->FirstChildElement("material"))) {
|
nuclear@19
|
195 int idx;
|
nuclear@19
|
196 if(elem->QueryIntAttribute("int", &idx) == XML_NO_ERROR) {
|
nuclear@19
|
197 mesh->material = scn->get_material(idx);
|
nuclear@19
|
198 } else {
|
nuclear@19
|
199 // try string
|
nuclear@19
|
200 const char *mtlstr = elem->Attribute("string");
|
nuclear@19
|
201 if(mtlstr) {
|
nuclear@19
|
202 mesh->material = scn->get_material(mtlstr);
|
nuclear@19
|
203 }
|
nuclear@19
|
204 }
|
nuclear@19
|
205 }
|
nuclear@19
|
206
|
nuclear@19
|
207 /* reading mesh data from XML is not supported, only MESH_FILE can be used to
|
nuclear@19
|
208 * specify an external mesh file to be loaded
|
nuclear@19
|
209 */
|
nuclear@19
|
210
|
nuclear@19
|
211 if((elem = xml_mesh->FirstChildElement("file"))) {
|
nuclear@19
|
212 const char *fname = elem->Attribute("string");
|
nuclear@19
|
213 if(fname) {
|
nuclear@19
|
214 if(!mesh->load(fname)) {
|
nuclear@19
|
215 delete mesh;
|
nuclear@19
|
216 return 0;
|
nuclear@19
|
217 }
|
nuclear@19
|
218 }
|
nuclear@19
|
219 }
|
nuclear@19
|
220
|
nuclear@19
|
221 return mesh;
|
nuclear@19
|
222 }
|
nuclear@19
|
223
|
nuclear@19
|
224 static std::string get_name(XMLElement *node, int idx)
|
nuclear@19
|
225 {
|
nuclear@19
|
226 char buf[64];
|
nuclear@19
|
227 const char *name = 0;
|
nuclear@19
|
228
|
nuclear@19
|
229 XMLElement *elem;
|
nuclear@19
|
230 if((elem = node->FirstChildElement("name"))) {
|
nuclear@19
|
231 name = elem->Attribute("string");
|
nuclear@19
|
232 }
|
nuclear@19
|
233
|
nuclear@19
|
234 if(!name) {
|
nuclear@19
|
235 sprintf(buf, "mesh%04d", idx);
|
nuclear@19
|
236 name = buf;
|
nuclear@19
|
237 }
|
nuclear@19
|
238
|
nuclear@19
|
239 return std::string(name);
|
nuclear@19
|
240 }
|