ODE on slow machines

Blitz3D Forums/Blitz3D Userlibs/ODE on slow machines

I've just tried my program on a 233 MHz K6. As expceted it runs very slowly, about half speed I'd guess. In every way it does run correctly though, EXCEPT in some rotating forces. In one example I have a roundabout with two ragdolls on, a motor force applied to the roundabout, but on this K6 PC it doesn't turn at all. In another example I have a couple of invisble sphere's rotating about a point to push a plank left and right, and the plank doesn't move.

I'm using triple QuickStep calls of size 0.1. I can't see why the forces aren't being applied. They shuld just be being applied in slow motion, the same as the rest of the ODE.

I've tried to replicate the problem by slowing the main loop to a crawl, but don't see any evidence of missing forces here. How are you applying the force?

I get exactly what you would expect, everything running in slow motion.

There must be some other factor involved, how much memory is on the K6 PC? What OS is it running?

If I slow down the program on my main machine, it still works correctly just as you experience.

The K6 is Win98. Not sure about the RAM. It could be quite low. I'll have to check.

Obviously my concern here is finding the minimum spec to run my software. I was expecting that almost any machine could run it, but in slow motion. I'm facing the scarey prospect that some machines won't run it correctly at all.

Weird problem, I'm sure we'll get to the root of it though :)

Can you post the full spec of the machine, DX version everything...

At the moment I'm stumped, I can't think of any reason for this in either ODE or JV-ODE? The only differences that would matter to ODE between the dev machine and the K6 machine is the timing and memory right? How can that be affecting forces?

Have you tried getting some debug info from the K6 machine when it's running the simulation to see what value the forces are at?

I would suspect some of the crafty Shifty framerate control code. hehe

Perhaps Geezer could show us his render loop.

