summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock9
-rw-r--r--crates/teleia/Cargo.toml1
-rw-r--r--crates/teleia/src/assets/shaders/slug/frag.glsl72
-rw-r--r--crates/teleia/src/assets/shaders/slug/vert.glsl4
-rw-r--r--crates/teleia/src/font.rs112
5 files changed, 196 insertions, 2 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0ec9cb2..0af34c1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1624,7 +1624,7 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7"
dependencies = [
- "ttf-parser",
+ "ttf-parser 0.20.0",
]
[[package]]
@@ -2402,6 +2402,7 @@ dependencies = [
"strum",
"tobj",
"tracing-wasm",
+ "ttf-parser 0.25.1",
"tungstenite",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -2602,6 +2603,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+
+[[package]]
name = "tungstenite"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/crates/teleia/Cargo.toml b/crates/teleia/Cargo.toml
index d5777a3..26c08a9 100644
--- a/crates/teleia/Cargo.toml
+++ b/crates/teleia/Cargo.toml
@@ -32,6 +32,7 @@ parry3d = "0.18.0" # collision detection
nalgebra = {version = "0.33.2", features = ["convert-glam029"]} # linear algebra library for rapier3d
byteorder = "1.5.0" # read numbers in particular endianness
base64 = "0.22.1" # encode/decode base64
+ttf-parser = "0.25.1" # ttf fonts
[target.'cfg(target_arch = "wasm32")'.dependencies]
winit = {version = "=0.29.15", features = ["serde"]} # windowing and events
diff --git a/crates/teleia/src/assets/shaders/slug/frag.glsl b/crates/teleia/src/assets/shaders/slug/frag.glsl
new file mode 100644
index 0000000..206cb63
--- /dev/null
+++ b/crates/teleia/src/assets/shaders/slug/frag.glsl
@@ -0,0 +1,72 @@
+uniform sampler2D texture_data;
+uniform int curves_len;
+
+struct curve { vec2 p1, p2, p3; };
+curve get_curve(int i) {
+ float tx = float(i * 2) / 4096.0;
+ float txn = float(i * 2 + 1) / 4096.0;
+ vec4 t1 = texture(texture_data, vec2(tx, 0.0));
+ vec4 t2 = texture(texture_data, vec2(txn, 0.0));
+ curve ret;
+ ret.p1 = t1.xy;
+ ret.p2 = t1.zw;
+ ret.p3 = t2.xy;
+ return ret;
+}
+vec2 compute_curve(curve c, float t) {
+ float invt = 1.0 - t;
+ return invt * invt * c.p1 + 2.0 * t * invt * c.p2 + t * t * c.p3;
+}
+ivec2 lookup_table_curve(curve c) {
+ bool yp1 = c.p1.y > 0.0;
+ bool yp2 = c.p2.y > 0.0;
+ bool yp3 = c.p3.y > 0.0;
+ int shift = (yp1 ? 2 : 0) + (yp2 ? 4 : 0) + (yp3 ? 8 : 0);
+ int res = 0x2e74 >> shift;
+ return ivec2(res & 1, (res >> 1) & 1);
+}
+vec2 roots_curve(curve c) {
+ vec2 a = c.p1 - c.p2 * 2.0 + c.p3;
+ vec2 b = c.p1 - c.p2;
+ float ra = 1.0 / a.y;
+ float rb = 0.5 / b.y;
+ float d = sqrt(max(b.y * b.y - a.y * c.p1.y, 0.0));
+ float t1 = (b.y - d) * ra;
+ float t2 = (b.y + d) * ra;
+ if (abs(a.y) < 1.0 / 65536.0) t1 = t2 = c.p1.y * rb;
+ return vec2(t1, t2);
+}
+
+void main() {
+ vec2 c = vertex_texcoord - vec2(0.5, 0.5);
+ int winding = 0;
+ for (int i = 0; i < curves_len; ++i) {
+ curve b = get_curve(i);
+ if (distance(c, b.p1) < 0.01 || distance(c, b.p2) < 0.01 || distance(c, b.p3) < 0.01) {
+ frag_color = vec4(0.0, 1.0, 0.0, 1.0);
+ return;
+ }
+ b.p1 -= c;
+ b.p2 -= c;
+ b.p3 -= c;
+ ivec2 code = lookup_table_curve(b);
+ vec2 roots = roots_curve(b);
+ if (code.x > 0) {
+ vec2 horiz = compute_curve(b, roots.x);
+ if (horiz.x >= 0.0) {
+ winding += 1;
+ }
+ }
+ if (code.y > 0) {
+ vec2 horiz = compute_curve(b, roots.y);
+ if (horiz.x >= 0.0) {
+ winding -= 1;
+ }
+ }
+ }
+ if (winding != 0) {
+ frag_color = vec4(1.0, 1.0, 1.0, 1.0);
+ } else {
+ discard;
+ }
+}
diff --git a/crates/teleia/src/assets/shaders/slug/vert.glsl b/crates/teleia/src/assets/shaders/slug/vert.glsl
new file mode 100644
index 0000000..e324f7e
--- /dev/null
+++ b/crates/teleia/src/assets/shaders/slug/vert.glsl
@@ -0,0 +1,4 @@
+void main()
+{
+ default_main();
+} \ No newline at end of file
diff --git a/crates/teleia/src/font.rs b/crates/teleia/src/font.rs
index d97e96c..0b1dce7 100644
--- a/crates/teleia/src/font.rs
+++ b/crates/teleia/src/font.rs
@@ -1,4 +1,4 @@
-use crate::{context, mesh, state, texture};
+use crate::{context, mesh, shader, state, texture, utils};
use glow::HasContext;
pub struct BitmapParams<'color> {
@@ -192,3 +192,113 @@ impl Bitmap {
self.render_text_parameterized(ctx, st, text, params);
}
}
+
+struct GlyphOutliner {
+ pos: (f32, f32),
+ data: Vec<((f32, f32), (f32, f32), (f32, f32))>,
+}
+impl GlyphOutliner {
+ fn new() -> Self {
+ Self {
+ pos: (0.0, 0.0),
+ data: Vec::new(),
+ }
+ }
+ fn add_curve(&mut self, p1: (f32, f32), p2: (f32, f32), p3: (f32, f32)) {
+ self.data.push(((p1.0, p1.1), (p2.0, p2.1), (p3.0, p3.1)),);
+ }
+}
+impl ttf_parser::OutlineBuilder for GlyphOutliner {
+ fn move_to(&mut self, x: f32, y: f32) {
+ self.pos = (x, y);
+ }
+ fn line_to(&mut self, x: f32, y: f32) {
+ self.add_curve(self.pos, (x, y), (x, y));
+ self.pos = (x, y);
+ }
+ fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
+ self.add_curve(self.pos, (x1, y1), (x, y));
+ self.pos = (x, y);
+ }
+ fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
+ panic!("cubic");
+ // self.add_curve(self.pos, ((x1 + x2) / 2.0, (y1 + y2) / 2.0), (x, y));
+ // self.pos = (x, y);
+ }
+ fn close(&mut self) {}
+}
+
+pub struct Truetype {
+ shader: shader::Shader,
+ tex_bezier: texture::Texture,
+ face: ttf_parser::Face<'static>,
+}
+impl Truetype {
+ pub fn new(ctx: &context::Context, bs: &[u8]) -> utils::Erm<Self> {
+ let shader = shader::Shader::new(ctx,
+ include_str!("assets/shaders/slug/vert.glsl"),
+ include_str!("assets/shaders/slug/frag.glsl"),
+ );
+ let mut v: Vec<u8> = Vec::new();
+ v.extend_from_slice(bs);
+ let data = Box::leak(Box::new(v));
+ let tex_bezier = texture::Texture::new_empty(ctx);
+ let face = ttf_parser::Face::parse(&*data, 0)?;
+ Ok(Self {
+ shader,
+ tex_bezier,
+ face,
+ })
+ }
+ pub fn render_glyph(&self,
+ ctx: &context::Context, st: &mut state::State,
+ pos: glam::Vec2, sz: f32,
+ char: char
+ ) -> utils::Erm<()> {
+ let mut outliner = GlyphOutliner::new();
+ let g = self.face.glyph_index(char).unwrap();
+ let bound = self.face.outline_glyph(g, &mut outliner).unwrap();
+ log::info!("glyph {:?} bound: {:?}", g, bound);
+ let width = (bound.x_max - bound.x_min) as f32;
+ let height = (bound.y_max - bound.y_min) as f32;
+ let curves_len = outliner.data.len() as i32;
+ let mut bytes = Vec::new();
+ let scale_point = |bound: &ttf_parser::Rect, p: (f32, f32)| {
+ ((p.0 - bound.x_min as f32) / width - 0.5,
+ (p.1 - bound.y_min as f32) / height - 0.5)
+ };
+ fn extend_with_point(bytes: &mut Vec<u8>, p: (f32, f32)) {
+ bytes.extend_from_slice(&p.0.to_ne_bytes());
+ bytes.extend_from_slice(&p.1.to_ne_bytes());
+ }
+ for (p1, p2, p3) in outliner.data {
+ extend_with_point(&mut bytes, scale_point(&bound, p1));
+ extend_with_point(&mut bytes, scale_point(&bound, p2));
+ extend_with_point(&mut bytes, scale_point(&bound, p3));
+ extend_with_point(&mut bytes, (0.0, 0.0));
+ }
+ bytes.resize(4096 * 4 * 4, 0);
+ self.tex_bezier.bind(ctx);
+ unsafe {
+ ctx.gl.tex_image_2d(
+ glow::TEXTURE_2D,
+ 0,
+ glow::RGBA32F as i32,
+ 4096,
+ 1,
+ 0,
+ glow::RGBA,
+ glow::FLOAT,
+ Some(&bytes),
+ );
+ }
+ st.bind_2d(ctx, &self.shader);
+ self.shader.set_position_2d(ctx, st,
+ &pos,
+ &glam::Vec2::new(sz, height * sz / width)
+ );
+ self.shader.set_i32(ctx, "curves_len", curves_len);
+ st.mesh_square.render(ctx);
+ Ok(())
+ }
+}