Code archives/Algorithms/Verlet physics with constraint collisions.

This code has been declared by its author to be Public Domain code.

Download source code

Verlet physics with constraint collisions. by Jeppe Nielsen2009
Verlet physics with collision between constraints.
also included:
- A space partioning algo for much faster collision queries
- 3d based rendering of lines and points (much faster than Line and Plot)

email: nielsen_jeppe@...
portfolio: http://www.sarkania.dk/jeppe
Converted from my xna source to blitz3d
Partially based on this paper by Thomas Jakobsen:
http://www.teknikus.dk/tj/gdc2001.htm
give credit where credit is due :-)
; Verlet physics demo by Jeppe Nielsen 2009
; email: nielsen_jeppe@hotmail.com
; portfolio: www.sarkania.dk/jeppe
; Converted from my xna source to blitz3d
; Partially based on this paper by Thomas Jakobsen:
; http://www.teknikus.dk/tj/gdc2001.htm
; give credit where credit is due :-)

Global debugComparisons

Graphics3D 1024,768,32,2

useSpace = True
gravityEnabled = True
renderPoints = True
useMotionBlur = True

CreateChain(100,500,GraphicsWidth()-100,500,10)

CreateBox(GraphicsWidth()/2,200,60,30,20)



draggingPoint.Point = Null
selectedPoint.Point = Null
selectedSpring.Spring = Null

; The space partitioning for much faster collision queries, space to see all tested against all
SpaceInit(0,0,1280,960,32,1000) ;<- increase the 1000, if you get array out of bounds errors

Repeat

If KeyHit(48)

	CreateBox(MouseX(),MouseY(),40,40)

EndIf

If MouseHit(1)
		
	If selectedPoint=Null
	
		selectedPoint = PointPick(MouseX(),MouseY(),10)
		If selectedPoint = Null
		
			selectedPoint = PointNew(MouseX(),MouseY(),1)
			selectedPoint\enabled = False
		
		EndIf
	
	Else
	
		otherPoint.Point = PointPick(MouseX(),MouseY(),10)
		If otherPoint= Null
		
			otherPoint = PointNew(MouseX(),MouseY(),1)
			otherPoint \enabled = False
		
		EndIf

		If selectedPoint<>otherPoint

			selectedPoint\enabled = True
			otherPoint \enabled = True
			selectedSpring = SpringNew(selectedPoint,otherPoint,16)
			
			selectedPoint = otherPoint
		
		EndIf
	
	EndIf
	
EndIf


If MouseDown(2)

	selectedSpring = SpringPick(MouseX(),MouseY(),10)
	
	selectedPoint = Null

	If draggingPoint=Null
	
		draggingPoint= PointPick(MouseX(),MouseY(),10)
	
	Else
	
		PointPosition (draggingPoint,MouseX(),MouseY())
	
	EndIf
	
Else

	draggingPoint= Null

EndIf

If (KeyHit(211) And selectedSpring<>Null)

SpringDelete selectedSpring

selectedSpring = Null

EndIf


If (KeyHit(57))

useSpace  = Not useSpace 

EndIf

If (KeyHit(25))

renderPoints = Not renderPoints 

EndIf

If (KeyHit(50))

useMotionBlur = Not useMotionBlur 

EndIf

If selectedSpring<>Null

	If KeyDown(200)
		selectedSpring\length = selectedSpring\length + 1
	ElseIf KeyDown(208)
		selectedSpring\length = selectedSpring\length - 1
	EndIf
	
	If KeyDown(203)
		selectedSpring\elasticity = selectedSpring\elasticity - 0.01
		If selectedSpring\elasticity<0.01
			selectedSpring\elasticity = 0.01
		EndIf
	ElseIf KeyDown(205)
		selectedSpring\elasticity = selectedSpring\elasticity + 0.01
		If selectedSpring\elasticity>1.5
			selectedSpring\elasticity = 1.5
		EndIf

	EndIf

EndIf


