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

View file

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

View file

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

View file

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

View file

@ -15,6 +15,7 @@ typedef struct {
VECTOR *Vector_Create(size_t item_size);
VECTOR *Vector_CreateAtCapacity(size_t item_size, int32_t capacity);
void Vector_EnsureCapacity(VECTOR *vector, int32_t capacity);
void Vector_Free(VECTOR *vector);
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);
void *Vector_Get(VECTOR *vector, int32_t index);
void *Vector_GetData(VECTOR *vector);
void Vector_Add(VECTOR *vector, 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);

View file

@ -42,6 +42,16 @@ VECTOR *Vector_CreateAtCapacity(const size_t item_size, const int32_t capacity)
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)
{
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);
}
void *Vector_GetData(VECTOR *const vector)
{
return P(vector).items;
}
void Vector_Add(VECTOR *const vector, const void *const item)
{
M_EnsureCapacity(vector, 1);

View file

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

View file

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

View file

@ -1,6 +1,7 @@
#pragma once
#include "game/output/shader.h"
#include "game/output/textures.h"
// clang-format off
#define VERT_NO_CAUSTICS 0b0000'0001 // = 0x01
@ -9,6 +10,15 @@
#define VERT_NO_LIGHTING 0b0000'1000 // = 0x08
// 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_Shutdown(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));
glEnableVertexAttribArray(2);
glVertexAttribIPointer(
2, 1, GL_UNSIGNED_INT, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, uvw_idx));
glVertexAttribPointer(
2, 3, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, uvw));
glEnableVertexAttribArray(3);
glVertexAttribPointer(
3, 2, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, trapezoid_ratio));
3, 4, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, texture_size));
glEnableVertexAttribArray(4);
glVertexAttribIPointer(
4, 1, GL_UNSIGNED_SHORT, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, flags));
glVertexAttribPointer(
4, 2, GL_FLOAT, GL_FALSE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, trapezoid_ratio));
glEnableVertexAttribArray(5);
glVertexAttribPointer(
5, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, color));
glVertexAttribIPointer(
5, 1, GL_UNSIGNED_SHORT, sizeof(OUTPUT_MESH_VERTEX),
(void *)(intptr_t)offsetof(OUTPUT_MESH_VERTEX, flags));
glEnableVertexAttribArray(6);
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));
}

View file

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

View file

