mittsu 0.2.4 → 0.3.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 +4 -4
- data/lib/mittsu/core/geometry.rb +9 -6
- data/lib/mittsu/extras/geometries.rb +13 -0
- data/lib/mittsu/extras/geometries/circle_geometry.rb +48 -0
- data/lib/mittsu/extras/geometries/cylinder_geometry.rb +145 -0
- data/lib/mittsu/extras/geometries/dodecahedron_geometry.rb +56 -0
- data/lib/mittsu/extras/geometries/icosahedron_geometry.rb +33 -0
- data/lib/mittsu/extras/geometries/lathe_geometry.rb +78 -0
- data/lib/mittsu/extras/geometries/octahedron_geometry.rb +26 -0
- data/lib/mittsu/extras/geometries/parametric_buffer_geometry.rb +92 -0
- data/lib/mittsu/extras/geometries/parametric_geometry.rb +93 -0
- data/lib/mittsu/extras/geometries/plane_buffer_geometry.rb +81 -0
- data/lib/mittsu/extras/geometries/plane_geometry.rb +22 -0
- data/lib/mittsu/extras/geometries/polyhedron_geometry.rb +168 -0
- data/lib/mittsu/extras/geometries/ring_geometry.rb +80 -0
- data/lib/mittsu/extras/geometries/tetrahedron_geometry.rb +26 -0
- data/lib/mittsu/extras/geometries/torus_geometry.rb +63 -0
- data/lib/mittsu/extras/geometries/torus_knot_buffer_geometry.rb +120 -0
- data/lib/mittsu/extras/geometries/torus_knot_geometry.rb +25 -0
- data/lib/mittsu/math/vector.rb +1 -1
- data/lib/mittsu/version.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d75b0ef6c6615fff22f50ab9f3bc438e5c2631d5924f30da913df6403d5ccd0
|
4
|
+
data.tar.gz: 4656d50526e5be69dd19f3f0de87b4f93de5ad0a2e664b6277b023b885d7c3a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4093db783cef7214c92fa5ce723cf84466a4dcd30d1e7a8fe6c5b9ff41f4be13d04eaeb072e30e6e039191d410303adf47c55288429439412e746ba300e05918
|
7
|
+
data.tar.gz: e3bafbb95cff8e606f36e6e456314a75cbfc9c0284022ef73258d40316918fd87dce5b11b21aa1b25802a071b91b413df53de8220f1294eadf892600f79868fd
|
data/lib/mittsu/core/geometry.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'securerandom'
|
2
2
|
require 'mittsu'
|
3
|
+
require 'securerandom'
|
3
4
|
|
4
5
|
module Mittsu
|
5
6
|
class Geometry
|
@@ -19,6 +20,8 @@ module Mittsu
|
|
19
20
|
|
20
21
|
@id = (@@id ||= 1).tap { @@id += 1 }
|
21
22
|
|
23
|
+
@uuid = SecureRandom.uuid
|
24
|
+
|
22
25
|
@name = ''
|
23
26
|
@type = 'Geometry'
|
24
27
|
|
@@ -105,7 +108,7 @@ module Mittsu
|
|
105
108
|
}
|
106
109
|
if indices
|
107
110
|
draw_calls = geometry.draw_calls
|
108
|
-
if !draw_calls.
|
111
|
+
if !draw_calls.empty?
|
109
112
|
draw_calls.each do |draw_call|
|
110
113
|
start = draw_call.start
|
111
114
|
count = draw_call.count
|
@@ -119,7 +122,7 @@ module Mittsu
|
|
119
122
|
end
|
120
123
|
else
|
121
124
|
indices.each_slice(3).with_index do |index|
|
122
|
-
add_face
|
125
|
+
add_face[*index]
|
123
126
|
end
|
124
127
|
end
|
125
128
|
else
|
@@ -130,10 +133,10 @@ module Mittsu
|
|
130
133
|
end
|
131
134
|
end
|
132
135
|
self.compute_face_normals
|
133
|
-
if geometry.
|
136
|
+
if geometry.bounding_box
|
134
137
|
@bounding_box = geometry.bounding_box.clone
|
135
138
|
end
|
136
|
-
if geometry.
|
139
|
+
if geometry.bounding_sphere
|
137
140
|
@bounding_sphere = geometry.bounding_sphere.clone
|
138
141
|
end
|
139
142
|
self
|
@@ -160,12 +163,12 @@ module Mittsu
|
|
160
163
|
end
|
161
164
|
end
|
162
165
|
|
163
|
-
def compute_vertex_normals(
|
166
|
+
def compute_vertex_normals(area_weighted = false)
|
164
167
|
vertices = Array.new(@vertices.length)
|
165
168
|
@vertices.length.times do |v|
|
166
169
|
vertices[v] = Mittsu::Vector3.new
|
167
170
|
end
|
168
|
-
if
|
171
|
+
if area_weighted
|
169
172
|
# vertex normals weighted by triangle areas
|
170
173
|
# http:#www.iquilezles.org/www/articles/normals/normals.htm
|
171
174
|
cb = Mittsu::Vector3.new, ab = Mittsu::Vector3.new
|
@@ -1,2 +1,15 @@
|
|
1
1
|
require 'mittsu/extras/geometries/box_geometry'
|
2
2
|
require 'mittsu/extras/geometries/sphere_geometry'
|
3
|
+
require 'mittsu/extras/geometries/ring_geometry'
|
4
|
+
require 'mittsu/extras/geometries/circle_geometry'
|
5
|
+
require 'mittsu/extras/geometries/cylinder_geometry'
|
6
|
+
require 'mittsu/extras/geometries/dodecahedron_geometry'
|
7
|
+
require 'mittsu/extras/geometries/icosahedron_geometry'
|
8
|
+
require 'mittsu/extras/geometries/octahedron_geometry'
|
9
|
+
require 'mittsu/extras/geometries/tetrahedron_geometry'
|
10
|
+
require 'mittsu/extras/geometries/plane_buffer_geometry'
|
11
|
+
require 'mittsu/extras/geometries/plane_geometry'
|
12
|
+
require 'mittsu/extras/geometries/torus_geometry'
|
13
|
+
require 'mittsu/extras/geometries/torus_knot_geometry'
|
14
|
+
require 'mittsu/extras/geometries/parametric_geometry'
|
15
|
+
require 'mittsu/extras/geometries/lathe_geometry'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
|
4
|
+
module Mittsu
|
5
|
+
class CircleGeometry < Geometry
|
6
|
+
def initialize(radius = 50.0, segments = 8, theta_start = 0.0, theta_length = (Math::PI * 2.0))
|
7
|
+
super()
|
8
|
+
|
9
|
+
@type = 'CircleGeometry'
|
10
|
+
|
11
|
+
@parameters = {
|
12
|
+
radius: radius,
|
13
|
+
segments: segments,
|
14
|
+
theta_start: theta_start,
|
15
|
+
theta_length: theta_length
|
16
|
+
}
|
17
|
+
|
18
|
+
segments = [3, segments].max
|
19
|
+
|
20
|
+
center = Vector3.new
|
21
|
+
center_uv = Vector2.new(0.5, 0.5)
|
22
|
+
|
23
|
+
@vertices << center
|
24
|
+
uvs = [center_uv]
|
25
|
+
|
26
|
+
for i in 0..segments do
|
27
|
+
vertex = Vector3.new
|
28
|
+
segment = theta_start + i.to_f / segments.to_f * theta_length
|
29
|
+
|
30
|
+
vertex.x = radius * Math.cos(segment)
|
31
|
+
vertex.y = radius * Math.sin(segment)
|
32
|
+
|
33
|
+
@vertices << vertex
|
34
|
+
uvs << Vector2.new((vertex.x / radius + 1.0) / 2.0, (vertex.y / radius + 1.0) / 2.0)
|
35
|
+
end
|
36
|
+
|
37
|
+
n = Vector3.new
|
38
|
+
|
39
|
+
for i in 1..segments do
|
40
|
+
@faces << Face3.new(i, i + 1, 0, [n.clone, n.clone, n.clone])
|
41
|
+
@face_vertex_uvs[0] << [uvs[i].clone, uvs[i + 1].clone, center_uv.clone]
|
42
|
+
end
|
43
|
+
|
44
|
+
compute_face_normals
|
45
|
+
@bounding_sphere = Sphere.new(Vector3.new, radius)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
|
4
|
+
module Mittsu
|
5
|
+
class CylinderGeometry < Geometry
|
6
|
+
def initialize(radius_top = 20.0, radius_bottom = 20.0, height = 100.0, radial_segments = 8, height_segments = 1, open_ended = false, theta_start = 0.0, theta_length = (Math::PI * 2.0))
|
7
|
+
super()
|
8
|
+
|
9
|
+
@type = 'CylinderGeometry'
|
10
|
+
|
11
|
+
@parameters = {
|
12
|
+
radius_top: radius_top,
|
13
|
+
radius_bottom: radius_bottom,
|
14
|
+
height: height,
|
15
|
+
radial_segments: radial_segments,
|
16
|
+
height_segments: height_segments,
|
17
|
+
open_ended: open_ended,
|
18
|
+
theta_start: theta_start,
|
19
|
+
theta_length: theta_length
|
20
|
+
}
|
21
|
+
|
22
|
+
height_half = height / 2.0
|
23
|
+
|
24
|
+
index_rows = []
|
25
|
+
uv_rows = []
|
26
|
+
|
27
|
+
for y in 0..height_segments do
|
28
|
+
index_row = []
|
29
|
+
uv_row = []
|
30
|
+
|
31
|
+
v = y.to_f / height_segments.to_f
|
32
|
+
radius = v * (radius_bottom - radius_top) + radius_top
|
33
|
+
|
34
|
+
for x in 0..radial_segments do
|
35
|
+
u = x.to_f / radial_segments
|
36
|
+
|
37
|
+
vertex = Vector3.new
|
38
|
+
vertex.x = radius * Math.sin(u * theta_length + theta_start)
|
39
|
+
vertex.y = -v * height + height_half
|
40
|
+
vertex.z = radius * Math.cos(u * theta_length + theta_start)
|
41
|
+
|
42
|
+
@vertices << vertex
|
43
|
+
|
44
|
+
index_row << (vertices.length - 1)
|
45
|
+
uv_row << Vector2.new(u, 1.0 - v)
|
46
|
+
end
|
47
|
+
|
48
|
+
index_rows << index_row
|
49
|
+
uv_rows << uv_row
|
50
|
+
end
|
51
|
+
|
52
|
+
tan_theta = (radius_bottom - radius_top) / height
|
53
|
+
|
54
|
+
na = nil
|
55
|
+
nb = nil
|
56
|
+
|
57
|
+
for x in 0...radial_segments do
|
58
|
+
if radius_top != 0
|
59
|
+
na = @vertices[index_rows[0][x]].clone
|
60
|
+
nb = @vertices[index_rows[0][x + 1]].clone
|
61
|
+
else
|
62
|
+
na = @vertices[index_rows[1][x]].clone
|
63
|
+
nb = @vertices[index_rows[1][x + 1]].clone
|
64
|
+
end
|
65
|
+
|
66
|
+
na.y = Math.sqrt(na.x * na.x + na.z * na.z) * tan_theta
|
67
|
+
na.normalize
|
68
|
+
|
69
|
+
nb.y = Math.sqrt(nb.x * nb.x + nb.z * nb.z) * tan_theta
|
70
|
+
nb.normalize
|
71
|
+
|
72
|
+
for y in 0...height_segments do
|
73
|
+
v1 = index_rows[y][x]
|
74
|
+
v2 = index_rows[y + 1][x]
|
75
|
+
v3 = index_rows[y + 1][x + 1]
|
76
|
+
v4 = index_rows[y][x + 1]
|
77
|
+
|
78
|
+
n1 = na.clone
|
79
|
+
n2 = na.clone
|
80
|
+
n3 = nb.clone
|
81
|
+
n4 = nb.clone
|
82
|
+
|
83
|
+
uv1 = uv_rows[y][x].clone
|
84
|
+
uv2 = uv_rows[y + 1][x].clone
|
85
|
+
uv3 = uv_rows[y + 1][x + 1].clone
|
86
|
+
uv4 = uv_rows[y][x + 1].clone
|
87
|
+
|
88
|
+
@faces << Face3.new(v1, v2, v4, [n1, n2, n4])
|
89
|
+
@face_vertex_uvs[0] << [uv1, uv2, uv4]
|
90
|
+
|
91
|
+
@faces << Face3.new(v2, v3, v4, [n2.clone, n3, n4.clone])
|
92
|
+
@face_vertex_uvs[0] << [uv2.clone, uv3, uv4.clone]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# top cap
|
97
|
+
|
98
|
+
if !open_ended && radius_top > 0.0
|
99
|
+
@vertices << Vector3.new(0, height_half, 0)
|
100
|
+
|
101
|
+
for x in 0...radial_segments do
|
102
|
+
v1 = index_rows[0][x]
|
103
|
+
v2 = index_rows[0][x + 1]
|
104
|
+
v3 = @vertices.length - 1
|
105
|
+
|
106
|
+
n1 = Vector3.new(0, 1, 0)
|
107
|
+
n2 = Vector3.new(0, 1, 0)
|
108
|
+
n3 = Vector3.new(0, 1, 0)
|
109
|
+
|
110
|
+
uv1 = uv_rows[0][x].clone
|
111
|
+
uv2 = uv_rows[0][x + 1].clone
|
112
|
+
uv3 = Vector2.new(uv2.x, 0)
|
113
|
+
|
114
|
+
@faces << Face3.new(v1, v2, v3, [n1, n2, n3])
|
115
|
+
@face_vertex_uvs[0] << [uv1, uv2, uv3]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# bottom cap
|
120
|
+
|
121
|
+
if !open_ended && radius_bottom > 0.0
|
122
|
+
@vertices << Vector3.new(0, -height_half, 0)
|
123
|
+
|
124
|
+
for x in 0...radial_segments do
|
125
|
+
v1 = index_rows[height_segments][x + 1]
|
126
|
+
v2 = index_rows[height_segments][x]
|
127
|
+
v3 = @vertices.length - 1
|
128
|
+
|
129
|
+
n1 = Vector3.new(0, -1, 0)
|
130
|
+
n2 = Vector3.new(0, -1, 0)
|
131
|
+
n3 = Vector3.new(0, -1, 0)
|
132
|
+
|
133
|
+
uv1 = uv_rows[height_segments][x].clone
|
134
|
+
uv2 = uv_rows[height_segments][x + 1].clone
|
135
|
+
uv3 = Vector2.new(uv2.x, 0)
|
136
|
+
|
137
|
+
@faces << Face3.new(v1, v2, v3, [n1, n2, n3])
|
138
|
+
@face_vertex_uvs[0] << [uv1, uv2, uv3]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
compute_face_normals
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
require 'mittsu/extras/geometries/polyhedron_geometry'
|
4
|
+
|
5
|
+
module Mittsu
|
6
|
+
class DodecahedronGeometry < PolyhedronGeometry
|
7
|
+
def initialize(radius = 1.0, detail = 0)
|
8
|
+
t = (1.0 + Math.sqrt(5.0)) / 2.0
|
9
|
+
r = 1.0 / t
|
10
|
+
|
11
|
+
vertices = [
|
12
|
+
# (±1, ±1, ±1)
|
13
|
+
-1, -1, -1, -1, -1, 1,
|
14
|
+
-1, 1, -1, -1, 1, 1,
|
15
|
+
1, -1, -1, 1, -1, 1,
|
16
|
+
1, 1, -1, 1, 1, 1,
|
17
|
+
|
18
|
+
# (0, ±1/φ, ±φ)
|
19
|
+
0, -r, -t, 0, -r, t,
|
20
|
+
0, r, -t, 0, r, t,
|
21
|
+
|
22
|
+
# (±1/φ, ±φ, 0)
|
23
|
+
-r, -t, 0, -r, t, 0,
|
24
|
+
r, -t, 0, r, t, 0,
|
25
|
+
|
26
|
+
# (±φ, 0, ±1/φ)
|
27
|
+
-t, 0, -r, t, 0, -r,
|
28
|
+
-t, 0, r, t, 0, r
|
29
|
+
]
|
30
|
+
|
31
|
+
indices = [
|
32
|
+
3, 11, 7, 3, 7, 15, 3, 15, 13,
|
33
|
+
7, 19, 17, 7, 17, 6, 7, 6, 15,
|
34
|
+
17, 4, 8, 17, 8, 10, 17, 10, 6,
|
35
|
+
8, 0, 16, 8, 16, 2, 8, 2, 10,
|
36
|
+
0, 12, 1, 0, 1, 18, 0, 18, 16,
|
37
|
+
6, 10, 2, 6, 2, 13, 6, 13, 15,
|
38
|
+
2, 16, 18, 2, 18, 3, 2, 3, 13,
|
39
|
+
18, 1, 9, 18, 9, 11, 18, 11, 3,
|
40
|
+
4, 14, 12, 4, 12, 0, 4, 0, 8,
|
41
|
+
11, 9, 5, 11, 5, 19, 11, 19, 7,
|
42
|
+
19, 5, 14, 19, 14, 4, 19, 4, 17,
|
43
|
+
1, 12, 14, 1, 14, 5, 1, 5, 9
|
44
|
+
]
|
45
|
+
|
46
|
+
super(vertices, indices, radius, detail)
|
47
|
+
|
48
|
+
@type = 'DodecahedronGeometry'
|
49
|
+
|
50
|
+
@parameters = {
|
51
|
+
radius: radius,
|
52
|
+
detail: detail
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
require 'mittsu/extras/geometries/polyhedron_geometry'
|
4
|
+
|
5
|
+
module Mittsu
|
6
|
+
class IcosahedronGeometry < PolyhedronGeometry
|
7
|
+
def initialize(radius = 1.0, detail = 0)
|
8
|
+
t = (1.0 + Math.sqrt(5)) / 2.0
|
9
|
+
|
10
|
+
vertices = [
|
11
|
+
- 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,
|
12
|
+
0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,
|
13
|
+
t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1
|
14
|
+
]
|
15
|
+
|
16
|
+
indices = [
|
17
|
+
0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,
|
18
|
+
1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
|
19
|
+
3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,
|
20
|
+
4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1
|
21
|
+
]
|
22
|
+
|
23
|
+
super(vertices, indices, radius, detail)
|
24
|
+
|
25
|
+
@type = 'IcosahedronGeometry'
|
26
|
+
|
27
|
+
@parameters = {
|
28
|
+
radius: radius,
|
29
|
+
detail: detail
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
|
4
|
+
module Mittsu
|
5
|
+
class LatheGeometry < Geometry
|
6
|
+
def initialize(points, segments = 12, phi_start = 0.0, phi_length = (Math::PI * 2.0))
|
7
|
+
super()
|
8
|
+
|
9
|
+
@type = 'LatheGeometry'
|
10
|
+
|
11
|
+
@parameters = {
|
12
|
+
points: points,
|
13
|
+
segments: segments,
|
14
|
+
phi_start: phi_start,
|
15
|
+
phi_length: phi_length
|
16
|
+
}
|
17
|
+
|
18
|
+
inverse_point_length = 1.0 / (points.length.to_f - 1.0)
|
19
|
+
inverse_segments = 1.0 / segments.to_f
|
20
|
+
|
21
|
+
for i in 0..segments do
|
22
|
+
phi = phi_start + i.to_f * inverse_segments * phi_length
|
23
|
+
|
24
|
+
c = Math.cos(phi)
|
25
|
+
s = Math.sin(phi)
|
26
|
+
|
27
|
+
for j in 0...points.length do
|
28
|
+
pt = points[j]
|
29
|
+
|
30
|
+
vertex = Vector3.new
|
31
|
+
|
32
|
+
vertex.x = c * pt.x
|
33
|
+
vertex.y = pt.y
|
34
|
+
vertex.z = s * pt.x
|
35
|
+
|
36
|
+
@vertices << vertex
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
np = points.length
|
41
|
+
|
42
|
+
for i in 0...segments do
|
43
|
+
for j in 0...(points.length - 1) do
|
44
|
+
base = j + np * i
|
45
|
+
a = base
|
46
|
+
b = base + np
|
47
|
+
c = base + 1 + np
|
48
|
+
d = base + 1
|
49
|
+
|
50
|
+
u0 = i.to_f * inverse_segments
|
51
|
+
v0 = j.to_f * inverse_point_length
|
52
|
+
u1 = u0 + inverse_segments
|
53
|
+
v1 = v0 + inverse_point_length
|
54
|
+
|
55
|
+
@faces << Face3.new(a, b, d)
|
56
|
+
|
57
|
+
@face_vertex_uvs[0] << [
|
58
|
+
Vector2.new(u0, v0),
|
59
|
+
Vector2.new(u1, v0),
|
60
|
+
Vector2.new(u0, v1)
|
61
|
+
]
|
62
|
+
|
63
|
+
@faces << Face3.new(b, c, d)
|
64
|
+
|
65
|
+
@face_vertex_uvs[0] << [
|
66
|
+
Vector2.new(u1, v0),
|
67
|
+
Vector2.new(u1, v1),
|
68
|
+
Vector2.new(u0, v1)
|
69
|
+
]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
merge_vertices
|
74
|
+
compute_face_normals
|
75
|
+
compute_vertex_normals
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
require 'mittsu/extras/geometries/polyhedron_geometry'
|
4
|
+
|
5
|
+
module Mittsu
|
6
|
+
class OctahedronGeometry < PolyhedronGeometry
|
7
|
+
def initialize(radius = 1.0, detail = 0)
|
8
|
+
vertices = [
|
9
|
+
1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1
|
10
|
+
]
|
11
|
+
|
12
|
+
indices = [
|
13
|
+
0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2
|
14
|
+
]
|
15
|
+
|
16
|
+
super(vertices, indices, radius, detail)
|
17
|
+
|
18
|
+
@type = 'OctahedronGeometry'
|
19
|
+
|
20
|
+
@parameters = {
|
21
|
+
radius: radius,
|
22
|
+
detail: detail
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Mittsu
|
2
|
+
class ParametricBufferGeometry < BufferGeometry
|
3
|
+
EPS = 0.00001
|
4
|
+
|
5
|
+
def initialize(func, slices, stacks)
|
6
|
+
super()
|
7
|
+
|
8
|
+
@type = 'ParametricBufferGeometry'
|
9
|
+
|
10
|
+
@parameters = {
|
11
|
+
func: func,
|
12
|
+
slices: slices,
|
13
|
+
stacks: stacks
|
14
|
+
}
|
15
|
+
|
16
|
+
# buffers
|
17
|
+
|
18
|
+
indices = []
|
19
|
+
vertices = []
|
20
|
+
normals = []
|
21
|
+
uvs = []
|
22
|
+
|
23
|
+
normal = Vector3.new
|
24
|
+
|
25
|
+
p0 = Vector3.new
|
26
|
+
p1 = Vector3.new
|
27
|
+
|
28
|
+
pu = Vector3.new
|
29
|
+
pv = Vector3.new
|
30
|
+
|
31
|
+
# generate vertices, normals, and uvs
|
32
|
+
|
33
|
+
slice_count = slices + 1
|
34
|
+
|
35
|
+
for i in 0..stacks do
|
36
|
+
v = i.to_f / stacks.to_f
|
37
|
+
|
38
|
+
for j in 0..slices do
|
39
|
+
u = j.to_f / slices.to_f
|
40
|
+
|
41
|
+
# vertex
|
42
|
+
func.call(u, v, p0)
|
43
|
+
vertices += p0.elements
|
44
|
+
|
45
|
+
# normal
|
46
|
+
|
47
|
+
# approximate tangent vectors via finite differences
|
48
|
+
if u - EPS >= 0
|
49
|
+
func.call(u - EPS, v, p1)
|
50
|
+
pu.sub_vectors(p0, p1)
|
51
|
+
else
|
52
|
+
func.call(u + EPS, v, p1)
|
53
|
+
pu.sub_vectors(p1, p0)
|
54
|
+
end
|
55
|
+
|
56
|
+
if v - EPS >= 0
|
57
|
+
func.call(u, v - EPS, p1)
|
58
|
+
pv.sub_vectors(p0, p1)
|
59
|
+
else
|
60
|
+
func.call(u, v + EPS, p1)
|
61
|
+
pv.sub_vectors(p1, p0)
|
62
|
+
end
|
63
|
+
|
64
|
+
# cross product of tangent vectors returns surface normal
|
65
|
+
normal.cross_vectors(pu, pv).normalize
|
66
|
+
normals += normal.elements
|
67
|
+
|
68
|
+
# uv
|
69
|
+
uvs << u << v
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
for i in 0...stacks do
|
74
|
+
for j in 0...slices do
|
75
|
+
a = i * slice_count + j
|
76
|
+
b = i * slice_count + j + 1
|
77
|
+
c = (i + 1) * slice_count + j + 1
|
78
|
+
d = (i + 1) * slice_count + j
|
79
|
+
|
80
|
+
# faces one and two
|
81
|
+
indices << a << b << d
|
82
|
+
indices << b << c << d
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
self[:index] = BufferAttribute.new(indices, 1)
|
87
|
+
self[:position] = BufferAttribute.new(vertices, 3)
|
88
|
+
self[:normal] = BufferAttribute.new(normals, 3)
|
89
|
+
self[:uv] = BufferAttribute.new(uvs, 2)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'mittsu/extras/geometries/parametric_buffer_geometry'
|
2
|
+
|
3
|
+
module Mittsu
|
4
|
+
class ParametricGeometry < Geometry
|
5
|
+
def initialize(func, slices, stacks)
|
6
|
+
super()
|
7
|
+
|
8
|
+
@type = 'ParametricGeometry'
|
9
|
+
|
10
|
+
@parameters = {
|
11
|
+
func: func,
|
12
|
+
slices: slices,
|
13
|
+
stacks: stacks
|
14
|
+
}
|
15
|
+
|
16
|
+
from_buffer_geometry(ParametricBufferGeometry.new(func, slices, stacks))
|
17
|
+
merge_vertices
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.klein
|
21
|
+
-> (v, u, target = Vector3.new) {
|
22
|
+
u *= Math::PI
|
23
|
+
v *= 2.0 * Math::PI
|
24
|
+
|
25
|
+
u = u * 2.0
|
26
|
+
x = nil
|
27
|
+
y = nil
|
28
|
+
z = nil
|
29
|
+
|
30
|
+
if u < Math::PI
|
31
|
+
x = 3.0 * Math.cos(u) * (1.0 + Math.sin(u)) + (2.0 * (1.0 - Math.cos(u) / 2.0)) * Math.cos(u) * Math.cos(v)
|
32
|
+
z = -8.0 * Math.sin(u) - 2.0 * (1.0 - Math.cos(u) / 2.0) * Math.sin(u) * Math.cos(v)
|
33
|
+
else
|
34
|
+
x = 3.0 * Math.cos(u) * (1.0 + Math.sin(u)) + (2.0 * (1.0 - Math.cos(u) / 2.0)) * Math.cos(v + Math::PI)
|
35
|
+
z = -8.0 * Math.sin(u)
|
36
|
+
end
|
37
|
+
|
38
|
+
y = -2.0 * (1.0 - Math.cos(u) / 2.0) * Math.sin(v)
|
39
|
+
|
40
|
+
target.set(x, y, z)
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.plane(width, height)
|
45
|
+
-> (u, v, target = Vector3.new) {
|
46
|
+
x = u.to_f * width.to_f
|
47
|
+
y = 0.0
|
48
|
+
z = v.to_f * height.to_f
|
49
|
+
|
50
|
+
target.set(x, y, z)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.mobius
|
55
|
+
-> (u, t, target = Vector3.new) {
|
56
|
+
# flat mobius strip
|
57
|
+
# http://www.wolframalpha.com/input/?i=M%C3%B6bius+strip+parametric+equations&lk=1&a=ClashPrefs_*Surface.MoebiusStrip.SurfaceProperty.ParametricEquations-
|
58
|
+
u = u - 0.5
|
59
|
+
v = 2.0 * Math::PI * t
|
60
|
+
|
61
|
+
a = 2.0
|
62
|
+
|
63
|
+
x = Math.cos(v) * (a + u * Math.cos(v / 2.0))
|
64
|
+
y = Math.sin(v) * (a + u * Math.cos(v / 2.0))
|
65
|
+
z = u * Math.sin(v / 2)
|
66
|
+
|
67
|
+
target.set(x, y, z)
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.mobius3d
|
72
|
+
-> (u, t, target = Vector3.new) {
|
73
|
+
# volumetric mobius strip
|
74
|
+
|
75
|
+
u *= Math::PI
|
76
|
+
t *= 2.0 * Math::PI
|
77
|
+
|
78
|
+
u = u * 2.0
|
79
|
+
phi = u / 2.0
|
80
|
+
major = 2.25
|
81
|
+
a = 0.125
|
82
|
+
b = 0.65
|
83
|
+
|
84
|
+
x = a * Math.cos(t) * Math.cos(phi) - b * Math.sin(t) * Math.sin(phi)
|
85
|
+
z = a * Math.cos(t) * Math.sin(phi) + b * Math.sin(t) * Math.cos(phi)
|
86
|
+
y = (major + x) * Math.sin(u)
|
87
|
+
x = (major + x) * Math.cos(u)
|
88
|
+
|
89
|
+
target.set(x, y, z)
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Mittsu
|
2
|
+
class PlaneBufferGeometry < BufferGeometry
|
3
|
+
def initialize(width, height, width_segments = 1, height_segments = 1)
|
4
|
+
super()
|
5
|
+
|
6
|
+
@type = 'PlaneBufferGeometry'
|
7
|
+
|
8
|
+
@parameters = {
|
9
|
+
width: width,
|
10
|
+
height: height,
|
11
|
+
width_segments: width_segments,
|
12
|
+
height_segments: height_segments
|
13
|
+
}
|
14
|
+
|
15
|
+
width_half = width / 2.0
|
16
|
+
height_half = height / 2.0
|
17
|
+
|
18
|
+
grid_x = width_segments || 1
|
19
|
+
grid_y = height_segments || 1
|
20
|
+
|
21
|
+
grid_x1 = grid_x + 1
|
22
|
+
grid_y1 = grid_y + 1
|
23
|
+
|
24
|
+
segment_width = width.to_f / grid_x.to_f
|
25
|
+
segment_height = height.to_f / grid_y.to_f
|
26
|
+
|
27
|
+
vertices = Array.new(grid_x1 * grid_y1 * 3) # Float32Array
|
28
|
+
normals = Array.new(grid_x1 * grid_y1 * 3) #Float32Array
|
29
|
+
uvs = Array.new(grid_x1 * grid_y1 * 2) # Float32Array
|
30
|
+
|
31
|
+
offset = 0
|
32
|
+
offset2 = 0
|
33
|
+
|
34
|
+
for iy in 0...grid_y1 do
|
35
|
+
y = iy.to_f * segment_height - height_half
|
36
|
+
for ix in 0...grid_x1 do
|
37
|
+
x = ix.to_f * segment_width - width_half
|
38
|
+
|
39
|
+
vertices[offset] = x
|
40
|
+
vertices[offset + 1] = -y
|
41
|
+
|
42
|
+
normals[offset + 2] = 1.0
|
43
|
+
|
44
|
+
uvs[offset2] = ix.to_f / grid_x.to_f
|
45
|
+
uvs[offset2 + 1] = 1.0 - (iy.to_f / grid_y.to_f)
|
46
|
+
|
47
|
+
offset += 3
|
48
|
+
offset2 += 2
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
offset = 0
|
53
|
+
|
54
|
+
indices = Array.new(grid_x * grid_y * 6) # ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )
|
55
|
+
|
56
|
+
for iy in 0...grid_y do
|
57
|
+
for ix in 0...grid_x do
|
58
|
+
a = ix + grid_x1 * iy
|
59
|
+
b = ix + grid_x1 * (iy + 1)
|
60
|
+
c = (ix + 1) + grid_x1 * (iy + 1)
|
61
|
+
d = (ix + 1) + grid_x1 * iy
|
62
|
+
|
63
|
+
indices[offset ] = a
|
64
|
+
indices[offset + 1] = b
|
65
|
+
indices[offset + 2] = d
|
66
|
+
|
67
|
+
indices[offset + 3] = b
|
68
|
+
indices[offset + 4] = c
|
69
|
+
indices[offset + 5] = d
|
70
|
+
|
71
|
+
offset += 6
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
self[:index] = BufferAttribute.new(indices, 1)
|
76
|
+
self[:position] = BufferAttribute.new(vertices, 3)
|
77
|
+
self[:normal] = BufferAttribute.new(normals, 3)
|
78
|
+
self[:uv] = BufferAttribute.new(uvs, 2)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'mittsu/extras/geometries/plane_buffer_geometry'
|
2
|
+
|
3
|
+
module Mittsu
|
4
|
+
class PlaneGeometry < Geometry
|
5
|
+
def initialize(width, height, width_segments = 1, height_segments = 1)
|
6
|
+
puts 'Mittsu::PlaneGeometry: Consider using Mittsu::PlaneBufferGeometry for lower memory footprint.'
|
7
|
+
|
8
|
+
super()
|
9
|
+
|
10
|
+
@type = 'PlaneGeometry'
|
11
|
+
|
12
|
+
@parameters = {
|
13
|
+
width: width,
|
14
|
+
height: height,
|
15
|
+
width_segments: width_segments,
|
16
|
+
height_segments: height_segments
|
17
|
+
}
|
18
|
+
|
19
|
+
from_buffer_geometry(PlaneBufferGeometry.new(width, height, width_segments, height_segments))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
|
4
|
+
module Mittsu
|
5
|
+
class PolyhedronGeometry < Geometry
|
6
|
+
def initialize(vertices, indices, radius = 1.0, detail = 0)
|
7
|
+
super()
|
8
|
+
|
9
|
+
@type = 'PolyhedronGeometry'
|
10
|
+
|
11
|
+
@parameters = {
|
12
|
+
vertices: vertices,
|
13
|
+
indices: indices,
|
14
|
+
radius: radius,
|
15
|
+
detail: detail
|
16
|
+
}
|
17
|
+
|
18
|
+
for i in (0...vertices.length).step(3) do
|
19
|
+
prepare(Vector3.new(vertices[i], vertices[i + 1], vertices[i + 2]))
|
20
|
+
end
|
21
|
+
|
22
|
+
faces = []
|
23
|
+
|
24
|
+
i = 0
|
25
|
+
j = 0
|
26
|
+
while i < indices.length do
|
27
|
+
v1 = @vertices[indices[i]]
|
28
|
+
v2 = @vertices[indices[i + 1]]
|
29
|
+
v3 = @vertices[indices[i + 2]]
|
30
|
+
binding.irb if v1.nil? || v2.nil? || v3.nil?
|
31
|
+
|
32
|
+
faces[j] = Face3.new(v1.index, v2.index, v3.index, [v1.clone, v2.clone, v3.clone])
|
33
|
+
|
34
|
+
i += 3
|
35
|
+
j += 1
|
36
|
+
end
|
37
|
+
|
38
|
+
@centroid = Vector3.new
|
39
|
+
|
40
|
+
for i in 0...faces.length do
|
41
|
+
subdivide(faces[i], detail)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Handle case when face straddles the seam
|
45
|
+
|
46
|
+
@face_vertex_uvs[0].each do |uv0, uv1, uv2|
|
47
|
+
x0 = uv0.x
|
48
|
+
x1 = uv1.x
|
49
|
+
x2 = uv2.x
|
50
|
+
|
51
|
+
max = [x0, x1, x2].max
|
52
|
+
min = [x0, x1, x2].min
|
53
|
+
|
54
|
+
if max > 0.9 && min < 0.1 # 0.9 is somewhat arbitrary
|
55
|
+
uv0.x += 1.0 if x0 < 0.2
|
56
|
+
uv1.x += 1.0 if x1 < 0.2
|
57
|
+
uv2.x += 1.0 if x2 < 0.2
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Apply radius
|
62
|
+
|
63
|
+
@vertices.each do |v|
|
64
|
+
v.multiply_scalar(radius)
|
65
|
+
end
|
66
|
+
|
67
|
+
merge_vertices
|
68
|
+
|
69
|
+
compute_face_normals
|
70
|
+
@bounding_sphere = Sphere.new(Vector3.new, radius)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# Project vector onto sphere's surface
|
76
|
+
def prepare(vector)
|
77
|
+
vertex = vector.normalize.clone
|
78
|
+
vertex.index = @vertices.push(vertex).length - 1
|
79
|
+
|
80
|
+
# Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
|
81
|
+
u = azimuth(vector) / 2.0 / Math::PI + 0.5
|
82
|
+
v = inclination(vector) / Math::PI + 0.5
|
83
|
+
vertex.uv = Vector2.new(u, 1.0 - v)
|
84
|
+
|
85
|
+
vertex
|
86
|
+
end
|
87
|
+
|
88
|
+
# Approximate a curved face with recursively sub-divided triangles.
|
89
|
+
def make(v1, v2, v3)
|
90
|
+
face = Face3.new(v1.index, v2.index, v3.index, [v1.clone, v2.clone, v3.clone])
|
91
|
+
@faces << face
|
92
|
+
|
93
|
+
@centroid.copy(v1).add(v2).add(v3).divide_scalar(3)
|
94
|
+
|
95
|
+
azi = azimuth(@centroid)
|
96
|
+
|
97
|
+
@face_vertex_uvs[0] << [
|
98
|
+
correct_uv(v1.uv, v1, azi),
|
99
|
+
correct_uv(v2.uv, v2, azi),
|
100
|
+
correct_uv(v3.uv, v3, azi)
|
101
|
+
]
|
102
|
+
end
|
103
|
+
|
104
|
+
# Analytically subdivide a face to the required detail level.
|
105
|
+
def subdivide(face, detail)
|
106
|
+
cols = 2.0 ** detail
|
107
|
+
a = prepare(@vertices[face.a])
|
108
|
+
b = prepare(@vertices[face.b])
|
109
|
+
c = prepare(@vertices[face.c])
|
110
|
+
v = []
|
111
|
+
|
112
|
+
# Construct all of the vertices for this subdivision.
|
113
|
+
for i in 0..cols do
|
114
|
+
v[i] = []
|
115
|
+
|
116
|
+
aj = prepare(a.clone.lerp(c, i.to_f / cols.to_f))
|
117
|
+
bj = prepare(b.clone.lerp(c, i.to_f / cols.to_f))
|
118
|
+
rows = cols - i
|
119
|
+
|
120
|
+
for j in 0..rows do
|
121
|
+
v[i][j] = if j.zero? && i == cols
|
122
|
+
aj
|
123
|
+
else
|
124
|
+
prepare(aj.clone.lerp(bj, j.to_f / rows.to_f))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Construct all of the faces
|
130
|
+
for i in 0...cols do
|
131
|
+
for j in (0...(2 * (cols - i) - 1)) do
|
132
|
+
k = j/2
|
133
|
+
|
134
|
+
if j.even?
|
135
|
+
make(
|
136
|
+
v[i][k + 1],
|
137
|
+
v[i + 1][k],
|
138
|
+
v[i][k]
|
139
|
+
)
|
140
|
+
else
|
141
|
+
make(
|
142
|
+
v[i][k + 1],
|
143
|
+
v[i + 1][k + 1],
|
144
|
+
v[i + 1][k]
|
145
|
+
)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Angle around the Y axis, counter-clockwise when looking from above.
|
152
|
+
def azimuth(vector)
|
153
|
+
Math.atan2(vector.z, -vector.x)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Angle above the XZ plane.
|
157
|
+
def inclination(vector)
|
158
|
+
Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z))
|
159
|
+
end
|
160
|
+
|
161
|
+
# Texture fixing helper. Spheres have some odd behaviours.
|
162
|
+
def correct_uv(uv, vector, azimuth)
|
163
|
+
return Vector2.new(uv.x - 1.0, uv.y) if azimuth < 0
|
164
|
+
return Vector2.new(azimuth / 2.0 / Math::PI + 0.5, uv.y) if vector.x.zero? && vector.z.zero?
|
165
|
+
uv.clone
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
|
4
|
+
module Mittsu
|
5
|
+
class RingGeometry < Geometry
|
6
|
+
def initialize(inner_radius = 0.0, outer_radius = 50.0, theta_segments = 8, phi_segments = 8, theta_start = 0.0, theta_length = (Math::PI * 2.0))
|
7
|
+
super()
|
8
|
+
|
9
|
+
@type = 'RingGeometry'
|
10
|
+
|
11
|
+
@parameters = {
|
12
|
+
inner_radius: inner_radius,
|
13
|
+
outer_radius: outer_radius,
|
14
|
+
theta_segments: theta_segments,
|
15
|
+
phi_segments: phi_segments,
|
16
|
+
theta_start: theta_start,
|
17
|
+
theta_length: theta_length
|
18
|
+
}
|
19
|
+
|
20
|
+
theta_segments = [3, theta_segments].max
|
21
|
+
phi_segments = [1, phi_segments].max
|
22
|
+
|
23
|
+
uvs = []
|
24
|
+
radius = inner_radius
|
25
|
+
radius_step = ((outer_radius - inner_radius) / phi_segments.to_f)
|
26
|
+
|
27
|
+
for i in 0..phi_segments do # concentric circles inside ring
|
28
|
+
for o in 0..theta_segments do # number of segments per circle
|
29
|
+
vertex = Vector3.new
|
30
|
+
segment = theta_start + o.to_f / theta_segments.to_f * theta_length
|
31
|
+
vertex.x = radius * Math.cos(segment)
|
32
|
+
vertex.y = radius * Math.sin(segment)
|
33
|
+
|
34
|
+
@vertices << vertex
|
35
|
+
uvs << Vector2.new((vertex.x / outer_radius + 1.0) / 2.0, (vertex.y / outer_radius + 1.0) / 2.0)
|
36
|
+
end
|
37
|
+
|
38
|
+
radius += radius_step
|
39
|
+
end
|
40
|
+
|
41
|
+
n = Vector3.new(0.0, 0.0, 1.0)
|
42
|
+
|
43
|
+
for i in 0...phi_segments do # concentric circles inside ring
|
44
|
+
theta_segment = i * (theta_segments + 1)
|
45
|
+
|
46
|
+
for o in 0...theta_segments do # number of segments per circle
|
47
|
+
segment = o + theta_segment
|
48
|
+
|
49
|
+
v1 = segment
|
50
|
+
v2 = segment + theta_segments + 1
|
51
|
+
v3 = segment + theta_segments + 2
|
52
|
+
|
53
|
+
@faces << Face3.new(v1, v2, v3, [n.clone, n.clone, n.clone])
|
54
|
+
@face_vertex_uvs[0] << [uvs[v1].clone, uvs[v2].clone, uvs[v3].clone]
|
55
|
+
|
56
|
+
v1 = segment
|
57
|
+
v2 = segment + theta_segments + 2
|
58
|
+
v3 = segment + 1
|
59
|
+
|
60
|
+
@faces << Face3.new(v1, v2, v3, [n.clone, n.clone, n.clone])
|
61
|
+
@face_vertex_uvs[0] << [uvs[v1].clone, uvs[v2].clone, uvs[v3].clone]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
compute_face_normals
|
66
|
+
@bounding_sphere = Sphere.new(Vector3.new, radius)
|
67
|
+
end
|
68
|
+
|
69
|
+
def clone
|
70
|
+
RingGeometry.new(
|
71
|
+
@parameters[:inner_radius],
|
72
|
+
@parameters[:outer_radius],
|
73
|
+
@parameters[:theta_segments],
|
74
|
+
@parameters[:phi_segments],
|
75
|
+
@parameters[:theta_start],
|
76
|
+
@parameters[:theta_length]
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
require 'mittsu/extras/geometries/polyhedron_geometry'
|
4
|
+
|
5
|
+
module Mittsu
|
6
|
+
class TetrahedronGeometry < PolyhedronGeometry
|
7
|
+
def initialize(radius = 1.0, detail = 0)
|
8
|
+
vertices = [
|
9
|
+
1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1
|
10
|
+
]
|
11
|
+
|
12
|
+
indices = [
|
13
|
+
2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
|
14
|
+
]
|
15
|
+
|
16
|
+
super(vertices, indices, radius, detail)
|
17
|
+
|
18
|
+
@type = 'TetrahedronGeometry'
|
19
|
+
|
20
|
+
@parameters = {
|
21
|
+
radius: radius,
|
22
|
+
detail: detail
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
|
4
|
+
module Mittsu
|
5
|
+
class TorusGeometry < Geometry
|
6
|
+
def initialize(radius = 100.0, tube = 40.0, radial_segments = 8, tubular_segments = 6, arc = (Math::PI * 2.0))
|
7
|
+
super()
|
8
|
+
|
9
|
+
@type = 'TorusGeometry'
|
10
|
+
|
11
|
+
@parameters = {
|
12
|
+
radius: radius,
|
13
|
+
tube: tube,
|
14
|
+
radial_segments: radial_segments,
|
15
|
+
tubular_segments: tubular_segments,
|
16
|
+
arc: arc
|
17
|
+
}
|
18
|
+
|
19
|
+
center = Vector3.new
|
20
|
+
uvs = []
|
21
|
+
normals = []
|
22
|
+
|
23
|
+
for j in 0..radial_segments do
|
24
|
+
for i in 0..tubular_segments do
|
25
|
+
u = i.to_f / tubular_segments * arc
|
26
|
+
v = j.to_f / radial_segments * Math::PI * 2.0
|
27
|
+
|
28
|
+
center.x = radius * Math.cos(u)
|
29
|
+
center.y = radius * Math.sin(u)
|
30
|
+
|
31
|
+
vertex = Vector3.new
|
32
|
+
vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u)
|
33
|
+
vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u)
|
34
|
+
vertex.z = tube * Math.sin(v)
|
35
|
+
|
36
|
+
@vertices << vertex
|
37
|
+
|
38
|
+
uvs << Vector2.new(i.to_f / tubular_segments, j.to_f / radial_segments)
|
39
|
+
normals << vertex.clone.sub(center).normalize
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
for j in 1..radial_segments do
|
44
|
+
for i in 1..tubular_segments do
|
45
|
+
a = (tubular_segments + 1) * j + i - 1
|
46
|
+
b = (tubular_segments + 1) * (j - 1) + i - 1
|
47
|
+
c = (tubular_segments + 1) * (j - 1) + i
|
48
|
+
d = (tubular_segments + 1) * j + i
|
49
|
+
|
50
|
+
face = Face3.new(a, b, d, [normals[a].clone, normals[b].clone, normals[d].clone])
|
51
|
+
@faces << face
|
52
|
+
@face_vertex_uvs[0] << [uvs[a].clone, uvs[b].clone, uvs[d].clone]
|
53
|
+
|
54
|
+
face = Face3.new(b, c, d, [normals[b].clone, normals[c].clone, normals[d].clone])
|
55
|
+
@faces << face
|
56
|
+
@face_vertex_uvs[0] << [uvs[b].clone, uvs[c].clone, uvs[d].clone]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
compute_face_normals
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Mittsu
|
2
|
+
class TorusKnotBufferGeometry < BufferGeometry
|
3
|
+
def initialize(radius = 100.0, tube = 40.0, radial_segments = 64, tubular_segments = 8, p_val = 2, q_val = 3)
|
4
|
+
super()
|
5
|
+
|
6
|
+
@type = 'TorusKnotBufferGeometry'
|
7
|
+
|
8
|
+
@parameters = {
|
9
|
+
radius: radius,
|
10
|
+
tube: tube,
|
11
|
+
radial_segments: radial_segments,
|
12
|
+
tubular_segments: tubular_segments,
|
13
|
+
p_val: p_val,
|
14
|
+
q_val: q_val
|
15
|
+
}
|
16
|
+
|
17
|
+
# buffers
|
18
|
+
|
19
|
+
indices = []
|
20
|
+
vertices = []
|
21
|
+
normals = []
|
22
|
+
uvs = []
|
23
|
+
|
24
|
+
# helper variables
|
25
|
+
|
26
|
+
vertex = Vector3.new
|
27
|
+
normal = Vector3.new
|
28
|
+
|
29
|
+
p1 = Vector3.new
|
30
|
+
p2 = Vector3.new
|
31
|
+
|
32
|
+
b = Vector3.new
|
33
|
+
t = Vector3.new
|
34
|
+
n = Vector3.new
|
35
|
+
|
36
|
+
# generate vertices, normals and uvs
|
37
|
+
|
38
|
+
for i in 0..tubular_segments do
|
39
|
+
# the radian "u" is used to calculate the position on the torus curve of the current tubular segement
|
40
|
+
u = i.to_f / tubular_segments.to_f * p_val.to_f * Math::PI * 2.0
|
41
|
+
|
42
|
+
# now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.
|
43
|
+
# these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions
|
44
|
+
calculate_position_on_curve(u, p_val, q_val, radius, p1)
|
45
|
+
calculate_position_on_curve(u + 0.01, p_val, q_val, radius, p2)
|
46
|
+
|
47
|
+
# calculate orthonormal basis
|
48
|
+
t.sub_vectors(p2, p1)
|
49
|
+
n.add_vectors(p2, p1)
|
50
|
+
b.cross_vectors(t, n)
|
51
|
+
n.cross_vectors(b, t)
|
52
|
+
|
53
|
+
# normalize B, N. T can be ignored, we don't use it
|
54
|
+
b.normalize
|
55
|
+
n.normalize
|
56
|
+
|
57
|
+
for j in 0..radial_segments do
|
58
|
+
# now calculate the vertices. they are nothing more than an extrusion of the torus curve.
|
59
|
+
# because we extrude a shape in the xy-plane, there is no need to calculate a z-value.
|
60
|
+
v = j.to_f / radial_segments.to_f * Math::PI * 2.0
|
61
|
+
cx = -tube * Math.cos(v)
|
62
|
+
cy = tube * Math.sin(v)
|
63
|
+
|
64
|
+
# now calculate the final vertex position.
|
65
|
+
# first we orient the extrusion with our basis vectos, then we add it to the current position on the curve
|
66
|
+
vertex.x = p1.x + (cx * n.x + cy * b.x)
|
67
|
+
vertex.y = p1.y + (cx * n.y + cy * b.y)
|
68
|
+
vertex.z = p1.z + (cx * n.z + cy * b.z)
|
69
|
+
|
70
|
+
vertices += vertex.elements
|
71
|
+
|
72
|
+
# normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)
|
73
|
+
normal.sub_vectors(vertex, p1).normalize
|
74
|
+
|
75
|
+
normals += normal.elements
|
76
|
+
|
77
|
+
# uv
|
78
|
+
uvs << i.to_f / tubular_segments.to_f
|
79
|
+
uvs << j.to_f / radial_segments.to_f
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# generate indices
|
84
|
+
|
85
|
+
for j in 1..tubular_segments do
|
86
|
+
for i in 1..radial_segments do
|
87
|
+
# indices
|
88
|
+
a = (radial_segments + 1) * (j - 1) + (i - 1)
|
89
|
+
b = (radial_segments + 1) * j + (i - 1)
|
90
|
+
c = (radial_segments + 1) * j + i
|
91
|
+
d = (radial_segments + 1) * (j - 1) + i
|
92
|
+
|
93
|
+
# faces
|
94
|
+
indices += [a, b, d]
|
95
|
+
indices += [b, c, d]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# build geometry
|
100
|
+
|
101
|
+
self[:index] = BufferAttribute.new(indices, 1)
|
102
|
+
self[:position] = BufferAttribute.new(vertices, 3)
|
103
|
+
self[:normal] = BufferAttribute.new(normals, 3)
|
104
|
+
self[:uv] = BufferAttribute.new(uvs, 2)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def calculate_position_on_curve(u, p_val, q_val, radius, position)
|
110
|
+
cu = Math.cos(u)
|
111
|
+
su = Math.sin(u)
|
112
|
+
qu_over_p = q_val.to_f / p_val.to_f * u
|
113
|
+
cs = Math.cos(qu_over_p)
|
114
|
+
|
115
|
+
position.x = radius * (2.0 + cs) * 0.5 * cu
|
116
|
+
position.y = radius * (2.0 + cs) * su * 0.5
|
117
|
+
position.z = radius * Math.sin(qu_over_p) * 0.5
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'mittsu/core'
|
2
|
+
require 'mittsu/math'
|
3
|
+
require 'mittsu/extras/geometries/torus_knot_buffer_geometry'
|
4
|
+
|
5
|
+
module Mittsu
|
6
|
+
class TorusKnotGeometry < Geometry
|
7
|
+
def initialize(radius = 100.0, tube = 40.0, radial_segments = 64, tubular_segments = 8, p_val = 2, q_val = 3)
|
8
|
+
super()
|
9
|
+
|
10
|
+
@type = 'TorusKnotGeometry'
|
11
|
+
|
12
|
+
@parameters = {
|
13
|
+
radius: radius,
|
14
|
+
tube: tube,
|
15
|
+
radial_segments: radial_segments,
|
16
|
+
tubular_segments: tubular_segments,
|
17
|
+
p_val: p_val,
|
18
|
+
q_val: q_val
|
19
|
+
}
|
20
|
+
|
21
|
+
from_buffer_geometry(TorusKnotBufferGeometry.new(radius, tube, tubular_segments, radial_segments, p_val, q_val))
|
22
|
+
merge_vertices
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mittsu/math/vector.rb
CHANGED
data/lib/mittsu/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mittsu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opengl-bindings
|
@@ -181,7 +181,23 @@ files:
|
|
181
181
|
- lib/mittsu/extras.rb
|
182
182
|
- lib/mittsu/extras/geometries.rb
|
183
183
|
- lib/mittsu/extras/geometries/box_geometry.rb
|
184
|
+
- lib/mittsu/extras/geometries/circle_geometry.rb
|
185
|
+
- lib/mittsu/extras/geometries/cylinder_geometry.rb
|
186
|
+
- lib/mittsu/extras/geometries/dodecahedron_geometry.rb
|
187
|
+
- lib/mittsu/extras/geometries/icosahedron_geometry.rb
|
188
|
+
- lib/mittsu/extras/geometries/lathe_geometry.rb
|
189
|
+
- lib/mittsu/extras/geometries/octahedron_geometry.rb
|
190
|
+
- lib/mittsu/extras/geometries/parametric_buffer_geometry.rb
|
191
|
+
- lib/mittsu/extras/geometries/parametric_geometry.rb
|
192
|
+
- lib/mittsu/extras/geometries/plane_buffer_geometry.rb
|
193
|
+
- lib/mittsu/extras/geometries/plane_geometry.rb
|
194
|
+
- lib/mittsu/extras/geometries/polyhedron_geometry.rb
|
195
|
+
- lib/mittsu/extras/geometries/ring_geometry.rb
|
184
196
|
- lib/mittsu/extras/geometries/sphere_geometry.rb
|
197
|
+
- lib/mittsu/extras/geometries/tetrahedron_geometry.rb
|
198
|
+
- lib/mittsu/extras/geometries/torus_geometry.rb
|
199
|
+
- lib/mittsu/extras/geometries/torus_knot_buffer_geometry.rb
|
200
|
+
- lib/mittsu/extras/geometries/torus_knot_geometry.rb
|
185
201
|
- lib/mittsu/extras/helpers.rb
|
186
202
|
- lib/mittsu/extras/helpers/camera_helper.rb
|
187
203
|
- lib/mittsu/extras/image.rb
|