Tokamak Rally

Miscellaneous Forums/Blitz Showcase/Tokamak Rally

Well, not really a rallygame but at least a car driving around a track.
The carphysics still needs alot of tweaking so don't expect anything spectacular.
Anyway, here it is...
http://www.svenberra.net/tokamakrally.zip


The carmodel is made by Pongo.

Pretty good! It feels fairly nice, even though it seems like the car is turning around it's center...It isn't fast enough, a bit pedestrian.

A bit Sega Rallyish, with a lot less speed :)

Can you roll the car ?

Please tell me yes, i've just spent 30 mins trying instead of finishing my work this Friday PM.

Looks promising - also are you using sensors ?

Funky Frank

Sweenie, thats great any chance you will release the code?

Nice demo! And to think I hated physics at school :)

Like Fredborg says, it goes too slow, but the movement is very nice indeed. Funky Frank -- I rolled it (eventually)!

@Funky Frank:
With the current frictionsettings I used in the demo it makes it a bit hard to roll.
But with some adjustments I could make it roll much easier.
Higher velocities will make it easier to roll as well.

@CopperCircle:
Sure thing. But I will need to cleanup the code a bit first.
I tend to write messy spaghetticode and then clean it up afterwards.

The method is pretty simple though.

One Rigidbodybox for the chassi and four rigidbodyspheres as wheels.
Then I attach the spheres to the body using the slider joint.
To get the suspension I simply use a basic springforce formula to apply forces to the spheres and the body.

The driving forces(longitudal force) is applied to the rear wheels and the sideways friction of the wheels is done by multiplying the wheels X-velocity by a certain gripfactor.
The lower the gripfactor the easier for the wheels to slip.
The steering comes automatically as the x-velocity removal is based on the wheels orientation.

But I realized that the key to a good simulation is lots of tweaking.

The shocks are way too loose, but this is the best car physics I have seen in Blitz. Good work.

Thats a nice car demo. My only crits are that it looks like to much linear damping. This is noticable when you go off a jump and kinda stop. This could just be the more realistic gravity though :P

Very impressive work dude :)

very cool, i like it, though i wish you could drive faster than right now.

The suspension is adjustable.
I used a very wobbly suspension in the demo to make it more noticeable to the viewer.

I will increase the allowed speed of the vehicle, but i'm gonna need to build a bigger track. ;)

Very stable!

Would you be kind enough to tell me what joint settings I need to use in order to make the slider joint sit between the car and the chassis?

And do I push the slider joint down or the car up? A little bit confused :)

The demo is really well done, very stable physics and nice feel!

Hay Id love the source to this as well.

On the subject of tokomak, I downloaded it again but the two samples you get done work I get some DLL function missing error or something like that.

Any chance you could check it out Sweenie, or am I just missing something.. The Dcls and DLL are in the userlib folder.

Nice game or whatever :)

Do a bit of work and you might have a really fun game or whatever :P

(I almost managed to roll the truck, but right before it stopped moving it fell off the edge!)

Sorry I meant what slider joint code shall I use for wheel to chassis?

I got it on its side. It's a bit weird though, I think I can still move it then. Hope that's fixable.
Nice job Sweenie!

I flipped it by driving off the edge of the terrain.

That was surprisingly fun.

This is exacly how "surprisingly fun" my prototypes are.

But better than anything I could accomplish

Any source? This is a pretty good base to work from.

cool. If you go off the edge, after a while the car is spinning so hard its wheels are floating around it.

Give us the source!

Ok, here it is...(messy & badly commented, the Sweenie way!)


Const FPS=90
Type vector
	Field x#
	Field y#
	Field z#
End Type
Global CProd.vector=New vector
Type csys
   Field cx#,cy#,cz#
   Field mx#,my#,sps
End Type

Graphics3D 800,600,0,2
SetBuffer BackBuffer()

camera = CreateCamera()
PositionEntity camera,10,8,-5

WireFrame False

TOKSIM_CreateSimulator(0,-9.0,0)
TOKSIM_SetMaterial 0,0.0,0.6 

ColBank=CreateBank(104*10)
TOKSIM_SetCInfoBank ColBank
TOKSIM_SetCollisionResponse -1,1,3 ; Impulse & Feedback 
TOKSIM_SetCollisionResponse -1,2,0 ; No Impulse Or Feedback 


tex = LoadTexture("track.bmp")
Terrain = LoadMesh("track.3ds")
ScaleMesh Terrain,25.0,25.0,25.0 
RotateMesh Terrain,-90,0,0
EntityTexture terrain,tex
MakeTokCollider(Terrain)

SeedRnd MilliSecs()  

Global Body_Mesh = LoadMesh("truck_body.b3d")
ScaleMesh Body_Mesh,0.3,0.3,0.3
PositionMesh Body_Mesh,0.0,-2.1,0.0
RotateMesh Body_Mesh,0,180,0

Global Body_RB = TOKRB_Create()
TOKRB_AddBox Body_RB,2.0,1.0,3.0
TOKRB_SetPosition Body_RB,0,5,0
TOKRB_SetMass Body_RB,1.0
TOKRB_SetBoxInertiaTensor Body_RB,2.0,1.0,3.0,1.0
ExtraWeight = TOKRB_Create()
TOKRB_SetCollisionID ExtraWeight,2
TOKRB_SetMass ExtraWeight,0.4
TOKRB_SetSphereInertiaTensor ExtraWeight,0.5,0.4 
TOKRB_SetPosition ExtraWeight,0,5,1.5
ConnJoint = TOKJOINT_Create(2,ExtraWeight,Body_RB)
TOKJOINT_SetType ConnJoint,1 
TOKJOINT_SetPositionAndRotationWorld ConnJoint,0,5.0,1.5,0,0,0 
TOKJOINT_Enable ConnJoint,True

Global RR_Spr_RB = TOKRB_Create()
TOKRB_AddSphere RR_Spr_RB,1.0
TOKRB_SetPosition RR_Spr_RB,1.2,3.0,-1.50
TOKRB_SetMass RR_Spr_RB,1.0
TOKRB_SetSphereInertiaTensor RR_Spr_RB,1.0,1.0
TOKRB_SetCollisionID RR_Spr_RB,1 
RR_SprJoint = TOKJOINT_Create(2,RR_Spr_RB,Body_RB)
TOKJOINT_SetType RR_SprJoint,4 ; Slidejoint
TOKJOINT_SetPositionAndRotationWorld RR_SprJoint,1.2,3.0,-1.50,0,0,0 
TOKJOINT_Enable RR_SprJoint,True

