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