Refactor LRooms and LPortals to no longer be part of scene graph

This commit is contained in:
lawnjelly 2019-09-15 15:39:01 +01:00 committed by GitHub
parent 9fbdfe2bf9
commit 56e785ab4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 694 additions and 850 deletions

3
SCsub
View File

@ -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

10
ldob.h
View File

@ -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:

View File

@ -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<Spatial>(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; n<nChildren; n++)
{
Node * pChild = pGroup->get_child(n);
String szChildName = pChild->get_name();
// is the name correct for the desired room?
if (szRoom != szChildName)
continue;
LRoom * pTargetRoom = Object::cast_to<LRoom>(pChild);
if (!pTargetRoom)
{
WARN_PRINT("Portal target is not a room");
return;
}
// found! link
pTargetRoom->MakeOppositePortal(this, pParentRoom);
return;
}
}
void LPortal::CreateGeometry(PoolVector<Vector3> p_vertices)
void LPortal::CreateGeometry(PoolVector<Vector3> 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<nPoints; n++)
{
m_ptsLocal.set(n, p_vertices[n]);
Variant pt = p_vertices[n];
print("\t\t" + itos(n) + "\t: " + pt);
Vector3 ptWorld = trans.xform(p_vertices[n]);
m_ptsWorld.set(n, ptWorld);
print("\t\t" + itos(n) + "\tLocal : " + Variant(p_vertices[n]) + "\tWorld : " + ptWorld);
}
SortVertsClockwise();
CalculateWorldPoints();
PlaneFromPoints();
}
// assume first 3 determine the desired normal
void LPortal::SortVertsClockwise()
{
Vector<Vector3> &verts = m_ptsLocal;
Vector<Vector3> &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<nPoints; m++)
{
if (p.distance_to(verts[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<Vector3> &verts = m_ptsLocal;
Vector<Vector3> &verts = m_ptsWorld;
Vector<Vector3> copy = verts;
for (int n=0; n<verts.size(); n++)
@ -289,57 +233,20 @@ void LPortal::ReverseWindingOrder()
}
// local from world and local transform
void LPortal::CalculateLocalPoints()
{
int nPoints = m_ptsLocal.size();
ERR_FAIL_COND(m_ptsLocal.size() != m_ptsWorld.size());
Transform tr = get_transform();
print("\tCalculateLocalPoints");
for (int n=0; n<nPoints; n++)
{
m_ptsLocal.set(n, tr.xform_inv(m_ptsWorld[n]));
Variant pt = m_ptsLocal[n];
print("\t\t" + itos(n) + "\t: " + pt);
}
}
// world from local and transform
void LPortal::CalculateWorldPoints()
{
int nPoints = m_ptsLocal.size();
ERR_FAIL_COND(m_ptsLocal.size() != m_ptsWorld.size());
Transform tr = get_global_transform();
print("\tCalculateWorldPoints");
for (int n=0; n<nPoints; n++)
{
m_ptsWorld.set(n, tr.xform(m_ptsLocal[n]));
Variant pt = m_ptsWorld[n];
print("\t\t" + itos(n) + "\t: " + pt);
}
}
void LPortal::CopyReversedGeometry(const LPortal &source)
{
print("CopyReversedGeometry");
// points are the same but reverse winding order
int nPoints = source.m_ptsWorld.size();
m_ptsLocal.resize(nPoints);
m_ptsWorld.resize(nPoints);
for (int n=0; n<nPoints; n++)
{
m_ptsWorld.set(n, source.m_ptsWorld[nPoints - n - 1]);
Variant pt = m_ptsWorld[n];
print("\t\t" + itos(n) + "\t: " + pt);
print("\t\t" + itos(n) + "\t: " + Variant(m_ptsWorld[n]));
}
CalculateLocalPoints();
PlaneFromPoints();
}
@ -355,77 +262,12 @@ void LPortal::PlaneFromPoints()
print("Plane normal world space : " + m_Plane);
// Plane opp = Plane(m_ptsWorld[2], m_ptsWorld[1], m_ptsWorld[0]);
// print_line("Plane opposite : " + opp);
}
bool LPortal::AddRoom(NodePath path)
{
print("LPortal::AddRoom path is " + path);
if (has_node(path))
{
LRoom * pNode = Object::cast_to<LRoom>(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<LRoom>(pObj);
if (!pRoom)
{
WARN_PRINT_ONCE("LRoomManager::FrameUpdate : curr room is not an LRoom");
}
return pRoom;
}
void LPortal::_bind_methods() {
m_iRoomNum = -1;
}

View File

@ -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<Plane> &planes) const;
public:
// normal determined by winding order
Vector<Vector3> m_ptsWorld;
Vector<Vector3> 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<Vector3> p_vertices);
void CreateGeometry(PoolVector<Vector3> p_vertices, const Transform &trans);
void PlaneFromPoints();
void CalculateWorldPoints();
void CalculateLocalPoints();
void SortVertsClockwise();
void ReverseWindingOrder();
public:
// 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;
};

311
lroom.cpp
View File

@ -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<Spatial>(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<nPortals; p++)
for (int p=0; p<m_iNumPortals; p++)
{
ObjectID id = m_portal_IDs[p];
const LPortal &port = manager.m_Portals[m_iFirstPortal + p];
// get the portal
Object *pObj = ObjectDB::get_instance(id);
// assuming is a portal
LPortal * pPortal = Object::cast_to<LPortal>(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<Spatial>(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<nPortals; p++)
{
ObjectID id = m_portal_IDs[p];
// get the portal
Object *pObj = ObjectDB::get_instance(id);
// assuming is a portal
LPortal * pPortal = Object::cast_to<LPortal>(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<Plane> &planes, Lawn::LBitField_Dynamic &BF_visible, ObjectID portalID_from)
void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector<Plane> &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<get_child_count(); n++)
for (int n=0; n<m_SOBs.size(); n++)
{
// ignore portals
Node * pNode = get_child(n);
LPortal * pPortal = Object::cast_to<LPortal>(pNode);
if (pPortal)
continue;
const LSob sob = m_SOBs[n];
Object * pNode = ObjectDB::get_instance(sob.m_ID);
VisualInstance * pObj = Object::cast_to<VisualInstance>(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<nPortals; p++)
for (int p=0; p<m_iNumPortals; p++)
{
ObjectID id = m_portal_IDs[p];
int port_id = m_iFirstPortal + p;
// ignore if the portal we are looking in from
if (id == portalID_from)
if (port_id == portalID_from)
continue;
// get the portal
Object *pObj = ObjectDB::get_instance(id);
// assuming is a portal
LPortal * pPortal = Object::cast_to<LPortal>(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; l<planes.size(); l++)
{
LPortal::eClipResult res = pPortal->ClipWithPlane(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<get_child_count(); n++)
{
Node * pChild = get_child(n);
MeshInstance * pMesh = Object::cast_to<MeshInstance>(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<Mesh> rmesh = pMeshInstance->get_mesh();
Array arrays = rmesh->surface_get_arrays(0);
PoolVector<Vector3> 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<get_child_count(); n++)
{
Node * pChild = get_child(n);
LPortal * pPortal = Object::cast_to<LPortal>(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<get_child_count(); n++)
{
Node * pChild = get_child(n);
LPortal * pPortal = Object::cast_to<LPortal>(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<get_child_count(); n++)
{
Node * pChild = get_child(n);
LPortal * pPortal = Object::cast_to<LPortal>(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() {
}

51
lroom.h
View File

@ -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<ObjectID> m_portal_IDs;
// godot vector for now .. can be lvector
// static objects
LVector<LSob> m_SOBs;
// dynamic objects
Vector<LDob> 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<Plane> &planes, Lawn::LBitField_Dynamic &BF_visible, ObjectID portalID_from = 0);
void DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector<Plane> &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);
};

357
lroom_converter.cpp Normal file
View File

@ -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; n<LMAN->get_child_count(); n++)
{
Node * pChild = LMAN->get_child(n);
if (!Node_IsRoom(pChild))
continue;
Spatial * pSpat = Object::cast_to<Spatial>(pChild);
assert (pSpat);
Convert_Room(pSpat, count++);
}
}
int LRoomConverter::FindRoom_ByName(String szName) const
{
for (int n=0; n<LMAN->m_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; n<nChildren; n++)
{
Node * pChild = pNode->get_child(n);
VisualInstance * pVI = Object::cast_to<VisualInstance>(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; n<LMAN->m_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; n<nChildren; n++)
{
if (Node_IsRoom(LMAN->get_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; n<pGRoom->get_child_count(); n++)
{
Node * pChild = pGRoom->get_child(n);
MeshInstance * pMesh = Object::cast_to<MeshInstance>(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<troom.m_Portals.size(); n++)
{
LPortal &lport_final = *LRoom_RequestNewPortal(lroom);
lport_final = troom.m_Portals[n];
}
}
// found a portal mesh! create a matching LPortal
void LRoomConverter::LRoom_DetectedPortalMesh(LRoom &lroom, LTempRoom &troom, MeshInstance * pMeshInstance, String szLinkRoom)
{
print("\tDetected PortalMesh");
// 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");
return;
}
// some godot jiggery pokery to get the mesh verts in local space
Ref<Mesh> rmesh = pMeshInstance->get_mesh();
Array arrays = rmesh->surface_get_arrays(0);
PoolVector<Vector3> 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; n<troom.m_Portals.size(); n++)
{
const LPortal &portal_orig = troom.m_Portals[n];
// get the temproom this portal is linking to
LTempRoom &nroom = m_TempRooms[portal_orig.m_iRoomNum];
// does a portal already exist back to the orig room?
// NOTE this doesn't cope with multiple portals between pairs of rooms yet.
bool bAlreadyLinked =false;
for (int p=0; p<nroom.m_Portals.size(); p++)
{
if (nroom.m_Portals[p].m_iRoomNum == n)
{
bAlreadyLinked = true;
break;
}
}
if (bAlreadyLinked)
continue;
// needs a new reverse link if got to here
TRoom_MakeOppositePortal(portal_orig, iRoomNum);
}
}
// There is a need for a mirror portal, let's make one!
void LRoomConverter::TRoom_MakeOppositePortal(const LPortal &port, int iRoomOrig)
{
LTempRoom &nroom = m_TempRooms[port.m_iRoomNum];
const LRoom &orig_lroom = LMAN->m_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<Spatial>(pNode);
if (!pSpat)
return false;
if (LPortal::NameStartsWith(pSpat, "room_"))
return true;
return false;
}
// keep the global namespace clean
#undef LMAN

88
lroom_converter.h Normal file
View File

@ -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<LPortal> 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<LTempRoom> m_TempRooms;
static void print(String sz);
};

View File

@ -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; n<m_room_IDs.size(); n++)
for (int n=0; n<m_Rooms.size(); n++)
{
LRoom * pRoom = GetRoomNum(n);
if (!pRoom)
continue;
const LRoom &lroom = m_Rooms[n];
float d = pt.distance_squared_to(pRoom->m_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<LRoom>(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_room_IDs.size(); n++)
if ((unsigned int) i >= 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<LRoom>(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<get_child_count(); n++)
{
Node * pChild = get_child(n);
// don't want to handle already converted rooms
LRoom * pRoom = Object::cast_to<LRoom>(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<get_child_count(); n++)
{
Node * pChild = get_child(n);
// don't want to handle already converted rooms
LRoom * pRoom = Object::cast_to<LRoom>(pChild);
if (pRoom)
continue;
Spatial * pSpatialChild = Object::cast_to<Spatial>(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<get_child_count(); n++)
{
Node * pChild = get_child(n);
// don't want to handle already converted rooms
LRoom * pRoom = Object::cast_to<LRoom>(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; n<nChildren; n++)
{
// reverse count
int c = nChildren - n - 1;
Node * pChild = pNode->get_child(c);
// change the transform of the child to take away the room transform
// Spatial * pSChild = Object::cast_to<Spatial>(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<VisualInstance>(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<Camera>(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<LRoom>(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
// luckily godot already has a function to return a list of the camera clipping planes
planes.copy_from(pCamera->get_frustum());
}
pRoom->DetermineVisibility_Recursive(*this, 0, cam, planes, m_BF_visible_rooms);
// 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<m_room_IDs.size(); n++)
{
Object *pObj = ObjectDB::get_instance(m_room_IDs[n]);
LRoom * pRoom = Object::cast_to<LRoom>(pObj);
if (pRoom)
// NOTE this will be done more efficiently, but is okay to start with
for (int n=0; n<m_Rooms.size(); 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);
}

View File

@ -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<ObjectID> 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<int> m_VisibleRoomList[2];
int m_CurrentVisibleRoomList;
// Vector<int> 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<LRoom> m_Rooms;
LVector<LPortal> m_Portals;
};
#endif

View File

@ -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 <assert.h>
#include <vector>
template <class T> 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<T> m_Vec;
std::vector<T> m_Vec;
// working size
int m_iSize;

View File

@ -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<LRoom>();
ClassDB::register_class<LRoomManager>();
ClassDB::register_class<LPortal>();
}
void unregister_lportal_types() {