From c0a93612ae08c130a15bec6104120e35d7a774a1 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 29 Nov 2024 00:09:29 +0300 Subject: [PATCH 01/26] Dynamic spotlight (#1488) * Initial commit * Update CHANGELOG.md * Provide helper method to convert rotation to directional vector * Remove unneeded arguments, add missing function registration * Fixed direction * Fixed incorrect behaviour of Moveable:GetJointRotation() function * Add optional castShadow flag to light functions * Fixed spotlight shadows * Cleanup * Fix spotlight shadows again * Interpolate dynamic lights, if unique ID is given * Use name instead of unique ID * Update docs * Update CHANGELOG.md * Fix incorrect variable type * Update CHANGELOG.md --- CHANGELOG.md | 5 + Documentation/doc/1 modules/Effects.html | 77 +++++- Documentation/doc/2 classes/Flow.Level.html | 20 ++ .../doc/3 primitive classes/Rotation.html | 25 ++ TombEngine/Game/effects/effects.cpp | 15 +- TombEngine/Game/effects/effects.h | 4 +- TombEngine/Game/effects/tomb4fx.cpp | 2 +- TombEngine/Objects/TR2/Entity/Bartoli.cpp | 10 +- TombEngine/Objects/TR2/Entity/Dragon.cpp | 8 +- TombEngine/Objects/TR3/Entity/SealMutant.cpp | 4 +- TombEngine/Objects/TR4/Entity/tr4_demigod.cpp | 2 +- .../Objects/TR5/Entity/tr5_roman_statue.cpp | 4 +- TombEngine/Objects/TR5/Trap/LaserBeam.cpp | 4 +- TombEngine/Renderer/Renderer.cpp | 29 +- TombEngine/Renderer/Renderer.h | 8 +- TombEngine/Renderer/RendererCompatibility.cpp | 4 + TombEngine/Renderer/RendererDraw.cpp | 128 ++++++--- TombEngine/Renderer/RendererFrame.cpp | 6 +- TombEngine/Renderer/RendererInit.cpp | 4 +- .../Renderer/Structures/RendererLight.h | 5 + .../Scripting/Internal/ReservedScriptNames.h | 5 + .../Internal/TEN/Effects/EffectsFunctions.cpp | 37 ++- .../TEN/Objects/Moveable/MoveableObject.cpp | 15 +- .../Internal/TEN/Rotation/Rotation.cpp | 19 ++ .../Internal/TEN/Rotation/Rotation.h | 2 + TombEngine/Shaders/Rooms.fx | 15 +- TombEngine/Shaders/ShaderLight.hlsli | 254 ++++++++---------- TombEngine/Shaders/Shadows.hlsli | 158 +++++------ TombEngine/Specific/trutils.cpp | 15 ++ TombEngine/Specific/trutils.h | 1 + 30 files changed, 562 insertions(+), 323 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89614c17d..8e6a4cbba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed original issue with deactivation of Dart Emitter. * Fixed Lens Flare object not functioning properly. * Fixed lens flares not being occluded by static meshes and moveables. +* Fixed spotlight shadows. * Fixed Skeleton and Mummy not reacting to shotgun hits. ### New Features @@ -43,11 +44,15 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Added Moveable:GetCollidable() and Moveable:SetCollidable() functions. * Added Flow.GetFreezeMode() and Flow.SetFreezeMode() functions. * Added Flow.GetNextLevel() function to get script entry for incoming level, if it's about to start. +* Added Effects.EmitSpotLight() function for directional spotlights. +* Added optional cast shadow and name parameters for Effects.EmitLight() function. * Added Effects.GetWind() function to get current wind speed vector. +* Added Rotation:Direction() method to get directional vector. * Added support for transparency value in DisplayString class. * Added extra argument for SetAmbientTrack() function to specify if new ambient track should play from the beginning. * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. * Fixed DisplayString class not supporting some Unicode characters and empty lines in multiline strings. +* Fixed incorrect behaviour of Moveable:GetJointRotation() function. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. * Fixed Util.HasLineOfSight() not taking static meshes into consideration. * Fixed collision callbacks not properly clearing after leveljump. diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html index 0e51a9a27..e1c3b28bb 100644 --- a/Documentation/doc/1 modules/Effects.html +++ b/Documentation/doc/1 modules/Effects.html @@ -125,10 +125,14 @@ Emit a shockwave, similar to that seen when a harpy projectile hits something. - EmitLight(pos, color, radius) + EmitLight(pos[, color][, radius][, shadows][, name]) Emit dynamic light that lasts for a single frame. + EmitSpotLight(pos, dir[, color][, radius][, falloff][, distance][, shadows][, name]) + Emit dynamic directional spotlight that lasts for a single frame. + + EmitBlood(pos, count) Emit blood. @@ -363,7 +367,7 @@
- EmitLight(pos, color, radius) + EmitLight(pos[, color][, radius][, shadows][, name])
Emit dynamic light that lasts for a single frame. @@ -375,17 +379,84 @@ + + +
+
+ + EmitSpotLight(pos, dir[, color][, radius][, falloff][, distance][, shadows][, name]) +
+
+ Emit dynamic directional spotlight that lasts for a single frame. + If you want a light that sticks around, you must call this each frame. + + + +

Parameters:

+ diff --git a/Documentation/doc/2 classes/Flow.Level.html b/Documentation/doc/2 classes/Flow.Level.html index 7872a908d..6fa508fd0 100644 --- a/Documentation/doc/2 classes/Flow.Level.html +++ b/Documentation/doc/2 classes/Flow.Level.html @@ -110,6 +110,10 @@

Members

+ + + + @@ -206,6 +210,22 @@

Members

+
+ + nameKey +
+
+ (string) string key for the level's (localised) name. + Corresponds to an entry in strings.lua. + + + + + + + + +
scriptFile diff --git a/Documentation/doc/3 primitive classes/Rotation.html b/Documentation/doc/3 primitive classes/Rotation.html index 9fffd646f..fc7ffcb82 100644 --- a/Documentation/doc/3 primitive classes/Rotation.html +++ b/Documentation/doc/3 primitive classes/Rotation.html @@ -130,6 +130,10 @@
+ + + + @@ -232,6 +236,27 @@ + +
+ + Direction() +
+
+ Converts rotation to a direction normal. + + + + +

Returns:

+
    + + Vec3 + resulting normal calculated from this rotation. +
+ + + +
diff --git a/TombEngine/Game/effects/effects.cpp b/TombEngine/Game/effects/effects.cpp index 9a47a93cb..80566e8da 100644 --- a/TombEngine/Game/effects/effects.cpp +++ b/TombEngine/Game/effects/effects.cpp @@ -1247,19 +1247,20 @@ void KillAllCurrentItems(short itemNumber) // TODO: Reimplement this functionality. } -// TODO: Rename to SpawnDynamicLight(). -void TriggerDynamicLight(const Vector3& pos, const Color& color, float falloff) +void TriggerDynamicPointLight(const Vector3& pos, const Color& color, float falloff, bool castShadows, int hash) { - g_Renderer.AddDynamicLight( - pos.x, pos.y, pos.z, - falloff * UCHAR_MAX, - color.x * UCHAR_MAX, color.y * UCHAR_MAX, color.z * UCHAR_MAX); + g_Renderer.AddDynamicPointLight(pos, falloff , color, castShadows, hash); +} + +void TriggerDynamicSpotLight(const Vector3& pos, const Vector3& dir, const Color& color, float radius, float falloff, float distance, bool castShadows, int hash) +{ + g_Renderer.AddDynamicSpotLight(pos, dir, radius, falloff, distance, color, castShadows, hash); } // Deprecated. Use above version instead. void TriggerDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b) { - g_Renderer.AddDynamicLight(x, y, z, falloff, r, g, b); + g_Renderer.AddDynamicPointLight(Vector3(x, y, z), (float)(falloff * UCHAR_MAX), Color(r / (float)UCHAR_MAX, g / (float)UCHAR_MAX, b / (float)UCHAR_MAX), false); } void SpawnPlayerWaterSurfaceEffects(const ItemInfo& item, int waterHeight, int waterDepth) diff --git a/TombEngine/Game/effects/effects.h b/TombEngine/Game/effects/effects.h index e980a8d22..3da3f6ab9 100644 --- a/TombEngine/Game/effects/effects.h +++ b/TombEngine/Game/effects/effects.h @@ -300,6 +300,8 @@ void ControlWaterfallMist(short itemNumber); void TriggerWaterfallMist(const ItemInfo& item); void KillAllCurrentItems(short itemNumber); void TriggerDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b); +void TriggerDynamicPointLight(const Vector3& pos, const Color& color, float falloff, bool castShadows = false, int hash = 0); +void TriggerDynamicSpotLight(const Vector3& pos, const Vector3& dir, const Color& color, float radius, float falloff, float distance, bool castShadows = false, int hash = 0); void TriggerRocketFlame(int x, int y, int z, int xv, int yv, int zv, int itemNumber); void TriggerRocketSmoke(int x, int y, int z); void TriggerFlashSmoke(int x, int y, int z, short roomNumber); @@ -313,5 +315,3 @@ void TriggerExplosionBubbles(int x, int y, int z, short roomNumber); void Ricochet(Pose& pos); void ProcessEffects(ItemInfo* item); void UpdateWibble(); - -void TriggerDynamicLight(const Vector3& pos, const Color& color, float falloff); diff --git a/TombEngine/Game/effects/tomb4fx.cpp b/TombEngine/Game/effects/tomb4fx.cpp index ef50c0418..46c46d2e4 100644 --- a/TombEngine/Game/effects/tomb4fx.cpp +++ b/TombEngine/Game/effects/tomb4fx.cpp @@ -1383,7 +1383,7 @@ void UpdateShockwaves() { auto lightColor = Color(shockwave.r / (float)UCHAR_MAX, shockwave.g / (float)UCHAR_MAX, shockwave.b / (float)UCHAR_MAX); auto pos = Vector3(shockwave.x, shockwave.y, shockwave.z); - TriggerDynamicLight(pos, lightColor, shockwave.life / (float)UCHAR_MAX); + TriggerDynamicPointLight(pos, lightColor, shockwave.life / 4.0f); } if (shockwave.style != (int)ShockwaveStyle::Knockback) diff --git a/TombEngine/Objects/TR2/Entity/Bartoli.cpp b/TombEngine/Objects/TR2/Entity/Bartoli.cpp index 1eb6b0287..3eedc9838 100644 --- a/TombEngine/Objects/TR2/Entity/Bartoli.cpp +++ b/TombEngine/Objects/TR2/Entity/Bartoli.cpp @@ -74,28 +74,26 @@ namespace TEN::Entities::Creatures::TR2 // Spawn light. auto lightPos = item.Pose.Position.ToVector3() + Vector3(0.0f, -CLICK(1), 0.0f); auto lightColor = Color(0.0f,Random::GenerateFloat(0.7f, 1.0f), Random::GenerateFloat(0.2f, 0.3f)); - float lightFalloff = Random::GenerateFloat(0.1f, 0.2f); - float transformationLightFalloff = (0.5, 1.0); - TriggerDynamicLight(lightPos, lightColor, lightFalloff); + TriggerDynamicPointLight(lightPos, lightColor, Random::GenerateFloat(BLOCK(6), BLOCK(12))); // Handle transformation. if (effectTimer == timeExplosion1) { - TriggerDynamicLight(lightPos, lightColor, transformationLightFalloff); + TriggerDynamicPointLight(lightPos, lightColor, Random::GenerateFloat(BLOCK(12), BLOCK(24))); SpawnBartoliTransformEffect(item, ID_SPHERE_OF_DOOM); SoundEffect(SFX_TR2_MARCO_BARTOLLI_TRANSFORM, &item.Pose); } if (effectTimer == timeExplosion2) { - TriggerDynamicLight(lightPos, lightColor, transformationLightFalloff); + TriggerDynamicPointLight(lightPos, lightColor, Random::GenerateFloat(BLOCK(12), BLOCK(24))); SpawnBartoliTransformEffect(item, ID_SPHERE_OF_DOOM2); SoundEffect(SFX_TR2_MARCO_BARTOLLI_TRANSFORM, &item.Pose); } if (effectTimer == timeExplosion3) { - TriggerDynamicLight(lightPos, lightColor, transformationLightFalloff); + TriggerDynamicPointLight(lightPos, lightColor, Random::GenerateFloat(BLOCK(12), BLOCK(24))); SpawnBartoliTransformEffect(item, ID_SPHERE_OF_DOOM3); SoundEffect(SFX_TR2_MARCO_BARTOLLI_TRANSFORM, &item.Pose); item.Animation.FrameNumber = animationFrameEnd; diff --git a/TombEngine/Objects/TR2/Entity/Dragon.cpp b/TombEngine/Objects/TR2/Entity/Dragon.cpp index 90e90a51c..893b5ff2b 100644 --- a/TombEngine/Objects/TR2/Entity/Dragon.cpp +++ b/TombEngine/Objects/TR2/Entity/Dragon.cpp @@ -193,9 +193,9 @@ namespace TEN::Entities::Creatures::TR2 Random::GenerateFloat(0.8f, 0.9f), Random::GenerateFloat(0.4f, 0.5f), Random::GenerateFloat(0.2f, 0.3f)); - float falloff = Random::GenerateFloat(0.1f, 0.4f); + float falloff = Random::GenerateFloat(BLOCK(6), BLOCK(20)); - TriggerDynamicLight(pos, color, falloff); + TriggerDynamicPointLight(pos, color, falloff); } break; @@ -205,9 +205,9 @@ namespace TEN::Entities::Creatures::TR2 Random::GenerateFloat(0.8f, 0.9f), Random::GenerateFloat(0.2f, 0.3f), Random::GenerateFloat(0.0f, 0.1f)); - float falloff = Random::GenerateFloat(0.1f, 0.2f); + float falloff = Random::GenerateFloat(BLOCK(6), BLOCK(12)); - TriggerDynamicLight(pos, color, falloff); + TriggerDynamicPointLight(pos, color, falloff); } break; } diff --git a/TombEngine/Objects/TR3/Entity/SealMutant.cpp b/TombEngine/Objects/TR3/Entity/SealMutant.cpp index 0050fa143..aa297dbe2 100644 --- a/TombEngine/Objects/TR3/Entity/SealMutant.cpp +++ b/TombEngine/Objects/TR3/Entity/SealMutant.cpp @@ -178,8 +178,8 @@ namespace TEN::Entities::Creatures::TR3 { auto pos = GetJointPosition(item, SealMutantGasBite.BoneID, Vector3(0.0f, -SEAL_MUTANT_FLAME_LIGHT_Y_OFFSET, 0.0f)); auto color = Color(Random::GenerateFloat(0.75f, 1.0f), Random::GenerateFloat(0.4f, 0.5f), Random::GenerateFloat(0.0f, 0.25f)); - float falloff = Random::GenerateFloat(0.03f, 0.04f); - TriggerDynamicLight(pos.ToVector3(), color, falloff); + float falloff = Random::GenerateFloat(BLOCK(1.5f), BLOCK(2.5f)); + TriggerDynamicPointLight(pos.ToVector3(), color, falloff); } } else if (TestAnimFrameRange(item, 1, 124)) diff --git a/TombEngine/Objects/TR4/Entity/tr4_demigod.cpp b/TombEngine/Objects/TR4/Entity/tr4_demigod.cpp index 4b4e1fe91..c7dbbca90 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_demigod.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_demigod.cpp @@ -713,7 +713,7 @@ namespace TEN::Entities::TR4 TriggerShockwave((Pose*)&pos, 24, 88, 200, 128, 128, 128, 32, EulerAngles::Identity, 8, true, false, true, (int)ShockwaveStyle::Normal); auto lightColor = Color(1.0f, 0.4f, 0.2f); - TriggerDynamicLight(pos.ToVector3(), lightColor, 0.1f); + TriggerDynamicPointLight(pos.ToVector3(), lightColor, BLOCK(6)); Camera.bounce = -128; diff --git a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp index 397276556..ef4c8e1d0 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp @@ -600,7 +600,7 @@ namespace TEN::Entities::Creatures::TR5 TriggerShockwave(&Pose(pos1), 16, 160, 64, 0, color / 2, color, 48, EulerAngles::Identity, 1, true, false, true, (int)ShockwaveStyle::Normal); auto lightColor = Color(0.4f, 0.3f, 0.0f); - TriggerDynamicLight(pos.ToVector3(), lightColor, 0.04f); + TriggerDynamicPointLight(pos.ToVector3(), lightColor, BLOCK(2.5f)); } deltaFrame = item->Animation.FrameNumber - GetAnimData(item).frameBase; @@ -629,7 +629,7 @@ namespace TEN::Entities::Creatures::TR5 if (item->ItemFlags[3]) { auto lightColor = Color(0.0f, 0.4f, 1.0f); - TriggerDynamicLight(pos.ToVector3(), lightColor, 0.06f); + TriggerDynamicPointLight(pos.ToVector3(), lightColor, BLOCK(4)); } } } diff --git a/TombEngine/Objects/TR5/Trap/LaserBeam.cpp b/TombEngine/Objects/TR5/Trap/LaserBeam.cpp index 83574e637..bf7252333 100644 --- a/TombEngine/Objects/TR5/Trap/LaserBeam.cpp +++ b/TombEngine/Objects/TR5/Trap/LaserBeam.cpp @@ -74,10 +74,10 @@ namespace TEN::Entities::Traps static void SpawnLaserBeamLight(const Vector3& pos, int roomNumber, const Color& color, float intensity, float amplitudeMax) { - constexpr auto FALLOFF = 0.03f; + constexpr auto LASER_BEAM_FALLOFF = BLOCK(1.5f); float intensityNorm = intensity - Random::GenerateFloat(0.0f, amplitudeMax); - TriggerDynamicLight(pos, color * intensityNorm, FALLOFF); + TriggerDynamicPointLight(pos, color * intensityNorm, LASER_BEAM_FALLOFF); } void LaserBeamEffect::StoreInterpolationData() diff --git a/TombEngine/Renderer/Renderer.cpp b/TombEngine/Renderer/Renderer.cpp index bb54ef41e..f55770a2e 100644 --- a/TombEngine/Renderer/Renderer.cpp +++ b/TombEngine/Renderer/Renderer.cpp @@ -32,8 +32,6 @@ namespace TEN::Renderer void Renderer::FreeRendererData() { - _shadowLight = nullptr; - _items.resize(0); _effects.resize(0); _moveableObjects.resize(0); @@ -47,6 +45,12 @@ namespace TEN::Renderer _animatedTextures.resize(0); _animatedTextureSets.resize(0); + _shadowLight = nullptr; + + _dynamicLightList = 0; + for (auto& dynamicLightList : _dynamicLights) + dynamicLightList.resize(0); + for (auto& mesh : _meshes) delete mesh; _meshes.resize(0); @@ -195,12 +199,23 @@ namespace TEN::Renderer } _context->PSSetSamplers((UINT)registerType, 1, &samplerState); - } + } + + void Renderer::BindLight(RendererLight& light, ShaderLight* lights, int index) + { + memcpy(&lights[index], &light, sizeof(ShaderLight)); + + if (light.Hash == 0) + return; + + lights[index].Position = Vector3::Lerp(light.PrevPosition, light.Position, GetInterpolationFactor()); + lights[index].Direction = Vector3::Lerp(light.PrevDirection, light.Direction, GetInterpolationFactor()); + } void Renderer::BindRoomLights(std::vector& lights) { for (int i = 0; i < lights.size(); i++) - memcpy(&_stRoom.RoomLights[i], lights[i], sizeof(ShaderLight)); + BindLight(*lights[i], _stRoom.RoomLights, i); _stRoom.NumRoomLights = (int)lights.size(); } @@ -208,7 +223,7 @@ namespace TEN::Renderer void Renderer::BindStaticLights(std::vector& lights) { for (int i = 0; i < lights.size(); i++) - memcpy(&_stStatic.Lights[i], lights[i], sizeof(ShaderLight)); + BindLight(*lights[i], _stStatic.Lights, i); _stStatic.NumLights = (int)lights.size(); } @@ -216,7 +231,7 @@ namespace TEN::Renderer void Renderer::BindInstancedStaticLights(std::vector& lights, int instanceID) { for (int i = 0; i < lights.size(); i++) - memcpy(&_stInstancedStaticMeshBuffer.StaticMeshes[instanceID].Lights[i], lights[i], sizeof(ShaderLight)); + BindLight(*lights[i], _stInstancedStaticMeshBuffer.StaticMeshes[instanceID].Lights, i); _stInstancedStaticMeshBuffer.StaticMeshes[instanceID].NumLights = (int)lights.size(); } @@ -242,7 +257,7 @@ namespace TEN::Renderer if (fadedCoeff == 0.0f) continue; - memcpy(&_stItem.Lights[numLights], lights[i], sizeof(ShaderLight)); + BindLight(*lights[i], _stItem.Lights, numLights); _stItem.Lights[numLights].Intensity *= fadedCoeff; numLights++; } diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 02a5f9a2f..6e733a52f 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -245,7 +245,8 @@ namespace TEN::Renderer // Lights - std::vector _dynamicLights; + int _dynamicLightList = 0; + std::vector _dynamicLights[2]; RendererLight* _shadowLight; // Lines @@ -417,6 +418,7 @@ namespace TEN::Renderer void ApplySMAA(RenderTarget2D* renderTarget, RenderView& view); void ApplyFXAA(RenderTarget2D* renderTarget, RenderView& view); void BindTexture(TextureRegister registerType, TextureBase* texture, SamplerStateRegister samplerType); + void BindLight(RendererLight& light, ShaderLight* lights, int index); void BindRoomLights(std::vector& lights); void BindStaticLights(std::vector& lights); void BindInstancedStaticLights(std::vector& lights, int instanceID); @@ -643,7 +645,9 @@ namespace TEN::Renderer void AddString(const std::string& string, const Vector2& pos, const Color& color, float scale, int flags); void AddDebugString(const std::string& string, const Vector2& pos, const Color& color, float scale, RendererDebugPage page = RendererDebugPage::None); void FreeRendererData(); - void AddDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b); + void AddDynamicPointLight(const Vector3& pos, float radius, const Color& color, bool castShadows, int hash = 0); + void AddDynamicSpotLight(const Vector3& pos, const Vector3& dir, float radius, float falloff, float distance, const Color& color, bool castShadows, int hash = 0); + void StoreInterpolatedDynamicLightData(RendererLight& light); void RenderLoadingScreen(float percentage); void RenderFreezeMode(float interpFactor, bool staticBackground); void UpdateProgress(float value); diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index 5f5be02ef..83e4408b0 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -34,6 +34,10 @@ namespace TEN::Renderer _meshes.clear(); + _dynamicLightList = 0; + for (auto& dynamicLightList : _dynamicLights) + dynamicLightList.clear(); + int allocatedItemSize = (int)g_Level.Items.size() + MAX_SPAWNED_ITEM_COUNT; auto item = RendererItem(); diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index 7f5ac704d..adb3d7ef7 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -148,8 +148,10 @@ namespace TEN::Renderer SetBlendMode(BlendMode::Opaque); SetCullMode(CullMode::CounterClockwise); - int steps = _shadowLight->Type == LightType::Point ? 6 : 1; - for (int step = 0; step < steps; step++) + auto shadowLightPos = _shadowLight->Hash == 0 ? _shadowLight->Position : + Vector3::Lerp(_shadowLight->PrevPosition, _shadowLight->Position, GetInterpolationFactor()); + + for (int step = 0; step < 6; step++) { // Bind render target _context->OMSetRenderTargets(1, _shadowMap.RenderTargetView[step].GetAddressOf(), @@ -158,7 +160,7 @@ namespace TEN::Renderer _context->RSSetViewports(1, &_shadowMapViewport); ResetScissor(); - if (_shadowLight->Position == item->Position) + if (shadowLightPos == item->Position) return; UINT stride = sizeof(Vertex); @@ -178,27 +180,11 @@ namespace TEN::Renderer BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp); // Set camera matrices - Matrix view; - Matrix projection; - if (_shadowLight->Type == LightType::Point) - { - view = Matrix::CreateLookAt(_shadowLight->Position, _shadowLight->Position + - RenderTargetCube::forwardVectors[step] * BLOCK(10), - RenderTargetCube::upVectors[step]); + Matrix view = Matrix::CreateLookAt(shadowLightPos, shadowLightPos + + RenderTargetCube::forwardVectors[step] * BLOCK(10), + RenderTargetCube::upVectors[step]); - projection = Matrix::CreatePerspectiveFieldOfView(90.0f * PI / 180.0f, 1.0f, 16.0f, _shadowLight->Out); - - } - else if (_shadowLight->Type == LightType::Spot) - { - view = Matrix::CreateLookAt(_shadowLight->Position, - _shadowLight->Position + _shadowLight->Direction * BLOCK(10), - Vector3(0.0f, -1.0f, 0.0f)); - - // Vertex lighting fades out in 1024-steps. increase angle artificially for a bigger blend radius. - float projectionAngle = _shadowLight->OutRange * 1.5f * (PI / 180.0f); - projection = Matrix::CreatePerspectiveFieldOfView(projectionAngle, 1.0f, 16.0f, _shadowLight->Out); - } + Matrix projection = Matrix::CreatePerspectiveFieldOfView(90.0f * PI / 180.0f, 1.0f, 16.0f, _shadowLight->Out); CCameraMatrixBuffer shadowProjection; shadowProjection.ViewProjection = view * projection; @@ -1536,41 +1522,101 @@ namespace TEN::Renderer AddDebugSphere(sphere.Center, sphere.Radius, color, page, isWireframe); } - void Renderer::AddDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b) + void Renderer::AddDynamicSpotLight(const Vector3& pos, const Vector3& dir, float radius, float falloff, float distance, const Color& color, bool castShadows, int hash) { if (_isLocked || g_GameFlow->LastFreezeMode != FreezeMode::None) return; RendererLight dynamicLight = {}; - if (falloff >= 8) - { - dynamicLight.Color = Vector3(r / 255.0f, g / 255.0f, b / 255.0f) * 2.0f; - } - else - { - r = (r * falloff) >> 3; - g = (g * falloff) >> 3; - b = (b * falloff) >> 3; + dynamicLight.Color = Vector3(color.x, color.y, color.z) * 2.0f; + if (falloff < 8) + dynamicLight.Color *= (falloff / 8.0f); - dynamicLight.Color = Vector3(r / 255.0f, g / 255.0f, b / 255.0f) * 2.0f; - } + // Calculate outer cone angle (degrees) based on radius at the cone's end distance. + float outerConeAngle = atan(radius / distance) * (180.0f / PI); + float innerConeAngle = atan((radius - falloff) / distance) * (180.0f / PI); + float innerDistance = std::max(0.0f, distance - distance * (falloff / radius)); + + // Normalize direction for safety. + auto normalizedDirection = dir; + normalizedDirection.Normalize(); dynamicLight.RoomNumber = NO_VALUE; dynamicLight.Intensity = 1.0f; - dynamicLight.Position = Vector3(float(x), float(y), float(z)); - dynamicLight.Out = falloff * 256.0f; - dynamicLight.Type = LightType::Point; - dynamicLight.BoundingSphere = BoundingSphere(dynamicLight.Position, dynamicLight.Out); + dynamicLight.Position = pos; + dynamicLight.Direction = normalizedDirection; + dynamicLight.In = innerDistance; + dynamicLight.Out = distance; + dynamicLight.InRange = innerConeAngle > 0.0f ? innerConeAngle : 0.5f; + dynamicLight.OutRange = outerConeAngle; + dynamicLight.Type = LightType::Spot; + dynamicLight.CastShadows = castShadows; + dynamicLight.BoundingSphere = BoundingSphere(pos, distance); dynamicLight.Luma = Luma(dynamicLight.Color); + dynamicLight.Hash = hash; - _dynamicLights.push_back(dynamicLight); + StoreInterpolatedDynamicLightData(dynamicLight); + _dynamicLights[_dynamicLightList].push_back(dynamicLight); + } + + void Renderer::AddDynamicPointLight(const Vector3& pos, float radius, const Color& color, bool castShadows, int hash) + { + if (_isLocked || g_GameFlow->LastFreezeMode != FreezeMode::None) + return; + + RendererLight dynamicLight = {}; + + dynamicLight.Color = Vector3(color.x, color.y, color.z) * 2.0f; + if (radius < BLOCK(2)) + dynamicLight.Color *= (radius / BLOCK(2)); + + dynamicLight.RoomNumber = NO_VALUE; + dynamicLight.Intensity = 1.0f; + dynamicLight.Position = pos; + dynamicLight.Out = radius; + dynamicLight.Type = LightType::Point; + dynamicLight.CastShadows = castShadows; + dynamicLight.BoundingSphere = BoundingSphere(pos, radius); + dynamicLight.Luma = Luma(dynamicLight.Color); + dynamicLight.Hash = hash; + + StoreInterpolatedDynamicLightData(dynamicLight); + _dynamicLights[_dynamicLightList].push_back(dynamicLight); + } + + void Renderer::StoreInterpolatedDynamicLightData(RendererLight& light) + { + // Hash is not provided, do not search for same light in old buffer. + if (light.Hash == 0) + return; + + // Determine the previous buffer index. + const auto& previousList = _dynamicLights[1 - _dynamicLightList]; + + // Find a light in the previous buffer with the same Hash. + auto it = std::find_if(previousList.begin(), previousList.end(), + [&light](const auto& prevLight) + { + return prevLight.Hash == light.Hash; + }); + + if (it == previousList.end()) + return; + + // If a matching light is found, copy its data. + const auto& prevLight = *it; + light.PrevPosition = prevLight.Position; + light.PrevDirection = prevLight.Direction; } void Renderer::PrepareScene() { if (g_GameFlow->CurrentFreezeMode == FreezeMode::None) - _dynamicLights.clear(); + { + _dynamicLightList ^= 1; + _dynamicLights[_dynamicLightList].clear(); + } _lines2DToDraw.clear(); _lines3DToDraw.clear(); diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index d40516df1..e1260fb6f 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -543,7 +543,7 @@ namespace TEN::Renderer float brightest = 0.0f; // Dynamic lights have the priority - for (auto& light : _dynamicLights) + for (auto& light : _dynamicLights[_dynamicLightList]) { float distSqr = SQUARE(position.x - light.Position.x) + @@ -784,9 +784,9 @@ namespace TEN::Renderer ROOM_INFO* r = &g_Level.Rooms[roomNumber]; // Collect dynamic lights for rooms - for (int i = 0; i < _dynamicLights.size(); i++) + for (int i = 0; i < _dynamicLights[_dynamicLightList].size(); i++) { - RendererLight* light = &_dynamicLights[i]; + RendererLight* light = &_dynamicLights[_dynamicLightList][i]; // If no radius, ignore if (light->Out == 0.0f) diff --git a/TombEngine/Renderer/RendererInit.cpp b/TombEngine/Renderer/RendererInit.cpp index 44ab3ef55..08d9b11a9 100644 --- a/TombEngine/Renderer/RendererInit.cpp +++ b/TombEngine/Renderer/RendererInit.cpp @@ -126,11 +126,13 @@ namespace TEN::Renderer _currentCausticsFrame = 0; // Preallocate lists - _dynamicLights = createVector(MAX_DYNAMIC_LIGHTS); _lines2DToDraw = createVector(MAX_LINES_2D); _lines3DToDraw = createVector(MAX_LINES_3D); _triangles3DToDraw = createVector(MAX_TRIANGLES_3D); + for (auto& dynamicLightList : _dynamicLights) + dynamicLightList = createVector(MAX_DYNAMIC_LIGHTS); + for (auto& item : _items) item.LightsToDraw = createVector(MAX_LIGHTS_PER_ITEM); diff --git a/TombEngine/Renderer/Structures/RendererLight.h b/TombEngine/Renderer/Structures/RendererLight.h index baf818784..e9ab8cf62 100644 --- a/TombEngine/Renderer/Structures/RendererLight.h +++ b/TombEngine/Renderer/Structures/RendererLight.h @@ -25,6 +25,11 @@ namespace TEN::Renderer::Structures bool AffectNeighbourRooms; bool CastShadows; float Luma; + + Vector3 PrevPosition; + Vector3 PrevDirection; + + int Hash = 0; }; struct RendererLightNode diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h index dcf278189..eccac5e63 100644 --- a/TombEngine/Scripting/Internal/ReservedScriptNames.h +++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h @@ -306,6 +306,7 @@ static constexpr char ScriptReserved_EmitParticle[] = "EmitParticle"; static constexpr char ScriptReserved_EmitLightningArc[] = "EmitLightningArc"; static constexpr char ScriptReserved_EmitShockwave[] = "EmitShockwave"; static constexpr char ScriptReserved_EmitLight[] = "EmitLight"; +static constexpr char ScriptReserved_EmitSpotLight[] = "EmitSpotLight"; static constexpr char ScriptReserved_EmitBlood[] = "EmitBlood"; static constexpr char ScriptReserved_EmitFire[] = "EmitFire"; static constexpr char ScriptReserved_MakeExplosion[] = "MakeExplosion"; @@ -421,3 +422,7 @@ constexpr char ScriptReserved_Vec3Length[] = "Length"; constexpr char ScriptReserved_Vec3Lerp[] = "Lerp"; constexpr char ScriptReserved_Vec3Normalize[] = "Normalize"; constexpr char ScriptReserved_Vec3Rotate[] = "Rotate"; + +// Rotation + +constexpr char ScriptReserved_RotationDirection[] = "Direction"; diff --git a/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp b/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp index e3404b00e..af8b20b74 100644 --- a/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp +++ b/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp @@ -23,6 +23,7 @@ #include "Scripting/Internal/TEN/Vec2/Vec2.h" #include "Sound/sound.h" #include "Specific/clock.h" +#include "Specific/trutils.h" /*** Functions to generate effects. @@ -258,15 +259,38 @@ namespace TEN::Scripting::Effects /***Emit dynamic light that lasts for a single frame. * If you want a light that sticks around, you must call this each frame. @function EmitLight -@tparam Vec3 pos -@tparam Color color (default Color(255, 255, 255)) -@tparam int radius (default 20) corresponds loosely to both intensity and range +@tparam Vec3 pos position of the light +@tparam[opt] Color color light color (default Color(255, 255, 255)) +@tparam[opt] int radius measured in "clicks" or 256 world units (default 20) +@tparam[opt] bool shadows determines whether light should generate dynamic shadows for applicable moveables (default is false) +@tparam[opt] string name if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights) */ - static void EmitLight(Vec3 pos, TypeOrNil col, TypeOrNil radius) + static void EmitLight(Vec3 pos, TypeOrNil col, TypeOrNil radius, TypeOrNil castShadows, TypeOrNil name) { auto color = USE_IF_HAVE(ScriptColor, col, ScriptColor(255, 255, 255)); - int rad = USE_IF_HAVE(int, radius, 20); - TriggerDynamicLight(pos.x, pos.y, pos.z, rad, color.GetR(), color.GetG(), color.GetB()); + int rad = (float)(USE_IF_HAVE(int, radius, 20) * BLOCK(0.25f)); + TriggerDynamicPointLight(pos.ToVector3(), color, rad, USE_IF_HAVE(bool, castShadows, false), GetHash(USE_IF_HAVE(std::string, name, std::string()))); + } + +/***Emit dynamic directional spotlight that lasts for a single frame. +* If you want a light that sticks around, you must call this each frame. +@function EmitSpotLight +@tparam Vec3 pos position of the light +@tparam Vec3 dir direction, or a point to which spotlight should be directed to +@tparam[opt] Color color (default Color(255, 255, 255)) +@tparam[opt] int radius overall radius at the endpoint of a light cone, measured in "clicks" or 256 world units (default 10) +@tparam[opt] int falloff radius, at which light starts to fade out, measured in "clicks" (default 5) +@tparam[opt] int distance distance, at which light cone fades out, measured in "clicks" (default 20) +@tparam[opt] bool shadows determines whether light should generate dynamic shadows for applicable moveables (default is false) +@tparam[opt] string name if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights) +*/ + static void EmitSpotLight(Vec3 pos, Vec3 dir, TypeOrNil col, TypeOrNil radius, TypeOrNil falloff, TypeOrNil distance, TypeOrNil castShadows, TypeOrNil name) + { + auto color = USE_IF_HAVE(ScriptColor, col, ScriptColor(255, 255, 255)); + int rad = (float)(USE_IF_HAVE(int, radius, 10) * BLOCK(0.25f)); + int fallOff = (float)(USE_IF_HAVE(int, falloff, 5) * BLOCK(0.25f)); + int dist = (float)(USE_IF_HAVE(int, distance, 20) * BLOCK(0.25f)); + TriggerDynamicSpotLight(pos.ToVector3(), dir.ToVector3(), color, rad, fallOff, dist, USE_IF_HAVE(bool, castShadows, false), GetHash(USE_IF_HAVE(std::string, name, std::string()))); } /***Emit blood. @@ -328,6 +352,7 @@ namespace TEN::Scripting::Effects tableEffects.set_function(ScriptReserved_EmitParticle, &EmitParticle); tableEffects.set_function(ScriptReserved_EmitShockwave, &EmitShockwave); tableEffects.set_function(ScriptReserved_EmitLight, &EmitLight); + tableEffects.set_function(ScriptReserved_EmitSpotLight, &EmitSpotLight); tableEffects.set_function(ScriptReserved_EmitBlood, &EmitBlood); tableEffects.set_function(ScriptReserved_MakeExplosion, &MakeExplosion); tableEffects.set_function(ScriptReserved_EmitFire, &EmitFire); diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp index 7308d8cf2..a41dfdc69 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp @@ -637,7 +637,20 @@ Vec3 Moveable::GetJointPos(int jointIndex, sol::optional offset) const Rotation Moveable::GetJointRot(int jointIndex) const { - return GetBoneOrientation(*m_item, jointIndex); + auto point1 = GetJointPosition(m_item, jointIndex); + auto point2 = GetJointPosition(m_item, jointIndex, Vector3::Forward * BLOCK(1)); + + auto normal = (point1 - point2).ToVector3(); + normal.Normalize(); + + auto eulers = EulerAngles(normal); + + return + { + TO_DEGREES(eulers.x), + TO_DEGREES(eulers.y), + TO_DEGREES(eulers.z) + }; } // This does not guarantee that the returned value will be identical diff --git a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp index 3a2cdf881..e12344984 100644 --- a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp +++ b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp @@ -18,6 +18,7 @@ void Rotation::Register(sol::table& parent) ctors(), sol::call_constructor, ctors(), sol::meta_function::to_string, &Rotation::ToString, + ScriptReserved_RotationDirection, &Rotation::Direction, /// (float) X angle component. // @mem x @@ -77,6 +78,24 @@ Rotation::operator Vector3() const return Vector3(x, y, z); }; +/// Converts rotation to a direction normal. +// @treturn Vec3 resulting normal calculated from this rotation. +// @function Direction +Vec3 Rotation::Direction() const +{ + // Convert degrees to radians. + float xRad = x * RADIAN; + float yRad = y * RADIAN; + + // Calculate the direction vector. + float dirX = sin(yRad) * cos(xRad); + float dirY = -sin(xRad); + float dirZ = cos(yRad) * cos(xRad); + + // Scale by the given distance. + return Vec3(dirX, dirY, dirZ); +} + /// @tparam Rotation rotation this Rotation. // @treturn string A string showing the X, Y, and Z angle components of the Rotation. // @function __tostring diff --git a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.h b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.h index 87919cc30..00ab7a96f 100644 --- a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.h +++ b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.h @@ -1,5 +1,6 @@ #pragma once #include "Math/Objects/EulerAngles.h" +#include "Scripting/Internal/TEN/Vec3/Vec3.h" class EulerAngles; class Pose; @@ -27,6 +28,7 @@ public: // Converters std::string ToString() const; EulerAngles ToEulerAngles() const; + Vec3 Direction() const; // Operators operator Vector3() const; diff --git a/TombEngine/Shaders/Rooms.fx b/TombEngine/Shaders/Rooms.fx index 286e78026..af2dac3ee 100644 --- a/TombEngine/Shaders/Rooms.fx +++ b/TombEngine/Shaders/Rooms.fx @@ -146,11 +146,10 @@ PixelShaderOutput PS(PixelShaderInput input) if (Light.Type == LT_POINT) { DoPointLightShadow(input.WorldPosition, lighting); - } else if (Light.Type == LT_SPOT) { - DoSpotLightShadow(input.WorldPosition, lighting); + DoSpotLightShadow(input.WorldPosition, normal, lighting); } } @@ -160,6 +159,7 @@ PixelShaderOutput PS(PixelShaderInput input) { for (int i = 0; i < NumRoomLights; i++) { + int lightType = RoomLights[i].Type; float3 lightPos = RoomLights[i].Position.xyz; float3 color = RoomLights[i].Color.xyz; float radius = RoomLights[i].Out; @@ -173,10 +173,15 @@ PixelShaderOutput PS(PixelShaderInput input) float d = saturate(dot(normal, lightVec )); if (d < 0) continue; - - float attenuation = pow(((radius - distance) / radius), 2); - lighting += color * attenuation * d; + if (lightType == LT_POINT) + { + lighting += DoPointLight(input.WorldPosition, normal, RoomLights[i]); + } + else if (lightType == LT_SPOT) + { + lighting += DoSpotLight(input.WorldPosition, normal, RoomLights[i]); + } } } diff --git a/TombEngine/Shaders/ShaderLight.hlsli b/TombEngine/Shaders/ShaderLight.hlsli index 64c47f13e..26a7ff8b7 100644 --- a/TombEngine/Shaders/ShaderLight.hlsli +++ b/TombEngine/Shaders/ShaderLight.hlsli @@ -8,120 +8,113 @@ float3 DoSpecularPoint(float3 pos, float3 n, ShaderLight light, float strength) { if ((strength <= 0.0)) return float3(0, 0, 0); - else - { - float3 lightPos = light.Position.xyz; - float radius = light.Out; + + float3 lightPos = light.Position.xyz; + float radius = light.Out; - float dist = distance(lightPos, pos); - if (dist > radius) - return float3(0, 0, 0); - else - { - float3 lightDir = normalize(lightPos - pos); - float3 reflectDir = reflect(lightDir, n); + float dist = distance(lightPos, pos); + if (dist > radius) + return float3(0, 0, 0); + + float3 lightDir = normalize(lightPos - pos); + float3 reflectDir = reflect(lightDir, n); - float3 color = light.Color.xyz; - float intensity = saturate(light.Intensity); - float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); - float attenuation = (radius - dist) / radius; + float3 color = light.Color.xyz; + float intensity = saturate(light.Intensity); + float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); + float attenuation = (radius - dist) / radius; - return attenuation * spec * color * intensity; - } - } + return attenuation * spec * color * intensity; } float3 DoSpecularSun(float3 n, ShaderLight light, float strength) { if (strength <= 0.0) return float3(0, 0, 0); - else - { - float3 lightDir = -normalize(light.Direction); - float3 reflectDir = reflect(lightDir, n); + + float3 lightDir = -normalize(light.Direction); + float3 reflectDir = reflect(lightDir, n); - float3 color = light.Color.xyz; - float intensity = saturate(light.Intensity); - float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); + float3 color = light.Color.xyz; + float intensity = saturate(light.Intensity); + float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); - return spec * color * intensity; - } + return spec * color * intensity; } -float3 DoSpecularSpot(float3 pos, float3 n, ShaderLight light, float strength) +float3 DoSpecularSpot(float3 pos, float3 normal, ShaderLight light, float strength) { if (strength <= 0.0) return float3(0, 0, 0); - else - { - float3 lightPos = light.Position.xyz; - float3 direction = light.Direction.xyz; - float innerRange = light.In; - float outerRange = light.Out; - float coneIn = light.InRange; - float coneOut = light.OutRange; - - float3 lightVec = pos - lightPos; - float distance = length(lightVec); - lightVec = normalize(lightVec); - - if (distance > outerRange) - return float3(0, 0, 0); - else - { - float cosine = dot(lightVec, direction); - - float minCosineIn = cos(coneIn * (PI / 180.0f)); - float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); - - float minCosineOut = cos(coneOut * (PI / 180.0f)); - float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); - - float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); - - if (attenuation > 0.0f) - { - float3 lightDir = -lightVec; - float3 reflectDir = reflect(lightDir, n); - - float3 color = light.Color.xyz; - float intensity = saturate(light.Intensity); - float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); - float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); - - return attenuation * spec * color * intensity * falloff; - } - else - return float3(0, 0, 0); - } - } -} - -float3 DoPointLight(float3 pos, float3 n, ShaderLight light) -{ + float3 lightPos = light.Position.xyz; + float3 direction = light.Direction.xyz; + float innerRange = light.In; + float outerRange = light.Out; + float coneIn = light.InRange; + float coneOut = light.OutRange; + + float3 lightVec = pos - lightPos; + float distance = length(lightVec); + lightVec = normalize(lightVec); + + if (distance > outerRange) + return float3(0, 0, 0); + + float cosine = dot(lightVec, direction); + + float minCosineIn = cos(coneIn * (PI / 180.0f)); + float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); + + float minCosineOut = cos(coneOut * (PI / 180.0f)); + float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); + + float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); + + if (attenuation <= 0.0f) + return float3(0, 0, 0); + + float3 lightDir = -lightVec; + float3 reflectDir = reflect(lightDir, normal); + float3 color = light.Color.xyz; float intensity = saturate(light.Intensity); + float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); + float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); - float3 lightVec = (lightPos - pos); - float distance = length(lightVec); - - if (distance > light.Out) - return float3(0, 0, 0); - else - { - lightVec = normalize(lightVec); - float d = saturate(dot(n, lightVec)); - - float attenuation = 1.0f; - if (distance > light.In) - attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); - - return saturate(color * intensity * attenuation * d); - } + return attenuation * spec * color * intensity * falloff; } -float3 DoShadowLight(float3 pos, float3 n, ShaderLight light) +float3 DoPointLight(float3 pos, float3 normal, ShaderLight light) +{ + float3 lightPos = light.Position.xyz; + float3 color = light.Color.xyz; + float intensity = saturate(light.Intensity); + + float3 lightVec = (lightPos - pos); + float distance = length(lightVec); + + if (distance > light.Out) + return float3(0, 0, 0); + + // Normalize lightVec and calculate the dot product with the normal + lightVec = normalize(lightVec); + float d = saturate(dot(normal, lightVec)); + + // Compute attenuation based on distance and light range + float attenuation = 1.0f; + if (distance > light.In) + attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); + + // Add a softening factor to handle spikes near surfaces + float softening = saturate((distance - 0.1f) / 0.1f); // Softens for distances < 0.1 + softening = smoothstep(0.0f, 0.1f, distance); // Alternative smooth transition + + // Return the softened light contribution + return saturate(color * intensity * attenuation * d * softening); +} + +float3 DoShadowLight(float3 pos, float3 normal, ShaderLight light) { float3 lightPos = light.Position.xyz; float3 color = light.Color.xyz; @@ -132,23 +125,21 @@ float3 DoShadowLight(float3 pos, float3 n, ShaderLight light) if (distance > light.Out) return float3(0, 0, 0); - else - { - lightVec = normalize(lightVec); - float d = saturate(dot(n, lightVec)); + + lightVec = normalize(lightVec); + float d = saturate(dot(normal, lightVec)); - float attenuation = 1.0f; - if (distance > light.In) - attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); + float attenuation = 1.0f; + if (distance > light.In) + attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); - float absolute = float3(color * intensity * attenuation); - float directional = absolute * d; + float absolute = float3(color * intensity * attenuation); + float directional = absolute * d; - return ((absolute * 0.33f) + (directional * 0.66f)) * 2.0f; - } + return ((absolute * 0.33f) + (directional * 0.66f)) * 2.0f; } -float3 DoSpotLight(float3 pos, float3 n, ShaderLight light) +float3 DoSpotLight(float3 pos, float3 normal, ShaderLight light) { float3 lightPos = light.Position.xyz; float3 color = light.Color.xyz; @@ -165,48 +156,40 @@ float3 DoSpotLight(float3 pos, float3 n, ShaderLight light) if (distance > outerRange) return float3(0, 0, 0); - else - { - float d = saturate(dot(n, -lightVec)); - if (d < 0) - return float3(0, 0, 0); - else - { - float cosine = dot(lightVec, direction); + + float d = saturate(dot(normal, -lightVec)); + if (d < 0) + return float3(0, 0, 0); + + float cosine = dot(lightVec, direction); - float minCosineIn = cos(coneIn * (PI / 180.0f)); - float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); + float minCosineIn = cos(coneIn * (PI / 180.0f)); + float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); - float minCosineOut = cos(coneOut * (PI / 180.0f)); - float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); + float minCosineOut = cos(coneOut * (PI / 180.0f)); + float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); - float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); - - if (attenuation > 0.0f) - { - float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); - return saturate(color * intensity * attenuation * falloff * d); - } - else - return float3(0, 0, 0); - } - } + float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); + + if (attenuation <= 0.0f) + return float3(0, 0, 0); + + float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); + return saturate(color * intensity * attenuation * falloff * d); } -float3 DoDirectionalLight(float3 pos, float3 n, ShaderLight light) +float3 DoDirectionalLight(float3 pos, float3 normal, ShaderLight light) { float3 color = light.Color.xyz; float3 intensity = light.Intensity; float3 direction = -light.Direction.xyz; - float d = max(dot(direction, n), .0f); + float d = max(dot(direction, normal), .0f); - if (d > 0.f) - { - return (color * intensity * d); - } - - return float3(0, 0, 0); + if (d <= 0.0f) + return float3(0, 0, 0); + + return (color * intensity * d); } float DoFogBulb(float3 pos, ShaderFogBulb bulb) @@ -464,7 +447,6 @@ float3 StaticLight(float3 vertex, float3 tex, float fogBulbsDensity) float3 result = tex * vertex; result -= float3(fogBulbsDensity, fogBulbsDensity, fogBulbsDensity); - return saturate(result); } diff --git a/TombEngine/Shaders/Shadows.hlsli b/TombEngine/Shaders/Shadows.hlsli index 72bc0df1b..373181408 100644 --- a/TombEngine/Shaders/Shadows.hlsli +++ b/TombEngine/Shaders/Shadows.hlsli @@ -1,3 +1,5 @@ +#include "./ShaderLight.hlsli" + #define SHADOW_INTENSITY (0.55f) #define INV_SHADOW_INTENSITY (1.0f - SHADOW_INTENSITY) @@ -66,73 +68,6 @@ float2 GetCubeUVFromDir(int faceIndex, float3 dir) return uv * .5 + .5; } -void DoPointLightShadow(float3 worldPos, inout float3 lighting) -{ - float shadowFactor = 1.0f; - for (int i = 0; i < 6; i++) - { - float3 dir = normalize(worldPos - Light.Position); - int face = GetCubeFaceIndex(dir); - //debug coloring - /* - switch (face) - { - case 0: - lighting += float3(0.2, 0, 0); - break; - case 1: - lighting += float3(0.1, 0, 0); - - break; - case 2: - lighting += float3(0, 0.2, 0); - - break; - case 3: - lighting += float3(0, 0.1, 0); - - break; - case 4: - lighting += float3(0, 0, 0.2); - - break; - default: - lighting += float3(0, 0, 0.1); - - break; - } - */ - float2 uv = GetCubeUVFromDir(face, dir); - float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[i]); - lightClipSpace.xyz /= lightClipSpace.w; - if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f && - lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f && - lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f) - { - lightClipSpace.x = lightClipSpace.x / 2 + 0.5; - lightClipSpace.y = lightClipSpace.y / -2 + 0.5; - - float sum = 0; - float x, y; - - // Perform PCF filtering on a 4 x 4 texel neighborhood - // what about borders of cubemap? - for (y = -1.5; y <= 1.5; y += 1.0) - { - for (x = -1.5; x <= 1.5; x += 1.0) - { - sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z); - } - } - - shadowFactor = sum / 16.0; - } - } - float distanceFactor = saturate(((distance(worldPos, Light.Position)) / (Light.Out))); - lighting *= saturate((shadowFactor + SHADOW_INTENSITY) + (pow(distanceFactor, 4) * INV_SHADOW_INTENSITY)); -} - - void DoBlobShadows(float3 worldPos, inout float3 lighting) { float shadowFactor = 1.0f; @@ -151,36 +86,77 @@ void DoBlobShadows(float3 worldPos, inout float3 lighting) lighting *= saturate((shadowFactor + SHADOW_INTENSITY)); } -void DoSpotLightShadow(float3 worldPos, inout float3 lighting) +void DoPointLightShadow(float3 worldPos, inout float3 lighting) { - float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[0]); - lightClipSpace.xyz /= lightClipSpace.w; - float2 shadowUV = lightClipSpace.xy * 0.5f + 0.5f; - shadowUV.y = (1 - shadowUV.y); float shadowFactor = 1.0f; - if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f && - lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f && - lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f) + for (int i = 0; i < 6; i++) { - float sum = 0; - float x, y; - //perform PCF filtering on a 4 x 4 texel neighborhood - - for (y = -1.5; y <= 1.5; y += 1.0) + float3 dir = normalize(worldPos - Light.Position); + float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[i]); + lightClipSpace.xyz /= lightClipSpace.w; + + if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f && + lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f && + lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f) { - for (x = -1.5; x <= 1.5; x += 1.0) + lightClipSpace.x = lightClipSpace.x / 2 + 0.5; + lightClipSpace.y = lightClipSpace.y / -2 + 0.5; + + float sum = 0; + float x, y; + + // Perform PCF filtering on a 4 x 4 texel neighborhood. + for (y = -1.5; y <= 1.5; y += 1.0) { - sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(shadowUV.xy + TexOffset(x, y), 0), lightClipSpace.z); + for (x = -1.5; x <= 1.5; x += 1.0) + { + sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z); + } } + + shadowFactor = sum / 16.0; } - - shadowFactor = sum / 16.0; - } - // Fade out towards the borders of the sampled texture - // for that we simply compare the distance between the shadow texture UV coordinate we sampled and the center (0.5,0.5) of the shadow texture - // use pow to "boost" the shadow intensity towards the center - float angleFactor = saturate(pow(distance(shadowUV.xy, 0.5f) * 2,2.2f)); - - lighting *= saturate((shadowFactor + SHADOW_INTENSITY) + ((angleFactor) * INV_SHADOW_INTENSITY)); + + // Compute attenuation and combine lighting contribution with shadow factor + float distanceFactor = saturate(((distance(worldPos, Light.Position)) / (Light.Out))); + lighting *= saturate((shadowFactor + SHADOW_INTENSITY) + (pow(distanceFactor, 4) * INV_SHADOW_INTENSITY)); +} + +void DoSpotLightShadow(float3 worldPos, float3 normal, inout float3 lighting) +{ + float influence = 1.0f - Luma(DoSpotLight(worldPos, normal, Light)); + + float shadowFactor = 1.0f; + for (int i = 0; i < 6; i++) + { + float3 dir = normalize(worldPos - Light.Position); + float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[i]); + lightClipSpace.xyz /= lightClipSpace.w; + + if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f && + lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f && + lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f) + { + lightClipSpace.x = lightClipSpace.x / 2 + 0.5; + lightClipSpace.y = lightClipSpace.y / -2 + 0.5; + + float sum = 0; + float x, y; + + // Perform PCF filtering on a 4 x 4 texel neighborhood. + for (y = -1.5; y <= 1.5; y += 1.0) + { + for (x = -1.5; x <= 1.5; x += 1.0) + { + sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z); + } + } + + shadowFactor = sum / 16.0; + } + } + + // Compute attenuation and combine lighting contribution with shadow factor + lighting *= saturate((shadowFactor + SHADOW_INTENSITY) + (pow(influence, 4) * INV_SHADOW_INTENSITY)); } diff --git a/TombEngine/Specific/trutils.cpp b/TombEngine/Specific/trutils.cpp index 0ed7885f2..dd8cdb28b 100644 --- a/TombEngine/Specific/trutils.cpp +++ b/TombEngine/Specific/trutils.cpp @@ -155,6 +155,21 @@ namespace TEN::Utils return strings; } + int GetHash(const std::string& string) + { + if (string.empty()) + return 0; + + uint32_t hash = 2166136261u; + for (char c : string) + { + hash ^= static_cast(c); + hash *= 16777619u; + } + + return static_cast(hash); + } + Vector2 GetAspectCorrect2DPosition(const Vector2& pos) { constexpr auto DISPLAY_SPACE_ASPECT = DISPLAY_SPACE_RES.x / DISPLAY_SPACE_RES.y; diff --git a/TombEngine/Specific/trutils.h b/TombEngine/Specific/trutils.h index 763beb1ec..5b094821c 100644 --- a/TombEngine/Specific/trutils.h +++ b/TombEngine/Specific/trutils.h @@ -12,6 +12,7 @@ namespace TEN::Utils std::wstring ToWString(const std::string& string); std::wstring ToWString(const char* cString); std::vector SplitString(const std::wstring& string); + int GetHash(const std::string& string); // 2D space utilities Vector2 GetAspectCorrect2DPosition(const Vector2& pos); From 12ea734bb754e6a1474ca510de73f70497591cda Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 29 Nov 2024 00:08:30 +0100 Subject: [PATCH 02/26] Fixed #1493 --- TombEngine/Game/Lara/lara_tests.cpp | 2 +- TombEngine/Game/collision/collide_item.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/TombEngine/Game/Lara/lara_tests.cpp b/TombEngine/Game/Lara/lara_tests.cpp index d2124d9ec..745d8d478 100644 --- a/TombEngine/Game/Lara/lara_tests.cpp +++ b/TombEngine/Game/Lara/lara_tests.cpp @@ -1778,7 +1778,7 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, fl bool atLeastOnePoleCollided = false; - auto collObjects = GetCollidedObjects(*item, true, false, BLOCK(1), ObjectCollectionMode::Items); + auto collObjects = GetCollidedObjects(*item, true, false, BLOCK(2), ObjectCollectionMode::Items); if (!collObjects.IsEmpty()) { auto laraBox = GameBoundingBox(item).ToBoundingOrientedBox(item->Pose); diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index 7970ec5dc..e9d74b743 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -118,7 +118,10 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, // Override extents if specified. if (customRadius > 0.0f) + { collidingAabb = BoundingBox(collidingItem.Pose.Position.ToVector3(), Vector3(customRadius)); + convertedBounds.Extents = Vector3(customRadius); + } // Run through neighboring rooms. const auto& room = g_Level.Rooms[collidingItem.RoomNumber]; From a4d620d7fdec19cdd3d719a9ac92763124795b0c Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 29 Nov 2024 01:08:16 +0100 Subject: [PATCH 03/26] Rollback ShaderLight --- TombEngine/Shaders/ShaderLight.hlsli | 252 ++++++++++++++------------- 1 file changed, 135 insertions(+), 117 deletions(-) diff --git a/TombEngine/Shaders/ShaderLight.hlsli b/TombEngine/Shaders/ShaderLight.hlsli index 26a7ff8b7..96744b309 100644 --- a/TombEngine/Shaders/ShaderLight.hlsli +++ b/TombEngine/Shaders/ShaderLight.hlsli @@ -6,115 +6,122 @@ float3 DoSpecularPoint(float3 pos, float3 n, ShaderLight light, float strength) { - if ((strength <= 0.0)) + if (strength <= 0.0) return float3(0, 0, 0); - - float3 lightPos = light.Position.xyz; - float radius = light.Out; + else + { + float3 lightPos = light.Position.xyz; + float radius = light.Out; - float dist = distance(lightPos, pos); - if (dist > radius) - return float3(0, 0, 0); - - float3 lightDir = normalize(lightPos - pos); - float3 reflectDir = reflect(lightDir, n); + float dist = distance(lightPos, pos); + if (dist > radius) + return float3(0, 0, 0); + else + { + float3 lightDir = normalize(lightPos - pos); + float3 reflectDir = reflect(lightDir, n); - float3 color = light.Color.xyz; - float intensity = saturate(light.Intensity); - float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); - float attenuation = (radius - dist) / radius; + float3 color = light.Color.xyz; + float intensity = saturate(light.Intensity); + float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); + float attenuation = (radius - dist) / radius; - return attenuation * spec * color * intensity; + return attenuation * spec * color * intensity; + } + } } float3 DoSpecularSun(float3 n, ShaderLight light, float strength) { if (strength <= 0.0) return float3(0, 0, 0); - - float3 lightDir = -normalize(light.Direction); - float3 reflectDir = reflect(lightDir, n); + else + { + float3 lightDir = -normalize(light.Direction); + float3 reflectDir = reflect(lightDir, n); - float3 color = light.Color.xyz; - float intensity = saturate(light.Intensity); - float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); + float3 color = light.Color.xyz; + float intensity = saturate(light.Intensity); + float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); - return spec * color * intensity; + return spec * color * intensity; + } } -float3 DoSpecularSpot(float3 pos, float3 normal, ShaderLight light, float strength) +float3 DoSpecularSpot(float3 pos, float3 n, ShaderLight light, float strength) { if (strength <= 0.0) return float3(0, 0, 0); - + else + { + float3 lightPos = light.Position.xyz; + float3 direction = light.Direction.xyz; + float innerRange = light.In; + float outerRange = light.Out; + float coneIn = light.InRange; + float coneOut = light.OutRange; + + float3 lightVec = pos - lightPos; + float distance = length(lightVec); + lightVec = normalize(lightVec); + + if (distance > outerRange) + return float3(0, 0, 0); + else + { + float cosine = dot(lightVec, direction); + + float minCosineIn = cos(coneIn * (PI / 180.0f)); + float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); + + float minCosineOut = cos(coneOut * (PI / 180.0f)); + float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); + + float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); + + if (attenuation > 0.0f) + { + float3 lightDir = -lightVec; + float3 reflectDir = reflect(lightDir, n); + + float3 color = light.Color.xyz; + float intensity = saturate(light.Intensity); + float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); + float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); + + return attenuation * spec * color * intensity * falloff; + } + else + return float3(0, 0, 0); + } + } +} + +float3 DoPointLight(float3 pos, float3 n, ShaderLight light) +{ float3 lightPos = light.Position.xyz; - float3 direction = light.Direction.xyz; - float innerRange = light.In; - float outerRange = light.Out; - float coneIn = light.InRange; - float coneOut = light.OutRange; - - float3 lightVec = pos - lightPos; - float distance = length(lightVec); - lightVec = normalize(lightVec); - - if (distance > outerRange) - return float3(0, 0, 0); - - float cosine = dot(lightVec, direction); - - float minCosineIn = cos(coneIn * (PI / 180.0f)); - float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); - - float minCosineOut = cos(coneOut * (PI / 180.0f)); - float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); - - float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); - - if (attenuation <= 0.0f) - return float3(0, 0, 0); - - float3 lightDir = -lightVec; - float3 reflectDir = reflect(lightDir, normal); - float3 color = light.Color.xyz; float intensity = saturate(light.Intensity); - float spec = pow(saturate(dot(CamDirectionWS.xyz, reflectDir)), strength * SPEC_FACTOR); - float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); - return attenuation * spec * color * intensity * falloff; + float3 lightVec = (lightPos - pos); + float distance = length(lightVec); + + if (distance > light.Out) + return float3(0, 0, 0); + else + { + lightVec = normalize(lightVec); + float d = saturate(dot(n, lightVec)); + + float attenuation = 1.0f; + if (distance > light.In) + attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); + + return saturate(color * intensity * attenuation * d); + } } -float3 DoPointLight(float3 pos, float3 normal, ShaderLight light) -{ - float3 lightPos = light.Position.xyz; - float3 color = light.Color.xyz; - float intensity = saturate(light.Intensity); - - float3 lightVec = (lightPos - pos); - float distance = length(lightVec); - - if (distance > light.Out) - return float3(0, 0, 0); - - // Normalize lightVec and calculate the dot product with the normal - lightVec = normalize(lightVec); - float d = saturate(dot(normal, lightVec)); - - // Compute attenuation based on distance and light range - float attenuation = 1.0f; - if (distance > light.In) - attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); - - // Add a softening factor to handle spikes near surfaces - float softening = saturate((distance - 0.1f) / 0.1f); // Softens for distances < 0.1 - softening = smoothstep(0.0f, 0.1f, distance); // Alternative smooth transition - - // Return the softened light contribution - return saturate(color * intensity * attenuation * d * softening); -} - -float3 DoShadowLight(float3 pos, float3 normal, ShaderLight light) +float3 DoShadowLight(float3 pos, float3 n, ShaderLight light) { float3 lightPos = light.Position.xyz; float3 color = light.Color.xyz; @@ -125,21 +132,23 @@ float3 DoShadowLight(float3 pos, float3 normal, ShaderLight light) if (distance > light.Out) return float3(0, 0, 0); - - lightVec = normalize(lightVec); - float d = saturate(dot(normal, lightVec)); + else + { + lightVec = normalize(lightVec); + float d = saturate(dot(n, lightVec)); - float attenuation = 1.0f; - if (distance > light.In) - attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); + float attenuation = 1.0f; + if (distance > light.In) + attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); - float absolute = float3(color * intensity * attenuation); - float directional = absolute * d; + float absolute = float3(color * intensity * attenuation); + float directional = absolute * d; - return ((absolute * 0.33f) + (directional * 0.66f)) * 2.0f; + return ((absolute * 0.33f) + (directional * 0.66f)) * 2.0f; + } } -float3 DoSpotLight(float3 pos, float3 normal, ShaderLight light) +float3 DoSpotLight(float3 pos, float3 n, ShaderLight light) { float3 lightPos = light.Position.xyz; float3 color = light.Color.xyz; @@ -156,26 +165,32 @@ float3 DoSpotLight(float3 pos, float3 normal, ShaderLight light) if (distance > outerRange) return float3(0, 0, 0); - - float d = saturate(dot(normal, -lightVec)); - if (d < 0) - return float3(0, 0, 0); - - float cosine = dot(lightVec, direction); + else + { + float d = saturate(dot(n, -lightVec)); + if (d < 0) + return float3(0, 0, 0); + else + { + float cosine = dot(lightVec, direction); - float minCosineIn = cos(coneIn * (PI / 180.0f)); - float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); + float minCosineIn = cos(coneIn * (PI / 180.0f)); + float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); - float minCosineOut = cos(coneOut * (PI / 180.0f)); - float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); + float minCosineOut = cos(coneOut * (PI / 180.0f)); + float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); - float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); - - if (attenuation <= 0.0f) - return float3(0, 0, 0); - - float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); - return saturate(color * intensity * attenuation * falloff * d); + float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); + + if (attenuation > 0.0f) + { + float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); + return saturate(color * intensity * attenuation * falloff * d); + } + else + return float3(0, 0, 0); + } + } } float3 DoDirectionalLight(float3 pos, float3 normal, ShaderLight light) @@ -186,10 +201,12 @@ float3 DoDirectionalLight(float3 pos, float3 normal, ShaderLight light) float d = max(dot(direction, normal), .0f); - if (d <= 0.0f) - return float3(0, 0, 0); - - return (color * intensity * d); + if (d > 0.f) + { + return (color * intensity * d); + } + + return float3(0, 0, 0); } float DoFogBulb(float3 pos, ShaderFogBulb bulb) @@ -447,6 +464,7 @@ float3 StaticLight(float3 vertex, float3 tex, float fogBulbsDensity) float3 result = tex * vertex; result -= float3(fogBulbsDensity, fogBulbsDensity, fogBulbsDensity); + return saturate(result); } From 8edd446a6dd2e1c70ac220811ba56cc32151e1c6 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 30 Nov 2024 18:43:39 +0100 Subject: [PATCH 04/26] Simplify light and shadow light collection --- TombEngine/Renderer/RendererFrame.cpp | 75 +++++++++------------------ TombEngine/Renderer/RendererInit.cpp | 13 +++++ 2 files changed, 38 insertions(+), 50 deletions(-) diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index e1260fb6f..390f50a4e 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -542,6 +542,15 @@ namespace TEN::Renderer RendererLight* brightestLight = nullptr; float brightest = 0.0f; + auto updateBrightestLight = [&brightestLight, &brightest, prioritizeShadowLight](RendererLight& light, float intensity) + { + if (light.CastShadows && prioritizeShadowLight && intensity >= brightest) + { + brightest = intensity; + brightestLight = &light; + } + }; + // Dynamic lights have the priority for (auto& light : _dynamicLights[_dynamicLightList]) { @@ -562,6 +571,8 @@ namespace TEN::Renderer float attenuation = 1.0f - distance / light.Out; float intensity = attenuation * light.Intensity * light.Luma; + updateBrightestLight(light, intensity); + RendererLightNode node = { &light, intensity, distance, 1 }; tempLights.push_back(node); } @@ -574,80 +585,44 @@ namespace TEN::Renderer auto& currentRoom = _rooms[roomToCheck]; int lightCount = (int)currentRoom.Lights.size(); - for (int j = 0; j < lightCount; j++) + for (auto& light : currentRoom.Lights) { - auto* light = ¤tRoom.Lights[j]; - float intensity = 0; float dist = 0; // Check only lights different from sun. - if (light->Type == LightType::Sun) + if (light.Type == LightType::Sun) { // Suns from non-adjacent rooms not added. if (roomToCheck != roomNumber && (prevRoomNumber != roomToCheck || prevRoomNumber == NO_VALUE)) continue; // Sun is added without distance checks. - intensity = light->Intensity * Luma(light->Color); + intensity = light.Intensity * Luma(light.Color); } - else if (light->Type == LightType::Point || light->Type == LightType::Shadow) + else if (light.Type == LightType::Point || + light.Type == LightType::Shadow || + light.Type == LightType::Spot) { float distSqr = - SQUARE(position.x - light->Position.x) + - SQUARE(position.y - light->Position.y) + - SQUARE(position.z - light->Position.z); - - // Collect only lights nearer than 20 blocks. - if (distSqr >= SQUARE(BLOCK(20))) - continue; - - // Check out radius. - if (distSqr > SQUARE(light->Out + radius)) - continue; - - dist = sqrt(distSqr); - float attenuation = 1.0f - dist / light->Out; - intensity = attenuation * light->Intensity * Luma(light->Color); - - // If collecting shadows, try collecting shadow-casting light. - if (light->CastShadows && prioritizeShadowLight && light->Type == LightType::Point) - { - if (intensity >= brightest) - { - brightest = intensity; - brightestLight = light; - } - } - } - else if (light->Type == LightType::Spot) - { - float distSqr = - SQUARE(position.x - light->Position.x) + - SQUARE(position.y - light->Position.y) + - SQUARE(position.z - light->Position.z); + SQUARE(position.x - light.Position.x) + + SQUARE(position.y - light.Position.y) + + SQUARE(position.z - light.Position.z); // Collect only lights nearer than 20 blocks. if (distSqr >= SQUARE(BLOCK(20))) continue; // Check range. - if (distSqr > SQUARE(light->Out + radius)) + if (distSqr > SQUARE(light.Out + radius)) continue; dist = sqrt(distSqr); - float attenuation = 1.0f - dist / light->Out; - intensity = attenuation * light->Intensity * light->Luma; + float attenuation = 1.0f - dist / light.Out; + intensity = attenuation * light.Intensity * light.Luma; // If shadow pointer provided, try collecting shadow-casting light. - if (light->CastShadows && prioritizeShadowLight) - { - if (intensity >= brightest) - { - brightest = intensity; - brightestLight = light; - } - } + updateBrightestLight(light, intensity); } else { @@ -655,7 +630,7 @@ namespace TEN::Renderer continue; } - RendererLightNode node = { light, intensity, dist, 0 }; + RendererLightNode node = { &light, intensity, dist, 0 }; if (roomsLights != nullptr) roomsLights->push_back(node); diff --git a/TombEngine/Renderer/RendererInit.cpp b/TombEngine/Renderer/RendererInit.cpp index 08d9b11a9..b40390982 100644 --- a/TombEngine/Renderer/RendererInit.cpp +++ b/TombEngine/Renderer/RendererInit.cpp @@ -653,6 +653,19 @@ namespace TEN::Renderer } Utils::throwIfFailed(res); + + // Display the device name and properties. + + IDXGIAdapter* adapter = nullptr; + IDXGIDevice* dxgiDevice = nullptr; + _device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice); + + dxgiDevice->GetAdapter(&adapter); + + DXGI_ADAPTER_DESC desc; + adapter->GetDesc(&desc); + auto str = std::wstring(desc.Description); + TENLog("Device created: " + std::string(str.begin(), str.end()), LogLevel::Info); } void Renderer::ToggleFullScreen(bool force) From dc0b746c319b4b381a0656e2e04d0dab13bed50f Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:27:33 +0100 Subject: [PATCH 05/26] Fixed BitField class bottleneck --- TombEngine/Renderer/RendererDraw.cpp | 2 +- TombEngine/Specific/BitField.cpp | 36 +++++++++++++++------------- TombEngine/Specific/BitField.h | 10 +------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index adb3d7ef7..52197f800 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -2338,7 +2338,7 @@ namespace TEN::Renderer for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++) { - if (!(nativeItem->MeshBits & (1 << k))) + if (!nativeItem->MeshBits.Test(k)) continue; DrawMoveableMesh(item, GetMesh(item->MeshIds[k]), room, k, view, rendererPass); diff --git a/TombEngine/Specific/BitField.cpp b/TombEngine/Specific/BitField.cpp index 45823a0cc..adfcd5ef2 100644 --- a/TombEngine/Specific/BitField.cpp +++ b/TombEngine/Specific/BitField.cpp @@ -38,6 +38,17 @@ namespace TEN::Utils _bits.push_back(bit == '1'); } + bool BitField::IndexIsCorrect(unsigned int index) const + { + if (index >= _bits.size()) + { + TENLog(std::string("BitField attempted to access bit at invalid index."), LogLevel::Warning); + return false; + } + + return true; + } + unsigned int BitField::GetSize() const { return (unsigned int)_bits.size(); @@ -59,11 +70,8 @@ namespace TEN::Utils { for (const unsigned int& index : indices) { - if (index >= _bits.size()) - { - TENLog(std::string("BitField attempted to set bit at invalid index."), LogLevel::Warning); + if (!IndexIsCorrect(index)) continue; - } _bits[index] = true; } @@ -83,11 +91,8 @@ namespace TEN::Utils { for (const unsigned int& index : indices) { - if (index >= _bits.size()) - { - TENLog(std::string("BitField attempted to clear bit at invalid index."), LogLevel::Warning); + if (!IndexIsCorrect(index)) continue; - } _bits[index] = false; } @@ -107,11 +112,8 @@ namespace TEN::Utils { for (const unsigned int& index : indices) { - if (index >= _bits.size()) - { - TENLog(std::string("BitField attempted to flip bit at invalid index."), LogLevel::Warning); + if (!IndexIsCorrect(index)) continue; - } _bits[index].flip(); } @@ -131,11 +133,8 @@ namespace TEN::Utils { for (const unsigned int& index : indices) { - if (index >= _bits.size()) - { - TENLog(std::string("BitField attempted to test bit at invalid index."), LogLevel::Warning); + if (!IndexIsCorrect(index)) continue; - } // Test if any bits at input indices are set. if (testAny) @@ -156,7 +155,10 @@ namespace TEN::Utils bool BitField::Test(unsigned int index) const { - return Test(std::vector{ index }); + if (!IndexIsCorrect(index)) + return false; + + return _bits[index]; } bool BitField::TestAny() const diff --git a/TombEngine/Specific/BitField.h b/TombEngine/Specific/BitField.h index 5bd3104e3..ecab1819b 100644 --- a/TombEngine/Specific/BitField.h +++ b/TombEngine/Specific/BitField.h @@ -8,29 +8,23 @@ namespace TEN::Utils class BitField { private: - // Constants - static constexpr auto SIZE_DEFAULT = 32; - // Members - std::vector _bits = {}; + bool IndexIsCorrect(unsigned int index) const; public: // Presets - static const BitField Empty; static const BitField Default; // Constructors - BitField(); BitField(unsigned int size); BitField(unsigned int size, unsigned int packedBits); BitField(const std::string& bitString); // Getters - unsigned int GetSize() const; unsigned int GetCount() const; @@ -46,14 +40,12 @@ namespace TEN::Utils void FlipAll(); // Inquirers - bool Test(const std::vector& indices, bool testAny = true) const; bool Test(unsigned int index) const; bool TestAny() const; bool TestAll() const; // Converters - unsigned int ToPackedBits() const; std::string ToString() const; From 5ab627af83154e4b9cee42dc6d0bc5d05c7d6ded Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:31:56 +0100 Subject: [PATCH 06/26] Revert unneeded code, as display adapter name is showing in debug page --- TombEngine/Renderer/RendererInit.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/TombEngine/Renderer/RendererInit.cpp b/TombEngine/Renderer/RendererInit.cpp index b40390982..08d9b11a9 100644 --- a/TombEngine/Renderer/RendererInit.cpp +++ b/TombEngine/Renderer/RendererInit.cpp @@ -653,19 +653,6 @@ namespace TEN::Renderer } Utils::throwIfFailed(res); - - // Display the device name and properties. - - IDXGIAdapter* adapter = nullptr; - IDXGIDevice* dxgiDevice = nullptr; - _device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice); - - dxgiDevice->GetAdapter(&adapter); - - DXGI_ADAPTER_DESC desc; - adapter->GetDesc(&desc); - auto str = std::wstring(desc.Description); - TENLog("Device created: " + std::string(str.begin(), str.end()), LogLevel::Info); } void Renderer::ToggleFullScreen(bool force) From bc20b9194f2f0d6af426685c628552c2145569b9 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:43:22 +0100 Subject: [PATCH 07/26] Remove double light calculations from the room shader --- TombEngine/Shaders/Rooms.fx | 13 ------------- TombEngine/Shaders/ShaderLight.hlsli | 3 +++ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/TombEngine/Shaders/Rooms.fx b/TombEngine/Shaders/Rooms.fx index af2dac3ee..4e8d379f7 100644 --- a/TombEngine/Shaders/Rooms.fx +++ b/TombEngine/Shaders/Rooms.fx @@ -160,19 +160,6 @@ PixelShaderOutput PS(PixelShaderInput input) for (int i = 0; i < NumRoomLights; i++) { int lightType = RoomLights[i].Type; - float3 lightPos = RoomLights[i].Position.xyz; - float3 color = RoomLights[i].Color.xyz; - float radius = RoomLights[i].Out; - - float3 lightVec = (lightPos - input.WorldPosition); - float distance = length(lightVec); - if (distance > radius) - continue; - - lightVec = normalize(lightVec); - float d = saturate(dot(normal, lightVec )); - if (d < 0) - continue; if (lightType == LT_POINT) { diff --git a/TombEngine/Shaders/ShaderLight.hlsli b/TombEngine/Shaders/ShaderLight.hlsli index 96744b309..d082f8ca5 100644 --- a/TombEngine/Shaders/ShaderLight.hlsli +++ b/TombEngine/Shaders/ShaderLight.hlsli @@ -113,6 +113,9 @@ float3 DoPointLight(float3 pos, float3 n, ShaderLight light) lightVec = normalize(lightVec); float d = saturate(dot(n, lightVec)); + if (d < 0) + return float3(0, 0, 0); + float attenuation = 1.0f; if (distance > light.In) attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); From bc2590872635f0cf4cbfb9264301bc83085df9d9 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:47:19 +0100 Subject: [PATCH 08/26] Simplify and optimize light collector --- TombEngine/Renderer/RendererFrame.cpp | 117 +++++++++----------------- 1 file changed, 41 insertions(+), 76 deletions(-) diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index 390f50a4e..c0293390c 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -530,7 +530,7 @@ namespace TEN::Renderer void Renderer::CollectLights(Vector3 position, float radius, int roomNumber, int prevRoomNumber, bool prioritizeShadowLight, bool useCachedRoomLights, std::vector* roomsLights, std::vector* outputLights) { - if (_rooms.size() < roomNumber) + if (_rooms.size() <= roomNumber) return; // Now collect lights from dynamic list and from rooms @@ -540,56 +540,50 @@ namespace TEN::Renderer auto& room = _rooms[roomNumber]; RendererLight* brightestLight = nullptr; - float brightest = 0.0f; + float highestIntensity = 0.0f; - auto updateBrightestLight = [&brightestLight, &brightest, prioritizeShadowLight](RendererLight& light, float intensity) + auto calculateIntensity = [](float distSqr, const RendererLight& light, float radius) -> std::optional { - if (light.CastShadows && prioritizeShadowLight && intensity >= brightest) + if (distSqr >= SQUARE(BLOCK(20)) || distSqr > SQUARE(light.Out + radius)) + return std::nullopt; // Light is too far. + + float distance = sqrt(distSqr); + float attenuation = 1.0f - distance / light.Out; + return attenuation * light.Intensity * light.Luma; + }; + + auto processLight = [&](RendererLight& light, float distSqr, int dynamicFlag) + { + float distance = sqrt(distSqr); + float intensity = calculateIntensity(distSqr, light, radius).value_or(0.0f); + + if (intensity <= EPSILON) + return; + + if (light.CastShadows && prioritizeShadowLight && intensity >= highestIntensity) { - brightest = intensity; + highestIntensity = intensity; brightestLight = &light; } + + tempLights.push_back({ &light, intensity, distance, dynamicFlag }); }; // Dynamic lights have the priority for (auto& light : _dynamicLights[_dynamicLightList]) { - float distSqr = - SQUARE(position.x - light.Position.x) + - SQUARE(position.y - light.Position.y) + - SQUARE(position.z - light.Position.z); - - // Collect only lights nearer than 20 sectors - if (distSqr >= SQUARE(BLOCK(20))) - continue; - - // Check the out radius - if (distSqr > SQUARE(light.Out + radius)) - continue; - - float distance = sqrt(distSqr); - float attenuation = 1.0f - distance / light.Out; - float intensity = attenuation * light.Intensity * light.Luma; - - updateBrightestLight(light, intensity); - - RendererLightNode node = { &light, intensity, distance, 1 }; - tempLights.push_back(node); + float distSqr = Vector3::DistanceSquared(position, light.Position); + processLight(light, distSqr, 1); } - + if (!useCachedRoomLights) { // Check current room and neighbor rooms. for (int roomToCheck : room.Neighbors) { auto& currentRoom = _rooms[roomToCheck]; - int lightCount = (int)currentRoom.Lights.size(); - for (auto& light : currentRoom.Lights) { - float intensity = 0; - float dist = 0; - // Check only lights different from sun. if (light.Type == LightType::Sun) { @@ -598,70 +592,41 @@ namespace TEN::Renderer continue; // Sun is added without distance checks. - intensity = light.Intensity * Luma(light.Color); + float intensity = light.Intensity * Luma(light.Color); + RendererLightNode node = { &light, intensity, 0.0f, 0 }; + tempLights.push_back(node); + + if (roomsLights != nullptr) + roomsLights->push_back(node); } else if (light.Type == LightType::Point || light.Type == LightType::Shadow || light.Type == LightType::Spot) { - float distSqr = - SQUARE(position.x - light.Position.x) + - SQUARE(position.y - light.Position.y) + - SQUARE(position.z - light.Position.z); - - // Collect only lights nearer than 20 blocks. - if (distSqr >= SQUARE(BLOCK(20))) - continue; - - // Check range. - if (distSqr > SQUARE(light.Out + radius)) - continue; - - dist = sqrt(distSqr); - float attenuation = 1.0f - dist / light.Out; - intensity = attenuation * light.Intensity * light.Luma; - - // If shadow pointer provided, try collecting shadow-casting light. - updateBrightestLight(light, intensity); + float distSqr = Vector3::DistanceSquared(position, light.Position); + processLight(light, distSqr, 0); } else { // Invalid light type. continue; } - - RendererLightNode node = { &light, intensity, dist, 0 }; - - if (roomsLights != nullptr) - roomsLights->push_back(node); - - tempLights.push_back(node); } } } else { - for (int i = 0; i < roomsLights->size(); i++) - tempLights.push_back(roomsLights->at(i)); + for (auto& node : *roomsLights) + tempLights.push_back(node); } // Sort lights. if (tempLights.size() > MAX_LIGHTS_PER_ITEM) { - std::sort( - tempLights.begin(), - tempLights.end(), - [](RendererLightNode a, RendererLightNode b) - { - if (a.Dynamic == b.Dynamic) - { - return (a.LocalIntensity > b.LocalIntensity); - } - else - { - return (a.Dynamic > b.Dynamic); - } - }); + std::sort(tempLights.begin(), tempLights.end(), [](const RendererLightNode& a, const RendererLightNode& b) + { + return (a.Dynamic == b.Dynamic) ? (a.LocalIntensity > b.LocalIntensity) : (a.Dynamic > b.Dynamic); + }); } // Put actual lights in provided vector. @@ -672,7 +637,7 @@ namespace TEN::Renderer outputLights->push_back(brightestLight); // Add max 8 lights per item, including shadow light for player eventually. - for (auto l : tempLights) + for (auto& l : tempLights) { if (prioritizeShadowLight && brightestLight == l.Light) continue; From 364458d68adbe0d1d1ae27e108f557fc205c157a Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 2 Dec 2024 01:19:05 +0100 Subject: [PATCH 09/26] Update RendererDraw.cpp --- TombEngine/Renderer/RendererDraw.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index 52197f800..f298ce6c0 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -1574,6 +1574,7 @@ namespace TEN::Renderer dynamicLight.RoomNumber = NO_VALUE; dynamicLight.Intensity = 1.0f; dynamicLight.Position = pos; + dynamicLight.In = 1.0f; dynamicLight.Out = radius; dynamicLight.Type = LightType::Point; dynamicLight.CastShadows = castShadows; From dbaaf9fd5e10f39a0f092d9c25dd9f1d08ad1877 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 2 Dec 2024 02:48:28 +0100 Subject: [PATCH 10/26] Fixed #1500 --- CHANGELOG.md | 1 + TombEngine/Game/control/trigger.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e6a4cbba..411d53f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed stutter during jumps between cameras in a flyby sequence. * Fixed uzi targeting issues after using flycheat. * Fixed snow particles not always melting on the ground. +* Fixed enemies not damaging Lara if she is staying on the sector where enemies were triggered. * Fixed enemy pickups dropping on death sectors. * Fixed Sarcophagus and Search Object pickup triggers. * Fixed vehicle transfer not happening for levels which were not previously visited. diff --git a/TombEngine/Game/control/trigger.cpp b/TombEngine/Game/control/trigger.cpp index 44e8c9119..ac47e9d19 100644 --- a/TombEngine/Game/control/trigger.cpp +++ b/TombEngine/Game/control/trigger.cpp @@ -370,7 +370,6 @@ void Trigger(short const value, short const flags) if (item->Flags & IFLAG_KILLED) return; - item->TouchBits = NO_JOINT_BITS; item->Flags |= TRIGGERED; if (flags & ONESHOT) @@ -408,6 +407,7 @@ void Trigger(short const value, short const flags) } item->Status = ITEM_ACTIVE; + item->TouchBits = NO_JOINT_BITS; item->DisableInterpolation = true; } } From 9642118cfa8f7c195657a314650a0e400b4caaaa Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 2 Dec 2024 02:56:09 +0100 Subject: [PATCH 11/26] Fixed #1499 --- CHANGELOG.md | 1 + TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 411d53f3e..4876cd53f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Added extra argument for SetAmbientTrack() function to specify if new ambient track should play from the beginning. * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. * Fixed DisplayString class not supporting some Unicode characters and empty lines in multiline strings. +* Fixed DisplayString not being deallocated after showing. * Fixed incorrect behaviour of Moveable:GetJointRotation() function. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. * Fixed Util.HasLineOfSight() not taking static meshes into consideration. diff --git a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp index 8e87c312c..22ecca1d7 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp @@ -30,7 +30,7 @@ Default: nil (i.e. infinite) @tparam bool autoDelete should be string automatically deleted after timeout is reached. If not given, the string will remain allocated even after timeout is reached, and can be shown again without re-initialization. -Default: false +Default: true */ table.set_function(ScriptReserved_ShowString, &StringsHandler::ShowString, this); @@ -93,7 +93,7 @@ void StringsHandler::ShowString(const DisplayString& str, sol::optional n auto it = m_userDisplayStrings.find(str.GetID()); it->second._timeRemaining = numSeconds.value_or(0.0f); it->second._isInfinite = !numSeconds.has_value(); - it->second._deleteWhenZero = autoDelete.value_or(false); + it->second._deleteWhenZero = autoDelete.value_or(true); } bool StringsHandler::IsStringDisplaying(const DisplayString& displayString) From 1f1d3b2aad15c21e064eeee680defd9a79cda168 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:57:17 +0100 Subject: [PATCH 12/26] Delete old documentation when recompiling --- Documentation/compile.bat | 3 + Documentation/doc/1 modules/Misc.html | 1009 ----------------- Documentation/doc/1 modules/Strings.html | 2 +- .../doc/2 classes/DisplaySprite.html | 529 --------- Documentation/doc/4 enums/Flow.BreakMode.html | 166 --- Documentation/doc/4 enums/Flow.GameMode.html | 165 --- Documentation/doc/4 enums/Misc.ActionID.html | 195 ---- .../doc/4 enums/Misc.CameraType.html | 157 --- Documentation/doc/4 enums/Misc.LogLevel.html | 153 --- .../doc/4 enums/Misc.SoundTrackType.html | 154 --- 10 files changed, 4 insertions(+), 2529 deletions(-) delete mode 100644 Documentation/doc/1 modules/Misc.html delete mode 100644 Documentation/doc/2 classes/DisplaySprite.html delete mode 100644 Documentation/doc/4 enums/Flow.BreakMode.html delete mode 100644 Documentation/doc/4 enums/Flow.GameMode.html delete mode 100644 Documentation/doc/4 enums/Misc.ActionID.html delete mode 100644 Documentation/doc/4 enums/Misc.CameraType.html delete mode 100644 Documentation/doc/4 enums/Misc.LogLevel.html delete mode 100644 Documentation/doc/4 enums/Misc.SoundTrackType.html diff --git a/Documentation/compile.bat b/Documentation/compile.bat index d6815322e..4ebed825b 100644 --- a/Documentation/compile.bat +++ b/Documentation/compile.bat @@ -1,8 +1,11 @@ @echo off setlocal +set DOC_DIR=.\doc set LDOC_DIR=.\compiler\ldoc set LUA_PATH=.\compiler\?.lua set LUA_CPATH=.\compiler\?.dll +rmdir /s /q %DOC_DIR% +mkdir %DOC_DIR% .\compiler\lua.exe %LDOC_DIR%\\ldoc.lua %* del output.xml exit /b %ERRORLEVEL% diff --git a/Documentation/doc/1 modules/Misc.html b/Documentation/doc/1 modules/Misc.html deleted file mode 100644 index 4338d7f99..000000000 --- a/Documentation/doc/1 modules/Misc.html +++ /dev/null @@ -1,1009 +0,0 @@ - - - - - TombEngine 1.1.0 Lua API - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Table Misc

-

Functions that don't fit in the other modules.

-

- -

- - -

Functions

-
nameKey(string) string key for the level's (localised) name.
scriptFile (string) Level-specific Lua script file.
Direction()Converts rotation to a direction normal.
__tostring(rotation)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HasLineOfSight(room1, pos1, pos2)Determine if there's a line of sight between two points.
Vibrate(strength, time)Vibrate game controller, if function is available and setting is on.
FadeOut(speed)Do a full-screen fade-to-black.
FadeIn(speed)Do a full-screen fade-in from black.
SetCineBars(height, speed)Move black cinematic bars in from the top and bottom of the game window.
SetFOV(angle)Set field of view.
GetCameraType()Shows the mode of the game camera.
PlayAudioTrack(name, type)Play an audio track
SetAmbientTrack(name)Set and play an ambient track
StopAudioTracks()Stop any audio tracks currently playing
StopAudioTrack(type)Stop audio track that is currently playing
GetAudioTrackLoudness(type)Get current loudness level for specified track type
GetCurrentSubtitle()Get current subtitle string for a voice track currently playing.
PlaySound(sound[, position])Play sound effect
IsSoundPlaying(Sound)Check if the sound effect is playing
IsAudioTrackPlaying(Track)Check if the audio track is playing
FlipMap(flipmap)Do FlipMap with specific ID
PlayFlyBy(flyby)Enable FlyBy with specific ID
CalculateDistance(posA, posB)Calculate the distance between two positions.
CalculateHorizontalDistance(posA, posB)Calculate the horizontal distance between two positions.
PercentToScreen(x, y)Translate a pair of percentages to screen-space pixel coordinates.
ScreenToPercent(x, y)Translate a pair of coordinates to percentages of window dimensions.
ResetObjCamera()Reset object camera back to Lara and deactivate object camera.
PrintLog(message, logLevel[, allowSpam])Write messages within the Log file
Vibrate(strength, time)Vibrate gamepad, if possible.
KeyIsHeld(action)Check if particular action key is held
KeyIsHit(action)Check if particular action key was hit (once)
KeyPush(action)Emulate pushing of a certain action key
KeyClear(action)Clears particular input from action key
- -
-
- - -

Functions

- -
-
- - HasLineOfSight(room1, pos1, pos2) -
-
- Determine if there's a line of sight between two points.

- -

i.e. if we run a direct line from one position to another -will any geometry get in the way?

- -

Note: if you use this with Moveable:GetPosition to test if (for example) -two creatures can see one another, you might have to do some extra adjustments.

- -

This is because the "position" for most objects refers to its base, i.e., the floor. -As a solution, you can increase the y-coordinate of this position to correspond to roughly where the -eyes of the creatures would be. - - - -

Parameters:

-
    -
  • room1 - float - ID of the room where the first position is -
  • -
  • pos1 - Vec3 - first position -
  • -
  • pos2 - Vec3 - second position -
  • -
- -

Returns:

-
    - - bool - is there a direct line of sight between the two positions? -
- - - -

Usage:

-
    -
    local flamePlinthPos = flamePlinth:GetPosition() + Vec3(0, flamePlinthHeight, 0);
    -print(Misc.HasLineOfSight(enemyHead:GetRoomNumber(), enemyHead:GetPosition(), flamePlinthPos))
    -
- -
-
- - Vibrate(strength, time) -
-
- Vibrate game controller, if function is available and setting is on. - - - -

Parameters:

-
    -
  • strength - float - Strength of the vibration -
  • -
  • time - float - (default 0.3) Time of the vibration, in seconds -
  • -
- - - - - -
-
- - FadeOut(speed) -
-
- Do a full-screen fade-to-black. The screen will remain black until a call to FadeIn. - - - -

Parameters:

-
    -
  • speed - float - (default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second. -
  • -
- - - - - -
-
- - FadeIn(speed) -
-
- Do a full-screen fade-in from black. - - - -

Parameters:

-
    -
  • speed - float - (default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second. -
  • -
- - - - - -
-
- - SetCineBars(height, speed) -
-
- Move black cinematic bars in from the top and bottom of the game window. - - - -

Parameters:

-
    -
  • height - float - (default 30) Percentage of the screen to be covered -
  • -
  • speed - float - (default 30) Coverage percent per second -
  • -
- - - - - -
-
- - SetFOV(angle) -
-
- Set field of view. - - - -

Parameters:

-
    -
  • angle - float - in degrees (clamped to [10, 170]) -
  • -
- - - - - -
-
- - GetCameraType() -
-
- Shows the mode of the game camera. - - - - -

Returns:

-
    - - CameraType - value used by the Main Camera. -
- - - -

Usage:

-
    -
    LevelFuncs.OnControlPhase = function()
    -	if (Misc.GetCameraType() == CameraType.Combat) then
    -		--Do your Actions here.
    -	end
    -end
    -
- -
-
- - PlayAudioTrack(name, type) -
-
- Play an audio track - - - -

Parameters:

-
    -
  • name - string - of track (without file extension) to play -
  • -
  • type - SoundTrackType - of the audio track to play -
  • -
- - - - - -
-
- - SetAmbientTrack(name) -
-
- Set and play an ambient track - - - -

Parameters:

-
    -
  • name - string - of track (without file extension) to play -
  • -
- - - - - -
-
- - StopAudioTracks() -
-
- Stop any audio tracks currently playing - - - - - - - - -
-
- - StopAudioTrack(type) -
-
- Stop audio track that is currently playing - - - -

Parameters:

- - - - - - -
-
- - GetAudioTrackLoudness(type) -
-
- Get current loudness level for specified track type - - - -

Parameters:

- - -

Returns:

-
    - - float - current loudness of a specified audio track -
- - - - -
-
- - GetCurrentSubtitle() -
-
- Get current subtitle string for a voice track currently playing. -Subtitle file must be in .srt format, have same filename as voice track, and be placed in same directory as voice track. -Returns nil if no voice track is playing or no subtitle present. - - - - -

Returns:

-
    - - string - current subtitle string -
- - - - -
-
- - PlaySound(sound[, position]) -
-
- Play sound effect - - - -

Parameters:

-
    -
  • sound - int - ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window. -
  • -
  • position - Vec3 - The 3D position of the sound, i.e. where the sound "comes from". If not given, the sound will not be positional. - (optional) -
  • -
- - - - - -
-
- - IsSoundPlaying(Sound) -
-
- Check if the sound effect is playing - - - -

Parameters:

-
    -
  • Sound - int - ID to check. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window. -
  • -
- - - - - -
-
- - IsAudioTrackPlaying(Track) -
-
- Check if the audio track is playing - - - -

Parameters:

-
    -
  • Track - string - filename to check. Should be without extension and without full directory path. -
  • -
- - - - - -
-
- - FlipMap(flipmap) -
-
- Do FlipMap with specific ID - - - -

Parameters:

-
    -
  • flipmap - int - (ID of flipmap) -
  • -
- - - - - -
-
- - PlayFlyBy(flyby) -
-
- Enable FlyBy with specific ID - - - -

Parameters:

-
    -
  • flyby - short - (ID of flyby) -
  • -
- - - - - -
-
- - CalculateDistance(posA, posB) -
-
- Calculate the distance between two positions. - - - -

Parameters:

-
    -
  • posA - Vec3 - first position -
  • -
  • posB - Vec3 - second position -
  • -
- -

Returns:

-
    - - int - the direct distance from one position to the other -
- - - - -
-
- - CalculateHorizontalDistance(posA, posB) -
-
- Calculate the horizontal distance between two positions. - - - -

Parameters:

-
    -
  • posA - Vec3 - first position -
  • -
  • posB - Vec3 - second position -
  • -
- -

Returns:

-
    - - int - the direct distance on the XZ plane from one position to the other -
- - - - -
-
- - PercentToScreen(x, y) -
-
- Translate a pair of percentages to screen-space pixel coordinates. -To be used with Strings.DisplayString:SetPosition and Strings.DisplayString. - - - -

Parameters:

-
    -
  • x - float - percent value to translate to x-coordinate -
  • -
  • y - float - percent value to translate to y-coordinate -
  • -
- -

Returns:

-
    -
  1. - int - x coordinate in pixels
  2. -
  3. - int - y coordinate in pixels
  4. -
- - - -

Usage:

-
    -
    local halfwayX, halfwayY = PercentToScreen(50, 50)
    -local baddy
    -local spawnLocationNullmesh = GetMoveableByName("position_behind_left_pillar")
    -local str1 = DisplayString("You spawned a baddy!", halfwayX, halfwayY, Color(255, 100, 100), false, {DisplayStringOption.SHADOW, DisplayStringOption.CENTER})
    -
    -LevelFuncs.triggerOne = function(obj)
    -	ShowString(str1, 4)
    -end
    -
- -
-
- - ScreenToPercent(x, y) -
-
- Translate a pair of coordinates to percentages of window dimensions. -To be used with Strings.DisplayString:GetPosition. - - - -

Parameters:

-
    -
  • x - int - pixel value to translate to a percentage of the window width -
  • -
  • y - int - pixel value to translate to a percentage of the window height -
  • -
- -

Returns:

-
    -
  1. - float - x coordinate as percentage
  2. -
  3. - float - y coordinate as percentage
  4. -
- - - - -
-
- - ResetObjCamera() -
-
- Reset object camera back to Lara and deactivate object camera. - - - - - - - - -
-
- - PrintLog(message, logLevel[, allowSpam]) -
-
- Write messages within the Log file - -

For native Lua handling of errors, see the official Lua website:

- -

Error management

- -

debug.traceback

- - -

Parameters:

-
    -
  • message - string - to be displayed within the Log -
  • -
  • logLevel - LogLevel - log level to be displayed -
  • -
  • allowSpam - bool - true allows spamming of the message - (optional) -
  • -
- - - - -

Usage:

-
    -
    PrintLog('test info log', LogLevel.INFO)
    -PrintLog('test warning log', LogLevel.WARNING)
    -PrintLog('test error log', LogLevel.ERROR)
    --- spammed message
    -PrintLog('test spam log', LogLevel.INFO, true) 
    -
- -
-
- - Vibrate(strength, time) -
-
- Vibrate gamepad, if possible. - - - -

Parameters:

-
    -
  • strength - float - - - -
  • -
  • time - float - (in seconds, default: 0.3) -
  • -
- - - - - -
-
- - KeyIsHeld(action) -
-
- Check if particular action key is held - - - -

Parameters:

-
    -
  • action - ActionID - action mapping index to check -
  • -
- - - - - -
-
- - KeyIsHit(action) -
-
- Check if particular action key was hit (once) - - - -

Parameters:

-
    -
  • action - ActionID - action mapping index to check -
  • -
- - - - - -
-
- - KeyPush(action) -
-
- Emulate pushing of a certain action key - - - -

Parameters:

-
    -
  • action - ActionID - action mapping index to push -
  • -
- - - - - -
-
- - KeyClear(action) -
-
- Clears particular input from action key - - - -

Parameters:

-
    -
  • action - ActionID - action mapping index to clear -
  • -
- - - - - -
-
- - - - -
-generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2023-09-30 13:48:57 -
- - - diff --git a/Documentation/doc/1 modules/Strings.html b/Documentation/doc/1 modules/Strings.html index 597b921ff..dfb0f1204 100644 --- a/Documentation/doc/1 modules/Strings.html +++ b/Documentation/doc/1 modules/Strings.html @@ -160,7 +160,7 @@ Default: nil (i.e. infinite) should be string automatically deleted after timeout is reached. If not given, the string will remain allocated even after timeout is reached, and can be shown again without re-initialization. -Default: false +Default: true diff --git a/Documentation/doc/2 classes/DisplaySprite.html b/Documentation/doc/2 classes/DisplaySprite.html deleted file mode 100644 index f40c12cab..000000000 --- a/Documentation/doc/2 classes/DisplaySprite.html +++ /dev/null @@ -1,529 +0,0 @@ - - - - - TombEngine 1.1.0 Lua API - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Class DisplaySprite

-

Represents a screen-space display sprite.

-

- -

- - -

Functions

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DisplaySprite(ID, int, pos, rot, scale[, color])Create a DisplaySprite object.
DisplaySprite:GetObjectID()Get the object ID of the sprite sequence object used by the display sprite.
DisplaySprite:GetSpriteID()Get the sprite ID in the sprite sequence object used by the display sprite.
DisplaySprite:GetPosition()Get the display position of the display sprite in percent.
DisplaySprite:GetRotation()Get the rotation of the display sprite in degrees.
DisplaySprite:GetScale()Get the horizontal and vertical scale of the display sprite in percent.
DisplaySprite:GetColor()Get the color of the display sprite.
DisplaySprite:SetObjectID(New)Set the sprite sequence object ID used by the display sprite.
DisplaySprite:SetSpriteID(New)Set the sprite ID in the sprite sequence object used by the display sprite.
DisplaySprite:SetPosition(New)Set the display position of the display sprite in percent.
DisplaySprite:SetRotation(New)Set the rotation of the display sprite in degrees.
DisplaySprite:SetScale(New)Set the horizontal and vertical scale of the display sprite in percent.
DisplaySprite:SetColor(New)Set the color of the display sprite.
DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode])Draw the display sprite in display space for the current frame.
- -
-
- - -

Functions

- -
-
- - DisplaySprite(ID, int, pos, rot, scale[, color]) -
-
- Create a DisplaySprite object. - - - -

Parameters:

-
    -
  • ID - ObjID - of the sprite sequence object. -
  • -
  • int - int - spriteID ID of the sprite in the sequence. -
  • -
  • pos - Vec2 - Display position in percent. -
  • -
  • rot - float - Rotation in degrees. -
  • -
  • scale - Vec2 - Horizontal and vertical scale in percent. Scaling is interpreted by the DisplaySpriteEnum.ScaleMode passed to the Draw() function call. -
  • -
  • color - Color - Color. Default: Color(255, 255, 255, 255) - (optional) -
  • -
- -

Returns:

-
    - - DisplaySprite - A new DisplaySprite object. -
- - - - -
-
- - DisplaySprite:GetObjectID() -
-
- Get the object ID of the sprite sequence object used by the display sprite. () - - - - -

Returns:

-
    - - ObjID - Sprite sequence object ID. -
- - - - -
-
- - DisplaySprite:GetSpriteID() -
-
- Get the sprite ID in the sprite sequence object used by the display sprite. () - - - - -

Returns:

-
    - - int - Sprite ID in the sprite sequence object. -
- - - - -
-
- - DisplaySprite:GetPosition() -
-
- Get the display position of the display sprite in percent. () - - - - -

Returns:

-
    - - Vec2 - Display position in percent. -
- - - - -
-
- - DisplaySprite:GetRotation() -
-
- Get the rotation of the display sprite in degrees. () - - - - -

Returns:

-
    - - float - Rotation in degrees. -
- - - - -
-
- - DisplaySprite:GetScale() -
-
- Get the horizontal and vertical scale of the display sprite in percent. () - - - - -

Returns:

-
    - - Vec2 - Horizontal and vertical scale in percent. -
- - - - -
-
- - DisplaySprite:GetColor() -
-
- Get the color of the display sprite. () - - - - -

Returns:

-
    - - Color - Color. -
- - - - -
-
- - DisplaySprite:SetObjectID(New) -
-
- Set the sprite sequence object ID used by the display sprite. (Objects.ObjID) - - - -

Parameters:

-
    -
  • New - ObjID - sprite sequence object ID. -
  • -
- - - - - -
-
- - DisplaySprite:SetSpriteID(New) -
-
- Set the sprite ID in the sprite sequence object used by the display sprite. (int) - - - -

Parameters:

-
    -
  • New - int - sprite ID in the sprite sequence object. -
  • -
- - - - - -
-
- - DisplaySprite:SetPosition(New) -
-
- Set the display position of the display sprite in percent. (Vec2) - - - -

Parameters:

-
    -
  • New - Vec2 - display position in percent. -
  • -
- - - - - -
-
- - DisplaySprite:SetRotation(New) -
-
- Set the rotation of the display sprite in degrees. (float) - - - -

Parameters:

-
    -
  • New - float - rotation in degrees. -
  • -
- - - - - -
-
- - DisplaySprite:SetScale(New) -
-
- Set the horizontal and vertical scale of the display sprite in percent. (Vec2) - - - -

Parameters:

-
    -
  • New - float - horizontal and vertical scale in percent. -
  • -
- - - - - -
-
- - DisplaySprite:SetColor(New) -
-
- Set the color of the display sprite. (Color) - - - -

Parameters:

-
    -
  • New - float - color. -
  • -
- - - - - -
-
- - DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode]) -
-
- Draw the display sprite in display space for the current frame. - - - -

Parameters:

-
    -
  • priority - ObjID - Draw priority. Can be thought of as a layer, with higher values having precedence. Default: 0 - (optional) -
  • -
  • alignMode - AlignMode - Align mode interpreting an offset from the sprite's position. Default: DisplaySprite.AlignMode.CENTER - (optional) -
  • -
  • scaleMode - ScaleMode - Scale mode interpreting the display sprite's horizontal and vertical scale. Default: DisplaySprite.ScaleMode.FIT - (optional) -
  • -
  • blendMode - BlendID - Blend mode. Default: Effects.BlendID.ALPHABLEND - (optional) -
  • -
- - - - - -
-
- - -
-
-
-generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2023-11-09 18:25:22 -
-
- - diff --git a/Documentation/doc/4 enums/Flow.BreakMode.html b/Documentation/doc/4 enums/Flow.BreakMode.html deleted file mode 100644 index db34cabb1..000000000 --- a/Documentation/doc/4 enums/Flow.BreakMode.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - TombEngine 1.5 Lua API - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Enum Flow.BreakMode

-

Constants for break modes.

-

- -

- - -

Flow.BreakMode constants

- - - - - -
CONSTANT_STRING_HERETable of break modes.
- -
-
- - -

Flow.BreakMode constants

- -
- - - -

