From dcaaec7fb13db3ac40ba1e950c9762b6872bbdbf Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sat, 21 Aug 2021 19:10:29 +0200 Subject: [PATCH] add vertical text with start of duet font --- CMakeLists.txt | 4 +- assets-cg/font.png | Bin 0 -> 925 bytes assets-cg/fxconv-metadata.txt | 12 +++- src/duet.h | 8 +++ src/main.c | 31 +++++---- src/physics.c | 10 ++- src/render.c | 2 +- src/text.c | 120 ++++++++++++++++++++++++++++++++++ 8 files changed, 166 insertions(+), 21 deletions(-) create mode 100644 assets-cg/font.png create mode 100644 src/text.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 207147b..fabbd85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" diff --git a/assets-cg/font.png b/assets-cg/font.png new file mode 100644 index 0000000000000000000000000000000000000000..ba626fbf1ae4b24aaef2bb82d8d865ae4439b64e GIT binary patch literal 925 zcmeAS@N?(olHy`uVBq!ia0vp^8-O^9g9%7Z=kk6Fq&N#aB8wRq^pruEv0|xx8BmbD z#M9T6{V9j2fR$+Hj!=1^kYtH#M2T~LZf;w)9=4b4D}KF zf8IR3X+bpyqr#Ea>VyR?0uJfAH@FTsGXOM%c0WN2X!U~=GKz{7xO zXs9}JOWs7T=g#)pdD@<3En?g$r)}8Qv%5UjP)uX3stMBhz{T_MY?`HOVwr@|wyNCe z+A6hL*OK!%_L#OkU^%dM)7F`nvkX2qZrlBbacXskVu99q>&~doqfyMR7xq~Sehu+> zmD@i*_S~I&{{mkh&bi9nH+w;w%k;vK=bKL1mF!8K0Qpb7 zVBY?p(JQ;lIbLTyQ<{E~Yr5x!=;d>d?&Uk_oOn0$nd-_%=|(9nYa+#KHJADGJY=-K ze$y%a%j)nvX8wos1&Zb=MU?+M`%-0(kkz-TQyyn@ODrl5IQeYW`(px9(;hg@S?|oy z#sdqV4n4-K>z%3Ixs&I=3;LFOLfztv!0k;la=)2uUZrqkPk~MNdh0iNRLaZO@}g6z!PgH@`I8#~q8o5i+?)~~Qvo9S_>;zs#{zXJ1= z?sHln`zvtRF2&DpP3_r|`qF>5KEHgwrl&>iR{lCovyV3FZRT?`w4_!E>`SRvb*X7u zD_q@ZzKa26lnW-lN)Yh}MzAUY5 zzm~0kdaCwMHBpN(U_N5-boFyt=akR{04+&@ZvX%Q literal 0 HcmV?d00001 diff --git a/assets-cg/fxconv-metadata.txt b/assets-cg/fxconv-metadata.txt index d435d5f..712b664 100644 --- a/assets-cg/fxconv-metadata.txt +++ b/assets-cg/fxconv-metadata.txt @@ -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 diff --git a/src/duet.h b/src/duet.h index 8b1681a..37d3db9 100644 --- a/src/duet.h +++ b/src/duet.h @@ -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 //--- diff --git a/src/main.c b/src/main.c index 0bf59da..535a61c 100644 --- a/src/main.c +++ b/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(); } diff --git a/src/physics.c b/src/physics.c index 57be031..02231b0 100644 --- a/src/physics.c +++ b/src/physics.c @@ -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; diff --git a/src/render.c b/src/render.c index 0383527..6bcb606 100644 --- a/src/render.c +++ b/src/render.c @@ -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); diff --git a/src/text.c b/src/text.c new file mode 100644 index 0000000..df5f911 --- /dev/null +++ b/src/text.c @@ -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); +}