Merge branch 'MontyTRC89:develop' into develop

This commit is contained in:
davidmarr 2024-12-11 15:48:14 +01:00 committed by GitHub
commit 6516a8b87b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 278 additions and 218 deletions

View file

@ -31,6 +31,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
* Fixed Lara's Home entry not working. * Fixed Lara's Home entry not working.
* Fixed exploding TR3 bosses. * Fixed exploding TR3 bosses.
* Fixed original issue with deactivation of Dart Emitter. * Fixed original issue with deactivation of Dart Emitter.
* Fixed original issue with weapon hotkeys available in binoculars or lasersight mode.
* Fixed Lens Flare object not functioning properly. * Fixed Lens Flare object not functioning properly.
* Fixed lens flares not being occluded by static meshes and moveables. * Fixed lens flares not being occluded by static meshes and moveables.
* Fixed spotlight shadows. * Fixed spotlight shadows.
@ -38,7 +39,9 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
### New Features ### New Features
* Added fast savegame reloading. * Added fast savegame reloading.
* Added dynamic shadow casting on objects and static meshes.
* Added ricochet sounds and make the effect more prominent. * Added ricochet sounds and make the effect more prominent.
* Allow camera shake during flybys.
* Allow to run the engine without title level. * Allow to run the engine without title level.
* Allow more than 1024 objects in a level. * Allow more than 1024 objects in a level.
* Allow more than 1000 static mesh slots in a level. * Allow more than 1000 static mesh slots in a level.
@ -56,6 +59,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
* Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry.
* Fixed DisplayString class not supporting some Unicode characters and empty lines in multiline strings. * Fixed DisplayString class not supporting some Unicode characters and empty lines in multiline strings.
* Fixed DisplayString not being deallocated after showing. * Fixed DisplayString not being deallocated after showing.
* Fixed GameVars not transferring between levels in hub mode.
* Fixed incorrect behaviour of Moveable:GetJointRotation() function. * Fixed incorrect behaviour of Moveable:GetJointRotation() function.
* Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions.
* Fixed Util.HasLineOfSight() not taking static meshes into consideration. * Fixed Util.HasLineOfSight() not taking static meshes into consideration.

View file

@ -202,7 +202,7 @@ Timer = {
str:SetColor(pausedColor) str:SetColor(pausedColor)
end end
TEN.Strings.ShowString(str, 1) TEN.Strings.ShowString(str, 1, false)
end end
end end

View file

@ -342,6 +342,10 @@ void HandlePlayerQuickActions(ItemInfo& item)
g_Gui.UseItem(item, GAME_OBJECT_ID::ID_BIGMEDI_ITEM); g_Gui.UseItem(item, GAME_OBJECT_ID::ID_BIGMEDI_ITEM);
} }
// Don't process weapon hotkeys in optics mode.
if (player.Control.Look.IsUsingBinoculars)
return;
// Handle weapon scroll request. // Handle weapon scroll request.
if (IsClicked(In::PreviousWeapon) || IsClicked(In::NextWeapon)) if (IsClicked(In::PreviousWeapon) || IsClicked(In::NextWeapon))
{ {
@ -888,7 +892,9 @@ void HandlePlayerFlyCheat(ItemInfo& item)
{ {
if (player.Context.Vehicle == NO_VALUE) if (player.Context.Vehicle == NO_VALUE)
{ {
GivePlayerItemsCheat(item); if (KeyMap[OIS::KeyCode::KC_LSHIFT] || KeyMap[OIS::KeyCode::KC_RSHIFT])
GivePlayerItemsCheat(item);
GivePlayerWeaponsCheat(item); GivePlayerWeaponsCheat(item);
if (player.Control.WaterStatus != WaterStatus::FlyCheat) if (player.Control.WaterStatus != WaterStatus::FlyCheat)

View file

@ -215,6 +215,38 @@ inline void RumbleFromBounce()
Rumble(std::clamp((float)abs(Camera.bounce) / 70.0f, 0.0f, 0.8f), 0.2f); Rumble(std::clamp((float)abs(Camera.bounce) / 70.0f, 0.0f, 0.8f), 0.2f);
} }
void CalculateBounce(bool binocularMode)
{
if (Camera.bounce == 0)
return;
if (Camera.bounce <= 0)
{
if (binocularMode)
{
Camera.target.x += (CLICK(0.25f) / 4) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce / 2));
Camera.target.y += (CLICK(0.25f) / 4) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce / 2));
Camera.target.z += (CLICK(0.25f) / 4) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce / 2));
}
else
{
int bounce = -Camera.bounce;
int bounce2 = bounce / 2;
Camera.target.x += GetRandomControl() % bounce - bounce2;
Camera.target.y += GetRandomControl() % bounce - bounce2;
Camera.target.z += GetRandomControl() % bounce - bounce2;
}
Camera.bounce += 5;
RumbleFromBounce();
}
else
{
Camera.pos.y += Camera.bounce;
Camera.target.y += Camera.bounce;
Camera.bounce = 0;
}
}
void InitializeCamera() void InitializeCamera()
{ {
@ -313,25 +345,7 @@ void MoveCamera(GameVector* ideal, int speed)
Camera.pos.z += (ideal->z - Camera.pos.z) / speed; Camera.pos.z += (ideal->z - Camera.pos.z) / speed;
Camera.pos.RoomNumber = ideal->RoomNumber; Camera.pos.RoomNumber = ideal->RoomNumber;
if (Camera.bounce) CalculateBounce(false);
{
if (Camera.bounce <= 0)
{
int bounce = -Camera.bounce;
int bounce2 = bounce / 2;
Camera.target.x += GetRandomControl() % bounce - bounce2;
Camera.target.y += GetRandomControl() % bounce - bounce2;
Camera.target.z += GetRandomControl() % bounce - bounce2;
Camera.bounce += 5;
RumbleFromBounce();
}
else
{
Camera.pos.y += Camera.bounce;
Camera.target.y += Camera.bounce;
Camera.bounce = 0;
}
}
int y = Camera.pos.y; int y = Camera.pos.y;
if (TestEnvironment(ENV_FLAG_SWAMP, Camera.pos.RoomNumber)) if (TestEnvironment(ENV_FLAG_SWAMP, Camera.pos.RoomNumber))
@ -1000,23 +1014,8 @@ void BinocularCamera(ItemInfo* item)
Camera.target.RoomNumber = item->RoomNumber; Camera.target.RoomNumber = item->RoomNumber;
} }
if (Camera.bounce && if (Camera.type == Camera.oldType)
Camera.type == Camera.oldType) CalculateBounce(true);
{
if (Camera.bounce <= 0)
{
Camera.target.x += (CLICK(0.25f) / 4) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce / 2));
Camera.target.y += (CLICK(0.25f) / 4) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce / 2));
Camera.target.z += (CLICK(0.25f) / 4) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce / 2));
Camera.bounce += 5;
RumbleFromBounce();
}
else
{
Camera.bounce = 0;
Camera.target.y += Camera.bounce;
}
}
Camera.target.RoomNumber = GetPointCollision(Camera.pos.ToVector3i(), Camera.target.RoomNumber).GetRoomNumber(); Camera.target.RoomNumber = GetPointCollision(Camera.pos.ToVector3i(), Camera.target.RoomNumber).GetRoomNumber();
LookAt(&Camera, 0); LookAt(&Camera, 0);

