ray-scene intersection
[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 %d in texture %s", tileIndex,
79 texture.c_str());
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::Status 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 Mf::Script::Value table[] =
231 {
232 script[1].requireTable(),
233 script[2].requireTable()
234 };
235
236 for (int i = 0; i <= 1; ++i)
237 {
238 for (int j = 1; j <= 3; ++j)
239 {
240 script.push(j);
241 table[i].pushField();
242 }
243 }
244
245 script[3].get(aabb.min[0]);
246 script[4].get(aabb.min[1]);
247 script[5].get(aabb.min[2]);
248 script[6].get(aabb.max[0]);
249 script[7].get(aabb.max[1]);
250 script[8].get(aabb.max[2]);
251
252 return 0;
253 }
254
255 int setBounds(Mf::Script& script)
256 {
257 int ret = loadBox(script, mBounds);
258 //mOctree = Mf::Octree<Quad>::alloc(mBounds);
259 return ret;
260 }
261
262 int resetTransform(Mf::Script& script)
263 {
264 mTransform.identity();
265 return 0;
266 }
267
268 int translate(Mf::Script& script)
269 {
270 Mf::Script::Value x = script[1].requireNumber();
271 Mf::Script::Value y = script[2].requireNumber();
272 Mf::Script::Value z = script[3].requireNumber();
273
274 Mf::Vector3 vec;
275 x.get(vec[0]);
276 y.get(vec[1]);
277 z.get(vec[2]);
278
279 Mf::Matrix4 translation;
280 cml::matrix_translation(translation, vec);
281 mTransform = translation * mTransform;
282
283 return 0;
284 }
285
286 int scale(Mf::Script& script)
287 {
288 if (script.getSize() == 3)
289 {
290 Mf::Vector3 vec;
291 script[1].requireNumber().get(vec[0]);
292 script[2].requireNumber().get(vec[1]);
293 script[3].requireNumber().get(vec[2]);
294
295 Mf::Matrix4 scaling;
296 cml::matrix_scale(scaling, vec);
297 mTransform = scaling * mTransform;
298 }
299 else if (script.getSize() == 1)
300 {
301 Mf::Scalar value = 1.0;
302 script[1].requireNumber().get(value);
303
304 Mf::Matrix4 scaling;
305 cml::matrix_uniform_scale(scaling, value);
306 mTransform = scaling * mTransform;
307 }
308 else
309 {
310 script.getTop().throwError("wrong number of arguments");
311 }
312
313 return 0;
314 }
315
316 int rotate(Mf::Script& script)
317 {
318 Mf::Script::Value axis = script[1].requireNumber();
319 Mf::Script::Value angle = script[2].requireNumber();
320
321 size_t index = 0;
322 axis.get(index);
323
324 Mf::Scalar value;
325 angle.get(value);
326
327 cml::matrix_rotate_about_world_axis(mTransform, index, cml::rad(value));
328
329 return 0;
330 }
331
332 int setTexture(Mf::Script& script)
333 {
334 script[1].requireString().get(mTexture);
335 return 0;
336 }
337
338 int drawTilemap(Mf::Script& script)
339 {
340 Mf::Script::Value table = script[1].requireTable();
341 Mf::Script::Value top = script[-1];
342
343 int width = 1;
344 int height = 1;
345 int nTiles = 0;
346
347 table.pushField("width");
348 top.get(width);
349 script.pop();
350
351 nTiles = table.getLength();
352 if (nTiles % width != 0) table.throwError("invalid number of tiles");
353
354 if (width == 0) table.throwError("width field must not be zero");
355 height = nTiles / width;
356
357 Mf::Vector3 vertices[height+1][width+1];
358
359 // the indices are stored upside-down in the scene file so that they are
360 // easier to edit as text, so we'll need to load them last row first
361
362 // do first row and first column of vertices
363
364 for (int w = 0; w <= width; ++w)
365 {
366 vertices[height][w] = Mf::demote(mTransform *
367 Mf::Vector4(w, height, 0.0, 1.0));
368 }
369 for (int h = 0; h < height; ++h)
370 {
371 vertices[h][0] = Mf::demote(mTransform *
372 Mf::Vector4(0.0, h, 0.0, 1.0));
373 }
374
375 size_t i = 1;
376 for (int h = height - 1; h >= 0; --h)
377 {
378 for (int w = 0; w < width; ++w, ++i)
379 {
380 int wPlus1 = w + 1;
381 int hPlus1 = h + 1;
382
383 table.pushField(i);
384
385 Tilemap::Index index;
386 top.get(index);
387
388 script.pop();
389
390 vertices[h][wPlus1] = Mf::demote(mTransform *
391 Mf::Vector4(wPlus1, h, 0.0, 1.0));
392
393 if (index == Tilemap::NO_TILE) continue;
394
395 const Mf::Vector3* corners[4] = {
396 &vertices[h][w],
397 &vertices[h][wPlus1],
398 &vertices[hPlus1][wPlus1],
399 &vertices[hPlus1][w]
400 };
401
402 Quad* quad = new Quad(corners, mTexture, index);
403 //quad->setSurface(surface);
404
405 boost::shared_ptr<Quad> quadPtr(quad);
406 mObjects.push_back(quadPtr);
407 }
408 }
409
410 Quad::Surface surface = Quad::NONE;
411
412 table.pushField("surface");
413 top.get(surface);
414 script.pop();
415
416 if (surface != Quad::NONE)
417 {
418 // need a 2d line for collisions
419 // assuming the camera always looks directly to -z when the
420 // scene is built, simply demoting the vector again should
421 // project the points to the xy-plane
422
423 //Mf::Vector2 tr = Mf::demote(vertices[height+1][width+1]);
424 //Mf::Vector2 bl = Mf::demote(vertices[0][0]);
425
426 Mf::Vector2 bl = Mf::demote(vertices[0][0]);
427 Mf::Vector2 tr = Mf::demote(vertices[height][width]);
428 //Mf::logInfo("pt1: %f, %f", bl[0], bl[1]);
429 //Mf::logInfo("pt2: %f, %f", tr[0], tr[1]);
430
431 mLines.push_back(Mf::Line<2>(bl, tr));
432 Mf::logInfo("new line");
433
434 //if (tl == tr)
435 //{
436 //mLines.push_back(Mf::Line<2>(bl, tl));
437 //}
438 //else
439 //{
440 //mLines.push_back(Mf::Line<2>(bl, tl));
441 //}
442 }
443
444 return 0;
445 }
446
447 int drawTile(Mf::Script& script)
448 {
449 Mf::Script::Value param = script[1];
450 Mf::Script::Value top = script[-1];
451
452 Tilemap::Index index = 0;
453 int width = 1;
454 bool blending = false;
455 bool fog = false;
456
457 if (param.isTable())
458 {
459 script.push(1);
460 param.pushField();
461 top.get(index);
462
463 param.pushField("u_scale");
464 top.get(width);
465
466 param.pushField("blend");
467 top.get(blending);
468
469 param.pushField("fog");
470 top.get(fog);
471 }
472 else if (param.isNumber())
473 {
474 param.get(index);
475 }
476
477 Mf::Vector3 vertices[2][width+1];
478
479 Mf::Scalar xf;
480 Mf::Scalar increment = 1.0 / Mf::Scalar(width);
481
482 for (int h = 0; h <= 1; ++h)
483 {
484 xf = 0.0;
485 for (int w = 0; w <= width; ++w, xf += increment)
486 {
487 vertices[h][w] = Mf::demote(mTransform *
488 Mf::Vector4(xf, Mf::Scalar(h), 0.0, 1.0));
489 }
490 }
491
492 for (int w = 0; w < width; ++w)
493 {
494 int wPlus1 = w + 1;
495
496 const Mf::Vector3* corners[4] = {
497 &vertices[0][w],
498 &vertices[0][wPlus1],
499 &vertices[1][wPlus1],
500 &vertices[1][w]
501 };
502
503 Quad* quad = new Quad(corners, mTexture, index);
504 quad->setBlending(blending);
505 quad->setFog(fog);
506
507 boost::shared_ptr<Quad> quadPtr(quad);
508 mObjects.push_back(quadPtr);
509 }
510
511 return 0;
512 }
513 };
514
515
516 Scene::Scene(const std::string& name) :
517 // pass through
518 mImpl(Scene::Impl::getInstance(name)) {}
519
520
521 Mf::Script::Status Scene::load(Mf::Script& script)
522 {
523 // pass through
524 return mImpl->load(script);
525 }
526
527
528 void Scene::draw(Mf::Scalar alpha) const
529 {
530 std::list< boost::shared_ptr<Impl::Quad> >& objects = mImpl->mObjects;
531 std::list< boost::shared_ptr<Impl::Quad> >::const_iterator it;
532
533 for (it = objects.begin(); it != objects.end(); ++it)
534 {
535 (*it)->draw(alpha);
536 }
537
538 mImpl->mBounds.draw();
539 }
540
541 void Scene::drawIfVisible(Mf::Scalar alpha, const Mf::Frustum& frustum) const
542 {
543 std::list< boost::shared_ptr<Impl::Quad> >& objects = mImpl->mObjects;
544 std::list< boost::shared_ptr<Impl::Quad> >::const_iterator it;
545
546 for (it = objects.begin(); it != objects.end(); ++it)
547 {
548 (*it)->drawIfVisible(alpha, frustum);
549 }
550
551 std::list< Mf::Line<2> >& lines = mImpl->mLines;
552 std::list< Mf::Line<2> >::const_iterator lit;
553
554 for (lit = lines.begin(); lit != lines.end(); ++lit)
555 {
556 (*lit).draw(alpha);
557 }
558
559 mImpl->mBounds.draw();
560 }
561
562
563 bool Scene::castRay(const Mf::Ray<2>& ray,
564 std::list<Mf::Ray<2>::Intersection>& hits) const
565 {
566 std::list< Mf::Line<2> >& lines = mImpl->mLines;
567 std::list< Mf::Line<2> >::const_iterator it;
568
569 for (it = lines.begin(); it != lines.end(); ++it)
570 {
571 Mf::Ray<2>::Intersection hit;
572 Mf::Scalar d = (*it).intersectRay(ray, hit);
573 if (d > 0.0)
574 {
575 hits.push_back(hit);
576 return true;
577 }
578 }
579
580 return false;
581 }
582
583 bool Scene::checkForCollision(Character& character)
584 {
585 return false;
586 //std::list< boost::shared_ptr<Impl::Quad> > objects;
587 //std::list<Mf::Octree<Impl::Quad>::InsertableP> objects;
588 //mImpl->mOctree->getNearbyObjects(objects, character);
589
590 std::list< boost::shared_ptr<Impl::Quad> >& objects = mImpl->mObjects;
591 std::list< boost::shared_ptr<Impl::Quad> >::const_iterator it;
592
593 int collisions = 0;
594 Mf::Sphere<3> sphere = character.getSphere();
595
596 for (it = objects.begin(); it != objects.end(); ++it)
597 {
598 Impl::Quad::Surface type = (*it)->getSurface();
599 if (type == Impl::Quad::NONE) continue;
600
601 if (Mf::checkCollision(sphere, (*it)->getSphere()))
602 {
603 ++collisions;
604
605 Mf::Vector2 impulse(0.0, 0.0);
606 Mf::Vector2 p = character.getState().momentum;
607
608 Mf::State2 state = character.getState(1.0);
609 sphere = character.getSphere();
610 Mf::Scalar alpha = 1.0;
611 while (Mf::checkCollision(sphere, (*it)->getSphere()))
612 {
613 alpha -= 0.05;
614 state = character.getState(alpha);
615 }
616
617 character.setPosition(state.position);
618
619 //switch (type)
620 //{
621 //case Impl::Quad::TOP:
622 //if (p[1] < 0.0) impulse[1] = -p[1];
623 //break;
624 //case Impl::Quad::LEFT:
625 //if (p[0] > 0.0) impulse[0] = 1.5*-p[0];
626 //break;
627 //case Impl::Quad::RIGHT:
628 //if (p[0] < 0.0) impulse[0] = 1.5*-p[0];
629 //break;
630 //}
631
632 //character.addImpulse(impulse);
633 }
634 }
635
636 if (collisions > 0)
637 {
638 Mf::logInfo("collisions: %d", collisions);
639 }
640
641 return false;
642 }
643
644
645 std::string Scene::getPath(const std::string& name)
646 {
647 return Mf::Resource::getPath("scenes/" + name + ".lua");
648 }
649
650
651 /** vim: set ts=4 sw=4 tw=80: *************************************************/
652
This page took 0.060582 seconds and 4 git commands to generate.