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