/*] 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 "Character.hh" #include "Scene.hh" struct Scene::impl : public moof::manager { struct Quad : public moof::entity { enum Surface { NONE = 0, LEFT = 1, RIGHT = 2, TOP = 3 }; Quad(const moof::vector3* vertices[4], const std::string& texture, int 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.tile_coordinates(tileIndex, mTexCoords)) { moof::log_warning << "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; } aabb_.enclose_vertices(mVertices, 4); sphere_.point = aabb_.center(); sphere_.radius = (aabb_.min - sphere_.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(moof::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 is_visible(const moof::frustum& frustum) const { return sphere_.is_visible(frustum); } moof::vector3 mVertices[4]; moof::scalar mTexCoords[8]; moof::texture mTilemap; bool mBlending; bool mFog; Surface mSurface; }; moof::matrix4 mTransform; std::string mTexture; std::list< boost::shared_ptr > mObjects; std::list mLines; moof::aabb<3> mBounds; enum AXIS { X = 0, Y = 1, Z = 2 }; void init(const std::string& name) {} void importSceneBindings(moof::settings& settings, moof::script& script) { script.import_function("SetBounds", boost::bind(&impl::setBounds, this, _1)); script.import_function("ResetTransform", boost::bind(&impl::resetTransform, this, _1)); script.import_function("Translate", boost::bind(&impl::translate, this, _1)); script.import_function("Scale", boost::bind(&impl::scale, this, _1)); script.import_function("Rotate", boost::bind(&impl::rotate, this, _1)); script.import_function("SetTexture", boost::bind(&impl::setTexture, this, _1)); script.import_function("DrawTilemap", boost::bind(&impl::drawTilemap, this, _1)); script.import_function("DrawTile", boost::bind(&impl::drawTile, this, _1)); int detail = 3; settings.get("detail", detail); script.globals().set_field("detail", detail); script.globals().set_field("LOW", 1); script.globals().set_field("MEDIUM", 2); script.globals().set_field("HIGH", 3); script.globals().set_field("X", X); script.globals().set_field("Y", Y); script.globals().set_field("Z", Z); script.globals().set_field("LEFT", Quad::LEFT); script.globals().set_field("RIGHT", Quad::RIGHT); script.globals().set_field("TOP", Quad::TOP); } moof::script::status load(moof::settings& settings, moof::script& script) { std::string path(name()); if (!resource::find(path)) { script.push("the scene file could not be found"); return moof::script::file_error; } importSceneBindings(settings, script); return script.do_file(path); } static int loadBox(moof::script& script, moof::aabb3& aabb) { script[1].require_table(); script[2].require_table(); script[1].push_field(1).get(aabb.min[0]); script[1].push_field(2).get(aabb.min[1]); script[1].push_field(3).get(aabb.min[2]); script[2].push_field(1).get(aabb.max[0]); script[2].push_field(2).get(aabb.max[1]); script[2].push_field(3).get(aabb.max[2]); return 0; } int setBounds(moof::script& script) { int ret = loadBox(script, mBounds); return ret; } int resetTransform(moof::script& script) { mTransform.identity(); return 0; } int translate(moof::script& script) { moof::vector3 vec; script[1].require_number().get(vec[0]); script[2].require_number().get(vec[1]); script[3].require_number().get(vec[2]); moof::matrix4 translation; moof::matrix_translation(translation, vec); mTransform = translation * mTransform; return 0; } int scale(moof::script& script) { int size = script.stack_size(); if (size == 1) { moof::scalar value = 1.0; script[1].require_number().get(value); moof::matrix4 scaling; moof::matrix_uniform_scale(scaling, value); mTransform = scaling * mTransform; } else if (size == 3) { moof::vector3 vec; script[1].require_number().get(vec[0]); script[2].require_number().get(vec[1]); script[3].require_number().get(vec[2]); moof::matrix4 scaling; moof::matrix_scale(scaling, vec); mTransform = scaling * mTransform; } else { script.top().raise("wrong number of arguments"); } return 0; } int rotate(moof::script& script) { size_t index = 0; script[1].require_number().get(index); moof::scalar value; script[2].require_number().get(value); moof::matrix_rotate_about_world_axis(mTransform, index, moof::rad(value)); return 0; } int setTexture(moof::script& script) { script[1].require_string().get(mTexture); return 0; } int drawTilemap(moof::script& script) { moof::script::slot table = script[1].require_table(); int width = 1; table.get(width, "width"); int nTiles = table.length(); if (nTiles % width != 0) { table.raise("invalid number of tiles"); } if (width == 0) table.raise("width field must not be zero"); int height = nTiles / width; moof::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] = moof::demote(mTransform * moof::vector4(w, height, 0.0, 1.0)); } for (int h = 0; h < height; ++h) { vertices[h][0] = moof::demote(mTransform * moof::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; int index; table.get(index, i); vertices[h][wPlus1] = moof::demote(mTransform * moof::vector4(wPlus1, h, 0.0, 1.0)); if (index == moof::texture::no_tile) continue; const moof::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 moof::vector2 bl = moof::demote(vertices[0][0]); moof::vector2 tr = moof::demote(vertices[height][width]); mLines.push_back(moof::line<2>(bl, tr)); } return 0; } int drawTile(moof::script& script) { moof::script::slot param = script[1]; moof::script::slot top = script[-1]; int index = 0; int width = 1; bool blending = false; bool fog = false; if (param.is_table()) { param.get(index, 1); param.get(width, "u_scale"); param.get(blending, "blend"); param.get(fog, "fog"); } else if (param.is_number()) { param.get(index); } moof::vector3 vertices[2][width+1]; moof::scalar xf; moof::scalar increment = SCALAR(1.0) / moof::scalar(width); for (int h = 0; h <= 1; ++h) { xf = 0.0; for (int w = 0; w <= width; ++w, xf += increment) { vertices[h][w] = moof::demote(mTransform * moof::vector4(xf, moof::scalar(h), 0.0, 1.0)); } } for (int w = 0; w < width; ++w) { int wPlus1 = w + 1; const moof::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 impl_(Scene::impl::instance(name)) {} moof::script::status Scene::load(moof::settings& settings, moof::script& script) { // pass through return impl_->load(settings, script); } void Scene::draw(moof::scalar alpha) const { std::list< boost::shared_ptr >& objects = impl_->mObjects; std::list< boost::shared_ptr >::const_iterator it; for (it = objects.begin(); it != objects.end(); ++it) { (*it)->draw(alpha); } impl_->mBounds.draw(); } void Scene::draw_if_visible(moof::scalar alpha, const moof::frustum& frustum) const { std::list< boost::shared_ptr >& objects = impl_->mObjects; std::list< boost::shared_ptr >::const_iterator it; for (it = objects.begin(); it != objects.end(); ++it) { (*it)->draw_if_visible(alpha, frustum); } std::list< moof::line<2> >& lines = impl_->mLines; std::list< moof::line<2> >::const_iterator lit; for (lit = lines.begin(); lit != lines.end(); ++lit) { (*lit).draw(alpha); } impl_->mBounds.draw(); } bool Scene::castRay(const moof::ray<2>& ray, std::list::contact>& hits) const { std::list< moof::line<2> >& lines = impl_->mLines; std::list< moof::line<2> >::const_iterator it; for (it = lines.begin(); it != lines.end(); ++it) { moof::ray<2>::contact hit; moof::scalar d = (*it).intersect_ray(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 = impl_->mObjects; std::list< boost::shared_ptr >::const_iterator it; int collisions = 0; moof::sphere<3> sphere = character.sphere(); for (it = objects.begin(); it != objects.end(); ++it) { impl::Quad::Surface type = (*it)->getSurface(); if (type == impl::Quad::NONE) continue; if (moof::checkCollision(sphere, (*it)->sphere())) { ++collisions; moof::vector2 impulse(0.0, 0.0); moof::vector2 p = character.state().momentum; moof::state2 state = character.state(1.0); sphere = character.sphere(); moof::scalar alpha = 1.0; while (moof::checkCollision(sphere, (*it)->sphere())) { alpha -= 0.05; state = character.state(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) { moof::log_info << "collisions: " << collisions << std::endl; } return false; }