View file

@ -99,6 +99,7 @@ void BounceCamera(ItemInfo* item, short bounce, short maxDistance);
void BinocularCamera(ItemInfo* item); void BinocularCamera(ItemInfo* item);
void ConfirmCameraTargetPos(); void ConfirmCameraTargetPos();
void CalculateCamera(const CollisionInfo& coll); void CalculateCamera(const CollisionInfo& coll);
void CalculateBounce(bool binocularMode);
void RumbleScreen(); void RumbleScreen();
bool TestBoundsCollideCamera(const GameBoundingBox& bounds, const Pose& pose, short radius); bool TestBoundsCollideCamera(const GameBoundingBox& bounds, const Pose& pose, short radius);
void ItemPushCamera(GameBoundingBox* bounds, Pose* pos, short radius); void ItemPushCamera(GameBoundingBox* bounds, Pose* pos, short radius);

View file

@ -157,7 +157,7 @@ GameStatus GamePhase(bool insideMenu)
// Pre-loop script and event handling. // Pre-loop script and event handling.
g_GameScript->OnLoop(DELTA_TIME, false); // TODO: Don't use DELTA_TIME constant with high framerate. g_GameScript->OnLoop(DELTA_TIME, false); // TODO: Don't use DELTA_TIME constant with high framerate.
HandleAllGlobalEvents(EventType::Loop, (Activator)LaraItem->Index); HandleAllGlobalEvents(EventType::Loop, (Activator)short(LaraItem->Index));
// Queued input actions are read again after OnLoop, so that remaining control loop can immediately register // Queued input actions are read again after OnLoop, so that remaining control loop can immediately register
// emulated keypresses from the script. // emulated keypresses from the script.
@ -244,7 +244,7 @@ GameStatus GamePhase(bool insideMenu)
// Call post-loop callbacks last time and end level. // Call post-loop callbacks last time and end level.
g_GameScript->OnLoop(DELTA_TIME, true); g_GameScript->OnLoop(DELTA_TIME, true);
g_GameScript->OnEnd(gameStatus); g_GameScript->OnEnd(gameStatus);
HandleAllGlobalEvents(EventType::End, (Activator)LaraItem->Index); HandleAllGlobalEvents(EventType::End, (Activator)short(LaraItem->Index));
} }
else else
{ {
@ -299,7 +299,7 @@ GameStatus FreezePhase()
// Poll controls and call scripting events. // Poll controls and call scripting events.
HandleControls(false); HandleControls(false);
g_GameScript->OnFreeze(); g_GameScript->OnFreeze();
HandleAllGlobalEvents(EventType::Freeze, (Activator)LaraItem->Index); HandleAllGlobalEvents(EventType::Freeze, (Activator)short(LaraItem->Index));
// Partially update scene if not using full freeze mode. // Partially update scene if not using full freeze mode.
if (g_GameFlow->LastFreezeMode != FreezeMode::Full) if (g_GameFlow->LastFreezeMode != FreezeMode::Full)
@ -376,7 +376,6 @@ unsigned CALLBACK GameMain(void *)
GameStatus DoLevel(int levelIndex, bool loadGame) GameStatus DoLevel(int levelIndex, bool loadGame)
{ {
bool isTitle = !levelIndex; bool isTitle = !levelIndex;
auto loadType = loadGame ? LevelLoadType::Load : (SaveGame::IsOnHub(levelIndex) ? LevelLoadType::Hub : LevelLoadType::New);
TENLog(isTitle ? "DoTitle" : "DoLevel", LogLevel::Info); TENLog(isTitle ? "DoTitle" : "DoLevel", LogLevel::Info);
@ -392,7 +391,7 @@ GameStatus DoLevel(int levelIndex, bool loadGame)
InitializeItemBoxData(); InitializeItemBoxData();
// Initialize scripting. // Initialize scripting.
InitializeScripting(levelIndex, loadType); InitializeScripting(levelIndex, loadGame);
InitializeNodeScripts(); InitializeNodeScripts();
// Initialize menu and inventory state. // Initialize menu and inventory state.
@ -540,12 +539,12 @@ void CleanUp()
ClearObjCamera(); ClearObjCamera();
} }
void InitializeScripting(int levelIndex, LevelLoadType type) void InitializeScripting(int levelIndex, bool loadGame)
{ {
TENLog("Loading level script...", LogLevel::Info); TENLog("Loading level script...", LogLevel::Info);
g_GameStringsHandler->ClearDisplayStrings(); g_GameStringsHandler->ClearDisplayStrings();
g_GameScript->ResetScripts(!levelIndex || type != LevelLoadType::New); g_GameScript->ResetScripts(!levelIndex || loadGame);
const auto& level = *g_GameFlow->GetLevel(levelIndex); const auto& level = *g_GameFlow->GetLevel(levelIndex);
@ -575,7 +574,7 @@ void InitializeScripting(int levelIndex, LevelLoadType type)
} }
// Play default background music. // Play default background music.
if (type != LevelLoadType::Load) if (!loadGame)
PlaySoundTrack(level.GetAmbientTrack(), SoundTrackType::BGM, 0, SOUND_XFADETIME_LEVELJUMP); PlaySoundTrack(level.GetAmbientTrack(), SoundTrackType::BGM, 0, SOUND_XFADETIME_LEVELJUMP);
} }
@ -594,13 +593,19 @@ void InitializeOrLoadGame(bool loadGame)
g_Gui.SetEnterInventory(NO_VALUE); g_Gui.SetEnterInventory(NO_VALUE);
// Restore game? // Restore game?
if (loadGame && SaveGame::Load(g_GameFlow->SelectedSaveGame)) if (loadGame)
{ {
if (!SaveGame::Load(g_GameFlow->SelectedSaveGame))
{
NextLevel = g_GameFlow->GetNumLevels();
return;
}
InitializeGame = false; InitializeGame = false;
g_GameFlow->SelectedSaveGame = 0; g_GameFlow->SelectedSaveGame = 0;
g_GameScript->OnLoad(); g_GameScript->OnLoad();
HandleAllGlobalEvents(EventType::Load, (Activator)LaraItem->Index); HandleAllGlobalEvents(EventType::Load, (Activator)short(LaraItem->Index));
} }
else else
{ {
@ -624,7 +629,7 @@ void InitializeOrLoadGame(bool loadGame)
} }
g_GameScript->OnStart(); g_GameScript->OnStart();
HandleAllGlobalEvents(EventType::Start, (Activator)LaraItem->Index); HandleAllGlobalEvents(EventType::Start, (Activator)short(LaraItem->Index));
} }
} }

