summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/assets/audio/mrbeast.mp3bin0 -> 33837 bytes
-rw-r--r--src/assets/meshes/cube.obj34
-rw-r--r--src/assets/shaders/flat/frag.glsl11
-rw-r--r--src/assets/shaders/flat/vert.glsl4
-rw-r--r--src/assets/shaders/plane/frag.glsl23
-rw-r--r--src/assets/shaders/plane/vert.glsl11
-rw-r--r--src/assets/textures/test.pngbin714 -> 772 bytes
-rw-r--r--src/game.rs178
-rw-r--r--src/helpers.js13
-rw-r--r--src/index.css34
-rw-r--r--src/lib.rs5
-rw-r--r--src/main.rs6
-rw-r--r--src/room.rs346
13 files changed, 576 insertions, 89 deletions
diff --git a/src/assets/audio/mrbeast.mp3 b/src/assets/audio/mrbeast.mp3
new file mode 100644
index 0000000..8cf3d8a
--- /dev/null
+++ b/src/assets/audio/mrbeast.mp3
Binary files differ
diff --git a/src/assets/meshes/cube.obj b/src/assets/meshes/cube.obj
new file mode 100644
index 0000000..6c93e45
--- /dev/null
+++ b/src/assets/meshes/cube.obj
@@ -0,0 +1,34 @@
+# Blender 3.6.2
+# www.blender.org
+o Cube
+v 1.000000 1.000000 -1.000000
+v 1.000000 -1.000000 -1.000000
+v 1.000000 1.000000 1.000000
+v 1.000000 -1.000000 1.000000
+v -1.000000 1.000000 -1.000000
+v -1.000000 -1.000000 -1.000000
+v -1.000000 1.000000 1.000000
+v -1.000000 -1.000000 1.000000
+vn -0.0000 1.0000 -0.0000
+vn -0.0000 -0.0000 1.0000
+vn -1.0000 -0.0000 -0.0000
+vn -0.0000 -1.0000 -0.0000
+vn 1.0000 -0.0000 -0.0000
+vn -0.0000 -0.0000 -1.0000
+vt 1.000000 0.000000
+vt 0.000000 1.000000
+vt 0.000000 0.000000
+vt 1.000000 1.000000
+s 0
+f 5/1/1 3/2/1 1/3/1
+f 3/1/2 8/2/2 4/3/2
+f 7/1/3 6/2/3 8/3/3
+f 2/1/4 8/2/4 6/3/4
+f 1/1/5 4/2/5 2/3/5
+f 5/1/6 2/2/6 6/3/6
+f 5/1/1 7/4/1 3/2/1
+f 3/1/2 7/4/2 8/2/2
+f 7/1/3 5/4/3 6/2/3
+f 2/1/4 4/4/4 8/2/4
+f 1/1/5 3/4/5 4/2/5
+f 5/1/6 1/4/6 2/2/6
diff --git a/src/assets/shaders/flat/frag.glsl b/src/assets/shaders/flat/frag.glsl
deleted file mode 100644
index 7006a2b..0000000
--- a/src/assets/shaders/flat/frag.glsl
+++ /dev/null
@@ -1,11 +0,0 @@
-uniform sampler2D texture_data;
-
-void main()
-{
- vec2 tcfull = vec2(vertex_texcoord.x, 1.0 - vertex_texcoord.y);
- vec4 texel = texture(texture_data, tcfull);
- if (texel.a != 1.0) {
- discard;
- }
- frag_color = texel;
-}
diff --git a/src/assets/shaders/flat/vert.glsl b/src/assets/shaders/flat/vert.glsl
deleted file mode 100644
index e324f7e..0000000
--- a/src/assets/shaders/flat/vert.glsl
+++ /dev/null
@@ -1,4 +0,0 @@
-void main()
-{
- default_main();
-} \ No newline at end of file
diff --git a/src/assets/shaders/plane/frag.glsl b/src/assets/shaders/plane/frag.glsl
new file mode 100644
index 0000000..a323cc8
--- /dev/null
+++ b/src/assets/shaders/plane/frag.glsl
@@ -0,0 +1,23 @@
+uniform sampler2D texture_data;
+
+uniform vec2 texture_scale;
+uniform vec2 texture_offset;
+uniform float fog_distance;
+uniform vec3 fog_color;
+uniform float opacity;
+
+in float vertex_depth;
+
+void main()
+{
+ vec2 tcfull = vec2(vertex_texcoord.x, 1.0 - vertex_texcoord.y);
+ vec4 texel = texture(texture_data, tcfull * texture_scale + texture_offset);
+ if (fog_distance > 0.0) {
+ float t = clamp(vertex_depth / fog_distance, 0.0, 1.0);
+ texel.rgb = mix(texel.rgb, fog_color, t);
+ if (t == 1.0) texel.a = 1.0;
+ }
+ texel.a *= opacity;
+ if (texel.a == 0.0) discard;
+ frag_color = texel;
+}
diff --git a/src/assets/shaders/plane/vert.glsl b/src/assets/shaders/plane/vert.glsl
new file mode 100644
index 0000000..c8932a5
--- /dev/null
+++ b/src/assets/shaders/plane/vert.glsl
@@ -0,0 +1,11 @@
+out float vertex_depth;
+
+void main()
+{
+ vertex_texcoord = texcoord;
+ vec3 pos = (position * vec4(vertex, 1.0)).xyz;
+ vertex_fragpos = pos;
+ vec4 vpos = view * vec4(pos, 1.0);
+ vertex_depth = vpos.z;
+ gl_Position = projection * vpos;
+}
diff --git a/src/assets/textures/test.png b/src/assets/textures/test.png
index 1f1edca..2b0227b 100644
--- a/src/assets/textures/test.png
+++ b/src/assets/textures/test.png
Binary files differ
diff --git a/src/game.rs b/src/game.rs
index 1abe66e..0596a2b 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -1,11 +1,16 @@
#![allow(dead_code, unused_variables)]
-use std::collections::HashMap;
+use std::{collections::HashMap, f32::consts::PI};
use teleia::*;
+use crate::room;
+
+const MOUSE_SENSITIVITY: f32 = 0.01;
+const PLAYER_SPEED: f32 = 0.1;
+const PLAYER_RADIUS: f32 = 0.5;
+
struct Assets {
font: font::Bitmap,
- shader_flat: shader::Shader,
- mesh_square: mesh::Mesh,
+ mesh_cube: mesh::Mesh,
texture_test: texture::Texture,
}
@@ -13,53 +18,176 @@ impl Assets {
fn new(ctx: &context::Context) -> Self {
Self {
font: font::Bitmap::default(ctx),
- shader_flat: shader::Shader::new(
- ctx,
- include_str!("assets/shaders/flat/vert.glsl"),
- include_str!("assets/shaders/flat/frag.glsl"),
- ),
- mesh_square: mesh::Mesh::from_obj(ctx, include_bytes!("assets/meshes/square.obj")),
+ mesh_cube: mesh::Mesh::from_obj(ctx, include_bytes!("assets/meshes/cube.obj")),
texture_test: texture::Texture::new(ctx, include_bytes!("assets/textures/test.png")),
}
}
}
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Mode {
+ Titlescreen,
+ Game,
+}
+
pub struct Game {
+ mode: Mode,
assets: Assets,
+ room_path: String,
+ room: room::Room,
+ mouse: (i32, i32),
+ camera_pitch: f32,
+ camera_yaw: f32,
+ player_pos: glam::Vec3,
}
impl Game {
- pub fn new(ctx: &context::Context) -> Self {
+ pub fn new(ctx: &context::Context, room_path: &str) -> Self {
Self {
+ mode: Mode::Titlescreen,
assets: Assets::new(ctx),
+ room_path: room_path.to_owned(),
+ room: room::Room::new(ctx, shader::Shader::new(ctx,
+ include_str!("assets/shaders/plane/vert.glsl"),
+ include_str!("assets/shaders/plane/frag.glsl"),
+ )),
+ mouse: (ctx.render_width as i32 / 2, ctx.render_height as i32 / 2),
+ camera_pitch: 0.0,
+ camera_yaw: 0.0,
+ player_pos: glam::Vec3::new(0.0, 0.0, 0.0),
}
}
+ fn looking_at_art(&self, ctx: &context::Context, st: &mut state::State) -> bool {
+ let q = glam::Quat::from_rotation_y(self.camera_yaw);
+ let dir = q.mul_vec3(glam::Vec3::new(0.0, 0.0, 1.0));
+ let p = self.player_pos + dir;
+ p.distance(glam::Vec3::ZERO) < 2.0
+ }
+ fn player_height(&self) -> f32 {
+ 1.0 / self.room.config.size
+ }
+ fn player_speed(&self) -> f32 {
+ PLAYER_SPEED / self.room.config.size * self.room.config.speed
+ }
}
impl teleia::state::Game for Game {
+ fn initialize(&mut self, ctx: &context::Context, st: &mut state::State) -> Erm<()> {
+ Ok(())
+ }
fn initialize_audio(&self, ctx: &context::Context, st: &state::State, actx: &audio::Context) -> HashMap<String, audio::Audio> {
HashMap::from_iter(vec![
- ("test".to_owned(), audio::Audio::new(&actx, include_bytes!("assets/audio/test.wav"))),
+ ("mrbeast".to_owned(), audio::Audio::new(&actx, include_bytes!("assets/audio/mrbeast.mp3"))),
])
}
- fn update(&mut self, _ctx: &context::Context, _st: &mut state::State) -> Erm<()> {
+ fn finish_title(&mut self, ctx: &context::Context, st: &mut state::State) -> Erm<()> {
+ if self.mode == Mode::Titlescreen {
+ self.mode = Mode::Game;
+ ctx.lock_mouse();
+ // TODO
+ if cfg!(target_arch = "wasm32") {
+ self.room.load_from_bytes(ctx, st,
+ Some(include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/room.ini")),
+ include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/FLOOR.png"),
+ include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/WALL.png"),
+ include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/ROOF.png"),
+ include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/ART.png"),
+ include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/CREDIT.png"),
+ Some(include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/MINI.png")),
+ Some(include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/INTERACT.png")),
+ Some(include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/LAYER.png")),
+ Some(include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/MUSIC.mp3")),
+ Some(include_bytes!("/home/llll/src/bapalon/DATA/NIGHT_1/ROOM_4/FOOT.wav")),
+ )?;
+ } else {
+ self.room.load(ctx, st, &self.room_path)?;
+ }
+ }
+ Ok(())
+ }
+ fn mouse_press(&mut self, ctx: &context::Context, st: &mut state::State) -> Erm<()> {
+ match self.mode {
+ Mode::Titlescreen => {
+ }
+ Mode::Game => {
+ self.room.credit_mode.toggle(st.tick);
+ },
+ }
+ Ok(())
+ }
+ fn mouse_move(&mut self, ctx: &context::Context, st: &mut state::State, x: i32, y: i32) -> Erm<()> {
+ match self.mode {
+ Mode::Titlescreen => {
+ }
+ Mode::Game => {
+ let dx = x - self.mouse.0;
+ let dy = y - self.mouse.1;
+ self.camera_yaw += dx as f32 * MOUSE_SENSITIVITY;
+ self.camera_pitch += dy as f32 * MOUSE_SENSITIVITY;
+ self.camera_pitch = self.camera_pitch.clamp(-PI/2.0 + 0.001, PI/2.0 - 0.001);
+ self.mouse = (x, y);
+ }
+ }
+ Ok(())
+ }
+ fn update(&mut self, ctx: &context::Context, st: &mut state::State) -> Erm<()> {
+ match self.mode {
+ Mode::Titlescreen => {
+ }
+ Mode::Game => {
+ let mut player_dir = glam::Vec3::ZERO;
+ if st.keys.up() { player_dir += glam::Vec3::new(0.0, 0.0, 1.0); }
+ if st.keys.down() { player_dir += glam::Vec3::new(0.0, 0.0, -1.0); }
+ if st.keys.left() { player_dir += glam::Vec3::new(-1.0, 0.0, 0.0); }
+ if st.keys.right() { player_dir += glam::Vec3::new(1.0, 0.0, 0.0); }
+ if player_dir != glam::Vec3::ZERO {
+ self.room.footstep(ctx, st)?;
+ player_dir = player_dir.normalize();
+ self.player_pos += self.player_speed()
+ * glam::Quat::from_rotation_y(self.camera_yaw).mul_vec3(player_dir);
+ self.player_pos.x = self.player_pos.x.clamp(
+ -room::PLANE_SIZE + PLAYER_RADIUS,
+ room::PLANE_SIZE - PLAYER_RADIUS
+ );
+ self.player_pos.z = self.player_pos.z.clamp(
+ -room::PLANE_SIZE + PLAYER_RADIUS,
+ room::PLANE_SIZE - PLAYER_RADIUS
+ );
+ }
+ if self.looking_at_art(ctx, st) {
+ if !self.room.interact_mode.is_active() {
+ self.room.interact_mode.toggle(st.tick);
+ }
+ } else {
+ if self.room.interact_mode.is_active() {
+ self.room.interact_mode.toggle(st.tick);
+ }
+ }
+ let q = glam::Quat::from_euler(glam::EulerRot::YXZ, self.camera_yaw, self.camera_pitch, 0.0);
+ let dir = q.mul_vec3(glam::Vec3::new(0.0, 0.0, 1.0));
+ st.move_camera(ctx,
+ &(self.player_pos + glam::Vec3::new(0.0, self.player_height(), 0.0)),
+ &dir,
+ &glam::Vec3::new(0.0, 1.0, 0.0),
+ );
+ }
+ }
Ok(())
}
fn render(&mut self, ctx: &context::Context, st: &mut state::State) -> Erm<()> {
ctx.clear();
- self.assets.font.render_text(
- ctx, st,
- &glam::Vec2::new(0.0, 0.0),
- "hello computer",
- );
- st.bind_2d(ctx, &self.assets.shader_flat);
- self.assets.texture_test.bind(ctx);
- self.assets.shader_flat.set_position_2d(
- ctx, st,
- &glam::Vec2::new(40.0, 40.0),
- &glam::Vec2::new(16.0, 16.0),
- );
- self.assets.mesh_square.render(ctx);
+ match self.mode {
+ Mode::Titlescreen => {
+ self.assets.font.render_text(
+ ctx, st,
+ &glam::Vec2::new(0.0, 0.0),
+ "click to continue",
+ );
+ }
+ Mode::Game => {
+ self.room.render(ctx, st, self.camera_yaw)?;
+ }
+ }
Ok(())
}
}
diff --git a/src/helpers.js b/src/helpers.js
deleted file mode 100644
index 20b86f4..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/index.css b/src/index.css
deleted file mode 100644
index 323fa50..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,34 +0,0 @@
-html {
- touch-action: manipulation;
-}
-body {
- background: #000000;
-}
-html, body {
- overflow: hidden;
- margin: 0 !important;
- padding: 0 !important;
- height: 100%;
- width: 100%;
-}
-canvas {
- margin-right: auto;
- margin-left: auto;
- display: block;
- position: absolute;
- top: 0%;
- left: 50%;
- transform: translate(-50%, 0%);
- color: #000000;
-}
-.centered {
- margin-right: auto;
- margin-left: auto;
- display: block;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- text-align: center;
- image-transform: pixelated;
-}
diff --git a/src/lib.rs b/src/lib.rs
index 66f9771..0060ab3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
mod game;
+mod room;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
@@ -6,5 +7,7 @@ use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub fn main_js() {
- teleia::run(240, 160, teleia::Options::empty(), game::Game::new);
+ teleia::run(240, 160, teleia::Options::empty(),
+ |ctx| game::Game::new(ctx, "broken path")
+ );
}
diff --git a/src/main.rs b/src/main.rs
index 25a240d..3c8ed4f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,8 +1,12 @@
mod game;
+mod room;
#[cfg(not(target_arch = "wasm32"))]
pub fn main() -> teleia::Erm<()> {
- teleia::run("game", 240, 160, teleia::Options::empty(), game::Game::new)
+ let room_path = std::env::args().nth(1).unwrap();
+ teleia::run("game", 640, 480, teleia::Options::FULLSCREEN_MOUSE,
+ |ctx| game::Game::new(ctx, &room_path)
+ )
}
#[cfg(target_arch = "wasm32")]
diff --git a/src/room.rs b/src/room.rs
new file mode 100644
index 0000000..81ed88b
--- /dev/null
+++ b/src/room.rs
@@ -0,0 +1,346 @@
+use std::{f32::consts::PI, path::{Path, PathBuf}};
+
+use teleia::*;
+
+use rand::Rng;
+
+pub const PLANE_SIZE: f32 = 12.0;
+pub const FOOTSTEP_TIME: u64 = 90;
+
+#[derive(Debug)]
+pub struct Config {
+ pub name: String,
+ pub credits: String,
+ pub links: String,
+ pub size: f32,
+ pub speed: f32,
+ pub fov: f32,
+ pub texture_repeat: f32,
+ pub room_height: f32,
+ pub layer_frames: f32,
+ pub fog_color: glam::Vec3,
+ pub fog_end: f32,
+ pub mini_speed: f32,
+}
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ name: "".to_owned(),
+ credits: "".to_owned(),
+ links: "".to_owned(),
+ size: 1.0,
+ speed: 1.0,
+ fov: 55.0,
+ texture_repeat: 3.0,
+ room_height: 350.0,
+ layer_frames: 1.0,
+ fog_color: glam::Vec3::ZERO,
+ fog_end: -1.0,
+ mini_speed: 1.0,
+ }
+ }
+}
+impl Config {
+ pub fn from_room_ini(bs: Vec<u8>) -> Erm<Self> {
+ let mut ret = Self::default();
+ let contents = String::from_utf8(bs)?;
+ let i = ini::Ini::load_from_str(&contents)?;
+ let Some(sec) = i.section::<&str>(None) else { return utils::erm_msg("failed to get properties"); };
+ if let Some(x) = sec.get("name") { ret.name = x.to_owned(); }
+ if let Some(x) = sec.get("credits") { ret.credits = x.to_owned(); }
+ if let Some(x) = sec.get("links") { ret.links = x.to_owned(); }
+ if let Some(x) = sec.get("size") { ret.size = x.parse()?; }
+ if let Some(x) = sec.get("speed") { ret.speed = x.parse()?; }
+ if let Some(x) = sec.get("FOV") { ret.fov = x.parse()?; }
+ if let Some(x) = sec.get("Texture_Repeat") { ret.texture_repeat = x.parse()?; }
+ if let Some(x) = sec.get("Room_Height") { ret.room_height = x.parse::<f32>()? / 350.0; }
+ if let Some(x) = sec.get("Layer_Frames") { ret.layer_frames = x.parse()?; }
+ if let Some(x) = sec.get("Fog_Color") {
+ let num: u64 = x.parse()?;
+ ret.fog_color = glam::Vec3::new(
+ (num & 0xff) as f32 / 255.0,
+ (num >> 8 & 0xff) as f32 / 255.0,
+ (num >> 16 & 0xff) as f32 / 255.0,
+ );
+ }
+ if let Some(x) = sec.get("Fog_End") { ret.fog_end = x.parse::<f32>()? / 10.0; }
+ if let Some(x) = sec.get("Mini_speed") { ret.mini_speed = x.parse::<f32>()? / 60.0; }
+ Ok(ret)
+ }
+}
+
+pub struct Room {
+ pub config: Config,
+ pub shader: shader::Shader,
+ pub floor: texture::Texture,
+ pub wall: texture::Texture,
+ pub roof: texture::Texture,
+ pub art: texture::Texture,
+ pub credit: texture::Texture,
+ pub mini: texture::Texture, pub has_mini: bool, pub mini_frame: u64,
+ pub mini_pos: glam::Vec2, pub mini_dir: glam::Vec2,
+ pub interact: texture::Texture, pub has_interact: bool,
+ pub layer: texture::Texture, pub has_layer: bool, pub layer_frame: u64,
+ pub interact_mode: ui::Mode,
+ pub credit_mode: ui::Mode,
+ pub music: Option<(audio::Audio, audio::AudioPlayingHandle)>,
+ pub foot: Option<audio::Audio>, pub foot_countdown: u64,
+}
+impl Room {
+ pub fn new(ctx: &context::Context, shader: shader::Shader) -> Self {
+ Self {
+ config: Config::default(),
+ shader,
+ floor: texture::Texture::new_empty(ctx),
+ wall: texture::Texture::new_empty(ctx),
+ roof: texture::Texture::new_empty(ctx),
+ art: texture::Texture::new_empty(ctx),
+ credit: texture::Texture::new_empty(ctx),
+ mini: texture::Texture::new_empty(ctx), has_mini: false, mini_frame: 0,
+ mini_pos: glam::Vec2::new(4.0, 4.0), mini_dir: glam::Vec2::new(1.0, 0.0),
+ interact: texture::Texture::new_empty(ctx), has_interact: false,
+ layer: texture::Texture::new_empty(ctx), has_layer: false, layer_frame: 0,
+ interact_mode: ui::Mode::new(20),
+ credit_mode: ui::Mode::new(10),
+ music: None,
+ foot: None,
+ foot_countdown: 0,
+ }
+ }
+ fn load_file(base: impl AsRef<Path>, needle: &str) -> Erm<Vec<u8>> {
+ let base = base.as_ref();
+ let uneedle = needle.to_uppercase();
+ for mp in std::fs::read_dir(base)? {
+ if let Ok(p) = mp && let Some(onm) = p.path().file_name() && let Some(nm) = onm.to_str() {
+ if nm.to_uppercase().contains(&uneedle) {
+ return Ok(std::fs::read(p.path())?);
+ }
+ }
+ }
+ utils::erm_msg(&format!("failed to find {} in {}", needle, base.display()))
+ }
+ pub fn load_from_bytes(
+ &mut self, ctx: &context::Context, st: &mut state::State,
+ config: Option<&[u8]>,
+ floor: &[u8], wall: &[u8], roof: &[u8], art: &[u8], credit: &[u8],
+ mini: Option<&[u8]>, interact: Option<&[u8]>, layer: Option<&[u8]>,
+ music: Option<&[u8]>, foot: Option<&[u8]>,
+ ) -> Erm<()> {
+ self.config = Config::default();
+ if let Some(bs) = config {
+ self.config = Config::from_room_ini(bs.to_owned())?;
+ }
+ self.floor.upload(ctx, floor); self.floor.set_repeat(ctx);
+ self.wall.upload(ctx, wall); self.wall.set_repeat(ctx);
+ self.roof.upload(ctx, roof); self.roof.set_repeat(ctx);
+ self.art.upload(ctx, art);
+ self.credit.upload(ctx, credit);
+ if let Some(bs) = mini {
+ self.mini.upload(ctx, &bs);
+ self.has_mini = true;
+ } else { self.has_mini = false; }
+ if let Some(bs) = interact {
+ self.interact.upload(ctx, &bs);
+ self.has_interact = true;
+ } else { self.has_interact = false; }
+ if let Some(bs) = layer {
+ self.layer.upload(ctx, &bs);
+ self.has_layer = true;
+ } else { self.has_layer = false; }
+ if let Some(bs) = music && let Some(audio) = st.audio.as_mut() {
+ let m = audio::Audio::new(&audio.ctx, &bs);
+ if let Some(handle) = m.play(&mut audio.ctx, Some((None, None))) {
+ self.music = Some((m, handle))
+ }
+ } else { self.music = None; }
+ if let Some(bs) = foot && let Some(audio) = st.audio.as_mut() {
+ let m = audio::Audio::new(&audio.ctx, &bs);
+ self.foot = Some(m);
+ } else { self.foot = None; }
+ Ok(())
+ }
+ pub fn load(&mut self, ctx: &context::Context, st: &mut state::State, dir: &str) -> Erm<()> {
+ let base = PathBuf::from(dir);
+ self.load_from_bytes(ctx, st,
+ Self::load_file(&base, "ROOM").ok().as_deref(),
+ &Self::load_file(&base, "FLOOR")?,
+ &Self::load_file(&base, "WALL")?,
+ &Self::load_file(&base, "ROOF")?,
+ &Self::load_file(&base, "ART")?,
+ &Self::load_file(&base, "CREDIT")?,
+ Self::load_file(&base, "MINI").ok().as_deref(),
+ Self::load_file(&base, "INTERACT").ok().as_deref(),
+ Self::load_file(&base, "LAYER").ok().as_deref(),
+ Self::load_file(&base, "MUSIC").ok().as_deref(),
+ Self::load_file(&base, "FOOT").ok().as_deref(),
+ )?;
+ Ok(())
+ }
+ pub fn footstep(&mut self, _ctx: &context::Context, st: &mut state::State) -> Erm<()> {
+ if let Some(f) = &self.foot && let Some(audio) = st.audio.as_mut() {
+ if self.foot_countdown == 0 {
+ f.play(&mut audio.ctx, None);
+ self.foot_countdown = FOOTSTEP_TIME;
+ } else {
+ self.foot_countdown -= 1;
+ }
+ }
+ Ok(())
+ }
+ pub fn render(&mut self, ctx: &context::Context, st: &mut state::State, yaw: f32) -> Erm<()> {
+ let scale = st.render_dims.x / 640.0;
+ st.projection = glam::Mat4::perspective_lh(
+ self.config.fov * PI / 180.0,
+ st.render_dims.x / st.render_dims.y,
+ 0.5,
+ 50.0,
+ );
+ st.bind_3d(ctx, &self.shader);
+ self.shader.set_vec2(ctx, "texture_scale",
+ &glam::Vec2::new(self.config.texture_repeat, self.config.texture_repeat)
+ );
+ self.shader.set_f32(ctx, "fog_distance", self.config.fog_end);
+ self.shader.set_vec3(ctx, "fog_color", &self.config.fog_color);
+ self.floor.bind(ctx);
+ self.shader.set_position_3d(
+ ctx, st,
+ &glam::Mat4::from_scale_rotation_translation(
+ glam::Vec3::new(PLANE_SIZE, PLANE_SIZE, 1.0),
+ glam::Quat::from_rotation_x(PI / 2.0),
+ glam::Vec3::new(0.0, 0.0, 0.0),
+ ),
+ );
+ st.mesh_square.render(ctx);
+ self.roof.bind(ctx);
+ self.shader.set_position_3d(
+ ctx, st,
+ &glam::Mat4::from_scale_rotation_translation(
+ glam::Vec3::new(PLANE_SIZE, PLANE_SIZE, 1.0),
+ glam::Quat::from_rotation_x(3.0 * PI / 2.0),
+ glam::Vec3::new(0.0, self.config.room_height * PLANE_SIZE * 2.0, 0.0),
+ ),
+ );
+ st.mesh_square.render(ctx);
+ self.wall.bind(ctx);
+ let wallpos = [
+ glam::Mat4::from_scale_rotation_translation(
+ glam::Vec3::new(PLANE_SIZE, PLANE_SIZE, 1.0),
+ glam::Quat::from_rotation_y(0.0),
+ glam::Vec3::new(0.0, PLANE_SIZE, PLANE_SIZE),
+ ),
+ glam::Mat4::from_scale_rotation_translation(
+ glam::Vec3::new(PLANE_SIZE, PLANE_SIZE, 1.0),
+ glam::Quat::from_rotation_y(PI),
+ glam::Vec3::new(0.0, PLANE_SIZE, -PLANE_SIZE),
+ ),
+ glam::Mat4::from_scale_rotation_translation(
+ glam::Vec3::new(PLANE_SIZE, PLANE_SIZE, 1.0),
+ glam::Quat::from_rotation_y(PI / 2.0),
+ glam::Vec3::new(PLANE_SIZE, PLANE_SIZE, 0.0),
+ ),
+ glam::Mat4::from_scale_rotation_translation(
+ glam::Vec3::new(PLANE_SIZE, PLANE_SIZE, 1.0),
+ glam::Quat::from_rotation_y(3.0 * PI / 2.0),
+ glam::Vec3::new(-PLANE_SIZE, PLANE_SIZE, 0.0),
+ ),
+ ];
+ for wp in wallpos {
+ self.shader.set_position_3d(ctx, st, &wp);
+ st.mesh_square.render(ctx);
+ }
+ self.shader.set_vec2(ctx, "texture_scale", &glam::Vec2::new(1.0, 1.0));
+ self.art.bind(ctx);
+ self.shader.set_position_3d(
+ ctx, st,
+ &glam::Mat4::from_scale_rotation_translation(
+ glam::Vec3::new(1.0, 1.0, 1.0),
+ glam::Quat::from_rotation_y(yaw),
+ glam::Vec3::new(0.0, 1.0, 0.0),
+ ),
+ );
+ st.mesh_square.render(ctx);
+ if self.has_mini {
+ self.mini.bind(ctx);
+ let xscale = 1.0 / 4.0;
+ let xoffset = xscale * self.mini_frame as f32;
+ if st.tick % 10 == 0 {
+ self.mini_frame += 1;
+ self.mini_frame %= 4;
+ }
+ self.shader.set_vec2(ctx, "texture_scale", &glam::Vec2::new(xscale, 1.0));
+ self.shader.set_vec2(ctx, "texture_offset", &glam::Vec2::new(xoffset, 0.0));
+ self.shader.set_position_3d(
+ ctx, st,
+ &glam::Mat4::from_scale_rotation_translation(
+ glam::Vec3::new(0.25, 0.25, 1.0),
+ glam::Quat::from_rotation_y(yaw),
+ glam::Vec3::new(self.mini_pos.x, 0.25, self.mini_pos.y),
+ ),
+ );
+ st.mesh_square.render(ctx);
+ self.shader.set_vec2(ctx, "texture_scale", &glam::Vec2::new(1.0, 1.0));
+ self.shader.set_vec2(ctx, "texture_offset", &glam::Vec2::new(0.0, 0.0));
+ let newpos = self.mini_pos + self.config.mini_speed * self.mini_dir;
+ if newpos.x.abs() > PLANE_SIZE || newpos.y.abs() > PLANE_SIZE {
+ let angle = rand::thread_rng().gen_range(0..360) as f32 * PI / 180.0;
+ self.mini_dir = glam::Vec2::new(angle.cos(), angle.sin());
+ } else {
+ self.mini_pos = newpos;
+ }
+ }
+
+ ctx.clear_depth();
+ self.shader.set_f32(ctx, "fog_distance", -1.0);
+ if self.has_layer {
+ st.bind_2d(ctx, &self.shader);
+ let xscale = 1.0 / self.config.layer_frames;
+ let xoffset = xscale * self.layer_frame as f32;
+ if st.tick % 10 == 0 {
+ self.layer_frame += 1;
+ self.layer_frame %= self.config.layer_frames as u64;
+ }
+ self.shader.set_vec2(ctx, "texture_scale", &glam::Vec2::new(xscale, 1.0));
+ self.shader.set_vec2(ctx, "texture_offset", &glam::Vec2::new(xoffset, 0.0));
+ self.layer.bind(ctx);
+ self.shader.set_position_2d(
+ ctx, st,
+ &glam::Vec2::new(0.0, 0.0),
+ &st.render_dims,
+ );
+ st.mesh_square.render(ctx);
+ self.shader.set_vec2(ctx, "texture_scale", &glam::Vec2::new(1.0, 1.0));
+ self.shader.set_vec2(ctx, "texture_offset", &glam::Vec2::new(0.0, 0.0));
+ }
+ if self.has_interact {
+ st.bind_2d(ctx, &self.shader);
+ self.shader.set_f32(ctx, "opacity", self.interact_mode.progress(st.tick));
+ self.interact.bind(ctx);
+ let iwidth = self.interact.width as f32 * scale;
+ let iheight = iwidth * (self.interact.height as f32 / self.interact.width as f32);
+ self.shader.set_position_2d(
+ ctx, st,
+ &glam::Vec2::new(0.0, 0.0),
+ &glam::Vec2::new(iwidth, iheight),
+ );
+ st.mesh_square.render(ctx);
+ self.shader.set_f32(ctx, "opacity", 1.0);
+ }
+ st.bind_2d(ctx, &self.shader);
+ self.credit.bind(ctx);
+ let cwidth = self.credit.width as f32 * scale;
+ let cheight = cwidth * (self.credit.height as f32 / self.credit.width as f32);
+ self.shader.set_position_2d(
+ ctx, st,
+ &glam::Vec2::new(st.render_dims.x - cwidth,
+ utils::lerp(
+ st.render_dims.y,
+ st.render_dims.y - cheight,
+ self.credit_mode.progress(st.tick),
+ ),
+ ),
+ &glam::Vec2::new(cwidth, cheight),
+ );
+ st.mesh_square.render(ctx);
+ Ok(())
+ }
+}