mittsu 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 (203) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +3 -0
  4. data/CODE_OF_CONDUCT.md +13 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +39 -0
  8. data/Rakefile +7 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +7 -0
  11. data/examples/01_-_Default1noCulling.png +0 -0
  12. data/examples/01_scene_example.rb +14 -0
  13. data/examples/02_box_mesh_example.rb +30 -0
  14. data/examples/02_sphere_mesh_example.rb +30 -0
  15. data/examples/03_complex_object_example.rb +52 -0
  16. data/examples/04_ambient_light_example.rb +33 -0
  17. data/examples/04_dir_light_example.rb +36 -0
  18. data/examples/04_hemi_light_example.rb +30 -0
  19. data/examples/04_point_light_example.rb +50 -0
  20. data/examples/04_spot_light_example.rb +44 -0
  21. data/examples/05_earth_example.rb +42 -0
  22. data/examples/05_earth_moon_example.rb +46 -0
  23. data/examples/05_texture_example.rb +32 -0
  24. data/examples/06_cube_texture_example.rb +36 -0
  25. data/examples/06_skybox_example.rb +60 -0
  26. data/examples/07_earth_normal_example.rb +36 -0
  27. data/examples/08_shadow_example.rb +87 -0
  28. data/examples/09_line_example.rb +52 -0
  29. data/examples/10_obj_loader_example.rb +68 -0
  30. data/examples/11_character_input_example.rb +18 -0
  31. data/examples/11_continuous_keyboard_input_example.rb +35 -0
  32. data/examples/11_keyboard_input_example.rb +43 -0
  33. data/examples/12_mouse_click_example.rb +38 -0
  34. data/examples/12_mouse_motion_example.rb +35 -0
  35. data/examples/12_mouse_scroll_example.rb +36 -0
  36. data/examples/12_orbit_zoom_example.rb +68 -0
  37. data/examples/13_joystick_example.rb +80 -0
  38. data/examples/cubemap/tron_bk.png +0 -0
  39. data/examples/cubemap/tron_dn.png +0 -0
  40. data/examples/cubemap/tron_ft.png +0 -0
  41. data/examples/cubemap/tron_lf.png +0 -0
  42. data/examples/cubemap/tron_rt.png +0 -0
  43. data/examples/cubemap/tron_up.png +0 -0
  44. data/examples/earth.png +0 -0
  45. data/examples/earth_normal.png +0 -0
  46. data/examples/example_helper.rb +2 -0
  47. data/examples/male-02-1noCulling.png +0 -0
  48. data/examples/male02.mtl +54 -0
  49. data/examples/male02.obj +13888 -0
  50. data/examples/moon.png +0 -0
  51. data/examples/orig_02_-_Defaul1noCulling.png +0 -0
  52. data/examples/texture.png +0 -0
  53. data/lib/mittsu.rb +15 -0
  54. data/lib/mittsu/cameras.rb +4 -0
  55. data/lib/mittsu/cameras/camera.rb +34 -0
  56. data/lib/mittsu/cameras/cube_camera.rb +74 -0
  57. data/lib/mittsu/cameras/orthographic_camera.rb +53 -0
  58. data/lib/mittsu/cameras/perspective_camera.rb +115 -0
  59. data/lib/mittsu/constants.rb +160 -0
  60. data/lib/mittsu/core.rb +10 -0
  61. data/lib/mittsu/core/buffer_attribute.rb +87 -0
  62. data/lib/mittsu/core/buffer_geometry.rb +694 -0
  63. data/lib/mittsu/core/clock.rb +44 -0
  64. data/lib/mittsu/core/dynamic_buffer_attribute.rb +16 -0
  65. data/lib/mittsu/core/event_dispatcher.rb +39 -0
  66. data/lib/mittsu/core/face3.rb +30 -0
  67. data/lib/mittsu/core/geometry.rb +596 -0
  68. data/lib/mittsu/core/hash_array.rb +36 -0
  69. data/lib/mittsu/core/hash_object.rb +19 -0
  70. data/lib/mittsu/core/object_3d.rb +421 -0
  71. data/lib/mittsu/core/raycaster.rb +78 -0
  72. data/lib/mittsu/extras.rb +3 -0
  73. data/lib/mittsu/extras/geometries.rb +2 -0
  74. data/lib/mittsu/extras/geometries/box_geometry.rb +108 -0
  75. data/lib/mittsu/extras/geometries/sphere_geometry.rb +88 -0
  76. data/lib/mittsu/extras/helpers.rb +1 -0
  77. data/lib/mittsu/extras/helpers/camera_helper.rb +155 -0
  78. data/lib/mittsu/extras/image.rb +3 -0
  79. data/lib/mittsu/extras/image_utils.rb +80 -0
  80. data/lib/mittsu/lights.rb +7 -0
  81. data/lib/mittsu/lights/ambient_light.rb +16 -0
  82. data/lib/mittsu/lights/area_light.rb +24 -0
  83. data/lib/mittsu/lights/directional_light.rb +131 -0
  84. data/lib/mittsu/lights/hemisphere_light.rb +29 -0
  85. data/lib/mittsu/lights/light.rb +21 -0
  86. data/lib/mittsu/lights/point_light.rb +27 -0
  87. data/lib/mittsu/lights/spot_light.rb +104 -0
  88. data/lib/mittsu/loaders.rb +7 -0
  89. data/lib/mittsu/loaders/cache.rb +53 -0
  90. data/lib/mittsu/loaders/file_loader.rb +22 -0
  91. data/lib/mittsu/loaders/image_loader.rb +32 -0
  92. data/lib/mittsu/loaders/loader.rb +212 -0
  93. data/lib/mittsu/loaders/loading_manager.rb +17 -0
  94. data/lib/mittsu/loaders/mtl_loader.rb +242 -0
  95. data/lib/mittsu/loaders/obj_mtl_loader.rb +225 -0
  96. data/lib/mittsu/materials.rb +7 -0
  97. data/lib/mittsu/materials/line_basic_material.rb +39 -0
  98. data/lib/mittsu/materials/material.rb +156 -0
  99. data/lib/mittsu/materials/mesh_basic_material.rb +122 -0
  100. data/lib/mittsu/materials/mesh_face_material.rb +30 -0
  101. data/lib/mittsu/materials/mesh_lambert_material.rb +126 -0
  102. data/lib/mittsu/materials/mesh_phong_material.rb +152 -0
  103. data/lib/mittsu/materials/shader_material.rb +108 -0
  104. data/lib/mittsu/math.rb +105 -0
  105. data/lib/mittsu/math/box2.rb +135 -0
  106. data/lib/mittsu/math/box3.rb +194 -0
  107. data/lib/mittsu/math/color.rb +252 -0
  108. data/lib/mittsu/math/color_keywords.rb +151 -0
  109. data/lib/mittsu/math/euler.rb +182 -0
  110. data/lib/mittsu/math/frustum.rb +106 -0
  111. data/lib/mittsu/math/line3.rb +76 -0
  112. data/lib/mittsu/math/matrix3.rb +163 -0
  113. data/lib/mittsu/math/matrix4.rb +581 -0
  114. data/lib/mittsu/math/plane.rb +128 -0
  115. data/lib/mittsu/math/quaternion.rb +309 -0
  116. data/lib/mittsu/math/ray.rb +292 -0
  117. data/lib/mittsu/math/sphere.rb +91 -0
  118. data/lib/mittsu/math/spline.rb +128 -0
  119. data/lib/mittsu/math/triangle.rb +121 -0
  120. data/lib/mittsu/math/vector2.rb +238 -0
  121. data/lib/mittsu/math/vector3.rb +491 -0
  122. data/lib/mittsu/math/vector4.rb +414 -0
  123. data/lib/mittsu/objects.rb +3 -0
  124. data/lib/mittsu/objects/group.rb +8 -0
  125. data/lib/mittsu/objects/line.rb +143 -0
  126. data/lib/mittsu/objects/mesh.rb +243 -0
  127. data/lib/mittsu/renderers.rb +1 -0
  128. data/lib/mittsu/renderers/glfw_window.rb +216 -0
  129. data/lib/mittsu/renderers/opengl/opengl_debug.rb +38 -0
  130. data/lib/mittsu/renderers/opengl/opengl_program.rb +402 -0
  131. data/lib/mittsu/renderers/opengl/opengl_shader.rb +58 -0
  132. data/lib/mittsu/renderers/opengl/opengl_state.rb +207 -0
  133. data/lib/mittsu/renderers/opengl/plugins/shadow_map_plugin.rb +416 -0
  134. data/lib/mittsu/renderers/opengl_render_target.rb +87 -0
  135. data/lib/mittsu/renderers/opengl_renderer.rb +3376 -0
  136. data/lib/mittsu/renderers/shaders/shader_chunk.rb +12 -0
  137. data/lib/mittsu/renderers/shaders/shader_chunk/alphamap_fragment.glsl +5 -0
  138. data/lib/mittsu/renderers/shaders/shader_chunk/alphamap_pars_fragment.glsl +5 -0
  139. data/lib/mittsu/renderers/shaders/shader_chunk/alphatest_fragment.glsl +5 -0
  140. data/lib/mittsu/renderers/shaders/shader_chunk/bumpmap_pars_fragment.glsl +40 -0
  141. data/lib/mittsu/renderers/shaders/shader_chunk/color_fragment.glsl +5 -0
  142. data/lib/mittsu/renderers/shaders/shader_chunk/color_pars_fragment.glsl +5 -0
  143. data/lib/mittsu/renderers/shaders/shader_chunk/color_pars_vertex.glsl +5 -0
  144. data/lib/mittsu/renderers/shaders/shader_chunk/color_vertex.glsl +5 -0
  145. data/lib/mittsu/renderers/shaders/shader_chunk/common.glsl +60 -0
  146. data/lib/mittsu/renderers/shaders/shader_chunk/default_vertex.glsl +15 -0
  147. data/lib/mittsu/renderers/shaders/shader_chunk/defaultnormal_vertex.glsl +21 -0
  148. data/lib/mittsu/renderers/shaders/shader_chunk/envmap_fragment.glsl +62 -0
  149. data/lib/mittsu/renderers/shaders/shader_chunk/envmap_pars_fragment.glsl +21 -0
  150. data/lib/mittsu/renderers/shaders/shader_chunk/envmap_pars_vertex.glsl +7 -0
  151. data/lib/mittsu/renderers/shaders/shader_chunk/envmap_vertex.glsl +17 -0
  152. data/lib/mittsu/renderers/shaders/shader_chunk/fog_fragment.glsl +26 -0
  153. data/lib/mittsu/renderers/shaders/shader_chunk/fog_pars_fragment.glsl +15 -0
  154. data/lib/mittsu/renderers/shaders/shader_chunk/lightmap_fragment.glsl +5 -0
  155. data/lib/mittsu/renderers/shaders/shader_chunk/lightmap_pars_fragment.glsl +6 -0
  156. data/lib/mittsu/renderers/shaders/shader_chunk/lightmap_pars_vertex.glsl +5 -0
  157. data/lib/mittsu/renderers/shaders/shader_chunk/lightmap_vertex.glsl +5 -0
  158. data/lib/mittsu/renderers/shaders/shader_chunk/lights_lambert_pars_vertex.glsl +43 -0
  159. data/lib/mittsu/renderers/shaders/shader_chunk/lights_lambert_vertex.glsl +196 -0
  160. data/lib/mittsu/renderers/shaders/shader_chunk/lights_phong_fragment.glsl +243 -0
  161. data/lib/mittsu/renderers/shaders/shader_chunk/lights_phong_pars_fragment.glsl +58 -0
  162. data/lib/mittsu/renderers/shaders/shader_chunk/lights_phong_pars_vertex.glsl +5 -0
  163. data/lib/mittsu/renderers/shaders/shader_chunk/lights_phong_vertex.glsl +5 -0
  164. data/lib/mittsu/renderers/shaders/shader_chunk/linear_to_gamma_fragment.glsl +2 -0
  165. data/lib/mittsu/renderers/shaders/shader_chunk/logdepthbuf_fragment.glsl +5 -0
  166. data/lib/mittsu/renderers/shaders/shader_chunk/logdepthbuf_pars_fragment.glsl +12 -0
  167. data/lib/mittsu/renderers/shaders/shader_chunk/logdepthbuf_pars_vertex.glsl +11 -0
  168. data/lib/mittsu/renderers/shaders/shader_chunk/logdepthbuf_vertex.glsl +15 -0
  169. data/lib/mittsu/renderers/shaders/shader_chunk/map_fragment.glsl +9 -0
  170. data/lib/mittsu/renderers/shaders/shader_chunk/map_pars_fragment.glsl +11 -0
  171. data/lib/mittsu/renderers/shaders/shader_chunk/map_pars_vertex.glsl +6 -0
  172. data/lib/mittsu/renderers/shaders/shader_chunk/map_particle_fragment.glsl +5 -0
  173. data/lib/mittsu/renderers/shaders/shader_chunk/map_particle_pars_fragment.glsl +6 -0
  174. data/lib/mittsu/renderers/shaders/shader_chunk/map_vertex.glsl +5 -0
  175. data/lib/mittsu/renderers/shaders/shader_chunk/morphnormal_vertex.glsl +12 -0
  176. data/lib/mittsu/renderers/shaders/shader_chunk/morphtarget_pars_vertex.glsl +13 -0
  177. data/lib/mittsu/renderers/shaders/shader_chunk/morphtarget_vertex.glsl +20 -0
  178. data/lib/mittsu/renderers/shaders/shader_chunk/normalmap_pars_fragment.glsl +27 -0
  179. data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_fragment.glsl +217 -0
  180. data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_pars_fragment.glsl +19 -0
  181. data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_pars_vertex.glsl +6 -0
  182. data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_vertex.glsl +9 -0
  183. data/lib/mittsu/renderers/shaders/shader_chunk/skinbase_vertex.glsl +8 -0
  184. data/lib/mittsu/renderers/shaders/shader_chunk/skinning_pars_vertex.glsl +47 -0
  185. data/lib/mittsu/renderers/shaders/shader_chunk/skinning_vertex.glsl +20 -0
  186. data/lib/mittsu/renderers/shaders/shader_chunk/skinnormal_vertex.glsl +20 -0
  187. data/lib/mittsu/renderers/shaders/shader_chunk/specularmap_fragment.glsl +12 -0
  188. data/lib/mittsu/renderers/shaders/shader_chunk/specularmap_pars_fragment.glsl +5 -0
  189. data/lib/mittsu/renderers/shaders/shader_chunk/worldpos_vertex.glsl +17 -0
  190. data/lib/mittsu/renderers/shaders/shader_lib.rb +420 -0
  191. data/lib/mittsu/renderers/shaders/uniforms_lib.rb +107 -0
  192. data/lib/mittsu/renderers/shaders/uniforms_utils.rb +31 -0
  193. data/lib/mittsu/scenes.rb +1 -0
  194. data/lib/mittsu/scenes/scene.rb +27 -0
  195. data/lib/mittsu/textures.rb +5 -0
  196. data/lib/mittsu/textures/compressed_texture.rb +30 -0
  197. data/lib/mittsu/textures/cube_texture.rb +19 -0
  198. data/lib/mittsu/textures/data_texture.rb +17 -0
  199. data/lib/mittsu/textures/texture.rb +92 -0
  200. data/lib/mittsu/textures/video_texture.rb +17 -0
  201. data/lib/mittsu/version.rb +4 -0
  202. data/mittsu.gemspec +31 -0
  203. metadata +357 -0
