Properly calculate relative hit position for rotated objects

This commit is contained in:
numzero 2024-06-10 22:41:04 +03:00
parent 3dec491bb5
commit 932029b064

View File

@ -39,14 +39,15 @@ pub fn main() {
let objs: Vec<_> = [-1.25, -1.00, -0.85, -0.50, 0.00, 0.40, 0.70, 0.95, 1.05]
.iter()
.enumerate()
.map(|(k, y)| Object {
.map(|(k, &y)| Object {
id: k as i32,
loc: {
let pos = vec2(0.0, y * tube.external_halflength);
let adj = tube.sqrt_at(pos).inverse().into();
let adj: Mat2 = tube.sqrt_at(pos).inverse().into();
let rot = Mat2::from_angle(y);
Location {
pos,
rot: adj,
rot: adj * rot,
}
},
r: 20.0,
@ -135,6 +136,11 @@ struct Hit {
rel: Vec2, // положение в локальной ортонормированной СК объекта
}
struct HitInternal<'a> {
object: &'a Object,
distance: f32,
}
struct FlatTraceResult {
end: Option<Ray>,
objects: Vec<Hit>,
@ -186,13 +192,17 @@ impl Space {
assert_eq!(self.which_subspace(ray.pos), Inner);
let cell = RectInside { rect: self.rect };
let ray = cell.ray_to_local(ray);
let objs = Self::trace_to_objects(self.list_objects_inner().as_slice(), ray);
let objs = self.list_objects_inner();
let hits = Self::trace_to_objects(objs.as_slice(), ray);
let dist = cell.to_boundary(ray).expect("Can't get outta here!");
let ray = ray.forward(dist);
FlatTraceResult {
end: Some(cell.ray_to_global(ray)),
objects: objs.into_iter().filter_map(|Hit { id, distance, pos, rel }|
if distance < dist { Some(Hit { id, distance, pos: cell.pos_to_global(pos), rel }) } else { None }
end: Some(cell.ray_to_global(ray.forward(dist))),
objects: hits.into_iter().filter_map(|HitInternal { object, distance }|
if distance < dist {
let pos = ray.forward(distance).pos;
let rel = object.loc.rot.inverse() * cell.ray_to_global(Ray { pos: object.loc.pos, dir: pos - object.loc.pos }).dir;
Some(Hit { id: object.id, distance, pos: cell.pos_to_global(pos), rel })
} else { None }
).collect(),
}
}
@ -200,16 +210,27 @@ impl Space {
fn trace_outer(&self, ray: Ray) -> FlatTraceResult {
assert_eq!(self.which_subspace(ray.pos), Outer);
let cell = basic_shapes::Rect { size: vec2(self.rect.outer_radius, self.rect.external_halflength) };
let objs = Self::trace_to_objects(self.list_objects_outer().as_slice(), ray);
let objs = self.list_objects_outer();
let hits = Self::trace_to_objects(objs.as_slice(), ray);
if let Some(dist) = cell.trace_into(ray) {
FlatTraceResult {
end: Some(ray.forward(dist)),
objects: objs.into_iter().filter(|hit| hit.distance < dist).collect(),
objects: hits.into_iter().filter_map(|HitInternal { object, distance }|
if distance < dist {
let pos = ray.forward(distance).pos;
let rel = object.loc.rot.inverse() * (pos - object.loc.pos);
Some(Hit { id: object.id, distance, pos, rel })
} else { None }
).collect(),
}
} else {
FlatTraceResult {
end: None,
objects: objs,
objects: hits.into_iter().map(|HitInternal { object, distance }| {
let pos = ray.forward(distance).pos;
let rel = object.loc.rot.inverse() * (pos - object.loc.pos);
Hit { id: object.id, distance, pos, rel }
}).collect(),
}
}
}
@ -228,11 +249,13 @@ impl Space {
Inner => {
let Vec2 { x, y } = obj.loc.pos; // в основной СК
let y = self.rect.u(y) + y.signum() * (self.rect.external_halflength - self.rect.internal_halflength);
let dy = self.rect.du(y, 1.0);
let m = Mat2::from_cols_array(&[1.0, 0.0, 0.0, dy]);
Object {
id: obj.id,
loc: Location {
pos: vec2(x, y), // в плоском продолжении СК Outer на область Inner
rot: obj.loc.rot,
rot: m * obj.loc.rot,
},
r: obj.r,
}
@ -262,7 +285,7 @@ impl Space {
}).collect()
}
fn trace_to_objects(objs: &[Object], ray: Ray) -> Vec<Hit> {
fn trace_to_objects(objs: &[Object], ray: Ray) -> Vec<HitInternal> {
objs.iter()
.filter_map(|obj| {
let rel = ray.pos - obj.loc.pos;
@ -271,7 +294,7 @@ impl Space {
let t = (-rel.dot(ray.dir) - diff.sqrt()) / ray.dir.length_squared();
if t >= 0.0 {
let pos = ray.forward(t).pos;
return Some(Hit { id: obj.id, distance: t, pos, rel: pos - obj.loc.pos });
return Some(HitInternal { object: &obj, distance: t });
}
}
None