Merge branch 'develop_60fps' of https://github.com/MontyTRC89/TombEngine into develop_60fps

This commit is contained in:
MontyTRC89 2024-05-17 13:39:10 +02:00
commit be95d77d92
8 changed files with 81 additions and 81 deletions

View file

@ -110,10 +110,10 @@ local strings =
total_secrets_found = { "Secrets Found Total" },
use = { "Use" },
used_medipacks = { "Medipacks Used" },
variable_framerate = { "Variable Framerate" },
vehicle_actions = { "Vehicle Actions" },
view = { "View" },
volumetric_fog = { "Volumetric Fog" },
variable_framerate = { "Variable Framerate" },
waiting_for_input = { "Waiting For Input" },
window_title = { "TombEngine" },
windowed = { "Windowed" },

View file

@ -48,7 +48,7 @@ namespace TEN::Hud
void SpeedometerController::Draw() const
{
constexpr auto POS = Vector2(DISPLAY_SPACE_RES.x - (DISPLAY_SPACE_RES.x / 6), DISPLAY_SPACE_RES.y - (DISPLAY_SPACE_RES.y / 10));
constexpr auto ORIENT_OFFSET = ANGLE(90.0f);
constexpr auto POINTER_ANGLE_OFFSET = ANGLE(90.0f);
constexpr auto SCALE = Vector2(0.35f);
constexpr auto DIAL_ELEMENT_SPRITE_ID = 0;
constexpr auto POINTER_ELEMENT_SPRITE_ID = 1;
@ -60,9 +60,8 @@ namespace TEN::Hud
if (_life <= 0.0f)
return;
// TODO: Interpolation.
auto color = Color(1.0f, 1.0f, 1.0f, _opacity);
short pointerAngle = (short)Lerp(_prevPointerAngle, _pointerAngle, g_Renderer.GetInterpolationFactor());
auto color = Color(1.0f, 1.0f, 1.0f, Lerp(_prevOpacity, _opacity, g_Renderer.GetInterpolationFactor()));
// Draw dial.
AddDisplaySprite(
@ -74,7 +73,7 @@ namespace TEN::Hud
// Draw pointer.
AddDisplaySprite(
ID_SPEEDOMETER, POINTER_ELEMENT_SPRITE_ID,
POS, _pointerAngle + ORIENT_OFFSET, SCALE, color,
POS, pointerAngle + POINTER_ANGLE_OFFSET, SCALE, color,
POINTER_PRIORITY, DisplaySpriteAlignMode::Center, DisplaySpriteScaleMode::Fit, BlendMode::AlphaBlend,
DisplaySpritePhase::Draw);
}

View file

@ -629,12 +629,6 @@ void UpdateLara(ItemInfo* item, bool isTitle)
// Control player.
InItemControlLoop = true;
// Copy current state to old state for interpolation
//memcpy(&item->OldPose, &item->Pose, sizeof(Pose));
//memcpy(&item->OldLocation, &item->Location, sizeof(Vector3i));
//memcpy(&item->OldAnimation, &item->Animation, sizeof(EntityAnimationData));
//memcpy(&OldLara, &Lara, sizeof(LaraInfo));
LaraControl(item, &LaraCollision);
HandlePlayerFlyCheat(*item);

View file

@ -93,9 +93,9 @@ constexpr auto WADE_WATER_DEPTH = STEPUP_HEIGHT;
constexpr auto SWIM_WATER_DEPTH = CLICK(2.75f);
constexpr auto SLOPE_DIFFERENCE = 60;
extern LaraInfo Lara;
extern LaraInfo PrevLara;
extern ItemInfo* LaraItem;
extern LaraInfo Lara;
extern LaraInfo PrevLara;
extern ItemInfo* LaraItem;
extern CollisionInfo LaraCollision;
void LaraControl(ItemInfo* item, CollisionInfo* coll);

View file

@ -202,7 +202,7 @@ void LookAt(CAMERA_INFO* cam, short roll)
float levelFarView = g_GameFlow->GetLevel(CurrentLevel)->GetFarView() * float(BLOCK(1));
g_Renderer.UpdateCameraMatrices(cam, r, fov, levelFarView);
g_Renderer.UpdateCameraMatrices(cam, r, fov, levelFarView);
}
void AlterFOV(short value, bool store)

