tr1/output: optimize uvw access
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / Build TR1 (push) Has been skipped
Publish a pre-release / Build TR2 (push) Has been skipped
Publish a pre-release / Create a prerelease (push) Has been skipped

This commit is contained in:
Marcin Kurczewski 2025-04-10 18:07:11 +02:00
parent 0df70ce4bf
commit 2aa0f2e161
25 changed files with 753 additions and 393 deletions

View file

@ -1,8 +1,6 @@
#ifdef VERTEX #ifdef VERTEX
uniform int uTime; uniform int uTime;
uniform samplerBuffer uUVW; // texture u, v, layer
uniform samplerBuffer uAtlasSizes; // texture x, y, w, h
uniform vec2 uViewportSize; uniform vec2 uViewportSize;
uniform mat4 uMatProjection; uniform mat4 uMatProjection;
uniform mat4 uMatModelView; uniform mat4 uMatModelView;
@ -11,11 +9,12 @@ uniform bool uWibbleEffect;
layout(location = 0) in vec3 inPosition; layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal; layout(location = 1) in vec3 inNormal;
layout(location = 2) in int inUVWIdx; layout(location = 2) in vec3 inUVW;
layout(location = 3) in vec2 inTrapezoidRatios; layout(location = 3) in vec4 inTextureSize;
layout(location = 4) in int inFlags; layout(location = 4) in vec2 inTrapezoidRatios;
layout(location = 5) in vec4 inColor; layout(location = 5) in int inFlags;
layout(location = 6) in float inShade; layout(location = 6) in vec4 inColor;
layout(location = 7) in float inShade;
out vec4 gWorldPos; out vec4 gWorldPos;
out vec3 gNormal; out vec3 gNormal;
@ -37,11 +36,10 @@ void main(void) {
waterWibble(gl_Position, uViewportSize, uTime); waterWibble(gl_Position, uViewportSize, uTime);
} }
vec3 uvw = texelFetch(uUVW, int(inUVWIdx)).xyz;
gAtlasSize = texelFetch(uAtlasSizes, int(inUVWIdx / 4));
gFlags = inFlags; gFlags = inFlags;
gTexLayer = int(uvw.z); gAtlasSize = inTextureSize;
gTexUV = uvw.xy; gTexUV = inUVW.xy;
gTexLayer = int(inUVW.z);
gTrapezoidRatios = inTrapezoidRatios; gTrapezoidRatios = inTrapezoidRatios;
if (uTrapezoidFilterEnabled) { if (uTrapezoidFilterEnabled) {
gTexUV *= inTrapezoidRatios; gTexUV *= inTrapezoidRatios;

View file

@ -9,7 +9,7 @@ uniform int uTime;
layout(location = 0) in vec3 inPosition; layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec2 inDisplacement; layout(location = 1) in vec2 inDisplacement;
layout(location = 2) in int inUVWIdx; layout(location = 2) in vec3 inUVW;
layout(location = 3) in float inShade; layout(location = 3) in float inShade;
out vec4 gWorldPos; out vec4 gWorldPos;
@ -28,9 +28,8 @@ void main(void) {
waterWibble(gl_Position, uViewportSize, uTime); waterWibble(gl_Position, uViewportSize, uTime);
} }
vec3 uvw = texelFetch(uUVW, int(inUVWIdx)).xyz; gTexUV = inUVW.xy;
gTexUV = uvw.xy; gTexLayer = int(inUVW.z);
gTexLayer = int(uvw.z);
gShade = inShade; gShade = inShade;
} }

View file

@ -7,6 +7,7 @@
#include "game/items.h" #include "game/items.h"
#include "game/objects/common.h" #include "game/objects/common.h"
#include "game/objects/traps/movable_block.h" #include "game/objects/traps/movable_block.h"
#include "game/output.h"
#include "game/rooms/const.h" #include "game/rooms/const.h"
#include "game/rooms/enum.h" #include "game/rooms/enum.h"
#include "game/sound/common.h" #include "game/sound/common.h"
@ -294,6 +295,8 @@ void Room_FlipMap(void)
room->effect_num = flipped->effect_num; room->effect_num = flipped->effect_num;
M_AddFlipItems(room); M_AddFlipItems(room);
Output_ObserveRoomFlip(flipped);
Output_ObserveRoomFlip(room);
} }
MovableBlock_HandleFlipMap(RFS_FLIPPED); MovableBlock_HandleFlipMap(RFS_FLIPPED);

View file

@ -4,6 +4,7 @@
extern void Output_ObserveLevelLoad(void); extern void Output_ObserveLevelLoad(void);
extern void Output_ObserveLevelUnload(void); extern void Output_ObserveLevelUnload(void);
extern void Output_ObserveRoomFlip(const ROOM *room);
extern bool Output_MakeScreenshot(const char *path); extern bool Output_MakeScreenshot(const char *path);
extern void Output_BeginScene(void); extern void Output_BeginScene(void);

View file

@ -15,6 +15,7 @@ typedef struct {
VECTOR *Vector_Create(size_t item_size); VECTOR *Vector_Create(size_t item_size);
VECTOR *Vector_CreateAtCapacity(size_t item_size, int32_t capacity); VECTOR *Vector_CreateAtCapacity(size_t item_size, int32_t capacity);
void Vector_EnsureCapacity(VECTOR *vector, int32_t capacity);
void Vector_Free(VECTOR *vector); void Vector_Free(VECTOR *vector);
int32_t Vector_IndexOf(const VECTOR *vector, const void *item); int32_t Vector_IndexOf(const VECTOR *vector, const void *item);
@ -22,6 +23,7 @@ int32_t Vector_LastIndexOf(const VECTOR *vector, const void *item);
bool Vector_Contains(const VECTOR *vector, const void *item); bool Vector_Contains(const VECTOR *vector, const void *item);
void *Vector_Get(VECTOR *vector, int32_t index); void *Vector_Get(VECTOR *vector, int32_t index);
void *Vector_GetData(VECTOR *vector);
void Vector_Add(VECTOR *vector, const void *item); void Vector_Add(VECTOR *vector, const void *item);
void Vector_Insert(VECTOR *vector, int32_t index, const void *item); void Vector_Insert(VECTOR *vector, int32_t index, const void *item);
void Vector_Swap(VECTOR *vector, int32_t index1, int32_t index2); void Vector_Swap(VECTOR *vector, int32_t index1, int32_t index2);

View file

@ -42,6 +42,16 @@ VECTOR *Vector_CreateAtCapacity(const size_t item_size, const int32_t capacity)
return vector; return vector;
} }
void Vector_EnsureCapacity(VECTOR *const vector, const int32_t capacity)
{
if (vector->capacity >= capacity) {
return;
}
vector->capacity = capacity;
P(vector).items =
Memory_Realloc(P(vector).items, vector->item_size * capacity);
}
void Vector_Free(VECTOR *vector) void Vector_Free(VECTOR *vector)
{ {
Memory_FreePointer(&P(vector).items); Memory_FreePointer(&P(vector).items);
@ -86,6 +96,11 @@ void *Vector_Get(VECTOR *const vector, const int32_t index)
return (void *)(items + index * vector->item_size); return (void *)(items + index * vector->item_size);
} }
void *Vector_GetData(VECTOR *const vector)
{
return P(vector).items;
}
void Vector_Add(VECTOR *const vector, const void *const item) void Vector_Add(VECTOR *const vector, const void *const item)
{ {
M_EnsureCapacity(vector, 1); M_EnsureCapacity(vector, 1);

View file

@ -2,6 +2,7 @@
#include "game/level.h" #include "game/level.h"
#include "game/output/meshes/common.h" #include "game/output/meshes/common.h"
#include "game/output/meshes/rooms.h"
#include "game/output/sprites.h" #include "game/output/sprites.h"
#include "game/output/textures.h" #include "game/output/textures.h"
#include "game/overlay.h" #include "game/overlay.h"
@ -440,6 +441,11 @@ void Output_ObserveLevelUnload(void)
Output_Meshes_ObserveLevelUnload(); Output_Meshes_ObserveLevelUnload();
} }
void Output_ObserveRoomFlip(const ROOM *room)
{
Output_Meshes_ObserveRoomFlip(room);
}
void Output_FlushTranslucentObjects(void) void Output_FlushTranslucentObjects(void)
{ {
Output_RememberState(); Output_RememberState();

View file

@ -158,7 +158,6 @@ void Output_DrawRoomPortals(const ROOM *const room)
} }
} }
for (int32_t i = 0; i < vertex_count; i++) { for (int32_t i = 0; i < vertex_count; i++) {
vertices[i].uvw_idx = -1;
vertices[i].flags = vertices[i].flags =
VERT_FLAT_SHADED | VERT_NO_LIGHTING | VERT_NO_CAUSTICS; VERT_FLAT_SHADED | VERT_NO_LIGHTING | VERT_NO_CAUSTICS;
vertices[i].color = color; vertices[i].color = color;

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "game/output/shader.h" #include "game/output/shader.h"
#include "game/output/textures.h"
// clang-format off // clang-format off
#define VERT_NO_CAUSTICS 0b0000'0001 // = 0x01 #define VERT_NO_CAUSTICS 0b0000'0001 // = 0x01
@ -9,6 +10,15 @@
#define VERT_NO_LIGHTING 0b0000'1000 // = 0x08 #define VERT_NO_LIGHTING 0b0000'1000 // = 0x08
// clang-format on // clang-format on
typedef struct {
// attribute 2
OUTPUT_UVW uvw;
// attribute 3
OUTPUT_TEXTURE_SIZE texture_size;
// attribute 4
float trapezoid_ratio[2];
} OUTPUT_MESH_TEXTURE;
void Output_Meshes_Init(void); void Output_Meshes_Init(void);
void Output_Meshes_Shutdown(void); void Output_Meshes_Shutdown(void);
void Output_Meshes_UploadProjectionMatrix(void); void Output_Meshes_UploadProjectionMatrix(void);

View file

@ -34,28 +34,33 @@ void Output_Meshes_InitDynamic(void)
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, normal)); (void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, normal));
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glVertexAttribIPointer( glVertexAttribPointer(
2, 1, GL_UNSIGNED_INT, sizeof(OUTPUT_MESH_VERTEX), 2, 3, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, uvw_idx)); (void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, uvw));
glEnableVertexAttribArray(3); glEnableVertexAttribArray(3);
glVertexAttribPointer( glVertexAttribPointer(
3, 2, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX), 3, 4, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, trapezoid_ratio)); (void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, texture_size));
glEnableVertexAttribArray(4); glEnableVertexAttribArray(4);
glVertexAttribIPointer( glVertexAttribPointer(
4, 1, GL_UNSIGNED_SHORT, sizeof(OUTPUT_MESH_VERTEX), 4, 2, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, flags)); (void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, trapezoid_ratio));
glEnableVertexAttribArray(5); glEnableVertexAttribArray(5);
glVertexAttribPointer( glVertexAttribIPointer(
5, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(OUTPUT_MESH_VERTEX), 5, 1, GL_UNSIGNED_SHORT, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, color)); (void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, flags));
glEnableVertexAttribArray(6); glEnableVertexAttribArray(6);
glVertexAttribPointer( glVertexAttribPointer(
6, 1, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX), 6, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, color));
glEnableVertexAttribArray(7);
glVertexAttribPointer(
7, 1, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, shade)); (void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, shade));
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "game/output/shader.h" #include "game/output/shader.h"
#include "game/output/textures.h"
#include <libtrx/game/math/types.h> #include <libtrx/game/math/types.h>
#include <libtrx/game/output/types.h> #include <libtrx/game/output/types.h>
@ -9,13 +10,14 @@
#pragma pack(push, 1) #pragma pack(push, 1)
typedef struct { typedef struct {
// clang-format off // clang-format off
XYZ_F pos; // attribute 0 XYZ_F pos; // attribute 0
XYZ_F normal; // attribute 1 XYZ_F normal; // attribute 1
int32_t uvw_idx; // attribute 2 OUTPUT_UVW uvw; // attribute 2
float trapezoid_ratio[2]; // attribute 3 OUTPUT_TEXTURE_SIZE texture_size; // attribute 3
uint16_t flags; // attribute 4 float trapezoid_ratio[2]; // attribute 4
RGBA_8888 color; // attribute 5 uint16_t flags; // attribute 5
int16_t shade; // attribute 6 RGBA_8888 color; // attribute 6
int16_t shade; // attribute 7
// clang-format on // clang-format on
} OUTPUT_MESH_VERTEX; } OUTPUT_MESH_VERTEX;
#pragma pack(pop) #pragma pack(pop)

