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=xF_3Fe2HRdk \
https://www.youtube.com/watch?v=NmlWkkhGoJA https://www.youtube.com/watch?v=NmlWkkhGoJA
_Feel free to leave suggestions / feature requests on the issue tracker, especially regarding ease of use._\ _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._
## Current status ## 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: There are three possibilities here:
https://github.com/godotengine/godot/pull/32475 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 ## Roadmap
* Auto conversion of named room spatials and portal mesh instances to LRoom and LPortal DONE * 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 * Optional convex hull bound for rooms DONE
* Add debug graphical view of portal planes DONE * Add debug graphical view of portal planes DONE
* Add debug graphical view of room bounds 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 * Bug fixing / testing ONGOING
* Closable portals * Closable portals
* PVS (primary and secondary) * PVS (primary and secondary)
* Investigate multiple passes (shadows, lights)
## Instructions ## Instructions
See [INSTRUCTIONS.md](INSTRUCTIONS.md) and [TUTORIAL.md](TUTORIAL.md) 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_iLoggingLevel = 0; // 2
int LDebug::m_iWarningLevel = 0; int LDebug::m_iWarningLevel = 0;
int LDebug::m_iTabDepth = 0; int LDebug::m_iTabDepth = 0;
bool LDebug::m_bRunning = false; bool LDebug::m_bRunning = true;
void LDebug::print(String sz) void LDebug::print(String sz)

View File

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

View File

