1use crate::libs::graphics::backend::{
14 BlendFactor, CullFace, DepthFunc, FrontFace, PrimitiveTopology, RenderBackend, ShaderHandle,
15 VertexAttribute, VertexAttributeType, VertexLayout,
16};
17use cgmath::{perspective, Deg, Matrix, Matrix4, Rad, Vector3, Vector4};
18use std::collections::HashMap;
19
20use super::backend::BufferHandle;
21
22pub const MAX_LIGHTS: usize = 8;
28
29#[repr(C)]
35#[derive(Debug, Clone, Copy, PartialEq)]
36#[allow(missing_docs)]
37pub enum PrimitiveType {
38 Cube = 0,
40 Sphere = 1,
42 Plane = 2,
44 Cylinder = 3,
46}
47
48#[repr(C)]
50#[derive(Debug, Clone, Copy, PartialEq)]
51#[allow(missing_docs)]
52pub enum LightType {
53 Point = 0,
55 Directional = 1,
57 Spot = 2,
59}
60
61#[repr(C)]
63#[derive(Debug, Clone, Copy, PartialEq)]
64#[allow(missing_docs)]
65pub enum GridRenderMode {
66 Blend = 0,
68 Overlap = 1,
70}
71
72#[repr(C)]
74#[derive(Debug, Clone)]
75#[allow(missing_docs)]
76pub struct PrimitiveCreateInfo {
77 pub primitive_type: PrimitiveType,
79 pub width: f32,
81 pub height: f32,
83 pub depth: f32,
85 pub segments: u32,
87 pub texture_id: u32,
89}
90
91#[derive(Debug)]
93struct Object3D {
94 buffer: BufferHandle,
95 vertex_count: i32,
96 position: Vector3<f32>,
97 rotation: Vector3<f32>,
98 scale: Vector3<f32>,
99 texture_id: u32,
100}
101
102#[derive(Debug, Clone)]
104#[allow(missing_docs)]
105pub struct Light {
106 pub light_type: LightType,
108 pub position: Vector3<f32>,
110 pub direction: Vector3<f32>,
112 pub color: Vector3<f32>,
114 pub intensity: f32,
116 pub range: f32,
118 pub spot_angle: f32,
120 pub enabled: bool,
122}
123
124impl Default for Light {
125 fn default() -> Self {
126 Self {
127 light_type: LightType::Point,
128 position: Vector3::new(0.0, 5.0, 0.0),
129 direction: Vector3::new(0.0, -1.0, 0.0),
130 color: Vector3::new(1.0, 1.0, 1.0),
131 intensity: 1.0,
132 range: 10.0,
133 spot_angle: 45.0,
134 enabled: true,
135 }
136 }
137}
138
139#[derive(Debug, Clone)]
141#[allow(missing_docs)]
142pub struct GridConfig {
143 pub enabled: bool,
145 pub size: f32,
147 pub divisions: u32,
149 pub show_xz_plane: bool,
151 pub show_xy_plane: bool,
153 pub show_yz_plane: bool,
155 pub render_mode: GridRenderMode,
157 pub line_color: Vector3<f32>,
159}
160
161impl Default for GridConfig {
162 fn default() -> Self {
163 Self {
164 enabled: true,
165 size: 20.0,
166 divisions: 20,
167 show_xz_plane: true,
168 show_xy_plane: false,
169 show_yz_plane: false,
170 render_mode: GridRenderMode::Blend,
171 line_color: Vector3::new(0.5, 0.5, 0.5),
172 }
173 }
174}
175
176#[derive(Debug, Clone)]
178#[allow(missing_docs)]
179pub struct SkyboxConfig {
180 pub enabled: bool,
182 pub color: Vector4<f32>,
184}
185
186impl Default for SkyboxConfig {
187 fn default() -> Self {
188 Self {
189 enabled: false,
190 color: Vector4::new(0.1, 0.1, 0.2, 1.0),
191 }
192 }
193}
194
195#[derive(Debug, Clone)]
197#[allow(missing_docs)]
198pub struct FogConfig {
199 pub enabled: bool,
201 pub color: Vector3<f32>,
203 pub density: f32,
205}
206
207impl Default for FogConfig {
208 fn default() -> Self {
209 Self {
210 enabled: true,
211 color: Vector3::new(0.05, 0.05, 0.1),
212 density: 0.02,
213 }
214 }
215}
216
217#[derive(Debug, Clone)]
219#[allow(missing_docs)]
220pub struct Camera3D {
221 pub position: Vector3<f32>,
223 pub rotation: Vector3<f32>,
225}
226
227impl Default for Camera3D {
228 fn default() -> Self {
229 Self {
230 position: Vector3::new(0.0, 10.0, -20.0),
231 rotation: Vector3::new(-30.0, 0.0, 0.0),
232 }
233 }
234}
235
236impl Camera3D {
237 pub fn view_matrix(&self) -> Matrix4<f32> {
239 let pitch = Rad::from(Deg(self.rotation.x));
240 let yaw = Rad::from(Deg(self.rotation.y));
241
242 let cos_pitch = pitch.0.cos();
243 let sin_pitch = pitch.0.sin();
244 let cos_yaw = yaw.0.cos();
245 let sin_yaw = yaw.0.sin();
246
247 let forward = Vector3::new(sin_yaw * cos_pitch, sin_pitch, cos_yaw * cos_pitch);
248
249 let target = self.position + forward;
250 let up = Vector3::new(0.0, 1.0, 0.0);
251
252 Matrix4::look_at_rh(
253 cgmath::Point3::new(self.position.x, self.position.y, self.position.z),
254 cgmath::Point3::new(target.x, target.y, target.z),
255 up,
256 )
257 }
258}
259
260const VERTEX_SHADER_3D: &str = r#"
265#version 330 core
266
267layout (location = 0) in vec3 aPos;
268layout (location = 1) in vec3 aNormal;
269layout (location = 2) in vec2 aTexCoord;
270
271out vec3 FragPos;
272out vec3 Normal;
273out vec2 TexCoord;
274
275uniform mat4 model;
276uniform mat4 view;
277uniform mat4 projection;
278
279void main()
280{
281 FragPos = vec3(model * vec4(aPos, 1.0));
282 Normal = mat3(transpose(inverse(model))) * aNormal;
283 TexCoord = aTexCoord;
284
285 gl_Position = projection * view * vec4(FragPos, 1.0);
286}
287"#;
288
289const FRAGMENT_SHADER_3D: &str = r#"
290#version 330 core
291
292in vec3 FragPos;
293in vec3 Normal;
294in vec2 TexCoord;
295
296out vec4 FragColor;
297
298struct Light {
299 int type; // 0 = point, 1 = directional, 2 = spot
300 vec3 position;
301 vec3 direction;
302 vec3 color;
303 float intensity;
304 float range;
305 float spotAngle;
306 bool enabled;
307};
308
309uniform sampler2D texture1;
310uniform vec3 viewPos;
311uniform int numLights;
312uniform Light lights[8];
313uniform bool useTexture;
314uniform vec4 objectColor;
315
316vec3 calculatePointLight(Light light, vec3 normal, vec3 fragPos, vec3 viewDir) {
317 vec3 lightDir = normalize(light.position - fragPos);
318 float distance = length(light.position - fragPos);
319 float attenuation = 1.0 / (1.0 + 0.09 * distance + 0.032 * distance * distance);
320
321 float diff = max(dot(normal, lightDir), 0.0);
322 vec3 diffuse = diff * light.color;
323
324 vec3 reflectDir = reflect(-lightDir, normal);
325 float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
326 vec3 specular = spec * light.color * 0.5;
327
328 float rangeAttenuation = 1.0 - smoothstep(0.0, light.range, distance);
329
330 return (diffuse + specular) * attenuation * rangeAttenuation * light.intensity;
331}
332
333vec3 calculateDirectionalLight(Light light, vec3 normal, vec3 viewDir) {
334 vec3 lightDir = normalize(-light.direction);
335
336 float diff = max(dot(normal, lightDir), 0.0);
337 vec3 diffuse = diff * light.color;
338
339 vec3 reflectDir = reflect(-lightDir, normal);
340 float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
341 vec3 specular = spec * light.color * 0.5;
342
343 return (diffuse + specular) * light.intensity;
344}
345
346vec3 calculateSpotLight(Light light, vec3 normal, vec3 fragPos, vec3 viewDir) {
347 vec3 lightDir = normalize(light.position - fragPos);
348 float theta = dot(lightDir, normalize(-light.direction));
349 float epsilon = cos(radians(light.spotAngle)) - cos(radians(light.spotAngle + 5.0));
350 float intensity = clamp((theta - cos(radians(light.spotAngle + 5.0))) / epsilon, 0.0, 1.0);
351
352 if (intensity > 0.0) {
353 return calculatePointLight(light, normal, fragPos, viewDir) * intensity;
354 }
355 return vec3(0.0);
356}
357
358uniform bool fogEnabled;
359uniform vec3 fogColor;
360uniform float fogDensity;
361
362float calculateFog(float distance) {
363 float fog = exp(-pow(fogDensity * distance, 2.0));
364 return clamp(fog, 0.0, 1.0);
365}
366
367void main() {
368 vec3 norm = normalize(Normal);
369 vec3 viewDir = normalize(viewPos - FragPos);
370 vec3 result = vec3(0.0);
371
372 vec3 ambient = vec3(0.1);
373 result += ambient;
374
375 for(int i = 0; i < numLights && i < 8; i++) {
376 if (!lights[i].enabled) continue;
377
378 vec3 lightContribution;
379 if (lights[i].type == 0) {
380 lightContribution = calculatePointLight(lights[i], norm, FragPos, viewDir);
381 }
382 else if (lights[i].type == 1) {
383 lightContribution = calculateDirectionalLight(lights[i], norm, viewDir);
384 }
385 else if (lights[i].type == 2) {
386 lightContribution = calculateSpotLight(lights[i], norm, FragPos, viewDir);
387 }
388
389 result += lightContribution;
390 }
391
392 vec4 baseColor;
393 if (useTexture) {
394 baseColor = texture(texture1, TexCoord);
395 } else {
396 baseColor = objectColor;
397 }
398
399 vec3 finalColor = result * baseColor.rgb;
400
401 if (fogEnabled) {
402 float distance = length(FragPos - viewPos);
403 float visibility = calculateFog(distance);
404 finalColor = mix(fogColor, finalColor, visibility);
405 }
406
407 FragColor = vec4(finalColor, baseColor.a);
408}
409"#;
410
411const GRID_VERTEX_SHADER: &str = r#"
412#version 330 core
413
414layout (location = 0) in vec3 aPos;
415layout (location = 1) in vec3 aColor;
416
417out vec3 vertexColor;
418out vec3 fragPos;
419
420uniform mat4 view;
421uniform mat4 projection;
422
423void main()
424{
425 fragPos = aPos;
426 vertexColor = aColor;
427 gl_Position = projection * view * vec4(aPos, 1.0);
428}
429"#;
430
431const GRID_FRAGMENT_SHADER: &str = r#"
432#version 330 core
433
434in vec3 vertexColor;
435in vec3 fragPos;
436
437out vec4 FragColor;
438
439uniform vec3 viewPos;
440uniform bool fogEnabled;
441uniform vec3 fogColor;
442uniform float fogDensity;
443uniform float alpha;
444
445void main()
446{
447 vec3 color = vertexColor;
448
449 if (fogEnabled) {
450 float distance = length(viewPos - fragPos);
451 float fogFactor = exp(-fogDensity * distance);
452 fogFactor = clamp(fogFactor, 0.0, 1.0);
453 color = mix(fogColor, color, fogFactor);
454 }
455
456 FragColor = vec4(color, alpha);
457}
458"#;
459
460struct LightUniforms {
465 light_type: i32,
466 position: i32,
467 direction: i32,
468 color: i32,
469 intensity: i32,
470 range: i32,
471 spot_angle: i32,
472 enabled: i32,
473}
474
475struct MainUniforms {
476 model: i32,
477 view: i32,
478 projection: i32,
479 view_pos: i32,
480 num_lights: i32,
481 use_texture: i32,
482 object_color: i32,
483 texture1: i32,
484 fog_enabled: i32,
485 fog_color: i32,
486 fog_density: i32,
487 lights: Vec<LightUniforms>,
488}
489
490struct GridUniforms {
491 view: i32,
492 projection: i32,
493 view_pos: i32,
494 fog_enabled: i32,
495 fog_color: i32,
496 fog_density: i32,
497 alpha: i32,
498}
499
500fn resolve_main_uniforms(backend: &dyn RenderBackend, shader: ShaderHandle) -> MainUniforms {
501 let loc = |name: &str| -> i32 { backend.get_uniform_location(shader, name).unwrap_or(-1) };
502
503 let mut lights = Vec::with_capacity(MAX_LIGHTS);
504 for i in 0..MAX_LIGHTS {
505 lights.push(LightUniforms {
506 light_type: loc(&format!("lights[{i}].type")),
507 position: loc(&format!("lights[{i}].position")),
508 direction: loc(&format!("lights[{i}].direction")),
509 color: loc(&format!("lights[{i}].color")),
510 intensity: loc(&format!("lights[{i}].intensity")),
511 range: loc(&format!("lights[{i}].range")),
512 spot_angle: loc(&format!("lights[{i}].spotAngle")),
513 enabled: loc(&format!("lights[{i}].enabled")),
514 });
515 }
516
517 MainUniforms {
518 model: loc("model"),
519 view: loc("view"),
520 projection: loc("projection"),
521 view_pos: loc("viewPos"),
522 num_lights: loc("numLights"),
523 use_texture: loc("useTexture"),
524 object_color: loc("objectColor"),
525 texture1: loc("texture1"),
526 fog_enabled: loc("fogEnabled"),
527 fog_color: loc("fogColor"),
528 fog_density: loc("fogDensity"),
529 lights,
530 }
531}
532
533fn resolve_grid_uniforms(backend: &dyn RenderBackend, shader: ShaderHandle) -> GridUniforms {
534 let loc = |name: &str| -> i32 { backend.get_uniform_location(shader, name).unwrap_or(-1) };
535
536 GridUniforms {
537 view: loc("view"),
538 projection: loc("projection"),
539 view_pos: loc("viewPos"),
540 fog_enabled: loc("fogEnabled"),
541 fog_color: loc("fogColor"),
542 fog_density: loc("fogDensity"),
543 alpha: loc("alpha"),
544 }
545}
546
547fn object_vertex_layout() -> VertexLayout {
552 VertexLayout::new(32)
554 .with_attribute(VertexAttribute::new(
555 0,
556 VertexAttributeType::Float3,
557 0,
558 false,
559 ))
560 .with_attribute(VertexAttribute::new(
561 1,
562 VertexAttributeType::Float3,
563 12,
564 false,
565 ))
566 .with_attribute(VertexAttribute::new(
567 2,
568 VertexAttributeType::Float2,
569 24,
570 false,
571 ))
572}
573
574fn grid_vertex_layout() -> VertexLayout {
575 VertexLayout::new(24)
577 .with_attribute(VertexAttribute::new(
578 0,
579 VertexAttributeType::Float3,
580 0,
581 false,
582 ))
583 .with_attribute(VertexAttribute::new(
584 1,
585 VertexAttributeType::Float3,
586 12,
587 false,
588 ))
589}
590
591pub struct Renderer3D {
600 backend: Box<dyn RenderBackend>,
601 shader_handle: ShaderHandle,
602 grid_shader_handle: ShaderHandle,
603 grid_buffer: BufferHandle,
604 grid_vertex_count: i32,
605 axis_buffer: BufferHandle,
606 axis_vertex_count: i32,
607 objects: HashMap<u32, Object3D>,
608 lights: HashMap<u32, Light>,
609 next_object_id: u32,
610 next_light_id: u32,
611 camera: Camera3D,
612 window_width: u32,
613 window_height: u32,
614 grid_config: GridConfig,
615 skybox_config: SkyboxConfig,
616 fog_config: FogConfig,
617 uniforms: MainUniforms,
618 grid_uniforms: GridUniforms,
619 object_layout: VertexLayout,
620 grid_layout: VertexLayout,
621}
622
623impl Renderer3D {
624 pub fn new(
626 mut backend: Box<dyn RenderBackend>,
627 window_width: u32,
628 window_height: u32,
629 ) -> Result<Self, String> {
630 let shader_handle = backend
631 .create_shader(VERTEX_SHADER_3D, FRAGMENT_SHADER_3D)
632 .map_err(|e| format!("Main 3D shader: {e}"))?;
633 let uniforms = resolve_main_uniforms(backend.as_ref(), shader_handle);
634
635 let grid_shader_handle = backend
636 .create_shader(GRID_VERTEX_SHADER, GRID_FRAGMENT_SHADER)
637 .map_err(|e| format!("Grid shader: {e}"))?;
638 let grid_uniforms = resolve_grid_uniforms(backend.as_ref(), grid_shader_handle);
639
640 let grid_layout = grid_vertex_layout();
641 let (grid_buffer, grid_vertex_count) = Self::create_grid_mesh(backend.as_mut(), 20.0, 20)?;
642 let (axis_buffer, axis_vertex_count) = Self::create_axis_mesh(backend.as_mut(), 5.0)?;
643
644 Ok(Self {
645 backend,
646 shader_handle,
647 grid_shader_handle,
648 grid_buffer,
649 grid_vertex_count,
650 axis_buffer,
651 axis_vertex_count,
652 objects: HashMap::new(),
653 lights: HashMap::new(),
654 next_object_id: 1,
655 next_light_id: 1,
656 camera: Camera3D::default(),
657 window_width,
658 window_height,
659 grid_config: GridConfig::default(),
660 skybox_config: SkyboxConfig::default(),
661 fog_config: FogConfig::default(),
662 uniforms,
663 grid_uniforms,
664 object_layout: object_vertex_layout(),
665 grid_layout,
666 })
667 }
668
669 fn upload_buffer(
674 backend: &mut dyn RenderBackend,
675 vertices: &[f32],
676 ) -> Result<BufferHandle, String> {
677 use crate::libs::graphics::backend::types::{BufferType, BufferUsage};
678 backend
679 .create_buffer(
680 BufferType::Vertex,
681 BufferUsage::Static,
682 bytemuck::cast_slice(vertices),
683 )
684 .map_err(|e| format!("Buffer creation failed: {e}"))
685 }
686
687 fn create_grid_mesh(
688 backend: &mut dyn RenderBackend,
689 size: f32,
690 divisions: u32,
691 ) -> Result<(BufferHandle, i32), String> {
692 let vertices = Self::generate_grid_vertices(size, divisions);
693 let count = (vertices.len() / 6) as i32;
694 let handle = Self::upload_buffer(backend, &vertices)?;
695 Ok((handle, count))
696 }
697
698 fn create_axis_mesh(
699 backend: &mut dyn RenderBackend,
700 size: f32,
701 ) -> Result<(BufferHandle, i32), String> {
702 let vertices = Self::generate_axis_vertices(size);
703 let count = (vertices.len() / 6) as i32;
704 let handle = Self::upload_buffer(backend, &vertices)?;
705 Ok((handle, count))
706 }
707
708 fn generate_grid_vertices(size: f32, divisions: u32) -> Vec<f32> {
713 let mut vertices = Vec::new();
714 let step = size / divisions as f32;
715 let half = size / 2.0;
716
717 let xz_color = [0.3, 0.3, 0.3];
718 let xy_color = [0.2, 0.25, 0.3];
719 let yz_color = [0.3, 0.2, 0.25];
720
721 for i in 0..=divisions {
722 let pos = -half + step * i as f32;
723
724 vertices.extend_from_slice(&[pos, 0.0, -half]);
725 vertices.extend_from_slice(&xz_color);
726 vertices.extend_from_slice(&[pos, 0.0, half]);
727 vertices.extend_from_slice(&xz_color);
728
729 vertices.extend_from_slice(&[-half, 0.0, pos]);
730 vertices.extend_from_slice(&xz_color);
731 vertices.extend_from_slice(&[half, 0.0, pos]);
732 vertices.extend_from_slice(&xz_color);
733 }
734
735 for i in 0..=divisions {
736 let pos = -half + step * i as f32;
737
738 vertices.extend_from_slice(&[pos, -half, 0.0]);
739 vertices.extend_from_slice(&xy_color);
740 vertices.extend_from_slice(&[pos, half, 0.0]);
741 vertices.extend_from_slice(&xy_color);
742
743 vertices.extend_from_slice(&[-half, pos, 0.0]);
744 vertices.extend_from_slice(&xy_color);
745 vertices.extend_from_slice(&[half, pos, 0.0]);
746 vertices.extend_from_slice(&xy_color);
747 }
748
749 for i in 0..=divisions {
750 let pos = -half + step * i as f32;
751
752 vertices.extend_from_slice(&[0.0, pos, -half]);
753 vertices.extend_from_slice(&yz_color);
754 vertices.extend_from_slice(&[0.0, pos, half]);
755 vertices.extend_from_slice(&yz_color);
756
757 vertices.extend_from_slice(&[0.0, -half, pos]);
758 vertices.extend_from_slice(&yz_color);
759 vertices.extend_from_slice(&[0.0, half, pos]);
760 vertices.extend_from_slice(&yz_color);
761 }
762
763 vertices
764 }
765
766 #[rustfmt::skip]
767 fn generate_axis_vertices(size: f32) -> Vec<f32> {
768 vec![
769 0.0, 0.0, 0.0, 1.0, 0.2, 0.2, size, 0.0, 0.0, 1.0, 0.2, 0.2,
771 0.0, 0.0, 0.0, 0.5, 0.1, 0.1, -size, 0.0, 0.0, 0.5, 0.1, 0.1,
772 0.0, 0.0, 0.0, 0.2, 1.0, 0.2, 0.0, size, 0.0, 0.2, 1.0, 0.2,
774 0.0, 0.0, 0.0, 0.1, 0.5, 0.1, 0.0,-size, 0.0, 0.1, 0.5, 0.1,
775 0.0, 0.0, 0.0, 0.2, 0.2, 1.0, 0.0, 0.0, size, 0.2, 0.2, 1.0,
777 0.0, 0.0, 0.0, 0.1, 0.1, 0.5, 0.0, 0.0,-size, 0.1, 0.1, 0.5,
778 -0.2, 0.0, 0.0, 1.0, 1.0, 1.0, 0.2, 0.0, 0.0, 1.0, 1.0, 1.0,
780 0.0,-0.2, 0.0, 1.0, 1.0, 1.0, 0.0, 0.2, 0.0, 1.0, 1.0, 1.0,
781 0.0, 0.0,-0.2, 1.0, 1.0, 1.0, 0.0, 0.0, 0.2, 1.0, 1.0, 1.0,
782 ]
783 }
784
785 fn generate_cube_vertices(width: f32, height: f32, depth: f32) -> Vec<f32> {
786 let w = width / 2.0;
787 let h = height / 2.0;
788 let d = depth / 2.0;
789
790 #[rustfmt::skip]
791 let v = vec![
792 -w,-h, d, 0.0, 0.0, 1.0, 0.0, 0.0, w,-h, d, 0.0, 0.0, 1.0, 1.0, 0.0,
794 w, h, d, 0.0, 0.0, 1.0, 1.0, 1.0, w, h, d, 0.0, 0.0, 1.0, 1.0, 1.0,
795 -w, h, d, 0.0, 0.0, 1.0, 0.0, 1.0, -w,-h, d, 0.0, 0.0, 1.0, 0.0, 0.0,
796 w,-h,-d, 0.0, 0.0,-1.0, 0.0, 0.0, -w,-h,-d, 0.0, 0.0,-1.0, 1.0, 0.0,
798 -w, h,-d, 0.0, 0.0,-1.0, 1.0, 1.0, -w, h,-d, 0.0, 0.0,-1.0, 1.0, 1.0,
799 w, h,-d, 0.0, 0.0,-1.0, 0.0, 1.0, w,-h,-d, 0.0, 0.0,-1.0, 0.0, 0.0,
800 -w,-h,-d,-1.0, 0.0, 0.0, 0.0, 0.0, -w,-h, d,-1.0, 0.0, 0.0, 1.0, 0.0,
802 -w, h, d,-1.0, 0.0, 0.0, 1.0, 1.0, -w, h, d,-1.0, 0.0, 0.0, 1.0, 1.0,
803 -w, h,-d,-1.0, 0.0, 0.0, 0.0, 1.0, -w,-h,-d,-1.0, 0.0, 0.0, 0.0, 0.0,
804 w,-h, d, 1.0, 0.0, 0.0, 0.0, 0.0, w,-h,-d, 1.0, 0.0, 0.0, 1.0, 0.0,
806 w, h,-d, 1.0, 0.0, 0.0, 1.0, 1.0, w, h,-d, 1.0, 0.0, 0.0, 1.0, 1.0,
807 w, h, d, 1.0, 0.0, 0.0, 0.0, 1.0, w,-h, d, 1.0, 0.0, 0.0, 0.0, 0.0,
808 -w,-h,-d, 0.0,-1.0, 0.0, 0.0, 0.0, w,-h,-d, 0.0,-1.0, 0.0, 1.0, 0.0,
810 w,-h, d, 0.0,-1.0, 0.0, 1.0, 1.0, w,-h, d, 0.0,-1.0, 0.0, 1.0, 1.0,
811 -w,-h, d, 0.0,-1.0, 0.0, 0.0, 1.0, -w,-h,-d, 0.0,-1.0, 0.0, 0.0, 0.0,
812 -w, h, d, 0.0, 1.0, 0.0, 0.0, 0.0, w, h, d, 0.0, 1.0, 0.0, 1.0, 0.0,
814 w, h,-d, 0.0, 1.0, 0.0, 1.0, 1.0, w, h,-d, 0.0, 1.0, 0.0, 1.0, 1.0,
815 -w, h,-d, 0.0, 1.0, 0.0, 0.0, 1.0, -w, h, d, 0.0, 1.0, 0.0, 0.0, 0.0,
816 ];
817 v
818 }
819
820 fn generate_plane_vertices(width: f32, depth: f32) -> Vec<f32> {
821 let w = width / 2.0;
822 let d = depth / 2.0;
823
824 #[rustfmt::skip]
825 let v = vec![
826 -w, 0.0, d, 0.0, 1.0, 0.0, 0.0, 1.0, w, 0.0, d, 0.0, 1.0, 0.0, 1.0, 1.0,
828 w, 0.0,-d, 0.0, 1.0, 0.0, 1.0, 0.0, w, 0.0,-d, 0.0, 1.0, 0.0, 1.0, 0.0,
829 -w, 0.0,-d, 0.0, 1.0, 0.0, 0.0, 0.0, -w, 0.0, d, 0.0, 1.0, 0.0, 0.0, 1.0,
830 -w, 0.0,-d, 0.0,-1.0, 0.0, 0.0, 0.0, w, 0.0,-d, 0.0,-1.0, 0.0, 1.0, 0.0,
832 w, 0.0, d, 0.0,-1.0, 0.0, 1.0, 1.0, w, 0.0, d, 0.0,-1.0, 0.0, 1.0, 1.0,
833 -w, 0.0, d, 0.0,-1.0, 0.0, 0.0, 1.0, -w, 0.0,-d, 0.0,-1.0, 0.0, 0.0, 0.0,
834 ];
835 v
836 }
837
838 fn generate_sphere_vertices(radius: f32, segments: u32) -> Vec<f32> {
839 let mut vertices = Vec::new();
840 let segment_count = segments.max(8);
841
842 for i in 0..segment_count {
843 let lat0 = std::f32::consts::PI * (-0.5 + (i as f32) / segment_count as f32);
844 let lat1 = std::f32::consts::PI * (-0.5 + ((i + 1) as f32) / segment_count as f32);
845
846 for j in 0..segment_count {
847 let lng0 = 2.0 * std::f32::consts::PI * (j as f32) / segment_count as f32;
848 let lng1 = 2.0 * std::f32::consts::PI * ((j + 1) as f32) / segment_count as f32;
849
850 let x0 = radius * lat0.cos() * lng0.cos();
851 let y0 = radius * lat0.sin();
852 let z0 = radius * lat0.cos() * lng0.sin();
853 let x1 = radius * lat0.cos() * lng1.cos();
854 let y1 = radius * lat0.sin();
855 let z1 = radius * lat0.cos() * lng1.sin();
856 let x2 = radius * lat1.cos() * lng1.cos();
857 let y2 = radius * lat1.sin();
858 let z2 = radius * lat1.cos() * lng1.sin();
859 let x3 = radius * lat1.cos() * lng0.cos();
860 let y3 = radius * lat1.sin();
861 let z3 = radius * lat1.cos() * lng0.sin();
862
863 let u0 = j as f32 / segment_count as f32;
864 let u1 = (j + 1) as f32 / segment_count as f32;
865 let v0 = i as f32 / segment_count as f32;
866 let v1 = (i + 1) as f32 / segment_count as f32;
867
868 vertices.extend_from_slice(&[
869 x0,
870 y0,
871 z0,
872 x0 / radius,
873 y0 / radius,
874 z0 / radius,
875 u0,
876 v0,
877 x1,
878 y1,
879 z1,
880 x1 / radius,
881 y1 / radius,
882 z1 / radius,
883 u1,
884 v0,
885 x2,
886 y2,
887 z2,
888 x2 / radius,
889 y2 / radius,
890 z2 / radius,
891 u1,
892 v1,
893 x0,
894 y0,
895 z0,
896 x0 / radius,
897 y0 / radius,
898 z0 / radius,
899 u0,
900 v0,
901 x2,
902 y2,
903 z2,
904 x2 / radius,
905 y2 / radius,
906 z2 / radius,
907 u1,
908 v1,
909 x3,
910 y3,
911 z3,
912 x3 / radius,
913 y3 / radius,
914 z3 / radius,
915 u0,
916 v1,
917 ]);
918 }
919 }
920
921 vertices
922 }
923
924 fn generate_cylinder_vertices(radius: f32, height: f32, segments: u32) -> Vec<f32> {
925 let mut vertices = Vec::new();
926 let segment_count = segments.max(8);
927 let h = height / 2.0;
928
929 for i in 0..segment_count {
930 let a0 = 2.0 * std::f32::consts::PI * (i as f32) / segment_count as f32;
931 let a1 = 2.0 * std::f32::consts::PI * ((i + 1) as f32) / segment_count as f32;
932
933 let x0 = radius * a0.cos();
934 let z0 = radius * a0.sin();
935 let x1 = radius * a1.cos();
936 let z1 = radius * a1.sin();
937
938 let u0 = i as f32 / segment_count as f32;
939 let u1 = (i + 1) as f32 / segment_count as f32;
940
941 vertices.extend_from_slice(&[
943 x0,
944 -h,
945 z0,
946 x0 / radius,
947 0.0,
948 z0 / radius,
949 u0,
950 0.0,
951 x1,
952 -h,
953 z1,
954 x1 / radius,
955 0.0,
956 z1 / radius,
957 u1,
958 0.0,
959 x1,
960 h,
961 z1,
962 x1 / radius,
963 0.0,
964 z1 / radius,
965 u1,
966 1.0,
967 x0,
968 -h,
969 z0,
970 x0 / radius,
971 0.0,
972 z0 / radius,
973 u0,
974 0.0,
975 x1,
976 h,
977 z1,
978 x1 / radius,
979 0.0,
980 z1 / radius,
981 u1,
982 1.0,
983 x0,
984 h,
985 z0,
986 x0 / radius,
987 0.0,
988 z0 / radius,
989 u0,
990 1.0,
991 ]);
992
993 vertices.extend_from_slice(&[
995 0.0,
996 h,
997 0.0,
998 0.0,
999 1.0,
1000 0.0,
1001 0.5,
1002 0.5,
1003 x1,
1004 h,
1005 z1,
1006 0.0,
1007 1.0,
1008 0.0,
1009 0.5 + 0.5 * a1.cos(),
1010 0.5 + 0.5 * a1.sin(),
1011 x0,
1012 h,
1013 z0,
1014 0.0,
1015 1.0,
1016 0.0,
1017 0.5 + 0.5 * a0.cos(),
1018 0.5 + 0.5 * a0.sin(),
1019 ]);
1020
1021 vertices.extend_from_slice(&[
1023 0.0,
1024 -h,
1025 0.0,
1026 0.0,
1027 -1.0,
1028 0.0,
1029 0.5,
1030 0.5,
1031 x0,
1032 -h,
1033 z0,
1034 0.0,
1035 -1.0,
1036 0.0,
1037 0.5 + 0.5 * a0.cos(),
1038 0.5 + 0.5 * a0.sin(),
1039 x1,
1040 -h,
1041 z1,
1042 0.0,
1043 -1.0,
1044 0.0,
1045 0.5 + 0.5 * a1.cos(),
1046 0.5 + 0.5 * a1.sin(),
1047 ]);
1048 }
1049
1050 vertices
1051 }
1052
1053 pub fn create_primitive(&mut self, info: PrimitiveCreateInfo) -> u32 {
1059 let vertices = match info.primitive_type {
1060 PrimitiveType::Cube => {
1061 Self::generate_cube_vertices(info.width, info.height, info.depth)
1062 }
1063 PrimitiveType::Plane => Self::generate_plane_vertices(info.width, info.depth),
1064 PrimitiveType::Sphere => {
1065 Self::generate_sphere_vertices(info.width / 2.0, info.segments)
1066 }
1067 PrimitiveType::Cylinder => {
1068 Self::generate_cylinder_vertices(info.width / 2.0, info.height, info.segments)
1069 }
1070 };
1071
1072 let buffer = match Self::upload_buffer(self.backend.as_mut(), &vertices) {
1073 Ok(h) => h,
1074 Err(e) => {
1075 log::error!("Failed to create primitive buffer: {e}");
1076 return 0;
1077 }
1078 };
1079
1080 let id = self.next_object_id;
1081 self.next_object_id += 1;
1082
1083 self.objects.insert(
1084 id,
1085 Object3D {
1086 buffer,
1087 vertex_count: (vertices.len() / 8) as i32,
1088 position: Vector3::new(0.0, 0.0, 0.0),
1089 rotation: Vector3::new(0.0, 0.0, 0.0),
1090 scale: Vector3::new(1.0, 1.0, 1.0),
1091 texture_id: info.texture_id,
1092 },
1093 );
1094
1095 id
1096 }
1097
1098 pub fn set_object_position(&mut self, id: u32, x: f32, y: f32, z: f32) -> bool {
1104 if let Some(obj) = self.objects.get_mut(&id) {
1105 obj.position = Vector3::new(x, y, z);
1106 true
1107 } else {
1108 false
1109 }
1110 }
1111
1112 pub fn set_object_rotation(&mut self, id: u32, x: f32, y: f32, z: f32) -> bool {
1114 if let Some(obj) = self.objects.get_mut(&id) {
1115 obj.rotation = Vector3::new(x, y, z);
1116 true
1117 } else {
1118 false
1119 }
1120 }
1121
1122 pub fn set_object_scale(&mut self, id: u32, x: f32, y: f32, z: f32) -> bool {
1124 if let Some(obj) = self.objects.get_mut(&id) {
1125 obj.scale = Vector3::new(x, y, z);
1126 true
1127 } else {
1128 false
1129 }
1130 }
1131
1132 pub fn remove_object(&mut self, id: u32) -> bool {
1134 if let Some(obj) = self.objects.remove(&id) {
1135 self.backend.destroy_buffer(obj.buffer);
1136 true
1137 } else {
1138 false
1139 }
1140 }
1141
1142 pub fn add_light(&mut self, light: Light) -> u32 {
1148 let id = self.next_light_id;
1149 self.next_light_id += 1;
1150 self.lights.insert(id, light);
1151 id
1152 }
1153
1154 pub fn update_light(&mut self, id: u32, light: Light) -> bool {
1156 use std::collections::hash_map::Entry;
1157 if let Entry::Occupied(mut e) = self.lights.entry(id) {
1158 e.insert(light);
1159 true
1160 } else {
1161 false
1162 }
1163 }
1164
1165 pub fn remove_light(&mut self, id: u32) -> bool {
1167 self.lights.remove(&id).is_some()
1168 }
1169
1170 pub fn set_camera_position(&mut self, x: f32, y: f32, z: f32) {
1176 self.camera.position = Vector3::new(x, y, z);
1177 }
1178
1179 pub fn set_camera_rotation(&mut self, pitch: f32, yaw: f32, roll: f32) {
1181 self.camera.rotation = Vector3::new(pitch, yaw, roll);
1182 }
1183
1184 pub fn configure_grid(&mut self, config: GridConfig) {
1190 if config.size != self.grid_config.size || config.divisions != self.grid_config.divisions {
1191 self.backend.destroy_buffer(self.grid_buffer);
1192 if let Ok((buf, count)) =
1193 Self::create_grid_mesh(self.backend.as_mut(), config.size, config.divisions)
1194 {
1195 self.grid_buffer = buf;
1196 self.grid_vertex_count = count;
1197 }
1198 }
1199 self.grid_config = config;
1200 }
1201
1202 pub fn set_grid_enabled(&mut self, enabled: bool) {
1204 self.grid_config.enabled = enabled;
1205 }
1206
1207 pub fn configure_skybox(&mut self, config: SkyboxConfig) {
1209 self.skybox_config = config;
1210 }
1211
1212 pub fn configure_fog(&mut self, config: FogConfig) {
1214 self.fog_config = config;
1215 }
1216
1217 pub fn set_fog_enabled(&mut self, enabled: bool) {
1219 self.fog_config.enabled = enabled;
1220 }
1221
1222 pub fn render(&mut self, texture_manager: Option<&dyn TextureManagerTrait>) {
1228 self.backend.enable_depth_test();
1229 self.backend.set_depth_func(DepthFunc::Less);
1230 self.backend.enable_culling();
1231 self.backend.set_cull_face(CullFace::Back);
1232 self.backend.set_front_face(FrontFace::Ccw);
1233
1234 if self.skybox_config.enabled {
1235 self.backend.set_clear_color(
1236 self.skybox_config.color.x,
1237 self.skybox_config.color.y,
1238 self.skybox_config.color.z,
1239 self.skybox_config.color.w,
1240 );
1241 }
1242 self.backend.clear_depth();
1243
1244 let aspect = self.window_width as f32 / self.window_height as f32;
1245 let projection: Matrix4<f32> = perspective(Deg(45.0), aspect, 0.1, 1000.0);
1246 let view = self.camera.view_matrix();
1247 let view_arr = mat4_to_array(&view);
1248 let proj_arr = mat4_to_array(&projection);
1249
1250 if self.grid_config.enabled {
1252 let _ = self.backend.bind_shader(self.grid_shader_handle);
1253
1254 self.backend
1255 .set_uniform_mat4(self.grid_uniforms.view, &view_arr);
1256 self.backend
1257 .set_uniform_mat4(self.grid_uniforms.projection, &proj_arr);
1258 self.backend.set_uniform_vec3(
1259 self.grid_uniforms.view_pos,
1260 self.camera.position.x,
1261 self.camera.position.y,
1262 self.camera.position.z,
1263 );
1264 self.backend
1265 .set_uniform_float(self.grid_uniforms.alpha, 0.4);
1266 self.backend.set_uniform_int(
1267 self.grid_uniforms.fog_enabled,
1268 i32::from(self.fog_config.enabled),
1269 );
1270 self.backend.set_uniform_vec3(
1271 self.grid_uniforms.fog_color,
1272 self.fog_config.color.x,
1273 self.fog_config.color.y,
1274 self.fog_config.color.z,
1275 );
1276 self.backend
1277 .set_uniform_float(self.grid_uniforms.fog_density, self.fog_config.density);
1278
1279 self.backend.enable_blending();
1280 self.backend
1281 .set_blend_func(BlendFactor::SrcAlpha, BlendFactor::OneMinusSrcAlpha);
1282 self.backend.set_depth_mask(false);
1283
1284 let _ = self.backend.bind_buffer(self.grid_buffer);
1285 self.backend.set_vertex_attributes(&self.grid_layout);
1286 let _ = self.backend.draw_arrays(
1287 PrimitiveTopology::Lines,
1288 0,
1289 self.grid_vertex_count as u32,
1290 );
1291
1292 self.backend.set_line_width(3.0);
1293 let _ = self.backend.bind_buffer(self.axis_buffer);
1294 self.backend.set_vertex_attributes(&self.grid_layout);
1295 self.backend
1296 .set_uniform_float(self.grid_uniforms.alpha, 1.0);
1297 let _ = self.backend.draw_arrays(
1298 PrimitiveTopology::Lines,
1299 0,
1300 self.axis_vertex_count as u32,
1301 );
1302 self.backend.set_line_width(1.0);
1303
1304 self.backend.set_depth_mask(true);
1305 self.backend.disable_blending();
1306 self.backend.unbind_shader();
1307 }
1308
1309 let _ = self.backend.bind_shader(self.shader_handle);
1311
1312 self.backend.set_uniform_mat4(self.uniforms.view, &view_arr);
1313 self.backend
1314 .set_uniform_mat4(self.uniforms.projection, &proj_arr);
1315 self.backend.set_uniform_vec3(
1316 self.uniforms.view_pos,
1317 self.camera.position.x,
1318 self.camera.position.y,
1319 self.camera.position.z,
1320 );
1321
1322 self.backend.set_uniform_int(
1323 self.uniforms.fog_enabled,
1324 i32::from(self.fog_config.enabled),
1325 );
1326 self.backend.set_uniform_vec3(
1327 self.uniforms.fog_color,
1328 self.fog_config.color.x,
1329 self.fog_config.color.y,
1330 self.fog_config.color.z,
1331 );
1332 self.backend
1333 .set_uniform_float(self.uniforms.fog_density, self.fog_config.density);
1334
1335 let light_count = self.lights.len().min(MAX_LIGHTS) as i32;
1336 self.backend
1337 .set_uniform_int(self.uniforms.num_lights, light_count);
1338
1339 for (i, (_, light)) in self.lights.iter().enumerate().take(MAX_LIGHTS) {
1340 let lu = &self.uniforms.lights[i];
1341 self.backend
1342 .set_uniform_int(lu.light_type, light.light_type as i32);
1343 self.backend.set_uniform_vec3(
1344 lu.position,
1345 light.position.x,
1346 light.position.y,
1347 light.position.z,
1348 );
1349 self.backend.set_uniform_vec3(
1350 lu.direction,
1351 light.direction.x,
1352 light.direction.y,
1353 light.direction.z,
1354 );
1355 self.backend
1356 .set_uniform_vec3(lu.color, light.color.x, light.color.y, light.color.z);
1357 self.backend
1358 .set_uniform_float(lu.intensity, light.intensity);
1359 self.backend.set_uniform_float(lu.range, light.range);
1360 self.backend
1361 .set_uniform_float(lu.spot_angle, light.spot_angle);
1362 self.backend
1363 .set_uniform_int(lu.enabled, i32::from(light.enabled));
1364 }
1365
1366 self.backend.set_uniform_int(self.uniforms.texture1, 0);
1367
1368 for obj in self.objects.values() {
1369 let model = Self::create_model_matrix(obj.position, obj.rotation, obj.scale);
1370 let model_arr = mat4_to_array(&model);
1371 self.backend
1372 .set_uniform_mat4(self.uniforms.model, &model_arr);
1373
1374 if obj.texture_id > 0 {
1375 if let Some(tm) = texture_manager {
1376 tm.bind_texture(obj.texture_id, 0);
1377 }
1378 self.backend.set_uniform_int(self.uniforms.use_texture, 1);
1379 self.backend
1380 .set_uniform_vec4(self.uniforms.object_color, 1.0, 1.0, 1.0, 1.0);
1381 } else {
1382 self.backend.set_uniform_int(self.uniforms.use_texture, 0);
1383 self.backend
1384 .set_uniform_vec4(self.uniforms.object_color, 0.8, 0.8, 0.8, 1.0);
1385 }
1386
1387 let _ = self.backend.bind_buffer(obj.buffer);
1388 self.backend.set_vertex_attributes(&self.object_layout);
1389 let _ =
1390 self.backend
1391 .draw_arrays(PrimitiveTopology::Triangles, 0, obj.vertex_count as u32);
1392 }
1393
1394 self.backend.unbind_shader();
1395 }
1396
1397 fn create_model_matrix(
1402 position: Vector3<f32>,
1403 rotation: Vector3<f32>,
1404 scale: Vector3<f32>,
1405 ) -> Matrix4<f32> {
1406 let translation = Matrix4::from_translation(position);
1407 let rot_x = Matrix4::from_angle_x(Deg(rotation.x));
1408 let rot_y = Matrix4::from_angle_y(Deg(rotation.y));
1409 let rot_z = Matrix4::from_angle_z(Deg(rotation.z));
1410 let rotation_matrix = rot_z * rot_y * rot_x;
1411 let scale_matrix = Matrix4::from_nonuniform_scale(scale.x, scale.y, scale.z);
1412 translation * rotation_matrix * scale_matrix
1413 }
1414}
1415
1416impl Drop for Renderer3D {
1417 fn drop(&mut self) {
1418 for obj in self.objects.values() {
1419 self.backend.destroy_buffer(obj.buffer);
1420 }
1421 self.backend.destroy_buffer(self.grid_buffer);
1422 self.backend.destroy_buffer(self.axis_buffer);
1423 self.backend.destroy_shader(self.shader_handle);
1424 self.backend.destroy_shader(self.grid_shader_handle);
1425 }
1426}
1427
1428pub trait TextureManagerTrait {
1434 fn bind_texture(&self, texture_id: u32, slot: u32);
1436}
1437
1438fn mat4_to_array(m: &Matrix4<f32>) -> [f32; 16] {
1443 let ptr = m.as_ptr();
1445 let mut arr = [0.0f32; 16];
1446 unsafe {
1448 std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 16);
1449 }
1450 arr
1451}