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