(m)sdf text rendering
This commit is contained in:
parent
7fffdd4798
commit
4c2d4be9fb
|
@ -0,0 +1,26 @@
|
|||
#version 150 core
|
||||
|
||||
uniform sampler2D Texture;
|
||||
uniform vec4 Fill;
|
||||
|
||||
in vec2 TextureCoord;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
#define MEDIAN(x, y, z) max(min(x, y), min(max(x, y), z))
|
||||
|
||||
void main() {
|
||||
vec4 msd = texture2D(Texture, TextureCoord);
|
||||
|
||||
// float sd = MEDIAN(msd.r, msd.g, msd.b);
|
||||
// float fw = fwidth(sd);
|
||||
// float alpha = smoothstep(0.5 - fw, 0.5 + fw, sd);
|
||||
|
||||
float sd = msd.a;
|
||||
float fw = 6 / 16.0;
|
||||
float alpha = smoothstep(0.5 - fw, 0.5 + fw, sd);
|
||||
|
||||
// float alpha = msd.a;
|
||||
|
||||
FragColor = Fill * alpha;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#version 150 core
|
||||
|
||||
uniform ivec2 Viewport;
|
||||
uniform mat3 Transform;
|
||||
uniform sampler2D Texture;
|
||||
|
||||
/* TODO: instanced */
|
||||
uniform vec2 Offset;
|
||||
uniform vec4 Atlas;
|
||||
uniform vec4 Plane;
|
||||
|
||||
in vec2 Vert;
|
||||
|
||||
out vec2 TextureCoord;
|
||||
|
||||
void main() {
|
||||
vec2 planeVert = mix(Plane.sq, Plane.pt, Vert);
|
||||
vec3 pos = Transform * vec3(planeVert + Offset, 1.0);
|
||||
|
||||
vec2 atlasVert = mix(Atlas.sq, Atlas.pt, Vert);
|
||||
TextureCoord = atlasVert / textureSize(Texture, 0);
|
||||
|
||||
gl_Position.xy = pos.xy * vec2(2.0, -2.0) / Viewport + vec2(-1.0, 1.0);
|
||||
gl_Position.z = 0.0;
|
||||
gl_Position.w = 1.0;
|
||||
}
|
|
@ -10,14 +10,19 @@ mkdir -p $out_dir
|
|||
function gen() {
|
||||
echo "$1..."
|
||||
|
||||
src=$(fc-list "$2" --format='%{file}\n')
|
||||
|
||||
[[ -f "$src" ]] ||
|
||||
(echo "no font matching $2"; exit 1)
|
||||
|
||||
dst_png=$out_dir/$1.png
|
||||
dst_map=$out_dir/$1.map
|
||||
src=$(find /usr/share/fonts -name $2)
|
||||
emsize=$3
|
||||
|
||||
[[ "$src" -nt "$dst_png" ]] &&
|
||||
($msdf \
|
||||
-font $src \
|
||||
-type mtsdf \
|
||||
-imageout $dst_png \
|
||||
-json >($gen_glyph_map > $dst_map) \
|
||||
-size $emsize \
|
||||
|
@ -25,4 +30,7 @@ function gen() {
|
|||
| exit 1)
|
||||
}
|
||||
|
||||
gen liberation LiberationMono-Regular.ttf 64
|
||||
gen liberation.128 "Liberation Mono:style=regular" 128
|
||||
gen liberation.32 "Liberation Mono:style=regular" 32
|
||||
gen p052-roman.96 "P052:style=roman" 96
|
||||
gen p052-roman.32 "P052:style=roman" 32
|
||||
|
|
24
src/main.ml
24
src/main.ml
|
@ -13,12 +13,22 @@ let main () =
|
|||
let ren = Renderer.make ~wnd in
|
||||
info (fun m -> m "renderer initialized");
|
||||
|
||||
let font1 = Asset.load_font "p052-roman.96" in
|
||||
let text1 = "39" in
|
||||
let text1_fg = rgb24 0xffffff in
|
||||
let text1_tf = mat2a 50.0 100.0 48.0 48.0 in
|
||||
|
||||
let font2 = Asset.load_font "liberation.32" in
|
||||
let text2 = "Hello, world" in
|
||||
let text2_fg = rgb24 0xffffff in
|
||||
let text2_tf = mat2a 50.0 200.0 24.0 24.0 in
|
||||
|
||||
let sg = SG.make () in
|
||||
SG.register_sprite_map sg "blocks"
|
||||
(Asset.load_sprite_map "blocks" ~dpi:192);
|
||||
SG.register_sprite_map sg "hud"
|
||||
(Asset.load_sprite_map "hud");
|
||||
debug (fun m -> m "loaded assets");
|
||||
debug (fun m -> m "loaded sprites");
|
||||
|
||||
let tg = TG.make () in
|
||||
let scene = Scene.load "main" ~tg ~sg in
|
||||
|
@ -27,20 +37,20 @@ let main () =
|
|||
|
||||
let render time =
|
||||
begin
|
||||
|
||||
let tf = TG.model (Scene.transform root) in
|
||||
begin
|
||||
let tx = 512.0 +. Float.sin (time *. 3.0) *. 20.0 in
|
||||
let ty = 400.0 in
|
||||
let _ = time in
|
||||
Mat2A.set tf ~tx ~ty ~sx:1.0 ~sy:1.0
|
||||
Mat2A.set tf ~tx:512.0 ~ty:400.0 ~sx:1.0 ~sy:1.0
|
||||
end;
|
||||
|
||||
Renderer.pre_draw ren;
|
||||
Renderer.clear ren (rgb24 0x131321);
|
||||
|
||||
TG.update tg;
|
||||
SG.render sg ~ren;
|
||||
if false then SG.render sg ~ren;
|
||||
|
||||
Renderer.draw_text ren font1 text1 ~tf:text1_tf ~fg:text1_fg;
|
||||
Renderer.draw_text ren font2 text2 ~tf:text2_tf ~fg:text2_fg;
|
||||
|
||||
Renderer.post_draw ren;
|
||||
|
||||
|
@ -56,7 +66,7 @@ let main () =
|
|||
end
|
||||
|
||||
let () =
|
||||
Ohlog.init () ~min_level:TRACE;
|
||||
Ohlog.init () ~min_level:DEBUG;
|
||||
|
||||
try main () with
|
||||
| Sdl.Error msg -> error (fun m -> m "SDL error: %s" msg)
|
||||
|
|
|
@ -5,21 +5,30 @@ include (val Ohlog.sublogs logger "Font")
|
|||
|
||||
type glyph = {
|
||||
advance : float;
|
||||
clip : aabb;
|
||||
rect : aabb;
|
||||
atlas : aabb;
|
||||
plane : aabb;
|
||||
}
|
||||
|
||||
type t = {
|
||||
texture : Texture.t;
|
||||
msdf : Texture.t;
|
||||
glyphs : (char, glyph) Hashtbl.t;
|
||||
}
|
||||
|
||||
let empty = aabb 1.0 1.0 0.0 0.0
|
||||
|
||||
module Renderer = struct
|
||||
let draw_text ren ~tf ~fg fnt str =
|
||||
ignore (ren, tf, fg, fnt, str);
|
||||
failwith "TODO: Renderer.draw_text"
|
||||
let offset = vec2 0.0 0.0
|
||||
|
||||
let draw_text ren ~tf ~fg { msdf; glyphs } str =
|
||||
offset.x <- 0.0;
|
||||
offset.y <- 0.0;
|
||||
for i = 0 to String.length str - 1 do
|
||||
(* TODO: line breaks *)
|
||||
let { advance; atlas; plane } = Hashtbl.find glyphs str.[i] in
|
||||
if not (AABB.is_empty atlas) then
|
||||
Renderer.draw_glyph ren msdf ~tf ~fg ~offset ~atlas ~plane;
|
||||
offset.x <- offset.x +. advance;
|
||||
done
|
||||
end
|
||||
|
||||
|
||||
|
@ -40,32 +49,33 @@ let glyph_of_sexp = function
|
|||
| sexp ->
|
||||
Sexp_conv.of_sexp_error "expected glyph advance" (List sexp)
|
||||
in
|
||||
let clip, rect = match args with
|
||||
| clip :: rect :: _ ->
|
||||
bounds_of_sexp clip, bounds_of_sexp rect
|
||||
| clip :: _ ->
|
||||
bounds_of_sexp clip, empty
|
||||
let atlas, plane = match args with
|
||||
| pl :: at :: _ ->
|
||||
bounds_of_sexp at, bounds_of_sexp pl
|
||||
| pl :: _ ->
|
||||
bounds_of_sexp pl, empty
|
||||
| [] -> empty, empty
|
||||
in
|
||||
chr.[0], { advance; clip; rect }
|
||||
chr.[0], { advance; atlas; plane }
|
||||
| sexp -> Sexp_conv.of_sexp_error "bad msdf glyph" sexp
|
||||
|
||||
let of_sexp ~texture = function
|
||||
let of_sexp ~msdf = function
|
||||
| Sexp.List (Atom "msdf" :: glyphs) ->
|
||||
let glyphs =
|
||||
List.to_seq glyphs |>
|
||||
Seq.map glyph_of_sexp |>
|
||||
Hashtbl.of_seq
|
||||
in
|
||||
{ texture; glyphs }
|
||||
trace (fun m -> m "%S" (String.of_seq (Hashtbl.to_seq_keys glyphs)));
|
||||
{ msdf; glyphs }
|
||||
| sexp -> Sexp_conv.of_sexp_error "invalid msdf" sexp
|
||||
|
||||
module Asset = struct
|
||||
let load_font name =
|
||||
let tex_path = Format.sprintf "fonts/%s.png" name in
|
||||
let map_path = Format.sprintf "fonts/%s.map" name in
|
||||
let texture = Texture.load_texture tex_path in
|
||||
let font = Asset.load_sexp_conv map_path (of_sexp ~texture) in
|
||||
let msdf = Texture.Asset.load_texture tex_path ~premultiply_alpha:false in
|
||||
let font = Asset.load_sexp_conv map_path (of_sexp ~msdf) in
|
||||
debug (fun m -> m "loaded font %S" name);
|
||||
font
|
||||
end
|
||||
|
|
|
@ -131,6 +131,7 @@ let uniform {spo} name =
|
|||
type 'a set_fn = 'a uniform -> 'a -> unit
|
||||
|
||||
let set_int : int set_fn = fun (U l) x -> Gl.uniform1i l x
|
||||
let set_vec2 : vec2 set_fn = fun (U l) {x; y} -> Gl.uniform2f l x y
|
||||
let set_ivec2 : ivec2 set_fn = fun (U l) (x, y) -> Gl.uniform2i l x y
|
||||
let set_color : color set_fn = fun (U l) c -> Gl.uniform4f l c.r c.g c.b c.a
|
||||
let set_aabb : aabb set_fn = fun (U l) b -> Gl.uniform4f l b.x0 b.y0 b.x1 b.y1
|
||||
|
@ -223,11 +224,15 @@ type t = {
|
|||
gl_ctx : Sdl.gl_context;
|
||||
|
||||
polygon : shader;
|
||||
polygon_rect : geometry;
|
||||
rect_g : geometry;
|
||||
|
||||
sprite : shader;
|
||||
sprite_rect : geometry;
|
||||
sprite_g : geometry;
|
||||
(* sprite_instances : vertex_buffer; *)
|
||||
|
||||
msdf : shader;
|
||||
text_g : geometry;
|
||||
(* msdf_instances : vertex_buffer; *)
|
||||
}
|
||||
|
||||
let unit_square =
|
||||
|
@ -273,7 +278,7 @@ let make ~(wnd : Sdl.window) : t =
|
|||
Gl.check_error "setup";
|
||||
|
||||
let polygon = load_shader ~name:"polygon" in
|
||||
let polygon_rect =
|
||||
let rect_g =
|
||||
make_geometry [
|
||||
make_static_vertex_buffer unit_square_with_norm [
|
||||
attr polygon "Vert" `float 2;
|
||||
|
@ -285,10 +290,10 @@ let make ~(wnd : Sdl.window) : t =
|
|||
in
|
||||
|
||||
let sprite = load_shader ~name:"sprite" in
|
||||
let sprite_rect =
|
||||
let sprite_g =
|
||||
make_geometry [
|
||||
make_static_vertex_buffer unit_square [
|
||||
attr polygon "Vert" `float 2;
|
||||
attr sprite "Vert" `float 2;
|
||||
]
|
||||
(* sprite_instances *)
|
||||
]
|
||||
|
@ -296,13 +301,27 @@ let make ~(wnd : Sdl.window) : t =
|
|||
~count:4
|
||||
in
|
||||
|
||||
let msdf = load_shader ~name:"msdf" in
|
||||
let text_g =
|
||||
make_geometry [
|
||||
make_static_vertex_buffer unit_square [
|
||||
attr msdf "Vert" `float 2;
|
||||
]
|
||||
(* msdf_instances *)
|
||||
]
|
||||
~draw_mode:Gl.triangle_strip
|
||||
~count:4
|
||||
in
|
||||
|
||||
{
|
||||
window = wnd;
|
||||
gl_ctx;
|
||||
polygon;
|
||||
polygon_rect;
|
||||
rect_g;
|
||||
sprite;
|
||||
sprite_rect;
|
||||
sprite_g;
|
||||
msdf;
|
||||
text_g;
|
||||
}
|
||||
|
||||
let destroy t =
|
||||
|
@ -314,6 +333,7 @@ let pre_draw t =
|
|||
Sdl.gl_make_current_exn t.window t.gl_ctx;
|
||||
use t.polygon; set_ivec2 (uniform t.polygon "Viewport") viewport;
|
||||
use t.sprite; set_ivec2 (uniform t.sprite "Viewport") viewport;
|
||||
use t.msdf; set_ivec2 (uniform t.msdf "Viewport") viewport;
|
||||
end
|
||||
|
||||
let post_draw t =
|
||||
|
@ -327,27 +347,41 @@ let clear _t (bg : color) =
|
|||
Gl.clear Gl.color_buffer_bit;
|
||||
end
|
||||
|
||||
(* TODO: store uniforms *)
|
||||
(* TODO: instanced rendering *)
|
||||
|
||||
let draw_rect t ~tf ~fill rect =
|
||||
let sh = t.polygon in
|
||||
let sh = t.polygon and ge = t.rect_g in
|
||||
begin
|
||||
(* TODO: cache/store uniform locations in some way *)
|
||||
use sh;
|
||||
set_mat2a (uniform sh "Transform") tf;
|
||||
set_aabb (uniform sh "BoundingBox") rect;
|
||||
set_int (uniform sh "Border") 0;
|
||||
set_color (uniform sh "Fill") fill;
|
||||
draw_geometry t.polygon_rect;
|
||||
draw_geometry ge;
|
||||
end
|
||||
|
||||
let draw_texture t ~tf ~rect ~clip ~tint tex =
|
||||
let sh = t.sprite in
|
||||
let sh = t.sprite and ge = t.sprite_g in
|
||||
begin
|
||||
(* TODO: cache/store uniform locations in some way *)
|
||||
use sh;
|
||||
set_mat2a (uniform sh "Transform") tf;
|
||||
set_tex (uniform sh "Texture") tex;
|
||||
set_aabb (uniform sh "Rect") rect;
|
||||
set_aabb (uniform sh "Clip") clip;
|
||||
set_color (uniform sh "Tint") tint;
|
||||
draw_geometry t.sprite_rect;
|
||||
draw_geometry ge;
|
||||
end
|
||||
|
||||
let draw_glyph t ~tf ~offset ~atlas ~plane ~fg msdf =
|
||||
let sh = t.msdf and ge = t.text_g in
|
||||
begin
|
||||
use sh;
|
||||
set_mat2a (uniform sh "Transform") tf;
|
||||
set_tex (uniform sh "Texture") msdf;
|
||||
set_vec2 (uniform sh "Offset") offset;
|
||||
set_aabb (uniform sh "Atlas") atlas;
|
||||
set_aabb (uniform sh "Plane") plane;
|
||||
set_color (uniform sh "Fill") fg;
|
||||
draw_geometry ge;
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ module Font = Font
|
|||
module Renderer = struct
|
||||
include Renderer
|
||||
include Sprite.Renderer
|
||||
include Font.Renderer
|
||||
end
|
||||
module Asset = struct
|
||||
include Asset
|
||||
|
|
|
@ -36,6 +36,7 @@ module Renderer : sig
|
|||
val clear : t -> color -> unit
|
||||
val draw_rect : t -> tf:mat2a -> fill:color -> aabb -> unit
|
||||
val draw_sprite : t -> tf:mat2a -> ?tint:color -> ?pos:vec2 -> Sprite.t -> unit
|
||||
val draw_text : t -> tf:mat2a -> fg:color -> Font.t -> string -> unit
|
||||
end
|
||||
|
||||
module Asset : sig
|
||||
|
|
Loading…
Reference in New Issue