Backup while investigate Godot light cull bug

This commit is contained in:
lawnjelly 2019-10-01 15:27:32 +01:00 committed by GitHub
parent 1e9152c69f
commit 0f8094e9f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 805 additions and 74 deletions

View File

@ -10,8 +10,7 @@ Video of initial testing:\
https://www.youtube.com/watch?v=xF_3Fe2HRdk \
https://www.youtube.com/watch?v=NmlWkkhGoJA
_Feel free to leave suggestions / feature requests on the issue tracker, especially regarding ease of use._\
_If you are interested in integrating LPortal into your game, feel free to get in touch, e.g. in the issue tracker, and I will endeavour to help._
_Feel free to leave suggestions / feature requests on the issue tracker, especially regarding ease of use._
## Current status
The basic system is mostly working. I am now testing / polishing the interface and adding debugging. I will leave PVS and closable portals until after the first release.

View File

@ -23,6 +23,25 @@
#include "lroom.h"
#include "ldebug.h"
#include "lroom_manager.h"
#include "scene/3d/light.h"
void LLight::SetDefaults()
{
m_GodotID = 0;
m_eType = LT_DIRECTIONAL;
m_fSpread = 0.0f; // for spotlight
m_fMaxDist = 100.0f;
m_RoomID = -1;
}
Light * LLight::GetGodotLight()
{
Object * pObj = ObjectDB::get_instance(m_GodotID);
Light * p = Object::cast_to<Light>(pObj);
return p;
}
bool LPortal::NameStartsWith(Node * pNode, String szSearch)
@ -71,38 +90,91 @@ String LPortal::FindNameAfter(Node * pNode, String szStart)
//////////////////////////////////////////////////////////
void LPortal::Debug_CheckPlaneValidity(const Plane &p) const
{
assert (p.distance_to(m_ptCentre) < 0.0f);
}
// preprocess
void LPortal::AddLightPlanes(const LLight &light, LVector<Plane> &planes) const
void LPortal::AddLightPlanes(LRoomManager &manager, const LLight &light, LVector<Plane> &planes, bool bReverse) const
{
const Vector<Vector3> &pts = m_ptsWorld;
// assuming ortho light
int nPoints = pts.size();
ERR_FAIL_COND(nPoints < 3);
const int max_points = 32;
Vector3 pushed_pts[max_points];
if (nPoints > max_points)
nPoints = max_points;
// transform pushed points
for (int n=0; n<nPoints; n++)
if (light.m_eType == LLight::LT_DIRECTIONAL)
{
pushed_pts[n] = pts[n] + light.m_ptDir;
// assuming ortho light
const int max_points = 32;
Vector3 pushed_pts[max_points];
if (nPoints > max_points)
nPoints = max_points;
// transform pushed points
Vector3 ptPush = light.m_ptDir * 2.0;
for (int n=0; n<nPoints; n++)
{
pushed_pts[n] = pts[n] + ptPush;
}
Plane p;
for (int n=0; n<nPoints; n++)
{
int nPLUS = (n + 1) % nPoints;
p = Plane(pts[n], pts[nPLUS], pushed_pts[n]);
if (bReverse) p = -p;
planes.push_back(p);
Debug_CheckPlaneValidity(p);
}
// first and last
// p = Plane(pts[nPoints-1], pts[0], pushed_pts[0]);
// if (bReverse) p = -p;
// planes.push_back(p);
// Debug_CheckPlaneValidity(p);
// debug
if (manager.m_bDebugLights)
{
for (int n=1; n<nPoints; n++)
{
if (n == 2)
continue;
manager.m_DebugPortalLightPlanes.push_back(pts[n-1]);
manager.m_DebugPortalLightPlanes.push_back(pts[n]);
manager.m_DebugPortalLightPlanes.push_back(pushed_pts[n]);
manager.m_DebugPortalLightPlanes.push_back(pushed_pts[n]);
manager.m_DebugPortalLightPlanes.push_back(pushed_pts[n-1]);
manager.m_DebugPortalLightPlanes.push_back(pts[n-1]);
}
}
return;
}
// use a point for the light for omni and spotlight
Plane p;
for (int n=1; n<nPoints; n++)
for (int n=0; n<nPoints; n++)
{
p = Plane(pts[n-1], pts[n], pushed_pts[n]);
int nPLUS = (n + 1) % nPoints;
p = Plane(pts[nPLUS], pts[n], light.m_ptPos);
if (bReverse) p = -p;
planes.push_back(p);
Debug_CheckPlaneValidity(p);
}
// first and last
p = Plane(pts[nPoints-1], pts[0], pushed_pts[0]);
planes.push_back(p);
// p = Plane(pts[0], pts[nPoints-1], light.m_ptPos);
// if (bReverse) p = -p;
// planes.push_back(p);
// Debug_CheckPlaneValidity(p);
}
@ -129,11 +201,13 @@ void LPortal::AddPlanes(LRoomManager &manager, const Vector3 &ptCam, LVector<Pla
// print(ptCam + pts[n] + pts[n-1]);
// }
planes.push_back(p);
Debug_CheckPlaneValidity(p);
}
// first and last
p = Plane(ptCam, pts[0], pts[nPoints-1]);
planes.push_back(p);
Debug_CheckPlaneValidity(p);
// debug
if (!manager.m_bDebugPlanes)

