mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 12:47:58 +03:00
output: allow aspect-ratio specific images
This commit is contained in:
parent
fbc26fcba6
commit
daf6dc3020
10 changed files with 250 additions and 32 deletions
|
@ -690,7 +690,18 @@ default game flow for examples.
|
|||
</td>
|
||||
<td><code>path</code></td>
|
||||
<td>String</td>
|
||||
<td> Displays the specified picture for a fixed time. </td>
|
||||
<td>
|
||||
Displays the specified picture for a fixed time.
|
||||
Files that are needed to function only with a specific aspect ratio can
|
||||
be placed in a directory adjacent to the main image, named according to
|
||||
the aspect ratio – for example, 4x3/title.png or 16x10/title.png. The
|
||||
game won't attempt to match these precisely; instead, it will select the
|
||||
file with the aspect ratio closest to the game's viewport. The main image
|
||||
designated by <code>path</code> is presumed to have a 16:9 aspect ratio
|
||||
for this purpose, and as such there's no need for 16x9-specific
|
||||
directory.<br/>
|
||||
This logic applies to all images.
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td><code>display_time</code></td>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
- added an ability to customize the water color [see the reference](/docs/GAME_FLOW.md#water-color-table) (#1532)
|
||||
- added support for a hex water color notation (eg. `#80FFFF`) in the game flow file
|
||||
- added support for antitriggers, like TR2+ (#2580)
|
||||
- added support for aspect ratio-specific images (#1840)
|
||||
- changed the `draw_distance_min` and `draw_distance_max` to `fog_start` and `fog_end`
|
||||
- changed `Select Detail` dialog title to `Graphic Options`
|
||||
- changed the number of static mesh slots from 50 to 256 (#2734)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
- added the ability for spike walls to be reset (antitriggered)
|
||||
- added the current music track and timestamp to the savegame so they now persist on load (#2579)
|
||||
- added waterfalls to the savegame so that they now persist on load (#2686)
|
||||
- added support for aspect ratio-specific images (#1840)
|
||||
- changed savegame files to be stored in the `saves` directory (#2087)
|
||||
- changed the default fog distance to 22 tiles cutting off at 30 tiles to match TR1X (#1622)
|
||||
- changed the number of static mesh slots from 50 to 256 (#2734)
|
||||
|
|
209
src/libtrx/game/output/background.c
Normal file
209
src/libtrx/game/output/background.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
#include "game/output/background.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "filesystem.h"
|
||||
#include "game/viewport.h"
|
||||
#include "log.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "utils.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define M_RELATIVE_ERROR(a, b) ABS((a) - (b)) / (b)
|
||||
|
||||
typedef struct {
|
||||
char *file_name;
|
||||
float diff;
|
||||
} M_CANDIDATE;
|
||||
|
||||
static char *m_LastPath = nullptr;
|
||||
|
||||
static IMAGE *M_CreateImageFromPath(const char *path);
|
||||
static float M_GetScreenAspectRatio(void);
|
||||
static int M_CompareCandidates(const void *a, const void *b);
|
||||
static VECTOR *M_ScanCandidates(const char *path, size_t *dir_len_out);
|
||||
static bool M_TryLoadCandidates(
|
||||
const char *path, VECTOR *candidates, size_t dir_len);
|
||||
static void M_FreeCandidates(VECTOR *candidates);
|
||||
|
||||
static IMAGE *M_CreateImageFromPath(const char *const path)
|
||||
{
|
||||
if (TR_VERSION == 1) {
|
||||
return Image_CreateFromFileInto(
|
||||
path, Viewport_GetWidth(), Viewport_GetHeight(), IMAGE_FIT_SMART);
|
||||
} else {
|
||||
return Image_CreateFromFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
static float M_GetScreenAspectRatio(void)
|
||||
{
|
||||
return Viewport_GetWidth() / (float)Viewport_GetHeight();
|
||||
}
|
||||
|
||||
static int M_CompareCandidates(const void *const a, const void *const b)
|
||||
{
|
||||
const M_CANDIDATE *const c1 = a;
|
||||
const M_CANDIDATE *const c2 = b;
|
||||
if (c1->diff < c2->diff) {
|
||||
return -1;
|
||||
}
|
||||
if (c1->diff > c2->diff) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static VECTOR *M_ScanCandidates(
|
||||
const char *const path, size_t *const dir_len_out)
|
||||
{
|
||||
VECTOR *candidates = nullptr;
|
||||
|
||||
const char *last_slash = strrchr(path, '/');
|
||||
const char *last_backslash = strrchr(path, '\\');
|
||||
const char *last_sep =
|
||||
last_slash > last_backslash ? last_slash : last_backslash;
|
||||
size_t dir_len;
|
||||
char *dir_path;
|
||||
if (last_sep != nullptr) {
|
||||
dir_len = last_sep - path;
|
||||
dir_path = String_Format("%.*s", (int)dir_len, path);
|
||||
} else {
|
||||
dir_len = 0;
|
||||
dir_path = Memory_DupStr(".");
|
||||
}
|
||||
|
||||
const char *file_name = last_sep ? last_sep + 1 : path;
|
||||
const char *ext_ptr = strrchr(file_name, '.');
|
||||
const float screen_ratio = M_GetScreenAspectRatio();
|
||||
|
||||
void *const dir_handle = File_OpenDirectory(dir_path);
|
||||
if (dir_handle == nullptr) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
candidates = Vector_Create(sizeof(M_CANDIDATE));
|
||||
const char *entry;
|
||||
while ((entry = File_ReadDirectory(dir_handle)) != nullptr) {
|
||||
// Match the file itself, and assume it's of 16:9 aspect ratio.
|
||||
if (String_Equivalent(entry, file_name)) {
|
||||
const float ratio = 16.0f / 9.0f;
|
||||
Vector_Add(
|
||||
candidates,
|
||||
&(M_CANDIDATE) {
|
||||
.file_name = Memory_DupStr(file_name),
|
||||
.diff = M_RELATIVE_ERROR(ratio, screen_ratio),
|
||||
});
|
||||
}
|
||||
|
||||
// Match directories with pattern: <width>x<height>
|
||||
int32_t w = 0, h = 0;
|
||||
if (sscanf(entry, "%dx%d", &w, &h) == 2) {
|
||||
const float ratio = w / (float)h;
|
||||
Vector_Add(
|
||||
candidates,
|
||||
&(M_CANDIDATE) {
|
||||
.file_name = String_Format("%s/%s", entry, file_name),
|
||||
.diff = M_RELATIVE_ERROR(ratio, screen_ratio),
|
||||
});
|
||||
}
|
||||
}
|
||||
File_CloseDirectory(dir_handle);
|
||||
|
||||
finish:
|
||||
*dir_len_out = dir_len;
|
||||
Memory_FreePointer(&dir_path);
|
||||
return candidates;
|
||||
}
|
||||
|
||||
static bool M_TryLoadCandidates(
|
||||
const char *const path, VECTOR *const candidates, const size_t dir_len)
|
||||
{
|
||||
for (int32_t i = 0; i < candidates->count; i++) {
|
||||
const M_CANDIDATE *candidate = Vector_Get(candidates, i);
|
||||
char *full_path;
|
||||
if (dir_len > 0) {
|
||||
full_path = String_Format(
|
||||
"%.*s/%s", (int32_t)dir_len, path, candidate->file_name);
|
||||
} else {
|
||||
full_path = String_Format("%s", candidate->file_name);
|
||||
}
|
||||
IMAGE *const image = M_CreateImageFromPath(full_path);
|
||||
Memory_FreePointer(&full_path);
|
||||
if (image != nullptr) {
|
||||
Output_LoadBackgroundFromImage(image);
|
||||
Image_Free(image);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void M_FreeCandidates(VECTOR *const candidates)
|
||||
{
|
||||
if (candidates == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (int32_t i = 0; i < candidates->count; i++) {
|
||||
M_CANDIDATE *const candidate = Vector_Get(candidates, i);
|
||||
Memory_Free(candidate->file_name);
|
||||
}
|
||||
Vector_Free(candidates);
|
||||
}
|
||||
|
||||
bool Output_LoadBackgroundFromFile(const char *const path)
|
||||
{
|
||||
LOG_INFO("Loading image %s", path);
|
||||
size_t dir_len = 0;
|
||||
bool result = false;
|
||||
|
||||
// Try aspect-ratio specific directories.
|
||||
VECTOR *candidates = M_ScanCandidates(path, &dir_len);
|
||||
if (candidates != nullptr) {
|
||||
M_CANDIDATE *raw_candidates = Vector_GetData(candidates);
|
||||
qsort(
|
||||
raw_candidates, candidates->count, sizeof(M_CANDIDATE),
|
||||
M_CompareCandidates);
|
||||
for (int32_t i = 0; i < candidates->count; i++) {
|
||||
const M_CANDIDATE *const candidate = Vector_Get(candidates, i);
|
||||
LOG_INFO(
|
||||
"Found candidate %s (diff=%.02f)", candidate->file_name,
|
||||
candidate->diff);
|
||||
}
|
||||
if (M_TryLoadCandidates(path, candidates, dir_len)) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
// Fallback to the main image.
|
||||
IMAGE *const image = M_CreateImageFromPath(path);
|
||||
if (image != nullptr) {
|
||||
result = true;
|
||||
Output_LoadBackgroundFromImage(image);
|
||||
Image_Free(image);
|
||||
}
|
||||
}
|
||||
|
||||
M_FreeCandidates(candidates);
|
||||
if (result) {
|
||||
char *prev = m_LastPath;
|
||||
m_LastPath = Memory_DupStr(path);
|
||||
Memory_FreePointer(&prev);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char *Output_GetLastBackgroundPath(void)
|
||||
{
|
||||
return m_LastPath;
|
||||
}
|
||||
|
||||
void Output_ClearLastBackgroundPath(void)
|
||||
{
|
||||
Memory_FreePointer(&m_LastPath);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "./output/background.h"
|
||||
#include "./output/common.h"
|
||||
#include "./output/const.h"
|
||||
#include "./output/draw.h"
|
||||
|
|
15
src/libtrx/include/libtrx/game/output/background.h
Normal file
15
src/libtrx/include/libtrx/game/output/background.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../engine/image.h"
|
||||
|
||||
bool Output_LoadBackgroundFromFile(const char *path);
|
||||
|
||||
extern bool Output_LoadBackgroundFromImage(const IMAGE *image);
|
||||
extern void Output_LoadBackgroundFromObject(void);
|
||||
extern void Output_UnloadBackground(void);
|
||||
|
||||
extern void Output_DrawBackground(void);
|
||||
|
||||
// TODO: make these functions private once output module is consolidated
|
||||
char *Output_GetLastBackgroundPath(void);
|
||||
void Output_ClearLastBackgroundPath(void);
|
|
@ -10,12 +10,7 @@ extern bool Output_MakeScreenshot(const char *path);
|
|||
extern void Output_BeginScene(void);
|
||||
extern void Output_EndScene(void);
|
||||
|
||||
extern bool Output_LoadBackgroundFromFile(const char *file_name);
|
||||
extern void Output_LoadBackgroundFromObject(void);
|
||||
extern void Output_UnloadBackground(void);
|
||||
|
||||
extern void Output_DrawBlackRectangle(int32_t opacity);
|
||||
extern void Output_DrawBackground(void);
|
||||
extern void Output_DrawPolyList(void);
|
||||
|
||||
extern void Output_SetupBelowWater(bool is_underwater);
|
||||
|
|
|
@ -181,6 +181,7 @@ sources = [
|
|||
'game/objects/names.c',
|
||||
'game/objects/traps/movable_block.c',
|
||||
'game/objects/vars.c',
|
||||
'game/output/background.c',
|
||||
'game/output/common.c',
|
||||
'game/output/textures.c',
|
||||
'game/packer.c',
|
||||
|
|
|
@ -63,7 +63,6 @@ static int32_t m_SurfaceHeight = 0;
|
|||
static GFX_2D_SURFACE *m_PictureSurface = nullptr;
|
||||
static GFX_2D_SURFACE *m_TextureSurfaces[GFX_MAX_TEXTURES] = { nullptr };
|
||||
|
||||
static char *m_BackdropImagePath = nullptr;
|
||||
static const char *m_ImageExtensions[] = {
|
||||
".png", ".jpg", ".jpeg", ".pcx", nullptr,
|
||||
};
|
||||
|
@ -318,7 +317,6 @@ static void M_DrawLightningSegment(const LIGHTNING *const lightning)
|
|||
vertices[vtx_idx].b = color.b; \
|
||||
vertices[vtx_idx].a = 128.0f;
|
||||
// clang-format off
|
||||
LOG_INFO("%d %d %d, %d %d %d, %d", p0.x, p0.y, p0.z, p1.x, p1.y, p1.z, t1);
|
||||
SET(0, p0.x, p0.y, p0.z, blue);
|
||||
SET(1, p0.x + t1 / 2, p0.y, p0.z, white);
|
||||
SET(2, p1.x + t2 / 2, p1.y, p1.z, white);
|
||||
|
@ -424,7 +422,7 @@ void Output_Shutdown(void)
|
|||
m_Renderer3D = nullptr;
|
||||
}
|
||||
GFX_Context_Detach();
|
||||
Memory_FreePointer(&m_BackdropImagePath);
|
||||
Output_ClearLastBackgroundPath();
|
||||
}
|
||||
|
||||
void Output_SetWindowSize(int32_t width, int32_t height)
|
||||
|
@ -467,8 +465,9 @@ void Output_ApplyRenderSettings(void)
|
|||
GFX_3D_Renderer_SetAnisotropyFilter(
|
||||
m_Renderer3D, g_Config.rendering.anisotropy_filter);
|
||||
|
||||
if (m_BackdropImagePath != nullptr) {
|
||||
Output_LoadBackgroundFromFile(m_BackdropImagePath);
|
||||
const char *const last_path = Output_GetLastBackgroundPath();
|
||||
if (last_path != nullptr) {
|
||||
Output_LoadBackgroundFromFile(last_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -781,21 +780,9 @@ void Output_DrawUISprite(
|
|||
}
|
||||
}
|
||||
|
||||
bool Output_LoadBackgroundFromFile(const char *const path)
|
||||
bool Output_LoadBackgroundFromImage(const IMAGE *const image)
|
||||
{
|
||||
ASSERT(path != nullptr);
|
||||
const char *old_path = m_BackdropImagePath;
|
||||
m_BackdropImagePath = File_GuessExtension(path, m_ImageExtensions);
|
||||
Memory_FreePointer(&old_path);
|
||||
|
||||
IMAGE *const img = Image_CreateFromFileInto(
|
||||
m_BackdropImagePath, Viewport_GetWidth(), Viewport_GetHeight(),
|
||||
IMAGE_FIT_SMART);
|
||||
if (img == nullptr) {
|
||||
return false;
|
||||
}
|
||||
M_DownloadBackdropSurface(img);
|
||||
Image_Free(img);
|
||||
M_DownloadBackdropSurface(image);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -808,7 +795,7 @@ void Output_LoadBackgroundFromObject(void)
|
|||
void Output_UnloadBackground(void)
|
||||
{
|
||||
M_DownloadBackdropSurface(nullptr);
|
||||
Memory_FreePointer(&m_BackdropImagePath);
|
||||
Output_ClearLastBackgroundPath();
|
||||
}
|
||||
|
||||
void Output_DrawLightningSegment(
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <libtrx/game/scaler.h>
|
||||
#include <libtrx/log.h>
|
||||
#include <libtrx/memory.h>
|
||||
#include <libtrx/strings.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
typedef enum {
|
||||
|
@ -738,14 +739,9 @@ BACKGROUND_TYPE Output_GetBackgroundType(void)
|
|||
return m_BackgroundType;
|
||||
}
|
||||
|
||||
bool Output_LoadBackgroundFromFile(const char *const file_name)
|
||||
bool Output_LoadBackgroundFromImage(const IMAGE *const image)
|
||||
{
|
||||
IMAGE *const image = Image_CreateFromFile(file_name);
|
||||
if (image == nullptr) {
|
||||
return false;
|
||||
}
|
||||
Render_LoadBackgroundFromImage(image);
|
||||
Image_Free(image);
|
||||
m_BackgroundType = BK_IMAGE;
|
||||
return true;
|
||||
}
|
||||
|
@ -774,6 +770,7 @@ void Output_UnloadBackground(void)
|
|||
{
|
||||
Render_UnloadBackground();
|
||||
m_BackgroundType = BK_TRANSPARENT;
|
||||
Output_ClearLastBackgroundPath();
|
||||
}
|
||||
|
||||
void Output_InsertBackPolygon(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue