From: Mikko Rasa Date: Sat, 6 Dec 2014 12:38:49 +0000 (+0200) Subject: Support different techniques per LoD in objects X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=5b532dcb2a4a2d15d0b6607374284c979ece36a8;p=libs%2Fgl.git Support different techniques per LoD in objects --- diff --git a/source/object.cpp b/source/object.cpp index d48b0843..fe1690d2 100644 --- a/source/object.cpp +++ b/source/object.cpp @@ -1,5 +1,6 @@ #include #include +#include "error.h" #include "material.h" #include "mesh.h" #include "object.h" @@ -17,7 +18,7 @@ namespace Msp { namespace GL { Object::Object(): - meshes(1) + lods(1) { } Object::Object(const Mesh *m, const Technique *t) @@ -26,29 +27,34 @@ Object::Object(const Mesh *m, const Technique *t) set_technique(t); } -// Avoid synthesizing ~RefPtr in files including object.h Object::~Object() { - if(meshes[0]) - if(ResourceManager *rm = meshes[0]->get_manager()) - rm->unwatch_resource(*meshes[0], *this); + if(lods[0].mesh) + if(ResourceManager *rm = lods[0].mesh->get_manager()) + rm->unwatch_resource(*lods[0].mesh, *this); } -void Object::set_mesh(unsigned i, const Mesh *m) +Object::LevelOfDetail &Object::get_lod(unsigned i, const char *caller) { - if(i>meshes.size()) - throw out_of_range("Object::set_mesh"); + if(i>lods.size()) + throw out_of_range(caller); + if(i>0 && (!lods[0].mesh || !lods[0].technique)) + throw invalid_operation(caller); - if(i==meshes.size()) - meshes.push_back(m); - else - { - if(i==0 && meshes[i]) - if(ResourceManager *rm = meshes[i]->get_manager()) - rm->unwatch_resource(*meshes[i], *this); - meshes[i] = m; - } - meshes[i].keep(); + if(i==lods.size()) + lods.push_back(lods.back()); + + return lods[i]; +} + +void Object::set_mesh(unsigned i, const Mesh *m) +{ + RefPtr &ptr = get_lod(i, "Object::set_mesh").mesh; + if(i==0 && ptr) + if(ResourceManager *rm = ptr->get_manager()) + rm->unwatch_resource(*ptr, *this); + ptr = m; + ptr.keep(); if(i==0 && m) if(ResourceManager *rm = m->get_manager()) @@ -60,12 +66,12 @@ void Object::set_mesh(unsigned i, const Mesh *m) void Object::update_bounding_sphere() { vector points; - for(vector >::const_iterator i=meshes.begin(); i!=meshes.end(); ++i) + for(vector::const_iterator i=lods.begin(); i!=lods.end(); ++i) { - if(!*i) + if(!i->mesh) continue; - const VertexArray &vertices = (*i)->get_vertices(); + const VertexArray &vertices = i->mesh->get_vertices(); int offset = vertices.get_format().offset(VERTEX3); bool three = true; @@ -91,21 +97,30 @@ void Object::update_bounding_sphere() const Mesh *Object::get_mesh(unsigned i) const { - if(i>=meshes.size()) + if(i>=lods.size()) return 0; - return meshes[i].get(); + return lods[i].mesh.get(); } -void Object::set_technique(const Technique *t) +void Object::set_technique(unsigned i, const Technique *t) { - technique = t; - technique.keep(); + RefPtr &ptr = get_lod(i, "Object::set_technique").technique; + ptr = t; + ptr.keep(); +} + +const Technique *Object::get_technique(unsigned i) const +{ + if(i>=lods.size()) + return 0; + + return lods[i].technique.get(); } void Object::render(const Tag &tag) const { - const RenderPass *pass = get_pass(tag); + const RenderPass *pass = get_pass(tag, 0); if(!pass) return; @@ -115,12 +130,12 @@ void Object::render(const Tag &tag) const Bind bind_material(pass->get_material()); Bind bind_texturing(pass->get_texturing()); - meshes.front()->draw(); + lods.front().mesh->draw(); } void Object::render(Renderer &renderer, const Tag &tag) const { - const RenderPass *pass = get_pass(tag); + const RenderPass *pass = get_pass(tag, 0); if(!pass) return; @@ -128,13 +143,14 @@ void Object::render(Renderer &renderer, const Tag &tag) const pass->apply(renderer); setup_render(renderer, tag); - meshes.front()->draw(renderer); + lods.front().mesh->draw(renderer); finish_render(renderer, tag); } void Object::render(Renderer &renderer, const ObjectInstance &inst, const Tag &tag) const { - const RenderPass *pass = get_pass(tag); + unsigned lod = min(inst.get_level_of_detail(renderer), lods.size()-1); + const RenderPass *pass = get_pass(tag, lod); if(!pass) return; @@ -143,48 +159,43 @@ void Object::render(Renderer &renderer, const ObjectInstance &inst, const Tag &t setup_render(renderer, tag); inst.setup_render(renderer, tag); - unsigned lod = min(inst.get_level_of_detail(renderer), meshes.size()-1); - meshes[lod]->draw(renderer); + lods[lod].mesh->draw(renderer); inst.finish_render(renderer, tag); finish_render(renderer, tag); } -const RenderPass *Object::get_pass(const Tag &tag) const +const RenderPass *Object::get_pass(const Tag &tag, unsigned lod) const { - if(!technique) - throw logic_error("!technique"); - if(!technique->has_pass(tag)) + const Technique *tech = lods[lod].technique.get(); + if(!tech) + throw logic_error("no technique"); + if(!tech->has_pass(tag)) return 0; - return &technique->get_pass(tag); + return &tech->get_pass(tag); } void Object::resource_loaded(Resource &res) { - if(!meshes.empty() && &res==meshes.front().get() && bounding_sphere.is_empty()) + if(&res==lods.front().mesh.get() && bounding_sphere.is_empty()) update_bounding_sphere(); } Object::Loader::Loader(Object &o): - DataFile::CollectionObjectLoader(o, 0) + LodLoader(o, 0, 0) { init(); } Object::Loader::Loader(Object &o, Collection &c): - DataFile::CollectionObjectLoader(o, &c) + LodLoader(o, 0, &c) { init(); } void Object::Loader::init() { - add("mesh", &Loader::mesh_inline); - add("mesh", &Loader::mesh_inline_lod); - add("mesh", &Loader::mesh); - add("mesh", &Loader::mesh_lod); - add("technique", &Loader::technique_inline); - add("technique", &Loader::technique); + add("level_of_detail", &Loader::level_of_detail); } void Object::Loader::finish() @@ -192,49 +203,49 @@ void Object::Loader::finish() obj.update_bounding_sphere(); } -void Object::Loader::mesh_inline() +void Object::Loader::level_of_detail(unsigned i) { - RefPtr msh = new Mesh; - load_sub(*msh); - obj.meshes.front() = msh; + LodLoader ldr(obj, i, coll); + load_sub_with(ldr); } -void Object::Loader::mesh_inline_lod(unsigned l) + +Object::LodLoader::LodLoader(Object &o, unsigned i, Collection *c): + DataFile::CollectionObjectLoader(o, c), + index(i), + lod(obj.get_lod(index, "Object::LodLoader::LodLoader")) { - if(l>obj.meshes.size()) - throw out_of_range("Object::Loader::mesh_inline_lod"); + add("mesh", &LodLoader::mesh_inline); + add("mesh", &LodLoader::mesh); + add("technique", &LodLoader::technique_inline); + add("technique", &LodLoader::technique); +} - RefPtr msh = new Mesh; - load_sub(*msh); - if(l==obj.meshes.size()) - obj.meshes.push_back(msh); - else - obj.meshes[l] = msh; +void Object::LodLoader::mesh(const string &n) +{ + obj.set_mesh(index, &get_collection().get(n)); } -void Object::Loader::mesh(const std::string &n) +void Object::LodLoader::mesh_inline() { - obj.set_mesh(&get_collection().get(n)); + RefPtr msh = new Mesh; + load_sub(*msh); + lod.mesh = msh; } -void Object::Loader::mesh_lod(unsigned l, const string &n) +void Object::LodLoader::technique(const std::string &n) { - obj.set_mesh(l, &get_collection().get(n)); + obj.set_technique(index, &get_collection().get(n)); } -void Object::Loader::technique_inline() +void Object::LodLoader::technique_inline() { RefPtr tech = new Technique; if(coll) load_sub(*tech, get_collection()); else load_sub(*tech); - obj.technique = tech; -} - -void Object::Loader::technique(const std::string &n) -{ - obj.set_technique(&get_collection().get(n)); + lod.technique = tech; } } // namespace GL diff --git a/source/object.h b/source/object.h index f432177e..95ba8fab 100644 --- a/source/object.h +++ b/source/object.h @@ -23,11 +23,34 @@ define which render passes the Object supports. In many cases, it's desirable to include multiple copies of an Object in a Scene, with different model matrices. ObjectInstances can be used to alter the rendering of an object on a per-instance basis. + +Objects can have multiple levels of detail. The most detailed level has index +0, with increasing indices having less detail. When rendering an instance, the +instance's get_level_of_detail method is called to determine which LoD to use. */ class Object: public Renderable, private ResourceWatcher { +private: + struct LevelOfDetail; + + class LodLoader: public DataFile::CollectionObjectLoader + { + private: + unsigned index; + LevelOfDetail &lod; + + public: + LodLoader(Object &, unsigned, Collection *); + + private: + void mesh(const std::string &); + void mesh_inline(); + void technique(const std::string &); + void technique_inline(); + }; + public: - class Loader: public DataFile::CollectionObjectLoader + class Loader: public LodLoader { public: Loader(Object &); @@ -36,17 +59,17 @@ public: void init(); virtual void finish(); - void mesh_inline(); - void mesh_inline_lod(unsigned); - void mesh(const std::string &); - void mesh_lod(unsigned, const std::string &); - void technique_inline(); - void technique(const std::string &); + void level_of_detail(unsigned); }; private: - std::vector > meshes; - RefPtr technique; + struct LevelOfDetail + { + RefPtr mesh; + RefPtr technique; + }; + + std::vector lods; Geometry::BoundingSphere bounding_sphere; public: @@ -54,14 +77,30 @@ public: Object(const Mesh *, const Technique *); ~Object(); +private: + LevelOfDetail &get_lod(unsigned, const char *); + +public: + /** Sets the mesh for the highest level of detail (index 0). */ void set_mesh(const Mesh *m) { set_mesh(0, m); } + + /** Sets the mesh for a given level of detail. Previous LoDs must have been + defined. */ void set_mesh(unsigned, const Mesh *); + private: void update_bounding_sphere(); public: const Mesh *get_mesh(unsigned = 0) const; - void set_technique(const Technique *); - const Technique *get_technique() const { return technique.get(); } + + /** Sets the technique for the highest level of detail (index 0). */ + void set_technique(const Technique *t) { set_technique(0, t); } + + /** Sets the technique for a given level of detail. Previous LoDs must have + been defined. */ + void set_technique(unsigned, const Technique *); + + const Technique *get_technique(unsigned = 0) const; virtual const Geometry::BoundingSphere *get_bounding_sphere() const { return &bounding_sphere; } @@ -78,7 +117,7 @@ protected: virtual void finish_render(Renderer &, const Tag &) const { } private: - const RenderPass *get_pass(const Tag &) const; + const RenderPass *get_pass(const Tag &, unsigned) const; virtual void resource_loaded(Resource &); };