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