]> Dogcows Code - chaz/yoink/blob - src/Scene.cc
fixed layer bugs; generalized octree
[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 #include <Moof/Tilemap.hh>
42
43 #include "Scene.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 Mf::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 Mf::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::Script script;
271
272 Mf::Matrix4 transform;
273 std::string texture;
274
275 Mf::Octree<Quad>::Ptr octree;
276
277
278 enum AXIS
279 {
280 X = 0,
281 Y = 1,
282 Z = 2
283 };
284
285
286
287 explicit Impl(const std::string& name) :
288 Mf::Mippleton<Impl>(name)
289 {
290 script.importStandardLibraries();
291 importLogScript(script);
292
293 importSceneBindings(script);
294 loadFromFile();
295 }
296
297 void importSceneBindings(Mf::Script& script)
298 {
299 script.importFunction("SetPlayfieldBounds",
300 boost::bind(&Impl::setPlayfieldBounds, this, _1));
301 script.importFunction("SetMaximumBounds",
302 boost::bind(&Impl::setMaximumBounds, this, _1));
303 script.importFunction("ResetTransform",
304 boost::bind(&Impl::resetTransform, this, _1));
305 script.importFunction("Translate",
306 boost::bind(&Impl::translate, this, _1));
307 script.importFunction("Scale",
308 boost::bind(&Impl::scale, this, _1));
309 script.importFunction("Rotate",
310 boost::bind(&Impl::rotate, this, _1));
311 script.importFunction("SetTexture",
312 boost::bind(&Impl::setTexture, this, _1));
313 script.importFunction("MakeTilemap",
314 boost::bind(&Impl::makeTilemap, this, _1));
315 script.importFunction("MakeBillboard",
316 boost::bind(&Impl::makeBillboard, this, _1));
317
318 long detail = 3;
319 Mf::Settings::getInstance().get("detail", detail);
320 script.push(detail); script.set("detail");
321
322 script.push(Quad::LEFT); script.set("LEFT");
323 script.push(Quad::RIGHT); script.set("RIGHT");
324 script.push(Quad::TOP); script.set("TOP");
325
326 script.push(X); script.set("X");
327 script.push(Y); script.set("Y");
328 script.push(Z); script.set("Z");
329 }
330
331
332 void loadFromFile()
333 {
334 std::string filePath = Scene::getPath(getName());
335
336 if (script.doFile(filePath) != Mf::Script::SUCCESS)
337 {
338 std::string str;
339 script[-1].get(str);
340 Mf::logScript("%s", str.c_str());
341 }
342 }
343
344
345 static int loadBox(Mf::Script& script, Mf::Aabb& aabb)
346 {
347 Mf::Script::Value table[] =
348 {
349 script[1].requireTable(),
350 script[2].requireTable()
351 };
352
353 if (!table[0].isTable() || !table[1].isTable())
354 {
355 Mf::logWarning("wrong arguments to setPlayfieldBounds; ignoring...");
356 return 0;
357 }
358
359 for (int i = 0; i <= 1; ++i)
360 {
361 for (int j = 1; j <= 3; ++j)
362 {
363 script.push((long)j);
364 table[i].pushField();
365 }
366 }
367
368 script[3].get(aabb.min[0]);
369 script[4].get(aabb.min[1]);
370 script[5].get(aabb.min[2]);
371 script[6].get(aabb.max[0]);
372 script[7].get(aabb.max[1]);
373 script[8].get(aabb.max[2]);
374
375 return 0;
376 }
377
378 int setPlayfieldBounds(Mf::Script& script)
379 {
380 Mf::Aabb bounds;
381 return loadBox(script, bounds);
382 }
383
384 int setMaximumBounds(Mf::Script& script)
385 {
386 Mf::Aabb bounds;
387 int ret = loadBox(script, bounds);
388 octree = Mf::Octree<Quad>::alloc(bounds);
389 return ret;
390 }
391
392 int resetTransform(Mf::Script& script)
393 {
394 transform.identity();
395 return 0;
396 }
397
398 int translate(Mf::Script& script)
399 {
400 Mf::Script::Value x = script[1].requireNumber();
401 Mf::Script::Value y = script[2].requireNumber();
402 Mf::Script::Value z = script[3].requireNumber();
403
404 Mf::Vector3 vec;
405 x.get(vec[0]);
406 y.get(vec[1]);
407 z.get(vec[2]);
408
409 Mf::Matrix4 translation;
410 cml::matrix_translation(translation, vec);
411 transform = translation * transform;
412
413 return 0;
414 }
415
416 int scale(Mf::Script& script)
417 {
418 if (script.getSize() == 3)
419 {
420 Mf::Vector3 vec;
421 script[1].requireNumber().get(vec[0]);
422 script[2].requireNumber().get(vec[1]);
423 script[3].requireNumber().get(vec[2]);
424
425 Mf::Matrix4 scaling;
426 cml::matrix_scale(scaling, vec);
427 transform = scaling * transform;
428 }
429 else if (script.getSize() == 1)
430 {
431 Mf::Scalar value = 1.0;
432 script[1].requireNumber().get(value);
433
434 Mf::Matrix4 scaling;
435 cml::matrix_uniform_scale(scaling, value);
436 transform = scaling * transform;
437 }
438 else
439 {
440 script.getTop().throwError("wrong number of arguments");
441 }
442
443 return 0;
444 }
445
446 int rotate(Mf::Script& script)
447 {
448 Mf::Script::Value axis = script[1].requireString();
449 Mf::Script::Value angle = script[2].requireNumber();
450
451 size_t index = 0;
452 axis.get(index);
453
454 Mf::Scalar value;
455 angle.get(value);
456
457 cml::matrix_rotate_about_world_axis(transform, index, cml::rad(value));
458
459 return 0;
460 }
461
462 int setTexture(Mf::Script& script)
463 {
464 Mf::Script::Value name = script[1].requireString();
465
466 name.get(texture);
467
468 return 0;
469 }
470
471 int makeTilemap(Mf::Script& script)
472 {
473 Mf::Script::Value table = script[1].requireTable();
474 Mf::Script::Value top = script[-1];
475
476 long width = 1;
477 long height = 1;
478
479 table.pushField("width");
480 top.get(width);
481
482 long nTiles = 0;
483
484 table.pushField("tiles");
485 Mf::Script::Value tiles = script.getTop();
486 nTiles = tiles.getLength();
487
488 if (nTiles % width != 0) table.throwError("invalid number of tiles");
489
490 std::vector< std::vector<Mf::Tilemap::Index> > indices;
491
492 int i, w, h;
493
494 height = nTiles / width;
495 indices.resize(height);
496
497 // the indices are stored upside-down in the scene file so that they
498 // are easier to edit as text, so we'll need to load them last row
499 // first
500
501 i = 1;
502 for (h = height - 1; h >= 0; --h)
503 {
504 std::vector<Mf::Tilemap::Index> row;
505
506 for (w = 0; w < width; ++w, ++i)
507 {
508 script.checkStack(2);
509 script.push(long(i));
510 tiles.pushField();
511
512 long index;
513 top.get(index);
514
515 row.push_back(Mf::Tilemap::Index(index));
516 }
517
518 indices[h] = row;
519 }
520
521 Mf::Vector4 vertices[height+1][width+1];
522
523 Mf::Matrix4 transposedTransform = transform;
524 transposedTransform.transpose();
525
526 for (int h = 0; h <= height; ++h)
527 {
528 for (int w = 0; w <= width; ++w)
529 {
530 vertices[h][w] = Mf::Vector4(Mf::Scalar(w), Mf::Scalar(h), 0.0, 1.0) *
531 transposedTransform;
532 }
533 }
534
535 for (int h = 0; h < height; ++h)
536 {
537 for (int w = 0; w < width; ++w)
538 {
539 if (indices[h][w] == Mf::Tilemap::NO_TILE) continue;
540
541 Mf::Vector3 quadVertices[4];
542
543 quadVertices[0] = Mf::demote(vertices[h][w]);
544 quadVertices[1] = Mf::demote(vertices[h][w+1]);
545 quadVertices[2] = Mf::demote(vertices[h+1][w+1]);
546 quadVertices[3] = Mf::demote(vertices[h+1][w]);
547
548 Quad* quad = new Quad(quadVertices, texture, indices[h][w]);
549 boost::shared_ptr<Quad> quadPtr(quad);
550
551 octree->insert(quadPtr);
552 }
553 }
554
555 return 0;
556 }
557
558 int makeBillboard(Mf::Script& script)
559 {
560 Mf::Script::Value table = script[1];
561 Mf::Script::Value top = script[-1];
562
563 long index = 0;
564 long width = 1;
565 bool blending = false;
566 bool fog = false;
567
568 if (table.isTable())
569 {
570 table.pushField("tile");
571 top.get(index);
572
573 table.pushField("u_scale");
574 top.get(width);
575
576 table.pushField("blend");
577 top.get(blending);
578
579 table.pushField("fog");
580 top.get(fog);
581 }
582
583 Mf::Vector4 vertices[2][width+1];
584
585 Mf::Matrix4 transposedTransform = transform;
586 transposedTransform.transpose();
587
588 Mf::Scalar xf;
589 Mf::Scalar increment = 1.0 / Mf::Scalar(width);
590
591 for (int h = 0; h <= 1; ++h)
592 {
593 xf = 0.0;
594 for (int w = 0; w <= width; ++w, xf += increment)
595 {
596 vertices[h][w] = Mf::Vector4(xf, Mf::Scalar(h), 0.0, 1.0) *
597 transposedTransform;
598 }
599 }
600
601 for (int w = 0; w < width; ++w)
602 {
603 Mf::Vector3 quadVertices[4];
604
605 quadVertices[0] = Mf::demote(vertices[0][w]);
606 quadVertices[1] = Mf::demote(vertices[0][w+1]);
607 quadVertices[2] = Mf::demote(vertices[1][w+1]);
608 quadVertices[3] = Mf::demote(vertices[1][w]);
609
610 Quad* quad = new Quad(quadVertices, texture, Mf::Tilemap::Index(index));
611 quad->setBlending(blending);
612 quad->setFog(fog);
613
614 boost::shared_ptr<Quad> quadPtr(quad);
615
616 octree->insert(quadPtr);
617 }
618
619 return 0;
620 }
621 };
622
623
624 Scene::Scene(const std::string& name) :
625 // pass through
626 impl_(Scene::Impl::getInstance(name)) {}
627
628
629 void Scene::draw(Mf::Scalar alpha) const
630 {
631 impl_->octree->draw(alpha);
632 }
633
634 void Scene::drawIfVisible(Mf::Scalar alpha, const Mf::Frustum& frustum) const
635 {
636 impl_->octree->drawIfVisible(alpha, frustum);
637 }
638
639
640 std::string Scene::getPath(const std::string& name)
641 {
642 return Mf::Resource::getPath("scenes/" + name + ".lua");
643 }
644
645
646 /** vim: set ts=4 sw=4 tw=80: *************************************************/
647
This page took 0.061621 seconds and 5 git commands to generate.