#[cfg(not(feature = "std"))]
use na::ComplexField;
#[cfg(feature = "std")]
use na::DVector;
use std::ops::Range;
use crate::utils::DefaultStorage;
#[cfg(feature = "cuda")]
use crate::utils::{CudaArrayPointer1, CudaStorage, CudaStoragePtr};
#[cfg(all(feature = "std", feature = "cuda"))]
use {crate::utils::CudaArray1, cust::error::CudaResult};
use na::Point2;
use crate::bounding_volume::Aabb;
use crate::math::{Real, Vector};
use crate::shape::Segment;
use crate::utils::Array1;
pub type HeightFieldCellStatus = bool;
pub trait HeightFieldStorage {
type Heights: Array1<Real>;
type Status: Array1<HeightFieldCellStatus>;
}
#[cfg(feature = "std")]
impl HeightFieldStorage for DefaultStorage {
type Heights = DVector<Real>;
type Status = DVector<HeightFieldCellStatus>;
}
#[cfg(all(feature = "std", feature = "cuda"))]
impl HeightFieldStorage for CudaStorage {
type Heights = CudaArray1<Real>;
type Status = CudaArray1<HeightFieldCellStatus>;
}
#[cfg(feature = "cuda")]
impl HeightFieldStorage for CudaStoragePtr {
type Heights = CudaArrayPointer1<Real>;
type Status = CudaArrayPointer1<HeightFieldCellStatus>;
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
archive(check_bytes)
)]
#[derive(Debug)]
#[repr(C)] pub struct GenericHeightField<Storage: HeightFieldStorage> {
heights: Storage::Heights,
status: Storage::Status,
scale: Vector<Real>,
aabb: Aabb,
}
impl<Storage> Clone for GenericHeightField<Storage>
where
Storage: HeightFieldStorage,
Storage::Heights: Clone,
Storage::Status: Clone,
{
fn clone(&self) -> Self {
Self {
heights: self.heights.clone(),
status: self.status.clone(),
scale: self.scale,
aabb: self.aabb,
}
}
}
impl<Storage> Copy for GenericHeightField<Storage>
where
Storage: HeightFieldStorage,
Storage::Heights: Copy,
Storage::Status: Copy,
{
}
#[cfg(feature = "cuda")]
unsafe impl<Storage> cust_core::DeviceCopy for GenericHeightField<Storage>
where
Storage: HeightFieldStorage,
Storage::Heights: cust_core::DeviceCopy + Copy,
Storage::Status: cust_core::DeviceCopy + Copy,
{
}
#[cfg(feature = "std")]
pub type HeightField = GenericHeightField<DefaultStorage>;
#[cfg(all(feature = "std", feature = "cuda"))]
pub type CudaHeightField = GenericHeightField<CudaStorage>;
#[cfg(feature = "cuda")]
pub type CudaHeightFieldPtr = GenericHeightField<CudaStoragePtr>;
#[cfg(feature = "std")]
impl HeightField {
pub fn new(heights: DVector<Real>, scale: Vector<Real>) -> Self {
assert!(
heights.len() > 1,
"A heightfield heights must have at least 2 elements."
);
let max = heights.max();
let min = heights.min();
let hscale = scale * 0.5;
let aabb = Aabb::new(
Point2::new(-hscale.x, min * scale.y),
Point2::new(hscale.x, max * scale.y),
);
let num_segments = heights.len() - 1;
HeightField {
heights,
status: DVector::repeat(num_segments, true),
scale,
aabb,
}
}
#[cfg(feature = "cuda")]
pub fn to_cuda(&self) -> CudaResult<CudaHeightField> {
Ok(CudaHeightField {
heights: CudaArray1::from_vector(&self.heights)?,
status: CudaArray1::from_vector(&self.status)?,
scale: self.scale,
aabb: self.aabb,
})
}
}
#[cfg(all(feature = "std", feature = "cuda"))]
impl CudaHeightField {
pub fn as_device_ptr(&self) -> CudaHeightFieldPtr {
CudaHeightFieldPtr {
heights: self.heights.as_device_ptr(),
status: self.status.as_device_ptr(),
aabb: self.aabb,
scale: self.scale,
}
}
}
impl<Storage: HeightFieldStorage> GenericHeightField<Storage> {
pub fn num_cells(&self) -> usize {
self.heights.len() - 1
}
pub fn heights(&self) -> &Storage::Heights {
&self.heights
}
pub fn scale(&self) -> &Vector<Real> {
&self.scale
}
pub fn set_scale(&mut self, new_scale: Vector<Real>) {
let ratio = new_scale.component_div(&self.scale);
self.aabb.mins.coords.component_mul_assign(&ratio);
self.aabb.maxs.coords.component_mul_assign(&ratio);
self.scale = new_scale;
}
pub fn scaled(mut self, scale: &Vector<Real>) -> Self {
self.set_scale(self.scale.component_mul(scale));
self
}
pub fn root_aabb(&self) -> &Aabb {
&self.aabb
}
pub fn cell_width(&self) -> Real {
self.unit_cell_width() * self.scale.x
}
pub fn unit_cell_width(&self) -> Real {
1.0 / (self.heights.len() as Real - 1.0)
}
pub fn start_x(&self) -> Real {
self.scale.x * -0.5
}
fn quantize_floor_unclamped(&self, val: Real, seg_length: Real) -> isize {
((val + 0.5) / seg_length).floor() as isize
}
fn quantize_ceil_unclamped(&self, val: Real, seg_length: Real) -> isize {
((val + 0.5) / seg_length).ceil() as isize
}
fn quantize_floor(&self, val: Real, seg_length: Real) -> usize {
na::clamp(
((val + 0.5) / seg_length).floor(),
0.0,
(self.num_cells() - 1) as Real,
) as usize
}
fn quantize_ceil(&self, val: Real, seg_length: Real) -> usize {
na::clamp(
((val + 0.5) / seg_length).ceil(),
0.0,
self.num_cells() as Real,
) as usize
}
pub fn cell_at_point(&self, pt: &Point2<Real>) -> Option<usize> {
let scaled_pt = pt.coords.component_div(&self.scale);
let seg_length = self.unit_cell_width();
if scaled_pt.x < -0.5 || scaled_pt.x > 0.5 {
None
} else {
Some(self.quantize_floor(scaled_pt.x, seg_length))
}
}
pub fn height_at_point(&self, pt: &Point2<Real>) -> Option<Real> {
let cell = self.cell_at_point(pt)?;
let seg = self.segment_at(cell)?;
let inter = crate::query::details::closest_points_line_line_parameters(
&seg.a,
&seg.scaled_direction(),
pt,
&Vector::y(),
);
Some(seg.a.y + inter.1)
}
pub fn segments(&self) -> impl Iterator<Item = Segment> + '_ {
(0..self.num_cells()).filter_map(move |i| self.segment_at(i))
}
pub fn segment_at(&self, i: usize) -> Option<Segment> {
if i >= self.num_cells() || self.is_segment_removed(i) {
return None;
}
let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
let x0 = -0.5 + seg_length * (i as Real);
let x1 = x0 + seg_length;
let y0 = self.heights[i];
let y1 = self.heights[i + 1];
let mut p0 = Point2::new(x0, y0);
let mut p1 = Point2::new(x1, y1);
p0.coords.component_mul_assign(&self.scale);
p1.coords.component_mul_assign(&self.scale);
Some(Segment::new(p0, p1))
}
pub fn set_segment_removed(&mut self, i: usize, removed: bool) {
self.status[i] = !removed
}
pub fn is_segment_removed(&self, i: usize) -> bool {
!self.status[i]
}
pub fn unclamped_elements_range_in_local_aabb(&self, aabb: &Aabb) -> Range<isize> {
let ref_mins = aabb.mins.coords.component_div(&self.scale);
let ref_maxs = aabb.maxs.coords.component_div(&self.scale);
let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
let min_x = self.quantize_floor_unclamped(ref_mins.x, seg_length);
let max_x = self.quantize_ceil_unclamped(ref_maxs.x, seg_length);
min_x..max_x
}
pub fn map_elements_in_local_aabb(&self, aabb: &Aabb, f: &mut impl FnMut(u32, &Segment)) {
let ref_mins = aabb.mins.coords.component_div(&self.scale);
let ref_maxs = aabb.maxs.coords.component_div(&self.scale);
let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
if ref_maxs.x < -0.5 || ref_mins.x > 0.5 {
return;
}
let min_x = self.quantize_floor(ref_mins.x, seg_length);
let max_x = self.quantize_ceil(ref_maxs.x, seg_length);
for i in min_x..max_x {
if self.is_segment_removed(i) {
continue;
}
let x0 = -0.5 + seg_length * (i as Real);
let x1 = x0 + seg_length;
let y0 = self.heights[i];
let y1 = self.heights[i + 1];
if (y0 > ref_maxs.y && y1 > ref_maxs.y) || (y0 < ref_mins.y && y1 < ref_mins.y) {
continue;
}
let mut p0 = Point2::new(x0, y0);
let mut p1 = Point2::new(x1, y1);
p0.coords.component_mul_assign(&self.scale);
p1.coords.component_mul_assign(&self.scale);
let seg = Segment::new(p0, p1);
f(i as u32, &seg);
}
}
}