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>
46 struct Scene::Impl
: public Mf::Mippleton
<Impl
>
48 class Quad
: public Mf::Entity
, public Mf::OctreeInsertable
50 Mf::Scalar vertices_
[12];
51 Mf::Scalar texCoords_
[8];
70 Quad(const Mf::Vector3 vertices
[4], const std::string
& texture
,
71 Tilemap::Index tileIndex
) :
76 for (int i
= 0, num
= 0; i
< 4; ++i
)
78 for (int j
= 0; j
< 3; ++j
, ++num
)
80 vertices_
[num
] = vertices
[i
][j
];
84 if (!tilemap_
.getTileCoords(tileIndex
, texCoords_
))
86 Mf::logWarning("no index %d in texture %s", tileIndex
,
89 texCoords_
[0] = texCoords_
[1] =
90 texCoords_
[3] = texCoords_
[6] = 0.0;
91 texCoords_
[2] = texCoords_
[4] =
92 texCoords_
[5] = texCoords_
[7] = 1.0;
95 aabb_
.encloseVertices(vertices
, 4);
96 sphere_
.point
= aabb_
.getCenter();
97 sphere_
.radius
= (aabb_
.min
- sphere_
.point
).length();
100 void setBlending(bool blending
)
102 blending_
= blending
;
105 void setFog(bool fog
)
110 void draw(Mf::Scalar alpha
= 0.0) const
115 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
121 glFogi(GL_FOG_MODE
, GL_LINEAR
);
124 //glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
127 glVertexPointer(3, GL_SCALAR
, 0, vertices_
);
128 glTexCoordPointer(2, GL_SCALAR
, 0, texCoords_
);
130 glDrawArrays(GL_TRIANGLE_FAN
, 0, 4);
136 bool isVisible(const Mf::Frustum
& frustum
) const
138 return sphere_
.isVisible(frustum
);
142 bool isInsideAabb(const Mf::Aabb
& aabb
) const
144 // make sure the entity is fully inside the volume
145 if (!(aabb_
.max
[0] < aabb
.max
[0] &&
146 aabb_
.min
[0] > aabb
.min
[0] &&
147 aabb_
.max
[1] < aabb
.max
[1] &&
148 aabb_
.min
[1] > aabb
.min
[1] &&
149 aabb_
.max
[2] < aabb
.max
[2] &&
150 aabb_
.min
[2] > aabb
.min
[2]))
158 int getOctant(const Mf::Aabb
& aabb
) const
162 Mf::Plane::Halfspace halfspace
;
164 Mf::Plane xy
= aabb
.getPlaneXY();
165 halfspace
= xy
.intersects(sphere_
);
166 if (halfspace
== Mf::Plane::INTERSECT
)
168 halfspace
= xy
.intersects(aabb_
);
171 if (halfspace
== Mf::Plane::POSITIVE
)
173 Mf::Plane xz
= aabb
.getPlaneXZ();
174 halfspace
= xz
.intersects(sphere_
);
175 if (halfspace
== Mf::Plane::INTERSECT
)
177 halfspace
= xz
.intersects(aabb_
);
180 if (halfspace
== Mf::Plane::POSITIVE
)
182 Mf::Plane yz
= aabb
.getPlaneYZ();
183 halfspace
= yz
.intersects(sphere_
);
184 if (halfspace
== Mf::Plane::INTERSECT
)
186 halfspace
= yz
.intersects(aabb_
);
189 if (halfspace
== Mf::Plane::POSITIVE
)
193 else if (halfspace
== Mf::Plane::NEGATIVE
)
198 else if (halfspace
== Mf::Plane::NEGATIVE
)
200 Mf::Plane yz
= aabb
.getPlaneYZ();
201 halfspace
= yz
.intersects(sphere_
);
202 if (halfspace
== Mf::Plane::INTERSECT
)
204 halfspace
= yz
.intersects(aabb_
);
207 if (halfspace
== Mf::Plane::POSITIVE
)
211 else if (halfspace
== Mf::Plane::NEGATIVE
)
217 else if (halfspace
== Mf::Plane::NEGATIVE
)
219 Mf::Plane xz
= aabb
.getPlaneXZ();
220 halfspace
= xz
.intersects(sphere_
);
221 if (halfspace
== Mf::Plane::INTERSECT
)
223 halfspace
= xz
.intersects(aabb_
);
226 if (halfspace
== Mf::Plane::POSITIVE
)
228 Mf::Plane yz
= aabb
.getPlaneYZ();
229 halfspace
= yz
.intersects(sphere_
);
230 if (halfspace
== Mf::Plane::INTERSECT
)
232 halfspace
= yz
.intersects(aabb_
);
235 if (halfspace
== Mf::Plane::POSITIVE
)
239 else if (halfspace
== Mf::Plane::NEGATIVE
)
244 else if (halfspace
== Mf::Plane::NEGATIVE
)
246 Mf::Plane yz
= aabb
.getPlaneYZ();
247 halfspace
= yz
.intersects(sphere_
);
248 if (halfspace
== Mf::Plane::INTERSECT
)
250 halfspace
= yz
.intersects(aabb_
);
253 if (halfspace
== Mf::Plane::POSITIVE
)
257 else if (halfspace
== Mf::Plane::NEGATIVE
)
270 Mf::Matrix4 transform
;
273 Mf::Octree
<Quad
>::Ptr octree
;
285 explicit Impl(const std::string
& name
) :
286 Mf::Mippleton
<Impl
>(name
)
291 void importSceneBindings(Mf::Script
& script
)
293 script
.importFunction("SetPlayfieldBounds",
294 boost::bind(&Impl::setPlayfieldBounds
, this, _1
));
295 script
.importFunction("SetMaximumBounds",
296 boost::bind(&Impl::setMaximumBounds
, this, _1
));
297 script
.importFunction("ResetTransform",
298 boost::bind(&Impl::resetTransform
, this, _1
));
299 script
.importFunction("Translate",
300 boost::bind(&Impl::translate
, this, _1
));
301 script
.importFunction("Scale",
302 boost::bind(&Impl::scale
, this, _1
));
303 script
.importFunction("Rotate",
304 boost::bind(&Impl::rotate
, this, _1
));
305 script
.importFunction("SetTexture",
306 boost::bind(&Impl::setTexture
, this, _1
));
307 script
.importFunction("MakeTilemap",
308 boost::bind(&Impl::makeTilemap
, this, _1
));
309 script
.importFunction("MakeBillboard",
310 boost::bind(&Impl::makeBillboard
, this, _1
));
313 Mf::Settings::getInstance().get("detail", detail
);
314 script
.push(detail
); script
.set("detail");
316 script
.push(Quad::LEFT
); script
.set("LEFT");
317 script
.push(Quad::RIGHT
); script
.set("RIGHT");
318 script
.push(Quad::TOP
); script
.set("TOP");
320 script
.push(X
); script
.set("X");
321 script
.push(Y
); script
.set("Y");
322 script
.push(Z
); script
.set("Z");
329 std::string filePath
= Scene::getPath(getName());
331 script
.importStandardLibraries();
332 importLogScript(script
);
333 importSceneBindings(script
);
335 if (script
.doFile(filePath
) != Mf::Script::SUCCESS
)
339 Mf::logScript("%s", str
.c_str());
344 static int loadBox(Mf::Script
& script
, Mf::Aabb
& aabb
)
346 Mf::Script::Value table
[] =
348 script
[1].requireTable(),
349 script
[2].requireTable()
352 if (!table
[0].isTable() || !table
[1].isTable())
354 Mf::logWarning("wrong arguments to setPlayfieldBounds; ignoring...");
358 for (int i
= 0; i
<= 1; ++i
)
360 for (int j
= 1; j
<= 3; ++j
)
362 script
.push((long)j
);
363 table
[i
].pushField();
367 script
[3].get(aabb
.min
[0]);
368 script
[4].get(aabb
.min
[1]);
369 script
[5].get(aabb
.min
[2]);
370 script
[6].get(aabb
.max
[0]);
371 script
[7].get(aabb
.max
[1]);
372 script
[8].get(aabb
.max
[2]);
377 int setPlayfieldBounds(Mf::Script
& script
)
380 return loadBox(script
, bounds
);
383 int setMaximumBounds(Mf::Script
& script
)
386 int ret
= loadBox(script
, bounds
);
387 octree
= Mf::Octree
<Quad
>::alloc(bounds
);
391 int resetTransform(Mf::Script
& script
)
393 transform
.identity();
397 int translate(Mf::Script
& script
)
399 Mf::Script::Value x
= script
[1].requireNumber();
400 Mf::Script::Value y
= script
[2].requireNumber();
401 Mf::Script::Value z
= script
[3].requireNumber();
408 Mf::Matrix4 translation
;
409 cml::matrix_translation(translation
, vec
);
410 transform
= translation
* transform
;
415 int scale(Mf::Script
& script
)
417 if (script
.getSize() == 3)
420 script
[1].requireNumber().get(vec
[0]);
421 script
[2].requireNumber().get(vec
[1]);
422 script
[3].requireNumber().get(vec
[2]);
425 cml::matrix_scale(scaling
, vec
);
426 transform
= scaling
* transform
;
428 else if (script
.getSize() == 1)
430 Mf::Scalar value
= 1.0;
431 script
[1].requireNumber().get(value
);
434 cml::matrix_uniform_scale(scaling
, value
);
435 transform
= scaling
* transform
;
439 script
.getTop().throwError("wrong number of arguments");
445 int rotate(Mf::Script
& script
)
447 Mf::Script::Value axis
= script
[1].requireString();
448 Mf::Script::Value angle
= script
[2].requireNumber();
456 cml::matrix_rotate_about_world_axis(transform
, index
, cml::rad(value
));
461 int setTexture(Mf::Script
& script
)
463 Mf::Script::Value name
= script
[1].requireString();
470 int makeTilemap(Mf::Script
& script
)
472 Mf::Script::Value table
= script
[1].requireTable();
473 Mf::Script::Value top
= script
[-1];
478 table
.pushField("width");
483 table
.pushField("tiles");
484 Mf::Script::Value tiles
= script
.getTop();
485 nTiles
= tiles
.getLength();
487 if (nTiles
% width
!= 0) table
.throwError("invalid number of tiles");
489 std::vector
< std::vector
<Tilemap::Index
> > indices
;
493 height
= nTiles
/ width
;
494 indices
.resize(height
);
496 // the indices are stored upside-down in the scene file so that they
497 // are easier to edit as text, so we'll need to load them last row
501 for (h
= height
- 1; h
>= 0; --h
)
503 std::vector
<Tilemap::Index
> row
;
505 for (w
= 0; w
< width
; ++w
, ++i
)
507 script
.checkStack(2);
508 script
.push(long(i
));
511 Tilemap::Index index
;
514 row
.push_back(index
);
520 Mf::Vector4 vertices
[height
+1][width
+1];
522 Mf::Matrix4 transposedTransform
= transform
;
523 transposedTransform
.transpose();
525 for (int h
= 0; h
<= height
; ++h
)
527 for (int w
= 0; w
<= width
; ++w
)
529 vertices
[h
][w
] = Mf::Vector4(w
, h
, 0.0, 1.0) * transposedTransform
;
533 for (int h
= 0; h
< height
; ++h
)
535 for (int w
= 0; w
< width
; ++w
)
537 if (indices
[h
][w
] == Tilemap::NO_TILE
) continue;
539 Mf::Vector3 quadVertices
[4];
541 quadVertices
[0] = Mf::demote(vertices
[h
][w
]);
542 quadVertices
[1] = Mf::demote(vertices
[h
][w
+1]);
543 quadVertices
[2] = Mf::demote(vertices
[h
+1][w
+1]);
544 quadVertices
[3] = Mf::demote(vertices
[h
+1][w
]);
546 Quad
* quad
= new Quad(quadVertices
, texture
, indices
[h
][w
]);
547 boost::shared_ptr
<Quad
> quadPtr(quad
);
549 octree
->insert(quadPtr
);
556 int makeBillboard(Mf::Script
& script
)
558 Mf::Script::Value table
= script
[1];
559 Mf::Script::Value top
= script
[-1];
563 bool blending
= false;
568 table
.pushField("tile");
571 table
.pushField("u_scale");
574 table
.pushField("blend");
577 table
.pushField("fog");
581 Mf::Vector4 vertices
[2][width
+1];
583 Mf::Matrix4 transposedTransform
= transform
;
584 transposedTransform
.transpose();
587 Mf::Scalar increment
= 1.0 / Mf::Scalar(width
);
589 for (int h
= 0; h
<= 1; ++h
)
592 for (int w
= 0; w
<= width
; ++w
, xf
+= increment
)
594 vertices
[h
][w
] = Mf::Vector4(xf
, Mf::Scalar(h
), 0.0, 1.0) *
599 for (int w
= 0; w
< width
; ++w
)
601 Mf::Vector3 quadVertices
[4];
603 quadVertices
[0] = Mf::demote(vertices
[0][w
]);
604 quadVertices
[1] = Mf::demote(vertices
[0][w
+1]);
605 quadVertices
[2] = Mf::demote(vertices
[1][w
+1]);
606 quadVertices
[3] = Mf::demote(vertices
[1][w
]);
608 Quad
* quad
= new Quad(quadVertices
, texture
, Tilemap::Index(index
));
609 quad
->setBlending(blending
);
612 boost::shared_ptr
<Quad
> quadPtr(quad
);
614 octree
->insert(quadPtr
);
622 Scene::Scene(const std::string
& name
) :
624 impl_(Scene::Impl::getInstance(name
)) {}
627 void Scene::draw(Mf::Scalar alpha
) const
629 impl_
->octree
->draw(alpha
);
632 void Scene::drawIfVisible(Mf::Scalar alpha
, const Mf::Frustum
& frustum
) const
634 impl_
->octree
->drawIfVisible(alpha
, frustum
);
638 std::string
Scene::getPath(const std::string
& name
)
640 return Mf::Resource::getPath("scenes/" + name
+ ".lua");
644 /** vim: set ts=4 sw=4 tw=80: *************************************************/