bulldog_physics 0.1.1 → 0.2.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.
- data/lib/Particles/particle_contact.rb +25 -12
- data/lib/Particles/particle_contact_resolver.rb +4 -4
- data/lib/Particles/particle_drag.rb +1 -1
- data/lib/Particles/particle_rod.rb +8 -7
- data/lib/Particles/particle_world.rb +1 -0
- data/lib/RigidBodies/Gravity.rb +18 -0
- data/lib/RigidBodies/buoyancy.rb +32 -0
- data/lib/RigidBodies/collision_shapes.rb +256 -0
- data/lib/RigidBodies/contact.rb +362 -0
- data/lib/RigidBodies/contact_generator.rb +7 -0
- data/lib/RigidBodies/contact_resolver.rb +143 -0
- data/lib/RigidBodies/force_generator.rb +7 -0
- data/lib/RigidBodies/force_registration.rb +12 -0
- data/lib/RigidBodies/force_registry.rb +48 -0
- data/lib/RigidBodies/joint.rb +45 -0
- data/lib/RigidBodies/rigid_body.rb +261 -0
- data/lib/RigidBodies/rigid_collisions.rb +292 -0
- data/lib/RigidBodies/spring.rb +38 -0
- data/lib/RigidBodies/world.rb +97 -0
- data/lib/bulldog_physics.rb +6 -4
- data/lib/examples/GlStuff/gl_utility.rb +4 -0
- data/lib/examples/GlStuff/lighting.rb +1 -1
- data/lib/examples/GlStuff/material.rb +11 -11
- data/lib/examples/buoyancy_game.rb +189 -0
- data/lib/examples/simple_game.rb +79 -76
- data/lib/vector3.rb +8 -13
- metadata +92 -120
- data/.document +0 -5
- data/Gemfile +0 -13
- data/Gemfile.lock +0 -22
- data/Rakefile +0 -55
- data/VERSION +0 -1
- data/bulldog_physics.gemspec +0 -92
- data/test/helper.rb +0 -18
- data/test/test_bulldog_physics.rb +0 -7
@@ -0,0 +1,48 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
class ForceRegistry
|
3
|
+
|
4
|
+
|
5
|
+
attr_accessor :registrations
|
6
|
+
|
7
|
+
def initialize()
|
8
|
+
@registrations = Array.new # array of ParticleForceRegistration(s)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Registers the given force generator to apply to the
|
12
|
+
# given particle.
|
13
|
+
def add(body, force_generator)
|
14
|
+
body_registration = ForceRegistration.new
|
15
|
+
body_registration.body = body
|
16
|
+
body_registration.force_generator = force_generator
|
17
|
+
@registrations << body_registration
|
18
|
+
puts "Registration count #{@registrations.count}"
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Removes the given registered pair from the registry.
|
23
|
+
# If the pair is not registered, this method will have
|
24
|
+
# no effect.
|
25
|
+
def remove(body,force_generator)
|
26
|
+
@registrations.remove_if do |registry|
|
27
|
+
registry.body.eql? body and registry.force_generator.eql? force_generator
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
### Clears all registrations from the registry. This will
|
32
|
+
### not delete the particles or the force generators
|
33
|
+
### themselves, just the records of their connection.
|
34
|
+
def clear
|
35
|
+
@registrations = Array.new
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
#Calls all the force generators to update the forces of
|
40
|
+
#their corresponding particles.
|
41
|
+
def update_forces(duration)
|
42
|
+
for registry in @registrations
|
43
|
+
registry.force_generator.update_force(registry.body,duration)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
class Joint < ContactGenerator
|
3
|
+
|
4
|
+
attr_accessor :body, :position, :error
|
5
|
+
|
6
|
+
|
7
|
+
def initialize()
|
8
|
+
@body = [RigidBody.new, RigidBody.new]
|
9
|
+
@position = [Vector3.new, Vector3.new]
|
10
|
+
@error = 0.0
|
11
|
+
end
|
12
|
+
def add_contact(contacts, limit)
|
13
|
+
a_pos_world = @body[0].get_point_in_world_space(@position[0])
|
14
|
+
b_pos_world = @body[1].get_point_in_world_space(@position[1])
|
15
|
+
a_to_b = b_pos_world - a_pos_world
|
16
|
+
normal = a_to_b.unit
|
17
|
+
|
18
|
+
length = a_to_b.magnitude
|
19
|
+
|
20
|
+
if(length.abs > error)
|
21
|
+
puts "add contact"
|
22
|
+
contact = Contact.new
|
23
|
+
contact.body = @body
|
24
|
+
contact.contact_normal = normal
|
25
|
+
contact.contact_point = (a_pos_world + b_pos_world) * 0.5
|
26
|
+
contact.penetration = length - error
|
27
|
+
contact.friction = 1.0
|
28
|
+
contact.restitution = 0
|
29
|
+
contacts << contact
|
30
|
+
return 1
|
31
|
+
end
|
32
|
+
return 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def set(a, a_pos, b, b_pos, error)
|
36
|
+
@body[0] = a
|
37
|
+
@body[1] = b
|
38
|
+
|
39
|
+
@position[0] = a_pos
|
40
|
+
@position[1] = b_pos
|
41
|
+
|
42
|
+
@error = error
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
class RigidBody
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
attr_accessor :inverse_mass, :linear_damping, :angular_damping, :position, :velocity, :rotation, :transform_matrix, :inverse_inertia_tensor_world,
|
7
|
+
:force_accum, :torque_accum, :acceleration, :material, :frozen, :last_frame_acceleration, :motion, :sleep_epsilon
|
8
|
+
|
9
|
+
attr :mass
|
10
|
+
|
11
|
+
attr_reader :mass, :is_awake, :orientation
|
12
|
+
# Holds the inverse of the body’s inertia tensor. The
|
13
|
+
# intertia tensor provided must not be degenerate
|
14
|
+
# (that would mean the body had zero inertia for
|
15
|
+
# spinning along one axis). As long as the tensor is
|
16
|
+
# finite, it will be invertible. The inverse tensor
|
17
|
+
# is used for similar reasons to the use of inverse * mass.
|
18
|
+
#
|
19
|
+
# The inertia tensor, unlike the other variables that
|
20
|
+
# define a rigid body, is given in body space.
|
21
|
+
attr :inverse_inertia_tensor
|
22
|
+
|
23
|
+
#public
|
24
|
+
|
25
|
+
|
26
|
+
def initialize()
|
27
|
+
@inverse_mass = 0
|
28
|
+
@mass = 1
|
29
|
+
@linear_damping , @angular_damping = 0.9, 0.9
|
30
|
+
@acceleration = Vector3.new
|
31
|
+
@position = Vector3.new
|
32
|
+
@velocity = Vector3.new
|
33
|
+
@orientation = Quaternion.new
|
34
|
+
@transform_matrix = Matrix4.new
|
35
|
+
@rotation = Vector3.new
|
36
|
+
@inverse_inertia_tensor = Matrix3.new
|
37
|
+
@inverse_inertia_tensor_world = Matrix3.new
|
38
|
+
@torque_accum = Vector3.new
|
39
|
+
@force_accum = Vector3.new
|
40
|
+
@material = Material.new
|
41
|
+
@last_frame_acceleration = Vector3.new
|
42
|
+
@motion = 0.0
|
43
|
+
@sleep_epsilon = 0.5
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def orientation=(orientation)
|
48
|
+
@orientation = orientation
|
49
|
+
@orientation.normalize
|
50
|
+
end
|
51
|
+
|
52
|
+
def mass=(mass)
|
53
|
+
@mass = mass
|
54
|
+
@inverse_mass = 1.0 / @mass
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def is_awake=(awake)
|
59
|
+
@is_awake = awake
|
60
|
+
if @is_awake
|
61
|
+
@motion = @sleep_epsilon * 2.0
|
62
|
+
else
|
63
|
+
@velocity.clear()
|
64
|
+
@rotation.clear()
|
65
|
+
end
|
66
|
+
end
|
67
|
+
# Calculates internal data from state data. This should be called * after the body’s state is altered directly (it is called
|
68
|
+
# automatically during integration). If you change the body’s state * and then intend to integrate before querying any data (such as
|
69
|
+
# the transform matrix), then you can omit this step.
|
70
|
+
def calculate_derived_data
|
71
|
+
@orientation.normalize()
|
72
|
+
RigidBody.calculate_transform_matrix(@transform_matrix, @position, @orientation)
|
73
|
+
RigidBody.transform_inertia_tensor(@inverse_inertia_tensor_world,
|
74
|
+
@orientation, @inverse_inertia_tensor, @transform_matrix);
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def set_inertia_tensor(inertia_tensor)
|
79
|
+
@inverse_inertia_tensor.setInverse(inertia_tensor)
|
80
|
+
end
|
81
|
+
# Adds the given force to center of mass of the rigid body
|
82
|
+
# The force is expressed in world coordinates.
|
83
|
+
def add_force(force)
|
84
|
+
@force_accum.addVector(force)
|
85
|
+
self.is_awake = true
|
86
|
+
end
|
87
|
+
|
88
|
+
def hasFiniteMass
|
89
|
+
return @mass < 0.0
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def integrate(duration)
|
94
|
+
|
95
|
+
return if !@is_awake
|
96
|
+
|
97
|
+
@last_frame_acceleration = @acceleration.dup
|
98
|
+
@last_frame_acceleration.addScaledVector(@force_accum, @inverse_mass)
|
99
|
+
|
100
|
+
angular_acceleration = @inverse_inertia_tensor_world.transform(@torque_accum)
|
101
|
+
|
102
|
+
@velocity.addScaledVector( @last_frame_acceleration, duration)
|
103
|
+
@rotation.addScaledVector(angular_acceleration, duration)
|
104
|
+
|
105
|
+
@velocity *= @linear_damping ** duration
|
106
|
+
@rotation *= @angular_damping ** duration
|
107
|
+
|
108
|
+
#$@rotation *= @angular_damping ** duration
|
109
|
+
#@velocity.multiplyByScalar( @linear_damping ** duration)
|
110
|
+
@position.addScaledVector(@velocity, duration)
|
111
|
+
@orientation.addScaledVector(@rotation, duration)
|
112
|
+
|
113
|
+
calculate_derived_data()
|
114
|
+
|
115
|
+
clear_accumulators()
|
116
|
+
|
117
|
+
@can_sleep ||= true
|
118
|
+
=begin
|
119
|
+
if can_sleep
|
120
|
+
current_motion = @velocity.scalarProduct(@velocity) + @rotation.scalarProduct(@rotation)
|
121
|
+
bias = 0.5 ** duration
|
122
|
+
@motion = (bias * @motion) + ((1-@bias)*current_motion)
|
123
|
+
|
124
|
+
if motion < @sleep_epsilon
|
125
|
+
self.is_awake=false
|
126
|
+
elsif @motion > 10 * @sleep_epsilon
|
127
|
+
@motion = 10 * @sleep_epsilon
|
128
|
+
end
|
129
|
+
end
|
130
|
+
=end
|
131
|
+
end
|
132
|
+
|
133
|
+
def clear_accumulators()
|
134
|
+
@force_accum.clear
|
135
|
+
@torque_accum.clear
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_force_at_body_point(force, point)
|
139
|
+
pt = get_point_in_world_space(point)
|
140
|
+
add_force_at_point(force, pt)
|
141
|
+
@is_awake = true
|
142
|
+
end
|
143
|
+
|
144
|
+
def add_force_at_point(force, point)
|
145
|
+
pt = point
|
146
|
+
pt -= @position
|
147
|
+
@force_accum += force
|
148
|
+
@torque_accum += pt % force
|
149
|
+
@is_awake = true
|
150
|
+
end
|
151
|
+
# Internal function to do an intertia tensor transform by a quaternion.
|
152
|
+
# Note that the implementation of this function was created by an
|
153
|
+
# automated code generator and optimizer.
|
154
|
+
def self.transform_inertia_tensor(iitWorld, q, iitBody, rotmat )
|
155
|
+
t4 = rotmat.data[0]*iitBody.data[0]+
|
156
|
+
rotmat.data[1]*iitBody.data[3]+
|
157
|
+
rotmat.data[2]*iitBody.data[6];
|
158
|
+
t9 = rotmat.data[0]*iitBody.data[1]+
|
159
|
+
rotmat.data[1]*iitBody.data[4]+
|
160
|
+
rotmat.data[2]*iitBody.data[7];
|
161
|
+
t14 = rotmat.data[0]*iitBody.data[2]+
|
162
|
+
rotmat.data[1]*iitBody.data[5]+
|
163
|
+
rotmat.data[2]*iitBody.data[8];
|
164
|
+
t28 = rotmat.data[4]*iitBody.data[0]+
|
165
|
+
rotmat.data[5]*iitBody.data[3]+
|
166
|
+
rotmat.data[6]*iitBody.data[6];
|
167
|
+
t33 = rotmat.data[4]*iitBody.data[1]+
|
168
|
+
rotmat.data[5]*iitBody.data[4]+
|
169
|
+
rotmat.data[6]*iitBody.data[7];
|
170
|
+
t38 = rotmat.data[4]*iitBody.data[2]+
|
171
|
+
rotmat.data[5]*iitBody.data[5]+
|
172
|
+
rotmat.data[6]*iitBody.data[8];
|
173
|
+
t52 = rotmat.data[8]*iitBody.data[0]+
|
174
|
+
rotmat.data[9]*iitBody.data[3]+
|
175
|
+
rotmat.data[10]*iitBody.data[6];
|
176
|
+
t57 = rotmat.data[8]*iitBody.data[1]+
|
177
|
+
rotmat.data[9]*iitBody.data[4]+
|
178
|
+
rotmat.data[10]*iitBody.data[7];
|
179
|
+
t62 = rotmat.data[8]*iitBody.data[2]+
|
180
|
+
rotmat.data[9]*iitBody.data[5]+ rotmat.data[10]*iitBody.data[8];
|
181
|
+
iitWorld.data[0] = t4*rotmat.data[0]+ t9*rotmat.data[1]+ t14*rotmat.data[2];
|
182
|
+
iitWorld.data[1] = t4*rotmat.data[4]+ t9*rotmat.data[5]+ t14*rotmat.data[6];
|
183
|
+
iitWorld.data[2] = t4*rotmat.data[8]+ t9*rotmat.data[9]+ t14*rotmat.data[10];
|
184
|
+
iitWorld.data[3] = t28*rotmat.data[0]+ t33*rotmat.data[1]+
|
185
|
+
t38*rotmat.data[2]; iitWorld.data[4] = t28*rotmat.data[4]+
|
186
|
+
t33*rotmat.data[5]+
|
187
|
+
t38*rotmat.data[6]; iitWorld.data[5] = t28*rotmat.data[8]+
|
188
|
+
t33*rotmat.data[9]+
|
189
|
+
t38*rotmat.data[10]; iitWorld.data[6] = t52*rotmat.data[0]+
|
190
|
+
t57*rotmat.data[1]+
|
191
|
+
t62*rotmat.data[2]; iitWorld.data[7] = t52*rotmat.data[4]+
|
192
|
+
t57*rotmat.data[5]+
|
193
|
+
t62*rotmat.data[6]; iitWorld.data[8] = t52*rotmat.data[8]+
|
194
|
+
t57*rotmat.data[9]+ t62*rotmat.data[10];
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.calculate_transform_matrix(transformMatrix, position, orientation)
|
199
|
+
transformMatrix.data[0] = 1-2*orientation.j*orientation.j -
|
200
|
+
2*orientation.k*orientation.k;
|
201
|
+
transformMatrix.data[1] = 2*orientation.i*orientation.j -
|
202
|
+
2*orientation.r*orientation.k;
|
203
|
+
transformMatrix.data[2] = 2*orientation.i*orientation.k +
|
204
|
+
2*orientation.r*orientation.j;
|
205
|
+
transformMatrix.data[3] = position.x;
|
206
|
+
|
207
|
+
transformMatrix.data[4] = 2*orientation.i*orientation.j +
|
208
|
+
2*orientation.r*orientation.k;
|
209
|
+
transformMatrix.data[5] = 1-2*orientation.i*orientation.i -
|
210
|
+
2*orientation.k*orientation.k;
|
211
|
+
transformMatrix.data[6] = 2*orientation.j*orientation.k -
|
212
|
+
2*orientation.r*orientation.i;
|
213
|
+
transformMatrix.data[7] = position.y;
|
214
|
+
transformMatrix.data[8] = 2*orientation.i*orientation.k -
|
215
|
+
2*orientation.r*orientation.j;
|
216
|
+
transformMatrix.data[9] = 2*orientation.j*orientation.k +
|
217
|
+
2*orientation.r*orientation.i;
|
218
|
+
transformMatrix.data[10] = 1-2*orientation.i*orientation.i -
|
219
|
+
2*orientation.j*orientation.j;
|
220
|
+
transformMatrix.data[11] = position.z;
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
def get_point_in_local_space(point)
|
225
|
+
@transform_matrix.transformInverse(point);
|
226
|
+
end
|
227
|
+
|
228
|
+
def get_point_in_world_space(point)
|
229
|
+
@transform_matrix.transform(point);
|
230
|
+
end
|
231
|
+
|
232
|
+
def get_gl_transform()
|
233
|
+
matrix = Array.new(16)
|
234
|
+
matrix[0] = @transform_matrix.data[0];
|
235
|
+
matrix[1] = @transform_matrix.data[4];
|
236
|
+
matrix[2] = @transform_matrix.data[8];
|
237
|
+
matrix[3] = 0;
|
238
|
+
|
239
|
+
matrix[4] = @transform_matrix.data[1];
|
240
|
+
matrix[5] = @transform_matrix.data[5];
|
241
|
+
matrix[6] = @transform_matrix.data[9];
|
242
|
+
matrix[7] = 0;
|
243
|
+
|
244
|
+
matrix[8] = @transform_matrix.data[2];
|
245
|
+
matrix[9] = @transform_matrix.data[6];
|
246
|
+
matrix[10] = @transform_matrix.data[10];
|
247
|
+
matrix[11] = 0;
|
248
|
+
|
249
|
+
matrix[12] = @transform_matrix.data[3];
|
250
|
+
matrix[13] = @transform_matrix.data[7];
|
251
|
+
matrix[14] = @transform_matrix.data[11];
|
252
|
+
matrix[15] = 1;
|
253
|
+
|
254
|
+
matrix
|
255
|
+
end
|
256
|
+
# def method_missing(meth, *args, &blk)
|
257
|
+
# puts "[RigidBody] method #{meth} is missing"
|
258
|
+
# end
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
@@ -0,0 +1,292 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
class BoundingSphere
|
3
|
+
attr_accessor :center, :radius
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
if args.size > 1
|
7
|
+
if args[0].is_a? Vector3
|
8
|
+
@center = args[0]
|
9
|
+
@radius = args[1]
|
10
|
+
elsif args[0].is_a? BoundingSphere
|
11
|
+
initialize_with_bounding_spheres(args[0],args[1])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize_with_bounding_spheres(one, two)
|
17
|
+
center_offset = two.center - one.center
|
18
|
+
distance = center_offset.squareMagnitude()
|
19
|
+
radius_diff = two.radius - one.radius
|
20
|
+
|
21
|
+
if radius_diff * radius_diff >= distance
|
22
|
+
if one.radius > two.radius
|
23
|
+
@center = one.center
|
24
|
+
@radius = one.radius
|
25
|
+
else
|
26
|
+
@center = two.center
|
27
|
+
@radius = two.radius
|
28
|
+
end
|
29
|
+
else
|
30
|
+
distance = Math.sqrt(distance)
|
31
|
+
@radius = (distance + one.radius + two.radius) * 0.5
|
32
|
+
@center = one.center
|
33
|
+
|
34
|
+
if distance > 0
|
35
|
+
@center += center_offset * ((@radius - one.radius) / distance)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def overlaps(other)
|
42
|
+
distance_squared = (@center - other.center).squareMagnitude
|
43
|
+
return distance_squared < (@radius + other.radius)*(@radius + other.radius)
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class BoundingBox
|
50
|
+
attr :center, :half_of_size
|
51
|
+
|
52
|
+
def initialize(center = Vector3.new, half_of_size = Vector3.new)
|
53
|
+
@center = center
|
54
|
+
@half_of_size = half_of_size
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Plane
|
59
|
+
attr_accessor :position, :direction
|
60
|
+
|
61
|
+
def initialize()
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# BSPChildType enum
|
66
|
+
module BSPChildType
|
67
|
+
NODE = 0
|
68
|
+
OBJECTS = 1
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
=begin
|
73
|
+
class BSPElement
|
74
|
+
|
75
|
+
attr_accessor :type # BSPChildType
|
76
|
+
attr_accessor :BSPNode
|
77
|
+
|
78
|
+
end
|
79
|
+
=end
|
80
|
+
class BSPElement
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
class BSPObjectSet < BSPElement
|
85
|
+
attr_accessor :objects
|
86
|
+
|
87
|
+
def initialize(objects = Array.new)
|
88
|
+
@objects = objects
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class BSPNode < BSPElement
|
93
|
+
attr_accessor :plane, :front, :back
|
94
|
+
|
95
|
+
def initialize(plane = Plane.new, front = BSPElement.new, back = BSPElement.new)
|
96
|
+
@plane, @front, @back = plane, front, back
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
class QuadTreeSector
|
103
|
+
BOTTOM_LEFT = 0
|
104
|
+
BOTTOM_RIGHT = 1
|
105
|
+
TOP_LEFT = 2
|
106
|
+
TOP_RIGHT = 3
|
107
|
+
end
|
108
|
+
|
109
|
+
class OctTreeSector
|
110
|
+
LOWER_BOTTOM_LEFT = 0
|
111
|
+
LOWER_BOTTOM_RIGHT = 1
|
112
|
+
HIGHER_BOTTOM_LEFT = 2
|
113
|
+
HIGHER_BOTTOM_RIGHT = 3
|
114
|
+
LOWER_TOP_LEFT = 4
|
115
|
+
LOWER_TOP_RIGHT = 5
|
116
|
+
HIGHER_TOP_LEFT = 6
|
117
|
+
HIGHER_TOP_RIGHT = 7
|
118
|
+
end
|
119
|
+
|
120
|
+
class QuadTreeNode
|
121
|
+
attr_accessor :position, :children
|
122
|
+
|
123
|
+
def initialize()
|
124
|
+
@position = Vector3.new
|
125
|
+
@children = Array.new(4, QuadTreeNode.new)
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_child_index(object_centre)
|
129
|
+
index = 0
|
130
|
+
index += 1 if object_centre.x > @position.x
|
131
|
+
index += 2 if object_centre.z > @position.z
|
132
|
+
index
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class OctTreeNode
|
137
|
+
attr_accessor :position, :children
|
138
|
+
|
139
|
+
def initialize()
|
140
|
+
@position = Vector3.new
|
141
|
+
@children = Array.new(8, OctTreeNode.new)
|
142
|
+
end
|
143
|
+
|
144
|
+
def get_child_index(object_centre)
|
145
|
+
index = 0
|
146
|
+
index += 1 if object_centre.x > @position.x
|
147
|
+
index += 2 if object_centre.y > @position.y
|
148
|
+
index += 4 if object_centre.z > @position.z
|
149
|
+
index
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Grid
|
154
|
+
attr_accessor :x_extent, :z_extent, :locations, :origin, :inverse_cell_size, :active_sets
|
155
|
+
|
156
|
+
def initialize
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_location_index(object_centre)
|
160
|
+
square = object_centre.componentProduct(inverseCellSize)
|
161
|
+
square.x + @x_extent * square.z
|
162
|
+
end
|
163
|
+
|
164
|
+
def add(object)
|
165
|
+
location = get_location_index(object.center)
|
166
|
+
set = @locations[location]
|
167
|
+
@active_sets.insert(set) if(set.size() == 1)
|
168
|
+
set.add(object)
|
169
|
+
end
|
170
|
+
|
171
|
+
def remove(object)
|
172
|
+
location = get_location_index(object.center)
|
173
|
+
set = @locations[location]
|
174
|
+
@active_sets.erase(set) if set.size == 2
|
175
|
+
set.remove(object)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class PotentialContact
|
180
|
+
attr :bodies
|
181
|
+
|
182
|
+
def initialize(bodies = Array.new)
|
183
|
+
@bodies = bodies
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class BVHNode
|
188
|
+
|
189
|
+
attr_accessor :children, :volume, :body, :parent
|
190
|
+
|
191
|
+
def initialize()
|
192
|
+
@children = Array.new
|
193
|
+
@volume = BHVNode.new
|
194
|
+
end
|
195
|
+
|
196
|
+
def is_leaf
|
197
|
+
!@body.nil?
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_potential_contacts(contacts, limit)
|
201
|
+
return if limit == 0 or is_leaf
|
202
|
+
|
203
|
+
@children[0].get_potential_contacts_with(@children[1], contacts, limit)
|
204
|
+
end
|
205
|
+
|
206
|
+
def get_potential_contacts_with(other, contacts,limit)
|
207
|
+
return if !overlaps(other) or limit == 0
|
208
|
+
|
209
|
+
if is_leaf or other.is_leaf
|
210
|
+
contacts.body[0] = @body
|
211
|
+
contacts.body[1] = other.body
|
212
|
+
end
|
213
|
+
|
214
|
+
if other.is_leaf or (!is_leaf and @volume.get_size >= other.volume.get_size)
|
215
|
+
|
216
|
+
count = @children[0].get_potential_contacts_with(other,contacts,limit)
|
217
|
+
|
218
|
+
if limit > count
|
219
|
+
return count + @children.get_potential_contacts_with(other, contacts[count], limit - count)
|
220
|
+
else
|
221
|
+
return count
|
222
|
+
end
|
223
|
+
|
224
|
+
else
|
225
|
+
count = get_potential_contacts_with(other.children[0], contacts, limit)
|
226
|
+
|
227
|
+
if limit > count
|
228
|
+
return count + get_potential_contacts_with(other.children[1], contacts[count], limit - count)
|
229
|
+
else
|
230
|
+
return count
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
def insert(new_body, new_volume)
|
238
|
+
|
239
|
+
if is_leaf
|
240
|
+
@children[0] = BHVNode.new(self, @volume, @body)
|
241
|
+
@children[1] = BHVNode.new(self, new_volume, new_body)
|
242
|
+
|
243
|
+
self.body = nil
|
244
|
+
|
245
|
+
recalculate_bounding_volume()
|
246
|
+
else
|
247
|
+
if @children[0].volume.get_growth(new_volume) < @children[1].get_growth(new_volume)
|
248
|
+
@children[0].insert(new_body, new_volume)
|
249
|
+
else
|
250
|
+
@children[1].insert(new_body, new_volume)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def overlaps(other)
|
256
|
+
@volume.overlaps(other.volume)
|
257
|
+
end
|
258
|
+
|
259
|
+
def destroy_node
|
260
|
+
if !@parent.nil?
|
261
|
+
if @parent.children[0].eql? self
|
262
|
+
sibling = @parent.children[1]
|
263
|
+
else
|
264
|
+
sibling = @parent.children[0]
|
265
|
+
end
|
266
|
+
|
267
|
+
@parent.volume = sibling.volume
|
268
|
+
@parent.body = sibling.volume
|
269
|
+
@parent.children[0] = sibling.children[0]
|
270
|
+
@parent.children[1] = sibling.children[1]
|
271
|
+
|
272
|
+
|
273
|
+
sibling.parent = nil
|
274
|
+
sibling.body = nil
|
275
|
+
sibling.children = nil
|
276
|
+
|
277
|
+
@parent.recalculate_bounding_volume()
|
278
|
+
end
|
279
|
+
|
280
|
+
if( @children[0])
|
281
|
+
@children[0].parent = nil
|
282
|
+
end
|
283
|
+
|
284
|
+
if( @children[1])
|
285
|
+
@children[1].parent = nil
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|