]> git.tdb.fi Git - libs/gl.git/blob - source/render/object.cpp
Refactor batch setup in Renderer to a helper function
[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         renderer.set_pipeline_key(this, tag.id);
160         method->apply(renderer);
161
162         setup_render(renderer, tag);
163         mesh->draw(renderer);
164         finish_render(renderer, tag);
165 }
166
167 void Object::render(Renderer &renderer, const ObjectInstance &inst, Tag tag) const
168 {
169         unsigned lod = min<unsigned>(inst.get_level_of_detail(renderer), lods.size()-1);
170         const RenderMethod *method = get_method(tag, lod);
171         if(!method)
172                 return;
173
174         const Mesh *mesh = lods[lod].mesh;
175         if (!mesh)
176                 throw logic_error("no mesh");
177
178         Renderer::Push push(renderer);
179         renderer.set_pipeline_key(this, tag.id);
180         method->apply(renderer);
181
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);
187 }
188
189 const RenderMethod *Object::get_method(Tag tag, unsigned lod) const
190 {
191         const Technique *tech = lods[lod].technique;
192         if(!tech)
193                 throw logic_error("no technique");
194         return tech->find_method(tag);
195 }
196
197 void Object::resource_loaded(Resource &res)
198 {
199         if(&res==lods.front().mesh && bounding_sphere.is_empty())
200                 update_bounding_sphere();
201 }
202
203 void Object::resource_removed(Resource &res)
204 {
205         if(&res==lods.front().mesh)
206                 lod0_watched = false;
207 }
208
209
210 Object::Loader::Loader(Object &o, Collection &c):
211         LodLoader(o, 0, c)
212 {
213         add("bounding_sphere_hint", &Loader::bounding_sphere_hint);
214         add("level_of_detail", &Loader::level_of_detail);
215 }
216
217 void Object::Loader::finish()
218 {
219         obj.update_bounding_sphere();
220 }
221
222 void Object::Loader::bounding_sphere_hint(float x, float y, float z, float r)
223 {
224         obj.bounding_sphere = Geometry::BoundingSphere<float, 3>(Vector3(x, y, z), r);
225 }
226
227 void Object::Loader::level_of_detail(unsigned i)
228 {
229         LodLoader ldr(obj, i, get_collection());
230         load_sub_with(ldr);
231 }
232
233
234 Object::LodLoader::LodLoader(Object &o, unsigned i, Collection &c):
235         DataFile::CollectionObjectLoader<Object>(o, &c),
236         index(i),
237         lod(obj.get_lod(index, "Object::LodLoader::LodLoader"))
238 {
239         add("mesh",      &LodLoader::mesh_inline);
240         add("mesh",      &LodLoader::mesh);
241         add("technique", &LodLoader::technique_inline);
242         add("technique", &LodLoader::technique);
243 }
244
245 void Object::LodLoader::mesh(const string &n)
246 {
247         obj.set_mesh(index, &get_collection().get<Mesh>(n));
248 }
249
250 void Object::LodLoader::mesh_inline()
251 {
252         RefPtr<Mesh> msh = new Mesh;
253         load_sub(*msh);
254         get_collection().add(format("%s/lod%d.mesh", FS::basename(get_source()), index), msh.get());
255         lod.mesh = msh.release();
256 }
257
258 void Object::LodLoader::technique(const string &n)
259 {
260         obj.set_technique(index, &get_collection().get<Technique>(n));
261 }
262
263 void Object::LodLoader::technique_inline()
264 {
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();
272 }
273
274 } // namespace GL
275 } // namespace Msp