diff --git a/CHANGELOG.md b/CHANGELOG.md index 9282cbb..0cc2a18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,11 @@ The format is based on [Keep a Changelog][kc], and this project adheres to ## [Unreleased] -This version introduces a lots of break changes and significant performance improvement by integrating with [rayon](https://github.com/rayon-rs/rayon). And the error handle mechanism has been refined with [failure](https://github.com/rust-lang-nursery/failure). +## [0.4.0] - 2018-04-03 + +* This version introduces a lots of break changes and significant performance improvement by integrating with [rayon](https://github.com/rayon-rs/rayon). + +* The error handle mechanism has been refined with [failure](https://github.com/rust-lang-nursery/failure). ## [0.3.0] - 2018-02-28 @@ -64,4 +68,5 @@ This version introduces a lots of break changes and significant performance impr [0.2.0]: https://github.com/shawnscode/crayon/compare/v0.1.0...v0.2.0 [0.2.1]: https://github.com/shawnscode/crayon/compare/v0.1.0...v0.2.1 [0.3.0]: https://github.com/shawnscode/crayon/compare/v0.2.1...v0.3.0 -[Unreleased]: https://github.com/shawnscode/crayon/compare/v0.3.0...HEAD \ No newline at end of file +[0.4.0]: https://github.com/shawnscode/crayon/compare/v0.3.0...v0.4.0 +[Unreleased]: https://github.com/shawnscode/crayon/compare/v0.4.0...HEAD \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index b4bcb41..0b985df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,13 +20,12 @@ codecov = { repository = "shawnscode/crayon", branch = "master", service = "gith members = [ "crayon-examples", "modules/imgui", "modules/3d" ] [dependencies] -libc = "0.2.36" -gl = "0.9.0" -glutin = "0.12.0" -cgmath = "0.16.0" +gl = "0.10.0" +glutin = "0.13.1" +cgmath = "0.16.1" failure = "0.1.1" rayon = "1.0.1" -zip = "0.3.0" +zip = "0.3.1" [dev-dependencies] rand = "0.4.2" diff --git a/crayon-examples/src/imgui/mod.rs b/crayon-examples/src/imgui/mod.rs index b5e63f8..5a39f30 100644 --- a/crayon-examples/src/imgui/mod.rs +++ b/crayon-examples/src/imgui/mod.rs @@ -17,7 +17,7 @@ impl Window { let canvas = Canvas::new(ctx).unwrap(); let mut setup = SurfaceSetup::default(); - setup.set_clear(Color::white(), None, None); + setup.set_clear(math::Color::white(), None, None); setup.set_sequence(true); let surface = ctx.shared::().create_surface(setup)?; diff --git a/crayon-examples/src/input/mod.rs b/crayon-examples/src/input/mod.rs index eeb706f..ff7e24b 100644 --- a/crayon-examples/src/input/mod.rs +++ b/crayon-examples/src/input/mod.rs @@ -22,7 +22,7 @@ impl Window { let canvas = Canvas::new(ctx).unwrap(); let mut setup = SurfaceSetup::default(); - setup.set_clear(Color::white(), None, None); + setup.set_clear(math::Color::white(), None, None); setup.set_sequence(true); let surface = ctx.shared::().create_surface(setup)?; diff --git a/crayon-examples/src/mesh/mod.rs b/crayon-examples/src/mesh/mod.rs index 280cd3c..55a96b4 100644 --- a/crayon-examples/src/mesh/mod.rs +++ b/crayon-examples/src/mesh/mod.rs @@ -32,7 +32,7 @@ impl Window { let video = ctx.shared::().clone(); // Create scene. - let mut scene = Scene::new(ctx)?; + let mut scene = Scene::new(ctx, SceneSetup::default())?; let camera = { let camera = scene.create(); @@ -71,11 +71,16 @@ impl Window { } fn create_lits(scene: &mut Scene, video: &GraphicsSystemShared) -> Result<[Entity; 4]> { - let shader = factory::pipeline::color(scene)?; + let shader = factory::pipeline::color(&mut scene.resources)?; let mesh = factory::mesh::cube(video)?; let mut lits = [Entity::nil(); 4]; - let colors = [Color::red(), Color::blue(), Color::green(), Color::cyan()]; + let colors = [ + math::Color::red(), + math::Color::blue(), + math::Color::green(), + math::Color::cyan(), + ]; let positions = [ [1.0, 0.5, 0.0], [-1.0, 0.5, 0.0], @@ -104,10 +109,11 @@ impl Window { ent.set_position(positions[i]); } - let color: [f32; 4] = colors[i].into(); - let mat = scene.create_material(MaterialSetup::new(shader))?; + let color: [f32; 4] = colors[i].rgba(); + let mat = scene.resources.create_material(MaterialSetup::new(shader))?; + { - let mut m = scene.material_mut(mat).unwrap(); + let mut m = scene.resources.material_mut(mat).unwrap(); m.bind("u_Color", color)?; } @@ -136,24 +142,24 @@ impl Window { scene: &mut Scene, video: &GraphicsSystemShared, ) -> Result<(Entity, MaterialHandle)> { - let shader = factory::pipeline::phong(scene)?; + let shader = factory::pipeline::phong(&mut scene.resources)?; let mut setup = MeshSetup::default(); setup.location = Location::shared("/std/cornell_box.obj"); let mesh = video.create_mesh_from_file::(setup)?; - let mat_wall = scene.create_material(MaterialSetup::new(shader))?; + let mat_wall = scene.resources.create_material(MaterialSetup::new(shader))?; { - let m = scene.material_mut(mat_wall).unwrap(); + let m = scene.resources.material_mut(mat_wall).unwrap(); m.bind("u_Ambient", [1.0, 1.0, 1.0])?; m.bind("u_Diffuse", [1.0, 1.0, 1.0])?; m.bind("u_Specular", [0.0, 0.0, 0.0])?; m.bind("u_Shininess", 0.0)?; } - let mat_block = scene.create_material(MaterialSetup::new(shader))?; + let mat_block = scene.resources.create_material(MaterialSetup::new(shader))?; { - let m = scene.material_mut(mat_block).unwrap(); + let m = scene.resources.material_mut(mat_block).unwrap(); m.bind("u_Ambient", [1.0, 1.0, 1.0])?; m.bind("u_Diffuse", [1.0, 1.0, 1.0])?; m.bind("u_Specular", [1.0, 1.0, 1.0])?; @@ -238,7 +244,7 @@ impl Application for Window { } { - let m = self.scene.material_mut(self.material).unwrap(); + let m = self.scene.resources.material_mut(self.material).unwrap(); m.bind("u_Ambient", *ambient)?; m.bind("u_Diffuse", *diffuse)?; m.bind("u_Specular", *specular)?; @@ -251,6 +257,7 @@ impl Application for Window { } else { self.scene.draw_shadow(None)?; } + Ok(()) } diff --git a/crayon-examples/src/render_target/mod.rs b/crayon-examples/src/render_target/mod.rs index 49a9f05..f2dbd38 100644 --- a/crayon-examples/src/render_target/mod.rs +++ b/crayon-examples/src/render_target/mod.rs @@ -59,7 +59,7 @@ impl Window { let mut setup = SurfaceSetup::default(); setup.set_attachments(&[rendered_texture], None)?; setup.set_order(0); - setup.set_clear(Color::gray(), None, None); + setup.set_clear(math::Color::gray(), None, None); let surface = video.create_surface(setup)?; // Create shader state. @@ -136,7 +136,7 @@ impl Application for Window { fn on_update(&mut self, _: &Context) -> Result<()> { { - let mut dc = DrawCall::new(self.pass.shader, self.pass.mesh); + let dc = DrawCall::new(self.pass.shader, self.pass.mesh); let cmd = dc.build_from(0, 3)?; self.video.submit(self.pass.surface, 0u64, cmd)?; } diff --git a/modules/3d/src/assets/factory.rs b/modules/3d/src/assets/factory.rs index 559beef..e54cf1a 100644 --- a/modules/3d/src/assets/factory.rs +++ b/modules/3d/src/assets/factory.rs @@ -3,7 +3,7 @@ pub mod pipeline { use crayon::resource::prelude::*; use self::UniformVariableType as UVT; - use scene::Scene; + use resources::Resources; use assets::pipeline::{PipelineHandle, PipelineSetup}; use errors::*; @@ -12,9 +12,9 @@ pub mod pipeline { pub const UNDEFINED: &str = "__Core/Scene/Shader/UNDEFINED"; pub const COLOR: &str = "__Core/Scene/Shader/COLOR"; - pub fn pbr(scene: &mut Scene) -> Result { + pub fn pbr(resources: &mut Resources) -> Result { let location = Location::shared(PBR); - if let Some(pipeline) = scene.lookup_pipeline(location) { + if let Some(pipeline) = resources.lookup_pipeline(location) { return Ok(pipeline); } @@ -44,12 +44,12 @@ pub mod pipeline { setup.params.uniforms = uniforms; let pipeline_setup = PipelineSetup::new(setup); - scene.create_pipeline(pipeline_setup) + resources.create_pipeline(pipeline_setup) } - pub fn phong(scene: &mut Scene) -> Result { + pub fn phong(resources: &mut Resources) -> Result { let location = Location::shared(PHONG); - if let Some(pipeline) = scene.lookup_pipeline(location) { + if let Some(pipeline) = resources.lookup_pipeline(location) { return Ok(pipeline); } @@ -100,12 +100,12 @@ pub mod pipeline { setup.params.uniforms = uniforms; let pipeline_setup = PipelineSetup::new(setup); - scene.create_pipeline(pipeline_setup) + resources.create_pipeline(pipeline_setup) } - pub fn color(scene: &mut Scene) -> Result { + pub fn color(resources: &mut Resources) -> Result { let location = Location::shared(COLOR); - if let Some(pipeline) = scene.lookup_pipeline(location) { + if let Some(pipeline) = resources.lookup_pipeline(location) { return Ok(pipeline); } @@ -133,12 +133,12 @@ pub mod pipeline { setup.params.uniforms = uniforms; let pipeline_setup = PipelineSetup::new(setup); - scene.create_pipeline(pipeline_setup) + resources.create_pipeline(pipeline_setup) } - pub fn undefined(scene: &mut Scene) -> Result { + pub fn undefined(resources: &mut Resources) -> Result { let location = Location::shared(UNDEFINED); - if let Some(pipeline) = scene.lookup_pipeline(location) { + if let Some(pipeline) = resources.lookup_pipeline(location) { return Ok(pipeline); } @@ -165,7 +165,7 @@ pub mod pipeline { setup.params.uniforms = uniforms; let pipeline_setup = PipelineSetup::new(setup); - scene.create_pipeline(pipeline_setup) + resources.create_pipeline(pipeline_setup) } } diff --git a/modules/3d/src/components/light.rs b/modules/3d/src/components/light.rs index b402f29..80eb34b 100644 --- a/modules/3d/src/components/light.rs +++ b/modules/3d/src/components/light.rs @@ -1,5 +1,5 @@ +use crayon::math; use crayon::ecs::prelude::*; -use crayon::utils::Color; #[derive(Debug, Clone, Copy)] pub struct Light { @@ -8,7 +8,7 @@ pub struct Light { /// Is this light casting shadow. pub shadow_caster: bool, /// Color of the light. - pub color: Color, + pub color: math::Color, /// Brightness of the light source, in lumens. pub intensity: f32, /// Light source @@ -38,7 +38,7 @@ impl Default for Light { Light { enable: true, shadow_caster: false, - color: Color::white(), + color: math::Color::white(), intensity: 1.0, source: LitSource::Dir, } diff --git a/modules/3d/src/components/node.rs b/modules/3d/src/components/node.rs index 3d1e7cf..8b38809 100644 --- a/modules/3d/src/components/node.rs +++ b/modules/3d/src/components/node.rs @@ -56,38 +56,38 @@ impl Node { impl Node { /// Attachs a new child to parent transform, before existing children. - pub fn set_parent(arena: &mut T1, child: Entity, parent: T2) -> Result<()> + pub fn set_parent(nodes: &mut T1, child: Entity, parent: T2) -> Result<()> where T1: ArenaGetMut, T2: Into>, { - if arena.get(child).is_none() { + if nodes.get(child).is_none() { Err(Error::NonTransformFound) } else { unsafe { - Self::set_parent_unchecked(arena, child, parent); + Self::set_parent_unchecked(nodes, child, parent); Ok(()) } } } /// Attachs a new child to parent transform, before existing children. - pub unsafe fn set_parent_unchecked(arena: &mut T1, child: Entity, parent: T2) + pub unsafe fn set_parent_unchecked(nodes: &mut T1, child: Entity, parent: T2) where T1: ArenaGetMut, T2: Into>, { - Self::remove_from_parent_unchecked(arena, child); + Self::remove_from_parent_unchecked(nodes, child); let parent = parent.into(); if let Some(parent) = parent { if parent != child { let next_sib = { - let node = arena.get_unchecked_mut(parent); + let node = nodes.get_unchecked_mut(parent); ::std::mem::replace(&mut node.first_child, Some(child)) }; - let child = arena.get_unchecked_mut(child); + let child = nodes.get_unchecked_mut(child); child.parent = Some(parent); child.next_sib = next_sib; } @@ -95,27 +95,27 @@ impl Node { } /// Detach a transform from its parent and siblings. Children are not affected. - pub fn remove_from_parent(arena: &mut T1, handle: Entity) -> Result<()> + pub fn remove_from_parent(nodes: &mut T1, handle: Entity) -> Result<()> where T1: ArenaGetMut, { - if arena.get(handle).is_none() { + if nodes.get(handle).is_none() { Err(Error::NonTransformFound) } else { unsafe { - Self::remove_from_parent_unchecked(arena, handle); + Self::remove_from_parent_unchecked(nodes, handle); Ok(()) } } } /// Detach a transform from its parent and siblings without doing bounds checking. - pub unsafe fn remove_from_parent_unchecked(arena: &mut T1, handle: Entity) + pub unsafe fn remove_from_parent_unchecked(nodes: &mut T1, handle: Entity) where T1: ArenaGetMut, { let (parent, next_sib, prev_sib) = { - let node = arena.get_unchecked_mut(handle); + let node = nodes.get_unchecked_mut(handle); ( node.parent.take(), node.next_sib.take(), @@ -124,91 +124,91 @@ impl Node { }; if let Some(next_sib) = next_sib { - arena.get_unchecked_mut(next_sib).prev_sib = prev_sib; + nodes.get_unchecked_mut(next_sib).prev_sib = prev_sib; } if let Some(prev_sib) = prev_sib { - arena.get_unchecked_mut(prev_sib).next_sib = next_sib; + nodes.get_unchecked_mut(prev_sib).next_sib = next_sib; } else if let Some(parent) = parent { // Take this transform as the first child of parent if there is no previous sibling. - arena.get_unchecked_mut(parent).first_child = next_sib; + nodes.get_unchecked_mut(parent).first_child = next_sib; } } /// Returns an iterator of references to its ancestors. - pub fn ancestors(arena: &T1, handle: Entity) -> Ancestors + pub fn ancestors(nodes: &T1, handle: Entity) -> Ancestors where T1: ArenaGet, { Ancestors { - cursor: arena.get(handle).and_then(|v| v.parent), - arena: arena, + cursor: nodes.get(handle).and_then(|v| v.parent), + nodes: nodes, } } /// Returns an iterator of references to its ancestors. - pub fn ancestors_in_place(arena: T1, handle: Entity) -> AncestorsInPlace + pub fn ancestors_in_place(nodes: T1, handle: Entity) -> AncestorsInPlace where T1: ArenaGet, { AncestorsInPlace { - cursor: arena.get(handle).and_then(|v| v.parent), - arena: arena, + cursor: nodes.get(handle).and_then(|v| v.parent), + nodes: nodes, } } /// Returns an iterator of references to this transform's children. - pub fn children(arena: &T1, handle: Entity) -> Children + pub fn children(nodes: &T1, handle: Entity) -> Children where T1: ArenaGet, { Children { - cursor: arena.get(handle).and_then(|v| v.first_child), - arena: arena, + cursor: nodes.get(handle).and_then(|v| v.first_child), + nodes: nodes, } } /// Returns an iterator of references to this transform's children. - pub fn children_in_place(arena: T1, handle: Entity) -> ChildrenInPlace + pub fn children_in_place(nodes: T1, handle: Entity) -> ChildrenInPlace where T1: ArenaGet, { ChildrenInPlace { - cursor: arena.get(handle).and_then(|v| v.first_child), - arena: arena, + cursor: nodes.get(handle).and_then(|v| v.first_child), + nodes: nodes, } } /// Returns an iterator of references to this transform's descendants in tree order. - pub fn descendants(arena: &T1, handle: Entity) -> Descendants + pub fn descendants(nodes: &T1, handle: Entity) -> Descendants where T1: ArenaGet, { Descendants { - arena: arena, + nodes: nodes, root: handle, - cursor: arena.get(handle).and_then(|v| v.first_child), + cursor: nodes.get(handle).and_then(|v| v.first_child), } } /// Returns an iterator of references to this transform's descendants in tree order. - pub fn descendants_in_place(arena: T1, handle: Entity) -> DescendantsInPlace + pub fn descendants_in_place(nodes: T1, handle: Entity) -> DescendantsInPlace where T1: ArenaGet, { DescendantsInPlace { - cursor: arena.get(handle).and_then(|v| v.first_child), - arena: arena, + cursor: nodes.get(handle).and_then(|v| v.first_child), + nodes: nodes, root: handle, } } /// Return true if rhs is one of the ancestor of this `Node`. - pub fn is_ancestor(arena: &T1, lhs: Entity, rhs: Entity) -> bool + pub fn is_ancestor(nodes: &T1, lhs: Entity, rhs: Entity) -> bool where T1: ArenaGet, { - for v in Node::ancestors(arena, lhs) { + for v in Node::ancestors(nodes, lhs) { if v == rhs { return true; } @@ -220,16 +220,16 @@ impl Node { /// An iterator of references to its ancestors. pub struct Ancestors<'a> { - arena: &'a ArenaGet, + nodes: &'a ArenaGet, cursor: Option, } impl<'a> Ancestors<'a> { #[inline] - pub fn next(arena: &ArenaGet, mut cursor: &mut Option) -> Option { + pub fn next(nodes: &ArenaGet, mut cursor: &mut Option) -> Option { unsafe { if let Some(node) = *cursor { - let v = arena.get_unchecked(node); + let v = nodes.get_unchecked(node); return ::std::mem::replace(&mut cursor, v.parent); } @@ -242,7 +242,7 @@ impl<'a> Iterator for Ancestors<'a> { type Item = Entity; fn next(&mut self) -> Option { - Ancestors::next(self.arena, &mut self.cursor) + Ancestors::next(self.nodes, &mut self.cursor) } } @@ -251,7 +251,7 @@ pub struct AncestorsInPlace where T: ArenaGet, { - arena: T, + nodes: T, cursor: Option, } @@ -262,22 +262,22 @@ where type Item = Entity; fn next(&mut self) -> Option { - Ancestors::next(&self.arena, &mut self.cursor) + Ancestors::next(&self.nodes, &mut self.cursor) } } /// An iterator of references to its children. pub struct Children<'a> { - arena: &'a ArenaGet, + nodes: &'a ArenaGet, cursor: Option, } impl<'a> Children<'a> { #[inline] - pub fn next(arena: &ArenaGet, mut cursor: &mut Option) -> Option { + pub fn next(nodes: &ArenaGet, mut cursor: &mut Option) -> Option { unsafe { if let Some(node) = *cursor { - let v = arena.get_unchecked(node); + let v = nodes.get_unchecked(node); return ::std::mem::replace(&mut cursor, v.next_sib); } @@ -290,7 +290,7 @@ impl<'a> Iterator for Children<'a> { type Item = Entity; fn next(&mut self) -> Option { - Children::next(self.arena, &mut self.cursor) + Children::next(self.nodes, &mut self.cursor) } } @@ -299,7 +299,7 @@ pub struct ChildrenInPlace where T: ArenaGet, { - arena: T, + nodes: T, cursor: Option, } @@ -310,13 +310,13 @@ where type Item = Entity; fn next(&mut self) -> Option { - Children::next(&self.arena, &mut self.cursor) + Children::next(&self.nodes, &mut self.cursor) } } /// An iterator of references to its descendants, in tree order. pub struct Descendants<'a> { - arena: &'a ArenaGet, + nodes: &'a ArenaGet, root: Entity, cursor: Option, } @@ -324,13 +324,13 @@ pub struct Descendants<'a> { impl<'a> Descendants<'a> { #[inline] pub fn next( - arena: &ArenaGet, + nodes: &ArenaGet, root: Entity, mut cursor: &mut Option, ) -> Option { unsafe { if let Some(node) = *cursor { - let mut v = *arena.get_unchecked(node); + let mut v = *nodes.get_unchecked(node); // Deep first search when iterating children recursively. if v.first_child.is_some() { @@ -347,7 +347,7 @@ impl<'a> Descendants<'a> { break; } - v = *arena.get_unchecked(v.parent.unwrap()); + v = *nodes.get_unchecked(v.parent.unwrap()); if v.next_sib.is_some() { return ::std::mem::replace(&mut cursor, v.next_sib); } @@ -363,7 +363,7 @@ impl<'a> Iterator for Descendants<'a> { type Item = Entity; fn next(&mut self) -> Option { - Descendants::next(self.arena, self.root, &mut self.cursor) + Descendants::next(self.nodes, self.root, &mut self.cursor) } } @@ -372,7 +372,7 @@ pub struct DescendantsInPlace where T: ArenaGet, { - arena: T, + nodes: T, root: Entity, cursor: Option, } @@ -384,6 +384,6 @@ where type Item = Entity; fn next(&mut self) -> Option { - Descendants::next(&self.arena, self.root, &mut self.cursor) + Descendants::next(&self.nodes, self.root, &mut self.cursor) } } diff --git a/modules/3d/src/components/transform.rs b/modules/3d/src/components/transform.rs index 870a73d..76c5de3 100644 --- a/modules/3d/src/components/transform.rs +++ b/modules/3d/src/components/transform.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crayon::ecs::prelude::*; use crayon::math; use crayon::math::Transform as _Transform; @@ -27,18 +29,24 @@ impl Default for Transform { } impl Transform { - /// Get the scale component in local space. + #[inline] + pub fn matrix(&self) -> math::Matrix4 { + math::Matrix4::from(self.decomposed) + } + + /// Gets the scale component in local space. #[inline] pub fn scale(&self) -> f32 { self.decomposed.scale } - /// Set the scale component in local space. + /// Sets the scale component in local space. #[inline] pub fn set_scale(&mut self, scale: f32) { self.decomposed.scale = scale; } + /// Gets the position component in local space. #[inline] pub fn position(&self) -> math::Vector3 { self.decomposed.disp @@ -80,30 +88,97 @@ impl Transform { { self.decomposed.rot = rotate.into() * self.decomposed.rot; } + + #[inline] + pub fn transform_direction(&self, v: T) -> math::Vector3 + where + T: Into>, + { + self.decomposed.rot * v.into() + } + + pub fn up(&self) -> math::Vector3 { + self.transform_direction(math::Vector3::new(0.0, 1.0, 0.0)) + } + + pub fn forward(&self) -> math::Vector3 { + self.transform_direction(math::Vector3::new(0.0, 0.0, 1.0)) + } + + pub fn right(&self) -> math::Vector3 { + self.transform_direction(math::Vector3::new(1.0, 0.0, 0.0)) + } +} + +impl Transform { + pub fn world_transforms( + entities: Entities, + nodes: &T1, + transforms: &T2, + ) -> HashMap + where + T1: ArenaGet, + T2: ArenaGet, + { + let mut result = HashMap::new(); + + unsafe { + for v in entities.with_2::() { + if nodes.get_unchecked(v).is_root() { + let d = transforms.get_unchecked(v).decomposed; + result.insert(v, Transform { decomposed: d }); + Self::world_transforms_from(nodes, transforms, v, d, &mut result); + } + } + } + + result + } + + unsafe fn world_transforms_from( + nodes: &T1, + transforms: &T2, + ancestor: Entity, + decomposed: math::Decomposed, math::Quaternion>, + result: &mut HashMap, + ) where + T1: ArenaGet, + T2: ArenaGet, + { + for child in Node::children(nodes, ancestor) { + let d = decomposed.concat(&transforms.get_unchecked(child).decomposed); + result.insert(child, Transform { decomposed: d }); + Self::world_transforms_from(nodes, transforms, child, d, result); + } + } } impl Transform { /// Get the transform matrix from local space to world space. - pub fn world_matrix(tree: &T1, arena: &T2, handle: Entity) -> Result> + pub fn world_matrix( + nodes: &T1, + transforms: &T2, + handle: Entity, + ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - let decomposed = Transform::world_decomposed(tree, arena, handle)?; + let decomposed = Transform::world_decomposed(nodes, transforms, handle)?; Ok(math::Matrix4::from(decomposed)) } /// Get the transform matrix from world space to local space. pub fn inverse_world_matrix( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - let decomposed = Transform::world_decomposed(tree, arena, handle)?; + let decomposed = Transform::world_decomposed(nodes, transforms, handle)?; if let Some(inverse) = decomposed.inverse_transform() { Ok(math::Matrix4::from(inverse)) } else { @@ -113,15 +188,15 @@ impl Transform { /// Get the view matrix from world space to view space. pub fn world_view_matrix( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - let decomposed = Transform::world_decomposed(tree, arena, handle)?; + let decomposed = Transform::world_decomposed(nodes, transforms, handle)?; let it = math::Matrix4::from_translation(-decomposed.disp); let ir = math::Matrix4::from(decomposed.rot).transpose(); // M = ( T * R ) ^ -1 @@ -130,15 +205,15 @@ impl Transform { /// Gets the inverse view matrix which transform vector from view space to world space. pub fn inverse_world_view_matrix( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - let decomposed = Transform::world_decomposed(tree, arena, handle)?; + let decomposed = Transform::world_decomposed(nodes, transforms, handle)?; let t = math::Matrix4::from_translation(decomposed.disp); let r = math::Matrix4::from(decomposed.rot); Ok(t * r) @@ -146,8 +221,8 @@ impl Transform { /// Set position of `Transform` in world space. pub fn set_world_position( - tree: &T1, - arena: &mut T2, + nodes: &T1, + transforms: &mut T2, handle: Entity, disp: T3, ) -> Result<()> @@ -156,20 +231,20 @@ impl Transform { T2: ArenaGetMut, T3: Into>, { - if arena.get(handle).is_none() { + if transforms.get(handle).is_none() { return Err(Error::NonTransformFound); } unsafe { - Self::set_world_position_unchecked(tree, arena, handle, disp); + Self::set_world_position_unchecked(nodes, transforms, handle, disp); Ok(()) } } /// Set position of `Transform` in world space without doing bounds checking. pub unsafe fn set_world_position_unchecked( - tree: &T1, - arena: &mut T2, + nodes: &T1, + transforms: &mut T2, handle: Entity, disp: T3, ) where @@ -178,17 +253,17 @@ impl Transform { T3: Into>, { let disp = disp.into(); - if tree.get(handle).is_none() { - arena.get_unchecked_mut(handle).set_position(disp); + if nodes.get(handle).is_none() { + transforms.get_unchecked_mut(handle).set_position(disp); } else { let mut ancestors_disp = math::Vector3::new(0.0, 0.0, 0.0); - for v in Node::ancestors(tree, handle) { - if let Some(transform) = arena.get(v) { + for v in Node::ancestors(nodes, handle) { + if let Some(transform) = transforms.get(v) { ancestors_disp += transform.position(); } } - arena + transforms .get_unchecked_mut(handle) .set_position(disp - ancestors_disp); } @@ -196,35 +271,35 @@ impl Transform { /// Get position of `Transform` in world space. pub fn world_position( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - if arena.get(handle).is_none() { + if transforms.get(handle).is_none() { Err(Error::NonTransformFound) } else { - unsafe { Ok(Self::world_position_unchecked(tree, arena, handle)) } + unsafe { Ok(Self::world_position_unchecked(nodes, transforms, handle)) } } } /// Get position of `Transform` in world space without doing bounds checking. pub unsafe fn world_position_unchecked( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> math::Vector3 where T1: ArenaGet, T2: ArenaGet, { - let transform = arena.get_unchecked(handle); + let transform = transforms.get_unchecked(handle); let mut disp = transform.position(); - for v in Node::ancestors(tree, handle) { - if let Some(ancestor) = arena.get(v) { + for v in Node::ancestors(nodes, handle) { + if let Some(ancestor) = transforms.get(v) { disp += ancestor.position(); } } @@ -234,8 +309,8 @@ impl Transform { /// Set uniform scale of `Transform` in world space. pub fn set_world_scale( - tree: &T1, - arena: &mut T2, + nodes: &T1, + transforms: &mut T2, handle: Entity, scale: f32, ) -> Result<()> @@ -243,40 +318,40 @@ impl Transform { T1: ArenaGet, T2: ArenaGetMut, { - if arena.get(handle).is_none() { + if transforms.get(handle).is_none() { return Err(Error::NonTransformFound); } unsafe { - Self::set_world_scale_unchecked(tree, arena, handle, scale); + Self::set_world_scale_unchecked(nodes, transforms, handle, scale); Ok(()) } } /// Set uniform scale of `Transform` in world space withoud doing bounds checking. pub unsafe fn set_world_scale_unchecked( - tree: &T1, - arena: &mut T2, + nodes: &T1, + transforms: &mut T2, handle: Entity, scale: f32, ) where T1: ArenaGet, T2: ArenaGetMut, { - if tree.get(handle).is_none() { - arena.get_unchecked_mut(handle).set_scale(scale); + if nodes.get(handle).is_none() { + transforms.get_unchecked_mut(handle).set_scale(scale); } else { let mut ancestors_scale = 1.0; - for v in Node::ancestors(tree, handle) { - if let Some(transform) = arena.get(v) { + for v in Node::ancestors(nodes, handle) { + if let Some(transform) = transforms.get(v) { ancestors_scale *= transform.scale(); } } if ancestors_scale < ::std::f32::EPSILON { - arena.get_unchecked_mut(handle).set_scale(scale); + transforms.get_unchecked_mut(handle).set_scale(scale); } else { - arena + transforms .get_unchecked_mut(handle) .set_scale(scale / ancestors_scale); } @@ -284,28 +359,28 @@ impl Transform { } /// Get the scale of `Transform` in world space. - pub fn world_scale(tree: &T1, arena: &T2, handle: Entity) -> Result + pub fn world_scale(nodes: &T1, transforms: &T2, handle: Entity) -> Result where T1: ArenaGet, T2: ArenaGet, { - if arena.get(handle).is_none() { + if transforms.get(handle).is_none() { Err(Error::NonTransformFound) } else { - unsafe { Ok(Self::world_scale_unchecked(tree, arena, handle)) } + unsafe { Ok(Self::world_scale_unchecked(nodes, transforms, handle)) } } } /// Get the scale of `Transform` in world space without doing bounds checking. - pub unsafe fn world_scale_unchecked(tree: &T1, arena: &T2, handle: Entity) -> f32 + pub unsafe fn world_scale_unchecked(nodes: &T1, transforms: &T2, handle: Entity) -> f32 where T1: ArenaGet, T2: ArenaGet, { - let transform = arena.get_unchecked(handle); + let transform = transforms.get_unchecked(handle); let mut scale = transform.scale(); - for v in Node::ancestors(tree, handle) { - if let Some(ancestor) = arena.get(v) { + for v in Node::ancestors(nodes, handle) { + if let Some(ancestor) = transforms.get(v) { scale *= ancestor.scale(); } } @@ -314,8 +389,8 @@ impl Transform { /// Set rotation of `Transform` in world space. pub fn set_world_rotation( - tree: &T1, - arena: &mut T2, + nodes: &T1, + transforms: &mut T2, handle: Entity, rotation: T3, ) -> Result<()> @@ -324,20 +399,20 @@ impl Transform { T2: ArenaGetMut, T3: Into>, { - if arena.get(handle).is_none() { + if transforms.get(handle).is_none() { return Err(Error::NonTransformFound); } unsafe { - Self::set_world_rotation_unchecked(tree, arena, handle, rotation); + Self::set_world_rotation_unchecked(nodes, transforms, handle, rotation); Ok(()) } } /// Set rotation of `Transform` in world space without doing bounds checking. pub unsafe fn set_world_rotation_unchecked( - tree: &T1, - arena: &mut T2, + nodes: &T1, + transforms: &mut T2, handle: Entity, rotation: T3, ) where @@ -345,17 +420,17 @@ impl Transform { T2: ArenaGetMut, T3: Into>, { - if tree.get(handle).is_none() { - arena.get_unchecked_mut(handle).set_rotation(rotation); + if nodes.get(handle).is_none() { + transforms.get_unchecked_mut(handle).set_rotation(rotation); } else { let mut ancestors_rotation = math::Quaternion::one(); - for v in Node::ancestors(tree, handle) { - if let Some(transform) = arena.get(v) { + for v in Node::ancestors(nodes, handle) { + if let Some(transform) = transforms.get(v) { ancestors_rotation = ancestors_rotation * transform.rotation(); } } - arena + transforms .get_unchecked_mut(handle) .set_rotation(rotation.into() * ancestors_rotation.invert()); } @@ -363,35 +438,35 @@ impl Transform { /// Get rotation of `Transform` in world space. pub fn world_rotation( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - if arena.get(handle).is_none() { + if transforms.get(handle).is_none() { Err(Error::NonTransformFound) } else { - unsafe { Ok(Self::world_rotation_unchecked(tree, arena, handle)) } + unsafe { Ok(Self::world_rotation_unchecked(nodes, transforms, handle)) } } } /// Get rotation of `Transform` in world space without doing bounds checking. pub unsafe fn world_rotation_unchecked( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> math::Quaternion where T1: ArenaGet, T2: ArenaGet, { - let transform = arena.get_unchecked(handle); + let transform = transforms.get_unchecked(handle); let mut rotation = transform.rotation(); - for v in Node::ancestors(tree, handle) { - if let Some(ancestor) = arena.get(v) { + for v in Node::ancestors(nodes, handle) { + if let Some(ancestor) = transforms.get(v) { rotation = rotation * ancestor.rotation(); } } @@ -401,8 +476,8 @@ impl Transform { #[allow(dead_code)] pub(crate) fn set_world_decomposed( - tree: &T1, - arena: &mut T2, + nodes: &T1, + transforms: &mut T2, handle: Entity, decomposed: math::Decomposed, math::Quaternion>, ) -> Result<()> @@ -410,11 +485,11 @@ impl Transform { T1: ArenaGet, T2: ArenaGetMut, { - let relative = Transform::world_decomposed(tree, arena, handle)?; + let relative = Transform::world_decomposed(nodes, transforms, handle)?; if let Some(inverse) = relative.inverse_transform() { unsafe { - arena.get_unchecked_mut(handle).decomposed = inverse.concat(&decomposed); + transforms.get_unchecked_mut(handle).decomposed = inverse.concat(&decomposed); } Ok(()) } else { @@ -423,34 +498,34 @@ impl Transform { } pub(crate) fn world_decomposed( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> Result, math::Quaternion>> where T1: ArenaGet, T2: ArenaGet, { - if arena.get(handle).is_none() { + if transforms.get(handle).is_none() { Err(Error::NonTransformFound) } else { - unsafe { Ok(Self::world_decomposed_unchecked(tree, arena, handle)) } + unsafe { Ok(Self::world_decomposed_unchecked(nodes, transforms, handle)) } } } pub(crate) unsafe fn world_decomposed_unchecked( - tree: &T1, - arena: &T2, + nodes: &T1, + transforms: &T2, handle: Entity, ) -> math::Decomposed, math::Quaternion> where T1: ArenaGet, T2: ArenaGet, { - let transform = arena.get_unchecked(handle); + let transform = transforms.get_unchecked(handle); let mut decomposed = transform.decomposed; - for v in Node::ancestors(tree, handle) { - if let Some(ancestor) = arena.get(v) { + for v in Node::ancestors(nodes, handle) { + if let Some(ancestor) = transforms.get(v) { decomposed = ancestor.decomposed.concat(&decomposed); } } @@ -460,9 +535,9 @@ impl Transform { impl Transform { /// Transforms position from local space to world space. - pub fn transform_point( - tree: &T1, - arena: &T2, + pub fn world_transform_point( + nodes: &T1, + transforms: &T2, handle: Entity, v: T3, ) -> Result> @@ -471,7 +546,7 @@ impl Transform { T2: ArenaGet, T3: Into>, { - let decomposed = Transform::world_decomposed(tree, arena, handle)?; + let decomposed = Transform::world_decomposed(nodes, transforms, handle)?; // M = T * R * S Ok(decomposed.rot * (v.into() * decomposed.scale) + decomposed.disp) } @@ -480,9 +555,9 @@ impl Transform { /// /// This operation is not affected by position of the transform, but is is affected by scale. /// The returned vector may have a different length than vector. - pub fn transform_vector( - tree: &T1, - arena: &T2, + pub fn world_transform_vector( + nodes: &T1, + transforms: &T2, handle: Entity, v: T3, ) -> Result> @@ -491,7 +566,7 @@ impl Transform { T2: ArenaGet, T3: Into>, { - let decomposed = Transform::world_decomposed(tree, arena, handle)?; + let decomposed = Transform::world_decomposed(nodes, transforms, handle)?; Ok(decomposed.transform_vector(v.into())) } @@ -499,9 +574,9 @@ impl Transform { /// /// This operation is not affected by scale or position of the transform. The returned /// vector has the same length as direction. - pub fn transform_direction( - tree: &T1, - arena: &T2, + pub fn world_transform_direction( + nodes: &T1, + transforms: &T2, handle: Entity, v: T3, ) -> Result> @@ -510,34 +585,61 @@ impl Transform { T2: ArenaGet, T3: Into>, { - let rotation = Transform::world_rotation(tree, arena, handle)?; + let rotation = Transform::world_rotation(nodes, transforms, handle)?; Ok(rotation * v.into()) } /// Return the up direction in world space, which is looking down the positive y-axis. - pub fn up(tree: &T1, arena: &T2, handle: Entity) -> Result> + pub fn world_up( + nodes: &T1, + transforms: &T2, + handle: Entity, + ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - Transform::transform_direction(tree, arena, handle, math::Vector3::new(0.0, 1.0, 0.0)) + Transform::world_transform_direction( + nodes, + transforms, + handle, + math::Vector3::new(0.0, 1.0, 0.0), + ) } /// Return the forward direction in world space, which is looking down the positive z-axis. - pub fn forward(tree: &T1, arena: &T2, handle: Entity) -> Result> + pub fn world_forward( + nodes: &T1, + transforms: &T2, + handle: Entity, + ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - Transform::transform_direction(tree, arena, handle, math::Vector3::new(0.0, 0.0, 1.0)) + Transform::world_transform_direction( + nodes, + transforms, + handle, + math::Vector3::new(0.0, 0.0, 1.0), + ) } /// Return the right direction in world space, which is looking down the positive x-axis. - pub fn right(tree: &T1, arena: &T2, handle: Entity) -> Result> + pub fn world_right( + nodes: &T1, + transforms: &T2, + handle: Entity, + ) -> Result> where T1: ArenaGet, T2: ArenaGet, { - Transform::transform_direction(tree, arena, handle, math::Vector3::new(1.0, 0.0, 0.0)) + Transform::world_transform_direction( + nodes, + transforms, + handle, + math::Vector3::new(1.0, 0.0, 0.0), + ) } } diff --git a/modules/3d/src/graphics/graph.rs b/modules/3d/src/graphics/data.rs similarity index 50% rename from modules/3d/src/graphics/graph.rs rename to modules/3d/src/graphics/data.rs index df58492..9403cad 100644 --- a/modules/3d/src/graphics/graph.rs +++ b/modules/3d/src/graphics/data.rs @@ -1,3 +1,6 @@ +use std::sync::Arc; +use std::collections::HashMap; + use crayon::application::Context; use crayon::ecs::prelude::*; use crayon::math; @@ -6,6 +9,7 @@ use crayon::graphics::prelude::*; use components::prelude::*; use errors::*; +use graphics::DrawSetup; #[derive(Debug, Copy, Clone)] pub struct RenderLit { @@ -25,6 +29,8 @@ pub struct RenderLit { pub struct RenderShadowCaster { /// The matrix that transform from world space into shadow space. pub shadow_space_matrix: math::Matrix4, + /// The matrix that transform from world space into view space. + pub shadow_view_matrix: math::Matrix4, /// The frustum that builds from `shadow_space_matrix`. pub shadow_frustum: math::Frustum, } @@ -45,52 +51,53 @@ pub struct RenderCamera { pub view_matrix: math::Matrix4, } -/// A trivial `RenderGraph` with brutal force iteration. -pub struct SimpleRenderGraph { - lits: Vec, - visible_entities: Vec, - video: GraphicsSystemGuard, - - /// The active render camera. - camera: RenderCamera, - /// Objects that are beyond this distance from the camera will be rendered with - /// no shadows at all. - shadow_max_distance: f32, -} - -impl SimpleRenderGraph { - pub fn new(ctx: &Context) -> Result { - let video = GraphicsSystemGuard::new(ctx.shared::().clone()); - +impl Default for RenderCamera { + fn default() -> Self { let projection = math::Projection::Ortho { width: 128.0, height: 128.0, near: 0.1, far: 128.0, }; + let default_frustum = math::Frustum::new(projection); - let render_camera = RenderCamera { + RenderCamera { id: Entity::nil(), component: Camera::default(), position: [0.0, 0.0, 0.0].into(), frustum: default_frustum, shadow_frustum: default_frustum, view_matrix: math::Matrix4::identity(), - }; + } + } +} - Ok(SimpleRenderGraph { +pub struct RenderData { + /// The active render camera. + pub camera: RenderCamera, + /// Entities which is visible from active render camera. + pub visible_entities: Vec, + /// Enable lits in the scene. + pub lits: Vec, + /// The world transforms. + pub world_transforms: HashMap, + + video: Arc, +} + +impl RenderData { + pub fn new(ctx: &Context) -> Self { + RenderData { lits: Vec::new(), visible_entities: Vec::new(), - video: video, - - camera: render_camera, - shadow_max_distance: 100.0, - }) + camera: RenderCamera::default(), + world_transforms: HashMap::new(), + video: ctx.shared::().clone(), + } } - /// Advances one frame with main camera. - pub fn advance(&mut self, world: &World, camera: Entity) -> Result<()> { + pub fn build(&mut self, world: &World, camera: Entity, setup: DrawSetup) -> Result<()> { if let Some(v) = world.get::(camera) { let (_, nodes, transforms) = world.view_r2::(); self.camera.id = camera; @@ -101,30 +108,36 @@ impl SimpleRenderGraph { return Err(Error::NonCameraFound); }; - TaskGetVisibleEntities { graph: self }.run_with(world)?; - TaskGetRenderLits { graph: self }.run_with(world)?; - - Ok(()) - } - - /// Gets the iterator into lits. - pub fn lits(&self) -> ::std::slice::Iter { - self.lits.iter() - } + { + let (entities, nodes, transforms) = world.view_r2::(); + self.world_transforms = Transform::world_transforms(entities, &nodes, &transforms); + } - /// Gets the visible entities from main camera. - pub fn visible_entities(&self) -> ::std::slice::Iter { - self.visible_entities.iter() - } + TaskGetVisibleEntities { + world_transforms: &self.world_transforms, + camera: &mut self.camera, + visible_entities: &mut self.visible_entities, + video: &self.video, + setup: setup, + }.run_with(world)?; + + TaskGetLitShadows { + world_transforms: &self.world_transforms, + camera: &self.camera, + lits: &mut self.lits, + setup: setup, + }.run_with(world)?; - /// Gets the active camera. - pub fn camera(&self) -> RenderCamera { - self.camera + Ok(()) } } -pub struct TaskGetVisibleEntities<'a> { - graph: &'a mut SimpleRenderGraph, +struct TaskGetVisibleEntities<'a> { + world_transforms: &'a HashMap, + camera: &'a mut RenderCamera, + visible_entities: &'a mut Vec, + video: &'a GraphicsSystemShared, + setup: DrawSetup, } impl<'a, 'b> System<'a> for TaskGetVisibleEntities<'b> { @@ -136,107 +149,115 @@ impl<'a, 'b> System<'a> for TaskGetVisibleEntities<'b> { type Err = Error; fn run(&mut self, entities: Entities, data: Self::Data) -> Result<()> { - self.graph.visible_entities.clear(); - - let cmp = self.graph.camera.component; - let view_matrix = self.graph.camera.view_matrix; + let cmp = self.camera.component; + let view_matrix = self.camera.view_matrix; let frustum = math::Frustum::new(cmp.projection()); - let clip = (cmp.near_clip_plane(), cmp.far_clip_plane()); - let mut tight_clip = (clip.1, clip.0); - - unsafe { - for v in entities.with_3::() { - let mesh = data.2.get_unchecked(v); + let mut compact = (clip.1, clip.0); + let video = &self.video; + let world_transforms = &self.world_transforms; + let v: Vec<_> = (entities, &data.0, &data.1, &data.2) + .par_join(&entities, 128) + .map(|(v, _, _, mesh)| { // Checks if mesh is visible. if !mesh.visible { - continue; + return None; } // Gets the underlying mesh params. - let mso = if let Some(mso) = self.graph.video.mesh(mesh.mesh) { + let mso = if let Some(mso) = video.mesh(mesh.mesh) { mso } else { - continue; + return None; }; // Checks if mesh is visible for camera frustum. - let model = Transform::world_matrix(&data.0, &data.1, v).unwrap(); + let model = world_transforms[&v].matrix(); let aabb = mso.aabb.transform(&(view_matrix * model)); if frustum.contains(&aabb) == math::PlaneRelation::Out { - continue; + return None; } - tight_clip.0 = clip.0.max(tight_clip.0.min(aabb.min().z)); - tight_clip.1 = clip.1.min(tight_clip.1.max(aabb.max().z)); - self.graph.visible_entities.push(v); - } - } + Some((aabb, v)) + }) + .while_some() + .collect(); + + self.visible_entities.clear(); + self.visible_entities.extend(v.iter().map(|&(_, id)| id)); + + compact = v.iter().map(|&(aabb, _)| aabb).fold(compact, |a, b| { + ( + clip.0.max(a.0.min(b.min().z)), + clip.1.min(a.1.max(b.max().z)), + ) + }); { - let mut camera = self.graph.camera.component; - camera.set_clip_plane(tight_clip.0 * 0.95, tight_clip.1 * 1.05); - self.graph.camera.frustum = camera.frustum(); + let mut camera = self.camera.component; + camera.set_clip_plane(compact.0 * 0.95, compact.1 * 1.05); + self.camera.frustum = camera.frustum(); } { - tight_clip.1 = tight_clip.1.min(self.graph.shadow_max_distance); - - let mut camera = self.graph.camera.component; - camera.set_clip_plane(tight_clip.0 * 0.99, tight_clip.1 * 1.01); - self.graph.camera.shadow_frustum = camera.frustum(); + compact.1 = compact.1.min(self.setup.max_shadow_distance); + let mut camera = self.camera.component; + camera.set_clip_plane(compact.0 * 0.99, compact.1 * 1.01); + self.camera.shadow_frustum = camera.frustum(); } Ok(()) } } -pub struct TaskGetRenderLits<'a> { - graph: &'a mut SimpleRenderGraph, +struct TaskGetLitShadows<'a> { + world_transforms: &'a HashMap, + camera: &'a RenderCamera, + lits: &'a mut Vec, + setup: DrawSetup, } -impl<'a, 'b> System<'a> for TaskGetRenderLits<'b> { +impl<'a, 'b> System<'a> for TaskGetLitShadows<'b> { type Data = (Fetch<'a, Node>, Fetch<'a, Transform>, Fetch<'a, Light>); type Err = Error; fn run(&mut self, entities: Entities, data: Self::Data) -> Result<()> { - unsafe { - let view_matrix = self.graph.camera.view_matrix; - let dir_matrix = math::Matrix3::from_cols( - view_matrix.x.truncate(), - view_matrix.y.truncate(), - view_matrix.z.truncate(), - ); - - let id = self.graph.camera.id; - let icvm = Transform::inverse_world_view_matrix(&data.0, &data.1, id)?; - let frustum = self.graph.camera.shadow_frustum; - let frustum_points: math::FrustumPoints<_> = frustum.into(); - - self.graph.lits.clear(); - for v in entities.with_3::() { - let lit = data.2.get_unchecked(v); + let view_matrix = self.camera.view_matrix; + let dir_matrix = math::Matrix3::from_cols( + view_matrix.x.truncate(), + view_matrix.y.truncate(), + view_matrix.z.truncate(), + ); + + let icvm = Transform::inverse_world_view_matrix(&data.0, &data.1, self.camera.id)?; + let frustum = self.camera.shadow_frustum; + let frustum_points: math::FrustumPoints<_> = frustum.into(); + let max_shadow_distance = self.setup.max_shadow_distance; + let world_transforms = &self.world_transforms; + + let v: Vec<_> = (entities, &data.0, &data.1, &data.2) + .par_join(&entities, 128) + .map(|(id, _, _, lit)| { if !lit.enable { - continue; + return None; } let shadow = match lit.source { LitSource::Dir => { if lit.shadow_caster { // camera view space -> world space -> shadow view space - let v = Transform::world_view_matrix(&data.0, &data.1, v)?; + let v = Transform::world_view_matrix(&data.0, &data.1, id).unwrap(); let aabb = frustum_points.transform(&(v * icvm)).aabb(); let (mut min, max) = (aabb.min(), aabb.max()); - if (max.z - min.z) > self.graph.shadow_max_distance { - min.z = max.z - self.graph.shadow_max_distance; + if (max.z - min.z) > max_shadow_distance { + min.z = max.z - max_shadow_distance; } - let dim = max - min; let projection = math::Projection::ortho(dim.x, dim.y, min.z, max.z); let frustum = math::Frustum::new(projection); - Some(RenderShadowCaster { + shadow_view_matrix: v, shadow_space_matrix: projection.to_matrix() * v, shadow_frustum: frustum, }) @@ -248,20 +269,20 @@ impl<'a, 'b> System<'a> for TaskGetRenderLits<'b> { _ => None, }; - let position = Transform::world_position(&data.0, &data.1, v).unwrap(); - let dir = Transform::forward(&data.0, &data.1, v).unwrap(); - - let rl = RenderLit { - handle: v, - position: (view_matrix * position.extend(1.0)).truncate(), - dir: (dir_matrix * dir).normalize(), + let transform = world_transforms[&id]; + Some(RenderLit { + handle: id, + position: (view_matrix * transform.position().extend(1.0)).truncate(), + dir: (dir_matrix * transform.forward()).normalize(), lit: *lit, shadow: shadow, - }; + }) + }) + .while_some() + .collect(); - self.graph.lits.push(rl); - } - } + self.lits.clear(); + self.lits.extend(v.iter().cloned()); Ok(()) } diff --git a/modules/3d/src/graphics/mod.rs b/modules/3d/src/graphics/mod.rs index 083675c..356b49d 100644 --- a/modules/3d/src/graphics/mod.rs +++ b/modules/3d/src/graphics/mod.rs @@ -1,9 +1,36 @@ -pub mod graph; +mod data; +mod shadow; pub mod renderer; -pub mod shadow; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum DrawOrder { Shadow = 0, Camera, Max, } + +#[derive(Debug, Clone, Copy)] +pub struct DrawSetup { + pub max_dir_lits: usize, + pub max_point_lits: usize, + pub max_shadow_casters: usize, + pub max_shadow_distance: f32, + pub max_shadow_resolution: (u16, u16), +} + +impl Default for DrawSetup { + fn default() -> Self { + DrawSetup { + max_dir_lits: 1, + max_point_lits: 4, + max_shadow_casters: 1, + max_shadow_distance: 100.0, + max_shadow_resolution: (512, 512), + } + } +} + +pub mod prelude { + pub use graphics::{DrawOrder, DrawSetup}; + pub use graphics::renderer::Renderer; +} diff --git a/modules/3d/src/graphics/renderer.rs b/modules/3d/src/graphics/renderer.rs index eaa5bb1..8c4d618 100644 --- a/modules/3d/src/graphics/renderer.rs +++ b/modules/3d/src/graphics/renderer.rs @@ -5,53 +5,57 @@ use crayon::math::{Matrix, SquareMatrix}; use crayon::graphics::prelude::*; use crayon::graphics::assets::prelude::*; use crayon::utils::Handle; +use crayon::rayon::prelude::*; use components::prelude::*; use assets::prelude::*; use errors::*; +use graphics::DrawSetup; use graphics::shadow::RenderShadow; -use graphics::graph::SimpleRenderGraph; +use graphics::data::RenderData; -use scene::Scene; +use resources::Resources; use assets::material::Material; use assets::pipeline::PipelineParams; -pub struct RendererSetup { - pub max_dir_lits: usize, - pub max_point_lits: usize, - pub max_shadow_casters: usize, -} - pub struct Renderer { video: GraphicsSystemGuard, - graph: SimpleRenderGraph, + data: RenderData, + shadow: RenderShadow, default_surface: SurfaceHandle, + default_material: MaterialHandle, + + setup: DrawSetup, } impl Renderer { - pub fn new(ctx: &Context) -> Result { + pub fn new(ctx: &Context, resources: &mut Resources, setup: DrawSetup) -> Result { let mut video = GraphicsSystemGuard::new(ctx.shared::().clone()); - let setup = SurfaceSetup::default(); - let surface = video.create_surface(setup)?; + let surface = video.create_surface(SurfaceSetup::default())?; + + let undefined = factory::pipeline::undefined(resources)?; + let mat = resources.create_material(MaterialSetup::new(undefined))?; Ok(Renderer { video: video, - graph: SimpleRenderGraph::new(ctx)?, + data: RenderData::new(ctx), shadow: RenderShadow::new(ctx)?, default_surface: surface, + default_material: mat, + setup: setup, }) } pub fn advance(&mut self, world: &World, camera: Entity) -> Result<()> { - self.graph.advance(world, camera)?; - self.shadow.advance(world, &self.graph)?; + self.data.build(world, camera, self.setup)?; + self.shadow.build(world, &self.data, self.setup)?; Ok(()) } pub fn draw_shadow(&self, surface: Option) -> Result<()> { - for v in self.graph.lits() { + for v in &self.data.lits { if v.shadow.is_some() { let surface = surface.unwrap_or(self.default_surface); return self.shadow.draw(v.handle, surface); @@ -61,37 +65,36 @@ impl Renderer { Ok(()) } - pub fn draw(&self, scene: &Scene) -> Result<()> { - TaskDraw { - scene: scene, + pub fn draw(&self, world: &World, resources: &Resources) -> Result<()> { + TaskDrawCall { + resources: resources, renderer: self, - default_surface: self.default_surface, - }.run_with(&scene.world) + }.run_with(world) } } -struct TaskDraw<'a> { - scene: &'a Scene, +struct TaskDrawCall<'a> { + resources: &'a Resources, renderer: &'a Renderer, - default_surface: SurfaceHandle, } -impl<'a> TaskDraw<'a> { - fn material(&self, handle: MaterialHandle) -> (&PipelineParams, &Material) { - if let Some(mat) = self.scene.materials.get(handle) { - if let Some(pipeline) = self.scene.pipelines.get(mat.pipeline) { - if self.scene.video.is_shader_alive(pipeline.shader) { +impl<'a> TaskDrawCall<'a> { + fn material( + video: &GraphicsSystemShared, + resources: &'a Resources, + handle: MaterialHandle, + fallback: MaterialHandle, + ) -> (&'a PipelineParams, &'a Material) { + if let Some(mat) = resources.materials.get(handle) { + if let Some(pipeline) = resources.pipelines.get(mat.pipeline) { + if video.is_shader_alive(pipeline.shader) { return (pipeline, mat); } } } - let mat = self.scene - .materials - .get(self.scene.fallback.unwrap()) - .unwrap(); - - let pipeline = self.scene.pipelines.get(mat.pipeline).unwrap(); + let mat = resources.materials.get(fallback).unwrap(); + let pipeline = resources.pipelines.get(mat.pipeline).unwrap(); (pipeline, mat) } @@ -111,52 +114,77 @@ impl<'a> TaskDraw<'a> { } } -impl<'a, 'b> System<'a> for TaskDraw<'b> { +impl<'a, 'b> System<'a> for TaskDrawCall<'b> { type Data = ( Fetch<'a, Node>, Fetch<'a, Transform>, Fetch<'a, MeshRenderer>, ); + type Err = Error; - fn run(&mut self, entities: Entities, data: Self::Data) -> Result<()> { + fn run(&mut self, _: Entities, cmps: Self::Data) -> Result<()> { use self::PipelineUniformVariable as RU; - unsafe { - let camera = self.renderer.graph.camera(); - let surface = camera.component.surface().unwrap_or(self.default_surface); - let projection_matrix = camera.frustum.to_matrix(); + let camera = self.renderer.data.camera; + let surface = camera + .component + .surface() + .unwrap_or(self.renderer.default_surface); + let projection_matrix = camera.frustum.to_matrix(); + + let data = &self.renderer.data; + let video = &self.renderer.video; + let default_material = self.renderer.default_material; + let resources = &self.resources; + + self.renderer + .data + .visible_entities + .par_iter() + .for_each(|&id| { + let mesh = if let Some(v) = cmps.2.get(id) { + v + } else { + return; + }; - for v in entities.with_3::() { - let mesh = data.2.get_unchecked(v); + let transform = data.world_transforms[&id]; // Gets the underlying mesh params. - let mso = if let Some(mso) = self.scene.video.mesh(mesh.mesh) { + let mso = if let Some(mso) = video.mesh(mesh.mesh) { + if mso.sub_mesh_offsets.len() <= 0 { + return; + } mso } else { - continue; + return; }; - // Gets the model matrix. - let model = Transform::world_matrix(&data.0, &data.1, v).unwrap(); - - // Checks if mesh is visible. - if !mesh.visible { - continue; - } - - // let aabb = mso.aabb.transform(&model); - // if !mesh.visible || camera.frustum.contains(&aabb) == math::PlaneRelation::Out { - // continue; - // } + let fallback = mesh.materials + .get(0) + .cloned() + .unwrap_or(Handle::nil().into()); // Iterates and draws the sub-meshes with corresponding material. + let mut from = mso.sub_mesh_offsets[0]; + let mut current_mat = fallback; + for i in 0..mso.sub_mesh_offsets.len() { - let (pipeline, mat) = - self.material(*mesh.materials.get(i).unwrap_or(&Handle::nil().into())); + let len = if i == mso.sub_mesh_offsets.len() - 1 { + mso.num_idxes - from + } else { + let mat = mesh.materials.get(i + 1).cloned().unwrap_or(fallback); + if current_mat == mat { + continue; + } - let p = Transform::world_position(&data.0, &data.1, v).unwrap(); - let mut csp = camera.view_matrix * math::Vector4::new(p.x, p.y, p.z, 1.0); + mso.sub_mesh_offsets[i + 1] - from + }; + + let (pipeline, mat) = + Self::material(video, resources, current_mat, default_material); + let mut csp = camera.view_matrix * transform.position().extend(1.0); csp /= csp.w; // Generate packed draw order. @@ -172,12 +200,13 @@ impl<'a, 'b> System<'a> for TaskDraw<'b> { dc.set_uniform_variable(*k, *v); } - let mv = camera.view_matrix * model; + let m = transform.matrix(); + let mv = camera.view_matrix * m; let mvp = projection_matrix * mv; let n = mv.invert().and_then(|v| Some(v.transpose())).unwrap_or(mv); // Model matrix. - Self::bind(&mut dc, pipeline, RU::ModelMatrix, model); + Self::bind(&mut dc, pipeline, RU::ModelMatrix, m); // Model view matrix. Self::bind(&mut dc, pipeline, RU::ModelViewMatrix, mv); // Mode view projection matrix. @@ -187,8 +216,8 @@ impl<'a, 'b> System<'a> for TaskDraw<'b> { // FIXME: The lits that affected object should be determined by the distance. let (mut dir_index, mut point_index) = (0, 0); - for l in self.renderer.graph.lits() { - let color: [f32; 4] = l.lit.color.into(); + for l in &self.renderer.data.lits { + let color: [f32; 4] = l.lit.color.rgba(); let color = [color[0], color[1], color[2]]; match l.lit.source { @@ -206,7 +235,7 @@ impl<'a, 'b> System<'a> for TaskDraw<'b> { // Shadow depth texture. Self::bind(&mut dc, pipeline, uniforms[2], rt); // Shadow space matrix. - let ssm = shadow.shadow_space_matrix * model; + let ssm = shadow.shadow_space_matrix * m; Self::bind(&mut dc, pipeline, uniforms[3], ssm); } } @@ -238,11 +267,14 @@ impl<'a, 'b> System<'a> for TaskDraw<'b> { } // Submit. - let sdc = dc.build_sub_mesh(i)?; - self.renderer.video.submit(surface, order, sdc)?; + let sdc = dc.build_from(from, len).unwrap(); + video.submit(surface, order, sdc).unwrap(); + + // + from = from + len; + current_mat = mesh.materials.get(i + 1).cloned().unwrap_or(fallback); } - } - } + }); Ok(()) } diff --git a/modules/3d/src/graphics/shadow.rs b/modules/3d/src/graphics/shadow.rs index 32ac467..c8c65aa 100644 --- a/modules/3d/src/graphics/shadow.rs +++ b/modules/3d/src/graphics/shadow.rs @@ -4,20 +4,16 @@ use crayon::application::Context; use crayon::ecs::prelude::*; use crayon::graphics::prelude::*; use crayon::graphics::assets::prelude::*; +use crayon::rayon::prelude::*; +use crayon::math; -use graphics::DrawOrder; -use graphics::graph::{RenderShadowCaster, SimpleRenderGraph}; +use graphics::{DrawOrder, DrawSetup}; +use graphics::data::{RenderData, RenderShadowCaster}; use assets::factory; use components::prelude::*; use errors::*; -#[derive(Debug, Clone, Copy)] -struct ShadowSurface { - render_texture: RenderTextureHandle, - surface: SurfaceHandle, -} - /// A shadow mapping builder. /// /// Some techniques that used to avoid artifacts could be found at : @@ -27,7 +23,6 @@ pub struct RenderShadow { video: GraphicsSystemGuard, depth_shader: ShaderHandle, draw_shader: ShaderHandle, - shadow_casters: HashMap, shadow_surfaces: Vec, } @@ -87,29 +82,32 @@ impl RenderShadow { }) } + /// Gets the handle of depth buffer. + pub fn depth_render_texture(&self, caster: Entity) -> Option { + if let Some(&(ss, _)) = self.shadow_casters.get(&caster) { + Some(ss.render_texture) + } else { + None + } + } + /// Advances one frame, builds the depth buffer of shadow mapping technique. - pub fn advance(&mut self, world: &World, graph: &SimpleRenderGraph) -> Result<()> { + pub fn build(&mut self, world: &World, data: &RenderData, setup: DrawSetup) -> Result<()> { for (_, v) in self.shadow_casters.drain() { self.shadow_surfaces.push(v.0); } - for lit in graph.lits() { + for lit in &data.lits { if let Some(caster) = lit.shadow { - let surface = self.alloc_surface()?; + let surface = self.alloc_surface(setup.max_shadow_resolution)?; self.shadow_casters.insert(lit.handle, (surface, caster)); } } - GenerateRenderShadow { shadow: self }.run_with(world) - } - - /// Gets the handle of depth buffer. - pub fn depth_render_texture(&self, caster: Entity) -> Option { - if let Some(&(ss, _)) = self.shadow_casters.get(&caster) { - Some(ss.render_texture) - } else { - None - } + TaskGenShadow { + data: data, + shadow: self, + }.run_with(world) } /// Draw the underlying depth buffer into the `surface`. @@ -126,15 +124,14 @@ impl RenderShadow { Ok(()) } - fn alloc_surface(&mut self) -> Result { + fn alloc_surface(&mut self, resolution: (u16, u16)) -> Result { if let Some(surface) = self.shadow_surfaces.pop() { return Ok(surface); } let mut setup = RenderTextureSetup::default(); setup.format = RenderTextureFormat::Depth16; - // FIXME: The dimensions of shadow texture should be configuratable. - setup.dimensions = (256, 256); + setup.dimensions = resolution; let render_texture = self.video.create_render_texture(setup)?; let mut setup = SurfaceSetup::default(); @@ -150,11 +147,18 @@ impl RenderShadow { } } -struct GenerateRenderShadow<'a> { +#[derive(Debug, Clone, Copy)] +struct ShadowSurface { + render_texture: RenderTextureHandle, + surface: SurfaceHandle, +} + +struct TaskGenShadow<'a> { + data: &'a RenderData, shadow: &'a RenderShadow, } -impl<'a, 'b> System<'a> for GenerateRenderShadow<'b> { +impl<'a, 'b> System<'a> for TaskGenShadow<'b> { type Data = ( Fetch<'a, Node>, Fetch<'a, Transform>, @@ -163,35 +167,43 @@ impl<'a, 'b> System<'a> for GenerateRenderShadow<'b> { type Err = Error; fn run(&mut self, entities: Entities, data: Self::Data) -> Result<()> { - unsafe { - for handle in entities.with_3::() { - let mesh = data.2.get_unchecked(handle); - - if !mesh.visible || !mesh.shadow_caster { - continue; - } - - // Gets the underlying mesh params. - let mso = if let Some(mso) = self.shadow.video.mesh(mesh.mesh) { - mso - } else { - continue; - }; - - for (_, &(ss, rsc)) in &self.shadow.shadow_casters { - let m = Transform::world_matrix(&data.0, &data.1, handle)?; - let mvp = rsc.shadow_space_matrix * m; - - let mut dc = DrawCall::new(self.shadow.depth_shader, mesh.mesh); - dc.set_uniform_variable("u_MVPMatrix", mvp); - - for i in 0..mso.sub_mesh_offsets.len() { - let sdc = dc.build_sub_mesh(i)?; - self.shadow.video.submit(ss.surface, 0u64, sdc)?; - } - } - } - } + let video = &self.shadow.video; + let world_transforms = &self.data.world_transforms; + let shader = self.shadow.depth_shader; + + self.shadow + .shadow_casters + .par_iter() + .for_each(|(_, &(ss, shadow))| { + (entities, &data.0, &data.1, &data.2) + .par_join(&entities, 128) + .for_each(|(v, _, _, mesh)| { + if !mesh.visible || !mesh.shadow_caster { + return; + } + + // Gets the underlying mesh params. + let mso = if let Some(mso) = video.mesh(mesh.mesh) { + mso + } else { + return; + }; + + // // Checks if mesh is visible for shadow frustum. + let m = world_transforms[&v].matrix(); + let aabb = mso.aabb.transform(&(shadow.shadow_view_matrix * m)); + if shadow.shadow_frustum.contains(&aabb) == math::PlaneRelation::Out { + return; + } + + let mvp = shadow.shadow_space_matrix * m; + let mut dc = DrawCall::new(shader, mesh.mesh); + dc.set_uniform_variable("u_MVPMatrix", mvp); + + let sdc = dc.build(MeshIndex::All).unwrap(); + video.submit(ss.surface, 0u64, sdc).unwrap(); + }); + }); Ok(()) } diff --git a/modules/3d/src/lib.rs b/modules/3d/src/lib.rs index affc7f1..b2fda8f 100644 --- a/modules/3d/src/lib.rs +++ b/modules/3d/src/lib.rs @@ -9,13 +9,14 @@ pub mod graphics; pub mod scene; pub mod ent; pub mod assets; +pub mod resources; pub mod prelude { - pub use scene::Scene; + pub use scene::{Scene, SceneSetup}; pub use assets::prelude::*; pub use components::prelude::*; + pub use graphics::prelude::*; pub use crayon::ecs::world::Entity; pub use crayon::ecs::view::{ArenaGet, ArenaGetMut, Join}; - pub use graphics::DrawOrder; pub use ent::{EntReader, EntRef, EntRefMut, EntWriter}; } diff --git a/modules/3d/src/resources.rs b/modules/3d/src/resources.rs new file mode 100644 index 0000000..7587e6f --- /dev/null +++ b/modules/3d/src/resources.rs @@ -0,0 +1,79 @@ +use crayon::application::Context; +use crayon::graphics::prelude::*; +use crayon::resource::utils::prelude::*; + +use errors::*; +use assets::prelude::*; +use assets::material::Material; +use assets::pipeline::PipelineParams; + +pub struct Resources { + video: GraphicsSystemGuard, + pub(crate) materials: Registery, + pub(crate) pipelines: Registery, +} + +impl Resources { + pub fn new(ctx: &Context) -> Self { + Resources { + video: GraphicsSystemGuard::new(ctx.shared::().clone()), + pipelines: Registery::new(), + materials: Registery::new(), + } + } + + /// Lookups pipeline object from location. + pub fn lookup_pipeline(&self, location: Location) -> Option { + self.pipelines.lookup(location).map(|v| v.into()) + } + + /// Creates a new pipeline object that indicates the whole render pipeline of `Scene`. + pub fn create_pipeline(&mut self, setup: PipelineSetup) -> Result { + if let Some(handle) = self.lookup_pipeline(setup.location()) { + self.pipelines.inc_rc(handle); + return Ok(handle.into()); + } + + let (location, setup, links) = setup.into(); + let params = setup.params.clone(); + let shader = self.video.create_shader(setup)?; + + Ok(self.pipelines + .create(location, PipelineParams::new(shader, params, links)) + .into()) + } + + /// Deletes a pipelie object. + pub fn delete_pipeline(&mut self, handle: PipelineHandle) { + self.pipelines.dec_rc(handle); + } + + /// Creates a new material instance from shader. + pub fn create_material(&mut self, setup: MaterialSetup) -> Result { + if let Some(po) = self.pipelines.get(setup.pipeline) { + let location = Location::unique(""); + let material = Material::new(setup.pipeline, setup.variables, po.shader_params.clone()); + Ok(self.materials.create(location, material).into()) + } else { + Err(Error::PipelineHandleInvalid(setup.pipeline)) + } + } + + /// Gets the reference to material. + pub fn material(&self, h: MaterialHandle) -> Option<&Material> { + self.materials.get(h) + } + + /// Gets the mutable reference to material. + pub fn material_mut(&mut self, h: MaterialHandle) -> Option<&mut Material> { + self.materials.get_mut(h) + } + + /// Deletes the material instance from `Scene`. Any meshes that associated with a + /// invalid/deleted material handle will be drawed with a fallback material marked + /// with purple color. + #[inline] + pub fn delete_material(&mut self, handle: MaterialHandle) { + self.materials.dec_rc(handle); + } +} diff --git a/modules/3d/src/scene.rs b/modules/3d/src/scene.rs index 99bc2b0..c28e1d4 100644 --- a/modules/3d/src/scene.rs +++ b/modules/3d/src/scene.rs @@ -3,14 +3,19 @@ use crayon::ecs::prelude::*; use crayon::graphics::prelude::*; use crayon::resource::utils::prelude::*; -use components::prelude::*; use assets::prelude::*; -use assets::material::Material; -use assets::pipeline::PipelineParams; -use graphics::renderer::Renderer; +use components::prelude::*; +use graphics::prelude::*; + +use resources::Resources; use ent::{EntRef, EntRefMut}; use errors::*; +#[derive(Debug, Clone, Copy, Default)] +pub struct SceneSetup { + draw: DrawSetup, +} + /// `Scene`s contain the environments of your game. Its relative easy to think of each /// unique scene as a unique level. In each `Scene`, you place your envrionments, /// obstacles, and decorations, essentially designing and building your game in pieces. @@ -40,20 +45,15 @@ use errors::*; /// ``` /// pub struct Scene { - pub(crate) world: World, - - pub(crate) video: GraphicsSystemGuard, - pub(crate) materials: Registery, - pub(crate) pipelines: Registery, + pub resources: Resources, + pub(crate) world: World, pub(crate) renderer: Renderer, - pub(crate) fallback: Option, } impl Scene { /// Creates a new `Scene`. - pub fn new(ctx: &Context) -> Result { - let video = GraphicsSystemGuard::new(ctx.shared::().clone()); + pub fn new(ctx: &Context, setup: SceneSetup) -> Result { let mut world = World::new(); world.register::(); world.register::(); @@ -62,15 +62,12 @@ impl Scene { world.register::(); world.register::(); + let mut resources = Resources::new(ctx); + let scene = Scene { world: world, - video: video, - - pipelines: Registery::new(), - materials: Registery::new(), - fallback: None, - - renderer: Renderer::new(ctx)?, + renderer: Renderer::new(ctx, &mut resources, setup.draw)?, + resources: resources, }; Ok(scene) @@ -123,51 +120,63 @@ impl Scene { Ok(()) } + /// Advance to next frame. + pub fn advance(&mut self, camera: Entity) -> Result<()> { + self.renderer.advance(&self.world, camera)?; + Ok(()) + } + + /// Draws the underlaying depth buffer of shadow mapping pass. This is used for + /// debugging. + pub fn draw_shadow(&mut self, surface: T) -> Result<()> + where + T: Into>, + { + self.renderer.draw_shadow(surface.into()) + } + + /// Renders objects into `Surface` from `Camera`. + pub fn draw(&self, _: Entity) -> Result<()> { + self.renderer.draw(&self.world, &self.resources)?; + Ok(()) + } +} + +impl Scene { /// Lookups pipeline object from location. + #[inline] pub fn lookup_pipeline(&self, location: Location) -> Option { - self.pipelines.lookup(location).map(|v| v.into()) + self.resources.lookup_pipeline(location) } /// Creates a new pipeline object that indicates the whole render pipeline of `Scene`. + #[inline] pub fn create_pipeline(&mut self, setup: PipelineSetup) -> Result { - if let Some(handle) = self.lookup_pipeline(setup.location()) { - self.pipelines.inc_rc(handle); - return Ok(handle.into()); - } - - let (location, setup, links) = setup.into(); - let params = setup.params.clone(); - let shader = self.video.create_shader(setup)?; - - Ok(self.pipelines - .create(location, PipelineParams::new(shader, params, links)) - .into()) + self.resources.create_pipeline(setup) } /// Deletes a pipelie object. + #[inline] pub fn delete_pipeline(&mut self, handle: PipelineHandle) { - self.pipelines.dec_rc(handle); + self.resources.delete_pipeline(handle) } /// Creates a new material instance from shader. + #[inline] pub fn create_material(&mut self, setup: MaterialSetup) -> Result { - if let Some(po) = self.pipelines.get(setup.pipeline) { - let location = Location::unique(""); - let material = Material::new(setup.pipeline, setup.variables, po.shader_params.clone()); - Ok(self.materials.create(location, material).into()) - } else { - Err(Error::PipelineHandleInvalid(setup.pipeline)) - } + self.resources.create_material(setup) } /// Gets the reference to material. - pub fn material(&self, h: MaterialHandle) -> Option<&Material> { - self.materials.get(h) + #[inline] + pub fn material(&self, handle: MaterialHandle) -> Option<&Material> { + self.resources.material(handle) } /// Gets the mutable reference to material. - pub fn material_mut(&mut self, h: MaterialHandle) -> Option<&mut Material> { - self.materials.get_mut(h) + #[inline] + pub fn material_mut(&mut self, handle: MaterialHandle) -> Option<&mut Material> { + self.resources.material_mut(handle) } /// Deletes the material instance from `Scene`. Any meshes that associated with a @@ -175,32 +184,6 @@ impl Scene { /// with purple color. #[inline] pub fn delete_material(&mut self, handle: MaterialHandle) { - self.materials.dec_rc(handle); - } - - /// Advance to next frame. - pub fn advance(&mut self, camera: Entity) -> Result<()> { - self.renderer.advance(&self.world, camera)?; - Ok(()) - } - - /// Draws the underlaying depth buffer of shadow mapping pass. This is used for - /// debugging. - pub fn draw_shadow(&mut self, surface: T) -> Result<()> - where - T: Into>, - { - self.renderer.draw_shadow(surface.into()) - } - - /// Renders objects into `Surface` from `Camera`. - pub fn draw(&mut self, camera: Entity) -> Result<()> { - if self.fallback.is_none() { - let undefined = factory::pipeline::undefined(self)?; - self.fallback = Some(self.create_material(MaterialSetup::new(undefined))?); - } - - self.renderer.draw(self)?; - Ok(()) + self.resources.delete_material(handle) } } diff --git a/modules/3d/tests/transform.rs b/modules/3d/tests/transform.rs index 6dfa07d..f68b636 100644 --- a/modules/3d/tests/transform.rs +++ b/modules/3d/tests/transform.rs @@ -67,13 +67,13 @@ pub fn hierachy() { Transform::set_world_rotation(&tree, &mut arena, e4, rotation).unwrap(); let v = [1.0, 0.0, 0.0]; - let dir = Transform::transform_direction(&tree, &arena, e4, v).unwrap(); + let dir = Transform::world_transform_direction(&tree, &arena, e4, v).unwrap(); assert!(ulps_eq!(dir, math::Vector3::new(0.0, 1.0, 0.0))); - let vec = Transform::transform_vector(&tree, &arena, e4, v).unwrap(); + let vec = Transform::world_transform_vector(&tree, &arena, e4, v).unwrap(); assert!(ulps_eq!(vec, math::Vector3::new(0.0, 2.0, 0.0))); - let pos = Transform::transform_point(&tree, &arena, e4, v).unwrap(); + let pos = Transform::world_transform_point(&tree, &arena, e4, v).unwrap(); assert!(ulps_eq!(pos, math::Vector3::new(1.0, 2.0, 2.0))); } } @@ -89,6 +89,6 @@ fn look_at() { Transform::set_world_position(&tree, &mut arena, e1, [0.0, 0.0, -5.0]).unwrap(); let v = [0.0, 0.0, 1.0]; - let pos = Transform::transform_point(&tree, &arena, e1, v).unwrap(); + let pos = Transform::world_transform_point(&tree, &arena, e1, v).unwrap(); assert!(ulps_eq!(pos, math::Vector3::new(0.0, 0.0, -4.0))); } diff --git a/modules/imgui/src/renderer.rs b/modules/imgui/src/renderer.rs index bbc6c49..74a2a90 100644 --- a/modules/imgui/src/renderer.rs +++ b/modules/imgui/src/renderer.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crayon::{application, utils}; +use crayon::{application, math}; use crayon::graphics::errors::*; use crayon::graphics::prelude::*; @@ -92,7 +92,7 @@ impl Renderer { let mut verts = Vec::with_capacity(tasks.vtx_buffer.len()); for v in tasks.vtx_buffer { - let color = utils::Color::from_abgr_u32(v.col).into(); + let color = math::Color::from_abgr_u32(v.col).into(); verts.push(CanvasVertex::new( [v.pos.x, v.pos.y], [v.uv.x, v.uv.y], diff --git a/src/ecs/system.rs b/src/ecs/system.rs index ce7fac3..17263a4 100644 --- a/src/ecs/system.rs +++ b/src/ecs/system.rs @@ -18,18 +18,18 @@ pub type Result = ::std::result::Result<(), E>; /// /// Notes that the decision of whether or not to execute systems in paralle is made /// dynamically, based on their dependencies and resource accessing requests. -pub struct SystemDispatcher { +pub struct SystemDispatcher<'scope, E: failure::Fail> { handles: HandlePool, - systems: Vec>>, + systems: Vec>>, } -struct SystemDispatcherItem { - system: Box Executable<'w, Err = E> + Send>, +struct SystemDispatcherItem<'scope, E: failure::Fail> { + system: Box Executable<'w, Err = E> + Send + 'scope>, children: Vec, dependencies: Vec, } -impl SystemDispatcher { +impl<'scope, E: failure::Fail> SystemDispatcher<'scope, E> { pub fn new() -> Self { SystemDispatcher { handles: HandlePool::new(), @@ -43,7 +43,7 @@ impl SystemDispatcher { /// Returns a `Handle` as the unique identifier of this system. pub fn add(&mut self, dependencies: &[Handle], system: T) -> Handle where - T: for<'w> System<'w, Err = E> + Send + 'static, + T: for<'w> System<'w, Err = E> + Send + 'scope, { self.add_executable(dependencies, system) } @@ -89,7 +89,10 @@ impl SystemDispatcher { } } - unsafe fn build(&mut self, world: &mut World) -> Vec + Send>>> { + unsafe fn build( + &mut self, + world: &mut World, + ) -> Vec + Send + 'scope>>> { let mut dependents = HashMap::new(); for k in &self.handles { let item = self.systems @@ -187,7 +190,7 @@ impl SystemDispatcher { fn add_executable(&mut self, dependencies: &[Handle], system: T1) -> Handle where - T1: for<'w> Executable<'w, Err = E> + Send + 'static, + T1: for<'w> Executable<'w, Err = E> + Send + 'scope, { let deps: HashSet = dependencies .iter() @@ -224,7 +227,7 @@ impl SystemDispatcher { macro_rules! impl_run_closure { ($name: ident, [$($readables: ident),*], [$($writables: ident),*]) => ( - impl SystemDispatcher { + impl<'scope, E: failure::Fail> SystemDispatcher<'scope, E> { /// Queues a new system into the command buffer. /// /// Each system queued within a single `SystemDispatcher` may be executed @@ -234,7 +237,7 @@ macro_rules! impl_run_closure { where $($readables: Component,)* $($writables: Component,)* - F: for<'a> FnMut(Entities<'a>, $(Fetch<'a, $readables>,)* $(FetchMut<'a, $writables>,)*) -> Result + Send + 'static + F: for<'a> FnMut(Entities<'a>, $(Fetch<'a, $readables>,)* $(FetchMut<'a, $writables>,)*) -> Result + Send + 'scope { let closure = move |world: &World| { // Safety of these fetches is ensured by the system scheduler. @@ -398,9 +401,9 @@ where /// A `SystemData` addresses a set of resources which are required for the execution /// of some kind of `System`. -pub trait SystemData<'w> { +pub trait SystemData<'s> { #[doc(hidden)] - unsafe fn fetch(world: &'w World) -> Self; + unsafe fn fetch(world: &'s World) -> Self; #[doc(hidden)] unsafe fn readables(world: &World) -> BitSet; diff --git a/src/graphics/assets/surface.rs b/src/graphics/assets/surface.rs index 359a474..2eeb21a 100644 --- a/src/graphics/assets/surface.rs +++ b/src/graphics/assets/surface.rs @@ -1,7 +1,7 @@ //! Named bucket of draw calls with the wrapping of rendering operations to a render //! target, clearing, MSAA resolving and so on. -use utils::Color; +use math; use graphics::MAX_FRAMEBUFFER_ATTACHMENTS; use graphics::errors::{Error, Result}; use graphics::assets::texture::RenderTextureHandle; @@ -21,7 +21,7 @@ use graphics::assets::texture::RenderTextureHandle; pub struct SurfaceSetup { pub(crate) colors: [Option; MAX_FRAMEBUFFER_ATTACHMENTS], pub(crate) depth_stencil: Option, - pub(crate) clear_color: Option, + pub(crate) clear_color: Option>, pub(crate) clear_depth: Option, pub(crate) clear_stencil: Option, pub(crate) order: u64, @@ -33,7 +33,7 @@ impl Default for SurfaceSetup { SurfaceSetup { colors: [None; MAX_FRAMEBUFFER_ATTACHMENTS], depth_stencil: None, - clear_color: Some(Color::black()), + clear_color: Some(math::Color::black()), clear_depth: Some(1.0), clear_stencil: None, sequence: false, @@ -87,7 +87,7 @@ impl SurfaceSetup { #[inline] pub fn set_clear(&mut self, color: C, depth: D, stentil: S) where - C: Into>, + C: Into>>, D: Into>, S: Into>, { diff --git a/src/graphics/backend/device.rs b/src/graphics/backend/device.rs index 9198857..16cc8c9 100644 --- a/src/graphics/backend/device.rs +++ b/src/graphics/backend/device.rs @@ -7,7 +7,8 @@ use std::collections::HashMap; use gl; use gl::types::*; -use utils::{Color, DataBuffer, Handle, HashValue, Rect}; +use math; +use utils::{DataBuffer, Handle, HashValue}; use graphics::assets::prelude::*; use super::errors::*; @@ -97,7 +98,7 @@ impl Device { pub unsafe fn run_one_frame(&self) -> Result<()> { self.active_shader.set(None); self.visitor.bind_framebuffer(0, false)?; - self.visitor.clear(Color::black(), None, None)?; + self.visitor.clear(math::Color::black(), None, None)?; self.visitor.set_scissor(SurfaceScissor::Disable)?; *self.frame_info.borrow_mut() = FrameInfo::default(); @@ -525,14 +526,15 @@ impl Device { pub unsafe fn update_texture( &mut self, handle: TextureHandle, - rect: Rect, + rect: math::Aabb2, data: &[u8], ) -> Result<()> { if let Some(texture) = self.textures.get(handle) { - if data.len() > rect.size() as usize || rect.min.x < 0 - || rect.min.x as u16 >= texture.params.dimensions.0 || rect.min.y < 0 - || rect.min.y as u16 >= texture.params.dimensions.1 || rect.max.x < 0 - || rect.max.y < 0 + if data.len() > rect.volume() as usize || rect.min.x < 0.0 + || rect.min.x as u16 >= texture.params.dimensions.0 + || rect.min.y < 0.0 + || rect.min.y as u16 >= texture.params.dimensions.1 + || rect.max.x < 0.0 || rect.max.y < 0.0 { return Err(Error::OutOfBounds); } diff --git a/src/graphics/backend/frame.rs b/src/graphics/backend/frame.rs index 1719577..a3711aa 100644 --- a/src/graphics/backend/frame.rs +++ b/src/graphics/backend/frame.rs @@ -4,14 +4,15 @@ use graphics::assets::prelude::*; use super::errors::*; use super::device::Device; -use utils::{DataBuffer, DataBufferPtr, HashValue, Rect}; +use math; +use utils::{DataBuffer, DataBufferPtr, HashValue}; #[derive(Debug, Clone)] pub(crate) enum PreFrameTask { CreateSurface(SurfaceHandle, SurfaceSetup), CreatePipeline(ShaderHandle, ShaderParams, String, String), CreateTexture(TextureHandle, TextureParams, Option>), - UpdateTexture(TextureHandle, Rect, DataBufferPtr<[u8]>), + UpdateTexture(TextureHandle, math::Aabb2, DataBufferPtr<[u8]>), CreateRenderTexture(RenderTextureHandle, RenderTextureSetup), CreateMesh( MeshHandle, @@ -30,7 +31,7 @@ pub(crate) enum FrameTask { UpdateSurfaceViewport(SurfaceViewport), UpdateVertexBuffer(MeshHandle, usize, DataBufferPtr<[u8]>), UpdateIndexBuffer(MeshHandle, usize, DataBufferPtr<[u8]>), - UpdateTexture(TextureHandle, Rect, DataBufferPtr<[u8]>), + UpdateTexture(TextureHandle, math::Aabb2, DataBufferPtr<[u8]>), } #[derive(Debug, Clone, Copy)] diff --git a/src/graphics/backend/visitor.rs b/src/graphics/backend/visitor.rs index 3c094f5..4ae9dd6 100644 --- a/src/graphics/backend/visitor.rs +++ b/src/graphics/backend/visitor.rs @@ -5,8 +5,7 @@ use std::collections::HashMap; use gl; use gl::types::*; -use utils::{Color, Rect}; - +use math; use graphics::MAX_UNIFORM_TEXTURE_SLOTS; use graphics::assets::prelude::*; @@ -249,7 +248,7 @@ impl OpenGLVisitor { pub unsafe fn clear(&self, color: C, depth: D, stencil: S) -> Result<()> where - C: Into>, + C: Into>>, D: Into>, S: Into>, { @@ -260,7 +259,7 @@ impl OpenGLVisitor { let mut bits = 0; if let Some(v) = color { bits |= gl::COLOR_BUFFER_BIT; - gl::ClearColor(v.0, v.1, v.2, v.3); + gl::ClearColor(v.r, v.g, v.b, v.a); } if let Some(v) = depth { @@ -632,7 +631,7 @@ impl OpenGLVisitor { id: GLuint, format: GLenum, tt: GLenum, - rect: Rect, + rect: math::Aabb2, data: &[u8], ) -> Result<()> { self.bind_texture(0, id)?; @@ -640,10 +639,10 @@ impl OpenGLVisitor { gl::TexSubImage2D( gl::TEXTURE_2D, 0, - rect.min.x, - rect.min.y, - rect.width(), - rect.height(), + rect.min.x as i32, + rect.min.y as i32, + rect.dim().x as i32, + rect.dim().y as i32, format, tt, &data[0] as *const u8 as *const ::std::os::raw::c_void, diff --git a/src/graphics/command.rs b/src/graphics/command.rs index 3a8a8ed..5d0e78b 100644 --- a/src/graphics/command.rs +++ b/src/graphics/command.rs @@ -2,8 +2,8 @@ use super::*; use super::errors::*; use graphics::assets::prelude::*; - -use utils::{HashValue, Rect}; +use math; +use utils::HashValue; /// `Command` will be executed in sequential order. pub enum Command<'a> { @@ -37,7 +37,7 @@ impl<'a> Command<'a> { Command::IndexBufferUpdate(task) } - pub fn update_texture(texture: TextureHandle, rect: Rect, data: &[u8]) -> Command { + pub fn update_texture(texture: TextureHandle, rect: math::Aabb2, data: &[u8]) -> Command { let task = TextureUpdate { texture: texture, rect: rect, @@ -91,7 +91,7 @@ pub struct IndexBufferUpdate<'a> { #[derive(Debug, Copy, Clone)] pub struct TextureUpdate<'a> { pub(crate) texture: TextureHandle, - pub(crate) rect: Rect, + pub(crate) rect: math::Aabb2, pub(crate) data: &'a [u8], } @@ -149,7 +149,7 @@ impl DrawCall { self.uniforms_len += 1; } - pub fn build(&mut self, index: MeshIndex) -> Result { + pub fn build(&self, index: MeshIndex) -> Result { let task = SliceDrawCall { shader: self.shader, uniforms: &self.uniforms[0..self.uniforms_len], @@ -160,7 +160,7 @@ impl DrawCall { Ok(task) } - pub fn build_from(&mut self, from: usize, len: usize) -> Result { + pub fn build_from(&self, from: usize, len: usize) -> Result { let task = SliceDrawCall { shader: self.shader, uniforms: &self.uniforms[0..self.uniforms_len], @@ -171,7 +171,7 @@ impl DrawCall { Ok(task) } - pub fn build_sub_mesh(&mut self, index: usize) -> Result { + pub fn build_sub_mesh(&self, index: usize) -> Result { let task = SliceDrawCall { shader: self.shader, uniforms: &self.uniforms[0..self.uniforms_len], diff --git a/src/graphics/service.rs b/src/graphics/service.rs index fcd26d3..dad56a4 100644 --- a/src/graphics/service.rs +++ b/src/graphics/service.rs @@ -3,10 +3,9 @@ use std::sync::{Arc, RwLock}; use std::time::Duration; -use utils::Rect; - use resource::prelude::*; use resource::utils::prelude::*; +use math; use graphics::errors::{Error, Result}; use graphics::assets::prelude::*; @@ -696,7 +695,12 @@ impl GraphicsSystemShared { } /// Update a contiguous subregion of an existing two-dimensional texture object. - pub fn update_texture(&self, handle: TextureHandle, rect: Rect, data: &[u8]) -> Result<()> { + pub fn update_texture( + &self, + handle: TextureHandle, + rect: math::Aabb2, + data: &[u8], + ) -> Result<()> { if let Some(state) = self.textures.read().unwrap().get(handle) { if let AssetState::Ready(ref texture) = *state { if texture.hint == TextureHint::Immutable { diff --git a/src/lib.rs b/src/lib.rs index c10acff..3816b9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,9 +34,8 @@ extern crate cgmath; extern crate failure; extern crate gl; extern crate glutin; -extern crate libc; -extern crate rayon; extern crate zip; +pub extern crate rayon; #[macro_use] pub mod utils; diff --git a/src/math/color.rs b/src/math/color.rs new file mode 100644 index 0000000..132e54d --- /dev/null +++ b/src/math/color.rs @@ -0,0 +1,163 @@ +use cgmath::BaseFloat; + +/// A RGBA `Color`. Each color component is a floating point value +/// with a range from 0 to 1. +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct Color { + pub r: S, + pub g: S, + pub b: S, + pub a: S, +} + +impl Into for Color { + fn into(self) -> u32 { + let color = self.clip(); + let mut encoded = ((color.r / 1.0 * 255.0) as u32) << 24; + encoded |= ((color.g / 1.0 * 255.0) as u32) << 16; + encoded |= ((color.b / 1.0 * 255.0) as u32) << 8; + encoded |= (color.a / 1.0 * 255.0) as u32; + encoded + } +} + +impl From for Color { + fn from(encoded: u32) -> Self { + let max = S::from(255.0).unwrap(); + Color::new( + S::from((encoded >> 24) & 0xFF).unwrap() / max, + S::from((encoded >> 16) & 0xFF).unwrap() / max, + S::from((encoded >> 8) & 0xFF).unwrap() / max, + S::from(encoded & 0xFF).unwrap() / max, + ) + } +} + +impl Into<[u8; 4]> for Color { + fn into(self) -> [u8; 4] { + let v = self.clip(); + let max = 255.0; + [ + (v.r * max) as u8, + (v.g * max) as u8, + (v.b * max) as u8, + (v.a * max) as u8, + ] + } +} + +impl From<[u8; 4]> for Color { + fn from(v: [u8; 4]) -> Self { + let max = S::from(255.0).unwrap(); + Color::new( + S::from(v[0]).unwrap() / max, + S::from(v[1]).unwrap() / max, + S::from(v[2]).unwrap() / max, + S::from(v[3]).unwrap() / max, + ) + } +} + +impl Color { + pub fn new(r: S, g: S, b: S, a: S) -> Self { + Color { + r: r, + g: g, + b: b, + a: a, + } + } + + /// Creates `Color` from a u32 encoded `ARGB`. + pub fn from_argb_u32(encoded: u32) -> Self { + let max = S::from(255.0).unwrap(); + Color::new( + S::from((encoded >> 16) & 0xFF).unwrap() / max, + S::from((encoded >> 8) & 0xFF).unwrap() / max, + S::from(encoded & 0xFF).unwrap() / max, + S::from((encoded >> 24) & 0xFF).unwrap() / max, + ) + } + + /// Creates `Color` from a u32 encoded `ABGR`. + pub fn from_abgr_u32(encoded: u32) -> Self { + let max = S::from(255.0).unwrap(); + Color::new( + S::from(encoded & 0xFF).unwrap() / max, + S::from((encoded >> 8) & 0xFF).unwrap() / max, + S::from((encoded >> 16) & 0xFF).unwrap() / max, + S::from((encoded >> 24) & 0xFF).unwrap() / max, + ) + } + + /// Returns the `grayscale` representation of RGB values. + pub fn grayscale(&self) -> S { + let fr = S::from(0.299).unwrap(); + let fg = S::from(0.587).unwrap(); + let fb = S::from(0.114).unwrap(); + + self.r * fr + self.g * fg + self.b * fb + } + + /// Clip to [0.0, 1.0] range. + pub fn clip(&self) -> Self { + let mut color = *self; + color.r = self.r.max(S::zero()).min(S::one()); + color.g = self.g.max(S::zero()).min(S::one()); + color.b = self.b.max(S::zero()).min(S::one()); + color.a = self.a.max(S::zero()).min(S::one()); + color + } + + /// Truncate alpha channel. + pub fn rgb(&self) -> [S; 3] { + [self.r, self.g, self.b] + } + + pub fn rgba(&self) -> [S; 4] { + [self.r, self.g, self.b, self.a] + } +} + +impl Color { + pub fn white() -> Self { + Color::new(S::one(), S::one(), S::one(), S::one()) + } + + pub fn gray() -> Self { + let half = S::from(0.5).unwrap(); + Color::new(half, half, half, S::one()) + } + + pub fn black() -> Self { + Color::new(S::zero(), S::zero(), S::zero(), S::one()) + } + + pub fn red() -> Self { + Color::new(S::one(), S::zero(), S::zero(), S::one()) + } + + pub fn green() -> Self { + Color::new(S::zero(), S::one(), S::zero(), S::one()) + } + + pub fn blue() -> Self { + Color::new(S::zero(), S::zero(), S::one(), S::one()) + } + + pub fn cyan() -> Self { + Color::new(S::zero(), S::one(), S::one(), S::one()) + } + + pub fn magenta() -> Self { + Color::new(S::one(), S::zero(), S::one(), S::one()) + } + + pub fn yellow() -> Self { + Color::new(S::one(), S::one(), S::zero(), S::one()) + } + + pub fn transparent() -> Self { + Color::new(S::zero(), S::zero(), S::zero(), S::zero()) + } +} diff --git a/src/math/mod.rs b/src/math/mod.rs index f8550f1..8829d51 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -10,3 +10,6 @@ pub use self::aabb::{Aabb2, Aabb3}; pub mod frustum; pub use self::frustum::{Frustum, FrustumPoints, Projection}; + +pub mod color; +pub use self::color::Color; diff --git a/src/prelude.rs b/src/prelude.rs index 142c64a..4329fa6 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -5,9 +5,6 @@ pub use application; pub use application::{Application, Context, Engine, FrameInfo, Settings, TimeSystem}; pub use application::{event, time}; -pub use utils; -pub use utils::{Color, Rect}; - pub use resource::prelude::*; pub use input::prelude::*; pub use graphics::prelude::*; diff --git a/src/utils/color.rs b/src/utils/color.rs deleted file mode 100644 index c97d4fc..0000000 --- a/src/utils/color.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::f32; - -/// A RGBA `Color`. Each color component is a floating point value -/// with a range from 0 to 1. -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct Color(pub f32, pub f32, pub f32, pub f32); - -impl Into for Color { - fn into(self) -> u32 { - let color = self.clip(); - let mut encoded = ((color.0 / 1.0 * 255.0) as u32) << 24; - encoded |= ((color.1 / 1.0 * 255.0) as u32) << 16; - encoded |= ((color.2 / 1.0 * 255.0) as u32) << 8; - encoded |= (color.3 / 1.0 * 255.0) as u32; - encoded - } -} - -impl From for Color { - fn from(encoded: u32) -> Self { - Color( - ((encoded >> 24) & 0xFF) as f32 / 255.0, - ((encoded >> 16) & 0xFF) as f32 / 255.0, - ((encoded >> 8) & 0xFF) as f32 / 255.0, - (encoded & 0xFF) as f32 / 255.0, - ) - } -} - -impl Into<[u8; 4]> for Color { - fn into(self) -> [u8; 4] { - [ - (self.0 / 1.0 * 255.0) as u8, - (self.1 / 1.0 * 255.0) as u8, - (self.2 / 1.0 * 255.0) as u8, - (self.3 / 1.0 * 255.0) as u8, - ] - } -} - -impl From<[u8; 4]> for Color { - fn from(v: [u8; 4]) -> Self { - Color( - f32::from(v[0]) / 255.0, - f32::from(v[1]) / 255.0, - f32::from(v[2]) / 255.0, - f32::from(v[3]) / 255.0, - ) - } -} - -impl Into<[f32; 4]> for Color { - fn into(self) -> [f32; 4] { - [self.0, self.1, self.2, self.3] - } -} - -impl Color { - /// Creates `Color` from a u32 encoded `ARGB`. - pub fn from_argb_u32(encoded: u32) -> Self { - Color( - ((encoded >> 16) & 0xFF) as f32 / 255.0, - ((encoded >> 8) & 0xFF) as f32 / 255.0, - (encoded & 0xFF) as f32 / 255.0, - ((encoded >> 24) & 0xFF) as f32 / 255.0, - ) - } - - /// Creates `Color` from a u32 encoded `ABGR`. - pub fn from_abgr_u32(encoded: u32) -> Self { - Color( - (encoded & 0xFF) as f32 / 255.0, - ((encoded >> 8) & 0xFF) as f32 / 255.0, - ((encoded >> 16) & 0xFF) as f32 / 255.0, - ((encoded >> 24) & 0xFF) as f32 / 255.0, - ) - } - - /// Returns the `grayscale` representation of RGB values. - pub fn grayscale(&self) -> f32 { - self.0 * 0.299 + self.1 * 0.587 + self.2 * 0.114 - } - - /// Clip to [0.0, 1.0] range. - pub fn clip(&self) -> Color { - let mut color = *self; - color.0 = clamp(self.0, 0.0, 1.0); - color.1 = clamp(self.1, 0.0, 1.0); - color.2 = clamp(self.2, 0.0, 1.0); - color.3 = clamp(self.3, 0.0, 1.0); - color - } - - /// Truncate alpha channel. - pub fn rgb(&self) -> [f32; 3] { - [self.0, self.1, self.2] - } -} - -impl Color { - pub fn white() -> Self { - Color(1.0, 1.0, 1.0, 1.0) - } - - pub fn gray() -> Self { - Color(0.5, 0.5, 0.5, 1.0) - } - - pub fn black() -> Self { - Color(0.0, 0.0, 0.0, 1.0) - } - - pub fn red() -> Self { - Color(1.0, 0.0, 0.0, 1.0) - } - - pub fn green() -> Self { - Color(0.0, 1.0, 0.0, 1.0) - } - - pub fn blue() -> Self { - Color(0.0, 0.0, 1.0, 1.0) - } - - pub fn cyan() -> Self { - Color(0.0, 1.0, 1.0, 1.0) - } - - pub fn magenta() -> Self { - Color(1.0, 0.0, 1.0, 1.0) - } - - pub fn yellow() -> Self { - Color(1.0, 1.0, 0.0, 1.0) - } - - pub fn transparent() -> Self { - Color(0.0, 0.0, 0.0, 0.0) - } -} - -fn clamp(v: f32, min: f32, max: f32) -> f32 { - let mut v = v; - - if v < min { - v = min; - } - - if v > max { - v = max; - } - - v -} diff --git a/src/utils/data_buf.rs b/src/utils/data_buf.rs index 9ca9e33..642b486 100644 --- a/src/utils/data_buf.rs +++ b/src/utils/data_buf.rs @@ -3,8 +3,6 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::borrow::Borrow; -use utils; - /// Where we store all the intermediate bytes. #[derive(Debug, Clone)] pub struct DataBuffer(Vec, HashMap>); @@ -55,19 +53,25 @@ impl DataBuffer { where T: Borrow, { - let v = utils::hash(&value.borrow()); - if let Some(ptr) = self.1.get(&v) { + use std::hash::{Hash, Hasher}; + use std::collections::hash_map::DefaultHasher; + + let value = value.borrow(); + let mut s = DefaultHasher::new(); + value.hash(&mut s); + let hash_value = s.finish(); + if let Some(ptr) = self.1.get(&hash_value) { return *ptr; } - let slice = self.extend_from_slice(value.borrow().as_bytes()); + let slice = self.extend_from_slice(value.as_bytes()); let ptr = DataBufferPtr { position: slice.position, size: slice.size, _phantom: PhantomData, }; - self.1.insert(v, ptr); + self.1.insert(hash_value, ptr); ptr } diff --git a/src/utils/finally.rs b/src/utils/finally.rs deleted file mode 100644 index 3511c74..0000000 --- a/src/utils/finally.rs +++ /dev/null @@ -1,67 +0,0 @@ -pub fn finally_with(arg: A, func: F) -> FinallyGuardWith -where - F: FnMut(&mut A), -{ - FinallyGuardWith { - arg: arg, - func: func, - } -} - -pub fn finally(func: F) -> FinallyGuard -where - F: FnMut(), -{ - FinallyGuard { func: func } -} - -pub struct FinallyGuardWith -where - F: FnMut(&mut A), -{ - arg: A, - func: F, -} - -impl FinallyGuardWith -where - F: FnMut(&mut A), -{ - pub fn forget(self) { - ::std::mem::forget(self); - } -} - -impl Drop for FinallyGuardWith -where - F: FnMut(&mut A), -{ - fn drop(&mut self) { - (self.func)(&mut self.arg) - } -} - -pub struct FinallyGuard -where - F: FnMut(), -{ - func: F, -} - -impl FinallyGuard -where - F: FnMut(), -{ - pub fn forget(self) { - ::std::mem::forget(self); - } -} - -impl Drop for FinallyGuard -where - F: FnMut(), -{ - fn drop(&mut self) { - (self.func)() - } -} diff --git a/src/utils/hash.rs b/src/utils/hash.rs deleted file mode 100644 index 9083962..0000000 --- a/src/utils/hash.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::hash::{Hash, Hasher}; -use std::collections::hash_map::DefaultHasher; - -pub fn hash(t: &T) -> u64 { - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() -} diff --git a/src/utils/hash_value.rs b/src/utils/hash_value.rs index 787dbda..b011803 100644 --- a/src/utils/hash_value.rs +++ b/src/utils/hash_value.rs @@ -2,8 +2,6 @@ use std::path::Path; use std::marker::PhantomData; use std::hash::{Hash, Hasher}; -use utils::hash; - #[derive(Debug, Eq)] pub struct HashValue(u64, PhantomData) where @@ -90,6 +88,14 @@ where } } +fn hash(t: &T) -> u64 { + use std::collections::hash_map::DefaultHasher; + + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + #[cfg(test)] mod test { use std::collections::HashSet; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index e324753..0495fb6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,21 +3,12 @@ #[macro_use] pub mod handle; pub mod handle_pool; -pub mod hash; pub mod hash_value; pub mod variant; pub mod data_buf; -mod finally; -mod color; -mod rect; - pub use self::handle::{Handle, HandleIndex}; pub use self::handle_pool::HandlePool; -pub use self::finally::{finally, finally_with}; -pub use self::hash::hash; pub use self::hash_value::HashValue; pub use self::variant::{VariantChar, VariantStr}; pub use self::data_buf::{DataBuffer, DataBufferPtr}; -pub use self::rect::*; -pub use self::color::*; diff --git a/src/utils/rect.rs b/src/utils/rect.rs deleted file mode 100644 index 1f19b46..0000000 --- a/src/utils/rect.rs +++ /dev/null @@ -1,54 +0,0 @@ -use math; - -/// A rectangle, with top-left corner at `min`, and bottom-right corner at `max`. -#[derive(Copy, Clone, Debug)] -pub struct Rect { - pub min: math::Point2, - pub max: math::Point2, -} - -impl Rect { - #[inline] - pub fn new(min: math::Point2, max: math::Point2) -> Self { - Rect { min: min, max: max } - } - - #[inline] - pub fn width(&self) -> i32 { - self.max.x - self.min.x - } - - #[inline] - pub fn height(&self) -> i32 { - self.max.y - self.min.y - } - - #[inline] - pub fn size(&self) -> i32 { - self.width() * self.height() - } - - #[inline] - pub fn overlap(&self, rhs: Self) -> Self { - use std::cmp; - Rect { - min: math::Point2::new( - cmp::max(self.min.x, rhs.min.x), - cmp::max(self.min.y, rhs.min.y), - ), - max: math::Point2::new( - cmp::min(self.max.x, rhs.max.x), - cmp::min(self.max.y, rhs.max.y), - ), - } - } - - #[inline] - pub fn contains