Global RL_Spr_RB = TOKRB_Create()
TOKRB_AddSphere RL_Spr_RB,1.0
TOKRB_SetPosition RL_Spr_RB,-1.2,3.0,-1.5
TOKRB_SetMass RL_Spr_RB,1.0
TOKRB_SetSphereInertiaTensor RL_Spr_RB,1.0,1.0
TOKRB_SetCollisionID RL_Spr_RB,1 
RL_SprJoint = TOKJOINT_Create(2,RL_Spr_RB,Body_RB)
TOKJOINT_SetType RL_SprJoint,4 ; Slidejoint
TOKJOINT_SetPositionAndRotationWorld RL_SprJoint,-1.2,3.0,-1.5,0,0,0 
TOKJOINT_Enable RL_SprJoint,True

Global FR_Spr_RB = TOKRB_Create()
TOKRB_AddSphere FR_Spr_RB,1.0
TOKRB_SetPosition FR_Spr_RB,1.2,3.0,1.65
TOKRB_SetMass FR_Spr_RB,1.0
TOKRB_SetSphereInertiaTensor FR_Spr_RB,1.0,1.0
TOKRB_SetCollisionID FR_Spr_RB,1 
FR_SprJoint = TOKJOINT_Create(2,FR_Spr_RB,Body_RB)
TOKJOINT_SetType FR_SprJoint,4 ; Slidejoint
TOKJOINT_SetPositionAndRotationWorld FR_SprJoint,1.2,3.0,1.65,0,0,0 
TOKJOINT_Enable FR_SprJoint,True

Global FL_Spr_RB = TOKRB_Create()
TOKRB_AddSphere FL_Spr_RB,1.0
TOKRB_SetPosition FL_Spr_RB,-1.2,3.0,1.65
TOKRB_SetMass FL_Spr_RB,1.0
TOKRB_SetSphereInertiaTensor FL_Spr_RB,1.0,1.0
TOKRB_SetCollisionID FL_Spr_RB,1 
FL_SprJoint = TOKJOINT_Create(2,FL_Spr_RB,Body_RB)
TOKJOINT_SetType FL_SprJoint,4 ; Slidejoint
TOKJOINT_SetPositionAndRotationWorld FL_SprJoint,-1.2,3.0,1.65,0,0,0 
TOKJOINT_Enable FL_SprJoint,True


FR_Wheel=LoadMesh("truck_wheel.b3d")
ScaleMesh FR_Wheel,0.3,0.3,0.3
FL_Wheel=LoadMesh("truck_wheel.b3d")
ScaleMesh FL_Wheel,0.3,0.3,0.3
RR_Wheel=LoadMesh("truck_wheel.b3d")
ScaleMesh RR_Wheel,0.3,0.3,0.3
RL_Wheel=LoadMesh("truck_wheel.b3d")
ScaleMesh RL_Wheel,0.3,0.3,0.3

CamPoint = TOKRB_Create()
TOKRB_SetMass CamPoint,1.0
TOKRB_SetSphereInertiaTensor FR_Spr_RB,1.0,1.0
TOKRB_SetLinearDamping CamPoint,0.01

Centerpivot = CreatePivot()

CamTargetPivot=CreatePivot()

Global speed#=0

GripFactor#=0.2

light=CreateLight()
LightColor light,128,128,128 
PositionEntity light,255,255,-255

PointEntity light,Centerpivot

period=1000/FPS
time=MilliSecs()-period
Global timestep#=1.5/FPS
; MainLoop
While Not KeyHit(1)

	Repeat
		elapsed=MilliSecs()-time
	Until elapsed

	ticks=elapsed/period
	tween#=Float(elapsed Mod period)/Float(period)

	For k=1 To ticks
		time=time+period
		If k=ticks Then CaptureWorld

		TOKSIM_Advance(1.5/FPS,1)


	UpdateWorld

    PlaceMesh Body_Mesh,Body_RB
	PositionEntity FR_Wheel,TOKRB_GetX#(FR_Spr_RB),TOKRB_GetY#(FR_Spr_RB),TOKRB_GetZ#(FR_Spr_RB)
	RotateEntity FR_Wheel,EntityPitch#(Body_Mesh),EntityYaw#(Body_Mesh),EntityRoll#(Body_Mesh)
	TurnEntity FR_Wheel,0,Turn#,0 
	PositionEntity FL_Wheel,TOKRB_GetX#(FL_Spr_RB),TOKRB_GetY#(FL_Spr_RB),TOKRB_GetZ#(FL_Spr_RB)
	RotateEntity FL_Wheel,EntityPitch#(Body_Mesh),EntityYaw#(Body_Mesh),EntityRoll#(Body_Mesh)
	TurnEntity FL_Wheel,0,Turn#,0 
	PositionEntity RR_Wheel,TOKRB_GetX#(RR_Spr_RB),TOKRB_GetY#(RR_Spr_RB),TOKRB_GetZ#(RR_Spr_RB)
	RotateEntity RR_Wheel,EntityPitch#(Body_Mesh),EntityYaw#(Body_Mesh),EntityRoll#(Body_Mesh)
	PositionEntity RL_Wheel,TOKRB_GetX#(RL_Spr_RB),TOKRB_GetY#(RL_Spr_RB),TOKRB_GetZ#(RL_Spr_RB)
	RotateEntity RL_Wheel,EntityPitch#(Body_Mesh),EntityYaw#(Body_Mesh),EntityRoll#(Body_Mesh)

 	PointEntity camera,Body_Mesh

	UpdateSpring 1.2,-0.5,-1.5,RR_Spr_RB,50,6.0,1.1
	UpdateSpring -1.2,-0.5,-1.5,RL_Spr_RB,50,6.0,1.1
	UpdateSpring 1.2,-0.5,1.65,FR_Spr_RB,50,6.0,1.1
	UpdateSpring -1.2,-0.5,1.65,FL_Spr_RB,50,6.0,1.1

	RRColl=False
	RLColl=False
	FRColl=False
	FLColl=False
	;Check Colliders
	If TOKSIM_GetCollisionCount()>0 Then
		 For c=0 To TOKSIM_GetCollisionCount()-1
			If PeekInt(ColBank,0+(c*104))=RR_Spr_RB Then RRColl=True
			If PeekInt(ColBank,0+(c*104))=RL_Spr_RB Then RLColl=True
			If PeekInt(ColBank,0+(c*104))=FR_Spr_RB Then FRColl=True
			If PeekInt(ColBank,0+(c*104))=FL_Spr_RB Then FLColl=True
	     Next
	EndIf

	;Driving-force

 If KeyDown(200) Then
  Acc#=Acc#+1.0
  If Acc#>11 Then Acc#=11
 ElseIf KeyDown(208) Then
  Acc#=Acc#-1.0
  If Acc#<6.0 Then Acc#=-6.0
 Else
  Acc#=Acc#*0.95
  If Acc#>-0.01 And Acc#<0.01 Then Acc#=0.0
 EndIf
	
	TFormVector 0,0,1,Body_Mesh,0
	vx#=TFormedX#()
	vy#=TFormedY#()
	vz#=TFormedZ#()
	
	 If RRColl=True Then
	  TOKRB_ApplyImpulse RR_Spr_RB,vx#*acc#*timestep#,vy#*acc#*timestep#,vz#*acc#*timestep#
     EndIf
	 If RLColl=True Then
	  TOKRB_ApplyImpulse RL_Spr_RB,vx#*acc#*timestep#,vy#*acc#*timestep#,vz#*acc#*timestep#
	 EndIf

	CarVelx#=TOKRB_GetVelocityX#(Body_RB)
	CarVely#=TOKRB_GetVelocityY#(Body_RB)
	CarVelz#=TOKRB_GetVelocityZ#(Body_RB)
	TFormNormal 0,0,1,Body_Mesh,0
	CarVel#=Abs(DotProduct#(TFormedX#(),TFormedY#(),TFormedZ#(),CarVelx#,CarVely#,CarVelz#))
	TurnFactor#=1.0-((1.0/35.0)*CarVel#)

