Soft body simulation toy ^^

Post your FreeBASIC tips and tricks here. Please don’t post your code without including an explanation.
h4tt3n
Posts: 691
Joined: Oct 22, 2005 21:12
Location: Denmark

Soft body simulation toy ^^

Postby h4tt3n » Mar 12, 2008 22:24

Here's a little thing I stirred up. Soft body simulation with lots of interaction possibilities.

It is based on the following paper. The code examples in the paper are heavily un-optimized, so I fixed it. There's also an error in the volume calculation, so I replaced that with a standard polygon area algorithm.

Have fun!

http://panoramix.ift.uni.wroc.pl/~maq/s ... ftbody.pdf

Cheers Michael

Code: Select all

'******************************************************************************'
'
'   Soft body simulation. Michael "h4tt3n" Nissen, march 2008
'   Press left mouse to pick up body and drag it around.
'   Press Right mouse to prod soft body around with ball obstacle.
'   Use left / right to decrease / incresase body pressure
'   Use up / down to increase / decrease body sircumference
'   (Press esc to quit)
'
'******************************************************************************'

''  set constants. experiment with these and see how the simulation reacts
Const Pi                =     4*Atn(1)  ''  pi (better not change ^^)
Const dt                =     0.01      ''  timestep, delta time
Const Prod_Radius       =     50
Const Num_Masses        =     64        ''  number of masses in rope
Const Num_Obstacles     =     4
Const Point_Mass        =     1         ''  mass of each point mass
Const Ks                =     8000      ''  spring stiffnes
Const Kld               =     6         ''  linear spring damping
Const Kad               =     8         ''  angular spring damping (beta)
Const Kps               =     10        ''  mouse-body pick up spring
Const Kpd               =     0.1       ''  mouse-body spring damping
Const Grav_Acc          =     800       ''  gravitational acceleration
Const Wall_Friction     =     0.85      ''  friction force on wall contact
Const Pick_mindist      =     25        ''  pick up mass within distance

Dim As Single Ball_Radius     = 150        ''  ideal radius of soft body
Dim As Single Rest_Len        = (Ball_Radius*2*Pi)/Num_Masses
Dim As Single P               =     5000000   ''  pressure constant

Randomize Timer

''  define types
Type Vector_Type
  As Single X, Y
End Type

Type Mass_Type
  As Single Mass
  As Vector_Type Frc, Acc, Vel, Pos
End Type

Type Spring_Type
  As Integer a, b
  As Single Length
  As Vector_Type normal, Lng_Hat, Vel
End Type

Type Obstacle_Type
  As Vector_Type Pos
  As Integer Radius
End Type

Dim As Vector_Type Dist, center_of_mass
Dim As Mass_Type Mass(1 to Num_Masses)
Dim As Spring_Type Spring(1 To Num_Masses)
Dim As Obstacle_Type Obstacle(1 To Num_Obstacles)
Dim As Single Force, temp_dist, distance, Damping
Dim As Integer i, j, x, y, Scrn_Wid, Scrn_Hgt, Area, Pressure, mouse_x, mouse_y, mouse_btn, pick_state, picked

Scrn_Wid = 800
Scrn_Hgt = 600
ScreenRes Scrn_Wid, Scrn_hgt, 16

For i = Lbound(Mass) To Ubound(Mass)
  With Mass(i)
    .Mass = Point_Mass
    .pos.X = scrn_wid\2 + Ball_Radius*Cos(2*Pi*(i/Num_Masses))
    .pos.Y = scrn_hgt\3 + Ball_Radius*Sin(2*Pi*(i/Num_Masses))
  End With
  With Spring(i)
    j = i+1
    If j > Num_Masses Then j -= Num_Masses
    .a = i
    .b = j
  End With
Next

For i = Lbound(Obstacle) To Ubound(Obstacle)
  With Obstacle(i)
    .Radius = ((Scrn_Wid/Num_Obstacles)/3)
    .Radius -= .Radius*Rnd*0.1
    .Pos.X = i*(Scrn_Wid/(Num_Obstacles+1))
    .Pos.Y = Scrn_Hgt-(.Radius+10+(Rnd*50))
  End With
