mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 12:47:58 +03:00
tr1/output: move good functions to its own module
This commit is contained in:
parent
8a6a852af3
commit
d647539581
5 changed files with 635 additions and 617 deletions
|
@ -1,32 +1,18 @@
|
|||
#include "game/output.h"
|
||||
|
||||
#include "game/output/meshes/common.h"
|
||||
#include "game/output/meshes/dynamic.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/utils.h"
|
||||
#include "game/overlay.h"
|
||||
#include "game/screen.h"
|
||||
#include "game/shell.h"
|
||||
#include "game/viewport.h"
|
||||
#include "global/vars.h"
|
||||
#include "specific/s_shell.h"
|
||||
|
||||
#include <libtrx/config.h>
|
||||
#include <libtrx/debug.h>
|
||||
#include <libtrx/engine/image.h>
|
||||
#include <libtrx/filesystem.h>
|
||||
#include <libtrx/game/game_buf.h>
|
||||
#include <libtrx/game/output.h>
|
||||
#include <libtrx/gfx/gl/track.h>
|
||||
#include <libtrx/gfx/gl/utils.h>
|
||||
#include <libtrx/log.h>
|
||||
#include <libtrx/memory.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_LIGHTNINGS 64
|
||||
#define MAP_DEPTH(zv) (g_FltResZBuf - g_FltResZ * (1.0 / (double)(zv)))
|
||||
|
||||
|
@ -36,21 +22,6 @@ typedef struct {
|
|||
int32_t thickness;
|
||||
} LIGHTNING;
|
||||
|
||||
static int32_t m_LsAdder = 0;
|
||||
static int32_t m_LsDivider = 0;
|
||||
static bool m_IsSkyboxEnabled = false;
|
||||
static bool m_IsWibbleEffect = false;
|
||||
static bool m_IsWaterEffect = false;
|
||||
static bool m_IsShadeEffect = false;
|
||||
|
||||
static int32_t m_Time = 0;
|
||||
static int32_t m_AnimatedTexturesOffset = 0;
|
||||
|
||||
static int32_t m_DrawDistFade = 0;
|
||||
static int32_t m_DrawDistMax = 0;
|
||||
static RGB_F m_WaterColor = {};
|
||||
static XYZ_32 m_LsVectorView = {};
|
||||
|
||||
static int32_t m_LightningCount = 0;
|
||||
static LIGHTNING m_LightningTable[MAX_LIGHTNINGS];
|
||||
static int32_t m_TextureMap[GFX_MAX_TEXTURES] = { GFX_NO_TEXTURE };
|
||||
|
@ -70,16 +41,6 @@ static const char *m_ImageExtensions[] = {
|
|||
".png", ".jpg", ".jpeg", ".pcx", nullptr,
|
||||
};
|
||||
|
||||
static struct {
|
||||
GLint bound_program;
|
||||
GLint bound_vao;
|
||||
GLint bound_vbo;
|
||||
GLint bound_active_texture;
|
||||
GLint bound_texture;
|
||||
GLint bound_polygon_mode[2];
|
||||
} m_CachedState;
|
||||
|
||||
static inline float M_GetUV(uint16_t uv);
|
||||
static void M_SelectTexture(int32_t texture_num);
|
||||
static void M_EnableDepthWrites(void);
|
||||
static void M_DisableDepthWrites(void);
|
||||
|
@ -101,20 +62,11 @@ static void M_DrawTriangleStrip(
|
|||
static void M_Draw2DQuad(
|
||||
int32_t x1, int32_t y1, int32_t x2, int32_t y2, RGBA_8888 tl, RGBA_8888 tr,
|
||||
RGBA_8888 bl, RGBA_8888 br);
|
||||
static void M_DrawSphere(XYZ_32 pos, int32_t radius);
|
||||
static void M_DrawLightningSegment(const LIGHTNING *const lightning);
|
||||
static void M_DrawSprite(
|
||||
int16_t x1, int16_t y1, int16_t x2, int32_t y2, int32_t z,
|
||||
int32_t sprite_idx, int16_t shade);
|
||||
|
||||
static inline float M_GetUV(const uint16_t uv)
|
||||
{
|
||||
return g_Config.rendering.pretty_pixels
|
||||
&& g_Config.rendering.texture_filter == GFX_TF_NN
|
||||
? uv / 65536.0f
|
||||
: ((uv & 0xFF00) + 127) / 65536.0f;
|
||||
}
|
||||
|
||||
static void M_SelectTexture(const int32_t texture_num)
|
||||
{
|
||||
if (texture_num == m_SelectedTexture) {
|
||||
|
@ -285,71 +237,6 @@ static void M_Draw2DQuad(
|
|||
GFX_3D_Renderer_SetBlendingMode(m_Renderer3D, GFX_BLEND_MODE_OFF);
|
||||
}
|
||||
|
||||
static void M_DrawSphere(const XYZ_32 pos, const int32_t radius)
|
||||
{
|
||||
// More subdivisions means smoother spheres.
|
||||
const int32_t subdivisions = 12;
|
||||
const int32_t position_count = SQUARE(subdivisions + 1);
|
||||
XYZ_F positions[position_count];
|
||||
int32_t index = 0;
|
||||
|
||||
for (int32_t i = 0; i <= subdivisions; i++) {
|
||||
const float theta = (M_PI * i) / subdivisions; // Latitude angle
|
||||
const float sin_theta = sinf(theta);
|
||||
const float cos_theta = cosf(theta);
|
||||
|
||||
for (int32_t j = 0; j <= subdivisions; j++) {
|
||||
const float phi = (2 * M_PI * j) / subdivisions; // Longitude angle
|
||||
const float sin_phi = sinf(phi);
|
||||
const float cos_phi = cosf(phi);
|
||||
|
||||
// Convert spherical coordinates to 3D points.
|
||||
positions[index] = (XYZ_F) {
|
||||
.x = pos.x + radius * cos_phi * sin_theta,
|
||||
.y = pos.y + radius * cos_theta,
|
||||
.z = pos.z + radius * sin_phi * sin_theta,
|
||||
};
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
const int32_t vertex_count =
|
||||
subdivisions * subdivisions * OUTPUT_QUAD_VERTICES;
|
||||
OUTPUT_MESH_VERTEX vertices[vertex_count];
|
||||
OUTPUT_MESH_VERTEX *out_vertex = vertices;
|
||||
for (int32_t i = 0; i < subdivisions; i++) {
|
||||
for (int32_t j = 0; j < subdivisions; j++) {
|
||||
const int32_t indices[4] = {
|
||||
i * (subdivisions + 1) + j,
|
||||
(i + 1) * (subdivisions + 1) + j,
|
||||
(i + 1) * (subdivisions + 1) + (j + 1),
|
||||
i * (subdivisions + 1) + (j + 1),
|
||||
};
|
||||
for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) {
|
||||
out_vertex->pos = positions[indices[OUTPUT_QUAD_TO_FAN(k)]];
|
||||
out_vertex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RGBA_8888 color_black = { 0, 0, 0, 128 };
|
||||
const RGBA_8888 color_white = { 255, 255, 255, 128 };
|
||||
const bool wireframe_state = GFX_Context_GetWireframeMode();
|
||||
for (int32_t i = 0; i < vertex_count; i++) {
|
||||
vertices[i].flags = VERT_FLAT_SHADED | VERT_NO_LIGHTING;
|
||||
vertices[i].color = wireframe_state ? color_black : color_white;
|
||||
}
|
||||
|
||||
glGetIntegerv(GL_POLYGON_MODE, &m_CachedState.bound_polygon_mode[0]);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Output_Shader_UploadMatrix(Output_Meshes_GetShader(), g_MatrixPtr);
|
||||
Output_Meshes_DrawTriangles(vertex_count, vertices);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, m_CachedState.bound_polygon_mode[0]);
|
||||
}
|
||||
|
||||
static void M_DrawLightningSegment(const LIGHTNING *const lightning)
|
||||
{
|
||||
const int32_t vertex_count = 8;
|
||||
|
@ -541,12 +428,6 @@ void Output_ObserveLevelUnload(void)
|
|||
Output_Meshes_ObserveLevelUnload();
|
||||
}
|
||||
|
||||
void Output_ObserveFOVChange(void)
|
||||
{
|
||||
Output_Meshes_UploadProjectionMatrix();
|
||||
Output_Sprites_UploadProjectionMatrix();
|
||||
}
|
||||
|
||||
void Output_FlushTranslucentObjects(void)
|
||||
{
|
||||
Output_RememberState();
|
||||
|
@ -592,298 +473,6 @@ void Output_ClearDepthBuffer(void)
|
|||
GFX_3D_Renderer_ClearDepth(m_Renderer3D);
|
||||
}
|
||||
|
||||
void Output_SetSkyboxEnabled(const bool enabled)
|
||||
{
|
||||
m_IsSkyboxEnabled = enabled;
|
||||
}
|
||||
|
||||
bool Output_IsSkyboxEnabled(void)
|
||||
{
|
||||
return m_IsSkyboxEnabled;
|
||||
}
|
||||
|
||||
void Output_DrawSkybox(const OBJECT_MESH *const mesh)
|
||||
{
|
||||
M_DisableDepthTest();
|
||||
Output_RememberState();
|
||||
Output_Meshes_RenderObjectMesh(g_MatrixPtr, Output_GetTint(), mesh);
|
||||
Output_RestoreState();
|
||||
M_EnableDepthTest();
|
||||
}
|
||||
|
||||
void Output_DrawObjectMesh(const OBJECT_MESH *const mesh, const int32_t clip)
|
||||
{
|
||||
M_Flush();
|
||||
Output_RememberState();
|
||||
Output_Meshes_RenderObjectMesh(g_MatrixPtr, Output_GetTint(), mesh);
|
||||
Output_RestoreState();
|
||||
|
||||
if (g_Config.rendering.enable_debug_spheres) {
|
||||
M_DrawSphere(
|
||||
(XYZ_32) { mesh->center.x, mesh->center.y, mesh->center.z },
|
||||
mesh->radius);
|
||||
}
|
||||
M_Flush();
|
||||
}
|
||||
|
||||
void Output_DrawObjectMesh_I(const OBJECT_MESH *const mesh, const int32_t clip)
|
||||
{
|
||||
Matrix_Push();
|
||||
Matrix_Interpolate();
|
||||
Output_DrawObjectMesh(mesh, clip);
|
||||
Matrix_Pop();
|
||||
}
|
||||
|
||||
void Output_DrawRoomMesh(ROOM *const room)
|
||||
{
|
||||
Output_RememberState();
|
||||
Output_LightRoom(room);
|
||||
Output_EnableScissor(
|
||||
room->bound_left, room->bound_bottom,
|
||||
room->bound_right - room->bound_left,
|
||||
room->bound_bottom - room->bound_top);
|
||||
Output_Meshes_RenderRoomMesh(g_MatrixPtr, Output_GetTint(), room);
|
||||
Output_DisableScissor();
|
||||
Output_RestoreState();
|
||||
}
|
||||
|
||||
void Output_DrawRoomPortals(const ROOM *const room)
|
||||
{
|
||||
const int32_t vertex_count = room->portals->count * 8;
|
||||
OUTPUT_MESH_VERTEX vertices[vertex_count];
|
||||
OUTPUT_MESH_VERTEX *out_vertex = vertices;
|
||||
const RGBA_8888 color = { 0, 0, 255, 255 };
|
||||
for (int32_t i = 0; i < room->portals->count; i++) {
|
||||
const PORTAL *const portal = &room->portals->portal[i];
|
||||
const XYZ_F positions[4] = {
|
||||
{ portal->vertex[0].x, portal->vertex[0].y, portal->vertex[0].z },
|
||||
{ portal->vertex[1].x, portal->vertex[1].y, portal->vertex[1].z },
|
||||
{ portal->vertex[2].x, portal->vertex[2].y, portal->vertex[2].z },
|
||||
{ portal->vertex[3].x, portal->vertex[3].y, portal->vertex[3].z },
|
||||
};
|
||||
const int32_t indices[8] = { 0, 1, 1, 2, 2, 3, 3, 0 };
|
||||
for (int32_t j = 0; j < 8; j++) {
|
||||
out_vertex->pos = positions[indices[j]];
|
||||
out_vertex++;
|
||||
}
|
||||
}
|
||||
for (int32_t i = 0; i < vertex_count; i++) {
|
||||
vertices[i].uvw_idx = -1;
|
||||
vertices[i].flags = VERT_FLAT_SHADED | VERT_NO_LIGHTING;
|
||||
vertices[i].color = color;
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glGetIntegerv(GL_POLYGON_MODE, &m_CachedState.bound_polygon_mode[0]);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Output_Shader_UploadMatrix(Output_Meshes_GetShader(), g_MatrixPtr);
|
||||
Output_Meshes_DrawPrimitives(GL_LINES, vertex_count, vertices);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, m_CachedState.bound_polygon_mode[0]);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void Output_DrawRoomTriggers(const ROOM *const room)
|
||||
{
|
||||
const RGBA_8888 color = { .r = 255, .g = 0, .b = 255, .a = 128 };
|
||||
const XZ_16 offsets[4] = { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 0 } };
|
||||
|
||||
int32_t vertex_count = 0;
|
||||
for (int32_t z = 0; z < room->size.z; z++) {
|
||||
for (int32_t x = 0; x < room->size.x; x++) {
|
||||
const SECTOR *sector = Room_GetUnitSector(room, x, z);
|
||||
if (sector->trigger == nullptr) {
|
||||
continue;
|
||||
}
|
||||
vertex_count += OUTPUT_QUAD_VERTICES;
|
||||
}
|
||||
}
|
||||
|
||||
OUTPUT_MESH_VERTEX *vertices =
|
||||
Memory_Alloc(vertex_count * sizeof(OUTPUT_MESH_VERTEX));
|
||||
OUTPUT_MESH_VERTEX *out_vertex = vertices;
|
||||
|
||||
for (int32_t z = 0; z < room->size.z; z++) {
|
||||
for (int32_t x = 0; x < room->size.x; x++) {
|
||||
const SECTOR *sector = Room_GetUnitSector(room, x, z);
|
||||
if (sector->trigger == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (int32_t i = 0; i < OUTPUT_QUAD_VERTICES; i++) {
|
||||
const int32_t j = OUTPUT_QUAD_TO_FAN(i);
|
||||
XYZ_16 vertex_pos = {
|
||||
.x = (x + offsets[j].x) * WALL_L,
|
||||
.z = (z + offsets[j].z) * WALL_L,
|
||||
};
|
||||
XYZ_32 world_pos = {
|
||||
.x = room->pos.x + x * WALL_L + offsets[j].x * (WALL_L - 1),
|
||||
.z = room->pos.z + z * WALL_L + offsets[j].z * (WALL_L - 1),
|
||||
.y = room->pos.y,
|
||||
};
|
||||
|
||||
int16_t room_num = room - Room_Get(0);
|
||||
sector = Room_GetSector(
|
||||
world_pos.x, world_pos.y, world_pos.z, &room_num);
|
||||
vertex_pos.y =
|
||||
Room_GetHeight(
|
||||
sector, world_pos.x, world_pos.y, world_pos.z)
|
||||
+ (m_IsWaterEffect ? -16 : -2);
|
||||
|
||||
out_vertex->pos =
|
||||
(XYZ_F) { vertex_pos.x, vertex_pos.y, vertex_pos.z };
|
||||
out_vertex->flags = VERT_FLAT_SHADED | VERT_NO_LIGHTING;
|
||||
out_vertex->color = color;
|
||||
out_vertex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
M_DisableTextureMode();
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Output_Shader_UploadMatrix(Output_Meshes_GetShader(), g_MatrixPtr);
|
||||
Output_Meshes_DrawTriangles(vertex_count, vertices);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
Memory_FreePointer(&vertices);
|
||||
}
|
||||
|
||||
void Output_DrawShadow(
|
||||
const int16_t size, const BOUNDS_16 *const bounds, const ITEM *const item)
|
||||
{
|
||||
if (!item->enable_shadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t vertex_count = g_Config.visuals.enable_round_shadow ? 32 : 8;
|
||||
const int32_t x0 = bounds->min.x;
|
||||
const int32_t x1 = bounds->max.x;
|
||||
const int32_t z0 = bounds->min.z;
|
||||
const int32_t z1 = bounds->max.z;
|
||||
const int32_t x_mid = (x0 + x1) / 2;
|
||||
const int32_t z_mid = (z0 + z1) / 2;
|
||||
const int32_t x_add = (x1 - x0) * size / 1024;
|
||||
const int32_t z_add = (z1 - z0) * size / 1024;
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateAbs(
|
||||
item->interp.result.pos.x, item->floor, item->interp.result.pos.z);
|
||||
Matrix_RotY(item->rot.y);
|
||||
|
||||
OUTPUT_MESH_VERTEX vertices[vertex_count * 3];
|
||||
for (int32_t i = 0; i < vertex_count; i++) {
|
||||
for (int32_t j = 0; j < 2; j++) {
|
||||
const int32_t angle = (DEG_180 + (i + j) * DEG_360) / vertex_count;
|
||||
vertices[i * 3 + j].pos.x =
|
||||
x_mid + ((x_add * 2) * Math_Sin(angle) >> W2V_SHIFT);
|
||||
vertices[i * 3 + j].pos.z =
|
||||
z_mid + ((z_add * 2) * Math_Cos(angle) >> W2V_SHIFT);
|
||||
}
|
||||
vertices[i * 3 + 2].pos.x = x_mid;
|
||||
vertices[i * 3 + 2].pos.z = z_mid;
|
||||
for (int32_t j = 0; j < 3; j++) {
|
||||
vertices[i * 3 + j].pos.y = -5;
|
||||
vertices[i * 3 + j].flags = VERT_FLAT_SHADED | VERT_NO_LIGHTING;
|
||||
vertices[i * 3 + j].color = (RGBA_8888) { 0, 0, 0, 128 };
|
||||
}
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Output_Shader_UploadMatrix(Output_Meshes_GetShader(), g_MatrixPtr);
|
||||
Output_Meshes_DrawTriangles(vertex_count * 3, vertices);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
|
||||
Matrix_Pop();
|
||||
}
|
||||
|
||||
int32_t Output_GetDrawDistMin(void)
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
int32_t Output_GetDrawDistFade(void)
|
||||
{
|
||||
return m_DrawDistFade;
|
||||
}
|
||||
|
||||
int32_t Output_GetDrawDistMax(void)
|
||||
{
|
||||
return m_DrawDistMax;
|
||||
}
|
||||
|
||||
void Output_SetDrawDistFade(const int32_t dist)
|
||||
{
|
||||
m_DrawDistFade = dist;
|
||||
}
|
||||
|
||||
void Output_SetDrawDistMax(const int32_t dist)
|
||||
{
|
||||
m_DrawDistMax = dist;
|
||||
|
||||
const double near_z = Output_GetNearZ();
|
||||
const double far_z = Output_GetFarZ();
|
||||
const double res_z = 0.99 * near_z * far_z / (far_z - near_z);
|
||||
g_FltResZ = res_z;
|
||||
g_FltResZBuf = 0.005 + res_z / near_z;
|
||||
}
|
||||
|
||||
void Output_SetWaterColor(const RGB_F *const color)
|
||||
{
|
||||
m_WaterColor.r = color->r;
|
||||
m_WaterColor.g = color->g;
|
||||
m_WaterColor.b = color->b;
|
||||
}
|
||||
|
||||
void Output_SetLightAdder(const int32_t adder)
|
||||
{
|
||||
m_LsAdder = adder;
|
||||
}
|
||||
|
||||
int32_t Output_GetLightAdder(void)
|
||||
{
|
||||
return m_LsAdder;
|
||||
}
|
||||
|
||||
void Output_SetLightDivider(const int32_t divider)
|
||||
{
|
||||
m_LsDivider = divider;
|
||||
}
|
||||
|
||||
int32_t Output_GetLightDivider(void)
|
||||
{
|
||||
return m_LsDivider;
|
||||
}
|
||||
|
||||
XYZ_32 Output_GetLightVectorView(void)
|
||||
{
|
||||
return m_LsVectorView;
|
||||
}
|
||||
|
||||
int32_t Output_GetNearZ(void)
|
||||
{
|
||||
return Output_GetDrawDistMin() << W2V_SHIFT;
|
||||
}
|
||||
|
||||
int32_t Output_GetFarZ(void)
|
||||
{
|
||||
return Output_GetDrawDistMax() << W2V_SHIFT;
|
||||
}
|
||||
|
||||
void Output_DrawSprite(
|
||||
const int32_t x, const int32_t y, const int32_t z, const int16_t sprite_idx,
|
||||
const int16_t shade)
|
||||
{
|
||||
Matrix_Push();
|
||||
Matrix_TranslateAbs(x, y, z);
|
||||
Output_Sprites_RenderSingleSprite(
|
||||
g_MatrixPtr, (XYZ_32) { 0, 0, 0 }, sprite_idx, shade);
|
||||
Matrix_Pop();
|
||||
}
|
||||
|
||||
void Output_DrawScreenFlatQuad(
|
||||
const int32_t sx, const int32_t sy, const int32_t w, const int32_t h,
|
||||
const RGBA_8888 color)
|
||||
|
@ -1171,64 +760,13 @@ void Output_DrawLightningSegment(
|
|||
m_LightningCount++;
|
||||
}
|
||||
|
||||
void Output_SetupBelowWater(const bool underwater)
|
||||
{
|
||||
m_IsWaterEffect = true;
|
||||
m_IsWibbleEffect = !underwater;
|
||||
m_IsShadeEffect = true;
|
||||
Output_RememberState();
|
||||
Output_Shader_UploadWaterEffect(Output_Meshes_GetShader(), m_IsWaterEffect);
|
||||
Output_RestoreState();
|
||||
}
|
||||
|
||||
void Output_SetupAboveWater(const bool underwater)
|
||||
{
|
||||
m_IsWaterEffect = false;
|
||||
m_IsWibbleEffect = underwater;
|
||||
m_IsShadeEffect = underwater;
|
||||
Output_RememberState();
|
||||
Output_Shader_UploadWaterEffect(Output_Meshes_GetShader(), m_IsWaterEffect);
|
||||
Output_RestoreState();
|
||||
}
|
||||
|
||||
void Output_AnimateTextures(const int32_t num_frames)
|
||||
{
|
||||
m_Time += num_frames;
|
||||
m_AnimatedTexturesOffset += num_frames;
|
||||
bool update = false;
|
||||
while (m_AnimatedTexturesOffset > 5) {
|
||||
Output_CycleAnimatedTextures();
|
||||
update = true;
|
||||
m_AnimatedTexturesOffset -= 5;
|
||||
}
|
||||
if (update) {
|
||||
Output_Textures_CycleAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
void Output_RotateLight(const int16_t pitch, const int16_t yaw)
|
||||
{
|
||||
const int32_t cp = Math_Cos(pitch);
|
||||
const int32_t sp = Math_Sin(pitch);
|
||||
const int32_t cy = Math_Cos(yaw);
|
||||
const int32_t sy = Math_Sin(yaw);
|
||||
const int32_t x = TRIGMULT2(cp, sy);
|
||||
const int32_t y = -sp;
|
||||
const int32_t z = TRIGMULT2(cp, cy);
|
||||
const MATRIX *const m = &g_W2VMatrix;
|
||||
m_LsVectorView.x = (m->_00 * x + m->_01 * y + m->_02 * z) >> W2V_SHIFT;
|
||||
m_LsVectorView.y = (m->_10 * x + m->_11 * y + m->_12 * z) >> W2V_SHIFT;
|
||||
m_LsVectorView.z = (m->_20 * x + m->_21 * y + m->_22 * z) >> W2V_SHIFT;
|
||||
}
|
||||
|
||||
void Output_DrawBlackRectangle(const int32_t opacity)
|
||||
{
|
||||
int32_t sx = 0;
|
||||
int32_t sy = 0;
|
||||
int32_t sw = Viewport_GetWidth();
|
||||
int32_t sh = Viewport_GetHeight();
|
||||
|
||||
RGBA_8888 background = { 0, 0, 0, opacity };
|
||||
const int32_t sx = 0;
|
||||
const int32_t sy = 0;
|
||||
const int32_t sw = Viewport_GetWidth();
|
||||
const int32_t sh = Viewport_GetHeight();
|
||||
const RGBA_8888 background = { 0, 0, 0, opacity };
|
||||
M_DisableDepthTest();
|
||||
Output_ClearDepthBuffer();
|
||||
Output_DrawScreenFlatQuad(sx, sy, sw, sh, background);
|
||||
|
@ -1274,153 +812,3 @@ void Output_ApplyFOV(void)
|
|||
g_PhdPersp /= s;
|
||||
}
|
||||
}
|
||||
|
||||
RGB_F Output_GetTint(void)
|
||||
{
|
||||
if (m_IsShadeEffect) {
|
||||
return m_WaterColor;
|
||||
}
|
||||
return (RGB_F) { 1.0f, 1.0f, 1.0f };
|
||||
}
|
||||
|
||||
bool Output_MakeScreenshot(const char *const path)
|
||||
{
|
||||
GFX_Context_ScheduleScreenshot(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t Output_GetObjectBounds(const BOUNDS_16 *const bounds)
|
||||
{
|
||||
// TODO: remove
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t Output_CalcFogShade(const int32_t depth)
|
||||
{
|
||||
// TODO: done in the shader
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t Output_GetRoomLightShade(const ROOM_LIGHT_MODE mode)
|
||||
{
|
||||
// TODO: remove
|
||||
ASSERT_FAIL();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Output_LightRoomVertices(const ROOM *const room)
|
||||
{
|
||||
// TODO: remove
|
||||
ASSERT_FAIL();
|
||||
}
|
||||
|
||||
bool Output_GetWaterEffect(void)
|
||||
{
|
||||
return m_IsWaterEffect;
|
||||
}
|
||||
|
||||
bool Output_GetWibbleEffect(void)
|
||||
{
|
||||
return m_IsWibbleEffect;
|
||||
}
|
||||
|
||||
int32_t Output_GetTime(void)
|
||||
{
|
||||
return m_Time;
|
||||
}
|
||||
|
||||
void Output_GetProjectionMatrix(GLfloat output[][4])
|
||||
{
|
||||
const float left = 0.0f;
|
||||
const float top = 0.0f;
|
||||
const float right = Viewport_GetWidth();
|
||||
const float bottom = Viewport_GetHeight();
|
||||
const float near = Output_GetNearZ() / (float)(1 << W2V_SHIFT);
|
||||
const float far = Output_GetFarZ() / (float)(1 << W2V_SHIFT);
|
||||
const float aspect = (float)right / (float)bottom;
|
||||
const float fov = Viewport_GetFOV() * M_PI / (float)DEG_180;
|
||||
const float f = 1.0f / tan(fov / 2.0f);
|
||||
|
||||
output[0][0] = f / aspect;
|
||||
output[0][1] = 0.0f;
|
||||
output[0][2] = 0.0f;
|
||||
output[0][3] = 0.0f;
|
||||
|
||||
output[1][0] = 0.0f;
|
||||
output[1][1] = -f;
|
||||
output[1][2] = 0.0f;
|
||||
output[1][3] = 0.0f;
|
||||
|
||||
output[2][0] = 0.0f;
|
||||
output[2][1] = 0.0f;
|
||||
output[2][2] = g_FltResZBuf;
|
||||
output[2][3] = -g_FltResZ / (float)(1 << W2V_SHIFT);
|
||||
|
||||
output[3][0] = 0.0f;
|
||||
output[3][1] = 0.0f;
|
||||
output[3][2] = 1.0f;
|
||||
output[3][3] = 0.0f;
|
||||
}
|
||||
|
||||
void Output_EnableScissor(
|
||||
const float x, const float y, const float w, const float h)
|
||||
{
|
||||
// Causes the rendering pipeline to discard every pixel outside of the
|
||||
// specified window. The window is in game framebuffer viewport's
|
||||
// coordinates; to make it work properly, we need to translate it to the
|
||||
// SDL window coordinates first.
|
||||
|
||||
const int32_t border = 2; // to deal with precision issues
|
||||
|
||||
struct {
|
||||
GLint x, y, w, h;
|
||||
} game_viewport = {
|
||||
.x = Viewport_GetMinX(),
|
||||
.y = Viewport_GetMinY(),
|
||||
.w = Viewport_GetWidth(),
|
||||
.h = Viewport_GetHeight(),
|
||||
}, gl_viewport, scissor;
|
||||
glGetIntegerv(GL_VIEWPORT, &gl_viewport.x);
|
||||
|
||||
const float scale_x = gl_viewport.w / (float)game_viewport.w;
|
||||
const float scale_y = gl_viewport.h / (float)game_viewport.h;
|
||||
scissor.x = gl_viewport.x + (x * scale_x) - border;
|
||||
scissor.y = gl_viewport.y + (game_viewport.h - y) * scale_y - border;
|
||||
scissor.w = w * scale_x + border * 2;
|
||||
scissor.h = h * scale_y + border * 2;
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(scissor.x, scissor.y, scissor.w, scissor.h);
|
||||
}
|
||||
|
||||
void Output_DisableScissor(void)
|
||||
{
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
void Output_RememberState(void)
|
||||
{
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &m_CachedState.bound_program);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &m_CachedState.bound_vao);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &m_CachedState.bound_vbo);
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &m_CachedState.bound_active_texture);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &m_CachedState.bound_texture);
|
||||
glGetIntegerv(GL_POLYGON_MODE, &m_CachedState.bound_polygon_mode[0]);
|
||||
GFX_GL_CheckError();
|
||||
}
|
||||
|
||||
void Output_RestoreState(void)
|
||||
{
|
||||
glUseProgram(m_CachedState.bound_program);
|
||||
glBindVertexArray(m_CachedState.bound_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_CachedState.bound_vbo);
|
||||
glActiveTexture(m_CachedState.bound_active_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_CachedState.bound_texture);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, m_CachedState.bound_polygon_mode[0]);
|
||||
GFX_GL_CheckError();
|
||||
}
|
||||
|
||||
float Output_AdjustUV(const uint16_t uv)
|
||||
{
|
||||
return M_GetUV(uv);
|
||||
}
|
||||
|
|
314
src/tr1/game/output/draw_misc.c
Normal file
314
src/tr1/game/output/draw_misc.c
Normal file
|
@ -0,0 +1,314 @@
|
|||
#include "game/output.h"
|
||||
#include "game/output/meshes/common.h"
|
||||
#include "game/output/meshes/dynamic.h"
|
||||
#include "game/output/meshes/objects.h"
|
||||
#include "game/output/meshes/rooms.h"
|
||||
#include "game/output/sprites.h"
|
||||
#include "game/output/utils.h"
|
||||
|
||||
#include <libtrx/config.h>
|
||||
#include <libtrx/memory.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static struct {
|
||||
GLint bound_polygon_mode[2];
|
||||
} m_CachedState;
|
||||
|
||||
static bool m_IsSkyboxEnabled = false;
|
||||
static inline float M_GetUV(uint16_t uv);
|
||||
static void M_DrawSphere(XYZ_32 pos, int32_t radius);
|
||||
|
||||
static inline float M_GetUV(const uint16_t uv)
|
||||
{
|
||||
return g_Config.rendering.pretty_pixels
|
||||
&& g_Config.rendering.texture_filter == GFX_TF_NN
|
||||
? uv / 65536.0f
|
||||
: ((uv & 0xFF00) + 127) / 65536.0f;
|
||||
}
|
||||
|
||||
static void M_DrawSphere(const XYZ_32 pos, const int32_t radius)
|
||||
{
|
||||
// More subdivisions means smoother spheres.
|
||||
const int32_t subdivisions = 12;
|
||||
const int32_t position_count = SQUARE(subdivisions + 1);
|
||||
XYZ_F positions[position_count];
|
||||
int32_t index = 0;
|
||||
|
||||
for (int32_t i = 0; i <= subdivisions; i++) {
|
||||
const float theta = (M_PI * i) / subdivisions; // Latitude angle
|
||||
const float sin_theta = sinf(theta);
|
||||
const float cos_theta = cosf(theta);
|
||||
|
||||
for (int32_t j = 0; j <= subdivisions; j++) {
|
||||
const float phi = (2 * M_PI * j) / subdivisions; // Longitude angle
|
||||
const float sin_phi = sinf(phi);
|
||||
const float cos_phi = cosf(phi);
|
||||
|
||||
// Convert spherical coordinates to 3D points.
|
||||
positions[index] = (XYZ_F) {
|
||||
.x = pos.x + radius * cos_phi * sin_theta,
|
||||
.y = pos.y + radius * cos_theta,
|
||||
.z = pos.z + radius * sin_phi * sin_theta,
|
||||
};
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
const int32_t vertex_count =
|
||||
subdivisions * subdivisions * OUTPUT_QUAD_VERTICES;
|
||||
OUTPUT_MESH_VERTEX vertices[vertex_count];
|
||||
OUTPUT_MESH_VERTEX *out_vertex = vertices;
|
||||
for (int32_t i = 0; i < subdivisions; i++) {
|
||||
for (int32_t j = 0; j < subdivisions; j++) {
|
||||
const int32_t indices[4] = {
|
||||
i * (subdivisions + 1) + j,
|
||||
(i + 1) * (subdivisions + 1) + j,
|
||||
(i + 1) * (subdivisions + 1) + (j + 1),
|
||||
i * (subdivisions + 1) + (j + 1),
|
||||
};
|
||||
for (int32_t k = 0; k < OUTPUT_QUAD_VERTICES; k++) {
|
||||
out_vertex->pos = positions[indices[OUTPUT_QUAD_TO_FAN(k)]];
|
||||
out_vertex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RGBA_8888 color_black = { 0, 0, 0, 128 };
|
||||
const RGBA_8888 color_white = { 255, 255, 255, 128 };
|
||||
const bool wireframe_state = GFX_Context_GetWireframeMode();
|
||||
for (int32_t i = 0; i < vertex_count; i++) {
|
||||
vertices[i].flags = VERT_FLAT_SHADED | VERT_NO_LIGHTING;
|
||||
vertices[i].color = wireframe_state ? color_black : color_white;
|
||||
}
|
||||
|
||||
glGetIntegerv(GL_POLYGON_MODE, &m_CachedState.bound_polygon_mode[0]);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Output_Shader_UploadMatrix(Output_Meshes_GetShader(), g_MatrixPtr);
|
||||
Output_Meshes_DrawTriangles(vertex_count, vertices);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, m_CachedState.bound_polygon_mode[0]);
|
||||
}
|
||||
|
||||
float Output_AdjustUV(const uint16_t uv)
|
||||
{
|
||||
return M_GetUV(uv);
|
||||
}
|
||||
|
||||
void Output_SetSkyboxEnabled(const bool enabled)
|
||||
{
|
||||
m_IsSkyboxEnabled = enabled;
|
||||
}
|
||||
|
||||
bool Output_IsSkyboxEnabled(void)
|
||||
{
|
||||
return m_IsSkyboxEnabled;
|
||||
}
|
||||
|
||||
void Output_DrawSkybox(const OBJECT_MESH *const mesh)
|
||||
{
|
||||
Output_RememberState();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
Output_Meshes_RenderObjectMesh(g_MatrixPtr, Output_GetTint(), mesh);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
Output_RestoreState();
|
||||
}
|
||||
|
||||
void Output_DrawObjectMesh(const OBJECT_MESH *const mesh, const int32_t clip)
|
||||
{
|
||||
Output_RememberState();
|
||||
Output_Meshes_RenderObjectMesh(g_MatrixPtr, Output_GetTint(), mesh);
|
||||
Output_RestoreState();
|
||||
|
||||
if (g_Config.rendering.enable_debug_spheres) {
|
||||
M_DrawSphere(
|
||||
(XYZ_32) { mesh->center.x, mesh->center.y, mesh->center.z },
|
||||
mesh->radius);
|
||||
}
|
||||
}
|
||||
|
||||
void Output_DrawObjectMesh_I(const OBJECT_MESH *const mesh, const int32_t clip)
|
||||
{
|
||||
Matrix_Push();
|
||||
Matrix_Interpolate();
|
||||
Output_DrawObjectMesh(mesh, clip);
|
||||
Matrix_Pop();
|
||||
}
|
||||
|
||||
void Output_DrawRoomMesh(ROOM *const room)
|
||||
{
|
||||
Output_RememberState();
|
||||
Output_LightRoom(room);
|
||||
Output_EnableScissor(
|
||||
room->bound_left, room->bound_bottom,
|
||||
room->bound_right - room->bound_left,
|
||||
room->bound_bottom - room->bound_top);
|
||||
Output_Meshes_RenderRoomMesh(g_MatrixPtr, Output_GetTint(), room);
|
||||
Output_DisableScissor();
|
||||
Output_RestoreState();
|
||||
}
|
||||
|
||||
void Output_DrawRoomPortals(const ROOM *const room)
|
||||
{
|
||||
const int32_t vertex_count = room->portals->count * 8;
|
||||
OUTPUT_MESH_VERTEX vertices[vertex_count];
|
||||
OUTPUT_MESH_VERTEX *out_vertex = vertices;
|
||||
const RGBA_8888 color = { 0, 0, 255, 255 };
|
||||
for (int32_t i = 0; i < room->portals->count; i++) {
|
||||
const PORTAL *const portal = &room->portals->portal[i];
|
||||
const XYZ_F positions[4] = {
|
||||
{ portal->vertex[0].x, portal->vertex[0].y, portal->vertex[0].z },
|
||||
{ portal->vertex[1].x, portal->vertex[1].y, portal->vertex[1].z },
|
||||
{ portal->vertex[2].x, portal->vertex[2].y, portal->vertex[2].z },
|
||||
{ portal->vertex[3].x, portal->vertex[3].y, portal->vertex[3].z },
|
||||
};
|
||||
const int32_t indices[8] = { 0, 1, 1, 2, 2, 3, 3, 0 };
|
||||
for (int32_t j = 0; j < 8; j++) {
|
||||
out_vertex->pos = positions[indices[j]];
|
||||
out_vertex++;
|
||||
}
|
||||
}
|
||||
for (int32_t i = 0; i < vertex_count; i++) {
|
||||
vertices[i].uvw_idx = -1;
|
||||
vertices[i].flags = VERT_FLAT_SHADED | VERT_NO_LIGHTING;
|
||||
vertices[i].color = color;
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glGetIntegerv(GL_POLYGON_MODE, &m_CachedState.bound_polygon_mode[0]);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Output_Shader_UploadMatrix(Output_Meshes_GetShader(), g_MatrixPtr);
|
||||
Output_Meshes_DrawPrimitives(GL_LINES, vertex_count, vertices);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, m_CachedState.bound_polygon_mode[0]);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void Output_DrawRoomTriggers(const ROOM *const room)
|
||||
{
|
||||
const RGBA_8888 color = { .r = 255, .g = 0, .b = 255, .a = 128 };
|
||||
const XZ_16 offsets[4] = { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 0 } };
|
||||
|
||||
int32_t vertex_count = 0;
|
||||
for (int32_t z = 0; z < room->size.z; z++) {
|
||||
for (int32_t x = 0; x < room->size.x; x++) {
|
||||
const SECTOR *sector = Room_GetUnitSector(room, x, z);
|
||||
if (sector->trigger == nullptr) {
|
||||
continue;
|
||||
}
|
||||
vertex_count += OUTPUT_QUAD_VERTICES;
|
||||
}
|
||||
}
|
||||
|
||||
OUTPUT_MESH_VERTEX *vertices =
|
||||
Memory_Alloc(vertex_count * sizeof(OUTPUT_MESH_VERTEX));
|
||||
OUTPUT_MESH_VERTEX *out_vertex = vertices;
|
||||
|
||||
for (int32_t z = 0; z < room->size.z; z++) {
|
||||
for (int32_t x = 0; x < room->size.x; x++) {
|
||||
const SECTOR *sector = Room_GetUnitSector(room, x, z);
|
||||
if (sector->trigger == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (int32_t i = 0; i < OUTPUT_QUAD_VERTICES; i++) {
|
||||
const int32_t j = OUTPUT_QUAD_TO_FAN(i);
|
||||
XYZ_16 vertex_pos = {
|
||||
.x = (x + offsets[j].x) * WALL_L,
|
||||
.z = (z + offsets[j].z) * WALL_L,
|
||||
};
|
||||
XYZ_32 world_pos = {
|
||||
.x = room->pos.x + x * WALL_L + offsets[j].x * (WALL_L - 1),
|
||||
.z = room->pos.z + z * WALL_L + offsets[j].z * (WALL_L - 1),
|
||||
.y = room->pos.y,
|
||||
};
|
||||
|
||||
int16_t room_num = room - Room_Get(0);
|
||||
sector = Room_GetSector(
|
||||
world_pos.x, world_pos.y, world_pos.z, &room_num);
|
||||
vertex_pos.y =
|
||||
Room_GetHeight(
|
||||
sector, world_pos.x, world_pos.y, world_pos.z)
|
||||
+ (Output_GetWaterEffect() ? -16 : -2);
|
||||
|
||||
out_vertex->pos =
|
||||
(XYZ_F) { vertex_pos.x, vertex_pos.y, vertex_pos.z };
|
||||
out_vertex->flags = VERT_FLAT_SHADED | VERT_NO_LIGHTING;
|
||||
out_vertex->color = color;
|
||||
out_vertex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Output_Shader_UploadMatrix(Output_Meshes_GetShader(), g_MatrixPtr);
|
||||
Output_Meshes_DrawTriangles(vertex_count, vertices);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
Memory_FreePointer(&vertices);
|
||||
}
|
||||
|
||||
void Output_DrawShadow(
|
||||
const int16_t size, const BOUNDS_16 *const bounds, const ITEM *const item)
|
||||
{
|
||||
if (!item->enable_shadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t vertex_count = g_Config.visuals.enable_round_shadow ? 32 : 8;
|
||||
const int32_t x0 = bounds->min.x;
|
||||
const int32_t x1 = bounds->max.x;
|
||||
const int32_t z0 = bounds->min.z;
|
||||
const int32_t z1 = bounds->max.z;
|
||||
const int32_t x_mid = (x0 + x1) / 2;
|
||||
const int32_t z_mid = (z0 + z1) / 2;
|
||||
const int32_t x_add = (x1 - x0) * size / 1024;
|
||||
const int32_t z_add = (z1 - z0) * size / 1024;
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateAbs(
|
||||
item->interp.result.pos.x, item->floor, item->interp.result.pos.z);
|
||||
Matrix_RotY(item->rot.y);
|
||||
|
||||
OUTPUT_MESH_VERTEX vertices[vertex_count * 3];
|
||||
for (int32_t i = 0; i < vertex_count; i++) {
|
||||
for (int32_t j = 0; j < 2; j++) {
|
||||
const int32_t angle = (DEG_180 + (i + j) * DEG_360) / vertex_count;
|
||||
vertices[i * 3 + j].pos.x =
|
||||
x_mid + ((x_add * 2) * Math_Sin(angle) >> W2V_SHIFT);
|
||||
vertices[i * 3 + j].pos.z =
|
||||
z_mid + ((z_add * 2) * Math_Cos(angle) >> W2V_SHIFT);
|
||||
}
|
||||
vertices[i * 3 + 2].pos.x = x_mid;
|
||||
vertices[i * 3 + 2].pos.z = z_mid;
|
||||
for (int32_t j = 0; j < 3; j++) {
|
||||
vertices[i * 3 + j].pos.y = -5;
|
||||
vertices[i * 3 + j].flags = VERT_FLAT_SHADED | VERT_NO_LIGHTING;
|
||||
vertices[i * 3 + j].color = (RGBA_8888) { 0, 0, 0, 128 };
|
||||
}
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Output_Shader_UploadMatrix(Output_Meshes_GetShader(), g_MatrixPtr);
|
||||
Output_Meshes_DrawTriangles(vertex_count * 3, vertices);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
|
||||
Matrix_Pop();
|
||||
}
|
||||
|
||||
void Output_DrawSprite(
|
||||
const int32_t x, const int32_t y, const int32_t z, const int16_t sprite_idx,
|
||||
const int16_t shade)
|
||||
{
|
||||
Matrix_Push();
|
||||
Matrix_TranslateAbs(x, y, z);
|
||||
Output_Sprites_RenderSingleSprite(
|
||||
g_MatrixPtr, (XYZ_32) { 0, 0, 0 }, sprite_idx, shade);
|
||||
Matrix_Pop();
|
||||
}
|
147
src/tr1/game/output/func.c
Normal file
147
src/tr1/game/output/func.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
#include "game/output.h"
|
||||
#include "game/output/meshes/common.h"
|
||||
#include "game/output/sprites.h"
|
||||
#include "game/viewport.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/debug.h>
|
||||
#include <libtrx/gfx/gl/utils.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static struct {
|
||||
GLint bound_program;
|
||||
GLint bound_vao;
|
||||
GLint bound_vbo;
|
||||
GLint bound_active_texture;
|
||||
GLint bound_texture;
|
||||
GLint bound_polygon_mode[2];
|
||||
} m_CachedState;
|
||||
|
||||
void Output_ObserveFOVChange(void)
|
||||
{
|
||||
Output_Meshes_UploadProjectionMatrix();
|
||||
Output_Sprites_UploadProjectionMatrix();
|
||||
}
|
||||
|
||||
bool Output_MakeScreenshot(const char *const path)
|
||||
{
|
||||
GFX_Context_ScheduleScreenshot(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t Output_GetObjectBounds(const BOUNDS_16 *const bounds)
|
||||
{
|
||||
// TODO: remove
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t Output_CalcFogShade(const int32_t depth)
|
||||
{
|
||||
// TODO: done in the shader
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t Output_GetRoomLightShade(const ROOM_LIGHT_MODE mode)
|
||||
{
|
||||
// TODO: remove
|
||||
ASSERT_FAIL();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Output_LightRoomVertices(const ROOM *const room)
|
||||
{
|
||||
// TODO: remove
|
||||
ASSERT_FAIL();
|
||||
}
|
||||
|
||||
void Output_GetProjectionMatrix(GLfloat output[][4])
|
||||
{
|
||||
const float left = 0.0f;
|
||||
const float top = 0.0f;
|
||||
const float right = Viewport_GetWidth();
|
||||
const float bottom = Viewport_GetHeight();
|
||||
const float near = Output_GetNearZ() / (float)(1 << W2V_SHIFT);
|
||||
const float far = Output_GetFarZ() / (float)(1 << W2V_SHIFT);
|
||||
const float aspect = (float)right / (float)bottom;
|
||||
const float fov = Viewport_GetFOV() * M_PI / (float)DEG_180;
|
||||
const float f = 1.0f / tan(fov / 2.0f);
|
||||
|
||||
output[0][0] = f / aspect;
|
||||
output[0][1] = 0.0f;
|
||||
output[0][2] = 0.0f;
|
||||
output[0][3] = 0.0f;
|
||||
|
||||
output[1][0] = 0.0f;
|
||||
output[1][1] = -f;
|
||||
output[1][2] = 0.0f;
|
||||
output[1][3] = 0.0f;
|
||||
|
||||
output[2][0] = 0.0f;
|
||||
output[2][1] = 0.0f;
|
||||
output[2][2] = g_FltResZBuf;
|
||||
output[2][3] = -g_FltResZ / (float)(1 << W2V_SHIFT);
|
||||
|
||||
output[3][0] = 0.0f;
|
||||
output[3][1] = 0.0f;
|
||||
output[3][2] = 1.0f;
|
||||
output[3][3] = 0.0f;
|
||||
}
|
||||
|
||||
void Output_EnableScissor(
|
||||
const float x, const float y, const float w, const float h)
|
||||
{
|
||||
// Causes the rendering pipeline to discard every pixel outside of the
|
||||
// specified window. The window is in game framebuffer viewport's
|
||||
// coordinates; to make it work properly, we need to translate it to the
|
||||
// SDL window coordinates first.
|
||||
|
||||
const int32_t border = 2; // to deal with precision issues
|
||||
|
||||
struct {
|
||||
GLint x, y, w, h;
|
||||
} game_viewport = {
|
||||
.x = Viewport_GetMinX(),
|
||||
.y = Viewport_GetMinY(),
|
||||
.w = Viewport_GetWidth(),
|
||||
.h = Viewport_GetHeight(),
|
||||
}, gl_viewport, scissor;
|
||||
glGetIntegerv(GL_VIEWPORT, &gl_viewport.x);
|
||||
|
||||
const float scale_x = gl_viewport.w / (float)game_viewport.w;
|
||||
const float scale_y = gl_viewport.h / (float)game_viewport.h;
|
||||
scissor.x = gl_viewport.x + (x * scale_x) - border;
|
||||
scissor.y = gl_viewport.y + (game_viewport.h - y) * scale_y - border;
|
||||
scissor.w = w * scale_x + border * 2;
|
||||
scissor.h = h * scale_y + border * 2;
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(scissor.x, scissor.y, scissor.w, scissor.h);
|
||||
}
|
||||
|
||||
void Output_DisableScissor(void)
|
||||
{
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
void Output_RememberState(void)
|
||||
{
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &m_CachedState.bound_program);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &m_CachedState.bound_vao);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &m_CachedState.bound_vbo);
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &m_CachedState.bound_active_texture);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &m_CachedState.bound_texture);
|
||||
glGetIntegerv(GL_POLYGON_MODE, &m_CachedState.bound_polygon_mode[0]);
|
||||
GFX_GL_CheckError();
|
||||
}
|
||||
|
||||
void Output_RestoreState(void)
|
||||
{
|
||||
glUseProgram(m_CachedState.bound_program);
|
||||
glBindVertexArray(m_CachedState.bound_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_CachedState.bound_vbo);
|
||||
glActiveTexture(m_CachedState.bound_active_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_CachedState.bound_texture);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, m_CachedState.bound_polygon_mode[0]);
|
||||
GFX_GL_CheckError();
|
||||
}
|
166
src/tr1/game/output/state.c
Normal file
166
src/tr1/game/output/state.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
#include "game/output.h"
|
||||
#include "game/output/meshes/common.h"
|
||||
#include "game/output/textures.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
static int32_t m_Time = 0;
|
||||
static int32_t m_AnimatedTexturesOffset = 0;
|
||||
|
||||
static int32_t m_DrawDistFade = 0;
|
||||
static int32_t m_DrawDistMax = 0;
|
||||
|
||||
static RGB_F m_WaterColor = {};
|
||||
|
||||
static int32_t m_LsAdder = 0;
|
||||
static int32_t m_LsDivider = 0;
|
||||
static XYZ_32 m_LsVectorView = {};
|
||||
|
||||
static bool m_IsWibbleEffect = false;
|
||||
static bool m_IsWaterEffect = false;
|
||||
static bool m_IsShadeEffect = false;
|
||||
|
||||
int32_t Output_GetDrawDistMin(void)
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
int32_t Output_GetDrawDistFade(void)
|
||||
{
|
||||
return m_DrawDistFade;
|
||||
}
|
||||
|
||||
int32_t Output_GetDrawDistMax(void)
|
||||
{
|
||||
return m_DrawDistMax;
|
||||
}
|
||||
|
||||
void Output_SetDrawDistFade(const int32_t dist)
|
||||
{
|
||||
m_DrawDistFade = dist;
|
||||
}
|
||||
|
||||
void Output_SetDrawDistMax(const int32_t dist)
|
||||
{
|
||||
m_DrawDistMax = dist;
|
||||
|
||||
const double near_z = Output_GetNearZ();
|
||||
const double far_z = Output_GetFarZ();
|
||||
const double res_z = 0.99 * near_z * far_z / (far_z - near_z);
|
||||
g_FltResZ = res_z;
|
||||
g_FltResZBuf = 0.005 + res_z / near_z;
|
||||
}
|
||||
|
||||
int32_t Output_GetNearZ(void)
|
||||
{
|
||||
return Output_GetDrawDistMin() << W2V_SHIFT;
|
||||
}
|
||||
|
||||
int32_t Output_GetFarZ(void)
|
||||
{
|
||||
return Output_GetDrawDistMax() << W2V_SHIFT;
|
||||
}
|
||||
|
||||
void Output_SetupBelowWater(const bool underwater)
|
||||
{
|
||||
m_IsWaterEffect = true;
|
||||
m_IsWibbleEffect = !underwater;
|
||||
m_IsShadeEffect = true;
|
||||
Output_RememberState();
|
||||
Output_Shader_UploadWaterEffect(Output_Meshes_GetShader(), m_IsWaterEffect);
|
||||
Output_RestoreState();
|
||||
}
|
||||
|
||||
void Output_SetupAboveWater(const bool underwater)
|
||||
{
|
||||
m_IsWaterEffect = false;
|
||||
m_IsWibbleEffect = underwater;
|
||||
m_IsShadeEffect = underwater;
|
||||
Output_RememberState();
|
||||
Output_Shader_UploadWaterEffect(Output_Meshes_GetShader(), m_IsWaterEffect);
|
||||
Output_RestoreState();
|
||||
}
|
||||
|
||||
bool Output_GetWaterEffect(void)
|
||||
{
|
||||
return m_IsWaterEffect;
|
||||
}
|
||||
|
||||
bool Output_GetWibbleEffect(void)
|
||||
{
|
||||
return m_IsWibbleEffect;
|
||||
}
|
||||
|
||||
void Output_SetWaterColor(const RGB_F *const color)
|
||||
{
|
||||
m_WaterColor.r = color->r;
|
||||
m_WaterColor.g = color->g;
|
||||
m_WaterColor.b = color->b;
|
||||
}
|
||||
|
||||
RGB_F Output_GetTint(void)
|
||||
{
|
||||
if (m_IsShadeEffect) {
|
||||
return m_WaterColor;
|
||||
}
|
||||
return (RGB_F) { 1.0f, 1.0f, 1.0f };
|
||||
}
|
||||
|
||||
void Output_SetLightAdder(const int32_t adder)
|
||||
{
|
||||
m_LsAdder = adder;
|
||||
}
|
||||
|
||||
int32_t Output_GetLightAdder(void)
|
||||
{
|
||||
return m_LsAdder;
|
||||
}
|
||||
|
||||
void Output_SetLightDivider(const int32_t divider)
|
||||
{
|
||||
m_LsDivider = divider;
|
||||
}
|
||||
|
||||
int32_t Output_GetLightDivider(void)
|
||||
{
|
||||
return m_LsDivider;
|
||||
}
|
||||
|
||||
XYZ_32 Output_GetLightVectorView(void)
|
||||
{
|
||||
return m_LsVectorView;
|
||||
}
|
||||
|
||||
void Output_RotateLight(const int16_t pitch, const int16_t yaw)
|
||||
{
|
||||
const int32_t cp = Math_Cos(pitch);
|
||||
const int32_t sp = Math_Sin(pitch);
|
||||
const int32_t cy = Math_Cos(yaw);
|
||||
const int32_t sy = Math_Sin(yaw);
|
||||
const int32_t x = TRIGMULT2(cp, sy);
|
||||
const int32_t y = -sp;
|
||||
const int32_t z = TRIGMULT2(cp, cy);
|
||||
const MATRIX *const m = &g_W2VMatrix;
|
||||
m_LsVectorView.x = (m->_00 * x + m->_01 * y + m->_02 * z) >> W2V_SHIFT;
|
||||
m_LsVectorView.y = (m->_10 * x + m->_11 * y + m->_12 * z) >> W2V_SHIFT;
|
||||
m_LsVectorView.z = (m->_20 * x + m->_21 * y + m->_22 * z) >> W2V_SHIFT;
|
||||
}
|
||||
|
||||
int32_t Output_GetTime(void)
|
||||
{
|
||||
return m_Time;
|
||||
}
|
||||
|
||||
void Output_AnimateTextures(const int32_t num_frames)
|
||||
{
|
||||
m_Time += num_frames;
|
||||
m_AnimatedTexturesOffset += num_frames;
|
||||
bool update = false;
|
||||
while (m_AnimatedTexturesOffset > 5) {
|
||||
Output_CycleAnimatedTextures();
|
||||
update = true;
|
||||
m_AnimatedTexturesOffset -= 5;
|
||||
}
|
||||
if (update) {
|
||||
Output_Textures_CycleAnimations();
|
||||
}
|
||||
}
|
|
@ -244,12 +244,15 @@ sources = [
|
|||
'game/option/option_passport.c',
|
||||
'game/option/option_sound.c',
|
||||
'game/output.c',
|
||||
'game/output/draw_misc.c',
|
||||
'game/output/func.c',
|
||||
'game/output/meshes/common.c',
|
||||
'game/output/meshes/dynamic.c',
|
||||
'game/output/meshes/objects.c',
|
||||
'game/output/meshes/rooms.c',
|
||||
'game/output/shader.c',
|
||||
'game/output/sprites.c',
|
||||
'game/output/state.c',
|
||||
'game/output/textures.c',
|
||||
'game/overlay.c',
|
||||
'game/requester.c',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue