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,289 @@
1
+ require 'rubygems'
2
+ require 'rubygame'
3
+ begin
4
+ require 'opengl'
5
+ include Gl,Glu,Glut
6
+ rescue LoadError
7
+ puts "ATTENTION: This demo requires the opengl extension for ruby."
8
+ raise
9
+ end
10
+
11
+ require 'bulldog_physics'
12
+
13
+ require_all 'GlStuff'
14
+
15
+ include BulldogPhysics
16
+ include BulldogPhysics::Particles
17
+ include BulldogPhysics::Particles::Generators
18
+ include BulldogPhysics::Particles::Collisions
19
+ include Rubygame
20
+ include GlStuff
21
+
22
+ ROD_SPECULAR = [1.0, 0.0, 0.0, 0.5]
23
+ BALL_SPECULAR = [1.0, 0.0, 1.0, 0.5]
24
+ SPRING_SPECULAR = [0.0, 1.0, 0.0, 0.5]
25
+ ANCHORED_SPECULAR = [0.0, 0.0, 1.0, 0.5]
26
+ ANCHOR_SPECULAR = [ 0.0, 0.5, 1.0, 0.5]
27
+ PARTICLE_DIFFUSE = [1.0, 0.0, 0.0, 0.5]
28
+ SCALE = 500.0
29
+ BULLET_LIFE = 20000.0
30
+ class Game
31
+
32
+
33
+ GRAVITATIONAL_CONST = 6.673 * (10 ** -4)
34
+
35
+ START_POSITION = Vector3.new(0.75,15,0)
36
+
37
+ SMOOTH = true
38
+ def initialize(width,height)
39
+
40
+ @width = width
41
+ @height = height
42
+ @queue = EventQueue.new
43
+ @queue.enable_new_style_events
44
+ @clock = Clock.new
45
+ @lighting = Lighting.new
46
+ @x_trans = 0; @y_trans = -5; @z_trans = -25
47
+ @font = TTF.new "/Library/Fonts/Times New Roman.ttf",point_size
48
+
49
+ @world = ParticleWorld.new(20, 1)
50
+
51
+
52
+
53
+ @anchor = Particle.new
54
+ @anchor.mass = 1
55
+ @anchor.position = Vector3.new(-5,0,0)
56
+ @anchor.damping = 1
57
+ @anchor.velocity = Vector3.new(0,0.0,0)
58
+ @anchor.material.specular = ANCHOR_SPECULAR
59
+ @anchor.material.diffuse = PARTICLE_DIFFUSE
60
+
61
+ #@world.add_gravity()
62
+
63
+
64
+ @ball = Particle.new
65
+ @ball.mass = 1
66
+ @ball.position = START_POSITION.dup
67
+ @ball.damping = 1
68
+ @ball.material.specular = BALL_SPECULAR
69
+ @ball.material.diffuse = PARTICLE_DIFFUSE
70
+ @ball.frozen = true
71
+
72
+
73
+ @current_ball = Particle.new
74
+ @current_ball.position = @ball.position
75
+ @current_ball.velocity = @ball.velocity
76
+ @current_ball.mass = @ball.mass
77
+ @current_ball.damping = @ball.damping
78
+ @current_ball.frozen = true
79
+ @world.registry.add( @current_ball, ParticleGravity.new())
80
+ @world.particles << @current_ball
81
+
82
+
83
+ particle1 = Particle.new
84
+ particle1.mass = 1
85
+ particle1.position = Vector3.new(5,2,0)
86
+ particle1.damping = 1
87
+ particle1.velocity = Vector3.new(0,2.0,0)
88
+ particle1.material.specular = ANCHOR_SPECULAR
89
+ particle1.material.diffuse = PARTICLE_DIFFUSE
90
+
91
+ particle2 = Particle.new
92
+ particle2.mass = 1
93
+ particle2.position = Vector3.new(0,2,0)
94
+ particle2.damping = 1
95
+ particle2.velocity = Vector3.new(0,0.0,0)
96
+ particle2.material.specular = ANCHOR_SPECULAR
97
+ particle2.material.diffuse = PARTICLE_DIFFUSE
98
+
99
+ prod = ParticleRod.new(particle1, particle2, 2.0)
100
+
101
+ #@world.particles << particle1
102
+ #@world.particles << particle2
103
+ #@world.contact_generators << prod
104
+
105
+ @world.contact_generators << ParticleGroundContacts.new(@world.particles)
106
+ @world.contact_generators << ParticleParticleContacts.new(@world.particles)
107
+
108
+ for i in 1..5
109
+ for j in 1..5
110
+ part = Particle.new
111
+ part.mass = 0
112
+ offset = j % 2 == 0 ? 1 : 0
113
+ part.position = Vector3.new( -5 + i * 2 + offset, j * 2, 0)
114
+ part.material.specular = SPRING_SPECULAR
115
+ part.material.diffuse = PARTICLE_DIFFUSE
116
+ @world.particles << part
117
+ end
118
+ end
119
+
120
+
121
+ puts "PARTICLE COUNT [ #{@world.particles.size} ] "
122
+ puts "Contact Generators count: #{@world.contact_generators.size}"
123
+
124
+
125
+ @w_down, @a_down, @s_down, @d_down = false, false, false, false
126
+
127
+ end
128
+
129
+ def init_game()
130
+ Rubygame.init
131
+
132
+ Rubygame::GL.set_attrib(Rubygame::GL::RED_SIZE, 5)
133
+ Rubygame::GL.set_attrib(Rubygame::GL::GREEN_SIZE, 5)
134
+ Rubygame::GL.set_attrib(Rubygame::GL::BLUE_SIZE, 5)
135
+ Rubygame::GL.set_attrib(Rubygame::GL::DEPTH_SIZE, 16)
136
+ Rubygame::GL.set_attrib(Rubygame::GL::DOUBLEBUFFER, 1)
137
+
138
+ @maximum_resolution = Screen.get_resolution
139
+ @screen = Screen.new([@width,@height], 16, [OPENGL])
140
+ @clock.target_framerate = 30
141
+ @clock.calibrate
142
+ @clock.enable_tick_events
143
+ ObjectSpace.garbage_collect
144
+
145
+ glViewport( 0, 0, WIDE, HIGH )
146
+ glMatrixMode( GL::PROJECTION )
147
+ glLoadIdentity( )
148
+ #GLU::Perspective( -35, WIDE/(HIGH.to_f), 0.5, 30.0)
149
+ GLU::Perspective( 45, WIDE/(HIGH.to_f), 0.5, 100.0)
150
+ glMatrixMode( GL::MODELVIEW )
151
+ glLoadIdentity( )
152
+
153
+
154
+
155
+ @lighting.setup
156
+
157
+ glShadeModel (GL_SMOOTH);
158
+ glEnable(GL::DEPTH_TEST)
159
+ glEnable(GL_LIGHTING)
160
+ glEnable(GL_LIGHT0)
161
+ glDepthFunc(GL::LESS)
162
+
163
+
164
+
165
+ end
166
+
167
+
168
+ def run()
169
+ catch(:rubygame_quit) do
170
+ loop do
171
+ @queue.each do |event|
172
+ case event
173
+ when Rubygame::Events::KeyPressed
174
+ # puts event.key
175
+ case event.key
176
+ when :escape
177
+ throw :rubygame_quit
178
+ when :q
179
+ throw :rubygame_quit
180
+ when :w
181
+ @w_down = true
182
+ when :s
183
+ @s_down = true
184
+ when :a
185
+ @a_down = true
186
+ when :d
187
+ @d_down = true
188
+ when :l
189
+ @world.particles.each do |part|
190
+ puts part.position
191
+ puts "NUM CONTACTS: #{@world.contacts.size}"
192
+ end
193
+ when :space
194
+ @current_ball.frozen = !@current_ball.frozen
195
+ when :r
196
+ puts "RESET"
197
+ @current_ball = Particle.new
198
+ @current_ball.position = START_POSITION.dup
199
+ @current_ball.velocity = @ball.velocity.dup
200
+ @current_ball.mass = @ball.mass
201
+ @current_ball.damping = @ball.damping
202
+ @current_ball.frozen = true
203
+ @world.registry.add( @current_ball, ParticleGravity.new())
204
+ @world.particles << @current_ball
205
+ end
206
+ when Rubygame::Events::KeyReleased
207
+ case event.key
208
+ when :w
209
+ @w_down = false
210
+ when :a
211
+ @a_down = false
212
+ when :d
213
+ @d_down = false
214
+ when :s
215
+ @s_down = false
216
+ end
217
+ when Rubygame::Events::MouseMoved
218
+ puts "Mouse moved to #{event.rel.inspect} with #{event.buttons.inspect} pressed"
219
+ if( event.buttons.include? :mouse_right )
220
+ puts "mouse_right"
221
+ @current_ball.position.x += event.rel[0] / 100.0
222
+ @current_ball.position.y -= event.rel[1] / 100.0
223
+ end
224
+ when Rubygame::Events::QuitRequested
225
+ throw :rubygame_quit
226
+ end
227
+ end
228
+
229
+ if @w_down;@current_ball.position.y += 0.01; end;
230
+ if @s_down;@current_ball.position.y -= 0.01; end;
231
+ if @a_down;@current_ball.position.x -= 0.01; end;
232
+ if @d_down;@current_ball.position.x += 0.01; end;
233
+
234
+ @time_elapsed = @clock.tick()
235
+ @time_alive = @clock.lifetime
236
+ seconds_elapsed = @time_elapsed.milliseconds / 1000.0
237
+ @world.start_frame
238
+
239
+ glClearColor(0.0, 0.0, 0.0, -1.0)
240
+ glClear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT)
241
+ glLoadIdentity()
242
+
243
+ glTranslatef(@x_trans,@y_trans,@z_trans)
244
+
245
+ glPushMatrix()
246
+ glBegin(GL_QUADS)
247
+ glVertex3f(-1000, -1 , 1000)
248
+ glVertex3f(-1000, -1, -1000)
249
+ glVertex3f(1000, -1, -1000)
250
+ glVertex3f(1000, -1, 1000)
251
+ glEnd
252
+ glPopMatrix()
253
+
254
+
255
+ draw_skybox()
256
+
257
+ #glClear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT)
258
+ @world.particles.each do |particle|
259
+ p = particle.position
260
+ glPushMatrix()
261
+ glTranslatef(p.x, p.y, p.z)
262
+ particle.material.setup_material()
263
+ glutSolidSphere(particle.radius, 16, 16)
264
+ glPopMatrix()
265
+ end
266
+
267
+ @world.run_physics(seconds_elapsed)
268
+
269
+ Rubygame::GL.swap_buffers()
270
+ @screen.update
271
+ @screen.flip
272
+ ObjectSpace.garbage_collect
273
+
274
+ end
275
+ end
276
+
277
+ end
278
+
279
+ end
280
+
281
+
282
+ REAL_MAX = 10000000
283
+ WIDE = 1280
284
+ HIGH = 1024
285
+
286
+
287
+ game = Game.new(WIDE, HIGH)
288
+ game.init_game
289
+ game.run
@@ -0,0 +1,242 @@
1
+ module BulldogPhysics
2
+
3
+ class Matrix3
4
+
5
+
6
+ attr_accessor :data
7
+
8
+
9
+ def initialize(*args)
10
+ @data = Array.new
11
+
12
+ if(args.size == 0)
13
+ @data = [ 0, 0, 0, \
14
+ 0, 0, 0, \
15
+ 0, 0, 0]
16
+ elsif( args[0].is_a? Float)
17
+ @data = [ args[0], args[1], args[2],\
18
+ args[3], args[4], args[5],\
19
+ args[6], args[7], args[8]]
20
+ elsif( args[0].is_a? Vector3)
21
+ setComponents(args[0], args[1], args[2])
22
+ end
23
+ end
24
+
25
+
26
+
27
+ def *(vector)
28
+
29
+ if( @data.size < 8)
30
+ raise Exception.new("@data not big enough for vector math")
31
+ end
32
+
33
+ if( vector.is_a? Matrix3)
34
+ o = vector.dup
35
+ return Matrix3.new(@data[0]*o.data[0] + @data[1]*o.data[3] + @data[2]*o.data[6], @data[0]*o.data[1] + @data[1]*o.data[4] + @data[2]*o.data[7], @data[0]*o.data[2] + @data[1]*o.data[5] + @data[2]*o.data[8], \
36
+ @data[3]*o.data[0] + @data[4]*o.data[3] + @data[5]*o.data[6], @data[3]*o.data[1] + @data[4]*o.data[4] + @data[5]*o.data[7], @data[3]*o.data[2] + @data[4]*o.data[5] + @data[5]*o.data[8], \
37
+ @data[6]*o.data[0] + @data[7]*o.data[3] + @data[8]*o.data[6], @data[6]*o.data[1] + @data[7]*o.data[4] + @data[8]*o.data[7], @data[6]*o.data[2] + @data[7]*o.data[5] + @data[8]*o.data[8] )
38
+ elsif vector.is_a? Float
39
+ scalar = vector
40
+ return Matrix3.new(@data[0] * scalar, @data[1] * scalar, @data[2] * scalar, \
41
+ @data[3] * scalar, @data[4] * scalar, @data[5] * scalar, \
42
+ @data[6] * scalar, @data[7] * scalar, @data[8] * scalar)
43
+ else
44
+
45
+ return Vector3.new( (vector.x * @data[0]) + (vector.y * @data[1]) + (vector.z * @data[2]) , \
46
+ vector.x * @data[3] + vector.y * @data[4] + vector.z * @data[5] , \
47
+ vector.x * @data[6] + vector.y * @data[7] + vector.z * @data[8])
48
+ end
49
+
50
+ end
51
+
52
+ def +(o)
53
+ Matrix3.new(
54
+ @data[0] + o.data[0], @data[1] + o.data[1], @data[2] + o.data[2], \
55
+ @data[3] + o.data[3], @data[4] + o.data[4], @data[5] + o.data[5], \
56
+ @data[6] + o.data[6], @data[7] + o.data[7], @data[8] + o.data[8] \
57
+ )
58
+ end
59
+
60
+ def multiplyByMatrix(o)
61
+ o = o.dup
62
+
63
+ t1 = @data[0]*o.data[0] + @data[1]*o.data[3] + @data[2]*o.data[6];
64
+ t2 = @data[0]*o.data[1] + @data[1]*o.data[4] + @data[2]*o.data[7];
65
+ t3 = @data[0]*o.data[2] + @data[1]*o.data[5] + @data[2]*o.data[8];
66
+ @data[0] = t1;
67
+ @data[1] = t2;
68
+ @data[2] = t3;
69
+ t1 = @data[3]*o.data[0] + @data[4]*o.data[3] + @data[5]*o.data[6];
70
+ t2 = @data[3]*o.data[1] + @data[4]*o.data[4] + @data[5]*o.data[7];
71
+ t3 = @data[3]*o.data[2] + @data[4]*o.data[5] + @data[5]*o.data[8];
72
+ @data[3] = t1;
73
+ @data[4] = t2;
74
+ @data[5] = t3;
75
+ t1 = @data[6]*o.data[0] + @data[7]*o.data[3] + @data[8]*o.data[6];
76
+ t2 = @data[6]*o.data[1] + @data[7]*o.data[4] + @data[8]*o.data[7];
77
+ t3 = @data[6]*o.data[2] + @data[7]*o.data[5] + @data[8]*o.data[8];
78
+ @data[6] = t1;
79
+ @data[7] = t2;
80
+ @data[8] = t3;
81
+ end
82
+
83
+ def multiplyByScalar(scalar)
84
+ @data[0] *= scalar; @data[1] *= scalar; @data[2] *= scalar;
85
+ @data[3] *= scalar; @data[4] *= scalar; @data[5] *= scalar;
86
+ @data[6] *= scalar; @data[7] *= scalar; @data[8] *= scalar;
87
+ end
88
+
89
+ def transform(vector)
90
+ self * vector
91
+ end
92
+
93
+ def transformTranspose(vector)
94
+ Vector3.new(
95
+ vector.x * @data[0] + vector.y * @data[3] + vector.z * @data[6],
96
+ vector.x * @data[1] + vector.y * @data[4] + vector.z * @data[7],
97
+ vector.x * @data[2] + vector.y * @data[5] + vector.z * @data[8]
98
+ )
99
+ end
100
+
101
+ def setOrientation(q)
102
+ @data[0] = 1 - (2*q.j*q.j + 2*q.k*q.k);
103
+ @data[1] = 2*q.i*q.j + 2*q.k*q.r;
104
+ @data[2] = 2*q.i*q.k - 2*q.j*q.r;
105
+ @data[3] = 2*q.i*q.j - 2*q.k*q.r;
106
+ @data[4] = 1 - (2*q.i*q.i + 2*q.k*q.k);
107
+ @data[5] = 2*q.j*q.k + 2*q.i*q.r;
108
+ @data[6] = 2*q.i*q.k + 2*q.j*q.r;
109
+ @data[7] = 2*q.j*q.k - 2*q.i*q.r;
110
+ @data[8] = 1 - (2*q.i*q.i + 2*q.j*q.j);
111
+ end
112
+
113
+ def getAxisVector(i)
114
+ Vector3.new( @data[i], @data[i+3], @data[i+6])
115
+ end
116
+
117
+
118
+ def setInverse(m)
119
+ t4 = m.data[0]*m.data[4];
120
+ t6 = m.data[0]*m.data[5];
121
+ t8 = m.data[1]*m.data[3];
122
+ t10 = m.data[2]*m.data[3];
123
+ t12 = m.data[1]*m.data[6];
124
+ t14 = m.data[2]*m.data[6];
125
+
126
+ # Calculate the determinant
127
+ t16 = (t4*m.data[8] - t6*m.data[7] - t8*m.data[8]+
128
+ t10*m.data[7] + t12*m.data[5] - t14*m.data[4]);
129
+
130
+ # Make sure the determinant is non-zero.
131
+ if (t16 == 0.0)
132
+ return
133
+ end
134
+
135
+ t17 = 1/t16;
136
+
137
+ @data[0] = (m.data[4]*m.data[8]-m.data[5]*m.data[7])*t17;
138
+ @data[1] = -(m.data[1]*m.data[8]-m.data[2]*m.data[7])*t17;
139
+ @data[2] = (m.data[1]*m.data[5]-m.data[2]*m.data[4])*t17;
140
+ @data[3] = -(m.data[3]*m.data[8]-m.data[5]*m.data[6])*t17;
141
+ @data[4] = (m.data[0]*m.data[8]-t14)*t17;
142
+ @data[5] = -(t6-t10)*t17;
143
+ @data[6] = (m.data[3]*m.data[7]-m.data[4]*m.data[6])*t17;
144
+ @data[7] = -(m.data[0]*m.data[7]-t12)*t17;
145
+ @data[8] = (t4-t8)*t17;
146
+ end
147
+
148
+ def inverse()
149
+ result = Matrix3.new
150
+ result.setInverse(self)
151
+ return result
152
+ end
153
+
154
+ def invert()
155
+ setInverse(self)
156
+ end
157
+
158
+ def setTranspose(m)
159
+ @data[0] = m.data[0];
160
+ @data[1] = m.data[3];
161
+ @data[2] = m.data[6];
162
+ @data[3] = m.data[1];
163
+ @data[4] = m.data[4];
164
+ @data[5] = m.data[7];
165
+ @data[6] = m.data[2];
166
+ @data[7] = m.data[5];
167
+ @data[8] = m.data[8];
168
+ end
169
+
170
+
171
+ def transform_transpose(vector)
172
+ return Vector3.new(
173
+ vector.x * @data[0] + vector.y * @data[3] + vector.z * @data[6],
174
+ vector.x * @data[1] + vector.y * @data[4] + vector.z * @data[7],
175
+ vector.x * @data[2] + vector.y * @data[5] + vector.z * @data[8]
176
+ );
177
+ end
178
+
179
+ def transpose()
180
+ result = Matrix3.new
181
+ result.setTranspose(self)
182
+ return result
183
+ end
184
+
185
+ def setDiagonal(a, b, c)
186
+ setInertiaTensorCoeffs(a, b, c)
187
+ end
188
+
189
+ def setInertiaTensorCoeffs(ix, iy, iz, ixy = 0, ixz = 0,iyz = 0)
190
+ @data[0] = ix;
191
+ @data[1] = @data[3] = -ixy;
192
+ @data[2] = @data[6] = -ixz;
193
+ @data[4] = iy;
194
+ @data[5] = @data[7] = -iyz;
195
+ @data[8] = iz;
196
+ end
197
+
198
+
199
+ def setBlockInertiaTensor(vHalfSizes, mass)
200
+ squares = vHalfSizes.componentProduct(vHalfSizes)
201
+ setInertiaTensorCoeffs(0.3 * mass * (squares.y + squares.z),
202
+ 0.3 * mass * (squares.x + squares.z),
203
+ 0.3 * mass * (squares.x + squares.y))
204
+ end
205
+
206
+ def setSkewSymmetric(vector)
207
+ @data[0] = @data[4] = @data[8] = 0;
208
+ @data[1] = -vector.z;
209
+ @data[2] = vector.y;
210
+ @data[3] = vector.z;
211
+ @data[5] = -vector.x;
212
+ @data[6] = -vector.y;
213
+ @data[7] = vector.x;
214
+ end
215
+
216
+ def setComponents(vCompOne, vCompTwo, vCompThree)
217
+ @data[0] = vCompOne.x
218
+ @data[1] = vCompTwo.x
219
+ @data[2] = vCompThree.x
220
+ @data[3] = vCompOne.y
221
+ @data[4] = vCompTwo.y
222
+ @data[5] = vCompThree.y
223
+ @data[6] = vCompOne.z
224
+ @data[7] = vCompTwo.z
225
+ @data[8] = vCompThree.z
226
+ end
227
+
228
+ def self.linearInterpolate(a, b, prop)
229
+ result = Matrix3.new
230
+ for i in 0..9
231
+ result.data[i] = a.data * (1-prop) + b.data[i] * prop
232
+ end
233
+ return result
234
+ end
235
+
236
+ def to_s
237
+ "[ #{@data[0]} #{@data[1]} #{@data[2]}\n \ #{@data[3]} #{@data[4]} #{@data[5]}\n \ #{@data[6]} #{@data[7]} #{@data[8]} ]"
238
+ end
239
+
240
+ end
241
+
242
+ end