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::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)]
321#[reflect(derived_type = "Node")]
322pub struct Mesh {
323 #[visit(rename = "Common")]
324 base: Base,
325
326 #[reflect(setter = "set_surfaces")]
327 surfaces: InheritableVariable<Vec<Surface>>,
328
329 #[reflect(setter = "set_render_path")]
330 render_path: InheritableVariable<RenderPath>,
331
332 #[visit(optional)]
347 #[reflect(setter = "set_batching_mode")]
348 batching_mode: InheritableVariable<BatchingMode>,
349
350 #[visit(optional)]
351 blend_shapes_property_name: String,
352
353 #[visit(optional)]
354 blend_shapes: InheritableVariable<Vec<BlendShape>>,
355
356 #[reflect(hidden)]
357 #[visit(skip)]
358 local_bounding_box: Cell<AxisAlignedBoundingBox>,
359
360 #[reflect(hidden)]
361 #[visit(skip)]
362 local_bounding_box_dirty: Cell<bool>,
363
364 #[reflect(hidden)]
365 #[visit(skip)]
366 world_bounding_box: Cell<AxisAlignedBoundingBox>,
367
368 #[reflect(hidden)]
369 #[visit(skip)]
370 batch_container: BatchContainerWrapper,
371}
372
373impl Default for Mesh {
374 fn default() -> Self {
375 Self {
376 base: Default::default(),
377 surfaces: Default::default(),
378 local_bounding_box: Default::default(),
379 world_bounding_box: Default::default(),
380 local_bounding_box_dirty: Cell::new(true),
381 render_path: InheritableVariable::new_modified(RenderPath::Deferred),
382 batching_mode: Default::default(),
383 blend_shapes_property_name: Mesh::DEFAULT_BLEND_SHAPES_PROPERTY_NAME.to_string(),
384 blend_shapes: Default::default(),
385 batch_container: Default::default(),
386 }
387 }
388}
389
390impl Deref for Mesh {
391 type Target = Base;
392
393 fn deref(&self) -> &Self::Target {
394 &self.base
395 }
396}
397
398impl DerefMut for Mesh {
399 fn deref_mut(&mut self) -> &mut Self::Target {
400 &mut self.base
401 }
402}
403
404impl TypeUuidProvider for Mesh {
405 fn type_uuid() -> Uuid {
406 uuid!("caaf9d7b-bd74-48ce-b7cc-57e9dc65c2e6")
407 }
408}
409
410impl Mesh {
411 pub const DEFAULT_BLEND_SHAPES_PROPERTY_NAME: &'static str = "blendShapesStorage";
413
414 pub fn set_surfaces(&mut self, surfaces: Vec<Surface>) -> Vec<Surface> {
416 self.surfaces.set_value_and_mark_modified(surfaces)
417 }
418
419 #[inline]
421 pub fn surfaces(&self) -> &[Surface] {
422 &self.surfaces
423 }
424
425 #[inline]
427 pub fn surfaces_mut(&mut self) -> &mut [Surface] {
428 self.local_bounding_box_dirty.set(true);
429 self.surfaces.get_value_mut_silent()
430 }
431
432 #[inline]
434 pub fn clear_surfaces(&mut self) {
435 self.surfaces.get_value_mut_and_mark_modified().clear();
436 self.local_bounding_box_dirty.set(true);
437 }
438
439 #[inline]
441 pub fn add_surface(&mut self, surface: Surface) {
442 self.surfaces
443 .get_value_mut_and_mark_modified()
444 .push(surface);
445 self.local_bounding_box_dirty.set(true);
446 }
447
448 pub fn blend_shapes(&self) -> &[BlendShape] {
450 &self.blend_shapes
451 }
452
453 pub fn blend_shapes_mut(&mut self) -> &mut [BlendShape] {
455 self.blend_shapes.get_value_mut_and_mark_modified()
456 }
457
458 pub fn set_render_path(&mut self, render_path: RenderPath) -> RenderPath {
460 self.render_path.set_value_and_mark_modified(render_path)
461 }
462
463 pub fn render_path(&self) -> RenderPath {
465 *self.render_path
466 }
467
468 pub fn accurate_world_bounding_box(&self, graph: &Graph) -> AxisAlignedBoundingBox {
471 let mut bounding_box = AxisAlignedBoundingBox::default();
472 for surface in self.surfaces.iter() {
473 let data = surface.data();
474 let data = data.data_ref();
475 if surface.bones().is_empty() {
476 for view in data.vertex_buffer.iter() {
477 let Ok(vertex_pos) = view.read_3_f32(VertexAttributeUsage::Position) else {
478 break;
479 };
480
481 bounding_box.add_point(
482 self.global_transform()
483 .transform_point(&Point3::from(vertex_pos))
484 .coords,
485 );
486 }
487 } else {
488 let bone_matrices = surface
493 .bones()
494 .iter()
495 .map(|&b| {
496 let bone_node = &graph[b];
497 bone_node.global_transform() * bone_node.inv_bind_pose_transform()
498 })
499 .collect::<Vec<Matrix4<f32>>>();
500
501 for view in data.vertex_buffer.iter() {
502 let mut position = Vector3::default();
503
504 let Ok(vertex_pos) = view.read_3_f32(VertexAttributeUsage::Position) else {
505 break;
506 };
507 let Ok(bone_indices) = view.read_4_u8(VertexAttributeUsage::BoneIndices) else {
508 break;
509 };
510 let Ok(bone_weights) = view.read_4_f32(VertexAttributeUsage::BoneWeight) else {
511 break;
512 };
513
514 for (&bone_index, &weight) in bone_indices.iter().zip(bone_weights.iter()) {
515 position += bone_matrices[bone_index as usize]
516 .transform_point(&Point3::from(vertex_pos))
517 .coords
518 .scale(weight);
519 }
520
521 bounding_box.add_point(position);
522 }
523 }
524 }
525 bounding_box
526 }
527
528 pub fn set_batching_mode(&mut self, mode: BatchingMode) -> BatchingMode {
543 if let BatchingMode::None | BatchingMode::Dynamic = mode {
544 std::mem::take(&mut self.batch_container);
546 }
547
548 self.batching_mode.set_value_and_mark_modified(mode)
549 }
550
551 pub fn batching_mode(&self) -> BatchingMode {
553 *self.batching_mode
554 }
555}
556
557fn extend_aabb_from_vertex_buffer(
558 vertex_buffer: &VertexBuffer,
559 bounding_box: &mut AxisAlignedBoundingBox,
560) {
561 if let Some(position_attribute_view) =
562 vertex_buffer.attribute_view::<Vector3<f32>>(VertexAttributeUsage::Position)
563 {
564 for i in 0..vertex_buffer.vertex_count() as usize {
565 bounding_box.add_point(*position_attribute_view.get(i).unwrap());
566 }
567 }
568}
569
570fn placeholder_material() -> MaterialResource {
571 let mut material = Material::standard();
572 material.bind("diffuseTexture", PLACEHOLDER.resource());
573 MaterialResource::new_ok(Uuid::new_v4(), ResourceKind::Embedded, material)
574}
575
576impl ConstructorProvider<Node, Graph> for Mesh {
577 fn constructor() -> NodeConstructor {
578 NodeConstructor::new::<Self>()
579 .with_variant("Empty", |_| {
580 MeshBuilder::new(BaseBuilder::new()).build_node().into()
581 })
582 .with_variant("Cube", |_| {
583 MeshBuilder::new(BaseBuilder::new().with_name("Cube"))
584 .with_surfaces(vec![SurfaceBuilder::new(surface::CUBE.resource.clone())
585 .with_material(placeholder_material())
586 .build()])
587 .build_node()
588 .into()
589 })
590 .with_variant("Cone", |_| {
591 MeshBuilder::new(BaseBuilder::new().with_name("Cone"))
592 .with_surfaces(vec![SurfaceBuilder::new(surface::CONE.resource.clone())
593 .with_material(placeholder_material())
594 .build()])
595 .build_node()
596 .into()
597 })
598 .with_variant("Cylinder", |_| {
599 MeshBuilder::new(BaseBuilder::new().with_name("Cylinder"))
600 .with_surfaces(vec![SurfaceBuilder::new(
601 surface::CYLINDER.resource.clone(),
602 )
603 .with_material(placeholder_material())
604 .build()])
605 .build_node()
606 .into()
607 })
608 .with_variant("Sphere", |_| {
609 MeshBuilder::new(BaseBuilder::new().with_name("Sphere"))
610 .with_surfaces(vec![SurfaceBuilder::new(surface::SPHERE.resource.clone())
611 .with_material(placeholder_material())
612 .build()])
613 .build_node()
614 .into()
615 })
616 .with_variant("Quad", |_| {
617 MeshBuilder::new(BaseBuilder::new().with_name("Quad"))
618 .with_surfaces(vec![SurfaceBuilder::new(surface::QUAD.resource.clone())
619 .with_material(placeholder_material())
620 .build()])
621 .build_node()
622 .into()
623 })
624 .with_group("Mesh")
625 }
626}
627
628impl NodeTrait for Mesh {
629 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
632 if self.local_bounding_box_dirty.get() {
633 let mut bounding_box = AxisAlignedBoundingBox::default();
634
635 if let BatchingMode::Static = *self.batching_mode {
636 let container = self.batch_container.0.safe_lock();
637 for batch in container.batches.values() {
638 let data = batch.data.data_ref();
639 extend_aabb_from_vertex_buffer(&data.vertex_buffer, &mut bounding_box);
640 }
641 } else {
642 for surface in self.surfaces.iter() {
643 let data = surface.data();
644 if data.is_ok() {
645 let data = data.data_ref();
646 extend_aabb_from_vertex_buffer(&data.vertex_buffer, &mut bounding_box);
647 }
648 }
649 }
650
651 self.local_bounding_box.set(bounding_box);
652 self.local_bounding_box_dirty.set(false);
653 }
654
655 self.local_bounding_box.get()
656 }
657
658 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
660 self.world_bounding_box.get()
661 }
662
663 fn id(&self) -> Uuid {
664 Self::type_uuid()
665 }
666
667 fn on_global_transform_changed(
668 &self,
669 new_global_transform: &Matrix4<f32>,
670 context: &mut SyncContext,
671 ) {
672 if self.surfaces.iter().any(|s| !s.bones.is_empty()) {
673 let mut world_aabb = self.local_bounding_box().transform(new_global_transform);
674
675 for surface in self.surfaces.iter() {
677 for &bone in surface.bones() {
678 if let Ok(node) = context.nodes.try_borrow(bone) {
679 world_aabb.add_point(node.global_position())
680 }
681 }
682 }
683
684 self.world_bounding_box.set(world_aabb)
685 } else {
686 self.world_bounding_box
687 .set(self.local_bounding_box().transform(new_global_transform));
688 }
689 }
690
691 fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
692 if !self.should_be_rendered(ctx.frustum, ctx.render_mask) {
693 return RdcControlFlow::Continue;
694 }
695
696 if renderer::is_shadow_pass(ctx.render_pass_name) && !self.cast_shadows() {
697 return RdcControlFlow::Continue;
698 }
699
700 let sorting_index = ctx.calculate_sorting_index(self.global_position());
701
702 if let BatchingMode::Static = *self.batching_mode {
703 let mut container = self.batch_container.0.safe_lock();
704
705 if container.batches.is_empty() {
706 container.fill(self.handle(), ctx);
707 }
708
709 for batch in container.batches.values() {
710 ctx.storage.push(
711 &batch.data,
712 &batch.material,
713 self.render_path(),
714 sorting_index,
715 SurfaceInstanceData {
716 world_transform: Matrix4::identity(),
717 bone_matrices: Default::default(),
718 blend_shapes_weights: Default::default(),
719 element_range: ElementRange::Full,
720 node_handle: self.handle(),
721 },
722 );
723 }
724
725 RdcControlFlow::Break
726 } else {
727 for surface in self.surfaces().iter() {
728 if !surface.data_ref().is_ok() {
729 continue;
730 }
731 let is_skinned = !surface.bones.is_empty();
732
733 let world = if is_skinned {
734 Matrix4::identity()
735 } else {
736 self.global_transform()
737 };
738
739 let batching_mode = match *self.batching_mode {
740 BatchingMode::None => BatchingMode::None,
741 BatchingMode::Static => BatchingMode::Static,
742 BatchingMode::Dynamic => {
743 let surface_data_guard = surface.data_ref().data_ref();
744 if self.blend_shapes().is_empty()
745 && surface.bones().is_empty()
746 && surface_data_guard.vertex_buffer.vertex_count() < 256
747 {
748 BatchingMode::Dynamic
749 } else {
750 BatchingMode::None
751 }
752 }
753 };
754
755 match batching_mode {
756 BatchingMode::None => {
757 let surface_data = surface.data_ref();
758 let substitute_material = surface_data
759 .data_ref()
760 .blend_shapes_container
761 .as_ref()
762 .and_then(|c| c.blend_shape_storage.as_ref())
763 .map(|texture| {
764 let material_copy = surface.material().deep_copy();
765 material_copy.data_ref().bind(
766 &self.blend_shapes_property_name,
767 MaterialResourceBinding::Texture(MaterialTextureBinding {
768 value: Some(texture.clone()),
769 }),
770 );
771 material_copy
772 });
773
774 ctx.storage.push(
775 surface_data,
776 substitute_material.as_ref().unwrap_or(surface.material()),
777 self.render_path(),
778 sorting_index,
779 SurfaceInstanceData {
780 world_transform: world,
781 bone_matrices: surface
782 .bones
783 .iter()
784 .map(|bone_handle| {
785 if let Ok(bone_node) = 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<Mesh> {
970 graph.add_node(self.build_node()).to_variant()
971 }
972}