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