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