//======================================================================== // SceneNodes.cpp - defines the base class for the 3D graphics scene graph, and // derived classes like RootNode, Alpha scene nodes, Camera, Grids, etc. // // Part of the GameCode4 Application // // GameCode4 is the sample application that encapsulates much of the source code // discussed in "Game Coding Complete - 4th Edition" by Mike McShaffry and David // "Rez" Graham, published by Charles River Media. // ISBN-10: 1133776574 | ISBN-13: 978-1133776574 // // If this source code has found it's way to you, and you think it has helped you // in any way, do the authors a favor and buy a new copy of the book - there are // detailed explanations in it that compliment this code well. Buy a copy at Amazon.com // by clicking here: // http://www.amazon.com/gp/product/1133776574/ref=olp_product_details?ie=UTF8&me=&seller= // // There's a companion web site at http://www.mcshaffry.com/GameCode/ // // The source code is managed and maintained through Google Code: // http://code.google.com/p/gamecode4/ // // (c) Copyright 2012 Michael L. McShaffry and David Graham // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser GPL v3 // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See // http://www.gnu.org/licenses/lgpl-3.0.txt for more details. // // You should have received a copy of the GNU Lesser GPL v3 // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //======================================================================== #include "../Unix/GameCodeStd.h" #include "../Actors/RenderComponent.h" #include "../Actors/TransformComponent.h" #include "SceneNodes.h" #include "Scene.h" #include //////////////////////////////////////////////////// // SceneNodeProperties Implementation //////////////////////////////////////////////////// SceneNodeProperties::SceneNodeProperties(void) { m_ActorId = INVALID_ACTOR_ID; m_Radius = 0; m_RenderPass = RenderPass_0; m_AlphaType = AlphaOpaque; } // // SceneNodeProperties::Transform - Chapter 16, page 528 // void SceneNodeProperties::Transform(glm::mat4 *toWorld, glm::mat4 *fromWorld) const { if (toWorld) *toWorld = m_ToWorld; if (fromWorld) *fromWorld = m_FromWorld; } //////////////////////////////////////////////////// // SceneNode Implementation //////////////////////////////////////////////////// SceneNode::SceneNode( ActorId actorId, WeakBaseRenderComponentPtr renderComponent, RenderPass renderPass, const glm::mat4 to, std::optional from ) { m_pParent= NULL; m_Props.m_ActorId = actorId; m_Props.m_Name = (renderComponent) ? renderComponent->VGetName() : "SceneNode"; m_Props.m_RenderPass = renderPass; m_Props.m_AlphaType = AlphaOpaque; m_RenderComponent = renderComponent; VSetTransform(to, from); SetRadius(0); // [mrmike] - these lines were moved to VOnRestore() post press //Color color = (renderComponent) ? renderComponent->GetColor() : g_White; //m_Props.m_Material.SetDiffuse(color); } SceneNode::~SceneNode() { } // // SceneNode::VOnRestore - Chapter 16, page 532 // int SceneNode::VOnRestore(Scene *pScene) { Color color = (m_RenderComponent) ? m_RenderComponent->GetColor() : g_White; m_Props.m_Material.SetDiffuse(color); // This is meant to be called from any class // that inherits from SceneNode and overloads // VOnRestore() SceneNodeList::iterator i = m_Children.begin(); SceneNodeList::iterator end = m_Children.end(); while (i != end) { (*i)->VOnRestore(pScene); ++i; } return 0; } // // SceneNode::VSetTransform - Chapter 16, page 531 // void SceneNode::VSetTransform( const glm::mat4 toWorld, std::optional fromWorld ) { m_Props.m_ToWorld = toWorld; if (!fromWorld) { m_Props.m_FromWorld = glm::inverse(m_Props.m_ToWorld); } else { m_Props.m_FromWorld = *fromWorld; } } // // SceneNode::VPostRender - Chapter 16, page 532 // int SceneNode::VPreRender(Scene *pScene) { StrongActorPtr pActor = MakeStrongPtr(g_pApp->GetGameLogic()->VGetActor(m_Props.m_ActorId)); if (pActor) { std::shared_ptr pTc = MakeStrongPtr(pActor->GetComponent(TransformComponent::g_Name)); if (pTc) { m_Props.m_ToWorld = pTc->GetTransform(); } } pScene->PushAndSetMatrix(m_Props.m_ToWorld); return 0; } // // SceneNode::VIsVisible - Chapter 16, page 533 // bool SceneNode::VIsVisible(Scene *pScene) const { // transform the location of this node into the camera space // of the camera attached to the scene // glm::mat4 toWorld, fromWorld; // pScene->GetCamera()->VGet()->Transform(&toWorld, &fromWorld); // glm::vec3 pos = GetWorldPosition(); // glm::vec3 fromWorldPos = fromWorld.Xform(pos); // Frustum const &frustum = pScene->GetCamera()->GetFrustum(); // bool isVisible = frustum.Inside(fromWorldPos, VGet()->Radius()); // return isVisible; return true; } // // SceneNode::VPostRender - Chapter 16, page 532 // int SceneNode::VPostRender(Scene *pScene) { pScene->PopMatrix(); return 0; } // // SceneNode::VOnUpdate - Chapter 16, page 532 // int SceneNode::VOnUpdate(Scene *pScene, uint64_t const elapsedMs) { // This is meant to be called from any class // that inherits from SceneNode and overloads // VOnUpdate() SceneNodeList::iterator i = m_Children.begin(); SceneNodeList::iterator end = m_Children.end(); while (i != end) { (*i)->VOnUpdate(pScene, elapsedMs); ++i; } return 0; } // // SceneNode::VRenderChildren - Chapter 16, page 533 // int SceneNode::VRenderChildren(Scene *pScene) { // Iterate through the children SceneNodeList::iterator i = m_Children.begin(); SceneNodeList::iterator end = m_Children.end(); while (i != end) { if ((*i)->VPreRender(pScene) == 0) { // You could short-circuit rendering // if an object returns E_FAIL from // VPreRender() // Don't render this node if you can't see it if ((*i)->VIsVisible(pScene)) { float alpha = (*i)->VGet()->m_Material.GetAlpha(); if (alpha==fOPAQUE) { (*i)->VRender(pScene); } // else if (alpha!=fTRANSPARENT) // { // // The object isn't totally transparent // AlphaSceneNode *asn = GCC_NEW AlphaSceneNode; // } (*i)->VRenderChildren(pScene); } // [mrmike] post-press fix - if the parent is not visible, the childrend // shouldn't be visible either. // (*i)->VRenderChildren(pScene); } (*i)->VPostRender(pScene); ++i; } return 0; } // // SceneNode::VAddChild - Chapter 16, page 535 // bool SceneNode::VAddChild(std::shared_ptr ikid) { m_Children.push_back(ikid); std::shared_ptr kid = static_pointer_cast(ikid); kid->m_pParent = this; // [mrmike] Post-press fix - the parent was never set! // The radius of the sphere should be fixed right here glm::vec3 kidPos = geometry::GetPosition(kid->VGet()->ToWorld()); // [mrmike] - Post-press fix. This was not correct! subtracting the parents's position from the kidPos // created a HUGE radius, depending on the location of the parent, which could be anywhere // in the game world. //Vec3 dir = kidPos - m_Props.ToWorld().GetPosition(); //float newRadius = dir.Length() + kid->VGet()->Radius(); float newRadius = glm::length(kidPos) + kid->VGet()->Radius(); if (newRadius > m_Props.m_Radius) m_Props.m_Radius = newRadius; return true; } // // SceneNode::VRemoveChild - not in the book // // If an actor is destroyed it should be removed from the scene graph. // Generally the HumanView will recieve a message saying the actor has been // destroyed, and it will then call Scene::RemoveChild which will traverse // the scene graph to find the child that needs removing. // bool SceneNode::VRemoveChild(ActorId id) { for(SceneNodeList::iterator i=m_Children.begin(); i!=m_Children.end(); ++i) { const SceneNodeProperties* pProps = (*i)->VGet(); if(pProps->GetActorId() != INVALID_ACTOR_ID && id == pProps->GetActorId()) { i = m_Children.erase(i); //this can be expensive for vectors return true; } } return false; } //////////////////////////////////////////////////// // RootNode Implementation //////////////////////////////////////////////////// // // RootNode::RootNode - Chapter 16, page 545 // RootNode::RootNode() : SceneNode(INVALID_ACTOR_ID, WeakBaseRenderComponentPtr(), RenderPass_0, glm::mat4(1.0f)) { m_Children.reserve(RenderPass_Last); std::shared_ptr staticGroup(GCC_NEW SceneNode(INVALID_ACTOR_ID, WeakBaseRenderComponentPtr(), RenderPass_Static, glm::mat4(1.0f))); m_Children.push_back(staticGroup); // RenderPass_Static = 0 std::shared_ptr actorGroup(GCC_NEW SceneNode(INVALID_ACTOR_ID, WeakBaseRenderComponentPtr(), RenderPass_Actor, glm::mat4(1.0f))); m_Children.push_back(actorGroup); // RenderPass_Actor = 1 std::shared_ptr skyGroup(GCC_NEW SceneNode(INVALID_ACTOR_ID, WeakBaseRenderComponentPtr(), RenderPass_Sky, glm::mat4(1.0f))); m_Children.push_back(skyGroup); // RenderPass_Sky = 2 std::shared_ptr invisibleGroup(GCC_NEW SceneNode(INVALID_ACTOR_ID, WeakBaseRenderComponentPtr(), RenderPass_NotRendered, glm::mat4(1.0f))); m_Children.push_back(invisibleGroup); // RenderPass_NotRendered = 3 } // // RootNode::VAddChild - Chapter 16, page 546 // bool RootNode::VAddChild(std::shared_ptr kid) { // The Root node has children that divide the scene graph into render passes. // Scene nodes will get added to these children based on the value of the // render pass member variable. RenderPass pass = kid->VGet()->GetRenderPass(); if ((unsigned)pass >= m_Children.size() || !m_Children[pass]) { GCC_ASSERT(0 && "There is no such render pass"); return false; } return m_Children[pass]->VAddChild(kid); } // // RootNode::VRemoveChild - not described in the book // // Returns false if nothing was removed // bool RootNode::VRemoveChild(ActorId id) { bool anythingRemoved = false; for(int i=RenderPass_0; iVRemoveChild(id)) { anythingRemoved = true; } } return anythingRemoved; } // // RootNode::VRenderChildren - Chapter 16, page 547 // int RootNode::VRenderChildren(Scene *pScene) { // This code creates fine control of the render passes. for (int pass = RenderPass_0; pass < RenderPass_Last; ++pass) { switch(pass) { case RenderPass_Static: case RenderPass_Actor: m_Children[pass]->VRenderChildren(pScene); break; case RenderPass_Sky: { std::shared_ptr skyPass = pScene->GetRenderer()->VPrepareSkyBoxPass(); m_Children[pass]->VRenderChildren(pScene); break; } } } return 0; } //////////////////////////////////////////////////// // CameraNode Implementation //////////////////////////////////////////////////// // // CameraNode::VRender - Chapter 16, page 550 // int CameraNode::VRender(Scene *pScene) { if (m_DebugCamera) { // m_Frustum.Render(); } return 1; } // // CameraNode::VOnRestore - Chapter 16, page 550 // int CameraNode::VOnRestore(Scene *pScene) { Point p = g_pApp->GetScreenSize(); m_Frustum.SetAspect((float) p.x / (float) p.y); m_Projection = glm::perspective( m_Frustum.m_Fov, m_Frustum.m_Aspect, m_Frustum.m_Near, m_Frustum.m_Far ); // [munnellg] this isn't necessary, but we'll do it to // preserve book code pScene->GetRenderer()->VSetProjectionTransform(m_Projection); return 0; } // // CameraNode::SetView - Chapter 16, page 550 // // Note: this is incorrectly called CameraNode::SetView in the book // int CameraNode::SetViewTransform(Scene *pScene) { // TODO(munnellg) - implement // If there is a target, make sure the camera is // rigidly attached right behind the target if(m_pTarget) { glm::mat4 mat = m_pTarget->VGet()->ToWorld(); glm::vec4 at = m_CamOffsetVector; glm::vec4 atWorld = mat * at; glm::vec3 pos = geometry::GetPosition(mat) + glm::vec3(atWorld); geometry::SetPosition(mat, pos); VSetTransform(mat); } m_View = VGet()->FromWorld(); // [munnellg] this isn't necessary, but we'll do it to // preserve book code pScene->GetRenderer()->VSetViewTransform(m_View); return 0; } // // CameraNode::GetWorldViewProjection - not described in the book // // Returns the concatenation of the world and view projection, which is generally sent into vertex shaders // glm::mat4 CameraNode::GetWorldViewProjection(Scene *pScene) { glm::mat4 world = pScene->GetTopMatrix(); glm::mat4 view = VGet()->FromWorld(); return m_Projection * view * world; } //////////////////////////////////////////////////// // D3DGrid9 Implementation // // D3D9 classes are not covered in the 4th edition. They are included here for those // people that don't have D3D11 compatible video cards. // // The code is somewhat similar to the D3DGrid11 class, which IS discussed in Chapter 16 // of the 4th edition. // //////////////////////////////////////////////////// OpenGLGrid::OpenGLGrid(ActorId actorId, WeakBaseRenderComponentPtr renderComponent, const glm::mat4 pMatrix) : SceneNode(actorId, renderComponent, RenderPass_0, pMatrix) { m_bTextureHasAlpha = false; m_numIndices = 0; m_vbo = 0; m_vao = 0; m_ebo = 0; } OpenGLGrid::~OpenGLGrid() { glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ebo); } int OpenGLGrid::VOnRestore(Scene *pScene) { // clear out any existing buffers glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ebo); // TODO(munnellg) - these VOnRestore calls need to have their results checked SceneNode::VOnRestore(pScene); // TODO(munnellg) - release shaders if allocating before creating shader so we don't leak memory m_ShaderProgram.OnRestore(pScene); GridRenderComponent* grc = static_cast(m_RenderComponent); int squares = grc->GetDivision(); SetRadius(sqrt(squares * squares / 2.0f) ); // Create the vertex buffer - we'll need enough verts // to populate the grid. If we want a 2x2 grid, we'll // need 3x3 set of verts. m_numVerts = (squares+1)*(squares+1); // Create vertex buffer // Fill the vertex buffer. We are setting the tu and tv texture // coordinates, which range from 0.0 to 1.0 VertexUnlitTextured *pVerts = GCC_NEW VertexUnlitTextured[m_numVerts]; GCC_ASSERT(pVerts && "Out of memory in OpenGLGrid::VOnRestore"); if (!pVerts) return 1; for( int j=0; j<(squares+1); j++ ) { for (int i=0; i<(squares+1); i++) { // Which vertex are we setting? int index = i + (j * (squares+1) ); VertexUnlitTextured *vert = &pVerts[index]; // Default position of the grid is centered on the origin flat on // the XZ plane float x = (float)i - (squares/2.0f); float y = (float)j - (squares/2.0f); vert->Pos = glm::vec3(x,0.f,y); vert->Normal = glm::vec3(0.0f, 1.0f, 0.0f); // The texture coordinates are set to x,y to make the // texture tile along with units - 1.0, 2.0, 3.0, etc vert->Uv.x = x; vert->Uv.y = y; } } // The number of indicies equals the number of polygons times 3 // since there are 3 indicies per polygon. Each grid square contains // two polygons. The indicies are 16 bit, since our grids won't // be that big! m_numPolys = squares * squares * 2; m_numIndices = m_numPolys * 3; GLuint *pIndices = GCC_NEW GLuint[m_numIndices]; GCC_ASSERT(pIndices && "Out of memory in OpenGLGrid::VOnRestore"); if (!pIndices) { delete[] pVerts; return 1; } // Loop through the grid squares and calc the values // of each index. Each grid square has two triangles: // // A - B // | / | // C - D GLuint *current = pIndices; for( int j=0; j(m_RenderComponent); m_ShaderProgram.SetTexture(grc->GetTextureResource()); m_ShaderProgram.SetupRender(pScene, this); glBindVertexArray(m_vao); glDrawElements(GL_TRIANGLES, m_numIndices, GL_UNSIGNED_INT, 0); glBindVertexArray(0); return 0; }