bulldog_physics 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +25 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/bulldog_physics.gemspec +92 -0
- data/lib/Particles/particle.rb +75 -0
- data/lib/Particles/particle_anchored_spring.rb +42 -0
- data/lib/Particles/particle_cable.rb +52 -0
- data/lib/Particles/particle_contact.rb +121 -0
- data/lib/Particles/particle_contact_generator.rb +28 -0
- data/lib/Particles/particle_contact_resolver.rb +49 -0
- data/lib/Particles/particle_drag.rb +30 -0
- data/lib/Particles/particle_force_generator.rb +23 -0
- data/lib/Particles/particle_force_registration.rb +15 -0
- data/lib/Particles/particle_force_registry.rb +50 -0
- data/lib/Particles/particle_gravity.rb +31 -0
- data/lib/Particles/particle_ground_contacts.rb +43 -0
- data/lib/Particles/particle_link.rb +41 -0
- data/lib/Particles/particle_particle_contacts.rb +48 -0
- data/lib/Particles/particle_rod.rb +58 -0
- data/lib/Particles/particle_spring.rb +44 -0
- data/lib/Particles/particle_world.rb +116 -0
- data/lib/Particles/projectile.rb +15 -0
- data/lib/bulldog_physics.rb +43 -0
- data/lib/examples/GlStuff/gl_utility.rb +21 -0
- data/lib/examples/GlStuff/lighting.rb +35 -0
- data/lib/examples/GlStuff/material.rb +24 -0
- data/lib/examples/GlStuff/terrain.rb +216 -0
- data/lib/examples/simple_game.rb +289 -0
- data/lib/matrix3.rb +242 -0
- data/lib/matrix4.rb +230 -0
- data/lib/quaternion.rb +86 -0
- data/lib/vector3.rb +155 -0
- data/test/helper.rb +18 -0
- data/test/test_bulldog_physics.rb +7 -0
- metadata +190 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Collisions
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
# Links connect two particles together, generating a contact if * they violate the constraints of their link. It is used as a
|
8
|
+
# base class for cables and rods, and could be used as a base
|
9
|
+
# class for springs with a limit to their extension.
|
10
|
+
|
11
|
+
class ParticleContactGenerator
|
12
|
+
|
13
|
+
# Fills the given contact structure with the generated
|
14
|
+
# contact. The contact pointer should point to the first
|
15
|
+
# available contact in a contact array, where limit is the
|
16
|
+
# maximum number of contacts in the array that can be written * to. The method returns the number of contacts that have
|
17
|
+
# been written.
|
18
|
+
#def add_contact(contact, limit)
|
19
|
+
#end
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Collisions
|
4
|
+
|
5
|
+
REAL_MAX = 10000000
|
6
|
+
# The contact resolution routine for particle contacts.
|
7
|
+
# One resolver instance can be shared for the entire simulation.
|
8
|
+
class ParticleContactResolver
|
9
|
+
|
10
|
+
attr_accessor :iterations
|
11
|
+
attr_accessor :iterations_used
|
12
|
+
|
13
|
+
def initialize(iters = 2)
|
14
|
+
@iterations = iters
|
15
|
+
@iterations_used = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def resolve_contacts(contact_array, num_contacts, duration)
|
19
|
+
|
20
|
+
max_index = num_contacts
|
21
|
+
max = REAL_MAX
|
22
|
+
@iterations_used = 0
|
23
|
+
while( @iterations_used < @iterations )
|
24
|
+
|
25
|
+
contact_array.each_with_index do |contact, i|
|
26
|
+
sep_vel = contact.calculate_separating_velocity
|
27
|
+
if( sep_vel < max and ( sep_vel < 0 or contact.penetration > 0) )
|
28
|
+
max = sep_vel
|
29
|
+
max_index = i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if( max_index.eql? num_contacts)
|
34
|
+
break
|
35
|
+
end
|
36
|
+
|
37
|
+
contact_array[max_index].resolve(duration)
|
38
|
+
|
39
|
+
@iterations_used += 1
|
40
|
+
end # end while
|
41
|
+
contact_array
|
42
|
+
end # end resolve_contacts
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class ParticleDrag < ParticleForceGenerator
|
6
|
+
attr :kl # holds the velocity drag coefficient
|
7
|
+
attr :k2 # holds the velocity squared drag coefficient
|
8
|
+
|
9
|
+
def initialize(k1,k2)
|
10
|
+
super
|
11
|
+
@k1 = k1
|
12
|
+
@k2 = k2
|
13
|
+
end
|
14
|
+
|
15
|
+
def update_force(particle, duration)
|
16
|
+
force = particle.velocity
|
17
|
+
|
18
|
+
dragCoeff = force.magnitude
|
19
|
+
|
20
|
+
dragCoeff = k1 * dragCoeff + k2 * dragCoeff * dragCoeff
|
21
|
+
|
22
|
+
force.normalize
|
23
|
+
force *= -dragCoeff
|
24
|
+
particle.addForce(force)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
# A force generator can be asked to add a force to one or more particles.
|
6
|
+
class ParticleForceGenerator
|
7
|
+
|
8
|
+
attr_accessor :registrations
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@registrations = Array.new # array of ParticleForceRegistration(s)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Overload this in implementations of the interface to calculate
|
15
|
+
# and update the force applied to the given particle.
|
16
|
+
def update_force(particle, duration)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
|
4
|
+
# Keeps track of one force generator and the particle it applies to.
|
5
|
+
class ParticleForceRegistration
|
6
|
+
attr_accessor :particle, :particle_force_generator
|
7
|
+
|
8
|
+
def initialize()
|
9
|
+
@particle = Particle.new
|
10
|
+
@particle_force_generator = ParticleForceGenerator.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
|
4
|
+
class ParticleForceRegistry
|
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(particle, force_generator)
|
14
|
+
particle_registration = ParticleForceRegistration.new
|
15
|
+
particle_registration.particle = particle
|
16
|
+
particle_registration.particle_force_generator = force_generator
|
17
|
+
@registrations << particle_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(particle,force_generator)
|
26
|
+
@registrations.remove_if do |registry|
|
27
|
+
registry.particle.eql? particle 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.particle_force_generator.update_force(registry.particle,duration)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
# A force generator that applies a gravitational force. One instance * can be used for multiple particles.
|
6
|
+
class ParticleGravity < ParticleForceGenerator
|
7
|
+
attr_accessor :gravity
|
8
|
+
|
9
|
+
def initialize(gravityForce = Vector3.new(0,-9.8,0))
|
10
|
+
#super if defined?(super)
|
11
|
+
@gravity = gravityForce # vector3
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
#override
|
16
|
+
def update_force(particle, duration)
|
17
|
+
unless particle.has_infinite_mass
|
18
|
+
#puts "update_force"
|
19
|
+
#puts particle.mass
|
20
|
+
force_to_add = @gravity * particle.mass
|
21
|
+
particle.addForce(@gravity * particle.mass)
|
22
|
+
#puts particle.object_id
|
23
|
+
end
|
24
|
+
particle
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Collisions
|
4
|
+
|
5
|
+
class ParticleGroundContacts < ParticleContactGenerator
|
6
|
+
|
7
|
+
attr :particles
|
8
|
+
|
9
|
+
def initialize(particles)
|
10
|
+
@particles = particles
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_contact(contactArray, limit)
|
14
|
+
|
15
|
+
count = 0
|
16
|
+
@particles.each do |p|
|
17
|
+
|
18
|
+
y = p.position.y
|
19
|
+
|
20
|
+
if y < 0.0
|
21
|
+
contact = ParticleContact.new(p, nil)
|
22
|
+
contact.penetration = (p.position - Vector3.new(p.position.x, 0, p.position.z)).magnitude.abs - p.radius
|
23
|
+
contact.restitution = 0.2
|
24
|
+
contactArray << contact
|
25
|
+
count+=1
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
if( count >= limit)
|
30
|
+
return count
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
return count
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Collisions
|
4
|
+
|
5
|
+
|
6
|
+
# Links connect two particles together, generating a contact if * they violate the constraints of their link. It is used as a
|
7
|
+
# base class for cables and rods, and could be used as a base
|
8
|
+
# class for springs with a limit to their extension.
|
9
|
+
|
10
|
+
class ParticleLink < ParticleContactGenerator
|
11
|
+
|
12
|
+
attr_accessor :particle1
|
13
|
+
attr_accessor :particle2
|
14
|
+
|
15
|
+
def initialize(particle1, particle2)
|
16
|
+
@particle1 = particle1
|
17
|
+
@particle2 = particle2
|
18
|
+
end
|
19
|
+
|
20
|
+
# Generates the contacts to keep this link from being
|
21
|
+
# violated. This class can only ever generate a single
|
22
|
+
# contact, so the pointer can be a pointer to a single
|
23
|
+
# element, the limit parameter is assumed to be at least 1
|
24
|
+
# (0 isn’t valid), and the return value is 0 if the
|
25
|
+
# cable wasn’t overextended, or 1 if a contact was needed. *
|
26
|
+
# NB: This method is declared in the same way (as pure
|
27
|
+
# virtual) in the parent class, but is replicated here for
|
28
|
+
# documentation purposes.
|
29
|
+
|
30
|
+
virtual :add_contact
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
virtual :current_length
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Collisions
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
class ParticleParticleContacts < ParticleContactGenerator
|
8
|
+
|
9
|
+
attr :particles
|
10
|
+
|
11
|
+
def initialize(particles)
|
12
|
+
@particles = particles
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_contact(contactArray, limit)
|
16
|
+
|
17
|
+
count = 0
|
18
|
+
@particles.each do |p|
|
19
|
+
|
20
|
+
|
21
|
+
other_particles = @particles.select{|x| x.object_id != p.object_id}
|
22
|
+
other_particles.each do |other|
|
23
|
+
|
24
|
+
if (p.position - other.position).magnitude < (p.radius + other.radius)
|
25
|
+
contact = ParticleContact.new(p, other)
|
26
|
+
contact.penetration = (p.position - other.position).magnitude - (p.radius + other.radius)
|
27
|
+
contact.restitution = 0.5
|
28
|
+
contactArray << contact
|
29
|
+
count+=1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
if( count >= limit)
|
35
|
+
return count
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
return count
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Collisions
|
4
|
+
|
5
|
+
|
6
|
+
# Cables link a pair of particles, generating a contact if they stray too far apart.
|
7
|
+
class ParticleRod < ParticleLink
|
8
|
+
|
9
|
+
|
10
|
+
# Holds the length of the rod
|
11
|
+
attr_accessor :length
|
12
|
+
|
13
|
+
|
14
|
+
def initialize(particle1, particle2, length)
|
15
|
+
super(particle1, particle2)
|
16
|
+
@length = length
|
17
|
+
puts "CREATING ROD WITH LENGTH #{@length}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# * Fills the given contact structure with the contact needed to keep the rod from extending or compressing.
|
21
|
+
def add_contact(contactArray, limit)
|
22
|
+
current_len = (@particle1.position - @particle2.position).magnitude
|
23
|
+
|
24
|
+
if( current_len == @length)
|
25
|
+
return 0
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
contact = ParticleContact.new(@particle1, @particle2)
|
30
|
+
|
31
|
+
|
32
|
+
# The contact normal depends on whether we’re extending or compressing.
|
33
|
+
|
34
|
+
if( current_len > @length)
|
35
|
+
contact.penetration = (current_len - @length)
|
36
|
+
#contact.penetration = -1
|
37
|
+
else
|
38
|
+
contact.contact_normal *= -1
|
39
|
+
contact.penetration = @length - current_len
|
40
|
+
#contact.penetration = 1
|
41
|
+
end
|
42
|
+
|
43
|
+
contact.restitution = 0
|
44
|
+
contactArray << contact
|
45
|
+
|
46
|
+
return 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_length
|
50
|
+
(@particle1.position - @particle2.position).magnitude
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module BulldogPhysics
|
2
|
+
module Particles
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class ParticleSpring < ParticleForceGenerator
|
6
|
+
|
7
|
+
attr_accessor :other_particle # particle at other end of the spring
|
8
|
+
attr_accessor :spring_constant
|
9
|
+
attr_accessor :rest_length # rest length of the string
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(other, spring_const, rest_len)
|
13
|
+
@other_particle = other
|
14
|
+
@spring_constant = spring_const
|
15
|
+
@rest_length = rest_len
|
16
|
+
end
|
17
|
+
|
18
|
+
def update_force(particle, duration)
|
19
|
+
|
20
|
+
#calculate vector of the spring
|
21
|
+
force = particle.position.dup
|
22
|
+
|
23
|
+
force.subtractVector(@other_particle.position)
|
24
|
+
# calculate magnitude of the force
|
25
|
+
magnitude = force.magnitude
|
26
|
+
magnitude = (magnitude - @rest_length).abs
|
27
|
+
magnitude *= @spring_constant
|
28
|
+
|
29
|
+
#calculate final force and apply it
|
30
|
+
force.normalize
|
31
|
+
|
32
|
+
force.multiplyByScalar(-magnitude)
|
33
|
+
particle.addForce(force)
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|