Here's the main loop
	; The actual level execution
	UI_Object=ODE_object_list\head\next_node\next_node
	EntityTexture buttons(UI_STOPGO)\sprite, buttons(UI_STOPGO)\texture2
	ShowEntity buttons(UI_BLANKER)\sprite
	ShowEntity buttons(UI_TRYAGAIN)\sprite
	ShowEntity buttons(UI_STOPGO)\sprite
	buttons(UI_TRYAGAIN)\active=True
	buttons(UI_STOPGO)\active=True
	buttons(UI_EXIT_MISSION)\active=True
	EntityAlpha buttons(UI_TRYAGAIN)\sprite,1.0
	FlushKeys()
	shiftycam\move_spd=30.0
	
	frame=0 : objects_stopped=0 : run_time=0
	
	mouse\current_object=0
	mouse\holding_object=0

	;Apply initial forces
	temp=ODE_Object_list\head
	While temp<>Null
		If temp\body
			v#=temp\force\x
			v#=temp\force\y
			v#=temp\force\z
			dBodyAddRelForce (temp\body, temp\force\x, 0.0, 0.0)
			dBodyVectorToWorld(temp\body, 1.0,0.0,0.0)
			dBodySetLinearVel (temp\body, dVectorX()*temp\force\y,dVectorY()*temp\force\y,dVectorZ()*temp\force\y)
		EndIf
		temp=temp\next_node
	Wend
	
	start_time=MilliSecs()

	While program_state=STATE_RUNNING
		frame_time=MilliSecs()-start_time
		start_time=MilliSecs()
		update_time=update_time+frame_time
		UPDATE_CUSTOM_PHYSICS=False
	
		If update_time/20>0
			UPDATE_CUSTOM_PHYSICS=True
			update_time=update_time Mod 20
			CaptureWorld()
		EndIf
		If UPDATE_CUSTOM_PHYSICS
			Shiftycam_Update(shiftycam)
		EndIf
		tween#=(update_time Mod 20)/20.0
		
		mouseupdate()
		If play_state>0
			run_time=run_time+frame_time
			;#Region Per object entry check
				temp=ODE_Object_list\head
				While temp<>Null
					If Not temp\on_set
						If frame>=temp\entry_time*5; And run_time<(temp\entry_time*100)+20
							enter_stage_left(temp)
						EndIf
					EndIf
					temp=temp\next_node
				Wend
			;#End Region
			;#Region Advance animation and camera
				frame=advance_simulation(frame)
				If UPDATE_CUSTOM_PHYSICS
					;#Region Update anims
						angle#=angle#+10
						angle#=wrapfloat(angle#,0,360)
						TurnTex(tex_fan,angle#)	
					;#End Region
				EndIf
			;#End Region
			clock_text$=(frame/50)+"."+(((frame Mod 50)*2)/10)
			TG_Change_Text(UI_TIME_TEXT,clock_text$)
		EndIf
		;#Region Per object respositioning
			temp=ODE_Object_list\head
			While temp<>Null
				If temp\body
					If dBodyIsEnabled(temp\body)
						update_object(temp)
					EndIf
				EndIf
				If Abs(shiftycam\zoom-shiftycam\zoom_target)>0.01
					If temp\outline_mesh
						rescale_outlines(temp,shiftycam\zoom)
					EndIf
				EndIf
				temp=temp\next_node
			Wend
		;#End Region
		;#Region Camera controls
			If mouse\speedz<>0
				mouse_z#=mouse\speedz*0.25
				Shiftycam_zoom(shiftycam,mouse_z)
			EndIf
			If mouse\x<TG_cam\siz_x	; if in view window
				If mouse\bl\down And (Not mouse\br\down)
					If mouse\bl\downtime>100
						Shiftycam_Turn_Camera(shiftycam,mouse\speedy / 25.0,-mouse\speedx / 25.0,0)
					Else
						found_object=get_object(shiftycam\camera)
						If found_object=1000
							If shiftycam\target_object<>ODE_object_list\current\mesh
								Shiftycam_Set_Tracking_Target(shiftycam,ODE_object_list\current\mesh)
								shiftycam\motion_state=CAMERA_TRACKING
							Else
;								Shiftycam_Turn_Camera(shiftycam,mouse\speedy / 25.0,-mouse\speedx / 25.0,0)
							EndIf
						Else
;							Shiftycam_Set_Tracking_Target(shiftycam,0)
;							shiftycam\motion_state=CAMERA_TRACKING
						EndIf
					EndIf
					mouse\x=mx
					mouse\y=my
					MoveMouse mx,my
					UI_object=ODE_object_list\head
				ElseIf mouse\br\down And (Not mouse\bl\down)
					shiftycam\motion_state=CAMERA_TRACKING
					PositionEntity shiftycam\target,0,50,0,1
					Shiftycam_Turn_Rostrum(shiftycam,mouse\speedy / 10.0,-mouse\speedx / 10.0,0)
					mouse\x=mx
					mouse\y=my
					MoveMouse mx,my
				Else If mouse\br\down And mouse\bl\down
					Shiftycam_zoom(shiftycam,mouse\speedy*-0.01)
					mouse\x=mx
					mouse\y=my
					MoveMouse mx,my
				EndIf
			EndIf
		;#End Region
		;#Region Keyboard Shortcuts
			If KeyHit(57)
				play_state=play_state*-1
				If play_state=-1
					EntityTexture buttons(UI_STOPGO)\sprite, buttons(UI_STOPGO)\texture2
						If AUDIO_FLAG
							chnl_Bing=PlaySound(snd_Bing)
							ChannelPitch chnl_Bing,32000
						EndIf
				Else
					EntityTexture buttons(UI_STOPGO)\sprite, buttons(UI_STOPGO)\texture1
						If AUDIO_FLAG
							chnl_Bing=PlaySound(snd_Bing)
							ChannelPitch chnl_Bing,44100
						EndIf
				EndIf	
			EndIf
			If KeyHit(31)
				debug_break=True
			EndIf
		;#End Region
		;#Region Level End
		If current_level=13
			; Determine if goals have been achieved
			; For each target, test Objects in group satisfy requirement
			win=True
			For  n=1 To 2
				; Loop through Object list
				; Set win to 0 and exit if violation found
				temp=ODE_Object_list\head\next_node
				While temp<>Null
					If temp\group=targets(n)\group
						Select targets(n)\class
							Case TARGET_INCLUSION
								x_distance#=EntityX(temp\mesh,1)-targets(n)\pos\x
								z_distance#=EntityZ(temp\mesh,1)-targets(n)\pos\z
								x_distance#=Sqr((x_distance*x_Distance)+(z_distance*z_Distance))
								If x_distance>targets(n)\radius
									win=False
								EndIf
						End Select
					EndIf
					temp=temp\next_node
				Wend
			Next
			test#=clock_text
			If test<17.5
				win=win*2
			EndIf
			If win Or frame>TIMEOUT
				program_state=STATE_LEVEL_END			
			EndIf

		Else If objects_stopped>=STOP_PERIOD Or frame>TIMEOUT
				; Determine if goals have been achieved
				; For each target, test Objects in group satisfy requirement
				win=True
				n=0
				While n<=MAX_TARGETS And win>0
					; Loop through Object list
					; Set win to 0 and exit if violation found
					If targets(n)\radius>1
						temp=ODE_Object_list\head\next_node
						While temp<>Null
							If temp\group=targets(n)\group
								Select targets(n)\class
									Case TARGET_INCLUSION
										x_distance#=EntityX(temp\mesh,1)-targets(n)\pos\x
										z_distance#=EntityZ(temp\mesh,1)-targets(n)\pos\z
										x_distance#=Sqr((x_distance*x_Distance)+(z_distance*z_Distance))
										If x_distance>targets(n)\radius
											win=False
										EndIf
									Case TARGET_EXCLUSION
										x_distance#=EntityX(temp\mesh,1)-targets(n)\pos\x
										z_distance#=EntityZ(temp\mesh,1)-targets(n)\pos\z
										x_distance#=Sqr((x_distance*x_Distance)+(z_distance*z_Distance))
										If x_distance<targets(n)\radius
											win=False
										EndIf
									Case TARGET_ROT_SPEED
										If Abs(temp\joint\rot_force1\x)/Abs(targets(n)\pos\x)>0.95 And Abs(temp\joint\rot_force1\x)/Abs(targets(n)\pos\x)<1.05	; 5% deviation
											win=win*2	; Multiply win is Passed
										EndIf
								End Select
							EndIf
							temp=temp\next_node
						Wend
					EndIf
					n=n+1
				Wend

				;Hard coded conditions
				Select current_level
					Case 2
						temp=ODE_Object_list\head\next_node
						While temp<>Null
						 	If temp\geom_type=GEOM_ROBOHEAD
						 		If EntityInView (temp\mesh, shiftycam\camera)
									x#=EntityX(shiftycam\camera)
									y#=EntityY(shiftycam\camera)
									z#=EntityZ(shiftycam\camera)
									dx#=EntityX(temp\mesh)
									dy#=EntityY(temp\mesh)
									dz#=EntityZ(temp\mesh)
									dx#=(dx-x)*2
									dy#=(dy-y)*2
									dz#=(dz-z)*2

						 			found_object = LinePick (x,y,z,dx,dy,dz)
						 			If found_object=temp\mesh
						 				win=win*2
						 			EndIf
						 		EndIf
						 	EndIf
							temp=temp\next_node
						Wend
					Case 3
						If EntityX(shiftycam\camera)>-120.0
							win=win*2
						EndIf
					Case 4
 						If Int(buttons(4)\val)=5
							win=win*2
						EndIf

				End Select
				program_state=STATE_LEVEL_END			
			EndIf
		;#End Region
	
		If mouse\bl\hit
			control_test=TG_Mouse_Over()
			If control_test<>mouse\current_object
				mouse\current_object=control_test
			EndIf	
			If control_test>0
				Select mouse\current_object
					Case UI_EXIT_GADGET
						UI_Confirm_Exit()
					Case UI_EXIT_MISSION
						program_state=STATE_SELECT_LEVEL			
						If AUDIO_FLAG
							chnl_Bing=PlaySound(snd_Bing)
						EndIf
					Case UI_STOPGO
						If play_state=1
							play_state=-1
							EntityTexture buttons(UI_STOPGO)\sprite, buttons(UI_STOPGO)\texture1
							
							If AUDIO_FLAG
								chnl_Bing=PlaySound(snd_Bing)
								ChannelPitch chnl_Bing,32000
							EndIf
						Else
							play_state=1
							EntityTexture buttons(UI_STOPGO)\sprite, buttons(UI_STOPGO)\texture2
							If AUDIO_FLAG
								chnl_Bing=PlaySound(snd_Bing)
								ChannelPitch chnl_Bing,44100
							EndIf
						EndIf				
					Case UI_TRYAGAIN
						play_state=-1
						erase_list()
						restore_scene(shiftycam)
						program_state=STATE_LEVEL_SETUP
						If AUDIO_FLAG
							chnl_Bing=PlaySound(snd_Beeou)
						EndIf
				End Select
			EndIf
		EndIf
		mx = mouse\X
		my = mouse\Y
		TG_Draw_UI()
		frame_time=MilliSecs()-start_time
		If frame_time<12 Then Delay 2
		VWait
		Flip False
	Wend
End Function


Here's the ODE update...
Function advance_simulation(t%)
	; Advances the simluation, incrementing frame count
	; Performs friction calculations
	; Disables 'non-existant' objects
	; Perform custom force processing 50 times a second
	If UPDATE_CUSTOM_PHYSICS
		dSpaceCollide(ODE_space,ODE_world,ODE_contactGroup)
	
		If t>100
			stop_time=1
		EndIf
		temp=ODE_object_list\head
		While temp<>Null
			If temp\prop And temp\magnetism>0
				collision_object=ODE_object_list\head
				While collision_object<>Null
					If collision_object\body
						If collision_object\magnetism>0 And dBodyIsEnabled(collision_object\body) And collision_object\on_set
							apply_magnetism(temp, collision_object)
						EndIf
					EndIf
				collision_object=collision_object\next_node
				Wend
			EndIf
			Select temp\geom_type
				Case GEOM_FAN
					;if it's a fan, apply fan forces to all other objects...
					;Collision_object is used as a temp
					collision_object=ODE_object_list\head\next_node
					While collision_object<>Null
						If collision_object\body
							If collision_object\prop=False And collision_object\on_set
								apply_blower(temp, collision_object)
							EndIf
						EndIf
					collision_object=collision_object\next_node
					Wend
	;			Case GEOM_MAGNET
					;if it's a magnet, apply magnet forces to all other objects...
					;Collision_object is used as a temp
				Default
					; Disable out of bounds objects
					; check if all objects have stoped
					; accumulate magnetic charges
					; rolling resistance for spheres
					If temp\on_set
						If temp\gravity>0
							collision_object=ODE_object_list\head
							While collision_object<>Null
								If collision_object\body
									If collision_object\prop=False And dBodyIsEnabled(collision_object\body) And collision_object\on_set
										Apply_Gravity(temp, collision_object)
									EndIf
								EndIf
							collision_object=collision_object\next_node
							Wend
						EndIf
						If temp\body
							; *** Disable out of bounds objects ###############################
							If Abs(dBodyGetPositionX(temp\body))>ARENA_X_RADIUS Or Abs(dBodyGetPositionZ(temp\body))>ARENA_Z_RADIUS
								dBodyDisable(temp\body)
								HideEntity temp\mesh
								HideEntity temp\outline_mesh
							EndIf
							
							If dBodyIsEnabled(temp\body)
								; *** Object stopped ########################################
If debug_break=True Then Stop
 								If stop_time And (temp\joint\joint_type<=0)
									bx#=dBodyGetLinearVelX(temp\body)
									by#=dBodyGetLinearVelY(temp\body)
									bz#=dBodyGetLinearVelZ(temp\body)
									b#=Sqr((bx*bx)+(by*by)+(bz*bz))
									If b>0.02
										stop_time=0	; record if all objects are static
									End If
								EndIf
								
								; *** Detect magnetism #####################################
								magnetic_collision=False
								numCollisions=dGeom1CountCollisions(temp\geom)
								If numCollisions>0
									For p=1 To numCollisions
										collision_geom=dGeom1CollisionGeom(temp\geom,p)
										collision_object=ODE_object_list\head
										While collision_object<>Null
											If collision_object\geom=collision_geom
												Exit
											EndIf
											collision_object=collision_object\next_node
										Wend ; Fetch collision object
										If collision_object<>Null
											If collision_object\geom_type=GEOM_MAGNET Or collision_object\magnetism=MAGNETIC_TIME
												magnetic_collision=True
												Exit
											EndIf
										EndIf
									Next
								EndIf	
								If Not magnetic_collision
									numCollisions=dGeom2CountCollisions(temp\geom)
									If numCollisions>0
										For p=1 To numCollisions
											collision_geom=dGeom2CollisionGeom(temp\geom,p)
											collision_object=ODE_object_list\head
											While collision_object<>Null
												If collision_object\geom=collision_geom
													Exit
												EndIf
												collision_object=collision_object\next_node
											Wend ; Fetch collision object
											If collision_object<>Null
												If collision_object\geom_type=GEOM_MAGNET Or collision_object\magnetism=MAGNETIC_TIME
													magnetic_collision=True
													Exit
												EndIf
											EndIf
										Next
									EndIf
								EndIf ; Magnetic collision
								; magnetic_collision=True if collided with magnetic object
								If magnetic_collision And temp\magnetism>0
									If temp\magnetism<MAGNETIC_TIME
										temp\magnetism=temp\magnetism+1
										If temp\magnetism=2
	;										dGeomContactSetMu(temp\geom,10000)
										EndIf
									Else
										dBodyDisable(temp\body)
									EndIf
								Else
									If (Not temp\magnetism) And temp\magnetism>=MAGNETIC_TIME
										temp\magnetism=1
									EndIf
								EndIf
								
								; *** Sphere Rotation Dampening #################################
								If temp\geom_type=GEOM_SPHERE
									hit_ground=False
									numCollisions=dGeom1CountCollisions(temp\geom)
									If numCollisions>0
										; For each collision, test if with ground
										For p=1 To numCollisions
											collision_geom=dGeom1CollisionGeom(temp\geom,p)
											collision_object=ODE_object_list\head
											While collision_object<>Null
												If collision_object\geom=collision_geom
													Exit
												EndIf
												collision_object=collision_object\next_node
											Wend ; Fetch collision object
											If collision_object<>Null
												If collision_object\geom_type=GEOM_TABLE
													hit_ground=True
													Exit
												EndIf
											EndIf
										Next
									EndIf
									numCollisions=dGeom2CountCollisions(temp\geom)
									If numCollisions>0 And hit_ground=False
										; For each collision, test if with ground
										For p=1 To numCollisions
											collision_geom=dGeom2CollisionGeom(temp\geom,p)
											collision_object=ODE_object_list\head
											While collision_object<>Null
												If collision_object\geom=collision_geom
													Exit
												EndIf
												collision_object=collision_object\next_node
											Wend ; Fetch collision object
											If collision_object<>Null
												If collision_object\geom_type=GEOM_TABLE
													hit_ground=True
													Exit
												EndIf
											EndIf
										Next
									EndIf	
									If hit_ground
										dBodyGetAngularVel(temp\body)
										xa#=dVectorX()
										ya#=dVectorY()
										za#=dVectorZ()
										angular_vel#=Sqr(xa^2+ya^2+za^2)*10
										If angular_vel>0
											; normalize vector
											xa=xa/angular_vel
											ya=ya/angular_vel
											za=za/angular_vel
										EndIf
						
										dBodyGetLinearVel(temp\body)
										xl#=dVectorX()
										yl#=dVectorY()
										zl#=dVectorZ()
										linear_vel#=Sqr(xl^2+yl^2+zl^2)*10
										If linear_vel>0
											; normalize vector
											xl=xl/linear_vel
											yl=yl/linear_vel
											zl=zl/linear_vel
										EndIf	 
							
										d#=Abs(Abs(linear_vel)-(temp\siz\x*Abs(angular_vel)))
										If d< 0.02 And d>=0.0		; angular and linear velocities matched
											; Perform slowdown
											reduction#=(temp\mat\rotational_friction)/(Log(temp\weight)+1)
											d=linear_vel-reduction
											If d>0
												xa=xa*(angular_vel-reduction);/ode\size
												ya=ya*(angular_vel-reduction);/ode\size
												za=za*(angular_vel-reduction);/ode\size
												dBodySetAngularVel(temp\body,xa,ya,za)
							
												xl=xl*(linear_vel-reduction);/ode\size
												yl=yl*(linear_vel-reduction);/ode\size
												zl=zl*(linear_vel-reduction);/ode\size
												dBodySetLinearVel(temp\body,xl,yl,zl)
											Else
												dBodySetAngularVel(temp\body,0,0,0)
												dBodySetLinearVel(temp\body,0,0,0)
											EndIf
										EndIf
									EndIf
								EndIf
							EndIf
						EndIf
					EndIf
			End Select
			temp=temp\next_node
		Wend
		NUM_UPDATE_CUSTOM_PHYSICS=NUM_UPDATE_CUSTOM_PHYSICS+1
		dWorldQuickStep(ODE_world,ODE_Resolution#)
		dJointGroupEmpty(ODE_contactGroup)
	
		dSpaceCollide(ODE_space,ODE_world,ODE_contactGroup)
		dWorldQuickStep(ODE_world,ODE_Resolution#)
		dJointGroupEmpty(ODE_contactGroup)
	
		dSpaceCollide(ODE_space,ODE_world,ODE_contactGroup)
		dWorldQuickStep(ODE_world,ODE_Resolution#)
		dJointGroupEmpty(ODE_contactGroup)
		objects_stopped=objects_stopped+stop_time
		t=t+1
	EndIf

	Return t
End Function



I've just tried on a PIII 600 MHz and things were much, much better. My target machine of an 800 MHz PIII should be reached. I don't know what the K6 is all about, and I was surprised the P3 is so much faster (both have 256 Mb RAM, Win98). Of course, now I'm getting random MAVs to worry about, so I'll have to stick Blitz on there and investigate. Hopefully this problem can be laid to rest without us ever having to solve it!

Well, it's not working. I've found that the method I use to create joints that works on modern computers doesn't work on old computer. How on earth am I supposed to understand that?

This pic shows one level. A series of boxes decends a chute. This white plank moves left and right to sort them. To move the plank I have a sphere either side (semitransparent) that pushes it, jointed with a hinge joint around the red pivot points. The rotation of these spheres is selected by the user.



On my computer and other modern computers, this work perfectly. On the older Win98 machines, it doesn't. On the P3 they just drop out of the sky, as though the joint wasn't created, which would explain the MAVs I was getting when I tried to change the joint parameters. Although I don't always get a MAV.

The pics are upside down BTW. The bottom pic is the starting positon.

This appears to be solved. In creating joints you have to specify the body/ies to connect it to, which are memory addresses. If you want to save the state of joints you can't use memory addresses so I used a simple Id of joint obj = 1 for this object, 2 for the previous object.

In my code I was replacing the joint values 1 and 2 with the memory locations for objects, but referencing the create joint function after that when object memory locations had changed. Hence MAVs and some joints not working.

In the more modern PCs, objects were being kept in the same memory locations when destroyed and recreated. Whether that's Win2000+ or more memory or what, I don't know.

Now my camera is starting to wander aimlessly, but hopefully I can pin that to the same problem.

Outstanding Shifty, I'm glad you were able to identify the issues, I guess MAV really works! 8)

Always nice to see what interesting things other people are doing. Thanks for posting.

It's not easy. I can't solve bugs on the development PC because it manages to cope with them. Whereas the P3 has about a 5 second lag on mouse input when debugging! Hopefully people will read this and avoid the pitfalls I've found. Basically referencing objects by pointer and delting+creating objects is a recipe for trouble that you might not encounter a until long after you thought you were finished and finding these sorts of critical bugs can cause maximum stress! Planning is absolutely key. No what your addressing and when. Unfortunately I'm not a natural coder and I find it very hard to keep track of how the thing works. Developing piecemeal code and gaffer-taping it all together is far from ideal.