Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fcebe9a
Add support for custom glTF vertex attributes.
komadori Jul 18, 2022
fda7b43
Fix AddCustomVertexAttributeExt.
komadori Jul 18, 2022
40088ca
Remove 'glTF' from warning strings since logger prints context.
komadori Jul 18, 2022
e56173a
Fix formatting.
komadori Jul 18, 2022
b0df749
Fix clippy doc lint.
komadori Jul 18, 2022
ebe7c5b
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Aug 8, 2022
a0aeb43
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Oct 1, 2022
99d97eb
Explicitly derive Resource trait for GltfConfiguration.
komadori Oct 1, 2022
a5c2beb
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Nov 2, 2022
26c0d55
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Nov 13, 2022
855b4df
Replace configuration resource with plugin settings.
komadori Nov 13, 2022
370214f
Fix signature of add_custom_vertex_attribute().
komadori Nov 13, 2022
fde3461
Add example using a custom glTF vertex attribute.
komadori Nov 14, 2022
322a122
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Dec 23, 2022
645bd62
Update example pages.
komadori Dec 23, 2022
0e41c30
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Jan 13, 2023
0527579
Add explanitory comments to custom_gltf_2d example.
komadori Jan 15, 2023
def0fa0
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Mar 16, 2023
a3dd678
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Mar 22, 2023
1a61d05
Fix use of deprecated method add_startup_system().
komadori Mar 22, 2023
8ebb525
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Apr 20, 2023
262244e
Refactor glTF attribute marshalling into a separate module.
komadori Apr 21, 2023
7a7f149
Tidy up attribute conversion error handling.
komadori Apr 22, 2023
e037078
Tighten attrs module code.
komadori Apr 22, 2023
1fc64df
Change barycentric model from glb to non-binary glTF format.
komadori Apr 23, 2023
8c02a4b
Rename bevy_gltf::attrs module to vertex_attributes.
komadori Apr 23, 2023
3ed7225
Rename custom_gltf_2d example to custom_gltf_vertex_attribute.
komadori Apr 23, 2023
94179e3
Merge branch 'main' of github.com:komadori/bevy into gltf-custom-attr…
komadori Apr 23, 2023
e30d883
Fix rustfmt.
komadori Apr 23, 2023
92f65ed
Update examples.
komadori Apr 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
* Low poly fox [by PixelMannen](https://opengameart.org/content/fox-and-shiba) (CC0 1.0 Universal)
* Rigging and animation [by @tomkranis on Sketchfab](https://sketchfab.com/models/371dea88d7e04a76af5763f2a36866bc) ([CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/))
* FiraMono by The Mozilla Foundation and Telefonica S.A (SIL Open Font License, Version 1.1: assets/fonts/FiraMono-LICENSE)
* Barycentric from [mk_bary_gltf](https://github.com/komadori/mk_bary_gltf) (MIT OR Apache-2.0)
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,16 @@ description = "Renders a rectangle, circle, and hexagon"
category = "2D Rendering"
wasm = true

[[example]]
name = "custom_gltf_vertex_attribute"
path = "examples/2d/custom_gltf_vertex_attribute.rs"

[package.metadata.example.custom_gltf_vertex_attribute]
name = "Custom glTF vertex attribute 2D"
description = "Renders a glTF mesh in 2D with a custom vertex attribute"
category = "2D Rendering"
wasm = true

[[example]]
name = "2d_gizmos"
path = "examples/2d/2d_gizmos.rs"
Expand Down
80 changes: 80 additions & 0 deletions assets/models/barycentric/barycentric.gltf
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"count": 4,
"componentType": 5126,
"type": "VEC3",
"min": [
-1.0,
-1.0,
0.0
],
"max": [
1.0,
1.0,
0.0
]
},
{
"bufferView": 0,
"byteOffset": 12,
"count": 4,
"componentType": 5126,
"type": "VEC4"
},
{
"bufferView": 0,
"byteOffset": 28,
"count": 4,
"componentType": 5126,
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 0,
"count": 6,
"componentType": 5123,
"type": "SCALAR"
}
],
"asset": {
"version": "2.0"
},
"buffers": [
{
"byteLength": 172,
"uri": "data:application/gltf-buffer;base64,AACAvwAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAgD8AAAAAAAAAAAAAgD8AAIC/AAAAAAAAAD8AAAA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIC/AACAPwAAAAAAAAA/AAAAPwAAAAAAAIA/AAAAAAAAAAAAAIA/AACAPwAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAQACAAIAAQADAA=="
}
],
"bufferViews": [
{
"buffer": 0,
"byteLength": 160,
"byteOffset": 0,
"byteStride": 40,
"target": 34962
},
{
"buffer": 0,
"byteLength": 12,
"byteOffset": 160,
"target": 34962
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"POSITION": 0,
"COLOR_0": 1,
"__BARYCENTRIC": 2
},
"indices": 3
}
]
}
]
}
36 changes: 36 additions & 0 deletions assets/shaders/custom_gltf_2d.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#import bevy_sprite::mesh2d_view_bindings
#import bevy_sprite::mesh2d_bindings
#import bevy_sprite::mesh2d_functions

