Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

1
vendor/bevy_mesh/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{"Cargo.lock":"0c82a0748e06bacb98c190dc16ad37439d6e111920c6528d8b94e301783118a2","Cargo.toml":"0125b533c039ecc4cbdf54a0e71a23872079a5bd80373a1262be606f9fd8548e","LICENSE-APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE-MIT":"508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1","src/conversions.rs":"a6c3d5645188e1b38be34bc560f1dbe1d257d64977983bbf295cade8d78ea434","src/index.rs":"750c26a0dd83618bb71b5ca4c012fd9242ef638ff212554a9695685e91c2a773","src/lib.rs":"ad33e48eb32db3a55d7b53dbae3d046d07639475034585b6f96db2cf3ea9c416","src/mesh.rs":"67cf3efc1b89f26c9772615fea94eec728f919ca6e12ebf232673511f07dba34","src/mikktspace.rs":"2cf626c60612fc33c1006ca20e5779bce45d54455f0119fd7b2c66388d78cce0","src/morph.rs":"9a766832602de04492d54df1c8f1bf06a92c3cd13a213288d4c3e3271619a8aa","src/primitives/dim2.rs":"4c6df74a68fd70c6d614bae2bc6d3f60510bf388cf743a0e6cdcec162ab1c8a5","src/primitives/dim3/capsule.rs":"4e1ef48de32bfc72e40ff49def5434b7c016a9598442840f5c7b43dbc55a5149","src/primitives/dim3/cone.rs":"f368594688743314ade6e0bf6080caedbab6b8db17a5c7793b02441f8710ebde","src/primitives/dim3/conical_frustum.rs":"157ac2770df678fb940900538ace992e13d2f8d85e0b56c1277c715118620685","src/primitives/dim3/cuboid.rs":"33f3b4aa94024de7b0accdb8a5b421ff0d4245a77f76e96d7c83580779a9399a","src/primitives/dim3/cylinder.rs":"d2e9551d506c10b9f0fc97c70b3e5c44222f26fcd76a94f8d26ed35f0a8801f5","src/primitives/dim3/mod.rs":"5c0ca67cce94052ff07577a3f36fe3d43eeb2db770a09506644e2ad94071d2ef","src/primitives/dim3/plane.rs":"9c10afad9eaba3bd3fc43ac2b9311b1cb8c606e78121497018ac2d52f6decfd2","src/primitives/dim3/sphere.rs":"5f117cf825b77744d4e320dedbfce89e0c36e0544da5fce9a6529248aa8af893","src/primitives/dim3/tetrahedron.rs":"2302b68467b6d0330cacf9d78006276b80eb01cd37c9ddf9425e1a860d0d6df1","src/primitives/dim3/torus.rs":"682013a456ea9f332eaaee04c18208e4858cf59f3482929268f26e47412fe18b","src/primitives/dim3/triangle3d.rs":"fae0a86ab38f754a43a671d5a7cdac92291a845abdadf30608132fb326f97102","src/primitives/extrusion.rs":"c6ec3aebbe939e263b29041594c5ce138802cda18580092b377c5a3e1afa666a","src/primitives/mod.rs":"2d185ba06cdfe70e76fb7fd4d167f152be580e19b6729c2d2b4a075514872c14","src/skinning.rs":"fdcb80d861d24c1fc1bc0a2ff07b527fc88396607a83de8a2eae18647ae177b8","src/vertex.rs":"2802806c876a70e2ca24da82292fd1c6cdae4586dd5de6cf26c483503ea6a988"},"package":"b10399c7027001edbc0406d7d0198596b1f07206c1aae715274106ba5bdcac40"}

2343
vendor/bevy_mesh/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

137
vendor/bevy_mesh/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,137 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2024"
name = "bevy_mesh"
version = "0.16.1"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Provides mesh types for Bevy Engine"
homepage = "https://bevyengine.org"
readme = false
keywords = ["bevy"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/bevyengine/bevy"
resolver = "2"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"-Zunstable-options",
"--generate-link-to-definition",
]
[lib]
name = "bevy_mesh"
path = "src/lib.rs"
[dependencies.bevy_asset]
version = "0.16.1"
[dependencies.bevy_derive]
version = "0.16.1"
[dependencies.bevy_ecs]
version = "0.16.1"
[dependencies.bevy_image]
version = "0.16.1"
[dependencies.bevy_math]
version = "0.16.1"
[dependencies.bevy_mikktspace]
version = "0.16.1"
[dependencies.bevy_platform]
version = "0.16.1"
features = [
"std",
"serialize",
]
default-features = false
[dependencies.bevy_reflect]
version = "0.16.1"
[dependencies.bevy_transform]
version = "0.16.1"
[dependencies.bevy_utils]
version = "0.16.1"
[dependencies.bitflags]
version = "2.3"
features = ["serde"]
[dependencies.bytemuck]
version = "1.5"
[dependencies.hexasphere]
version = "15.0"
[dependencies.serde]
version = "1"
features = ["derive"]
[dependencies.thiserror]
version = "2"
default-features = false
[dependencies.tracing]
version = "0.1"
features = ["std"]
default-features = false
[dependencies.wgpu-types]
version = "24"
default-features = false
[lints.clippy]
alloc_instead_of_core = "warn"
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
doc_markdown = "warn"
manual_let_else = "warn"
match_same_arms = "warn"
needless_lifetimes = "allow"
nonstandard_macro_braces = "warn"
print_stderr = "warn"
print_stdout = "warn"
ptr_as_ptr = "warn"
ptr_cast_constness = "warn"
redundant_closure_for_method_calls = "warn"
redundant_else = "warn"
ref_as_ptr = "warn"
semicolon_if_nothing_returned = "warn"
std_instead_of_alloc = "warn"
std_instead_of_core = "warn"
too_long_first_doc_paragraph = "allow"
too_many_arguments = "allow"
type_complexity = "allow"
undocumented_unsafe_blocks = "warn"
unwrap_or_default = "warn"
[lints.rust]
missing_docs = "warn"
unsafe_code = "deny"
unsafe_op_in_unsafe_fn = "warn"
unused_qualifications = "warn"
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(docsrs_dep)"]

176
vendor/bevy_mesh/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

19
vendor/bevy_mesh/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

459
vendor/bevy_mesh/src/conversions.rs vendored Normal file
View File