View file

@ -34,13 +34,6 @@ enum class FreezeMode
Player Player
}; };
enum class LevelLoadType
{
New,
Hub,
Load
};
enum CardinalDirection enum CardinalDirection
{ {
NORTH, NORTH,
@ -106,8 +99,8 @@ void UpdateShatters();
void CleanUp(); void CleanUp();
void InitializeOrLoadGame(bool loadGame); void InitializeOrLoadGame(bool loadGame);
void InitializeScripting(int levelIndex, LevelLoadType type); void InitializeScripting(int levelIndex, bool loadGame);
void DeInitializeScripting(int levelIndex); void DeInitializeScripting(int levelIndex, GameStatus reason);
void SetupInterpolation(); void SetupInterpolation();

View file

@ -841,7 +841,7 @@ void TestTriggers(ItemInfo* item, bool isHeavy, int heavyFlags)
short roomNumber = item->RoomNumber; short roomNumber = item->RoomNumber;
auto floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); auto floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, floor, item->Index, isHeavy, heavyFlags); TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, floor, (Activator)short(item->Index), isHeavy, heavyFlags);
} }
void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags) void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags)

View file

@ -2040,7 +2040,7 @@ namespace TEN::Gui
// Use item event handling. // Use item event handling.
g_GameScript->OnUseItem((GAME_OBJECT_ID)InventoryItemChosen); g_GameScript->OnUseItem((GAME_OBJECT_ID)InventoryItemChosen);
HandleAllGlobalEvents(EventType::UseItem, (Activator)item.Index); HandleAllGlobalEvents(EventType::UseItem, (Activator)short(item.Index));
// Quickly discard further processing if chosen item was reset in script. // Quickly discard further processing if chosen item was reset in script.
if (InventoryItemChosen == NO_VALUE) if (InventoryItemChosen == NO_VALUE)

View file

@ -206,6 +206,19 @@ bool SaveGame::DoesSaveGameExist(int slot, bool silent)
return true; return true;
} }
bool SaveGame::IsSaveGameValid(int slot)
{
SaveGameHeader header;
if (!LoadHeader(slot, &header))
return false;
// Hash mismatch between savegame and level file means that level version has changed.
if (header.LevelHash != LastLevelHash)
return false;
return true;
}
bool SaveGame::IsLoadGamePossible() bool SaveGame::IsLoadGamePossible()
{ {
for (int i = 0; i < SAVEGAME_MAX; i++) for (int i = 0; i < SAVEGAME_MAX; i++)
@ -1574,7 +1587,7 @@ bool SaveGame::Save(int slot)
return false; return false;
g_GameScript->OnSave(); g_GameScript->OnSave();
HandleAllGlobalEvents(EventType::Save, (Activator)LaraItem->Index); HandleAllGlobalEvents(EventType::Save, (Activator)short(LaraItem->Index));
// Savegame infos need to be reloaded so that last savegame counter properly increases. // Savegame infos need to be reloaded so that last savegame counter properly increases.
LoadHeaders(); LoadHeaders();
@ -1614,15 +1627,9 @@ bool SaveGame::Save(int slot)
bool SaveGame::Load(int slot) bool SaveGame::Load(int slot)
{ {
if (!IsSaveGameSlotValid(slot)) if (!IsSaveGameValid(slot))
{ {
TENLog("Savegame slot " + std::to_string(slot) + " is invalid, load is impossible.", LogLevel::Error); TENLog("Loading from savegame in slot " + std::to_string(slot) + " is impossible, data is missing or level has changed.", LogLevel::Error);
return false;
}
if (!DoesSaveGameExist(slot))
{
TENLog("Savegame in slot " + std::to_string(slot) + " does not exist.", LogLevel::Error);
return false; return false;
} }
@ -1704,7 +1711,7 @@ static void ParseStatistics(const Save::SaveGame* s, bool isHub)
SaveGame::Statistics.Game.Timer = s->game()->timer(); SaveGame::Statistics.Game.Timer = s->game()->timer();
} }
static void ParseLua(const Save::SaveGame* s) static void ParseLua(const Save::SaveGame* s, bool hubMode)
{ {
// Event sets // Event sets
@ -1808,7 +1815,7 @@ static void ParseLua(const Save::SaveGame* s)
} }
} }
g_GameScript->SetVariables(loadedVars); g_GameScript->SetVariables(loadedVars, hubMode);
auto populateCallbackVecs = [&s](auto callbackFunc) auto populateCallbackVecs = [&s](auto callbackFunc)
{ {
@ -2672,7 +2679,7 @@ void SaveGame::Parse(const std::vector<byte>& buffer, bool hubMode)
const Save::SaveGame* s = Save::GetSaveGame(buffer.data()); const Save::SaveGame* s = Save::GetSaveGame(buffer.data());
ParseLevel(s, hubMode); ParseLevel(s, hubMode);
ParseLua(s); ParseLua(s, hubMode);
ParseStatistics(s, hubMode); ParseStatistics(s, hubMode);
// Effects and player data is ignored when loading hub. // Effects and player data is ignored when loading hub.

View file

@ -66,6 +66,7 @@ public:
static bool DoesSaveGameExist(int slot, bool silent = false); static bool DoesSaveGameExist(int slot, bool silent = false);
static bool IsLoadGamePossible(); static bool IsLoadGamePossible();
static bool IsSaveGameValid(int slot);
static void SaveHub(int index); static void SaveHub(int index);
static void LoadHub(int index); static void LoadHub(int index);

View file

@ -494,6 +494,7 @@ void CalculateSpotCameras()
Camera.target.x = ctx; Camera.target.x = ctx;
Camera.target.y = cty; Camera.target.y = cty;
Camera.target.z = ctz; Camera.target.z = ctz;
CalculateBounce(false);
} }
int outsideRoom = IsRoomOutside(cpx, cpy, cpz); int outsideRoom = IsRoomOutside(cpx, cpy, cpz);

