beginning CD implementation
[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(Quad::LEFT); script.set("LEFT");
328 script.push(Quad::RIGHT); script.set("RIGHT");
329 script.push(Quad::TOP); script.set("TOP");
330
331 script.push(X); script.set("X");
332 script.push(Y); script.set("Y");
333 script.push(Z); script.set("Z");
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].requireString();
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 Mf::Script::Value name = script[1].requireString();
467
468 name.get(texture);
469
470 return 0;
471 }
472
473 int makeTilemap(Mf::Script& script)
474 {
475 Mf::Script::Value table = script[1].requireTable();
476 Mf::Script::Value top = script[-1];
477
478 Quad::SURFACE_TYPE surfaceType;
479 table.pushField("surface_type");
480 top.get(surfaceType);
481
482 int width = 1;
483 int height = 1;
484
485 table.pushField("width");
486 top.get(width);
487
488 int nTiles = 0;
489
490 table.pushField("tiles");
491 Mf::Script::Value tiles = script.getTop();
492 nTiles = tiles.getLength();
493
494 if (nTiles % width != 0) table.throwError("invalid number of tiles");
495
496 std::vector< std::vector<Tilemap::Index> > indices;
497
498 int i, w, h;
499
500 height = nTiles / width;
501 indices.resize(height);
502
503 // the indices are stored upside-down in the scene file so that they
504 // are easier to edit as text, so we'll need to load them last row
505 // first
506
507 i = 1;
508 for (h = height - 1; h >= 0; --h)
509 {
510 std::vector<Tilemap::Index> row;
511
512 for (w = 0; w < width; ++w, ++i)
513 {
514 script.checkStack(2);
515 script.push(i);
516 tiles.pushField();
517
518 Tilemap::Index index;
519 top.get(index);
520
521 row.push_back(index);
522 }
523
524 indices[h] = row;
525 }
526
527 Mf::Vector4 vertices[height+1][width+1];
528
529 Mf::Matrix4 transposedTransform = transform;
530 transposedTransform.transpose();
531
532 for (int h = 0; h <= height; ++h)
533 {
534 for (int w = 0; w <= width; ++w)
535 {
536 vertices[h][w] = Mf::Vector4(w, h, 0.0, 1.0) * transposedTransform;
537 }
538 }
539
540 for (int h = 0; h < height; ++h)
541 {
542 for (int w = 0; w < width; ++w)
543 {
544 if (indices[h][w] == Tilemap::NO_TILE) continue;
545
546 Mf::Vector3 demotedVertices[4];
547
548 demotedVertices[0] = Mf::demote(vertices[h][w]);
549 demotedVertices[1] = Mf::demote(vertices[h][w+1]);
550 demotedVertices[2] = Mf::demote(vertices[h+1][w+1]);
551 demotedVertices[3] = Mf::demote(vertices[h+1][w]);
552
553 Quad* quad = new Quad(demotedVertices, texture, indices[h][w]);
554 quad->setSurfaceType(surfaceType);
555
556 boost::shared_ptr<Quad> quadPtr(quad);
557 octree->insert(quadPtr);
558 }
559 }
560
561 return 0;
562 }
563
564 int makeBillboard(Mf::Script& script)
565 {
566 Mf::Script::Value table = script[1];
567 Mf::Script::Value top = script[-1];
568
569 Tilemap::Index index = 0;
570 int width = 1;
571 bool blending = false;
572 bool fog = false;
573
574 if (table.isTable())
575 {
576 table.pushField("tile");
577 top.get(index);
578
579 table.pushField("u_scale");
580 top.get(width);
581
582 table.pushField("blend");
583 top.get(blending);
584
585 table.pushField("fog");
586 top.get(fog);
587 }
588
589 Mf::Vector4 vertices[2][width+1];
590
591 Mf::Matrix4 transposedTransform = transform;
592 transposedTransform.transpose();
593
594 Mf::Scalar xf;
595 Mf::Scalar increment = 1.0 / Mf::Scalar(width);
596
597 for (int h = 0; h <= 1; ++h)
598 {
599 xf = 0.0;
600 for (int w = 0; w <= width; ++w, xf += increment)
601 {
602 vertices[h][w] = Mf::Vector4(xf, Mf::Scalar(h), 0.0, 1.0) *
603 transposedTransform;
604 }
605 }
606
607 for (int w = 0; w < width; ++w)
608 {
609 Mf::Vector3 demotedVertices[4];
610
611 demotedVertices[0] = Mf::demote(vertices[0][w]);
612 demotedVertices[1] = Mf::demote(vertices[0][w+1]);
613 demotedVertices[2] = Mf::demote(vertices[1][w+1]);
614 demotedVertices[3] = Mf::demote(vertices[1][w]);
615
616 Quad* quad = new Quad(demotedVertices, texture, index);
617 quad->setBlending(blending);
618 quad->setFog(fog);
619
620 boost::shared_ptr<Quad> quadPtr(quad);
621 octree->insert(quadPtr);
622 }
623
624 return 0;
625 }
626 };
627
628
629 Scene::Scene(const std::string& name) :
630 // pass through
631 impl_(Scene::Impl::getInstance(name)) {}
632
633
634 void Scene::draw(Mf::Scalar alpha) const
635 {
636 impl_->octree->draw(alpha);
637 }
638
639 void Scene::drawIfVisible(Mf::Scalar alpha, const Mf::Frustum& frustum) const
640 {
641 impl_->octree->drawIfVisible(alpha, frustum);
642 }
643
644
645 bool Scene::checkForCollision(Character& character)
646 {
647 std::list< boost::shared_ptr<Impl::Quad> > objects;
648 //std::list<Mf::Octree<Impl::Quad>::InsertableP> objects;
649 impl_->octree->getNearbyObjects(objects, character);
650 impl_->maximumBounds.draw();
651
652 Mf::logDebug("nearby objects: %d", objects.size());
653 return false;
654 }
655
656
657 std::string Scene::getPath(const std::string& name)
658 {
659 return Mf::Resource::getPath("scenes/" + name + ".lua");
660 }
661
662
663 /** vim: set ts=4 sw=4 tw=80: *************************************************/
664
This page took 0.05927 seconds and 4 git commands to generate.