View file

@ -4,38 +4,45 @@
#include "game/output/meshes/common.h" #include "game/output/meshes/common.h"
#include "game/output/textures.h" #include "game/output/textures.h"
#include "game/output/utils.h" #include "game/output/utils.h"
#include "game/output/vertex_range.h"
#include <libtrx/debug.h> #include <libtrx/debug.h>
#include <libtrx/game/output/objects.h> #include <libtrx/game/output/objects.h>
#include <libtrx/gfx/gl/utils.h> #include <libtrx/gfx/gl/utils.h>
#include <libtrx/memory.h> #include <libtrx/memory.h>
#include <libtrx/vector.h>
#pragma pack(push, 1) #pragma pack(push, 1)
typedef struct { typedef struct {
// clang-format off // attribute 0
XYZ_F pos; // attribute 0 XYZ_F pos;
XYZ_F normal; // attribute 1 // attribute 1
int32_t uvw_idx; // attribute 2 XYZ_F normal;
float trapezoid_ratio[2]; // attribute 3 // attribute 5
uint16_t flags; // attribute 4 uint16_t flags;
RGBA_8888 color; // attribute 5 // attribute 6
RGBA_8888 color;
// clang-format on // clang-format on
} M_MESH_VERTEX; } M_MESH_VERTEX;
// attribute 7
typedef int16_t M_MESH_SHADE; typedef int16_t M_MESH_SHADE;
#pragma pack(pop) #pragma pack(pop)
typedef struct { typedef struct {
int32_t vertex_start; int32_t vertex_start;
int32_t vertex_count; int32_t vertex_count;
VECTOR *animated_vertices;
} M_BATCH; } M_BATCH;
static struct { static struct {
size_t vertex_count;
GLuint vao; GLuint vao;
GLuint geom_vbo; GLuint geom_vbo;
size_t vertex_count; GLuint tex_vbo;
GLuint shade_vbo; GLuint shade_vbo;
M_MESH_VERTEX *geom_vbo_data; M_MESH_VERTEX *geom_vbo_data;
OUTPUT_MESH_TEXTURE *tex_vbo_data;
M_MESH_SHADE *shade_vbo_data; M_MESH_SHADE *shade_vbo_data;
int32_t *light_idx_map; int32_t *light_idx_map;
size_t batch_count; size_t batch_count;
@ -65,8 +72,18 @@ static uint16_t M_GetFlags(const OBJECT_MESH *const mesh, const bool flat)
return flags; return flags;
} }
static void M_FillTexture(
OUTPUT_MESH_TEXTURE *const out_texture, const int32_t uvw_idx,
const float trapezoid_ratio_x, const float trapezoid_ratio_y)
{
out_texture->uvw = Output_Textures_GetUVW(uvw_idx);
out_texture->texture_size = Output_Textures_GetAtlasSize(uvw_idx / 4);
out_texture->trapezoid_ratio[0] = trapezoid_ratio_x;
out_texture->trapezoid_ratio[1] = trapezoid_ratio_y;
}
static void M_FillVertex( static void M_FillVertex(
M_MESH_VERTEX *const out_vertex, const int32_t uvw_idx, M_MESH_VERTEX *out_vertex, const int32_t uvw_idx,
const OBJECT_MESH *const mesh, const int32_t vertex_idx, const OBJECT_MESH *const mesh, const int32_t vertex_idx,
const int32_t palette_idx) const int32_t palette_idx)
{ {
@ -75,7 +92,6 @@ static void M_FillVertex(
out_vertex->pos = (XYZ_F) { .x = pos.x, .y = pos.y, .z = pos.z }; out_vertex->pos = (XYZ_F) { .x = pos.x, .y = pos.y, .z = pos.z };
out_vertex->normal = out_vertex->normal =
(XYZ_F) { .x = normal.x, .y = -normal.y, .z = normal.z }; (XYZ_F) { .x = normal.x, .y = -normal.y, .z = normal.z };
out_vertex->uvw_idx = uvw_idx;
out_vertex->flags = M_GetFlags(mesh, palette_idx >= 0); out_vertex->flags = M_GetFlags(mesh, palette_idx >= 0);
if (palette_idx >= 0) { if (palette_idx >= 0) {
out_vertex->color = out_vertex->color =
@ -85,57 +101,63 @@ static void M_FillVertex(
} }
} }
static M_MESH_VERTEX *M_FillFace4( static void M_FillFace4(
const OBJECT_MESH *const mesh, M_MESH_VERTEX *out_vertex, const OBJECT_MESH *const mesh, M_MESH_VERTEX **out_vertex,
const FACE4 *const face, const bool flat) OUTPUT_MESH_TEXTURE **out_texture, const FACE4 *const face, const bool flat)
{ {
for (int32_t i = 0; i < OUTPUT_QUAD_VERTICES; i++) { for (int32_t i = 0; i < OUTPUT_QUAD_VERTICES; i++) {
const int32_t j = OUTPUT_QUAD_TO_FAN(i); const int32_t j = OUTPUT_QUAD_TO_FAN(i);
const int32_t uvw_idx = face->texture_idx * 4 + j; const int32_t uvw_idx = face->texture_idx * 4 + j;
M_FillVertex( M_FillVertex(
out_vertex, uvw_idx, mesh, face->vertices[j], *out_vertex, uvw_idx, mesh, face->vertices[j],
flat ? face->palette_idx : -1); flat ? face->palette_idx : -1);
out_vertex->trapezoid_ratio[0] = face->texture_zw[j].z; M_FillTexture(
out_vertex->trapezoid_ratio[1] = face->texture_zw[j].w; *out_texture, uvw_idx, face->texture_zw[j].z,
out_vertex++; face->texture_zw[j].w);
(*out_vertex)++;
(*out_texture)++;
} }
return out_vertex;
} }
static M_MESH_VERTEX *M_FillFace3( static void M_FillFace3(
const OBJECT_MESH *const mesh, M_MESH_VERTEX *out_vertex, const OBJECT_MESH *const mesh, M_MESH_VERTEX **out_vertex,
const FACE3 *const face, const bool flat) OUTPUT_MESH_TEXTURE **out_texture, const FACE3 *const face, const bool flat)
{ {
const M_BATCH *const batch = M_GetBatch(mesh); const M_BATCH *const batch = M_GetBatch(mesh);
for (int32_t i = 0; i < OUTPUT_TRI_VERTICES; i++) { for (int32_t i = 0; i < OUTPUT_TRI_VERTICES; i++) {
const int32_t j = OUTPUT_TRI_TO_FAN(i); const int32_t j = OUTPUT_TRI_TO_FAN(i);
const int32_t uvw_idx = face->texture_idx * 4 + j; const int32_t uvw_idx = face->texture_idx * 4 + j;
M_FillVertex( M_FillVertex(
out_vertex, uvw_idx, mesh, face->vertices[j], *out_vertex, uvw_idx, mesh, face->vertices[j],
flat ? face->palette_idx : -1); flat ? face->palette_idx : -1);
out_vertex->trapezoid_ratio[0] = 1.0f; M_FillTexture(*out_texture, uvw_idx, 1.0f, 1.0f);
out_vertex->trapezoid_ratio[1] = 1.0f; (*out_vertex)++;
out_vertex++; (*out_texture)++;
} }
return out_vertex;
} }
static void M_UpdateGeometry(const OBJECT_MESH *const mesh) static void M_UpdateGeometry(const OBJECT_MESH *const mesh)
{ {
const M_BATCH *const batch = M_GetBatch(mesh); const M_BATCH *const batch = M_GetBatch(mesh);
M_MESH_VERTEX *out_vertex = &m_Priv.geom_vbo_data[batch->vertex_start]; M_MESH_VERTEX *out_vertex = &m_Priv.geom_vbo_data[batch->vertex_start];
OUTPUT_MESH_TEXTURE *out_texture =
&m_Priv.tex_vbo_data[batch->vertex_start];
for (int32_t i = 0; i < mesh->num_tex_face4s; i++) { for (int32_t i = 0; i < mesh->num_tex_face4s; i++) {
out_vertex = M_FillFace4(mesh, out_vertex, &mesh->tex_face4s[i], false); M_FillFace4(
mesh, &out_vertex, &out_texture, &mesh->tex_face4s[i], false);
} }
for (int32_t i = 0; i < mesh->num_tex_face3s; i++) { for (int32_t i = 0; i < mesh->num_tex_face3s; i++) {
out_vertex = M_FillFace3(mesh, out_vertex, &mesh->tex_face3s[i], false); M_FillFace3(
mesh, &out_vertex, &out_texture, &mesh->tex_face3s[i], false);
} }
for (int32_t i = 0; i < mesh->num_flat_face4s; i++) { for (int32_t i = 0; i < mesh->num_flat_face4s; i++) {
out_vertex = M_FillFace4(mesh, out_vertex, &mesh->flat_face4s[i], true); M_FillFace4(
mesh, &out_vertex, &out_texture, &mesh->flat_face4s[i], true);
} }
for (int32_t i = 0; i < mesh->num_flat_face3s; i++) { for (int32_t i = 0; i < mesh->num_flat_face3s; i++) {
out_vertex = M_FillFace3(mesh, out_vertex, &mesh->flat_face3s[i], true); M_FillFace3(
mesh, &out_vertex, &out_texture, &mesh->flat_face3s[i], true);
} }
#define SET(v, j) light_idx_map[v] = face->vertices[j]; #define SET(v, j) light_idx_map[v] = face->vertices[j];
@ -241,6 +263,12 @@ static void M_UpdateVertices(void)
m_Priv.vertex_count * sizeof(M_MESH_VERTEX), m_Priv.geom_vbo_data, m_Priv.vertex_count * sizeof(M_MESH_VERTEX), m_Priv.geom_vbo_data,
GL_STATIC_DRAW); GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.tex_vbo);
GFX_TRACK_DATA(
glBufferData, GL_ARRAY_BUFFER,
m_Priv.vertex_count * sizeof(OUTPUT_MESH_TEXTURE), m_Priv.tex_vbo_data,
GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo);
GFX_TRACK_DATA( GFX_TRACK_DATA(
glBufferData, GL_ARRAY_BUFFER, glBufferData, GL_ARRAY_BUFFER,
@ -268,54 +296,105 @@ static void M_PrepareBuffers(void)
} }
m_Priv.vertex_count = last_vertex; m_Priv.vertex_count = last_vertex;
int32_t current_vertex = 0;
for (int32_t i = 0; i < Object_GetMeshCount(); i++) {
const OBJECT_MESH *const obj_mesh = Object_GetMesh(i);
M_BATCH *const batch = &m_Priv.batches[i];
batch->animated_vertices = Vector_Create(sizeof(OUTPUT_VERTEX_RANGE));
for (int32_t j = 0; j < obj_mesh->num_tex_face4s; j++) {
const FACE4 *const face = &obj_mesh->tex_face4s[j];
if (Output_Textures_IsObjectTextureAnimated(face->texture_idx)) {
Vector_Add(
batch->animated_vertices,
&(OUTPUT_VERTEX_RANGE) {
.vertex_start = current_vertex,
.vertex_count = OUTPUT_QUAD_VERTICES,
});
}
current_vertex += OUTPUT_QUAD_VERTICES;
}
for (int32_t j = 0; j < obj_mesh->num_tex_face3s; j++) {
const FACE3 *const face = &obj_mesh->tex_face3s[j];
if (Output_Textures_IsObjectTextureAnimated(face->texture_idx)) {
Vector_Add(
batch->animated_vertices,
&(OUTPUT_VERTEX_RANGE) {
.vertex_start = current_vertex,
.vertex_count = OUTPUT_TRI_VERTICES,
});
}
current_vertex += OUTPUT_TRI_VERTICES;
}
current_vertex += obj_mesh->num_flat_face4s * OUTPUT_QUAD_VERTICES;
current_vertex += obj_mesh->num_flat_face3s * OUTPUT_TRI_VERTICES;
Output_GlueVertexRanges(batch->animated_vertices);
}
m_Priv.geom_vbo_data = m_Priv.geom_vbo_data =
Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_VERTEX)); Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_VERTEX));
m_Priv.tex_vbo_data =
Memory_Alloc(m_Priv.vertex_count * sizeof(OUTPUT_MESH_TEXTURE));
m_Priv.shade_vbo_data = m_Priv.shade_vbo_data =
Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_SHADE)); Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_SHADE));
m_Priv.light_idx_map = Memory_Alloc(m_Priv.vertex_count * sizeof(int32_t)); m_Priv.light_idx_map = Memory_Alloc(m_Priv.vertex_count * sizeof(int32_t));
glGenVertexArrays(1, &m_Priv.vao); glGenVertexArrays(1, &m_Priv.vao);
glGenBuffers(1, &m_Priv.geom_vbo); glGenBuffers(1, &m_Priv.geom_vbo);
glGenBuffers(1, &m_Priv.tex_vbo);
glGenBuffers(1, &m_Priv.shade_vbo); glGenBuffers(1, &m_Priv.shade_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo);
glBindVertexArray(m_Priv.vao); glBindVertexArray(m_Priv.vao);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo);
// attribute 0: position
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer( glVertexAttribPointer(
0, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX), 0, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, pos)); (void *)(intptr_t)offsetof(M_MESH_VERTEX, pos));
// attribute 1: normal
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribPointer( glVertexAttribPointer(
1, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX), 1, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, normal)); (void *)(intptr_t)offsetof(M_MESH_VERTEX, normal));
glEnableVertexAttribArray(2); // attribute 5: flags
glEnableVertexAttribArray(5);
glVertexAttribIPointer( glVertexAttribIPointer(
2, 1, GL_UNSIGNED_INT, sizeof(M_MESH_VERTEX), 5, 1, GL_UNSIGNED_SHORT, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, uvw_idx));
glEnableVertexAttribArray(3);
glVertexAttribPointer(
3, 2, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, trapezoid_ratio));
glEnableVertexAttribArray(4);
glVertexAttribIPointer(
4, 1, GL_UNSIGNED_SHORT, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, flags)); (void *)(intptr_t)offsetof(M_MESH_VERTEX, flags));
glEnableVertexAttribArray(5); // attribute 6: color
glVertexAttribPointer(
5, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, color));
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo);
glEnableVertexAttribArray(6); glEnableVertexAttribArray(6);
glVertexAttribPointer( glVertexAttribPointer(
6, 1, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(M_MESH_SHADE), 0); 6, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, color));
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.tex_vbo);
// attribute 2: uvw
glEnableVertexAttribArray(2);
glVertexAttribPointer(
2, 3, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_TEXTURE),
(void *)(intptr_t)offsetof(OUTPUT_MESH_TEXTURE, uvw));
// attribute 3: texture size
glEnableVertexAttribArray(3);
glVertexAttribPointer(
3, 4, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_TEXTURE),
(void *)(intptr_t)offsetof(OUTPUT_MESH_TEXTURE, texture_size));
// attribute 4: trapezoid ratios
glEnableVertexAttribArray(4);
glVertexAttribPointer(
4, 2, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_TEXTURE),
(void *)(intptr_t)offsetof(OUTPUT_MESH_TEXTURE, trapezoid_ratio));
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo);
// attribute 7: shade
glEnableVertexAttribArray(7);
glVertexAttribPointer(
7, 1, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(M_MESH_SHADE), 0);
M_UpdateVertices(); M_UpdateVertices();
} }
@ -332,13 +411,21 @@ static void M_FreeBuffers(void)
glDeleteBuffers(1, &m_Priv.geom_vbo); glDeleteBuffers(1, &m_Priv.geom_vbo);
m_Priv.geom_vbo = 0; m_Priv.geom_vbo = 0;
} }
if (m_Priv.tex_vbo != 0) {
glDeleteBuffers(1, &m_Priv.tex_vbo);
m_Priv.tex_vbo = 0;
}
if (m_Priv.shade_vbo != 0) { if (m_Priv.shade_vbo != 0) {
glDeleteBuffers(1, &m_Priv.shade_vbo); glDeleteBuffers(1, &m_Priv.shade_vbo);
m_Priv.shade_vbo = 0; m_Priv.shade_vbo = 0;
} }
Memory_FreePointer(&m_Priv.geom_vbo_data); Memory_FreePointer(&m_Priv.geom_vbo_data);
Memory_FreePointer(&m_Priv.tex_vbo_data);
Memory_FreePointer(&m_Priv.shade_vbo_data); Memory_FreePointer(&m_Priv.shade_vbo_data);
Memory_FreePointer(&m_Priv.light_idx_map); Memory_FreePointer(&m_Priv.light_idx_map);
for (int32_t i = 0; i < (int32_t)m_Priv.batch_count; i++) {
Vector_Free(m_Priv.batches[i].animated_vertices);
}
Memory_FreePointer(&m_Priv.batches); Memory_FreePointer(&m_Priv.batches);
} }
@ -362,6 +449,65 @@ void Output_Meshes_ObserveLevelUnloadObjects(void)
M_FreeBuffers(); M_FreeBuffers();
} }
static void M_FillAnimatedTextures(const OBJECT_MESH *const obj_mesh)
{
const M_BATCH *const batch = M_GetBatch(obj_mesh);
OUTPUT_MESH_TEXTURE *out_texture =
&m_Priv.tex_vbo_data[batch->vertex_start];
for (int32_t j = 0; j < obj_mesh->num_tex_face4s; j++) {
const FACE4 *const face = &obj_mesh->tex_face4s[j];
if (!Output_Textures_IsObjectTextureAnimated(face->texture_idx)) {
out_texture += OUTPUT_QUAD_VERTICES;
continue;
}
for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) {
const int32_t l = OUTPUT_QUAD_TO_FAN(k);
const int32_t uvw_idx = face->texture_idx * 4 + l;
M_FillTexture(
out_texture, uvw_idx, face->texture_zw[l].z,
face->texture_zw[l].w);
out_texture++;
}
}
for (int32_t j = 0; j < obj_mesh->num_tex_face3s; j++) {
const FACE3 *const face = &obj_mesh->tex_face3s[j];
if (!Output_Textures_IsObjectTextureAnimated(face->texture_idx)) {
out_texture += OUTPUT_TRI_VERTICES;
continue;
}
for (int32_t k = 0; k < OUTPUT_TRI_VERTICES; k++) {
const int32_t l = OUTPUT_TRI_TO_FAN(k);
const int32_t uvw_idx = face->texture_idx * 4 + l;
M_FillTexture(out_texture, uvw_idx, 1.0f, 1.0f);
out_texture++;
}
}
}
static void M_UpdateAnimedTextures(const OBJECT_MESH *const obj_mesh)
{
const M_BATCH *const batch = M_GetBatch(obj_mesh);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.tex_vbo);
for (int32_t i = 0; i < batch->animated_vertices->count; i++) {
const OUTPUT_VERTEX_RANGE *const range =
Vector_Get(batch->animated_vertices, i);
GFX_TRACK_DATA(
glBufferSubData, GL_ARRAY_BUFFER,
range->vertex_start * sizeof(OUTPUT_MESH_TEXTURE),
range->vertex_count * sizeof(OUTPUT_MESH_TEXTURE),
&m_Priv.tex_vbo_data[range->vertex_start]);
}
}
void Output_Meshes_ObserveTextureAnimationObjects(void)
{
for (int32_t i = 0; i < Object_GetMeshCount(); i++) {
const OBJECT_MESH *const obj_mesh = Object_GetMesh(i);
M_FillAnimatedTextures(obj_mesh);
M_UpdateAnimedTextures(obj_mesh);
}
}
void Output_Meshes_ObserveObjectMeshSwap( void Output_Meshes_ObserveObjectMeshSwap(
const OBJECT_MESH *const mesh_1, const OBJECT_MESH *const mesh_2) const OBJECT_MESH *const mesh_1, const OBJECT_MESH *const mesh_2)
{ {
@ -393,6 +539,14 @@ void Output_Meshes_ObserveObjectMeshUpdate(const OBJECT_MESH *const mesh)
batch->vertex_count * sizeof(M_MESH_VERTEX), batch->vertex_count * sizeof(M_MESH_VERTEX),
&m_Priv.geom_vbo_data[batch->vertex_start]); &m_Priv.geom_vbo_data[batch->vertex_start]);
GFX_GL_CheckError(); GFX_GL_CheckError();
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.tex_vbo);
GFX_TRACK_SUBDATA(
glBufferSubData, GL_ARRAY_BUFFER,
batch->vertex_start * sizeof(OUTPUT_MESH_TEXTURE),
batch->vertex_count * sizeof(OUTPUT_MESH_TEXTURE),
&m_Priv.tex_vbo_data[batch->vertex_start]);
GFX_GL_CheckError();
} }
void Output_Meshes_RenderObjectMesh( void Output_Meshes_RenderObjectMesh(
@ -421,11 +575,7 @@ void Output_Meshes_RenderObjectMesh(
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture()); glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture());
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetUVWsTexture());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture()); glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture());
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetAtlasSizesTexture());
GFX_GL_CheckError(); GFX_GL_CheckError();
Output_Shader_UploadWibbleEffect(m_Shader, false); Output_Shader_UploadWibbleEffect(m_Shader, false);

