From 56e785ab4b06d3243737e35f0b1d8317a198b58b Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Sun, 15 Sep 2019 15:39:01 +0100 Subject: [PATCH] Refactor LRooms and LPortals to no longer be part of scene graph --- SCsub | 3 +- ldob.h | 10 ++ lportal.cpp | 178 ++-------------------- lportal.h | 37 ++--- lroom.cpp | 311 ++++++------------------------------- lroom.h | 51 +++---- lroom_converter.cpp | 357 +++++++++++++++++++++++++++++++++++++++++++ lroom_converter.h | 88 +++++++++++ lroom_manager.cpp | 363 +++++++------------------------------------- lroom_manager.h | 74 ++++----- lvector.h | 68 ++++++++- register_types.cpp | 4 - 12 files changed, 694 insertions(+), 850 deletions(-) create mode 100644 lroom_converter.cpp create mode 100644 lroom_converter.h diff --git a/SCsub b/SCsub index 892ae70..3ab563c 100644 --- a/SCsub +++ b/SCsub @@ -5,6 +5,7 @@ sources = [ "register_types.cpp", "lroom.cpp", "lroom_manager.cpp", + "lroom_converter.cpp", "lportal.cpp", "lplanes_pool.cpp", "ldob.cpp", @@ -12,7 +13,7 @@ sources = [ ] module_env = env.Clone() -module_env.Append(CXXFLAGS=['-O2', '-std=c++11']) +module_env.Append(CXXFLAGS=['-O2', '-std=c++11', '-Wno-sign-compare', '-Wno-strict-aliasing']) if ARGUMENTS.get('lportal_shared', 'no') == 'yes': # Shared lib compilation diff --git a/ldob.h b/ldob.h index 3c063d5..2dd8dff 100644 --- a/ldob.h +++ b/ldob.h @@ -2,6 +2,16 @@ #include "scene/3d/spatial.h" + +// static object +class LSob +{ +public: + ObjectID m_ID; // godot object + AABB m_aabb; // world space +}; + +// dynamic object class LDob { public: diff --git a/lportal.cpp b/lportal.cpp index 885d9fc..71320f1 100644 --- a/lportal.cpp +++ b/lportal.cpp @@ -126,86 +126,31 @@ LPortal::eClipResult LPortal::ClipWithPlane(const Plane &p) const } - - -// use the name of the portal to find a room to link to -void LPortal::Link(LRoom * pParentRoom) -{ - // should start with 'portal_' - if (!NameStartsWith(this, "lportal_")) - { - WARN_PRINT("Portal name should begin with lportal_"); - return; - } - - String szRoom = FindNameAfter(this, "lportal_"); - - print("LPortal::Link to room " + szRoom); - - // find the room group - Spatial * pGroup = Object::cast_to(pParentRoom->get_parent()); - if (!pGroup) - { - WARN_PRINT("Room parent is not a spatial"); - return; - } - - // attempt to find a child of the group that has the name specified - int nChildren = pGroup->get_child_count(); - - for (int n=0; nget_child(n); - - String szChildName = pChild->get_name(); - - // is the name correct for the desired room? - if (szRoom != szChildName) - continue; - - LRoom * pTargetRoom = Object::cast_to(pChild); - - if (!pTargetRoom) - { - WARN_PRINT("Portal target is not a room"); - return; - } - - // found! link - pTargetRoom->MakeOppositePortal(this, pParentRoom); - return; - } -} - - -void LPortal::CreateGeometry(PoolVector p_vertices) +void LPortal::CreateGeometry(PoolVector p_vertices, const Transform &trans) { int nPoints = p_vertices.size(); ERR_FAIL_COND(nPoints < 3); - m_ptsLocal.resize(nPoints); m_ptsWorld.resize(nPoints); print("\tLPortal::CreateGeometry nPoints : " + itos(nPoints)); for (int n=0; n &verts = m_ptsLocal; + Vector &verts = m_ptsWorld; // find normal Plane plane = Plane(verts[0], verts[1], verts[2]); @@ -237,9 +182,8 @@ void LPortal::SortVertsClockwise() for (int m=n+1; m 0.0f) -// if (p.WhichSideNDLCompatible(m_Verts[m], 0.0f) != CoPlane::NEGATIVE_SIDE) { - Vector3 b = m_ptsLocal[m] - ptCentre; + Vector3 b = verts[m] - ptCentre; b.normalize(); double Angle = a.dot(b); @@ -278,7 +222,7 @@ void LPortal::SortVertsClockwise() void LPortal::ReverseWindingOrder() { - Vector &verts = m_ptsLocal; + Vector &verts = m_ptsWorld; Vector copy = verts; for (int n=0; n(get_node(path)); - if (pNode) - { - ObjectID id = pNode->get_instance_id(); - - m_room_path = path; - m_room_ID = id; - - // make the portal name correct and feature the room name - int num_names = path.get_name_count(); - if (num_names < 1) - { - WARN_PRINT("LPortal::AddRoom : Path too short"); - return false; - } - String szRoom = path.get_name(num_names-1); - - String szPortal = "lportal_" + szRoom; - set_name(szPortal); - - return true; - } - else - { - WARN_PRINT("not a room"); - return false; - } - } - else - { - WARN_PRINTS("portal link room not found : " + path); - } - - return false; -} - LPortal::LPortal() { // unset - m_room_ID = 0; -} - -LRoom * LPortal::GetLinkedRoom() const -{ - Object *pObj = ObjectDB::get_instance(m_room_ID); - - if (!pObj) - return 0; - - LRoom * pRoom = Object::cast_to(pObj); - if (!pRoom) - { - WARN_PRINT_ONCE("LRoomManager::FrameUpdate : curr room is not an LRoom"); - } - - return pRoom; -} - - -void LPortal::_bind_methods() { - + m_iRoomNum = -1; } diff --git a/lportal.h b/lportal.h index 95a3d3d..17d2bd4 100644 --- a/lportal.h +++ b/lportal.h @@ -32,12 +32,8 @@ class LRoom; -class LPortal : public Spatial { - GDCLASS(LPortal, Spatial); - - friend class LRoom; - friend class LRoomManager; -private: +class LPortal { +public: enum eClipResult { @@ -47,46 +43,31 @@ private: }; - ObjectID m_room_ID; - NodePath m_room_path; + const String &get_name() const {return m_szName;} -protected: - static void _bind_methods(); + // linked room, this is the number not godot ID + int m_iRoomNum; + String m_szName; LPortal::eClipResult ClipWithPlane(const Plane &p) const; void AddPlanes(const Vector3 &ptCam, LVector &planes) const; -public: // normal determined by winding order Vector m_ptsWorld; - Vector m_ptsLocal; Plane m_Plane; - - ObjectID GetLinkedRoomID() const {return m_room_ID;} - LRoom * GetLinkedRoom() const; - LPortal(); -private: - // use the name of the portal to find a room to link to - void Link(LRoom * pParentRoom); - bool AddRoom(NodePath path); void CopyReversedGeometry(const LPortal &source); - void CreateGeometry(PoolVector p_vertices); + void CreateGeometry(PoolVector p_vertices, const Transform &trans); void PlaneFromPoints(); - - void CalculateWorldPoints(); - void CalculateLocalPoints(); - void SortVertsClockwise(); void ReverseWindingOrder(); -public: -// useful funcs + + // useful funcs static bool NameStartsWith(Node * pNode, String szSearch); static String FindNameAfter(Node * pNode, String szStart); static void print(String sz); -protected: static bool m_bRunning; }; diff --git a/lroom.cpp b/lroom.cpp index 0a48ed4..33b48d1 100644 --- a/lroom.cpp +++ b/lroom.cpp @@ -27,13 +27,27 @@ void LRoom::print(String sz) { -// print_line(sz); LPortal::print(sz); } LRoom::LRoom() { - m_LocalRoomID = -1; + m_RoomID = -1; m_uiFrameTouched = 0; + m_iFirstPortal = 0; + m_iNumPortals = 0; +} + + + + +Spatial * LRoom::GetGodotRoom() const +{ + Object *pObj = ObjectDB::get_instance(m_GodotID); + + // assuming is a portal + Spatial * pSpat = Object::cast_to(pObj); + + return pSpat; } @@ -63,8 +77,8 @@ bool LRoom::RemoveDOB(Node * pDOB) } -// returns -1 if no change, or the objectID of the linked room -LRoom * LRoom::UpdateDOB(Spatial * pDOB) +// returns -1 if no change, or the linked room we are moving into +LRoom * LRoom::UpdateDOB(LRoomManager &manager, Spatial * pDOB) { const Vector3 &pt = pDOB->get_global_transform().origin; @@ -76,114 +90,26 @@ LRoom * LRoom::UpdateDOB(Spatial * pDOB) // slop = 0.0f; // check each portal - has the object crossed it into the neighbouring room? - int nPortals = m_portal_IDs.size(); - for (int p=0; p(pObj); - - if (!pPortal) - { - WARN_PRINT_ONCE("LRoom::UpdateDynamicObject : Not a portal"); - continue; - } - - float dist = pPortal->m_Plane.distance_to(pt); + float dist = port.m_Plane.distance_to(pt); if (dist > slop) { - print("DOB at pos " + pt + " ahead of portal " + pPortal->get_name() + " by " + String(Variant(dist))); + print("DOB at pos " + pt + " ahead of portal " + port.get_name() + " by " + String(Variant(dist))); - // move into the adjoining room - return pPortal->GetLinkedRoom(); - -// LRoom * pNewRoom = pPortal->GetLinkedRoom(); -// if (pNewRoom) -// { -// // detach from this room and add to the new room -// remove_child(pDynObj); -// pNewRoom->add_child(pDynObj); -// // only allow one transition per frame -// return true; -// } -// else -// { -// WARN_PRINT_ONCE("LRoom::UpdateDynamicObject : portal linked room is NULL"); -// } + // we want to move into the adjoining room + return &manager.Portal_GetLinkedRoom(port); } } return 0; } -/* -// assumes that the object is within, or just outside the bounds of the room... -// if not the results will be 'interesting'. -// Works simply by detecting crossing portals -bool LRoom::UpdateDynamicObject(Node * pDynObj) -{ - Spatial * pSpatial = Object::cast_to(pDynObj); - if (!pSpatial) - { - WARN_PRINT_ONCE("LRoom::UpdateDynamicObject : object is not a spatial"); - return false; - } - const Vector3 &pt = pSpatial->get_global_transform().origin; - - const float slop = 0.2f; - - // check each portal - has the object crossed it into the neighbouring room? - int nPortals = m_portal_IDs.size(); - for (int p=0; p(pObj); - - if (!pPortal) - { - WARN_PRINT_ONCE("LRoom::UpdateDynamicObject : Not a portal"); - continue; - } - - float dist = pPortal->m_Plane.distance_to(pt); - - if (dist > slop) - { - print("DOB at pos " + pt + " ahead of portal " + pPortal->get_name() + " by " + String(Variant(dist))); - - // move into the adjoining room - LRoom * pNewRoom = pPortal->GetLinkedRoom(); - if (pNewRoom) - { - // detach from this room and add to the new room - remove_child(pDynObj); - pNewRoom->add_child(pDynObj); - // only allow one transition per frame - return true; - } - else - { - WARN_PRINT_ONCE("LRoom::UpdateDynamicObject : portal linked room is NULL"); - } - } - } - - return false; -} -*/ - -void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector &planes, Lawn::LBitField_Dynamic &BF_visible, ObjectID portalID_from) +void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector &planes, int portalID_from) { // prevent too much depth if (depth >= 8) @@ -199,19 +125,18 @@ void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, cons m_uiFrameTouched = manager.m_uiFrameCounter; // show this room and add to visible list of rooms - show(); - BF_visible.SetBit(m_LocalRoomID, true); + GetGodotRoom()->show(); + manager.m_BF_visible_rooms.SetBit(m_RoomID, true); // clip all objects in this room to the clipping planes - for (int n=0; n(pNode); - if (pPortal) - continue; + const LSob sob = m_SOBs[n]; + Object * pNode = ObjectDB::get_instance(sob.m_ID); VisualInstance * pObj = Object::cast_to(pNode); + + // should always be a visual instance, only these are added as SOBs if (pObj) { //Vector3 pt = pObj->get_global_transform().origin; @@ -251,36 +176,20 @@ void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, cons } - - - // go through each portal out of here - int nPortals = m_portal_IDs.size(); - -// ObjectID this_room_id = get_instance_id(); - - for (int p=0; p(pObj); - - if (!pPortal) - { - WARN_PRINT_ONCE("LRoom::DetermineVisibility_Recursive : Not a portal"); - continue; - } + const LPortal &port = manager.m_Portals[port_id]; // have we already handled the room on this frame? // get the room pointed to by the portal - LRoom * pLinkedRoom = pPortal->GetLinkedRoom(); + LRoom * pLinkedRoom = &manager.Portal_GetLinkedRoom(port); + if (pLinkedRoom->m_uiFrameTouched == manager.m_uiFrameCounter) continue; @@ -305,7 +214,7 @@ void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, cons // If it isn't we would need a different strategy for (int l=1; lClipWithPlane(planes[l]); + LPortal::eClipResult res = port.ClipWithPlane(planes[l]); switch (res) { @@ -339,152 +248,24 @@ void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, cons new_planes.copy_from(planes); // add the planes for the portal - pPortal->AddPlanes(cam.m_ptPos, new_planes); + port.AddPlanes(cam.m_ptPos, new_planes); if (pLinkedRoom) - pLinkedRoom->DetermineVisibility_Recursive(manager, depth + 1, cam, new_planes, BF_visible, id); + pLinkedRoom->DetermineVisibility_Recursive(manager, depth + 1, cam, new_planes, port_id); + // we no longer need these planes manager.m_Pool.Free(uiPoolMem); } else { // planes pool is empty! + // This will happen if the view goes through shedloads of portals + // The solution is either to increase the plane pool size, or build levels + // with views through multiple portals. Looking through multiple portals is likely to be + // slow anyway because of the number of planes to test. WARN_PRINT_ONCE("Planes pool is empty"); } } } - -// initial setup, allows importing portals as meshes from modelling program, -// which will be auto converted to LPortals with this method -void LRoom::DetectPortalMeshes() -{ - print("DetectPortalMeshes"); - - bool bFoundOne = true; - - while (bFoundOne) - { - bFoundOne = false; - - for (int n=0; n(pChild); - if (pMesh) - { - // name must start with portal_ - if (LPortal::NameStartsWith(pMesh, "portal_")) - { - String szLinkRoom = LPortal::FindNameAfter(pMesh, "portal_"); - DetectedPortalMesh(pMesh, szLinkRoom); - bFoundOne = true; - } - } - - if (bFoundOne) - break; - } - } - -} - -void LRoom::DetectedPortalMesh(MeshInstance * pMeshInstance, String szLinkRoom) -{ - print("\tDetected PortalMesh"); - - Ref rmesh = pMeshInstance->get_mesh(); - - Array arrays = rmesh->surface_get_arrays(0); - PoolVector p_vertices = arrays[VS::ARRAY_VERTEX]; - - LPortal * pNew = memnew(LPortal); - pNew->set_name("lportal_"); - add_child(pNew); - - pNew->set_transform(pMeshInstance->get_transform()); - - NodePath temppath = "../../" + szLinkRoom; - pNew->AddRoom(temppath); - - // create the portal geometry - pNew->CreateGeometry(p_vertices); - - // delete the original child - pMeshInstance->get_parent()->remove_child(pMeshInstance); - - pMeshInstance->queue_delete(); -} - -void LRoom::MakePortalsTwoWay() -{ - for (int n=0; n(pChild); - if (pPortal) - { - pPortal->Link(this); - } - } - -} - -// assuming that portals are a child of the room, detect these and make them 2 way -void LRoom::MakePortalQuickList() -{ - // this function could be called more than one time... - m_portal_IDs.clear(); - - for (int n=0; n(pChild); - if (pPortal) - { - ObjectID id = pPortal->get_instance_id(); - m_portal_IDs.push_back(id); - } - } -} - -// make sure there is a back (opposite) portal leading from the portal from to the roomto, from this room -void LRoom::MakeOppositePortal(LPortal * pPortalFrom, LRoom * pRoomTo) -{ - ObjectID room_to_id = pRoomTo->get_instance_id(); - - // does an opposite portal exist already? - for (int n=0; n(pChild); - if (pPortal) - { - if (pPortal->GetLinkedRoomID() == room_to_id) - // already linked - return; - } - } - - // if we got to here it isn't linked... - // add a new portal - // NYI - LPortal * pNew = memnew(LPortal); - pNew->set_name("lportal"); - add_child(pNew); - - pNew->AddRoom(pRoomTo->get_path()); - pNew->CopyReversedGeometry(*pPortalFrom); -} - - -void LRoom::_bind_methods() { - -} - - diff --git a/lroom.h b/lroom.h index 82a131a..7cfbf83 100644 --- a/lroom.h +++ b/lroom.h @@ -47,56 +47,55 @@ public: }; -class LRoom : public Spatial { - GDCLASS(LRoom, Spatial); +class LRoom +{ friend class LPortal; friend class LRoomManager; + friend class LRoomConverter; private: - // a quick list of object IDs of child portals of this room - Vector m_portal_IDs; - // godot vector for now .. can be lvector + // static objects + LVector m_SOBs; + + // dynamic objects Vector m_DOBs; + // portals are stored in the manager in a contiguous list + int m_iFirstPortal; + int m_iNumPortals; + // Just very rough, room centre for determining start rooms of dobs Vector3 m_ptCentre; + AABB m_AABB; // world bound - // in the Room Manager, NOT the godot object ID - int m_LocalRoomID; + // ID in the Room Manager, NOT the godot object ID + int m_RoomID; + + ObjectID m_GodotID; // frame counter when last touched .. prevents handling rooms multiple times unsigned int m_uiFrameTouched; + String m_szName; + +public: + const String &get_name() const {return m_szName;} + protected: - static void _bind_methods(); - - // initial setup, allows importing portals as meshes from modelling program, - // which will be auto converted to LPortals with this method - void DetectPortalMeshes(); - // assuming that portals are a child of the room, detect these and make them 2 way - void MakePortalsTwoWay(); - void MakePortalQuickList(); - // main function - void DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector &planes, Lawn::LBitField_Dynamic &BF_visible, ObjectID portalID_from = 0); + void DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector &planes, int portalID_from = -1); - // dynamic objects -// bool UpdateDynamicObject(Node * pDynObj); void AddDOB(Spatial * pDOB); bool RemoveDOB(Node * pDOB); - LRoom * UpdateDOB(Spatial * pDOB); -// specific + LRoom * UpdateDOB(LRoomManager &manager, Spatial * pDOB); + public: LRoom(); + Spatial * GetGodotRoom() const; private: - - - // void SetupPortal(LPortal * pPortal); - void MakeOppositePortal(LPortal * pPortalFrom, LRoom * pRoomTo); - void DetectedPortalMesh(MeshInstance * pMeshInstance, String szLinkRoom); static void print(String sz); }; diff --git a/lroom_converter.cpp b/lroom_converter.cpp new file mode 100644 index 0000000..00492ba --- /dev/null +++ b/lroom_converter.cpp @@ -0,0 +1,357 @@ +// Copyright (c) 2019 Lawnjelly + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +#include "lroom_converter.h" +#include "lroom_manager.h" +#include "lportal.h" +#include "scene/3d/mesh_instance.h" + +// save typing, I am lazy +#define LMAN m_pManager + +void LRoomConverter::print(String sz) +{ + // easy to turn on and off debugging + print_line(sz); +} + + +void LRoomConverter::Convert(LRoomManager &manager) +{ + // This just is simply used to set how much debugging output .. more during conversion, less during running + // except when requested by explicitly clearing this flag. + LPortal::m_bRunning = false; + print_line("running convert"); + + LMAN = &manager; + int count = CountRooms(); + + // make sure bitfield is right size for number of rooms + LMAN->m_BF_visible_rooms.Create(count); + + + LMAN->m_Rooms.clear(true); + LMAN->m_Rooms.resize(count); + + m_TempRooms.clear(true); + m_TempRooms.resize(count); + + + Convert_Rooms(); + Convert_Portals(); + LPortal::m_bRunning = true; + + // temp rooms no longer needed + m_TempRooms.clear(true); +} + + +void LRoomConverter::Convert_Rooms() +{ + print_line("Convert_Rooms"); + + // first find all room empties and convert to LRooms + int count = 0; + + for (int n=0; nget_child_count(); n++) + { + Node * pChild = LMAN->get_child(n); + + if (!Node_IsRoom(pChild)) + continue; + + Spatial * pSpat = Object::cast_to(pChild); + assert (pSpat); + + Convert_Room(pSpat, count++); + } + +} + +int LRoomConverter::FindRoom_ByName(String szName) const +{ + for (int n=0; nm_Rooms.size(); n++) + { + if (LMAN->m_Rooms[n].m_szName == szName) + return n; + } + + return -1; +} + +bool LRoomConverter::Convert_Room(Spatial * pNode, int lroomID) +{ + // get the room part of the name + String szFullName = pNode->get_name(); + String szRoom = LPortal::FindNameAfter(pNode, "room_"); + + print_line("Convert_Room : " + szFullName); + + // get a reference to the lroom we are writing to + LRoom &lroom = LMAN->m_Rooms[lroomID]; + + // store the godot room + lroom.m_GodotID = pNode->get_instance_id(); + lroom.m_RoomID = lroomID; + + // create a new LRoom to exchange the children over to, and delete the original empty + lroom.m_szName = szRoom; + + // keep a running bounding volume as we go through the visual instances + // to determine the overall bound of the room + LAABB bb_room; + bb_room.SetToMaxOpposite(); + + int nChildren = pNode->get_child_count(); + for (int n=0; nget_child(n); + + VisualInstance * pVI = Object::cast_to(pChild); + if (pVI) + { + print("\t\tFound VI : " + pVI->get_name()); + + + // update bound to find centre of room roughly + AABB bb = pVI->get_transformed_aabb(); + bb_room.ExpandToEnclose(bb); + + // store some info about the static object for use at runtime + LSob sob; + sob.m_ID = pVI->get_instance_id(); + sob.m_aabb = bb; + + lroom.m_SOBs.push_back(sob); + } + else + { + // not visual instances NYI + } + } + + // store the lroom centre and bound + lroom.m_ptCentre = bb_room.FindCentre(); + + // bound (untested) + lroom.m_AABB.position = bb_room.m_ptMins; + lroom.m_AABB.size = bb_room.m_ptMaxs - bb_room.m_ptMins; + + print_line("\t" + String(lroom.m_szName) + " centre " + lroom.m_ptCentre); + + return true; +} + +void LRoomConverter::Convert_Portals() +{ + for (int pass=0; pass<3; pass++) + { + print_line("Convert_Portals pass " + itos(pass)); + + for (int n=0; nm_Rooms.size(); n++) + { + LRoom &lroom = LMAN->m_Rooms[n]; + LTempRoom &troom = m_TempRooms[n]; + + switch (pass) + { + case 0: + LRoom_DetectPortalMeshes(lroom, troom); + break; + case 1: + LRoom_MakePortalsTwoWay(lroom, troom, n); + break; + case 2: + LRoom_MakePortalFinalList(lroom, troom); + break; + } + + } + + } +} + + +int LRoomConverter::CountRooms() +{ + int nChildren = LMAN->get_child_count(); + int count = 0; + + for (int n=0; nget_child(n))) + count++; + } + + return count; +} + + +// 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"); + + Spatial * pGRoom = lroom.GetGodotRoom(); + assert (pGRoom); + + 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_")) + { + String szLinkRoom = LPortal::FindNameAfter(pMesh, "portal_"); + LRoom_DetectedPortalMesh(lroom, troom, pMesh, szLinkRoom); + } + } + + } + +} + +// handles the slight faff involved in getting a new portal in the manager contiguous list of portals +LPortal * LRoomConverter::LRoom_RequestNewPortal(LRoom &lroom) +{ + // is this the first portal? + if (lroom.m_iNumPortals == 0) + lroom.m_iFirstPortal = LMAN->m_Portals.size(); + + lroom.m_iNumPortals++; + + return LMAN->m_Portals.request(); +} + +// convert the list on each room to a single contiguous list in the manager +void LRoomConverter::LRoom_MakePortalFinalList(LRoom &lroom, LTempRoom &troom) +{ + for (int n=0; n rmesh = pMeshInstance->get_mesh(); + Array arrays = rmesh->surface_get_arrays(0); + PoolVector p_vertices = arrays[VS::ARRAY_VERTEX]; + + // create a new LPortal to fill with this wonderful info + LPortal &lport = *troom.m_Portals.request(); + lport.m_szName = szLinkRoom; + lport.m_iRoomNum = iLinkRoom; + + // 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(); +} + + +// This aims to make life easier for level designers. They only need to make a portal facing one way and LPortal +// will automatically create a mirror portal the other way. +void LRoomConverter::LRoom_MakePortalsTwoWay(LRoom &lroom, LTempRoom &troom, int iRoomNum) +{ + for (int n=0; nm_Rooms[iRoomOrig]; + + // the new portal should have the name of the room the original came from + LPortal &new_port = *nroom.m_Portals.request(); + new_port.m_szName = orig_lroom.m_szName; + new_port.m_iRoomNum = iRoomOrig; + + // the portal vertices should be the same but reversed (to flip the normal) + new_port.CopyReversedGeometry(port); +} + + +/////////////////////////////////////////////////// + +// helper +bool LRoomConverter::Node_IsRoom(Node * pNode) const +{ + Spatial * pSpat = Object::cast_to(pNode); + if (!pSpat) + return false; + + if (LPortal::NameStartsWith(pSpat, "room_")) + return true; + + return false; +} + + + +// keep the global namespace clean +#undef LMAN diff --git a/lroom_converter.h b/lroom_converter.h new file mode 100644 index 0000000..9899a6e --- /dev/null +++ b/lroom_converter.h @@ -0,0 +1,88 @@ +#pragma once + +#include "scene/3d/spatial.h" +#include "lvector.h" +#include "lportal.h" + +class LRoomManager; +class LRoom; +class MeshInstance; + +// simple min max aabb +class LAABB +{ +public: + Vector3 m_ptMins; + Vector3 m_ptMaxs; + void SetToMaxOpposite() + { + float ma = FLT_MAX; + float mi = FLT_MIN; + m_ptMins = Vector3(ma, ma, ma); + m_ptMaxs = Vector3(mi, mi, mi); + } + void ExpandToEnclose(const AABB &bb) + { + if (bb.position.x < m_ptMins.x) m_ptMins.x = bb.position.x; + if (bb.position.y < m_ptMins.y) m_ptMins.y = bb.position.y; + if (bb.position.z < m_ptMins.z) m_ptMins.z = bb.position.z; + if (bb.position.x + bb.size.x > m_ptMaxs.x) m_ptMaxs.x = bb.position.x + bb.size.x; + if (bb.position.y + bb.size.y > m_ptMaxs.y) m_ptMaxs.y = bb.position.y + bb.size.y; + if (bb.position.z + bb.size.z > m_ptMaxs.z) m_ptMaxs.z = bb.position.z + bb.size.z; + } + Vector3 FindCentre() const + { + Vector3 pt; + pt.x = (m_ptMaxs.x - m_ptMins.x) * 0.5f; + pt.y = (m_ptMaxs.y - m_ptMins.y) * 0.5f; + pt.z = (m_ptMaxs.z - m_ptMins.z) * 0.5f; + pt += m_ptMins; + return pt; + } +}; + + + +class LRoomConverter +{ +public: + // temp rooms are used as an intermediate during conversion, because we need to convert the original portals + // and the mirror portals from 2 bits of code, and we want to end up with a final contiguous list of portals + // for efficient rendering. + class LTempRoom + { + public: + LVector m_Portals; + }; + + // this function calls everything else in the converter + void Convert(LRoomManager &manager); + +private: + int CountRooms(); + + void Convert_Rooms(); + bool Convert_Room(Spatial * pNode, int lroomID); + void Convert_Portals(); + + + void LRoom_DetectPortalMeshes(LRoom &lroom, LTempRoom &troom); + void LRoom_MakePortalsTwoWay(LRoom &lroom, LTempRoom &troom, int iRoomNum); + void LRoom_MakePortalFinalList(LRoom &lroom, LTempRoom &troom); + void LRoom_DetectedPortalMesh(LRoom &lroom, LTempRoom &troom, MeshInstance * pMeshInstance, String szLinkRoom); + LPortal * LRoom_RequestNewPortal(LRoom &lroom); + + void TRoom_MakeOppositePortal(const LPortal &port, int iRoomOrig); + + + // helper + bool Node_IsRoom(Node * pNode) const; + int FindRoom_ByName(String szName) const; + + + LRoomManager * m_pManager; + LVector m_TempRooms; + + static void print(String sz); + +}; diff --git a/lroom_manager.cpp b/lroom_manager.cpp index d427619..76e3866 100644 --- a/lroom_manager.cpp +++ b/lroom_manager.cpp @@ -19,15 +19,13 @@ // SOFTWARE. #include "lroom_manager.h" -#include "lportal.h" -#include "lroom.h" #include "core/engine.h" #include "scene/3d/camera.h" #include "scene/3d/mesh_instance.h" +#include "lroom_converter.h" LRoomManager::LRoomManager() { -// m_room_curr = 0; m_cameraID = 0; m_uiFrameCounter = 0; } @@ -38,14 +36,11 @@ int LRoomManager::FindClosestRoom(const Vector3 &pt) const int closest = -1; float closest_dist = FLT_MAX; - for (int n=0; nm_ptCentre); -// print_line("\troom " + itos(n) + " dist " + String(Variant(d))); + float d = pt.distance_squared_to(lroom.m_ptCentre); if (d < closest_dist) { @@ -58,32 +53,30 @@ int LRoomManager::FindClosestRoom(const Vector3 &pt) const } -LRoom * LRoomManager::GetRoomNum(int i) const +const LRoom * LRoomManager::GetRoom(int i) const { - assert (i < m_room_IDs.size()); - Object *pObj = ObjectDB::get_instance(m_room_IDs[i]); - if (!pObj) + if ((unsigned int) i >= m_Rooms.size()) + { + WARN_PRINT_ONCE("LRoomManager::GetRoom out of range"); return 0; - - LRoom * pRoom = Object::cast_to(pObj); - if (!pRoom) - return 0; - - return pRoom; + } + return &m_Rooms[i]; } -int LRoomManager::GetRoomNumFromLRoom(LRoom * pRoom) const +LRoom * LRoomManager::GetRoom(int i) { - // slow .. use metadata for this - int search_id = pRoom->get_instance_id(); - - for (int n=0; n= m_Rooms.size()) { - if (m_room_IDs[n] == search_id) - return n; + WARN_PRINT_ONCE("LRoomManager::GetRoom out of range"); + return 0; } + return &m_Rooms[i]; +} - return -1; + +LRoom &LRoomManager::Portal_GetLinkedRoom(const LPortal &port) +{ + return m_Rooms[port.m_iRoomNum]; } @@ -104,7 +97,7 @@ int LRoomManager::Obj_GetRoomNum(Node * pNode) const return v; } -LRoom * LRoomManager::GetRoomFromDOB(Node * pNode) const +LRoom * LRoomManager::GetRoomFromDOB(Node * pNode) { int iRoom = Obj_GetRoomNum(pNode); if (iRoom == -1) @@ -113,7 +106,7 @@ LRoom * LRoomManager::GetRoomFromDOB(Node * pNode) const return 0; } - LRoom * pRoom = GetRoomNum(iRoom); + LRoom * pRoom = GetRoom(iRoom); if (pRoom == 0) { WARN_PRINT_ONCE("LRoomManager::GetRoomFromDOB : pRoom is NULL"); @@ -138,7 +131,7 @@ void LRoomManager::register_dob(Node * pDOB) if (iRoomNum == -1) return; - LRoom * pRoom = GetRoomNum(iRoomNum); + LRoom * pRoom = GetRoom(iRoomNum); if (!pRoom) return; @@ -163,12 +156,12 @@ bool LRoomManager::update_dob(Node * pDOB) // is it the camera? //bool bCamera = pDOB->get_instance_id() == m_cameraID; - LRoom * pNewRoom = pRoom->UpdateDOB(pSpat); + LRoom * pNewRoom = pRoom->UpdateDOB(*this, pSpat); if (pNewRoom) { // remove from the list in old room and add to list in new room, and change the metadata - int iRoomNum = GetRoomNumFromLRoom(pNewRoom); + int iRoomNum = pNewRoom->m_RoomID; pRoom->RemoveDOB(pDOB); pNewRoom->AddDOB(pSpat); @@ -194,34 +187,6 @@ void LRoomManager::unregister_dob(Node * pDOB) } -/* -bool LRoomManager::update_object(Node * pObj) -{ - // find the room the object is attached to - Node * pParent = pObj->get_parent(); - LRoom * pRoom = Object::cast_to(pParent); - if (!pRoom) - { - WARN_PRINT_ONCE("LRoomManager::update_object : object parent is not an LRoom"); - return false; - } - - bool bChanged = pRoom->UpdateDynamicObject(pObj); - - // special .. for camera keep the camera room ID up to date - // could alternatively just use the parent of the camera? -// if (bChanged) -// { -// if (pObj->get_instance_id() == m_cameraID) -// { -// m_room_curr = pObj->get_parent()->get_instance_id(); -// } -// } - - return bChanged; -} -*/ - void LRoomManager::set_camera(Node * pCam) { m_cameraID = 0; @@ -245,192 +210,10 @@ void LRoomManager::set_camera(Node * pCam) // convert empties and meshes to rooms and portals void LRoomManager::convert() { - LPortal::m_bRunning = false; - print_line("running convert"); - - Convert_Rooms(); - Convert_Portals(); - Find_Rooms(); - LPortal::m_bRunning = true; + LRoomConverter conv; + conv.Convert(*this); } -void LRoomManager::Find_Rooms() -{ - print_line ("Find_Rooms"); - m_room_IDs.clear(); - - // first find all room empties and convert to LRooms - for (int n=0; n(pChild); - if (pRoom) - { - pRoom->m_LocalRoomID = m_room_IDs.size(); - m_room_IDs.push_back(pRoom->get_instance_id()); - } - } - - /* - m_room_curr = 0; - - // just set current room to first room - if (m_room_IDs.size()) - { - m_room_curr = m_room_IDs[0]; - print_line("first room ID is " + itos(m_room_curr)); - } - */ - - // make sure bitfield is right size for number of rooms - m_BF_visible_rooms.Create(m_room_IDs.size()); -} - -void LRoomManager::Convert_Rooms() -{ - print_line("Convert_Rooms"); - - bool bConvertedOne = true; - - // instead of recursive routine - while (bConvertedOne) - { - bConvertedOne = false; - - // first find all room empties and convert to LRooms - for (int n=0; n(pChild); - if (pRoom) - continue; - - Spatial * pSpatialChild = Object::cast_to(pChild); - if (!pSpatialChild) - continue; - - if (LPortal::NameStartsWith(pSpatialChild, "room_")) - { - if (Convert_Room(pSpatialChild)) - bConvertedOne = true; - } - - if (bConvertedOne) - break; - } - } - -} - -void LRoomManager::Convert_Portals() -{ - for (int pass=0; pass<3; pass++) - { - print_line("Convert_Portals pass " + itos(pass)); - - // first find all room empties and convert to LRooms - for (int n=0; n(pChild); - if (pRoom) - { - switch (pass) - { - case 0: - pRoom->DetectPortalMeshes(); - break; - case 1: - pRoom->MakePortalsTwoWay(); - break; - case 2: - pRoom->MakePortalQuickList(); - break; - } - } - } - - } -} - - -bool LRoomManager::Convert_Room(Spatial * pNode) -{ - // get the room part of the name - String szFullName = pNode->get_name(); - String szRoom = LPortal::FindNameAfter(pNode, "room_"); - - print_line("Convert_Room : " + szFullName); - - // create a new LRoom to exchange the children over to, and delete the original empty - LRoom * pNew = memnew(LRoom); - pNew->set_name(szRoom); - add_child(pNew); - - // make the transform of the L room match the original spatial - pNew->set_transform(pNode->get_transform()); - - // New .. room is at origin, all the child nodes are now transformed - // so everything is in world space ... makes dynamic objects changing rooms easier - //Transform tr_orig = pNode->get_transform(); - - int nChildren = pNode->get_child_count(); - - LAABB bb_room; - bb_room.SetToMaxOpposite(); - - for (int n=0; nget_child(c); - - // change the transform of the child to take away the room transform -// Spatial * pSChild = Object::cast_to(pChild); -// Transform tr_world; -// if (pSChild) -// { -// tr_world = pSChild->get_global_transform(); -// } - - // update bound to find centre of room roughly - VisualInstance * pVI = Object::cast_to(pChild); - if (pVI) - { - AABB bb = pVI->get_transformed_aabb(); - bb_room.ExpandToEnclose(bb); - } - - - pNode->remove_child(pChild); - - // add the child to the new node - pNew->add_child(pChild); - - -// if (pSChild) -// { -// pSChild->set_transform(tr_world); -// } - - } - - pNew->m_ptCentre = bb_room.FindCentre(); - print_line(String(pNew->get_name()) + " centre " + pNew->m_ptCentre); - - // all moved .. now finally delete the empty - remove_child(pNode); - pNode->queue_delete(); - - return true; -} void LRoomManager::FrameUpdate() { @@ -440,6 +223,7 @@ void LRoomManager::FrameUpdate() return; } + // we keep a frame counter to prevent visiting things multiple times on the same frame in recursive functions m_uiFrameCounter++; // get the camera desired and make into lcamera @@ -450,42 +234,37 @@ void LRoomManager::FrameUpdate() pCamera = Object::cast_to(pObj); } else - // camera not set + // camera not set .. do nothing return; - // camera not a camera?? + // camera not a camera?? shouldn't happen but we'll check if (!pCamera) return; - // if not started -// if (!m_room_curr) -// return; - - // determine visibility -// Object *pObj = ObjectDB::get_instance(m_room_curr); + // Which room is the camera currently in? LRoom * pRoom = GetRoomFromDOB(pCamera); -// Node * pObj = pCamera->get_parent(); -// if (!pObj) -// return; - -// LRoom * pRoom = Object::cast_to(pObj); if (!pRoom) { - WARN_PRINT_ONCE("LRoomManager::FrameUpdate : curr room is not an LRoom"); - //print_line("LRoomManager::FrameUpdate : curr room is not an LRoom"); -// m_room_curr = 0; + WARN_PRINT_ONCE("LRoomManager::FrameUpdate : Camera is not in an LRoom"); return; } + // as we hit visible rooms we will mark them in a bitset, so we can hide any rooms + // that are showing that haven't been hit this frame m_BF_visible_rooms.Blank(); + // lcamera contains the info needed for culling LCamera cam; cam.m_ptPos = Vector3(0, 0, 0); cam.m_ptDir = Vector3 (-1, 0, 0); - // reset the pool for another frame + // reset the planes pool for another frame m_Pool.Reset(); + + // the first set of planes are allocated and filled with the view frustum planes + // Note that the visual server doesn't actually need to do view frustum culling as a result... + // (but is still doing it for now) unsigned int pool_member = m_Pool.Request(); assert (pool_member != -1); @@ -494,39 +273,29 @@ void LRoomManager::FrameUpdate() // get the camera desired and make into lcamera assert (pCamera); -// if (pCamera) - { - Transform tr = pCamera->get_global_transform(); - cam.m_ptPos = tr.origin; - cam.m_ptDir = tr.basis.get_row(2); // or possibly get_axis .. z is what we want + Transform tr = pCamera->get_global_transform(); + cam.m_ptPos = tr.origin; + cam.m_ptDir = tr.basis.get_row(2); // or possibly get_axis .. z is what we want - planes.copy_from(pCamera->get_frustum()); - } - - pRoom->DetermineVisibility_Recursive(*this, 0, cam, planes, m_BF_visible_rooms); + // luckily godot already has a function to return a list of the camera clipping planes + planes.copy_from(pCamera->get_frustum()); + // the whole visibility algorithm is recursive, spreading out from the camera room, + // rendering through any portals in view into other rooms, etc etc + pRoom->DetermineVisibility_Recursive(*this, 0, cam, planes); // finally hide all the rooms that are currently visible but not in the visible bitfield as having been hit - // NOTE this could be more efficient - for (int n=0; n(pObj); - if (pRoom) + if (!m_BF_visible_rooms.GetBit(n)) { - if (!m_BF_visible_rooms.GetBit(n)) - { - pRoom->hide(); - } + m_Rooms[n].GetGodotRoom()->hide(); } } + // when running, emit less debugging output so as not to choke the IDE LPortal::m_bRunning = true; - - - // only do once for now -// m_room_curr = 0; } @@ -534,46 +303,30 @@ void LRoomManager::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { -// bool bVisible = is_visible_in_tree(); -// ChangeFlags(SF_INVISIBLE, bVisible == false); -// SetProcessing(); + // turn on process, unless we are in the editor if (!Engine::get_singleton()->is_editor_hint()) set_process_internal(true); else set_process_internal(false); - -// // we can't translate string name of Target to a node until we are in the tree -// ResolveTargetPath(); } break; -// case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { -// FixedUpdate(); -// } break; case NOTIFICATION_INTERNAL_PROCESS: { FrameUpdate(); } break; -// case NOTIFICATION_VISIBILITY_CHANGED: { -// bool bVisible = is_visible_in_tree(); -// ChangeFlags(SF_INVISIBLE, bVisible == false); -// SetProcessing(); -//// if (bVisible) -//// print_line("now visible"); -//// else -//// print_line("now hidden"); -// } break; } } void LRoomManager::_bind_methods() { + // main functions ClassDB::bind_method(D_METHOD("rooms_convert"), &LRoomManager::convert); ClassDB::bind_method(D_METHOD("rooms_set_camera"), &LRoomManager::set_camera); -// ClassDB::bind_method(D_METHOD("update_object"), &LRoomManager::update_object); - + // functions to add dynamic objects to the culling system + // Note that these should not be placed directly in rooms, the system will 'soft link' to them + // so they can be held, e.g. in pools elsewhere in the scene graph ClassDB::bind_method(D_METHOD("dob_register"), &LRoomManager::register_dob); ClassDB::bind_method(D_METHOD("dob_unregister"), &LRoomManager::unregister_dob); ClassDB::bind_method(D_METHOD("dob_update"), &LRoomManager::update_dob); ClassDB::bind_method(D_METHOD("dob_teleport"), &LRoomManager::teleport_dob); - } diff --git a/lroom_manager.h b/lroom_manager.h index 90fb12a..c75cf21 100644 --- a/lroom_manager.h +++ b/lroom_manager.h @@ -30,58 +30,26 @@ #include "lbitfield_dynamic.h" #include "lplanes_pool.h" +#include "lroom.h" +#include "lportal.h" -class LRoom; -// simple min max aabb -class LAABB -{ -public: - Vector3 m_ptMins; - Vector3 m_ptMaxs; - void SetToMaxOpposite() - { - float ma = FLT_MAX; - float mi = FLT_MIN; - m_ptMins = Vector3(ma, ma, ma); - m_ptMaxs = Vector3(mi, mi, mi); - } - void ExpandToEnclose(const AABB &bb) - { - if (bb.position.x < m_ptMins.x) m_ptMins.x = bb.position.x; - if (bb.position.y < m_ptMins.y) m_ptMins.y = bb.position.y; - if (bb.position.z < m_ptMins.z) m_ptMins.z = bb.position.z; - if (bb.position.x + bb.size.x > m_ptMaxs.x) m_ptMaxs.x = bb.position.x + bb.size.x; - if (bb.position.y + bb.size.y > m_ptMaxs.y) m_ptMaxs.y = bb.position.y + bb.size.y; - if (bb.position.z + bb.size.z > m_ptMaxs.z) m_ptMaxs.z = bb.position.z + bb.size.z; - } - Vector3 FindCentre() const - { - Vector3 pt; - pt.x = (m_ptMaxs.x - m_ptMins.x) * 0.5f; - pt.y = (m_ptMaxs.y - m_ptMins.y) * 0.5f; - pt.z = (m_ptMaxs.z - m_ptMins.z) * 0.5f; - pt += m_ptMins; - return pt; - } -}; class LRoomManager : public Spatial { GDCLASS(LRoomManager, Spatial); friend class LRoom; + friend class LRoomConverter; - // a quick list of object IDs of child rooms - Vector m_room_IDs; -// ObjectID m_room_curr; + // godot ID of the camera (which should be registered as a DOB to allow moving between rooms) ObjectID m_cameraID; // 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; - Vector m_VisibleRoomList[2]; - int m_CurrentVisibleRoomList; +// Vector m_VisibleRoomList[2]; +// int m_CurrentVisibleRoomList; // keep a frame counter, to mark when objects have been hit by the visiblity algorithm // already to prevent multiple hits on rooms and objects @@ -97,7 +65,10 @@ public: // normally this will be your main camera, but you can choose another for debugging void set_camera(Node * pCam); - // updating dynamic objects in case they move out of their current room + // Dynamic objects .. cameras, players, boxes etc + // 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 register_dob(Node * pDOB); void unregister_dob(Node * pDOB); bool update_dob(Node * pDOB); @@ -108,25 +79,32 @@ protected: static void _bind_methods(); void _notification(int p_what); + // The recursive visibility function needs to allocate loads of planes. + // We use a pool for this instead of allocating on the fly. LPlanesPool m_Pool; private: - // one time conversion and setup - void Convert_Rooms(); - bool Convert_Room(Spatial * pNode); - void Convert_Portals(); - void Find_Rooms(); // helper funcs - LRoom * GetRoomNum(int i) const; - LRoom * GetRoomFromDOB(Node * pNode) const; - int GetRoomNumFromLRoom(LRoom * pRoom) const; + const LRoom * GetRoom(int i) const; + LRoom * GetRoom(int i); + + LRoom * GetRoomFromDOB(Node * pNode); int FindClosestRoom(const Vector3 &pt) const; + // for DOBs, we need some way of storing the room ID on them, so we use metadata (currently) + // this is pretty gross but hey ho int Obj_GetRoomNum(Node * pNode) const; void Obj_SetRoomNum(Node * pNode, int num); - + // this is where we do all the culling void FrameUpdate(); + + // find which room is linked by a portal + LRoom &Portal_GetLinkedRoom(const LPortal &port); + + // lists of rooms and portals, contiguous list so cache friendly + LVector m_Rooms; + LVector m_Portals; }; #endif diff --git a/lvector.h b/lvector.h index e9b2c70..42ae257 100644 --- a/lvector.h +++ b/lvector.h @@ -1,8 +1,30 @@ #pragma once -// just a light wrapper around the Godot vector until we get the allocation issues sorted +// Copyright (c) 2019 Lawnjelly + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +// just a light wrapper around the a vector until we get the Godot vector allocation issues sorted #include "core/vector.h" #include +#include template class LVector { @@ -14,6 +36,7 @@ public: assert (ui < m_iSize); return m_Vec[ui]; } + const T& operator[](unsigned int ui) const { assert (ui < (unsigned int) m_iSize); @@ -37,10 +60,44 @@ public: m_Vec.resize(s); } + void resize(int s, bool bCompact = false) + { + // new size + m_iSize = s; + + // if compacting is not desired, no need to shrink vector + if (m_iSize < m_Vec.size()) + { + if (!bCompact) + { + return; + } + } + + m_Vec.resize(s); + } + void set(unsigned int ui, const T &t) { assert (ui < (unsigned int) m_iSize); - m_Vec.set(ui, t); + m_Vec[ui] = t; + } + + T * request() + { + m_iSize++; + if (m_iSize >= m_Vec.size()) + grow(); + + return &m_Vec[m_iSize-1]; + } + + void grow() + { + int new_size = m_Vec.size() * 2; + if (!new_size) new_size = 1; + + reserve(new_size); } void push_back(const T &t) @@ -51,12 +108,12 @@ public: { int size = m_iSize; m_iSize = size_p1; - m_Vec.set(size, t); + set(size, t); } else { // need more space - reserve(m_Vec.size() * 2); + grow(); // call recursive push_back(t); @@ -102,10 +159,11 @@ public: m_iSize = 0; } + int size() const {return m_iSize;} private: - Vector m_Vec; + std::vector m_Vec; // working size int m_iSize; diff --git a/register_types.cpp b/register_types.cpp index 2c63aef..4487cf7 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -3,16 +3,12 @@ #include "register_types.h" #include "core/class_db.h" -#include "lroom.h" #include "lroom_manager.h" -#include "lportal.h" void register_lportal_types() { - ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); } void unregister_lportal_types() {