View file

@ -201,43 +201,62 @@ namespace TEN::Renderer
_context->PSSetSamplers((UINT)registerType, 1, &samplerState); _context->PSSetSamplers((UINT)registerType, 1, &samplerState);
} }
void Renderer::BindLight(RendererLight& light, ShaderLight* lights, int index) int Renderer::BindLight(RendererLight& light, ShaderLight* lights, int index)
{ {
memcpy(&lights[index], &light, sizeof(ShaderLight)); memcpy(&lights[index], &light, sizeof(ShaderLight));
if (light.Hash == 0) // Precalculate ranges so that it's not recalculated in shader for every pixel.
return; if (light.Type == LightType::Spot)
{
lights[index].InRange = cos(light.InRange * (PI / 180.0f));
lights[index].OutRange = cos(light.OutRange * (PI / 180.0f));
}
lights[index].Position = Vector3::Lerp(light.PrevPosition, light.Position, GetInterpolationFactor()); // If light has hash, interpolate its position with previous position.
lights[index].Direction = Vector3::Lerp(light.PrevDirection, light.Direction, GetInterpolationFactor()); if (light.Hash != 0)
{
lights[index].Position = Vector3::Lerp(light.PrevPosition, light.Position, GetInterpolationFactor());
lights[index].Direction = Vector3::Lerp(light.PrevDirection, light.Direction, GetInterpolationFactor());
}
// Bitmask light type to filter it in the shader later.
return (1 << (31 - (int)light.Type));
} }
void Renderer::BindRoomLights(std::vector<RendererLight*>& lights) void Renderer::BindRoomLights(std::vector<RendererLight*>& lights)
{ {
int lightTypeMask = 0;
for (int i = 0; i < lights.size(); i++) for (int i = 0; i < lights.size(); i++)
BindLight(*lights[i], _stRoom.RoomLights, i); lightTypeMask = lightTypeMask | BindLight(*lights[i], _stRoom.RoomLights, i);
_stRoom.NumRoomLights = (int)lights.size(); _stRoom.NumRoomLights = (int)lights.size() | lightTypeMask;
} }
void Renderer::BindStaticLights(std::vector<RendererLight*>& lights) void Renderer::BindStaticLights(std::vector<RendererLight*>& lights)
{ {
int lightTypeMask = 0;
for (int i = 0; i < lights.size(); i++) for (int i = 0; i < lights.size(); i++)
BindLight(*lights[i], _stStatic.Lights, i); lightTypeMask = lightTypeMask | BindLight(*lights[i], _stStatic.Lights, i);
_stStatic.NumLights = (int)lights.size(); _stStatic.NumLights = (int)lights.size() | lightTypeMask;
} }
void Renderer::BindInstancedStaticLights(std::vector<RendererLight*>& lights, int instanceID) void Renderer::BindInstancedStaticLights(std::vector<RendererLight*>& lights, int instanceID)
{ {
for (int i = 0; i < lights.size(); i++) int lightTypeMask = 0;
BindLight(*lights[i], _stInstancedStaticMeshBuffer.StaticMeshes[instanceID].Lights, i);
_stInstancedStaticMeshBuffer.StaticMeshes[instanceID].NumLights = (int)lights.size(); for (int i = 0; i < lights.size(); i++)
lightTypeMask = lightTypeMask | BindLight(*lights[i], _stInstancedStaticMeshBuffer.StaticMeshes[instanceID].Lights, i);
_stInstancedStaticMeshBuffer.StaticMeshes[instanceID].NumLights = (int)lights.size() | lightTypeMask;
} }
void Renderer::BindMoveableLights(std::vector<RendererLight*>& lights, int roomNumber, int prevRoomNumber, float fade) void Renderer::BindMoveableLights(std::vector<RendererLight*>& lights, int roomNumber, int prevRoomNumber, float fade)
{ {
int lightTypeMask = 0;
int numLights = 0; int numLights = 0;
for (int i = 0; i < lights.size(); i++) for (int i = 0; i < lights.size(); i++)
{ {
@ -257,12 +276,12 @@ namespace TEN::Renderer
if (fadedCoeff == 0.0f) if (fadedCoeff == 0.0f)
continue; continue;
BindLight(*lights[i], _stItem.Lights, numLights); lightTypeMask = lightTypeMask | BindLight(*lights[i], _stItem.Lights, numLights);
_stItem.Lights[numLights].Intensity *= fadedCoeff; _stItem.Lights[numLights].Intensity *= fadedCoeff;
numLights++; numLights++;
} }
_stItem.NumLights = numLights; _stItem.NumLights = numLights | lightTypeMask;
} }
void Renderer::BindConstantBufferVS(ConstantBufferRegister constantBufferType, ID3D11Buffer** buffer) void Renderer::BindConstantBufferVS(ConstantBufferRegister constantBufferType, ID3D11Buffer** buffer)

View file

@ -418,7 +418,7 @@ namespace TEN::Renderer
void ApplySMAA(RenderTarget2D* renderTarget, RenderView& view); void ApplySMAA(RenderTarget2D* renderTarget, RenderView& view);
void ApplyFXAA(RenderTarget2D* renderTarget, RenderView& view); void ApplyFXAA(RenderTarget2D* renderTarget, RenderView& view);
void BindTexture(TextureRegister registerType, TextureBase* texture, SamplerStateRegister samplerType); void BindTexture(TextureRegister registerType, TextureBase* texture, SamplerStateRegister samplerType);
void BindLight(RendererLight& light, ShaderLight* lights, int index); int BindLight(RendererLight& light, ShaderLight* lights, int index);
void BindRoomLights(std::vector<RendererLight*>& lights); void BindRoomLights(std::vector<RendererLight*>& lights);
void BindStaticLights(std::vector<RendererLight*>& lights); void BindStaticLights(std::vector<RendererLight*>& lights);
void BindInstancedStaticLights(std::vector<RendererLight*>& lights, int instanceID); void BindInstancedStaticLights(std::vector<RendererLight*>& lights, int instanceID);

View file

