Strings / Ropes, Etc.
Blitz3D Forums/Blitz3D Programming/Strings / Ropes, Etc.
| ||
Hey All, I'm working on a rope / string simulation based on hugo elias's model for a string, found here... [link]http://freespace.virgin.net/hugo.elias/models/m_string.htm[/link] It seems to be working o.k., but there are a few things about it which i don't like, mainly the fact that it seems to continuously relax from gravity. It also is a little bit too flexible. Does anyone have any ideas for improvement, or other models? thanks, Roland Graphics3D 640,480,0,2 Global cam = CreateCamera() PositionEntity cam,0,10,-40 l=CreateLight() Type Node Field entity Field x# Field y# Field xspeed# Field yspeed# Field num Field newx# Field newy# Field dragging End Type Global gravity# = -.15 Global damping# = .9 Global length# = 2 Global endnode Global startnode Global mag1# Global mag2# Global extension1# Global extension2# Global xvector1# Global xvector2# Global yvector1# Global yvector2# Global xv# Global yv# Global picker = CreateCube() ScaleEntity picker,100,100,.2 EntityAlpha picker,.2 EntityPickMode picker,2 For x=0 To 31 n.Node = New Node n\entity = CreateSphere(3) ScaleEntity n\entity,.75,.75,.75 n\x = 0 n\y = Float(x) n\num = x PositionEntity n\entity,n\x,n\y,0 If x = 0 Then endnode = Handle(n) If x = 31 Then startnode = Handle(n) EntityPickMode n\entity,2 Next While Not KeyHit(1) updatenodes() If MouseHit(1) CameraPick cam,MouseX(),MouseY() For n.Node = Each Node If PickedEntity() = n\entity n\dragging = 1 EntityColor n\entity,255,0,0 Else n\dragging = 0 EntityColor n\entity,255,255,255 EndIf Next EndIf If MouseDown(1) CameraPick cam,MouseX(),MouseY() For n.Node = Each Node If n\dragging = 1 n\x = PickedX() n\y = PickedY() EndIf Next EndIf UpdateWorld RenderWorld n.Node = First Node Text 0,0,n\x Flip Wend End Function dist#(xlen#,ylen#) Return Sqr((xlen^2)+(ylen^2)) End Function Function updatenodes() For n.Node = Each Node hasno1 = 0 hasno2 = 0 For no.Node = Each Node If (no\num = n\num-1) xvector1 = no\x-n\x yvector1 = no\y-n\y mag1 = dist#(xvector1,yvector1) extension1 = mag1-length hasno1 = 1 Else If (no\num = n\num+1) xvector2 = no\x-n\x yvector2 = no\y-n\y mag2 = dist#(xvector2,yvector2) extension2 = mag2-length hasno2 = 1 EndIf Next If hasno1 <> 1 xvector1 = 0 yvector1 = 0 mag1 = 1 extension1 = 0 EndIf If hasno2 <> 1 xvector2 = 0 yvector2 = 0 mag2 = 1 extension2 = 0 EndIf xv = (xvector1/mag1*extension1)+(xvector2/mag2*extension2) yv = (yvector1/mag1*extension1)+(yvector2/mag2*extension2)+gravity n\xspeed = n\xspeed*damping+(xv*.07) n\yspeed = n\yspeed*damping+(yv*.07) n\newx = n\x+n\xspeed n\newy = n\y+n\yspeed If Handle(n) = startnode n\newx = 0 n\newy = 31 EndIf Next For n.Node = Each Node n\x = n\newx n\y = n\newy If Handle(n) = startnode n\x = 0 n\y = 31 EndIf PositionEntity n\entity,n\x,n\y,0 Next End Function |
| ||
neat. |
| ||
I haven't looked at your code at length but you're probably better using verlet integration for this kind of thing. Weibo of Thrust Extreme fame posted some code a while back which could prove useful. Basically, you assign specific weight to pointmasses which then determines their influence during the constraint phase. Found it ... |
| ||
Wow, that code is incredible! I'll have to see how I can apply it to what I'm doing, as it seems to be really fast and really stable. The only thing I don't see there is how to change the "springiness" of the constraints-- there doesn't seem to be a variable that relates to "K", or whatever it's called :) Here's a new piece of code I threw together based on a tutorial on NeHe.com... It seems to work really well for a rope simulation, but it's really slow. I can't seem to sort out how to do the timestep / iterations piece. So I just fudged some numbers in, but it's still really slow. Speeding it up seems to make it extremely unstable. Any thoughts on it? Thanks for your help! roland Graphics3D 640,480,0,2 Global cam = CreateCamera() PositionEntity cam,0,0,-5 l=CreateLight() Type node Field entity Field x# Field y# Field xspeed# Field yspeed# Field forcex# Field forcey# Field mass# Field length# Field dragging End Type Global gravity# = -9.81 Global springconstant# = -500 Global springfriction# = .2 Global airfriction# = .02 Global dt# Global maxpossible_dt# = .002 Global numiterations Global totalmasses = 30 Global timeelapsed = 0 Global length# = .2 Global picker = CreateCube() ScaleEntity picker,100,100,.1 PositionEntity picker,0,0,.1 EntityAlpha picker,.2 EntityPickMode picker,2 Global masses[30] For a=0 To totalmasses-1 n.node = New node n\length = .2 n\entity = CreateSphere(3) ScaleEntity n\entity,.1,.1,.1 n\x=Float(a)*n\length n\y =0 n\mass = .05 PositionEntity n\entity,n\x,n\y,0 masses[a] = Handle(n) EntityPickMode n\entity,2 Next Global startmillisecs# = MilliSecs() While Not KeyHit(1) If MouseHit(1) CameraPick cam,MouseX(),MouseY() For n.node = Each node If n\entity = PickedEntity() n\dragging = 1 EntityColor n\entity,255,0,0 Else n\dragging = 0 EntityColor n\entity,255,255,255 EndIf Next EndIf If MouseDown(1) CameraPick cam,MouseX(),MouseY() For n.node = Each node If n\dragging = 1 n\x = PickedX() n\y = PickedY() EndIf Next EndIf dt = (MilliSecs()-startmillisecs)/1000 timeelapsed = timeelapsed+dt numiterations = (dt/maxpossible_dt)+1 If (numiterations <> 0) dt = dt/numiterations EndIf dt = .006 For i= 0 To 300 For n.node = Each node n\forcex = 0 n\forcey = 0 Next For a=0 To totalmasses-2 forcex# = 0 forcey# = 0 n.node = Object.node(masses[a]) n1.node = Object.node(masses[a+1]) xdist# = n1\x-n\x ydist# = n1\y-n\y r# = dist(xdist,ydist) If r<> 0 forcex# = forcex#+(-(xdist/r)*(r-length)*springconstant) forcey# = forcey#+(-(ydist/r)*(r-length)*springconstant) forcex# = forcex#+(-(n\xspeed-n1\xspeed)*springfriction) forcey# = forcey#+(-(n\yspeed-n1\yspeed)*springfriction) n\forcex = n\forcex+forcex n\forcey = n\forcey+forcey n1\forcex = n1\forcex-forcex n1\forcey = n1\forcey-forcey EndIf Next For n.node = Each node n\forcey = n\forcey+(gravity*n\mass) n\forcex = n\forcex+(-n\mass*airfriction) n\forcey = n\forcey+(-n\mass*airfriction) n\xspeed = (n\forcex/n\mass)*dt n\yspeed = (n\forcey/n\mass)*dt n\x = n\x+(n\xspeed*dt) n\y = n\y+(n\yspeed*dt) If n\y <-2.5 Then n\y = -2.5 PositionEntity n\entity,n\x,n\y,0 Next n.node = Object.node(masses[0]) n\xspeed = 0 n\yspeed = 0 n\x = 0 n\y = 0 PositionEntity n\entity,n\x,n\y,0 Next UpdateWorld RenderWorld Flip False Wend Function dist#(xlen#,ylen#) Return Sqr((xlen^2)+(ylen^2)) End Function |
| ||
Stevie: That's a nice bit of code you posted by Wiebo there - thanks for that. I shall have to study it and see what I can learn about this verlet lark. :) |
| ||
I can't try the code above as at work but you can change the springiness of the constraint in the above example by diff# by a number less than 1. SPRINGINESS# = .5 diff# = ( deltalength - RESTLENGTH ) / (deltalength * ( -p\mass + -p\nxt\mass ) ) * SPRINGINESS# Also, the weight of the pointmasses causes elasticity automatically. Basically the heavier the pointmass the more time it should take to constrain. I would recommend a constant TIMESTEP of something like .05 for verlet integration. Changing the TIMESTEP every frame will cause major stability problems and IMO it's better that it runs smoothly at a lower FPS on a lower spec machine. In my vehicle engine I need only 3 iterations of the constraint phase for complete stability but I have a few tricks up my sleeve on this one ;) Also, you don't need your dist function .. just use entity distance. Hope this helps. Stevie |
| ||
Thanks again, Stevie... that works great for making it more springy... I was hoping to make it less springy, but i'm guessing that has to do with the number of iterations during the constraint phase, right? hee hee. This is definately a really cool little app here. I'll have to spend some time trying to adapt it to my 3d rope and see what I can come up with. Thanks again for your help! cheers, roland |
| ||
ok... here's a 3d version with a ground and simple ground collisions / friction. Not sure if this is the best way to do it, but it seems to work ok!! thanks again, roland ; quick thrust game, by Wiebo de Wit ; Using verlet integration for movement and stick constraints for the ball and chain =] ; green line is your ship. I'm sorry but i didn't have the strength to include my new ; vector object engine. a and s to rotate, r-shift to thrust Graphics3D 640,480,0,2 SetBuffer BackBuffer() Global cam = CreateCamera() PositionEntity cam,0,10,-25 l=CreateLight() Const TIMESTEP# = 0.05 Const GRAVITYANGLE = 0 ; 0 = straight down, 90 = right, etc Const GRAVITYFORCE# = -2000 Const DRAG# = 0.99 Const THRUST# = 100 Const NUM_PARTICLES = 15 Const RESTLENGTH# = 10 Const GROUNDFRICTION# = .2 Type Particle Field x#, y# Field old_x#, old_y# Field a_x#, a_y# Field mass# Field nxt.particle Field entity Field dragging End Type Type Player Field angle# Field thrust# Field point.particle End Type Global player.player, p.particle Global picker = CreateCube() ScaleEntity picker,100,100,.1 EntityAlpha picker,.2 EntityPickMode picker,2 Global ground = CreateCube() PositionEntity ground,0,-5,0 ScaleEntity ground,50,.5,50 EntityType ground,1 Collisions 2,1,2,2 ; create player player.player = New player ; player is a particle too, but you can control it player\point = New particle p.particle = player\point p\x = 0 : p\y = 0 p\old_x = 0 : p\old_y = 0 p\a_x = 0 : p\a_y = 0 p\entity = CreateCone() ScaleEntity p\entity,.5,.5,.5 EntityColor p\entity,255,255,0 ; make ship heavy p\mass =50 For count = 1 To NUM_PARTICLES p\nxt = New particle p = p\nxt p\x = 0 : p\y = 0 p\old_x = Rand(0,0) p\old_y = Rand(0,0) p\a_x = 0 : p\a_y = 0 p\mass = 60 p\entity = CreateSphere(3) ScaleEntity p\entity,.4,.4,.4 EntityColor p\entity,255,255,255 Next ; last particle is ball. make heavy p\mass = 50 For p.particle = Each particle EntityPickMode p\entity,2 EntityType p\entity,2 Next ; loop While KeyHit( 1 ) = False If MouseHit(1) CameraPick cam,MouseX(),MouseY() For p.particle = Each particle If p\entity = PickedEntity() p\dragging = 1 EntityColor p\entity,255,0,0 Else p\dragging = 0 EntityColor p\entity,255,255,255 EndIf Next EndIf If MouseDown(1) CameraPick cam,MouseX(),MouseY() For p.particle = Each particle If p\dragging = 1 p\x = PickedX()*20 p\y = PickedY()*20 EndIf Next EndIf GetInput() ApplyPhysics() Verlet() For x=0 To 4 ConstrainParticlesMass() Next UpdateWorld RenderWorld p.particle = First particle Text 0,0,p\y Flip Cls Wend ; ------------------------------------------------------------ Function GetInput() If KeyDown( 203 ) ;rotate player\angle = player\angle + 5 EndIf If KeyDown( 205 ) ;rotate player\angle = player\angle - 5 EndIf p.particle = player\point RotateEntity p\entity,0,0,player\angle If KeyDown( 200 ) ;thrust, adjust player particle acceleration p.particle = player\point p\a_x = p\a_x - Sin( player\angle ) * THRUST p\a_y = p\a_y + Cos( player\angle ) * THRUST EndIf End Function Function ApplyPhysics() ; apply gravity to particles For p.particle = Each particle If EntityCollided(p\entity,1) EntityColor p\entity,0,255,0 p\a_x = p\a_x + Sin( GRAVITYANGLE ) * GRAVITYFORCE * GROUNDFRICTION / p\mass Else EntityColor p\entity,255,255,255 p\a_x = p\a_x + Sin( GRAVITYANGLE ) * GRAVITYFORCE / p\mass p\a_y = p\a_y + Cos( GRAVITYANGLE ) * GRAVITYFORCE / p\mass EndIf Next End Function Function ConstrainParticlesMass() p.particle = player\point While p\nxt <> Null dx# = p\nxt\x - p\x dy# = p\nxt\y - p\y deltalength# = Sqr( dx*dx + dy*dy ) diff# = ( deltalength - RESTLENGTH ) / (deltalength * ( -p\mass + -p\nxt\mass ) ) p\x = p\x + -p\mass * dx * diff p\y = p\y + -p\mass * dy * diff p = p\nxt p\x = p\x - -p\mass * dx * diff p\y = p\y - -p\mass * dy * diff Wend For p.particle = Each particle PositionEntity p\entity,p\x*.05,p\y*.05,0 Next End Function Function Verlet() ; eg: move particles, introducing drag For p.particle = Each particle x# = p\x y# = p\y tempx# = p\x tempy# = p\y oldx# = p\old_x oldy# = p\old_y If EntityCollided(p\entity,1) p\x = p\x + (DRAG * x - DRAG * oldx + p\a_x * TIMESTEP * TIMESTEP)*GROUNDFRICTION Else p\x = p\x + DRAG * x - DRAG * oldx + p\a_x * TIMESTEP * TIMESTEP EndIf p\y = p\y + DRAG * y - DRAG * oldy + p\a_y * TIMESTEP * TIMESTEP p\old_x = tempx p\old_y = tempy If EntityCollided(p\entity,1) p\y = p\old_y EndIf ; reset acceleration after moving particle p\a_x = 0 : p\a_y = 0 Next End Function |