@@ -0,0 +1,128 @@
1
+ require 'mittsu/math'
2
+
3
+ module Mittsu
4
+ class Plane
5
+ attr_accessor :normal, :constant
6
+
7
+ def initialize(normal = Mittsu::Vector3.new(1, 0, 0), constant = 0.0)
8
+ @normal, @constant = normal, constant.to_f
9
+ end
10
+
11
+ def set(normal, constant)
12
+ @normal.copy(normal)
13
+ @constant = constant.to_f
14
+ self
15
+ end
16
+
17
+ def set_components(x, y, z, w)
18
+ @normal.set(x, y, z)
19
+ @constant = w.to_f
20
+ self
21
+ end
22
+
23
+ def set_from_normal_and_coplanar_point(normal, point)
24
+ @normal.copy(normal)
25
+ @constant = -point.dot(@normal) # must be @normal, not normal, as @normal is normalized
26
+ self
27
+ end
28
+
29
+ def set_from_coplanar_points(a, b, c)
30
+ v1 = Mittsu::Vector3.new
31
+ v2 = Mittsu::Vector3.new
32
+ normal = v1.sub_vectors(c, b).cross(v2.sub_vectors(a, b)).normalize
33
+ # Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
34
+ self.set_from_normal_and_coplanar_point(normal, a)
35
+ self
36
+ end
37
+
38
+ def copy(plane)
39
+ @normal.copy(plane.normal)
40
+ @constant = plane.constant
41
+ self
42
+ end
43
+
44
+ def normalize
45
+ # Note: will lead to a divide by zero if the plane is invalid.
46
+ inverse_normal_length = 1.0 / @normal.length
47
+ @normal.multiply_scalar(inverse_normal_length)
48
+ @constant *= inverse_normal_length
49
+ self
50
+ end
51
+
52
+ def negate
53
+ @constant *= -1.0
54
+ @normal.negate
55
+ self
56
+ end
57
+
58
+ def distance_to_point(point)
59
+ @normal.dot(point) + @constant
60
+ end
61
+
62
+ def distance_to_sphere(sphere)
63
+ self.distance_to_point(sphere.center) - sphere.radius
64
+ end
65
+
66
+ def project_point(point, target = Mittsu::Vector3.new)
67
+ self.ortho_point(point, target).sub(point).negate
68
+ end
69
+
70
+ def ortho_point(point, target = Mittsu::Vector3.new)
71
+ perpendicular_magnitude = self.distance_to_point(point)
72
+ target.copy(@normal).multiply_scalar(perpendicular_magnitude)
73
+ end
74
+
75
+ def intersection_line?(line)
76
+ # Note: self tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
77
+ start_sign = self.distance_to_point(line.start_point)
78
+ end_sign = self.distance_to_point(line.end_point)
79
+ (start_sign < 0 && end_sign > 0) || (end_sign < 0 && start_sign > 0)
80
+ end
81
+
82
+ def intersect_line(line, target = Mittsu::Vector3.new)
83
+ v1 = Mittsu::Vector3.new
84
+ direction = line.delta(v1)
85
+ denominator = @normal.dot(direction)
86
+ if denominator.zero?
87
+ # line is coplanar, return origin
88
+ if self.distance_to_point(line.start_point).zero?
89
+ return target.copy(line.start_point)
90
+ end
91
+ # Unsure if this is the correct method to handle this case.
92
+ return nil
93
+ end
94
+ t = -(line.start_point.dot(@normal) + @constant) / denominator
95
+ return nil if t < 0 || t > 1
96
+ target.copy(direction).multiply_scalar(t).add(line.start_point)
97
+ end
98
+
99
+ def coplanar_point(target = Mittsu::Vector3.new)
100
+ target.copy(@normal).multiply_scalar(- @constant)
101
+ end
102
+
103
+ def apply_matrix4(matrix, normal_matrix = Mittsu::Matrix3.new.normal_matrix(matrix))
104
+ v1 = Mittsu::Vector3.new
105
+ v2 = Mittsu::Vector3.new
106
+ # compute new normal based on theory here:
107
+ # http:#www.songho.ca/opengl/gl_normaltransform.html
108
+ new_normal = v1.copy(@normal).apply_matrix3(normal_matrix)
109
+ new_coplanar_point = self.coplanar_point(v2)
110
+ new_coplanar_point.apply_matrix4(matrix)
111
+ self.set_from_normal_and_coplanar_point(new_normal, new_coplanar_point)
112
+ self
113
+ end
114
+
115
+ def translate(offset)
116
+ @constant = @constant - offset.dot(@normal)
117
+ self
118
+ end
119
+
120
+ def ==(plane)
121
+ plane.normal == @normal && plane.constant == @constant
122
+ end
123
+
124
+ def clone
125
+ Mittsu::Plane.new.copy(self)
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,309 @@
1
+ require 'mittsu/math'
2
+
3
+ module Mittsu
4
+ class Quaternion
5
+ EPS = 0.000001
6
+
7
+ attr_reader :x, :y, :z, :w
8
+
9
+ def initialize(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
10
+ @x, @y, @z, @w = x, y, z, w
11
+ end
12
+
13
+ def set(x, y, z, w)
14
+ @x = x.to_f
15
+ @y = y.to_f
16
+ @z = z.to_f
17
+ @w = w.to_f
18
+ self.on_change_callback
19
+ self
20
+ end
21
+
22
+ def x=(x)
23
+ @x = x.to_f
24
+ self.on_change_callback
25
+ end
26
+
27
+ def y=(y)
28
+ @y = y.to_f
29
+ self.on_change_callback
30
+ end
31
+
32
+ def z=(z)
33
+ @z = z.to_f
34
+ self.on_change_callback
35
+ end
36
+
37
+ def w=(w)
38
+ @w = w.to_f
39
+ self.on_change_callback
40
+ end
41
+
42
+ def copy(quaternion)
43
+ @x = quaternion.x
44
+ @y = quaternion.y
45
+ @z = quaternion.z
46
+ @w = quaternion.w
47
+ self.on_change_callback
48
+ self
49
+ end
50
+
51
+ def set_from_euler(euler, update = true)
52
+ # http:#www.mathworks.com/matlabcentral/fileexchange/
53
+ # 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
54
+ # content/SpinCalc.m
55
+ c1 = Math.cos(euler.x / 2.0)
56
+ c2 = Math.cos(euler.y / 2.0)
57
+ c3 = Math.cos(euler.z / 2.0)
58
+ s1 = Math.sin(euler.x / 2.0)
59
+ s2 = Math.sin(euler.y / 2.0)
60
+ s3 = Math.sin(euler.z / 2.0)
61
+ if euler.order == 'XYZ'
62
+ @x = s1 * c2 * c3 + c1 * s2 * s3
63
+ @y = c1 * s2 * c3 - s1 * c2 * s3
64
+ @z = c1 * c2 * s3 + s1 * s2 * c3
65
+ @w = c1 * c2 * c3 - s1 * s2 * s3
66
+ elsif euler.order == 'YXZ'
67
+ @x = s1 * c2 * c3 + c1 * s2 * s3
68
+ @y = c1 * s2 * c3 - s1 * c2 * s3
69
+ @z = c1 * c2 * s3 - s1 * s2 * c3
70
+ @w = c1 * c2 * c3 + s1 * s2 * s3
71
+ elsif euler.order == 'ZXY'
72
+ @x = s1 * c2 * c3 - c1 * s2 * s3
73
+ @y = c1 * s2 * c3 + s1 * c2 * s3
74
+ @z = c1 * c2 * s3 + s1 * s2 * c3
75
+ @w = c1 * c2 * c3 - s1 * s2 * s3
76
+ elsif euler.order == 'ZYX'
77
+ @x = s1 * c2 * c3 - c1 * s2 * s3
78
+ @y = c1 * s2 * c3 + s1 * c2 * s3
79
+ @z = c1 * c2 * s3 - s1 * s2 * c3
80
+ @w = c1 * c2 * c3 + s1 * s2 * s3
81
+ elsif euler.order == 'YZX'
82
+ @x = s1 * c2 * c3 + c1 * s2 * s3
83
+ @y = c1 * s2 * c3 + s1 * c2 * s3
84
+ @z = c1 * c2 * s3 - s1 * s2 * c3
85
+ @w = c1 * c2 * c3 - s1 * s2 * s3
86
+ elsif euler.order == 'XZY'
87
+ @x = s1 * c2 * c3 - c1 * s2 * s3
88
+ @y = c1 * s2 * c3 - s1 * c2 * s3
89
+ @z = c1 * c2 * s3 + s1 * s2 * c3
90
+ @w = c1 * c2 * c3 + s1 * s2 * s3
91
+ end
92
+ self.on_change_callback if update
93
+ self
94
+ end
95
+
96
+ def set_from_axis_angle(axis, angle)
97
+ # http:#www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
98
+ # assumes axis is normalized
99
+ half_angle = angle / 2.0
100
+ s = Math.sin(half_angle)
101
+ @x = axis.x * s
102
+ @y = axis.y * s
103
+ @z = axis.z * s
104
+ @w = Math.cos(half_angle)
105
+ self.on_change_callback
106
+ self
107
+ end
108
+
109
+ def set_from_rotation_matrix(m)
110
+ # http:#www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
111
+ # assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
112
+ te = m.elements
113
+ m11 = te[0]; m12 = te[4]; m13 = te[8]
114
+ m21 = te[1]; m22 = te[5]; m23 = te[9]
115
+ m31 = te[2]; m32 = te[6]; m33 = te[10]
116
+ trace = m11 + m22 + m33
117
+ if trace > 0
118
+ s = 0.5 / Math.sqrt(trace + 1.0)
119
+ @w = 0.25 / s
120
+ @x = (m32 - m23) * s
121
+ @y = (m13 - m31) * s
122
+ @z = (m21 - m12) * s
123
+ elsif m11 > m22 && m11 > m33
124
+ s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33)
125
+ @w = (m32 - m23) / s
126
+ @x = 0.25 * s
127
+ @y = (m12 + m21) / s
128
+ @z = (m13 + m31) / s
129
+ elsif m22 > m33
130
+ s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33)
131
+ @w = (m13 - m31) / s
132
+ @x = (m12 + m21) / s
133
+ @y = 0.25 * s
134
+ @z = (m23 + m32) / s
135
+ else
136
+ s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22)
137
+ @w = (m21 - m12) / s
138
+ @x = (m13 + m31) / s
139
+ @y = (m23 + m32) / s
140
+ @z = 0.25 * s
141
+ end
142
+ self.on_change_callback
143
+ self
144
+ end
145
+
146
+ def set_from_unit_vectors(v_from, v_to)
147
+ # http:#lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
148
+ # assumes direction vectors v_from and v_to are normalized
149
+ v1 = Mittsu::Vector3.new
150
+ r = v_from.dot(v_to) + 1.0
151
+ if r < EPS
152
+ r = 0.0
153
+ if v_from.x.abs > v_from.z.abs
154
+ v1.set(-v_from.y, v_from.x, 0.0)
155
+ else
156
+ v1.set(0.0, -v_from.z, v_from.y)
157
+ end
158
+ else
159
+ v1.cross_vectors(v_from, v_to)
160
+ end
161
+ @x = v1.x
162
+ @y = v1.y
163
+ @z = v1.z
164
+ @w = r
165
+ self.normalize
166
+ self
167
+ end
168
+
169
+ def inverse
170
+ self.conjugate.normalize
171
+ self
172
+ end
173
+
174
+ def conjugate
175
+ @x *= -1.0
176
+ @y *= -1.0
177
+ @z *= -1.0
178
+ self.on_change_callback
179
+ self
180
+ end
181
+
182
+ def dot(v)
183
+ @x * v._x + @y * v._y + @z * v._z + @w * v._w
184
+ end
185
+
186
+ def length_sq
187
+ @x * @x + @y * @y + @z * @z + @w * @w
188
+ end
189
+
190
+ def length
191
+ Math.sqrt(@x * @x + @y * @y + @z * @z + @w * @w)
192
+ end
193
+
194
+ def normalize
195
+ l = self.length
196
+ if l == 0.0
197
+ @x = 0.0
198
+ @y = 0.0
199
+ @z = 0.0
200
+ @w = 1.0
201
+ else
202
+ l = 1.0 / l
203
+ @x = @x * l
204
+ @y = @y * l
205
+ @z = @z * l
206
+ @w = @w * l
207
+ end
208
+ self.on_change_callback
209
+ self
210
+ end
211
+
212
+ def multiply(q)
213
+ self.multiply_quaternions(self, q)
214
+ end
215
+
216
+ def multiply_quaternions(a, b)
217
+ # from http:#www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
218
+ qax = a.x; qay = a.y; qaz = a.z; qaw = a.w
219
+ qbx = b.x; qby = b.y; qbz = b.z; qbw = b.w
220
+ @x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby
221
+ @y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz
222
+ @z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx
223
+ @w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz
224
+ self.on_change_callback
225
+ self
226
+ end
227
+
228
+ def slerp(qb, t)
229
+ return self if t.zero?
230
+ return self.copy(qb) if t == 1.0
231
+ _x, _y, _z, _w = @x, @y, @z, @w
232
+ # http:#www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
233
+ cos_half_theta = _w * qb.w + _x * qb.x + _y * qb.y + _z * qb.z
234
+ if cos_half_theta < 0.0
235
+ @w = -qb.w
236
+ @x = -qb.x
237
+ @y = -qb.y
238
+ @z = -qb.z
239
+ cos_half_theta = - cos_half_theta
240
+ else
241
+ self.copy(qb)
242
+ end
243
+ if cos_half_theta >= 1.0
244
+ @w = _w
245
+ @x = _x
246
+ @y = _y
247
+ @z = _z
248
+ return self
249
+ end
250
+ half_theta = Math.acos(cos_half_theta)
251
+ sin_half_theta = Math.sqrt(1.0 - cos_half_theta * cos_half_theta)
252
+ if sin_half_theta.abs < 0.001
253
+ @w = 0.5 * (_w + @w)
254
+ @x = 0.5 * (_x + @x)
255
+ @y = 0.5 * (_y + @y)
256
+ @z = 0.5 * (_z + @z)
257
+ return self
258
+ end
259
+ ratio_a = Math.sin((1.0. - t) * half_theta) / sin_half_theta,
260
+ ratio_b = Math.sin(t * half_theta) / sin_half_theta
261
+ @w = (_w * ratio_a + @w * ratio_b)
262
+ @x = (_x * ratio_a + @x * ratio_b)
263
+ @y = (_y * ratio_a + @y * ratio_b)
264
+ @z = (_z * ratio_a + @z * ratio_b)
265
+ self.on_change_callback
266
+ self
267
+ end
268
+
269
+ def ==(quaternion)
270
+ (quaternion.x == @x) && (quaternion.y == @y) && (quaternion.z == @z) && (quaternion.w == @w)
271
+ end
272
+
273
+ def from_array(array, offset = 0)
274
+ @x = array[offset]
275
+ @y = array[offset + 1]
276
+ @z = array[offset + 2]
277
+ @w = array[offset + 3]
278
+ self.on_change_callback
279
+ self
280
+ end
281
+
282
+ def to_array(array = [], offset = 0)
283
+ array[offset] = @x
284
+ array[offset + 1] = @y
285
+ array[offset + 2] = @z
286
+ array[offset + 3] = @w
287
+ array
288
+ end
289
+
290
+ def on_change(&callback)
291
+ @on_change_callback = callback
292
+ self
293
+ end
294
+
295
+ def on_change_callback
296
+ return unless @on_change_callback
297
+ @on_change_callback.call
298
+ end
299
+
300
+ def clone
301
+ Mittsu::Quaternion.new(@x, @y, @z, @w)
302
+ end
303
+
304
+ def self.slerp(qa, qb, qm, t)
305
+ qm.copy(qa).slerp(qb, t)
306
+ end
307
+
308
+ end
309
+ end
@@ -0,0 +1,292 @@
1
+ require 'mittsu/math'
2
+
3
+ module Mittsu
4
+ class Ray
5
+ attr_accessor :origin, :direction
6
+
7
+ def initialize(origin = Mittsu::Vector3.new, direction = Mittsu::Vector3.new)
8
+ @origin, @direction = origin, direction
9
+ end
10
+
11
+ def set(origin, direction)
12
+ @origin.copy(origin)
13
+ @direction.copy(direction)
14
+ self
15
+ end
16
+
17
+ def copy(ray)
18
+ @origin.copy(ray.origin)
19
+ @direction.copy(ray.direction)
20
+ self
21
+ end
22
+
23
+ def at(t, target = Mittsu::Vector3.new)
24
+ target.copy(@direction).multiply_scalar(t).add(@origin)
25
+ end
26
+
27
+ def recast(t)
28
+ v1 = Mittsu::Vector3.new
29
+ @origin.copy(self.at(t, v1))
30
+ self
31
+ end
32
+
33
+ def closest_point_to_point(point, target = Mittsu::Vector3.new)
34
+ target.sub_vectors(point, @origin)
35
+ direction_distance = target.dot(@direction)
36
+ if direction_distance < 0
37
+ return target.copy(@origin)
38
+ end
39
+ target.copy(@direction).multiply_scalar(direction_distance).add(@origin)
40
+ end
41
+
42
+ def distance_to_point(point)
43
+ v1 = Mittsu::Vector3.new
44
+ direction_distance = v1.sub_vectors(point, @origin).dot(@direction)
45
+ # point behind the ray
46
+ if direction_distance < 0
47
+ return @origin.distance_to(point)
48
+ end
49
+ v1.copy(@direction).multiply_scalar(direction_distance).add(@origin)
50
+ v1.distance_to(point)
51
+ end
52
+
53
+ def distance_sq_to_segment(v0, v1, point_on_ray = nil, point_on_segment = nil)
54
+ seg_center = Mittsu::Vector3.new
55
+ seg_dir = Mittsu::Vector3.new
56
+ diff = Mittsu::Vector3.new
57
+ # from http:#www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
58
+ # It returns the min distance between the ray and the segment
59
+ # defined by v0 and v1
60
+ # It can also set two optional targets :
61
+ # - The closest point on the ray
62
+ # - The closest point on the segment
63
+ seg_center.copy(v0).add(v1).multiply_scalar(0.5)
64
+ seg_dir.copy(v1).sub(v0).normalize
65
+ diff.copy(@origin).sub(seg_center)
66
+ seg_extent = v0.distance_to(v1) * 0.5
67
+ a01 = -@direction.dot(seg_dir)
68
+ b0 = diff.dot(@direction)
69
+ b1 = -diff.dot(seg_dir)
70
+ c = diff.length_sq
71
+ det = (1.0 - a01 * a01).abs
72
+ if det > 0
73
+ # The ray and segment are not parallel.
74
+ s0 = a01 * b1 - b0
75
+ s1 = a01 * b0 - b1
76
+ ext_det = seg_extent * det
77
+ if s0 >= 0
78
+ if s1 >= -ext_det
79
+ if s1 <= ext_det
80
+ # region 0
81
+ # Minimum at interior points of ray and segment.
82
+ inv_det = 1.0 / det
83
+ s0 *= inv_det
84
+ s1 *= inv_det
85
+ sqr_dist = s0 * (s0 + a01 * s1 + 2.0 * b0) + s1 * (a01 * s0 + s1 + 2.0 * b1) + c
86
+ else
87
+ # region 1
88
+ s1 = seg_extent
89
+ s0 = [0.0, -(a01 * s1 + b0)].max
90
+ sqr_dist = - s0 * s0 + s1 * (s1 + 2.0 * b1) + c
91
+ end
92
+ else
93
+ # region 5
94
+ s1 = - seg_extent
95
+ s0 = [0.0, -(a01 * s1 + b0)].max
96
+ sqr_dist = -s0 * s0 + s1 * (s1 + 2.0 * b1) + c
97
+ end
98
+ else
99
+ if s1 <= - ext_det
100
+ # region 4
101
+ s0 = [0.0, -(-a01 * seg_extent + b0)].max
102
+ s1 = (s0 > 0) ? -seg_extent : [[-seg_extent, -b1].max, seg_extent].min
103
+ sqr_dist = - s0 * s0 + s1 * (s1 + 2 * b1) + c
104
+ elsif s1 <= ext_det
105
+ # region 3
106
+ s0 = 0.0
107
+ s1 = [[-seg_extent, -b1].max, seg_extent].min
108
+ sqr_dist = s1 * (s1 + 2.0 * b1) + c
109
+ else
110
+ # region 2
111
+ s0 = [0.0, -(a01 * seg_extent + b0)].max
112
+ s1 = (s0 > 0) ? seg_extent : [[-seg_extent, -b1].max, seg_extent].min
113
+ sqr_dist = -s0 * s0 + s1 * (s1 + 2.0 * b1) + c
114
+ end
115
+ end
116
+ else
117
+ # Ray and segment are parallel.
118
+ s1 = (a01 > 0) ? -seg_extent : seg_extent
119
+ s0 = [0.0, -(a01 * s1 + b0)].max
120
+ sqr_dist = -s0 * s0 + s1 * (s1 + 2.0 * b1) + c
121
+ end
122
+ if point_on_ray
123
+ point_on_ray.copy(@direction).multiply_scalar(s0).add(@origin)
124
+ end
125
+ if point_on_segment
126
+ point_on_segment.copy(seg_dir).multiply_scalar(s1).add(seg_center)
127
+ end
128
+ sqr_dist
129
+ end
130
+
131
+ def intersection_sphere?(sphere)
132
+ self.distance_to_point(sphere.center) <= sphere.radius
133
+ end
134
+
135
+ def intersect_sphere(sphere, target = Mittsu::Vector3.new)
136
+ # from http:#www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/
137
+ v1 = Mittsu::Vector3.new
138
+ v1.sub_vectors(sphere.center, @origin)
139
+ tca = v1.dot(@direction)
140
+ d2 = v1.dot(v1) - tca * tca
141
+ radius2 = sphere.radius * sphere.radius
142
+ return nil if d2 > radius2
143
+ thc = Math.sqrt(radius2 - d2)
144
+ # t0 = first intersect point - entrance on front of sphere
145
+ t0 = tca - thc
146
+ # t1 = second intersect point - exit point on back of sphere
147
+ t1 = tca + thc
148
+ # test to see if both t0 and t1 are behind the ray - if so, return nil
149
+ return nil if t0 < 0 && t1 < 0
150
+ # test to see if t0 is behind the ray:
151
+ # if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
152
+ # in order to always return an intersect point that is in front of the ray.
153
+ return self.at(t1, target) if t0 < 0
154
+ # else t0 is in front of the ray, so return the first collision point scaled by t0
155
+ self.at(t0, target)
156
+ end
157
+
158
+ def intersection_plane?(plane)
159
+ # check if the ray lies on the plane first
160
+ dist_to_point = plane.distance_to_point(@origin)
161
+ return true if dist_to_point.zero?
162
+ denominator = plane.normal.dot(@direction)
163
+ return true if denominator * dist_to_point < 0
164
+ # ray origin is behind the plane (and is pointing behind it)
165
+ false
166
+ end
167
+
168
+ def distance_to_plane(plane)
169
+ denominator = plane.normal.dot(@direction)
170
+ if denominator.zero?
171
+ # line is coplanar, return origin
172
+ return 0.0 if plane.distance_to_point(@origin).zero?
173
+ # Null is preferable to nil since nil means.... it is nil
174
+ return nil
175
+ end
176
+ t = -(@origin.dot(plane.normal) + plane.constant) / denominator
177
+ # Return if the ray never intersects the plane
178
+ t >= 0 ? t : nil
179
+ end
180
+
181
+ def intersect_plane(plane, target = Mittsu::Vector3.new)
182
+ t = self.distance_to_plane(plane)
183
+ return nil if t.nil?
184
+ self.at(t, target)
185
+ end
186
+
187
+ def intersection_box?(box)
188
+ v = Mittsu::Vector3.new
189
+ !self.intersect_box(box, v).nil?
190
+ end
191
+
192
+ def intersect_box(box, target = Mittsu::Vector3.new)
193
+ # http:#www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
194
+ invdirx = 1.0 / @direction.x
195
+ invdiry = 1.0 / @direction.y
196
+ invdirz = 1.0 / @direction.z
197
+ origin = @origin
198
+ if invdirx >= 0
199
+ tmin = (box.min.x - origin.x) * invdirx
200
+ tmax = (box.max.x - origin.x) * invdirx
201
+ else
202
+ tmin = (box.max.x - origin.x) * invdirx
203
+ tmax = (box.min.x - origin.x) * invdirx
204
+ end
205
+ if invdiry >= 0
206
+ tymin = (box.min.y - origin.y) * invdiry
207
+ tymax = (box.max.y - origin.y) * invdiry
208
+ else
209
+ tymin = (box.max.y - origin.y) * invdiry
210
+ tymax = (box.min.y - origin.y) * invdiry
211
+ end
212
+ return nil if tmin > tymax || tymin > tmax
213
+ # These lines also handle the case where tmin or tmax is NaN
214
+ # (result of 0 * Infinity). x != x returns true if x is NaN
215
+ tmin = tymin if tymin > tmin || tmin != tmin
216
+ tmax = tymax if tymax < tmax || tmax != tmax
217
+ if invdirz >= 0
218
+ tzmin = (box.min.z - origin.z) * invdirz
219
+ tzmax = (box.max.z - origin.z) * invdirz
220
+ else
221
+ tzmin = (box.max.z - origin.z) * invdirz
222
+ tzmax = (box.min.z - origin.z) * invdirz
223
+ end
224
+ return nil if tmin > tzmax || tzmin > tmax
225
+ tmin = tzmin if tzmin > tmin || tmin != tmin
226
+ tmax = tzmax if tzmax < tmax || tmax != tmax
227
+ #return point closest to the ray (positive side)
228
+ return nil if tmax < 0
229
+ self.at(tmin >= 0 ? tmin : tmax, target)
230
+ end
231
+
232
+ def intersect_triangle(a, b, c, backface_culling, target = Mittsu::Vector3.new)
233
+ # Compute the offset origin, edges, and normal.
234
+ diff = Mittsu::Vector3.new
235
+ edge1 = Mittsu::Vector3.new
236
+ edge2 = Mittsu::Vector3.new
237
+ normal = Mittsu::Vector3.new
238
+ # from http:#www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp
239
+ edge1.sub_vectors(b, a)
240
+ edge2.sub_vectors(c, a)
241
+ normal.cross_vectors(edge1, edge2)
242
+ # Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
243
+ # E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
244
+ # |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
245
+ # |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
246
+ # |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
247
+ d_dot_n = @direction.dot(normal)
248
+ sign
249
+ if d_dot_n > 0
250
+ return nil if backface_culling
251
+ sign = 1.0
252
+ elsif d_dot_n < 0
253
+ sign = -1.0
254
+ d_dot_n = - d_dot_n
255
+ else
256
+ return nil
257
+ end
258
+ diff.sub_vectors(@origin, a)
259
+ d_dot_q_x_e2 = sign * @direction.dot(edge2.cross_vectors(diff, edge2))
260
+ # b1 < 0, no intersection
261
+ return nil if d_dot_q_x_e2 < 0
262
+ d_dot_e1_x_q = sign * @direction.dot(edge1.cross(diff))
263
+ # b2 < 0, no intersection
264
+ return nil if d_dot_e1_x_q < 0
265
+ # b1+b2 > 1, no intersection
266
+ return nil if d_dot_q_x_e2 + d_dot_e1_x_q > d_dot_n
267
+ # Line intersects triangle, check if ray does.
268
+ q_dot_n = -sign * diff.dot(normal)
269
+ # t < 0, no intersection
270
+ return nil if q_dot_n < 0
271
+ # Ray intersects triangle.
272
+ self.at(q_dot_n / d_dot_n, target)
273
+ end
274
+
275
+ def apply_matrix4(matrix4)
276
+ @direction.add(@origin).apply_matrix4(matrix4)
277
+ @origin.apply_matrix4(matrix4)
278
+ @direction.sub(@origin)
279
+ @direction.normalize
280
+ self
281
+ end
282
+
283
+ def ==(ray)
284
+ ray.origin == @origin && ray.direction == @direction
285
+ end
286
+
287
+ def clone
288
+ Mittsu::Ray.new.copy(self)
289
+ end
290
+
291
+ end
292
+ end