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: handle common lighting in TRX #2358

Merged
merged 9 commits into from
Jan 23, 2025
4 changes: 2 additions & 2 deletions src/libtrx/game/level/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,11 @@ void Level_ReadRoomMesh(const int32_t room_num, VFILE *const file)
for (int32_t i = 0; i < room->mesh.num_vertices; i++) {
ROOM_VERTEX *const vertex = &room->mesh.vertices[i];
M_ReadVertex(&vertex->pos, file);
vertex->light_base = VFile_ReadS16(file);
#if TR_VERSION == 1
vertex->shade = VFile_ReadU16(file);
vertex->flags = 0;
vertex->light_adder = vertex->light_base;
#elif TR_VERSION == 2
vertex->light_base = VFile_ReadS16(file);
vertex->light_table_value = VFile_ReadU8(file);
vertex->flags = VFile_ReadU8(file);
vertex->light_adder = VFile_ReadS16(file);
Expand Down
295 changes: 295 additions & 0 deletions src/libtrx/game/output.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
#include "game/output.h"

#include "game/const.h"
#include "game/matrix.h"
#include "utils.h"

#define MAX_DYNAMIC_LIGHTS 10

typedef struct {
XYZ_32 pos;
int32_t shade;
} COMMON_LIGHT;

static int32_t m_DynamicLightCount = 0;
static LIGHT m_DynamicLights[MAX_DYNAMIC_LIGHTS] = {};

static void M_CalculateBrightestLight(
XYZ_32 pos, const ROOM *room, COMMON_LIGHT *brightest_light);
static int32_t M_CalculateDynamicLight(
XYZ_32 pos, COMMON_LIGHT *brightest_light);

static void M_CalculateBrightestLight(
const XYZ_32 pos, const ROOM *const room,
COMMON_LIGHT *const brightest_light)
{
#if TR_VERSION == 2
if (room->light_mode != RLM_NORMAL) {
const int32_t light_shade = Output_GetRoomLightShade(room->light_mode);
for (int32_t i = 0; i < room->num_lights; i++) {
const LIGHT *const light = &room->lights[i];
const int32_t dx = pos.x - light->pos.x;
const int32_t dy = pos.y - light->pos.y;
const int32_t dz = pos.z - light->pos.z;

const int32_t falloff_1 = SQUARE(light->falloff.value_1) >> 12;
const int32_t falloff_2 = SQUARE(light->falloff.value_2) >> 12;
const int32_t dist = (SQUARE(dx) + SQUARE(dy) + SQUARE(dz)) >> 12;

const int32_t shade_1 =
falloff_1 * light->shade.value_1 / (falloff_1 + dist);
const int32_t shade_2 =
falloff_2 * light->shade.value_2 / (falloff_2 + dist);
const int32_t shade =
shade_1 + (shade_2 - shade_1) * light_shade / (WIBBLE_SIZE - 1);

if (shade > brightest_light->shade) {
brightest_light->shade = shade;
brightest_light->pos = light->pos;
}
}
return;
}
#endif

const int32_t ambient = TR_VERSION == 1 ? (0x1FFF - room->ambient) : 0;
for (int32_t i = 0; i < room->num_lights; i++) {
const LIGHT *const light = &room->lights[i];
const int32_t dx = pos.x - light->pos.x;
const int32_t dy = pos.y - light->pos.y;
const int32_t dz = pos.z - light->pos.z;
const int32_t falloff = SQUARE(light->falloff.value_1) >> 12;
const int32_t dist = (SQUARE(dx) + SQUARE(dy) + SQUARE(dz)) >> 12;
const int32_t shade =
ambient + (falloff * light->shade.value_1 / (falloff + dist));
if (shade > brightest_light->shade) {
brightest_light->shade = shade;
brightest_light->pos = light->pos;
}
}
}

static int32_t M_CalculateDynamicLight(
const XYZ_32 pos, COMMON_LIGHT *const brightest_light)
{
int32_t adder = 0;
for (int32_t i = 0; i < m_DynamicLightCount; i++) {
const LIGHT *const light = &m_DynamicLights[i];
const int32_t dx = pos.x - light->pos.x;
const int32_t dy = pos.y - light->pos.y;
const int32_t dz = pos.z - light->pos.z;
const int32_t radius = 1 << light->falloff.value_1;
if (dx < -radius || dx > radius || dy < -radius || dy > radius
|| dz < -radius || dz > radius) {
continue;
}

const int32_t dist = SQUARE(dx) + SQUARE(dy) + SQUARE(dz);
if (dist > SQUARE(radius)) {
continue;
}

const int32_t shade = (1 << light->shade.value_1)
- (dist >> (2 * light->falloff.value_1 - light->shade.value_1));
if (shade > brightest_light->shade) {
brightest_light->shade = shade;
brightest_light->pos = light->pos;
}
adder += shade;
}

return adder;
}

void Output_CalculateLight(const XYZ_32 pos, const int16_t room_num)
{
const ROOM *const room = Room_Get(room_num);
COMMON_LIGHT brightest_light = {};

M_CalculateBrightestLight(pos, room, &brightest_light);
int32_t adder = brightest_light.shade;
int32_t dynamic_adder = M_CalculateDynamicLight(pos, &brightest_light);

adder = (adder + dynamic_adder) / 2;
if (TR_VERSION == 1 && (room->num_lights > 0 || dynamic_adder > 0)) {
adder += (0x1FFF - room->ambient) / 2;
}

// TODO: use m_LsAdder and m_LsDivider once ported
int32_t global_adder;
int32_t global_divider;
if (adder == 0) {
global_adder = room->ambient;
global_divider = 0;
} else {
#if TR_VERSION == 1
global_adder = 0x1FFF - adder;
global_divider =
(1 << (W2V_SHIFT + 12)) / (brightest_light.shade - adder);
#else
global_adder = room->ambient - adder;
global_divider = (1 << (W2V_SHIFT + 12)) / adder;
#endif
int16_t angles[2];
Math_GetVectorAngles(
pos.x - brightest_light.pos.x, pos.y - brightest_light.pos.y,
pos.z - brightest_light.pos.z, angles);
Output_RotateLight(angles[1], angles[0]);
}

const int32_t depth = g_MatrixPtr->_23 >> W2V_SHIFT;
global_adder += Output_CalcFogShade(depth);
CLAMPG(global_adder, 0x1FFF);

Output_SetLightAdder(global_adder);
Output_SetLightDivider(global_divider);
}

void Output_CalculateStaticLight(const int16_t adder)
{
// TODO: use m_LsAdder
int32_t global_adder = adder - 0x1000;
const int32_t depth = g_MatrixPtr->_23 >> W2V_SHIFT;
global_adder += Output_CalcFogShade(depth);
CLAMPG(global_adder, 0x1FFF);
Output_SetLightAdder(global_adder);
}

void Output_CalculateStaticMeshLight(
const XYZ_32 pos, const SHADE shade, const ROOM *const room)
{
int32_t adder = shade.value_1;
if (TR_VERSION == 2 && room->light_mode != RLM_NORMAL) {
const int32_t room_shade = Output_GetRoomLightShade(room->light_mode);
adder +=
(shade.value_2 - shade.value_1) * room_shade / (WIBBLE_SIZE - 1);
}

for (int32_t i = 0; i < m_DynamicLightCount; i++) {
const LIGHT *const light = &m_DynamicLights[i];
const int32_t dx = pos.x - light->pos.x;
const int32_t dy = pos.y - light->pos.y;
const int32_t dz = pos.z - light->pos.z;
const int32_t radius = 1 << light->falloff.value_1;
if (dx < -radius || dx > radius || dy < -radius || dy > radius
|| dz < -radius || dz > radius) {
continue;
}

const int32_t dist = SQUARE(dx) + SQUARE(dy) + SQUARE(dz);
if (dist > SQUARE(radius)) {
continue;
}

const int32_t shade = (1 << light->shade.value_1)
- (dist >> (2 * light->falloff.value_1 - light->shade.value_1));
adder -= shade;
if (adder < 0) {
break;
}
}

Output_CalculateStaticLight(adder);
}

void Output_CalculateObjectLighting(
const ITEM *const item, const BOUNDS_16 *const bounds)
{
if (item->shade.value_1 >= 0) {
Output_CalculateStaticMeshLight(
item->pos, item->shade, Room_Get(item->room_num));
return;
}

Matrix_PushUnit();

Matrix_TranslateSet(0, 0, 0);
Matrix_Rot16(item->rot);
Matrix_TranslateRel32((XYZ_32) {
.x = (bounds->min.x + bounds->max.x) / 2,
.y = (bounds->max.y + bounds->min.y) / 2,
.z = (bounds->max.z + bounds->min.z) / 2,
});
const XYZ_32 pos = {
.x = item->pos.x + (g_MatrixPtr->_03 >> W2V_SHIFT),
.y = item->pos.y + (g_MatrixPtr->_13 >> W2V_SHIFT),
.z = item->pos.z + (g_MatrixPtr->_23 >> W2V_SHIFT),
};
Matrix_Pop();

Output_CalculateLight(pos, item->room_num);
}

void Output_LightRoom(ROOM *const room)
{
if (TR_VERSION == 2 && room->light_mode != RLM_NORMAL) {
Output_LightRoomVertices(room);
} else if (room->flags & RF_DYNAMIC_LIT) {
for (int32_t i = 0; i < room->mesh.num_vertices; i++) {
ROOM_VERTEX *const vtx = &room->mesh.vertices[i];
vtx->light_adder = vtx->light_base;
}
room->flags &= ~RF_DYNAMIC_LIT;
}

const int32_t x_min = WALL_L;
const int32_t z_min = WALL_L;
const int32_t x_max = (room->size.x - 1) * WALL_L;
const int32_t z_max = (room->size.z - 1) * WALL_L;

for (int32_t i = 0; i < m_DynamicLightCount; i++) {
const LIGHT *const light = &m_DynamicLights[i];
const int32_t x = light->pos.x - room->pos.x;
const int32_t y = light->pos.y;
const int32_t z = light->pos.z - room->pos.z;
const int32_t radius = 1 << light->falloff.value_1;
if (x - radius > x_max || z - radius > z_max || x + radius < x_min
|| z + radius < z_min) {
continue;
}

room->flags |= RF_DYNAMIC_LIT;

for (int32_t j = 0; j < room->mesh.num_vertices; j++) {
ROOM_VERTEX *const v = &room->mesh.vertices[j];
if (v->light_adder == 0) {
continue;
}

const int32_t dx = v->pos.x - x;
const int32_t dy = v->pos.y - y;
const int32_t dz = v->pos.z - z;
if (dx < -radius || dx > radius || dy < -radius || dy > radius
|| dz < -radius || dz > radius) {
continue;
}

const int32_t dist = SQUARE(dx) + SQUARE(dy) + SQUARE(dz);
if (dist > SQUARE(radius)) {
continue;
}

const int32_t shade = (1 << light->shade.value_1)
- (dist >> (2 * light->falloff.value_1 - light->shade.value_1));
v->light_adder -= shade;
CLAMPL(v->light_adder, 0);
}
}
}

void Output_ResetDynamicLights(void)
{
m_DynamicLightCount = 0;
}

void Output_AddDynamicLight(
const XYZ_32 pos, const int32_t intensity, const int32_t falloff)
{
const int32_t idx =
m_DynamicLightCount < MAX_DYNAMIC_LIGHTS ? m_DynamicLightCount++ : 0;

LIGHT *const light = &m_DynamicLights[idx];
light->pos = pos;
light->shade.value_1 = intensity;
light->falloff.value_1 = falloff;
}
5 changes: 2 additions & 3 deletions src/libtrx/include/libtrx/game/items/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "../math.h"
#include "../objects/ids.h"
#include "../output/types.h"
#include "./enum.h"

#if TR_VERSION == 1
Expand Down Expand Up @@ -41,15 +42,13 @@ typedef struct {
int16_t timer;
uint16_t flags;

SHADE shade;
#if TR_VERSION == 1
int16_t shade;
void *data;
void *priv;
CARRIED_ITEM *carried_item;
bool enable_shadow;
#elif TR_VERSION == 2
int16_t shade_1;
int16_t shade_2;
int16_t carried_item;
void *data;
#endif
Expand Down
15 changes: 15 additions & 0 deletions src/libtrx/include/libtrx/game/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "./output/const.h"
#include "./output/types.h"
#include "./output/vars.h"
#include "./rooms.h"

#include <stdint.h>

Expand All @@ -29,3 +30,17 @@ extern void Output_SetupAboveWater(bool is_underwater);
extern void Output_RotateLight(int16_t pitch, int16_t yaw);
extern void Output_SetLightAdder(int32_t adder);
extern void Output_SetLightDivider(int32_t divider);

// Temporary
extern int32_t Output_CalcFogShade(int32_t depth);
extern int32_t Output_GetRoomLightShade(ROOM_LIGHT_MODE mode);
extern void Output_LightRoomVertices(const ROOM *room);

void Output_CalculateLight(XYZ_32 pos, int16_t room_num);
void Output_CalculateStaticLight(int16_t adder);
void Output_CalculateStaticMeshLight(XYZ_32 pos, SHADE shade, const ROOM *room);
void Output_CalculateObjectLighting(const ITEM *item, const BOUNDS_16 *bounds);
void Output_LightRoom(ROOM *room);

void Output_ResetDynamicLights(void);
void Output_AddDynamicLight(XYZ_32 pos, int32_t intensity, int32_t falloff);
2 changes: 2 additions & 0 deletions src/libtrx/include/libtrx/game/output/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
#define MAX_OBJECT_TEXTURES 2048
#define MAX_SPRITE_TEXTURES 512
#endif

#define WIBBLE_SIZE 32
10 changes: 10 additions & 0 deletions src/libtrx/include/libtrx/game/output/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

#include <stdint.h>

typedef struct {
int16_t value_1;
int16_t value_2;
} SHADE;

typedef struct {
int32_t value_1;
int32_t value_2;
} FALLOFF;

typedef struct {
uint16_t u;
uint16_t v;
Expand Down
8 changes: 8 additions & 0 deletions src/libtrx/include/libtrx/game/rooms/enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ typedef enum {
RF_INSIDE = 0x40,
} ROOM_FLAG;

typedef enum {
RLM_NORMAL = 0,
RLM_FLICKER = 1,
RLM_GLOW = 2,
RLM_SUNSET = 3,
RLM_NUMBER_OF = 4,
} ROOM_LIGHT_MODE;

typedef enum {
FT_FLOOR = 0,
FT_DOOR = 1,
Expand Down
Loading
Loading