More realtime support

This commit is contained in:
lawnjelly 2019-11-19 10:29:53 +00:00 committed by GitHub
parent 2a63205867
commit 204f4a43d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 3226 additions and 361 deletions

32
larea.cpp Normal file
View File

@ -0,0 +1,32 @@
// 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 "larea.h"
void LArea::Create(String szName)
{
m_iFirstRoom = 0;
m_iNumRooms = 0;
m_iFirstLight = 0;
m_iNumLights = 0;
m_szName = szName;
}

38
larea.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
// 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 "core/ustring.h"
class LArea
{
public:
void Create(String szName);
String m_szName;
int m_iFirstRoom;
int m_iNumRooms;
// each area contains a list of global lights that affect this area
int m_iFirstLight;
int m_iNumLights;
};

View File

@ -1,3 +1,23 @@
// 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 "ldebug.h"
namespace Lawn

View File

@ -1,12 +1,36 @@
#pragma once
// 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.
// most of the performance sensitive debug output will be compiled out in release builds
// you won't be able to get frame debugging of the visibility tree though.
#ifdef DEBUG_ENABLED
#define LPRINT_RUN(a, b) {String sz;\
for (int n=0; n<Lawn::LDebug::m_iTabDepth; n++)\
sz += "\t";\
LPRINT(a, sz + b);}
//#define LPRINT_RUN(a, b) ;
#else
#define LPRINT_RUN(a, b) ;
#endif
@ -23,6 +47,8 @@ Lawn::LDebug::print(b);\
Lawn::LDebug::print(String("\tWARNING : ") + b);\
}
String ftos(float f) {return String(Variant(f));}
namespace Lawn
{

View File

@ -62,16 +62,76 @@ void LHidable::Show(bool bShow)
/////////////////////////////////////////////////////////////////////
void LLight::SetDefaults()
const char * LSource::m_szTypes[] = {"CAM", "DIR", "SPOT", "OMNI", };
const char * LSource::m_szClasses[] = {"STATIC", "ROOM", "DYN", };
void LSource::Source_SetDefaults()
{
m_GodotID = 0;
m_eType = LT_DIRECTIONAL;
m_fSpread = 0.0f; // for spotlight
m_fMaxDist = 100.0f;
m_eType = ST_CAMERA;
m_eClass = SC_STATIC;
m_fSpread = 0.0f;
m_fRange = FLT_MAX;
m_RoomID = -1;
// spotlight spread definition (light.cpp, line 146)
// float size = Math::tan(Math::deg2rad(param[PARAM_SPOT_ANGLE])) * len;
}
String LSource::MakeDebugString() const
{
String sz = "SOURCE : ";
sz += String(m_szTypes[m_eType]) + " - ";
sz += String(m_szClasses[m_eClass]) + "\tpos ";
sz += String(Variant(m_ptPos)) + "\tdir ";
sz += String(Variant(m_ptDir)) + "\n";
return sz;
}
///////////////////////////////////////////////////
void LLight::Light_SetDefaults()
{
m_Source.Source_SetDefaults();
m_GodotID = 0;
// m_eType = LT_DIRECTIONAL;
// m_eClass = LT_STATIC;
// m_fSpread = 0.0f; // for spotlight
// m_fMaxDist = 100.0f;
// m_RoomID = -1;
m_FirstCaster = 0;
m_NumCasters = 0;
m_NumAffectedRooms = 0;
m_iArea = -1;
}
bool LLight::AddAffectedRoom(int room_id)
{
if (m_NumAffectedRooms >= MAX_AFFECTED_ROOMS)
return false;
m_AffectedRooms[m_NumAffectedRooms++] = room_id;
return true;
}
// dynamic light update
void LLight::Update()
{
// firstly, has it crossed into another room like the dobs
}
String LLight::MakeDebugString() const
{
String sz;
sz += m_Source.MakeDebugString();
return sz;
}

75
ldob.h
View File

@ -73,30 +73,83 @@ public:
};
class LLight : public LHidable
//class LCamera
// trace source can be camera or light
class LSource
{
public:
enum eLightType
enum eSourceType
{
LT_DIRECTIONAL,
LT_SPOTLIGHT,
LT_OMNI,
ST_CAMERA, // frustum planes will have been added
ST_DIRECTIONAL,
ST_SPOTLIGHT, // trace should add back plane and cone planes
ST_OMNI, // no planes, can go in any direction
};
void SetDefaults();
Light * GetGodotLight();
enum eSourceClass
{
SC_STATIC, // non moving light
SC_ROOM, // only moves within the room
SC_DYNAMIC, // can move between rooms (register as a DOB)
};
void Source_SetDefaults();
String MakeDebugString() const;
// funcs
bool IsGlobal() const {return m_RoomID == -1;}
Vector3 m_ptDir;
// all in world space, culling done in world space
Vector3 m_ptPos;
ObjectID m_GodotID;
eLightType m_eType;
Vector3 m_ptDir;
eSourceType m_eType;
eSourceClass m_eClass;
float m_fSpread; // for spotlight
float m_fMaxDist; // shadow distance not light distance
float m_fRange; // shadow distance not light distance
// source room
int m_RoomID; // or -1 for global lights
private:
static const char * m_szTypes[];
static const char * m_szClasses[];
};
class LLight : public LHidable
{
public:
enum {MAX_AFFECTED_ROOMS=64};
LSource m_Source;
ObjectID m_GodotID;
// shadow casters
int m_FirstCaster;
int m_NumCasters;
// funcs
// for dynamic lights
// move them light dobs across planes
// and update the rooms that are affected by the light
void Update();
String MakeDebugString() const;
void Light_SetDefaults();
Light * GetGodotLight();
bool AddAffectedRoom(int room_id);
void ClearAffectedRooms() {m_NumAffectedRooms = 0;}
// keep a list of the rooms affected by this light
uint16_t m_AffectedRooms[MAX_AFFECTED_ROOMS];
int m_NumAffectedRooms;
// for global lights, this is the area or -1 if unset
int m_iArea;
String m_szArea; // set to the area string in the case of area lights, else ""
};

1003
lmain_camera.cpp Normal file

File diff suppressed because it is too large Load Diff

101
lmain_camera.h Normal file
View File

@ -0,0 +1,101 @@
#pragma once
// 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.
//#define LMAINCAMERA_CALC_LUT
// we get the main camera clipping planes and derive the points from 3 plane equations
// in order to cull shadow casters to the camera frustum
class LMainCamera
{
public:
// same order as godot
enum ePlane {
P_NEAR,
P_FAR,
P_LEFT,
P_TOP,
P_RIGHT,
P_BOTTOM,
P_TOTAL,
};
// same order as godot
enum ePoint {
PT_FAR_LEFT_TOP,
PT_FAR_LEFT_BOTTOM,
PT_FAR_RIGHT_TOP,
PT_FAR_RIGHT_BOTTOM,
PT_NEAR_LEFT_TOP,
PT_NEAR_LEFT_BOTTOM,
PT_NEAR_RIGHT_TOP,
PT_NEAR_RIGHT_BOTTOM,
};
static const char * m_szPlanes[];
static const char * m_szPoints[];
// 6 bits, 6 planes
enum { NUM_CAM_PLANES = 6,
NUM_CAM_POINTS = 8,
MAX_CULL_PLANES = 16,
LUT_SIZE = 64,
};
// create the LUT
LMainCamera();
bool Prepare(LRoomManager &manager, Camera * pCam);
// main use of this object, we can create a clipping volume that is a mix of the light frustum and the camera volume
bool AddCameraLightPlanes(LRoomManager &manager, const LSource &lsource, LVector<Plane> &planes) const;
LVector<Plane> m_Planes;
LVector<Vector3> m_Points;
// centre of camera frustum
Vector3 m_ptCentre;
private:
bool AddCameraLightPlanes_Directional(LRoomManager &manager, const LSource &lsource, LVector<Plane> &planes) const;
String String_PlaneBF(unsigned int BF);
#ifdef LMAINCAMERA_CALC_LUT
void GetNeighbours(ePlane p, ePlane neigh_planes[4]) const;
void GetCornersOfPlanes( ePlane _fpPlane0, ePlane _fpPlane1, ePoint _fpRet[2] ) const;
void CreateLUT();
void CompactLUT_Entry(int n);
void DebugPrintLUT();
void DebugPrintLUT_AsTable();
void AddLUT(int p0, int p1, ePoint pts[2]);
void AddLUT_Entry(unsigned int n, ePoint pts[2]);
String DebugStringLUT_Entry(const LVector<uint8_t> &entry);
String String_LUTEntry(const LVector<uint8_t> &entry);
// contains a list of points for each combination of plane facing directions
LVector<uint8_t> m_LUT[LUT_SIZE];
#endif
// precalculated LUT
static uint8_t m_LUT_EntrySizes[LUT_SIZE];
static uint8_t m_LUT_Entries[LUT_SIZE][8];
};

View File

