mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 12:47:58 +03:00
tr1/output: rewrite room drawing
This commit is contained in:
parent
01e18f2cf0
commit
273a63bafe
26 changed files with 992 additions and 418 deletions
|
@ -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
|
||||
|
|
91
data/tr1/ship/shaders/meshes.glsl
Normal file
91
data/tr1/ship/shaders/meshes.glsl
Normal file
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 <libtrx/engine/image.h>
|
||||
#include <libtrx/filesystem.h>
|
||||
#include <libtrx/game/game_buf.h>
|
||||
#include <libtrx/game/math.h>
|
||||
#include <libtrx/game/matrix.h>
|
||||
#include <libtrx/gfx/context.h>
|
||||
#include <libtrx/game/output.h>
|
||||
#include <libtrx/memory.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
25
src/tr1/game/output/meshes.c
Normal file
25
src/tr1/game/output/meshes.c
Normal file
|
@ -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;
|
||||
}
|
12
src/tr1/game/output/meshes.h
Normal file
12
src/tr1/game/output/meshes.h
Normal file
|
@ -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);
|
327
src/tr1/game/output/rooms.c
Normal file
327
src/tr1/game/output/rooms.c
Normal file
|
@ -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 <libtrx/gfx/gl/utils.h>
|
||||
#include <libtrx/memory.h>
|
||||
|
||||
#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();
|
||||
}
|
12
src/tr1/game/output/rooms.h
Normal file
12
src/tr1/game/output/rooms.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <libtrx/game/matrix.h>
|
||||
#include <libtrx/game/output/types.h>
|
||||
#include <libtrx/game/rooms/types.h>
|
||||
|
||||
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);
|
177
src/tr1/game/output/shader.c
Normal file
177
src/tr1/game/output/shader.c
Normal file
|
@ -0,0 +1,177 @@
|
|||
#include "game/output/shader.h"
|
||||
|
||||
#include "game/output.h"
|
||||
#include "game/viewport.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/config.h>
|
||||
#include <libtrx/gfx/gl/utils.h>
|
||||
#include <libtrx/memory.h>
|
||||
|
||||
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);
|
||||
}
|
20
src/tr1/game/output/shader.h
Normal file
20
src/tr1/game/output/shader.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <libtrx/game/matrix.h>
|
||||
#include <libtrx/game/output/types.h>
|
||||
|
||||
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);
|
|
@ -1,132 +0,0 @@
|
|||
#include "game/output/sprite_program.h"
|
||||
|
||||
#include "game/output.h"
|
||||
#include "game/viewport.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/config.h>
|
||||
#include <libtrx/gfx/gl/utils.h>
|
||||
|
||||
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);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <libtrx/game/matrix.h>
|
||||
#include <libtrx/game/output/types.h>
|
||||
|
||||
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);
|
|
@ -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 <libtrx/memory.h>
|
||||
#include <libtrx/vector.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
5
src/tr1/game/output/utils.h
Normal file
5
src/tr1/game/output/utils.h
Normal file
|
@ -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])
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue