#include "Entity.hh"
#include "Log.hh"
#include "Math.hh"
-#include "Mippleton.hh"
-#include "OpenGL.hh"
#include "Scene.hh"
#include "Serializable.hh"
#include "Tilemap.hh"
namespace Mf {
-class Scene::Impl : public Mippleton<Impl>
+static void loadBox(Aabb& theBox, SerializableP obj)
{
- class Quad : public Entity
- {
- public:
- Quad(const Vector3 vertices[4], const std::string& texture,
- Tilemap::Index tileIndex) :
- tilemap_(texture),
- detail_(0),
- blending_(false),
- fog_(false)
- {
- for (int i = 0, num = 0; i < 4; ++i)
- {
- for (int j = 0; j < 3; ++j, ++num)
- {
- vertices_[num] = vertices[i][j];
- }
- }
-
- if (!tilemap_.getTileCoords(tileIndex, texCoords_))
- {
- logWarning("no index %d in texture %s", tileIndex,
- texture.c_str());
-
- texCoords_[0] = texCoords_[1] =
- texCoords_[3] = texCoords_[6] = 0.0;
- texCoords_[2] = texCoords_[4] =
- texCoords_[5] = texCoords_[7] = 1.0;
- }
-
- aabb_.encloseVertices(vertices, 4);
- sphere_.point = aabb_.getCenter();
- sphere_.radius = (aabb_.min - sphere_.point).length();
- }
-
- void setDetail(long detail)
- {
- detail_ = detail;
- }
-
- void setBlending(bool blending)
- {
- blending_ = blending;
- }
-
- void setFog(bool fog)
- {
- fog_ = fog;
- }
-
- void draw(Scalar alpha = 0.0) const
- {
- if (blending_)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- if (fog_)
- {
- glEnable(GL_FOG);
- glFogi(GL_FOG_MODE, GL_LINEAR);
- }
-
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- tilemap_.bind();
-
- glVertexPointer(3, GL_SCALAR, 0, vertices_);
- glTexCoordPointer(2, GL_SCALAR, 0, texCoords_);
-
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ Serializable::Array numbers;
- glDisable(GL_BLEND);
- glDisable(GL_FOG);
- }
+ if (obj->get(numbers) && numbers.size() == 6)
+ {
+ Serializable::Float num;
+
+ if (numbers[0]->getNumber(num)) theBox.min[0] = Scalar(num);
+ if (numbers[1]->getNumber(num)) theBox.min[1] = Scalar(num);
+ if (numbers[2]->getNumber(num)) theBox.min[2] = Scalar(num);
+ if (numbers[3]->getNumber(num)) theBox.max[0] = Scalar(num);
+ if (numbers[4]->getNumber(num)) theBox.max[1] = Scalar(num);
+ if (numbers[5]->getNumber(num)) theBox.max[2] = Scalar(num);
+ }
+}
- bool isVisible(const Camera& cam) const
- {
- return sphere_.isVisible(cam);
- }
- private:
- Scalar vertices_[12];
- Scalar texCoords_[8];
-
- Tilemap tilemap_;
+static void loadTilemap(SerializableP root, const Matrix4& transform,
+ const std::string& texture, OctreeP octree)
+{
+ Serializable::Map rootObj;
+ Serializable::Map::iterator it;
- long detail_;
- bool blending_;
- bool fog_;
- };
+ if (!root->get(rootObj))
+ {
+ logError("invalid tilemap instruction");
+ return;
+ }
+ long width = 1;
+ long height = 1;
+ std::vector< std::vector<Tilemap::Index> > indices;
- static void loadBox(Aabb& theBox, SerializableP obj)
+ if ((it = rootObj.find("width")) != rootObj.end())
{
- Serializable::Array numbers;
-
- if (obj->get(numbers) && numbers.size() == 6)
- {
- Serializable::Float num;
-
- if (numbers[0]->getNumber(num)) theBox.min[0] = Scalar(num);
- if (numbers[1]->getNumber(num)) theBox.min[1] = Scalar(num);
- if (numbers[2]->getNumber(num)) theBox.min[2] = Scalar(num);
- if (numbers[3]->getNumber(num)) theBox.max[0] = Scalar(num);
- if (numbers[4]->getNumber(num)) theBox.max[1] = Scalar(num);
- if (numbers[5]->getNumber(num)) theBox.max[2] = Scalar(num);
- }
+ (*it).second->get(width);
}
-
-public:
- Impl(const std::string& name) :
- Mippleton<Impl>(name)
+ else
{
- loadFromFile();
+ logError("missing required field width for tilemap instruction");
+ return;
}
+ Serializable::Array tiles;
- void loadInstructions(SerializableP root)
+ if ((it = rootObj.find("tiles")) != rootObj.end() &&
+ (*it).second->get(tiles) &&
+ tiles.size() % width == 0)
{
- Serializable::Array rootObj;
- Serializable::Array::iterator it;
+ Serializable::Array::iterator jt;
+ int w, h;
- if (!root->get(rootObj))
- {
- logError("scene instructions must be an array");
- return;
- }
+ height = tiles.size() / width;
+ indices.resize(height);
- Matrix4 transform;
- std::string texture;
+ // the indices are stored upside-down in the scene file so that they
+ // are easier to edit as text, so we'll need to load them last row
+ // first
- for (it = rootObj.begin(); it != rootObj.end(); ++it)
+ for (h = height - 1, jt = tiles.begin(); jt != tiles.end(); --h)
{
- std::string instruction;
+ std::vector<Tilemap::Index> row;
- if ((*it)->get(instruction))
+ for (w = 0; w < width && jt != tiles.end(); ++w, ++jt)
{
- if (instruction == "reset_transform")
- {
- transform.identity();
- }
- else if (instruction == "translate")
- {
- Serializable::Array values;
-
- ++it;
- if ((*it)->get(values))
- {
- Vector3 vec;
-
- for (size_t i = 0; i < values.size(); ++i)
- {
- Serializable::Float value;
-
- if (values[i]->getNumber(value))
- {
- vec[i] = value;
- }
- }
+ Serializable::Integer index;
- Matrix4 translation;
- cml::matrix_translation(translation, vec);
- transform = translation * transform;
- }
- }
- else if (instruction == "scale")
+ if ((*jt)->get(index))
{
- Serializable::Array values;
-
- ++it;
- if ((*it)->get(values))
- {
- if (values.size() == 1)
- {
- Serializable::Float value = 1.0;
+ row.push_back(Tilemap::Index(index));
+ }
+ }
- values[0]->getNumber(value);
+ indices[h] = row;
+ }
+ }
+ else
+ {
+ logError("invalid tiles in tilemap instruction");
+ return;
+ }
- Matrix4 scaling;
- cml::matrix_uniform_scale(scaling,
- Scalar(value));
- transform = scaling * transform;
- }
- else if (values.size() == 3)
- {
- Vector3 vec;
+ Vector4 vertices[height+1][width+1];
- for (size_t i = 0; i < values.size(); ++i)
- {
- Serializable::Float value;
+ Matrix4 transposedTransform = transform;
+ transposedTransform.transpose();
- if (values[i]->getNumber(value))
- {
- vec[i] = value;
- }
- }
+ for (int h = 0; h <= height; ++h)
+ {
+ for (int w = 0; w <= width; ++w)
+ {
+ vertices[h][w] = Vector4(Scalar(w), Scalar(h), 0.0, 1.0) *
+ transposedTransform;
+ }
+ }
- Matrix4 scaling;
- cml::matrix_scale(scaling, vec);
- transform = scaling * transform;
- }
- }
- }
- else if (instruction == "rotate")
- {
- Serializable::Array values;
+ for (int h = 0; h < height; ++h)
+ {
+ for (int w = 0; w < width; ++w)
+ {
+ if (indices[h][w] == Tilemap::NO_TILE) continue;
- ++it;
- if ((*it)->get(values))
- {
- if (values.size() == 2)
- {
- std::string axis;
- size_t index = 0;
- Serializable::Float value = 0.0;
+ Vector3 quadVertices[4];
- if (values[0]->get(axis))
- {
- if (axis == "x") index = 0;
- else if (axis == "y") index = 1;
- else if (axis == "z") index = 2;
+ demoteVector(quadVertices[0], vertices[h][w]);
+ demoteVector(quadVertices[1], vertices[h][w+1]);
+ demoteVector(quadVertices[2], vertices[h+1][w+1]);
+ demoteVector(quadVertices[3], vertices[h+1][w]);
- values[1]->getNumber(value);
- }
+ Quad* quad = new Quad(quadVertices, texture, indices[h][w]);
+ boost::shared_ptr<Quad> quadPtr(quad);
- cml::matrix_rotate_about_world_axis(transform,
- index, cml::rad(Scalar(value)));
- }
- }
- }
- else if (instruction == "texture")
- {
- ++it;
- (*it)->get(texture);
- }
- else if (instruction == "tilemap")
- {
- ++it;
- loadTilemap(*it, transform, texture);
- }
- else if (instruction == "billboard")
- {
- ++it;
- loadBillboard(*it, transform, texture);
- }
- }
+ octree->insert(quadPtr);
}
}
+}
+static void loadBillboard(SerializableP root, const Matrix4& transform,
+ const std::string& texture, OctreeP octree)
+{
+ Serializable::Map rootObj;
+ Serializable::Map::iterator it;
- void loadTilemap(SerializableP root, const Matrix4& transform,
- const std::string& texture)
- {
- Serializable::Map rootObj;
- Serializable::Map::iterator it;
+ Tilemap::Index index = 0;
+ long width = 1;
+ bool blending = false;
+ bool fog = false;
- if (!root->get(rootObj))
+ if (root->get(rootObj))
+ {
+ if ((it = rootObj.find("tile")) != rootObj.end())
{
- logError("invalid tilemap instruction");
- return;
+ Serializable::Integer value;
+ if ((*it).second->get(value))
+ {
+ index = Tilemap::Index(value);
+ }
}
- long width = 1;
- long height = 1;
- std::vector< std::vector<Tilemap::Index> > indices;
-
- if ((it = rootObj.find("width")) != rootObj.end())
+ if ((it = rootObj.find("u_scale")) != rootObj.end())
{
(*it).second->get(width);
}
- else
+
+ if ((it = rootObj.find("blend")) != rootObj.end())
{
- logError("missing required field width for tilemap instruction");
- return;
+ (*it).second->get(blending);
}
- Serializable::Array tiles;
-
- if ((it = rootObj.find("tiles")) != rootObj.end() &&
- (*it).second->get(tiles) &&
- tiles.size() % width == 0)
+ if ((it = rootObj.find("fog")) != rootObj.end())
{
- Serializable::Array::iterator jt;
- int w, h;
-
- height = tiles.size() / width;
- indices.resize(height);
+ (*it).second->get(fog);
+ }
+ }
- // the indices are stored upside-down in the scene file so that they
- // are easier to edit as text, so we'll need to load them last row
- // first
- for (h = height - 1, jt = tiles.begin(); jt != tiles.end(); --h)
- {
- std::vector<Tilemap::Index> row;
+ Vector4 vertices[2][width+1];
- for (w = 0; w < width && jt != tiles.end(); ++w, ++jt)
- {
- Serializable::Integer index;
+ Matrix4 transposedTransform = transform;
+ transposedTransform.transpose();
- if ((*jt)->get(index))
- {
- row.push_back(Tilemap::Index(index));
- }
- }
+ Scalar xf;
+ Scalar increment = 1.0 / Scalar(width);
- indices[h] = row;
- }
- }
- else
+ for (int h = 0; h <= 1; ++h)
+ {
+ xf = 0.0;
+ for (int w = 0; w <= width; ++w, xf += increment)
{
- logError("invalid tiles in tilemap instruction");
- return;
+ vertices[h][w] = Vector4(xf, Scalar(h), 0.0, 1.0) *
+ transposedTransform;
}
+ }
- Vector4 vertices[height+1][width+1];
+ for (int w = 0; w < width; ++w)
+ {
+ Vector3 quadVertices[4];
- Matrix4 transposedTransform = transform;
- transposedTransform.transpose();
+ demoteVector(quadVertices[0], vertices[0][w]);
+ demoteVector(quadVertices[1], vertices[0][w+1]);
+ demoteVector(quadVertices[2], vertices[1][w+1]);
+ demoteVector(quadVertices[3], vertices[1][w]);
- for (int h = 0; h <= height; ++h)
- {
- for (int w = 0; w <= width; ++w)
- {
- vertices[h][w] = Vector4(Scalar(w), Scalar(h), 0.0, 1.0) *
- transposedTransform;
- }
- }
+ Quad* quad = new Quad(quadVertices, texture, index);
+ quad->setBlending(blending);
+ quad->setFog(fog);
- for (int h = 0; h < height; ++h)
- {
- for (int w = 0; w < width; ++w)
- {
- if (indices[h][w] == Tilemap::NO_TILE) continue;
+ boost::shared_ptr<Quad> quadPtr(quad);
- Vector3 quadVertices[4];
+ octree->insert(quadPtr);
+ }
+}
- demoteVector(quadVertices[0], vertices[h][w]);
- demoteVector(quadVertices[1], vertices[h][w+1]);
- demoteVector(quadVertices[2], vertices[h+1][w+1]);
- demoteVector(quadVertices[3], vertices[h+1][w]);
- Quad* quad = new Quad(quadVertices, texture, indices[h][w]);
- boost::shared_ptr<Quad> quadPtr(quad);
+static void loadInstructions(SerializableP root, OctreeP octree)
+{
+ Serializable::Array rootObj;
+ Serializable::Array::iterator it;
- octree->insert(quadPtr);
- }
- }
+ if (!root->get(rootObj))
+ {
+ logError("scene instructions must be an array");
+ return;
}
- void loadBillboard(SerializableP root, const Matrix4& transform,
- const std::string& texture)
- {
- Serializable::Map rootObj;
- Serializable::Map::iterator it;
+ Matrix4 transform;
+ std::string texture;
- Tilemap::Index index = 0;
- long width = 1;
- bool blending = false;
- bool fog = false;
+ for (it = rootObj.begin(); it != rootObj.end(); ++it)
+ {
+ std::string instruction;
- if (root->get(rootObj))
+ if ((*it)->get(instruction))
{
- if ((it = rootObj.find("tile")) != rootObj.end())
+ if (instruction == "reset_transform")
{
- Serializable::Integer value;
- if ((*it).second->get(value))
- {
- index = Tilemap::Index(value);
- }
+ transform.identity();
}
-
- if ((it = rootObj.find("u_scale")) != rootObj.end())
+ else if (instruction == "translate")
{
- (*it).second->get(width);
- }
+ Serializable::Array values;
- if ((it = rootObj.find("blend")) != rootObj.end())
- {
- (*it).second->get(blending);
- }
-
- if ((it = rootObj.find("fog")) != rootObj.end())
- {
- (*it).second->get(fog);
- }
- }
-
-
- Vector4 vertices[2][width+1];
+ ++it;
+ if ((*it)->get(values))
+ {
+ Vector3 vec;
- Matrix4 transposedTransform = transform;
- transposedTransform.transpose();
+ for (size_t i = 0; i < values.size(); ++i)
+ {
+ Serializable::Float value;
- Scalar xf;
- Scalar increment = 1.0 / Scalar(width);
+ if (values[i]->getNumber(value))
+ {
+ vec[i] = value;
+ }
+ }
- for (int h = 0; h <= 1; ++h)
- {
- xf = 0.0;
- for (int w = 0; w <= width; ++w, xf += increment)
- {
- vertices[h][w] = Vector4(xf, Scalar(h), 0.0, 1.0) *
- transposedTransform;
+ Matrix4 translation;
+ cml::matrix_translation(translation, vec);
+ transform = translation * transform;
+ }
}
- }
-
- for (int w = 0; w < width; ++w)
- {
- Vector3 quadVertices[4];
-
- demoteVector(quadVertices[0], vertices[0][w]);
- demoteVector(quadVertices[1], vertices[0][w+1]);
- demoteVector(quadVertices[2], vertices[1][w+1]);
- demoteVector(quadVertices[3], vertices[1][w]);
-
- Quad* quad = new Quad(quadVertices, texture, index);
- quad->setBlending(blending);
- quad->setFog(fog);
+ else if (instruction == "scale")
+ {
+ Serializable::Array values;
- boost::shared_ptr<Quad> quadPtr(quad);
+ ++it;
+ if ((*it)->get(values))
+ {
+ if (values.size() == 1)
+ {
+ Serializable::Float value = 1.0;
- octree->insert(quadPtr);
- }
- }
+ values[0]->getNumber(value);
+ Matrix4 scaling;
+ cml::matrix_uniform_scale(scaling,
+ Scalar(value));
+ transform = scaling * transform;
+ }
+ else if (values.size() == 3)
+ {
+ Vector3 vec;
- void loadFromFile()
- {
- std::string filePath = Scene::getPath(getName());
+ for (size_t i = 0; i < values.size(); ++i)
+ {
+ Serializable::Float value;
- Deserializer deserializer(filePath, true);
- SerializableP root = deserializer.deserialize();
+ if (values[i]->getNumber(value))
+ {
+ vec[i] = value;
+ }
+ }
- Serializable::Map rootObj;
- Serializable::Map::iterator it;
+ Matrix4 scaling;
+ cml::matrix_scale(scaling, vec);
+ transform = scaling * transform;
+ }
+ }
+ }
+ else if (instruction == "rotate")
+ {
+ Serializable::Array values;
- if (!root || !root->get(rootObj))
- {
- logError("no root map in scene file");
- return;
- }
+ ++it;
+ if ((*it)->get(values))
+ {
+ if (values.size() == 2)
+ {
+ std::string axis;
+ size_t index = 0;
+ Serializable::Float value = 0.0;
- if ((it = rootObj.find("playfield_bounds")) != rootObj.end())
- {
- loadBox(playfieldBounds, (*it).second);
- }
- if ((it = rootObj.find("maximum_bounds")) != rootObj.end())
- {
- loadBox(maximumBounds, (*it).second);
- }
- else
- {
- logError("missing required maximum bounds");
- return;
- }
+ if (values[0]->get(axis))
+ {
+ if (axis == "x") index = 0;
+ else if (axis == "y") index = 1;
+ else if (axis == "z") index = 2;
- // create the tree to store the quads
- octree = Octree::alloc(maximumBounds);
+ values[1]->getNumber(value);
+ }
- if ((it = rootObj.find("instructions")) != rootObj.end())
- {
- loadInstructions((*it).second);
+ cml::matrix_rotate_about_world_axis(transform,
+ index, cml::rad(Scalar(value)));
+ }
+ }
+ }
+ else if (instruction == "texture")
+ {
+ ++it;
+ (*it)->get(texture);
+ }
+ else if (instruction == "tilemap")
+ {
+ ++it;
+ loadTilemap(*it, transform, texture, octree);
+ }
+ else if (instruction == "billboard")
+ {
+ ++it;
+ loadBillboard(*it, transform, texture, octree);
+ }
}
-
-
-
- octree->sort();
}
+}
- void draw(Scalar alpha, const Camera& cam) const
- {
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-
- octree->drawIfVisible(alpha, cam);
+static std::string getPath(const std::string& name)
+{
+ return Resource::getPath("scenes/" + name + ".json");
+}
- //glDisableClientState(GL_VERTEX_ARRAY);
- //glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+OctreeP loadScene(const std::string& name)
+{
+ std::string filePath = getPath(name);
- //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ Deserializer deserializer(filePath, true);
+ SerializableP root = deserializer.deserialize();
- //Texture::resetBind();
- //glColor3f(0.0f, 1.0f, 0.0f);
- //playfieldBounds.draw();
- //glColor3f(0.0f, 0.0f, 1.0f);
- //maximumBounds.draw();
+ Serializable::Map rootObj;
+ Serializable::Map::iterator it;
- //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ if (!root || !root->get(rootObj))
+ {
+ logError("no root map in scene file");
+ return OctreeP();
}
-
Aabb playfieldBounds;
Aabb maximumBounds;
- OctreeP octree;
-};
-
-
-Scene::Scene(const std::string& name) :
- // pass through
- impl_(Scene::Impl::getInstance(name)) {}
-
-
-void Scene::draw(Scalar alpha, const Camera& cam) const
-{
- // pass through
- impl_->draw(alpha, cam);
-}
-
-void Scene::refresh()
-{
- //impl_->objects.clear();
- impl_->loadFromFile();
-}
+ if ((it = rootObj.find("playfield_bounds")) != rootObj.end())
+ {
+ loadBox(playfieldBounds, (*it).second);
+ }
+ if ((it = rootObj.find("maximum_bounds")) != rootObj.end())
+ {
+ loadBox(maximumBounds, (*it).second);
+ }
+ else
+ {
+ logError("missing required maximum bounds");
+ return OctreeP();
+ }
+ // create the tree to store the quads
+ OctreeP octree = Octree::alloc(maximumBounds);
-OctreeP Scene::getOctree() const
-{
- // pass through
- return impl_->octree;
-}
+ if ((it = rootObj.find("instructions")) != rootObj.end())
+ {
+ loadInstructions((*it).second, octree);
+ }
-/**
- * Specialized search location for scene files. They can be found in the
- * "scenes" subdirectory of any of the searched directories.
- */
+ octree->sort();
-std::string Scene::getPath(const std::string& name)
-{
- return Resource::getPath("scenes/" + name + ".json");
+ return octree;
}