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>
41 #include <Moof/Tilemap.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 Mf::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
)
272 Mf::Matrix4 transform
;
275 Mf::Octree
<Quad
>::Ptr octree
;
287 explicit Impl(const std::string
& name
) :
288 Mf::Mippleton
<Impl
>(name
)
290 script
.importStandardLibraries();
291 importLogScript(script
);
293 importSceneBindings(script
);
297 void importSceneBindings(Mf::Script
& script
)
299 script
.importFunction("SetPlayfieldBounds",
300 boost::bind(&Impl::setPlayfieldBounds
, this, _1
));
301 script
.importFunction("SetMaximumBounds",
302 boost::bind(&Impl::setMaximumBounds
, this, _1
));
303 script
.importFunction("ResetTransform",
304 boost::bind(&Impl::resetTransform
, this, _1
));
305 script
.importFunction("Translate",
306 boost::bind(&Impl::translate
, this, _1
));
307 script
.importFunction("Scale",
308 boost::bind(&Impl::scale
, this, _1
));
309 script
.importFunction("Rotate",
310 boost::bind(&Impl::rotate
, this, _1
));
311 script
.importFunction("SetTexture",
312 boost::bind(&Impl::setTexture
, this, _1
));
313 script
.importFunction("MakeTilemap",
314 boost::bind(&Impl::makeTilemap
, this, _1
));
315 script
.importFunction("MakeBillboard",
316 boost::bind(&Impl::makeBillboard
, this, _1
));
319 Mf::Settings::getInstance().get("detail", detail
);
320 script
.push(detail
); script
.set("detail");
322 script
.push(Quad::LEFT
); script
.set("LEFT");
323 script
.push(Quad::RIGHT
); script
.set("RIGHT");
324 script
.push(Quad::TOP
); script
.set("TOP");
326 script
.push(X
); script
.set("X");
327 script
.push(Y
); script
.set("Y");
328 script
.push(Z
); script
.set("Z");
334 std::string filePath
= Scene::getPath(getName());
336 if (script
.doFile(filePath
) != Mf::Script::SUCCESS
)
340 Mf::logScript("%s", str
.c_str());
345 static int loadBox(Mf::Script
& script
, Mf::Aabb
& aabb
)
347 Mf::Script::Value table
[] =
349 script
[1].requireTable(),
350 script
[2].requireTable()
353 if (!table
[0].isTable() || !table
[1].isTable())
355 Mf::logWarning("wrong arguments to setPlayfieldBounds; ignoring...");
359 for (int i
= 0; i
<= 1; ++i
)
361 for (int j
= 1; j
<= 3; ++j
)
363 script
.push((long)j
);
364 table
[i
].pushField();
368 script
[3].get(aabb
.min
[0]);
369 script
[4].get(aabb
.min
[1]);
370 script
[5].get(aabb
.min
[2]);
371 script
[6].get(aabb
.max
[0]);
372 script
[7].get(aabb
.max
[1]);
373 script
[8].get(aabb
.max
[2]);
378 int setPlayfieldBounds(Mf::Script
& script
)
381 return loadBox(script
, bounds
);
384 int setMaximumBounds(Mf::Script
& script
)
387 int ret
= loadBox(script
, bounds
);
388 octree
= Mf::Octree
<Quad
>::alloc(bounds
);
392 int resetTransform(Mf::Script
& script
)
394 transform
.identity();
398 int translate(Mf::Script
& script
)
400 Mf::Script::Value x
= script
[1].requireNumber();
401 Mf::Script::Value y
= script
[2].requireNumber();
402 Mf::Script::Value z
= script
[3].requireNumber();
409 Mf::Matrix4 translation
;
410 cml::matrix_translation(translation
, vec
);
411 transform
= translation
* transform
;
416 int scale(Mf::Script
& script
)
418 if (script
.getSize() == 3)
421 script
[1].requireNumber().get(vec
[0]);
422 script
[2].requireNumber().get(vec
[1]);
423 script
[3].requireNumber().get(vec
[2]);
426 cml::matrix_scale(scaling
, vec
);
427 transform
= scaling
* transform
;
429 else if (script
.getSize() == 1)
431 Mf::Scalar value
= 1.0;
432 script
[1].requireNumber().get(value
);
435 cml::matrix_uniform_scale(scaling
, value
);
436 transform
= scaling
* transform
;
440 script
.getTop().throwError("wrong number of arguments");
446 int rotate(Mf::Script
& script
)
448 Mf::Script::Value axis
= script
[1].requireString();
449 Mf::Script::Value angle
= script
[2].requireNumber();
457 cml::matrix_rotate_about_world_axis(transform
, index
, cml::rad(value
));
462 int setTexture(Mf::Script
& script
)
464 Mf::Script::Value name
= script
[1].requireString();
471 int makeTilemap(Mf::Script
& script
)
473 Mf::Script::Value table
= script
[1].requireTable();
474 Mf::Script::Value top
= script
[-1];
479 table
.pushField("width");
484 table
.pushField("tiles");
485 Mf::Script::Value tiles
= script
.getTop();
486 nTiles
= tiles
.getLength();
488 if (nTiles
% width
!= 0) table
.throwError("invalid number of tiles");
490 std::vector
< std::vector
<Mf::Tilemap::Index
> > indices
;
494 height
= nTiles
/ width
;
495 indices
.resize(height
);
497 // the indices are stored upside-down in the scene file so that they
498 // are easier to edit as text, so we'll need to load them last row
502 for (h
= height
- 1; h
>= 0; --h
)
504 std::vector
<Mf::Tilemap::Index
> row
;
506 for (w
= 0; w
< width
; ++w
, ++i
)
508 script
.checkStack(2);
509 script
.push(long(i
));
515 row
.push_back(Mf::Tilemap::Index(index
));
521 Mf::Vector4 vertices
[height
+1][width
+1];
523 Mf::Matrix4 transposedTransform
= transform
;
524 transposedTransform
.transpose();
526 for (int h
= 0; h
<= height
; ++h
)
528 for (int w
= 0; w
<= width
; ++w
)
530 vertices
[h
][w
] = Mf::Vector4(Mf::Scalar(w
), Mf::Scalar(h
), 0.0, 1.0) *
535 for (int h
= 0; h
< height
; ++h
)
537 for (int w
= 0; w
< width
; ++w
)
539 if (indices
[h
][w
] == Mf::Tilemap::NO_TILE
) continue;
541 Mf::Vector3 quadVertices
[4];
543 quadVertices
[0] = Mf::demote(vertices
[h
][w
]);
544 quadVertices
[1] = Mf::demote(vertices
[h
][w
+1]);
545 quadVertices
[2] = Mf::demote(vertices
[h
+1][w
+1]);
546 quadVertices
[3] = Mf::demote(vertices
[h
+1][w
]);
548 Quad
* quad
= new Quad(quadVertices
, texture
, indices
[h
][w
]);
549 boost::shared_ptr
<Quad
> quadPtr(quad
);
551 octree
->insert(quadPtr
);
558 int makeBillboard(Mf::Script
& script
)
560 Mf::Script::Value table
= script
[1];
561 Mf::Script::Value top
= script
[-1];
565 bool blending
= false;
570 table
.pushField("tile");
573 table
.pushField("u_scale");
576 table
.pushField("blend");
579 table
.pushField("fog");
583 Mf::Vector4 vertices
[2][width
+1];
585 Mf::Matrix4 transposedTransform
= transform
;
586 transposedTransform
.transpose();
589 Mf::Scalar increment
= 1.0 / Mf::Scalar(width
);
591 for (int h
= 0; h
<= 1; ++h
)
594 for (int w
= 0; w
<= width
; ++w
, xf
+= increment
)
596 vertices
[h
][w
] = Mf::Vector4(xf
, Mf::Scalar(h
), 0.0, 1.0) *
601 for (int w
= 0; w
< width
; ++w
)
603 Mf::Vector3 quadVertices
[4];
605 quadVertices
[0] = Mf::demote(vertices
[0][w
]);
606 quadVertices
[1] = Mf::demote(vertices
[0][w
+1]);
607 quadVertices
[2] = Mf::demote(vertices
[1][w
+1]);
608 quadVertices
[3] = Mf::demote(vertices
[1][w
]);
610 Quad
* quad
= new Quad(quadVertices
, texture
, Mf::Tilemap::Index(index
));
611 quad
->setBlending(blending
);
614 boost::shared_ptr
<Quad
> quadPtr(quad
);
616 octree
->insert(quadPtr
);
624 Scene::Scene(const std::string
& name
) :
626 impl_(Scene::Impl::getInstance(name
)) {}
629 void Scene::draw(Mf::Scalar alpha
) const
631 impl_
->octree
->draw(alpha
);
634 void Scene::drawIfVisible(Mf::Scalar alpha
, const Mf::Frustum
& frustum
) const
636 impl_
->octree
->drawIfVisible(alpha
, frustum
);
640 std::string
Scene::getPath(const std::string
& name
)
642 return Mf::Resource::getPath("scenes/" + name
+ ".lua");
646 /** vim: set ts=4 sw=4 tw=80: *************************************************/