summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/assets/fonts/font1.pngbin1508 -> 0 bytes
-rw-r--r--src/assets/fonts/font2.pngbin1883 -> 0 bytes
-rw-r--r--src/assets/fonts/simple.pngbin1042 -> 0 bytes
-rw-r--r--src/assets/shaders/bitmap/frag.glsl17
-rw-r--r--src/assets/shaders/bitmap/vert.glsl17
-rw-r--r--src/assets/shaders/common/frag.glsl159
-rw-r--r--src/assets/shaders/common/vert.glsl30
-rw-r--r--src/assets/shaders/scale/frag.glsl12
-rw-r--r--src/assets/shaders/scale/vert.glsl22
-rw-r--r--src/assets/shaders/scene/frag.glsl11
-rw-r--r--src/assets/shaders/scene/vert.glsl18
-rw-r--r--src/assets/shaders/test/frag.glsl23
-rw-r--r--src/assets/shaders/test/vert.glsl4
-rw-r--r--src/assets/shaders/tiled/frag.glsl12
-rw-r--r--src/assets/shaders/tiled/vert.glsl14
-rw-r--r--src/assets/shaders/truetype/frag.glsl16
-rw-r--r--src/assets/shaders/truetype/vert.glsl17
-rw-r--r--src/audio.rs209
-rw-r--r--src/context.rs228
-rw-r--r--src/font.rs363
-rw-r--r--src/framebuffer.rs145
-rw-r--r--src/helpers.js13
-rw-r--r--src/js/module.js7
-rw-r--r--src/level2d.rs1
-rw-r--r--src/level2d/tiled.rs298
-rw-r--r--src/lib.rs328
-rw-r--r--src/mesh.rs132
-rw-r--r--src/net.rs1
-rw-r--r--src/net/client.rs2
-rw-r--r--src/net/client/wasm.rs67
-rw-r--r--src/physics.rs0
-rw-r--r--src/save.rs43
-rw-r--r--src/scene.rs398
-rw-r--r--src/shader.rs253
-rw-r--r--src/shadow.rs127
-rw-r--r--src/state.rs514
-rw-r--r--src/texture.rs83
-rw-r--r--src/ui.rs153
-rw-r--r--src/utils.rs131
39 files changed, 0 insertions, 3868 deletions
diff --git a/src/assets/fonts/font1.png b/src/assets/fonts/font1.png
deleted file mode 100644
index ec06424..0000000
--- a/src/assets/fonts/font1.png
+++ /dev/null
Binary files differ
diff --git a/src/assets/fonts/font2.png b/src/assets/fonts/font2.png
deleted file mode 100644
index 8435cad..0000000
--- a/src/assets/fonts/font2.png
+++ /dev/null
Binary files differ
diff --git a/src/assets/fonts/simple.png b/src/assets/fonts/simple.png
deleted file mode 100644
index 7b1d2a3..0000000
--- a/src/assets/fonts/simple.png
+++ /dev/null
Binary files differ
diff --git a/src/assets/shaders/bitmap/frag.glsl b/src/assets/shaders/bitmap/frag.glsl
deleted file mode 100644
index 7df9a5c..0000000
--- a/src/assets/shaders/bitmap/frag.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-#version 300 es
-precision highp float;
-
-uniform sampler2D texture_data;
-
-in vec2 vertex_texcoord;
-in vec3 vertex_color;
-out vec4 frag_color;
-
-void main() {
- vec4 texel = texture(texture_data, vertex_texcoord);
- if (texel.rgb == vec3(0.0, 0.0, 0.0)) discard;
- texel.r = vertex_color.r;
- texel.g = vertex_color.g;
- texel.b = vertex_color.b;
- frag_color = texel;
-}
diff --git a/src/assets/shaders/bitmap/vert.glsl b/src/assets/shaders/bitmap/vert.glsl
deleted file mode 100644
index 192d4b0..0000000
--- a/src/assets/shaders/bitmap/vert.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-#version 300 es
-precision highp float;
-
-in vec2 vertex;
-in vec2 texcoord;
-in vec3 color;
-
-uniform mat4 transform;
-
-out vec2 vertex_texcoord;
-out vec3 vertex_color;
-
-void main() {
- vertex_texcoord = texcoord;
- vertex_color = color;
- gl_Position = transform * vec4(vertex, 0.0, 1.0);
-}
diff --git a/src/assets/shaders/common/frag.glsl b/src/assets/shaders/common/frag.glsl
deleted file mode 100644
index 26d63fa..0000000
--- a/src/assets/shaders/common/frag.glsl
+++ /dev/null
@@ -1,159 +0,0 @@
-#version 300 es
-precision highp float;
-
-uniform vec3 camera_pos;
-uniform float time;
-
-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 highp sampler2DShadow light_shadowbuffer_dir;
-uniform samplerCube light_shadowbuffer_point[5];
-
-uniform int has_point_shadows;
-
-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;
-
-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)));
- return mat3(-tangent * invmax, -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;
-}
-
-float dir_shadow(vec3 normal) {
- vec3 proj = vertex_fragpos_shadow_dir.xyz / vertex_fragpos_shadow_dir.w;
- float bias = 0.002;
- // float current_depth = proj.z;
- // float bias = max(0.05 * (1.0 - dot(normal, -normalize(light_dir))), 0.005);
- proj.z -= bias;
- proj *= 0.5; proj += 0.5;
- if (proj.z > 1.0) return 0.0;
- return 1.0 - texture(light_shadowbuffer_dir, proj.xyz);
-}
-
-float point_shadow(vec3 normal, vec3 shadow_vector, float closest_depth) {
- closest_depth *= 25.0;
- float current_depth = length(shadow_vector);
- float bias = max(0.1 * (1.0 - dot(normal, normalize(shadow_vector))), 0.005);
- bias = min(bias + current_depth * 0.01, 0.2);
- float shadow = current_depth - bias > closest_depth ? 1.0 : 0.0;
- return shadow;
-}
-
-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 compute_lighting(vec3 normal) {
- vec3 ambient_light = light_ambient_color;
-
- vec3 from_dir = dir_light(normal) * (1.0 - dir_shadow(normal));
-
- vec3 shadow_vector[5];
- for (int i = 0; i < light_count; ++i) {
- shadow_vector[i] = vertex_fragpos - light_pos[i];
- shadow_vector[i].x *= -1.0;
- }
-
- // cannot only index array of samplers with a constant, hence the weird setup
- #define SAMPLE_SHADOW(n) n < light_count ? texture(light_shadowbuffer_point[n], shadow_vector[n]).r : 1.0
- float shadow_depth[5];
- shadow_depth[0] = SAMPLE_SHADOW(0);
- shadow_depth[1] = SAMPLE_SHADOW(1);
- shadow_depth[2] = SAMPLE_SHADOW(2);
- shadow_depth[3] = SAMPLE_SHADOW(3);
- shadow_depth[4] = SAMPLE_SHADOW(4);
-
- vec3 from_points = vec3(0.0, 0.0, 0.0);
- for (int i = 0; i < light_count; ++i) {
- from_points += has_point_shadows != 0
- ? point_light(normal, i) * (1.0 - point_shadow(normal, shadow_vector[i], shadow_depth[i]))
- : point_light(normal, i);
- }
-
- return (ambient_light + from_dir + from_points);
-}
-
-vec3 compute_lighting_noshadow(vec3 normal) {
- vec3 ambient_light = light_ambient_color;
-
- vec3 from_dir = dir_light(normal);
-
- vec3 from_points = vec3(0.0, 0.0, 0.0);
- for (int i = 0; i < light_count; ++i) {
- from_points += point_light(normal, i);
- }
-
- return (ambient_light + from_dir + from_points);
-}
-
-vec3 compute_lighting_billboard(vec3 normal) {
- vec3 ambient_light = light_ambient_color;
-
- vec3 from_dir = light_dir_color / 2.0;
-
- vec3 from_points = vec3(0.0, 0.0, 0.0);
- for (int i = 0; i < light_count; ++i) {
- from_points += point_light_billboard(i);
- }
-
- return (ambient_light + from_dir + from_points);
-}
diff --git a/src/assets/shaders/common/vert.glsl b/src/assets/shaders/common/vert.glsl
deleted file mode 100644
index b8e11c5..0000000
--- a/src/assets/shaders/common/vert.glsl
+++ /dev/null
@@ -1,30 +0,0 @@
-#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;
-
-out vec2 vertex_texcoord;
-out vec3 vertex_normal;
-out vec3 vertex_fragpos;
-out vec4 vertex_fragpos_shadow_dir;
-out vec3 vertex_view_vector;
-
-void default_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);
-}
diff --git a/src/assets/shaders/scale/frag.glsl b/src/assets/shaders/scale/frag.glsl
deleted file mode 100644
index 5fce547..0000000
--- a/src/assets/shaders/scale/frag.glsl
+++ /dev/null
@@ -1,12 +0,0 @@
-#version 300 es
-precision highp float;
-
-uniform sampler2D texture_data;
-
-in vec2 vertex_texcoord;
-out vec4 frag_color;
-
-void main() {
- vec4 texel = texture(texture_data, vertex_texcoord);
- frag_color = texel;
-}
diff --git a/src/assets/shaders/scale/vert.glsl b/src/assets/shaders/scale/vert.glsl
deleted file mode 100644
index e05bbb6..0000000
--- a/src/assets/shaders/scale/vert.glsl
+++ /dev/null
@@ -1,22 +0,0 @@
-#version 300 es
-precision highp float;
-
-out vec2 vertex_texcoord;
-
-void main() {
- const vec2 positions[4] = vec2[](
- vec2(-1, -1),
- vec2(+1, -1),
- vec2(-1, +1),
- vec2(+1, +1)
- );
- const vec2 coords[4] = vec2[](
- vec2(0, 0),
- vec2(1, 0),
- vec2(0, 1),
- vec2(1, 1)
- );
-
- vertex_texcoord = coords[gl_VertexID];
- gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
-}
diff --git a/src/assets/shaders/scene/frag.glsl b/src/assets/shaders/scene/frag.glsl
deleted file mode 100644
index 81e08b9..0000000
--- a/src/assets/shaders/scene/frag.glsl
+++ /dev/null
@@ -1,11 +0,0 @@
-uniform sampler2D texture_data;
-
-void main()
-{
- vec4 texel = texture(texture_data, vertex_texcoord);
- if (texel.a != 1.0) {
- discard;
- }
-
- frag_color = vec4(texel.rgb, texel.a);
-}
diff --git a/src/assets/shaders/scene/vert.glsl b/src/assets/shaders/scene/vert.glsl
deleted file mode 100644
index 64f400c..0000000
--- a/src/assets/shaders/scene/vert.glsl
+++ /dev/null
@@ -1,18 +0,0 @@
-in vec4 joint;
-in vec4 weight;
-
-uniform mat4 joint_matrices[128];
-
-void main()
-{
- vertex_texcoord = texcoord;
- vertex_normal = (normal_matrix * vec4(normal, 1.0)).xyz;
- mat4 skin
- = weight.x * joint_matrices[int(joint.x)]
- + weight.y * joint_matrices[int(joint.y)]
- + weight.z * joint_matrices[int(joint.z)]
- + weight.w * joint_matrices[int(joint.w)];
- vec3 pos = (position * skin * vec4(vertex, 1.0)).xyz;
- vertex_view_vector = camera_pos - pos;
- gl_Position = projection * view * vec4(pos, 1.0);
-}
diff --git a/src/assets/shaders/test/frag.glsl b/src/assets/shaders/test/frag.glsl
deleted file mode 100644
index a52aa15..0000000
--- a/src/assets/shaders/test/frag.glsl
+++ /dev/null
@@ -1,23 +0,0 @@
-// uniform int has_normal_map;
-// uniform sampler2D normal_map;
-
-uniform sampler2D texture_data;
-
-void main()
-{
- vec2 inverted_texcoord = vec2(vertex_texcoord.x, 1.0 - vertex_texcoord.y);
- vec4 texel = texture(texture_data, inverted_texcoord);
- if (texel.a != 1.0) {
- discard;
- }
-
- // mat3 tbn = compute_tbn();
- // vec3 normal = has_normal_map != 0
- // ? normalize(tbn * (texture(normal_map, inverted_texcoord).xyz * 2.0 - 1.0))
- // : normalize(vertex_normal);
- vec3 normal = normalize(vertex_normal);
-
- vec3 lighting = compute_lighting_noshadow(normal);
-
- frag_color = vec4(texel.rgb * lighting, texel.a);
-}
diff --git a/src/assets/shaders/test/vert.glsl b/src/assets/shaders/test/vert.glsl
deleted file mode 100644
index e324f7e..0000000
--- a/src/assets/shaders/test/vert.glsl
+++ /dev/null
@@ -1,4 +0,0 @@
-void main()
-{
- default_main();
-} \ No newline at end of file
diff --git a/src/assets/shaders/tiled/frag.glsl b/src/assets/shaders/tiled/frag.glsl
deleted file mode 100644
index 5fce547..0000000
--- a/src/assets/shaders/tiled/frag.glsl
+++ /dev/null
@@ -1,12 +0,0 @@
-#version 300 es
-precision highp float;
-
-uniform sampler2D texture_data;
-
-in vec2 vertex_texcoord;
-out vec4 frag_color;
-
-void main() {
- vec4 texel = texture(texture_data, vertex_texcoord);
- frag_color = texel;
-}
diff --git a/src/assets/shaders/tiled/vert.glsl b/src/assets/shaders/tiled/vert.glsl
deleted file mode 100644
index 4ff9865..0000000
--- a/src/assets/shaders/tiled/vert.glsl
+++ /dev/null
@@ -1,14 +0,0 @@
-#version 300 es
-precision highp float;
-
-in vec2 vertex;
-in vec2 texcoord;
-
-uniform mat4 transform;
-
-out vec2 vertex_texcoord;
-
-void main() {
- vertex_texcoord = texcoord;
- gl_Position = transform * vec4(vertex, 0.0, 1.0);
-}
diff --git a/src/assets/shaders/truetype/frag.glsl b/src/assets/shaders/truetype/frag.glsl
deleted file mode 100644
index 3f62e01..0000000
--- a/src/assets/shaders/truetype/frag.glsl
+++ /dev/null
@@ -1,16 +0,0 @@
-#version 300 es
-precision highp float;
-
-uniform sampler2D texture_data;
-
-in vec2 vertex_texcoord;
-in vec3 vertex_color;
-out vec4 frag_color;
-
-void main()
-{
- float val = texture(texture_data, vertex_texcoord).r;
- if (val == 0.0) discard;
- vec4 texel = vec4(vertex_color, val);
- frag_color = texel;
-}
diff --git a/src/assets/shaders/truetype/vert.glsl b/src/assets/shaders/truetype/vert.glsl
deleted file mode 100644
index 192d4b0..0000000
--- a/src/assets/shaders/truetype/vert.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-#version 300 es
-precision highp float;
-
-in vec2 vertex;
-in vec2 texcoord;
-in vec3 color;
-
-uniform mat4 transform;
-
-out vec2 vertex_texcoord;
-out vec3 vertex_color;
-
-void main() {
- vertex_texcoord = texcoord;
- vertex_color = color;
- gl_Position = transform * vec4(vertex, 0.0, 1.0);
-}
diff --git a/src/audio.rs b/src/audio.rs
deleted file mode 100644
index 2b4226c..0000000
--- a/src/audio.rs
+++ /dev/null
@@ -1,209 +0,0 @@
-use std::collections::HashMap;
-
-#[cfg(target_arch = "wasm32")]
-use std::cell::RefCell;
-
-#[cfg(target_arch = "wasm32")]
-pub struct Context {
- pub audio: web_sys::AudioContext,
-}
-
-#[cfg(target_arch = "wasm32")]
-impl Context {
- pub fn new() -> Self {
- let audio = web_sys::AudioContext::new()
- .expect("failed to create audio context");
- Self {
- audio,
- }
- }
-}
-
-#[cfg(target_arch = "wasm32")]
-pub struct Audio {
- pub buffer: &'static RefCell<Option<web_sys::AudioBuffer>>,
- //pub source: &'static web_sys::AudioBufferSourceNode,
-}
-
-#[cfg(target_arch = "wasm32")]
-impl Audio {
- pub fn new(ctx: &Context, bytes: &[u8]) -> Self {
- let sbuffer: &_ = Box::leak(Box::new(RefCell::new(None)));
- let sclone: &'static RefCell<Option<web_sys::AudioBuffer>> =
- <&_>::clone(&sbuffer);
- let ret = Audio {
- buffer: sclone,
- };
- let jsp = ctx.audio.decode_audio_data(&js_sys::Uint8Array::from(bytes).buffer()).expect("failed to decode audio");
- let promise = wasm_bindgen_futures::JsFuture::from(jsp);
- wasm_bindgen_futures::spawn_local(async {
- if let Some(data) = promise.await.ok() {
- *sbuffer.borrow_mut() = Some(web_sys::AudioBuffer::from(data));
- }
- ()
- });
- ret
- }
-
- pub fn play(&self, ctx: &Context, looping: Option<(Option<f64>, Option<f64>)>) -> Option<web_sys::AudioBufferSourceNode> {
- let source = ctx.audio.create_buffer_source().ok()?;
- source.set_buffer((&*self.buffer.borrow()).as_ref());
- if let Some((ms, me)) = looping {
- source.set_loop(true);
- if let Some(s) = ms { source.set_loop_start(s) }
- if let Some(e) = me { source.set_loop_end(e) }
- }
- source.connect_with_audio_node(&ctx.audio.destination()).ok()?;
- source.start().ok()?;
- Some(source)
- }
-}
-
-#[cfg(target_arch = "wasm32")]
-pub struct Assets {
- pub ctx: Context,
-
- pub audio: HashMap<String, Audio>,
-
- pub music_node: Option<web_sys::AudioBufferSourceNode>,
-}
-
-#[cfg(target_arch = "wasm32")]
-impl Assets {
- pub fn new<F>(f : F) -> Self where F: Fn(&Context) -> HashMap<String, Audio> {
- let ctx = Context::new();
-
- let audio = f(&ctx);
-
- Self {
- ctx,
- audio,
- music_node: None,
- }
- }
-
- pub fn play_sfx(&mut self, name: &str) {
- if let Some(a) = self.audio.get(name) {
- a.play(&self.ctx, None);
- }
- }
-
- pub fn is_music_playing(&self) -> bool {
- if let Some(ms) = &self.music_node {
- ms.buffer().is_some()
- } else { false }
- }
-
- pub fn play_music(&mut self, name: &str, start: Option<f64>, end: Option<f64>) {
- if let Some(s) = &self.music_node {
- let _ = s.stop();
- }
- if let Some(a) = self.audio.get(name) {
- self.music_node = a.play(&self.ctx, Some((start, end)));
- }
- }
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub struct Context {
- manager: kira::manager::AudioManager,
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-impl Context {
- pub fn new() -> Self {
- Self {
- manager: kira::manager::AudioManager::new(kira::manager::AudioManagerSettings::default())
- .expect("failed to create audio manager"),
- }
- }
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub struct Audio {
- data: kira::sound::static_sound::StaticSoundData,
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-impl Audio {
- pub fn new(_ctx: &Context, bytes: &'static [u8]) -> Self {
- Self {
- data: kira::sound::static_sound::StaticSoundData::from_cursor(std::io::Cursor::new(bytes))
- .expect("failed to decode audio"),
- }
- }
-
- pub fn play(
- &self,
- ctx: &mut Context,
- looping: Option<(Option<f64>, Option<f64>)>
- ) -> Result<kira::sound::static_sound::StaticSoundHandle, String>
- {
- let sd = if let Some((ss, se)) = looping {
- let start = if let Some(s) = ss { s } else { 0.0 };
- if let Some(e) = se {
- self.data.loop_region(start..e)
- } else {
- self.data.loop_region(start..)
- }
- } else {
- self.data.clone()
- };
- match ctx.manager.play(sd) {
- Ok(h) => Ok(h),
- Err(e) => Err(e.to_string()),
- }
- }
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub struct Assets {
- pub ctx: Context,
- pub audio: HashMap<String, Audio>,
- pub music_handle: Option<kira::sound::static_sound::StaticSoundHandle>,
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-impl Assets {
- pub fn new<F>(f : F) -> Self where F: Fn(&Context) -> HashMap<String, Audio> {
- let ctx = Context::new();
-
- let audio = f(&ctx);
-
- Self {
- ctx,
- audio,
- music_handle: None,
- }
- }
-
- pub fn play_sfx(&mut self, name: &str) {
- if let Some(a) = self.audio.get(name) {
- if let Err(e) = a.play(&mut self.ctx, None) {
- log::warn!("failed to play sound {}: {}", name, e);
- }
- }
- }
-
- pub fn is_music_playing(&self) -> bool {
- if let Some(mh) = &self.music_handle {
- mh.state() == kira::sound::PlaybackState::Playing
- } else { false }
- }
-
- pub fn play_music(&mut self, name: &str, start: Option<f64>, end: Option<f64>) {
- if let Some(s) = &mut self.music_handle {
- let _ = s.stop(kira::tween::Tween::default());
- }
- if let Some(a) = self.audio.get(name) {
- match a.play(&mut self.ctx, Some((start, end))) {
- Ok(h) => {
- self.music_handle = Some(h);
- },
- Err(e) => {
- log::warn!("failed to play music {}: {}", name, e);
- }
- }
- }
- }
-}
diff --git a/src/context.rs b/src/context.rs
deleted file mode 100644
index 9f0d4ad..0000000
--- a/src/context.rs
+++ /dev/null
@@ -1,228 +0,0 @@
-use glow::HasContext;
-
-#[cfg(target_arch = "wasm32")]
-use wasm_bindgen::prelude::*;
-
-#[cfg(target_arch = "wasm32")]
-use winit::platform::web::WindowExtWebSys;
-
-#[cfg(target_arch = "wasm32")]
-#[wasm_bindgen(module = "/src/helpers.js")]
-extern {
- fn js_track_resized_setup();
- fn js_poll_resized() -> bool;
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub struct Context {
- pub render_width: f32,
- pub render_height: f32,
- pub resize: bool,
- pub glfw: std::cell::RefCell<glfw::Glfw>,
- pub window: std::cell::RefCell<glfw::PWindow>,
- pub gl: glow::Context,
- pub emptyvao: glow::VertexArray,
- pub start_instant: std::time::Instant,
-}
-
-
-#[cfg(target_arch = "wasm32")]
-pub struct Context {
- pub render_width: f32,
- pub render_height: f32,
- pub resize: bool,
- pub window: winit::window::Window,
- pub gl: glow::Context,
- pub emptyvao: glow::VertexArray,
- pub performance: web_sys::Performance,
-}
-
-impl Context {
- pub fn compute_upscale(&self, windoww: u32, windowh: u32) -> u32 {
- let mut ratio = 1;
- loop {
- if (self.render_width as u32) * ratio > windoww
- || (self.render_height as u32) * ratio > windowh
- {
- break;
- }
- ratio += 1;
- }
- (ratio - 1).max(1)
- }
-
- #[cfg(not(target_arch = "wasm32"))]
- pub fn new(glfw: std::cell::RefCell<glfw::Glfw>, window: std::cell::RefCell<glfw::PWindow>, gl: glow::Context, render_width: f32, render_height: f32, resize: bool) -> Self {
- let emptyvao = unsafe {
- gl.create_vertex_array().expect("failed to initialize vao")
- };
- let ret = Self {
- render_width, render_height,
- resize,
- glfw, window,
- gl,
- emptyvao,
- start_instant: std::time::Instant::now(),
- };
- ret.init();
- ret
- }
-
- #[cfg(target_arch = "wasm32")]
- pub fn new(window: winit::window::Window, gl: glow::Context, render_width: f32, render_height: f32, resize: bool) -> Self {
- let emptyvao = unsafe {
- gl.create_vertex_array().expect("failed to initialize vao")
- };
-
- #[cfg(target_arch = "wasm32")]
- js_track_resized_setup();
-
- let ret = Self {
- render_width, render_height,
- resize,
- window,
- gl,
- emptyvao,
-
- #[cfg(target_arch = "wasm32")]
- performance: web_sys::window().expect("failed to find window")
- .performance().expect("failed to get performance"),
- };
- ret.init();
- ret
- }
-
- pub fn init(&self) {
- unsafe {
- self.gl.clear_color(0.1, 0.1, 0.1, 1.0);
- self.gl.clear_depth_f32(1.0);
-
- self.gl.enable(glow::DEPTH_TEST);
- self.gl.depth_func(glow::LEQUAL);
-
- self.gl.enable(glow::BLEND);
- self.gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
-
- self.gl.enable(glow::STENCIL_TEST);
-
- self.gl.cull_face(glow::FRONT);
- }
- }
-
- #[cfg(target_arch = "wasm32")]
- pub fn maximize_canvas(&self) {
- if self.resize {
- web_sys::window()
- .and_then(|win| win.document())
- .and_then(|doc| {
- let inner_size = {
- let browser_window = doc.default_view()
- .or_else(web_sys::window)
- .unwrap();
- winit::dpi::PhysicalSize::new(
- browser_window.inner_width().unwrap().as_f64().unwrap(),
- browser_window.inner_height().unwrap().as_f64().unwrap(),
- )
- };
- self.window.canvas().unwrap().set_width(inner_size.width as _);
- self.window.canvas().unwrap().set_height(inner_size.height as _);
- let _ = self.window.request_inner_size(inner_size);
- Some(())
- })
- .expect("failed to resize canvas");
- }
- }
-
- #[cfg(target_arch = "wasm32")]
- pub fn resize_necessary(&self) -> bool {
- js_poll_resized()
- }
-
- #[cfg(not(target_arch = "wasm32"))]
- pub fn resize_necessary(&self) -> bool {
- false
- }
-
- pub fn clear_color(&self, color: glam::Vec4) {
- unsafe {
- self.gl.clear_color(color.x, color.y, color.z, color.w);
- }
- }
-
- pub fn clear_depth(&self) {
- unsafe {
- self.gl.clear(glow::DEPTH_BUFFER_BIT);
- }
- }
-
- pub fn clear(&self) {
- unsafe {
- self.gl.stencil_mask(0xff);
- self.gl.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT | glow::STENCIL_BUFFER_BIT);
- }
- }
-
- pub fn begin_stencil(&self) {
- unsafe {
- self.gl.stencil_func(glow::ALWAYS, 1, 0xff);
- self.gl.stencil_op(glow::KEEP, glow::KEEP, glow::REPLACE);
- }
- }
-
- pub fn use_stencil(&self) {
- unsafe {
- self.gl.stencil_func(glow::EQUAL, 1, 0xff);
- self.gl.stencil_op(glow::KEEP, glow::KEEP, glow::KEEP);
- }
- }
-
- pub fn end_stencil(&self) {
- unsafe {
- self.gl.stencil_func(glow::ALWAYS, 1, 0xff);
- self.gl.stencil_op(glow::KEEP, glow::KEEP, glow::KEEP);
- }
- }
-
- pub fn render_no_geometry(&self) {
- unsafe {
- self.gl.bind_vertex_array(Some(self.emptyvao));
- self.gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
- }
- }
-
- pub fn reset_blend(&self) {
- unsafe {
- self.gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
- }
- }
-
- pub fn inverse_blend(&self) {
- unsafe {
- self.gl.blend_func(
- glow::ONE_MINUS_DST_COLOR,
- glow::ZERO,
- );
- }
- }
-
- pub fn enable_culling(&self) {
- unsafe {
- self.gl.enable(glow::CULL_FACE);
- }
- }
-
- pub fn disable_culling(&self) {
- unsafe {
- self.gl.disable(glow::CULL_FACE);
- }
- }
-
- pub fn check_error(&self) {
- unsafe {
- let err = self.gl.get_error();
- if err != 0 {
- log::warn!("gl error: {}", err);
- }
- }
- }
-}
diff --git a/src/font.rs b/src/font.rs
deleted file mode 100644
index e431896..0000000
--- a/src/font.rs
+++ /dev/null
@@ -1,363 +0,0 @@
-use std::collections::HashMap;
-
-use crate::{context, mesh, shader, texture};
-use glow::HasContext;
-
-pub struct Bitmap {
- pub char_width: i32,
- pub char_height: i32,
- pub font_width: i32,
- pub font_height: i32,
- pub shader: shader::Shader,
- pub font: texture::Texture,
- pub vao: glow::VertexArray,
- pub vertex_buf: glow::Buffer,
- pub texcoords_buf: glow::Buffer,
- pub colors_buf: glow::Buffer,
- pub index_buf: glow::Buffer,
-}
-
-impl Bitmap {
- pub fn from_image(
- ctx: &context::Context,
- char_width: i32, char_height: i32,
- font_width: i32, font_height: i32,
- data: &[u8],
- ) -> Self {
- let shader = shader::Shader::new_nolib(
- &ctx,
- include_str!("assets/shaders/bitmap/vert.glsl"),
- include_str!("assets/shaders/bitmap/frag.glsl"),
- );
- let font = texture::Texture::new(ctx, data);
- unsafe {
- let vao = ctx.gl.create_vertex_array().expect("failed to initialize vao");
- ctx.gl.bind_vertex_array(Some(vao));
- let vertex_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buf));
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_VERTEX, 2, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_VERTEX);
- let texcoords_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(texcoords_buf));
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_TEXCOORD, 2, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_TEXCOORD);
- let colors_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(colors_buf));
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_COLOR, 3, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_COLOR);
- let index_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buf));
- Self {
- char_width, char_height,
- font_width, font_height,
- shader,
- font,
- vao,
- vertex_buf,
- texcoords_buf,
- colors_buf,
- index_buf,
- }
- }
- }
-
- pub fn new(ctx: &context::Context) -> Self {
- Self::from_image(ctx, 7, 9, 112, 54, include_bytes!("assets/fonts/simple.png"))
- }
-
- pub fn render_text_helper(&self, ctx: &context::Context, pos: &glam::Vec2, text: &str, color: &[glam::Vec3]) {
- let mut cur = glam::Vec2::new(0.0, 0.0);
- let mut vertices = Vec::new();
- let mut texcoords = Vec::new();
- let mut colors = Vec::new();
- let mut indices = Vec::new();
- let cwidth = self.char_width as f32 / self.font_width as f32;
- let cheight = self.char_height as f32 / self.font_height as f32;
- let row_len = self.font_width as u32 / self.char_width as u32;
- for (i, c) in text.chars().enumerate() {
- if c == '\n' {
- cur.x = 0.0;
- cur.y -= self.char_height as f32;
- } else {
- let idx = vertices.len() as u32;
- vertices.push(cur);
- vertices.push(cur + glam::Vec2::new(self.char_width as f32, 0.0));
- vertices.push(cur + glam::Vec2::new(self.char_width as f32, self.char_height as f32));
- vertices.push(cur + glam::Vec2::new(0.0, self.char_height as f32));
- let cidx = c as u32 - ' ' as u32;
- let col = cidx % row_len;
- let row = cidx / row_len;
- let tcbase = glam::Vec2::new(col as f32 * cwidth, row as f32 * cheight);
- texcoords.push(tcbase + glam::Vec2::new(0.0, cheight));
- texcoords.push(tcbase + glam::Vec2::new(cwidth, cheight));
- texcoords.push(tcbase + glam::Vec2::new(cwidth, 0.0));
- texcoords.push(tcbase);
- let c = if let Some(c) = color.get(if color.len() == 0 { 0 } else { i % color.len() }) {
- *c
- } else {
- glam::Vec3::new(1.0, 1.0, 1.0)
- };
- colors.push(c); colors.push(c); colors.push(c); colors.push(c);
- indices.push(idx + 0); indices.push(idx + 1); indices.push(idx + 2);
- indices.push(idx + 0); indices.push(idx + 3); indices.push(idx + 2);
- cur.x += self.char_width as f32;
- }
- }
- let index_bytes: Vec<u8> = indices.iter().flat_map(|x| x.to_ne_bytes()).collect();
- self.shader.bind(ctx);
- self.font.bind(ctx);
- let scale = glam::Vec2::new(2.0 / ctx.render_width, 2.0 / ctx.render_height);
- let offset = glam::Vec2::new(
- -ctx.render_width / 2.0,
- ctx.render_height / 2.0 - self.char_height as f32,
- );
- let npos = (glam::Vec2::new(pos.x, -pos.y) + offset) * scale;
- self.shader.set_mat4(
- ctx, "transform",
- &glam::Mat4::from_scale_rotation_translation(
- glam::Vec3::new(scale.x, scale.y, 1.0),
- glam::Quat::IDENTITY,
- glam::Vec3::new(npos.x, npos.y, 0.0),
- ),
- );
- unsafe {
- ctx.gl.bind_vertex_array(Some(self.vao));
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- vertices.as_ptr() as _,
- vertices.len() * std::mem::size_of::<f32>() * 2,
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.texcoords_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- texcoords.as_ptr() as _,
- texcoords.len() * std::mem::size_of::<f32>() * 2,
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.colors_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- colors.as_ptr() as _,
- colors.len() * std::mem::size_of::<f32>() * 3,
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ELEMENT_ARRAY_BUFFER,
- &index_bytes,
- glow::STATIC_DRAW,
- );
- ctx.gl.draw_elements(glow::TRIANGLES, indices.len() as _, glow::UNSIGNED_INT, 0);
- }
- }
-
- pub fn render_text(&self, ctx: &context::Context, pos: &glam::Vec2, text: &str) {
- self.render_text_helper(ctx, pos, text, &[]);
- }
-}
-
-pub struct AtlasInfo {
- pub pos: usize,
-}
-
-pub struct TrueType {
- pub shader: shader::Shader,
- pub font: fontdue::Font,
- pub atlas: texture::Texture,
- pub atlaswidth: usize,
- pub cellwidth: usize,
- pub cellheight: usize,
- pub info: HashMap<char, AtlasInfo>,
- pub vao: glow::VertexArray,
- pub vertex_buf: glow::Buffer,
- pub texcoords_buf: glow::Buffer,
- pub colors_buf: glow::Buffer,
- pub index_buf: glow::Buffer,
-}
-
-impl TrueType {
- pub fn new(ctx: &context::Context, size: f32, data: &[u8]) -> Self {
- let shader = shader::Shader::new_nolib(
- &ctx,
- include_str!("assets/shaders/truetype/vert.glsl"),
- include_str!("assets/shaders/truetype/frag.glsl"),
- );
- let font = fontdue::Font::from_bytes(data, fontdue::FontSettings::default())
- .expect("failed to load font");
- let mut chardata = HashMap::new();
- for ci in 0..128 {
- if let Some(c) = char::from_u32(ci) {
- if !c.is_ascii_graphic() { continue; }
- let res = font.rasterize(c, size);
- chardata.insert(c, res);
- }
- }
- let mut cellwidth = 0;
- let mut cellbase = 0;
- let mut cellextra = 0;
- for (_, (m, _)) in &chardata {
- if m.width > cellwidth { cellwidth = m.width }
- if m.height > cellbase { cellbase = m.height }
- let extra = (-m.ymin.min(0)) as usize;
- if extra > cellextra { cellextra = extra }
- }
- let mut cellheight = cellbase + cellextra;
- cellwidth = cellwidth.next_power_of_two();
- cellheight = cellheight.next_power_of_two();
- let atlaswidth = (chardata.len() * cellwidth).next_power_of_two();
- let mut info = HashMap::new();
- let mut atlas_bmp: Vec<u8> = vec![0; atlaswidth * cellheight];
- for (i, (c, (m, bmp))) in chardata.iter().enumerate() {
- let by = ((cellbase as i32) - (m.height as i32) - m.ymin) as usize;
- let bx = cellwidth * i;
- info.insert(*c, AtlasInfo {
- pos: cellwidth * i,
- });
- for x in 0..m.width {
- for y in 0..m.height {
- atlas_bmp[bx + x + (by + y) * atlaswidth] = bmp[x + y * m.width];
- }
- }
- }
- let atlas = texture::Texture::new_empty(ctx);
- unsafe {
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(atlas.tex));
- ctx.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
- ctx.gl.tex_image_2d(
- glow::TEXTURE_2D,
- 0,
- glow::R8 as i32,
- atlaswidth as i32,
- cellheight as i32,
- 0,
- glow::RED,
- glow::UNSIGNED_BYTE,
- Some(&atlas_bmp),
- );
- ctx.gl.generate_mipmap(glow::TEXTURE_2D);
- let vao = ctx.gl.create_vertex_array().expect("failed to initialize vao");
- ctx.gl.bind_vertex_array(Some(vao));
- let vertex_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buf));
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_VERTEX, 2, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_VERTEX);
- let texcoords_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(texcoords_buf));
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_TEXCOORD, 2, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_TEXCOORD);
- let colors_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(colors_buf));
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_COLOR, 3, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_COLOR);
- let index_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buf));
- Self {
- shader, font, atlas, atlaswidth, cellwidth, cellheight, info,
- vao, vertex_buf, texcoords_buf, colors_buf, index_buf,
- }
- }
- }
-
- pub fn render_text_helper(&self, ctx: &context::Context, pos: &glam::Vec2, spacing: &glam::Vec2, text: &str, color: &[glam::Vec3]) {
- let mut cur = glam::Vec2::new(0.0, 0.0);
- let mut vertices = Vec::new();
- let mut texcoords = Vec::new();
- let mut colors = Vec::new();
- let mut indices = Vec::new();
- let cellwidth = self.cellwidth as f32;
- let cellheight = self.cellheight as f32;
- let cwidth = cellwidth / self.atlaswidth as f32;
- let cheight = 1.0;
- for (i, c) in text.chars().enumerate() {
- if c == '\n' {
- cur.x = 0.0;
- cur.y -= spacing.y;
- } else {
- let idx = vertices.len() as u32;
- if let Some(off) = self.info.get(&c) {
- vertices.push(cur);
- vertices.push(cur + glam::Vec2::new(cellwidth, 0.0));
- vertices.push(cur + glam::Vec2::new(cellwidth, cellheight));
- vertices.push(cur + glam::Vec2::new(0.0, cellheight));
- let tcbase = glam::Vec2::new(off.pos as f32 / self.atlaswidth as f32, 0.0);
- texcoords.push(tcbase + glam::Vec2::new(0.0, cheight));
- texcoords.push(tcbase + glam::Vec2::new(cwidth, cheight));
- texcoords.push(tcbase + glam::Vec2::new(cwidth, 0.0));
- texcoords.push(tcbase);
- let c = if let Some(c) = color.get(i) {
- *c
- } else {
- glam::Vec3::new(1.0, 1.0, 1.0)
- };
- colors.push(c); colors.push(c); colors.push(c); colors.push(c);
- indices.push(idx + 0); indices.push(idx + 1); indices.push(idx + 2);
- indices.push(idx + 0); indices.push(idx + 3); indices.push(idx + 2);
- }
- cur.x += spacing.x;
- }
- }
- let index_bytes: Vec<u8> = indices.iter().flat_map(|x| x.to_ne_bytes()).collect();
- let scale = glam::Vec2::new(2.0 / ctx.render_width, 2.0 / ctx.render_height);
- let offset = glam::Vec2::new(
- -ctx.render_width / 2.0,
- ctx.render_height / 2.0 - cellheight as f32,
- );
- let npos = (glam::Vec2::new(pos.x, -pos.y) + offset) * scale;
- self.shader.bind(ctx);
- self.shader.set_mat4(
- ctx, "transform",
- &glam::Mat4::from_scale_rotation_translation(
- glam::Vec3::new(scale.x, scale.y, 1.0),
- glam::Quat::IDENTITY,
- glam::Vec3::new(npos.x, npos.y, 0.0),
- ),
- );
- unsafe {
- ctx.gl.active_texture(glow::TEXTURE0);
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(self.atlas.tex));
- ctx.gl.bind_vertex_array(Some(self.vao));
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- vertices.as_ptr() as _,
- vertices.len() * std::mem::size_of::<f32>() * 2,
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.texcoords_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- texcoords.as_ptr() as _,
- texcoords.len() * std::mem::size_of::<f32>() * 2,
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.colors_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- colors.as_ptr() as _,
- colors.len() * std::mem::size_of::<f32>() * 3,
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ELEMENT_ARRAY_BUFFER,
- &index_bytes,
- glow::STATIC_DRAW,
- );
- ctx.gl.draw_elements(glow::TRIANGLES, indices.len() as _, glow::UNSIGNED_INT, 0);
- }
- }
-}
diff --git a/src/framebuffer.rs b/src/framebuffer.rs
deleted file mode 100644
index db808e4..0000000
--- a/src/framebuffer.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use glow::HasContext;
-
-use crate::context;
-
-pub struct Framebuffer {
- pub tex: Option<glow::Texture>,
- pub fbo: Option<glow::Framebuffer>,
- pub dims: glam::Vec2,
- pub offsets: glam::Vec2,
-}
-
-impl Framebuffer {
- pub fn screen(ctx: &context::Context) -> Self {
- #[cfg(target_arch = "wasm32")]
- let (windoww, windowh): (f32, f32) = if ctx.resize {
- ctx.window.inner_size().into()
- } else {
- (ctx.render_width, ctx.render_height)
- };
- #[cfg(not(target_arch = "wasm32"))]
- let (windoww, windowh) = {
- let (w, h) = ctx.window.borrow().get_size();
- (w as f32, h as f32)
- };
- let ratio = ctx.compute_upscale(windoww as _, windowh as _) as f32;
- let upscalew = ctx.render_width * ratio;
- let upscaleh = ctx.render_height * ratio;
- let offsetx = (windoww - upscalew) / 2.0;
- let offsety = (windowh - upscaleh) / 2.0;
- log::info!("resize window: {:?}, upscale: {:?}, offset: {:?}", (windoww, windowh), (upscalew, upscaleh), (offsetx, offsety));
- Self {
- tex: None,
- fbo: None,
- dims: glam::Vec2::new(upscalew, upscaleh),
- offsets: glam::Vec2::new(offsetx, offsety),
- }
- }
-
- pub fn new(ctx: &context::Context, dims: &glam::Vec2, offsets: &glam::Vec2) -> Self {
- unsafe {
- let fbo = ctx.gl.create_framebuffer()
- .expect("failed to create framebuffer");
- ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo));
-
- let depth_buffer = ctx.gl.create_renderbuffer()
- .expect("failed to create depth buffer");
- ctx.gl.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
- ctx.gl.renderbuffer_storage(glow::RENDERBUFFER, glow::DEPTH_COMPONENT32F, dims.x as _, dims.y as _);
- ctx.gl.framebuffer_renderbuffer(glow::FRAMEBUFFER, glow::DEPTH_ATTACHMENT, glow::RENDERBUFFER, Some(depth_buffer));
-
- let stencil_buffer = ctx.gl.create_renderbuffer()
- .expect("failed to create stencil buffer");
- ctx.gl.bind_renderbuffer(glow::RENDERBUFFER, Some(stencil_buffer));
- ctx.gl.renderbuffer_storage(glow::RENDERBUFFER, glow::DEPTH_STENCIL, dims.x as _, dims.y as _);
- ctx.gl.framebuffer_renderbuffer(glow::FRAMEBUFFER, glow::DEPTH_STENCIL_ATTACHMENT, glow::RENDERBUFFER, Some(stencil_buffer));
-
- let tex = ctx.gl.create_texture()
- .expect("failed to create framebuffer texture");
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
- ctx.gl.tex_image_2d(
- glow::TEXTURE_2D,
- 0,
- glow::RGBA as _,
- dims.x as _,
- dims.y as _,
- 0,
- glow::RGBA,
- glow::UNSIGNED_BYTE,
- None,
- );
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as _);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as _);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as _);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::NEAREST as _);
- ctx.gl.framebuffer_texture_2d(glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::TEXTURE_2D, Some(tex), 0);
- ctx.gl.draw_buffer(glow::COLOR_ATTACHMENT0);
-
- let status = ctx.gl.check_framebuffer_status(glow::FRAMEBUFFER);
- if status != glow::FRAMEBUFFER_COMPLETE {
- panic!("error initializing framebuffer:\n{}", status);
- }
-
- Self {
- tex: Some(tex),
- fbo: Some(fbo),
- dims: dims.clone(),
- offsets: offsets.clone(),
- }
- }
- }
-
- pub fn bind_texture(&self, ctx: &context::Context) {
- unsafe {
- ctx.gl.active_texture(glow::TEXTURE0);
- ctx.gl.bind_texture(glow::TEXTURE_2D, self.tex);
- }
- }
-
- pub fn bind(&self, ctx: &context::Context) {
- unsafe {
- ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, self.fbo);
- ctx.gl.viewport(
- self.offsets.x as _,
- self.offsets.y as _,
- self.dims.x as _,
- self.dims.y as _,
- );
- }
- }
-
- pub fn blit(&self, ctx: &context::Context, dest: &Self, pos: &glam::Vec2, scale: &glam::Vec2) {
- unsafe {
- ctx.gl.bind_framebuffer(glow::READ_FRAMEBUFFER, self.fbo);
- ctx.gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, dest.fbo);
- ctx.gl.blit_framebuffer(
- 0, 0, self.dims.x as _, self.dims.y as _,
- pos.x as _, pos.y as _, (pos.x + scale.x) as _, (pos.y + scale.y) as _,
- glow::COLOR_BUFFER_BIT, glow::NEAREST
- );
- }
- }
-
- pub fn get_pixels(&self, ctx: &context::Context, buf: &mut [glam::Vec3]) {
- let w = self.dims.x as usize;
- let h = self.dims.y as usize;
- let tmp = vec![glam::Vec3::default(); w * h];
- unsafe {
- ctx.gl.bind_texture(glow::TEXTURE_2D, self.tex);
- ctx.gl.get_tex_image(
- glow::TEXTURE_2D, 0, glow::RGB, glow::FLOAT,
- glow::PixelPackData::Slice(
- std::slice::from_raw_parts_mut(
- tmp.as_ptr() as *mut u8,
- tmp.len() * std::mem::size_of::<glam::Vec3>()
- )
- ),
- );
- }
- for y in 0..h {
- for x in 0..w {
- buf[x + (h - 1 - y) * w] = tmp[x + y * w];
- }
- }
- }
-}
diff --git a/src/helpers.js b/src/helpers.js
deleted file mode 100644
index aaaafa1..0000000
--- a/src/helpers.js
+++ /dev/null
@@ -1,13 +0,0 @@
-let resized = false;
-
-export async function js_track_resized_setup() {
- window.addEventListener("resize", () => {
- resized = true;
- });
-}
-
-export function js_poll_resized() {
- let ret = resized;
- resized = false;
- return ret;
-}
diff --git a/src/js/module.js b/src/js/module.js
deleted file mode 100644
index f7bff39..0000000
--- a/src/js/module.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export function js_build_interface() {
- return {
- env: {
- log_info: window.wasmBindings.log_info,
- },
- };
-}
diff --git a/src/level2d.rs b/src/level2d.rs
deleted file mode 100644
index 8b033ff..0000000
--- a/src/level2d.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod tiled;
diff --git a/src/level2d/tiled.rs b/src/level2d/tiled.rs
deleted file mode 100644
index 466260a..0000000
--- a/src/level2d/tiled.rs
+++ /dev/null
@@ -1,298 +0,0 @@
-use std::collections::HashMap;
-use serde::Deserialize;
-use glow::HasContext;
-
-use crate::{context, erm, mesh, shader, texture, Erm};
-
-#[derive(Debug)]
-pub enum Err {
- LayerIndexOutOfBounds,
- LayerDataTooSmall,
- GIDNotFound(u32),
- AssetNotFound(String),
- GL(String),
-}
-impl std::error::Error for Err {}
-impl std::fmt::Display for Err {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::LayerIndexOutOfBounds => write!(f, "layer index out of bounds"),
- Self::LayerDataTooSmall => write!(f, "layer data too small for dimensions"),
- Self::GIDNotFound(gid) => write!(f, "GID not found: {}", gid),
- Self::AssetNotFound(ass) => write!(f, "asset not found: {}", ass),
- Self::GL(msg) => write!(f, "GL error: {msg:}"),
- }
- }
-}
-
-#[derive(Debug, Deserialize)]
-pub enum LayerType {
- #[serde(rename = "tilelayer")]
- Tile,
- #[serde(rename = "imagelayer")]
- Image,
- #[serde(rename = "objectgroup")]
- ObjectGroup,
- #[serde(rename = "group")]
- Group,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct Layer {
- name: String,
- id: i32,
- #[serde(rename = "type")] ty: LayerType,
- width: i32, height: i32,
- x: i32, y: i32,
- opacity: f32,
- visible: bool,
- data: Vec<u32>,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct LevelTileset {
- firstgid: i32,
- source: String,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct Level {
- width: i32, height: i32,
- tilewidth: i32, tileheight: i32,
- layers: Vec<Layer>,
- tilesets: Vec<LevelTileset>,
-}
-impl Level {
- pub fn new(bytes: &str) -> Erm<Self> {
- Ok(serde_json::from_str(bytes)?)
- }
-}
-
-#[derive(Debug, Deserialize)]
-pub struct Tileset {
- name: String,
- imagewidth: i32, imageheight: i32,
- tilewidth: i32, tileheight: i32,
- margin: i32, spacing: i32,
-}
-impl Tileset {
- pub fn new(bytes: &str) -> Erm<Self> {
- Ok(serde_json::from_str(bytes)?)
- }
-}
-
-// TODO
-pub enum Flip {
- None,
-}
-
-pub struct Asset {
- tileset: Tileset,
- texture: texture::Texture,
-}
-pub struct Assets {
- entries: HashMap<String, Asset>,
-}
-impl Assets {
- pub fn new() -> Self { Self { entries: HashMap::new() } }
- pub fn load(&mut self, ctx: &context::Context, nm: &str, ts: &str, img: &[u8]) -> Erm<()> {
- let ass = Asset {
- tileset: Tileset::new(ts)?,
- texture: texture::Texture::new(ctx, img),
- };
- if self.entries.insert(nm.to_string(), ass).is_some() {
- log::warn!("duplicate tileset entry named: {}", nm);
- }
- Ok(())
- }
- pub fn lookup_gid(&self, level: &Level, gid: u32) -> Erm<(i32, &Asset, Flip)> {
- let offset = (gid & 0x0fffffff) as i32;
- for lts in level.tilesets.iter().rev() {
- if lts.firstgid <= offset {
- return Ok((
- offset - lts.firstgid,
- self.entries.get(&lts.source).ok_or(Err::AssetNotFound(lts.source.clone()))?,
- Flip::None
- ))
- }
- }
- return erm(Err::GIDNotFound(gid));
- }
-}
-
-pub struct LayerRenderer {
- pub vao: glow::VertexArray,
- pub vertex_buf: glow::Buffer,
- pub texcoords_buf: glow::Buffer,
- pub index_buf: glow::Buffer,
- pub index_count: usize,
-}
-impl LayerRenderer {
- pub fn new(ctx: &context::Context) -> Erm<Self> {
- unsafe {
- let vao = ctx.gl.create_vertex_array().map_err(Err::GL)?;
- ctx.gl.bind_vertex_array(Some(vao));
- let vertex_buf = ctx.gl.create_buffer().map_err(Err::GL)?;
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buf));
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_VERTEX, 2, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_VERTEX);
- let texcoords_buf = ctx.gl.create_buffer().map_err(Err::GL)?;
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(texcoords_buf));
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_TEXCOORD, 2, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_TEXCOORD);
- let index_buf = ctx.gl.create_buffer().map_err(Err::GL)?;
- ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buf));
- Ok(Self {
- vao,
- vertex_buf,
- texcoords_buf,
- index_buf,
- index_count: 0,
- })
- }
- }
-}
-pub struct LevelRenderer {
- pub layers: Vec<LayerRenderer>,
- pub shader: shader::Shader,
-}
-impl LevelRenderer {
- pub fn new(ctx: &context::Context, level: &Level) -> Erm<Self> {
- let mut layers = Vec::new();
- for _ in level.layers.iter() {
- layers.push(LayerRenderer::new(ctx)?);
- }
- let shader = shader::Shader::new_nolib(
- &ctx,
- include_str!("../assets/shaders/tiled/vert.glsl"),
- include_str!("../assets/shaders/tiled/frag.glsl"),
- );
- Ok(Self {
- layers,
- shader,
- })
- }
- pub fn populate_layer(
- &mut self,
- ctx: &context::Context,
- assets: &Assets,
- level: &Level,
- lidx: usize
- ) -> Erm<()> {
- let lr = self.layers.get_mut(lidx).ok_or(Err::LayerIndexOutOfBounds)?;
- let layer = level.layers.get(lidx).ok_or(Err::LayerIndexOutOfBounds)?;
- let mut vertices = Vec::new();
- let mut texcoords = Vec::new();
- let mut indices = Vec::new();
- for y in 0..layer.height {
- for x in 0..layer.width {
- let idx = x as usize + (y * layer.width) as usize;
- let gid = *layer.data.get(idx).ok_or(Err::LayerDataTooSmall)?;
- if gid == 0 { continue; }
- let (lid, ass, _) = assets.lookup_gid(level, gid)?;
- let cols = ass.tileset.imagewidth / ass.tileset.tilewidth;
- let rows = ass.tileset.imageheight / ass.tileset.tileheight;
- let col = lid % cols;
- let row = lid / cols;
- let twidth = 1.0 / cols as f32;
- let theight = 1.0 / rows as f32;
-
- let i = vertices.len() as u32;
- let v = glam::Vec2::new(x as _, y as _);
- vertices.push(v);
- vertices.push(v + glam::Vec2::new(1.0, 0.0));
- vertices.push(v + glam::Vec2::new(1.0, 1.0));
- vertices.push(v + glam::Vec2::new(0.0, 1.0));
- let uvbase = glam::Vec2::new(col as f32 / cols as f32, row as f32 / rows as f32);
- texcoords.push(uvbase + glam::Vec2::new(0.0, theight));
- texcoords.push(uvbase + glam::Vec2::new(twidth, theight));
- texcoords.push(uvbase + glam::Vec2::new(twidth, 0.0));
- texcoords.push(uvbase);
- indices.push(i + 0); indices.push(i + 1); indices.push(i + 2);
- indices.push(i + 0); indices.push(i + 3); indices.push(i + 2);
- }
- }
- let index_bytes: Vec<u8> = indices.iter().flat_map(|x| x.to_ne_bytes()).collect();
- unsafe {
- ctx.gl.bind_vertex_array(Some(lr.vao));
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(lr.vertex_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- vertices.as_ptr() as _,
- vertices.len() * std::mem::size_of::<f32>() * 2,
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(lr.texcoords_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- texcoords.as_ptr() as _,
- texcoords.len() * std::mem::size_of::<f32>() * 2,
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(lr.index_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ELEMENT_ARRAY_BUFFER,
- &index_bytes,
- glow::STATIC_DRAW,
- );
- lr.index_count = indices.len();
- }
- Ok(())
- }
- pub fn render_layer(
- &self,
- ctx: &context::Context,
- assets: &Assets,
- level: &Level,
- lidx: usize,
- ) -> Erm<()> {
- let layer = level.layers.get(lidx).ok_or(Err::LayerIndexOutOfBounds)?;
- let lr = self.layers.get(lidx).ok_or(Err::LayerIndexOutOfBounds)?;
- // TODO: handle layers with multiple textures
- let gid = *layer.data.iter().find(|g| **g > 0).unwrap();
- let (_, ass, _) = assets.lookup_gid(level, gid)?;
- ass.texture.bind(ctx);
- unsafe {
- ctx.gl.bind_vertex_array(Some(lr.vao));
- ctx.gl.draw_elements(glow::TRIANGLES, lr.index_count as _, glow::UNSIGNED_INT, 0);
- }
- Ok(())
- }
- pub fn populate(
- &mut self,
- ctx: &context::Context,
- assets: &Assets,
- level: &Level,
- ) -> Erm<()> {
- for lidx in 0..level.layers.len() {
- self.populate_layer(ctx, assets, level, lidx)?;
- }
- Ok(())
- }
- pub fn render(
- &self,
- ctx: &context::Context,
- assets: &Assets,
- level: &Level,
- ) -> Erm<()> {
- self.shader.bind(ctx);
- let sx = 2.0 * level.tilewidth as f32 / ctx.render_width;
- let sy = 2.0 * level.tileheight as f32 / ctx.render_height;
- self.shader.set_mat4(
- ctx, "transform",
- &glam::Mat4::from_scale_rotation_translation(
- glam::Vec3::new(sx, -sy, 1.0),
- glam::Quat::IDENTITY,
- glam::Vec3::new(0.0, 0.0, 0.0),
- ),
- );
- for lidx in 0..level.layers.len() {
- self.render_layer(ctx, assets, level, lidx)?;
- }
- Ok(())
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 916a405..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,328 +0,0 @@
-pub mod utils;
-pub mod ui;
-pub mod context;
-pub mod state;
-pub mod framebuffer;
-pub mod shader;
-pub mod mesh;
-pub mod texture;
-pub mod scene;
-pub mod font;
-pub mod shadow;
-pub mod audio;
-pub mod net;
-pub mod physics;
-pub mod save;
-pub mod level2d;
-
-pub use utils::{erm, install_error_handler, Erm};
-pub use color_eyre::eyre::WrapErr;
-
-#[cfg(target_arch = "wasm32")]
-use winit::platform::web::EventLoopExtWebSys;
-
-#[cfg(target_arch = "wasm32")]
-use winit::platform::web::WindowExtWebSys;
-
-#[cfg(target_arch = "wasm32")]
-use wasm_bindgen::JsCast;
-
-#[cfg(not(target_arch = "wasm32"))]
-use glfw::Context;
-
-use bitflags::bitflags;
-bitflags! {
- pub struct Options: u32 {
- const OVERLAY = 0b00000001;
- const HIDDEN = 0b00000010;
- const NORESIZE = 0b00000100;
- }
-}
-
-static mut CTX: Option<*const context::Context> = None;
-static mut ST: Option<*mut state::State> = None;
-static mut G: Option<*mut std::ffi::c_void> = None;
-
-pub fn contextualize<F, G, X>(mut f: F) -> X
-where
- G: state::Game + 'static,
- F: FnMut(&context::Context, &mut state::State, &mut G) -> X {
- unsafe {
- match (CTX, ST, G) {
- (Some(c), Some(s), Some(g)) => f(&*c, &mut*s, &mut*(g as *mut G)),
- _ => panic!("context not set"),
- }
- }
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub fn run<'a, F, G>(title: &str, w: u32, h: u32, options: Options, gnew: F) -> Erm<()>
-where
- G: state::Game + 'static,
- F: (Fn(&'a context::Context) -> G),
-{
- env_logger::Builder::new()
- .filter(None, log::LevelFilter::Info)
- .init();
- install_error_handler();
-
- log::info!("hello computer, starting up...");
-
- let resize = !options.contains(Options::NORESIZE);
- let (rglfw, rwindow, gl, events) = {
- use glfw::fail_on_errors;
- let mut glfw = glfw::init(glfw::fail_on_errors!()).expect("failed to initialize GLFW");
- // let gl_attr = video.gl_attr();
- // gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
- // gl_attr.set_context_version(3, 0);
- let (mut window, events) = glfw.with_primary_monitor(|glfw, primary| {
- if options.contains(Options::HIDDEN) {
- glfw.window_hint(glfw::WindowHint::Visible(false));
- glfw.create_window(w as _, h as _, title, glfw::WindowMode::Windowed)
- .expect("failed to create window")
- } else if options.contains(Options::OVERLAY) {
- let mon = primary.expect("failed to get monitor");
- let mode = mon.get_video_mode().expect("failed to get video mode");
- glfw.window_hint(glfw::WindowHint::RedBits(Some(mode.red_bits)));
- glfw.window_hint(glfw::WindowHint::GreenBits(Some(mode.green_bits)));
- glfw.window_hint(glfw::WindowHint::BlueBits(Some(mode.blue_bits)));
- glfw.window_hint(glfw::WindowHint::RefreshRate(Some(mode.refresh_rate)));
- glfw.window_hint(glfw::WindowHint::Resizable(false));
- glfw.window_hint(glfw::WindowHint::Decorated(false));
- glfw.window_hint(glfw::WindowHint::Floating(true));
- glfw.window_hint(glfw::WindowHint::TransparentFramebuffer(true));
- unsafe {
- // glfw.window_hint(glfw::WindowHint::MousePassthrough(true));
- glfw::ffi::glfwWindowHint(0x0002000D, 1); // mouse passthrough
- }
- glfw.create_window(mode.width, mode.height, title, glfw::WindowMode::FullScreen(mon))
- .expect("failed to create window")
- } else {
- glfw.create_window(w as _, h as _, title, glfw::WindowMode::Windowed)
- .expect("failed to create window")
- }
- });
- window.make_current();
- window.set_key_polling(true);
- window.set_mouse_button_polling(true);
- window.set_size_polling(true);
- window.set_focus_polling(true);
- window.set_cursor_pos_polling(true);
- let gl = unsafe {
- glow::Context::from_loader_function(|s| window.get_proc_address(s) as *const _)
- };
- glfw.set_swap_interval(glfw::SwapInterval::Sync(1));
- (glfw, window, gl, events)
- };
- let glfw = std::cell::RefCell::new(rglfw);
- let window = std::cell::RefCell::new(rwindow);
-
- let ctx = Box::leak(Box::new(context::Context::new(
- glfw, window, gl,
- w as f32, h as f32, resize,
- )));
- let game = Box::leak(Box::new(gnew(ctx)));
- let st = Box::leak(Box::new(state::State::new(&ctx)));
-
- unsafe {
- CTX = Some(ctx as _);
- ST = Some(st as _);
- G = Some(game as *mut G as *mut std::ffi::c_void);
- }
-
- game.initialize(ctx, st)?;
- 'running: loop {
- if ctx.window.borrow().should_close() {
- game.finalize(ctx, st)?;
- log::info!("bye!");
- break 'running;
- }
- ctx.glfw.borrow_mut().poll_events();
- for (_, event) in glfw::flush_messages(&events) {
- match event {
- glfw::WindowEvent::Size(_, _) => st.handle_resize(&ctx),
- glfw::WindowEvent::Focus(false) => {
- st.keys = state::Keys::new();
- },
- glfw::WindowEvent::CursorPos(x, y) => {
- st.mouse_moved(&ctx, x as f32, y as f32, game);
- }
- glfw::WindowEvent::MouseButton(_, glfw::Action::Press, _) => {
- st.mouse_pressed(&ctx, game)
- },
- glfw::WindowEvent::MouseButton(_, glfw::Action::Release, _) => {
- st.mouse_released(&ctx)
- },
- glfw::WindowEvent::Key(key, _, glfw::Action::Press, _) => {
- st.key_pressed(&ctx, state::Keycode::new(key))
- },
- glfw::WindowEvent::Key(key, _, glfw::Action::Release, _) => {
- st.key_released(&ctx, state::Keycode::new(key))
- },
- _ => {},
- }
- }
- if ctx.resize_necessary() {
- st.handle_resize(&ctx);
- }
- if let Some(f) = &mut st.request {
- match std::future::Future::poll(f.as_mut(), &mut st.waker_ctx) {
- std::task::Poll::Pending => {},
- std::task::Poll::Ready(res) => {
- st.request = None;
- match res {
- Ok(r) => st.request_returned(&ctx, game, r),
- Err(e) => log::warn!("error during HTTP request: {}", e),
- }
- },
- }
- }
- st.run_update(&ctx, game)?;
- st.run_render(&ctx, game)?;
- ctx.window.borrow_mut().swap_buffers();
- }
- Ok(())
-}
-
-#[cfg(target_arch = "wasm32")]
-pub fn run<'a, F, G>(w: u32, h: u32, options: Options, gnew: F)
-where
- G: state::Game + 'static,
- F: (Fn(&'a context::Context) -> G),
-{
- console_log::init_with_level(log::Level::Debug).unwrap();
- console_error_panic_hook::set_once();
- tracing_wasm::set_as_global_default();
- // install_error_handler();
-
- log::info!("hello computer, starting up...");
-
- let event_loop = winit::event_loop::EventLoop::new()
- .expect("failed to initialize event loop");
-
- let resize = !options.contains(Options::NORESIZE);
- let (window, gl) = {
- let window = winit::window::WindowBuilder::new()
- .with_maximized(resize)
- .with_decorations(false)
- .build(&event_loop)
- .expect("failed to initialize window");
- let gl = web_sys::window()
- .and_then(|win| win.document())
- .and_then(|doc| {
- let dst = doc.get_element_by_id("teleia-parent")?;
- let canvas = web_sys::Element::from(window.canvas().expect("failed to find canvas"));
- dst.append_child(&canvas).ok()?;
- let c = canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()?;
- let webgl2_context = c.get_context("webgl2").ok()??
- .dyn_into::<web_sys::WebGl2RenderingContext>().ok()?;
- Some(glow::Context::from_webgl2_context(webgl2_context))
- })
- .expect("couldn't add canvas to document");
- (window, gl)
- };
-
- let ctx = Box::leak(Box::new(context::Context::new(window, gl, w as f32, h as f32, resize)));
- ctx.maximize_canvas();
- let game = Box::leak(Box::new(gnew(ctx)));
- let st = Box::leak(Box::new(state::State::new(&ctx)));
-
- unsafe {
- CTX = Some(ctx as _);
- ST = Some(st as _);
- G = Some(game as *mut G as *mut std::ffi::c_void);
- }
-
- let _ = game.initialize(ctx, st);
- let res = std::rc::Rc::new(std::cell::RefCell::new(Ok(())));
- let result = res.clone();
- event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
- event_loop.spawn(move |event, elwt| {
- let res: Erm<()> = contextualize(|ctx, st, game: &mut G| {
- match &event {
- winit::event::Event::WindowEvent {
- event: wev,
- window_id,
- ..
- } => match wev {
- winit::event::WindowEvent::CloseRequested
- if *window_id == ctx.window.id() => elwt.exit(),
- winit::event::WindowEvent::Resized{..} => {
- #[cfg(target_arch = "wasm32")]
- ctx.maximize_canvas();
- st.handle_resize(&ctx);
- },
- winit::event::WindowEvent::Focused(false) => {
- st.keys = state::Keys::new();
- },
- winit::event::WindowEvent::CursorMoved { position, ..} => {
- st.mouse_moved(&ctx, position.x as f32, position.y as f32, game);
- },
- winit::event::WindowEvent::MouseInput {
- state,
- ..
- } => match state {
- winit::event::ElementState::Pressed => {
- st.mouse_pressed(&ctx, game)
- },
- winit::event::ElementState::Released => {
- st.mouse_released(&ctx)
- },
- }
- winit::event::WindowEvent::KeyboardInput {
- event: winit::event::KeyEvent {
- physical_key: winit::keyboard::PhysicalKey::Code(key),
- state,
- repeat: false,
- ..
- },
- ..
- } => match state {
- winit::event::ElementState::Pressed => {
- st.key_pressed(&ctx, *key)
- },
- winit::event::ElementState::Released => {
- st.key_released(&ctx, *key)
- },
- }
- _ => {},
- },
-
- winit::event::Event::AboutToWait => {
- if ctx.resize_necessary() {
- #[cfg(target_arch = "wasm32")]
- ctx.maximize_canvas();
- st.handle_resize(&ctx);
- }
- if let Some(f) = &mut st.request {
- match std::future::Future::poll(f.as_mut(), &mut st.waker_ctx) {
- std::task::Poll::Pending => {},
- std::task::Poll::Ready(res) => {
- st.request = None;
- match res {
- Ok(r) => st.request_returned(&ctx, game, r),
- Err(e) => log::warn!("error during HTTP request: {}", e),
- }
- },
- }
- // f.poll();
- }
- st.run_update(&ctx, game)?;
- st.run_render(&ctx, game)?;
- ctx.window.request_redraw();
- },
-
- _ => {},
- }
- Ok(())
- });
- if let Err(e) = res {
- *result.borrow_mut() = Err(e);
- elwt.exit();
- }
- });
- let _ = game.finalize(ctx, st);
- if let Err(e) = res.replace(Ok(())) {
- panic!("{}", e);
- }
-}
diff --git a/src/mesh.rs b/src/mesh.rs
deleted file mode 100644
index 7209de1..0000000
--- a/src/mesh.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-use glow::HasContext;
-
-use crate::context;
-
-pub const ATTRIB_VERTEX: u32 = 0;
-pub const ATTRIB_NORMAL: u32 = 1;
-pub const ATTRIB_TEXCOORD: u32 = 2;
-pub const ATTRIB_JOINT: u32 = 3;
-pub const ATTRIB_WEIGHT: u32 = 4;
-pub const ATTRIB_COLOR: u32 = 5;
-
-pub struct Mesh {
- pub vao: glow::VertexArray,
- pub mode: u32, // glow::TRIANGLES, etc.
- pub index_count: usize,
- pub index_type: u32, // glow::BYTE, glow::FLOAT, etc.
- pub index_offset: i32,
-}
-
-impl Mesh {
- pub fn build(
- ctx: &context::Context,
- vertices: &Vec<f32>,
- indices: &Vec<u32>,
- snormals: &Option<Vec<f32>>,
- stexcoords: &Option<Vec<f32>>,
- ) -> Self {
- unsafe {
- let vao = ctx.gl.create_vertex_array().expect("failed to initialize vao");
- ctx.gl.bind_vertex_array(Some(vao));
-
- let buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- vertices.as_ptr() as _,
- vertices.len() * std::mem::size_of::<f32>(),
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.vertex_attrib_pointer_f32(ATTRIB_VERTEX, 3, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(ATTRIB_VERTEX);
-
- let indices_vbo = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(indices_vbo));
- ctx.gl.buffer_data_u8_slice(
- glow::ELEMENT_ARRAY_BUFFER,
- std::slice::from_raw_parts(
- indices.as_ptr() as _,
- indices.len() * std::mem::size_of::<f32>(),
- ),
- glow::STATIC_DRAW,
- );
-
- if let Some(normals) = snormals {
- let normals_vbo = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(normals_vbo));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- normals.as_ptr() as _,
- normals.len() * std::mem::size_of::<f32>(),
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.vertex_attrib_pointer_f32(ATTRIB_NORMAL, 3, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(ATTRIB_NORMAL);
- }
-
- if let Some(texcoords) = stexcoords {
- let texcoords_vbo = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(texcoords_vbo));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- texcoords.as_ptr() as _,
- texcoords.len() * std::mem::size_of::<f32>(),
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.vertex_attrib_pointer_f32(ATTRIB_TEXCOORD, 2, glow::FLOAT, false, 0, 0);
- ctx.gl.enable_vertex_attrib_array(ATTRIB_TEXCOORD);
- }
-
- Self {
- vao,
- mode: glow::TRIANGLES,
- index_count: indices.len(),
- index_type: glow::UNSIGNED_INT,
- index_offset: 0,
- }
- }
- }
-
- pub fn from_obj(ctx: &context::Context, mut bytes: &[u8]) -> Self {
- let lopts = tobj::LoadOptions {
- triangulate: true,
- single_index: true,
- ..Default::default()
- };
- let (meshes, _materials) = tobj::load_obj_buf(
- &mut bytes,
- &lopts,
- |_| Err(tobj::LoadError::GenericFailure)
- ).expect("failed to load mesh");
- let mesh = meshes.into_iter().next()
- .expect("failed to load mesh")
- .mesh;
- Self::build(
- ctx,
- &mesh.positions,
- &mesh.indices,
- &Some(mesh.normals),
- &Some(mesh.texcoords),
- )
- }
-
- pub fn render(&self, ctx: &context::Context) {
- unsafe {
- ctx.gl.bind_vertex_array(Some(self.vao));
- ctx.gl.draw_elements(self.mode, self.index_count as _, self.index_type, self.index_offset);
- }
- }
-
- pub fn render_instanced(&self, ctx: &context::Context, count: u64) {
- unsafe {
- ctx.gl.bind_vertex_array(Some(self.vao));
- ctx.gl.draw_elements_instanced(self.mode, self.index_count as _, self.index_type, self.index_offset, count as _);
- }
- }
-}
diff --git a/src/net.rs b/src/net.rs
deleted file mode 100644
index b9babe5..0000000
--- a/src/net.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod client;
diff --git a/src/net/client.rs b/src/net/client.rs
deleted file mode 100644
index fcd51b8..0000000
--- a/src/net/client.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-#[cfg(target_arch = "wasm32")]
-pub mod wasm;
diff --git a/src/net/client/wasm.rs b/src/net/client/wasm.rs
deleted file mode 100644
index d9936e5..0000000
--- a/src/net/client/wasm.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-use std::{collections::VecDeque, sync::{Arc, Mutex}};
-
-use wasm_bindgen::prelude::*;
-
-#[derive(Debug)]
-pub enum Message {
- Binary(Vec<u8>),
- Text(String),
-}
-
-impl Message {
- pub fn from_messageevent(e: web_sys::MessageEvent) -> Self {
- if let Ok(abuf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
- let array = js_sys::Uint8Array::new(&abuf).to_vec();
- Message::Binary(array)
- } else if let Ok(txt) = e.data().dyn_into::<js_sys::JsString>() {
- Message::Text(txt.into())
- } else {
- panic!("received weird websocked message: {:?}", e);
- }
- }
-}
-
-pub struct Client {
- pub ws: Option<web_sys::WebSocket>,
- pub messages: Arc<Mutex<VecDeque<Message>>>,
-}
-
-impl Client {
- pub fn new() -> Self {
- Self {
- ws: None,
- messages: Arc::new(Mutex::new(VecDeque::new())),
- }
- }
- pub fn connect(&mut self, url: &str) {
- let ws = web_sys::WebSocket::new(url).expect("failed to open websocket");
- let messages_ref = self.messages.clone();
- let cb: Closure<dyn Fn(web_sys::MessageEvent)> = Closure::new(move |e: web_sys::MessageEvent| {
- let msg = Message::from_messageevent(e);
- log::info!("incoming: {:?}", msg);
- messages_ref.lock().unwrap().push_back(msg);
- });
- ws.set_onmessage(Some(cb.as_ref().unchecked_ref()));
- cb.forget();
- self.ws = Some(ws);
- }
- pub fn poll(&mut self) -> Option<Message> {
- self.messages.lock().unwrap().pop_front()
- }
- pub fn send(&self, msg: Message) {
- if let Some(ws) = &self.ws {
- match msg {
- Message::Text(txt) => {
- if let Err(e) = ws.send_with_str(&txt) {
- log::warn!("failed to send string: {:?}", e);
- }
- },
- Message::Binary(bytes) => {
- if let Err(e) = ws.send_with_u8_array(&bytes) {
- log::warn!("failed to send bytes: {:?}", e);
- }
- },
- }
- }
- }
-}
diff --git a/src/physics.rs b/src/physics.rs
deleted file mode 100644
index e69de29..0000000
--- a/src/physics.rs
+++ /dev/null
diff --git a/src/save.rs b/src/save.rs
deleted file mode 100644
index 3f244cc..0000000
--- a/src/save.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-#[cfg(target_arch = "wasm32")]
-pub fn save<W>(id: &str, data: &W) where W: serde::Serialize {
- let window = web_sys::window().expect("failed to get window object");
- let storage = window.local_storage()
- .expect("failed to get local storage")
- .expect("local storage not present");
- let key = format!("{}_save", id);
- let val = serde_json::to_string(data).expect("failed to serialize save");
- storage.set_item(&key, &val).expect("failed to set save");
-}
-
-#[cfg(target_arch = "wasm32")]
-pub fn load<W>(id: &str) -> Option<W> where W: serde::de::DeserializeOwned {
- let window = web_sys::window().expect("failed to get window object");
- let storage = window.local_storage()
- .expect("failed to get local storage")
- .expect("local storage not present");
- let key = format!("{}_save", id);
- let s = storage.get_item(&key).expect("failed to get save").expect("save not present");
- let mut cur = std::io::Cursor::new(s);
- serde_json::from_reader(&mut cur).ok()
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub fn save<W>(id: &str, data: &W) where W: serde::Serialize {
- let pd = directories::ProjectDirs::from("", "milkfat", id).expect("failed to get save directory");
- let _ = std::fs::create_dir_all(pd.data_dir());
- let path = pd.data_dir().join("teleia.save");
- let mut file = std::fs::File::create(&path).expect("failed to open save file");
- // serde_json::to_writer(file, data).expect("failed to write save file");
- bincode::serde::encode_into_std_write(data, &mut file, bincode::config::standard())
- .expect("failed to write save file");
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub fn load<W>(id: &str) -> Option<W> where W: serde::de::DeserializeOwned {
- let pd = directories::ProjectDirs::from("", "milkfat", id).expect("failed to get save directory");
- let _ = std::fs::create_dir_all(pd.data_dir());
- let path = pd.data_dir().join("teleia.save");
- let mut file = std::fs::File::open(&path).ok()?;
- // serde_json::from_reader(file).ok()
- bincode::serde::decode_from_std_read(&mut file, bincode::config::standard()).ok()
-}
diff --git a/src/scene.rs b/src/scene.rs
deleted file mode 100644
index 3146b5d..0000000
--- a/src/scene.rs
+++ /dev/null
@@ -1,398 +0,0 @@
-use std::{collections::{HashMap, VecDeque}, mem::offset_of};
-
-use glow::HasContext;
-use image::EncodableLayout;
-
-use crate::{context, mesh, shader, texture};
-
-pub type Index = usize;
-
-pub struct Primitive {
- pub mesh: mesh::Mesh,
- pub material: Index,
-}
-
-pub struct Object {
- pub primitives: Vec<Primitive>,
-}
-
-pub struct Material {
- pub base_color_factor: glam::Vec4,
- pub base_color_texture: Option<Index>,
-
- pub metallic_factor: f32,
- pub roughness_factor: f32,
- pub metallic_roughness_texture: Option<Index>,
-
- pub normal_texture: Option<Index>,
-
- pub occlusion_texture: Option<Index>,
-
- pub emissive_factor: glam::Vec3,
- pub emissive_texture: Option<Index>,
-}
-
-pub struct Skin {
- pub inverse_bind_matrices: Vec<glam::Mat4>,
- pub joints: Vec<Index>,
-}
-
-pub enum ChannelValues {
- Translation(Vec<glam::Vec3>),
- Rotation(Vec<glam::Quat>),
- Scale(Vec<glam::Vec3>),
-}
-
-pub enum Interpolation {
- Linear,
- Step,
- CubicSpline,
-}
-
-pub struct Channel {
- pub target: Index,
- pub interpolation: Interpolation,
- pub keyframes: Vec<f32>,
- pub values: ChannelValues,
-}
-
-pub struct Animation {
- pub channels: Vec<Channel>,
-}
-
-pub struct Node {
- pub children: Vec<Index>,
- pub object: Option<Index>,
- pub skin: Option<Index>,
- pub transform: glam::Mat4,
-}
-
-pub struct Scene {
- pub objects: Vec<Object>,
- pub textures: Vec<texture::Texture>,
- pub materials: Vec<Material>,
- pub skins: Vec<Skin>,
- pub animations: HashMap<String, Animation>,
- pub nodes: Vec<Node>,
- pub nodes_by_name: HashMap<String, Index>,
- pub scene_nodes: Vec<Index>,
-}
-
-impl Scene {
- pub fn load_default_shader(ctx: &context::Context) -> shader::Shader {
- shader::Shader::new(
- ctx,
- include_str!("assets/shaders/scene/vert.glsl"),
- include_str!("assets/shaders/scene/frag.glsl")
- )
- }
- pub fn from_gltf(ctx: &context::Context, bytes: &[u8]) -> Self {
- let (gltf, buffers, images) = gltf::import_slice(bytes).expect("failed to parse GLTF");
- let get_buffer_data = |b: gltf::Buffer| {
- buffers.get(b.index()).map(|gltf::buffer::Data(bytes)| bytes.as_slice())
- };
- let objects = gltf.meshes().map(|m| {
- let primitives = m.primitives().filter_map(|p| {
- let mode = match p.mode() {
- gltf::mesh::Mode::Points => glow::POINTS,
- gltf::mesh::Mode::Lines => glow::LINES,
- gltf::mesh::Mode::LineLoop => glow::LINE_LOOP,
- gltf::mesh::Mode::LineStrip => glow::LINE_STRIP,
- gltf::mesh::Mode::Triangles => glow::TRIANGLES,
- gltf::mesh::Mode::TriangleStrip => glow::TRIANGLE_STRIP,
- gltf::mesh::Mode::TriangleFan => glow::TRIANGLE_FAN,
- };
- unsafe {
- let vao = ctx.gl.create_vertex_array().expect("failed to initialize vao");
- ctx.gl.bind_vertex_array(Some(vao));
-
- // in the past, I've been lazy and just uploaded whole buffers to the GPU.
- // this is certainly not the right thing to do in general.
- // perhaps I am misunderstanding, but it feels like GLTF makes it pretty difficult to do
- // that in general, on account of things like sparse accessors.
- // instead, we'll use the gltf crate's handy "reader" abstraction to iterate over all of the
- // data in the buffers, assemble it ourselves, and then upload that.
- let reader = p.reader(get_buffer_data);
-
- // on to the actual vertex data.
- // this is the layout of a single vertex in the buffer we send to the GPU.
- struct Vertex {
- pos: glam::Vec3,
- normal: glam::Vec3,
- texcoord: glam::Vec2,
- joints: glam::Vec4,
- weights: glam::Vec4,
- }
-
- // vertices always have positions
- let mut vertices = Vec::new();
- for pos in reader.read_positions().expect("primitive has no positions") {
- vertices.push(Vertex {
- pos: glam::Vec3::from_array(pos),
- normal: glam::Vec3::default(),
- texcoord: glam::Vec2::default(),
- joints: glam::Vec4::default(),
- weights: glam::Vec4::default(),
- });
- }
-
- // if we find indices, use those. otherwise generate indices
- let indices: Vec<u32> = if let Some(ri) = reader.read_indices() {
- ri.into_u32().collect()
- } else {
- vertices.iter().enumerate().map(|(i, _)| i as u32).collect()
- };
- let indices_bytes: Vec<u8> = indices.iter().flat_map(|x| x.to_ne_bytes()).collect();
- let indices_buf = ctx.gl.create_buffer().expect("failed to create index buffer object");
- ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(indices_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ELEMENT_ARRAY_BUFFER,
- &indices_bytes,
- glow::STATIC_DRAW,
- );
-
- // optionally, we might have some other vertex attributes too
- if let Some(iter) = reader.read_normals() {
- for (i, n) in iter.enumerate() {
- vertices[i].normal = glam::Vec3::from_array(n)
- }
- }
- if let Some(iter) = reader.read_tex_coords(0) {
- for (i, uv) in iter.into_f32().enumerate() {
- vertices[i].texcoord = glam::Vec2::from_array(uv)
- }
- }
- if let Some(iter) = reader.read_joints(0) {
- for (i, j) in iter.into_u16().enumerate() {
- vertices[i].joints = glam::Vec4::from_slice(&j.into_iter().map(|x| x as f32).collect::<Vec<f32>>())
- }
- }
- if let Some(iter) = reader.read_weights(0) {
- for (i, w) in iter.into_f32().enumerate() {
- vertices[i].weights = glam::Vec4::from_array(w)
- }
- }
-
- let vertex_size = std::mem::size_of::<Vertex>() as i32;
- let vertices_buf = ctx.gl.create_buffer().expect("failed to create buffer object");
- ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertices_buf));
- ctx.gl.buffer_data_u8_slice(
- glow::ARRAY_BUFFER,
- std::slice::from_raw_parts(
- vertices.as_ptr() as _,
- vertices.len() * (vertex_size as usize),
- ),
- glow::STATIC_DRAW,
- );
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_VERTEX);
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_VERTEX, 3, glow::FLOAT, false, vertex_size, offset_of!(Vertex, pos) as _);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_NORMAL);
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_NORMAL, 3, glow::FLOAT, false, vertex_size, offset_of!(Vertex, normal) as _);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_TEXCOORD);
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_TEXCOORD, 2, glow::FLOAT, false, vertex_size, offset_of!(Vertex, texcoord) as _);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_JOINT);
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_JOINT, 4, glow::FLOAT, false, vertex_size, offset_of!(Vertex, joints) as _);
- ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_WEIGHT);
- ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_WEIGHT, 4, glow::FLOAT, false, vertex_size, offset_of!(Vertex, weights) as _);
-
-
- Some(Primitive {
- mesh: mesh::Mesh {
- vao,
- mode,
- index_count: indices.len(),
- index_type: glow::UNSIGNED_INT,
- index_offset: 0,
- },
- material: p.material().index().unwrap(),
- })
- }
- }).collect();
- Object {
- primitives,
- }
- }).collect();
- let textures: Vec<texture::Texture> = images.into_iter().map(|bi| {
- unsafe {
- let i = bi.image.into_rgba8();
- let tex = ctx.gl.create_texture().expect("failed to create texture");
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::NEAREST as i32);
- ctx.gl.tex_image_2d(
- glow::TEXTURE_2D,
- 0,
- glow::RGBA as i32,
- i.width() as i32,
- i.height() as i32,
- 0,
- glow::RGBA,
- glow::UNSIGNED_BYTE,
- Some(&i.as_bytes()),
- );
- ctx.gl.generate_mipmap(glow::TEXTURE_2D);
- texture::Texture { tex }
- }
- }).collect();
- let materials: Vec<Material> = gltf.materials().map(|m| {
- let pbr = m.pbr_metallic_roughness();
- let [bcr, bcg, bcb, bca] = pbr.base_color_factor();
- let [emx, emy, emz] = m.emissive_factor();
- Material {
- base_color_factor: glam::Vec4::new(bcr, bcg, bcb, bca),
- base_color_texture: pbr.base_color_texture().map(|tex| tex.texture().source().index()),
- metallic_factor: pbr.metallic_factor(),
- roughness_factor: pbr.roughness_factor(),
- metallic_roughness_texture: pbr.metallic_roughness_texture().map(|tex| tex.texture().source().index()),
- normal_texture: m.normal_texture().map(|tex| tex.texture().source().index()),
- occlusion_texture: m.occlusion_texture().map(|tex| tex.texture().source().index()),
- emissive_factor: glam::Vec3::new(emx, emy, emz),
- emissive_texture: m.emissive_texture().map(|tex| tex.texture().source().index()),
- }
- }).collect();
-
- let skins = gltf.skins().map(|s| {
- let ibm = s.reader(get_buffer_data).read_inverse_bind_matrices()
- .expect("missing read inverse bind matrices")
- .map(|m| glam::Mat4::from_cols_array_2d(&m)).collect();
- Skin {
- inverse_bind_matrices: ibm,
- joints: s.joints().map(|j| j.index()).collect(),
- }
- }).collect();
-
- let animations = HashMap::from_iter(gltf.animations().filter_map(|a| {
- let channels = a.channels().map(|c| {
- let read = c.reader(get_buffer_data);
- Channel {
- target: c.target().node().index(),
- interpolation: match c.sampler().interpolation() {
- gltf::animation::Interpolation::Linear => Interpolation::Linear,
- gltf::animation::Interpolation::Step => Interpolation::Step,
- gltf::animation::Interpolation::CubicSpline => Interpolation::CubicSpline,
- },
- keyframes: read.read_inputs().expect("channel has no inputs").collect(),
- values: match read.read_outputs().expect("channel has no outputs") {
- gltf::animation::util::ReadOutputs::Translations(ts) =>
- ChannelValues::Translation(ts.map(glam::Vec3::from_array).collect()),
- gltf::animation::util::ReadOutputs::Rotations(ts) =>
- ChannelValues::Rotation(ts.into_f32().map(glam::Quat::from_array).collect()),
- gltf::animation::util::ReadOutputs::Scales(ts) =>
- ChannelValues::Scale(ts.map(glam::Vec3::from_array).collect()),
- _ => panic!("unsupport channel outputs"),
- },
- }
- }).collect();
- a.name().map(|nm| (nm.to_owned(), Animation { channels }))
- }));
-
- let nodes = gltf.nodes().map(|n| {
- Node {
- children: n.children().map(|c| c.index()).collect(),
- object: n.mesh().map(|m| m.index()),
- skin: n.skin().map(|s| s.index()),
- transform: glam::Mat4::from_cols_array_2d(&n.transform().matrix()),
- }
- }).collect();
-
- let mut nodes_by_name = HashMap::new();
- for n in gltf.nodes() {
- if let Some(nm) = n.name() {
- nodes_by_name.insert(nm.to_owned(), n.index());
- }
- }
-
- let scene_nodes = gltf.default_scene().unwrap().nodes().map(|n| n.index()).collect();
-
- Self {
- objects,
- textures,
- materials,
- skins,
- animations,
- nodes,
- nodes_by_name,
- scene_nodes,
- }
- }
-
- pub fn compute_joint_matrices(&self, skin: &Skin) -> Vec<glam::Mat4> {
- let mut q: VecDeque<(Index, glam::Mat4)> = VecDeque::new();
- q.push_back((skin.joints[0], glam::Mat4::IDENTITY));
- let mut transforms = vec![glam::Mat4::IDENTITY; self.nodes.len()];
- while let Some((ni, m)) = q.pop_front() {
- let n = &self.nodes[ni];
- transforms[ni] = m.mul_mat4(&n.transform);
- for ci in &n.children {
- q.push_back((*ci, transforms[ni]));
- }
- }
- let mut ret = vec![glam::Mat4::IDENTITY; skin.joints.len()];
- for (idx, ni) in skin.joints.iter().enumerate() {
- ret[idx] = transforms[*ni].mul_mat4(&skin.inverse_bind_matrices[idx]);
- }
- ret
- }
-
- fn render_node(&self, ctx: &context::Context, shader: &shader::Shader, n: &Node) {
- if let Some(o) = n.object.and_then(|i| self.objects.get(i)) {
- if let Some(s) = n.skin.and_then(|i| self.skins.get(i)) {
- let jms = self.compute_joint_matrices(s);
- shader.set_mat4_array(ctx, "joint_matrices[0]", &jms);
- }
- for p in &o.primitives {
- if let Some(tex) = self.materials.get(p.material)
- .and_then(|m| m.base_color_texture)
- .and_then(|t| self.textures.get(t)) {
- tex.bind(ctx);
- }
- p.mesh.render(ctx);
- }
- }
- }
-
- pub fn render(&self, ctx: &context::Context, shader: &shader::Shader) {
- let mut q: VecDeque<Index> = VecDeque::new();
- for sn in &self.scene_nodes {
- q.push_back(*sn);
- }
- while let Some(ni) = q.pop_front() {
- let n = &self.nodes[ni];
- self.render_node(ctx, shader, n);
- for ci in &n.children {
- q.push_back(*ci);
- }
- }
- }
-
- pub fn reflect_animation(&mut self, nm: &str, time: f32) {
- if let Some(anim) = self.animations.get(nm) {
- for c in &anim.channels {
- let (previ, nexti) = if let Some(nexti) = c.keyframes.iter().position(|x| *x > time) {
- let previ = if nexti > 0 { nexti - 1 } else { c.keyframes.len() - 1 };
- (previ, nexti)
- } else {
- (c.keyframes.len() - 1, 0)
- };
- match &c.values {
- ChannelValues::Rotation(vs) => {
- let prevt = c.keyframes[previ];
- let nextt = c.keyframes[nexti];
- let prev = vs[previ];
- let next = vs[nexti];
- let new = prev.slerp(next, (time - prevt) / (nextt - prevt));
- let (scale, _, trans) = self.nodes[c.target].transform.to_scale_rotation_translation();
- self.nodes[c.target].transform = glam::Mat4::from_scale_rotation_translation(
- scale,
- new,
- trans,
- );
- },
- _ => {},
- }
- }
- }
- }
-}
diff --git a/src/shader.rs b/src/shader.rs
deleted file mode 100644
index 178833a..0000000
--- a/src/shader.rs
+++ /dev/null
@@ -1,253 +0,0 @@
-use std::collections::HashMap;
-
-use glow::HasContext;
-
-use crate::{context, mesh};
-
-const COMMON_VERT: &'static str = include_str!("assets/shaders/common/vert.glsl");
-const COMMON_FRAG: &'static str = include_str!("assets/shaders/common/frag.glsl");
-
-#[derive(Clone)]
-pub struct Shader {
- pub program: glow::Program,
- pub uniforms: std::rc::Rc<HashMap<String, glow::UniformLocation>>
-}
-
-impl Shader {
- pub fn new_helper(ctx: &context::Context, vsrc: &str, fsrc: &str) -> Result<Self, String> {
- unsafe {
- let program = ctx.gl.create_program()?;
-
- let vert = ctx.gl.create_shader(glow::VERTEX_SHADER)?;
- ctx.gl.shader_source(vert, &vsrc);
- ctx.gl.compile_shader(vert);
- if !ctx.gl.get_shader_compile_status(vert) {
- return Err(format!(
- "failed to compile vertex shader:\n{}",
- ctx.gl.get_shader_info_log(vert)
- ));
- }
- ctx.gl.attach_shader(program, vert);
-
- let frag = ctx.gl.create_shader(glow::FRAGMENT_SHADER)?;
- ctx.gl.shader_source(frag, &fsrc);
- ctx.gl.compile_shader(frag);
- if !ctx.gl.get_shader_compile_status(frag) {
- return Err(format!(
- "failed to compile fragment shader:\n{}",
- ctx.gl.get_shader_info_log(frag)
- ));
- }
- ctx.gl.attach_shader(program, frag);
-
- ctx.gl.bind_attrib_location(program, mesh::ATTRIB_VERTEX, "vertex");
- ctx.gl.bind_attrib_location(program, mesh::ATTRIB_NORMAL, "normal");
- ctx.gl.bind_attrib_location(program, mesh::ATTRIB_TEXCOORD, "texcoord");
- ctx.gl.bind_attrib_location(program, mesh::ATTRIB_JOINT, "joint");
- ctx.gl.bind_attrib_location(program, mesh::ATTRIB_WEIGHT, "weight");
- ctx.gl.bind_attrib_location(program, mesh::ATTRIB_COLOR, "color");
-
- ctx.gl.link_program(program);
- if !ctx.gl.get_program_link_status(program) {
- return Err(format!(
- "failed to link shader program:\n{}",
- ctx.gl.get_program_info_log(program),
- ));
- }
-
- ctx.gl.detach_shader(program, vert);
- ctx.gl.delete_shader(vert);
- ctx.gl.detach_shader(program, frag);
- ctx.gl.delete_shader(frag);
-
- ctx.gl.use_program(Some(program));
-
- let mut uniforms = HashMap::new();
- for index in 0..ctx.gl.get_active_uniforms(program) {
- if let Some(active) = ctx.gl.get_active_uniform(program, index) {
- if let Some(loc) = ctx.gl.get_uniform_location(program, &active.name) {
- uniforms.insert(active.name, loc);
- } else {
- log::warn!("failed to get location for uniform: {}", active.name);
- }
- } else {
- log::warn!("failed to get active uniform for index: {}", index);
- }
- }
-
- Ok(Self {
- program,
- uniforms: std::rc::Rc::new(uniforms),
- })
- }
- }
-
- pub fn new_nolib(ctx: &context::Context, vsrc: &str, fsrc: &str) -> Self {
- Self::new_helper(ctx, vsrc, fsrc).expect("failed to load shader")
- }
-
- pub fn new(ctx: &context::Context, vsrcstr: &str, fsrcstr: &str) -> Self {
- let vsrc = format!("{}\n{}\n", COMMON_VERT, vsrcstr);
- let fsrc = format!("{}\n{}\n", COMMON_FRAG, fsrcstr);
- Self::new_nolib(ctx, &vsrc, &fsrc)
- }
-
- pub fn delete(&mut self, ctx: &context::Context) {
- unsafe { ctx.gl.delete_program(self.program); }
- }
-
- pub fn replace(&mut self, ctx: &context::Context, vsrc: &str, fsrc: &str) -> Result<(), String> {
- self.delete(ctx);
- *self = Self::new_helper(ctx, vsrc, fsrc)?;
- Ok(())
- }
-
- pub fn set_i32(&self, ctx: &context::Context, name: &str, val: i32) {
- if let Some(loc) = self.uniforms.get(name) {
- unsafe { ctx.gl.uniform_1_i32(Some(loc), val) }
- }
- }
-
- pub fn set_i32_array(&self, ctx: &context::Context, name: &str, val: &[i32]) {
- if let Some(loc) = self.uniforms.get(name) {
- unsafe {
- ctx.gl.uniform_1_i32_slice(Some(loc), val)
- }
- }
- }
-
- pub fn set_f32(&self, ctx: &context::Context, name: &str, val: f32) {
- if let Some(loc) = self.uniforms.get(name) {
- unsafe { ctx.gl.uniform_1_f32(Some(loc), val) }
- }
- }
-
- pub fn set_vec2(&self, ctx: &context::Context, name: &str, val: &glam::Vec2) {
- if let Some(loc) = self.uniforms.get(name) {
- unsafe {
- ctx.gl.uniform_2_f32(
- Some(loc),
- val.x,
- val.y,
- );
- }
- }
- }
-
- pub fn set_vec2_array(&self, ctx: &context::Context, name: &str, val: &[glam::Vec2]) {
- if let Some(loc) = self.uniforms.get(name) {
- let vs: Vec<f32> = val.iter().flat_map(|v| [v.x, v.y]).collect();
- unsafe {
- ctx.gl.uniform_2_f32_slice(
- Some(loc),
- &vs,
- );
- }
- }
- }
-
- pub fn set_vec3(&self, ctx: &context::Context, name: &str, val: &glam::Vec3) {
- if let Some(loc) = self.uniforms.get(name) {
- unsafe {
- ctx.gl.uniform_3_f32(
- Some(loc),
- val.x,
- val.y,
- val.z,
- );
- }
- }
- }
-
- pub fn set_vec3_array(&self, ctx: &context::Context, name: &str, val: &[glam::Vec3]) {
- if let Some(loc) = self.uniforms.get(name) {
- let vs: Vec<f32> = val.iter().flat_map(|v| [v.x, v.y, v.z]).collect();
- unsafe {
- ctx.gl.uniform_3_f32_slice(
- Some(loc),
- &vs,
- );
- }
- }
- }
-
- pub fn set_vec4(&self, ctx: &context::Context, name: &str, val: &glam::Vec4) {
- if let Some(loc) = self.uniforms.get(name) {
- unsafe {
- ctx.gl.uniform_4_f32(
- Some(loc),
- val.x,
- val.y,
- val.z,
- val.w,
- );
- }
- }
- }
-
- pub fn set_mat4(&self, ctx: &context::Context, name: &str, val: &glam::Mat4) {
- if let Some(loc) = self.uniforms.get(name) {
- unsafe {
- ctx.gl.uniform_matrix_4_f32_slice(
- Some(loc),
- false,
- &val.to_cols_array(),
- );
- }
- }
- }
-
- pub fn set_mat4_array(&self, ctx: &context::Context, name: &str, val: &[glam::Mat4]) {
- if let Some(loc) = self.uniforms.get(name) {
- let vs: Vec<f32> = val.iter().flat_map(|m| m.to_cols_array()).collect();
- unsafe {
- ctx.gl.uniform_matrix_4_f32_slice(
- Some(loc),
- false,
- &vs,
- );
- }
- }
- }
-
- pub fn set_position_3d(&self, ctx: &context::Context, position: &glam::Mat4) {
- self.set_mat4(&ctx, "position", &position);
- self.set_mat4(&ctx, "normal_matrix", &position.inverse().transpose());
- }
-
- pub fn set_position_2d_helper(&self, ctx: &context::Context, pos: &glam::Vec2, dims: &glam::Vec2, rot: &glam::Quat) {
- let halfwidth = dims.x / 2.0;
- let halfheight = dims.y / 2.0;
- self.set_mat4(
- &ctx, "position",
- &glam::Mat4::from_scale_rotation_translation(
- glam::Vec3::new(halfwidth, halfheight, 1.0),
- rot.clone(),
- glam::Vec3::new(
- -ctx.render_width / 2.0 + pos.x + halfwidth,
- ctx.render_height / 2.0 - pos.y - halfheight,
- 0.0,
- ),
- )
- );
- }
-
- pub fn set_position_2d(&self, ctx: &context::Context, pos: &glam::Vec2, dims: &glam::Vec2) {
- self.set_position_2d_helper(ctx, pos, dims, &glam::Quat::IDENTITY)
- }
-
- pub fn set_texture_offset(&self, ctx: &context::Context, inc: i32, x: i32, y: i32) {
- let count = inc as f32;
- let ratio = 1.0 / count;
- self.set_vec3(
- ctx, "texture_offset",
- &glam::Vec3::new((x % inc) as f32 * ratio, (y % inc) as f32 * ratio, count)
- );
- }
-
- pub fn bind(&self, ctx: &context::Context) {
- unsafe {
- ctx.gl.use_program(Some(self.program));
- }
- }
-}
diff --git a/src/shadow.rs b/src/shadow.rs
deleted file mode 100644
index f827967..0000000
--- a/src/shadow.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-use glow::HasContext;
-
-use crate::context;
-
-pub struct ShadowBuffer {
- pub fbo: glow::Framebuffer,
- pub depth_tex: glow::Texture,
- pub width: i32,
- pub height: i32,
-}
-
-impl ShadowBuffer {
- pub fn new(ctx: &context::Context, w: i32, h: i32) -> Self {
- unsafe {
- // generate and bind FBO
- let fbo = ctx.gl.create_framebuffer().expect("failed to create framebuffer");
- ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo));
-
- // generate and attach depth buffer
- let depth_tex = ctx.gl.create_texture().expect("failed to create texture");
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(depth_tex));
- ctx.gl.tex_image_2d(
- glow::TEXTURE_2D,
- 0,
- glow::DEPTH_COMPONENT as i32,
- w,
- h,
- 0,
- glow::DEPTH_COMPONENT,
- glow::FLOAT,
- None,
- );
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_BORDER as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_BORDER as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_COMPARE_MODE, glow::COMPARE_REF_TO_TEXTURE as i32);
- ctx.gl.tex_parameter_f32_slice(glow::TEXTURE_2D, glow::TEXTURE_BORDER_COLOR, &[1.0, 1.0, 1.0, 1.0]);
- ctx.gl.framebuffer_texture_2d(glow::FRAMEBUFFER, glow::DEPTH_ATTACHMENT, glow::TEXTURE_2D, Some(depth_tex), 0);
- ctx.gl.draw_buffer(glow::NONE);
- ctx.gl.read_buffer(glow::NONE);
-
- let status = ctx.gl.check_framebuffer_status(glow::FRAMEBUFFER);
- if status != glow::FRAMEBUFFER_COMPLETE {
- panic!("error initializing framebuffer: {}", status);
- }
- Self {
- fbo,
- depth_tex,
- width: w,
- height: h,
- }
- }
- }
-
- pub fn bind(&self, ctx: &context::Context) {
- unsafe {
- ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo));
- ctx.gl.viewport(0, 0, self.width as _, self.height as _);
- ctx.gl.clear(glow::DEPTH_BUFFER_BIT);
-
- }
- }
-}
-
-pub struct ShadowBuffer3D {
- pub fbo: glow::Framebuffer,
- pub depth_cubemap: glow::Texture,
- pub width: i32,
- pub height: i32,
-}
-
-impl ShadowBuffer3D {
- pub fn new(ctx: &context::Context, w: i32, h: i32) -> Self {
- unsafe {
- // generate and bind FBO
- let fbo = ctx.gl.create_framebuffer().expect("failed to create framebuffer");
- ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo));
-
- // generate and attach depth buffer
- let depth_cubemap = ctx.gl.create_texture().expect("failed to create texture");
- ctx.gl.bind_texture(glow::TEXTURE_CUBE_MAP, Some(depth_cubemap));
- for i in 0..6 {
- ctx.gl.tex_image_2d(
- glow::TEXTURE_CUBE_MAP_POSITIVE_X + i,
- 0,
- glow::DEPTH_COMPONENT as i32,
- w,
- h,
- 0,
- glow::DEPTH_COMPONENT,
- glow::FLOAT,
- None,
- );
- ctx.gl.tex_parameter_i32(glow::TEXTURE_CUBE_MAP, glow::TEXTURE_MIN_FILTER, glow::LINEAR as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_CUBE_MAP, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_CUBE_MAP, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_CUBE_MAP, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_CUBE_MAP, glow::TEXTURE_WRAP_R, glow::CLAMP_TO_EDGE as i32);
- }
-
- ctx.gl.framebuffer_texture(glow::FRAMEBUFFER, glow::DEPTH_ATTACHMENT, Some(depth_cubemap), 0);
- ctx.gl.draw_buffer(glow::NONE);
- ctx.gl.read_buffer(glow::NONE);
-
- let status = ctx.gl.check_framebuffer_status(glow::FRAMEBUFFER);
- if status != glow::FRAMEBUFFER_COMPLETE {
- panic!("error initializing framebuffer: {}", status);
- }
- Self {
- fbo,
- depth_cubemap,
- width: w,
- height: h,
- }
- }
- }
-
- pub fn bind(&self, ctx: &context::Context) {
- unsafe {
- ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo));
- ctx.gl.viewport(0, 0, self.width as _, self.height as _);
- ctx.gl.clear(glow::DEPTH_BUFFER_BIT);
-
- }
- }
-}
diff --git a/src/state.rs b/src/state.rs
deleted file mode 100644
index 09f0f77..0000000
--- a/src/state.rs
+++ /dev/null
@@ -1,514 +0,0 @@
-#![allow(dead_code, unused_variables)]
-use std::collections::HashMap;
-use bimap::BiHashMap;
-use enum_map::{enum_map, Enum, EnumMap};
-use serde::{Serialize, Deserialize};
-
-use crate::{audio, context, framebuffer, shader, utils};
-
-const DELTA_TIME: f64 = 1.0 / 60.0;
-
-pub struct WinitWaker {}
-impl WinitWaker {
- fn new() -> Self { Self {} }
-}
-impl std::task::Wake for WinitWaker {
- fn wake(self: std::sync::Arc<Self>) {}
-}
-
-pub struct Response {
- pub url: String,
- pub status: reqwest::StatusCode,
- pub body: bytes::Bytes,
-}
-
-pub trait Game {
- fn initialize(&self, ctx: &context::Context, st: &State) -> utils::Erm<()> { Ok(()) }
- fn finalize(&self, ctx: &context::Context, st: &State) -> utils::Erm<()> { Ok(()) }
- fn initialize_audio(&self, ctx: &context::Context, st: &State, actx: &audio::Context) ->
- HashMap<String, audio::Audio>
- {
- HashMap::new()
- }
- fn finish_title(&mut self, st: &mut State) {}
- fn mouse_move(&mut self, ctx: &context::Context, st: &mut State, x: i32, y: i32) {}
- fn mouse_press(&mut self, ctx: &context::Context, st: &mut State) {}
- fn request_return(&mut self, ctx: &context::Context, st: &mut State, res: Response) {}
- fn update(&mut self, ctx: &context::Context, st: &mut State) -> utils::Erm<()> { Ok(()) }
- fn render(&mut self, ctx: &context::Context, st: &mut State) -> utils::Erm<()> { Ok(()) }
-}
-
-#[derive(Debug, Enum, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub enum Key {
- Up, Down, Left, Right,
- A, B, L, R,
- Start, Select,
-}
-pub const KEYS: [Key; 10] = [
- Key::Up, Key::Down, Key::Left, Key::Right,
- Key::A, Key::B, Key::L, Key::R,
- Key::Start, Key::Select,
-];
-pub struct Keys {
- pub pressed: EnumMap<Key, bool>,
- pub new: EnumMap<Key, bool>,
-}
-impl Keys {
- pub fn new() -> Self {
- Self {
- pressed: enum_map! {
- Key::Up => false, Key::Down => false, Key::Left => false, Key::Right => false,
- Key::A => false, Key::B => false, Key::L => false, Key::R => false,
- Key::Start => false, Key::Select => false,
- },
- new: enum_map! {
- Key::Up => false, Key::Down => false, Key::Left => false, Key::Right => false,
- Key::A => false, Key::B => false, Key::L => false, Key::R => false,
- Key::Start => false, Key::Select => false,
- },
- }
- }
- pub fn up(&self) -> bool { self.pressed[Key::Up] }
- pub fn down(&self) -> bool { self.pressed[Key::Down] }
- pub fn left(&self) -> bool { self.pressed[Key::Left] }
- pub fn right(&self) -> bool { self.pressed[Key::Right] }
- pub fn a(&self) -> bool { self.pressed[Key::A] }
- pub fn b(&self) -> bool { self.pressed[Key::B] }
- pub fn l(&self) -> bool { self.pressed[Key::L] }
- pub fn r(&self) -> bool { self.pressed[Key::R] }
- pub fn start(&self) -> bool { self.pressed[Key::Start] }
- pub fn select(&self) -> bool { self.pressed[Key::Select] }
- pub fn new_up(&mut self) -> bool { let ret = self.new[Key::Up]; self.new[Key::Up] = false; ret }
- pub fn new_down(&mut self) -> bool { let ret = self.new[Key::Down]; self.new[Key::Down] = false; ret }
- pub fn new_left(&mut self) -> bool { let ret = self.new[Key::Left]; self.new[Key::Left] = false; ret }
- pub fn new_right(&mut self) -> bool { let ret = self.new[Key::Right]; self.new[Key::Right] = false; ret }
- pub fn new_a(&mut self) -> bool { let ret = self.new[Key::A]; self.new[Key::A] = false; ret }
- pub fn new_b(&mut self) -> bool { let ret = self.new[Key::B]; self.new[Key::B] = false; ret }
- pub fn new_l(&mut self) -> bool { let ret = self.new[Key::L]; self.new[Key::L] = false; ret }
- pub fn new_r(&mut self) -> bool { let ret = self.new[Key::R]; self.new[Key::R] = false; ret }
- pub fn new_start(&mut self) -> bool { let ret = self.new[Key::Start]; self.new[Key::Start] = false; ret }
- pub fn new_select(&mut self) -> bool { let ret = self.new[Key::Select]; self.new[Key::Select] = false; ret }
-}
-
-pub struct PointLight {
- pub pos: glam::Vec3,
- pub color: glam::Vec3,
- pub attenuation: glam::Vec2,
-}
-
-type Timestamp = f64;
-
-#[cfg(target_arch = "wasm32")]
-pub type Keycode = winit::keyboard::KeyCode;
-
-#[cfg(not(target_arch = "wasm32"))]
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct Keycode {
- pub kc: glfw::Key
-}
-#[cfg(not(target_arch = "wasm32"))]
-impl Keycode {
- pub fn new(kc: glfw::Key) -> Self { Self { kc } }
-}
-#[cfg(not(target_arch = "wasm32"))]
-impl Serialize for Keycode {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where S: serde::Serializer {
- (self.kc as i32).serialize(serializer)
- }
-}
-#[cfg(not(target_arch = "wasm32"))]
-impl<'de> Deserialize<'de> for Keycode {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where D: serde::Deserializer<'de> {
- i32::deserialize(deserializer)
- .map(|x| unsafe {
- std::mem::transmute(x)
- })
- }
-}
-
-pub struct State {
- pub acc: f64,
- pub last: Timestamp,
- pub tick: u64,
-
- pub rebinding: Option<Key>,
- pub keybindings: BiHashMap<Keycode, Key>,
- pub keys: Keys,
-
- pub screen: framebuffer::Framebuffer,
- pub render_framebuffer: framebuffer::Framebuffer,
- pub shader_upscale: shader::Shader,
- pub audio: Option<audio::Assets>,
-
- pub projection: glam::Mat4,
- pub camera: (glam::Vec3, glam::Vec3, glam::Vec3),
- pub lighting: (glam::Vec3, glam::Vec3, glam::Vec3),
- pub point_lights: Vec<PointLight>,
-
- pub waker_ctx: std::task::Context<'static>,
- pub http_client: reqwest::Client,
- pub request: Option<std::pin::Pin<Box<dyn std::future::Future<Output = reqwest::Result<Response>>>>>,
-
- pub log: Vec<(u64, String)>,
-}
-
-#[cfg(target_arch = "wasm32")]
-pub fn now(ctx: &context::Context) -> Timestamp {
- ctx.performance.now() / 1000.0
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub fn now(ctx: &context::Context) -> Timestamp {
- let elapsed = ctx.start_instant.elapsed();
- let ms = elapsed.as_millis();
- (ms as f64) / 1000.0
-}
-
-#[cfg(target_arch = "wasm32")]
-pub fn default_keybindings() -> BiHashMap<Keycode, Key> {
- BiHashMap::from_iter(vec![
- (winit::keyboard::KeyCode::KeyW, Key::Up),
- (winit::keyboard::KeyCode::KeyS, Key::Down),
- (winit::keyboard::KeyCode::KeyA, Key::Left),
- (winit::keyboard::KeyCode::KeyD, Key::Right),
- (winit::keyboard::KeyCode::Digit1, Key::A),
- (winit::keyboard::KeyCode::Digit2, Key::B),
- (winit::keyboard::KeyCode::KeyQ, Key::L),
- (winit::keyboard::KeyCode::KeyE, Key::R),
- (winit::keyboard::KeyCode::Tab, Key::Start),
- (winit::keyboard::KeyCode::Space, Key::Select),
- ])
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-pub fn default_keybindings() -> BiHashMap<Keycode, Key> {
- BiHashMap::from_iter(vec![
- (Keycode::new(glfw::Key::W), Key::Up),
- (Keycode::new(glfw::Key::S), Key::Down),
- (Keycode::new(glfw::Key::A), Key::Left),
- (Keycode::new(glfw::Key::D), Key::Right),
- (Keycode::new(glfw::Key::Num1), Key::A),
- (Keycode::new(glfw::Key::Num2), Key::B),
- (Keycode::new(glfw::Key::Q), Key::L),
- (Keycode::new(glfw::Key::E), Key::R),
- (Keycode::new(glfw::Key::Tab), Key::Start),
- (Keycode::new(glfw::Key::Space), Key::Select),
- ])
-}
-
-impl State {
- pub fn new(ctx: &context::Context) -> Self {
- let screen = framebuffer::Framebuffer::screen(ctx);
- let render_framebuffer = framebuffer::Framebuffer::new(
- ctx,
- &glam::Vec2::new(ctx.render_width, ctx.render_height),
- &glam::Vec2::new(0.0, 0.0),
- );
- let shader_upscale = shader::Shader::new_nolib(
- ctx,
- include_str!("assets/shaders/scale/vert.glsl"),
- include_str!("assets/shaders/scale/frag.glsl"),
- );
-
- let waker = std::sync::Arc::new(WinitWaker::new());
- let cwaker = Box::leak(Box::new(waker.into()));
- let waker_ctx = std::task::Context::from_waker(cwaker);
-
- let acc = 0.0;
- let last = now(ctx);
-
- Self {
- acc,
- last,
- // we initialize the tick to 1000, which allows us to use "0" as the default time for
- // various animation starts on entities without having them all play at game start
- tick: 1000,
-
- rebinding: None,
- keybindings: default_keybindings(),
- keys: Keys::new(),
-
- screen,
- render_framebuffer,
- shader_upscale,
- audio: None,
-
- projection: glam::Mat4::perspective_lh(
- std::f32::consts::PI / 4.0,
- ctx.render_width / ctx.render_height,
- // 0.1,
- 0.5,
- 50.0,
- ),
- camera: (glam::Vec3::new(0.0, 0.0, 0.0), glam::Vec3::new(0.0, 0.0, 1.0), glam::Vec3::new(0.0, 1.0, 0.0)),
- lighting: (
- glam::Vec3::new(1.0, 1.0, 1.0),
- glam::Vec3::new(1.0, 1.0, 1.0),
- glam::Vec3::new(1.0, -1.0, 1.0),
- ),
- point_lights: Vec::new(),
-
- waker_ctx,
- http_client: reqwest::Client::new(),
- request: None,
-
- log: Vec::new(),
- }
- }
-
- pub fn write_log(&mut self, e: &str) {
- log::info!("log: {}", e.to_owned());
- self.log.push((self.tick, e.to_owned()));
- }
-
- pub fn handle_resize(&mut self, ctx: &context::Context) {
- self.screen = framebuffer::Framebuffer::screen(ctx);
- }
-
- pub fn move_camera(
- &mut self,
- _ctx: &context::Context,
- pos: &glam::Vec3,
- dir: &glam::Vec3,
- up: &glam::Vec3,
- ) {
- self.camera = (pos.clone(), dir.clone(), up.clone());
- }
-
- pub fn set_lighting(
- &mut self,
- _ctx: &context::Context,
- ambient: &glam::Vec3,
- color: &glam::Vec3,
- dir: &glam::Vec3,
- ) {
- self.lighting = (ambient.clone(), color.clone(), dir.clone());
- }
-
- pub fn add_point_light(
- &mut self,
- _ctx: &context::Context,
- pos: &glam::Vec3,
- color: &glam::Vec3,
- attenuation: &glam::Vec2,
- ) {
- self.point_lights.push(
- PointLight {
- pos: pos.clone(),
- color: color.clone(),
- attenuation: attenuation.clone(),
- },
- );
- }
-
- pub fn clear_point_lights(&mut self, _ctx: &context::Context) {
- self.point_lights.clear();
- }
-
- pub fn view(&self) -> glam::Mat4 {
- glam::Mat4::look_to_lh(
- self.camera.0,
- self.camera.1,
- self.camera.2,
- )
- }
-
- pub fn bind_3d_helper(&mut self, ctx: &context::Context, shader: &shader::Shader, plc: usize) {
- shader.bind(ctx);
- shader.set_mat4(ctx, "projection", &self.projection);
- shader.set_mat4(ctx, "view", &self.view());
- shader.set_vec3(
- ctx, "light_ambient_color",
- &self.lighting.0,
- );
- shader.set_vec3(
- ctx, "light_dir_color",
- &self.lighting.1,
- );
- shader.set_vec3(
- ctx, "light_dir",
- &self.lighting.2.normalize(),
- );
- shader.set_i32(
- ctx, &format!("light_count"),
- plc as _,
- );
- }
-
- pub fn bind_3d_no_point_lights(&mut self, ctx: &context::Context, shader: &shader::Shader) {
- self.bind_3d_helper(ctx, shader, 0);
- }
-
- pub fn bind_3d(&mut self, ctx: &context::Context, shader: &shader::Shader) {
- let plc = self.point_lights.len().min(5);
- self.bind_3d_helper(ctx, shader, plc);
- if plc > 0 {
- let lpos: Vec<_> = self.point_lights.iter().take(plc).map(|l| l.pos).collect();
- shader.set_vec3_array(
- ctx, &format!("light_pos[0]"),
- &lpos,
- );
- let lcolor: Vec<_> = self.point_lights.iter().take(plc).map(|l| l.color).collect();
- shader.set_vec3_array(
- ctx, &format!("light_color[0]"),
- &lcolor,
- );
- let lattenuation: Vec<_> = self.point_lights.iter().take(plc).map(|l| l.attenuation).collect();
- shader.set_vec2_array(
- ctx, &format!("light_attenuation[0]"),
- &lattenuation,
- );
- }
- }
-
- pub fn bind_2d(&mut self, ctx: &context::Context, shader: &shader::Shader) {
- shader.bind(ctx);
- shader.set_mat4(&ctx, "projection", &glam::Mat4::IDENTITY);
- shader.set_mat4(
- ctx, "view",
- &glam::Mat4::from_scale(
- glam::Vec3::new(
- 2.0 / ctx.render_width,
- 2.0 / ctx.render_height,
- 1.0,
- ),
- ),
- );
- }
-
- pub fn mouse_moved<G>(
- &mut self,
- ctx: &context::Context,
- x: f32, y: f32,
- game: &mut G
- ) where G: Game
- {
- let rx = ((x - self.screen.offsets.x) * ctx.render_width / self.screen.dims.x) as i32;
- let ry = ((y - self.screen.offsets.y) * ctx.render_height / self.screen.dims.y) as i32;
- if !(rx < 0 || rx >= ctx.render_width as i32 || ry < 0 || ry >= ctx.render_height as i32) {
- game.mouse_move(ctx, self, rx, ry);
- }
- }
-
- pub fn mouse_pressed<G>(
- &mut self,
- ctx: &context::Context,
- game: &mut G
- ) where G: Game {
- log::info!("click");
- if self.audio.is_none() {
- self.audio = Some(audio::Assets::new(|actx| {
- game.initialize_audio(ctx, &self, actx)
- }));
- game.finish_title(self);
- }
- game.mouse_press(ctx, self);
- }
-
- pub fn mouse_released(
- &mut self,
- _ctx: &context::Context,
- ) {
- }
-
- pub fn key_pressed(
- &mut self,
- _ctx: &context::Context,
- key: Keycode,
- ) {
- #[cfg(target_arch = "wasm32")]
- let rebind = key == winit::keyboard::KeyCode::F12;
- #[cfg(not(target_arch = "wasm32"))]
- let rebind = key.kc == glfw::Key::F12;
- if rebind {
- self.keybindings = default_keybindings();
- self.rebinding = None;
- self.write_log("Reset keybindings!");
- } else if let Some(k) = self.rebinding {
- self.keybindings.insert(key, k);
- self.rebinding = None;
- } else if let Some(k) = self.keybindings.get_by_left(&key) {
- self.keys.pressed[*k] = true;
- self.keys.new[*k] = true;
- }
- }
-
- pub fn key_released(
- &mut self,
- _ctx: &context::Context,
- key: Keycode,
- ) {
- if let Some(k) = self.keybindings.get_by_left(&key) {
- self.keys.pressed[*k] = false;
- }
- }
-
- /// Return the first keybinding for the given virtual key
- pub fn keybinding_for(&self, k: &Key) -> Option<String> {
- if let Some(kc) = self.keybindings.get_by_right(k) {
- Some(format!("{:?}", kc))
- } else {
- None
- }
- }
-
- pub fn rebind_key(&mut self, k: &Key) {
- self.rebinding = Some(*k);
- }
-
- pub fn request<F>(&mut self, f: F)
- where F: Fn(&reqwest::Client) -> reqwest::RequestBuilder
- {
- let builder = f(&self.http_client);
- let fut = async {
- let resp = builder.send().await?;
- let url = resp.url().clone().to_string();
- let status = resp.status().clone();
- let body = resp.bytes().await?;
- reqwest::Result::Ok(Response {
- url,
- status,
- body,
- })
- };
- self.request = Some(Box::pin(fut));
- }
-
- pub fn requesting(&self) -> bool { self.request.is_some() }
-
- pub fn request_returned<G>(&mut self, ctx: &context::Context, game: &mut G, res: Response)
- where G: Game
- {
- game.request_return(ctx, self, res);
- }
-
- pub fn run_update<G>(&mut self, ctx: &context::Context, game: &mut G) -> utils::Erm<()> where G: Game {
- let now = now(ctx);
- let diff = now - self.last;
-
- // update, if enough time has accumulated since last update
- if diff >= DELTA_TIME {
- self.last = now;
- self.tick += 1;
- game.update(ctx, self)?;
- self.acc = 0.0;
- }
- Ok(())
- }
-
- pub fn run_render<G>(&mut self, ctx: &context::Context, game: &mut G) -> utils::Erm<()> where G: Game {
- self.render_framebuffer.bind(&ctx);
-
- game.render(ctx, self)?;
-
- self.screen.bind(&ctx);
- ctx.clear_color(glam::Vec4::new(0.0, 0.0, 0.0, 0.0));
- ctx.clear();
- self.shader_upscale.bind(&ctx);
- self.render_framebuffer.bind_texture(&ctx);
- ctx.render_no_geometry();
- Ok(())
- }
-}
diff --git a/src/texture.rs b/src/texture.rs
deleted file mode 100644
index 68272cb..0000000
--- a/src/texture.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use glow::HasContext;
-use image::EncodableLayout;
-
-use crate::context;
-
-pub struct Texture {
- pub tex: glow::Texture,
-}
-
-impl Texture {
- pub fn new_empty(ctx: &context::Context) -> Self {
- unsafe {
- let tex = ctx.gl.create_texture().expect("failed to create texture");
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::NEAREST as i32);
- Self {
- tex,
- }
- }
- }
-
- pub fn new(ctx: &context::Context, bytes: &[u8]) -> Self {
- let rgba = image::io::Reader::new(std::io::Cursor::new(bytes))
- .with_guessed_format()
- .expect("failed to guess image format")
- .decode()
- .expect("failed to decode image")
- .into_rgba8();
- let pixels = rgba.as_bytes();
- unsafe {
- let tex = ctx.gl.create_texture().expect("failed to create texture");
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::NEAREST as i32);
- ctx.gl.tex_image_2d(
- glow::TEXTURE_2D,
- 0,
- glow::RGBA as i32,
- rgba.width() as i32,
- rgba.height() as i32,
- 0,
- glow::RGBA,
- glow::UNSIGNED_BYTE,
- Some(pixels),
- );
- ctx.gl.generate_mipmap(glow::TEXTURE_2D);
-
- Self {
- tex,
- }
- }
- }
-
- pub fn set_anisotropic_filtering(&self, ctx: &context::Context) {
- unsafe {
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(self.tex));
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::REPEAT as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::REPEAT as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR_MIPMAP_LINEAR as i32);
- ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32);
- ctx.gl.tex_parameter_f32(glow::TEXTURE_2D, glow::TEXTURE_MAX_ANISOTROPY_EXT, 4.0);
- }
- }
-
- pub fn bind(&self, ctx: &context::Context) {
- unsafe {
- ctx.gl.active_texture(glow::TEXTURE0);
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(self.tex));
- }
- }
-
- pub fn bind_index(&self, ctx: &context::Context, idx: u32) {
- unsafe {
- ctx.gl.active_texture(glow::TEXTURE0 + idx);
- ctx.gl.bind_texture(glow::TEXTURE_2D, Some(self.tex));
- }
- }
-}
diff --git a/src/ui.rs b/src/ui.rs
deleted file mode 100644
index 09afdf9..0000000
--- a/src/ui.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-use crate::utils;
-
-fn compute_reverse(frames: u64, tick: u64, start: u64) -> u64 {
- let leftover = frames - (tick - start)
- .clamp(0, frames);
- tick - leftover
-}
-
-pub enum ModeToggle {
- Inactive { start: u64 },
- Active { start: u64 },
-}
-
-pub struct Mode {
- frames: u64,
- toggle: ModeToggle,
- locked: bool,
-}
-
-impl Mode {
- pub fn new(frames: u64) -> Self {
- Self {
- frames,
- toggle: ModeToggle::Inactive { start: 0 },
- locked: false,
- }
- }
-
- /// Is the current state active?
- pub fn is_active(&self) -> bool {
- match self.toggle {
- ModeToggle::Inactive {..} => false,
- ModeToggle::Active {..} => true,
- }
- }
-
- pub fn is_locked(&self) -> bool {
- self.locked
- }
-
- /// Has the current transition finished?
- pub fn is_ready(&self, tick: u64) -> bool {
- let started = match self.toggle {
- ModeToggle::Inactive { start } => start,
- ModeToggle::Active { start } => start,
- };
- tick - started > self.frames
- }
-
- pub fn progress(&self, tick: u64) -> f32 {
- match self.toggle {
- ModeToggle::Inactive { start } => {
- 1.0 - (((tick - start) as f32) / self.frames as f32)
- .clamp(0.0, 1.0)
- },
- ModeToggle::Active { start } => {
- (((tick - start) as f32) / self.frames as f32)
- .clamp(0.0, 1.0)
- }
- }
- }
-
- pub fn reset(&mut self) {
- self.locked = false;
- self.toggle = ModeToggle::Inactive { start: 0 };
- }
-
- pub fn reverse(&mut self, tick: u64) -> bool {
- if !self.locked {
- self.locked = true;
- match self.toggle {
- ModeToggle::Inactive { start } => {
- self.toggle = ModeToggle::Active {
- start: compute_reverse(self.frames, tick, start)
- };
- },
- ModeToggle::Active { start } => {
- self.toggle = ModeToggle::Inactive {
- start: compute_reverse(self.frames, tick, start)
- };
- },
- }
- true
- } else { false }
- }
-
- pub fn lock(&mut self) {
- self.locked = true;
- }
-
- pub fn unlock(&mut self) {
- self.locked = false;
- }
-}
-
-pub struct Cursor {
- pub index: i32,
- pub prev_index: i32,
- pub change_started: u64,
- pub bound: i32,
- pub frames: u64,
- pub locked: bool,
-}
-
-impl Cursor {
- pub fn new(bound: i32, frames: u64) -> Self {
- Self {
- index: 0,
- prev_index: 0,
- change_started: 0,
- bound,
- frames,
- locked: false,
- }
- }
-
- pub fn animation_index(&self, tick: u64) -> f32 {
- let progress = ((tick - self.change_started) as f32)
- / (self.frames as f32 / 2.0);
- utils::lerp(
- self.prev_index as f32,
- self.index as f32,
- progress
- )
- }
-
- pub fn is_ready(&self, tick: u64) -> bool {
- tick - self.change_started > self.frames
- }
-
- pub fn set(&mut self, val: i32, tick: u64) -> bool {
- if self.is_ready(tick) || !self.locked {
- self.change_started = tick;
- self.prev_index = self.index;
- self.index = val;
- self.index %= self.bound;
- self.locked = true;
- true
- } else { false }
- }
-
- pub fn increment(&mut self, tick: u64) -> bool {
- self.set(self.index + 1, tick)
- }
-
- pub fn decrement(&mut self, tick: u64) -> bool {
- self.set(self.index + self.bound - 1, tick)
- }
-
- pub fn unlock(&mut self) {
- self.locked = false;
- }
-}
diff --git a/src/utils.rs b/src/utils.rs
deleted file mode 100644
index 05e9251..0000000
--- a/src/utils.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-use std::f32::consts::PI;
-
-use serde::{Serialize, Deserialize};
-use strum::EnumIter;
-
-pub type Erm<T> = color_eyre::Result<T>;
-
-pub fn erm<E, T>(e: E) -> Erm<T> where E: std::error::Error + std::marker::Send + std::marker::Sync + 'static {
- Err(e.into())
-}
-
-pub struct ErrorHandler;
-impl color_eyre::eyre::EyreHandler for ErrorHandler {
- fn debug(
- &self,
- error: &(dyn std::error::Error + 'static),
- f: &mut core::fmt::Formatter<'_>,
- ) -> core::fmt::Result {
- if f.alternate() {
- return core::fmt::Debug::fmt(error, f);
- }
- let mut first = true;
- if let Some(s) = error.source() {
- let errors: Vec<_> = std::iter::successors(Some(s), |e| (*e).source()).collect();
- for err in errors.iter().rev() {
- writeln!(f)?; write!(f, "{}{}", if first {""} else {" - "}, err)?;
- first = false;
- }
- }
- writeln!(f)?; write!(f, "{}{}", if first {""} else {" - "}, error)?;
- Ok(())
- }
-}
-
-pub fn install_error_handler() {
- let (panic_hook, _) = color_eyre::config::HookBuilder::default().into_hooks();
- panic_hook.install();
- color_eyre::eyre::set_hook(Box::new(move |_| Box::new(ErrorHandler))).expect("failed to install error handler");
-}
-
-#[derive(Clone, Copy, Debug, EnumIter, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
-pub enum Cardinal {
- North,
- South,
- West,
- East,
-}
-
-impl Cardinal {
- pub fn to_string(&self) -> &'static str {
- match self {
- Self::North => "north",
- Self::South => "south",
- Self::West => "west",
- Self::East => "east",
- }
- }
-
- pub fn turn_cw(&self) -> Self {
- match self {
- Self::North => Self::East,
- Self::South => Self::West,
- Self::West => Self::North,
- Self::East => Self::South,
- }
- }
-
- pub fn turn_ccw(&self) -> Self {
- match self {
- Self::North => Self::West,
- Self::South => Self::East,
- Self::West => Self::South,
- Self::East => Self::North,
- }
- }
-
- pub fn dir(&self) -> glam::Vec3 {
- match self {
- Self::North => glam::Vec3::new(0.0, 1.0, 0.0),
- Self::South => glam::Vec3::new(0.0, -1.0, 0.0),
- Self::West => glam::Vec3::new(-1.0, 0.0, 0.0),
- Self::East => glam::Vec3::new(1.0, 0.0, 0.0),
- }
- }
-
- pub fn offsets(&self) -> (i32, i32) {
- match self {
- Self::North => (0, 1),
- Self::South => (0, -1),
- Self::West => (-1, 0),
- Self::East => (1, 0),
- }
- }
-
- pub fn angle(&self) -> f32 {
- match self {
- Self::North => 0.0,
- Self::South => PI,
- Self::West => PI / 2.0,
- Self::East => 3.0 * PI / 2.0,
- }
- }
-
- pub fn turn_by(&self, o: &Self) -> Self {
- match o {
- Self::North => self.clone(),
- Self::South => self.turn_cw().turn_cw(),
- Self::West => self.turn_cw(),
- Self::East => self.turn_ccw(),
- }
- }
-
- pub fn angle_between(&self, o: &Self) -> f32 {
- if o == &self.turn_cw() { -PI / 2.0 }
- else if o == &self.turn_ccw() { PI / 2.0 }
- else if o == &self.turn_cw().turn_cw() { PI }
- else { 0.0 }
- }
-}
-
-pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
- a + t.clamp(0.0, 1.0) * (b - a)
-}
-
-pub fn dir_lerp(a: &glam::Vec3, b: glam::Vec3, t: f32) -> glam::Vec3 {
- let dirrotaxis = a.cross(b).normalize();
- let dirrotangle = a.angle_between(b);
- let dirrotfull = glam::Quat::from_axis_angle(dirrotaxis, dirrotangle);
- let dirrot = glam::Quat::IDENTITY.slerp(dirrotfull, t);
- dirrot.mul_vec3(a.clone())
-}