JV-ODE Physics Thread 3

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

I've just had a mess around and created a primitive 'Pong meets Breakout' in about an hour. It uses my Constructor library that my tute introduced. However, I've found that .pdf is lousy for copy+paste code, so I'll dupe the library here.

Save this as 'ODE_Object_Library.bb;
Const RESOLUTION_SPHERE = 12
Const RESOLUTION_CYLINDER = 12

; Enumerated types for materials
Const CON_WOOD=1,CON_GOLD=2,CON_RUBBER=3

; Enumerated Types for geometries
Const GEOM_CUBE=1,GEOM_SPHERE=2,GEOM_CYLINDER=3

; Constants for pointer and forces
Global NUM_OBJECTS
Global mat_wood.material, mat_gold.material, mat_rubber.material

Global ODE_world, ODE_space, ODE_scenery_space, ODE_contactGroup

Type vector
	Field x#
	Field y#
	Field z#
End Type
Type material
	Field index				; Index for determining type of material
	Field density#
	Field friction#
	Field restitution#
End Type
Type ODE_object
	Field body
	Field geom
	Field mesh
	Field mass
	Field mat.material
End Type
Type actor
	Field geom			; index of geometry type
	Field pos.vector
	Field rot.vector
	Field siz.vector
	Field mat				; matieral index
End Type

; Define Material Properties
mat_wood.material=New material
mat_wood\index = CON_WOOD
mat_wood\friction = 10.0
mat_wood\restitution = 0.4
mat_wood\density = 0.9

mat_gold.material=New material
mat_gold\index = CON_GOLD
mat_gold\friction = 3.0
mat_gold\restitution = 0.2
mat_gold\density = 10.0

mat_rubber.material=New material
mat_rubber\index = CON_RUBBER
mat_rubber\friction = 500.0
mat_rubber\restitution = 0.9
mat_rubber\density = 1.5

Function create_Box_Mesh(obj.ODE_object)
	obj\mesh=CreateCube()
	Select obj\mat\index
		Case CON_WOOD
			EntityColor obj\mesh,120,80,40
			EntityShininess obj\mesh,0.2
		Case CON_GOLD
			EntityColor obj\mesh,200,120,40
			EntityShininess obj\mesh,0.95
		Case CON_RUBBER
			EntityColor obj\mesh,70,70,80
			EntityShininess obj\mesh,0.2
	End Select
End Function
Function create_Sphere_Mesh(obj.ODE_object)
	obj\mesh=CreateSphere(RESOLUTION_SPHERE)
	Select obj\mat\index
		Case CON_WOOD
			EntityColor obj\mesh,120,80,40
			EntityShininess obj\mesh,0.2
		Case CON_GOLD
			EntityColor obj\mesh,200,120,40
			EntityShininess obj\mesh,0.95
		Case CON_RUBBER
			EntityColor obj\mesh,70,70,80
			EntityShininess obj\mesh,0.2
	End Select
End Function
Function create_Cylinder_Mesh(obj.ODE_object)
	obj\mesh=CreateCylinder(RESOLUTION_CYLINDER)
	Select obj\mat\index
		Case CON_WOOD
			EntityColor obj\mesh,120,80,40
			EntityShininess obj\mesh,0.2
		Case CON_GOLD
			EntityColor obj\mesh,200,120,40
			EntityShininess obj\mesh,0.95
		Case CON_RUBBER
			EntityColor obj\mesh,70,70,80
			EntityShininess obj\mesh,0.2
	End Select
End Function
			
