Preliminary lighting support

This commit is contained in:
lawnjelly 2019-10-09 11:33:19 +01:00 committed by GitHub
parent 09dd7e90d7
commit 0a4f14b6c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1108 additions and 135 deletions

View File

@ -10,18 +10,19 @@ 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 working, I am now currently working on shadow casters. In a simple system you would pre-bake your lighting with e.g. lightmaps, however with realtime lights, shadow casters outside the view of the camera can cast shadows onto the objects in view. Rather than rendering everything as a possible shadow caster, it makes sense to use the same room / portal system to cull out as many shadow casters as possible that could not contribute to the final image.
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.
Note that I've had to move from showing and hiding objects, to using Godot object and camera layers to differentiate between which objects to render from the camera and which for shadows, at least for now. This is a little hacky, and might interfere with your own code if you are using godot layers - layer 1 (default layer) is unset for all objects, and 19 and 20 are currently used by the system to determine whether objects are shadow casters or visible (or both), respectively.
I am now working on shadows. Shadows can be cast by objects that are not 'in view', thus with the naive culling system, in some circumstances shadows will pop into and out of view.
I've also uncovered a bug in Godot core shadow casting:
https://github.com/godotengine/godot/pull/32475
There are three possibilities here:
1) Draw everything in the shadow pass (use Godot default octree). This gets rid of artefacts but would be slow and negate a lot of the benefits of visibility culling.
2) Draw everything roughly around the visible objects. The current version does this now, with the main pass only rendering objects in view, but the shadow pass draws everything within the rooms in view. This is better than naive version, but still can suffer 'popping' of shadows at doorways.
3) More intelligent system to try and work out which shadow casters are relevant to the view.
which I'm finding the best fix for to help with the showing / hiding of shadow casters. There is a workaround but I'm keen to get it fixed in core.
Note that I've had to move from showing and hiding objects, to using Godot object and camera layers to differentiate between which objects to render from the camera and which for shadows, at least for now. This is a little hacky, and might interfere with your own code if you are using godot layers - layer 1 (default layer) is unset for all objects, and 19 and 20 are currently used by the system to toggle visibility internally.
## Roadmap
* Auto conversion of named room spatials and portal mesh instances to LRoom and LPortal DONE
@ -36,10 +37,11 @@ which I'm finding the best fix for to help with the showing / hiding of shadow c
* Optional convex hull bound for rooms DONE
* Add debug graphical view of portal planes DONE
* Add debug graphical view of room bounds DONE
* Dealing with shadows from objects outside of view ONGOING
* Dealing with shadows from objects outside of view
* Bug fixing / testing ONGOING
* Closable portals
* PVS (primary and secondary)
* Investigate multiple passes (shadows, lights)
## Instructions
See [INSTRUCTIONS.md](INSTRUCTIONS.md) and [TUTORIAL.md](TUTORIAL.md)

View File

@ -6,7 +6,7 @@ namespace Lawn
int LDebug::m_iLoggingLevel = 0; // 2
int LDebug::m_iWarningLevel = 0;
int LDebug::m_iTabDepth = 0;
bool LDebug::m_bRunning = false;
bool LDebug::m_bRunning = true;
void LDebug::print(String sz)

View File

@ -1,11 +1,11 @@
#pragma once
//#define LPRINT_RUN(a, b) ;
#define LPRINT_RUN(a, b) ;
#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) {String sz;\
//for (int n=0; n<Lawn::LDebug::m_iTabDepth; n++)\
//sz += "\t";\
//LPRINT(a, sz + b);}
#define LPRINT(a, b) if (!Lawn::LDebug::m_bRunning) {\
if (a >= Lawn::LDebug::m_iLoggingLevel)\

104
ldob.cpp
View File

