diff --git a/data/tr1/ship/shaders/common.glsl b/data/tr1/ship/shaders/common.glsl index fa3b28ad3..711f0f990 100644 --- a/data/tr1/ship/shaders/common.glsl +++ b/data/tr1/ship/shaders/common.glsl @@ -1,15 +1,22 @@ +#define WALL_L 1024 +#define NEUTRAL_SHADE 0x1000 +#define MAX_SHADE 0x1FFF + +#define VERT_NO_CAUSTICS 0x01 + +#define WIBBLE_SIZE 32 +#define MAX_WIBBLE 2 #define PI 3.1415926538 -vec3 waterWibble(vec4 position, vec2 viewportSize, float wibbleOffset) +vec3 waterWibble(vec4 position, vec2 viewportSize, int time) { // get screen coordinates vec3 ndc = position.xyz / position.w; //perspective divide/normalize vec2 viewportCoord = ndc.xy * 0.5 + 0.5; //ndc is -1 to 1 in GL. scale for 0 to 1 vec2 viewportPixelCoord = viewportCoord * viewportSize; - float amplitude = 2.0; - viewportPixelCoord.x += sin((wibbleOffset + viewportPixelCoord.y) * 2.0 * PI / 32) * amplitude; - viewportPixelCoord.y += sin((wibbleOffset + viewportPixelCoord.x) * 2.0 * PI / 32) * amplitude; + viewportPixelCoord.x += sin((float(time) + viewportPixelCoord.y) * 2.0 * PI / WIBBLE_SIZE) * MAX_WIBBLE; + viewportPixelCoord.y += sin((float(time) + viewportPixelCoord.x) * 2.0 * PI / WIBBLE_SIZE) * MAX_WIBBLE; // reverse transform viewportCoord = viewportPixelCoord / viewportSize; @@ -17,6 +24,24 @@ vec3 waterWibble(vec4 position, vec2 viewportSize, float wibbleOffset) return ndc * position.w; } +float shadeFog(float shade, float depth, vec2 fog) +{ + float fogBegin = fog.x; + float fogEnd = fog.y; + if (depth < fogBegin) { + return shade + 0.0; + } else if (depth >= fogEnd) { + return shade + float(MAX_SHADE); + } else { + return shade + (depth - fogBegin) * MAX_SHADE / (fogEnd - fogBegin); + } +} + +vec3 applyShade(vec3 color, float shade) +{ + return color * (2.0 - (shade / NEUTRAL_SHADE)); +} + bool discardTranslucent(sampler2D tex, vec2 uv) { // do not use smoothing for chroma key diff --git a/data/tr1/ship/shaders/meshes.glsl b/data/tr1/ship/shaders/meshes.glsl new file mode 100644 index 000000000..d897bef92 --- /dev/null +++ b/data/tr1/ship/shaders/meshes.glsl @@ -0,0 +1,91 @@ +#ifdef VERTEX + +uniform int uTime; +uniform samplerBuffer uUVW; // texture u, v, layer +uniform vec2 uViewportSize; +uniform mat4 uMatProjection; +uniform mat4 uMatModelView; +uniform bool uTrapezoidFilterEnabled; +uniform bool uWibbleEffect; + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in int inUVWIdx; +layout(location = 2) in vec2 inTrapezoidRatios; +layout(location = 3) in int inFlags; +layout(location = 4) in float inShade; + +out vec4 gWorldPos; +flat out int gFlags; +flat out int gTexLayer; +out vec2 gTexUV; +out vec2 gTrapezoidRatios; +out float gShade; + +void main(void) { + gWorldPos = uMatModelView * vec4(inPosition.xyz, 1.0); + gl_Position = uMatProjection * gWorldPos; + + if (uWibbleEffect && (inFlags & VERT_NO_CAUSTICS) == 0) { + gl_Position.xyz = + waterWibble(gl_Position, uViewportSize, uTime); + } + + vec3 uvw = texelFetch(uUVW, int(inUVWIdx)).xyz; + gFlags = inFlags; + gTexLayer = int(uvw.z); + gTexUV = uvw.xy; + gTrapezoidRatios = inTrapezoidRatios; + if (uTrapezoidFilterEnabled) { + gTexUV *= inTrapezoidRatios; + } + gShade = inShade; +} + +#elif defined(FRAGMENT) + +uniform int uTime; +uniform sampler2DArray uTexAtlas; +uniform bool uSmoothingEnabled; +uniform bool uAlphaDiscardEnabled; +uniform bool uTrapezoidFilterEnabled; +uniform float uAlphaThreshold; +uniform float uBrightnessMultiplier; +uniform vec3 uGlobalTint; +uniform vec2 uFog; // x = fog start, y = fog end +uniform bool uWaterEffect; + +in vec4 gWorldPos; +flat in int gFlags; +flat in int gTexLayer; +in vec2 gTexUV; +in vec2 gTrapezoidRatios; +in float gShade; +out vec4 outColor; + +void main(void) { + vec4 texColor = vec4(1); + vec3 texCoords = vec3(gTexUV.x, gTexUV.y, gTexLayer); + if (uTrapezoidFilterEnabled) { + texCoords.xy /= gTrapezoidRatios.xy; + } + + if (texCoords.z >= 0) { + if (uAlphaDiscardEnabled && uSmoothingEnabled && discardTranslucent(uTexAtlas, texCoords)) { + discard; + } + + texColor = texture(uTexAtlas, texCoords); + if (uAlphaThreshold >= 0.0 && texColor.a <= uAlphaThreshold) { + discard; + } + } + + float shade = gShade; + shade = shadeFog(shade, gWorldPos.z, uFog); + + texColor.rgb = applyShade(texColor.rgb, shade); + texColor.rgb *= uBrightnessMultiplier; + texColor.rgb *= uGlobalTint; + outColor = vec4(texColor.rgb, 1.0); +} +#endif diff --git a/data/tr1/ship/shaders/sprites.glsl b/data/tr1/ship/shaders/sprites.glsl index 74df3ef32..c612c5ebf 100644 --- a/data/tr1/ship/shaders/sprites.glsl +++ b/data/tr1/ship/shaders/sprites.glsl @@ -6,12 +6,12 @@ uniform samplerBuffer uUVW; // texture u, v, layer uniform vec2 uViewportSize; uniform mat4 uMatProjection; uniform mat4 uMatModelView; -uniform float uWibbleOffset; -uniform vec2 uFog; // x = start, y = end +uniform bool uWibbleEffect; +uniform int uTime; layout(location = 0) in vec3 inPosition; layout(location = 1) in vec2 inDisplacement; -layout(location = 2) in int inTextureIdx; +layout(location = 2) in int inUVWIdx; layout(location = 3) in float inShade; out vec2 gTexUV; @@ -23,12 +23,12 @@ void main(void) { centerEyeSpace.xy += inDisplacement; gl_Position = uMatProjection * centerEyeSpace; - if (uWibbleOffset >= 0.0) { + if (uWibbleEffect) { gl_Position.xyz = - waterWibble(gl_Position, uViewportSize, uWibbleOffset); + waterWibble(gl_Position, uViewportSize, uTime); } - vec3 uvw = texelFetch(uUVW, int(inTextureIdx)).xyz; + vec3 uvw = texelFetch(uUVW, int(inUVWIdx)).xyz; gTexUV = uvw.xy; gTexLayer = int(uvw.z); gShade = inShade; @@ -36,7 +36,7 @@ void main(void) { #elif defined(FRAGMENT) -uniform sampler2DArray uTexture; +uniform sampler2DArray uTexAtlas; uniform bool uSmoothingEnabled; uniform float uBrightnessMultiplier; uniform vec3 uGlobalTint; @@ -48,8 +48,8 @@ out vec4 outColor; void main(void) { vec3 uvw = vec3(gTexUV.x, gTexUV.y, gTexLayer); - vec4 texColor = texture(uTexture, uvw); - if (uSmoothingEnabled && discardTranslucent(uTexture, uvw)) { + vec4 texColor = texture(uTexAtlas, uvw); + if (uSmoothingEnabled && discardTranslucent(uTexAtlas, uvw)) { discard; } diff --git a/docs/tr1/CHANGELOG.md b/docs/tr1/CHANGELOG.md index db0561503..fd09f8b26 100644 --- a/docs/tr1/CHANGELOG.md +++ b/docs/tr1/CHANGELOG.md @@ -1,4 +1,6 @@ ## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr1-4.9...develop) - ××××-××-×× +- fixed anisotropy filter causing black lines (#902) +- improved rendering performance ## [4.9](https://github.com/LostArtefacts/TRX/compare/tr1-4.8.3...tr1-4.9) - 2025-03-31 - added quadrilateral interpolation (#354) diff --git a/src/libtrx/gfx/gl/program.c b/src/libtrx/gfx/gl/program.c index ff1d30c24..71950aa77 100644 --- a/src/libtrx/gfx/gl/program.c +++ b/src/libtrx/gfx/gl/program.c @@ -32,7 +32,7 @@ void GFX_GL_Program_Close(GFX_GL_PROGRAM *program) } } -void GFX_GL_Program_Bind(GFX_GL_PROGRAM *program) +void GFX_GL_Program_Bind(const GFX_GL_PROGRAM *const program) { ASSERT(program != nullptr); glUseProgram(program->id); diff --git a/src/libtrx/include/libtrx/gfx/gl/program.h b/src/libtrx/include/libtrx/gfx/gl/program.h index fd6a2afb0..0bbfadc44 100644 --- a/src/libtrx/include/libtrx/gfx/gl/program.h +++ b/src/libtrx/include/libtrx/gfx/gl/program.h @@ -12,7 +12,7 @@ typedef struct { bool GFX_GL_Program_Init(GFX_GL_PROGRAM *program); void GFX_GL_Program_Close(GFX_GL_PROGRAM *program); -void GFX_GL_Program_Bind(GFX_GL_PROGRAM *program); +void GFX_GL_Program_Bind(const GFX_GL_PROGRAM *program); char *GFX_GL_Program_PreprocessShader( const char *content, GLenum type, GFX_GL_BACKEND backend); void GFX_GL_Program_AttachShader( diff --git a/src/tr1/game/level.c b/src/tr1/game/level.c index 54e4a1db0..f9b758934 100644 --- a/src/tr1/game/level.c +++ b/src/tr1/game/level.c @@ -269,7 +269,7 @@ static void M_CompleteSetup(const GF_LEVEL *const level) Level_LoadTexturePages(); Level_LoadPalettes(); Level_LoadFaces(); - Output_DownloadTextures(); + Output_ObserveLevelLoad(); // Initialise the sound effects. LEVEL_INFO *const info = Level_GetInfo(); @@ -376,6 +376,11 @@ void Level_Load(const GF_LEVEL *const level) Benchmark_End(&benchmark, nullptr); } +void Level_Unload(void) +{ + Output_ObserveLevelUnload(); +} + bool Level_Initialise( const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) { @@ -424,6 +429,7 @@ bool Level_Initialise( Pierre_Reset(); Lara_InitialiseLoad(NO_ITEM); + Level_Unload(); Level_Load(level); GameStringTable_Apply(level); diff --git a/src/tr1/game/objects/common.c b/src/tr1/game/objects/common.c index e51bc1872..30f3022a3 100644 --- a/src/tr1/game/objects/common.c +++ b/src/tr1/game/objects/common.c @@ -278,19 +278,15 @@ void Object_SetMeshReflective( OBJECT_MESH *const mesh = Object_GetMesh(obj->mesh_idx + mesh_idx); mesh->enable_reflections = enabled; - for (int32_t i = 0; i < mesh->num_tex_face4s; i++) { mesh->tex_face4s[i].enable_reflections = enabled; } - for (int32_t i = 0; i < mesh->num_tex_face3s; i++) { mesh->tex_face3s[i].enable_reflections = enabled; } - for (int32_t i = 0; i < mesh->num_flat_face4s; i++) { mesh->flat_face4s[i].enable_reflections = enabled; } - for (int32_t i = 0; i < mesh->num_flat_face3s; i++) { mesh->flat_face3s[i].enable_reflections = enabled; } diff --git a/src/tr1/game/output.c b/src/tr1/game/output.c index 7c6630ff2..36e2aeba0 100644 --- a/src/tr1/game/output.c +++ b/src/tr1/game/output.c @@ -1,15 +1,12 @@ #include "game/output.h" -#include "game/clock.h" +#include "game/output/meshes.h" +#include "game/output/rooms.h" #include "game/output/sprites.h" #include "game/output/textures.h" #include "game/overlay.h" -#include "game/random.h" -#include "game/room.h" #include "game/shell.h" #include "game/viewport.h" -#include "global/const.h" -#include "global/types.h" #include "global/vars.h" #include "specific/s_output.h" @@ -18,14 +15,8 @@ #include #include #include -#include -#include -#include +#include #include -#include - -#include -#include #define MAX_LIGHTNINGS 64 #define PHD_IONE (PHD_ONE / 4) @@ -49,19 +40,9 @@ static bool m_IsSkyboxEnabled = false; static bool m_IsWibbleEffect = false; static bool m_IsWaterEffect = false; static bool m_IsShadeEffect = false; -static int m_OverlayCurAlpha = 0; -static int m_OverlayDstAlpha = 0; -static int m_BackdropCurAlpha = 0; -static int m_BackdropDstAlpha = 0; -static int32_t m_WibbleOffset = 0; +static int32_t m_Time = 0; static int32_t m_AnimatedTexturesOffset = 0; -static CLOCK_TIMER m_WibbleTimer = { .type = CLOCK_TIMER_SIM }; -static CLOCK_TIMER m_AnimatedTexturesTimer = { .type = CLOCK_TIMER_SIM }; -static CLOCK_TIMER m_FadeTimer = { .type = CLOCK_TIMER_SIM }; -static int32_t m_WibbleTable[WIBBLE_SIZE] = {}; -static int32_t m_ShadeTable[WIBBLE_SIZE] = {}; -static int32_t m_RandTable[WIBBLE_SIZE] = {}; static PHD_VBUF *m_VBuf = nullptr; static TEXTURE_UV *m_EnvMapUV = nullptr; @@ -82,7 +63,9 @@ static struct { GLint bound_program; GLint bound_vao; GLint bound_vbo; + GLint bound_active_texture; GLint bound_texture; + GLint bound_polygon_mode[2]; } m_CachedState; static void M_DrawSphere(const XYZ_32 pos, const int32_t radius); @@ -93,14 +76,10 @@ static void M_DrawTexturedFace4s(const FACE4 *faces, int32_t count); static void M_DrawObjectFace3EnvMap(const FACE3 *faces, int32_t count); static void M_DrawObjectFace4EnvMap(const FACE4 *faces, int32_t count); static uint16_t M_CalcVertex(PHD_VBUF *vbuf, const XYZ_16 pos); -static void M_CalcVertexWibble(PHD_VBUF *vbuf); static bool M_CalcObjectVertices(const XYZ_16 *vertices, int16_t count); static void M_CalcVerticeLight(const OBJECT_MESH *mesh); static bool M_CalcVerticeEnvMap(const OBJECT_MESH *mesh); static void M_CalcSkyboxLight(const OBJECT_MESH *mesh); -static void M_CalcRoomVertices(const ROOM_MESH *mesh); -static void M_CalcRoomVerticesWibble(const ROOM_MESH *mesh); -static void M_CalcWibbleTable(void); static void M_DrawSphere(const XYZ_32 pos, const int32_t radius) { @@ -354,31 +333,6 @@ static uint16_t M_CalcVertex(PHD_VBUF *const vbuf, const XYZ_16 pos) return clip_flags; } -static void M_CalcVertexWibble(PHD_VBUF *const vbuf) -{ - double xs = vbuf->xs; - double ys = vbuf->ys; - xs += m_WibbleTable[(m_WibbleOffset + (int)ys) & (WIBBLE_SIZE - 1)]; - ys += m_WibbleTable[(m_WibbleOffset + (int)xs) & (WIBBLE_SIZE - 1)]; - - int16_t clip_flags = vbuf->clip & ~15; - if (xs < g_PhdLeft) { - clip_flags |= 1; - } else if (xs > g_PhdRight) { - clip_flags |= 2; - } - - if (ys < g_PhdTop) { - clip_flags |= 4; - } else if (ys > g_PhdBottom) { - clip_flags |= 8; - } - - vbuf->xs = xs; - vbuf->ys = ys; - vbuf->clip = clip_flags; -} - static bool M_CalcObjectVertices( const XYZ_16 *const vertices, const int16_t count) { @@ -493,70 +447,20 @@ static void M_CalcSkyboxLight(const OBJECT_MESH *const mesh) } } -static void M_CalcRoomVertices(const ROOM_MESH *const mesh) -{ - for (int32_t i = 0; i < mesh->num_vertices; i++) { - PHD_VBUF *const vbuf = &m_VBuf[i]; - const ROOM_VERTEX *const vertex = &mesh->vertices[i]; - - M_CalcVertex(vbuf, vertex->pos); - - vbuf->g = vertex->light_adder; - if (vbuf->zv >= Output_GetNearZ()) { - const int32_t depth = ((int32_t)vbuf->zv) >> W2V_SHIFT; - if (depth > Output_GetDrawDistMax()) { - vbuf->g = MAX_LIGHTING; - if (!m_IsSkyboxEnabled) { - vbuf->clip |= 16; - } - } else { - vbuf->g += Output_CalcFogShade(depth); - if (!m_IsWaterEffect) { - CLAMPG(vbuf->g, MAX_LIGHTING); - } - } - } - - if (m_IsWaterEffect) { - vbuf->g += m_ShadeTable[( - ((uint8_t)m_WibbleOffset - + (uint8_t)m_RandTable[(mesh->num_vertices - i) % WIBBLE_SIZE]) - % WIBBLE_SIZE)]; - CLAMP(vbuf->g, 0, 0x1FFF); - } - } -} - -static void M_CalcRoomVerticesWibble(const ROOM_MESH *const mesh) -{ - for (int32_t i = 0; i < mesh->num_vertices; i++) { - if (mesh->vertices[i].flags & NO_VERT_MOVE) { - continue; - } - M_CalcVertexWibble(&m_VBuf[i]); - } -} - -static void M_CalcWibbleTable(void) -{ - for (int i = 0; i < WIBBLE_SIZE; i++) { - PHD_ANGLE angle = (i * DEG_360) / WIBBLE_SIZE; - m_WibbleTable[i] = Math_Sin(angle) * MAX_WIBBLE >> W2V_SHIFT; - m_ShadeTable[i] = Math_Sin(angle) * MAX_SHADE >> W2V_SHIFT; - m_RandTable[i] = (Random_GetDraw() >> 5) - 0x01FF; - } -} - bool Output_Init(void) { - M_CalcWibbleTable(); - Output_Sprites_Init(); + const bool result = S_Output_Init(); Output_Textures_Init(); - return S_Output_Init(); + Output_Sprites_Init(); + Output_Meshes_Init(); + Output_Rooms_Init(); + return result; } void Output_Shutdown(void) { + Output_Rooms_Shutdown(); + Output_Meshes_Shutdown(); Output_Sprites_Shutdown(); Output_Textures_Shutdown(); S_Output_Shutdown(); @@ -576,18 +480,23 @@ void Output_SetWindowSize(int width, int height) void Output_ApplyRenderSettings(void) { + Output_Textures_ApplyRenderSettings(); S_Output_ApplyRenderSettings(); if (m_BackdropImagePath) { Output_LoadBackgroundFromFile(m_BackdropImagePath); } } -void Output_DownloadTextures(void) +void Output_ObserveLevelLoad(void) { S_Output_DownloadTextures(Output_GetTexturePageCount()); - Output_Textures_ObserveLevelLoad(); Output_Sprites_ObserveLevelLoad(); + Output_Rooms_ObserveLevelLoad(); +} + +void Output_ObserveLevelUnload(void) +{ } void Output_DrawBlack(void) @@ -613,7 +522,9 @@ void Output_BeginScene(void) Output_ApplyFOV(); Text_DrawReset(); + Output_RememberState(); Output_Sprites_RenderBegin(); + Output_Meshes_RenderBegin(); S_Output_RenderBegin(); m_LightningCount = 0; } @@ -707,21 +618,17 @@ void Output_DrawSkybox(const OBJECT_MESH *const mesh) S_Output_EnableDepthTest(); } -void Output_DrawRoomMesh(const ROOM *const room) +void Output_DrawRoomMesh(ROOM *const room) { - const ROOM_MESH *const mesh = &room->mesh; - M_CalcRoomVertices(mesh); - - if (m_IsWibbleEffect) { - S_Output_DisableDepthWrites(); - M_DrawTexturedFace4s(mesh->face4s, mesh->num_face4s); - M_DrawTexturedFace3s(mesh->face3s, mesh->num_face3s); - S_Output_EnableDepthWrites(); - M_CalcRoomVerticesWibble(mesh); - } - - M_DrawTexturedFace4s(mesh->face4s, mesh->num_face4s); - M_DrawTexturedFace3s(mesh->face3s, mesh->num_face3s); + Output_RememberState(); + Output_LightRoom(room); + Output_EnableScissor( + room->bound_left, room->bound_bottom, + room->bound_right - room->bound_left, + room->bound_bottom - room->bound_top); + Output_Rooms_RenderRoom(g_MatrixPtr, Output_GetTint(), room); + Output_DisableScissor(); + Output_RestoreState(); } void Output_DrawRoomPortals(const ROOM *const room) @@ -917,6 +824,16 @@ void Output_SetLightDivider(const int32_t divider) m_LsDivider = divider; } +int32_t Output_GetLightDivider(void) +{ + return m_LsDivider; +} + +XYZ_32 Output_GetLightVectorView(void) +{ + return m_LsVectorView; +} + int32_t Output_GetNearZ(void) { return Output_GetDrawDistMin() << W2V_SHIFT; @@ -1137,6 +1054,9 @@ void Output_SetupBelowWater(bool underwater) m_IsWaterEffect = true; m_IsWibbleEffect = !underwater; m_IsShadeEffect = true; + Output_RememberState(); + Output_Shader_UploadWaterEffect(Output_Meshes_GetShader(), m_IsWaterEffect); + Output_RestoreState(); } void Output_SetupAboveWater(bool underwater) @@ -1144,11 +1064,14 @@ void Output_SetupAboveWater(bool underwater) m_IsWaterEffect = false; m_IsWibbleEffect = underwater; m_IsShadeEffect = underwater; + Output_RememberState(); + Output_Shader_UploadWaterEffect(Output_Meshes_GetShader(), m_IsWaterEffect); + Output_RestoreState(); } void Output_AnimateTextures(const int32_t num_frames) { - m_WibbleOffset = (m_WibbleOffset + num_frames) % WIBBLE_SIZE; + m_Time = (m_Time + num_frames); m_AnimatedTexturesOffset += num_frames; bool update = false; while (m_AnimatedTexturesOffset > 5) { @@ -1157,7 +1080,7 @@ void Output_AnimateTextures(const int32_t num_frames) m_AnimatedTexturesOffset -= 5; } if (update) { - Output_Textures_Update(); + Output_Textures_CycleAnimations(); } } @@ -1377,12 +1300,19 @@ void Output_LightRoomVertices(const ROOM *room) ASSERT_FAIL(); } -int32_t Output_GetWibbleOffset(void) +bool Output_GetWaterEffect(void) { - if (!m_IsWibbleEffect) { - return -1; - } - return m_WibbleOffset; + return m_IsWaterEffect; +} + +bool Output_GetWibbleEffect(void) +{ + return m_IsWibbleEffect; +} + +int32_t Output_GetTime(void) +{ + return m_Time; } void Output_GetProjectionMatrix(GLfloat output[][4]) @@ -1429,8 +1359,9 @@ void Output_EnableScissor( glEnable(GL_SCISSOR_TEST); glScissor( - x * scale_x, (GFX_Context_GetDisplayHeight() - y) * scale_y, - w * scale_x, h * scale_y); + x * scale_x - border, + (GFX_Context_GetDisplayHeight() - y) * scale_y - border, + w * scale_x + border * 2, h * scale_y + border * 2); } void Output_DisableScissor(void) @@ -1443,15 +1374,19 @@ void Output_RememberState(void) glGetIntegerv(GL_CURRENT_PROGRAM, &m_CachedState.bound_program); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &m_CachedState.bound_vao); glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &m_CachedState.bound_vbo); + glGetIntegerv(GL_ACTIVE_TEXTURE, &m_CachedState.bound_active_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &m_CachedState.bound_texture); + glGetIntegerv(GL_POLYGON_MODE, &m_CachedState.bound_polygon_mode[0]); GFX_GL_CheckError(); } void Output_RestoreState(void) { + glUseProgram(m_CachedState.bound_program); glBindVertexArray(m_CachedState.bound_vao); glBindBuffer(GL_ARRAY_BUFFER, m_CachedState.bound_vbo); + glActiveTexture(m_CachedState.bound_active_texture); glBindTexture(GL_TEXTURE_2D, m_CachedState.bound_texture); - glUseProgram(m_CachedState.bound_program); + glPolygonMode(GL_FRONT_AND_BACK, m_CachedState.bound_polygon_mode[0]); GFX_GL_CheckError(); } diff --git a/src/tr1/game/output.h b/src/tr1/game/output.h index be285ccb3..b6da77533 100644 --- a/src/tr1/game/output.h +++ b/src/tr1/game/output.h @@ -14,7 +14,8 @@ void Output_ReserveVertexBuffer(size_t size); void Output_SetWindowSize(int width, int height); void Output_ApplyRenderSettings(void); -void Output_DownloadTextures(void); +void Output_ObserveLevelLoad(void); +void Output_ObserveLevelUnload(void); int32_t Output_GetNearZ(void); int32_t Output_GetFarZ(void); @@ -38,7 +39,7 @@ void Output_SetSkyboxEnabled(bool enabled); bool Output_IsSkyboxEnabled(void); void Output_DrawSkybox(const OBJECT_MESH *mesh); -void Output_DrawRoomMesh(const ROOM *mesh); +void Output_DrawRoomMesh(ROOM *mesh); void Output_DrawRoomPortals(const ROOM *room); void Output_DrawRoomTriggers(const ROOM *room); void Output_DrawShadow(int16_t size, const BOUNDS_16 *bounds, const ITEM *item); @@ -88,7 +89,9 @@ RGB_F Output_GetTint(void); void Output_FillEnvironmentMap(void); bool Output_MakeScreenshot(const char *path); -int32_t Output_GetWibbleOffset(void); +bool Output_GetWaterEffect(void); +bool Output_GetWibbleEffect(void); +int32_t Output_GetTime(void); void Output_GetProjectionMatrix(GLfloat output[][4]); void Output_EnableScissor(float x, float y, float w, float h); void Output_DisableScissor(void); @@ -96,3 +99,7 @@ void Output_DisableScissor(void); // TODO: these functions are poor in their design and should be not needed void Output_RememberState(void); void Output_RestoreState(void); + +float Output_AdjustUV(uint16_t uv); +int32_t Output_GetLightDivider(void); +XYZ_32 Output_GetLightVectorView(void); diff --git a/src/tr1/game/output/meshes.c b/src/tr1/game/output/meshes.c new file mode 100644 index 000000000..37617c96d --- /dev/null +++ b/src/tr1/game/output/meshes.c @@ -0,0 +1,25 @@ +#include "game/output/meshes.h" + +static OUTPUT_SHADER *m_Shader = nullptr; + +void Output_Meshes_Init(void) +{ + m_Shader = Output_Shader_Create("shaders/meshes.glsl"); +} + +void Output_Meshes_Shutdown(void) +{ + Output_Shader_Free(m_Shader); + m_Shader = nullptr; +} + +void Output_Meshes_RenderBegin(void) +{ + Output_Shader_UploadCommonUniforms(m_Shader); + Output_Shader_UploadProjectionMatrix(m_Shader); +} + +OUTPUT_SHADER *Output_Meshes_GetShader(void) +{ + return m_Shader; +} diff --git a/src/tr1/game/output/meshes.h b/src/tr1/game/output/meshes.h new file mode 100644 index 000000000..350bf18d5 --- /dev/null +++ b/src/tr1/game/output/meshes.h @@ -0,0 +1,12 @@ +#pragma once + +#include "game/output/shader.h" + +// clang-format off +#define VERT_NO_CAUSTICS 0b0000'0001 // = 0x01 +// clang-format on + +void Output_Meshes_Init(void); +void Output_Meshes_Shutdown(void); +void Output_Meshes_RenderBegin(void); +OUTPUT_SHADER *Output_Meshes_GetShader(void); diff --git a/src/tr1/game/output/rooms.c b/src/tr1/game/output/rooms.c new file mode 100644 index 000000000..7656641b5 --- /dev/null +++ b/src/tr1/game/output/rooms.c @@ -0,0 +1,327 @@ +#include "game/output/rooms.h" + +#include "game/output.h" +#include "game/output/meshes.h" +#include "game/output/textures.h" +#include "game/output/utils.h" +#include "game/random.h" +#include "game/room.h" + +#include +#include + +#pragma pack(push, 1) +typedef struct { + // clang-format off + XYZ_F pos; // attribute 0 + int32_t uvw_idx; // attribute 1 + float trapezoid_ratio[2]; // attribute 2 + uint16_t flags; // attribute 3 + // clang-format on +} M_MESH_VERTEX; + +typedef int16_t M_MESH_SHADE; +#pragma pack(pop) + +typedef struct { + int32_t vertex_start; + int32_t vertex_count; + int16_t *caustics; +} M_BATCH; + +static struct { + GLuint vao; + GLuint geom_vbo; + size_t vertex_count; + GLuint shade_vbo; + M_MESH_VERTEX *geom_vbo_data; + M_MESH_SHADE *shade_vbo_data; + size_t batch_count; + M_BATCH *batches; +} m_Priv; + +static int32_t m_ShadeTable[WIBBLE_SIZE] = {}; +static int32_t m_CausticsTable[WIBBLE_SIZE] = {}; +static OUTPUT_SHADER *m_Shader = nullptr; + +static M_MESH_SHADE M_ShadeCaustics(M_MESH_SHADE source, uint8_t caustic) +{ + if (Output_GetWaterEffect()) { + source += + m_ShadeTable[((uint8_t)Output_GetTime() + caustic) % WIBBLE_SIZE]; + CLAMP(source, 0, MAX_LIGHTING); + } else { + CLAMPG(source, MAX_LIGHTING); + } + return source; +} + +static M_BATCH *M_GetBatch(const ROOM *const room) +{ + // Room data gets swapped when flipping, but the VBOs do not. So a room 2 + // that gets flipped to room 17 ends up getting the data from room 2, + // whereas the VBO needs to take data from room 17. + const int16_t room_num = + Room_GetFlipStatus() && room->flipped_room != NO_ROOM_NEG + ? room->flipped_room + : Room_GetNumber(room); + return &m_Priv.batches[room_num]; +} + +static void M_FillVertex( + M_MESH_VERTEX *const out_vertex, const int32_t uvw_idx, const XYZ_16 pos, + const uint16_t flags) +{ + out_vertex->pos = (XYZ_F) { .x = pos.x, .y = pos.y, .z = pos.z }; + out_vertex->uvw_idx = uvw_idx; + out_vertex->flags = flags & NO_VERT_MOVE ? VERT_NO_CAUSTICS : 0; +} + +static M_MESH_VERTEX *M_FillRoomFace4( + const ROOM *const room, M_MESH_VERTEX *out_vertex, const FACE4 *const face) +{ + for (int32_t i = 0; i < OUTPUT_QUAD_VERTICES; i++) { + const int32_t j = OUTPUT_QUAD_TO_FAN(i); + const int32_t uvw_idx = face->texture_idx * 4 + j; + const ROOM_VERTEX *const room_vertex = + &room->mesh.vertices[face->vertices[j]]; + M_FillVertex(out_vertex, uvw_idx, room_vertex->pos, room_vertex->flags); + out_vertex->trapezoid_ratio[0] = face->texture_zw[j].z; + out_vertex->trapezoid_ratio[1] = face->texture_zw[j].w; + out_vertex++; + } + return out_vertex; +} + +static M_MESH_VERTEX *M_FillRoomFace3( + const ROOM *const room, M_MESH_VERTEX *out_vertex, const FACE3 *const face) +{ + for (int32_t i = 0; i < OUTPUT_TRI_VERTICES; i++) { + const int32_t j = OUTPUT_TRI_TO_FAN(i); + const int32_t uvw_idx = face->texture_idx * 4 + j; + const ROOM_VERTEX *const room_vertex = + &room->mesh.vertices[face->vertices[j]]; + M_FillVertex(out_vertex, uvw_idx, room_vertex->pos, room_vertex->flags); + out_vertex->trapezoid_ratio[0] = 1.0f; + out_vertex->trapezoid_ratio[1] = 1.0f; + out_vertex++; + } + return out_vertex; +} + +static void M_UpdateRoomGeometry(const ROOM *const room) +{ + M_BATCH *const batch = &m_Priv.batches[Room_GetNumber(room)]; + M_MESH_VERTEX *out_vertex = &m_Priv.geom_vbo_data[batch->vertex_start]; + for (int32_t i = 0; i < room->mesh.num_face4s; i++) { + out_vertex = M_FillRoomFace4(room, out_vertex, &room->mesh.face4s[i]); + } + for (int32_t i = 0; i < room->mesh.num_face3s; i++) { + out_vertex = M_FillRoomFace3(room, out_vertex, &room->mesh.face3s[i]); + } +} + +static void M_UpdateRoomShades(const ROOM *const room) +{ + M_BATCH *const batch = M_GetBatch(room); + M_MESH_SHADE *out_shade = &m_Priv.shade_vbo_data[batch->vertex_start]; + for (int32_t i = 0; i < room->mesh.num_face4s; i++) { + const FACE4 *const face = &room->mesh.face4s[i]; + for (int32_t j = 0; j < OUTPUT_QUAD_VERTICES; j++) { + const int32_t k = OUTPUT_QUAD_TO_FAN(j); + *out_shade = room->mesh.vertices[face->vertices[k]].light_adder; + *out_shade = + M_ShadeCaustics(*out_shade, batch->caustics[face->vertices[k]]); + *out_shade++; + } + } + for (int32_t i = 0; i < room->mesh.num_face3s; i++) { + const FACE3 *const face = &room->mesh.face3s[i]; + for (int32_t j = 0; j < OUTPUT_TRI_VERTICES; j++) { + const int32_t k = OUTPUT_TRI_TO_FAN(j); + *out_shade = room->mesh.vertices[face->vertices[k]].light_adder; + *out_shade = + M_ShadeCaustics(*out_shade, batch->caustics[face->vertices[k]]); + out_shade++; + } + } +} + +static void M_UpdateVertices(void) +{ + for (int32_t i = 0; i < Room_GetCount(); i++) { + const ROOM *const room = Room_Get(i); + M_UpdateRoomGeometry(room); + } + + glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo); + GFX_TRACK_DATA( + glBufferData, GL_ARRAY_BUFFER, + m_Priv.vertex_count * sizeof(M_MESH_VERTEX), m_Priv.geom_vbo_data, + GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo); + GFX_TRACK_DATA( + glBufferData, GL_ARRAY_BUFFER, + m_Priv.vertex_count * sizeof(M_MESH_SHADE), m_Priv.shade_vbo_data, + GL_DYNAMIC_DRAW); // shades are always dynamic +} + +static void M_PrepareBuffers(void) +{ + m_Priv.batch_count = Room_GetCount(); + Memory_FreePointer(&m_Priv.batches); + m_Priv.batches = Memory_Alloc(sizeof(M_BATCH) * m_Priv.batch_count); + + int32_t last_vertex = 0; + for (int32_t i = 0; i < Room_GetCount(); i++) { + const ROOM *const room = Room_Get(i); + M_BATCH *const batch = &m_Priv.batches[i]; + batch->caustics = + Memory_Alloc(room->mesh.num_vertices * sizeof(int16_t)); + batch->vertex_start = last_vertex; + batch->vertex_count = 0; + batch->vertex_count += + Room_Get(i)->mesh.num_face4s * OUTPUT_QUAD_VERTICES; + batch->vertex_count += + Room_Get(i)->mesh.num_face3s * OUTPUT_TRI_VERTICES; + last_vertex += batch->vertex_count; + + for (int32_t j = 0; j < room->mesh.num_vertices; j++) { + batch->caustics[j] = + m_CausticsTable[(room->mesh.num_vertices - j) % WIBBLE_SIZE]; + } + } + + m_Priv.vertex_count = last_vertex; + + m_Priv.geom_vbo_data = + Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_VERTEX)); + m_Priv.shade_vbo_data = + Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_SHADE)); + + glGenVertexArrays(1, &m_Priv.vao); + glGenBuffers(1, &m_Priv.geom_vbo); + glGenBuffers(1, &m_Priv.shade_vbo); + + glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo); + glBindVertexArray(m_Priv.vao); + + glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo); + glEnableVertexAttribArray(0); + glVertexAttribPointer( + 0, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX), + (void *)(intptr_t)offsetof(M_MESH_VERTEX, pos)); + + glEnableVertexAttribArray(1); + glVertexAttribIPointer( + 1, 1, GL_UNSIGNED_INT, sizeof(M_MESH_VERTEX), + (void *)(intptr_t)offsetof(M_MESH_VERTEX, uvw_idx)); + + glEnableVertexAttribArray(2); + glVertexAttribPointer( + 2, 2, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX), + (void *)(intptr_t)offsetof(M_MESH_VERTEX, trapezoid_ratio)); + + glEnableVertexAttribArray(3); + glVertexAttribIPointer( + 3, 1, GL_UNSIGNED_SHORT, sizeof(M_MESH_VERTEX), + (void *)(intptr_t)offsetof(M_MESH_VERTEX, flags)); + + glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo); + glEnableVertexAttribArray(4); + glVertexAttribPointer( + 4, 1, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(M_MESH_SHADE), 0); + + M_UpdateVertices(); +} + +static void M_FreeBuffers(void) +{ + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + if (m_Priv.vao != 0) { + glDeleteVertexArrays(1, &m_Priv.vao); + m_Priv.vao = 0; + } + if (m_Priv.geom_vbo != 0) { + glDeleteBuffers(1, &m_Priv.geom_vbo); + m_Priv.geom_vbo = 0; + } + if (m_Priv.shade_vbo != 0) { + glDeleteBuffers(1, &m_Priv.shade_vbo); + m_Priv.shade_vbo = 0; + } + Memory_FreePointer(&m_Priv.geom_vbo_data); + Memory_FreePointer(&m_Priv.shade_vbo_data); + for (int32_t i = 0; i < (int32_t)m_Priv.batch_count; i++) { + Memory_FreePointer(&m_Priv.batches[i].caustics); + } + Memory_FreePointer(&m_Priv.batches); +} + +void Output_Rooms_Init(void) +{ + m_Shader = Output_Meshes_GetShader(); + for (int32_t i = 0; i < WIBBLE_SIZE; i++) { + const int16_t angle = (i * DEG_360) / WIBBLE_SIZE; + m_ShadeTable[i] = Math_Sin(angle) * MAX_SHADE >> W2V_SHIFT; + m_CausticsTable[i] = (Random_GetDraw() >> 5) - 0x01FF; + } +} + +void Output_Rooms_Shutdown(void) +{ + M_FreeBuffers(); +} + +void Output_Rooms_ObserveLevelLoad(void) +{ + M_FreeBuffers(); + M_PrepareBuffers(); +} + +void Output_Rooms_RenderRoom( + const MATRIX *const matrix, const RGB_F tint, const ROOM *const room) +{ + const M_BATCH *const batch = M_GetBatch(room); + + M_UpdateRoomShades(room); + + Output_Shader_UploadMatrix(m_Shader, matrix); + Output_Shader_UploadTint(m_Shader, tint); + GFX_GL_CheckError(); + + glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo); + GFX_TRACK_SUBDATA( + glBufferSubData, GL_ARRAY_BUFFER, + batch->vertex_start * sizeof(M_MESH_SHADE), + batch->vertex_count * sizeof(M_MESH_SHADE), + &m_Priv.shade_vbo_data[batch->vertex_start]); + GFX_GL_CheckError(); + + glEnable(GL_CULL_FACE); + glBindVertexArray(m_Priv.vao); + GFX_GL_CheckError(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture()); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetObjectUVWsTexture()); + GFX_GL_CheckError(); + + if (Output_GetWibbleEffect()) { + Output_Shader_UploadWibbleEffect(m_Shader, false); + glDepthMask(GL_FALSE); + glDrawArrays(GL_TRIANGLES, batch->vertex_start, batch->vertex_count); + glDepthMask(GL_TRUE); + Output_Shader_UploadWibbleEffect(m_Shader, true); + glDrawArrays(GL_TRIANGLES, batch->vertex_start, batch->vertex_count); + } else { + Output_Shader_UploadWibbleEffect(m_Shader, false); + glDrawArrays(GL_TRIANGLES, batch->vertex_start, batch->vertex_count); + } + + glDisable(GL_CULL_FACE); + GFX_GL_CheckError(); +} diff --git a/src/tr1/game/output/rooms.h b/src/tr1/game/output/rooms.h new file mode 100644 index 000000000..d1fc843d2 --- /dev/null +++ b/src/tr1/game/output/rooms.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +void Output_Rooms_Init(void); +void Output_Rooms_Shutdown(void); +void Output_Rooms_ObserveLevelLoad(void); + +void Output_Rooms_RenderRoom( + const MATRIX *matrix, RGB_F tint, const ROOM *room); diff --git a/src/tr1/game/output/shader.c b/src/tr1/game/output/shader.c new file mode 100644 index 000000000..fd14c54dd --- /dev/null +++ b/src/tr1/game/output/shader.c @@ -0,0 +1,177 @@ +#include "game/output/shader.h" + +#include "game/output.h" +#include "game/viewport.h" +#include "global/vars.h" + +#include +#include +#include + +typedef enum { + M_UNIFORM_TIME, + M_UNIFORM_TEX_ATLAS, + M_UNIFORM_TEX_UVW, + M_UNIFORM_SMOOTHING_ENABLED, + M_UNIFORM_ALPHA_DISCARD_ENABLED, + M_UNIFORM_ALPHA_THRESHOLD, + M_UNIFORM_TRAPEZOID_FILTER_ENABLED, + M_UNIFORM_BRIGHTNESS_MULTIPLIER, + M_UNIFORM_GLOBAL_TINT, + M_UNIFORM_FOG, + M_UNIFORM_VIEWPORT_SIZE, + M_UNIFORM_PROJECTION_MATRIX, + M_UNIFORM_MODEL_MATRIX, + M_UNIFORM_WIBBLE_EFFECT, + M_UNIFORM_WATER_EFFECT, + M_UNIFORM_NUMBER_OF, +} M_UNIFORM; + +struct OUTPUT_SHADER { + GFX_GL_PROGRAM program; + GLint uniforms[M_UNIFORM_NUMBER_OF]; +}; + +OUTPUT_SHADER *Output_Shader_Create(const char *const path) +{ + OUTPUT_SHADER *const shader = Memory_Alloc(sizeof(OUTPUT_SHADER)); + + GFX_GL_Program_Init(&shader->program); + GFX_GL_Program_AttachShader( + &shader->program, GL_VERTEX_SHADER, path, + GFX_Context_GetConfig()->backend); + GFX_GL_Program_AttachShader( + &shader->program, GL_FRAGMENT_SHADER, path, + GFX_Context_GetConfig()->backend); + GFX_GL_Program_FragmentData(&shader->program, "outColor"); + GFX_GL_Program_Link(&shader->program); + + const char *const uniform_names[] = { + [M_UNIFORM_TIME] = "uTime", + [M_UNIFORM_TEX_ATLAS] = "uTexAtlas", + [M_UNIFORM_TEX_UVW] = "uUVW", + [M_UNIFORM_SMOOTHING_ENABLED] = "uSmoothingEnabled", + [M_UNIFORM_ALPHA_DISCARD_ENABLED] = "uAlphaDiscardEnabled", + [M_UNIFORM_ALPHA_THRESHOLD] = "uAlphaThreshold", + [M_UNIFORM_TRAPEZOID_FILTER_ENABLED] = "uTrapezoidFilterEnabled", + [M_UNIFORM_BRIGHTNESS_MULTIPLIER] = "uBrightnessMultiplier", + [M_UNIFORM_GLOBAL_TINT] = "uGlobalTint", + [M_UNIFORM_FOG] = "uFog", + [M_UNIFORM_VIEWPORT_SIZE] = "uViewportSize", + [M_UNIFORM_PROJECTION_MATRIX] = "uMatProjection", + [M_UNIFORM_MODEL_MATRIX] = "uMatModelView", + [M_UNIFORM_WIBBLE_EFFECT] = "uWibbleEffect", + [M_UNIFORM_WATER_EFFECT] = "uWaterEffect", + }; + for (int32_t i = 0; i < M_UNIFORM_NUMBER_OF; i++) { + shader->uniforms[i] = + GFX_GL_Program_UniformLocation(&shader->program, uniform_names[i]); + GFX_GL_CheckError(); + } + + GFX_GL_Program_Bind(&shader->program); + glUniform1i(shader->uniforms[M_UNIFORM_TEX_ATLAS], 0); + glUniform1i(shader->uniforms[M_UNIFORM_TEX_UVW], 1); + return shader; +} + +void Output_Shader_Free(OUTPUT_SHADER *const shader) +{ + GFX_GL_Program_Close(&shader->program); + Memory_Free(shader); +} + +void Output_Shader_UploadCommonUniforms(const OUTPUT_SHADER *const shader) +{ + GFX_GL_Program_Bind(&shader->program); + + GFX_TRACK_UNIFORM( + glUniform1f, shader->uniforms[M_UNIFORM_SMOOTHING_ENABLED], + g_Config.rendering.texture_filter); + GFX_TRACK_UNIFORM( + glUniform1f, shader->uniforms[M_UNIFORM_ALPHA_THRESHOLD], + g_Config.rendering.enable_wireframe ? -1.0f : 0.0f); + GFX_TRACK_UNIFORM( + glUniform1i, shader->uniforms[M_UNIFORM_ALPHA_DISCARD_ENABLED], + !g_Config.rendering.enable_wireframe); + GFX_TRACK_UNIFORM( + glUniform1f, shader->uniforms[M_UNIFORM_TRAPEZOID_FILTER_ENABLED], + g_Config.rendering.enable_trapezoid_filter); + GFX_TRACK_UNIFORM( + glUniform1f, shader->uniforms[M_UNIFORM_BRIGHTNESS_MULTIPLIER], + g_Config.visuals.brightness); + GFX_TRACK_UNIFORM( + glUniform2f, shader->uniforms[M_UNIFORM_VIEWPORT_SIZE], + GFX_Context_GetDisplayWidth(), GFX_Context_GetDisplayHeight()); + GFX_TRACK_UNIFORM( + glUniform2f, shader->uniforms[M_UNIFORM_FOG], Output_GetDrawDistFade(), + Output_GetDrawDistMax()); + GFX_TRACK_UNIFORM( + glUniform1i, shader->uniforms[M_UNIFORM_TIME], Output_GetTime()); +} + +void Output_Shader_UploadMatrix( + const OUTPUT_SHADER *const shader, const MATRIX *const source) +{ + GLfloat target[4][4]; + target[0][0] = source->_00 / (float)(1 << W2V_SHIFT); + target[0][1] = source->_01 / (float)(1 << W2V_SHIFT); + target[0][2] = source->_02 / (float)(1 << W2V_SHIFT); + target[0][3] = source->_03 / (float)(1 << W2V_SHIFT); + + target[1][0] = source->_10 / (float)(1 << W2V_SHIFT); + target[1][1] = source->_11 / (float)(1 << W2V_SHIFT); + target[1][2] = source->_12 / (float)(1 << W2V_SHIFT); + target[1][3] = source->_13 / (float)(1 << W2V_SHIFT); + + target[2][0] = source->_20 / (float)(1 << W2V_SHIFT); + target[2][1] = source->_21 / (float)(1 << W2V_SHIFT); + target[2][2] = source->_22 / (float)(1 << W2V_SHIFT); + target[2][3] = source->_23 / (float)(1 << W2V_SHIFT); + + target[3][0] = 0.0; + target[3][1] = 0.0; + target[3][2] = 0.0; + target[3][3] = 1.0; + + GFX_GL_Program_Bind(&shader->program); + GFX_TRACK_UNIFORM( + glUniformMatrix4fv, shader->uniforms[M_UNIFORM_MODEL_MATRIX], 1, + GL_TRUE, &target[0][0]); +} + +void Output_Shader_UploadProjectionMatrix(const OUTPUT_SHADER *const shader) +{ + GFX_GL_Program_Bind(&shader->program); + + GLfloat projection[4][4]; + Output_GetProjectionMatrix(projection); + GFX_TRACK_UNIFORM( + glUniformMatrix4fv, shader->uniforms[M_UNIFORM_PROJECTION_MATRIX], 1, + GL_TRUE, &projection[0][0]); +} + +void Output_Shader_UploadWibbleEffect( + const OUTPUT_SHADER *const shader, const bool is_enabled) +{ + GFX_GL_Program_Bind(&shader->program); + GFX_TRACK_UNIFORM( + glUniform1i, shader->uniforms[M_UNIFORM_WIBBLE_EFFECT], is_enabled); +} + +void Output_Shader_UploadWaterEffect( + const OUTPUT_SHADER *const shader, const bool is_enabled) +{ + GFX_GL_Program_Bind(&shader->program); + GFX_TRACK_UNIFORM( + glUniform1i, shader->uniforms[M_UNIFORM_WATER_EFFECT], is_enabled); +} + +void Output_Shader_UploadTint( + const OUTPUT_SHADER *const shader, const RGB_F tint) +{ + GFX_GL_Program_Bind(&shader->program); + GFX_TRACK_UNIFORM( + glUniform3f, shader->uniforms[M_UNIFORM_GLOBAL_TINT], tint.r, tint.g, + tint.b); +} diff --git a/src/tr1/game/output/shader.h b/src/tr1/game/output/shader.h new file mode 100644 index 000000000..5902d5268 --- /dev/null +++ b/src/tr1/game/output/shader.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +typedef struct OUTPUT_SHADER OUTPUT_SHADER; + +OUTPUT_SHADER *Output_Shader_Create(const char *path); +void Output_Shader_Free(OUTPUT_SHADER *shader); +void Output_Shader_UploadCommonUniforms(const OUTPUT_SHADER *shader); +void Output_Shader_UploadProjectionMatrix(const OUTPUT_SHADER *shader); + +// TODO: these functions are poor design. +void Output_Shader_UploadMatrix( + const OUTPUT_SHADER *shader, const MATRIX *source); +void Output_Shader_UploadWibbleEffect( + const OUTPUT_SHADER *shader, bool is_enabled); +void Output_Shader_UploadWaterEffect( + const OUTPUT_SHADER *shader, bool is_enabled); +void Output_Shader_UploadTint(const OUTPUT_SHADER *shader, RGB_F tint); diff --git a/src/tr1/game/output/sprite_program.c b/src/tr1/game/output/sprite_program.c deleted file mode 100644 index b09363eae..000000000 --- a/src/tr1/game/output/sprite_program.c +++ /dev/null @@ -1,132 +0,0 @@ -#include "game/output/sprite_program.h" - -#include "game/output.h" -#include "game/viewport.h" -#include "global/vars.h" - -#include -#include - -typedef enum { - M_UNIFORM_TEX_ATLAS, - M_UNIFORM_TEX_UVW, - M_UNIFORM_SMOOTHING_ENABLED, - M_UNIFORM_BRIGHTNESS_MULTIPLIER, - M_UNIFORM_GLOBAL_TINT, - M_UNIFORM_VIEWPORT_SIZE, - M_UNIFORM_PROJECTION_MATRIX, - M_UNIFORM_MODEL_MATRIX, - M_UNIFORM_WIBBLE_OFFSET, - M_UNIFORM_NUMBER_OF, -} M_UNIFORM; - -static GFX_GL_PROGRAM m_Program; -static GLint m_Uniforms[M_UNIFORM_NUMBER_OF]; - -void Output_SpriteProgram_Init(void) -{ - GFX_GL_Program_Init(&m_Program); - GFX_GL_Program_AttachShader( - &m_Program, GL_VERTEX_SHADER, "shaders/sprites.glsl", - GFX_Context_GetConfig()->backend); - GFX_GL_Program_AttachShader( - &m_Program, GL_FRAGMENT_SHADER, "shaders/sprites.glsl", - GFX_Context_GetConfig()->backend); - GFX_GL_Program_FragmentData(&m_Program, "outColor"); - GFX_GL_Program_Link(&m_Program); - - const char *const uniform_names[] = { - [M_UNIFORM_TEX_ATLAS] = "uTexture", - [M_UNIFORM_TEX_UVW] = "uUVW", - [M_UNIFORM_SMOOTHING_ENABLED] = "uSmoothingEnabled", - [M_UNIFORM_BRIGHTNESS_MULTIPLIER] = "uBrightnessMultiplier", - [M_UNIFORM_GLOBAL_TINT] = "uGlobalTint", - [M_UNIFORM_VIEWPORT_SIZE] = "uViewportSize", - [M_UNIFORM_PROJECTION_MATRIX] = "uMatProjection", - [M_UNIFORM_MODEL_MATRIX] = "uMatModelView", - [M_UNIFORM_WIBBLE_OFFSET] = "uWibbleOffset", - }; - for (int32_t i = 0; i < M_UNIFORM_NUMBER_OF; i++) { - m_Uniforms[i] = - GFX_GL_Program_UniformLocation(&m_Program, uniform_names[i]); - GFX_GL_CheckError(); - } - - GFX_GL_Program_Bind(&m_Program); - glUniform1i(m_Uniforms[M_UNIFORM_TEX_ATLAS], 0); - glUniform1i(m_Uniforms[M_UNIFORM_TEX_UVW], 1); -} - -void Output_SpriteProgram_Shutdown(void) -{ - GFX_GL_Program_Close(&m_Program); -} - -void Output_SpriteProgram_Bind(void) -{ - GFX_GL_Program_Bind(&m_Program); -} - -void Output_SpriteProgram_UploadCommonUniforms(void) -{ - Output_SpriteProgram_Bind(); - GFX_TRACK_UNIFORM( - glUniform1f, m_Uniforms[M_UNIFORM_SMOOTHING_ENABLED], - g_Config.rendering.texture_filter); - GFX_TRACK_UNIFORM( - glUniform1f, m_Uniforms[M_UNIFORM_BRIGHTNESS_MULTIPLIER], - g_Config.visuals.brightness); - GFX_TRACK_UNIFORM( - glUniform2f, m_Uniforms[M_UNIFORM_VIEWPORT_SIZE], - GFX_Context_GetDisplayWidth(), GFX_Context_GetDisplayHeight()); - GFX_TRACK_UNIFORM( - glUniform1f, m_Uniforms[M_UNIFORM_WIBBLE_OFFSET], - Output_GetWibbleOffset()); -} - -void Output_SpriteProgram_UploadMatrix(const MATRIX *const source) -{ - GLfloat target[4][4]; - target[0][0] = source->_00 / (float)(1 << W2V_SHIFT); - target[0][1] = source->_01 / (float)(1 << W2V_SHIFT); - target[0][2] = source->_02 / (float)(1 << W2V_SHIFT); - target[0][3] = source->_03 / (float)(1 << W2V_SHIFT); - - target[1][0] = source->_10 / (float)(1 << W2V_SHIFT); - target[1][1] = source->_11 / (float)(1 << W2V_SHIFT); - target[1][2] = source->_12 / (float)(1 << W2V_SHIFT); - target[1][3] = source->_13 / (float)(1 << W2V_SHIFT); - - target[2][0] = source->_20 / (float)(1 << W2V_SHIFT); - target[2][1] = source->_21 / (float)(1 << W2V_SHIFT); - target[2][2] = source->_22 / (float)(1 << W2V_SHIFT); - target[2][3] = source->_23 / (float)(1 << W2V_SHIFT); - - target[3][0] = 0.0; - target[3][1] = 0.0; - target[3][2] = 0.0; - target[3][3] = 1.0; - - Output_SpriteProgram_Bind(); - GFX_TRACK_UNIFORM( - glUniformMatrix4fv, m_Uniforms[M_UNIFORM_MODEL_MATRIX], 1, GL_TRUE, - &target[0][0]); -} - -void Output_SpriteProgram_UploadProjectionMatrix(void) -{ - Output_SpriteProgram_Bind(); - - GLfloat projection[4][4]; - Output_GetProjectionMatrix(projection); - GFX_TRACK_UNIFORM( - glUniformMatrix4fv, m_Uniforms[M_UNIFORM_PROJECTION_MATRIX], 1, GL_TRUE, - &projection[0][0]); -} - -void Output_SpriteProgram_UploadTint(const RGB_F tint) -{ - GFX_GL_Program_Bind(&m_Program); - GFX_TRACK_UNIFORM( - glUniform3f, m_Uniforms[M_UNIFORM_GLOBAL_TINT], tint.r, tint.g, tint.b); -} diff --git a/src/tr1/game/output/sprite_program.h b/src/tr1/game/output/sprite_program.h deleted file mode 100644 index d56bf7bc4..000000000 --- a/src/tr1/game/output/sprite_program.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -void Output_SpriteProgram_Init(void); -void Output_SpriteProgram_Shutdown(void); -void Output_SpriteProgram_Bind(void); -void Output_SpriteProgram_UploadCommonUniforms(void); -void Output_SpriteProgram_UploadProjectionMatrix(void); - -// TODO: these functions are poor design. -void Output_SpriteProgram_UploadMatrix(const MATRIX *source); -void Output_SpriteProgram_UploadTint(RGB_F tint); diff --git a/src/tr1/game/output/sprites.c b/src/tr1/game/output/sprites.c index f723b406e..0d7117221 100644 --- a/src/tr1/game/output/sprites.c +++ b/src/tr1/game/output/sprites.c @@ -1,8 +1,9 @@ #include "game/output/sprites.h" #include "game/output.h" -#include "game/output/sprite_program.h" +#include "game/output/shader.h" #include "game/output/textures.h" +#include "game/output/utils.h" #include "game/room.h" #include "specific/s_output.h" @@ -11,8 +12,6 @@ #include #include -#define M_QUAD_VERTICES 6 - #pragma pack(push, 1) typedef struct { // attribute 0 @@ -24,7 +23,7 @@ typedef struct { } displacement; // attribute 2 - int32_t texture_idx; + int32_t uvw_idx; } M_SPRITE_VERTEX; typedef uint16_t M_SPRITE_SHADE; @@ -53,7 +52,7 @@ typedef struct { int32_t quad_count; } M_ROOM_BATCH; -static const int32_t m_QuadToFan[] = { 0, 1, 2, 0, 2, 3 }; +static OUTPUT_SHADER *m_Shader = nullptr; static struct { M_SPRITE_BUFFER sprite_buf; @@ -76,7 +75,7 @@ static void M_MakeQuad( } for (int32_t k = 0; k < 4; k++) { - out_quad[k].texture_idx = sprite_idx * 4 + k; + out_quad[k].uvw_idx = sprite_idx * 4 + k; } out_quad[0].displacement.x = sprite->x0; @@ -118,24 +117,6 @@ static void M_BufferReallocGPU(M_SPRITE_BUFFER *const buffer) buffer->shade_vbo_data, GL_DYNAMIC_DRAW); // shades are always dynamic } -static void M_BufferUploadGPU( - M_SPRITE_BUFFER *const buffer, const int32_t start, const int32_t count) -{ - glBindBuffer(GL_ARRAY_BUFFER, buffer->geom_vbo); - GFX_GL_CheckError(); - GFX_TRACK_DATA( - glBufferSubData, GL_ARRAY_BUFFER, start * sizeof(M_SPRITE_VERTEX), - count * sizeof(M_SPRITE_VERTEX), &buffer->geom_vbo_data[start]); - GFX_GL_CheckError(); - - glBindBuffer(GL_ARRAY_BUFFER, buffer->shade_vbo); - GFX_GL_CheckError(); - GFX_TRACK_DATA( - glBufferSubData, GL_ARRAY_BUFFER, start * sizeof(M_SPRITE_SHADE), - count * sizeof(M_SPRITE_SHADE), &buffer->shade_vbo_data[start]); - GFX_GL_CheckError(); -} - static void M_PrepareBuffer( M_SPRITE_BUFFER *const buffer, const size_t capacity, const GLenum usage) { @@ -168,7 +149,7 @@ static void M_PrepareBuffer( glEnableVertexAttribArray(2); glVertexAttribIPointer( 2, 1, GL_UNSIGNED_INT, sizeof(M_SPRITE_VERTEX), - (void *)(intptr_t)offsetof(M_SPRITE_VERTEX, texture_idx)); + (void *)(intptr_t)offsetof(M_SPRITE_VERTEX, uvw_idx)); glBindBuffer(GL_ARRAY_BUFFER, buffer->shade_vbo); glEnableVertexAttribArray(3); @@ -210,15 +191,13 @@ static void M_DrawBuffer( { glBindVertexArray(buffer->vao); GFX_GL_CheckError(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture()); glActiveTexture(GL_TEXTURE1); - GFX_GL_CheckError(); glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetSpriteUVWsTexture()); GFX_GL_CheckError(); - glActiveTexture(GL_TEXTURE0); - GFX_GL_CheckError(); - glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture()); - GFX_GL_CheckError(); glDrawArrays(GL_TRIANGLES, start, count); GFX_GL_CheckError(); } @@ -240,8 +219,6 @@ static void M_PrepareLevelBatches(void) static void M_UpdateRoomGeometry(const ROOM *const room) { - M_BufferReallocGPU(&m_LevelData.sprite_buf); - int32_t current_vertex = 0; for (int32_t i = 0; i < Room_GetCount(); i++) { const ROOM *const room = Room_Get(i); @@ -254,9 +231,9 @@ static void M_UpdateRoomGeometry(const ROOM *const room) M_SPRITE_VERTEX quad[4]; M_MakeQuad(quad, sprite_idx, pos); - for (int32_t k = 0; k < M_QUAD_VERTICES; k++) { + for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) { m_LevelData.sprite_buf.geom_vbo_data[current_vertex] = - quad[m_QuadToFan[k]]; + quad[OUTPUT_QUAD_TO_FAN(k)]; current_vertex++; } } @@ -266,10 +243,10 @@ static void M_UpdateRoomGeometry(const ROOM *const room) static void M_UpdateRoomShades(const ROOM *const room) { const M_ROOM_BATCH *const room_batch = M_GetRoomBatch(room); - int32_t current_vertex = room_batch->quad_start * M_QUAD_VERTICES; + int32_t current_vertex = room_batch->quad_start * OUTPUT_QUAD_VERTICES; for (int32_t j = 0; j < room->mesh.num_sprites; j++) { const ROOM_SPRITE *const room_sprite = &room->mesh.sprites[j]; - for (int32_t k = 0; k < M_QUAD_VERTICES; k++) { + for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) { m_LevelData.sprite_buf.shade_vbo_data[current_vertex] = room->mesh.vertices[room_sprite->vertex].light_adder; current_vertex++; @@ -296,25 +273,28 @@ static void M_PrepareLevelBuffers(void) } M_PrepareBuffer(&m_Dynamic.sprite_buf, 500, GL_DYNAMIC_DRAW); M_PrepareBuffer( - &m_LevelData.sprite_buf, num_quads * M_QUAD_VERTICES, GL_STATIC_DRAW); + &m_LevelData.sprite_buf, num_quads * OUTPUT_QUAD_VERTICES, + GL_STATIC_DRAW); - m_LevelData.sprite_buf.vertex_count = num_quads * M_QUAD_VERTICES; + m_LevelData.sprite_buf.vertex_count = num_quads * OUTPUT_QUAD_VERTICES; for (int32_t i = 0; i < Room_GetCount(); i++) { const ROOM *const room = Room_Get(i); M_UpdateRoomGeometry(room); } + M_BufferReallocGPU(&m_LevelData.sprite_buf); } void Output_Sprites_Init(void) { - Output_SpriteProgram_Init(); + m_Shader = Output_Shader_Create("shaders/sprites.glsl"); m_Dynamic.source = Vector_CreateAtCapacity(sizeof(M_DYNAMIC_SPRITE), 50); } void Output_Sprites_Shutdown(void) { M_FreeBuffers(); - Output_SpriteProgram_Shutdown(); + Output_Shader_Free(m_Shader); + m_Shader = nullptr; } void Output_Sprites_ObserveLevelLoad(void) @@ -325,7 +305,7 @@ void Output_Sprites_ObserveLevelLoad(void) } void Output_Sprites_RenderRoomSprites( - const MATRIX *matrix, const RGB_F tint, const ROOM *const room) + const MATRIX *const matrix, const RGB_F tint, const ROOM *const room) { M_UpdateRoomShades(room); @@ -334,20 +314,21 @@ void Output_Sprites_RenderRoomSprites( GFX_GL_CheckError(); GFX_TRACK_SUBDATA( glBufferSubData, GL_ARRAY_BUFFER, - room_batch->quad_start * M_QUAD_VERTICES * sizeof(M_SPRITE_SHADE), - room_batch->quad_count * M_QUAD_VERTICES * sizeof(M_SPRITE_SHADE), + room_batch->quad_start * OUTPUT_QUAD_VERTICES * sizeof(M_SPRITE_SHADE), + room_batch->quad_count * OUTPUT_QUAD_VERTICES * sizeof(M_SPRITE_SHADE), &m_LevelData.sprite_buf - .shade_vbo_data[room_batch->quad_start * M_QUAD_VERTICES]); + .shade_vbo_data[room_batch->quad_start * OUTPUT_QUAD_VERTICES]); GFX_GL_CheckError(); - Output_SpriteProgram_UploadMatrix(matrix); - Output_SpriteProgram_UploadTint(tint); + Output_Shader_UploadWibbleEffect(m_Shader, Output_GetWibbleEffect()); + Output_Shader_UploadMatrix(m_Shader, matrix); + Output_Shader_UploadTint(m_Shader, tint); GFX_GL_CheckError(); const M_ROOM_BATCH *const batch = M_GetRoomBatch(room); M_DrawBuffer( - &m_LevelData.sprite_buf, batch->quad_start * M_QUAD_VERTICES, - batch->quad_count * M_QUAD_VERTICES); + &m_LevelData.sprite_buf, batch->quad_start * OUTPUT_QUAD_VERTICES, + batch->quad_count * OUTPUT_QUAD_VERTICES); } void Output_Sprites_RenderSingleSprite( @@ -365,8 +346,8 @@ void Output_Sprites_RenderSingleSprite( void Output_Sprites_RenderBegin(void) { - Output_SpriteProgram_UploadCommonUniforms(); - Output_SpriteProgram_UploadProjectionMatrix(); + Output_Shader_UploadCommonUniforms(m_Shader); + Output_Shader_UploadProjectionMatrix(m_Shader); } bool Output_Sprites_Flush(void) @@ -376,10 +357,10 @@ bool Output_Sprites_Flush(void) } M_SPRITE_BUFFER *const buffer = &m_Dynamic.sprite_buf; - if ((size_t)m_Dynamic.source->count * M_QUAD_VERTICES + if ((size_t)m_Dynamic.source->count * OUTPUT_QUAD_VERTICES > buffer->vertex_capacity) { buffer->vertex_capacity = - (m_Dynamic.source->count + 50) * M_QUAD_VERTICES; + (m_Dynamic.source->count + 50) * OUTPUT_QUAD_VERTICES; buffer->geom_vbo_data = Memory_Realloc( buffer->geom_vbo_data, buffer->vertex_capacity * sizeof(M_SPRITE_VERTEX)); @@ -394,20 +375,23 @@ bool Output_Sprites_Flush(void) M_MakeQuad( quad, sprite->sprite_idx, (XYZ_16) { sprite->pos.x, sprite->pos.y, sprite->pos.z }); - for (int32_t i = 0; i < M_QUAD_VERTICES; i++) { - buffer->geom_vbo_data[buffer->vertex_count] = quad[m_QuadToFan[i]]; + for (int32_t i = 0; i < OUTPUT_QUAD_VERTICES; i++) { + buffer->geom_vbo_data[buffer->vertex_count] = + quad[OUTPUT_QUAD_TO_FAN(i)]; buffer->shade_vbo_data[buffer->vertex_count] = sprite->shade; buffer->vertex_count++; } } M_BufferReallocGPU(buffer); - Output_SpriteProgram_UploadTint((RGB_F) { 1.0f, 1.0f, 1.0f }); + Output_Shader_UploadWibbleEffect(m_Shader, Output_GetWibbleEffect()); + Output_Shader_UploadTint(m_Shader, (RGB_F) { 1.0f, 1.0f, 1.0f }); for (int32_t i = 0; i < m_Dynamic.source->count; i++) { const M_DYNAMIC_SPRITE *const sprite = Vector_Get(m_Dynamic.source, i); - Output_SpriteProgram_UploadMatrix(&sprite->matrix); + Output_Shader_UploadMatrix(m_Shader, &sprite->matrix); M_DrawBuffer( - &m_Dynamic.sprite_buf, i * M_QUAD_VERTICES, M_QUAD_VERTICES); + &m_Dynamic.sprite_buf, i * OUTPUT_QUAD_VERTICES, + OUTPUT_QUAD_VERTICES); } Vector_Clear(m_Dynamic.source); diff --git a/src/tr1/game/output/textures.c b/src/tr1/game/output/textures.c index a20d92d01..ee6755d1d 100644 --- a/src/tr1/game/output/textures.c +++ b/src/tr1/game/output/textures.c @@ -41,18 +41,19 @@ static struct { } m_AnimationRanges; static struct { - GLuint tex; // 3D texture to hold atlas pages + GLuint tex_atlas; + M_TEXTURE_DATA objects; M_TEXTURE_DATA sprites; -} m_LevelData = {}; +} m_Priv = {}; -int M_CompareAnimationRange(const void *const a, const void *const b) +static int M_CompareAnimationRange(const void *const a, const void *const b) { const M_ANIMATION_RANGE *const range_a = (M_ANIMATION_RANGE *)a; const M_ANIMATION_RANGE *const range_b = (M_ANIMATION_RANGE *)b; return range_a->index - range_b->index; } -void M_MergeAndGlueAnimationRanges(M_ANIMATION_RANGES *const source) +static void M_MergeAndGlueAnimationRanges(M_ANIMATION_RANGES *const source) { ASSERT(source != nullptr); if (source->range_count == 0) { @@ -156,29 +157,27 @@ static void M_PrepareAnimationRanges(void) M_PrepareSpriteAnimationRanges(); } -void Output_Textures_Init(void) +static void M_FreeTextureData(M_TEXTURE_DATA *const data) { + if (data->tbo != 0) { + glDeleteBuffers(1, &data->tbo); + data->tbo = 0; + } + if (data->tex != 0) { + glDeleteTextures(1, &data->tex); + data->tex = 0; + } } -void Output_Textures_Shutdown(void) +static void M_FillObjectUVW(const int32_t i) { - glBindTexture(GL_TEXTURE_BUFFER, 0); - glBindBuffer(GL_TEXTURE_BUFFER, 0); - if (m_LevelData.sprites.tbo != 0) { - glDeleteBuffers(1, &m_LevelData.sprites.tbo); - m_LevelData.sprites.tbo = 0; + const OBJECT_TEXTURE *const texture = Output_GetObjectTexture(i); + M_UVW *const corners = m_Priv.objects.uvw[i].corners; + for (int32_t j = 0; j < 4; j++) { + corners[j].u = Output_AdjustUV(texture->uv[j].u); + corners[j].v = Output_AdjustUV(texture->uv[j].v); + corners[j].w = texture->tex_page; } - if (m_LevelData.sprites.tex != 0) { - glDeleteTextures(1, &m_LevelData.sprites.tex); - m_LevelData.sprites.tex = 0; - } - if (m_LevelData.tex != 0) { - glDeleteTextures(1, &m_LevelData.tex); - m_LevelData.tex = 0; - } - - Memory_FreePointer(&m_AnimationRanges.objects.ranges); - Memory_FreePointer(&m_AnimationRanges.sprites.ranges); } static void M_FillSpriteUVW(const int32_t i) @@ -189,7 +188,7 @@ static void M_FillSpriteUVW(const int32_t i) const float v0 = (sprite->offset >> 8) / 256.0f + adj; const float u1 = u0 + (sprite->width >> 8) / 256.0f - 2 * adj; const float v1 = v0 + (sprite->height >> 8) / 256.0f - 2 * adj; - M_UVW *corners = m_LevelData.sprites.uvw[i].corners; + M_UVW *const corners = m_Priv.sprites.uvw[i].corners; // clang-format off corners[0].u = u0; corners[0].v = v0; corners[0].w = sprite->tex_page; corners[1].u = u1; corners[1].v = v0; corners[1].w = sprite->tex_page; @@ -198,6 +197,13 @@ static void M_FillSpriteUVW(const int32_t i) // clang-format on } +static void M_FillObjectUVWs(void) +{ + for (int32_t i = 0; i < Output_GetObjectTextureCount(); i++) { + M_FillObjectUVW(i); + } +} + static void M_FillSpriteUVWs(void) { for (int32_t i = 0; i < Output_GetSpriteTextureCount(); i++) { @@ -205,18 +211,44 @@ static void M_FillSpriteUVWs(void) } } +static void M_UploadTextureData(const M_TEXTURE_DATA *const data) +{ + glBindBuffer(GL_TEXTURE_BUFFER, data->tbo); + GFX_TRACK_DATA( + glBufferData, GL_TEXTURE_BUFFER, data->count * sizeof(M_UVW_PACK), + data->uvw, GL_DYNAMIC_DRAW); + GFX_GL_CheckError(); +} + +static void M_UploadObjectUVWs(void) +{ + M_UploadTextureData(&m_Priv.objects); +} + static void M_UploadSpriteUVWs(void) { - glBindBuffer(GL_TEXTURE_BUFFER, m_LevelData.sprites.tbo); - GFX_TRACK_DATA( - glBufferData, GL_TEXTURE_BUFFER, - m_LevelData.sprites.count * sizeof(M_UVW_PACK), m_LevelData.sprites.uvw, - GL_DYNAMIC_DRAW); + M_UploadTextureData(&m_Priv.sprites); +} + +static void M_UploadObjectAnimatedUVWs(const M_ANIMATION_RANGES *const source) +{ + glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.objects.tbo); + for (int32_t i = 0; i < source->range_count; i++) { + const M_ANIMATION_RANGE *const range = &source->ranges[i]; + for (int32_t j = 0; j < range->count; j++) { + M_FillObjectUVW(range->index + j); + } + GFX_TRACK_DATA( + glBufferSubData, GL_TEXTURE_BUFFER, + range->index * sizeof(M_UVW_PACK), + range->count * sizeof(M_UVW_PACK), + m_Priv.objects.uvw + range->index); + } } static void M_UploadSpriteAnimatedUVWs(const M_ANIMATION_RANGES *const source) { - glBindBuffer(GL_TEXTURE_BUFFER, m_LevelData.sprites.tbo); + glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.sprites.tbo); for (int32_t i = 0; i < source->range_count; i++) { const M_ANIMATION_RANGE *const range = &source->ranges[i]; for (int32_t j = 0; j < range->count; j++) { @@ -226,44 +258,56 @@ static void M_UploadSpriteAnimatedUVWs(const M_ANIMATION_RANGES *const source) glBufferSubData, GL_TEXTURE_BUFFER, range->index * sizeof(M_UVW_PACK), range->count * sizeof(M_UVW_PACK), - m_LevelData.sprites.uvw + range->index); + m_Priv.sprites.uvw + range->index); } } +static void M_PrepareTextureData(M_TEXTURE_DATA *const data, const size_t count) +{ + data->count = count; + data->uvw = Memory_Alloc(data->count * sizeof(M_UVW_PACK)); + + glGenBuffers(1, &data->tbo); + glBindBuffer(GL_TEXTURE_BUFFER, data->tbo); + + GLint limit; + glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &limit); + ASSERT(data->count * sizeof(M_UVW_PACK) <= (size_t)limit); + + glGenTextures(1, &data->tex); + glBindTexture(GL_TEXTURE_BUFFER, data->tex); + glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, data->tbo); + GFX_GL_CheckError(); +} + +static void M_PrepareObjectUVWs(void) +{ + M_PrepareTextureData(&m_Priv.objects, Output_GetObjectTextureCount()); + M_FillObjectUVWs(); +} + static void M_PrepareSpriteUVWs(void) { - m_LevelData.sprites.count = Output_GetSpriteTextureCount(); - m_LevelData.sprites.uvw = - Memory_Alloc(m_LevelData.sprites.count * sizeof(M_UVW_PACK)); + M_PrepareTextureData(&m_Priv.sprites, Output_GetSpriteTextureCount()); M_FillSpriteUVWs(); - - glGenBuffers(1, &m_LevelData.sprites.tbo); - glBindBuffer(GL_TEXTURE_BUFFER, m_LevelData.sprites.tbo); - - GLint limit; - glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &limit); - ASSERT(m_LevelData.sprites.count * sizeof(M_UVW_PACK) <= (size_t)limit); - - glGenTextures(1, &m_LevelData.sprites.tex); - glBindTexture(GL_TEXTURE_BUFFER, m_LevelData.sprites.tex); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, m_LevelData.sprites.tbo); } static void M_UploadAtlas(void) { - glGenTextures(1, &m_LevelData.tex); - glBindTexture(GL_TEXTURE_2D_ARRAY, m_LevelData.tex); + glGenTextures(1, &m_Priv.tex_atlas); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_Priv.tex_atlas); glTexStorage3D( GL_TEXTURE_2D_ARRAY, 1, // number of mipmaps GL_RGBA8, TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT, Output_GetTexturePageCount()); + GFX_GL_CheckError(); - // TODO: handle bilinear toggle glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GFX_GL_CheckError(); for (int32_t i = 0; i < Output_GetTexturePageCount(); i++) { const RGBA_8888 *const input_ptr = Output_GetTexturePage32(i); @@ -278,31 +322,73 @@ static void M_UploadAtlas(void) 1, // depth GL_RGBA, GL_UNSIGNED_BYTE, input_ptr); } + GFX_GL_CheckError(); +} + +static void M_FreeLevelData(void) +{ + glBindTexture(GL_TEXTURE_BUFFER, 0); + glBindBuffer(GL_TEXTURE_BUFFER, 0); + M_FreeTextureData(&m_Priv.objects); + M_FreeTextureData(&m_Priv.sprites); + if (m_Priv.tex_atlas != 0) { + glDeleteTextures(1, &m_Priv.tex_atlas); + m_Priv.tex_atlas = 0; + } + Memory_FreePointer(&m_AnimationRanges.objects.ranges); + Memory_FreePointer(&m_AnimationRanges.sprites.ranges); +} + +void Output_Textures_Init(void) +{ +} + +void Output_Textures_Shutdown(void) +{ + M_FreeLevelData(); } void Output_Textures_ObserveLevelLoad(void) { - Output_Textures_Shutdown(); + M_FreeLevelData(); M_PrepareAnimationRanges(); + M_PrepareObjectUVWs(); M_PrepareSpriteUVWs(); + M_UploadObjectUVWs(); M_UploadSpriteUVWs(); M_UploadAtlas(); } -void Output_Textures_Update(void) +void Output_Textures_CycleAnimations(void) { - if (m_LevelData.sprites.tex == 0) { - return; + if (m_Priv.sprites.tex != 0) { + M_UploadSpriteAnimatedUVWs(&m_AnimationRanges.sprites); } - M_UploadSpriteAnimatedUVWs(&m_AnimationRanges.sprites); + if (m_Priv.objects.tex != 0) { + M_UploadObjectAnimatedUVWs(&m_AnimationRanges.objects); + } +} + +GLuint Output_Textures_GetObjectUVWsTexture(void) +{ + return m_Priv.objects.tex; } GLuint Output_Textures_GetSpriteUVWsTexture(void) { - return m_LevelData.sprites.tex; + return m_Priv.sprites.tex; } GLuint Output_Textures_GetAtlasTexture(void) { - return m_LevelData.tex; + return m_Priv.tex_atlas; +} + +void Output_Textures_ApplyRenderSettings(void) +{ + // re-adjust UVs when the bilinear filter is toggled. + if (m_Priv.objects.tex != 0) { + M_FillObjectUVWs(); + M_UploadObjectUVWs(); + } } diff --git a/src/tr1/game/output/textures.h b/src/tr1/game/output/textures.h index 1aa644bd6..641722828 100644 --- a/src/tr1/game/output/textures.h +++ b/src/tr1/game/output/textures.h @@ -5,6 +5,8 @@ void Output_Textures_Init(void); void Output_Textures_Shutdown(void); void Output_Textures_ObserveLevelLoad(void); -void Output_Textures_Update(void); +void Output_Textures_CycleAnimations(void); +void Output_Textures_ApplyRenderSettings(void); +GLuint Output_Textures_GetObjectUVWsTexture(void); GLuint Output_Textures_GetSpriteUVWsTexture(void); GLuint Output_Textures_GetAtlasTexture(void); diff --git a/src/tr1/game/output/utils.h b/src/tr1/game/output/utils.h new file mode 100644 index 000000000..790208024 --- /dev/null +++ b/src/tr1/game/output/utils.h @@ -0,0 +1,5 @@ +#define OUTPUT_QUAD_VERTICES 6 +#define OUTPUT_TRI_VERTICES 3 + +#define OUTPUT_QUAD_TO_FAN(i) ((int32_t[]) { 0, 2, 1, 0, 3, 2 }[i]) +#define OUTPUT_TRI_TO_FAN(i) ((int32_t[]) { 0, 2, 1 }[i]) diff --git a/src/tr1/game/overlay.c b/src/tr1/game/overlay.c index aa7f8b54a..909fc411f 100644 --- a/src/tr1/game/overlay.c +++ b/src/tr1/game/overlay.c @@ -7,6 +7,8 @@ #include "game/inventory.h" #include "game/items.h" #include "game/output.h" +#include "game/output/meshes.h" +#include "game/output/shader.h" #include "game/screen.h" #include "game/text.h" #include "game/viewport.h" diff --git a/src/tr1/game/room_draw.c b/src/tr1/game/room_draw.c index f3a692c2c..215b9f808 100644 --- a/src/tr1/game/room_draw.c +++ b/src/tr1/game/room_draw.c @@ -280,7 +280,6 @@ void Room_DrawSingleRoom(int16_t room_num) g_PhdTop = room->bound_top; g_PhdBottom = room->bound_bottom; - Output_LightRoom(room); Output_DrawRoomMesh(room); int16_t item_num = room->item_num; diff --git a/src/tr1/meson.build b/src/tr1/meson.build index f7a688658..f48a5fc80 100644 --- a/src/tr1/meson.build +++ b/src/tr1/meson.build @@ -244,8 +244,10 @@ sources = [ 'game/option/option_passport.c', 'game/option/option_sound.c', 'game/output.c', + 'game/output/meshes.c', + 'game/output/rooms.c', + 'game/output/shader.c', 'game/output/sprites.c', - 'game/output/sprite_program.c', 'game/output/textures.c', 'game/overlay.c', 'game/requester.c', diff --git a/src/tr1/specific/s_output.c b/src/tr1/specific/s_output.c index f91e35a3d..acc484931 100644 --- a/src/tr1/specific/s_output.c +++ b/src/tr1/specific/s_output.c @@ -1192,3 +1192,8 @@ void S_Output_2ToneColourTextBox( S_Output_DisableTextureMode(); M_DrawTriangleStrip(vertices, 18); } + +float Output_AdjustUV(const uint16_t uv) +{ + return M_GetUV(uv); +}