JV-ODE Physics Thread 4

Blitz3D Forums/Blitz3D Userlibs/JV-ODE Physics Thread 4

Here's an example program for magnetism for people (Wayne!) to have a nose at.
Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Global WorldERP#=0.8			; ### World Error Correction
Global WorldFriction#=0			; ### World Friction

Global SpaceInfo=0

Global Pointer
Global PlaneGeom
Global magnet.ODEGeom, magnet_on
Const NUM_OBJECTS=20

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type
Dim clutter.ODEGeom(NUM_OBJECTS)
; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)

Global GroundSpace=dHashSpaceCreate(Space)
Global ObjSpace=dHashSpaceCreate(Space)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dWorldSetERP(World,WorldERP)

dContactSetMode(dContactSlip1)
dContactSetMu(WorldFriction)

; ### Create light
Light=CreateLight()
RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera
pivot=CreatePivot()
Camera=CreateCamera(pivot)
PositionEntity Camera,-0,40,-80
RotateEntity Camera,30,0,0
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000
CameraZoom camera,2

; ### Create plane
PlaneGeom=dCreatePlane(GroundSpace,0,1,0,0)
Plane=CreatePlane()
EntityAlpha Plane,0.8
PlaneTexture=CreateTexture(128,128,9)
ClsColor 0,200,80
Cls
Color 255,255,255
Rect 0,0,64,64,1
Rect 64,64,64,64,1
CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

; ### Create some objects
	GRAVITY_ENABLED=True
	SIZE#=2.0
	CYLINDER_RADIUS#=1.7319
	DENSITY#=1.0
	FRICTION#=10.0
	gamma#=0.5

; Change Cube size to match Sphere's volume
SIZE=3
; Ode3 = Cube with density based mass
x#=1.0
y#=5.0
z#=1.0
magnet.ODEGeom=New ODEGeom
	magnet\geom=dCreateBox(Space,x,y,z)
	dGeomSetPosition (magnet\geom,0,2.5,0)
	dGeomContactSetMu(magnet\geom,0.0)
	magnet\mesh=CreateCube()
	ScaleMesh magnet\mesh,x/2,y/2,z/2
	PositionEntity magnet\mesh,0,2.5,0
	EntityColor magnet\mesh,255,0,0
	
SeedRnd MilliSecs() 
For n=1 To NUM_OBJECTS
	clutter(n)=New ODEGeom
;	r=Rand(1,2) ; Random objects
;	r = 1 ; Create all objects as spheres
	r = 2 ; Create all objects as boxes
	x#=Rnd(1,3)
	y#=Rnd(1,3)
	z#=Rnd(1,3)	
	Select r
		Case 1
			clutter(n)\body=dBodyCreate(World)
			clutter(n)\geom=dCreateSphere(Space,x)
			dGeomSetBody(clutter(n)\geom,clutter(n)\body)
			dGeomContactSetMu(clutter(n)\geom,0.0)
			mass=dMassCreate()
				dMassSetSphere(mass,DENSITY,x)
				dBodySetMass(clutter(n)\body,mass)
				dMassDestroy(mass)
			clutter(n)\mesh=CreateSphere()
			ScaleMesh clutter(n)\mesh,x,x,x	
		Case 2
;			x=x*2 :	y=y*2 : 	z=z*2		; Double box sizes
			clutter(n)\body=dBodyCreate(World)
			clutter(n)\geom=dCreateBox(Space,x,y,z)
			dGeomSetBody(clutter(n)\geom,clutter(n)\body)
			dGeomContactSetMu(clutter(n)\geom,0.0)
			mass=dMassCreate()
				dMassSetBox(mass,DENSITY,x,y,z)
				dBodySetMass(clutter(n)\body,mass)
				dMassDestroy(mass)
			clutter(n)\mesh=CreateCube()
			ScaleMesh clutter(n)\mesh,x/2,y/2,z/2	
	End Select
	dBodySetRotation(clutter(n)\body,Rand(-90,90),Rand(-90,90),Rand(-90,90))
	dBodySetPosition(clutter(n)\body,Rand(-20,20),Rand(3,5),Rand(-20,20))

Next

time=0
magnet_on=False
While Not KeyHit(1)

	time=advance_simulation(time)
	If KeyHit(57)
		magnet_on=True
	ElseIf KeyDown(203)
		TurnEntity pivot,0,0.5,0
	ElseIf KeyDown(205)
		TurnEntity pivot,0,-0.5,0
	ElseIf KeyDown(200)
		MoveEntity camera,0,0,1
	ElseIf KeyDown(208)
		MoveEntity camera,0,0,-1
	EndIf
	PointEntity camera,magnet\mesh
	UpdateWorld
	RenderWorld
	
	y=15
	Text 20,y,OBJECT_MASS : y=y+15

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

Function advance_simulation(t%)
	; Advances the simluation, incrementing frame count
	dSpaceCollide(Space,World,ContactGroup)

	;#Region Apply Forces
	objects_stopped=True
	;#End Region
	For n=1 To NUM_OBJECTS
		PositionEntity clutter(n)\mesh,dGeomGetPositionX(clutter(n)\geom),dGeomGetPositionY(clutter(n)\geom),dGeomGetPositionZ(clutter(n)\geom)
		RotateEntity clutter(n)\mesh,dGeomGetPitch(clutter(n)\geom),dGeomGetYaw(clutter(n)\geom),dGeomGetRoll(clutter(n)\geom)
		If magnet_on
			apply_magnetism(magnet,clutter(n))
		EndIf
		
		; Per collision processing
		If dBodyIsEnabled(clutter(n)\body)
			numCollisions=dGeomCountCollisions(clutter(n)\geom)
			If numCollisions>0
				; For each collision, test if with magnet
				For m=1 To numCollisions
					collision_geom=dGeomCollisionGeom(clutter(n)\geom,m)
					If collision_geom=magnet\geom
						; Collided with magnet
						dGeomSetBody(clutter(n)\geom,0)
						EntityColor clutter(n)\mesh,0,0,255
					EndIf						
				Next
			EndIf
			; Limit Linear Velocity...
;			bx#=dBodyGetLinearVelX(clutter(n)\body)
;			by#=dBodyGetLinearVelY(clutter(n)\body)
;			bz#=dBodyGetLinearVelZ(clutter(n)\body)
;			b#=Sqr((bx*bx)+(by*by)+(bz*bz))
;			If b>MAX_SPEED
;				b=MAX_SPEED/b
;				dBodySetLinearVel(temp\body,bx*b,by*b,bz*b)
;			EndIf
		EndIf
	Next

	dWorldQuickStep(World,0.15)
	dJointGroupEmpty(ContactGroup)
	Return t+1
End Function
Function apply_magnetism(magnet.ODEGeom, target.ODEGeom)
	; FUNCTION : Applies a force in direction of fan based on angle to fan
	dBodyGetPosition(target\body)
		x#=dVectorX()
		y#=dVectorY()
		z#=dVectorZ()
	dGeomGetPosition(magnet\geom)
		x#=x-dVectorX()
		y#=y-dVectorY()
		z#=z-dVectorZ()
	; (x,y,z) is direction of force. Need angle and magnitude
		mag#=Sqr(x * x + y * y + z * z)
		magnitude#=mag
		If mag>0
			x = x / mag
			y = y / mag
			z = z / mag
		Else
			x=1.0
			y=0.0
			z=0.0
		EndIf
		magnitude=magnitude*magnitude
	; (x,y,z) = normalized vector
		magnitude#=1.0/(magnitude)
		If magnitude > 0.001
			magnitude=magnitude*1000.0
	
			x=x * magnitude#
			y=y * magnitude#
			z=z * magnitude#

			If magnitude>500
				magnitude=500
				dBodyEnable(target\body)
				dBodyAddForce (target\body,-x,-y,-z)
			Else If magnitude>0.01
				dBodyEnable(target\body)
				dBodyAddForce (target\body,-x,-y,-z)
			EndIf
		EndIf
End Function


Hit <spcae> to turn on the magnet. Cursor keys let you change view. When an object hits the magnet it's deactivated (geom set to no body) and turned blue.

This example is all blocks. From line 92 where the objects are created you can select between all blocks, spheres, or a mixture. With blocks and spheres they hit but penetrate a little into the surface. Line 113 has an option to double the size of the blocks. When big they hit the magnet but don't get deactivate, but also don't penetrate.

Probems to solve are to stop any objhect penetration, and to ensure all objects stick regardless of size.

An extra feature wouldn't shouldn't be too hard is to have objects get deactivated when they hit any magnetised object.

The ultimate feature would be to have objects slide around other magnetised objects towards the centre of magnetism.

Also in the code, but disabled above, is code to limit the object's linear velocity. When using powerful magnets over large distances the acceleration becomes extreme and the objects can just fly through the magnet. In real life wind resistance limits top speed and reduces acceleration as the object gets faster. Not sure how to emulate this is ODE. I'm not after a full air dynamic model, but to limit top speed . In the above example I've capped the maximum force applied by the magnet which seem fairly realistic in results.