use crate::{
core::{
algebra::{Matrix4, Point3, Vector3, Vector4},
color::Color,
math::aabb::AxisAlignedBoundingBox,
parking_lot::Mutex,
pool::Handle,
reflect::prelude::*,
type_traits::prelude::*,
variable::InheritableVariable,
visitor::prelude::*,
TypeUuidProvider,
},
graph::{BaseSceneGraph, SceneGraph},
material::MaterialResource,
renderer::{
self,
bundle::{
PersistentIdentifier, RenderContext, RenderDataBundleStorageTrait, SurfaceInstanceData,
},
framework::geometry_buffer::ElementRange,
},
scene::{
base::{Base, BaseBuilder},
debug::{Line, SceneDrawingContext},
graph::Graph,
mesh::{
buffer::{
BytesStorage, TriangleBuffer, TriangleBufferRefMut, VertexAttributeDescriptor,
VertexAttributeUsage, VertexBuffer, VertexBufferRefMut, VertexReadTrait,
VertexViewMut, VertexWriteTrait,
},
surface::{BlendShape, Surface, SurfaceData, SurfaceSharedData},
},
node::{Node, NodeTrait, RdcControlFlow, SyncContext},
},
};
use fxhash::{FxHashMap, FxHasher};
use std::{
cell::Cell,
hash::{Hash, Hasher},
ops::{Deref, DerefMut},
};
use strum_macros::{AsRefStr, EnumString, VariantNames};
pub mod buffer;
pub mod surface;
pub mod vertex;
#[derive(
Default,
Copy,
Clone,
PartialOrd,
PartialEq,
Eq,
Ord,
Hash,
Debug,
Visit,
Reflect,
AsRefStr,
EnumString,
VariantNames,
TypeUuidProvider,
)]
#[type_uuid(id = "009bccb6-42e4-4dc6-bb26-6a8a70b3fab9")]
#[repr(u32)]
pub enum RenderPath {
#[default]
Deferred = 0,
Forward = 1,
}
fn transform_vertex(mut vertex: VertexViewMut, world: &Matrix4<f32>) {
if let Ok(position) = vertex.cast_attribute::<Vector3<f32>>(VertexAttributeUsage::Position) {
*position = world.transform_point(&(*position).into()).coords;
}
if let Ok(normal) = vertex.cast_attribute::<Vector3<f32>>(VertexAttributeUsage::Normal) {
*normal = world.transform_vector(normal);
}
if let Ok(tangent) = vertex.cast_attribute::<Vector4<f32>>(VertexAttributeUsage::Tangent) {
let new_tangent = world.transform_vector(&tangent.xyz());
*tangent = Vector4::new(
new_tangent.x,
new_tangent.y,
new_tangent.z,
tangent.w,
);
}
}
#[derive(
Default,
Copy,
Clone,
PartialOrd,
PartialEq,
Eq,
Ord,
Hash,
Debug,
Visit,
Reflect,
AsRefStr,
EnumString,
VariantNames,
TypeUuidProvider,
)]
#[type_uuid(id = "745e6f32-63f5-46fe-8edb-9708699ae328")]
#[repr(u32)]
pub enum BatchingMode {
#[default]
None,
Static,
Dynamic,
}
#[derive(Debug, Clone)]
struct Batch {
data: SurfaceSharedData,
material: MaterialResource,
}
#[derive(Debug, Default, Clone)]
struct BatchContainer {
batches: FxHashMap<u64, Batch>,
}
impl BatchContainer {
fn fill(&mut self, from: Handle<Node>, ctx: &mut RenderContext) {
for descendant_handle in ctx.graph.traverse_handle_iter(from) {
if descendant_handle == from {
continue;
}
let descendant = &ctx.graph[descendant_handle];
descendant.collect_render_data(&mut RenderContext {
observer_position: ctx.observer_position,
z_near: ctx.z_near,
z_far: ctx.z_far,
view_matrix: ctx.view_matrix,
projection_matrix: ctx.projection_matrix,
frustum: None,
storage: self,
graph: ctx.graph,
render_pass_name: ctx.render_pass_name,
});
}
}
}
#[derive(Debug, Default)]
struct BatchContainerWrapper(Mutex<BatchContainer>);
impl Clone for BatchContainerWrapper {
fn clone(&self) -> Self {
Self(Mutex::new(self.0.lock().clone()))
}
}
impl RenderDataBundleStorageTrait for BatchContainer {
fn push_triangles(
&mut self,
layout: &[VertexAttributeDescriptor],
material: &MaterialResource,
_render_path: RenderPath,
_decal_layer_index: u8,
_sort_index: u64,
_is_skinned: bool,
_node_handle: Handle<Node>,
func: &mut dyn FnMut(VertexBufferRefMut, TriangleBufferRefMut),
) {
let mut hasher = FxHasher::default();
layout.hash(&mut hasher);
hasher.write_u64(material.key() as u64);
let batch_hash = hasher.finish();
let batch = self.batches.entry(batch_hash).or_insert_with(|| Batch {
data: SurfaceSharedData::new(SurfaceData::new(
VertexBuffer::new_with_layout(layout, 0, BytesStorage::with_capacity(4096))
.unwrap(),
TriangleBuffer::new(Vec::with_capacity(4096)),
false,
)),
material: material.clone(),
});
let mut batch_data_guard = batch.data.lock();
let batch_data = &mut *batch_data_guard;
func(
batch_data.vertex_buffer.modify(),
batch_data.geometry_buffer.modify(),
);
}
fn push(
&mut self,
data: &SurfaceSharedData,
material: &MaterialResource,
_render_path: RenderPath,
_decal_layer_index: u8,
_sort_index: u64,
instance_data: SurfaceInstanceData,
) {
let src_data = data.lock();
let mut hasher = FxHasher::default();
src_data.vertex_buffer.layout().hash(&mut hasher);
hasher.write_u64(material.key() as u64);
let batch_hash = hasher.finish();
let batch = self.batches.entry(batch_hash).or_insert_with(|| Batch {
data: SurfaceSharedData::new(SurfaceData::new(
src_data.vertex_buffer.clone_empty(4096),
TriangleBuffer::new(Vec::with_capacity(4096)),
false,
)),
material: material.clone(),
});
let mut batch_data_guard = batch.data.lock();
let batch_data = &mut *batch_data_guard;
let start_vertex_index = batch_data.vertex_buffer.vertex_count();
let mut batch_vertex_buffer = batch_data.vertex_buffer.modify();
for src_vertex in src_data.vertex_buffer.iter() {
batch_vertex_buffer
.push_vertex_raw(&src_vertex.transform(&mut |vertex| {
transform_vertex(vertex, &instance_data.world_transform)
}))
.expect("Vertex size must match!");
}
let mut batch_geometry_buffer = batch_data.geometry_buffer.modify();
batch_geometry_buffer.push_triangles_with_offset(
start_vertex_index,
src_data.geometry_buffer.triangles_ref(),
);
}
}
#[derive(Debug, Reflect, Clone, Visit)]
pub struct Mesh {
#[visit(rename = "Common")]
base: Base,
#[reflect(setter = "set_surfaces")]
surfaces: InheritableVariable<Vec<Surface>>,
#[reflect(setter = "set_render_path")]
render_path: InheritableVariable<RenderPath>,
#[reflect(setter = "set_decal_layer_index")]
decal_layer_index: InheritableVariable<u8>,
#[visit(optional)]
#[reflect(
setter = "set_batching_mode",
description = "Enable or disable dynamic batching. It could be useful to reduce amount \
of draw calls per frame if you have lots of meshes with small vertex count. Does not work with \
meshes, that have skin or blend shapes. Such meshes will be drawn in a separate draw call."
)]
batching_mode: InheritableVariable<BatchingMode>,
#[visit(optional)]
blend_shapes: InheritableVariable<Vec<BlendShape>>,
#[reflect(hidden)]
#[visit(skip)]
local_bounding_box: Cell<AxisAlignedBoundingBox>,
#[reflect(hidden)]
#[visit(skip)]
local_bounding_box_dirty: Cell<bool>,
#[reflect(hidden)]
#[visit(skip)]
world_bounding_box: Cell<AxisAlignedBoundingBox>,
#[reflect(hidden)]
#[visit(skip)]
batch_container: BatchContainerWrapper,
}
impl Default for Mesh {
fn default() -> Self {
Self {
base: Default::default(),
surfaces: Default::default(),
local_bounding_box: Default::default(),
world_bounding_box: Default::default(),
local_bounding_box_dirty: Cell::new(true),
render_path: InheritableVariable::new_modified(RenderPath::Deferred),
decal_layer_index: InheritableVariable::new_modified(0),
batching_mode: Default::default(),
blend_shapes: Default::default(),
batch_container: Default::default(),
}
}
}
impl Deref for Mesh {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for Mesh {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl TypeUuidProvider for Mesh {
fn type_uuid() -> Uuid {
uuid!("caaf9d7b-bd74-48ce-b7cc-57e9dc65c2e6")
}
}
impl Mesh {
pub fn set_surfaces(&mut self, surfaces: Vec<Surface>) -> Vec<Surface> {
self.surfaces.set_value_and_mark_modified(surfaces)
}
#[inline]
pub fn surfaces(&self) -> &[Surface] {
&self.surfaces
}
#[inline]
pub fn surfaces_mut(&mut self) -> &mut [Surface] {
self.local_bounding_box_dirty.set(true);
self.surfaces.get_value_mut_silent()
}
#[inline]
pub fn clear_surfaces(&mut self) {
self.surfaces.get_value_mut_and_mark_modified().clear();
self.local_bounding_box_dirty.set(true);
}
#[inline]
pub fn add_surface(&mut self, surface: Surface) {
self.surfaces
.get_value_mut_and_mark_modified()
.push(surface);
self.local_bounding_box_dirty.set(true);
}
pub fn blend_shapes(&self) -> &[BlendShape] {
&self.blend_shapes
}
pub fn blend_shapes_mut(&mut self) -> &mut [BlendShape] {
self.blend_shapes.get_value_mut_and_mark_modified()
}
pub fn set_render_path(&mut self, render_path: RenderPath) -> RenderPath {
self.render_path.set_value_and_mark_modified(render_path)
}
pub fn render_path(&self) -> RenderPath {
*self.render_path
}
pub fn accurate_world_bounding_box(&self, graph: &Graph) -> AxisAlignedBoundingBox {
let mut bounding_box = AxisAlignedBoundingBox::default();
for surface in self.surfaces.iter() {
let data = surface.data();
let data = data.lock();
if surface.bones().is_empty() {
for view in data.vertex_buffer.iter() {
bounding_box.add_point(
self.global_transform()
.transform_point(&Point3::from(
view.read_3_f32(VertexAttributeUsage::Position).unwrap(),
))
.coords,
);
}
} else {
let bone_matrices = surface
.bones()
.iter()
.map(|&b| {
let bone_node = &graph[b];
bone_node.global_transform() * bone_node.inv_bind_pose_transform()
})
.collect::<Vec<Matrix4<f32>>>();
for view in data.vertex_buffer.iter() {
let mut position = Vector3::default();
for (&bone_index, &weight) in view
.read_4_u8(VertexAttributeUsage::BoneIndices)
.unwrap()
.iter()
.zip(
view.read_4_f32(VertexAttributeUsage::BoneWeight)
.unwrap()
.iter(),
)
{
position += bone_matrices[bone_index as usize]
.transform_point(&Point3::from(
view.read_3_f32(VertexAttributeUsage::Position).unwrap(),
))
.coords
.scale(weight);
}
bounding_box.add_point(position);
}
}
}
bounding_box
}
pub fn set_decal_layer_index(&mut self, index: u8) -> u8 {
self.decal_layer_index.set_value_and_mark_modified(index)
}
pub fn decal_layer_index(&self) -> u8 {
*self.decal_layer_index
}
pub fn set_batching_mode(&mut self, mode: BatchingMode) -> BatchingMode {
if let BatchingMode::None | BatchingMode::Dynamic = mode {
std::mem::take(&mut self.batch_container);
}
self.batching_mode.set_value_and_mark_modified(mode)
}
pub fn batching_mode(&self) -> BatchingMode {
*self.batching_mode
}
}
fn extend_aabb_from_vertex_buffer(
vertex_buffer: &VertexBuffer,
bounding_box: &mut AxisAlignedBoundingBox,
) {
if let Some(position_attribute_view) =
vertex_buffer.attribute_view::<Vector3<f32>>(VertexAttributeUsage::Position)
{
for i in 0..vertex_buffer.vertex_count() as usize {
bounding_box.add_point(*position_attribute_view.get(i).unwrap());
}
}
}
impl NodeTrait for Mesh {
crate::impl_query_component!();
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
if self.local_bounding_box_dirty.get() {
let mut bounding_box = AxisAlignedBoundingBox::default();
if let BatchingMode::Static = *self.batching_mode {
let container = self.batch_container.0.lock();
for batch in container.batches.values() {
let data = batch.data.lock();
extend_aabb_from_vertex_buffer(&data.vertex_buffer, &mut bounding_box);
}
} else {
for surface in self.surfaces.iter() {
let data = surface.data();
let data = data.lock();
extend_aabb_from_vertex_buffer(&data.vertex_buffer, &mut bounding_box);
}
}
self.local_bounding_box.set(bounding_box);
self.local_bounding_box_dirty.set(false);
}
self.local_bounding_box.get()
}
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.world_bounding_box.get()
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
fn sync_transform(&self, _new_global_transform: &Matrix4<f32>, context: &mut SyncContext) {
if self.surfaces.iter().any(|s| !s.bones.is_empty()) {
let mut world_aabb = self
.local_bounding_box()
.transform(&self.global_transform());
for surface in self.surfaces.iter() {
for &bone in surface.bones() {
if let Some(node) = context.nodes.try_borrow(bone) {
world_aabb.add_point(node.global_position())
}
}
}
self.world_bounding_box.set(world_aabb)
} else {
self.world_bounding_box.set(
self.local_bounding_box()
.transform(&self.global_transform()),
);
}
}
fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
if !self.global_visibility()
|| !self.is_globally_enabled()
|| (self.frustum_culling()
&& !ctx
.frustum
.map_or(true, |f| f.is_intersects_aabb(&self.world_bounding_box())))
{
return RdcControlFlow::Continue;
}
if renderer::is_shadow_pass(ctx.render_pass_name) && !self.cast_shadows() {
return RdcControlFlow::Continue;
}
if let BatchingMode::Static = *self.batching_mode {
let mut container = self.batch_container.0.lock();
if container.batches.is_empty() {
container.fill(self.self_handle, ctx);
}
for (index, batch) in container.batches.values().enumerate() {
ctx.storage.push(
&batch.data,
&batch.material,
self.render_path(),
self.decal_layer_index(),
batch.material.key() as u64,
SurfaceInstanceData {
world_transform: Matrix4::identity(),
bone_matrices: Default::default(),
depth_offset: self.depth_offset_factor(),
blend_shapes_weights: Default::default(),
element_range: ElementRange::Full,
persistent_identifier: PersistentIdentifier::new_combined(
&batch.data,
self.self_handle,
index,
),
node_handle: self.self_handle,
},
);
}
RdcControlFlow::Break
} else {
for (index, surface) in self.surfaces().iter().enumerate() {
let is_skinned = !surface.bones.is_empty();
let world = if is_skinned {
Matrix4::identity()
} else {
self.global_transform()
};
let batching_mode = match *self.batching_mode {
BatchingMode::None => BatchingMode::None,
BatchingMode::Static => BatchingMode::Static,
BatchingMode::Dynamic => {
let surface_data_guard = surface.data_ref().lock();
if self.blend_shapes().is_empty()
&& surface.bones().is_empty()
&& surface_data_guard.vertex_buffer.vertex_count() < 256
{
BatchingMode::Dynamic
} else {
BatchingMode::None
}
}
};
match batching_mode {
BatchingMode::None => {
ctx.storage.push(
surface.data_ref(),
surface.material(),
self.render_path(),
self.decal_layer_index(),
surface.material().key() as u64,
SurfaceInstanceData {
world_transform: world,
bone_matrices: surface
.bones
.iter()
.map(|bone_handle| {
if let Some(bone_node) = ctx.graph.try_get(*bone_handle) {
bone_node.global_transform()
* bone_node.inv_bind_pose_transform()
} else {
Matrix4::identity()
}
})
.collect::<Vec<_>>(),
depth_offset: self.depth_offset_factor(),
blend_shapes_weights: self
.blend_shapes()
.iter()
.map(|bs| bs.weight / 100.0)
.collect(),
element_range: ElementRange::Full,
persistent_identifier: PersistentIdentifier::new_combined(
surface.data_ref(),
self.self_handle,
index,
),
node_handle: self.self_handle,
},
);
}
BatchingMode::Dynamic => {
let surface_data_guard = surface.data_ref().lock();
ctx.storage.push_triangles(
&surface_data_guard
.vertex_buffer
.layout_descriptor()
.collect::<Vec<_>>(),
surface.material(),
*self.render_path,
self.decal_layer_index(),
0,
false,
self.self_handle,
&mut move |mut vertex_buffer, mut triangle_buffer| {
let start_vertex_index = vertex_buffer.vertex_count();
for vertex in surface_data_guard.vertex_buffer.iter() {
vertex_buffer
.push_vertex_raw(&vertex.transform(&mut |vertex| {
transform_vertex(vertex, &world)
}))
.unwrap();
}
triangle_buffer.push_triangles_with_offset(
start_vertex_index,
surface_data_guard.geometry_buffer.triangles_ref(),
)
},
);
}
_ => (),
}
}
RdcControlFlow::Continue
}
}
fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
let transform = self.global_transform();
for surface in self.surfaces() {
for vertex in surface.data().lock().vertex_buffer.iter() {
let len = 0.025;
let position = transform
.transform_point(&Point3::from(
vertex.read_3_f32(VertexAttributeUsage::Position).unwrap(),
))
.coords;
let vertex_tangent = vertex.read_4_f32(VertexAttributeUsage::Tangent).unwrap();
let tangent = transform
.transform_vector(&vertex_tangent.xyz())
.normalize()
.scale(len);
let normal = transform
.transform_vector(
&vertex
.read_3_f32(VertexAttributeUsage::Normal)
.unwrap()
.xyz(),
)
.normalize()
.scale(len);
let binormal = normal
.xyz()
.cross(&tangent)
.scale(vertex_tangent.w)
.normalize()
.scale(len);
ctx.add_line(Line {
begin: position,
end: position + tangent,
color: Color::RED,
});
ctx.add_line(Line {
begin: position,
end: position + normal,
color: Color::BLUE,
});
ctx.add_line(Line {
begin: position,
end: position + binormal,
color: Color::GREEN,
});
}
}
}
}
pub struct MeshBuilder {
base_builder: BaseBuilder,
surfaces: Vec<Surface>,
render_path: RenderPath,
decal_layer_index: u8,
blend_shapes: Vec<BlendShape>,
batching_mode: BatchingMode,
}
impl MeshBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
surfaces: Default::default(),
render_path: RenderPath::Deferred,
decal_layer_index: 0,
blend_shapes: Default::default(),
batching_mode: BatchingMode::None,
}
}
pub fn with_surfaces(mut self, surfaces: Vec<Surface>) -> Self {
self.surfaces = surfaces;
self
}
pub fn with_render_path(mut self, render_path: RenderPath) -> Self {
self.render_path = render_path;
self
}
pub fn with_decal_layer_index(mut self, decal_layer_index: u8) -> Self {
self.decal_layer_index = decal_layer_index;
self
}
pub fn with_blend_shapes(mut self, blend_shapes: Vec<BlendShape>) -> Self {
self.blend_shapes = blend_shapes;
self
}
pub fn with_batching_mode(mut self, mode: BatchingMode) -> Self {
self.batching_mode = mode;
self
}
pub fn build_node(self) -> Node {
Node::new(Mesh {
blend_shapes: self.blend_shapes.into(),
base: self.base_builder.build_base(),
surfaces: self.surfaces.into(),
local_bounding_box: Default::default(),
local_bounding_box_dirty: Cell::new(true),
render_path: self.render_path.into(),
decal_layer_index: self.decal_layer_index.into(),
world_bounding_box: Default::default(),
batching_mode: self.batching_mode.into(),
batch_container: Default::default(),
})
}
pub fn build(self, graph: &mut Graph) -> Handle<Node> {
graph.add_node(self.build_node())
}
}