The following constants are inside Flow.BreakMode.

- -
NONE
-FULL
-SPECTATOR
-PLAYER
-
- - -
-
-
- - CONSTANT_STRING_HERE -
-
- Table of break modes. - - - - - - - - -
-
- - -
-
-
-generated by TEN-LDoc (a fork of LDoc 1.4.6) -
-
- - diff --git a/Documentation/doc/4 enums/Flow.GameMode.html b/Documentation/doc/4 enums/Flow.GameMode.html deleted file mode 100644 index d6b98b125..000000000 --- a/Documentation/doc/4 enums/Flow.GameMode.html +++ /dev/null @@ -1,165 +0,0 @@ - - - - - TombEngine 1.5 Lua API - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Enum Flow.GameMode

-

Constants for game modes.

-

- -

- - -

Flow.GameMode constants

- - - - - -
CONSTANT_STRING_HERETable of game modes.
- -
-
- - -

Flow.GameMode constants

- -
- - - -

The following constants are inside Flow.GameMode.

- -
NORMAL
-FROZEN
-MENU
-
- - -
-
-
- - CONSTANT_STRING_HERE -
-
- Table of game modes. - - - - - - - - -
-
- - -
-
-
-generated by TEN-LDoc (a fork of LDoc 1.4.6) -
-
- - diff --git a/Documentation/doc/4 enums/Misc.ActionID.html b/Documentation/doc/4 enums/Misc.ActionID.html deleted file mode 100644 index 9aa746fde..000000000 --- a/Documentation/doc/4 enums/Misc.ActionID.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - - TombEngine 1.1.0 Lua API - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Enum Misc.ActionID

