bulldog_physics 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.document +5 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +22 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +25 -0
  6. data/Rakefile +55 -0
  7. data/VERSION +1 -0
  8. data/bulldog_physics.gemspec +92 -0
  9. data/lib/Particles/particle.rb +75 -0
  10. data/lib/Particles/particle_anchored_spring.rb +42 -0
  11. data/lib/Particles/particle_cable.rb +52 -0
  12. data/lib/Particles/particle_contact.rb +121 -0
  13. data/lib/Particles/particle_contact_generator.rb +28 -0
  14. data/lib/Particles/particle_contact_resolver.rb +49 -0
  15. data/lib/Particles/particle_drag.rb +30 -0
  16. data/lib/Particles/particle_force_generator.rb +23 -0
  17. data/lib/Particles/particle_force_registration.rb +15 -0
  18. data/lib/Particles/particle_force_registry.rb +50 -0
  19. data/lib/Particles/particle_gravity.rb +31 -0
  20. data/lib/Particles/particle_ground_contacts.rb +43 -0
  21. data/lib/Particles/particle_link.rb +41 -0
  22. data/lib/Particles/particle_particle_contacts.rb +48 -0
  23. data/lib/Particles/particle_rod.rb +58 -0
  24. data/lib/Particles/particle_spring.rb +44 -0
  25. data/lib/Particles/particle_world.rb +116 -0
  26. data/lib/Particles/projectile.rb +15 -0
  27. data/lib/bulldog_physics.rb +43 -0
  28. data/lib/examples/GlStuff/gl_utility.rb +21 -0
  29. data/lib/examples/GlStuff/lighting.rb +35 -0
  30. data/lib/examples/GlStuff/material.rb +24 -0
  31. data/lib/examples/GlStuff/terrain.rb +216 -0
  32. data/lib/examples/simple_game.rb +289 -0
  33. data/lib/matrix3.rb +242 -0
  34. data/lib/matrix4.rb +230 -0
  35. data/lib/quaternion.rb +86 -0
  36. data/lib/vector3.rb +155 -0
  37. data/test/helper.rb +18 -0
  38. data/test/test_bulldog_physics.rb +7 -0
  39. metadata +190 -0
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem 'require_all'
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.4"
12
+ gem "rcov", ">= 0"
13
+ end
@@ -0,0 +1,22 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.4)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rake (0.9.2.2)
10
+ rcov (0.9.11)
11
+ require_all (1.2.1)
12
+ shoulda (2.11.3)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ bundler (~> 1.0.0)
19
+ jeweler (~> 1.6.4)
20
+ rcov
21
+ require_all
22
+ shoulda
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Conner Wingard
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,25 @@
1
+ = bulldog_physics
2
+
3
+ bulldog_physics started as a school project at Champlain College. It has been put together from
4
+ code samples provided in the book "Game Physics Engine Development : How to Build a Robust Commercial-Grade Physics Engine for your game" by Ian Millington. As the code samples in the book were in C++,
5
+ all of it had to be converted. As such, a lot of it still looks C like.
6
+
7
+ There is an example showing some of the things that the engine can do, gravity, bouncing of planes, being connected via springs (of various types) . Many more effects can be added by merely by subclassing one of the ForceGenerators.
8
+
9
+ Documentation will be created as soon as time allows for me to do so.
10
+
11
+ == Contributing to bulldog_physics
12
+
13
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
14
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
15
+ * Fork the project
16
+ * Start a feature/bugfix branch
17
+ * Commit and push until you are happy with your contribution
18
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
19
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
20
+
21
+ == Copyright
22
+
23
+ Copyright (c) 2012 Conner Wingard. See LICENSE.txt for
24
+ further details.
25
+
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "bulldog_physics"
18
+ gem.homepage = "http://github.com/ConnerMan/bulldog_physics"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A 3D Physics Engine in Ruby}
21
+ gem.description = %Q{3D Physics Engine written in pure ruby that can be used for creating 3d simulations or games}
22
+ gem.email = "conner.wingard@gmail.com"
23
+ gem.authors = ["Conner Wingard"]
24
+
25
+ gem.add_dependency 'require_all'
26
+ # dependencies defined in Gemfile
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |test|
39
+ test.libs << 'test'
40
+ test.pattern = 'test/**/test_*.rb'
41
+ test.verbose = true
42
+ test.rcov_opts << '--exclude "gems/*"'
43
+ end
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "bulldog_physics #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,92 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "bulldog_physics"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Conner Wingard"]
12
+ s.date = "2012-04-24"
13
+ s.description = "3D Physics Engine written in pure ruby that can be used for creating 3d simulations or games"
14
+ s.email = "conner.wingard@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bulldog_physics.gemspec",
28
+ "lib/Particles/particle.rb",
29
+ "lib/Particles/particle_anchored_spring.rb",
30
+ "lib/Particles/particle_cable.rb",
31
+ "lib/Particles/particle_contact.rb",
32
+ "lib/Particles/particle_contact_generator.rb",
33
+ "lib/Particles/particle_contact_resolver.rb",
34
+ "lib/Particles/particle_drag.rb",
35
+ "lib/Particles/particle_force_generator.rb",
36
+ "lib/Particles/particle_force_registration.rb",
37
+ "lib/Particles/particle_force_registry.rb",
38
+ "lib/Particles/particle_gravity.rb",
39
+ "lib/Particles/particle_ground_contacts.rb",
40
+ "lib/Particles/particle_link.rb",
41
+ "lib/Particles/particle_particle_contacts.rb",
42
+ "lib/Particles/particle_rod.rb",
43
+ "lib/Particles/particle_spring.rb",
44
+ "lib/Particles/particle_world.rb",
45
+ "lib/Particles/projectile.rb",
46
+ "lib/bulldog_physics.rb",
47
+ "lib/examples/GlStuff/gl_utility.rb",
48
+ "lib/examples/GlStuff/lighting.rb",
49
+ "lib/examples/GlStuff/material.rb",
50
+ "lib/examples/GlStuff/terrain.rb",
51
+ "lib/examples/simple_game.rb",
52
+ "lib/matrix3.rb",
53
+ "lib/matrix4.rb",
54
+ "lib/quaternion.rb",
55
+ "lib/vector3.rb",
56
+ "test/helper.rb",
57
+ "test/test_bulldog_physics.rb"
58
+ ]
59
+ s.homepage = "http://github.com/ConnerMan/bulldog_physics"
60
+ s.licenses = ["MIT"]
61
+ s.require_paths = ["lib"]
62
+ s.rubygems_version = "1.8.13"
63
+ s.summary = "A 3D Physics Engine in Ruby"
64
+
65
+ if s.respond_to? :specification_version then
66
+ s.specification_version = 3
67
+
68
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69
+ s.add_runtime_dependency(%q<require_all>, [">= 0"])
70
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
71
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
72
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
73
+ s.add_development_dependency(%q<rcov>, [">= 0"])
74
+ s.add_runtime_dependency(%q<require_all>, [">= 0"])
75
+ else
76
+ s.add_dependency(%q<require_all>, [">= 0"])
77
+ s.add_dependency(%q<shoulda>, [">= 0"])
78
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
79
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
80
+ s.add_dependency(%q<rcov>, [">= 0"])
81
+ s.add_dependency(%q<require_all>, [">= 0"])
82
+ end
83
+ else
84
+ s.add_dependency(%q<require_all>, [">= 0"])
85
+ s.add_dependency(%q<shoulda>, [">= 0"])
86
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
87
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
88
+ s.add_dependency(%q<rcov>, [">= 0"])
89
+ s.add_dependency(%q<require_all>, [">= 0"])
90
+ end
91
+ end
92
+
@@ -0,0 +1,75 @@
1
+ module BulldogPhysics
2
+ module Particles
3
+
4
+ class Particle
5
+
6
+ attr_accessor :position, :velocity, :acceleration, :damping, :force_accum, :mass, :material, :radius, :frozen
7
+
8
+
9
+ def initialize()
10
+ @position = Vector3.new
11
+ @velocity = Vector3.new
12
+ @acceleration = Vector3.new
13
+ @force_accum = Vector3.new
14
+ @mass = 0
15
+ @damping = 0.0
16
+ @material = Material.new
17
+ @radius = 0.5
18
+ @frozen = false
19
+ end
20
+
21
+ def inverse_mass
22
+ if @mass.eql? 0
23
+ return 0
24
+ else
25
+ return 1.0 / @mass
26
+ end
27
+ end
28
+
29
+
30
+ def integrate(duration)
31
+
32
+ if @frozen
33
+ clearAccumulator
34
+ return
35
+ end
36
+ if(inverse_mass <= 0.0)
37
+ return
38
+ end
39
+
40
+ if(duration < 0.0)
41
+ raise Exception.new
42
+ end
43
+
44
+ @position.addScaledVector(@velocity, duration)
45
+
46
+ resulting_acc = @acceleration.dup
47
+ resulting_acc.addScaledVector(@force_accum, inverse_mass)
48
+
49
+ @velocity.addScaledVector(resulting_acc, duration)
50
+
51
+ unless @damping == 0.0
52
+ modifier = @damping ** duration
53
+ @velocity.multiplyByScalar( modifier )
54
+ end
55
+
56
+
57
+ clearAccumulator
58
+ end
59
+
60
+ def addForce(forceVector3)
61
+ @force_accum.addVector( forceVector3 )
62
+ end
63
+
64
+ def clearAccumulator
65
+ @force_accum.clear();
66
+ end
67
+
68
+ def has_infinite_mass
69
+ @mass < 0
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,42 @@
1
+ module BulldogPhysics
2
+ module Particles
3
+ module Generators
4
+
5
+ class ParticleAnchoredSpring < ParticleForceGenerator
6
+
7
+ attr_accessor :anchor # location of the anchored end of the spring
8
+ attr_accessor :spring_constant
9
+ attr_accessor :rest_length # rest length of the string
10
+
11
+
12
+ def initialize(anchor, spring_const, rest_len)
13
+ @anchor = anchor
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
+ force = force.subtractVector(@anchor)
23
+ # calculate magnitude of the force
24
+ magnitude = force.magnitude
25
+ magnitude = (@rest_length - magnitude) * @spring_constant
26
+ magnitude *= @spring_constant
27
+
28
+ #calculate final force and apply it
29
+ force = force.unit
30
+
31
+ force.multiplyByScalar(magnitude)
32
+ particle.addForce(force)
33
+ end
34
+
35
+
36
+
37
+ end
38
+
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ module BulldogPhysics
2
+ module Particles
3
+ module Collisions
4
+
5
+
6
+
7
+ # Cables link a pair of particles, generating a contact if they stray too far apart.
8
+ class ParticleCable < ParticleLink
9
+
10
+
11
+ # Holds the maximum length of the cable.
12
+ attr_accessor :max_length
13
+
14
+ # Holds the restitution (bounciness) of the cable.
15
+ attr_accessor :restitution
16
+
17
+
18
+ def initialize(particle1, particle2, max_length = 1.0, restitution = 1.0)
19
+ super(particle1, particle2)
20
+ @max_length = max_length
21
+ @restitution = restitution
22
+ end
23
+
24
+ # see ParticleLink for description
25
+ def add_contact(contactArray, limit)
26
+ length = (@particle1.position - @particle2.position).magnitude
27
+
28
+
29
+ if( length < @max_length)
30
+ return 0
31
+ end
32
+
33
+ contact = ParticleContact.new(@particle1, @particle2)
34
+
35
+ contact.penetration = length - @max_length
36
+ contact.restitution = @restitution
37
+
38
+ contactArray << contact
39
+
40
+ return 1
41
+ end
42
+
43
+ def current_length
44
+ (@particle1.position - @particle2.position).magnitude
45
+ end
46
+
47
+ end
48
+
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,121 @@
1
+ module BulldogPhysics
2
+ module Particles
3
+ module Collisions
4
+
5
+ class ParticleContact
6
+
7
+ attr_accessor :particle1
8
+ attr_accessor :particle2
9
+ attr_accessor :restitution
10
+ attr_accessor :contact_normal
11
+ attr_accessor :penetration
12
+
13
+ def initialize(particle1, particle2 = nil, contact_normal = Vector3.new(0,1,0), penetration = 0)
14
+ @particle1 = particle1
15
+ @particle2 = particle2
16
+ @penetration = penetration
17
+ @contact_normal = contact_normal
18
+ unless @particle2.nil?
19
+ @contact_normal = (@particle1.position - @particle2.position).unit
20
+ #@penetration = (@particle1.position - @particle2.position).magnitude.abs - (@particle1.radius + @particle2.radius)
21
+ #puts "PENETRATION #{@penetration}"
22
+ end
23
+ @restitution = 0.0
24
+ end
25
+
26
+ def resolve(duration)
27
+ resolve_velocity(duration)
28
+ resolve_interpenetration(duration)
29
+ end
30
+
31
+ def calculate_separating_velocity
32
+ relative_velocity = @particle1.velocity
33
+ unless @particle2.nil?
34
+ relative_velocity -= @particle2.velocity
35
+ end
36
+ return relative_velocity.scalarProduct(@contact_normal)
37
+ end
38
+
39
+
40
+ private
41
+
42
+ def resolve_velocity(duration)
43
+
44
+ #Find the velocity in the direction of the contact.
45
+ separating_velocity = calculate_separating_velocity()
46
+
47
+ # Check if it needs to be resolved.
48
+ if(separating_velocity > 0)
49
+ # The contact is either separating, or stationary; there’s no impulse required.
50
+ return
51
+ end
52
+ new_sep_velocity = -separating_velocity * @restitution
53
+ delta_velocity = new_sep_velocity - separating_velocity
54
+
55
+ total_inverse_mass = @particle1.inverse_mass
56
+
57
+
58
+ unless( @particle2.nil? )
59
+ total_inverse_mass += @particle2.inverse_mass
60
+ end
61
+
62
+ if( total_inverse_mass <= 0)
63
+ return
64
+ end
65
+
66
+ impulse = delta_velocity / total_inverse_mass
67
+
68
+ impulse_per_i_mass = @contact_normal * impulse
69
+
70
+ @particle1.velocity = @particle1.velocity + ( impulse_per_i_mass * @particle1.inverse_mass )
71
+
72
+ unless( @particle2.nil? )
73
+ @particle2.velocity = @particle2.velocity + (impulse_per_i_mass * -@particle2.inverse_mass )
74
+ end
75
+
76
+ end
77
+
78
+ def resolve_interpenetration(duration)
79
+ if(@penetration <= 0)
80
+ return
81
+ end
82
+
83
+ total_inverse_mass = @particle1.inverse_mass
84
+ unless( @particle2.nil? )
85
+ total_inverse_mass += @particle2.inverse_mass
86
+ end
87
+
88
+ if(total_inverse_mass <= 0)
89
+ return
90
+ end
91
+
92
+ # Calculate the movement amounts.
93
+ move_per_i_mass = @contact_normal * (@penetration / total_inverse_mass)
94
+
95
+ particle_movement_1 = move_per_i_mass * @particle1.inverse_mass
96
+ particle_movement_2 = Vector3.new
97
+
98
+ unless( @particle2.nil? )
99
+ particle_movement_2 = move_per_i_mass * -@particle2.inverse_mass
100
+ else
101
+ particle_movement_2.clear()
102
+ end
103
+
104
+ @particle1.position = @particle1.position + particle_movement_1
105
+
106
+ unless( @particle2.nil? )
107
+ @particle2.position = @particle2.position + particle_movement_2
108
+ end
109
+
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
116
+
117
+
118
+
119
+
120
+
121
+