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