2 /*******************************************************************************
4 Copyright (c) 2009, Charles McGarvey
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
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.
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.
27 *******************************************************************************/
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>
42 #include "Character.hh"
47 struct Scene::Impl
: public Mf::Mippleton
<Impl
>
49 struct Quad
: public Mf::Entity
, public Mf::OctreeInsertable
59 Quad(const Mf::Vector3 vertices
[4], const std::string
& texture
,
60 Tilemap::Index tileIndex
) :
66 for (int i
= 0, num
= 0; i
< 4; ++i
)
68 for (int j
= 0; j
< 3; ++j
, ++num
)
70 vertices_
[num
] = vertices
[i
][j
];
74 if (!tilemap_
.getTileCoords(tileIndex
, texCoords_
))
76 Mf::logWarning("no index %d in texture %s", tileIndex
,
79 texCoords_
[0] = texCoords_
[1] =
80 texCoords_
[3] = texCoords_
[6] = 0.0;
81 texCoords_
[2] = texCoords_
[4] =
82 texCoords_
[5] = texCoords_
[7] = 1.0;
85 aabb_
.encloseVertices(vertices
, 4);
86 sphere_
.point
= aabb_
.getCenter();
87 sphere_
.radius
= (aabb_
.min
- sphere_
.point
).length();
90 void setBlending(bool blending
)
100 void setSurfaceType(SURFACE_TYPE type
)
105 void draw(Mf::Scalar alpha
= 0.0) const
110 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
116 glFogi(GL_FOG_MODE
, GL_LINEAR
);
119 //glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
122 glVertexPointer(3, GL_SCALAR
, 0, vertices_
);
123 glTexCoordPointer(2, GL_SCALAR
, 0, texCoords_
);
125 glDrawArrays(GL_TRIANGLE_FAN
, 0, 4);
131 bool isVisible(const Mf::Frustum
& frustum
) const
133 return sphere_
.isVisible(frustum
);
137 bool isInsideAabb(const Mf::Aabb
& aabb
) const
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]))
153 int getOctant(const Mf::Aabb
& aabb
) const
157 Mf::Plane::Halfspace halfspace
;
159 Mf::Plane xy
= aabb
.getPlaneXY();
160 halfspace
= xy
.intersects(sphere_
);
161 if (halfspace
== Mf::Plane::INTERSECT
)
163 halfspace
= xy
.intersects(aabb_
);
166 if (halfspace
== Mf::Plane::POSITIVE
)
168 Mf::Plane xz
= aabb
.getPlaneXZ();
169 halfspace
= xz
.intersects(sphere_
);
170 if (halfspace
== Mf::Plane::INTERSECT
)
172 halfspace
= xz
.intersects(aabb_
);
175 if (halfspace
== Mf::Plane::POSITIVE
)
177 Mf::Plane yz
= aabb
.getPlaneYZ();
178 halfspace
= yz
.intersects(sphere_
);
179 if (halfspace
== Mf::Plane::INTERSECT
)
181 halfspace
= yz
.intersects(aabb_
);
184 if (halfspace
== Mf::Plane::POSITIVE
)
188 else if (halfspace
== Mf::Plane::NEGATIVE
)
193 else if (halfspace
== Mf::Plane::NEGATIVE
)
195 Mf::Plane yz
= aabb
.getPlaneYZ();
196 halfspace
= yz
.intersects(sphere_
);
197 if (halfspace
== Mf::Plane::INTERSECT
)
199 halfspace
= yz
.intersects(aabb_
);
202 if (halfspace
== Mf::Plane::POSITIVE
)
206 else if (halfspace
== Mf::Plane::NEGATIVE
)
212 else if (halfspace
== Mf::Plane::NEGATIVE
)
214 Mf::Plane xz
= aabb
.getPlaneXZ();
215 halfspace
= xz
.intersects(sphere_
);
216 if (halfspace
== Mf::Plane::INTERSECT
)
218 halfspace
= xz
.intersects(aabb_
);
221 if (halfspace
== Mf::Plane::POSITIVE
)
223 Mf::Plane yz
= aabb
.getPlaneYZ();
224 halfspace
= yz
.intersects(sphere_
);
225 if (halfspace
== Mf::Plane::INTERSECT
)
227 halfspace
= yz
.intersects(aabb_
);
230 if (halfspace
== Mf::Plane::POSITIVE
)
234 else if (halfspace
== Mf::Plane::NEGATIVE
)
239 else if (halfspace
== Mf::Plane::NEGATIVE
)
241 Mf::Plane yz
= aabb
.getPlaneYZ();
242 halfspace
= yz
.intersects(sphere_
);
243 if (halfspace
== Mf::Plane::INTERSECT
)
245 halfspace
= yz
.intersects(aabb_
);
248 if (halfspace
== Mf::Plane::POSITIVE
)
252 else if (halfspace
== Mf::Plane::NEGATIVE
)
263 Mf::Scalar vertices_
[12];
264 Mf::Scalar texCoords_
[8];
270 SURFACE_TYPE surfaceType_
;
278 Mf::Matrix4 transform
;
281 Mf::Octree
<Quad
>::Ptr octree
;
283 Mf::Aabb playfieldBounds
;
284 Mf::Aabb maximumBounds
;
296 explicit Impl(const std::string
& name
) :
297 Mf::Mippleton
<Impl
>(name
)
302 void importSceneBindings(Mf::Script
& script
)
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
));
324 Mf::Settings::getInstance().get("detail", detail
);
325 script
.push(detail
); script
.set("detail");
327 script
.push(Quad::LEFT
); script
.set("LEFT");
328 script
.push(Quad::RIGHT
); script
.set("RIGHT");
329 script
.push(Quad::TOP
); script
.set("TOP");
331 script
.push(X
); script
.set("X");
332 script
.push(Y
); script
.set("Y");
333 script
.push(Z
); script
.set("Z");
340 std::string filePath
= Scene::getPath(getName());
342 script
.importStandardLibraries();
343 importLogScript(script
);
344 importSceneBindings(script
);
346 if (script
.doFile(filePath
) != Mf::Script::SUCCESS
)
350 Mf::logScript("%s", str
.c_str());
355 static int loadBox(Mf::Script
& script
, Mf::Aabb
& aabb
)
357 Mf::Script::Value table
[] =
359 script
[1].requireTable(),
360 script
[2].requireTable()
363 for (int i
= 0; i
<= 1; ++i
)
365 for (int j
= 1; j
<= 3; ++j
)
368 table
[i
].pushField();
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]);
382 int setPlayfieldBounds(Mf::Script
& script
)
384 return loadBox(script
, playfieldBounds
);
387 int setMaximumBounds(Mf::Script
& script
)
389 int ret
= loadBox(script
, maximumBounds
);
390 octree
= Mf::Octree
<Quad
>::alloc(maximumBounds
);
394 int resetTransform(Mf::Script
& script
)
396 transform
.identity();
400 int translate(Mf::Script
& script
)
402 Mf::Script::Value x
= script
[1].requireNumber();
403 Mf::Script::Value y
= script
[2].requireNumber();
404 Mf::Script::Value z
= script
[3].requireNumber();
411 Mf::Matrix4 translation
;
412 cml::matrix_translation(translation
, vec
);
413 transform
= translation
* transform
;
418 int scale(Mf::Script
& script
)
420 if (script
.getSize() == 3)
423 script
[1].requireNumber().get(vec
[0]);
424 script
[2].requireNumber().get(vec
[1]);
425 script
[3].requireNumber().get(vec
[2]);
428 cml::matrix_scale(scaling
, vec
);
429 transform
= scaling
* transform
;
431 else if (script
.getSize() == 1)
433 Mf::Scalar value
= 1.0;
434 script
[1].requireNumber().get(value
);
437 cml::matrix_uniform_scale(scaling
, value
);
438 transform
= scaling
* transform
;
442 script
.getTop().throwError("wrong number of arguments");
448 int rotate(Mf::Script
& script
)
450 Mf::Script::Value axis
= script
[1].requireString();
451 Mf::Script::Value angle
= script
[2].requireNumber();
459 cml::matrix_rotate_about_world_axis(transform
, index
, cml::rad(value
));
464 int setTexture(Mf::Script
& script
)
466 Mf::Script::Value name
= script
[1].requireString();
473 int makeTilemap(Mf::Script
& script
)
475 Mf::Script::Value table
= script
[1].requireTable();
476 Mf::Script::Value top
= script
[-1];
478 Quad::SURFACE_TYPE surfaceType
;
479 table
.pushField("surface_type");
480 top
.get(surfaceType
);
485 table
.pushField("width");
490 table
.pushField("tiles");
491 Mf::Script::Value tiles
= script
.getTop();
492 nTiles
= tiles
.getLength();
494 if (nTiles
% width
!= 0) table
.throwError("invalid number of tiles");
496 std::vector
< std::vector
<Tilemap::Index
> > indices
;
500 height
= nTiles
/ width
;
501 indices
.resize(height
);
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
508 for (h
= height
- 1; h
>= 0; --h
)
510 std::vector
<Tilemap::Index
> row
;
512 for (w
= 0; w
< width
; ++w
, ++i
)
514 script
.checkStack(2);
518 Tilemap::Index index
;
521 row
.push_back(index
);
527 Mf::Vector4 vertices
[height
+1][width
+1];
529 Mf::Matrix4 transposedTransform
= transform
;
530 transposedTransform
.transpose();
532 for (int h
= 0; h
<= height
; ++h
)
534 for (int w
= 0; w
<= width
; ++w
)
536 vertices
[h
][w
] = Mf::Vector4(w
, h
, 0.0, 1.0) * transposedTransform
;
540 for (int h
= 0; h
< height
; ++h
)
542 for (int w
= 0; w
< width
; ++w
)
544 if (indices
[h
][w
] == Tilemap::NO_TILE
) continue;
546 Mf::Vector3 demotedVertices
[4];
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
]);
553 Quad
* quad
= new Quad(demotedVertices
, texture
, indices
[h
][w
]);
554 quad
->setSurfaceType(surfaceType
);
556 boost::shared_ptr
<Quad
> quadPtr(quad
);
557 octree
->insert(quadPtr
);
564 int makeBillboard(Mf::Script
& script
)
566 Mf::Script::Value table
= script
[1];
567 Mf::Script::Value top
= script
[-1];
569 Tilemap::Index index
= 0;
571 bool blending
= false;
576 table
.pushField("tile");
579 table
.pushField("u_scale");
582 table
.pushField("blend");
585 table
.pushField("fog");
589 Mf::Vector4 vertices
[2][width
+1];
591 Mf::Matrix4 transposedTransform
= transform
;
592 transposedTransform
.transpose();
595 Mf::Scalar increment
= 1.0 / Mf::Scalar(width
);
597 for (int h
= 0; h
<= 1; ++h
)
600 for (int w
= 0; w
<= width
; ++w
, xf
+= increment
)
602 vertices
[h
][w
] = Mf::Vector4(xf
, Mf::Scalar(h
), 0.0, 1.0) *
607 for (int w
= 0; w
< width
; ++w
)
609 Mf::Vector3 demotedVertices
[4];
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
]);
616 Quad
* quad
= new Quad(demotedVertices
, texture
, index
);
617 quad
->setBlending(blending
);
620 boost::shared_ptr
<Quad
> quadPtr(quad
);
621 octree
->insert(quadPtr
);
629 Scene::Scene(const std::string
& name
) :
631 impl_(Scene::Impl::getInstance(name
)) {}
634 void Scene::draw(Mf::Scalar alpha
) const
636 impl_
->octree
->draw(alpha
);
639 void Scene::drawIfVisible(Mf::Scalar alpha
, const Mf::Frustum
& frustum
) const
641 impl_
->octree
->drawIfVisible(alpha
, frustum
);
645 bool Scene::checkForCollision(Character
& character
)
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();
652 Mf::logDebug("nearby objects: %d", objects
.size());
657 std::string
Scene::getPath(const std::string
& name
)
659 return Mf::Resource::getPath("scenes/" + name
+ ".lua");
663 /** vim: set ts=4 sw=4 tw=80: *************************************************/