summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio.rs66
-rw-r--r--src/context.rs36
-rw-r--r--src/lib.rs284
-rw-r--r--src/module.rs45
-rw-r--r--src/request.rs20
-rw-r--r--src/state.rs28
6 files changed, 299 insertions, 180 deletions
diff --git a/src/audio.rs b/src/audio.rs
index 2b190de..5cbce90 100644
--- a/src/audio.rs
+++ b/src/audio.rs
@@ -1,9 +1,11 @@
use std::{cell::RefCell, collections::HashMap};
+#[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()
@@ -14,11 +16,13 @@ impl Context {
}
}
+#[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)));
@@ -52,6 +56,7 @@ impl Audio {
}
}
+#[cfg(target_arch = "wasm32")]
pub struct Assets {
pub ctx: Context,
@@ -60,6 +65,7 @@ pub struct Assets {
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();
@@ -94,3 +100,63 @@ impl Assets {
}
}
}
+
+#[cfg(not(target_arch = "wasm32"))]
+pub struct Context {
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+impl Context {
+ pub fn new() -> Self {
+ Self {
+ }
+ }
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+pub struct Audio {
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+impl Audio {
+ pub fn new(ctx: &Context, _bytes: &[u8]) -> Self {
+ Self {
+ }
+ }
+
+ pub fn play(&self, ctx: &Context, looping: Option<(Option<f64>, Option<f64>)>) {
+ }
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+pub struct Assets {
+ pub ctx: Context,
+ pub audio: HashMap<String, Audio>,
+}
+
+#[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,
+ }
+ }
+
+ 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 {
+ false
+ }
+
+ pub fn play_music(&mut self, name: &str, start: Option<f64>, end: Option<f64>) {
+ }
+}
diff --git a/src/context.rs b/src/context.rs
index dc1d3a2..c6c68bc 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -1,17 +1,15 @@
-use wasm_bindgen::JsCast;
-use winit::platform::web::WindowExtWebSys;
use glow::HasContext;
+#[cfg(target_arch = "wasm32")]
+use winit::platform::web::WindowExtWebSys;
+
+#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "./helpers.js")]
extern {
fn js_track_resized_setup();
fn js_poll_resized() -> bool;
}
-// pub const RENDER_WIDTH: f32 = 640.0;
-// pub const RENDER_HEIGHT: f32 = 360.0;
-// pub const RENDER_WIDTH: f32 = 320.0;
-// pub const RENDER_HEIGHT: f32 = 180.0;
pub const RENDER_WIDTH: f32 = 240.0;
pub const RENDER_HEIGHT: f32 = 160.0;
@@ -32,23 +30,13 @@ pub struct Context {
pub window: winit::window::Window,
pub gl: glow::Context,
pub emptyvao: glow::VertexArray,
+
+ #[cfg(target_arch = "wasm32")]
pub performance: web_sys::Performance,
}
impl Context {
- pub fn new(window: winit::window::Window) -> Self {
- 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");
+ pub fn new(window: winit::window::Window, gl: glow::Context) -> Self {
unsafe {
gl.clear_color(0.1, 0.1, 0.1, 1.0);
gl.clear_depth_f32(1.0);
@@ -68,17 +56,21 @@ impl Context {
gl.create_vertex_array().expect("failed to initialize vao")
};
+ #[cfg(target_arch = "wasm32")]
unsafe { js_track_resized_setup(); }
Self {
window,
gl,
emptyvao,
+
+ #[cfg(target_arch = "wasm32")]
performance: web_sys::window().expect("failed to find window")
.performance().expect("failed to get performance"),
}
}
+ #[cfg(target_arch = "wasm32")]
pub fn maximize_canvas(&self) {
web_sys::window()
.and_then(|win| win.document())
@@ -100,12 +92,18 @@ impl Context {
.expect("failed to resize canvas");
}
+ #[cfg(target_arch = "wasm32")]
pub fn resize_necessary(&self) -> bool {
unsafe {
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);
diff --git a/src/lib.rs b/src/lib.rs
index 41bfee8..b89b27e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,8 +1,5 @@
-use winit::platform::web::EventLoopExtWebSys;
-
pub mod utils;
pub mod ui;
-pub mod request;
pub mod context;
pub mod state;
pub mod framebuffer;
@@ -10,9 +7,17 @@ pub mod shader;
pub mod mesh;
pub mod texture;
pub mod font;
-pub mod audio;
pub mod shadow;
-pub mod module;
+pub mod audio;
+
+#[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;
static mut CTX: Option<*const context::Context> = None;
static mut ST: Option<*mut state::State> = None;
@@ -30,28 +35,186 @@ where
}
}
+pub fn event_loop_body<G>(event: winit::event::Event<()>, elwt: &winit::event_loop::EventLoopWindowTarget<()>)
+ where G: state::Game + 'static,
+{
+ 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 {
+ button,
+ state,
+ ..
+ } => match state {
+ winit::event::ElementState::Pressed => {
+ st.mouse_pressed(&ctx, *button, game)
+ },
+ winit::event::ElementState::Released => {
+ st.mouse_released(&ctx, *button)
+ },
+ }
+ 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();
+ },
+
+ _ => {},
+ }
+ });
+}
+
pub async fn run<'a, F, G, Fut>(gnew: F)
where
Fut: std::future::Future<Output = G>,
G: state::Game + 'static,
F: (Fn(&'a context::Context) -> Fut),
{
- console_log::init_with_level(log::Level::Debug).unwrap();
- console_error_panic_hook::set_once();
- tracing_wasm::set_as_global_default();
+ #[cfg(target_arch = "wasm32")]
+ {
+ console_log::init_with_level(log::Level::Debug).unwrap();
+ console_error_panic_hook::set_once();
+ tracing_wasm::set_as_global_default();
+ }
+ #[cfg(not(target_arch = "wasm32"))]
+ {
+ env_logger::Builder::new()
+ .filter(None, log::LevelFilter::Info)
+ .init();
+ }
log::info!("hello computer, starting up...");
let event_loop = winit::event_loop::EventLoop::new()
.expect("failed to initialize event loop");
- let window = winit::window::WindowBuilder::new()
- .with_maximized(true)
- .with_decorations(false)
- .build(&event_loop)
- .expect("failed to initialize window");
+ #[cfg(target_arch = "wasm32")]
+ let (window, gl) = {
+ let window = winit::window::WindowBuilder::new()
+ .with_maximized(true)
+ .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)
+ };
+
+ #[cfg(not(target_arch = "wasm32"))]
+ let (window, gl) = {
+ use glutin::config::GlConfig;
+ use glutin::context::NotCurrentGlContext;
+ use glutin::display::{GlDisplay, GetGlDisplay};
+ use glutin::surface::GlSurface;
+ use raw_window_handle::HasRawWindowHandle;
+ use glutin_winit::GlWindow;
+ let window_builder = winit::window::WindowBuilder::new()
+ .with_title("teleia")
+ .with_maximized(true)
+ .with_decorations(false);
+ let template = glutin::config::ConfigTemplateBuilder::new();
+ let display_builder = glutin_winit::DisplayBuilder::new().with_window_builder(Some(window_builder));
+ let (window, gl_config) = display_builder
+ .build(&event_loop, template, |configs| {
+ configs.reduce(|a, c| {
+ if c.num_samples() > a.num_samples() { c } else { a }
+ }).expect("failed to obtain select configuration")
+ }).expect("failed to obtain opengl display");
+ let window = window.expect("failed to create window");
+ let raw_window_handle = window.raw_window_handle();
+ let gl_display = gl_config.display();
+ let context_attributes = glutin::context::ContextAttributesBuilder::new()
+ // .with_context_api(glutin::context::ContextApi::OpenGl(Some(glutin::context::Version {
+ // major: 3,
+ // minor: 3,
+ // })))
+ .build(Some(raw_window_handle));
+ unsafe {
+ let not_current_gl_context = gl_display.create_context(&gl_config, &context_attributes)
+ .expect("failed to obtain opengl context");
+ let attrs = window.build_surface_attributes(Default::default());
+ let gl_surface = gl_display.create_window_surface(&gl_config, &attrs)
+ .expect("failed to create opengl surface");
+ let gl_context = not_current_gl_context.make_current(&gl_surface)
+ .expect("failed to set openglt context");
+ let gl = glow::Context::from_loader_function_cstr(|s| gl_display.get_proc_address(s));
+ gl_surface
+ .set_swap_interval(&gl_context, glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap()))
+ .expect("failed to set swap interval");
+ (window, gl)
+ }
+ };
+
+ let ctx = Box::leak(Box::new(context::Context::new(window, gl)));
+
+ #[cfg(target_arch = "wasm32")]
+ {
+ ctx.maximize_canvas();
+ }
- let ctx = Box::leak(Box::new(context::Context::new(window)));
- ctx.maximize_canvas();
let game = Box::leak(Box::new(gnew(ctx).await));
let st = Box::leak(Box::new(state::State::new(&ctx)));
// request = Some(Box::new(async {
@@ -64,83 +227,20 @@ where
G = Some(game as *mut G as *mut std::ffi::c_void);
}
- event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
- event_loop.spawn(|event, elwt| {
- 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{..} => {
- 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 {
- button,
- state,
- ..
- } => match state {
- winit::event::ElementState::Pressed => {
- st.mouse_pressed(&ctx, *button, game)
- },
- winit::event::ElementState::Released => {
- st.mouse_released(&ctx, *button)
- },
- }
- 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() {
- 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();
- },
- _ => {},
- }
+ #[cfg(target_arch = "wasm32")]
+ {
+ event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
+ event_loop.spawn(|event, elwt| {
+ event_loop_body::<G>(event, elwt);
});
- });
+ }
+
+ #[cfg(not(target_arch = "wasm32"))]
+ {
+ event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);
+ event_loop.run(|event, elwt| {
+ event_loop_body::<G>(event, elwt);
+ }).expect("window closed");
+ }
}
diff --git a/src/module.rs b/src/module.rs
deleted file mode 100644
index 1fc4100..0000000
--- a/src/module.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use wasm_bindgen::prelude::*;
-
-#[wasm_bindgen]
-pub fn log_info(msg: i8) {
- log::info!("{:?}", msg);
-}
-
-#[wasm_bindgen(module="/src/js/module.js")]
-extern "C" {
- fn js_build_interface() -> js_sys::Object;
-}
-
-pub struct Module {
- pub wasm: js_sys::WebAssembly::Instance,
-}
-
-impl Module {
- pub async fn new(bytes: &[u8]) -> Option<Self> {
- let imp = js_build_interface();
- let o = wasm_bindgen_futures::JsFuture::from(
- js_sys::WebAssembly::instantiate_buffer(bytes, &imp)
- ).await.unwrap();
- let i = js_sys::Reflect::get(&o, &"instance".into()).unwrap();
- if let Ok(wasm) = i.dyn_into::<js_sys::WebAssembly::Instance>() {
- Some(Self {
- wasm,
- })
- } else {
- log::info!("failed 3");
- None
- }
- }
- pub fn call(&self, nm: &str) {
- let exp = self.wasm.exports();
- if let Ok(fo) = js_sys::Reflect::get(&exp, &nm.into()) {
- if let Ok(func) = fo.dyn_into::<js_sys::Function>() {
- let _ = func.call0(&JsValue::undefined());
- } else {
- log::warn!("couldn't cast module function: {}", nm);
- }
- } else {
- log::warn!("couldn't find module function: {}", nm);
- }
- }
-}
diff --git a/src/request.rs b/src/request.rs
deleted file mode 100644
index 7f66f57..0000000
--- a/src/request.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use wasm_bindgen::JsCast;
-
-pub async fn get_store(key: &str) -> Option<String> {
- let mut opts = web_sys::RequestInit::new();
- opts.method("GET");
- opts.mode(web_sys::RequestMode::Cors);
-
- let url = format!("https://colonq.computer/bullfrog/api/get/{}", key);
-
- let request = web_sys::Request::new_with_str_and_init(&url, &opts).ok()?;
-
- let window = web_sys::window().unwrap();
- let resp_value = wasm_bindgen_futures::JsFuture::from(window.fetch_with_request(&request)).await.ok()?;
-
- assert!(resp_value.is_instance_of::<web_sys::Response>());
- let resp: web_sys::Response = resp_value.dyn_into().unwrap();
-
- let text = wasm_bindgen_futures::JsFuture::from(resp.text().ok()?).await.ok()?;
- text.as_string()
-}
diff --git a/src/state.rs b/src/state.rs
index 8c70c51..c5478a9 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -93,9 +93,15 @@ pub struct PointLight {
pub attenuation: glam::Vec2,
}
+#[cfg(target_arch = "wasm32")]
+type Timestamp = f64;
+
+#[cfg(not(target_arch = "wasm32"))]
+type Timestamp = std::time::Instant;
+
pub struct State {
pub acc: f64,
- pub last: f64,
+ pub last: Timestamp,
pub tick: u64,
pub rebinding: Option<Key>,
@@ -119,10 +125,16 @@ pub struct State {
pub log: Vec<(u64, String)>,
}
-pub fn now(ctx: &context::Context) -> f64 {
+#[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 {
+ std::time::Instant::now()
+}
+
pub fn default_keybindings() -> BiHashMap<winit::keyboard::KeyCode, Key> {
BiHashMap::from_iter(vec![
(winit::keyboard::KeyCode::KeyW, Key::Up),
@@ -156,9 +168,12 @@ impl State {
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: 0.0,
- last: now(ctx),
+ 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,
@@ -421,7 +436,12 @@ impl State {
pub fn run_update<G>(&mut self, ctx: &context::Context, game: &mut G) where G: Game {
let now = now(ctx);
+
+ #[cfg(target_arch = "wasm32")]
let diff = now - self.last;
+ #[cfg(not(target_arch = "wasm32"))]
+ let diff = now.duration_since(self.last).as_secs_f64();
+
self.acc += diff;
self.last = now;