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/fractal.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,
}
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,
}
}
}
fn box_sdf(p: ::glam::Vec3, b: ::glam::Vec3) -> f32 {
let q = p.abs() - b;
q.max_element()
}
#[derive(Copy, Clone)]
struct Menger {
pub pos: ::glam::Vec3,
pub alpha: f32,
pub beta: f32,
pub size: f32,
}
impl SDF<3, ::glam::Vec3> for Menger {
fn dist(&self, pk: ::glam::Vec3) -> f32 {
let mut p = pk - self.pos;
let mut size = self.size;
let s = [
::glam::Vec3::new(1.0, 1.0, 1.0),
::glam::Vec3::new(1.0, 1.0, 0.0),
];
for _ in 0..5 {
let mut t = p.xz();
t = ::glam::Mat2::from_angle(self.alpha) * t;
p.x = t.x;
p.z = t.y;
let mut t = p.yz();
t = ::glam::Mat2::from_angle(self.beta) * t;
p.y = t.x;
p.z = t.y;
p = p.abs();
if p.y > p.x {
let t = p.y;
p.y = p.x;
p.x = t;
}
if p.z > p.y {
let t = p.y;
p.y = p.z;
p.z = t;
}
p -= size * s[if p.z > 0.5 * size { 0 } else { 1 }];
size /= 3.0;
}
box_sdf(p, ::glam::Vec3::ONE * 1.5 * size)
}
fn aabb(&self) -> AABB<3, ::glam::Vec3> {
let r = self.size * 2.5;
AABB {
min: self.pos + ::glam::Vec3::new(-r, -r, -r),
max: self.pos + ::glam::Vec3::new(r, r, 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),
UniformDesc::new("menger_pos", UniformType::Float3),
UniformDesc::new("menger_params", UniformType::Float3),
],
pipeline_params,
..Default::default()
},
)
.unwrap();
let plane = YPlane {
h: 5.0,
thickness: 0.5,
};
let mut sponge = Menger {
pos: ::glam::Vec3::new(3.0, 3.0, 0.0),
alpha: 1.0,
beta: 0.5,
size: 2.0,
};
let mut sphere = Sphere {
center: ::glam::Vec3::new(0.0, -2.0, 2.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;
params.collision_epsilon = 0.001;
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.2));
mat.set_uniform("sphere", sphere.center.extend(sphere.radius));
mat.set_uniform("menger_pos", sponge.pos);
mat.set_uniform("menger_params", [sponge.alpha, sponge.beta, sponge.size]);
draw_rectangle(0.0, 0.0, screen_width(), screen_height(), WHITE);
sphere.floor_cast.origin = sphere.center;
sphere.floor_cast.is_colliding = false;
sphere.floor_cast.query(&plane, ¶ms);
sphere.floor_cast.query(&sponge, ¶ms);
sphere.vel.x *= 0.0;
sphere.vel.z *= 0.0;
if is_key_pressed(KeyCode::Space) && sphere.floor_cast.is_colliding {
sphere.vel += ::glam::Vec3::Y * -12.0;
}
if is_key_pressed(KeyCode::R) {
sphere.center = ::glam::Vec3::new(0.0, -2.0, 2.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(&sponge, &mut params) {
let scaled = res.gradient * 1.0 * get_frame_time();
sphere.center += scaled;
let d = sphere.dist(res.point);
sphere.center -= scaled * d;
sphere.vel = res.gradient;
}
sphere.center += sphere.vel * get_frame_time();
let speed = 0.3;
let range = 0.8;
sponge.alpha = (get_time() as f32 * speed).sin() * range;
sponge.beta = (get_time() as f32 * speed).cos() * range;
next_frame().await
}
}