tr1/output: add quadrilateral interpolation

Resolves #354
This commit is contained in:
Marcin Kurczewski 2025-03-09 22:10:27 +01:00
parent d91f0afeef
commit cf9c0c9557
23 changed files with 183 additions and 65 deletions

View file

@ -356,7 +356,6 @@
"DETAIL_FBO_FILTER": "FBO filter",
"DETAIL_FLOAT_FMT": "%.1f",
"DETAIL_FPS": "FPS",
"DETAIL_PERSPECTIVE": "Perspective",
"DETAIL_PRETTY_PIXELS": "Pretty pixels",
"DETAIL_REFLECTIONS": "Reflections",
"DETAIL_RENDER_MODE": "Render mode",
@ -367,6 +366,7 @@
"DETAIL_SELECT_DETAIL": "Select Detail",
"DETAIL_STRING_FMT": "%s",
"DETAIL_TEXTURE_FILTER": "Texture filter",
"DETAIL_TRAPEZOID_FILTER": "Trapezoid filter",
"DETAIL_UI_BAR_SCALE": "UI bar scale",
"DETAIL_UI_TEXT_SCALE": "UI text scale",
"DETAIL_VSYNC": "VSync",
@ -463,8 +463,6 @@
"OSD_LOAD_GAME_FAIL_INVALID_SLOT": "Invalid save slot %d",
"OSD_LOAD_GAME_FAIL_UNAVAILABLE_SLOT": "Save slot %d is not available",
"OSD_OBJECT_NOT_FOUND": "Object not found",
"OSD_PERSPECTIVE_FILTER_OFF": "Perspective correction: off",
"OSD_PERSPECTIVE_FILTER_ON": "Perspective correction: on",
"OSD_PHOTO_MODE_LAUNCHED": "Entering photo mode, press %s for help",
"OSD_PLAY_CUTSCENE": "Loading cutscene %d",
"OSD_PLAY_DEMO": "Loading demo %d",
@ -486,6 +484,8 @@
"OSD_TEXTURE_FILTER_BILINEAR": "bilinear",
"OSD_TEXTURE_FILTER_NN": "nearest-neighbor",
"OSD_TEXTURE_FILTER_SET": "Texture filter set to %s",
"OSD_TRAPEZOID_FILTER_OFF": "Trapezoid filter disabled",
"OSD_TRAPEZOID_FILTER_ON": "Trapezoid filter enabled",
"OSD_UI_OFF": "UI disabled",
"OSD_UI_ON": "UI enabled",
"OSD_UNKNOWN_COMMAND": "Unknown command: %s",

View file