View File

@ -32,14 +32,31 @@
class LRoom;
class LRoomManager;
class Light;
class LLight
{
public:
enum eLightType
{
LT_DIRECTIONAL,
LT_SPOTLIGHT,
LT_OMNI,
};
void SetDefaults();
Light * GetGodotLight();
bool IsGlobal() const {return m_RoomID == -1;}
Vector3 m_ptDir;
Vector3 m_ptPos;
ObjectID m_GodotID;
eLightType m_eType;
float m_fSpread; // for spotlight
float m_fMaxDist; // shadow distance not light distance
// source room
int m_RoomID; // or -1 for global lights
};
@ -62,7 +79,10 @@ public:
LPortal::eClipResult ClipWithPlane(const Plane &p) const;
void AddPlanes(LRoomManager &manager, const Vector3 &ptCam, LVector<Plane> &planes) const;
void AddLightPlanes(const LLight &light, LVector<Plane> &planes) const;
// reverse direction if we are going back through portals TOWARDS the light rather than away from it
// (the planes will need reversing because the portal winding will be opposite)
void AddLightPlanes(LRoomManager &manager, const LLight &light, LVector<Plane> &planes, bool bReverse) const;
// normal determined by winding order
Vector<Vector3> m_ptsWorld;
@ -87,6 +107,9 @@ public:
// useful funcs
static bool NameStartsWith(Node * pNode, String szSearch);
static String FindNameAfter(Node * pNode, String szStart);
private:
void Debug_CheckPlaneValidity(const Plane &p) const;
};

View File

