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