]> git.tdb.fi Git - libs/gl.git/blob - source/render/object.cpp
Check the flat qualifier from the correct member
[libs/gl.git] / source / render / object.cpp
1 #include <msp/datafile/collection.h>
2 #include <msp/fs/utils.h>
3 #include <msp/strings/format.h>
4 #include "error.h"
5 #include "mesh.h"
6 #include "object.h"
7 #include "objectinstance.h"
8 #include "renderer.h"
9 #include "resourcemanager.h"
10 #include "technique.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace GL {
16
17 const Matrix Object::identity_matrix;
18
19 Object::Object():
20         lods(1)
21 { }
22
23 Object::Object(const Mesh *m, const Technique *t):
24         Object()
25 {
26         set_mesh(m);
27         set_technique(t);
28 }
29
30 Object::Object(const Object &other):
31         lods(other.lods),
32         bounding_sphere(other.bounding_sphere)
33 {
34         if(other.lod0_watched)
35                 watch_lod0();
36 }
37
38 Object::Object(Object &&other):
39         lods(move(other.lods)),
40         bounding_sphere(move(other.bounding_sphere))
41 {
42         if(other.lod0_watched)
43                 watch_lod0();
44 }
45
46 Object::~Object()
47 {
48         if(lods[0].mesh && lod0_watched)
49                 if(ResourceManager *rm = lods[0].mesh->get_manager())
50                         rm->unobserve_resource(*lods[0].mesh, *this);
51 }
52
53 Object::LevelOfDetail &Object::get_lod(unsigned i, const char *caller)
54 {
55         if(i>lods.size())
56                 throw out_of_range(caller);
57         if(i>0 && (!lods[0].mesh || !lods[0].technique))
58                 throw invalid_operation(caller);
59
60         if(i==lods.size())
61                 lods.push_back(lods.back());
62
63         return lods[i];
64 }
65
66 void Object::set_mesh(unsigned i, const Mesh *m)
67 {
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);
72         ptr = m;
73         lod0_watched = false;
74
75         if(i==0 && m)
76                 watch_lod0();
77
78         update_bounding_sphere();
79 }
80
81 void Object::watch_lod0()
82 {
83         if(ResourceManager *rm = lods[0].mesh->get_manager())
84         {
85                 rm->observe_resource(*lods[0].mesh, *this);
86                 lod0_watched = true;
87         }
88 }
89
90 void Object::update_bounding_sphere()
91 {
92         vector<Vector3> points;
93         for(const LevelOfDetail &l: lods)
94         {
95                 if(!l.mesh || !l.mesh->is_loaded())
96                         continue;
97
98                 const VertexArray &vertices = l.mesh->get_vertices();
99
100                 int offset = vertices.get_format().offset(VERTEX3);
101                 bool three = true;
102                 if(offset<0)
103                 {
104                         offset = vertices.get_format().offset(VERTEX2);
105                         three = false;
106                         if(offset<0)
107                                 continue;
108                 }
109
110                 unsigned n_vertices = vertices.size();
111                 points.reserve(points.size()+n_vertices);
112                 for(unsigned j=0; j<n_vertices; ++j)
113                 {
114                         const float *v = reinterpret_cast<const float *>(vertices[j]+offset);
115                         points.emplace_back(v[0], v[1], (three ? v[2] : 0.0f));
116                 }
117         }
118
119         /* Don't touch the bounding sphere if we had no vertices to avoid
120         overwriting a possible hint. */
121         if(points.empty())
122                 return;
123
124         bounding_sphere = Geometry::BoundingSphere<float, 3>::from_point_cloud(points.begin(), points.end());
125 }
126
127 const Mesh *Object::get_mesh(unsigned i) const
128 {
129         if(i>=lods.size())
130                 return 0;
131
132         return lods[i].mesh;
133 }
134
135 void Object::set_technique(unsigned i, const Technique *t)
136 {
137         get_lod(i, "Object::set_technique").technique = t;
138 }
139
140 const Technique *Object::get_technique(unsigned i) const
141 {
142         if(i>=lods.size())
143                 return 0;
144
145         return lods[i].technique;
146 }
147
148 void Object::render(Renderer &renderer, Tag tag) const
149 {
150         const RenderMethod *method = get_method(tag, 0);
151         if(!method)
152                 return;
153
154         const Mesh *mesh = lods.front().mesh;
155         if (!mesh)
156                 throw logic_error("no mesh");
157
158         Renderer::Push push(renderer);
159         method->apply(renderer);
160
161         setup_render(renderer, tag);
162         mesh->draw(renderer);
163         finish_render(renderer, tag);
164 }
165
166 void Object::render(Renderer &renderer, const ObjectInstance &inst, Tag tag) const
167 {
168         unsigned lod = min<unsigned>(inst.get_level_of_detail(renderer), lods.size()-1);
169         const RenderMethod *method = get_method(tag, lod);
170         if(!method)
171                 return;
172
173         const Mesh *mesh = lods[lod].mesh;
174         if (!mesh)
175                 throw logic_error("no mesh");
176
177         Renderer::Push push(renderer);
178         method->apply(renderer);
179
180         setup_render(renderer, tag);
181         inst.setup_render(renderer, tag);
182         mesh->draw(renderer);
183         inst.finish_render(renderer, tag);
184         finish_render(renderer, tag);
185 }
186
187 const RenderMethod *Object::get_method(Tag tag, unsigned lod) const
188 {
189         const Technique *tech = lods[lod].technique;
190         if(!tech)
191                 throw logic_error("no technique");
192         return tech->find_method(tag);
193 }
194
195 void Object::resource_loaded(Resource &res)
196 {
197         if(&res==lods.front().mesh && bounding_sphere.is_empty())
198                 update_bounding_sphere();
199 }
200
201 void Object::resource_removed(Resource &res)
202 {
203         if(&res==lods.front().mesh)
204                 lod0_watched = false;
205 }
206
207
208 Object::Loader::Loader(Object &o, Collection &c):
209         LodLoader(o, 0, c)
210 {
211         add("bounding_sphere_hint", &Loader::bounding_sphere_hint);
212         add("level_of_detail", &Loader::level_of_detail);
213 }
214
215 void Object::Loader::finish()
216 {
217         obj.update_bounding_sphere();
218 }
219
220 void Object::Loader::bounding_sphere_hint(float x, float y, float z, float r)
221 {
222         obj.bounding_sphere = Geometry::BoundingSphere<float, 3>(Vector3(x, y, z), r);
223 }
224
225 void Object::Loader::level_of_detail(unsigned i)
226 {
227         LodLoader ldr(obj, i, get_collection());
228         load_sub_with(ldr);
229 }
230
231
232 Object::LodLoader::LodLoader(Object &o, unsigned i, Collection &c):
233         DataFile::CollectionObjectLoader<Object>(o, &c),
234         index(i),
235         lod(obj.get_lod(index, "Object::LodLoader::LodLoader"))
236 {
237         add("mesh",      &LodLoader::mesh_inline);
238         add("mesh",      &LodLoader::mesh);
239         add("technique", &LodLoader::technique_inline);
240         add("technique", &LodLoader::technique);
241 }
242
243 void Object::LodLoader::mesh(const string &n)
244 {
245         obj.set_mesh(index, &get_collection().get<Mesh>(n));
246 }
247
248 void Object::LodLoader::mesh_inline()
249 {
250         RefPtr<Mesh> msh = new Mesh;
251         load_sub(*msh);
252         get_collection().add(format("%s/lod%d.mesh", FS::basename(get_source()), index), msh.get());
253         lod.mesh = msh.release();
254 }
255
256 void Object::LodLoader::technique(const string &n)
257 {
258         obj.set_technique(index, &get_collection().get<Technique>(n));
259 }
260
261 void Object::LodLoader::technique_inline()
262 {
263         RefPtr<Technique> tech = new Technique;
264         Technique::Loader ldr(*tech, get_collection());
265         string name = format("%s/lod%d.tech", FS::basename(get_source()), index);
266         ldr.set_inline_base_name(name);
267         load_sub(*tech, get_collection());
268         get_collection().add(name, tech.get());
269         lod.technique = tech.release();
270 }
271
272 } // namespace GL
273 } // namespace Msp