@@ -0,0 +1,459 @@
//! These implementations allow you to
//! convert `std::vec::Vec<T>` to `VertexAttributeValues::T` and back.
//!
//! # Examples
//!
//! ```
//! use bevy_mesh::VertexAttributeValues;
//!
//! // creating std::vec::Vec
//! let buffer = vec![[0_u32; 4]; 10];
//!
//! // converting std::vec::Vec to bevy_mesh::VertexAttributeValues
//! let values = VertexAttributeValues::from(buffer.clone());
//!
//! // converting bevy_mesh::VertexAttributeValues to std::vec::Vec with two ways
//! let result_into: Vec<[u32; 4]> = values.clone().try_into().unwrap();
//! let result_from: Vec<[u32; 4]> = Vec::try_from(values.clone()).unwrap();
//!
//! // getting an error when trying to convert incorrectly
//! let error: Result<Vec<u32>, _> = values.try_into();
//!
//! assert_eq!(buffer, result_into);
//! assert_eq!(buffer, result_from);
//! assert!(error.is_err());
//! ```
use super::VertexAttributeValues;
use bevy_math::{IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4};
use thiserror::Error;
#[derive(Debug, Clone, Error)]
#[error("cannot convert VertexAttributeValues::{variant} to {into}")]
pub struct FromVertexAttributeError {
from: VertexAttributeValues,
variant: &'static str,
into: &'static str,
}
impl FromVertexAttributeError {
fn new<T: 'static>(from: VertexAttributeValues) -> Self {
Self {
variant: from.enum_variant_name(),
into: core::any::type_name::<T>(),
from,
}
}
}
macro_rules! impl_from {
($from:ty, $variant:tt) => {
impl From<Vec<$from>> for VertexAttributeValues {
fn from(vec: Vec<$from>) -> Self {
VertexAttributeValues::$variant(vec)
}
}
};
}
macro_rules! impl_from_into {
($from:ty, $variant:tt) => {
impl From<Vec<$from>> for VertexAttributeValues {
fn from(vec: Vec<$from>) -> Self {
let vec: Vec<_> = vec.into_iter().map(|t| t.into()).collect();
VertexAttributeValues::$variant(vec)
}
}
};
}
impl_from!(f32, Float32);
impl_from!([f32; 2], Float32x2);
impl_from_into!(Vec2, Float32x2);
impl_from!([f32; 3], Float32x3);
impl_from_into!(Vec3, Float32x3);
impl_from_into!(Vec3A, Float32x3);
impl_from!([f32; 4], Float32x4);
impl_from_into!(Vec4, Float32x4);
impl_from!(i32, Sint32);
impl_from!([i32; 2], Sint32x2);
impl_from_into!(IVec2, Sint32x2);
impl_from!([i32; 3], Sint32x3);
impl_from_into!(IVec3, Sint32x3);
impl_from!([i32; 4], Sint32x4);
impl_from_into!(IVec4, Sint32x4);
impl_from!(u32, Uint32);
impl_from!([u32; 2], Uint32x2);
impl_from_into!(UVec2, Uint32x2);
impl_from!([u32; 3], Uint32x3);
impl_from_into!(UVec3, Uint32x3);
impl_from!([u32; 4], Uint32x4);
impl_from_into!(UVec4, Uint32x4);
macro_rules! impl_try_from {
($into:ty, $($variant:tt), +) => {
impl TryFrom<VertexAttributeValues> for Vec<$into> {
type Error = FromVertexAttributeError;
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value {
$(VertexAttributeValues::$variant(value)) |+ => Ok(value),
_ => Err(FromVertexAttributeError::new::<Self>(value)),
}
}
}
};
}
macro_rules! impl_try_from_into {
($into:ty, $($variant:tt), +) => {
impl TryFrom<VertexAttributeValues> for Vec<$into> {
type Error = FromVertexAttributeError;
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value {
$(VertexAttributeValues::$variant(value)) |+ => {
Ok(value.into_iter().map(|t| t.into()).collect())
}
_ => Err(FromVertexAttributeError::new::<Self>(value)),
}
}
}
};
}
impl_try_from!(f32, Float32);
impl_try_from!([f32; 2], Float32x2);
impl_try_from_into!(Vec2, Float32x2);
impl_try_from!([f32; 3], Float32x3);
impl_try_from_into!(Vec3, Float32x3);
impl_try_from_into!(Vec3A, Float32x3);
impl_try_from!([f32; 4], Float32x4);
impl_try_from_into!(Vec4, Float32x4);
impl_try_from!(i32, Sint32);
impl_try_from!([i32; 2], Sint32x2);
impl_try_from_into!(IVec2, Sint32x2);
impl_try_from!([i32; 3], Sint32x3);
impl_try_from_into!(IVec3, Sint32x3);
impl_try_from!([i32; 4], Sint32x4);
impl_try_from_into!(IVec4, Sint32x4);
impl_try_from!(u32, Uint32);
impl_try_from!([u32; 2], Uint32x2);
impl_try_from_into!(UVec2, Uint32x2);
impl_try_from!([u32; 3], Uint32x3);
impl_try_from_into!(UVec3, Uint32x3);
impl_try_from!([u32; 4], Uint32x4);
impl_try_from_into!(UVec4, Uint32x4);
impl_try_from!([i8; 2], Sint8x2, Snorm8x2);
impl_try_from!([i8; 4], Sint8x4, Snorm8x4);
impl_try_from!([u8; 2], Uint8x2, Unorm8x2);
impl_try_from!([u8; 4], Uint8x4, Unorm8x4);
impl_try_from!([i16; 2], Sint16x2, Snorm16x2);
impl_try_from!([i16; 4], Sint16x4, Snorm16x4);
impl_try_from!([u16; 2], Uint16x2, Unorm16x2);
impl_try_from!([u16; 4], Uint16x4, Unorm16x4);
#[cfg(test)]
mod tests {
use bevy_math::{IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4};
use super::VertexAttributeValues;
#[test]
fn f32() {
let buffer = vec![0.0; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<f32> = values.clone().try_into().unwrap();
let result_from: Vec<f32> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn i32() {
let buffer = vec![0; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<i32> = values.clone().try_into().unwrap();
let result_from: Vec<i32> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn u32() {
let buffer = vec![0_u32; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<u32> = values.clone().try_into().unwrap();
let result_from: Vec<u32> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<f32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn f32_2() {
let buffer = vec![[0.0; 2]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[f32; 2]> = values.clone().try_into().unwrap();
let result_from: Vec<[f32; 2]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn vec2() {
let buffer = vec![Vec2::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Float32x2(_)));
let result_into: Vec<Vec2> = values.clone().try_into().unwrap();
let result_from: Vec<Vec2> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn i32_2() {
let buffer = vec![[0; 2]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[i32; 2]> = values.clone().try_into().unwrap();
let result_from: Vec<[i32; 2]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn ivec2() {
let buffer = vec![IVec2::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Sint32x2(_)));
let result_into: Vec<IVec2> = values.clone().try_into().unwrap();
let result_from: Vec<IVec2> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn u32_2() {
let buffer = vec![[0_u32; 2]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[u32; 2]> = values.clone().try_into().unwrap();
let result_from: Vec<[u32; 2]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn uvec2() {
let buffer = vec![UVec2::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Uint32x2(_)));
let result_into: Vec<UVec2> = values.clone().try_into().unwrap();
let result_from: Vec<UVec2> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn f32_3() {
let buffer = vec![[0.0; 3]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[f32; 3]> = values.clone().try_into().unwrap();
let result_from: Vec<[f32; 3]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn vec3() {
let buffer = vec![Vec3::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Float32x3(_)));
let result_into: Vec<Vec3> = values.clone().try_into().unwrap();
let result_from: Vec<Vec3> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn vec3a() {
let buffer = vec![Vec3A::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Float32x3(_)));
let result_into: Vec<Vec3A> = values.clone().try_into().unwrap();
let result_from: Vec<Vec3A> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn i32_3() {
let buffer = vec![[0; 3]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[i32; 3]> = values.clone().try_into().unwrap();
let result_from: Vec<[i32; 3]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn ivec3() {
let buffer = vec![IVec3::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Sint32x3(_)));
let result_into: Vec<IVec3> = values.clone().try_into().unwrap();
let result_from: Vec<IVec3> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn u32_3() {
let buffer = vec![[0_u32; 3]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[u32; 3]> = values.clone().try_into().unwrap();
let result_from: Vec<[u32; 3]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn uvec3() {
let buffer = vec![UVec3::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Uint32x3(_)));
let result_into: Vec<UVec3> = values.clone().try_into().unwrap();
let result_from: Vec<UVec3> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn f32_4() {
let buffer = vec![[0.0; 4]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[f32; 4]> = values.clone().try_into().unwrap();
let result_from: Vec<[f32; 4]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn vec4() {
let buffer = vec![Vec4::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Float32x4(_)));
let result_into: Vec<Vec4> = values.clone().try_into().unwrap();
let result_from: Vec<Vec4> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn i32_4() {
let buffer = vec![[0; 4]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[i32; 4]> = values.clone().try_into().unwrap();
let result_from: Vec<[i32; 4]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn ivec4() {
let buffer = vec![IVec4::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Sint32x4(_)));
let result_into: Vec<IVec4> = values.clone().try_into().unwrap();
let result_from: Vec<IVec4> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn u32_4() {
let buffer = vec![[0_u32; 4]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[u32; 4]> = values.clone().try_into().unwrap();
let result_from: Vec<[u32; 4]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn uvec4() {
let buffer = vec![UVec4::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Uint32x4(_)));
let result_into: Vec<UVec4> = values.clone().try_into().unwrap();
let result_from: Vec<UVec4> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn correct_message() {
let buffer = vec![[0_u32; 4]; 3];
let values = VertexAttributeValues::from(buffer);
let error_result: Result<Vec<u32>, _> = values.try_into();
let error = match error_result {
Ok(..) => unreachable!(),
Err(error) => error,
};
assert_eq!(
error.to_string(),
"cannot convert VertexAttributeValues::Uint32x4 to alloc::vec::Vec<u32>"
);
assert_eq!(format!("{error:?}"),
"FromVertexAttributeError { from: Uint32x4([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), variant: \"Uint32x4\", into: \"alloc::vec::Vec<u32>\" }");
}
}

225
vendor/bevy_mesh/src/index.rs vendored Normal file
View File

@@ -0,0 +1,225 @@
use bevy_reflect::Reflect;
use core::iter;
use core::iter::FusedIterator;
use thiserror::Error;
use wgpu_types::IndexFormat;
/// A disjunction of four iterators. This is necessary to have a well-formed type for the output
/// of [`Mesh::triangles`](super::Mesh::triangles), which produces iterators of four different types depending on the
/// branch taken.
pub(crate) enum FourIterators<A, B, C, D> {
First(A),
Second(B),
Third(C),
Fourth(D),
}
impl<A, B, C, D, I> Iterator for FourIterators<A, B, C, D>
where
A: Iterator<Item = I>,
B: Iterator<Item = I>,
C: Iterator<Item = I>,
D: Iterator<Item = I>,
{
type Item = I;
fn next(&mut self) -> Option<Self::Item> {
match self {
FourIterators::First(iter) => iter.next(),
FourIterators::Second(iter) => iter.next(),
FourIterators::Third(iter) => iter.next(),
FourIterators::Fourth(iter) => iter.next(),
}
}
}
/// An error that occurred while trying to invert the winding of a [`Mesh`](super::Mesh).
#[derive(Debug, Error)]
pub enum MeshWindingInvertError {
/// This error occurs when you try to invert the winding for a mesh with [`PrimitiveTopology::PointList`](super::PrimitiveTopology::PointList).
#[error("Mesh winding inversion does not work for primitive topology `PointList`")]
WrongTopology,
/// This error occurs when you try to invert the winding for a mesh with
/// * [`PrimitiveTopology::TriangleList`](super::PrimitiveTopology::TriangleList), but the indices are not in chunks of 3.
/// * [`PrimitiveTopology::LineList`](super::PrimitiveTopology::LineList), but the indices are not in chunks of 2.
#[error("Indices weren't in chunks according to topology")]
AbruptIndicesEnd,
}
/// An error that occurred while trying to extract a collection of triangles from a [`Mesh`](super::Mesh).
#[derive(Debug, Error)]
pub enum MeshTrianglesError {
#[error("Source mesh does not have primitive topology TriangleList or TriangleStrip")]
WrongTopology,
#[error("Source mesh lacks position data")]
MissingPositions,
#[error("Source mesh position data is not Float32x3")]
PositionsFormat,
#[error("Source mesh lacks face index data")]
MissingIndices,
#[error("Face index data references vertices that do not exist")]
BadIndices,
}
/// An array of indices into the [`VertexAttributeValues`](super::VertexAttributeValues) for a mesh.
///
/// It describes the order in which the vertex attributes should be joined into faces.
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub enum Indices {
U16(Vec<u16>),
U32(Vec<u32>),
}
impl Indices {
/// Returns an iterator over the indices.
pub fn iter(&self) -> impl Iterator<Item = usize> + '_ {
match self {
Indices::U16(vec) => IndicesIter::U16(vec.iter()),
Indices::U32(vec) => IndicesIter::U32(vec.iter()),
}
}
/// Returns the number of indices.
pub fn len(&self) -> usize {
match self {
Indices::U16(vec) => vec.len(),
Indices::U32(vec) => vec.len(),
}
}
/// Returns `true` if there are no indices.
pub fn is_empty(&self) -> bool {
match self {
Indices::U16(vec) => vec.is_empty(),
Indices::U32(vec) => vec.is_empty(),
}
}
/// Add an index. If the index is greater than `u16::MAX`,
/// the storage will be converted to `u32`.
pub fn push(&mut self, index: u32) {
self.extend([index]);
}
}
/// Extend the indices with indices from an iterator.
/// Semantically equivalent to calling [`push`](Indices::push) for each element in the iterator,
/// but more efficient.
impl Extend<u32> for Indices {
fn extend<T: IntoIterator<Item = u32>>(&mut self, iter: T) {
let mut iter = iter.into_iter();
match self {
Indices::U32(indices) => indices.extend(iter),
Indices::U16(indices) => {
indices.reserve(iter.size_hint().0);
while let Some(index) = iter.next() {
match u16::try_from(index) {
Ok(index) => indices.push(index),
Err(_) => {
let new_vec = indices
.iter()
.map(|&index| u32::from(index))
.chain(iter::once(index))
.chain(iter)
.collect::<Vec<u32>>();
*self = Indices::U32(new_vec);
break;
}
}
}
}
}
}
}
/// An Iterator for the [`Indices`].
enum IndicesIter<'a> {
U16(core::slice::Iter<'a, u16>),
U32(core::slice::Iter<'a, u32>),
}
impl Iterator for IndicesIter<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
match self {
IndicesIter::U16(iter) => iter.next().map(|val| *val as usize),
IndicesIter::U32(iter) => iter.next().map(|val| *val as usize),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
IndicesIter::U16(iter) => iter.size_hint(),
IndicesIter::U32(iter) => iter.size_hint(),
}
}
}
impl<'a> ExactSizeIterator for IndicesIter<'a> {}
impl<'a> FusedIterator for IndicesIter<'a> {}
impl From<&Indices> for IndexFormat {
fn from(indices: &Indices) -> Self {
match indices {
Indices::U16(_) => IndexFormat::Uint16,
Indices::U32(_) => IndexFormat::Uint32,
}
}
}
#[cfg(test)]
mod tests {
use crate::Indices;
use wgpu_types::IndexFormat;
#[test]
fn test_indices_push() {
let mut indices = Indices::U16(Vec::new());
indices.push(10);
assert_eq!(IndexFormat::Uint16, IndexFormat::from(&indices));
assert_eq!(vec![10], indices.iter().collect::<Vec<_>>());
// Add a value that is too large for `u16` so the storage should be converted to `U32`.
indices.push(0x10000);
assert_eq!(IndexFormat::Uint32, IndexFormat::from(&indices));
assert_eq!(vec![10, 0x10000], indices.iter().collect::<Vec<_>>());
indices.push(20);
indices.push(0x20000);
assert_eq!(IndexFormat::Uint32, IndexFormat::from(&indices));
assert_eq!(
vec![10, 0x10000, 20, 0x20000],
indices.iter().collect::<Vec<_>>()
);
}
#[test]
fn test_indices_extend() {
let mut indices = Indices::U16(Vec::new());
indices.extend([10, 11]);
assert_eq!(IndexFormat::Uint16, IndexFormat::from(&indices));
assert_eq!(vec![10, 11], indices.iter().collect::<Vec<_>>());
// Add a value that is too large for `u16` so the storage should be converted to `U32`.
indices.extend([12, 0x10013, 0x10014]);
assert_eq!(IndexFormat::Uint32, IndexFormat::from(&indices));
assert_eq!(
vec![10, 11, 12, 0x10013, 0x10014],
indices.iter().collect::<Vec<_>>()
);
indices.extend([15, 0x10016]);
assert_eq!(IndexFormat::Uint32, IndexFormat::from(&indices));
assert_eq!(
vec![10, 11, 12, 0x10013, 0x10014, 15, 0x10016],
indices.iter().collect::<Vec<_>>()
);
}
}

57
vendor/bevy_mesh/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,57 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
extern crate alloc;
extern crate core;
mod conversions;
mod index;
mod mesh;
mod mikktspace;
pub mod morph;
pub mod primitives;
pub mod skinning;
mod vertex;
use bitflags::bitflags;
pub use index::*;
pub use mesh::*;
pub use mikktspace::*;
pub use primitives::*;
pub use vertex::*;
pub use wgpu_types::VertexFormat;
bitflags! {
/// Our base mesh pipeline key bits start from the highest bit and go
/// downward. The PBR mesh pipeline key bits start from the lowest bit and
/// go upward. This allows the PBR bits in the downstream crate `bevy_pbr`
/// to coexist in the same field without any shifts.
#[derive(Clone, Debug)]
pub struct BaseMeshPipelineKey: u64 {
const MORPH_TARGETS = 1 << (u64::BITS - 1);
}
}
impl BaseMeshPipelineKey {
pub const PRIMITIVE_TOPOLOGY_MASK_BITS: u64 = 0b111;
pub const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u64 =
(u64::BITS - 1 - Self::PRIMITIVE_TOPOLOGY_MASK_BITS.count_ones()) as u64;
pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self {
let primitive_topology_bits = ((primitive_topology as u64)
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS)
<< Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
Self::from_bits_retain(primitive_topology_bits)
}
pub fn primitive_topology(&self) -> PrimitiveTopology {
let primitive_topology_bits = (self.bits() >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS;
match primitive_topology_bits {
x if x == PrimitiveTopology::PointList as u64 => PrimitiveTopology::PointList,
x if x == PrimitiveTopology::LineList as u64 => PrimitiveTopology::LineList,
x if x == PrimitiveTopology::LineStrip as u64 => PrimitiveTopology::LineStrip,
x if x == PrimitiveTopology::TriangleList as u64 => PrimitiveTopology::TriangleList,
x if x == PrimitiveTopology::TriangleStrip as u64 => PrimitiveTopology::TriangleStrip,
_ => PrimitiveTopology::default(),
}
}
}

1554
vendor/bevy_mesh/src/mesh.rs vendored Normal file

File diff suppressed because it is too large Load Diff

142
vendor/bevy_mesh/src/mikktspace.rs vendored Normal file
View File

@@ -0,0 +1,142 @@
use super::{Indices, Mesh, VertexAttributeValues};
use bevy_math::Vec3;
use thiserror::Error;
use wgpu_types::{PrimitiveTopology, VertexFormat};
struct MikktspaceGeometryHelper<'a> {
indices: Option<&'a Indices>,
positions: &'a Vec<[f32; 3]>,
normals: &'a Vec<[f32; 3]>,
uvs: &'a Vec<[f32; 2]>,
tangents: Vec<[f32; 4]>,
}
impl MikktspaceGeometryHelper<'_> {
fn index(&self, face: usize, vert: usize) -> usize {
let index_index = face * 3 + vert;
match self.indices {
Some(Indices::U16(indices)) => indices[index_index] as usize,
Some(Indices::U32(indices)) => indices[index_index] as usize,
None => index_index,
}
}
}
impl bevy_mikktspace::Geometry for MikktspaceGeometryHelper<'_> {
fn num_faces(&self) -> usize {
self.indices
.map(Indices::len)
.unwrap_or_else(|| self.positions.len())
/ 3
}
fn num_vertices_of_face(&self, _: usize) -> usize {
3
}
fn position(&self, face: usize, vert: usize) -> [f32; 3] {
self.positions[self.index(face, vert)]
}
fn normal(&self, face: usize, vert: usize) -> [f32; 3] {
self.normals[self.index(face, vert)]
}
fn tex_coord(&self, face: usize, vert: usize) -> [f32; 2] {
self.uvs[self.index(face, vert)]
}
fn set_tangent_encoded(&mut self, tangent: [f32; 4], face: usize, vert: usize) {
let idx = self.index(face, vert);
self.tangents[idx] = tangent;
}
}
#[derive(Error, Debug)]
/// Failed to generate tangents for the mesh.
pub enum GenerateTangentsError {
#[error("cannot generate tangents for {0:?}")]
UnsupportedTopology(PrimitiveTopology),
#[error("missing indices")]
MissingIndices,
#[error("missing vertex attributes '{0}'")]
MissingVertexAttribute(&'static str),
#[error("the '{0}' vertex attribute should have {1:?} format")]
InvalidVertexAttributeFormat(&'static str, VertexFormat),
#[error("mesh not suitable for tangent generation")]
MikktspaceError,
}
pub(crate) fn generate_tangents_for_mesh(
mesh: &Mesh,
) -> Result<Vec<[f32; 4]>, GenerateTangentsError> {
match mesh.primitive_topology() {
PrimitiveTopology::TriangleList => {}
other => return Err(GenerateTangentsError::UnsupportedTopology(other)),
};
let positions = mesh.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
)?;
let VertexAttributeValues::Float32x3(positions) = positions else {
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
Mesh::ATTRIBUTE_POSITION.name,
VertexFormat::Float32x3,
));
};
let normals = mesh.attribute(Mesh::ATTRIBUTE_NORMAL).ok_or(
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_NORMAL.name),
)?;
let VertexAttributeValues::Float32x3(normals) = normals else {
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
Mesh::ATTRIBUTE_NORMAL.name,
VertexFormat::Float32x3,
));
};
let uvs = mesh.attribute(Mesh::ATTRIBUTE_UV_0).ok_or(
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_UV_0.name),
)?;
let VertexAttributeValues::Float32x2(uvs) = uvs else {
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
Mesh::ATTRIBUTE_UV_0.name,
VertexFormat::Float32x2,
));
};
let len = positions.len();
let tangents = vec![[0., 0., 0., 0.]; len];
let mut mikktspace_mesh = MikktspaceGeometryHelper {
indices: mesh.indices(),
positions,
normals,
uvs,
tangents,
};
let success = bevy_mikktspace::generate_tangents(&mut mikktspace_mesh);
if !success {
return Err(GenerateTangentsError::MikktspaceError);
}
// mikktspace seems to assume left-handedness so we can flip the sign to correct for this
for tangent in &mut mikktspace_mesh.tangents {
tangent[3] = -tangent[3];
}
Ok(mikktspace_mesh.tangents)
}
/// Correctly scales and renormalizes an already normalized `normal` by the scale determined by its reciprocal `scale_recip`
pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
// This is basically just `normal * scale_recip` but with the added rule that `0. * anything == 0.`
// This is necessary because components of `scale_recip` may be infinities, which do not multiply to zero
let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);
// If n is finite, no component of `scale_recip` was infinite or the normal was perpendicular to the scale
// else the scale had at least one zero-component and the normal needs to point along the direction of that component
if n.is_finite() {
n.normalize_or_zero()
} else {
Vec3::select(n.abs().cmpeq(Vec3::INFINITY), n.signum(), Vec3::ZERO).normalize()
}
}

253
vendor/bevy_mesh/src/morph.rs vendored Normal file
View File

@@ -0,0 +1,253 @@
use super::Mesh;
use bevy_asset::{Handle, RenderAssetUsages};
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_math::Vec3;
use bevy_reflect::prelude::*;
use bytemuck::{Pod, Zeroable};
use thiserror::Error;
use wgpu_types::{Extent3d, TextureDimension, TextureFormat};
const MAX_TEXTURE_WIDTH: u32 = 2048;
// NOTE: "component" refers to the element count of math objects,
// Vec3 has 3 components, Mat2 has 4 components.
const MAX_COMPONENTS: u32 = MAX_TEXTURE_WIDTH * MAX_TEXTURE_WIDTH;
/// Max target count available for [morph targets](MorphWeights).
pub const MAX_MORPH_WEIGHTS: usize = 64;
#[derive(Error, Clone, Debug)]
pub enum MorphBuildError {
#[error(
"Too many vertex×components in morph target, max is {MAX_COMPONENTS}, \
got {vertex_count}×{component_count} = {}",
*vertex_count * *component_count as usize
)]
TooManyAttributes {
vertex_count: usize,
component_count: u32,
},
#[error(
"Bevy only supports up to {} morph targets (individual poses), tried to \
create a model with {target_count} morph targets",
MAX_MORPH_WEIGHTS
)]
TooManyTargets { target_count: usize },
}
/// An image formatted for use with [`MorphWeights`] for rendering the morph target.
#[derive(Debug)]
pub struct MorphTargetImage(pub Image);
impl MorphTargetImage {
/// Generate textures for each morph target.
///
/// This accepts an "iterator of [`MorphAttributes`] iterators". Each item iterated in the top level
/// iterator corresponds "the attributes of a specific morph target".
///
/// Each pixel of the texture is a component of morph target animated
/// attributes. So a set of 9 pixels is this morph's displacement for
/// position, normal and tangents of a single vertex (each taking 3 pixels).
pub fn new(
targets: impl ExactSizeIterator<Item = impl Iterator<Item = MorphAttributes>>,
vertex_count: usize,
asset_usage: RenderAssetUsages,
) -> Result<Self, MorphBuildError> {
let max = MAX_TEXTURE_WIDTH;
let target_count = targets.len();
if target_count > MAX_MORPH_WEIGHTS {
return Err(MorphBuildError::TooManyTargets { target_count });
}
let component_count = (vertex_count * MorphAttributes::COMPONENT_COUNT) as u32;
let Some((Rect(width, height), padding)) = lowest_2d(component_count, max) else {
return Err(MorphBuildError::TooManyAttributes {
vertex_count,
component_count,
});
};
let data = targets
.flat_map(|mut attributes| {
let layer_byte_count = (padding + component_count) as usize * size_of::<f32>();
let mut buffer = Vec::with_capacity(layer_byte_count);
for _ in 0..vertex_count {
let Some(to_add) = attributes.next() else {
break;
};
buffer.extend_from_slice(bytemuck::bytes_of(&to_add));
}
// Pad each layer so that they fit width * height
buffer.extend(core::iter::repeat_n(0, padding as usize * size_of::<f32>()));
debug_assert_eq!(buffer.len(), layer_byte_count);
buffer
})
.collect();
let extents = Extent3d {
width,
height,
depth_or_array_layers: target_count as u32,
};
let image = Image::new(
extents,
TextureDimension::D3,
data,
TextureFormat::R32Float,
asset_usage,
);
Ok(MorphTargetImage(image))
}
}
/// Controls the [morph targets] for all child `Mesh3d` entities. In most cases, [`MorphWeights`] should be considered
/// the "source of truth" when writing morph targets for meshes. However you can choose to write child [`MeshMorphWeights`]
/// if your situation requires more granularity. Just note that if you set [`MorphWeights`], it will overwrite child
/// [`MeshMorphWeights`] values.
///
/// This exists because Bevy's [`Mesh`] corresponds to a _single_ surface / material, whereas morph targets
/// as defined in the GLTF spec exist on "multi-primitive meshes" (where each primitive is its own surface with its own material).
/// Therefore in Bevy [`MorphWeights`] an a parent entity are the "canonical weights" from a GLTF perspective, which then
/// synchronized to child `Mesh3d` / [`MeshMorphWeights`] (which correspond to "primitives" / "surfaces" from a GLTF perspective).
///
/// Add this to the parent of one or more [`Entities`](`Entity`) with a `Mesh3d` with a [`MeshMorphWeights`].
///
/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
#[derive(Reflect, Default, Debug, Clone, Component)]
#[reflect(Debug, Component, Default, Clone)]
pub struct MorphWeights {
weights: Vec<f32>,
/// The first mesh primitive assigned to these weights
first_mesh: Option<Handle<Mesh>>,
}
impl MorphWeights {
pub fn new(
weights: Vec<f32>,
first_mesh: Option<Handle<Mesh>>,
) -> Result<Self, MorphBuildError> {
if weights.len() > MAX_MORPH_WEIGHTS {
let target_count = weights.len();
return Err(MorphBuildError::TooManyTargets { target_count });
}
Ok(MorphWeights {
weights,
first_mesh,
})
}
/// The first child `Mesh3d` primitive controlled by these weights.
/// This can be used to look up metadata information such as [`Mesh::morph_target_names`].
pub fn first_mesh(&self) -> Option<&Handle<Mesh>> {
self.first_mesh.as_ref()
}
pub fn weights(&self) -> &[f32] {
&self.weights
}
pub fn weights_mut(&mut self) -> &mut [f32] {
&mut self.weights
}
}
/// Control a specific [`Mesh`] instance's [morph targets]. These control the weights of
/// specific "mesh primitives" in scene formats like GLTF. They can be set manually, but
/// in most cases they should "automatically" synced by setting the [`MorphWeights`] component
/// on a parent entity.
///
/// See [`MorphWeights`] for more details on Bevy's morph target implementation.
///
/// Add this to an [`Entity`] with a `Mesh3d` with a [`MorphAttributes`] set
/// to control individual weights of each morph target.
///
/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
#[derive(Reflect, Default, Debug, Clone, Component)]
#[reflect(Debug, Component, Default, Clone)]
pub struct MeshMorphWeights {
weights: Vec<f32>,
}
impl MeshMorphWeights {
pub fn new(weights: Vec<f32>) -> Result<Self, MorphBuildError> {
if weights.len() > MAX_MORPH_WEIGHTS {
let target_count = weights.len();
return Err(MorphBuildError::TooManyTargets { target_count });
}
Ok(MeshMorphWeights { weights })
}
pub fn weights(&self) -> &[f32] {
&self.weights
}
pub fn weights_mut(&mut self) -> &mut [f32] {
&mut self.weights
}
pub fn clear_weights(&mut self) {
self.weights.clear();
}
pub fn extend_weights(&mut self, weights: &[f32]) {
self.weights.extend(weights);
}
}
/// Attributes **differences** used for morph targets.
///
/// See [`MorphTargetImage`] for more information.
#[derive(Copy, Clone, PartialEq, Pod, Zeroable, Default)]
#[repr(C)]
pub struct MorphAttributes {
/// The vertex position difference between base mesh and this target.
pub position: Vec3,
/// The vertex normal difference between base mesh and this target.
pub normal: Vec3,
/// The vertex tangent difference between base mesh and this target.
///
/// Note that tangents are a `Vec4`, but only the `xyz` components are
/// animated, as the `w` component is the sign and cannot be animated.
pub tangent: Vec3,
}
impl From<[Vec3; 3]> for MorphAttributes {
fn from([position, normal, tangent]: [Vec3; 3]) -> Self {
MorphAttributes {
position,
normal,
tangent,
}
}
}
impl MorphAttributes {
/// How many components `MorphAttributes` has.
///
/// Each `Vec3` has 3 components, we have 3 `Vec3`, for a total of 9.
pub const COMPONENT_COUNT: usize = 9;
pub fn new(position: Vec3, normal: Vec3, tangent: Vec3) -> Self {
MorphAttributes {
position,
normal,
tangent,
}
}
}
struct Rect(u32, u32);
/// Find the smallest rectangle of maximum edge size `max_edge` that contains
/// at least `min_includes` cells. `u32` is how many extra cells the rectangle
/// has.
///
/// The following rectangle contains 27 cells, and its longest edge is 9:
/// ```text
/// ----------------------------
/// |1 |2 |3 |4 |5 |6 |7 |8 |9 |
/// ----------------------------
/// |2 | | | | | | | | |
/// ----------------------------
/// |3 | | | | | | | | |
/// ----------------------------
/// ```
///
/// Returns `None` if `max_edge` is too small to build a rectangle
/// containing `min_includes` cells.
fn lowest_2d(min_includes: u32, max_edge: u32) -> Option<(Rect, u32)> {
(1..=max_edge)
.filter_map(|a| {
let b = min_includes.div_ceil(a);
let diff = (a * b).checked_sub(min_includes)?;
Some((Rect(a, b), diff))
})
.filter_map(|(rect, diff)| (rect.1 <= max_edge).then_some((rect, diff)))
.min_by_key(|(_, diff)| *diff)
}

1257
vendor/bevy_mesh/src/primitives/dim2.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,436 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Capsule3d, Vec2, Vec3};
use bevy_reflect::prelude::*;
/// Manner in which UV coordinates are distributed vertically.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub enum CapsuleUvProfile {
/// UV space is distributed by how much of the capsule consists of the hemispheres.
#[default]
Aspect,
/// Hemispheres get UV space according to the ratio of latitudes to rings.
Uniform,
/// Upper third of the texture goes to the northern hemisphere, middle third to the cylinder
/// and lower third to the southern one.
Fixed,
}
/// A builder used for creating a [`Mesh`] with a [`Capsule3d`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct Capsule3dMeshBuilder {
/// The [`Capsule3d`] shape.
pub capsule: Capsule3d,
/// The number of horizontal lines subdividing the cylindrical part of the capsule.
/// The default is `0`.
pub rings: u32,
/// The number of vertical lines subdividing the hemispheres of the capsule.
/// The default is `32`.
pub longitudes: u32,
/// The number of horizontal lines subdividing the hemispheres of the capsule.
/// The default is `16`.
pub latitudes: u32,
/// The manner in which UV coordinates are distributed vertically.
/// The default is [`CapsuleUvProfile::Aspect`].
pub uv_profile: CapsuleUvProfile,
}
impl Default for Capsule3dMeshBuilder {
fn default() -> Self {
Self {
capsule: Capsule3d::default(),
rings: 0,
longitudes: 32,
latitudes: 16,
uv_profile: CapsuleUvProfile::default(),
}
}
}
impl Capsule3dMeshBuilder {
/// Creates a new [`Capsule3dMeshBuilder`] from a given radius, height, longitudes, and latitudes.
///
/// Note that `height` is the distance between the centers of the hemispheres.
/// `radius` will be added to both ends to get the real height of the mesh.
#[inline]
pub fn new(radius: f32, height: f32, longitudes: u32, latitudes: u32) -> Self {
Self {
capsule: Capsule3d::new(radius, height),
longitudes,
latitudes,
..Default::default()
}
}
/// Sets the number of horizontal lines subdividing the cylindrical part of the capsule.
#[inline]
pub const fn rings(mut self, rings: u32) -> Self {
self.rings = rings;
self
}
/// Sets the number of vertical lines subdividing the hemispheres of the capsule.
#[inline]
pub const fn longitudes(mut self, longitudes: u32) -> Self {
self.longitudes = longitudes;
self
}
/// Sets the number of horizontal lines subdividing the hemispheres of the capsule.
#[inline]
pub const fn latitudes(mut self, latitudes: u32) -> Self {
self.latitudes = latitudes;
self
}
/// Sets the manner in which UV coordinates are distributed vertically.
#[inline]
pub const fn uv_profile(mut self, uv_profile: CapsuleUvProfile) -> Self {
self.uv_profile = uv_profile;
self
}
}
impl MeshBuilder for Capsule3dMeshBuilder {
fn build(&self) -> Mesh {
// code adapted from https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db
let Capsule3dMeshBuilder {
capsule,
rings,
longitudes,
latitudes,
uv_profile,
} = *self;
let Capsule3d {
radius,
half_length,
} = capsule;
let calc_middle = rings > 0;
let half_lats = latitudes / 2;
let half_latsn1 = half_lats - 1;
let half_latsn2 = half_lats - 2;
let ringsp1 = rings + 1;
let lonsp1 = longitudes + 1;
let summit = half_length + radius;
// Vertex index offsets.
let vert_offset_north_hemi = longitudes;
let vert_offset_north_equator = vert_offset_north_hemi + lonsp1 * half_latsn1;
let vert_offset_cylinder = vert_offset_north_equator + lonsp1;
let vert_offset_south_equator = if calc_middle {
vert_offset_cylinder + lonsp1 * rings
} else {
vert_offset_cylinder
};
let vert_offset_south_hemi = vert_offset_south_equator + lonsp1;
let vert_offset_south_polar = vert_offset_south_hemi + lonsp1 * half_latsn2;
let vert_offset_south_cap = vert_offset_south_polar + lonsp1;
// Initialize arrays.
let vert_len = (vert_offset_south_cap + longitudes) as usize;
let mut vs: Vec<Vec3> = vec![Vec3::ZERO; vert_len];
let mut vts: Vec<Vec2> = vec![Vec2::ZERO; vert_len];
let mut vns: Vec<Vec3> = vec![Vec3::ZERO; vert_len];
let to_theta = 2.0 * core::f32::consts::PI / longitudes as f32;
let to_phi = core::f32::consts::PI / latitudes as f32;
let to_tex_horizontal = 1.0 / longitudes as f32;
let to_tex_vertical = 1.0 / half_lats as f32;
let vt_aspect_ratio = match uv_profile {
CapsuleUvProfile::Aspect => radius / (2.0 * half_length + radius + radius),
CapsuleUvProfile::Uniform => half_lats as f32 / (ringsp1 + latitudes) as f32,
CapsuleUvProfile::Fixed => 1.0 / 3.0,
};
let vt_aspect_north = 1.0 - vt_aspect_ratio;
let vt_aspect_south = vt_aspect_ratio;
let mut theta_cartesian: Vec<Vec2> = vec![Vec2::ZERO; longitudes as usize];
let mut rho_theta_cartesian: Vec<Vec2> = vec![Vec2::ZERO; longitudes as usize];
let mut s_texture_cache: Vec<f32> = vec![0.0; lonsp1 as usize];
for j in 0..longitudes as usize {
let jf = j as f32;
let s_texture_polar = 1.0 - ((jf + 0.5) * to_tex_horizontal);
let theta = jf * to_theta;
theta_cartesian[j] = Vec2::from_angle(theta);
rho_theta_cartesian[j] = radius * theta_cartesian[j];
// North.
vs[j] = Vec3::new(0.0, summit, 0.0);
vts[j] = Vec2::new(s_texture_polar, 1.0);
vns[j] = Vec3::Y;
// South.
let idx = vert_offset_south_cap as usize + j;
vs[idx] = Vec3::new(0.0, -summit, 0.0);
vts[idx] = Vec2::new(s_texture_polar, 0.0);
vns[idx] = Vec3::new(0.0, -1.0, 0.0);
}
// Equatorial vertices.
for (j, s_texture_cache_j) in s_texture_cache.iter_mut().enumerate().take(lonsp1 as usize) {
let s_texture = 1.0 - j as f32 * to_tex_horizontal;
*s_texture_cache_j = s_texture;
// Wrap to first element upon reaching last.
let j_mod = j % longitudes as usize;
let tc = theta_cartesian[j_mod];
let rtc = rho_theta_cartesian[j_mod];
// North equator.
let idxn = vert_offset_north_equator as usize + j;
vs[idxn] = Vec3::new(rtc.x, half_length, -rtc.y);
vts[idxn] = Vec2::new(s_texture, vt_aspect_north);
vns[idxn] = Vec3::new(tc.x, 0.0, -tc.y);
// South equator.
let idxs = vert_offset_south_equator as usize + j;
vs[idxs] = Vec3::new(rtc.x, -half_length, -rtc.y);
vts[idxs] = Vec2::new(s_texture, vt_aspect_south);
vns[idxs] = Vec3::new(tc.x, 0.0, -tc.y);
}
// Hemisphere vertices.
for i in 0..half_latsn1 {
let ip1f = i as f32 + 1.0;
let phi = ip1f * to_phi;
// For coordinates.
let (sin_phi_south, cos_phi_south) = ops::sin_cos(phi);
// Symmetrical hemispheres mean cosine and sine only needs
// to be calculated once.
let cos_phi_north = sin_phi_south;
let sin_phi_north = -cos_phi_south;
let rho_cos_phi_north = radius * cos_phi_north;
let rho_sin_phi_north = radius * sin_phi_north;
let z_offset_north = half_length - rho_sin_phi_north;
let rho_cos_phi_south = radius * cos_phi_south;
let rho_sin_phi_south = radius * sin_phi_south;
let z_offset_sout = -half_length - rho_sin_phi_south;
// For texture coordinates.
let t_tex_fac = ip1f * to_tex_vertical;
let cmpl_tex_fac = 1.0 - t_tex_fac;
let t_tex_north = cmpl_tex_fac + vt_aspect_north * t_tex_fac;
let t_tex_south = cmpl_tex_fac * vt_aspect_south;
let i_lonsp1 = i * lonsp1;
let vert_curr_lat_north = vert_offset_north_hemi + i_lonsp1;
let vert_curr_lat_south = vert_offset_south_hemi + i_lonsp1;
for (j, s_texture) in s_texture_cache.iter().enumerate().take(lonsp1 as usize) {
let j_mod = j % longitudes as usize;
let tc = theta_cartesian[j_mod];
// North hemisphere.
let idxn = vert_curr_lat_north as usize + j;
vs[idxn] = Vec3::new(
rho_cos_phi_north * tc.x,
z_offset_north,
-rho_cos_phi_north * tc.y,
);
vts[idxn] = Vec2::new(*s_texture, t_tex_north);
vns[idxn] = Vec3::new(cos_phi_north * tc.x, -sin_phi_north, -cos_phi_north * tc.y);
// South hemisphere.
let idxs = vert_curr_lat_south as usize + j;
vs[idxs] = Vec3::new(
rho_cos_phi_south * tc.x,
z_offset_sout,
-rho_cos_phi_south * tc.y,
);
vts[idxs] = Vec2::new(*s_texture, t_tex_south);
vns[idxs] = Vec3::new(cos_phi_south * tc.x, -sin_phi_south, -cos_phi_south * tc.y);
}
}
// Cylinder vertices.
if calc_middle {
// Exclude both origin and destination edges
// (North and South equators) from the interpolation.
let to_fac = 1.0 / ringsp1 as f32;
let mut idx_cyl_lat = vert_offset_cylinder as usize;
for h in 1..ringsp1 {
let fac = h as f32 * to_fac;
let cmpl_fac = 1.0 - fac;
let t_texture = cmpl_fac * vt_aspect_north + fac * vt_aspect_south;
let z = half_length - 2.0 * half_length * fac;
for (j, s_texture) in s_texture_cache.iter().enumerate().take(lonsp1 as usize) {
let j_mod = j % longitudes as usize;
let tc = theta_cartesian[j_mod];
let rtc = rho_theta_cartesian[j_mod];
vs[idx_cyl_lat] = Vec3::new(rtc.x, z, -rtc.y);
vts[idx_cyl_lat] = Vec2::new(*s_texture, t_texture);
vns[idx_cyl_lat] = Vec3::new(tc.x, 0.0, -tc.y);
idx_cyl_lat += 1;
}
}
}
// Triangle indices.
// Stride is 3 for polar triangles;
// stride is 6 for two triangles forming a quad.
let lons3 = longitudes * 3;
let lons6 = longitudes * 6;
let hemi_lons = half_latsn1 * lons6;
let tri_offset_north_hemi = lons3;
let tri_offset_cylinder = tri_offset_north_hemi + hemi_lons;
let tri_offset_south_hemi = tri_offset_cylinder + ringsp1 * lons6;
let tri_offset_south_cap = tri_offset_south_hemi + hemi_lons;
let fs_len = tri_offset_south_cap + lons3;
let mut tris: Vec<u32> = vec![0; fs_len as usize];
// Polar caps.
let mut i = 0;
let mut k = 0;
let mut m = tri_offset_south_cap as usize;
while i < longitudes {
// North.
tris[k] = i;
tris[k + 1] = vert_offset_north_hemi + i;
tris[k + 2] = vert_offset_north_hemi + i + 1;
// South.
tris[m] = vert_offset_south_cap + i;
tris[m + 1] = vert_offset_south_polar + i + 1;
tris[m + 2] = vert_offset_south_polar + i;
i += 1;
k += 3;
m += 3;
}
// Hemispheres.
let mut i = 0;
let mut k = tri_offset_north_hemi as usize;
let mut m = tri_offset_south_hemi as usize;
while i < half_latsn1 {
let i_lonsp1 = i * lonsp1;
let vert_curr_lat_north = vert_offset_north_hemi + i_lonsp1;
let vert_next_lat_north = vert_curr_lat_north + lonsp1;
let vert_curr_lat_south = vert_offset_south_equator + i_lonsp1;
let vert_next_lat_south = vert_curr_lat_south + lonsp1;
let mut j = 0;
while j < longitudes {
// North.
let north00 = vert_curr_lat_north + j;
let north01 = vert_next_lat_north + j;
let north11 = vert_next_lat_north + j + 1;
let north10 = vert_curr_lat_north + j + 1;
tris[k] = north00;
tris[k + 1] = north11;
tris[k + 2] = north10;
tris[k + 3] = north00;
tris[k + 4] = north01;
tris[k + 5] = north11;
// South.
let south00 = vert_curr_lat_south + j;
let south01 = vert_next_lat_south + j;
let south11 = vert_next_lat_south + j + 1;
let south10 = vert_curr_lat_south + j + 1;
tris[m] = south00;
tris[m + 1] = south11;
tris[m + 2] = south10;
tris[m + 3] = south00;
tris[m + 4] = south01;
tris[m + 5] = south11;
j += 1;
k += 6;
m += 6;
}
i += 1;
}
// Cylinder.
let mut i = 0;
let mut k = tri_offset_cylinder as usize;
while i < ringsp1 {
let vert_curr_lat = vert_offset_north_equator + i * lonsp1;
let vert_next_lat = vert_curr_lat + lonsp1;
let mut j = 0;
while j < longitudes {
let cy00 = vert_curr_lat + j;
let cy01 = vert_next_lat + j;
let cy11 = vert_next_lat + j + 1;
let cy10 = vert_curr_lat + j + 1;
tris[k] = cy00;
tris[k + 1] = cy11;
tris[k + 2] = cy10;
tris[k + 3] = cy00;
tris[k + 4] = cy01;
tris[k + 5] = cy11;
j += 1;
k += 6;
}
i += 1;
}
let vs: Vec<[f32; 3]> = vs.into_iter().map(Into::into).collect();
let vns: Vec<[f32; 3]> = vns.into_iter().map(Into::into).collect();
let vts: Vec<[f32; 2]> = vts.into_iter().map(Into::into).collect();
assert_eq!(vs.len(), vert_len);
assert_eq!(tris.len(), fs_len as usize);
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vs)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vns)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vts)
.with_inserted_indices(Indices::U32(tris))
}
}
impl Meshable for Capsule3d {
type Output = Capsule3dMeshBuilder;
fn mesh(&self) -> Self::Output {
Capsule3dMeshBuilder {
capsule: *self,
..Default::default()
}
}
}
impl From<Capsule3d> for Mesh {
fn from(capsule: Capsule3d) -> Self {
capsule.mesh().build()
}
}

View File

@@ -0,0 +1,271 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Cone, Vec3};
use bevy_reflect::prelude::*;
/// Anchoring options for [`ConeMeshBuilder`]
#[derive(Debug, Copy, Clone, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub enum ConeAnchor {
#[default]
/// Midpoint between the tip of the cone and the center of its base.
MidPoint,
/// The Tip of the triangle
Tip,
/// The center of the base circle
Base,
}
/// A builder used for creating a [`Mesh`] with a [`Cone`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct ConeMeshBuilder {
/// The [`Cone`] shape.
pub cone: Cone,
/// The number of vertices used for the base of the cone.
///
/// The default is `32`.
pub resolution: u32,
/// The anchor point for the cone mesh, defaults to the midpoint between
/// the tip of the cone and the center of its base
pub anchor: ConeAnchor,
}
impl Default for ConeMeshBuilder {
fn default() -> Self {
Self {
cone: Cone::default(),
resolution: 32,
anchor: ConeAnchor::default(),
}
}
}
impl ConeMeshBuilder {
/// Creates a new [`ConeMeshBuilder`] from a given radius, height,
/// and number of vertices used for the base of the cone.
#[inline]
pub const fn new(radius: f32, height: f32, resolution: u32) -> Self {
Self {
cone: Cone { radius, height },
resolution,
anchor: ConeAnchor::MidPoint,
}
}
/// Sets the number of vertices used for the base of the cone.
#[inline]
pub const fn resolution(mut self, resolution: u32) -> Self {
self.resolution = resolution;
self
}
/// Sets a custom anchor point for the mesh
#[inline]
pub const fn anchor(mut self, anchor: ConeAnchor) -> Self {
self.anchor = anchor;
self
}
}
impl MeshBuilder for ConeMeshBuilder {
fn build(&self) -> Mesh {
let half_height = self.cone.height / 2.0;
// `resolution` vertices for the base, `resolution` vertices for the bottom of the lateral surface,
// and one vertex for the tip.
let num_vertices = self.resolution as usize * 2 + 1;
let num_indices = self.resolution as usize * 6 - 6;
let mut positions = Vec::with_capacity(num_vertices);
let mut normals = Vec::with_capacity(num_vertices);
let mut uvs = Vec::with_capacity(num_vertices);
let mut indices = Vec::with_capacity(num_indices);
// Tip
positions.push([0.0, half_height, 0.0]);
// The tip doesn't have a singular normal that works correctly.
// We use an invalid normal here so that it becomes NaN in the fragment shader
// and doesn't affect the overall shading. This might seem hacky, but it's one of
// the only ways to get perfectly smooth cones without creases or other shading artifacts.
//
// Note that this requires that normals are not normalized in the vertex shader,
// as that would make the entire triangle invalid and make the cone appear as black.
normals.push([0.0, 0.0, 0.0]);
// The UVs of the cone are in polar coordinates, so it's like projecting a circle texture from above.
// The center of the texture is at the center of the lateral surface, at the tip of the cone.
uvs.push([0.5, 0.5]);
// Now we build the lateral surface, the side of the cone.
// The vertex normals will be perpendicular to the surface.
//
// Here we get the slope of a normal and use it for computing
// the multiplicative inverse of the length of a vector in the direction
// of the normal. This allows us to normalize vertex normals efficiently.
let normal_slope = self.cone.radius / self.cone.height;
// Equivalent to Vec2::new(1.0, slope).length().recip()
let normalization_factor = (1.0 + normal_slope * normal_slope).sqrt().recip();
// How much the angle changes at each step
let step_theta = core::f32::consts::TAU / self.resolution as f32;
// Add vertices for the bottom of the lateral surface.
for segment in 0..self.resolution {
let theta = segment as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
// The vertex normal perpendicular to the side
let normal = Vec3::new(cos, normal_slope, sin) * normalization_factor;
positions.push([self.cone.radius * cos, -half_height, self.cone.radius * sin]);
normals.push(normal.to_array());
uvs.push([0.5 + cos * 0.5, 0.5 + sin * 0.5]);
}
// Add indices for the lateral surface. Each triangle is formed by the tip
// and two vertices at the base.
for j in 1..self.resolution {
indices.extend_from_slice(&[0, j + 1, j]);
}
// Close the surface with a triangle between the tip, first base vertex, and last base vertex.
indices.extend_from_slice(&[0, 1, self.resolution]);
// Now we build the actual base of the cone.
let index_offset = positions.len() as u32;
// Add base vertices.
for i in 0..self.resolution {
let theta = i as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([cos * self.cone.radius, -half_height, sin * self.cone.radius]);
normals.push([0.0, -1.0, 0.0]);
uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
}
// Add base indices.
for i in 1..(self.resolution - 1) {
indices.extend_from_slice(&[index_offset, index_offset + i, index_offset + i + 1]);
}
// Offset the vertex positions Y axis to match the anchor
match self.anchor {
ConeAnchor::Tip => positions.iter_mut().for_each(|p| p[1] -= half_height),
ConeAnchor::Base => positions.iter_mut().for_each(|p| p[1] += half_height),
ConeAnchor::MidPoint => (),
};
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl Meshable for Cone {
type Output = ConeMeshBuilder;
fn mesh(&self) -> Self::Output {
ConeMeshBuilder {
cone: *self,
..Default::default()
}
}
}
impl From<Cone> for Mesh {
fn from(cone: Cone) -> Self {
cone.mesh().build()
}
}
#[cfg(test)]
mod tests {
use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
use bevy_math::{primitives::Cone, Vec2};
/// Rounds floats to handle floating point error in tests.
fn round_floats<const N: usize>(points: &mut [[f32; N]]) {
for point in points.iter_mut() {
for coord in point.iter_mut() {
let round = (*coord * 100.0).round() / 100.0;
if (*coord - round).abs() < 0.00001 {
*coord = round;
}
}
}
}
#[test]
fn cone_mesh() {
let mut mesh = Cone {
radius: 0.5,
height: 1.0,
}
.mesh()
.resolution(4)
.build();
let Some(VertexAttributeValues::Float32x3(mut positions)) =
mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
else {
panic!("Expected positions f32x3");
};
let Some(VertexAttributeValues::Float32x3(mut normals)) =
mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
else {
panic!("Expected normals f32x3");
};
round_floats(&mut positions);
round_floats(&mut normals);
// Vertex positions
assert_eq!(
[
// Tip
[0.0, 0.5, 0.0],
// Lateral surface
[0.5, -0.5, 0.0],
[0.0, -0.5, 0.5],
[-0.5, -0.5, 0.0],
[0.0, -0.5, -0.5],
// Base
[0.5, -0.5, 0.0],
[0.0, -0.5, 0.5],
[-0.5, -0.5, 0.0],
[0.0, -0.5, -0.5],
],
&positions[..]
);
// Vertex normals
let [x, y] = Vec2::new(0.5, -1.0).perp().normalize().to_array();
assert_eq!(
&[
// Tip
[0.0, 0.0, 0.0],
// Lateral surface
[x, y, 0.0],
[0.0, y, x],
[-x, y, 0.0],
[0.0, y, -x],
// Base
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
],
&normals[..]
);
}
}

View File

@@ -0,0 +1,184 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::ConicalFrustum, Vec3};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`ConicalFrustum`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct ConicalFrustumMeshBuilder {
/// The [`ConicalFrustum`] shape.
pub frustum: ConicalFrustum,
/// The number of vertices used for the top and bottom of the conical frustum.
///
/// The default is `32`.
pub resolution: u32,
/// The number of horizontal lines subdividing the lateral surface of the conical frustum.
///
/// The default is `1`.
pub segments: u32,
}
impl Default for ConicalFrustumMeshBuilder {
fn default() -> Self {
Self {
frustum: ConicalFrustum::default(),
resolution: 32,
segments: 1,
}
}
}
impl ConicalFrustumMeshBuilder {
/// Creates a new [`ConicalFrustumMeshBuilder`] from the given top and bottom radii, a height,
/// and a resolution used for the top and bottom.
#[inline]
pub const fn new(radius_top: f32, radius_bottom: f32, height: f32, resolution: u32) -> Self {
Self {
frustum: ConicalFrustum {
radius_top,
radius_bottom,
height,
},
resolution,
segments: 1,
}
}
/// Sets the number of vertices used for the top and bottom of the conical frustum.
#[inline]
pub const fn resolution(mut self, resolution: u32) -> Self {
self.resolution = resolution;
self
}
/// Sets the number of horizontal lines subdividing the lateral surface of the conical frustum.
#[inline]
pub const fn segments(mut self, segments: u32) -> Self {
self.segments = segments;
self
}
}
impl MeshBuilder for ConicalFrustumMeshBuilder {
fn build(&self) -> Mesh {
debug_assert!(self.resolution > 2);
debug_assert!(self.segments > 0);
let ConicalFrustum {
radius_top,
radius_bottom,
height,
} = self.frustum;
let half_height = height / 2.0;
let num_rings = self.segments + 1;
let num_vertices = (self.resolution * 2 + num_rings * (self.resolution + 1)) as usize;
let num_faces = self.resolution * (num_rings - 2);
let num_indices = ((2 * num_faces + 2 * (self.resolution - 1) * 2) * 3) as usize;
let mut positions = Vec::with_capacity(num_vertices);
let mut normals = Vec::with_capacity(num_vertices);
let mut uvs = Vec::with_capacity(num_vertices);
let mut indices = Vec::with_capacity(num_indices);
let step_theta = core::f32::consts::TAU / self.resolution as f32;
let step_y = height / self.segments as f32;
let step_radius = (radius_top - radius_bottom) / self.segments as f32;
// Rings
for ring in 0..num_rings {
let y = -half_height + ring as f32 * step_y;
let radius = radius_bottom + ring as f32 * step_radius;
for segment in 0..=self.resolution {
let theta = segment as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([radius * cos, y, radius * sin]);
normals.push(
Vec3::new(cos, (radius_bottom - radius_top) / height, sin)
.normalize()
.to_array(),
);
uvs.push([
segment as f32 / self.resolution as f32,
ring as f32 / self.segments as f32,
]);
}
}
// Lateral surface
for i in 0..self.segments {
let ring = i * (self.resolution + 1);
let next_ring = (i + 1) * (self.resolution + 1);
for j in 0..self.resolution {
indices.extend_from_slice(&[
ring + j,
next_ring + j,
ring + j + 1,
next_ring + j,
next_ring + j + 1,
ring + j + 1,
]);
}
}
// Caps
let mut build_cap = |top: bool, radius: f32| {
let offset = positions.len() as u32;
let (y, normal_y, winding) = if top {
(half_height, 1.0, (1, 0))
} else {
(-half_height, -1.0, (0, 1))
};
for i in 0..self.resolution {
let theta = i as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([cos * radius, y, sin * radius]);
normals.push([0.0, normal_y, 0.0]);
uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
}
for i in 1..(self.resolution - 1) {
indices.extend_from_slice(&[
offset,
offset + i + winding.0,
offset + i + winding.1,
]);
}
};
build_cap(true, radius_top);
build_cap(false, radius_bottom);
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl Meshable for ConicalFrustum {
type Output = ConicalFrustumMeshBuilder;
fn mesh(&self) -> Self::Output {
ConicalFrustumMeshBuilder {
frustum: *self,
..Default::default()
}
}
}
impl From<ConicalFrustum> for Mesh {
fn from(frustum: ConicalFrustum) -> Self {
frustum.mesh().build()
}
}

View File

@@ -0,0 +1,99 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{primitives::Cuboid, Vec3};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`Cuboid`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct CuboidMeshBuilder {
half_size: Vec3,
}
impl Default for CuboidMeshBuilder {
/// Returns the default [`CuboidMeshBuilder`] with a width, height, and depth of `1.0`.
fn default() -> Self {
Self {
half_size: Vec3::splat(0.5),
}
}
}
impl MeshBuilder for CuboidMeshBuilder {
fn build(&self) -> Mesh {
let min = -self.half_size;
let max = self.half_size;
// Suppose Y-up right hand, and camera look from +Z to -Z
let vertices = &[
// Front
([min.x, min.y, max.z], [0.0, 0.0, 1.0], [0.0, 0.0]),
([max.x, min.y, max.z], [0.0, 0.0, 1.0], [1.0, 0.0]),
([max.x, max.y, max.z], [0.0, 0.0, 1.0], [1.0, 1.0]),
([min.x, max.y, max.z], [0.0, 0.0, 1.0], [0.0, 1.0]),
// Back
([min.x, max.y, min.z], [0.0, 0.0, -1.0], [1.0, 0.0]),
([max.x, max.y, min.z], [0.0, 0.0, -1.0], [0.0, 0.0]),
([max.x, min.y, min.z], [0.0, 0.0, -1.0], [0.0, 1.0]),
([min.x, min.y, min.z], [0.0, 0.0, -1.0], [1.0, 1.0]),
// Right
([max.x, min.y, min.z], [1.0, 0.0, 0.0], [0.0, 0.0]),
([max.x, max.y, min.z], [1.0, 0.0, 0.0], [1.0, 0.0]),
([max.x, max.y, max.z], [1.0, 0.0, 0.0], [1.0, 1.0]),
([max.x, min.y, max.z], [1.0, 0.0, 0.0], [0.0, 1.0]),
// Left
([min.x, min.y, max.z], [-1.0, 0.0, 0.0], [1.0, 0.0]),
([min.x, max.y, max.z], [-1.0, 0.0, 0.0], [0.0, 0.0]),
([min.x, max.y, min.z], [-1.0, 0.0, 0.0], [0.0, 1.0]),
([min.x, min.y, min.z], [-1.0, 0.0, 0.0], [1.0, 1.0]),
// Top
([max.x, max.y, min.z], [0.0, 1.0, 0.0], [1.0, 0.0]),
([min.x, max.y, min.z], [0.0, 1.0, 0.0], [0.0, 0.0]),
([min.x, max.y, max.z], [0.0, 1.0, 0.0], [0.0, 1.0]),
([max.x, max.y, max.z], [0.0, 1.0, 0.0], [1.0, 1.0]),
// Bottom
([max.x, min.y, max.z], [0.0, -1.0, 0.0], [0.0, 0.0]),
([min.x, min.y, max.z], [0.0, -1.0, 0.0], [1.0, 0.0]),
([min.x, min.y, min.z], [0.0, -1.0, 0.0], [1.0, 1.0]),
([max.x, min.y, min.z], [0.0, -1.0, 0.0], [0.0, 1.0]),
];
let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect();
let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect();
let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect();
let indices = Indices::U32(vec![
0, 1, 2, 2, 3, 0, // front
4, 5, 6, 6, 7, 4, // back
8, 9, 10, 10, 11, 8, // right
12, 13, 14, 14, 15, 12, // left
16, 17, 18, 18, 19, 16, // top
20, 21, 22, 22, 23, 20, // bottom
]);
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
.with_inserted_indices(indices)
}
}
impl Meshable for Cuboid {
type Output = CuboidMeshBuilder;
fn mesh(&self) -> Self::Output {
CuboidMeshBuilder {
half_size: self.half_size,
}
}
}
impl From<Cuboid> for Mesh {
fn from(cuboid: Cuboid) -> Self {
cuboid.mesh().build()
}
}

View File

@@ -0,0 +1,222 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Cylinder};
use bevy_reflect::prelude::*;
/// Anchoring options for [`CylinderMeshBuilder`]
#[derive(Debug, Copy, Clone, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub enum CylinderAnchor {
#[default]
/// Midpoint between the top and bottom caps of the cylinder
MidPoint,
/// The center of the top circle cap
Top,
/// The center of the bottom circle cap
Bottom,
}
/// A builder used for creating a [`Mesh`] with a [`Cylinder`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct CylinderMeshBuilder {
/// The [`Cylinder`] shape.
pub cylinder: Cylinder,
/// The number of vertices used for the top and bottom of the cylinder.
///
/// The default is `32`.
pub resolution: u32,
/// The number of segments along the height of the cylinder.
/// Must be greater than `0` for geometry to be generated.
///
/// The default is `1`.
pub segments: u32,
/// If set to `true`, the cylinder caps (flat circle faces) are built,
/// otherwise the mesh will be a shallow tube
pub caps: bool,
/// The anchor point for the cylinder mesh, defaults to the midpoint between
/// the top and bottom caps
pub anchor: CylinderAnchor,
}
impl Default for CylinderMeshBuilder {
fn default() -> Self {
Self {
cylinder: Cylinder::default(),
resolution: 32,
segments: 1,
caps: true,
anchor: CylinderAnchor::default(),
}
}
}
impl CylinderMeshBuilder {
/// Creates a new [`CylinderMeshBuilder`] from the given radius, a height,
/// and a resolution used for the top and bottom.
#[inline]
pub fn new(radius: f32, height: f32, resolution: u32) -> Self {
Self {
cylinder: Cylinder::new(radius, height),
resolution,
..Default::default()
}
}
/// Sets the number of vertices used for the top and bottom of the cylinder.
#[inline]
pub const fn resolution(mut self, resolution: u32) -> Self {
self.resolution = resolution;
self
}
/// Sets the number of segments along the height of the cylinder.
/// Must be greater than `0` for geometry to be generated.
#[inline]
pub const fn segments(mut self, segments: u32) -> Self {
self.segments = segments;
self
}
/// Ignore the cylinder caps, making the mesh a shallow tube instead
#[inline]
pub const fn without_caps(mut self) -> Self {
self.caps = false;
self
}
/// Sets a custom anchor point for the mesh
#[inline]
pub const fn anchor(mut self, anchor: CylinderAnchor) -> Self {
self.anchor = anchor;
self
}
}
impl MeshBuilder for CylinderMeshBuilder {
fn build(&self) -> Mesh {
let resolution = self.resolution;
let segments = self.segments;
debug_assert!(resolution > 2);
debug_assert!(segments > 0);
let num_rings = segments + 1;
let num_vertices = resolution * 2 + num_rings * (resolution + 1);
let num_faces = resolution * (num_rings - 2);
let num_indices = (2 * num_faces + 2 * (resolution - 1) * 2) * 3;
let mut positions = Vec::with_capacity(num_vertices as usize);
let mut normals = Vec::with_capacity(num_vertices as usize);
let mut uvs = Vec::with_capacity(num_vertices as usize);
let mut indices = Vec::with_capacity(num_indices as usize);
let step_theta = core::f32::consts::TAU / resolution as f32;
let step_y = 2.0 * self.cylinder.half_height / segments as f32;
// rings
for ring in 0..num_rings {
let y = -self.cylinder.half_height + ring as f32 * step_y;
for segment in 0..=resolution {
let theta = segment as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([self.cylinder.radius * cos, y, self.cylinder.radius * sin]);
normals.push([cos, 0., sin]);
uvs.push([
segment as f32 / resolution as f32,
ring as f32 / segments as f32,
]);
}
}
// barrel skin
for i in 0..segments {
let ring = i * (resolution + 1);
let next_ring = (i + 1) * (resolution + 1);
for j in 0..resolution {
indices.extend_from_slice(&[
ring + j,
next_ring + j,
ring + j + 1,
next_ring + j,
next_ring + j + 1,
ring + j + 1,
]);
}
}
// caps
if self.caps {
let mut build_cap = |top: bool| {
let offset = positions.len() as u32;
let (y, normal_y, winding) = if top {
(self.cylinder.half_height, 1., (1, 0))
} else {
(-self.cylinder.half_height, -1., (0, 1))
};
for i in 0..self.resolution {
let theta = i as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([cos * self.cylinder.radius, y, sin * self.cylinder.radius]);
normals.push([0.0, normal_y, 0.0]);
uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
}
for i in 1..(self.resolution - 1) {
indices.extend_from_slice(&[
offset,
offset + i + winding.0,
offset + i + winding.1,
]);
}
};
build_cap(true);
build_cap(false);
}
// Offset the vertex positions Y axis to match the anchor
match self.anchor {
CylinderAnchor::Top => positions
.iter_mut()
.for_each(|p| p[1] -= self.cylinder.half_height),
CylinderAnchor::Bottom => positions
.iter_mut()
.for_each(|p| p[1] += self.cylinder.half_height),
CylinderAnchor::MidPoint => (),
};
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl Meshable for Cylinder {
type Output = CylinderMeshBuilder;
fn mesh(&self) -> Self::Output {
CylinderMeshBuilder {
cylinder: *self,
..Default::default()
}
}
}
impl From<Cylinder> for Mesh {
fn from(cylinder: Cylinder) -> Self {
cylinder.mesh().build()
}
}

View File

@@ -0,0 +1,21 @@
mod capsule;
mod cone;
mod conical_frustum;
mod cuboid;
mod cylinder;
mod plane;
mod sphere;
mod tetrahedron;
mod torus;
pub(crate) mod triangle3d;
pub use capsule::*;
pub use cone::*;
pub use conical_frustum::*;
pub use cuboid::*;
pub use cylinder::*;
pub use plane::*;
pub use sphere::*;
pub use tetrahedron::*;
pub use torus::*;
pub use triangle3d::*;

View File

@@ -0,0 +1,160 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{primitives::Plane3d, Dir3, Quat, Vec2, Vec3};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`Plane3d`] shape.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct PlaneMeshBuilder {
/// The [`Plane3d`] shape.
pub plane: Plane3d,
/// The number of subdivisions in the mesh.
///
/// 0 - is the original plane geometry, the 4 points in the XZ plane.
///
/// 1 - is split by 1 line in the middle of the plane on both the X axis and the Z axis, resulting in a plane with 4 quads / 8 triangles.
///
/// 2 - is a plane split by 2 lines on both the X and Z axes, subdividing the plane into 3 equal sections along each axis, resulting in a plane with 9 quads / 18 triangles.
///
/// and so on...
pub subdivisions: u32,
}
impl PlaneMeshBuilder {
/// Creates a new [`PlaneMeshBuilder`] from a given normal and size.
#[inline]
pub fn new(normal: Dir3, size: Vec2) -> Self {
Self {
plane: Plane3d {
normal,
half_size: size / 2.0,
},
subdivisions: 0,
}
}
/// Creates a new [`PlaneMeshBuilder`] from the given size, with the normal pointing upwards.
#[inline]
pub fn from_size(size: Vec2) -> Self {
Self {
plane: Plane3d {
half_size: size / 2.0,
..Default::default()
},
subdivisions: 0,
}
}
/// Creates a new [`PlaneMeshBuilder`] from the given length, with the normal pointing upwards,
/// and the resulting [`PlaneMeshBuilder`] being a square.
#[inline]
pub fn from_length(length: f32) -> Self {
Self {
plane: Plane3d {
half_size: Vec2::splat(length) / 2.0,
..Default::default()
},
subdivisions: 0,
}
}
/// Sets the normal of the plane, aka the direction the plane is facing.
#[inline]
#[doc(alias = "facing")]
pub fn normal(mut self, normal: Dir3) -> Self {
self.plane = Plane3d {
normal,
..self.plane
};
self
}
/// Sets the size of the plane mesh.
#[inline]
pub fn size(mut self, width: f32, height: f32) -> Self {
self.plane.half_size = Vec2::new(width, height) / 2.0;
self
}
/// Sets the subdivisions of the plane mesh.
///
/// 0 - is the original plane geometry, the 4 points in the XZ plane.
///
/// 1 - is split by 1 line in the middle of the plane on both the X axis and the Z axis,
/// resulting in a plane with 4 quads / 8 triangles.
///
/// 2 - is a plane split by 2 lines on both the X and Z axes, subdividing the plane into 3
/// equal sections along each axis, resulting in a plane with 9 quads / 18 triangles.
#[inline]
pub fn subdivisions(mut self, subdivisions: u32) -> Self {
self.subdivisions = subdivisions;
self
}
}
impl MeshBuilder for PlaneMeshBuilder {
fn build(&self) -> Mesh {
let z_vertex_count = self.subdivisions + 2;
let x_vertex_count = self.subdivisions + 2;
let num_vertices = (z_vertex_count * x_vertex_count) as usize;
let num_indices = ((z_vertex_count - 1) * (x_vertex_count - 1) * 6) as usize;
let mut positions: Vec<Vec3> = Vec::with_capacity(num_vertices);
let mut normals: Vec<[f32; 3]> = Vec::with_capacity(num_vertices);
let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(num_vertices);
let mut indices: Vec<u32> = Vec::with_capacity(num_indices);
let rotation = Quat::from_rotation_arc(Vec3::Y, *self.plane.normal);
let size = self.plane.half_size * 2.0;
for z in 0..z_vertex_count {
for x in 0..x_vertex_count {
let tx = x as f32 / (x_vertex_count - 1) as f32;
let tz = z as f32 / (z_vertex_count - 1) as f32;
let pos = rotation * Vec3::new((-0.5 + tx) * size.x, 0.0, (-0.5 + tz) * size.y);
positions.push(pos);
normals.push(self.plane.normal.to_array());
uvs.push([tx, tz]);
}
}
for z in 0..z_vertex_count - 1 {
for x in 0..x_vertex_count - 1 {
let quad = z * x_vertex_count + x;
indices.push(quad + x_vertex_count + 1);
indices.push(quad + 1);
indices.push(quad + x_vertex_count);
indices.push(quad);
indices.push(quad + x_vertex_count);
indices.push(quad + 1);
}
}
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl Meshable for Plane3d {
type Output = PlaneMeshBuilder;
fn mesh(&self) -> Self::Output {
PlaneMeshBuilder {
plane: *self,
subdivisions: 0,
}
}
}
impl From<Plane3d> for Mesh {
fn from(plane: Plane3d) -> Self {
plane.mesh().build()
}
}

View File

@@ -0,0 +1,264 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Sphere};
use bevy_reflect::prelude::*;
use core::f32::consts::PI;
use hexasphere::shapes::IcoSphere;
use thiserror::Error;
/// An error when creating an icosphere [`Mesh`] from a [`SphereMeshBuilder`].
#[derive(Clone, Copy, Debug, Error)]
pub enum IcosphereError {
/// The icosphere has too many vertices.
#[error("Cannot create an icosphere of {subdivisions} subdivisions due to there being too many vertices being generated: {number_of_resulting_points}. (Limited to 65535 vertices or 79 subdivisions)")]
TooManyVertices {
/// The number of subdivisions used. 79 is the largest allowed value for a mesh to be generated.
subdivisions: u32,
/// The number of vertices generated. 65535 is the largest allowed value for a mesh to be generated.
number_of_resulting_points: u32,
},
}
/// A type of sphere mesh.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub enum SphereKind {
/// An icosphere, a spherical mesh that consists of similar sized triangles.
Ico {
/// The number of subdivisions applied.
/// The number of faces quadruples with each subdivision.
subdivisions: u32,
},
/// A UV sphere, a spherical mesh that consists of quadrilaterals
/// apart from triangles at the top and bottom.
Uv {
/// The number of longitudinal sectors, aka the horizontal resolution.
#[doc(alias = "horizontal_resolution")]
sectors: u32,
/// The number of latitudinal stacks, aka the vertical resolution.
#[doc(alias = "vertical_resolution")]
stacks: u32,
},
}
impl Default for SphereKind {
fn default() -> Self {
Self::Ico { subdivisions: 5 }
}
}
/// A builder used for creating a [`Mesh`] with an [`Sphere`] shape.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct SphereMeshBuilder {
/// The [`Sphere`] shape.
pub sphere: Sphere,
/// The type of sphere mesh that will be built.
pub kind: SphereKind,
}
impl SphereMeshBuilder {
/// Creates a new [`SphereMeshBuilder`] from a radius and [`SphereKind`].
#[inline]
pub const fn new(radius: f32, kind: SphereKind) -> Self {
Self {
sphere: Sphere { radius },
kind,
}
}
/// Sets the [`SphereKind`] that will be used for building the mesh.
#[inline]
pub const fn kind(mut self, kind: SphereKind) -> Self {
self.kind = kind;
self
}
/// Creates an icosphere mesh with the given number of subdivisions.
///
/// The number of faces quadruples with each subdivision.
/// If there are `80` or more subdivisions, the vertex count will be too large,
/// and an [`IcosphereError`] is returned.
///
/// A good default is `5` subdivisions.
pub fn ico(&self, subdivisions: u32) -> Result<Mesh, IcosphereError> {
if subdivisions >= 80 {
/*
Number of triangles:
N = 20
Number of edges:
E = 30
Number of vertices:
V = 12
Number of points within a triangle (triangular numbers):
inner(s) = (s^2 + s) / 2
Number of points on an edge:
edges(s) = s
Add up all vertices on the surface:
vertices(s) = edges(s) * E + inner(s - 1) * N + V
Expand and simplify. Notice that the triangular number formula has roots at -1, and 0, so translating it one to the right fixes it.
subdivisions(s) = 30s + 20((s^2 - 2s + 1 + s - 1) / 2) + 12
subdivisions(s) = 30s + 10s^2 - 10s + 12
subdivisions(s) = 10(s^2 + 2s) + 12
Factor an (s + 1) term to simplify in terms of calculation
subdivisions(s) = 10(s + 1)^2 + 12 - 10
resulting_vertices(s) = 10(s + 1)^2 + 2
*/
let temp = subdivisions + 1;
let number_of_resulting_points = temp * temp * 10 + 2;
return Err(IcosphereError::TooManyVertices {
subdivisions,
number_of_resulting_points,
});
}
let generated = IcoSphere::new(subdivisions as usize, |point| {
let inclination = ops::acos(point.y);
let azimuth = ops::atan2(point.z, point.x);
let norm_inclination = inclination / PI;
let norm_azimuth = 0.5 - (azimuth / core::f32::consts::TAU);
[norm_azimuth, norm_inclination]
});
let raw_points = generated.raw_points();
let points = raw_points
.iter()
.map(|&p| (p * self.sphere.radius).into())
.collect::<Vec<[f32; 3]>>();
let normals = raw_points
.iter()
.copied()
.map(Into::into)
.collect::<Vec<[f32; 3]>>();
let uvs = generated.raw_data().to_owned();
let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20);
for i in 0..20 {
generated.get_indices(i, &mut indices);
}
let indices = Indices::U32(indices);
Ok(Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(indices)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, points)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs))
}
/// Creates a UV sphere [`Mesh`] with the given number of
/// longitudinal sectors and latitudinal stacks, aka horizontal and vertical resolution.
///
/// A good default is `32` sectors and `18` stacks.
pub fn uv(&self, sectors: u32, stacks: u32) -> Mesh {
// Largely inspired from http://www.songho.ca/opengl/gl_sphere.html
let sectors_f32 = sectors as f32;
let stacks_f32 = stacks as f32;
let length_inv = 1. / self.sphere.radius;
let sector_step = 2. * PI / sectors_f32;
let stack_step = PI / stacks_f32;
let n_vertices = (stacks * sectors) as usize;
let mut vertices: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
let mut normals: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(n_vertices);
let mut indices: Vec<u32> = Vec::with_capacity(n_vertices * 2 * 3);
for i in 0..stacks + 1 {
let stack_angle = PI / 2. - (i as f32) * stack_step;
let xy = self.sphere.radius * ops::cos(stack_angle);
let z = self.sphere.radius * ops::sin(stack_angle);
for j in 0..sectors + 1 {
let sector_angle = (j as f32) * sector_step;
let x = xy * ops::cos(sector_angle);
let y = xy * ops::sin(sector_angle);
vertices.push([x, y, z]);
normals.push([x * length_inv, y * length_inv, z * length_inv]);
uvs.push([(j as f32) / sectors_f32, (i as f32) / stacks_f32]);
}
}
// indices
// k1--k1+1
// | / |
// | / |
// k2--k2+1
for i in 0..stacks {
let mut k1 = i * (sectors + 1);
let mut k2 = k1 + sectors + 1;
for _j in 0..sectors {
if i != 0 {
indices.push(k1);
indices.push(k2);
indices.push(k1 + 1);
}
if i != stacks - 1 {
indices.push(k1 + 1);
indices.push(k2);
indices.push(k2 + 1);
}
k1 += 1;
k2 += 1;
}
}
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl MeshBuilder for SphereMeshBuilder {
/// Builds a [`Mesh`] according to the configuration in `self`.
///
/// # Panics
///
/// Panics if the sphere is a [`SphereKind::Ico`] with a subdivision count
/// that is greater than or equal to `80` because there will be too many vertices.
fn build(&self) -> Mesh {
match self.kind {
SphereKind::Ico { subdivisions } => self.ico(subdivisions).unwrap(),
SphereKind::Uv { sectors, stacks } => self.uv(sectors, stacks),
}
}
}
impl Meshable for Sphere {
type Output = SphereMeshBuilder;
fn mesh(&self) -> Self::Output {
SphereMeshBuilder {
sphere: *self,
..Default::default()
}
}
}
impl From<Sphere> for Mesh {
fn from(sphere: Sphere) -> Self {
sphere.mesh().build()
}
}

View File

@@ -0,0 +1,66 @@
use super::triangle3d;
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::primitives::{Tetrahedron, Triangle3d};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`Tetrahedron`] shape.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct TetrahedronMeshBuilder {
tetrahedron: Tetrahedron,
}
impl MeshBuilder for TetrahedronMeshBuilder {
fn build(&self) -> Mesh {
let mut faces: Vec<_> = self.tetrahedron.faces().into();
// If the tetrahedron has negative orientation, reverse all the triangles so that
// they still face outward.
if self.tetrahedron.signed_volume().is_sign_negative() {
faces.iter_mut().for_each(Triangle3d::reverse);
}
let mut positions = vec![];
let mut normals = vec![];
let mut uvs = vec![];
// Each face is meshed as a `Triangle3d`, and we just shove the data into the
// vertex attributes sequentially.
for face in faces {
positions.extend(face.vertices);
let face_normal = triangle3d::normal_vec(&face);
normals.extend(vec![face_normal; 3]);
let face_uvs = triangle3d::uv_coords(&face);
uvs.extend(face_uvs);
}
// There are four faces and none of them share vertices.
let indices = Indices::U32(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
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)
}
}
impl Meshable for Tetrahedron {
type Output = TetrahedronMeshBuilder;
fn mesh(&self) -> Self::Output {
TetrahedronMeshBuilder { tetrahedron: *self }
}
}
impl From<Tetrahedron> for Mesh {
fn from(tetrahedron: Tetrahedron) -> Self {
tetrahedron.mesh().build()
}
}

