add vertical text with start of duet font
This commit is contained in:
parent
562debd926
commit
dcaaec7fb1
|
@ -13,8 +13,10 @@ set(SOURCES
|
|||
src/main.c
|
||||
src/render.c
|
||||
src/physics.c
|
||||
src/text.c
|
||||
)
|
||||
set(ASSETS
|
||||
assets-cg/font.png
|
||||
assets-cg/level/level1.txt
|
||||
assets-cg/level/level2.txt
|
||||
assets-cg/level/level3.txt
|
||||
|
@ -28,7 +30,7 @@ fxconv_declare_converters(converters.py)
|
|||
fxconv_declare_assets(${ASSETS} WITH_METADATA)
|
||||
|
||||
add_executable(addin ${SOURCES} ${ASSETS})
|
||||
target_compile_options(addin PRIVATE -Wall -Wextra -Os)
|
||||
target_compile_options(addin PRIVATE -Wall -Wextra -Os -ffast-math)
|
||||
target_link_libraries(addin Gint::Gint)
|
||||
|
||||
generate_g3a(TARGET addin OUTPUT "Duet.g3a"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 925 B |
|
@ -1,3 +1,9 @@
|
|||
example.png:
|
||||
type: bopti-image
|
||||
name: img_example
|
||||
font.png:
|
||||
type: font
|
||||
name: font_duet
|
||||
charset: print
|
||||
grid.size: 9x13
|
||||
grid.padding: 1
|
||||
grid.border: 0
|
||||
proportional: true
|
||||
height: 10
|
||||
|
|
|
@ -96,6 +96,14 @@ void drectoid(rect_t const *r, int color);
|
|||
|
||||
void render_player(float angle);
|
||||
|
||||
//---
|
||||
// Duet Text
|
||||
//---
|
||||
|
||||
/* (x,y) is screen as usual, but align is relative to rotated text */
|
||||
void duet_text_opt(int x, int y, int fg, int bg, int halign, int valign,
|
||||
char const *str, int size);
|
||||
|
||||
//---
|
||||
// Physics
|
||||
//---
|
||||
|
|
31
src/main.c
31
src/main.c
|
@ -35,20 +35,16 @@ level_t level0 = {
|
|||
|
||||
int main(void)
|
||||
{
|
||||
volatile int need_frame = 1;
|
||||
|
||||
game_t game;
|
||||
float dt = 0;
|
||||
int timer;
|
||||
|
||||
__printf_enable_fp();
|
||||
|
||||
game_t game;
|
||||
memset(&game, 0x00, sizeof game);
|
||||
game.level = &level4;
|
||||
game.current = game.level->blocks;
|
||||
game.time = -5.0;
|
||||
|
||||
timer = timer_configure(TIMER_ANY, 33000, GINT_CALL_SET(&need_frame));
|
||||
volatile int need_frame = 1;
|
||||
int timer = timer_configure(TIMER_ANY, 33000, GINT_CALL_SET(&need_frame));
|
||||
timer_start(timer);
|
||||
|
||||
while (1) {
|
||||
|
@ -57,7 +53,7 @@ int main(void)
|
|||
while (need_frame == 0) sleep();
|
||||
need_frame = 0;
|
||||
|
||||
dt = (1.0 / 30) * game.level->tempo * 2.7;
|
||||
float dt = (1.0 / 30) * game.level->tempo * 2.7;
|
||||
game.time += dt;
|
||||
|
||||
/* Input analysis */
|
||||
|
@ -78,15 +74,15 @@ int main(void)
|
|||
|
||||
/* Level generation */
|
||||
|
||||
// Remove rectangles that have passed their lifetime by 2 seconds
|
||||
// Remove rectangles that have passed their lifetime by 4 tempo
|
||||
for(int i = 0; i < game.cursor;) {
|
||||
if(game.time > game.table[i].meta->time + 2)
|
||||
if(game.time > game.table[i].meta->time + 4)
|
||||
game.table[i] = game.table[--game.cursor];
|
||||
else
|
||||
i++;
|
||||
}
|
||||
|
||||
// Find rectangles that need to be loaded
|
||||
// Find rectangles that need to be loaded, 10 tempo in advance
|
||||
rectmeta_t const *meta = game.current;
|
||||
while(meta < game.level->blocks + game.level->block_count) {
|
||||
if(meta - game.current > RECT_TABLE_SIZE - game.cursor)
|
||||
|
@ -103,6 +99,11 @@ int main(void)
|
|||
rect_load(r, r->meta);
|
||||
}
|
||||
|
||||
// End of level
|
||||
if(game.current >= game.level->blocks + game.level->block_count
|
||||
&& game.cursor == 0)
|
||||
break;
|
||||
|
||||
/* Physics */
|
||||
|
||||
// if(player_collision(&game))
|
||||
|
@ -120,12 +121,14 @@ int main(void)
|
|||
/* Rendering */
|
||||
|
||||
dclear(player_collision(&game) ? C_RED : C_BLACK);
|
||||
dprint(0, 0, C_WHITE, "game time: %.2fs", game.time);
|
||||
dprint(0, 11, C_WHITE, "rectangles loaded: %d", game.cursor);
|
||||
|
||||
if(game.time < -2)
|
||||
duet_text_opt(DWIDTH*3/4, DHEIGHT/2, C_WHITE, C_NONE, DTEXT_CENTER,
|
||||
DTEXT_MIDDLE, "Change is inevitable.", -1);
|
||||
|
||||
render_player(game.player_rota);
|
||||
for(int i = 0; i < game.cursor; i++)
|
||||
drectoid(&game.table[i], C_WHITE);
|
||||
render_player(game.player_rota);
|
||||
dupdate();
|
||||
}
|
||||
|
||||
|
|
|
@ -95,10 +95,16 @@ void rect_load(rect_t *r, rectmeta_t const *meta)
|
|||
|
||||
switch(meta->position) {
|
||||
case Position_Left:
|
||||
r->y = DHEIGHT/2 - CORRIDOR_SIZE/2 + r->h/2;
|
||||
if(meta->shape == Shape_Square)
|
||||
r->y = DHEIGHT/2 - 0.22*CORRIDOR_SIZE;
|
||||
else
|
||||
r->y = DHEIGHT/2 - CORRIDOR_SIZE/2 + r->h/2;
|
||||
break;
|
||||
case Position_Right:
|
||||
r->y = DHEIGHT/2 + CORRIDOR_SIZE/2 - r->h/2;
|
||||
if(meta->shape == Shape_Square)
|
||||
r->y = DHEIGHT/2 + 0.22*CORRIDOR_SIZE;
|
||||
else
|
||||
r->y = DHEIGHT/2 + CORRIDOR_SIZE/2 - r->h/2;
|
||||
break;
|
||||
case Position_Middle:
|
||||
r->y = DHEIGHT/2;
|
||||
|
|
|
@ -101,7 +101,7 @@ void drectoid(rect_t const *r, int color)
|
|||
void render_player(float angle)
|
||||
{
|
||||
int x=PLAYER_X, y=DHEIGHT/2;
|
||||
dcircle(x, y, PLAYER_R, C_WHITE, false);
|
||||
dcircle(x, y, PLAYER_R, C_RGB(10, 10, 10), false);
|
||||
|
||||
float x1, y1, x2, y2;
|
||||
player_position(angle, &x1, &y1, &x2, &y2);
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
#include "duet.h"
|
||||
|
||||
extern font_t font_duet;
|
||||
|
||||
/* topti functions exposed to each implementation */
|
||||
int topti_glyph_index(font_t const *f, uint32_t code_point);
|
||||
int topti_offset(font_t const *f, uint glyph);
|
||||
uint32_t topti_utf8_next(uint8_t const **str_pointer);
|
||||
|
||||
/* topti_glyph(): Render a glyph on the VRAM
|
||||
Prints a glyph naively using word accesses, because for most fonts with a
|
||||
small size (including gint's 8x9 font) this will be more efficient than the
|
||||
complex logic for longword accesses.
|
||||
|
||||
This function assumes that at least one of [fg] and [bg] is not transparent.
|
||||
|
||||
@vram Target position on VRAM, adjusted to [top] and [left]
|
||||
@data Glyph data
|
||||
@left Left-position of subglyph
|
||||
@top Top-Position of subglyph
|
||||
@width Subglyph width
|
||||
@height Subglyph height
|
||||
@dataw Glyph width
|
||||
@datah Total font height
|
||||
@fg @bg Foreground and background colors */
|
||||
static void topti_glyph(uint16_t *vram, uint32_t const * data, int left,
|
||||
int top, int width, int height, int dataw, int datah, int fg, int bg)
|
||||
{
|
||||
/* A C version of what is usually done in assembler */
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
int bit_number = (top + y) * dataw + (left + x);
|
||||
int bit = (data[bit_number >> 5] >> (31 - (bit_number & 31))) & 1;
|
||||
|
||||
/* Swithing x -> datah-y-1 and y -> x here gives vertical glyphs */
|
||||
if((bit ? fg : bg) != C_NONE)
|
||||
vram[DWIDTH*x + (datah-y-1)] = bit ? fg : bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void topti_render(int x, int y, char const *str_char, font_t const *f,
|
||||
int fg, int bg, int size)
|
||||
{
|
||||
uint8_t const *str = (void *)str_char;
|
||||
uint8_t const *str0 = str;
|
||||
|
||||
/* Raw glyph data */
|
||||
uint32_t const *data = f->data;
|
||||
|
||||
/* Storage height, top position within glyph */
|
||||
int height = f->data_height, top = 0;
|
||||
|
||||
/* Vertical clipping */
|
||||
if(x > 395 || y > 223 || y + height <= 0) return;
|
||||
if(y + height > 224) height = 224 - y;
|
||||
if(y < 0) top = -y, height += y, y = 0;
|
||||
|
||||
/* Character spacing waiting to be drawn, in pixels */
|
||||
int space = 0;
|
||||
|
||||
/* Read each character from the input string */
|
||||
while(1)
|
||||
{
|
||||
uint32_t code_point = topti_utf8_next(&str);
|
||||
if(!code_point || (size >= 0 && str - str0 > size)) break;
|
||||
|
||||
int glyph = topti_glyph_index(f, code_point);
|
||||
if(glyph < 0) continue;
|
||||
|
||||
int dataw = f->prop ? f->glyph_width[glyph] : f->width;
|
||||
|
||||
/* Draw character spacing if background is opaque */
|
||||
if(space && bg >= 0) drect(x, y, x+height-1, y+space-1, bg);
|
||||
y += space;
|
||||
if(y >= DHEIGHT) break;
|
||||
|
||||
int index = topti_offset(f, glyph);
|
||||
|
||||
/* Compute horizontal intersection between glyph and screen */
|
||||
|
||||
int width = dataw, left = 0;
|
||||
|
||||
if(y + dataw <= 0)
|
||||
{
|
||||
y += dataw;
|
||||
space = f->char_spacing;
|
||||
continue;
|
||||
}
|
||||
if(y < 0) left = -y, width += y;
|
||||
if(y + width > DHEIGHT) width = DHEIGHT - y;
|
||||
|
||||
/* Render glyph */
|
||||
|
||||
topti_glyph(gint_vram + 396 * y + x, data + index, left, top, width,
|
||||
height, dataw, f->data_height, fg, bg);
|
||||
|
||||
/* Switching x -> y here (and some other places, but mostly here) gives
|
||||
vertical text */
|
||||
y += dataw;
|
||||
space = f->char_spacing;
|
||||
}
|
||||
}
|
||||
|
||||
void duet_text_opt(int x, int y, int fg, int bg, int halign, int valign,
|
||||
char const *str, int size)
|
||||
{
|
||||
if(halign != DTEXT_LEFT || valign != DTEXT_TOP)
|
||||
{
|
||||
int w, h;
|
||||
dnsize(str, size, &font_duet, &w, &h);
|
||||
|
||||
if(halign == DTEXT_RIGHT) x -= h - 1;
|
||||
if(halign == DTEXT_CENTER) x -= (h >> 1);
|
||||
if(valign == DTEXT_BOTTOM) y -= w - 1;
|
||||
if(valign == DTEXT_MIDDLE) y -= (w >> 1);
|
||||
}
|
||||
|
||||
topti_render(x, y, str, &font_duet, fg, bg, size);
|
||||
}
|
Loading…
Reference in New Issue