Next

Do
 
  Area = 0
  Center_Of_Mass.x = 0
  Center_Of_Mass.y = 0
 
  ''  up arrow
  If Multikey(&h48) Then
    Ball_Radius += 0.1
    Rest_Len = (Ball_Radius*2*Pi)/Num_Masses
  End If
  ''  down arrow
  If Multikey(&h50) Then
    Ball_Radius -= 0.1
    Rest_Len = (Ball_Radius*2*Pi)/Num_Masses
  End If
  ''  left arrow
  If Multikey(&h4b) Then
    P /= 1.001
  End If
  ''  Right arrow
  If Multikey(&h4d) Then
    P *= 1.001
  End If
 
  Getmouse Mouse_X, Mouse_Y,, Mouse_Btn
 
  ''  pick up point mass on leftmouse
  If Mouse_Btn = 1 Then
   
    If Pick_State = 0 Then
     
      Temp_Dist = Pick_MinDist
     
      For i = Lbound(mass) To Ubound(mass)
       
        Dist.X = mass(i).Pos.X-Mouse_X
        Dist.Y = mass(i).Pos.Y-Mouse_Y
        Distance = Sqr(Dist.X*Dist.X+Dist.Y*Dist.Y)
       
        If Distance < Temp_Dist Then
         
          Temp_Dist = Distance
          Picked = i
         
        End If
       
      Next
     
      If Picked <> -1 Then
       
        Pick_State = 1
       
      End If
     
    Else
     
      With Mass(Picked)
       
        Dist.X = .Pos.X-Mouse_X
        Dist.Y = .Pos.Y-Mouse_Y
        Distance = Sqr(Dist.X*Dist.X+Dist.Y*Dist.Y)
       
        Force = -Kps*Distance-Kpd*((Dist.X*.Vel.X+Dist.Y*.Vel.Y)/Distance)
       
        Mass(Picked).Frc.X += Force*Dist.X
        Mass(Picked).Frc.Y += Force*Dist.Y
       
      End With
     
    End If
   
  Else
   
    Pick_State = 0
    Picked = -1
   
  End If
 
  ''  prod body around on right mouse
  If Mouse_Btn = 2 Then
   
    Pick_State = 0
    Picked = -1
   
    For i = Lbound(mass) To Ubound(mass)
     
      Dist.X = mass(i).Pos.X-Mouse_X
      Dist.Y = mass(i).Pos.Y-Mouse_Y
      Distance = Sqr(Dist.X*Dist.X+Dist.Y*Dist.Y)
     
      If Distance < Prod_Radius Then
       
        With Mass(i)
         
          Force = -200*(Distance-Prod_Radius)-0.5*((Dist.X*.Vel.X+Dist.Y*.Vel.Y)/Distance)
         
          .Frc.X += Force*Dist.X
          .Frc.Y += Force*Dist.Y
         
          .Vel.X *= Wall_Friction
          .Vel.Y *= Wall_Friction
         
        End With
       
      End If
     
    Next
   
  End If
 
  ''  calculate spring force, area, and center of mass
  For i = Lbound(Spring) To Ubound(Spring)
   
    With Spring(i)
     
      Dist.X = Mass(.b).Pos.X-Mass(.a).Pos.X
      Dist.Y = Mass(.b).Pos.Y-Mass(.a).Pos.Y
      .Length = Sqr(Dist.X*Dist.X+Dist.Y*Dist.Y)
     
      .Lng_Hat.X = Dist.X/.Length
      .Lng_Hat.Y = Dist.Y/.Length
     
      Area += Mass(.a).Pos.X*Mass(.b).Pos.Y-Mass(.b).Pos.X*Mass(.a).Pos.Y
     
      Center_Of_Mass.X += (Mass(.a).Pos.X+Mass(.b).Pos.X)*(Mass(.a).Pos.X*Mass(.b).Pos.Y-Mass(.b).Pos.X*Mass(.a).Pos.Y)
      Center_Of_Mass.Y += (Mass(.a).Pos.Y+Mass(.b).Pos.Y)*(Mass(.a).Pos.X*Mass(.b).Pos.Y-Mass(.b).Pos.X*Mass(.a).Pos.Y)
     
      .Vel.X = Mass(.b).Vel.X-Mass(.a).Vel.X
      .Vel.Y = Mass(.b).Vel.Y-Mass(.a).Vel.Y
     
      Force = -Ks*(.Length-Rest_Len)-Kld*(.Lng_Hat.X*.Vel.X+.Lng_Hat.Y*.Vel.Y)
     
      Mass(.a).Frc.X -= Force*.Lng_Hat.X
      Mass(.a).Frc.Y -= Force*.Lng_Hat.Y
      Mass(.b).Frc.X += Force*.Lng_Hat.X
      Mass(.b).Frc.Y += Force*.Lng_Hat.Y
     
    End With
   
  Next
 
  ''  finish calculation of area, pressire and COM
  Area *= 0.5
  Pressure = P/Area
  Center_Of_Mass.X *= 1/(6*Area)
  Center_Of_Mass.Y *= 1/(6*Area)
 
  ''  calculate pressure force
  For i = Lbound(Spring) To Ubound(Spring)
   
    With Spring(i)
     
      Force = .Length*Pressure
      Damping = -Kad*(.Lng_Hat.X*.Vel.Y-.Lng_Hat.Y*.Vel.X)
     
      Mass(.a).Frc.X += (Force+Damping)*.Lng_Hat.Y
      Mass(.a).Frc.Y += (Force+Damping)*-.Lng_Hat.X
      Mass(.b).Frc.X += (Force-Damping)*.Lng_Hat.Y
      Mass(.b).Frc.Y += (Force-Damping)*-.Lng_Hat.X
     
    End With
   
  Next
 
  ''  body-obstacle collision
  For i = Lbound(mass) To Ubound(mass)
   
    For j = Lbound(Obstacle) To Ubound(Obstacle)
     
      Dist.X = mass(i).Pos.X-Obstacle(j).Pos.X
      Dist.Y = mass(i).Pos.Y-Obstacle(j).Pos.Y
      Distance = Sqr(Dist.X*Dist.X+Dist.Y*Dist.Y)
     
      If Distance < Obstacle(j).Radius Then
       
        With Mass(i)
         
          Force = -200*(Distance-Obstacle(j).Radius)-0.2*((Dist.X*.Vel.X+Dist.Y*.Vel.Y)/Distance)
         
          .Frc.X += Force*Dist.X
          .Frc.Y += Force*Dist.Y
         
          .Vel.X *= Wall_Friction
          .Vel.Y *= Wall_Friction
         
        End With
       
      End If
     
    Next
   
  Next

  ''  integrate
  For i = Lbound(Mass) To Ubound(Mass)
   
    With Mass(i)
     
      .Acc.X = .Frc.X/.mass
      .Acc.Y = .Frc.Y/.mass
     
      .Acc.Y += Grav_Acc
     
      .Vel.X += .Acc.X*dt
      .Vel.Y += .Acc.Y*dt
     
      .Pos.X += .Vel.X*dt
      .Pos.Y += .Vel.Y*dt
     
      .frc.x = 0
      .frc.y = 0
     
    End With
   
  Next
 
  ''  screen boundaries
  For i = Lbound(Mass) To Ubound(Mass)
   
    With Mass(i)
     
      ''  right
      If .Pos.x > Scrn_Wid-1 Then
        .Vel.X = -.vel.x
        .pos.x = Scrn_Wid-1
        .Vel.X *= wall_friction
        .Vel.Y *= wall_friction
      End If
      ''  left
      If .Pos.x < 1 Then
        .Vel.X = -.vel.x
        .pos.x = 1
        .Vel.X *= wall_friction
        .Vel.Y *= wall_friction
      End If
      ''  bottom
      If .Pos.Y > Scrn_Hgt-1 Then
        .vel.y = -.vel.y
        .pos.y = Scrn_Hgt-1
        .Vel.X *= wall_friction
        .Vel.Y *= wall_friction
      End If
      ''  top
      If .Pos.Y < 1 Then
        .vel.y = -.vel.y
        .pos.y = 1
        .Vel.X *= wall_friction
        .Vel.Y *= wall_friction
      End If
     
    End With
   
  Next
 
  ''  render to screen
  ScreenLock
   
    Cls
   
    For i = Lbound(Spring) To Ubound(Spring)
     
      With Spring(i)
       
        Line _
          (Mass(.a).Pos.X, Mass(.a).Pos.Y)-_
          (Mass(.b).Pos.X, Mass(.b).Pos.Y), _
          Rgb(64, 255, 64)
         
      End With
     
    Next
   
    For i = Lbound(Obstacle) To Ubound(Obstacle)
     
      With Obstacle(i)
       
        Circle(.Pos.X, .Pos.Y), .Radius, RGB(255, 64, 64),,,1
       
      End With
     
    Next
   
    Line _
      (Center_Of_Mass.x, Center_Of_Mass.Y)-_
      (Mass(1).Pos.X, Mass(1).Pos.Y), _
      Rgb(32, 128, 32)
   
    If Pick_State = 1 Then
     
      Line _
      (Mouse_x, Mouse_Y)-_
      (Mass(Picked).Pos.X, Mass(Picked).Pos.Y), _
      Rgb(255, 64, 64)
     
    End If
   
    If Mouse_Btn = 2 Then
     
      Circle(Mouse_X, Mouse_Y), Prod_Radius, RGB(255, 64, 64),,,1
     
    End If
   
    Locate 2, 1: Print Using " Pressure coeff. : ########.##"; P
    Locate 3, 1: Print Using " Ball Radius     : ########.##"; Ball_Radius
   
  ScreenUnlock
 
  Sleep 1, 1
 
Loop Until Multikey(1)

End


And here's a stripped down version with just the physics:

Code: Select all

'******************************************************************************'
'
'   Soft body simulation. Michael "h4tt3n" Nissen, march 2008
'
'   (Press esc to quit)
'
'******************************************************************************'

''  set constants. experiment with these and see how the simulation reacts
Const Pi                =     4*Atn(1)  ''  pi (better not change ^^)
Const dt                =     0.01      ''  timestep, delta time
Const Num_Masses        =     32        ''  number of masses in body
Const Point_Mass        =     1         ''  mass of each point mass
Const Ks                =     8000      ''  spring stiffnes
Const Kld               =     6         ''  linear spring damping
Const Grav_Acc          =     800       ''  gravitational acceleration
Const Wall_Friction     =     0.85      ''  friction force on wall contact
Const Ball_Radius       =     150       ''  ideal radius of soft body
Const Rest_Len          =     (Ball_Radius*2*Pi)/Num_Masses ''spring rest length
Const P                 =     5000000   ''  pressure constant

''  define types
Type Vector_Type
  As Single X, Y
End Type

Type Mass_Type
  As Single Mass
  As Vector_Type Frc, Acc, Vel, Pos
End Type

Type Spring_Type
  As Integer a, b
  As Single Length
  As Vector_Type normal, Lng_Hat, Vel
