]> Dogcows Code - chaz/yoink/blob - src/Moof/Scene.cc
better logging (maybe) and exception handling
[chaz/yoink] / src / Moof / Scene.cc
1
2 /*******************************************************************************
3
4 Copyright (c) 2009, Charles McGarvey
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 *******************************************************************************/
28
29 #include <iostream>
30 #include <map>
31 #include <vector>
32
33 #include "Aabb.hh"
34 #include "Camera.hh"
35 #include "Deserializer.hh"
36 #include "Entity.hh"
37 #include "Math.hh"
38 #include "Mippleton.hh"
39 #include "OpenGL.hh"
40 #include "Scene.hh"
41 #include "Serializable.hh"
42 #include "Tilemap.hh"
43
44
45 namespace Mf {
46
47
48 class Scene::Impl : public Mippleton<Impl>
49 {
50 class Quad : public Entity
51 {
52 public:
53 Quad(const Vector3 vertices[4], const std::string& texture,
54 Tilemap::Index tileIndex) :
55 tilemap_(texture),
56 detail_(0),
57 blending_(false),
58 fog_(false)
59 {
60 for (int i = 0, num = 0; i < 4; ++i)
61 {
62 for (int j = 0; j < 3; ++j, ++num)
63 {
64 vertices_[num] = vertices[i][j];
65 }
66 }
67
68 if (!tilemap_.getTileCoords(tileIndex, texCoords_))
69 {
70 std::cerr << "no coords for tile's texture" << std::endl;
71 }
72
73 aabb_.encloseVertices(vertices, 4);
74 sphere_.point = aabb_.getCenter();
75 sphere_.radius = (aabb_.min - sphere_.point).length();
76 }
77
78 void setDetail(long detail)
79 {
80 detail_ = detail;
81 }
82
83 void setBlending(bool blending)
84 {
85 blending_ = blending;
86 }
87
88 void setFog(bool fog)
89 {
90 fog_ = fog;
91 }
92
93 void draw(Scalar alpha = 0.0) const
94 {
95 if (blending_)
96 {
97 glEnable(GL_BLEND);
98 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
99 }
100
101 if (fog_)
102 {
103 glEnable(GL_FOG);
104 glFogi(GL_FOG_MODE, GL_LINEAR);
105 }
106
107 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
108 tilemap_.bind();
109
110 glVertexPointer(3, GL_SCALAR, 0, vertices_);
111 glTexCoordPointer(2, GL_SCALAR, 0, texCoords_);
112
113 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
114
115 glDisable(GL_BLEND);
116 glDisable(GL_FOG);
117 }
118
119 bool isVisible(const Camera& cam) const
120 {
121 return sphere_.isVisible(cam);
122 }
123
124 private:
125 Scalar vertices_[12];
126 Scalar texCoords_[8];
127
128 Tilemap tilemap_;
129
130 long detail_;
131 bool blending_;
132 bool fog_;
133 };
134
135
136 static void loadBox(Aabb& theBox, SerializablePtr obj)
137 {
138 std::vector<SerializablePtr> numbers;
139
140 if (obj->get(numbers) && numbers.size() == 6)
141 {
142 double num;
143
144 if (numbers[0]->getNumber(num)) theBox.min[0] = Scalar(num);
145 if (numbers[1]->getNumber(num)) theBox.min[1] = Scalar(num);
146 if (numbers[2]->getNumber(num)) theBox.min[2] = Scalar(num);
147 if (numbers[3]->getNumber(num)) theBox.max[0] = Scalar(num);
148 if (numbers[4]->getNumber(num)) theBox.max[1] = Scalar(num);
149 if (numbers[5]->getNumber(num)) theBox.max[2] = Scalar(num);
150 }
151 }
152
153 public:
154 Impl(const std::string& name) :
155 Mippleton<Impl>(name)
156 {
157 loadFromFile();
158 }
159
160
161 void loadInstructions(SerializablePtr root)
162 {
163 std::vector<SerializablePtr> rootObj;
164 std::vector<SerializablePtr>::iterator it;
165
166 if (!root->get(rootObj))
167 {
168 std::cerr << "error loading scene instructions" << std::endl;
169 return;
170 }
171
172 Matrix4 transform;
173 std::string texture;
174
175 for (it = rootObj.begin(); it != rootObj.end(); ++it)
176 {
177 std::string instruction;
178
179 if ((*it)->get(instruction))
180 {
181 if (instruction == "reset_transform")
182 {
183 transform.identity();
184 }
185 else if (instruction == "translate")
186 {
187 std::vector<SerializablePtr> values;
188
189 ++it;
190 if ((*it)->get(values))
191 {
192 Vector3 vec;
193
194 for (size_t i = 0; i < values.size(); ++i)
195 {
196 double value;
197
198 if (values[i]->getNumber(value))
199 {
200 vec[i] = value;
201 }
202 }
203
204 Matrix4 translation;
205 cml::matrix_translation(translation, vec);
206 transform = translation * transform;
207 }
208 }
209 else if (instruction == "scale")
210 {
211 std::vector<SerializablePtr> values;
212
213 ++it;
214 if ((*it)->get(values))
215 {
216 if (values.size() == 1)
217 {
218 double value = 1.0;
219
220 values[0]->getNumber(value);
221
222 Matrix4 scaling;
223 cml::matrix_uniform_scale(scaling,
224 Scalar(value));
225 transform = scaling * transform;
226 }
227 else if (values.size() == 3)
228 {
229 Vector3 vec;
230
231 for (size_t i = 0; i < values.size(); ++i)
232 {
233 double value;
234
235 if (values[i]->getNumber(value))
236 {
237 vec[i] = value;
238 }
239 }
240
241 Matrix4 scaling;
242 cml::matrix_scale(scaling, vec);
243 transform = scaling * transform;
244 }
245 }
246 }
247 else if (instruction == "rotate")
248 {
249 std::vector<SerializablePtr> values;
250
251 ++it;
252 if ((*it)->get(values))
253 {
254 if (values.size() == 2)
255 {
256 std::string axis;
257 size_t index = 0;
258 double value = 0.0;
259
260 if (values[0]->get(axis))
261 {
262 if (axis == "x") index = 0;
263 else if (axis == "y") index = 1;
264 else if (axis == "z") index = 2;
265
266 values[1]->getNumber(value);
267 }
268
269 cml::matrix_rotate_about_world_axis(transform,
270 index, cml::rad(Scalar(value)));
271 }
272 }
273 }
274 else if (instruction == "texture")
275 {
276 ++it;
277 (*it)->get(texture);
278 }
279 else if (instruction == "tilemap")
280 {
281 ++it;
282 loadTilemap(*it, transform, texture);
283 }
284 else if (instruction == "billboard")
285 {
286 ++it;
287 loadBillboard(*it, transform, texture);
288 }
289 }
290 }
291 }
292
293
294 void loadTilemap(SerializablePtr root, const Matrix4& transform,
295 const std::string& texture)
296 {
297 std::map<std::string,SerializablePtr> rootObj;
298 std::map<std::string,SerializablePtr>::iterator it;
299
300 if (!root->get(rootObj))
301 {
302 std::cerr << "error loading scene tilemap object" << std::endl;
303 return;
304 }
305
306 long width = 1;
307 long height = 1;
308 std::vector< std::vector<Tilemap::Index> > indices;
309
310 if ((it = rootObj.find("width")) != rootObj.end())
311 {
312 (*it).second->get(width);
313 }
314 else
315 {
316 std::cerr << "width is a required field of a tilemap" << std::endl;
317 return;
318 }
319
320 std::vector<SerializablePtr> tiles;
321
322 if ((it = rootObj.find("tiles")) != rootObj.end() &&
323 (*it).second->get(tiles) &&
324 tiles.size() % width == 0)
325 {
326 std::vector<SerializablePtr>::iterator jt;
327 int w, h;
328
329 height = tiles.size() / width;
330 indices.resize(height);
331
332 // the indices are stored upside-down in the scene file so that they
333 // are easier to edit as text, so we'll need to load them last row
334 // first
335
336 for (h = height - 1, jt = tiles.begin(); jt != tiles.end(); --h)
337 {
338 std::vector<Tilemap::Index> row;
339
340 for (w = 0; w < width && jt != tiles.end(); ++w, ++jt)
341 {
342 long index;
343
344 if ((*jt)->get(index))
345 {
346 row.push_back(Tilemap::Index(index));
347 }
348 }
349
350 indices[h] = row;
351 }
352 }
353 else
354 {
355 std::cerr << "error loading tiles from tilemap object" << std::endl;
356 return;
357 }
358
359 Vector4 vertices[height+1][width+1];
360
361 Matrix4 transposedTransform = transform;
362 transposedTransform.transpose();
363
364 for (int h = 0; h <= height; ++h)
365 {
366 for (int w = 0; w <= width; ++w)
367 {
368 vertices[h][w] = Vector4(Scalar(w), Scalar(h), 0.0, 1.0) *
369 transposedTransform;
370 }
371 }
372
373 for (int h = 0; h < height; ++h)
374 {
375 for (int w = 0; w < width; ++w)
376 {
377 if (indices[h][w] == Tilemap::NO_TILE) continue;
378
379 Vector3 quadVertices[4];
380
381 demoteVector(quadVertices[0], vertices[h][w]);
382 demoteVector(quadVertices[1], vertices[h][w+1]);
383 demoteVector(quadVertices[2], vertices[h+1][w+1]);
384 demoteVector(quadVertices[3], vertices[h+1][w]);
385
386 Quad* quad = new Quad(quadVertices, texture, indices[h][w]);
387 boost::shared_ptr<Quad> quadPtr(quad);
388
389 octree->insert(quadPtr);
390 }
391 }
392 }
393
394 void loadBillboard(SerializablePtr root, const Matrix4& transform,
395 const std::string& texture)
396 {
397 std::map<std::string,SerializablePtr> rootObj;
398 std::map<std::string,SerializablePtr>::iterator it;
399
400 Tilemap::Index index = 0;
401 long width = 1;
402 bool blending = false;
403 bool fog = false;
404
405 if (root->get(rootObj))
406 {
407 if ((it = rootObj.find("tile")) != rootObj.end())
408 {
409 long value;
410 if ((*it).second->get(value))
411 {
412 index = Tilemap::Index(value);
413 }
414 }
415
416 if ((it = rootObj.find("u_scale")) != rootObj.end())
417 {
418 (*it).second->get(width);
419 }
420
421 if ((it = rootObj.find("blend")) != rootObj.end())
422 {
423 (*it).second->get(blending);
424 }
425
426 if ((it = rootObj.find("fog")) != rootObj.end())
427 {
428 (*it).second->get(fog);
429 }
430 }
431
432
433 Vector4 vertices[2][width+1];
434
435 Matrix4 transposedTransform = transform;
436 transposedTransform.transpose();
437
438 Scalar xf;
439 Scalar increment = 1.0 / Scalar(width);
440
441 for (int h = 0; h <= 1; ++h)
442 {
443 xf = 0.0;
444 for (int w = 0; w <= width; ++w, xf += increment)
445 {
446 vertices[h][w] = Vector4(xf, Scalar(h), 0.0, 1.0) *
447 transposedTransform;
448 }
449 }
450
451 for (int w = 0; w < width; ++w)
452 {
453 Vector3 quadVertices[4];
454
455 demoteVector(quadVertices[0], vertices[0][w]);
456 demoteVector(quadVertices[1], vertices[0][w+1]);
457 demoteVector(quadVertices[2], vertices[1][w+1]);
458 demoteVector(quadVertices[3], vertices[1][w]);
459
460 Quad* quad = new Quad(quadVertices, texture, index);
461 quad->setBlending(blending);
462 quad->setFog(fog);
463
464 boost::shared_ptr<Quad> quadPtr(quad);
465
466 octree->insert(quadPtr);
467 }
468 }
469
470
471 void loadFromFile()
472 {
473 std::string filePath = Scene::getPath(getName());
474
475 Deserializer deserializer(filePath, true);
476 SerializablePtr root = deserializer.deserialize();
477
478 std::map<std::string,SerializablePtr> rootObj;
479 std::map<std::string,SerializablePtr>::iterator it;
480
481 if (!root || !root->get(rootObj))
482 {
483 std::cerr << "error loading scene file" << std::endl;
484 return;
485 }
486
487 if ((it = rootObj.find("playfield_bounds")) != rootObj.end())
488 {
489 loadBox(playfieldBounds, (*it).second);
490 }
491 if ((it = rootObj.find("maximum_bounds")) != rootObj.end())
492 {
493 loadBox(maximumBounds, (*it).second);
494 }
495 else
496 {
497 std::cerr << "maximum bounds required in scene" << std::endl;
498 return;
499 }
500
501 // create the tree to store the quads
502 octree = OctreePtr(new Octree(maximumBounds));
503
504 if ((it = rootObj.find("instructions")) != rootObj.end())
505 {
506 loadInstructions((*it).second);
507 }
508
509 octree->sort();
510 }
511
512
513 void draw(Scalar alpha, const Camera& cam) const
514 {
515 glEnableClientState(GL_VERTEX_ARRAY);
516 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
517
518 octree->drawIfVisible(alpha, cam);
519
520 //glDisableClientState(GL_VERTEX_ARRAY);
521 //glDisableClientState(GL_TEXTURE_COORD_ARRAY);
522
523 //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
524
525 //Texture::resetBind();
526 //glColor3f(0.0f, 1.0f, 0.0f);
527 //playfieldBounds.draw();
528 //glColor3f(0.0f, 0.0f, 1.0f);
529 //maximumBounds.draw();
530
531 //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
532 }
533
534
535 Aabb playfieldBounds;
536 Aabb maximumBounds;
537
538 OctreePtr octree;
539 };
540
541
542 Scene::Scene(const std::string& name) :
543 // pass through
544 impl_(Scene::Impl::getInstance(name)) {}
545
546
547 void Scene::draw(Scalar alpha, const Camera& cam) const
548 {
549 // pass through
550 impl_->draw(alpha, cam);
551 }
552
553 void Scene::refresh()
554 {
555 //impl_->objects.clear();
556 impl_->loadFromFile();
557 }
558
559
560 OctreePtr Scene::getOctree() const
561 {
562 // pass through
563 return impl_->octree;
564 }
565
566 /**
567 * Specialized search location for scene files. They can be found in the
568 * "scenes" subdirectory of any of the searched directories.
569 */
570
571 std::string Scene::getPath(const std::string& name)
572 {
573 return Resource::getPath("scenes/" + name + ".json");
574 }
575
576
577 } // namespace Mf
578
579 /** vim: set ts=4 sw=4 tw=80: *************************************************/
580
This page took 0.055131 seconds and 4 git commands to generate.