@ -132,29 +132,48 @@ LRoom * LRoom::DOB_Update(LRoomManager &manager, Spatial * pDOB)
// objects can still be rendered outside immediate view for casting shadows.
// All objects in view (that are set to cast shadows) should cast shadows, so the actual
// shown objects are a superset of the softshown.
void LRoom::SoftShow(VisualInstance * pVI, bool bShow)
void LRoom::SoftShow(VisualInstance * pVI, uint32_t show_flags)
{
// hijack this layer number
uint32_t mask = pVI->get_layer_mask();
uint32_t orig_mask = mask;
const int SOFT_SHOW_MASK = 1 << SOFT_SHOW_BIT;
const int SOFT_HIDE_MASK = 1 << SOFT_HIDE_BIT;
// debug, to check shadow casters are correct for different light types
//#define DEBUG_SHOW_CASTERS_ONLY
#ifdef DEBUG_SHOW_CASTERS_ONLY
bShow = true;
if (bShow)
{
// set
mask |= SOFT_SHOW_MASK;
// clear
mask &= ~(1 | SOFT_HIDE_MASK);
}
#else
if (show_flags & LAYER_MASK_CAMERA)
mask |= LAYER_MASK_CAMERA; // set
else
{
// set
mask |= SOFT_HIDE_MASK;
// clear
mask &= ~(1 | SOFT_SHOW_MASK);
}
mask &= ~LAYER_MASK_CAMERA; // clear
if (show_flags & LAYER_MASK_LIGHT)
mask |= LAYER_MASK_LIGHT;
else
mask &= ~LAYER_MASK_LIGHT;
// if (bShow)
// {
// // set
// mask |= SOFT_SHOW_MASK;
// // clear
// mask &= ~(1 | SOFT_HIDE_MASK);
// }
// else
// {
// // set
// mask |= SOFT_HIDE_MASK;
// // clear
// mask &= ~(1 | SOFT_SHOW_MASK);
// }
#endif
// noop? don't touch the visual server if no change to mask
@ -163,9 +182,7 @@ void LRoom::SoftShow(VisualInstance * pVI, bool bShow)
pVI->set_layer_mask(mask);
// pVI->set_layer_mask_bit(0, false);
// pVI->set_layer_mask_bit(SOFT_HIDE_BIT, bShow == false);
// pVI->set_layer_mask_bit(SOFT_SHOW_BIT, bShow);
// test the visual server - NOT A BOTTLENECK. set_layer_mask is cheap
}
@ -174,6 +191,18 @@ void LRoom::AddShadowCasters(LRoomManager &manager)
{
LPRINT(2, "ADDSHADOWCASTERS room " + get_name() + ", " + itos(m_iNumShadowCasters_SOB) + " shadow casters");
// 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);
}
}
// new!! use precalced list of shadow casters
int last = m_iFirstShadowCaster_SOB + m_iNumShadowCasters_SOB;
for (int n=m_iFirstShadowCaster_SOB; n<last; n++)
@ -189,7 +218,7 @@ void LRoom::AddShadowCasters(LRoomManager &manager)
}
else
{
LPRINT(2, "\t" + itos(sobID) + ", ALREADY CASTER " + manager.m_SOBs[sobID].GetSpatial()->get_name());
//LPRINT(2, "\t" + itos(sobID) + ", ALREADY CASTER " + manager.m_SOBs[sobID].GetSpatial()->get_name());
}
}
@ -215,6 +244,8 @@ void LRoom::AddShadowCasters(LRoomManager &manager)
// (it might be expensive)
void LRoom::FinalizeVisibility(LRoomManager &manager)
{
// make sure all lights needed are turned on
// int last_sob = m_iFirstSOB + m_iNumSOBs;
// for (int n=m_iFirstSOB; n<last_sob; n++)
// {

13
lroom.h
View File

@ -54,8 +54,12 @@ class LRoom
private:
public:
static const int SOFT_SHOW_BIT = 18;
static const int SOFT_HIDE_BIT = 19;
// using flags we can determine which the object is visible to - the camera, or the lights (i.e. shadow caster), or both
static const int LAYER_LIGHT_BIT = 18;
static const int LAYER_CAMERA_BIT = 19;
static const int LAYER_MASK_LIGHT = 1 << LAYER_LIGHT_BIT;
static const int LAYER_MASK_CAMERA = 1 << LAYER_CAMERA_BIT;
// static objects are stored in the manager in a contiguous list
int m_iFirstSOB;
@ -64,6 +68,9 @@ public:
// dynamic objects
LVector<LDob> m_DOBs;
// local lights affecting this room
LVector<int> m_LocalLights;
// portals are stored in the manager in a contiguous list
int m_iFirstPortal;
int m_iNumPortals;
@ -130,7 +137,7 @@ public:
// instead of directly showing and hiding objects we now set their layer,
// 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, bool bShow);
static void SoftShow(VisualInstance * pVI, uint32_t show_flags);
private:
// whether lportal thinks this room is currently visible

View File

@ -25,6 +25,7 @@
#include "scene/3d/mesh_instance.h"
#include "core/math/quick_hull.h"
#include "ldebug.h"
#include "scene/3d/light.h"
// save typing, I am lazy
#define LMAN m_pManager
@ -39,17 +40,21 @@ void LRoomConverter::Convert(LRoomManager &manager)
LPRINT(5, "running convert");
LMAN = &manager;
// force clear all arrays
manager.ReleaseResources(true);
int count = CountRooms();
LMAN->m_SOBs.clear();
LMAN->m_ShadowCasters_SOB.clear();
//LMAN->m_SOBs.clear();
//LMAN->m_ShadowCasters_SOB.clear();
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_Rooms.clear(true);
// LMAN->m_Rooms.clear(true);
LMAN->m_Rooms.resize(count);
m_TempRooms.clear(true);
@ -68,7 +73,11 @@ void LRoomConverter::Convert(LRoomManager &manager)
LMAN->m_BF_master_SOBs.Create(num_sobs);
LMAN->m_BF_master_SOBs_prev.Create(num_sobs);
LMAN->m_BF_ActiveLights.Create(LMAN->m_Lights.size());
LMAN->m_BF_ActiveLights_prev.Create(LMAN->m_Lights.size());
// must be done after the bitfields
Convert_Lights();
Convert_ShadowCasters();
// hide all in preparation for first frame
@ -78,7 +87,7 @@ void LRoomConverter::Convert(LRoomManager &manager)
m_TempRooms.clear(true);
// clear out the local room lights, leave only global lights
LMAN->m_Lights.resize(num_global_lights);
//LMAN->m_Lights.resize(num_global_lights);
Lawn::LDebug::m_bRunning = true;
}
@ -118,6 +127,25 @@ int LRoomConverter::FindRoom_ByName(String szName) const
return -1;
}
void LRoomConverter::Convert_Room_SetDefaultCullMask_Recursive(Node * pParent)
{
int nChildren = pParent->get_child_count();
for (int n=0; n<nChildren; n++)
{
Node * pChild = pParent->get_child(n);
// default cull mask should always be visible to camera and lights
VisualInstance * pVI = Object::cast_to<VisualInstance>(pChild);
if (pVI)
{
// LRoom::SoftShow(pVI, LRoom::LAYER_MASK_CAMERA | LRoom::LAYER_MASK_LIGHT);
}
Convert_Room_SetDefaultCullMask_Recursive(pChild);
}
}
void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &lroom, LAABB &bb_room)
{
int nChildren = pParent->get_child_count();
@ -135,10 +163,18 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l
if (Node_IsBound(pChild))
continue;
// lights
if (Node_IsLight(pChild))
{
LRoom_DetectedLight(lroom, pChild);
continue;
}
VisualInstance * pVI = Object::cast_to<VisualInstance>(pChild);
if (pVI)
{
LPRINT(2, "\t\tFound VI : " + pVI->get_name());
@ -153,6 +189,9 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l
//lroom.m_SOBs.push_back(sob);
LRoom_PushBackSOB(lroom, sob);
// take away layer 0 from the sob, so it can be culled effectively
pVI->set_layer_mask(0);
}
else
{
@ -193,6 +232,9 @@ bool LRoomConverter::Convert_Room(Spatial * pNode, int lroomID)
LAABB bb_room;
bb_room.SetToMaxOpposite();
// set default cull masks
Convert_Room_SetDefaultCullMask_Recursive(pNode);
// recursively find statics
Convert_Room_FindObjects_Recursive(pNode, lroom, bb_room);
@ -309,15 +351,209 @@ void LRoomConverter::Convert_HideAll()
}
}
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())
continue; // ignore globals .. affect all rooms
Light_Trace(n);
}
}
void LRoomConverter::Light_Trace(int iLightID)
{
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);
LVector<Plane> &planes = LMAN->m_Pool.Get(pool_member);
planes.clear();
Lawn::LDebug::m_iTabDepth = 0;
Light_TraceRecursive(0, LMAN->m_Rooms[l.m_RoomID], l, iLightID, planes);
}
void LRoomConverter::Light_TraceRecursive(int depth, LRoom &lroom, const LLight &light, int iLightID, const LVector<Plane> &planes)
{
// prevent too much depth
if (depth > 8)
{
LPRINT_RUN(2, "\t\t\tLight_TraceRecursive DEPTH LIMIT REACHED");
return;
}
Lawn::LDebug::m_iTabDepth = depth;
LPRINT_RUN(2, "ROOM " + lroom.get_name() + " affected by local light");
// add to the local lights affecting this room
// already in list?
bool bAlreadyInList = false;
for (int n=0; n<lroom.m_LocalLights.size(); n++)
{
if (lroom.m_LocalLights[n] == iLightID)
{
bAlreadyInList = true;
break;
}
}
// add to local lights if not already in list
if (!bAlreadyInList)
{
lroom.m_LocalLights.push_back(iLightID);
}
// look through every portal out
for (int n=0; n<lroom.m_iNumPortals; n++)
{
int portalID = lroom.m_iFirstPortal + n;
const LPortal &port = LMAN->m_Portals[portalID];
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);
if (dot <= 0.0f)
{
LPRINT_RUN(2, "\t\tCULLED (wrong direction)");
continue;
}
// is it culled by the planes?
LPortal::eClipResult overall_res = LPortal::eClipResult::CLIP_INSIDE;
// cull portal with planes
for (int l=0; 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;
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;
}
LRoom &linked_room = LMAN->Portal_GetLinkedRoom(port);
// 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);
// copy the existing planes
new_planes.copy_from(planes);
// add the planes for the portal
port.AddLightPlanes(*LMAN, light, new_planes, false);
Light_TraceRecursive(depth + 1, linked_room, light, iLightID, new_planes);
// 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("LRoom_FindShadowCasters_Recursive : Planes pool is empty");
}
}
}
void LRoomConverter::Convert_ShadowCasters()
{
LPRINT(5,"Convert_ShadowCasters ... numlights " + itos (LMAN->m_Lights.size()));
int nLights = LMAN->m_Lights.size();
LPRINT(5,"\nConvert_ShadowCasters ... numlights " + itos (nLights));
for (int n=0; n<LMAN->m_Rooms.size(); n++)
for (int l=0; l<nLights; l++)
{
LPRINT(2,"\tRoom " + itos(n));
LRoom &lroom = LMAN->m_Rooms[n];
LRoom_FindShadowCasters(lroom);
const LLight &light = LMAN->m_Lights[l];
String sz = "Light " + itos (l);
if (light.IsGlobal())
sz += " GLOBAL";
else
sz += " LOCAL from room " + itos(light.m_RoomID);
LPRINT(5, sz + " direction " + light.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;
// if the light is local, does it affect this room?
if (!light.IsGlobal())
{
// a local light .. does it affect this room?
bAffectsRoom = false;
for (int i=0; i<lroom.m_LocalLights.size(); i++)
{
// if the light id is found among the local lights for this room
if (lroom.m_LocalLights[i] == l)
{
bAffectsRoom = true;
break;
}
}
}
if (bAffectsRoom)
{
LPRINT(2,"\n\tAFFECTS room " + itos(n) + ", " + lroom.get_name());
LRoom_FindShadowCasters_FromLight(lroom, light);
//LRoom_FindShadowCasters(lroom, l, light);
}
}
}
}
@ -401,17 +637,37 @@ int LRoomConverter::CountRooms()
// find all objects that cast shadows onto the objects in this room
void LRoomConverter::LRoom_FindShadowCasters(LRoom &lroom)
{
// each global light, and each light affecting this room
for (int n=0; n<LMAN->m_Lights.size(); n++)
{
LRoom_FindShadowCasters_FromLight(lroom, LMAN->m_Lights[n]);
}
//void LRoomConverter::LRoom_FindShadowCasters(LRoom &lroom, int lightID, const LLight &light)
//{
// // each global light, and each light affecting this room
// for (int n=0; n<LMAN->m_Lights.size(); n++)
// {
// // if the light is not a global light, we are only interested if it emits from this room
// const LLight &l = LMAN->m_Lights[n];
// bool bAffectsRoom = true;
// if (l.m_RoomID != -1)
// {
// // a local light .. does it affect this room?
// bAffectsRoom = false;
// for (int i=0; i<lroom.m_LocalLights.size(); i++)
// {
// // if the light id is found among the local lights for this room
// if (lroom.m_LocalLights[i] == n)
// {
// bAffectsRoom = true;
// break;
// }
// }
// }
// if (bAffectsRoom)
// LRoom_FindShadowCasters_FromLight(lroom, l);
// }
return;
}
// return;
//}
void LRoomConverter::LRoom_AddShadowCaster_SOB(LRoom &lroom, int sobID)
{
@ -461,7 +717,7 @@ void LRoomConverter::LRoom_FindShadowCasters_FromLight(LRoom &lroom, const LLigh
planes.clear();
Lawn::LDebug::m_iTabDepth = 0;
LRoom_FindShadowCasters_Recursive(lroom, 0, lroom, light, planes);
LRoom_FindShadowCasters_Recursive(lroom, 1, lroom, light, planes);
}
@ -507,7 +763,10 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int
if (r_min > 0.0f)
// if (r_max < 0.0f)
{
//LPRINT_RUN(2, "\tR_MIN is " + String(Variant(r_min)) + " R_MAX is " + String(Variant(r_max))+ ", for plane " + itos(p));
bShow = false;
break;
}
@ -518,6 +777,10 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int
LPRINT_RUN(2, "\tcaster " + itos(n) + ", " + sob.GetSpatial()->get_name());
LRoom_AddShadowCaster_SOB(source_lroom, n);
}
else
{
//LPRINT_RUN(2, "\tculled " + itos(n) + ", " + sob.GetSpatial()->get_name());
}
}
// look through every portal out
@ -530,8 +793,21 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int
LPRINT_RUN(2, "\tPORTAL " + itos (n) + " (" + itos(portalID) + ") " + port.get_name() + " normal " + port.m_Plane.normal);
// cull with light direction
float dot = port.m_Plane.normal.dot(light.m_ptDir);
if (dot <= 0.0f)
float dot;
if (light.m_eType == LLight::LT_DIRECTIONAL)
{
dot = port.m_Plane.normal.dot(light.m_ptDir);
}
else
{
// cull with light direction to portal
Vector3 ptLightToPort = port.m_ptCentre - light.m_ptPos;
dot = port.m_Plane.normal.dot(ptLightToPort);
}
// float dot = port.m_Plane.normal.dot(light.m_ptDir);
if (dot >= 0.0f)
{
LPRINT_RUN(2, "\t\tCULLED (wrong direction)");
continue;
@ -583,7 +859,7 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int
new_planes.copy_from(planes);
// add the planes for the portal
port.AddLightPlanes(light, new_planes);
port.AddLightPlanes(*LMAN, light, new_planes, true);
LRoom_FindShadowCasters_Recursive(source_lroom, depth + 1, linked_room, light, new_planes);
// for debugging need to reset tab depth
@ -696,6 +972,71 @@ void LRoomConverter::LRoom_MakePortalFinalList(LRoom &lroom, LTempRoom &troom)
}
}
void LRoomConverter::LRoom_DetectedLight(LRoom &lroom, Node * pNode)
{
Light * pLight = Object::cast_to<Light>(pNode);
assert (pLight);
LMAN->LightCreate(pLight, lroom.m_RoomID);
/*
// create new light
LLight l;
l.SetDefaults();
l.m_GodotID = pLight->get_instance_id();
// 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_fMaxDist = pLight->get_param(Light::PARAM_SHADOW_MAX_DISTANCE);
// source room ID
l.m_RoomID = lroom.m_RoomID;
bool bOK = false;
// what kind of light?
SpotLight * pSL = Object::cast_to<SpotLight>(pNode);
if (pSL)
{
LPRINT(2, "\tSPOTLIGHT detected " + pNode->get_name());
l.m_eType = LLight::LT_SPOTLIGHT;
l.m_fSpread = pSL->get_param(Light::PARAM_SPOT_ANGLE);
bOK = true;
}
OmniLight * pOL = Object::cast_to<OmniLight>(pNode);
if (pOL)
{
LPRINT(2, "\tOMNILIGHT detected " + pNode->get_name());
l.m_eType = LLight::LT_OMNI;
bOK = true;
}
DirectionalLight * pDL = Object::cast_to<DirectionalLight>(pNode);
if (pDL)
{
LPRINT(2, "\tDIRECTIONALLIGHT detected " + pNode->get_name());
l.m_eType = LLight::LT_DIRECTIONAL;
bOK = true;
}
// don't add if not recognised
if (!bOK)
{
LPRINT(2, "\tLIGHT type unrecognised " + pNode->get_name());
return;
}
// turn the local light off to start with
pLight->hide();
LMAN->m_Lights.push_back(l);
*/
}
// found a portal mesh! create a matching LPortal
void LRoomConverter::LRoom_DetectedPortalMesh(LRoom &lroom, LTempRoom &troom, MeshInstance * pMeshInstance, String szLinkRoom)
{
@ -792,6 +1133,16 @@ void LRoomConverter::TRoom_MakeOppositePortal(const LPortal &port, int iRoomOrig
///////////////////////////////////////////////////
// helper
bool LRoomConverter::Node_IsLight(Node * pNode) const
{
Light * pLight = Object::cast_to<Light>(pNode);
if (!pLight)
return false;
return true;
}
bool LRoomConverter::Node_IsRoom(Node * pNode) const
{
Spatial * pSpat = Object::cast_to<Spatial>(pNode);

View File

@ -89,11 +89,13 @@ private:
void Convert_Rooms();
bool Convert_Room(Spatial * pNode, int lroomID);
void Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &lroom, LAABB &bb_room);
void Convert_Room_SetDefaultCullMask_Recursive(Node * pParent);
void Convert_Portals();
void Convert_Bounds();
bool Convert_Bound(LRoom &lroom, MeshInstance * pMI);
void Convert_ShadowCasters();
void Convert_Lights();
void Convert_HideAll();
@ -104,8 +106,13 @@ private:
LPortal * LRoom_RequestNewPortal(LRoom &lroom);
void LRoom_PushBackSOB(LRoom &lroom, const LSob &sob);
// lights
void LRoom_DetectedLight(LRoom &lroom, Node * pNode);
void Light_Trace(int iLightID);
void Light_TraceRecursive(int depth, LRoom &lroom, const LLight &light, int iLightID, const LVector<Plane> &planes);
// shadows
void LRoom_FindShadowCasters(LRoom &lroom);
// 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);
@ -118,6 +125,7 @@ private:
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;

View File

@ -43,6 +43,7 @@ LRoomManager::LRoomManager()
m_bDebugPlanes = false;
m_bDebugBounds = false;
m_bDebugLights = false;
}
int LRoomManager::FindClosestRoom(const Vector3 &pt) const
@ -220,9 +221,20 @@ void LRoomManager::CreateDebug()
m_mat_Debug_Bounds = Ref<SpatialMaterial>(memnew(SpatialMaterial));
//m_mat_Debug_Bounds->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
m_mat_Debug_Bounds->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
m_mat_Debug_Bounds->set_cull_mode(SpatialMaterial::CULL_DISABLED);
m_mat_Debug_Bounds->set_albedo(Color(0, 0, 1, 0.4));
b->set_material_override(m_mat_Debug_Bounds);
b->hide();
{
ImmediateGeometry * b = memnew(ImmediateGeometry);
b->set_name("debug_lights");
add_child(b);
m_ID_DebugLights = b->get_instance_id();
b->set_material_override(m_mat_Debug_Bounds);
//b->hide();
}
}
@ -231,7 +243,12 @@ ObjectID LRoomManager::DobRegister_FindVIRecursive(Node * pNode) const
// is the node a VI?
VisualInstance * pVI = Object::cast_to<VisualInstance>(pNode);
if (pVI)
{
// take away layer 0 from the dob, so it can be culled effectively
pVI->set_layer_mask(0);
return pVI->get_instance_id();
}
// try the children
for (int n=0; n<pNode->get_child_count(); n++)
@ -453,6 +470,79 @@ bool LRoomManager::dob_unregister(Node * pDOB)
}
// common stuff for global and local light creation
bool LRoomManager::LightCreate(Light * pLight, int roomID)
{
// set culling flag for light
// 1 is for lighting objects outside the room system
pLight->set_cull_mask(1 | LRoom::LAYER_MASK_LIGHT);
// create new light
LLight l;
l.SetDefaults();
l.m_GodotID = pLight->get_instance_id();
// 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);
bool bOK = false;
// what kind of light?
SpotLight * pSL = Object::cast_to<SpotLight>(pLight);
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);
bOK = true;
}
OmniLight * pOL = Object::cast_to<OmniLight>(pLight);
if (pOL)
{
LPRINT(2, "\tOMNILIGHT detected " + pLight->get_name());
l.m_eType = LLight::LT_OMNI;
bOK = true;
}
DirectionalLight * pDL = Object::cast_to<DirectionalLight>(pLight);
if (pDL)
{
LPRINT(2, "\tDIRECTIONALLIGHT detected " + pLight->get_name());
l.m_eType = LLight::LT_DIRECTIONAL;
bOK = true;
}
// don't add if not recognised
if (!bOK)
{
LPRINT(2, "\tLIGHT type unrecognised " + pLight->get_name());
return false;
}
// turn the local light off to start with
if (!l.IsGlobal())
pLight->hide();
m_Lights.push_back(l);
return true;
}
bool LRoomManager::light_register(Node * pLightNode)
{
if (!pLightNode)
@ -470,18 +560,27 @@ bool LRoomManager::light_register(Node * pLightNode)
return false;
}
// create new light
LLight l;
l.m_GodotID = pLight->get_instance_id();
return LightCreate(pLight, -1);
// 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
// // set culling flag for light
// // 1 is for lighting objects outside the room system
// pLight->set_cull_mask(1 | LRoom::LAYER_MASK_LIGHT);
m_Lights.push_back(l);
// // create new light
// LLight l;
// l.SetDefaults();
// l.m_GodotID = pLight->get_instance_id();
return true;
// // 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 = -1;
// l.m_eType = LLight::LT_DIRECTIONAL;
// m_Lights.push_back(l);
// return true;
}
@ -523,6 +622,21 @@ 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;
@ -610,7 +724,8 @@ void LRoomManager::rooms_set_camera(Node * pCam)
m_ID_camera = pCam->get_instance_id();
// new .. select the cull layer
pCamera->set_cull_mask_bit(LRoom::SOFT_HIDE_BIT, false);
// 1 is for showing objects outside the room system
pCamera->set_cull_mask(1 | LRoom::LAYER_MASK_CAMERA);
// use this temporarily to force debug
// rooms_log_frame();
@ -626,7 +741,30 @@ void LRoomManager::rooms_convert()
// free memory for current set of rooms, prepare for converting a new game level
void LRoomManager::rooms_release()
{
m_Lights.clear();
ReleaseResources(false);
}
void LRoomManager::ReleaseResources(bool bPrepareConvert)
{
m_ShadowCasters_SOB.clear();
m_Rooms.clear(true);
m_Portals.clear(true);
m_SOBs.clear();
if (!bPrepareConvert)
m_Lights.clear();
m_ActiveLights.clear();
m_ActiveLights_prev.clear();
m_VisibleRoomList_A.clear();
m_VisibleRoomList_B.clear();
m_MasterList_SOBs.clear();
m_MasterList_SOBs_prev.clear();
m_VisibleList_SOBs.clear();
m_CasterList_SOBs.clear();
}
@ -645,6 +783,7 @@ void LRoomManager::FrameUpdate_Prepare()
// keep previous
m_BF_master_SOBs_prev.CopyFrom(m_BF_master_SOBs);
m_BF_master_SOBs.Blank();
// note this can be done more efficiently with swapping pointer
m_MasterList_SOBs_prev.copy_from(m_MasterList_SOBs);
@ -655,7 +794,12 @@ void LRoomManager::FrameUpdate_Prepare()
m_BF_caster_SOBs.Blank();
m_BF_visible_SOBs.Blank();
m_BF_master_SOBs.Blank();
// lights
m_BF_ActiveLights_prev.CopyFrom(m_BF_ActiveLights);
m_ActiveLights_prev.copy_from(m_ActiveLights);
m_ActiveLights.clear();
m_BF_ActiveLights.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
@ -851,7 +995,53 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow()
{
//SoftShow(pVI, sob.m_bSOBVisible);
bool bVisible = m_BF_visible_SOBs.GetBit(ID) != 0;
LRoom::SoftShow(pVI, bVisible);
bool bCaster = m_BF_caster_SOBs.GetBit(ID) != 0;
uint32_t flags = 0;
if (bVisible) flags |= LRoom::LAYER_MASK_CAMERA;
if (bCaster) flags |= LRoom::LAYER_MASK_LIGHT;
LRoom::SoftShow(pVI, flags);
}
}
// lights
for (int n=0; n<m_ActiveLights.size(); n++)
{
int lid = m_ActiveLights[n];
if (!m_BF_ActiveLights_prev.GetBit(lid))
{
LLight &light = m_Lights[lid];
Light * pLight = light.GetGodotLight();
if (pLight)
{
//Lawn::LDebug::print("Showing light " + itos (n));
pLight->show();
// FIX GODOT BUG - light cull mask is not preserved when hiding and showing
// set culling flag for light
// 1 is for lighting objects outside the room system
//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);
}
}
}
for (int n=0; n<m_ActiveLights_prev.size(); n++)
{
int lid = m_ActiveLights_prev[n];
if (!m_BF_ActiveLights.GetBit(lid))
{
LLight &light = m_Lights[lid];
Light * pLight = light.GetGodotLight();
if (pLight)
{
//Lawn::LDebug::print("Hiding light " + itos (n));
pLight->hide();
}
}
}
@ -886,7 +1076,17 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_WithinRooms()
{
int ID = m_MasterList_SOBs[n];
LSob &sob = m_SOBs[ID];
// show / hide is relatively expensive because of propagating messages between nodes ...
// should be minimized
sob.Show(true);
// see how expensive show is
// for (int t=0; t<10000; t++)
// {
// sob.Show(false);
// sob.Show(true);
// }
}
}
@ -894,6 +1094,27 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_WithinRooms()
void LRoomManager::FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom)
{
// light portal planes
if (m_bDebugLights)
{
Object * pObj = ObjectDB::get_instance(m_ID_DebugLights);
ImmediateGeometry * im = Object::cast_to<ImmediateGeometry>(pObj);
if (!im)
return;
im->clear();
im->begin(Mesh::PRIMITIVE_TRIANGLES, NULL);
int nVerts = m_DebugPortalLightPlanes.size();
for (int n=0; n<nVerts; n++)
{
im->add_vertex(m_DebugPortalLightPlanes[n]);
}
im->end();
}
if (m_bDebugPlanes)
{
Vector3 ptCam = cam.m_ptPos;
@ -949,6 +1170,8 @@ void LRoomManager::FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom)
im->end();
}
}
void LRoomManager::_notification(int p_what) {
@ -989,6 +1212,7 @@ void LRoomManager::_bind_methods()
ClassDB::bind_method(D_METHOD("rooms_set_active"), &LRoomManager::rooms_set_active);
ClassDB::bind_method(D_METHOD("rooms_set_debug_planes"), &LRoomManager::rooms_set_debug_planes);
ClassDB::bind_method(D_METHOD("rooms_set_debug_bounds"), &LRoomManager::rooms_set_debug_bounds);
ClassDB::bind_method(D_METHOD("rooms_set_debug_lights"), &LRoomManager::rooms_set_debug_lights);
// functions to add dynamic objects to the culling system

View File

@ -90,7 +90,14 @@ private:
// 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;
@ -127,8 +134,10 @@ private:
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 ReleaseResources(bool bPrepareConvert);
// helper funcs
@ -147,12 +156,16 @@ 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;
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;
@ -179,6 +192,7 @@ public:
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);