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