@ -2653,7 +2653,7 @@ namespace TEN::Renderer
// Set shadow map data and bind shadow map texture. // Set shadow map data and bind shadow map texture.
if (_shadowLight != nullptr) if (_shadowLight != nullptr)
{ {
memcpy(&_stShadowMap.Light, _shadowLight, sizeof(ShaderLight)); BindLight(*_shadowLight, &_stShadowMap.Light, 0);
_stShadowMap.ShadowMapSize = g_Configuration.ShadowMapSize; _stShadowMap.ShadowMapSize = g_Configuration.ShadowMapSize;
_stShadowMap.CastShadows = true; _stShadowMap.CastShadows = true;

View file

@ -655,7 +655,7 @@ namespace TEN::Renderer
std::vector<RendererLight*> lightsToDraw; std::vector<RendererLight*> lightsToDraw;
CollectLights(Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z), CAMERA_LIGHT_COLLECTION_RADIUS, Camera.pos.RoomNumber, NO_VALUE, true, false, nullptr, &lightsToDraw); CollectLights(Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z), CAMERA_LIGHT_COLLECTION_RADIUS, Camera.pos.RoomNumber, NO_VALUE, true, false, nullptr, &lightsToDraw);
if (!lightsToDraw.empty() && lightsToDraw.front()->CastShadows) if (g_Configuration.ShadowType != ShadowMode::None && !lightsToDraw.empty() && lightsToDraw.front()->CastShadows)
{ {
_shadowLight = lightsToDraw.front(); _shadowLight = lightsToDraw.front();
} }

View file

