wander spawner

This commit is contained in:
Carter Anderson 2019-12-03 00:30:30 -08:00
parent 21ce87ba45
commit 816d0c9bdd
8 changed files with 279 additions and 73 deletions

View file

@ -13,4 +13,5 @@ winit = "0.20.0-alpha4"
glsl-to-spirv = "0.1"
zerocopy = "0.2"
log = "0.4"
env_logger = "0.7"
env_logger = "0.7"
rand = "0.7.2"

View file

@ -1,28 +1,149 @@
use bevy::*;
use bevy::{render::*, asset::{Asset, AssetStorage}, math};
use bevy::{render::*, asset::{Asset, AssetStorage, Handle}, math, Schedulable};
use rand::{rngs::StdRng, Rng, SeedableRng, random};
// fn build_move_system() -> Box<dyn Scheduleable> {
// SystemBuilder::new("MoveSystem")
// .with_query(<>)
// }
fn build_wander_system(world: &mut World) -> Box<dyn Schedulable> {
let mut rng = StdRng::from_entropy();
SystemBuilder::new("Wander")
.read_resource::<Time>()
.with_query(<(
Read<Person>,
Read<Translation>,
Write<Wander>,
Write<NavigationPoint>,
)>::query())
.build(move |_, world, time , person_query| {
for (_, translation, mut wander, mut navigation_point) in person_query.iter(world) {
wander.elapsed += time.delta_seconds;
if wander.elapsed >= wander.duration {
let direction = math::vec3(
rng.gen_range(-1.0, 1.0),
rng.gen_range(-1.0, 1.0),
rng.gen_range(0.0, 0.001),
).normalize();
let distance = rng.gen_range(wander.distance_bounds.x, wander.distance_bounds.y);
navigation_point.target = translation.vector + direction * distance;
wander.elapsed = 0.0;
wander.duration = rng.gen_range(wander.duration_bounds.x, wander.duration_bounds.y);
}
}
})
}
fn build_navigate_system(world: &mut World) -> Box<dyn Schedulable> {
SystemBuilder::new("Navigate")
.with_query(<(
Read<Person>,
Write<Translation>,
Write<Velocity>,
Write<NavigationPoint>,
)>::query())
.build(move |_, world, _, person_query| {
for (_, mut translation, mut velocity, mut navigation_point) in person_query.iter(world) {
let distance = navigation_point.target - translation.vector;
if math::length(&distance) > 0.01 {
let direction = distance.normalize();
velocity.value = direction * 2.0;
} else {
velocity.value = math::vec3(0.0, 0.0, 0.0);
}
}
})
}
fn build_move_system(world: &mut World) -> Box<dyn Schedulable> {
SystemBuilder::new("Move")
.read_resource::<Time>()
.with_query(<(
Write<Translation>,
Read<Velocity>,
)>::query())
.build(move |_, world, time , person_query| {
for (mut translation, velocity) in person_query.iter(world) {
translation.vector += velocity.value * time.delta_seconds;
}
})
}
fn build_print_status_system(world: &mut World) -> Box<dyn Schedulable> {
let mut elapsed = 0.0;
SystemBuilder::new("PrintStatus")
.read_resource::<Time>()
.with_query(<(
Read<Person>,
)>::query())
.build(move |_, world, time , person_query| {
elapsed += time.delta_seconds;
if elapsed > 1.0 {
println!("fps: {}", if time.delta_seconds == 0.0 { 0.0 } else { 1.0 / time.delta_seconds });
println!("peeps: {}", person_query.iter(world).count());
elapsed = 0.0;
}
})
}
fn build_spawner_system(world: &mut World) -> Box<dyn Schedulable> {
let mesh_handle = {
let mut mesh_storage = world.resources.get_mut::<AssetStorage<Mesh, MeshType>>().unwrap();
mesh_storage.get_named("cube").unwrap()
};
let mut elapsed = 0.0;
SystemBuilder::new("Spawner")
.read_resource::<Time>()
.with_query(<(
Read<Person>,
)>::query())
.build(move |command_buffer, world, time , person_query| {
elapsed += time.delta_seconds;
if elapsed > 0.5 {
for i in 0..20 {
spawn_person(command_buffer, mesh_handle.clone());
}
elapsed = 0.0;
}
})
}
struct Person {
}
struct Velocity {
pub value: math::Vec3,
}
struct NavigationPoint {
pub target: math::Vec3,
}
struct Wander {
pub duration_bounds: math::Vec2,
pub distance_bounds: math::Vec2,
pub duration: f32,
pub elapsed: f32,
}
fn main() {
let universe = Universe::new();
let mut world = universe.create_world();
let mut scheduler = SystemScheduler::<ApplicationStage>::new();
let transform_system_bundle = transform_system_bundle::build(&mut world);
scheduler.add_systems(ApplicationStage::Update, transform_system_bundle);
// Create a query which finds all `Position` and `Velocity` components
// let mut query = Read::<Transform>::query();
let cube = Mesh::load(MeshType::Cube);
let plane = Mesh::load(MeshType::Plane{ size: 10 });
let plane = Mesh::load(MeshType::Plane{ size: 25 });
let mut mesh_storage = AssetStorage::<Mesh, MeshType>::new();
// this currently breaks because Arcs cant be modified after they are cloned :(
let mesh_handle = mesh_storage.add(cube);
let plane_handle = mesh_storage.add(plane);
let mesh_handle = mesh_storage.add(cube, "cube");
let plane_handle = mesh_storage.add(plane, "plane");
world.resources.insert(mesh_storage);
let transform_system_bundle = transform_system_bundle::build(&mut world);
scheduler.add_systems(ApplicationStage::Update, transform_system_bundle);
scheduler.add_system(ApplicationStage::Update, build_wander_system(&mut world));
scheduler.add_system(ApplicationStage::Update, build_navigate_system(&mut world));
scheduler.add_system(ApplicationStage::Update, build_move_system(&mut world));
scheduler.add_system(ApplicationStage::Update, build_spawner_system(&mut world));
scheduler.add_system(ApplicationStage::Update, build_print_status_system(&mut world));
world.insert((), vec![
// plane
(
@ -31,20 +152,8 @@ fn main() {
LocalToWorld::identity(),
Translation::new(0.0, 0.0, 0.0)
),
// cubes
(
Material::new(math::vec4(0.1, 0.1, 0.6, 1.0)),
mesh_handle.clone(),
LocalToWorld::identity(),
Translation::new(1.5, 0.0, 1.0)
),
(
Material::new(math::vec4(0.6, 0.1, 0.1, 1.0)),
mesh_handle,
LocalToWorld::identity(),
Translation::new(-1.5, 0.0, 1.0)
),
]);
world.insert((), vec![
// lights
(
@ -80,6 +189,7 @@ fn main() {
Translation::new(-5.0, 7.0, 10.0)
),
]);
world.insert((), vec![
// camera
(
@ -90,7 +200,7 @@ fn main() {
aspect_ratio: 1.0,
}),
LocalToWorld(math::look_at_rh(
&math::vec3(3.0, -10.0, 6.0),
&math::vec3(6.0, -40.0, 20.0),
&math::vec3(0.0, 0.0, 0.0),
&math::vec3(0.0, 0.0, 1.0),)),
// Translation::new(0.0, 0.0, 0.0),
@ -98,4 +208,29 @@ fn main() {
]);
Application::run(universe, world, scheduler);
}
fn spawn_person(command_buffer: &mut CommandBuffer, mesh_handle: Handle<Mesh>) {
command_buffer.insert((), vec![
(
Person{},
Wander {
duration_bounds: math::vec2(3.0, 10.0),
distance_bounds: math::vec2(-50.0, 50.0),
elapsed: 0.0,
duration: 0.0,
},
NavigationPoint {
target: math::vec3(0.0, 0.0, 0.0),
},
Velocity {
value: math::vec3(0.0, 0.0, 0.0),
},
Material::new(math::vec4(0.5, 0.3, 0.3, 1.0) * random::<f32>()),
mesh_handle.clone(),
LocalToWorld::identity(),
Translation::new(0.0, 0.0, 1.0)
),
]);
}

View file

@ -9,11 +9,12 @@ use zerocopy::AsBytes;
use legion::prelude::*;
use std::sync::Arc;
use std::rc::Rc;
use std::mem;
use wgpu::{Surface, Device, Queue, SwapChain, SwapChainDescriptor};
use crate::{vertex::*, render::*, math, LocalToWorld, ApplicationStage};
use crate::{vertex::*, render::*, math, LocalToWorld, ApplicationStage, Time};
pub struct Application
{
@ -37,13 +38,13 @@ impl Application {
(Self::MAX_LIGHTS * mem::size_of::<LightRaw>()) as wgpu::BufferAddress;
let local_bind_group_layout =
self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
Rc::new(self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: &[wgpu::BindGroupLayoutBinding {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
}],
});
}));
let light_uniform_buffer = Arc::new(UniformBuffer {
buffer: self.device.create_buffer(&wgpu::BufferDescriptor {
@ -55,29 +56,6 @@ impl Application {
size: light_uniform_size,
});
let mut materials = <Write<Material>>::query();
for mut material in materials.iter(&mut self.world) {
let entity_uniform_size = mem::size_of::<MaterialUniforms>() as wgpu::BufferAddress;
let uniform_buf = self.device.create_buffer(&wgpu::BufferDescriptor {
size: entity_uniform_size,
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
});
let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &local_bind_group_layout,
bindings: &[wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::Buffer {
buffer: &uniform_buf,
range: 0 .. entity_uniform_size,
},
}],
});
material.bind_group = Some(bind_group);
material.uniform_buf = Some(uniform_buf);
}
let light_count = <Read<Light>>::query().iter(&mut self.world).count();
let forward_uniforms = ForwardUniforms {
proj: math::Mat4::identity().into(),
@ -103,12 +81,25 @@ impl Application {
],
};
let shadow_pass = ShadowPass::new(&mut self.device, &mut self.world, light_uniform_buffer.clone(), vb_desc.clone(), &local_bind_group_layout, Self::MAX_LIGHTS as u32);
let shadow_pass = ShadowPass::new(&mut self.device, &mut self.world, light_uniform_buffer.clone(), vb_desc.clone(), local_bind_group_layout.clone(), Self::MAX_LIGHTS as u32);
let forward_pass = ForwardPass::new(&mut self.device, forward_uniforms, light_uniform_buffer.clone(), &shadow_pass, vb_desc, &local_bind_group_layout, &self.swap_chain_descriptor);
self.render_passes.push(Box::new(shadow_pass));
self.render_passes.push(Box::new(forward_pass));
}
fn update(&mut self) {
{
let mut time = self.world.resources.get_mut::<Time>().unwrap();
time.start();
}
self.scheduler.execute(&mut self.world);
self.render();
{
let mut time = self.world.resources.get_mut::<Time>().unwrap();
time.stop();
}
}
fn resize(&mut self, width: u32, height: u32)
{
self.swap_chain_descriptor.width = width;
@ -137,7 +128,7 @@ impl Application {
self.queue.submit(&[command_buffer]);
}
fn update(&mut self, _: WindowEvent)
fn handle_event(&mut self, _: WindowEvent)
{
}
@ -150,7 +141,7 @@ impl Application {
let mut encoder =
self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
let mut entities = <(Read<Material>, Read<LocalToWorld>)>::query();
let mut entities = <(Write<Material>, Read<LocalToWorld>)>::query();
let entities_count = entities.iter(&mut self.world).count();
let size = mem::size_of::<MaterialUniforms>();
let temp_buf_data = self.device
@ -174,7 +165,12 @@ impl Application {
}
let temp_buf = temp_buf_data.finish();
for pass in self.render_passes.iter_mut() {
pass.render(&mut self.device, &mut frame, &mut encoder, &mut self.world);
}
// TODO: this should happen before rendering
for (i, (entity, _)) in entities.iter(&mut self.world).enumerate() {
encoder.copy_buffer_to_buffer(
&temp_buf,
@ -185,16 +181,12 @@ impl Application {
);
}
for pass in self.render_passes.iter_mut() {
pass.render(&mut self.device, &mut frame, &mut encoder, &mut self.world);
}
let command_buffer = encoder.finish();
self.queue.submit(&[command_buffer]);
}
#[allow(dead_code)]
pub fn run(universe: Universe, world: World, system_scheduler: SystemScheduler<ApplicationStage>) {
pub fn run(universe: Universe, mut world: World, system_scheduler: SystemScheduler<ApplicationStage>) {
env_logger::init();
let event_loop = EventLoop::new();
log::info!("Initializing the window...");
@ -217,6 +209,7 @@ impl Application {
let (window, hidpi_factor, size, surface) = {
let window = winit::window::Window::new(&event_loop).unwrap();
window.set_title("bevy");
window.set_inner_size((1920, 1080).into());
let hidpi_factor = window.hidpi_factor();
let size = window.inner_size().to_physical(hidpi_factor);
let surface = wgpu::Surface::create(&window);
@ -231,7 +224,9 @@ impl Application {
present_mode: wgpu::PresentMode::Vsync,
};
let swap_chain = device.create_swap_chain(&surface, &swap_chain_descriptor);
world.resources.insert(Time::new());
log::info!("Initializing the example...");
let mut app = Application {
universe,
@ -280,12 +275,11 @@ impl Application {
*control_flow = ControlFlow::Exit;
}
_ => {
app.update(event);
app.handle_event(event);
}
},
event::Event::EventsCleared => {
app.scheduler.execute(&mut app.world);
app.render();
app.update();
}
_ => (),
}

View file

@ -1,4 +1,4 @@
use std::{sync::{Arc, RwLock}, marker::PhantomData, ops::Drop};
use std::{sync::{Arc, RwLock}, marker::PhantomData, ops::Drop, collections::HashMap};
pub struct Handle<T>
{
@ -35,6 +35,7 @@ pub trait Asset<D> {
pub struct AssetStorage<T, D> where T: Asset<D> {
assets: Vec<Option<T>>,
free_indices: Arc<RwLock<Vec<usize>>>,
names: HashMap<String, Arc<RwLock<usize>>>,
marker: PhantomData<D>,
}
@ -43,24 +44,43 @@ impl<T, D> AssetStorage<T, D> where T: Asset<D> {
AssetStorage {
assets: Vec::new(),
free_indices: Arc::new(RwLock::new(Vec::new())),
names: HashMap::new(),
marker: PhantomData,
}
}
pub fn add(&mut self, asset: T) -> Handle<T> {
pub fn get_named(&self, name: &str) -> Option<Handle<T>> {
match self.names.get(name) {
Some(id) => {
Some(Handle {
id: id.clone(),
marker: PhantomData,
free_indices: self.free_indices.clone()
})
},
None => None,
}
}
pub fn add(&mut self, asset: T, name: &str) -> Handle<T> {
match self.free_indices.write().unwrap().pop() {
Some(id) => {
self.assets[id as usize] = Some(asset);
let handle = Arc::new(RwLock::new(id));
self.names.insert(name.to_string(), handle.clone());
Handle {
id: Arc::new(RwLock::new(id)),
id: handle,
marker: PhantomData,
free_indices: self.free_indices.clone()
}
},
None => {
self.assets.push(Some(asset));
let id = self.assets.len() - 1;
let handle = Arc::new(RwLock::new(id));
self.names.insert(name.to_string(), handle.clone());
Handle {
id: Arc::new(RwLock::new(self.assets.len() - 1)),
id: handle,
marker: PhantomData,
free_indices: self.free_indices.clone()
}

View file

@ -1,5 +1,8 @@
use legion::schedule::Stage;
pub mod time;
pub use time::Time;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ApplicationStage {
Update,

26
src/core/time.rs Normal file
View file

@ -0,0 +1,26 @@
use std::time::{Duration, Instant};
pub struct Time {
pub delta: Duration,
pub instant: Instant,
pub delta_seconds: f32
}
impl Time {
pub fn new() -> Time {
Time {
delta: Duration::from_secs(0),
instant: Instant::now(),
delta_seconds: 0.0,
}
}
pub fn start(&mut self) {
self.instant = Instant::now();
}
pub fn stop(&mut self) {
self.delta = Instant::now() - self.instant;
self.delta_seconds = self.delta.as_secs() as f32 + (self.delta.subsec_nanos() as f32 / 1.0e9);
}
}

View file

@ -2,7 +2,7 @@ use crate::{render::*, asset::*, LocalToWorld};
use wgpu::{BindGroupLayout, Buffer, CommandEncoder, Device, VertexBufferDescriptor, SwapChainOutput, SwapChainDescriptor};
use legion::prelude::*;
use zerocopy::AsBytes;
use std::{mem, sync::Arc};
use std::{mem, sync::Arc, rc::Rc};
pub struct ShadowPass {
pub pipeline: wgpu::RenderPipeline,
@ -11,6 +11,7 @@ pub struct ShadowPass {
pub shadow_texture: wgpu::Texture,
pub shadow_view: wgpu::TextureView,
pub shadow_sampler: wgpu::Sampler,
pub local_bind_group_layout: Rc<BindGroupLayout>,
pub light_uniform_buffer: Arc::<UniformBuffer>,
pub lights_are_dirty: bool,
}
@ -47,6 +48,30 @@ impl Pass for ShadowPass {
);
}
for mut material in <Write<Material>>::query().iter(world) {
if let None = material.bind_group {
let entity_uniform_size = mem::size_of::<MaterialUniforms>() as wgpu::BufferAddress;
let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
size: entity_uniform_size,
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.local_bind_group_layout,
bindings: &[wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::Buffer {
buffer: &uniform_buf,
range: 0 .. entity_uniform_size,
},
}],
});
material.bind_group = Some(bind_group);
material.uniform_buf = Some(uniform_buf);
}
}
for (i, (mut light, _)) in <(Write<Light>, Read<LocalToWorld>)>::query().iter(world).enumerate() {
if let None = light.target_view {
light.target_view = Some(self.shadow_texture.create_view(&wgpu::TextureViewDescriptor {
@ -114,7 +139,7 @@ impl ShadowPass {
depth: 1,
};
pub fn new(device: &Device, _: &World, light_uniform_buffer: Arc::<UniformBuffer>, vertex_buffer_descriptor: VertexBufferDescriptor, local_bind_group_layout: &BindGroupLayout, max_lights: u32) -> ShadowPass {
pub fn new(device: &Device, _: &World, light_uniform_buffer: Arc::<UniformBuffer>, vertex_buffer_descriptor: VertexBufferDescriptor, local_bind_group_layout: Rc<BindGroupLayout>, max_lights: u32) -> ShadowPass {
// Create pipeline layout
let bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -222,6 +247,7 @@ impl ShadowPass {
shadow_view,
shadow_sampler,
light_uniform_buffer,
local_bind_group_layout,
lights_are_dirty: true,
}
}

View file

@ -4,6 +4,7 @@
# run using one of these commands
# lld linker makes compiles faster
# rust backtrace gives you a nice backtrace on panics
# -Zshare-generics=y makes generics slightly faster for some reason
env RUSTFLAGS="-C link-arg=-fuse-ld=lld" cargo run --release
env RUSTFLAGS="-C link-arg=-fuse-ld=lld" RUST_BACKTRACE=1 cargo run --release
env RUSTFLAGS="-C link-arg=-fuse-ld=lld -Zshare-generics=y" RUST_BACKTRACE=1 cargo run --release
```