*******************************************************************************/
-#include <iostream>
#include <map>
#include <vector>
#include "Aabb.hh"
#include "Camera.hh"
-#include "Cullable.hh"
#include "Deserializer.hh"
-#include "Drawable.hh"
+#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::SceneImpl : public Mippleton<SceneImpl>
+static void loadBox(Aabb& theBox, SerializableP obj)
{
- class Scenery : public Drawable, public Cullable
+ Serializable::Array numbers;
+
+ if (obj->get(numbers) && numbers.size() == 6)
{
- public:
- Scenery(const Matrix4& transform, const std::string& textureName) :
- transformation(transform),
- image(textureName) {}
-
- protected:
- Matrix4 transformation;
- Tilemap image;
- bool blending;
- long detail;
- bool fog;
- };
-
- class TilePanel : public Scenery
+ 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);
+ }
+}
+
+
+static void loadTilemap(SerializableP root, const Matrix4& transform,
+ const std::string& texture, OctreeP octree)
+{
+ Serializable::Map rootObj;
+ Serializable::Map::iterator it;
+
+ if (!root->get(rootObj))
{
- public:
- TilePanel(const Matrix4& transform, const std::string& textureName,
- SerializablePtr root) :
- Scenery(transform, textureName),
- width(1),
- height(1)
- {
- std::map<std::string,SerializablePtr> rootObj;
+ logError("invalid tilemap instruction");
+ return;
+ }
- if (root->get(rootObj))
- {
- std::map<std::string,SerializablePtr>::iterator it;
+ long width = 1;
+ long height = 1;
+ std::vector< std::vector<Tilemap::Index> > indices;
- if ((it = rootObj.find("width")) != rootObj.end())
- {
- (*it).second->get(width);
- }
- if ((it = rootObj.find("tiles")) != rootObj.end())
- {
- std::vector<SerializablePtr> theTiles;
+ if ((it = rootObj.find("width")) != rootObj.end())
+ {
+ (*it).second->get(width);
+ }
+ else
+ {
+ logError("missing required field width for tilemap instruction");
+ return;
+ }
- if ((*it).second->get(theTiles))
- {
- std::vector<SerializablePtr>::iterator jt;
+ Serializable::Array tiles;
- height = theTiles.size() / width;
- int w, h;
+ if ((it = rootObj.find("tiles")) != rootObj.end() &&
+ (*it).second->get(tiles) &&
+ tiles.size() % width == 0)
+ {
+ Serializable::Array::iterator jt;
+ int w, h;
- indices.resize(height);
+ height = tiles.size() / width;
+ indices.resize(height);
- for (h = height - 1, jt = theTiles.begin();
- jt != theTiles.end(); h--)
- {
- std::vector<Tilemap::Index> row;
+ // 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 (w = 0; w < width && jt != theTiles.end();
- w++, jt++)
- {
- long index;
+ for (h = height - 1, jt = tiles.begin(); jt != tiles.end(); --h)
+ {
+ std::vector<Tilemap::Index> row;
- if ((*jt)->get(index))
- {
- row.push_back(Tilemap::Index(index));
- }
- }
+ for (w = 0; w < width && jt != tiles.end(); ++w, ++jt)
+ {
+ Serializable::Integer index;
- indices[h] = row;
- }
- }
+ if ((*jt)->get(index))
+ {
+ row.push_back(Tilemap::Index(index));
}
}
+
+ indices[h] = row;
}
+ }
+ else
+ {
+ logError("invalid tiles in tilemap instruction");
+ return;
+ }
- void draw(Scalar alpha)
- {
- glPushMatrix();
- //std::cout << "transforming..." << std::endl;
- //std::cout << transformation << std::endl;
- glMultMatrixf(transformation.data());
+ Vector4 vertices[height+1][width+1];
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- image.bind();
+ Matrix4 transposedTransform = transform;
+ transposedTransform.transpose();
- long x, y;
- Scalar xf, yf;
+ 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;
+ }
+ }
- for (y = 0, yf = 0.0; y < height; y++, yf += 1.0)
- {
- for (x = 0, xf = 0.0; x < width; x++, xf += 1.0)
- {
- Scalar texCoords[8];
+ for (int h = 0; h < height; ++h)
+ {
+ for (int w = 0; w < width; ++w)
+ {
+ if (indices[h][w] == Tilemap::NO_TILE) continue;
- Tilemap::Index index = indices[y][x];
+ Vector3 quadVertices[4];
- if (image.getTileCoords(index, texCoords))
- {
- glBegin(GL_TRIANGLE_FAN);
- glTexCoord2f(texCoords[0], texCoords[1]);
- glVertex3f(xf, yf, 0.0f);
- glTexCoord2f(texCoords[2], texCoords[3]);
- glVertex3f(xf+1.0, yf, 0.0f);
- glTexCoord2f(texCoords[4], texCoords[5]);
- glVertex3f(xf+1.0, yf+1.0, 0.0f);
- glTexCoord2f(texCoords[6], texCoords[7]);
- glVertex3f(xf, yf+1.0, 0.0f);
- glEnd();
- }
- }
- }
+ 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]);
- glPopMatrix();
- }
+ Quad* quad = new Quad(quadVertices, texture, indices[h][w]);
+ boost::shared_ptr<Quad> quadPtr(quad);
- bool isVisible(const Camera& cam)
- {
- return true;
+ octree->insert(quadPtr);
}
+ }
+}
+
+static void loadBillboard(SerializableP root, const Matrix4& transform,
+ const std::string& texture, OctreeP octree)
+{
+ Serializable::Map rootObj;
+ Serializable::Map::iterator it;
- private:
- long width, height;
- std::vector<std::vector<Tilemap::Index> > indices;
- };
+ Tilemap::Index index = 0;
+ long width = 1;
+ bool blending = false;
+ bool fog = false;
- class Billboard : public Scenery
+ if (root->get(rootObj))
{
- public:
- Billboard(const Matrix4& transform, const std::string& textureName,
- SerializablePtr root) :
- Scenery(transform, textureName),
- index(0)
+ if ((it = rootObj.find("tile")) != rootObj.end())
{
- std::map<std::string,SerializablePtr> rootObj;
-
- if (root->get(rootObj))
+ Serializable::Integer value;
+ if ((*it).second->get(value))
{
- std::map<std::string,SerializablePtr>::iterator it;
-
- if ((it = rootObj.find("tile")) != rootObj.end())
- {
- long value;
- if ((*it).second->get(value))
- {
- index = Tilemap::Index(value);
- }
- }
+ index = Tilemap::Index(value);
}
+ }
- image.getTileCoords(index, texCoords);
+ if ((it = rootObj.find("u_scale")) != rootObj.end())
+ {
+ (*it).second->get(width);
}
- void draw(Scalar alpha)
+ if ((it = rootObj.find("blend")) != rootObj.end())
{
- glPushMatrix();
- glMultMatrixf(transformation.data());
-
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- image.bind();
-
- glBegin(GL_TRIANGLE_FAN);
- glTexCoord2f(texCoords[0], texCoords[1]);
- glVertex2f(0.0f, 0.0f);
- glTexCoord2f(texCoords[2], texCoords[3]);
- glVertex2f(1.0f, 0.0f);
- glTexCoord2f(texCoords[4], texCoords[5]);
- glVertex2f(1.0f, 1.0f);
- glTexCoord2f(texCoords[6], texCoords[7]);
- glVertex2f(0.0f, 1.0f);
- glEnd();
-
- glPopMatrix();
+ (*it).second->get(blending);
}
- bool isVisible(const Camera& cam)
+ if ((it = rootObj.find("fog")) != rootObj.end())
{
- return false;
+ (*it).second->get(fog);
}
+ }
- private:
- Tilemap::Index index;
- Scalar texCoords[8];
- };
+ Vector4 vertices[2][width+1];
- static bool loadBox(Aabb& theBox, SerializablePtr obj)
- {
- std::vector<SerializablePtr> numbers;
+ Matrix4 transposedTransform = transform;
+ transposedTransform.transpose();
+
+ Scalar xf;
+ Scalar increment = 1.0 / Scalar(width);
- if (obj->get(numbers))
+ for (int h = 0; h <= 1; ++h)
+ {
+ xf = 0.0;
+ for (int w = 0; w <= width; ++w, xf += increment)
{
- if (numbers.size() == 6)
- {
- double num;
+ vertices[h][w] = Vector4(xf, Scalar(h), 0.0, 1.0) *
+ transposedTransform;
+ }
+ }
- if (numbers[0]->getNumber(num))
- {
+ 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);
+
+ boost::shared_ptr<Quad> quadPtr(quad);
- return false;
+ octree->insert(quadPtr);
}
+}
+
-public:
- SceneImpl(const std::string& name) :
- Mippleton<SceneImpl>(name)
+static void loadInstructions(SerializableP root, OctreeP octree)
+{
+ Serializable::Array rootObj;
+ Serializable::Array::iterator it;
+
+ if (!root->get(rootObj))
{
- loadFromFile();
+ logError("scene instructions must be an array");
+ return;
}
+ Matrix4 transform;
+ std::string texture;
- void loadInstructions(SerializablePtr root)
+ for (it = rootObj.begin(); it != rootObj.end(); ++it)
{
- std::vector<SerializablePtr> rootObj;
+ std::string instruction;
- if (root->get(rootObj))
+ if ((*it)->get(instruction))
{
- std::vector<SerializablePtr>::iterator it;
-
- Matrix4 transform;
- std::string texture;
-
- for (it = rootObj.begin(); it != rootObj.end(); it++)
+ if (instruction == "reset_transform")
{
- std::string instruction;
+ transform.identity();
+ }
+ else if (instruction == "translate")
+ {
+ Serializable::Array values;
- if ((*it)->get(instruction))
+ ++it;
+ if ((*it)->get(values))
{
- if (instruction == "reset_transform")
- {
- transform.identity();
- //std::cout << "===================RESET=====================" << std::endl;
- }
- else if (instruction == "translate")
+ Vector3 vec;
+
+ for (size_t i = 0; i < values.size(); ++i)
{
- std::vector<SerializablePtr> values;
+ Serializable::Float value;
- it++;
- if ((*it)->get(values))
+ if (values[i]->getNumber(value))
{
- Vector3 vec;
+ vec[i] = value;
+ }
+ }
- for (size_t i = 0; i < values.size(); i++)
- {
- double value;
+ Matrix4 translation;
+ cml::matrix_translation(translation, vec);
+ transform = translation * transform;
+ }
+ }
+ else if (instruction == "scale")
+ {
+ Serializable::Array values;
- if (values[i]->getNumber(value))
- {
- vec[i] = value;
- }
- }
+ ++it;
+ if ((*it)->get(values))
+ {
+ if (values.size() == 1)
+ {
+ Serializable::Float value = 1.0;
- Matrix4 translation;
- cml::matrix_translation(translation, vec);
- transform = translation * transform;
- //std::cout << "TRANSLATE\t" << vec << std::endl
- //<< transform << std::endl;
- }
+ values[0]->getNumber(value);
+
+ Matrix4 scaling;
+ cml::matrix_uniform_scale(scaling,
+ Scalar(value));
+ transform = scaling * transform;
}
- else if (instruction == "scale")
+ else if (values.size() == 3)
{
- std::vector<SerializablePtr> values;
+ Vector3 vec;
- it++;
- if ((*it)->get(values))
+ for (size_t i = 0; i < values.size(); ++i)
{
- if (values.size() == 1)
- {
- double value = 1.0;
-
- values[0]->getNumber(value);
+ Serializable::Float value;
- Matrix4 scaling;
- cml::matrix_uniform_scale(scaling, Scalar(value));
- transform = scaling * transform;
- //std::cout << "SCALE\t\t" << value << std::endl
- //<< transform << std::endl;
- }
- else if (values.size() == 3)
+ if (values[i]->getNumber(value))
{
- Vector3 vec;
-
- for (size_t i = 0; i < values.size(); i++)
- {
- double value;
-
- if (values[i]->getNumber(value))
- {
- vec[i] = value;
- }
- }
-
- Matrix4 scaling;
- cml::matrix_scale(scaling, vec);
- transform = scaling * transform;
- //std::cout << "SCALE\t\t" << vec << std::endl
- //<< transform << std::endl;
+ vec[i] = value;
}
}
+
+ Matrix4 scaling;
+ cml::matrix_scale(scaling, vec);
+ transform = scaling * transform;
}
- else if (instruction == "rotate")
+ }
+ }
+ else if (instruction == "rotate")
+ {
+ Serializable::Array values;
+
+ ++it;
+ if ((*it)->get(values))
+ {
+ if (values.size() == 2)
{
- std::vector<SerializablePtr> values;
+ std::string axis;
+ size_t index = 0;
+ Serializable::Float value = 0.0;
- it++;
- if ((*it)->get(values))
+ if (values[0]->get(axis))
{
- if (values.size() == 2)
- {
- std::string axis;
- size_t axisIndex = 0;
- double value = 0.0;
-
- if (values[0]->get(axis))
- {
- if (axis == "x")
- {
- axisIndex = 0;
- }
- else if (axis == "y")
- {
- axisIndex = 1;
- }
- else if (axis == "z")
- {
- axisIndex = 2;
- }
- values[1]->getNumber(value);
- }
-
- cml::matrix_rotate_about_local_axis(transform,
- axisIndex, Scalar(value * cml::constantsd::rad_per_deg()));
- //std::cout << "ROTATE\t" << axis << " " << value << std::endl
- //<< transform << std::endl;
- }
+ if (axis == "x") index = 0;
+ else if (axis == "y") index = 1;
+ else if (axis == "z") index = 2;
+
+ values[1]->getNumber(value);
}
- }
- else if (instruction == "texture")
- {
- it++;
- (*it)->get(texture);
- }
- else if (instruction == "tilemap")
- {
- //std::cout << "TILEMAP\t" << texture<< std::endl;
- //std::cout << transform << std::endl;
-
- it++;
- TilePanel* tilePanel = new TilePanel(transform, texture,
- *it);
- boost::shared_ptr<Scenery> sceneItem(tilePanel);
- objects.push_back(sceneItem);
- }
- else if (instruction == "billboard")
- {
- //std::cout << "BILLBOARD\t" << texture << std::endl;
- //std::cout << transform << std::endl;
-
- it++;
- Billboard* billboard = new Billboard(transform, texture,
- *it);
- boost::shared_ptr<Scenery> sceneItem(billboard);
- objects.push_back(sceneItem);
+
+ 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);
+ }
}
}
+}
- void loadFromFile()
- {
- std::string filePath = Scene::getPathToResource(getName());
-
- Deserializer deserializer(filePath, true);
-
- SerializablePtr root = deserializer.deserialize();
+static std::string getPath(const std::string& name)
+{
+ return Resource::getPath("scenes/" + name + ".json");
+}
- if (root)
- {
- std::map<std::string,SerializablePtr> rootObj;
+OctreeP loadScene(const std::string& name)
+{
+ std::string filePath = getPath(name);
- if (root->get(rootObj))
- {
- std::map<std::string,SerializablePtr>::iterator it;
+ Deserializer deserializer(filePath, true);
+ SerializableP root = deserializer.deserialize();
- if ((it = rootObj.find("playfield_bounds")) != rootObj.end())
- {
- loadBox(playfieldBounds, (*it).second);
- }
- if ((it = rootObj.find("maximum_bounds")) != rootObj.end())
- {
- loadBox(maximumBounds, (*it).second);
- }
- if ((it = rootObj.find("instructions")) != rootObj.end())
- {
- loadInstructions((*it).second);
- }
- }
- }
- }
+ Serializable::Map rootObj;
+ Serializable::Map::iterator it;
-
- void draw(Scalar alpha)
+ if (!root || !root->get(rootObj))
{
- SceneryVector::iterator it;
-
- for (it = objects.begin(); it != objects.end(); it++)
- {
- //std::cout << "draw object";
- (*it)->draw(alpha);
- }
+ logError("no root map in scene file");
+ return OctreeP();
}
-
Aabb playfieldBounds;
Aabb maximumBounds;
- typedef std::vector<boost::shared_ptr<Scenery> > SceneryVector;
- SceneryVector objects;
-};
-
-
-Scene::Scene(const std::string& name) :
- // pass through
- impl_(Scene::SceneImpl::retain(name), &Scene::SceneImpl::release) {}
-
+ 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();
+ }
-void Scene::draw(Scalar alpha)
-{
- // pass through
- impl_->draw(alpha);
-}
+ // create the tree to store the quads
+ OctreeP octree = Octree::alloc(maximumBounds);
+ 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::getPathToResource(const std::string& name)
-{
- return Resource::getPathToResource("scenes/" + name + ".json");
+ return octree;
}