struct Vertex {
@location(0) position: vec3<f32>,
@location(1) color: vec4<f32>,
@location(2) barycentric: vec3<f32>,
};

struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) barycentric: vec3<f32>,
};

@vertex
fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput;
out.clip_position = mesh2d_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
out.color = vertex.color;
out.barycentric = vertex.barycentric;
return out;
}

struct FragmentInput {
@location(0) color: vec4<f32>,
@location(1) barycentric: vec3<f32>,
};

@fragment
fn fragment(input: FragmentInput) -> @location(0) vec4<f32> {
let d = min(input.barycentric.x, min(input.barycentric.y, input.barycentric.z));
let t = 0.05 * (0.85 + sin(5.0 * globals.time));
return mix(vec4(1.0,1.0,1.0,1.0), input.color, smoothstep(t, t+0.01, d));
}
43 changes: 35 additions & 8 deletions crates/bevy_gltf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,55 @@ use bevy_animation::AnimationClip;
use bevy_utils::HashMap;

mod loader;
mod vertex_attributes;
pub use loader::*;

use bevy_app::prelude::*;
use bevy_asset::{AddAsset, Handle};
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
use bevy_pbr::StandardMaterial;
use bevy_reflect::{Reflect, TypeUuid};
use bevy_render::mesh::Mesh;
use bevy_render::{
mesh::{Mesh, MeshVertexAttribute},
renderer::RenderDevice,
texture::CompressedImageFormats,
};
use bevy_scene::Scene;

/// Adds support for glTF file loading to the app.
#[derive(Default)]
pub struct GltfPlugin;
pub struct GltfPlugin {
custom_vertex_attributes: HashMap<String, MeshVertexAttribute>,
}

impl GltfPlugin {
pub fn add_custom_vertex_attribute(
mut self,
name: &str,
attribute: MeshVertexAttribute,
) -> Self {
self.custom_vertex_attributes
.insert(name.to_string(), attribute);
self
}
}

impl Plugin for GltfPlugin {
fn build(&self, app: &mut App) {
app.init_asset_loader::<GltfLoader>()
.register_type::<GltfExtras>()
.add_asset::<Gltf>()
.add_asset::<GltfNode>()
.add_asset::<GltfPrimitive>()
.add_asset::<GltfMesh>();
let supported_compressed_formats = match app.world.get_resource::<RenderDevice>() {
Some(render_device) => CompressedImageFormats::from_features(render_device.features()),

None => CompressedImageFormats::all(),
};
app.add_asset_loader::<GltfLoader>(GltfLoader {
supported_compressed_formats,
custom_vertex_attributes: self.custom_vertex_attributes.clone(),
})
.register_type::<GltfExtras>()
.add_asset::<Gltf>()
.add_asset::<GltfNode>()
.add_asset::<GltfPrimitive>()
.add_asset::<GltfMesh>();
}
}

Expand Down
92 changes: 28 additions & 64 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bevy_asset::{
};
use bevy_core::Name;
use bevy_core_pipeline::prelude::Camera3dBundle;
use bevy_ecs::{entity::Entity, prelude::FromWorld, world::World};
use bevy_ecs::{entity::Entity, world::World};
use bevy_hierarchy::{BuildWorldChildren, WorldChildBuilder};
use bevy_log::warn;
use bevy_math::{Mat4, Vec3};
Expand All @@ -17,12 +17,11 @@ use bevy_render::{
color::Color,
mesh::{
skinning::{SkinnedMesh, SkinnedMeshInverseBindposes},
Indices, Mesh, VertexAttributeValues,
Indices, Mesh, MeshVertexAttribute, VertexAttributeValues,
},
prelude::SpatialBundle,
primitives::Aabb,
render_resource::{AddressMode, Face, FilterMode, PrimitiveTopology, SamplerDescriptor},
renderer::RenderDevice,
texture::{CompressedImageFormats, Image, ImageSampler, ImageType, TextureError},
};
use bevy_scene::Scene;
Expand All @@ -32,13 +31,14 @@ use bevy_transform::components::Transform;

use bevy_utils::{HashMap, HashSet};
use gltf::{
mesh::Mode,
mesh::{util::ReadIndices, Mode},
texture::{MagFilter, MinFilter, WrappingMode},
Material, Node, Primitive,
};
use std::{collections::VecDeque, path::Path};
use thiserror::Error;

use crate::vertex_attributes::*;
use crate::{Gltf, GltfExtras, GltfNode};

