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