]> Dogcows Code - chaz/yoink/blob - src/moof/mesh.cc
fixed documentation about where to find licenses
[chaz/yoink] / src / moof / mesh.cc
1
2 /*] Copyright (c) 2009-2011, Charles McGarvey [*****************************
3 **] All rights reserved.
4 *
5 * Distributable under the terms and conditions of the 2-clause BSD license;
6 * see the file COPYING for a complete text of the license.
7 *
8 *****************************************************************************/
9
10 #include <cstring>
11 #include <fstream>
12 #include <sstream>
13 #include <stack>
14 #include <stdexcept>
15
16 #include <boost/algorithm/string/trim.hpp>
17 #include <zlib.h>
18
19 #include "compression.hh"
20 #include "debug.hh"
21 #include "log.hh"
22 #include "mesh.hh"
23 #include "opengl.hh"
24
25 #define AC3D_FORMAT_VERSION 0x0b
26
27
28 namespace moof {
29
30
31 MOOF_REGISTER_RESOURCE(mesh, ac, models);
32 MOOF_REGISTER_RESOURCE(mesh, acz, models);
33
34
35 static std::string read_string(std::istream& stream)
36 {
37 std::string str;
38 char atom;
39
40 do stream.get(atom);
41 while (stream && std::isspace(atom));
42
43 if (atom == '"')
44 {
45 do {
46 stream.get(atom);
47 if (atom == '"') break;
48 str += atom;
49 }
50 while (stream);
51 }
52 else
53 {
54 do {
55 stream.get(atom);
56 if (std::isspace(atom)) break;
57 str += atom;
58 }
59 while (stream);
60 }
61
62 return str;
63 }
64
65 static int read_hex(std::istream& stream)
66 {
67 int hex;
68 std::ios::fmtflags flags = stream.flags();
69 stream.setf(std::ios::hex, std::ios::basefield);
70 stream >> hex;
71 stream.flags(flags);
72 return hex;
73 }
74
75 static vector2 read_pair(std::istream& stream)
76 {
77 vector2 pair;
78 stream >> pair[0] >> pair[1];
79 return pair;
80 }
81
82 static vector3 read_triplet(std::istream& stream)
83 {
84 vector3 triplet;
85 stream >> triplet[0] >> triplet[1] >> triplet[2];
86 return triplet;
87 }
88
89 static vector4 read_color(std::istream& stream)
90 {
91 vector4 color;
92 stream >> color[0] >> color[1] >> color[2];
93 color[3] = SCALAR(1.0);
94 return color;
95 }
96
97
98 static inline void throw_invalid_atom(const std::string& atom)
99 {
100 throw std::runtime_error("unexpected atom: " + atom);
101 }
102
103
104 void mesh::load(std::istream& stream)
105 {
106 // read and verify the AC3D header
107 char magic[5];
108 unsigned version = 0;
109
110 stream.get(magic, sizeof(magic));
111 if (!stream || strncmp(magic, "AC3D", 4) != 0)
112 {
113 throw std::runtime_error("invalid mesh header");
114 }
115
116 version = read_hex(stream);
117 if (version > AC3D_FORMAT_VERSION)
118 {
119 throw std::runtime_error("wrong mesh file format version");
120 }
121
122 std::string atom;
123 object_ptr obj;
124 std::stack<int> kids;
125
126 while (stream)
127 {
128 stream >> atom;
129 if (!stream) break;
130
131 if (atom == "OBJECT")
132 {
133 obj = load_object(stream, obj);
134 }
135 else if (atom == "MATERIAL")
136 {
137 load_material(stream);
138 }
139 else if (atom == "name")
140 {
141 if (obj)
142 {
143 obj->name = read_string(stream);
144 object_ptr parent = obj->parent.lock();
145 if (parent) parent->kids_byname.insert(std::make_pair(obj->name, obj));
146 }
147 else
148 {
149 throw_invalid_atom(atom);
150 }
151 }
152 else if (atom == "data")
153 {
154 std::getline(stream, atom);
155 std::getline(stream, obj ? obj->data : atom);
156 }
157 else if (atom == "texture")
158 {
159 if (obj) obj->texture = resource::load(read_string(stream));
160 }
161 else if (atom == "texrep")
162 {
163 if (obj) obj->texrep = read_pair(stream);
164 else throw_invalid_atom(atom);
165 }
166 else if (atom == "rot")
167 {
168 // TODO
169 std::getline(stream, atom);
170 }
171 else if (atom == "loc")
172 {
173 // TODO
174 std::getline(stream, atom);
175 }
176 else if (atom == "url")
177 {
178 if (obj) std::getline(stream, obj->url);
179 else throw_invalid_atom(atom);
180 }
181 else if (atom == "numvert")
182 {
183 if (!obj) throw_invalid_atom(atom);
184
185 int numvert = 0;
186 stream >> numvert;
187
188 for (int i = 0; i < numvert; ++i)
189 {
190 obj->verts.push_back(read_triplet(stream));
191 }
192 }
193 else if (atom == "numsurf")
194 {
195 if (!obj) throw_invalid_atom(atom);
196
197 int numsurf = 0;
198 stream >> numsurf;
199
200 for (int i = 0; i < numsurf; ++i)
201 {
202 stream >> atom;
203 if (atom != "SURF") throw_invalid_atom(atom);
204 load_surface(stream, obj);
205 }
206
207 }
208 else if (atom == "kids")
209 {
210 int numkids = 0;
211 stream >> numkids;
212 if (0 < numkids)
213 {
214 kids.push(numkids);
215 }
216 else
217 {
218 if (0 < kids.size() && 0 < --kids.top()) kids.pop();
219 obj = obj->parent.lock();
220 }
221 }
222 }
223 while (stream);
224 }
225
226
227 void mesh::load_material(std::istream& stream)
228 {
229 materials_.push_back(material(read_string(stream)));
230
231 std::string atom;
232 stream >> atom;
233 materials_.back().diffuse = read_color(stream);
234 stream >> atom;
235 materials_.back().ambient = read_color(stream);
236 stream >> atom;
237 materials_.back().emissive = read_color(stream);
238 stream >> atom;
239 materials_.back().specular = read_color(stream);
240
241 stream >> atom >> materials_.back().shininess;
242 stream >> atom >> materials_.back().diffuse[3];
243 materials_.back().diffuse[3] = SCALAR(1.0) - materials_.back().diffuse[3];
244 }
245
246 mesh::object_ptr mesh::load_object(std::istream& stream, object_ptr parent)
247 {
248 std::string atom;
249 stream >> atom;
250 if (atom != "world" && atom != "group" && atom != "poly")
251 {
252 throw_invalid_atom(atom);
253 }
254
255 object_ptr obj = object::alloc(*this);
256
257 if (parent)
258 {
259 parent->kids.push_back(obj);
260 obj->parent = parent;
261 }
262 else
263 {
264 objects_.push_back(obj);
265 }
266
267 return obj;
268 }
269
270 void mesh::load_surface(std::istream& stream, object_ptr obj)
271 {
272 std::string atom;
273 read_hex(stream);
274
275 int material = 0;
276 stream >> atom;
277 if (atom == "mat") stream >> material >> atom;
278 if (atom != "refs") throw_invalid_atom(atom);
279
280 int numrefs = 0;
281 stream >> numrefs;
282 ASSERT(numrefs >= 3);
283
284 if ((int)obj->faces.size() <= material) obj->faces.resize(material + 1);
285 material_group& face = obj->faces[material];
286
287 std::vector<unsigned> verts(numrefs);
288 std::vector<vector2> uv(numrefs);
289
290 for (int i = 0; i < numrefs; ++i)
291 {
292 stream >> verts[i];
293 uv[i] = read_pair(stream);
294 }
295
296 // translate texture coordinates
297 if (obj->texture) obj->texture->fix_uv(uv);
298 // TODO why isn't texture always defined at this point?
299
300 for (int i = 0; i < numrefs; ++i)
301 {
302 scalar vert = verts[i];
303 vector2 texcoord = uv[i];
304 if (vert < face.triangles_uv.size())
305 {
306 if (texcoord != face.triangles_uv[vert])
307 {
308 obj->verts.push_back(obj->verts[vert]);
309 face.triangles_uv.resize(obj->verts.size());
310 vert = obj->verts.size() - 1;
311 }
312 }
313 else face.triangles_uv.resize(vert + 1);
314 face.triangles_uv[vert] = texcoord;
315 verts[i] = vert;
316 }
317
318 face.triangles.push_back(verts[0]);
319 face.triangles.push_back(verts[1]);
320 face.triangles.push_back(verts[2]);
321 for (int i = 3; i < numrefs; ++i)
322 {
323 face.triangles.push_back(verts[0]);
324 face.triangles.push_back(verts[i-1]);
325 face.triangles.push_back(verts[i]);
326 }
327
328 #if 0
329 unsigned vert;
330 stream >> vert;
331 vector2 uv = read_pair(stream);
332 if (vert < face.triangles_uv.size())
333 {
334 if (uv != face.triangles_uv[vert])
335 {
336 obj->verts.push_back(obj->verts[vert]);
337 face.triangles_uv.resize(obj->verts.size());
338 vert = obj->verts.size() - 1;
339 }
340 }
341 else face.triangles_uv.resize(vert + 1);
342 face.triangles_uv[vert] = uv;
343 face.triangles.push_back(vert);
344
345 unsigned first = vert;
346
347 stream >> vert;
348 uv = read_pair(stream);
349 if (vert < face.triangles_uv.size())
350 {
351 if (uv != face.triangles_uv[vert])
352 {
353 obj->verts.push_back(obj->verts[vert]);
354 face.triangles_uv.resize(obj->verts.size());
355 vert = obj->verts.size() - 1;
356 }
357 }
358 else face.triangles_uv.resize(vert + 1);
359 face.triangles_uv[vert] = uv;
360 face.triangles.push_back(vert);
361
362 stream >> vert;
363 uv = read_pair(stream);
364 if (vert < face.triangles_uv.size())
365 {
366 if (uv != face.triangles_uv[vert])
367 {
368 obj->verts.push_back(obj->verts[vert]);
369 face.triangles_uv.resize(obj->verts.size());
370 vert = obj->verts.size() - 1;
371 }
372 }
373 else face.triangles_uv.resize(vert + 1);
374 face.triangles_uv[vert] = uv;
375 face.triangles.push_back(vert);
376
377 unsigned last = vert;
378
379 for (int j = 3; j < numrefs; ++j)
380 {
381 face.triangles.push_back(first);
382 face.triangles.push_back(last);
383
384 stream >> vert;
385 uv = read_pair(stream);
386 if (vert < face.triangles_uv.size())
387 {
388 if (uv != face.triangles_uv[vert])
389 {
390 obj->verts.push_back(obj->verts[vert]);
391 face.triangles_uv.resize(obj->verts.size());
392 vert = obj->verts.size() - 1;
393 }
394 }
395 else face.triangles_uv.resize(vert + 1);
396 face.triangles_uv[vert] = uv;
397 face.triangles.push_back(vert);
398
399 last = face.triangles.back();
400 }
401 #endif
402 }
403
404
405 mesh::mesh(const std::string& path)
406 {
407 std::ifstream file(path.c_str(), std::ios::binary);
408 if (!file) throw std::runtime_error("cannot find mesh file");
409
410 // if we can read the header, the file isn't compressed
411 char magic[5];
412 file.get(magic, sizeof(magic));
413 if (strncmp(magic, "AC3D", 4) == 0)
414 {
415 log_info("text mesh detected");
416 file.seekg(std::ios::beg);
417 load(file);
418 }
419 else
420 {
421 log_info("decompressing mesh...");
422 file.seekg(std::ios::beg);
423 std::stringstream stream;
424 inflate(file, stream);
425 load(stream);
426 }
427 }
428
429 void mesh::draw(scalar alpha) const
430 {
431 glEnableClientState(GL_VERTEX_ARRAY);
432
433 std::vector<object_ptr>::const_iterator it;
434 for (it = objects_.begin(); it != objects_.end(); ++it)
435 {
436 (*it)->draw(alpha);
437 }
438
439 // TODO: disable vertex array?
440 }
441
442 void mesh::set_material(int index) const
443 {
444 set_material(materials_[index]);
445 }
446
447 void mesh::set_material(const material& material) const
448 {
449 glColor(material.diffuse);
450 glMaterial(GL_FRONT, GL_DIFFUSE, material.diffuse);
451 glMaterial(GL_FRONT, GL_AMBIENT, material.ambient);
452 glMaterial(GL_FRONT, GL_SPECULAR, material.specular);
453 glMaterial(GL_FRONT, GL_EMISSION, material.emissive);
454 glMaterial(GL_FRONT, GL_SHININESS, material.shininess);
455 }
456
457
458 void mesh::object::draw(scalar alpha, bool recurse) const
459 {
460 glVertexPointer(verts);
461
462 if (texture)
463 {
464 texture->bind();
465 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
466 }
467 else
468 {
469 image::reset_binding();
470 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
471 }
472
473 for (size_t i = 0; i < faces.size(); ++i)
474 {
475 const material_group& face = faces[i];
476 if (face.triangles.size() == 0) continue;
477
478 mesh.set_material(i);
479
480 if (texture) glTexCoordPointer(face.triangles_uv);
481 glDrawElements(GL_TRIANGLES, face.triangles);
482 }
483
484 if (recurse)
485 {
486 std::vector<object_ptr>::const_iterator jt;
487 for (jt = kids.begin(); jt != kids.end(); ++jt)
488 (*jt)->draw(alpha);
489 }
490 }
491
492
493 } // namespace moof
494
This page took 0.054875 seconds and 4 git commands to generate.