Upstream raycasting UVs (#19791)
# Objective - Upstream mesh raycast UV support used in #19199 ## Solution - Compute UVs, debug a bunch of math issues with barycentric coordinates and add docs. ## Testing - Tested in diagetic UI in the linked PR.
This commit is contained in:
parent
bd4258bf5c
commit
a3d406dd49
@ -155,6 +155,7 @@ fn bench(c: &mut Criterion) {
|
||||
&mesh.positions,
|
||||
Some(&mesh.normals),
|
||||
Some(&mesh.indices),
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use bevy_math::{bounding::Aabb3d, Dir3, Mat4, Ray3d, Vec3, Vec3A};
|
||||
use bevy_mesh::{Indices, Mesh, PrimitiveTopology};
|
||||
use bevy_math::{bounding::Aabb3d, Dir3, Mat4, Ray3d, Vec2, Vec3, Vec3A};
|
||||
use bevy_mesh::{Indices, Mesh, PrimitiveTopology, VertexAttributeValues};
|
||||
use bevy_reflect::Reflect;
|
||||
|
||||
use super::Backfaces;
|
||||
@ -18,6 +18,8 @@ pub struct RayMeshHit {
|
||||
pub distance: f32,
|
||||
/// The vertices of the triangle that was hit.
|
||||
pub triangle: Option<[Vec3; 3]>,
|
||||
/// UV coordinate of the hit, if the mesh has UV attributes.
|
||||
pub uv: Option<Vec2>,
|
||||
/// The index of the triangle that was hit.
|
||||
pub triangle_index: Option<usize>,
|
||||
}
|
||||
@ -26,6 +28,10 @@ pub struct RayMeshHit {
|
||||
#[derive(Default, Debug)]
|
||||
pub struct RayTriangleHit {
|
||||
pub distance: f32,
|
||||
/// Note this uses the convention from the Moller-Trumbore algorithm:
|
||||
/// P = (1 - u - v)A + uB + vC
|
||||
/// This is different from the more common convention of
|
||||
/// P = uA + vB + (1 - u - v)C
|
||||
pub barycentric_coords: (f32, f32),
|
||||
}
|
||||
|
||||
@ -34,7 +40,7 @@ pub(super) fn ray_intersection_over_mesh(
|
||||
mesh: &Mesh,
|
||||
transform: &Mat4,
|
||||
ray: Ray3d,
|
||||
culling: Backfaces,
|
||||
cull: Backfaces,
|
||||
) -> Option<RayMeshHit> {
|
||||
if mesh.primitive_topology() != PrimitiveTopology::TriangleList {
|
||||
return None; // ray_mesh_intersection assumes vertices are laid out in a triangle list
|
||||
@ -47,26 +53,37 @@ pub(super) fn ray_intersection_over_mesh(
|
||||
.attribute(Mesh::ATTRIBUTE_NORMAL)
|
||||
.and_then(|normal_values| normal_values.as_float3());
|
||||
|
||||
let uvs = mesh
|
||||
.attribute(Mesh::ATTRIBUTE_UV_0)
|
||||
.and_then(|uvs| match uvs {
|
||||
VertexAttributeValues::Float32x2(uvs) => Some(uvs.as_slice()),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
match mesh.indices() {
|
||||
Some(Indices::U16(indices)) => {
|
||||
ray_mesh_intersection(ray, transform, positions, normals, Some(indices), culling)
|
||||
ray_mesh_intersection(ray, transform, positions, normals, Some(indices), uvs, cull)
|
||||
}
|
||||
Some(Indices::U32(indices)) => {
|
||||
ray_mesh_intersection(ray, transform, positions, normals, Some(indices), culling)
|
||||
ray_mesh_intersection(ray, transform, positions, normals, Some(indices), uvs, cull)
|
||||
}
|
||||
None => ray_mesh_intersection::<usize>(ray, transform, positions, normals, None, culling),
|
||||
None => ray_mesh_intersection::<usize>(ray, transform, positions, normals, None, uvs, cull),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a ray intersects a mesh, and returns the nearest intersection if one exists.
|
||||
pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
|
||||
pub fn ray_mesh_intersection<I>(
|
||||
ray: Ray3d,
|
||||
mesh_transform: &Mat4,
|
||||
positions: &[[f32; 3]],
|
||||
vertex_normals: Option<&[[f32; 3]]>,
|
||||
indices: Option<&[I]>,
|
||||
uvs: Option<&[[f32; 2]]>,
|
||||
backface_culling: Backfaces,
|
||||
) -> Option<RayMeshHit> {
|
||||
) -> Option<RayMeshHit>
|
||||
where
|
||||
I: TryInto<usize> + Clone + Copy,
|
||||
{
|
||||
let world_to_mesh = mesh_transform.inverse();
|
||||
|
||||
let ray = Ray3d::new(
|
||||
@ -139,17 +156,12 @@ pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
|
||||
closest_hit.and_then(|(tri_idx, hit)| {
|
||||
let [a, b, c] = match indices {
|
||||
Some(indices) => {
|
||||
let triangle = indices.get((tri_idx * 3)..(tri_idx * 3 + 3))?;
|
||||
|
||||
let [Ok(a), Ok(b), Ok(c)] = [
|
||||
triangle[0].try_into(),
|
||||
triangle[1].try_into(),
|
||||
triangle[2].try_into(),
|
||||
] else {
|
||||
return None;
|
||||
};
|
||||
|
||||
[a, b, c]
|
||||
let [i, j, k] = [tri_idx * 3, tri_idx * 3 + 1, tri_idx * 3 + 2];
|
||||
[
|
||||
indices.get(i).copied()?.try_into().ok()?,
|
||||
indices.get(j).copied()?.try_into().ok()?,
|
||||
indices.get(k).copied()?.try_into().ok()?,
|
||||
]
|
||||
}
|
||||
None => [tri_idx * 3, tri_idx * 3 + 1, tri_idx * 3 + 2],
|
||||
};
|
||||
@ -168,10 +180,12 @@ pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
|
||||
});
|
||||
|
||||
let point = ray.get_point(hit.distance);
|
||||
// Note that we need to convert from the Möller-Trumbore convention to the more common
|
||||
// P = uA + vB + (1 - u - v)C convention.
|
||||
let u = hit.barycentric_coords.0;
|
||||
let v = hit.barycentric_coords.1;
|
||||
let w = 1.0 - u - v;
|
||||
let barycentric = Vec3::new(u, v, w);
|
||||
let barycentric = Vec3::new(w, u, v);
|
||||
|
||||
let normal = if let Some(normals) = tri_normals {
|
||||
normals[1] * u + normals[2] * v + normals[0] * w
|
||||
@ -181,9 +195,29 @@ pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
|
||||
.normalize()
|
||||
};
|
||||
|
||||
let uv = uvs.and_then(|uvs| {
|
||||
let tri_uvs = if let Some(indices) = indices {
|
||||
let i = tri_idx * 3;
|
||||
[
|
||||
uvs[indices[i].try_into().ok()?],
|
||||
uvs[indices[i + 1].try_into().ok()?],
|
||||
uvs[indices[i + 2].try_into().ok()?],
|
||||
]
|
||||
} else {
|
||||
let i = tri_idx * 3;
|
||||
[uvs[i], uvs[i + 1], uvs[i + 2]]
|
||||
};
|
||||
Some(
|
||||
barycentric.x * Vec2::from(tri_uvs[0])
|
||||
+ barycentric.y * Vec2::from(tri_uvs[1])
|
||||
+ barycentric.z * Vec2::from(tri_uvs[2]),
|
||||
)
|
||||
});
|
||||
|
||||
Some(RayMeshHit {
|
||||
point: mesh_transform.transform_point3(point),
|
||||
normal: mesh_transform.transform_vector3(normal),
|
||||
uv,
|
||||
barycentric_coords: barycentric,
|
||||
distance: mesh_transform
|
||||
.transform_vector3(ray.direction * hit.distance)
|
||||
@ -329,6 +363,7 @@ mod tests {
|
||||
positions,
|
||||
vertex_normals,
|
||||
indices,
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
@ -350,6 +385,7 @@ mod tests {
|
||||
positions,
|
||||
vertex_normals,
|
||||
indices,
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
@ -372,6 +408,7 @@ mod tests {
|
||||
positions,
|
||||
vertex_normals,
|
||||
indices,
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
@ -394,6 +431,7 @@ mod tests {
|
||||
positions,
|
||||
vertex_normals,
|
||||
indices,
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
@ -415,6 +453,7 @@ mod tests {
|
||||
positions,
|
||||
vertex_normals,
|
||||
indices,
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
@ -436,6 +475,7 @@ mod tests {
|
||||
positions,
|
||||
vertex_normals,
|
||||
indices,
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
@ -457,6 +497,7 @@ mod tests {
|
||||
positions,
|
||||
vertex_normals,
|
||||
indices,
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
@ -478,6 +519,7 @@ mod tests {
|
||||
positions,
|
||||
vertex_normals,
|
||||
indices,
|
||||
None,
|
||||
backface_culling,
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user