celestialsim/
scatter_mesh.rs1use godot::classes::base_material_3d::{CullMode, Flags};
20use godot::classes::mesh::{ArrayType, PrimitiveType};
21use godot::classes::{ArrayMesh, StandardMaterial3D};
22use godot::prelude::*;
23
24const ROWS: [(f32, f32, f32); 5] = [
27 (0.00, 0.050, 0.000),
28 (0.35, 0.046, 0.020),
29 (0.62, 0.038, 0.065),
30 (0.84, 0.026, 0.130),
31 (1.00, 0.000, 0.220),
32];
33
34const COLORS: [Color; 5] = [
36 Color::from_rgb(0.05, 0.20, 0.03),
37 Color::from_rgb(0.08, 0.28, 0.04),
38 Color::from_rgb(0.14, 0.40, 0.06),
39 Color::from_rgb(0.24, 0.52, 0.09),
40 Color::from_rgb(0.38, 0.62, 0.13),
41];
42
43const ROUND_ANGLE: f32 = 0.7;
46
47pub fn grass_blade_mesh() -> Gd<ArrayMesh> {
50 let mut verts = PackedVector3Array::new();
51 let mut normals = PackedVector3Array::new();
52 let mut colors = PackedColorArray::new();
53
54 let (sin_a, cos_a) = ROUND_ANGLE.sin_cos();
55 let n_rows = ROWS.len();
56 for (row, &(y, hw, bend)) in ROWS.iter().enumerate() {
57 let t = row as f32 / (n_rows - 1) as f32;
58 let forward = (Vector3::new(0.0, 0.0, 1.0).lerp(Vector3::new(0.0, 1.0, 0.0), t)).normalized();
60 let tangent = Vector3::new(1.0, 0.0, 0.0);
61 let left_n = (forward * cos_a - tangent * sin_a).normalized();
63 let right_n = (forward * cos_a + tangent * sin_a).normalized();
64 if row + 1 < n_rows {
65 verts.push(Vector3::new(-hw, y, bend));
66 verts.push(Vector3::new(hw, y, bend));
67 normals.push(left_n);
68 normals.push(right_n);
69 colors.push(COLORS[row]);
70 colors.push(COLORS[row]);
71 } else {
72 verts.push(Vector3::new(0.0, y, bend)); normals.push(forward);
74 colors.push(COLORS[row]);
75 }
76 }
77
78 let mut indices: Vec<i32> = Vec::new();
81 for row in 0..n_rows - 1 {
82 let a = (2 * row) as i32; let b = a + 1; if row + 2 < n_rows {
85 let c = (2 * (row + 1)) as i32; let d = c + 1; indices.extend_from_slice(&[a, c, b, b, c, d]);
88 } else {
89 let tip = (2 * (n_rows - 1)) as i32;
90 indices.extend_from_slice(&[a, tip, b]);
91 }
92 }
93 let indices: PackedInt32Array = indices.into_iter().collect();
94
95 let mut arrays = VarArray::new();
96 arrays.resize(ArrayType::MAX.ord() as usize, &Variant::nil());
97 arrays.set(ArrayType::VERTEX.ord() as usize, &verts.to_variant());
98 arrays.set(ArrayType::NORMAL.ord() as usize, &normals.to_variant());
99 arrays.set(ArrayType::COLOR.ord() as usize, &colors.to_variant());
100 arrays.set(ArrayType::INDEX.ord() as usize, &indices.to_variant());
101
102 let mut material = StandardMaterial3D::new_gd();
103 material.set_flag(Flags::ALBEDO_FROM_VERTEX_COLOR, true);
104 material.set_cull_mode(CullMode::DISABLED);
105 material.set_roughness(0.85);
106 material.set_specular(0.3);
107
108 let mut mesh = ArrayMesh::new_gd();
109 mesh.add_surface_from_arrays(PrimitiveType::TRIANGLES, &arrays);
110 mesh.surface_set_material(0, &material.upcast::<godot::classes::Material>());
111 mesh
112}