How to roll a wheel not ball down a halfpipe?

Blitz3D Forums/Blitz3D Programming/How to roll a wheel not ball down a halfpipe?

Rob(Posted 2003) [#1]
Hi all,

The other topic got very confused! I really need to be able to roll a wheel. What happens is, if the wheel turns, it shouldn't continue sliding down a halfpipe, but run along it. The energy built up by speed should be enough for it to travel upwards too.

Any tips? I'll pay for this code if it's good enough.


Rob(Posted 2003) [#2]
got this far:
	;get averaged normals from surface
	numcols=CountCollisions(board)
	If numcols
		For i=1 To numcols
			nx#=nx#+CollisionNX(wheel,i)
			ny#=ny#+CollisionNY(wheel,i)
			nz#=nz#+CollisionNZ(wheel,i)			
		Next
		nx=nx/numcols
		ny=ny/numcols
		nz=nz/numcols
	EndIf

	;dot product
	dot# = vx*nx + vy*ny + vz*nz

	;normal force
	nfx# = - 1 * nx * dot
	nfy# = - 1 * ny * dot
	nfz# = - 1 * nz * dot
	
	;add result back to velocities
	vx = vx + nfx
	vy = vy + nfy
	vz = vz + nfz

	;gravity
	vy = vy - 0.05




DH(Posted 2003) [#3]
We ran into the same problems...

Idealy, we wanted to simply push the ball in a direction, have its inertia keep it moving (with a air force or drag and friction slowing it down). Ideally, it would help with rolling up half pipe's where gravity would then take over and bring it back down.

I went through 8 different versions of our physics library to find that I can't get it to the realism I wanted. Originally I had taken the Ball's velocity, and the normal it was colliding with and extracting a new velocity upon that (that way the ball had true 3d velocity). Problem is that I wanted real gravity, and with that was a timed calculation of 9.8 ms^2 (within the library, I found how many Millisecs had passed between now and my last gravity calculation, converted to seconds, and applied the gravity forumula to it to determine the correct downward force).

Issue here was "how do you know when the time should be reset on gravity, cause if it wasnt reset, it would continually grow in value". The answer orriginally was if it was on a normal which faced up. Issue now was "what if it is on a hill, where the gravity would affect the directional velocities and not just the vertical velocities". In this case, it couldnt always be reset on an upward collision normal.

Its been a constant battle as you can see. With each breakthrough (as I saw it), there were more problems arrising. Ultimatly (and because I dont have the time to create a nice physics model for general use), I took the whole thing to situational logic. Meaning that inertia is affected by movement, movement is affected by force, and movement is affected by a fraction of inertia. Ultimatly meaning that the inertia is the adapting factor, and in that, the averaging.

Perhaps i am getting ahead of myself here, but after a few months of trying to get somethign to work, it gets you a bit crazy.


Rob(Posted 2003) [#4]
Yes, but thats still a ball :) Where is sswift when you need him? in fact sswift should operate on a code-this-for-paypal basis...


poopla(Posted 2003) [#5]
Rob, tell me what you want and we could work out a contract. I'll even do it cheap. ;)

(granted I undestand what you're trying to do ;))


Rob(Posted 2003) [#6]
What I really want is reliable skateboard or wheel physics...


Jeppe Nielsen(Posted 2003) [#7]
I have modified my ball code to feature a wheel instead.
Hope you can use it :)

;wheel rolling physics example by Jeppe Nielsen 2003

Const gravity#=-0.04;-0.02 ;gravity constant

Graphics3D 800,600,16,2


Const wheel_col=1
Const world_col=2
Collisions wheel_col,world_col,2,3


;create old amiga texture :-)
Global texture

texture=CreateTexture(16,16)

SetBuffer TextureBuffer(texture)

Color 255,255,255
Rect 0,0,16,16,1

Color 255,0,0
Rect 0,0,8,8,1
Rect 8,8,8,8,1

ScaleTexture texture,0.1,0.1

Global groundtexture=CreateTexture(32,32,1+8)

SetBuffer TextureBuffer(groundtexture)

Color 0,0,0
Rect 0,0,32,32,1

Color 0,0,255
Rect 0,0,32,32,0

ScaleTexture groundtexture,2,2

SetBuffer BackBuffer()
Color 255,255,255

plane=CreatePlane()
EntityTexture plane,groundtexture
EntityType plane,world_col
EntityAlpha plane,0.5



light=CreateLight(1)
RotateEntity light,30,20,0



obj=CreateCube()
EntityType obj,world_col
PositionEntity obj,0,0,0
ScaleEntity obj,40,40,40
RotateEntity obj,45,0,0
EntityTexture obj,texture


camera=CreateCamera()
CameraClsColor camera,0,0,255


wheel.wheel=wheelnew(0,4,-120,2,0.8)

zoom#=14

Repeat

zoom#=zoom#-MouseZSpeed()
wheelcamera(camera,wheel,0,zoom#,-zoom#,0,5,0)


wheelcontrol()
wheelupdate()

RenderWorld()

Color 255,255,255

Text 10,10,"Arrow keys to control wheel"
Text 10,20,"Space to lift wheel"
Text 10,30,"Scroll on mouse to zoom"



Flip


Until KeyDown(1)
End


Type wheel

Field e ;entity

Field sphere

Field pivot

Field x#,y#,z# ; position in 3d-space

Field vx#,vy#,vz# ; velocity

Field ax#,ay#,az# ; acceleration

Field size#

Field bounce# ; bounce factor

Field vel#

Field vx2#,vy2#,vz2# ; temp velocity

End Type


Function wheelnew.wheel(x#,y#,z#,size#=1,bounce#=0.9)
	
	c.wheel=New wheel
	
	c\x#=x#
	c\y#=y#
	c\z#=z#
	
	c\size=size
	
	c\bounce#=bounce#
	
	c\e=CreatePivot();CreateCylinder(64)
	
		
	c\sphere=CreateCylinder(64) ;CreateSphere(64)
	RotateMesh c\sphere,0,0,90
	
	c\pivot=CreatePivot()
	
	EntityType c\e,wheel_col
	EntityRadius c\e,c\size
	
	PositionEntity c\e,c\x,c\y,c\z
	ScaleEntity c\sphere,c\size,c\size,c\size
	
	EntityTexture c\sphere,texture
	
	Return c
	
End Function

Function wheelupdate()
	
	For c.wheel=Each wheel
	
			
		c\vy#=c\vy#+gravity#
		
		c\vx#=c\vx#+c\ax#
		c\vy#=c\vy#+c\ay#
		c\vz#=c\vz#+c\az#
		
		c\x#=EntityX(c\e)
		c\y#=EntityY(c\e)
		c\z#=EntityZ(c\e)
		
		TranslateEntity c\e,c\vx,c\vy,c\vz	
	
	Next
	
	UpdateWorld()
		
	For c.wheel=Each wheel
	
		;correct velocity if collided
		c\vx2=(EntityX(c\e)-c\x)
		c\vy2=(EntityY(c\e)-c\y)
		c\vz2=(EntityZ(c\e)-c\z)
		
		c\x#=EntityX(c\e)
		c\y#=EntityY(c\e)
		c\z#=EntityZ(c\e)
		
		PositionEntity c\sphere,c\x,c\y,c\z
		PositionEntity c\pivot,c\x,c\y,c\z
		
		TFormVector 1,0,0,c\e,0
		AlignToVector c\sphere,TFormedX(),TFormedY(),TFormedZ(),1
				
		If EntityCollided(c\e,world_col)
				
			avnx#=0
			avny#=0
			avnz#=0
			
			count=CountCollisions(c\e)	
				
			For i = 1 To CountCollisions(c\e)
				; Get the normal of the surface which the entity collided with. 
				Nx# = CollisionNX(c\e, i) 
				Ny# = CollisionNY(c\e, i) 
				Nz# = CollisionNZ(c\e, i) 
				
				avnx#=avnx+Nx
				avny#=avny+Ny
				avnz#=avnz+Nz
				
				; Compute the dot product of the entity's motion vector and the normal of the surface collided with. 
				VdotN# = c\vx#*Nx# + c\vy#*Ny# + c\vz#*Nz# 
				
				; Calculate the normal force. 
				NFx# = -2.0 * Nx# * VdotN# 
				NFy# = -2.0 * Ny# * VdotN# 
				NFz# = -2.0 * Nz# * VdotN# 
				
				; Add the normal force to the direction vector. 
				c\vx# = c\vx# + NFx# * c\bounce#
				c\vy# = c\vy# + NFy# * c\bounce#
				c\vz# = c\vz# + NFz# * c\bounce#
							
			Next
				
				If count>0
				
				;average collision normals
				avnx#=avnx/count
				avny#=avny/count
				avnz#=avnz/count

				;align controlpivot to averaged collision normals
				AlignToVector c\e,avnx,avny,avnz,2
				
				;calculate length of motion vector, or total velocity.
				l#=Sqr(c\vx*c\vx+c\vy*c\vy+c\vz*c\vz)

				;get normalized direction of motion, the vector will have a length of one.
				;the +0.00001 is there to aviod NaN (not a number) numbers. This happens
				;if the length of the vector is 0.
				TFormNormal c\vx,c\vy+0.00001,c\vz,0,0
				vx#=TFormedX()
				vy#=TFormedY()
				vz#=TFormedZ()
											
				;get direction of wheel
				TFormNormal 0,0,1,c\e,0
				
				;calculate a dot product, a number between -1 and 1 to determine angle
				;calculate angle like this: angle#=Acos(dot#)		
				dot#=TFormedX()*vx+TFormedY()*vy+TFormedZ()*vz
				
				;correct velocities, by multiplying the controlpivotīs direction with
				;the dot product, followed by the original velocity vector length:
				c\vx=TFormedX()*dot*l
				c\vy=TFormedY()*dot*l
				c\vz=TFormedZ()*dot*l
						
					
				TFormVector -1,0,0,c\sphere,0 				
				AlignToVector c\pivot,TFormedX(),TFormedY(),TFormedZ(),1
																					
			EndIf
						
			c\vel#=Sqr(c\vx2*c\vx2+c\vy2*c\vy2+c\vz2*c\vz2)
						
			;slow down due To friction
			c\vx#=c\vx*0.98
			c\vy#=c\vy*0.98
			c\vz#=c\vz*0.98
			
		EndIf
	
	
		EntityParent c\sphere,c\pivot
								
		TurnEntity c\pivot,-c\vel#*(180/Pi)/c\size#,0,0

		EntityParent c\sphere,0

		;reset acceleration
		c\ax#=0
		c\ay#=0
		c\az#=0	
		
	Next
	
End Function


Function wheelcontrol()
	
	For c.wheel=Each wheel
		
		If KeyDown(200)
			
			TFormVector 0,0,0.05,c\e,0
			
			c\ax#=TFormedX()
			c\ay#=TFormedY()
			c\az#=TFormedZ()
			
		EndIf
		
		If KeyDown(208)
			
			c\vx=c\vx*0.94
			c\vy=c\vy*0.94
			c\vz=c\vz*0.94
			
		EndIf
		
		If KeyDown(57)
			
			c\ay#=c\ay+0.05
														
		EndIf
		
		If KeyDown(203)
			
			TurnEntity c\e,0,2,0
			
		EndIf
		
		If KeyDown(205)
			
			TurnEntity c\e,0,-2,0
			
		EndIf
		
		
	Next
	
	
	
End Function

Function wheelcamera(camera,b.wheel,camx#,camy#,camz#,aimx#=0,aimy#=0,aimz#=0,smoothcam#=0.1,roll#=0)
	
	TFormPoint camx#,camy#,camz#,b\e,0
	
	dx#=(TFormedX()-EntityX(camera))*smoothcam#
	dy#=(TFormedY()-EntityY(camera))*smoothcam#
	dz#=(TFormedZ()-EntityZ(camera))*smoothcam#
	
	TranslateEntity camera,dx,dy,dz
	
	TFormPoint aimx#,aimy#,aimz#,b\e,0
	
	dx# = EntityX(camera)-TFormedX()
	dy# = EntityY(camera)-TFormedY()
	dz# = EntityZ(camera)-TFormedZ()
	dist#=Sqr#((dx#*dx#)+(dz#*dz#))
	pitch#=ATan2(dy#,dist#)
	yaw#=ATan2(dx#,-dz#)
	
	RotateEntity camera,pitch#,yaw#,roll#
	
End Function



Rob(Posted 2003) [#8]
Hi Jeppe,

Thats absolutely perfect! I really have no idea how you did it so well! Definately one for codearchives and for me to learn from.

Thanks a million!


Jeppe Nielsen(Posted 2003) [#9]
Youīre welcome :-)

I just sat down and drew it on paper to work it out. And in blitz I had the vectors drawn to the screen, to see if they behaved like I would have them to.

These are the lines that do the trick:
;calculate length of motion vector, or total velocity.
				l#=Sqr(c\vx*c\vx+c\vy*c\vy+c\vz*c\vz)

				;get normalized direction of motion, the vector will have a length of one.
				;the +0.00001 is there to aviod NaN (not a number) numbers. This happens
				;if the length of the vector is 0.
				TFormNormal c\vx,c\vy+0.00001,c\vz,0,0
				vx#=TFormedX()
				vy#=TFormedY()
				vz#=TFormedZ()
											
				;get direction of wheel
				TFormNormal 0,0,1,c\e,0
				
				;calculate a dot product, a number between -1 and 1 to determine angle
				;calculate angle like this: angle#=Acos(dot#)		
				dot#=TFormedX()*vx+TFormedY()*vy+TFormedZ()*vz
				
				;correct velocities, by multiplying the controlpivotīs direction with
				;the dot product, followed by the original velocity vector length:
				c\vx=TFormedX()*dot*l
				c\vy=TFormedY()*dot*l
				c\vz=TFormedZ()*dot*l


btw it is added to the codearchives.


Pongo(Posted 2003) [#10]
Very nice,... I've been following this closely as well, since it is very similar to a project I am working on right now too.

I noticed one thing though,... when you roll backwards, the wheel rolls the wrong way. It is tough to see with the current texture,... if you change it to be less tiled this will be more obvious.


TartanTangerine (was Indiepath)(Posted 2003) [#11]
Nice one Jeppe, I tried to change your code but my wheel moved in a very "DRUNKEN" fashion i.e it wobbled lots :)


Rob(Posted 2003) [#12]
this just shows how important physics are - even a simple3d game based on a vehicle of any kind will suck otherwise


Jeppe Nielsen(Posted 2003) [#13]
Just updated the code in the archives, the rolling bug is now corrected. Get it here


Rob(Posted 2003) [#14]
Wow thanks!


patisawesome(Posted 2003) [#15]
Cool physics!


patisawesome(Posted 2003) [#16]
Jeppe Nielsen,
When the wheel rolls down, it needs to turn around and go down.