arcanoae 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/CHANGELOG.md +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +128 -0
- data/arcanoae.gemspec +31 -0
- data/examples/01_hello_world.rb +47 -0
- data/examples/02_basic_shapes.rb +58 -0
- data/examples/03_materials.rb +69 -0
- data/examples/04_textures.rb +48 -0
- data/examples/05_lighting.rb +66 -0
- data/examples/06_camera_controls.rb +49 -0
- data/examples/07_animation.rb +83 -0
- data/examples/08_particles.rb +86 -0
- data/examples/09_physics.rb +66 -0
- data/examples/10_post_processing.rb +60 -0
- data/examples/11_gui.rb +85 -0
- data/examples/12_load_model.rb +73 -0
- data/examples/13_pbr_demo.rb +98 -0
- data/examples/14_complete_game.rb +159 -0
- data/examples/15_spectral_cornell_box.rb +350 -0
- data/examples/16_ward_brdf_teapot.rb +321 -0
- data/examples/17_terrain_flight.rb +263 -0
- data/examples/18_cellular_automata.rb +417 -0
- data/lib/arcanoae/animation/animatable.rb +108 -0
- data/lib/arcanoae/animation/animation.rb +110 -0
- data/lib/arcanoae/animation/animation_group.rb +159 -0
- data/lib/arcanoae/animation/easing_functions.rb +214 -0
- data/lib/arcanoae/audio/audio_engine.rb +85 -0
- data/lib/arcanoae/audio/sound.rb +135 -0
- data/lib/arcanoae/audio/sound_track.rb +53 -0
- data/lib/arcanoae/backends/glfw_bindings.rb +99 -0
- data/lib/arcanoae/backends/opengl_bindings.rb +240 -0
- data/lib/arcanoae/cameras/arc_rotate_camera.rb +134 -0
- data/lib/arcanoae/cameras/camera.rb +52 -0
- data/lib/arcanoae/cameras/perspective_camera.rb +35 -0
- data/lib/arcanoae/core/disposable.rb +25 -0
- data/lib/arcanoae/core/engine.rb +163 -0
- data/lib/arcanoae/core/logger.rb +51 -0
- data/lib/arcanoae/dsl/scene_builder.rb +238 -0
- data/lib/arcanoae/gui/container.rb +50 -0
- data/lib/arcanoae/gui/control.rb +91 -0
- data/lib/arcanoae/gui/controls.rb +195 -0
- data/lib/arcanoae/input/input_manager.rb +172 -0
- data/lib/arcanoae/lights/directional_light.rb +24 -0
- data/lib/arcanoae/lights/hemispheric_light.rb +26 -0
- data/lib/arcanoae/lights/light.rb +30 -0
- data/lib/arcanoae/lights/point_light.rb +23 -0
- data/lib/arcanoae/lights/shadow_generator.rb +151 -0
- data/lib/arcanoae/loaders/gltf_loader.rb +223 -0
- data/lib/arcanoae/loaders/obj_loader.rb +179 -0
- data/lib/arcanoae/materials/material.rb +67 -0
- data/lib/arcanoae/materials/pbr_material.rb +75 -0
- data/lib/arcanoae/materials/standard_material.rb +38 -0
- data/lib/arcanoae/math/bounding_box.rb +98 -0
- data/lib/arcanoae/math/bounding_sphere.rb +52 -0
- data/lib/arcanoae/math/color3.rb +183 -0
- data/lib/arcanoae/math/color4.rb +143 -0
- data/lib/arcanoae/math/frustum.rb +101 -0
- data/lib/arcanoae/math/matrix4.rb +357 -0
- data/lib/arcanoae/math/plane.rb +51 -0
- data/lib/arcanoae/math/quaternion.rb +247 -0
- data/lib/arcanoae/math/ray.rb +78 -0
- data/lib/arcanoae/math/vector2.rb +159 -0
- data/lib/arcanoae/math/vector3.rb +173 -0
- data/lib/arcanoae/math/vector4.rb +132 -0
- data/lib/arcanoae/math.rb +45 -0
- data/lib/arcanoae/meshes/mesh.rb +101 -0
- data/lib/arcanoae/meshes/mesh_builder.rb +286 -0
- data/lib/arcanoae/meshes/vertex_data.rb +166 -0
- data/lib/arcanoae/physics/physics_engine.rb +145 -0
- data/lib/arcanoae/physics/physics_impostor.rb +84 -0
- data/lib/arcanoae/post_process/effects/fxaa_post_process.rb +112 -0
- data/lib/arcanoae/post_process/post_process.rb +68 -0
- data/lib/arcanoae/post_process/post_process_manager.rb +62 -0
- data/lib/arcanoae/rendering/gpu_buffer.rb +70 -0
- data/lib/arcanoae/rendering/mesh_renderer.rb +144 -0
- data/lib/arcanoae/rendering/render_pipeline.rb +98 -0
- data/lib/arcanoae/rendering/shader_program.rb +114 -0
- data/lib/arcanoae/rendering/shader_store.rb +226 -0
- data/lib/arcanoae/scene/scene.rb +131 -0
- data/lib/arcanoae/scene/transform_node.rb +185 -0
- data/lib/arcanoae/textures/base_texture.rb +48 -0
- data/lib/arcanoae/textures/render_target_texture.rb +73 -0
- data/lib/arcanoae/textures/texture.rb +90 -0
- data/lib/arcanoae/version.rb +5 -0
- data/lib/arcanoae.rb +89 -0
- data/shaders/blur.frag +29 -0
- data/shaders/default.frag +30 -0
- data/shaders/default.vert +23 -0
- data/shaders/fxaa.frag +55 -0
- data/shaders/particles.frag +25 -0
- data/shaders/particles.vert +33 -0
- data/shaders/pbr.frag +176 -0
- data/shaders/pbr.vert +31 -0
- data/shaders/post_process.vert +11 -0
- data/shaders/shadow_map.frag +4 -0
- data/shaders/shadow_map.vert +10 -0
- data/shaders/standard.frag +99 -0
- data/shaders/standard.vert +29 -0
- metadata +184 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arcanoae
|
|
4
|
+
module Audio
|
|
5
|
+
class Sound
|
|
6
|
+
include Arcanoae::Disposable
|
|
7
|
+
|
|
8
|
+
attr_accessor :name, :loop, :volume, :playback_rate
|
|
9
|
+
attr_accessor :spatial_sound
|
|
10
|
+
attr_reader :url, :scene, :attached_mesh
|
|
11
|
+
|
|
12
|
+
def initialize(name, url, scene, options = {})
|
|
13
|
+
@name = name
|
|
14
|
+
@url = url
|
|
15
|
+
@scene = scene
|
|
16
|
+
|
|
17
|
+
@loop = options.fetch(:loop, false)
|
|
18
|
+
@volume = options.fetch(:volume, 1.0)
|
|
19
|
+
@playback_rate = options.fetch(:playback_rate, 1.0)
|
|
20
|
+
@spatial_sound = options.fetch(:spatial_sound, false)
|
|
21
|
+
@autoplay = options.fetch(:autoplay, false)
|
|
22
|
+
|
|
23
|
+
@is_playing = false
|
|
24
|
+
@is_paused = false
|
|
25
|
+
@is_ready = false
|
|
26
|
+
@attached_mesh = nil
|
|
27
|
+
@position = Math::Vector3.zero
|
|
28
|
+
|
|
29
|
+
@on_ready_callbacks = []
|
|
30
|
+
@on_ended_callbacks = []
|
|
31
|
+
|
|
32
|
+
load_sound(url) if url
|
|
33
|
+
|
|
34
|
+
scene.audio_engine&.add_sound(self)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def play(time = 0, offset = 0)
|
|
38
|
+
return unless @is_ready
|
|
39
|
+
|
|
40
|
+
@is_playing = true
|
|
41
|
+
@is_paused = false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def pause
|
|
45
|
+
return unless @is_playing
|
|
46
|
+
|
|
47
|
+
@is_paused = true
|
|
48
|
+
@is_playing = false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def stop
|
|
52
|
+
@is_playing = false
|
|
53
|
+
@is_paused = false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def resume
|
|
57
|
+
return unless @is_paused
|
|
58
|
+
|
|
59
|
+
@is_paused = false
|
|
60
|
+
@is_playing = true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def playing?
|
|
64
|
+
@is_playing && !@is_paused
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def paused?
|
|
68
|
+
@is_paused
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def ready?
|
|
72
|
+
@is_ready
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def set_position(position)
|
|
76
|
+
@position = position
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def get_position
|
|
80
|
+
@position
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def attach_to_mesh(mesh)
|
|
84
|
+
@attached_mesh = mesh
|
|
85
|
+
@spatial_sound = true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def detach_from_mesh
|
|
89
|
+
@attached_mesh = nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def update(delta_time)
|
|
93
|
+
if @attached_mesh && @spatial_sound
|
|
94
|
+
@position = @attached_mesh.absolute_position
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def update_volume
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def on_ready(&block)
|
|
102
|
+
@on_ready_callbacks << block
|
|
103
|
+
block.call if @is_ready
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def on_ended(&block)
|
|
107
|
+
@on_ended_callbacks << block
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def dispose
|
|
111
|
+
return if disposed?
|
|
112
|
+
|
|
113
|
+
stop
|
|
114
|
+
@scene.audio_engine&.remove_sound(self)
|
|
115
|
+
@on_ready_callbacks.clear
|
|
116
|
+
@on_ended_callbacks.clear
|
|
117
|
+
|
|
118
|
+
super
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def load_sound(url)
|
|
124
|
+
return unless File.exist?(url)
|
|
125
|
+
|
|
126
|
+
@is_ready = true
|
|
127
|
+
@on_ready_callbacks.each(&:call)
|
|
128
|
+
|
|
129
|
+
play if @autoplay
|
|
130
|
+
|
|
131
|
+
Arcanoae::Logger.debug("Sound loaded: #{url}")
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arcanoae
|
|
4
|
+
module Audio
|
|
5
|
+
class SoundTrack
|
|
6
|
+
include Arcanoae::Disposable
|
|
7
|
+
|
|
8
|
+
attr_accessor :name, :volume
|
|
9
|
+
attr_reader :sounds
|
|
10
|
+
|
|
11
|
+
def initialize(name, scene, options = {})
|
|
12
|
+
@name = name
|
|
13
|
+
@scene = scene
|
|
14
|
+
@volume = options.fetch(:volume, 1.0)
|
|
15
|
+
@sounds = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add_sound(sound)
|
|
19
|
+
@sounds << sound unless @sounds.include?(sound)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def remove_sound(sound)
|
|
23
|
+
@sounds.delete(sound)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def play
|
|
27
|
+
@sounds.each(&:play)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def pause
|
|
31
|
+
@sounds.each(&:pause)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def stop
|
|
35
|
+
@sounds.each(&:stop)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def set_volume(volume)
|
|
39
|
+
@volume = Arcanoae::Math.clamp(volume, 0.0, 1.0)
|
|
40
|
+
@sounds.each(&:update_volume)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def dispose
|
|
44
|
+
return if disposed?
|
|
45
|
+
|
|
46
|
+
@sounds.each(&:dispose)
|
|
47
|
+
@sounds.clear
|
|
48
|
+
|
|
49
|
+
super
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ffi'
|
|
4
|
+
|
|
5
|
+
module Arcanoae
|
|
6
|
+
module Backends
|
|
7
|
+
module GLFW
|
|
8
|
+
extend FFI::Library
|
|
9
|
+
|
|
10
|
+
case RbConfig::CONFIG['host_os']
|
|
11
|
+
when /darwin/
|
|
12
|
+
ffi_lib '/opt/homebrew/lib/libglfw.dylib'
|
|
13
|
+
when /linux/
|
|
14
|
+
ffi_lib 'libglfw.so.3'
|
|
15
|
+
when /mingw|mswin/
|
|
16
|
+
ffi_lib 'glfw3.dll'
|
|
17
|
+
else
|
|
18
|
+
ffi_lib 'glfw'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
TRUE = 1
|
|
22
|
+
FALSE = 0
|
|
23
|
+
|
|
24
|
+
RELEASE = 0
|
|
25
|
+
PRESS = 1
|
|
26
|
+
REPEAT = 2
|
|
27
|
+
|
|
28
|
+
CONTEXT_VERSION_MAJOR = 0x00022002
|
|
29
|
+
CONTEXT_VERSION_MINOR = 0x00022003
|
|
30
|
+
OPENGL_PROFILE = 0x00022008
|
|
31
|
+
OPENGL_FORWARD_COMPAT = 0x00022006
|
|
32
|
+
OPENGL_CORE_PROFILE = 0x00032001
|
|
33
|
+
SAMPLES = 0x0002100D
|
|
34
|
+
|
|
35
|
+
KEY_ESCAPE = 256
|
|
36
|
+
KEY_SPACE = 32
|
|
37
|
+
KEY_0 = 48
|
|
38
|
+
KEY_1 = 49
|
|
39
|
+
KEY_2 = 50
|
|
40
|
+
KEY_3 = 51
|
|
41
|
+
KEY_4 = 52
|
|
42
|
+
KEY_5 = 53
|
|
43
|
+
KEY_6 = 54
|
|
44
|
+
KEY_7 = 55
|
|
45
|
+
KEY_8 = 56
|
|
46
|
+
KEY_9 = 57
|
|
47
|
+
KEY_W = 87
|
|
48
|
+
KEY_A = 65
|
|
49
|
+
KEY_S = 83
|
|
50
|
+
KEY_D = 68
|
|
51
|
+
KEY_UP = 265
|
|
52
|
+
KEY_DOWN = 264
|
|
53
|
+
KEY_LEFT = 263
|
|
54
|
+
KEY_RIGHT = 262
|
|
55
|
+
|
|
56
|
+
attach_function :glfwInit, [], :int
|
|
57
|
+
attach_function :glfwTerminate, [], :void
|
|
58
|
+
attach_function :glfwWindowHint, [:int, :int], :void
|
|
59
|
+
attach_function :glfwCreateWindow, [:int, :int, :string, :pointer, :pointer], :pointer
|
|
60
|
+
attach_function :glfwDestroyWindow, [:pointer], :void
|
|
61
|
+
attach_function :glfwMakeContextCurrent, [:pointer], :void
|
|
62
|
+
attach_function :glfwSwapBuffers, [:pointer], :void
|
|
63
|
+
attach_function :glfwSwapInterval, [:int], :void
|
|
64
|
+
attach_function :glfwPollEvents, [], :void
|
|
65
|
+
attach_function :glfwWindowShouldClose, [:pointer], :int
|
|
66
|
+
attach_function :glfwSetWindowShouldClose, [:pointer, :int], :void
|
|
67
|
+
attach_function :glfwGetTime, [], :double
|
|
68
|
+
attach_function :glfwGetFramebufferSize, [:pointer, :pointer, :pointer], :void
|
|
69
|
+
attach_function :glfwGetKey, [:pointer, :int], :int
|
|
70
|
+
attach_function :glfwGetMouseButton, [:pointer, :int], :int
|
|
71
|
+
attach_function :glfwGetCursorPos, [:pointer, :pointer, :pointer], :void
|
|
72
|
+
attach_function :glfwSetInputMode, [:pointer, :int, :int], :void
|
|
73
|
+
|
|
74
|
+
callback :key_callback, [:pointer, :int, :int, :int, :int], :void
|
|
75
|
+
callback :cursor_pos_callback, [:pointer, :double, :double], :void
|
|
76
|
+
callback :mouse_button_callback, [:pointer, :int, :int, :int], :void
|
|
77
|
+
callback :framebuffer_size_callback, [:pointer, :int, :int], :void
|
|
78
|
+
|
|
79
|
+
attach_function :glfwSetKeyCallback, [:pointer, :key_callback], :pointer
|
|
80
|
+
attach_function :glfwSetCursorPosCallback, [:pointer, :cursor_pos_callback], :pointer
|
|
81
|
+
attach_function :glfwSetMouseButtonCallback, [:pointer, :mouse_button_callback], :pointer
|
|
82
|
+
attach_function :glfwSetFramebufferSizeCallback, [:pointer, :framebuffer_size_callback], :pointer
|
|
83
|
+
|
|
84
|
+
def self.get_framebuffer_size(window)
|
|
85
|
+
width_ptr = FFI::MemoryPointer.new(:int)
|
|
86
|
+
height_ptr = FFI::MemoryPointer.new(:int)
|
|
87
|
+
glfwGetFramebufferSize(window, width_ptr, height_ptr)
|
|
88
|
+
[width_ptr.read_int, height_ptr.read_int]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.get_cursor_pos(window)
|
|
92
|
+
x_ptr = FFI::MemoryPointer.new(:double)
|
|
93
|
+
y_ptr = FFI::MemoryPointer.new(:double)
|
|
94
|
+
glfwGetCursorPos(window, x_ptr, y_ptr)
|
|
95
|
+
[x_ptr.read_double, y_ptr.read_double]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ffi'
|
|
4
|
+
|
|
5
|
+
module Arcanoae
|
|
6
|
+
module Backends
|
|
7
|
+
module GL
|
|
8
|
+
extend FFI::Library
|
|
9
|
+
|
|
10
|
+
case RbConfig::CONFIG['host_os']
|
|
11
|
+
when /darwin/
|
|
12
|
+
ffi_lib '/System/Library/Frameworks/OpenGL.framework/OpenGL'
|
|
13
|
+
when /linux/
|
|
14
|
+
ffi_lib 'libGL.so.1'
|
|
15
|
+
when /mingw|mswin/
|
|
16
|
+
ffi_lib 'opengl32.dll'
|
|
17
|
+
else
|
|
18
|
+
ffi_lib 'GL'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
GL_FALSE = 0
|
|
22
|
+
GL_TRUE = 1
|
|
23
|
+
|
|
24
|
+
GL_DEPTH_BUFFER_BIT = 0x00000100
|
|
25
|
+
GL_COLOR_BUFFER_BIT = 0x00004000
|
|
26
|
+
|
|
27
|
+
GL_POINTS = 0x0000
|
|
28
|
+
GL_LINES = 0x0001
|
|
29
|
+
GL_TRIANGLES = 0x0004
|
|
30
|
+
|
|
31
|
+
GL_DEPTH_TEST = 0x0B71
|
|
32
|
+
GL_CULL_FACE = 0x0B44
|
|
33
|
+
GL_BLEND = 0x0BE2
|
|
34
|
+
GL_MULTISAMPLE = 0x809D
|
|
35
|
+
|
|
36
|
+
GL_FRONT = 0x0404
|
|
37
|
+
GL_BACK = 0x0405
|
|
38
|
+
|
|
39
|
+
GL_SRC_ALPHA = 0x0302
|
|
40
|
+
GL_ONE_MINUS_SRC_ALPHA = 0x0303
|
|
41
|
+
|
|
42
|
+
GL_FLOAT = 0x1406
|
|
43
|
+
GL_UNSIGNED_INT = 0x1405
|
|
44
|
+
|
|
45
|
+
GL_ARRAY_BUFFER = 0x8892
|
|
46
|
+
GL_ELEMENT_ARRAY_BUFFER = 0x8893
|
|
47
|
+
GL_STATIC_DRAW = 0x88E4
|
|
48
|
+
GL_DYNAMIC_DRAW = 0x88E8
|
|
49
|
+
|
|
50
|
+
GL_FRAGMENT_SHADER = 0x8B30
|
|
51
|
+
GL_VERTEX_SHADER = 0x8B31
|
|
52
|
+
GL_COMPILE_STATUS = 0x8B81
|
|
53
|
+
GL_LINK_STATUS = 0x8B82
|
|
54
|
+
GL_INFO_LOG_LENGTH = 0x8B84
|
|
55
|
+
|
|
56
|
+
attach_function :glEnable, [:uint], :void
|
|
57
|
+
attach_function :glDisable, [:uint], :void
|
|
58
|
+
attach_function :glClear, [:uint], :void
|
|
59
|
+
attach_function :glClearColor, [:float, :float, :float, :float], :void
|
|
60
|
+
attach_function :glViewport, [:int, :int, :int, :int], :void
|
|
61
|
+
attach_function :glCullFace, [:uint], :void
|
|
62
|
+
attach_function :glBlendFunc, [:uint, :uint], :void
|
|
63
|
+
|
|
64
|
+
attach_function :glGenBuffers, [:int, :pointer], :void
|
|
65
|
+
attach_function :glDeleteBuffers, [:int, :pointer], :void
|
|
66
|
+
attach_function :glBindBuffer, [:uint, :uint], :void
|
|
67
|
+
attach_function :glBufferData, [:uint, :long, :pointer, :uint], :void
|
|
68
|
+
|
|
69
|
+
attach_function :glGenVertexArrays, [:int, :pointer], :void
|
|
70
|
+
attach_function :glDeleteVertexArrays, [:int, :pointer], :void
|
|
71
|
+
attach_function :glBindVertexArray, [:uint], :void
|
|
72
|
+
attach_function :glEnableVertexAttribArray, [:uint], :void
|
|
73
|
+
attach_function :glVertexAttribPointer, [:uint, :int, :uint, :uchar, :int, :pointer], :void
|
|
74
|
+
|
|
75
|
+
attach_function :glCreateShader, [:uint], :uint
|
|
76
|
+
attach_function :glDeleteShader, [:uint], :void
|
|
77
|
+
attach_function :glShaderSource, [:uint, :int, :pointer, :pointer], :void
|
|
78
|
+
attach_function :glCompileShader, [:uint], :void
|
|
79
|
+
attach_function :glGetShaderiv, [:uint, :uint, :pointer], :void
|
|
80
|
+
attach_function :glGetShaderInfoLog, [:uint, :int, :pointer, :pointer], :void
|
|
81
|
+
|
|
82
|
+
attach_function :glCreateProgram, [], :uint
|
|
83
|
+
attach_function :glDeleteProgram, [:uint], :void
|
|
84
|
+
attach_function :glAttachShader, [:uint, :uint], :void
|
|
85
|
+
attach_function :glLinkProgram, [:uint], :void
|
|
86
|
+
attach_function :glUseProgram, [:uint], :void
|
|
87
|
+
attach_function :glGetProgramiv, [:uint, :uint, :pointer], :void
|
|
88
|
+
attach_function :glGetProgramInfoLog, [:uint, :int, :pointer, :pointer], :void
|
|
89
|
+
|
|
90
|
+
attach_function :glGetUniformLocation, [:uint, :string], :int
|
|
91
|
+
attach_function :glGetAttribLocation, [:uint, :string], :int
|
|
92
|
+
attach_function :glUniform1i, [:int, :int], :void
|
|
93
|
+
attach_function :glUniform1f, [:int, :float], :void
|
|
94
|
+
attach_function :glUniform2f, [:int, :float, :float], :void
|
|
95
|
+
attach_function :glUniform3f, [:int, :float, :float, :float], :void
|
|
96
|
+
attach_function :glUniform4f, [:int, :float, :float, :float, :float], :void
|
|
97
|
+
attach_function :glUniformMatrix4fv, [:int, :int, :uchar, :pointer], :void
|
|
98
|
+
|
|
99
|
+
attach_function :glDrawArrays, [:uint, :int, :int], :void
|
|
100
|
+
attach_function :glDrawElements, [:uint, :int, :uint, :pointer], :void
|
|
101
|
+
attach_function :glGetError, [], :uint
|
|
102
|
+
|
|
103
|
+
GL_NO_ERROR = 0
|
|
104
|
+
GL_INVALID_ENUM = 0x0500
|
|
105
|
+
GL_INVALID_VALUE = 0x0501
|
|
106
|
+
GL_INVALID_OPERATION = 0x0502
|
|
107
|
+
GL_OUT_OF_MEMORY = 0x0505
|
|
108
|
+
|
|
109
|
+
GL_TEXTURE_2D = 0x0DE1
|
|
110
|
+
GL_TEXTURE0 = 0x84C0
|
|
111
|
+
GL_TEXTURE_MIN_FILTER = 0x2801
|
|
112
|
+
GL_TEXTURE_MAG_FILTER = 0x2800
|
|
113
|
+
GL_TEXTURE_WRAP_S = 0x2802
|
|
114
|
+
GL_TEXTURE_WRAP_T = 0x2803
|
|
115
|
+
GL_NEAREST = 0x2600
|
|
116
|
+
GL_LINEAR = 0x2601
|
|
117
|
+
GL_REPEAT = 0x2901
|
|
118
|
+
GL_CLAMP_TO_EDGE = 0x812F
|
|
119
|
+
|
|
120
|
+
GL_RGBA = 0x1908
|
|
121
|
+
GL_RGBA32F = 0x8814
|
|
122
|
+
|
|
123
|
+
GL_FRAMEBUFFER = 0x8D40
|
|
124
|
+
GL_COLOR_ATTACHMENT0 = 0x8CE0
|
|
125
|
+
GL_FRAMEBUFFER_COMPLETE = 0x8CD5
|
|
126
|
+
|
|
127
|
+
attach_function :glGenTextures, [:int, :pointer], :void
|
|
128
|
+
attach_function :glDeleteTextures, [:int, :pointer], :void
|
|
129
|
+
attach_function :glBindTexture, [:uint, :uint], :void
|
|
130
|
+
attach_function :glTexImage2D, [:uint, :int, :int, :int, :int, :int, :uint, :uint, :pointer], :void
|
|
131
|
+
attach_function :glTexParameteri, [:uint, :uint, :int], :void
|
|
132
|
+
attach_function :glActiveTexture, [:uint], :void
|
|
133
|
+
|
|
134
|
+
attach_function :glGenFramebuffers, [:int, :pointer], :void
|
|
135
|
+
attach_function :glDeleteFramebuffers, [:int, :pointer], :void
|
|
136
|
+
attach_function :glBindFramebuffer, [:uint, :uint], :void
|
|
137
|
+
attach_function :glFramebufferTexture2D, [:uint, :uint, :uint, :uint, :int], :void
|
|
138
|
+
attach_function :glCheckFramebufferStatus, [:uint], :uint
|
|
139
|
+
|
|
140
|
+
def self.check_error(context = "")
|
|
141
|
+
error = glGetError
|
|
142
|
+
return if error == GL_NO_ERROR
|
|
143
|
+
|
|
144
|
+
error_name = case error
|
|
145
|
+
when GL_INVALID_ENUM then "GL_INVALID_ENUM"
|
|
146
|
+
when GL_INVALID_VALUE then "GL_INVALID_VALUE"
|
|
147
|
+
when GL_INVALID_OPERATION then "GL_INVALID_OPERATION"
|
|
148
|
+
when GL_OUT_OF_MEMORY then "GL_OUT_OF_MEMORY"
|
|
149
|
+
else "Unknown error #{error}"
|
|
150
|
+
end
|
|
151
|
+
puts "OpenGL Error [#{context}]: #{error_name}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.gen_buffer
|
|
155
|
+
buf = FFI::MemoryPointer.new(:uint)
|
|
156
|
+
glGenBuffers(1, buf)
|
|
157
|
+
buf.read_uint
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def self.gen_vertex_array
|
|
161
|
+
buf = FFI::MemoryPointer.new(:uint)
|
|
162
|
+
glGenVertexArrays(1, buf)
|
|
163
|
+
buf.read_uint
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def self.delete_buffer(id)
|
|
167
|
+
buf = FFI::MemoryPointer.new(:uint)
|
|
168
|
+
buf.write_uint(id)
|
|
169
|
+
glDeleteBuffers(1, buf)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def self.delete_vertex_array(id)
|
|
173
|
+
buf = FFI::MemoryPointer.new(:uint)
|
|
174
|
+
buf.write_uint(id)
|
|
175
|
+
glDeleteVertexArrays(1, buf)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def self.get_shader_iv(shader, pname)
|
|
179
|
+
buf = FFI::MemoryPointer.new(:int)
|
|
180
|
+
glGetShaderiv(shader, pname, buf)
|
|
181
|
+
buf.read_int
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def self.get_program_iv(program, pname)
|
|
185
|
+
buf = FFI::MemoryPointer.new(:int)
|
|
186
|
+
glGetProgramiv(program, pname, buf)
|
|
187
|
+
buf.read_int
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def self.get_shader_info_log(shader)
|
|
191
|
+
len = get_shader_iv(shader, GL_INFO_LOG_LENGTH)
|
|
192
|
+
return "" if len <= 0
|
|
193
|
+
|
|
194
|
+
buf = FFI::MemoryPointer.new(:char, len)
|
|
195
|
+
glGetShaderInfoLog(shader, len, nil, buf)
|
|
196
|
+
buf.read_string
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def self.get_program_info_log(program)
|
|
200
|
+
len = get_program_iv(program, GL_INFO_LOG_LENGTH)
|
|
201
|
+
return "" if len <= 0
|
|
202
|
+
|
|
203
|
+
buf = FFI::MemoryPointer.new(:char, len)
|
|
204
|
+
glGetProgramInfoLog(program, len, nil, buf)
|
|
205
|
+
buf.read_string
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def self.shader_source(shader, source)
|
|
209
|
+
src_ptr = FFI::MemoryPointer.from_string(source)
|
|
210
|
+
src_array = FFI::MemoryPointer.new(:pointer)
|
|
211
|
+
src_array.write_pointer(src_ptr)
|
|
212
|
+
glShaderSource(shader, 1, src_array, nil)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def self.buffer_data(target, data, usage)
|
|
216
|
+
if data.is_a?(Array)
|
|
217
|
+
if data.first.is_a?(Integer)
|
|
218
|
+
packed = data.pack('L*')
|
|
219
|
+
else
|
|
220
|
+
packed = data.pack('f*')
|
|
221
|
+
end
|
|
222
|
+
else
|
|
223
|
+
packed = data
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
ptr = FFI::MemoryPointer.new(:char, packed.bytesize)
|
|
227
|
+
ptr.put_bytes(0, packed)
|
|
228
|
+
glBufferData(target, packed.bytesize, ptr, usage)
|
|
229
|
+
packed.bytesize
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def self.uniform_matrix4fv(location, matrix)
|
|
233
|
+
data = matrix.is_a?(Array) ? matrix : matrix.to_a
|
|
234
|
+
ptr = FFI::MemoryPointer.new(:float, 16)
|
|
235
|
+
ptr.put_array_of_float(0, data)
|
|
236
|
+
glUniformMatrix4fv(location, 1, GL_FALSE, ptr)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arcanoae
|
|
4
|
+
module Cameras
|
|
5
|
+
class ArcRotateCamera < Camera
|
|
6
|
+
attr_accessor :alpha, :beta, :radius, :target
|
|
7
|
+
attr_accessor :lower_alpha_limit, :upper_alpha_limit
|
|
8
|
+
attr_accessor :lower_beta_limit, :upper_beta_limit
|
|
9
|
+
attr_accessor :lower_radius_limit, :upper_radius_limit
|
|
10
|
+
attr_accessor :panning_inertia, :wheel_precision
|
|
11
|
+
|
|
12
|
+
def initialize(name, alpha, beta, radius, target, scene = nil)
|
|
13
|
+
super(name, compute_position(alpha, beta, radius, target), scene)
|
|
14
|
+
@alpha = alpha
|
|
15
|
+
@beta = beta
|
|
16
|
+
@radius = radius
|
|
17
|
+
@target = target.clone
|
|
18
|
+
|
|
19
|
+
@lower_alpha_limit = nil
|
|
20
|
+
@upper_alpha_limit = nil
|
|
21
|
+
@lower_beta_limit = 0.01
|
|
22
|
+
@upper_beta_limit = ::Math::PI - 0.01
|
|
23
|
+
@lower_radius_limit = 0.01
|
|
24
|
+
@upper_radius_limit = nil
|
|
25
|
+
@panning_inertia = 0.9
|
|
26
|
+
@wheel_precision = 3.0
|
|
27
|
+
@fov = ::Math::PI / 4
|
|
28
|
+
@aspect_ratio = 16.0 / 9.0
|
|
29
|
+
|
|
30
|
+
rebuild_position
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_accessor :fov, :aspect_ratio
|
|
34
|
+
|
|
35
|
+
def set_position(position)
|
|
36
|
+
@position = position.clone
|
|
37
|
+
rebuild_angles_and_radius
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def set_target(target)
|
|
42
|
+
@target = target.clone
|
|
43
|
+
rebuild_position
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def zoom_on(meshes)
|
|
48
|
+
return self if meshes.empty?
|
|
49
|
+
|
|
50
|
+
min = Math::Vector3.new(Float::INFINITY, Float::INFINITY, Float::INFINITY)
|
|
51
|
+
max = Math::Vector3.new(-Float::INFINITY, -Float::INFINITY, -Float::INFINITY)
|
|
52
|
+
|
|
53
|
+
meshes.each do |mesh|
|
|
54
|
+
info = mesh.bounding_info
|
|
55
|
+
next unless info
|
|
56
|
+
|
|
57
|
+
box = info[:bounding_box]
|
|
58
|
+
min = Math::Vector3.new([min.x, box.min.x].min, [min.y, box.min.y].min, [min.z, box.min.z].min)
|
|
59
|
+
max = Math::Vector3.new([max.x, box.max.x].max, [max.y, box.max.y].max, [max.z, box.max.z].max)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
center = Math::Vector3.new(
|
|
63
|
+
(min.x + max.x) / 2,
|
|
64
|
+
(min.y + max.y) / 2,
|
|
65
|
+
(min.z + max.z) / 2
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@target = center
|
|
69
|
+
distance = max.distance_to(min)
|
|
70
|
+
@radius = distance / (2 * ::Math.tan(@fov / 2))
|
|
71
|
+
rebuild_position
|
|
72
|
+
self
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def view_matrix
|
|
76
|
+
Math::Matrix4.look_at(@position, @target, Math::Vector3.up)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def projection_matrix
|
|
80
|
+
aspect = compute_aspect_ratio
|
|
81
|
+
Math::Matrix4.perspective(@fov, aspect, @min_z, @max_z)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def compute_aspect_ratio
|
|
85
|
+
if @scene&.engine
|
|
86
|
+
@scene.engine.render_width.to_f / @scene.engine.render_height
|
|
87
|
+
else
|
|
88
|
+
@aspect_ratio
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def rebuild_angles_and_radius
|
|
95
|
+
diff = @position - @target
|
|
96
|
+
@radius = diff.length
|
|
97
|
+
return if @radius.zero?
|
|
98
|
+
|
|
99
|
+
@alpha = ::Math.atan2(diff.x, diff.z)
|
|
100
|
+
@beta = ::Math.acos(diff.y / @radius)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def rebuild_position
|
|
104
|
+
@beta = clamp_beta(@beta)
|
|
105
|
+
@alpha = clamp_alpha(@alpha)
|
|
106
|
+
@radius = clamp_radius(@radius)
|
|
107
|
+
@position = compute_position(@alpha, @beta, @radius, @target)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def compute_position(alpha, beta, radius, target)
|
|
111
|
+
x = target.x + radius * ::Math.sin(beta) * ::Math.sin(alpha)
|
|
112
|
+
y = target.y + radius * ::Math.cos(beta)
|
|
113
|
+
z = target.z + radius * ::Math.sin(beta) * ::Math.cos(alpha)
|
|
114
|
+
Math::Vector3.new(x, y, z)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def clamp_alpha(value)
|
|
118
|
+
return value unless @lower_alpha_limit && @upper_alpha_limit
|
|
119
|
+
|
|
120
|
+
[[value, @lower_alpha_limit].max, @upper_alpha_limit].min
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def clamp_beta(value)
|
|
124
|
+
[[value, @lower_beta_limit].max, @upper_beta_limit].min
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def clamp_radius(value)
|
|
128
|
+
min = @lower_radius_limit || 0.01
|
|
129
|
+
max = @upper_radius_limit || Float::INFINITY
|
|
130
|
+
[[value, min].max, max].min
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arcanoae
|
|
4
|
+
module Cameras
|
|
5
|
+
class Camera < Scene::TransformNode
|
|
6
|
+
attr_accessor :min_z, :max_z, :viewport, :inertia
|
|
7
|
+
|
|
8
|
+
def initialize(name, position, scene = nil)
|
|
9
|
+
super(name, scene)
|
|
10
|
+
@position = position.clone
|
|
11
|
+
@min_z = 0.1
|
|
12
|
+
@max_z = 10000.0
|
|
13
|
+
@viewport = { x: 0, y: 0, width: 1, height: 1 }
|
|
14
|
+
@inertia = 0.9
|
|
15
|
+
@attached = false
|
|
16
|
+
|
|
17
|
+
scene&.add_camera(self)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def attach_control(canvas = nil, no_prevent_default = false)
|
|
21
|
+
@attached = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def detach_control
|
|
25
|
+
@attached = false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def attached?
|
|
29
|
+
@attached
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def view_matrix
|
|
33
|
+
raise NotImplementedError, "Subclasses must implement view_matrix"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def projection_matrix
|
|
37
|
+
raise NotImplementedError, "Subclasses must implement projection_matrix"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def get_transform_matrix
|
|
41
|
+
view_matrix * projection_matrix
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def dispose
|
|
45
|
+
return if disposed?
|
|
46
|
+
|
|
47
|
+
@scene&.remove_camera(self)
|
|
48
|
+
super
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|