Path: blob/main/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs
6598 views
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};1use bevy_asset::RenderAssetUsages;2use bevy_math::{primitives::Triangle3d, Vec3};3use bevy_reflect::prelude::*;45/// A builder used for creating a [`Mesh`] with a [`Triangle3d`] shape.6#[derive(Clone, Copy, Debug, Default, Reflect)]7#[reflect(Default, Debug, Clone)]8pub struct Triangle3dMeshBuilder {9triangle: Triangle3d,10}1112impl MeshBuilder for Triangle3dMeshBuilder {13fn build(&self) -> Mesh {14let positions: Vec<_> = self.triangle.vertices.into();15let uvs: Vec<_> = uv_coords(&self.triangle).into();1617// Every vertex has the normal of the face of the triangle (or zero if the triangle is degenerate).18let normal: Vec3 = normal_vec(&self.triangle);19let normals = vec![normal; 3];2021let indices = Indices::U32(vec![0, 1, 2]);2223Mesh::new(24PrimitiveTopology::TriangleList,25RenderAssetUsages::default(),26)27.with_inserted_indices(indices)28.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)29.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)30.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)31}32}3334impl Meshable for Triangle3d {35type Output = Triangle3dMeshBuilder;3637fn mesh(&self) -> Self::Output {38Triangle3dMeshBuilder { triangle: *self }39}40}4142/// The normal of a [`Triangle3d`] with zeroing so that a [`Vec3`] is always obtained for meshing.43#[inline]44pub(crate) fn normal_vec(triangle: &Triangle3d) -> Vec3 {45triangle.normal().map_or(Vec3::ZERO, Into::into)46}4748/// Unskewed uv-coordinates for a [`Triangle3d`].49#[inline]50pub(crate) fn uv_coords(triangle: &Triangle3d) -> [[f32; 2]; 3] {51let [a, b, c] = triangle.vertices;5253let main_length = a.distance(b);54let Some(x) = (b - a).try_normalize() else {55return [[0., 0.], [1., 0.], [0., 1.]];56};57let y = c - a;5859// `x` corresponds to one of the axes in uv-coordinates;60// to uv-map the triangle without skewing, we use the orthogonalization61// of `y` with respect to `x` as the second direction and construct a rectangle that62// contains `triangle`.63let y_proj = y.project_onto_normalized(x);6465// `offset` represents the x-coordinate of the point `c`; note that x has been shrunk by a66// factor of `main_length`, so `offset` follows it.67let offset = y_proj.dot(x) / main_length;6869// Obtuse triangle leaning to the left => x direction extends to the left, shifting a from 0.70if offset < 0. {71let total_length = 1. - offset;72let a_uv = [offset.abs() / total_length, 0.];73let b_uv = [1., 0.];74let c_uv = [0., 1.];7576[a_uv, b_uv, c_uv]77}78// Obtuse triangle leaning to the right => x direction extends to the right, shifting b from 1.79else if offset > 1. {80let a_uv = [0., 0.];81let b_uv = [1. / offset, 0.];82let c_uv = [1., 1.];8384[a_uv, b_uv, c_uv]85}86// Acute triangle => no extending necessary; a remains at 0 and b remains at 1.87else {88let a_uv = [0., 0.];89let b_uv = [1., 0.];90let c_uv = [offset, 1.];9192[a_uv, b_uv, c_uv]93}94}9596impl From<Triangle3d> for Mesh {97fn from(triangle: Triangle3d) -> Self {98triangle.mesh().build()99}100}101102#[cfg(test)]103mod tests {104use super::uv_coords;105use bevy_math::primitives::Triangle3d;106107#[test]108fn uv_test() {109use bevy_math::vec3;110let mut triangle = Triangle3d::new(vec3(0., 0., 0.), vec3(2., 0., 0.), vec3(-1., 1., 0.));111112let [a_uv, b_uv, c_uv] = uv_coords(&triangle);113assert_eq!(a_uv, [1. / 3., 0.]);114assert_eq!(b_uv, [1., 0.]);115assert_eq!(c_uv, [0., 1.]);116117triangle.vertices[2] = vec3(3., 1., 0.);118let [a_uv, b_uv, c_uv] = uv_coords(&triangle);119assert_eq!(a_uv, [0., 0.]);120assert_eq!(b_uv, [2. / 3., 0.]);121assert_eq!(c_uv, [1., 1.]);122123triangle.vertices[2] = vec3(2., 1., 0.);124let [a_uv, b_uv, c_uv] = uv_coords(&triangle);125assert_eq!(a_uv, [0., 0.]);126assert_eq!(b_uv, [1., 0.]);127assert_eq!(c_uv, [1., 1.]);128}129}130131132