2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
10 **************************************************************************/
18 #include <boost/algorithm/string/trim.hpp>
21 #include <stlplus/strings/string_utilities.hpp>
29 #define AC3D_FORMAT_VERSION (0x0b)
30 #define ZLIB_BUF_SIZE (262114)
36 static std::string
read_string(std::istream
& stream
)
41 do // skip to the next non-space character
45 while (stream
&& std::isspace(atom
));
52 if (atom
== '"') break;
62 if (std::isspace(atom
)) break;
71 inline int read_hex(std::istream
& stream
)
74 std::ios::fmtflags flags
= stream
.flags();
75 stream
.setf(std::ios::hex
, std::ios::basefield
);
81 inline vector2
read_pair(std::istream
& stream
)
84 stream
>> triplet
[0] >> triplet
[1];
90 inline vector
< scalar
, fixed
<D
> > read_triplet(std::istream
& stream
)
92 vector
< scalar
, fixed
<D
> > triplet
;
93 stream
>> triplet
[0] >> triplet
[1] >> triplet
[2];
98 void mesh::import(std::istream
& stream
)
103 std::stack
<int> kids
;
105 // read and verify the AC3D header
108 unsigned version
= 0;
110 stream
.get(magic
, sizeof(magic
));
111 if (!stream
|| strncmp(magic
, "AC3D", 4) != 0)
113 throw std::runtime_error("invalid mesh header");
116 version
= read_hex(stream
);
117 if (version
> AC3D_FORMAT_VERSION
)
119 throw std::runtime_error("wrong mesh file format version");
128 if (atom
== "MATERIAL")
130 materials_
.push_back(material(read_string(stream
)));
133 materials_
.back().diffuse
= read_triplet
<4>(stream
);
135 materials_
.back().ambient
= read_triplet
<3>(stream
);
137 materials_
.back().emissive
= read_triplet
<3>(stream
);
139 materials_
.back().specular
= read_triplet
<3>(stream
);
141 stream
>> atom
>> materials_
.back().shininess
;
142 stream
>> atom
>> materials_
.back().diffuse
[3]; // transparency
143 materials_
.back().diffuse
[3] = 1.0; // FIXME: temporary
145 log_info("read material", materials_
.back().name
,
146 materials_
.back().diffuse
);
148 else if (atom
== "OBJECT")
151 if (atom
!= "world" && atom
!= "group" && atom
!= "poly")
153 throw std::runtime_error("unexpected object type " + atom
);
156 object_ptr newObj
= object::alloc();
160 obj
->kids
.push_back(newObj
);
161 newObj
->parent
= obj
;
165 objects_
.push_back(newObj
);
170 else if (atom
== "name")
172 if (obj
) obj
->name
= read_string(stream
);
173 else throw std::runtime_error("unexpected atom: " + atom
);
175 else if (atom
== "data")
177 std::getline(stream
, atom
);
178 std::getline(stream
, obj
? obj
->data
: atom
);
180 else if (atom
== "texture")
182 if (obj
) obj
->texture
= resource::load(read_string(stream
));
184 else if (atom
== "texrep")
186 if (obj
) obj
->texrep
= read_pair(stream
);
187 else throw std::runtime_error("unexpected atom: " + atom
);
189 else if (atom
== "rot")
192 std::getline(stream
, atom
);
194 else if (atom
== "loc")
197 std::getline(stream
, atom
);
199 else if (atom
== "url")
201 if (obj
) std::getline(stream
, obj
->url
);
202 else throw std::runtime_error("unexpected atom: " + atom
);
204 else if (atom
== "numvert")
206 if (!obj
) throw std::runtime_error("unexpected atom: " + atom
);
211 log_warning("adding verts", numvert
);
213 for (int i
= 0; i
< numvert
; ++i
)
215 obj
->verts
.push_back(read_triplet
<3>(stream
));
216 log_error("vert", obj
->verts
.back());
219 else if (atom
== "numsurf")
221 if (!obj
) throw std::runtime_error("unexpected atom: " + atom
);
226 for (int i
= 0; i
< numsurf
; ++i
)
229 if (atom
!= "SURF") throw std::runtime_error("uh oh");
231 int flags
= read_hex(stream
);
236 if (atom
== "mat") stream
>> material
>> atom
;
240 throw std::runtime_error("blaaaaaaaahhh!!");
245 ASSERT(numrefs
>= 3);
247 if ((int)obj
->faces
.size() <= material
)
249 log_info("inserting face...");
250 //obj->faces.insert(obj->faces.begin() + material,
252 obj
->faces
.resize(material
+ 1);
253 log_info("inserted face", material
, obj
->faces
.size());
256 material_group
& face
= obj
->faces
[material
];
260 vector2 uv
= read_pair(stream
);
261 face
.triangles
.push_back(vert
);
262 face
.triangles_uv
.push_back(uv
);
264 unsigned first
= face
.triangles
.back();
265 vector2 first_uv
= face
.triangles_uv
.back();
268 uv
= read_pair(stream
);
269 face
.triangles
.push_back(vert
);
270 face
.triangles_uv
.push_back(uv
);
273 uv
= read_pair(stream
);
274 face
.triangles
.push_back(vert
);
275 face
.triangles_uv
.push_back(uv
);
277 unsigned last
= face
.triangles
.back();
278 vector2 last_uv
= face
.triangles_uv
.back();
280 for (int j
= 3; j
< numrefs
; ++j
)
283 face
.triangles
.push_back(first
);
284 face
.triangles_uv
.push_back(first_uv
);
287 face
.triangles
.push_back(last
);
288 face
.triangles_uv
.push_back(last_uv
);
291 uv
= read_pair(stream
);
292 face
.triangles
.push_back(vert
);
293 face
.triangles_uv
.push_back(uv
);
295 last
= face
.triangles
.back();
296 last_uv
= face
.triangles_uv
.back();
300 else if (atom
== "kids")
302 for (int i
= kids
.size(); i
> 0; --i
)
304 if (--kids
.top() <= 0)
306 ASSERT(obj
&& "should be an object");
315 if (numkids
> 0) kids
.push(numkids
);
319 log_info("KIDS", kids
.top(), "|", kids
.size());
324 log_warning("UNKNOWN ATOM:", atom
);
331 mesh::mesh(const std::string
& path
)
333 std::ifstream
file(path
.c_str(), std::ifstream::in
|
334 std::ifstream::binary
);
335 if (!file
) throw std::runtime_error("cannot find mesh file");
337 // if we can read the header, the file isn't compressed
339 file
.get(magic
, sizeof(magic
));
340 if (strncmp(magic
, "AC3D", 4) == 0)
342 log_info("text mesh detected");
343 file
.seekg(std::ios::beg
);
349 log_info("compressed mesh detected");
350 file
.seekg(std::ios::beg
);
352 std::stringstream stream
;
353 char in
[ZLIB_BUF_SIZE
];
354 char out
[ZLIB_BUF_SIZE
];
358 zstream
.zalloc
= Z_NULL
;
359 zstream
.zfree
= Z_NULL
;
360 zstream
.opaque
= Z_NULL
;
361 zstream
.avail_in
= 0;
362 zstream
.next_in
= Z_NULL
;
364 int result
= inflateInit2(&zstream
, 32+MAX_WBITS
);
365 if (result
!= Z_OK
) throw std::runtime_error("zlib init error");
369 file
.read(in
, sizeof(in
));
370 zstream
.next_in
= (Bytef
*)in
;
371 zstream
.avail_in
= file
.gcount();
373 if (zstream
.avail_in
== 0) break;
377 zstream
.next_out
= (Bytef
*)out
;
378 zstream
.avail_out
= sizeof(out
);
380 result
= inflate(&zstream
, Z_NO_FLUSH
);
386 inflateEnd(&zstream
);
387 throw std::runtime_error("zlib inflate error");
389 throw std::runtime_error("zlib stream error");
392 int inflated
= sizeof(out
) - zstream
.avail_out
;
393 stream
.write(out
, inflated
);
395 while (zstream
.avail_out
== 0);
397 while(result
!= Z_STREAM_END
);
399 inflateEnd(&zstream
);
406 void mesh::draw(scalar alpha
) const
408 //glEnableClientState(GL_VERTEX_ARRAY);
409 //glEnableClientState(GL_TEXTURE_COORD_ARRAY);
411 glColor4f(1.0f
, 1.0f
, 1.0f
, 1.0f
);
413 std::vector
<object_ptr
>::const_iterator it
;
414 for (it
= objects_
.begin(); it
!= objects_
.end(); ++it
)
416 (*it
)->draw(*this, alpha
);
421 void mesh::set_material(int index
) const
423 set_material(materials_
[index
]);
426 void mesh::set_material(const material
& material
) const
428 glMaterialfv(GL_FRONT_AND_BACK
, GL_DIFFUSE
, material
.diffuse
.data());
429 glMaterialfv(GL_FRONT_AND_BACK
, GL_AMBIENT
, material
.ambient
.data());
430 glMaterialfv(GL_FRONT_AND_BACK
, GL_SPECULAR
, material
.specular
.data());
431 glMaterialfv(GL_FRONT_AND_BACK
, GL_EMISSION
, material
.emissive
.data());
432 glMaterialf(GL_FRONT_AND_BACK
, GL_SHININESS
, material
.shininess
);
433 //glColor(material.diffuse);
437 void mesh::object::draw(const mesh
& mesh
, scalar alpha
) const
439 //log_info("cool", verts.size());
441 //image::reset_binding();
442 //std::vector<vector3>::const_iterator it;
443 //glBegin(GL_LINE_STRIP);
444 //for (it = verts.begin(); it != verts.end(); ++it)
451 //glPolygonMode(GL_BACK, GL_LINE);
452 //glVertexPointer(3, GL_SCALAR, 0, verts[0].data());
453 if (texture
) texture
->bind();
454 else image::reset_binding();
456 for (size_t i
= 0; i
< faces
.size(); ++i
)
458 const material_group
& face
= faces
[i
];
459 mesh
.set_material(i
);
461 //std::vector<unsigned>::const_iterator jt;
462 int count
= face
.triangles
.size();
463 for (int j
= 0; j
< count
; j
+= 3)
465 glBegin(GL_TRIANGLES
);
466 glTexCoord(face
.triangles_uv
[j
]);
467 glVertex(verts
[face
.triangles
[j
]]);
468 glTexCoord(face
.triangles_uv
[j
+1]);
469 glVertex(verts
[face
.triangles
[j
+1]]);
470 glTexCoord(face
.triangles_uv
[j
+2]);
471 glVertex(verts
[face
.triangles
[j
+2]]);
476 std::vector
<object_ptr
>::const_iterator jt
;
477 for (jt
= kids
.begin(); jt
!= kids
.end(); ++jt
)
479 (*jt
)->draw(mesh
, alpha
);
483 void mesh::material_group::draw(scalar alpha
) const
485 // TODO: setup material
488 if (triangles.size() > 0)
490 //log_info("drawing triangles:", triangles.size()/3);
491 glTexCoordPointer(2, GL_SCALAR, 0, triangles_uv[0].data());
492 glDrawElements(GL_TRIANGLES,
493 triangles.size(), GL_UNSIGNED_INT,
500 class mesh_resource_loader
504 mesh_resource_loader()
506 resource::register_type
<mesh
>("ac", "models");
509 ~mesh_resource_loader()
511 resource::unregister_type("ac");
515 static mesh_resource_loader loader
;