View file

@ -9,6 +9,7 @@ void Output_Meshes_InitObjects(void);
void Output_Meshes_ShutdownObjects(void); void Output_Meshes_ShutdownObjects(void);
void Output_Meshes_ObserveLevelLoadObjects(void); void Output_Meshes_ObserveLevelLoadObjects(void);
void Output_Meshes_ObserveLevelUnloadObjects(void); void Output_Meshes_ObserveLevelUnloadObjects(void);
void Output_Meshes_ObserveTextureAnimationObjects(void);
void Output_Meshes_RenderObjectMesh( void Output_Meshes_RenderObjectMesh(
const MATRIX *matrix, RGB_F tint, const OBJECT_MESH *mesh); const MATRIX *matrix, RGB_F tint, const OBJECT_MESH *mesh);

View file

@ -4,22 +4,23 @@
#include "game/output/meshes/common.h" #include "game/output/meshes/common.h"
#include "game/output/textures.h" #include "game/output/textures.h"
#include "game/output/utils.h" #include "game/output/utils.h"
#include "game/output/vertex_range.h"
#include "game/random.h" #include "game/random.h"
#include "game/room.h" #include "game/room.h"
#include <libtrx/gfx/gl/utils.h> #include <libtrx/gfx/gl/utils.h>
#include <libtrx/memory.h> #include <libtrx/memory.h>
#include <libtrx/vector.h>
#pragma pack(push, 1) #pragma pack(push, 1)
typedef struct { typedef struct {
// clang-format off // attribute 0
XYZ_F pos; // attribute 0 XYZ_F pos;
int32_t uvw_idx; // attribute 2 // attribute 5
float trapezoid_ratio[2]; // attribute 3 uint16_t flags;
uint16_t flags; // attribute 4
// clang-format on
} M_MESH_VERTEX; } M_MESH_VERTEX;
// attribute 7
typedef int16_t M_MESH_SHADE; typedef int16_t M_MESH_SHADE;
#pragma pack(pop) #pragma pack(pop)
@ -27,14 +28,17 @@ typedef struct {
int32_t vertex_start; int32_t vertex_start;
int32_t vertex_count; int32_t vertex_count;
int16_t *caustics; int16_t *caustics;
VECTOR *animated_vertices;
} M_BATCH; } M_BATCH;
static struct { static struct {
size_t vertex_count;
GLuint vao; GLuint vao;
GLuint geom_vbo; GLuint geom_vbo;
size_t vertex_count; GLuint tex_vbo;
GLuint shade_vbo; GLuint shade_vbo;
M_MESH_VERTEX *geom_vbo_data; M_MESH_VERTEX *geom_vbo_data;
OUTPUT_MESH_TEXTURE *tex_vbo_data;
M_MESH_SHADE *shade_vbo_data; M_MESH_SHADE *shade_vbo_data;
size_t batch_count; size_t batch_count;
M_BATCH *batches; M_BATCH *batches;
@ -69,55 +73,67 @@ static M_BATCH *M_GetBatch(const ROOM *const room)
} }
static void M_FillVertex( static void M_FillVertex(
M_MESH_VERTEX *const out_vertex, const int32_t uvw_idx, const XYZ_16 pos, M_MESH_VERTEX *const out_vertex, const XYZ_16 pos, const uint16_t flags)
const uint16_t flags)
{ {
out_vertex->pos = (XYZ_F) { .x = pos.x, .y = pos.y, .z = pos.z }; 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; out_vertex->flags = flags & NO_VERT_MOVE ? VERT_NO_CAUSTICS : 0;
} }
static M_MESH_VERTEX *M_FillRoomFace4( static void M_FillTexture(
const ROOM *const room, M_MESH_VERTEX *out_vertex, const FACE4 *const face) OUTPUT_MESH_TEXTURE *const out_texture, const int32_t uvw_idx,
const float trapezoid_ratio_x, const float trapezoid_ratio_y)
{
out_texture->uvw = Output_Textures_GetUVW(uvw_idx);
out_texture->texture_size = Output_Textures_GetAtlasSize(uvw_idx / 4);
out_texture->trapezoid_ratio[0] = trapezoid_ratio_x;
out_texture->trapezoid_ratio[1] = trapezoid_ratio_y;
}
static void M_FillRoomFace4(
const ROOM *const room, M_MESH_VERTEX **out_vertex,
OUTPUT_MESH_TEXTURE **out_texture, const FACE4 *const face)
{ {
for (int32_t i = 0; i < OUTPUT_QUAD_VERTICES; i++) { for (int32_t i = 0; i < OUTPUT_QUAD_VERTICES; i++) {
const int32_t j = OUTPUT_QUAD_TO_FAN(i); const int32_t j = OUTPUT_QUAD_TO_FAN(i);
const int32_t uvw_idx = face->texture_idx * 4 + j; const int32_t uvw_idx = face->texture_idx * 4 + j;
const ROOM_VERTEX *const room_vertex = const ROOM_VERTEX *const room_vertex =
&room->mesh.vertices[face->vertices[j]]; &room->mesh.vertices[face->vertices[j]];
M_FillVertex(out_vertex, uvw_idx, room_vertex->pos, room_vertex->flags); M_FillVertex(*out_vertex, room_vertex->pos, room_vertex->flags);
out_vertex->trapezoid_ratio[0] = face->texture_zw[j].z; M_FillTexture(
out_vertex->trapezoid_ratio[1] = face->texture_zw[j].w; *out_texture, uvw_idx, face->texture_zw[j].z,
out_vertex++; face->texture_zw[j].w);
(*out_vertex)++;
(*out_texture)++;
} }
return out_vertex;
} }
static M_MESH_VERTEX *M_FillRoomFace3( static void M_FillRoomFace3(
const ROOM *const room, M_MESH_VERTEX *out_vertex, const FACE3 *const face) const ROOM *const room, M_MESH_VERTEX **out_vertex,
OUTPUT_MESH_TEXTURE **out_texture, const FACE3 *const face)
{ {
for (int32_t i = 0; i < OUTPUT_TRI_VERTICES; i++) { for (int32_t i = 0; i < OUTPUT_TRI_VERTICES; i++) {
const int32_t j = OUTPUT_TRI_TO_FAN(i); const int32_t j = OUTPUT_TRI_TO_FAN(i);
const int32_t uvw_idx = face->texture_idx * 4 + j; const int32_t uvw_idx = face->texture_idx * 4 + j;
const ROOM_VERTEX *const room_vertex = const ROOM_VERTEX *const room_vertex =
&room->mesh.vertices[face->vertices[j]]; &room->mesh.vertices[face->vertices[j]];
M_FillVertex(out_vertex, uvw_idx, room_vertex->pos, room_vertex->flags); M_FillVertex(*out_vertex, room_vertex->pos, room_vertex->flags);
out_vertex->trapezoid_ratio[0] = 1.0f; M_FillTexture(*out_texture, uvw_idx, 1.0f, 1.0f);
out_vertex->trapezoid_ratio[1] = 1.0f; (*out_vertex)++;
out_vertex++; (*out_texture)++;
} }
return out_vertex;
} }
static void M_UpdateRoomGeometry(const ROOM *const room) static void M_UpdateRoomGeometry(const ROOM *const room)
{ {
M_BATCH *const batch = &m_Priv.batches[Room_GetNumber(room)]; M_BATCH *const batch = &m_Priv.batches[Room_GetNumber(room)];
M_MESH_VERTEX *out_vertex = &m_Priv.geom_vbo_data[batch->vertex_start]; M_MESH_VERTEX *out_vertex = &m_Priv.geom_vbo_data[batch->vertex_start];
OUTPUT_MESH_TEXTURE *out_texture =
&m_Priv.tex_vbo_data[batch->vertex_start];
for (int32_t i = 0; i < room->mesh.num_face4s; i++) { for (int32_t i = 0; i < room->mesh.num_face4s; i++) {
out_vertex = M_FillRoomFace4(room, out_vertex, &room->mesh.face4s[i]); M_FillRoomFace4(room, &out_vertex, &out_texture, &room->mesh.face4s[i]);
} }
for (int32_t i = 0; i < room->mesh.num_face3s; i++) { for (int32_t i = 0; i < room->mesh.num_face3s; i++) {
out_vertex = M_FillRoomFace3(room, out_vertex, &room->mesh.face3s[i]); M_FillRoomFace3(room, &out_vertex, &out_texture, &room->mesh.face3s[i]);
} }
} }
@ -160,6 +176,12 @@ static void M_UpdateVertices(void)
m_Priv.vertex_count * sizeof(M_MESH_VERTEX), m_Priv.geom_vbo_data, m_Priv.vertex_count * sizeof(M_MESH_VERTEX), m_Priv.geom_vbo_data,
GL_STATIC_DRAW); GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.tex_vbo);
GFX_TRACK_DATA(
glBufferData, GL_ARRAY_BUFFER,
m_Priv.vertex_count * sizeof(OUTPUT_MESH_TEXTURE), m_Priv.tex_vbo_data,
GL_DYNAMIC_DRAW); // allow animating textures
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo);
GFX_TRACK_DATA( GFX_TRACK_DATA(
glBufferData, GL_ARRAY_BUFFER, glBufferData, GL_ARRAY_BUFFER,
@ -181,10 +203,8 @@ static void M_PrepareBuffers(void)
Memory_Alloc(room->mesh.num_vertices * sizeof(int16_t)); Memory_Alloc(room->mesh.num_vertices * sizeof(int16_t));
batch->vertex_start = last_vertex; batch->vertex_start = last_vertex;
batch->vertex_count = 0; batch->vertex_count = 0;
batch->vertex_count += batch->vertex_count += room->mesh.num_face4s * OUTPUT_QUAD_VERTICES;
Room_Get(i)->mesh.num_face4s * OUTPUT_QUAD_VERTICES; batch->vertex_count += room->mesh.num_face3s * OUTPUT_TRI_VERTICES;
batch->vertex_count +=
Room_Get(i)->mesh.num_face3s * OUTPUT_TRI_VERTICES;
last_vertex += batch->vertex_count; last_vertex += batch->vertex_count;
for (int32_t j = 0; j < room->mesh.num_vertices; j++) { for (int32_t j = 0; j < room->mesh.num_vertices; j++) {
@ -192,50 +212,96 @@ static void M_PrepareBuffers(void)
m_CausticsTable[(room->mesh.num_vertices - j) % WIBBLE_SIZE]; m_CausticsTable[(room->mesh.num_vertices - j) % WIBBLE_SIZE];
} }
} }
m_Priv.vertex_count = last_vertex; m_Priv.vertex_count = last_vertex;
int32_t current_vertex = 0;
for (int32_t i = 0; i < Room_GetCount(); i++) {
const ROOM *const room = Room_Get(i);
M_BATCH *const batch = M_GetBatch(room);
batch->animated_vertices = Vector_Create(sizeof(OUTPUT_VERTEX_RANGE));
for (int32_t j = 0; j < room->mesh.num_face4s; j++) {
const FACE4 *const face = &room->mesh.face4s[j];
if (Output_Textures_IsObjectTextureAnimated(face->texture_idx)) {
Vector_Add(
batch->animated_vertices,
&(OUTPUT_VERTEX_RANGE) {
.vertex_start = current_vertex,
.vertex_count = OUTPUT_QUAD_VERTICES,
});
}
current_vertex += OUTPUT_QUAD_VERTICES;
}
for (int32_t j = 0; j < room->mesh.num_face3s; j++) {
const FACE3 *const face = &room->mesh.face3s[j];
if (Output_Textures_IsObjectTextureAnimated(face->texture_idx)) {
Vector_Add(
batch->animated_vertices,
&(OUTPUT_VERTEX_RANGE) {
.vertex_start = current_vertex,
.vertex_count = OUTPUT_TRI_VERTICES,
});
}
current_vertex += OUTPUT_TRI_VERTICES;
}
Output_GlueVertexRanges(batch->animated_vertices);
}
m_Priv.geom_vbo_data = m_Priv.geom_vbo_data =
Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_VERTEX)); Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_VERTEX));
m_Priv.tex_vbo_data =
Memory_Alloc(m_Priv.vertex_count * sizeof(OUTPUT_MESH_TEXTURE));
m_Priv.shade_vbo_data = m_Priv.shade_vbo_data =
Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_SHADE)); Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_SHADE));
glGenVertexArrays(1, &m_Priv.vao); glGenVertexArrays(1, &m_Priv.vao);
glGenBuffers(1, &m_Priv.geom_vbo); glGenBuffers(1, &m_Priv.geom_vbo);
glGenBuffers(1, &m_Priv.tex_vbo);
glGenBuffers(1, &m_Priv.shade_vbo); glGenBuffers(1, &m_Priv.shade_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo);
glBindVertexArray(m_Priv.vao); glBindVertexArray(m_Priv.vao);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_Priv.geom_vbo);
// attribute 0: position
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer( glVertexAttribPointer(
0, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX), 0, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, pos)); (void *)(intptr_t)offsetof(M_MESH_VERTEX, pos));
// ignore attribute 1 (normals) - rooms never have reflective faces // attribute 1: normal (ignore)
glEnableVertexAttribArray(2); // attribute 5: flags
glEnableVertexAttribArray(5);
glVertexAttribIPointer( glVertexAttribIPointer(
2, 1, GL_UNSIGNED_INT, sizeof(M_MESH_VERTEX), 5, 1, GL_UNSIGNED_SHORT, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, uvw_idx));
glEnableVertexAttribArray(3);
glVertexAttribPointer(
3, 2, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, trapezoid_ratio));
glEnableVertexAttribArray(4);
glVertexAttribIPointer(
4, 1, GL_UNSIGNED_SHORT, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, flags)); (void *)(intptr_t)offsetof(M_MESH_VERTEX, flags));
// ignore attribute 5 (mesh color) - rooms only ever use textured faces // attribute 6: mesh color (ignore)
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.tex_vbo);
// attribute 2: uvw
glEnableVertexAttribArray(2);
glVertexAttribPointer(
2, 3, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_TEXTURE),
(void *)(intptr_t)offsetof(OUTPUT_MESH_TEXTURE, uvw));
// attribute 3: texture size
glEnableVertexAttribArray(3);
glVertexAttribPointer(
3, 4, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_TEXTURE),
(void *)(intptr_t)offsetof(OUTPUT_MESH_TEXTURE, texture_size));
// attribute 4: trapezoid ratios
glEnableVertexAttribArray(4);
glVertexAttribPointer(
4, 2, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_TEXTURE),
(void *)(intptr_t)offsetof(OUTPUT_MESH_TEXTURE, trapezoid_ratio));
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_Priv.shade_vbo);
glEnableVertexAttribArray(6);
// attribute 7 (shade)
glEnableVertexAttribArray(7);
glVertexAttribPointer( glVertexAttribPointer(
6, 1, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(M_MESH_SHADE), 0); 7, 1, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(M_MESH_SHADE), 0);
M_UpdateVertices(); M_UpdateVertices();
} }
@ -252,13 +318,19 @@ static void M_FreeBuffers(void)
glDeleteBuffers(1, &m_Priv.geom_vbo); glDeleteBuffers(1, &m_Priv.geom_vbo);
m_Priv.geom_vbo = 0; m_Priv.geom_vbo = 0;
} }
if (m_Priv.tex_vbo != 0) {
glDeleteBuffers(1, &m_Priv.tex_vbo);
m_Priv.tex_vbo = 0;
}
if (m_Priv.shade_vbo != 0) { if (m_Priv.shade_vbo != 0) {
glDeleteBuffers(1, &m_Priv.shade_vbo); glDeleteBuffers(1, &m_Priv.shade_vbo);
m_Priv.shade_vbo = 0; m_Priv.shade_vbo = 0;
} }
Memory_FreePointer(&m_Priv.geom_vbo_data); Memory_FreePointer(&m_Priv.geom_vbo_data);
Memory_FreePointer(&m_Priv.tex_vbo_data);
Memory_FreePointer(&m_Priv.shade_vbo_data); Memory_FreePointer(&m_Priv.shade_vbo_data);
for (int32_t i = 0; i < (int32_t)m_Priv.batch_count; i++) { for (int32_t i = 0; i < (int32_t)m_Priv.batch_count; i++) {
Vector_Free(m_Priv.batches[i].animated_vertices);
Memory_FreePointer(&m_Priv.batches[i].caustics); Memory_FreePointer(&m_Priv.batches[i].caustics);
} }
Memory_FreePointer(&m_Priv.batches); Memory_FreePointer(&m_Priv.batches);
@ -285,6 +357,71 @@ void Output_Meshes_ObserveLevelLoadRooms(void)
M_PrepareBuffers(); M_PrepareBuffers();
} }
static void M_FillAnimatedTextures(const ROOM *const room)
{
const M_BATCH *const batch = M_GetBatch(room);
OUTPUT_MESH_TEXTURE *out_texture =
&m_Priv.tex_vbo_data[batch->vertex_start];
for (int32_t j = 0; j < room->mesh.num_face4s; j++) {
const FACE4 *const face = &room->mesh.face4s[j];
if (!Output_Textures_IsObjectTextureAnimated(face->texture_idx)) {
out_texture += OUTPUT_QUAD_VERTICES;
continue;
}
for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) {
const int32_t l = OUTPUT_QUAD_TO_FAN(k);
const int32_t uvw_idx = face->texture_idx * 4 + l;
M_FillTexture(
out_texture, uvw_idx, face->texture_zw[l].z,
face->texture_zw[l].w);
out_texture++;
}
}
for (int32_t j = 0; j < room->mesh.num_face3s; j++) {
const FACE3 *const face = &room->mesh.face3s[j];
if (!Output_Textures_IsObjectTextureAnimated(face->texture_idx)) {
out_texture += OUTPUT_TRI_VERTICES;
continue;
}
for (int32_t k = 0; k < OUTPUT_TRI_VERTICES; k++) {
const int32_t l = OUTPUT_TRI_TO_FAN(k);
const int32_t uvw_idx = face->texture_idx * 4 + l;
M_FillTexture(out_texture, uvw_idx, 1.0f, 1.0f);
out_texture++;
}
}
}
static void M_UpdateAnimatedTextures(const ROOM *const room)
{
const M_BATCH *const batch = M_GetBatch(room);
glBindBuffer(GL_ARRAY_BUFFER, m_Priv.tex_vbo);
for (int32_t i = 0; i < batch->animated_vertices->count; i++) {
const OUTPUT_VERTEX_RANGE *const range =
Vector_Get(batch->animated_vertices, i);
GFX_TRACK_DATA(
glBufferSubData, GL_ARRAY_BUFFER,
range->vertex_start * sizeof(OUTPUT_MESH_TEXTURE),
range->vertex_count * sizeof(OUTPUT_MESH_TEXTURE),
&m_Priv.tex_vbo_data[range->vertex_start]);
}
}
void Output_Meshes_ObserveTextureAnimationRooms(void)
{
for (int32_t i = 0; i < Room_GetCount(); i++) {
const ROOM *const room = Room_Get(i);
M_FillAnimatedTextures(room);
M_UpdateAnimatedTextures(room);
}
}
void Output_Meshes_ObserveRoomFlip(const ROOM *const room)
{
M_FillAnimatedTextures(room);
M_UpdateAnimatedTextures(room);
}
void Output_Meshes_RenderRoomMesh( void Output_Meshes_RenderRoomMesh(
const MATRIX *const matrix, const RGB_F tint, const ROOM *const room) const MATRIX *const matrix, const RGB_F tint, const ROOM *const room)
{ {
@ -311,11 +448,7 @@ void Output_Meshes_RenderRoomMesh(
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture()); glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture());
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetUVWsTexture());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture()); glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture());
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetAtlasSizesTexture());
GFX_GL_CheckError(); GFX_GL_CheckError();
if (Output_GetWibbleEffect()) { if (Output_GetWibbleEffect()) {

View file

@ -7,6 +7,8 @@
void Output_Meshes_InitRooms(void); void Output_Meshes_InitRooms(void);
void Output_Meshes_ShutdownRooms(void); void Output_Meshes_ShutdownRooms(void);
void Output_Meshes_ObserveLevelLoadRooms(void); void Output_Meshes_ObserveLevelLoadRooms(void);
void Output_Meshes_ObserveTextureAnimationRooms(void);
void Output_Meshes_ObserveRoomFlip(const ROOM *room);
void Output_Meshes_RenderRoomMesh( void Output_Meshes_RenderRoomMesh(
const MATRIX *matrix, RGB_F tint, const ROOM *room); const MATRIX *matrix, RGB_F tint, const ROOM *room);

View file

@ -11,8 +11,6 @@
typedef enum { typedef enum {
M_UNIFORM_TIME, M_UNIFORM_TIME,
M_UNIFORM_TEX_ATLAS, M_UNIFORM_TEX_ATLAS,
M_UNIFORM_TEX_ATLAS_SIZES,
M_UNIFORM_TEX_UVW,
M_UNIFORM_TEX_ENV_MAP, M_UNIFORM_TEX_ENV_MAP,
M_UNIFORM_SMOOTHING_ENABLED, M_UNIFORM_SMOOTHING_ENABLED,
M_UNIFORM_ALPHA_DISCARD_ENABLED, M_UNIFORM_ALPHA_DISCARD_ENABLED,
@ -52,8 +50,6 @@ OUTPUT_SHADER *Output_Shader_Create(const char *const path)
const char *const uniform_names[] = { const char *const uniform_names[] = {
[M_UNIFORM_TIME] = "uTime", [M_UNIFORM_TIME] = "uTime",
[M_UNIFORM_TEX_ATLAS] = "uTexAtlas", [M_UNIFORM_TEX_ATLAS] = "uTexAtlas",
[M_UNIFORM_TEX_ATLAS_SIZES] = "uAtlasSizes",
[M_UNIFORM_TEX_UVW] = "uUVW",
[M_UNIFORM_TEX_ENV_MAP] = "uTexEnvMap", [M_UNIFORM_TEX_ENV_MAP] = "uTexEnvMap",
[M_UNIFORM_SMOOTHING_ENABLED] = "uSmoothingEnabled", [M_UNIFORM_SMOOTHING_ENABLED] = "uSmoothingEnabled",
[M_UNIFORM_ALPHA_DISCARD_ENABLED] = "uAlphaDiscardEnabled", [M_UNIFORM_ALPHA_DISCARD_ENABLED] = "uAlphaDiscardEnabled",
@ -77,9 +73,7 @@ OUTPUT_SHADER *Output_Shader_Create(const char *const path)
GFX_GL_Program_Bind(&shader->program); GFX_GL_Program_Bind(&shader->program);
glUniform1i(shader->uniforms[M_UNIFORM_TEX_ATLAS], 0); glUniform1i(shader->uniforms[M_UNIFORM_TEX_ATLAS], 0);
glUniform1i(shader->uniforms[M_UNIFORM_TEX_UVW], 1); glUniform1i(shader->uniforms[M_UNIFORM_TEX_ENV_MAP], 1);
glUniform1i(shader->uniforms[M_UNIFORM_TEX_ENV_MAP], 2);
glUniform1i(shader->uniforms[M_UNIFORM_TEX_ATLAS_SIZES], 3);
return shader; return shader;
} }

View file

@ -4,6 +4,7 @@
#include "game/output/shader.h" #include "game/output/shader.h"
#include "game/output/textures.h" #include "game/output/textures.h"
#include "game/output/utils.h" #include "game/output/utils.h"
#include "game/output/vertex_range.h"
#include "game/room.h" #include "game/room.h"
#include <libtrx/gfx/gl/utils.h> #include <libtrx/gfx/gl/utils.h>
@ -22,7 +23,7 @@ typedef struct {
} displacement; } displacement;
// attribute 2 // attribute 2
int32_t uvw_idx; OUTPUT_UVW uvw;
} M_SPRITE_VERTEX; } M_SPRITE_VERTEX;
typedef uint16_t M_SPRITE_SHADE; typedef uint16_t M_SPRITE_SHADE;
@ -57,6 +58,7 @@ static struct {
M_SPRITE_BUFFER sprite_buf; M_SPRITE_BUFFER sprite_buf;
size_t room_batch_count; size_t room_batch_count;
M_ROOM_BATCH *room_batches; M_ROOM_BATCH *room_batches;
VECTOR *animated_vertices;
} m_LevelData = {}; } m_LevelData = {};
static struct { static struct {
@ -71,8 +73,8 @@ static void M_MakeQuad(
for (int32_t k = 0; k < 4; k++) { for (int32_t k = 0; k < 4; k++) {
out_quad[k].pos = (XYZ_F) { .x = pos.x, .y = pos.y, .z = pos.z }; out_quad[k].pos = (XYZ_F) { .x = pos.x, .y = pos.y, .z = pos.z };
out_quad[k].uvw_idx = out_quad[k].uvw = Output_Textures_GetUVW(
Output_Textures_GetSpritesUVWsBase() + sprite_idx * 4 + k; Output_Textures_GetSpritesUVWsBase() + sprite_idx * 4 + k);
} }
out_quad[0].displacement.x = sprite->x0; out_quad[0].displacement.x = sprite->x0;
@ -144,9 +146,9 @@ static void M_PrepareBuffer(
(void *)(intptr_t)offsetof(M_SPRITE_VERTEX, displacement)); (void *)(intptr_t)offsetof(M_SPRITE_VERTEX, displacement));
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glVertexAttribIPointer( glVertexAttribPointer(
2, 1, GL_UNSIGNED_INT, sizeof(M_SPRITE_VERTEX), 2, 3, GL_FLOAT, GL_FALSE, sizeof(M_SPRITE_VERTEX),
(void *)(intptr_t)offsetof(M_SPRITE_VERTEX, uvw_idx)); (void *)(intptr_t)offsetof(M_SPRITE_VERTEX, uvw));
glBindBuffer(GL_ARRAY_BUFFER, buffer->shade_vbo); glBindBuffer(GL_ARRAY_BUFFER, buffer->shade_vbo);
glEnableVertexAttribArray(3); glEnableVertexAttribArray(3);
@ -192,8 +194,6 @@ static void M_DrawBuffer(
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture()); glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture());
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetUVWsTexture());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture()); glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture());
GFX_GL_CheckError(); GFX_GL_CheckError();
@ -214,9 +214,29 @@ static void M_PrepareLevelBatches(void)
room_batch->quad_count = room->mesh.num_sprites; room_batch->quad_count = room->mesh.num_sprites;
current_quad += room->mesh.num_sprites; current_quad += room->mesh.num_sprites;
} }
Vector_Clear(m_LevelData.animated_vertices);
int32_t current_vertex = 0;
for (int32_t i = 0; i < Room_GetCount(); i++) {
const ROOM *const room = Room_Get(i);
for (int32_t j = 0; j < room->mesh.num_sprites; j++) {
const ROOM_SPRITE *const room_sprite = &room->mesh.sprites[j];
const int16_t sprite_idx = room_sprite->texture;
if (Output_Textures_IsSpriteTextureAnimated(sprite_idx)) {
Vector_Add(
m_LevelData.animated_vertices,
&(OUTPUT_VERTEX_RANGE) {
.vertex_start = current_vertex,
.vertex_count = OUTPUT_QUAD_VERTICES,
});
}
current_vertex += OUTPUT_QUAD_VERTICES;
}
}
Output_GlueVertexRanges(m_LevelData.animated_vertices);
} }
static void M_UpdateRoomGeometry(const ROOM *const room) static void M_FillRoomGeometry(const ROOM *const room)
{ {
int32_t current_vertex = 0; int32_t current_vertex = 0;
for (int32_t i = 0; i < Room_GetCount(); i++) { for (int32_t i = 0; i < Room_GetCount(); i++) {
@ -230,6 +250,7 @@ static void M_UpdateRoomGeometry(const ROOM *const room)
M_SPRITE_VERTEX quad[4]; M_SPRITE_VERTEX quad[4];
M_MakeQuad(quad, sprite_idx, pos); M_MakeQuad(quad, sprite_idx, pos);
for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) { for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) {
m_LevelData.sprite_buf.geom_vbo_data[current_vertex] = m_LevelData.sprite_buf.geom_vbo_data[current_vertex] =
quad[OUTPUT_QUAD_TO_FAN(k)]; quad[OUTPUT_QUAD_TO_FAN(k)];
@ -273,12 +294,12 @@ static void M_PrepareLevelBuffers(void)
M_PrepareBuffer(&m_Dynamic.sprite_buf, 500, GL_DYNAMIC_DRAW); M_PrepareBuffer(&m_Dynamic.sprite_buf, 500, GL_DYNAMIC_DRAW);
M_PrepareBuffer( M_PrepareBuffer(
&m_LevelData.sprite_buf, num_quads * OUTPUT_QUAD_VERTICES, &m_LevelData.sprite_buf, num_quads * OUTPUT_QUAD_VERTICES,
GL_STATIC_DRAW); GL_DYNAMIC_DRAW);
m_LevelData.sprite_buf.vertex_count = num_quads * OUTPUT_QUAD_VERTICES; m_LevelData.sprite_buf.vertex_count = num_quads * OUTPUT_QUAD_VERTICES;
for (int32_t i = 0; i < Room_GetCount(); i++) { for (int32_t i = 0; i < Room_GetCount(); i++) {
const ROOM *const room = Room_Get(i); const ROOM *const room = Room_Get(i);
M_UpdateRoomGeometry(room); M_FillRoomGeometry(room);
} }
M_BufferReallocGPU(&m_LevelData.sprite_buf); M_BufferReallocGPU(&m_LevelData.sprite_buf);
} }
@ -286,11 +307,14 @@ static void M_PrepareLevelBuffers(void)
void Output_Sprites_Init(void) void Output_Sprites_Init(void)
{ {
m_Shader = Output_Shader_Create("shaders/sprites.glsl"); m_Shader = Output_Shader_Create("shaders/sprites.glsl");
m_LevelData.animated_vertices = Vector_Create(sizeof(OUTPUT_VERTEX_RANGE));
m_Dynamic.source = Vector_CreateAtCapacity(sizeof(M_DYNAMIC_SPRITE), 50); m_Dynamic.source = Vector_CreateAtCapacity(sizeof(M_DYNAMIC_SPRITE), 50);
} }
void Output_Sprites_Shutdown(void) void Output_Sprites_Shutdown(void)
{ {
Vector_Free(m_LevelData.animated_vertices);
Vector_Free(m_Dynamic.source);
M_FreeBuffers(); M_FreeBuffers();
Output_Shader_Free(m_Shader); Output_Shader_Free(m_Shader);
m_Shader = nullptr; m_Shader = nullptr;
@ -303,6 +327,39 @@ void Output_Sprites_ObserveLevelLoad(void)
M_PrepareLevelBatches(); M_PrepareLevelBatches();
} }
void Output_Sprites_ObserveTextureAnimation(void)
{
int32_t current_vertex = 0;
for (int32_t i = 0; i < Room_GetCount(); i++) {
const ROOM *const room = Room_Get(i);
for (int32_t j = 0; j < room->mesh.num_sprites; j++) {
const ROOM_SPRITE *const room_sprite = &room->mesh.sprites[j];
const int16_t sprite_idx = room_sprite->texture;
if (!Output_Textures_IsSpriteTextureAnimated(sprite_idx)) {
current_vertex += OUTPUT_QUAD_VERTICES;
continue;
}
for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) {
m_LevelData.sprite_buf.geom_vbo_data[current_vertex].uvw =
Output_Textures_GetUVW(
Output_Textures_GetSpritesUVWsBase() + sprite_idx * 4
+ OUTPUT_QUAD_TO_FAN(k));
current_vertex++;
}
}
}
glBindBuffer(GL_ARRAY_BUFFER, m_LevelData.sprite_buf.geom_vbo);
for (int32_t i = 0; i < m_LevelData.animated_vertices->count; i++) {
const OUTPUT_VERTEX_RANGE *const range =
Vector_Get(m_LevelData.animated_vertices, i);
GFX_TRACK_DATA(
glBufferSubData, GL_ARRAY_BUFFER,
range->vertex_start * sizeof(M_SPRITE_VERTEX),
range->vertex_count * sizeof(M_SPRITE_VERTEX),
&m_LevelData.sprite_buf.geom_vbo_data[range->vertex_start]);
}
}
void Output_Sprites_RenderRoomSprites( void Output_Sprites_RenderRoomSprites(
const MATRIX *const matrix, const RGB_F tint, const ROOM *const room) const MATRIX *const matrix, const RGB_F tint, const ROOM *const room)
{ {

View file

@ -8,6 +8,7 @@
void Output_Sprites_Init(void); void Output_Sprites_Init(void);
void Output_Sprites_Shutdown(void); void Output_Sprites_Shutdown(void);
void Output_Sprites_ObserveLevelLoad(void); void Output_Sprites_ObserveLevelLoad(void);
void Output_Sprites_ObserveTextureAnimation(void);
void Output_Sprites_UploadProjectionMatrix(void); void Output_Sprites_UploadProjectionMatrix(void);
void Output_Sprites_RenderBegin(void); void Output_Sprites_RenderBegin(void);

View file

@ -1,5 +1,8 @@
#include "game/output.h" #include "game/output.h"
#include "game/output/meshes/common.h" #include "game/output/meshes/common.h"
#include "game/output/meshes/objects.h"
#include "game/output/meshes/rooms.h"
#include "game/output/sprites.h"
#include "game/output/textures.h" #include "game/output/textures.h"
#include "global/vars.h" #include "global/vars.h"
@ -145,5 +148,8 @@ void Output_AnimateTextures(const int32_t num_frames)
} }
if (update) { if (update) {
Output_Textures_CycleAnimations(); Output_Textures_CycleAnimations();
Output_Sprites_ObserveTextureAnimation();
Output_Meshes_ObserveTextureAnimationRooms();
Output_Meshes_ObserveTextureAnimationObjects();
} }
} }

