00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "scenegraph/model.hpp"
00035
00036 #include "data/list.hpp"
00037 #include "data/dictionary.hpp"
00038 #include "data/fstream.hpp"
00039 #include "data/file.hpp"
00040 #include "data/cache.hpp"
00041
00042 #include "platform/vbuffer.hpp"
00043 #include "platform/lowlevel.hpp"
00044
00045 #include <cmath>
00046
00047 namespace gsgl
00048 {
00049
00050 using namespace data;
00051 using namespace io;
00052 using namespace platform;
00053
00054 namespace scenegraph
00055 {
00056
00057
00058 class material_impl
00059 {
00060 public:
00061 string name;
00062
00063 color ambient;
00064 color diffuse;
00065 color specular;
00066 color emissive;
00067
00068 float shininess;
00069 bool flat;
00070
00071 texture *tex;
00072
00073 material_impl();
00074 ~material_impl();
00075 };
00076
00077
00078 material_impl::material_impl()
00079 : ambient(0.8f, 0.8f, 0.8f, 1),
00080 diffuse(0.8f, 0.8f, 0.8f, 1),
00081 specular(0.8f, 0.8f, 0.8f, 1),
00082 emissive(0.8f, 0.8f, 0.8f, 1),
00083 shininess(128.0f),
00084 flat(false),
00085 tex(0)
00086 {
00087 }
00088
00089
00090 material_impl::~material_impl()
00091 {
00092 delete tex;
00093 }
00094
00095
00096
00097
00098 class material_file
00099 : public shared_object
00100 {
00101 public:
00102 dictionary<material_impl *, string> materials;
00103
00104 material_file(const string & fname);
00105 ~material_file();
00106
00107 private:
00108 void load_mtl_file(const string & fname);
00109 };
00110
00111
00112 material_file::material_file(const string & fname)
00113 : shared_object()
00114 {
00115 string full_path = io::file::get_full_path(fname);
00116
00117 string ext = full_path.right_substring(4);
00118 if (ext.make_lower() == L".mtl")
00119 load_mtl_file(full_path);
00120 else
00121 throw runtime_exception(L"%ls: unknown material file format!", full_path.w_string());
00122 }
00123
00124
00125 material_file::~material_file()
00126 {
00127 for (dictionary<material_impl *, string>::iterator i = materials.iter(); i.is_valid(); ++i)
00128 delete *i;
00129
00130 materials.clear();
00131 }
00132
00133
00134 void material_file::load_mtl_file(const string & fname)
00135 {
00136 ft_stream fs(fname);
00137
00138 material_impl *cur = 0;
00139
00140 string line;
00141 int line_number = 0;
00142
00143 for (fs >> line; !fs.at_end(); fs >> line)
00144 {
00145 line_number++;
00146
00147 line.trim();
00148 if (line.size() == 0 || line[0] == L'#')
00149 continue;
00150
00151 list<string> tokens = line.split(L" ");
00152
00153 if (tokens[0] == L"newmtl")
00154 {
00155 if (materials.contains_index(tokens[1]))
00156 throw runtime_exception(L"Duplicate material '%ls' in %ls.", tokens[1].w_string(), fname.w_string());
00157
00158 cur = new material_impl();
00159 materials[tokens[1]] = cur;
00160 }
00161 else if (tokens[0] == L"Ka")
00162 {
00163 cur->ambient[color::COMPONENT_RED] = static_cast<float>(tokens[1].to_double());
00164 cur->ambient[color::COMPONENT_GREEN] = static_cast<float>(tokens[2].to_double());
00165 cur->ambient[color::COMPONENT_BLUE] = static_cast<float>(tokens[3].to_double());
00166 }
00167 else if (tokens[0] == L"Kd")
00168 {
00169 cur->diffuse[color::COMPONENT_RED] = static_cast<float>(tokens[1].to_double());
00170 cur->diffuse[color::COMPONENT_GREEN] = static_cast<float>(tokens[2].to_double());
00171 cur->diffuse[color::COMPONENT_BLUE] = static_cast<float>(tokens[3].to_double());
00172 }
00173 else if (tokens[0] == L"Ks")
00174 {
00175 cur->specular[color::COMPONENT_RED] = static_cast<float>(tokens[1].to_double());
00176 cur->specular[color::COMPONENT_GREEN] = static_cast<float>(tokens[2].to_double());
00177 cur->specular[color::COMPONENT_BLUE] = static_cast<float>(tokens[3].to_double());
00178 }
00179 else if (tokens[0] == L"d")
00180 {
00181 float alpha = static_cast<float>(tokens[1].to_double());
00182
00183 cur->ambient[color::COMPONENT_ALPHA] = alpha;
00184 cur->diffuse[color::COMPONENT_ALPHA] = alpha;
00185 cur->specular[color::COMPONENT_ALPHA] = alpha;
00186 }
00187 else if (tokens[0] == L"Tr")
00188 {
00189 float alpha = 1.0f - static_cast<float>(tokens[1].to_double());
00190
00191 cur->ambient[color::COMPONENT_ALPHA] = alpha;
00192 cur->diffuse[color::COMPONENT_ALPHA] = alpha;
00193 cur->specular[color::COMPONENT_ALPHA] = alpha;
00194 }
00195 else if (tokens[0] == L"illum")
00196 {
00197 int code = tokens[1].to_int();
00198 if (code == 1)
00199 cur->flat = true;
00200 }
00201 else if (tokens[0] == L"Ns")
00202 {
00203 cur->shininess = static_cast<float>(tokens[1].to_double());
00204 if (cur->shininess > 128.0)
00205 cur->shininess = 128.0;
00206 }
00207 else if (tokens[0] == L"map_Ka" || tokens[0] == L"map_Kd")
00208 {
00209 file f(fname);
00210 cur->tex = new platform::texture(f.get_dir_name() + tokens[1]);
00211 }
00212 else
00213 {
00214 throw runtime_exception(L"Syntax error in MTL file %ls:%d", fname.w_string(), line_number);
00215 }
00216 }
00217 }
00218
00219
00220
00221
00222 typedef cache<material_file> material_cache;
00223
00224
00225 material::material(const string & filename, const string & material_name)
00226 : impl(0)
00227 {
00228 material_cache & global_material_files = *material_cache::global_instance();
00229
00230 material_file *mf = 0;
00231 if (global_material_files.contains_index(filename))
00232 {
00233 mf = global_material_files[filename];
00234 }
00235 else
00236 {
00237 mf = new material_file(filename);
00238 global_material_files[filename] = mf;
00239 mf->attach();
00240 }
00241 mf->attach();
00242
00243 if (!(impl = mf->materials[material_name]))
00244 {
00245 throw runtime_exception(L"Unable to find material '%ls' in material file '%ls'.", material_name.w_string(), filename.w_string());
00246 }
00247 }
00248
00249
00250 material::~material()
00251 {
00252 }
00253
00254
00255 string & material::get_name() { assert(impl); return impl->name; }
00256 color & material::get_ambient() { assert(impl); return impl->ambient; }
00257 color & material::get_diffuse() { assert(impl); return impl->diffuse; }
00258 color & material::get_specular() { assert(impl); return impl->specular; }
00259 float & material::get_shininess() { assert(impl); return impl->shininess; }
00260 bool & material::get_flat() { assert(impl); return impl->flat; }
00261 texture *material::get_texture() { assert(impl); return impl->tex; }
00262
00263
00264 bool material::is_opaque()
00265 {
00266 assert(impl);
00267 return impl->ambient[color::COMPONENT_ALPHA] == 1.0f && impl->diffuse[color::COMPONENT_ALPHA] == 1.0f;
00268 }
00269
00270
00271
00272 void material::bind(gsgl::flags_t render_flags)
00273 {
00274 assert(impl);
00275
00276 glColor4fv(impl->diffuse.get_val()); CHECK_GL_ERRORS();
00277
00278 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, impl->ambient.get_val()); CHECK_GL_ERRORS();
00279 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, impl->diffuse.get_val()); CHECK_GL_ERRORS();
00280 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, impl->specular.get_val()); CHECK_GL_ERRORS();
00281 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, impl->emissive.get_val()); CHECK_GL_ERRORS();
00282 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, impl->shininess); CHECK_GL_ERRORS();
00283
00284 if (impl->flat)
00285 {
00286 glShadeModel(GL_FLAT); CHECK_GL_ERRORS();
00287 }
00288 else
00289 {
00290 glShadeModel(GL_SMOOTH); CHECK_GL_ERRORS();
00291 }
00292
00293 if (impl->tex && !(render_flags & context::RENDER_UNTEXTURED))
00294 {
00295 impl->tex->bind();
00296 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); CHECK_GL_ERRORS();
00297
00298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); CHECK_GL_ERRORS();
00299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); CHECK_GL_ERRORS();
00300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); CHECK_GL_ERRORS();
00301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); CHECK_GL_ERRORS();
00302 }
00303 }
00304
00305
00306
00307 gsgl::data_object *material::create_global_material_cache()
00308 {
00309 return new material_cache(L"material cache");
00310 }
00311
00312
00313
00314
00315
00316
00317 class submesh
00318 {
00319 public:
00320 material *mat;
00321
00322
00323 vertex_buffer point_vertices;
00324 vertex_buffer line_vertices;
00325 vertex_buffer triangle_vertices;
00326 vertex_buffer triangle_texcoords;
00327 vertex_buffer triangle_normals;
00328
00329 submesh();
00330 ~submesh();
00331
00332 void load();
00333 void unload();
00334 void draw(gsgl::flags_t render_flags = 0);
00335 };
00336
00337
00338 submesh::submesh()
00339 : mat(0),
00340 point_vertices(vbuffer::STATIC),
00341 line_vertices(vbuffer::STATIC),
00342 triangle_vertices(vbuffer::STATIC),
00343 triangle_texcoords(vbuffer::STATIC),
00344 triangle_normals(vbuffer::STATIC)
00345 {
00346 }
00347
00348
00349 submesh::~submesh()
00350 {
00351 unload();
00352
00353 }
00354
00355
00356 void submesh::load()
00357 {
00358 point_vertices.load();
00359 line_vertices.load();
00360 triangle_vertices.load();
00361 triangle_texcoords.load();
00362 triangle_normals.load();
00363 }
00364
00365
00366 void submesh::unload()
00367 {
00368 point_vertices.unload();
00369 line_vertices.unload();
00370 triangle_vertices.unload();
00371 triangle_texcoords.unload();
00372 triangle_normals.unload();
00373 }
00374
00375
00376 void submesh::draw(gsgl::flags_t render_flags)
00377 {
00378 glPushAttrib(GL_ALL_ATTRIB_BITS); CHECK_GL_ERRORS();
00379 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); CHECK_GL_ERRORS();
00380
00381 glEnable(GL_POINT_SMOOTH); CHECK_GL_ERRORS();
00382 glEnable(GL_LINE_SMOOTH); CHECK_GL_ERRORS();
00383 glEnable(GL_POLYGON_SMOOTH); CHECK_GL_ERRORS();
00384
00385 glEnableClientState(GL_VERTEX_ARRAY); CHECK_GL_ERRORS();
00386 glEnableClientState(GL_NORMAL_ARRAY); CHECK_GL_ERRORS();
00387 glEnableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_GL_ERRORS();
00388
00389
00390 glEnable(GL_LIGHTING); CHECK_GL_ERRORS();
00391
00392 glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); CHECK_GL_ERRORS();
00393 glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); CHECK_GL_ERRORS();
00394
00395 glEnable(GL_TEXTURE_2D); CHECK_GL_ERRORS();
00396
00397 mat->bind(render_flags);
00398
00399
00400 if (point_vertices.size())
00401 {
00402 point_vertices.bind();
00403 glVertexPointer(3, GL_FLOAT, 0, 0); CHECK_GL_ERRORS();
00404
00405 glDrawArrays(GL_POINTS, 0, point_vertices.size()/3); CHECK_GL_ERRORS();
00406 }
00407
00408
00409 if (line_vertices.size())
00410 {
00411 line_vertices.bind();
00412 glVertexPointer(3, GL_FLOAT, 0, 0); CHECK_GL_ERRORS();
00413
00414 glDrawArrays(GL_LINES, 0, line_vertices.size()/3); CHECK_GL_ERRORS();
00415 }
00416
00417
00418 glEnable(GL_CULL_FACE); CHECK_GL_ERRORS();
00419
00420 if (triangle_vertices.size())
00421 {
00422 if (render_flags & context::RENDER_WIREFRAME)
00423 {
00424 glPolygonMode(GL_FRONT, GL_LINE); CHECK_GL_ERRORS();
00425 }
00426 else
00427 {
00428 glPolygonMode(GL_FRONT, GL_FILL); CHECK_GL_ERRORS();
00429 }
00430
00431
00432 if (triangle_normals.size() < triangle_vertices.size())
00433 triangle_normals[triangle_vertices.size() - 1] = 0;
00434 if (triangle_texcoords.size()/2 < triangle_vertices.size()/3)
00435 triangle_texcoords[(triangle_vertices.size()/3)*2 - 1] = 0;
00436
00437 triangle_normals.bind();
00438 glNormalPointer(GL_FLOAT, 0, 0); CHECK_GL_ERRORS();
00439
00440 triangle_texcoords.bind();
00441 glTexCoordPointer(2, GL_FLOAT, 0, 0); CHECK_GL_ERRORS();
00442
00443 triangle_vertices.bind();
00444 glVertexPointer(3, GL_FLOAT, 0, 0); CHECK_GL_ERRORS();
00445
00446 glDrawArrays(GL_TRIANGLES, 0, triangle_vertices.size()/3); CHECK_GL_ERRORS();
00447 }
00448
00449 glPopClientAttrib(); CHECK_GL_ERRORS();
00450 glPopAttrib(); CHECK_GL_ERRORS();
00451 }
00452
00453
00454
00455
00456
00457 class mesh
00458 {
00459 string name;
00460 simple_array<submesh *> submeshes;
00461
00462 public:
00463 mesh(const string & name);
00464 ~mesh();
00465
00466 string & get_name() { return name; }
00467 simple_array<submesh *> & get_submeshes() { return submeshes; }
00468 };
00469
00470
00471 mesh::mesh(const string & name)
00472 : name(name)
00473 {
00474 }
00475
00476
00477 mesh::~mesh()
00478 {
00479 for (simple_array<submesh *>::iterator i = submeshes.iter(); i.is_valid(); ++i)
00480 delete *i;
00481 submeshes.clear();
00482 }
00483
00484
00485
00486
00487 class mesh_file
00488 : public shared_object
00489 {
00490 dictionary<mesh *, string> meshes;
00491 public:
00492 mesh_file(const string & fname);
00493 ~mesh_file();
00494
00495 dictionary<mesh *, string> & get_meshes() { return meshes; }
00496
00497 private:
00498 void load_obj_file(const string & fname);
00499 };
00500
00501
00502
00503
00504 mesh_file::mesh_file(const string & fname)
00505 : shared_object()
00506 {
00507 string full_path = io::file::get_full_path(fname);
00508
00509 string ext = full_path.right_substring(4);
00510 if (ext.make_lower() == L".obj")
00511 load_obj_file(full_path);
00512 else
00513 throw runtime_exception(L"%ls: invalid mesh file format.", full_path.w_string());
00514 }
00515
00516
00517 mesh_file::~mesh_file()
00518 {
00519 for (dictionary<mesh *, string>::iterator i = meshes.iter(); i.is_valid(); ++i)
00520 delete *i;
00521 meshes.clear();
00522 }
00523
00524
00525
00526
00527 static void add_triangle_vertex(submesh *sm, const string & s,
00528 simple_array<gsgl::real_t> & vertices,
00529 simple_array<gsgl::real_t> & texcoords,
00530 simple_array<gsgl::real_t> & normals)
00531 {
00532
00533 int indices[3];
00534 int & vvv = indices[0];
00535 int & ttt = indices[1];
00536 int & nnn = indices[2];
00537
00538 for (int i = 0; i < 3; ++i)
00539 indices[i] = 0;
00540
00541 list<string> index_strs = s.split(L"/");
00542 list<string>::iterator index_str = index_strs.iter();
00543 for (int i = 0; index_str.is_valid(); ++i, ++index_str)
00544 indices[i] = index_str->to_int();
00545
00546 if (vvv < 0 || ttt < 0 || nnn < 0)
00547 throw runtime_exception(L"Negative vertices not allowed in Periapsis OBJ files.");
00548
00549
00550 for (int i = 0; i < 3; ++i)
00551 {
00552 if (vvv)
00553 sm->triangle_vertices.get_buffer().append(vertices[(vvv-1)*3 + i]);
00554 if (ttt && i < 2)
00555 sm->triangle_texcoords.get_buffer().append(texcoords[(ttt-1)*2 + i]);
00556 if (nnn)
00557 sm->triangle_normals.get_buffer().append(normals[(nnn-1)*3 + i]);
00558 }
00559 }
00560
00561
00562 static void add_point_vertex(submesh *sm, const string & s,
00563 const simple_array<gsgl::real_t> & vertices)
00564 {
00565 list<string> a = s.split(L"/");
00566 int vvv = a[0].to_int();
00567 if (vvv < 0)
00568 throw runtime_exception(L"Negative vertices not allowed in Periapsis OBJ files.");
00569
00570 for (int i = 0; i < 3; ++i)
00571 sm->point_vertices.get_buffer().append(vertices[(vvv-1)*3 + i]);
00572 }
00573
00574
00575 static void add_line_vertex(submesh *sm, const string & s,
00576 const simple_array<gsgl::real_t> & vertices)
00577 {
00578 list<string> a = s.split(L"/");
00579 int vvv = a[0].to_int();
00580 if (vvv < 0)
00581 throw runtime_exception(L"Negative vertices not allowed in Periapsis OBJ files.");
00582
00583 for (int i = 0; i < 3; ++i)
00584 sm->line_vertices.get_buffer().append(vertices[(vvv-1)*3 + i]);
00585 }
00586
00587
00588
00589
00590 void mesh_file::load_obj_file(const string & fname)
00591 {
00592
00593 string mesh_name = L"__default__mesh__";
00594 mesh *cur_mesh = new mesh(mesh_name);
00595 meshes[mesh_name] = cur_mesh;
00596
00597 submesh *cur_submesh = 0;
00598
00599
00600 string cur_mtllib_name;
00601 string cur_material_name;
00602
00603
00604 simple_array<gsgl::real_t> vertices;
00605 simple_array<gsgl::real_t> texcoords;
00606 simple_array<gsgl::real_t> normals;
00607
00608
00609 file f(fname);
00610 ft_stream fs(fname);
00611 int line_number = 0;
00612 string line;
00613
00614 for (fs >> line; !fs.at_end(); fs >> line)
00615 {
00616 line_number++;
00617 line.trim();
00618
00619 if (line.size() == 0 || line[0] == L'#')
00620 continue;
00621
00622 list<string> tokens = line.split(L" \t");
00623
00624 if (tokens[0] == L"g" || tokens[0] == L"o")
00625 {
00626 cur_mesh = new mesh(tokens[1]);
00627 meshes[tokens[1]] = cur_mesh;
00628
00629 cur_submesh = 0;
00630 }
00631 else if (tokens[0] == L"mtllib")
00632 {
00633 cur_mtllib_name = f.get_dir_name() + tokens[1];
00634 }
00635 else if (tokens[0] == L"usemtl")
00636 {
00637 cur_material_name = tokens[1];
00638 cur_submesh = 0;
00639 }
00640 else if (tokens[0] == L"v")
00641 {
00642 for (gsgl::index_t i = 1; i < 4; ++i)
00643 vertices.append(static_cast<gsgl::real_t>(tokens[i].to_double()));
00644 }
00645 else if (tokens[0] == L"vt")
00646 {
00647 for (gsgl::index_t i = 1; i < 3; ++i)
00648 texcoords.append(static_cast<gsgl::real_t>(tokens[i].to_double()));
00649 }
00650 else if (tokens[0] == L"vn")
00651 {
00652 for (gsgl::index_t i = 1; i < 4; ++i)
00653 normals.append(static_cast<gsgl::real_t>(tokens[i].to_double()));
00654 }
00655 else if (tokens[0] == L"f")
00656 {
00657 if (!cur_submesh)
00658 {
00659 cur_mesh->get_submeshes().append(cur_submesh = new submesh());
00660 cur_submesh->mat = new material(cur_mtllib_name, cur_material_name);
00661 }
00662
00663 if (tokens.size() == 5)
00664 {
00665 add_triangle_vertex(cur_submesh, tokens[1], vertices, texcoords, normals);
00666 add_triangle_vertex(cur_submesh, tokens[2], vertices, texcoords, normals);
00667 add_triangle_vertex(cur_submesh, tokens[3], vertices, texcoords, normals);
00668
00669 add_triangle_vertex(cur_submesh, tokens[3], vertices, texcoords, normals);
00670 add_triangle_vertex(cur_submesh, tokens[4], vertices, texcoords, normals);
00671 add_triangle_vertex(cur_submesh, tokens[1], vertices, texcoords, normals);
00672 }
00673 else if (tokens.size() == 4)
00674 {
00675 add_triangle_vertex(cur_submesh, tokens[1], vertices, texcoords, normals);
00676 add_triangle_vertex(cur_submesh, tokens[2], vertices, texcoords, normals);
00677 add_triangle_vertex(cur_submesh, tokens[3], vertices, texcoords, normals);
00678 }
00679 else if (tokens.size() > 5)
00680 {
00681 throw runtime_exception(L"Error in %ls at line %d: this program can only handle quads or triangles.", fname.w_string(), line_number);
00682 }
00683
00684 }
00685 else if (tokens[0] == L"p")
00686 {
00687 if (!cur_submesh)
00688 {
00689 cur_mesh->get_submeshes().append(cur_submesh = new submesh());
00690 cur_submesh->mat = new material(cur_mtllib_name, cur_material_name);
00691 }
00692
00693 for (gsgl::index_t i = 1; i < tokens.size(); ++i)
00694 {
00695 add_point_vertex(cur_submesh, tokens[i], vertices);
00696 }
00697 }
00698 else if (tokens[0] == L"l")
00699 {
00700 if (!cur_submesh)
00701 {
00702 cur_mesh->get_submeshes().append(cur_submesh = new submesh());
00703 cur_submesh->mat = new material(cur_mtllib_name, cur_material_name);
00704 }
00705
00706 for (gsgl::index_t i = 2; i < tokens.size(); ++i)
00707 {
00708 add_line_vertex(cur_submesh, tokens[i-1], vertices);
00709 add_line_vertex(cur_submesh, tokens[i], vertices);
00710 }
00711 }
00712 else if (tokens[0] == L"s" || tokens[0] == L"sg" || tokens[0] == L"mg")
00713 {
00714
00715 }
00716 else
00717 {
00718 throw runtime_exception(L"%ls (%d): syntax error; unknown command '%ls'.", fname.w_string(), line_number, tokens[0]);
00719 }
00720 }
00721 }
00722
00723
00724
00725
00726 typedef cache<mesh_file> mesh_file_cache;
00727
00728
00729
00730 class submesh_node
00731 : public scenegraph::node
00732 {
00733 bool opaque;
00734 simple_array<submesh *> submeshes;
00735
00736 mutable gsgl::real_t cached_max_extent;
00737
00738 public:
00739 submesh_node(const string & name, node *parent, bool opaque);
00740 virtual ~submesh_node();
00741
00742 bool get_opaque() { return opaque; }
00743 simple_array<submesh *> & get_submeshes() { return submeshes; }
00744
00745
00746 virtual gsgl::real_t get_priority(gsgl::scenegraph::context *);
00747 virtual gsgl::real_t max_extent() const;
00748
00749 virtual void init(gsgl::scenegraph::context *c);
00750 virtual void draw(gsgl::scenegraph::context *c);
00751 virtual void update(gsgl::scenegraph::context *c);
00752 virtual void cleanup(gsgl::scenegraph::context *c);
00753 };
00754
00755
00756 submesh_node::submesh_node(const string & name, node *parent, bool opaque)
00757 : node(name, parent), opaque(opaque), cached_max_extent(-1)
00758 {
00759 }
00760
00761
00762 submesh_node::~submesh_node()
00763 {
00764
00765 }
00766
00767
00768 gsgl::real_t submesh_node::get_priority(context *)
00769 {
00770 return opaque ? NODE_DRAW_SOLID : NODE_DRAW_TRANSLUCENT;
00771 }
00772
00773
00774 static double get_max_squared(const vertex_buffer & vertices)
00775 {
00776 double m, ms = 0;
00777 gsgl::real_t x, y, z;
00778
00779 int i, len = vertices.size() / 3;
00780 for (i = 0; i < len; ++i)
00781 {
00782 x = vertices[i*3+0];
00783 y = vertices[i*3+1];
00784 z = vertices[i*3+2];
00785
00786 m = x*x + y*y + z*z;
00787 if (m > ms)
00788 ms = m;
00789 }
00790
00791 return ms;
00792 }
00793
00794
00795 gsgl::real_t submesh_node::max_extent() const
00796 {
00797 if (cached_max_extent < 0)
00798 {
00799 double ms, msx = 0;
00800
00801 for (simple_array<submesh *>::const_iterator i = submeshes.iter(); i.is_valid(); ++i)
00802 {
00803 ms = get_max_squared( (*i)->point_vertices );
00804 if (ms > msx)
00805 msx = ms;
00806
00807 ms = get_max_squared( (*i)->line_vertices );
00808 if (ms > msx)
00809 msx = ms;
00810
00811 ms = get_max_squared( (*i)->triangle_vertices );
00812 if (ms > msx)
00813 msx = ms;
00814 }
00815
00816 cached_max_extent = static_cast<gsgl::real_t>( ::sqrt(msx) );
00817 }
00818
00819 return cached_max_extent;
00820 }
00821
00822
00823 void submesh_node::init(context *)
00824 {
00825 int i, len = submeshes.size();
00826 for (i = 0; i < len; ++i)
00827 {
00828 submeshes[i]->load();
00829 }
00830 }
00831
00832
00833 void submesh_node::draw(context *c)
00834 {
00835 int i, len = submeshes.size();
00836 for (i = 0; i < len; ++i)
00837 {
00838 submeshes[i]->draw(c->render_flags);
00839 }
00840 }
00841
00842
00843 void submesh_node::update(context *)
00844 {
00845 }
00846
00847
00848 void submesh_node::cleanup(context *)
00849 {
00850 int i, len = submeshes.size();
00851 for (i = 0; i < len; ++i)
00852 {
00853 submeshes[i]->unload();
00854 }
00855 }
00856
00857
00858
00859
00860 static int num_model_parts = 0;
00861
00862
00863 model_part::model_part(const config_record & obj_config)
00864 : node(string::format(L"model_part_%d", num_model_parts++), 0), opaque(0), translucent(0), inertial(0), collision(0)
00865 {
00866 mesh_file_cache & global_mesh_files = *mesh_file_cache::global_instance();
00867
00868 opaque = new submesh_node(get_name() + L"_opaque", this, true);
00869 translucent = new submesh_node(get_name() + L"_translucent", this, false);
00870
00871
00872 if (!obj_config[L"file"].is_empty())
00873 {
00874 string mesh_fname = obj_config.get_directory().get_full_path() + obj_config[L"file"];
00875
00876 mesh_file *mf = 0;
00877 if (global_mesh_files.contains_index(mesh_fname))
00878 {
00879 mf = global_mesh_files[mesh_fname];
00880 }
00881 else
00882 {
00883 mf = new mesh_file(mesh_fname);
00884 global_mesh_files[mesh_fname] = mf;
00885 mf->attach();
00886 }
00887 mf->attach();
00888
00889
00890 mesh *visual_mesh = 0;
00891
00892 string visual_name = obj_config[L"visual"];
00893 if (!visual_name.is_empty())
00894 {
00895 mesh *m = mf->get_meshes()[visual_name];
00896
00897 if (m)
00898 {
00899 visual_mesh = m;
00900
00901 for (simple_array<submesh *>::iterator i = m->get_submeshes().iter(); i.is_valid(); ++i)
00902 {
00903 if ((*i)->mat->is_opaque())
00904 opaque->get_submeshes().append(*i);
00905 else
00906 translucent->get_submeshes().append(*i);
00907 }
00908 }
00909 else
00910 {
00911 throw runtime_exception(L"Invalid visual object name %ls in model part %ls in %ls.", visual_name.w_string(), get_name().w_string(), obj_config.get_file().get_full_path().w_string());
00912 }
00913 }
00914 else
00915 {
00916 throw runtime_exception(L"Invalid model part %ls in %ls: missing visual object.", get_name().w_string(), obj_config.get_file().get_full_path().w_string());
00917 }
00918
00919
00920 string collision_name = obj_config[L"collision"];
00921 if (!collision_name.is_empty())
00922 {
00923 mesh *m = mf->get_meshes()[collision_name];
00924
00925 if (m)
00926 {
00927 collision = m;
00928 }
00929 else
00930 {
00931 throw runtime_exception(L"Invalid collision object name %ls in model part %ls in %ls.", visual_name.w_string(), get_name().w_string(), obj_config.get_file().get_full_path().w_string());
00932 }
00933 }
00934 else
00935 {
00936 collision = visual_mesh;
00937 }
00938
00939
00940 string inertial_name = obj_config[L"inertial"];
00941 if (!inertial_name.is_empty())
00942 {
00943 mesh *m = mf->get_meshes()[inertial_name];
00944
00945 if (m)
00946 {
00947 inertial = m;
00948 }
00949 else
00950 {
00951 throw runtime_exception(L"Invalid inertial object name %ls in model part %ls in %ls.", visual_name.w_string(), get_name().w_string(), obj_config.get_file().get_full_path().w_string());
00952 }
00953 }
00954 else
00955 {
00956 inertial = visual_mesh;
00957 }
00958 }
00959 else
00960 {
00961 throw runtime_exception(L"Invalid model part %ls in %ls: missing file name.", get_name().w_string(), obj_config.get_file().get_full_path().w_string());
00962 }
00963 }
00964
00965
00966 model_part::~model_part()
00967 {
00968
00969 }
00970
00971
00972 data::list<platform::vertex_buffer *> & model_part::get_inertial_triangles()
00973 {
00974 assert(inertial);
00975
00976 if (inertial_triangles.size() == 0)
00977 {
00978 for (simple_array<submesh *>::iterator i = inertial->get_submeshes().iter(); i.is_valid(); ++i)
00979 {
00980 inertial_triangles.append( & (*i)->triangle_vertices );
00981 }
00982 }
00983
00984 return inertial_triangles;
00985 }
00986
00987
00988 data::list<platform::vertex_buffer *> & model_part::get_collision_triangles()
00989 {
00990 assert(collision);
00991
00992 if (collision_triangles.size() == 0)
00993 {
00994 for (simple_array<submesh *>::iterator i = collision->get_submeshes().iter(); i.is_valid(); ++i)
00995 {
00996 collision_triangles.append( & (*i)->triangle_vertices );
00997 }
00998 }
00999
01000 return collision_triangles;
01001 }
01002
01003
01004
01005
01006
01007 static int num_models = 0;
01008
01009
01010 model::model(const config_record & obj_config)
01011 : node(string::format(L"model_%d", num_models++), 0)
01012 {
01013 for (list<config_record>::const_iterator i = obj_config.get_children().iter(); i.is_valid(); ++i)
01014 {
01015 if (i->get_name() == L"model_part")
01016 {
01017 model_part *mp = new model_part(*i);
01018
01019 mp->get_name() = string::format(L"%ls: %ls", get_name().w_string(), mp->get_name().w_string());
01020 model_parts.append(mp);
01021 add_child(mp);
01022 }
01023 }
01024 }
01025
01026
01027 model::~model()
01028 {
01029 }
01030
01031
01032 data::list<platform::vertex_buffer *> & model::get_inertial_triangles()
01033 {
01034 if (inertial_triangles.size() == 0)
01035 {
01036 for (simple_array<model_part *>::iterator i = model_parts.iter(); i.is_valid(); ++i)
01037 {
01038 inertial_triangles.append( (*i)->get_inertial_triangles() );
01039 }
01040 }
01041
01042 return inertial_triangles;
01043 }
01044
01045
01046 data::list<platform::vertex_buffer *> & model::get_collision_triangles()
01047 {
01048 if (collision_triangles.size() == 0)
01049 {
01050 for (simple_array<model_part *>::iterator i = model_parts.iter(); i.is_valid(); ++i)
01051 {
01052 collision_triangles.append( (*i)->get_collision_triangles() );
01053 }
01054 }
01055
01056 return collision_triangles;
01057 }
01058
01059
01060
01061 gsgl::data_object *model::create_global_model_cache()
01062 {
01063 return new mesh_file_cache(L"mesh file cache");
01064 }
01065
01066
01067 }
01068
01069
01070
01071 scenegraph::material_cache *scenegraph::material_cache::instance;
01072
01073
01074 scenegraph::mesh_file_cache *scenegraph::mesh_file_cache::instance;
01075
01076 }