-

Constants for action key IDs.

-

- -

- - -

Misc.ActionID constants

- - - - - -
CONSTANT_STRING_HERETable of action ID constants (for use with KeyIsHeld / KeyIsHit / etc commands).
- -
-
- - -

Misc.ActionID constants

- -
- - - -

The following constants are inside ActionID.

- -
FORWARD
-BACK
-LEFT
-RIGHT
-STEP_LEFT
-STEP_RIGHT
-WALK
-SPRINT
-CROUCH
-JUMP
-ROLL
-ACTION
-DRAW
-LOOK
-
-ACCELERATE
-REVERSE
-SPEED
-SLOW
-BRAKE
-FIRE
-
-FLARE
-SMALL_MEDIPACK
-LARGE_MEDIPACK
-PREVIOUS_WEAPON
-NEXT_WEAPON
-WEAPON_1
-WEAPON_2
-WEAPON_3
-WEAPON_4
-WEAPON_5
-WEAPON_6
-WEAPON_7
-WEAPON_8
-WEAPON_9
-WEAPON_10
-
-SELECT
-DESELECT
-PAUSE
-INVENTORY
-SAVE
-LOAD
-
- - -
-
-
- - CONSTANT_STRING_HERE -
-
- Table of action ID constants (for use with KeyIsHeld / KeyIsHit / etc commands). - - - - - - - - -
-
- - -
-
-
-generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2023-09-30 13:48:57 -
-
- - diff --git a/Documentation/doc/4 enums/Misc.CameraType.html b/Documentation/doc/4 enums/Misc.CameraType.html deleted file mode 100644 index 5c57ac2d6..000000000 --- a/Documentation/doc/4 enums/Misc.CameraType.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - TombEngine 1.1.0 Lua API - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Enum Misc.CameraType