View file

@ -153,20 +153,18 @@ GameStatus ControlPhase()
SetupInterpolation();
g_Renderer.SaveOldState();
// Controls are polled before OnLoop, so input data could be
// overwritten by script API methods.
// Controls are polled before OnLoop to allow input data to be overwritten by script API methods.
HandleControls(isTitle);
// Pre-loop script and event handling.
g_GameScript->OnLoop(DELTA_TIME, false); // TODO: Don't use DELTA_TIME constant with variable framerate
HandleAllGlobalEvents(EventType::Loop, (Activator)LaraItem->Index);
// Control lock is processed after handling scripts, because builder may want to
// process input externally, while still locking Lara from input.
// Control lock is processed after handling scripts because builder may want to process input externally while locking player from input.
if (!isTitle && Lara.Control.IsLocked)
ClearAllActions();
// Handle inventory / pause / load / save screens.
// Handle inventory, pause, load, save screens.
auto result = HandleMenuCalls(isTitle);
if (result != GameStatus::Normal)
return result;
@ -176,9 +174,8 @@ GameStatus ControlPhase()
if (result != GameStatus::Normal)
return result;
// Queued input actions are read again and cleared after UI
// interrupts are processed, so first frame after exiting UI
// will still register it.
// Queued input actions are read again and cleared after UI interrupts are processed,
// so first game frame after exiting UI will still register it.
ApplyActionQueue();
ClearActionQueue();
@ -188,14 +185,14 @@ GameStatus ControlPhase()
g_GameScriptEntities->TestCollidingObjects();
// Draw flyby cameras.
if (UseSpotCam)
{
// Draw flyby cameras.
CalculateSpotCameras();
}
// Do standard camera.
else
{
// Do the standard camera.
TrackCameraInit = false;
CalculateCamera(LaraCollision);
}
@ -206,11 +203,11 @@ GameStatus ControlPhase()
// Smash shatters and clear stopper flags under them.
UpdateShatters();
// Clear last selected item in inventory (need to be after on loop event handling, so they can detect that).
// Clear last selected item in inventory (must be after on loop event handling, so they can detect that).
g_Gui.CancelInventorySelection();
// Control lock is processed after handling scripts, because builder may want to
// process input externally, while still locking Lara from input.
// Control lock is processed after handling scripts because builder may want to
// process input externally while locking player from input.
if (!isTitle && Lara.Control.IsLocked)
ClearAllActions();
@ -286,22 +283,26 @@ GameStatus ControlPhase()
unsigned CALLBACK GameMain(void *)
{
TENLog("Starting GameMain...", LogLevel::Info);
TENLog("Starting GameMain()...", LogLevel::Info);
TimeInit();
// Do a fixed time title image.
// Do fixed-time title image.
if (g_GameFlow->IntroImagePath.empty())
TENLog("Intro image path is not set.", LogLevel::Warning);
{
TENLog("Intro image path not set.", LogLevel::Warning);
}
else
{
g_Renderer.RenderTitleImage();
}
// Execute the Lua gameflow and play the game.
// Execute Lua gameflow and play game.
g_GameFlow->DoFlow();
DoTheGame = false;
// Finish the thread.
// Finish thread.
PostMessage(WindowsHandle, WM_CLOSE, NULL, NULL);
EndThread();
@ -317,7 +318,7 @@ GameStatus DoLevel(int levelIndex, bool loadGame)
// Load level. Fall back to title if unsuccessful.
if (!LoadLevelFile(levelIndex))
return isTitle ? GameStatus::ExitGame : GameStatus::ExitToTitle;
return (isTitle ? GameStatus::ExitGame : GameStatus::ExitToTitle);
// Initialize items, effects, lots, and cameras.
HairEffect.Initialize();
@ -374,11 +375,15 @@ void KillMoveItems()
{
for (int i = 0; i < ItemNewRoomNo; i++)
{
short itemNumber = ItemNewRooms[2 * i];
int itemNumber = ItemNewRooms[i * 2];
if (itemNumber >= 0)
ItemNewRoom(itemNumber, ItemNewRooms[2 * i + 1]);
{
ItemNewRoom(itemNumber, ItemNewRooms[(i * 2) + 1]);
}
else
{
KillItem(itemNumber & 0x7FFF);
}
}
}
@ -391,11 +396,15 @@ void KillMoveEffects()
{
for (int i = 0; i < ItemNewRoomNo; i++)
{
short itemNumber = ItemNewRooms[2 * i];
int itemNumber = ItemNewRooms[i * 2];
if (itemNumber >= 0)
EffectNewRoom(itemNumber, ItemNewRooms[2 * i + 1]);
{
EffectNewRoom(itemNumber, ItemNewRooms[(i * 2) + 1]);
}
else
{
KillEffect(itemNumber & 0x7FFF);
}
}
}
@ -417,13 +426,13 @@ void CleanUp()
// Reset oscillator seed.
Wibble = 0;
// Needs to be cleared, otherwise controls will lock if user exits to title while playing flyby with locked controls.
// Clear player lock, otherwise controls will lock if user exits to title while playing flyby with locked controls.
Lara.Control.IsLocked = false;
// Resets lightning and wind parameters to avoid holding over previous weather to new level.
Weather.Clear();
// Needs to be cleared, otherwise a list of active creatures from previous level will spill into new level.
// Clear creatures, otherwise list of active creatures from previous level will spill into new level.
ActiveCreatures.clear();
// Clear ropes.
@ -473,26 +482,27 @@ void InitializeScripting(int levelIndex, LevelLoadType type)
g_GameStringsHandler->ClearDisplayStrings();
g_GameScript->ResetScripts(!levelIndex || type != LevelLoadType::New);
auto* level = g_GameFlow->GetLevel(levelIndex);
const auto& level = *g_GameFlow->GetLevel(levelIndex);
// Run level script if it exists.
if (!level->ScriptFileName.empty())
if (!level.ScriptFileName.empty())
{
g_GameScript->ExecuteScriptFile(g_GameFlow->GetGameDir() + level->ScriptFileName);
g_GameScript->ExecuteScriptFile(g_GameFlow->GetGameDir() + level.ScriptFileName);
g_GameScript->InitCallbacks();
g_GameStringsHandler->SetCallbackDrawString([](const std::string& key, D3DCOLOR color, const Vec2& pos, float scale, int flags)
{
g_Renderer.AddString(
key,
Vector2(((float)pos.x / (float)g_Configuration.ScreenWidth * DISPLAY_SPACE_RES.x),
((float)pos.y / (float)g_Configuration.ScreenHeight * DISPLAY_SPACE_RES.y)),
Vector2(
(pos.x / g_Configuration.ScreenWidth) * DISPLAY_SPACE_RES.x,
(pos.y / g_Configuration.ScreenHeight) * DISPLAY_SPACE_RES.y),
Color(color), scale, flags);
});
}
// Play default background music.
if (type != LevelLoadType::Load)
PlaySoundTrack(level->GetAmbientTrack(), SoundTrackType::BGM);
PlaySoundTrack(level.GetAmbientTrack(), SoundTrackType::BGM);
}
void DeInitializeScripting(int levelIndex, GameStatus reason)
@ -503,7 +513,7 @@ void DeInitializeScripting(int levelIndex, GameStatus reason)
g_GameScript->FreeLevelScripts();
g_GameScriptEntities->FreeEntities();
if (!levelIndex)
if (levelIndex == 0)
g_GameScript->ResetScripts(true);
}
@ -512,7 +522,7 @@ void InitializeOrLoadGame(bool loadGame)
g_Gui.SetInventoryItemChosen(NO_VALUE);
g_Gui.SetEnterInventory(NO_VALUE);
// Restore the game?
// Restore game?
if (loadGame)
{
SaveGame::Load(g_GameFlow->SelectedSaveGame);
@ -533,7 +543,7 @@ void InitializeOrLoadGame(bool loadGame)
}
else
{
// If not loading a savegame, clear all info.
// If not loading savegame, clear all info.
SaveGame::Statistics.Level = {};
if (InitializeGame)
@ -559,19 +569,19 @@ void InitializeOrLoadGame(bool loadGame)
GameStatus DoGameLoop(int levelIndex)
{
int numFrames = LOOP_FRAME_COUNT;
constexpr auto CONTROL_FRAME_TIME = 1000.0f / 30.0f;
int frameCount = LOOP_FRAME_COUNT;
auto& status = g_GameFlow->LastGameStatus;
// Before entering actual game loop, ControlPhase must be
// Before entering actual game loop, ControlPhase() must be
// called once to sort out various runtime shenanigangs (e.g. hair).
status = ControlPhase();
LARGE_INTEGER lastTime;
LARGE_INTEGER currentTime;
double controlLag = 0;
double frameTime = 0;
constexpr auto controlFrameTime = 1000.0f / 30.0f;
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
@ -600,21 +610,21 @@ GameStatus DoGameLoop(int levelIndex)
controlLag += frameTime;
}
while (controlLag >= controlFrameTime)
while (controlLag >= CONTROL_FRAME_TIME)
{
#if _DEBUG
constexpr auto DEBUG_SKIP_FRAMES = 10;
if (controlLag >= DEBUG_SKIP_FRAMES * controlFrameTime)
if (controlLag >= DEBUG_SKIP_FRAMES * CONTROL_FRAME_TIME)
{
TENLog("Game loop is running too slow!", LogLevel::Warning);
TENLog("Game loop is running too slow.", LogLevel::Warning);
App.ResetClock = true;
break;
}
#endif
status = ControlPhase();
controlLag -= controlFrameTime;
controlLag -= CONTROL_FRAME_TIME;
controlCalls++;
legacy30FpsDoneDraw = false;
@ -634,7 +644,7 @@ GameStatus DoGameLoop(int levelIndex)
}
else
{
float interpolationFactor = std::min((float)controlLag / (float)controlFrameTime, 1.0f);
float interpolationFactor = std::min((float)controlLag / (float)CONTROL_FRAME_TIME, 1.0f);
DrawPhase(!levelIndex, interpolationFactor);
drawCalls++;
}
@ -658,9 +668,7 @@ void EndGameLoop(int levelIndex, GameStatus reason)
void SetupInterpolation()
{
for (int i = 0; i < g_Level.Items.size(); i++)
{
g_Level.Items[i].DisableInterpolation = false;
}
}
void HandleControls(bool isTitle)
@ -675,10 +683,10 @@ void HandleControls(bool isTitle)
GameStatus HandleMenuCalls(bool isTitle)
{
auto result = GameStatus::Normal;
auto gameStatus = GameStatus::Normal;
if (ScreenFading)
return result;
return gameStatus;
if (isTitle)
{
@ -697,10 +705,10 @@ GameStatus HandleMenuCalls(bool isTitle)
return GameStatus::ExitGame;
}
return result;
return gameStatus;
}
// Does the player want to enter inventory?
// Handle inventory.
if (IsClicked(In::Save) && LaraItem->HitPoints > 0 &&
g_Gui.GetInventoryMode() != InventoryMode::Save &&
g_GameFlow->IsLoadSaveEnabled())
@ -717,28 +725,28 @@ GameStatus HandleMenuCalls(bool isTitle)
g_Gui.SetInventoryMode(InventoryMode::Load);
if (g_Gui.CallInventory(LaraItem, false))
result = GameStatus::LoadGame;
gameStatus = GameStatus::LoadGame;
}
else if (IsClicked(In::Pause) && LaraItem->HitPoints > 0 &&
g_Gui.GetInventoryMode() != InventoryMode::Pause)
{
if (g_Gui.CallPause())
result = GameStatus::ExitToTitle;
gameStatus = GameStatus::ExitToTitle;
}
else if ((IsClicked(In::Inventory) || g_Gui.GetEnterInventory() != NO_VALUE) &&
LaraItem->HitPoints > 0 && !Lara.Control.Look.IsUsingBinoculars)
{
if (g_Gui.CallInventory(LaraItem, true))
result = GameStatus::LoadGame;
gameStatus = GameStatus::LoadGame;
}
if (result != GameStatus::Normal)
if (gameStatus != GameStatus::Normal)
{
StopAllSounds();
StopRumble();
}
return result;
return gameStatus;
}
GameStatus HandleGlobalInputEvents(bool isTitle)

