summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/teleia/Cargo.toml2
-rw-r--r--crates/teleia/src/assets/fonts/default.png (renamed from crates/teleia/src/assets/fonts/simple.png)bin1042 -> 1042 bytes
-rw-r--r--crates/teleia/src/assets/fonts/font1.pngbin1508 -> 0 bytes
-rw-r--r--crates/teleia/src/assets/fonts/font2.pngbin1883 -> 0 bytes
-rw-r--r--crates/teleia/src/assets/shaders/uber/frag.glsl189
-rw-r--r--crates/teleia/src/assets/shaders/uber/vert.glsl31
-rw-r--r--crates/teleia/src/context.rs1
-rw-r--r--crates/teleia/src/font.rs4
-rw-r--r--crates/teleia/src/lib.rs1
-rw-r--r--crates/teleia/src/renderer.rs283
-rw-r--r--crates/teleia/src/state.rs21
-rw-r--r--crates/teleia_macros/src/lib.rs57
12 files changed, 558 insertions, 31 deletions
diff --git a/crates/teleia/Cargo.toml b/crates/teleia/Cargo.toml
index 290a977..0889a8e 100644
--- a/crates/teleia/Cargo.toml
+++ b/crates/teleia/Cargo.toml
@@ -24,7 +24,7 @@ enum-map = "*" # fast maps with enums as keys
bimap = "*" # bijective maps
# reqwest = "*" # http requests
bytes = "*" # bytes for http responses
-bitflags = "*" # bitwise flags
+bitflags = { version = "*", features = ["serde"] } # bitwise flags
simple-eyre = { version = "*", default-features = false } # error reporting and formatting
rapier3d = "*" # rigid-body physics
parry3d = "*" # collision detection
diff --git a/crates/teleia/src/assets/fonts/simple.png b/crates/teleia/src/assets/fonts/default.png
index 7b1d2a3..7b1d2a3 100644
--- a/crates/teleia/src/assets/fonts/simple.png
+++ b/crates/teleia/src/assets/fonts/default.png
Binary files differ
diff --git a/crates/teleia/src/assets/fonts/font1.png b/crates/teleia/src/assets/fonts/font1.png
deleted file mode 100644
index ec06424..0000000
--- a/crates/teleia/src/assets/fonts/font1.png
+++ /dev/null
Binary files differ
diff --git a/crates/teleia/src/assets/fonts/font2.png b/crates/teleia/src/assets/fonts/font2.png
deleted file mode 100644
index 8435cad..0000000
--- a/crates/teleia/src/assets/fonts/font2.png
+++ /dev/null
Binary files differ
diff --git a/crates/teleia/src/assets/shaders/uber/frag.glsl b/crates/teleia/src/assets/shaders/uber/frag.glsl
new file mode 100644
index 0000000..ccdb346
--- /dev/null
+++ b/crates/teleia/src/assets/shaders/uber/frag.glsl
@@ -0,0 +1,189 @@
+uniform int flags;
+
+uniform vec3 camera_pos;
+uniform float time;
+
+uniform vec2 sprite_offset;
+uniform vec2 sprite_dims;
+
+uniform vec4 color;
+uniform sampler2D texture_color;
+uniform sampler2D texture_normal;
+uniform vec3 light_ambient_color;
+uniform vec3 light_dir;
+uniform vec3 light_dir_color;
+uniform int light_count;
+uniform vec3 light_pos[5];
+uniform vec3 light_color[5];
+uniform vec2 light_attenuation[5];
+
+uniform int effect_flip;
+uniform float effect_flash;
+uniform float effect_hueshift;
+uniform float effect_huescale;
+
+in vec2 vertex_texcoord;
+in vec3 vertex_normal;
+in vec3 vertex_fragpos;
+in vec4 vertex_fragpos_shadow_dir;
+in vec3 vertex_view_vector;
+
+out vec4 frag_color;
+
+bool flag(int mask) {
+ return (flags & mask) != 0;
+}
+
+mat3 compute_tbn() {
+ vec3 p = -vertex_view_vector;
+ vec3 normal = normalize(vertex_normal);
+ vec3 dpx = dFdx(p);
+ vec3 dpy = dFdy(p);
+ vec2 duvx = dFdx(vertex_texcoord);
+ vec2 duvy = dFdy(vertex_texcoord);
+ vec3 dpyperp = cross(dpy, normal);
+ vec3 dpxperp = cross(normal, dpx);
+ vec3 tangent = dpyperp * duvx.x + dpxperp * duvy.x;
+ vec3 bitangent = dpyperp * duvx.y + dpxperp * duvy.y;
+ float invmax = inversesqrt(max(dot(bitangent, bitangent), dot(bitangent, bitangent)));
+ float flip = flag(FLIP_TEXTURE) ? 1.0 : -1.0;
+ return mat3(-tangent * invmax, flip * bitangent * invmax, normal);
+
+}
+
+vec4 normal_as_color(vec3 n) {
+ float r = (128.0 + 127.0 * n.r) / 255.0;
+ float g = (128.0 + 127.0 * n.g) / 255.0;
+ float b = (128.0 + 127.0 * n.b) / 255.0;
+ return vec4(r, g, b, 1.0);
+}
+
+vec3 dir_light(vec3 normal) {
+ return max(dot(normal, -normalize(light_dir)), 0.0) * light_dir_color;
+}
+
+vec3 point_light(vec3 normal, const int idx) {
+ vec3 pos = light_pos[idx];
+ vec3 color = light_color[idx];
+ float linear = light_attenuation[idx].x;
+ float quadratic = light_attenuation[idx].y;
+ vec3 light_vector = pos - vertex_fragpos;
+ float distance = length(light_vector);
+ float attenuation = 1.0 / (1.0 + distance * linear + distance * distance * quadratic);
+
+ float directional = max(dot(normal.xyz, normalize(light_vector)), 0.0);
+ vec3 directional_light = color * directional;
+
+ vec3 view_dir = normalize(camera_pos - vertex_fragpos);
+ vec3 reflect_dir = reflect(-normalize(light_vector), normalize(normal.xyz));
+ float specular = pow(max(dot(view_dir, reflect_dir), 0.0), 32.0);
+ vec3 specular_light = 0.5 * specular * color;
+ // return (directional_light + specular_light) * attenuation;
+ return directional_light * attenuation;
+}
+
+vec3 point_light_billboard(const int idx) {
+ vec3 pos = light_pos[idx];
+ vec3 color = light_color[idx];
+ float linear = light_attenuation[idx].x;
+ float quadratic = light_attenuation[idx].y;
+ vec3 light_vector = pos - vertex_fragpos;
+ float distance = length(light_vector);
+ float attenuation = 1.0 / (1.0 + distance * linear + distance * distance * quadratic);
+
+ return color * attenuation;
+}
+
+vec3 rgb_to_hsl(vec3 rgb) {
+ vec3 ret;
+ float min = min(min(rgb.r, rgb.g), rgb.b);
+ float max = max(max(rgb.r, rgb.g), rgb.b);
+ float lum = (max + min) / 2.0;
+ ret.z = lum;
+ if (max == min) {
+ ret.x = ret.y = 0.0;
+ } else {
+ float chroma = max - min;
+ ret.y = chroma / (1.0 - abs(2.0 * lum - 1.0));
+ if (max == rgb.r) {
+ ret.x = (rgb.g - rgb.b) / chroma + (rgb.g < rgb.b ? 6.0 : 0.0);
+ } else if (max == rgb.g) {
+ ret.x = (rgb.b - rgb.r) / chroma + 2.0;
+ } else {
+ ret.x = (rgb.r - rgb.g) / chroma + 4.0;
+ }
+ ret.x /= 6.0;
+ }
+ return ret;
+}
+
+float hue_to_rgb(float p, float q, float t) {
+ if (t < 0.0) t += 1.0;
+ if (t > 1.0) t -= 1.0;
+ if (t < 1.0/6.0) return p + (q - p) * 6.0 * t;
+ if (t < 1.0/2.0) return q;
+ if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0;
+ return p;
+}
+
+vec3 hsl_to_rgb(vec3 hsl) {
+ vec3 ret;
+ if (hsl.y == 0.0) {
+ ret.r = ret.g = ret.b = hsl.z;
+ } else {
+ float q = hsl.z < 0.5 ? hsl.z * (1.0 + hsl.y) : hsl.z + hsl.y - hsl.z * hsl.y;
+ float p = 2.0 * hsl.z - q;
+ ret.r = hue_to_rgb(p, q, hsl.x + 1.0/3.0);
+ ret.g = hue_to_rgb(p, q, hsl.x);
+ ret.b = hue_to_rgb(p, q, hsl.x - 1.0/3.0);
+ }
+ return ret;
+}
+
+void main() {
+ float tcy = flag(FLIP_TEXTURE) ? vertex_texcoord.y : 1.0 - vertex_texcoord.y;
+ vec2 tc = vec2(vertex_texcoord.x, tcy);
+ mat3 tbn = compute_tbn();
+ if (flag(EFFECTS)) {
+ float fbase = float(effect_flip);
+ float fmul = 1.0 - 2.0 * fbase;
+ tc = vec2(fbase + fmul * vertex_texcoord.x, tcy);
+ }
+ if (flag(SPRITE)) {
+ tc *= sprite_dims;
+ tc += sprite_offset;
+ }
+ frag_color = color;
+ if (flag(TEXTURE_COLOR)) {
+ frag_color = texture(texture_color, tc);
+ }
+ if (flag(EFFECTS)) {
+ vec3 hsl = rgb_to_hsl(frag_color.rgb);
+ hsl.x = mod(hsl.x * effect_huescale + effect_hueshift, 1.0);
+ vec3 p = hsl_to_rgb(hsl);
+ frag_color.rgb = vec3(p.r + effect_flash, p.g + effect_flash, p.b + effect_flash);
+ }
+ vec3 normal = vertex_normal;
+ if (flag(TEXTURE_NORMAL)) {
+ normal = normalize(tbn * (texture(texture_normal, tc).xyz * 2.0 - 1.0));
+ }
+ vec3 from_ambient = vec3(1.0, 1.0, 1.0);
+ if (flag(LIGHT_AMBIENT)) {
+ from_ambient = light_ambient_color;
+ }
+ vec3 from_dir = vec3(0.0, 0.0, 0.0);
+ if (flag(LIGHT_DIR)) {
+ from_dir = dir_light(normal);
+ }
+ vec3 from_points = vec3(0.0, 0.0, 0.0);
+ if (flag(LIGHT_POINT)) {
+ for (int i = 0; i < light_count; ++i) {
+ vec3 pl = point_light(normal, i);
+ from_points += pl;
+ }
+ }
+ frag_color.rgb *= (from_ambient + from_dir + from_points);
+ if (frag_color.a == 0.0) {
+ discard;
+ }
+}
diff --git a/crates/teleia/src/assets/shaders/uber/vert.glsl b/crates/teleia/src/assets/shaders/uber/vert.glsl
new file mode 100644
index 0000000..15223a0
--- /dev/null
+++ b/crates/teleia/src/assets/shaders/uber/vert.glsl
@@ -0,0 +1,31 @@
+#version 300 es
+precision highp float;
+
+in vec3 vertex;
+in vec3 normal;
+in vec2 texcoord;
+
+uniform mat4 view;
+uniform mat4 position;
+uniform mat4 projection;
+uniform mat4 normal_matrix;
+uniform mat4 lightspace_matrix;
+uniform vec3 camera_pos;
+uniform vec3 offset;
+
+out vec2 vertex_texcoord;
+out vec3 vertex_normal;
+out vec3 vertex_fragpos;
+out vec4 vertex_fragpos_shadow_dir;
+out vec3 vertex_view_vector;
+
+void main() {
+ vertex_texcoord = texcoord;
+ vertex_normal = (normal_matrix * vec4(normal, 1.0)).xyz;
+ vec3 pos = (position * vec4(vertex, 1.0)).xyz;
+ vertex_fragpos = pos;
+ vertex_fragpos_shadow_dir = lightspace_matrix * vec4(pos, 1.0);
+ vertex_view_vector = camera_pos - pos;
+ gl_Position = projection * view * vec4(pos, 1.0);
+ gl_Position.xyz += offset;
+}
diff --git a/crates/teleia/src/context.rs b/crates/teleia/src/context.rs
index 4bcdea0..602e2cc 100644
--- a/crates/teleia/src/context.rs
+++ b/crates/teleia/src/context.rs
@@ -101,6 +101,7 @@ impl Context {
self.gl.clear_color(0.1, 0.1, 0.1, 1.0);
self.gl.clear_depth_f32(1.0);
+ #[cfg(debug_assertions)]
self.gl.enable(glow::DEBUG_OUTPUT);
self.gl.enable(glow::DEPTH_TEST);
diff --git a/crates/teleia/src/font.rs b/crates/teleia/src/font.rs
index b4dc68d..0587556 100644
--- a/crates/teleia/src/font.rs
+++ b/crates/teleia/src/font.rs
@@ -63,8 +63,8 @@ impl Bitmap {
}
}
- pub fn new(ctx: &context::Context) -> Self {
- Self::from_image(ctx, 7, 9, 112, 54, include_bytes!("assets/fonts/simple.png"))
+ pub fn default(ctx: &context::Context) -> Self {
+ Self::from_image(ctx, 7, 9, 112, 54, include_bytes!("assets/fonts/default.png"))
}
pub fn render_text_parameterized(&self,
diff --git a/crates/teleia/src/lib.rs b/crates/teleia/src/lib.rs
index cb7aefb..8e991ef 100644
--- a/crates/teleia/src/lib.rs
+++ b/crates/teleia/src/lib.rs
@@ -11,6 +11,7 @@ pub mod texture;
pub mod scene;
pub mod font;
pub mod shadow;
+pub mod renderer;
pub mod audio;
pub mod net;
pub mod physics;
diff --git a/crates/teleia/src/renderer.rs b/crates/teleia/src/renderer.rs
new file mode 100644
index 0000000..d40c0cd
--- /dev/null
+++ b/crates/teleia/src/renderer.rs
@@ -0,0 +1,283 @@
+use crate::{context, state, shader, texture, mesh};
+
+use bitflags::bitflags;
+
+bitflags! {
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+ pub struct UberFlags: u32 {
+ const TEXTURE_COLOR = 0b00000001;
+ const TEXTURE_NORMAL = 0b00000010;
+ const FLIP_TEXTURE = 0b00000100;
+ const LIGHT_AMBIENT = 0b00001000;
+ const LIGHT_DIR = 0b00010000;
+ const LIGHT_POINT = 0b00100000;
+ const SPRITE = 0b01000000;
+ const EFFECTS = 0b10000000;
+ }
+}
+impl UberFlags {
+ fn prelude() -> String {
+ let mut s = String::new();
+ s += "#version 300 es\nprecision highp float;";
+ for (nm, f) in Self::all().iter_names() {
+ s += &format!("const int {} = {};\n", nm, f.bits());
+ }
+ s
+ }
+ fn set_flags(self, ctx: &context::Context, shader: &shader::Shader) {
+ shader.set_i32(ctx, "flags", self.bits() as i32);
+ }
+}
+
+pub trait Assets {
+ type Shader: PartialEq + Eq + Clone + Copy;
+ fn shader(&self, i: Self::Shader) -> &shader::Shader;
+ type Texture: PartialEq + Eq + Clone + Copy;
+ fn texture(&self, i: Self::Texture) -> &texture::Texture;
+ type Mesh: PartialEq + Eq + Clone + Copy;
+ fn mesh(&self, i: Self::Mesh) -> &mesh::Mesh;
+ type Material: PartialEq + Eq + Clone + Copy;
+ fn material(&self, i: Self::Material) -> &texture::Material;
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum ShaderMode { TwoDimension, ThreeDimension, }
+#[derive(Debug, Clone, Copy)]
+enum BoundShader<A: Assets> { None, Uber(UberFlags, ShaderMode), Shader(A::Shader, ShaderMode) }
+impl<A: Assets> PartialEq for BoundShader<A> {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (Self::None, Self::None) => true,
+ (Self::Uber(sf, sm), Self::Uber(of, om)) => sf == of && sm == om,
+ (Self::Shader(ss, sm), Self::Shader(os, om)) => ss == os && sm == om,
+ _ => false,
+ }
+ }
+}
+#[derive(Debug, Clone, Copy)]
+enum BoundTexture<A: Assets> { None, Texture(A::Texture), Material(A::Material) }
+impl<A: Assets> PartialEq for BoundTexture<A> {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (Self::None, Self::None) => true,
+ (Self::Texture(s), Self::Texture(o)) => s == o,
+ (Self::Material(s), Self::Material(o)) => s == o,
+ _ => false,
+ }
+ }
+}
+pub struct Renderer<A: Assets> {
+ pub assets: A,
+ shader_uber: shader::Shader,
+ shader: BoundShader<A>,
+ texture: BoundTexture<A>,
+}
+impl<A: Assets> Renderer<A> {
+ pub fn new<F>(ctx: &context::Context, f: F) -> Self
+ where F: FnOnce(&context::Context) -> A {
+ let shader_uber = shader::Shader::new_nolib(ctx,
+ include_str!("assets/shaders/uber/vert.glsl"),
+ &format!("{}{}", UberFlags::prelude(), include_str!("assets/shaders/uber/frag.glsl")),
+ );
+ shader_uber.bind(ctx);
+ shader_uber.set_i32(ctx, "texture_normal", 1);
+ Self {
+ assets: f(ctx),
+ shader_uber,
+ shader: BoundShader::None,
+ texture: BoundTexture::None,
+ }
+ }
+ pub fn font_char_width(&self, st: &state::State) -> f32 { st.font_default.char_width as f32 }
+ pub fn font_char_height(&self, st: &state::State) -> f32 { st.font_default.char_height as f32 }
+ pub fn bind_texture(&mut self, ctx: &context::Context, _st: &mut state::State, texture: A::Texture) {
+ if self.texture != BoundTexture::Texture(texture) {
+ self.assets.texture(texture).bind(ctx);
+ self.texture = BoundTexture::Texture(texture);
+ }
+ }
+ pub fn bind_material(&mut self, ctx: &context::Context, _st: &mut state::State, mat: A::Material) {
+ if self.texture != BoundTexture::Material(mat) {
+ self.assets.material(mat).bind(ctx);
+ self.texture = BoundTexture::Material(mat);
+ }
+ }
+ fn shader(&self) -> Option<(&shader::Shader, ShaderMode)> {
+ match self.shader {
+ BoundShader::Uber(_, sm) => Some((&self.shader_uber, sm)),
+ BoundShader::Shader(s, sm) => Some((self.assets.shader(s), sm)),
+ _ => None,
+ }
+ }
+ fn bind_uber(&mut self,
+ ctx: &context::Context, st: &mut state::State,
+ flags: UberFlags, mode: ShaderMode,
+ ) {
+ if let BoundShader::Uber(f, sm) = self.shader {
+ if f == flags && sm == mode {
+ return;
+ }
+ }
+ match mode {
+ ShaderMode::TwoDimension => st.bind_2d(ctx, &self.shader_uber),
+ ShaderMode::ThreeDimension => st.bind_3d(ctx, &self.shader_uber),
+ }
+ flags.set_flags(ctx, &self.shader_uber);
+ self.shader = BoundShader::Uber(flags, mode)
+ }
+ fn bind_shader(&mut self,
+ ctx: &context::Context, st: &mut state::State,
+ shader: A::Shader, mode: ShaderMode,
+ ) {
+ if let BoundShader::Shader(s, sm) = self.shader {
+ if s == shader && sm == mode {
+ return;
+ }
+ }
+ match mode {
+ ShaderMode::TwoDimension => st.bind_2d(ctx, &self.assets.shader(shader)),
+ ShaderMode::ThreeDimension => st.bind_3d(ctx, &self.assets.shader(shader)),
+ }
+ self.shader = BoundShader::Shader(shader, mode)
+ }
+ pub fn bind_uber_2d(&mut self, ctx: &context::Context, st: &mut state::State, flags: UberFlags) {
+ self.bind_uber(ctx, st, flags, ShaderMode::TwoDimension);
+ }
+ pub fn bind_uber_3d(&mut self, ctx: &context::Context, st: &mut state::State, flags: UberFlags) {
+ self.bind_uber(ctx, st, flags, ShaderMode::ThreeDimension);
+ }
+ pub fn bind_shader_2d(&mut self, ctx: &context::Context, st: &mut state::State, shader: A::Shader) {
+ self.bind_shader(ctx, st, shader, ShaderMode::TwoDimension);
+ }
+ pub fn bind_shader_3d(&mut self, ctx: &context::Context, st: &mut state::State, shader: A::Shader) {
+ self.bind_shader(ctx, st, shader, ShaderMode::ThreeDimension);
+ }
+ pub fn render(&self, ctx: &context::Context, _st: &state::State, mesh: A::Mesh) {
+ self.assets.mesh(mesh).render(ctx)
+ }
+ pub fn render_square(&self, ctx: &context::Context, st: &state::State) {
+ st.mesh_square.render(ctx)
+ }
+ pub fn set_position_2d(&self, ctx: &context::Context, st: &state::State, pos: glam::Vec2, dims: glam::Vec2) {
+ if let Some((s, sm)) = self.shader() {
+ debug_assert!(sm == ShaderMode::TwoDimension, "attempted to set_position_2d in wrong mode");
+ s.set_position_2d(ctx, st, &pos, &dims)
+ }
+ }
+ pub fn set_position_3d(&self, ctx: &context::Context, st: &state::State, pos: glam::Mat4) {
+ if let Some((s, sm)) = self.shader() {
+ debug_assert!(sm == ShaderMode::ThreeDimension, "attempted to set_position_3d in wrong mode");
+ s.set_position_3d(ctx, st, &pos)
+ }
+ }
+ pub fn set_i32(&self, ctx: &context::Context, _st: &state::State, nm: &str, val: i32) {
+ if let Some((s, _)) = self.shader() { s.set_i32(ctx, nm, val) }
+ }
+ pub fn set_f32(&self, ctx: &context::Context, _st: &state::State, nm: &str, val: f32) {
+ if let Some((s, _)) = self.shader() { s.set_f32(ctx, nm, val) }
+ }
+ pub fn set_vec2(&self, ctx: &context::Context, _st: &state::State, nm: &str, val: glam::Vec2) {
+ if let Some((s, _)) = self.shader() { s.set_vec2(ctx, nm, &val) }
+ }
+ pub fn set_vec3(&self, ctx: &context::Context, _st: &state::State, nm: &str, val: glam::Vec3) {
+ if let Some((s, _)) = self.shader() { s.set_vec3(ctx, nm, &val) }
+ }
+ pub fn set_vec4(&self, ctx: &context::Context, _st: &state::State, nm: &str, val: glam::Vec4) {
+ if let Some((s, _)) = self.shader() { s.set_vec4(ctx, nm, &val) }
+ }
+ pub fn set_mat4(&self, ctx: &context::Context, _st: &state::State, nm: &str, val: glam::Mat4) {
+ if let Some((s, _)) = self.shader() { s.set_mat4(ctx, nm, &val) }
+ }
+ pub fn set_texture_offset(&self, ctx: &context::Context, st: &state::State, inc: i32, x: i32, y: i32) {
+ let count = inc as f32;
+ let ratio = 1.0 / count;
+ self.set_vec2(
+ ctx, st, "sprite_dims",
+ glam::Vec2::new(ratio, ratio),
+ );
+ self.set_vec2(
+ ctx, st, "sprite_offset",
+ glam::Vec2::new((x % inc) as f32 * ratio, (y % inc) as f32 * ratio),
+ );
+ }
+
+ /// Common case: draw the given textured mesh in the world (units are world tiles)
+ pub fn textured_mesh_world(&mut self,
+ ctx: &context::Context, st: &mut state::State,
+ shader: A::Shader,
+ texture: A::Texture,
+ mesh: A::Mesh,
+ pos: glam::Mat4,
+ ) {
+ self.bind_shader_3d(ctx, st, shader);
+ self.bind_texture(ctx, st, texture);
+ self.set_position_3d(ctx, st, pos);
+ self.render(ctx, st, mesh);
+ }
+
+ /// Common case: draw the given color in a rectangle on the screen (units are pixels, pos is top left)
+ pub fn color_screen(&mut self,
+ ctx: &context::Context, st: &mut state::State,
+ color: glam::Vec4,
+ pos: glam::Vec2,
+ dims: glam::Vec2,
+ ) {
+ self.bind_uber_2d(ctx, st, UberFlags::empty());
+ self.set_vec4(ctx, st, "color", color);
+ self.set_position_2d(ctx, st, pos, dims);
+ self.render_square(ctx, st);
+ }
+
+ /// Common case: draw the given texture on the screen (units are pixels, pos is top left)
+ pub fn texture_screen(&mut self,
+
+ ctx: &context::Context, st: &mut state::State,
+ texture: A::Texture,
+ pos: glam::Vec2,
+ dims: glam::Vec2,
+ ) {
+ self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR);
+ self.bind_texture(ctx, st, texture);
+ self.set_position_2d(ctx, st, pos, dims);
+ self.render_square(ctx, st);
+ }
+
+ /// Common case: text in the default font (units are pixels, pos is top left)
+ pub fn text_screen(&mut self,
+ ctx: &context::Context, st: &mut state::State,
+ pos: glam::Vec2,
+ s: &str,
+ ) {
+ // drawing text might bind the shader and texture
+ self.shader = BoundShader::None; self.texture = BoundTexture::None;
+ st.font_default.render_text(ctx, st, &pos, s);
+ }
+
+ /// Common case: text in the default font, with a color (units are pixels, pos is top left)
+ pub fn text_colored_screen(&mut self,
+ ctx: &context::Context, st: &mut state::State,
+ pos: glam::Vec2,
+ col: glam::Vec3,
+ s: &str,
+ ) {
+ // drawing text might bind the shader and texture
+ self.shader = BoundShader::None; self.texture = BoundTexture::None;
+ st.font_default.render_text_helper(ctx, st, &pos, s, &[col]);
+ }
+
+ /// Common case: text in the default font (units are pixels, pos is center)
+ pub fn text_centered_screen(&mut self,
+ ctx: &context::Context, st: &mut state::State,
+ pos: glam::Vec2,
+ s: &str,
+ ) {
+ // drawing text might bind the shader and texture
+ self.shader = BoundShader::None; self.texture = BoundTexture::None;
+ let width = s.len() as f32 * st.font_default.char_width as f32;
+ let height = st.font_default.char_height as f32;
+ st.font_default.render_text(ctx, st,
+ &(pos - glam::Vec2::new((width / 2.0).round(), (height / 2.0).round())),
+ s
+ );
+ }
+}
diff --git a/crates/teleia/src/state.rs b/crates/teleia/src/state.rs
index 46b46b1..2b23ae7 100644
--- a/crates/teleia/src/state.rs
+++ b/crates/teleia/src/state.rs
@@ -6,7 +6,7 @@ use glow::HasContext;
use serde::{Serialize, Deserialize};
use strum::EnumIter;
-use crate::{audio, context, framebuffer, mesh, shader, utils};
+use crate::{audio, context, font, framebuffer, mesh, shader, utils};
const DELTA_TIME: f64 = 0.016; // todo
@@ -167,6 +167,7 @@ pub struct State {
pub render_dims: glam::Vec2,
pub shader_upscale: shader::Shader,
pub mesh_square: mesh::Mesh,
+ pub font_default: font::Bitmap,
pub audio: Option<audio::Assets>,
pub projection: glam::Mat4,
@@ -261,6 +262,7 @@ impl State {
render_dims: glam::Vec2::new(ctx.render_width, ctx.render_height),
shader_upscale,
mesh_square,
+ font_default: font::Bitmap::default(ctx),
audio: None,
projection: glam::Mat4::perspective_lh(
@@ -534,13 +536,16 @@ impl State {
self.shader_upscale.bind(&ctx);
self.render_framebuffer.bind_texture(&ctx);
ctx.render_no_geometry();
- let err = unsafe { ctx.gl.get_error() };
- if err != glow::NO_ERROR {
- log::warn!("opengl error: {}", err);
- }
- let log = unsafe { ctx.gl.get_debug_message_log(5) };
- for m in log {
- log::warn!("opengl debug message: {:?}", m);
+ #[cfg(debug_assertions)]
+ {
+ let err = unsafe { ctx.gl.get_error() };
+ if err != glow::NO_ERROR {
+ log::warn!("opengl error: {}", err);
+ }
+ let log = unsafe { ctx.gl.get_debug_message_log(5) };
+ for m in log {
+ log::warn!("opengl debug message: {:?}", m);
+ }
}
Ok(())
}
diff --git a/crates/teleia_macros/src/lib.rs b/crates/teleia_macros/src/lib.rs
index ead8f50..9e358d9 100644
--- a/crates/teleia_macros/src/lib.rs
+++ b/crates/teleia_macros/src/lib.rs
@@ -76,24 +76,20 @@ impl Field {
ents.push((d.enum_entry(&self.nm), exp));
}
}
- if ents.len() > 0 {
- let (enm, ty) = match self.nm.as_str() {
- "meshes" => ("Mesh", "teleia::mesh::Mesh"),
- "textures" => ("Texture", "teleia::texture::Texture"),
- "materials" => ("Material", "teleia::texture::Material"),
- "shaders" => ("Shader", "teleia::shader::Shader"),
- _ => panic!("unknown asset type: {}", self.nm),
- };
- let enums: Vec<_> = ents.iter().map(|(e, _)| e.clone()).collect();
- let edecl = format!("#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, enum_map::Enum)]
+ let (enm, ty) = match self.nm.as_str() {
+ "meshes" => ("Mesh", "teleia::mesh::Mesh"),
+ "textures" => ("Texture", "teleia::texture::Texture"),
+ "materials" => ("Material", "teleia::texture::Material"),
+ "shaders" => ("Shader", "teleia::shader::Shader"),
+ _ => return None,
+ };
+ let enums: Vec<_> = ents.iter().map(|(e, _)| e.clone()).collect();
+ let edecl = format!("#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, enum_map::Enum)]
pub enum {} {{ {} }}", enm, enums.join(", "));
- let decl = format!("pub {}: enum_map::EnumMap<{}, {}>", self.nm, enm, ty);
- let inits: Vec<_> = ents.into_iter().map(|(e, exp)| format!("{}::{} => {}", enm, e, exp)).collect();
- let init = format!("{}: enum_map::enum_map!({})", self.nm, inits.join(", "));
- Some((edecl, decl, init))
- } else {
- None
- }
+ let decl = format!("pub {}: enum_map::EnumMap<{}, {}>", self.nm, enm, ty);
+ let inits: Vec<_> = ents.into_iter().map(|(e, exp)| format!("{}::{} => {}", enm, e, exp)).collect();
+ let init = format!("{}: enum_map::enum_map!({})", self.nm, inits.join(", "));
+ Some((edecl, decl, init))
}
}
@@ -105,11 +101,24 @@ impl AssetData {
fn new(base: &str) -> Self {
let mut fields = Vec::new();
let dirs = std::fs::read_dir(base).expect(&format!("failed to read assets directory: {}", base));
+ let (mut has_meshes, mut has_textures, mut has_materials, mut has_shaders) = (false, false, false, false);
for dir in dirs {
if let Ok(d) = dir {
- fields.push(Field::new(base, &d.file_name().into_string().unwrap()));
+ let nm = d.file_name().into_string().unwrap();
+ fields.push(Field::new(base, &nm));
+ match &*nm {
+ "meshes" => has_meshes = true,
+ "textures" => has_textures = true,
+ "materials" => has_materials = true,
+ "shaders" => has_shaders = true,
+ _ => {},
+ };
}
}
+ if !has_meshes { fields.push(Field { nm: "meshes".to_owned(), entries: HashSet::new() }); }
+ if !has_textures { fields.push(Field { nm: "textures".to_owned(), entries: HashSet::new() }); }
+ if !has_materials { fields.push(Field { nm: "materials".to_owned(), entries: HashSet::new() }); }
+ if !has_shaders { fields.push(Field { nm: "shaders".to_owned(), entries: HashSet::new() }); }
Self {
fields,
}
@@ -121,16 +130,24 @@ impl AssetData {
res += edecl; res += "\n";
}
res += "pub struct Assets {\n";
- res += "pub font_default: teleia::font::Bitmap,\n";
for (_, decl, _) in fdata.iter() {
res += decl; res += ",\n";
}
res += "}\nimpl Assets {\npub fn new(ctx: &teleia::context::Context) -> Self {\nSelf {\n";
- res += "font_default: teleia::font::Bitmap::new(ctx),\n";
for (_, _, init) in fdata.iter() {
res += init; res += ",\n";
}
res += "}\n}\n}\n";
+ res += "impl teleia::renderer::Assets for Assets {\n";
+ res += "type Shader = Shader;\n";
+ res += "fn shader(&self, i: Self::Shader) -> &teleia::shader::Shader { &self.shaders[i] }\n";
+ res += "type Texture = Texture;\n";
+ res += "fn texture(&self, i: Self::Texture) -> &teleia::texture::Texture { &self.textures[i] }\n";
+ res += "type Material = Material;\n";
+ res += "fn material(&self, i: Self::Material) -> &teleia::texture::Material { &self.materials[i] }\n";
+ res += "type Mesh = Mesh;\n";
+ res += "fn mesh(&self, i: Self::Mesh) -> &teleia::mesh::Mesh { &self.meshes[i] }\n";
+ res += "}\n";
res
}
}