If (KeyHit(31))

	point.Point = PointPick(MouseX(),MouseY(),20)
	If point<>Null
		point\enabled = Not point\enabled
	EndIf

EndIf

If (KeyHit(34))

gravityEnabled = Not gravityEnabled 

EndIf

If gravityEnabled 
	SimulationUpdate(0.1,useSpace ,6)
Else
	SimulationUpdate(0.1,useSpace ,0)
EndIf

;SimulationRender()

SimulationRender3D(selectedSpring,renderPoints,useMotionBlur)


y=10
yh=12
Text 10,y,"Comparisons : " + debugComparisons : y=y+yh

If useSpace
	Text 10,y,"Space partitioning is used (space to change)" : y=y+yh
Else
	Text 10,y,"Brute force is used (space to change)" : y=y+yh
EndIf

Text 10,y,"B to spawn box at mouse position" : y=y+yh
Text 10,y,"Left click to create springs" : y=y+yh
Text 10,y,"Right click to drag points and select springs (selected spring is red)" : y=y+yh

If gravityEnabled 
Text 10,y, "G to toggle gravity : on" : y=y+yh
Else
Text 10,y, "G to toggle gravity : off" : y=y+yh
EndIf
Text 10,y, "S to toggle point static (blue) / dynamic (white)" : y=y+yh
Text 10,y, "Delete to delete selected spring (red)" : y=y+yh
Text 10,y, "P to toggle points rendering" : y=y+yh
Text 10,y, "M to toggle motion blur" : y=y+yh
Text 10,y, "Up/down arrow keys to extend/withdraw selected spring (red)" : y=y+yh

If selectedSpring<>Null
	Text 10,y, "Left/right to change elasticity of selected spring:"+selectedSpring\elasticity : y=y+yh
Else

	Text 10,y, "Left/right to change elasticity of selected spring:" : y=y+yh
EndIf

Flip

Until KeyDown(1)
End

