Frame Tweening/Delta timing in physics wrappers

Blitz3D Forums/Blitz3D Userlibs/Frame Tweening/Delta timing in physics wrappers

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

This is the system I finally developed :

Fetch the time elapsed between frames.
Accumlate this time.
If the time is over 20 ms (50 Hz), perform physics and capture the scene. Reset the frame timer.
For each frame, calculate a tween value.
Render with Tween.

Here's the code :
CaptureWorld()
start_time=MilliSecs() ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Get frame start time
	
While Not KeyHit(1)
	frame_time=MilliSecs()-start_time	;<<<<<<<<<<<<<<<<<<<<< Get time for frame
	start_time=MilliSecs() ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Reset frame start
	update_time=update_time+frame_time	;<<<<<<<<<<<<<<<< Increase count for time between updates

	If update_time/20>0	;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< If updating 50 fps
		update_time=update_time Mod 20 ;<<<<<<<<<<<<<<<<<< Return remainder
		CaptureWorld() ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Record object positions
		;<DO OBJECT MOTION, PHYSICS, ETC.>
	EndIf
	tween#=(update_time Mod 20)/20.0	;<<<<<<<<<<<<<<<<< Get render tween amount
	UpdateWorld
	RenderWorld tween#
	Flip
Wend


Here's an example of doctoring the JV-ODE Box Demo :
; ###################################################################################################
; #										JV-ODE - Cubes Demo											#
; #									Code by Jim Williams (VIP3R)									#
; ###################################################################################################

AppTitle "JV-ODE - Cubes Demo"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

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

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dContactSetMode(dContactBounce)
dContactSetBounce(0.1)
dContactSetMu(48)

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera
pivot=CreatePivot()
Global Camera=CreateCamera(pivot)
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000
PositionEntity Camera,0,20,-50
RotateEntity Camera,30,0,0

; ### Create plane
dCreatePlane(Space,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
Mirror=CreateMirror()

; ###################################################################################################

CaptureWorld()
start_time=MilliSecs() ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Get frame start time
	
While Not KeyHit(1)
	frame_time=MilliSecs()-start_time	;<<<<<<<<<<<<<<<<<<<<< Get time for frame
	start_time=MilliSecs() ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Reset frame start
	update_time=update_time+frame_time	;<<<<<<<<<<<<<<<< Increase count for time between updates

	If update_time/20>0	;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< If updating 50 fps
		update_time=update_time Mod 20 ;<<<<<<<<<<<<<<<<<< Return remainder
		CaptureWorld() ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Record object positions
		UpdateGeoms()
		dSpaceCollide(Space,World,ContactGroup)
		dWorldQuickStep(World,0.15)
		dJointGroupEmpty(ContactGroup)
		TurnEntity pivot,0.0,0.2,0.0		
	EndIf
	tween#=(update_time Mod 20)/20.0	;<<<<<<<<<<<<<<<<< Get render tween amount

	If MilliSecs()-Timer>300
		AddObject()
		UpdateGeoms()
		box_count=box_count+1
		Timer=MilliSecs()
	End If


	UpdateWorld
	RenderWorld tween#
	Text 5,5,"boxes : "+box_count
	Flip False

Wend

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

End

; ###################################################################################################

Function AddObject()

xp=Rand(-10,10)
zp=Rand(-10,10)
xs=Rnd(3,6)
ys=Rnd(3,6)
zs=Rnd(3,6)
ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,xp,30,zp)
dBodySetAutoDisableFlag(ode\body,1)
ode\geom=dCreateBox(Space,xs,ys,zs)
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,xs/2.0,ys/2.0,zs/2.0
EntityColor ode\mesh,Rand(255),Rand(255),Rand(255)
EntityAlpha ode\mesh,1
EntityShininess ode\mesh,0.7

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	If dBodyIsEnabled(ode\body)
		RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
		PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
		End If
Next

End Function

; ###################################################################################################

The 50 Hz is hard coded as the value 20 in the above. You could use a CONST and set that to something else. 50 Hz was used as it divides nicely into the limited 1000 ms resolution of the timer. 60 Hz doesn't, and I think it'll give rounding errors. Using a period less than 1/60th of a frame will have no effect if you lock to the vertical refresh. If you lose the vertical lock (Flip False) you could get a higher framerate than the screen update, but at a cost of introducing tearing.

This method has been tested to provide the same physics update rate at 60 Hz and 72 Hz, including custom forces (just apply all forces in the 'If update_time' section Update) where my previous time-based Step-size didn't.