funfriend is real! chatters about :)

This commit is contained in:
Jill 2023-05-30 16:47:50 +03:00
parent dfc7ae4b5e
commit 044b405289
Signed by: oat
GPG Key ID: 33489AA58A955108
98 changed files with 952 additions and 3 deletions

103
assets/fonts/SpaceMono.fnt Normal file
View File

@ -0,0 +1,103 @@
info font="Space Mono" size=10 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=2,2,2,2 spacing=2,2
common lineHeight=20 base=15 scaleW=128 scaleH=128 pages=1 packed=0
page id=0 file="SpaceMono.png"
chars count=97
char id=125 x=2 y=2 width=4 height=14 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=0 letter="}"
char id=124 x=8 y=2 width=1 height=14 xoffset=3 yoffset=3 xadvance=8 page=0 chnl=0 letter="|"
char id=93 x=11 y=2 width=3 height=14 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=0 letter="]"
char id=123 x=16 y=2 width=4 height=14 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=0 letter="{"
char id=41 x=22 y=2 width=6 height=14 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0 letter=")"
char id=40 x=30 y=2 width=6 height=14 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=0 letter="("
char id=91 x=38 y=2 width=3 height=14 xoffset=3 yoffset=3 xadvance=8 page=0 chnl=0 letter="["
char id=92 x=43 y=2 width=5 height=12 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=0 letter="\"
char id=81 x=50 y=2 width=7 height=12 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="Q"
char id=47 x=59 y=2 width=5 height=12 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=0 letter="/"
char id=106 x=66 y=2 width=5 height=12 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="j"
char id=55 x=73 y=2 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="7"
char id=53 x=82 y=2 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="5"
char id=56 x=91 y=2 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="8"
char id=51 x=100 y=2 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="3"
char id=54 x=109 y=2 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="6"
char id=113 x=118 y=2 width=7 height=10 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="q"
char id=50 x=2 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="2"
char id=57 x=11 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="9"
char id=98 x=20 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="b"
char id=79 x=29 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="O"
char id=52 x=38 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="4"
char id=72 x=47 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="H"
char id=71 x=56 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="G"
char id=67 x=65 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="C"
char id=37 x=74 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="%"
char id=70 x=83 y=18 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="F"
char id=69 x=91 y=18 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="E"
char id=68 x=99 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="D"
char id=65 x=108 y=18 width=8 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="A"
char id=48 x=118 y=18 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="0"
char id=63 x=2 y=30 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="?"
char id=49 x=11 y=30 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="1"
char id=116 x=20 y=30 width=6 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="t"
char id=66 x=28 y=30 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="B"
char id=38 x=37 y=30 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="&"
char id=33 x=46 y=30 width=2 height=10 xoffset=3 yoffset=5 xadvance=8 page=0 chnl=0 letter="!"
char id=73 x=50 y=30 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="I"
char id=77 x=58 y=30 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="M"
char id=74 x=67 y=30 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="J"
char id=75 x=76 y=30 width=7 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="K"
char id=80 x=85 y=30 width=7 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="P"
char id=121 x=94 y=30 width=6 height=10 xoffset=1 yoffset=8 xadvance=8 page=0 chnl=0 letter="y"
char id=82 x=102 y=30 width=7 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="R"
char id=84 x=111 y=30 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="T"
char id=107 x=2 y=42 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="k"
char id=85 x=10 y=42 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="U"
char id=86 x=19 y=42 width=8 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="V"
char id=83 x=29 y=42 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="S"
char id=100 x=38 y=42 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="d"
char id=108 x=47 y=42 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="l"
char id=105 x=55 y=42 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="i"
char id=104 x=63 y=42 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="h"
char id=89 x=71 y=42 width=8 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="Y"
char id=102 x=81 y=42 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="f"
char id=90 x=89 y=42 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="Z"
char id=78 x=98 y=42 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="N"
char id=76 x=107 y=42 width=6 height=10 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="L"
char id=103 x=115 y=42 width=7 height=10 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="g"
char id=88 x=2 y=54 width=7 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="X"
char id=87 x=11 y=54 width=8 height=10 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="W"
char id=112 x=21 y=54 width=7 height=10 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="p"
char id=36 x=30 y=54 width=6 height=9 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=0 letter="$"
char id=59 x=38 y=54 width=2 height=9 xoffset=3 yoffset=8 xadvance=8 page=0 chnl=0 letter=";"
char id=64 x=42 y=54 width=7 height=8 xoffset=0 yoffset=7 xadvance=8 page=0 chnl=0 letter="@"
char id=109 x=51 y=54 width=7 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="m"
char id=58 x=60 y=54 width=2 height=7 xoffset=3 yoffset=8 xadvance=8 page=0 chnl=0 letter=":"
char id=101 x=64 y=54 width=7 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="e"
char id=42 x=73 y=54 width=7 height=7 xoffset=0 yoffset=7 xadvance=8 page=0 chnl=0 letter="*"
char id=120 x=82 y=54 width=7 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="x"
char id=97 x=91 y=54 width=7 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="a"
char id=99 x=100 y=54 width=7 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="c"
char id=115 x=109 y=54 width=6 height=7 xoffset=1 yoffset=8 xadvance=8 page=0 chnl=0 letter="s"
char id=122 x=117 y=54 width=6 height=7 xoffset=1 yoffset=8 xadvance=8 page=0 chnl=0 letter="z"
char id=111 x=2 y=66 width=7 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="o"
char id=35 x=11 y=66 width=7 height=7 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0 letter="#"
char id=110 x=20 y=66 width=6 height=7 xoffset=1 yoffset=8 xadvance=8 page=0 chnl=0 letter="n"
char id=117 x=28 y=66 width=6 height=7 xoffset=1 yoffset=8 xadvance=8 page=0 chnl=0 letter="u"
char id=114 x=36 y=66 width=7 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="r"
char id=119 x=45 y=66 width=8 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="w"
char id=118 x=55 y=66 width=7 height=7 xoffset=0 yoffset=8 xadvance=8 page=0 chnl=0 letter="v"
char id=60 x=64 y=66 width=6 height=6 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=0 letter="<"
char id=43 x=72 y=66 width=6 height=6 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=0 letter="+"
char id=62 x=80 y=66 width=6 height=6 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=0 letter=">"
char id=44 x=88 y=66 width=2 height=4 xoffset=3 yoffset=13 xadvance=8 page=0 chnl=0 letter=","
char id=39 x=92 y=66 width=2 height=4 xoffset=3 yoffset=5 xadvance=8 page=0 chnl=0 letter="'"
char id=34 x=96 y=66 width=5 height=4 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0 letter="""
char id=94 x=103 y=66 width=7 height=4 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=0 letter="^"
char id=126 x=112 y=66 width=7 height=3 xoffset=0 yoffset=10 xadvance=8 page=0 chnl=0 letter="~"
char id=61 x=2 y=75 width=6 height=3 xoffset=1 yoffset=9 xadvance=8 page=0 chnl=0 letter="="
char id=46 x=10 y=75 width=2 height=2 xoffset=3 yoffset=13 xadvance=8 page=0 chnl=0 letter="."
char id=96 x=14 y=75 width=3 height=2 xoffset=2 yoffset=5 xadvance=8 page=0 chnl=0 letter="`"
char id=95 x=19 y=75 width=7 height=1 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0 letter="_"
char id=45 x=28 y=75 width=4 height=1 xoffset=2 yoffset=11 xadvance=8 page=0 chnl=0 letter="-"
char id=32 x=34 y=75 width=0 height=0 xoffset=0 yoffset=15 xadvance=8 page=0 chnl=0 letter=" "
char id=127 x=36 y=75 width=0 height=0 xoffset=0 yoffset=15 xadvance=7 page=0 chnl=0 letter=""
kernings count=2
kerning first=102 second=105 amount=-8
kerning first=102 second=108 amount=-8

BIN
assets/fonts/SpaceMono.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,2 +1,26 @@
version: 2.0
shards: {}
shards:
crystgl:
git: https://github.com/calebuharrison/crystgl.git
version: 0.1.0+git.commit.c39912c8723873ea50b628940d79ce725b6cd37f
crystglfw:
git: https://github.com/calebuharrison/crystglfw.git
version: 0.2.0+git.commit.53de73c6ec07999ad11c42512929f0eff6f617a9
crystimage:
git: https://github.com/calebuharrison/crystimage.git
version: 0.1.0+git.commit.98f8a7b80100ee4bab3205ceb924128ec41e3148
lib_gl:
git: https://github.com/calebuharrison/libgl.git
version: 0.1.0+git.commit.fed2ca7f6cd15efea94458b234c3ca5b0a8ffc5e
lib_glfw:
git: https://github.com/calebuharrison/libglfw.git
version: 0.1.0+git.commit.4cf7c3774f278d7e044bb30a35b104f5b12fdc53
lib_stb_image:
git: https://github.com/calebuharrison/libstbimage.git
version: 0.1.0+git.commit.32bc23af165dbe004a7468426f09e6701463bc72

View File

@ -4,6 +4,19 @@ version: 0.1.0
authors:
- Jill "oatmealine" Monoids <oatmealine@disroot.org>
dependencies:
crystglfw:
github: calebuharrison/CrystGLFW
branch: master
lib_glfw:
github: calebuharrison/LibGLFW
branch: master
crystgl:
github: calebuharrison/CrystGL
branch: master
crystimage:
github: calebuharrison/CrystImage
targets:
funfriend:
main: src/funfriend.cr

61
src/chatter_context.cr Normal file
View File

@ -0,0 +1,61 @@
class Funfriend::ChatterContext < Funfriend::WindowContext
getter renderer : TextRenderer
getter window_size : NamedTuple(width: Int32, height: Int32)
property timer : Float64
WINDOW_SIZE = {width: 256, height: 32}
DEFAULT_DURATION = 10.0
def initialize(text : String, position : NamedTuple(x: Int32, y: Int32), duration : Float64 = DEFAULT_DURATION)
sheet = FontMan.parse_bm(File.read "assets/fonts/SpaceMono.fnt")
position_data = FontMan.position_text(text, sheet)
@window_size = {
width: position_data[:width],
height: position_data[:height]
}
super(
title: "??__FUNFRIEND__?? > CHATTER",
width: window_size[:width], height: window_size[:height],
transparent: false
)
# just for initialization, let OpenGL know this is the current context
window.make_context_current
@timer = duration
@renderer = TextRenderer.new(text, sheet)
window.position = {
x: position[:x] - window_size[:width]//2,
y: position[:y] - window_size[:height]//2,
}
end
def render(dt : Float64)
# let OpenGL draw to it
window.make_context_current
LibGL.clear_color(0.0, 0.0, 0.0, 1.0)
LibGL.clear(Buffer::Bit::Color)
renderer.render(dt, window_size[:width], window_size[:height])
end
def update(dt : Float64)
@timer = @timer - dt
if @timer <= 0.0
window.should_close
end
render(dt)
window.swap_buffers
end
def clean_up
renderer.clean_up
end
end

103
src/fontman.cr Normal file
View File

@ -0,0 +1,103 @@
module Funfriend::FontMan
extend self
private def quoted_space_split(string : String)
accum = [] of String
chars = [] of Char
quoted = false
string.each_char do |char|
if !quoted && char == ' '
if (chars.count &.!= ' ') > 0
accum << chars.join
end
chars = [] of Char
else
if char == '"'
quoted = !quoted
end
chars << char
end
end
if (chars.count &.!= ' ') > 0
accum << chars.join
end
return accum
end
alias BMCommon = NamedTuple(line_height: Int32, base: Int32, scale_w: Int32, scale_h: Int32)
alias BMChar = NamedTuple(id: Int32, x: Int32, y: Int32, width: Int32, height: Int32, xoffset: Int32, yoffset: Int32, xadvance: Int32, letter: Char)
alias BMKerning = NamedTuple(first: Int32, second: Int32, amount: Int32)
alias BMSheet = NamedTuple(common: BMCommon, chars: Array(BMChar), kernings: Array(BMKerning))
def parse_bm(data : String) : BMSheet
common = nil
chars = [] of BMChar
kernings = [] of BMKerning
data.each_line do |line|
words = line.split
key = words[0]?
args_array = quoted_space_split(words[1..].join(" ")).map &.split('=', 2)
args = Hash.zip(args_array.map &.[0], args_array.map &.[1])
case key
when "common"
common = {
line_height: args["lineHeight"].to_i,
base: args["base"].to_i,
scale_w: args["scaleW"].to_i,
scale_h: args["scaleH"].to_i
}
when "char"
chars << {
id: args["id"].to_i,
x: args["x"].to_i,
y: args["y"].to_i,
width: args["width"].to_i,
height: args["height"].to_i,
xoffset: args["xoffset"].to_i,
yoffset: args["yoffset"].to_i,
xadvance: args["xadvance"].to_i,
letter: args["letter"].lchop('"').rchop('"')[0],
}
when "kerning"
kernings << {
first: args["first"].to_i,
second: args["second"].to_i,
amount: args["amount"].to_i,
}
end
end
return {
common: common.not_nil!,
chars: chars,
kernings: kernings,
}
end
# TODO: kerning
def position_text(text : String, sheet : BMSheet)
positions = [] of NamedTuple(x: Int32, y: Int32, char: BMChar)
x = 0
text.each_char do |char|
bm_char = sheet[:chars].find! { |c| c[:letter] == char }
positions << {
x: x + bm_char[:xoffset],
y: sheet[:common][:base] - bm_char[:height],
char: bm_char,
}
x = x + bm_char[:xadvance]
end
return {
width: x,
height: sheet[:common][:line_height],
positions: positions,
}
end
end

View File

@ -1,6 +1,77 @@
# TODO: Write documentation for `Funfriend`
require "crystglfw"
require "lib_glfw"
require "crystgl"
require "crystimage"
require "./log.cr"
require "./gl.cr"
require "./textureman.cr"
require "./fontman.cr"
require "./window_context.cr"
require "./funfriend_context.cr"
require "./funfriend_renderer.cr"
require "./chatter_context.cr"
require "./text_renderer.cr"
include CrystGLFW
include CrystGL
module Funfriend
VERSION = "0.1.0"
# TODO: Put your code here
LOG = ::Log.for("")
@@contexts = [] of WindowContext
@@main_context : WindowContext?
@@clean_up_queue = [] of WindowContext
def self.should_close?
@@main_context && @@main_context.not_nil!.window.should_close?
end
def self.init_contexts
add_context(FunfriendContext.new)
@@main_context = @@contexts[0]
end
def self.add_context(context : WindowContext)
@@contexts << context
end
def self.contexts
@@contexts
end
def self.run
Logging.init
CrystGLFW.run do
init_contexts
last_t = CrystGLFW.time
until should_close?
dt = CrystGLFW.time - last_t
last_t = CrystGLFW.time
@@contexts = @@contexts.select do |context|
if context.window.should_close?
context.close
@@clean_up_queue << context
false
else
context.update(dt)
true
end
end
CrystGLFW.wait_events(1/120)
end
@@contexts.each &.destroy
@@clean_up_queue.each &.clean_up
end
end
end
Funfriend.run

77
src/funfriend_context.cr Normal file
View File

@ -0,0 +1,77 @@
class Funfriend::FunfriendContext < Funfriend::WindowContext
getter renderer : FunfriendRenderer
property chatter_timer : Float64
WINDOW_SIZE = {width: 82, height: 82}
CHATTER_TIMER = 5.0
def initialize
super(
title: "??_FUNFRIEND_??",
width: WINDOW_SIZE[:width], height: WINDOW_SIZE[:height],
transparent: true
)
@chatter_timer = 1.0
# just for initialization, let OpenGL know this is the current context
window.make_context_current
@renderer = FunfriendRenderer.new
window.on_mouse_button do |event|
if event.action.press? && event.mouse_button.two?
renderer.catmoding = !renderer.catmoding
end
end
window.on_key do |event|
if event.action.press? && event.key.escape?
event.window.should_close
end
end
# pick a random pos
monitor = Monitor.primary
window.position = {
x: monitor.position[:x] + (monitor.video_mode.size[:width] * rand((0.0..1.0))).to_i,
y: monitor.position[:y] + (monitor.video_mode.size[:height] * rand((0.0..1.0))).to_i
}
end
def render(dt : Float64)
# let OpenGL draw to it
window.make_context_current
# draw funfriend
renderer.render(dt, WINDOW_SIZE[:width], WINDOW_SIZE[:height])
end
def update(dt : Float64)
@chatter_timer = @chatter_timer - dt
if @chatter_timer <= 0.0
@chatter_timer = @chatter_timer + CHATTER_TIMER
Funfriend.contexts.each do |context|
if context.is_a?(ChatterContext)
context.window.position = {
x: context.window.position[:x],
y: context.window.position[:y] - 30
}
end
end
Funfriend.add_context(ChatterContext.new(renderer.catmoding ? "HEWWO INTEWLOPEW" : "HELLO INTERLOPER", {
x: window.position[:x] + WINDOW_SIZE[:width] // 2,
y: window.position[:y] + WINDOW_SIZE[:height] // 2 - 70
}))
end
render(dt)
window.swap_buffers
end
def clean_up
renderer.clean_up
end
end

113
src/funfriend_renderer.cr Normal file
View File

@ -0,0 +1,113 @@
class Funfriend::FunfriendRenderer
FUNFRIEND_SIZE = {width: 64, height: 64}
FPS = 10.0
getter shader_program : Program
getter vertex_array : VertexArray
getter vertex_buffer : Buffer
getter textures : Hash(String, TextureMan::TextureBasket)
property catmoding : Bool
def initialize
@catmoding = false
@shader_program = init_shaders
@vertex_array, @vertex_buffer = init_buffers
@textures = init_textures
end
def init_textures
return {
"normal" => TextureMan::TextureBasket.new(
(0..39).map { |i| TextureMan.load_texture("assets/funfriend/funfriend_#{i.to_s.rjust(2, '0')}.png") },
FPS
),
"cat" => TextureMan::TextureBasket.new(
(0..39).map { |i| TextureMan.load_texture("assets/funfriend/catfriend_#{i.to_s.rjust(2, '0')}.png") },
FPS
)
}
end
def init_buffers
vertices = {
# ------ positions ------- texture coordinates
1.0f32, 1.0f32, 0.0f32, 1.0f32, 1.0f32, # top right
1.0f32, -1.0f32, 0.0f32, 1.0f32, 0.0f32, # bottom right
-1.0f32, -1.0f32, 0.0f32, 0.0f32, 0.0f32, # bottom left
-1.0f32, 1.0f32, 0.0f32, 0.0f32, 1.0f32 # top left
}
# Indices used for indexed draw calls. Each is used as an index into the four vertices.
indices = {
0, 1, 3, # first triangle
1, 2, 3 # second triangle
}
vertex_buffer = Buffer.new
element_buffer = Buffer.new
vertex_array = VertexArray.new
vertex_array.bind do
vertex_buffer.bind(Buffer::Target::Array) do |buffer, target|
target.buffer_data(vertices, Buffer::UsageHint::StaticDraw)
vertex_array.define_attributes do |va|
va.attribute(3, DataType::Float, false)
va.attribute(2, DataType::Float, false)
end
end
# bind the element buffer to GL_ELEMENT_ARRAY_BUFFER
element_buffer.bind(Buffer::Target::ElementArray)
# Send index data to the GPU.
Buffer::Target::ElementArray.buffer_data(indices, Buffer::UsageHint::StaticDraw)
end
# unbind the element buffer.
element_buffer.unbind
return {vertex_array, vertex_buffer}
end
def init_shaders
vertex_shader_source = File.read "src/glsl/nop.vert"
fragment_shader_source = File.read "src/glsl/funfriend.frag"
vertex_shader = VertexShader.create(vertex_shader_source)
fragment_shader = FragmentShader.create(fragment_shader_source)
shaders = {vertex_shader, fragment_shader}
return Program.create(shaders)
end
def render(dt : Float64, window_width : Int32, window_height : Int32)
LibGL.clear_color(0.0, 0.0, 0.0, 0.0)
LibGL.clear(Buffer::Bit::Color)
textures.each_value &.update(dt)
texture_basket = textures[catmoding ? "cat" : "normal"]
frame = texture_basket.texture
width, height = frame[:width], frame[:height]
Texture::Unit.activate(0)
frame[:tex].bind(Texture::Target::Texture2D) do
shader_program.use do |shader|
shader.set_uniform("texture1", 0)
shader.set_uniform("funfriendSize", FUNFRIEND_SIZE[:width].to_f32, FUNFRIEND_SIZE[:height].to_f32)
shader.set_uniform("resolution", window_width.to_f32, window_height.to_f32)
shader.set_uniform("time", CrystGLFW.time.to_f32)
vertex_array.bind do
LibGL.draw_elements(LibGL::TRIANGLES, 6, DataType::UnsignedInt, Pointer(Void).new(0))
end
end
end
end
def clean_up
{vertex_array, vertex_buffer}.each { |v| v.delete }
end
end

15
src/gl.cr Normal file
View File

@ -0,0 +1,15 @@
module Funfriend::GL
extend self
def buffer_data_array(target : Enum, data : Array, usage_hint : Buffer::UsageHint)
data_size = data.reduce(0) {|memo, i| memo + sizeof(typeof(i))}
LibGL.buffer_data(target, data_size, Pointer(Void).new(0), usage_hint)
offset = 0
data.each do |byte|
size = sizeof(typeof(byte))
LibGL.buffer_sub_data(target, offset, size, pointerof(byte))
offset += size
end
end
end

11
src/glsl/font.frag Normal file
View File

@ -0,0 +1,11 @@
#version 330 core
in vec2 TexCoord;
uniform sampler2D texture1;
out vec4 FragColor;
void main() {
vec4 tex = texture(texture1, TexCoord);
FragColor = vec4(mix(vec3(0.0, 0.0, 0.0), tex.rgb, tex.a), 1.0);
}

55
src/glsl/funfriend.frag Normal file
View File

@ -0,0 +1,55 @@
#version 330 core
#define PI 3.141592653589793
in vec2 TexCoord;
uniform sampler2D texture1;
uniform vec2 funfriendSize;
uniform vec2 resolution;
uniform float time;
out vec4 FragColor;
vec2 rotate(vec2 uv, float angle) {
// algebra formula for rotation by matrix , https://en.wikipedia.org/wiki/Rotation_matrix
mat2 m = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
// rotation of uv with matrix algebra formula where is set the rotation angle
return m * uv;
}
vec3 rotateY(vec3 uv, float angle) {
mat3 m = mat3(
cos(angle), 0.0, sin(angle),
0.0, 1.0, 0.0,
-sin(angle), 0.0, cos(angle)
);
return m * uv;
}
void main() {
vec2 uv = TexCoord;
// [0.0 - 1.0] -> [-0.5 - 0.5]
uv -= 0.5;
// BUDDYBOUNCE-Y
float y = sin(time * PI);
uv.y += y * 0.05;
uv = rotateY(vec3(uv, 0.0), y * (20. / 360.) * PI*2.).xy;
// BUDDYBOUNCE-X
float x = sin(time * 0.5 * PI);
uv.x += x * 0.05;
uv = rotate(uv, x * (5. / 360.) * PI*2.);
// scale funfriend to fit in the center
vec2 scale = funfriendSize / resolution;
uv /= scale;
// [-0.5 - 0.5] -> [0.0 - 1.0]
uv += 0.5;
// ooo you're a vflipper... you like flipping your y coordinate.... ooooo
uv.y = 1.0 - uv.y;
FragColor = texture(texture1, uv);
}

10
src/glsl/nop.frag Normal file
View File

@ -0,0 +1,10 @@
#version 330 core
in vec2 TexCoord;
uniform sampler2D texture1;
out vec4 FragColor;
void main() {
FragColor = texture(texture1, TexCoord);
}

10
src/glsl/nop.vert Normal file
View File

@ -0,0 +1,10 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
void main() {
gl_Position = vec4(aPos, 1.0);
TexCoord = aTexCoord.xy;
}

45
src/log.cr Normal file
View File

@ -0,0 +1,45 @@
require "log"
require "colorize"
module Funfriend::Logging
extend self
struct FunfriendFormat < Log::StaticFormatter
private def severity_color(severity : Log::Severity) : Colorize::Object
case severity
when .trace?
Colorize.with.dark_gray
when .debug?
Colorize.with.dark_gray
when .info?
Colorize.with.cyan
when .notice?
Colorize.with.cyan
when .warn?
Colorize.with.yellow
when .error?
Colorize.with.red
when .fatal?
Colorize.with.light_red
else
Colorize.with.white
end
end
def run
severity_color(@entry.severity).surround(@io) do
@entry.severity.label.rjust(@io, 6)
end
string " "
Colorize.with.white.surround(@io) do
source
end
string " "
message
end
end
def init
Log.setup(backend: Log::IOBackend.new(formatter: FunfriendFormat, dispatcher: Log::DispatchMode::Sync))
end
end

120
src/text_renderer.cr Normal file
View File

@ -0,0 +1,120 @@
class Funfriend::TextRenderer
getter text : String
getter shader_program : Program
getter vertex_array : VertexArray
getter vertex_buffer : Buffer
getter font_texture : Texture
getter sheet : FontMan::BMSheet
def initialize(text : String, sheet : FontMan::BMSheet)
@text = text
@sheet = sheet
@shader_program = init_shaders
@vertex_array, @vertex_buffer = init_buffers
@font_texture = init_textures
end
def init_textures
TextureMan.load_texture("assets/fonts/SpaceMono.png", {
Texture::ParameterName::TextureWrapS => Texture::ParameterValue::ClampToBorder,
Texture::ParameterName::TextureWrapT => Texture::ParameterValue::ClampToBorder,
Texture::ParameterName::TextureMinFilter => Texture::ParameterValue::Linear,
Texture::ParameterName::TextureMagFilter => Texture::ParameterValue::Linear
})[:tex]
end
def get_letter_crop(char : FontMan::BMChar)
x = (char[:x] / sheet[:common][:scale_w]).to_f32
y = (char[:y] / sheet[:common][:scale_h]).to_f32
w = (char[:width] / sheet[:common][:scale_w]).to_f32
h = (char[:height] / sheet[:common][:scale_h]).to_f32
return {x, y, w, h}
end
def init_buffers
vertices = [] of Float32
indices = [] of Int32
position_data = FontMan.position_text(text, sheet)
i = 0
position_data[:positions].each do |letter|
char = letter[:char]
x, y, w, h = get_letter_crop(char)
pos_x = (letter[:x] / position_data[:width]).to_f32 * 2 - 1
pos_w = (char[:width] / position_data[:width]).to_f32 * 2
pos_y = (letter[:y] / position_data[:height]).to_f32 * 2 - 1
pos_h = (char[:height] / position_data[:height]).to_f32 * 2
vertices += [
# ------ positions ------- texture coordinates
pos_x + pos_w, pos_y + pos_h, 0.0f32, x + w, y, # top right
pos_x + pos_w, pos_y, 0.0f32, x + w, y + h, # bottom right
pos_x, pos_y, 0.0f32, x, y + h, # bottom left
pos_x, pos_y + pos_h, 0.0f32, x, y, # top left
]
# Indices used for indexed draw calls. Each is used as an index into the four vertices.
indices += [
0, 1, 3, # first triangle
1, 2, 3 # second triangle
].map &.+ (i * 4)
i = i + 1
end
vertex_buffer = Buffer.new
element_buffer = Buffer.new
vertex_array = VertexArray.new
vertex_array.bind do
vertex_buffer.bind(Buffer::Target::Array) do |buffer, target|
GL.buffer_data_array(target, vertices, Buffer::UsageHint::StaticDraw)
vertex_array.define_attributes do |va|
va.attribute(3, DataType::Float, false)
va.attribute(2, DataType::Float, false)
end
end
# bind the element buffer to GL_ELEMENT_ARRAY_BUFFER
element_buffer.bind(Buffer::Target::ElementArray)
# Send index data to the GPU.
GL.buffer_data_array(Buffer::Target::ElementArray, indices, Buffer::UsageHint::StaticDraw)
end
# unbind the element buffer.
element_buffer.unbind
return {vertex_array, vertex_buffer}
end
def init_shaders
vertex_shader_source = File.read "src/glsl/nop.vert"
fragment_shader_source = File.read "src/glsl/font.frag"
vertex_shader = VertexShader.create(vertex_shader_source)
fragment_shader = FragmentShader.create(fragment_shader_source)
shaders = {vertex_shader, fragment_shader}
return Program.create(shaders)
end
def render(dt : Float64, window_width : Int32, window_height : Int32)
font_texture.bind(Texture::Target::Texture2D) do
shader_program.use do |p|
p.set_uniform("texture1", 0)
vertex_array.bind do
LibGL.draw_elements(LibGL::TRIANGLES, 6 * text.size, DataType::UnsignedInt, Pointer(Void).new(0))
end
end
end
end
def clean_up
{vertex_array, vertex_buffer}.each { |v| v.delete }
end
end

63
src/textureman.cr Normal file
View File

@ -0,0 +1,63 @@
module Funfriend::TextureMan
extend self
DEFAULT_TEXTURE_PARAMS = {
Texture::ParameterName::TextureWrapS => Texture::ParameterValue::ClampToBorder,
Texture::ParameterName::TextureWrapT => Texture::ParameterValue::ClampToBorder,
Texture::ParameterName::TextureMinFilter => Texture::ParameterValue::Nearest,
Texture::ParameterName::TextureMagFilter => Texture::ParameterValue::Nearest
}
alias SizedTexture = NamedTuple(tex: Texture, width: Int32, height: Int32)
def load_texture(filepath : String, texture_params : Texture::ParameterHash = DEFAULT_TEXTURE_PARAMS) : SizedTexture
# Create a texture
texture = Texture.new
width, height = 0, 0
# Bind the texture the 2D target
texture.bind(Texture::Target::Texture2D) do |tex, target|
# Apply the previously declared configurations.
target.set_parameters(texture_params)
# Open up container image
CrystImage.open(filepath) do |image|
width, height = image.width, image.height
# Send the image data to the GPU
target.image_2d(0, BaseInternalFormat::RGBA, image.width, image.height, Format::RGBA, DataType::UnsignedByte, image.data)
end
end
return {
tex: texture,
width: width,
height: height
}
end
class TextureBasket
property textures : Array(SizedTexture)
property fps : Float64
property t : Float64
def initialize(textures : Array(SizedTexture), fps : Float64)
@textures = textures
@fps = fps
@t = 0
end
def frame : Int32
(t * fps).floor().to_i % textures.size
end
def texture : SizedTexture
textures[frame]
end
def update(delta : Float64)
@t += delta
end
end
end

55
src/window_context.cr Normal file
View File

@ -0,0 +1,55 @@
# GLFW_TRANSPARENT_FRAMEBUFFER, seems to be unsupported by LibGLFW
GLFW_TRANSPARENT_FRAMEBUFFER = 0x0002000A
# GLFW_FOCUS_ON_SHOW, also unsupported by LibGLFW
GLFW_FOCUS_ON_SHOW = 0x0002000C
class Funfriend::WindowContext
getter window : CrystGLFW::Window
DEFAULT_HINTS = {
# supposedly required on MacOS
Window::HintLabel::ContextVersionMajor => 3,
Window::HintLabel::ContextVersionMinor => 3,
Window::HintLabel::OpenGLForwardCompat => true,
Window::HintLabel::OpenGLProfile => OpenGLProfile::Core,
Window::HintLabel::ClientAPI => ClientAPI::OpenGL,
Window::HintLabel::Resizable => false,
Window::HintLabel::Decorated => false,
Window::HintLabel::Floating => true,
Window::HintLabel::Focused => false,
}
def initialize(title : String, width : Int32, height : Int32, transparent : Bool, hints = DEFAULT_HINTS)
if transparent
LibGLFW.window_hint(GLFW_TRANSPARENT_FRAMEBUFFER, 1)
end
LibGLFW.window_hint(GLFW_FOCUS_ON_SHOW, 0)
@window = Window.new title: title, width: width, height: height, hints: hints
LOG.info { "creating context #{self}" }
end
def update(dt : Float64)
# let OpenGL draw to it
window.make_context_current
# nothing to draw; dummy out
# idk why you need to do this but oh well
window.swap_buffers
end
def close
window.destroy
end
def clean_up
end
def destroy
close
clean_up
end
end