Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

output: move texture pages and palettes to TRX #2415

Merged
merged 11 commits into from
Jan 31, 2025
2 changes: 1 addition & 1 deletion docs/tr1/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- added exit fade-out effects (#2348)
- added optional dynamic lighting for gun flashes and explosions, similar to TR2+ (#2357)
- ⚠️ changed the game data to use a separate strings file for text information, removing it from the game flow file
- changed the sprite texture limit from 2048 to unlimited (within game's overall memory cap)
- changed the object texture limit from 2048 to unlimited (within game's overall memory cap)
- changed the sprite texture limit from 512 to unlimited (within game's overall memory cap)
- changed demo to be interrupted only by esc or action keys
- changed the turbo cheat to also affect ingame timer (#2167)
Expand Down
135 changes: 135 additions & 0 deletions src/libtrx/game/level/common.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "game/level/common.h"

#include "benchmark.h"
#include "debug.h"
#include "game/anims.h"
#include "game/const.h"
Expand All @@ -16,12 +17,35 @@

static int16_t *m_AnimCommands = nullptr;

static RGBA_8888 M_ARGB1555To8888(uint16_t argb1555);
static void M_ReadVertex(XYZ_16 *vertex, VFILE *file);
static void M_ReadFace4(FACE4 *face, VFILE *file);
static void M_ReadFace3(FACE3 *face, VFILE *file);
static void M_ReadObjectMesh(OBJECT_MESH *mesh, VFILE *file);
static void M_ReadBounds16(BOUNDS_16 *bounds, VFILE *file);

static RGBA_8888 M_ARGB1555To8888(const uint16_t argb1555)
{
// Extract 5-bit values for each ARGB component
uint8_t a1 = (argb1555 >> 15) & 0x01;
uint8_t r5 = (argb1555 >> 10) & 0x1F;
uint8_t g5 = (argb1555 >> 5) & 0x1F;
uint8_t b5 = argb1555 & 0x1F;

// Expand 5-bit color components to 8-bit
uint8_t a8 = a1 * 255; // 1-bit alpha (either 0 or 255)
uint8_t r8 = (r5 << 3) | (r5 >> 2);
uint8_t g8 = (g5 << 3) | (g5 >> 2);
uint8_t b8 = (b5 << 3) | (b5 >> 2);

return (RGBA_8888) {
.r = r8,
.g = g8,
.b = b8,
.a = a8,
};
}

static void M_ReadVertex(XYZ_16 *const vertex, VFILE *const file)
{
vertex->x = VFile_ReadS16(file);
Expand Down Expand Up @@ -128,6 +152,87 @@ static void M_ReadBounds16(BOUNDS_16 *const bounds, VFILE *const file)
bounds->max.z = VFile_ReadS16(file);
}

void Level_ReadPalette(LEVEL_INFO *const info, VFILE *const file)
{
BENCHMARK *const benchmark = Benchmark_Start();

const int32_t palette_size = 256;
info->palette.size = palette_size;

info->palette.data_24 = Memory_Alloc(sizeof(RGB_888) * palette_size);
VFile_Read(file, info->palette.data_24, sizeof(RGB_888) * palette_size);
info->palette.data_24[0].r = 0;
info->palette.data_24[0].g = 0;
info->palette.data_24[0].b = 0;
for (int32_t i = 1; i < palette_size; i++) {
RGB_888 *const col = &info->palette.data_24[i];
col->r = (col->r << 2) | (col->r >> 4);
col->g = (col->g << 2) | (col->g >> 4);
col->b = (col->b << 2) | (col->b >> 4);
}

#if TR_VERSION == 1
info->palette.data_32 = nullptr;
#else
RGBA_8888 palette_16[palette_size];
info->palette.data_32 = Memory_Alloc(sizeof(RGB_888) * palette_size);
VFile_Read(file, palette_16, sizeof(RGBA_8888) * palette_size);
for (int32_t i = 0; i < palette_size; i++) {
info->palette.data_32[i].r = palette_16[i].r;
info->palette.data_32[i].g = palette_16[i].g;
info->palette.data_32[i].b = palette_16[i].b;
}
#endif

Benchmark_End(benchmark, nullptr);
}

// TODO: replace extra_pages with value from injection interface
void Level_ReadTexturePages(
LEVEL_INFO *const info, const int32_t extra_pages, VFILE *const file)
{
BENCHMARK *const benchmark = Benchmark_Start();

const int32_t num_pages = VFile_ReadS32(file);
info->textures.page_count = num_pages;
LOG_INFO("texture pages: %d", num_pages);

const int32_t texture_size_8_bit =
num_pages * TEXTURE_PAGE_SIZE * sizeof(uint8_t);
const int32_t texture_size_32_bit =
(num_pages + extra_pages) * TEXTURE_PAGE_SIZE * sizeof(RGBA_8888);

info->textures.pages_24 = Memory_Alloc(texture_size_8_bit);
VFile_Read(file, info->textures.pages_24, texture_size_8_bit);

info->textures.pages_32 = Memory_Alloc(texture_size_32_bit);
RGBA_8888 *output = info->textures.pages_32;

#if TR_VERSION == 1
const uint8_t *input = info->textures.pages_24;
for (int32_t i = 0; i < texture_size_8_bit; i++) {
const uint8_t index = *input++;
const RGB_888 pix = info->palette.data_24[index];
output->r = pix.r;
output->g = pix.g;
output->b = pix.b;
output->a = index == 0 ? 0 : 0xFF;
output++;
}
#else
const int32_t texture_size_16_bit =
num_pages * TEXTURE_PAGE_SIZE * sizeof(uint16_t);
uint16_t *input = Memory_Alloc(texture_size_16_bit);
VFile_Read(file, input, texture_size_16_bit);
for (int32_t i = 0; i < num_pages * TEXTURE_PAGE_SIZE; i++) {
*output++ = M_ARGB1555To8888(*input++);
}
Memory_FreePointer(&input);
#endif

Benchmark_End(benchmark, NULL);
}

void Level_ReadRoomMesh(const int32_t room_num, VFILE *const file)
{
ROOM *const room = Room_Get(room_num);
Expand Down Expand Up @@ -455,3 +560,33 @@ void Level_ReadAnimatedTextureRanges(
file, range->textures, sizeof(int16_t) * range->num_textures);
}
}

void Level_LoadTexturePages(LEVEL_INFO *const info)
{
const int32_t num_pages = info->textures.page_count;
Output_InitialiseTexturePages(num_pages, TR_VERSION == 2);
for (int32_t i = 0; i < num_pages; i++) {
#if TR_VERSION == 2
uint8_t *const target_8 = Output_GetTexturePage8(i);
const uint8_t *const source_8 =
&info->textures.pages_24[i * TEXTURE_PAGE_SIZE];
memcpy(target_8, source_8, TEXTURE_PAGE_SIZE * sizeof(uint8_t));
#endif

RGBA_8888 *const target_32 = Output_GetTexturePage32(i);
const RGBA_8888 *const source_32 =
&info->textures.pages_32[i * TEXTURE_PAGE_SIZE];
memcpy(target_32, source_32, TEXTURE_PAGE_SIZE * sizeof(RGBA_8888));
}

Memory_FreePointer(&info->textures.pages_24);
Memory_FreePointer(&info->textures.pages_32);
}

void Level_LoadPalettes(LEVEL_INFO *const info)
{
Output_InitialisePalettes(
info->palette.size, info->palette.data_24, info->palette.data_32);
Memory_FreePointer(&info->palette.data_24);
Memory_FreePointer(&info->palette.data_32);
}
117 changes: 117 additions & 0 deletions src/libtrx/game/output/textures.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,61 @@
#include "debug.h"
#include "game/const.h"
#include "game/game_buf.h"
#include "game/objects/common.h"
#include "game/output.h"
#include "game/shell.h"
#include "utils.h"

static int32_t m_TexturePageCount = 0;
static uint8_t *m_TexturePages8 = nullptr;
static RGBA_8888 *m_TexturePages32 = nullptr;

static int32_t m_PaletteSize = 0;
static RGB_888 *m_Palette8 = nullptr;
static RGB_888 *m_Palette16 = nullptr;

static int32_t m_ObjectTextureCount = 0;
static OBJECT_TEXTURE *m_ObjectTextures = nullptr;
static SPRITE_TEXTURE *m_SpriteTextures = nullptr;
static ANIMATED_TEXTURE_RANGE *m_AnimTextureRanges = nullptr;

void Output_InitialiseTexturePages(const int32_t num_pages, const bool use_8bit)
{
m_TexturePageCount = num_pages;
if (num_pages == 0) {
m_TexturePages32 = nullptr;
m_TexturePages8 = nullptr;
return;
}

const int32_t page_size = num_pages * TEXTURE_PAGE_SIZE;
m_TexturePages32 =
GameBuf_Alloc(sizeof(RGBA_8888) * page_size, GBUF_TEXTURE_PAGES);
m_TexturePages8 = use_8bit
? GameBuf_Alloc(sizeof(uint8_t) * page_size, GBUF_TEXTURE_PAGES)
: nullptr;
}

void Output_InitialisePalettes(
const int32_t palette_size, const RGB_888 *const palette_8,
const RGB_888 *const palette_16)
{
ASSERT(palette_size != 0);
ASSERT(palette_8 != nullptr);
m_PaletteSize = palette_size;

m_Palette8 = GameBuf_Alloc(sizeof(RGB_888) * palette_size, GBUF_PALETTES);
memcpy(m_Palette8, palette_8, sizeof(RGB_888) * palette_size);

if (palette_16 != nullptr) {
m_Palette16 =
GameBuf_Alloc(sizeof(RGB_888) * palette_size, GBUF_PALETTES);
memcpy(m_Palette16, palette_16, sizeof(RGB_888) * palette_size);
} else {
m_Palette16 = nullptr;
}
}

void Output_InitialiseObjectTextures(const int32_t num_textures)
{
m_ObjectTextureCount = num_textures;
Expand All @@ -35,6 +82,48 @@ void Output_InitialiseAnimatedTextures(const int32_t num_ranges)
GBUF_ANIMATED_TEXTURE_RANGES);
}

int32_t Output_GetTexturePageCount(void)
{
return m_TexturePageCount;
}

uint8_t *Output_GetTexturePage8(const int32_t page_idx)
{
if (m_TexturePages8 == nullptr) {
return nullptr;
}
return &m_TexturePages8[page_idx * TEXTURE_PAGE_SIZE];
}

RGBA_8888 *Output_GetTexturePage32(const int32_t page_idx)
{
if (m_TexturePages32 == nullptr) {
return nullptr;
}
return &m_TexturePages32[page_idx * TEXTURE_PAGE_SIZE];
}

int32_t Output_GetPaletteSize(void)
{
return m_PaletteSize;
}

RGB_888 Output_GetPaletteColor8(const uint16_t idx)
{
if (m_Palette8 == nullptr) {
return (RGB_888) { 0, 0, 0 };
}
return m_Palette8[idx];
}

RGB_888 Output_GetPaletteColor16(const uint16_t idx)
{
if (m_Palette16 == nullptr) {
return (RGB_888) { 0, 0, 0 };
}
return m_Palette16[idx];
}

int32_t Output_GetObjectTextureCount(void)
{
return m_ObjectTextureCount;
Expand Down Expand Up @@ -64,6 +153,34 @@ ANIMATED_TEXTURE_RANGE *Output_GetAnimatedTextureRange(const int32_t range_idx)
return &m_AnimTextureRanges[range_idx];
}

RGBA_8888 Output_RGB2RGBA(const RGB_888 color)
{
RGBA_8888 ret = { .r = color.r, .g = color.g, .b = color.b, .a = 255 };
return ret;
}

int16_t Output_FindColor8(const RGB_888 color)
{
if (m_Palette8 == nullptr) {
return -1;
}

int32_t best_idx = 0;
int32_t best_diff = INT32_MAX;
for (int32_t i = 0; i < m_PaletteSize; i++) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can start at index 1, since we assume best_idx to be 0.
Do we care to return -1 if there is no palette?

const int32_t dr = color.r - m_Palette8[i].r;
const int32_t dg = color.g - m_Palette8[i].g;
const int32_t db = color.b - m_Palette8[i].b;
const int32_t diff = SQUARE(dr) + SQUARE(dg) + SQUARE(db);
if (diff < best_diff) {
best_diff = diff;
best_idx = i;
}
}

return best_idx;
}

void Output_CycleAnimatedTextures(void)
{
const ANIMATED_TEXTURE_RANGE *range = m_AnimTextureRanges;
Expand Down
1 change: 1 addition & 0 deletions src/libtrx/include/libtrx/game/enum_map.def
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ENUM_MAP_DEFINE(GFX_TEXTURE_FILTER, GFX_TF_BILINEAR, "bilinear")
ENUM_MAP_DEFINE(GFX_TEXTURE_FILTER, GFX_TF_NN, "point")

ENUM_MAP_DEFINE(GAME_BUFFER, GBUF_TEXTURE_PAGES, "Texture pages")
ENUM_MAP_DEFINE(GAME_BUFFER, GBUF_PALETTES, "Color palettes")
ENUM_MAP_DEFINE(GAME_BUFFER, GBUF_OBJECT_TEXTURES, "Object textures")
ENUM_MAP_DEFINE(GAME_BUFFER, GBUF_SPRITE_TEXTURES, "Sprite textures")
ENUM_MAP_DEFINE(GAME_BUFFER, GBUF_MESH_POINTERS, "Mesh pointers")
Expand Down
1 change: 1 addition & 0 deletions src/libtrx/include/libtrx/game/game_buf.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
typedef enum {
// clang-format off
GBUF_TEXTURE_PAGES,
GBUF_PALETTES,
GBUF_OBJECT_TEXTURES,
GBUF_SPRITE_TEXTURES,
GBUF_MESH_POINTERS,
Expand Down
4 changes: 4 additions & 0 deletions src/libtrx/include/libtrx/game/level/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#define ANIM_BONE_SIZE 4

void Level_ReadPalette(LEVEL_INFO *info, VFILE *file);
void Level_ReadTexturePages(LEVEL_INFO *info, int32_t extra_pages, VFILE *file);
void Level_ReadRoomMesh(int32_t room_num, VFILE *file);
void Level_ReadFloorData(VFILE *file);
void Level_ReadObjectMeshes(
Expand All @@ -25,3 +27,5 @@ void Level_ReadSpriteTextures(
int32_t base_idx, int16_t base_page_idx, int32_t num_textures, VFILE *file);
void Level_ReadSpriteSequences(int32_t num_sequences, VFILE *file);
void Level_ReadAnimatedTextureRanges(int32_t num_ranges, VFILE *file);
void Level_LoadTexturePages(LEVEL_INFO *info);
void Level_LoadPalettes(LEVEL_INFO *info);
1 change: 1 addition & 0 deletions src/libtrx/include/libtrx/game/level/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ typedef struct {
struct {
int32_t size;
RGB_888 *data_24;
RGB_888 *data_32;
} palette;

struct {
Expand Down
11 changes: 11 additions & 0 deletions src/libtrx/include/libtrx/game/output/textures.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,24 @@

#include "./types.h"

void Output_InitialiseTexturePages(int32_t num_pages, bool use_8bit);
void Output_InitialisePalettes(
int32_t palette_size, const RGB_888 *palette_8, const RGB_888 *palette_16);
void Output_InitialiseObjectTextures(int32_t num_textures);
void Output_InitialiseSpriteTextures(int32_t num_textures);
void Output_InitialiseAnimatedTextures(int32_t num_ranges);

int32_t Output_GetTexturePageCount(void);
uint8_t *Output_GetTexturePage8(int32_t page_idx);
RGBA_8888 *Output_GetTexturePage32(int32_t page_idx);
int32_t Output_GetPaletteSize(void);
RGB_888 Output_GetPaletteColor8(uint16_t idx);
RGB_888 Output_GetPaletteColor16(uint16_t idx);
int32_t Output_GetObjectTextureCount(void);
OBJECT_TEXTURE *Output_GetObjectTexture(int32_t texture_idx);
SPRITE_TEXTURE *Output_GetSpriteTexture(int32_t texture_idx);
ANIMATED_TEXTURE_RANGE *Output_GetAnimatedTextureRange(int32_t range_idx);

RGBA_8888 Output_RGB2RGBA(RGB_888 color);
int16_t Output_FindColor8(RGB_888 color);
void Output_CycleAnimatedTextures(void);
2 changes: 1 addition & 1 deletion src/tr1/game/inventory_ring/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ static void M_DrawItem(
Output_DrawScreenLine(
sx + sprite->pos.x, sy + sprite->pos.y, sprite->param1,
sprite->param2,
Output_RGB2RGBA(Output_GetPaletteColor(
Output_RGB2RGBA(Output_GetPaletteColor8(
(uint8_t)sprite->sprite_num)));
break;
case SHAPE_BOX: {
Expand Down
Loading