From 8a0c695b020e18320799d4faaa1b718da5587e21 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sat, 22 Feb 2025 17:42:01 +0100 Subject: [PATCH] tr2: add 60 fps --- docs/tr1/README.md | 1 + docs/tr2/CHANGELOG.md | 2 + docs/tr2/COMMANDS.md | 4 + docs/tr2/README.md | 1 + src/libtrx/config/map_tr2.def | 1 + src/libtrx/config/priv_tr2.c | 3 + src/libtrx/game/camera/common.c | 10 + src/libtrx/game/interpolation.c | 213 +++++++++++++++++- src/libtrx/include/libtrx/config/types_tr2.h | 1 + .../include/libtrx/game/camera/common.h | 1 + src/libtrx/include/libtrx/game/camera/types.h | 6 +- src/libtrx/include/libtrx/game/effects.h | 1 + .../include/libtrx/game/effects/common.h | 14 ++ .../include/libtrx/game/effects/types.h | 2 - .../include/libtrx/game/interpolation.h | 6 + src/libtrx/include/libtrx/game/items/types.h | 2 - src/libtrx/include/libtrx/game/lara/hair.h | 21 ++ src/libtrx/include/libtrx/game/lara/types.h | 13 ++ src/libtrx/include/libtrx/game/rooms/common.h | 2 + src/tr1/game/camera/common.c | 5 - src/tr1/game/camera/common.h | 1 - src/tr1/game/cutscene.c | 2 +- src/tr1/game/demo.c | 2 +- src/tr1/game/effects.h | 10 - src/tr1/game/game/game.c | 2 +- src/tr1/game/game/game_draw.c | 2 +- src/tr1/game/interpolation.c | 210 ----------------- src/tr1/game/interpolation.h | 9 - src/tr1/game/inventory_ring/control.c | 2 +- src/tr1/game/inventory_ring/draw.c | 2 +- src/tr1/game/items.c | 2 +- src/tr1/game/lara/hair.h | 19 +- .../game/objects/traps/lightning_emitter.c | 4 +- src/tr1/game/room.h | 1 - src/tr1/meson.build | 1 - src/tr2/decomp/flares.c | 4 +- src/tr2/decomp/skidoo.c | 4 +- src/tr2/game/camera.c | 7 +- src/tr2/game/clock.c | 2 +- src/tr2/game/collide.c | 4 +- src/tr2/game/console/cmd/easy_config.c | 3 +- src/tr2/game/cutscene.c | 5 +- src/tr2/game/demo.c | 2 + src/tr2/game/effects.c | 14 +- src/tr2/game/effects.h | 10 - src/tr2/game/game.c | 6 +- src/tr2/game/inventory_ring/control.c | 4 +- src/tr2/game/inventory_ring/draw.c | 5 + src/tr2/game/items.c | 30 ++- src/tr2/game/lara/draw.c | 24 +- src/tr2/game/lara/hair.c | 35 ++- src/tr2/game/lara/hair.h | 4 +- src/tr2/game/level.c | 2 + src/tr2/game/objects/common.c | 9 +- src/tr2/game/objects/creatures/xian_common.c | 4 +- src/tr2/game/objects/general/sphere_of_doom.c | 4 +- src/tr2/game/output.c | 3 +- src/tr2/game/room.h | 2 - .../TR2X_ConfigTool/Resources/Lang/en.json | 4 + .../Resources/specification.json | 10 + 60 files changed, 436 insertions(+), 343 deletions(-) create mode 100644 src/libtrx/include/libtrx/game/effects/common.h create mode 100644 src/libtrx/include/libtrx/game/lara/hair.h delete mode 100644 src/tr1/game/interpolation.c delete mode 100644 src/tr1/game/interpolation.h diff --git a/docs/tr1/README.md b/docs/tr1/README.md index 415a2a27e..cb459ed95 100644 --- a/docs/tr1/README.md +++ b/docs/tr1/README.md @@ -388,6 +388,7 @@ Not all options are turned on by default. Refer to `TR1X_ConfigTool.exe` for det - added ability to use Lara's underwater swimming physics from TR2+ - added ability to wade, similar to TR2+ - added Lara's exit-water-to-medium-height animation from TR2+ +- added support for 60 FPS rendering - added a pause screen - added a choice whether to play NG or NG+ without having to play the entire game - added Japanese mode (guns deal twice the damage, inspired by JP release of TR3); available for both NG and NG+ diff --git a/docs/tr2/CHANGELOG.md b/docs/tr2/CHANGELOG.md index fa9280305..b4a4d686a 100644 --- a/docs/tr2/CHANGELOG.md +++ b/docs/tr2/CHANGELOG.md @@ -1,6 +1,8 @@ ## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-0.9.2...develop) - ××××-××-×× +- added support for 60 FPS rendering - added a `/cheats` console command - added a `/wireframe` console command (#2500) +- added a `/fps` console command - fixed smashed windows blocking enemy pathing after loading a save (#2535) - fixed several instances of the camera going out of bounds (#1034) - fixed Lara getting stuck in a T-pose after jumping/falling and then dying before reaching fast fall speed (#2575) diff --git a/docs/tr2/COMMANDS.md b/docs/tr2/COMMANDS.md index 757776d46..9e677cafe 100644 --- a/docs/tr2/COMMANDS.md +++ b/docs/tr2/COMMANDS.md @@ -81,6 +81,10 @@ Currently supported commands: - `/speed {num}` Retrieves or sets current game speed. +- `/fps` +- `/fps {num}` + Retrieves or sets current frames per second. + - `/set {option}` - `/set {option} {value}` Retrieves or assigns a new value to the given configuration option. Some options need a game re-launch to apply. The option names use `-` rather than `_`. diff --git a/docs/tr2/README.md b/docs/tr2/README.md index a166e874e..e1a6c43bb 100644 --- a/docs/tr2/README.md +++ b/docs/tr2/README.md @@ -39,6 +39,7 @@ game with new enhancements and features. - step bug - free flare from underwater pickup - drifting into walls during underwater pickups +- added support for 60 FPS rendering - added a pause screen - added a photo mode feature - changed inventory to pause the music rather than muting it diff --git a/src/libtrx/config/map_tr2.def b/src/libtrx/config/map_tr2.def index b893f107b..3f2d96aa8 100644 --- a/src/libtrx/config/map_tr2.def +++ b/src/libtrx/config/map_tr2.def @@ -24,6 +24,7 @@ CFG_BOOL(g_Config, visuals.use_pcx_fov, true) CFG_DOUBLE(g_Config, ui.text_scale, 1.0) CFG_DOUBLE(g_Config, ui.bar_scale, 1.0) CFG_BOOL(g_Config, ui.enable_photo_mode_ui, true) +CFG_INT32(g_Config, rendering.fps, 60) CFG_ENUM(g_Config, rendering.screenshot_format, SCREENSHOT_FORMAT_JPEG, SCREENSHOT_FORMAT) CFG_ENUM(g_Config, rendering.render_mode, RM_HARDWARE, RENDER_MODE) CFG_ENUM(g_Config, rendering.aspect_mode, AM_ANY, ASPECT_MODE) diff --git a/src/libtrx/config/priv_tr2.c b/src/libtrx/config/priv_tr2.c index bfe36f0e0..19953ccaf 100644 --- a/src/libtrx/config/priv_tr2.c +++ b/src/libtrx/config/priv_tr2.c @@ -133,6 +133,9 @@ void Config_Sanitize(void) } CLAMP(g_Config.rendering.nearest_adjustment, 0, 256); CLAMP(g_Config.rendering.linear_adjustment, 0, 256); + if (g_Config.rendering.fps != 30 && g_Config.rendering.fps != 60) { + g_Config.rendering.fps = 30; + } CLAMP(g_Config.visuals.fov, 30, 150); CLAMP(g_Config.ui.bar_scale, 0.5, 2.0); diff --git a/src/libtrx/game/camera/common.c b/src/libtrx/game/camera/common.c index 50f8618d1..cd2bd46ff 100644 --- a/src/libtrx/game/camera/common.c +++ b/src/libtrx/game/camera/common.c @@ -1,3 +1,8 @@ +#include "game/camera/common.h" + +#include "game/camera/vars.h" +#include "game/rooms/const.h" + static bool m_IsChunky = false; bool Camera_IsChunky(void) @@ -9,3 +14,8 @@ void Camera_SetChunky(const bool is_chunky) { m_IsChunky = is_chunky; } + +void Camera_Reset(void) +{ + g_Camera.pos.room_num = NO_ROOM; +} diff --git a/src/libtrx/game/interpolation.c b/src/libtrx/game/interpolation.c index 5fe888ba3..5241ad956 100644 --- a/src/libtrx/game/interpolation.c +++ b/src/libtrx/game/interpolation.c @@ -1,19 +1,49 @@ #include "game/interpolation.h" #include "config.h" +#include "game/camera/vars.h" +#include "game/effects.h" +#include "game/lara/common.h" +#include "game/lara/hair.h" +#include "game/rooms.h" +#include "utils.h" #include +#define REMEMBER(target, member) (target)->interp.prev.member = (target)->member + +#define COMMIT(target, member) (target)->interp.result.member = (target)->member + +#define INTERPOLATE_F(target, member, ratio) \ + (target)->interp.result.member = ((target)->interp.prev.member) \ + + (((target)->member - ((target)->interp.prev.member)) * (ratio)); + +#define INTERPOLATE(target, member, ratio, max_diff) \ + if (ABS(((target)->member) - ((target)->interp.prev.member)) \ + >= (max_diff)) { \ + COMMIT((target), member); \ + } else { \ + INTERPOLATE_F(target, member, ratio); \ + } + +#define INTERPOLATE_ROT(target, member, ratio, max_diff) \ + if (!Math_AngleInCone( \ + (target)->member, (target)->interp.prev.member, (max_diff))) { \ + COMMIT((target), member); \ + } else { \ + INTERPOLATE_ROT_F(target, member, ratio); \ + } + +#define INTERPOLATE_ROT_F(target, member, ratio) \ + (target)->interp.result.member = Math_AngleMean( \ + (target)->interp.prev.member, (target)->member, (ratio)) + static bool m_IsEnabled = true; static double m_Rate = 0.0; static int32_t M_GetFPS(void) { -#if TR_VERSION == 1 return g_Config.rendering.fps; -#elif TR_VERSION == 2 - return 30; -#endif } bool Interpolation_IsEnabled(void) @@ -43,3 +73,178 @@ void Interpolation_SetRate(double rate) { m_Rate = rate; } + +void Interpolation_Commit(void) +{ + const double ratio = Interpolation_GetRate(); + + if (g_Camera.pos.room_num != NO_ROOM) { + INTERPOLATE(&g_Camera, shift, ratio, 128); + INTERPOLATE(&g_Camera, pos.x, ratio, 512); + INTERPOLATE(&g_Camera, pos.y, ratio, 512); + INTERPOLATE(&g_Camera, pos.z, ratio, 512); + INTERPOLATE(&g_Camera, target.x, ratio, 512); + INTERPOLATE(&g_Camera, target.y, ratio, 512); + INTERPOLATE(&g_Camera, target.z, ratio, 512); + + g_Camera.interp.room_num = g_Camera.pos.room_num; + Room_GetSector( + g_Camera.interp.result.pos.x, + g_Camera.interp.result.pos.y + g_Camera.interp.result.shift, + g_Camera.interp.result.pos.z, &g_Camera.interp.room_num); + } + + LARA_INFO *const lara = Lara_GetLaraInfo(); + INTERPOLATE_ROT(&lara->left_arm, rot.x, ratio, DEG_45); + INTERPOLATE_ROT(&lara->left_arm, rot.y, ratio, DEG_45); + INTERPOLATE_ROT(&lara->left_arm, rot.z, ratio, DEG_45); + INTERPOLATE_ROT(&lara->right_arm, rot.x, ratio, DEG_45); + INTERPOLATE_ROT(&lara->right_arm, rot.y, ratio, DEG_45); + INTERPOLATE_ROT(&lara->right_arm, rot.z, ratio, DEG_45); + INTERPOLATE_ROT(lara, torso_rot.x, ratio, DEG_45); + INTERPOLATE_ROT(lara, torso_rot.y, ratio, DEG_45); + INTERPOLATE_ROT(lara, torso_rot.z, ratio, DEG_45); + INTERPOLATE_ROT(lara, head_rot.x, ratio, DEG_45); + INTERPOLATE_ROT(lara, head_rot.y, ratio, DEG_45); + INTERPOLATE_ROT(lara, head_rot.z, ratio, DEG_45); + + for (int i = 0; i < Item_GetTotalCount(); i++) { + ITEM *const item = Item_Get(i); + bool is_erratic = false; +#if TR_VERSION == 1 + is_erratic |= item->object_id == O_BAT; +#endif + if ((item->flags & IF_KILLED) || item->status == IS_INACTIVE + || is_erratic) { + COMMIT(item, pos.x); + COMMIT(item, pos.y); + COMMIT(item, pos.z); + COMMIT(item, rot.x); + COMMIT(item, rot.y); + COMMIT(item, rot.z); + continue; + } + + const int32_t max_xz = item->object_id == O_DART ? 200 : 128; + const int32_t max_y = MAX(128, item->fall_speed * 2); + INTERPOLATE(item, pos.x, ratio, max_xz); + INTERPOLATE(item, pos.y, ratio, max_y); + INTERPOLATE(item, pos.z, ratio, max_xz); + INTERPOLATE_ROT(item, rot.x, ratio, DEG_45); + INTERPOLATE_ROT(item, rot.y, ratio, DEG_45); + INTERPOLATE_ROT(item, rot.z, ratio, DEG_45); + } + + ITEM *const lara_item = Lara_GetItem(); + if (lara_item != nullptr) { + INTERPOLATE(lara_item, pos.x, ratio, 128); + INTERPOLATE( + lara_item, pos.y, ratio, MAX(128, lara_item->fall_speed * 2)); + INTERPOLATE(lara_item, pos.z, ratio, 128); + INTERPOLATE_ROT(lara_item, rot.x, ratio, DEG_45); + INTERPOLATE_ROT(lara_item, rot.y, ratio, DEG_45); + INTERPOLATE_ROT(lara_item, rot.z, ratio, DEG_45); + } + + int16_t effect_num = Effect_GetActiveNum(); + while (effect_num != NO_EFFECT) { + EFFECT *const effect = Effect_Get(effect_num); + INTERPOLATE(effect, pos.x, ratio, 128); + INTERPOLATE(effect, pos.y, ratio, MAX(128, effect->fall_speed * 2)); + INTERPOLATE(effect, pos.z, ratio, 128); + INTERPOLATE_ROT(effect, rot.x, ratio, DEG_45); + INTERPOLATE_ROT(effect, rot.y, ratio, DEG_45); + INTERPOLATE_ROT(effect, rot.z, ratio, DEG_45); + effect_num = effect->next_active; + } + + if (Lara_Hair_IsActive()) { + for (int i = 0; i < Lara_Hair_GetSegmentCount(); i++) { + HAIR_SEGMENT *const hair = Lara_Hair_GetSegment(i); + INTERPOLATE(hair, pos.x, ratio, 128); + INTERPOLATE( + hair, pos.y, ratio, MAX(128, lara_item->fall_speed * 2)); + INTERPOLATE(hair, pos.z, ratio, 128); + INTERPOLATE_ROT(hair, rot.x, ratio, DEG_45); + INTERPOLATE_ROT(hair, rot.y, ratio, DEG_45); + INTERPOLATE_ROT(hair, rot.z, ratio, DEG_45); + } + } +} + +void Interpolation_Remember(void) +{ + if (g_Camera.pos.room_num != NO_ROOM) { + REMEMBER(&g_Camera, shift); + REMEMBER(&g_Camera, pos.x); + REMEMBER(&g_Camera, pos.y); + REMEMBER(&g_Camera, pos.z); + REMEMBER(&g_Camera, target.x); + REMEMBER(&g_Camera, target.y); + REMEMBER(&g_Camera, target.z); + } + + LARA_INFO *const lara = Lara_GetLaraInfo(); + REMEMBER(&lara->left_arm, rot.x); + REMEMBER(&lara->left_arm, rot.y); + REMEMBER(&lara->left_arm, rot.z); + REMEMBER(&lara->right_arm, rot.x); + REMEMBER(&lara->right_arm, rot.y); + REMEMBER(&lara->right_arm, rot.z); + REMEMBER(lara, torso_rot.x); + REMEMBER(lara, torso_rot.y); + REMEMBER(lara, torso_rot.z); + REMEMBER(lara, head_rot.x); + REMEMBER(lara, head_rot.y); + REMEMBER(lara, head_rot.z); + + for (int i = 0; i < Item_GetTotalCount(); i++) { + ITEM *const item = Item_Get(i); + REMEMBER(item, pos.x); + REMEMBER(item, pos.y); + REMEMBER(item, pos.z); + REMEMBER(item, rot.x); + REMEMBER(item, rot.y); + REMEMBER(item, rot.z); + } + + ITEM *const lara_item = Lara_GetItem(); + if (lara_item != nullptr) { + REMEMBER(lara_item, pos.x); + REMEMBER(lara_item, pos.y); + REMEMBER(lara_item, pos.z); + REMEMBER(lara_item, rot.x); + REMEMBER(lara_item, rot.y); + REMEMBER(lara_item, rot.z); + } + + int16_t effect_num = Effect_GetActiveNum(); + while (effect_num != NO_EFFECT) { + EFFECT *const effect = Effect_Get(effect_num); + REMEMBER(effect, pos.x); + REMEMBER(effect, pos.y); + REMEMBER(effect, pos.z); + REMEMBER(effect, rot.x); + REMEMBER(effect, rot.y); + REMEMBER(effect, rot.z); + effect_num = effect->next_active; + } + + if (Lara_Hair_IsActive()) { + for (int i = 0; i < Lara_Hair_GetSegmentCount(); i++) { + HAIR_SEGMENT *const hair = Lara_Hair_GetSegment(i); + REMEMBER(hair, pos.x); + REMEMBER(hair, pos.y); + REMEMBER(hair, pos.z); + REMEMBER(hair, rot.x); + REMEMBER(hair, rot.y); + REMEMBER(hair, rot.z); + } + } +} + +void Interpolation_RememberItem(ITEM *item) +{ + item->interp.prev.pos = item->pos; + item->interp.prev.rot = item->rot; +} diff --git a/src/libtrx/include/libtrx/config/types_tr2.h b/src/libtrx/include/libtrx/config/types_tr2.h index c1cae83d2..4c1f51916 100644 --- a/src/libtrx/include/libtrx/config/types_tr2.h +++ b/src/libtrx/include/libtrx/config/types_tr2.h @@ -92,6 +92,7 @@ typedef struct { } window; struct { + int32_t fps; RENDER_MODE render_mode; ASPECT_MODE aspect_mode; bool enable_zbuffer; diff --git a/src/libtrx/include/libtrx/game/camera/common.h b/src/libtrx/include/libtrx/game/camera/common.h index c871c49dc..5e528525f 100644 --- a/src/libtrx/include/libtrx/game/camera/common.h +++ b/src/libtrx/include/libtrx/game/camera/common.h @@ -4,3 +4,4 @@ extern void Camera_Update(void); extern void Camera_Apply(void); bool Camera_IsChunky(void); void Camera_SetChunky(bool is_chunky); +void Camera_Reset(void); diff --git a/src/libtrx/include/libtrx/game/camera/types.h b/src/libtrx/include/libtrx/game/camera/types.h index bc802f0b8..9f9896091 100644 --- a/src/libtrx/include/libtrx/game/camera/types.h +++ b/src/libtrx/include/libtrx/game/camera/types.h @@ -34,6 +34,9 @@ typedef struct { // used for the manual camera control int16_t additional_angle; int16_t additional_elevation; +#else + XYZ_32 mic_pos; +#endif struct { struct { @@ -43,9 +46,6 @@ typedef struct { } result, prev; int16_t room_num; } interp; -#else - XYZ_32 mic_pos; -#endif } CAMERA_INFO; typedef struct { diff --git a/src/libtrx/include/libtrx/game/effects.h b/src/libtrx/include/libtrx/game/effects.h index 8255bcdcf..3b2901a72 100644 --- a/src/libtrx/include/libtrx/game/effects.h +++ b/src/libtrx/include/libtrx/game/effects.h @@ -1,4 +1,5 @@ #pragma once +#include "effects/common.h" #include "effects/const.h" #include "effects/types.h" diff --git a/src/libtrx/include/libtrx/game/effects/common.h b/src/libtrx/include/libtrx/game/effects/common.h new file mode 100644 index 000000000..31f0959fc --- /dev/null +++ b/src/libtrx/include/libtrx/game/effects/common.h @@ -0,0 +1,14 @@ +#pragma once + +#include "./types.h" + +extern void Effect_InitialiseArray(void); +extern void Effect_Control(void); + +extern EFFECT *Effect_Get(int16_t effect_num); +extern int16_t Effect_GetNum(const EFFECT *effect); +extern int16_t Effect_GetActiveNum(void); +extern int16_t Effect_Create(int16_t room_num); +extern void Effect_Kill(int16_t effect_num); +extern void Effect_NewRoom(int16_t effect_num, int16_t room_num); +extern void Effect_Draw(int16_t effect_num); diff --git a/src/libtrx/include/libtrx/game/effects/types.h b/src/libtrx/include/libtrx/game/effects/types.h index 7ed7324fd..f813d687b 100644 --- a/src/libtrx/include/libtrx/game/effects/types.h +++ b/src/libtrx/include/libtrx/game/effects/types.h @@ -24,12 +24,10 @@ typedef struct { int16_t counter; int16_t shade; -#if TR_VERSION == 1 struct { struct { XYZ_32 pos; XYZ_16 rot; } result, prev; } interp; -#endif } EFFECT; diff --git a/src/libtrx/include/libtrx/game/interpolation.h b/src/libtrx/include/libtrx/game/interpolation.h index b1b864581..7549327d5 100644 --- a/src/libtrx/include/libtrx/game/interpolation.h +++ b/src/libtrx/include/libtrx/game/interpolation.h @@ -1,8 +1,14 @@ #pragma once +#include "items/types.h" + bool Interpolation_IsEnabled(void); void Interpolation_Disable(void); void Interpolation_Enable(void); double Interpolation_GetRate(void); void Interpolation_SetRate(double rate); + +void Interpolation_Commit(void); +void Interpolation_Remember(void); +void Interpolation_RememberItem(ITEM *item); diff --git a/src/libtrx/include/libtrx/game/items/types.h b/src/libtrx/include/libtrx/game/items/types.h index b188db89f..28f338e35 100644 --- a/src/libtrx/include/libtrx/game/items/types.h +++ b/src/libtrx/include/libtrx/game/items/types.h @@ -69,12 +69,10 @@ typedef struct { uint16_t pad : 7; // 0x0200…0x8000 #endif -#if TR_VERSION == 1 struct { struct { XYZ_32 pos; XYZ_16 rot; } result, prev; } interp; -#endif } ITEM; diff --git a/src/libtrx/include/libtrx/game/lara/hair.h b/src/libtrx/include/libtrx/game/lara/hair.h new file mode 100644 index 000000000..566cca297 --- /dev/null +++ b/src/libtrx/include/libtrx/game/lara/hair.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../math.h" + +typedef struct { + XYZ_32 pos; + XYZ_16 rot; + struct { + struct { + XYZ_32 pos; + XYZ_16 rot; + } result, prev; + } interp; +} HAIR_SEGMENT; + +extern void Lara_Hair_Initialise(void); +extern bool Lara_Hair_IsActive(void); +extern void Lara_Hair_Draw(void); + +extern int32_t Lara_Hair_GetSegmentCount(void); +extern HAIR_SEGMENT *Lara_Hair_GetSegment(int32_t n); diff --git a/src/libtrx/include/libtrx/game/lara/types.h b/src/libtrx/include/libtrx/game/lara/types.h index cd010b5a6..e23b874ba 100644 --- a/src/libtrx/include/libtrx/game/lara/types.h +++ b/src/libtrx/include/libtrx/game/lara/types.h @@ -89,6 +89,12 @@ typedef struct { int16_t lock; XYZ_16 rot; int16_t flash_gun; + + struct { + struct { + XYZ_16 rot; + } result, prev; + } interp; } LARA_ARM; typedef struct { @@ -142,6 +148,13 @@ typedef struct { AMMO_INFO grenade_ammo; AMMO_INFO m16_ammo; CREATURE *creature; + + struct { + struct { + XYZ_16 head_rot; + XYZ_16 torso_rot; + } result, prev; + } interp; } LARA_INFO; #endif diff --git a/src/libtrx/include/libtrx/game/rooms/common.h b/src/libtrx/include/libtrx/game/rooms/common.h index 43f483665..8d24385c5 100644 --- a/src/libtrx/include/libtrx/game/rooms/common.h +++ b/src/libtrx/include/libtrx/game/rooms/common.h @@ -32,6 +32,8 @@ int16_t Room_GetIndexFromPos(int32_t x, int32_t y, int32_t z); int32_t Room_FindByPos(int32_t x, int32_t y, int32_t z); BOUNDS_32 Room_GetWorldBounds(void); +extern SECTOR *Room_GetSector( + int32_t x, int32_t y, int32_t z, int16_t *room_num); SECTOR *Room_GetWorldSector(const ROOM *room, int32_t x_pos, int32_t z_pos); SECTOR *Room_GetUnitSector( const ROOM *room, int32_t x_sector, int32_t z_sector); diff --git a/src/tr1/game/camera/common.c b/src/tr1/game/camera/common.c index 8ca6dbbf2..a25f89ca9 100644 --- a/src/tr1/game/camera/common.c +++ b/src/tr1/game/camera/common.c @@ -613,11 +613,6 @@ void Camera_Initialise(void) Camera_Update(); } -void Camera_Reset(void) -{ - g_Camera.pos.room_num = NO_ROOM; -} - void Camera_ResetPosition(void) { ASSERT(g_LaraItem != nullptr); diff --git a/src/tr1/game/camera/common.h b/src/tr1/game/camera/common.h index 62d706e04..a8a217c91 100644 --- a/src/tr1/game/camera/common.h +++ b/src/tr1/game/camera/common.h @@ -5,7 +5,6 @@ #include void Camera_Initialise(void); -void Camera_Reset(void); void Camera_ResetPosition(void); void Camera_UpdateCutscene(void); void Camera_RefreshFromTrigger(const TRIGGER *trigger); diff --git a/src/tr1/game/cutscene.c b/src/tr1/game/cutscene.c index bbdf64bc6..65f8d31fe 100644 --- a/src/tr1/game/cutscene.c +++ b/src/tr1/game/cutscene.c @@ -5,7 +5,6 @@ #include "game/game.h" #include "game/game_flow.h" #include "game/input.h" -#include "game/interpolation.h" #include "game/items.h" #include "game/lara/common.h" #include "game/lara/hair.h" @@ -19,6 +18,7 @@ #include "global/vars.h" #include +#include #include static void M_InitialiseLara(const GF_LEVEL *level); diff --git a/src/tr1/game/demo.c b/src/tr1/game/demo.c index 0ba2166de..5bcc8454b 100644 --- a/src/tr1/game/demo.c +++ b/src/tr1/game/demo.c @@ -6,7 +6,6 @@ #include "game/game_flow.h" #include "game/game_string.h" #include "game/input.h" -#include "game/interpolation.h" #include "game/item_actions.h" #include "game/lara/cheat.h" #include "game/lara/common.h" @@ -24,6 +23,7 @@ #include #include +#include #include #define MODIFY_CONFIG() \ diff --git a/src/tr1/game/effects.h b/src/tr1/game/effects.h index 1687b2a3f..a4d673b6a 100644 --- a/src/tr1/game/effects.h +++ b/src/tr1/game/effects.h @@ -1,13 +1,3 @@ #pragma once #include - -void Effect_InitialiseArray(void); -EFFECT *Effect_Get(int16_t effect_num); -int16_t Effect_GetNum(const EFFECT *effect); -int16_t Effect_GetActiveNum(void); -void Effect_Control(void); -int16_t Effect_Create(int16_t room_num); -void Effect_Kill(int16_t effect_num); -void Effect_NewRoom(int16_t effect_num, int16_t room_num); -void Effect_Draw(int16_t effect_num); diff --git a/src/tr1/game/game/game.c b/src/tr1/game/game/game.c index 5ad09fd93..b5ddafb1e 100644 --- a/src/tr1/game/game/game.c +++ b/src/tr1/game/game/game.c @@ -5,7 +5,6 @@ #include "game/effects.h" #include "game/game_flow.h" #include "game/input.h" -#include "game/interpolation.h" #include "game/inventory.h" #include "game/item_actions.h" #include "game/lara/cheat.h" @@ -23,6 +22,7 @@ #include #include +#include #include #define FRAME_BUFFER(key) \ diff --git a/src/tr1/game/game/game_draw.c b/src/tr1/game/game/game_draw.c index f2825347a..842d453c8 100644 --- a/src/tr1/game/game/game_draw.c +++ b/src/tr1/game/game/game_draw.c @@ -1,7 +1,6 @@ #include "game/game.h" #include "game/camera.h" -#include "game/interpolation.h" #include "game/lara/draw.h" #include "game/lara/hair.h" #include "game/output.h" @@ -12,6 +11,7 @@ #include "global/vars.h" #include +#include #include diff --git a/src/tr1/game/interpolation.c b/src/tr1/game/interpolation.c deleted file mode 100644 index f27be00bc..000000000 --- a/src/tr1/game/interpolation.c +++ /dev/null @@ -1,210 +0,0 @@ -#include "game/interpolation.h" - -#include "game/camera.h" -#include "game/effects.h" -#include "game/items.h" -#include "game/lara/hair.h" -#include "game/room.h" -#include "global/const.h" -#include "global/vars.h" - -#include -#include -#include - -#include - -#define REMEMBER(target, member) (target)->interp.prev.member = (target)->member - -#define COMMIT(target, member) (target)->interp.result.member = (target)->member - -#define INTERPOLATE_F(target, member, ratio) \ - (target)->interp.result.member = ((target)->interp.prev.member) \ - + (((target)->member - ((target)->interp.prev.member)) * (ratio)); - -#define INTERPOLATE(target, member, ratio, max_diff) \ - if (ABS(((target)->member) - ((target)->interp.prev.member)) \ - >= (max_diff)) { \ - COMMIT((target), member); \ - } else { \ - INTERPOLATE_F(target, member, ratio); \ - } - -#define INTERPOLATE_ROT(target, member, ratio, max_diff) \ - if (!Math_AngleInCone( \ - (target)->member, (target)->interp.prev.member, (max_diff))) { \ - COMMIT((target), member); \ - } else { \ - INTERPOLATE_ROT_F(target, member, ratio); \ - } - -#define INTERPOLATE_ROT_F(target, member, ratio) \ - (target)->interp.result.member = Math_AngleMean( \ - (target)->interp.prev.member, (target)->member, (ratio)) - -void Interpolation_Commit(void) -{ - const double ratio = Interpolation_GetRate(); - - if (g_Camera.pos.room_num != NO_ROOM) { - INTERPOLATE(&g_Camera, shift, ratio, 128); - INTERPOLATE(&g_Camera, pos.x, ratio, 512); - INTERPOLATE(&g_Camera, pos.y, ratio, 512); - INTERPOLATE(&g_Camera, pos.z, ratio, 512); - INTERPOLATE(&g_Camera, target.x, ratio, 512); - INTERPOLATE(&g_Camera, target.y, ratio, 512); - INTERPOLATE(&g_Camera, target.z, ratio, 512); - - g_Camera.interp.room_num = g_Camera.pos.room_num; - Room_GetSector( - g_Camera.interp.result.pos.x, - g_Camera.interp.result.pos.y + g_Camera.interp.result.shift, - g_Camera.interp.result.pos.z, &g_Camera.interp.room_num); - } - - INTERPOLATE_ROT(&g_Lara.left_arm, rot.x, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara.left_arm, rot.y, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara.left_arm, rot.z, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara.right_arm, rot.x, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara.right_arm, rot.y, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara.right_arm, rot.z, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara, torso_rot.x, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara, torso_rot.y, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara, torso_rot.z, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara, head_rot.x, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara, head_rot.y, ratio, DEG_45); - INTERPOLATE_ROT(&g_Lara, head_rot.z, ratio, DEG_45); - - for (int i = 0; i < Item_GetTotalCount(); i++) { - ITEM *const item = Item_Get(i); - if ((item->flags & IF_KILLED) || item->status == IS_INACTIVE - || item->object_id == O_BAT) { - COMMIT(item, pos.x); - COMMIT(item, pos.y); - COMMIT(item, pos.z); - COMMIT(item, rot.x); - COMMIT(item, rot.y); - COMMIT(item, rot.z); - continue; - } - - const int32_t max_xz = item->object_id == O_DART ? 200 : 128; - const int32_t max_y = MAX(128, item->fall_speed * 2); - INTERPOLATE(item, pos.x, ratio, max_xz); - INTERPOLATE(item, pos.y, ratio, max_y); - INTERPOLATE(item, pos.z, ratio, max_xz); - INTERPOLATE_ROT(item, rot.x, ratio, DEG_45); - INTERPOLATE_ROT(item, rot.y, ratio, DEG_45); - INTERPOLATE_ROT(item, rot.z, ratio, DEG_45); - } - - if (g_LaraItem) { - INTERPOLATE(g_LaraItem, pos.x, ratio, 128); - INTERPOLATE( - g_LaraItem, pos.y, ratio, MAX(128, g_LaraItem->fall_speed * 2)); - INTERPOLATE(g_LaraItem, pos.z, ratio, 128); - INTERPOLATE_ROT(g_LaraItem, rot.x, ratio, DEG_45); - INTERPOLATE_ROT(g_LaraItem, rot.y, ratio, DEG_45); - INTERPOLATE_ROT(g_LaraItem, rot.z, ratio, DEG_45); - } - - int16_t effect_num = Effect_GetActiveNum(); - while (effect_num != NO_EFFECT) { - EFFECT *const effect = Effect_Get(effect_num); - INTERPOLATE(effect, pos.x, ratio, 128); - INTERPOLATE(effect, pos.y, ratio, MAX(128, effect->fall_speed * 2)); - INTERPOLATE(effect, pos.z, ratio, 128); - INTERPOLATE_ROT(effect, rot.x, ratio, DEG_45); - INTERPOLATE_ROT(effect, rot.y, ratio, DEG_45); - INTERPOLATE_ROT(effect, rot.z, ratio, DEG_45); - effect_num = effect->next_active; - } - - if (Lara_Hair_IsActive()) { - for (int i = 0; i < Lara_Hair_GetSegmentCount(); i++) { - HAIR_SEGMENT *const hair = Lara_Hair_GetSegment(i); - INTERPOLATE(hair, pos.x, ratio, 128); - INTERPOLATE( - hair, pos.y, ratio, MAX(128, g_LaraItem->fall_speed * 2)); - INTERPOLATE(hair, pos.z, ratio, 128); - INTERPOLATE_ROT(hair, rot.x, ratio, DEG_45); - INTERPOLATE_ROT(hair, rot.y, ratio, DEG_45); - INTERPOLATE_ROT(hair, rot.z, ratio, DEG_45); - } - } -} - -void Interpolation_Remember(void) -{ - if (g_Camera.pos.room_num != NO_ROOM) { - REMEMBER(&g_Camera, shift); - REMEMBER(&g_Camera, pos.x); - REMEMBER(&g_Camera, pos.y); - REMEMBER(&g_Camera, pos.z); - REMEMBER(&g_Camera, target.x); - REMEMBER(&g_Camera, target.y); - REMEMBER(&g_Camera, target.z); - } - - REMEMBER(&g_Lara.left_arm, rot.x); - REMEMBER(&g_Lara.left_arm, rot.y); - REMEMBER(&g_Lara.left_arm, rot.z); - REMEMBER(&g_Lara.right_arm, rot.x); - REMEMBER(&g_Lara.right_arm, rot.y); - REMEMBER(&g_Lara.right_arm, rot.z); - REMEMBER(&g_Lara, torso_rot.x); - REMEMBER(&g_Lara, torso_rot.y); - REMEMBER(&g_Lara, torso_rot.z); - REMEMBER(&g_Lara, head_rot.x); - REMEMBER(&g_Lara, head_rot.y); - REMEMBER(&g_Lara, head_rot.z); - - for (int i = 0; i < Item_GetTotalCount(); i++) { - ITEM *const item = Item_Get(i); - REMEMBER(item, pos.x); - REMEMBER(item, pos.y); - REMEMBER(item, pos.z); - REMEMBER(item, rot.x); - REMEMBER(item, rot.y); - REMEMBER(item, rot.z); - } - - if (g_LaraItem) { - REMEMBER(g_LaraItem, pos.x); - REMEMBER(g_LaraItem, pos.y); - REMEMBER(g_LaraItem, pos.z); - REMEMBER(g_LaraItem, rot.x); - REMEMBER(g_LaraItem, rot.y); - REMEMBER(g_LaraItem, rot.z); - } - - int16_t effect_num = Effect_GetActiveNum(); - while (effect_num != NO_EFFECT) { - EFFECT *const effect = Effect_Get(effect_num); - REMEMBER(effect, pos.x); - REMEMBER(effect, pos.y); - REMEMBER(effect, pos.z); - REMEMBER(effect, rot.x); - REMEMBER(effect, rot.y); - REMEMBER(effect, rot.z); - effect_num = effect->next_active; - } - - if (Lara_Hair_IsActive()) { - for (int i = 0; i < Lara_Hair_GetSegmentCount(); i++) { - HAIR_SEGMENT *const hair = Lara_Hair_GetSegment(i); - REMEMBER(hair, pos.x); - REMEMBER(hair, pos.y); - REMEMBER(hair, pos.z); - REMEMBER(hair, rot.x); - REMEMBER(hair, rot.y); - REMEMBER(hair, rot.z); - } - } -} - -void Interpolation_RememberItem(ITEM *item) -{ - item->interp.prev.pos = item->pos; - item->interp.prev.rot = item->rot; -} diff --git a/src/tr1/game/interpolation.h b/src/tr1/game/interpolation.h deleted file mode 100644 index 53d4c014c..000000000 --- a/src/tr1/game/interpolation.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "global/types.h" - -#include - -void Interpolation_Commit(void); -void Interpolation_Remember(void); -void Interpolation_RememberItem(ITEM *item); diff --git a/src/tr1/game/inventory_ring/control.c b/src/tr1/game/inventory_ring/control.c index eb571b3e8..9209f636e 100644 --- a/src/tr1/game/inventory_ring/control.c +++ b/src/tr1/game/inventory_ring/control.c @@ -6,7 +6,6 @@ #include "game/game_flow.h" #include "game/game_string.h" #include "game/input.h" -#include "game/interpolation.h" #include "game/inventory.h" #include "game/inventory_ring/vars.h" #include "game/lara/common.h" @@ -24,6 +23,7 @@ #include "global/vars.h" #include +#include #include #include diff --git a/src/tr1/game/inventory_ring/draw.c b/src/tr1/game/inventory_ring/draw.c index 1dd37e4e6..21add3a34 100644 --- a/src/tr1/game/inventory_ring/draw.c +++ b/src/tr1/game/inventory_ring/draw.c @@ -2,7 +2,6 @@ #include "game/game.h" #include "game/input.h" -#include "game/interpolation.h" #include "game/inventory_ring.h" #include "game/objects/common.h" #include "game/option.h" @@ -15,6 +14,7 @@ #include "global/vars.h" #include +#include #include #include diff --git a/src/tr1/game/items.c b/src/tr1/game/items.c index 8d45ca071..87ef811fa 100644 --- a/src/tr1/game/items.c +++ b/src/tr1/game/items.c @@ -2,7 +2,6 @@ #include "game/carrier.h" #include "game/effects.h" -#include "game/interpolation.h" #include "game/random.h" #include "game/room.h" #include "game/shell.h" @@ -12,6 +11,7 @@ #include "global/vars.h" #include +#include #include #include #include diff --git a/src/tr1/game/lara/hair.h b/src/tr1/game/lara/hair.h index 95c5ead59..acde13f67 100644 --- a/src/tr1/game/lara/hair.h +++ b/src/tr1/game/lara/hair.h @@ -2,24 +2,7 @@ #include "global/types.h" -#include +#include -typedef struct { - XYZ_32 pos; - XYZ_16 rot; - struct { - struct { - XYZ_32 pos; - XYZ_16 rot; - } result, prev; - } interp; -} HAIR_SEGMENT; - -bool Lara_Hair_IsActive(void); -void Lara_Hair_Initialise(void); void Lara_Hair_SetLaraType(GAME_OBJECT_ID lara_type); void Lara_Hair_Control(void); -void Lara_Hair_Draw(void); - -int32_t Lara_Hair_GetSegmentCount(void); -HAIR_SEGMENT *Lara_Hair_GetSegment(int32_t n); diff --git a/src/tr1/game/objects/traps/lightning_emitter.c b/src/tr1/game/objects/traps/lightning_emitter.c index 0731756e3..ce1e8a086 100644 --- a/src/tr1/game/objects/traps/lightning_emitter.c +++ b/src/tr1/game/objects/traps/lightning_emitter.c @@ -175,8 +175,8 @@ static void M_Draw(const ITEM *const item) Item_GetFrames(item, frmptr, &rate); Matrix_Push(); - Matrix_TranslateAbs32(item->pos); - Matrix_Rot16(item->rot); + Matrix_TranslateAbs32(item->interp.result.pos); + Matrix_Rot16(item->interp.result.rot); int32_t clip = Output_GetObjectBounds(&frmptr[0]->bounds); if (!clip) { diff --git a/src/tr1/game/room.h b/src/tr1/game/room.h index ab41fb4d7..d49ff8518 100644 --- a/src/tr1/game/room.h +++ b/src/tr1/game/room.h @@ -12,7 +12,6 @@ int32_t Room_FindGridShift(int32_t src, int32_t dst); void Room_GetNewRoom(int32_t x, int32_t y, int32_t z, int16_t room_num); void Room_GetNearByRooms( int32_t x, int32_t y, int32_t z, int32_t r, int32_t h, int16_t room_num); -SECTOR *Room_GetSector(int32_t x, int32_t y, int32_t z, int16_t *room_num); int16_t Room_GetWaterHeight(int32_t x, int32_t y, int32_t z, int16_t room_num); void Room_TestTriggers(const ITEM *item); diff --git a/src/tr1/meson.build b/src/tr1/meson.build index c998e97e2..fd64085ec 100644 --- a/src/tr1/meson.build +++ b/src/tr1/meson.build @@ -124,7 +124,6 @@ sources = [ 'game/gun/gun_rifle.c', 'game/inject.c', 'game/input.c', - 'game/interpolation.c', 'game/inventory.c', 'game/inventory_ring/control.c', 'game/inventory_ring/draw.c', diff --git a/src/tr2/decomp/flares.c b/src/tr2/decomp/flares.c index 27ad0a632..42cb861c5 100644 --- a/src/tr2/decomp/flares.c +++ b/src/tr2/decomp/flares.c @@ -139,8 +139,8 @@ void Flare_DrawInAir(const ITEM *const item) ANIM_FRAME *frames[2]; Item_GetFrames(item, frames, &rate); Matrix_Push(); - Matrix_TranslateAbs32(item->pos); - Matrix_Rot16(item->rot); + Matrix_TranslateAbs32(item->interp.result.pos); + Matrix_Rot16(item->interp.result.rot); const int32_t clip = Output_GetObjectBounds(&frames[0]->bounds); if (clip != 0) { Output_CalculateObjectLighting(item, &frames[0]->bounds); diff --git a/src/tr2/decomp/skidoo.c b/src/tr2/decomp/skidoo.c index 5b511397f..d4ccbfa7b 100644 --- a/src/tr2/decomp/skidoo.c +++ b/src/tr2/decomp/skidoo.c @@ -959,8 +959,8 @@ void Skidoo_Draw(const ITEM *const item) const int32_t frac = Item_GetFrames(item, frames, &rate); Matrix_Push(); - Matrix_TranslateAbs32(item->pos); - Matrix_Rot16(item->rot); + Matrix_TranslateAbs32(item->interp.result.pos); + Matrix_Rot16(item->interp.result.rot); const int32_t clip = Output_GetObjectBounds(&frames[0]->bounds); if (!clip) { diff --git a/src/tr2/game/camera.c b/src/tr2/game/camera.c index a54a2555b..03cdf70d1 100644 --- a/src/tr2/game/camera.c +++ b/src/tr2/game/camera.c @@ -989,6 +989,9 @@ void Camera_Apply(void) { M_EnsureEnvironment(); Matrix_LookAt( - g_Camera.pos.x, g_Camera.pos.y + g_Camera.shift, g_Camera.pos.z, - g_Camera.target.x, g_Camera.target.y, g_Camera.target.z, g_Camera.roll); + g_Camera.interp.result.pos.x, + g_Camera.interp.result.pos.y + g_Camera.interp.result.shift, + g_Camera.interp.result.pos.z, g_Camera.interp.result.target.x, + g_Camera.interp.result.target.y, g_Camera.interp.result.target.z, + g_Camera.roll); } diff --git a/src/tr2/game/clock.c b/src/tr2/game/clock.c index 8e3defe36..a7e6bc50c 100644 --- a/src/tr2/game/clock.c +++ b/src/tr2/game/clock.c @@ -4,5 +4,5 @@ int32_t Clock_GetCurrentFPS(void) { - return LOGIC_FPS; + return g_Config.rendering.fps; } diff --git a/src/tr2/game/collide.c b/src/tr2/game/collide.c index 243f81259..f160bf1fa 100644 --- a/src/tr2/game/collide.c +++ b/src/tr2/game/collide.c @@ -503,10 +503,10 @@ int32_t Collide_GetSpheres( pos.y = 0; pos.z = 0; Matrix_Push(); - Matrix_TranslateAbs32(item->pos); + Matrix_TranslateAbs32(item->interp.result.pos); } - Matrix_Rot16(item->rot); + Matrix_Rot16(item->interp.result.rot); const ANIM_FRAME *const frame = Item_GetBestFrame(item); Matrix_TranslateRel16(frame->offset); diff --git a/src/tr2/game/console/cmd/easy_config.c b/src/tr2/game/console/cmd/easy_config.c index c350382d4..448421fc6 100644 --- a/src/tr2/game/console/cmd/easy_config.c +++ b/src/tr2/game/console/cmd/easy_config.c @@ -11,6 +11,7 @@ typedef struct { static COMMAND_TO_OPTION_MAP m_CommandToOptionMap[] = { { "cheats", &g_Config.gameplay.enable_cheats }, { "wireframe", &g_Config.rendering.enable_wireframe }, + { "fps", &g_Config.rendering.fps }, { nullptr, nullptr }, }; @@ -31,4 +32,4 @@ static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) return CR_FAILURE; } -REGISTER_CONSOLE_COMMAND("cheats|wireframe", M_Entrypoint) +REGISTER_CONSOLE_COMMAND("cheats|wireframe|fps", M_Entrypoint) diff --git a/src/tr2/game/cutscene.c b/src/tr2/game/cutscene.c index 0c758d39f..9040bd043 100644 --- a/src/tr2/game/cutscene.c +++ b/src/tr2/game/cutscene.c @@ -17,6 +17,7 @@ #include #include +#include #include static CAMERA_INFO m_LocalCamera = {}; @@ -58,6 +59,7 @@ void Cutscene_End(void) GF_COMMAND Cutscene_Control(void) { + Interpolation_Remember(); M_FixAudioDrift(); Input_Update(); @@ -96,8 +98,9 @@ GF_COMMAND Cutscene_Control(void) void Cutscene_Draw(void) { g_CameraUnderwater = false; + Interpolation_Commit(); Camera_Apply(); - Room_DrawAllRooms(g_Camera.pos.room_num); + Room_DrawAllRooms(g_Camera.interp.room_num); Output_DrawPolyList(); } diff --git a/src/tr2/game/demo.c b/src/tr2/game/demo.c index 347770951..3a1b47391 100644 --- a/src/tr2/game/demo.c +++ b/src/tr2/game/demo.c @@ -21,6 +21,7 @@ #include #include +#include #include typedef struct { @@ -126,6 +127,7 @@ bool Demo_Start(const int32_t level_num) ASSERT(GF_GetCurrentLevel() == p->level); M_PrepareConfig(p); + Interpolation_Remember(); const uint32_t *const data = Demo_GetData(); if (data == nullptr) { diff --git a/src/tr2/game/effects.c b/src/tr2/game/effects.c index e89d8f624..b5a275e6d 100644 --- a/src/tr2/game/effects.c +++ b/src/tr2/game/effects.c @@ -161,8 +161,9 @@ void Effect_Draw(const int16_t effect_num) if (effect->object_id == O_GLOW) { Output_DrawSprite( - (effect->rot.y << 16) | (uint16_t)effect->rot.x, effect->pos.x, - effect->pos.y, effect->pos.z, Object_Get(O_GLOW)->mesh_idx, + (effect->rot.y << 16) | (uint16_t)effect->rot.x, + effect->interp.result.pos.x, effect->interp.result.pos.y, + effect->interp.result.pos.z, Object_Get(O_GLOW)->mesh_idx, effect->shade, effect->frame_num); return; } @@ -171,15 +172,16 @@ void Effect_Draw(const int16_t effect_num) Output_DrawSprite( SPRITE_ABS | (obj->semi_transparent ? SPRITE_SEMI_TRANS : 0) | SPRITE_SHADE, - effect->pos.x, effect->pos.y, effect->pos.z, - obj->mesh_idx - effect->frame_num, effect->shade, 0); + effect->interp.result.pos.x, effect->interp.result.pos.y, + effect->interp.result.pos.z, obj->mesh_idx - effect->frame_num, + effect->shade, 0); return; } Matrix_Push(); - Matrix_TranslateAbs32(effect->pos); + Matrix_TranslateAbs32(effect->interp.result.pos); if (g_MatrixPtr->_23 > g_PhdNearZ && g_MatrixPtr->_23 < g_PhdFarZ) { - Matrix_Rot16(effect->rot); + Matrix_Rot16(effect->interp.result.rot); if (obj->mesh_count) { Output_CalculateStaticLight(effect->shade); Object_DrawMesh(obj->mesh_idx, -1, false); diff --git a/src/tr2/game/effects.h b/src/tr2/game/effects.h index ec9faf524..a4d673b6a 100644 --- a/src/tr2/game/effects.h +++ b/src/tr2/game/effects.h @@ -1,13 +1,3 @@ #pragma once #include - -void Effect_InitialiseArray(void); -void Effect_Control(void); -EFFECT *Effect_Get(int16_t effect_num); -int16_t Effect_GetNum(const EFFECT *effect); -int16_t Effect_GetActiveNum(void); -int16_t Effect_Create(int16_t room_num); -void Effect_Kill(int16_t effect_num); -void Effect_NewRoom(int16_t effect_num, int16_t room_num); -void Effect_Draw(int16_t effect_num); diff --git a/src/tr2/game/game.c b/src/tr2/game/game.c index 8115f0ccd..e37f9dceb 100644 --- a/src/tr2/game/game.c +++ b/src/tr2/game/game.c @@ -22,6 +22,7 @@ #include "global/vars.h" #include +#include bool Game_Start(const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) { @@ -29,6 +30,7 @@ bool Game_Start(const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) g_OverlayStatus = 1; Camera_Initialise(); + Interpolation_Remember(); Stats_StartTimer(); return true; } @@ -52,6 +54,7 @@ void Game_Resume(void) GF_COMMAND Game_Control(const bool demo_mode) { + Interpolation_Remember(); if (g_GameFlow.cheat_keys) { Lara_Cheat_CheckKeys(); } @@ -154,8 +157,9 @@ GF_COMMAND Game_Control(const bool demo_mode) void Game_Draw(bool draw_overlay) { + Interpolation_Commit(); Camera_Apply(); - Room_DrawAllRooms(g_Camera.pos.room_num); + Room_DrawAllRooms(g_Camera.interp.room_num); Output_DrawPolyList(); if (draw_overlay) { Overlay_DrawGameInfo(); diff --git a/src/tr2/game/inventory_ring/control.c b/src/tr2/game/inventory_ring/control.c index 5968641c2..666e9f629 100644 --- a/src/tr2/game/inventory_ring/control.c +++ b/src/tr2/game/inventory_ring/control.c @@ -21,6 +21,7 @@ #include "global/vars.h" #include +#include #include #include #include @@ -745,6 +746,7 @@ static GF_COMMAND M_Control(INV_RING *const ring) } Sound_EndScene(); + Interpolation_Remember(); return (GF_COMMAND) { .action = GF_NOOP }; } @@ -844,7 +846,7 @@ INV_RING *InvRing_Open(const INVENTORY_MODE mode) } g_Inv_Mode = mode; - + Interpolation_Remember(); if (!Game_IsInGym()) { Stats_StartTimer(); } diff --git a/src/tr2/game/inventory_ring/draw.c b/src/tr2/game/inventory_ring/draw.c index 864e90343..dd606ca13 100644 --- a/src/tr2/game/inventory_ring/draw.c +++ b/src/tr2/game/inventory_ring/draw.c @@ -9,6 +9,7 @@ #include "game/overlay.h" #include "global/vars.h" +#include #include #include @@ -111,6 +112,10 @@ void InvRing_Draw(INV_RING *const ring) ring->camera.pos.z = ring->radius + 598; + if (ring->mode == INV_TITLE_MODE) { + Interpolation_Commit(); + } + XYZ_32 view_pos; XYZ_16 view_rot; InvRing_GetView(ring, &view_pos, &view_rot); diff --git a/src/tr2/game/items.c b/src/tr2/game/items.c index 24ece3354..ea0337aa9 100644 --- a/src/tr2/game/items.c +++ b/src/tr2/game/items.c @@ -9,7 +9,9 @@ #include "global/const.h" #include "global/vars.h" +#include #include +#include #include #include #include @@ -329,6 +331,7 @@ int32_t Item_GetFrames(const ITEM *item, ANIM_FRAME *frmptr[], int32_t *rate) { const ANIM *const anim = Item_GetAnim(item); const int32_t cur_frame_num = item->frame_num - anim->frame_base; + const int32_t last_frame_num = anim->frame_end - anim->frame_base; const int32_t key_frame_span = anim->interpolation; const int32_t key_frame_shift = cur_frame_num % key_frame_span; const int32_t first_key_frame_num = cur_frame_num / key_frame_span; @@ -347,8 +350,31 @@ int32_t Item_GetFrames(const ITEM *item, ANIM_FRAME *frmptr[], int32_t *rate) frmptr[0] = &anim->frame_ptr[first_key_frame_num]; frmptr[1] = &anim->frame_ptr[second_key_frame_num]; - *rate = denominator; - return numerator; + + // OG + if (g_Config.rendering.fps == 30) { + *rate = denominator; + return numerator; + } + + // interpolated + if (item != g_LaraItem && !item->active) { + *rate = denominator; + return numerator; + } + + const double clock_ratio = Interpolation_GetRate() - 0.5; + const double final = + (key_frame_shift + clock_ratio) / (double)key_frame_span; + const double interp_frame_num = + (first_key_frame_num * key_frame_span) + (final * key_frame_span); + if (interp_frame_num >= last_frame_num) { + *rate = denominator; + return numerator; + } + + *rate = 10; + return final * 10; } BOUNDS_16 *Item_GetBoundsAccurate(const ITEM *const item) diff --git a/src/tr2/game/lara/draw.c b/src/tr2/game/lara/draw.c index db64d534a..60cf7ce56 100644 --- a/src/tr2/game/lara/draw.c +++ b/src/tr2/game/lara/draw.c @@ -66,8 +66,8 @@ void Lara_Draw(const ITEM *const item) saved_matrix = *g_MatrixPtr; Matrix_Push(); - Matrix_TranslateAbs32(item->pos); - Matrix_Rot16(item->rot); + Matrix_TranslateAbs32(item->interp.result.pos); + Matrix_Rot16(item->interp.result.rot); const MATRIX item_matrix = *g_MatrixPtr; const int32_t clip = Output_GetObjectBounds(&frame->bounds); if (!clip) { @@ -105,7 +105,7 @@ void Lara_Draw(const ITEM *const item) } Matrix_Rot16(mesh_rots[LM_TORSO]); - Matrix_Rot16(g_Lara.torso_rot); + Matrix_Rot16(g_Lara.interp.result.torso_rot); Output_DrawObjectMesh(g_Lara.mesh_ptrs[LM_TORSO], clip); Matrix_Push(); @@ -113,7 +113,7 @@ void Lara_Draw(const ITEM *const item) mesh_rots_c = mesh_rots; Matrix_Rot16(mesh_rots[LM_HEAD]); mesh_rots = mesh_rots_c; - Matrix_Rot16(g_Lara.head_rot); + Matrix_Rot16(g_Lara.interp.result.head_rot); Output_DrawObjectMesh(g_Lara.mesh_ptrs[LM_HEAD], clip); *g_MatrixPtr = saved_matrix; @@ -184,7 +184,7 @@ void Lara_Draw(const ITEM *const item) g_MatrixPtr->_20 = item_matrix._20; g_MatrixPtr->_21 = item_matrix._21; g_MatrixPtr->_22 = item_matrix._22; - Matrix_Rot16(g_Lara.right_arm.rot); + Matrix_Rot16(g_Lara.right_arm.interp.result.rot); const ANIM *anim = Anim_GetAnim(g_Lara.right_arm.anim_num); mesh_rots = g_Lara.right_arm @@ -212,7 +212,7 @@ void Lara_Draw(const ITEM *const item) g_MatrixPtr->_20 = item_matrix._20; g_MatrixPtr->_21 = item_matrix._21; g_MatrixPtr->_22 = item_matrix._22; - Matrix_Rot16(g_Lara.left_arm.rot); + Matrix_Rot16(g_Lara.left_arm.interp.result.rot); anim = Anim_GetAnim(g_Lara.left_arm.anim_num); mesh_rots = g_Lara.left_arm @@ -297,8 +297,8 @@ void Lara_Draw_I( MATRIX saved_matrix = *g_MatrixPtr; Matrix_Push(); - Matrix_TranslateAbs32(item->pos); - Matrix_Rot16(item->rot); + Matrix_TranslateAbs32(item->interp.result.pos); + Matrix_Rot16(item->interp.result.rot); const int32_t clip = Output_GetObjectBounds(&frame1->bounds); @@ -341,7 +341,7 @@ void Lara_Draw_I( } Matrix_Rot16_ID(mesh_rots_1[LM_TORSO], mesh_rots_2[LM_TORSO]); - Matrix_Rot16_I(g_Lara.torso_rot); + Matrix_Rot16_I(g_Lara.interp.result.torso_rot); Output_DrawObjectMesh_I(g_Lara.mesh_ptrs[LM_TORSO], clip); Matrix_Push_I(); @@ -351,7 +351,7 @@ void Lara_Draw_I( Matrix_Rot16_ID(mesh_rots_1[LM_HEAD], mesh_rots_2[LM_HEAD]); mesh_rots_1 = mesh_rots_1_c; mesh_rots_2 = mesh_rots_2_c; - Matrix_Rot16_I(g_Lara.head_rot); + Matrix_Rot16_I(g_Lara.interp.result.head_rot); Output_DrawObjectMesh_I(g_Lara.mesh_ptrs[LM_HEAD], clip); *g_MatrixPtr = saved_matrix; @@ -418,7 +418,7 @@ void Lara_Draw_I( Matrix_Push_I(); Matrix_TranslateRel32_I(bone[7].pos); Matrix_InterpolateArm(); - Matrix_Rot16(g_Lara.right_arm.rot); + Matrix_Rot16(g_Lara.right_arm.interp.result.rot); const ANIM *anim = Anim_GetAnim(g_Lara.right_arm.anim_num); mesh_rots_1 = g_Lara.right_arm @@ -438,7 +438,7 @@ void Lara_Draw_I( Matrix_Push_I(); Matrix_TranslateRel32_I(bone[10].pos); Matrix_InterpolateArm(); - Matrix_Rot16(g_Lara.left_arm.rot); + Matrix_Rot16(g_Lara.left_arm.interp.result.rot); anim = Anim_GetAnim(g_Lara.left_arm.anim_num); mesh_rots_1 = g_Lara.left_arm diff --git a/src/tr2/game/lara/hair.c b/src/tr2/game/lara/hair.c index b1acace40..e4085e7b1 100644 --- a/src/tr2/game/lara/hair.c +++ b/src/tr2/game/lara/hair.c @@ -8,17 +8,13 @@ #include "global/vars.h" #include +#include #include #include #include #define HAIR_SEGMENTS 6 -typedef struct { - XYZ_32 pos; - XYZ_16 rot; -} HAIR_SEGMENT; - static bool m_IsFirstHair; static SPHERE m_HairSpheres[HAIR_SEGMENTS - 1]; static XYZ_32 m_HairVelocity[HAIR_SEGMENTS + 1]; @@ -53,7 +49,7 @@ static void M_CalculateSpheres(const ANIM_FRAME *const frame) } Matrix_Rot16(mesh_rots[LM_TORSO]); - Matrix_Rot16(g_Lara.torso_rot); + Matrix_Rot16(g_Lara.interp.result.torso_rot); Matrix_Push(); mesh = g_Lara.mesh_ptrs[LM_TORSO]; Matrix_TranslateRel16(mesh->center); @@ -88,7 +84,7 @@ static void M_CalculateSpheres(const ANIM_FRAME *const frame) Matrix_TranslateRel32(bone[LM_HEAD - 1].pos); Matrix_Rot16(mesh_rots[LM_HEAD]); - Matrix_Rot16(g_Lara.head_rot); + Matrix_Rot16(g_Lara.interp.result.head_rot); Matrix_Push(); mesh = g_Lara.mesh_ptrs[LM_HEAD]; @@ -131,7 +127,7 @@ static void M_CalculateSpheres_I( } Matrix_Rot16_ID(mesh_rots_1[LM_TORSO], mesh_rots_2[LM_TORSO]); - Matrix_Rot16_I(g_Lara.torso_rot); + Matrix_Rot16_I(g_Lara.interp.result.torso_rot); Matrix_Push_I(); mesh = g_Lara.mesh_ptrs[LM_TORSO]; @@ -171,7 +167,7 @@ static void M_CalculateSpheres_I( Matrix_TranslateRel32_I(bone[LM_HEAD - 1].pos); Matrix_Rot16_ID(mesh_rots_1[LM_HEAD], mesh_rots_2[LM_HEAD]); - Matrix_Rot16_I(g_Lara.head_rot); + Matrix_Rot16_I(g_Lara.interp.result.head_rot); Matrix_Push_I(); mesh = g_Lara.mesh_ptrs[LM_HEAD]; @@ -393,10 +389,25 @@ void Lara_Hair_Draw(void) for (int32_t i = 0; i < HAIR_SEGMENTS; i++) { const HAIR_SEGMENT *const s = &m_HairSegments[i]; Matrix_Push(); - Matrix_TranslateAbs32(s->pos); - Matrix_RotY(s->rot.y); - Matrix_RotX(s->rot.x); + Matrix_TranslateAbs32(s->interp.result.pos); + Matrix_RotY(s->interp.result.rot.y); + Matrix_RotX(s->interp.result.rot.x); Object_DrawMesh(obj->mesh_idx + i, 1, false); Matrix_Pop(); } } + +bool Lara_Hair_IsActive(void) +{ + return Object_Get(O_LARA_HAIR)->loaded && Object_Get(O_LARA)->loaded; +} + +int32_t Lara_Hair_GetSegmentCount(void) +{ + return HAIR_SEGMENTS; +} + +HAIR_SEGMENT *Lara_Hair_GetSegment(const int32_t n) +{ + return &m_HairSegments[n]; +} diff --git a/src/tr2/game/lara/hair.h b/src/tr2/game/lara/hair.h index 56b4cdded..bab0c210f 100644 --- a/src/tr2/game/lara/hair.h +++ b/src/tr2/game/lara/hair.h @@ -1,5 +1,5 @@ #pragma once -void Lara_Hair_Initialise(void); +#include + void Lara_Hair_Control(bool in_cutscene); -void Lara_Hair_Draw(void); diff --git a/src/tr2/game/level.c b/src/tr2/game/level.c index 428f62379..0317cfc6e 100644 --- a/src/tr2/game/level.c +++ b/src/tr2/game/level.c @@ -386,4 +386,6 @@ void Level_Unload(void) if (Output_GetBackgroundType() == BK_OBJECT) { Output_UnloadBackground(); } + + Camera_Reset(); } diff --git a/src/tr2/game/objects/common.c b/src/tr2/game/objects/common.c index b7173a8b3..dedf66541 100644 --- a/src/tr2/game/objects/common.c +++ b/src/tr2/game/objects/common.c @@ -27,8 +27,8 @@ void Object_DrawAnimatingItem(const ITEM *item) } Matrix_Push(); - Matrix_TranslateAbs32(item->pos); - Matrix_Rot16(item->rot); + Matrix_TranslateAbs32(item->interp.result.pos); + Matrix_Rot16(item->interp.result.rot); const int32_t clip = Output_GetObjectBounds(&frames[0]->bounds); if (!clip) { @@ -134,14 +134,15 @@ void Object_DrawUnclippedItem(const ITEM *const item) void Object_DrawSpriteItem(const ITEM *const item) { Output_CalculateStaticMeshLight( - item->pos, item->shade, Room_Get(item->room_num)); + item->interp.result.pos, item->shade, Room_Get(item->room_num)); const OBJECT *const obj = Object_Get(item->object_id); Output_DrawSprite( SPRITE_ABS | (obj->semi_transparent ? SPRITE_SEMI_TRANS : 0) | SPRITE_SHADE, - item->pos.x, item->pos.y, item->pos.z, obj->mesh_idx - item->frame_num, + item->interp.result.pos.x, item->interp.result.pos.y, + item->interp.result.pos.z, obj->mesh_idx - item->frame_num, Output_GetLightAdder() + 4096, 0); } diff --git a/src/tr2/game/objects/creatures/xian_common.c b/src/tr2/game/objects/creatures/xian_common.c index 37291cf7b..969fe5d94 100644 --- a/src/tr2/game/objects/creatures/xian_common.c +++ b/src/tr2/game/objects/creatures/xian_common.c @@ -20,8 +20,8 @@ void XianWarrior_Draw(const ITEM *item) } Matrix_Push(); - Matrix_TranslateAbs32(item->pos); - Matrix_Rot16(item->rot); + Matrix_TranslateAbs32(item->interp.result.pos); + Matrix_Rot16(item->interp.result.rot); const int32_t clip = Output_GetObjectBounds(&frames[0]->bounds); if (clip == 0) { diff --git a/src/tr2/game/objects/general/sphere_of_doom.c b/src/tr2/game/objects/general/sphere_of_doom.c index f62ba2def..a606231c4 100644 --- a/src/tr2/game/objects/general/sphere_of_doom.c +++ b/src/tr2/game/objects/general/sphere_of_doom.c @@ -100,8 +100,8 @@ static void M_Control(const int16_t item_num) static void M_Draw(const ITEM *const item) { Matrix_Push(); - Matrix_TranslateAbs32(item->pos); - Matrix_RotY(item->rot.y); + Matrix_TranslateAbs32(item->interp.result.pos); + Matrix_RotY(item->interp.result.rot.y); MATRIX *const mptr = g_MatrixPtr; mptr->_00 = (mptr->_00 * item->timer) >> 8; diff --git a/src/tr2/game/output.c b/src/tr2/game/output.c index 2ffda99a5..6fc348625 100644 --- a/src/tr2/game/output.c +++ b/src/tr2/game/output.c @@ -811,7 +811,8 @@ void Output_InsertShadow( g_FltWinCenterY = (float)(g_PhdWinCenterY); Matrix_Push(); - Matrix_TranslateAbs(item->pos.x, item->floor, item->pos.z); + Matrix_TranslateAbs( + item->interp.result.pos.x, item->floor, item->interp.result.pos.z); Matrix_RotY(item->rot.y); if (M_CalcObjectVertices(shadow_info.vertex, shadow_info.vertex_count)) { Render_InsertTransOctagon(g_PhdVBuf, 24); diff --git a/src/tr2/game/room.h b/src/tr2/game/room.h index 460ec9732..9187540d5 100644 --- a/src/tr2/game/room.h +++ b/src/tr2/game/room.h @@ -15,8 +15,6 @@ int16_t Room_GetTiltType(const SECTOR *sector, int32_t x, int32_t y, int32_t z); // TODO: poor abstraction void Room_InitCinematic(void); -SECTOR *Room_GetSector(int32_t x, int32_t y, int32_t z, int16_t *room_num); - int32_t Room_GetWaterHeight(int32_t x, int32_t y, int32_t z, int16_t room_num); void Room_TestTriggers(const ITEM *item); diff --git a/tools/tr2/config/TR2X_ConfigTool/Resources/Lang/en.json b/tools/tr2/config/TR2X_ConfigTool/Resources/Lang/en.json index 3a5f4d4fa..52d0b1f19 100644 --- a/tools/tr2/config/TR2X_ConfigTool/Resources/Lang/en.json +++ b/tools/tr2/config/TR2X_ConfigTool/Resources/Lang/en.json @@ -22,6 +22,10 @@ "Title": "Enable more responsive passport", "Description": "Disables blocking user input when passport flips pages, scheduling the page flips instead." }, + "fps": { + "Title": "FPS", + "Description": "Controls the framerate at which to render the game." + }, "enable_3d_pickups": { "Title": "3D pickups", "Description": "Enables 3D models to be rendered in place of the sprites for pickup items." diff --git a/tools/tr2/config/TR2X_ConfigTool/Resources/specification.json b/tools/tr2/config/TR2X_ConfigTool/Resources/specification.json index 6cf418ec9..2be8e96a6 100644 --- a/tools/tr2/config/TR2X_ConfigTool/Resources/specification.json +++ b/tools/tr2/config/TR2X_ConfigTool/Resources/specification.json @@ -4,6 +4,10 @@ "jpg", "png" ], + "fps": [ + "30", + "60" + ], "aspect_mode": [ "any", "16:9", @@ -131,6 +135,12 @@ "DataType": "Bool", "DefaultValue": true }, + { + "Field": "fps", + "DataType": "Enum", + "EnumKey": "fps", + "DefaultValue": "60" + }, { "Field": "aspect_mode", "DataType": "Enum",