1use crate::components::Mesh;
2use crate::renderer::Vertex;
3use gizmo_math::Vec3;
4use std::sync::Arc;
5use wgpu::util::DeviceExt;
6
7impl super::AssetManager {
8 pub fn create_inverted_cube(device: &wgpu::Device) -> Mesh {
11 let positions: [[f32; 3]; 8] = [
14 [-1.0, -1.0, -1.0], [1.0, -1.0, -1.0], [1.0, 1.0, -1.0], [-1.0, 1.0, -1.0], [-1.0, -1.0, 1.0], [1.0, -1.0, 1.0], [1.0, 1.0, 1.0], [-1.0, 1.0, 1.0], ];
23
24 struct FaceDef {
25 indices: [usize; 6],
26 normal: [f32; 3],
27 uvs: [[f32; 2]; 6],
28 }
29
30 let faces: [FaceDef; 6] = [
31 FaceDef {
33 indices: [0, 1, 2, 0, 2, 3],
34 normal: [0.0, 0.0, 1.0],
35 uvs: [
36 [1.0, 1.0],
37 [0.0, 1.0],
38 [0.0, 0.0],
39 [1.0, 1.0],
40 [0.0, 0.0],
41 [1.0, 0.0],
42 ],
43 },
44 FaceDef {
46 indices: [4, 6, 5, 4, 7, 6],
47 normal: [0.0, 0.0, -1.0],
48 uvs: [
49 [0.0, 1.0],
50 [1.0, 0.0],
51 [1.0, 1.0],
52 [0.0, 1.0],
53 [0.0, 0.0],
54 [1.0, 0.0],
55 ],
56 },
57 FaceDef {
59 indices: [0, 5, 1, 0, 4, 5],
60 normal: [0.0, 1.0, 0.0],
61 uvs: [
62 [0.0, 0.0],
63 [1.0, 1.0],
64 [1.0, 0.0],
65 [0.0, 0.0],
66 [0.0, 1.0],
67 [1.0, 1.0],
68 ],
69 },
70 FaceDef {
72 indices: [3, 2, 6, 3, 6, 7],
73 normal: [0.0, -1.0, 0.0],
74 uvs: [
75 [0.0, 0.0],
76 [1.0, 0.0],
77 [1.0, 1.0],
78 [0.0, 0.0],
79 [1.0, 1.0],
80 [0.0, 1.0],
81 ],
82 },
83 FaceDef {
85 indices: [0, 3, 7, 0, 7, 4],
86 normal: [1.0, 0.0, 0.0],
87 uvs: [
88 [0.0, 1.0],
89 [0.0, 0.0],
90 [1.0, 0.0],
91 [0.0, 1.0],
92 [1.0, 0.0],
93 [1.0, 1.0],
94 ],
95 },
96 FaceDef {
98 indices: [1, 6, 2, 1, 5, 6],
99 normal: [-1.0, 0.0, 0.0],
100 uvs: [
101 [1.0, 1.0],
102 [0.0, 0.0],
103 [1.0, 0.0],
104 [1.0, 1.0],
105 [0.0, 1.0],
106 [0.0, 0.0],
107 ],
108 },
109 ];
110
111 let mut vertices = Vec::with_capacity(36);
112 for face in &faces {
113 for i in 0..6 {
114 vertices.push(Vertex {
115 position: positions[face.indices[i]],
116 color: [1.0, 1.0, 1.0],
117 normal: face.normal,
118 tex_coords: face.uvs[i],
119 joint_indices: [0; 4],
120 joint_weights: [0.0; 4],
121 });
122 }
123 }
124
125 let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
126 label: Some("Skybox Inverted Cube VBuf"),
127 contents: bytemuck::cast_slice(&vertices),
128 usage: wgpu::BufferUsages::VERTEX,
129 });
130
131 Mesh::new(
132 device,
133 Arc::new(vbuf),
134 &vertices,
135 Vec3::ZERO,
136 "inverted_cube".to_string(),
137 )
138 }
139
140 pub fn create_cube(device: &wgpu::Device) -> Mesh {
142 let positions: [[f32; 3]; 8] = [
143 [-1.0, -1.0, -1.0], [1.0, -1.0, -1.0], [1.0, 1.0, -1.0], [-1.0, 1.0, -1.0], [-1.0, -1.0, 1.0], [1.0, -1.0, 1.0], [1.0, 1.0, 1.0], [-1.0, 1.0, 1.0], ];
152
153 struct FaceDef {
156 indices: [usize; 6],
157 normal: [f32; 3],
158 uvs: [[f32; 2]; 6],
159 }
160
161 let faces: [FaceDef; 6] = [
162 FaceDef {
164 indices: [0, 2, 1, 0, 3, 2],
165 normal: [0.0, 0.0, -1.0],
166 uvs: [
167 [1.0, 1.0],
168 [0.0, 0.0],
169 [0.0, 1.0],
170 [1.0, 1.0],
171 [1.0, 0.0],
172 [0.0, 0.0],
173 ],
174 },
175 FaceDef {
177 indices: [4, 5, 6, 4, 6, 7],
178 normal: [0.0, 0.0, 1.0],
179 uvs: [
180 [0.0, 1.0],
181 [1.0, 1.0],
182 [1.0, 0.0],
183 [0.0, 1.0],
184 [1.0, 0.0],
185 [0.0, 0.0],
186 ],
187 },
188 FaceDef {
190 indices: [0, 1, 5, 0, 5, 4],
191 normal: [0.0, -1.0, 0.0],
192 uvs: [
193 [0.0, 0.0],
194 [1.0, 0.0],
195 [1.0, 1.0],
196 [0.0, 0.0],
197 [1.0, 1.0],
198 [0.0, 1.0],
199 ],
200 },
201 FaceDef {
203 indices: [3, 6, 2, 3, 7, 6],
204 normal: [0.0, 1.0, 0.0],
205 uvs: [
206 [0.0, 0.0],
207 [1.0, 1.0],
208 [1.0, 0.0],
209 [0.0, 0.0],
210 [0.0, 1.0],
211 [1.0, 1.0],
212 ],
213 },
214 FaceDef {
216 indices: [0, 4, 7, 0, 7, 3],
217 normal: [-1.0, 0.0, 0.0],
218 uvs: [
219 [0.0, 1.0],
220 [1.0, 1.0],
221 [1.0, 0.0],
222 [0.0, 1.0],
223 [1.0, 0.0],
224 [0.0, 0.0],
225 ],
226 },
227 FaceDef {
229 indices: [1, 2, 6, 1, 6, 5],
230 normal: [1.0, 0.0, 0.0],
231 uvs: [
232 [1.0, 1.0],
233 [1.0, 0.0],
234 [0.0, 0.0],
235 [1.0, 1.0],
236 [0.0, 0.0],
237 [0.0, 1.0],
238 ],
239 },
240 ];
241
242 let mut vertices = Vec::with_capacity(36);
243 for face in &faces {
244 for i in 0..6 {
245 vertices.push(Vertex {
246 position: positions[face.indices[i]],
247 color: [1.0, 1.0, 1.0],
248 normal: face.normal,
249 tex_coords: face.uvs[i],
250 joint_indices: [0; 4],
251 joint_weights: [0.0; 4],
252 });
253 }
254 }
255
256 let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
257 label: Some("Standard Cube VBuf"),
258 contents: bytemuck::cast_slice(&vertices),
259 usage: wgpu::BufferUsages::VERTEX,
260 });
261
262 Mesh::new(
263 device,
264 Arc::new(vbuf),
265 &vertices,
266 Vec3::ZERO,
267 "standard_cube".to_string(),
268 )
269 }
270
271 pub fn create_gizmo_arrow(device: &wgpu::Device) -> Mesh {
272 let w = 0.03; let hw = 0.12; let sl = 0.8; let positions: [[f32; 3]; 13] = [
277 [-w, 0.0, -w],
279 [w, 0.0, -w],
280 [w, sl, -w],
281 [-w, sl, -w],
282 [-w, 0.0, w],
283 [w, 0.0, w],
284 [w, sl, w],
285 [-w, sl, w],
286 [-hw, sl, -hw],
288 [hw, sl, -hw],
289 [hw, sl, hw],
290 [-hw, sl, hw],
291 [0.0, 1.0, 0.0],
293 ];
294
295 let head_dy = 1.0 - sl;
296 let head_dxz = hw;
297 let head_norm_len = (head_dy * head_dy + head_dxz * head_dxz).sqrt();
298 let n_y = head_dxz / head_norm_len;
299 let n_xz = head_dy / head_norm_len;
300
301 let faces: Vec<(Vec<usize>, [f32; 3])> = vec![
303 (vec![0, 2, 1, 0, 3, 2], [0.0, 0.0, -1.0]), (vec![4, 5, 6, 4, 6, 7], [0.0, 0.0, 1.0]), (vec![0, 1, 5, 0, 5, 4], [0.0, -1.0, 0.0]), (vec![0, 4, 7, 0, 7, 3], [-1.0, 0.0, 0.0]), (vec![1, 2, 6, 1, 6, 5], [1.0, 0.0, 0.0]), (vec![8, 9, 10, 8, 10, 11], [0.0, -1.0, 0.0]),
311 (vec![11, 10, 12], [0.0, n_y, n_xz]), (vec![9, 8, 12], [0.0, n_y, -n_xz]), (vec![10, 9, 12], [n_xz, n_y, 0.0]), (vec![8, 11, 12], [-n_xz, n_y, 0.0]), ];
317
318 let mut vertices = Vec::new();
319 for (indices, normal) in faces {
320 for idx in indices {
321 vertices.push(Vertex {
322 position: positions[idx],
323 color: [1.0, 1.0, 1.0],
324 normal,
325 tex_coords: [0.0, 0.0],
326 joint_indices: [0; 4],
327 joint_weights: [0.0; 4],
328 });
329 }
330 }
331
332 let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
333 label: Some("Gizmo Arrow VBuf"),
334 contents: bytemuck::cast_slice(&vertices),
335 usage: wgpu::BufferUsages::VERTEX,
336 });
337
338 Mesh::new(
339 device,
340 Arc::new(vbuf),
341 &vertices,
342 Vec3::ZERO,
343 "gizmo_arrow".to_string(),
344 )
345 }
346
347 pub fn create_plane(device: &wgpu::Device, size: f32) -> Mesh {
349 let half = size / 2.0;
350 let y = 0.0;
351
352 let def_j = [0; 4];
354 let def_w = [0.0; 4];
355 let vertices = [
356 Vertex {
358 position: [-half, y, -half],
359 color: [1.0, 1.0, 1.0],
360 normal: [0.0, 1.0, 0.0],
361 tex_coords: [0.0, 0.0],
362 joint_indices: def_j,
363 joint_weights: def_w,
364 },
365 Vertex {
366 position: [half, y, half],
367 color: [1.0, 1.0, 1.0],
368 normal: [0.0, 1.0, 0.0],
369 tex_coords: [size, size],
370 joint_indices: def_j,
371 joint_weights: def_w,
372 },
373 Vertex {
374 position: [half, y, -half],
375 color: [1.0, 1.0, 1.0],
376 normal: [0.0, 1.0, 0.0],
377 tex_coords: [size, 0.0],
378 joint_indices: def_j,
379 joint_weights: def_w,
380 },
381 Vertex {
383 position: [-half, y, -half],
384 color: [1.0, 1.0, 1.0],
385 normal: [0.0, 1.0, 0.0],
386 tex_coords: [0.0, 0.0],
387 joint_indices: def_j,
388 joint_weights: def_w,
389 },
390 Vertex {
391 position: [-half, y, half],
392 color: [1.0, 1.0, 1.0],
393 normal: [0.0, 1.0, 0.0],
394 tex_coords: [0.0, size],
395 joint_indices: def_j,
396 joint_weights: def_w,
397 },
398 Vertex {
399 position: [half, y, half],
400 color: [1.0, 1.0, 1.0],
401 normal: [0.0, 1.0, 0.0],
402 tex_coords: [size, size],
403 joint_indices: def_j,
404 joint_weights: def_w,
405 },
406 ];
407
408 let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
409 label: Some("Plane VBuf"),
410 contents: bytemuck::cast_slice(&vertices),
411 usage: wgpu::BufferUsages::VERTEX,
412 });
413
414 Mesh::new(
415 device,
416 Arc::new(vbuf),
417 &vertices,
418 Vec3::ZERO,
419 format!("plane_{}", size),
420 )
421 }
422
423 pub fn create_editor_grid_mesh(device: &wgpu::Device, extents: f32) -> Mesh {
425 let mut vertices = Vec::new();
426 let scale = extents;
428 let v = [
429 [-scale, 0.0, -scale],
430 [scale, 0.0, -scale],
431 [scale, 0.0, scale],
432 [-scale, 0.0, scale],
433 ];
434
435 let indices = [0, 2, 1, 0, 3, 2];
436 for i in indices {
437 vertices.push(Vertex {
438 position: v[i],
439 color: [1.0, 1.0, 1.0],
440 normal: [0.0, 1.0, 0.0],
441 tex_coords: [0.0, 0.0],
442 joint_indices: [0; 4],
443 joint_weights: [0.0; 4],
444 });
445 }
446
447 let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
448 label: Some("Editor Infinite Grid VBuf"),
449 contents: bytemuck::cast_slice(&vertices),
450 usage: wgpu::BufferUsages::VERTEX,
451 });
452
453 Mesh::new(
454 device,
455 Arc::new(vbuf),
456 &vertices,
457 Vec3::ZERO,
458 "editor_grid".to_string(),
459 )
460 }
461
462 pub fn create_sprite_quad(device: &wgpu::Device, width: f32, height: f32) -> Mesh {
465 let hw = width / 2.0;
466 let hh = height / 2.0;
467 let def_j = [0; 4];
468 let def_w = [0.0; 4];
469
470 let vertices = [
472 Vertex {
473 position: [-hw, -hh, 0.0],
474 color: [1.0, 1.0, 1.0],
475 normal: [0.0, 0.0, 1.0],
476 tex_coords: [0.0, 1.0],
477 joint_indices: def_j,
478 joint_weights: def_w,
479 },
480 Vertex {
481 position: [hw, -hh, 0.0],
482 color: [1.0, 1.0, 1.0],
483 normal: [0.0, 0.0, 1.0],
484 tex_coords: [1.0, 1.0],
485 joint_indices: def_j,
486 joint_weights: def_w,
487 },
488 Vertex {
489 position: [hw, hh, 0.0],
490 color: [1.0, 1.0, 1.0],
491 normal: [0.0, 0.0, 1.0],
492 tex_coords: [1.0, 0.0],
493 joint_indices: def_j,
494 joint_weights: def_w,
495 },
496 Vertex {
497 position: [hw, hh, 0.0],
498 color: [1.0, 1.0, 1.0],
499 normal: [0.0, 0.0, 1.0],
500 tex_coords: [1.0, 0.0],
501 joint_indices: def_j,
502 joint_weights: def_w,
503 },
504 Vertex {
505 position: [-hw, hh, 0.0],
506 color: [1.0, 1.0, 1.0],
507 normal: [0.0, 0.0, 1.0],
508 tex_coords: [0.0, 0.0],
509 joint_indices: def_j,
510 joint_weights: def_w,
511 },
512 Vertex {
513 position: [-hw, -hh, 0.0],
514 color: [1.0, 1.0, 1.0],
515 normal: [0.0, 0.0, 1.0],
516 tex_coords: [0.0, 1.0],
517 joint_indices: def_j,
518 joint_weights: def_w,
519 },
520 ];
521
522 let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
523 label: Some("Sprite Quad VBuf"),
524 contents: bytemuck::cast_slice(&vertices),
525 usage: wgpu::BufferUsages::VERTEX,
526 });
527
528 Mesh::new(
529 device,
530 Arc::new(vbuf),
531 &vertices,
532 Vec3::ZERO,
533 "sprite_quad".to_string(),
534 )
535 }
536
537 pub fn create_sphere(device: &wgpu::Device, radius: f32, stacks: u32, slices: u32) -> Mesh {
539 let stacks = stacks.max(3);
540 let slices = slices.max(3);
541 let mut vertices = Vec::new();
542 let pi = std::f32::consts::PI;
543
544 for i in 0..stacks {
545 let theta1 = (i as f32 / stacks as f32) * pi;
546 let theta2 = ((i + 1) as f32 / stacks as f32) * pi;
547
548 for j in 0..slices {
549 let phi1 = (j as f32 / slices as f32) * 2.0 * pi;
550 let phi2 = ((j + 1) as f32 / slices as f32) * 2.0 * pi;
551
552 let p1 = [
554 radius * theta1.sin() * phi1.cos(),
555 radius * theta1.cos(),
556 radius * theta1.sin() * phi1.sin(),
557 ];
558 let p2 = [
559 radius * theta2.sin() * phi1.cos(),
560 radius * theta2.cos(),
561 radius * theta2.sin() * phi1.sin(),
562 ];
563 let p3 = [
564 radius * theta2.sin() * phi2.cos(),
565 radius * theta2.cos(),
566 radius * theta2.sin() * phi2.sin(),
567 ];
568 let p4 = [
569 radius * theta1.sin() * phi2.cos(),
570 radius * theta1.cos(),
571 radius * theta1.sin() * phi2.sin(),
572 ];
573
574 let n1 = [
575 theta1.sin() * phi1.cos(),
576 theta1.cos(),
577 theta1.sin() * phi1.sin(),
578 ];
579 let n2 = [
580 theta2.sin() * phi1.cos(),
581 theta2.cos(),
582 theta2.sin() * phi1.sin(),
583 ];
584 let n3 = [
585 theta2.sin() * phi2.cos(),
586 theta2.cos(),
587 theta2.sin() * phi2.sin(),
588 ];
589 let n4 = [
590 theta1.sin() * phi2.cos(),
591 theta1.cos(),
592 theta1.sin() * phi2.sin(),
593 ];
594
595 let uv1 = [
596 if i == 0 {
597 (j as f32 + 0.5) / slices as f32
598 } else {
599 j as f32 / slices as f32
600 },
601 i as f32 / stacks as f32,
602 ];
603 let uv2 = [
604 if i + 1 == stacks {
605 (j as f32 + 0.5) / slices as f32
606 } else {
607 j as f32 / slices as f32
608 },
609 (i + 1) as f32 / stacks as f32,
610 ];
611 let uv3 = [
612 if i + 1 == stacks {
613 (j as f32 + 0.5) / slices as f32
614 } else {
615 (j + 1) as f32 / slices as f32
616 },
617 (i + 1) as f32 / stacks as f32,
618 ];
619 let uv4 = [
620 if i == 0 {
621 (j as f32 + 0.5) / slices as f32
622 } else {
623 (j + 1) as f32 / slices as f32
624 },
625 i as f32 / stacks as f32,
626 ];
627
628 let def_j = [0; 4];
629 let def_w = [0.0; 4];
630
631 vertices.push(Vertex {
633 position: p1,
634 color: [1.0; 3],
635 normal: n1,
636 tex_coords: uv1,
637 joint_indices: def_j,
638 joint_weights: def_w,
639 });
640 vertices.push(Vertex {
641 position: p2,
642 color: [1.0; 3],
643 normal: n2,
644 tex_coords: uv2,
645 joint_indices: def_j,
646 joint_weights: def_w,
647 });
648 vertices.push(Vertex {
649 position: p3,
650 color: [1.0; 3],
651 normal: n3,
652 tex_coords: uv3,
653 joint_indices: def_j,
654 joint_weights: def_w,
655 });
656 vertices.push(Vertex {
658 position: p1,
659 color: [1.0; 3],
660 normal: n1,
661 tex_coords: uv1,
662 joint_indices: def_j,
663 joint_weights: def_w,
664 });
665 vertices.push(Vertex {
666 position: p3,
667 color: [1.0; 3],
668 normal: n3,
669 tex_coords: uv3,
670 joint_indices: def_j,
671 joint_weights: def_w,
672 });
673 vertices.push(Vertex {
674 position: p4,
675 color: [1.0; 3],
676 normal: n4,
677 tex_coords: uv4,
678 joint_indices: def_j,
679 joint_weights: def_w,
680 });
681 }
682 }
683
684 let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
685 label: Some("Sphere VBuf"),
686 contents: bytemuck::cast_slice(&vertices),
687 usage: wgpu::BufferUsages::VERTEX,
688 });
689
690 Mesh::new(
691 device,
692 Arc::new(vbuf),
693 &vertices,
694 Vec3::ZERO,
695 format!("sphere_{}_{}_{}", radius, stacks, slices),
696 )
697 }
698
699 pub fn create_terrain(
700 device: &wgpu::Device,
701 heightmap_path: &str,
702 width: f32,
703 depth: f32,
704 max_height: f32,
705 ) -> Result<(Mesh, Vec<f32>, u32, u32), String> {
706 let canonical = std::path::Path::new(heightmap_path)
707 .canonicalize()
708 .map(|p| p.to_string_lossy().to_string())
709 .unwrap_or_else(|_| heightmap_path.to_string());
710
711 let img = image::open(&canonical)
712 .map_err(|e| format!("Heightmap yuklenemedi! {} ({})", canonical, e))?
713 .into_luma8(); let (img_width, img_height) = img.dimensions();
716 if img_width < 2 || img_height < 2 {
717 return Err(
718 "Heightmap boyutlari en az 2x2 olmalidir. 1x1 piksel ile arazi olusturulamaz."
719 .to_string(),
720 );
721 }
722 let mut vertices: Vec<Vertex> = Vec::with_capacity((img_width * img_height) as usize);
725 let mut heights: Vec<f32> = Vec::with_capacity((img_width * img_height) as usize);
726
727 let half_w = width / 2.0;
728 let half_d = depth / 2.0;
729
730 for y in 0..img_height {
732 for x in 0..img_width {
733 let pixel = img.get_pixel(x, y)[0] as f32 / 255.0; heights.push(pixel);
735 let world_y = pixel * max_height;
736
737 let world_x = -half_w + (x as f32 / (img_width as f32 - 1.0)) * width;
738 let world_z = -half_d + (y as f32 / (img_height as f32 - 1.0)) * depth;
739
740 let uv_x = (x as f32 / (img_width as f32 - 1.0)) * 10.0;
742 let uv_y = (y as f32 / (img_height as f32 - 1.0)) * 10.0;
743
744 vertices.push(Vertex {
745 position: [world_x, world_y, world_z],
746 color: [1.0, 1.0, 1.0],
747 normal: [0.0, 1.0, 0.0], tex_coords: [uv_x, uv_y],
749 joint_indices: [0; 4],
750 joint_weights: [0.0; 4],
751 });
752 }
753 }
754
755 let mut indices = Vec::with_capacity(((img_width - 1) * (img_height - 1) * 6) as usize);
757 for y in 0..(img_height - 1) {
758 for x in 0..(img_width - 1) {
759 let i0 = y * img_width + x;
760 let i1 = y * img_width + (x + 1);
761 let i2 = (y + 1) * img_width + x;
762 let i3 = (y + 1) * img_width + (x + 1);
763
764 indices.push(i0);
766 indices.push(i2);
767 indices.push(i1);
768
769 indices.push(i1);
771 indices.push(i2);
772 indices.push(i3);
773 }
774 }
775
776 let mut final_vertices = Vec::with_capacity(indices.len());
778 for chunk in indices.chunks(3) {
779 let i0 = chunk[0] as usize;
780 let i1 = chunk[1] as usize;
781 let i2 = chunk[2] as usize;
782
783 let p0 = Vec3::from_array(vertices[i0].position);
784 let p1 = Vec3::from_array(vertices[i1].position);
785 let p2 = Vec3::from_array(vertices[i2].position);
786
787 let norm = (p1 - p0).cross(p2 - p0);
788 let normal = if norm.length_squared() > 1e-6 {
789 norm.normalize()
790 } else {
791 Vec3::new(0.0, 1.0, 0.0)
792 };
793
794 let mut v0 = vertices[i0];
796 v0.normal = [normal.x, normal.y, normal.z];
797 let mut v1 = vertices[i1];
798 v1.normal = [normal.x, normal.y, normal.z];
799 let mut v2 = vertices[i2];
800 v2.normal = [normal.x, normal.y, normal.z];
801
802 final_vertices.push(v0);
803 final_vertices.push(v1);
804 final_vertices.push(v2);
805 }
806
807 let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
808 label: Some(&format!("Terrain ({})", heightmap_path)),
809 contents: bytemuck::cast_slice(&final_vertices),
810 usage: wgpu::BufferUsages::VERTEX,
811 });
812
813 let mesh = Mesh::new(
814 device,
815 Arc::new(vbuf),
816 &final_vertices,
817 Vec3::ZERO,
818 format!("terrain:{}", heightmap_path),
819 );
820 Ok((mesh, heights, img_width, img_height))
821 }
822}