View File

@@ -0,0 +1,176 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Torus, Vec3};
use bevy_reflect::prelude::*;
use core::ops::RangeInclusive;
/// A builder used for creating a [`Mesh`] with a [`Torus`] shape.
#[derive(Clone, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct TorusMeshBuilder {
/// The [`Torus`] shape.
pub torus: Torus,
/// The number of vertices used for each circular segment
/// in the ring or tube of the torus.
///
/// The default is `24`.
pub minor_resolution: usize,
/// The number of segments used for the main ring of the torus.
///
/// A resolution of `4` would make the torus appear rectangular,
/// while a resolution of `32` resembles a circular ring.
///
/// The default is `32`.
pub major_resolution: usize,
/// Optional angle range in radians, defaults to a full circle (0.0..=2 * PI)
pub angle_range: RangeInclusive<f32>,
}
impl Default for TorusMeshBuilder {
fn default() -> Self {
Self {
torus: Torus::default(),
minor_resolution: 24,
major_resolution: 32,
angle_range: (0.0..=2.0 * core::f32::consts::PI),
}
}
}
impl TorusMeshBuilder {
/// Creates a new [`TorusMeshBuilder`] from an inner and outer radius.
///
/// The inner radius is the radius of the hole, and the outer radius
/// is the radius of the entire object.
#[inline]
pub fn new(inner_radius: f32, outer_radius: f32) -> Self {
Self {
torus: Torus::new(inner_radius, outer_radius),
..Default::default()
}
}
/// Sets the number of vertices used for each circular segment
/// in the ring or tube of the torus.
#[inline]
pub const fn minor_resolution(mut self, resolution: usize) -> Self {
self.minor_resolution = resolution;
self
}
/// Sets the number of segments used for the main ring of the torus.
///
/// A resolution of `4` would make the torus appear rectangular,
/// while a resolution of `32` resembles a circular ring.
#[inline]
pub const fn major_resolution(mut self, resolution: usize) -> Self {
self.major_resolution = resolution;
self
}
/// Sets a custom angle range in radians instead of a full circle
#[inline]
pub const fn angle_range(mut self, range: RangeInclusive<f32>) -> Self {
self.angle_range = range;
self
}
}
impl MeshBuilder for TorusMeshBuilder {
fn build(&self) -> Mesh {
// code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html
let n_vertices = (self.major_resolution + 1) * (self.minor_resolution + 1);
let mut positions: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
let mut normals: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(n_vertices);
let start_angle = self.angle_range.start();
let end_angle = self.angle_range.end();
let segment_stride = (end_angle - start_angle) / self.major_resolution as f32;
let side_stride = 2.0 * core::f32::consts::PI / self.minor_resolution as f32;
for segment in 0..=self.major_resolution {
let theta = start_angle + segment_stride * segment as f32;
for side in 0..=self.minor_resolution {
let phi = side_stride * side as f32;
let (sin_theta, cos_theta) = ops::sin_cos(theta);
let (sin_phi, cos_phi) = ops::sin_cos(phi);
let radius = self.torus.major_radius + self.torus.minor_radius * cos_phi;
let position = Vec3::new(
cos_theta * radius,
self.torus.minor_radius * sin_phi,
sin_theta * radius,
);
let center = Vec3::new(
self.torus.major_radius * cos_theta,
0.,
self.torus.major_radius * sin_theta,
);
let normal = (position - center).normalize();
positions.push(position.into());
normals.push(normal.into());
uvs.push([
segment as f32 / self.major_resolution as f32,
side as f32 / self.minor_resolution as f32,
]);
}
}
let n_faces = (self.major_resolution) * (self.minor_resolution);
let n_triangles = n_faces * 2;
let n_indices = n_triangles * 3;
let mut indices: Vec<u32> = Vec::with_capacity(n_indices);
let n_vertices_per_row = self.minor_resolution + 1;
for segment in 0..self.major_resolution {
for side in 0..self.minor_resolution {
let lt = side + segment * n_vertices_per_row;
let rt = (side + 1) + segment * n_vertices_per_row;
let lb = side + (segment + 1) * n_vertices_per_row;
let rb = (side + 1) + (segment + 1) * n_vertices_per_row;
indices.push(lt as u32);
indices.push(rt as u32);
indices.push(lb as u32);
indices.push(rt as u32);
indices.push(rb as u32);
indices.push(lb as u32);
}
}
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl Meshable for Torus {
type Output = TorusMeshBuilder;
fn mesh(&self) -> Self::Output {
TorusMeshBuilder {
torus: *self,
..Default::default()
}
}
}
impl From<Torus> for Mesh {
fn from(torus: Torus) -> Self {
torus.mesh().build()
}
}

View File

@@ -0,0 +1,130 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{primitives::Triangle3d, Vec3};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`Triangle3d`] shape.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct Triangle3dMeshBuilder {
triangle: Triangle3d,
}
impl MeshBuilder for Triangle3dMeshBuilder {
fn build(&self) -> Mesh {
let positions: Vec<_> = self.triangle.vertices.into();
let uvs: Vec<_> = uv_coords(&self.triangle).into();
// Every vertex has the normal of the face of the triangle (or zero if the triangle is degenerate).
let normal: Vec3 = normal_vec(&self.triangle);
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)
}
}
impl Meshable for Triangle3d {
type Output = Triangle3dMeshBuilder;
fn mesh(&self) -> Self::Output {
Triangle3dMeshBuilder { triangle: *self }
}
}
/// The normal of a [`Triangle3d`] with zeroing so that a [`Vec3`] is always obtained for meshing.
#[inline]
pub(crate) fn normal_vec(triangle: &Triangle3d) -> Vec3 {
triangle.normal().map_or(Vec3::ZERO, Into::into)
}
/// 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().build()
}
}
#[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.]);
}
}