View file

@ -3588,8 +3588,8 @@ namespace TEN::Gui
auto needleOrient = EulerAngles(0, CompassNeedleAngle, 0);
needleOrient.Lerp(EulerAngles(0, item->Pose.Orientation.y, 0), LERP_ALPHA);
float wibble = std::sin(((float)(GameTimer & 0x3F) / (float)0x3F) * PI_MUL_2);
CompassNeedleAngle = needleOrient.y + ANGLE(wibble);
float wibble = std::sin((float(GameTimer & 0x3F) / (float)0x3F) * PI_MUL_2);
CompassNeedleAngle = needleOrient.y + ANGLE(wibble / 2);
// HACK: Needle is rotated in the draw function.
const auto& invObject = InventoryObjectTable[INV_OBJECT_COMPASS];

View file

@ -3547,8 +3547,8 @@ namespace TEN::Renderer
void Renderer::DrawItemSorted(RendererSortableObject* objectInfo, RendererObjectType lastObjectType, RenderView& view)
{
UINT stride = sizeof(Vertex);
UINT offset = 0;
unsigned int stride = sizeof(Vertex);
unsigned int offset = 0;
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
_context->IASetInputLayout(_inputLayout.Get());
@ -3561,25 +3561,24 @@ namespace TEN::Renderer
_context->VSSetShader(_vsItems.Get(), nullptr, 0);
_context->PSSetShader(_psItems.Get(), nullptr, 0);
ItemInfo* nativeItem = &g_Level.Items[objectInfo->Item->ItemNumber];
RendererRoom* room = &_rooms[objectInfo->Item->RoomNumber];
RendererObject& moveableObj = *_moveableObjects[objectInfo->Item->ObjectNumber];
// Bind item main properties
// Bind main item properties.
_stItem.World = objectInfo->Item->InterpolatedWorld;
_stItem.Color = objectInfo->Item->Color;
_stItem.AmbientLight = objectInfo->Item->AmbientLight;
memcpy(_stItem.BonesMatrices, objectInfo->Item->InterpolatedAnimationTransforms, sizeof(Matrix) * MAX_BONES);
const auto& moveableObj = *_moveableObjects[objectInfo->Item->ObjectNumber];
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);
_cbItem.UpdateData(_stItem, _context.Get());
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[objectInfo->Bucket->Texture]),
BindTexture(
TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[objectInfo->Bucket->Texture]),
SamplerStateRegister::AnisotropicClamp);
BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[objectInfo->Bucket->Texture]),
BindTexture(
TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[objectInfo->Bucket->Texture]),
SamplerStateRegister::AnisotropicClamp);
_sortedPolygonsIndexBuffer.Update(_context.Get(), _sortedPolygonsIndices, 0, (int)_sortedPolygonsIndices.size());