mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 14:00:25 +02:00
Merge pull request #286 from hkmodd/fix/hires-text-fit
This commit is contained in:
commit
bbe00a4049
4 files changed
+91
-31
No files matched your search
@@ -1038,7 +1038,7 @@ crafted file) is refused, and the level starts over.
|
||||
| Game code | All 466 C files of the game. The changes are in "Game source changes". |
|
||||
| Graphics | Direct3D 8 on OpenGL 4.5 core through SDL3 (`src/d3d8_gl.c`). The port translates the NV2A vertex shaders and register combiners to GLSL. It decodes all the Xbox texture formats. The vertex and index buffers come from a GL copy of the Xbox memory. |
|
||||
| High-res HUD | The HUD is drawn from high-res assets: redraws at 8x the size of the maps' bitmaps (4x for the largest), in `port/assets/hud`. They cover the meters, counters, panels and their outlines, the motion sensor, reticles, waypoints and scopes, but no bitmap with English text. `tools/hud_assets.py` makes them from the SVG redraws (the weapons' reticles from those of the PC HUD sheet that match the Xbox's exactly, the sniper rifle's 2x and 10x set in Overpass), and the build embeds them in the executable. When the game uploads one of those bitmaps, `src/hud_hires.c` gives the high-res texture in its place, if the bitmap's pixels are those of the English maps: another language's maps keep their own. The game sizes and places the HUD from its tags as before. `display.high_res_hud = false` turns this off. |
|
||||
| High-res text | The menus' and HUD's text is drawn with Overpass (`port/assets/fonts`, SIL Open Font License) in place of the maps' bitmap fonts, which are Interstate. `src/text_hires.c` rasterizes each glyph with stb_truetype (`port/third_party/stb`) at the resolution the game draws at, into an atlas that a placeholder bitmap of the game stands for. The game lays the text out from its font tags as before. The menus' titles (the screens' headers and the main menu's items) are pictures of text in the maps, so they are drawn as the high-res HUD is: `tools/title_assets.py` sets each one again in OpenCE, Roger White's public-domain Newtown respaced to match the maps' commercial title typeface (`tools/title_font.py`), at 4x the bitmap's size over its own plate or glow, each letter placed where the map's letter is, in `port/assets/titles`. The postgame carnage report's title is set over a hand-made SVG redraw of its panel (`port/assets/titles/svg`) instead. The A, B, X and Y button icons beside the menus' key labels and in their text ("Press A to Join") are drawn from hand-made SVG redraws at 8x the size of the maps' bitmaps (`port/assets/buttons`, made by `tools/button_assets.py`), in the same sizes and places. `display.high_res_text = false` turns it off. |
|
||||
| High-res text | The menus' and HUD's text is drawn with Overpass (`port/assets/fonts`, SIL Open Font License) in place of the maps' bitmap fonts, which are Interstate. `src/text_hires.c` rasterizes each glyph with stb_truetype (`port/third_party/stb`) at the resolution the game draws at, into an atlas that a placeholder bitmap of the game stands for. The game lays the text out from its font tags as before: each font is as tall as its tag's capitals, and narrowed where the tag's letters are narrower than the Xbox maps' (the fonts of a Custom Edition `loc.map`), so that its letters do not run into each other. The menus' titles (the screens' headers and the main menu's items) are pictures of text in the maps, so they are drawn as the high-res HUD is: `tools/title_assets.py` sets each one again in OpenCE, Roger White's public-domain Newtown respaced to match the maps' commercial title typeface (`tools/title_font.py`), at 4x the bitmap's size over its own plate or glow, each letter placed where the map's letter is, in `port/assets/titles`. The postgame carnage report's title is set over a hand-made SVG redraw of its panel (`port/assets/titles/svg`) instead. The A, B, X and Y button icons beside the menus' key labels and in their text ("Press A to Join") are drawn from hand-made SVG redraws at 8x the size of the maps' bitmaps (`port/assets/buttons`, made by `tools/button_assets.py`), in the same sizes and places. `display.high_res_text = false` turns it off. |
|
||||
| Anti-aliasing | Off unless `display.anti_aliasing` is set (`src/d3d8_gl.c`, `src/xgpu_post.c`). FXAA (written in the port) and SMAA (`port/third_party/smaa`, MIT licensed, at its HIGH preset, compiled as GLSL) are passes over the 3D view of each window, after the lens flares and before the HUD and the menus (`render/render.c`). Their programs are built when the setting is chosen. Supersampling draws the render targets the size of the screen at two times the resolution in each direction, and the display blit scales them down. Multisampling draws the back buffer and its depth buffer into multisampled renderbuffers, and with them any target that is drawn together with one of them (a mirror's view, in the secondary target with the back buffer's depth buffer), so that the attachments of a framebuffer are all multisampled or none is. A target's pixels are resolved into its texture before something reads the texture (as a texture, or at the display blit). Visibility tests count samples, divided by the samples of a pixel. An alpha-tested surface (foliage, grates) covers the samples of a pixel in proportion to its alpha past the reference (`gl_SampleMask`, not on Android). |
|
||||
| Sound | Xbox DirectSound on SDL3 audio (`src/dsound_sdl.c`): PCM and Xbox ADPCM, mixed at 48 kHz, with volume, pitch, mix bins, distance, stereo pan, occlusion and obstruction. There is no Doppler effect, no cones and no reverb. |
|
||||
| Input | XInput on SDL3 (`src/xinput_sdl.c`): keyboard, mouse, gamepads with rumble, and the debug keyboard for the console. |
|
||||
|
||||
@@ -6,7 +6,10 @@ The game's text drawn with fonts at the display's resolution (text_hires.h).
|
||||
Each font tag the fonts draw (port/assets/fonts/fonts.json) gets its font
|
||||
sized so that its capitals are as tall as the tag's: the game's layout,
|
||||
from the tag's character widths, is kept, and the fonts were chosen to fit
|
||||
those widths. A glyph is rasterized when first drawn, at the display's
|
||||
the Xbox maps' widths. A tag whose letters are narrower (the fonts of a
|
||||
Custom Edition loc.map) gets its font narrowed until its letters are, on
|
||||
the whole, as wide as the tag's, so that they do not run into each other;
|
||||
the Xbox maps' fonts are narrowed by a few hundredths. A glyph is rasterized when first drawn, at the display's
|
||||
pixels per unit of the 480 lines, into an 8-bit atlas; the GL texture the
|
||||
game's placeholder bitmap stands for (xbox_textures.c) is the atlas, its new
|
||||
rows uploaded when the placeholder is bound. A change of the display's
|
||||
@@ -28,7 +31,9 @@ scale (a window made fullscreen) or a full atlas starts it over.
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "../../third_party/stb/stb_truetype.h"
|
||||
|
||||
#define MAXIMUM_FONTS 8
|
||||
/* (the Xbox maps' tags and a Custom Edition loc.map's, which share names,
|
||||
each at its oversamples) */
|
||||
#define MAXIMUM_FONTS 16
|
||||
#define ATLAS_SIZE 2048
|
||||
/* clear texels around each glyph, so that filtering never reaches the next */
|
||||
#define ATLAS_PADDING 2
|
||||
@@ -37,9 +42,14 @@ scale (a window made fullscreen) or a full atlas starts it over.
|
||||
static struct
|
||||
{
|
||||
char tag[64];
|
||||
/* the tag's measures it was made for */
|
||||
float cap_height;
|
||||
long letters_width;
|
||||
stbtt_fontinfo info;
|
||||
/* font units to units of the 480 lines */
|
||||
float scale;
|
||||
/* the glyphs' widths, against their heights, to fit the tag's letters */
|
||||
float narrowing;
|
||||
/* how many times the display's pixels its glyphs are rasterized with */
|
||||
float oversample;
|
||||
} fonts[MAXIMUM_FONTS];
|
||||
@@ -113,27 +123,37 @@ static void atlas_reset(float scale)
|
||||
dirty_bottom = ATLAS_SIZE;
|
||||
}
|
||||
|
||||
long text_hires_font(char const *tag_name, float cap_height, float oversample)
|
||||
long text_hires_font(char const *tag_name, float cap_height, short const *letter_widths, float oversample)
|
||||
{
|
||||
unsigned int index;
|
||||
long font;
|
||||
long letters_width = 0;
|
||||
int x0, y0, x1, y1;
|
||||
int letter;
|
||||
|
||||
if (!text_enabled() || !tag_name || cap_height <= 0.0f)
|
||||
if (!text_enabled() || !tag_name || cap_height <= 0.0f || !letter_widths)
|
||||
return -1;
|
||||
/* (text is never drawn with fewer pixels than the display's) */
|
||||
if (oversample < 1.0f)
|
||||
oversample = 1.0f;
|
||||
for (letter = 0; letter < TEXT_HIRES_FIT_LETTERS; letter++)
|
||||
letters_width += letter_widths[letter] > 0 ? letter_widths[letter] : 0;
|
||||
for (font = 0; font < font_count; font++)
|
||||
{
|
||||
if (!names_differ(fonts[font].tag, tag_name) && fonts[font].oversample == oversample)
|
||||
if (!names_differ(fonts[font].tag, tag_name) && fonts[font].cap_height == cap_height &&
|
||||
fonts[font].letters_width == letters_width && fonts[font].oversample == oversample)
|
||||
{
|
||||
return fonts[font].scale > 0.0f ? font : -1;
|
||||
}
|
||||
}
|
||||
if (font_count >= MAXIMUM_FONTS)
|
||||
return -1;
|
||||
font = font_count++;
|
||||
snprintf(fonts[font].tag, sizeof(fonts[font].tag), "%s", tag_name);
|
||||
fonts[font].cap_height = cap_height;
|
||||
fonts[font].letters_width = letters_width;
|
||||
fonts[font].scale = 0.0f;
|
||||
fonts[font].narrowing = 1.0f;
|
||||
fonts[font].oversample = oversample;
|
||||
for (index = 0; index < text_hires_embedded_count; index++)
|
||||
{
|
||||
@@ -149,7 +169,26 @@ long text_hires_font(char const *tag_name, float cap_height, float oversample)
|
||||
break;
|
||||
}
|
||||
fonts[font].scale = cap_height / (float)(y1 - y0);
|
||||
platform_log("high-res text: %s drawn with %s", tag_name, embedded->file);
|
||||
{
|
||||
float tag_width = 0.0f;
|
||||
float font_width = 0.0f;
|
||||
|
||||
for (letter = 0; letter < TEXT_HIRES_FIT_LETTERS; letter++)
|
||||
{
|
||||
int glyph = stbtt_FindGlyphIndex(&fonts[font].info, letter < 26 ? 'A' + letter : 'a' + letter - 26);
|
||||
int advance = 0, bearing = 0;
|
||||
|
||||
if (letter_widths[letter] <= 0 || !glyph)
|
||||
continue;
|
||||
stbtt_GetGlyphHMetrics(&fonts[font].info, glyph, &advance, &bearing);
|
||||
tag_width += letter_widths[letter];
|
||||
font_width += advance * fonts[font].scale;
|
||||
}
|
||||
if (tag_width > 0.0f && font_width > tag_width)
|
||||
fonts[font].narrowing = tag_width / font_width;
|
||||
}
|
||||
platform_log("high-res text: %s (capitals %.0f units) drawn with %s, %.0f%% wide",
|
||||
tag_name, cap_height, embedded->file, fonts[font].narrowing * 100.0f);
|
||||
break;
|
||||
}
|
||||
return fonts[font].scale > 0.0f ? font : -1;
|
||||
@@ -191,13 +230,14 @@ int text_hires_glyph(long font, unsigned long code, struct text_hires_glyph *gly
|
||||
{
|
||||
/* rasterized now */
|
||||
float pixels = fonts[font].scale * scale * fonts[font].oversample;
|
||||
float pixels_x = pixels * fonts[font].narrowing;
|
||||
int index = stbtt_FindGlyphIndex(&fonts[font].info, (int)code);
|
||||
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||
int width, height, advance = 0, bearing = 0;
|
||||
|
||||
if (index)
|
||||
{
|
||||
stbtt_GetGlyphBitmapBox(&fonts[font].info, index, pixels, pixels, &x0, &y0, &x1, &y1);
|
||||
stbtt_GetGlyphBitmapBox(&fonts[font].info, index, pixels_x, pixels, &x0, &y0, &x1, &y1);
|
||||
stbtt_GetGlyphHMetrics(&fonts[font].info, index, &advance, &bearing);
|
||||
}
|
||||
width = x1 - x0;
|
||||
@@ -219,7 +259,7 @@ int text_hires_glyph(long font, unsigned long code, struct text_hires_glyph *gly
|
||||
}
|
||||
stbtt_MakeGlyphBitmap(&fonts[font].info,
|
||||
atlas + (pack_y + ATLAS_PADDING) * ATLAS_SIZE + pack_x + ATLAS_PADDING,
|
||||
width, height, ATLAS_SIZE, pixels, pixels, index);
|
||||
width, height, ATLAS_SIZE, pixels_x, pixels, index);
|
||||
if (pack_y < dirty_top)
|
||||
dirty_top = pack_y;
|
||||
if (pack_y + height + 2 * ATLAS_PADDING > dirty_bottom)
|
||||
@@ -233,7 +273,7 @@ int text_hires_glyph(long font, unsigned long code, struct text_hires_glyph *gly
|
||||
glyphs[slot].height = (short)(width > 0 && height > 0 ? height : 0);
|
||||
glyphs[slot].left = (short)x0;
|
||||
glyphs[slot].top = (short)y0;
|
||||
glyphs[slot].advance = advance * fonts[font].scale;
|
||||
glyphs[slot].advance = advance * fonts[font].scale * fonts[font].narrowing;
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
pack_x += width + 2 * ATLAS_PADDING;
|
||||
|
||||
@@ -35,11 +35,15 @@ struct text_hires_glyph
|
||||
float advance;
|
||||
};
|
||||
|
||||
/* the letters a font is fitted to its tag by: A to Z, then a to z */
|
||||
#define TEXT_HIRES_FIT_LETTERS 52
|
||||
|
||||
/* the font standing for a font tag, sized so that its capitals are
|
||||
cap_height units tall, its glyphs rasterized with oversample times the
|
||||
display's pixels (for text drawn scaled up); -1 if none, or
|
||||
display.high_res_text is off */
|
||||
long text_hires_font(char const *tag_name, float cap_height, float oversample);
|
||||
cap_height units tall and narrowed where its letters are wider than the
|
||||
tag's letter_widths (TEXT_HIRES_FIT_LETTERS of them, 0 for a letter the tag
|
||||
has not), its glyphs rasterized with oversample times the display's pixels
|
||||
(for text drawn scaled up); -1 if none, or display.high_res_text is off */
|
||||
long text_hires_font(char const *tag_name, float cap_height, short const *letter_widths, float oversample);
|
||||
/* whether the font has a glyph for the character (it draws a string only
|
||||
if it has all of its characters) */
|
||||
int text_hires_covers(long font, unsigned long code);
|
||||
|
||||
@@ -88,7 +88,8 @@ struct text_hires_glyph
|
||||
float advance;
|
||||
};
|
||||
|
||||
long text_hires_font(char const *tag_name, float cap_height, float oversample);
|
||||
#define TEXT_HIRES_FIT_LETTERS 52
|
||||
long text_hires_font(char const *tag_name, float cap_height, short const *letter_widths, float oversample);
|
||||
int text_hires_covers(long font, unsigned long code);
|
||||
int text_hires_glyph(long font, unsigned long code, struct text_hires_glyph *glyph);
|
||||
void text_hires_register_atlas(unsigned long const *texture, unsigned long width, unsigned long height);
|
||||
@@ -239,6 +240,15 @@ static void rasterizer_draw_hires_character_with_dropshadow(
|
||||
static struct hardware_character_cache hardware_character_cache;
|
||||
static struct bitmap_data *hires_text_atlas = NULL;
|
||||
static long hires_text_font = NONE;
|
||||
/* port: the high-res text's fonts of the font tags of the map (forgotten
|
||||
when its tags are, by rasterizer_text_cache_flush) */
|
||||
static struct
|
||||
{
|
||||
struct font_header *font;
|
||||
real oversample;
|
||||
long hires_font;
|
||||
} hires_text_fonts[MAXIMUM_HIRES_TEXT_FONTS];
|
||||
static short hires_text_next_font = 0;
|
||||
/* port: the text's scale about a point (rasterizer_text_set_scale) */
|
||||
static real text_scale = 1.0f;
|
||||
static real text_scale_origin_x = 0.0f;
|
||||
@@ -347,6 +357,9 @@ rasterizer_text_cache_flush(
|
||||
hardware_character++;
|
||||
} while (--hardware_character_count);
|
||||
}
|
||||
/* port: the next map's font tags may be at the same addresses */
|
||||
memset(hires_text_fonts, 0, sizeof(hires_text_fonts));
|
||||
hires_text_next_font = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -793,22 +806,17 @@ static void rasterizer_text_draw_scaled_character(
|
||||
}
|
||||
|
||||
/* port: the high-res text's font for a font tag, sized by the height of its
|
||||
capital H (its rows with ink), its glyphs drawn with oversample times the
|
||||
pixels (for text scaled up), or NONE */
|
||||
capital H (its rows with ink) and fitted to the widths of its letters, its
|
||||
glyphs drawn with oversample times the pixels (for text scaled up), or NONE */
|
||||
static long hires_text_font_get(
|
||||
long font_index,
|
||||
real oversample)
|
||||
{
|
||||
static struct
|
||||
{
|
||||
struct font_header *font;
|
||||
real oversample;
|
||||
long hires_font;
|
||||
} fonts[MAXIMUM_HIRES_TEXT_FONTS];
|
||||
static short next_font = 0;
|
||||
struct font_header *font;
|
||||
struct font_character *capital;
|
||||
short letter_widths[TEXT_HIRES_FIT_LETTERS];
|
||||
short font_slot;
|
||||
short letter;
|
||||
short top = NONE;
|
||||
short bottom = NONE;
|
||||
short row;
|
||||
@@ -819,8 +827,16 @@ static long hires_text_font_get(
|
||||
font = font_definition_get(font_index);
|
||||
for (font_slot = 0; font_slot < MAXIMUM_HIRES_TEXT_FONTS; font_slot++)
|
||||
{
|
||||
if (fonts[font_slot].font == font && fonts[font_slot].oversample == oversample)
|
||||
return fonts[font_slot].hires_font;
|
||||
if (hires_text_fonts[font_slot].font == font && hires_text_fonts[font_slot].oversample == oversample)
|
||||
return hires_text_fonts[font_slot].hires_font;
|
||||
}
|
||||
/* (A to Z, then a to z; none where the tag has not the letter) */
|
||||
for (letter = 0; letter < TEXT_HIRES_FIT_LETTERS; letter++)
|
||||
{
|
||||
struct font_character *character = font_get_character_by_ascii_code(font,
|
||||
(word)(letter < 26 ? 'A' + letter : 'a' + letter - 26));
|
||||
|
||||
letter_widths[letter] = character ? character->character_width : 0;
|
||||
}
|
||||
capital = font_get_character_by_ascii_code(font, 'H');
|
||||
if (capital)
|
||||
@@ -844,13 +860,13 @@ static long hires_text_font_get(
|
||||
}
|
||||
}
|
||||
hires_font = top == NONE ? NONE :
|
||||
text_hires_font(tag_get_name(font_index), (float)(bottom - top + 1), (float)oversample);
|
||||
text_hires_font(tag_get_name(font_index), (float)(bottom - top + 1), letter_widths, (float)oversample);
|
||||
if (hires_font < 0)
|
||||
hires_font = NONE;
|
||||
fonts[next_font].font = font;
|
||||
fonts[next_font].oversample = oversample;
|
||||
fonts[next_font].hires_font = hires_font;
|
||||
next_font = (short)((next_font + 1) % MAXIMUM_HIRES_TEXT_FONTS);
|
||||
hires_text_fonts[hires_text_next_font].font = font;
|
||||
hires_text_fonts[hires_text_next_font].oversample = oversample;
|
||||
hires_text_fonts[hires_text_next_font].hires_font = hires_font;
|
||||
hires_text_next_font = (short)((hires_text_next_font + 1) % MAXIMUM_HIRES_TEXT_FONTS);
|
||||
|
||||
return hires_font;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user