tr2/viewport: allow players to change fov

Resolves #2177.
This commit is contained in:
Marcin Kurczewski 2024-12-31 01:03:41 +01:00
parent 97f4b782ca
commit 3e2d54c3c5
19 changed files with 85 additions and 80 deletions

View file

@ -376,7 +376,6 @@ Not all options are turned on by default. Refer to `TR1X_ConfigTool.exe` for det
- improved support for windowed mode
#### Gameplay
- added ability to set user-defined FOV
- added ability to select weapons / using items with numeric keys
- added ability to look around while running
- added ability to forward and backward jump while looking
@ -501,6 +500,7 @@ Not all options are turned on by default. Refer to `TR1X_ConfigTool.exe` for det
- added unobtainable pickups, kills, and secrets stats support in the gameflow
#### Visuals
- added ability to set user-defined FOV
- added optional shotgun flash sprites
- added optional rendering of pickups on the ground as 3D meshes
- added optional rendering of pickups in the UI as 3D meshes

View file

@ -1,5 +1,6 @@
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-0.7.1...develop) - ××××-××-××
- completed decompilation efforts  TR2X.dll is gone, Tomb2.exe no longer needed (#1694)
- added the ability to set user-defined FOV (no UI for it yet) (#2177)
- added the ability to turn FMVs off (#2110)
- added an option to use PS1 contrast levels, available under F8 (#1646)
- added an option to use TR3+ side steps (#2111)

View file

@ -86,6 +86,7 @@ game with new enhancements and features.
- fixed enemies that are run over by the skidoo not being counted in the statistics
#### Visuals
- added ability to set user-defined FOV
- added support for HD FMVs
- added wireframe mode
- added an option for 1-2-3-4× pixel upscaling

View file

@ -147,6 +147,7 @@ void Config_Sanitize(void)
CLAMP(g_Config.rendering.nearest_adjustment, 0, 256);
CLAMP(g_Config.rendering.linear_adjustment, 0, 256);
CLAMP(g_Config.visuals.fov, 30, 150);
CLAMP(g_Config.ui.bar_scale, 0.5, 2.0);
CLAMP(g_Config.ui.text_scale, 0.5, 2.0);
}

View file

@ -20,6 +20,8 @@ typedef struct {
bool enable_3d_pickups;
bool enable_fade_effects;
bool fix_item_rots;
int32_t fov;
bool use_pcx_fov;
} visuals;
struct {

View file

@ -16,6 +16,8 @@ CFG_INT32(g_Config, gameplay.turbo_speed, 0)
CFG_BOOL(g_Config, visuals.enable_3d_pickups, true)
CFG_BOOL(g_Config, visuals.enable_fade_effects, true)
CFG_BOOL(g_Config, visuals.fix_item_rots, true)
CFG_INT32(g_Config, visuals.fov, 80)
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_ENUM(g_Config, rendering.screenshot_format, SCREENSHOT_FORMAT_JPEG, SCREENSHOT_FORMAT)

View file

@ -10,6 +10,7 @@
#include "game/random.h"
#include "game/room.h"
#include "game/sound.h"
#include "game/viewport.h"
#include "global/const.h"
#include "global/vars.h"
@ -85,7 +86,7 @@ void Camera_Initialise(void)
{
g_Camera.last = NO_CAMERA;
Camera_ResetPosition();
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
Viewport_AlterFOV(-1);
Camera_Update();
}
@ -871,7 +872,7 @@ void Camera_LoadCutsceneFrame(void)
g_Camera.pos.y = g_CinePos.pos.y + cy;
g_Camera.pos.z = g_CinePos.pos.z + ((cz * c - cx * s) >> W2V_SHIFT);
Output_AlterFOV(fov);
Viewport_AlterFOV(fov);
Matrix_LookAt(
g_Camera.pos.x, g_Camera.pos.y, g_Camera.pos.z, g_Camera.target.x,
g_Camera.target.y, g_Camera.target.z, roll);
@ -924,7 +925,7 @@ void Camera_UpdateCutscene(void)
if (room_num != NO_ROOM_NEG) {
g_Camera.pos.room_num = room_num;
}
Output_AlterFOV(fov);
Viewport_AlterFOV(fov);
Matrix_LookAt(
campos.x, campos.y, campos.z, camtar.x, camtar.y, camtar.z, roll);
}

View file

@ -19,6 +19,7 @@
#include "game/shell.h"
#include "game/sound.h"
#include "game/stats.h"
#include "game/viewport.h"
#include "global/vars.h"
#include <libtrx/game/objects/names.h>
@ -177,6 +178,7 @@ static void M_End(INV_RING *const ring)
if (ring->mode == INV_TITLE_MODE) {
Music_Stop();
}
Viewport_AlterFOV(ring->old_fov);
Memory_Free(ring);
Output_UnloadBackground();
}
@ -459,29 +461,13 @@ INV_RING *InvRing_Open(const INVENTORY_MODE mode)
g_PhdWinBottom = g_PhdWinMaxY;
g_Inv_Chosen = NO_OBJECT;
if (g_CurrentLevel != LV_GYM) {
Stats_StartTimer();
}
if (mode == INV_TITLE_MODE) {
Output_LoadBackgroundFromFile("data/title.pcx");
} else {
Output_LoadBackgroundFromObject();
}
Overlay_HideGameInfo();
Output_AlterFOV(80 * PHD_DEGREE);
Sound_StopAllSamples();
if (mode != INV_TITLE_MODE) {
Music_Pause();
}
M_Construct(mode);
INV_RING *const ring = Memory_Alloc(sizeof(INV_RING));
ring->mode = mode;
ring->is_pass_open = false;
ring->is_demo_needed = false;
ring->old_fov = Viewport_GetFOV();
m_NoInputCounter = 0;
switch (mode) {
@ -513,6 +499,24 @@ INV_RING *InvRing_Open(const INVENTORY_MODE mode)
}
g_Inv_Mode = mode;
if (g_CurrentLevel != LV_GYM) {
Stats_StartTimer();
}
if (mode == INV_TITLE_MODE) {
Output_LoadBackgroundFromFile("data/title.pcx");
} else {
Output_LoadBackgroundFromObject();
}
Overlay_HideGameInfo();
Sound_StopAllSamples();
if (mode != INV_TITLE_MODE) {
Music_Pause();
}
Viewport_AlterFOV(80 * PHD_DEGREE);
return ring;
}

View file

@ -108,4 +108,5 @@ typedef struct {
bool is_demo_needed;
bool is_pass_open;
bool has_spun_out;
int32_t old_fov;
} INV_RING;

View file

@ -4,12 +4,12 @@
#include "game/collide.h"
#include "game/lara/hair.h"
#include "game/music.h"
#include "game/output.h"
#include "game/random.h"
#include "game/room.h"
#include "game/sound.h"
#include "game/spawn.h"
#include "game/stats.h"
#include "game/viewport.h"
#include "global/vars.h"
#include <libtrx/utils.h>
@ -143,7 +143,7 @@ void M_LaraNormal(ITEM *const item)
item->anim_num = LA_STAND_STILL;
item->frame_num = g_Anims[item->anim_num].frame_base;
g_Camera.type = CAM_CHASE;
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
Viewport_AlterFOV(-1);
}
void M_Boiler(ITEM *const item)

View file

@ -12,10 +12,10 @@
#include "game/math.h"
#include "game/objects/common.h"
#include "game/objects/vars.h"
#include "game/output.h"
#include "game/room.h"
#include "game/sound.h"
#include "game/spawn.h"
#include "game/viewport.h"
#include "global/utils.h"
#include "global/vars.h"
@ -155,7 +155,7 @@ bool Lara_Cheat_EnterFlyMode(void)
M_ReinitialiseGunMeshes();
g_Camera.type = CAM_CHASE;
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
Viewport_AlterFOV(-1);
Console_Log(GS(OSD_FLY_MODE_ON));
return true;
@ -422,7 +422,7 @@ bool Lara_Cheat_Teleport(int32_t x, int32_t y, int32_t z)
g_Lara.mesh_effects = 0;
g_Camera.type = CAM_CHASE;
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
Viewport_AlterFOV(-1);
Camera_ResetPosition();
return true;

View file

@ -7,9 +7,9 @@
#include "game/lara/look.h"
#include "game/lara/misc.h"
#include "game/music.h"
#include "game/output.h"
#include "game/room.h"
#include "game/sound.h"
#include "game/viewport.h"
#include "global/const.h"
#include "global/vars.h"
@ -704,7 +704,7 @@ void Lara_State_Extra_Breath(ITEM *item, COLL_INFO *coll)
if (g_Camera.type != CAM_HEAVY) {
g_Camera.type = CAM_CHASE;
}
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
Viewport_AlterFOV(-1);
}
void Lara_State_Extra_YetiKill(ITEM *item, COLL_INFO *coll)
@ -803,7 +803,7 @@ void Lara_State_Extra_StartHouse(ITEM *item, COLL_INFO *coll)
Inv_AddItem(O_PUZZLE_ITEM_1);
} else if (item->frame_num == g_Anims[item->anim_num].frame_end) {
g_Camera.type = CAM_CHASE;
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
Viewport_AlterFOV(-1);
}
}

View file

@ -11,7 +11,6 @@
#include "game/render/priv.h"
#include "game/scaler.h"
#include "game/shell.h"
#include "game/viewport.h"
#include "global/vars.h"
#include <libtrx/log.h>
@ -454,11 +453,6 @@ void Output_RotateLight(int16_t pitch, int16_t yaw)
g_LsVectorView.z = (m->_20 * x + m->_21 * y + m->_22 * z) >> W2V_SHIFT;
}
void Output_AlterFOV(int16_t fov)
{
Viewport_AlterFOV(fov);
}
const int16_t *Output_InsertRoomSprite(
const int16_t *obj_ptr, const int32_t vtx_count)
{

View file

@ -24,7 +24,6 @@ const int16_t *Output_CalcVerticeLight(const int16_t *obj_ptr);
const int16_t *Output_CalcRoomVertices(
const int16_t *obj_ptr, int32_t far_clip);
void Output_RotateLight(int16_t pitch, int16_t yaw);
void Output_AlterFOV(int16_t fov);
const int16_t *Output_InsertRoomSprite(
const int16_t *obj_ptr, int32_t vtx_count);

View file

@ -381,6 +381,9 @@ static void M_DrawPickup3D(const DISPLAY_PICKUP *const pickup)
const VIEWPORT old_vp = *Viewport_Get();
Viewport_AlterFOV(80 * PHD_DEGREE);
VIEWPORT new_vp = *Viewport_Get();
BOUNDS_16 bounds = frame->bounds;
if (frame->bounds.min_x == frame->bounds.max_x
&& frame->bounds.min_y == frame->bounds.max_y) {
@ -389,12 +392,12 @@ static void M_DrawPickup3D(const DISPLAY_PICKUP *const pickup)
}
const int32_t scale = 1280;
const int32_t padding_right = MIN(old_vp.width, old_vp.height) / 10;
const int32_t padding_right = MIN(new_vp.width, new_vp.height) / 10;
const int32_t padding_bottom = padding_right;
// Try to fit in a quarter of the screen
const int32_t available_width = old_vp.width * 0.4 - padding_right;
const int32_t available_height = old_vp.height / 2 - padding_bottom;
const int32_t available_width = new_vp.width * 0.4 - padding_right;
const int32_t available_height = new_vp.height / 2 - padding_bottom;
// maintain aspect ratio
const int32_t cell_width = available_width / MAX_PICKUP_COLUMNS;
@ -403,15 +406,14 @@ static void M_DrawPickup3D(const DISPLAY_PICKUP *const pickup)
const int32_t vp_width = cell_width;
const int32_t vp_height = cell_height;
const int32_t vp_src_x = old_vp.width + offscreen_offset;
const int32_t vp_dst_x = old_vp.width - (cell_width / 2 + padding_right)
const int32_t vp_src_x = new_vp.width + offscreen_offset;
const int32_t vp_dst_x = new_vp.width - (cell_width / 2 + padding_right)
- pickup->grid_x * cell_width;
const int32_t vp_src_y = old_vp.height - (cell_height / 2 + padding_bottom);
const int32_t vp_src_y = new_vp.height - (cell_height / 2 + padding_bottom);
const int32_t vp_dst_y = vp_src_y - pickup->grid_y * cell_height;
const int32_t vp_x = vp_src_x + (vp_dst_x - vp_src_x) * ease;
const int32_t vp_y = vp_src_y + (vp_dst_y - vp_src_y) * ease;
VIEWPORT new_vp = old_vp;
new_vp.game_vars.win_center_x = vp_x;
new_vp.game_vars.win_center_y = vp_y;
Viewport_Restore(&new_vp);

View file

@ -268,31 +268,34 @@ static void M_HandleConfigChange(const EVENT *const event, void *const data)
{
const CONFIG *const old = &g_Config;
const CONFIG *const new = &g_SavedConfig;
if (old->window.is_fullscreen != new->window.is_fullscreen
|| old->window.is_maximized != new->window.is_maximized
|| old->window.x != new->window.x || old->window.y != new->window.y
|| old->window.width != new->window.width
|| old->window.height != new->window.height
|| old->rendering.scaler != new->rendering.scaler
|| old->rendering.sizer != new->rendering.sizer
|| old->rendering.aspect_mode != new->rendering.aspect_mode) {
#define CHANGED(subject) (old->subject != new->subject)
if (CHANGED(window.is_fullscreen) || CHANGED(window.is_maximized)
|| CHANGED(window.x) || CHANGED(window.y) || CHANGED(window.width)
|| CHANGED(window.height) || CHANGED(rendering.scaler)
|| CHANGED(rendering.sizer) || CHANGED(rendering.aspect_mode)) {
LOG_DEBUG("Change in settings detected");
M_SyncToWindow();
M_RefreshRendererViewport();
}
if (old->rendering.render_mode != new->rendering.render_mode) {
if (CHANGED(rendering.render_mode)) {
Render_Reset(RENDER_RESET_ALL);
} else if (
old->rendering.enable_zbuffer != new->rendering.enable_zbuffer
|| old->rendering.enable_perspective_filter
!= new->rendering.enable_perspective_filter
|| old->rendering.enable_wireframe != new->rendering.enable_wireframe
|| old->rendering.texture_filter != new->rendering.texture_filter
|| old->rendering.lighting_contrast
!= new->rendering.lighting_contrast) {
CHANGED(rendering.enable_zbuffer)
|| CHANGED(rendering.enable_perspective_filter)
|| CHANGED(rendering.enable_wireframe)
|| CHANGED(rendering.texture_filter)
|| CHANGED(rendering.lighting_contrast)) {
Render_Reset(RENDER_RESET_PARAMS);
}
if (CHANGED(visuals.fov) || CHANGED(visuals.use_pcx_fov)) {
if (Viewport_GetFOV() == -1) {
Viewport_AlterFOV(-1);
}
}
}
static void M_DisplayLegal(void)
@ -334,7 +337,7 @@ void Shell_Main(void)
Output_CalculateWibbleTable();
Shell_Start();
Viewport_AlterFOV(GAME_FOV * PHD_DEGREE);
Viewport_AlterFOV(-1);
Viewport_Reset();
if (!GF_LoadScriptFile("data\\tombPC.dat")) {

View file

@ -49,8 +49,11 @@ static void M_ApplyGameVars(const VIEWPORT *vp);
static void M_AlterFov(VIEWPORT *const vp)
{
const int32_t view_angle = vp->view_angle;
const int32_t fov_width = vp->game_vars.win_height * 320 / 240;
const int32_t view_angle = vp->view_angle <= 0
? g_Config.visuals.fov * PHD_DEGREE
: vp->view_angle;
const int32_t fov_width = vp->game_vars.win_height * 320
/ (g_Config.visuals.use_pcx_fov ? 200 : 240);
vp->game_vars.persp =
fov_width / 2 * Math_Cos(view_angle / 2) / Math_Sin(view_angle / 2);
@ -151,8 +154,9 @@ void Viewport_Reset(void)
vp->near_z = VIEW_NEAR;
vp->far_z = VIEW_FAR;
// Do not mess with the FOV upon plain reset requests.
// vp->view_angle = GAME_FOV * PHD_DEGREE;
// We do not update vp->view_angle on purpose, as it's managed by the game
// rather than the window manager. (Think cutscenes, special cameras, etc.)
switch (g_Config.rendering.render_mode) {
case RM_SOFTWARE:
@ -188,22 +192,15 @@ void Viewport_Restore(const VIEWPORT *ref_vp)
M_ApplyGameVars(&m_Viewport);
}
int16_t Viewport_GetFOV(void)
{
return m_Viewport.view_angle;
}
void Viewport_AlterFOV(const int16_t view_angle)
{
m_Viewport.view_angle = view_angle;
M_PullGameVars(&m_Viewport);
M_AlterFov(&m_Viewport);
M_ApplyGameVars(&m_Viewport);
}
int16_t Viewport_GetFOV(void)
{
return m_Viewport.view_angle == -1 ? Viewport_GetUserFOV()
: m_Viewport.view_angle;
}
int16_t Viewport_GetUserFOV(void)
{
return GAME_FOV * PHD_DEGREE;
}

View file

@ -52,9 +52,7 @@ void Viewport_Reset(void);
void Viewport_Restore(const VIEWPORT *ref_vp);
int16_t Viewport_GetFOV(void);
void Viewport_AlterFOV(int16_t view_angle);
const VIEWPORT *Viewport_Get(void);
int16_t Viewport_GetFOV(void);
int16_t Viewport_GetUserFOV(void);

View file

@ -30,7 +30,6 @@
#define LIGHT_MAP_SIZE 32
#define MAX_ROOM_LIGHT_UNIT (0x2000 / (WIBBLE_SIZE / 2))
#define GAME_FOV 80
#define MIN_SQUARE SQUARE(WALL_L / 3)
#define NO_BOX (-1)
#define NO_ITEM (-1)