-

Constants for the type of the Camera.

-

- -

- - -

Misc.CameraType constants

- - - - - -
CONSTANT_STRING_HERETable of camera type constants (for use with GetCameraType() function).
- -
-
- - -

Misc.CameraType constants

- -
- - - -

The following constants are inside CameraType.

- -
CHASE
-FIXED
-LOOK
-COMBAT
-HEAVY
-OBJECT
-
- - -
-
-
- - CONSTANT_STRING_HERE -
-
- Table of camera type constants (for use with GetCameraType() function). - - - - - - - - -
-
- - -
-
-
-generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2023-09-30 13:48:57 -
-
- - diff --git a/Documentation/doc/4 enums/Misc.LogLevel.html b/Documentation/doc/4 enums/Misc.LogLevel.html deleted file mode 100644 index 97a3f2081..000000000 --- a/Documentation/doc/4 enums/Misc.LogLevel.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - TombEngine 1.1.0 Lua API - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Enum Misc.LogLevel

-

Constants for LogLevel IDs.

-

- -

- - -

Misc.LogLevel constants

- - - - - -
CONSTANT_STRING_HERETable of LogLevel ID constants (for use with PrintLog() command).
- -
-
- - -

Misc.LogLevel constants

- -
- - -

The following constants are inside LogLevel.

