Skip to content

Meshing for Triangle3d primitive #12686

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions crates/bevy_render/src/mesh/primitives/dim2.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use crate::{
mesh::primitives::dim3::triangle3d,
mesh::{Indices, Mesh},
render_asset::RenderAssetUsages,
};

use super::Meshable;
use bevy_math::{
primitives::{
Annulus, Capsule2d, Circle, Ellipse, Rectangle, RegularPolygon, Triangle2d, WindingOrder,
},
Vec2,
use bevy_math::primitives::{
Annulus, Capsule2d, Circle, Ellipse, Rectangle, RegularPolygon, Triangle2d, Triangle3d,
WindingOrder,
};
use wgpu::PrimitiveTopology;

Expand Down Expand Up @@ -317,25 +316,23 @@ impl Meshable for Triangle2d {
type Output = Mesh;

fn mesh(&self) -> Self::Output {
let [a, b, c] = self.vertices;
let vertices_3d = self.vertices.map(|v| v.extend(0.));

let positions = vec![[a.x, a.y, 0.0], [b.x, b.y, 0.0], [c.x, c.y, 0.0]];
let positions: Vec<_> = vertices_3d.into();
let normals = vec![[0.0, 0.0, 1.0]; 3];

// The extents of the bounding box of the triangle,
// used to compute the UV coordinates of the points.
let extents = a.min(b).min(c).abs().max(a.max(b).max(c)) * Vec2::new(1.0, -1.0);
let uvs = vec![
a / extents / 2.0 + 0.5,
b / extents / 2.0 + 0.5,
c / extents / 2.0 + 0.5,
];
let uvs: Vec<_> = triangle3d::uv_coords(&Triangle3d::new(
vertices_3d[0],
vertices_3d[1],
vertices_3d[2],
))
.into();
Comment on lines -320 to +329
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fancy


let is_ccw = self.winding_order() == WindingOrder::CounterClockwise;
let indices = if is_ccw {
Indices::U32(vec![0, 1, 2])
} else {
Indices::U32(vec![0, 2, 1])
Indices::U32(vec![2, 1, 0])
};

Mesh::new(
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/src/mesh/primitives/dim3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod cylinder;
mod plane;
mod sphere;
mod torus;
pub(crate) mod triangle3d;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this in a different format to the other module declarations?

Copy link
Contributor Author

@mweatherley mweatherley Apr 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pub(crate) so that the Triangle2d code can see the Triangle3d UV generation code. Maybe there's a more elegant way to accomplish that, but this seemed like the simplest thing at the time.


pub use capsule::*;
pub use cylinder::*;
Expand Down
114 changes: 114 additions & 0 deletions crates/bevy_render/src/mesh/primitives/dim3/triangle3d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use bevy_math::{primitives::Triangle3d, Vec3};
use wgpu::PrimitiveTopology;

use crate::{
mesh::{Indices, Mesh, Meshable},
render_asset::RenderAssetUsages,
};

impl Meshable for Triangle3d {
type Output = Mesh;

fn mesh(&self) -> Self::Output {
let positions: Vec<_> = self.vertices.into();
let uvs: Vec<_> = uv_coords(self).into();

// Every vertex has the normal of the face of the triangle (or zero if the triangle is degenerate).
let normal: Vec3 = self.normal().map_or(Vec3::ZERO, |n| n.into());
let normals = vec![normal; 3];

let indices = Indices::U32(vec![0, 1, 2]);

Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(indices)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}

/// Unskewed uv-coordinates for a [`Triangle3d`].
#[inline]
pub(crate) fn uv_coords(triangle: &Triangle3d) -> [[f32; 2]; 3] {
let [a, b, c] = triangle.vertices;

let main_length = a.distance(b);
let Some(x) = (b - a).try_normalize() else {
return [[0., 0.], [1., 0.], [0., 1.]];
};
let y = c - a;

// `x` corresponds to one of the axes in uv-coordinates;
// to uv-map the triangle without skewing, we use the orthogonalization
// of `y` with respect to `x` as the second direction and construct a rectangle that
// contains `triangle`.
let y_proj = y.project_onto_normalized(x);

// `offset` represents the x-coordinate of the point `c`; note that x has been shrunk by a
// factor of `main_length`, so `offset` follows it.
let offset = y_proj.dot(x) / main_length;

// Obtuse triangle leaning to the left => x direction extends to the left, shifting a from 0.
if offset < 0. {
let total_length = 1. - offset;
let a_uv = [offset.abs() / total_length, 0.];
let b_uv = [1., 0.];
let c_uv = [0., 1.];

[a_uv, b_uv, c_uv]
}
// Obtuse triangle leaning to the right => x direction extends to the right, shifting b from 1.
else if offset > 1. {
let a_uv = [0., 0.];
let b_uv = [1. / offset, 0.];
let c_uv = [1., 1.];

[a_uv, b_uv, c_uv]
}
// Acute triangle => no extending necessary; a remains at 0 and b remains at 1.
else {
let a_uv = [0., 0.];
let b_uv = [1., 0.];
let c_uv = [offset, 1.];

[a_uv, b_uv, c_uv]
}
}

impl From<Triangle3d> for Mesh {
fn from(triangle: Triangle3d) -> Self {
triangle.mesh()
}
}

#[cfg(test)]
mod tests {
use super::uv_coords;
use bevy_math::primitives::Triangle3d;

#[test]
fn uv_test() {
use bevy_math::vec3;
let mut triangle = Triangle3d::new(vec3(0., 0., 0.), vec3(2., 0., 0.), vec3(-1., 1., 0.));

let [a_uv, b_uv, c_uv] = uv_coords(&triangle);
assert_eq!(a_uv, [1. / 3., 0.]);
assert_eq!(b_uv, [1., 0.]);
assert_eq!(c_uv, [0., 1.]);

triangle.vertices[2] = vec3(3., 1., 0.);
let [a_uv, b_uv, c_uv] = uv_coords(&triangle);
assert_eq!(a_uv, [0., 0.]);
assert_eq!(b_uv, [2. / 3., 0.]);
assert_eq!(c_uv, [1., 1.]);

triangle.vertices[2] = vec3(2., 1., 0.);
let [a_uv, b_uv, c_uv] = uv_coords(&triangle);
assert_eq!(a_uv, [0., 0.]);
assert_eq!(b_uv, [1., 0.]);
assert_eq!(c_uv, [1., 1.]);
}
}