(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() {
|
function gen() {
|
||||||
echo "$1..."
|
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_png=$out_dir/$1.png
|
||||||
dst_map=$out_dir/$1.map
|
dst_map=$out_dir/$1.map
|
||||||
src=$(find /usr/share/fonts -name $2)
|
|
||||||
emsize=$3
|
emsize=$3
|
||||||
|
|
||||||
[[ "$src" -nt "$dst_png" ]] &&
|
[[ "$src" -nt "$dst_png" ]] &&
|
||||||
($msdf \
|
($msdf \
|
||||||
-font $src \
|
-font $src \
|
||||||
|
-type mtsdf \
|
||||||
-imageout $dst_png \
|
-imageout $dst_png \
|
||||||
-json >($gen_glyph_map > $dst_map) \
|
-json >($gen_glyph_map > $dst_map) \
|
||||||
-size $emsize \
|
-size $emsize \
|
||||||
|
@ -25,4 +30,7 @@ function gen() {
|
||||||
| exit 1)
|
| 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
|
let ren = Renderer.make ~wnd in
|
||||||
info (fun m -> m "renderer initialized");
|
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
|
let sg = SG.make () in
|
||||||
SG.register_sprite_map sg "blocks"
|
SG.register_sprite_map sg "blocks"
|
||||||
(Asset.load_sprite_map "blocks" ~dpi:192);
|
(Asset.load_sprite_map "blocks" ~dpi:192);
|
||||||
SG.register_sprite_map sg "hud"
|
SG.register_sprite_map sg "hud"
|
||||||
(Asset.load_sprite_map "hud");
|
(Asset.load_sprite_map "hud");
|
||||||
debug (fun m -> m "loaded assets");
|
debug (fun m -> m "loaded sprites");
|
||||||
|
|
||||||
let tg = TG.make () in
|
let tg = TG.make () in
|
||||||
let scene = Scene.load "main" ~tg ~sg in
|
let scene = Scene.load "main" ~tg ~sg in
|
||||||
|
@ -27,20 +37,20 @@ let main () =
|
||||||
|
|
||||||
let render time =
|
let render time =
|
||||||
begin
|
begin
|
||||||
|
|
||||||
let tf = TG.model (Scene.transform root) in
|
let tf = TG.model (Scene.transform root) in
|
||||||
begin
|
begin
|
||||||
let tx = 512.0 +. Float.sin (time *. 3.0) *. 20.0 in
|
|
||||||
let ty = 400.0 in
|
|
||||||
let _ = time 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;
|
end;
|
||||||
|
|
||||||
Renderer.pre_draw ren;
|
Renderer.pre_draw ren;
|
||||||
Renderer.clear ren (rgb24 0x131321);
|
Renderer.clear ren (rgb24 0x131321);
|
||||||
|
|
||||||
TG.update tg;
|
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;
|
Renderer.post_draw ren;
|
||||||
|
|
||||||
|
@ -56,7 +66,7 @@ let main () =
|
||||||
end
|
end
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
Ohlog.init () ~min_level:TRACE;
|
Ohlog.init () ~min_level:DEBUG;
|
||||||
|
|
||||||
try main () with
|
try main () with
|
||||||
| Sdl.Error msg -> error (fun m -> m "SDL error: %s" msg)
|
| Sdl.Error msg -> error (fun m -> m "SDL error: %s" msg)
|
||||||
|
|
|
@ -5,21 +5,30 @@ include (val Ohlog.sublogs logger "Font")
|
||||||
|
|
||||||
type glyph = {
|
type glyph = {
|
||||||
advance : float;
|
advance : float;
|
||||||
clip : aabb;
|
atlas : aabb;
|
||||||
rect : aabb;
|
plane : aabb;
|
||||||
}
|
}
|
||||||
|
|
||||||
type t = {
|
type t = {
|
||||||
texture : Texture.t;
|
msdf : Texture.t;
|
||||||
glyphs : (char, glyph) Hashtbl.t;
|
glyphs : (char, glyph) Hashtbl.t;
|
||||||
}
|
}
|
||||||
|
|
||||||
let empty = aabb 1.0 1.0 0.0 0.0
|
let empty = aabb 1.0 1.0 0.0 0.0
|
||||||
|
|
||||||
module Renderer = struct
|
module Renderer = struct
|
||||||
let draw_text ren ~tf ~fg fnt str =
|
let offset = vec2 0.0 0.0
|
||||||
ignore (ren, tf, fg, fnt, str);
|
|
||||||
failwith "TODO: Renderer.draw_text"
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,32 +49,33 @@ let glyph_of_sexp = function
|
||||||
| sexp ->
|
| sexp ->
|
||||||
Sexp_conv.of_sexp_error "expected glyph advance" (List sexp)
|
Sexp_conv.of_sexp_error "expected glyph advance" (List sexp)
|
||||||
in
|
in
|
||||||
let clip, rect = match args with
|
let atlas, plane = match args with
|
||||||
| clip :: rect :: _ ->
|
| pl :: at :: _ ->
|
||||||
bounds_of_sexp clip, bounds_of_sexp rect
|
bounds_of_sexp at, bounds_of_sexp pl
|
||||||
| clip :: _ ->
|
| pl :: _ ->
|
||||||
bounds_of_sexp clip, empty
|
bounds_of_sexp pl, empty
|
||||||
| [] -> empty, empty
|
| [] -> empty, empty
|
||||||
in
|
in
|
||||||
chr.[0], { advance; clip; rect }
|
chr.[0], { advance; atlas; plane }
|
||||||
| sexp -> Sexp_conv.of_sexp_error "bad msdf glyph" sexp
|
| 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) ->
|
| Sexp.List (Atom "msdf" :: glyphs) ->
|
||||||
let glyphs =
|
let glyphs =
|
||||||
List.to_seq glyphs |>
|
List.to_seq glyphs |>
|
||||||
Seq.map glyph_of_sexp |>
|
Seq.map glyph_of_sexp |>
|
||||||
Hashtbl.of_seq
|
Hashtbl.of_seq
|
||||||
in
|
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
|
| sexp -> Sexp_conv.of_sexp_error "invalid msdf" sexp
|
||||||
|
|
||||||
module Asset = struct
|
module Asset = struct
|
||||||
let load_font name =
|
let load_font name =
|
||||||
let tex_path = Format.sprintf "fonts/%s.png" name in
|
let tex_path = Format.sprintf "fonts/%s.png" name in
|
||||||
let map_path = Format.sprintf "fonts/%s.map" name in
|
let map_path = Format.sprintf "fonts/%s.map" name in
|
||||||
let texture = Texture.load_texture tex_path in
|
let msdf = Texture.Asset.load_texture tex_path ~premultiply_alpha:false in
|
||||||
let font = Asset.load_sexp_conv map_path (of_sexp ~texture) in
|
let font = Asset.load_sexp_conv map_path (of_sexp ~msdf) in
|
||||||
debug (fun m -> m "loaded font %S" name);
|
debug (fun m -> m "loaded font %S" name);
|
||||||
font
|
font
|
||||||
end
|
end
|
||||||
|
|
|
@ -131,6 +131,7 @@ let uniform {spo} name =
|
||||||
type 'a set_fn = 'a uniform -> 'a -> unit
|
type 'a set_fn = 'a uniform -> 'a -> unit
|
||||||
|
|
||||||
let set_int : int set_fn = fun (U l) x -> Gl.uniform1i l x
|
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_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_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
|
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;
|
gl_ctx : Sdl.gl_context;
|
||||||
|
|
||||||
polygon : shader;
|
polygon : shader;
|
||||||
polygon_rect : geometry;
|
rect_g : geometry;
|
||||||
|
|
||||||
sprite : shader;
|
sprite : shader;
|
||||||
sprite_rect : geometry;
|
sprite_g : geometry;
|
||||||
(* sprite_instances : vertex_buffer; *)
|
(* sprite_instances : vertex_buffer; *)
|
||||||
|
|
||||||
|
msdf : shader;
|
||||||
|
text_g : geometry;
|
||||||
|
(* msdf_instances : vertex_buffer; *)
|
||||||
}
|
}
|
||||||
|
|
||||||
let unit_square =
|
let unit_square =
|
||||||
|
@ -273,7 +278,7 @@ let make ~(wnd : Sdl.window) : t =
|
||||||
Gl.check_error "setup";
|
Gl.check_error "setup";
|
||||||
|
|
||||||
let polygon = load_shader ~name:"polygon" in
|
let polygon = load_shader ~name:"polygon" in
|
||||||
let polygon_rect =
|
let rect_g =
|
||||||
make_geometry [
|
make_geometry [
|
||||||
make_static_vertex_buffer unit_square_with_norm [
|
make_static_vertex_buffer unit_square_with_norm [
|
||||||
attr polygon "Vert" `float 2;
|
attr polygon "Vert" `float 2;
|
||||||
|
@ -285,10 +290,10 @@ let make ~(wnd : Sdl.window) : t =
|
||||||
in
|
in
|
||||||
|
|
||||||
let sprite = load_shader ~name:"sprite" in
|
let sprite = load_shader ~name:"sprite" in
|
||||||
let sprite_rect =
|
let sprite_g =
|
||||||
make_geometry [
|
make_geometry [
|
||||||
make_static_vertex_buffer unit_square [
|
make_static_vertex_buffer unit_square [
|
||||||
attr polygon "Vert" `float 2;
|
attr sprite "Vert" `float 2;
|
||||||
]
|
]
|
||||||
(* sprite_instances *)
|
(* sprite_instances *)
|
||||||
]
|
]
|
||||||
|
@ -296,13 +301,27 @@ let make ~(wnd : Sdl.window) : t =
|
||||||
~count:4
|
~count:4
|
||||||
in
|
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;
|
window = wnd;
|
||||||
gl_ctx;
|
gl_ctx;
|
||||||
polygon;
|
polygon;
|
||||||
polygon_rect;
|
rect_g;
|
||||||
sprite;
|
sprite;
|
||||||
sprite_rect;
|
sprite_g;
|
||||||
|
msdf;
|
||||||
|
text_g;
|
||||||
}
|
}
|
||||||
|
|
||||||
let destroy t =
|
let destroy t =
|
||||||
|
@ -314,6 +333,7 @@ let pre_draw t =
|
||||||
Sdl.gl_make_current_exn t.window t.gl_ctx;
|
Sdl.gl_make_current_exn t.window t.gl_ctx;
|
||||||
use t.polygon; set_ivec2 (uniform t.polygon "Viewport") viewport;
|
use t.polygon; set_ivec2 (uniform t.polygon "Viewport") viewport;
|
||||||
use t.sprite; set_ivec2 (uniform t.sprite "Viewport") viewport;
|
use t.sprite; set_ivec2 (uniform t.sprite "Viewport") viewport;
|
||||||
|
use t.msdf; set_ivec2 (uniform t.msdf "Viewport") viewport;
|
||||||
end
|
end
|
||||||
|
|
||||||
let post_draw t =
|
let post_draw t =
|
||||||
|
@ -327,27 +347,41 @@ let clear _t (bg : color) =
|
||||||
Gl.clear Gl.color_buffer_bit;
|
Gl.clear Gl.color_buffer_bit;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
(* TODO: store uniforms *)
|
||||||
|
(* TODO: instanced rendering *)
|
||||||
|
|
||||||
let draw_rect t ~tf ~fill rect =
|
let draw_rect t ~tf ~fill rect =
|
||||||
let sh = t.polygon in
|
let sh = t.polygon and ge = t.rect_g in
|
||||||
begin
|
begin
|
||||||
(* TODO: cache/store uniform locations in some way *)
|
|
||||||
use sh;
|
use sh;
|
||||||
set_mat2a (uniform sh "Transform") tf;
|
set_mat2a (uniform sh "Transform") tf;
|
||||||
set_aabb (uniform sh "BoundingBox") rect;
|
set_aabb (uniform sh "BoundingBox") rect;
|
||||||
set_int (uniform sh "Border") 0;
|
set_int (uniform sh "Border") 0;
|
||||||
set_color (uniform sh "Fill") fill;
|
set_color (uniform sh "Fill") fill;
|
||||||
draw_geometry t.polygon_rect;
|
draw_geometry ge;
|
||||||
end
|
end
|
||||||
|
|
||||||
let draw_texture t ~tf ~rect ~clip ~tint tex =
|
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
|
begin
|
||||||
(* TODO: cache/store uniform locations in some way *)
|
|
||||||
use sh;
|
use sh;
|
||||||
set_mat2a (uniform sh "Transform") tf;
|
set_mat2a (uniform sh "Transform") tf;
|
||||||
set_tex (uniform sh "Texture") tex;
|
set_tex (uniform sh "Texture") tex;
|
||||||
set_aabb (uniform sh "Rect") rect;
|
set_aabb (uniform sh "Rect") rect;
|
||||||
set_aabb (uniform sh "Clip") clip;
|
set_aabb (uniform sh "Clip") clip;
|
||||||
set_color (uniform sh "Tint") tint;
|
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
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@ module Font = Font
|
||||||
module Renderer = struct
|
module Renderer = struct
|
||||||
include Renderer
|
include Renderer
|
||||||
include Sprite.Renderer
|
include Sprite.Renderer
|
||||||
|
include Font.Renderer
|
||||||
end
|
end
|
||||||
module Asset = struct
|
module Asset = struct
|
||||||
include Asset
|
include Asset
|
||||||
|
|
|
@ -36,6 +36,7 @@ module Renderer : sig
|
||||||
val clear : t -> color -> unit
|
val clear : t -> color -> unit
|
||||||
val draw_rect : t -> tf:mat2a -> fill:color -> aabb -> 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_sprite : t -> tf:mat2a -> ?tint:color -> ?pos:vec2 -> Sprite.t -> unit
|
||||||
|
val draw_text : t -> tf:mat2a -> fg:color -> Font.t -> string -> unit
|
||||||
end
|
end
|
||||||
|
|
||||||
module Asset : sig
|
module Asset : sig
|
||||||
|
|
Loading…
Reference in New Issue