/// An error that occurs when loading a glTF file.
Expand Down Expand Up @@ -68,7 +68,8 @@ pub enum GltfError {

/// Loads glTF files with all of their data as their corresponding bevy representations.
pub struct GltfLoader {
supported_compressed_formats: CompressedImageFormats,
pub(crate) supported_compressed_formats: CompressedImageFormats,
pub(crate) custom_vertex_attributes: HashMap<String, MeshVertexAttribute>,
}

impl AssetLoader for GltfLoader {
Expand All @@ -77,34 +78,19 @@ impl AssetLoader for GltfLoader {
bytes: &'a [u8],
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<()>> {
Box::pin(async move {
Ok(load_gltf(bytes, load_context, self.supported_compressed_formats).await?)
})
Box::pin(async move { Ok(load_gltf(bytes, load_context, self).await?) })
}

fn extensions(&self) -> &[&str] {
&["gltf", "glb"]
}
}

impl FromWorld for GltfLoader {
fn from_world(world: &mut World) -> Self {
let supported_compressed_formats = match world.get_resource::<RenderDevice>() {
Some(render_device) => CompressedImageFormats::from_features(render_device.features()),

None => CompressedImageFormats::all(),
};
Self {
supported_compressed_formats,
}
}
}

/// Loads an entire glTF file.
async fn load_gltf<'a, 'b>(
bytes: &'a [u8],
load_context: &'a mut LoadContext<'b>,
supported_compressed_formats: CompressedImageFormats,
loader: &GltfLoader,
) -> Result<(), GltfError> {
let gltf = gltf::Gltf::from_slice(bytes)?;
let buffer_data = load_buffers(&gltf, load_context, load_context.path()).await?;
Expand Down Expand Up @@ -233,53 +219,31 @@ async fn load_gltf<'a, 'b>(
let mut primitives = vec![];
for primitive in mesh.primitives() {
let primitive_label = primitive_label(&mesh, &primitive);
let reader = primitive.reader(|buffer| Some(&buffer_data[buffer.index()]));
let primitive_topology = get_primitive_topology(primitive.mode())?;

let mut mesh = Mesh::new(primitive_topology);

if let Some(vertex_attribute) = reader
.read_positions()
.map(|v| VertexAttributeValues::Float32x3(v.collect()))
{
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertex_attribute);
}

if let Some(vertex_attribute) = reader
.read_normals()
.map(|v| VertexAttributeValues::Float32x3(v.collect()))
{
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vertex_attribute);
}

if let Some(vertex_attribute) = reader
.read_tex_coords(0)
.map(|v| VertexAttributeValues::Float32x2(v.into_f32().collect()))
{
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vertex_attribute);
}

if let Some(vertex_attribute) = reader
.read_colors(0)
.map(|v| VertexAttributeValues::Float32x4(v.into_rgba_f32().collect()))
{
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute);
}

if let Some(iter) = reader.read_joints(0) {
let vertex_attribute = VertexAttributeValues::Uint16x4(iter.into_u16().collect());
mesh.insert_attribute(Mesh::ATTRIBUTE_JOINT_INDEX, vertex_attribute);
}

if let Some(vertex_attribute) = reader
.read_weights(0)
.map(|v| VertexAttributeValues::Float32x4(v.into_f32().collect()))
{
mesh.insert_attribute(Mesh::ATTRIBUTE_JOINT_WEIGHT, vertex_attribute);
// Read vertex attributes
for (semantic, accessor) in primitive.attributes() {
match convert_attribute(
semantic,
accessor,
&buffer_data,
&loader.custom_vertex_attributes,
) {
Ok((attribute, values)) => mesh.insert_attribute(attribute, values),
Err(err) => warn!("{}", err),
}
}

// Read vertex indices
let reader = primitive.reader(|buffer| Some(buffer_data[buffer.index()].as_slice()));
if let Some(indices) = reader.read_indices() {
mesh.set_indices(Some(Indices::U32(indices.into_u32().collect())));
mesh.set_indices(Some(match indices {
ReadIndices::U8(is) => Indices::U16(is.map(|x| x as u16).collect()),
ReadIndices::U16(is) => Indices::U16(is.collect()),
ReadIndices::U32(is) => Indices::U32(is.collect()),
}));
};

if mesh.attribute(Mesh::ATTRIBUTE_NORMAL).is_none()
Expand Down Expand Up @@ -403,7 +367,7 @@ async fn load_gltf<'a, 'b>(
&buffer_data,
&linear_textures,
load_context,
supported_compressed_formats,
loader.supported_compressed_formats,
)
.await?;
load_context.set_labeled_asset(&label, LoadedAsset::new(texture));
Expand All @@ -422,7 +386,7 @@ async fn load_gltf<'a, 'b>(
buffer_data,
linear_textures,
load_context,
supported_compressed_formats,
loader.supported_compressed_formats,
)
.await
});
Expand Down
Loading