@ -4,38 +4,45 @@
#include "game/output/meshes/common.h"
#include "game/output/textures.h"
#include "game/output/utils.h"
#include "game/output/vertex_range.h"
#include <libtrx/debug.h>
#include <libtrx/game/output/objects.h>
#include <libtrx/gfx/gl/utils.h>
#include <libtrx/memory.h>
#include <libtrx/vector.h>
#pragma pack(push, 1)
typedef struct {
// clang-format off
XYZ_F pos; // attribute 0
XYZ_F normal; // attribute 1
int32_t uvw_idx; // attribute 2
float trapezoid_ratio[2]; // attribute 3
uint16_t flags; // attribute 4
RGBA_8888 color; // attribute 5
// attribute 0
XYZ_F pos;
// attribute 1
XYZ_F normal;
// attribute 5
uint16_t flags;
// attribute 6
RGBA_8888 color;
// clang-format on
} M_MESH_VERTEX;
// attribute 7
typedef int16_t M_MESH_SHADE;
#pragma pack(pop)
typedef struct {
int32_t vertex_start;
int32_t vertex_count;
VECTOR *animated_vertices;
} M_BATCH;
static struct {
size_t vertex_count;
GLuint vao;
GLuint geom_vbo;
size_t vertex_count;
GLuint tex_vbo;
GLuint shade_vbo;
M_MESH_VERTEX *geom_vbo_data;
OUTPUT_MESH_TEXTURE *tex_vbo_data;
M_MESH_SHADE *shade_vbo_data;
int32_t *light_idx_map;
size_t batch_count;
@ -65,8 +72,18 @@ static uint16_t M_GetFlags(const OBJECT_MESH *const mesh, const bool flat)
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(
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 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->normal =
(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);
if (palette_idx >= 0) {
out_vertex->color =
@ -85,57 +101,63 @@ static void M_FillVertex(
}
}
static M_MESH_VERTEX *M_FillFace4(
const OBJECT_MESH *const mesh, M_MESH_VERTEX *out_vertex,
const FACE4 *const face, const bool flat)
static void M_FillFace4(
const OBJECT_MESH *const mesh, M_MESH_VERTEX **out_vertex,
OUTPUT_MESH_TEXTURE **out_texture, const FACE4 *const face, const bool flat)
{
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;
M_FillVertex(
out_vertex, uvw_idx, mesh, face->vertices[j],
*out_vertex, uvw_idx, mesh, face->vertices[j],
flat ? face->palette_idx : -1);
out_vertex->trapezoid_ratio[0] = face->texture_zw[j].z;
out_vertex->trapezoid_ratio[1] = face->texture_zw[j].w;
out_vertex++;
M_FillTexture(
*out_texture, uvw_idx, face->texture_zw[j].z,
face->texture_zw[j].w);
(*out_vertex)++;
(*out_texture)++;
}
return out_vertex;
}
static M_MESH_VERTEX *M_FillFace3(
const OBJECT_MESH *const mesh, M_MESH_VERTEX *out_vertex,
const FACE3 *const face, const bool flat)
static void M_FillFace3(
const OBJECT_MESH *const mesh, M_MESH_VERTEX **out_vertex,
OUTPUT_MESH_TEXTURE **out_texture, const FACE3 *const face, const bool flat)
{
const M_BATCH *const batch = M_GetBatch(mesh);
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;
M_FillVertex(
out_vertex, uvw_idx, mesh, face->vertices[j],
*out_vertex, uvw_idx, mesh, face->vertices[j],
flat ? face->palette_idx : -1);
out_vertex->trapezoid_ratio[0] = 1.0f;
out_vertex->trapezoid_ratio[1] = 1.0f;
out_vertex++;
M_FillTexture(*out_texture, uvw_idx, 1.0f, 1.0f);
(*out_vertex)++;
(*out_texture)++;
}
return out_vertex;
}
static void M_UpdateGeometry(const OBJECT_MESH *const mesh)
{
const M_BATCH *const batch = M_GetBatch(mesh);
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++) {
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++) {
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++) {
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++) {
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];
@ -241,6 +263,12 @@ static void M_UpdateVertices(void)
m_Priv.vertex_count * sizeof(M_MESH_VERTEX), m_Priv.geom_vbo_data,
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);
GFX_TRACK_DATA(
glBufferData, GL_ARRAY_BUFFER,
@ -268,54 +296,105 @@ static void M_PrepareBuffers(void)
}
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 =
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 =
Memory_Alloc(m_Priv.vertex_count * sizeof(M_MESH_SHADE));
m_Priv.light_idx_map = Memory_Alloc(m_Priv.vertex_count * sizeof(int32_t));
glGenVertexArrays(1, &m_Priv.vao);
glGenBuffers(1, &m_Priv.geom_vbo);
glGenBuffers(1, &m_Priv.tex_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);
// attribute 0: position
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, pos));
// attribute 1: normal
glEnableVertexAttribArray(1);
glVertexAttribPointer(
1, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, normal));
glEnableVertexAttribArray(2);
// attribute 5: flags
glEnableVertexAttribArray(5);
glVertexAttribIPointer(
2, 1, GL_UNSIGNED_INT, 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),
5, 1, GL_UNSIGNED_SHORT, sizeof(M_MESH_VERTEX),
(void *)(intptr_t)offsetof(M_MESH_VERTEX, flags));
glEnableVertexAttribArray(5);
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);
// attribute 6: color
glEnableVertexAttribArray(6);
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();
}
@ -332,13 +411,21 @@ static void M_FreeBuffers(void)
glDeleteBuffers(1, &m_Priv.geom_vbo);
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) {
glDeleteBuffers(1, &m_Priv.shade_vbo);
m_Priv.shade_vbo = 0;
}
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.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);
}
@ -362,6 +449,65 @@ void Output_Meshes_ObserveLevelUnloadObjects(void)
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(
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),
&m_Priv.geom_vbo_data[batch->vertex_start]);
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(
@ -421,11 +575,7 @@ void Output_Meshes_RenderObjectMesh(
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture());
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetUVWsTexture());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture());
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetAtlasSizesTexture());
GFX_GL_CheckError();
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_ObserveLevelLoadObjects(void);
void Output_Meshes_ObserveLevelUnloadObjects(void);
void Output_Meshes_ObserveTextureAnimationObjects(void);
void Output_Meshes_RenderObjectMesh(
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/textures.h"
#include "game/output/utils.h"
#include "game/output/vertex_range.h"
#include "game/random.h"
#include "game/room.h"
#include <libtrx/gfx/gl/utils.h>
#include <libtrx/memory.h>
#include <libtrx/vector.h>
#pragma pack(push, 1)
typedef struct {
// clang-format off
XYZ_F pos; // attribute 0
int32_t uvw_idx; // attribute 2
float trapezoid_ratio[2]; // attribute 3
uint16_t flags; // attribute 4
// clang-format on
// attribute 0
XYZ_F pos;
// attribute 5
uint16_t flags;
} M_MESH_VERTEX;
// attribute 7
typedef int16_t M_MESH_SHADE;
#pragma pack(pop)
@ -27,14 +28,17 @@ typedef struct {
int32_t vertex_start;
int32_t vertex_count;
int16_t *caustics;
VECTOR *animated_vertices;
} M_BATCH;
static struct {
size_t vertex_count;
GLuint vao;
GLuint geom_vbo;
size_t vertex_count;
GLuint tex_vbo;
GLuint shade_vbo;
M_MESH_VERTEX *geom_vbo_data;
OUTPUT_MESH_TEXTURE *tex_vbo_data;
M_MESH_SHADE *shade_vbo_data;
size_t batch_count;
M_BATCH *batches;
@ -69,55 +73,67 @@ static M_BATCH *M_GetBatch(const ROOM *const room)
}
static void M_FillVertex(
M_MESH_VERTEX *const out_vertex, const int32_t uvw_idx, const XYZ_16 pos,
const uint16_t flags)
M_MESH_VERTEX *const out_vertex, 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)
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_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++) {
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++;
M_FillVertex(*out_vertex, room_vertex->pos, room_vertex->flags);
M_FillTexture(
*out_texture, uvw_idx, face->texture_zw[j].z,
face->texture_zw[j].w);
(*out_vertex)++;
(*out_texture)++;
}
return out_vertex;
}
static M_MESH_VERTEX *M_FillRoomFace3(
const ROOM *const room, M_MESH_VERTEX *out_vertex, const FACE3 *const face)
static void M_FillRoomFace3(
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++) {
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++;
M_FillVertex(*out_vertex, room_vertex->pos, room_vertex->flags);
M_FillTexture(*out_texture, uvw_idx, 1.0f, 1.0f);
(*out_vertex)++;
(*out_texture)++;
}
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];
OUTPUT_MESH_TEXTURE *out_texture =
&m_Priv.tex_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]);
M_FillRoomFace4(room, &out_vertex, &out_texture, &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]);
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,
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);
GFX_TRACK_DATA(
glBufferData, GL_ARRAY_BUFFER,
@ -181,10 +203,8 @@ static void M_PrepareBuffers(void)
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;
batch->vertex_count += room->mesh.num_face4s * OUTPUT_QUAD_VERTICES;
batch->vertex_count += room->mesh.num_face3s * OUTPUT_TRI_VERTICES;
last_vertex += batch->vertex_count;
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_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 =
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 =
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.tex_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);
// attribute 0: position
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0, 3, GL_FLOAT, GL_FALSE, sizeof(M_MESH_VERTEX),
(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(
2, 1, GL_UNSIGNED_INT, 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),
5, 1, GL_UNSIGNED_SHORT, sizeof(M_MESH_VERTEX),
(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);
glEnableVertexAttribArray(6);
// attribute 7 (shade)
glEnableVertexAttribArray(7);
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();
}
@ -252,13 +318,19 @@ static void M_FreeBuffers(void)
glDeleteBuffers(1, &m_Priv.geom_vbo);
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) {
glDeleteBuffers(1, &m_Priv.shade_vbo);
m_Priv.shade_vbo = 0;
}
Memory_FreePointer(&m_Priv.geom_vbo_data);
Memory_FreePointer(&m_Priv.tex_vbo_data);
Memory_FreePointer(&m_Priv.shade_vbo_data);
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);
@ -285,6 +357,71 @@ void Output_Meshes_ObserveLevelLoadRooms(void)
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(
const MATRIX *const matrix, const RGB_F tint, const ROOM *const room)
{
@ -311,11 +448,7 @@ void Output_Meshes_RenderRoomMesh(
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture());
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetUVWsTexture());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture());
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetAtlasSizesTexture());
GFX_GL_CheckError();
if (Output_GetWibbleEffect()) {

View file

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

View file

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

View file

@ -4,6 +4,7 @@
#include "game/output/shader.h"
#include "game/output/textures.h"
#include "game/output/utils.h"
#include "game/output/vertex_range.h"
#include "game/room.h"
#include <libtrx/gfx/gl/utils.h>
@ -22,7 +23,7 @@ typedef struct {
} displacement;
// attribute 2
int32_t uvw_idx;
OUTPUT_UVW uvw;
} M_SPRITE_VERTEX;
typedef uint16_t M_SPRITE_SHADE;
@ -57,6 +58,7 @@ static struct {
M_SPRITE_BUFFER sprite_buf;
size_t room_batch_count;
M_ROOM_BATCH *room_batches;
VECTOR *animated_vertices;
} m_LevelData = {};
static struct {
@ -71,8 +73,8 @@ static void M_MakeQuad(
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].uvw_idx =
Output_Textures_GetSpritesUVWsBase() + sprite_idx * 4 + k;
out_quad[k].uvw = Output_Textures_GetUVW(
Output_Textures_GetSpritesUVWsBase() + sprite_idx * 4 + k);
}
out_quad[0].displacement.x = sprite->x0;
@ -144,9 +146,9 @@ static void M_PrepareBuffer(
(void *)(intptr_t)offsetof(M_SPRITE_VERTEX, displacement));
glEnableVertexAttribArray(2);
glVertexAttribIPointer(
2, 1, GL_UNSIGNED_INT, sizeof(M_SPRITE_VERTEX),
(void *)(intptr_t)offsetof(M_SPRITE_VERTEX, uvw_idx));
glVertexAttribPointer(
2, 3, GL_FLOAT, GL_FALSE, sizeof(M_SPRITE_VERTEX),
(void *)(intptr_t)offsetof(M_SPRITE_VERTEX, uvw));
glBindBuffer(GL_ARRAY_BUFFER, buffer->shade_vbo);
glEnableVertexAttribArray(3);
@ -192,8 +194,6 @@ static void M_DrawBuffer(
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, Output_Textures_GetAtlasTexture());
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, Output_Textures_GetUVWsTexture());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, Output_Textures_GetEnvMapTexture());
GFX_GL_CheckError();
@ -214,9 +214,29 @@ static void M_PrepareLevelBatches(void)
room_batch->quad_count = 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;
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_MakeQuad(quad, sprite_idx, pos);
for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) {
m_LevelData.sprite_buf.geom_vbo_data[current_vertex] =
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_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;
for (int32_t i = 0; i < Room_GetCount(); i++) {
const ROOM *const room = Room_Get(i);
M_UpdateRoomGeometry(room);
M_FillRoomGeometry(room);
}
M_BufferReallocGPU(&m_LevelData.sprite_buf);
}
@ -286,11 +307,14 @@ static void M_PrepareLevelBuffers(void)
void Output_Sprites_Init(void)
{
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);
}
void Output_Sprites_Shutdown(void)
{
Vector_Free(m_LevelData.animated_vertices);
Vector_Free(m_Dynamic.source);
M_FreeBuffers();
Output_Shader_Free(m_Shader);
m_Shader = nullptr;
@ -303,6 +327,39 @@ void Output_Sprites_ObserveLevelLoad(void)
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(
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_Shutdown(void);
void Output_Sprites_ObserveLevelLoad(void);
void Output_Sprites_ObserveTextureAnimation(void);
void Output_Sprites_UploadProjectionMatrix(void);
void Output_Sprites_RenderBegin(void);

View file

@ -1,5 +1,8 @@
#include "game/output.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 "global/vars.h"
@ -145,5 +148,8 @@ void Output_AnimateTextures(const int32_t num_frames)
}
if (update) {
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.h"
#include "game/output/vertex_range.h"
#include <libtrx/debug.h>
#include <libtrx/gfx/gl/utils.h>
@ -8,35 +9,13 @@
#include <stdlib.h>
#pragma pack(push, 1)
typedef struct {
float x0, y0, x1, y1;
} 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];
OUTPUT_UVW corners[4];
} M_UVW_PACK;
static struct {
M_ANIMATION_RANGES objects;
M_ANIMATION_RANGES sprites;
VECTOR *objects;
VECTOR *sprites;
} m_AnimationRanges;
static struct {
@ -44,139 +23,117 @@ static struct {
GLuint tex_env_map;
struct {
GLuint tbo; // buffer to hold UV data
GLuint tex; // texture to hold UV buffer
int32_t count;
int32_t count_objects;
int32_t count_sprites;
M_UVW_PACK *data;
M_UVW_PACK *data_objects;
M_UVW_PACK *data_sprites;
bool *animated_objects;
bool *animated_sprites;
} uvws;
struct {
GLuint tex;
GLuint tbo;
M_ATLAS_SIZE *data;
M_ATLAS_SIZE *data_objects;
M_ATLAS_SIZE *data_sprites;
OUTPUT_TEXTURE_SIZE *data;
OUTPUT_TEXTURE_SIZE *data_objects;
OUTPUT_TEXTURE_SIZE *data_sprites;
} atlas_sizes;
} 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)
{
m_AnimationRanges.objects.range_count = 0;
size_t required_size = 0;
for (const ANIMATED_TEXTURE_RANGE *src_range =
Output_GetAnimatedTextureRange(0);
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(
m_AnimationRanges.objects.ranges,
m_AnimationRanges.objects.range_count * sizeof(M_ANIMATION_RANGE));
Vector_Clear(m_AnimationRanges.objects);
Vector_EnsureCapacity(m_AnimationRanges.objects, required_size);
M_ANIMATION_RANGE *dst_range = m_AnimationRanges.objects.ranges;
for (const ANIMATED_TEXTURE_RANGE *src_range =
Output_GetAnimatedTextureRange(0);
src_range != nullptr; src_range = src_range->next_range) {
for (int32_t i = 0; i < src_range->num_textures; i++) {
dst_range->index = src_range->textures[i];
dst_range->count = 1;
dst_range++;
Vector_Add(
m_AnimationRanges.objects,
&(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)
{
m_AnimationRanges.sprites.range_count = 0;
size_t required_size = 0;
for (int32_t i = 0; i < MAX_STATIC_OBJECTS; i++) {
const STATIC_OBJECT_2D *const obj = Object_Get2DStatic(i);
if (!obj->loaded || obj->frame_count == 1) {
continue;
}
m_AnimationRanges.sprites.range_count++;
required_size++;
}
m_AnimationRanges.sprites.ranges = Memory_Realloc(
m_AnimationRanges.sprites.ranges,
m_AnimationRanges.sprites.range_count * sizeof(M_ANIMATION_RANGE));
Vector_Clear(m_AnimationRanges.sprites);
Vector_EnsureCapacity(m_AnimationRanges.sprites, required_size);
M_ANIMATION_RANGE *dst_range = m_AnimationRanges.sprites.ranges;
for (int32_t i = 0; i < MAX_STATIC_OBJECTS; i++) {
const STATIC_OBJECT_2D *const obj = Object_Get2DStatic(i);
if (!obj->loaded || obj->frame_count == 1) {
continue;
}
dst_range->index = obj->texture_idx;
dst_range->count = obj->frame_count;
dst_range++;
Vector_Add(
m_AnimationRanges.sprites,
&(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)
{
M_PrepareObjectAnimationRanges();
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)
{
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);
size->x0 = texture->uv[0].u;
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)
{
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 float adj = 0.1 / 256.0f;
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)
{
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++) {
corners[j].u = texture->uv[j].u / 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 u1 = u0 + sprite->width / 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
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;
@ -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);
GFX_TRACK_DATA(
glBufferData, GL_TEXTURE_BUFFER, m_Priv.uvws.count * sizeof(M_UVW_PACK),
m_Priv.uvws.data, GL_DYNAMIC_DRAW);
GFX_GL_CheckError();
}
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);
for (int32_t i = 0; i < source->count; i++) {
const OUTPUT_VERTEX_RANGE *const range = Vector_Get(source, i);
for (int32_t j = 0; j < range->vertex_count; j++) {
M_FillObjectUVW(range->vertex_start + j);
M_FillAtlasObjectSize(range->vertex_start + 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->range_count; i++) {
const M_ANIMATION_RANGE *const range = &source->ranges[i];
for (int32_t j = 0; j < range->count; j++) {
M_FillSpriteUVW(range->index + j);
for (int32_t i = 0; i < source->count; i++) {
const OUTPUT_VERTEX_RANGE *const range = Vector_Get(source, i);
for (int32_t j = 0; j < range->vertex_count; j++) {
M_FillSpriteUVW(range->vertex_start + 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_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_objects = m_Priv.uvws.data;
m_Priv.uvws.data_sprites = m_Priv.uvws.data + m_Priv.uvws.count_objects;
glGenBuffers(1, &m_Priv.uvws.tbo);
glBindBuffer(GL_TEXTURE_BUFFER, m_Priv.uvws.tbo);
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_Priv.uvws.animated_objects =
Memory_Alloc(m_Priv.uvws.count_objects * sizeof(bool));
m_Priv.uvws.animated_sprites =
Memory_Alloc(m_Priv.uvws.count_sprites * sizeof(bool));
M_FillObjectUVWs();
M_FillSpriteUVWs();
}
@ -364,16 +259,11 @@ static void M_PrepareEnvMap(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_sprites = Output_GetSpriteTextureCount();
const int32_t count = count_objects + count_sprites;
m_Priv.atlas_sizes.data =
Memory_Realloc(m_Priv.atlas_sizes.data, count * sizeof(M_ATLAS_SIZE));
m_Priv.atlas_sizes.data = Memory_Realloc(
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_sprites = m_Priv.atlas_sizes.data + count_objects;
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++) {
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)
@ -428,38 +315,27 @@ static void M_FreeLevelData(void)
{
glBindTexture(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) {
glDeleteTextures(1, &m_Priv.tex_atlas);
m_Priv.tex_atlas = 0;
}
if (m_Priv.atlas_sizes.tex != 0) {
glDeleteTextures(1, &m_Priv.atlas_sizes.tex);
m_Priv.atlas_sizes.tex = 0;
}
if (m_Priv.atlas_sizes.tbo != 0) {
glDeleteBuffers(1, &m_Priv.atlas_sizes.tbo);
m_Priv.atlas_sizes.tbo = 0;
}
Memory_FreePointer(&m_Priv.uvws.data);
Memory_FreePointer(&m_Priv.uvws.animated_objects);
Memory_FreePointer(&m_Priv.uvws.animated_sprites);
Memory_FreePointer(&m_Priv.atlas_sizes.data);
Memory_FreePointer(&m_AnimationRanges.objects.ranges);
Memory_FreePointer(&m_AnimationRanges.sprites.ranges);
}
void Output_Textures_Init(void)
{
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)
{
Vector_Free(m_AnimationRanges.objects);
Vector_Free(m_AnimationRanges.sprites);
M_FreeLevelData();
if (m_Priv.tex_env_map != 0) {
glDeleteTextures(1, &m_Priv.tex_env_map);
@ -470,9 +346,8 @@ void Output_Textures_Shutdown(void)
void Output_Textures_ObserveLevelLoad(void)
{
M_FreeLevelData();
M_PrepareAnimationRanges();
M_PrepareUVWs();
M_UploadUVWs();
M_PrepareAnimationRanges();
M_UploadAtlas();
}
@ -500,27 +375,17 @@ void Output_Textures_UpdateEnvironmentMap(void)
void Output_Textures_CycleAnimations(void)
{
if (m_Priv.uvws.tex != 0) {
M_UploadSpriteAnimatedUVWs(&m_AnimationRanges.sprites);
M_UploadObjectAnimatedUVWs(&m_AnimationRanges.objects);
if (m_Priv.uvws.count != 0) {
M_UpdateSpriteAnimatedUVWs(m_AnimationRanges.sprites);
M_UpdateObjectAnimatedUVWs(m_AnimationRanges.objects);
}
}
GLuint Output_Textures_GetUVWsTexture(void)
{
return m_Priv.uvws.tex;
}
GLuint Output_Textures_GetAtlasTexture(void)
{
return m_Priv.tex_atlas;
}
GLuint Output_Textures_GetAtlasSizesTexture(void)
{
return m_Priv.atlas_sizes.tex;
}
GLuint Output_Textures_GetEnvMapTexture(void)
{
return m_Priv.tex_env_map;
@ -528,16 +393,35 @@ GLuint Output_Textures_GetEnvMapTexture(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);
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)
{
// re-adjust UVs when the bilinear filter is toggled.
if (m_Priv.uvws.tex != 0) {
if (m_Priv.uvws.count != 0) {
M_FillObjectUVWs();
M_UploadUVWs();
}
}

View file

@ -2,14 +2,32 @@
#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_Shutdown(void);
void Output_Textures_ObserveLevelLoad(void);
void Output_Textures_UpdateEnvironmentMap(void);
void Output_Textures_CycleAnimations(void);
void Output_Textures_ApplyRenderSettings(void);
GLuint Output_Textures_GetUVWsTexture(void);
GLuint Output_Textures_GetAtlasTexture(void);
GLuint Output_Textures_GetAtlasSizesTexture(void);
GLuint Output_Textures_GetEnvMapTexture(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/state.c',
'game/output/textures.c',
'game/output/vertex_range.c',
'game/overlay.c',
'game/requester.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)
{
g_FltWinLeft = 0.0f;