View File

@@ -0,0 +1,425 @@
use bevy_math::{
primitives::{Annulus, Capsule2d, Circle, Ellipse, Extrusion, Primitive2d},
Vec2, Vec3,
};
use super::{MeshBuilder, Meshable};
use crate::{Indices, Mesh, PrimitiveTopology, VertexAttributeValues};
/// A type representing a segment of the perimeter of an extrudable mesh.
pub enum PerimeterSegment {
/// This segment of the perimeter will be shaded smooth.
///
/// This has the effect of rendering the segment's faces with softened edges, so it is appropriate for curved shapes.
///
/// The normals for the vertices that are part of this segment will be calculated based on the positions of their neighbors.
/// Each normal is interpolated between the normals of the two line segments connecting it with its neighbors.
/// Closer vertices have a stronger effect on the normal than more distant ones.
///
/// Since the vertices corresponding to the first and last indices do not have two neighboring vertices, their normals must be provided manually.
Smooth {
/// The normal of the first vertex.
first_normal: Vec2,
/// The normal of the last vertex.
last_normal: Vec2,
/// A list of indices representing this segment of the perimeter of the mesh.
///
/// The indices must be ordered such that the *outside* of the mesh is to the right
/// when walking along the vertices of the mesh in the order provided by the indices.
///
/// For geometry to be rendered, you must provide at least two indices.
indices: Vec<u32>,
},
/// This segment of the perimeter will be shaded flat.
///
/// This has the effect of rendering the segment's faces with hard edges.
Flat {
/// A list of indices representing this segment of the perimeter of the mesh.
///
/// The indices must be ordered such that the *outside* of the mesh is to the right
/// when walking along the vertices of the mesh in the order provided by indices.
///
/// For geometry to be rendered, you must provide at least two indices.
indices: Vec<u32>,
},
}
impl PerimeterSegment {
/// Returns the amount of vertices each 'layer' of the extrusion should include for this perimeter segment.
///
/// A layer is the set of vertices sharing a common Z value or depth.
fn vertices_per_layer(&self) -> u32 {
match self {
PerimeterSegment::Smooth { indices, .. } => indices.len() as u32,
PerimeterSegment::Flat { indices } => 2 * (indices.len() as u32 - 1),
}
}
/// Returns the amount of indices each 'segment' of the extrusion should include for this perimeter segment.
///
/// A segment is the set of faces on the mantel of the extrusion between two layers of vertices.
fn indices_per_segment(&self) -> usize {
match self {
PerimeterSegment::Smooth { indices, .. } | PerimeterSegment::Flat { indices } => {
6 * (indices.len() - 1)
}
}
}
}
/// A trait required for implementing `Meshable` for `Extrusion<T>`.
///
/// ## Warning
///
/// By implementing this trait you guarantee that the `primitive_topology` of the mesh returned by
/// this builder is [`PrimitiveTopology::TriangleList`]
/// and that your mesh has a [`Mesh::ATTRIBUTE_POSITION`] attribute.
pub trait Extrudable: MeshBuilder {
/// A list of the indices each representing a part of the perimeter of the mesh.
fn perimeter(&self) -> Vec<PerimeterSegment>;
}
impl<P> Meshable for Extrusion<P>
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
type Output = ExtrusionBuilder<P>;
fn mesh(&self) -> Self::Output {
ExtrusionBuilder {
base_builder: self.base_shape.mesh(),
half_depth: self.half_depth,
segments: 1,
}
}
}
/// A builder used for creating a [`Mesh`] with an [`Extrusion`] shape.
pub struct ExtrusionBuilder<P>
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
pub base_builder: P::Output,
pub half_depth: f32,
pub segments: usize,
}
impl<P> ExtrusionBuilder<P>
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
/// Create a new `ExtrusionBuilder<P>` from a given `base_shape` and the full `depth` of the extrusion.
pub fn new(base_shape: &P, depth: f32) -> Self {
Self {
base_builder: base_shape.mesh(),
half_depth: depth / 2.,
segments: 1,
}
}
/// Sets the number of segments along the depth of the extrusion.
/// Must be greater than `0` for the geometry of the mantel to be generated.
pub fn segments(mut self, segments: usize) -> Self {
self.segments = segments;
self
}
}
impl ExtrusionBuilder<Circle> {
/// Sets the number of vertices used for the circle mesh at each end of the extrusion.
pub fn resolution(mut self, resolution: u32) -> Self {
self.base_builder.resolution = resolution;
self
}
}
impl ExtrusionBuilder<Ellipse> {
/// Sets the number of vertices used for the ellipse mesh at each end of the extrusion.
pub fn resolution(mut self, resolution: u32) -> Self {
self.base_builder.resolution = resolution;
self
}
}
impl ExtrusionBuilder<Annulus> {
/// Sets the number of vertices used in constructing the concentric circles of the annulus mesh at each end of the extrusion.
pub fn resolution(mut self, resolution: u32) -> Self {
self.base_builder.resolution = resolution;
self
}
}
impl ExtrusionBuilder<Capsule2d> {
/// Sets the number of vertices used for each hemicircle at the ends of the extrusion.
pub fn resolution(mut self, resolution: u32) -> Self {
self.base_builder.resolution = resolution;
self
}
}
impl<P> MeshBuilder for ExtrusionBuilder<P>
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
fn build(&self) -> Mesh {
// Create and move the base mesh to the front
let mut front_face =
self.base_builder
.build()
.translated_by(Vec3::new(0., 0., self.half_depth));
// Move the uvs of the front face to be between (0., 0.) and (0.5, 0.5)
if let Some(VertexAttributeValues::Float32x2(uvs)) =
front_face.attribute_mut(Mesh::ATTRIBUTE_UV_0)
{
for uv in uvs {
*uv = uv.map(|coord| coord * 0.5);
}
}
let back_face = {
let topology = front_face.primitive_topology();
// Flip the normals, etc. and move mesh to the back
let mut back_face = front_face.clone().scaled_by(Vec3::new(1., 1., -1.));
// Move the uvs of the back face to be between (0.5, 0.) and (1., 0.5)
if let Some(VertexAttributeValues::Float32x2(uvs)) =
back_face.attribute_mut(Mesh::ATTRIBUTE_UV_0)
{
for uv in uvs {
*uv = [uv[0] + 0.5, uv[1]];
}
}
// By swapping the first and second indices of each triangle we invert the winding order thus making the mesh visible from the other side
if let Some(indices) = back_face.indices_mut() {
match topology {
PrimitiveTopology::TriangleList => match indices {
Indices::U16(indices) => {
indices.chunks_exact_mut(3).for_each(|arr| arr.swap(1, 0));
}
Indices::U32(indices) => {
indices.chunks_exact_mut(3).for_each(|arr| arr.swap(1, 0));
}
},
_ => {
panic!("Meshes used with Extrusions must have a primitive topology of `PrimitiveTopology::TriangleList`");
}
};
}
back_face
};
// An extrusion of depth 0 does not need a mantel
if self.half_depth == 0. {
front_face.merge(&back_face).unwrap();
return front_face;
}
let mantel = {
let Some(VertexAttributeValues::Float32x3(cap_verts)) =
front_face.attribute(Mesh::ATTRIBUTE_POSITION)
else {
panic!("The base mesh did not have vertex positions");
};
debug_assert!(self.segments > 0);
let layers = self.segments + 1;
let layer_depth_delta = self.half_depth * 2.0 / self.segments as f32;
let perimeter = self.base_builder.perimeter();
let (vert_count, index_count) =
perimeter
.iter()
.fold((0, 0), |(verts, indices), perimeter| {
(
verts + layers * perimeter.vertices_per_layer() as usize,
indices + self.segments * perimeter.indices_per_segment(),
)
});
let mut positions = Vec::with_capacity(vert_count);
let mut normals = Vec::with_capacity(vert_count);
let mut indices = Vec::with_capacity(index_count);
let mut uvs = Vec::with_capacity(vert_count);
// Compute the amount of horizontal space allocated to each segment of the perimeter.
let uv_segment_delta = 1. / perimeter.len() as f32;
for (i, segment) in perimeter.into_iter().enumerate() {
// The start of the x range of the area of the current perimeter-segment.
let uv_start = i as f32 * uv_segment_delta;
match segment {
PerimeterSegment::Flat {
indices: segment_indices,
} => {
let uv_delta = uv_segment_delta / (segment_indices.len() - 1) as f32;
for i in 0..(segment_indices.len() - 1) {
let uv_x = uv_start + uv_delta * i as f32;
// Get the positions for the current and the next index.
let a = cap_verts[segment_indices[i] as usize];
let b = cap_verts[segment_indices[i + 1] as usize];
// Get the index of the next vertex added to the mantel.
let index = positions.len() as u32;
// Push the positions of the two indices and their equivalent points on each layer.
for i in 0..layers {
let i = i as f32;
let z = a[2] - layer_depth_delta * i;
positions.push([a[0], a[1], z]);
positions.push([b[0], b[1], z]);
// UVs for the mantel are between (0, 0.5) and (1, 1).
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_x, uv_y]);
uvs.push([uv_x + uv_delta, uv_y]);
}
// The normal is calculated to be the normal of the line segment connecting a and b.
let n = Vec3::from_array([b[1] - a[1], a[0] - b[0], 0.])
.normalize_or_zero()
.to_array();
normals.extend_from_slice(&vec![n; 2 * layers]);
// Add the indices for the vertices created above to the mesh.
for i in 0..self.segments as u32 {
let base_index = index + 2 * i;
indices.extend_from_slice(&[
base_index,
base_index + 2,
base_index + 1,
base_index + 1,
base_index + 2,
base_index + 3,
]);
}
}
}
PerimeterSegment::Smooth {
first_normal,
last_normal,
indices: segment_indices,
} => {
let uv_delta = uv_segment_delta / (segment_indices.len() - 1) as f32;
// Since the indices for this segment will be added after its vertices have been added,
// we need to store the index of the first vertex that is part of this segment.
let base_index = positions.len() as u32;
// If there is a first vertex, we need to add it and its counterparts on each layer.
// The normal is provided by `segment.first_normal`.
if let Some(i) = segment_indices.first() {
let p = cap_verts[*i as usize];
for i in 0..layers {
let i = i as f32;
let z = p[2] - layer_depth_delta * i;
positions.push([p[0], p[1], z]);
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_start, uv_y]);
}
normals.extend_from_slice(&vec![
first_normal.extend(0.).to_array();
layers
]);
}
// For all points inbetween the first and last vertices, we can automatically compute the normals.
for i in 1..(segment_indices.len() - 1) {
let uv_x = uv_start + uv_delta * i as f32;
// Get the positions for the last, current and the next index.
let a = cap_verts[segment_indices[i - 1] as usize];
let b = cap_verts[segment_indices[i] as usize];
let c = cap_verts[segment_indices[i + 1] as usize];
// Add the current vertex and its counterparts on each layer.
for i in 0..layers {
let i = i as f32;
let z = b[2] - layer_depth_delta * i;
positions.push([b[0], b[1], z]);
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_x, uv_y]);
}
// The normal for the current vertices can be calculated based on the two neighboring vertices.
// The normal is interpolated between the normals of the two line segments connecting the current vertex with its neighbors.
// Closer vertices have a stronger effect on the normal than more distant ones.
let n = {
let ab = Vec2::from_slice(&b) - Vec2::from_slice(&a);
let bc = Vec2::from_slice(&c) - Vec2::from_slice(&b);
let n = ab.normalize_or_zero() + bc.normalize_or_zero();
Vec2::new(n.y, -n.x)
.normalize_or_zero()
.extend(0.)
.to_array()
};
normals.extend_from_slice(&vec![n; layers]);
}
// If there is a last vertex, we need to add it and its counterparts on each layer.
// The normal is provided by `segment.last_normal`.
if let Some(i) = segment_indices.last() {
let p = cap_verts[*i as usize];
for i in 0..layers {
let i = i as f32;
let z = p[2] - layer_depth_delta * i;
positions.push([p[0], p[1], z]);
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_start + uv_segment_delta, uv_y]);
}
normals.extend_from_slice(&vec![
last_normal.extend(0.).to_array();
layers
]);
}
let columns = segment_indices.len() as u32;
let segments = self.segments as u32;
let layers = segments + 1;
for s in 0..segments {
for column in 0..(columns - 1) {
let index = base_index + s + column * layers;
indices.extend_from_slice(&[
index,
index + 1,
index + layers,
index + layers,
index + 1,
index + layers + 1,
]);
}
}
}
}
}
Mesh::new(PrimitiveTopology::TriangleList, front_face.asset_usage)
.with_inserted_indices(Indices::U32(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
};
front_face.merge(&back_face).unwrap();
front_face.merge(&mantel).unwrap();
front_face
}
}
impl<P> From<Extrusion<P>> for Mesh
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
fn from(value: Extrusion<P>) -> Self {
value.mesh().build()
}
}

