]> git.tdb.fi Git - libs/gl.git/blob - source/render/object.cpp
ccc7b97ecdd0086db8f6b6029fc9546e94b82771
[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 "mesh.h"
6 #include "object.h"
7 #include "objectinstance.h"
8 #include "renderer.h"
9 #include "resourcemanager.h"
10 #include "technique.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace GL {
16
17 const Matrix Object::identity_matrix;
18
19 Object::Object():
20         lods(1),
21         lod0_watched(false)
22 { }
23
24 Object::Object(const Mesh *m, const Technique *t):
25         lods(1),
26         lod0_watched(false)
27 {
28         set_mesh(m);
29         set_technique(t);
30 }
31
32 // TODO should have copy-c'tor to set watch on lod0 mesh if necessary
33
34 Object::~Object()
35 {
36         if(lods[0].mesh && lod0_watched)
37                 if(ResourceManager *rm = lods[0].mesh->get_manager())
38                         rm->unobserve_resource(*lods[0].mesh, *this);
39 }
40
41 Object::LevelOfDetail &Object::get_lod(unsigned i, const char *caller)
42 {
43         if(i>lods.size())
44                 throw out_of_range(caller);
45         if(i>0 && (!lods[0].mesh || !lods[0].technique))
46                 throw invalid_operation(caller);
47
48         if(i==lods.size())
49                 lods.push_back(lods.back());
50
51         return lods[i];
52 }
53
54 void Object::set_mesh(unsigned i, const Mesh *m)
55 {
56         const Mesh *&ptr = get_lod(i, "Object::set_mesh").mesh;
57         if(i==0 && ptr && lod0_watched)
58                 if(ResourceManager *rm = ptr->get_manager())
59                         rm->unobserve_resource(*ptr, *this);
60         ptr = m;
61         lod0_watched = false;
62
63         if(i==0 && m)
64                 if(ResourceManager *rm = m->get_manager())
65                 {
66                         rm->observe_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(const LevelOfDetail &l: lods)
77         {
78                 if(!l.mesh || !l.mesh->is_loaded())
79                         continue;
80
81                 const VertexArray &vertices = l.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 = reinterpret_cast<const float *>(vertices[j]+offset);
98                         points.push_back(Vector3(v[0], v[1], (three ? v[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;
116 }
117
118 void Object::set_technique(unsigned i, const Technique *t)
119 {
120         get_lod(i, "Object::set_technique").technique = t;
121 }
122
123 const Technique *Object::get_technique(unsigned i) const
124 {
125         if(i>=lods.size())
126                 return 0;
127
128         return lods[i].technique;
129 }
130
131 void Object::render(Renderer &renderer, Tag tag) const
132 {
133         const RenderMethod *method = get_method(tag, 0);
134         if(!method)
135                 return;
136
137         const Mesh *mesh = lods.front().mesh;
138         if (!mesh)
139                 throw logic_error("no mesh");
140
141         Renderer::Push push(renderer);
142         method->apply(renderer);
143
144         setup_render(renderer, tag);
145         mesh->draw(renderer);
146         finish_render(renderer, tag);
147 }
148
149 void Object::render(Renderer &renderer, const ObjectInstance &inst, Tag tag) const
150 {
151         unsigned lod = min<unsigned>(inst.get_level_of_detail(renderer), lods.size()-1);
152         const RenderMethod *method = get_method(tag, lod);
153         if(!method)
154                 return;
155
156         const Mesh *mesh = lods[lod].mesh;
157         if (!mesh)
158                 throw logic_error("no mesh");
159
160         Renderer::Push push(renderer);
161         method->apply(renderer);
162
163         setup_render(renderer, tag);
164         inst.setup_render(renderer, tag);
165         mesh->draw(renderer);
166         inst.finish_render(renderer, tag);
167         finish_render(renderer, tag);
168 }
169
170 const RenderMethod *Object::get_method(Tag tag, unsigned lod) const
171 {
172         const Technique *tech = lods[lod].technique;
173         if(!tech)
174                 throw logic_error("no technique");
175         return tech->find_method(tag);
176 }
177
178 void Object::resource_loaded(Resource &res)
179 {
180         if(&res==lods.front().mesh && bounding_sphere.is_empty())
181                 update_bounding_sphere();
182 }
183
184 void Object::resource_removed(Resource &res)
185 {
186         if(&res==lods.front().mesh)
187                 lod0_watched = false;
188 }
189
190
191 Object::Loader::Loader(Object &o, Collection &c):
192         LodLoader(o, 0, c)
193 {
194         add("bounding_sphere_hint", &Loader::bounding_sphere_hint);
195         add("level_of_detail", &Loader::level_of_detail);
196 }
197
198 void Object::Loader::finish()
199 {
200         obj.update_bounding_sphere();
201 }
202
203 void Object::Loader::bounding_sphere_hint(float x, float y, float z, float r)
204 {
205         obj.bounding_sphere = Geometry::BoundingSphere<float, 3>(Vector3(x, y, z), r);
206 }
207
208 void Object::Loader::level_of_detail(unsigned i)
209 {
210         LodLoader ldr(obj, i, get_collection());
211         load_sub_with(ldr);
212 }
213
214
215 Object::LodLoader::LodLoader(Object &o, unsigned i, Collection &c):
216         DataFile::CollectionObjectLoader<Object>(o, &c),
217         index(i),
218         lod(obj.get_lod(index, "Object::LodLoader::LodLoader"))
219 {
220         add("mesh",      &LodLoader::mesh_inline);
221         add("mesh",      &LodLoader::mesh);
222         add("technique", &LodLoader::technique_inline);
223         add("technique", &LodLoader::technique);
224 }
225
226 void Object::LodLoader::mesh(const string &n)
227 {
228         obj.set_mesh(index, &get_collection().get<Mesh>(n));
229 }
230
231 void Object::LodLoader::mesh_inline()
232 {
233         RefPtr<Mesh> msh = new Mesh;
234         load_sub(*msh);
235         get_collection().add(format("%s/lod%d.mesh", FS::basename(get_source()), index), msh.get());
236         lod.mesh = msh.release();
237 }
238
239 void Object::LodLoader::technique(const string &n)
240 {
241         obj.set_technique(index, &get_collection().get<Technique>(n));
242 }
243
244 void Object::LodLoader::technique_inline()
245 {
246         RefPtr<Technique> tech = new Technique;
247         Technique::Loader ldr(*tech, get_collection());
248         string name = format("%s/lod%d.tech", FS::basename(get_source()), index);
249         ldr.set_inline_base_name(name);
250         load_sub(*tech, get_collection());
251         get_collection().add(name, tech.get());
252         lod.technique = tech.release();
253 }
254
255 } // namespace GL
256 } // namespace Msp