From 0f8094e9f3a640839f76bc3eeeecfa7787fbc4eb Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Tue, 1 Oct 2019 15:27:32 +0100 Subject: [PATCH] Backup while investigate Godot light cull bug --- README.md | 3 +- lportal.cpp | 104 ++++++++++-- lportal.h | 25 ++- lroom.cpp | 65 ++++++-- lroom.h | 13 +- lroom_converter.cpp | 395 +++++++++++++++++++++++++++++++++++++++++--- lroom_converter.h | 10 +- lroom_manager.cpp | 250 ++++++++++++++++++++++++++-- lroom_manager.h | 14 ++ 9 files changed, 805 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 5656aaf..9444e69 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,7 @@ Video of initial testing:\ https://www.youtube.com/watch?v=xF_3Fe2HRdk \ https://www.youtube.com/watch?v=NmlWkkhGoJA -_Feel free to leave suggestions / feature requests on the issue tracker, especially regarding ease of use._\ -_If you are interested in integrating LPortal into your game, feel free to get in touch, e.g. in the issue tracker, and I will endeavour to help._ +_Feel free to leave suggestions / feature requests on the issue tracker, especially regarding ease of use._ ## Current status The basic system is mostly working. I am now testing / polishing the interface and adding debugging. I will leave PVS and closable portals until after the first release. diff --git a/lportal.cpp b/lportal.cpp index d393a97..fe5ba1c 100644 --- a/lportal.cpp +++ b/lportal.cpp @@ -23,6 +23,25 @@ #include "lroom.h" #include "ldebug.h" #include "lroom_manager.h" +#include "scene/3d/light.h" + +void LLight::SetDefaults() +{ + m_GodotID = 0; + m_eType = LT_DIRECTIONAL; + m_fSpread = 0.0f; // for spotlight + m_fMaxDist = 100.0f; + m_RoomID = -1; +} + + +Light * LLight::GetGodotLight() +{ + Object * pObj = ObjectDB::get_instance(m_GodotID); + Light * p = Object::cast_to(pObj); + return p; +} + bool LPortal::NameStartsWith(Node * pNode, String szSearch) @@ -71,38 +90,91 @@ String LPortal::FindNameAfter(Node * pNode, String szStart) ////////////////////////////////////////////////////////// +void LPortal::Debug_CheckPlaneValidity(const Plane &p) const +{ + assert (p.distance_to(m_ptCentre) < 0.0f); +} + + // preprocess -void LPortal::AddLightPlanes(const LLight &light, LVector &planes) const +void LPortal::AddLightPlanes(LRoomManager &manager, const LLight &light, LVector &planes, bool bReverse) const { const Vector &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 max_points) + nPoints = max_points; + + // transform pushed points + Vector3 ptPush = light.m_ptDir * 2.0; + + for (int n=0; n &planes) const; - void AddLightPlanes(const LLight &light, LVector &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 &planes, bool bReverse) const; // normal determined by winding order Vector m_ptsWorld; @@ -87,6 +107,9 @@ public: // useful funcs static bool NameStartsWith(Node * pNode, String szSearch); static String FindNameAfter(Node * pNode, String szStart); + +private: + void Debug_CheckPlaneValidity(const Plane &p) const; }; diff --git a/lroom.cpp b/lroom.cpp index cbb2860..1442fae 100644 --- a/lroom.cpp +++ b/lroom.cpp @@ -132,29 +132,48 @@ LRoom * LRoom::DOB_Update(LRoomManager &manager, Spatial * pDOB) // objects can still be rendered outside immediate view for casting shadows. // All objects in view (that are set to cast shadows) should cast shadows, so the actual // shown objects are a superset of the softshown. -void LRoom::SoftShow(VisualInstance * pVI, bool bShow) +void LRoom::SoftShow(VisualInstance * pVI, uint32_t show_flags) { + // hijack this layer number uint32_t mask = pVI->get_layer_mask(); uint32_t orig_mask = mask; - const int SOFT_SHOW_MASK = 1 << SOFT_SHOW_BIT; - const int SOFT_HIDE_MASK = 1 << SOFT_HIDE_BIT; + // debug, to check shadow casters are correct for different light types +//#define DEBUG_SHOW_CASTERS_ONLY +#ifdef DEBUG_SHOW_CASTERS_ONLY + bShow = true; if (bShow) { - // set - mask |= SOFT_SHOW_MASK; - // clear - mask &= ~(1 | SOFT_HIDE_MASK); + } +#else + if (show_flags & LAYER_MASK_CAMERA) + mask |= LAYER_MASK_CAMERA; // set else - { - // set - mask |= SOFT_HIDE_MASK; - // clear - mask &= ~(1 | SOFT_SHOW_MASK); - } + mask &= ~LAYER_MASK_CAMERA; // clear + + if (show_flags & LAYER_MASK_LIGHT) + mask |= LAYER_MASK_LIGHT; + else + mask &= ~LAYER_MASK_LIGHT; + +// if (bShow) +// { +// // set +// mask |= SOFT_SHOW_MASK; +// // clear +// mask &= ~(1 | SOFT_HIDE_MASK); +// } +// else +// { +// // set +// mask |= SOFT_HIDE_MASK; +// // clear +// mask &= ~(1 | SOFT_SHOW_MASK); +// } +#endif // noop? don't touch the visual server if no change to mask @@ -163,9 +182,7 @@ void LRoom::SoftShow(VisualInstance * pVI, bool bShow) pVI->set_layer_mask(mask); -// pVI->set_layer_mask_bit(0, false); -// pVI->set_layer_mask_bit(SOFT_HIDE_BIT, bShow == false); -// pVI->set_layer_mask_bit(SOFT_SHOW_BIT, bShow); + // test the visual server - NOT A BOTTLENECK. set_layer_mask is cheap } @@ -174,6 +191,18 @@ void LRoom::AddShadowCasters(LRoomManager &manager) { LPRINT(2, "ADDSHADOWCASTERS room " + get_name() + ", " + itos(m_iNumShadowCasters_SOB) + " shadow casters"); + // add all the active lights in this room + for (int n=0; nget_name()); + //LPRINT(2, "\t" + itos(sobID) + ", ALREADY CASTER " + manager.m_SOBs[sobID].GetSpatial()->get_name()); } } @@ -215,6 +244,8 @@ void LRoom::AddShadowCasters(LRoomManager &manager) // (it might be expensive) void LRoom::FinalizeVisibility(LRoomManager &manager) { + // make sure all lights needed are turned on + // int last_sob = m_iFirstSOB + m_iNumSOBs; // for (int n=m_iFirstSOB; n m_DOBs; + // local lights affecting this room + LVector m_LocalLights; + // portals are stored in the manager in a contiguous list int m_iFirstPortal; int m_iNumPortals; @@ -130,7 +137,7 @@ public: // instead of directly showing and hiding objects we now set their layer, // and the camera will hide them with a cull mask. This is so that // objects can still be rendered outside immediate view for casting shadows. - static void SoftShow(VisualInstance * pVI, bool bShow); + static void SoftShow(VisualInstance * pVI, uint32_t show_flags); private: // whether lportal thinks this room is currently visible diff --git a/lroom_converter.cpp b/lroom_converter.cpp index 612a7d4..3b8638f 100644 --- a/lroom_converter.cpp +++ b/lroom_converter.cpp @@ -25,6 +25,7 @@ #include "scene/3d/mesh_instance.h" #include "core/math/quick_hull.h" #include "ldebug.h" +#include "scene/3d/light.h" // save typing, I am lazy #define LMAN m_pManager @@ -39,17 +40,21 @@ void LRoomConverter::Convert(LRoomManager &manager) LPRINT(5, "running convert"); LMAN = &manager; + + // force clear all arrays + manager.ReleaseResources(true); + int count = CountRooms(); - LMAN->m_SOBs.clear(); - LMAN->m_ShadowCasters_SOB.clear(); + //LMAN->m_SOBs.clear(); + //LMAN->m_ShadowCasters_SOB.clear(); int num_global_lights = LMAN->m_Lights.size(); // make sure bitfield is right size for number of rooms LMAN->m_BF_visible_rooms.Create(count); - LMAN->m_Rooms.clear(true); +// LMAN->m_Rooms.clear(true); LMAN->m_Rooms.resize(count); m_TempRooms.clear(true); @@ -68,7 +73,11 @@ void LRoomConverter::Convert(LRoomManager &manager) LMAN->m_BF_master_SOBs.Create(num_sobs); LMAN->m_BF_master_SOBs_prev.Create(num_sobs); + LMAN->m_BF_ActiveLights.Create(LMAN->m_Lights.size()); + LMAN->m_BF_ActiveLights_prev.Create(LMAN->m_Lights.size()); + // must be done after the bitfields + Convert_Lights(); Convert_ShadowCasters(); // hide all in preparation for first frame @@ -78,7 +87,7 @@ void LRoomConverter::Convert(LRoomManager &manager) m_TempRooms.clear(true); // clear out the local room lights, leave only global lights - LMAN->m_Lights.resize(num_global_lights); + //LMAN->m_Lights.resize(num_global_lights); Lawn::LDebug::m_bRunning = true; } @@ -118,6 +127,25 @@ int LRoomConverter::FindRoom_ByName(String szName) const return -1; } +void LRoomConverter::Convert_Room_SetDefaultCullMask_Recursive(Node * pParent) +{ + int nChildren = pParent->get_child_count(); + for (int n=0; nget_child(n); + + // default cull mask should always be visible to camera and lights + VisualInstance * pVI = Object::cast_to(pChild); + if (pVI) + { +// LRoom::SoftShow(pVI, LRoom::LAYER_MASK_CAMERA | LRoom::LAYER_MASK_LIGHT); + } + + Convert_Room_SetDefaultCullMask_Recursive(pChild); + } +} + + void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &lroom, LAABB &bb_room) { int nChildren = pParent->get_child_count(); @@ -135,10 +163,18 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l if (Node_IsBound(pChild)) continue; + // lights + if (Node_IsLight(pChild)) + { + LRoom_DetectedLight(lroom, pChild); + continue; + } VisualInstance * pVI = Object::cast_to(pChild); if (pVI) { + + LPRINT(2, "\t\tFound VI : " + pVI->get_name()); @@ -153,6 +189,9 @@ void LRoomConverter::Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &l //lroom.m_SOBs.push_back(sob); LRoom_PushBackSOB(lroom, sob); + + // take away layer 0 from the sob, so it can be culled effectively + pVI->set_layer_mask(0); } else { @@ -193,6 +232,9 @@ bool LRoomConverter::Convert_Room(Spatial * pNode, int lroomID) LAABB bb_room; bb_room.SetToMaxOpposite(); + // set default cull masks + Convert_Room_SetDefaultCullMask_Recursive(pNode); + // recursively find statics Convert_Room_FindObjects_Recursive(pNode, lroom, bb_room); @@ -309,15 +351,209 @@ void LRoomConverter::Convert_HideAll() } } +void LRoomConverter::Convert_Lights() +{ + // trace local lights out from rooms and add to each room the light affects + for (int n=0; nm_Lights.size(); n++) + { + LLight &l = LMAN->m_Lights[n]; + if (l.IsGlobal()) + continue; // ignore globals .. affect all rooms + + Light_Trace(n); + } +} + +void LRoomConverter::Light_Trace(int iLightID) +{ + + LLight &l = LMAN->m_Lights[iLightID]; + + LPRINT(5,"\nLight_Trace " + itos (iLightID) + " direction " + l.m_ptDir); + + // reset the planes pool for each render out from the source room + LMAN->m_Pool.Reset(); + + + // the first set of planes are blank + unsigned int pool_member = LMAN->m_Pool.Request(); + assert (pool_member != -1); + + LVector &planes = LMAN->m_Pool.Get(pool_member); + planes.clear(); + + Lawn::LDebug::m_iTabDepth = 0; + + Light_TraceRecursive(0, LMAN->m_Rooms[l.m_RoomID], l, iLightID, planes); +} + + +void LRoomConverter::Light_TraceRecursive(int depth, LRoom &lroom, const LLight &light, int iLightID, const LVector &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; nm_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; lPortal_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 &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; nm_Rooms.size(); n++) + + for (int l=0; lm_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; nm_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; im_Lights.size(); n++) - { - LRoom_FindShadowCasters_FromLight(lroom, LMAN->m_Lights[n]); - } +//void LRoomConverter::LRoom_FindShadowCasters(LRoom &lroom, int lightID, const LLight &light) +//{ +// // each global light, and each light affecting this room +// for (int n=0; nm_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 0.0f) +// if (r_max < 0.0f) { + //LPRINT_RUN(2, "\tR_MIN is " + String(Variant(r_min)) + " R_MAX is " + String(Variant(r_max))+ ", for plane " + itos(p)); + bShow = false; break; } @@ -518,6 +777,10 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int LPRINT_RUN(2, "\tcaster " + itos(n) + ", " + sob.GetSpatial()->get_name()); LRoom_AddShadowCaster_SOB(source_lroom, n); } + else + { + //LPRINT_RUN(2, "\tculled " + itos(n) + ", " + sob.GetSpatial()->get_name()); + } } // look through every portal out @@ -530,8 +793,21 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int LPRINT_RUN(2, "\tPORTAL " + itos (n) + " (" + itos(portalID) + ") " + port.get_name() + " normal " + port.m_Plane.normal); // cull with light direction - float dot = port.m_Plane.normal.dot(light.m_ptDir); - if (dot <= 0.0f) + float dot; + if (light.m_eType == LLight::LT_DIRECTIONAL) + { + dot = port.m_Plane.normal.dot(light.m_ptDir); + } + else + { + // cull with light direction to portal + Vector3 ptLightToPort = port.m_ptCentre - light.m_ptPos; + dot = port.m_Plane.normal.dot(ptLightToPort); + } + + +// float dot = port.m_Plane.normal.dot(light.m_ptDir); + if (dot >= 0.0f) { LPRINT_RUN(2, "\t\tCULLED (wrong direction)"); continue; @@ -583,7 +859,7 @@ void LRoomConverter::LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int new_planes.copy_from(planes); // add the planes for the portal - port.AddLightPlanes(light, new_planes); + port.AddLightPlanes(*LMAN, light, new_planes, true); LRoom_FindShadowCasters_Recursive(source_lroom, depth + 1, linked_room, light, new_planes); // for debugging need to reset tab depth @@ -696,6 +972,71 @@ void LRoomConverter::LRoom_MakePortalFinalList(LRoom &lroom, LTempRoom &troom) } } + +void LRoomConverter::LRoom_DetectedLight(LRoom &lroom, Node * pNode) +{ + Light * pLight = Object::cast_to(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(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(pNode); + if (pOL) + { + LPRINT(2, "\tOMNILIGHT detected " + pNode->get_name()); + l.m_eType = LLight::LT_OMNI; + bOK = true; + } + + DirectionalLight * pDL = Object::cast_to(pNode); + if (pDL) + { + LPRINT(2, "\tDIRECTIONALLIGHT detected " + pNode->get_name()); + l.m_eType = LLight::LT_DIRECTIONAL; + bOK = true; + } + + // don't add if not recognised + if (!bOK) + { + LPRINT(2, "\tLIGHT type unrecognised " + pNode->get_name()); + return; + } + + + // turn the local light off to start with + pLight->hide(); + + LMAN->m_Lights.push_back(l); + */ +} + // found a portal mesh! create a matching LPortal void LRoomConverter::LRoom_DetectedPortalMesh(LRoom &lroom, LTempRoom &troom, MeshInstance * pMeshInstance, String szLinkRoom) { @@ -792,6 +1133,16 @@ void LRoomConverter::TRoom_MakeOppositePortal(const LPortal &port, int iRoomOrig /////////////////////////////////////////////////// // helper +bool LRoomConverter::Node_IsLight(Node * pNode) const +{ + Light * pLight = Object::cast_to(pNode); + if (!pLight) + return false; + + return true; +} + + bool LRoomConverter::Node_IsRoom(Node * pNode) const { Spatial * pSpat = Object::cast_to(pNode); diff --git a/lroom_converter.h b/lroom_converter.h index ff4fdf3..469ad50 100644 --- a/lroom_converter.h +++ b/lroom_converter.h @@ -89,11 +89,13 @@ private: void Convert_Rooms(); bool Convert_Room(Spatial * pNode, int lroomID); void Convert_Room_FindObjects_Recursive(Node * pParent, LRoom &lroom, LAABB &bb_room); + void Convert_Room_SetDefaultCullMask_Recursive(Node * pParent); void Convert_Portals(); void Convert_Bounds(); bool Convert_Bound(LRoom &lroom, MeshInstance * pMI); void Convert_ShadowCasters(); + void Convert_Lights(); void Convert_HideAll(); @@ -104,8 +106,13 @@ private: LPortal * LRoom_RequestNewPortal(LRoom &lroom); void LRoom_PushBackSOB(LRoom &lroom, const LSob &sob); + // lights + void LRoom_DetectedLight(LRoom &lroom, Node * pNode); + void Light_Trace(int iLightID); + void Light_TraceRecursive(int depth, LRoom &lroom, const LLight &light, int iLightID, const LVector &planes); + // shadows - void LRoom_FindShadowCasters(LRoom &lroom); +// void LRoom_FindShadowCasters(LRoom &lroom, int lightID, const LLight &light); void LRoom_FindShadowCasters_FromLight(LRoom &lroom, const LLight &light); void LRoom_FindShadowCasters_Recursive(LRoom &source_lroom, int depth, LRoom &lroom, const LLight &light, const LVector &planes); void LRoom_AddShadowCaster_SOB(LRoom &lroom, int sobID); @@ -118,6 +125,7 @@ private: bool Node_IsPortal(Node * pNode) const; bool Node_IsBound(Node * pNode) const; bool Node_IsIgnore(Node * pNode) const; + bool Node_IsLight(Node * pNode) const; int FindRoom_ByName(String szName) const; diff --git a/lroom_manager.cpp b/lroom_manager.cpp index 12b92d1..905ad82 100644 --- a/lroom_manager.cpp +++ b/lroom_manager.cpp @@ -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(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(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; nget_child_count(); n++) @@ -453,6 +470,79 @@ bool LRoomManager::dob_unregister(Node * pDOB) } +// common stuff for global and local light creation +bool LRoomManager::LightCreate(Light * pLight, int roomID) +{ + // set culling flag for light + // 1 is for lighting objects outside the room system + pLight->set_cull_mask(1 | LRoom::LAYER_MASK_LIGHT); + + // create new light + LLight l; + l.SetDefaults(); + l.m_GodotID = pLight->get_instance_id(); + + // direction + Transform tr = pLight->get_global_transform(); + l.m_ptPos = tr.origin; + l.m_ptDir = -tr.basis.get_axis(2); // or possibly get_axis .. z is what we want + l.m_RoomID = roomID; + + l.m_fMaxDist = pLight->get_param(Light::PARAM_SHADOW_MAX_DISTANCE); + + + //l.m_eType = LLight::LT_DIRECTIONAL; + + //m_Lights.push_back(l); + + + bool bOK = false; + + // what kind of light? + SpotLight * pSL = Object::cast_to(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(pLight); + if (pOL) + { + LPRINT(2, "\tOMNILIGHT detected " + pLight->get_name()); + l.m_eType = LLight::LT_OMNI; + bOK = true; + } + + DirectionalLight * pDL = Object::cast_to(pLight); + if (pDL) + { + LPRINT(2, "\tDIRECTIONALLIGHT detected " + pLight->get_name()); + l.m_eType = LLight::LT_DIRECTIONAL; + bOK = true; + } + + // don't add if not recognised + if (!bOK) + { + LPRINT(2, "\tLIGHT type unrecognised " + pLight->get_name()); + return false; + } + + + // turn the local light off to start with + if (!l.IsGlobal()) + pLight->hide(); + + m_Lights.push_back(l); + + return true; +} + + bool LRoomManager::light_register(Node * pLightNode) { if (!pLightNode) @@ -470,18 +560,27 @@ bool LRoomManager::light_register(Node * pLightNode) return false; } - // create new light - LLight l; - l.m_GodotID = pLight->get_instance_id(); + return LightCreate(pLight, -1); - // direction - Transform tr = pLight->get_global_transform(); - l.m_ptPos = tr.origin; - l.m_ptDir = tr.basis.get_axis(2); // or possibly get_axis .. z is what we want +// // set culling flag for light +// // 1 is for lighting objects outside the room system +// pLight->set_cull_mask(1 | LRoom::LAYER_MASK_LIGHT); - m_Lights.push_back(l); +// // create new light +// LLight l; +// l.SetDefaults(); +// l.m_GodotID = pLight->get_instance_id(); - return true; +// // direction +// Transform tr = pLight->get_global_transform(); +// l.m_ptPos = tr.origin; +// l.m_ptDir = -tr.basis.get_axis(2); // or possibly get_axis .. z is what we want +// l.m_RoomID = -1; +// l.m_eType = LLight::LT_DIRECTIONAL; + +// m_Lights.push_back(l); + +// return true; } @@ -523,6 +622,21 @@ Node * LRoomManager::rooms_get_room(int room_id) return pRoom->GetGodotRoom(); } +void LRoomManager::rooms_set_debug_lights(bool bActive) +{ + m_bDebugLights = bActive; + Object * pObj = ObjectDB::get_instance(m_ID_DebugLights); + ImmediateGeometry * im = Object::cast_to(pObj); + if (!im) + return; + + if (bActive) + im->show(); + else + im->hide(); +} + + void LRoomManager::rooms_set_debug_bounds(bool bActive) { m_bDebugBounds = bActive; @@ -610,7 +724,8 @@ void LRoomManager::rooms_set_camera(Node * pCam) m_ID_camera = pCam->get_instance_id(); // new .. select the cull layer - pCamera->set_cull_mask_bit(LRoom::SOFT_HIDE_BIT, false); + // 1 is for showing objects outside the room system + pCamera->set_cull_mask(1 | LRoom::LAYER_MASK_CAMERA); // use this temporarily to force debug // rooms_log_frame(); @@ -626,7 +741,30 @@ void LRoomManager::rooms_convert() // free memory for current set of rooms, prepare for converting a new game level void LRoomManager::rooms_release() { - m_Lights.clear(); + ReleaseResources(false); +} + +void LRoomManager::ReleaseResources(bool bPrepareConvert) +{ + m_ShadowCasters_SOB.clear(); + m_Rooms.clear(true); + m_Portals.clear(true); + m_SOBs.clear(); + + if (!bPrepareConvert) + m_Lights.clear(); + + m_ActiveLights.clear(); + m_ActiveLights_prev.clear(); + + m_VisibleRoomList_A.clear(); + m_VisibleRoomList_B.clear(); + + m_MasterList_SOBs.clear(); + m_MasterList_SOBs_prev.clear(); + + m_VisibleList_SOBs.clear(); + m_CasterList_SOBs.clear(); } @@ -645,6 +783,7 @@ void LRoomManager::FrameUpdate_Prepare() // keep previous m_BF_master_SOBs_prev.CopyFrom(m_BF_master_SOBs); + m_BF_master_SOBs.Blank(); // note this can be done more efficiently with swapping pointer m_MasterList_SOBs_prev.copy_from(m_MasterList_SOBs); @@ -655,7 +794,12 @@ void LRoomManager::FrameUpdate_Prepare() m_BF_caster_SOBs.Blank(); m_BF_visible_SOBs.Blank(); - m_BF_master_SOBs.Blank(); + + // lights + m_BF_ActiveLights_prev.CopyFrom(m_BF_ActiveLights); + m_ActiveLights_prev.copy_from(m_ActiveLights); + m_ActiveLights.clear(); + m_BF_ActiveLights.Blank(); // as we hit visible rooms we will mark them in a bitset, so we can hide any rooms // that are showing that haven't been hit this frame @@ -851,7 +995,53 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_SoftShow() { //SoftShow(pVI, sob.m_bSOBVisible); bool bVisible = m_BF_visible_SOBs.GetBit(ID) != 0; - LRoom::SoftShow(pVI, bVisible); + bool bCaster = m_BF_caster_SOBs.GetBit(ID) != 0; + + uint32_t flags = 0; + if (bVisible) flags |= LRoom::LAYER_MASK_CAMERA; + if (bCaster) flags |= LRoom::LAYER_MASK_LIGHT; + + LRoom::SoftShow(pVI, flags); + } + } + + // lights + for (int n=0; nshow(); + + // 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; nhide(); + } } } @@ -886,7 +1076,17 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_WithinRooms() { int ID = m_MasterList_SOBs[n]; LSob &sob = m_SOBs[ID]; + + // show / hide is relatively expensive because of propagating messages between nodes ... + // should be minimized sob.Show(true); + + // see how expensive show is +// for (int t=0; t<10000; t++) +// { +// sob.Show(false); +// sob.Show(true); +// } } } @@ -894,6 +1094,27 @@ void LRoomManager::FrameUpdate_FinalizeVisibility_WithinRooms() void LRoomManager::FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom) { + // light portal planes + if (m_bDebugLights) + { + Object * pObj = ObjectDB::get_instance(m_ID_DebugLights); + ImmediateGeometry * im = Object::cast_to(pObj); + if (!im) + return; + + im->clear(); + + im->begin(Mesh::PRIMITIVE_TRIANGLES, NULL); + + int nVerts = m_DebugPortalLightPlanes.size(); + + for (int n=0; nadd_vertex(m_DebugPortalLightPlanes[n]); + } + im->end(); + } + if (m_bDebugPlanes) { Vector3 ptCam = cam.m_ptPos; @@ -949,6 +1170,8 @@ void LRoomManager::FrameUpdate_DrawDebug(const LCamera &cam, const LRoom &lroom) im->end(); } + + } void LRoomManager::_notification(int p_what) { @@ -989,6 +1212,7 @@ void LRoomManager::_bind_methods() ClassDB::bind_method(D_METHOD("rooms_set_active"), &LRoomManager::rooms_set_active); ClassDB::bind_method(D_METHOD("rooms_set_debug_planes"), &LRoomManager::rooms_set_debug_planes); ClassDB::bind_method(D_METHOD("rooms_set_debug_bounds"), &LRoomManager::rooms_set_debug_bounds); + ClassDB::bind_method(D_METHOD("rooms_set_debug_lights"), &LRoomManager::rooms_set_debug_lights); // functions to add dynamic objects to the culling system diff --git a/lroom_manager.h b/lroom_manager.h index d71a6f5..06f370d 100644 --- a/lroom_manager.h +++ b/lroom_manager.h @@ -90,7 +90,14 @@ private: // static objects LVector m_SOBs; + + // lights LVector m_Lights; + // active lights + LVector m_ActiveLights; + LVector 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 m_ShadowCasters_SOB; @@ -127,8 +134,10 @@ private: bool DobRegister(Spatial * pDOB, float radius, int iRoom); ObjectID DobRegister_FindVIRecursive(Node * pNode) const; bool DobTeleport(Spatial * pDOB, int iNewRoomID); + bool LightCreate(Light * pLight, int roomID); void CreateDebug(); void DobChangeVisibility(Spatial * pDOB, const LRoom * pOld, const LRoom * pNew); + void ReleaseResources(bool bPrepareConvert); // helper funcs @@ -147,12 +156,16 @@ public: // whether debug planes is switched on bool m_bDebugPlanes; bool m_bDebugBounds; + bool m_bDebugLights; // the planes are shown as a list of lines from the camera to the portal verts LVector m_DebugPlanes; + LVector m_DebugPortalLightPlanes; + private: ObjectID m_ID_DebugPlanes; ObjectID m_ID_DebugBounds; + ObjectID m_ID_DebugLights; Ref m_mat_Debug_Planes; Ref m_mat_Debug_Bounds; @@ -179,6 +192,7 @@ public: void rooms_set_active(bool bActive); void rooms_set_debug_planes(bool bActive); void rooms_set_debug_bounds(bool bActive); + void rooms_set_debug_lights(bool bActive); // 0 to 6 .. defaults to 4 which is (2) in our priorities (i.e. 6 - level) void rooms_set_logging(int level);