@ -24,8 +24,67 @@
#include "ldob.h"
#include "scene/3d/mesh_instance.h"
#include "scene/3d/light.h"
void LHidable::Hidable_Create(Node * pNode)
{
// m_pNode = 0; m_pParent = 0; m_bShow = true;
m_pNode = pNode;
m_pParent = m_pNode->get_parent();
m_bShow = true;
}
void LHidable::Show(bool bShow)
{
// noop
if (bShow == m_bShow)
return;
// new state
m_bShow = bShow;
assert (m_pParent);
if (bShow)
{
// add to tree
m_pParent->add_child(m_pNode);
}
else
{
// remove from tree
m_pParent->remove_child(m_pNode);
}
}
/////////////////////////////////////////////////////////////////////
void LLight::SetDefaults()
{
m_GodotID = 0;
m_eType = LT_DIRECTIONAL;
m_fSpread = 0.0f; // for spotlight
m_fMaxDist = 100.0f;
m_RoomID = -1;
m_FirstCaster = 0;
m_NumCasters = 0;
}
Light * LLight::GetGodotLight()
{
Object * pObj = ObjectDB::get_instance(m_GodotID);
Light * p = Object::cast_to<Light>(pObj);
return p;
}
/////////////////////////////////////////////////////////////////////
Spatial * LSob::GetSpatial() const
{
Object * pObj = ObjectDB::get_instance(m_ID);
@ -59,18 +118,53 @@ VisualInstance * LSob::GetVI() const
return pVI;
}
/*
void LSob::Show(bool bShow)
{
// noop
if (bShow == m_bShow)
return;
// new state
m_bShow = bShow;
Spatial * pS = GetSpatial();
if (!pS)
return;
if (bShow)
pS->show();
else
pS->hide();
}
assert (m_pParent);
if (bShow)
{
// add to tree
m_pParent->add_child(m_pNode);
}
else
{
// remove from tree
m_pParent->remove_child(m_pNode);
}
// noop
// if (pS->is_visible() == bShow)
// return;
// if (bShow)
// pS->show();
// else
// pS->hide();
// GeometryInstance * pGI = Object::cast_to<GeometryInstance>(pS);
// if (pGI)
// {
// // godot visible bug workaround
// pGI->set_extra_cull_margin(0.0f);
// }
}
*/
Spatial * LDob::GetSpatial() const

49
ldob.h
View File

@ -28,19 +28,33 @@
#include "scene/3d/spatial.h"
class VisualInstance;
class Light;
class LHidable
{
public:
void Hidable_Create(Node * pNode);
void Show(bool bShow);
// new .. can be separated from the scene tree to cull
Node * m_pNode;
Node * m_pParent;
// separate flag so we don't have to touch the godot lookup
bool m_bShow;
};
// static object
class LSob
class LSob : public LHidable
{
public:
Spatial * GetSpatial() const;
VisualInstance * GetVI() const;
void Show(bool bShow);
//void Show(bool bShow);
bool IsShadowCaster() const;
ObjectID m_ID; // godot object
AABB m_aabb; // world space
//bool m_bSOBVisible;
};
// dynamic object
@ -55,3 +69,32 @@ public:
bool m_bVisible;
float m_fRadius;
};
class LLight : public LHidable
{
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
// shadow casters
int m_FirstCaster;
int m_NumCasters;
};

View File

