Disclaimer: this code is not even close to finished. Dozens of things need to fixed/improved. I decided to put it up anyways because it will be weeks before I have the time to finish it. I works well enough to demonstrate how verlet physics can be applied to a real world object.
;work in progress!!!!! ;its not optimized, friction is not completed, and about a dozen other things arent finished. ;just putting this out there because its going to be weeks before i can finish these things. Graphics3D HEIGHT,WIDTH,16,2 Const FPS=30 Const HEIGHT=640 ; graphics width Const WIDTH=480 ; graphics height Const ACTIVE=1 ; collision type Const VERLET=2 ; collision Type Const FRONT_TIRE=0 ; Vobject part name Const REAR_TIRE=1 ; Vobject part name Const BODY=2 ; Vobject part name Const CENTER=3 ; Vobject part name Const SIZE#=8 ; just a multiplier for how big the Vobject will be Const ITERATIONS = 2 ; How many loops through the "relaxation" routine, Lower = speed up / lose accuracy Global DeltaTime# ; Time between physics calculations Global vGrav.Vector = Vector(0.0,9,0.0) ; Gravity. yes i know its a positive value. still dont know why this is but it works. Global vTemp1.Vector = Vector(0,0,0) ; Some temporary vectors for calculations Global vTemp2.Vector = Vector(0,0,0) Global world_friction#=0 ; air resistance Global temp_turn#=0 Global traction#=0 Global roll# front_stiff=20 ; This is actually the mass value for the front wheel. lower mass = less stiff. caution will collapse rear_stiff=30 ; This is actually the mass value for the rear wheel. lower mass = less stiff. caution will collapse Type Vector Field x# Field y# Field z# End Type Type VerletPack Field start_loc.vector ; Defines where this verlet started at so we can fix it when it gets to far out of place Field current.Vector ; Current position Field old.Vector ; Last position Field gravity.Vector ; Accumulated forces Field radius# ; Collider radius Field mass.vector ; Point mass of this particular verlet Field entity ; Pivot assigned to this verlet for both position and collision Field mesh ; Mesh assigned to this verlet if any such as wheels or body parts Field part ; Describes what verlet represents. example wheel or body part. Field state ; Collideable status true/false Field contact_friction# ; When in contact with the ground how much friction is applied End Type Type Constraint ; Two verlets connected by a mutual spring Field v1.VerletPack ; First verlet Field v2.VerletPack ; Second verlet Field d# ; Distance between verlets End Type Type Vobject ; This is the parent object that the verlets are associated with Field v.VerletPack[8] ; The array of individual verlets Field num_verlets ; Total number of verlets (array dim value) Field player_pivot ; Pivot used to represent player. this pivot is dynamically controlled by player. Field player ; Mesh used to represent the player. this mesh is parented to player_pivot Field center.VerletPack ; Used to position player_pivot but not neccessarily parented to. Field x1.VerletPack ; x1 and x2 are used to define a vector left/right to align player_pivot Field x2.VerletPack Field z1.VerletPack ; z1 and z2 are used to define a vector forward/aft to align player_pivot Field z2.VerletPack Field X_vec# ; vectors for calculations Field Y_vec# Field Z_vec# Field mass# ; Mass for physics calculation on entire Vobject Field horsepower# ; Force for physics calculations on entire Vobject Field acceleration# ; Rate at which the Vobject accelerates Field velocity# ; Velocity of entire Vobject Field distance# ; Distance Vobject has travelled Field position# ; Position is the distance accumulator End Type ;***** make the vehicle **************************************** tempx# = 0 ;start location of vehicle tempy# = 20 ;start location of vehicle tempz# = 150 ;start location of vehicle Global car.Vobject = New Vobject ;initialize the Vobject type car\player_pivot=CreatePivot() ;this is the master pivot that the player will contol car\player=CreateCube(car\player_pivot) ;mesh that is assigned to player_pivot. in this case a simple cube for a car body ScaleEntity car\player, 15,6,30 ;get car body into postion and scaled etc EntityColor car\player, 255,0,0 PositionEntity car\player, 0, 2, -6 car\num_verlets = 8 ;total number of verlets in the Vobject. count from zero car\mass=10 ;this mass value is used for physics calculations on player_pivot. car\horsepower = 200 ;the force exerted on player_pivot sx# = 1.9 ;just a multiplier for the Vobject size sy# = 1.2 ;just a multiplier for the Vobject size sz# = 2.65 ;just a multiplier for the Vobject size ;parameters x,y,z,radius, mass, part,mesh,state,collision_type, contact_friction a.VerletPack = Verlet(tempx - SIZE * sx, tempy - SIZE * sy, tempz - SIZE * sz, 7, front_stiff, FRONT_TIRE, CreateCylinder(12), True, VERLET, 0) b.VerletPack = Verlet(tempx + SIZE * sx, tempy - SIZE * sy, tempz - SIZE * sz, 7, front_stiff, FRONT_TIRE, CreateCylinder(12), True, VERLET, 0) c.VerletPack = Verlet(tempx + SIZE * sx, tempy + SIZE * sy, tempz - SIZE * sz, 7, 400.0, BODY, 0, True, VERLET, 0) d.VerletPack = Verlet(tempx - SIZE * sx, tempy + SIZE * sy, tempz - SIZE * sz, 7, 400.0, BODY, 0, True, VERLET, 0) e.VerletPack = Verlet(tempx - SIZE * sx, tempy - SIZE * sy, tempz + SIZE * sz, 9, rear_stiff, REAR_TIRE, CreateCylinder(12), True, VERLET, 0) f.VerletPack = Verlet(tempx + SIZE * sx, tempy - SIZE * sy, tempz + SIZE * sz, 9, rear_stiff, REAR_TIRE, CreateCylinder(12), True, VERLET, 0) g.VerletPack = Verlet(tempx + SIZE * sx, tempy + SIZE * sy, tempz + SIZE * sz, 7, 400.0, BODY, 0, True, VERLET, 0) h.VerletPack = Verlet(tempx - SIZE * sx, tempy + SIZE * sy, tempz + SIZE * sz, 7, 400.0, BODY, 0, True, VERLET, 0) i.VerletPack = Verlet(tempx, tempy + 5, tempz + 8, 7, 400.0, CENTER, 0, False, VERLET, 0) ; Edges Constraint2(a,b) Constraint2(b,c) Constraint2(a,d) Constraint2(c,d) Constraint2(e,f) Constraint2(f,g) Constraint2(e,h) Constraint2(g,h) Constraint2(a,e) Constraint2(b,f) Constraint2(c,g) Constraint2(d,h) ; Cross-faces Constraint2(a,c) Constraint2(b,d) Constraint2(e,g) Constraint2(f,h) Constraint2(a,f) Constraint2(a,h) Constraint2(b,e) Constraint2(b,g) Constraint2(c,f) Constraint2(c,h) Constraint2(d,e) Constraint2(d,g) ; Diagonals Constraint2(a,g) Constraint2(b,h) Constraint2(c,e) Constraint2(d,f) ; Constraining the center sphere Constraint2(a,i) Constraint2(b,i) Constraint2(c,i) Constraint2(d,i) Constraint2(e,i) Constraint2(f,i) Constraint2(g,i) Constraint2(h,i) ; Assign individual verlets to the array car\v[0] = i car\v[1] = a car\v[2] = b car\v[3] = c car\v[4] = d car\v[5] = e car\v[6] = f car\v[7] = g car\v[8] = h ; assign a verlet the center position car\center=i ; Set up variables used for player mesh/pivot aligntovector. one is left to right, other is forward back. car\x1=c car\x2=d car\z1=g car\z2=c PositionEntity car\player_pivot, EntityX(car\center\entity),EntityY(car\center\entity),EntityZ(car\center\entity) ;Next ;*********************************************************************************************** measure_pivots(car.Vobject, 0) ;ClearTextureFilters ;SetBuffer BackBuffer() Collisions VERLET,ACTIVE,2,2 MoveMouse WIDTH/2,HEIGHT/2 lgt = CreateLight() LightColor lgt,300,300,300 RotateEntity lgt,35,0,0 AmbientLight 94,94,80 Global cam = CreateCamera() CameraFogMode cam,0 CameraFogColor cam,128,128,180 CameraClsColor cam,128,128,180 CameraRange cam,1,4000 CameraFogRange cam,500,3000 CameraZoom cam,1 bump=CreateCylinder(4) ScaleEntity bump, 5,600,20 RotateEntity bump,0,0,90 EntityType bump, ACTIVE EntityColor bump,0,255,0 PositionEntity bump,0,0,-500 PositionEntity CopyEntity(bump),0,0,-600 PositionEntity CopyEntity(bump),0,0,-700 PositionEntity CopyEntity(bump),0,0,-800 PositionEntity CopyEntity(bump),0,0,-900 jump=CreateCube() ScaleEntity jump,200,100,100 RotateEntity jump,15,0,0 PositionEntity jump,0,-70,-600 EntityType jump,ACTIVE landing=CreateCube() ScaleEntity landing,200,100,100 RotateEntity landing,15,180,0 PositionEntity landing,0,-70,-1000 EntityType landing,ACTIVE grid_tex=CreateTexture( 256,256,1+4+8 ) SetBuffer TextureBuffer( grid_tex ) ScaleTexture grid_tex,500,500 Color 255,255,255:Rect 0,0,256,256,False grid_tex2=CreateTexture( 32,32,1+8 ) SetBuffer TextureBuffer( grid_tex2 ) ScaleTexture grid_tex2,50,50 Color 0,0,255:Rect 0,0,32,32,False SetBuffer BackBuffer() flr=CreatePlane() EntityType flr,ACTIVE EntityTexture flr,grid_tex,0,1 EntityTexture flr,grid_tex2,0,0 TextureBlend grid_tex,3 EntityBlend flr,1 EntityAlpha flr,.6 EntityFX flr,1 PositionEntity flr,0,0,0 ScaleEntity car\v[1]\mesh,car\v[1]\radius,car\v[1]\radius/2,car\v[1]\radius RotateEntity car\v[1]\mesh,0,0,90 PositionEntity car\v[1]\mesh,0,0,0 ScaleEntity car\v[2]\mesh,car\v[2]\radius,car\v[2]\radius/2,car\v[2]\radius RotateEntity car\v[2]\mesh,0,0,90 PositionEntity car\v[2]\mesh,0,0,0 ScaleEntity car\v[5]\mesh,car\v[5]\radius,car\v[5]\radius/2,car\v[5]\radius RotateEntity car\v[5]\mesh,0,0,90 PositionEntity car\v[5]\mesh,0,0,0 ScaleEntity car\v[6]\mesh,car\v[6]\radius,car\v[6]\radius/2,car\v[6]\radius RotateEntity car\v[6]\mesh,0,0,90 PositionEntity car\v[6]\mesh,0,0,0 ;********* functions defined ******************************************** ; measure the start locations for each verlet so it can be fixed if it collapses Function measure_pivots(name.Vobject, center_element) For count=1 To name\num_verlets If name\v[count]\part <> CENTER Then EntityParent name\v[count]\entity, name\v[center_element]\entity name\v[count]\start_loc\x=EntityX(name\v[count]\entity) name\v[count]\start_loc\y=EntityY(name\v[count]\entity) name\v[count]\start_loc\z=EntityZ(name\v[count]\entity) EntityParent name\v[count]\entity, 0 Else name\v[count]\start_loc\x=0 name\v[count]\start_loc\y=0 name\v[count]\start_loc\z=0 End If Next End Function ; use this function to fix a collapsed Vobject Function fix_verlet(name.Vobject) For count=1 To name\num_verlets EntityParent name\v[count]\entity, name\center\entity tempx# = name\v[count]\start_loc\x + name\center\current\x tempy# = name\v[count]\start_loc\y + name\center\current\y tempz# = name\v[count]\start_loc\z + name\center\current\z PositionEntity name\v[count]\entity, tempx, tempy, tempz name\v[count]\current\x = tempx name\v[count]\current\y = tempy name\v[count]\current\z = tempz name\v[count]\old\x = tempx name\v[count]\old\y = tempy name\v[count]\old\z = tempz EntityParent name\v[count]\entity, 0 Next End Function Global crash Function DoVerlet(name.Vobject, accelerator) For count = 0 To name\num_verlets If (name\v[count]\part = REAR_TIRE Or name\v[count]\part = FRONT_TIRE) And CountCollisions(name\v[count]\entity) >0 TFormVector vgrav\x, vgrav\y, vgrav\z, 0, name\player_pivot tempvax# = TFormedX() tempvay# = TFormedY() tempvaz# = TFormedZ() TFormVector name\v[count]\current\x, name\v[count]\current\y, name\v[count]\current\z, 0, name\player_pivot temptempx#= TFormedX() temptempy#= TFormedY() temptempz#= TFormedZ() tempvmx# = TFormedX() tempvmy# = TFormedY() tempvmz# = TFormedZ() TFormVector name\v[count]\old\x, name\v[count]\old\y, name\v[count]\old\z, 0, name\player_pivot tempvoldx# = TFormedX() tempvoldy# = TFormedY() tempvoldz# = TFormedZ() TFormVector EntityX(name\v[count]\entity,True), EntityY(name\v[count]\entity,True), EntityZ(name\v[count]\entity,True), 0, name\player_pivot tempentityx# = TFormedX() tempentityy# = TFormedY() tempentityz# = TFormedZ() ;tempvmx is purposefully not computed so the wheels only travel up and down, and forward/back tempvmy = tempvmy - (tempvoldy + (tempvay * (DeltaTime * DeltaTime))) + temptempy tempvmz = tempvmz - (tempvoldz + (tempvaz * (DeltaTime * DeltaTime))) + temptempz tempvoldx = temptempx tempvoldy = temptempy tempvoldz = temptempz TFormVector tempvmx, tempvmy, tempvmz, name\player_pivot, 0 name\v[count]\current\x = TFormedX() name\v[count]\current\y = TFormedY() name\v[count]\current\z = TFormedZ() TFormVector tempvoldx, tempvoldy, tempvoldz, name\player_pivot, 0 name\v[count]\old\x = TFormedX() name\v[count]\old\y = TFormedY() name\v[count]\old\z = TFormedZ() Else name\v[count]\gravity\x = vgrav\x name\v[count]\gravity\y = vgrav\y name\v[count]\gravity\z = vgrav\z tempx# = name\v[count]\current\x tempy# = name\v[count]\current\y tempz# = name\v[count]\current\z name\v[count]\current\x = name\v[count]\current\x - (name\v[count]\old\x + (name\v[count]\gravity\x * (DeltaTime * DeltaTime))) + tempx name\v[count]\current\y = name\v[count]\current\y - (name\v[count]\old\y + (name\v[count]\gravity\y * (DeltaTime * DeltaTime))) + tempy name\v[count]\current\z = name\v[count]\current\z - (name\v[count]\old\z + (name\v[count]\gravity\z * (DeltaTime * DeltaTime))) + tempz name\v[count]\old\x = tempx name\v[count]\old\y = tempy name\v[count]\old\z = tempz End If If name\v[count]\part = REAR_TIRE Or name\v[count]\part = FRONT_TIRE Or name\v[count]\part = BODY Then PositionEntity name\v[count]\entity, name\v[count]\current\x, name\v[count]\current\y, name\v[count]\current\z EntityParent name\v[count]\entity, name\player_pivot name\acceleration = (-accelerator * name\horsepower * traction) / name\mass name\velocity = (name\velocity - (name\velocity * world_friction)) + (DeltaTime# * name\acceleration) name\distance = DeltaTime# * DeltaTime# * name\acceleration * .5 * traction name\position=name\position + name\distance MoveEntity name\player_pivot, 0, 0, name\distance EntityParent name\v[count]\entity, 0 If name\v[count]\part = FRONT_TIRE PositionEntity name\v[count]\entity, name\v[count]\current\x, name\v[count]\current\y, name\v[count]\current\z EntityParent name\v[count]\entity, name\player_pivot TurnEntity name\player_pivot, 0, temp_turn, 0 EntityParent name\v[count]\entity, 0 End If Else PositionEntity name\v[count]\entity, name\v[count]\current\x, name\v[count]\current\y, name\v[count]\current\z End If Next UpdateWorld crash=0 For count = 0 To name\num_verlets If (name\v[count]\part = FRONT_TIRE Or REAR_TIRE) And Abs(Name\v[count]\current\y - EntityY(name\v[count]\Entity)) >9 Then crash=crash+1 Next If crash >= 1 Then For count = 0 To name\num_verlets name\v[count]\current\y=name\v[count]\current\y + 5 name\v[count]\old\y=name\v[count]\current\y PositionEntity name\v[count]\entity, name\v[count]\current\x, name\v[count]\current\y, name\v[count]\current\z Next UpdateWorld End If turn_rate = 0 traction = 0 For count = 0 To name\num_verlets If CountCollisions(name\v[count]\entity) And name\v[count]\part = FRONT_TIRE Then turn_rate = turn_rate + .7 traction = traction + .25 End If If CountCollisions(name\v[count]\entity) And name\v[count]\part = REAR_TIRE Then traction = traction + .25 End If name\v[count]\current\x = EntityX(name\v[count]\entity) name\v[count]\current\y = EntityY(name\v[count]\entity) name\v[count]\current\z = EntityZ(name\v[count]\entity) Next For n = 1 To ITERATIONS For c.Constraint = Each Constraint setDistance(c\v1, c\v2, c\d) c\v1\old\x = c\v1\old\x + (c\v1\current\x - c\v1\old\x) * (c\v1\contact_friction * DeltaTime) c\v1\old\y = c\v1\old\y + (c\v1\current\y - c\v1\old\y) * (c\v1\contact_friction * DeltaTime) c\v1\old\z = c\v1\old\z + (c\v1\current\z - c\v1\old\z) * (c\v1\contact_friction * DeltaTime) Next Next ;body and wheel stuff PositionEntity name\player_pivot, EntityX(name\center\entity), EntityY(name\center\entity), EntityZ(name\center\entity) name\X_vec = EntityX(name\x1\entity) - EntityX(name\x2\entity) name\Y_vec = EntityY(name\x1\entity) - EntityY(name\x2\entity) name\Z_vec = EntityZ(name\x1\entity) - EntityZ(name\x2\entity) AlignToVector name\player_pivot, name\X_vec, name\Y_vec, name\Z_vec, 1 For count=0 To name\num_verlets If name\v[count]\part = FRONT_TIRE Or name\v[count]\part = REAR_TIRE Then AlignToVector name\v[count]\entity, name\X_vec, name\Y_vec, name\Z_vec, 1 End If Next name\X_vec = EntityX(name\z1\entity) - EntityX(name\z2\entity) name\Y_vec = EntityY(name\z1\entity) - EntityY(name\z2\entity) name\Z_vec = EntityZ(name\z1\entity) - EntityZ(name\z2\entity) AlignToVector name\player_pivot, name\X_vec, name\Y_vec, name\Z_vec, 3 For count=0 To name\num_verlets If name\v[count]\part = FRONT_TIRE Or name\v[count]\part = REAR_TIRE Then AlignToVector name\v[1]\entity, name\X_vec, name\Y_vec, name\Z_vec, 3 End If Next ;wheel spin isnt working right. blitz internal bug? TFormVector name\v[0]\current\x - name\v[0]\old\x, name\v[0]\current\y - name\v[0]\old\y, name\v[0]\current\z - name\v[0]\old\z, name\v[0]\entity, name\player_pivot roll=roll+TFormedZ() ;TurnEntity name\v[1]\entity, roll * radius, 0, 0, False TurnEntity name\v[1]\entity, TFormedZ() * name\v[1]\radius, 0, 0, False TurnEntity name\v[2]\entity, TFormedZ() * name\v[2]\radius, 0, 0, False TurnEntity name\v[5]\entity, TFormedZ() * name\v[5]\radius, 0, 0, False TurnEntity name\v[6]\entity, TFormedZ() * name\v[6]\radius, 0, 0, False End Function Function SetDistance(v1.VerletPack,v2.VerletPack,dist#) vtemp1\x=v1\current\x - v2\current\x vtemp1\y=v1\current\y - v2\current\y vtemp1\z=v1\current\z - v2\current\z deltalength# = Sqr((vTemp1\x * vTemp1\x) + (vTemp1\y * vTemp1\y) + (vTemp1\z * vTemp1\z)) If deltalength <= 0.0 deltalength = 0.0000001 diff# = (deltalength - dist) / deltalength txmass# = v1\mass\x + v2\mass\x tymass# = v1\mass\y + v2\mass\y tzmass# = v1\mass\z + v2\mass\z v1\current\x=v1\current\x - vtemp1\x * (diff * (v2\mass\x / txmass)); * (diff+1.2) v1\current\y=v1\current\y - vtemp1\y * (diff * (v2\mass\y / tymass)) * (diff+1) v1\current\z=v1\current\z - vtemp1\z * (diff * (v2\mass\z / tzmass)); * (diff+1.2) v2\current\x=v2\current\x + vtemp1\x *(diff * (v1\mass\x / txmass)); / (diff+1.2) v2\current\y=v2\current\y + vtemp1\y *(diff * (v1\mass\y / tymass)) / (diff+1) v2\current\z=v2\current\z + vtemp1\z *(diff * (v1\mass\z / tzmass)); / (diff+1.2) End Function Function Verlet.VerletPack(x#,y#,z#,radius#, mass#, part,mesh,state,collision_type, contact_friction#) v.VerletPack = New VerletPack v\current = Vector(x,y,z) ;current location v\old = Vector(x,y,z) ;old location v\start_loc = Vector(0,0,0) ; initalize with zeros to be filled later v\part=part ;part identifier such as wheel or body If radius <= 0.0 radius = 0.01 v\radius = radius ;collision radius If mass <= 0.0 mass = 0.01 If v\part= FRONT_TIRE Or v\part= REAR_TIRE Then v\mass = Vector(100,mass,100) Else v\mass = Vector(400,400,400) ;v\mass\y = mass ;point mass v\entity = CreatePivot() ;used as the collision element PositionEntity v\entity,x,y,z,True v\mesh=mesh ;mesh assigned if any If mesh<>0 Then EntityParent v\mesh, v\entity,True v\state=state ;true for collideable, false for non collideable If v\state=True Then EntityType v\entity, collision_type EntityRadius v\entity,radius# End If v\contact_friction=contact_friction# v\gravity = Vector(0.0,0.0,0.0) Return v End Function Function Constraint2.Constraint(v1.VerletPack,v2.VerletPack) c.Constraint = New Constraint c\v1 = v1 c\v2 = v2 c\d = Sqr((v2\current\x - v1\current\x)^2 + (v2\current\y - v1\current\y)^2 + (v2\current\z - v1\current\z)^2) ;Return c ; You may need this someday ... we don't for what we have here End Function ;// Create a Vector Function Vector.Vector(x#=0.0,y#=0.0,z#=0.0) v.Vector = New Vector v\x=x v\y=y v\z=z Return v End Function ;display directions While Not KeyHit(57) RenderWorld Text 20,20, "Tab changes view from mouse look modes to front/side views." Text 20,35, "Arrow keys control vehicle. Text 20,50, "Escape exits program." Text 20,65, "Space bar resets vehicle" Text 20,80, "Hit space bar to begin." Flip False Wend ;temp_Vobject2.Vobject = First Vobject Global turn_rate#=0 DeltaTime#=.09 HidePointer fRate = (1000/72) ;*set second number to desired frame rate ;**************** main loop *************************************** While Not KeyHit(1) time1 = MilliSecs() ;time at start of processing loop temp_turn#=0 accelerator=0 If KeyDown(203) temp_turn=turn_rate If KeyDown(205) temp_turn=-turn_rate If KeyDown(200) accelerator=1 If KeyDown(208) accelerator=-1 If KeyDown(57) Then vGrav\y=0 fix_verlet(car.Vobject) Else vGrav.Vector = Vector(0.0,9,0.0) End If DoVerlet(car.Vobject, accelerator) If KeyHit(15) Then view=view+1 If view >=3 Then view=0 End If PositionEntity cam,EntityX(car\center\entity),EntityY(car\center\entity),EntityZ(car\center\entity) tempmousex#=tempmousex-MouseXSpeed() tempmousey#=tempmousey+MouseYSpeed() Select view Case 0 RotateEntity cam, tempmousey+10 , tempmousex+180, 0 Case 1 RotateEntity cam,tempmousey , EntityYaw(car\player_pivot,True)+90, 0 Case 2 RotateEntity cam,tempmousey , EntityYaw(car\player_pivot,True)+180, 0 End Select MoveEntity cam,0,0,-90 MoveMouse WIDTH/2,HEIGHT/2 RenderWorld Delay fRate - (MilliSecs() - time1) ;delay execution Flip False Wend End