View file

@ -1,6 +1,7 @@
#include "game/output/textures.h" #include "game/output/textures.h"
#include "game/output.h" #include "game/output.h"
#include "game/output/vertex_range.h"
#include <libtrx/debug.h> #include <libtrx/debug.h>
#include <libtrx/gfx/gl/utils.h> #include <libtrx/gfx/gl/utils.h>
@ -8,35 +9,13 @@
#include <stdlib.h> #include <stdlib.h>
#pragma pack(push, 1)
typedef struct { typedef struct {
float x0, y0, x1, y1; OUTPUT_UVW corners[4];
} M_ATLAS_SIZE;
#pragma pack(pop)
typedef struct {
int32_t index;
int32_t count;
} M_ANIMATION_RANGE;
typedef struct {
int32_t range_count;
M_ANIMATION_RANGE *ranges;
} M_ANIMATION_RANGES;
typedef struct {
float u;
float v;
float w;
} M_UVW;
typedef struct {
M_UVW corners[4];
} M_UVW_PACK; } M_UVW_PACK;
static struct { static struct {
M_ANIMATION_RANGES objects; VECTOR *objects;
M_ANIMATION_RANGES sprites; VECTOR *sprites;
} m_AnimationRanges; } m_AnimationRanges;
static struct { static struct {
@ -44,139 +23,117 @@ static struct {
GLuint tex_env_map; GLuint tex_env_map;
struct { struct {
GLuint tbo; // buffer to hold UV data
GLuint tex; // texture to hold UV buffer
int32_t count; int32_t count;
int32_t count_objects; int32_t count_objects;
int32_t count_sprites; int32_t count_sprites;
M_UVW_PACK *data; M_UVW_PACK *data;
M_UVW_PACK *data_objects; M_UVW_PACK *data_objects;
M_UVW_PACK *data_sprites; M_UVW_PACK *data_sprites;
bool *animated_objects;
bool *animated_sprites;
} uvws; } uvws;
struct { struct {
GLuint tex; OUTPUT_TEXTURE_SIZE *data;
GLuint tbo; OUTPUT_TEXTURE_SIZE *data_objects;
M_ATLAS_SIZE *data; OUTPUT_TEXTURE_SIZE *data_sprites;
M_ATLAS_SIZE *data_objects;
M_ATLAS_SIZE *data_sprites;
} atlas_sizes; } atlas_sizes;
} m_Priv = {}; } m_Priv = {};
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;
}
static void M_MergeAndGlueAnimationRanges(M_ANIMATION_RANGES *const source)
{
ASSERT(source != nullptr);
if (source->range_count == 0) {
return;
}
// Sort ranges by index
qsort(
source->ranges, source->range_count, sizeof(M_ANIMATION_RANGE),
M_CompareAnimationRange);
// Initialize a new index to store the merged ranges
int32_t new_range_count = 0;
// Iterate over sorted ranges and merge them
for (int32_t i = 0; i < source->range_count; i++) {
if (new_range_count == 0) {
// First range - just copy it
source->ranges[new_range_count++] = source->ranges[i];
} else {
// Check if the previous range can be merged with the current one
M_ANIMATION_RANGE *const last_range =
&source->ranges[new_range_count - 1];
const int32_t last_start = last_range->index;
const int32_t last_end = last_range->index + last_range->count;
const int32_t current_start = source->ranges[i].index;
const int32_t current_end =
source->ranges[i].index + source->ranges[i].count;
if (current_start >= last_start && current_start <= last_end) {
last_range->count = current_end - last_range->index;
} else if (current_end >= last_start && current_end <= last_end) {
last_range->index = source->ranges[i].index;
} else {
source->ranges[new_range_count++] = source->ranges[i];
}
}
}
// Update the range count with the new number of merged ranges
source->range_count = new_range_count;
}
static void M_PrepareObjectAnimationRanges(void) static void M_PrepareObjectAnimationRanges(void)
{ {
m_AnimationRanges.objects.range_count = 0; size_t required_size = 0;
for (const ANIMATED_TEXTURE_RANGE *src_range = for (const ANIMATED_TEXTURE_RANGE *src_range =
Output_GetAnimatedTextureRange(0); Output_GetAnimatedTextureRange(0);
src_range != nullptr; src_range = src_range->next_range) { src_range != nullptr; src_range = src_range->next_range) {
m_AnimationRanges.objects.range_count += src_range->num_textures; required_size += src_range->num_textures;
} }
m_AnimationRanges.objects.ranges = Memory_Realloc( Vector_Clear(m_AnimationRanges.objects);
m_AnimationRanges.objects.ranges, Vector_EnsureCapacity(m_AnimationRanges.objects, required_size);
m_AnimationRanges.objects.range_count * sizeof(M_ANIMATION_RANGE));
M_ANIMATION_RANGE *dst_range = m_AnimationRanges.objects.ranges;
for (const ANIMATED_TEXTURE_RANGE *src_range = for (const ANIMATED_TEXTURE_RANGE *src_range =
Output_GetAnimatedTextureRange(0); Output_GetAnimatedTextureRange(0);
src_range != nullptr; src_range = src_range->next_range) { src_range != nullptr; src_range = src_range->next_range) {
for (int32_t i = 0; i < src_range->num_textures; i++) { for (int32_t i = 0; i < src_range->num_textures; i++) {
dst_range->index = src_range->textures[i]; Vector_Add(
dst_range->count = 1; m_AnimationRanges.objects,
dst_range++; &(OUTPUT_VERTEX_RANGE) {
.vertex_start = src_range->textures[i],
.vertex_count = 1,
});
} }
} }
M_MergeAndGlueAnimationRanges(&m_AnimationRanges.objects); Output_GlueVertexRanges(m_AnimationRanges.objects);
} }
static void M_PrepareSpriteAnimationRanges(void) static void M_PrepareSpriteAnimationRanges(void)
{ {
m_AnimationRanges.sprites.range_count = 0; size_t required_size = 0;
for (int32_t i = 0; i < MAX_STATIC_OBJECTS; i++) { for (int32_t i = 0; i < MAX_STATIC_OBJECTS; i++) {
const STATIC_OBJECT_2D *const obj = Object_Get2DStatic(i); const STATIC_OBJECT_2D *const obj = Object_Get2DStatic(i);
if (!obj->loaded || obj->frame_count == 1) { if (!obj->loaded || obj->frame_count == 1) {
continue; continue;
} }
m_AnimationRanges.sprites.range_count++; required_size++;
} }
m_AnimationRanges.sprites.ranges = Memory_Realloc( Vector_Clear(m_AnimationRanges.sprites);
m_AnimationRanges.sprites.ranges, Vector_EnsureCapacity(m_AnimationRanges.sprites, required_size);
m_AnimationRanges.sprites.range_count * sizeof(M_ANIMATION_RANGE));
M_ANIMATION_RANGE *dst_range = m_AnimationRanges.sprites.ranges;
for (int32_t i = 0; i < MAX_STATIC_OBJECTS; i++) { for (int32_t i = 0; i < MAX_STATIC_OBJECTS; i++) {
const STATIC_OBJECT_2D *const obj = Object_Get2DStatic(i); const STATIC_OBJECT_2D *const obj = Object_Get2DStatic(i);
if (!obj->loaded || obj->frame_count == 1) { if (!obj->loaded || obj->frame_count == 1) {
continue; continue;
} }
dst_range->index = obj->texture_idx; Vector_Add(
dst_range->count = obj->frame_count; m_AnimationRanges.sprites,
dst_range++; &(OUTPUT_VERTEX_RANGE) {
.vertex_start = obj->texture_idx,
.vertex_count = obj->frame_count,
});
} }
M_MergeAndGlueAnimationRanges(&m_AnimationRanges.sprites); Output_GlueVertexRanges(m_AnimationRanges.sprites);
} }
static void M_PrepareAnimationRanges(void) static void M_PrepareAnimationRanges(void)
{ {
M_PrepareObjectAnimationRanges(); M_PrepareObjectAnimationRanges();
M_PrepareSpriteAnimationRanges(); M_PrepareSpriteAnimationRanges();
for (int32_t i = 0; i < Output_GetObjectTextureCount(); i++) {
m_Priv.uvws.animated_objects[i] = false;
for (int32_t j = 0; j < m_AnimationRanges.objects->count; j++) {
const OUTPUT_VERTEX_RANGE *const dst_range =
Vector_Get(m_AnimationRanges.objects, j);
const int32_t range_start = dst_range->vertex_start;
const int32_t range_end = range_start + dst_range->vertex_count;
if (i >= range_start && i < range_end) {
m_Priv.uvws.animated_objects[i] = true;
break;
}
}
}
for (int32_t i = 0; i < Output_GetSpriteTextureCount(); i++) {
m_Priv.uvws.animated_sprites[i] = false;
for (int32_t j = 0; j < m_AnimationRanges.sprites->count; j++) {
const OUTPUT_VERTEX_RANGE *const dst_range =
Vector_Get(m_AnimationRanges.sprites, j);
const int32_t range_start = dst_range->vertex_start;
const int32_t range_end = range_start + dst_range->vertex_count;
if (i >= range_start && i < range_end) {
m_Priv.uvws.animated_sprites[i] = true;
break;
}
}
}
} }
static void M_FillAtlasObjectSize(const int32_t i) static void M_FillAtlasObjectSize(const int32_t i)
{ {
M_ATLAS_SIZE *const size = &m_Priv.atlas_sizes.data_objects[i]; OUTPUT_TEXTURE_SIZE *const size = &m_Priv.atlas_sizes.data_objects[i];
const OBJECT_TEXTURE *const texture = Output_GetObjectTexture(i); const OBJECT_TEXTURE *const texture = Output_GetObjectTexture(i);
size->x0 = texture->uv[0].u; size->x0 = texture->uv[0].u;
size->y0 = texture->uv[0].v; size->y0 = texture->uv[0].v;
@ -196,7 +153,7 @@ static void M_FillAtlasObjectSize(const int32_t i)
static void M_FillAtlasSpriteSize(const int32_t i) static void M_FillAtlasSpriteSize(const int32_t i)
{ {
M_ATLAS_SIZE *const size = &m_Priv.atlas_sizes.data_sprites[i]; OUTPUT_TEXTURE_SIZE *const size = &m_Priv.atlas_sizes.data_sprites[i];
const SPRITE_TEXTURE *const sprite = Output_GetSpriteTexture(i); const SPRITE_TEXTURE *const sprite = Output_GetSpriteTexture(i);
const float adj = 0.1 / 256.0f; const float adj = 0.1 / 256.0f;
const float u0 = (sprite->offset & 0xFF) / 256.0f + adj; const float u0 = (sprite->offset & 0xFF) / 256.0f + adj;
@ -212,7 +169,7 @@ static void M_FillAtlasSpriteSize(const int32_t i)
static void M_FillObjectUVW(const int32_t i) static void M_FillObjectUVW(const int32_t i)
{ {
const OBJECT_TEXTURE *const texture = Output_GetObjectTexture(i); const OBJECT_TEXTURE *const texture = Output_GetObjectTexture(i);
M_UVW *const corners = m_Priv.uvws.data_objects[i].corners; OUTPUT_UVW *const corners = m_Priv.uvws.data_objects[i].corners;
for (int32_t j = 0; j < 4; j++) { for (int32_t j = 0; j < 4; j++) {
corners[j].u = texture->uv[j].u / 65535.0f; corners[j].u = texture->uv[j].u / 65535.0f;
corners[j].v = texture->uv[j].v / 65535.0f; corners[j].v = texture->uv[j].v / 65535.0f;
@ -228,7 +185,7 @@ static void M_FillSpriteUVW(const int32_t i)
const float v0 = (sprite->offset >> 8) / 256.0f + adj; const float v0 = (sprite->offset >> 8) / 256.0f + adj;
const float u1 = u0 + sprite->width / 65536.0f - 2 * adj; const float u1 = u0 + sprite->width / 65536.0f - 2 * adj;
const float v1 = v0 + sprite->height / 65536.0f - 2 * adj; const float v1 = v0 + sprite->height / 65536.0f - 2 * adj;
M_UVW *const corners = m_Priv.uvws.data_sprites[i].corners; OUTPUT_UVW *const corners = m_Priv.uvws.data_sprites[i].corners;
// clang-format off // clang-format off
corners[0].u = u0; corners[0].v = v0; corners[0].w = sprite->tex_page; 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; corners[1].u = u1; corners[1].v = v0; corners[1].w = sprite->tex_page;
@ -251,78 +208,29 @@ static void M_FillSpriteUVWs(void)
} }
} }
static void M_UploadUVWs(void) static void M_UpdateObjectAnimatedUVWs(VECTOR *const source)
{ {
glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.uvws.tbo); for (int32_t i = 0; i < source->count; i++) {
GFX_TRACK_DATA( const OUTPUT_VERTEX_RANGE *const range = Vector_Get(source, i);
glBufferData, GL_TEXTURE_BUFFER, m_Priv.uvws.count * sizeof(M_UVW_PACK), for (int32_t j = 0; j < range->vertex_count; j++) {
m_Priv.uvws.data, GL_DYNAMIC_DRAW); M_FillObjectUVW(range->vertex_start + j);
GFX_GL_CheckError(); M_FillAtlasObjectSize(range->vertex_start + j);
}
static void M_UploadObjectAnimatedUVWs(const M_ANIMATION_RANGES *const source)
{
glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.uvws.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);
} }
const M_UVW_PACK *const source =
m_Priv.uvws.data_objects + range->index;
GFX_TRACK_DATA(
glBufferSubData, GL_TEXTURE_BUFFER,
(source - m_Priv.uvws.data) * sizeof(M_UVW_PACK),
range->count * sizeof(M_UVW_PACK), source);
}
glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.atlas_sizes.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_FillAtlasObjectSize(range->index + j);
}
const M_ATLAS_SIZE *const source =
m_Priv.atlas_sizes.data_objects + range->index;
GFX_TRACK_DATA(
glBufferSubData, GL_TEXTURE_BUFFER,
(source - m_Priv.atlas_sizes.data) * sizeof(M_ATLAS_SIZE),
range->count * sizeof(M_ATLAS_SIZE), source);
} }
} }
static void M_UploadSpriteAnimatedUVWs(const M_ANIMATION_RANGES *const source) static void M_UpdateSpriteAnimatedUVWs(VECTOR *const source)
{ {
glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.uvws.tbo); for (int32_t i = 0; i < source->count; i++) {
for (int32_t i = 0; i < source->range_count; i++) { const OUTPUT_VERTEX_RANGE *const range = Vector_Get(source, i);
const M_ANIMATION_RANGE *const range = &source->ranges[i]; for (int32_t j = 0; j < range->vertex_count; j++) {
for (int32_t j = 0; j < range->count; j++) { M_FillSpriteUVW(range->vertex_start + j);
M_FillSpriteUVW(range->index + j); M_FillAtlasSpriteSize(range->vertex_start + j);
} }
const M_UVW_PACK *const source =
m_Priv.uvws.data_sprites + range->index;
GFX_TRACK_DATA(
glBufferSubData, GL_TEXTURE_BUFFER,
(source - m_Priv.uvws.data) * sizeof(M_UVW_PACK),
range->count * sizeof(M_UVW_PACK), source);
}
glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.atlas_sizes.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_FillAtlasSpriteSize(range->index + j);
}
const M_ATLAS_SIZE *const source =
m_Priv.atlas_sizes.data_sprites + range->index;
GFX_TRACK_DATA(
glBufferSubData, GL_TEXTURE_BUFFER,
(source - m_Priv.atlas_sizes.data) * sizeof(M_ATLAS_SIZE),
range->count * sizeof(M_ATLAS_SIZE), source);
} }
} }
static void M_PrepareUVWBuffers(void) static void M_PrepareUVWs(void)
{ {
m_Priv.uvws.count_objects = Output_GetObjectTextureCount(); m_Priv.uvws.count_objects = Output_GetObjectTextureCount();
m_Priv.uvws.count_sprites = Output_GetSpriteTextureCount(); m_Priv.uvws.count_sprites = Output_GetSpriteTextureCount();
@ -330,23 +238,10 @@ static void M_PrepareUVWBuffers(void)
m_Priv.uvws.data = Memory_Alloc(m_Priv.uvws.count * sizeof(M_UVW_PACK)); m_Priv.uvws.data = Memory_Alloc(m_Priv.uvws.count * sizeof(M_UVW_PACK));
m_Priv.uvws.data_objects = m_Priv.uvws.data; m_Priv.uvws.data_objects = m_Priv.uvws.data;
m_Priv.uvws.data_sprites = m_Priv.uvws.data + m_Priv.uvws.count_objects; m_Priv.uvws.data_sprites = m_Priv.uvws.data + m_Priv.uvws.count_objects;
m_Priv.uvws.animated_objects =
glGenBuffers(1, &m_Priv.uvws.tbo); Memory_Alloc(m_Priv.uvws.count_objects * sizeof(bool));
glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.uvws.tbo); m_Priv.uvws.animated_sprites =
Memory_Alloc(m_Priv.uvws.count_sprites * sizeof(bool));
GLint limit;
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &limit);
ASSERT(m_Priv.uvws.count * sizeof(M_UVW_PACK) <= (size_t)limit);
glGenTextures(1, &m_Priv.uvws.tex);
glBindTexture(GL_TEXTURE_BUFFER, m_Priv.uvws.tex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, m_Priv.uvws.tbo);
GFX_GL_CheckError();
}
static void M_PrepareUVWs(void)
{
M_PrepareUVWBuffers();
M_FillObjectUVWs(); M_FillObjectUVWs();
M_FillSpriteUVWs(); M_FillSpriteUVWs();
} }
@ -364,16 +259,11 @@ static void M_PrepareEnvMap(void)
static void M_PrepareAtlasSizes(void) static void M_PrepareAtlasSizes(void)
{ {
glGenBuffers(1, &m_Priv.atlas_sizes.tbo);
glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.atlas_sizes.tbo);
glGenTextures(1, &m_Priv.atlas_sizes.tex);
glBindTexture(GL_TEXTURE_BUFFER, m_Priv.atlas_sizes.tex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_Priv.atlas_sizes.tbo);
const int32_t count_objects = Output_GetObjectTextureCount(); const int32_t count_objects = Output_GetObjectTextureCount();
const int32_t count_sprites = Output_GetSpriteTextureCount(); const int32_t count_sprites = Output_GetSpriteTextureCount();
const int32_t count = count_objects + count_sprites; const int32_t count = count_objects + count_sprites;
m_Priv.atlas_sizes.data = m_Priv.atlas_sizes.data = Memory_Realloc(
Memory_Realloc(m_Priv.atlas_sizes.data, count * sizeof(M_ATLAS_SIZE)); m_Priv.atlas_sizes.data, count * sizeof(OUTPUT_TEXTURE_SIZE));
m_Priv.atlas_sizes.data_objects = m_Priv.atlas_sizes.data; m_Priv.atlas_sizes.data_objects = m_Priv.atlas_sizes.data;
m_Priv.atlas_sizes.data_sprites = m_Priv.atlas_sizes.data + count_objects; m_Priv.atlas_sizes.data_sprites = m_Priv.atlas_sizes.data + count_objects;
for (int32_t i = 0; i < count_objects; i++) { for (int32_t i = 0; i < count_objects; i++) {
@ -382,9 +272,6 @@ static void M_PrepareAtlasSizes(void)
for (int32_t i = 0; i < count_sprites; i++) { for (int32_t i = 0; i < count_sprites; i++) {
M_FillAtlasSpriteSize(i); M_FillAtlasSpriteSize(i);
} }
GFX_TRACK_DATA(
glBufferData, GL_TEXTURE_BUFFER, count * sizeof(M_ATLAS_SIZE),
m_Priv.atlas_sizes.data, GL_DYNAMIC_DRAW);
} }
static void M_UploadAtlas(void) static void M_UploadAtlas(void)
@ -428,38 +315,27 @@ static void M_FreeLevelData(void)
{ {
glBindTexture(GL_TEXTURE_BUFFER, 0); glBindTexture(GL_TEXTURE_BUFFER, 0);
glBindBuffer(GL_TEXTURE_BUFFER, 0); glBindBuffer(GL_TEXTURE_BUFFER, 0);
if (m_Priv.uvws.tbo != 0) {
glDeleteBuffers(1, &m_Priv.uvws.tbo);
m_Priv.uvws.tbo = 0;
}
if (m_Priv.uvws.tex != 0) {
glDeleteTextures(1, &m_Priv.uvws.tex);
m_Priv.uvws.tex = 0;
}
if (m_Priv.tex_atlas != 0) { if (m_Priv.tex_atlas != 0) {
glDeleteTextures(1, &m_Priv.tex_atlas); glDeleteTextures(1, &m_Priv.tex_atlas);
m_Priv.tex_atlas = 0; m_Priv.tex_atlas = 0;
} }
if (m_Priv.atlas_sizes.tex != 0) { Memory_FreePointer(&m_Priv.uvws.data);
glDeleteTextures(1, &m_Priv.atlas_sizes.tex); Memory_FreePointer(&m_Priv.uvws.animated_objects);
m_Priv.atlas_sizes.tex = 0; Memory_FreePointer(&m_Priv.uvws.animated_sprites);
}
if (m_Priv.atlas_sizes.tbo != 0) {
glDeleteBuffers(1, &m_Priv.atlas_sizes.tbo);
m_Priv.atlas_sizes.tbo = 0;
}
Memory_FreePointer(&m_Priv.atlas_sizes.data); Memory_FreePointer(&m_Priv.atlas_sizes.data);
Memory_FreePointer(&m_AnimationRanges.objects.ranges);
Memory_FreePointer(&m_AnimationRanges.sprites.ranges);
} }
void Output_Textures_Init(void) void Output_Textures_Init(void)
{ {
M_PrepareEnvMap(); M_PrepareEnvMap();
m_AnimationRanges.objects = Vector_Create(sizeof(OUTPUT_VERTEX_RANGE));
m_AnimationRanges.sprites = Vector_Create(sizeof(OUTPUT_VERTEX_RANGE));
} }
void Output_Textures_Shutdown(void) void Output_Textures_Shutdown(void)
{ {
Vector_Free(m_AnimationRanges.objects);
Vector_Free(m_AnimationRanges.sprites);
M_FreeLevelData(); M_FreeLevelData();
if (m_Priv.tex_env_map != 0) { if (m_Priv.tex_env_map != 0) {
glDeleteTextures(1, &m_Priv.tex_env_map); glDeleteTextures(1, &m_Priv.tex_env_map);
@ -470,9 +346,8 @@ void Output_Textures_Shutdown(void)
void Output_Textures_ObserveLevelLoad(void) void Output_Textures_ObserveLevelLoad(void)
{ {
M_FreeLevelData(); M_FreeLevelData();
M_PrepareAnimationRanges();
M_PrepareUVWs(); M_PrepareUVWs();
M_UploadUVWs(); M_PrepareAnimationRanges();
M_UploadAtlas(); M_UploadAtlas();
} }
@ -500,27 +375,17 @@ void Output_Textures_UpdateEnvironmentMap(void)
void Output_Textures_CycleAnimations(void) void Output_Textures_CycleAnimations(void)
{ {
if (m_Priv.uvws.tex != 0) { if (m_Priv.uvws.count != 0) {
M_UploadSpriteAnimatedUVWs(&m_AnimationRanges.sprites); M_UpdateSpriteAnimatedUVWs(m_AnimationRanges.sprites);
M_UploadObjectAnimatedUVWs(&m_AnimationRanges.objects); M_UpdateObjectAnimatedUVWs(m_AnimationRanges.objects);
} }
} }
GLuint Output_Textures_GetUVWsTexture(void)
{
return m_Priv.uvws.tex;
}
GLuint Output_Textures_GetAtlasTexture(void) GLuint Output_Textures_GetAtlasTexture(void)
{ {
return m_Priv.tex_atlas; return m_Priv.tex_atlas;
} }
GLuint Output_Textures_GetAtlasSizesTexture(void)
{
return m_Priv.atlas_sizes.tex;
}
GLuint Output_Textures_GetEnvMapTexture(void) GLuint Output_Textures_GetEnvMapTexture(void)
{ {
return m_Priv.tex_env_map; return m_Priv.tex_env_map;
@ -528,16 +393,35 @@ GLuint Output_Textures_GetEnvMapTexture(void)
int32_t Output_Textures_GetSpritesUVWsBase(void) int32_t Output_Textures_GetSpritesUVWsBase(void)
{ {
const size_t num = sizeof(M_UVW_PACK) / sizeof(M_UVW); const size_t num = sizeof(M_UVW_PACK) / sizeof(OUTPUT_UVW);
ASSERT(num == 4); ASSERT(num == 4);
return m_Priv.uvws.count_objects * num; return m_Priv.uvws.count_objects * num;
} }
OUTPUT_UVW Output_Textures_GetUVW(const int32_t uvw_idx)
{
return m_Priv.uvws.data[uvw_idx / 4].corners[uvw_idx % 4];
}
OUTPUT_TEXTURE_SIZE Output_Textures_GetAtlasSize(const int32_t uvw_idx)
{
return m_Priv.atlas_sizes.data[uvw_idx];
}
bool Output_Textures_IsObjectTextureAnimated(const int32_t texture_idx)
{
return m_Priv.uvws.animated_objects[texture_idx];
}
bool Output_Textures_IsSpriteTextureAnimated(const int32_t uvw_idx)
{
return m_Priv.uvws.animated_sprites[uvw_idx];
}
void Output_Textures_ApplyRenderSettings(void) void Output_Textures_ApplyRenderSettings(void)
{ {
// re-adjust UVs when the bilinear filter is toggled. // re-adjust UVs when the bilinear filter is toggled.
if (m_Priv.uvws.tex != 0) { if (m_Priv.uvws.count != 0) {
M_FillObjectUVWs(); M_FillObjectUVWs();
M_UploadUVWs();
} }
} }

View file

@ -2,14 +2,32 @@
#include <libtrx/gfx/gl/utils.h> #include <libtrx/gfx/gl/utils.h>
#pragma pack(push, 1)
typedef struct {
float x0;
float y0;
float x1;
float y1;
} OUTPUT_TEXTURE_SIZE;
typedef struct {
float u;
float v;
float w;
} OUTPUT_UVW;
#pragma pack(pop)
void Output_Textures_Init(void); void Output_Textures_Init(void);
void Output_Textures_Shutdown(void); void Output_Textures_Shutdown(void);
void Output_Textures_ObserveLevelLoad(void); void Output_Textures_ObserveLevelLoad(void);
void Output_Textures_UpdateEnvironmentMap(void); void Output_Textures_UpdateEnvironmentMap(void);
void Output_Textures_CycleAnimations(void); void Output_Textures_CycleAnimations(void);
void Output_Textures_ApplyRenderSettings(void); void Output_Textures_ApplyRenderSettings(void);
GLuint Output_Textures_GetUVWsTexture(void);
GLuint Output_Textures_GetAtlasTexture(void); GLuint Output_Textures_GetAtlasTexture(void);
GLuint Output_Textures_GetAtlasSizesTexture(void);
GLuint Output_Textures_GetEnvMapTexture(void); GLuint Output_Textures_GetEnvMapTexture(void);
int32_t Output_Textures_GetSpritesUVWsBase(void); int32_t Output_Textures_GetSpritesUVWsBase(void);
OUTPUT_UVW Output_Textures_GetUVW(int32_t uvw_idx);
OUTPUT_TEXTURE_SIZE Output_Textures_GetAtlasSize(int32_t uvw_idx);
bool Output_Textures_IsObjectTextureAnimated(int32_t texture_idx);
bool Output_Textures_IsSpriteTextureAnimated(int32_t sprite_idx);

View file

@ -0,0 +1,59 @@
#include "game/output/vertex_range.h"
#include <libtrx/debug.h>
#include <stdlib.h>
static int M_CompareRanges(const void *const a, const void *const b)
{
const OUTPUT_VERTEX_RANGE *const range_a = (OUTPUT_VERTEX_RANGE *)a;
const OUTPUT_VERTEX_RANGE *const range_b = (OUTPUT_VERTEX_RANGE *)b;
return range_a->vertex_start - range_b->vertex_start;
}
void Output_GlueVertexRanges(VECTOR *const target)
{
ASSERT(target != nullptr);
if (target->count == 0) {
return;
}
OUTPUT_VERTEX_RANGE *const ranges =
(OUTPUT_VERTEX_RANGE *)Vector_Get(target, 0);
qsort(ranges, target->count, sizeof(OUTPUT_VERTEX_RANGE), M_CompareRanges);
// Initialize a new index to store the merged ranges
int32_t new_range_count = 0;
// Iterate over sorted ranges and merge them
for (int32_t i = 0; i < target->count; i++) {
if (new_range_count == 0) {
// First range - just copy it
ranges[new_range_count] = ranges[i];
new_range_count++;
} else {
// Check if the previous range can be merged with the current one
OUTPUT_VERTEX_RANGE *const last_range =
&ranges[new_range_count - 1];
const int32_t last_start = last_range->vertex_start;
const int32_t last_end =
last_range->vertex_start + last_range->vertex_count;
const int32_t current_start = ranges[i].vertex_start;
const int32_t current_end =
ranges[i].vertex_start + ranges[i].vertex_count;
if (current_start >= last_start && current_start <= last_end) {
last_range->vertex_count =
current_end - last_range->vertex_start;
} else if (current_end >= last_start && current_end <= last_end) {
last_range->vertex_start = ranges[i].vertex_start;
} else {
ranges[new_range_count++] = ranges[i];
}
}
}
// Update the range vertex_count with the new number of merged ranges
target->count = new_range_count;
}

View file

@ -0,0 +1,10 @@
#pragma once
#include <libtrx/vector.h>
typedef struct {
int32_t vertex_start;
int32_t vertex_count;
} OUTPUT_VERTEX_RANGE;
void Output_GlueVertexRanges(VECTOR *vertex_range);

View file

@ -254,6 +254,7 @@ sources = [
'game/output/sprites.c', 'game/output/sprites.c',
'game/output/state.c', 'game/output/state.c',
'game/output/textures.c', 'game/output/textures.c',
'game/output/vertex_range.c',
'game/overlay.c', 'game/overlay.c',
'game/requester.c', 'game/requester.c',
'game/room.c', 'game/room.c',

View file

@ -434,6 +434,10 @@ void Output_ObserveLevelUnload(void)
} }
} }
void Output_ObserveRoomFlip(const ROOM *room)
{
}
void Output_DrawObjectMesh(const OBJECT_MESH *const mesh, const int32_t clip) void Output_DrawObjectMesh(const OBJECT_MESH *const mesh, const int32_t clip)
{ {
g_FltWinLeft = 0.0f; g_FltWinLeft = 0.0f;