mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
Add Meshable
trait and implement meshing for 2D primitives (#11431)
# Objective The first part of #10569, split up from #11007. The goal is to implement meshing support for Bevy's new geometric primitives, starting with 2D primitives. 3D meshing will be added in a follow-up, and we can consider removing the old mesh shapes completely. ## Solution Add a `Meshable` trait that primitives need to implement to support meshing, as suggested by the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/12-primitive-shapes.md#meshing). ```rust /// A trait for shapes that can be turned into a [`Mesh`]. pub trait Meshable { /// The output of [`Self::mesh`]. This can either be a [`Mesh`] /// or a builder used for creating a [`Mesh`]. type Output; /// Creates a [`Mesh`] for a shape. fn mesh(&self) -> Self::Output; } ``` This PR implements it for the following primitives: - `Circle` - `Ellipse` - `Rectangle` - `RegularPolygon` - `Triangle2d` The `mesh` method typically returns a builder-like struct such as `CircleMeshBuilder`. This is needed to support shape-specific configuration for things like mesh resolution or UV configuration: ```rust meshes.add(Circle { radius: 0.5 }.mesh().resolution(64)); ``` Note that if no configuration is needed, you can even skip calling `mesh` because `From<MyPrimitive>` is implemented for `Mesh`: ```rust meshes.add(Circle { radius: 0.5 }); ``` I also updated the `2d_shapes` example to use primitives, and tweaked the colors to have better contrast against the dark background. Before: ![Old 2D shapes](https://github.com/bevyengine/bevy/assets/57632562/f1d8c2d5-55be-495f-8ed4-5890154b81ca) After: ![New 2D shapes](https://github.com/bevyengine/bevy/assets/57632562/f166c013-34b8-4752-800a-5517b284d978) Here you can see the UVs and different facing directions: (taken from #11007, so excuse the 3D primitives at the bottom left) ![UVs and facing directions](https://github.com/bevyengine/bevy/assets/57632562/eaf0be4e-187d-4b6d-8fb8-c996ba295a8a) --- ## Changelog - Added `bevy_render::mesh::primitives` module - Added `Meshable` trait and implemented it for: - `Circle` - `Ellipse` - `Rectangle` - `RegularPolygon` - `Triangle2d` - Implemented `Default` and `Copy` for several 2D primitives - Updated `2d_shapes` example to use primitives - Tweaked colors in `2d_shapes` example to have better contrast against the (new-ish) dark background --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
149a313850
commit
2bf481c03b
8 changed files with 393 additions and 24 deletions
|
@ -402,7 +402,7 @@ doc-scrape-examples = true
|
|||
|
||||
[package.metadata.example.2d_shapes]
|
||||
name = "2D Shapes"
|
||||
description = "Renders a rectangle, circle, and hexagon"
|
||||
description = "Renders simple 2D primitive shapes like circles and polygons"
|
||||
category = "2D Rendering"
|
||||
wasm = true
|
||||
|
||||
|
|
|
@ -90,6 +90,13 @@ pub struct Circle {
|
|||
}
|
||||
impl Primitive2d for Circle {}
|
||||
|
||||
impl Default for Circle {
|
||||
/// Returns the default [`Circle`] with a radius of `0.5`.
|
||||
fn default() -> Self {
|
||||
Self { radius: 0.5 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
/// Create a new [`Circle`] from a `radius`
|
||||
#[inline(always)]
|
||||
|
@ -147,6 +154,15 @@ pub struct Ellipse {
|
|||
}
|
||||
impl Primitive2d for Ellipse {}
|
||||
|
||||
impl Default for Ellipse {
|
||||
/// Returns the default [`Ellipse`] with a half-width of `1.0` and a half-height of `0.5`.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
half_size: Vec2::new(1.0, 0.5),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ellipse {
|
||||
/// Create a new `Ellipse` from half of its width and height.
|
||||
///
|
||||
|
@ -197,6 +213,15 @@ pub struct Plane2d {
|
|||
}
|
||||
impl Primitive2d for Plane2d {}
|
||||
|
||||
impl Default for Plane2d {
|
||||
/// Returns the default [`Plane2d`] with a normal pointing in the `+Y` direction.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
normal: Direction2d::Y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plane2d {
|
||||
/// Create a new `Plane2d` from a normal
|
||||
///
|
||||
|
@ -343,10 +368,19 @@ pub struct Triangle2d {
|
|||
}
|
||||
impl Primitive2d for Triangle2d {}
|
||||
|
||||
impl Default for Triangle2d {
|
||||
/// Returns the default [`Triangle2d`] with the vertices `[0.0, 0.5]`, `[-0.5, -0.5]`, and `[0.5, -0.5]`.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
vertices: [Vec2::Y * 0.5, Vec2::new(-0.5, -0.5), Vec2::new(0.5, -0.5)],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Triangle2d {
|
||||
/// Create a new `Triangle2d` from points `a`, `b`, and `c`
|
||||
#[inline(always)]
|
||||
pub fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
|
||||
pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
|
||||
Self {
|
||||
vertices: [a, b, c],
|
||||
}
|
||||
|
@ -438,6 +472,15 @@ pub struct Rectangle {
|
|||
pub half_size: Vec2,
|
||||
}
|
||||
|
||||
impl Default for Rectangle {
|
||||
/// Returns the default [`Rectangle`] with a half-width and half-height of `0.5`.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
half_size: Vec2::splat(0.5),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
/// Create a new `Rectangle` from a full width and height
|
||||
#[inline(always)]
|
||||
|
@ -559,9 +602,19 @@ pub struct RegularPolygon {
|
|||
}
|
||||
impl Primitive2d for RegularPolygon {}
|
||||
|
||||
impl Default for RegularPolygon {
|
||||
/// Returns the default [`RegularPolygon`] with six sides (a hexagon) and a circumradius of `0.5`.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
circumcircle: Circle { radius: 0.5 },
|
||||
sides: 6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegularPolygon {
|
||||
/// Create a new `RegularPolygon`
|
||||
/// from the radius of the circumcircle and number of sides
|
||||
/// from the radius of the circumcircle and a number of sides
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
|
|
@ -34,7 +34,7 @@ pub mod prelude {
|
|||
Projection,
|
||||
},
|
||||
color::Color,
|
||||
mesh::{morph::MorphWeights, shape, Mesh},
|
||||
mesh::{morph::MorphWeights, primitives::Meshable, shape, Mesh},
|
||||
render_resource::Shader,
|
||||
spatial_bundle::SpatialBundle,
|
||||
texture::{Image, ImagePlugin},
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#[allow(clippy::module_inception)]
|
||||
mod mesh;
|
||||
pub mod morph;
|
||||
pub mod primitives;
|
||||
/// Generation for some primitive shape meshes.
|
||||
pub mod shape;
|
||||
|
||||
pub use mesh::*;
|
||||
pub use primitives::*;
|
||||
|
||||
use crate::{prelude::Image, render_asset::RenderAssetPlugin};
|
||||
use bevy_app::{App, Plugin};
|
||||
|
|
268
crates/bevy_render/src/mesh/primitives/dim2.rs
Normal file
268
crates/bevy_render/src/mesh/primitives/dim2.rs
Normal file
|
@ -0,0 +1,268 @@
|
|||
use crate::{
|
||||
mesh::{Indices, Mesh},
|
||||
render_asset::RenderAssetPersistencePolicy,
|
||||
};
|
||||
|
||||
use super::Meshable;
|
||||
use bevy_math::{
|
||||
primitives::{Circle, Ellipse, Rectangle, RegularPolygon, Triangle2d, WindingOrder},
|
||||
Vec2,
|
||||
};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
||||
/// A builder used for creating a [`Mesh`] with a [`Circle`] shape.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CircleMeshBuilder {
|
||||
/// The [`Circle`] shape.
|
||||
pub circle: Circle,
|
||||
/// The number of vertices used for the circle mesh.
|
||||
/// The default is `32`.
|
||||
#[doc(alias = "vertices")]
|
||||
pub resolution: usize,
|
||||
}
|
||||
|
||||
impl Default for CircleMeshBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
circle: Circle::default(),
|
||||
resolution: 32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CircleMeshBuilder {
|
||||
/// Creates a new [`CircleMeshBuilder`] from a given radius and vertex count.
|
||||
#[inline]
|
||||
pub const fn new(radius: f32, resolution: usize) -> Self {
|
||||
Self {
|
||||
circle: Circle { radius },
|
||||
resolution,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the number of vertices used for the circle mesh.
|
||||
#[inline]
|
||||
#[doc(alias = "vertices")]
|
||||
pub const fn resolution(mut self, resolution: usize) -> Self {
|
||||
self.resolution = resolution;
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
||||
pub fn build(&self) -> Mesh {
|
||||
RegularPolygon::new(self.circle.radius, self.resolution).mesh()
|
||||
}
|
||||
}
|
||||
|
||||
impl Meshable for Circle {
|
||||
type Output = CircleMeshBuilder;
|
||||
|
||||
fn mesh(&self) -> Self::Output {
|
||||
CircleMeshBuilder {
|
||||
circle: *self,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Circle> for Mesh {
|
||||
fn from(circle: Circle) -> Self {
|
||||
circle.mesh().build()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CircleMeshBuilder> for Mesh {
|
||||
fn from(circle: CircleMeshBuilder) -> Self {
|
||||
circle.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Meshable for RegularPolygon {
|
||||
type Output = Mesh;
|
||||
|
||||
fn mesh(&self) -> Self::Output {
|
||||
// The ellipse mesh is just a regular polygon with two radii
|
||||
Ellipse::new(self.circumcircle.radius, self.circumcircle.radius)
|
||||
.mesh()
|
||||
.resolution(self.sides)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegularPolygon> for Mesh {
|
||||
fn from(polygon: RegularPolygon) -> Self {
|
||||
polygon.mesh()
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder used for creating a [`Mesh`] with an [`Ellipse`] shape.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct EllipseMeshBuilder {
|
||||
/// The [`Ellipse`] shape.
|
||||
pub ellipse: Ellipse,
|
||||
/// The number of vertices used for the ellipse mesh.
|
||||
/// The default is `32`.
|
||||
#[doc(alias = "vertices")]
|
||||
pub resolution: usize,
|
||||
}
|
||||
|
||||
impl Default for EllipseMeshBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ellipse: Ellipse::default(),
|
||||
resolution: 32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EllipseMeshBuilder {
|
||||
/// Creates a new [`EllipseMeshBuilder`] from a given half width and half height and a vertex count.
|
||||
#[inline]
|
||||
pub const fn new(half_width: f32, half_height: f32, resolution: usize) -> Self {
|
||||
Self {
|
||||
ellipse: Ellipse::new(half_width, half_height),
|
||||
resolution,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the number of vertices used for the ellipse mesh.
|
||||
#[inline]
|
||||
#[doc(alias = "vertices")]
|
||||
pub const fn resolution(mut self, resolution: usize) -> Self {
|
||||
self.resolution = resolution;
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
||||
pub fn build(&self) -> Mesh {
|
||||
let mut indices = Vec::with_capacity((self.resolution - 2) * 3);
|
||||
let mut positions = Vec::with_capacity(self.resolution);
|
||||
let normals = vec![[0.0, 0.0, 1.0]; self.resolution];
|
||||
let mut uvs = Vec::with_capacity(self.resolution);
|
||||
|
||||
// Add pi/2 so that there is a vertex at the top (sin is 1.0 and cos is 0.0)
|
||||
let start_angle = std::f32::consts::FRAC_PI_2;
|
||||
let step = std::f32::consts::TAU / self.resolution as f32;
|
||||
|
||||
for i in 0..self.resolution {
|
||||
// Compute vertex position at angle theta
|
||||
let theta = start_angle + i as f32 * step;
|
||||
let (sin, cos) = theta.sin_cos();
|
||||
let x = cos * self.ellipse.half_size.x;
|
||||
let y = sin * self.ellipse.half_size.y;
|
||||
|
||||
positions.push([x, y, 0.0]);
|
||||
uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
|
||||
}
|
||||
|
||||
for i in 1..(self.resolution as u32 - 1) {
|
||||
indices.extend_from_slice(&[0, i, i + 1]);
|
||||
}
|
||||
|
||||
Mesh::new(
|
||||
PrimitiveTopology::TriangleList,
|
||||
RenderAssetPersistencePolicy::Keep,
|
||||
)
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
|
||||
.with_indices(Some(Indices::U32(indices)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Meshable for Ellipse {
|
||||
type Output = EllipseMeshBuilder;
|
||||
|
||||
fn mesh(&self) -> Self::Output {
|
||||
EllipseMeshBuilder {
|
||||
ellipse: *self,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ellipse> for Mesh {
|
||||
fn from(ellipse: Ellipse) -> Self {
|
||||
ellipse.mesh().build()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EllipseMeshBuilder> for Mesh {
|
||||
fn from(ellipse: EllipseMeshBuilder) -> Self {
|
||||
ellipse.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Meshable for Triangle2d {
|
||||
type Output = Mesh;
|
||||
|
||||
fn mesh(&self) -> Self::Output {
|
||||
let [a, b, c] = self.vertices;
|
||||
|
||||
let positions = vec![[a.x, a.y, 0.0], [b.x, b.y, 0.0], [c.x, c.y, 0.0]];
|
||||
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 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])
|
||||
};
|
||||
|
||||
Mesh::new(
|
||||
PrimitiveTopology::TriangleList,
|
||||
RenderAssetPersistencePolicy::Keep,
|
||||
)
|
||||
.with_indices(Some(indices))
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Triangle2d> for Mesh {
|
||||
fn from(triangle: Triangle2d) -> Self {
|
||||
triangle.mesh()
|
||||
}
|
||||
}
|
||||
|
||||
impl Meshable for Rectangle {
|
||||
type Output = Mesh;
|
||||
|
||||
fn mesh(&self) -> Self::Output {
|
||||
let [hw, hh] = [self.half_size.x, self.half_size.y];
|
||||
let positions = vec![
|
||||
[hw, hh, 0.0],
|
||||
[-hw, hh, 0.0],
|
||||
[-hw, -hh, 0.0],
|
||||
[hw, -hh, 0.0],
|
||||
];
|
||||
let normals = vec![[0.0, 0.0, 1.0]; 4];
|
||||
let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
|
||||
let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]);
|
||||
|
||||
Mesh::new(
|
||||
PrimitiveTopology::TriangleList,
|
||||
RenderAssetPersistencePolicy::Keep,
|
||||
)
|
||||
.with_indices(Some(indices))
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rectangle> for Mesh {
|
||||
fn from(rectangle: Rectangle) -> Self {
|
||||
rectangle.mesh()
|
||||
}
|
||||
}
|
35
crates/bevy_render/src/mesh/primitives/mod.rs
Normal file
35
crates/bevy_render/src/mesh/primitives/mod.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
//! Mesh generation for [primitive shapes](bevy_math::primitives).
|
||||
//!
|
||||
//! Primitives that support meshing implement the [`Meshable`] trait.
|
||||
//! Calling [`mesh`](Meshable::mesh) will return either a [`Mesh`](super::Mesh) or a builder
|
||||
//! that can be used to specify shape-specific configuration for creating the [`Mesh`](super::Mesh).
|
||||
//!
|
||||
//! ```
|
||||
//! # use bevy_asset::Assets;
|
||||
//! # use bevy_ecs::prelude::ResMut;
|
||||
//! # use bevy_math::prelude::Circle;
|
||||
//! # use bevy_render::prelude::*;
|
||||
//! #
|
||||
//! # fn setup(mut meshes: ResMut<Assets<Mesh>>) {
|
||||
//! // Create circle mesh with default configuration
|
||||
//! let circle = meshes.add(Circle { radius: 25.0 });
|
||||
//!
|
||||
//! // Specify number of vertices
|
||||
//! let circle = meshes.add(Circle { radius: 25.0 }.mesh().resolution(64));
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod dim2;
|
||||
pub use dim2::{CircleMeshBuilder, EllipseMeshBuilder};
|
||||
|
||||
/// A trait for shapes that can be turned into a [`Mesh`](super::Mesh).
|
||||
pub trait Meshable {
|
||||
/// The output of [`Self::mesh`]. This can either be a [`Mesh`](super::Mesh)
|
||||
/// or a builder used for creating a [`Mesh`](super::Mesh).
|
||||
type Output;
|
||||
|
||||
/// Creates a [`Mesh`](super::Mesh) for a shape.
|
||||
fn mesh(&self) -> Self::Output;
|
||||
}
|
|
@ -18,36 +18,47 @@ fn setup(
|
|||
|
||||
// Circle
|
||||
commands.spawn(MaterialMesh2dBundle {
|
||||
mesh: meshes.add(shape::Circle::new(50.)).into(),
|
||||
material: materials.add(Color::PURPLE),
|
||||
transform: Transform::from_translation(Vec3::new(-150., 0., 0.)),
|
||||
mesh: meshes.add(Circle { radius: 50.0 }).into(),
|
||||
material: materials.add(Color::VIOLET),
|
||||
transform: Transform::from_translation(Vec3::new(-225.0, 0.0, 0.0)),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Ellipse
|
||||
commands.spawn(MaterialMesh2dBundle {
|
||||
mesh: meshes.add(Ellipse::new(25.0, 50.0)).into(),
|
||||
material: materials.add(Color::TURQUOISE),
|
||||
transform: Transform::from_translation(Vec3::new(-100.0, 0.0, 0.0)),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Rectangle
|
||||
commands.spawn(SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color: Color::rgb(0.25, 0.25, 0.75),
|
||||
custom_size: Some(Vec2::new(50.0, 100.0)),
|
||||
..default()
|
||||
},
|
||||
transform: Transform::from_translation(Vec3::new(-50., 0., 0.)),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Quad
|
||||
commands.spawn(MaterialMesh2dBundle {
|
||||
mesh: meshes.add(shape::Quad::new(Vec2::new(50., 100.))).into(),
|
||||
mesh: meshes.add(Rectangle::new(50.0, 100.0)).into(),
|
||||
material: materials.add(Color::LIME_GREEN),
|
||||
transform: Transform::from_translation(Vec3::new(50., 0., 0.)),
|
||||
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Hexagon
|
||||
commands.spawn(MaterialMesh2dBundle {
|
||||
mesh: meshes.add(shape::RegularPolygon::new(50., 6)).into(),
|
||||
material: materials.add(Color::TURQUOISE),
|
||||
transform: Transform::from_translation(Vec3::new(150., 0., 0.)),
|
||||
mesh: meshes.add(RegularPolygon::new(50.0, 6)).into(),
|
||||
material: materials.add(Color::YELLOW),
|
||||
transform: Transform::from_translation(Vec3::new(125.0, 0.0, 0.0)),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Triangle
|
||||
commands.spawn(MaterialMesh2dBundle {
|
||||
mesh: meshes
|
||||
.add(Triangle2d::new(
|
||||
Vec2::Y * 50.0,
|
||||
Vec2::new(-50.0, -50.0),
|
||||
Vec2::new(50.0, -50.0),
|
||||
))
|
||||
.into(),
|
||||
material: materials.add(Color::ORANGE),
|
||||
transform: Transform::from_translation(Vec3::new(250.0, 0.0, 0.0)),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ Example | Description
|
|||
[2D Bloom](../examples/2d/bloom_2d.rs) | Illustrates bloom post-processing in 2d
|
||||
[2D Gizmos](../examples/2d/2d_gizmos.rs) | A scene showcasing 2D gizmos
|
||||
[2D Rotation](../examples/2d/rotation.rs) | Demonstrates rotating entities in 2D with quaternions
|
||||
[2D Shapes](../examples/2d/2d_shapes.rs) | Renders a rectangle, circle, and hexagon
|
||||
[2D Shapes](../examples/2d/2d_shapes.rs) | Renders simple 2D primitive shapes like circles and polygons
|
||||
[2D Viewport To World](../examples/2d/2d_viewport_to_world.rs) | Demonstrates how to use the `Camera::viewport_to_world_2d` method
|
||||
[Custom glTF vertex attribute 2D](../examples/2d/custom_gltf_vertex_attribute.rs) | Renders a glTF mesh in 2D with a custom vertex attribute
|
||||
[Manual Mesh 2D](../examples/2d/mesh2d_manual.rs) | Renders a custom mesh "manually" with "mid-level" renderer apis
|
||||
|
|
Loading…
Reference in a new issue