Add support for jointed (skinned) models.
This commit is contained in:
parent
5a2dc4a728
commit
0f4098714b
|
@ -33,3 +33,10 @@ bevy_ui = ["bevy/bevy_ui", "bevy/bevy_sprite", "bevy/bevy_text"]
|
|||
[[example]]
|
||||
name = "shapes"
|
||||
path = "examples/shapes.rs"
|
||||
|
||||
[[example]]
|
||||
name = "animated_fox"
|
||||
path = "examples/animated_fox.rs"
|
||||
required-features = [
|
||||
"bevy/animation", "bevy/bevy_gltf", "bevy/png", "bevy/bevy_scene"
|
||||
]
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,87 @@
|
|||
use std::f32::consts::PI;
|
||||
|
||||
use bevy::{prelude::*, window::close_on_esc};
|
||||
use bevy_mod_outline::{
|
||||
AutoGenerateOutlineNormalsPlugin, Outline, OutlineBundle, OutlinePlugin, OutlineStencil,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(OutlinePlugin)
|
||||
.add_plugin(AutoGenerateOutlineNormalsPlugin)
|
||||
.insert_resource(AmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 1.0,
|
||||
})
|
||||
.add_startup_system(setup)
|
||||
.add_system(setup_scene_once_loaded)
|
||||
.add_system(close_on_esc)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
// Insert a resource with the current animation
|
||||
commands.insert_resource::<Handle<AnimationClip>>(asset_server.load("Fox.glb#Animation0"));
|
||||
|
||||
// Camera
|
||||
commands.spawn_bundle(Camera3dBundle {
|
||||
transform: Transform::from_xyz(100.0, 100.0, 150.0)
|
||||
.looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Plane
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 500000.0 })),
|
||||
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Light
|
||||
commands.spawn_bundle(DirectionalLightBundle {
|
||||
transform: Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
|
||||
directional_light: DirectionalLight {
|
||||
shadows_enabled: true,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
});
|
||||
|
||||
// Fox
|
||||
commands.spawn_bundle(SceneBundle {
|
||||
scene: asset_server.load("Fox.glb#Scene0"),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
// Once the scene is loaded, start the animation and add an outline
|
||||
fn setup_scene_once_loaded(
|
||||
mut commands: Commands,
|
||||
animation: Res<Handle<AnimationClip>>,
|
||||
mut player: Query<&mut AnimationPlayer>,
|
||||
entities: Query<Entity, With<Handle<Mesh>>>,
|
||||
mut done: Local<bool>,
|
||||
) {
|
||||
if !*done {
|
||||
if let Ok(mut player) = player.get_single_mut() {
|
||||
player.play(animation.clone_weak()).repeat();
|
||||
for entity in entities.iter() {
|
||||
commands.entity(entity).insert_bundle(OutlineBundle {
|
||||
outline: Outline {
|
||||
visible: true,
|
||||
width: 3.0,
|
||||
colour: Color::RED,
|
||||
},
|
||||
stencil: OutlineStencil,
|
||||
});
|
||||
}
|
||||
*done = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,12 @@
|
|||
@group(1) @binding(0)
|
||||
var<uniform> mesh: Mesh;
|
||||
|
||||
#ifdef SKINNED
|
||||
@group(1) @binding(1)
|
||||
var<uniform> joint_matrices: SkinnedMesh;
|
||||
#import bevy_pbr::skinning
|
||||
#endif
|
||||
|
||||
fn model_origin_z(model: mat4x4<f32>, view_proj: mat4x4<f32>) -> f32 {
|
||||
var origin = model[3];
|
||||
var proj_zw = mat4x2<f32>(
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) normal: vec3<f32>,
|
||||
#ifdef SKINNED
|
||||
@location(2) joint_indexes: vec4<u32>,
|
||||
@location(3) joint_weights: vec4<f32>,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct OutlineViewUniform {
|
||||
|
@ -43,8 +47,13 @@ fn mat4to3(m: mat4x4<f32>) -> mat3x3<f32> {
|
|||
|
||||
@vertex
|
||||
fn vertex(vertex: VertexInput) -> @builtin(position) vec4<f32> {
|
||||
var clip_pos = view.view_proj * (mesh.model * vec4<f32>(vertex.position, 1.0));
|
||||
var clip_norm = mat4to3(view.view_proj) * (mat4to3(mesh.model) * vertex.normal);
|
||||
#ifdef SKINNED
|
||||
let model = skin_model(vertex.joint_indexes, vertex.joint_weights);
|
||||
#else
|
||||
let model = mesh.model;
|
||||
#endif
|
||||
var clip_pos = view.view_proj * (model * vec4<f32>(vertex.position, 1.0));
|
||||
var clip_norm = mat4to3(view.view_proj) * (mat4to3(model) * vertex.normal);
|
||||
var ndc_pos = clip_pos.xy / clip_pos.w;
|
||||
var ndc_delta = vstage.width * normalize(clip_norm.xy) * view_uniform.scale;
|
||||
return vec4<f32>(ndc_pos + ndc_delta, model_origin_z(mesh.model, view.view_proj), 1.0);
|
||||
|
|
|
@ -112,11 +112,25 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
|||
mesh_layout: &MeshVertexBufferLayout,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut targets = vec![];
|
||||
let mut bind_layouts = vec![
|
||||
self.mesh_pipeline.view_layout.clone(),
|
||||
self.mesh_pipeline.mesh_layout.clone(),
|
||||
];
|
||||
let mut bind_layouts = vec![self.mesh_pipeline.view_layout.clone()];
|
||||
let mut buffer_attrs = vec![Mesh::ATTRIBUTE_POSITION.at_shader_location(0)];
|
||||
let mut shader_defs = if cfg!(feature = "align16") {
|
||||
vec!["ALIGN_16".to_string()]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
bind_layouts.push(
|
||||
if mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
|
||||
&& mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
|
||||
{
|
||||
shader_defs.push("SKINNED".to_string());
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(2));
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(3));
|
||||
self.mesh_pipeline.skinned_mesh_layout.clone()
|
||||
} else {
|
||||
self.mesh_pipeline.mesh_layout.clone()
|
||||
},
|
||||
);
|
||||
let shader;
|
||||
match pass_type {
|
||||
PassType::Stencil => {
|
||||
|
@ -146,11 +160,6 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
|||
);
|
||||
}
|
||||
}
|
||||
let shader_defs = if cfg!(feature = "align16") {
|
||||
vec!["ALIGN_16".to_string()]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let buffers = vec![mesh_layout.get_layout(&buffer_attrs)?];
|
||||
Ok(RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
|
|
|
@ -2,11 +2,20 @@
|
|||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
#ifdef SKINNED
|
||||
@location(2) joint_indexes: vec4<u32>,
|
||||
@location(3) joint_weights: vec4<f32>,
|
||||
#endif
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vertex(vertex: VertexInput) -> @builtin(position) vec4<f32> {
|
||||
var clip_pos = view.view_proj * (mesh.model * vec4<f32>(vertex.position, 1.0));
|
||||
#ifdef SKINNED
|
||||
let model = skin_model(vertex.joint_indexes, vertex.joint_weights);
|
||||
#else
|
||||
let model = mesh.model;
|
||||
#endif
|
||||
var clip_pos = view.view_proj * (model * vec4<f32>(vertex.position, 1.0));
|
||||
var ndc_pos = clip_pos.xy / clip_pos.w;
|
||||
return vec4<f32>(ndc_pos, model_origin_z(mesh.model, view.view_proj), 1.0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue