59ece168a4167089f4f9e056122115fa2f485ae9
[chaz/yoink] / src / 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 <Moof/Aabb.hh>
33 #include <Moof/Camera.hh>
34 #include <Moof/Entity.hh>
35 #include <Moof/Log.hh>
36 #include <Moof/Math.hh>
37 #include <Moof/Mippleton.hh>
38 #include <Moof/Octree.hh>
39 #include <Moof/Script.hh>
40 #include <Moof/Settings.hh>
41
42 #include "Scene.hh"
43 #include "Tilemap.hh"
44
45
46 struct Scene::Impl : public Mf::Mippleton<Impl>
47 {
48 class Quad : public Mf::Entity, public Mf::OctreeInsertable
49 {
50 Mf::Scalar vertices_[12];
51 Mf::Scalar texCoords_[8];
52
53 Tilemap tilemap_;
54
55 bool blending_;
56 bool fog_;
57
58 Mf::Aabb aabb_;
59 Mf::Sphere sphere_;
60
61 public:
62
63 enum SURFACE_TYPE
64 {
65 LEFT = 1,
66 RIGHT = 2,
67 TOP = 3
68 };
69
70 Quad(const Mf::Vector3 vertices[4], const std::string& texture,
71 Tilemap::Index tileIndex) :
72 tilemap_(texture),
73 blending_(false),
74 fog_(false)
75 {
76 for (int i = 0, num = 0; i < 4; ++i)
77 {
78 for (int j = 0; j < 3; ++j, ++num)
79 {
80 vertices_[num] = vertices[i][j];
81 }
82 }
83
84 if (!tilemap_.getTileCoords(tileIndex, texCoords_))
85 {
86 Mf::logWarning("no index %d in texture %s", tileIndex,
87 texture.c_str());
88
89 texCoords_[0] = texCoords_[1] =
90 texCoords_[3] = texCoords_[6] = 0.0;
91 texCoords_[2] = texCoords_[4] =
92 texCoords_[5] = texCoords_[7] = 1.0;
93 }
94
95 aabb_.encloseVertices(vertices, 4);
96 sphere_.point = aabb_.getCenter();
97 sphere_.radius = (aabb_.min - sphere_.point).length();
98 }
99
100 void setBlending(bool blending)
101 {
102 blending_ = blending;
103 }
104
105 void setFog(bool fog)
106 {
107 fog_ = fog;
108 }
109
110 void draw(Mf::Scalar alpha = 0.0) const
111 {
112 if (blending_)
113 {
114 glEnable(GL_BLEND);
115 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
116 }
117
118 if (fog_)
119 {
120 glEnable(GL_FOG);
121 glFogi(GL_FOG_MODE, GL_LINEAR);
122 }
123
124 //glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
125 tilemap_.bind();
126
127 glVertexPointer(3, GL_SCALAR, 0, vertices_);
128 glTexCoordPointer(2, GL_SCALAR, 0, texCoords_);
129
130 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
131
132 glDisable(GL_BLEND);
133 glDisable(GL_FOG);
134 }
135
136 bool isVisible(const Mf::Frustum& frustum) const
137 {
138 return sphere_.isVisible(frustum);
139 }
140
141
142 bool isInsideAabb(const Mf::Aabb& aabb) const
143 {
144 // make sure the entity is fully inside the volume
145 if (!(aabb_.max[0] < aabb.max[0] &&
146 aabb_.min[0] > aabb.min[0] &&
147 aabb_.max[1] < aabb.max[1] &&
148 aabb_.min[1] > aabb.min[1] &&
149 aabb_.max[2] < aabb.max[2] &&
150 aabb_.min[2] > aabb.min[2]))
151 {
152 return false;
153 }
154
155 return true;
156 }
157
158 int getOctant(const Mf::Aabb& aabb) const
159 {
160 int octantNum = -1;
161
162 Mf::Plane::Halfspace halfspace;
163
164 Mf::Plane xy = aabb.getPlaneXY();
165 halfspace = xy.intersects(sphere_);
166 if (halfspace == Mf::Plane::INTERSECT)
167 {
168 halfspace = xy.intersects(aabb_);
169 }
170
171 if (halfspace == Mf::Plane::POSITIVE)
172 {
173 Mf::Plane xz = aabb.getPlaneXZ();
174 halfspace = xz.intersects(sphere_);
175 if (halfspace == Mf::Plane::INTERSECT)
176 {
177 halfspace = xz.intersects(aabb_);
178 }
179
180 if (halfspace == Mf::Plane::POSITIVE)
181 {
182 Mf::Plane yz = aabb.getPlaneYZ();
183 halfspace = yz.intersects(sphere_);
184 if (halfspace == Mf::Plane::INTERSECT)
185 {
186 halfspace = yz.intersects(aabb_);
187 }
188
189 if (halfspace == Mf::Plane::POSITIVE)
190 {
191 octantNum = 2;
192 }
193 else if (halfspace == Mf::Plane::NEGATIVE)
194 {
195 octantNum = 3;
196 }
197 }
198 else if (halfspace == Mf::Plane::NEGATIVE)
199 {
200 Mf::Plane yz = aabb.getPlaneYZ();
201 halfspace = yz.intersects(sphere_);
202 if (halfspace == Mf::Plane::INTERSECT)
203 {
204 halfspace = yz.intersects(aabb_);
205 }
206
207 if (halfspace == Mf::Plane::POSITIVE)
208 {
209 octantNum = 1;
210 }
211 else if (halfspace == Mf::Plane::NEGATIVE)
212 {
213 octantNum = 0;
214 }
215 }
216 }
217 else if (halfspace == Mf::Plane::NEGATIVE)
218 {
219 Mf::Plane xz = aabb.getPlaneXZ();
220 halfspace = xz.intersects(sphere_);
221 if (halfspace == Mf::Plane::INTERSECT)
222 {
223 halfspace = xz.intersects(aabb_);
224 }
225
226 if (halfspace == Mf::Plane::POSITIVE)
227 {
228 Mf::Plane yz = aabb.getPlaneYZ();
229 halfspace = yz.intersects(sphere_);
230 if (halfspace == Mf::Plane::INTERSECT)
231 {
232 halfspace = yz.intersects(aabb_);
233 }
234
235 if (halfspace == Mf::Plane::POSITIVE)
236 {
237 octantNum = 6;
238 }
239 else if (halfspace == Mf::Plane::NEGATIVE)
240 {
241 octantNum = 7;
242 }
243 }
244 else if (halfspace == Mf::Plane::NEGATIVE)
245 {
246 Mf::Plane yz = aabb.getPlaneYZ();
247 halfspace = yz.intersects(sphere_);
248 if (halfspace == Mf::Plane::INTERSECT)
249 {
250 halfspace = yz.intersects(aabb_);
251 }
252
253 if (halfspace == Mf::Plane::POSITIVE)
254 {
255 octantNum = 5;
256 }
257 else if (halfspace == Mf::Plane::NEGATIVE)
258 {
259 octantNum = 4;
260 }
261 }
262 }
263
264 return octantNum;
265 }
266 };
267
268
269
270 Mf::Matrix4 transform;
271 std::string texture;
272
273 Mf::Octree<Quad>::Ptr octree;
274
275
276 enum AXIS
277 {
278 X = 0,
279 Y = 1,
280 Z = 2
281 };
282
283
284
285 explicit Impl(const std::string& name) :
286 Mf::Mippleton<Impl>(name)
287 {
288 loadFromFile();
289 }
290
291 void importSceneBindings(Mf::Script& script)
292 {
293 script.importFunction("SetPlayfieldBounds",
294 boost::bind(&Impl::setPlayfieldBounds, this, _1));
295 script.importFunction("SetMaximumBounds",
296 boost::bind(&Impl::setMaximumBounds, this, _1));
297 script.importFunction("ResetTransform",
298 boost::bind(&Impl::resetTransform, this, _1));
299 script.importFunction("Translate",
300 boost::bind(&Impl::translate, this, _1));
301 script.importFunction("Scale",
302 boost::bind(&Impl::scale, this, _1));
303 script.importFunction("Rotate",
304 boost::bind(&Impl::rotate, this, _1));
305 script.importFunction("SetTexture",
306 boost::bind(&Impl::setTexture, this, _1));
307 script.importFunction("MakeTilemap",
308 boost::bind(&Impl::makeTilemap, this, _1));
309 script.importFunction("MakeBillboard",
310 boost::bind(&Impl::makeBillboard, this, _1));
311
312 long detail = 3;
313 Mf::Settings::getInstance().get("detail", detail);
314 script.push(detail); script.set("detail");
315
316 script.push(Quad::LEFT); script.set("LEFT");
317 script.push(Quad::RIGHT); script.set("RIGHT");
318 script.push(Quad::TOP); script.set("TOP");
319
320 script.push(X); script.set("X");
321 script.push(Y); script.set("Y");
322 script.push(Z); script.set("Z");
323 }
324
325
326 void loadFromFile()
327 {
328 Mf::Script script;
329 std::string filePath = Scene::getPath(getName());
330
331 script.importStandardLibraries();
332 importLogScript(script);
333 importSceneBindings(script);
334
335 if (script.doFile(filePath) != Mf::Script::SUCCESS)
336 {
337 std::string str;
338 script[-1].get(str);
339 Mf::logScript("%s", str.c_str());
340 }
341 }
342
343
344 static int loadBox(Mf::Script& script, Mf::Aabb& aabb)
345 {
346 Mf::Script::Value table[] =
347 {
348 script[1].requireTable(),
349 script[2].requireTable()
350 };
351
352 if (!table[0].isTable() || !table[1].isTable())
353 {
354 Mf::logWarning("wrong arguments to setPlayfieldBounds; ignoring...");
355 return 0;
356 }
357
358 for (int i = 0; i <= 1; ++i)
359 {
360 for (int j = 1; j <= 3; ++j)
361 {
362 script.push((long)j);
363 table[i].pushField();
364 }
365 }
366
367 script[3].get(aabb.min[0]);
368 script[4].get(aabb.min[1]);
369 script[5].get(aabb.min[2]);
370 script[6].get(aabb.max[0]);
371 script[7].get(aabb.max[1]);
372 script[8].get(aabb.max[2]);
373
374 return 0;
375 }
376
377 int setPlayfieldBounds(Mf::Script& script)
378 {
379 Mf::Aabb bounds;
380 return loadBox(script, bounds);
381 }
382
383 int setMaximumBounds(Mf::Script& script)
384 {
385 Mf::Aabb bounds;
386 int ret = loadBox(script, bounds);
387 octree = Mf::Octree<Quad>::alloc(bounds);
388 return ret;
389 }
390
391 int resetTransform(Mf::Script& script)
392 {
393 transform.identity();
394 return 0;
395 }
396
397 int translate(Mf::Script& script)
398 {
399 Mf::Script::Value x = script[1].requireNumber();
400 Mf::Script::Value y = script[2].requireNumber();
401 Mf::Script::Value z = script[3].requireNumber();
402
403 Mf::Vector3 vec;
404 x.get(vec[0]);
405 y.get(vec[1]);
406 z.get(vec[2]);
407
408 Mf::Matrix4 translation;
409 cml::matrix_translation(translation, vec);
410 transform = translation * transform;
411
412 return 0;
413 }
414
415 int scale(Mf::Script& script)
416 {
417 if (script.getSize() == 3)
418 {
419 Mf::Vector3 vec;
420 script[1].requireNumber().get(vec[0]);
421 script[2].requireNumber().get(vec[1]);
422 script[3].requireNumber().get(vec[2]);
423
424 Mf::Matrix4 scaling;
425 cml::matrix_scale(scaling, vec);
426 transform = scaling * transform;
427 }
428 else if (script.getSize() == 1)
429 {
430 Mf::Scalar value = 1.0;
431 script[1].requireNumber().get(value);
432
433 Mf::Matrix4 scaling;
434 cml::matrix_uniform_scale(scaling, value);
435 transform = scaling * transform;
436 }
437 else
438 {
439 script.getTop().throwError("wrong number of arguments");
440 }
441
442 return 0;
443 }
444
445 int rotate(Mf::Script& script)
446 {
447 Mf::Script::Value axis = script[1].requireString();
448 Mf::Script::Value angle = script[2].requireNumber();
449
450 size_t index = 0;
451 axis.get(index);
452
453 Mf::Scalar value;
454 angle.get(value);
455
456 cml::matrix_rotate_about_world_axis(transform, index, cml::rad(value));
457
458 return 0;
459 }
460
461 int setTexture(Mf::Script& script)
462 {
463 Mf::Script::Value name = script[1].requireString();
464
465 name.get(texture);
466
467 return 0;
468 }
469
470 int makeTilemap(Mf::Script& script)
471 {
472 Mf::Script::Value table = script[1].requireTable();
473 Mf::Script::Value top = script[-1];
474
475 long width = 1;
476 long height = 1;
477
478 table.pushField("width");
479 top.get(width);
480
481 long nTiles = 0;
482
483 table.pushField("tiles");
484 Mf::Script::Value tiles = script.getTop();
485 nTiles = tiles.getLength();
486
487 if (nTiles % width != 0) table.throwError("invalid number of tiles");
488
489 std::vector< std::vector<Tilemap::Index> > indices;
490
491 int i, w, h;
492
493 height = nTiles / width;
494 indices.resize(height);
495
496 // the indices are stored upside-down in the scene file so that they
497 // are easier to edit as text, so we'll need to load them last row
498 // first
499
500 i = 1;
501 for (h = height - 1; h >= 0; --h)
502 {
503 std::vector<Tilemap::Index> row;
504
505 for (w = 0; w < width; ++w, ++i)
506 {
507 script.checkStack(2);
508 script.push(long(i));
509 tiles.pushField();
510
511 Tilemap::Index index;
512 top.get(index);
513
514 row.push_back(index);
515 }
516
517 indices[h] = row;
518 }
519
520 Mf::Vector4 vertices[height+1][width+1];
521
522 Mf::Matrix4 transposedTransform = transform;
523 transposedTransform.transpose();
524
525 for (int h = 0; h <= height; ++h)
526 {
527 for (int w = 0; w <= width; ++w)
528 {
529 vertices[h][w] = Mf::Vector4(w, h, 0.0, 1.0) * transposedTransform;
530 }
531 }
532
533 for (int h = 0; h < height; ++h)
534 {
535 for (int w = 0; w < width; ++w)
536 {
537 if (indices[h][w] == Tilemap::NO_TILE) continue;
538
539 Mf::Vector3 quadVertices[4];
540
541 quadVertices[0] = Mf::demote(vertices[h][w]);
542 quadVertices[1] = Mf::demote(vertices[h][w+1]);
543 quadVertices[2] = Mf::demote(vertices[h+1][w+1]);
544 quadVertices[3] = Mf::demote(vertices[h+1][w]);
545
546 Quad* quad = new Quad(quadVertices, texture, indices[h][w]);
547 boost::shared_ptr<Quad> quadPtr(quad);
548
549 octree->insert(quadPtr);
550 }
551 }
552
553 return 0;
554 }
555
556 int makeBillboard(Mf::Script& script)
557 {
558 Mf::Script::Value table = script[1];
559 Mf::Script::Value top = script[-1];
560
561 long index = 0;
562 long width = 1;
563 bool blending = false;
564 bool fog = false;
565
566 if (table.isTable())
567 {
568 table.pushField("tile");
569 top.get(index);
570
571 table.pushField("u_scale");
572 top.get(width);
573
574 table.pushField("blend");
575 top.get(blending);
576
577 table.pushField("fog");
578 top.get(fog);
579 }
580
581 Mf::Vector4 vertices[2][width+1];
582
583 Mf::Matrix4 transposedTransform = transform;
584 transposedTransform.transpose();
585
586 Mf::Scalar xf;
587 Mf::Scalar increment = 1.0 / Mf::Scalar(width);
588
589 for (int h = 0; h <= 1; ++h)
590 {
591 xf = 0.0;
592 for (int w = 0; w <= width; ++w, xf += increment)
593 {
594 vertices[h][w] = Mf::Vector4(xf, Mf::Scalar(h), 0.0, 1.0) *
595 transposedTransform;
596 }
597 }
598
599 for (int w = 0; w < width; ++w)
600 {
601 Mf::Vector3 quadVertices[4];
602
603 quadVertices[0] = Mf::demote(vertices[0][w]);
604 quadVertices[1] = Mf::demote(vertices[0][w+1]);
605 quadVertices[2] = Mf::demote(vertices[1][w+1]);
606 quadVertices[3] = Mf::demote(vertices[1][w]);
607
608 Quad* quad = new Quad(quadVertices, texture, Tilemap::Index(index));
609 quad->setBlending(blending);
610 quad->setFog(fog);
611
612 boost::shared_ptr<Quad> quadPtr(quad);
613
614 octree->insert(quadPtr);
615 }
616
617 return 0;
618 }
619 };
620
621
622 Scene::Scene(const std::string& name) :
623 // pass through
624 impl_(Scene::Impl::getInstance(name)) {}
625
626
627 void Scene::draw(Mf::Scalar alpha) const
628 {
629 impl_->octree->draw(alpha);
630 }
631
632 void Scene::drawIfVisible(Mf::Scalar alpha, const Mf::Frustum& frustum) const
633 {
634 impl_->octree->drawIfVisible(alpha, frustum);
635 }
636
637
638 std::string Scene::getPath(const std::string& name)
639 {
640 return Mf::Resource::getPath("scenes/" + name + ".lua");
641 }
642
643
644 /** vim: set ts=4 sw=4 tw=80: *************************************************/
645
This page took 0.057205 seconds and 3 git commands to generate.