diff --git a/Cargo.toml b/Cargo.toml index 9992349f..981141d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ all-features = true miniquad = { version = "0.4", features = ["log-impl"] } quad-rand = "0.2.1" glam = {version = "0.24", features = ["scalar-math","mint"] } -fontdue = { version = "0.8", default-features=false, features = ["std"] } +fontdue = { git = "https://github.com/not-fl3/fontdue.git", default-features=false, features = ["std"] } backtrace = { version = "0.3.60", optional = true, default-features = false, features = [ "std", "libbacktrace" ] } log = { version = "0.4", optional = true } quad-snd = { version = "0.2", optional = true } @@ -43,13 +43,6 @@ quad-gl = { git = "https://github.com/not-fl3/quad-gl", branch = "v0.4" } [dev-dependencies] dolly = "*" -#macroquad-particles = { path = "./particles" } -#macroquad-tiled = { path = "./tiled" } -#macroquad-profiler = { path = "./profiler" } -#macroquad-platformer = { path = "./physics-platformer" } - -# workaround to fix the examples -# this allows to use macroquad-particles in examples without two versions of #[patch."https://github.com/not-fl3/quad-gl"] #quad-gl = { path = '../quad-gl' } @@ -62,8 +55,7 @@ macroquad = { path = './' } #quad-snd = {path = '../quad-snd'} #miniquad = { path = '../miniquad' } -fontdue = { git = 'https://github.com/not-fl3/fontdue.git' } -miniquad = { git = 'https://github.com/not-fl3/miniquad.git' } +#miniquad = { git = 'https://github.com/not-fl3/miniquad.git' } #fontdue = { path = '../fontdue' } #quad-gl = {path = '../quad-gl'} #zune-jpeg = { path = "../zune-image/zune-jpeg" } diff --git a/examples/basic_shapes_compat.rs b/examples/basic_shapes_compat.rs index ae47b45c..407359fc 100644 --- a/examples/basic_shapes_compat.rs +++ b/examples/basic_shapes_compat.rs @@ -1,7 +1,7 @@ use macroquad::compat::*; async fn game(ctx: macroquad::Context) { - init_compat_mode(ctx); + init_compat_mode(&ctx); loop { clear_background(LIGHTGRAY); diff --git a/examples/gizmos/main.rs b/examples/gizmos/main.rs new file mode 100644 index 00000000..01c962de --- /dev/null +++ b/examples/gizmos/main.rs @@ -0,0 +1,68 @@ +use macroquad::{ + file::load_file, + gizmos::{draw_gizmos, gizmos_add_line, init_gizmos}, + math::{vec2, vec3}, + quad_gl::{camera::Environment, color}, + time::get_time, + window::next_frame, +}; + +mod orbit_camera; + +async fn game(ctx: macroquad::Context) { + unsafe { + macroquad::miniquad::gl::glEnable(macroquad::miniquad::gl::GL_TEXTURE_CUBE_MAP_SEAMLESS) + }; + + init_gizmos(&ctx); + + let mut scene = ctx.new_scene(); + + let heightmap = load_file("examples/ferris.png").await.unwrap(); + let heightmap = quad_gl::image::decode(&heightmap).unwrap(); + + let indices = vec![0u16, 1, 2, 0, 2, 3]; + + let vertices = vec![ + vec3(-0.5, 0., -0.5), + vec3(-0.5, 0., 0.5), + vec3(0.5, 0., 0.5), + vec3(0.5, 0., -0.5), + ]; + + for v in &vertices { + gizmos_add_line(true, *v, *v + vec3(0.0, 0.1, 0.0)); + } + + let uvs = vec![vec2(0., 1.), vec2(0., 0.), vec2(1., 0.), vec2(1., 1.)]; + let normals = vec![vec3(0., 1., 0.); 4]; + let mesh = quad_gl::models::CpuMesh(vertices, uvs, normals, indices); + let mesh = ctx.mesh( + mesh, + Some(ctx.new_texture_from_rgba8( + heightmap.width as _, + heightmap.height as _, + &heightmap.data, + )), + ); + let _mesh = scene.add_model(&mesh); + let mut orbit = orbit_camera::OrbitCamera::new(); + + loop { + let t = get_time(); + let mut p = vec3(t.sin() as f32, 0.0, t.cos() as f32); + + gizmos_add_line(true, p, p * 1.2); + gizmos_add_line(false, vec3(0.0, 0.0, 0.0), p); + + ctx.clear_screen(color::WHITE); + orbit.orbit(&ctx); + scene.draw(&orbit.camera); + draw_gizmos(&orbit.camera); + next_frame().await + } +} + +fn main() { + macroquad::start(Default::default(), |ctx| game(ctx)); +} diff --git a/examples/gizmos/orbit_camera.rs b/examples/gizmos/orbit_camera.rs new file mode 100644 index 00000000..5a8c1d92 --- /dev/null +++ b/examples/gizmos/orbit_camera.rs @@ -0,0 +1,65 @@ +use dolly::prelude::*; +use macroquad::{ + input::MouseButton, + math::{vec3, Vec3}, + quad_gl::{ + camera::{Camera, Environment, Projection}, + color, + }, +}; + +pub struct OrbitCamera { + dolly_rig: CameraRig, + pub camera: Camera, + zoom: f32, +} + +impl OrbitCamera { + pub fn new() -> OrbitCamera { + let dolly_rig: CameraRig = CameraRig::builder() + .with(YawPitch::new().yaw_degrees(45.0).pitch_degrees(-10.0)) + .with(Smooth::new_rotation(0.7)) + .with(Arm::new(Vec3::Z * 4.0)) + .build(); + + let camera = Camera { + environment: Environment::SolidColor(color::WHITE), + depth_enabled: true, + projection: Projection::Perspective, + position: vec3(0., 1.5, 4.), + up: vec3(0., 1., 0.), + target: vec3(0., 0., 0.), + z_near: 0.1, + z_far: 1500.0, + ..Default::default() + }; + + OrbitCamera { + dolly_rig, + camera, + zoom: 4.0, + } + } + + pub fn orbit(&mut self, ctx: ¯oquad::Context) { + if !ctx.root_ui().is_mouse_over(ctx.mouse_position()) + && ctx.is_mouse_button_down(MouseButton::Left) + { + self.dolly_rig + .driver_mut::() + .rotate_yaw_pitch(ctx.mouse_delta().x * 100., ctx.mouse_delta().y * 100.); + } + if ctx.mouse_wheel().1 != 0.0 { + self.zoom -= ctx.mouse_wheel().1 * 0.4; + self.zoom = self.zoom.clamp(1.8, 10.0); + self.dolly_rig.driver_mut::().offset = (Vec3::Z * self.zoom).into(); + } + let delta = 0.1; + let dolly_transform = self.dolly_rig.update(delta); + self.camera.position = dolly_transform.position.into(); + self.camera.up = dolly_transform.up(); + let p: Vec3 = dolly_transform.position.into(); + let f: Vec3 = dolly_transform.forward::().into(); + self.camera.target = p + f; + } +} diff --git a/src/compat.rs b/src/compat.rs index e017ff21..8ef7be75 100644 --- a/src/compat.rs +++ b/src/compat.rs @@ -3,10 +3,13 @@ pub use crate::window::next_frame; pub use quad_gl::color::*; -use std::cell::RefCell; +use std::{ + cell::RefCell, + sync::{Arc, Mutex}, +}; pub struct CompatContext { - ctx: crate::Context, + quad_ctx: Arc>>, canvas: quad_gl::sprite_batcher::SpriteBatcher, } @@ -17,11 +20,12 @@ thread_local! { fn with_ctx(f: F) { CTX.with_borrow_mut(|v| f(v.as_mut().unwrap())); } -pub fn init_compat_mode(ctx: crate::Context) { +pub fn init_compat_mode(ctx: &crate::Context) { let canvas = ctx.new_canvas(); + let quad_ctx = ctx.quad_ctx.clone(); CTX.with_borrow_mut(|v| { - *v = Some(CompatContext { ctx, canvas }); + *v = Some(CompatContext { quad_ctx, canvas }); }); } @@ -35,8 +39,7 @@ pub(crate) fn end_frame() { } pub fn clear_background(color: Color) { with_ctx(|ctx| { - ctx.ctx - .quad_ctx + ctx.quad_ctx .lock() .unwrap() .clear(Some((1., 1., 1., 1.)), None, None) diff --git a/src/gizmos.rs b/src/gizmos.rs new file mode 100644 index 00000000..a997b9af --- /dev/null +++ b/src/gizmos.rs @@ -0,0 +1,79 @@ +pub use crate::{math::Vec3, window::next_frame}; +pub use quad_gl::{ + color::*, + draw_calls_batcher::{DrawCallsBatcher, DrawMode}, +}; + +use std::{ + cell::RefCell, + sync::{Arc, Mutex}, +}; + +struct Line { + persist: bool, + p0: Vec3, + p1: Vec3, +} + +pub struct Gizmos { + quad_ctx: Arc>>, + canvas: quad_gl::sprite_batcher::SpriteBatcher, + lines: Vec, +} + +thread_local! { + pub static CTX: RefCell> = { RefCell::new(None) }; +} + +fn with_ctx(f: F) { + CTX.with_borrow_mut(|v| f(v.as_mut().unwrap())); +} +pub fn init_gizmos(ctx: &crate::Context) { + let canvas = ctx.new_canvas(); + let quad_ctx = ctx.quad_ctx.clone(); + + CTX.with_borrow_mut(|v| { + *v = Some(Gizmos { + quad_ctx, + canvas, + lines: vec![], + }); + }); +} + +fn draw_line(gl: &mut DrawCallsBatcher, p0: Vec3, p1: Vec3) { + let uv = [0., 0.]; + let color: [f32; 4] = [0.0, 0.0, 1.0, 1.0]; + let indices = [0, 1]; + + let line = [ + ([p0.x, p0.y, p0.z], uv, color), + ([p1.x, p1.y, p1.z], uv, color), + ]; + gl.texture(None); + gl.draw_mode(DrawMode::Lines); + gl.geometry(&line[..], &indices); +} + +pub fn draw_gizmos(camera: &quad_gl::camera::Camera) { + if CTX.with_borrow(|ctx| ctx.is_some()) { + with_ctx(|ctx| { + let mut gl = ctx.canvas.gl(); + gl.depth_test(true); + for line in &mut ctx.lines { + draw_line(gl, line.p0, line.p1); + } + + ctx.canvas.draw2(camera); + ctx.canvas.reset(); + + ctx.lines.retain(|line| line.persist); + }); + } +} + +pub fn gizmos_add_line(persist: bool, p0: Vec3, p1: Vec3) { + with_ctx(|ctx| { + ctx.lines.push(Line { persist, p0, p1 }); + }); +} diff --git a/src/lib.rs b/src/lib.rs index 5857b20c..ddceed3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,11 +72,14 @@ pub use ::log as logging; pub use miniquad; pub mod compat; +pub mod gizmos; pub mod resources; pub use quad_gl; pub use quad_gl::math; +use quad_gl::{models::CpuMesh, scene::Model, texture::Image, texture::Texture2D}; + use glam::{vec2, Mat4, Vec2}; use std::sync::{Arc, Mutex, Weak}; @@ -400,23 +403,23 @@ impl Context { quad_gl.new_scene() } - pub fn new_texture_from_image( - &self, - image: &quad_gl::texture::Image, - ) -> quad_gl::texture::Texture2D { + pub fn new_texture_from_image(&self, image: &Image) -> Texture2D { self.quad_gl.lock().unwrap().from_image(&image) } + pub fn new_texture_from_rgba8(&self, width: u16, height: u16, data: &[u8]) -> Texture2D { + self.quad_gl + .lock() + .unwrap() + .from_rgba8(width, height, &data) + } + pub fn mesh(&self, mesh: CpuMesh, texture: Option) -> Model { + self.quad_gl.lock().unwrap().mesh(mesh, texture) + } + pub fn root_ui<'a>(&'a self) -> impl std::ops::DerefMut + 'a { self.ui.lock().unwrap() } - // pub fn new_scene(&self) -> scene::Scene { - // scene::Scene::new(self.quad_ctx.clone(), self.fonts_storage.clone()) - // } - - // pub fn new_canvas(&self) -> sprite_layer::SpriteLayer { - // //sprite_layer::SpriteLayer::new(self.quad_ctx.clone(), self.fonts_storage.clone()) - // } } pub fn start Fut + 'static, Fut: Future + 'static>( diff --git a/src/scene/frustum.rs b/src/scene/frustum.rs deleted file mode 100644 index c51b1bf8..00000000 --- a/src/scene/frustum.rs +++ /dev/null @@ -1,81 +0,0 @@ -use glam::{vec3, vec4, Mat4, Vec3, Vec4, Vec4Swizzles}; - -use crate::{camera::Camera, scene::AABB}; - -#[derive(Debug, Default, Clone, Copy)] -pub struct Plane { - pub normal: Vec3, - pub distance: f32, -} -impl Plane { - pub fn new(normal: Vec3, distance: f32) -> Plane { - Plane { normal, distance } - } - - pub fn from_point_and_normal(point: Vec3, normal: Vec3) -> Plane { - let normal = normal.normalize(); - let distance = normal.dot(point); - Plane { normal, distance } - } - - pub fn signed_distance_to_plane(&self, point: Vec3) -> f32 { - self.normal.dot(point) - self.distance - } - - pub fn clip(&self, aabb: AABB) -> bool { - let points = [ - vec3(aabb.min.x, aabb.min.y, aabb.min.z), - vec3(aabb.min.x, aabb.max.y, aabb.min.z), - vec3(aabb.max.x, aabb.max.y, aabb.min.z), - vec3(aabb.max.x, aabb.min.y, aabb.min.z), - vec3(aabb.min.x, aabb.min.y, aabb.max.z), - vec3(aabb.min.x, aabb.max.y, aabb.max.z), - vec3(aabb.max.x, aabb.max.y, aabb.max.z), - vec3(aabb.max.x, aabb.min.y, aabb.max.z), - ]; - points.into_iter().any(|p| self.signed_distance_to_plane(*p) > 0.0) - } -} - -pub fn projection_planes(camera: &Camera) -> [Plane; 6] { - let camera_direction = (camera.target - camera.position).normalize(); - let camera_right = camera_direction.cross(camera.up); - let far = camera_direction * camera.z_far; - let near = camera_direction * camera.z_near; - - let (proj, view) = camera.proj_view(); - let inv = (proj * view).inverse(); - let mut ndc = [ - vec4(-1.0, 0.0, 1.0, 1.0), - vec4(1.0, 0.0, 1.0, 1.0), - vec4(0.0, 1.0, 1.0, 1.0), - vec4(0.0, -1.0, 1.0, 1.0), - ]; - for ndc in &mut ndc { - *ndc = inv * *ndc; - *ndc /= ndc.w; - } - - let planes = [ - Plane::from_point_and_normal(camera.position + near, camera_direction), - Plane::from_point_and_normal(camera.position + far, -camera_direction), - Plane::from_point_and_normal( - camera.position, - (ndc[0].xyz() - camera.position).cross(camera.up), - ), - Plane::from_point_and_normal( - camera.position, - camera.up.cross(ndc[1].xyz() - camera.position), - ), - Plane::from_point_and_normal( - camera.position, - (ndc[2].xyz() - camera.position).cross(camera_right), - ), - Plane::from_point_and_normal( - camera.position, - camera_right.cross(ndc[3].xyz() - camera.position), - ), - ]; - - planes -} diff --git a/src/time.rs b/src/time.rs index f416e858..8bcac420 100644 --- a/src/time.rs +++ b/src/time.rs @@ -13,15 +13,15 @@ // context.frame_time as f32 // } -// /// Returns elapsed wall-clock time in seconds since start -// /// -// /// Note that as real world time progresses during computation, -// /// the value returned will change. Therefore if you want -// /// your game logic update to happen at the same *in-game* time -// /// for all game objects, you should call this function once -// /// save the value and reuse it throughout your code. -// pub fn get_time() -> f64 { -// //let context = get_context(); +/// Returns elapsed wall-clock time in seconds since start +/// +/// Note that as real world time progresses during computation, +/// the value returned will change. Therefore if you want +/// your game logic update to happen at the same *in-game* time +/// for all game objects, you should call this function once +/// save the value and reuse it throughout your code. +pub fn get_time() -> f64 { + //let context = get_context(); -// miniquad::date::now() -// } + miniquad::date::now() +}