3rb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/3rb.gemspec +29 -0
  4. data/CHANGELOG.md +12 -0
  5. data/LICENSE +21 -0
  6. data/README.md +321 -0
  7. data/Rakefile +13 -0
  8. data/examples/01_hello_cube.rb +29 -0
  9. data/examples/02_basic_geometries.rb +56 -0
  10. data/examples/03_materials.rb +61 -0
  11. data/examples/04_lighting.rb +63 -0
  12. data/examples/05_animation.rb +79 -0
  13. data/examples/06_custom_shader.rb +92 -0
  14. data/examples/07_scene_graph.rb +74 -0
  15. data/examples/08_orbit_controls.rb +50 -0
  16. data/examples/09_3d_chart.rb +71 -0
  17. data/examples/10_procedural_terrain.rb +140 -0
  18. data/examples/11_particle_system.rb +68 -0
  19. data/examples/12_model_loader.rb +73 -0
  20. data/examples/13_game_prototype.rb +145 -0
  21. data/examples/14_utah_teapot.rb +291 -0
  22. data/examples/15_stanford_bunny.rb +200 -0
  23. data/examples/16_cornell_box.rb +373 -0
  24. data/examples/17_weird_fractal4.rb +130 -0
  25. data/examples/18_platonic_solids.rb +268 -0
  26. data/lib/3rb/animation/animation_clip.rb +287 -0
  27. data/lib/3rb/animation/animation_mixer.rb +366 -0
  28. data/lib/3rb/cameras/camera.rb +50 -0
  29. data/lib/3rb/cameras/orthographic_camera.rb +92 -0
  30. data/lib/3rb/cameras/perspective_camera.rb +103 -0
  31. data/lib/3rb/controls/orbit_controls.rb +341 -0
  32. data/lib/3rb/core/buffer_attribute.rb +172 -0
  33. data/lib/3rb/core/group.rb +9 -0
  34. data/lib/3rb/core/object3d.rb +298 -0
  35. data/lib/3rb/core/scene.rb +78 -0
  36. data/lib/3rb/dsl/helpers.rb +57 -0
  37. data/lib/3rb/dsl/scene_builder.rb +288 -0
  38. data/lib/3rb/ffi/glfw.rb +61 -0
  39. data/lib/3rb/ffi/opengl.rb +137 -0
  40. data/lib/3rb/ffi/platform.rb +65 -0
  41. data/lib/3rb/geometries/box_geometry.rb +101 -0
  42. data/lib/3rb/geometries/buffer_geometry.rb +345 -0
  43. data/lib/3rb/geometries/cone_geometry.rb +29 -0
  44. data/lib/3rb/geometries/cylinder_geometry.rb +149 -0
  45. data/lib/3rb/geometries/plane_geometry.rb +75 -0
  46. data/lib/3rb/geometries/sphere_geometry.rb +93 -0
  47. data/lib/3rb/geometries/torus_geometry.rb +77 -0
  48. data/lib/3rb/lights/ambient_light.rb +9 -0
  49. data/lib/3rb/lights/directional_light.rb +57 -0
  50. data/lib/3rb/lights/hemisphere_light.rb +26 -0
  51. data/lib/3rb/lights/light.rb +27 -0
  52. data/lib/3rb/lights/point_light.rb +68 -0
  53. data/lib/3rb/lights/rect_area_light.rb +35 -0
  54. data/lib/3rb/lights/spot_light.rb +88 -0
  55. data/lib/3rb/loaders/gltf_loader.rb +304 -0
  56. data/lib/3rb/loaders/loader.rb +94 -0
  57. data/lib/3rb/loaders/obj_loader.rb +186 -0
  58. data/lib/3rb/loaders/texture_loader.rb +55 -0
  59. data/lib/3rb/materials/basic_material.rb +70 -0
  60. data/lib/3rb/materials/lambert_material.rb +102 -0
  61. data/lib/3rb/materials/material.rb +114 -0
  62. data/lib/3rb/materials/phong_material.rb +106 -0
  63. data/lib/3rb/materials/shader_material.rb +104 -0
  64. data/lib/3rb/materials/standard_material.rb +106 -0
  65. data/lib/3rb/math/color.rb +246 -0
  66. data/lib/3rb/math/euler.rb +156 -0
  67. data/lib/3rb/math/math_utils.rb +132 -0
  68. data/lib/3rb/math/matrix3.rb +269 -0
  69. data/lib/3rb/math/matrix4.rb +501 -0
  70. data/lib/3rb/math/quaternion.rb +337 -0
  71. data/lib/3rb/math/vector2.rb +216 -0
  72. data/lib/3rb/math/vector3.rb +366 -0
  73. data/lib/3rb/math/vector4.rb +233 -0
  74. data/lib/3rb/native/gl.rb +382 -0
  75. data/lib/3rb/native/native.rb +55 -0
  76. data/lib/3rb/native/window.rb +111 -0
  77. data/lib/3rb/native.rb +9 -0
  78. data/lib/3rb/objects/line.rb +116 -0
  79. data/lib/3rb/objects/mesh.rb +40 -0
  80. data/lib/3rb/objects/points.rb +71 -0
  81. data/lib/3rb/renderers/opengl_renderer.rb +567 -0
  82. data/lib/3rb/renderers/renderer.rb +60 -0
  83. data/lib/3rb/renderers/shader_lib.rb +100 -0
  84. data/lib/3rb/textures/cube_texture.rb +26 -0
  85. data/lib/3rb/textures/data_texture.rb +35 -0
  86. data/lib/3rb/textures/render_target.rb +125 -0
  87. data/lib/3rb/textures/texture.rb +190 -0
  88. data/lib/3rb/version.rb +5 -0
  89. data/lib/3rb.rb +86 -0
  90. data/shaders/basic.frag +19 -0
  91. data/shaders/basic.vert +15 -0
  92. data/shaders/common/lights.glsl +53 -0
  93. data/shaders/common/uniforms.glsl +9 -0
  94. data/shaders/lambert.frag +37 -0
  95. data/shaders/lambert.vert +22 -0
  96. data/shaders/phong.frag +51 -0
  97. data/shaders/phong.vert +28 -0
  98. data/shaders/standard.frag +92 -0
  99. data/shaders/standard.vert +28 -0
  100. metadata +155 -0
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Custom Shaders using ShaderMaterial
5
+
6
+ require_relative "../lib/3rb"
7
+
8
+ VERTEX_SHADER = <<~GLSL
9
+ #version 330 core
10
+
11
+ layout(location = 0) in vec3 position;
12
+ layout(location = 1) in vec3 normal;
13
+ layout(location = 2) in vec2 uv;
14
+
15
+ uniform mat4 modelViewMatrix;
16
+ uniform mat4 projectionMatrix;
17
+ uniform float time;
18
+
19
+ out vec2 vUv;
20
+ out vec3 vNormal;
21
+ out float vDisplacement;
22
+
23
+ void main() {
24
+ vUv = uv;
25
+ vNormal = normal;
26
+
27
+ float displacement = sin(position.x * 5.0 + time) *
28
+ sin(position.y * 5.0 + time) * 0.2;
29
+ vDisplacement = displacement;
30
+
31
+ vec3 newPosition = position + normal * displacement;
32
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
33
+ }
34
+ GLSL
35
+
36
+ FRAGMENT_SHADER = <<~GLSL
37
+ #version 330 core
38
+
39
+ in vec2 vUv;
40
+ in vec3 vNormal;
41
+ in float vDisplacement;
42
+
43
+ uniform float time;
44
+ uniform vec3 color1;
45
+ uniform vec3 color2;
46
+
47
+ out vec4 fragColor;
48
+
49
+ void main() {
50
+ float mixFactor = (vDisplacement + 0.2) * 2.5;
51
+ vec3 color = mix(color1, color2, mixFactor);
52
+
53
+ float fresnel = pow(1.0 - abs(dot(vNormal, vec3(0.0, 0.0, 1.0))), 2.0);
54
+ color += fresnel * 0.3;
55
+
56
+ fragColor = vec4(color, 1.0);
57
+ }
58
+ GLSL
59
+
60
+ scene = Sunrb::Scene.new
61
+ scene.background = Sunrb::Color.new(0x000011)
62
+
63
+ camera = Sunrb::PerspectiveCamera.new(fov: 75, aspect: 800.0 / 600.0, near: 0.1, far: 1000)
64
+ camera.position.set(0, 0, 3)
65
+
66
+ geometry = Sunrb::SphereGeometry.new(radius: 1, width_segments: 64, height_segments: 64)
67
+
68
+ shader_material = Sunrb::ShaderMaterial.new(
69
+ vertex_shader: VERTEX_SHADER,
70
+ fragment_shader: FRAGMENT_SHADER,
71
+ uniforms: {
72
+ time: 0.0,
73
+ color1: Sunrb::Color.new(0x00ff88),
74
+ color2: Sunrb::Color.new(0xff0088)
75
+ }
76
+ )
77
+
78
+ sphere = Sunrb::Mesh.new(geometry, shader_material)
79
+ scene.add(sphere)
80
+
81
+ puts "Custom shader material created"
82
+ puts "Press ESC to exit"
83
+
84
+ renderer = Sunrb::OpenGLRenderer.new(width: 800, height: 600, title: "Custom Shader Example")
85
+
86
+ time = 0.0
87
+ renderer.run do |delta|
88
+ time += delta
89
+ shader_material.uniforms[:time] = time
90
+ sphere.rotation.y += delta * 0.5
91
+ renderer.render(scene, camera)
92
+ end
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Scene Graph - Parent-child relationships (Solar System)
5
+
6
+ require_relative "../lib/3rb"
7
+
8
+ scene = Sunrb::Scene.new
9
+ scene.background = Sunrb::Color.new(0x000022)
10
+
11
+ camera = Sunrb::PerspectiveCamera.new(fov: 60, aspect: 800.0 / 600.0, near: 0.1, far: 1000)
12
+ camera.position.set(0, 10, 20)
13
+ camera.look_at(Sunrb::Vector3.new(0, 0, 0))
14
+
15
+ ambient = Sunrb::AmbientLight.new(color: 0x404040)
16
+ scene.add(ambient)
17
+
18
+ solar_system = Sunrb::Group.new
19
+ solar_system.name = "solar_system"
20
+ scene.add(solar_system)
21
+
22
+ sun_geometry = Sunrb::SphereGeometry.new(radius: 1.5)
23
+ sun_material = Sunrb::MeshBasicMaterial.new(color: 0xffff00)
24
+ sun = Sunrb::Mesh.new(sun_geometry, sun_material)
25
+ sun.name = "sun"
26
+ solar_system.add(sun)
27
+
28
+ earth_orbit = Sunrb::Group.new
29
+ earth_orbit.name = "earth_orbit"
30
+ solar_system.add(earth_orbit)
31
+
32
+ earth_geometry = Sunrb::SphereGeometry.new(radius: 0.5)
33
+ earth_material = Sunrb::MeshBasicMaterial.new(color: 0x3498db)
34
+ earth = Sunrb::Mesh.new(earth_geometry, earth_material)
35
+ earth.name = "earth"
36
+ earth.position.set(5, 0, 0)
37
+ earth_orbit.add(earth)
38
+
39
+ moon_orbit = Sunrb::Group.new
40
+ moon_orbit.name = "moon_orbit"
41
+ moon_orbit.position.copy(earth.position)
42
+ earth_orbit.add(moon_orbit)
43
+
44
+ moon_geometry = Sunrb::SphereGeometry.new(radius: 0.15)
45
+ moon_material = Sunrb::MeshBasicMaterial.new(color: 0xbdc3c7)
46
+ moon = Sunrb::Mesh.new(moon_geometry, moon_material)
47
+ moon.name = "moon"
48
+ moon.position.set(1, 0, 0)
49
+ moon_orbit.add(moon)
50
+
51
+ mars_orbit = Sunrb::Group.new
52
+ mars_orbit.name = "mars_orbit"
53
+ solar_system.add(mars_orbit)
54
+
55
+ mars_geometry = Sunrb::SphereGeometry.new(radius: 0.4)
56
+ mars_material = Sunrb::MeshBasicMaterial.new(color: 0xe74c3c)
57
+ mars = Sunrb::Mesh.new(mars_geometry, mars_material)
58
+ mars.name = "mars"
59
+ mars.position.set(8, 0, 0)
60
+ mars_orbit.add(mars)
61
+
62
+ puts "=== Solar System Demo ==="
63
+ puts "Press ESC to exit"
64
+
65
+ renderer = Sunrb::OpenGLRenderer.new(width: 800, height: 600, title: "Scene Graph - Solar System")
66
+
67
+ renderer.run do |delta|
68
+ sun.rotation.y += delta * 0.5
69
+ earth_orbit.rotation.y += delta * 0.8
70
+ moon_orbit.rotation.y += delta * 2.0
71
+ mars_orbit.rotation.y += delta * 0.5
72
+
73
+ renderer.render(scene, camera)
74
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # OrbitControls - Automatic camera orbit around target
5
+
6
+ require_relative "../lib/3rb"
7
+
8
+ scene = Sunrb::Scene.new
9
+ scene.background = Sunrb::Color.new(0x1a1a2e)
10
+
11
+ camera = Sunrb::PerspectiveCamera.new(fov: 60, aspect: 800.0 / 600.0, near: 0.1, far: 1000)
12
+ camera.position.set(5, 5, 5)
13
+
14
+ controls = Sunrb::OrbitControls.new(camera)
15
+ controls.enable_damping = true
16
+ controls.damping_factor = 0.05
17
+ controls.min_distance = 3
18
+ controls.max_distance = 20
19
+ controls.auto_rotate = true
20
+ controls.auto_rotate_speed = 2.0
21
+
22
+ ambient = Sunrb::AmbientLight.new(color: 0x404040)
23
+ scene.add(ambient)
24
+
25
+ directional = Sunrb::DirectionalLight.new(color: 0xffffff, intensity: 0.8)
26
+ directional.position.set(5, 10, 5)
27
+ scene.add(directional)
28
+
29
+ geometry = Sunrb::BoxGeometry.new(width: 2, height: 2, depth: 2)
30
+ material = Sunrb::MeshBasicMaterial.new(color: 0x00ff88)
31
+ cube = Sunrb::Mesh.new(geometry, material)
32
+ scene.add(cube)
33
+
34
+ floor_geo = Sunrb::PlaneGeometry.new(width: 20, height: 20)
35
+ floor_mat = Sunrb::MeshBasicMaterial.new(color: 0x333333)
36
+ floor = Sunrb::Mesh.new(floor_geo, floor_mat)
37
+ floor.rotation.x = -Math::PI / 2
38
+ floor.position.y = -1.5
39
+ scene.add(floor)
40
+
41
+ puts "=== OrbitControls Demo ==="
42
+ puts "Camera auto-rotating around the cube"
43
+ puts "Press ESC to exit"
44
+
45
+ renderer = Sunrb::OpenGLRenderer.new(width: 800, height: 600, title: "OrbitControls Example")
46
+
47
+ renderer.run do |delta|
48
+ controls.update(delta)
49
+ renderer.render(scene, camera)
50
+ end
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # 3D Bar Chart - Data visualization
5
+
6
+ require_relative "../lib/3rb"
7
+
8
+ DATA = {
9
+ "Ruby" => 85,
10
+ "Python" => 92,
11
+ "JavaScript" => 78,
12
+ "Go" => 65,
13
+ "Rust" => 45
14
+ }.freeze
15
+
16
+ COLORS = [0xe74c3c, 0x3498db, 0xf1c40f, 0x2ecc71, 0x9b59b6].freeze
17
+
18
+ scene = Sunrb::Scene.new
19
+ scene.background = Sunrb::Color.new(0xffffff)
20
+
21
+ camera = Sunrb::PerspectiveCamera.new(fov: 50, aspect: 800.0 / 600.0, near: 0.1, far: 1000)
22
+ camera.position.set(8, 8, 12)
23
+ camera.look_at(Sunrb::Vector3.new(0, 2, 0))
24
+
25
+ ambient = Sunrb::AmbientLight.new(color: 0x606060)
26
+ scene.add(ambient)
27
+
28
+ directional = Sunrb::DirectionalLight.new(color: 0xffffff, intensity: 0.8)
29
+ directional.position.set(10, 15, 10)
30
+ scene.add(directional)
31
+
32
+ floor_geo = Sunrb::PlaneGeometry.new(width: 15, height: 10)
33
+ floor_mat = Sunrb::MeshBasicMaterial.new(color: 0xeeeeee)
34
+ floor = Sunrb::Mesh.new(floor_geo, floor_mat)
35
+ floor.rotation.x = -Math::PI / 2
36
+ scene.add(floor)
37
+
38
+ chart_group = Sunrb::Group.new
39
+ chart_group.name = "chart"
40
+ scene.add(chart_group)
41
+
42
+ max_value = DATA.values.max.to_f
43
+ bar_width = 1.5
44
+ spacing = 2.5
45
+
46
+ DATA.each_with_index do |(label, value), index|
47
+ height = (value / max_value) * 6
48
+ x_pos = (index - DATA.size / 2.0) * spacing
49
+
50
+ bar_geo = Sunrb::BoxGeometry.new(width: bar_width, height: height, depth: bar_width)
51
+ bar_mat = Sunrb::MeshBasicMaterial.new(color: COLORS[index])
52
+ bar = Sunrb::Mesh.new(bar_geo, bar_mat)
53
+ bar.position.set(x_pos, height / 2, 0)
54
+ bar.name = label
55
+ chart_group.add(bar)
56
+ end
57
+
58
+ puts "=== 3D Bar Chart ==="
59
+ puts "Data: #{DATA.map { |k, v| "#{k}: #{v}" }.join(', ')}"
60
+ puts "Press ESC to exit"
61
+
62
+ renderer = Sunrb::OpenGLRenderer.new(width: 800, height: 600, title: "3D Bar Chart")
63
+
64
+ time = 0.0
65
+ renderer.run do |delta|
66
+ time += delta
67
+ chart_group.children.each_with_index do |bar, i|
68
+ bar.rotation.y = Math.sin(time + i * 0.5) * 0.1
69
+ end
70
+ renderer.render(scene, camera)
71
+ end
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Procedural Terrain Generation using noise functions
5
+
6
+ require_relative "../lib/3rb"
7
+
8
+ class SimplexNoise
9
+ def initialize(seed = 0)
10
+ @perm = (0...256).to_a.shuffle(random: Random.new(seed))
11
+ @perm += @perm
12
+ end
13
+
14
+ def noise2d(x, y)
15
+ xi = x.floor & 255
16
+ yi = y.floor & 255
17
+ xf = x - x.floor
18
+ yf = y - y.floor
19
+
20
+ u = fade(xf)
21
+ v = fade(yf)
22
+
23
+ aa = @perm[@perm[xi] + yi]
24
+ ab = @perm[@perm[xi] + yi + 1]
25
+ ba = @perm[@perm[xi + 1] + yi]
26
+ bb = @perm[@perm[xi + 1] + yi + 1]
27
+
28
+ lerp(v,
29
+ lerp(u, grad(aa, xf, yf), grad(ba, xf - 1, yf)),
30
+ lerp(u, grad(ab, xf, yf - 1), grad(bb, xf - 1, yf - 1)))
31
+ end
32
+
33
+ def octave_noise(x, y, octaves: 4, persistence: 0.5)
34
+ total = 0.0
35
+ frequency = 1.0
36
+ amplitude = 1.0
37
+ max_value = 0.0
38
+
39
+ octaves.times do
40
+ total += noise2d(x * frequency, y * frequency) * amplitude
41
+ max_value += amplitude
42
+ amplitude *= persistence
43
+ frequency *= 2
44
+ end
45
+
46
+ total / max_value
47
+ end
48
+
49
+ private
50
+
51
+ def fade(t)
52
+ t * t * t * (t * (t * 6 - 15) + 10)
53
+ end
54
+
55
+ def lerp(t, a, b)
56
+ a + t * (b - a)
57
+ end
58
+
59
+ def grad(hash, x, y)
60
+ case hash & 3
61
+ when 0 then x + y
62
+ when 1 then -x + y
63
+ when 2 then x - y
64
+ else -x - y
65
+ end
66
+ end
67
+ end
68
+
69
+ WIDTH = 50
70
+ HEIGHT = 50
71
+ SCALE = 0.1
72
+ HEIGHT_SCALE = 5.0
73
+
74
+ scene = Sunrb::Scene.new
75
+ scene.background = Sunrb::Color.new(0x87ceeb)
76
+
77
+ camera = Sunrb::PerspectiveCamera.new(fov: 60, aspect: 800.0 / 600.0, near: 0.1, far: 1000)
78
+ camera.position.set(30, 25, 30)
79
+ camera.look_at(Sunrb::Vector3.new(0, 0, 0))
80
+
81
+ ambient = Sunrb::AmbientLight.new(color: 0x404040)
82
+ scene.add(ambient)
83
+
84
+ sun = Sunrb::DirectionalLight.new(color: 0xffffcc, intensity: 1.0)
85
+ sun.position.set(50, 50, 50)
86
+ scene.add(sun)
87
+
88
+ noise = SimplexNoise.new(42)
89
+
90
+ positions = []
91
+ indices = []
92
+
93
+ (0...HEIGHT).each do |z|
94
+ (0...WIDTH).each do |x|
95
+ height = noise.octave_noise(x * SCALE, z * SCALE, octaves: 6) * HEIGHT_SCALE
96
+ height = [height, 0].max
97
+
98
+ world_x = x - WIDTH / 2.0
99
+ world_z = z - HEIGHT / 2.0
100
+ positions.push(world_x, height, world_z)
101
+ end
102
+ end
103
+
104
+ (0...(HEIGHT - 1)).each do |z|
105
+ (0...(WIDTH - 1)).each do |x|
106
+ a = z * WIDTH + x
107
+ b = z * WIDTH + x + 1
108
+ c = (z + 1) * WIDTH + x
109
+ d = (z + 1) * WIDTH + x + 1
110
+
111
+ indices.push(a, b, c)
112
+ indices.push(b, d, c)
113
+ end
114
+ end
115
+
116
+ geometry = Sunrb::BufferGeometry.new
117
+ geometry.set_attribute(:position, Sunrb::Float32BufferAttribute.new(positions, 3))
118
+ geometry.set_index(indices)
119
+ geometry.compute_vertex_normals
120
+
121
+ material = Sunrb::MeshBasicMaterial.new(color: 0x27ae60)
122
+
123
+ terrain = Sunrb::Mesh.new(geometry, material)
124
+ terrain.name = "terrain"
125
+ scene.add(terrain)
126
+
127
+ puts "=== Procedural Terrain Generator ==="
128
+ puts "Grid size: #{WIDTH}x#{HEIGHT}, Vertices: #{positions.length / 3}"
129
+ puts "Press ESC to exit"
130
+
131
+ renderer = Sunrb::OpenGLRenderer.new(width: 800, height: 600, title: "Procedural Terrain")
132
+
133
+ angle = 0.0
134
+ renderer.run do |delta|
135
+ angle += delta * 0.2
136
+ camera.position.x = Math.cos(angle) * 40
137
+ camera.position.z = Math.sin(angle) * 40
138
+ camera.look_at(Sunrb::Vector3.new(0, 0, 0))
139
+ renderer.render(scene, camera)
140
+ end
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Particle System using Points
5
+
6
+ require_relative "../lib/3rb"
7
+
8
+ PARTICLE_COUNT = 200
9
+ SPREAD = 15.0
10
+
11
+ scene = Sunrb::Scene.new
12
+ scene.background = Sunrb::Color.new(0x000022)
13
+
14
+ camera = Sunrb::PerspectiveCamera.new(fov: 75, aspect: 800.0 / 600.0, near: 0.1, far: 1000)
15
+ camera.position.set(0, 5, 25)
16
+ camera.look_at(Sunrb::Vector3.new(0, 0, 0))
17
+
18
+ particles_group = Sunrb::Group.new
19
+ particles_group.name = "particles"
20
+ scene.add(particles_group)
21
+
22
+ particles = []
23
+ PARTICLE_COUNT.times do
24
+ geo = Sunrb::SphereGeometry.new(radius: 0.1)
25
+ mat = Sunrb::MeshBasicMaterial.new(
26
+ color: (0xff0000 + rand(0xffff))
27
+ )
28
+ mesh = Sunrb::Mesh.new(geo, mat)
29
+ mesh.position.set(
30
+ (rand - 0.5) * SPREAD,
31
+ (rand - 0.5) * SPREAD,
32
+ (rand - 0.5) * SPREAD
33
+ )
34
+ particles_group.add(mesh)
35
+ particles << {
36
+ mesh: mesh,
37
+ velocity: Sunrb::Vector3.new(
38
+ (rand - 0.5) * 0.5,
39
+ rand * 0.5 + 0.2,
40
+ (rand - 0.5) * 0.5
41
+ )
42
+ }
43
+ end
44
+
45
+ puts "=== Particle System ==="
46
+ puts "Particle count: #{PARTICLE_COUNT}"
47
+ puts "Press ESC to exit"
48
+
49
+ renderer = Sunrb::OpenGLRenderer.new(width: 800, height: 600, title: "Particle System")
50
+
51
+ renderer.run do |delta|
52
+ particles.each do |p|
53
+ p[:mesh].position.x += p[:velocity].x * delta * 5
54
+ p[:mesh].position.y += p[:velocity].y * delta * 5
55
+ p[:mesh].position.z += p[:velocity].z * delta * 5
56
+
57
+ if p[:mesh].position.y > SPREAD / 2
58
+ p[:mesh].position.set(
59
+ (rand - 0.5) * SPREAD,
60
+ -SPREAD / 2,
61
+ (rand - 0.5) * SPREAD
62
+ )
63
+ end
64
+ end
65
+
66
+ particles_group.rotation.y += delta * 0.1
67
+ renderer.render(scene, camera)
68
+ end
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Simple House - Building from basic geometries
5
+
6
+ require_relative "../lib/3rb"
7
+
8
+ scene = Sunrb::Scene.new
9
+ scene.background = Sunrb::Color.new(0x87ceeb)
10
+
11
+ camera = Sunrb::PerspectiveCamera.new(fov: 60, aspect: 800.0 / 600.0, near: 0.1, far: 1000)
12
+ camera.position.set(8, 6, 8)
13
+ camera.look_at(Sunrb::Vector3.new(0, 1.5, 0))
14
+
15
+ ambient = Sunrb::AmbientLight.new(color: 0x606060)
16
+ scene.add(ambient)
17
+
18
+ sun = Sunrb::DirectionalLight.new(color: 0xffffff, intensity: 0.8)
19
+ sun.position.set(10, 10, 5)
20
+ scene.add(sun)
21
+
22
+ house = Sunrb::Group.new
23
+ house.name = "house"
24
+
25
+ body_geo = Sunrb::BoxGeometry.new(width: 4, height: 3, depth: 4)
26
+ body_mat = Sunrb::MeshBasicMaterial.new(color: 0xd35400)
27
+ body = Sunrb::Mesh.new(body_geo, body_mat)
28
+ body.position.y = 1.5
29
+ house.add(body)
30
+
31
+ roof_geo = Sunrb::ConeGeometry.new(radius: 3.5, height: 2, radial_segments: 4)
32
+ roof_mat = Sunrb::MeshBasicMaterial.new(color: 0x8b4513)
33
+ roof = Sunrb::Mesh.new(roof_geo, roof_mat)
34
+ roof.position.y = 4
35
+ roof.rotation.y = Math::PI / 4
36
+ house.add(roof)
37
+
38
+ door_geo = Sunrb::BoxGeometry.new(width: 0.8, height: 1.5, depth: 0.1)
39
+ door_mat = Sunrb::MeshBasicMaterial.new(color: 0x5d4037)
40
+ door = Sunrb::Mesh.new(door_geo, door_mat)
41
+ door.position.set(0, 0.75, 2.05)
42
+ house.add(door)
43
+
44
+ [-1, 1].each do |x|
45
+ window_geo = Sunrb::BoxGeometry.new(width: 0.6, height: 0.6, depth: 0.1)
46
+ window_mat = Sunrb::MeshBasicMaterial.new(color: 0x87ceeb)
47
+ window = Sunrb::Mesh.new(window_geo, window_mat)
48
+ window.position.set(x * 1.2, 2, 2.05)
49
+ house.add(window)
50
+ end
51
+
52
+ scene.add(house)
53
+
54
+ ground_geo = Sunrb::PlaneGeometry.new(width: 20, height: 20)
55
+ ground_mat = Sunrb::MeshBasicMaterial.new(color: 0x27ae60)
56
+ ground = Sunrb::Mesh.new(ground_geo, ground_mat)
57
+ ground.rotation.x = -Math::PI / 2
58
+ scene.add(ground)
59
+
60
+ puts "=== Simple House Model ==="
61
+ puts "House parts: #{house.children.length}"
62
+ puts "Press ESC to exit"
63
+
64
+ renderer = Sunrb::OpenGLRenderer.new(width: 800, height: 600, title: "House Model")
65
+
66
+ angle = 0.0
67
+ renderer.run do |delta|
68
+ angle += delta * 0.3
69
+ camera.position.x = Math.cos(angle) * 10
70
+ camera.position.z = Math.sin(angle) * 10
71
+ camera.look_at(Sunrb::Vector3.new(0, 1.5, 0))
72
+ renderer.render(scene, camera)
73
+ end
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Simple Game Prototype - Player, enemies, and collision detection
5
+
6
+ require_relative "../lib/3rb"
7
+
8
+ class GameObject
9
+ attr_accessor :mesh, :velocity, :radius
10
+
11
+ def initialize(mesh:, radius: 0.5)
12
+ @mesh = mesh
13
+ @velocity = Sunrb::Vector3.new
14
+ @radius = radius
15
+ end
16
+
17
+ def position
18
+ @mesh.position
19
+ end
20
+
21
+ def update(delta)
22
+ position.x += velocity.x * delta
23
+ position.z += velocity.z * delta
24
+ end
25
+
26
+ def collides_with?(other)
27
+ dx = position.x - other.position.x
28
+ dz = position.z - other.position.z
29
+ distance = Math.sqrt(dx * dx + dz * dz)
30
+ distance < (@radius + other.radius)
31
+ end
32
+ end
33
+
34
+ scene = Sunrb::Scene.new
35
+ scene.background = Sunrb::Color.new(0x1a1a2e)
36
+
37
+ camera = Sunrb::PerspectiveCamera.new(fov: 60, aspect: 800.0 / 600.0, near: 0.1, far: 1000)
38
+ camera.position.set(0, 15, 15)
39
+ camera.look_at(Sunrb::Vector3.new(0, 0, 0))
40
+
41
+ ambient = Sunrb::AmbientLight.new(color: 0x404040)
42
+ scene.add(ambient)
43
+
44
+ sun = Sunrb::DirectionalLight.new(color: 0xffffff, intensity: 0.8)
45
+ sun.position.set(10, 20, 10)
46
+ scene.add(sun)
47
+
48
+ floor_geo = Sunrb::PlaneGeometry.new(width: 20, height: 20)
49
+ floor_mat = Sunrb::MeshBasicMaterial.new(color: 0x2c3e50)
50
+ floor = Sunrb::Mesh.new(floor_geo, floor_mat)
51
+ floor.rotation.x = -Math::PI / 2
52
+ scene.add(floor)
53
+
54
+ player_geo = Sunrb::BoxGeometry.new(width: 1, height: 1, depth: 1)
55
+ player_mat = Sunrb::MeshBasicMaterial.new(color: 0x3498db)
56
+ player_mesh = Sunrb::Mesh.new(player_geo, player_mat)
57
+ player_mesh.position.set(0, 0.5, 0)
58
+ scene.add(player_mesh)
59
+ player = GameObject.new(mesh: player_mesh, radius: 0.7)
60
+
61
+ enemies = []
62
+ 5.times do |i|
63
+ enemy_geo = Sunrb::SphereGeometry.new(radius: 0.5)
64
+ enemy_mat = Sunrb::MeshBasicMaterial.new(color: 0xe74c3c)
65
+ enemy_mesh = Sunrb::Mesh.new(enemy_geo, enemy_mat)
66
+
67
+ angle = (i.to_f / 5) * Math::PI * 2
68
+ radius = 5 + rand * 3
69
+ enemy_mesh.position.set(Math.cos(angle) * radius, 0.5, Math.sin(angle) * radius)
70
+ scene.add(enemy_mesh)
71
+
72
+ enemy = GameObject.new(mesh: enemy_mesh, radius: 0.5)
73
+ enemy.velocity.set((rand - 0.5) * 4, 0, (rand - 0.5) * 4)
74
+ enemies << enemy
75
+ end
76
+
77
+ collectibles = []
78
+ 8.times do
79
+ gem_geo = Sunrb::BoxGeometry.new(width: 0.4, height: 0.4, depth: 0.4)
80
+ gem_mat = Sunrb::MeshBasicMaterial.new(color: 0xf1c40f)
81
+ gem_mesh = Sunrb::Mesh.new(gem_geo, gem_mat)
82
+ gem_mesh.position.set((rand - 0.5) * 16, 0.5, (rand - 0.5) * 16)
83
+ scene.add(gem_mesh)
84
+ collectibles << GameObject.new(mesh: gem_mesh, radius: 0.3)
85
+ end
86
+
87
+ score = 0
88
+ game_over = false
89
+
90
+ puts "=== Simple Game Prototype ==="
91
+ puts "Enemies: #{enemies.length}, Collectibles: #{collectibles.length}"
92
+ puts "Press ESC to exit"
93
+
94
+ renderer = Sunrb::OpenGLRenderer.new(width: 800, height: 600, title: "Game Prototype")
95
+
96
+ time = 0.0
97
+ renderer.run do |delta|
98
+ time += delta
99
+
100
+ unless game_over
101
+ player.mesh.position.x = Math.sin(time) * 3
102
+ player.mesh.position.z = Math.cos(time * 0.7) * 3
103
+
104
+ enemies.each do |enemy|
105
+ enemy.update(delta)
106
+
107
+ if enemy.position.x.abs > 9
108
+ enemy.velocity.x *= -1
109
+ enemy.position.x = enemy.position.x.positive? ? 9 : -9
110
+ end
111
+ if enemy.position.z.abs > 9
112
+ enemy.velocity.z *= -1
113
+ enemy.position.z = enemy.position.z.positive? ? 9 : -9
114
+ end
115
+
116
+ enemy.mesh.rotation.y += delta * 2
117
+
118
+ if player.collides_with?(enemy)
119
+ game_over = true
120
+ puts "GAME OVER! Final score: #{score}"
121
+ end
122
+ end
123
+
124
+ collectibles.reject! do |gem|
125
+ gem.mesh.rotation.y += delta * 3
126
+ gem.mesh.rotation.x += delta * 2
127
+
128
+ if player.collides_with?(gem)
129
+ scene.remove(gem.mesh)
130
+ score += 100
131
+ puts "Collected! Score: #{score}"
132
+ true
133
+ else
134
+ false
135
+ end
136
+ end
137
+
138
+ if collectibles.empty? && !game_over
139
+ puts "YOU WIN! Final score: #{score}"
140
+ game_over = true
141
+ end
142
+ end
143
+
144
+ renderer.render(scene, camera)
145
+ end