End Type

Dim As Vector_Type Dist
Dim As Mass_Type Mass(1 to Num_Masses)
Dim As Spring_Type Spring(1 To Num_Masses)
Dim As Single Force
Dim As Integer i, j, Scrn_Wid, Scrn_Hgt, Area, Pressure

Scrn_Wid = 800
Scrn_Hgt = 600
ScreenRes Scrn_Wid, Scrn_hgt, 16

For i = Lbound(Mass) To Ubound(Mass)
  With Mass(i)
    .Mass = Point_Mass
    .pos.X = scrn_wid\2 + Ball_Radius*Cos(2*Pi*(i/Num_Masses))
    .pos.Y = scrn_hgt\3 + Ball_Radius*Sin(2*Pi*(i/Num_Masses))
  End With
  With Spring(i)
    j = i+1
    If j > Num_Masses Then j -= Num_Masses
    .a = i
    .b = j
  End With
Next

Do
 
  ''  reset area
  Area = 0
 
  ''  spring force and damping, area of soft body
  For i = Lbound(Spring) To Ubound(Spring)
   
    With Spring(i)
     
      Dist.X = Mass(.b).Pos.X-Mass(.a).Pos.X
      Dist.Y = Mass(.b).Pos.Y-Mass(.a).Pos.Y
      .Length = Sqr(Dist.X*Dist.X+Dist.Y*Dist.Y)
     
      ''  unit normal vector
      .Lng_Hat.X = Dist.X/.Length
      .Lng_Hat.Y = Dist.Y/.Length
     
      Area += Mass(.a).Pos.X*Mass(.b).Pos.Y-Mass(.b).Pos.X*Mass(.a).Pos.Y
     
      .Vel.X = Mass(.b).Vel.X-Mass(.a).Vel.X
      .Vel.Y = Mass(.b).Vel.Y-Mass(.a).Vel.Y
     
      Force = -Ks*(.Length-Rest_Len)-Kld*(.Lng_Hat.X*.Vel.X+.Lng_Hat.Y*.Vel.Y)
     
      Mass(.a).Frc.X -= Force*.Lng_Hat.X
      Mass(.a).Frc.Y -= Force*.Lng_Hat.Y
      Mass(.b).Frc.X += Force*.Lng_Hat.X
      Mass(.b).Frc.Y += Force*.Lng_Hat.Y
     
    End With
   
  Next
 
  ''  finish area of soft body, pressure
  Area *= 0.5
  Pressure = P/Area
 
  ''  pressure force normal to springs
  For i = Lbound(Spring) To Ubound(Spring)
   
    With Spring(i)
     
      Force = .Length*Pressure
     
      Mass(.a).Frc.X += Force*.Lng_Hat.Y
      Mass(.a).Frc.Y += Force*-.Lng_Hat.X
      Mass(.b).Frc.X += Force*.Lng_Hat.Y
      Mass(.b).Frc.Y += Force*-.Lng_Hat.X
     
    End With
   
  Next

  ''  integrate
  For i = Lbound(Mass) To Ubound(Mass)
   
    With Mass(i)
     
      .Acc.X = .Frc.X/.mass
      .Acc.Y = .Frc.Y/.mass
     
      .Acc.Y += Grav_Acc
     
      .Vel.X += .Acc.X*dt
      .Vel.Y += .Acc.Y*dt
     
      .Pos.X += .Vel.X*dt
      .Pos.Y += .Vel.Y*dt
     
      .frc.x = 0
      .frc.y = 0
     
    End With
   
  Next
 
  ''  screen boundaries
  For i = Lbound(Mass) To Ubound(Mass)
   
    With Mass(i)
     
      ''  right
      If .Pos.x > Scrn_Wid-1 Then
        .Vel.X = -.vel.x
        .pos.x = Scrn_Wid-1
        .Vel.X *= wall_friction
        .Vel.Y *= wall_friction
      End If
      ''  left
      If .Pos.x < 1 Then
        .Vel.X = -.vel.x
        .pos.x = 1
        .Vel.X *= wall_friction
        .Vel.Y *= wall_friction
      End If
      ''  bottom
      If .Pos.Y > Scrn_Hgt-1 Then
        .vel.y = -.vel.y
        .pos.y = Scrn_Hgt-1
        .Vel.X *= wall_friction
        .Vel.Y *= wall_friction
      End If
      ''  top
      If .Pos.Y < 1 Then
        .vel.y = -.vel.y
        .pos.y = 1
        .Vel.X *= wall_friction
        .Vel.Y *= wall_friction
      End If
     
    End With
   
  Next
 
  ''  draw to screen
  ScreenLock
   
    Cls
   
    For i = Lbound(Spring) To Ubound(Spring)
     
      With Spring(i)
       
        Line _
          (Mass(.a).Pos.X, Mass(.a).Pos.Y)-_
          (Mass(.b).Pos.X, Mass(.b).Pos.Y), _
          Rgb(64, 255, 64)
         
      End With
     
    Next
   
  ScreenUnlock
 
  Sleep 1, 1
 