Function create_Box(obj.ODE_object, x_pos#, y_pos#, z_pos#, width#, length#, depth#, x_rot#, y_rot#, z_rot#)
	obj\body=dBodyCreate(ODE_world)
		
	dMassSetBox(obj\mass,obj\mat\density, width, length, depth)
	dBodySetMass(obj\body,obj\mass)
	
	dBodySetRotation(obj\body, x_rot, y_rot, z_rot)
	dBodySetPosition(obj\body, x_pos, y_pos, z_pos)
	dBodySetAutoDisableFlag(obj\body,1)	
	
	obj\geom=dCreateBox(ODE_space,width, length, depth)
	dGeomSetBody(obj\geom,obj\body)
	dGeomContactSetBounce(obj\geom,obj\mat\restitution)
	dGeomContactSetMu(obj\geom,obj\mat\friction)

	create_Box_Mesh(obj)
	; Blitz box lengths 2x longer than ODE box length
	ScaleMesh obj\mesh,width/2,length/2,depth/2
End Function
Function create_Rigid_Box(obj.ODE_object, x_pos#, y_pos#, z_pos#, width#, length#, depth#, x_rot#, y_rot#, z_rot#)
	obj\geom=dCreateBox(ODE_scenery_space,width, length, depth)
	dGeomSetRotation(obj\geom, x_rot, y_rot, z_rot)
	dGeomSetPosition(obj\geom, x_pos, y_pos, z_pos)
	
	; Blitz box lengths 2x longer than ODE box length
	create_Box_Mesh(obj)
	ScaleMesh obj\mesh,width/2,length/2,depth/2
	
	RotateEntity obj\mesh,dGeomGetPitch#(obj\geom),dGeomGetYaw#(obj\geom),dGeomGetRoll#(obj\geom)
	PositionEntity obj\mesh,dGeomGetPositionX#(obj\geom),dGeomGetPositionY#(obj\geom),dGeomGetPositionZ#(obj\geom)
	
End Function

Function create_Sphere(obj.ODE_object, x_pos#, y_pos#, z_pos#, radius#, x_rot#, y_rot#, z_rot#)
	obj\body=dBodyCreate(ODE_world)
	
	dMassSetSphere(obj\mass,obj\mat\density, radius)
	dBodySetMass(obj\body,obj\mass)
	
	dBodySetRotation(obj\body, x_rot, y_rot, z_rot)
	dBodySetPosition(obj\body, x_pos, y_pos, z_pos)
	dBodySetAutoDisableFlag(obj\body,1)	
	
	obj\geom=dCreateSphere(ODE_space,radius)
	dGeomSetBody(obj\geom,obj\body)
	dGeomContactSetBounce(obj\geom,obj\mat\restitution)
	dGeomContactSetMu(obj\geom,obj\mat\friction)

	create_Sphere_Mesh(obj)
	ScaleMesh obj\mesh,radius,radius,radius
	
End Function
Function create_Rigid_Sphere(obj.ODE_object, x_pos#, y_pos#, z_pos#, radius#, x_rot#, y_rot#, z_rot#)
	obj\geom=dCreateSphere(ODE_scenery_space,radius)
	dGeomSetRotation(obj\geom, x_rot, y_rot, z_rot)
	dGeomSetPosition(obj\geom, x_pos, y_pos, z_pos)
	
	create_Sphere_Mesh(obj)
	ScaleMesh obj\mesh,radius,radius,radius
	
	RotateEntity obj\mesh,dGeomGetPitch#(obj\geom),dGeomGetYaw#(obj\geom),dGeomGetRoll#(obj\geom)
	PositionEntity obj\mesh,dGeomGetPositionX#(obj\geom),dGeomGetPositionY#(obj\geom),dGeomGetPositionZ#(obj\geom)
	
End Function

Function create_Cylinder(obj.ODE_object, x_pos#, y_pos#, z_pos#, radius#, length#, x_rot#, y_rot#, z_rot#)
	obj\body=dBodyCreate(ODE_world)
	
;	dMassSetSphere(v,obj\mat\density, radius)
	dMassSetCylinder(obj\mass,obj\mat\density,1, radius,length)
	dBodySetMass(obj\body,obj\mass)
	
	dBodySetRotation(obj\body, x_rot, y_rot, z_rot)
	dBodySetPosition(obj\body, x_pos, y_pos, z_pos)
	dBodySetAutoDisableFlag(obj\body,1)	
	
	obj\geom=dCreateCylinder(ODE_space,radius,length)
	dGeomSetBody(obj\geom,obj\body)
	dGeomContactSetBounce(obj\geom,obj\mat\restitution)
	dGeomContactSetMu(obj\geom,obj\mat\friction)
	
	; Blitz cylinder mesh is 2x longer than ODE cylinder length
	create_Cylinder_Mesh(obj)
	ScaleMesh obj\mesh,radius,length/2,radius
	
End Function
Function create_Rigid_Cylinder(obj.ODE_object, x_pos#, y_pos#, z_pos#, radius#,length#, x_rot#, y_rot#, z_rot#)
	obj\geom=dCreateCylinder(ODE_scenery_space,radius,length)
	dGeomSetRotation(obj\geom, x_rot, y_rot, z_rot)
	dGeomSetPosition(obj\geom, x_pos, y_pos, z_pos)
	
	; Blitz cylinder mesh is 2x longer than ODE cylinder length
	create_Cylinder_Mesh(obj)
	ScaleMesh obj\mesh,radius,length/2,radius
	
	RotateEntity obj\mesh,dGeomGetPitch#(obj\geom),dGeomGetYaw#(obj\geom),dGeomGetRoll#(obj\geom)
	PositionEntity obj\mesh,dGeomGetPositionX#(obj\geom),dGeomGetPositionY#(obj\geom),dGeomGetPositionZ#(obj\geom)
	
End Function

Function set_material(obj.ODE_object,m.material)
	obj\mat\index = m\index
	obj\mat\friction = m\friction
	obj\mat\density = m\density
	obj\mat\restitution = m\restitution
End Function


And here's the program...

Include "JV-ode.bb"
Include "ODE_Object_Library.bb"
Graphics3D 800,600,0,0
HidePointer()

Const FPS=60, GRAVITY#=-0.981
Const PERIOD=1000/FPS
Const MAX_OBJECTS=50

index_ball=0				; index for ball
index_paddle1=0		; Index for paddle 1
index_paddle2=0		; index for paddle 2

Dim objects.ODE_object(MAX_OBJECTS)

ODE_world=dWorldCreate()
ODE_space=dHashSpaceCreate(0)
ODE_scenery_space=dHashSpaceCreate(ODE_Space)	; Sub-space for static scenery
ODE_contactGroup=dJointGroupCreate(0)

dSetInternalContactSurfaceMode(1)		; Sets bounce value to Max(a,b) for collisions
dSetInternalSpaceCollideMode(0)		; Turns off internal space collision

; Change Rubber Properties
mat_rubber\friction = 0.0
mat_rubber\restitution = 1.0
mat_rubber\density = 5.0

; Change 'wood' material properties for better racket friction
mat_wood\friction = 40.0
mat_wood\restitution = 1.3	; Adds energy per bounce
mat_wood\density = 0.9

; Change 'gold' material properties for better bricks
mat_gold\friction = 5.0
mat_gold\restitution = 0.2	; Adds energy per bounce
mat_gold\density = 0.1

For n=0 To MAX_OBJECTS
	objects(n) = New ODE_object
	objects(n)\mass=dMassCreate()
	objects(n)\mat.material = New material
Next	; initialise ODE objects array

dWorldSetAutoDisableFlag(ODE_world,1)			; Turn on auto disable
dWorldSetGravity(ODE_world,0,GRAVITY#,0)			; Gravity in m/s/s
dWorldSetERP(ODE_world,0.5)				; Global 'error correction'
dWorldSetCFM(ODE_world,0.000001)
dContactSetMode(dContactBounce+dContactSlip1)
dContactSetBounce(0.2)
dContactSetMu(0.0)

AmbientLight 128,148,168
light1=CreateLight()
TurnEntity light1,45,40,0
LightColor light1,180,160,110
light2=CreateLight()
TurnEntity light2,15,-40,0
LightColor light2,110,160,110

rostrum_pan=CreatePivot()
rostrum_tilt=CreatePivot(rostrum_pan)
camera=CreateCamera(rostrum_tilt)
PointEntity rostrum_pan,camera
PointEntity camera,rostrum_pan
CameraZoom camera,3
PositionEntity camera,0,40,-80
RotateEntity camera,30,0,0
	
SeedRnd(MilliSecs())

; Create Pong 'court'
current_object=Create_Court()

; create Ball
current_object=current_object+1
	pos_x# = 3
	pos_y# = 0.5
	pos_z# = 3
	siz_x# = 0.5
	rot_x# = 0
	rot_y# = 0
	rot_z# = 0

	set_material(objects(current_object),mat_rubber)
	create_Sphere(objects(current_object),pos_x,pos_y,pos_z,siz_x,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,220,220
	
	; Apply random force to sphere for initial movement
	dBodyAddForce(objects(current_object)\body,150+Rnd(-10,10),0,150+Rnd(-10,10))
	index_ball=current_object
	
; create_paddle_1
current_object=current_object+1
	pos_x# = 0
	pos_y# = 1.5
	pos_z# = -29
	siz_x# = 5
	siz_y# = 2.5
	siz_z# = 2
	rot_x# = 0
	rot_y# = 0
	rot_z# = 0

	set_material(objects(current_object),mat_wood)
	create_Rigid_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,50,50,220
	index_paddle1=current_object

; create_paddle_2
current_object=current_object+1
	pos_x# = 0
	pos_y# = 1.5
	pos_z# = 29
	siz_x# = 5
	siz_y# = 2.5
	siz_z# = 2
	rot_x# = 0
	rot_y# = 0
	rot_z# = 0

	set_material(objects(current_object),mat_wood)
	create_Rigid_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,50,220,50
	index_paddle2=current_object

; Add bricks
current_object=current_object+1
	pos_x# = -9
	pos_y# = 0.6
	pos_z# = 0
	siz_x# = 2
	siz_y# = 1
	siz_z# = 1
	rot_x# = 0
	rot_y# = 0
	rot_z# = 0

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = -6

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = -3

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = 0

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = 3

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = 6

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = 9

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = -7.5
	pos_y# = 1.6

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = -4.5
	pos_y# = 1.6

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = -1.5
	pos_y# = 1.6

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = 1.5
	pos_y# = 1.6

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = 4.5
	pos_y# = 1.6

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

current_object=current_object+1
	pos_x# = 7.5
	pos_y# = 1.6

	set_material(objects(current_object),mat_gold)
	create_Box(objects(current_object),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(current_object)\mesh,220,50,50

NUM_objects=current_object ; Number of ODE objects in our array

Floor.ODE_Object = New ODE_Object
Floor\geom = dCreatePlane(ODE_space,0,1,0,0)
Floor\mesh = CreatePlane()
EntityColor Floor\mesh,120,120,120

MoveMouse GraphicsWidth()/2,GraphicsHeight()/2
player_x=MouseX()

While Not KeyHit(1)

	; Computer controlled paddle
	dGeomSetPosition objects(index_paddle2)\geom, dGeomGetPositionX(objects(index_ball)\geom), 1, 29
	
	; Player controlled paddle
	player_x=MouseX()
	set_paddle_position_from_2D(camera, objects(index_paddle1), MouseX(), 0, 3)
	
	; Update objects
	For n=0 To NUM_OBJECTS
		If objects(n)\geom
			RotateEntity objects(n)\mesh,dGeomGetPitch#(objects(n)\geom),dGeomGetYaw#(objects(n)\geom),dGeomGetRoll#(objects(n)\geom)
			PositionEntity objects(n)\mesh,dGeomGetPositionX#(objects(n)\geom),dGeomGetPositionY#(objects(n)\geom),dGeomGetPositionZ#(objects(n)\geom)
		End If
	Next

	dSpaceCollide(ODE_space,ODE_world,ODE_contactGroup)
	dWorldQuickStep(ODE_world,0.10)
	dJointGroupEmpty(ODE_contactGroup)

	UpdateWorld	
	RenderWorld

	Flip

Wend

dJointGroupDestroy(ODE_contactGroup)
dSpaceDestroy(ODE_space)
dWorldDestroy(ODE_world)
dCloseODE()

End

Function Create_Court()
	; Package court creation into a function to keep the main code tidy

	n=0	; First object - left wall
	
	pos_x# = -20
	pos_y# = 3
	pos_z# = 0
	siz_x# = 3
	siz_y# = 6
	siz_z# = 60
	rot_x# = 0
	rot_y# = 0
	rot_z# = 0

	set_material(objects(n),mat_rubber)
	create_rigid_Box(objects(n),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(n)\mesh,155,60,60

	n=n+1	; Second object - right wall
	
	pos_x = 20
	pos_y = 3
	pos_z = 0
	siz_x = 3
	siz_y = 6
	siz_z = 60
	rot_x = 0
	rot_y = 0
	rot_z = 0

	set_material(objects(n),mat_rubber)
	create_rigid_Box(objects(n),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(n)\mesh,155,60,60

	n=n+1	; Third object - left back endpiece
	
	pos_x = -15
	pos_y = 3
	pos_z = 31.5
	siz_x = 3
	siz_y = 6
	siz_z = 10
	rot_x = 0
	rot_y = 90
	rot_z = 0

	set_material(objects(n),mat_rubber)
	create_rigid_Box(objects(n),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(n)\mesh,60,155,60

	n=n+1	; Fourth object - right back endpiece
	
	pos_x = 15
	pos_y = 3
	pos_z = 31.5
	siz_x = 3
	siz_y = 6
	siz_z = 10
	rot_x = 0
	rot_y = 90
	rot_z = 0

	set_material(objects(n),mat_rubber)
	create_rigid_Box(objects(n),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(n)\mesh,60,155,60

	n=n+1	; Fifth object - left back endpiece
	
	pos_x = -15
	pos_y = 3
	pos_z = -31.5
	siz_x = 3
	siz_y = 6
	siz_z = 10
	rot_x = 0
	rot_y = 90
	rot_z = 0

	set_material(objects(n),mat_rubber)
	create_rigid_Box(objects(n),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(n)\mesh,60,60,155
	EntityAlpha objects(n)\mesh,0.5
	
	n=n+1	; Sixth object - right back endpiece
	
	pos_x = 15
	pos_y = 3
	pos_z = -31.5
	siz_x = 3
	siz_y = 6
	siz_z = 10
	rot_x = 0
	rot_y = 90
	rot_z = 0

	set_material(objects(n),mat_rubber)
	create_rigid_Box(objects(n),pos_x,pos_y,pos_z,siz_x,siz_y,siz_z,rot_x,rot_y,rot_z)
	EntityColor objects(n)\mesh,60,60,155
	EntityAlpha objects(n)\mesh,0.5
	
	Return n
End Function

Function set_paddle_position_from_2D(usecam, obj.ODE_object, x2d#,  positionGlobal = 0, camZoom# = 1)
	gw = GraphicsWidth ()
	gh = GraphicsHeight ()
	x# = -((gw / 2) - x2d)
	y# = (gh / 2) - y2d
	parent = GetParent (obj\mesh)
	EntityParent obj\mesh, usecam
	z3d# = Abs (EntityZ(obj\mesh))
	div# = (gw / (2 / camZoom)) / z3d
	PositionEntity obj\mesh, x / div, y / div, z3d, positionGlobal
	dGeomSetPosition (obj\geom,EntityX(obj\mesh,1),1, -29)
	EntityParent obj\mesh, parent
End Function


I reuse the material names in the ODE_Object_Lib as I couldn't be bothered to change them, but in a proper program you'd obviously spend a little more time documenting and structuring the program.

Also note I use an array of objects and store index value to reference different ODE objects. This makes it easy to save and restore level data. I'm sure there's a squillion optimizations and improvements people can make, but it's nice to see how quick and straightforward it is to create a physics based game. There's really no excuse not to include physics in everything!!