MaxTurn#=27.0
CurrTurn#=MaxTurn#*TurnFactor#
If KeyDown(205) Then
 Turn#=Turn#-0.7
 If Turn#<-MaxTurn#*TurnFactor# Then Turn#=-MaxTurn#*TurnFactor#
ElseIf KeyDown(203) Then
 Turn#=Turn#+0.7
 If Turn#>MaxTurn#*TurnFactor# Then Turn#=MaxTurn#*TurnFactor#
Else
 Turn=Turn*0.95
 If Turn#>-0.1 And Turn#<0.1 Then Turn#=0.0
EndIf

	AngVelx#=TOKRB_GetAngularVelocityX#(Body_RB)
	AngVely#=TOKRB_GetAngularVelocityY#(Body_RB)
	AngVelz#=TOKRB_GetAngularVelocityZ#(Body_RB)
	TFormNormal 0,1,0,Body_Mesh,0
	AngVel#=Abs(DotProduct#(TFormedX#(),TFormedY#(),TFormedZ#(),AngVelx#,AngVely#,AngVelz#))

If KeyHit(57) Or (AngVel#>1.0 And CarVel#>12.0) Then
 Slip=True
EndIf

	If Slip=True Then 
	 GripFactor#=0.99
	EndIf	
	If AngVel#<0.7 And Slip=True Then 
	 GripFactor#=0.98
	 ;Slip=False
	EndIf	
	If AngVel#<0.5 And Slip=True Then 
	 GripFactor#=0.97
	 ;Slip=False
	EndIf	
	If AngVel#<0.3 And Slip=True Then 
	 GripFactor#=0.9
	 Slip=False
	EndIf	


If KeyHit(16) Then
 Slip=True
 TOKRB_ApplyTwist Body_RB,0,50,0
EndIf
	
	;Friction

	;Find sideslip-speed for RightRearWheel
    Vx#=TOKRB_GetVelocityX#(RR_Spr_RB)
    Vy#=TOKRB_GetVelocityY#(RR_Spr_RB)
    Vz#=TOKRB_GetVelocityZ#(RR_Spr_RB)
	TFormVector Vx#,Vy#,Vz#,0,Body_Mesh
	TFormVector TFormedX#()*GripFactor#,TFormedY#(),TFormedZ#(),Body_Mesh,0
	If RRColl=True Then
	 TOKRB_SetVelocity RR_Spr_RB,TFormedX#(),TFormedY#(),TFormedZ#()
 	EndIf

	;Find sideslip-speed for LeftRearWheel
    Vx#=TOKRB_GetVelocityX#(RL_Spr_RB)
    Vy#=TOKRB_GetVelocityY#(RL_Spr_RB)
    Vz#=TOKRB_GetVelocityZ#(RL_Spr_RB)
	TFormVector Vx#,Vy#,Vz#,0,Body_Mesh
	TFormVector TFormedX#()*GripFactor#,TFormedY#(),TFormedZ#(),Body_Mesh,0
	If RLColl=True Then
	 TOKRB_SetVelocity RL_Spr_RB,TFormedX#(),TFormedY#(),TFormedZ#()
	EndIf
	
	;Find sideslip-speed for RightFrontWheel
    Vx#=TOKRB_GetVelocityX#(FR_Spr_RB)
    Vy#=TOKRB_GetVelocityY#(FR_Spr_RB)
    Vz#=TOKRB_GetVelocityZ#(FR_Spr_RB)
	TFormVector Vx#,Vy#,Vz#,0,FR_Wheel
	TFormVector TFormedX#()*GripFactor#,TFormedY#(),TFormedZ#(),FR_Wheel,0
	If FRColl=True Then
	 TOKRB_SetVelocity FR_Spr_RB,TFormedX#(),TFormedY#(),TFormedZ#()
	EndIf
	
	;Find sideslip-speed for LeftFrontWheel
    Vx#=TOKRB_GetVelocityX#(FL_Spr_RB)
    Vy#=TOKRB_GetVelocityY#(FL_Spr_RB)
    Vz#=TOKRB_GetVelocityZ#(FL_Spr_RB)
	TFormVector Vx#,Vy#,Vz#,0,FL_Wheel
	TFormVector TFormedX#()*GripFactor#,TFormedY#(),TFormedZ#(),FL_Wheel,0
	If FLColl=True Then
	 TOKRB_SetVelocity FL_Spr_RB,TFormedX#(),TFormedY#(),TFormedZ#()
	EndIf


	;Drag
	TFormNormal 0,0,-1,Body_Mesh,0
	dragConstant# = 1.1
 	velX#=TOKRB_GetVelocityX#(Body_RB)
 	velY#=TOKRB_GetVelocityY#(Body_RB)
 	velZ#=TOKRB_GetVelocityZ#(Body_RB)

 	dot#=DotProduct#(TFormedX#(),TFormedY#(),TFormedZ#(),velX#,velY#,velZ#)
 	dragX#=dot#*TFormedX#()*-dragConstant#
 	dragY#=dot#*TFormedY#()*-dragConstant#
 	dragZ#=dot#*TFormedZ#()*-dragConstant#
	TOKRB_ApplyImpulse Body_RB,dragX#*timestep#,dragY#*timestep#,dragZ#*timestep#


	;Camera
	PositionEntity camtargetpivot,EntityX#(Body_Mesh),EntityY#(Body_Mesh),EntityZ#(Body_Mesh)
	RotateEntity camtargetpivot,EntityPitch#(Body_Mesh),EntityYaw#(Body_Mesh),EntityRoll#(Body_Mesh)
	MoveEntity camtargetpivot,0,3,-7
	UpdateCamera Camera,camtargetpivot,0.02


Next



	RenderWorld tween

	Flip False

Wend

TOKSIM_DestroySimulator()

End


Function UpdateSpring(px#,py#,pz#,Wheel_RB,Stiffness#,Damping#,SpringLength#)

;Add spring force
TFormPoint px#,py#,pz#,Body_Mesh,0
wpx#=TFormedX#()
wpy#=TFormedY#()
wpz#=TFormedZ#()

diffX#=wpx# - TOKRB_GetX#(Wheel_RB)
diffY#=wpy# - TOKRB_GetY#(Wheel_RB)
diffZ#=wpz# - TOKRB_GetZ#(Wheel_RB)
diffLen#=Sqr(diffX#*diffX#+diffY#*diffY#+diffZ#*diffZ#)
Displacement#=diffLen#-SpringLength#
nx#=diffx#/diffLen#
ny#=diffy#/diffLen#
nz#=diffz#/diffLen#
fx#=nx#*(Displacement#*Stiffness#)
fy#=ny#*(Displacement#*Stiffness#)
fz#=nz#*(Displacement#*Stiffness#)

;Add damping force
GetPointVel Body_RB,wpx#,wpy#,wpz#
Vx#=CProd\x#-TOKRB_GetVelocityX#(Wheel_RB)
Vy#=CProd\y#-TOKRB_GetVelocityY#(Wheel_RB)
Vz#=CProd\z#-TOKRB_GetVelocityZ#(Wheel_RB)

speed#=DotProduct#(nx#,ny#,nz#,Vx#,Vy#,Vz#)
dampX#=nx#*speed#*Damping#
dampY#=ny#*speed#*Damping#
dampZ#=nz#*speed#*Damping#

fx#=fx#+dampX#
fy#=fy#+dampY#
fz#=fz#+dampZ#

TOKRB_ApplyImpulse2 Body_RB,-fx#*timestep#,-fy#*timestep#,-fz#*timestep#,wpx#,wpy#,wpz#
TOKRB_ApplyImpulse Wheel_RB,fx#*timestep#,fy#*timestep#,fz#*timestep#

End Function

Function PlaceMesh(Mesh,RB)
   PositionEntity Mesh,TOKRB_GetX#(RB),TOKRB_GetY#(RB),TOKRB_GetZ#(RB) 
   RotateEntity Mesh,TOKRB_GetPitch#(RB),TOKRB_GetYaw#(RB),TOKRB_GetRoll#(RB),False
End Function


Function DotProduct#(x1#,y1#,z1#,x2#,y2#,z2#)
	DProd#=((x1#*x2#)+(y1#*y2#)+(z1#*z2#))
	Return DProd#
End Function
Function CrossProduct(x1#,y1#,z1#,x2#,y2#,z2#)
	CProd\x#=(y1#*z2#)-(z1#*y2#)
	CProd\y#=(z1#*x2#)-(x1#*z2#)
	CProd\z#=(x1#*y2#)-(y1#*x2#)
End Function

Function GetPointVel(RB,px#,py#,pz#)
 vx#=TOKRB_GetVelocityX#(RB)
 vy#=TOKRB_GetVelocityY#(RB)
 vz#=TOKRB_GetVelocityZ#(RB)
 avx#=TOKRB_GetAngularVelocityX#(RB)
 avy#=TOKRB_GetAngularVelocityY#(RB)
 avz#=TOKRB_GetAngularVelocityZ#(RB)
 CrossProduct avx#,avy#,avz#,px#-TOKRB_GetX#(RB),py#-TOKRB_GetY#(RB),pz#-TOKRB_GetZ#(RB)
 CProd\x#=CProd\x#+vx#
 CProd\y#=CProd\y#+vy#
 CProd\z#=CProd\z#+vz#
End Function

Function MakeTokCollider(mesh)
 scount=CountSurfaces(mesh)
 For ind=1 To scount
  surface=GetSurface(mesh,ind)
  ttltris=ttltris+CountTriangles(surface)
  ttlvert=ttlvert+CountVertices(surface)
 Next
 vertices=CreateBank(16*ttlvert)
 triangles=CreateBank(24*ttltris)
 offsetv=0
 offsett=0
 For ind=1 To scount
  surface = GetSurface(mesh,ind)
  ctr=CountTriangles(surface)
  tric=tric+cvt
  cvt=CountVertices(surface)
  ;fill bank with vertices
  For v=0 To cvt-1
   PokeFloat vertices,offsetv,VertexX#(surface,v)
   PokeFloat vertices,offsetv+4,VertexY#(surface,v)
   PokeFloat vertices,offsetv+8,VertexZ#(surface,v)
   PokeFloat vertices,offsetv+12,0.0
   offsetv=offsetv+16
  Next
  ;fill bank with triangles
  For v=0 To ctr-1
   PokeInt triangles,offsett,tric+TriangleVertex(surface,v,0)
   PokeInt triangles,offsett+4,tric+TriangleVertex(surface,v,1)
   PokeInt triangles,offsett+8,tric+TriangleVertex(surface,v,2)
   PokeInt triangles,offsett+12,2	; Material ID
   PokeInt triangles,offsett+16,0
   PokeInt triangles,offsett+20,0
   offsett=offsett+24
  Next
 Next

 ;Hand over the terrain data to Tokamak
 TOKSIM_SetStaticMesh vertices,ttlvert,triangles,ttltris
 ; Now we can free the banks as Tokamak has copied all data
 FreeBank vertices
 FreeBank triangles
End Function

Function RemoveComponent(x1#,y1#,z1#,x2#,y2#,z2#)
 tmpdot# = DotProduct#(x1#,y1#,z1#,x2#,y2#,z2#)
 CProd\x#=x1#-x2#*tmpdot#
 CProd\y#=y1#-y2#*tmpdot#
 CProd\z#=z1#-z2#*tmpdot#
End Function

Function GripFactor#(Vel#)
	If Vel#>=0.0 And Vel<3.0 Then
	 GripFactor#=0.50
	EndIf
	If Vel#>=3.0 And Vel<6.0 Then
	 GripFactor#=0.95
	EndIf
	If Vel#>=6.0 And Vel<10.0 Then
	 GripFactor#=0.97
	EndIf
	If Vel#>=10.0 And Vel<15.0 Then
	 GripFactor#=0.98
	EndIf
	If Vel#>=15.0 Then
	 GripFactor#=0.99
	EndIf
	Return 0.999
	Return GripFactor#
End Function


Function UpdateCamera(Camera,Target,Rate#)

CamX#=EntityX#(Camera)
CamY#=EntityY#(Camera)
CamZ#=EntityZ#(Camera)
DiffX#=EntityX#(Target)-CamX#
DiffY#=EntityY#(Target)-CamY#
DiffZ#=EntityZ#(Target)-CamZ#
TranslateEntity Camera,DiffX#*Rate#,DiffY#*Rate#,DiffZ#*Rate#

End Function


Thanks, man! This will work great for a little WillysMB jeep.

If anyone wants to, you can tighten the suspension with this:
UpdateSpring 1.2,-0.5,-1.5,RR_Spr_RB,50,15.0,1.1
UpdateSpring -1.2,-0.5,-1.5,RL_Spr_RB,50,15.0,1.1
UpdateSpring 1.2,-0.5,1.65,FR_Spr_RB,50,15.0,1.1
UpdateSpring -1.2,-0.5,1.65,FL_Spr_RB,50,15.0,1.1

Thanks! A great way for me to learn tokamak and have sum fun messing up the code!

Great, thank you!
This just put tokamak up a notch for me and gave me a revelation about choosing physics engines.
Maybe I will put wheeled vehicles into my game after all...

edit: Cool! When I make the suspension insanely low, it slows down drastically when it hits the ground. For some reason I didn't at all expect that.

Wow, thanks, Sweenie! This is the most fun and easily tweakable car physics code I've played with in Blitz so far (not to put down any of the others)! Excellent fun when you make the acceleration limit 20 (even though your wheels can get knocked out of place)...

Hmm, yes. The wheels can get a bit misplaced if they get smashed into the ground too hard.
But that is easily fixed by setting limits on the slider joint that prevents the wheels from moving too far.

Can someone reupload the file with source of any car Physic in Tokamak ?
I search for it from a long time but every link is died :/

Here it is.

You'll find the source further up in this thread

In case anyone needs it here is the relevant Tokamak.decls wrapper (v0.71) which needs copying to the Blitz3D userlibs folder
Other versions available in the BLitz3D userlibs forum

.lib "tokamakwrapper.dll"

; *** Simulator ***
; If the values below isn't set before you create the simulator,
; default values will be used.  
TOKSIM_SetRigidBodiesCount(Count%):"_TOKSIM_SetRigidBodiesCount@4"
;Default = 50

TOKSIM_SetAnimatedBodiesCount(Count%):"_TOKSIM_SetAnimatedBodiesCount@4"
;Default = 50

TOKSIM_SetRigidParticleCount(Count%):"_TOKSIM_SetRigidParticleCount@4"
;Default = 50

TOKSIM_SetControllersCount(Count%):"_TOKSIM_SetControllersCount@4"
;Default = 50

TOKSIM_SetOverlappedPairsCount(Count%):"_TOKSIM_SetOverlappedPairsCount@4"
;Default = 1225 = (TBodies*(TBodies-1))/2 where TBodies is the total number
;of Rigid & Animated bodies

TOKSIM_SetGeometriesCount(Count%):"_TOKSIM_SetGeometriesCount@4"
;Default = 50

TOKSIM_SetConstraintsCount(Count%):"_TOKSIM_SetConstraintsCount@4"
;Default = 100

TOKSIM_SetConstraintSetsCount(Count%):"_TOKSIM_SetConstraintSetsCount@4"
;Default = 100

TOKSIM_SetConstraintBufferSize(Count%):"_TOKSIM_SetConstraintBufferSize@4"
;Default = 2000

TOKSIM_SetStaticMeshNodesStartCount(Count%):"_TOKSIM_SetStaticMeshNodesStartCount@4"
;Default = 200

TOKSIM_SetStaticMeshNodesGrowByCount(Count%):"_TOKSIM_SetStaticMeshNodesGrowByCount@4"
;Default = -1 = The nodecount will increase by 100% when growing.

TOKSIM_CreateSimulator%(GravityX#,GravityY#,GravityZ#):"_TOKSIM_CreateSimulator@12"
TOKSIM_DestroySimulator():"_TOKSIM_DestroySimulator@0"
TOKSIM_Advance(timestep#,SubSteps%):"_TOKSIM_Advance@8"
TOKSIM_Advance2(timestep#,minTimeStep#,maxTimeStep#):"_TOKSIM_Advance@12"
TOKSIM_GetPhysicsTime#():"_TOKSIM_GetPhysicsTime@0"
TOKSIM_SetStaticMesh(Vertices*,VertexCount%,Triangles*,TriangleCount%):"_TOKSIM_SetStaticMesh@16"
TOKSIM_FreeStaticMesh():"_TOKSIM_FreeStaticMesh@0"
TOKSIM_SetMaterial(Index%,Friction#,Restitution#):"_TOKSIM_SetMaterial@12"
TOKSIM_GetMaterialFriction#(Index%):"_TOKSIM_GetMaterialFriction@4"
TOKSIM_GetMaterialRestitution#(Index%):"_TOKSIM_GetMaterialRestitution@4"

TOKSIM_SetCInfoBank(CInfoBank*):"_TOKSIM_SetCInfoBank@4"
TOKSIM_SetCollisionResponse(CollisionID1%,CollisionID2%,Response%):"_TOKSIM_SetCollisionResponse@12"
TOKSIM_GetCollisionCount%():"_TOKSIM_GetCollisionCount@0"

TOKSIM_SetGravity(GravityX#,GravityY#,GravityZ#):"_TOKSIM_SetGravity@12"
TOKSIM_GetGravityX#():"_TOKSIM_GravityX@0"
TOKSIM_GetGravityY#():"_TOKSIM_GravityY@0"
TOKSIM_GetGravityZ#():"_TOKSIM_GravityZ@0"

; *** Rigid Body *** 
TOKRB_Create%():"_TOKRB_Create@0"
TOKRB_CreateParticle%():"_TOKRB_CreateParticle@0"
TOKRB_Free(RigidBody%):"_TOKRB_Free@4"
TOKRB_SetCollisionID(RigidBody%,ColID%):"_TOKRB_SetCollisionID@8"
TOKRB_GetCollisionID%(RigidBody%):"_TOKRB_GetCollisionID@4"
TOKRB_GetX#(RigidBody%):"_TOKRB_GetX@4"
TOKRB_GetY#(RigidBody%):"_TOKRB_GetY@4"
TOKRB_GetZ#(RigidBody%):"_TOKRB_GetZ@4"
TOKRB_SetPosition(RigidBody%,X#,Y#,Z#):"_TOKRB_SetPosition@16"
TOKRB_GetPitch#(RigidBody%):"_TOKRB_GetPitch@4"
TOKRB_GetYaw#(RigidBody%):"_TOKRB_GetYaw@4"
TOKRB_GetRoll#(RigidBody%):"_TOKRB_GetRoll@4"
TOKRB_SetRotation(RigidBody%,Pitch#,Yaw#,Roll#):"_TOKRB_SetRotation@16"
TOKRB_AddBox%(RigidBody%,Width#,Height#,Depth#):"_TOKRB_AddBox@16"
TOKRB_AddSphere%(RigidBody%,Diameter#):"_TOKRB_AddSphere@8"
TOKRB_AddCylinder%(RigidBody%,Diameter#,Height#):"_TOKRB_AddCylinder@12"
TOKRB_AddConvex%(RigidBody%,Bank*,BankSize%):"_TOKRB_AddConvex@12"
TOKRB_UpdateBoundingInfo(RigidBody%):"_TOKRB_UpdateBoundingInfo@4"
TOKRB_SetBoxInertiaTensor(RigidBody%,Width#,Height#,Depth#,Mass#):"_TOKRB_SetBoxInertiaTensor@20"
TOKRB_SetSphereInertiaTensor(RigidBody%,Diameter#,Mass#):"_TOKRB_SetSphereInertiaTensor@12"
TOKRB_SetCylinderInertiaTensor(RigidBody%,Diameter#,Height#,Mass#):"_TOKRB_SetCylinderInertiaTensor@16"
TOKRB_SetMass(RigidBody%,Mass#):"_TOKRB_SetMass@8"
TOKRB_SetLinearDamping(RigidBody%,Damping#):"_TOKRB_SetLinearDamping@8"
TOKRB_SetAngularDamping(RigidBody%,Damping#):"_TOKRB_SetAngularDamping@8"
TOKRB_IsIdle%(RigidBody%):"_TOKRB_IsIdle@4"
TOKRB_CollideConnected(RigidBody%,Connected%):"_TOKRB_CollideConnected@8"
;TOKRB_CollideDirectlyConnected(RigidBody%,Connected%):"_TOKRB_CollideDirectlyConnected@8"
TOKRB_SetSleepingParameter(RigidBody%,sleepparam#):"_TOKRB_SetSleepingParameter@8"
TOKRB_Active(RigidBody%,Active%):"_TOKRB_Active@8"
TOKRB_IsActive%(RigidBody%):"_TOKRB_IsActive@4"
TOKRB_GravityEnable(RigidBody%,Enable%):"_TOKRB_GravityEnable@8"
TOKRB_IsGravityEnabled%(RigidBody%):"_TOKRB_IsGravityEnabled@4"
TOKRB_ApplyImpulse(RigidBody%,X#,Y#,Z#):"_TOKRB_ApplyImpulse@16"
TOKRB_ApplyImpulse2(RigidBody%,X#,Y#,Z#,XPos#,YPos#,ZPos#):"_TOKRB_ApplyImpulse2@28"
TOKRB_ApplyTwist(RigidBody%,X#,Y#,Z#):"_TOKRB_ApplyTwist@16"
TOKRB_SetForce(RigidBody%,X#,Y#,Z#):"_TOKRB_SetForce@16"
TOKRB_SetForce2(RigidBody%,X#,Y#,Z#,XPos#,YPos#,ZPos#):"_TOKRB_SetForce2@28"
TOKRB_SetTorque(RigidBody%,X#,Y#,Z#):"_TOKRB_SetTorque@16"
TOKRB_SetVelocity(RigidBody%,X#,Y#,Z#):"_TOKRB_SetVelocity@16"
TOKRB_SetAngularMomentum(RigidBody%,X#,Y#,Z#):"_TOKRB_SetAngularMomentum@16"
TOKRB_GetVelocityX#(RigidBody%):"_TOKRB_GetVelocityX@4"
TOKRB_GetVelocityY#(RigidBody%):"_TOKRB_GetVelocityY@4"
TOKRB_GetVelocityZ#(RigidBody%):"_TOKRB_GetVelocityZ@4"
TOKRB_GetAngularMomentumX#(RigidBody%):"_TOKRB_GetAngularMomentumX@4"
TOKRB_GetAngularMomentumY#(RigidBody%):"_TOKRB_GetAngularMomentumY@4"
TOKRB_GetAngularMomentumZ#(RigidBody%):"_TOKRB_GetAngularMomentumZ@4"
TOKRB_GetAngularVelocityX#(RigidBody%):"_TOKRB_GetAngularVelocityX@4"
TOKRB_GetAngularVelocityY#(RigidBody%):"_TOKRB_GetAngularVelocityY@4"
TOKRB_GetAngularVelocityZ#(RigidBody%):"_TOKRB_GetAngularVelocityZ@4"
TOKRB_GetVelocityAtPointX#(RigidBody%,X#,Y#,Z#):"_TOKRB_GetVelocityAtPointX@16"
TOKRB_GetVelocityAtPointY#(RigidBody%,X#,Y#,Z#):"_TOKRB_GetVelocityAtPointY@16"
TOKRB_GetVelocityAtPointZ#(RigidBody%,X#,Y#,Z#):"_TOKRB_GetVelocityAtPointZ@16"
TOKRB_RemoveGeometry(RigidBody%,Geometry%):"_TOKRB_RemoveGeometry@8"
TOKRB_GetGeometryCount%(RigidBody%):"_TOKRB_GetGeometryCount@4"
TOKRB_BeginIterateGeometry(RigidBody%):"_TOKRB_BeginIterateGeometry@4"
TOKRB_GetNextGeometry%(RigidBody%):"_TOKRB_GetNextGeometry@4"

TOKRB_AddSensor%(RigidBody%,PosX#,PosY#,PosZ#,DirX#,DirY#,DirZ#):"_TOKRB_AddSensor@28"
TOKRB_BeginIterateSensor(RigidBody%):"_TOKRB_BeginIterateSensor@4"
TOKRB_GetNextSensor%(RigidBody%):"_TOKRB_GetNextSensor@4"
TOKRB_RemoveSensor(RigidBody%,Sensor%):"_TOKRB_RemoveSensor@8"
TOKRB_SetUserData(RigidBody%,UserData%):"_TOKRB_SetUserData@8"
TOKRB_GetUserData%(RigidBody%):"_TOKRB_GetUserData@4"

; *** Animated Body ***
TOKAB_Create%():"_TOKAB_Create@0"
TOKAB_Free(AnimatedBody%):"_TOKAB_Free@4"
TOKAB_SetCollisionID(AnimatedBody%,ColID%):"_TOKAB_SetCollisionID@8"
TOKAB_GetCollisionID%(AnimatedBody%):"_TOKAB_GetCollisionID@4"
TOKAB_SetPosition(AnimatedBody%,X#,Y#,Z#):"_TOKAB_SetPosition@16"
TOKAB_SetRotation(AnimatedBody%,Pitch#,Yaw#,Roll#):"_TOKAB_SetRotation@16"
TOKAB_GetX#(AnimatedBody%):"_TOKAB_GetX@4"
TOKAB_GetY#(AnimatedBody%):"_TOKAB_GetY@4"
TOKAB_GetZ#(AnimatedBody%):"_TOKAB_GetZ@4"
TOKAB_GetPitch#(AnimatedBody%):"_TOKAB_GetPitch@4"
TOKAB_GetYaw#(AnimatedBody%):"_TOKAB_GetYaw@4"
TOKAB_GetRoll#(AnimatedBody%):"_TOKAB_GetRoll@4"
TOKAB_AddBox%(AnimatedBody%,Width#,Height#,Depth#):"_TOKAB_AddBox@16"
TOKAB_AddSphere%(AnimatedBody%,Diameter#):"_TOKAB_AddSphere@8"
TOKAB_AddCylinder%(AnimatedBody%,Diameter#,Height#):"_TOKAB_AddCylinder@12"
TOKAB_AddConvex%(AnimatedBody%,Bank*,BankSize%):"_TOKAB_AddConvex@12"
TOKAB_UpdateBoundingInfo(AnimatedBody%):"_TOKAB_UpdateBoundingInfo@4"
TOKAB_RemoveGeometry(AnimatedBody%,Geometry%):"_TOKAB_RemoveGeometry@8"
TOKAB_GetGeometryCount%(AnimatedBody%):"_TOKAB_GetGeometryCount@4"
TOKAB_BeginIterateGeometry(AnimatedBody%):"_TOKAB_BeginIterateGeometry@4"
TOKAB_GetNextGeometry%(AnimatedBody%):"_TOKAB_GetNextGeometry@4"
TOKAB_CollideConnected(AnimatedBody%,Connected%):"_TOKAB_CollideConnected@8"
;TOKAB_CollideDirectlyConnected(AnimatedBody%,Connected%):"_TOKAB_CollideDirectlyConnected@8"
TOKAB_SetUserData(AnimatedBody%,UserData%):"_TOKAB_SetUserData@8"
TOKAB_GetUserData%(AnimatedBody%):"_TOKAB_GetUserData@4"

; *** Geometry ***
TOKGEOM_SetPositionAndRotation(Geometry%,X#,Y#,Z#,Pitch#,Yaw#,Roll#):"_TOKGEOM_SetPositionAndRotation@28"
TOKGEOM_SetMaterialIndex(Geometry%,Index%):"_TOKGEOM_SetMaterialIndex@8"
TOKGEOM_GetMaterialIndex%(Geometry%):"_TOKGEOM_GetMaterialIndex@4"
TOKGEOM_SetUserData(Geometry%,UserData%):"_TOKGEOM_SetUserData@8"
TOKGEOM_GetUserData%(Geometry%):"_TOKGEOM_GetUserData@4"

; *** Joint ***
TOKJOINT_Create%(ConnectionType%,BodyA%,BodyB%):"_TOKJOINT_Create@12"
TOKJOINT_Free%(Joint%):"_TOKJOINT_Free@4"
TOKJOINT_SetType(Joint%,JointType%):"_TOKJOINT_SetType@8"
TOKJOINT_SetPositionAndRotationWorld(Joint%,X#,Y#,Z#,Pitch#,Yaw#,Roll#):"_TOKJOINT_SetPositionWorld@28"
TOKJOINT_SetPositionAndRotationFrameA(Joint%,X#,Y#,Z#,Pitch#,Yaw#,Roll#):"_TOKJOINT_SetPositionFrameA@28"
TOKJOINT_SetPositionAndRotationFrameB(Joint%,X#,Y#,Z#,Pitch#,Yaw#,Roll#):"_TOKJOINT_SetPositionFrameB@28"
TOKJOINT_SetJointLength(Joint%,Length#):"_TOKJOINT_SetJointLength@8"
TOKJOINT_GetJointLength#(Joint%):"_TOKJOINT_GetJointLength@4"
TOKJOINT_Enable(Joint%,Enable):"_TOKJOINT_Enable@8"
TOKJOINT_IsEnabled%(Joint%):"_TOKJOINT_IsEnabled@4"
TOKJOINT_SetUpperLimit(Joint%,Limit#):"_TOKJOINT_SetUpperLimit@8"
TOKJOINT_SetLowerLimit(Joint%,Limit#):"_TOKJOINT_SetLowerLimit@8"
TOKJOINT_SetUpperLimit2(Joint%,Limit#):"_TOKJOINT_SetUpperLimit2@8"
TOKJOINT_SetLowerLimit2(Joint%,Limit#):"_TOKJOINT_SetLowerLimit2@8"
TOKJOINT_GetUpperLimit#(Joint%):"_TOKJOINT_GetUpperLimit@4"
TOKJOINT_GetLowerLimit#(Joint%):"_TOKJOINT_GetLowerLimit@4"
TOKJOINT_GetUpperLimit2#(Joint%):"_TOKJOINT_GetUpperLimit2@4"
TOKJOINT_GetLowerLimit2#(Joint%):"_TOKJOINT_GetLowerLimit2@4"
TOKJOINT_EnableLimit(Joint%,Enabled%):"_TOKJOINT_EnableLimit@8"
TOKJOINT_EnableLimit2(Joint%,Enabled%):"_TOKJOINT_EnableLimit2@8"
TOKJOINT_IsLimitEnabled%(Joint%):"_TOKJOINT_IsLimitEnabled@4"
TOKJOINT_IsLimit2Enabled%(Joint%):"_TOKJOINT_IsLimit2Enabled@4"
TOKJOINT_SetEpsilon(Joint%,Epsilon#):"_TOKJOINT_SetEpsilon@8"
TOKJOINT_SetIterations(Joint%,Iterations%):"_TOKJOINT_SetIterations@8"
TOKJOINT_GetEpsilon#(Joint%):"_TOKJOINT_GetEpsilon@4"
TOKJOINT_GetIterations%(Joint%):"_TOKJOINT_GetIterations@4"
TOKJOINT_GetFrameAX#(Joint%):"_TOKJOINT_GetFrameAX@4"
TOKJOINT_GetFrameAY#(Joint%):"_TOKJOINT_GetFrameAY@4"
TOKJOINT_GetFrameAZ#(Joint%):"_TOKJOINT_GetFrameAZ@4"
TOKJOINT_GetFrameBX#(Joint%):"_TOKJOINT_GetFrameBX@4"
TOKJOINT_GetFrameBY#(Joint%):"_TOKJOINT_GetFrameBY@4"
TOKJOINT_GetFrameBZ#(Joint%):"_TOKJOINT_GetFrameBZ@4"
TOKJOINT_GetFrameAPitch#(Joint%):"_TOKJOINT_GetFrameAPitch@4"
TOKJOINT_GetFrameAYaw#(Joint%):"_TOKJOINT_GetFrameAYaw@4"
TOKJOINT_GetFrameARoll#(Joint%):"_TOKJOINT_GetFrameARoll@4"
TOKJOINT_GetFrameBPitch#(Joint%):"_TOKJOINT_GetFrameBPitch@4"
TOKJOINT_GetFrameBYaw#(Joint%):"_TOKJOINT_GetFrameBYaw@4"
TOKJOINT_GetFrameBRoll#(Joint%):"_TOKJOINT_GetFrameBRoll@4"
TOKJOINT_SetDampingFactor(Joint%,Damping#):"_TOKJOINT_SetDampingFactor@8"
TOKJOINT_GetDampingFactor#(Joint%):"_TOKJOINT_GetDampingFactor@4"
TOKJOINT_EnableMotor(Joint%,Enabled%):"_TOKJOINT_EnableMotor@8"
TOKJOINT_IsMotorEnabled%(Joint%):"_TOKJOINT_IsMotorEnabled@4"
TOKJOINT_SetMotorSpeed(Joint%,DesiredForce#,MaxForce#):"_TOKJOINT_SetMotorSpeed@12"
TOKJOINT_EnableMotor2(Joint%,Enabled%):"_TOKJOINT_EnableMotor2@8"
TOKJOINT_IsMotor2Enabled%(Joint%):"_TOKJOINT_IsMotor2Enabled@4"
TOKJOINT_SetMotor2Speed(Joint%,DesiredForce#,MaxForce#):"_TOKJOINT_SetMotor2Speed@12"

; *** Sensor ***
TOKSENSOR_SetLineSensor(Sensor%,PosX#,PosY#,PosZ#,DirX#,DirY#,DirZ#):"_TOKSENSOR_SetLineSensor@28"
TOKSENSOR_GetLineVectorX#(Sensor%):"_TOKSENSOR_GetLineVectorX@4"
TOKSENSOR_GetLineVectorY#(Sensor%):"_TOKSENSOR_GetLineVectorY@4"
TOKSENSOR_GetLineVectorZ#(Sensor%):"_TOKSENSOR_GetLineVectorZ@4"
TOKSENSOR_GetLineUnitVectorX#(Sensor%):"_TOKSENSOR_GetLineUnitVectorX@4"
TOKSENSOR_GetLineUnitVectorY#(Sensor%):"_TOKSENSOR_GetLineUnitVectorY@4"
TOKSENSOR_GetLineUnitVectorZ#(Sensor%):"_TOKSENSOR_GetLineUnitVectorZ@4"
TOKSENSOR_GetLinePosX#(Sensor%):"_TOKSENSOR_GetLinePosX@4"
TOKSENSOR_GetLinePosY#(Sensor%):"_TOKSENSOR_GetLinePosY@4"
TOKSENSOR_GetLinePosZ#(Sensor%):"_TOKSENSOR_GetLinePosZ@4"
TOKSENSOR_GetDetectDepth#(Sensor%):"_TOKSENSOR_GetDetectDepth@4"
TOKSENSOR_GetDetectNormalX#(Sensor%):"_TOKSENSOR_GetDetectNormalX@4"
TOKSENSOR_GetDetectNormalY#(Sensor%):"_TOKSENSOR_GetDetectNormalY@4"
TOKSENSOR_GetDetectNormalZ#(Sensor%):"_TOKSENSOR_GetDetectNormalZ@4"
TOKSENSOR_GetDetectContactPointX#(Sensor%):"_TOKSENSOR_GetDetectContactPointX@4"
TOKSENSOR_GetDetectContactPointY#(Sensor%):"_TOKSENSOR_GetDetectContactPointY@4"
TOKSENSOR_GetDetectContactPointZ#(Sensor%):"_TOKSENSOR_GetDetectContactPointZ@4"
TOKSENSOR_GetDetectRigidBody%(Sensor%):"_TOKSENSOR_GetDetectRigidBody@4"
TOKSENSOR_GetDetectAnimatedBody%(Sensor%):"_TOKSENSOR_GetDetectAnimatedBody@4"
TOKSENSOR_GetDetectMaterial%(Sensor%):"_TOKSENSOR_GetDetectMaterial@4"
TOKSENSOR_SetUserData(Sensor%,UserData%):"_TOKSENSOR_SetUserData@8"
TOKSENSOR_GetUserData%(Sensor%):"_TOKSENSOR_GetUserData@4"


I'd forgotten about this, though it was still buried on my hard drive... it's great.