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