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,
|
&mesh.positions,
|
||||||
Some(&mesh.normals),
|
Some(&mesh.normals),
|
||||||
Some(&mesh.indices),
|
Some(&mesh.indices),
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use bevy_math::{bounding::Aabb3d, Dir3, Mat4, Ray3d, Vec3, Vec3A};
|
use bevy_math::{bounding::Aabb3d, Dir3, Mat4, Ray3d, Vec2, Vec3, Vec3A};
|
||||||
use bevy_mesh::{Indices, Mesh, PrimitiveTopology};
|
use bevy_mesh::{Indices, Mesh, PrimitiveTopology, VertexAttributeValues};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
|
|
||||||
use super::Backfaces;
|
use super::Backfaces;
|
||||||
@ -18,6 +18,8 @@ pub struct RayMeshHit {
|
|||||||
pub distance: f32,
|
pub distance: f32,
|
||||||
/// The vertices of the triangle that was hit.
|
/// The vertices of the triangle that was hit.
|
||||||
pub triangle: Option<[Vec3; 3]>,
|
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.
|
/// The index of the triangle that was hit.
|
||||||
pub triangle_index: Option<usize>,
|
pub triangle_index: Option<usize>,
|
||||||
}
|
}
|
||||||
@ -26,6 +28,10 @@ pub struct RayMeshHit {
|
|||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct RayTriangleHit {
|
pub struct RayTriangleHit {
|
||||||
pub distance: f32,
|
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),
|
pub barycentric_coords: (f32, f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +40,7 @@ pub(super) fn ray_intersection_over_mesh(
|
|||||||
mesh: &Mesh,
|
mesh: &Mesh,
|
||||||
transform: &Mat4,
|
transform: &Mat4,
|
||||||
ray: Ray3d,
|
ray: Ray3d,
|
||||||
culling: Backfaces,
|
cull: Backfaces,
|
||||||
) -> Option<RayMeshHit> {
|
) -> Option<RayMeshHit> {
|
||||||
if mesh.primitive_topology() != PrimitiveTopology::TriangleList {
|
if mesh.primitive_topology() != PrimitiveTopology::TriangleList {
|
||||||
return None; // ray_mesh_intersection assumes vertices are laid out in a triangle list
|
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)
|
.attribute(Mesh::ATTRIBUTE_NORMAL)
|
||||||
.and_then(|normal_values| normal_values.as_float3());
|
.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() {
|
match mesh.indices() {
|
||||||
Some(Indices::U16(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)) => {
|
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.
|
/// 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,
|
ray: Ray3d,
|
||||||
mesh_transform: &Mat4,
|
mesh_transform: &Mat4,
|
||||||
positions: &[[f32; 3]],
|
positions: &[[f32; 3]],
|
||||||
vertex_normals: Option<&[[f32; 3]]>,
|
vertex_normals: Option<&[[f32; 3]]>,
|
||||||
indices: Option<&[I]>,
|
indices: Option<&[I]>,
|
||||||
|
uvs: Option<&[[f32; 2]]>,
|
||||||
backface_culling: Backfaces,
|
backface_culling: Backfaces,
|
||||||
) -> Option<RayMeshHit> {
|
) -> Option<RayMeshHit>
|
||||||
|
where
|
||||||
|
I: TryInto<usize> + Clone + Copy,
|
||||||
|
{
|
||||||
let world_to_mesh = mesh_transform.inverse();
|
let world_to_mesh = mesh_transform.inverse();
|
||||||
|
|
||||||
let ray = Ray3d::new(
|
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)| {
|
closest_hit.and_then(|(tri_idx, hit)| {
|
||||||
let [a, b, c] = match indices {
|
let [a, b, c] = match indices {
|
||||||
Some(indices) => {
|
Some(indices) => {
|
||||||
let triangle = indices.get((tri_idx * 3)..(tri_idx * 3 + 3))?;
|
let [i, j, k] = [tri_idx * 3, tri_idx * 3 + 1, tri_idx * 3 + 2];
|
||||||
|
[
|
||||||
let [Ok(a), Ok(b), Ok(c)] = [
|
indices.get(i).copied()?.try_into().ok()?,
|
||||||
triangle[0].try_into(),
|
indices.get(j).copied()?.try_into().ok()?,
|
||||||
triangle[1].try_into(),
|
indices.get(k).copied()?.try_into().ok()?,
|
||||||
triangle[2].try_into(),
|
]
|
||||||
] else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
[a, b, c]
|
|
||||||
}
|
}
|
||||||
None => [tri_idx * 3, tri_idx * 3 + 1, tri_idx * 3 + 2],
|
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);
|
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 u = hit.barycentric_coords.0;
|
||||||
let v = hit.barycentric_coords.1;
|
let v = hit.barycentric_coords.1;
|
||||||
let w = 1.0 - u - v;
|
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 {
|
let normal = if let Some(normals) = tri_normals {
|
||||||
normals[1] * u + normals[2] * v + normals[0] * w
|
normals[1] * u + normals[2] * v + normals[0] * w
|
||||||
@ -181,9 +195,29 @@ pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
|
|||||||
.normalize()
|
.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 {
|
Some(RayMeshHit {
|
||||||
point: mesh_transform.transform_point3(point),
|
point: mesh_transform.transform_point3(point),
|
||||||
normal: mesh_transform.transform_vector3(normal),
|
normal: mesh_transform.transform_vector3(normal),
|
||||||
|
uv,
|
||||||
barycentric_coords: barycentric,
|
barycentric_coords: barycentric,
|
||||||
distance: mesh_transform
|
distance: mesh_transform
|
||||||
.transform_vector3(ray.direction * hit.distance)
|
.transform_vector3(ray.direction * hit.distance)
|
||||||
@ -329,6 +363,7 @@ mod tests {
|
|||||||
positions,
|
positions,
|
||||||
vertex_normals,
|
vertex_normals,
|
||||||
indices,
|
indices,
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -350,6 +385,7 @@ mod tests {
|
|||||||
positions,
|
positions,
|
||||||
vertex_normals,
|
vertex_normals,
|
||||||
indices,
|
indices,
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -372,6 +408,7 @@ mod tests {
|
|||||||
positions,
|
positions,
|
||||||
vertex_normals,
|
vertex_normals,
|
||||||
indices,
|
indices,
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -394,6 +431,7 @@ mod tests {
|
|||||||
positions,
|
positions,
|
||||||
vertex_normals,
|
vertex_normals,
|
||||||
indices,
|
indices,
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -415,6 +453,7 @@ mod tests {
|
|||||||
positions,
|
positions,
|
||||||
vertex_normals,
|
vertex_normals,
|
||||||
indices,
|
indices,
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -436,6 +475,7 @@ mod tests {
|
|||||||
positions,
|
positions,
|
||||||
vertex_normals,
|
vertex_normals,
|
||||||
indices,
|
indices,
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -457,6 +497,7 @@ mod tests {
|
|||||||
positions,
|
positions,
|
||||||
vertex_normals,
|
vertex_normals,
|
||||||
indices,
|
indices,
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -478,6 +519,7 @@ mod tests {
|
|||||||
positions,
|
positions,
|
||||||
vertex_normals,
|
vertex_normals,
|
||||||
indices,
|
indices,
|
||||||
|
None,
|
||||||
backface_culling,
|
backface_culling,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user