Loop Until Multikey(1)

End

Last edited by h4tt3n on Mar 12, 2008 23:47, edited 2 times in total.
Hezad
Posts: 469
Joined: Dec 17, 2006 23:37
Contact:

Postby Hezad » Mar 12, 2008 22:39

Rah that's awesome !

Really funny toy :p
DaveUnit
Posts: 239
Joined: Apr 20, 2006 15:47
Location: Central MA
Contact:

Postby DaveUnit » Mar 12, 2008 23:06

that's f*cking awesome.
KristopherWindsor
Posts: 2428
Joined: Jul 19, 2006 19:17
Location: Sunnyvale, CA
Contact:

Postby KristopherWindsor » Mar 12, 2008 23:39

Nice! :-)

I managed to subdue the blob:
Image

:-P
h4tt3n
Posts: 691
Joined: Oct 22, 2005 21:12
Location: Denmark

Postby h4tt3n » Mar 12, 2008 23:45

Ah well, there's still no self-collision or anything. Once I get that added you won't be able to do that. And then you can have several blobs crawling on top of each other ^^

Maybe a cool game could be based on this. Like Gish... Or like a really weird pong game :/ ... catch the bogeyman... 8-S

Anyway, If you're intereted I've posted a stripped down example with just the necessary physics in the ip.
Lachie Dazdarian
Posts: 2338
Joined: May 31, 2005 9:59
Location: Croatia
Contact:

