use crate::core::{
algebra::{Matrix4, Point3, Vector3},
color::Color,
math::{aabb::AxisAlignedBoundingBox, frustum::Frustum, Matrix4Ext},
};
use rg3d_core::algebra::Vector2;
use std::ops::Range;
#[derive(Clone, Debug)]
pub struct Line {
pub begin: Vector3<f32>,
pub end: Vector3<f32>,
pub color: Color,
}
#[derive(Default, Clone, Debug)]
pub struct SceneDrawingContext {
pub lines: Vec<Line>,
}
impl SceneDrawingContext {
pub fn draw_frustum(&mut self, frustum: &Frustum, color: Color) {
let left_top_front = frustum.left_top_front_corner();
let left_bottom_front = frustum.left_bottom_front_corner();
let right_bottom_front = frustum.right_bottom_front_corner();
let right_top_front = frustum.right_top_front_corner();
let left_top_back = frustum.left_top_back_corner();
let left_bottom_back = frustum.left_bottom_back_corner();
let right_bottom_back = frustum.right_bottom_back_corner();
let right_top_back = frustum.right_top_back_corner();
self.add_line(Line {
begin: left_top_front,
end: right_top_front,
color,
});
self.add_line(Line {
begin: right_top_front,
end: right_bottom_front,
color,
});
self.add_line(Line {
begin: right_bottom_front,
end: left_bottom_front,
color,
});
self.add_line(Line {
begin: left_bottom_front,
end: left_top_front,
color,
});
self.add_line(Line {
begin: left_top_back,
end: right_top_back,
color,
});
self.add_line(Line {
begin: right_top_back,
end: right_bottom_back,
color,
});
self.add_line(Line {
begin: right_bottom_back,
end: left_bottom_back,
color,
});
self.add_line(Line {
begin: left_bottom_back,
end: left_top_back,
color,
});
self.add_line(Line {
begin: left_top_front,
end: left_top_back,
color,
});
self.add_line(Line {
begin: right_top_front,
end: right_top_back,
color,
});
self.add_line(Line {
begin: right_bottom_front,
end: right_bottom_back,
color,
});
self.add_line(Line {
begin: left_bottom_front,
end: left_bottom_back,
color,
});
}
pub fn draw_aabb(&mut self, aabb: &AxisAlignedBoundingBox, color: Color) {
let left_bottom_front = Vector3::new(aabb.min.x, aabb.min.y, aabb.max.z);
let left_top_front = Vector3::new(aabb.min.x, aabb.max.y, aabb.max.z);
let right_top_front = Vector3::new(aabb.max.x, aabb.max.y, aabb.max.z);
let right_bottom_front = Vector3::new(aabb.max.x, aabb.min.y, aabb.max.z);
let left_bottom_back = Vector3::new(aabb.min.x, aabb.min.y, aabb.min.z);
let left_top_back = Vector3::new(aabb.min.x, aabb.max.y, aabb.min.z);
let right_top_back = Vector3::new(aabb.max.x, aabb.max.y, aabb.min.z);
let right_bottom_back = Vector3::new(aabb.max.x, aabb.min.y, aabb.min.z);
self.add_line(Line {
begin: left_top_front,
end: right_top_front,
color,
});
self.add_line(Line {
begin: right_top_front,
end: right_bottom_front,
color,
});
self.add_line(Line {
begin: right_bottom_front,
end: left_bottom_front,
color,
});
self.add_line(Line {
begin: left_bottom_front,
end: left_top_front,
color,
});
self.add_line(Line {
begin: left_top_back,
end: right_top_back,
color,
});
self.add_line(Line {
begin: right_top_back,
end: right_bottom_back,
color,
});
self.add_line(Line {
begin: right_bottom_back,
end: left_bottom_back,
color,
});
self.add_line(Line {
begin: left_bottom_back,
end: left_top_back,
color,
});
self.add_line(Line {
begin: left_top_front,
end: left_top_back,
color,
});
self.add_line(Line {
begin: right_top_front,
end: right_top_back,
color,
});
self.add_line(Line {
begin: right_bottom_front,
end: right_bottom_back,
color,
});
self.add_line(Line {
begin: left_bottom_front,
end: left_bottom_back,
color,
});
}
pub fn draw_oob(
&mut self,
aabb: &AxisAlignedBoundingBox,
transform: Matrix4<f32>,
color: Color,
) {
let left_bottom_front = transform
.transform_point(&Point3::new(aabb.min.x, aabb.min.y, aabb.max.z))
.coords;
let left_top_front = transform
.transform_point(&Point3::new(aabb.min.x, aabb.max.y, aabb.max.z))
.coords;
let right_top_front = transform
.transform_point(&Point3::new(aabb.max.x, aabb.max.y, aabb.max.z))
.coords;
let right_bottom_front = transform
.transform_point(&Point3::new(aabb.max.x, aabb.min.y, aabb.max.z))
.coords;
let left_bottom_back = transform
.transform_point(&Point3::new(aabb.min.x, aabb.min.y, aabb.min.z))
.coords;
let left_top_back = transform
.transform_point(&Point3::new(aabb.min.x, aabb.max.y, aabb.min.z))
.coords;
let right_top_back = transform
.transform_point(&Point3::new(aabb.max.x, aabb.max.y, aabb.min.z))
.coords;
let right_bottom_back = transform
.transform_point(&Point3::new(aabb.max.x, aabb.min.y, aabb.min.z))
.coords;
self.add_line(Line {
begin: left_top_front,
end: right_top_front,
color,
});
self.add_line(Line {
begin: right_top_front,
end: right_bottom_front,
color,
});
self.add_line(Line {
begin: right_bottom_front,
end: left_bottom_front,
color,
});
self.add_line(Line {
begin: left_bottom_front,
end: left_top_front,
color,
});
self.add_line(Line {
begin: left_top_back,
end: right_top_back,
color,
});
self.add_line(Line {
begin: right_top_back,
end: right_bottom_back,
color,
});
self.add_line(Line {
begin: right_bottom_back,
end: left_bottom_back,
color,
});
self.add_line(Line {
begin: left_bottom_back,
end: left_top_back,
color,
});
self.add_line(Line {
begin: left_top_front,
end: left_top_back,
color,
});
self.add_line(Line {
begin: right_top_front,
end: right_top_back,
color,
});
self.add_line(Line {
begin: right_bottom_front,
end: right_bottom_back,
color,
});
self.add_line(Line {
begin: left_bottom_front,
end: left_bottom_back,
color,
});
}
pub fn draw_transform(&mut self, matrix: Matrix4<f32>) {
let x = matrix.transform_vector(&Vector3::x());
let y = matrix.transform_vector(&Vector3::y());
let z = matrix.transform_vector(&Vector3::z());
let origin = matrix.position();
self.add_line(Line {
begin: origin,
end: origin + x,
color: Color::RED,
});
self.add_line(Line {
begin: origin,
end: origin + y,
color: Color::GREEN,
});
self.add_line(Line {
begin: origin,
end: origin + z,
color: Color::BLUE,
});
}
pub fn draw_triangle(
&mut self,
a: Vector3<f32>,
b: Vector3<f32>,
c: Vector3<f32>,
color: Color,
) {
self.add_line(Line {
begin: a,
end: b,
color,
});
self.add_line(Line {
begin: b,
end: c,
color,
});
self.add_line(Line {
begin: c,
end: a,
color,
});
}
pub fn draw_circle(
&mut self,
position: Vector3<f32>,
radius: f32,
segments: usize,
transform: Matrix4<f32>,
color: Color,
) {
let d_phi = 2.0 * std::f32::consts::PI / segments as f32;
for i in 0..segments {
let x1 = position.x + radius * (d_phi * i as f32).cos();
let y1 = position.y + radius * (d_phi * i as f32).sin();
let x2 = position.x + radius * (d_phi * (i + 1) as f32).cos();
let y2 = position.y + radius * (d_phi * (i + 1) as f32).sin();
self.add_line(Line {
begin: transform.transform_point(&Point3::new(x1, y1, 0.0)).coords,
end: transform.transform_point(&Point3::new(x2, y2, 0.0)).coords,
color,
})
}
}
pub fn draw_circle_segment(
&mut self,
position: Vector3<f32>,
radius: f32,
segments: usize,
begin_angle: f32,
end_angle: f32,
transform: Matrix4<f32>,
color: Color,
) {
let d_angle = 2.0 * std::f32::consts::PI / segments as f32;
let mut angle = begin_angle;
while angle < end_angle {
let x1 = position.x + radius * (angle).cos();
let y1 = position.y + radius * (angle).sin();
let x2 = position.x + radius * (angle + d_angle).cos();
let y2 = position.y + radius * (angle + d_angle).sin();
self.add_line(Line {
begin: transform.transform_point(&Point3::new(x1, y1, 0.0)).coords,
end: transform.transform_point(&Point3::new(x2, y2, 0.0)).coords,
color,
});
angle += d_angle;
}
}
pub fn draw_rectangle(
&mut self,
half_width: f32,
half_height: f32,
transform: Matrix4<f32>,
color: Color,
) {
let a = transform
.transform_point(&Point3::new(-half_width, half_height, 0.0))
.coords;
let b = transform
.transform_point(&Point3::new(half_width, half_height, 0.0))
.coords;
let c = transform
.transform_point(&Point3::new(half_width, -half_height, 0.0))
.coords;
let d = transform
.transform_point(&Point3::new(-half_width, -half_height, 0.0))
.coords;
self.add_line(Line {
begin: a,
end: b,
color,
});
self.add_line(Line {
begin: b,
end: c,
color,
});
self.add_line(Line {
begin: c,
end: d,
color,
});
self.add_line(Line {
begin: d,
end: a,
color,
});
}
pub fn draw_sphere(
&mut self,
position: Vector3<f32>,
slices: usize,
stacks: usize,
radius: f32,
color: Color,
) {
let d_theta = std::f32::consts::PI / slices as f32;
let d_phi = 2.0 * std::f32::consts::PI / stacks as f32;
for i in 0..stacks {
for j in 0..slices {
let nj = j + 1;
let ni = i + 1;
let k0 = radius * (d_theta * i as f32).sin();
let k1 = (d_phi * j as f32).cos();
let k2 = (d_phi * j as f32).sin();
let k3 = radius * (d_theta * i as f32).cos();
let k4 = radius * (d_theta * ni as f32).sin();
let k5 = (d_phi * nj as f32).cos();
let k6 = (d_phi * nj as f32).sin();
let k7 = radius * (d_theta * ni as f32).cos();
if i != (stacks - 1) {
self.draw_triangle(
position + Vector3::new(k0 * k1, k0 * k2, k3),
position + Vector3::new(k4 * k1, k4 * k2, k7),
position + Vector3::new(k4 * k5, k4 * k6, k7),
color,
);
}
if i != 0 {
self.draw_triangle(
position + Vector3::new(k4 * k5, k4 * k6, k7),
position + Vector3::new(k0 * k5, k0 * k6, k3),
position + Vector3::new(k0 * k1, k0 * k2, k3),
color,
);
}
}
}
}
pub fn draw_sphere_section(
&mut self,
radius: f32,
theta_range: Range<f32>,
theta_steps: usize,
phi_range: Range<f32>,
phi_steps: usize,
transform: Matrix4<f32>,
color: Color,
) {
assert!(theta_range.start < theta_range.end);
assert!(phi_range.start < phi_range.end);
assert_ne!(phi_steps, 0);
assert_ne!(theta_steps, 0);
let theta_step = (theta_range.end - theta_range.start) / theta_steps as f32;
let phi_step = (phi_range.end - phi_range.start) / phi_steps as f32;
fn spherical_to_cartesian(radius: f32, theta: f32, phi: f32) -> Vector3<f32> {
Vector3::new(
radius * theta.sin() * phi.cos(),
radius * theta.cos(),
radius * theta.sin() * phi.sin(),
)
}
let mut theta = theta_range.start;
while theta < theta_range.end {
let mut phi = phi_range.start;
while phi < phi_range.end {
let p0 = transform
.transform_point(&Point3::from(spherical_to_cartesian(radius, theta, phi)))
.coords;
let p1 = transform
.transform_point(&Point3::from(spherical_to_cartesian(
radius,
theta,
phi + phi_step,
)))
.coords;
let p2 = transform
.transform_point(&Point3::from(spherical_to_cartesian(
radius,
theta + theta_step,
phi + phi_step,
)))
.coords;
let p3 = transform
.transform_point(&Point3::from(spherical_to_cartesian(
radius,
theta + theta_step,
phi,
)))
.coords;
self.draw_triangle(p0, p1, p2, color);
self.draw_triangle(p0, p2, p3, color);
phi += phi_step;
}
theta += theta_step;
}
}
pub fn draw_cone(
&mut self,
sides: usize,
r: f32,
h: f32,
transform: Matrix4<f32>,
color: Color,
) {
let d_phi = 2.0 * std::f32::consts::PI / sides as f32;
let half_height = h / 2.0;
for i in 0..sides {
let nx0 = (d_phi * i as f32).cos();
let ny0 = (d_phi * i as f32).sin();
let nx1 = (d_phi * (i + 1) as f32).cos();
let ny1 = (d_phi * (i + 1) as f32).sin();
let x0 = r * nx0;
let z0 = r * ny0;
let x1 = r * nx1;
let z1 = r * ny1;
self.draw_triangle(
transform
.transform_point(&Point3::new(0.0, -half_height, 0.0))
.coords,
transform
.transform_point(&Point3::new(x0, -half_height, z0))
.coords,
transform
.transform_point(&Point3::new(x1, -half_height, z1))
.coords,
color,
);
self.draw_triangle(
transform
.transform_point(&Point3::new(0.0, half_height, 0.0))
.coords,
transform
.transform_point(&Point3::new(x1, -half_height, z1))
.coords,
transform
.transform_point(&Point3::new(x0, -half_height, z0))
.coords,
color,
);
}
}
pub fn draw_cylinder(
&mut self,
sides: usize,
r: f32,
h: f32,
caps: bool,
transform: Matrix4<f32>,
color: Color,
) {
let d_phi = 2.0 * std::f32::consts::PI / sides as f32;
let half_height = h / 2.0;
for i in 0..sides {
let nx0 = (d_phi * i as f32).cos();
let ny0 = (d_phi * i as f32).sin();
let nx1 = (d_phi * (i + 1) as f32).cos();
let ny1 = (d_phi * (i + 1) as f32).sin();
let x0 = r * nx0;
let z0 = r * ny0;
let x1 = r * nx1;
let z1 = r * ny1;
if caps {
self.draw_triangle(
transform
.transform_point(&Point3::new(x1, half_height, z1))
.coords,
transform
.transform_point(&Point3::new(x0, half_height, z0))
.coords,
transform
.transform_point(&Point3::new(0.0, half_height, 0.0))
.coords,
color,
);
self.draw_triangle(
transform
.transform_point(&Point3::new(x0, -half_height, z0))
.coords,
transform
.transform_point(&Point3::new(x1, -half_height, z1))
.coords,
transform
.transform_point(&Point3::new(0.0, -half_height, 0.0))
.coords,
color,
);
}
self.draw_triangle(
transform
.transform_point(&Point3::new(x0, -half_height, z0))
.coords,
transform
.transform_point(&Point3::new(x0, half_height, z0))
.coords,
transform
.transform_point(&Point3::new(x1, -half_height, z1))
.coords,
color,
);
self.draw_triangle(
transform
.transform_point(&Point3::new(x1, -half_height, z1))
.coords,
transform
.transform_point(&Point3::new(x0, half_height, z0))
.coords,
transform
.transform_point(&Point3::new(x1, half_height, z1))
.coords,
color,
);
}
}
pub fn draw_flat_capsule(
&mut self,
radius: f32,
height: f32,
segments: usize,
transform: Matrix4<f32>,
color: Color,
) {
self.draw_circle_segment(
Vector3::new(0.0, height * 0.5, 0.0),
radius,
segments,
0.0,
std::f32::consts::PI,
transform,
color,
);
self.draw_circle_segment(
Vector3::new(0.0, -height * 0.5, 0.0),
radius,
segments,
std::f32::consts::PI,
std::f32::consts::TAU,
transform,
color,
);
self.add_line(Line {
begin: transform
.transform_point(&Point3::new(-radius, height * 0.5, 0.0))
.coords,
end: transform
.transform_point(&Point3::new(-radius, -height * 0.5, 0.0))
.coords,
color,
});
self.add_line(Line {
begin: transform
.transform_point(&Point3::new(radius, height * 0.5, 0.0))
.coords,
end: transform
.transform_point(&Point3::new(radius, -height * 0.5, 0.0))
.coords,
color,
});
}
pub fn draw_capsule(
&mut self,
radius: f32,
height: f32,
transform: Matrix4<f32>,
color: Color,
) {
self.draw_sphere_section(
radius,
0.0..std::f32::consts::FRAC_PI_2,
10,
0.0..std::f32::consts::TAU,
10,
transform * Matrix4::new_translation(&Vector3::new(0.0, height * 0.5 - radius, 0.0)),
color,
);
self.draw_sphere_section(
radius,
std::f32::consts::PI..std::f32::consts::PI * 1.5,
10,
0.0..std::f32::consts::TAU,
10,
transform * Matrix4::new_translation(&Vector3::new(0.0, -height * 0.5 + radius, 0.0)),
color,
);
let cylinder_height = height - 2.0 * radius;
if cylinder_height > 0.0 {
self.draw_cylinder(10, radius, cylinder_height, false, transform, color);
}
}
pub fn draw_segment_flat_capsule(
&mut self,
begin: Vector2<f32>,
end: Vector2<f32>,
radius: f32,
segments: usize,
transform: Matrix4<f32>,
color: Color,
) {
self.draw_circle(
Vector3::new(begin.x, begin.y, 0.0),
radius,
segments,
transform,
color,
);
self.draw_circle(
Vector3::new(end.x, end.y, 0.0),
radius,
segments,
transform,
color,
);
let perp = (end - begin)
.try_normalize(f32::EPSILON)
.map(|v| Vector2::new(v.y, -v.x).scale(radius))
.unwrap_or_default();
self.add_line(Line {
begin: transform
.transform_point(&Point3::from((begin - perp).to_homogeneous()))
.coords,
end: transform
.transform_point(&Point3::from((end - perp).to_homogeneous()))
.coords,
color,
});
self.add_line(Line {
begin: transform
.transform_point(&Point3::from((begin + perp).to_homogeneous()))
.coords,
end: transform
.transform_point(&Point3::from((end + perp).to_homogeneous()))
.coords,
color,
});
}
pub fn draw_segment_capsule(
&mut self,
begin: Vector3<f32>,
end: Vector3<f32>,
radius: f32,
v_segments: usize,
h_segments: usize,
transform: Matrix4<f32>,
color: Color,
) {
let axis = end - begin;
let length = axis.norm();
let z_axis = axis.try_normalize(f32::EPSILON).unwrap_or_else(Vector3::z);
let y_axis = z_axis
.cross(
&(if z_axis.y != 0.0 || z_axis.z != 0.0 {
Vector3::x()
} else {
Vector3::y()
}),
)
.try_normalize(f32::EPSILON)
.unwrap_or_else(Vector3::y);
let x_axis = z_axis
.cross(&y_axis)
.try_normalize(f32::EPSILON)
.unwrap_or_else(Vector3::x);
let shaft_point = |u: f32, v: f32| -> Vector3<f32> {
transform
.transform_point(&Point3::from(
begin
+ x_axis.scale((std::f32::consts::TAU * u).cos() * radius)
+ y_axis.scale((std::f32::consts::TAU * u).sin() * radius)
+ z_axis.scale(v * length),
))
.coords
};
let start_hemisphere_point = |u: f32, v: f32| -> Vector3<f32> {
let latitude = std::f32::consts::FRAC_PI_2 * (v - 1.0);
transform
.transform_point(&Point3::from(
begin
+ x_axis.scale((std::f32::consts::TAU * u).cos() * latitude.cos() * radius)
+ y_axis.scale((std::f32::consts::TAU * u).sin() * latitude.cos() * radius)
+ z_axis.scale(latitude.sin() * radius),
))
.coords
};
let end_hemisphere_point = |u: f32, v: f32| -> Vector3<f32> {
let latitude = std::f32::consts::FRAC_PI_2 * v;
transform
.transform_point(&Point3::from(
end + x_axis.scale((std::f32::consts::TAU * u).cos() * latitude.cos() * radius)
+ y_axis.scale((std::f32::consts::TAU * u).sin() * latitude.cos() * radius)
+ z_axis.scale(latitude.sin() * radius),
))
.coords
};
let dv = 1.0 / h_segments as f32;
let du = 1.0 / v_segments as f32;
let mut u = 0.0;
while u < 1.0 {
let sa = shaft_point(u, 0.0);
let sb = shaft_point(u, 1.0);
let sc = shaft_point(u + du, 1.0);
let sd = shaft_point(u + du, 0.0);
self.draw_triangle(sa, sb, sc, color);
self.draw_triangle(sa, sc, sd, color);
u += du;
}
u = 0.0;
while u < 1.0 {
let mut v = 0.0;
while v < 1.0 {
let sa = start_hemisphere_point(u, v);
let sb = start_hemisphere_point(u, v + dv);
let sc = start_hemisphere_point(u + du, v + dv);
let sd = start_hemisphere_point(u + du, v);
self.draw_triangle(sa, sb, sc, color);
self.draw_triangle(sa, sc, sd, color);
let ea = end_hemisphere_point(u, v);
let eb = end_hemisphere_point(u, v + dv);
let ec = end_hemisphere_point(u + du, v + dv);
let ed = end_hemisphere_point(u + du, v);
self.draw_triangle(ea, eb, ec, color);
self.draw_triangle(ea, ec, ed, color);
v += dv;
}
u += du;
}
}
pub fn add_line(&mut self, line: Line) {
self.lines.push(line);
}
pub fn clear_lines(&mut self) {
self.lines.clear()
}
}