52
vendor/bevy_mesh/src/primitives/mod.rs vendored Normal file
View File

@@ -0,0 +1,52 @@
//! 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`] or a builder
//! that can be used to specify shape-specific configuration for creating the [`Mesh`].
//!
//! ```
//! # use bevy_asset::Assets;
//! # use bevy_ecs::prelude::ResMut;
//! # use bevy_math::prelude::Circle;
//! # use bevy_mesh::*;
//! #
//! # 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));
//! # }
//! ```
mod dim2;
pub use dim2::*;
mod dim3;
pub use dim3::*;
mod extrusion;
pub use extrusion::*;
use super::Mesh;
/// A trait for shapes that can be turned into a [`Mesh`].
pub trait Meshable {
/// The output of [`Self::mesh`]. This will be a [`MeshBuilder`] used for creating a [`Mesh`].
type Output: MeshBuilder;
/// Creates a [`Mesh`] for a shape.
fn mesh(&self) -> Self::Output;
}
/// A trait used to build [`Mesh`]es from a configuration
pub trait MeshBuilder {
/// Builds a [`Mesh`] based on the configuration in `self`.
fn build(&self) -> Mesh;
}
impl<T: MeshBuilder> From<T> for Mesh {
fn from(builder: T) -> Self {
builder.build()
}
}