@ -24,8 +24,67 @@
#include "ldob.h" #include "ldob.h"
#include "scene/3d/mesh_instance.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 Spatial * LSob::GetSpatial() const
{ {
Object * pObj = ObjectDB::get_instance(m_ID); Object * pObj = ObjectDB::get_instance(m_ID);
@ -59,18 +118,53 @@ VisualInstance * LSob::GetVI() const
return pVI; return pVI;
} }
/*
void LSob::Show(bool bShow) void LSob::Show(bool bShow)
{ {
// noop
if (bShow == m_bShow)
return;
// new state
m_bShow = bShow;
Spatial * pS = GetSpatial(); Spatial * pS = GetSpatial();
if (!pS) if (!pS)
return; return;
assert (m_pParent);
if (bShow) if (bShow)
pS->show(); {
// add to tree
m_pParent->add_child(m_pNode);
}
else else
pS->hide(); {
// 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 Spatial * LDob::GetSpatial() const

49
ldob.h
View File

@ -28,19 +28,33 @@
#include "scene/3d/spatial.h" #include "scene/3d/spatial.h"
class VisualInstance; 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 // static object
class LSob class LSob : public LHidable
{ {
public: public:
Spatial * GetSpatial() const; Spatial * GetSpatial() const;
VisualInstance * GetVI() const; VisualInstance * GetVI() const;
void Show(bool bShow); //void Show(bool bShow);
bool IsShadowCaster() const; bool IsShadowCaster() const;
ObjectID m_ID; // godot object ObjectID m_ID; // godot object
AABB m_aabb; // world space AABB m_aabb; // world space
//bool m_bSOBVisible;
}; };
// dynamic object // dynamic object
@ -55,3 +69,32 @@ public:
bool m_bVisible; bool m_bVisible;
float m_fRadius; 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" #include "lroom_manager.h"
bool LPortal::NameStartsWith(Node * pNode, String szSearch) bool LPortal::NameStartsWith(Node * pNode, String szSearch)
{ {
int sl = szSearch.length(); int sl = szSearch.length();
@ -71,15 +72,23 @@ String LPortal::FindNameAfter(Node * pNode, String szStart)
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
void LPortal::Debug_CheckPlaneValidity(const Plane &p) const
{
assert (p.distance_to(m_ptCentre) < 0.0f);
}
// preprocess // 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; const Vector<Vector3> &pts = m_ptsWorld;
// assuming ortho light
int nPoints = pts.size(); int nPoints = pts.size();
ERR_FAIL_COND(nPoints < 3); ERR_FAIL_COND(nPoints < 3);
if (light.m_eType == LLight::LT_DIRECTIONAL)
{
// assuming ortho light
const int max_points = 32; const int max_points = 32;
Vector3 pushed_pts[max_points]; Vector3 pushed_pts[max_points];
@ -87,22 +96,67 @@ void LPortal::AddLightPlanes(const LLight &light, LVector<Plane> &planes) const
nPoints = max_points; nPoints = max_points;
// transform pushed points // transform pushed points
Vector3 ptPush = light.m_ptDir * 2.0;
for (int n=0; n<nPoints; n++) for (int n=0; n<nPoints; n++)
{ {
pushed_pts[n] = pts[n] + light.m_ptDir; pushed_pts[n] = pts[n] + ptPush;
} }
Plane p; 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[n], pts[nPLUS], pushed_pts[n]);
if (bReverse) p = -p;
planes.push_back(p); planes.push_back(p);
Debug_CheckPlaneValidity(p);
} }
// first and last // first and last
p = Plane(pts[nPoints-1], pts[0], pushed_pts[0]); // 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=0; n<nPoints; n++)
{
int nPLUS = (n + 1) % nPoints;
p = Plane(pts[nPLUS], pts[n], light.m_ptPos);
if (bReverse) p = -p;
planes.push_back(p); planes.push_back(p);
Debug_CheckPlaneValidity(p);
}
// first and last
// 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]); // print(ptCam + pts[n] + pts[n-1]);
// } // }
planes.push_back(p); planes.push_back(p);
Debug_CheckPlaneValidity(p);
} }
// first and last // first and last
p = Plane(ptCam, pts[0], pts[nPoints-1]); p = Plane(ptCam, pts[0], pts[nPoints-1]);
planes.push_back(p); planes.push_back(p);
Debug_CheckPlaneValidity(p);
// debug // debug
if (!manager.m_bDebugPlanes) if (!manager.m_bDebugPlanes)

View File

@ -32,15 +32,9 @@
class LRoom; class LRoom;
class LRoomManager; class LRoomManager;
class Light;
class LLight
{
public:
Vector3 m_ptDir;
Vector3 m_ptPos;
ObjectID m_GodotID;
};
class LPortal { class LPortal {
@ -62,7 +56,10 @@ public:
LPortal::eClipResult ClipWithPlane(const Plane &p) const; LPortal::eClipResult ClipWithPlane(const Plane &p) const;
void AddPlanes(LRoomManager &manager, const Vector3 &ptCam, LVector<Plane> &planes) 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 // normal determined by winding order
Vector<Vector3> m_ptsWorld; Vector<Vector3> m_ptsWorld;
@ -87,6 +84,9 @@ public:
// useful funcs // useful funcs
static bool NameStartsWith(Node * pNode, String szSearch); static bool NameStartsWith(Node * pNode, String szSearch);
static String FindNameAfter(Node * pNode, String szStart); static String FindNameAfter(Node * pNode, String szStart);
private:
void Debug_CheckPlaneValidity(const Plane &p) const;
}; };

134
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. // 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 // All objects in view (that are set to cast shadows) should cast shadows, so the actual
// shown objects are a superset of the softshown. // 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 // hijack this layer number
uint32_t mask = pVI->get_layer_mask(); uint32_t mask = pVI->get_layer_mask();
uint32_t orig_mask = 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) 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 else
{ mask &= ~LAYER_MASK_CAMERA; // clear
// set
mask |= SOFT_HIDE_MASK; if (show_flags & LAYER_MASK_LIGHT)
// clear mask |= LAYER_MASK_LIGHT;
mask &= ~(1 | SOFT_SHOW_MASK); 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 // 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(mask);
// pVI->set_layer_mask_bit(0, false); // test godot bug
// pVI->set_layer_mask_bit(SOFT_HIDE_BIT, bShow == false); // GeometryInstance * pGI = Object::cast_to<GeometryInstance>(pVI);
// pVI->set_layer_mask_bit(SOFT_SHOW_BIT, bShow); // 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 // naive version, adds all the non visible objects in visible rooms as shadow casters
void LRoom::AddShadowCasters(LRoomManager &manager) 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");
// new!! use precalced list of shadow casters // add all the active lights in this room
int last = m_iFirstShadowCaster_SOB + m_iNumShadowCasters_SOB; for (int n=0; n<m_LocalLights.size(); n++)
for (int n=m_iFirstShadowCaster_SOB; n<last; n++)
{ {
int sobID = manager.m_ShadowCasters_SOB[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);
// only add to the caster list if not in it already // 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)) if (!manager.m_BF_caster_SOBs.GetBit(sobID))
{ {
LPRINT(2, "\t" + itos(sobID) + ", " + manager.m_SOBs[sobID].GetSpatial()->get_name()); LPRINT_RUN(2, "\t" + itos(sobID) + ", " + manager.m_SOBs[sobID].GetSpatial()->get_name());
manager.m_BF_caster_SOBs.SetBit(sobID, true); manager.m_BF_caster_SOBs.SetBit(sobID, true);
manager.m_CasterList_SOBs.push_back(sobID); manager.m_CasterList_SOBs.push_back(sobID);
} }
else 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());
}
}
} }
} }
/* // new!! use precalced list of shadow casters
int last_sob = m_iFirstSOB + m_iNumSOBs; // int last = m_iFirstShadowCaster_SOB + m_iNumShadowCasters_SOB;
for (int n=m_iFirstSOB; n<last_sob; n++) // for (int n=m_iFirstShadowCaster_SOB; n<last; n++)
{ // {
// bool bVisible = manager.m_BF_visible_SOBs.GetBit(n) != 0; // int sobID = manager.m_ShadowCasters_SOB[n];
// // already in list // // only add to the caster list if not in it already
// if (bVisible) // if (!manager.m_BF_caster_SOBs.GetBit(sobID))
// continue; // {
// 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());
// }
// }
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) // (it might be expensive)
void LRoom::FinalizeVisibility(LRoomManager &manager) void LRoom::FinalizeVisibility(LRoomManager &manager)
{ {
// make sure all lights needed are turned on
// int last_sob = m_iFirstSOB + m_iNumSOBs; // int last_sob = m_iFirstSOB + m_iNumSOBs;
// for (int n=m_iFirstSOB; n<last_sob; n++) // for (int n=m_iFirstSOB; n<last_sob; n++)
// { // {
@ -250,7 +296,13 @@ void LRoom::FinalizeVisibility(LRoomManager &manager)
VisualInstance * pVI = dob.GetVI(); VisualInstance * pVI = dob.GetVI();
if (pVI) 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) // if (dob.m_bVisible)
// { // {
// //print("LRoom::FinalizeVisibility making visible dob " + pS->get_name()); // //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 // show godot room and all linked dobs and all sobs
void LRoom::Debug_ShowAll() void LRoom::Debug_ShowAll(bool bActive)
{ {
Room_MakeVisible(true); Room_MakeVisible(true);
@ -328,6 +380,12 @@ void LRoom::Debug_ShowAll()
// Spatial * pS = sob.GetSpatial(); // Spatial * pS = sob.GetSpatial();
// if (pS) // if (pS)
// pS->show(); // 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: private:
public: public:
static const int SOFT_SHOW_BIT = 18; // 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 SOFT_HIDE_BIT = 19; 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 // static objects are stored in the manager in a contiguous list
int m_iFirstSOB; int m_iFirstSOB;
@ -64,6 +68,9 @@ public:
// dynamic objects // dynamic objects
LVector<LDob> m_DOBs; LVector<LDob> m_DOBs;
// local lights affecting this room
LVector<int> m_LocalLights;
// portals are stored in the manager in a contiguous list // portals are stored in the manager in a contiguous list
int m_iFirstPortal; int m_iFirstPortal;
int m_iNumPortals; int m_iNumPortals;
@ -105,7 +112,7 @@ public:
// void Hide_All(); // void Hide_All();
// show godot room and all linked dobs and all sobs // 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 // hide all the objects not hit on this frame .. instead of calling godot hide without need
// (it might be expensive) // (it might be expensive)
@ -130,7 +137,7 @@ public:
// instead of directly showing and hiding objects we now set their layer, // 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 // 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. // 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: private:
// whether lportal thinks this room is currently visible // whether lportal thinks this room is currently visible

View File

@ -25,6 +25,7 @@
#include "scene/3d/mesh_instance.h" #include "scene/3d/mesh_instance.h"
#include "core/math/quick_hull.h" #include "core/math/quick_hull.h"
#include "ldebug.h" #include "ldebug.h"
#include "scene/3d/light.h"
// save typing, I am lazy // save typing, I am lazy
#define LMAN m_pManager #define LMAN m_pManager
@ -39,17 +40,17 @@ void LRoomConverter::Convert(LRoomManager &manager)
LPRINT(5, "running convert"); LPRINT(5, "running convert");
LMAN = &manager; LMAN = &manager;
// force clear all arrays
manager.ReleaseResources(true);
int count = CountRooms(); int count = CountRooms();
LMAN->m_SOBs.clear();
LMAN->m_ShadowCasters_SOB.clear();
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 // make sure bitfield is right size for number of rooms
LMAN->m_BF_visible_rooms.Create(count); LMAN->m_BF_visible_rooms.Create(count);
LMAN->m_Rooms.clear(true);
LMAN->m_Rooms.resize(count); LMAN->m_Rooms.resize(count);
m_TempRooms.clear(true); 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.Create(num_sobs);
LMAN->m_BF_master_SOBs_prev.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 // must be done after the bitfields
Convert_Lights();
Convert_ShadowCasters(); Convert_ShadowCasters();
// hide all in preparation for first frame // hide all in preparation for first frame
@ -78,7 +83,7 @@ void LRoomConverter::Convert(LRoomManager &manager)
m_TempRooms.clear(true); m_TempRooms.clear(true);
// clear out the local room lights, leave only global lights // 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; Lawn::LDebug::m_bRunning = true;
} }
@ -118,6 +123,25 @@ int LRoomConverter::FindRoom_ByName(String szName) const
return -1; 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) void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &lroom, LAABB &bb_room)
{ {
int nChildren = pParent->get_child_count(); int nChildren = pParent->get_child_count();
@ -135,10 +159,18 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l
if (Node_IsBound(pChild)) if (Node_IsBound(pChild))
continue; continue;
// lights
if (Node_IsLight(pChild))
{
LRoom_DetectedLight(lroom, pChild);
continue;
}
VisualInstance * pVI = Object::cast_to<VisualInstance>(pChild); VisualInstance * pVI = Object::cast_to<VisualInstance>(pChild);
if (pVI) if (pVI)
{ {
LPRINT(2, "\t\tFound VI : " + pVI->get_name()); LPRINT(2, "\t\tFound VI : " + pVI->get_name());
@ -150,9 +182,13 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l
LSob sob; LSob sob;
sob.m_ID = pVI->get_instance_id(); sob.m_ID = pVI->get_instance_id();
sob.m_aabb = bb; sob.m_aabb = bb;
sob.Hidable_Create(pChild);
//lroom.m_SOBs.push_back(sob); //lroom.m_SOBs.push_back(sob);
LRoom_PushBackSOB(lroom, sob); LRoom_PushBackSOB(lroom, sob);
// take away layer 0 from the sob, so it can be culled effectively
pVI->set_layer_mask(0);
} }
else else
{ {
@ -193,6 +229,9 @@ bool LRoomConverter::Convert_Room(Spatial * pNode, int lroomID)
LAABB bb_room; LAABB bb_room;
bb_room.SetToMaxOpposite(); bb_room.SetToMaxOpposite();
// set default cull masks
Convert_Room_SetDefaultCullMask_Recursive(pNode);
// recursively find statics // recursively find statics
Convert_Room_FindObjects_Recursive(pNode, lroom, bb_room); Convert_Room_FindObjects_Recursive(pNode, lroom, bb_room);
@ -307,17 +346,272 @@ void LRoomConverter::Convert_HideAll()
LSob &sob = LMAN->m_SOBs[n]; LSob &sob = LMAN->m_SOBs[n];
sob.Show(false); 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() 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 l=0; l<nLights; l++)
{
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++) for (int n=0; n<LMAN->m_Rooms.size(); n++)
{ {
LPRINT(2,"\tRoom " + itos(n));
LRoom &lroom = LMAN->m_Rooms[n]; LRoom &lroom = LMAN->m_Rooms[n];
LRoom_FindShadowCasters(lroom);
// 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 // 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 // // each global light, and each light affecting this room
for (int n=0; n<LMAN->m_Lights.size(); n++) // for (int n=0; n<LMAN->m_Lights.size(); n++)
{ // {
LRoom_FindShadowCasters_FromLight(lroom, LMAN->m_Lights[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)
{
// 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) 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) // 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(); planes.clear();
Lawn::LDebug::m_iTabDepth = 0; 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_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; bShow = false;
break; break;
} }
@ -518,6 +856,10 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int
LPRINT_RUN(2, "\tcaster " + itos(n) + ", " + sob.GetSpatial()->get_name()); LPRINT_RUN(2, "\tcaster " + itos(n) + ", " + sob.GetSpatial()->get_name());
LRoom_AddShadowCaster_SOB(source_lroom, n); LRoom_AddShadowCaster_SOB(source_lroom, n);
} }
else
{
//LPRINT_RUN(2, "\tculled " + itos(n) + ", " + sob.GetSpatial()->get_name());
}
} }
// look through every portal out // 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); LPRINT_RUN(2, "\tPORTAL " + itos (n) + " (" + itos(portalID) + ") " + port.get_name() + " normal " + port.m_Plane.normal);
// cull with light direction // cull with light direction
float dot = port.m_Plane.normal.dot(light.m_ptDir); float dot;
if (dot <= 0.0f) 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)"); LPRINT_RUN(2, "\t\tCULLED (wrong direction)");
continue; continue;
@ -583,7 +938,7 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int
new_planes.copy_from(planes); new_planes.copy_from(planes);
// add the planes for the portal // 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); LRoom_FindShadowCasters_Recursive(source_lroom, depth + 1, linked_room, light, new_planes);
// for debugging need to reset tab depth // 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 // found a portal mesh! create a matching LPortal
void LRoomConverter::LRoom_DetectedPortalMesh(LRoom &lroom, LTempRoom &troom, MeshInstance * pMeshInstance, String szLinkRoom) 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 // 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 bool LRoomConverter::Node_IsRoom(Node * pNode) const
{ {
Spatial * pSpat = Object::cast_to<Spatial>(pNode); Spatial * pSpat = Object::cast_to<Spatial>(pNode);

View File

@ -89,11 +89,13 @@ private:
void Convert_Rooms(); void Convert_Rooms();
bool Convert_Room(Spatial * pNode, int lroomID); bool Convert_Room(Spatial * pNode, int lroomID);
void Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &lroom, LAABB &bb_room); void Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &lroom, LAABB &bb_room);
void Convert_Room_SetDefaultCullMask_Recursive(Node * pParent);
void Convert_Portals(); void Convert_Portals();
void Convert_Bounds(); void Convert_Bounds();
bool Convert_Bound(LRoom &lroom, MeshInstance * pMI); bool Convert_Bound(LRoom &lroom, MeshInstance * pMI);
void Convert_ShadowCasters(); void Convert_ShadowCasters();
void Convert_Lights();
void Convert_HideAll(); void Convert_HideAll();
@ -104,12 +106,19 @@ private:
LPortal * LRoom_RequestNewPortal(LRoom &lroom); LPortal * LRoom_RequestNewPortal(LRoom &lroom);
void LRoom_PushBackSOB(LRoom &lroom, const LSob &sob); 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 // 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_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_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 LRoom_AddShadowCaster_SOB(LRoom &lroom, int sobID);
void TRoom_MakeOppositePortal(const LPortal &port, int iRoomOrig); void TRoom_MakeOppositePortal(const LPortal &port, int iRoomOrig);
@ -118,6 +127,7 @@ private:
bool Node_IsPortal(Node * pNode) const; bool Node_IsPortal(Node * pNode) const;
bool Node_IsBound(Node * pNode) const; bool Node_IsBound(Node * pNode) const;
bool Node_IsIgnore(Node * pNode) const; bool Node_IsIgnore(Node * pNode) const;
bool Node_IsLight(Node * pNode) const;
int FindRoom_ByName(String szName) const; int FindRoom_ByName(String szName) const;

View File

@ -43,6 +43,7 @@ LRoomManager::LRoomManager()
m_bDebugPlanes = false; m_bDebugPlanes = false;
m_bDebugBounds = false; m_bDebugBounds = false;
m_bDebugLights = false;
} }
int LRoomManager::FindClosestRoom(const Vector3 &pt) const 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 = Ref<SpatialMaterial>(memnew(SpatialMaterial));
//m_mat_Debug_Bounds->set_flag(SpatialMaterial::FLAG_UNSHADED, true); //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_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)); m_mat_Debug_Bounds->set_albedo(Color(0, 0, 1, 0.4));
b->set_material_override(m_mat_Debug_Bounds); b->set_material_override(m_mat_Debug_Bounds);
b->hide(); 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? // is the node a VI?
VisualInstance * pVI = Object::cast_to<VisualInstance>(pNode); VisualInstance * pVI = Object::cast_to<VisualInstance>(pNode);
if (pVI) 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(); return pVI->get_instance_id();
}
// try the children // try the children
for (int n=0; n<pNode->get_child_count(); n++) 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) bool LRoomManager::light_register(Node * pLightNode)
{ {
if (!pLightNode) if (!pLightNode)
@ -470,18 +564,27 @@ bool LRoomManager::light_register(Node * pLightNode)
return false; return false;
} }
// create new light return LightCreate(pLight, -1);
LLight l;
l.m_GodotID = pLight->get_instance_id();
// direction // // set culling flag for light
Transform tr = pLight->get_global_transform(); // // 1 is for lighting objects outside the room system
l.m_ptPos = tr.origin; // pLight->set_cull_mask(1 | LRoom::LAYER_MASK_LIGHT);
l.m_ptDir = tr.basis.get_axis(2); // or possibly get_axis .. z is what we want
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(); 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) void LRoomManager::rooms_set_debug_bounds(bool bActive)
{ {
m_bDebugBounds = bActive; m_bDebugBounds = bActive;
@ -574,10 +692,33 @@ void LRoomManager::rooms_set_active(bool bActive)
for (int n=0; n<m_Rooms.size(); n++) for (int n=0; n<m_Rooms.size(); n++)
{ {
LRoom &lroom = m_Rooms[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) 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(); m_ID_camera = pCam->get_instance_id();
// new .. select the cull layer // 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 // use this temporarily to force debug
// rooms_log_frame(); // 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 // free memory for current set of rooms, prepare for converting a new game level
void LRoomManager::rooms_release() void LRoomManager::rooms_release()
{ {
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_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 // keep previous
m_BF_master_SOBs_prev.CopyFrom(m_BF_master_SOBs); 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 // note this can be done more efficiently with swapping pointer
m_MasterList_SOBs_prev.copy_from(m_MasterList_SOBs); m_MasterList_SOBs_prev.copy_from(m_MasterList_SOBs);
@ -655,7 +822,12 @@ void LRoomManager::FrameUpdate_Prepare()
m_BF_caster_SOBs.Blank(); m_BF_caster_SOBs.Blank();
m_BF_visible_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 // 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 // that are showing that haven't been hit this frame
@ -833,6 +1005,7 @@ void LRoomManager::FrameUpdate_AddShadowCasters()
m_Rooms[r].AddShadowCasters(*this); m_Rooms[r].AddShadowCasters(*this);
} }
LPRINT(2, "TOTAL shadow casters " + itos(m_CasterList_SOBs.size()));
} }
void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow() void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow()
@ -851,7 +1024,56 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow()
{ {
//SoftShow(pVI, sob.m_bSOBVisible); //SoftShow(pVI, sob.m_bSOBVisible);
bool bVisible = m_BF_visible_SOBs.GetBit(ID) != 0; 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]; int ID = m_MasterList_SOBs[n];
LSob &sob = m_SOBs[ID]; LSob &sob = m_SOBs[ID];
// show / hide is relatively expensive because of propagating messages between nodes ...
// should be minimized
sob.Show(true); 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) 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) if (m_bDebugPlanes)
{ {
Vector3 ptCam = cam.m_ptPos; Vector3 ptCam = cam.m_ptPos;
@ -949,6 +1202,8 @@ void LRoomManager::FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom)
im->end(); im->end();
} }
} }
void LRoomManager::_notification(int p_what) { 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_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_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_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 // functions to add dynamic objects to the culling system

View File

@ -90,11 +90,21 @@ private:
// static objects // static objects
LVector<LSob> m_SOBs; LVector<LSob> m_SOBs;
// lights
LVector<LLight> m_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 // master list of shadow casters for each room
LVector<uint32_t> m_ShadowCasters_SOB; LVector<uint32_t> m_ShadowCasters_SOB;
// master list of casters for each light
LVector<uint32_t> m_LightCasters_SOB;
protected: protected:
static void _bind_methods(); static void _bind_methods();
void _notification(int p_what); void _notification(int p_what);
@ -127,8 +137,10 @@ private:
bool DobRegister(Spatial * pDOB, float radius, int iRoom); bool DobRegister(Spatial * pDOB, float radius, int iRoom);
ObjectID DobRegister_FindVIRecursive(Node * pNode) const; ObjectID DobRegister_FindVIRecursive(Node * pNode) const;
bool DobTeleport(Spatial * pDOB, int iNewRoomID); bool DobTeleport(Spatial * pDOB, int iNewRoomID);
bool LightCreate(Light * pLight, int roomID);
void CreateDebug(); void CreateDebug();
void DobChangeVisibility(Spatial * pDOB, const LRoom * pOld, const LRoom * pNew); void DobChangeVisibility(Spatial * pDOB, const LRoom * pOld, const LRoom * pNew);
void ReleaseResources(bool bPrepareConvert);
// helper funcs // helper funcs
@ -147,12 +159,16 @@ public:
// whether debug planes is switched on // whether debug planes is switched on
bool m_bDebugPlanes; bool m_bDebugPlanes;
bool m_bDebugBounds; bool m_bDebugBounds;
bool m_bDebugLights;
// the planes are shown as a list of lines from the camera to the portal verts // the planes are shown as a list of lines from the camera to the portal verts
LVector<Vector3> m_DebugPlanes; LVector<Vector3> m_DebugPlanes;
LVector<Vector3> m_DebugPortalLightPlanes;
private: private:
ObjectID m_ID_DebugPlanes; ObjectID m_ID_DebugPlanes;
ObjectID m_ID_DebugBounds; ObjectID m_ID_DebugBounds;
ObjectID m_ID_DebugLights;
Ref<SpatialMaterial> m_mat_Debug_Planes; Ref<SpatialMaterial> m_mat_Debug_Planes;
Ref<SpatialMaterial> m_mat_Debug_Bounds; Ref<SpatialMaterial> m_mat_Debug_Bounds;
@ -179,6 +195,7 @@ public:
void rooms_set_active(bool bActive); void rooms_set_active(bool bActive);
void rooms_set_debug_planes(bool bActive); void rooms_set_debug_planes(bool bActive);
void rooms_set_debug_bounds(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) // 0 to 6 .. defaults to 4 which is (2) in our priorities (i.e. 6 - level)
void rooms_set_logging(int level); void rooms_set_logging(int level);