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