tr2: add 60 fps

This commit is contained in:
Marcin Kurczewski 2025-02-22 17:42:01 +01:00
parent 9b7577eef1
commit 8a0c695b02
60 changed files with 436 additions and 343 deletions

View file

@ -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+

View file

@ -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)

View file

@ -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 `_`.

View file

@ -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

View file

@ -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)

View file

@ -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);

View file

@ -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;
}

View file

@ -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 <stdint.h>
#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;
}

View file

@ -92,6 +92,7 @@ typedef struct {
} window;
struct {
int32_t fps;
RENDER_MODE render_mode;
ASPECT_MODE aspect_mode;
bool enable_zbuffer;

View file

@ -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);

View file

@ -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 {

View file

@ -1,4 +1,5 @@
#pragma once
#include "effects/common.h"
#include "effects/const.h"
#include "effects/types.h"

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -5,7 +5,6 @@
#include <stdint.h>
void Camera_Initialise(void);
void Camera_Reset(void);
void Camera_ResetPosition(void);
void Camera_UpdateCutscene(void);
void Camera_RefreshFromTrigger(const TRIGGER *trigger);

View file

@ -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 <libtrx/debug.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/memory.h>
static void M_InitialiseLara(const GF_LEVEL *level);

View file

@ -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 <libtrx/config.h>
#include <libtrx/debug.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/log.h>
#define MODIFY_CONFIG() \

View file

@ -1,13 +1,3 @@
#pragma once
#include <libtrx/game/effects.h>
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);

View file

@ -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 <libtrx/config.h>
#include <libtrx/debug.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/game/ui/common.h>
#define FRAME_BUFFER(key) \

View file

@ -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 <libtrx/config.h>
#include <libtrx/game/interpolation.h>
#include <stdint.h>

View file

@ -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 <libtrx/config.h>
#include <libtrx/game/math.h>
#include <libtrx/utils.h>
#include <stdint.h>
#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;
}

View file

@ -1,9 +0,0 @@
#pragma once
#include "global/types.h"
#include <libtrx/game/interpolation.h>
void Interpolation_Commit(void);
void Interpolation_Remember(void);
void Interpolation_RememberItem(ITEM *item);

View file

@ -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 <libtrx/config.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/game/inventory_ring/priv.h>
#include <libtrx/memory.h>

View file

@ -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 <libtrx/config.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/game/inventory_ring/priv.h>
#include <libtrx/game/matrix.h>

View file

@ -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 <libtrx/config.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/game/math.h>
#include <libtrx/game/matrix.h>
#include <libtrx/utils.h>

View file

@ -2,24 +2,7 @@
#include "global/types.h"
#include <stdint.h>
#include <libtrx/game/lara/hair.h>
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);

View file

@ -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) {

View file

@ -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);

View file

@ -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',

View file

@ -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);

View file

@ -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) {

View file

@ -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);
}

View file

@ -4,5 +4,5 @@
int32_t Clock_GetCurrentFPS(void)
{
return LOGIC_FPS;
return g_Config.rendering.fps;
}

View file

@ -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);

View file

@ -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)

View file

@ -17,6 +17,7 @@
#include <libtrx/config.h>
#include <libtrx/debug.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/utils.h>
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();
}

View file

@ -21,6 +21,7 @@
#include <libtrx/config.h>
#include <libtrx/debug.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/log.h>
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) {

View file

@ -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);

View file

@ -1,13 +1,3 @@
#pragma once
#include <libtrx/game/effects.h>
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);

View file

@ -22,6 +22,7 @@
#include "global/vars.h"
#include <libtrx/config.h>
#include <libtrx/game/interpolation.h>
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();

View file

@ -21,6 +21,7 @@
#include "global/vars.h"
#include <libtrx/config.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/game/inventory_ring/priv.h>
#include <libtrx/game/matrix.h>
#include <libtrx/game/objects/names.h>
@ -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();
}

View file

@ -9,6 +9,7 @@
#include "game/overlay.h"
#include "global/vars.h"
#include <libtrx/game/interpolation.h>
#include <libtrx/game/inventory_ring/priv.h>
#include <libtrx/game/matrix.h>
@ -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);

View file

@ -9,7 +9,9 @@
#include "global/const.h"
#include "global/vars.h"
#include <libtrx/config.h>
#include <libtrx/debug.h>
#include <libtrx/game/interpolation.h>
#include <libtrx/game/math.h>
#include <libtrx/game/matrix.h>
#include <libtrx/utils.h>
@ -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,10 +350,33 @@ 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];
// 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)
{
int32_t rate;

View file

@ -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

View file

@ -8,17 +8,13 @@
#include "global/vars.h"
#include <libtrx/game/lara/common.h>
#include <libtrx/game/lara/hair.h>
#include <libtrx/game/math.h>
#include <libtrx/game/matrix.h>
#include <libtrx/utils.h>
#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];
}

View file

@ -1,5 +1,5 @@
#pragma once
void Lara_Hair_Initialise(void);
#include <libtrx/game/lara/hair.h>
void Lara_Hair_Control(bool in_cutscene);
void Lara_Hair_Draw(void);

View file

@ -386,4 +386,6 @@ void Level_Unload(void)
if (Output_GetBackgroundType() == BK_OBJECT) {
Output_UnloadBackground();
}
Camera_Reset();
}

View file

@ -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);
}

View file

@ -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) {

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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."

View file

@ -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",