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.
@@ -0,0 +1,7 @@
1
+ module BulldogPhysics
2
+ class ForceGenerator
3
+
4
+ virtual :update_force
5
+
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module BulldogPhysics
2
+ class ForceRegistration
3
+
4
+ attr_accessor :body, :force_generator
5
+
6
+ def initialize()
7
+ @body = RigidBody.new
8
+ @pforce_generator = ForceGenerator.new
9
+ end
10
+
11
+ end
12
+ end
@@ -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