/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * **************************************************************************/ #include #include #include #include #include #include #include #include //#include #include #include #include #include "Character.hh" #include "Scene.hh" struct Scene::Impl : public Mf::Manager { struct Quad : public Mf::Entity { enum Surface { NONE = 0, LEFT = 1, RIGHT = 2, TOP = 3 }; Quad(const Mf::Vector3* vertices[4], const std::string& texture, Mf::Texture::TileIndex tileIndex) : mTilemap(texture), mBlending(false), mFog(false), mSurface(NONE) { for (int i = 0; i < 4; ++i) { mVertices[i] = *vertices[i]; //for (int j = 0; j < 3; ++j, ++num) //{ //mVertices[num] = (*vertices[i])[j]; //} } if (!mTilemap.getTileCoords(tileIndex, mTexCoords)) { Mf::logWarning << "no index " << tileIndex << " in texture " << texture << std::endl; mTexCoords[0] = mTexCoords[1] = mTexCoords[3] = mTexCoords[6] = 0.0; mTexCoords[2] = mTexCoords[4] = mTexCoords[5] = mTexCoords[7] = 1.0; } mAabb.encloseVertices(mVertices, 4); mSphere.point = mAabb.getCenter(); mSphere.radius = (mAabb.min - mSphere.point).length(); } void setBlending(bool blending) { mBlending = blending; } void setFog(bool fog) { mFog = fog; } void setSurface(Surface type) { mSurface = type; } Surface getSurface() const { return mSurface; } void draw(Mf::Scalar alpha = 0.0) const { if (mBlending) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } if (mFog) { glEnable(GL_FOG); glFogi(GL_FOG_MODE, GL_LINEAR); } //glColor4f(1.0f, 1.0f, 1.0f, 1.0f); mTilemap.bind(); glVertexPointer(3, GL_SCALAR, 0, mVertices[0].data()); glTexCoordPointer(2, GL_SCALAR, 0, mTexCoords); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisable(GL_BLEND); glDisable(GL_FOG); } bool isVisible(const Mf::Frustum& frustum) const { return mSphere.isVisible(frustum); } Mf::Vector3 mVertices[4]; Mf::Scalar mTexCoords[8]; Mf::Texture mTilemap; bool mBlending; bool mFog; Surface mSurface; }; Mf::Matrix4 mTransform; std::string mTexture; //Mf::Octree::Ptr mOctree; std::list< boost::shared_ptr > mObjects; std::list mLines; Mf::Aabb<3> mBounds; enum AXIS { X = 0, Y = 1, Z = 2 }; void init(const std::string& name) {} void importSceneBindings(Mf::Settings& settings, Mf::Script& script) { script.importFunction("SetBounds", boost::bind(&Impl::setBounds, this, _1)); script.importFunction("ResetTransform", boost::bind(&Impl::resetTransform, this, _1)); script.importFunction("Translate", boost::bind(&Impl::translate, this, _1)); script.importFunction("Scale", boost::bind(&Impl::scale, this, _1)); script.importFunction("Rotate", boost::bind(&Impl::rotate, this, _1)); script.importFunction("SetTexture", boost::bind(&Impl::setTexture, this, _1)); script.importFunction("DrawTilemap", boost::bind(&Impl::drawTilemap, this, _1)); script.importFunction("DrawTile", boost::bind(&Impl::drawTile, this, _1)); int detail = 3; settings.get("detail", detail); script.push(detail); script.set("detail"); script.push(1); script.set("LOW"); script.push(2); script.set("MEDIUM"); script.push(3); script.set("HIGH"); script.push(X); script.set("X"); script.push(Y); script.set("Y"); script.push(Z); script.set("Z"); script.push(Quad::LEFT); script.set("LEFT"); script.push(Quad::RIGHT); script.set("RIGHT"); script.push(Quad::TOP); script.set("TOP"); } Mf::Script::Result load(Mf::Settings& settings, Mf::Script& script) { std::string path(getName()); if (!Scene::getPath(path)) { script.push("the scene file could not be found"); return Mf::Script::FILE_ERROR; } importSceneBindings(settings, script); return script.doFile(path); } static int loadBox(Mf::Script& script, Mf::Aabb<3>& aabb) { script[1].requireTable(); script[2].requireTable(); script.setSize(2); for (int i = 1; i <= 2; ++i) { for (int j = 1; j <= 3; ++j) { script[i].pushField(j); } } script[3].get(aabb.min[0]); script[4].get(aabb.min[1]); script[5].get(aabb.min[2]); script[6].get(aabb.max[0]); script[7].get(aabb.max[1]); script[8].get(aabb.max[2]); return 0; } int setBounds(Mf::Script& script) { int ret = loadBox(script, mBounds); //mOctree = Mf::Octree::alloc(mBounds); return ret; } int resetTransform(Mf::Script& script) { mTransform.identity(); return 0; } int translate(Mf::Script& script) { Mf::Vector3 vec; script[1].requireNumber().get(vec[0]); script[2].requireNumber().get(vec[1]); script[3].requireNumber().get(vec[2]); Mf::Matrix4 translation; cml::matrix_translation(translation, vec); mTransform = translation * mTransform; return 0; } int scale(Mf::Script& script) { int size = script.getSize(); if (size == 1) { Mf::Scalar value = 1.0; script[1].requireNumber().get(value); Mf::Matrix4 scaling; cml::matrix_uniform_scale(scaling, value); mTransform = scaling * mTransform; } else if (size == 3) { Mf::Vector3 vec; script[1].requireNumber().get(vec[0]); script[2].requireNumber().get(vec[1]); script[3].requireNumber().get(vec[2]); Mf::Matrix4 scaling; cml::matrix_scale(scaling, vec); mTransform = scaling * mTransform; } else { script.getTop().throwError("wrong number of arguments"); } return 0; } int rotate(Mf::Script& script) { size_t index = 0; script[1].requireNumber().get(index); Mf::Scalar value; script[2].requireNumber().get(value); cml::matrix_rotate_about_world_axis(mTransform, index, cml::rad(value)); return 0; } int setTexture(Mf::Script& script) { script[1].requireString().get(mTexture); return 0; } int drawTilemap(Mf::Script& script) { Mf::Script::Slot table = script[1].requireTable(); int width = 1; int height = 1; int nTiles = 0; table.get(width, "width"); nTiles = table.getLength(); if (nTiles % width != 0) { table.throwError("invalid number of tiles"); } if (width == 0) table.throwError("width field must not be zero"); height = nTiles / width; Mf::Vector3 vertices[height+1][width+1]; // 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 // do first row and first column of vertices for (int w = 0; w <= width; ++w) { vertices[height][w] = Mf::demote(mTransform * Mf::Vector4(w, height, 0.0, 1.0)); } for (int h = 0; h < height; ++h) { vertices[h][0] = Mf::demote(mTransform * Mf::Vector4(0.0, h, 0.0, 1.0)); } size_t i = 1; for (int h = height - 1; h >= 0; --h) { for (int w = 0; w < width; ++w, ++i) { int wPlus1 = w + 1; int hPlus1 = h + 1; Mf::Texture::TileIndex index; table.get(index, i); vertices[h][wPlus1] = Mf::demote(mTransform * Mf::Vector4(wPlus1, h, 0.0, 1.0)); if (index == Mf::Texture::NO_TILE) continue; const Mf::Vector3* corners[4] = { &vertices[h][w], &vertices[h][wPlus1], &vertices[hPlus1][wPlus1], &vertices[hPlus1][w] }; Quad* quad = new Quad(corners, mTexture, index); //quad->setSurface(surface); boost::shared_ptr quadPtr(quad); mObjects.push_back(quadPtr); } } Quad::Surface surface = Quad::NONE; table.get(surface, "surface"); if (surface != Quad::NONE) { // need a 2d line for collisions // assuming the camera always looks directly to -z when the // scene is built, simply demoting the vector again should // project the points to the xy-plane Mf::Vector2 bl = Mf::demote(vertices[0][0]); Mf::Vector2 tr = Mf::demote(vertices[height][width]); mLines.push_back(Mf::Line<2>(bl, tr)); } return 0; } int drawTile(Mf::Script& script) { Mf::Script::Slot param = script[1]; Mf::Script::Slot top = script[-1]; Mf::Texture::TileIndex index = 0; int width = 1; bool blending = false; bool fog = false; if (param.isTable()) { param.get(index, 1); param.get(width, "u_scale"); param.get(blending, "blend"); param.get(fog, "fog"); } else if (param.isNumber()) { param.get(index); } Mf::Vector3 vertices[2][width+1]; Mf::Scalar xf; Mf::Scalar increment = SCALAR(1.0) / Mf::Scalar(width); for (int h = 0; h <= 1; ++h) { xf = 0.0; for (int w = 0; w <= width; ++w, xf += increment) { vertices[h][w] = Mf::demote(mTransform * Mf::Vector4(xf, Mf::Scalar(h), 0.0, 1.0)); } } for (int w = 0; w < width; ++w) { int wPlus1 = w + 1; const Mf::Vector3* corners[4] = { &vertices[0][w], &vertices[0][wPlus1], &vertices[1][wPlus1], &vertices[1][w] }; Quad* quad = new Quad(corners, mTexture, index); quad->setBlending(blending); quad->setFog(fog); boost::shared_ptr quadPtr(quad); mObjects.push_back(quadPtr); } return 0; } }; Scene::Scene(const std::string& name) : // pass through mImpl(Scene::Impl::getInstance(name)) {} Mf::Script::Result Scene::load(Mf::Settings& settings, Mf::Script& script) { // pass through return mImpl->load(settings, script); } void Scene::draw(Mf::Scalar alpha) const { std::list< boost::shared_ptr >& objects = mImpl->mObjects; std::list< boost::shared_ptr >::const_iterator it; for (it = objects.begin(); it != objects.end(); ++it) { (*it)->draw(alpha); } mImpl->mBounds.draw(); } void Scene::drawIfVisible(Mf::Scalar alpha, const Mf::Frustum& frustum) const { std::list< boost::shared_ptr >& objects = mImpl->mObjects; std::list< boost::shared_ptr >::const_iterator it; for (it = objects.begin(); it != objects.end(); ++it) { (*it)->drawIfVisible(alpha, frustum); } std::list< Mf::Line<2> >& lines = mImpl->mLines; std::list< Mf::Line<2> >::const_iterator lit; for (lit = lines.begin(); lit != lines.end(); ++lit) { (*lit).draw(alpha); } mImpl->mBounds.draw(); } bool Scene::castRay(const Mf::Ray<2>& ray, std::list::Contact>& hits) const { std::list< Mf::Line<2> >& lines = mImpl->mLines; std::list< Mf::Line<2> >::const_iterator it; for (it = lines.begin(); it != lines.end(); ++it) { Mf::Ray<2>::Contact hit; Mf::Scalar d = (*it).intersectRay(ray, hit); if (d > 0.0) { hits.push_back(hit); //return true; } } hits.sort(); return !hits.empty(); //return false; } bool Scene::checkForCollision(Character& character) { return false; //std::list< boost::shared_ptr > objects; //std::list::InsertableP> objects; //mImpl->mOctree->getNearbyObjects(objects, character); std::list< boost::shared_ptr >& objects = mImpl->mObjects; std::list< boost::shared_ptr >::const_iterator it; int collisions = 0; Mf::Sphere<3> sphere = character.getSphere(); for (it = objects.begin(); it != objects.end(); ++it) { Impl::Quad::Surface type = (*it)->getSurface(); if (type == Impl::Quad::NONE) continue; if (Mf::checkCollision(sphere, (*it)->getSphere())) { ++collisions; Mf::Vector2 impulse(0.0, 0.0); Mf::Vector2 p = character.getState().momentum; Mf::State2 state = character.getState(1.0); sphere = character.getSphere(); Mf::Scalar alpha = 1.0; while (Mf::checkCollision(sphere, (*it)->getSphere())) { alpha -= 0.05; state = character.getState(alpha); } character.setPosition(state.position); //switch (type) //{ //case Impl::Quad::TOP: //if (p[1] < 0.0) impulse[1] = -p[1]; //break; //case Impl::Quad::LEFT: //if (p[0] > 0.0) impulse[0] = 1.5*-p[0]; //break; //case Impl::Quad::RIGHT: //if (p[0] < 0.0) impulse[0] = 1.5*-p[0]; //break; //} //character.addImpulse(impulse); } } if (collisions > 0) { Mf::logInfo << "collisions: " << collisions << std::endl; } return false; } bool Scene::getPath(std::string& name) { return Mf::Resource::getPath(name, "scenes/", "lua"); }