#include "Base.h" #include "AudioListener.h" #include "Scene.h" #include "SceneLoader.h" #include "MeshSkin.h" #include "Joint.h" namespace gameplay { Scene::Scene() : _activeCamera(NULL), _firstNode(NULL), _lastNode(NULL), _nodeCount(0), _bindAudioListenerToCamera(true), _debugBatch(NULL) { } Scene::~Scene() { // Unbind our active camera from the audio listener if (_activeCamera) { AudioListener* audioListener = AudioListener::getInstance(); if (audioListener && (audioListener->getCamera() == _activeCamera)) { audioListener->setCamera(NULL); } SAFE_RELEASE(_activeCamera); } // Remove all nodes from the scene removeAllNodes(); SAFE_DELETE(_debugBatch); } Scene* Scene::create() { return new Scene(); } Scene* Scene::load(const char* filePath) { return SceneLoader::load(filePath); } const char* Scene::getId() const { return _id.c_str(); } void Scene::setId(const char* id) { if (id) { _id = id; } } Node* Scene::findNode(const char* id, bool recursive, bool exactMatch) const { GP_ASSERT(id); // Search immediate children first. for (Node* child = getFirstNode(); child != NULL; child = child->getNextSibling()) { // Does this child's ID match? if ((exactMatch && child->_id == id) || (!exactMatch && child->_id.find(id) == 0)) { return child; } } // Recurse. if (recursive) { for (Node* child = getFirstNode(); child != NULL; child = child->getNextSibling()) { Node* match = child->findNode(id, true, exactMatch); if (match) { return match; } } } return NULL; } unsigned int Scene::findNodes(const char* id, std::vector& nodes, bool recursive, bool exactMatch) const { GP_ASSERT(id); unsigned int count = 0; // Search immediate children first. for (Node* child = getFirstNode(); child != NULL; child = child->getNextSibling()) { // Does this child's ID match? if ((exactMatch && child->_id == id) || (!exactMatch && child->_id.find(id) == 0)) { nodes.push_back(child); ++count; } } // Recurse. if (recursive) { for (Node* child = getFirstNode(); child != NULL; child = child->getNextSibling()) { count += child->findNodes(id, nodes, true, exactMatch); } } return count; } Node* Scene::addNode(const char* id) { Node* node = Node::create(id); GP_ASSERT(node); addNode(node); // Call release to decrement the ref count to 1 before returning. node->release(); return node; } void Scene::addNode(Node* node) { GP_ASSERT(node); if (node->_scene == this) { // The node is already a member of this scene. return; } node->addRef(); // If the node is part of another scene, remove it. if (node->_scene && node->_scene != this) { node->_scene->removeNode(node); } // If the node is part of another node hierarchy, remove it. if (node->getParent()) { node->getParent()->removeChild(node); } // Link the new node into our list. if (_lastNode) { _lastNode->_nextSibling = node; node->_prevSibling = _lastNode; _lastNode = node; } else { _firstNode = _lastNode = node; } node->_scene = this; ++_nodeCount; // If we don't have an active camera set, then check for one and set it. if (_activeCamera == NULL) { Camera* camera = node->getCamera(); if (camera) { setActiveCamera(camera); } } } void Scene::removeNode(Node* node) { GP_ASSERT(node); if (node->_scene != this) return; if (node == _firstNode) { _firstNode = node->_nextSibling; } if (node == _lastNode) { _lastNode = node->_prevSibling; } node->remove(); node->_scene = NULL; SAFE_RELEASE(node); --_nodeCount; } void Scene::removeAllNodes() { while (_lastNode) { removeNode(_lastNode); } } unsigned int Scene::getNodeCount() const { return _nodeCount; } Node* Scene::getFirstNode() const { return _firstNode; } Camera* Scene::getActiveCamera() const { return _activeCamera; } void Scene::setActiveCamera(Camera* camera) { // Make sure we don't release the camera if the same camera is set twice. if (_activeCamera != camera) { AudioListener* audioListener = AudioListener::getInstance(); if (_activeCamera) { // Unbind the active camera from the audio listener if (audioListener && (audioListener->getCamera() == _activeCamera)) { audioListener->setCamera(NULL); } SAFE_RELEASE(_activeCamera); } _activeCamera = camera; if (_activeCamera) { _activeCamera->addRef(); if (audioListener && _bindAudioListenerToCamera) { audioListener->setCamera(_activeCamera); } } } } void Scene::bindAudioListenerToCamera(bool bind) { if (_bindAudioListenerToCamera != bind) { _bindAudioListenerToCamera = bind; if (AudioListener::getInstance()) { AudioListener::getInstance()->setCamera(bind ? _activeCamera : NULL); } } } const Vector3& Scene::getAmbientColor() const { return _ambientColor; } void Scene::setAmbientColor(float red, float green, float blue) { _ambientColor.set(red, green, blue); } static Material* createDebugMaterial() { // Vertex shader for drawing colored lines. const char* vs_str = { "uniform mat4 u_viewProjectionMatrix;\n" "attribute vec4 a_position;\n" "attribute vec4 a_color;\n" "varying vec4 v_color;\n" "void main(void) {\n" " v_color = a_color;\n" " gl_Position = u_viewProjectionMatrix * a_position;\n" "}" }; // Fragment shader for drawing colored lines. const char* fs_str = { #ifdef OPENGL_ES "precision highp float;\n" #endif "varying vec4 v_color;\n" "void main(void) {\n" " gl_FragColor = v_color;\n" "}" }; Effect* effect = Effect::createFromSource(vs_str, fs_str); Material* material = Material::create(effect); GP_ASSERT(material && material->getStateBlock()); material->getStateBlock()->setDepthTest(true); SAFE_RELEASE(effect); return material; } /** * DebugVertex structure. * @script{ignore} */ struct DebugVertex { /** * The x coordinate of the vertex. */ float x; /** * The y coordinate of the vertex. */ float y; /** * The z coordinate of the vertex. */ float z; /** * The red color component of the vertex. */ float r; /** * The green color component of the vertex. */ float g; /** * The blue color component of the vertex. */ float b; /** * The alpha component of the vertex. */ float a; }; static void drawDebugLine(MeshBatch* batch, const Vector3& point1, const Vector3& point2, const Vector3& color) { GP_ASSERT(batch); static DebugVertex verts[2]; verts[0].x = point1.x; verts[0].y = point1.y; verts[0].z = point1.z; verts[0].r = color.x; verts[0].g = color.y; verts[0].b = color.z; verts[0].a = 1.0f; verts[1].x = point2.x; verts[1].y = point2.y; verts[1].z = point2.z; verts[1].r = color.x; verts[1].g = color.y; verts[1].b = color.z; verts[1].a = 1.0f; batch->add(verts, 2); } #define DEBUG_BOX_COLOR Vector3(0, 1, 0) #define DEBUG_SPHERE_COLOR Vector3(0, 1, 0) static void drawDebugBox(MeshBatch* batch, const BoundingBox& box, const Matrix& matrix) { // Transform box into world space (since we only store local boxes on mesh) BoundingBox worldSpaceBox(box); worldSpaceBox.transform(matrix); // Get box corners static Vector3 corners[8]; worldSpaceBox.getCorners(corners); // Draw box lines drawDebugLine(batch, corners[0], corners[1], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[1], corners[2], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[2], corners[3], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[3], corners[0], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[4], corners[5], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[5], corners[6], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[6], corners[7], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[7], corners[4], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[0], corners[7], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[1], corners[6], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[2], corners[5], DEBUG_BOX_COLOR); drawDebugLine(batch, corners[3], corners[4], DEBUG_BOX_COLOR); } static void drawDebugSphere(MeshBatch* batch, const BoundingSphere& sphere) { // Draw three rings for the sphere (one for the x, y and z axes) Vector3 pos1, pos2; float step = MATH_PI * 0.2f; float max = MATH_PIX2 + step; // X ring for (float r = 0.0f; r < max; r += step) { pos2.x = sphere.center.x; pos2.y = sphere.center.y + std::cos(r) * sphere.radius; pos2.z = sphere.center.z + std::sin(r) * sphere.radius; if (r > 0) drawDebugLine(batch, pos1, pos2, DEBUG_SPHERE_COLOR); pos1 = pos2; } // Y ring for (float r = 0.0f; r < max; r += step) { pos2.x = sphere.center.x + std::cos(r) * sphere.radius; pos2.y = sphere.center.y; pos2.z = sphere.center.z + std::sin(r) * sphere.radius; if (r > 0) drawDebugLine(batch, pos1, pos2, DEBUG_SPHERE_COLOR); pos1 = pos2; } // Z ring for (float r = 0.0f; r < max; r += step) { pos2.x = sphere.center.x + std::cos(r) * sphere.radius; pos2.y = sphere.center.y + std::sin(r) * sphere.radius; pos2.z = sphere.center.z; if (r > 0) drawDebugLine(batch, pos1, pos2, DEBUG_SPHERE_COLOR); pos1 = pos2; } } static void drawDebugNode(MeshBatch* batch, Node* node, unsigned int debugFlags) { GP_ASSERT(node); Model* model = node->getModel(); if ((debugFlags & Scene::DEBUG_BOXES) && model) { GP_ASSERT(model->getMesh()); MeshSkin* skin = model->getSkin(); if (skin && skin->getRootJoint() && skin->getRootJoint()->getParent()) { // For skinned meshes that have a parent node to the skin's root joint, // we need to transform the bounding volume by that parent node's transform // as well to get the full skinned bounding volume. drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix() * skin->getRootJoint()->getParent()->getWorldMatrix()); } else { drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix()); } } if ((debugFlags & Scene::DEBUG_SPHERES) && model) { drawDebugSphere(batch, node->getBoundingSphere()); } for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling()) { drawDebugNode(batch, child, debugFlags); } } void Scene::drawDebug(unsigned int debugFlags) { if (_debugBatch == NULL) { Material* material = createDebugMaterial(); VertexFormat::Element elements[] = { VertexFormat::Element(VertexFormat::POSITION, 3), VertexFormat::Element(VertexFormat::COLOR, 4) }; _debugBatch = MeshBatch::create(VertexFormat(elements, 2), Mesh::LINES, material, false); SAFE_RELEASE(material); } _debugBatch->start(); for (Node* node = _firstNode; node != NULL; node = node->_nextSibling) { drawDebugNode(_debugBatch, node, debugFlags); } _debugBatch->finish(); if (_activeCamera) { GP_ASSERT(_debugBatch->getMaterial()); GP_ASSERT(_debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix")); _debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_activeCamera->getViewProjectionMatrix()); } _debugBatch->draw(); } }