From 2969fd49003bbe075dadab2bb24a78fa469af905 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Mon, 16 Sep 2019 14:23:10 +0100 Subject: [PATCH] DOB visibility culling added --- ldob.cpp | 18 ++++ ldob.h | 8 ++ lportal.cpp | 32 ++++++-- lportal.h | 4 + lroom.cpp | 194 ++++++++++++++++++++++++++++++++++++++++---- lroom.h | 15 +++- lroom_converter.cpp | 82 ++++++++++++++----- lroom_manager.cpp | 61 +++++++++++--- lroom_manager.h | 6 +- lvector.h | 11 ++- 10 files changed, 370 insertions(+), 61 deletions(-) diff --git a/ldob.cpp b/ldob.cpp index 0e94b1d..0c9962d 100644 --- a/ldob.cpp +++ b/ldob.cpp @@ -1,2 +1,20 @@ #include "ldob.h" +#include "scene/3d/mesh_instance.h" + + +Spatial * LSob::GetSpatial() const +{ + Object * pObj = ObjectDB::get_instance(m_ID); + Spatial * pSpat = Object::cast_to(pObj); + return pSpat; +} + + + +Spatial * LDob::GetSpatial() const +{ + Object * pObj = ObjectDB::get_instance(m_ID); + Spatial * pSpat = Object::cast_to(pObj); + return pSpat; +} diff --git a/ldob.h b/ldob.h index 2dd8dff..af833ba 100644 --- a/ldob.h +++ b/ldob.h @@ -2,18 +2,26 @@ #include "scene/3d/spatial.h" +class VisualInstance; // static object class LSob { public: + Spatial * GetSpatial() const; + ObjectID m_ID; // godot object AABB m_aabb; // world space + bool m_bVisible; }; // dynamic object class LDob { public: + Spatial * GetSpatial() const; + ObjectID m_ID; + bool m_bVisible; + float m_fRadius; }; diff --git a/lportal.cpp b/lportal.cpp index 98e2448..a26175d 100644 --- a/lportal.cpp +++ b/lportal.cpp @@ -23,10 +23,13 @@ #include "lroom.h" +//#define LPORTAL_VERBOSE + bool LPortal::m_bRunning = false; void LPortal::print(String sz) { +#ifdef LPORTAL_VERBOSE if (m_bRunning) { } @@ -34,6 +37,7 @@ void LPortal::print(String sz) { print_line(sz); } +#endif } @@ -64,7 +68,20 @@ String LPortal::FindNameAfter(Node * pNode, String szStart) String name = pNode->get_name(); szRes = name.substr(szStart.length()); - print("\t\tNameAfter is " + szRes); + // because godot doesn't support multiple nodes with the same name, we will strip e.g. a number + // after an @ on the end of the name... + // e.g. portal_kitchen@2 + for (int c=0; c p_vertices, const Transform &tr m_ptsWorld.resize(nPoints); - print("\tLPortal::CreateGeometry nPoints : " + itos(nPoints)); + //print("\t\t\tLPortal::CreateGeometry nPoints : " + itos(nPoints)); for (int n=0; n p_vertices, const Transform &tr m_ptsWorld.set(n, ptWorld); m_ptCentre += ptWorld; - print("\t\t" + itos(n) + "\tLocal : " + Variant(p_vertices[n]) + "\tWorld : " + ptWorld); + //print("\t\t\t\t" + itos(n) + "\tLocal : " + Variant(p_vertices[n]) + "\tWorld : " + ptWorld); } SortVertsClockwise(); @@ -237,7 +256,7 @@ void LPortal::ReverseWindingOrder() void LPortal::CopyReversedGeometry(const LPortal &source) { - print("CopyReversedGeometry"); + //print("\t\t\tCopyReversedGeometry"); // points are the same but reverse winding order int nPoints = source.m_ptsWorld.size(); m_ptCentre = source.m_ptCentre; @@ -247,7 +266,7 @@ void LPortal::CopyReversedGeometry(const LPortal &source) for (int n=0; nget_instance_id(); +// LDob dob; +// dob.m_ID = pDOB->get_instance_id(); m_DOBs.push_back(dob); } -bool LRoom::DOB_Remove(Node * pDOB) +unsigned int LRoom::DOB_Find(Node * pDOB) const { ObjectID id = pDOB->get_instance_id(); @@ -68,11 +72,21 @@ bool LRoom::DOB_Remove(Node * pDOB) { if (m_DOBs[n].m_ID == id) { - m_DOBs.remove(n); - return true; + return n; } } + return -1; +} + +bool LRoom::DOB_Remove(unsigned int ui) +{ + if (ui < m_DOBs.size()) + { + m_DOBs.remove_unsorted(ui); + return true; + } + return false; } @@ -102,7 +116,9 @@ LRoom * LRoom::DOB_Update(LRoomManager &manager, Spatial * pDOB) if (dist > slop) { +#ifdef LROOM_VERBOSE print("DOB at pos " + pt + " ahead of portal " + port.get_name() + " by " + String(Variant(dist))); +#endif // we want to move into the adjoining room return &manager.Portal_GetLinkedRoom(port); @@ -112,6 +128,61 @@ LRoom * LRoom::DOB_Update(LRoomManager &manager, Spatial * pDOB) return 0; } +// hide all the objects not hit on this frame .. instead of calling godot hide without need +// (it might be expensive) +void LRoom::FinalizeVisibility(LRoomManager &manager) +{ + //print_line("FinalizeVisibility room " + get_name() + " NumSOBs " + itos(m_SOBs.size()) + ", NumDOBs " + itos(m_DOBs.size())); + + for (int n=0; nshow(); + else + pS->hide(); + } + } + + for (int n=0; nget_name()); + pS->show(); + } + else + pS->hide(); + } + } +} + +// hide godot room and all linked dobs +void LRoom::Hide_All() +{ + GetGodotRoom()->hide(); + + for (int n=0; nhide(); + } +} void LRoom::FirstTouch(LRoomManager &manager) { @@ -123,14 +194,11 @@ void LRoom::FirstTouch(LRoomManager &manager) // hide all objects for (int n=0; n(pNode); + m_SOBs[n].m_bVisible = false; - if (pObj) - pObj->hide(); - } + // hide all dobs + for (int n=0; n= 8) { +#ifdef LROOM_VERBOSE print("\t\t\tDEPTH LIMIT REACHED"); +#endif return; } +#ifdef LROOM_VERBOSE print("DetermineVisibility_Recursive from " + get_name()); +#endif // only handle one touch per frame so far (one portal into room) - assert (manager.m_uiFrameCounter > m_uiFrameTouched); + //assert (manager.m_uiFrameCounter > m_uiFrameTouched); // first touch if (m_uiFrameTouched < manager.m_uiFrameCounter) @@ -156,6 +228,51 @@ void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, cons GetGodotRoom()->show(); manager.m_BF_visible_rooms.SetBit(m_RoomID, true); +#define LPORTAL_CULL_STATIC +#ifdef LPORTAL_CULL_STATIC + + // clip all objects in this room to the clipping planes + for (int n=0; nget_name()); + + for (int p=0; p 0.0f) + { + bShow = false; + break; + } + } + + if (bShow) + sob.m_bVisible = true; + + } + + +#else // clip all objects in this room to the clipping planes for (int n=0; nshow(); + sob.m_bVisible = true; +// pObj->show(); // else // pObj->hide(); } } +#endif + + // cull DOBs + for (int n=0; nget_global_transform().origin; + + //print_line("\t\t\tculling dob " + pObj->get_name()); + float radius = dob.m_fRadius; + + for (int p=0; p radius) + { + bShow = false; + break; + } + } + + if (bShow) + dob.m_bVisible = true; + } + } + + // look through portals for (int p=0; pm_uiFrameTouched == manager.m_uiFrameCounter) - continue; +// if (pLinkedRoom->m_uiFrameTouched == manager.m_uiFrameCounter) +// continue; // cull by portal angle to camera. // Note we need to deal with 'side on' portals, and the camera has a spreading view, so we cannot simply dot // the portal normal with camera direction, we need to take into account angle to the portal itself. const Vector3 &portal_normal = port.m_Plane.normal; +#ifdef LROOM_VERBOSE print("\ttesting portal " + port.get_name() + " normal " + portal_normal); +#endif // we will dot the portal angle with a ray from the camera to the portal centre // (there might be an even better ray direction but this will do for now) @@ -237,7 +393,9 @@ void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, cons // float dot = cam.m_ptDir.dot(portal_normal); if (dot <= -0.0f) // 0.0 { +#ifdef LROOM_VERBOSE print("\t\tportal culled (wrong direction) dot is " + String(Variant(dot)) + ", dir_portal is " + dir_portal); +#endif continue; } @@ -269,7 +427,9 @@ void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, cons // this portal is culled if (overall_res == LPortal::eClipResult::CLIP_OUTSIDE) { +#ifdef LROOM_VERBOSE print("\t\tportal culled (outside planes)"); +#endif continue; } diff --git a/lroom.h b/lroom.h index 53ab8c8..de37be8 100644 --- a/lroom.h +++ b/lroom.h @@ -59,7 +59,7 @@ private: LVector m_SOBs; // dynamic objects - Vector m_DOBs; + LVector m_DOBs; // portals are stored in the manager in a contiguous list int m_iFirstPortal; @@ -87,9 +87,18 @@ protected: void DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector &planes, int portalID_from = -1); void FirstTouch(LRoomManager &manager); + // hide godot room and all linked dobs + void Hide_All(); - void DOB_Add(Spatial * pDOB); - bool DOB_Remove(Node * pDOB); + // hide all the objects not hit on this frame .. instead of calling godot hide without need + // (it might be expensive) + void FinalizeVisibility(LRoomManager &manager); + + + void DOB_Add(const LDob &dob); + const LDob &DOB_Get(unsigned int ui) const {return m_DOBs[ui];} + unsigned int DOB_Find(Node * pDOB) const; + bool DOB_Remove(unsigned int ui); LRoom * DOB_Update(LRoomManager &manager, Spatial * pDOB); public: diff --git a/lroom_converter.cpp b/lroom_converter.cpp index 00492ba..abe59a3 100644 --- a/lroom_converter.cpp +++ b/lroom_converter.cpp @@ -208,11 +208,12 @@ int LRoomConverter::CountRooms() // go through the nodes hanging off the room looking for those that are meshes to mark portal locations void LRoomConverter::LRoom_DetectPortalMeshes(LRoom &lroom, LTempRoom &troom) { - print("DetectPortalMeshes"); + print("DetectPortalMeshes from room " + lroom.get_name()); Spatial * pGRoom = lroom.GetGodotRoom(); assert (pGRoom); + for (int n=0; nget_child_count(); n++) { Node * pChild = pGRoom->get_child(n); @@ -228,9 +229,41 @@ void LRoomConverter::LRoom_DetectPortalMeshes(LRoom &lroom, LTempRoom &troom) LRoom_DetectedPortalMesh(lroom, troom, pMesh, szLinkRoom); } } - } + // we need an enclosing while loop because we might be deleting children and mucking up the iterator + bool bDetectedOne = true; + + while (bDetectedOne) + { + bDetectedOne = false; + + for (int n=0; nget_child_count(); n++) + { + Node * pChild = pGRoom->get_child(n); + + MeshInstance * pMesh = Object::cast_to(pChild); + if (pMesh) + { + // name must start with 'portal_' + // and ends with the name of the room we want to link to (without the 'room_') + if (LPortal::NameStartsWith(pMesh, "portal_")) + { + // delete the original child, as it is no longer needed at runtime (except maybe for debugging .. NYI?) + // pMeshInstance->hide(); + pMesh->get_parent()->remove_child(pMesh); + pMesh->queue_delete(); + + bDetectedOne = true; + } + } + + if (bDetectedOne) + break; + } // for loop + + } // while + } // handles the slight faff involved in getting a new portal in the manager contiguous list of portals @@ -258,13 +291,14 @@ void LRoomConverter::LRoom_MakePortalFinalList(LRoom &lroom, LTempRoom &troom) // found a portal mesh! create a matching LPortal void LRoomConverter::LRoom_DetectedPortalMesh(LRoom &lroom, LTempRoom &troom, MeshInstance * pMeshInstance, String szLinkRoom) { - print("\tDetected PortalMesh"); + print("\tDetected PortalMesh to " + szLinkRoom); // which room does this portal want to link to? int iLinkRoom = FindRoom_ByName(szLinkRoom); if (iLinkRoom == -1) { - WARN_PRINTS("portal to room " + szLinkRoom + ", room not found"); + print("\t\tWARNING : portal to room " + szLinkRoom + ", room not found"); + //WARN_PRINTS("portal to room " + szLinkRoom + ", room not found"); return; } @@ -281,10 +315,8 @@ void LRoomConverter::LRoom_DetectedPortalMesh(LRoom &lroom, LTempRoom &troom, Me // create the portal geometry lport.CreateGeometry(p_vertices, pMeshInstance->get_global_transform()); - // delete the original child, as it is no longer needed at runtime (except maybe for debugging .. NYI?) -// pMeshInstance->hide(); - pMeshInstance->get_parent()->remove_child(pMeshInstance); - pMeshInstance->queue_delete(); + + print("\t\t\tnum portals now " + itos(troom.m_Portals.size())); } @@ -292,28 +324,39 @@ void LRoomConverter::LRoom_DetectedPortalMesh(LRoom &lroom, LTempRoom &troom, Me // will automatically create a mirror portal the other way. void LRoomConverter::LRoom_MakePortalsTwoWay(LRoom &lroom, LTempRoom &troom, int iRoomNum) { + print("LRoomConverter::LRoom_MakePortalsTwoWay from room " + lroom.get_name() + ", contains " + itos (troom.m_Portals.size()) + " portals"); for (int n=0; nget_name()); @@ -139,7 +139,11 @@ void LRoomManager::dob_register(Node * pDOB) if (!pRoom) return; - pRoom->DOB_Add(pSpat); + LDob dob; + dob.m_ID = pSpat->get_instance_id(); + dob.m_fRadius = radius; + + pRoom->DOB_Add(dob); // save the room ID on the dob metadata Obj_SetRoomNum(pSpat, iRoomNum); @@ -164,8 +168,18 @@ bool LRoomManager::dob_update(Node * pDOB) // remove from the list in old room and add to list in new room, and change the metadata int iRoomNum = pNewRoom->m_RoomID; - pRoom->DOB_Remove(pDOB); - pNewRoom->DOB_Add(pSpat); + // get dob data to move to new room + unsigned int dob_id = pRoom->DOB_Find(pDOB); + assert (dob_id != -1); + + // copy across data before removing + const LDob &data = pRoom->DOB_Get(dob_id); + pNewRoom->DOB_Add(data); + + // remove from old room + pRoom->DOB_Remove(dob_id); + + // save the room ID on the dob metadata Obj_SetRoomNum(pSpat, iRoomNum); @@ -186,7 +200,13 @@ bool LRoomManager::dob_teleport(Node * pDOB) void LRoomManager::dob_unregister(Node * pDOB) { LRoom * pRoom = GetRoomFromDOB(pDOB); - pRoom->DOB_Remove(pDOB); + + if (pRoom) + { + unsigned int dob_id = pRoom->DOB_Find(pDOB); + assert (dob_id != -1); + pRoom->DOB_Remove(dob_id); + } } int LRoomManager::dob_get_room_id(Node * pDOB) @@ -316,7 +336,7 @@ void LRoomManager::FrameUpdate() { if (!m_BF_visible_rooms.GetBit(n)) { - m_Rooms[n].GetGodotRoom()->hide(); + m_Rooms[n].Hide_All(); } } } @@ -328,15 +348,30 @@ void LRoomManager::FrameUpdate() int r = (*m_pPrev_VisibleRoomList)[n]; if (!m_BF_visible_rooms.GetBit(r)) - m_Rooms[r].GetGodotRoom()->hide(); + m_Rooms[r].Hide_All(); } - // swap the current and previous visible room list - LVector * pTemp = m_pCurr_VisibleRoomList; - m_pCurr_VisibleRoomList = m_pPrev_VisibleRoomList; - m_pPrev_VisibleRoomList = pTemp; } + + // and hide all the dobs that are in visible rooms that haven't been made visible +// if (m_pCurr_VisibleRoomList->size() == 0) +// print_line("WARNING : vis room list size is 0"); + + for (int n=0; nsize(); n++) + { + int r = (*m_pCurr_VisibleRoomList)[n]; + m_Rooms[r].FinalizeVisibility(*this); + } + + // swap the current and previous visible room list + LVector * pTemp = m_pCurr_VisibleRoomList; + m_pCurr_VisibleRoomList = m_pPrev_VisibleRoomList; + m_pPrev_VisibleRoomList = pTemp; + + + // hide all the DOB + // when running, emit less debugging output so as not to choke the IDE LPortal::m_bRunning = true; } diff --git a/lroom_manager.h b/lroom_manager.h index 9f9c722..8f0636a 100644 --- a/lroom_manager.h +++ b/lroom_manager.h @@ -48,7 +48,9 @@ class LRoomManager : public Spatial { // keep track of which rooms are visible, so we can hide ones that aren't hit that were previously on Lawn::LBitField_Dynamic m_BF_visible_rooms; - LVector m_VisibleRoomList[2]; + + LVector m_VisibleRoomList_A; + LVector m_VisibleRoomList_B; LVector * m_pCurr_VisibleRoomList; LVector * m_pPrev_VisibleRoomList; @@ -71,7 +73,7 @@ public: // These are defined by their ability to move from room to room. // You can still move static objects within the same room (e.g. elevators, moving platforms) // as these don't require checks for changing rooms. - void dob_register(Node * pDOB); + void dob_register(Node * pDOB, float radius); void dob_unregister(Node * pDOB); bool dob_update(Node * pDOB); bool dob_teleport(Node * pDOB); diff --git a/lvector.h b/lvector.h index 42ae257..530779c 100644 --- a/lvector.h +++ b/lvector.h @@ -21,7 +21,7 @@ // SOFTWARE. -// just a light wrapper around the a vector until we get the Godot vector allocation issues sorted +// just a light wrapper around a vector until we get the Godot vector allocation issues sorted #include "core/vector.h" #include #include @@ -31,6 +31,7 @@ template class LVector public: // array subscript access + // note this is not available in Godot Vector T& operator[](unsigned int ui) { assert (ui < m_iSize); @@ -83,6 +84,14 @@ public: m_Vec[ui] = t; } + // efficient unsorted + void remove_unsorted(unsigned int ui) + { + // just swap the end element and decrement count + m_Vec[ui] = m_Vec[m_iSize-1]; + m_iSize--; + } + T * request() { m_iSize++;