Function CreateBox(x#,y#,width#,height#, mass#=1, thickness#=16, elasticity#=1.5)

p1.Point = PointNew(x-width,y-height,mass)
p1\collisionID = Handle(p1)
	
p2.Point = PointNew(x+width,y-height,mass)
p2\collisionID = p1\collisionID 

p3.Point = PointNew(x+width,y+height,mass)
p3\collisionID = p1\collisionID 

p4.Point = PointNew(x-width,y+height,mass)
p4\collisionID = p1\collisionID 

s1.Spring = SpringNew(p1,p2,thickness,elasticity)
s1\collisionID = p1\collisionID 

s2.Spring = SpringNew(p2,p3,thickness,elasticity)
s2\collisionID = p1\collisionID 

s3.Spring = SpringNew(p3,p4,thickness,elasticity)
s3\collisionID = p1\collisionID 

s4.Spring = SpringNew(p4,p1,thickness,elasticity)
s4\collisionID = p1\collisionID 

s5.Spring = SpringNew(p1,p3,thickness,elasticity)
s5\collisionID = p1\collisionID 

End Function

Function CreateChain(x1#,y1#,x2#,y2#,links = 20,mass#=1, thickness#=16, elasticity#=1.5)

thickness#=16

dx#=x2-x1
dy#=y2-y1

dx=dx/links
dy=dy/links

lastPoint.Point = Null

For i = 0 To links

x#=x1 + dx * i
y#=y1 + dy * i

p.Point = PointNew(x,y,mass)

If i = 0 Or i = links

	p\enabled = False

EndIf

If lastPoint<>Null

	SpringNew(lastPoint,p,thickness,elasticity)

EndIf

lastPoint = p

Next


End Function

Type Point
	
	Field x#,y#,xOld#,yOld#
	
	Field mass#
	
	Field otherPoint.Point
	
	Field enabled
	
	Field collisionID
	
End Type

Function PointNew.Point(x#,y#, mass# = 1)

p.Point = New Point

p\x=x
p\y=y
p\xOld = x
p\yOld = y
p\mass = mass
p\enabled = True

Return p
End Function

Function PointPick.Point(x#,y#,radius#)

radius = radius * radius

For p.Point = Each Point

dx#=p\x - x
dy#=p\y - y

If ((dx*dx+dy*dy)<radius)

	Return p

EndIf

Next

End Function

Function PointDelete(p.Point)

Delete p

End Function

Function PointPosition(p.Point,x#,y#)

p\x = x
p\y = y

If Not p\enabled

p\xOld = x
p\yOld = y

EndIf

End Function

Function PointHasSpring.Spring(p.Point)

	For s.Spring = Each Spring
		If s\a = p Or s\b = p
			Return s
		EndIf
	Next

	Return Null
End Function

Type Spring

Field a.Point
Field b.Point
Field length#
Field enabled
Field thickness#
Field tension#
Field elasticity#

Field minX#,minY#
Field maxX#,maxY#

Field collisionID
	
End Type

Function SpringNew.Spring(a.Point, b.Point, thickness#, elasticity#=1.0)

s.Spring = New Spring
s\a = a
s\b = b
s\thickness = thickness
s\enabled = True
s\tension = 0
s\elasticity = elasticity
SpringCalculateRestLength(s)

s\a\otherPoint = s\b
s\b\otherPoint = s\a

Return s
End Function

Function SpringCalculateRestLength(s.Spring)

dx#=s\b\x - s\a\x
dy#=s\b\y - s\a\y

s\length = Sqr(dx*dx+dy*dy)

End Function

Function SpringDelete(s.Spring)

pA.Point = s\a
pB.Point = s\b

Delete s

springThatHasA.Spring = PointHasSpring(pA)
springThatHasB.Spring = PointHasSpring(pB)


If springThatHasA = Null
	PointDelete pA
Else

	If pA\otherPoint = pB
		If pA<>springThatHasA\b
			pA\otherPoint = springThatHasA\b
		Else
			pA\otherPoint = springThatHasA\a
		EndIf
	EndIf
EndIf

If springThatHasB = Null
	PointDelete pB
Else

	If pB\otherPoint = pB
		If pB<>springThatHasB\a
			pB\otherPoint = springThatHasB\a
		Else
			pB\otherPoint = springThatHasB\b
		EndIf
	EndIf
EndIf



End Function

Function SpringPick.Spring(x#,y#, radius#)

radius = radius * radius

For s.Spring = Each Spring
	
	distance# = SimulationLineToCircle(s\a\x,s\a\y,s\b\x,s\b\y,x,y)
	
	If (distance<radius)
	
		Return s
	
	EndIf
	
Next

End Function

Function SpringUpdate(s.Spring)

If s\enabled

	dx#=s\b\x - s\a\x
	dy#=s\b\y - s\a\y

	realLength# = Sqr(dx*dx+dy*dy)
	
	dx=dx/realLength
	dy=dy/realLength
	
	totalMass# = s\a\mass + s\b\mass
	
	m1#=s\a\mass / totalMass
	m2#=1 - m1;s\b\mass / totalMass

	dl#=(realLength - s\length) * s\elasticity#

	s\tension = s\tension + dl
	
	If s\a\enabled
	
		s\a\x = s\a\x + dx * dl * m2
		s\a\y = s\a\y + dy * dl * m2
	
	EndIf

		
	If s\b\enabled
	
		s\b\x = s\b\x - dx * dl * m1
		s\b\y = s\b\y - dy * dl * m1
	
	EndIf

EndIf

End Function

Function SpringCalculateMinMax(s.Spring)

If s\a\x<s\b\x
	s\minX = s\a\x - s\thickness
	s\maxX = s\b\x + s\thickness
Else
	s\minX = s\b\x - s\thickness
	s\maxX = s\a\x + s\thickness
EndIf

If s\a\y<s\b\y
	s\minY = s\a\y - s\thickness
	s\maxY = s\b\y + s\thickness
Else
	s\minY = s\b\y - s\thickness
	s\maxY = s\a\y + s\thickness
EndIf

End Function

Function SimulationRender()

For s.Spring = Each Spring
	
	Line s\a\x,s\a\y,s\b\x,s\b\y

Next


For p.Point = Each Point

	Rect p\x-2,p\y-2,4,4,True

Next

End Function

Global simulationCamera, simulationPivot, simulationMesh, simulationSurface, simulationCube

Function SimulationRender3D(selected.Spring, renderPoints, useMotionBlur, lineWidth#=1, pointSize#=2)

If simulationCamera=0

simulationCamera = CreateCamera()
CameraRange simulationCamera,0.1,2

simulationPivot = CreatePivot(simulationCamera)

a#=Float(GraphicsHeight()) / GraphicsWidth()
w#=2.0/Float(GraphicsWidth())
PositionEntity simulationPivot,-1,a,1
ScaleEntity simulationPivot,w,-w,1

simulationMesh = CreateMesh(simulationPivot)

simulationSurface = CreateSurface(simulationMesh)

EntityFX simulationMesh,1+2+32

EntityColor simulationMesh,255,255,0


CameraClsColor simulationCamera,0,0,0
CameraClsMode simulationCamera,0,1

simulationCube= CreateCube(simulationCamera)
EntityColor simulationCube,0,0,0
PositionEntity simulationCube,0,0,3
ScaleEntity simulationCube,2,2,1
EntityFX simulationCube,1

EndIf

If useMotionBlur
	EntityAlpha simulationCube,0.2	
Else
	EntityAlpha simulationCube,1
EndIf


ClearSurface simulationSurface 


For s.Spring = Each Spring

dx# = s\b\x - s\a\x
dy# = s\b\y - s\a\y

l#=Sqr(dx*dx+dy*dy)

nx#=(dy/l)*lineWidth
ny#=-(dx/l)*lineWidth


v = AddVertex( simulationSurface,s\a\x+nx,s\a\y+ny,0)
AddVertex( simulationSurface,s\b\x+nx,s\b\y+ny,0)
AddVertex simulationSurface,s\b\x-nx,s\b\y-ny,0
AddVertex simulationSurface,s\a\x-nx,s\a\y-ny,0

AddTriangle simulationSurface,v,v+1,v+2
AddTriangle simulationSurface,v,v+2,v+3

If s = selected
	For i =0 To 3
		VertexColor simulationSurface,v+i,255,0,0,1
	Next
Else
	For i =0 To 3
		VertexColor simulationSurface,v+i,255,255,0,1
	Next
EndIf



Next

If renderPoints

For p.Point = Each Point

v = AddVertex( simulationSurface,p\x-pointSize,p\y-pointSize,0)
AddVertex( simulationSurface,p\x+pointSize,p\y-pointSize,0)
AddVertex simulationSurface,p\x+pointSize,p\y+pointSize,0
AddVertex simulationSurface,p\x-pointSize,p\y+pointSize,0

AddTriangle simulationSurface,v,v+1,v+2
AddTriangle simulationSurface,v,v+2,v+3

If p\enabled
	For i =0 To 3
		VertexColor simulationSurface,v+i,255,255,255,0.5
	Next
Else
	For i =0 To 3
		VertexColor simulationSurface,v+i,0,0,255,0.5
	Next
EndIf

Next

EndIf



RenderWorld

End Function





Function SimulationUpdate(dt#, useSpace, gravity# = 6, iterations = 10)

debugComparisons = 0

gravity = gravity * dt * dt

damping# = 0.85 ^ dt

For p.Point = Each Point

	If p\enabled
	
		tempX#=p\x
		tempY#=p\y
		
		p\x = p\x + (p\x - p\xOld) * damping
		p\y = p\y + (p\y - p\yOld) * damping + gravity
		
		p\xOld = tempX
		p\yOld = tempY
			
	EndIf

Next

For s.Spring = Each Spring

	s\tension = 0

Next

For i = 1 To iterations

	For s.Spring = Each Spring

		SpringUpdate(s)

	Next
	
	If useSpace
		
		SpaceClear()
		
		For s.Spring = Each Spring
		
			SpaceInsert(s)
		
		Next
	
	EndIf
	

	For p.Point = Each Point
	
		If useSpace
		
			SpaceUpdate(p)
		
		Else
	
			For s.Spring = Each Spring
					
				SimulationCheckPointAgainstSpring p,s
						
			Next
			
		EndIf
		
		
		If p\y>GraphicsHeight()
				
			p\y = GraphicsHeight()
		
		EndIf
		
		If p\x<0
		
			p\x = 0
		
		ElseIf p\x>GraphicsWidth()
		
			p\x = GraphicsWidth()
		
		EndIf


	Next


Next



End Function

Function SimulationCheckPointAgainstSpring(p.Point, s.Spring)



If (p = s\a Or p = s\b)
	
	Return		
				
EndIf

If (p\collisionID <> 0 And s\collisionID <> 0 And p\collisionID = s\collisionID)

	Return

EndIf

debugComparisons = debugComparisons + 1

Local pointVectorX#,pointVectorY#

If p\otherPoint<>Null

	If p\otherPoint = s\a Or p\otherPoint = s\b
	
		pointVectorX = s\a\x - p\x
		pointVectorY = s\a\y - p\y
		
	Else
	
		pointVectorX = s\a\x - p\otherPoint\x
		pointVectorY = s\a\y - p\otherPoint\y
	
	EndIf


Else
	
	pointVectorX = p\xOld - p\x
	pointVectorY = p\yOld - p\y

EndIf

distance# = SimulationLineToCircle(s\a\x,s\a\y,s\b\x,s\b\y,p\x,p\y)

thickSq#= s\thickness * s\thickness

If distance < thickSq

dirX# = s\b\x - s\a\x
dirY# = s\b\y - s\a\y

l#=Sqr(dirX*dirX + dirY*dirY)

dirX = dirX / l
dirY = dirY / l

normalX# = -dirY
normalY# = dirX

dot# = normalX * pointVectorX + normalY * pointVectorY

If dot>0

	normalX = -normalX
	normalY = -normalY

EndIf

dcoll# = (s\thickness - Sqr(distance)) * 0.5

ima# = 1.0 / p\mass
imb# = 1.0 / (s\a\mass + (s\b\mass - s\a\mass) * SimulationIntersectAB)

dcollMasses# = dcoll / (ima + imb)

separationX# = normalX * dcollMasses
separationY# = normalY * dcollMasses 

i1# = SimulationIntersectAB
i2# = 1 - SimulationIntersectAB

i1i2Inv# = 1.0 / (i1*i1+i2*i2)

seX# = separationX * imb * i1i2Inv
seY# = separationY * imb * i1i2Inv

vel1X# = p\x - p\xOld
vel1Y# = p\y - p\yOld

velAX# = s\a\x - s\a\xOld
velAY# = s\a\y - s\a\yOld

velBX# = s\b\x - s\b\xOld
velBY# = s\b\y - s\b\yOld

vel2X# = velAX + (velBX - velAX) * SimulationIntersectAB
vel2Y# = velAY + (velBY - velAY) * SimulationIntersectAB

vcollX# = vel2X - vel1X
vcollY# = vel2Y - vel1Y

dirX = normalY
dirY = -normalX

friction# = 0.2

dot = vcollX * dirX + vcollY * dirY

massInv# = 1.0 / (ima + imb)

impulseX# = dirX * dot * friction * massInv
impulseY# = dirY * dot * friction * massInv

If p\enabled

	p\x = p\x + separationX * ima
	p\y = p\y + separationY * ima
	
	p\xOld = p\xOld - impulseX * ima 
	p\yOld = p\yOld - impulseY * ima

EndIf


dImX# = impulseX * imb * i1i2Inv
dImY# = impulseY * imb * i1i2Inv

If s\a\enabled

	s\a\x = s\a\x - seX * i2
	s\a\y = s\a\y - seY * i2
	
	s\a\xOld = s\a\xOld + dImX * i2
	s\a\yOld = s\a\yOld + dImY * i2

EndIf

If s\b\enabled

	s\b\x = s\b\x - seX * i1
	s\b\y = s\b\y - seY * i1
	
	s\b\xOld = s\b\xOld + dImX * i1
	s\b\yOld = s\b\yOld + dImY * i1

EndIf

EndIf

End Function

Global SimulationIntersectAB#

Function SimulationLineToCircle#(x1#,y1#,x2#,y2#,x#,y#)

	dx#=x2-x1
	dy#=y2-y1

	SimulationIntersectAB = ((x-x1) * dx + (y-y1) * dy) / (dx*dx+dy*dy)

	If SimulationIntersectAB < 0 
		SimulationIntersectAB = 0
	ElseIf SimulationIntersectAB > 1
		SimulationIntersectAB = 1
	EndIf

	cx#=x1 + dx * SimulationIntersectAB 
	cy#=y1 + dy * SimulationIntersectAB 
		
	tx# = x - cx
	ty# = y - cy
	
	Return tx*tx + ty*ty
End Function

Dim SpaceCell.Spring(0,0,0)
Dim SpaceCellCount(0,0)

Global SpaceMinX#,SpaceMinY#,SpaceMaxX#,SpaceMaxY#
Global SpaceCells,SpaceMulX#, SpaceMulY#

Function SpaceInit(minX#,minY#,maxX#,maxY#,cells,maxInEachCell)

SpaceMinX = minX
SpaceMinY = minY

SpaceMaxX = maxX
SpaceMaxY = maxY

SpaceCells = cells


Dim SpaceCell(SpaceCells,SpaceCells, maxInEachCell)
Dim SpaceCellCount(SpaceCells,SpaceCells)

SpaceMulX = SpaceCells / (SpaceMaxX - SpaceMinX)
SpaceMulY = SpaceCells / (SpaceMaxY - SpaceMinY)

End Function


Function SpaceClear()

For y=0 To SpaceCells

	For x=0 To SpaceCells

		SpaceCellCount(x,y) = 0

	Next

Next

End Function

Function SpaceInsert(s.Spring)

SpringCalculateMinMax(s)

minX = Floor((s\minX -  SpaceMinX) * SpaceMulX)
minY = Floor((s\minY -  SpaceMinY) * SpaceMulY)

maxX = Floor((s\maxX -  SpaceMinX) * SpaceMulX)
maxY = Floor((s\maxY -  SpaceMinY) * SpaceMulY)

If (minX<0)
	minX = 0
Else If (minX>=SpaceCells)
	minX = SpaceCells
EndIf

If (minY<0)
	minY = 0
Else If (minY>=SpaceCells)
	minY = SpaceCells
EndIf


If (maxX<0)
	maxX= 0
Else If (maxX>=SpaceCells)
	maxX= SpaceCells
EndIf

If (maxY<0)
	maxY= 0
Else If (maxY>=SpaceCells)
	maxY= SpaceCells
EndIf



For y=minY To maxY
	For x=minX To maxX
	
		
		SpaceCell(x,y,SpaceCellCount(x,y)) = s
		
		SpaceCellCount(x,y) = SpaceCellCount(x,y)+1
		
	Next
Next
End Function


Function SpaceUpdate(p.Point)

x = Floor((p\x -  SpaceMinX) * SpaceMulX)
y = Floor((p\y -  SpaceMinY) * SpaceMulY)



If (x<0)
	x= 0
Else If (x>=SpaceCells)
	x= SpaceCells
EndIf

If (y<0)
	y= 0
Else If (y>=SpaceCells)
	y= SpaceCells
EndIf


count = SpaceCellCount(x,y)-1


For n=0 To count
	SimulationCheckPointAgainstSpring(p,SpaceCell(x,y,n))

Next

End Function

Comments

Richard Betson2010
This is very cool! I have just the project for this:)

I really have found allot of your example post very useful. Smooth bezier curves between points and 2D Collision Example are just wonderful.

Thanks for the above.


Code Archives Forum