- -
INFO
-WARNING
-ERROR
-
- - -
-
-
- - CONSTANT_STRING_HERE -
-
- Table of LogLevel ID constants (for use with PrintLog() command). - - - - - - - - -
-
- - -
-
-
-generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2023-09-30 13:48:57 -
-
- - diff --git a/Documentation/doc/4 enums/Misc.SoundTrackType.html b/Documentation/doc/4 enums/Misc.SoundTrackType.html deleted file mode 100644 index 31e0cd998..000000000 --- a/Documentation/doc/4 enums/Misc.SoundTrackType.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - TombEngine 1.1.0 Lua API - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Enum Misc.SoundTrackType

-

Constants for the type of the audio tracks.

-

- -

- - -

Misc.SoundTrackType constants

- - - - - -
CONSTANT_STRING_HERETable of sound track type constants (for use with sound track functions).
- -
-
- - -

Misc.SoundTrackType constants

- -
- - - -

The following constants are inside SoundTrackType.

- -
ONESHOT
-LOOPED
-VOICE
-
- - -
-
-
- - CONSTANT_STRING_HERE -
-
- Table of sound track type constants (for use with sound track functions). - - - - - - - - -
-
- - -
-
-
-generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2023-09-30 13:48:57 -
-
- - From 8679e63b89e00c90f31f841fe5f89df8e36bf077 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Tue, 3 Dec 2024 00:12:59 +0100 Subject: [PATCH 13/26] Rollback room point light formula --- TombEngine/Shaders/Rooms.fx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/TombEngine/Shaders/Rooms.fx b/TombEngine/Shaders/Rooms.fx index 4e8d379f7..62189a526 100644 --- a/TombEngine/Shaders/Rooms.fx +++ b/TombEngine/Shaders/Rooms.fx @@ -163,7 +163,23 @@ PixelShaderOutput PS(PixelShaderInput input) if (lightType == LT_POINT) { - lighting += DoPointLight(input.WorldPosition, normal, RoomLights[i]); + // Use simplified unrolled light calculation for point lights, because + // using DoPointLight can cause extreme slowdowns on slower systems. + + float radius = RoomLights[i].Out; + + float3 lightVec = (RoomLights[i].Position.xyz - input.WorldPosition); + float distance = length(lightVec); + if (distance > radius) + continue; + + lightVec = normalize(lightVec); + float d = saturate(dot(normal, lightVec)); + if (d < 0) + continue; + + float attenuation = pow(((radius - distance) / radius), 2); + lighting += RoomLights[i].Color.xyz * attenuation * d; } else if (lightType == LT_SPOT) { From a2681cb0d30786cf4fd97843545287f25327b4e4 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 4 Dec 2024 01:21:17 +0100 Subject: [PATCH 14/26] Fix error for infinite strings showing without extra args --- TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp index 22ecca1d7..e2db6fd0a 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp @@ -110,7 +110,7 @@ void StringsHandler::ProcessDisplayStrings(float deltaTime) { auto& str = it->second; bool endOfLife = 0.0f >= str._timeRemaining; - if (str._deleteWhenZero && endOfLife) + if (!str._isInfinite && str._deleteWhenZero && endOfLife) { ScriptAssertF(!str._isInfinite, "The infinite string {} (key \"{}\") went out of scope without being hidden.", it->first, str._key); it = m_userDisplayStrings.erase(it); From ecddc1a330ba50f94847aa4ec708273d6868bc1e Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 4 Dec 2024 02:59:50 +0100 Subject: [PATCH 15/26] Fixed #1491 --- TombEngine/Game/collision/floordata.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TombEngine/Game/collision/floordata.cpp b/TombEngine/Game/collision/floordata.cpp index 30af7c2c8..29da6a4e2 100644 --- a/TombEngine/Game/collision/floordata.cpp +++ b/TombEngine/Game/collision/floordata.cpp @@ -381,8 +381,8 @@ namespace TEN::Collision::Floordata // Calculate and return tilt. auto sign = isFloor ? 1 : -1; return Vector2i( - round(scaledNormal.x * 4), - round(scaledNormal.z * 4)) * sign; + (round(scaledNormal.x) * 4), + (round(scaledNormal.z) * 4)) * sign; } Vector2i GetSectorPoint(int x, int z) From e17080c95ceb70c1213cc36876b3d15bdaa48c7f Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:58:05 +0100 Subject: [PATCH 16/26] Draw HUD and display sprites only when game status is normal --- TombEngine/Renderer/RendererDraw.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index f298ce6c0..3cc21fec1 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -1901,7 +1901,7 @@ namespace TEN::Renderer _context->ClearDepthStencilView(_renderTarget.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); // HUD must be drawn before post-processing to be antialiased. - if (renderMode == SceneRenderMode::Full) + if (renderMode == SceneRenderMode::Full && g_GameFlow->LastGameStatus == GameStatus::Normal) g_Hud.Draw(*LaraItem); if (renderMode != SceneRenderMode::NoPostprocess) @@ -1936,7 +1936,7 @@ namespace TEN::Renderer DrawLines2D(); } - if (renderMode == SceneRenderMode::Full) + if (renderMode == SceneRenderMode::Full && g_GameFlow->LastGameStatus == GameStatus::Normal) { // Draw display sprites sorted by priority. CollectDisplaySprites(view); From 4f2a0b05546848e0610f8e32638407130f239f90 Mon Sep 17 00:00:00 2001 From: "l.m. (Leif Melles)" <75839472+goldlife2904@users.noreply.github.com> Date: Thu, 5 Dec 2024 02:27:26 +0100 Subject: [PATCH 17/26] Fixed SetIntroImagePath() (#1498) * Fixed SetIntroImagePath() Fixed SetIntroImagePath() not using the correct path * Update RendererDrawMenu.cpp --------- Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com> --- CHANGELOG.md | 1 + TombEngine/Renderer/RendererDrawMenu.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4876cd53f..4f69f3a95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. * Fixed Util.HasLineOfSight() not taking static meshes into consideration. * Fixed collision callbacks not properly clearing after leveljump. +* Fixed SetIntroImagePath() not using the correct path ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index 6e8685634..10d407b43 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -885,7 +885,7 @@ namespace TEN::Renderer void Renderer::RenderTitleImage() { Texture2D texture; - SetTextureOrDefault(texture, TEN::Utils::ToWString(g_GameFlow->IntroImagePath.c_str())); + SetTextureOrDefault(texture, TEN::Utils::ToWString(g_GameFlow->GetGameDir() + g_GameFlow->IntroImagePath.c_str())); if (!texture.Texture) return; From d6cf25f16e068d67f0c32ab1b9b48e53b71e2ed5 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:49:00 +0100 Subject: [PATCH 18/26] Remove light calculation branching which caused extreme slowdowns --- TombEngine/Shaders/Rooms.fx | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/TombEngine/Shaders/Rooms.fx b/TombEngine/Shaders/Rooms.fx index 62189a526..8afc88069 100644 --- a/TombEngine/Shaders/Rooms.fx +++ b/TombEngine/Shaders/Rooms.fx @@ -159,32 +159,11 @@ PixelShaderOutput PS(PixelShaderInput input) { for (int i = 0; i < NumRoomLights; i++) { - int lightType = RoomLights[i].Type; + float isPointLightRoom = step(0.5f, RoomLights[i].Type == LT_POINT); + float isSpotLightRoom = step(0.5f, RoomLights[i].Type == LT_SPOT); - if (lightType == LT_POINT) - { - // Use simplified unrolled light calculation for point lights, because - // using DoPointLight can cause extreme slowdowns on slower systems. - - float radius = RoomLights[i].Out; - - float3 lightVec = (RoomLights[i].Position.xyz - input.WorldPosition); - float distance = length(lightVec); - if (distance > radius) - continue; - - lightVec = normalize(lightVec); - float d = saturate(dot(normal, lightVec)); - if (d < 0) - continue; - - float attenuation = pow(((radius - distance) / radius), 2); - lighting += RoomLights[i].Color.xyz * attenuation * d; - } - else if (lightType == LT_SPOT) - { - lighting += DoSpotLight(input.WorldPosition, normal, RoomLights[i]); - } + lighting += DoPointLight(input.WorldPosition, normal, RoomLights[i]) * isPointLightRoom; + lighting += DoSpotLight(input.WorldPosition, normal, RoomLights[i]) * isSpotLightRoom; } } From 2ee37ea7de2da126288130b7fb1abdcccdb44f0a Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:59:57 +0100 Subject: [PATCH 19/26] Remove branching from ShaderLight as well --- TombEngine/Shaders/ShaderLight.hlsli | 36 ++++++++++++---------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/TombEngine/Shaders/ShaderLight.hlsli b/TombEngine/Shaders/ShaderLight.hlsli index d082f8ca5..cb9578ab5 100644 --- a/TombEngine/Shaders/ShaderLight.hlsli +++ b/TombEngine/Shaders/ShaderLight.hlsli @@ -428,31 +428,25 @@ float3 CombineLights(float3 ambient, float3 vertex, float3 tex, float3 pos, floa for (int i = 0; i < numLights; i++) { - int lightType = lights[i].Type; + float isPoint = step(0.5f, float(lights[i].Type == LT_POINT)); + float isShadow = step(0.5f, float(lights[i].Type == LT_SHADOW)); + float isSun = step(0.5f, float(lights[i].Type == LT_SUN)); + float isSpot = step(0.5f, float(lights[i].Type == LT_SPOT)); - if (lightType == LT_POINT) - { - diffuse += DoPointLight(pos, normal, lights[i]); - spec += DoSpecularPoint(pos, normal, lights[i], sheen); - } - else if (lightType == LT_SHADOW) - { - shadow += DoShadowLight(pos, normal, lights[i]); - } - else if (lightType == LT_SUN) - { - diffuse += DoDirectionalLight(pos, normal, lights[i]); - spec += DoSpecularSun(normal, lights[i], sheen); - } - else if (lightType == LT_SPOT) - { - diffuse += DoSpotLight(pos, normal, lights[i]); - spec += DoSpecularSpot(pos, normal, lights[i], sheen); - } + diffuse += isPoint * DoPointLight(pos, normal, lights[i]); + spec += isPoint * DoSpecularPoint(pos, normal, lights[i], sheen); + + shadow += isShadow * DoShadowLight(pos, normal, lights[i]); + + diffuse += isSun * DoDirectionalLight(pos, normal, lights[i]); + spec += isSun * DoSpecularSun(normal, lights[i], sheen); + + diffuse += isSpot * DoSpotLight(pos, normal, lights[i]); + spec += isSpot * DoSpecularSpot(pos, normal, lights[i], sheen); } shadow = saturate(shadow); - diffuse.xyz *= tex.xyz; + diffuse *= tex; float3 ambTex = saturate(ambient - shadow) * tex; float3 combined = ambTex + diffuse + spec; From 936d2ff98e1a8563db5dff0ac92d7e1be7cc6d53 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:07:16 +0100 Subject: [PATCH 20/26] Additional shader optimizations --- TombEngine/Shaders/Rooms.fx | 31 ++++++++++++++----------------- TombEngine/Shaders/Shadows.hlsli | 28 ++++++++++++++++++---------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/TombEngine/Shaders/Rooms.fx b/TombEngine/Shaders/Rooms.fx index 8afc88069..a6af630e6 100644 --- a/TombEngine/Shaders/Rooms.fx +++ b/TombEngine/Shaders/Rooms.fx @@ -135,7 +135,7 @@ PixelShaderOutput PS(PixelShaderInput input) if (AmbientOcclusion == 1) { float2 samplePosition; - samplePosition = input.PositionCopy.xy / input.PositionCopy.w; // perspective divide + samplePosition = input.PositionCopy.xy / input.PositionCopy.w; // Perspective divide samplePosition = samplePosition * 0.5f + 0.5f; // transform to range 0.0 - 1.0 samplePosition.y = 1.0f - samplePosition.y; occlusion = pow(SSAOTexture.Sample(SSAOSampler, samplePosition).x, AmbientOcclusionExponent); @@ -143,28 +143,25 @@ PixelShaderOutput PS(PixelShaderInput input) if (CastShadows) { - if (Light.Type == LT_POINT) - { - DoPointLightShadow(input.WorldPosition, lighting); - } - else if (Light.Type == LT_SPOT) - { - DoSpotLightShadow(input.WorldPosition, normal, lighting); - } + float isPointLight = step(0.5f, Light.Type == LT_POINT); // 1.0 if LT_POINT, 0.0 otherwise + float isSpotLight = step(0.5f, Light.Type == LT_SPOT); // 1.0 if LT_SPOT, 0.0 otherwise + + float3 pointLightShadow = DoPointLightShadow(input.WorldPosition, lighting); + float3 spotLightShadow = DoSpotLightShadow(input.WorldPosition, normal, lighting); + + // Blend the shadows based on the light type + lighting = pointLightShadow * isPointLight + spotLightShadow * isSpotLight; } DoBlobShadows(input.WorldPosition, lighting); - if (doLights) + for (int i = 0; i < NumRoomLights; i++) { - for (int i = 0; i < NumRoomLights; i++) - { - float isPointLightRoom = step(0.5f, RoomLights[i].Type == LT_POINT); - float isSpotLightRoom = step(0.5f, RoomLights[i].Type == LT_SPOT); + float isPointLightRoom = step(0.5f, RoomLights[i].Type == LT_POINT); + float isSpotLightRoom = step(0.5f, RoomLights[i].Type == LT_SPOT); - lighting += DoPointLight(input.WorldPosition, normal, RoomLights[i]) * isPointLightRoom; - lighting += DoSpotLight(input.WorldPosition, normal, RoomLights[i]) * isSpotLightRoom; - } + lighting += DoPointLight(input.WorldPosition, normal, RoomLights[i]) * isPointLightRoom; + lighting += DoSpotLight(input.WorldPosition, normal, RoomLights[i]) * isSpotLightRoom; } if (Caustics) diff --git a/TombEngine/Shaders/Shadows.hlsli b/TombEngine/Shaders/Shadows.hlsli index 373181408..dfdcbab6a 100644 --- a/TombEngine/Shaders/Shadows.hlsli +++ b/TombEngine/Shaders/Shadows.hlsli @@ -68,27 +68,29 @@ float2 GetCubeUVFromDir(int faceIndex, float3 dir) return uv * .5 + .5; } -void DoBlobShadows(float3 worldPos, inout float3 lighting) +float3 DoBlobShadows(float3 worldPos, float3 lighting) { float shadowFactor = 1.0f; + for (int i = 0; i < NumSpheres; i++) { Sphere s = Spheres[i]; float dist = distance(worldPos, s.position); - if (dist > s.radius) - continue; + float insideSphere = saturate(1.0f - step(s.radius, dist)); // Eliminates branching float radiusFactor = dist / s.radius; - float factor = 1 - (saturate(radiusFactor)); + float factor = (1.0f - saturate(radiusFactor)) * insideSphere; shadowFactor -= factor * shadowFactor; - } + shadowFactor = saturate(shadowFactor); - lighting *= saturate((shadowFactor + SHADOW_INTENSITY)); + return lighting * saturate(shadowFactor + SHADOW_INTENSITY); } -void DoPointLightShadow(float3 worldPos, inout float3 lighting) +float3 DoPointLightShadow(float3 worldPos, float3 lighting) { float shadowFactor = 1.0f; + + [unroll] for (int i = 0; i < 6; i++) { float3 dir = normalize(worldPos - Light.Position); @@ -106,8 +108,10 @@ void DoPointLightShadow(float3 worldPos, inout float3 lighting) float x, y; // Perform PCF filtering on a 4 x 4 texel neighborhood. + [unroll] for (y = -1.5; y <= 1.5; y += 1.0) { + [unroll] for (x = -1.5; x <= 1.5; x += 1.0) { sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z); @@ -120,14 +124,16 @@ void DoPointLightShadow(float3 worldPos, inout float3 lighting) // Compute attenuation and combine lighting contribution with shadow factor float distanceFactor = saturate(((distance(worldPos, Light.Position)) / (Light.Out))); - lighting *= saturate((shadowFactor + SHADOW_INTENSITY) + (pow(distanceFactor, 4) * INV_SHADOW_INTENSITY)); + return lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(distanceFactor, 4) * INV_SHADOW_INTENSITY)); } -void DoSpotLightShadow(float3 worldPos, float3 normal, inout float3 lighting) +float3 DoSpotLightShadow(float3 worldPos, float3 normal, float3 lighting) { float influence = 1.0f - Luma(DoSpotLight(worldPos, normal, Light)); float shadowFactor = 1.0f; + + [unroll] for (int i = 0; i < 6; i++) { float3 dir = normalize(worldPos - Light.Position); @@ -145,8 +151,10 @@ void DoSpotLightShadow(float3 worldPos, float3 normal, inout float3 lighting) float x, y; // Perform PCF filtering on a 4 x 4 texel neighborhood. + [unroll] for (y = -1.5; y <= 1.5; y += 1.0) { + [unroll] for (x = -1.5; x <= 1.5; x += 1.0) { sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z); @@ -158,5 +166,5 @@ void DoSpotLightShadow(float3 worldPos, float3 normal, inout float3 lighting) } // Compute attenuation and combine lighting contribution with shadow factor - lighting *= saturate((shadowFactor + SHADOW_INTENSITY) + (pow(influence, 4) * INV_SHADOW_INTENSITY)); + return lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(influence, 4) * INV_SHADOW_INTENSITY)); } From e6dbd363b61b4552a0656531a32b02476c1997b9 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 5 Dec 2024 23:46:23 +0100 Subject: [PATCH 21/26] Remove branching in some light functions --- TombEngine/Shaders/ShaderLight.hlsli | 132 +++++++-------------------- 1 file changed, 35 insertions(+), 97 deletions(-) diff --git a/TombEngine/Shaders/ShaderLight.hlsli b/TombEngine/Shaders/ShaderLight.hlsli index cb9578ab5..2bcbe328e 100644 --- a/TombEngine/Shaders/ShaderLight.hlsli +++ b/TombEngine/Shaders/ShaderLight.hlsli @@ -97,119 +97,57 @@ float3 DoSpecularSpot(float3 pos, float3 n, ShaderLight light, float strength) } } -float3 DoPointLight(float3 pos, float3 n, ShaderLight light) +float3 DoPointLight(float3 pos, float3 normal, ShaderLight light) { - float3 lightPos = light.Position.xyz; - float3 color = light.Color.xyz; - float intensity = saturate(light.Intensity); - - float3 lightVec = (lightPos - pos); - float distance = length(lightVec); - - if (distance > light.Out) - return float3(0, 0, 0); - else - { - lightVec = normalize(lightVec); - float d = saturate(dot(n, lightVec)); - - if (d < 0) - return float3(0, 0, 0); - - float attenuation = 1.0f; - if (distance > light.In) - attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); - - return saturate(color * intensity * attenuation * d); - } + float3 lightVec = light.Position.xyz - pos; + float distance = length(lightVec); + float3 lightDir = normalize(lightVec); + + float attenuation = saturate((light.Out - distance) / (light.Out - light.In)); + float d = saturate(dot(normal, lightDir)); + + return saturate(light.Color.xyz * light.Intensity * attenuation * d); } -float3 DoShadowLight(float3 pos, float3 n, ShaderLight light) +float3 DoShadowLight(float3 pos, float3 normal, ShaderLight light) { - float3 lightPos = light.Position.xyz; - float3 color = light.Color.xyz; - float intensity = light.Intensity; + float3 lightVec = light.Position.xyz - pos; + float distance = length(lightVec); + float3 lightDir = normalize(lightVec); + + float attenuation = saturate((light.Out - distance) / (light.Out - light.In)); + float d = saturate(dot(normal, lightDir)); - float3 lightVec = (lightPos - pos); - float distance = length(lightVec); + float absolute = light.Color.xyz * light.Intensity * attenuation; + float directional = absolute * d; - if (distance > light.Out) - return float3(0, 0, 0); - else - { - lightVec = normalize(lightVec); - float d = saturate(dot(n, lightVec)); - - float attenuation = 1.0f; - if (distance > light.In) - attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In)); - - float absolute = float3(color * intensity * attenuation); - float directional = absolute * d; - - return ((absolute * 0.33f) + (directional * 0.66f)) * 2.0f; - } + return saturate((absolute * 0.33f) + (directional * 0.66f)) * 2.0f; } -float3 DoSpotLight(float3 pos, float3 n, ShaderLight light) +float3 DoSpotLight(float3 pos, float3 normal, ShaderLight light) { - float3 lightPos = light.Position.xyz; - float3 color = light.Color.xyz; - float intensity = saturate(light.Intensity); - float3 direction = light.Direction.xyz; - float innerRange = light.In; - float outerRange = light.Out; - float coneIn = light.InRange; - float coneOut = light.OutRange; + float3 lightVec = pos - light.Position.xyz; + float distance = length(lightVec); + float3 lightDir = normalize(lightVec); + float cosine = dot(lightDir, light.Direction.xyz); - float3 lightVec = pos - lightPos; - float distance = length(lightVec); - lightVec = normalize(lightVec); + // Angle attenuation + float coneInCos = cos(light.InRange * (PI / 180.0f)); + float coneOutCos = cos(light.OutRange * (PI / 180.0f)); + float angleAttenuation = saturate((cosine - coneOutCos) / (coneInCos - coneOutCos)); - if (distance > outerRange) - return float3(0, 0, 0); - else - { - float d = saturate(dot(n, -lightVec)); - if (d < 0) - return float3(0, 0, 0); - else - { - float cosine = dot(lightVec, direction); + // Distance attenuation + float distanceAttenuation = saturate((light.Out - distance) / (light.Out - light.In)); - float minCosineIn = cos(coneIn * (PI / 180.0f)); - float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn); - - float minCosineOut = cos(coneOut * (PI / 180.0f)); - float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut); - - float attenuation = saturate(attenuationIn * 2.0f + attenuationOut); - - if (attenuation > 0.0f) - { - float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f)); - return saturate(color * intensity * attenuation * falloff * d); - } - else - return float3(0, 0, 0); - } - } + // Surface lighting + float d = saturate(dot(normal, -lightDir)); + return saturate(light.Color.xyz * light.Intensity * angleAttenuation * distanceAttenuation * d); } float3 DoDirectionalLight(float3 pos, float3 normal, ShaderLight light) { - float3 color = light.Color.xyz; - float3 intensity = light.Intensity; - float3 direction = -light.Direction.xyz; - - float d = max(dot(direction, normal), .0f); - - if (d > 0.f) - { - return (color * intensity * d); - } - - return float3(0, 0, 0); + float d = saturate(dot(-light.Direction.xyz, normal)); + return light.Color.xyz * light.Intensity * d; } float DoFogBulb(float3 pos, ShaderFogBulb bulb) From 4a76487620798e7ca4ddc43e1dae919b94378878 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 6 Dec 2024 00:18:29 +0100 Subject: [PATCH 22/26] Update ShaderLight.hlsli --- CHANGELOG.md | 6 ++++-- TombEngine/Shaders/ShaderLight.hlsli | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f69f3a95..2cea2b24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,14 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ## Version 1.6 - xxxx-xx-xx ### Bug fixes -* Fixed engine performance around bridges. -* Fixed engine performance if weather or bubble effects are active. +* Significantly improved renderer performance. +* Improved engine performance around bridges. +* Improved engine performance if weather or bubble effects are active. * Fixed silent crashes if loaded level is corrupted or in incorrect format. * Fixed occasional crashes if there are static meshes placed within room border walls. * Fixed incorrect clipping of scaled off-centered static meshes. * Fixed incorrect collision detection for off-centered moveables. +* Fixed incorrect slide directions for sub-click geometry. * Fixed stutter during jumps between cameras in a flyby sequence. * Fixed uzi targeting issues after using flycheat. * Fixed snow particles not always melting on the ground. diff --git a/TombEngine/Shaders/ShaderLight.hlsli b/TombEngine/Shaders/ShaderLight.hlsli index 2bcbe328e..77938f0ba 100644 --- a/TombEngine/Shaders/ShaderLight.hlsli +++ b/TombEngine/Shaders/ShaderLight.hlsli @@ -367,20 +367,20 @@ float3 CombineLights(float3 ambient, float3 vertex, float3 tex, float3 pos, floa for (int i = 0; i < numLights; i++) { float isPoint = step(0.5f, float(lights[i].Type == LT_POINT)); - float isShadow = step(0.5f, float(lights[i].Type == LT_SHADOW)); - float isSun = step(0.5f, float(lights[i].Type == LT_SUN)); float isSpot = step(0.5f, float(lights[i].Type == LT_SPOT)); + float isSun = step(0.5f, float(lights[i].Type == LT_SUN)); + float isShadow = step(0.5f, float(lights[i].Type == LT_SHADOW)); diffuse += isPoint * DoPointLight(pos, normal, lights[i]); spec += isPoint * DoSpecularPoint(pos, normal, lights[i], sheen); - shadow += isShadow * DoShadowLight(pos, normal, lights[i]); + diffuse += isSpot * DoSpotLight(pos, normal, lights[i]); + spec += isSpot * DoSpecularSpot(pos, normal, lights[i], sheen); diffuse += isSun * DoDirectionalLight(pos, normal, lights[i]); spec += isSun * DoSpecularSun(normal, lights[i], sheen); - diffuse += isSpot * DoSpotLight(pos, normal, lights[i]); - spec += isSpot * DoSpecularSpot(pos, normal, lights[i], sheen); + shadow += isShadow * DoShadowLight(pos, normal, lights[i]); } shadow = saturate(shadow); From f9f1f59eba9f3d2bc167a7a90c8c0f12bb36e73e Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 6 Dec 2024 00:29:37 +0100 Subject: [PATCH 23/26] Fix blob shadows not showing --- TombEngine/Shaders/Rooms.fx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TombEngine/Shaders/Rooms.fx b/TombEngine/Shaders/Rooms.fx index a6af630e6..abdfdee3c 100644 --- a/TombEngine/Shaders/Rooms.fx +++ b/TombEngine/Shaders/Rooms.fx @@ -153,7 +153,7 @@ PixelShaderOutput PS(PixelShaderInput input) lighting = pointLightShadow * isPointLight + spotLightShadow * isSpotLight; } - DoBlobShadows(input.WorldPosition, lighting); + lighting = DoBlobShadows(input.WorldPosition, lighting); for (int i = 0; i < NumRoomLights; i++) { From 87373b4c78f31f3f925ae8961803182aa7aff392 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 6 Dec 2024 20:36:46 +0100 Subject: [PATCH 24/26] Fixed black rooms in case brightest light is not point or spot --- TombEngine/Shaders/Rooms.fx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TombEngine/Shaders/Rooms.fx b/TombEngine/Shaders/Rooms.fx index abdfdee3c..afb02de4c 100644 --- a/TombEngine/Shaders/Rooms.fx +++ b/TombEngine/Shaders/Rooms.fx @@ -145,12 +145,13 @@ PixelShaderOutput PS(PixelShaderInput input) { float isPointLight = step(0.5f, Light.Type == LT_POINT); // 1.0 if LT_POINT, 0.0 otherwise float isSpotLight = step(0.5f, Light.Type == LT_SPOT); // 1.0 if LT_SPOT, 0.0 otherwise + float isOtherLight = 1.0 - (isPointLight + isSpotLight); // 1.0 if neither LT_POINT nor LT_SPOT float3 pointLightShadow = DoPointLightShadow(input.WorldPosition, lighting); float3 spotLightShadow = DoSpotLightShadow(input.WorldPosition, normal, lighting); // Blend the shadows based on the light type - lighting = pointLightShadow * isPointLight + spotLightShadow * isSpotLight; + lighting = pointLightShadow * isPointLight + spotLightShadow * isSpotLight + lighting * isOtherLight; } lighting = DoBlobShadows(input.WorldPosition, lighting); From 15aee1c5b0921678758abe406b28af36ad42ede5 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 6 Dec 2024 21:04:53 +0100 Subject: [PATCH 25/26] Don't collect shadow casting lights of unsupported types --- TombEngine/Renderer/RendererFrame.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index c0293390c..696c8ed81 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -560,7 +560,8 @@ namespace TEN::Renderer if (intensity <= EPSILON) return; - if (light.CastShadows && prioritizeShadowLight && intensity >= highestIntensity) + if ((light.Type == LightType::Point || light.Type == LightType::Spot) && + light.CastShadows && prioritizeShadowLight && intensity >= highestIntensity) { highestIntensity = intensity; brightestLight = &light; @@ -632,7 +633,7 @@ namespace TEN::Renderer // Put actual lights in provided vector. outputLights->clear(); - // Add brightest ligh, if collecting shadow light is specified, even if it's far in range. + // Add brightest light, if collecting shadow light is specified, even if it's far in range. if (prioritizeShadowLight && brightestLight) outputLights->push_back(brightestLight); @@ -654,7 +655,7 @@ namespace TEN::Renderer std::vector lightsToDraw; CollectLights(Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z), CAMERA_LIGHT_COLLECTION_RADIUS, Camera.pos.RoomNumber, NO_VALUE, true, false, nullptr, &lightsToDraw); - if (lightsToDraw.size() > 0 && lightsToDraw.front()->CastShadows) + if (!lightsToDraw.empty() && lightsToDraw.front()->CastShadows) { _shadowLight = lightsToDraw.front(); } From d96bf4ba8cee214c6f094c81e9ef713a9d2b92d3 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 6 Dec 2024 21:53:45 +0100 Subject: [PATCH 26/26] Debranch fog calculations --- TombEngine/Shaders/ShaderLight.hlsli | 33 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/TombEngine/Shaders/ShaderLight.hlsli b/TombEngine/Shaders/ShaderLight.hlsli index 77938f0ba..4a837cad9 100644 --- a/TombEngine/Shaders/ShaderLight.hlsli +++ b/TombEngine/Shaders/ShaderLight.hlsli @@ -310,13 +310,12 @@ float DoFogBulbForSky(float3 pos, ShaderFogBulb bulb) float DoDistanceFogForVertex(float3 pos) { - float fog = 0.0f; + float d = length(CamPositionWS.xyz - pos); + float fogRange = FogMaxDistance * 1024 - FogMinDistance * 1024; - if (FogMaxDistance > 0.0f) - { - float d = length(CamPositionWS.xyz - pos); - fog = clamp((d - FogMinDistance * 1024) / (FogMaxDistance * 1024 - FogMinDistance * 1024), 0, 1); - } + float fogEnabled = step(0.0f, FogMaxDistance); + float fogFactor = (d - FogMinDistance * 1024) / fogRange; + float fog = saturate(fogFactor) * fogEnabled; return fog; } @@ -324,16 +323,16 @@ float DoDistanceFogForVertex(float3 pos) float4 DoFogBulbsForVertex(float3 pos) { float4 fog = float4(0.0f, 0.0f, 0.0f, 0.0f); - + for (int i = 0; i < NumFogBulbs; i++) { float fogFactor = DoFogBulb(pos, FogBulbs[i]); - fog.xyz += FogBulbs[i].Color.xyz * fogFactor; + float3 fogColor = FogBulbs[i].Color.xyz * fogFactor; + + fog.xyz += fogColor; fog.w += fogFactor; - if (fog.w >= 1.0f) - { - break; - } + + fog.w = saturate(fog.w); } return fog; @@ -346,12 +345,12 @@ float4 DoFogBulbsForSky(float3 pos) for (int i = 0; i < NumFogBulbs; i++) { float fogFactor = DoFogBulbForSky(pos, FogBulbs[i]); - fog.xyz += FogBulbs[i].Color.xyz * fogFactor; + float3 fogColor = FogBulbs[i].Color.xyz * fogFactor; + + fog.xyz += fogColor; fog.w += fogFactor; - if (fog.w >= 1.0f) - { - break; - } + + fog.w = saturate(fog.w); } return fog;