39
vendor/bevy_mesh/src/skinning.rs vendored Normal file
View File

@@ -0,0 +1,39 @@
use bevy_asset::{AsAssetId, Asset, AssetId, Handle};
use bevy_ecs::{component::Component, entity::Entity, prelude::ReflectComponent};
use bevy_math::Mat4;
use bevy_reflect::prelude::*;
use core::ops::Deref;
#[derive(Component, Debug, Default, Clone, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct SkinnedMesh {
pub inverse_bindposes: Handle<SkinnedMeshInverseBindposes>,
#[entities]
pub joints: Vec<Entity>,
}
impl AsAssetId for SkinnedMesh {
type Asset = SkinnedMeshInverseBindposes;
// We implement this so that `AssetChanged` will work to pick up any changes
// to `SkinnedMeshInverseBindposes`.
fn as_asset_id(&self) -> AssetId<Self::Asset> {
self.inverse_bindposes.id()
}
}
#[derive(Asset, TypePath, Debug)]
pub struct SkinnedMeshInverseBindposes(Box<[Mat4]>);
impl From<Vec<Mat4>> for SkinnedMeshInverseBindposes {
fn from(value: Vec<Mat4>) -> Self {
Self(value.into_boxed_slice())
}
}
impl Deref for SkinnedMeshInverseBindposes {
type Target = [Mat4];
fn deref(&self) -> &Self::Target {
&self.0
}
}