@ -2,7 +2,7 @@
// Vertex shader
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec2 inTexCoords;
layout(location = 1) in vec4 inTexCoords;
layout(location = 2) in float inTexZ;
layout(location = 3) in vec4 inColor;
@ -11,11 +11,11 @@ uniform mat4 matModelView;
#ifdef OGL33C
out vec4 vertColor;
out vec2 vertTexCoords;
out vec4 vertTexCoords;
out float vertTexZ;
#else
varying vec4 vertColor;
varying vec2 vertTexCoords;
varying vec4 vertTexCoords;
varying float vertTexZ;
#endif
@ -23,6 +23,7 @@ void main(void) {
gl_Position = matProjection * matModelView * vec4(inPosition, 1);
vertColor = inColor / 255.0;
vertTexCoords = inTexCoords;
vertTexCoords.xy *= vertTexCoords.zw;
vertTexCoords *= inTexZ;
vertTexZ = inTexZ;
}
@ -44,7 +45,7 @@ uniform float brightnessMultiplier;
#define TEXELFETCH texelFetch
in vec4 vertColor;
in vec2 vertTexCoords;
in vec4 vertTexCoords;
in float vertTexZ;
out vec4 OUTCOLOR;
#else
@ -54,7 +55,7 @@ uniform float brightnessMultiplier;
#define TEXTURE texture2D
varying vec4 vertColor;
varying vec2 vertTexCoords;
varying vec4 vertTexCoords;
varying float vertTexZ;
#endif
@ -62,7 +63,7 @@ void main(void) {
OUTCOLOR = vertColor;
vec2 texCoords = vertTexCoords.xy;
texCoords.xy /= vertTexZ;
texCoords.xy /= vertTexCoords.zw;
if (texturingEnabled) {
#if defined(GL_EXT_gpu_shader4) || defined(OGL33C)

View file

@ -2,7 +2,7 @@
// Vertex shader
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec2 inTexCoords;
layout(location = 1) in vec4 inTexCoords;
layout(location = 2) in float inTexZ;
layout(location = 3) in vec4 inColor;
@ -11,11 +11,11 @@ uniform mat4 matModelView;
#ifdef OGL33C
out vec4 vertColor;
out vec2 vertTexCoords;
out vec4 vertTexCoords;
out float vertTexZ;
#else
varying vec4 vertColor;
varying vec2 vertTexCoords;
varying vec4 vertTexCoords;
varying float vertTexZ;
#endif
@ -23,6 +23,7 @@ void main(void) {
gl_Position = matProjection * matModelView * vec4(inPosition, 1);
vertColor = inColor / 255.0;
vertTexCoords = inTexCoords;
vertTexCoords.xy *= vertTexCoords.zw;
vertTexCoords *= inTexZ;
vertTexZ = inTexZ;
}
@ -44,7 +45,7 @@ uniform float brightnessMultiplier;
#define TEXELFETCH texelFetch
in vec4 vertColor;
in vec2 vertTexCoords;
in vec4 vertTexCoords;
in float vertTexZ;
out vec4 OUTCOLOR;
#else
@ -54,7 +55,7 @@ uniform float brightnessMultiplier;
#define TEXTURE texture2D
varying vec4 vertColor;
varying vec2 vertTexCoords;
varying vec4 vertTexCoords;
varying float vertTexZ;
#endif
@ -62,7 +63,7 @@ void main(void) {
OUTCOLOR = vertColor;
vec2 texCoords = vertTexCoords.xy;
texCoords.xy /= vertTexZ;
texCoords.xy /= vertTexCoords.zw;
if (texturingEnabled) {
#if defined(GL_EXT_gpu_shader4) || defined(OGL33C)

View file

@ -1,4 +1,5 @@
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr1-4.8.3...develop) - ××××-××-××
- added quadrilateral interpolation (#354)
- added support for `-l`/`--level` argument to play a single level
- added support for custom levels to use `disable_floor` in the gameflow, similar to TR2's Floating Islands (#2541)
- added drawing of object mesh spheres to the `/debug` console command
@ -12,6 +13,7 @@
- fixed Lara's meshes not resetting after using the fly cheat (#2565, #2572, regressions from 4.8)
- fixed guns appearing in Lara's hands if the draw input is held when unarmed and while picking up a gun item (#2577, regressions from 0.8/4.3)
- fixed being able to play with Lara invisible after using the explosion cheat then the fly cheat (#2584, regression from 4.8)
- removed perspective filter toggle (it had no effect; repurposed to trapezoid interpolation toggle)
## [4.8.3](https://github.com/LostArtefacts/TRX/compare/tr1-4.8.2...tr1-4.8.3) - 2025-02-17
- fixed some of Lara's speech in the gym not playing in response to player action (#2514, regression from 4.8)

View file

@ -103,7 +103,7 @@ CFG_BOOL(g_Config, rendering.enable_debug_portals, false)
CFG_BOOL(g_Config, rendering.enable_debug_spheres, false)
CFG_BOOL(g_Config, rendering.enable_wireframe, false)
CFG_DOUBLE(g_Config, rendering.wireframe_width, 2.5)
CFG_BOOL(g_Config, rendering.enable_perspective_filter, true)
CFG_BOOL(g_Config, rendering.enable_trapezoid_filter, true)
CFG_BOOL(g_Config, rendering.enable_vsync, true)
CFG_BOOL(g_Config, rendering.pretty_pixels, true)
CFG_BOOL(g_Config, visuals.enable_reflections, true)

View file

@ -42,7 +42,7 @@ static bool m_IsRoleHardcoded[INPUT_ROLE_NUMBER_OF] = {
#if TR_VERSION == 1
[INPUT_ROLE_UNBIND_KEY] = 1,
[INPUT_ROLE_RESET_BINDINGS] = 1,
[INPUT_ROLE_PERSPECTIVE] = 1,
[INPUT_ROLE_TRAPEZOID_FILTER] = 1,
#endif
[INPUT_ROLE_MENU_CONFIRM] = 1,
[INPUT_ROLE_MENU_BACK] = 1,
@ -264,7 +264,7 @@ bool Input_AssignFromJSONObject(
case 38: role = INPUT_ROLE_TOGGLE_PHOTO_MODE; break;
case 39: role = INPUT_ROLE_UNBIND_KEY; break;
case 40: role = INPUT_ROLE_RESET_BINDINGS; break;
case 42: role = INPUT_ROLE_PERSPECTIVE; break;
case 42: role = INPUT_ROLE_TRAPEZOID_FILTER; break;
case 43: role = INPUT_ROLE_MENU_CONFIRM; break;
case 44: role = INPUT_ROLE_MENU_BACK; break;
case 45: role = INPUT_ROLE_MENU_LEFT; break;

View file

@ -1,6 +1,8 @@
#include "game/math.h"
#include "utils.h"
#include <math.h>
uint32_t Math_Sqrt(uint32_t n)
{
uint32_t result = 0;
@ -128,3 +130,27 @@ bool XYZ_32_AreEquivalent(const XYZ_32 *const pos1, const XYZ_32 *const pos2)
{
return pos1->x == pos2->x && pos1->y == pos2->y && pos1->z == pos2->z;
}
float XYZ_F_DotProduct(const XYZ_F a, const XYZ_F b)
{
return a.x * b.x + a.y * b.y + a.z * b.z;
}
float XYZ_F_Length2(const XYZ_F pos)
{
return XYZ_F_DotProduct(pos, pos);
}
float XYZ_F_Length(const XYZ_F pos)
{
return sqrtf(XYZ_F_Length2(pos));
}
XYZ_F XYZ_F_Subtract(const XYZ_F a, const XYZ_F b)
{
return (XYZ_F) {
.x = a.x - b.x,
.y = a.y - b.y,
.z = a.z - b.z,
};
}

View file

@ -56,7 +56,7 @@ void GFX_3D_VertexStream_Init(GFX_3D_VERTEX_STREAM *const vertex_stream)
&vertex_stream->vtc_format, 0, 3, GL_FLOAT, GL_FALSE,
sizeof(GFX_3D_VERTEX), offsetof(GFX_3D_VERTEX, x));
GFX_GL_VertexArray_Attribute(
&vertex_stream->vtc_format, 1, 2, GL_FLOAT, GL_FALSE,
&vertex_stream->vtc_format, 1, 4, GL_FLOAT, GL_FALSE,
sizeof(GFX_3D_VERTEX), offsetof(GFX_3D_VERTEX, s));
GFX_GL_VertexArray_Attribute(
&vertex_stream->vtc_format, 2, 1, GL_FLOAT, GL_FALSE,

View file

@ -199,7 +199,7 @@ typedef struct {
int32_t resolution_height;
GFX_RENDER_MODE render_mode;
int32_t fps;
bool enable_perspective_filter;
bool enable_trapezoid_filter;
GFX_TEXTURE_FILTER texture_filter;
GFX_TEXTURE_FILTER fbo_filter;
bool enable_debug_triggers;

View file

@ -121,5 +121,3 @@ GS_DEFINE(PASSPORT_NEW_GAME, "New Game")
GS_DEFINE(PASSPORT_EXIT_GAME, "Exit Game")
GS_DEFINE(PASSPORT_EXIT_TO_TITLE, "Exit to Title")
GS_DEFINE(SOUND_SET_VOLUMES, "Set Volumes")
GS_DEFINE(OSD_PERSPECTIVE_FILTER_ON, "Perspective correction: on")
GS_DEFINE(OSD_PERSPECTIVE_FILTER_OFF, "Perspective correction: off")

View file

@ -41,7 +41,7 @@ INPUT_KEYBOARD_ASSIGN(INPUT_ROLE_USE_BIG_MEDI, SDL_SCANCODE_9)
INPUT_KEYBOARD_ASSIGN(INPUT_ROLE_TOGGLE_FULLSCREEN, SDL_SCANCODE_UNKNOWN)
INPUT_KEYBOARD_ASSIGN(INPUT_ROLE_FPS, SDL_SCANCODE_F2)
INPUT_KEYBOARD_ASSIGN(INPUT_ROLE_BILINEAR, SDL_SCANCODE_F3)
INPUT_KEYBOARD_ASSIGN(INPUT_ROLE_PERSPECTIVE, SDL_SCANCODE_F4)
INPUT_KEYBOARD_ASSIGN(INPUT_ROLE_TRAPEZOID_FILTER, SDL_SCANCODE_F4)
INPUT_KEYBOARD_ASSIGN(INPUT_ROLE_UNBIND_KEY, SDL_SCANCODE_BACKSPACE)
INPUT_KEYBOARD_ASSIGN(INPUT_ROLE_RESET_BINDINGS, SDL_SCANCODE_R)

View file

@ -53,4 +53,4 @@ INPUT_ROLE_DEFINE(CAMERA_DOWN, camera_down)
INPUT_ROLE_DEFINE(TOGGLE_PHOTO_MODE, toggle_photo_mode)
INPUT_ROLE_DEFINE(UNBIND_KEY, unbind_key)
INPUT_ROLE_DEFINE(RESET_BINDINGS, reset_bindings)
INPUT_ROLE_DEFINE(PERSPECTIVE, toggle_perspective_filter)
INPUT_ROLE_DEFINE(TRAPEZOID_FILTER, toggle_trapezoid_filter)

View file

@ -13,6 +13,12 @@ DIRECTION Math_GetDirection(int16_t angle);
DIRECTION Math_GetDirectionCone(int16_t angle, int16_t cone);
int16_t Math_DirectionToAngle(DIRECTION dir);
int32_t Math_AngleMean(int32_t angle1, int32_t angle2, double ratio);
int32_t XYZ_32_GetDistance(const XYZ_32 *pos1, const XYZ_32 *pos2);
int32_t XYZ_32_GetDistance0(const XYZ_32 *pos);
bool XYZ_32_AreEquivalent(const XYZ_32 *pos1, const XYZ_32 *pos2);
float XYZ_F_DotProduct(XYZ_F a, XYZ_F b);
float XYZ_F_Length2(XYZ_F pos);
float XYZ_F_Length(XYZ_F pos);
XYZ_F XYZ_F_Subtract(XYZ_F a, XYZ_F b);

View file

@ -26,6 +26,10 @@ typedef struct {
int16_t z;
} XYZ_16;
typedef struct {
float x, y, z;
} XYZ_F;
typedef enum {
DIR_UNKNOWN = -1,
DIR_NORTH = 0,

View file

@ -57,8 +57,8 @@ void Game_ProcessInput(void)
if (g_Config.input.enable_buffering && Game_IsPlaying()) {
if (g_Input.toggle_bilinear_filter) {
FRAME_BUFFER(toggle_bilinear_filter);
} else if (g_Input.toggle_perspective_filter) {
FRAME_BUFFER(toggle_perspective_filter);
} else if (g_Input.toggle_trapezoid_filter) {
FRAME_BUFFER(toggle_trapezoid_filter);
} else if (g_Input.toggle_fps_counter) {
FRAME_BUFFER(toggle_fps_counter);
}

View file

@ -9,7 +9,7 @@ GS_DEFINE(PASSPORT_MODE_NEW_GAME_JP, "Japanese NG")
GS_DEFINE(PASSPORT_MODE_NEW_GAME_JP_PLUS, "Japanese NG+")
GS_DEFINE(DETAIL_SELECT_DETAIL, "Select Detail")
GS_DEFINE(DETAIL_FPS, "FPS")
GS_DEFINE(DETAIL_PERSPECTIVE, "Perspective")
GS_DEFINE(DETAIL_TRAPEZOID_FILTER, "Trapezoid filter")
GS_DEFINE(DETAIL_PRETTY_PIXELS, "Pretty pixels")
GS_DEFINE(DETAIL_REFLECTIONS, "Reflections")
GS_DEFINE(DETAIL_BILINEAR, "Bilinear")
@ -56,8 +56,8 @@ GS_DEFINE(OSD_COMPLETE_LEVEL, "Level complete!")
GS_DEFINE(OSD_TEXTURE_FILTER_SET, "Texture filter set to %s")
GS_DEFINE(OSD_TEXTURE_FILTER_NN, "nearest-neighbor")
GS_DEFINE(OSD_TEXTURE_FILTER_BILINEAR, "bilinear")
GS_DEFINE(OSD_PERSPECTIVE_FILTER_ON, "Perspective filter enabled")
GS_DEFINE(OSD_PERSPECTIVE_FILTER_OFF, "Perspective filter disabled")
GS_DEFINE(OSD_TRAPEZOID_FILTER_ON, "Trapezoid filter enabled")
GS_DEFINE(OSD_TRAPEZOID_FILTER_OFF, "Trapezoid filter disabled")
GS_DEFINE(OSD_FPS_COUNTER_ON, "FPS counter enabled")
GS_DEFINE(OSD_FPS_COUNTER_OFF, "FPS counter disabled")
GS_DEFINE(OSD_DOOR_OPEN, "Open Sesame!")

View file

@ -69,7 +69,7 @@ static void M_UpdateFromBackend(
s->screenshot |= backend->is_pressed(layout, INPUT_ROLE_SCREENSHOT);
s->toggle_fps_counter |= backend->is_pressed(layout, INPUT_ROLE_FPS);
s->toggle_bilinear_filter |= backend->is_pressed(layout, INPUT_ROLE_BILINEAR);
s->toggle_perspective_filter |= backend->is_pressed(layout, INPUT_ROLE_PERSPECTIVE);
s->toggle_trapezoid_filter |= backend->is_pressed(layout, INPUT_ROLE_TRAPEZOID_FILTER);
// clang-format on
backend->custom_update(s, layout);

View file

@ -16,11 +16,11 @@
#define TOP_Y (-85)
#define BORDER 8
#define COL_OFFSET_0 (-142)
#define COL_OFFSET_1 10
#define COL_OFFSET_0 (-152)
#define COL_OFFSET_1 20
#define ROW_HEIGHT 17
#define ROW_WIDTH 300
#define OPTION_LENGTH 256
#define ROW_WIDTH 320
#define OPTION_LENGTH 276
#define LEFT_ARROW_OFFSET (-20)
#define RIGHT_ARROW_OFFSET_MIN 35
#define RIGHT_ARROW_OFFSET_MAX 85
@ -46,7 +46,7 @@ typedef enum {
OPTION_UI_BAR_SCALE,
OPTION_RENDER_MODE,
OPTION_RESOLUTION,
OPTION_PERSPECTIVE,
OPTION_TRAPEZOID_FILTER,
OPTION_PRETTY_PIXELS,
OPTION_REFLECTIONS,
OPTION_NUMBER_OF,
@ -85,7 +85,7 @@ static const GRAPHICS_OPTION_ROW m_GfxOptionRows[] = {
{ OPTION_RENDER_MODE, GS_ID(DETAIL_RENDER_MODE), GS_ID(DETAIL_STRING_FMT) },
{ OPTION_RESOLUTION, GS_ID(DETAIL_RESOLUTION),
GS_ID(DETAIL_RESOLUTION_FMT) },
{ OPTION_PERSPECTIVE, GS_ID(DETAIL_PERSPECTIVE), GS_ID(MISC_ON) },
{ OPTION_TRAPEZOID_FILTER, GS_ID(DETAIL_TRAPEZOID_FILTER), GS_ID(MISC_ON) },
{ OPTION_PRETTY_PIXELS, GS_ID(DETAIL_PRETTY_PIXELS), GS_ID(MISC_ON) },
{ OPTION_REFLECTIONS, GS_ID(DETAIL_REFLECTIONS), GS_ID(MISC_ON) },
// end
@ -210,8 +210,7 @@ static void M_InitText(void)
Text_SetPos(
m_Text[TEXT_LEFT_ARROW], m_GraphicsMenu.value_texts[0]->pos.x - 20,
m_GraphicsMenu.value_texts[0]->pos.y);
m_HideArrowLeft =
g_Config.rendering.enable_perspective_filter ? false : true;
m_HideArrowLeft = g_Config.rendering.enable_trapezoid_filter ? false : true;
m_Text[TEXT_RIGHT_ARROW] = Text_Create(0, 0, "\\{button right}");
Text_CentreV(m_Text[TEXT_RIGHT_ARROW], 1);
@ -219,7 +218,7 @@ static void M_InitText(void)
m_Text[TEXT_RIGHT_ARROW], m_GraphicsMenu.value_texts[0]->pos.x + 40,
m_GraphicsMenu.value_texts[0]->pos.y);
m_HideArrowRight =
g_Config.rendering.enable_perspective_filter ? true : false;
g_Config.rendering.enable_trapezoid_filter ? true : false;
m_Text[TEXT_UP_ARROW] = Text_Create(0, TOP_Y + BORDER * 2, "\\{arrow up}");
Text_SetScale(
@ -302,9 +301,9 @@ static void M_UpdateArrows(
m_HideArrowLeft = !Screen_CanSetPrevRes();
m_HideArrowRight = !Screen_CanSetNextRes();
break;
case OPTION_PERSPECTIVE:
m_HideArrowLeft = !g_Config.rendering.enable_perspective_filter;
m_HideArrowRight = g_Config.rendering.enable_perspective_filter;
case OPTION_TRAPEZOID_FILTER:
m_HideArrowLeft = !g_Config.rendering.enable_trapezoid_filter;
m_HideArrowRight = g_Config.rendering.enable_trapezoid_filter;
break;
case OPTION_PRETTY_PIXELS:
m_HideArrowLeft = !g_Config.rendering.pretty_pixels;
@ -447,8 +446,8 @@ static void M_ChangeTextOption(
Text_ChangeText(value_text, buf);
break;
case OPTION_PERSPECTIVE: {
bool is_enabled = g_Config.rendering.enable_perspective_filter;
case OPTION_TRAPEZOID_FILTER: {
bool is_enabled = g_Config.rendering.enable_trapezoid_filter;
Text_ChangeText(value_text, is_enabled ? GS(MISC_ON) : GS(MISC_OFF));
break;
}
@ -570,10 +569,10 @@ void Option_Graphics_Control(INVENTORY_ITEM *inv_item, const bool is_busy)
}
break;
case OPTION_PERSPECTIVE:
if (!g_Config.rendering.enable_perspective_filter) {
g_Config.rendering.enable_perspective_filter = true;
reset = OPTION_PERSPECTIVE;
case OPTION_TRAPEZOID_FILTER:
if (!g_Config.rendering.enable_trapezoid_filter) {
g_Config.rendering.enable_trapezoid_filter = true;
reset = OPTION_TRAPEZOID_FILTER;
}
break;
@ -669,10 +668,10 @@ void Option_Graphics_Control(INVENTORY_ITEM *inv_item, const bool is_busy)
}
break;
case OPTION_PERSPECTIVE:
if (g_Config.rendering.enable_perspective_filter) {
g_Config.rendering.enable_perspective_filter = false;
reset = OPTION_PERSPECTIVE;
case OPTION_TRAPEZOID_FILTER:
if (g_Config.rendering.enable_trapezoid_filter) {
g_Config.rendering.enable_trapezoid_filter = false;
reset = OPTION_TRAPEZOID_FILTER;
}
break;

View file

@ -77,6 +77,7 @@ static const char *m_ImageExtensions[] = {
};
static void M_DrawSphere(const XYZ_32 pos, const int32_t radius);
static void M_FixTrapezoidRatios(PHD_VBUF *const vbuf[4]);
static void M_DrawFlatFace3s(const FACE3 *faces, int32_t count);
static void M_DrawFlatFace4s(const FACE4 *faces, int32_t count);
static void M_DrawTexturedFace3s(const FACE3 *faces, int32_t count);
@ -154,6 +155,51 @@ static void M_DrawSphere(const XYZ_32 pos, const int32_t radius)
GFX_Context_SetWireframeMode(wireframe_state);
}
static void M_FixTrapezoidRatios(PHD_VBUF *const vbuf[4])
{
for (int32_t j = 0; j < 4; j++) {
vbuf[j]->trapezoid_ratios[0] = 1.0;
vbuf[j]->trapezoid_ratios[1] = 1.0;
}
XYZ_F c0, c1, c2, c3, *coords[4] = { &c0, &c1, &c2, &c3 };
for (int32_t i = 0; i < 4; i++) {
coords[i]->x = vbuf[i]->world_pos.x;
coords[i]->y = vbuf[i]->world_pos.y;
coords[i]->z = vbuf[i]->world_pos.z;
}
const XYZ_F a = XYZ_F_Subtract(c0, c1);
const XYZ_F b = XYZ_F_Subtract(c3, c2);
const XYZ_F c = XYZ_F_Subtract(c0, c3);
const XYZ_F d = XYZ_F_Subtract(c1, c2);
const float a_l = XYZ_F_Length(a);
const float b_l = XYZ_F_Length(b);
const float c_l = XYZ_F_Length(c);
const float d_l = XYZ_F_Length(d);
const float ab = XYZ_F_DotProduct(a, b) / (a_l * b_l);
const float cd = XYZ_F_DotProduct(c, d) / (c_l * d_l);
const float tx = ABS(vbuf[0]->u - vbuf[3]->u);
const float ty = ABS(vbuf[0]->v - vbuf[3]->v);
if (ab > cd) {
const int k = (tx > ty) ? 3 : 2;
if (a_l > b_l) {
vbuf[2]->tex_coord[k] = vbuf[3]->tex_coord[k] = (b_l / a_l);
} else if (a_l < b_l) {
vbuf[0]->tex_coord[k] = vbuf[1]->tex_coord[k] = (a_l / b_l);
}
} else if (ab < cd) {
const int k = (tx > ty) ? 2 : 3;
if (c_l > d_l) {
vbuf[1]->tex_coord[k] = vbuf[2]->tex_coord[k] = (d_l / c_l);
} else if (c_l < d_l) {
vbuf[0]->tex_coord[k] = vbuf[3]->tex_coord[k] = (c_l / d_l);
}
}
}
static void M_DrawFlatFace3s(const FACE3 *const faces, const int32_t count)
{
S_Output_DisableTextureMode();
@ -234,6 +280,9 @@ static void M_DrawTexturedFace4s(const FACE4 *const faces, const int32_t count)
vns[j]->v = tex->uv[j].v;
}
if (g_Config.rendering.enable_trapezoid_filter) {
M_FixTrapezoidRatios(vns);
}
S_Output_DrawTexturedQuad(
vns[0], vns[1], vns[2], vns[3], tex->tex_page, tex->draw_type);
}
@ -336,6 +385,9 @@ static uint16_t M_CalcVertex(PHD_VBUF *const vbuf, const XYZ_16 pos)
vbuf->xv = xv;
vbuf->yv = yv;
vbuf->zv = zv;
vbuf->tex_coord[2] = 1.0;
vbuf->tex_coord[3] = 1.0;
vbuf->world_pos = pos;
uint16_t clip_flags;
if (zv < Output_GetNearZ()) {

View file

@ -315,12 +315,12 @@ void Shell_ProcessInput(void)
Config_Write();
}
if (g_InputDB.toggle_perspective_filter) {
g_Config.rendering.enable_perspective_filter ^= true;
if (g_InputDB.toggle_trapezoid_filter) {
g_Config.rendering.enable_trapezoid_filter ^= true;
Console_Log(
g_Config.rendering.enable_perspective_filter
? GS(OSD_PERSPECTIVE_FILTER_ON)
: GS(OSD_PERSPECTIVE_FILTER_OFF));
g_Config.rendering.enable_trapezoid_filter
? GS(OSD_TRAPEZOID_FILTER_ON)
: GS(OSD_TRAPEZOID_FILTER_OFF));
Config_Write();
}

View file

@ -137,8 +137,13 @@ typedef struct {
float ys;
int16_t clip;
int16_t g;
int16_t u;
int16_t v;
union {
struct {
float u, v, trapezoid_ratios[2];
};
float tex_coord[4];
};
XYZ_16 world_pos;
} PHD_VBUF;
typedef struct {

View file

@ -178,6 +178,10 @@ static int32_t M_ZedClipper(
v->w = 1.0f / near_z;
v->s = M_GetUV(vn0->u) + (M_GetUV(vn1->u) - M_GetUV(vn0->u)) * clip;
v->t = M_GetUV(vn0->v) + (M_GetUV(vn1->v) - M_GetUV(vn0->v)) * clip;
v->tex_coord[2] = vn0->tex_coord[2]
+ (vn1->tex_coord[2] - vn0->tex_coord[2]) * clip;
v->tex_coord[3] = vn0->tex_coord[3]
+ (vn1->tex_coord[3] - vn0->tex_coord[3]) * clip;
v->r = v->g = v->b =
(8192.0f - (vn0->g + (vn1->g - vn0->g) * clip)) * multiplier;
@ -194,6 +198,8 @@ static int32_t M_ZedClipper(
v->w = 1.0f / vn0->zv;
v->s = M_GetUV(vn0->u);
v->t = M_GetUV(vn0->v);
v->tex_coord[2] = vn0->tex_coord[2];
v->tex_coord[3] = vn0->tex_coord[3];
v->r = v->g = v->b = (8192.0f - vn0->g) * multiplier;
Output_ApplyTint(&v->r, &v->g, &v->b);
@ -349,6 +355,8 @@ void S_Output_DrawSprite(
vertices[0].z = vz;
vertices[0].s = u0;
vertices[0].t = v0;
vertices[0].tex_coord[2] = 1.0;
vertices[0].tex_coord[3] = 1.0;
vertices[0].w = rhw;
vertices[0].r = vshade;
vertices[0].g = vshade;
@ -359,6 +367,8 @@ void S_Output_DrawSprite(
vertices[1].z = vz;
vertices[1].s = u1;
vertices[1].t = v0;
vertices[1].tex_coord[2] = 1.0;
vertices[1].tex_coord[3] = 1.0;
vertices[1].w = rhw;
vertices[1].r = vshade;
vertices[1].g = vshade;
@ -369,6 +379,8 @@ void S_Output_DrawSprite(
vertices[2].z = vz;
vertices[2].s = u1;
vertices[2].t = v1;
vertices[2].tex_coord[2] = 1.0;
vertices[2].tex_coord[3] = 1.0;
vertices[2].w = rhw;
vertices[2].r = vshade;
vertices[2].g = vshade;
@ -379,6 +391,8 @@ void S_Output_DrawSprite(
vertices[3].z = vz;
vertices[3].s = u0;
vertices[3].t = v1;
vertices[3].tex_coord[2] = 1.0;
vertices[3].tex_coord[3] = 1.0;
vertices[3].w = rhw;
vertices[3].r = vshade;
vertices[3].g = vshade;
@ -572,7 +586,7 @@ void S_Output_DrawShadow(PHD_VBUF *vbufs, int clip, int vertex_count)
// needs to be more than 8 cause clipping might return more polygons.
GFX_3D_VERTEX vertices[vertex_count * CLIP_VERTCOUNT_SCALE];
for (int i = 0; i < vertex_count; i++) {
for (int32_t i = 0; i < vertex_count; i++) {
GFX_3D_VERTEX *vertex = &vertices[i];
PHD_VBUF *vbuf = &vbufs[i];
vertex->x = vbuf->xs;
@ -686,7 +700,7 @@ void S_Output_DrawFlatTriangle(
}
float multiplier = g_Config.visuals.brightness / (16.0f * 255.0f);
for (int i = 0; i < vertex_count; i++) {
for (int32_t i = 0; i < vertex_count; i++) {
vertices[i].x = src_vbuf[i]->xs;
vertices[i].y = src_vbuf[i]->ys;
vertices[i].z = MAP_DEPTH(src_vbuf[i]->zv);
@ -713,7 +727,7 @@ void S_Output_DrawFlatTriangle(
if (vertex_count == 0) {
return;
}
for (int i = 0; i < vertex_count; i++) {
for (int32_t i = 0; i < vertex_count; i++) {
vertices[i].r *= color.r / 255.0f;
vertices[i].g *= color.g / 255.0f;
vertices[i].b *= color.b / 255.0f;
@ -755,6 +769,8 @@ void S_Output_DrawEnvMapTriangle(
vertices[i].w = 1.0f / src_vbuf[i]->zv;
vertices[i].s = M_GetUV(src_vbuf[i]->u);
vertices[i].t = M_GetUV(src_vbuf[i]->v);
vertices[i].tex_coord[2] = src_vbuf[i]->tex_coord[2];
vertices[i].tex_coord[3] = src_vbuf[i]->tex_coord[3];
vertices[i].r = vertices[i].g = vertices[i].b =
(8192.0f - src_vbuf[i]->g) * multiplier;
@ -819,7 +835,7 @@ void S_Output_DrawEnvMapQuad(
const PHD_VBUF *const src_vbuf[4] = { vn2, vn1, vn3, vn4 };
for (int i = 0; i < vertex_count; i++) {
for (int32_t i = 0; i < vertex_count; i++) {
vertices[i].x = src_vbuf[i]->xs;
vertices[i].y = src_vbuf[i]->ys;
vertices[i].z = MAP_DEPTH(src_vbuf[i]->zv);
@ -827,6 +843,8 @@ void S_Output_DrawEnvMapQuad(
vertices[i].w = 1.0f / src_vbuf[i]->zv;
vertices[i].s = M_GetUV(src_vbuf[i]->u);
vertices[i].t = M_GetUV(src_vbuf[i]->v);
vertices[i].tex_coord[2] = src_vbuf[i]->tex_coord[2];
vertices[i].tex_coord[3] = src_vbuf[i]->tex_coord[3];
vertices[i].r = vertices[i].g = vertices[i].b =
(8192.0f - src_vbuf[i]->g) * multiplier;
@ -866,7 +884,7 @@ void S_Output_DrawTexturedTriangle(
return;
}
for (int i = 0; i < vertex_count; i++) {
for (int32_t i = 0; i < vertex_count; i++) {
vertices[i].x = src_vbuf[i]->xs;
vertices[i].y = src_vbuf[i]->ys;
vertices[i].z = MAP_DEPTH(src_vbuf[i]->zv);
@ -874,6 +892,8 @@ void S_Output_DrawTexturedTriangle(
vertices[i].w = 1.0f / src_vbuf[i]->zv;
vertices[i].s = M_GetUV(src_vbuf[i]->u);
vertices[i].t = M_GetUV(src_vbuf[i]->v);
vertices[i].tex_coord[2] = src_vbuf[i]->tex_coord[2];
vertices[i].tex_coord[3] = src_vbuf[i]->tex_coord[3];
vertices[i].r = vertices[i].g = vertices[i].b =
(8192.0f - src_vbuf[i]->g) * multiplier;
@ -941,7 +961,7 @@ void S_Output_DrawTexturedQuad(
float multiplier = g_Config.visuals.brightness / 16.0f;
for (int i = 0; i < vertex_count; i++) {
for (int32_t i = 0; i < vertex_count; i++) {
vertices[i].x = src_vbuf[i]->xs;
vertices[i].y = src_vbuf[i]->ys;
vertices[i].z = MAP_DEPTH(src_vbuf[i]->zv);
@ -949,6 +969,8 @@ void S_Output_DrawTexturedQuad(
vertices[i].w = 1.0f / src_vbuf[i]->zv;
vertices[i].s = M_GetUV(src_vbuf[i]->u);
vertices[i].t = M_GetUV(src_vbuf[i]->v);
vertices[i].tex_coord[2] = src_vbuf[i]->tex_coord[2];
vertices[i].tex_coord[3] = src_vbuf[i]->tex_coord[3];
vertices[i].r = vertices[i].g = vertices[i].b =
(8192.0f - src_vbuf[i]->g) * multiplier;

View file

@ -39,3 +39,5 @@ GS_DEFINE(OSD_LIGHTNING_CONTRAST_FMT, "Lighting Contrast: %s")
GS_DEFINE(OSD_SCALER_FMT, "Scaler: x%d")
GS_DEFINE(OSD_HARDWARE_RENDERING, "Hardware rendering")
GS_DEFINE(OSD_SOFTWARE_RENDERING, "Software rendering")
GS_DEFINE(OSD_PERSPECTIVE_FILTER_ON, "Perspective correction: on")
GS_DEFINE(OSD_PERSPECTIVE_FILTER_OFF, "Perspective correction: off")