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], ..Default::default()
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        struct FaceDef {
154            indices: [usize; 6],
155            normal: [f32; 3],
156            uvs: [[f32; 2]; 6],
157        }
158
159        let faces: [FaceDef; 6] = [
160            // Arka (-Z)
161            FaceDef {
162                indices: [1, 0, 3, 1, 3, 2],
163                normal: [0.0, 0.0, -1.0],
164                uvs: [
165                    [0.0, 1.0],
166                    [1.0, 1.0],
167                    [1.0, 0.0],
168                    [0.0, 1.0],
169                    [1.0, 0.0],
170                    [0.0, 0.0],
171                ],
172            },
173            // Ön (+Z)
174            FaceDef {
175                indices: [4, 5, 6, 4, 6, 7],
176                normal: [0.0, 0.0, 1.0],
177                uvs: [
178                    [0.0, 1.0],
179                    [1.0, 1.0],
180                    [1.0, 0.0],
181                    [0.0, 1.0],
182                    [1.0, 0.0],
183                    [0.0, 0.0],
184                ],
185            },
186            // Alt (-Y)
187            FaceDef {
188                indices: [0, 1, 5, 0, 5, 4],
189                normal: [0.0, -1.0, 0.0],
190                uvs: [
191                    [0.0, 0.0],
192                    [1.0, 0.0],
193                    [1.0, 1.0],
194                    [0.0, 0.0],
195                    [1.0, 1.0],
196                    [0.0, 1.0],
197                ],
198            },
199            // Üst (+Y)
200            FaceDef {
201                indices: [7, 6, 2, 7, 2, 3],
202                normal: [0.0, 1.0, 0.0],
203                uvs: [
204                    [0.0, 1.0],
205                    [1.0, 1.0],
206                    [1.0, 0.0],
207                    [0.0, 1.0],
208                    [1.0, 0.0],
209                    [0.0, 0.0],
210                ],
211            },
212            // Sol (-X)
213            FaceDef {
214                indices: [0, 4, 7, 0, 7, 3],
215                normal: [-1.0, 0.0, 0.0],
216                uvs: [
217                    [0.0, 1.0],
218                    [1.0, 1.0],
219                    [1.0, 0.0],
220                    [0.0, 1.0],
221                    [1.0, 0.0],
222                    [0.0, 0.0],
223                ],
224            },
225            // Sağ (+X)
226            FaceDef {
227                indices: [5, 1, 2, 5, 2, 6],
228                normal: [1.0, 0.0, 0.0],
229                uvs: [
230                    [0.0, 1.0],
231                    [1.0, 1.0],
232                    [1.0, 0.0],
233                    [0.0, 1.0],
234                    [1.0, 0.0],
235                    [0.0, 0.0],
236                ],
237            },
238        ];
239
240        let mut vertices = Vec::with_capacity(36);
241        for face in &faces {
242            for i in 0..6 {
243                vertices.push(Vertex {
244                    position: positions[face.indices[i]],
245                    color: [1.0, 1.0, 1.0],
246                    normal: face.normal,
247                    tex_coords: face.uvs[i],
248                    joint_indices: [0; 4],
249                    joint_weights: [0.0; 4], ..Default::default()
250                });
251            }
252        }
253
254        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
255            label: Some("Standard Cube VBuf"),
256            contents: bytemuck::cast_slice(&vertices),
257            usage: wgpu::BufferUsages::VERTEX,
258        });
259
260        Mesh::new(
261            device,
262            Arc::new(vbuf),
263            &vertices,
264            Vec3::ZERO,
265            "standard_cube".to_string(),
266        )
267    }
268
269    pub fn create_gizmo_arrow(device: &wgpu::Device) -> Mesh {
270        let w = 0.03; // Shaft thickness
271        let hw = 0.12; // Head width
272        let sl = 0.8; // Shaft length
273
274        let positions: [[f32; 3]; 13] = [
275            // Shaft (0..8)
276            [-w, 0.0, -w],
277            [w, 0.0, -w],
278            [w, sl, -w],
279            [-w, sl, -w],
280            [-w, 0.0, w],
281            [w, 0.0, w],
282            [w, sl, w],
283            [-w, sl, w],
284            // Head Base (8..12)
285            [-hw, sl, -hw],
286            [hw, sl, -hw],
287            [hw, sl, hw],
288            [-hw, sl, hw],
289            // Apex (12)
290            [0.0, 1.0, 0.0],
291        ];
292
293        let head_dy = 1.0 - sl;
294        let head_dxz = hw;
295        let head_norm_len = (head_dy * head_dy + head_dxz * head_dxz).sqrt();
296        let n_y = head_dxz / head_norm_len;
297        let n_xz = head_dy / head_norm_len;
298
299        // Tuple of (Indices, Normal)
300        let faces: Vec<(Vec<usize>, [f32; 3])> = vec![
301            // Shaft
302            (vec![0, 2, 1, 0, 3, 2], [0.0, 0.0, -1.0]), // Back
303            (vec![4, 5, 6, 4, 6, 7], [0.0, 0.0, 1.0]),  // Front
304            (vec![0, 1, 5, 0, 5, 4], [0.0, -1.0, 0.0]), // Bottom
305            (vec![0, 4, 7, 0, 7, 3], [-1.0, 0.0, 0.0]), // Left
306            (vec![1, 2, 6, 1, 6, 5], [1.0, 0.0, 0.0]),  // Right
307            // Arrowhead Base
308            (vec![8, 9, 10, 8, 10, 11], [0.0, -1.0, 0.0]),
309            // Arrowhead Sides
310            (vec![11, 10, 12], [0.0, n_y, n_xz]), // Front (+Z)
311            (vec![9, 8, 12], [0.0, n_y, -n_xz]),  // Back (-Z)
312            (vec![10, 9, 12], [n_xz, n_y, 0.0]),  // Right (+X)
313            (vec![8, 11, 12], [-n_xz, n_y, 0.0]), // Left (-X)
314        ];
315
316        let mut vertices = Vec::new();
317        for (indices, normal) in faces {
318            for idx in indices {
319                vertices.push(Vertex {
320                    position: positions[idx],
321                    color: [1.0, 1.0, 1.0],
322                    normal,
323                    tex_coords: [0.0, 0.0],
324                    joint_indices: [0; 4],
325                    joint_weights: [0.0; 4], ..Default::default()
326                });
327            }
328        }
329
330        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
331            label: Some("Gizmo Arrow VBuf"),
332            contents: bytemuck::cast_slice(&vertices),
333            usage: wgpu::BufferUsages::VERTEX,
334        });
335
336        Mesh::new(
337            device,
338            Arc::new(vbuf),
339            &vertices,
340            Vec3::ZERO,
341            "gizmo_arrow".to_string(),
342        )
343    }
344
345    /// Basit, yatay bir düzlem (Plane) üretir.
346    pub fn create_plane(device: &wgpu::Device, size: f32) -> Mesh {
347        let half = size / 2.0;
348        let y = 0.0;
349
350        // Üstten bakışla Saat yönünün tersi (CCW) 2 üçgen (Quad)
351        let def_j = [0; 4];
352        let def_w = [0.0; 4];
353        let vertices = [
354            // İlk Üçgen (CW from above)
355            Vertex {
356                position: [-half, y, -half],
357                color: [1.0, 1.0, 1.0],
358                normal: [0.0, 1.0, 0.0],
359                tex_coords: [0.0, 0.0],
360                joint_indices: def_j,
361                joint_weights: def_w, ..Default::default()
362            },
363            Vertex {
364                position: [half, y, -half],
365                color: [1.0, 1.0, 1.0],
366                normal: [0.0, 1.0, 0.0],
367                tex_coords: [size, 0.0],
368                joint_indices: def_j,
369                joint_weights: def_w, ..Default::default()
370            },
371            Vertex {
372                position: [half, y, half],
373                color: [1.0, 1.0, 1.0],
374                normal: [0.0, 1.0, 0.0],
375                tex_coords: [size, size],
376                joint_indices: def_j,
377                joint_weights: def_w, ..Default::default()
378            },
379            // İkinci Üçgen (CW from above)
380            Vertex {
381                position: [-half, y, -half],
382                color: [1.0, 1.0, 1.0],
383                normal: [0.0, 1.0, 0.0],
384                tex_coords: [0.0, 0.0],
385                joint_indices: def_j,
386                joint_weights: def_w, ..Default::default()
387            },
388            Vertex {
389                position: [half, y, half],
390                color: [1.0, 1.0, 1.0],
391                normal: [0.0, 1.0, 0.0],
392                tex_coords: [size, size],
393                joint_indices: def_j,
394                joint_weights: def_w, ..Default::default()
395            },
396            Vertex {
397                position: [-half, y, half],
398                color: [1.0, 1.0, 1.0],
399                normal: [0.0, 1.0, 0.0],
400                tex_coords: [0.0, size],
401                joint_indices: def_j,
402                joint_weights: def_w, ..Default::default()
403            },
404        ];
405
406        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
407            label: Some("Plane VBuf"),
408            contents: bytemuck::cast_slice(&vertices),
409            usage: wgpu::BufferUsages::VERTEX,
410        });
411
412        Mesh::new(
413            device,
414            Arc::new(vbuf),
415            &vertices,
416            Vec3::ZERO,
417            format!("plane_{}", size),
418        )
419    }
420
421    /// Yuvarlak bir disk (Çember tabanı) üretir. Bevy'nin Circle::new(radius) karşılığıdır.
422    pub fn create_circle(device: &wgpu::Device, radius: f32, segments: u32) -> Mesh {
423        let segments = segments.max(3);
424        let mut vertices = Vec::with_capacity((segments * 3) as usize);
425
426        let center = [0.0, 0.0, 0.0];
427        let normal = [0.0, 1.0, 0.0];
428        let def_j = [0; 4];
429        let def_w = [0.0; 4];
430
431        for i in 0..segments {
432            let angle1 = (i as f32 / segments as f32) * std::f32::consts::PI * 2.0;
433            let angle2 = ((i + 1) as f32 / segments as f32) * std::f32::consts::PI * 2.0;
434
435            let p1 = [radius * angle1.cos(), 0.0, radius * angle1.sin()];
436            let p2 = [radius * angle2.cos(), 0.0, radius * angle2.sin()];
437
438            let uv_center = [0.5, 0.5];
439            let uv1 = [0.5 + 0.5 * angle1.cos(), 0.5 + 0.5 * angle1.sin()];
440            let uv2 = [0.5 + 0.5 * angle2.cos(), 0.5 + 0.5 * angle2.sin()];
441
442            // CW sarmalı (Center -> P1 -> P2)
443            vertices.push(Vertex {
444                position: center,
445                color: [1.0, 1.0, 1.0],
446                normal,
447                tex_coords: uv_center,
448                joint_indices: def_j,
449                joint_weights: def_w, ..Default::default()
450            });
451            vertices.push(Vertex {
452                position: p1,
453                color: [1.0, 1.0, 1.0],
454                normal,
455                tex_coords: uv1,
456                joint_indices: def_j,
457                joint_weights: def_w, ..Default::default()
458            });
459            vertices.push(Vertex {
460                position: p2,
461                color: [1.0, 1.0, 1.0],
462                normal,
463                tex_coords: uv2,
464                joint_indices: def_j,
465                joint_weights: def_w, ..Default::default()
466            });
467        }
468
469        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
470            label: Some("Circle VBuf"),
471            contents: bytemuck::cast_slice(&vertices),
472            usage: wgpu::BufferUsages::VERTEX,
473        });
474
475        Mesh::new(
476            device,
477            Arc::new(vbuf),
478            &vertices,
479            Vec3::ZERO,
480            format!("circle_{}_{}", radius, segments),
481        )
482    }
483
484    /// Editör sahneleri için GPU'da çizilen sonsuz grid mesh (tek bir quad). Shader içinde matematiksel olarak çizilir.
485    pub fn create_editor_grid_mesh(device: &wgpu::Device, extents: f32) -> Mesh {
486        let mut vertices = Vec::new();
487        // Zemin boyunca devasa bir XY (veya XZ düzleminde) quad oluştur.
488        let scale = extents;
489        let v = [
490            [-scale, 0.0, -scale],
491            [scale, 0.0, -scale],
492            [scale, 0.0, scale],
493            [-scale, 0.0, scale],
494        ];
495
496        let indices = [0, 2, 1, 0, 3, 2];
497        for i in indices {
498            vertices.push(Vertex {
499                position: v[i],
500                color: [1.0, 1.0, 1.0],
501                normal: [0.0, 1.0, 0.0],
502                tex_coords: [0.0, 0.0],
503                joint_indices: [0; 4],
504                joint_weights: [0.0; 4], ..Default::default()
505            });
506        }
507
508        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
509            label: Some("Editor Infinite Grid VBuf"),
510            contents: bytemuck::cast_slice(&vertices),
511            usage: wgpu::BufferUsages::VERTEX,
512        });
513
514        Mesh::new(
515            device,
516            Arc::new(vbuf),
517            &vertices,
518            Vec3::ZERO,
519            "editor_grid".to_string(),
520        )
521    }
522
523    /// 2D Sprite dörtgeni oluşturur (XY düzleminde, kameraya paralel).
524    /// Ortografik projeksiyon ile kullanıldığında 2D oyun desteği sağlar.
525    pub fn create_sprite_quad(device: &wgpu::Device, width: f32, height: f32) -> Mesh {
526        let hw = width / 2.0;
527        let hh = height / 2.0;
528        let def_j = [0; 4];
529        let def_w = [0.0; 4];
530
531        // XY düzleminde dörtgen (Z=0), kameraya bakan yön +Z
532        let vertices = [
533            Vertex {
534                position: [-hw, -hh, 0.0],
535                color: [1.0, 1.0, 1.0],
536                normal: [0.0, 0.0, 1.0],
537                tex_coords: [0.0, 1.0],
538                joint_indices: def_j,
539                joint_weights: def_w, ..Default::default()
540            },
541            Vertex {
542                position: [hw, -hh, 0.0],
543                color: [1.0, 1.0, 1.0],
544                normal: [0.0, 0.0, 1.0],
545                tex_coords: [1.0, 1.0],
546                joint_indices: def_j,
547                joint_weights: def_w, ..Default::default()
548            },
549            Vertex {
550                position: [hw, hh, 0.0],
551                color: [1.0, 1.0, 1.0],
552                normal: [0.0, 0.0, 1.0],
553                tex_coords: [1.0, 0.0],
554                joint_indices: def_j,
555                joint_weights: def_w, ..Default::default()
556            },
557            Vertex {
558                position: [hw, hh, 0.0],
559                color: [1.0, 1.0, 1.0],
560                normal: [0.0, 0.0, 1.0],
561                tex_coords: [1.0, 0.0],
562                joint_indices: def_j,
563                joint_weights: def_w, ..Default::default()
564            },
565            Vertex {
566                position: [-hw, hh, 0.0],
567                color: [1.0, 1.0, 1.0],
568                normal: [0.0, 0.0, 1.0],
569                tex_coords: [0.0, 0.0],
570                joint_indices: def_j,
571                joint_weights: def_w, ..Default::default()
572            },
573            Vertex {
574                position: [-hw, -hh, 0.0],
575                color: [1.0, 1.0, 1.0],
576                normal: [0.0, 0.0, 1.0],
577                tex_coords: [0.0, 1.0],
578                joint_indices: def_j,
579                joint_weights: def_w, ..Default::default()
580            },
581        ];
582
583        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
584            label: Some("Sprite Quad VBuf"),
585            contents: bytemuck::cast_slice(&vertices),
586            usage: wgpu::BufferUsages::VERTEX,
587        });
588
589        Mesh::new(
590            device,
591            Arc::new(vbuf),
592            &vertices,
593            Vec3::ZERO,
594            "sprite_quad".to_string(),
595        )
596    }
597
598    /// Programatik UV Küre (Sphere) üretir.
599    pub fn create_sphere(device: &wgpu::Device, radius: f32, stacks: u32, slices: u32) -> Mesh {
600        let stacks = stacks.max(3);
601        let slices = slices.max(3);
602        let mut vertices = Vec::new();
603        let pi = std::f32::consts::PI;
604
605        for i in 0..stacks {
606            let theta1 = (i as f32 / stacks as f32) * pi;
607            let theta2 = ((i + 1) as f32 / stacks as f32) * pi;
608
609            for j in 0..slices {
610                let phi1 = (j as f32 / slices as f32) * 2.0 * pi;
611                let phi2 = ((j + 1) as f32 / slices as f32) * 2.0 * pi;
612
613                // 4 köşe noktası
614                let p1 = [
615                    radius * theta1.sin() * phi1.cos(),
616                    radius * theta1.cos(),
617                    radius * theta1.sin() * phi1.sin(),
618                ];
619                let p2 = [
620                    radius * theta2.sin() * phi1.cos(),
621                    radius * theta2.cos(),
622                    radius * theta2.sin() * phi1.sin(),
623                ];
624                let p3 = [
625                    radius * theta2.sin() * phi2.cos(),
626                    radius * theta2.cos(),
627                    radius * theta2.sin() * phi2.sin(),
628                ];
629                let p4 = [
630                    radius * theta1.sin() * phi2.cos(),
631                    radius * theta1.cos(),
632                    radius * theta1.sin() * phi2.sin(),
633                ];
634
635                let n1 = [
636                    theta1.sin() * phi1.cos(),
637                    theta1.cos(),
638                    theta1.sin() * phi1.sin(),
639                ];
640                let n2 = [
641                    theta2.sin() * phi1.cos(),
642                    theta2.cos(),
643                    theta2.sin() * phi1.sin(),
644                ];
645                let n3 = [
646                    theta2.sin() * phi2.cos(),
647                    theta2.cos(),
648                    theta2.sin() * phi2.sin(),
649                ];
650                let n4 = [
651                    theta1.sin() * phi2.cos(),
652                    theta1.cos(),
653                    theta1.sin() * phi2.sin(),
654                ];
655
656                let uv1 = [
657                    if i == 0 {
658                        (j as f32 + 0.5) / slices as f32
659                    } else {
660                        j as f32 / slices as f32
661                    },
662                    i as f32 / stacks as f32,
663                ];
664                let uv2 = [
665                    if i + 1 == stacks {
666                        (j as f32 + 0.5) / slices as f32
667                    } else {
668                        j as f32 / slices as f32
669                    },
670                    (i + 1) as f32 / stacks as f32,
671                ];
672                let uv3 = [
673                    if i + 1 == stacks {
674                        (j as f32 + 0.5) / slices as f32
675                    } else {
676                        (j + 1) as f32 / slices as f32
677                    },
678                    (i + 1) as f32 / stacks as f32,
679                ];
680                let uv4 = [
681                    if i == 0 {
682                        (j as f32 + 0.5) / slices as f32
683                    } else {
684                        (j + 1) as f32 / slices as f32
685                    },
686                    i as f32 / stacks as f32,
687                ];
688
689                let def_j = [0; 4];
690                let def_w = [0.0; 4];
691
692                // Üçgen 1 (CCW)
693                vertices.push(Vertex {
694                    position: p1,
695                    color: [1.0; 3],
696                    normal: n1,
697                    tex_coords: uv1,
698                    joint_indices: def_j,
699                    joint_weights: def_w, ..Default::default()
700                });
701                vertices.push(Vertex {
702                    position: p2,
703                    color: [1.0; 3],
704                    normal: n2,
705                    tex_coords: uv2,
706                    joint_indices: def_j,
707                    joint_weights: def_w, ..Default::default()
708                });
709                vertices.push(Vertex {
710                    position: p3,
711                    color: [1.0; 3],
712                    normal: n3,
713                    tex_coords: uv3,
714                    joint_indices: def_j,
715                    joint_weights: def_w, ..Default::default()
716                });
717                // Üçgen 2 (CCW)
718                vertices.push(Vertex {
719                    position: p1,
720                    color: [1.0; 3],
721                    normal: n1,
722                    tex_coords: uv1,
723                    joint_indices: def_j,
724                    joint_weights: def_w, ..Default::default()
725                });
726                vertices.push(Vertex {
727                    position: p3,
728                    color: [1.0; 3],
729                    normal: n3,
730                    tex_coords: uv3,
731                    joint_indices: def_j,
732                    joint_weights: def_w, ..Default::default()
733                });
734                vertices.push(Vertex {
735                    position: p4,
736                    color: [1.0; 3],
737                    normal: n4,
738                    tex_coords: uv4,
739                    joint_indices: def_j,
740                    joint_weights: def_w, ..Default::default()
741                });
742            }
743        }
744
745        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
746            label: Some("Sphere VBuf"),
747            contents: bytemuck::cast_slice(&vertices),
748            usage: wgpu::BufferUsages::VERTEX,
749        });
750
751        Mesh::new(
752            device,
753            Arc::new(vbuf),
754            &vertices,
755            Vec3::ZERO,
756            format!("sphere_{}_{}_{}", radius, stacks, slices),
757        )
758    }
759
760    pub fn create_cylinder(device: &wgpu::Device, radius: f32, height: f32, radial_segments: u32) -> Mesh {
761        let radial_segments = radial_segments.max(3);
762        let mut vertices = Vec::new();
763        let pi = std::f32::consts::PI;
764        let half_h = height / 2.0;
765
766        // Tube
767        for i in 0..radial_segments {
768            let t1 = (i as f32 / radial_segments as f32) * 2.0 * pi;
769            let t2 = ((i + 1) as f32 / radial_segments as f32) * 2.0 * pi;
770
771            let u1 = i as f32 / radial_segments as f32;
772            let u2 = (i + 1) as f32 / radial_segments as f32;
773
774            let p1_top = [radius * t1.cos(), half_h, radius * t1.sin()];
775            let p1_bot = [radius * t1.cos(), -half_h, radius * t1.sin()];
776            let p2_top = [radius * t2.cos(), half_h, radius * t2.sin()];
777            let p2_bot = [radius * t2.cos(), -half_h, radius * t2.sin()];
778
779            let n1 = [t1.cos(), 0.0, t1.sin()];
780            let n2 = [t2.cos(), 0.0, t2.sin()];
781
782            let def_j = [0; 4]; let def_w = [0.0; 4];
783            let col = [1.0; 3];
784
785            // Tri 1 (CCW)
786            vertices.push(Vertex { position: p1_top, normal: n1, tex_coords: [u1, 0.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
787            vertices.push(Vertex { position: p1_bot, normal: n1, tex_coords: [u1, 1.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
788            vertices.push(Vertex { position: p2_bot, normal: n2, tex_coords: [u2, 1.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
789
790            // Tri 2 (CCW)
791            vertices.push(Vertex { position: p1_top, normal: n1, tex_coords: [u1, 0.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
792            vertices.push(Vertex { position: p2_bot, normal: n2, tex_coords: [u2, 1.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
793            vertices.push(Vertex { position: p2_top, normal: n2, tex_coords: [u2, 0.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
794
795            // Top Cap (CCW from above)
796            vertices.push(Vertex { position: [0.0, half_h, 0.0], normal: [0.0, 1.0, 0.0], tex_coords: [0.5, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
797            vertices.push(Vertex { position: p1_top, normal: [0.0, 1.0, 0.0], tex_coords: [0.5 + 0.5 * t1.cos(), 0.5 + 0.5 * t1.sin()], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
798            vertices.push(Vertex { position: p2_top, normal: [0.0, 1.0, 0.0], tex_coords: [0.5 + 0.5 * t2.cos(), 0.5 + 0.5 * t2.sin()], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
799
800            // Bottom Cap (CCW from below)
801            vertices.push(Vertex { position: [0.0, -half_h, 0.0], normal: [0.0, -1.0, 0.0], tex_coords: [0.5, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
802            vertices.push(Vertex { position: p2_bot, normal: [0.0, -1.0, 0.0], tex_coords: [0.5 + 0.5 * t2.cos(), 0.5 + 0.5 * t2.sin()], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
803            vertices.push(Vertex { position: p1_bot, normal: [0.0, -1.0, 0.0], tex_coords: [0.5 + 0.5 * t1.cos(), 0.5 + 0.5 * t1.sin()], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
804        }
805
806        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Cylinder VBuf"), contents: bytemuck::cast_slice(&vertices), usage: wgpu::BufferUsages::VERTEX });
807        Mesh::new(device, Arc::new(vbuf), &vertices, Vec3::ZERO, format!("cylinder_{}_{}", radius, height))
808    }
809
810    pub fn create_cone(device: &wgpu::Device, radius: f32, height: f32, radial_segments: u32) -> Mesh {
811        let radial_segments = radial_segments.max(3);
812        let mut vertices = Vec::new();
813        let pi = std::f32::consts::PI;
814        let half_h = height / 2.0;
815
816        let slant = (radius * radius + height * height).sqrt();
817        let ny = radius / slant;
818        let n_xz = height / slant;
819
820        for i in 0..radial_segments {
821            let t1 = (i as f32 / radial_segments as f32) * 2.0 * pi;
822            let t2 = ((i + 1) as f32 / radial_segments as f32) * 2.0 * pi;
823
824            let p1_bot = [radius * t1.cos(), -half_h, radius * t1.sin()];
825            let p2_bot = [radius * t2.cos(), -half_h, radius * t2.sin()];
826            let apex = [0.0, half_h, 0.0];
827
828            let n1 = [n_xz * t1.cos(), ny, n_xz * t1.sin()];
829            let n2 = [n_xz * t2.cos(), ny, n_xz * t2.sin()];
830            let navg = [n_xz * ((t1+t2)/2.0).cos(), ny, n_xz * ((t1+t2)/2.0).sin()];
831
832            let u1 = i as f32 / radial_segments as f32;
833            let u2 = (i + 1) as f32 / radial_segments as f32;
834            let umid = (u1 + u2) / 2.0;
835
836            let def_j = [0; 4]; let def_w = [0.0; 4];
837            let col = [1.0; 3];
838
839            // Side Tri (CCW from outside)
840            vertices.push(Vertex { position: apex, normal: navg, tex_coords: [umid, 0.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
841            vertices.push(Vertex { position: p1_bot, normal: n1, tex_coords: [u1, 1.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
842            vertices.push(Vertex { position: p2_bot, normal: n2, tex_coords: [u2, 1.0], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
843
844            // Bottom Cap (CCW from below)
845            vertices.push(Vertex { position: [0.0, -half_h, 0.0], normal: [0.0, -1.0, 0.0], tex_coords: [0.5, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
846            vertices.push(Vertex { position: p2_bot, normal: [0.0, -1.0, 0.0], tex_coords: [0.5 + 0.5 * t2.cos(), 0.5 + 0.5 * t2.sin()], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
847            vertices.push(Vertex { position: p1_bot, normal: [0.0, -1.0, 0.0], tex_coords: [0.5 + 0.5 * t1.cos(), 0.5 + 0.5 * t1.sin()], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
848        }
849
850        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Cone VBuf"), contents: bytemuck::cast_slice(&vertices), usage: wgpu::BufferUsages::VERTEX });
851        Mesh::new(device, Arc::new(vbuf), &vertices, Vec3::ZERO, format!("cone_{}_{}", radius, height))
852    }
853
854    pub fn create_torus(device: &wgpu::Device, radius: f32, tube_radius: f32, radial_segments: u32, tubular_segments: u32) -> Mesh {
855        let radial_segments = radial_segments.max(3);
856        let tubular_segments = tubular_segments.max(3);
857        let mut vertices = Vec::new();
858        let pi = std::f32::consts::PI;
859
860        for i in 0..radial_segments {
861            for j in 0..tubular_segments {
862                let u1 = i as f32 / radial_segments as f32;
863                let u2 = (i + 1) as f32 / radial_segments as f32;
864                let v1 = j as f32 / tubular_segments as f32;
865                let v2 = (j + 1) as f32 / tubular_segments as f32;
866
867                let t1 = u1 * 2.0 * pi;
868                let t2 = u2 * 2.0 * pi;
869                let p1 = v1 * 2.0 * pi;
870                let p2 = v2 * 2.0 * pi;
871
872                let pos = |t: f32, p: f32| {
873                    [(radius + tube_radius * p.cos()) * t.cos(), tube_radius * p.sin(), (radius + tube_radius * p.cos()) * t.sin()]
874                };
875                let norm = |t: f32, p: f32| {
876                    [p.cos() * t.cos(), p.sin(), p.cos() * t.sin()]
877                };
878
879                let p_00 = pos(t1, p1); let n_00 = norm(t1, p1);
880                let p_10 = pos(t2, p1); let n_10 = norm(t2, p1);
881                let p_01 = pos(t1, p2); let n_01 = norm(t1, p2);
882                let p_11 = pos(t2, p2); let n_11 = norm(t2, p2);
883
884                let def_j = [0; 4]; let def_w = [0.0; 4];
885                let col = [1.0; 3];
886
887                vertices.push(Vertex { position: p_00, normal: n_00, tex_coords: [u1, v1], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
888                vertices.push(Vertex { position: p_01, normal: n_01, tex_coords: [u1, v2], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
889                vertices.push(Vertex { position: p_10, normal: n_10, tex_coords: [u2, v1], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
890
891                vertices.push(Vertex { position: p_10, normal: n_10, tex_coords: [u2, v1], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
892                vertices.push(Vertex { position: p_01, normal: n_01, tex_coords: [u1, v2], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
893                vertices.push(Vertex { position: p_11, normal: n_11, tex_coords: [u2, v2], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
894            }
895        }
896
897        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Torus VBuf"), contents: bytemuck::cast_slice(&vertices), usage: wgpu::BufferUsages::VERTEX });
898        Mesh::new(device, Arc::new(vbuf), &vertices, Vec3::ZERO, format!("torus_{}_{}", radius, tube_radius))
899    }
900
901    pub fn create_capsule(device: &wgpu::Device, radius: f32, depth: f32, latitudes: u32, longitudes: u32) -> Mesh {
902        let latitudes = latitudes.max(4);
903        let longitudes = longitudes.max(4);
904        let mut vertices = Vec::new();
905        let pi = std::f32::consts::PI;
906        let half_d = depth / 2.0;
907
908        for i in 0..=latitudes {
909            let u1 = i as f32 / latitudes as f32;
910            let u2 = (i + 1) as f32 / latitudes as f32;
911            let theta1 = u1 * pi;
912            let theta2 = u2 * pi;
913
914            let y_offset1 = if u1 < 0.5 { half_d } else if u1 > 0.5 { -half_d } else { 0.0 };
915            let y_offset2 = if u2 < 0.5 { half_d } else if u2 > 0.5 { -half_d } else { 0.0 };
916            
917            // To properly insert a tube, we duplicate the equator loop
918            let is_equator = i == latitudes / 2;
919
920            if is_equator {
921                // Tube segment
922                for j in 0..longitudes {
923                    let v1 = j as f32 / longitudes as f32;
924                    let v2 = (j + 1) as f32 / longitudes as f32;
925                    let phi1 = v1 * 2.0 * pi;
926                    let phi2 = v2 * 2.0 * pi;
927
928                    let p1_top = [radius * phi1.cos(), half_d, radius * phi1.sin()];
929                    let p1_bot = [radius * phi1.cos(), -half_d, radius * phi1.sin()];
930                    let p2_top = [radius * phi2.cos(), half_d, radius * phi2.sin()];
931                    let p2_bot = [radius * phi2.cos(), -half_d, radius * phi2.sin()];
932
933                    let n1 = [phi1.cos(), 0.0, phi1.sin()];
934                    let n2 = [phi2.cos(), 0.0, phi2.sin()];
935
936                    let def_j = [0; 4]; let def_w = [0.0; 4]; let col = [1.0; 3];
937
938                    // Tri 1 (CCW)
939                    vertices.push(Vertex { position: p1_top, normal: n1, tex_coords: [v1, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
940                    vertices.push(Vertex { position: p1_bot, normal: n1, tex_coords: [v1, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
941                    vertices.push(Vertex { position: p2_bot, normal: n2, tex_coords: [v2, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
942
943                    // Tri 2 (CCW)
944                    vertices.push(Vertex { position: p1_top, normal: n1, tex_coords: [v1, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
945                    vertices.push(Vertex { position: p2_bot, normal: n2, tex_coords: [v2, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
946                    vertices.push(Vertex { position: p2_top, normal: n2, tex_coords: [v2, 0.5], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
947                }
948            }
949
950            if i < latitudes {
951                for j in 0..longitudes {
952                    let v1 = j as f32 / longitudes as f32;
953                    let v2 = (j + 1) as f32 / longitudes as f32;
954                    let phi1 = v1 * 2.0 * pi;
955                    let phi2 = v2 * 2.0 * pi;
956
957                    let p1 = [radius * theta1.sin() * phi1.cos(), radius * theta1.cos() + y_offset1, radius * theta1.sin() * phi1.sin()];
958                    let p2 = [radius * theta2.sin() * phi1.cos(), radius * theta2.cos() + y_offset2, radius * theta2.sin() * phi1.sin()];
959                    let p3 = [radius * theta2.sin() * phi2.cos(), radius * theta2.cos() + y_offset2, radius * theta2.sin() * phi2.sin()];
960                    let p4 = [radius * theta1.sin() * phi2.cos(), radius * theta1.cos() + y_offset1, radius * theta1.sin() * phi2.sin()];
961
962                    let n1 = [theta1.sin() * phi1.cos(), theta1.cos(), theta1.sin() * phi1.sin()];
963                    let n2 = [theta2.sin() * phi1.cos(), theta2.cos(), theta2.sin() * phi1.sin()];
964                    let n3 = [theta2.sin() * phi2.cos(), theta2.cos(), theta2.sin() * phi2.sin()];
965                    let n4 = [theta1.sin() * phi2.cos(), theta1.cos(), theta1.sin() * phi2.sin()];
966
967                    let def_j = [0; 4]; let def_w = [0.0; 4]; let col = [1.0; 3];
968
969                    // Tri 1 (CCW)
970                    vertices.push(Vertex { position: p1, normal: n1, tex_coords: [v1, u1], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
971                    vertices.push(Vertex { position: p2, normal: n2, tex_coords: [v1, u2], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
972                    vertices.push(Vertex { position: p3, normal: n3, tex_coords: [v2, u2], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
973
974                    // Tri 2 (CCW)
975                    vertices.push(Vertex { position: p1, normal: n1, tex_coords: [v1, u1], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
976                    vertices.push(Vertex { position: p3, normal: n3, tex_coords: [v2, u2], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
977                    vertices.push(Vertex { position: p4, normal: n4, tex_coords: [v2, u1], color: col, joint_indices: def_j, joint_weights: def_w, ..Default::default() });
978                }
979            }
980        }
981
982        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Capsule VBuf"), contents: bytemuck::cast_slice(&vertices), usage: wgpu::BufferUsages::VERTEX });
983        Mesh::new(device, Arc::new(vbuf), &vertices, Vec3::ZERO, format!("capsule_{}_{}", radius, depth))
984    }
985
986
987    pub fn create_terrain(
988        device: &wgpu::Device,
989        heightmap_path: &str,
990        width: f32,
991        depth: f32,
992        max_height: f32,
993    ) -> Result<(Mesh, Vec<f32>, u32, u32), String> {
994        let canonical = std::path::Path::new(heightmap_path)
995            .canonicalize()
996            .map(|p| p.to_string_lossy().to_string())
997            .unwrap_or_else(|_| heightmap_path.to_string());
998
999        let img = image::open(&canonical)
1000            .map_err(|e| format!("Heightmap yuklenemedi! {} ({})", canonical, e))?
1001            .into_luma8(); // Grayscale format
1002
1003        let (img_width, img_height) = img.dimensions();
1004        if img_width < 2 || img_height < 2 {
1005            return Err(
1006                "Heightmap boyutlari en az 2x2 olmalidir. 1x1 piksel ile arazi olusturulamaz."
1007                    .to_string(),
1008            );
1009        }
1010        // Sınırlama: 512x512'den büyükse performans için uyar ya da downscale et
1011
1012        let mut vertices: Vec<Vertex> = Vec::with_capacity((img_width * img_height) as usize);
1013        let mut heights: Vec<f32> = Vec::with_capacity((img_width * img_height) as usize);
1014
1015        let half_w = width / 2.0;
1016        let half_d = depth / 2.0;
1017
1018        // 1. GRID VERTEX'LERİ ÜRET
1019        for y in 0..img_height {
1020            for x in 0..img_width {
1021                let pixel = img.get_pixel(x, y)[0] as f32 / 255.0; // 0.0 - 1.0
1022                heights.push(pixel);
1023                let world_y = pixel * max_height;
1024
1025                let world_x = -half_w + (x as f32 / (img_width as f32 - 1.0)) * width;
1026                let world_z = -half_d + (y as f32 / (img_height as f32 - 1.0)) * depth;
1027
1028                // UV Mapping: Repeat 10 times across terrain so grass doesn't look stretched
1029                let uv_x = (x as f32 / (img_width as f32 - 1.0)) * 10.0;
1030                let uv_y = (y as f32 / (img_height as f32 - 1.0)) * 10.0;
1031
1032                vertices.push(Vertex {
1033                    position: [world_x, world_y, world_z],
1034                    color: [1.0, 1.0, 1.0],
1035                    normal: [0.0, 1.0, 0.0], // İlk başta düz yukarı
1036                    tex_coords: [uv_x, uv_y],
1037                    joint_indices: [0; 4],
1038                    joint_weights: [0.0; 4], ..Default::default()
1039                });
1040            }
1041        }
1042
1043        // 2. INDEX'LERİ OLUŞTUR VE NORMALLERİ HESAPLA
1044        let mut indices = Vec::with_capacity(((img_width - 1) * (img_height - 1) * 6) as usize);
1045        for y in 0..(img_height - 1) {
1046            for x in 0..(img_width - 1) {
1047                let i0 = y * img_width + x;
1048                let i1 = y * img_width + (x + 1);
1049                let i2 = (y + 1) * img_width + x;
1050                let i3 = (y + 1) * img_width + (x + 1);
1051
1052                // Triangle 1
1053                indices.push(i0);
1054                indices.push(i2);
1055                indices.push(i1);
1056
1057                // Triangle 2
1058                indices.push(i1);
1059                indices.push(i2);
1060                indices.push(i3);
1061            }
1062        }
1063
1064        // Face ve Smooth Normalleri hesapla
1065        let mut final_vertices = Vec::with_capacity(indices.len());
1066        for chunk in indices.chunks(3) {
1067            let i0 = chunk[0] as usize;
1068            let i1 = chunk[1] as usize;
1069            let i2 = chunk[2] as usize;
1070
1071            let p0 = Vec3::from_array(vertices[i0].position);
1072            let p1 = Vec3::from_array(vertices[i1].position);
1073            let p2 = Vec3::from_array(vertices[i2].position);
1074
1075            let norm = (p1 - p0).cross(p2 - p0);
1076            let normal = if norm.length_squared() > 1e-6 {
1077                norm.normalize()
1078            } else {
1079                Vec3::new(0.0, 1.0, 0.0)
1080            };
1081
1082            // Triangle count for WGPU. Note: using flat normal per face first, optionally can be smoothed
1083            let mut v0 = vertices[i0];
1084            v0.normal = [normal.x, normal.y, normal.z];
1085            let mut v1 = vertices[i1];
1086            v1.normal = [normal.x, normal.y, normal.z];
1087            let mut v2 = vertices[i2];
1088            v2.normal = [normal.x, normal.y, normal.z];
1089
1090            final_vertices.push(v0);
1091            final_vertices.push(v1);
1092            final_vertices.push(v2);
1093        }
1094
1095        let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
1096            label: Some(&format!("Terrain ({})", heightmap_path)),
1097            contents: bytemuck::cast_slice(&final_vertices),
1098            usage: wgpu::BufferUsages::VERTEX,
1099        });
1100
1101        let mesh = Mesh::new(
1102            device,
1103            Arc::new(vbuf),
1104            &final_vertices,
1105            Vec3::ZERO,
1106            format!("terrain:{}", heightmap_path),
1107        );
1108        Ok((mesh, heights, img_width, img_height))
1109    }
1110}