Postby Lachie Dazdarian » Mar 12, 2008 23:56

Yeah, this is really cool.

If I only knew how to exploit this code.
h4tt3n
Posts: 691
Joined: Oct 22, 2005 21:12
Location: Denmark

Postby h4tt3n » Mar 13, 2008 0:21

Hey, if you don't understand my crappy code, just ask. The theory behind all this is fairly simple, it just looks a tad messy in code form.
integer
Posts: 386
Joined: Feb 01, 2007 16:54
Location: usa

Postby integer » Mar 13, 2008 0:26

...this is really cool.


...that's awesome !


that's f*cking awesome.


ditto
Dr_D
Posts: 2392
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » Mar 13, 2008 0:41

Nice. ;)
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Postby anonymous1337 » Mar 13, 2008 0:46

Kill me for thinking naughty thoughts when I read the thread title as well as when I read "ditto".

;_;... Anyways awesome program :D
Lachie Dazdarian
Posts: 2338
Joined: May 31, 2005 9:59
Location: Croatia
Contact:

Postby Lachie Dazdarian » Mar 13, 2008 22:22

Yeah....soft...bodey. :P

Anyway, it's not that I don't understand your code h4tt3n. It just, I have no idea how to make something of it...like, a playable game.
DaveUnit
Posts: 239
Joined: Apr 20, 2006 15:47
Location: Central MA
Contact:

Postby DaveUnit » Mar 13, 2008 23:37

If you could find a way to use it, you could make a game similar to Loco Roco.
http://en.wikipedia.org/wiki/LocoRoco
h4tt3n
Posts: 691
Joined: Oct 22, 2005 21:12
Location: Denmark

Postby h4tt3n » Mar 14, 2008 0:10

anonymous1337 you... you... ewww! >X-C

Anyway check out this site for physics based games

http://www.fun-motion.com

especially the game Gish, which has a blob main character a bit like the one in my demo.
Merick
Posts: 1038
Joined: May 28, 2007 1:52

Postby Merick » Mar 14, 2008 0:11

indigo
Posts: 2
Joined: Mar 10, 2008 11:00
Location: Greece

Postby indigo » Mar 17, 2008 9:54

This is very fun to watch! Good job h4tt3n!!! ;)

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 1 guest