Skip to content

Commit

Permalink
Converted nodes to be SDF based
Browse files Browse the repository at this point in the history
  • Loading branch information
markusmoenig committed Mar 29, 2024
1 parent e56f8aa commit 96878ff
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 97 deletions.
22 changes: 15 additions & 7 deletions creator/src/modelfxeditor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,27 @@ impl ModelFXEditor {
let mut toolbar_canvas = TheCanvas::default();
let mut toolbar_hlayout = TheHLayout::new(TheId::empty());
toolbar_hlayout.limiter_mut().set_max_height(25);
toolbar_hlayout.set_margin(vec4i(10, 2, 5, 3));
toolbar_hlayout.set_margin(vec4i(70, 2, 5, 3));

let mut add_wall_button = TheTraybarButton::new(TheId::named("ModelFX Add Wall"));
let mut floors_button = TheTraybarButton::new(TheId::named("ModelFX Add Floor"));
//add_button.set_icon_name("icon_role_add".to_string());
add_wall_button.set_text(str!("Add Wall"));
add_wall_button.set_status_text("Clears the currently selected marker.");
floors_button.set_text(str!("Floor & Furniture"));
floors_button.set_status_text("Nodes which model floors and furniture like tables.");

add_wall_button.set_context_menu(Some(TheContextMenu {
let mut walls_button = TheTraybarButton::new(TheId::named("ModelFX Add Wall"));
//add_button.set_icon_name("icon_role_add".to_string());
walls_button.set_text(str!("Wall & Components"));
walls_button.set_status_text(
"Nodes which model walls and components like windows, doors and decoration.",
);

walls_button.set_context_menu(Some(TheContextMenu {
items: vec![
TheContextMenuItem::new(
"Wall Horizontal".to_string(),
TheId::named("Wall Horizontal"),
),
TheContextMenuItem::new("Vertical Wall".to_string(), TheId::named("Vertical Wall")),
TheContextMenuItem::new("Wall Vertical".to_string(), TheId::named("Wall Vertical")),
],
..Default::default()
}));
Expand All @@ -60,7 +67,8 @@ impl ModelFXEditor {
zoom.set_continuous(true);
zoom.limiter_mut().set_max_width(120);

toolbar_hlayout.add_widget(Box::new(add_wall_button));
toolbar_hlayout.add_widget(Box::new(floors_button));
toolbar_hlayout.add_widget(Box::new(walls_button));
toolbar_hlayout.add_widget(Box::new(zoom));
toolbar_hlayout.set_reverse_index(Some(1));

Expand Down
2 changes: 2 additions & 0 deletions shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod regionfx;
pub mod renderer;
pub mod renderer_utils;
pub mod screen;
pub mod sdf3d;
pub mod server;
pub mod tilearea;
pub mod tiledrawer;
Expand Down Expand Up @@ -43,6 +44,7 @@ pub mod prelude {
pub use crate::renderer::Renderer;
//pub use crate::renderer_utils::*;
pub use crate::screen::*;
pub use crate::sdf3d::*;
pub use crate::server::context::ServerContext;
pub use crate::server::{Server, ServerState};
pub use crate::tilearea::TileArea;
Expand Down
109 changes: 94 additions & 15 deletions shared/src/modelfx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,23 +314,23 @@ impl ModelFX {
camera_offset,
);

let mut hit: Option<Hit> = None;

for fx in self.nodes.iter() {
if let Some(h) = fx.hit(&ray) {
if let Some(chit) = hit.clone() {
if h.distance < chit.distance {
hit = Some(h);
}
} else {
hit = Some(h);
}
let max_t = 3.0 * 1.732;
let mut t = 0.0;

let mut p = ray.at(t);

while t < max_t {
let d = self.distance(p);
if d < 0.001 {
break;
}
t += d;
p = ray.at(t);
}

if let Some(hit) = hit {
let c =
dot(hit.normal, normalize(vec3f(1.0, 2.0, 3.0))) * 0.5 + 0.5;
if t < max_t {
let normal = self.normal(p);
let c = dot(normal, normalize(vec3f(1.0, 2.0, 3.0))) * 0.5 + 0.5;
color.x = c;
color.y = c;
color.z = c;
Expand Down Expand Up @@ -391,8 +391,40 @@ impl ModelFX {
camera_offset,
);

let hit: Option<Hit> = node.hit(&ray);
let max_t = 3.0 * 1.732;
let mut t = 0.0;

let mut p = ray.at(t);

while t < max_t {
let d = node.distance(p);
if d < 0.001 {
break;
}
t += d;
p = ray.at(t);
}

if t < max_t {
let normal = self.normal_node(p, node);

let nx = normal.x.abs();
let ny = normal.y.abs();
let nz = normal.z.abs();

if nx > ny && nx > nz {
// X-face
color = TheColor::from_u8_array(RED).to_vec4f();
} else if ny > nx && ny > nz {
// Y-face
color = TheColor::from_u8_array(YELLOW).to_vec4f();
} else {
// Z-face
color = TheColor::from_u8_array(BLUE).to_vec4f();
}
}

/*
if let Some(hit) = hit {
if hit.face == HitFace::XFace {
color = TheColor::from_u8_array(RED).to_vec4f();
Expand All @@ -404,6 +436,7 @@ impl ModelFX {
color = TheColor::from_u8_array(BLUE).to_vec4f();
}
}
*/

total += color;
}
Expand All @@ -419,4 +452,50 @@ impl ModelFX {
}
});
}

/// Get the distance at the given position for all nodes.
#[inline(always)]
pub fn distance(&self, p: Vec3f) -> f32 {
let mut d = f32::MAX;
for fx in self.nodes.iter() {
d = d.min(fx.distance(p));
}
d
}

pub fn normal(&self, p: Vec3f) -> Vec3f {
let scale = 0.5773 * 0.0005;
let e = vec2f(1.0 * scale, -1.0 * scale);

// IQs normal function

let e1 = vec3f(e.x, e.y, e.y);
let e2 = vec3f(e.y, e.y, e.x);
let e3 = vec3f(e.y, e.x, e.y);
let e4 = vec3f(e.x, e.x, e.x);

let n = e1 * self.distance(p + e1)
+ e2 * self.distance(p + e2)
+ e3 * self.distance(p + e3)
+ e4 * self.distance(p + e4);
normalize(n)
}

pub fn normal_node(&self, p: Vec3f, node: &ModelFXNode) -> Vec3f {
let scale = 0.5773 * 0.0005;
let e = vec2f(1.0 * scale, -1.0 * scale);

// IQs normal function

let e1 = vec3f(e.x, e.y, e.y);
let e2 = vec3f(e.y, e.y, e.x);
let e3 = vec3f(e.y, e.x, e.y);
let e4 = vec3f(e.x, e.x, e.x);

let n = e1 * node.distance(p + e1)
+ e2 * node.distance(p + e2)
+ e3 * node.distance(p + e3)
+ e4 * node.distance(p + e4);
normalize(n)
}
}
90 changes: 15 additions & 75 deletions shared/src/modelfxnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ impl ModelFXNode {
}
}

/// Ray hit test for the ModelFX.
pub fn hit(&self, ray: &Ray) -> Option<Hit> {
/// Returns the distance to p.
pub fn distance(&self, p: Vec3f) -> f32 {
match self {
Self::WallHorizontal(collection) => {
let position = collection.get_f32_default("Position", 0.5);
Expand All @@ -76,9 +76,12 @@ impl ModelFXNode {
min -= adjustment;
max -= adjustment;
}
let aabb_min = Vec3f::new(0.0, 0.0, min);
let aabb_max = Vec3f::new(1.0, 1.0, max);
self.ray_aabb(ray, aabb_min, aabb_max)
// let aabb_min = Vec3f::new(0.0, 0.0, min);
// let aabb_max = Vec3f::new(1.0, 1.0, max);
sd_box(
p - vec3f(0.5, 0.5, (min + max) / 2.0),
vec3f(0.5, 0.5, (max - min) / 2.0),
)
}
Self::WallVertical(collection) => {
let position = collection.get_f32_default("Position", 0.5);
Expand All @@ -95,80 +98,17 @@ impl ModelFXNode {
min -= adjustment;
max -= adjustment;
}
let aabb_min = Vec3f::new(min, 0.0, 0.0);
let aabb_max = Vec3f::new(max, 1.0, 1.0);
self.ray_aabb(ray, aabb_min, aabb_max)
//let aabb_min = Vec3f::new(min, 0.0, 0.0);
//let aabb_max = Vec3f::new(max, 1.0, 1.0);
//self.ray_aabb(ray, aabb_min, aabb_max)
sd_box(
p - vec3f((min + max) / 2.0, 0.5, 0.5),
vec3f((max - min) / 2.0, 0.5, 0.5),
)
}
}
}

/// Ray AABB / Cube hit test.
pub fn ray_aabb(&self, ray: &Ray, aabb_min: Vec3f, aabb_max: Vec3f) -> Option<Hit> {
let t0s = (aabb_min - ray.o) * ray.inv_direction;
let t1s = (aabb_max - ray.o) * ray.inv_direction;

let mut tmin = f32::NEG_INFINITY;
let mut tmax = f32::INFINITY;
let mut normal = Vec3::new(0.0, 0.0, 0.0);

for i in 0..3 {
let axis_normal = match i {
0 => Vec3f::new(1.0, 0.0, 0.0),
1 => Vec3f::new(0.0, 1.0, 0.0),
_ => Vec3f::new(0.0, 0.0, 1.0),
};
if ray.inv_direction[i] >= 0.0 {
if t0s[i] > tmin {
tmin = t0s[i];
normal = axis_normal * -1.0; // Invert the normal if we're hitting the min side
}
tmax = tmax.min(t1s[i]);
} else {
if t1s[i] > tmin {
tmin = t1s[i];
normal = axis_normal; // Normal points in the positive axis direction
}
tmax = tmax.min(t0s[i]);
}
}

if tmax >= tmin && tmin >= 0.0 {
// Calculate intersection point
let hit_point = ray.o + ray.d * tmin;
let mut face = HitFace::XFace;

// Determine which face of the box was hit and calculate UV coordinates
let mut u = 0.0;
let mut v = 0.0;
if normal == Vec3::new(1.0, 0.0, 0.0) || normal == Vec3::new(-1.0, 0.0, 0.0) {
// Hit the X face
v = 1.0 - (hit_point.y - aabb_min.y) / (aabb_max.y - aabb_min.y);
u = (hit_point.z - aabb_min.z) / (aabb_max.z - aabb_min.z);
face = HitFace::XFace;
} else if normal == Vec3::new(0.0, 1.0, 0.0) || normal == Vec3::new(0.0, -1.0, 0.0) {
// Hit the Y face
u = (hit_point.x - aabb_min.x) / (aabb_max.x - aabb_min.x);
v = (hit_point.z - aabb_min.z) / (aabb_max.z - aabb_min.z);
face = HitFace::YFace;
} else if normal == Vec3::new(0.0, 0.0, 1.0) || normal == Vec3::new(0.0, 0.0, -1.0) {
// Hit the Z face
u = (hit_point.x - aabb_min.x) / (aabb_max.x - aabb_min.x);
v = 1.0 - (hit_point.y - aabb_min.y) / (aabb_max.y - aabb_min.y);
face = HitFace::ZFace;
}

Some(Hit {
distance: tmin,
hit_point,
normal,
uv: vec2f(u, v),
face,
})
} else {
None
}
}

/// Convert to kind.
pub fn to_kind(&self) -> String {
match self {
Expand Down
8 changes: 8 additions & 0 deletions shared/src/sdf3d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use theframework::prelude::*;

// From IQs SDF3D page at https://iquilezles.org/articles/distfunctions/

pub fn sd_box(p: Vec3f, b: Vec3f) -> f32 {
let q = abs(p) - b;
length(max(q, Vec3f::zero())) + min(max(q.x, max(q.y, q.z)), 0.0)
}

0 comments on commit 96878ff

Please sign in to comment.