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