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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/examples/01_-_Default1noCulling.png +0 -0
- data/examples/01_scene_example.rb +14 -0
- data/examples/02_box_mesh_example.rb +30 -0
- data/examples/02_sphere_mesh_example.rb +30 -0
- data/examples/03_complex_object_example.rb +52 -0
- data/examples/04_ambient_light_example.rb +33 -0
- data/examples/04_dir_light_example.rb +36 -0
- data/examples/04_hemi_light_example.rb +30 -0
- data/examples/04_point_light_example.rb +50 -0
- data/examples/04_spot_light_example.rb +44 -0
- data/examples/05_earth_example.rb +42 -0
- data/examples/05_earth_moon_example.rb +46 -0
- data/examples/05_texture_example.rb +32 -0
- data/examples/06_cube_texture_example.rb +36 -0
- data/examples/06_skybox_example.rb +60 -0
- data/examples/07_earth_normal_example.rb +36 -0
- data/examples/08_shadow_example.rb +87 -0
- data/examples/09_line_example.rb +52 -0
- data/examples/10_obj_loader_example.rb +68 -0
- data/examples/11_character_input_example.rb +18 -0
- data/examples/11_continuous_keyboard_input_example.rb +35 -0
- data/examples/11_keyboard_input_example.rb +43 -0
- data/examples/12_mouse_click_example.rb +38 -0
- data/examples/12_mouse_motion_example.rb +35 -0
- data/examples/12_mouse_scroll_example.rb +36 -0
- data/examples/12_orbit_zoom_example.rb +68 -0
- data/examples/13_joystick_example.rb +80 -0
- data/examples/cubemap/tron_bk.png +0 -0
- data/examples/cubemap/tron_dn.png +0 -0
- data/examples/cubemap/tron_ft.png +0 -0
- data/examples/cubemap/tron_lf.png +0 -0
- data/examples/cubemap/tron_rt.png +0 -0
- data/examples/cubemap/tron_up.png +0 -0
- data/examples/earth.png +0 -0
- data/examples/earth_normal.png +0 -0
- data/examples/example_helper.rb +2 -0
- data/examples/male-02-1noCulling.png +0 -0
- data/examples/male02.mtl +54 -0
- data/examples/male02.obj +13888 -0
- data/examples/moon.png +0 -0
- data/examples/orig_02_-_Defaul1noCulling.png +0 -0
- data/examples/texture.png +0 -0
- data/lib/mittsu.rb +15 -0
- data/lib/mittsu/cameras.rb +4 -0
- data/lib/mittsu/cameras/camera.rb +34 -0
- data/lib/mittsu/cameras/cube_camera.rb +74 -0
- data/lib/mittsu/cameras/orthographic_camera.rb +53 -0
- data/lib/mittsu/cameras/perspective_camera.rb +115 -0
- data/lib/mittsu/constants.rb +160 -0
- data/lib/mittsu/core.rb +10 -0
- data/lib/mittsu/core/buffer_attribute.rb +87 -0
- data/lib/mittsu/core/buffer_geometry.rb +694 -0
- data/lib/mittsu/core/clock.rb +44 -0
- data/lib/mittsu/core/dynamic_buffer_attribute.rb +16 -0
- data/lib/mittsu/core/event_dispatcher.rb +39 -0
- data/lib/mittsu/core/face3.rb +30 -0
- data/lib/mittsu/core/geometry.rb +596 -0
- data/lib/mittsu/core/hash_array.rb +36 -0
- data/lib/mittsu/core/hash_object.rb +19 -0
- data/lib/mittsu/core/object_3d.rb +421 -0
- data/lib/mittsu/core/raycaster.rb +78 -0
- data/lib/mittsu/extras.rb +3 -0
- data/lib/mittsu/extras/geometries.rb +2 -0
- data/lib/mittsu/extras/geometries/box_geometry.rb +108 -0
- data/lib/mittsu/extras/geometries/sphere_geometry.rb +88 -0
- data/lib/mittsu/extras/helpers.rb +1 -0
- data/lib/mittsu/extras/helpers/camera_helper.rb +155 -0
- data/lib/mittsu/extras/image.rb +3 -0
- data/lib/mittsu/extras/image_utils.rb +80 -0
- data/lib/mittsu/lights.rb +7 -0
- data/lib/mittsu/lights/ambient_light.rb +16 -0
- data/lib/mittsu/lights/area_light.rb +24 -0
- data/lib/mittsu/lights/directional_light.rb +131 -0
- data/lib/mittsu/lights/hemisphere_light.rb +29 -0
- data/lib/mittsu/lights/light.rb +21 -0
- data/lib/mittsu/lights/point_light.rb +27 -0
- data/lib/mittsu/lights/spot_light.rb +104 -0
- data/lib/mittsu/loaders.rb +7 -0
- data/lib/mittsu/loaders/cache.rb +53 -0
- data/lib/mittsu/loaders/file_loader.rb +22 -0
- data/lib/mittsu/loaders/image_loader.rb +32 -0
- data/lib/mittsu/loaders/loader.rb +212 -0
- data/lib/mittsu/loaders/loading_manager.rb +17 -0
- data/lib/mittsu/loaders/mtl_loader.rb +242 -0
- data/lib/mittsu/loaders/obj_mtl_loader.rb +225 -0
- data/lib/mittsu/materials.rb +7 -0
- data/lib/mittsu/materials/line_basic_material.rb +39 -0
- data/lib/mittsu/materials/material.rb +156 -0
- data/lib/mittsu/materials/mesh_basic_material.rb +122 -0
- data/lib/mittsu/materials/mesh_face_material.rb +30 -0
- data/lib/mittsu/materials/mesh_lambert_material.rb +126 -0
- data/lib/mittsu/materials/mesh_phong_material.rb +152 -0
- data/lib/mittsu/materials/shader_material.rb +108 -0
- data/lib/mittsu/math.rb +105 -0
- data/lib/mittsu/math/box2.rb +135 -0
- data/lib/mittsu/math/box3.rb +194 -0
- data/lib/mittsu/math/color.rb +252 -0
- data/lib/mittsu/math/color_keywords.rb +151 -0
- data/lib/mittsu/math/euler.rb +182 -0
- data/lib/mittsu/math/frustum.rb +106 -0
- data/lib/mittsu/math/line3.rb +76 -0
- data/lib/mittsu/math/matrix3.rb +163 -0
- data/lib/mittsu/math/matrix4.rb +581 -0
- data/lib/mittsu/math/plane.rb +128 -0
- data/lib/mittsu/math/quaternion.rb +309 -0
- data/lib/mittsu/math/ray.rb +292 -0
- data/lib/mittsu/math/sphere.rb +91 -0
- data/lib/mittsu/math/spline.rb +128 -0
- data/lib/mittsu/math/triangle.rb +121 -0
- data/lib/mittsu/math/vector2.rb +238 -0
- data/lib/mittsu/math/vector3.rb +491 -0
- data/lib/mittsu/math/vector4.rb +414 -0
- data/lib/mittsu/objects.rb +3 -0
- data/lib/mittsu/objects/group.rb +8 -0
- data/lib/mittsu/objects/line.rb +143 -0
- data/lib/mittsu/objects/mesh.rb +243 -0
- data/lib/mittsu/renderers.rb +1 -0
- data/lib/mittsu/renderers/glfw_window.rb +216 -0
- data/lib/mittsu/renderers/opengl/opengl_debug.rb +38 -0
- data/lib/mittsu/renderers/opengl/opengl_program.rb +402 -0
- data/lib/mittsu/renderers/opengl/opengl_shader.rb +58 -0
- data/lib/mittsu/renderers/opengl/opengl_state.rb +207 -0
- data/lib/mittsu/renderers/opengl/plugins/shadow_map_plugin.rb +416 -0
- data/lib/mittsu/renderers/opengl_render_target.rb +87 -0
- data/lib/mittsu/renderers/opengl_renderer.rb +3376 -0
- data/lib/mittsu/renderers/shaders/shader_chunk.rb +12 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/alphamap_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/alphamap_pars_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/alphatest_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/bumpmap_pars_fragment.glsl +40 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/color_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/color_pars_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/color_pars_vertex.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/color_vertex.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/common.glsl +60 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/default_vertex.glsl +15 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/defaultnormal_vertex.glsl +21 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/envmap_fragment.glsl +62 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/envmap_pars_fragment.glsl +21 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/envmap_pars_vertex.glsl +7 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/envmap_vertex.glsl +17 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/fog_fragment.glsl +26 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/fog_pars_fragment.glsl +15 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lightmap_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lightmap_pars_fragment.glsl +6 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lightmap_pars_vertex.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lightmap_vertex.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lights_lambert_pars_vertex.glsl +43 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lights_lambert_vertex.glsl +196 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lights_phong_fragment.glsl +243 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lights_phong_pars_fragment.glsl +58 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lights_phong_pars_vertex.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/lights_phong_vertex.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/linear_to_gamma_fragment.glsl +2 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/logdepthbuf_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/logdepthbuf_pars_fragment.glsl +12 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/logdepthbuf_pars_vertex.glsl +11 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/logdepthbuf_vertex.glsl +15 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/map_fragment.glsl +9 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/map_pars_fragment.glsl +11 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/map_pars_vertex.glsl +6 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/map_particle_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/map_particle_pars_fragment.glsl +6 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/map_vertex.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/morphnormal_vertex.glsl +12 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/morphtarget_pars_vertex.glsl +13 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/morphtarget_vertex.glsl +20 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/normalmap_pars_fragment.glsl +27 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_fragment.glsl +217 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_pars_fragment.glsl +19 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_pars_vertex.glsl +6 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_vertex.glsl +9 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/skinbase_vertex.glsl +8 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/skinning_pars_vertex.glsl +47 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/skinning_vertex.glsl +20 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/skinnormal_vertex.glsl +20 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/specularmap_fragment.glsl +12 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/specularmap_pars_fragment.glsl +5 -0
- data/lib/mittsu/renderers/shaders/shader_chunk/worldpos_vertex.glsl +17 -0
- data/lib/mittsu/renderers/shaders/shader_lib.rb +420 -0
- data/lib/mittsu/renderers/shaders/uniforms_lib.rb +107 -0
- data/lib/mittsu/renderers/shaders/uniforms_utils.rb +31 -0
- data/lib/mittsu/scenes.rb +1 -0
- data/lib/mittsu/scenes/scene.rb +27 -0
- data/lib/mittsu/textures.rb +5 -0
- data/lib/mittsu/textures/compressed_texture.rb +30 -0
- data/lib/mittsu/textures/cube_texture.rb +19 -0
- data/lib/mittsu/textures/data_texture.rb +17 -0
- data/lib/mittsu/textures/texture.rb +92 -0
- data/lib/mittsu/textures/video_texture.rb +17 -0
- data/lib/mittsu/version.rb +4 -0
- data/mittsu.gemspec +31 -0
- 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
|