1use crate::{
25 core::{
26 algebra::{Matrix4, Point3, Vector3, Vector4},
27 color::Color,
28 math::aabb::AxisAlignedBoundingBox,
29 parking_lot::Mutex,
30 pool::Handle,
31 reflect::prelude::*,
32 type_traits::prelude::*,
33 variable::InheritableVariable,
34 visitor::prelude::*,
35 SafeLock,
36 },
37 graph::{BaseSceneGraph, SceneGraph},
38 graphics::ElementRange,
39 material::{
40 Material, MaterialResource, MaterialResourceBinding, MaterialResourceExtension,
41 MaterialTextureBinding,
42 },
43 renderer::{
44 self,
45 bundle::{RenderContext, RenderDataBundleStorageTrait, SurfaceInstanceData},
46 cache::DynamicSurfaceCache,
47 },
48 resource::texture::PLACEHOLDER,
49 scene::{
50 base::{Base, BaseBuilder},
51 debug::{Line, SceneDrawingContext},
52 graph::Graph,
53 mesh::{
54 buffer::{
55 TriangleBuffer, TriangleBufferRefMut, VertexAttributeDescriptor,
56 VertexAttributeUsage, VertexBuffer, VertexBufferRefMut, VertexReadTrait,
57 VertexViewMut, VertexWriteTrait,
58 },
59 surface::SurfaceBuilder,
60 surface::{BlendShape, Surface, SurfaceData, SurfaceResource},
61 },
62 node::constructor::NodeConstructor,
63 node::{Node, NodeTrait, RdcControlFlow, SyncContext},
64 },
65};
66use fxhash::{FxHashMap, FxHasher};
67use fyrox_graph::constructor::ConstructorProvider;
68use fyrox_resource::untyped::ResourceKind;
69use std::{
70 cell::Cell,
71 hash::{Hash, Hasher},
72 ops::{Deref, DerefMut},
73};
74use strum_macros::{AsRefStr, EnumString, VariantNames};
75
76pub mod buffer;
77pub mod surface;
78pub mod vertex;
79
80#[derive(
82 Default,
83 Copy,
84 Clone,
85 PartialOrd,
86 PartialEq,
87 Eq,
88 Ord,
89 Hash,
90 Debug,
91 Visit,
92 Reflect,
93 AsRefStr,
94 EnumString,
95 VariantNames,
96 TypeUuidProvider,
97)]
98#[type_uuid(id = "009bccb6-42e4-4dc6-bb26-6a8a70b3fab9")]
99#[repr(u32)]
100pub enum RenderPath {
101 #[default]
104 Deferred = 0,
105
106 Forward = 1,
109}
110
111fn transform_vertex(mut vertex: VertexViewMut, world: &Matrix4<f32>) {
112 if let Ok(position) = vertex.cast_attribute::<Vector3<f32>>(VertexAttributeUsage::Position) {
113 *position = world.transform_point(&(*position).into()).coords;
114 }
115 if let Ok(normal) = vertex.cast_attribute::<Vector3<f32>>(VertexAttributeUsage::Normal) {
116 *normal = world.transform_vector(normal);
117 }
118 if let Ok(tangent) = vertex.cast_attribute::<Vector4<f32>>(VertexAttributeUsage::Tangent) {
119 let new_tangent = world.transform_vector(&tangent.xyz());
120 *tangent = Vector4::new(
121 new_tangent.x,
122 new_tangent.y,
123 new_tangent.z,
124 tangent.w,
126 );
127 }
128}
129
130#[derive(
132 Default,
133 Copy,
134 Clone,
135 PartialOrd,
136 PartialEq,
137 Eq,
138 Ord,
139 Hash,
140 Debug,
141 Visit,
142 Reflect,
143 AsRefStr,
144 EnumString,
145 VariantNames,
146 TypeUuidProvider,
147)]
148#[type_uuid(id = "745e6f32-63f5-46fe-8edb-9708699ae328")]
149#[repr(u32)]
150pub enum BatchingMode {
151 #[default]
153 None,
154 Static,
158 Dynamic,
161}
162
163#[derive(Debug, Clone)]
164struct Batch {
165 data: SurfaceResource,
166 material: MaterialResource,
167}
168
169#[derive(Debug, Default, Clone)]
170struct BatchContainer {
171 batches: FxHashMap<u64, Batch>,
172}
173
174impl BatchContainer {
175 fn fill(&mut self, from: Handle<Node>, ctx: &mut RenderContext) {
176 for (descendant_handle, descendant) in ctx.graph.traverse_iter(from) {
177 if descendant_handle == from {
178 continue;
179 }
180
181 descendant.collect_render_data(&mut RenderContext {
182 render_mask: ctx.render_mask,
183 elapsed_time: ctx.elapsed_time,
184 observer_position: ctx.observer_position,
185 frustum: None,
186 storage: self,
187 graph: ctx.graph,
188 render_pass_name: ctx.render_pass_name,
189 dynamic_surface_cache: ctx.dynamic_surface_cache,
190 });
191 }
192 }
193}
194
195#[derive(Debug, Default)]
196struct BatchContainerWrapper(Mutex<BatchContainer>);
197
198impl Clone for BatchContainerWrapper {
199 fn clone(&self) -> Self {
200 Self(Mutex::new(self.0.safe_lock().clone()))
201 }
202}
203
204impl RenderDataBundleStorageTrait for BatchContainer {
205 fn push_triangles(
206 &mut self,
207 dynamic_surface_cache: &mut DynamicSurfaceCache,
208 layout: &[VertexAttributeDescriptor],
209 material: &MaterialResource,
210 _render_path: RenderPath,
211 _sort_index: u64,
212 _node_handle: Handle<Node>,
213 func: &mut dyn FnMut(VertexBufferRefMut, TriangleBufferRefMut),
214 ) {
215 let mut hasher = FxHasher::default();
216 layout.hash(&mut hasher);
217 hasher.write_u64(material.key());
218 let batch_hash = hasher.finish();
219
220 let batch = self.batches.entry(batch_hash).or_insert_with(|| Batch {
221 data: dynamic_surface_cache.get_or_create(batch_hash, layout),
222 material: material.clone(),
223 });
224
225 let mut batch_data_guard = batch.data.data_ref();
226 let batch_data = &mut *batch_data_guard;
227
228 func(
229 batch_data.vertex_buffer.modify(),
230 batch_data.geometry_buffer.modify(),
231 );
232 }
233
234 fn push(
235 &mut self,
236 data: &SurfaceResource,
237 material: &MaterialResource,
238 _render_path: RenderPath,
239 _sort_index: u64,
240 instance_data: SurfaceInstanceData,
241 ) {
242 let src_data = data.data_ref();
243
244 let mut hasher = FxHasher::default();
245 src_data.vertex_buffer.layout().hash(&mut hasher);
246 hasher.write_u64(material.key());
247 let batch_hash = hasher.finish();
248
249 let batch = self.batches.entry(batch_hash).or_insert_with(|| Batch {
250 data: SurfaceResource::new_ok(
251 Uuid::new_v4(),
252 ResourceKind::Embedded,
253 SurfaceData::new(
254 src_data.vertex_buffer.clone_empty(4096),
255 TriangleBuffer::new(Vec::with_capacity(4096)),
256 ),
257 ),
258 material: material.clone(),
259 });
260
261 let mut batch_data_guard = batch.data.data_ref();
262 let batch_data = &mut *batch_data_guard;
263 let start_vertex_index = batch_data.vertex_buffer.vertex_count();
264 let mut batch_vertex_buffer = batch_data.vertex_buffer.modify();
265 for src_vertex in src_data.vertex_buffer.iter() {
266 batch_vertex_buffer
267 .push_vertex_raw(&src_vertex.transform(&mut |vertex| {
268 transform_vertex(vertex, &instance_data.world_transform)
269 }))
270 .expect("Vertex size must match!");
271 }
272
273 let mut batch_geometry_buffer = batch_data.geometry_buffer.modify();
274 batch_geometry_buffer.push_triangles_with_offset(
275 start_vertex_index,
276 src_data.geometry_buffer.triangles_ref(),
277 );
278 }
279}
280
281#[derive(Debug, Reflect, Clone, Visit, ComponentProvider)]
320#[reflect(derived_type = "Node")]
321pub struct Mesh {
322 #[visit(rename = "Common")]
323 base: Base,
324
325 #[reflect(setter = "set_surfaces")]
326 surfaces: InheritableVariable<Vec<Surface>>,
327
328 #[reflect(setter = "set_render_path")]
329 render_path: InheritableVariable<RenderPath>,
330
331 #[visit(optional)]
346 #[reflect(setter = "set_batching_mode")]
347 batching_mode: InheritableVariable<BatchingMode>,
348
349 #[visit(optional)]
350 blend_shapes_property_name: String,
351
352 #[visit(optional)]
353 blend_shapes: InheritableVariable<Vec<BlendShape>>,
354
355 #[reflect(hidden)]
356 #[visit(skip)]
357 local_bounding_box: Cell<AxisAlignedBoundingBox>,
358
359 #[reflect(hidden)]
360 #[visit(skip)]
361 local_bounding_box_dirty: Cell<bool>,
362
363 #[reflect(hidden)]
364 #[visit(skip)]
365 world_bounding_box: Cell<AxisAlignedBoundingBox>,
366
367 #[reflect(hidden)]
368 #[visit(skip)]
369 batch_container: BatchContainerWrapper,
370}
371
372impl Default for Mesh {
373 fn default() -> Self {
374 Self {
375 base: Default::default(),
376 surfaces: Default::default(),
377 local_bounding_box: Default::default(),
378 world_bounding_box: Default::default(),
379 local_bounding_box_dirty: Cell::new(true),
380 render_path: InheritableVariable::new_modified(RenderPath::Deferred),
381 batching_mode: Default::default(),
382 blend_shapes_property_name: Mesh::DEFAULT_BLEND_SHAPES_PROPERTY_NAME.to_string(),
383 blend_shapes: Default::default(),
384 batch_container: Default::default(),
385 }
386 }
387}
388
389impl Deref for Mesh {
390 type Target = Base;
391
392 fn deref(&self) -> &Self::Target {
393 &self.base
394 }
395}
396
397impl DerefMut for Mesh {
398 fn deref_mut(&mut self) -> &mut Self::Target {
399 &mut self.base
400 }
401}
402
403impl TypeUuidProvider for Mesh {
404 fn type_uuid() -> Uuid {
405 uuid!("caaf9d7b-bd74-48ce-b7cc-57e9dc65c2e6")
406 }
407}
408
409impl Mesh {
410 pub const DEFAULT_BLEND_SHAPES_PROPERTY_NAME: &'static str = "blendShapesStorage";
412
413 pub fn set_surfaces(&mut self, surfaces: Vec<Surface>) -> Vec<Surface> {
415 self.surfaces.set_value_and_mark_modified(surfaces)
416 }
417
418 #[inline]
420 pub fn surfaces(&self) -> &[Surface] {
421 &self.surfaces
422 }
423
424 #[inline]
426 pub fn surfaces_mut(&mut self) -> &mut [Surface] {
427 self.local_bounding_box_dirty.set(true);
428 self.surfaces.get_value_mut_silent()
429 }
430
431 #[inline]
433 pub fn clear_surfaces(&mut self) {
434 self.surfaces.get_value_mut_and_mark_modified().clear();
435 self.local_bounding_box_dirty.set(true);
436 }
437
438 #[inline]
440 pub fn add_surface(&mut self, surface: Surface) {
441 self.surfaces
442 .get_value_mut_and_mark_modified()
443 .push(surface);
444 self.local_bounding_box_dirty.set(true);
445 }
446
447 pub fn blend_shapes(&self) -> &[BlendShape] {
449 &self.blend_shapes
450 }
451
452 pub fn blend_shapes_mut(&mut self) -> &mut [BlendShape] {
454 self.blend_shapes.get_value_mut_and_mark_modified()
455 }
456
457 pub fn set_render_path(&mut self, render_path: RenderPath) -> RenderPath {
459 self.render_path.set_value_and_mark_modified(render_path)
460 }
461
462 pub fn render_path(&self) -> RenderPath {
464 *self.render_path
465 }
466
467 pub fn accurate_world_bounding_box(&self, graph: &Graph) -> AxisAlignedBoundingBox {
470 let mut bounding_box = AxisAlignedBoundingBox::default();
471 for surface in self.surfaces.iter() {
472 let data = surface.data();
473 let data = data.data_ref();
474 if surface.bones().is_empty() {
475 for view in data.vertex_buffer.iter() {
476 let Ok(vertex_pos) = view.read_3_f32(VertexAttributeUsage::Position) else {
477 break;
478 };
479
480 bounding_box.add_point(
481 self.global_transform()
482 .transform_point(&Point3::from(vertex_pos))
483 .coords,
484 );
485 }
486 } else {
487 let bone_matrices = surface
492 .bones()
493 .iter()
494 .map(|&b| {
495 let bone_node = &graph[b];
496 bone_node.global_transform() * bone_node.inv_bind_pose_transform()
497 })
498 .collect::<Vec<Matrix4<f32>>>();
499
500 for view in data.vertex_buffer.iter() {
501 let mut position = Vector3::default();
502
503 let Ok(vertex_pos) = view.read_3_f32(VertexAttributeUsage::Position) else {
504 break;
505 };
506 let Ok(bone_indices) = view.read_4_u8(VertexAttributeUsage::BoneIndices) else {
507 break;
508 };
509 let Ok(bone_weights) = view.read_4_f32(VertexAttributeUsage::BoneWeight) else {
510 break;
511 };
512
513 for (&bone_index, &weight) in bone_indices.iter().zip(bone_weights.iter()) {
514 position += bone_matrices[bone_index as usize]
515 .transform_point(&Point3::from(vertex_pos))
516 .coords
517 .scale(weight);
518 }
519
520 bounding_box.add_point(position);
521 }
522 }
523 }
524 bounding_box
525 }
526
527 pub fn set_batching_mode(&mut self, mode: BatchingMode) -> BatchingMode {
542 if let BatchingMode::None | BatchingMode::Dynamic = mode {
543 std::mem::take(&mut self.batch_container);
545 }
546
547 self.batching_mode.set_value_and_mark_modified(mode)
548 }
549
550 pub fn batching_mode(&self) -> BatchingMode {
552 *self.batching_mode
553 }
554}
555
556fn extend_aabb_from_vertex_buffer(
557 vertex_buffer: &VertexBuffer,
558 bounding_box: &mut AxisAlignedBoundingBox,
559) {
560 if let Some(position_attribute_view) =
561 vertex_buffer.attribute_view::<Vector3<f32>>(VertexAttributeUsage::Position)
562 {
563 for i in 0..vertex_buffer.vertex_count() as usize {
564 bounding_box.add_point(*position_attribute_view.get(i).unwrap());
565 }
566 }
567}
568
569fn placeholder_material() -> MaterialResource {
570 let mut material = Material::standard();
571 material.bind("diffuseTexture", PLACEHOLDER.resource());
572 MaterialResource::new_ok(Uuid::new_v4(), ResourceKind::Embedded, material)
573}
574
575impl ConstructorProvider<Node, Graph> for Mesh {
576 fn constructor() -> NodeConstructor {
577 NodeConstructor::new::<Self>()
578 .with_variant("Empty", |_| {
579 MeshBuilder::new(BaseBuilder::new()).build_node().into()
580 })
581 .with_variant("Cube", |_| {
582 MeshBuilder::new(BaseBuilder::new().with_name("Cube"))
583 .with_surfaces(vec![SurfaceBuilder::new(surface::CUBE.resource.clone())
584 .with_material(placeholder_material())
585 .build()])
586 .build_node()
587 .into()
588 })
589 .with_variant("Cone", |_| {
590 MeshBuilder::new(BaseBuilder::new().with_name("Cone"))
591 .with_surfaces(vec![SurfaceBuilder::new(surface::CONE.resource.clone())
592 .with_material(placeholder_material())
593 .build()])
594 .build_node()
595 .into()
596 })
597 .with_variant("Cylinder", |_| {
598 MeshBuilder::new(BaseBuilder::new().with_name("Cylinder"))
599 .with_surfaces(vec![SurfaceBuilder::new(
600 surface::CYLINDER.resource.clone(),
601 )
602 .with_material(placeholder_material())
603 .build()])
604 .build_node()
605 .into()
606 })
607 .with_variant("Sphere", |_| {
608 MeshBuilder::new(BaseBuilder::new().with_name("Sphere"))
609 .with_surfaces(vec![SurfaceBuilder::new(surface::SPHERE.resource.clone())
610 .with_material(placeholder_material())
611 .build()])
612 .build_node()
613 .into()
614 })
615 .with_variant("Quad", |_| {
616 MeshBuilder::new(BaseBuilder::new().with_name("Quad"))
617 .with_surfaces(vec![SurfaceBuilder::new(surface::QUAD.resource.clone())
618 .with_material(placeholder_material())
619 .build()])
620 .build_node()
621 .into()
622 })
623 .with_group("Mesh")
624 }
625}
626
627impl NodeTrait for Mesh {
628 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
631 if self.local_bounding_box_dirty.get() {
632 let mut bounding_box = AxisAlignedBoundingBox::default();
633
634 if let BatchingMode::Static = *self.batching_mode {
635 let container = self.batch_container.0.safe_lock();
636 for batch in container.batches.values() {
637 let data = batch.data.data_ref();
638 extend_aabb_from_vertex_buffer(&data.vertex_buffer, &mut bounding_box);
639 }
640 } else {
641 for surface in self.surfaces.iter() {
642 let data = surface.data();
643 if data.is_ok() {
644 let data = data.data_ref();
645 extend_aabb_from_vertex_buffer(&data.vertex_buffer, &mut bounding_box);
646 }
647 }
648 }
649
650 self.local_bounding_box.set(bounding_box);
651 self.local_bounding_box_dirty.set(false);
652 }
653
654 self.local_bounding_box.get()
655 }
656
657 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
659 self.world_bounding_box.get()
660 }
661
662 fn id(&self) -> Uuid {
663 Self::type_uuid()
664 }
665
666 fn on_global_transform_changed(
667 &self,
668 new_global_transform: &Matrix4<f32>,
669 context: &mut SyncContext,
670 ) {
671 if self.surfaces.iter().any(|s| !s.bones.is_empty()) {
672 let mut world_aabb = self.local_bounding_box().transform(new_global_transform);
673
674 for surface in self.surfaces.iter() {
676 for &bone in surface.bones() {
677 if let Some(node) = context.nodes.try_borrow(bone) {
678 world_aabb.add_point(node.global_position())
679 }
680 }
681 }
682
683 self.world_bounding_box.set(world_aabb)
684 } else {
685 self.world_bounding_box
686 .set(self.local_bounding_box().transform(new_global_transform));
687 }
688 }
689
690 fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
691 if !self.should_be_rendered(ctx.frustum, ctx.render_mask) {
692 return RdcControlFlow::Continue;
693 }
694
695 if renderer::is_shadow_pass(ctx.render_pass_name) && !self.cast_shadows() {
696 return RdcControlFlow::Continue;
697 }
698
699 let sorting_index = ctx.calculate_sorting_index(self.global_position());
700
701 if let BatchingMode::Static = *self.batching_mode {
702 let mut container = self.batch_container.0.safe_lock();
703
704 if container.batches.is_empty() {
705 container.fill(self.handle(), ctx);
706 }
707
708 for batch in container.batches.values() {
709 ctx.storage.push(
710 &batch.data,
711 &batch.material,
712 self.render_path(),
713 sorting_index,
714 SurfaceInstanceData {
715 world_transform: Matrix4::identity(),
716 bone_matrices: Default::default(),
717 blend_shapes_weights: Default::default(),
718 element_range: ElementRange::Full,
719 node_handle: self.handle(),
720 },
721 );
722 }
723
724 RdcControlFlow::Break
725 } else {
726 for surface in self.surfaces().iter() {
727 if !surface.data_ref().is_ok() {
728 continue;
729 }
730 let is_skinned = !surface.bones.is_empty();
731
732 let world = if is_skinned {
733 Matrix4::identity()
734 } else {
735 self.global_transform()
736 };
737
738 let batching_mode = match *self.batching_mode {
739 BatchingMode::None => BatchingMode::None,
740 BatchingMode::Static => BatchingMode::Static,
741 BatchingMode::Dynamic => {
742 let surface_data_guard = surface.data_ref().data_ref();
743 if self.blend_shapes().is_empty()
744 && surface.bones().is_empty()
745 && surface_data_guard.vertex_buffer.vertex_count() < 256
746 {
747 BatchingMode::Dynamic
748 } else {
749 BatchingMode::None
750 }
751 }
752 };
753
754 match batching_mode {
755 BatchingMode::None => {
756 let surface_data = surface.data_ref();
757 let substitute_material = surface_data
758 .data_ref()
759 .blend_shapes_container
760 .as_ref()
761 .and_then(|c| c.blend_shape_storage.as_ref())
762 .map(|texture| {
763 let material_copy = surface.material().deep_copy();
764 material_copy.data_ref().bind(
765 &self.blend_shapes_property_name,
766 MaterialResourceBinding::Texture(MaterialTextureBinding {
767 value: Some(texture.clone()),
768 }),
769 );
770 material_copy
771 });
772
773 ctx.storage.push(
774 surface_data,
775 substitute_material.as_ref().unwrap_or(surface.material()),
776 self.render_path(),
777 sorting_index,
778 SurfaceInstanceData {
779 world_transform: world,
780 bone_matrices: surface
781 .bones
782 .iter()
783 .map(|bone_handle| {
784 if let Some(bone_node) =
785 ctx.graph.try_get_node(*bone_handle)
786 {
787 bone_node.global_transform()
788 * bone_node.inv_bind_pose_transform()
789 } else {
790 Matrix4::identity()
791 }
792 })
793 .collect::<Vec<_>>(),
794 blend_shapes_weights: self
795 .blend_shapes()
796 .iter()
797 .map(|bs| bs.weight / 100.0)
798 .collect(),
799 element_range: ElementRange::Full,
800 node_handle: self.handle(),
801 },
802 );
803 }
804 BatchingMode::Dynamic => {
805 let surface_data_guard = surface.data_ref().data_ref();
806
807 ctx.storage.push_triangles(
808 ctx.dynamic_surface_cache,
809 &surface_data_guard
810 .vertex_buffer
811 .layout_descriptor()
812 .collect::<Vec<_>>(),
813 surface.material(),
814 *self.render_path,
815 sorting_index,
816 self.handle(),
817 &mut move |mut vertex_buffer, mut triangle_buffer| {
818 let start_vertex_index = vertex_buffer.vertex_count();
819
820 for vertex in surface_data_guard.vertex_buffer.iter() {
821 vertex_buffer
822 .push_vertex_raw(&vertex.transform(&mut |vertex| {
823 transform_vertex(vertex, &world)
824 }))
825 .unwrap();
826 }
827
828 triangle_buffer.push_triangles_with_offset(
829 start_vertex_index,
830 surface_data_guard.geometry_buffer.triangles_ref(),
831 )
832 },
833 );
834 }
835 _ => (),
836 }
837 }
838
839 RdcControlFlow::Continue
840 }
841 }
842
843 fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
844 let transform = self.global_transform();
845
846 for surface in self.surfaces() {
847 for vertex in surface.data().data_ref().vertex_buffer.iter() {
848 let len = 0.025;
849 let position = transform
850 .transform_point(&Point3::from(
851 vertex.read_3_f32(VertexAttributeUsage::Position).unwrap(),
852 ))
853 .coords;
854 let vertex_tangent = vertex.read_4_f32(VertexAttributeUsage::Tangent).unwrap();
855 let tangent = transform
856 .transform_vector(&vertex_tangent.xyz())
857 .normalize()
858 .scale(len);
859 let normal = transform
860 .transform_vector(
861 &vertex
862 .read_3_f32(VertexAttributeUsage::Normal)
863 .unwrap()
864 .xyz(),
865 )
866 .normalize()
867 .scale(len);
868 let binormal = normal
869 .xyz()
870 .cross(&tangent)
871 .scale(vertex_tangent.w)
872 .normalize()
873 .scale(len);
874
875 ctx.add_line(Line {
876 begin: position,
877 end: position + tangent,
878 color: Color::RED,
879 });
880
881 ctx.add_line(Line {
882 begin: position,
883 end: position + normal,
884 color: Color::BLUE,
885 });
886
887 ctx.add_line(Line {
888 begin: position,
889 end: position + binormal,
890 color: Color::GREEN,
891 });
892 }
893 }
894 }
895}
896
897pub struct MeshBuilder {
899 base_builder: BaseBuilder,
900 surfaces: Vec<Surface>,
901 render_path: RenderPath,
902 blend_shapes: Vec<BlendShape>,
903 batching_mode: BatchingMode,
904 blend_shapes_property_name: String,
905}
906
907impl MeshBuilder {
908 pub fn new(base_builder: BaseBuilder) -> Self {
910 Self {
911 base_builder,
912 surfaces: Default::default(),
913 render_path: RenderPath::Deferred,
914 blend_shapes: Default::default(),
915 batching_mode: BatchingMode::None,
916 blend_shapes_property_name: Mesh::DEFAULT_BLEND_SHAPES_PROPERTY_NAME.to_string(),
917 }
918 }
919
920 pub fn with_surfaces(mut self, surfaces: Vec<Surface>) -> Self {
922 self.surfaces = surfaces;
923 self
924 }
925
926 pub fn with_render_path(mut self, render_path: RenderPath) -> Self {
929 self.render_path = render_path;
930 self
931 }
932
933 pub fn with_blend_shapes(mut self, blend_shapes: Vec<BlendShape>) -> Self {
936 self.blend_shapes = blend_shapes;
937 self
938 }
939
940 pub fn with_batching_mode(mut self, mode: BatchingMode) -> Self {
942 self.batching_mode = mode;
943 self
944 }
945
946 pub fn with_blend_shapes_property_name(mut self, name: String) -> Self {
948 self.blend_shapes_property_name = name;
949 self
950 }
951
952 pub fn build_node(self) -> Node {
954 Node::new(Mesh {
955 blend_shapes: self.blend_shapes.into(),
956 base: self.base_builder.build_base(),
957 surfaces: self.surfaces.into(),
958 local_bounding_box: Default::default(),
959 local_bounding_box_dirty: Cell::new(true),
960 render_path: self.render_path.into(),
961 world_bounding_box: Default::default(),
962 batching_mode: self.batching_mode.into(),
963 batch_container: Default::default(),
964 blend_shapes_property_name: self.blend_shapes_property_name,
965 })
966 }
967
968 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
970 graph.add_node(self.build_node())
971 }
972}