diff --git a/CHANGELOG.md b/CHANGELOG.md index b9ff56c8f..551a93d26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Significantly improved renderer performance. * Improved engine performance around bridges. * Improved engine performance if weather or bubble effects are active. +* Improved engine start-up time. * 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 climbable pushables clipping Lara under the bridges when pulled. @@ -30,6 +31,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed incorrect object camera position. * Fixed incorrect camera movement near walls after leaving look mode. * Fixed binocular or lasersight camera not switching off correctly after flyby. +* Fixed binocular or lasersight camera transitions. +* Fixed target highlighter still being active in binocular or lasersight mode. * Fixed Lara's Home entry not working. * Fixed exploding TR3 bosses. * Fixed original issue with deactivation of Dart Emitter. @@ -41,9 +44,10 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed Skeleton and Mummy not reacting to shotgun hits. ### New Features -* Added fast savegame reloading. +* Added classic mirror effect with ability to reflect moveables and static meshes. * Added ability to customize many hardcoded parameters, such as flare, weapon, and hair settings. * Added dynamic shadow casting on objects and static meshes. +* Added fast savegame reloading. * Added ricochet sounds and make the effect more prominent. * Allow camera shake during flybys. * Allow to run the engine without title level. diff --git a/Documentation/doc/4 enums/Objects.ObjID.html b/Documentation/doc/4 enums/Objects.ObjID.html index 91be24388..31d15d280 100644 --- a/Documentation/doc/4 enums/Objects.ObjID.html +++ b/Documentation/doc/4 enums/Objects.ObjID.html @@ -170,9 +170,9 @@ ACTOR1_SPEECH_HEAD1 ACTOR1_SPEECH_HEAD2 ACTOR2_SPEECH_HEAD1 ACTOR2_SPEECH_HEAD2 +LARA_BINOCULARS_MESH LARA_EXTRA_MESH1 LARA_EXTRA_MESH2 -LARA_EXTRA_MESH3 LARA_WATER_MESH LARA_PETROL_MESH LARA_DIRT_MESH diff --git a/Documentation/doc/ldoc.css b/Documentation/doc/ldoc.css index 306dcd9f5..b00cfcf57 100644 --- a/Documentation/doc/ldoc.css +++ b/Documentation/doc/ldoc.css @@ -1,240 +1,240 @@ - -/* styles for prettification of source */ -pre .comment { color: #558817; } -pre .constant { color: #a8660d; } -pre .escape { color: #844631; } -pre .keyword { color: #aa5050; font-weight: bold; } -pre .library { color: #0e7c6b; } -pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } -pre .string { color: #8080ff; } -pre .number { color: #f8660d; } -pre .operator { color: #2239a8; font-weight: bold; } -pre .preprocessor, pre .prepro { color: #a33243; } -pre .global { color: #800080; } -pre .user-keyword { color: #800080; } -pre .prompt { color: #558817; } -pre .url { color: #272fc2; text-decoration: underline; } - -body { - margin-left: 1em; - margin-right: 1em; - font-family: arial, helvetica, geneva, sans-serif; - background-color: #ffffff; margin: 0px; -} - -code, tt { font-family: monospace; font-size: 1.1em; } -span.parameter { font-family:monospace; } -span.parameter:after { content:":"; } -span.types:before { content:"("; } -span.types:after { content:")"; } -.type { font-weight: bold; font-style:italic } - -body, td, th { font-size: .95em; line-height: 1.2em;} -p { line-height: 1.2em;} - -p, ul { margin: 10px 0 0 0px;} - -strong { font-weight: bold;} - -here { font-weight: bold; color: #c8751d;} - -em { font-style: italic;} - -h1 { - font-size: 1.5em; - margin: 20px 0 20px 0; -} -h2, h3, h4 { margin: 15px 0 10px 0; } -h2 { font-size: 1.25em; } -h3 { font-size: 1.15em; } -h4 { font-size: 1.06em; } - -a:link { font-weight: bold; color: #004080; text-decoration: none; } -a:visited { font-weight: bold; color: #006699; text-decoration: none; } -a:link:hover { text-decoration: underline; } - -hr { - color:#cccccc; - background: #00007f; - height: 1px; -} - -blockquote { margin-left: 3em; } - -ul { list-style-type: disc; } - -p.name { - font-family: "Andale Mono", monospace; - padding-top: 1em; -} - -pre { - background-color: rgb(245, 245, 245); - border: 1px solid #C0C0C0; /* silver */ - padding: 10px; - margin: 10px 0 10px 0; - overflow: auto; - font-family: "Andale Mono", monospace; -} - -pre.example { - font-size: .85em; -} - -table.index { border: 1px #00007f; } -table.index td { text-align: left; vertical-align: top; } - -#container { - margin-left: 1em; - margin-right: 1em; - background-color: #f0f0f0; -} - -#product { - text-align: center; - border-bottom: 1px solid #cccccc; - background-color: #ffffff; -} - -#product big { - font-size: 2em; -} - -#main { - background-color: #f0f0f0; - border-left: 2px solid #cccccc; -} - -#navigation { - float: left; - width: 16em; - vertical-align: top; - background-color: #f0f0f0; - overflow: visible; -} - -#navigation h2 { - background-color:#e7e7e7; - font-size:1.1em; - color:#000000; - text-align: left; - padding:0.2em; - border-top:1px solid #dddddd; - border-bottom:1px solid #dddddd; -} - -#navigation ul -{ - font-size:1em; - list-style-type: none; - margin: 1px 1px 10px 1px; -} - -#navigation li { - text-indent: -1em; - display: block; - margin: 3px 0px 0px 22px; -} - -#navigation li li a { - margin: 0px 3px 0px -1em; -} - -#content { - margin-left: 18em; - padding: 2em; - width: 900px; - border-left: 2px solid #cccccc; - border-right: 2px solid #cccccc; - background-color: #ffffff; -} - -#about { - clear: both; - padding: 5px; - border-top: 2px solid #cccccc; - background-color: #ffffff; -} - -@media print { - body { - font: 12pt "Times New Roman", "TimeNR", Times, serif; - } - a { font-weight: bold; color: #004080; text-decoration: underline; } - - #main { - background-color: #ffffff; - border-left: 0px; - } - - #container { - margin-left: 2%; - margin-right: 2%; - background-color: #ffffff; - } - - #content { - padding: 1em; - background-color: #ffffff; - } - - #navigation { - display: none; - } - pre.example { - font-family: "Andale Mono", monospace; - font-size: 10pt; - page-break-inside: avoid; - } -} - -table.module_list { - border-width: 1px; - border-style: solid; - border-color: #cccccc; - border-collapse: collapse; -} -table.module_list td { - border-width: 1px; - padding: 3px; - border-style: solid; - border-color: #cccccc; -} -table.module_list td.name { background-color: #f0f0f0; min-width: 250px; } -table.module_list td.summary { width: 100%; } - - -table.function_list { - border-width: 1px; - border-style: solid; - border-color: #cccccc; - border-collapse: collapse; -} -table.function_list td { - border-width: 1px; - padding: 3px; - border-style: solid; - border-color: #cccccc; -} -table.function_list td.name { background-color: #f0f0f0; min-width: 250px; } -table.function_list td.summary { width: 100%; } - -ul.nowrap { - white-space:nowrap; -} - -dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} -dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} -dl.table h3, dl.function h3 {font-size: .95em;} - -/* stop sublists from having initial vertical space */ -ul ul { margin-top: 0px; } -ol ul { margin-top: 0px; } -ol ol { margin-top: 0px; } -ul ol { margin-top: 0px; } - -/* make the target distinct; helps when we're navigating to a function */ -a:target + * { - background-color: #FF9; -} - + +/* styles for prettification of source */ +pre .comment { color: #558817; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #aa5050; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #800080; } +pre .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } +span.parameter { font-family:monospace; } +span.parameter:after { content:":"; } +span.types:before { content:"("; } +span.types:after { content:")"; } +.type { font-weight: bold; font-style:italic } + +body, td, th { font-size: .95em; line-height: 1.2em;} +p { line-height: 1.2em;} + +p, ul { margin: 10px 0 0 0px;} + +strong { font-weight: bold;} + +here { font-weight: bold; color: #c8751d;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 20px 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 16em; + vertical-align: top; + background-color: #f0f0f0; + overflow: visible; +} + +#navigation h2 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 18em; + padding: 2em; + width: 900px; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; min-width: 250px; } +table.module_list td.summary { width: 100%; } + + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f0f0f0; min-width: 250px; } +table.function_list td.summary { width: 100%; } + +ul.nowrap { + white-space:nowrap; +} + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + diff --git a/Scripts/Gameflow.lua b/Scripts/Gameflow.lua index 2b1f2f0f3..4467179a4 100644 --- a/Scripts/Gameflow.lua +++ b/Scripts/Gameflow.lua @@ -57,7 +57,7 @@ test = Level.new() test.nameKey = "level_test" test.scriptFile = "Scripts\\Levels\\New_Level.lua" test.ambientTrack = "108" -test.levelFile = "Data\\TestLevel.ten" +test.levelFile = "Data\\Tut1_water.ten" test.loadScreenFile = "Screens\\rome.jpg" -- 0 is no weather, 1 is rain, 2 is snow. diff --git a/TombEngine/Game/Hud/TargetHighlighter.cpp b/TombEngine/Game/Hud/TargetHighlighter.cpp index dcbf0c4aa..a1d895216 100644 --- a/TombEngine/Game/Hud/TargetHighlighter.cpp +++ b/TombEngine/Game/Hud/TargetHighlighter.cpp @@ -171,8 +171,10 @@ namespace TEN::Hud void TargetHighlighterController::Update(const ItemInfo& playerItem) { - // Check if target highlighter is enabled. - if (!g_Configuration.EnableTargetHighlighter) + const auto& player = GetLaraInfo(playerItem); + + // Check if target highlighter is enabled or lasersight is active. + if (!g_Configuration.EnableTargetHighlighter || player.Control.Look.IsUsingBinoculars) { if (!_crosshairs.empty()) _crosshairs.clear(); @@ -180,8 +182,6 @@ namespace TEN::Hud return; } - const auto& player = GetLaraInfo(playerItem); - // Loop over player targets. auto itemNumbers = std::vector{}; for (const auto* item : player.TargetList) diff --git a/TombEngine/Game/Lara/lara_fire.cpp b/TombEngine/Game/Lara/lara_fire.cpp index bbaeb03ff..fd4fbc5ae 100644 --- a/TombEngine/Game/Lara/lara_fire.cpp +++ b/TombEngine/Game/Lara/lara_fire.cpp @@ -572,7 +572,7 @@ void HandleWeapon(ItemInfo& laraItem) player.Control.HandStatus = HandStatus::WeaponUndraw; } } - else if (player.Inventory.TotalFlares) + else if (player.Inventory.TotalFlares && !player.Control.Look.IsUsingBinoculars) { if (player.Inventory.TotalFlares != -1) player.Inventory.TotalFlares--; diff --git a/TombEngine/Game/Lara/lara_flare.cpp b/TombEngine/Game/Lara/lara_flare.cpp index 0e99f0e11..219952047 100644 --- a/TombEngine/Game/Lara/lara_flare.cpp +++ b/TombEngine/Game/Lara/lara_flare.cpp @@ -20,6 +20,7 @@ #include "Sound/sound.h" #include "Specific/clock.h" #include "Specific/level.h" +#include "Specific/trutils.h" using namespace TEN::Collision::Point; using namespace TEN::Entities::Effects; @@ -496,7 +497,7 @@ bool DoFlareLight(ItemInfo& item, const Vector3i& pos, int flareLife) auto color = (flareColor * intensity * std::clamp(mult, 0.0f, 1.0f)); // Spawn dynamic light. - TriggerDynamicPointLight(lightPos, Color(color), falloff, false); + TriggerDynamicPointLight(lightPos, Color(color), falloff, false, GetHash(item.Name)); // Spawn lensflare if brightness is not 0. if (settings.LensflareBrightness > EPSILON) diff --git a/TombEngine/Game/Lara/lara_helpers.cpp b/TombEngine/Game/Lara/lara_helpers.cpp index 288703527..2c92a6fbf 100644 --- a/TombEngine/Game/Lara/lara_helpers.cpp +++ b/TombEngine/Game/Lara/lara_helpers.cpp @@ -448,70 +448,6 @@ static void ClearPlayerLookAroundActions(const ItemInfo& item) } } -static void SetPlayerOptics(ItemInfo* item) -{ - constexpr auto OPTIC_RANGE_DEFAULT = ANGLE(0.7f); - - auto& player = GetLaraInfo(*item); - - bool breakOptics = true; - - // Standing; can use optics. - if (item->Animation.ActiveState == LS_IDLE || item->Animation.AnimNumber == LA_STAND_IDLE) - breakOptics = false; - - // Crouching; can use optics. - if ((player.Control.IsLow || !IsHeld(In::Crouch)) && - (item->Animation.TargetState == LS_CROUCH_IDLE || item->Animation.AnimNumber == LA_CROUCH_IDLE)) - { - breakOptics = false; - } - - // If lasersight and Look is not held, exit optics. - if (player.Control.Look.IsUsingLasersight && !IsHeld(In::Look)) - breakOptics = true; - - // If lasersight and weapon is holstered, exit optics. - if (player.Control.Look.IsUsingLasersight && IsHeld(In::Draw)) - breakOptics = true; - - // Engage lasersight if available. - if (!player.Control.Look.IsUsingLasersight && !breakOptics && IsHeld(In::Look)) - { - if (player.Control.HandStatus == HandStatus::WeaponReady && - ((player.Control.Weapon.GunType == LaraWeaponType::HK && player.Weapons[(int)LaraWeaponType::HK].HasLasersight) || - (player.Control.Weapon.GunType == LaraWeaponType::Revolver && player.Weapons[(int)LaraWeaponType::Revolver].HasLasersight) || - (player.Control.Weapon.GunType == LaraWeaponType::Crossbow && player.Weapons[(int)LaraWeaponType::Crossbow].HasLasersight))) - { - player.Control.Look.OpticRange = OPTIC_RANGE_DEFAULT; - player.Control.Look.IsUsingBinoculars = true; - player.Control.Look.IsUsingLasersight = true; - player.Inventory.IsBusy = true; - - Camera.DisableInterpolation = true; - BinocularOldCamera = Camera.oldType; - return; - } - } - - if (!breakOptics) - return; - - // Not using optics; return early. - if (!player.Control.Look.IsUsingBinoculars && !player.Control.Look.IsUsingLasersight) - return; - - player.Control.Look.OpticRange = 0; - player.Control.Look.IsUsingBinoculars = false; - player.Control.Look.IsUsingLasersight = false; - player.Inventory.IsBusy = false; - - Camera.DisableInterpolation = true; - Camera.type = BinocularOldCamera; - Camera.bounce = 0; - AlterFOV(LastFOV); -} - static short NormalizeLookAroundTurnRate(short turnRate, short opticRange) { constexpr auto ZOOM_LEVEL_MAX = ANGLE(10.0f); @@ -525,54 +461,15 @@ static short NormalizeLookAroundTurnRate(short turnRate, short opticRange) void HandlePlayerLookAround(ItemInfo& item, bool invertXAxis) { - constexpr auto OPTIC_RANGE_MAX = ANGLE(8.5f); - constexpr auto OPTIC_RANGE_MIN = ANGLE(0.7f); - constexpr auto OPTIC_RANGE_RATE = ANGLE(0.35f); - constexpr auto TURN_RATE_MAX = ANGLE(4.0f); - constexpr auto TURN_RATE_ACCEL = ANGLE(0.75f); + constexpr auto TURN_RATE_MAX = ANGLE(4.0f); + constexpr auto TURN_RATE_ACCEL = ANGLE(0.75f); auto& player = GetLaraInfo(item); // Set optics. Camera.type = CameraType::Look; - SetPlayerOptics(LaraItem); bool isSlow = IsHeld(In::Walk); - - // Zoom optics. - if (player.Control.Look.IsUsingBinoculars || player.Control.Look.IsUsingLasersight) - { - short rangeRate = isSlow ? (OPTIC_RANGE_RATE / 2) : OPTIC_RANGE_RATE; - - // NOTE: Zooming allowed with either StepLeft/StepRight or Walk/Sprint. - if ((IsHeld(In::StepLeft) && !IsHeld(In::StepRight)) || - (IsHeld(In::Walk) && !IsHeld(In::Sprint))) - { - player.Control.Look.OpticRange -= rangeRate; - if (player.Control.Look.OpticRange < OPTIC_RANGE_MIN) - { - player.Control.Look.OpticRange = OPTIC_RANGE_MIN; - } - else - { - SoundEffect(SFX_TR4_BINOCULARS_ZOOM, nullptr, SoundEnvironment::Land, 0.9f); - } - } - else if ((IsHeld(In::StepRight) && !IsHeld(In::StepLeft)) || - (IsHeld(In::Sprint) && !IsHeld(In::Walk))) - { - player.Control.Look.OpticRange += rangeRate; - if (player.Control.Look.OpticRange > OPTIC_RANGE_MAX) - { - player.Control.Look.OpticRange = OPTIC_RANGE_MAX; - } - else - { - SoundEffect(SFX_TR4_BINOCULARS_ZOOM, nullptr, SoundEnvironment::Land, 1.0f); - } - } - } - auto axisCoeff = Vector2::Zero; // Determine X axis coefficient. diff --git a/TombEngine/Game/Lara/lara_optics.cpp b/TombEngine/Game/Lara/lara_optics.cpp new file mode 100644 index 000000000..dc3600c5f --- /dev/null +++ b/TombEngine/Game/Lara/lara_optics.cpp @@ -0,0 +1,286 @@ +#include "framework.h" +#include "Game/Lara/lara_optics.h" + +#include "Game/camera.h" +#include "Game/control/los.h" +#include "Game/effects/effects.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" +#include "Game/Lara/lara_flare.h" +#include "Game/Lara/lara_helpers.h" +#include "Game/Lara/lara_one_gun.h" +#include "Game/Lara/lara_struct.h" +#include "Game/Lara/lara_two_guns.h" +#include "Game/Setup.h" +#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" +#include "Specific/Input/Input.h" + +using namespace TEN::Input; + +static void HandlePlayerOpticZoom(ItemInfo& item) +{ + constexpr auto OPTICS_RANGE_MAX = ANGLE(8.5f); + constexpr auto OPTICS_RANGE_MIN = ANGLE(0.7f); + constexpr auto OPTICS_RANGE_RATE = ANGLE(0.35f); + + auto& player = GetLaraInfo(item); + bool isSlow = IsHeld(In::Walk); + + // Zoom optics. + if (player.Control.Look.IsUsingBinoculars || player.Control.Look.IsUsingLasersight) + { + short rangeRate = isSlow ? (OPTICS_RANGE_RATE / 2) : OPTICS_RANGE_RATE; + + // NOTE: Zooming allowed with either StepLeft/StepRight or Walk/Sprint. + if ((IsHeld(In::StepLeft) && !IsHeld(In::StepRight)) || + (IsHeld(In::Walk) && !IsHeld(In::Sprint))) + { + player.Control.Look.OpticRange -= rangeRate; + if (player.Control.Look.OpticRange < OPTICS_RANGE_MIN) + { + player.Control.Look.OpticRange = OPTICS_RANGE_MIN; + } + else + { + SoundEffect(SFX_TR4_BINOCULARS_ZOOM, nullptr, SoundEnvironment::Land, 0.9f); + } + } + else if ((IsHeld(In::StepRight) && !IsHeld(In::StepLeft)) || + (IsHeld(In::Sprint) && !IsHeld(In::Walk))) + { + player.Control.Look.OpticRange += rangeRate; + if (player.Control.Look.OpticRange > OPTICS_RANGE_MAX) + { + player.Control.Look.OpticRange = OPTICS_RANGE_MAX; + } + else + { + SoundEffect(SFX_TR4_BINOCULARS_ZOOM, nullptr, SoundEnvironment::Land, 1.0f); + } + } + } +} + +static void HandlePlayerOpticAnimations(ItemInfo& item) +{ + auto& player = GetLaraInfo(item); + + if (!player.Control.Look.IsUsingBinoculars && !player.Control.Look.IsUsingLasersight) + return; + + int animNumber = Objects[ID_LARA_BINOCULARS_MESH].loaded ? LA_BINOCULARS_IDLE : LA_STAND_IDLE; + if (player.Control.Look.IsUsingLasersight) + { + switch (player.Control.Weapon.GunType) + { + case LaraWeaponType::Crossbow: + animNumber = Objects[ID_CROSSBOW_ANIM].animIndex + 2; + break; + + case LaraWeaponType::Revolver: + animNumber = Objects[ID_REVOLVER_ANIM].animIndex + 3; + break; + + case LaraWeaponType::HK: + animNumber = Objects[ID_HK_ANIM].animIndex + 2; + break; + } + } + else if (player.Control.Look.IsUsingBinoculars) + { + // Silently holster any weapon or drop any item currently in hand. + if (player.Control.Weapon.GunType == LaraWeaponType::Flare || + player.Control.Weapon.GunType == LaraWeaponType::Torch) + { + CreateFlare(item, player.Control.Weapon.GunType == LaraWeaponType::Flare ? ID_FLARE_ITEM : ID_BURNING_TORCH_ITEM, 0); + UndrawFlareMeshes(item); + + player.Torch.State = TorchState::Holding; + player.Torch.IsLit = false; + player.Flare.ControlLeft = false; + player.Flare.Life = 0; + player.Control.Weapon.GunType = + player.Control.Weapon.RequestGunType = player.Control.Weapon.LastGunType; + } + else if (player.Control.Weapon.GunType != LaraWeaponType::None && + player.Control.HandStatus != HandStatus::Free) + { + if (player.Control.Weapon.GunType <= LaraWeaponType::Uzi) + { + UndrawPistolMesh(item, player.Control.Weapon.GunType, false); + UndrawPistolMesh(item, player.Control.Weapon.GunType, true); + } + else + { + if (player.Control.Weapon.WeaponItem != NO_VALUE) + { + KillItem(player.Control.Weapon.WeaponItem); + player.Control.Weapon.WeaponItem = NO_VALUE; + } + UndrawShotgunMeshes(item, player.Control.Weapon.GunType); + } + + player.TargetEntity = nullptr; + } + + int objNumber = Objects[ID_LARA_BINOCULARS_MESH].loaded ? ID_LARA_BINOCULARS_MESH : ID_LARA_SKIN; + item.Model.MeshIndex[LM_RHAND] = Objects[objNumber].meshIndex + LM_RHAND; + + player.Control.HandStatus = HandStatus::Free; + } + + player.LeftArm.Locked = + player.RightArm.Locked = false; + player.LeftArm.FrameNumber = + player.RightArm.FrameNumber = 0; + player.LeftArm.AnimNumber = + player.RightArm.AnimNumber = animNumber; + player.LeftArm.FrameBase = + player.RightArm.FrameBase = GetAnimData(animNumber).FramePtr; +} + +static void ResetPlayerOpticAnimations(ItemInfo& item) +{ + auto& player = GetLaraInfo(item); + + ResetPlayerFlex(&item); + + player.LeftArm.Locked = + player.RightArm.Locked = false; + player.LeftArm.AnimNumber = + player.RightArm.AnimNumber = 0; + player.LeftArm.FrameNumber = + player.RightArm.FrameNumber = 0; + player.RightArm.FrameBase = + player.LeftArm.FrameBase = GetAnimData(item).FramePtr; + player.Control.HandStatus = player.Control.Look.IsUsingLasersight ? HandStatus::WeaponReady : HandStatus::Free; + + if (!player.Control.Look.IsUsingLasersight) + item.Model.MeshIndex[LM_RHAND] = item.Model.BaseMesh + LM_RHAND; + + player.Control.Look.OpticRange = 0; + player.Control.Look.IsUsingBinoculars = player.Control.Look.IsUsingLasersight = false; + player.Inventory.IsBusy = false; + + Camera.DisableInterpolation = true; + Camera.type = BinocularOldCamera; + Camera.bounce = 0; + AlterFOV(LastFOV); + SetScreenFadeIn(OPTICS_FADE_SPEED); +} + +static void DoOpticsHighlight(const ItemInfo& item, const Vector3i& origin, const Vector3i& target) +{ + auto origin2 = GameVector(origin, item.RoomNumber); + auto target2 = GameVector(target); + + const auto& binocularsColor = g_GameFlow->GetSettings()->Camera.BinocularLightColor; + const auto& lasersightColor = g_GameFlow->GetSettings()->Camera.LasersightLightColor; + const auto& color = GetLaraInfo(item).Control.Look.IsUsingLasersight ? lasersightColor : binocularsColor; + + TriggerDynamicLight(origin2.x, origin2.y, origin2.z, 12, color.GetR(), color.GetG(), color.GetB()); + + if (!LOS(&origin2, &target2)) + { + int luma = sqrt(SQUARE(origin2.x - target2.x) + SQUARE(origin2.y - target2.y) + SQUARE(origin2.z - target2.z)) * CLICK(1); + if ((luma + 8) > 31) + luma = 31; + + auto dir = origin2.ToVector3() - target2.ToVector3(); + dir.Normalize(); + dir *= BLOCK(1); + + byte r = std::max(0, color.GetR() - luma); + byte g = std::max(0, color.GetG() - luma); + byte b = std::max(0, color.GetB() - luma); + TriggerDynamicLight(target2.x + dir.x, target2.y + dir.y, target2.z + dir.z, luma + 12, r, g, b); + } +} + +bool HandlePlayerOptics(ItemInfo& item) +{ + auto& player = GetLaraInfo(item); + + bool breakOptics = true; + + // Standing; can use optics. + if (item.Animation.ActiveState == LS_IDLE || item.Animation.AnimNumber == LA_STAND_IDLE) + breakOptics = false; + + // Crouching; can use optics. + if ((player.Control.IsLow || !IsHeld(In::Crouch)) && + (item.Animation.TargetState == LS_CROUCH_IDLE || item.Animation.AnimNumber == LA_CROUCH_IDLE)) + { + breakOptics = false; + } + + // If lasersight and Look is not held, exit optics. + if (player.Control.Look.IsUsingLasersight && !IsHeld(In::Look)) + breakOptics = true; + + // If lasersight and weapon is holstered, exit optics. + if (player.Control.Look.IsUsingLasersight && IsHeld(In::Draw)) + breakOptics = true; + + // Engage lasersight if available. + if (!breakOptics && !player.Control.Look.IsUsingLasersight && IsHeld(In::Look)) + { + if (player.Control.HandStatus == HandStatus::WeaponReady && + ((player.Control.Weapon.GunType == LaraWeaponType::HK && player.Weapons[(int)LaraWeaponType::HK].HasLasersight) || + (player.Control.Weapon.GunType == LaraWeaponType::Revolver && player.Weapons[(int)LaraWeaponType::Revolver].HasLasersight) || + (player.Control.Weapon.GunType == LaraWeaponType::Crossbow && player.Weapons[(int)LaraWeaponType::Crossbow].HasLasersight))) + { + player.Control.Look.OpticRange = OPTICS_RANGE_DEFAULT; + player.Control.Look.IsUsingBinoculars = true; + player.Control.Look.IsUsingLasersight = true; + player.Inventory.IsBusy = true; + + Camera.DisableInterpolation = true; + BinocularOldCamera = Camera.oldType; + SetScreenFadeIn(OPTICS_FADE_SPEED); + } + } + + // Not using optics; return early. + if (!player.Control.Look.IsUsingBinoculars && !player.Control.Look.IsUsingLasersight) + return true; + + AlterFOV(7 * (ANGLE(11.5f) - player.Control.Look.OpticRange), false); + + // Handle various binocular controls. + if (!player.Control.Look.IsUsingLasersight) + { + if (IsClicked(In::Deselect) || + IsClicked(In::Roll) || + IsClicked(In::Jump) || + IsClicked(In::Draw) || + IsClicked(In::Look) || + IsHeld(In::Flare)) + { + breakOptics = true; + } + } + + // Handle lasersight highlight. + if (player.Control.Look.IsUsingLasersight || IsHeld(In::Action)) + { + if (!player.Control.Look.IsUsingLasersight) + ClearAction(In::Action); + + auto origin = Camera.pos.ToVector3i(); + auto target = Camera.target.ToVector3i(); + DoOpticsHighlight(item, origin, target); + } + + if (!breakOptics) + { + HandlePlayerOpticAnimations(item); + HandlePlayerOpticZoom(item); + return true; + } + else + { + ResetPlayerOpticAnimations(item); + return false; + } +} diff --git a/TombEngine/Game/Lara/lara_optics.h b/TombEngine/Game/Lara/lara_optics.h new file mode 100644 index 000000000..57206a566 --- /dev/null +++ b/TombEngine/Game/Lara/lara_optics.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Specific/clock.h" + +struct ItemInfo; + +constexpr auto OPTICS_FADE_SPEED = 6.0f / FPS; +constexpr auto OPTICS_RANGE_DEFAULT = ANGLE(0.7f); + +bool HandlePlayerOptics(ItemInfo& item); diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index 7067def78..2ff2778d9 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -12,6 +12,7 @@ #include "Game/Lara/lara.h" #include "Game/Lara/lara_fire.h" #include "Game/Lara/lara_helpers.h" +#include "Game/Lara/lara_optics.h" #include "Game/room.h" #include "Game/savegame.h" #include "Game/Setup.h" @@ -50,6 +51,7 @@ struct OLD_CAMERA }; bool ItemCameraOn; +GameVector LastPosition; GameVector LastTarget; GameVector LastIdeal; GameVector Ideals[5]; @@ -58,7 +60,6 @@ int CameraSnaps = 0; int TargetSnaps = 0; GameVector LookCamPosition; GameVector LookCamTarget; -Vector3i CamOldPos; CAMERA_INFO Camera; GameVector ForcedFixedCamera; int UseForcedFixedCamera; @@ -945,50 +946,13 @@ void BinocularCamera(ItemInfo* item) { auto& player = GetLaraInfo(*item); - if (!player.Control.Look.IsUsingLasersight) - { - if (IsClicked(In::Deselect) || - IsClicked(In::Draw) || - IsClicked(In::Look) || - IsHeld(In::Flare)) - { - ResetPlayerFlex(item); - player.Control.Look.OpticRange = 0; - player.Control.Look.IsUsingBinoculars = false; - player.Inventory.IsBusy = false; - - Camera.type = BinocularOldCamera; - Camera.DisableInterpolation = true; - Camera.target = LastTarget; - AlterFOV(LastFOV); - return; - } - - if (IsHeld(In::Action)) - { - ClearAction(In::Action); - - auto origin = Camera.pos.ToVector3i(); - auto target = Camera.target.ToVector3i(); - LaraTorch(&origin, &target); - } - } - AlterFOV(7 * (ANGLE(11.5f) - player.Control.Look.OpticRange), false); int x = item->Pose.Position.x; - int y = item->Pose.Position.y - CLICK(2); + int y = item->Pose.Position.y + GameBoundingBox(item).Y1; int z = item->Pose.Position.z; auto pointColl = GetPointCollision(Vector3i(x, y, z), item->RoomNumber); - if (pointColl.GetCeilingHeight() <= (y - CLICK(1))) - { - y -= CLICK(1); - } - else - { - y = pointColl.GetCeilingHeight() + CLICK(0.25f); - } Camera.pos.x = x; Camera.pos.y = y; @@ -1083,18 +1047,24 @@ static bool CalculateDeathCamera(const ItemInfo& item) void CalculateCamera(const CollisionInfo& coll) { - CamOldPos.x = Camera.pos.x; - CamOldPos.y = Camera.pos.y; - CamOldPos.z = Camera.pos.z; + if (ItemCameraOn) + return; + + if (!HandlePlayerOptics(*LaraItem)) + { + Camera.pos = LastPosition; + Camera.target = LastTarget; + } if (Lara.Control.Look.IsUsingBinoculars) { BinocularCamera(LaraItem); return; } - - if (ItemCameraOn) - return; + else + { + LastPosition = Camera.pos; + } if (UseForcedFixedCamera != 0) { @@ -1386,7 +1356,7 @@ void ItemPushCamera(GameBoundingBox* bounds, Pose* pos, short radius) auto pointColl = GetPointCollision(Camera.pos.ToVector3i(), Camera.pos.RoomNumber); if (pointColl.GetFloorHeight() == NO_HEIGHT || Camera.pos.y > pointColl.GetFloorHeight() || Camera.pos.y < pointColl.GetCeilingHeight()) - Camera.pos = GameVector(CamOldPos, pointColl.GetRoomNumber()); + Camera.pos = GameVector(LastPosition.ToVector3i(), pointColl.GetRoomNumber()); } bool CheckItemCollideCamera(ItemInfo* item) diff --git a/TombEngine/Game/control/los.cpp b/TombEngine/Game/control/los.cpp index faea4f675..861b92bb4 100644 --- a/TombEngine/Game/control/los.cpp +++ b/TombEngine/Game/control/los.cpp @@ -262,6 +262,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo { Lara.Control.Weapon.HasFired = true; Lara.Control.Weapon.Fired = true; + Lara.RightArm.GunFlash = Weapons[(int)Lara.Control.Weapon.GunType].FlashTime; if (Lara.Control.Weapon.GunType == LaraWeaponType::Revolver) SoundEffect(SFX_TR4_REVOLVER_FIRE, nullptr); @@ -442,12 +443,6 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo } } - if (drawTarget && (hasHit || !result)) - { - auto& color = g_GameFlow->GetSettings()->Camera.LasersightLightColor; - TriggerDynamicLight(target2.x, target2.y, target2.z, 64, color.GetR(), color.GetG(), color.GetB()); - } - return hitProcessed; } diff --git a/TombEngine/Game/effects/effects.cpp b/TombEngine/Game/effects/effects.cpp index 0594939cb..196a5da53 100644 --- a/TombEngine/Game/effects/effects.cpp +++ b/TombEngine/Game/effects/effects.cpp @@ -1260,7 +1260,7 @@ void TriggerDynamicSpotLight(const Vector3& pos, const Vector3& dir, const Color // Deprecated. Use above version instead. void TriggerDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte 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); + g_Renderer.AddDynamicPointLight(Vector3(x, y, z), (float)(falloff * UCHAR_MAX), Color(r / (float)CHAR_MAX, g / (float)CHAR_MAX, b / (float)CHAR_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 3da3f6ab9..bfb9c5b4f 100644 --- a/TombEngine/Game/effects/effects.h +++ b/TombEngine/Game/effects/effects.h @@ -300,8 +300,11 @@ 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); + +// TODO: use Spawn prefix instead. 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); diff --git a/TombEngine/Game/gui.cpp b/TombEngine/Game/gui.cpp index 26b9ad59c..d5b38f83a 100644 --- a/TombEngine/Game/gui.cpp +++ b/TombEngine/Game/gui.cpp @@ -12,6 +12,7 @@ #include "Game/Lara/lara.h" #include "Game/Lara/lara_fire.h" #include "Game/Lara/lara_helpers.h" +#include "Game/Lara/lara_optics.h" #include "Game/Lara/lara_one_gun.h" #include "Game/Lara/lara_two_guns.h" #include "Game/pickup/pickup.h" @@ -2057,8 +2058,6 @@ namespace TEN::Gui auto& player = GetLaraInfo(item); - short prevOpticRange = player.Control.Look.OpticRange; - player.Control.Look.OpticRange = 0; player.Inventory.OldBusy = false; item.MeshBits = ALL_JOINT_BITS; @@ -2201,26 +2200,11 @@ namespace TEN::Gui (player.Control.IsLow && !IsHeld(In::Crouch))) && !UseSpotCam && !TrackCameraInit) { - Camera.DisableInterpolation = true; - player.Control.Look.OpticRange = ANGLE(0.7f); + SetScreenFadeIn(OPTICS_FADE_SPEED); + BinocularOldCamera = Camera.oldType; + player.Control.Look.OpticRange = OPTICS_RANGE_DEFAULT; player.Control.Look.IsUsingBinoculars = true; player.Inventory.OldBusy = true; - - // TODO: To prevent Lara from crouching or performing other actions, the inherent state of - // LA_BINOCULARS_IDLE must be changed to LS_IDLE. @Sezz 2022.05.19 - //SetAnimation(item, LA_BINOCULARS_IDLE); - - if (player.Control.HandStatus != HandStatus::Free) - player.Control.HandStatus = HandStatus::WeaponUndraw; - } - - if (prevOpticRange != ANGLE(0.0f)) - { - player.Control.Look.OpticRange = prevOpticRange; - } - else - { - BinocularOldCamera = Camera.oldType; } InventoryItemChosen = NO_VALUE; diff --git a/TombEngine/Objects/Generic/Object/burning_torch.cpp b/TombEngine/Objects/Generic/Object/burning_torch.cpp index f12876454..075bd3122 100644 --- a/TombEngine/Objects/Generic/Object/burning_torch.cpp +++ b/TombEngine/Objects/Generic/Object/burning_torch.cpp @@ -296,33 +296,6 @@ namespace TEN::Entities::Generic } } - void LaraTorch(Vector3i* origin, Vector3i* target) - { - auto pos1 = GameVector(*origin, LaraItem->RoomNumber); - auto pos2 = GameVector(*target); - - const auto& color = g_GameFlow->GetSettings()->Camera.BinocularLightColor; - - TriggerDynamicLight(pos1.x, pos1.y, pos1.z, 12, color.GetR(), color.GetG(), color.GetB()); - - if (!LOS(&pos1, &pos2)) - { - int l = sqrt(pow(pos1.x - pos2.x, 2) + pow(pos1.y - pos2.y, 2) + pow(pos1.z - pos2.z, 2)) * CLICK(1); - - if (l + 8 > 31) - l = 31; - - auto dir = pos1.ToVector3() - pos2.ToVector3(); - dir.Normalize(); - dir *= BLOCK(1); - - byte r = std::max(0, color.GetR() - l); - byte g = std::max(0, color.GetG() - l); - byte b = std::max(0, color.GetB() - l); - TriggerDynamicLight(pos2.x + dir.x, pos2.y + dir.y, pos2.z + dir.z, l + 12, r, g, b); - } - } - void FireCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) { auto* torchItem = &g_Level.Items[itemNumber]; diff --git a/TombEngine/Objects/Generic/Object/burning_torch.h b/TombEngine/Objects/Generic/Object/burning_torch.h index be8d62f55..668a7b415 100644 --- a/TombEngine/Objects/Generic/Object/burning_torch.h +++ b/TombEngine/Objects/Generic/Object/burning_torch.h @@ -10,6 +10,5 @@ namespace TEN::Entities::Generic void DoFlameTorch(); void GetFlameTorch(); void TorchControl(short itemNumber); - void LaraTorch(Vector3i* origin, Vector3i* target); void FireCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); } diff --git a/TombEngine/Objects/TR5/Trap/LaserBeam.cpp b/TombEngine/Objects/TR5/Trap/LaserBeam.cpp index bf7252333..2154be045 100644 --- a/TombEngine/Objects/TR5/Trap/LaserBeam.cpp +++ b/TombEngine/Objects/TR5/Trap/LaserBeam.cpp @@ -37,8 +37,6 @@ namespace TEN::Entities::Traps Radius = (item.TriggerFlags == 0) ? RADIUS_STEP : (abs(item.TriggerFlags) * RADIUS_STEP); IsLethal = (item.TriggerFlags > 0); IsHeavyActivator = (item.TriggerFlags <= 0); - - Update(item); } static void SpawnLaserSpark(const GameVector& pos, short angle, int count, const Vector4& colorStart) diff --git a/TombEngine/Objects/game_object_ids.h b/TombEngine/Objects/game_object_ids.h index 5bf09dace..cd5b96e2b 100644 --- a/TombEngine/Objects/game_object_ids.h +++ b/TombEngine/Objects/game_object_ids.h @@ -32,9 +32,9 @@ enum GAME_OBJECT_ID : short ID_ACTOR1_SPEECH_HEAD2, ID_ACTOR2_SPEECH_HEAD1, ID_ACTOR2_SPEECH_HEAD2, + ID_LARA_BINOCULARS_MESH, ID_LARA_EXTRA_MESH1, ID_LARA_EXTRA_MESH2, - ID_LARA_EXTRA_MESH3, ID_LARA_WATER_MESH, ID_LARA_PETROL_MESH, ID_LARA_DIRT_MESH, diff --git a/TombEngine/Renderer/RenderView.cpp b/TombEngine/Renderer/RenderView.cpp index 1f02db401..10dd2c347 100644 --- a/TombEngine/Renderer/RenderView.cpp +++ b/TombEngine/Renderer/RenderView.cpp @@ -53,6 +53,7 @@ namespace TEN::Renderer FogBulbsToDraw.clear(); LensFlaresToDraw.clear(); TransparentObjectsToDraw.clear(); + Mirrors.clear(); } RenderViewCamera::RenderViewCamera(CAMERA_INFO* cam, float roll, float fov, float n, float f, int w, int h) diff --git a/TombEngine/Renderer/RenderView.h b/TombEngine/Renderer/RenderView.h index b028b4788..bd0f9337e 100644 --- a/TombEngine/Renderer/RenderView.h +++ b/TombEngine/Renderer/RenderView.h @@ -15,6 +15,7 @@ #include "Renderer/Structures/RendererSortableObject.h" #include "Renderer/Structures/RendererSpriteToDraw.h" #include "Renderer/Structures/RendererLensFlare.h" +#include "Renderer/Structures/RendererMirror.h" namespace TEN::Renderer { @@ -45,14 +46,15 @@ namespace TEN::Renderer RenderViewCamera Camera; D3D11_VIEWPORT Viewport; - std::vector RoomsToDraw = {}; - std::vector LightsToDraw = {}; - std::vector FogBulbsToDraw = {}; - std::vector SpritesToDraw = {}; - std::vector DisplaySpritesToDraw = {}; - std::map> SortedStaticsToDraw = {}; + std::vector RoomsToDraw = {}; + std::vector LightsToDraw = {}; + std::vector FogBulbsToDraw = {}; + std::vector SpritesToDraw = {}; + std::vector DisplaySpritesToDraw = {}; + std::map> SortedStaticsToDraw = {}; std::vector TransparentObjectsToDraw = {}; - std::vector LensFlaresToDraw = {}; + std::vector LensFlaresToDraw = {}; + std::vector Mirrors = {}; RenderView(CAMERA_INFO* cam, float roll, float fov, float nearPlane, float farPlane, int w, int h); RenderView(const Vector3& pos, const Vector3& dir, const Vector3& up, int w, int h, int room, float nearPlane, float farPlane, float fov); diff --git a/TombEngine/Renderer/Renderer.cpp b/TombEngine/Renderer/Renderer.cpp index 0db0ed2d0..4fb6b4322 100644 --- a/TombEngine/Renderer/Renderer.cpp +++ b/TombEngine/Renderer/Renderer.cpp @@ -219,6 +219,9 @@ namespace TEN::Renderer lights[index].Direction = Vector3::Lerp(light.PrevDirection, light.Direction, GetInterpolationFactor()); } + ReflectVectorOptionally(lights[index].Position); + ReflectVectorOptionally(lights[index].Direction); + // Bitmask light type to filter it in the shader later. return (1 << (31 - (int)light.Type)); } @@ -253,11 +256,13 @@ namespace TEN::Renderer _stInstancedStaticMeshBuffer.StaticMeshes[instanceID].NumLights = (int)lights.size() | lightTypeMask; } - void Renderer::BindMoveableLights(std::vector& lights, int roomNumber, int prevRoomNumber, float fade) + void Renderer::BindMoveableLights(std::vector& lights, int roomNumber, int prevRoomNumber, float fade, bool shadow) { - int lightTypeMask = 0; + constexpr int SHADOWABLE_MASK = (1 << 16); + int lightTypeMask = 0; int numLights = 0; + for (int i = 0; i < lights.size(); i++) { float fadedCoeff = 1.0f; @@ -281,7 +286,7 @@ namespace TEN::Renderer numLights++; } - _stItem.NumLights = numLights | lightTypeMask; + _stItem.NumLights = numLights | lightTypeMask | (shadow ? SHADOWABLE_MASK : 0); } void Renderer::BindConstantBufferVS(ConstantBufferRegister constantBufferType, ID3D11Buffer** buffer) diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 99a621ab0..365fbcb83 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -65,6 +65,7 @@ #include "Renderer/Structures/RendererRoomAmbientMap.h" #include "Renderer/Structures/RendererObject.h" #include "Renderer/Structures/RendererStar.h" +#include "Structures/RendererShader.h" enum GAME_OBJECT_ID : short; enum class SphereSpaceType; @@ -124,43 +125,31 @@ namespace TEN::Renderer // Shaders - ComPtr _vsRooms; - ComPtr _vsRoomsAnimatedTextures; - ComPtr _psRooms; - ComPtr _psRoomsTransparent; - ComPtr _vsItems; - ComPtr _psItems; - ComPtr _vsStatics; - ComPtr _psStatics; - ComPtr _vsSky; - ComPtr _psSky; - ComPtr _vsSprites; - ComPtr _psSprites; - ComPtr _vsInstancedSprites; - ComPtr _psInstancedSprites; - ComPtr _vsInstancedStaticMeshes; - ComPtr _psInstancedStaticMeshes; - ComPtr _vsSolid; - ComPtr _psSolid; - ComPtr _vsInventory; - ComPtr _psInventory; - ComPtr _vsFullScreenQuad; - ComPtr _psFullScreenQuad; - ComPtr _vsShadowMap; - ComPtr _psShadowMap; - ComPtr _vsHUD; - ComPtr _psHUDColor; - ComPtr _psHUDTexture; - ComPtr _psHUDBarColor; - ComPtr _vsGBufferRooms; - ComPtr _vsGBufferRoomsAnimated; - ComPtr _vsGBufferItems; - ComPtr _vsGBufferStatics; - ComPtr _vsGBufferInstancedStatics; - ComPtr _psGBuffer; - ComPtr _vsRoomAmbient; - ComPtr _vsRoomAmbientSky; - ComPtr _psRoomAmbient; + RendererShader _sRooms; + RendererShader _sRoomsAnimated; + RendererShader _sRoomsTransparent; + RendererShader _sRoomAmbient; + RendererShader _sRoomAmbientSky; + RendererShader _sItems; + RendererShader _sStatics; + RendererShader _sInstancedStatics; + RendererShader _sSprites; + RendererShader _sInstancedSprites; + RendererShader _sSky; + RendererShader _sSolid; + RendererShader _sInventory; + RendererShader _sFullScreenQuad; + RendererShader _sShadowMap; + RendererShader _sHUD; + RendererShader _sHUDColor; + RendererShader _sHUDTexture; + RendererShader _sHUDBarColor; + RendererShader _sGBuffer; + RendererShader _sGBufferRooms; + RendererShader _sGBufferRoomsAnimated; + RendererShader _sGBufferItems; + RendererShader _sGBufferStatics; + RendererShader _sGBufferInstancedStatics; // Constant buffers @@ -352,17 +341,13 @@ namespace TEN::Renderer RenderTarget2D _SMAAEdgesRenderTarget; RenderTarget2D _SMAABlendRenderTarget; - ComPtr _SMAAEdgeDetectionVS; - ComPtr _SMAALumaEdgeDetectionPS; - ComPtr _SMAAColorEdgeDetectionPS; - ComPtr _SMAADepthEdgeDetectionPS; - ComPtr _SMAABlendingWeightCalculationVS; - ComPtr _SMAABlendingWeightCalculationPS; - ComPtr _SMAANeighborhoodBlendingVS; - ComPtr _SMAANeighborhoodBlendingPS; - - ComPtr _vsFXAA; - ComPtr _psFXAA; + RendererShader _sSMAAEdgeDetection; + RendererShader _sSMAALumaEdgeDetection; + RendererShader _sSMAAColorEdgeDetection; + RendererShader _sSMAADepthEdgeDetection; + RendererShader _sSMAABlendingWeightCalculation; + RendererShader _sSMAANeighborhoodBlending; + RendererShader _sFXAA; // Post-process @@ -372,33 +357,34 @@ namespace TEN::Renderer VertexBuffer _fullscreenTriangleVertexBuffer; ComPtr _fullscreenTriangleInputLayout = nullptr; - ComPtr _vsPostProcess; - ComPtr _psPostProcessCopy; - ComPtr _psPostProcessMonochrome; - ComPtr _psPostProcessNegative; - ComPtr _psPostProcessExclusion; - ComPtr _psPostProcessFinalPass; - ComPtr _psPostProcessLensFlare; + RendererShader _sPostProcess; + RendererShader _sPostProcessMonochrome; + RendererShader _sPostProcessNegative; + RendererShader _sPostProcessExclusion; + RendererShader _sPostProcessFinalPass; + RendererShader _sPostProcessLensFlare; bool _doingFullscreenPass = false; // SSAO - ComPtr _vsSSAO; - ComPtr _psSSAO; - ComPtr _psSSAOBlur; Texture2D _SSAONoiseTexture; RenderTarget2D _SSAORenderTarget; RenderTarget2D _SSAOBlurredRenderTarget; std::vector _SSAOKernel; + RendererShader _sSSAO; + RendererShader _sSSAOBlur; + // New ambient light techinque + RenderTarget2D _roomAmbientMapFront; RenderTarget2D _roomAmbientMapBack; // Special effects std::vector _causticTextures; + RendererMirror* _currentMirror = nullptr; // Transparency @@ -410,11 +396,13 @@ namespace TEN::Renderer // High framerate. float _interpolationFactor = 0.0f; - bool _graphicsSettingsChanged = false; // Private functions + RendererShader CompileOrLoadShader(const std::string& fileName, const std::string& funcName, ShaderType type, const D3D_SHADER_MACRO* defines = nullptr); + void BindShader(const RendererShader& shader); + void ApplySMAA(RenderTarget2D* renderTarget, RenderView& view); void ApplyFXAA(RenderTarget2D* renderTarget, RenderView& view); void BindTexture(TextureRegister registerType, TextureBase* texture, SamplerStateRegister samplerType); @@ -422,7 +410,7 @@ namespace TEN::Renderer void BindRoomLights(std::vector& lights); void BindStaticLights(std::vector& lights); void BindInstancedStaticLights(std::vector& lights, int instanceID); - void BindMoveableLights(std::vector& lights, int roomNumber, int prevRoomNumber, float fade); + void BindMoveableLights(std::vector& lights, int roomNumber, int prevRoomNumber, float fade, bool shadow); void BindRenderTargetAsTexture(TextureRegister registerType, RenderTarget2D* target, SamplerStateRegister samplerType); void BindConstantBufferVS(ConstantBufferRegister constantBufferType, ID3D11Buffer** buffer); void BindConstantBufferPS(ConstantBufferRegister constantBufferType, ID3D11Buffer** buffer); @@ -431,6 +419,7 @@ namespace TEN::Renderer void UpdateAnimation(RendererItem* item, RendererObject& obj, const AnimFrameInterpData& frameData, int mask, bool useObjectWorldRotation = false); bool CheckPortal(short parentRoomNumber, RendererDoor* door, Vector4 viewPort, Vector4* clipPort, RenderView& renderView); void GetVisibleRooms(short from, short to, Vector4 viewPort, bool water, int count, bool onlyRooms, RenderView& renderView); + void CollectMirrors(RenderView& renderView); void CollectRooms(RenderView& renderView, bool onlyRooms); void CollectItems(short roomNumber, RenderView& renderView); void CollectStatics(short roomNumber, RenderView& renderView); @@ -450,11 +439,12 @@ namespace TEN::Renderer void InitializeMenuBars(int y); void InitializeSky(); void DrawAllStrings(); + void PrepareDynamicLight(RendererLight& light); void PrepareLaserBarriers(RenderView& view); void PrepareSingleLaserBeam(RenderView& view); void DrawHorizonAndSky(RenderView& renderView, ID3D11DepthStencilView* depthTarget); void DrawRooms(RenderView& view, RendererPass rendererPass); - void DrawItems(RenderView& view, RendererPass rendererPass); + void DrawItems(RenderView& view, RendererPass rendererPass, bool onlyPlayer = false); void DrawAnimatingItem(RendererItem* item, RenderView& view, RendererPass rendererPass); void DrawWaterfalls(RendererItem* item, RenderView& view, int fps, RendererPass rendererPass); void DrawBaddyGunflashes(RenderView& view); @@ -469,6 +459,8 @@ namespace TEN::Renderer void PrepareWeatherParticles(RenderView& view); void PrepareDrips(RenderView& view); void PrepareBubbles(RenderView& view); + void DoRenderPass(RendererPass pass, RenderView& view, bool drawMirrors); + void DrawObjects(RendererPass pass, RenderView& view, bool player, bool moveables, bool statics, bool sprites); void DrawEffects(RenderView& view, RendererPass rendererPass); void DrawEffect(RenderView& view, RendererEffect* effect, RendererPass rendererPass); void PrepareSplashes(RenderView& view); @@ -534,6 +526,7 @@ namespace TEN::Renderer void SetScissor(RendererRectangle rectangle); bool SetupBlendModeAndAlphaTest(BlendMode blendMode, RendererPass rendererPass, int drawPass); void SortAndPrepareSprites(RenderView& view); + void SortTransparentFaces(RenderView& view); void ResetItems(); void ResetScissor(); void ResetDebugVariables(); @@ -572,6 +565,29 @@ namespace TEN::Renderer void CreateSSAONoiseTexture(); void InitializeSMAA(); + bool IsRoomReflected(RenderView& renderView, int roomNumber); + + inline bool IgnoreReflectionPassForRoom(int roomNumber) + { + return (_currentMirror != nullptr && roomNumber != _currentMirror->RoomNumber); + } + + inline void ReflectVectorOptionally(Vector3& vector) + { + if (_currentMirror == nullptr) + return; + + vector = Vector3::Transform(vector, _currentMirror->ReflectionMatrix); + } + + inline void ReflectMatrixOptionally(Matrix& matrix) + { + if (_currentMirror == nullptr) + return; + + matrix = matrix * _currentMirror->ReflectionMatrix; + } + inline void DrawIndexedTriangles(int count, int baseIndex, int baseVertex) { _context->DrawIndexed(count, baseIndex, baseVertex); @@ -647,7 +663,6 @@ namespace TEN::Renderer void FreeRendererData(); 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/RendererAntialiasing.cpp b/TombEngine/Renderer/RendererAntialiasing.cpp index 165a14494..b78fd9b1b 100644 --- a/TombEngine/Renderer/RendererAntialiasing.cpp +++ b/TombEngine/Renderer/RendererAntialiasing.cpp @@ -14,7 +14,7 @@ namespace TEN::Renderer ResetScissor(); // Common vertex shader to all fullscreen effects - _context->VSSetShader(_vsPostProcess.Get(), nullptr, 0); + BindShader(_sPostProcess); // We draw a fullscreen triangle _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); @@ -30,7 +30,6 @@ namespace TEN::Renderer _context->ClearRenderTargetView(_SMAASceneRenderTarget.RenderTargetView.Get(), clearColor); _context->OMSetRenderTargets(1, _SMAASceneRenderTarget.RenderTargetView.GetAddressOf(), nullptr); - _context->PSSetShader(_psPostProcessCopy.Get(), nullptr, 0); BindRenderTargetAsTexture(TextureRegister::ColorMap, renderTarget, SamplerStateRegister::PointWrap); DrawTriangles(3, 0); @@ -41,8 +40,8 @@ namespace TEN::Renderer SetCullMode(CullMode::CounterClockwise); _context->OMSetRenderTargets(1, _SMAAEdgesRenderTarget.RenderTargetView.GetAddressOf(), nullptr); - _context->VSSetShader(_SMAAEdgeDetectionVS.Get(), nullptr, 0); - _context->PSSetShader(_SMAAColorEdgeDetectionPS.Get(), nullptr, 0); + BindShader(_sSMAAEdgeDetection); + BindShader(_sSMAAColorEdgeDetection); _stSMAABuffer.BlendFactor = 1.0f; _cbSMAABuffer.UpdateData(_stSMAABuffer, _context.Get()); @@ -60,8 +59,7 @@ namespace TEN::Renderer // 2) Blend weights calculation. _context->OMSetRenderTargets(1, _SMAABlendRenderTarget.RenderTargetView.GetAddressOf(), nullptr); - _context->VSSetShader(_SMAABlendingWeightCalculationVS.Get(), nullptr, 0); - _context->PSSetShader(_SMAABlendingWeightCalculationPS.Get(), nullptr, 0); + BindShader(_sSMAABlendingWeightCalculation); _stSMAABuffer.SubsampleIndices = Vector4::Zero; _cbSMAABuffer.UpdateData(_stSMAABuffer, _context.Get()); @@ -78,8 +76,7 @@ namespace TEN::Renderer // 3) Neighborhood blending. _context->OMSetRenderTargets(1, renderTarget->RenderTargetView.GetAddressOf(), nullptr); - _context->VSSetShader(_SMAANeighborhoodBlendingVS.Get(), nullptr, 0); - _context->PSSetShader(_SMAANeighborhoodBlendingPS.Get(), nullptr, 0); + BindShader(_sSMAANeighborhoodBlending); BindRenderTargetAsTexture(static_cast(0), &_SMAASceneRenderTarget, SamplerStateRegister::LinearClamp); BindRenderTargetAsTexture(static_cast(1), &_SMAASceneSRGBRenderTarget, SamplerStateRegister::LinearClamp); @@ -103,7 +100,7 @@ namespace TEN::Renderer ResetScissor(); // Common vertex shader to all fullscreen effects - _context->VSSetShader(_vsPostProcess.Get(), nullptr, 0); + BindShader(_sPostProcess); // We draw a fullscreen triangle _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); @@ -119,7 +116,6 @@ namespace TEN::Renderer _context->ClearRenderTargetView(_tempRenderTarget.RenderTargetView.Get(), clearColor); _context->OMSetRenderTargets(1, _tempRenderTarget.RenderTargetView.GetAddressOf(), nullptr); - _context->PSSetShader(_psPostProcessCopy.Get(), nullptr, 0); BindRenderTargetAsTexture(TextureRegister::ColorMap, renderTarget, SamplerStateRegister::PointWrap); DrawTriangles(3, 0); @@ -127,7 +123,7 @@ namespace TEN::Renderer _context->ClearRenderTargetView(renderTarget->RenderTargetView.Get(), Colors::Black); _context->OMSetRenderTargets(1, renderTarget->RenderTargetView.GetAddressOf(), nullptr); - _context->PSSetShader(_psFXAA.Get(), nullptr, 0); + BindShader(_sFXAA); _stPostProcessBuffer.ViewportWidth = _screenWidth; _stPostProcessBuffer.ViewportHeight = _screenHeight; diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index 81bf29a01..fc18ca9e5 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -167,8 +167,7 @@ namespace TEN::Renderer UINT offset = 0; // Set shaders - _context->VSSetShader(_vsShadowMap.Get(), nullptr, 0); - _context->PSSetShader(_psShadowMap.Get(), nullptr, 0); + BindShader(_sShadowMap); _context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset); _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); @@ -210,6 +209,12 @@ namespace TEN::Renderer for (int k = 0; k < obj.ObjectMeshes.size(); k++) { + if (item->MeshIds.size() <= k) + { + TENLog("Mesh structure was not properly initialized for object " + GetObjectName((GAME_OBJECT_ID)item->ObjectID)); + break; + } + auto* mesh = GetMesh(item->MeshIds[k]); for (auto& bucket : mesh->Buckets) @@ -253,6 +258,9 @@ namespace TEN::Renderer if (gunshell->counter <= 0) continue; + if (IgnoreReflectionPassForRoom(gunshell->roomNumber)) + continue; + objectID = gunshell->objectNumber; auto translation = Matrix::CreateTranslation(gunshell->pos.Position.ToVector3()); @@ -267,6 +275,7 @@ namespace TEN::Renderer auto prevWorldMatrix = prevRotMatrix * prevTranslation; worldMatrix = Matrix::Lerp(prevWorldMatrix, worldMatrix, GetInterpolationFactor()); + ReflectMatrixOptionally(worldMatrix); _stInstancedStaticMeshBuffer.StaticMeshes[gunShellCount].World = worldMatrix; _stInstancedStaticMeshBuffer.StaticMeshes[gunShellCount].Ambient = room.AmbientLight; @@ -281,8 +290,7 @@ namespace TEN::Renderer { auto& moveableObject = *_moveableObjects[objectID]; - _context->VSSetShader(_vsInstancedStaticMeshes.Get(), nullptr, 0); - _context->PSSetShader(_psInstancedStaticMeshes.Get(), nullptr, 0); + BindShader(_sInstancedStatics); unsigned int stride = sizeof(Vertex); unsigned int offset = 0; @@ -359,8 +367,7 @@ namespace TEN::Renderer SetDepthState(DepthState::Read); SetCullMode(CullMode::None); - _context->VSSetShader(_vsSolid.Get(), nullptr, 0); - _context->PSSetShader(_psSolid.Get(), nullptr, 0); + BindShader(_sSolid); auto worldMatrix = Matrix::CreateOrthographicOffCenter(0, _screenWidth, _screenHeight, 0, _viewport.MinDepth, _viewport.MaxDepth); _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); @@ -465,9 +472,7 @@ namespace TEN::Renderer void Renderer::DrawRats(RenderView& view, RendererPass rendererPass) { if (!Objects[ID_RATS_EMITTER].loaded) - { return; - } if (rendererPass == RendererPass::CollectTransparentFaces) { @@ -475,6 +480,9 @@ namespace TEN::Renderer { auto* rat = &Rats[i]; + if (IgnoreReflectionPassForRoom(rat->RoomNumber)) + continue; + if (rat->On) { RendererMesh* mesh = GetMesh(Objects[ID_RATS_EMITTER].meshIndex + (rand() % 8)); @@ -515,6 +523,9 @@ namespace TEN::Renderer { auto* rat = &Rats[i]; + if (IgnoreReflectionPassForRoom(rat->RoomNumber)) + continue; + if (rat->On) { activeRatsExist = true; @@ -526,13 +537,12 @@ namespace TEN::Renderer { if (rendererPass == RendererPass::GBuffer) { - _context->VSSetShader(_vsGBufferStatics.Get(), nullptr, 0); - _context->PSSetShader(_psGBuffer.Get(), nullptr, 0); + BindShader(_sGBuffer); + BindShader(_sGBufferStatics); } else { - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); } UINT stride = sizeof(Vertex); @@ -551,34 +561,31 @@ namespace TEN::Renderer if (rat->On) { - RendererMesh* mesh = GetMesh(Objects[ID_RATS_EMITTER].meshIndex + (rand() % 8)); + const auto& mesh = *GetMesh(Objects[ID_RATS_EMITTER].meshIndex + (rand() % 8)); - _stStatic.World = rat->Transform; + auto world = rat->Transform; + ReflectMatrixOptionally(world); + + _stStatic.World = world; _stStatic.Color = Vector4::One; _stStatic.AmbientLight = _rooms[rat->RoomNumber].AmbientLight; if (rendererPass != RendererPass::GBuffer) - { BindStaticLights(_rooms[rat->RoomNumber].LightsToDraw); - } _cbStatic.UpdateData(_stStatic, _context.Get()); - for (auto& bucket : mesh->Buckets) + for (const auto& bucket : mesh.Buckets) { if (bucket.NumVertices == 0) - { continue; - } int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1; for (int p = 0; p < passes; p++) { if (!SetupBlendModeAndAlphaTest(bucket.BlendMode, rendererPass, p)) - { continue; - } BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp); BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp); @@ -649,13 +656,12 @@ namespace TEN::Renderer { if (rendererPass == RendererPass::GBuffer) { - _context->VSSetShader(_vsGBufferStatics.Get(), nullptr, 0); - _context->PSSetShader(_psGBuffer.Get(), nullptr, 0); + BindShader(_sGBuffer); + BindShader(_sGBufferStatics); } else { - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); } unsigned int stride = sizeof(Vertex); @@ -722,6 +728,9 @@ namespace TEN::Renderer if (!bat.On) continue; + if (IgnoreReflectionPassForRoom(bat.RoomNumber)) + continue; + for (auto& bucket : mesh.Buckets) { if (!IsSortedBlendMode(bucket.BlendMode)) @@ -755,13 +764,19 @@ namespace TEN::Renderer { const auto& bat = Bats[i]; + if (IgnoreReflectionPassForRoom(bat.RoomNumber)) + continue; + if (bat.On) { auto& room = _rooms[bat.RoomNumber]; auto transformMatrix = Matrix::Lerp(bat.PrevTransform, bat.Transform, GetInterpolationFactor()); - _stInstancedStaticMeshBuffer.StaticMeshes[batCount].World = transformMatrix; + auto world = transformMatrix; + ReflectMatrixOptionally(world); + + _stInstancedStaticMeshBuffer.StaticMeshes[batCount].World = world; _stInstancedStaticMeshBuffer.StaticMeshes[batCount].Ambient = room.AmbientLight; _stInstancedStaticMeshBuffer.StaticMeshes[batCount].Color = Vector4::One; _stInstancedStaticMeshBuffer.StaticMeshes[batCount].LightMode = (int)mesh.LightMode; @@ -777,13 +792,12 @@ namespace TEN::Renderer { if (rendererPass == RendererPass::GBuffer) { - _context->VSSetShader(_vsGBufferInstancedStatics.Get(), nullptr, 0); - _context->PSSetShader(_psGBuffer.Get(), nullptr, 0); + BindShader(_sGBuffer); + BindShader(_sGBufferInstancedStatics); } else { - _context->VSSetShader(_vsInstancedStaticMeshes.Get(), nullptr, 0); - _context->PSSetShader(_psInstancedStaticMeshes.Get(), nullptr, 0); + BindShader(_sInstancedStatics); } unsigned int stride = sizeof(Vertex); @@ -834,6 +848,9 @@ namespace TEN::Renderer if (!beetle.On) continue; + if (IgnoreReflectionPassForRoom(beetle.RoomNumber)) + continue; + auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, GetInterpolationFactor()); for (auto& bucket : mesh.Buckets) @@ -867,42 +884,47 @@ namespace TEN::Renderer for (int i = 0; i < TEN::Entities::TR4::NUM_BEETLES; i++) { const auto& beetle = TEN::Entities::TR4::BeetleSwarm[i]; + + if (!beetle.On) + continue; - if (beetle.On) + if (IgnoreReflectionPassForRoom(beetle.RoomNumber)) + continue; + + auto& room = _rooms[beetle.RoomNumber]; + + auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, GetInterpolationFactor()); + + auto world = transformMatrix; + ReflectMatrixOptionally(world); + + _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].World = world; + _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Ambient = room.AmbientLight; + _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Color = Vector4::One; + _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].LightMode = (int)mesh.LightMode; + + if (rendererPass != RendererPass::GBuffer) { - auto& room = _rooms[beetle.RoomNumber]; - - auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, GetInterpolationFactor()); - - _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].World = transformMatrix; - _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Ambient = room.AmbientLight; - _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Color = Vector4::One; - _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].LightMode = (int)mesh.LightMode; - - if (rendererPass != RendererPass::GBuffer) - { - auto lights = std::vector{}; - for (int i = 0; i < std::min((int)room.LightsToDraw.size(), MAX_LIGHTS_PER_ITEM); i++) - lights.push_back(room.LightsToDraw[i]); + auto lights = std::vector{}; + for (int i = 0; i < std::min((int)room.LightsToDraw.size(), MAX_LIGHTS_PER_ITEM); i++) + lights.push_back(room.LightsToDraw[i]); - BindInstancedStaticLights(lights, beetleCount); - } - - beetleCount++; + BindInstancedStaticLights(lights, beetleCount); } + beetleCount++; + if (beetleCount == INSTANCED_STATIC_MESH_BUCKET_SIZE || (i == TEN::Entities::TR4::NUM_BEETLES - 1 && beetleCount > 0)) { if (rendererPass == RendererPass::GBuffer) { - _context->VSSetShader(_vsGBufferInstancedStatics.Get(), nullptr, 0); - _context->PSSetShader(_psGBuffer.Get(), nullptr, 0); + BindShader(_sGBuffer); + BindShader(_sGBufferInstancedStatics); } else { - _context->VSSetShader(_vsInstancedStaticMeshes.Get(), nullptr, 0); - _context->PSSetShader(_psInstancedStaticMeshes.Get(), nullptr, 0); + BindShader(_sInstancedStatics); } unsigned int stride = sizeof(Vertex); @@ -954,6 +976,9 @@ namespace TEN::Renderer if (!locust.on) continue; + if (IgnoreReflectionPassForRoom(locust.roomNumber)) + continue; + auto& mesh = *GetMesh(Objects[ID_LOCUSTS].meshIndex + (-locust.counter & 3)); for (auto& bucket : mesh.Buckets) @@ -990,6 +1015,9 @@ namespace TEN::Renderer if (!locust.on) continue; + if (IgnoreReflectionPassForRoom(locust.roomNumber)) + continue; + activeLocustsExist = true; break; } @@ -998,13 +1026,12 @@ namespace TEN::Renderer { if (rendererPass == RendererPass::GBuffer) { - _context->VSSetShader(_vsGBufferStatics.Get(), nullptr, 0); - _context->PSSetShader(_psGBuffer.Get(), nullptr, 0); + BindShader(_sGBuffer); + BindShader(_sGBufferStatics); } else { - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); } unsigned int stride = sizeof(Vertex); @@ -1025,7 +1052,10 @@ namespace TEN::Renderer auto& mesh = *GetMesh(Objects[ID_LOCUSTS].meshIndex + (-locust.counter & 3)); - _stStatic.World = Matrix::Lerp(locust.PrevTransform, locust.Transform, GetInterpolationFactor()); + auto world = Matrix::Lerp(locust.PrevTransform, locust.Transform, GetInterpolationFactor()); + ReflectMatrixOptionally(world); + + _stStatic.World = world; _stStatic.Color = Vector4::One; _stStatic.AmbientLight = _rooms[locust.roomNumber].AmbientLight; _cbStatic.UpdateData(_stStatic, _context.Get()); @@ -1059,8 +1089,7 @@ namespace TEN::Renderer SetBlendMode(BlendMode::Additive); SetCullMode(CullMode::None); - _context->VSSetShader(_vsSolid.Get(), nullptr, 0); - _context->PSSetShader(_psSolid.Get(), nullptr, 0); + BindShader(_sSolid); _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); @@ -1093,8 +1122,7 @@ namespace TEN::Renderer SetBlendMode(BlendMode::Additive); SetCullMode(CullMode::None); - _context->VSSetShader(_vsSolid.Get(), nullptr, 0); - _context->PSSetShader(_psSolid.Get(), nullptr, 0); + BindShader(_sSolid); _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetInputLayout(_inputLayout.Get()); @@ -1529,7 +1557,7 @@ namespace TEN::Renderer RendererLight dynamicLight = {}; - dynamicLight.Color = Vector3(color.x, color.y, color.z) * 2.0f; + dynamicLight.Color = Vector3(color.x, color.y, color.z); if (falloff < 8) dynamicLight.Color *= (falloff / 8.0f); @@ -1556,8 +1584,7 @@ namespace TEN::Renderer dynamicLight.Luma = Luma(dynamicLight.Color); dynamicLight.Hash = hash; - StoreInterpolatedDynamicLightData(dynamicLight); - _dynamicLights[_dynamicLightList].push_back(dynamicLight); + PrepareDynamicLight(dynamicLight); } void Renderer::AddDynamicPointLight(const Vector3& pos, float radius, const Color& color, bool castShadows, int hash) @@ -1565,9 +1592,9 @@ namespace TEN::Renderer if (_isLocked || g_GameFlow->LastFreezeMode != FreezeMode::None) return; - RendererLight dynamicLight = {}; + auto dynamicLight = RendererLight{}; - dynamicLight.Color = Vector3(color.x, color.y, color.z) * 2.0f; + dynamicLight.Color = Vector3(color.x, color.y, color.z); if (radius < BLOCK(2)) dynamicLight.Color *= (radius / BLOCK(2)); @@ -1582,33 +1609,55 @@ namespace TEN::Renderer dynamicLight.Luma = Luma(dynamicLight.Color); dynamicLight.Hash = hash; - StoreInterpolatedDynamicLightData(dynamicLight); - _dynamicLights[_dynamicLightList].push_back(dynamicLight); + PrepareDynamicLight(dynamicLight); } - void Renderer::StoreInterpolatedDynamicLightData(RendererLight& light) + void Renderer::PrepareDynamicLight(RendererLight& light) { - // Hash is not provided, do not search for same light in old buffer. - if (light.Hash == 0) - return; + // If hash is provided, search for same light in previous buffer. + if (light.Hash != 0) + { + // Determine previous buffer index. + const auto& prevList = _dynamicLights[1 - _dynamicLightList]; - // Determine the previous buffer index. - const auto& previousList = _dynamicLights[1 - _dynamicLightList]; + // Find light in previous buffer with same hash. + auto it = std::find_if( + prevList.begin(), prevList.end(), + [&light](const auto& prevLight) + { + return (prevLight.Hash == light.Hash); + }); - // Find a light in the previous buffer with the same Hash. - auto it = std::find_if(previousList.begin(), previousList.end(), - [&light](const auto& prevLight) + if (it != prevList.end()) { - return prevLight.Hash == light.Hash; - }); + // If matching light is found, copy it. + const auto& prevLight = *it; + light.PrevPosition = prevLight.Position; + light.PrevDirection = prevLight.Direction; + } + } - if (it == previousList.end()) - return; + // Queue dynamic light. + _dynamicLights[_dynamicLightList].push_back(light); - // If a matching light is found, copy its data. - const auto& prevLight = *it; - light.PrevPosition = prevLight.Position; - light.PrevDirection = prevLight.Direction; + // Check if light is spawned in mirrored room and create reflection. + for (const auto& mirror : g_Level.Mirrors) + { + if (!mirror.ReflectLights) + continue; + + // TODO: Avoid LaraItem global. + if ((Camera.pos.RoomNumber == mirror.RoomNumber || LaraItem->RoomNumber == mirror.RoomNumber) && + IsPointInRoom(light.Position, mirror.RoomNumber)) + { + auto reflectedLight = light; + reflectedLight.Position = Vector3::Transform(light.Position, mirror.ReflectionMatrix); + reflectedLight.Direction = Vector3::Transform(light.Direction, mirror.ReflectionMatrix); + reflectedLight.Hash = 0; + + _dynamicLights[_dynamicLightList].push_back(reflectedLight); + } + } } void Renderer::PrepareScene() @@ -1661,12 +1710,11 @@ namespace TEN::Renderer ResetDebugVariables(); - _doingFullscreenPass = false; - auto& level = *g_GameFlow->GetLevel(CurrentLevel); // Prepare scene to draw. auto time1 = std::chrono::high_resolution_clock::now(); + CollectMirrors(view); CollectRooms(view, false); auto time = std::chrono::high_resolution_clock::now(); _timeRoomsCollector = (std::chrono::duration_cast(time - time1)).count() / 1000000; @@ -1773,7 +1821,7 @@ namespace TEN::Renderer cameraConstantBuffer.FogMinDistance = level.GetFogMinDistance(); cameraConstantBuffer.FogMaxDistance = level.GetFogMaxDistance(); } - else + else { cameraConstantBuffer.FogMaxDistance = 0; cameraConstantBuffer.FogColor = Vector4::Zero; @@ -1792,10 +1840,10 @@ namespace TEN::Renderer cameraConstantBuffer.FogBulbs[i].Color = view.FogBulbsToDraw[i].Color; cameraConstantBuffer.FogBulbs[i].SquaredCameraToFogBulbDistance = SQUARE(view.FogBulbsToDraw[i].Distance); cameraConstantBuffer.FogBulbs[i].FogBulbToCameraVector = view.FogBulbsToDraw[i].FogBulbToCameraVector; - } + } _cbCameraMatrices.UpdateData(cameraConstantBuffer, _context.Get()); - + ID3D11RenderTargetView* pRenderViewPtrs[2]; // Bind main render target. @@ -1803,7 +1851,7 @@ namespace TEN::Renderer // Draw horizon and sky. DrawHorizonAndSky(view, _renderTarget.DepthStencilView.Get()); - + // Build G-Buffer (normals + depth). _context->ClearRenderTargetView(_normalsRenderTarget.RenderTargetView.Get(), Colors::Black); _context->ClearRenderTargetView(_depthRenderTarget.RenderTargetView.Get(), Colors::White); @@ -1812,85 +1860,30 @@ namespace TEN::Renderer pRenderViewPtrs[1] = _depthRenderTarget.RenderTargetView.Get(); _context->OMSetRenderTargets(2, &pRenderViewPtrs[0], _renderTarget.DepthStencilView.Get()); - DrawRooms(view, RendererPass::GBuffer); - DrawItems(view, RendererPass::GBuffer); - DrawStatics(view, RendererPass::GBuffer); - DrawSpiders(view, RendererPass::GBuffer); - DrawScarabs(view, RendererPass::GBuffer); - DrawGunShells(view, RendererPass::GBuffer); - DrawBats(view, RendererPass::GBuffer); - DrawEffects(view, RendererPass::GBuffer); - DrawRats(view, RendererPass::GBuffer); - DrawLocusts(view, RendererPass::GBuffer); + // Render G-Buffer pass. + DoRenderPass(RendererPass::GBuffer, view, true); // Calculate ambient occlusion. if (g_Configuration.EnableAmbientOcclusion) - { - _doingFullscreenPass = true; CalculateSSAO(view); - _doingFullscreenPass = false; - } _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetInputLayout(_inputLayout.Get()); - SetBlendMode(BlendMode::Opaque); - SetCullMode(CullMode::CounterClockwise); - SetDepthState(DepthState::Write); - _context->RSSetViewports(1, &view.Viewport); ResetScissor(); - + // Bind main render target again. Main depth buffer is already filled and avoids overdraw in following steps. _context->OMSetRenderTargets(1, _renderTarget.RenderTargetView.GetAddressOf(), _renderTarget.DepthStencilView.Get()); + + DoRenderPass(RendererPass::Opaque, view, true); + DoRenderPass(RendererPass::Additive, view, true); + DoRenderPass(RendererPass::CollectTransparentFaces, view, false); + SortTransparentFaces(view); + + DoRenderPass(RendererPass::Transparent, view, true); + DoRenderPass(RendererPass::GunFlashes, view, true); // HACK: Gunflashes are drawn after everything because they are near camera. - // Draw opaque, alpha test, and fast alpha blend faces. - DrawRooms(view, RendererPass::Opaque); - DrawItems(view, RendererPass::Opaque); - DrawStatics(view, RendererPass::Opaque); - DrawSpiders(view, RendererPass::Opaque); - DrawScarabs(view, RendererPass::Opaque); - DrawGunShells(view, RendererPass::Opaque); - DrawBats(view, RendererPass::Opaque); - DrawEffects(view, RendererPass::Opaque); - DrawRats(view, RendererPass::Opaque); - DrawLocusts(view, RendererPass::Opaque); - DrawDebris(view, RendererPass::Opaque); - DrawSprites(view, RendererPass::Opaque); - DrawFishSwarm(view, RendererPass::Opaque); - - // Draw additive faces. - DrawRooms(view, RendererPass::Additive); - DrawItems(view, RendererPass::Additive); - DrawStatics(view, RendererPass::Additive); - DrawSpiders(view, RendererPass::Additive); - DrawScarabs(view, RendererPass::Additive); - DrawBats(view, RendererPass::Additive); - DrawEffects(view, RendererPass::Additive); - DrawRats(view, RendererPass::Additive); - DrawLocusts(view, RendererPass::Additive); - DrawDebris(view, RendererPass::Additive); - DrawSprites(view, RendererPass::Additive); - DrawFishSwarm(view, RendererPass::Additive); - - // Collect all non-commutative transparent faces. - // NOTE: Sorted sprites already collected at beginning of frame. - DrawRooms(view, RendererPass::CollectTransparentFaces); - DrawItems(view, RendererPass::CollectTransparentFaces); - DrawStatics(view, RendererPass::CollectTransparentFaces); - DrawBats(view, RendererPass::CollectTransparentFaces); - DrawEffects(view, RendererPass::CollectTransparentFaces); - DrawRats(view, RendererPass::CollectTransparentFaces); - DrawLocusts(view, RendererPass::CollectTransparentFaces); - DrawFishSwarm(view, RendererPass::CollectTransparentFaces); - - // Draw sorted faces. - DrawSortedFaces(view); - - // HACK: Gunflashes drawn after everything because they are very near the camera. - DrawGunFlashes(view); - DrawBaddyGunflashes(view); - // Draw 3D debug lines and triangles. DrawLines3D(view); DrawTriangles3D(view); @@ -1906,33 +1899,8 @@ namespace TEN::Renderer if (renderMode != SceneRenderMode::NoPostprocess) { - _doingFullscreenPass = true; - - // Apply antialiasing. - switch (g_Configuration.AntialiasingMode) - { - case AntialiasingMode::None: - break; - - case AntialiasingMode::Low: - ApplyFXAA(&_renderTarget, view); - break; - - case AntialiasingMode::Medium: - case AntialiasingMode::High: - ApplySMAA(&_renderTarget, view); - break; - } - - // Draw post-process effects (cinematic bars, fade, flash, HDR, tone mapping, etc.). DrawPostprocess(renderTarget, view, renderMode); - - _doingFullscreenPass = false; - - // Draw binoculars or lasersight overlay. DrawOverlays(view); - - // Draw 2D debug lines. DrawLines2D(); } @@ -1960,7 +1928,7 @@ namespace TEN::Renderer SetBlendMode(BlendMode::Opaque); SetCullMode(CullMode::CounterClockwise); - _context->PSSetShader(_psRoomAmbient.Get(), nullptr, 0); + BindShader(_sRoomAmbient); // Bind and clear render target _context->ClearRenderTargetView(renderTarget->RenderTargetView.Get(), Colors::Black); @@ -2010,7 +1978,7 @@ namespace TEN::Renderer if (levelPtr->Horizon) { - _context->VSSetShader(_vsRoomAmbientSky.Get(), nullptr, 0); + BindShader(_sRoomAmbientSky); if (Lara.Control.Look.OpticRange != 0) AlterFOV(ANGLE(DEFAULT_FOV) - Lara.Control.Look.OpticRange, false); @@ -2092,7 +2060,7 @@ namespace TEN::Renderer _context->ClearDepthStencilView(renderTarget->DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); } - _context->VSSetShader(_vsRoomAmbient.Get(), nullptr, 0); + BindShader(_sRoomAmbient); // Draw rooms UINT stride = sizeof(Vertex); @@ -2213,7 +2181,82 @@ namespace TEN::Renderer RenderScene(&_dumpScreenRenderTarget, _gameCamera, renderMode); } - void Renderer::DrawItems(RenderView& view, RendererPass rendererPass) + void Renderer::DoRenderPass(RendererPass pass, RenderView& view, bool drawMirrors) + { + // Reset GPU state. + SetBlendMode(BlendMode::Opaque); + SetCullMode(CullMode::CounterClockwise); + SetDepthState(DepthState::Write); + + // Draw room geometry first if applicable for a given pass. + if (pass != RendererPass::Transparent && pass != RendererPass::GunFlashes) + DrawRooms(view, pass); + + // Draw all objects. + DrawObjects(pass, view, true, true, true, true); + + // If mirrors are in view, render mirrored objects for every mirror. + if (drawMirrors && !view.Mirrors.empty()) + { + SetCullMode(CullMode::Clockwise); + for (auto& mirror : view.Mirrors) + { + _currentMirror = &mirror; + DrawObjects(pass, view, mirror.ReflectPlayer, mirror.ReflectMoveables, mirror.ReflectStatics, mirror.ReflectSprites); + _currentMirror = nullptr; + } + + SetCullMode(CullMode::CounterClockwise); + } + } + + void Renderer::DrawObjects(RendererPass pass, RenderView& view, bool player, bool moveables, bool statics, bool sprites) + { + switch (pass) + { + case RendererPass::Transparent: + DrawSortedFaces(view); + break; + + case RendererPass::GunFlashes: + DrawGunFlashes(view); + DrawBaddyGunflashes(view); + break; + + default: + if (moveables) + { + DrawItems(view, pass); + DrawEffects(view, pass); + DrawGunShells(view, pass); + DrawSpiders(view, pass); + DrawScarabs(view, pass); + DrawBats(view, pass); + DrawRats(view, pass); + DrawLocusts(view, pass); + DrawFishSwarm(view, pass); + } + else if (player) + { + DrawItems(view, pass, true); + DrawGunShells(view, pass); + } + + if (statics) + { + DrawStatics(view, pass); + DrawDebris(view, pass); // Debris mostly originate from shatter statics. + } + + // Sorted sprites already collected at beginning of frame. + if (sprites && pass != RendererPass::CollectTransparentFaces) + DrawSprites(view, pass); + + break; + } + } + + void Renderer::DrawItems(RenderView& view, RendererPass rendererPass, bool onlyPlayer) { unsigned int stride = sizeof(Vertex); unsigned int offset = 0; @@ -2224,26 +2267,34 @@ namespace TEN::Renderer // Set shaders. if (rendererPass == RendererPass::GBuffer) { - _context->VSSetShader(_vsGBufferItems.Get(), nullptr, 0); - _context->PSSetShader(_psGBuffer.Get(), nullptr, 0); + BindShader(_sGBuffer); + BindShader(_sGBufferItems); } else { - _context->VSSetShader(_vsItems.Get(), nullptr, 0); - _context->PSSetShader(_psItems.Get(), nullptr, 0); + BindShader(_sItems); } BindRenderTargetAsTexture(TextureRegister::SSAO, &_SSAOBlurredRenderTarget, SamplerStateRegister::PointWrap); for (auto room : view.RoomsToDraw) { + if (IgnoreReflectionPassForRoom(room->RoomNumber)) + continue; + for (auto itemToDraw : room->ItemsToDraw) { + if (_currentMirror != nullptr && (g_Level.Items[itemToDraw->ItemNumber].Flags & IFLAG_CLEAR_BODY)) + continue; + + if (onlyPlayer && itemToDraw->ObjectID != ID_LARA) + continue; + switch (itemToDraw->ObjectID) { case ID_LARA: DrawLara(view, rendererPass); - break; + continue; case ID_WATERFALL1: case ID_WATERFALL2: @@ -2258,7 +2309,7 @@ namespace TEN::Renderer default: DrawAnimatingItem(itemToDraw, view, rendererPass); - break; + continue; } } } @@ -2327,6 +2378,8 @@ namespace TEN::Renderer // Bind item main properties _stItem.World = item->InterpolatedWorld; + ReflectMatrixOptionally(_stItem.World); + _stItem.Color = item->Color; _stItem.AmbientLight = item->AmbientLight; memcpy(_stItem.BonesMatrices, item->InterpolatedAnimTransforms, sizeof(Matrix) * MAX_BONES); @@ -2334,7 +2387,8 @@ namespace TEN::Renderer for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++) _stItem.BoneLightModes[k] = (int)moveableObj.ObjectMeshes[k]->LightMode; - BindMoveableLights(item->LightsToDraw, item->RoomNumber, item->PrevRoomNumber, item->LightFade); + bool acceptsShadows = moveableObj.ShadowType == ShadowMode::None; + BindMoveableLights(item->LightsToDraw, item->RoomNumber, item->PrevRoomNumber, item->LightFade, acceptsShadows); _cbItem.UpdateData(_stItem, _context.Get()); for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++) @@ -2349,22 +2403,19 @@ namespace TEN::Renderer void Renderer::DrawStatics(RenderView& view, RendererPass rendererPass) { if (_staticTextures.size() == 0 || view.SortedStaticsToDraw.size() == 0) - { return; - } if (rendererPass != RendererPass::CollectTransparentFaces) { #ifdef DISABLE_INSTANCING if (rendererPass == RendererPass::GBuffer) { - _context->VSSetShader(_vsGBufferStatics.Get(), NULL, 0); - _context->PSSetShader(_psGBuffer.Get(), NULL, 0); + BindShader(_sGBuffer); + BindShader(_sGBufferStatics); } else { - _context->VSSetShader(_vsStatics.Get(), NULL, 0); - _context->PSSetShader(_psStatics.Get(), NULL, 0); + BindShader(_sStatics); } // Bind vertex and index buffer @@ -2393,6 +2444,9 @@ namespace TEN::Renderer RendererStatic* current = statics[s]; RendererRoom* room = &_rooms[current->RoomNumber]; + if (IgnoreReflectionPassForRoom(current->RoomNumber)) + continue; + _stStatic.World = current->World; _stStatic.Color = current->Color; _stStatic.AmbientLight = room->AmbientLight; @@ -2437,13 +2491,12 @@ namespace TEN::Renderer #else if (rendererPass == RendererPass::GBuffer) { - _context->VSSetShader(_vsGBufferInstancedStatics.Get(), NULL, 0); - _context->PSSetShader(_psGBuffer.Get(), NULL, 0); + BindShader(_sGBuffer); + BindShader(_sGBufferInstancedStatics); } else { - _context->VSSetShader(_vsInstancedStaticMeshes.Get(), NULL, 0); - _context->PSSetShader(_psInstancedStaticMeshes.Get(), NULL, 0); + BindShader(_sInstancedStatics); } // Bind vertex and index buffer @@ -2456,14 +2509,14 @@ namespace TEN::Renderer for (auto it = view.SortedStaticsToDraw.begin(); it != view.SortedStaticsToDraw.end(); it++) { - std::vector statics = it->second; + auto statics = it->second; - RendererStatic* refStatic = statics[0]; - RendererObject& refStaticObj = GetStaticRendererObject(refStatic->ObjectNumber); + auto* refStatic = statics[0]; + auto& refStaticObj = GetStaticRendererObject(refStatic->ObjectNumber); if (refStaticObj.ObjectMeshes.size() == 0) continue; - RendererMesh* refMesh = refStaticObj.ObjectMeshes[0]; + auto* refMesh = refStaticObj.ObjectMeshes[0]; int staticsCount = (int)statics.size(); int bucketSize = INSTANCED_STATIC_MESH_BUCKET_SIZE; @@ -2471,57 +2524,58 @@ namespace TEN::Renderer while (baseStaticIndex < staticsCount) { - int k = 0; - int instanceCount = std::min(bucketSize, staticsCount - baseStaticIndex); + int instancesCount = 0; int max = std::min(baseStaticIndex + bucketSize, staticsCount); for (int s = baseStaticIndex; s < max; s++) { - RendererStatic* current = statics[s]; - RendererRoom* room = &_rooms[current->RoomNumber]; + auto* current = statics[s]; + auto* room = &_rooms[current->RoomNumber]; - _stInstancedStaticMeshBuffer.StaticMeshes[k].World = current->World; - _stInstancedStaticMeshBuffer.StaticMeshes[k].Color = current->Color; - _stInstancedStaticMeshBuffer.StaticMeshes[k].Ambient = room->AmbientLight; - _stInstancedStaticMeshBuffer.StaticMeshes[k].LightMode = (int)refMesh->LightMode; + if (IgnoreReflectionPassForRoom(current->RoomNumber)) + continue; + + auto world = current->World; + ReflectMatrixOptionally(world); + + _stInstancedStaticMeshBuffer.StaticMeshes[instancesCount].World = world; + _stInstancedStaticMeshBuffer.StaticMeshes[instancesCount].Color = current->Color; + _stInstancedStaticMeshBuffer.StaticMeshes[instancesCount].Ambient = room->AmbientLight; + _stInstancedStaticMeshBuffer.StaticMeshes[instancesCount].LightMode = (int)refMesh->LightMode; if (rendererPass != RendererPass::GBuffer) - { - BindInstancedStaticLights(current->LightsToDraw, k); - } + BindInstancedStaticLights(current->LightsToDraw, instancesCount); - k++; + instancesCount++; } - _cbInstancedStaticMeshBuffer.UpdateData(_stInstancedStaticMeshBuffer, _context.Get()); - baseStaticIndex += bucketSize; - for (auto& bucket : refMesh->Buckets) + if (instancesCount > 0) { - if (bucket.NumVertices == 0) - { - continue; - } + _cbInstancedStaticMeshBuffer.UpdateData(_stInstancedStaticMeshBuffer, _context.Get()); - int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1; - - for (int p = 0; p < passes; p++) + for (const auto& bucket : refMesh->Buckets) { - if (!SetupBlendModeAndAlphaTest(bucket.BlendMode, rendererPass, p)) - { + if (bucket.NumVertices == 0) continue; + + int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1; + for (int p = 0; p < passes; p++) + { + if (!SetupBlendModeAndAlphaTest(bucket.BlendMode, rendererPass, p)) + continue; + + BindTexture(TextureRegister::ColorMap, + &std::get<0>(_staticTextures[bucket.Texture]), + SamplerStateRegister::AnisotropicClamp); + BindTexture(TextureRegister::NormalMap, + &std::get<1>(_staticTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp); + + DrawIndexedInstancedTriangles(bucket.NumIndices, instancesCount, bucket.StartIndex, 0); + + _numInstancedStaticsDrawCalls++; } - - BindTexture(TextureRegister::ColorMap, - &std::get<0>(_staticTextures[bucket.Texture]), - SamplerStateRegister::AnisotropicClamp); - BindTexture(TextureRegister::NormalMap, - &std::get<1>(_staticTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp); - - DrawIndexedInstancedTriangles(bucket.NumIndices, instanceCount, bucket.StartIndex, 0); - - _numInstancedStaticsDrawCalls++; } } } @@ -2530,18 +2584,17 @@ namespace TEN::Renderer } else { - // Collect sorted blend modes faces ordered by room, if transparent pass - + // Collect sorted blend modes faces ordered by room if doing transparent pass. for (auto it = view.SortedStaticsToDraw.begin(); it != view.SortedStaticsToDraw.end(); it++) { - std::vector statics = it->second; + auto statics = it->second; - RendererStatic* refStatic = statics[0]; - RendererObject& refStaticObj = GetStaticRendererObject(refStatic->ObjectNumber); + auto* refStatic = statics[0]; + auto& refStaticObj = GetStaticRendererObject(refStatic->ObjectNumber); if (refStaticObj.ObjectMeshes.size() == 0) continue; - RendererMesh* refMesh = refStaticObj.ObjectMeshes[0]; + auto* refMesh = refStaticObj.ObjectMeshes[0]; for (int i = 0; i < statics.size(); i++) { @@ -2550,15 +2603,13 @@ namespace TEN::Renderer auto& bucket = refMesh->Buckets[j]; if (bucket.NumVertices == 0) - { continue; - } if (IsSortedBlendMode(bucket.BlendMode)) { for (int p = 0; p < bucket.Polygons.size(); p++) { - RendererSortableObject object; + auto object = RendererSortableObject{}; object.ObjectType = RendererObjectType::Static; object.Bucket = &bucket; @@ -2625,11 +2676,11 @@ namespace TEN::Renderer { if (rendererPass == RendererPass::GBuffer) { - _context->PSSetShader(_psGBuffer.Get(), nullptr, 0); + BindShader(_sGBuffer); } else { - _context->PSSetShader(_psRooms.Get(), nullptr, 0); + BindShader(_sRooms); } UINT stride = sizeof(Vertex); @@ -2693,11 +2744,11 @@ namespace TEN::Renderer { if (rendererPass != RendererPass::GBuffer) { - _context->VSSetShader((animated == 0) ? _vsRooms.Get() : _vsRoomsAnimatedTextures.Get(), nullptr, 0); + if (animated == 0) BindShader(_sRooms); else BindShader(_sRoomsAnimated); } else { - _context->VSSetShader((animated == 0) ? _vsGBufferRooms.Get() : _vsGBufferRoomsAnimated.Get(), nullptr, 0); + if (animated == 0) BindShader(_sGBufferRooms); else BindShader(_sGBufferRoomsAnimated); } for (const auto& bucket : room.Buckets) @@ -2796,9 +2847,7 @@ namespace TEN::Renderer // Draw sky. auto rotation = Matrix::CreateRotationX(PI); - _context->VSSetShader(_vsSky.Get(), nullptr, 0); - _context->PSSetShader(_psSky.Get(), nullptr, 0); - + BindShader(_sSky); BindTexture(TextureRegister::ColorMap, &_skyTexture, SamplerStateRegister::AnisotropicClamp); _context->IASetVertexBuffers(0, 1, _skyVertexBuffer.Buffer.GetAddressOf(), &stride, &offset); @@ -2839,8 +2888,7 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - _context->VSSetShader(_vsInstancedSprites.Get(), nullptr, 0); - _context->PSSetShader(_psInstancedSprites.Get(), nullptr, 0); + BindShader(_sInstancedSprites); // Set up vertex buffer and parameters. UINT stride = sizeof(Vertex); @@ -2979,8 +3027,7 @@ namespace TEN::Renderer _context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset); _context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0); - _context->VSSetShader(_vsSky.Get(), nullptr, 0); - _context->PSSetShader(_psSky.Get(), nullptr, 0); + BindShader(_sSky); auto& moveableObj = *_moveableObjects[ID_HORIZON]; @@ -3024,8 +3071,7 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - _context->VSSetShader(_vsInstancedSprites.Get(), nullptr, 0); - _context->PSSetShader(_psInstancedSprites.Get(), nullptr, 0); + BindShader(_sInstancedSprites); // Set up vertex buffer and parameters. unsigned int stride = sizeof(Vertex); @@ -3070,11 +3116,6 @@ namespace TEN::Renderer // Clear just the Z-buffer to start drawing on top of horizon. _context->ClearDepthStencilView(depthTarget, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); - - // Reset the GPU state - SetDepthState(DepthState::Write); - SetBlendMode(BlendMode::Opaque); - SetCullMode(CullMode::CounterClockwise); } void Renderer::Render(float interpFactor) @@ -3231,7 +3272,7 @@ namespace TEN::Renderer return true; } - void Renderer::DrawSortedFaces(RenderView& view) + void Renderer::SortTransparentFaces(RenderView& view) { std::sort( view.TransparentObjectsToDraw.begin(), @@ -3241,15 +3282,21 @@ namespace TEN::Renderer return (a.Distance > b.Distance); } ); + } + void Renderer::DrawSortedFaces(RenderView& view) + { for (int i = 0; i < view.TransparentObjectsToDraw.size(); i++) { - RendererSortableObject* object = &view.TransparentObjectsToDraw[i]; - RendererObjectType lastObjectType = (i > 0 ? view.TransparentObjectsToDraw[i - 1].ObjectType : RendererObjectType::Unknown); + auto* object = &view.TransparentObjectsToDraw[i]; + auto lastObjectType = (i > 0 ? view.TransparentObjectsToDraw[i - 1].ObjectType : RendererObjectType::Unknown); _sortedPolygonsVertices.clear(); _sortedPolygonsIndices.clear(); + if (_currentMirror != nullptr && object->ObjectType == RendererObjectType::Room) + continue; + if (object->ObjectType == RendererObjectType::Room) { while (i < view.TransparentObjectsToDraw.size() && @@ -3259,7 +3306,7 @@ namespace TEN::Renderer view.TransparentObjectsToDraw[i].Bucket->BlendMode == object->Bucket->BlendMode && _sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES) { - RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i]; + auto* currentObject = &view.TransparentObjectsToDraw[i]; _sortedPolygonsIndices.bulk_push_back( _roomsIndices.data(), currentObject->Polygon->BaseIndex, @@ -3270,9 +3317,7 @@ namespace TEN::Renderer DrawRoomSorted(object, lastObjectType, view); if (i == view.TransparentObjectsToDraw.size()) - { return; - } i--; } @@ -3285,7 +3330,7 @@ namespace TEN::Renderer view.TransparentObjectsToDraw[i].Bucket->BlendMode == object->Bucket->BlendMode && _sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES) { - RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i]; + auto* currentObject = &view.TransparentObjectsToDraw[i]; _sortedPolygonsIndices.bulk_push_back( _moveablesIndices.data(), currentObject->Polygon->BaseIndex, @@ -3296,9 +3341,7 @@ namespace TEN::Renderer DrawItemSorted(object, lastObjectType, view); if (i == view.TransparentObjectsToDraw.size()) - { return; - } i--; } @@ -3312,7 +3355,7 @@ namespace TEN::Renderer view.TransparentObjectsToDraw[i].Bucket->BlendMode == object->Bucket->BlendMode && _sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES) { - RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i]; + auto* currentObject = &view.TransparentObjectsToDraw[i]; _sortedPolygonsIndices.bulk_push_back( _staticsIndices.data(), currentObject->Polygon->BaseIndex, @@ -3323,9 +3366,7 @@ namespace TEN::Renderer DrawStaticSorted(object, lastObjectType, view); if (i == view.TransparentObjectsToDraw.size()) - { return; - } i--; } @@ -3338,7 +3379,7 @@ namespace TEN::Renderer view.TransparentObjectsToDraw[i].Bucket->BlendMode == object->Bucket->BlendMode && _sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES) { - RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i]; + auto* currentObject = &view.TransparentObjectsToDraw[i]; _sortedPolygonsIndices.bulk_push_back( _staticsIndices.data(), currentObject->Polygon->BaseIndex, @@ -3349,9 +3390,7 @@ namespace TEN::Renderer DrawMoveableAsStaticSorted(object, lastObjectType, view); if (i == view.TransparentObjectsToDraw.size()) - { return; - } i--; } @@ -3400,23 +3439,25 @@ namespace TEN::Renderer uv2 = spr->Sprite->UV[2]; uv3 = spr->Sprite->UV[3]; + auto world = GetWorldMatrixForSprite(currentObject->Sprite, view); + Vertex v0; - v0.Position = Vector3::Transform(p0t, currentObject->World); + v0.Position = Vector3::Transform(p0t, world); v0.UV = uv0; v0.Color = spr->c1; Vertex v1; - v1.Position = Vector3::Transform(p1t, currentObject->World); + v1.Position = Vector3::Transform(p1t, world); v1.UV = uv1; v1.Color = spr->c2; Vertex v2; - v2.Position = Vector3::Transform(p2t, currentObject->World); + v2.Position = Vector3::Transform(p2t, world); v2.UV = uv2; v2.Color = spr->c3; Vertex v3; - v3.Position = Vector3::Transform(p3t, currentObject->World); + v3.Position = Vector3::Transform(p3t, world); v3.UV = uv3; v3.Color = spr->c4; @@ -3449,7 +3490,7 @@ namespace TEN::Renderer ROOM_INFO* nativeRoom = &g_Level.Rooms[objectInfo->Room->RoomNumber]; - _context->PSSetShader(_psRooms.Get(), nullptr, 0); + BindShader(_sRooms); UINT stride = sizeof(Vertex); UINT offset = 0; @@ -3465,14 +3506,8 @@ namespace TEN::Renderer SetScissor(objectInfo->Room->ClipBounds); - if (objectInfo->Bucket->Animated == 0) - { - _context->VSSetShader(_vsRooms.Get(), nullptr, 0); - } - else - { - _context->VSSetShader(_vsRoomsAnimatedTextures.Get(), nullptr, 0); - } + if (objectInfo->Bucket->Animated != 0) + BindShader(_sRoomsAnimated); SetBlendMode(objectInfo->Bucket->BlendMode); SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD); @@ -3538,11 +3573,11 @@ namespace TEN::Renderer SetBlendMode(objectInfo->Bucket->BlendMode); SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD); - _context->VSSetShader(_vsItems.Get(), nullptr, 0); - _context->PSSetShader(_psItems.Get(), nullptr, 0); + BindShader(_sItems); // Bind main item properties. - _stItem.World = objectInfo->Item->InterpolatedWorld; + Matrix world = objectInfo->Item->InterpolatedWorld; + _stItem.World = world; _stItem.Color = objectInfo->Item->Color; _stItem.AmbientLight = objectInfo->Item->AmbientLight; memcpy(_stItem.BonesMatrices, objectInfo->Item->InterpolatedAnimTransforms, sizeof(Matrix) * MAX_BONES); @@ -3551,7 +3586,8 @@ namespace TEN::Renderer for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++) _stItem.BoneLightModes[k] = (int)moveableObj.ObjectMeshes[k]->LightMode; - BindMoveableLights(objectInfo->Item->LightsToDraw, objectInfo->Item->RoomNumber, objectInfo->Item->PrevRoomNumber, objectInfo->Item->LightFade); + bool acceptsShadows = moveableObj.ShadowType == ShadowMode::None; + BindMoveableLights(objectInfo->Item->LightsToDraw, objectInfo->Item->RoomNumber, objectInfo->Item->PrevRoomNumber, objectInfo->Item->LightFade, acceptsShadows); _cbItem.UpdateData(_stItem, _context.Get()); BindTexture( @@ -3578,10 +3614,11 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetInputLayout(_inputLayout.Get()); - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); + + auto world = objectInfo->Static->World; + _stStatic.World = world; - _stStatic.World = objectInfo->Static->World; _stStatic.Color = objectInfo->Static->Color; _stStatic.AmbientLight = objectInfo->Room->AmbientLight; _stStatic.LightMode = (int)GetStaticRendererObject(objectInfo->Static->ObjectNumber).ObjectMeshes[0]->LightMode; @@ -3615,10 +3652,11 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetInputLayout(_inputLayout.Get()); - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); + + auto world = objectInfo->World; + _stStatic.World = world; - _stStatic.World = objectInfo->World; _stStatic.Color = Vector4::One; _stStatic.AmbientLight = objectInfo->Room->AmbientLight; _stStatic.LightMode = (int)objectInfo->Mesh->LightMode; @@ -3646,15 +3684,17 @@ namespace TEN::Renderer void Renderer::CalculateSSAO(RenderView& view) { + _doingFullscreenPass = true; + SetBlendMode(BlendMode::Opaque); SetCullMode(CullMode::CounterClockwise); SetDepthState(DepthState::Write); // Common vertex shader to all full screen effects - _context->VSSetShader(_vsPostProcess.Get(), nullptr, 0); + BindShader(_sPostProcess); // SSAO pixel shader - _context->PSSetShader(_psSSAO.Get(), nullptr, 0); + BindShader(_sSSAO); _context->ClearRenderTargetView(_SSAORenderTarget.RenderTargetView.Get(), Colors::White); _context->OMSetRenderTargets(1, _SSAORenderTarget.RenderTargetView.GetAddressOf(), nullptr); @@ -3698,7 +3738,7 @@ namespace TEN::Renderer DrawTriangles(3, 0); // Blur step - _context->PSSetShader(_psSSAOBlur.Get(), nullptr, 0); + BindShader(_sSSAOBlur); _context->ClearRenderTargetView(_SSAOBlurredRenderTarget.RenderTargetView.Get(), Colors::Black); _context->OMSetRenderTargets(1, _SSAOBlurredRenderTarget.RenderTargetView.GetAddressOf(), nullptr); @@ -3706,6 +3746,8 @@ namespace TEN::Renderer BindRenderTargetAsTexture(TextureRegister::SSAO, &_SSAORenderTarget, SamplerStateRegister::PointWrap); DrawTriangles(3, 0); + + _doingFullscreenPass = false; } void Renderer::InterpolateCamera(float interpFactor) diff --git a/TombEngine/Renderer/RendererDraw2D.cpp b/TombEngine/Renderer/RendererDraw2D.cpp index 93194e04e..698b9dea9 100644 --- a/TombEngine/Renderer/RendererDraw2D.cpp +++ b/TombEngine/Renderer/RendererDraw2D.cpp @@ -132,8 +132,8 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetIndexBuffer(bar.IndexBufferBorder.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0); - _context->VSSetShader(_vsHUD.Get(), nullptr, 0); - _context->PSSetShader(_psHUDTexture.Get(), nullptr, 0); + BindShader(_sHUD); + BindShader(_sHUDTexture); SetBlendMode(BlendMode::Opaque); SetDepthState(DepthState::None); @@ -161,8 +161,8 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetIndexBuffer(bar.InnerIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0); - _context->VSSetShader(_vsHUD.Get(), nullptr, 0); - _context->PSSetShader(_psHUDBarColor.Get(), nullptr, 0); + BindShader(_sHUD); + BindShader(_sHUDBarColor); _stHUDBar.Percent = percent; _stHUDBar.Poisoned = isPoisoned; @@ -195,8 +195,8 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetIndexBuffer(g_LoadingBar->IndexBufferBorder.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0); - _context->VSSetShader(_vsHUD.Get(), NULL, 0); - _context->PSSetShader(_psHUDTexture.Get(), NULL, 0); + BindShader(_sHUD); + BindShader(_sHUDTexture); SetBlendMode(BlendMode::Opaque); SetDepthState(DepthState::None); @@ -220,8 +220,8 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetIndexBuffer(g_LoadingBar->InnerIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0); - _context->VSSetShader(_vsHUD.Get(), nullptr, 0); - _context->PSSetShader(_psHUDBarColor.Get(), nullptr, 0); + BindShader(_sHUD); + BindShader(_sHUDBarColor); _stHUDBar.Percent = percentage / 100.0f; _stHUDBar.Poisoned = false; @@ -305,8 +305,7 @@ namespace TEN::Renderer vertices[3].UV.y = 1.0f; vertices[3].Color = Vector4(1.0f, 0.0f, 0.0f, 1.0f); - _context->VSSetShader(_vsFullScreenQuad.Get(), nullptr, 0); - _context->PSSetShader(_psFullScreenQuad.Get(), nullptr, 0); + BindShader(_sFullScreenQuad); _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetInputLayout(_inputLayout.Get()); @@ -342,8 +341,7 @@ namespace TEN::Renderer if (renderView.DisplaySpritesToDraw.empty()) return; - _context->VSSetShader(_vsFullScreenQuad.Get(), nullptr, 0); - _context->PSSetShader(_psFullScreenQuad.Get(), nullptr, 0); + BindShader(_sFullScreenQuad); _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); _context->IASetInputLayout(_inputLayout.Get()); @@ -465,8 +463,7 @@ namespace TEN::Renderer vertices[3].UV.y = uvEnd.y; vertices[3].Color = colorVec4; - _context->VSSetShader(_vsFullScreenQuad.Get(), nullptr, 0); - _context->PSSetShader(_psFullScreenQuad.Get(), nullptr, 0); + BindShader(_sFullScreenQuad); _context->PSSetShaderResources(0, 1, &texture); @@ -543,8 +540,7 @@ namespace TEN::Renderer vertices[3].UV.y = uvEnd.y; vertices[3].Color = Vector4(color.x, color.y, color.z, 1.0f); - _context->VSSetShader(_vsFullScreenQuad.Get(), nullptr, 0); - _context->PSSetShader(_psFullScreenQuad.Get(), nullptr, 0); + BindShader(_sFullScreenQuad); _context->PSSetShaderResources(0, 1, &texture); auto* sampler = _renderStates->AnisotropicClamp(); diff --git a/TombEngine/Renderer/RendererDrawEffect.cpp b/TombEngine/Renderer/RendererDrawEffect.cpp index 66a845e45..24c605741 100644 --- a/TombEngine/Renderer/RendererDrawEffect.cpp +++ b/TombEngine/Renderer/RendererDrawEffect.cpp @@ -1010,20 +1010,19 @@ namespace TEN::Renderer } } - bool Renderer::DrawGunFlashes(RenderView& view) + bool Renderer::DrawGunFlashes(RenderView& view) { if (!Lara.RightArm.GunFlash && !Lara.LeftArm.GunFlash) return false; - if (Lara.Control.Look.OpticRange > 0) + if (Lara.Control.Look.OpticRange > 0 && _currentMirror == nullptr) return false; const auto& settings = g_GameFlow->GetSettings()->Weapons[(int)Lara.Control.Weapon.GunType - 1]; if (!settings.MuzzleFlash) return false; - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); unsigned int stride = sizeof(Vertex); unsigned int offset = 0; @@ -1111,6 +1110,7 @@ namespace TEN::Renderer worldMatrix = itemPtr->AnimTransforms[LM_LHAND] * itemPtr->World; worldMatrix = tMatrix * worldMatrix; worldMatrix = rotMatrix * worldMatrix; + ReflectMatrixOptionally(worldMatrix); _stStatic.World = worldMatrix; _cbStatic.UpdateData(_stStatic, _context.Get()); @@ -1125,6 +1125,7 @@ namespace TEN::Renderer worldMatrix = itemPtr->AnimTransforms[LM_RHAND] * itemPtr->World; worldMatrix = tMatrix * worldMatrix; worldMatrix = rotMatrix * worldMatrix; + ReflectMatrixOptionally(worldMatrix); _stStatic.World = worldMatrix; _cbStatic.UpdateData(_stStatic, _context.Get()); @@ -1142,8 +1143,7 @@ namespace TEN::Renderer void Renderer::DrawBaddyGunflashes(RenderView& view) { - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); unsigned int stride = sizeof(Vertex); unsigned int offset = 0; @@ -1153,6 +1153,9 @@ namespace TEN::Renderer for (auto* rRoomPtr : view.RoomsToDraw) { + if (IgnoreReflectionPassForRoom(rRoomPtr->RoomNumber)) + continue; + for (auto* rItemPtr : rRoomPtr->ItemsToDraw) { auto& nativeItem = g_Level.Items[rItemPtr->ItemNumber]; @@ -1202,6 +1205,8 @@ namespace TEN::Renderer if (creature.MuzzleFlash[0].ApplyZRotation) worldMatrix = rotMatrixZ * worldMatrix; + ReflectMatrixOptionally(worldMatrix); + _stStatic.World = worldMatrix; _cbStatic.UpdateData(_stStatic, _context.Get()); @@ -1242,6 +1247,8 @@ namespace TEN::Renderer if (creature.MuzzleFlash[1].ApplyZRotation) worldMatrix = rotMatrixZ * worldMatrix; + ReflectMatrixOptionally(worldMatrix); + _stStatic.World = worldMatrix; _cbStatic.UpdateData(_stStatic, _context.Get()); @@ -1278,12 +1285,23 @@ namespace TEN::Renderer auto spriteMatrix = Matrix::Identity; auto scaleMatrix = Matrix::CreateScale(sprite->Width * sprite->Scale, sprite->Height * sprite->Scale, sprite->Scale); + auto spritePos = sprite->pos; + + if (sprite->Type == SpriteType::ThreeD) + { + ReflectMatrixOptionally(spriteMatrix); + } + else + { + ReflectVectorOptionally(spritePos); + } + switch (sprite->Type) { case SpriteType::Billboard: { auto cameraUp = Vector3(view.Camera.View._12, view.Camera.View._22, view.Camera.View._32); - spriteMatrix = scaleMatrix * Matrix::CreateRotationZ(sprite->Rotation) * Matrix::CreateBillboard(sprite->pos, Camera.pos.ToVector3(), cameraUp); + spriteMatrix = scaleMatrix * Matrix::CreateRotationZ(sprite->Rotation) * Matrix::CreateBillboard(spritePos, Camera.pos.ToVector3(), cameraUp); } break; @@ -1292,7 +1310,7 @@ namespace TEN::Renderer auto rotMatrix = Matrix::CreateRotationY(sprite->Rotation); auto quadForward = Vector3(0.0f, 0.0f, 1.0f); spriteMatrix = scaleMatrix * Matrix::CreateConstrainedBillboard( - sprite->pos, + spritePos, Camera.pos.ToVector3(), sprite->ConstrainAxis, nullptr, @@ -1302,9 +1320,9 @@ namespace TEN::Renderer case SpriteType::LookAtBillboard: { - auto tMatrix = Matrix::CreateTranslation(sprite->pos); + auto translationMatrix = Matrix::CreateTranslation(spritePos); auto rotMatrix = Matrix::CreateRotationZ(sprite->Rotation) * Matrix::CreateLookAt(Vector3::Zero, sprite->LookAtAxis, Vector3::UnitZ); - spriteMatrix = scaleMatrix * rotMatrix * tMatrix; + spriteMatrix = scaleMatrix * rotMatrix * translationMatrix; } break; @@ -1316,11 +1334,14 @@ namespace TEN::Renderer return spriteMatrix; } - void Renderer::DrawEffect(RenderView& view, RendererEffect* effect, RendererPass rendererPass) + void Renderer::DrawEffect(RenderView& view, RendererEffect* effect, RendererPass rendererPass) { const auto& room = _rooms[effect->RoomNumber]; - _stStatic.World = effect->InterpolatedWorld; + auto world = effect->InterpolatedWorld; + ReflectMatrixOptionally(world); + + _stStatic.World = world; _stStatic.Color = effect->Color; _stStatic.AmbientLight = effect->AmbientLight; _stStatic.LightMode = (int)LightMode::Dynamic; @@ -1350,10 +1371,9 @@ namespace TEN::Renderer } } - void Renderer::DrawEffects(RenderView& view, RendererPass rendererPass) + void Renderer::DrawEffects(RenderView& view, RendererPass rendererPass) { - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); unsigned int stride = sizeof(Vertex); unsigned int offset = 0; @@ -1363,6 +1383,9 @@ namespace TEN::Renderer for (auto* roomPtr : view.RoomsToDraw) { + if (IgnoreReflectionPassForRoom(roomPtr->RoomNumber)) + continue; + for (auto* effectPtr : roomPtr->EffectsToDraw) { const auto& room = _rooms[effectPtr->RoomNumber]; @@ -1381,6 +1404,9 @@ namespace TEN::Renderer { if (deb.active) { + if (IgnoreReflectionPassForRoom(deb.roomNumber)) + continue; + activeDebrisExist = true; break; } @@ -1388,8 +1414,7 @@ namespace TEN::Renderer if (activeDebrisExist) { - _context->VSSetShader(_vsStatics.Get(), nullptr, 0); - _context->PSSetShader(_psStatics.Get(), nullptr, 0); + BindShader(_sStatics); SetCullMode(CullMode::None); @@ -1399,6 +1424,9 @@ namespace TEN::Renderer { if (deb.active) { + if (IgnoreReflectionPassForRoom(deb.roomNumber)) + continue; + if (!SetupBlendModeAndAlphaTest(deb.mesh.blendMode, rendererPass, 0)) continue; @@ -1419,6 +1447,7 @@ namespace TEN::Renderer _cbStatic.UpdateData(_stStatic, _context.Get()); auto matrix = Matrix::Lerp(deb.PrevTransform, deb.Transform, GetInterpolationFactor()); + ReflectMatrixOptionally(matrix); Vertex vtx0; vtx0.Position = Vector3::Transform(deb.mesh.Positions[0], matrix); @@ -1453,7 +1482,6 @@ namespace TEN::Renderer SetBlendMode(BlendMode::Opaque, true); SetDepthState(DepthState::Write, true); - SetCullMode(CullMode::CounterClockwise, true); } } diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index a7d9cb809..1da3c1df4 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -817,8 +817,7 @@ namespace TEN::Renderer _context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0); // Set shaders. - _context->VSSetShader(_vsInventory.Get(), nullptr, 0); - _context->PSSetShader(_psInventory.Get(), nullptr, 0); + BindShader(_sInventory); // Set matrices. CCameraMatrixBuffer hudCamera; @@ -890,7 +889,7 @@ namespace TEN::Renderer if (!texture.Texture) return; - int timeout = 20; + int timeout = 10; float currentFade = FADE_FACTOR; while (timeout || currentFade > 0.0f) @@ -1011,8 +1010,7 @@ namespace TEN::Renderer _context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0); // Set shaders - _context->VSSetShader(_vsInventory.Get(), nullptr, 0); - _context->PSSetShader(_psInventory.Get(), nullptr, 0); + BindShader(_sInventory); if (CurrentLevel == 0) { diff --git a/TombEngine/Renderer/RendererEnums.h b/TombEngine/Renderer/RendererEnums.h index 23df7a369..e71f4af86 100644 --- a/TombEngine/Renderer/RendererEnums.h +++ b/TombEngine/Renderer/RendererEnums.h @@ -226,6 +226,7 @@ enum class RendererPass CollectTransparentFaces, Additive, GBuffer, + GunFlashes, RoomAmbient }; diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index 5b919f28b..c47b5f33d 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -14,10 +14,12 @@ #include "Renderer/RenderView.h" #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Specific/level.h" +#include "Specific/trutils.h" using namespace TEN::Entities::Effects; using namespace TEN::Collision::Sphere; using namespace TEN::Math; +using namespace TEN::Utils; namespace TEN::Renderer { @@ -355,102 +357,128 @@ namespace TEN::Renderer _visitedRoomsStack.pop_back(); } + void Renderer::CollectMirrors(RenderView& renderView) + { + // Collect mirrors first because they are needed while collecting moveables. + for (const auto& mirror : g_Level.Mirrors) + { + // TODO: Avoid LaraItem global. + if (mirror.RoomNumber != Camera.pos.RoomNumber && mirror.RoomNumber != LaraItem->RoomNumber) + continue; + + if (!mirror.Enabled) + continue; + + auto& rendererMirror = renderView.Mirrors.emplace_back(); + rendererMirror.RoomNumber = mirror.RoomNumber; + rendererMirror.ReflectionMatrix = mirror.ReflectionMatrix; + rendererMirror.ReflectPlayer = mirror.ReflectPlayer; + rendererMirror.ReflectMoveables = mirror.ReflectMoveables; + rendererMirror.ReflectStatics = mirror.ReflectStatics; + rendererMirror.ReflectSprites = mirror.ReflectSprites; + rendererMirror.ReflectLights = mirror.ReflectLights; + } + } + void Renderer::CollectItems(short roomNumber, RenderView& renderView) { if (_rooms.size() < roomNumber) return; - auto& room = _rooms[roomNumber]; - auto* r = &g_Level.Rooms[room.RoomNumber]; + auto& rendererRoom = _rooms[roomNumber]; + const auto& room = g_Level.Rooms[rendererRoom.RoomNumber]; - short itemNum = NO_VALUE; - for (itemNum = r->itemNumber; itemNum != NO_VALUE; itemNum = g_Level.Items[itemNum].NextItem) + bool isRoomReflected = IsRoomReflected(renderView, roomNumber); + + short itemNumber = NO_VALUE; + for (itemNumber = room.itemNumber; itemNumber != NO_VALUE; itemNumber = g_Level.Items[itemNumber].NextItem) { - auto* item = &g_Level.Items[itemNum]; + const auto& item = g_Level.Items[itemNumber]; - if (item->ObjectNumber == ID_LARA && itemNum == g_Level.Items[itemNum].NextItem) + if (item.ObjectNumber == ID_LARA && itemNumber == g_Level.Items[itemNumber].NextItem) break; - if (item->Status == ITEM_INVISIBLE) + if (item.Status == ITEM_INVISIBLE) continue; - if (item->ObjectNumber == ID_LARA && (Lara.Control.Look.OpticRange || SpotcamOverlay || SpotcamDontDrawLara)) + if (item.ObjectNumber == ID_LARA && (SpotcamOverlay || SpotcamDontDrawLara)) continue; - if (item->ObjectNumber == ID_LARA && CurrentLevel == 0 && !g_GameFlow->IsLaraInTitleEnabled()) + if (item.ObjectNumber == ID_LARA && CurrentLevel == 0 && !g_GameFlow->IsLaraInTitleEnabled()) continue; - if (!_moveableObjects[item->ObjectNumber].has_value()) + if (!_moveableObjects[item.ObjectNumber].has_value()) continue; - auto& obj = _moveableObjects[item->ObjectNumber].value(); + auto& obj = _moveableObjects[item.ObjectNumber].value(); if (obj.DoNotDraw) continue; - // Clip object by frustum only if it doesn't cast shadows. Otherwise we may see - // disappearing shadows if object gets out of frustum. - - if (obj.ShadowType == ShadowMode::None) + // Clip object by frustum only if it doesn't cast shadows and is not in mirror room, + // otherwise disappearing shadows or reflections may be seen if object gets out of frustum. + if (!isRoomReflected && obj.ShadowType == ShadowMode::None) { // Get all spheres and check if frustum intersects any of them. - auto spheres = GetSpheres(itemNum); + auto spheres = GetSpheres(itemNumber); bool inFrustum = false; for (int i = 0; !inFrustum, i < spheres.size(); i++) + { // Blow up sphere radius by half for cases of too small calculated spheres. if (renderView.Camera.Frustum.SphereInFrustum(spheres[i].Center, spheres[i].Radius * 1.5f)) inFrustum = true; + } if (!inFrustum) continue; } - auto newItem = &_items[itemNum]; + auto& newItem = _items[itemNumber]; - newItem->ItemNumber = itemNum; - newItem->ObjectID = item->ObjectNumber; - newItem->Color = item->Model.Color; - newItem->Position = item->Pose.Position.ToVector3(); - newItem->Translation = Matrix::CreateTranslation(newItem->Position.x, newItem->Position.y, newItem->Position.z); - newItem->Rotation = item->Pose.Orientation.ToRotationMatrix(); - newItem->Scale = Matrix::CreateScale(1.0f); - newItem->World = newItem->Rotation * newItem->Translation; + newItem.ItemNumber = itemNumber; + newItem.ObjectID = item.ObjectNumber; + newItem.Color = item.Model.Color; + newItem.Position = item.Pose.Position.ToVector3(); + newItem.Translation = Matrix::CreateTranslation(newItem.Position.x, newItem.Position.y, newItem.Position.z); + newItem.Rotation = item.Pose.Orientation.ToRotationMatrix(); + newItem.Scale = Matrix::CreateScale(1.0f); + newItem.World = newItem.Rotation * newItem.Translation; // Disable interpolation either when renderer slot or item slot has flag. - // Renderer slot has no interpolation flag set in case it is fetched for the first time (e.g. item first time in frustum). - newItem->DisableInterpolation = item->DisableInterpolation || newItem->DisableInterpolation; + // Renderer slot has no interpolation flag set in case it is fetched for first time (e.g. item first time in frustum). + newItem.DisableInterpolation = item.DisableInterpolation || newItem.DisableInterpolation; - if (newItem->DisableInterpolation) + if (newItem.DisableInterpolation) { - // NOTE: Interpolation alwasy returns same result. - newItem->PrevPosition = newItem->Position; - newItem->PrevTranslation = newItem->Translation; - newItem->PrevRotation = newItem->Rotation; - newItem->PrevWorld = newItem->World; + // NOTE: Interpolation always returns same result. + newItem.PrevPosition = newItem.Position; + newItem.PrevTranslation = newItem.Translation; + newItem.PrevRotation = newItem.Rotation; + newItem.PrevWorld = newItem.World; - // Otherwise all frames until the next ControlPhase will not be interpolated. - newItem->DisableInterpolation = false; + // Otherwise all frames until next ControlPhase will not be interpolated. + newItem.DisableInterpolation = false; for (int j = 0; j < MAX_BONES; j++) - newItem->PrevAnimTransforms[j] = newItem->AnimTransforms[j]; + newItem.PrevAnimTransforms[j] = newItem.AnimTransforms[j]; } - // Force interpolation only for Lara in player freeze mode. - bool forceValue = g_GameFlow->CurrentFreezeMode == FreezeMode::Player && item->ObjectNumber == ID_LARA; + // Force interpolation only for player in player freeze mode. + bool forceValue = g_GameFlow->CurrentFreezeMode == FreezeMode::Player && item.ObjectNumber == ID_LARA; - newItem->InterpolatedPosition = Vector3::Lerp(newItem->PrevPosition, newItem->Position, GetInterpolationFactor(forceValue)); - newItem->InterpolatedTranslation = Matrix::Lerp(newItem->PrevTranslation, newItem->Translation, GetInterpolationFactor(forceValue)); - newItem->InterpolatedRotation = Matrix::Lerp(newItem->InterpolatedRotation, newItem->Rotation, GetInterpolationFactor(forceValue)); - newItem->InterpolatedWorld = Matrix::Lerp(newItem->PrevWorld, newItem->World, GetInterpolationFactor(forceValue)); + newItem.InterpolatedPosition = Vector3::Lerp(newItem.PrevPosition, newItem.Position, GetInterpolationFactor(forceValue)); + newItem.InterpolatedTranslation = Matrix::Lerp(newItem.PrevTranslation, newItem.Translation, GetInterpolationFactor(forceValue)); + newItem.InterpolatedRotation = Matrix::Lerp(newItem.InterpolatedRotation, newItem.Rotation, GetInterpolationFactor(forceValue)); + newItem.InterpolatedWorld = Matrix::Lerp(newItem.PrevWorld, newItem.World, GetInterpolationFactor(forceValue)); for (int j = 0; j < MAX_BONES; j++) - newItem->InterpolatedAnimTransforms[j] = Matrix::Lerp(newItem->PrevAnimTransforms[j], newItem->AnimTransforms[j], GetInterpolationFactor(forceValue)); + newItem.InterpolatedAnimTransforms[j] = Matrix::Lerp(newItem.PrevAnimTransforms[j], newItem.AnimTransforms[j], GetInterpolationFactor(forceValue)); - CalculateLightFades(newItem); - CollectLightsForItem(newItem); + CalculateLightFades(&newItem); + CollectLightsForItem(&newItem); - room.ItemsToDraw.push_back(newItem); + rendererRoom.ItemsToDraw.push_back(&newItem); } } @@ -465,6 +493,8 @@ namespace TEN::Renderer if (r->mesh.empty()) return; + bool isRoomReflected = IsRoomReflected(renderView, roomNumber); + for (int i = 0; i < room.Statics.size(); i++) { auto* mesh = &room.Statics[i]; @@ -493,9 +523,9 @@ namespace TEN::Renderer if (obj.ObjectMeshes.empty()) continue; - if (!renderView.Camera.Frustum.SphereInFrustum(mesh->Sphere.Center, mesh->Sphere.Radius)) + if (!isRoomReflected && !renderView.Camera.Frustum.SphereInFrustum(mesh->Sphere.Center, mesh->Sphere.Radius)) continue; - + // Collect the lights std::vector lights; std::vector cachedRoomLights; @@ -717,13 +747,11 @@ namespace TEN::Renderer void Renderer::CollectLightsForRoom(short roomNumber, RenderView &renderView) { if (_rooms.size() < roomNumber) - { return; - } RendererRoom& room = _rooms[roomNumber]; ROOM_INFO* r = &g_Level.Rooms[roomNumber]; - + // Collect dynamic lights for rooms for (int i = 0; i < _dynamicLights[_dynamicLightList].size(); i++) { diff --git a/TombEngine/Renderer/RendererHelper.cpp b/TombEngine/Renderer/RendererHelper.cpp index 2e6a96965..0a28218af 100644 --- a/TombEngine/Renderer/RendererHelper.cpp +++ b/TombEngine/Renderer/RendererHelper.cpp @@ -600,6 +600,18 @@ namespace TEN::Renderer return rendererItem->BoneOrientations[boneID]; } + bool Renderer::IsRoomReflected(RenderView& renderView, int roomNumber) + { + for (const auto& mirror : renderView.Mirrors) + { + // TODO: Avoid LaraItem global. + if (roomNumber == mirror.RoomNumber && (Camera.pos.RoomNumber == mirror.RoomNumber || LaraItem->RoomNumber == mirror.RoomNumber)) + return true; + } + + return false; + } + void Renderer::SaveScreenshot() { char buffer[64]; diff --git a/TombEngine/Renderer/RendererInit.cpp b/TombEngine/Renderer/RendererInit.cpp index 08d9b11a9..d0c0f6655 100644 --- a/TombEngine/Renderer/RendererInit.cpp +++ b/TombEngine/Renderer/RendererInit.cpp @@ -1,19 +1,23 @@ #include "framework.h" -#include -#include -#include + +// TODO: To framework. +#include +#include + #include "Renderer/Renderer.h" +#include "Renderer/RendererUtils.h" +#include "Renderer/SMAA/AreaTex.h" +#include "Renderer/SMAA/SearchTex.h" #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Specific/configuration.h" #include "Specific/memory/Vector.h" #include "Specific/trutils.h" #include "Specific/winmain.h" -#include "Renderer/SMAA/AreaTex.h" -#include "Renderer/SMAA/SearchTex.h" -#include extern GameConfiguration g_Configuration; +using namespace TEN::Renderer::Utils; + static std::wstring GetAssetPath(const wchar_t* fileName) { return TEN::Utils::ToWString(g_GameFlow->GetGameDir()) + fileName; @@ -35,12 +39,9 @@ namespace TEN::Renderer _renderStates = std::make_unique(_device.Get()); // Load shaders - ComPtr blob; const D3D_SHADER_MACRO roomDefinesAnimated[] = { "ANIMATED", "", nullptr, nullptr }; const D3D_SHADER_MACRO roomDefinesShadowMap[] = { "SHADOW_MAP", "", nullptr, nullptr }; - _vsRooms = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\Rooms.fx"), "VS", "vs_5_0", nullptr, blob); - // Initialize input layout using first vertex shader. D3D11_INPUT_ELEMENT_DESC inputLayoutItems[] = { @@ -57,50 +58,41 @@ namespace TEN::Renderer { "DRAWINDEX", 0, DXGI_FORMAT_R32_UINT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "HASH", 0, DXGI_FORMAT_R32_SINT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 } }; - Utils::throwIfFailed(_device->CreateInputLayout(inputLayoutItems, 12, blob->GetBufferPointer(), blob->GetBufferSize(), &_inputLayout)); - _vsRoomsAnimatedTextures = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\Rooms.fx"), "VS", "vs_5_0", &roomDefinesAnimated[0], blob); - _psRooms = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\Rooms.fx"), "PS", "ps_5_0", nullptr, blob); - _vsItems = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\Items.fx"), "VS", "vs_5_0", nullptr, blob); - _psItems = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\Items.fx"), "PS", "ps_5_0", nullptr, blob); - _vsStatics = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\Statics.fx"), "VS", "vs_5_0", nullptr, blob); - _psStatics = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\Statics.fx"), "PS", "ps_5_0", nullptr, blob); - _vsSky = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\Sky.fx"), "VS", "vs_5_0", nullptr, blob); - _psSky = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\Sky.fx"), "PS", "ps_5_0", nullptr, blob); - _vsSprites = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\Sprites.fx"), "VS", "vs_5_0", nullptr, blob); - _psSprites = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\Sprites.fx"), "PS", "ps_5_0", nullptr, blob); - _vsSolid = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\Solid.fx"), "VS", "vs_5_0", nullptr, blob); - _psSolid = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\Solid.fx"), "PS", "ps_5_0", nullptr, blob); - _vsInventory = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\Inventory.fx"), "VS", "vs_5_0", nullptr, blob); - _psInventory = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\Inventory.fx"), "PS", "ps_5_0", nullptr, blob); - _vsFullScreenQuad = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\FullScreenQuad.fx"), "VS", "vs_5_0", nullptr, blob); - _psFullScreenQuad = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\FullScreenQuad.fx"), "PS", "ps_5_0", nullptr, blob); - _vsShadowMap = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\ShadowMap.fx"), "VS", "vs_5_0", nullptr, blob); - _psShadowMap = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\ShadowMap.fx"), "PS", "ps_5_0", nullptr, blob); - _vsHUD = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\HUD.hlsl"), "VS", "vs_5_0", nullptr, blob); - _psHUDColor = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\HUD.hlsl"), "PSColoredHUD", "ps_5_0", nullptr, blob); - _psHUDTexture = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\HUD.hlsl"), "PSTexturedHUD", "ps_5_0", nullptr, blob); - _psHUDBarColor = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\HUD.hlsl"), "PSTexturedHUDBar", "ps_5_0", nullptr, blob); - _vsInstancedStaticMeshes = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\InstancedStatics.fx"), "VS", "vs_5_0", nullptr, blob); - _psInstancedStaticMeshes = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\InstancedStatics.fx"), "PS", "ps_5_0", nullptr, blob); - _vsInstancedSprites = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\InstancedSprites.fx"), "VS", "vs_5_0", nullptr, blob); - _psInstancedSprites = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\InstancedSprites.fx"), "PS", "ps_5_0", nullptr, blob); - _vsGBufferRooms = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\GBuffer.fx"), "VSRooms", "vs_5_0", nullptr, blob); - _vsGBufferRoomsAnimated = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\GBuffer.fx"), "VSRooms", "vs_5_0", &roomDefinesAnimated[0], blob); - _vsGBufferItems = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\GBuffer.fx"), "VSItems", "vs_5_0", nullptr, blob); - _vsGBufferStatics = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\GBuffer.fx"), "VSStatics", "vs_5_0", nullptr, blob); - _vsGBufferInstancedStatics = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\GBuffer.fx"), "VSInstancedStatics", "vs_5_0", nullptr, blob); - _psGBuffer = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\GBuffer.fx"), "PS", "ps_5_0", nullptr, blob); - _vsRoomAmbient = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\RoomAmbient.fx"), "VS", "vs_5_0", nullptr, blob); - _vsRoomAmbientSky = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\RoomAmbient.fx"), "VSSky", "vs_5_0", nullptr, blob); - _psRoomAmbient = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\RoomAmbient.fx"), "PS", "ps_5_0", nullptr, blob); - _vsFXAA = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\FXAA.fx"), "VS", "vs_5_0", nullptr, blob); - _psFXAA = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\FXAA.fx"), "PS", "ps_5_0", nullptr, blob); - _psSSAO = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\SSAO.fx"), "PS", "ps_5_0", nullptr, blob); - _psSSAOBlur = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\SSAO.fx"), "PSBlur", "ps_5_0", nullptr, blob); + _sRooms = CompileOrLoadShader("Rooms", "", ShaderType::PixelAndVertex); + Utils::throwIfFailed(_device->CreateInputLayout(inputLayoutItems, 12, _sRooms.Vertex.Blob->GetBufferPointer(), _sRooms.Vertex.Blob->GetBufferSize(), &_inputLayout)); + + _sRoomsAnimated = CompileOrLoadShader("Rooms", "", ShaderType::Vertex, &roomDefinesAnimated[0]); + _sItems = CompileOrLoadShader("Items", "", ShaderType::PixelAndVertex); + _sStatics = CompileOrLoadShader("Statics", "", ShaderType::PixelAndVertex); + _sSky = CompileOrLoadShader("Sky", "", ShaderType::PixelAndVertex); + _sSprites = CompileOrLoadShader("Sprites", "", ShaderType::PixelAndVertex); + _sSolid = CompileOrLoadShader("Solid", "", ShaderType::PixelAndVertex); + _sInventory = CompileOrLoadShader("Inventory", "", ShaderType::PixelAndVertex); + _sFullScreenQuad = CompileOrLoadShader("FullScreenQuad", "", ShaderType::PixelAndVertex); + _sShadowMap = CompileOrLoadShader("ShadowMap", "", ShaderType::PixelAndVertex, &roomDefinesShadowMap[0]); + _sHUD = CompileOrLoadShader("HUD", "", ShaderType::Vertex); + _sHUDColor = CompileOrLoadShader("HUD", "ColoredHUD", ShaderType::Pixel); + _sHUDTexture = CompileOrLoadShader("HUD", "TexturedHUD", ShaderType::Pixel); + _sHUDBarColor = CompileOrLoadShader("HUD", "TexturedHUDBar", ShaderType::Pixel); + _sInstancedStatics = CompileOrLoadShader("InstancedStatics", "", ShaderType::PixelAndVertex); + _sInstancedSprites = CompileOrLoadShader("InstancedSprites", "", ShaderType::PixelAndVertex); + + _sGBuffer = CompileOrLoadShader("GBuffer", "", ShaderType::Pixel); + _sGBufferRooms = CompileOrLoadShader("GBuffer", "Rooms", ShaderType::Vertex); + _sGBufferRoomsAnimated = CompileOrLoadShader("GBuffer", "Rooms", ShaderType::Vertex, &roomDefinesAnimated[0]); + _sGBufferItems = CompileOrLoadShader("GBuffer", "Items", ShaderType::Vertex); + _sGBufferStatics = CompileOrLoadShader("GBuffer", "Statics", ShaderType::Vertex); + _sGBufferInstancedStatics = CompileOrLoadShader("GBuffer", "InstancedStatics", ShaderType::Vertex); + + _sRoomAmbient = CompileOrLoadShader("RoomAmbient", "", ShaderType::PixelAndVertex); + _sRoomAmbientSky = CompileOrLoadShader("RoomAmbient", "Sky", ShaderType::Vertex); + _sFXAA = CompileOrLoadShader("FXAA", "", ShaderType::Pixel); + _sSSAO = CompileOrLoadShader("SSAO", "", ShaderType::Pixel); + _sSSAOBlur = CompileOrLoadShader("SSAO", "Blur", ShaderType::Pixel); const D3D_SHADER_MACRO transparentDefines[] = { "TRANSPARENT", "", nullptr, nullptr }; - _psRoomsTransparent = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\Rooms.fx"), "PS", "ps_5_0", &transparentDefines[0], blob); + _sRoomsTransparent = CompileOrLoadShader("Rooms", "", ShaderType::Pixel, &transparentDefines[0]); // Initialize constant buffers _cbCameraMatrices = CreateConstantBuffer(); @@ -292,9 +284,7 @@ namespace TEN::Renderer _fullscreenTriangleVertexBuffer = VertexBuffer(_device.Get(), 3, &vertices[0]); - ComPtr blob; - - _vsPostProcess = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\PostProcess.fx"), "VS", "vs_5_0", nullptr, blob); + _sPostProcess = CompileOrLoadShader("PostProcess", "", ShaderType::PixelAndVertex); D3D11_INPUT_ELEMENT_DESC postProcessInputLayoutItems[] = { @@ -302,14 +292,15 @@ namespace TEN::Renderer { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 } }; - Utils::throwIfFailed(_device->CreateInputLayout(postProcessInputLayoutItems, 3, blob->GetBufferPointer(), blob->GetBufferSize(), &_fullscreenTriangleInputLayout)); - - _psPostProcessCopy = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\PostProcess.fx"), "PSCopy", "ps_5_0", nullptr, blob); - _psPostProcessMonochrome = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\PostProcess.fx"), "PSMonochrome", "ps_5_0", nullptr, blob); - _psPostProcessNegative = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\PostProcess.fx"), "PSNegative", "ps_5_0", nullptr, blob); - _psPostProcessExclusion = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\PostProcess.fx"), "PSExclusion", "ps_5_0", nullptr, blob); - _psPostProcessFinalPass = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\PostProcess.fx"), "PSFinalPass", "ps_5_0", nullptr, blob); - _psPostProcessLensFlare = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\PostProcess.fx"), "PSLensFlare", "ps_5_0", nullptr, blob); + + Utils::throwIfFailed(_device->CreateInputLayout(postProcessInputLayoutItems, 3, + _sPostProcess.Vertex.Blob->GetBufferPointer(), _sPostProcess.Vertex.Blob->GetBufferSize(), &_fullscreenTriangleInputLayout)); + + _sPostProcessMonochrome = CompileOrLoadShader("PostProcess", "Monochrome", ShaderType::Pixel); + _sPostProcessNegative = CompileOrLoadShader("PostProcess", "Negative", ShaderType::Pixel); + _sPostProcessExclusion = CompileOrLoadShader("PostProcess", "Exclusion", ShaderType::Pixel); + _sPostProcessFinalPass = CompileOrLoadShader("PostProcess", "FinalPass", ShaderType::Pixel); + _sPostProcessLensFlare = CompileOrLoadShader("PostProcess", "LensFlare", ShaderType::Pixel); } void Renderer::CreateSSAONoiseTexture() @@ -597,16 +588,12 @@ namespace TEN::Renderer auto null = D3D10_SHADER_MACRO{ nullptr, nullptr }; defines.push_back(null); - auto blob = ComPtr{}; - _SMAALumaEdgeDetectionPS = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\SMAA.fx"), "DX11_SMAALumaEdgeDetectionPS", "ps_5_0", defines.data(), blob); - _SMAAColorEdgeDetectionPS = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\SMAA.fx"), "DX11_SMAAColorEdgeDetectionPS", "ps_5_0", defines.data(), blob); - _SMAADepthEdgeDetectionPS = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\SMAA.fx"), "DX11_SMAADepthEdgeDetectionPS", "ps_5_0", defines.data(), blob); - _SMAABlendingWeightCalculationPS = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\SMAA.fx"), "DX11_SMAABlendingWeightCalculationPS", "ps_5_0", defines.data(), blob); - _SMAANeighborhoodBlendingPS = Utils::compilePixelShader(_device.Get(), GetAssetPath(L"Shaders\\SMAA.fx"), "DX11_SMAANeighborhoodBlendingPS", "ps_5_0", defines.data(), blob); - _SMAAEdgeDetectionVS = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\SMAA.fx"), "DX11_SMAAEdgeDetectionVS", "vs_5_0", defines.data(), blob); - _SMAABlendingWeightCalculationVS = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\SMAA.fx"), "DX11_SMAABlendingWeightCalculationVS", "vs_5_0", defines.data(), blob); - _SMAANeighborhoodBlendingVS = Utils::compileVertexShader(_device.Get(), GetAssetPath(L"Shaders\\SMAA.fx"), "DX11_SMAANeighborhoodBlendingVS", "vs_5_0", defines.data(), blob); - + _sSMAALumaEdgeDetection = CompileOrLoadShader("SMAA", "LumaEdgeDetection", ShaderType::Pixel, defines.data()); + _sSMAAColorEdgeDetection = CompileOrLoadShader("SMAA", "ColorEdgeDetection", ShaderType::Pixel, defines.data()); + _sSMAADepthEdgeDetection = CompileOrLoadShader("SMAA", "DepthEdgeDetection", ShaderType::Pixel, defines.data()); + _sSMAABlendingWeightCalculation = CompileOrLoadShader("SMAA", "BlendingWeightCalculation", ShaderType::PixelAndVertex, defines.data()); + _sSMAANeighborhoodBlending = CompileOrLoadShader("SMAA", "NeighborhoodBlending", ShaderType::PixelAndVertex, defines.data()); + _sSMAAEdgeDetection = CompileOrLoadShader("SMAA", "EdgeDetection", ShaderType::Vertex, defines.data()); } void Renderer::InitializeCommonTextures() @@ -688,4 +675,152 @@ namespace TEN::Renderer UpdateWindow(WindowsHandle); } + + void Renderer::BindShader(const RendererShader& shader) + { + if (shader.Vertex.Shader != nullptr) _context->VSSetShader(shader.Vertex.Shader.Get(), nullptr, 0); + if (shader.Pixel.Shader != nullptr) _context->PSSetShader(shader.Pixel.Shader.Get(), nullptr, 0); + if (shader.Compute.Shader != nullptr) _context->CSSetShader(shader.Compute.Shader.Get(), nullptr, 0); + } + + RendererShader Renderer::CompileOrLoadShader(const std::string& fileName, const std::string& funcName, ShaderType type, const D3D_SHADER_MACRO* defines) + { + RendererShader result = {}; + + // We need to increment the counter to avoid overwriting compiled shaders with the same source file name. + static int compileCounter = 0; + + // Define paths for native (uncompiled) shaders and compiled shaders. + std::wstring shaderPath = GetAssetPath(L"Shaders\\"); + std::wstring compiledShaderPath = shaderPath + L"Bin\\"; + std::wstring wideFileName = TEN::Utils::ToWString(fileName); + + // Ensure the /Bin subdirectory exists. + std::filesystem::create_directories(compiledShaderPath); + + // Helper function to load or compile a shader. + auto loadOrCompileShader = [this, type, defines, shaderPath, compiledShaderPath] + (const std::wstring& baseFileName, const std::string& shaderType, const std::string& functionName, const char* model, ComPtr& bytecode) + { + // Construct the full paths using GetAssetPath. + auto prefix = ((compileCounter < 10) ? L"0" : L"") + std::to_wstring(compileCounter) + L"_"; + auto csoFileName = compiledShaderPath + prefix + baseFileName + L"." + std::wstring(shaderType.begin(), shaderType.end()) + L".cso"; + auto srcFileName = shaderPath + baseFileName; + + // Try both .hlsl and .fx extensions for the source shader. + auto srcFileNameWithExtension = srcFileName + L".hlsl"; + if (!std::filesystem::exists(srcFileNameWithExtension)) + { + srcFileNameWithExtension = srcFileName + L".fx"; + if (!std::filesystem::exists(srcFileNameWithExtension)) + { + TENLog("Shader source file not found: " + TEN::Utils::ToString(srcFileNameWithExtension), LogLevel::Error); + throw std::runtime_error("Shader source file not found"); + } + } + + // Check modification dates of the source and compiled files. + bool shouldRecompile = false; + if (std::filesystem::exists(csoFileName)) + { + auto csoTime = std::filesystem::last_write_time(csoFileName); + auto srcTime = std::filesystem::last_write_time(srcFileNameWithExtension); + shouldRecompile = srcTime > csoTime; // Recompile if the source is newer. + } + + // Load compiled shader if it exists and is up to date. + if (!shouldRecompile) + { + std::ifstream csoFile(csoFileName, std::ios::binary); + + if (csoFile.is_open()) + { + // Load compiled shader. + csoFile.seekg(0, std::ios::end); + size_t fileSize = csoFile.tellg(); + csoFile.seekg(0, std::ios::beg); + + std::vector buffer(fileSize); + csoFile.read(buffer.data(), fileSize); + csoFile.close(); + + D3DCreateBlob(fileSize, &bytecode); + memcpy(bytecode->GetBufferPointer(), buffer.data(), fileSize); + + return; + } + } + + // Set up compilation flags according to the build configuration. + unsigned int flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_PACK_MATRIX_ROW_MAJOR; + + if constexpr (DebugBuild) + flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; + else + flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_IEEE_STRICTNESS; + + auto trimmedFileName = std::filesystem::path(srcFileNameWithExtension).filename().string(); + TENLog("Compiling shader: " + trimmedFileName, LogLevel::Info); + + // Compile shader. + ComPtr errors; + HRESULT res = D3DCompileFromFile(srcFileNameWithExtension.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, + (shaderType + functionName).c_str(), model, flags, 0, bytecode.GetAddressOf(), errors.GetAddressOf()); + + if (FAILED(res)) + { + if (errors) + { + auto error = std::string(static_cast(errors->GetBufferPointer())); + TENLog(error, LogLevel::Error); + throw std::runtime_error(error); + } + else + { + TENLog("Error while compiling shader: " + trimmedFileName, LogLevel::Error); + throwIfFailed(res); + } + } + + // Save compiled shader to .cso file. + std::ofstream outCsoFile(csoFileName, std::ios::binary); + if (outCsoFile.is_open()) + { + outCsoFile.write(reinterpret_cast(bytecode->GetBufferPointer()), bytecode->GetBufferSize()); + outCsoFile.close(); + } + }; + + // Load or compile and create pixel shader. + if (type == ShaderType::Pixel || type == ShaderType::PixelAndVertex) + { + loadOrCompileShader(wideFileName, "PS", funcName, "ps_5_0", result.Pixel.Blob); + throwIfFailed(_device->CreatePixelShader(result.Pixel.Blob->GetBufferPointer(), result.Pixel.Blob->GetBufferSize(), + nullptr, result.Pixel.Shader.GetAddressOf() + )); + } + + // Load or compile and create vertex shader. + if (type == ShaderType::Vertex || type == ShaderType::PixelAndVertex) + { + loadOrCompileShader(wideFileName, "VS", funcName, "vs_5_0", result.Vertex.Blob); + throwIfFailed(_device->CreateVertexShader(result.Vertex.Blob->GetBufferPointer(), result.Vertex.Blob->GetBufferSize(), + nullptr, result.Vertex.Shader.GetAddressOf() + )); + } + + // Load or compile and create compute shader. + if (type == ShaderType::Compute) + { + loadOrCompileShader(wideFileName, "CS", funcName, "cs_5_0", result.Compute.Blob); + throwIfFailed(_device->CreateComputeShader(result.Compute.Blob->GetBufferPointer(), result.Compute.Blob->GetBufferSize(), + nullptr, result.Compute.Shader.GetAddressOf() + )); + } + + // Increment the compile counter. + compileCounter++; + + return result; + } } \ No newline at end of file diff --git a/TombEngine/Renderer/RendererLara.cpp b/TombEngine/Renderer/RendererLara.cpp index 64fedd83f..7b825d2c5 100644 --- a/TombEngine/Renderer/RendererLara.cpp +++ b/TombEngine/Renderer/RendererLara.cpp @@ -111,10 +111,19 @@ void Renderer::UpdateLaraAnimations(bool force) auto frameData = GetFrameInterpData(*LaraItem); UpdateAnimation(&rItem, playerObject, frameData, mask); + auto gunType = Lara.Control.Weapon.GunType; + auto handStatus = Lara.Control.HandStatus; + + // HACK: Treat binoculars as two-handed weapon. + if (Lara.Control.Look.IsUsingBinoculars) + { + gunType = LaraWeaponType::Shotgun; + handStatus = HandStatus::WeaponReady; + } + // Then the arms, based on current weapon status. - if (Lara.Control.Weapon.GunType != LaraWeaponType::Flare && - (Lara.Control.HandStatus == HandStatus::Free || Lara.Control.HandStatus == HandStatus::Busy) || - Lara.Control.Weapon.GunType == LaraWeaponType::Flare && !Lara.Flare.ControlLeft) + if (gunType != LaraWeaponType::Flare && (handStatus == HandStatus::Free || handStatus == HandStatus::Busy) || + gunType == LaraWeaponType::Flare && !Lara.Flare.ControlLeft) { // Both arms mask = MESH_BITS(LM_LINARM) | MESH_BITS(LM_LOUTARM) | MESH_BITS(LM_LHAND) | MESH_BITS(LM_RINARM) | MESH_BITS(LM_ROUTARM) | MESH_BITS(LM_RHAND); @@ -124,15 +133,14 @@ void Renderer::UpdateLaraAnimations(bool force) else { // While handling weapon, extra rotation may be applied to arms. - if (Lara.Control.Weapon.GunType == LaraWeaponType::Pistol || - Lara.Control.Weapon.GunType == LaraWeaponType::Uzi) + if (gunType == LaraWeaponType::Revolver) { - playerObject.LinearizedBones[LM_LINARM]->ExtraRotation *= Lara.LeftArm.Orientation.ToQuaternion(); + playerObject.LinearizedBones[LM_LINARM]->ExtraRotation = playerObject.LinearizedBones[LM_RINARM]->ExtraRotation *= Lara.RightArm.Orientation.ToQuaternion(); } else { - playerObject.LinearizedBones[LM_LINARM]->ExtraRotation = + playerObject.LinearizedBones[LM_LINARM]->ExtraRotation *= Lara.LeftArm.Orientation.ToQuaternion(); playerObject.LinearizedBones[LM_RINARM]->ExtraRotation *= Lara.RightArm.Orientation.ToQuaternion(); } @@ -140,7 +148,7 @@ void Renderer::UpdateLaraAnimations(bool force) ArmInfo* rightArm = &Lara.RightArm; // HACK: Back guns are handled differently. - switch (Lara.Control.Weapon.GunType) + switch (gunType) { case LaraWeaponType::Shotgun: case LaraWeaponType::HK: @@ -152,7 +160,7 @@ void Renderer::UpdateLaraAnimations(bool force) // Left arm mask = MESH_BITS(LM_LINARM) | MESH_BITS(LM_LOUTARM) | MESH_BITS(LM_LHAND); - if (shouldAnimateUpperBody(Lara.Control.Weapon.GunType)) + if (shouldAnimateUpperBody(gunType)) mask |= MESH_BITS(LM_TORSO) | MESH_BITS(LM_HEAD); auto shotgunFrameData = AnimFrameInterpData @@ -278,14 +286,11 @@ void Renderer::UpdateLaraAnimations(bool force) rItem.DoneAnimations = true; } -void TEN::Renderer::Renderer::DrawLara(RenderView& view, RendererPass rendererPass) +void Renderer::DrawLara(RenderView& view, RendererPass rendererPass) { - // Don't draw player if using optics. - if (Lara.Control.Look.OpticRange != 0 || SpotcamDontDrawLara) - return; - - // Don't draw player if on title level and disabled. - if (CurrentLevel == 0 && !g_GameFlow->IsLaraInTitleEnabled()) + // TODO: Avoid Lara global. + // Don't draw player if using optics (but still draw reflections). + if (Lara.Control.Look.OpticRange != 0 && _currentMirror == nullptr) return; auto* item = &_items[LaraItem->Index]; @@ -305,7 +310,9 @@ void TEN::Renderer::Renderer::DrawLara(RenderView& view, RendererPass rendererPa RendererRoom* room = &_rooms[LaraItem->RoomNumber]; - _stItem.World = item->InterpolatedWorld; // _laraWorldMatrix; + _stItem.World = item->InterpolatedWorld; + ReflectMatrixOptionally(_stItem.World); + _stItem.Color = item->Color; _stItem.AmbientLight = item->AmbientLight; memcpy(_stItem.BonesMatrices, item->InterpolatedAnimTransforms, laraObj.AnimationTransforms.size() * sizeof(Matrix)); @@ -313,7 +320,9 @@ void TEN::Renderer::Renderer::DrawLara(RenderView& view, RendererPass rendererPa { _stItem.BoneLightModes[k] = (int)GetMesh(nativeItem->Model.MeshIndex[k])->LightMode; } - BindMoveableLights(item->LightsToDraw, item->RoomNumber, item->PrevRoomNumber, item->LightFade); + + bool acceptsShadows = laraObj.ShadowType == ShadowMode::None; + BindMoveableLights(item->LightsToDraw, item->RoomNumber, item->PrevRoomNumber, item->LightFade, acceptsShadows); _cbItem.UpdateData(_stItem, _context.Get()); for (int k = 0; k < laraSkin.ObjectMeshes.size(); k++) @@ -347,6 +356,7 @@ void Renderer::DrawLaraHair(RendererItem* itemToDraw, RendererRoom* room, Render _stItem.World = Matrix::Identity; _stItem.BonesMatrices[0] = itemToDraw->InterpolatedAnimTransforms[HairUnit::GetRootMeshID(i)] * itemToDraw->InterpolatedWorld; + ReflectMatrixOptionally(_stItem.BonesMatrices[0]); for (int i = 0; i < unit.Segments.size(); i++) { @@ -356,6 +366,8 @@ void Renderer::DrawLaraHair(RendererItem* itemToDraw, RendererRoom* room, Render Quaternion::Lerp(segment.PrevOrientation, segment.Orientation, GetInterpolationFactor(forceValue))) * Matrix::CreateTranslation( Vector3::Lerp(segment.PrevPosition, segment.Position, GetInterpolationFactor(forceValue))); + + ReflectMatrixOptionally(worldMatrix); _stItem.BonesMatrices[i + 1] = worldMatrix; _stItem.BoneLightModes[i] = (int)LightMode::Dynamic; diff --git a/TombEngine/Renderer/RendererPostProcess.cpp b/TombEngine/Renderer/RendererPostProcess.cpp index 656a6e90b..91370ef08 100644 --- a/TombEngine/Renderer/RendererPostProcess.cpp +++ b/TombEngine/Renderer/RendererPostProcess.cpp @@ -6,6 +6,24 @@ namespace TEN::Renderer { void Renderer::DrawPostprocess(RenderTarget2D* renderTarget, RenderView& view, SceneRenderMode renderMode) { + _doingFullscreenPass = true; + + // Apply antialiasing. + switch (g_Configuration.AntialiasingMode) + { + case AntialiasingMode::None: + break; + + case AntialiasingMode::Low: + ApplyFXAA(&_renderTarget, view); + break; + + case AntialiasingMode::Medium: + case AntialiasingMode::High: + ApplySMAA(&_renderTarget, view); + break; + } + SetBlendMode(BlendMode::Opaque); SetCullMode(CullMode::CounterClockwise); SetDepthState(DepthState::Write); @@ -24,7 +42,7 @@ namespace TEN::Renderer _cbPostProcessBuffer.UpdateData(_stPostProcessBuffer, _context.Get()); // Common vertex shader to all fullscreen effects. - _context->VSSetShader(_vsPostProcess.Get(), nullptr, 0); + BindShader(_sPostProcess); // Draw fullscreen triangle. _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); @@ -40,7 +58,6 @@ namespace TEN::Renderer _context->ClearRenderTargetView(_postProcessRenderTarget[0].RenderTargetView.Get(), clearColor); _context->OMSetRenderTargets(1, _postProcessRenderTarget[0].RenderTargetView.GetAddressOf(), nullptr); - _context->PSSetShader(_psPostProcessCopy.Get(), nullptr, 0); BindRenderTargetAsTexture(TextureRegister::ColorMap, &_renderTarget, SamplerStateRegister::PointWrap); DrawTriangles(3, 0); @@ -48,44 +65,13 @@ namespace TEN::Renderer int currentRenderTarget = 0; int destRenderTarget = 1; - // Apply color scheme. - if (_postProcessMode != PostProcessMode::None && _postProcessStrength > EPSILON) - { - _context->ClearRenderTargetView(_postProcessRenderTarget[destRenderTarget].RenderTargetView.Get(), clearColor); - _context->OMSetRenderTargets(1, _postProcessRenderTarget[destRenderTarget].RenderTargetView.GetAddressOf(), nullptr); - - switch (_postProcessMode) - { - case PostProcessMode::Monochrome: - _context->PSSetShader(_psPostProcessMonochrome.Get(), nullptr, 0); - break; - - case PostProcessMode::Negative: - _context->PSSetShader(_psPostProcessNegative.Get(), nullptr, 0); - break; - - case PostProcessMode::Exclusion: - _context->PSSetShader(_psPostProcessExclusion.Get(), nullptr, 0); - break; - - default: - return; - } - - BindRenderTargetAsTexture(TextureRegister::ColorMap, &_postProcessRenderTarget[currentRenderTarget], SamplerStateRegister::PointWrap); - DrawTriangles(3, 0); - - destRenderTarget = (destRenderTarget == 1) ? 0 : 1; - currentRenderTarget = (currentRenderTarget == 1) ? 0 : 1; - } - // Lens flares. if (!view.LensFlaresToDraw.empty()) { _context->ClearRenderTargetView(_postProcessRenderTarget[destRenderTarget].RenderTargetView.Get(), clearColor); _context->OMSetRenderTargets(1, _postProcessRenderTarget[destRenderTarget].RenderTargetView.GetAddressOf(), nullptr); - _context->PSSetShader(_psPostProcessLensFlare.Get(), nullptr, 0); + BindShader(_sPostProcessLensFlare); for (int i = 0; i < view.LensFlaresToDraw.size(); i++) { @@ -102,8 +88,39 @@ namespace TEN::Renderer currentRenderTarget = (currentRenderTarget == 1) ? 0 : 1; } + // Apply color scheme. + if (_postProcessMode != PostProcessMode::None && _postProcessStrength > EPSILON) + { + _context->ClearRenderTargetView(_postProcessRenderTarget[destRenderTarget].RenderTargetView.Get(), clearColor); + _context->OMSetRenderTargets(1, _postProcessRenderTarget[destRenderTarget].RenderTargetView.GetAddressOf(), nullptr); + + switch (_postProcessMode) + { + case PostProcessMode::Monochrome: + BindShader(_sPostProcessMonochrome); + break; + + case PostProcessMode::Negative: + BindShader(_sPostProcessNegative); + break; + + case PostProcessMode::Exclusion: + BindShader(_sPostProcessExclusion); + break; + + default: + return; + } + + BindRenderTargetAsTexture(TextureRegister::ColorMap, &_postProcessRenderTarget[currentRenderTarget], SamplerStateRegister::PointWrap); + DrawTriangles(3, 0); + + destRenderTarget = (destRenderTarget == 1) ? 0 : 1; + currentRenderTarget = (currentRenderTarget == 1) ? 0 : 1; + } + // Do final pass. - _context->PSSetShader(_psPostProcessFinalPass.Get(), nullptr, 0); + BindShader(_sPostProcessFinalPass); _context->ClearRenderTargetView(renderTarget->RenderTargetView.Get(), Colors::Black); _context->OMSetRenderTargets(1, renderTarget->RenderTargetView.GetAddressOf(), nullptr); @@ -111,6 +128,8 @@ namespace TEN::Renderer BindTexture(TextureRegister::ColorMap, &_postProcessRenderTarget[currentRenderTarget], SamplerStateRegister::PointWrap); DrawTriangles(3, 0); + + _doingFullscreenPass = false; } PostProcessMode Renderer::GetPostProcessMode() @@ -152,7 +171,7 @@ namespace TEN::Renderer ResetScissor(); // Common vertex shader to all fullscreen effects - _context->VSSetShader(_vsPostProcess.Get(), nullptr, 0); + BindShader(_sPostProcess); // We draw a fullscreen triangle _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); @@ -167,7 +186,6 @@ namespace TEN::Renderer _context->ClearRenderTargetView(dest->RenderTargetView.Get(), clearColor); _context->OMSetRenderTargets(1, dest->RenderTargetView.GetAddressOf(), nullptr); - _context->PSSetShader(_psPostProcessCopy.Get(), nullptr, 0); BindRenderTargetAsTexture(TextureRegister::ColorMap, source, SamplerStateRegister::PointWrap); DrawTriangles(3, 0); } diff --git a/TombEngine/Renderer/RendererSprites.cpp b/TombEngine/Renderer/RendererSprites.cpp index 417155097..a4cc72c7c 100644 --- a/TombEngine/Renderer/RendererSprites.cpp +++ b/TombEngine/Renderer/RendererSprites.cpp @@ -243,7 +243,6 @@ namespace TEN::Renderer object.Centre = rDrawSprite.pos; object.Distance = distance; object.Sprite = &rDrawSprite; - object.World = GetWorldMatrixForSprite(&rDrawSprite, view); view.TransparentObjectsToDraw.push_back(object); } @@ -280,8 +279,7 @@ namespace TEN::Renderer SetDepthState(DepthState::Read); SetCullMode(CullMode::None); - _context->VSSetShader(_vsInstancedSprites.Get(), nullptr, 0); - _context->PSSetShader(_psInstancedSprites.Get(), nullptr, 0); + BindShader(_sInstancedSprites); // Set up vertex buffer and parameters. unsigned int stride = sizeof(Vertex); @@ -342,8 +340,7 @@ namespace TEN::Renderer SetDepthState(DepthState::Read); SetCullMode(CullMode::None); - _context->VSSetShader(_vsSprites.Get(), nullptr, 0); - _context->PSSetShader(_psSprites.Get(), nullptr, 0); + BindShader(_sSprites); wasGPUSet = true; } @@ -363,21 +360,29 @@ namespace TEN::Renderer vertex0.UV = rDrawSprite.Sprite->UV[0]; vertex0.Color = rDrawSprite.c1; + ReflectVectorOptionally(vertex0.Position); + auto vertex1 = Vertex{}; vertex1.Position = rDrawSprite.vtx2; vertex1.UV = rDrawSprite.Sprite->UV[1]; vertex1.Color = rDrawSprite.c2; + ReflectVectorOptionally(vertex1.Position); + auto vertex2 = Vertex{}; vertex2.Position = rDrawSprite.vtx3; vertex2.UV = rDrawSprite.Sprite->UV[2]; vertex2.Color = rDrawSprite.c3; + ReflectVectorOptionally(vertex2.Position); + auto vertex3 = Vertex{}; vertex3.Position = rDrawSprite.vtx4; vertex3.UV = rDrawSprite.Sprite->UV[3]; vertex3.Color = rDrawSprite.c4; + ReflectVectorOptionally(vertex3.Position); + _primitiveBatch->DrawTriangle(vertex0, vertex1, vertex3); _primitiveBatch->DrawTriangle(vertex1, vertex2, vertex3); @@ -406,8 +411,7 @@ namespace TEN::Renderer SetBlendMode(object->Sprite->BlendMode); SetAlphaTest(AlphaTestMode::GreatherThan, ALPHA_TEST_THRESHOLD); - _context->VSSetShader(_vsInstancedSprites.Get(), nullptr, 0); - _context->PSSetShader(_psInstancedSprites.Get(), nullptr, 0); + BindShader(_sInstancedSprites); // Set up vertex buffer and parameters. UINT stride = sizeof(Vertex); @@ -452,8 +456,7 @@ namespace TEN::Renderer SetBlendMode(object->Sprite->BlendMode); SetAlphaTest(AlphaTestMode::GreatherThan, ALPHA_TEST_THRESHOLD); - _context->VSSetShader(_vsSprites.Get(), nullptr, 0); - _context->PSSetShader(_psSprites.Get(), nullptr, 0); + BindShader(_sSprites); _stSprite.IsSoftParticle = object->Sprite->SoftParticle ? 1 : 0; _stSprite.RenderType = (int)object->Sprite->renderType; @@ -498,8 +501,7 @@ namespace TEN::Renderer UINT stride = sizeof(Vertex); UINT offset = 0; - _context->VSSetShader(_vsSprites.Get(), nullptr, 0); - _context->PSSetShader(_psSprites.Get(), nullptr, 0); + BindShader(_sSprites); _sortedPolygonsVertexBuffer.Update(_context.Get(), _sortedPolygonsVertices.data(), 0, (int)_sortedPolygonsVertices.size()); diff --git a/TombEngine/Renderer/RendererUtils.cpp b/TombEngine/Renderer/RendererUtils.cpp index b6601bbe2..ae6b6a030 100644 --- a/TombEngine/Renderer/RendererUtils.cpp +++ b/TombEngine/Renderer/RendererUtils.cpp @@ -1,20 +1,18 @@ #include "framework.h" + #include #include #include #include #include #include + #include "Renderer/Renderer.h" #include "Specific/trutils.h" +#include "Structures/RendererShader.h" namespace TEN::Renderer::Utils { - using std::wstring; - using std::string; - using Microsoft::WRL::ComPtr; - using std::vector; - void throwIfFailed(const HRESULT& res) { if (FAILED(res)) @@ -44,119 +42,4 @@ namespace TEN::Renderer::Utils throw std::runtime_error("An error occured!"); } } - - ComPtr compileVertexShader(ID3D11Device* device, const std::wstring& fileName, const std::string& function, const std::string& model, const D3D_SHADER_MACRO * defines, ComPtr& bytecode) - { - ComPtr errors; - HRESULT res = (D3DCompileFromFile(fileName.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, function.c_str(), model.c_str(), GetShaderFlags(), 0, bytecode.GetAddressOf(),errors.GetAddressOf())); - if (FAILED(res)) - { - ID3D10Blob* errorObj = errors.Get(); - if (errorObj != nullptr) - { - auto error = std::string((char*)errorObj->GetBufferPointer()); - TENLog(error, LogLevel::Error); - throw std::runtime_error(error); - } - else - { - TENLog("Error while compiling VS shader: " + TEN::Utils::ToString(fileName.c_str()), LogLevel::Error); - throwIfFailed(res); - } - } - - ComPtr shader; - throwIfFailed(device->CreateVertexShader(bytecode->GetBufferPointer(), bytecode->GetBufferSize(), nullptr, shader.GetAddressOf())); - - if constexpr (DebugBuild) - { - char buffer[100]; - unsigned int size = (unsigned int)std::wcstombs(buffer, fileName.c_str(), 100); - shader->SetPrivateData(WKPDID_D3DDebugObjectName, size, buffer); - } - - return shader; - } - - ComPtr compilePixelShader(ID3D11Device* device, const wstring& fileName, const string& function, const string& model, const D3D_SHADER_MACRO* defines, ComPtr& bytecode) - { - ComPtr errors; - HRESULT res = (D3DCompileFromFile(fileName.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, function.c_str(), model.c_str(), GetShaderFlags(), 0, bytecode.GetAddressOf(), errors.GetAddressOf())); - if (FAILED(res)) - { - ID3D10Blob* errorObj = errors.Get(); - if (errorObj != nullptr) - { - auto error = std::string((char*)errorObj->GetBufferPointer()); - TENLog(error, LogLevel::Error); - throw std::runtime_error(error); - } - else - { - TENLog("Error while compiling PS shader: " + TEN::Utils::ToString(fileName.c_str()), LogLevel::Error); - throwIfFailed(res); - } - } - - ComPtr shader; - throwIfFailed(device->CreatePixelShader(bytecode->GetBufferPointer(), bytecode->GetBufferSize(), nullptr, shader.GetAddressOf())); - - if constexpr (DebugBuild) - { - char buffer[100]; - unsigned int size = (unsigned int)std::wcstombs(buffer, fileName.c_str(), 100); - shader->SetPrivateData(WKPDID_D3DDebugObjectName, size, buffer); - } - - return shader; - } - - ComPtr compileComputeShader(ID3D11Device* device, const wstring& fileName, const string& function, const string& model, const D3D_SHADER_MACRO* defines, ComPtr& bytecode) - { - auto errors = ComPtr{}; - auto res = D3DCompileFromFile(fileName.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, function.c_str(), model.c_str(), GetShaderFlags(), 0, bytecode.GetAddressOf(), errors.GetAddressOf()); - if (FAILED(res)) - { - auto* errorObj = errors.Get(); - if (errorObj != nullptr) - { - auto error = std::string((char*)errorObj->GetBufferPointer()); - TENLog(error, LogLevel::Error); - throw std::runtime_error(error); - } - else - { - TENLog("Error while compiling CS shader: " + TEN::Utils::ToString(fileName.c_str()), LogLevel::Error); - throwIfFailed(res); - } - } - - auto shader = ComPtr{}; - throwIfFailed(device->CreateComputeShader(bytecode->GetBufferPointer(), bytecode->GetBufferSize(), nullptr, shader.GetAddressOf())); - - if constexpr (DebugBuild) - { - char buffer[100]; - unsigned int size = (unsigned int)std::wcstombs(buffer, fileName.c_str(), 100); - shader->SetPrivateData(WKPDID_D3DDebugObjectName, size, buffer); - } - - return shader; - } - - constexpr unsigned int Utils::GetShaderFlags() - { - unsigned int flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_PACK_MATRIX_ROW_MAJOR; - - if constexpr (DebugBuild) - { - flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; - } - else - { - flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_IEEE_STRICTNESS; - } - - return flags; - } } diff --git a/TombEngine/Renderer/RendererUtils.h b/TombEngine/Renderer/RendererUtils.h index 2efd92ba2..8fc12b203 100644 --- a/TombEngine/Renderer/RendererUtils.h +++ b/TombEngine/Renderer/RendererUtils.h @@ -9,9 +9,4 @@ namespace TEN::Renderer::Utils void throwIfFailed(const HRESULT& res); void throwIfFailed(const HRESULT& res, const std::string& info); void throwIfFailed(const HRESULT& res, const std::wstring& info); - - [[nodiscard]] Microsoft::WRL::ComPtr compileVertexShader(ID3D11Device* device, const std::wstring& fileName, const std::string& function, const std::string& model, const D3D_SHADER_MACRO* defines, Microsoft::WRL::ComPtr& bytecode); - constexpr [[nodiscard]] unsigned int GetShaderFlags(); - [[nodiscard]] Microsoft::WRL::ComPtr compilePixelShader(ID3D11Device* device, const std::wstring& fileName, const std::string& function, const std::string& model, const D3D_SHADER_MACRO* defines, Microsoft::WRL::ComPtr& bytecode); - [[nodiscard]] Microsoft::WRL::ComPtr compileComputeShader(ID3D11Device* device, const std::wstring& fileName, const std::string& function, const std::string& model, const D3D_SHADER_MACRO* defines, Microsoft::WRL::ComPtr& bytecode); } diff --git a/TombEngine/Renderer/Structures/RendererMirror.h b/TombEngine/Renderer/Structures/RendererMirror.h new file mode 100644 index 000000000..c361046b2 --- /dev/null +++ b/TombEngine/Renderer/Structures/RendererMirror.h @@ -0,0 +1,17 @@ +#pragma once + +namespace TEN::Renderer::Structures +{ + struct RendererMirror + { + int RoomNumber = 0; + Plane Plane = SimpleMath::Plane(); + Matrix ReflectionMatrix = Matrix::Identity; + + bool ReflectPlayer = false; + bool ReflectMoveables = false; + bool ReflectStatics = false; + bool ReflectSprites = false; + bool ReflectLights = false; + }; +} diff --git a/TombEngine/Renderer/Structures/RendererShader.h b/TombEngine/Renderer/Structures/RendererShader.h new file mode 100644 index 000000000..ec865bf33 --- /dev/null +++ b/TombEngine/Renderer/Structures/RendererShader.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include + +using Microsoft::WRL::ComPtr; + +namespace TEN::Renderer::Structures +{ + enum class ShaderType + { + Pixel, + Vertex, + PixelAndVertex, + Compute + }; + + struct RendererPixelShaderAndBlob + { + ComPtr Shader = nullptr; + ComPtr Blob = nullptr; + }; + + struct RendererVertexShaderAndBlob + { + ComPtr Shader = nullptr; + ComPtr Blob = nullptr; + }; + + struct RendererComputeShaderAndBlob + { + ComPtr Shader = nullptr; + ComPtr Blob = nullptr; + }; + + struct RendererShader + { + RendererPixelShaderAndBlob Pixel = {}; + RendererVertexShaderAndBlob Vertex = {}; + RendererComputeShaderAndBlob Compute = {}; + }; +} diff --git a/TombEngine/Resources/title.bin b/TombEngine/Resources/title.bin index 9475b7db0..2ec566646 100644 Binary files a/TombEngine/Resources/title.bin and b/TombEngine/Resources/title.bin differ diff --git a/TombEngine/Scripting/Internal/TEN/Objects/ObjectIDs.h b/TombEngine/Scripting/Internal/TEN/Objects/ObjectIDs.h index 2f1e2e899..564b99b21 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/ObjectIDs.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/ObjectIDs.h @@ -45,9 +45,9 @@ The following constants are inside ObjID. ACTOR1_SPEECH_HEAD2 ACTOR2_SPEECH_HEAD1 ACTOR2_SPEECH_HEAD2 + LARA_BINOCULARS_MESH LARA_EXTRA_MESH1 LARA_EXTRA_MESH2 - LARA_EXTRA_MESH3 LARA_WATER_MESH LARA_PETROL_MESH LARA_DIRT_MESH @@ -1227,9 +1227,9 @@ static const std::unordered_map kObjIDs { { "ACTOR1_SPEECH_HEAD2", ID_ACTOR1_SPEECH_HEAD2 }, { "ACTOR2_SPEECH_HEAD1", ID_ACTOR2_SPEECH_HEAD1 }, { "ACTOR2_SPEECH_HEAD2", ID_ACTOR2_SPEECH_HEAD2 }, + { "LARA_BINOCULARS_MESH", ID_LARA_BINOCULARS_MESH }, { "LARA_EXTRA_MESH1", ID_LARA_EXTRA_MESH1 }, { "LARA_EXTRA_MESH2", ID_LARA_EXTRA_MESH2 }, - { "LARA_EXTRA_MESH3", ID_LARA_EXTRA_MESH3 }, { "LARA_WATER_MESH", ID_LARA_WATER_MESH }, { "LARA_PETROL_MESH", ID_LARA_PETROL_MESH }, { "LARA_DIRT_MESH", ID_LARA_DIRT_MESH }, diff --git a/TombEngine/Shaders/HUD.hlsl b/TombEngine/Shaders/HUD.fx similarity index 100% rename from TombEngine/Shaders/HUD.hlsl rename to TombEngine/Shaders/HUD.fx diff --git a/TombEngine/Shaders/Items.fx b/TombEngine/Shaders/Items.fx index 1b493ae65..05e122f6a 100644 --- a/TombEngine/Shaders/Items.fx +++ b/TombEngine/Shaders/Items.fx @@ -155,7 +155,9 @@ PixelShaderOutput PS(PixelShaderInput input) input.FogBulbs.w) : StaticLight(input.Color.xyz, tex.xyz, input.FogBulbs.w); - color = DoShadow(input.WorldPosition, normal, color, -0.5f); + float shadowable = step(0.5f, float((NumItemLights & SHADOWABLE_MASK) == SHADOWABLE_MASK)); + float3 shadow = DoShadow(input.WorldPosition, normal, color, -0.5f); + color = lerp(color, shadow, shadowable); output.Color = saturate(float4(color * occlusion, tex.w)); output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f)); diff --git a/TombEngine/Shaders/Math.hlsli b/TombEngine/Shaders/Math.hlsli index b82033c57..39a86bb0f 100644 --- a/TombEngine/Shaders/Math.hlsli +++ b/TombEngine/Shaders/Math.hlsli @@ -17,6 +17,8 @@ #define LT_MASK_SPOT (1 << (31 - LT_SPOT)) #define LT_MASK_SHADOW (1 << (31 - LT_SHADOW)) +#define SHADOWABLE_MASK (1 << 16) + #define MAX_LIGHTS_PER_ROOM 48 #define MAX_LIGHTS_PER_ITEM 8 #define MAX_FOG_BULBS 32 diff --git a/TombEngine/Shaders/PostProcess.fx b/TombEngine/Shaders/PostProcess.fx index fc66398a7..79c3ebef2 100644 --- a/TombEngine/Shaders/PostProcess.fx +++ b/TombEngine/Shaders/PostProcess.fx @@ -32,7 +32,7 @@ PixelShaderInput VS(VertexShaderInput input) return output; } -float4 PSCopy(PixelShaderInput input) : SV_Target +float4 PS(PixelShaderInput input) : SV_Target { return ColorTexture.Sample(ColorSampler, input.UV); } diff --git a/TombEngine/Shaders/SMAA.fx b/TombEngine/Shaders/SMAA.fx index 63b1c42e3..ff4af2432 100644 --- a/TombEngine/Shaders/SMAA.fx +++ b/TombEngine/Shaders/SMAA.fx @@ -139,7 +139,7 @@ Texture2D searchTex : register(t8); /** * Function wrappers */ -void DX11_SMAAEdgeDetectionVS(float3 position : POSITION, +void VSEdgeDetection(float3 position : POSITION, out float4 svPosition : SV_POSITION, inout float2 texcoord : TEXCOORD0, out float4 offset[3] : TEXCOORD1) { @@ -147,7 +147,7 @@ void DX11_SMAAEdgeDetectionVS(float3 position : POSITION, SMAAEdgeDetectionVS(texcoord, offset); } -void DX11_SMAABlendingWeightCalculationVS(float3 position : POSITION, +void VSBlendingWeightCalculation(float3 position : POSITION, out float4 svPosition : SV_POSITION, inout float2 texcoord : TEXCOORD0, out float2 pixcoord : TEXCOORD1, @@ -156,7 +156,7 @@ void DX11_SMAABlendingWeightCalculationVS(float3 position : POSITION, SMAABlendingWeightCalculationVS(texcoord, pixcoord, offset); } -void DX11_SMAANeighborhoodBlendingVS(float3 position : POSITION, +void VSNeighborhoodBlending(float3 position : POSITION, out float4 svPosition : SV_POSITION, inout float2 texcoord : TEXCOORD0, out float4 offset : TEXCOORD1) { @@ -164,19 +164,19 @@ void DX11_SMAANeighborhoodBlendingVS(float3 position : POSITION, SMAANeighborhoodBlendingVS(texcoord, offset); } -void DX11_SMAAResolveVS(float3 position : POSITION, +void VSResolve(float3 position : POSITION, out float4 svPosition : SV_POSITION, inout float2 texcoord : TEXCOORD0) { svPosition = float4(position, 1.0f); } -void DX11_SMAASeparateVS(float3 position : POSITION, +void VSSeparate(float3 position : POSITION, out float4 svPosition : SV_POSITION, inout float2 texcoord : TEXCOORD0) { svPosition = float4(position, 1.0f); } -float2 DX11_SMAALumaEdgeDetectionPS(float4 position : SV_POSITION, +float2 PSLumaEdgeDetection(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0, float4 offset[3] : TEXCOORD1) : SV_TARGET{ #if SMAA_PREDICATION @@ -186,7 +186,7 @@ return SMAALumaEdgeDetectionPS(texcoord, offset, colorTexGamma); #endif } -float2 DX11_SMAAColorEdgeDetectionPS(float4 position : SV_POSITION, +float2 PSColorEdgeDetection(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0, float4 offset[3] : TEXCOORD1) : SV_TARGET{ #if SMAA_PREDICATION @@ -196,20 +196,20 @@ return SMAAColorEdgeDetectionPS(texcoord, offset, colorTexGamma); #endif } -float2 DX11_SMAADepthEdgeDetectionPS(float4 position : SV_POSITION, +float2 PSDepthEdgeDetection(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0, float4 offset[3] : TEXCOORD1) : SV_TARGET{ return SMAADepthEdgeDetectionPS(texcoord, offset, depthTex); } -float4 DX11_SMAABlendingWeightCalculationPS(float4 position : SV_POSITION, +float4 PSBlendingWeightCalculation(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0, float2 pixcoord : TEXCOORD1, float4 offset[3] : TEXCOORD2) : SV_TARGET{ return SMAABlendingWeightCalculationPS(texcoord, pixcoord, offset, edgesTex, areaTex, searchTex, subsampleIndices); } -float4 DX11_SMAANeighborhoodBlendingPS(float4 position : SV_POSITION, +float4 PSNeighborhoodBlending(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0, float4 offset : TEXCOORD1) : SV_TARGET{ #if SMAA_REPROJECTION @@ -219,7 +219,7 @@ return SMAANeighborhoodBlendingPS(texcoord, offset, colorTex, blendTex); #endif } -float4 DX11_SMAAResolvePS(float4 position : SV_POSITION, +float4 PSResolve(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0) : SV_TARGET{ #if SMAA_REPROJECTION return SMAAResolvePS(texcoord, colorTex, colorTexPrev, velocityTex); @@ -228,7 +228,7 @@ return SMAAResolvePS(texcoord, colorTex, colorTexPrev); #endif } -void DX11_SMAASeparatePS(float4 position : SV_POSITION, +void PSSeparate(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0, out float4 target0 : SV_TARGET0, out float4 target1 : SV_TARGET1) { @@ -240,9 +240,9 @@ void DX11_SMAASeparatePS(float4 position : SV_POSITION, */ technique10 LumaEdgeDetection { pass LumaEdgeDetection { - SetVertexShader(CompileShader(vs_4_0, DX11_SMAAEdgeDetectionVS())); + SetVertexShader(CompileShader(vs_4_0, VSEdgeDetection())); SetGeometryShader(NULL); - SetPixelShader(CompileShader(PS_VERSION, DX11_SMAALumaEdgeDetectionPS())); + SetPixelShader(CompileShader(PS_VERSION, PSLumaEdgeDetection())); SetDepthStencilState(DisableDepthReplaceStencil, 1); SetBlendState(NoBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF); @@ -251,9 +251,9 @@ technique10 LumaEdgeDetection { technique10 ColorEdgeDetection { pass ColorEdgeDetection { - SetVertexShader(CompileShader(vs_4_0, DX11_SMAAEdgeDetectionVS())); + SetVertexShader(CompileShader(vs_4_0, VSEdgeDetection())); SetGeometryShader(NULL); - SetPixelShader(CompileShader(PS_VERSION, DX11_SMAAColorEdgeDetectionPS())); + SetPixelShader(CompileShader(PS_VERSION, PSColorEdgeDetection())); SetDepthStencilState(DisableDepthReplaceStencil, 1); SetBlendState(NoBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF); @@ -262,9 +262,9 @@ technique10 ColorEdgeDetection { technique10 DepthEdgeDetection { pass DepthEdgeDetection { - SetVertexShader(CompileShader(vs_4_0, DX11_SMAAEdgeDetectionVS())); + SetVertexShader(CompileShader(vs_4_0, VSEdgeDetection())); SetGeometryShader(NULL); - SetPixelShader(CompileShader(PS_VERSION, DX11_SMAADepthEdgeDetectionPS())); + SetPixelShader(CompileShader(PS_VERSION, PSDepthEdgeDetection())); SetDepthStencilState(DisableDepthReplaceStencil, 1); SetBlendState(NoBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF); @@ -276,9 +276,9 @@ technique10 DepthEdgeDetection { */ technique10 BlendingWeightCalculation { pass BlendingWeightCalculation { - SetVertexShader(CompileShader(vs_4_0, DX11_SMAABlendingWeightCalculationVS())); + SetVertexShader(CompileShader(vs_4_0, VSBlendingWeightCalculation())); SetGeometryShader(NULL); - SetPixelShader(CompileShader(PS_VERSION, DX11_SMAABlendingWeightCalculationPS())); + SetPixelShader(CompileShader(PS_VERSION, PSBlendingWeightCalculation())); SetDepthStencilState(DisableDepthUseStencil, 1); SetBlendState(NoBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF); @@ -290,9 +290,9 @@ technique10 BlendingWeightCalculation { */ technique10 NeighborhoodBlending { pass NeighborhoodBlending { - SetVertexShader(CompileShader(vs_4_0, DX11_SMAANeighborhoodBlendingVS())); + SetVertexShader(CompileShader(vs_4_0, VSNeighborhoodBlending())); SetGeometryShader(NULL); - SetPixelShader(CompileShader(PS_VERSION, DX11_SMAANeighborhoodBlendingPS())); + SetPixelShader(CompileShader(PS_VERSION, PSNeighborhoodBlending())); SetDepthStencilState(DisableDepthStencil, 0); SetBlendState(Blend, float4(blendFactor, blendFactor, blendFactor, blendFactor), 0xFFFFFFFF); @@ -305,9 +305,9 @@ technique10 NeighborhoodBlending { */ technique10 Resolve { pass Resolve { - SetVertexShader(CompileShader(vs_4_0, DX11_SMAAResolveVS())); + SetVertexShader(CompileShader(vs_4_0, VSResolve())); SetGeometryShader(NULL); - SetPixelShader(CompileShader(PS_VERSION, DX11_SMAAResolvePS())); + SetPixelShader(CompileShader(PS_VERSION, PSResolve())); SetDepthStencilState(DisableDepthStencil, 0); SetBlendState(NoBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF); @@ -319,9 +319,9 @@ technique10 Resolve { */ technique10 Separate { pass Separate { - SetVertexShader(CompileShader(vs_4_0, DX11_SMAASeparateVS())); + SetVertexShader(CompileShader(vs_4_0, VSSeparate())); SetGeometryShader(NULL); - SetPixelShader(CompileShader(PS_VERSION, DX11_SMAASeparatePS())); + SetPixelShader(CompileShader(PS_VERSION, PSSeparate())); SetDepthStencilState(DisableDepthStencil, 0); SetBlendState(NoBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF); diff --git a/TombEngine/Shaders/Shadows.hlsli b/TombEngine/Shaders/Shadows.hlsli index c71c2f32e..6af7a9283 100644 --- a/TombEngine/Shaders/Shadows.hlsli +++ b/TombEngine/Shaders/Shadows.hlsli @@ -2,8 +2,9 @@ #include "./Math.hlsli" #include "./ShaderLight.hlsli" -#define SHADOW_INTENSITY (0.55f) -#define INV_SHADOW_INTENSITY (1.0f - SHADOW_INTENSITY) +#define SHADOW_INTENSITY (0.6f) +#define SHADOW_BLUR_MIN (2.0f) +#define SHADOW_BLUR_MAX (8.0f) struct Sphere { @@ -102,6 +103,11 @@ float3 DoShadow(float3 worldPos, float3 normal, float3 lighting, float bias) float ndot = dot(normal, dir); float facingFactor = saturate((ndot - bias) / (1.0f - bias + EPSILON)); + // Calculate distance-based blur factor with non-linear progression + float distanceToLight = length(Light.Position - worldPos); + float blurFactor = pow(saturate(distanceToLight / Light.Out), 2.0f); + float kernelSize = lerp(SHADOW_BLUR_MIN, SHADOW_BLUR_MAX, blurFactor); + [unroll] for (int i = 0; i < 6; i++) { @@ -119,18 +125,19 @@ float3 DoShadow(float3 worldPos, float3 normal, float3 lighting, float bias) lightClipSpace.y = lightClipSpace.y / -2 + 0.5; float sum = 0; - float x, y; + float samples = 0; - // Perform PCF filtering on a 4 x 4 texel neighborhood. - for (y = -1.5; y <= 1.5; y += 1.0) + // Perform basic PCF filtering with distance-based kernel size + for (float y = -kernelSize; y <= kernelSize; y += 1.0) { - for (x = -1.5; x <= 1.5; x += 1.0) + for (float x = -kernelSize; x <= kernelSize; x += 1.0) { sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z); + samples += 1.0; } } - shadowFactor = lerp(shadowFactor, sum / 16.0, facingFactor * insideLightBounds); + shadowFactor = lerp(shadowFactor, sum / samples, facingFactor * insideLightBounds); } } @@ -138,11 +145,11 @@ float3 DoShadow(float3 worldPos, float3 normal, float3 lighting, float bias) float isSpot = step(0.5f, Light.Type == LT_SPOT); // 1.0 if LT_SPOT, 0.0 otherwise float isOther = 1.0 - (isPoint + isSpot); // 1.0 if neither LT_POINT nor LT_SPOT - float pointFactor = 1.0f - Luma(DoPointLight(worldPos, normal, Light)); - float spotFactor = 1.0f - Luma(DoSpotLight(worldPos, normal, Light)); + float pointFactor = Luma(DoPointLight(worldPos, normal, Light)); + float spotFactor = Luma(DoSpotLight(worldPos, normal, Light)); - float3 pointShadow = lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(pointFactor, 4) * INV_SHADOW_INTENSITY)); - float3 spotShadow = lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(spotFactor, 4) * INV_SHADOW_INTENSITY)); + float3 pointShadow = lighting * (1.0f - (1.0f - shadowFactor) * SHADOW_INTENSITY * pointFactor); + float3 spotShadow = lighting * (1.0f - (1.0f - shadowFactor) * SHADOW_INTENSITY * spotFactor ); return pointShadow * isPoint + spotShadow * isSpot + lighting * isOther; } \ No newline at end of file diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 544dd248e..bea473edb 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1009,6 +1009,7 @@ void FreeLevel(bool partial) g_Level.Commands.resize(0); g_Level.Frames.resize(0); g_Level.Sprites.resize(0); + g_Level.Mirrors.resize(0); g_Level.SoundDetails.resize(0); g_Level.SoundMap.resize(0); g_Level.FloorData.resize(0); @@ -1061,7 +1062,7 @@ void LoadAnimatedTextures() auto sequence = ANIMATED_TEXTURES_SEQUENCE{}; sequence.atlas = ReadInt32(); sequence.Fps = ReadInt32(); - sequence.numFrames = ReadInt32(); + sequence.numFrames = ReadCount(); for (int j = 0; j < sequence.numFrames; j++) { @@ -1363,6 +1364,7 @@ bool LoadLevel(const std::string& path, bool partial) LoadSprites(); LoadBoxes(); + LoadMirrors(); LoadAnimatedTextures(); UpdateProgress(70); @@ -1517,6 +1519,33 @@ void LoadBoxes() } } +void LoadMirrors() +{ + int mirrorCount = ReadCount(); + TENLog("Mirror count: " + std::to_string(mirrorCount), LogLevel::Info); + g_Level.Mirrors.reserve(mirrorCount); + + for (int i = 0; i < mirrorCount; i++) + { + auto& mirror = g_Level.Mirrors.emplace_back(); + + mirror.RoomNumber = ReadInt16(); // TODO: Write Int32 to level instead. Short isn't used for room numbers anymore. + mirror.Plane.x = ReadFloat(); + mirror.Plane.y = ReadFloat(); + mirror.Plane.z = ReadFloat(); + mirror.Plane.w = ReadFloat(); + + mirror.ReflectPlayer = ReadBool(); + mirror.ReflectMoveables = ReadBool(); + mirror.ReflectStatics = ReadBool(); + mirror.ReflectSprites = ReadBool(); + mirror.ReflectLights = ReadBool(); + mirror.Enabled = true; + + mirror.ReflectionMatrix = Matrix::CreateReflection(mirror.Plane); + } +} + bool LoadLevelFile(int levelIndex) { const auto& level = *g_GameFlow->GetLevel(levelIndex); diff --git a/TombEngine/Specific/level.h b/TombEngine/Specific/level.h index 0c79bba91..11777a521 100644 --- a/TombEngine/Specific/level.h +++ b/TombEngine/Specific/level.h @@ -85,6 +85,20 @@ struct MESH std::vector buckets; }; +struct MirrorData +{ + int RoomNumber = 0; + Plane Plane = SimpleMath::Plane(); + Matrix ReflectionMatrix = Matrix::Identity; + + bool Enabled = false; + bool ReflectPlayer = false; + bool ReflectMoveables = false; + bool ReflectStatics = false; + bool ReflectLights = false; + bool ReflectSprites = false; +}; + // LevelData struct LEVEL { @@ -123,6 +137,7 @@ struct LEVEL std::vector LoopedEventSetIndices = {}; std::vector AIObjects = {}; std::vector Sprites = {}; + std::vector Mirrors = {}; // Texture data TEXTURE SkyTexture = {}; @@ -164,6 +179,7 @@ void LoadSoundSources(); void LoadAnimatedTextures(); void LoadEventSets(); void LoadAIObjects(); +void LoadMirrors(); void LoadPortal(ROOM_INFO& room); diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index 49587e421..26cebdd14 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -88,7 +88,7 @@ $(Configuration)\ - true + false $(SolutionDir)Build\$(Configuration)\Bin\x86\ $(ExecutablePath);$(DXSDK_DIR)Utilities\bin\x86 $(SolutionDir)Libs;$(SolutionDir)Libs\lua;$(SolutionDir)Libs\sol;$(SolutionDir)Libs\zlib;$(SolutionDir)Libs\spdlog;$(SolutionDir)Libs\ois;$(SolutionDir)Libs\bass;$(SolutionDir)Libs\srtparser;$(IncludePath) @@ -100,7 +100,7 @@ $(SolutionDir)Libs;$(SolutionDir)Libs\lua;$(SolutionDir)Libs\sol;$(SolutionDir)Libs\zlib;$(SolutionDir)Libs\spdlog;$(SolutionDir)Libs\ois;$(SolutionDir)Libs\bass;$(SolutionDir)Libs\srtparser;$(IncludePath) $(LibraryPath);$(DXSDK_DIR)Lib\x64;$(SolutionDir)Libs\spdlog\x64;$(SolutionDir)Libs\lua\x64;$(SolutionDir)Libs\zlib\x64;$(SolutionDir)Libs\bass\x64;$(SolutionDir)Libs\ois\x64 .exe - true + false $(SolutionDir)Build\$(Configuration)\Bin\x64\ $(Configuration)\ $(ProjectName) @@ -321,7 +321,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. false true MachineX86 - Default + UseLinkTimeCodeGeneration del "$(TargetDir)*.pdb" /q @@ -400,7 +400,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. comctl32.lib;lua53.lib;bass.lib;bassmix.lib;bass_fx.lib;D3DCompiler.lib;dxgi.lib;dxguid.lib;d3d11.lib;version.lib;zlib.lib;spdlog.lib;OIS.lib;%(AdditionalDependencies) false true - Default + UseLinkTimeCodeGeneration del "$(TargetDir)*.pdb" /q @@ -469,6 +469,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. + @@ -821,6 +822,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. + @@ -852,6 +854,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. + @@ -1028,6 +1031,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. +