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