@ -69,7 +69,7 @@ public:
virtual void ExecuteFunction(const std::string& luaFuncName, short idOne, short idTwo = 0) = 0; virtual void ExecuteFunction(const std::string& luaFuncName, short idOne, short idTwo = 0) = 0;
virtual void GetVariables(std::vector<SavedVar>& vars) = 0; virtual void GetVariables(std::vector<SavedVar>& vars) = 0;
virtual void SetVariables(const std::vector<SavedVar>& vars) = 0; virtual void SetVariables(const std::vector<SavedVar>& vars, bool onlyLevelVars) = 0;
virtual void GetCallbackStrings( virtual void GetCallbackStrings(
std::vector<std::string>& preStart, std::vector<std::string>& preStart,

View file

@ -323,7 +323,7 @@ Possible event type values:
*/ */
void LogicHandler::HandleEvent(const std::string& name, EventType type, sol::optional<Moveable&> activator) void LogicHandler::HandleEvent(const std::string& name, EventType type, sol::optional<Moveable&> activator)
{ {
TEN::Control::Volumes::HandleEvent(name, type, activator.has_value() ? (Activator)activator.value().GetIndex() : (Activator)LaraItem->Index); TEN::Control::Volumes::HandleEvent(name, type, activator.has_value() ? (Activator)activator.value().GetIndex() : (Activator)short(LaraItem->Index));
} }
/*** Attempt to find an event set and enable specified event in it. /*** Attempt to find an event set and enable specified event in it.
@ -485,9 +485,11 @@ void LogicHandler::FreeLevelScripts()
} }
// Used when loading. // Used when loading.
void LogicHandler::SetVariables(const std::vector<SavedVar>& vars) void LogicHandler::SetVariables(const std::vector<SavedVar>& vars, bool onlyLevelVars)
{ {
ResetGameTables(); if (!onlyLevelVars)
ResetGameTables();
ResetLevelTables(); ResetLevelTables();
std::unordered_map<unsigned int, sol::table> solTables; std::unordered_map<unsigned int, sol::table> solTables;
@ -565,6 +567,9 @@ void LogicHandler::SetVariables(const std::vector<SavedVar>& vars)
for (auto& [first, second] : levelVars) for (auto& [first, second] : levelVars)
(*m_handler.GetState())[ScriptReserved_LevelVars][first] = second; (*m_handler.GetState())[ScriptReserved_LevelVars][first] = second;
if (onlyLevelVars)
return;
sol::table gameVars = rootTable[ScriptReserved_GameVars]; sol::table gameVars = rootTable[ScriptReserved_GameVars];
for (auto& [first, second] : gameVars) for (auto& [first, second] : gameVars)
(*m_handler.GetState())[ScriptReserved_GameVars][first] = second; (*m_handler.GetState())[ScriptReserved_GameVars][first] = second;

View file

@ -145,7 +145,7 @@ public:
void ExecuteFunction(const std::string& name, short idOne, short idTwo) override; void ExecuteFunction(const std::string& name, short idOne, short idTwo) override;
void GetVariables(std::vector<SavedVar>& vars) override; void GetVariables(std::vector<SavedVar>& vars) override;
void SetVariables(const std::vector<SavedVar>& vars) override; void SetVariables(const std::vector<SavedVar>& vars, bool onlyLevelVars) override;
void ResetVariables(); void ResetVariables();
void SetCallbackStrings(const std::vector<std::string>& preStart, void SetCallbackStrings(const std::vector<std::string>& preStart,

View file

@ -7,7 +7,7 @@
#define ALPHATEST_GREATER_THAN 1 #define ALPHATEST_GREATER_THAN 1
#define ALPHATEST_LESS_THAN 2 #define ALPHATEST_LESS_THAN 2
#define BLENDMODE_OPAQUE 0, #define BLENDMODE_OPAQUE 0
#define BLENDMODE_ALPHATEST 1 #define BLENDMODE_ALPHATEST 1
#define BLENDMODE_ADDITIVE 2 #define BLENDMODE_ADDITIVE 2
#define BLENDMODE_NOZTEST 4 #define BLENDMODE_NOZTEST 4

View file

@ -4,6 +4,7 @@
#include "./VertexEffects.hlsli" #include "./VertexEffects.hlsli"
#include "./VertexInput.hlsli" #include "./VertexInput.hlsli"
#include "./Blending.hlsli" #include "./Blending.hlsli"
#include "./Shadows.hlsli"
#define INSTANCED_STATIC_MESH_BUCKET_SIZE 100 #define INSTANCED_STATIC_MESH_BUCKET_SIZE 100
@ -124,6 +125,8 @@ PixelShaderOutput PS(PixelShaderInput input)
input.FogBulbs.w) : input.FogBulbs.w) :
StaticLight(input.Color.xyz, tex.xyz, input.FogBulbs.w); StaticLight(input.Color.xyz, tex.xyz, input.FogBulbs.w);
color = DoShadow(input.WorldPosition, normal, color, -0.5f);
output.Color = float4(color * occlusion, tex.w); output.Color = float4(color * occlusion, tex.w);
output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f)); output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f));
output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog); output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog);

View file

@ -5,7 +5,7 @@
#include "./VertexInput.hlsli" #include "./VertexInput.hlsli"
#include "./Blending.hlsli" #include "./Blending.hlsli"
#include "./AnimatedTextures.hlsli" #include "./AnimatedTextures.hlsli"
//#include "./Shadows.hlsli" #include "./Shadows.hlsli"
#define MAX_BONES 32 #define MAX_BONES 32
@ -155,6 +155,8 @@ PixelShaderOutput PS(PixelShaderInput input)
input.FogBulbs.w) : input.FogBulbs.w) :
StaticLight(input.Color.xyz, tex.xyz, input.FogBulbs.w); StaticLight(input.Color.xyz, tex.xyz, input.FogBulbs.w);
color = DoShadow(input.WorldPosition, normal, color, -0.5f);
output.Color = saturate(float4(color * occlusion, tex.w)); output.Color = saturate(float4(color * occlusion, tex.w));
output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f)); output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f));
output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog); output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog);

View file

@ -3,12 +3,19 @@
#define PI 3.1415926535897932384626433832795028841971693993751058209749445923 #define PI 3.1415926535897932384626433832795028841971693993751058209749445923
#define PI2 6.2831853071795864769252867665590057683943387987502116419498891846 #define PI2 6.2831853071795864769252867665590057683943387987502116419498891846
#define EPSILON 1e-38
#define OCTAVES 6 #define OCTAVES 6
#define LT_SUN 0 #define LT_SUN 0
#define LT_POINT 1 #define LT_POINT 1
#define LT_SPOT 2 #define LT_SPOT 2
#define LT_SHADOW 3 #define LT_SHADOW 3
#define LT_MASK 0xFFFF
#define LT_MASK_SUN (1 << (31 - LT_SUN))
#define LT_MASK_POINT (1 << (31 - LT_POINT))
#define LT_MASK_SPOT (1 << (31 - LT_SPOT))
#define LT_MASK_SHADOW (1 << (31 - LT_SHADOW))
#define MAX_LIGHTS_PER_ROOM 48 #define MAX_LIGHTS_PER_ROOM 48
#define MAX_LIGHTS_PER_ITEM 8 #define MAX_LIGHTS_PER_ITEM 8

View file

@ -7,6 +7,8 @@
#include "./Shadows.hlsli" #include "./Shadows.hlsli"
#include "./ShaderLight.hlsli" #include "./ShaderLight.hlsli"
#define ROOM_LIGHT_COEFF 0.7f
cbuffer RoomBuffer : register(b5) cbuffer RoomBuffer : register(b5)
{ {
int Water; int Water;
@ -141,28 +143,31 @@ PixelShaderOutput PS(PixelShaderInput input)
occlusion = pow(SSAOTexture.Sample(SSAOSampler, samplePosition).x, AmbientOcclusionExponent); occlusion = pow(SSAOTexture.Sample(SSAOSampler, samplePosition).x, AmbientOcclusionExponent);
} }
if (CastShadows) lighting = DoShadow(input.WorldPosition, normal, lighting, -2.5f);
lighting = DoBlobShadows(input.WorldPosition, lighting);
bool onlyPointLights = (NumRoomLights & ~LT_MASK) == LT_MASK_POINT;
int numLights = NumRoomLights & LT_MASK;
for (int i = 0; i < numLights; i++)
{ {
float isPointLight = step(0.5f, Light.Type == LT_POINT); // 1.0 if LT_POINT, 0.0 otherwise if (onlyPointLights)
float isSpotLight = step(0.5f, Light.Type == LT_SPOT); // 1.0 if LT_SPOT, 0.0 otherwise {
float isOtherLight = 1.0 - (isPointLight + isSpotLight); // 1.0 if neither LT_POINT nor LT_SPOT lighting += DoPointLight(input.WorldPosition, normal, RoomLights[i]) * ROOM_LIGHT_COEFF;
}
float3 pointLightShadow = DoPointLightShadow(input.WorldPosition, lighting); else
float3 spotLightShadow = DoSpotLightShadow(input.WorldPosition, normal, lighting); {
// Room dynamic lights can only be spot or point, so we use simplified function for that.
// Blend the shadows based on the light type
lighting = pointLightShadow * isPointLight + spotLightShadow * isSpotLight + lighting * isOtherLight;
}
lighting = DoBlobShadows(input.WorldPosition, lighting); float isPoint = step(0.5f, RoomLights[i].Type == LT_POINT);
float isSpot = step(0.5f, RoomLights[i].Type == LT_SPOT);
for (int i = 0; i < NumRoomLights; i++) float3 pointLight = float3(0.0f, 0.0f, 0.0f);
{ float3 spotLight = float3(0.0f, 0.0f, 0.0f);
float isPointLightRoom = step(0.5f, RoomLights[i].Type == LT_POINT); DoPointAndSpotLight(input.WorldPosition, normal, RoomLights[i], pointLight, spotLight);
float isSpotLightRoom = step(0.5f, RoomLights[i].Type == LT_SPOT);
lighting += pointLight * isPoint * ROOM_LIGHT_COEFF + spotLight * isSpot * ROOM_LIGHT_COEFF;
lighting += DoPointLight(input.WorldPosition, normal, RoomLights[i]) * isPointLightRoom; }
lighting += DoSpotLight(input.WorldPosition, normal, RoomLights[i]) * isSpotLightRoom;
} }
if (Caustics) if (Caustics)

View file

@ -129,21 +129,30 @@ float3 DoSpotLight(float3 pos, float3 normal, ShaderLight light)
float3 lightVec = pos - light.Position.xyz; float3 lightVec = pos - light.Position.xyz;
float distance = length(lightVec); float distance = length(lightVec);
float3 lightDir = normalize(lightVec); float3 lightDir = normalize(lightVec);
float cosine = dot(lightDir, light.Direction.xyz);
// Angle attenuation float cosine = dot(lightDir, light.Direction.xyz);
float coneInCos = cos(light.InRange * (PI / 180.0f)); float angleAttenuation = saturate((cosine - light.OutRange) / (light.InRange - light.OutRange));
float coneOutCos = cos(light.OutRange * (PI / 180.0f));
float angleAttenuation = saturate((cosine - coneOutCos) / (coneInCos - coneOutCos));
// Distance attenuation
float distanceAttenuation = saturate((light.Out - distance) / (light.Out - light.In)); float distanceAttenuation = saturate((light.Out - distance) / (light.Out - light.In));
// Surface lighting
float d = saturate(dot(normal, -lightDir)); float d = saturate(dot(normal, -lightDir));
return saturate(light.Color.xyz * light.Intensity * angleAttenuation * distanceAttenuation * d); return saturate(light.Color.xyz * light.Intensity * angleAttenuation * distanceAttenuation * d);
} }
void DoPointAndSpotLight(float3 pos, float3 normal, ShaderLight light, out float3 pointOutput, out float3 spotOutput)
{
float3 lightVec = light.Position.xyz - pos;
float distance = length(lightVec);
float3 lightDir = normalize(lightVec);
float cosine = dot(-lightDir, light.Direction.xyz);
float distanceAttenuation = saturate((light.Out - distance) / (light.Out - light.In));
float angleAttenuation = saturate((cosine - light.OutRange) / (light.InRange - light.OutRange));
float d = saturate(dot(normal, lightDir));
pointOutput = saturate(light.Color.xyz * light.Intensity * distanceAttenuation * d);
spotOutput = saturate(light.Color.xyz * light.Intensity * angleAttenuation * distanceAttenuation * d);
}
float3 DoDirectionalLight(float3 pos, float3 normal, ShaderLight light) float3 DoDirectionalLight(float3 pos, float3 normal, ShaderLight light)
{ {
float d = saturate(dot(-light.Direction.xyz, normal)); float d = saturate(dot(-light.Direction.xyz, normal));
@ -310,12 +319,13 @@ float DoFogBulbForSky(float3 pos, ShaderFogBulb bulb)
float DoDistanceFogForVertex(float3 pos) float DoDistanceFogForVertex(float3 pos)
{ {
float d = length(CamPositionWS.xyz - pos); float fog = 0.0f;
float fogRange = FogMaxDistance * 1024 - FogMinDistance * 1024;
float fogEnabled = step(0.0f, FogMaxDistance); if (FogMaxDistance > 0.0f)
float fogFactor = (d - FogMinDistance * 1024) / fogRange; {
float fog = saturate(fogFactor) * fogEnabled; float d = length(CamPositionWS.xyz - pos);
fog = clamp((d - FogMinDistance * 1024) / (FogMaxDistance * 1024 - FogMinDistance * 1024), 0, 1);
}
return fog; return fog;
} }
@ -363,23 +373,37 @@ float3 CombineLights(float3 ambient, float3 vertex, float3 tex, float3 pos, floa
float3 shadow = 0; float3 shadow = 0;
float3 spec = 0; float3 spec = 0;
int lightTypeMask = (numLights & ~LT_MASK);
numLights = numLights & LT_MASK;
for (int i = 0; i < numLights; i++) for (int i = 0; i < numLights; i++)
{ {
float isPoint = step(0.5f, float(lights[i].Type == LT_POINT)); if (lightTypeMask & LT_MASK_SUN)
float isSpot = step(0.5f, float(lights[i].Type == LT_SPOT)); {
float isSun = step(0.5f, float(lights[i].Type == LT_SUN)); float isSun = step(0.5f, float(lights[i].Type == LT_SUN));
float isShadow = step(0.5f, float(lights[i].Type == LT_SHADOW)); diffuse += isSun * DoDirectionalLight(pos, normal, lights[i]);
spec += isSun * DoSpecularSun(normal, lights[i], sheen);
}
diffuse += isPoint * DoPointLight(pos, normal, lights[i]); if (lightTypeMask & LT_MASK_POINT)
spec += isPoint * DoSpecularPoint(pos, normal, lights[i], sheen); {
float isPoint = step(0.5f, float(lights[i].Type == LT_POINT));
diffuse += isPoint * DoPointLight(pos, normal, lights[i]);
spec += isPoint * DoSpecularPoint(pos, normal, lights[i], sheen);
}
diffuse += isSpot * DoSpotLight(pos, normal, lights[i]); if (lightTypeMask & LT_MASK_SPOT)
spec += isSpot * DoSpecularSpot(pos, normal, lights[i], sheen); {
float isSpot = step(0.5f, float(lights[i].Type == LT_SPOT));
diffuse += isSun * DoDirectionalLight(pos, normal, lights[i]); diffuse += isSpot * DoSpotLight(pos, normal, lights[i]);
spec += isSun * DoSpecularSun(normal, lights[i], sheen); spec += isSpot * DoSpecularSpot(pos, normal, lights[i], sheen);
}
shadow += isShadow * DoShadowLight(pos, normal, lights[i]);
if (lightTypeMask & LT_MASK_SHADOW)
{
float isShadow = step(0.5f, float(lights[i].Type == LT_SHADOW));
shadow += isShadow * DoShadowLight(pos, normal, lights[i]);
}
} }
shadow = saturate(shadow); shadow = saturate(shadow);

View file

@ -1,3 +1,5 @@
#include "./Blending.hlsli"
#include "./Math.hlsli"
#include "./ShaderLight.hlsli" #include "./ShaderLight.hlsli"
#define SHADOW_INTENSITY (0.55f) #define SHADOW_INTENSITY (0.55f)
@ -86,20 +88,32 @@ float3 DoBlobShadows(float3 worldPos, float3 lighting)
return lighting * saturate(shadowFactor + SHADOW_INTENSITY); return lighting * saturate(shadowFactor + SHADOW_INTENSITY);
} }
float3 DoPointLightShadow(float3 worldPos, float3 lighting) float3 DoShadow(float3 worldPos, float3 normal, float3 lighting, float bias)
{ {
if (!CastShadows)
return lighting;
if (BlendMode != BLENDMODE_OPAQUE && BlendMode != BLENDMODE_ALPHATEST && BlendMode != BLENDMODE_ALPHABLEND)
return lighting;
float shadowFactor = 1.0f; float shadowFactor = 1.0f;
float3 dir = normalize(Light.Position - worldPos);
float ndot = dot(normal, dir);
float facingFactor = saturate((ndot - bias) / (1.0f - bias + EPSILON));
[unroll] [unroll]
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
float3 dir = normalize(worldPos - Light.Position);
float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[i]); float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[i]);
lightClipSpace.xyz /= lightClipSpace.w; lightClipSpace.xyz /= lightClipSpace.w;
if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f && float insideLightBounds =
lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f && step(-1.0f, lightClipSpace.x) * step(lightClipSpace.x, 1.0f) *
lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f) step(-1.0f, lightClipSpace.y) * step(lightClipSpace.y, 1.0f) *
step( 0.0f, lightClipSpace.z) * step(lightClipSpace.z, 1.0f);
if (insideLightBounds > 0.0f)
{ {
lightClipSpace.x = lightClipSpace.x / 2 + 0.5; lightClipSpace.x = lightClipSpace.x / 2 + 0.5;
lightClipSpace.y = lightClipSpace.y / -2 + 0.5; lightClipSpace.y = lightClipSpace.y / -2 + 0.5;
@ -108,63 +122,29 @@ float3 DoPointLightShadow(float3 worldPos, float3 lighting)
float x, y; float x, y;
// Perform PCF filtering on a 4 x 4 texel neighborhood. // Perform PCF filtering on a 4 x 4 texel neighborhood.
[unroll] [unroll]
for (y = -1.5; y <= 1.5; y += 1.0) for (y = -1.5; y <= 1.5; y += 1.0)
{ {
[unroll] [unroll]
for (x = -1.5; x <= 1.5; x += 1.0) for (x = -1.5; x <= 1.5; x += 1.0)
{ {
sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z); sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z);
} }
} }
shadowFactor = sum / 16.0; shadowFactor = lerp(shadowFactor, sum / 16.0, facingFactor * insideLightBounds);
} }
} }
// Compute attenuation and combine lighting contribution with shadow factor
float distanceFactor = saturate(((distance(worldPos, Light.Position)) / (Light.Out)));
return lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(distanceFactor, 4) * INV_SHADOW_INTENSITY));
}
float3 DoSpotLightShadow(float3 worldPos, float3 normal, float3 lighting) float isPoint = step(0.5f, Light.Type == LT_POINT); // 1.0 if LT_POINT, 0.0 otherwise
{ float isSpot = step(0.5f, Light.Type == LT_SPOT); // 1.0 if LT_SPOT, 0.0 otherwise
float influence = 1.0f - Luma(DoSpotLight(worldPos, normal, Light)); float isOther = 1.0 - (isPoint + isSpot); // 1.0 if neither LT_POINT nor LT_SPOT
float shadowFactor = 1.0f;
[unroll]
for (int i = 0; i < 6; i++)
{
float3 dir = normalize(worldPos - Light.Position);
float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[i]);
lightClipSpace.xyz /= lightClipSpace.w;
if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f && float pointFactor = 1.0f - Luma(DoPointLight(worldPos, normal, Light));
lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f && float spotFactor = 1.0f - Luma(DoSpotLight(worldPos, normal, Light));
lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f)
{
lightClipSpace.x = lightClipSpace.x / 2 + 0.5;
lightClipSpace.y = lightClipSpace.y / -2 + 0.5;
float sum = 0; float3 pointShadow = lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(pointFactor, 4) * INV_SHADOW_INTENSITY));
float x, y; float3 spotShadow = lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(spotFactor, 4) * INV_SHADOW_INTENSITY));
// Perform PCF filtering on a 4 x 4 texel neighborhood. return pointShadow * isPoint + spotShadow * isSpot + lighting * isOther;
[unroll] }
for (y = -1.5; y <= 1.5; y += 1.0)
{
[unroll]
for (x = -1.5; x <= 1.5; x += 1.0)
{
sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z);
}
}
shadowFactor = sum / 16.0;
}
}
// Compute attenuation and combine lighting contribution with shadow factor
return lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(influence, 4) * INV_SHADOW_INTENSITY));
}

View file

@ -5,6 +5,7 @@
#include "./VertexInput.hlsli" #include "./VertexInput.hlsli"
#include "./Blending.hlsli" #include "./Blending.hlsli"
#include "./CBStatic.hlsli" #include "./CBStatic.hlsli"
#include "./Shadows.hlsli"
struct PixelShaderInput struct PixelShaderInput
{ {
@ -92,6 +93,8 @@ PixelShaderOutput PS(PixelShaderInput input)
input.FogBulbs.w) : input.FogBulbs.w) :
StaticLight(input.Color.xyz, tex.xyz, input.FogBulbs.w); StaticLight(input.Color.xyz, tex.xyz, input.FogBulbs.w);
color = DoShadow(input.WorldPosition, normal, color, -0.5f);
output.Color = float4(color, tex.w); output.Color = float4(color, tex.w);
output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f)); output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f));
output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog); output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog);

View file

@ -4,34 +4,26 @@
float Wibble(float3 effect, int hash) float Wibble(float3 effect, int hash)
{ {
if (effect.x > 0.0f || effect.y > 0.0f) float shouldWibble = step(0.0f, effect.x + effect.y);
return sin((((Frame + hash) % 256) / WIBBLE_FRAME_PERIOD) * (PI2)); float wibble = sin((((Frame + hash) % 256) / WIBBLE_FRAME_PERIOD) * PI2);
else
return 0.0f; // Don't calculate if not necessary return wibble * shouldWibble;
} }
float3 Glow(float3 color, float3 effect, float wibble) float3 Glow(float3 color, float3 effect, float wibble)
{ {
float3 col = color; float shouldGlow = step(0.0f, effect.x);
float intensity = effect.x * lerp(-0.5f, 1.0f, wibble * 0.5f + 0.5f);
float3 glowEffect = float3(intensity, intensity, intensity) * shouldGlow;
if (effect.x > 0.0f) return color + glowEffect;
{
float intensity = effect.x * lerp(-0.5f, 1.0f, wibble * 0.5f + 0.5f);
col = color + float3(intensity, intensity, intensity);
}
return col;
} }
float3 Move(float3 position, float3 effect, float wibble) float3 Move(float3 position, float3 effect, float wibble)
{ {
float3 pos = position; float weight = effect.z;
float weight = effect.z; float shouldMove = step(0.0f, effect.y) * step(0.0f, weight);
float offset = wibble * effect.y * weight * 128.0f * shouldMove;
if (effect.y > 0.0f && weight > 0.0f) return position + float3(0.0f, offset, 0.0f);
{
pos.y += wibble * effect.y * weight * 128.0f; // 128 units offset to top and bottom (256 total)
}
return pos;
} }

View file

@ -1379,7 +1379,10 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings.
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent>
<FileType>Text</FileType> <FileType>Text</FileType>
</None> </None>
<None Include="Shaders\Blending.hlsli" /> <None Include="Shaders\Blending.hlsli">
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</DeploymentContent>
<FileType>Document</FileType>
</None>
<None Include="Shaders\CBPostProcess.hlsli"> <None Include="Shaders\CBPostProcess.hlsli">
<DeploymentContent>true</DeploymentContent> <DeploymentContent>true</DeploymentContent>
</None> </None>