*******************************************************************************/
-#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"
namespace Mf {
-class Scene::SceneImpl : public Mippleton<SceneImpl>
+class Scene::Impl : public Mippleton<Impl>
{
- class Scenery : public Drawable, public Cullable
+ class Quad : public Entity
{
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
- {
- public:
- TilePanel(const Matrix4& transform, const std::string& textureName,
- SerializablePtr root) :
- Scenery(transform, textureName),
- width(1),
- height(1)
+ Quad(const Vector3 vertices[4], const std::string& texture,
+ Tilemap::Index tileIndex) :
+ tilemap_(texture),
+ detail_(0),
+ blending_(false),
+ fog_(false)
{
- std::map<std::string,SerializablePtr> rootObj;
-
- if (root->get(rootObj))
+ for (int i = 0, num = 0; i < 4; ++i)
{
- std::map<std::string,SerializablePtr>::iterator it;
-
- if ((it = rootObj.find("width")) != rootObj.end())
- {
- (*it).second->get(width);
- }
- if ((it = rootObj.find("tiles")) != rootObj.end())
+ for (int j = 0; j < 3; ++j, ++num)
{
- std::vector<SerializablePtr> theTiles;
-
- if ((*it).second->get(theTiles))
- {
- std::vector<SerializablePtr>::iterator jt;
-
- height = theTiles.size() / width;
- int w, h;
-
- indices.resize(height);
-
- for (h = height - 1, jt = theTiles.begin();
- jt != theTiles.end(); h--)
- {
- std::vector<Tilemap::Index> row;
-
- for (w = 0; w < width && jt != theTiles.end();
- w++, jt++)
- {
- long index;
-
- if ((*jt)->get(index))
- {
- row.push_back(Tilemap::Index(index));
- }
- }
-
- indices[h] = row;
- }
- }
+ vertices_[num] = vertices[i][j];
}
}
- }
-
- void draw(Scalar alpha)
- {
- glPushMatrix();
- //std::cout << "transforming..." << std::endl;
- //std::cout << transformation << std::endl;
- glMultMatrix(transformation.data());
-
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- image.bind();
- long x, y;
- Scalar xf, yf;
-
- for (y = 0, yf = 0.0; y < height; y++, yf += 1.0)
+ if (!tilemap_.getTileCoords(tileIndex, texCoords_))
{
- for (x = 0, xf = 0.0; x < width; x++, xf += 1.0)
- {
- Scalar texCoords[8];
+ logWarning("no index %d in texture %s", tileIndex,
+ texture.c_str());
- Tilemap::Index index = indices[y][x];
-
- if (image.getTileCoords(index, texCoords))
- {
- glBegin(GL_TRIANGLE_FAN);
- glTexCoord2f(texCoords[0], texCoords[1]);
- glVertex2f(xf, yf);
- glTexCoord2f(texCoords[2], texCoords[3]);
- glVertex2f(xf+1.0, yf);
- glTexCoord2f(texCoords[4], texCoords[5]);
- glVertex2f(xf+1.0, yf+1.0);
- glTexCoord2f(texCoords[6], texCoords[7]);
- glVertex2f(xf, yf+1.0);
- glEnd();
- }
- }
+ texCoords_[0] = texCoords_[1] =
+ texCoords_[3] = texCoords_[6] = 0.0;
+ texCoords_[2] = texCoords_[4] =
+ texCoords_[5] = texCoords_[7] = 1.0;
}
- glPopMatrix();
+ aabb_.encloseVertices(vertices, 4);
+ sphere_.point = aabb_.getCenter();
+ sphere_.radius = (aabb_.min - sphere_.point).length();
}
- bool isVisible(const Camera& cam)
+ void setDetail(long detail)
{
- return true;
+ detail_ = detail;
}
- private:
- long width, height;
- std::vector<std::vector<Tilemap::Index> > indices;
- };
+ void setBlending(bool blending)
+ {
+ blending_ = blending;
+ }
- class Billboard : public Scenery
- {
- public:
- Billboard(const Matrix4& transform, const std::string& textureName,
- SerializablePtr root) :
- Scenery(transform, textureName),
- index(0),
- uScale(1)
+ void setFog(bool fog)
{
- std::map<std::string,SerializablePtr> rootObj;
+ fog_ = fog;
+ }
- if (root->get(rootObj))
+ void draw(Scalar alpha = 0.0) const
+ {
+ if (blending_)
{
- 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);
- }
- }
- if ((it = rootObj.find("u_scale")) != rootObj.end())
- {
- (*it).second->get(uScale);
- }
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
- image.getTileCoords(index, texCoords);
- }
-
- void draw(Scalar alpha)
- {
- glPushMatrix();
- glMultMatrix(transformation.data());
+ if (fog_)
+ {
+ glEnable(GL_FOG);
+ glFogi(GL_FOG_MODE, GL_LINEAR);
+ }
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- image.bind();
+ tilemap_.bind();
- float increment = 1.0f / float(uScale);
- int x;
- float xf;
+ glVertexPointer(3, GL_SCALAR, 0, vertices_);
+ glTexCoordPointer(2, GL_SCALAR, 0, texCoords_);
- for (x = 0, xf = 0.0f; x < uScale; x++, xf += increment)
- {
- glBegin(GL_TRIANGLE_FAN);
- glTexCoord2f(texCoords[0], texCoords[1]);
- glVertex2f(xf, 0.0f);
- glTexCoord2f(texCoords[2], texCoords[3]);
- glVertex2f(xf+increment, 0.0f);
- glTexCoord2f(texCoords[4], texCoords[5]);
- glVertex2f(xf+increment, 1.0f);
- glTexCoord2f(texCoords[6], texCoords[7]);
- glVertex2f(xf, 1.0f);
- glEnd();
- }
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- glPopMatrix();
+ glDisable(GL_BLEND);
+ glDisable(GL_FOG);
}
- bool isVisible(const Camera& cam)
+ bool isVisible(const Camera& cam) const
{
- return false;
+ return sphere_.isVisible(cam);
}
private:
- Tilemap::Index index;
- Scalar texCoords[8];
- long uScale;
+ Scalar vertices_[12];
+ Scalar texCoords_[8];
+
+ Tilemap tilemap_;
+
+ long detail_;
+ bool blending_;
+ bool fog_;
};
- static bool loadBox(Aabb& theBox, SerializablePtr obj)
+ static void loadBox(Aabb& theBox, SerializableP obj)
{
- std::vector<SerializablePtr> numbers;
+ Serializable::Array numbers;
- if (obj->get(numbers))
+ if (obj->get(numbers) && numbers.size() == 6)
{
- if (numbers.size() == 6)
- {
- double num;
-
- if (numbers[0]->getNumber(num))
- {
-
- }
- }
+ 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);
}
-
- return false;
}
public:
- SceneImpl(const std::string& name) :
- Mippleton<SceneImpl>(name)
+ Impl(const std::string& name) :
+ Mippleton<Impl>(name)
{
loadFromFile();
}
- void loadInstructions(SerializablePtr root)
+ void loadInstructions(SerializableP root)
{
- std::vector<SerializablePtr> rootObj;
+ Serializable::Array rootObj;
+ Serializable::Array::iterator it;
- if (root->get(rootObj))
+ if (!root->get(rootObj))
{
- std::vector<SerializablePtr>::iterator it;
+ logError("scene instructions must be an array");
+ return;
+ }
- Matrix4 transform;
- std::string texture;
+ Matrix4 transform;
+ std::string texture;
- for (it = rootObj.begin(); it != rootObj.end(); it++)
- {
- std::string instruction;
+ for (it = rootObj.begin(); it != rootObj.end(); ++it)
+ {
+ std::string instruction;
- if ((*it)->get(instruction))
+ if ((*it)->get(instruction))
+ {
+ if (instruction == "reset_transform")
{
- if (instruction == "reset_transform")
- {
- transform.identity();
- //std::cout << "===================RESET=====================" << std::endl;
- }
- else if (instruction == "translate")
+ transform.identity();
+ }
+ else if (instruction == "translate")
+ {
+ Serializable::Array values;
+
+ ++it;
+ if ((*it)->get(values))
{
- std::vector<SerializablePtr> values;
+ Vector3 vec;
- it++;
- if ((*it)->get(values))
+ for (size_t i = 0; i < values.size(); ++i)
{
- Vector3 vec;
+ Serializable::Float value;
- for (size_t i = 0; i < values.size(); i++)
+ if (values[i]->getNumber(value))
{
- double value;
-
- if (values[i]->getNumber(value))
- {
- vec[i] = value;
- }
+ vec[i] = value;
}
-
- Matrix4 translation;
- cml::matrix_translation(translation, vec);
- transform = translation * transform;
- //std::cout << "TRANSLATE\t" << vec << std::endl
- //<< transform << std::endl;
}
+
+ Matrix4 translation;
+ cml::matrix_translation(translation, vec);
+ transform = translation * transform;
}
- else if (instruction == "scale")
- {
- std::vector<SerializablePtr> values;
+ }
+ else if (instruction == "scale")
+ {
+ Serializable::Array values;
- it++;
- if ((*it)->get(values))
+ ++it;
+ if ((*it)->get(values))
+ {
+ if (values.size() == 1)
{
- if (values.size() == 1)
- {
- double value = 1.0;
+ Serializable::Float value = 1.0;
- values[0]->getNumber(value);
+ values[0]->getNumber(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)
+ Matrix4 scaling;
+ cml::matrix_uniform_scale(scaling,
+ Scalar(value));
+ transform = scaling * transform;
+ }
+ else if (values.size() == 3)
+ {
+ Vector3 vec;
+
+ for (size_t i = 0; i < values.size(); ++i)
{
- Vector3 vec;
+ Serializable::Float value;
- for (size_t i = 0; i < values.size(); i++)
+ if (values[i]->getNumber(value))
{
- double value;
-
- if (values[i]->getNumber(value))
- {
- vec[i] = 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;
}
+
+ Matrix4 scaling;
+ cml::matrix_scale(scaling, vec);
+ transform = scaling * transform;
}
}
- else if (instruction == "rotate")
- {
- std::vector<SerializablePtr> values;
+ }
+ else if (instruction == "rotate")
+ {
+ Serializable::Array values;
- it++;
- if ((*it)->get(values))
+ ++it;
+ if ((*it)->get(values))
+ {
+ if (values.size() == 2)
{
- if (values.size() == 2)
- {
- std::string axis;
- size_t axisIndex = 0;
- double value = 0.0;
+ std::string axis;
+ size_t index = 0;
+ Serializable::Float 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);
- }
+ if (values[0]->get(axis))
+ {
+ if (axis == "x") index = 0;
+ else if (axis == "y") index = 1;
+ else if (axis == "z") index = 2;
- 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;
+ values[1]->getNumber(value);
}
+
+ cml::matrix_rotate_about_world_axis(transform,
+ index, cml::rad(Scalar(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);
- }
+ }
+ 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);
}
}
}
}
- void loadFromFile()
+ void loadTilemap(SerializableP root, const Matrix4& transform,
+ const std::string& texture)
{
- std::string filePath = Scene::getPathToResource(getName());
+ Serializable::Map rootObj;
+ Serializable::Map::iterator it;
+
+ if (!root->get(rootObj))
+ {
+ logError("invalid tilemap instruction");
+ return;
+ }
- Deserializer deserializer(filePath, true);
+ long width = 1;
+ long height = 1;
+ std::vector< std::vector<Tilemap::Index> > indices;
+
+ if ((it = rootObj.find("width")) != rootObj.end())
+ {
+ (*it).second->get(width);
+ }
+ else
+ {
+ logError("missing required field width for tilemap instruction");
+ return;
+ }
- SerializablePtr root = deserializer.deserialize();
+ Serializable::Array tiles;
- if (root)
+ if ((it = rootObj.find("tiles")) != rootObj.end() &&
+ (*it).second->get(tiles) &&
+ tiles.size() % width == 0)
{
- std::map<std::string,SerializablePtr> rootObj;
+ Serializable::Array::iterator jt;
+ int w, h;
+
+ height = tiles.size() / width;
+ indices.resize(height);
+
+ // 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
- if (root->get(rootObj))
+ for (h = height - 1, jt = tiles.begin(); jt != tiles.end(); --h)
{
- std::map<std::string,SerializablePtr>::iterator it;
+ std::vector<Tilemap::Index> row;
- if ((it = rootObj.find("playfield_bounds")) != rootObj.end())
+ for (w = 0; w < width && jt != tiles.end(); ++w, ++jt)
{
- loadBox(playfieldBounds, (*it).second);
- }
- if ((it = rootObj.find("maximum_bounds")) != rootObj.end())
- {
- loadBox(maximumBounds, (*it).second);
+ Serializable::Integer index;
+
+ if ((*jt)->get(index))
+ {
+ row.push_back(Tilemap::Index(index));
+ }
}
- if ((it = rootObj.find("instructions")) != rootObj.end())
+
+ indices[h] = row;
+ }
+ }
+ else
+ {
+ logError("invalid tiles in tilemap instruction");
+ return;
+ }
+
+ Vector4 vertices[height+1][width+1];
+
+ Matrix4 transposedTransform = transform;
+ transposedTransform.transpose();
+
+ 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 (int h = 0; h < height; ++h)
+ {
+ for (int w = 0; w < width; ++w)
+ {
+ if (indices[h][w] == Tilemap::NO_TILE) continue;
+
+ Vector3 quadVertices[4];
+
+ 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);
+
+ octree->insert(quadPtr);
+ }
+ }
+ }
+
+ void loadBillboard(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 ((it = rootObj.find("tile")) != rootObj.end())
+ {
+ Serializable::Integer value;
+ if ((*it).second->get(value))
{
- loadInstructions((*it).second);
+ index = Tilemap::Index(value);
}
}
+
+ if ((it = rootObj.find("u_scale")) != rootObj.end())
+ {
+ (*it).second->get(width);
+ }
+
+ 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];
+
+ Matrix4 transposedTransform = transform;
+ transposedTransform.transpose();
+
+ Scalar xf;
+ Scalar increment = 1.0 / Scalar(width);
+
+ 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;
+ }
+ }
+
+ 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);
+
+ octree->insert(quadPtr);
}
}
- void draw(Scalar alpha)
+ void loadFromFile()
{
- SceneryVector::iterator it;
+ std::string filePath = Scene::getPath(getName());
+
+ Deserializer deserializer(filePath, true);
+ SerializableP root = deserializer.deserialize();
+
+ Serializable::Map rootObj;
+ Serializable::Map::iterator it;
+
+ if (!root || !root->get(rootObj))
+ {
+ logError("no root map in scene file");
+ return;
+ }
- for (it = objects.begin(); it != objects.end(); it++)
+ if ((it = rootObj.find("playfield_bounds")) != rootObj.end())
{
- //std::cout << "draw object";
- (*it)->draw(alpha);
+ loadBox(playfieldBounds, (*it).second);
}
+ if ((it = rootObj.find("maximum_bounds")) != rootObj.end())
+ {
+ loadBox(maximumBounds, (*it).second);
+ }
+ else
+ {
+ logError("missing required maximum bounds");
+ return;
+ }
+
+ // create the tree to store the quads
+ octree = Octree::alloc(maximumBounds);
+
+ if ((it = rootObj.find("instructions")) != rootObj.end())
+ {
+ loadInstructions((*it).second);
+ }
+
+
+
+ octree->sort();
+ }
+
+
+ void draw(Scalar alpha, const Camera& cam) const
+ {
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ octree->drawIfVisible(alpha, cam);
+
+ //glDisableClientState(GL_VERTEX_ARRAY);
+ //glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ //Texture::resetBind();
+ //glColor3f(0.0f, 1.0f, 0.0f);
+ //playfieldBounds.draw();
+ //glColor3f(0.0f, 0.0f, 1.0f);
+ //maximumBounds.draw();
+
+ //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
Aabb playfieldBounds;
Aabb maximumBounds;
- typedef std::vector<boost::shared_ptr<Scenery> > SceneryVector;
- SceneryVector objects;
+ OctreeP octree;
};
Scene::Scene(const std::string& name) :
// pass through
- impl_(Scene::SceneImpl::retain(name), &Scene::SceneImpl::release) {}
+ impl_(Scene::Impl::getInstance(name)) {}
-void Scene::draw(Scalar alpha)
+void Scene::draw(Scalar alpha, const Camera& cam) const
{
// pass through
- impl_->draw(alpha);
+ impl_->draw(alpha, cam);
}
+void Scene::refresh()
+{
+ //impl_->objects.clear();
+ impl_->loadFromFile();
+}
+
+
+OctreeP Scene::getOctree() const
+{
+ // pass through
+ return impl_->octree;
+}
/**
* Specialized search location for scene files. They can be found in the
* "scenes" subdirectory of any of the searched directories.
*/
-std::string Scene::getPathToResource(const std::string& name)
+std::string Scene::getPath(const std::string& name)
{
- return Resource::getPathToResource("scenes/" + name + ".json");
+ return Resource::getPath("scenes/" + name + ".json");
}