426
vendor/bevy_mesh/src/vertex.rs vendored Normal file
View File

@@ -0,0 +1,426 @@
use alloc::sync::Arc;
use bevy_derive::EnumVariantMeta;
use bevy_ecs::resource::Resource;
use bevy_math::Vec3;
use bevy_platform::collections::HashSet;
use bytemuck::cast_slice;
use core::hash::{Hash, Hasher};
use thiserror::Error;
use wgpu_types::{BufferAddress, VertexAttribute, VertexFormat, VertexStepMode};
#[derive(Debug, Clone, Copy)]
pub struct MeshVertexAttribute {
/// The friendly name of the vertex attribute
pub name: &'static str,
/// The _unique_ id of the vertex attribute. This will also determine sort ordering
/// when generating vertex buffers. Built-in / standard attributes will use "close to zero"
/// indices. When in doubt, use a random / very large u64 to avoid conflicts.
pub id: MeshVertexAttributeId,
/// The format of the vertex attribute.
pub format: VertexFormat,
}
impl MeshVertexAttribute {
pub const fn new(name: &'static str, id: u64, format: VertexFormat) -> Self {
Self {
name,
id: MeshVertexAttributeId(id),
format,
}
}
pub const fn at_shader_location(&self, shader_location: u32) -> VertexAttributeDescriptor {
VertexAttributeDescriptor::new(shader_location, self.id, self.name)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct MeshVertexAttributeId(u64);
impl From<MeshVertexAttribute> for MeshVertexAttributeId {
fn from(attribute: MeshVertexAttribute) -> Self {
attribute.id
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MeshVertexBufferLayout {
pub(crate) attribute_ids: Vec<MeshVertexAttributeId>,
pub(crate) layout: VertexBufferLayout,
}
impl MeshVertexBufferLayout {
pub fn new(attribute_ids: Vec<MeshVertexAttributeId>, layout: VertexBufferLayout) -> Self {
Self {
attribute_ids,
layout,
}
}
#[inline]
pub fn contains(&self, attribute_id: impl Into<MeshVertexAttributeId>) -> bool {
self.attribute_ids.contains(&attribute_id.into())
}
#[inline]
pub fn attribute_ids(&self) -> &[MeshVertexAttributeId] {
&self.attribute_ids
}
#[inline]
pub fn layout(&self) -> &VertexBufferLayout {
&self.layout
}
pub fn get_layout(
&self,
attribute_descriptors: &[VertexAttributeDescriptor],
) -> Result<VertexBufferLayout, MissingVertexAttributeError> {
let mut attributes = Vec::with_capacity(attribute_descriptors.len());
for attribute_descriptor in attribute_descriptors {
if let Some(index) = self
.attribute_ids
.iter()
.position(|id| *id == attribute_descriptor.id)
{
let layout_attribute = &self.layout.attributes[index];
attributes.push(VertexAttribute {
format: layout_attribute.format,
offset: layout_attribute.offset,
shader_location: attribute_descriptor.shader_location,
});
} else {
return Err(MissingVertexAttributeError {
id: attribute_descriptor.id,
name: attribute_descriptor.name,
pipeline_type: None,
});
}
}
Ok(VertexBufferLayout {
array_stride: self.layout.array_stride,
step_mode: self.layout.step_mode,
attributes,
})
}
}
#[derive(Error, Debug)]
#[error("Mesh is missing requested attribute: {name} ({id:?}, pipeline type: {pipeline_type:?})")]
pub struct MissingVertexAttributeError {
pub pipeline_type: Option<&'static str>,
id: MeshVertexAttributeId,
name: &'static str,
}
pub struct VertexAttributeDescriptor {
pub shader_location: u32,
pub id: MeshVertexAttributeId,
name: &'static str,
}
impl VertexAttributeDescriptor {
pub const fn new(shader_location: u32, id: MeshVertexAttributeId, name: &'static str) -> Self {
Self {
shader_location,
id,
name,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct MeshAttributeData {
pub(crate) attribute: MeshVertexAttribute,
pub(crate) values: VertexAttributeValues,
}
/// Compute a vector whose direction is the normal of the triangle formed by
/// points a, b, c, and whose magnitude is double the area of the triangle. This
/// is useful for computing smooth normals where the contributing normals are
/// proportionate to the areas of the triangles as [discussed
/// here](https://iquilezles.org/articles/normals/).
///
/// Question: Why double the area? Because the area of a triangle _A_ is
/// determined by this equation:
///
/// _A = |(b - a) x (c - a)| / 2_
///
/// By computing _2 A_ we avoid a division operation, and when calculating the
/// the sum of these vectors which are then normalized, a constant multiple has
/// no effect.
#[inline]
pub fn face_area_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] {
let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c));
(b - a).cross(c - a).into()
}
/// Compute the normal of a face made of three points: a, b, and c.
#[inline]
pub fn face_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] {
let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c));
(b - a).cross(c - a).normalize().into()
}
/// Contains an array where each entry describes a property of a single vertex.
/// Matches the [`VertexFormats`](VertexFormat).
#[derive(Clone, Debug, EnumVariantMeta)]
pub enum VertexAttributeValues {
Float32(Vec<f32>),
Sint32(Vec<i32>),
Uint32(Vec<u32>),
Float32x2(Vec<[f32; 2]>),
Sint32x2(Vec<[i32; 2]>),
Uint32x2(Vec<[u32; 2]>),
Float32x3(Vec<[f32; 3]>),
Sint32x3(Vec<[i32; 3]>),
Uint32x3(Vec<[u32; 3]>),
Float32x4(Vec<[f32; 4]>),
Sint32x4(Vec<[i32; 4]>),
Uint32x4(Vec<[u32; 4]>),
Sint16x2(Vec<[i16; 2]>),
Snorm16x2(Vec<[i16; 2]>),
Uint16x2(Vec<[u16; 2]>),
Unorm16x2(Vec<[u16; 2]>),
Sint16x4(Vec<[i16; 4]>),
Snorm16x4(Vec<[i16; 4]>),
Uint16x4(Vec<[u16; 4]>),
Unorm16x4(Vec<[u16; 4]>),
Sint8x2(Vec<[i8; 2]>),
Snorm8x2(Vec<[i8; 2]>),
Uint8x2(Vec<[u8; 2]>),
Unorm8x2(Vec<[u8; 2]>),
Sint8x4(Vec<[i8; 4]>),
Snorm8x4(Vec<[i8; 4]>),
Uint8x4(Vec<[u8; 4]>),
Unorm8x4(Vec<[u8; 4]>),
}
impl VertexAttributeValues {
/// Returns the number of vertices in this [`VertexAttributeValues`]. For a single
/// mesh, all of the [`VertexAttributeValues`] must have the same length.
#[expect(
clippy::match_same_arms,
reason = "Although the `values` binding on some match arms may have matching types, each variant has different semantics; thus it's not guaranteed that they will use the same type forever."
)]
pub fn len(&self) -> usize {
match self {
VertexAttributeValues::Float32(values) => values.len(),
VertexAttributeValues::Sint32(values) => values.len(),
VertexAttributeValues::Uint32(values) => values.len(),
VertexAttributeValues::Float32x2(values) => values.len(),
VertexAttributeValues::Sint32x2(values) => values.len(),
VertexAttributeValues::Uint32x2(values) => values.len(),
VertexAttributeValues::Float32x3(values) => values.len(),
VertexAttributeValues::Sint32x3(values) => values.len(),
VertexAttributeValues::Uint32x3(values) => values.len(),
VertexAttributeValues::Float32x4(values) => values.len(),
VertexAttributeValues::Sint32x4(values) => values.len(),
VertexAttributeValues::Uint32x4(values) => values.len(),
VertexAttributeValues::Sint16x2(values) => values.len(),
VertexAttributeValues::Snorm16x2(values) => values.len(),
VertexAttributeValues::Uint16x2(values) => values.len(),
VertexAttributeValues::Unorm16x2(values) => values.len(),
VertexAttributeValues::Sint16x4(values) => values.len(),
VertexAttributeValues::Snorm16x4(values) => values.len(),
VertexAttributeValues::Uint16x4(values) => values.len(),
VertexAttributeValues::Unorm16x4(values) => values.len(),
VertexAttributeValues::Sint8x2(values) => values.len(),
VertexAttributeValues::Snorm8x2(values) => values.len(),
VertexAttributeValues::Uint8x2(values) => values.len(),
VertexAttributeValues::Unorm8x2(values) => values.len(),
VertexAttributeValues::Sint8x4(values) => values.len(),
VertexAttributeValues::Snorm8x4(values) => values.len(),
VertexAttributeValues::Uint8x4(values) => values.len(),
VertexAttributeValues::Unorm8x4(values) => values.len(),
}
}
/// Returns `true` if there are no vertices in this [`VertexAttributeValues`].
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the values as float triples if possible.
pub fn as_float3(&self) -> Option<&[[f32; 3]]> {
match self {
VertexAttributeValues::Float32x3(values) => Some(values),
_ => None,
}
}
// TODO: add vertex format as parameter here and perform type conversions
/// Flattens the [`VertexAttributeValues`] into a sequence of bytes. This is
/// useful for serialization and sending to the GPU.
#[expect(
clippy::match_same_arms,
reason = "Although the `values` binding on some match arms may have matching types, each variant has different semantics; thus it's not guaranteed that they will use the same type forever."
)]
pub fn get_bytes(&self) -> &[u8] {
match self {
VertexAttributeValues::Float32(values) => cast_slice(values),
VertexAttributeValues::Sint32(values) => cast_slice(values),
VertexAttributeValues::Uint32(values) => cast_slice(values),
VertexAttributeValues::Float32x2(values) => cast_slice(values),
VertexAttributeValues::Sint32x2(values) => cast_slice(values),
VertexAttributeValues::Uint32x2(values) => cast_slice(values),
VertexAttributeValues::Float32x3(values) => cast_slice(values),
VertexAttributeValues::Sint32x3(values) => cast_slice(values),
VertexAttributeValues::Uint32x3(values) => cast_slice(values),
VertexAttributeValues::Float32x4(values) => cast_slice(values),
VertexAttributeValues::Sint32x4(values) => cast_slice(values),
VertexAttributeValues::Uint32x4(values) => cast_slice(values),
VertexAttributeValues::Sint16x2(values) => cast_slice(values),
VertexAttributeValues::Snorm16x2(values) => cast_slice(values),
VertexAttributeValues::Uint16x2(values) => cast_slice(values),
VertexAttributeValues::Unorm16x2(values) => cast_slice(values),
VertexAttributeValues::Sint16x4(values) => cast_slice(values),
VertexAttributeValues::Snorm16x4(values) => cast_slice(values),
VertexAttributeValues::Uint16x4(values) => cast_slice(values),
VertexAttributeValues::Unorm16x4(values) => cast_slice(values),
VertexAttributeValues::Sint8x2(values) => cast_slice(values),
VertexAttributeValues::Snorm8x2(values) => cast_slice(values),
VertexAttributeValues::Uint8x2(values) => cast_slice(values),
VertexAttributeValues::Unorm8x2(values) => cast_slice(values),
VertexAttributeValues::Sint8x4(values) => cast_slice(values),
VertexAttributeValues::Snorm8x4(values) => cast_slice(values),
VertexAttributeValues::Uint8x4(values) => cast_slice(values),
VertexAttributeValues::Unorm8x4(values) => cast_slice(values),
}
}
}
impl From<&VertexAttributeValues> for VertexFormat {
fn from(values: &VertexAttributeValues) -> Self {
match values {
VertexAttributeValues::Float32(_) => VertexFormat::Float32,
VertexAttributeValues::Sint32(_) => VertexFormat::Sint32,
VertexAttributeValues::Uint32(_) => VertexFormat::Uint32,
VertexAttributeValues::Float32x2(_) => VertexFormat::Float32x2,
VertexAttributeValues::Sint32x2(_) => VertexFormat::Sint32x2,
VertexAttributeValues::Uint32x2(_) => VertexFormat::Uint32x2,
VertexAttributeValues::Float32x3(_) => VertexFormat::Float32x3,
VertexAttributeValues::Sint32x3(_) => VertexFormat::Sint32x3,
VertexAttributeValues::Uint32x3(_) => VertexFormat::Uint32x3,
VertexAttributeValues::Float32x4(_) => VertexFormat::Float32x4,
VertexAttributeValues::Sint32x4(_) => VertexFormat::Sint32x4,
VertexAttributeValues::Uint32x4(_) => VertexFormat::Uint32x4,
VertexAttributeValues::Sint16x2(_) => VertexFormat::Sint16x2,
VertexAttributeValues::Snorm16x2(_) => VertexFormat::Snorm16x2,
VertexAttributeValues::Uint16x2(_) => VertexFormat::Uint16x2,
VertexAttributeValues::Unorm16x2(_) => VertexFormat::Unorm16x2,
VertexAttributeValues::Sint16x4(_) => VertexFormat::Sint16x4,
VertexAttributeValues::Snorm16x4(_) => VertexFormat::Snorm16x4,
VertexAttributeValues::Uint16x4(_) => VertexFormat::Uint16x4,
VertexAttributeValues::Unorm16x4(_) => VertexFormat::Unorm16x4,
VertexAttributeValues::Sint8x2(_) => VertexFormat::Sint8x2,
VertexAttributeValues::Snorm8x2(_) => VertexFormat::Snorm8x2,
VertexAttributeValues::Uint8x2(_) => VertexFormat::Uint8x2,
VertexAttributeValues::Unorm8x2(_) => VertexFormat::Unorm8x2,
VertexAttributeValues::Sint8x4(_) => VertexFormat::Sint8x4,
VertexAttributeValues::Snorm8x4(_) => VertexFormat::Snorm8x4,
VertexAttributeValues::Uint8x4(_) => VertexFormat::Uint8x4,
VertexAttributeValues::Unorm8x4(_) => VertexFormat::Unorm8x4,
}
}
}
/// Describes how the vertex buffer is interpreted.
#[derive(Default, Clone, Debug, Hash, Eq, PartialEq)]
pub struct VertexBufferLayout {
/// The stride, in bytes, between elements of this buffer.
pub array_stride: BufferAddress,
/// How often this vertex buffer is "stepped" forward.
pub step_mode: VertexStepMode,
/// The list of attributes which comprise a single vertex.
pub attributes: Vec<VertexAttribute>,
}
impl VertexBufferLayout {
/// Creates a new densely packed [`VertexBufferLayout`] from an iterator of vertex formats.
/// Iteration order determines the `shader_location` and `offset` of the [`VertexAttributes`](VertexAttribute).
/// The first iterated item will have a `shader_location` and `offset` of zero.
/// The `array_stride` is the sum of the size of the iterated [`VertexFormats`](VertexFormat) (in bytes).
pub fn from_vertex_formats<T: IntoIterator<Item = VertexFormat>>(
step_mode: VertexStepMode,
vertex_formats: T,
) -> Self {
let mut offset = 0;
let mut attributes = Vec::new();
for (shader_location, format) in vertex_formats.into_iter().enumerate() {
attributes.push(VertexAttribute {
format,
offset,
shader_location: shader_location as u32,
});
offset += format.size();
}
VertexBufferLayout {
array_stride: offset,
step_mode,
attributes,
}
}
/// Returns a [`VertexBufferLayout`] with the shader location of every attribute offset by
/// `location`.
pub fn offset_locations_by(mut self, location: u32) -> Self {
self.attributes.iter_mut().for_each(|attr| {
attr.shader_location += location;
});
self
}
}
/// Describes the layout of the mesh vertices in GPU memory.
///
/// At most one copy of a mesh vertex buffer layout ever exists in GPU memory at
/// once. Therefore, comparing these for equality requires only a single pointer
/// comparison, and this type's [`PartialEq`] and [`Hash`] implementations take
/// advantage of this. To that end, this type doesn't implement
/// [`bevy_derive::Deref`] or [`bevy_derive::DerefMut`] in order to reduce the
/// possibility of accidental deep comparisons, which would be needlessly
/// expensive.
#[derive(Clone, Debug)]
pub struct MeshVertexBufferLayoutRef(pub Arc<MeshVertexBufferLayout>);
/// Stores the single copy of each mesh vertex buffer layout.
#[derive(Clone, Default, Resource)]
pub struct MeshVertexBufferLayouts(HashSet<Arc<MeshVertexBufferLayout>>);
impl MeshVertexBufferLayouts {
/// Inserts a new mesh vertex buffer layout in the store and returns a
/// reference to it, reusing the existing reference if this mesh vertex
/// buffer layout was already in the store.
pub fn insert(&mut self, layout: MeshVertexBufferLayout) -> MeshVertexBufferLayoutRef {
// Because the special `PartialEq` and `Hash` implementations that
// compare by pointer are on `MeshVertexBufferLayoutRef`, not on
// `Arc<MeshVertexBufferLayout>`, this compares the mesh vertex buffer
// structurally, not by pointer.
MeshVertexBufferLayoutRef(
self.0
.get_or_insert_with(&layout, |layout| Arc::new(layout.clone()))
.clone(),
)
}
}
impl PartialEq for MeshVertexBufferLayoutRef {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for MeshVertexBufferLayoutRef {}
impl Hash for MeshVertexBufferLayoutRef {
fn hash<H: Hasher>(&self, state: &mut H) {
// Hash the address of the underlying data, so two layouts that share the same
// `MeshVertexBufferLayout` will have the same hash.
(Arc::as_ptr(&self.0) as usize).hash(state);
}
}