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
@@ -29,18 +29,18 @@ module BulldogPhysics
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def calculate_separating_velocity
|
32
|
-
relative_velocity = @particle1.velocity
|
32
|
+
relative_velocity = @particle1.velocity.dup
|
33
33
|
unless @particle2.nil?
|
34
34
|
relative_velocity -= @particle2.velocity
|
35
35
|
end
|
36
|
-
return relative_velocity
|
36
|
+
return relative_velocity * @contact_normal
|
37
37
|
end
|
38
38
|
|
39
39
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def resolve_velocity(duration)
|
43
|
-
|
43
|
+
|
44
44
|
#Find the velocity in the direction of the contact.
|
45
45
|
separating_velocity = calculate_separating_velocity()
|
46
46
|
|
@@ -49,19 +49,32 @@ module BulldogPhysics
|
|
49
49
|
# The contact is either separating, or stationary; there’s no impulse required.
|
50
50
|
return
|
51
51
|
end
|
52
|
+
|
52
53
|
new_sep_velocity = -separating_velocity * @restitution
|
54
|
+
|
55
|
+
|
56
|
+
acc_caused_velocity = @particle1.acceleration.dup
|
57
|
+
|
58
|
+
unless @particle2.nil?
|
59
|
+
acc_caused_velocity -= @particle2.acceleration
|
60
|
+
end
|
61
|
+
|
62
|
+
acc_caused_velocity = acc_caused_velocity * @contact_normal * duration;
|
63
|
+
|
64
|
+
if acc_caused_velocity < 0
|
65
|
+
new_sep_velocity += @restitution * acc_caused_velocity
|
66
|
+
new_sep_velocity = 0 if new_sep_velocity < 0
|
67
|
+
end
|
68
|
+
|
53
69
|
delta_velocity = new_sep_velocity - separating_velocity
|
54
|
-
|
55
70
|
total_inverse_mass = @particle1.inverse_mass
|
56
|
-
|
57
|
-
|
71
|
+
|
72
|
+
|
58
73
|
unless( @particle2.nil? )
|
59
74
|
total_inverse_mass += @particle2.inverse_mass
|
60
75
|
end
|
61
76
|
|
62
|
-
if( total_inverse_mass <= 0)
|
63
|
-
return
|
64
|
-
end
|
77
|
+
return if( total_inverse_mass <= 0)
|
65
78
|
|
66
79
|
impulse = delta_velocity / total_inverse_mass
|
67
80
|
|
@@ -76,9 +89,9 @@ module BulldogPhysics
|
|
76
89
|
end
|
77
90
|
|
78
91
|
def resolve_interpenetration(duration)
|
79
|
-
|
80
|
-
|
81
|
-
|
92
|
+
|
93
|
+
return if(@penetration <= 0)
|
94
|
+
|
82
95
|
|
83
96
|
total_inverse_mass = @particle1.inverse_mass
|
84
97
|
unless( @particle2.nil? )
|
@@ -24,15 +24,15 @@ module BulldogPhysics
|
|
24
24
|
|
25
25
|
contact_array.each_with_index do |contact, i|
|
26
26
|
sep_vel = contact.calculate_separating_velocity
|
27
|
-
if( sep_vel < max
|
27
|
+
if( sep_vel < max && ( sep_vel < 0 || contact.penetration > 0) )
|
28
28
|
max = sep_vel
|
29
29
|
max_index = i
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
|
34
|
+
break if( max_index.eql? num_contacts)
|
35
|
+
|
36
36
|
|
37
37
|
contact_array[max_index].resolve(duration)
|
38
38
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module BulldogPhysics
|
1
|
+
module BulldogPhysics
|
2
2
|
module Particles
|
3
3
|
module Collisions
|
4
4
|
|
@@ -19,8 +19,8 @@ module BulldogPhysics
|
|
19
19
|
|
20
20
|
# * Fills the given contact structure with the contact needed to keep the rod from extending or compressing.
|
21
21
|
def add_contact(contactArray, limit)
|
22
|
-
current_len =
|
23
|
-
|
22
|
+
current_len = current_length
|
23
|
+
|
24
24
|
if( current_len == @length)
|
25
25
|
return 0
|
26
26
|
end
|
@@ -28,18 +28,19 @@ module BulldogPhysics
|
|
28
28
|
|
29
29
|
contact = ParticleContact.new(@particle1, @particle2)
|
30
30
|
|
31
|
-
|
31
|
+
normal = @particle2.position - @particle1.position
|
32
|
+
normal.normalize
|
32
33
|
# The contact normal depends on whether we’re extending or compressing.
|
33
34
|
|
35
|
+
contact.contact_normal = normal
|
36
|
+
|
34
37
|
if( current_len > @length)
|
35
38
|
contact.penetration = (current_len - @length)
|
36
|
-
#contact.penetration = -1
|
37
39
|
else
|
38
40
|
contact.contact_normal *= -1
|
39
41
|
contact.penetration = @length - current_len
|
40
|
-
#contact.penetration = 1
|
41
42
|
end
|
42
|
-
|
43
|
+
|
43
44
|
contact.restitution = 0
|
44
45
|
contactArray << contact
|
45
46
|
|
@@ -29,6 +29,7 @@ module BulldogPhysics
|
|
29
29
|
# give a number of contact-resolution iterations to use. If you * don’t give a number of iterations, then twice the number of
|
30
30
|
# contacts will be used.
|
31
31
|
def initialize(max_contacts = 100, iterations = 1)
|
32
|
+
puts "INIT WORLD"
|
32
33
|
@max_contacts = max_contacts
|
33
34
|
@calculate_iterations = iterations
|
34
35
|
@particles = Array.new
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
class Gravity < ForceGenerator
|
3
|
+
|
4
|
+
attr :gravity # vector3 accel do to gravity
|
5
|
+
|
6
|
+
def initialize(grav)
|
7
|
+
@gravity = grav
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_force(body, duration)
|
11
|
+
#return if body.hasFiniteMass
|
12
|
+
|
13
|
+
body.add_force(gravity * body.mass)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
class Buoyancy < ForceGenerator
|
3
|
+
|
4
|
+
attr_accessor :max_depth, :volume, :water_height, :liquid_density, :center_of_buoyancy
|
5
|
+
|
6
|
+
def initialize(c_of_b, max_depth, volume, water_height, liquid_density = 1000.0)
|
7
|
+
@center_of_buoyancy, @max_depth, @volume, @water_height, @liquid_density =
|
8
|
+
c_of_b, max_depth, volume, water_height, liquid_density
|
9
|
+
end
|
10
|
+
|
11
|
+
def update_force(body, duration)
|
12
|
+
point_in_world = body.get_point_in_world_space(@center_of_buoyancy)
|
13
|
+
depth = point_in_world.y
|
14
|
+
|
15
|
+
if(depth >= @water_height + @max_depth)
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
force = Vector3.new
|
20
|
+
|
21
|
+
if depth <= @water_height - @max_depth
|
22
|
+
force.y = @liquid_density * @volume
|
23
|
+
body.add_force_at_point(force, @center_of_buoyancy)
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
force.y = @liquid_density * @volume * (depth - @max_depth - @water_height) / (2 * @max_depth)
|
28
|
+
body.add_force_at_point(force, @center_of_buoyancy)
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
|
3
|
+
class CollisionPrimitive
|
4
|
+
|
5
|
+
attr_accessor :body, :offset
|
6
|
+
|
7
|
+
|
8
|
+
def initialize()
|
9
|
+
#puts "INIT"
|
10
|
+
@body = RigidBody.new
|
11
|
+
@offset = Matrix4.new
|
12
|
+
@transform = Matrix4.new
|
13
|
+
end
|
14
|
+
|
15
|
+
attr :transform
|
16
|
+
|
17
|
+
def calculate_internals()
|
18
|
+
#puts "OFFSET #{@offset}"
|
19
|
+
@transform = @body.transform_matrix * @offset
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_axis(index)
|
23
|
+
@transform.getAxisVector(index)
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_transform
|
27
|
+
@transform
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
class CollisionSphere < CollisionPrimitive
|
33
|
+
attr_accessor :radius
|
34
|
+
|
35
|
+
def initialize()
|
36
|
+
super()
|
37
|
+
@radius = 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
class CollisionPlane
|
43
|
+
attr_accessor :direction, :offset
|
44
|
+
|
45
|
+
def initialize(direction = Vector3.new, offset = 0.0)
|
46
|
+
@direction = direction
|
47
|
+
@offset = offset
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class CollisionBox < CollisionPrimitive
|
52
|
+
|
53
|
+
attr_accessor :half_size
|
54
|
+
|
55
|
+
def initialize(half_size = Vector3.new)
|
56
|
+
super()
|
57
|
+
@half_size = half_size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
class CollisionData
|
63
|
+
attr_accessor :contacts, :current_contact, :contacts_count, :friction, :restitution, :tolerance, :contacts_left
|
64
|
+
|
65
|
+
def initialize()
|
66
|
+
@contacts = Array.new
|
67
|
+
@current_contact = @contacts_count = @contacts_left = 0
|
68
|
+
@friction = @restitution = @tolerance = 0.0
|
69
|
+
end
|
70
|
+
|
71
|
+
def has_more_contacts()
|
72
|
+
@contacts_left > 0
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset(max_contacts)
|
76
|
+
@contacts_left = max_contacts
|
77
|
+
@contacts_count = 0
|
78
|
+
@current_contact = 0
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_contacts(count)
|
82
|
+
@contacts_left -= count
|
83
|
+
@contacts_count += count
|
84
|
+
|
85
|
+
@current_contact += count
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def transform_to_axis(box, axis)
|
91
|
+
return box.half_size.x * (axis * box.get_axis(0)).abs + \
|
92
|
+
box.half_size.y * (axis * box.get_axis(1)).abs + \
|
93
|
+
box.half_size.z * (axis * box.get_axis(2)).abs
|
94
|
+
end
|
95
|
+
|
96
|
+
def overlap_on_axis(one, two, axis, to_centre)
|
97
|
+
one_project = transform_to_axis(one, axis)
|
98
|
+
two_project = transform_to_axis(two, axis)
|
99
|
+
|
100
|
+
distance = (to_centre * axis).abs
|
101
|
+
|
102
|
+
distance < one_project + two_project
|
103
|
+
end
|
104
|
+
|
105
|
+
class IntersectionTests
|
106
|
+
|
107
|
+
def self.sphere_and_half_space(sphere, plane)
|
108
|
+
|
109
|
+
ball_distance = plane.direction * sphere.get_axis(3) - sphere.radius
|
110
|
+
|
111
|
+
ball_distance <= plane.offset
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.sphere_and_sphere(one, two)
|
115
|
+
|
116
|
+
mid_line = one.get_axis(3) - two.get_axis(3)
|
117
|
+
|
118
|
+
return mid_line.sqaureMagnitude < (one.radius + two.radius) * (one.radius + two.radius)
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.box_and_box(one, two)
|
122
|
+
|
123
|
+
to_center = two.get_axis(3) - one.get_axis(3)
|
124
|
+
|
125
|
+
return overlap_on_axis(one,two,one.getAxis(0), to_center) && \
|
126
|
+
overlap_on_axis(one,two,one.getAxis(1), to_center) && \
|
127
|
+
overlap_on_axis(one,two,one.getAxis(2), to_center) && \
|
128
|
+
overlap_on_axis(one,two,two.getAxis(0), to_center) && \
|
129
|
+
overlap_on_axis(one,two,two.getAxis(1), to_center) && \
|
130
|
+
overlap_on_axis(one,two,two.getAxis(2), to_center) && \
|
131
|
+
overlap_on_axis(one,two,one.getAxis(0) % two.getAxis(0), to_center) && \
|
132
|
+
overlap_on_axis(one,two,one.getAxis(0) % two.getAxis(1), to_center) && \
|
133
|
+
overlap_on_axis(one,two,one.getAxis(0) % two.getAxis(2), to_center) && \
|
134
|
+
overlap_on_axis(one,two,one.getAxis(1) % two.getAxis(0), to_center) && \
|
135
|
+
overlap_on_axis(one,two,one.getAxis(1) % two.getAxis(1), to_center) && \
|
136
|
+
overlap_on_axis(one,two,one.getAxis(1) % two.getAxis(2), to_center) && \
|
137
|
+
overlap_on_axis(one,two,one.getAxis(2) % two.getAxis(0), to_center) && \
|
138
|
+
overlap_on_axis(one,two,one.getAxis(2) % two.getAxis(1), to_center) && \
|
139
|
+
overlap_on_axis(one,two,one.getAxis(2) % two.getAxis(2), to_center)
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.box_and_half_space(box, plane)
|
143
|
+
projection_radius = transform_to_axis( box, plane.direction)
|
144
|
+
box_distance = plane.direction * box.get_axis(3) - projection_radius
|
145
|
+
box_distance < plane.offset
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
class CollisionDetector
|
151
|
+
|
152
|
+
def self.sphere_and_half_space(sphere, plane, contacts)
|
153
|
+
#eturn 0 if(data.contacts_left <= 0)
|
154
|
+
|
155
|
+
position = sphere.get_axis(3)
|
156
|
+
|
157
|
+
ball_distance = plane.direction * position - sphere.radius - plane.offset
|
158
|
+
|
159
|
+
puts "ball distance"
|
160
|
+
return 0 if ball_distance >= 0
|
161
|
+
|
162
|
+
contact = Contact.new
|
163
|
+
contact.contact_normal = plane.direction
|
164
|
+
contact.penetration = -ball_distance
|
165
|
+
contact.contact_point = position - plane.direction * (ball_distance + sphere.radius)
|
166
|
+
contact.set_body_data(sphere.body, nil, 0.9, 0.6)
|
167
|
+
contacts << contact
|
168
|
+
return 1
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.sphere_and_sphere(one, two, contatcs)
|
172
|
+
|
173
|
+
positionOne = one.get_axis(3)
|
174
|
+
positionTwo = two.get_axis(3)
|
175
|
+
|
176
|
+
midline = positionOne - positionTwo
|
177
|
+
|
178
|
+
size = midline.magnitude
|
179
|
+
|
180
|
+
if size <= 0.0 || size >= one.radius + two.radius
|
181
|
+
return 0
|
182
|
+
end
|
183
|
+
|
184
|
+
normal = midline * (1.0/size)
|
185
|
+
|
186
|
+
contact = Contact.new
|
187
|
+
contact.normal = normal
|
188
|
+
contact.contact_point = positionOne + midline * 0.5
|
189
|
+
contact.penetration = one.radius + two.radius - size
|
190
|
+
contact.set_body_data( one.body, two.body, 0.9, 0.6)
|
191
|
+
contatcs << contact
|
192
|
+
return 1
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.box_and_box(one, two, data)
|
196
|
+
|
197
|
+
to_center = two.get_axis(3) - one.get_axis(3)
|
198
|
+
puts "NOT DONE YET"
|
199
|
+
return overlap_on_axis(one,two,one.getAxis(0), to_center) && \
|
200
|
+
overlap_on_axis(one,two,one.getAxis(1), to_center) && \
|
201
|
+
overlap_on_axis(one,two,one.getAxis(2), to_center) && \
|
202
|
+
overlap_on_axis(one,two,two.getAxis(0), to_center) && \
|
203
|
+
overlap_on_axis(one,two,two.getAxis(1), to_center) && \
|
204
|
+
overlap_on_axis(one,two,two.getAxis(2), to_center) && \
|
205
|
+
overlap_on_axis(one,two,one.getAxis(0) % two.getAxis(0), to_center) && \
|
206
|
+
overlap_on_axis(one,two,one.getAxis(0) % two.getAxis(1), to_center) && \
|
207
|
+
overlap_on_axis(one,two,one.getAxis(0) % two.getAxis(2), to_center) && \
|
208
|
+
overlap_on_axis(one,two,one.getAxis(1) % two.getAxis(0), to_center) && \
|
209
|
+
overlap_on_axis(one,two,one.getAxis(1) % two.getAxis(1), to_center) && \
|
210
|
+
overlap_on_axis(one,two,one.getAxis(1) % two.getAxis(2), to_center) && \
|
211
|
+
overlap_on_axis(one,two,one.getAxis(2) % two.getAxis(0), to_center) && \
|
212
|
+
overlap_on_axis(one,two,one.getAxis(2) % two.getAxis(1), to_center) && \
|
213
|
+
overlap_on_axis(one,two,one.getAxis(2) % two.getAxis(2), to_center)
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.box_and_half_space(box, plane, contacts)
|
217
|
+
#return 0 if data.contacts_left <= 0
|
218
|
+
|
219
|
+
#return 0 unless IntersectionTests.box_and_half_space(box,plane)
|
220
|
+
|
221
|
+
mults = [ [1,1,1] , [-1,1,1] , [1,-1,1] , [-1,-1,1] , \
|
222
|
+
[1,1,-1] , [-1,1,-1], [1,-1,-1] , [-1,-1,-1]]
|
223
|
+
|
224
|
+
contacts_used = 0
|
225
|
+
|
226
|
+
#contact = contacts[contacts_used]
|
227
|
+
|
228
|
+
for i in 0..7
|
229
|
+
vertexPos = Vector3.new(mults[i][0], mults[i][1], mults[i][2]);
|
230
|
+
vertexPos.componentProductUpdate(box.half_size);
|
231
|
+
vertexPos = box.transform.transform(vertexPos);
|
232
|
+
vertexDistance = vertexPos * plane.direction;
|
233
|
+
|
234
|
+
if(vertexDistance <= plane.offset)
|
235
|
+
puts "test"
|
236
|
+
contact = Contact.new
|
237
|
+
contact.contact_point = plane.direction
|
238
|
+
contact.contact_point *= (vertexDistance - plane.offset)
|
239
|
+
contact.contact_point = vertexPos # ??????
|
240
|
+
contact.contact_normal = plane.direction
|
241
|
+
contact.penetration = plane.offset - vertexDistance
|
242
|
+
|
243
|
+
contacts_used+=1
|
244
|
+
contact.set_body_data(box.body, nil, 0.9, 0.6)
|
245
|
+
contacts << contact
|
246
|
+
#contact = data.contacts[contacts_used]
|
247
|
+
#return contacts_used if contacts_used == data.contacts_left
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
#data.add_contacts(contacts_used)
|
252
|
+
return contacts_used
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
end
|