@ -25,6 +25,7 @@
#include "lroom_manager.h"
bool LPortal::NameStartsWith(Node * pNode, String szSearch)
{
int sl = szSearch.length();
@ -71,38 +72,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 +183,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,15 +32,9 @@
class LRoom;
class LRoomManager;
class Light;
class LLight
{
public:
Vector3 m_ptDir;
Vector3 m_ptPos;
ObjectID m_GodotID;
};
class LPortal {
@ -62,7 +56,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 +84,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;
};

158
lroom.cpp
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,51 +182,76 @@ 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 godot bug
// GeometryInstance * pGI = Object::cast_to<GeometryInstance>(pVI);
// if (pGI)
// {
// // godot visible bug workaround
// pGI->set_extra_cull_margin(0.0f);
// }
// test the visual server - NOT A BOTTLENECK. set_layer_mask is cheap
}
// naive version, adds all the non visible objects in visible rooms as shadow casters
void LRoom::AddShadowCasters(LRoomManager &manager)
{
LPRINT(2, "ADDSHADOWCASTERS room " + get_name() + ", " + itos(m_iNumShadowCasters_SOB) + " shadow casters");
LPRINT_RUN(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);
// 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());
}
}
}
}
// new!! use precalced list of shadow casters
int last = m_iFirstShadowCaster_SOB + m_iNumShadowCasters_SOB;
for (int n=m_iFirstShadowCaster_SOB; n<last; n++)
{
int sobID = manager.m_ShadowCasters_SOB[n];
// int last = m_iFirstShadowCaster_SOB + m_iNumShadowCasters_SOB;
// for (int n=m_iFirstShadowCaster_SOB; n<last; n++)
// {
// int sobID = manager.m_ShadowCasters_SOB[n];
// only add to the caster list if not in it already
if (!manager.m_BF_caster_SOBs.GetBit(sobID))
{
LPRINT(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());
}
}
// // only add to the caster list if not in it already
// if (!manager.m_BF_caster_SOBs.GetBit(sobID))
// {
// LPRINT(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());
// }
// }
/*
int last_sob = m_iFirstSOB + m_iNumSOBs;
for (int n=m_iFirstSOB; n<last_sob; n++)
{
// bool bVisible = manager.m_BF_visible_SOBs.GetBit(n) != 0;
// // already in list
// if (bVisible)
// continue;
manager.m_BF_caster_SOBs.SetBit(n, true);
manager.m_CasterList_SOBs.push_back(n);
}
*/
}
@ -215,6 +259,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++)
// {
@ -250,7 +296,13 @@ void LRoom::FinalizeVisibility(LRoomManager &manager)
VisualInstance * pVI = dob.GetVI();
if (pVI)
{
SoftShow(pVI, dob.m_bVisible);
uint32_t mask = 0;
if (dob.m_bVisible)
{
mask = LRoom::LAYER_MASK_CAMERA | LRoom::LAYER_MASK_LIGHT;
}
SoftShow(pVI, mask);
// if (dob.m_bVisible)
// {
// //print("LRoom::FinalizeVisibility making visible dob " + pS->get_name());
@ -317,7 +369,7 @@ void LRoom::Room_MakeVisible(bool bVisible)
//}
// show godot room and all linked dobs and all sobs
void LRoom::Debug_ShowAll()
void LRoom::Debug_ShowAll(bool bActive)
{
Room_MakeVisible(true);
@ -328,6 +380,12 @@ void LRoom::Debug_ShowAll()
// Spatial * pS = sob.GetSpatial();
// if (pS)
// pS->show();
// VisualInstance * pVI = sob.GetVI();
// if (pVI)
// {
// SoftShow(pVI, LRoom::LAYER_MASK_CAMERA | LRoom::LAYER_MASK_LIGHT);
// }
// }
}

15
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;
@ -105,7 +112,7 @@ public:
// void Hide_All();
// show godot room and all linked dobs and all sobs
void Debug_ShowAll();
void Debug_ShowAll(bool bActive);
// hide all the objects not hit on this frame .. instead of calling godot hide without need
// (it might be expensive)
@ -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,17 @@ 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();
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.resize(count);
m_TempRooms.clear(true);
@ -68,7 +69,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 +83,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 +123,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 +159,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());
@ -150,9 +182,13 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l
LSob sob;
sob.m_ID = pVI->get_instance_id();
sob.m_aabb = bb;
sob.Hidable_Create(pChild);
//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 +229,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);
@ -307,17 +346,272 @@ void LRoomConverter::Convert_HideAll()
LSob &sob = LMAN->m_SOBs[n];
sob.Show(false);
}
// hide all lights that are non global
for (int n=0; n<LMAN->m_Lights.size(); n++)
{
LLight &light = LMAN->m_Lights[n];
if (!light.IsGlobal())
light.Show(false);
}
}
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)
{
// 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);
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, 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);
}
// add each light caster that is within the planes to the light caster list
// clip all objects in this room to the clipping planes
int last_sob = lroom.m_iFirstSOB + lroom.m_iNumSOBs;
for (int n=lroom.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 (LMAN->m_BF_caster_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)
{
bShow = false;
break;
}
}
if (bShow)
{
Light_AddCaster_SOB(light, n);
}
} // for through sobs
// 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,18 +695,59 @@ int LRoomConverter::CountRooms()
// find all objects that cast shadows onto the objects in this room
void LRoomConverter::LRoom_FindShadowCasters(LRoom &lroom)
//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;
//}
void LRoomConverter::Light_AddCaster_SOB(LLight &light, int sobID)
{
// 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]);
}
// we will reuse the rendering bitflags for shadow casters for this ... to check for double entries (fnaa fnaa)
if (LMAN->m_BF_caster_SOBs.GetBit(sobID))
return;
return;
LPRINT_RUN(2, "\t\t\tLightCaster " + itos(sobID));
LMAN->m_BF_caster_SOBs.SetBit(sobID, true);
// first?
if (!light.m_NumCasters)
light.m_FirstCaster = LMAN->m_LightCasters_SOB.size();
LMAN->m_LightCasters_SOB.push_back(sobID);
light.m_NumCasters++;
}
void LRoomConverter::LRoom_AddShadowCaster_SOB(LRoom &lroom, int sobID)
{
// we will reuse the rendering bitflags for shadow casters for this ... to check for double entries (fnaa fnaa)
@ -461,7 +796,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 +842,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 +856,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 +872,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 +938,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 +1051,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 +1212,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,12 +106,19 @@ 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, LLight &light, int iLightID, const LVector<Plane> &planes);
void Light_AddCaster_SOB(LLight &light, int sobID);
// 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);
void TRoom_MakeOppositePortal(const LPortal &port, int iRoomOrig);
@ -118,6 +127,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,83 @@ 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.Hidable_Create(pLight);
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())
{
// l.Show(false);
//pLight->hide();
}
m_Lights.push_back(l);
return true;
}
bool LRoomManager::light_register(Node * pLightNode)
{
if (!pLightNode)
@ -470,18 +564,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 +626,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;
@ -574,10 +692,33 @@ void LRoomManager::rooms_set_active(bool bActive)
for (int n=0; n<m_Rooms.size(); n++)
{
LRoom &lroom = m_Rooms[n];
lroom.Debug_ShowAll();
lroom.Debug_ShowAll(bActive);
}
for (int n=0; n<m_SOBs.size(); n++)
{
LSob &sob = m_SOBs[n];
Spatial * pS = sob.GetSpatial();
if (pS)
if (!bActive)
pS->show();
else
pS->hide();
VisualInstance * pVI = sob.GetVI();
if (pVI)
{
uint32_t mask = 0;
if (!bActive)
{
mask = LRoom::LAYER_MASK_CAMERA | LRoom::LAYER_MASK_LIGHT;
}
LRoom::SoftShow(pVI, mask);
}
}
}
void LRoomManager::rooms_set_logging(int level)
@ -610,7 +751,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 +768,31 @@ 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_LightCasters_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 +811,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 +822,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
@ -833,6 +1005,7 @@ void LRoomManager::FrameUpdate_AddShadowCasters()
m_Rooms[r].AddShadowCasters(*this);
}
LPRINT(2, "TOTAL shadow casters " + itos(m_CasterList_SOBs.size()));
}
void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow()
@ -851,7 +1024,56 @@ 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.Show(true);
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.Show(false);
// Light * pLight = light.GetGodotLight();
// if (pLight)
// {
// //Lawn::LDebug::print("Hiding light " + itos (n));
// pLight->hide();
// }
}
}
@ -886,7 +1108,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 +1126,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 +1202,8 @@ void LRoomManager::FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom)
im->end();
}
}
void LRoomManager::_notification(int p_what) {
@ -989,6 +1244,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,11 +90,21 @@ 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;
// master list of casters for each light
LVector<uint32_t> m_LightCasters_SOB;
protected:
static void _bind_methods();
void _notification(int p_what);
@ -127,8 +137,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 +159,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 +195,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);