Skip to main content

gizmo_renderer/asset/
primitives.rs

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    /// İçi boş ters yüzlü küp (Skybox) mesh üretir.
9    /// Normaller içe bakar, böylece kamera küpün merkezinden dışarıya baktığında yüzeyler görünür.
10    pub fn create_inverted_cube(device: &wgpu::Device) -> Mesh {
11        // 6 yüz × 2 üçgen × 3 köşe = 36 vertex
12        // Her yüzün normali İÇE bakar (ters küp)
13        let positions: [[f32; 3]; 8] = [
14            [-1.0, -1.0, -1.0], // 0
15            [1.0, -1.0, -1.0],  // 1
16            [1.0, 1.0, -1.0],   // 2
17            [-1.0, 1.0, -1.0],  // 3
18            [-1.0, -1.0, 1.0],  // 4
19            [1.0, -1.0, 1.0],   // 5
20            [1.0, 1.0, 1.0],    // 6
21            [-1.0, 1.0, 1.0],   // 7
22        ];
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            // Arka (+Z içe)
32            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            // Ön (-Z içe)
45            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            // Alt (+Y içe)
58            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            // Üst (-Y içe)
71            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            // Sol (+X içe)
84            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            // Sağ (-X içe)
97            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    /// Düzenli Küp mesh üretir (Dışa bakan normaller, PBR ışıklandırma ve gölgelendirme için doğru)
141    pub fn create_cube(device: &wgpu::Device) -> Mesh {
142        let positions: [[f32; 3]; 8] = [
143            [-1.0, -1.0, -1.0], // 0
144            [1.0, -1.0, -1.0],  // 1
145            [1.0, 1.0, -1.0],   // 2
146            [-1.0, 1.0, -1.0],  // 3
147            [-1.0, -1.0, 1.0],  // 4
148            [1.0, -1.0, 1.0],   // 5
149            [1.0, 1.0, 1.0],    // 6
150            [-1.0, 1.0, 1.0],   // 7
151        ];
152
153        // Her yüz: 6 vertex indeksi, normal, ve 6 UV koordinatı
154        // UV'ler her üçgen için sırasıyla: tri1(v0,v1,v2), tri2(v3,v4,v5)
155        struct FaceDef {
156            indices: [usize; 6],
157            normal: [f32; 3],
158            uvs: [[f32; 2]; 6],
159        }
160
161        let faces: [FaceDef; 6] = [
162            // Arka (-Z)
163            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            // Ön (+Z)
176            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            // Alt (-Y)
189            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            // Üst (+Y)
202            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            // Sol (-X)
215            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            // Sağ (+X)
228            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; // Shaft thickness
273        let hw = 0.12; // Head width
274        let sl = 0.8; // Shaft length
275
276        let positions: [[f32; 3]; 13] = [
277            // Shaft (0..8)
278            [-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            // Head Base (8..12)
287            [-hw, sl, -hw],
288            [hw, sl, -hw],
289            [hw, sl, hw],
290            [-hw, sl, hw],
291            // Apex (12)
292            [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        // Tuple of (Indices, Normal)
302        let faces: Vec<(Vec<usize>, [f32; 3])> = vec![
303            // Shaft
304            (vec![0, 2, 1, 0, 3, 2], [0.0, 0.0, -1.0]), // Back
305            (vec![4, 5, 6, 4, 6, 7], [0.0, 0.0, 1.0]),  // Front
306            (vec![0, 1, 5, 0, 5, 4], [0.0, -1.0, 0.0]), // Bottom
307            (vec![0, 4, 7, 0, 7, 3], [-1.0, 0.0, 0.0]), // Left
308            (vec![1, 2, 6, 1, 6, 5], [1.0, 0.0, 0.0]),  // Right
309            // Arrowhead Base
310            (vec![8, 9, 10, 8, 10, 11], [0.0, -1.0, 0.0]),
311            // Arrowhead Sides
312            (vec![11, 10, 12], [0.0, n_y, n_xz]), // Front (+Z)
313            (vec![9, 8, 12], [0.0, n_y, -n_xz]),  // Back (-Z)
314            (vec![10, 9, 12], [n_xz, n_y, 0.0]),  // Right (+X)
315            (vec![8, 11, 12], [-n_xz, n_y, 0.0]), // Left (-X)
316        ];
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    /// Basit, yatay bir düzlem (Plane) üretir.
348    pub fn create_plane(device: &wgpu::Device, size: f32) -> Mesh {
349        let half = size / 2.0;
350        let y = 0.0;
351
352        // Üstten bakışla Saat yönünün tersi (CCW) 2 üçgen (Quad)
353        let def_j = [0; 4];
354        let def_w = [0.0; 4];
355        let vertices = [
356            // İlk Üçgen (CCW from above)
357            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            // İkinci Üçgen (CCW from above)
382            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    /// Editör sahneleri için GPU'da çizilen sonsuz grid mesh (tek bir quad). Shader içinde matematiksel olarak çizilir.
424    pub fn create_editor_grid_mesh(device: &wgpu::Device, extents: f32) -> Mesh {
425        let mut vertices = Vec::new();
426        // Zemin boyunca devasa bir XY (veya XZ düzleminde) quad oluştur.
427        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    /// 2D Sprite dörtgeni oluşturur (XY düzleminde, kameraya paralel).
463    /// Ortografik projeksiyon ile kullanıldığında 2D oyun desteği sağlar.
464    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        // XY düzleminde dörtgen (Z=0), kameraya bakan yön +Z
471        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    /// Programatik UV Küre (Sphere) üretir.
538    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                // 4 köşe noktası
553                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                // Üçgen 1
632                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                // Üçgen 2
657                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(); // Grayscale format
714
715        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        // Sınırlama: 512x512'den büyükse performans için uyar ya da downscale et
723
724        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        // 1. GRID VERTEX'LERİ ÜRET
731        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; // 0.0 - 1.0
734                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                // UV Mapping: Repeat 10 times across terrain so grass doesn't look stretched
741                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], // İlk başta düz yukarı
748                    tex_coords: [uv_x, uv_y],
749                    joint_indices: [0; 4],
750                    joint_weights: [0.0; 4],
751                });
752            }
753        }
754
755        // 2. INDEX'LERİ OLUŞTUR VE NORMALLERİ HESAPLA
756        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                // Triangle 1
765                indices.push(i0);
766                indices.push(i2);
767                indices.push(i1);
768
769                // Triangle 2
770                indices.push(i1);
771                indices.push(i2);
772                indices.push(i3);
773            }
774        }
775
776        // Face ve Smooth Normalleri hesapla
777        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            // Triangle count for WGPU. Note: using flat normal per face first, optionally can be smoothed
795            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}