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.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/3rb.gemspec +29 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE +21 -0
- data/README.md +321 -0
- data/Rakefile +13 -0
- data/examples/01_hello_cube.rb +29 -0
- data/examples/02_basic_geometries.rb +56 -0
- data/examples/03_materials.rb +61 -0
- data/examples/04_lighting.rb +63 -0
- data/examples/05_animation.rb +79 -0
- data/examples/06_custom_shader.rb +92 -0
- data/examples/07_scene_graph.rb +74 -0
- data/examples/08_orbit_controls.rb +50 -0
- data/examples/09_3d_chart.rb +71 -0
- data/examples/10_procedural_terrain.rb +140 -0
- data/examples/11_particle_system.rb +68 -0
- data/examples/12_model_loader.rb +73 -0
- data/examples/13_game_prototype.rb +145 -0
- data/examples/14_utah_teapot.rb +291 -0
- data/examples/15_stanford_bunny.rb +200 -0
- data/examples/16_cornell_box.rb +373 -0
- data/examples/17_weird_fractal4.rb +130 -0
- data/examples/18_platonic_solids.rb +268 -0
- data/lib/3rb/animation/animation_clip.rb +287 -0
- data/lib/3rb/animation/animation_mixer.rb +366 -0
- data/lib/3rb/cameras/camera.rb +50 -0
- data/lib/3rb/cameras/orthographic_camera.rb +92 -0
- data/lib/3rb/cameras/perspective_camera.rb +103 -0
- data/lib/3rb/controls/orbit_controls.rb +341 -0
- data/lib/3rb/core/buffer_attribute.rb +172 -0
- data/lib/3rb/core/group.rb +9 -0
- data/lib/3rb/core/object3d.rb +298 -0
- data/lib/3rb/core/scene.rb +78 -0
- data/lib/3rb/dsl/helpers.rb +57 -0
- data/lib/3rb/dsl/scene_builder.rb +288 -0
- data/lib/3rb/ffi/glfw.rb +61 -0
- data/lib/3rb/ffi/opengl.rb +137 -0
- data/lib/3rb/ffi/platform.rb +65 -0
- data/lib/3rb/geometries/box_geometry.rb +101 -0
- data/lib/3rb/geometries/buffer_geometry.rb +345 -0
- data/lib/3rb/geometries/cone_geometry.rb +29 -0
- data/lib/3rb/geometries/cylinder_geometry.rb +149 -0
- data/lib/3rb/geometries/plane_geometry.rb +75 -0
- data/lib/3rb/geometries/sphere_geometry.rb +93 -0
- data/lib/3rb/geometries/torus_geometry.rb +77 -0
- data/lib/3rb/lights/ambient_light.rb +9 -0
- data/lib/3rb/lights/directional_light.rb +57 -0
- data/lib/3rb/lights/hemisphere_light.rb +26 -0
- data/lib/3rb/lights/light.rb +27 -0
- data/lib/3rb/lights/point_light.rb +68 -0
- data/lib/3rb/lights/rect_area_light.rb +35 -0
- data/lib/3rb/lights/spot_light.rb +88 -0
- data/lib/3rb/loaders/gltf_loader.rb +304 -0
- data/lib/3rb/loaders/loader.rb +94 -0
- data/lib/3rb/loaders/obj_loader.rb +186 -0
- data/lib/3rb/loaders/texture_loader.rb +55 -0
- data/lib/3rb/materials/basic_material.rb +70 -0
- data/lib/3rb/materials/lambert_material.rb +102 -0
- data/lib/3rb/materials/material.rb +114 -0
- data/lib/3rb/materials/phong_material.rb +106 -0
- data/lib/3rb/materials/shader_material.rb +104 -0
- data/lib/3rb/materials/standard_material.rb +106 -0
- data/lib/3rb/math/color.rb +246 -0
- data/lib/3rb/math/euler.rb +156 -0
- data/lib/3rb/math/math_utils.rb +132 -0
- data/lib/3rb/math/matrix3.rb +269 -0
- data/lib/3rb/math/matrix4.rb +501 -0
- data/lib/3rb/math/quaternion.rb +337 -0
- data/lib/3rb/math/vector2.rb +216 -0
- data/lib/3rb/math/vector3.rb +366 -0
- data/lib/3rb/math/vector4.rb +233 -0
- data/lib/3rb/native/gl.rb +382 -0
- data/lib/3rb/native/native.rb +55 -0
- data/lib/3rb/native/window.rb +111 -0
- data/lib/3rb/native.rb +9 -0
- data/lib/3rb/objects/line.rb +116 -0
- data/lib/3rb/objects/mesh.rb +40 -0
- data/lib/3rb/objects/points.rb +71 -0
- data/lib/3rb/renderers/opengl_renderer.rb +567 -0
- data/lib/3rb/renderers/renderer.rb +60 -0
- data/lib/3rb/renderers/shader_lib.rb +100 -0
- data/lib/3rb/textures/cube_texture.rb +26 -0
- data/lib/3rb/textures/data_texture.rb +35 -0
- data/lib/3rb/textures/render_target.rb +125 -0
- data/lib/3rb/textures/texture.rb +190 -0
- data/lib/3rb/version.rb +5 -0
- data/lib/3rb.rb +86 -0
- data/shaders/basic.frag +19 -0
- data/shaders/basic.vert +15 -0
- data/shaders/common/lights.glsl +53 -0
- data/shaders/common/uniforms.glsl +9 -0
- data/shaders/lambert.frag +37 -0
- data/shaders/lambert.vert +22 -0
- data/shaders/phong.frag +51 -0
- data/shaders/phong.vert +28 -0
- data/shaders/standard.frag +92 -0
- data/shaders/standard.vert +28 -0
- 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
|