(&self, p: P) -> bool - where - P: Into>, - { - let p = p.into(); - p.x >= self.min.x && p.x < self.max.x && p.y >= self.min.y && p.y < self.max.y - } -} diff --git a/tests/ecs.rs b/tests/ecs.rs index 384b921..1005322 100644 --- a/tests/ecs.rs +++ b/tests/ecs.rs @@ -1,4 +1,6 @@ extern crate crayon; +#[macro_use] +extern crate failure; extern crate rand; use crayon::ecs::prelude::*; @@ -31,35 +33,6 @@ impl Component for Reference { type Arena = HashMapArena; } -// struct IncXSystem {} -// struct DecXSystem {} - -// impl<'a> SystemMut<'a> for IncXSystem { -// type ViewWithMut = FetchMut<'a, Position>; -// type ResultMut = (); - -// fn run_mut(&mut self, view: View, mut arena: Self::ViewWithMut) { -// unsafe { -// for v in view { -// arena.get_unchecked_mut(v).x += 1; -// } -// } -// } -// } - -// impl<'a> SystemMut<'a> for DecXSystem { -// type ViewWithMut = FetchMut<'a, Position>; -// type ResultMut = (); - -// fn run_mut(&mut self, view: View, mut arena: Self::ViewWithMut) { -// unsafe { -// for v in view { -// arena.get_unchecked_mut(v).x -= 1; -// } -// } -// } -// } - #[test] fn basic() { let mut world = World::new(); @@ -312,19 +285,109 @@ fn builder() { assert!(!world.has::(e1)); } -// #[test] -// fn system() { -// let mut world = World::new(); -// world.register::(); -// let e1 = world.build().with_default::().finish(); +#[derive(Debug, Fail)] +pub enum Error { + #[fail(display = "None")] _None, +} + +pub type Result = ::std::result::Result<(), Error>; -// let mut inc = IncXSystem {}; -// inc.run_mut_at(&mut world); -// assert!(world.get::(e1).unwrap().x == 1); +struct IncXSystem<'s> { + value: &'s u32, +} + +impl<'a, 's> System<'a> for IncXSystem<'s> { + type Data = FetchMut<'a, Position>; + type Err = Error; + + fn run(&mut self, entities: Entities, data: Self::Data) -> Result { + for mut v in data.join(&entities) { + v.x += *self.value; + } -// let mut dec = DecXSystem {}; -// dec.run_mut_at(&mut world); -// assert!(world.get::(e1).unwrap().x == 0); + Ok(()) + } +} + +struct MulXSystem {} + +impl<'a> System<'a> for MulXSystem { + type Data = FetchMut<'a, Position>; + type Err = Error; + + fn run(&mut self, entities: Entities, data: Self::Data) -> Result { + for mut v in data.join(&entities) { + v.x *= 2; + } -// // assert!(!validate(&world, &[&inc, &dec])); -// } + Ok(()) + } +} + +#[test] +fn system() { + let mut world = World::new(); + world.register::(); + let e1 = world.build().with_default::().finish(); + + let value = 1; + let mut v1 = 0; + + { + let mut dispatcher = SystemDispatcher::new(); + + dispatcher.add_w1( + &[], + |entities: Entities, positions: FetchMut| -> Result { + for mut v in positions.join(&entities) { + v.x += value; + } + + v1 = 1; + Ok(()) + }, + ); + + dispatcher.run(&mut world).unwrap(); + } + assert_eq!(world.get::(e1).unwrap().x, 1); + assert_eq!(v1, 1); + + { + let mut inc = IncXSystem { value: &value }; + inc.run_with_mut(&mut world).unwrap(); + } + assert_eq!(world.get::(e1).unwrap().x, 2); + + { + let mut dispatcher = SystemDispatcher::new(); + dispatcher.add(&[], IncXSystem { value: &value }); + dispatcher.run(&mut world).unwrap(); + } + assert_eq!(world.get::(e1).unwrap().x, 3); +} + +#[test] +fn system_dependencies() { + let mut world = World::new(); + world.register::(); + let e1 = world.build().with_default::().finish(); + + let value = 1; + + { + let mut dispatcher = SystemDispatcher::new(); + let s1 = dispatcher.add(&[], IncXSystem { value: &value }); + dispatcher.add(&[s1], MulXSystem {}); + dispatcher.run(&mut world).unwrap(); + assert_eq!(world.get::(e1).unwrap().x, 2); + } + + { + let mut dispatcher = SystemDispatcher::new(); + let s1 = dispatcher.add(&[], MulXSystem {}); + dispatcher.add(&[s1], IncXSystem { value: &value }); + dispatcher.run(&mut world).unwrap(); + assert_eq!(world.get::(e1).unwrap().x, 5); + } +}