diff --git a/Cargo.toml b/Cargo.toml index d21702310c137..2fb3ba61eadb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -253,6 +253,7 @@ common_api = [ "bevy_gizmos_render", "bevy_anti_alias", "bevy_gltf", + "bevy_gltf_render", "bevy_pbr", "bevy_post_process", "gltf_animation", @@ -317,6 +318,9 @@ bevy_gilrs = ["bevy_internal/bevy_gilrs"] # [glTF](https://www.khronos.org/gltf/) support bevy_gltf = ["bevy_internal/bevy_gltf"] +# Renders GLTFs via PBR +bevy_gltf_render = ["bevy_internal/bevy_gltf_render"] + # Adds PBR rendering bevy_pbr = ["bevy_internal/bevy_pbr"] diff --git a/crates/bevy_gltf/Cargo.toml b/crates/bevy_gltf/Cargo.toml index 8df8933fa7d23..c790cbdee8833 100644 --- a/crates/bevy_gltf/Cargo.toml +++ b/crates/bevy_gltf/Cargo.toml @@ -9,12 +9,12 @@ license = "MIT OR Apache-2.0" keywords = ["bevy"] [features] -pbr_transmission_textures = ["bevy_pbr/pbr_transmission_textures"] +pbr_transmission_textures = ["bevy_material/pbr_transmission_textures"] pbr_multi_layer_material_textures = [ - "bevy_pbr/pbr_multi_layer_material_textures", + "bevy_material/pbr_multi_layer_material_textures", ] -pbr_anisotropy_texture = ["bevy_pbr/pbr_anisotropy_texture"] -pbr_specular_textures = ["bevy_pbr/pbr_specular_textures"] +pbr_anisotropy_texture = ["bevy_material/pbr_anisotropy_texture"] +pbr_specular_textures = ["bevy_material/pbr_specular_textures"] [dependencies] # bevy @@ -29,10 +29,11 @@ bevy_image = { path = "../bevy_image", version = "0.18.0-dev" } bevy_light = { path = "../bevy_light", version = "0.18.0-dev" } bevy_camera = { path = "../bevy_camera", version = "0.18.0-dev" } bevy_math = { path = "../bevy_math", version = "0.18.0-dev" } -bevy_mesh = { path = "../bevy_mesh", version = "0.18.0-dev" } -bevy_pbr = { path = "../bevy_pbr", version = "0.18.0-dev" } +bevy_mesh = { path = "../bevy_mesh", version = "0.18.0-dev", features = [ + "morph", + "bevy_mikktspace", +] } bevy_reflect = { path = "../bevy_reflect", version = "0.18.0-dev" } -bevy_render = { path = "../bevy_render", version = "0.18.0-dev" } bevy_material = { path = "../bevy_material", version = "0.18.0-dev" } bevy_scene = { path = "../bevy_scene", version = "0.18.0-dev" } bevy_transform = { path = "../bevy_transform", version = "0.18.0-dev" } @@ -65,6 +66,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.140" smallvec = { version = "1", default-features = false } tracing = { version = "0.1", default-features = false, features = ["std"] } +wgpu-types = { version = "26", default-features = false } [dev-dependencies] bevy_log = { path = "../bevy_log", version = "0.18.0-dev" } diff --git a/crates/bevy_gltf/render/Cargo.toml b/crates/bevy_gltf/render/Cargo.toml new file mode 100644 index 0000000000000..62a33426cc7c4 --- /dev/null +++ b/crates/bevy_gltf/render/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "bevy_gltf_render" +version = "0.18.0-dev" +edition = "2024" +description = "Bevy Engine GLTF loading" +homepage = "https://bevy.org" +repository = "https://github.com/bevyengine/bevy" +license = "MIT OR Apache-2.0" +keywords = ["bevy"] + +[features] +pbr_transmission_textures = ["bevy_material/pbr_transmission_textures"] +pbr_multi_layer_material_textures = [ + "bevy_material/pbr_multi_layer_material_textures", +] +pbr_anisotropy_texture = ["bevy_material/pbr_anisotropy_texture"] +pbr_specular_textures = ["bevy_material/pbr_specular_textures"] + +[dependencies] +# bevy +bevy_app = { path = "../../bevy_app", version = "0.18.0-dev" } +bevy_asset = { path = "../../bevy_asset", version = "0.18.0-dev" } +bevy_ecs = { path = "../../bevy_ecs", version = "0.18.0-dev" } +bevy_gltf = { path = "../", version = "0.18.0-dev" } +bevy_material = { path = "../../bevy_material", version = "0.18.0-dev" } +bevy_pbr = { path = "../../bevy_pbr", version = "0.18.0-dev" } + +# other + +[dev-dependencies] +bevy_log = { path = "../../bevy_log", version = "0.18.0-dev" } + +[lints] +workspace = true + +[package.metadata.docs.rs] +rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"] +all-features = true diff --git a/crates/bevy_gltf/render/LICENSE-APACHE b/crates/bevy_gltf/render/LICENSE-APACHE new file mode 100644 index 0000000000000..d9a10c0d8e868 --- /dev/null +++ b/crates/bevy_gltf/render/LICENSE-APACHE @@ -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 diff --git a/crates/bevy_gltf/render/LICENSE-MIT b/crates/bevy_gltf/render/LICENSE-MIT new file mode 100644 index 0000000000000..9cf106272ac3b --- /dev/null +++ b/crates/bevy_gltf/render/LICENSE-MIT @@ -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. diff --git a/crates/bevy_gltf/render/src/lib.rs b/crates/bevy_gltf/render/src/lib.rs new file mode 100644 index 0000000000000..eccdd5b2bf0c3 --- /dev/null +++ b/crates/bevy_gltf/render/src/lib.rs @@ -0,0 +1,26 @@ +#![cfg_attr(docsrs, feature(doc_cfg))] +#![forbid(unsafe_code)] +#![doc( + html_logo_url = "https://bevy.org/assets/icon.png", + html_favicon_url = "https://bevy.org/assets/icon.png" +)] + +//! Plugin for rendering GLTF's using PBR + +use bevy_app::{App, Plugin, PreUpdate}; + + +pub mod render; + +pub use render::*; + +#[derive(Default)] +pub struct GltfRenderPlugin; + +impl Plugin for GltfRenderPlugin { + fn build(&self, app: &mut App) { + // TODO: set this schedule to just after the Marker gets added + app.add_systems(PreUpdate, swap_marker_mesh_material_3d); + } +} + diff --git a/crates/bevy_gltf/render/src/render.rs b/crates/bevy_gltf/render/src/render.rs new file mode 100644 index 0000000000000..482e9f66a0742 --- /dev/null +++ b/crates/bevy_gltf/render/src/render.rs @@ -0,0 +1,76 @@ +use bevy_asset::Assets; +use bevy_ecs::{ + entity::Entity, + system::{Commands, Query, Res, ResMut}, +}; +use bevy_gltf::material::{MarkerMeshMaterial3d, GltfMaterial}; +use bevy_pbr::{MeshMaterial3d, StandardMaterial}; + + +fn material_from_short_material(material: &GltfMaterial) -> StandardMaterial { + StandardMaterial { + base_color: material.base_color, + base_color_channel: material.base_color_channel.clone(), + base_color_texture: material.base_color_texture.clone(), + emissive: material.emissive, + emissive_channel: material.emissive_channel.clone(), + emissive_texture: material.emissive_texture.clone(), + perceptual_roughness: material.perceptual_roughness, + metallic: material.metallic, + metallic_roughness_channel: material.metallic_roughness_channel.clone(), + metallic_roughness_texture: material.metallic_roughness_texture.clone(), + reflectance: material.reflectance, + specular_tint: material.specular_tint, + specular_transmission: material.specular_transmission, + #[cfg(feature = "pbr_transmission_textures")] + specular_transmission_channel: material.specular_transmission_channel.clone(), + #[cfg(feature = "pbr_transmission_textures")] + specular_transmission_texture: material.specular_transmission_texture.clone(), + thickness: material.thickness, + #[cfg(feature = "pbr_transmission_textures")] + thickness_channel: material.thickness_channel.clone(), + #[cfg(feature = "pbr_transmission_textures")] + thickness_texture: material.thickness_texture.clone(), + ior: material.ior, + attenuation_distance: material.attenuation_distance, + attenuation_color: material.attenuation_color, + normal_map_channel: material.normal_map_channel.clone(), + normal_map_texture: material.normal_map_texture.clone(), + occlusion_channel: material.occlusion_channel.clone(), + occlusion_texture: material.occlusion_texture.clone(), + clearcoat: material.clearcoat, + clearcoat_perceptual_roughness: material.clearcoat_perceptual_roughness, + anisotropy_strength: material.anisotropy_strength, + anisotropy_rotation: material.anisotropy_rotation, + double_sided: material.double_sided, + cull_mode: material.cull_mode, + unlit: material.unlit, + alpha_mode: material.alpha_mode, + uv_transform: material.uv_transform, + ..Default::default() + } +} + +pub(crate) fn swap_marker_mesh_material_3d( + mut commands: Commands, + query: Query<(Entity, &MarkerMeshMaterial3d)>, + short_materials: Res>, + mut materials: ResMut>, +) { + for (entity, marker) in &query { + let content = &marker.0; + + let short_material = short_materials.get(content).unwrap(); + + let material = material_from_short_material(short_material); + + let content2 = materials.add(material); + + let mesh_material_3d = MeshMaterial3d::(content2); + + commands + .entity(entity) + .remove::() + .insert(mesh_material_3d); + } +} diff --git a/crates/bevy_gltf/src/assets.rs b/crates/bevy_gltf/src/assets.rs index bfc920ebcea1f..62cea475f226f 100644 --- a/crates/bevy_gltf/src/assets.rs +++ b/crates/bevy_gltf/src/assets.rs @@ -7,12 +7,11 @@ use bevy_animation::AnimationClip; use bevy_asset::{Asset, Handle}; use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_mesh::{skinning::SkinnedMeshInverseBindposes, Mesh}; -use bevy_pbr::StandardMaterial; use bevy_platform::collections::HashMap; use bevy_reflect::{prelude::ReflectDefault, Reflect, TypePath}; use bevy_scene::Scene; -use crate::GltfAssetLabel; +use crate::{GltfAssetLabel, GltfMaterial}; /// Representation of a loaded glTF file. #[derive(Asset, Debug, TypePath)] @@ -26,9 +25,9 @@ pub struct Gltf { /// Named meshes loaded from the glTF file. pub named_meshes: HashMap, Handle>, /// All materials loaded from the glTF file. - pub materials: Vec>, + pub materials: Vec>, /// Named materials loaded from the glTF file. - pub named_materials: HashMap, Handle>, + pub named_materials: HashMap, Handle>, /// All nodes loaded from the glTF file. pub nodes: Vec>, /// Named nodes loaded from the glTF file. @@ -158,7 +157,7 @@ impl GltfNode { } } -/// Part of a [`GltfMesh`] that consists of a [`Mesh`], an optional [`StandardMaterial`] and [`GltfExtras`]. +/// Part of a [`GltfMesh`] that consists of a [`Mesh`], an optional [`GltfMaterial`] and [`GltfExtras`]. /// /// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-mesh-primitive). #[derive(Asset, Debug, Clone, TypePath)] @@ -172,7 +171,7 @@ pub struct GltfPrimitive { /// Topology to be rendered. pub mesh: Handle, /// Material to apply to the `mesh`. - pub material: Option>, + pub material: Option>, /// Additional data. pub extras: Option, /// Additional data of the `material`. @@ -185,7 +184,7 @@ impl GltfPrimitive { gltf_mesh: &gltf::Mesh, gltf_primitive: &gltf::Primitive, mesh: Handle, - material: Option>, + material: Option>, extras: Option, material_extras: Option, ) -> Self { diff --git a/crates/bevy_gltf/src/label.rs b/crates/bevy_gltf/src/label.rs index b74d5ab2d6631..5f75a3ddd4854 100644 --- a/crates/bevy_gltf/src/label.rs +++ b/crates/bevy_gltf/src/label.rs @@ -54,16 +54,16 @@ pub enum GltfAssetLabel { }, /// `Texture{}`: glTF Texture as a Bevy [`Image`](bevy_image::prelude::Image) Texture(usize), - /// `Material{}`: glTF Material as a Bevy [`StandardMaterial`](bevy_pbr::StandardMaterial) + /// `Material{}`: glTF Material as a Bevy [`GltfMaterial`](bevy_material::GltfMaterial) Material { /// Index of this material index: usize, - /// Used to set the [`Face`](bevy_render::render_resource::Face) of the material, + /// Used to set the [`Face`](bevy_material::render_resource::Face) of the material, /// useful if it is used with negative scale is_scale_inverted: bool, }, /// `DefaultMaterial`: glTF's default Material as a - /// Bevy [`StandardMaterial`](bevy_pbr::StandardMaterial) + /// Bevy [`GltfMaterial`](bevy_material::GltfMaterial) DefaultMaterial, /// `Animation{}`: glTF Animation as Bevy [`AnimationClip`](bevy_animation::AnimationClip) Animation(usize), diff --git a/crates/bevy_gltf/src/lib.rs b/crates/bevy_gltf/src/lib.rs index bb01fb034fc30..00474af114ba0 100644 --- a/crates/bevy_gltf/src/lib.rs +++ b/crates/bevy_gltf/src/lib.rs @@ -131,6 +131,7 @@ mod assets; mod convert_coordinates; mod label; mod loader; +pub mod material; mod vertex_attributes; extern crate alloc; @@ -155,7 +156,7 @@ pub mod prelude { pub use crate::{assets::Gltf, assets::GltfExtras, label::GltfAssetLabel}; } -pub use {assets::*, label::GltfAssetLabel, loader::*}; +pub use {assets::*, label::GltfAssetLabel, loader::*, material::*}; // Has to store an Arc> as there is no other way to mutate fields of asset loaders. /// Stores default [`ImageSamplerDescriptor`] in main world. @@ -247,6 +248,7 @@ impl Plugin for GltfPlugin { app.init_asset::() .init_asset::() .init_asset::() + .init_asset::() .init_asset::() .init_asset::() .preregister_asset_loader::(&["gltf", "glb"]); diff --git a/crates/bevy_gltf/src/loader/extensions/khr_materials_specular.rs b/crates/bevy_gltf/src/loader/extensions/khr_materials_specular.rs index 83743edf90f66..c97ceafc577b1 100644 --- a/crates/bevy_gltf/src/loader/extensions/khr_materials_specular.rs +++ b/crates/bevy_gltf/src/loader/extensions/khr_materials_specular.rs @@ -21,7 +21,7 @@ use { /// `KHR_materials_specular` specification requirement that stems from the fact /// that glTF is specified in terms of a specular strength model, not the /// reflectance model that Filament and Bevy use. A workaround, which is noted -/// in the [`StandardMaterial`](bevy_pbr::StandardMaterial) documentation, is to set the reflectance value +/// in the [`GltfMaterial`](bevy_material::GltfMaterial) documentation, is to set the reflectance value /// to 2.0, which spreads the specular map range from [0.0, 1.0] as normal. /// /// See the specification: diff --git a/crates/bevy_gltf/src/loader/gltf_ext/material.rs b/crates/bevy_gltf/src/loader/gltf_ext/material.rs index 4616decccf473..360184935da6e 100644 --- a/crates/bevy_gltf/src/loader/gltf_ext/material.rs +++ b/crates/bevy_gltf/src/loader/gltf_ext/material.rs @@ -1,6 +1,6 @@ +use bevy_material::alpha::AlphaMode; use bevy_material::UvChannel; use bevy_math::Affine2; -use bevy_render::alpha::AlphaMode; use gltf::{json::texture::Info, Material}; diff --git a/crates/bevy_gltf/src/loader/mod.rs b/crates/bevy_gltf/src/loader/mod.rs index 12f69cbc349cb..56e90c74fc434 100644 --- a/crates/bevy_gltf/src/loader/mod.rs +++ b/crates/bevy_gltf/src/loader/mod.rs @@ -28,15 +28,14 @@ use bevy_image::{ use bevy_light::{DirectionalLight, PointLight, SpotLight}; #[cfg(feature = "pbr_transmission_textures")] use bevy_material::UvChannel; +use bevy_material::{mesh::skin::MAX_JOINTS, render_resource::Face}; use bevy_math::{Mat4, Vec3}; use bevy_mesh::{ morph::{MeshMorphWeights, MorphAttributes, MorphTargetImage, MorphWeights}, skinning::{SkinnedMesh, SkinnedMeshInverseBindposes}, Indices, Mesh, Mesh3d, MeshVertexAttribute, PrimitiveTopology, }; -use bevy_pbr::{MeshMaterial3d, StandardMaterial, MAX_JOINTS}; use bevy_platform::collections::{HashMap, HashSet}; -use bevy_render::render_resource::Face; use bevy_scene::Scene; #[cfg(not(target_arch = "wasm32"))] use bevy_tasks::IoTaskPool; @@ -57,8 +56,7 @@ use thiserror::Error; use tracing::{error, info_span, warn}; use crate::{ - vertex_attributes::convert_attribute, Gltf, GltfAssetLabel, GltfExtras, GltfMaterialExtras, - GltfMaterialName, GltfMeshExtras, GltfMeshName, GltfNode, GltfSceneExtras, GltfSkin, + vertex_attributes::convert_attribute, Gltf, GltfAssetLabel, GltfExtras, GltfMaterial, GltfMaterialExtras, GltfMaterialName, GltfMeshExtras, GltfMeshName, GltfNode, GltfSceneExtras, GltfSkin, MarkerMeshMaterial3d }; #[cfg(feature = "bevy_animation")] @@ -1143,13 +1141,13 @@ async fn load_image<'a, 'b>( } } -/// Loads a glTF material as a bevy [`StandardMaterial`] and returns it. +/// Loads a glTF material as a bevy [`GltfMaterial`] and returns it. fn load_material( material: &Material, load_context: &mut LoadContext, document: &Document, is_scale_inverted: bool, -) -> Handle { +) -> Handle { let material_label = material_label(material, is_scale_inverted); load_context .labeled_asset_scope::<_, ()>(material_label.to_string(), |load_context| { @@ -1303,7 +1301,7 @@ fn load_material( let base_emissive = LinearRgba::rgb(emissive[0], emissive[1], emissive[2]); let emissive = base_emissive * material.emissive_strength().unwrap_or(1.0); - Ok(StandardMaterial { + Ok(GltfMaterial { base_color: Color::linear_rgba(color[0], color[1], color[2], color[3]), base_color_channel, base_color_texture, @@ -1534,9 +1532,7 @@ fn load_node( let mut mesh_entity = parent.spawn(( // TODO: handle missing label handle errors here? Mesh3d(load_context.get_label_handle(primitive_label.to_string())), - MeshMaterial3d::( - load_context.get_label_handle(&material_label), - ), + MarkerMeshMaterial3d(load_context.get_label_handle(&material_label)), )); let target_count = primitive.morph_targets().len(); @@ -1915,9 +1911,9 @@ mod test { use bevy_ecs::{resource::Resource, world::World}; use bevy_image::{Image, ImageLoaderSettings}; use bevy_log::LogPlugin; + use bevy_material::GltfMaterial; use bevy_mesh::skinning::SkinnedMeshInverseBindposes; use bevy_mesh::MeshPlugin; - use bevy_pbr::StandardMaterial; use bevy_scene::ScenePlugin; fn test_app(dir: Dir) -> App { @@ -2408,7 +2404,7 @@ mod test { fn reads_images_in_custom_asset_source() { let (mut app, dir) = test_app_custom_asset_source(); - app.init_asset::(); + app.init_asset::(); // Note: We need the material here since otherwise we don't store the texture handle, which // can result in the image getting dropped leading to the gltf never being loaded with diff --git a/crates/bevy_gltf/src/material.rs b/crates/bevy_gltf/src/material.rs new file mode 100644 index 0000000000000..8152280bd1a77 --- /dev/null +++ b/crates/bevy_gltf/src/material.rs @@ -0,0 +1,147 @@ +use bevy_asset::{Asset, Handle}; +use bevy_color::{Color, LinearRgba}; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; +use bevy_image::Image; +use bevy_material::{alpha::AlphaMode, UvChannel}; +use bevy_math::Affine2; +use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath}; +use wgpu_types::Face; + + + +#[derive(Component, Clone, Debug, Reflect)] +#[reflect(Component, Default, Clone, PartialEq)] +pub struct MarkerMeshMaterial3d(pub Handle); + +impl Default for MarkerMeshMaterial3d { + fn default() -> Self { + Self(Handle::default()) + } +} + +impl PartialEq for MarkerMeshMaterial3d { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for MarkerMeshMaterial3d {} + +// Data to build a bevy_pbr::StandardMaterial +#[derive(Asset, Debug, TypePath)] +pub struct GltfMaterial { + pub base_color: Color, + pub base_color_channel: UvChannel, + pub base_color_texture: Option>, + pub emissive: LinearRgba, + pub emissive_channel: UvChannel, + pub emissive_texture: Option>, + pub perceptual_roughness: f32, + pub metallic: f32, + pub metallic_roughness_channel: UvChannel, + pub metallic_roughness_texture: Option>, + pub reflectance: f32, + pub specular_tint: Color, + pub specular_transmission: f32, + #[cfg(feature = "pbr_transmission_textures")] + pub specular_transmission_channel: UvChannel, + #[cfg(feature = "pbr_transmission_textures")] + pub specular_transmission_texture: Option>, + pub thickness: f32, + #[cfg(feature = "pbr_transmission_textures")] + pub thickness_channel: UvChannel, + #[cfg(feature = "pbr_transmission_textures")] + pub thickness_texture: Option>, + pub ior: f32, + pub attenuation_distance: f32, + pub attenuation_color: Color, + pub normal_map_channel: UvChannel, + pub normal_map_texture: Option>, + pub occlusion_channel: UvChannel, + pub occlusion_texture: Option>, + pub clearcoat: f32, + pub clearcoat_perceptual_roughness: f32, + pub anisotropy_strength: f32, + pub anisotropy_rotation: f32, + pub double_sided: bool, + pub cull_mode: Option, + pub unlit: bool, + pub alpha_mode: AlphaMode, + pub uv_transform: Affine2, +} + +impl Default for GltfMaterial { + fn default() -> Self { + GltfMaterial { + // White because it gets multiplied with texture values if someone uses + // a texture. + base_color: Color::WHITE, + base_color_channel: UvChannel::Uv0, + base_color_texture: None, + emissive: LinearRgba::BLACK, + emissive_channel: UvChannel::Uv0, + emissive_texture: None, + // Matches Blender's default roughness. + perceptual_roughness: 0.5, + // Metallic should generally be set to 0.0 or 1.0. + metallic: 0.0, + metallic_roughness_channel: UvChannel::Uv0, + metallic_roughness_texture: None, + // Minimum real-world reflectance is 2%, most materials between 2-5% + // Expressed in a linear scale and equivalent to 4% reflectance see + // + reflectance: 0.5, + specular_transmission: 0.0, + #[cfg(feature = "pbr_transmission_textures")] + specular_transmission_channel: UvChannel::Uv0, + #[cfg(feature = "pbr_transmission_textures")] + specular_transmission_texture: None, + thickness: 0.0, + #[cfg(feature = "pbr_transmission_textures")] + thickness_channel: UvChannel::Uv0, + #[cfg(feature = "pbr_transmission_textures")] + thickness_texture: None, + ior: 1.5, + attenuation_color: Color::WHITE, + attenuation_distance: f32::INFINITY, + occlusion_channel: UvChannel::Uv0, + occlusion_texture: None, + normal_map_channel: UvChannel::Uv0, + normal_map_texture: None, + #[cfg(feature = "pbr_specular_textures")] + specular_channel: UvChannel::Uv0, + #[cfg(feature = "pbr_specular_textures")] + specular_texture: None, + specular_tint: Color::WHITE, + #[cfg(feature = "pbr_specular_textures")] + specular_tint_channel: UvChannel::Uv0, + #[cfg(feature = "pbr_specular_textures")] + specular_tint_texture: None, + clearcoat: 0.0, + clearcoat_perceptual_roughness: 0.5, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_channel: UvChannel::Uv0, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_texture: None, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_roughness_channel: UvChannel::Uv0, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_roughness_texture: None, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_normal_channel: UvChannel::Uv0, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_normal_texture: None, + anisotropy_strength: 0.0, + anisotropy_rotation: 0.0, + #[cfg(feature = "pbr_anisotropy_texture")] + anisotropy_channel: UvChannel::Uv0, + #[cfg(feature = "pbr_anisotropy_texture")] + anisotropy_texture: None, + double_sided: false, + cull_mode: Some(Face::Back), + unlit: false, + alpha_mode: AlphaMode::Opaque, + uv_transform: Affine2::IDENTITY, + } + } +} diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index cc671e33ccf5e..0d8bde59b2f1b 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -263,7 +263,8 @@ bevy_ui_render = ["dep:bevy_ui_render", "bevy_sprite_render", "bevy_ui"] bevy_solari = ["dep:bevy_solari", "bevy_pbr"] bevy_gizmos = ["dep:bevy_gizmos", "bevy_camera"] bevy_gizmos_render = ["dep:bevy_gizmos_render", "bevy_gizmos"] -bevy_gltf = ["dep:bevy_gltf", "bevy_scene", "bevy_pbr"] +bevy_gltf = ["dep:bevy_gltf", "bevy_material", "bevy_scene", "bevy_pbr"] +bevy_gltf_render = ["dep:bevy_gltf_render", "bevy_gltf"] # Used to disable code that is unsupported when Bevy is dynamically linked dynamic_linking = ["bevy_diagnostic/dynamic_linking"] @@ -495,6 +496,7 @@ bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.18.0-dev" } bevy_gizmos = { path = "../bevy_gizmos", optional = true, version = "0.18.0-dev", default-features = false } bevy_gizmos_render = { path = "../bevy_gizmos_render", optional = true, version = "0.18.0-dev", default-features = false } bevy_gltf = { path = "../bevy_gltf", optional = true, version = "0.18.0-dev" } +bevy_gltf_render = { path = "../bevy_gltf/render", optional = true, version = "0.18.0-dev" } bevy_feathers = { path = "../bevy_feathers", optional = true, version = "0.18.0-dev" } bevy_image = { path = "../bevy_image", optional = true, version = "0.18.0-dev" } bevy_shader = { path = "../bevy_shader", optional = true, version = "0.18.0-dev" } diff --git a/crates/bevy_internal/src/default_plugins.rs b/crates/bevy_internal/src/default_plugins.rs index 75b47cf5c57e7..8d7c885efdd6b 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -45,6 +45,8 @@ plugin_group! { bevy_camera:::CameraPlugin, #[cfg(feature = "bevy_light")] bevy_light:::LightPlugin, + #[cfg(feature = "bevy_gltf")] + bevy_gltf:::GltfPlugin, #[cfg(feature = "bevy_render")] #[custom(cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded")))] bevy_render::pipelined_rendering:::PipelinedRenderingPlugin, @@ -68,8 +70,8 @@ plugin_group! { bevy_pbr:::PbrPlugin, // NOTE: Load this after renderer initialization so that it knows about the supported // compressed texture formats. - #[cfg(feature = "bevy_gltf")] - bevy_gltf:::GltfPlugin, + #[cfg(feature = "bevy_gltf_render")] + bevy_gltf_render:::GltfRenderPlugin, #[cfg(feature = "bevy_audio")] bevy_audio:::AudioPlugin, #[cfg(feature = "bevy_gilrs")] diff --git a/crates/bevy_material/Cargo.toml b/crates/bevy_material/Cargo.toml index e8a35b480f4c2..5e0a966144124 100644 --- a/crates/bevy_material/Cargo.toml +++ b/crates/bevy_material/Cargo.toml @@ -8,6 +8,12 @@ repository = "https://github.com/bevyengine/bevy" license = "MIT OR Apache-2.0" keywords = ["bevy"] +[features] +pbr_transmission_textures = [] +pbr_multi_layer_material_textures = [] +pbr_anisotropy_texture = [] +pbr_specular_textures = [] + [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.18.0-dev" } diff --git a/crates/bevy_material/src/lib.rs b/crates/bevy_material/src/lib.rs index 1d372eae6a78f..71ce1bab112b9 100644 --- a/crates/bevy_material/src/lib.rs +++ b/crates/bevy_material/src/lib.rs @@ -5,6 +5,7 @@ extern crate alloc; pub mod alpha; pub mod material; +pub mod mesh; pub mod opaque; pub mod pbr_material; pub mod render; diff --git a/crates/bevy_material/src/mesh/mod.rs b/crates/bevy_material/src/mesh/mod.rs new file mode 100644 index 0000000000000..1ac166094a0af --- /dev/null +++ b/crates/bevy_material/src/mesh/mod.rs @@ -0,0 +1 @@ +pub mod skin; diff --git a/crates/bevy_material/src/mesh/skin.rs b/crates/bevy_material/src/mesh/skin.rs new file mode 100644 index 0000000000000..240129490f41f --- /dev/null +++ b/crates/bevy_material/src/mesh/skin.rs @@ -0,0 +1,34 @@ +use core::mem::size_of; + +use bevy_math::Mat4; + +/// Maximum number of joints supported for skinned meshes. +/// +/// It is used to allocate buffers. +/// The correctness of the value depends on the GPU/platform. +/// The current value is chosen because it is guaranteed to work everywhere. +/// To allow for bigger values, a check must be made for the limits +/// of the GPU at runtime, which would mean not using consts anymore. +pub const MAX_JOINTS: usize = 256; + +/// The total number of joints we support. +/// +/// This is 256 GiB worth of joint matrices, which we will never hit under any +/// reasonable circumstances. +pub const MAX_TOTAL_JOINTS: u32 = 1024 * 1024 * 1024; + +/// The number of joints that we allocate at a time. +/// +/// Some hardware requires that uniforms be allocated on 256-byte boundaries, so +/// we need to allocate 4 64-byte matrices at a time to satisfy alignment +/// requirements. +pub const JOINTS_PER_ALLOCATION_UNIT: u32 = (256 / size_of::()) as u32; + +/// The maximum ratio of the number of entities whose transforms changed to the +/// total number of joints before we re-extract all joints. +/// +/// We use this as a heuristic to decide whether it's worth switching over to +/// fine-grained detection to determine which skins need extraction. If the +/// number of changed entities is over this threshold, we skip change detection +/// and simply re-extract the transforms of all joints. +pub const JOINT_EXTRACTION_THRESHOLD_FACTOR: f64 = 0.25; diff --git a/crates/bevy_pbr/Cargo.toml b/crates/bevy_pbr/Cargo.toml index 96dc4897ccb34..b7beb3333d7ca 100644 --- a/crates/bevy_pbr/Cargo.toml +++ b/crates/bevy_pbr/Cargo.toml @@ -11,11 +11,13 @@ keywords = ["bevy"] [features] webgl = ["bevy_light/webgl"] webgpu = ["bevy_light/webgpu"] -pbr_transmission_textures = [] -pbr_multi_layer_material_textures = [] -pbr_anisotropy_texture = [] +pbr_transmission_textures = ["bevy_material/pbr_transmission_textures"] +pbr_multi_layer_material_textures = [ + "bevy_material/pbr_multi_layer_material_textures", +] +pbr_anisotropy_texture = ["bevy_material/pbr_anisotropy_texture"] +pbr_specular_textures = ["bevy_material/pbr_specular_textures"] experimental_pbr_pcss = ["bevy_light/experimental_pbr_pcss"] -pbr_specular_textures = [] pbr_clustered_decals = [] pbr_light_textures = [] bluenoise_texture = ["bevy_image/ktx2", "bevy_image/zstd"] diff --git a/crates/bevy_pbr/src/render/mesh_bindings.rs b/crates/bevy_pbr/src/render/mesh_bindings.rs index 44211ca242f7f..dc2eaf113f1f2 100644 --- a/crates/bevy_pbr/src/render/mesh_bindings.rs +++ b/crates/bevy_pbr/src/render/mesh_bindings.rs @@ -1,5 +1,6 @@ //! Bind group layout related definitions for the mesh pipeline. +use bevy_material::mesh::skin::MAX_JOINTS; pub use bevy_material::render::MeshLayouts; use bevy_math::Mat4; use bevy_mesh::morph::MAX_MORPH_WEIGHTS; @@ -8,7 +9,7 @@ use bevy_render::{ renderer::{RenderAdapter, RenderDevice}, }; -use crate::{binding_arrays_are_usable, render::skin::MAX_JOINTS, LightmapSlab}; +use crate::{binding_arrays_are_usable, LightmapSlab}; const MORPH_WEIGHT_SIZE: usize = size_of::(); diff --git a/crates/bevy_pbr/src/render/mod.rs b/crates/bevy_pbr/src/render/mod.rs index 6a29823022b58..58fc67a90981b 100644 --- a/crates/bevy_pbr/src/render/mod.rs +++ b/crates/bevy_pbr/src/render/mod.rs @@ -14,4 +14,4 @@ pub use mesh::*; pub use mesh_bindings::MeshLayouts; pub use mesh_view_bindings::*; pub use morph::*; -pub use skin::{extract_skins, prepare_skins, skins_use_uniform_buffers, SkinUniforms, MAX_JOINTS}; +pub use skin::{extract_skins, prepare_skins, skins_use_uniform_buffers, SkinUniforms}; diff --git a/crates/bevy_pbr/src/render/skin.rs b/crates/bevy_pbr/src/render/skin.rs index 93e86d3124de7..aee8171a39acb 100644 --- a/crates/bevy_pbr/src/render/skin.rs +++ b/crates/bevy_pbr/src/render/skin.rs @@ -4,6 +4,9 @@ use std::sync::OnceLock; use bevy_asset::{prelude::AssetChanged, Assets}; use bevy_camera::visibility::ViewVisibility; use bevy_ecs::prelude::*; +use bevy_material::mesh::skin::{ + JOINTS_PER_ALLOCATION_UNIT, JOINT_EXTRACTION_THRESHOLD_FACTOR, MAX_JOINTS, MAX_TOTAL_JOINTS, +}; use bevy_math::Mat4; use bevy_mesh::skinning::{SkinnedMesh, SkinnedMeshInverseBindposes}; use bevy_platform::collections::hash_map::Entry; diff --git a/crates/bevy_render/src/mesh/skin.rs b/crates/bevy_render/src/mesh/skin.rs index e6ca697a78675..9bf2d2dd0ff9e 100644 --- a/crates/bevy_render/src/mesh/skin.rs +++ b/crates/bevy_render/src/mesh/skin.rs @@ -1,43 +1,13 @@ use core::mem::size_of; use bevy_ecs::prelude::*; +use bevy_material::mesh::skin::JOINTS_PER_ALLOCATION_UNIT; use bevy_math::Mat4; use bevy_render::render_resource::Buffer; use bevy_render::sync_world::{MainEntity, MainEntityHashMap}; use offset_allocator::{Allocation, Allocator}; use smallvec::SmallVec; -/// Maximum number of joints supported for skinned meshes. -/// -/// It is used to allocate buffers. -/// The correctness of the value depends on the GPU/platform. -/// The current value is chosen because it is guaranteed to work everywhere. -/// To allow for bigger values, a check must be made for the limits -/// of the GPU at runtime, which would mean not using consts anymore. -pub const MAX_JOINTS: usize = 256; - -/// The total number of joints we support. -/// -/// This is 256 GiB worth of joint matrices, which we will never hit under any -/// reasonable circumstances. -pub const MAX_TOTAL_JOINTS: u32 = 1024 * 1024 * 1024; - -/// The number of joints that we allocate at a time. -/// -/// Some hardware requires that uniforms be allocated on 256-byte boundaries, so -/// we need to allocate 4 64-byte matrices at a time to satisfy alignment -/// requirements. -pub const JOINTS_PER_ALLOCATION_UNIT: u32 = (256 / size_of::()) as u32; - -/// The maximum ratio of the number of entities whose transforms changed to the -/// total number of joints before we re-extract all joints. -/// -/// We use this as a heuristic to decide whether it's worth switching over to -/// fine-grained detection to determine which skins need extraction. If the -/// number of changed entities is over this threshold, we skip change detection -/// and simply re-extract the transforms of all joints. -pub const JOINT_EXTRACTION_THRESHOLD_FACTOR: f64 = 0.25; - /// The location of the first joint matrix in the skin uniform buffer. #[derive(Clone, Copy)] pub struct SkinByteOffset {