use std::f32::INFINITY;
use ::glam::Vec3Swizzles;
use macroquad::prelude::*;
use macroquad::window::miniquad::*;
use thimni::raycast::RayCast;
use thimni::sdf::SDF;
use thimni::utils::{AABB, CollisionParameters};
const VERTEX: &str = r#"#version 100
attribute vec3 position;
attribute vec2 texcoord;
varying mediump vec2 uv;
uniform mat4 Model;
uniform mat4 Projection;
void main() {
gl_Position = Projection * Model * vec4(position, 1);
uv = texcoord;
}"#;
const FRAGMENT: &str = include_str!("resources/raymarch.glsl");
const GRAVITY: ::glam::Vec3 = ::glam::Vec3 {
x: 0.0,
y: 0.1,
z: 0.0,
};
#[derive(Copy, Clone)]
struct YPlane {
h: f32,
thickness: f32,
}
impl SDF<3, ::glam::Vec3> for YPlane {
fn dist(&self, p: ::glam::Vec3) -> f32 {
(-p.y + self.h).abs() - self.thickness
}
fn gradient(
&self,
_p: ::glam::Vec3,
_params: &thimni::utils::CollisionParameters,
) -> ::glam::Vec3 {
-::glam::Vec3::Y
}
fn aabb(&self) -> thimni::utils::AABB<3, ::glam::Vec3> {
AABB {
min: ::glam::Vec3::new(-INFINITY, self.h - self.thickness, -INFINITY),
max: ::glam::Vec3::new(INFINITY, self.h + self.thickness, INFINITY),
}
}
}
#[derive(Copy, Clone)]
struct Sphere {
center: ::glam::Vec3,
radius: f32,
vel: ::glam::Vec3,
floor_cast: RayCast<3, ::glam::Vec3>,
}
impl SDF<3, ::glam::Vec3> for Sphere {
fn dist(&self, p: ::glam::Vec3) -> f32 {
let k = p - self.center;
k.length() - self.radius
}
fn gradient(
&self,
p: ::glam::Vec3,
_params: &thimni::utils::CollisionParameters,
) -> ::glam::Vec3 {
let k = p - self.center;
k.normalize_or_zero()
}
fn aabb(&self) -> AABB<3, ::glam::Vec3> {
AABB {
min: self.center - self.radius,
max: self.center + self.radius,
}
}
}
#[derive(Copy, Clone)]
struct Cylinder {
pub pos: ::glam::Vec3,
pub h: f32,
pub r: f32,
}
impl SDF<3, ::glam::Vec3> for Cylinder {
fn dist(&self, p: ::glam::Vec3) -> f32 {
let k = p - self.pos;
let d = ::glam::Vec2::new(k.xz().length(), k.y).abs() - ::glam::Vec2::new(self.r, self.h);
d.max_element().min(0.0) + d.max(::glam::Vec2::ZERO).length()
}
fn aabb(&self) -> AABB<3, ::glam::Vec3> {
AABB {
min: self.pos + ::glam::Vec3::new(-self.r, -self.h, -self.r),
max: self.pos + ::glam::Vec3::new(self.r, self.h, self.r),
}
}
}
#[macroquad::main("threed")]
async fn main() {
let pipeline_params = PipelineParams {
color_blend: Some(BlendState::new(
Equation::Add,
BlendFactor::Value(BlendValue::SourceAlpha),
BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
)),
..Default::default()
};
let mat = load_material(
ShaderSource::Glsl {
vertex: VERTEX,
fragment: FRAGMENT,
},
MaterialParams {
uniforms: vec![
UniformDesc::new("res", UniformType::Float2),
UniformDesc::new("mouse", UniformType::Float2),
UniformDesc::new("sphere", UniformType::Float4),
],
pipeline_params,
..Default::default()
},
)
.unwrap();
let plane = YPlane {
h: 5.0,
thickness: 0.5,
};
let cylinder = Cylinder {
pos: ::glam::Vec3::new(3.0, 3.0, 0.0),
h: 1.0,
r: 1.0,
};
let mut sphere = Sphere {
center: ::glam::Vec3::new(0.0, -1.0, 3.0),
radius: 1.0,
vel: ::glam::Vec3::new(0.0, 0.0, 0.0),
floor_cast: RayCast::new(::glam::Vec3::new(0.0, -1.0, 3.0), ::glam::Vec3::Y, 1.2),
};
let mut params = CollisionParameters::default();
params.normal_epsilon = 0.01;
loop {
clear_background(BLACK);
gl_use_material(&mat);
mat.set_uniform("res", Vec2::new(screen_width(), screen_height()));
mat.set_uniform("mouse", Vec2::new(0.0, 0.0));
mat.set_uniform("sphere", sphere.center.extend(sphere.radius));
draw_rectangle(0.0, 0.0, screen_width(), screen_height(), WHITE);
sphere.vel.x *= 0.0;
sphere.vel.z *= 0.0;
sphere.floor_cast.origin = sphere.center;
sphere.floor_cast.is_colliding = false;
sphere.floor_cast.query(&plane, ¶ms);
sphere.floor_cast.query(&cylinder, ¶ms);
if is_key_pressed(KeyCode::Space) && sphere.floor_cast.is_colliding {
sphere.vel += ::glam::Vec3::Y * -6.0;
}
sphere.vel += GRAVITY;
let mut dir = ::glam::Vec3::ZERO;
if is_key_down(KeyCode::W) {
dir += ::glam::Vec3::Z * 1.0;
}
if is_key_down(KeyCode::S) {
dir += ::glam::Vec3::Z * -1.0;
}
if is_key_down(KeyCode::D) {
dir += ::glam::Vec3::X * 1.0;
}
if is_key_down(KeyCode::A) {
dir += ::glam::Vec3::X * -1.0;
}
sphere.vel += dir.normalize_or_zero() * 9.0;
if let Some(res) = sphere.get_coll_point(&plane, &mut params) {
let scaled = res.gradient * get_frame_time();
sphere.center += scaled;
let d = sphere.dist(res.point);
sphere.center -= scaled * d;
sphere.vel = scaled;
}
if let Some(res) = sphere.get_coll_point(&cylinder, &mut params) {
let scaled = res.gradient * get_frame_time();
sphere.center += scaled;
let d = sphere.dist(res.point);
sphere.center -= scaled * d;
sphere.vel = scaled;
}
sphere.center += sphere.vel * get_frame_time();
next_frame().await
}
}