@Dodicat
Wow!
That's awesome.
billiard style ball movement
-
- Posts: 2655
- Joined: Aug 28, 2008 10:54
- Location: new york
Really cool stuff, will need to look at your code. It reminds me of this little thing I made a few years back. It has the same features except lens mapping, and it comes with both linear and rotational friction.
Cheers,
Mike
And heres the "vec2f.bi" file for good measure:
Cheers,
Mike
Code: Select all
'' impulse based 2d ball-ball and ball-line collision with friction
'' made by Mike "h4tt3n"
const pi = 4*atn(1)
const num_balls = 32
const g_acc = 2000
const scrn_wid = 900
const scrn_hgt = 600
const rest_fps = 60 '' ideal framerate
const inv_rest_fps = 1/rest_fps '' inverse ideal framerate
Const dt = 0.01 '' timestep, delta time
Const StaticFrictionVelocity = 0.01
#include once "vec2f.bi"
randomize timer
type ball_type
as uinteger col
as vec2f frc, acc, vel, pos
as Single trq, ang_acc, ang_vel, sin_ang, cos_ang, ang, mass, InverseMass, _
dens, radius, RadiusSquared, I, InverseI, Restitution, dynamicfriction, staticfriction
end type
type wall_type
as uinteger col
as vec2f pos(1 to 2), dst
as Single Restitution, dynamicfriction, staticfriction
end type
dim shared as vec2f dst, ncoll, separation_vector, vcoll, closest_point
dim shared as ball_type ptr ball
dim shared as wall_type wall(1 to 10)
dim shared as Single dst_sqd, radius, RadiusSquared, distance_, force
dim shared as integer a, b
dim shared as integer FPS, FPS_Counter
dim shared as Single FPS_Timer, t0
declare sub BallBallCollisionDetection()
declare sub BallWallCollisionDetection()
declare sub BallWallCollisionResponse(byref a as ball_type, byref w as wall_type)
declare sub IntegrateEulerCromer1stOrder()
declare sub DrawSceneToScreen()
declare sub brake()
declare function Vec_Pnt_Lin(a1 As vec2f, a2 As vec2f, p1 As vec2f) As vec2f
ball = Callocate(num_balls*sizeof(ball_type))
'' set startup condition
with wall(1)
.pos(1).x = 10
.pos(1).y = 10
.pos(2).x = scrn_wid-11
.pos(2).y = 10
end with
with wall(2)
.pos(1).x = scrn_wid-11
.pos(1).y = 10
.pos(2).x = scrn_wid-11
.pos(2).y = scrn_hgt-81
end with
with wall(3)
.pos(1).x = scrn_wid-11
.pos(1).y = scrn_hgt-81
.pos(2).x = (scrn_wid\2)+80
.pos(2).y = scrn_hgt-11
end with
with wall(4)
.pos(1).x = (scrn_wid\2)+80
.pos(1).y = scrn_hgt-11
.pos(2).x = (scrn_wid\2)
.pos(2).y = scrn_hgt-50
end with
with wall(5)
.pos(1).x = scrn_wid-250
.pos(1).y = 450
.pos(2).x = .pos(1).x+60
.pos(2).y = .pos(1).y-20
end with
with wall(6)
.pos(1).x = (scrn_wid\2)
.pos(1).y = scrn_hgt-50
.pos(2).x = 10
.pos(2).y = scrn_hgt-11
end with
with wall(7)
.pos(1).x = 10
.pos(1).y = scrn_hgt-11
.pos(2).x = 10
.pos(2).y = 10
end with
with wall(8)
.pos(1).x = 11
.pos(1).y = 100
.pos(2).x = scrn_wid-150
.pos(2).y = 150
end with
with wall(9)
.pos(1).x = 150
.pos(1).y = 300
.pos(2).x = scrn_wid-11
.pos(2).y = 250
end with
with wall(10)
.pos(1).x = 11
.pos(1).y = 400
.pos(2).x = scrn_wid-250
.pos(2).y = 450
end with
for a = 1 to ubound(wall)
with wall(a)
.Restitution = 0.9
.dynamicfriction = 0.2
.staticfriction = 0.8
end with
next
for a = 0 to num_balls-1
with ball[a]
.col = rgb(96+rnd*160, 96+rnd*160, 96+rnd*160)
.mass = 2+(rnd*18^(1/4))^4
.InverseMass = 1/.mass
.Restitution = 0.9
.dens = 0.0002
.radius = ((.mass/.dens)/((4/3)*pi))^(1/3)
.RadiusSquared = .radius*.radius
.I = (1/2)*.mass*.radius*.radius
.InverseI = 1/.I
.pos.x = .radius + rnd*(scrn_wid-2*.radius)
.pos.y = .radius + rnd*(scrn_hgt\3-2*.radius)
'.vel.Randomise(0)
.ang = rnd*2*pi
.cos_ang = cos(.ang)
.sin_ang = sin(.ang)
.ang_vel = (rnd-rnd)*100
.dynamicfriction = 0.2
.staticfriction = 0.8
end with
next
''----------------------------------------------------------------------------''
screenres scrn_wid, scrn_hgt, 16
color 0, rgb(255, 255, 255)
do
BallBallCollisionDetection()
BallWallCollisionDetection()
IntegrateEulerCromer1stOrder()
DrawSceneToScreen()
brake()
loop until multikey(1)
deallocate(ball)
end
''----------------------------------------------------------------------------''
sub BallWallCollisionDetection()
for a as integer = lbound(wall) to ubound(wall)
for b as integer = 0 to num_balls-1
closest_point = Vec_Pnt_Lin(wall(a).pos(1), wall(a).pos(2), ball[b].pos)
dst = ball[b].pos - closest_point
dst_sqd = dst.MagnitudeSquared
if dst_sqd < ball[b].RadiusSquared then
BallWallCollisionResponse(ball[b], wall(a))
end if
next
next
end sub
sub BallWallCollisionResponse(byref a as ball_type, byref w as wall_type)
distance_ = dst.Magnitude
'' collision plane normal
dim as vec2f NormalVector = dst/distance_
'' collision plane tangent
dim as vec2f TangentVector = NormalVector.Normal
'' overlap distance_
dim as Single intersection = a.radius-distance_
a.pos += intersection*NormalVector
dim as vec2f contactPointVelocity = a.vel + a.radius * TangentVector * a.ang_vel
dim as Single contactPointVelocityParallel = dot(contactPointVelocity, TangentVector)
dim as Single contactPointVelocityNormal = dot(contactPointVelocity, normalVector)
'' if ball is moving towards wall then calculate impulse
if contactPointVelocityNormal < 0 then
'' normal impulse magnitude
dim as Single impulseNormal = -(1+(w.Restitution+a.Restitution)*0.5)*contactPointVelocityNormal/a.InverseMass
dim as Single impulseRequiredParallel = -contactPointVelocityParallel/(a.InverseMass+(a.radius*a.radius/a.i))
'dim as Single ImpulseTangential
dim as Single frictioncoefficient
'' if contact point is at rest compared to the wall, then apply static
'' friction, else apply dynamic / kinetic friction
if contactpointvelocityparallel < StaticFrictionVelocity then
frictioncoefficient = (a.staticfriction+w.staticfriction)*0.5
'a.col = RGB(255, 32, 32)
else
frictioncoefficient = (a.dynamicfriction+w.dynamicfriction)*0.5
'a.col = rgb(32, 255, 32)
endif
dim as Single ImpulseTangential = IIf(Abs(impulseRequiredParallel) < frictioncoefficient*impulseNormal, _
impulseRequiredParallel, Sgn(impulseRequiredParallel)* frictioncoefficient*impulseNormal)
a.vel += (ImpulseNormal*NormalVector+ImpulseTangential*TangentVector)*a.InverseMass
a.ang_vel += ImpulseTangential*a.Radius/a.I
end if
end sub
sub BallBallCollisionDetection()
dim as integer a, b
for a = 0 to num_balls-2
for b = a+1 to num_balls-1
dst = ball[a].pos-ball[b].pos
dst_sqd = MagnitudeSquared(dst)
radius = ball[a].radius+ball[b].radius
RadiusSquared = radius*radius
If dst_sqd < RadiusSquared Then
Dim As vec2f distance_Vector = ball[a].pos-ball[b].pos
Dim As double distance_Squared = MagnitudeSquared(distance_Vector)
Dim As double distance_ = sqr(distance_Squared)
dim as vec2f NormalVector = distance_Vector/distance_
dim as vec2f TangentVector = normal(NormalVector)
dim as double intersection = radius-distance_
dim as vec2f separation_vector = (normalvector*intersection)/(ball[a].InverseMass+ball[b].InverseMass)
ball[a].pos += separation_vector*ball[a].InverseMass
ball[b].pos -= separation_vector*ball[b].InverseMass
Dim As vec2f contactpoint_A = ball[a].pos-ball[a].radius*NormalVector
Dim As vec2f contactpoint_B = ball[b].pos+ball[b].radius*NormalVector
dim as vec2f contactPointVelocity_A = ball[a].vel+ball[a].ang_vel*normal(ball[a].pos-contactpoint_A)
dim as vec2f contactPointVelocity_B = ball[b].vel+ball[b].ang_vel*normal(ball[b].pos-contactpoint_B)
dim as vec2f contactPointVelocity = contactPointVelocity_a - contactPointVelocity_b
dim as double contactPointVelocityNormal = dot(contactPointVelocity, normalVector)
if contactPointVelocityNormal < 0 Then
Dim As double Restitution = (ball[a].Restitution+ball[b].Restitution)*0.5
dim as double impulseNormal = -(1+Restitution)*contactPointVelocityNormal/(ball[a].InverseMass+ball[b].InverseMass)
Dim As double contactpointvelocityTangent = dot(contactpointvelocity, tangentvector)
Dim As double FrictionCoefficient = IIf(Abs(ContactPointVelocityTangent) < StaticFrictionVelocity, (Ball[a].StaticFriction+Ball[b].StaticFriction)*0.5, (Ball[a].DynamicFriction+Ball[b].DynamicFriction)*0.5)
Dim as double MaxImpulseTangent = -ContactPointVelocityTangent/(Ball[a].InverseMass+Ball[b].InverseMass+Ball[a].RadiusSquared*Ball[a].InverseI+Ball[b].RadiusSquared*Ball[b].InverseI)
Dim As double ImpulseTangent = IIf(Abs(MaxImpulseTangent) < FrictionCoefficient*ImpulseNormal, MaxImpulseTangent, Sgn(MaxImpulseTangent)*FrictionCoefficient*ImpulseNormal)
Dim As vec2f Impulse = ImpulseNormal*Normalvector+ImpulseTangent*TangentVector
ball[a].vel += Impulse*ball[a].InverseMass
ball[a].ang_vel += dotnormal(Impulse, ball[a].pos-contactpoint_a)*ball[a].InverseI
ball[b].vel -= Impulse*ball[b].InverseMass
ball[b].ang_vel -= dotnormal(Impulse, ball[b].pos-contactpoint_b)*ball[b].InverseI
End If
end if
next
next
end sub
sub IntegrateEulerCromer1stOrder()
dim as integer a
for a = 0 to num_balls-1
with ball[a]
.acc = .frc/.mass
.acc.y += g_acc
.frc = vec2f(0, 0)
.vel += .acc*dt
.pos += .vel*dt
.ang_acc = .trq/.I
.trq = 0
.ang_vel += .ang_acc*dt
.ang += .ang_vel*dt
.cos_ang = cos(.ang)
.sin_ang = sin(.ang)
end with
next
end sub
sub DrawSceneToScreen()
dim as integer a
screenlock
cls
locate 3, (scrn_wid\8)-4: print using "###"; fps
for a = 0 to num_balls-1
with ball[a]
circle (.pos.x, .pos.y), .radius, 0,,,1, f
circle (.pos.x, .pos.y), .radius-2, .col,,,1, f
Line(.pos.x, .pos.y)-(.pos.x+.cos_ang*.radius, .pos.y+.sin_ang*.radius), 0
end with
next
for a = 1 to ubound(wall)
with wall(a)
Line(.pos(1).x, .pos(1).y)-(.pos(2).x, .pos(2).y), .col
end with
next
screenunlock
end sub
sub brake()
if timer < fps_timer then
fps_counter += 1
else
fps = fps_counter
fps_counter = 0
fps_timer = timer+1
end if
do: sleep 1, 1: loop while timer-t0 < inv_rest_fps
t0 = timer
end sub
Function Vec_Pnt_Lin(a1 As vec2f, a2 As vec2f, p1 As vec2f) As vec2f
Dim As vec2f ab = a2-a1
Dim As vec2f ap = p1-a1
Dim As double t = dot(ap, ab)/magnitudesquared(ab)
If t < 0 Then t = 0
If t > 1 Then t = 1
Return a1+ab*t
End function
Code: Select all
''*******************************************************************************
''
'' Freebasic 2d floating point vector library
'' version 0.4b, may 2009, Michael "h4tt3n" Nissen, jernmager@yahoo.dk
''
'' function syntax:
''
'' (return type) (function name) (argument type (, ...))
''
'' function list:
''
'' vector absolute (vector) - absolute value
'' vector normal (vector) - normal vector
'' vector normalised (vector) - normalised vector
'' vector normalisednormal (vector) - normalised normal vector
'' scalar magnitude (vector) - magnitude
'' scalar magnitudesquared (vector) - magnitude squared
'' scalar distance (vector, vector) - vector distance
'' scalar distancesquared (vector, vector) - vector distance squared
'' scalar dot (vector, vector) - dot product
'' scalar dotnormal (vector, vector) - normal dot product
'' vector project (vector, vector) - vector projection
'' vector component (vector, vector) - vector component
'' vector randomise (scalar) - randomise in range +/- value
''
'' all functions are defined both as members and non-members, and thus can
'' be called in two different ways:
''
'' vector_a.function(vector_b), function(vector_a, vector_b)
'' vector_a.function(parameter), function(vector_a, parameter)
''
''*******************************************************************************
type float as double
'' 2d float vector structure
type vec2f
'' variables
as float x, y
'' constructor declarations
declare constructor ()
declare constructor (byval X as float, byval Y as float)
'' compound arithmetic member operator declarations
declare operator += (byref rhs as vec2f)
declare operator -= (byref rhs as vec2f)
declare operator *= (byref rhs as vec2f)
declare operator *= (byref rhs as float)
declare operator /= (byref rhs as float)
declare operator let (byref rhs as vec2f)
'' member function declarations
declare function absolute() as vec2f
declare function normal() as vec2f
declare function normalised() as vec2f
declare function normalisednormal() as vec2f
declare function magnitude() as float
declare function magnitudesquared() as float
declare function distance(byref rhs as vec2f) as float
declare function distancesquared(byref rhs as vec2f) as float
declare function dot(byref rhs as vec2f) as float
declare function dotnormal(byref rhs as vec2f) as float
declare function project(byref rhs as vec2f) as vec2f
declare function component(byref rhs as vec2f) as vec2f
declare function randomise(byval rhs as float) as vec2f
end type
'' unary arithmetic non-member operator declarations
declare operator - (byref rhs as vec2f) as vec2f
'' binary arithmetic non-member operator declarations
declare operator + (byval lhs as vec2f, byref rhs as vec2f) as vec2f
declare operator - (byval lhs as vec2f, byref rhs as vec2f) as vec2f
declare operator * (byval lhs as vec2f, byref rhs as vec2f) as vec2f
declare operator * (byval lhs as float, byref rhs as vec2f) as vec2f
declare operator * (byval lhs as vec2f, byval rhs as float) as vec2f
declare operator / (byval lhs as vec2f, byval rhs as float) as vec2f
'' non-member function declarations
declare function absolute (byref lhs as vec2f) as vec2f
declare function normal (byref lhs as vec2f) as vec2f
declare function normalised (byref lhs as vec2f) as vec2f
declare function normalisednormal(byref lhs as vec2f) as vec2f
declare function magnitude (byref lhs as vec2f) as float
declare function magnitudesquared (byref lhs as vec2f) as float
declare function distance (byval lhs as vec2f, byref rhs as vec2f) as float
declare function distancesquared (byval lhs as vec2f, byref rhs as vec2f) as float
declare function dot (byval lhs as vec2f, byref rhs as vec2f) as float
declare function dotnormal (byval lhs as vec2f, byref rhs as vec2f) as float
declare function project (byval lhs as vec2f, byref rhs as vec2f) as vec2f
declare function component(byval lhs as vec2f, byref rhs as vec2f) as vec2f
declare function trigonometry(byref lhs as float) as vec2f
'' constructors
constructor vec2f(): this.x = 0.0: this.y = 0.0: end constructor
constructor vec2f(byval x as float, byval y as float): this.x = x: this.y = y: end constructor
'' compound arithmetic member operators
operator vec2f.+= (byref rhs as vec2f): x += rhs.x: y += rhs.y: end operator
operator vec2f.-= (byref rhs as vec2f): x -= rhs.x: y -= rhs.y: end operator
operator vec2f.*= (byref rhs as vec2f): x *= rhs.x: y *= rhs.y: end operator
operator vec2f.*= (byref rhs as float): x *= rhs: y *= rhs: end operator
operator vec2f./= (byref rhs as float): x /= rhs: y /= rhs: end operator
operator vec2f.let (byref rhs as vec2f): x = rhs.x: y = rhs.y: end operator
'' member functions
function vec2f.absolute() as vec2f: return vec2f(abs(x), abs(y)): end function
function vec2f.normal() as vec2f: return vec2f(y, -x): end function
function vec2f.normalised() as vec2f: return this/magnitude(): end function
function vec2f.normalisednormal() as vec2f: return this.normal()/magnitude(): end function
function vec2f.magnitude() as float: return sqr(magnitudesquared()): end function
function vec2f.magnitudesquared() as float: return this.dot(this): end function
function vec2f.distance(byref rhs as vec2f) as float: return sqr(distancesquared(rhs)): end function
function vec2f.distancesquared(byref rhs as vec2f) as float: return (x-rhs.x)*(x-rhs.x)+(y-rhs.y)*(y-rhs.y): end function
function vec2f.dot(byref rhs as vec2f) as float: return (x*rhs.x+y*rhs.y): end function
function vec2f.dotnormal(byref rhs as vec2f) as float: return this.dot(rhs.normal()): end function
function vec2f.project(byref rhs as vec2f) as vec2f: return (dot(rhs)/magnitudesquared())*rhs: end function
function vec2f.component(byref rhs as vec2f) as vec2f: return (dot(rhs)/rhs.magnitudesquared)*rhs: end function
function vec2f.randomise(byval rhs as float) as vec2f: return vec2f((rnd-rnd)*rhs, (rnd-rnd)*rhs): end function
'' unary arithmetic non-member operators
operator - (byref rhs as vec2f) as vec2f: return vec2f(-rhs.x, -rhs.y): end operator
'' binary arithmetic non-member operators
operator + (byval lhs as vec2f, byref rhs as vec2f) as vec2f: return vec2f(lhs.x+rhs.x, lhs.y+rhs.y): end operator
operator - (byval lhs as vec2f, byref rhs as vec2f) as vec2f: return vec2f(lhs.x-rhs.x, lhs.y-rhs.y): end operator
operator * (byval lhs as vec2f, byref rhs as vec2f) as vec2f: return vec2f(lhs.x*rhs.x, lhs.y*rhs.y): end operator
operator * (byval lhs as float, byref rhs as vec2f) as vec2f: return vec2f(lhs*rhs.x, lhs*rhs.y): end operator
operator * (byval lhs as vec2f, byval rhs as float) as vec2f: return vec2f(lhs.x*rhs, lhs.y*rhs): end operator
operator / (byval lhs as vec2f, byval rhs as float) as vec2f: return vec2f(lhs.x/rhs, lhs.y/rhs): end operator
'' non-member functions
function absolute (byref lhs as vec2f) as vec2f: return lhs.absolute(): end function
function normal (byref lhs as vec2f) as vec2f: return lhs.normal(): end function
function normalised (byref lhs as vec2f) as vec2f: return lhs.normalised(): end function
function normalisednormal(byref lhs as vec2f) as vec2f: return lhs.normalisednormal(): end function
function magnitude (byref lhs as vec2f) as float: return lhs.magnitude(): end function
function magnitudesquared (byref lhs as vec2f) as float: return lhs.magnitudesquared(): end function
function distance (byval lhs as vec2f, byref rhs as vec2f) as float: return lhs.distance(rhs): end function
function distancesquared (byval lhs as vec2f, byref rhs as vec2f) as float: return lhs.distancesquared(rhs): end function
function dot (byval lhs as vec2f, byref rhs as vec2f) as float: return lhs.dot(rhs): end function
function dotnormal (byval lhs as vec2f, byref rhs as vec2f) as float: return lhs.dotnormal(rhs): end function
function project (byval lhs as vec2f, byref rhs as vec2f) as vec2f: return lhs.project(rhs): end function
function component(byval lhs as vec2f, byref rhs as vec2f) as vec2f: return lhs.component(rhs): end function
function trigonometry(byref lhs as float) as vec2f: return vec2f(cos(lhs), sin(lhs)): end function
Hi qbworkerqbworker wrote:My lib does bouncing off any line as well as inter-ball bouncing as well.
I'm just fiddling around a bit with this stuff.
I've tried a bounce off a polynomial base, I'm just about to post it in the squares topic.
Hi h4tt3n
Thanks for the code, it is sophisticated and runs well.
Rollie~ wanted a kind of sphere mapping idea, so The thing is posted in the squares topic.
@ qbworker and h4tt3n
These collisions tend to run on tenderhooks if certain constraints are not applied.
I've applied no restraints, so an odd jam up may occur.
My problem is how many frames to run before re-enabling collisions.
I'll just post my thingy in squares now.