1 #include <msp/datafile/collection.h>
2 #include <msp/fs/utils.h>
3 #include <msp/strings/format.h>
7 #include "objectinstance.h"
9 #include "resourcemanager.h"
10 #include "technique.h"
17 const Matrix Object::identity_matrix;
23 Object::Object(const Mesh *m, const Technique *t):
30 Object::Object(const Object &other):
32 bounding_sphere(other.bounding_sphere)
34 if(other.lod0_watched)
38 Object::Object(Object &&other):
39 lods(move(other.lods)),
40 bounding_sphere(move(other.bounding_sphere))
42 if(other.lod0_watched)
48 if(lods[0].mesh && lod0_watched)
49 if(ResourceManager *rm = lods[0].mesh->get_manager())
50 rm->unobserve_resource(*lods[0].mesh, *this);
53 Object::LevelOfDetail &Object::get_lod(unsigned i, const char *caller)
56 throw out_of_range(caller);
57 if(i>0 && (!lods[0].mesh || !lods[0].technique))
58 throw invalid_operation(caller);
61 lods.push_back(lods.back());
66 void Object::set_mesh(unsigned i, const Mesh *m)
68 const Mesh *&ptr = get_lod(i, "Object::set_mesh").mesh;
69 if(i==0 && ptr && lod0_watched)
70 if(ResourceManager *rm = ptr->get_manager())
71 rm->unobserve_resource(*ptr, *this);
78 update_bounding_sphere();
81 void Object::watch_lod0()
83 if(ResourceManager *rm = lods[0].mesh->get_manager())
85 rm->observe_resource(*lods[0].mesh, *this);
90 void Object::update_bounding_sphere()
92 vector<Vector3> points;
93 for(const LevelOfDetail &l: lods)
95 if(!l.mesh || !l.mesh->is_loaded())
98 const VertexArray &vertices = l.mesh->get_vertices();
100 int offset = vertices.get_format().offset(VERTEX3);
104 offset = vertices.get_format().offset(VERTEX2);
110 unsigned n_vertices = vertices.size();
111 points.reserve(points.size()+n_vertices);
112 for(unsigned j=0; j<n_vertices; ++j)
114 const float *v = reinterpret_cast<const float *>(vertices[j]+offset);
115 points.emplace_back(v[0], v[1], (three ? v[2] : 0.0f));
119 /* Don't touch the bounding sphere if we had no vertices to avoid
120 overwriting a possible hint. */
124 bounding_sphere = Geometry::BoundingSphere<float, 3>::from_point_cloud(points.begin(), points.end());
127 const Mesh *Object::get_mesh(unsigned i) const
135 void Object::set_technique(unsigned i, const Technique *t)
137 get_lod(i, "Object::set_technique").technique = t;
140 const Technique *Object::get_technique(unsigned i) const
145 return lods[i].technique;
148 void Object::render(Renderer &renderer, Tag tag) const
150 const RenderMethod *method = get_method(tag, 0);
154 const Mesh *mesh = lods.front().mesh;
156 throw logic_error("no mesh");
158 Renderer::Push push(renderer);
159 renderer.set_pipeline_key(this, tag.id);
160 method->apply(renderer);
162 setup_render(renderer, tag);
163 mesh->draw(renderer);
164 finish_render(renderer, tag);
167 void Object::render(Renderer &renderer, const ObjectInstance &inst, Tag tag) const
169 unsigned lod = min<unsigned>(inst.get_level_of_detail(renderer), lods.size()-1);
170 const RenderMethod *method = get_method(tag, lod);
174 const Mesh *mesh = lods[lod].mesh;
176 throw logic_error("no mesh");
178 Renderer::Push push(renderer);
179 renderer.set_pipeline_key(this, tag.id);
180 method->apply(renderer);
182 setup_render(renderer, tag);
183 inst.setup_render(renderer, tag);
184 mesh->draw(renderer);
185 inst.finish_render(renderer, tag);
186 finish_render(renderer, tag);
189 const RenderMethod *Object::get_method(Tag tag, unsigned lod) const
191 const Technique *tech = lods[lod].technique;
193 throw logic_error("no technique");
194 return tech->find_method(tag);
197 void Object::resource_loaded(Resource &res)
199 if(&res==lods.front().mesh && bounding_sphere.is_empty())
200 update_bounding_sphere();
203 void Object::resource_removed(Resource &res)
205 if(&res==lods.front().mesh)
206 lod0_watched = false;
210 Object::Loader::Loader(Object &o, Collection &c):
213 add("bounding_sphere_hint", &Loader::bounding_sphere_hint);
214 add("level_of_detail", &Loader::level_of_detail);
217 void Object::Loader::finish()
219 obj.update_bounding_sphere();
222 void Object::Loader::bounding_sphere_hint(float x, float y, float z, float r)
224 obj.bounding_sphere = Geometry::BoundingSphere<float, 3>(Vector3(x, y, z), r);
227 void Object::Loader::level_of_detail(unsigned i)
229 LodLoader ldr(obj, i, get_collection());
234 Object::LodLoader::LodLoader(Object &o, unsigned i, Collection &c):
235 DataFile::CollectionObjectLoader<Object>(o, &c),
237 lod(obj.get_lod(index, "Object::LodLoader::LodLoader"))
239 add("mesh", &LodLoader::mesh_inline);
240 add("mesh", &LodLoader::mesh);
241 add("technique", &LodLoader::technique_inline);
242 add("technique", &LodLoader::technique);
245 void Object::LodLoader::mesh(const string &n)
247 obj.set_mesh(index, &get_collection().get<Mesh>(n));
250 void Object::LodLoader::mesh_inline()
252 RefPtr<Mesh> msh = new Mesh;
254 get_collection().add(format("%s/lod%d.mesh", FS::basename(get_source()), index), msh.get());
255 lod.mesh = msh.release();
258 void Object::LodLoader::technique(const string &n)
260 obj.set_technique(index, &get_collection().get<Technique>(n));
263 void Object::LodLoader::technique_inline()
265 RefPtr<Technique> tech = new Technique;
266 Technique::Loader ldr(*tech, get_collection());
267 string name = format("%s/lod%d.tech", FS::basename(get_source()), index);
268 ldr.set_inline_base_name(name);
269 load_sub(*tech, get_collection());
270 get_collection().add(name, tech.get());
271 lod.technique = tech.release();