@ -24,7 +24,7 @@
#include "ldebug.h"
#include "lroom_manager.h"
/////////////////////////////////////////////////////////////////////
bool LPortal::NameStartsWith(Node * pNode, String szSearch)
{
@ -86,7 +86,7 @@ void LPortal::AddLightPlanes(LRoomManager &manager, const LLight &light, LVector
int nPoints = pts.size();
ERR_FAIL_COND(nPoints < 3);
if (light.m_eType == LLight::LT_DIRECTIONAL)
if (light.m_Source.m_eType == LSource::ST_DIRECTIONAL)
{
// assuming ortho light
const int max_points = 32;
@ -96,7 +96,7 @@ void LPortal::AddLightPlanes(LRoomManager &manager, const LLight &light, LVector
nPoints = max_points;
// transform pushed points
Vector3 ptPush = light.m_ptDir * 2.0;
Vector3 ptPush = light.m_Source.m_ptDir * 2.0;
for (int n=0; n<nPoints; n++)
{
@ -146,7 +146,7 @@ void LPortal::AddLightPlanes(LRoomManager &manager, const LLight &light, LVector
for (int n=0; n<nPoints; n++)
{
int nPLUS = (n + 1) % nPoints;
p = Plane(pts[nPLUS], pts[n], light.m_ptPos);
p = Plane(pts[nPLUS], pts[n], light.m_Source.m_ptPos);
if (bReverse) p = -p;
planes.push_back(p);
Debug_CheckPlaneValidity(p);

View File

@ -1,3 +1,10 @@
// defines
// frame debug strings
#define LDEBUG_CAMERA
#define LDEBUG_LIGHTS
#define LDEBUG_LIGHT_AFFECTED_ROOMS
// single compilation unit
#include "register_types.cpp"
#include "ldebug.cpp"
@ -11,3 +18,7 @@
#include "lbitfield_dynamic.cpp"
#include "lhelper.cpp"
#include "lscene_saver.cpp"
#include "ltrace.cpp"
#include "lmain_camera.cpp"
#include "larea.cpp"

View File

@ -192,43 +192,67 @@ void LRoom::SoftShow(VisualInstance * pVI, uint32_t show_flags)
}
bool LRoom::RemoveLocalLight(int light_id)
{
int found = m_LocalLights.find(light_id);
if (found == -1)
return false;
m_LocalLights.remove_unsorted(found);
return true;
}
// naive version, adds all the non visible objects in visible rooms as shadow casters
void LRoom::AddShadowCasters(LRoomManager &manager)
{
LPRINT_RUN(2, "ADDSHADOWCASTERS room " + get_name() + ", " + itos(m_iNumShadowCasters_SOB) + " shadow casters");
#ifdef LDEBUG_LIGHT_AFFECTED_ROOMS
if (manager.m_bDebugFrameString)
manager.DebugString_Add("Room " + itos(m_RoomID) + " local lights : ");
#endif
// add all the active lights in this room
for (int n=0; n<m_LocalLights.size(); n++)
{
int lightID = m_LocalLights[n];
if (!manager.m_BF_ActiveLights.GetBit(lightID))
{
manager.m_BF_ActiveLights.SetBit(lightID, true);
manager.m_ActiveLights.push_back(lightID);
manager.Light_FrameProcess(lightID);
// add all shadow casters for this light (new method)
const LLight &light = manager.m_Lights[lightID];
int last_caster = light.m_FirstCaster + light.m_NumCasters;
for (int c=light.m_FirstCaster; c<last_caster; c++)
{
int sobID = manager.m_LightCasters_SOB[c];
// only add to the caster list if not in it already (does this check need to happen, can this ever occur?)
if (!manager.m_BF_caster_SOBs.GetBit(sobID))
{
LPRINT_RUN(2, "\t" + itos(sobID) + ", " + manager.m_SOBs[sobID].GetSpatial()->get_name());
manager.m_BF_caster_SOBs.SetBit(sobID, true);
manager.m_CasterList_SOBs.push_back(sobID);
}
else
{
//LPRINT(2, "\t" + itos(sobID) + ", ALREADY CASTER " + manager.m_SOBs[sobID].GetSpatial()->get_name());
}
}
}
#ifdef LDEBUG_LIGHT_AFFECTED_ROOMS
if (manager.m_bDebugFrameString)
manager.DebugString_Add(itos(lightID) + ", ");
#endif
}
#ifdef LDEBUG_LIGHT_AFFECTED_ROOMS
if (manager.m_bDebugFrameString)
manager.DebugString_Add("\n");
#endif
// NEW .. global area directional lights
// could be done with area bitflags... more efficiently
for (int n=0; n<m_GlobalLights.size(); n++)
{
int lightID = m_GlobalLights[n];
manager.Light_FrameProcess(lightID);
}
/*
for (int n=0; n<m_Areas.size(); n++)
{
int areaID = m_Areas[n];
const LArea &area = manager.m_Areas[areaID];
int last_light = area.m_iFirstLight + area.m_iNumLights;
for (int l=area.m_iFirstLight; l<last_light; l++)
{
int lightID = manager.m_AreaLights[l];
manager.Light_FrameProcess(lightID);
}
}
*/
// new!! use precalced list of shadow casters
// int last = m_iFirstShadowCaster_SOB + m_iNumShadowCasters_SOB;
@ -322,12 +346,24 @@ void LRoom::Release(LRoomManager &manager)
if (pS)
{
// signifies released or unregistered
manager.Obj_SetRoomNum(pS, -2);
manager.Meta_SetRoomNum(pS, -2);
}
}
}
bool LRoom::IsInArea(int area) const
{
for (int n=0; n<m_Areas.size(); n++)
{
if (m_Areas[n] == area)
return true;
}
return false;
}
// allows us to show / hide all dobs as the room visibility changes
void LRoom::Room_MakeVisible(bool bVisible)
{
@ -404,7 +440,7 @@ void LRoom::Debug_ShowAll(bool bActive)
}
/*
void LRoom::FirstTouch(LRoomManager &manager)
{
// set the frame counter
@ -430,9 +466,10 @@ void LRoom::FirstTouch(LRoomManager &manager)
for (int n=0; n<m_DOBs.size(); n++)
m_DOBs[n].m_bVisible = false;
}
*/
void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector<Plane> &planes, int first_portal_plane)
/*
void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LSource &cam, const LVector<Plane> &planes, int first_portal_plane)
{
// prevent too much depth
if (depth > 8)
@ -668,5 +705,5 @@ void LRoom::DetermineVisibility_Recursive(LRoomManager &manager, int depth, cons
} // for p through portals
}
*/

23
lroom.h
View File

@ -39,14 +39,6 @@ class LPortal;
class LRoomManager;
class MeshInstance;
class LCamera
{
public:
// all in world space, culling done in world space
Vector3 m_ptPos;
Vector3 m_ptDir;
};
class LRoom
@ -71,6 +63,12 @@ public:
// local lights affecting this room
LVector<int> m_LocalLights;
// global lights affecting this room
LVector<int> m_GlobalLights;
// areas this room is in
LVector<int> m_Areas;
// portals are stored in the manager in a contiguous list
int m_iFirstPortal;
int m_iNumPortals;
@ -100,8 +98,8 @@ public:
const String &get_name() const {return m_szName;}
// main function
void DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LCamera &cam, const LVector<Plane> &planes, int first_portal_plane = 1);
void FirstTouch(LRoomManager &manager);
// void DetermineVisibility_Recursive(LRoomManager &manager, int depth, const LSource &cam, const LVector<Plane> &planes, int first_portal_plane = 1);
// void FirstTouch(LRoomManager &manager);
// allows us to show / hide all dobs as the room visibility changes
@ -129,6 +127,10 @@ public:
LRoom();
Spatial * GetGodotRoom() const;
// light casting .. changing the local light list
bool RemoveLocalLight(int light_id);
void AddLocalLight(int light_id) {m_LocalLights.push_back(light_id);}
// retained purely for debugging visualization
Geometry::MeshData m_Bound_MeshData;
@ -137,6 +139,7 @@ public:
// and the camera will hide them with a cull mask. This is so that
// objects can still be rendered outside immediate view for casting shadows.
static void SoftShow(VisualInstance * pVI, uint32_t show_flags);
bool IsInArea(int area) const;
private:
// whether lportal thinks this room is currently visible

View File

@ -43,20 +43,6 @@ void LRoomConverter::Convert(LRoomManager &manager, bool bVerbose, bool bPrepara
// except when requested by explicitly clearing this flag.
Lawn::LDebug::m_bRunning = (bVerbose == false);
// test pool vector
// PoolVector<Vector2> arr;
// arr.append(Vector2(0, 0));
// arr.append(Vector2(1, 0));
// arr.append(Vector2(2, 0));
// arr.insert(1, arr[1]);
// LPRINT(5, "DEBUG POOLVECTOR");
// for (int n=0; n<arr.size(); n++)
// {
// LPRINT(2, String(Variant(arr[n])));
// }
if (!m_bFinalRun)
{
LPRINT(5, "running convert PREPARATION RUN");
@ -76,10 +62,11 @@ void LRoomConverter::Convert(LRoomManager &manager, bool bVerbose, bool bPrepara
int count = CountRooms();
int num_global_lights = LMAN->m_Lights.size();
//int num_global_lights = LMAN->m_Lights.size();
// make sure bitfield is right size for number of rooms
LMAN->m_BF_visible_rooms.Create(count);
LMAN->m_LightRender.m_BF_Temp_Visible_Rooms.Create(count);
LMAN->m_Rooms.resize(count);
@ -99,12 +86,16 @@ void LRoomConverter::Convert(LRoomManager &manager, bool bVerbose, bool bPrepara
LMAN->m_BF_master_SOBs.Create(num_sobs);
LMAN->m_BF_master_SOBs_prev.Create(num_sobs);
LMAN->m_LightRender.m_BF_Temp_SOBs.Create(num_sobs);
LMAN->m_BF_ActiveLights.Create(LMAN->m_Lights.size());
LMAN->m_BF_ActiveLights_prev.Create(LMAN->m_Lights.size());
LMAN->m_BF_ProcessedLights.Create(LMAN->m_Lights.size());
// must be done after the bitfields
Convert_Lights();
Convert_ShadowCasters();
Convert_AreaLights();
// hide all in preparation for first frame
//LMAN->ShowAll(false);
@ -119,6 +110,33 @@ void LRoomConverter::Convert(LRoomManager &manager, bool bVerbose, bool bPrepara
int LRoomConverter::Convert_Rooms_Recursive(Node * pParent, int count, int area)
{
for (int n=0; n<pParent->get_child_count(); n++)
{
Node * pChild = pParent->get_child(n);
if (Node_IsRoom(pChild))
{
Spatial * pSpat = Object::cast_to<Spatial>(pChild);
assert (pSpat);
Convert_Room(pSpat, count++, area);
}
else if (Node_IsArea(pChild))
{
// get the area name
String szArea = LPortal::FindNameAfter(pChild, "area_");
// find or create an area with this name
int area_child = Area_FindOrCreate(szArea);
count = Convert_Rooms_Recursive(pChild, count, area_child);
}
}
return count;
}
void LRoomConverter::Convert_Rooms()
{
@ -126,22 +144,27 @@ void LRoomConverter::Convert_Rooms()
// first find all room empties and convert to LRooms
int count = 0;
int area = -1;
for (int n=0; n<LROOMLIST->get_child_count(); n++)
count = Convert_Rooms_Recursive(LROOMLIST, count, area);
}
int LRoomConverter::Area_FindOrCreate(String szName)
{
for (int n=0; n<LMAN->m_Areas.size(); n++)
{
Node * pChild = LROOMLIST->get_child(n);
if (!Node_IsRoom(pChild))
continue;
Spatial * pSpat = Object::cast_to<Spatial>(pChild);
assert (pSpat);
Convert_Room(pSpat, count++);
if (LMAN->m_Areas[n].m_szName == szName)
return n;
}
// create
LArea area;
area.Create(szName);
LMAN->m_Areas.push_back(area);
return LMAN->m_Areas.size() - 1;
}
int LRoomConverter::FindRoom_ByName(String szName) const
{
for (int n=0; n<LMAN->m_Rooms.size(); n++)
@ -179,6 +202,14 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l
{
Node * pChild = pParent->get_child(n);
// ignore invisible
Spatial * pSpatialChild = Object::cast_to<Spatial>(pChild);
if (pSpatialChild && (pSpatialChild->is_visible_in_tree() == false))
{
pSpatialChild->queue_delete();
continue;
}
// we are not interested in portal meshes, as they will be deleted later in conversion
if (Node_IsPortal(pChild))
continue;
@ -196,6 +227,13 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l
continue;
}
// area
if (Node_IsArea(pChild))
{
LRoom_DetectedArea(lroom, pChild);
continue;
}
VisualInstance * pVI = Object::cast_to<VisualInstance>(pChild);
if (pVI)
{
@ -234,13 +272,21 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l
}
bool LRoomConverter::Convert_Room(Spatial * pNode, int lroomID)
// areaID could be -1 if unset
bool LRoomConverter::Convert_Room(Spatial * pNode, int lroomID, int areaID)
{
// get the room part of the name
String szFullName = pNode->get_name();
String szRoom = LPortal::FindNameAfter(pNode, "room_");
LPRINT(4, "Convert_Room : " + szFullName);
if (areaID == -1)
{
LPRINT(4, "Convert_Room : " + szFullName);
}
else
{
LPRINT(4, "Convert_Room : " + szFullName + " area_id " + itos(areaID));
}
// get a reference to the lroom we are writing to
LRoom &lroom = LMAN->m_Rooms[lroomID];
@ -252,11 +298,15 @@ bool LRoomConverter::Convert_Room(Spatial * pNode, int lroomID)
// save the room ID on the godot room metadata
// This is used when registering DOBs and teleporting them with hints
// i.e. the Godot room is used to lookup the room ID of the startroom.
LMAN->Obj_SetRoomNum(pNode, lroomID);
LMAN->Meta_SetRoomNum(pNode, lroomID);
// create a new LRoom to exchange the children over to, and delete the original empty
lroom.m_szName = szRoom;
// area
if (areaID != -1)
lroom.m_Areas.push_back(areaID);
// keep a running bounding volume as we go through the visual instances
// to determine the overall bound of the room
LAABB bb_room;
@ -390,33 +440,180 @@ bool LRoomConverter::Convert_Bound(LRoom &lroom, MeshInstance * pMI)
//}
void LRoomConverter::Convert_AreaLights()
{
// list the rooms in each area
for (int a=0; a<LMAN->m_Areas.size(); a++)
{
LArea &area = LMAN->m_Areas[a];
// add every room in this area to the light affected rooms list
for (int r=0; r<LMAN->m_Rooms.size(); r++)
{
LRoom &room = LMAN->m_Rooms[r];
if (room.IsInArea(a))
{
// add the room to the area room list
if (area.m_iNumRooms == 0)
area.m_iFirstRoom = LMAN->m_AreaRooms.size();
area.m_iNumRooms += 1;
LMAN->m_AreaRooms.push_back(r);
}
}
}
// first identify which lights are area lights, and match area strings to area IDs
for (int n=0; n<LMAN->m_Lights.size(); n++)
{
LLight &l = LMAN->m_Lights[n];
// global area light?
if (!l.m_Source.IsGlobal())
continue;
assert (l.m_iArea == -1);
// match area string to area
// find the area
for (int n=0; n<LMAN->m_Areas.size(); n++)
{
if (LMAN->m_Areas[n].m_szName == l.m_szArea)
{
l.m_iArea = n;
break;
}
}
// area not found?
if (l.m_iArea == -1)
{
LWARN(2, "Convert_AreaLights area not found : " + l.m_szArea);
}
else
{
LPRINT(5,"Area light " + itos (n) + " area " + l.m_szArea + " found area_id " + itos(l.m_iArea));
}
}
// add each light within an area to the area light list
for (int a=0; a<LMAN->m_Areas.size(); a++)
{
LArea &area = LMAN->m_Areas[a];
for (int n=0; n<LMAN->m_Lights.size(); n++)
{
LLight &l = LMAN->m_Lights[n];
int areaID = l.m_iArea;
if (areaID != a)
continue;
// this light affects this area
if (area.m_iNumLights == 0)
area.m_iFirstLight = LMAN->m_AreaLights.size();
LMAN->m_AreaLights.push_back(n);
area.m_iNumLights++;
} // for n
} // for a
// for each global light we can calculate the affected rooms
for (int n=0; n<LMAN->m_Lights.size(); n++)
{
LLight &l = LMAN->m_Lights[n];
int areaID = l.m_iArea;
// not a global light
if (areaID == -1)
continue;
LPRINT(5,"Area light " + itos (n) + " affected rooms:");
// add every room in this area to the light affected rooms list
for (int r=0; r<LMAN->m_Rooms.size(); r++)
{
LRoom &room = LMAN->m_Rooms[r];
if (room.IsInArea(areaID))
{
//l.AddAffectedRoom(r); // no need as this is now done by area
LPRINT(5,"\t" + itos (r));
// store the global lights on the room
room.m_GlobalLights.push_back(n);
}
}
}
}
void LRoomConverter::Convert_Lights()
{
// trace local lights out from rooms and add to each room the light affects
for (int n=0; n<LMAN->m_Lights.size(); n++)
{
LLight &l = LMAN->m_Lights[n];
if (l.IsGlobal())
if (l.m_Source.IsGlobal())
continue; // ignore globals .. affect all rooms
Light_Trace(n);
}
}
void LRoomConverter::Light_Trace(int iLightID)
{
// get the light
LLight &l = LMAN->m_Lights[iLightID];
LPRINT(5,"_____________________________________________________________");
LPRINT(5,"\nLight_Trace " + itos (iLightID));
LMAN->m_Trace.Trace_Light(*LMAN, l, LTrace::LR_CONVERT);
// now save the data from the trace
LRoomManager::LLightRender &lr = LMAN->m_LightRender;
// visible rooms
for (int n=0; n<lr.m_Temp_Visible_Rooms.size(); n++)
{
int room_id = lr.m_Temp_Visible_Rooms[n];
LRoom &room = *LMAN->GetRoom(room_id);
room.AddLocalLight(iLightID);
// store the affected room on the light
l.AddAffectedRoom(room_id);
}
// sobs
for (int n=0; n<lr.m_Temp_Visible_SOBs.size(); n++)
{
int sob_id = lr.m_Temp_Visible_SOBs[n];
// first?
if (!l.m_NumCasters)
l.m_FirstCaster = LMAN->m_LightCasters_SOB.size();
LMAN->m_LightCasters_SOB.push_back(sob_id);
l.m_NumCasters++;
}
LPRINT(5, itos(lr.m_Temp_Visible_Rooms.size()) + " visible rooms, " + itos (lr.m_Temp_Visible_SOBs.size()) + " visible SOBs.\n");
/*
// blank this each time as it is used to create the list of casters
LMAN->m_BF_caster_SOBs.Blank();
// get the light
LLight &l = LMAN->m_Lights[iLightID];
LPRINT(5,"\nLight_Trace " + itos (iLightID) + " direction " + l.m_ptDir);
// reset the planes pool for each render out from the source room
LMAN->m_Pool.Reset();
// the first set of planes are blank
unsigned int pool_member = LMAN->m_Pool.Request();
assert (pool_member != -1);
@ -426,10 +623,11 @@ void LRoomConverter::Light_Trace(int iLightID)
Lawn::LDebug::m_iTabDepth = 0;
Light_TraceRecursive(0, LMAN->m_Rooms[l.m_RoomID], l, iLightID, planes);
Light_TraceRecursive(0, LMAN->m_Rooms[l.m_Source.m_RoomID], l, iLightID, planes);
*/
}
/*
void LRoomConverter::Light_TraceRecursive(int depth, LRoom &lroom, LLight &light, int iLightID, const LVector<Plane> &planes)
{
// prevent too much depth
@ -458,6 +656,9 @@ void LRoomConverter::Light_TraceRecursive(int depth, LRoom &lroom, LLight &light
if (!bAlreadyInList)
{
lroom.m_LocalLights.push_back(iLightID);
// store the affected room on the light
light.AddAffectedRoom(lroom.m_RoomID);
}
// add each light caster that is within the planes to the light caster list
@ -519,7 +720,7 @@ void LRoomConverter::Light_TraceRecursive(int depth, LRoom &lroom, LLight &light
LPRINT_RUN(2, "\tPORTAL " + itos (n) + " (" + itos(portalID) + ") " + port.get_name() + " normal " + port.m_Plane.normal);
float dot = port.m_Plane.normal.dot(light.m_ptDir);
float dot = port.m_Plane.normal.dot(light.m_Source.m_ptDir);
if (dot <= 0.0f)
{
@ -596,7 +797,7 @@ void LRoomConverter::Light_TraceRecursive(int depth, LRoom &lroom, LLight &light
}
}
*/
void LRoomConverter::Convert_ShadowCasters()
{
@ -608,22 +809,22 @@ void LRoomConverter::Convert_ShadowCasters()
{
const LLight &light = LMAN->m_Lights[l];
String sz = "Light " + itos (l);
if (light.IsGlobal())
if (light.m_Source.IsGlobal())
sz += " GLOBAL";
else
sz += " LOCAL from room " + itos(light.m_RoomID);
sz += " LOCAL from room " + itos(light.m_Source.m_RoomID);
LPRINT(5, sz + " direction " + light.m_ptDir);
LPRINT(5, sz + " direction " + light.m_Source.m_ptDir);
for (int n=0; n<LMAN->m_Rooms.size(); n++)
{
LRoom &lroom = LMAN->m_Rooms[n];
// global lights affect every room
bool bAffectsRoom = true;
bool bAffectsRoom = false; // true
// if the light is local, does it affect this room?
if (!light.IsGlobal())
if (!light.m_Source.IsGlobal())
{
// a local light .. does it affect this room?
bAffectsRoom = false;
@ -720,8 +921,22 @@ int LRoomConverter::CountRooms()
for (int n=0; n<nChildren; n++)
{
if (Node_IsRoom(LROOMLIST->get_child(n)))
Node * pChild = LROOMLIST->get_child(n);
if (Node_IsRoom(pChild))
count++;
else
{
// also check the children if this is an area
if (Node_IsArea(pChild))
{
for (int c=0; c<pChild->get_child_count(); c++)
{
Node * pChild2 = pChild->get_child(c);
if (Node_IsRoom(pChild2))
count++;
}
}
}
}
return count;
@ -761,6 +976,7 @@ int LRoomConverter::CountRooms()
// return;
//}
/*
void LRoomConverter::Light_AddCaster_SOB(LLight &light, int sobID)
{
// we will reuse the rendering bitflags for shadow casters for this ... to check for double entries (fnaa fnaa)
@ -780,7 +996,7 @@ void LRoomConverter::Light_AddCaster_SOB(LLight &light, int sobID)
LMAN->m_LightCasters_SOB.push_back(sobID);
light.m_NumCasters++;
}
*/
void LRoomConverter::LRoom_AddShadowCaster_SOB(LRoom &lroom, int sobID)
{
@ -907,14 +1123,14 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int
// cull with light direction
float dot;
if (light.m_eType == LLight::LT_DIRECTIONAL)
if (light.m_Source.m_eType == LSource::ST_DIRECTIONAL)
{
dot = port.m_Plane.normal.dot(light.m_ptDir);
dot = port.m_Plane.normal.dot(light.m_Source.m_ptDir);
}
else
{
// cull with light direction to portal
Vector3 ptLightToPort = port.m_ptCentre - light.m_ptPos;
Vector3 ptLightToPort = port.m_ptCentre - light.m_Source.m_ptPos;
dot = port.m_Plane.normal.dot(ptLightToPort);
}
@ -1090,6 +1306,26 @@ void LRoomConverter::LRoom_MakePortalFinalList(LRoom &lroom, LTempRoom &troom)
}
void LRoomConverter::LRoom_DetectedArea(LRoom &lroom, Node * pNode)
{
// find the area name
String szArea = LPortal::FindNameAfter(pNode, "area_");
// find or create an area with this name
int area = Area_FindOrCreate(szArea);
// check for duplicates? maybe a level design mistake?
if (lroom.m_Areas.find(area) != -1)
{
LWARN(2, "LRoom_DetectedArea : duplicate area in room, ignoring : " + szArea);
return;
}
// add it to the lroom
lroom.m_Areas.push_back(area);
}
void LRoomConverter::LRoom_DetectedLight(LRoom &lroom, Node * pNode)
{
Light * pLight = Object::cast_to<Light>(pNode);
@ -1214,6 +1450,18 @@ bool LRoomConverter::Node_IsLight(Node * pNode) const
return true;
}
bool LRoomConverter::Node_IsArea(Node * pNode) const
{
Spatial * pSpat = Object::cast_to<Spatial>(pNode);
if (!pSpat)
return false;
if (LPortal::NameStartsWith(pSpat, "area_"))
return true;
return false;
}
bool LRoomConverter::Node_IsRoom(Node * pNode) const
{

View File

@ -31,6 +31,7 @@
class LRoomManager;
class LRoom;
class LArea;
class MeshInstance;
// simple min max aabb
@ -87,7 +88,8 @@ private:
int CountRooms();
void Convert_Rooms();
bool Convert_Room(Spatial * pNode, int lroomID);
int Convert_Rooms_Recursive(Node * pParent, int count, int area);
bool Convert_Room(Spatial * pNode, int lroomID, int areaID);
void Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &lroom, LAABB &bb_room);
void Convert_Room_SetDefaultCullMask_Recursive(Node * pParent);
@ -96,7 +98,7 @@ private:
bool Convert_Bound(LRoom &lroom, MeshInstance * pMI);
void Convert_ShadowCasters();
void Convert_Lights();
// void Convert_HideAll();
void Convert_AreaLights();
void LRoom_DetectPortalMeshes(LRoom &lroom, LTempRoom &troom);
@ -109,11 +111,9 @@ private:
// lights
void LRoom_DetectedLight(LRoom &lroom, Node * pNode);
void Light_Trace(int iLightID);
void Light_TraceRecursive(int depth, LRoom &lroom, LLight &light, int iLightID, const LVector<Plane> &planes);
void Light_AddCaster_SOB(LLight &light, int sobID);
void LRoom_DetectedArea(LRoom &lroom, Node * pNode);
// shadows
// void LRoom_FindShadowCasters(LRoom &lroom, int lightID, const LLight &light);
void LRoom_FindShadowCasters_FromLight(LRoom &lroom, const LLight &light);
void LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int depth, LRoom &lroom, const LLight &light, const LVector<Plane> &planes);
void LRoom_AddShadowCaster_SOB(LRoom &lroom, int sobID);
@ -124,13 +124,14 @@ private:
// helper
bool Node_IsRoom(Node * pNode) const;
bool Node_IsArea(Node * pNode) const;
bool Node_IsPortal(Node * pNode) const;
bool Node_IsBound(Node * pNode) const;
bool Node_IsIgnore(Node * pNode) const;
bool Node_IsLight(Node * pNode) const;
int FindRoom_ByName(String szName) const;
int Area_FindOrCreate(String szName);
// set up on entry

View File

@ -45,6 +45,9 @@ LRoomManager::LRoomManager()
m_ID_DebugPlanes = 0;
m_ID_DebugBounds = 0;
m_ID_DebugLights = 0;
m_ID_DebugLightVolumes = 0;
m_ID_DebugFrustums = 0;
m_ID_RoomList = 0;
m_uiFrameCounter = 0;
@ -59,6 +62,9 @@ LRoomManager::LRoomManager()
m_bDebugPlanes = false;
m_bDebugBounds = false;
m_bDebugLights = false;
m_bDebugLightVolumes = false;
m_bDebugFrustums = false;
m_bDebugFrameString = false;
m_pRoomList = 0;
@ -148,14 +154,31 @@ LRoom &LRoomManager::Portal_GetLinkedRoom(const LPortal &port)
}
void LRoomManager::Obj_SetRoomNum(Node * pNode, int num)
// for lights we store the light ID in the metadata
void LRoomManager::Meta_SetLightID(Node * pNode, int id)
{
pNode->set_meta("_llight", id);
}
int LRoomManager::Meta_GetLightID(Node * pNode) const
{
//assert (pNode->has_meta("_lroom"));
Variant v = pNode->get_meta("_llight");
if (v.get_type() == Variant::NIL)
return -1;
return v;
}
void LRoomManager::Meta_SetRoomNum(Node * pNode, int num)
{
pNode->set_meta("_lroom", num);
assert (Obj_GetRoomNum(pNode) == num);
assert (Meta_GetRoomNum(pNode) == num);
}
int LRoomManager::Obj_GetRoomNum(Node * pNode) const
int LRoomManager::Meta_GetRoomNum(Node * pNode) const
{
//assert (pNode->has_meta("_lroom"));
Variant v = pNode->get_meta("_lroom");
@ -167,7 +190,7 @@ int LRoomManager::Obj_GetRoomNum(Node * pNode) const
LRoom * LRoomManager::GetRoomFromDOB(Node * pNode)
{
int iRoom = Obj_GetRoomNum(pNode);
int iRoom = Meta_GetRoomNum(pNode);
if (iRoom < 0)
{
if (iRoom == -1)
@ -211,7 +234,7 @@ bool LRoomManager::dob_register_hint(Node * pDOB, float radius, Node * pRoom)
}
int iRoom = Obj_GetRoomNum(pRoom);
int iRoom = Meta_GetRoomNum(pRoom);
Spatial * pSpat = Object::cast_to<Spatial>(pDOB);
if (!pSpat)
@ -267,7 +290,49 @@ void LRoomManager::CreateDebug()
move_child(b, get_child_count()-1);
m_ID_DebugLights = b->get_instance_id();
b->set_material_override(m_mat_Debug_Bounds);
//b->hide();
b->hide();
}
{
ImmediateGeometry * p = memnew(ImmediateGeometry);
p->set_name("debug_lightvolumes");
add_child(p);
move_child(p, get_child_count()-1);
m_ID_DebugLightVolumes = p->get_instance_id();
// m_mat_Debug_Planes->set_as_toplevel(true);
m_mat_Debug_LightVolumes = Ref<SpatialMaterial>(memnew(SpatialMaterial));
m_mat_Debug_LightVolumes->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
// m_mat_Debug_Planes->set_line_width(6.0);
// m_mat_Debug_Planes->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
// m_mat_Debug_Planes->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
// m_mat_Debug_Planes->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
m_mat_Debug_LightVolumes->set_albedo(Color(0, 1, 1, 1));
p->set_material_override(m_mat_Debug_LightVolumes);
p->hide();
}
{
ImmediateGeometry * p = memnew(ImmediateGeometry);
p->set_name("debug_frustums");
add_child(p);
move_child(p, get_child_count()-1);
m_ID_DebugFrustums = p->get_instance_id();
// m_mat_Debug_Planes->set_as_toplevel(true);
m_mat_Debug_Frustums = Ref<SpatialMaterial>(memnew(SpatialMaterial));
m_mat_Debug_Frustums->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
// m_mat_Debug_Planes->set_line_width(6.0);
// m_mat_Debug_Planes->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
// m_mat_Debug_Planes->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
// m_mat_Debug_Planes->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
m_mat_Debug_Frustums->set_albedo(Color(1, 0, 0, 1));
p->set_material_override(m_mat_Debug_Frustums);
p->hide();
}
}
@ -321,7 +386,7 @@ bool LRoomManager::DobRegister(Spatial * pDOB, float radius, int iRoom)
pRoom->DOB_Add(dob);
// save the room ID on the dob metadata
Obj_SetRoomNum(pDOB, iRoom);
Meta_SetRoomNum(pDOB, iRoom);
// change visibility
DobChangeVisibility(pDOB, 0, pRoom);
@ -396,7 +461,7 @@ int LRoomManager::dob_update(Node * pDOB)
DobChangeVisibility(pSpat, pRoom, pNewRoom);
// save the room ID on the dob metadata
Obj_SetRoomNum(pSpat, iRoomNum);
Meta_SetRoomNum(pSpat, iRoomNum);
// new room number
return iRoomNum;
@ -425,7 +490,7 @@ bool LRoomManager::dob_teleport_hint(Node * pDOB, Node * pRoom)
}
int iRoom = Obj_GetRoomNum(pRoom);
int iRoom = Meta_GetRoomNum(pRoom);
Spatial * pSpat = Object::cast_to<Spatial>(pDOB);
if (!pSpat)
@ -471,7 +536,7 @@ bool LRoomManager::DobTeleport(Spatial * pDOB, int iNewRoomID)
pOldRoom->DOB_Remove(dob_id);
// save the room ID on the dob metadata
Obj_SetRoomNum(pDOB, iNewRoomID);
Meta_SetRoomNum(pDOB, iNewRoomID);
// change visibility
DobChangeVisibility(pDOB, pOldRoom, pNewRoom);
@ -509,7 +574,7 @@ bool LRoomManager::dob_unregister(Node * pDOB)
LRoom * pRoom = GetRoomFromDOB(pDOB);
// change the meta data on the DOB .. this will catch trying to update an unregistered DOB
Obj_SetRoomNum(pDOB, -2);
Meta_SetRoomNum(pDOB, -2);
if (pRoom)
{
@ -521,8 +586,135 @@ bool LRoomManager::dob_unregister(Node * pDOB)
}
void LRoomManager::Light_FrameProcess(int lightID)
{
if (!m_BF_ProcessedLights.GetBit(lightID))
{
m_BF_ProcessedLights.SetBit(lightID, true);
// some lights may be processed but found not to intersect the camera frustum
if (Light_FindCasters(lightID))
{
m_BF_ActiveLights.SetBit(lightID, true);
m_ActiveLights.push_back(lightID);
}
}
}
// now we are centralizing the tracing out from static and dynamic lights for each frame to this function
// returns false if the entire light should be culled
bool LRoomManager::Light_FindCasters(int lightID)
{
// add all shadow casters for this light (new method)
const LLight &light = m_Lights[lightID];
/*
if (light.m_eClass == LLight::LT_STATIC)
{
int last_caster = light.m_FirstCaster + light.m_NumCasters;
for (int c=light.m_FirstCaster; c<last_caster; c++)
{
int sobID = m_LightCasters_SOB[c];
// only add to the caster list if not in it already (does this check need to happen, can this ever occur?)
if (!m_BF_caster_SOBs.GetBit(sobID))
{
LPRINT_RUN(2, "\t" + itos(sobID) + ", " + m_SOBs[sobID].GetSpatial()->get_name());
m_BF_caster_SOBs.SetBit(sobID, true);
m_CasterList_SOBs.push_back(sobID);
}
else
{
//LPRINT(2, "\t" + itos(sobID) + ", ALREADY CASTER " + manager.m_SOBs[sobID].GetSpatial()->get_name());
}
} // for c through caster
} // static lights have a list of SOB casters
*/
// special case of global area lights
if (light.m_iArea != -1)
{
// special trace for area light
if (m_Trace.Trace_Light(*this, light, LTrace::LR_ALL) == false)
return false;
}
else
{
// can only deal with lights in rooms for now
if (light.m_Source.m_RoomID == -1)
{
return true;
}
LRoom * pRoom = GetRoom(light.m_Source.m_RoomID);
if (!pRoom)
return true;
if (m_Trace.Trace_Light(*this, light, LTrace::LR_ALL) == false)
return false;
} // non-area light
/*
// we now need to trace either just DOBs (in the case of static lights)
// or SOBs and DOBs (in the case of dynamic lights)
m_LightRender.m_BF_Temp_SOBs.Blank();
m_LightRender.m_Temp_Visible_SOBs.clear();
const LSource &cam = light.m_Source;
// cam.Source_SetDefaults();
// cam.m_ptPos = light.m_ptPos;
// cam.m_ptDir = light.m_ptDir;
m_Trace.Trace_Prepare(*this, cam, m_LightRender.m_BF_Temp_SOBs, m_BF_visible_rooms, m_LightRender.m_Temp_Visible_SOBs, *m_pCurr_VisibleRoomList);
unsigned int pool_member = m_Pool.Request();
assert (pool_member != -1);
LVector<Plane> &planes = m_Pool.Get(pool_member);
planes.clear();
// create subset planes of light frustum and camera frustum
m_MainCamera.AddCameraLightPlanes(*this, cam, planes);
m_Trace.Trace_Begin(*pRoom, planes);
// we no longer need these planes
m_Pool.Free(pool_member);
*/
// process the sobs that were visible
for (int n=0; n<m_LightRender.m_Temp_Visible_SOBs.size(); n++)
{
int sobID = m_LightRender.m_Temp_Visible_SOBs[n];
// only add to the caster list if not in it already (does this check need to happen, can this ever occur?)
if (!m_BF_caster_SOBs.GetBit(sobID))
{
LPRINT_RUN(2, "\t" + itos(sobID) + ", " + m_SOBs[sobID].GetSpatial()->get_name());
m_BF_caster_SOBs.SetBit(sobID, true);
m_CasterList_SOBs.push_back(sobID);
}
else
{
//LPRINT(2, "\t" + itos(sobID) + ", ALREADY CASTER " + manager.m_SOBs[sobID].GetSpatial()->get_name());
}
}
return true;
}
void LRoomManager::Light_UpdateTransform(LLight &light, const Light &glight) const
{
// assert (glight.is_in_tree());
Transform tr = glight.get_global_transform();
light.m_Source.m_ptPos = tr.origin;
light.m_Source.m_ptDir = -tr.basis.get_axis(2); // or possibly get_axis .. z is what we want
}
// common stuff for global and local light creation
bool LRoomManager::LightCreate(Light * pLight, int roomID)
bool LRoomManager::LightCreate(Light * pLight, int roomID, String szArea)
{
// set culling flag for light
// 1 is for lighting objects outside the room system
@ -530,23 +722,21 @@ bool LRoomManager::LightCreate(Light * pLight, int roomID)
// create new light
LLight l;
l.SetDefaults();
l.Light_SetDefaults();
l.Hidable_Create(pLight);
l.m_GodotID = pLight->get_instance_id();
//l.m_iArea = areaID;
// store the area name as a string if an area light
// as the areas aren't actually created until calling convert
if (szArea != "")
l.m_szArea = szArea;
LSource &lsource = l.m_Source;
// direction
Transform tr = pLight->get_global_transform();
l.m_ptPos = tr.origin;
l.m_ptDir = -tr.basis.get_axis(2); // or possibly get_axis .. z is what we want
l.m_RoomID = roomID;
l.m_fMaxDist = pLight->get_param(Light::PARAM_SHADOW_MAX_DISTANCE);
//l.m_eType = LLight::LT_DIRECTIONAL;
//m_Lights.push_back(l);
Light_UpdateTransform(l, *pLight);
lsource.m_RoomID = roomID;
bool bOK = false;
@ -555,8 +745,9 @@ bool LRoomManager::LightCreate(Light * pLight, int roomID)
if (pSL)
{
LPRINT(2, "\tSPOTLIGHT detected " + pLight->get_name());
l.m_eType = LLight::LT_SPOTLIGHT;
l.m_fSpread = pSL->get_param(Light::PARAM_SPOT_ANGLE);
lsource.m_eType = LSource::ST_SPOTLIGHT;
lsource.m_fSpread = pSL->get_param(Light::PARAM_SPOT_ANGLE);
lsource.m_fRange = pLight->get_param(Light::PARAM_RANGE);
bOK = true;
}
@ -565,7 +756,8 @@ bool LRoomManager::LightCreate(Light * pLight, int roomID)
if (pOL)
{
LPRINT(2, "\tOMNILIGHT detected " + pLight->get_name());
l.m_eType = LLight::LT_OMNI;
lsource.m_eType = LSource::ST_OMNI;
lsource.m_fRange = pLight->get_param(Light::PARAM_RANGE);
bOK = true;
}
@ -573,7 +765,10 @@ bool LRoomManager::LightCreate(Light * pLight, int roomID)
if (pDL)
{
LPRINT(2, "\tDIRECTIONALLIGHT detected " + pLight->get_name());
l.m_eType = LLight::LT_DIRECTIONAL;
lsource.m_eType = LSource::ST_DIRECTIONAL;
// no range but max distance? NYI
bOK = true;
}
@ -586,7 +781,7 @@ bool LRoomManager::LightCreate(Light * pLight, int roomID)
// turn the local light off to start with
if (!l.IsGlobal())
if (!lsource.IsGlobal())
{
// l.Show(false);
//pLight->hide();
@ -598,10 +793,168 @@ bool LRoomManager::LightCreate(Light * pLight, int roomID)
}
bool LRoomManager::light_register(Node * pLightNode)
bool LRoomManager::dynamic_light_register(Node * pLightNode, float radius)
{
CHECK_ROOM_LIST
if (!pLightNode)
{
WARN_PRINT_ONCE("dynamic_light_register : pLightNode is NULL");
return false;
}
ObjectID light_id = pLightNode->get_instance_id();
// does the light already exist in the light list? I.e. was it imported as part of the roomlist?
for (int n=0; n<m_Lights.size(); n++)
{
if (m_Lights[n].m_GodotID == light_id)
{
m_Lights[n].m_Source.m_eClass = LSource::SC_DYNAMIC;
// store the light ID in the metadata for the node
Meta_SetLightID(pLightNode, n);
return true;
}
}
return false;
}
bool LRoomManager::dynamic_light_register_hint(Node * pLightNode, float radius, Node * pRoom)
{
// NYI
return true;
}
bool LRoomManager::dynamic_light_unregister(Node * pLightNode)
{
// NYI
return true;
}
int LRoomManager::dynamic_light_update(Node * pLightNode) // returns room within
{
if (!pLightNode)
{
WARN_PRINT_ONCE("dynamic_light_update : pLightNode is NULL");
return -1;
}
if (pLightNode->is_inside_tree() == false)
{
#ifdef LDEBUG_LIGHT_AFFECTED_ROOMS
if (m_bDebugFrameString)
DebugString_Add("DynamicLight not in tree\n");
#endif
return -1;
}
// find the light ID from meta data
int light_id = Meta_GetLightID(pLightNode);
if ((unsigned int) light_id >= (unsigned int) m_Lights.size())
{
WARN_PRINT_ONCE("dynamic_light_update : meta light ID out of range");
return -1;
}
LLight &light = m_Lights[light_id];
// update the llight transform from the node
Light * pGLight = light.GetGodotLight();
Light_UpdateTransform(light, *pGLight);
Spatial * pSpat = Object::cast_to<Spatial>(pGLight);
if (!pSpat)
return -1;
int iRoom = light.m_Source.m_RoomID;
if (iRoom == -1)
{
WARN_PRINT_ONCE("dynamic_light_update : can't update global light");
return -1;
}
LRoom * pRoom = GetRoom(iRoom);
if (pRoom == 0)
{
WARN_PRINT_ONCE("dynamic_light_update : pRoom is NULL");
return -1;
}
LRoom * pNewRoom = pRoom->DOB_Update(*this, pSpat);
if (pNewRoom)
{
// remove from the list in old room and add to list in new room, and change the metadata
int iNewRoomID = pNewRoom->m_RoomID;
light.m_Source.m_RoomID = iNewRoomID;
// change visibility
//DobChangeVisibility(pSpat, pRoom, pNewRoom);
}
// update with a new Trace (we are assuming update is only called if the light has moved)
// remove the old local lights
for (int n=0; n<light.m_NumAffectedRooms; n++)
{
int r = light.m_AffectedRooms[n];
GetRoom(r)->RemoveLocalLight(light_id);
}
light.ClearAffectedRooms();
// now do a new trace, and add all the rooms that are hit
m_Trace.Trace_Light(*this, light, LTrace::LR_ROOMS);
// we should now have a list of the rooms hit in m_LightRender.m_Temp_Visible_Rooms
for (int n=0; n<m_LightRender.m_Temp_Visible_Rooms.size(); n++)
{
int r = m_LightRender.m_Temp_Visible_Rooms[n];
// add to the list on the light
light.AddAffectedRoom(r);
// add to the list of local lights in the room
GetRoom(r)->AddLocalLight(light_id);
}
// this may or may not have changed
return light.m_Source.m_RoomID;
}
void LRoomManager::DebugString_Light_AffectedRooms(int light_id)
{
#ifdef LDEBUG_LIGHT_AFFECTED_ROOMS
if (m_bDebugFrameString)
{
const LLight &light = m_Lights[light_id];
if (!light.m_bShow)
return;
DebugString_Add("Light " + itos(light_id) + " affect room ");
// affected rooms
for (int n=0; n<light.m_NumAffectedRooms; n++)
{
int room_id = light.m_AffectedRooms[n];
DebugString_Add(itos(room_id) + ", ");
}
DebugString_Add("\n");
}
#endif
}
bool LRoomManager::light_register(Node * pLightNode, String szArea)
{
//CHECK_ROOM_LIST
if (!pLightNode)
{
WARN_PRINT_ONCE("light_register : pLightNode is NULL");
@ -617,7 +970,14 @@ bool LRoomManager::light_register(Node * pLightNode)
return false;
}
return LightCreate(pLight, -1);
DirectionalLight * pDLight = Object::cast_to<DirectionalLight>(pLightNode);
if (!pDLight)
{
WARN_PRINT_ONCE("light_register : only DirectionalLights are supported, place spotlights and omnis within rooms");
return false;
}
return LightCreate(pLight, -1, szArea);
}
@ -646,7 +1006,7 @@ void LRoomManager::DobChangeVisibility(Spatial * pDOB, const LRoom * pOld, const
int LRoomManager::dob_get_room_id(Node * pDOB)
{
return Obj_GetRoomNum(pDOB);
return Meta_GetRoomNum(pDOB);
}
// helpers to enable the client to manage switching on and off physics and AI
@ -688,51 +1048,6 @@ Node * LRoomManager::rooms_get_room(int room_id)
return pRoom->GetGodotRoom();
}
void LRoomManager::rooms_set_debug_lights(bool bActive)
{
m_bDebugLights = bActive;
Object * pObj = ObjectDB::get_instance(m_ID_DebugLights);
ImmediateGeometry * im = Object::cast_to<ImmediateGeometry>(pObj);
if (!im)
return;
if (bActive)
im->show();
else
im->hide();
}
void LRoomManager::rooms_set_debug_bounds(bool bActive)
{
m_bDebugBounds = bActive;
Object * pObj = ObjectDB::get_instance(m_ID_DebugBounds);
ImmediateGeometry * im = Object::cast_to<ImmediateGeometry>(pObj);
if (!im)
return;
if (bActive)
im->show();
else
im->hide();
}
void LRoomManager::rooms_set_debug_planes(bool bActive)
{
m_bDebugPlanes = bActive;
Object * pObj = ObjectDB::get_instance(m_ID_DebugPlanes);
ImmediateGeometry * im = Object::cast_to<ImmediateGeometry>(pObj);
if (!im)
return;
if (bActive)
im->show();
else
im->hide();
}
// move the initial hiding to where the camera is set, so we can save the scene etc
@ -748,7 +1063,7 @@ void LRoomManager::ShowAll(bool bShow)
for (int n=0; n<m_Lights.size(); n++)
{
LLight &light = m_Lights[n];
if (!light.IsGlobal())
if (!light.m_Source.IsGlobal())
light.Show(bShow);
}
@ -787,12 +1102,6 @@ void LRoomManager::rooms_set_active(bool bActive)
{
LSob &sob = m_SOBs[n];
sob.Show(!bActive);
// Spatial * pS = sob.GetSpatial();
// if (pS)
// if (!bActive)
// pS->show();
// else
// pS->hide();
VisualInstance * pVI = sob.GetVI();
if (pVI)
@ -809,6 +1118,12 @@ void LRoomManager::rooms_set_active(bool bActive)
}
String LRoomManager::rooms_get_debug_frame_string()
{
return m_szDebugString;
}
void LRoomManager::rooms_set_logging(int level)
{
// 0 is no logging, 6 is max logging (i.e. reverse of the priorities in the code)
@ -989,8 +1304,12 @@ void LRoomManager::ReleaseResources(bool bPrepareConvert)
m_LightCasters_SOB.clear();
m_Rooms.clear(true);
m_Portals.clear(true);
m_Areas.clear(true);
m_SOBs.clear();
m_AreaLights.clear(true);
m_AreaRooms.clear(true);
if (!bPrepareConvert)
m_Lights.clear();
@ -1018,6 +1337,13 @@ void LRoomManager::FrameUpdate_Prepare()
{
if (m_bDebugPlanes)
m_DebugPlanes.clear();
if (m_bDebugLightVolumes)
m_DebugLightVolumes.clear();
if (m_bDebugFrustums)
m_DebugFrustums.clear();
// clear the visible room list to write to each frame
m_pCurr_VisibleRoomList->clear();
@ -1040,6 +1366,7 @@ void LRoomManager::FrameUpdate_Prepare()
m_ActiveLights_prev.copy_from(m_ActiveLights);
m_ActiveLights.clear();
m_BF_ActiveLights.Blank();
m_BF_ProcessedLights.Blank();
// 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
@ -1057,6 +1384,8 @@ bool LRoomManager::FrameUpdate()
return false;
}
DebugString_Set("");
// could turn off internal processing? not that important
if (!m_bActive)
return false;
@ -1101,11 +1430,26 @@ bool LRoomManager::FrameUpdate()
return false;
}
// lcamera contains the info needed for culling
LCamera cam;
#ifdef LDEBUG_CAMERA
if (m_bDebugFrameString)
DebugString_Add("Camera in room " + itos(pRoom->m_RoomID) + "\n");
#endif
// lcamera contains the info needed for running the recursive trace using the main camera
LSource cam; cam.Source_SetDefaults();
cam.m_ptPos = Vector3(0, 0, 0);
cam.m_ptDir = Vector3 (-1, 0, 0);
// get the camera desired and make into lcamera
assert (pCamera);
Transform tr = pCamera->get_global_transform();
cam.m_ptPos = tr.origin;
cam.m_ptDir = -tr.basis.get_axis(2); // or possibly get_axis .. z is what we want
// if we can't prepare the frustum is invalid
if (!m_MainCamera.Prepare(*this, pCamera))
return false;
// 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...
@ -1116,18 +1460,16 @@ bool LRoomManager::FrameUpdate()
LVector<Plane> &planes = m_Pool.Get(pool_member);
planes.clear();
// get the camera desired and make into lcamera
assert (pCamera);
Transform tr = pCamera->get_global_transform();
cam.m_ptPos = tr.origin;
cam.m_ptDir = -tr.basis.get_axis(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());
planes.copy_from(m_MainCamera.m_Planes);
// 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);
m_Trace.Trace_Prepare(*this, cam, m_BF_visible_SOBs, m_BF_visible_rooms, m_VisibleList_SOBs, *m_pCurr_VisibleRoomList);
m_Trace.Trace_Begin(*pRoom, planes);
// we no longer need these planes
m_Pool.Free(pool_member);
// finally hide all the rooms that are currently visible but not in the visible bitfield as having been hit
FrameUpdate_FinalizeRooms();
@ -1221,7 +1563,12 @@ void LRoomManager::FrameUpdate_AddShadowCasters()
m_Rooms[r].AddShadowCasters(*this);
}
LPRINT(2, "TOTAL shadow casters " + itos(m_CasterList_SOBs.size()));
#ifdef LDEBUG_LIGHTS
if (m_bDebugFrameString)
DebugString_Add("TOTAL shadow casters " + itos(m_CasterList_SOBs.size()) + "\n");
#endif
LPRINT_RUN(2, "TOTAL shadow casters " + itos(m_CasterList_SOBs.size()));
}
void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow()
@ -1250,6 +1597,12 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow()
}
}
#ifdef LDEBUG_LIGHTS
if (m_bDebugFrameString)
DebugString_Add("nActiveLights " + itos(m_ActiveLights.size()) + "\n");
#endif
// lights
for (int n=0; n<m_ActiveLights.size(); n++)
{
@ -1272,10 +1625,15 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow()
//pLight->set_shadow(false);
//pLight->set_shadow(true);
//pLight->set_cull_mask(1 | LRoom::LAYER_MASK_LIGHT);
Vector3 ptBugFix = pLight->get_translation();
pLight->set_translation(ptBugFix);
// Vector3 ptBugFix = pLight->get_translation();
// pLight->set_translation(ptBugFix);
}
}
// debug
DebugString_Light_AffectedRooms(lid);
}
for (int n=0; n<m_ActiveLights_prev.size(); n++)
{
@ -1333,7 +1691,7 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_WithinRooms()
}
void LRoomManager::FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom)
void LRoomManager::FrameUpdate_DrawDebug(const LSource &cam, const LRoom &lroom)
{
// light portal planes
if (m_bDebugLights)
@ -1381,6 +1739,47 @@ void LRoomManager::FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom)
im->end();
}
if (m_bDebugLightVolumes)
{
Object * pObj = ObjectDB::get_instance(m_ID_DebugLightVolumes);
ImmediateGeometry * im = Object::cast_to<ImmediateGeometry>(pObj);
if (!im)
return;
im->clear();
im->begin(Mesh::PRIMITIVE_LINES, NULL);
int nVerts = m_DebugLightVolumes.size();
for (int n=0; n<nVerts; n++)
{
im->add_vertex(m_DebugLightVolumes[n]);
}
im->end();
}
if (m_bDebugFrustums)
{
Object * pObj = ObjectDB::get_instance(m_ID_DebugFrustums);
ImmediateGeometry * im = Object::cast_to<ImmediateGeometry>(pObj);
if (!im)
return;
im->clear();
im->begin(Mesh::PRIMITIVE_LINES, NULL);
int nVerts = m_DebugFrustums.size();
for (int n=0; n<nVerts; n++)
{
im->add_vertex(m_DebugFrustums[n]);
}
im->end();
}
// if debug bounds are on and there is a bound for this room
const Geometry::MeshData &md = lroom.m_Bound_MeshData;
if (m_bDebugBounds && md.faces.size())
@ -1461,6 +1860,11 @@ void LRoomManager::_bind_methods()
ClassDB::bind_method(D_METHOD("rooms_set_debug_planes", "active"), &LRoomManager::rooms_set_debug_planes);
ClassDB::bind_method(D_METHOD("rooms_set_debug_bounds", "active"), &LRoomManager::rooms_set_debug_bounds);
ClassDB::bind_method(D_METHOD("rooms_set_debug_lights", "active"), &LRoomManager::rooms_set_debug_lights);
ClassDB::bind_method(D_METHOD("rooms_set_debug_shadows", "active"), &LRoomManager::rooms_set_debug_shadows);
ClassDB::bind_method(D_METHOD("rooms_set_debug_frustums", "active"), &LRoomManager::rooms_set_debug_frustums);
ClassDB::bind_method(D_METHOD("rooms_set_debug_frame_string", "active"), &LRoomManager::rooms_set_debug_frame_string);
ClassDB::bind_method(D_METHOD("rooms_get_debug_frame_string"), &LRoomManager::rooms_get_debug_frame_string);
// lightmapping
ClassDB::bind_method(D_METHOD("rooms_convert_lightmap_internal", "proxy filename", "level filename"), &LRoomManager::rooms_convert_lightmap_internal);
@ -1479,7 +1883,12 @@ void LRoomManager::_bind_methods()
ClassDB::bind_method(D_METHOD("dob_get_room_id", "dob"), &LRoomManager::dob_get_room_id);
ClassDB::bind_method(D_METHOD("light_register", "light"), &LRoomManager::light_register);
ClassDB::bind_method(D_METHOD("light_register", "light", "area"), &LRoomManager::light_register);
ClassDB::bind_method(D_METHOD("dynamic_light_register", "light", "radius"), &LRoomManager::dynamic_light_register);
ClassDB::bind_method(D_METHOD("dynamic_light_register_hint", "light", "radius", "room"), &LRoomManager::dynamic_light_register_hint);
ClassDB::bind_method(D_METHOD("dynamic_light_unregister", "light"), &LRoomManager::dynamic_light_unregister);
ClassDB::bind_method(D_METHOD("dynamic_light_update", "light"), &LRoomManager::dynamic_light_update);
// helper
ClassDB::bind_method(D_METHOD("rooms_get_room", "room id"), &LRoomManager::rooms_get_room);
@ -1614,3 +2023,37 @@ void LRoomManager::ResolveRoomListPath()
_set_rooms(NULL);
}
/////////////////////////////////////////////////////////////////////////////////
// rooms_set_debug commands .. done as macros to make easier to change
// macros to combine to single identifier
//#define SCU_IDENT(x) x
#define COMB_NX(A, B) A##B
#define COMB_IDENT(A, B) COMB_NX(A,B)
#define IMPLEMENT_DEBUG_MESH(a, b) void LRoomManager::COMB_IDENT(rooms_set_debug_, a)(bool bActive)\
{\
COMB_IDENT(m_bDebug, b) = bActive;\
Object * pObj = ObjectDB::get_instance(COMB_IDENT(m_ID_Debug, b)); \
ImmediateGeometry * im = Object::cast_to<ImmediateGeometry>(pObj); \
if (!im) \
return; \
if (bActive) \
im->show(); \
else \
im->hide(); \
}
IMPLEMENT_DEBUG_MESH(shadows,LightVolumes)
IMPLEMENT_DEBUG_MESH(frustums,Frustums)
IMPLEMENT_DEBUG_MESH(lights,Lights)
IMPLEMENT_DEBUG_MESH(bounds,Bounds)
IMPLEMENT_DEBUG_MESH(planes,Planes)
#undef COMB_NX
#undef COMB_IDENT
#undef IMPLEMENT_DEBUG_MESH
void LRoomManager::rooms_set_debug_frame_string(bool bActive)
{
m_bDebugFrameString = bActive;
}

View File

@ -32,9 +32,9 @@
#include "lroom.h"
#include "lportal.h"
#include "larea.h"
#include "ltrace.h"
#include "lmain_camera.h"
class LRoomManager : public Spatial {
GDCLASS(LRoomManager, Spatial);
@ -42,7 +42,111 @@ class LRoomManager : public Spatial {
friend class LRoom;
friend class LRoomConverter;
friend class LHelper;
friend class LTrace;
friend class LMainCamera;
public:
// PUBLIC INTERFACE TO GDSCRIPT
//______________________________________________________________________________________
// Roomlist path
void set_rooms(const Object *p_rooms);
void _set_rooms(Object *p_rooms);
void set_rooms_path(const NodePath &p_path);
NodePath get_rooms_path() const;
void remove_rooms_path();
//______________________________________________________________________________________
// MAIN
// convert empties and meshes to rooms and portals
bool rooms_convert(bool bVerbose, bool bDeleteLights);
// free memory for current set of rooms, prepare for converting a new game level
void rooms_release();
// choose which camera you want to use to determine visibility.
// normally this will be your main camera, but you can choose another for debugging
bool rooms_set_camera(Node * pCam);
// get the Godot room that is associated with an LPortal room
// (can be used to find the name etc of a room ID returned by dob_update)
Node * rooms_get_room(int room_id);
//______________________________________________________________________________________
// DOBS
// 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.
bool dob_register(Node * pDOB, float radius);
// register but let LPortal know which room the dob should start in
bool dob_register_hint(Node * pDOB, float radius, Node * pRoom);
bool dob_unregister(Node * pDOB);
// returns the room ID within
int dob_update(Node * pDOB);
// if we are moving the DOB possibly through multiple rooms, then teleport rather than detect
// portal crossings
bool dob_teleport(Node * pDOB);
bool dob_teleport_hint(Node * pDOB, Node * pRoom);
//______________________________________________________________________________________
// LIGHTS
// global directional lights that will apply to all rooms
bool light_register(Node * pLightNode, String szArea);
// dynamic lights (spot or omni within rooms)
bool dynamic_light_register(Node * pLightNode, float radius);
bool dynamic_light_register_hint(Node * pLightNode, float radius, Node * pRoom);
bool dynamic_light_unregister(Node * pLightNode);
int dynamic_light_update(Node * pLightNode); // returns room within
//______________________________________________________________________________________
// LIGHTMAPS
// helper function to merge SOB meshes for producing lightmaps VIA external blender workflow
bool rooms_merge_sobs(Node * pMergeMeshInstance);
bool rooms_unmerge_sobs(Node * pMergeMeshInstance);
bool rooms_transfer_uv2s(Node * pMeshInstance_From, Node * pMeshInstance_To);
// one function to do all the uv mapping and lightmap creation in one
// (for godot lightmap workflow)
MeshInstance * rooms_convert_lightmap_internal(String szProxyFilename, String szLevelFilename);
//______________________________________________________________________________________
// HELPERS
// helper function for general use .. LPortal has the functionality, why not...
bool rooms_save_scene(Node * pNode, String szFilename);
// helpers to enable the client to manage switching on and off physics and AI
int rooms_get_num_rooms() const;
bool rooms_is_room_visible(int room_id) const;
Array rooms_get_visible_rooms() const;
// helper func, not needed usually as dob_update returns the room
int dob_get_room_id(Node * pDOB);
//______________________________________________________________________________________
// DEBUGGING
// turn on and off culling for debugging
void rooms_set_active(bool bActive);
void rooms_set_debug_planes(bool bActive);
void rooms_set_debug_bounds(bool bActive);
void rooms_set_debug_lights(bool bActive);
void rooms_set_debug_shadows(bool bActive);
void rooms_set_debug_frustums(bool bActive);
void rooms_set_debug_frame_string(bool bActive);
// 0 to 6 .. less to more
// defaults to 4 which is (2) in our priorities (i.e. 6 - level)
void rooms_set_logging(int level);
// optionally lportal can output some debug info in a string each frame
String rooms_get_debug_frame_string();
// provide debugging output on the next frame
void rooms_log_frame();
private:
// PER FRAME STUFF
// godot ID of the camera (which should be registered as a DOB to allow moving between rooms)
ObjectID m_ID_camera;
@ -74,10 +178,105 @@ class LRoomManager : public Spatial {
LVector<int> * m_pCurr_VisibleRoomList;
LVector<int> * m_pPrev_VisibleRoomList;
// active lights
LVector<int> m_ActiveLights;
LVector<int> m_ActiveLights_prev;
Lawn::LBitField_Dynamic m_BF_ActiveLights;
Lawn::LBitField_Dynamic m_BF_ActiveLights_prev;
// some lights may be processed on a frame but found not to intersect the view frustum
Lawn::LBitField_Dynamic m_BF_ProcessedLights;
// keep all the light rendering stuff together
struct LLightRender
{
// each time we render from a light point of view, we reuse this list to store each caster ID
Lawn::LBitField_Dynamic m_BF_Temp_SOBs;
Lawn::LBitField_Dynamic m_BF_Temp_Visible_Rooms;
LVector<int> m_Temp_Visible_SOBs;
LVector<int> m_Temp_Visible_Rooms;
} m_LightRender;
// keep a frame counter, to mark when objects have been hit by the visiblity algorithm
// already to prevent multiple hits on rooms and objects
unsigned int m_uiFrameCounter;
private:
// lists of rooms and portals, contiguous list so cache friendly
LVector<LRoom> m_Rooms;
LVector<LPortal> m_Portals;
LVector<LArea> m_Areas;
// static objects
LVector<LSob> m_SOBs;
// lights
LVector<LLight> m_Lights;
// SHADOWS
// master list of shadow casters for each room
LVector<uint32_t> m_ShadowCasters_SOB; // not used any more?
// master list of casters for each light (precalculated list)
LVector<uint32_t> m_LightCasters_SOB;
// AREAS
// master list of lights affecting each area
LVector<uint32_t> m_AreaLights;
// master list of rooms in each area
LVector<uint32_t> m_AreaRooms;
// 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;
public:
// whether debug planes is switched on
bool m_bDebugPlanes;
bool m_bDebugBounds;
bool m_bDebugLights;
bool m_bDebugLightVolumes;
bool m_bDebugFrustums;
// the planes are shown as a list of lines from the camera to the portal verts
LVector<Vector3> m_DebugPlanes;
LVector<Vector3> m_DebugPortalLightPlanes;
LVector<Vector3> m_DebugLightVolumes;
LVector<Vector3> m_DebugFrustums;
// we are now referencing the rooms indirectly via a nodepath rather than directly being children
// of the LRoomManager node
NodePath m_path_RoomList;
ObjectID m_ID_RoomList;
private:
LTrace m_Trace;
// unchecked
Spatial * m_pRoomList;
LMainCamera m_MainCamera;
// DEBUGGING
// 0 to 5
int m_iLoggingLevel;
ObjectID m_ID_DebugPlanes;
ObjectID m_ID_DebugBounds;
ObjectID m_ID_DebugLights;
ObjectID m_ID_DebugLightVolumes;
ObjectID m_ID_DebugFrustums;
Ref<SpatialMaterial> m_mat_Debug_Planes;
Ref<SpatialMaterial> m_mat_Debug_Bounds;
Ref<SpatialMaterial> m_mat_Debug_LightVolumes;
Ref<SpatialMaterial> m_mat_Debug_Frustums;
String m_szDebugString;
bool m_bDebugFrameString;
// for debugging, can turn LPortal on and off
bool m_bActive;
@ -85,38 +284,7 @@ class LRoomManager : public Spatial {
bool m_bFrustumOnly;
private:
// lists of rooms and portals, contiguous list so cache friendly
LVector<LRoom> m_Rooms;
LVector<LPortal> m_Portals;
// static objects
LVector<LSob> m_SOBs;
// lights
LVector<LLight> m_Lights;
// active lights
LVector<int> m_ActiveLights;
LVector<int> m_ActiveLights_prev;
Lawn::LBitField_Dynamic m_BF_ActiveLights;
Lawn::LBitField_Dynamic m_BF_ActiveLights_prev;
// master list of shadow casters for each room
LVector<uint32_t> m_ShadowCasters_SOB;
// master list of casters for each light
LVector<uint32_t> m_LightCasters_SOB;
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;
// 0 to 5
int m_iLoggingLevel;
private:
// PRIVATE FUNCS
// this is where we do all the culling
bool FrameUpdate();
void FrameUpdate_Prepare();
@ -130,19 +298,30 @@ private:
void FrameUpdate_FrustumOnly();
// draw planes and room hulls
void FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom);
void FrameUpdate_DrawDebug(const LSource &cam, const LRoom &lroom);
// internal
LRoom &Portal_GetLinkedRoom(const LPortal &port);
// dobs
bool DobRegister(Spatial * pDOB, float radius, int iRoom);
ObjectID DobRegister_FindVIRecursive(Node * pNode) const;
bool DobTeleport(Spatial * pDOB, int iNewRoomID);
bool LightCreate(Light * pLight, int roomID);
void CreateDebug();
void DobChangeVisibility(Spatial * pDOB, const LRoom * pOld, const LRoom * pNew);
void CreateDebug();
void ReleaseResources(bool bPrepareConvert);
void ShowAll(bool bShow);
void ResolveRoomListPath();
// frame debug string
void DebugString_Set(String sz) {m_szDebugString = sz;}
void DebugString_Add(String sz) {m_szDebugString += sz;}
void DebugString_Light_AffectedRooms(int light_id);
// now we are centralizing the tracing out from static and dynamic lights for each frame to this function
bool LightCreate(Light * pLight, int roomID, String szArea = "");
void Light_UpdateTransform(LLight &light, const Light &glight) const;
void Light_FrameProcess(int lightID);
bool Light_FindCasters(int lightID);
// helper funcs
@ -152,37 +331,16 @@ private:
LRoom * GetRoomFromDOB(Node * pNode);
int FindClosestRoom(const Vector3 &pt) const;
LRoom &Portal_GetLinkedRoom(const LPortal &port);
// 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);
int Meta_GetRoomNum(Node * pNode) const;
void Meta_SetRoomNum(Node * pNode, int num);
public:
// whether debug planes is switched on
bool m_bDebugPlanes;
bool m_bDebugBounds;
bool m_bDebugLights;
// the planes are shown as a list of lines from the camera to the portal verts
LVector<Vector3> m_DebugPlanes;
LVector<Vector3> m_DebugPortalLightPlanes;
// we are now referencing the rooms indirectly via a nodepath rather than directly being children
// of the LRoomManager node
NodePath m_path_RoomList;
ObjectID m_ID_RoomList;
private:
ObjectID m_ID_DebugPlanes;
ObjectID m_ID_DebugBounds;
ObjectID m_ID_DebugLights;
Ref<SpatialMaterial> m_mat_Debug_Planes;
Ref<SpatialMaterial> m_mat_Debug_Bounds;
// unchecked
Spatial * m_pRoomList;
void ResolveRoomListPath();
// for lights we store the light ID in the metadata
void Meta_SetLightID(Node * pNode, int id);
int Meta_GetLightID(Node * pNode) const;
public:
// makes sure m_pRoomList is up to date and valid
@ -191,86 +349,13 @@ public:
Spatial * GetRoomList_Checked();
// unchecked, be sure to call checked version first which will set m_pRoomList
Spatial * GetRoomList() const {return m_pRoomList;}
protected:
static void _bind_methods();
void _notification(int p_what);
public:
LRoomManager();
// PUBLIC INTERFACE TO GDSCRIPT
void set_rooms(const Object *p_rooms);
void _set_rooms(Object *p_rooms);
void set_rooms_path(const NodePath &p_path);
NodePath get_rooms_path() const;
void remove_rooms_path();
// convert empties and meshes to rooms and portals
bool rooms_convert(bool bVerbose, bool bDeleteLights);
// free memory for current set of rooms, prepare for converting a new game level
void rooms_release();
// choose which camera you want to use to determine visibility.
// normally this will be your main camera, but you can choose another for debugging
bool rooms_set_camera(Node * pCam);
// get the Godot room that is associated with an LPortal room
// (can be used to find the name etc of a room ID returned by dob_update)
Node * rooms_get_room(int room_id);
// helpers to enable the client to manage switching on and off physics and AI
int rooms_get_num_rooms() const;
bool rooms_is_room_visible(int room_id) const;
Array rooms_get_visible_rooms() const;
// helper function to merge SOB meshes for producing lightmaps VIA external blender workflow
bool rooms_merge_sobs(Node * pMergeMeshInstance);
bool rooms_unmerge_sobs(Node * pMergeMeshInstance);
bool rooms_transfer_uv2s(Node * pMeshInstance_From, Node * pMeshInstance_To);
// one function to do all the uv mapping and lightmap creation in one
// (for godot lightmap workflow)
MeshInstance * rooms_convert_lightmap_internal(String szProxyFilename, String szLevelFilename);
// helper function for general use .. LPortal has the functionality, why not...
bool rooms_save_scene(Node * pNode, String szFilename);
// turn on and off culling for debugging
void rooms_set_active(bool bActive);
void rooms_set_debug_planes(bool bActive);
void rooms_set_debug_bounds(bool bActive);
void rooms_set_debug_lights(bool bActive);
// 0 to 6 .. defaults to 4 which is (2) in our priorities (i.e. 6 - level)
void rooms_set_logging(int level);
// provide debugging output on the next frame
void rooms_log_frame();
// 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.
bool dob_register(Node * pDOB, float radius);
// register but let LPortal know which room the dob should start in
bool dob_register_hint(Node * pDOB, float radius, Node * pRoom);
bool dob_unregister(Node * pDOB);
// returns the room ID within
int dob_update(Node * pDOB);
// if we are moving the DOB possibly through multiple rooms, then teleport rather than detect
// portal crossings
bool dob_teleport(Node * pDOB);
bool dob_teleport_hint(Node * pDOB, Node * pRoom);
// helper func, not needed usually as dob_update returns the room
int dob_get_room_id(Node * pDOB);
// LIGHTS
// global lights that will apply to all rooms
bool light_register(Node * pLightNode);
};
#endif

606
ltrace.cpp Normal file
View File

@ -0,0 +1,606 @@
// 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 "ltrace.h"
#include "ldebug.h"
#include "ldob.h"
#include "lportal.h"
#include "lbitfield_dynamic.h"
#include "lroom_manager.h"
#define LMAN m_pManager
//void LTrace::Trace_Prepare(LRoomManager &manager, const LCamera &cam, Lawn::LBitField_Dynamic &BF_SOBs, Lawn::LBitField_Dynamic &BF_DOBs, Lawn::LBitField_Dynamic &BF_Rooms, LVector<int> &visible_SOBs, LVector<int> &visible_DOBs, LVector<int> &visible_Rooms)
void LTrace::Trace_Prepare(LRoomManager &manager, const LSource &cam, Lawn::LBitField_Dynamic &BF_SOBs, Lawn::LBitField_Dynamic &BF_Rooms, LVector<int> &visible_SOBs, LVector<int> &visible_Rooms)
{
m_pManager = &manager;
m_pCamera = &cam;
// default
m_TraceFlags = CULL_SOBS | CULL_DOBS | TOUCH_ROOMS | MAKE_ROOM_VISIBLE;
m_pBF_SOBs = &BF_SOBs;
// m_pBF_DOBs = &BF_DOBs;
m_pBF_Rooms = &BF_Rooms;
m_pVisible_SOBs = &visible_SOBs;
// m_pVisible_DOBs = &visible_DOBs;
m_pVisible_Rooms = &visible_Rooms;
}
void LTrace::CullSOBs(LRoom &room, const LVector<Plane> &planes)
{
// clip all objects in this room to the clipping planes
int last_sob = room.m_iFirstSOB + room.m_iNumSOBs;
for (int n=room.m_iFirstSOB; n<last_sob; n++)
{
LSob &sob = LMAN->m_SOBs[n];
//LPRINT_RUN(2, "sob " + itos(n) + " " + sob.GetSpatial()->get_name());
// already determined to be visible through another portal
if (m_pBF_SOBs->GetBit(n))
{
//LPRINT_RUN(2, "\talready visible");
continue;
}
bool bShow = true;
// estimate the radius .. for now
const AABB &bb = sob.m_aabb;
// print("\t\t\tculling object " + pObj->get_name());
for (int p=0; p<planes.size(); p++)
{
// float dist = planes[p].distance_to(pt);
// print("\t\t\t\t" + itos(p) + " : dist " + String(Variant(dist)));
float r_min, r_max;
bb.project_range_in_plane(planes[p], r_min, r_max);
// print("\t\t\t\t" + itos(p) + " : r_min " + String(Variant(r_min)) + ", r_max " + String(Variant(r_max)));
if (r_min > 0.0f)
{
//LPRINT_RUN(2, "\tout of view");
bShow = false;
break;
}
}
if (bShow)
{
// sob is renderable and visible (not shadow only)
//LPRINT_RUN(2, "\tin view");
m_pBF_SOBs->SetBit(n, true);
m_pVisible_SOBs->push_back(n);
}
} // for through sobs
}
void LTrace::CullDOBs(LRoom &room, const LVector<Plane> &planes)
{
// NYI this isn't efficient, there may be more than 1 portal to the same room
// cull DOBs
int nDOBs = room.m_DOBs.size();
for (int n=0; n<nDOBs; n++)
{
LDob &dob = room.m_DOBs[n];
Spatial * pObj = dob.GetSpatial();
if (pObj)
{
bool bShow = true;
const Vector3 &pt = pObj->get_global_transform().origin;
float radius = dob.m_fRadius;
for (int p=0; p<planes.size(); p++)
{
float dist = planes[p].distance_to(pt);
//print("\t\t\t\t" + itos(p) + " : dist " + String(Variant(dist)));
if (dist > radius)
{
bShow = false;
break;
}
}
if (bShow)
{
LPRINT_RUN(1, "\tDOB " + pObj->get_name() + " visible");
dob.m_bVisible = true;
}
else
{
LPRINT_RUN(1, "\tDOB " + pObj->get_name() + " culled");
}
}
} // for through dobs
}
bool LTrace::Trace_Light(LRoomManager &manager, const LLight &light, eLightRun eRun)
{
m_pManager = &manager;
LRoom * pRoom;
// non area light
if (light.m_iArea == -1)
{
// can only deal with lights in rooms for now
if (light.m_Source.m_RoomID == -1)
{
WARN_PRINT_ONCE("LTrace::Trace_Light can only trace lights in rooms");
return true;
}
pRoom = manager.GetRoom(light.m_Source.m_RoomID);
if (!pRoom)
return true;
}
else
{
// area light
pRoom = 0;
}
const LSource &cam = light.m_Source;
unsigned int pool_member = manager.m_Pool.Request();
assert (pool_member != -1);
LVector<Plane> &planes = manager.m_Pool.Get(pool_member);
planes.clear();
// we now need to trace either just DOBs (in the case of static lights)
// or SOBs and DOBs (in the case of dynamic lights)
LRoomManager::LLightRender &lr = manager.m_LightRender;
lr.m_BF_Temp_SOBs.Blank();
lr.m_Temp_Visible_SOBs.clear();
lr.m_BF_Temp_Visible_Rooms.Blank();
lr.m_Temp_Visible_Rooms.clear();
bool bLightInView = true;
switch (eRun)
{
// finding all shadow casters at runtime
case LR_ALL:
{
//Trace_Prepare(manager, cam, lr.m_BF_Temp_SOBs, manager.m_BF_visible_rooms, lr.m_Temp_Visible_SOBs, *manager.m_pCurr_VisibleRoomList);
Trace_Prepare(manager, cam, lr.m_BF_Temp_SOBs, lr.m_BF_Temp_Visible_Rooms, lr.m_Temp_Visible_SOBs, lr.m_Temp_Visible_Rooms);
Trace_SetFlags(CULL_SOBS | CULL_DOBS | MAKE_ROOM_VISIBLE);
// create subset planes of light frustum and camera frustum
bLightInView = manager.m_MainCamera.AddCameraLightPlanes(manager, cam, planes);
}
break;
// finding only visible rooms at runtime
case LR_ROOMS:
{
Trace_Prepare(manager, cam, lr.m_BF_Temp_SOBs, lr.m_BF_Temp_Visible_Rooms, lr.m_Temp_Visible_SOBs, lr.m_Temp_Visible_Rooms);
// we ONLY want a list of rooms hit
Trace_SetFlags(MAKE_ROOM_VISIBLE);
}
break;
// finding all in preconversion
case LR_CONVERT:
{
Trace_Prepare(manager, cam, lr.m_BF_Temp_SOBs, lr.m_BF_Temp_Visible_Rooms, lr.m_Temp_Visible_SOBs, lr.m_Temp_Visible_Rooms);
// we want sobs but not to touch rooms
m_TraceFlags = CULL_SOBS | MAKE_ROOM_VISIBLE; // | CULL_DOBS | TOUCH_ROOMS;
}
break;
}
if (bLightInView)
{
// non area light
if (pRoom)
{
Trace_Begin(*pRoom, planes);
}
else
{
// area light
// area lights don't go through portals, e.g. coming from above like sunlight
// they instead have a predefined list of rooms governed by the area
m_TraceFlags |= DONT_TRACE_PORTALS;
// new .. trace according to area, not affected rooms, as affected rooms has a limit
assert (light.m_iArea != -1);
const LArea &area = LMAN->m_Areas[light.m_iArea];
int last_room = area.m_iFirstRoom + area.m_iNumRooms;
for (int r=area.m_iFirstRoom; r<last_room; r++)
{
int room_id = LMAN->m_AreaRooms[r];
LRoom * pRoom = manager.GetRoom(room_id);
// should not happen, assert?
assert (pRoom);
// trace as usual but don't go through the portals
Trace_Recursive(0, *pRoom, planes, 0);
}
/*
// go through each affected room
for (int r=0; r<light.m_NumAffectedRooms; r++)
{
int room_id = light.m_AffectedRooms[r];
LRoom * pRoom = manager.GetRoom(room_id);
// should not happen, assert?
assert (pRoom);
// trace as usual but don't go through the portals
Trace_Recursive(0, *pRoom, planes, 0);
}
*/
} // if area light
} // if light in view
// we no longer need these planes
manager.m_Pool.Free(pool_member);
return bLightInView;
}
void LTrace::AddSpotlightPlanes(LVector<Plane> &planes) const
{
Plane p(m_pCamera->m_ptPos, -m_pCamera->m_ptDir);
planes.push_back(p);
// this is kinda crappy, because ideally we'd want a cone, but instead we'll fake a frustum
Vector3 pts[4];
// assuming here that d is normalized!
const Vector3 &d = m_pCamera->m_ptDir;
const Vector3 &ptCam = m_pCamera->m_ptPos;
assert (d.length_squared() < 1.1f);
assert (d.length_squared() > 0.9f);
// spotlight has no 'up' vector, as it is regular shape around direction axis
// so we can use anything for side vector
// this might balls up with a light pointing straight up
Vector3 ptSide = Vector3(0, 1, 0).cross(d);
float l = ptSide.length();
if (l < 0.1f)
{
// special case straight up, lets cross against something else
ptSide = d.cross(Vector3(1, 0, 0));
l = ptSide.length();
assert (l);
}
// unitize side
ptSide *= 1.0 / l;
Vector3 ptUp = ptSide.cross(d);
ptUp.normalize();
// now we've got the vecs, lets create some planes
// spotlight spread definition (light.cpp, line 146)
//float size = Math::tan(Math::deg2rad(param[PARAM_SPOT_ANGLE])) * len;
// this is the size at distance 1 .. it would be more efficient to calc distance at which sides were 1, but whatever...
float size = Math::tan(Math::deg2rad(m_pCamera->m_fSpread));
ptSide *= size; // or half size? not sure yet
ptUp *= -size;
// pts will be bot left, top left, top right, bot right
Vector3 ptEx = ptCam + d;
pts[0] = ptEx - ptSide - ptUp;
pts[1] = ptEx - ptSide + ptUp;
pts[2] = ptEx + ptSide + ptUp;
pts[3] = ptEx + ptSide - ptUp;
Plane left(ptCam, pts[0], pts[1], COUNTERCLOCKWISE);
Plane top(ptCam, pts[1], pts[2], COUNTERCLOCKWISE);
Plane right(ptCam, pts[2], pts[3], COUNTERCLOCKWISE);
Plane bottom(ptCam, pts[3], pts[0], COUNTERCLOCKWISE);
planes.push_back(left);
planes.push_back(top);
planes.push_back(right);
planes.push_back(bottom);
// debug
if (LMAN->m_bDebugFrustums)
{
for (int n=0; n<4; n++)
{
LMAN->m_DebugFrustums.push_back(ptCam);
LMAN->m_DebugFrustums.push_back(pts[n]);
}
}
}
void LTrace::Trace_Begin(LRoom &room, LVector<Plane> &planes)
{
int first_plane = 0;
switch (m_pCamera->m_eType)
{
case LSource::ST_SPOTLIGHT:
{
// special cases of spotlight, add some extra planes to define the cone
AddSpotlightPlanes(planes);
}
break;
case LSource::ST_CAMERA:
first_plane = 1;
break;
default:
break;
}
LPRINT_RUN(2, "TRACE BEGIN");
LPRINT_RUN(2, m_pCamera->MakeDebugString());
Trace_Recursive(0, room, planes, first_plane);
}
void LTrace::Trace_Recursive(int depth, LRoom &room, const LVector<Plane> &planes, int first_portal_plane)
{
// prevent too much depth
if (depth > 8)
{
LPRINT_RUN(2, "\t\t\tDEPTH LIMIT REACHED");
WARN_PRINT_ONCE("LPortal Depth Limit reached (seeing through > 8 portals)");
return;
}
// for debugging
Lawn::LDebug::m_iTabDepth = depth;
LPRINT_RUN(2, "");
LPRINT_RUN(2, "ROOM '" + itos(room.m_RoomID) + " : " + room.get_name() + "' planes " + itos(planes.size()) + " portals " + itos(room.m_iNumPortals) );
// only handle one touch per frame so far (one portal into room)
//assert (manager.m_uiFrameCounter > m_uiFrameTouched);
// first touch
DetectFirstTouch(room);
if (m_TraceFlags & CULL_SOBS)
CullSOBs(room, planes);
if (m_TraceFlags & CULL_DOBS)
CullDOBs(room, planes);
// portals
if (m_TraceFlags & DONT_TRACE_PORTALS)
return;
// look through portals
int nPortals = room.m_iNumPortals;
for (int port_num=0; port_num<nPortals; port_num++)
{
int port_id = room.m_iFirstPortal + port_num;
const LPortal &port = LMAN->m_Portals[port_id];
// have we already handled the room on this frame?
// get the room pointed to by the portal
LRoom * pLinkedRoom = &LMAN->Portal_GetLinkedRoom(port);
// cull by portal angle to camera.
// NEW! I've come up with a much better way of culling portals by direction to camera...
// instead of using dot product with a varying view direction, we simply find which side of the portal
// plane the camera is on! If it is behind, the portal can be seen through, if in front, it can't! :)
float dist_cam = port.m_Plane.distance_to(m_pCamera->m_ptPos);
LPRINT_RUN(2, "\tPORTAL " + itos (port_num) + " (" + itos(port_id) + ") " + port.get_name());
if (dist_cam > 0.0f)
{
LPRINT_RUN(2, "\t\tCULLED (back facing)");
continue;
}
/*
// Note we need to deal with 'side on' portals, and the camera has a spreading view, so we cannot simply dot
// the portal normal with camera direction, we need to take into account angle to the portal itself.
const Vector3 &portal_normal = port.m_Plane.normal;
LPRINT_RUN(2, "\tPORTAL " + itos (port_num) + " (" + itos(port_id) + ") " + port.get_name() + " normal " + portal_normal);
// we will dot the portal angle with a ray from the camera to the portal centre
// (there might be an even better ray direction but this will do for now)
Vector3 dir_portal = port.m_ptCentre - m_pCamera->m_ptPos;
// doesn't actually need to be normalized?
float dot = dir_portal.dot(portal_normal);
if (dot <= -0.0f) // 0.0
{
//LPRINT_RUN(2, "\t\tCULLED (wrong direction) dot is " + String(Variant(dot)) + ", dir_portal is " + dir_portal);
LPRINT_RUN(2, "\t\tCULLED (wrong direction)");
continue;
}
*/
// is it culled by the planes?
LPortal::eClipResult overall_res = LPortal::eClipResult::CLIP_INSIDE;
// while clipping to the planes we maintain a list of partial planes, so we can add them to the
// recursive next iteration of planes to check
static LVector<int> partial_planes;
partial_planes.clear();
// for portals, we want to ignore the near clipping plane, as we might be right on the edge of a doorway
// and still want to look through the portal.
// So we are starting this loop from 1, ASSUMING that plane zero is the near clipping plane.
// If it isn't we would need a different strategy
// Note that now this only occurs for the first portal out of the current room. After that,
// 0 is passed as first_portal_plane, because the near plane will probably be irrelevant,
// and we are now not necessarily copying the camera planes.
for (int l=first_portal_plane; l<planes.size(); l++)
{
LPortal::eClipResult res = port.ClipWithPlane(planes[l]);
switch (res)
{
case LPortal::eClipResult::CLIP_OUTSIDE:
overall_res = res;
break;
case LPortal::eClipResult::CLIP_PARTIAL:
overall_res = res;
partial_planes.push_back(l);
break;
default: // suppress warning
break;
}
if (overall_res == LPortal::eClipResult::CLIP_OUTSIDE)
break;
}
// this portal is culled
if (overall_res == LPortal::eClipResult::CLIP_OUTSIDE)
{
LPRINT_RUN(2, "\t\tCULLED (outside planes)");
continue;
}
// else recurse into that portal
unsigned int uiPoolMem = LMAN->m_Pool.Request();
if (uiPoolMem != -1)
{
// get a vector of planes from the pool
LVector<Plane> &new_planes = LMAN->m_Pool.Get(uiPoolMem);
new_planes.clear();
// NEW!! if portal is totally inside the planes, don't copy the old planes
if (overall_res != LPortal::eClipResult::CLIP_INSIDE)
{
// copy the existing planes
//new_planes.copy_from(planes);
// new .. only copy the partial planes that the portal cuts through
for (int n=0; n<partial_planes.size(); n++)
new_planes.push_back(planes[partial_planes[n]]);
}
// add the planes for the portal
// NOTE that we can also optimize by not adding portal planes for edges that
// were behind a partial plane. NYI
port.AddPlanes(*LMAN, m_pCamera->m_ptPos, new_planes);
if (pLinkedRoom)
{
Trace_Recursive(depth+1, *pLinkedRoom, new_planes, 0);
//pLinkedRoom->DetermineVisibility_Recursive(manager, depth + 1, cam, new_planes, 0);
// for debugging need to reset tab depth
Lawn::LDebug::m_iTabDepth = depth;
}
// we no longer need these planes
LMAN->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");
}
} // for p through portals
}
void LTrace::DetectFirstTouch(LRoom &room)
{
// mark if not reached yet on this trace
if (!m_pBF_Rooms->GetBit(room.m_RoomID))
{
m_pBF_Rooms->SetBit(room.m_RoomID, true);
if (m_TraceFlags & MAKE_ROOM_VISIBLE)
{
// keep track of which rooms are shown this trace
m_pVisible_Rooms->push_back(room.m_RoomID);
}
// camera and light traces
if (m_TraceFlags & TOUCH_ROOMS)
{
if (room.m_uiFrameTouched < LMAN->m_uiFrameCounter)
FirstTouch(room);
}
}
}
void LTrace::FirstTouch(LRoom &room)
{
// set the frame counter
room.m_uiFrameTouched = LMAN->m_uiFrameCounter;
// show this room and add to visible list of rooms
room.Room_MakeVisible(true);
// m_pBF_Rooms->SetBit(room.m_RoomID, true);
// keep track of which rooms are shown this frame
// m_pVisible_Rooms->push_back(room.m_RoomID);
// hide all dobs
for (int n=0; n<room.m_DOBs.size(); n++)
room.m_DOBs[n].m_bVisible = false;
}

81
ltrace.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
// 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 "lvector.h"
class LSource;
class LRoomManager;
class LRoom;
class LLight;
namespace Lawn {class LBitField_Dynamic;}
class LTrace
{
public:
enum eTraceFlags
{
CULL_SOBS = 1 << 0,
CULL_DOBS = 1 << 1,
TOUCH_ROOMS = 1 << 2,
MAKE_ROOM_VISIBLE = 1 << 3,
DONT_TRACE_PORTALS = 1 << 4,
};
enum eLightRun
{
LR_ALL, // runtime find all shadow casters
LR_ROOMS, // find affected rooms
LR_CONVERT, // initial conversion
};
void Trace_Prepare(LRoomManager &manager, const LSource &cam, Lawn::LBitField_Dynamic &BF_SOBs, Lawn::LBitField_Dynamic &BF_Rooms, LVector<int> &visible_SOBs, LVector<int> &visible_Rooms);
// void Trace_Prepare(LRoomManager &manager, const LCamera &cam, Lawn::LBitField_Dynamic &BF_SOBs, Lawn::LBitField_Dynamic &BF_DOBs, Lawn::LBitField_Dynamic &BF_Rooms, LVector<int> &visible_SOBs, LVector<int> &visible_DOBs, LVector<int> &visible_Rooms);
void Trace_SetFlags(unsigned int flags) {m_TraceFlags = flags;}
void Trace_Begin(LRoom &room, LVector<Plane> &planes);
// simpler method of doing a trace for lights, no need to call prepare and begin
bool Trace_Light(LRoomManager &manager, const LLight &light, eLightRun eRun);
private:
void AddSpotlightPlanes(LVector<Plane> &planes) const;
void Trace_Recursive(int depth, LRoom &room, const LVector<Plane> &planes, int first_portal_plane);
void CullSOBs(LRoom &room, const LVector<Plane> &planes);
void CullDOBs(LRoom &room, const LVector<Plane> &planes);
void FirstTouch(LRoom &room);
void DetectFirstTouch(LRoom &room);
LRoomManager * m_pManager;
const LSource * m_pCamera;
Lawn::LBitField_Dynamic * m_pBF_SOBs;
//Lawn::LBitField_Dynamic * m_pBF_DOBs;
Lawn::LBitField_Dynamic * m_pBF_Rooms;
LVector<int> * m_pVisible_SOBs;
//LVector<int> * m_pVisible_DOBs;
LVector<int> * m_pVisible_Rooms;
unsigned int m_TraceFlags;
};

View File

@ -163,6 +163,23 @@ public:
}
}
void insert(int i, const T &val)
{
m_Vec.insert(m_Vec.begin() + i, val);
m_iSize++;
}
int find(const T &val)
{
for (int n=0; n<size(); n++)
{
if (m_Vec[n] == val)
return n;
}
return -1; // not found
}
LVector()
{
m_iSize = 0;