From c581bd88aeec0bf4b4570a18e19e8f164947558e Mon Sep 17 00:00:00 2001 From: Matt Kleinschafer Date: Tue, 30 Jan 2024 15:45:32 +1100 Subject: [PATCH 1/3] More work on rendering --- modules/graphics/src/headless.rs | 18 +---- modules/graphics/src/lib.rs | 3 +- modules/graphics/src/opengl.rs | 28 +++---- modules/graphics/src/rendering/commands.rs | 79 ++++++++++---------- modules/graphics/src/rendering/culling.rs | 86 +++++++++++++--------- modules/graphics/src/targets.rs | 33 ++------- 6 files changed, 106 insertions(+), 141 deletions(-) diff --git a/modules/graphics/src/headless.rs b/modules/graphics/src/headless.rs index 06288310..67a6062e 100644 --- a/modules/graphics/src/headless.rs +++ b/modules/graphics/src/headless.rs @@ -221,27 +221,15 @@ impl GraphicsBackend for HeadlessGraphicsBackend { Ok(()) } - fn target_blit( - &self, - from: TargetId, - to: TargetId, - source_rect: &Rectangle, - dest_rect: &Rectangle, - filter: TextureFilter, - ) -> Result<(), TargetError> { - Ok(()) - } - - fn target_blit_to_display( + fn target_blit_to_active( &self, target: TargetId, - source_rect: &Rectangle, - dest_rect: &Rectangle, + source_rect: Option, + dest_rect: Option, filter: TextureFilter, ) -> Result<(), TargetError> { Ok(()) } - fn target_delete(&self, target: TargetId) -> Result<(), TargetError> { Ok(()) } diff --git a/modules/graphics/src/lib.rs b/modules/graphics/src/lib.rs index e8f8f72c..4dc744c3 100644 --- a/modules/graphics/src/lib.rs +++ b/modules/graphics/src/lib.rs @@ -150,7 +150,6 @@ pub trait GraphicsBackend { fn target_create(&self, color_attachment: TextureId, depth_attachment: Option, stencil_attachment: Option) -> Result; fn target_activate(&self, target: TargetId) -> Result<(), TargetError>; fn target_set_default(&self) -> Result<(), TargetError>; - fn target_blit(&self, from: TargetId, to: TargetId, source_rect: &common::Rectangle, dest_rect: &common::Rectangle, filter: TextureFilter) -> Result<(), TargetError>; - fn target_blit_to_display(&self, target: TargetId, source_rect: &common::Rectangle, dest_rect: &common::Rectangle, filter: TextureFilter) -> Result<(), TargetError>; + fn target_blit_to_active(&self, target: TargetId, source_rect: Option, dest_rect: Option, filter: TextureFilter) -> Result<(), TargetError>; fn target_delete(&self, target: TargetId) -> Result<(), TargetError>; } diff --git a/modules/graphics/src/opengl.rs b/modules/graphics/src/opengl.rs index bf595904..ea56451e 100644 --- a/modules/graphics/src/opengl.rs +++ b/modules/graphics/src/opengl.rs @@ -800,17 +800,19 @@ impl GraphicsBackend for OpenGLGraphicsBackend { } } - fn target_blit( + fn target_blit_to_active( &self, - from: TargetId, - to: TargetId, - source_rect: &Rectangle, - dest_rect: &Rectangle, + target: TargetId, + source_rect: Option, + dest_rect: Option, filter: TextureFilter, ) -> Result<(), TargetError> { unsafe { - gl::BindFramebuffer(gl::READ_FRAMEBUFFER, from.into()); - gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, to.into()); + gl::BindFramebuffer(gl::READ_FRAMEBUFFER, target.into()); + gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, TargetId::NONE.into()); + + let source_rect = source_rect.unwrap_or(Rectangle::from_corner_points(0., 0., 1., 1.)); + let dest_rect = dest_rect.unwrap_or(Rectangle::from_corner_points(0., 0., 1., 1.)); gl::BlitFramebuffer( source_rect.left() as i32, @@ -835,18 +837,6 @@ impl GraphicsBackend for OpenGLGraphicsBackend { } } - fn target_blit_to_display( - &self, - handle: TargetId, - source_rect: &Rectangle, - dest_rect: &Rectangle, - filter: TextureFilter, - ) -> Result<(), TargetError> { - self.target_blit(handle, TargetId::NONE, source_rect, dest_rect, filter)?; - - Ok(()) - } - fn target_delete(&self, target: TargetId) -> Result<(), TargetError> { unsafe { gl::DeleteFramebuffers(1, &target.into()); diff --git a/modules/graphics/src/rendering/commands.rs b/modules/graphics/src/rendering/commands.rs index af4a4ac0..8462a9b6 100644 --- a/modules/graphics/src/rendering/commands.rs +++ b/modules/graphics/src/rendering/commands.rs @@ -1,5 +1,3 @@ -//! Command queueing for common operations against the renderer. - use std::sync::Mutex; use super::*; @@ -17,32 +15,6 @@ pub struct RenderQueue { commands: Mutex>, } -#[derive(Debug)] -pub enum RenderError { - BufferError(BufferError), - TextureError(TextureError), - ShaderError(ShaderError), - MeshError(MeshError), - TargetError(TargetError), -} - -macro_rules! impl_from_error { - ($error:tt) => { - impl From<$error> for RenderError { - #[inline(always)] - fn from(error: $error) -> Self { - Self::$error(error) - } - } - }; -} - -impl_from_error!(BufferError); -impl_from_error!(TextureError); -impl_from_error!(ShaderError); -impl_from_error!(MeshError); -impl_from_error!(TargetError); - /// A single command for a [`RenderQueue`] to execute. #[allow(dead_code)] enum RenderCommand { @@ -81,8 +53,37 @@ enum RenderCommand { vertex_count: usize, index_count: usize, }, + /// Blits the given render target to the active render target. + BlitRenderTargetToActive { target_id: TargetId, filter: TextureFilter }, } +/// Represents an error that occurred while using the render queue. +#[derive(Debug)] +pub enum RenderQueueError { + BufferError(BufferError), + TextureError(TextureError), + ShaderError(ShaderError), + MeshError(MeshError), + TargetError(TargetError), +} + +macro_rules! impl_from_error { + ($error:tt) => { + impl From<$error> for RenderQueueError { + #[inline(always)] + fn from(error: $error) -> Self { + Self::$error(error) + } + } + }; +} + +impl_from_error!(BufferError); +impl_from_error!(TextureError); +impl_from_error!(ShaderError); +impl_from_error!(MeshError); +impl_from_error!(TargetError); + impl RenderQueue { /// Creates a new [`RenderQueue`]. pub fn new() -> Self { @@ -138,19 +139,10 @@ impl RenderQueue { self.enqueue(RenderCommand::ClearColorBuffer { color }); } - // self.enqueue(RenderCommand::SetShader { - // shader_id: target.blit_shader().id(), - // uniforms: Box::new(target.blit_uniforms().clone()), - // blend_state: BlendState::Disabled, - // culling_mode: CullingMode::Disabled, - // scissor_mode: ScissorMode::Disabled, - // }); - // self.enqueue(RenderCommand::DrawMesh { - // mesh_id: target.blit_mesh().id(), - // topology: PrimitiveTopology::TriangleList, - // vertex_count: target.blit_mesh().vertices(), - // index_count: target.blit_mesh().indices(), - // }); + self.enqueue(RenderCommand::BlitRenderTargetToActive { + target_id: target.id(), + filter: TextureFilter::Linear, + }); } /// Clears all [`RenderCommand`] from the queue. @@ -161,7 +153,7 @@ impl RenderQueue { } /// Flushes all [`RenderCommand`]s in the queue to the given renderer. - pub fn flush(&mut self, graphics: &GraphicsEngine) -> Result<(), RenderError> { + pub fn flush(&mut self, graphics: &GraphicsEngine) -> Result<(), RenderQueueError> { let mut commands = self.commands.lock().unwrap(); for command in commands.drain(..) { @@ -225,6 +217,9 @@ impl RenderQueue { } => { graphics.mesh_draw(mesh_id, topology, vertex_count, index_count)?; } + RenderCommand::BlitRenderTargetToActive { target_id, filter } => { + graphics.target_blit_to_active(target_id, None, None, filter)?; + } } } diff --git a/modules/graphics/src/rendering/culling.rs b/modules/graphics/src/rendering/culling.rs index 2fb7e019..9b955453 100644 --- a/modules/graphics/src/rendering/culling.rs +++ b/modules/graphics/src/rendering/culling.rs @@ -3,7 +3,39 @@ use common::Frustum; use super::*; -// TODO: flesh out the culling system and add some tests +/// A set of visible objects that can be rendered in a scene. +/// +/// This is a subset of the objects in a scene that are visible to a specific +/// camera, and can be used to optimize rendering by only rendering the objects +/// that are visible to the camera. +pub struct VisibleObjectSet<'a> { + /// The frustum of the camera that was used to cull the objects. + pub frustum: Frustum, + /// The objects that are visible to the camera. + pub objects: Vec>, +} + +/// Represents an object that is visible to a camera. +/// +/// This is a reference to an object in a scene, along with the material that +/// should be used to render it. +/// +/// This is used to sort objects into batches for efficient rendering, +/// minimizing state changes between draw calls. +pub struct VisibleObject<'a> { + /// The object itself. + pub object: &'a dyn RenderObject, + /// The material of the object. + pub material: &'a Material, +} + +/// A key that can be used to uniquely identify the kind of material. +/// +/// This is used to sort materials into batches for efficient rendering, +/// minimizing state changes between draw calls. +#[repr(transparent)] +#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] +pub struct MaterialSortingKey(u64); bitflags! { /// Flags that indicate the required state of the graphics pipeline for a material. @@ -20,48 +52,29 @@ bitflags! { } } -/// A key that can be used to uniquely identify the kind of material. -/// -/// This is used to sort materials into batches for efficient rendering, -/// minimizing state changes between draw calls. -#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] -pub struct MaterialSortingKey { - flags: MaterialFlags, -} - -/// Represents an object that is visible to a camera. -pub struct VisibleObject<'a> { - /// The object itself. - pub object: &'a dyn RenderObject, - /// The material of the object. - pub material: &'a Material, -} - -/// A set of visible objects that can be rendered in a scene. -/// -/// This is a subset of the objects in a scene that are visible to a specific -/// camera, and can be used to optimize rendering by only rendering the objects -/// that are visible to the camera. -pub struct VisibleObjectSet<'a> { - /// The frustum of the camera that was used to cull the objects. - pub frustum: Frustum, - /// The objects that are visible to the camera. - pub objects: Vec>, -} - impl<'a> VisibleObjectSet<'a> { - /// Gets an iterator over the objects in the set. + /// Groups the objects by material sorting key. pub fn group_by_material(&self) -> impl Iterator])> { self .objects - .chunk_by(|a, b| MaterialSortingKey::from(a.material) == MaterialSortingKey::from(b.material)) + .chunk_by(|a, b| { + let a = MaterialSortingKey::for_material(a.material); + let b = MaterialSortingKey::for_material(b.material); + + a == b + }) .map(|chunk| (chunk[0].material, chunk)) } } -impl From<&Material> for MaterialSortingKey { +impl MaterialSortingKey { /// Gets the sorting key for the given material. - fn from(material: &Material) -> Self { + /// + /// A sorting key is defined as a 64-bit integer, where the first 32 bits + /// represent the flags that indicate the required state of the graphics + /// pipeline, and the last 32 bits represent the ID of the shader that should + /// be used to render the material. + pub fn for_material(material: &Material) -> Self { let mut flags = MaterialFlags::empty(); let shader = material.shader(); @@ -91,6 +104,9 @@ impl From<&Material> for MaterialSortingKey { flags.insert(MaterialFlags::DEPTH_WRITING); } - Self { flags } + let flags = u64::from(flags.bits()); + let shader = u64::from(shader.id()); + + Self(flags << 32 | shader) } } diff --git a/modules/graphics/src/targets.rs b/modules/graphics/src/targets.rs index 4456b667..774aae1c 100644 --- a/modules/graphics/src/targets.rs +++ b/modules/graphics/src/targets.rs @@ -6,8 +6,6 @@ use std::{cell::RefCell, rc::Rc}; -use common::Rectangle; - use super::*; /// Describes how to build a [`RenderTarget`]. @@ -130,34 +128,13 @@ impl RenderTarget { .expect("Failed to deactivate render target"); } - /// Blits this render target to the given other target. - pub fn blit_to(&self, other: &RenderTarget, filter: TextureFilter) { - let state = self.state.borrow(); - - let source_color = &state.color_attachment; - let source = Rectangle::from_corner_points(0., 0., source_color.width() as f32, source_color.height() as f32); - let dest_color = other.color_attachment(); - let dest = Rectangle::from_corner_points(0., 0., dest_color.width() as f32, dest_color.height() as f32); - - state - .graphics - .target_blit(state.id, other.id(), &source, &dest, filter) - .expect("Failed to blit render target"); - } - - /// Blits this render target to the active display. - pub fn blit_to_display(&self, filter: TextureFilter) { + /// Blits this render target to the active target. + pub fn blit_to_active(&self, filter: TextureFilter) { let state = self.state.borrow(); - let color = &state.color_attachment; - - let (width, height) = state.graphics.viewport_size(); + let graphics = &state.graphics; - let source = Rectangle::from_corner_points(0., 0., color.width() as f32, color.height() as f32); - let dest = Rectangle::from_corner_points(0., 0., width as f32, height as f32); - - state - .graphics - .target_blit_to_display(state.id, &source, &dest, filter) + graphics + .target_blit_to_active(state.id, None, None, filter) .expect("Failed to blit render target to display"); } } From e04d92e50dbf557214c24717e2a836e8f0e8e8e7 Mon Sep 17 00:00:00 2001 From: Matt Kleinschafer Date: Tue, 30 Jan 2024 15:54:17 +1100 Subject: [PATCH 2/3] More sketching --- modules/graphics/Cargo.toml | 2 - modules/graphics/src/lib.rs | 2 - modules/physics/src/box2d.rs | 228 +++++++++++++++++++++++++++++++++++ modules/physics/src/lib.rs | 9 +- 4 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 modules/physics/src/box2d.rs diff --git a/modules/graphics/Cargo.toml b/modules/graphics/Cargo.toml index 66f30a54..fe7a1966 100644 --- a/modules/graphics/Cargo.toml +++ b/modules/graphics/Cargo.toml @@ -9,7 +9,5 @@ common = { package = "surreal-common", path = "../../common" } macros = { package = "surreal-macros", path = "../../macros" } serde = { workspace = true, optional = true } bitflags = { workspace = true } - -# local dependencies image = { version = "0.24.8", default-features = false, features = ["png"] } gl = "0.14.0" diff --git a/modules/graphics/src/lib.rs b/modules/graphics/src/lib.rs index 4dc744c3..7c0e81f6 100644 --- a/modules/graphics/src/lib.rs +++ b/modules/graphics/src/lib.rs @@ -91,8 +91,6 @@ pub enum TargetError { FailedToBuildAttachments, } -pub trait GraphicsVisitor {} - /// An abstraction on top of the underlying graphics API. /// /// This is a mid-level abstraction that makes use of 'opaque' resource IDs to diff --git a/modules/physics/src/box2d.rs b/modules/physics/src/box2d.rs new file mode 100644 index 00000000..606c0f24 --- /dev/null +++ b/modules/physics/src/box2d.rs @@ -0,0 +1,228 @@ +use super::*; + +/// A Bullet [`PhysicsBackend`] implementation. +#[derive(Default)] +pub struct Box2dPhysicsBackend; + +#[allow(unused_variables)] +impl PhysicsBackend for Box2dPhysicsBackend { + fn step(&self, delta_time: f32) { + todo!() + } + + fn reset(&self) { + todo!() + } + + fn body_create(&self, kind: BodyKind, initial_position: Vec3) -> BodyId { + todo!() + } + + fn body_add_collider(&self, body: BodyId, collider: ColliderId) { + todo!() + } + + fn body_remove_collider(&self, body: BodyId, collider: ColliderId) { + todo!() + } + + fn body_set_position(&self, body: BodyId, position: Vec3) { + todo!() + } + + fn body_get_position(&self, body: BodyId) -> Vec3 { + todo!() + } + + fn body_set_rotation(&self, body: BodyId, rotation: Quat) { + todo!() + } + + fn body_get_rotation(&self, body: BodyId) -> Quat { + todo!() + } + + fn body_set_scale(&self, body: BodyId, scale: Vec3) { + todo!() + } + + fn body_get_scale(&self, body: BodyId) -> Vec3 { + todo!() + } + + fn body_set_velocity(&self, body: BodyId, velocity: Vec3) { + todo!() + } + + fn body_get_velocity(&self, body: BodyId) -> Vec3 { + todo!() + } + + fn body_set_angular_velocity(&self, body: BodyId, velocity: Vec3) { + todo!() + } + + fn body_get_angular_velocity(&self, body: BodyId) -> Vec3 { + todo!() + } + + fn body_delete(&self, body: BodyId) { + todo!() + } + + fn collider_create_sphere(&self, kind: ColliderKind, initial_position: Vec3, radius: f32) -> ColliderId { + todo!() + } + + fn collider_create_box(&self, kind: ColliderKind, initial_position: Vec3, size: Vec3) -> ColliderId { + todo!() + } + + fn collider_create_capsule( + &self, + kind: ColliderKind, + initial_position: Vec3, + radius: f32, + height: f32, + ) -> ColliderId { + todo!() + } + + fn collider_create_cylinder( + &self, + kind: ColliderKind, + initial_position: Vec3, + radius: f32, + height: f32, + ) -> ColliderId { + todo!() + } + + fn collider_create_cone(&self, kind: ColliderKind, initial_position: Vec3, radius: f32, height: f32) -> ColliderId { + todo!() + } + + fn collider_create_convex_hull(&self, kind: ColliderKind, initial_position: Vec3, vertices: &[Vec3]) -> ColliderId { + todo!() + } + + fn collider_create_triangle_mesh( + &self, + kind: ColliderKind, + initial_position: Vec3, + vertices: &[Vec3], + indices: &[u32], + ) -> ColliderId { + todo!() + } + + fn collider_create_height_field( + &self, + kind: ColliderKind, + initial_position: Vec3, + size: Vec3, + heights: &[f32], + ) -> ColliderId { + todo!() + } + + fn collider_get_kind(&self, collider: ColliderId) -> ColliderKind { + todo!() + } + + fn collider_set_position(&self, collider: ColliderId, position: Vec3) { + todo!() + } + + fn collider_get_position(&self, collider: ColliderId) -> Vec3 { + todo!() + } + + fn collider_set_rotation(&self, collider: ColliderId, rotation: Quat) { + todo!() + } + + fn collider_get_rotation(&self, collider: ColliderId) -> Quat { + todo!() + } + + fn collider_set_scale(&self, collider: ColliderId, scale: Vec3) { + todo!() + } + + fn collider_get_scale(&self, collider: ColliderId) -> Vec3 { + todo!() + } + + fn collider_delete(&self, collider: ColliderId) { + todo!() + } + + fn effector_create_sphere(&self, kind: EffectorKind, initial_position: Vec3, radius: f32) -> EffectorId { + todo!() + } + + fn effector_create_box(&self, kind: EffectorKind, initial_position: Vec3, size: Vec3) -> EffectorId { + todo!() + } + + fn effector_create_capsule( + &self, + kind: EffectorKind, + initial_position: Vec3, + radius: f32, + height: f32, + ) -> EffectorId { + todo!() + } + + fn effector_create_cylinder( + &self, + kind: EffectorKind, + initial_position: Vec3, + radius: f32, + height: f32, + ) -> EffectorId { + todo!() + } + + fn effector_get_kind(&self, effector: EffectorId) -> EffectorKind { + todo!() + } + + fn effector_set_position(&self, effector: EffectorId, position: Vec3) { + todo!() + } + + fn effector_get_position(&self, effector: EffectorId) -> Vec3 { + todo!() + } + + fn effector_set_rotation(&self, effector: EffectorId, rotation: Quat) { + todo!() + } + + fn effector_get_rotation(&self, effector: EffectorId) -> Quat { + todo!() + } + + fn effector_set_scale(&self, effector: EffectorId, scale: Vec3) { + todo!() + } + + fn effector_get_scale(&self, effector: EffectorId) -> Vec3 { + todo!() + } + + fn effector_set_strength(&self, effector: EffectorId, strength: f32) { + todo!() + } + + fn effector_get_strength(&self, effector: EffectorId) -> f32 { + todo!() + } + + fn effector_delete(&self, effector: EffectorId) { + todo!() + } +} diff --git a/modules/physics/src/lib.rs b/modules/physics/src/lib.rs index 5fa40ce0..3a89d179 100644 --- a/modules/physics/src/lib.rs +++ b/modules/physics/src/lib.rs @@ -4,6 +4,8 @@ pub use scenes::*; +#[cfg(feature = "box2d")] +mod box2d; mod internal; mod scenes; @@ -21,9 +23,10 @@ impl PhysicsEngine { Self::new(internal::InternalPhysicsBackend::default()) } - /// Creates a new [`PhysicsEngine`] with the Bullet backend. - pub fn bullet() -> Self { - todo!() + /// Creates a new [`PhysicsEngine`] with the Box 2D backend. + #[cfg(feature = "box2d")] + pub fn box2d() -> Self { + Self::new(box2d::Box2dPhysicsBackend::default()) } } From 0a2f56c1b407a079297e5760ada66650c82df0ec Mon Sep 17 00:00:00 2001 From: Matt Kleinschafer Date: Tue, 30 Jan 2024 16:00:20 +1100 Subject: [PATCH 3/3] More work on rendering --- modules/graphics/src/materials.rs | 34 ++++++++++++++++++ modules/graphics/src/rendering/culling.rs | 40 ++++++--------------- modules/graphics/src/rendering/pipelines.rs | 6 ++-- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/modules/graphics/src/materials.rs b/modules/graphics/src/materials.rs index 41906dd3..bc2293ad 100644 --- a/modules/graphics/src/materials.rs +++ b/modules/graphics/src/materials.rs @@ -80,6 +80,40 @@ impl Material { &self.shader } + /// Gets the flags of the material. + pub fn flags(&self) -> MaterialFlags { + let mut flags = MaterialFlags::empty(); + + let shader = &self.shader; + let metadata = shader.flags(); + + if self.blend_state() != BlendState::Disabled { + flags.insert(MaterialFlags::ALPHA_BLENDING); + } + + if self.culling_mode() != CullingMode::Disabled { + flags.insert(MaterialFlags::BACKFACE_CULLING); + } + + if self.scissor_mode() != ScissorMode::Disabled { + flags.insert(MaterialFlags::SCISSOR_TESTING); + } + + if metadata.contains(ShaderFlags::ALPHA_TESTING) { + flags.insert(MaterialFlags::ALPHA_TESTING); + } + + if metadata.contains(ShaderFlags::DEPTH_TESTING) { + flags.insert(MaterialFlags::DEPTH_TESTING); + } + + if metadata.contains(ShaderFlags::DEPTH_WRITING) { + flags.insert(MaterialFlags::DEPTH_WRITING); + } + + flags + } + /// Gets the underlying [`ShaderUniformSet`] of the material. pub fn uniforms(&self) -> &ShaderUniformSet { &self.uniforms diff --git a/modules/graphics/src/rendering/culling.rs b/modules/graphics/src/rendering/culling.rs index 9b955453..e6bed68f 100644 --- a/modules/graphics/src/rendering/culling.rs +++ b/modules/graphics/src/rendering/culling.rs @@ -54,7 +54,10 @@ bitflags! { impl<'a> VisibleObjectSet<'a> { /// Groups the objects by material sorting key. - pub fn group_by_material(&self) -> impl Iterator])> { + pub fn group_by_material( + &self, + required_flags: MaterialFlags, + ) -> impl Iterator])> { self .objects .chunk_by(|a, b| { @@ -63,7 +66,12 @@ impl<'a> VisibleObjectSet<'a> { a == b }) - .map(|chunk| (chunk[0].material, chunk)) + .filter(move |it| { + let flags = it[0].material.flags(); + + flags.contains(required_flags) + }) + .map(|it| (it[0].material, it)) } } @@ -75,34 +83,8 @@ impl MaterialSortingKey { /// pipeline, and the last 32 bits represent the ID of the shader that should /// be used to render the material. pub fn for_material(material: &Material) -> Self { - let mut flags = MaterialFlags::empty(); - let shader = material.shader(); - let metadata = shader.flags(); - - if material.blend_state() != BlendState::Disabled { - flags.insert(MaterialFlags::ALPHA_BLENDING); - } - - if material.culling_mode() != CullingMode::Disabled { - flags.insert(MaterialFlags::BACKFACE_CULLING); - } - - if material.scissor_mode() != ScissorMode::Disabled { - flags.insert(MaterialFlags::SCISSOR_TESTING); - } - - if metadata.contains(ShaderFlags::ALPHA_TESTING) { - flags.insert(MaterialFlags::ALPHA_TESTING); - } - - if metadata.contains(ShaderFlags::DEPTH_TESTING) { - flags.insert(MaterialFlags::DEPTH_TESTING); - } - - if metadata.contains(ShaderFlags::DEPTH_WRITING) { - flags.insert(MaterialFlags::DEPTH_WRITING); - } + let flags = material.flags(); let flags = u64::from(flags.bits()); let shader = u64::from(shader.id()); diff --git a/modules/graphics/src/rendering/pipelines.rs b/modules/graphics/src/rendering/pipelines.rs index 7eacd7f0..a0c0c73a 100644 --- a/modules/graphics/src/rendering/pipelines.rs +++ b/modules/graphics/src/rendering/pipelines.rs @@ -14,9 +14,11 @@ pub struct RenderFrame<'a> { impl<'a> RenderFrame<'a> { /// Draws the object visible to the given camera. pub fn draw_camera(&mut self, scene: &dyn RenderScene, camera: &dyn Camera) { - let visible_object_set = scene.cull_visible_objects(camera); + // find all objects visible to the camera + let visible_objects = scene.cull_visible_objects(camera); - for (material, group) in visible_object_set.group_by_material() { + // render each object, minimizing state changes by material + for (material, group) in visible_objects.group_by_material(MaterialFlags::all()) { self.queue.set_material(material); for entry in group {