top of page
Search
cedarcantab

Softbody Physics [WIP]

Updated: Apr 10



function CreateBall(x, y, radius, res) {

var offset = 0;

for (var i = 0; i < Math.PI 2; i += Math.PI 2 / res) {

var cx = x + Math.cos(i) * radius;

var cy = y + Math.sin(i) * radius;

var point = new Point(cx, cy, 1);

points.push(point);

offset++;

}

var start = points.length - offset;

for (var i = start; i < points.length; i++) {

var p1 = points[i];

var p2 = points[i + 1] || points[start];

var spring = new Spring(p1, p2, Stiffness, Damping);

springs.push(spring);

}

}


Point Object


class Point {

  constructor(x, y, mass) {
    
    this.x = x;
    this.y = y;
    this.vx = 0;
    this.vy = 0;
    this.mass = mass;
  }

  update(dt) {

    this.vx += gravity.x * dt;
    this.vy += gravity.y * dt;
    
    var minY = -5;
    var C = minY - this.y;
    if (C > 0) {
      var impulse = getConstraintImpulse([0, -1], [this.vx, this.vy], [this.mass, this.mass], C, 0.3, dt);
      this.vx += impulse[0] / this.mass;
      this.vy += impulse[1] / this.mass;
    }
    
    this.x += this.vx * dt;
    this.y += this.vy * dt;
  }
  
  render() {

    graphics.fillStyle('0xff0000');
  
    graphics.fillCircle(width / 2 + this.x * 25, height / 2 - this.y * 25, 3)
  }

}

Spring Object



class Spring {

  constructor(a, b, stiffness, damping, len) {

    if (!a || !b) throw "A or B is null";
    
    this.a = a;
    this.b = b;
    this.stiffness = stiffness;
    this.damping = damping;
    this.len = len || Distance.getDistance(this.a.x, this.a.y, this.b.x, this.b.y); // natural length
  
  }

  update(dt) {

    var distance = Distance.getDistance(this.a.x, this.a.y, this.b.x, this.b.y);
    var error = distance - this.len;
    var dirVector = Vec2.normalizeVector({x: this.a.x - this.b.x, y: this.a.y - this.b.y });
    
    var relativeVelocity = { x: this.a.vx - this.b.vx, y: this.a.vy - this.b.vy}

    var dampingScalar = Vec2.dot(relativeVelocity, dirVector) * this.damping;
    var dampingForce = { x: dirVector.x * dampingScalar, y: dirVector.y * dampingScalar };
    
    this.a.vx += -(dirVector.x * error * this.stiffness + dampingForce.x) * 1;
    this.a.vy += -(dirVector.y * error * this.stiffness + dampingForce.y) * 1;
    this.b.vx += (dirVector.x * error * this.stiffness + dampingForce.x) * 1;
    this.b.vy += (dirVector.y * error * this.stiffness + dampingForce.y) * 1;
    
  }

}

Constraint functions



function getConstraintImpulse(jacobian, velocities, masses, C, biasFactor, h) {

  var bias = biasFactor / h * C;

  var effectiveMass = getEffectiveMass(jacobian, masses);
  var lambda = getLambda(effectiveMass, jacobian, velocities, bias);

  var output = [];
  for (var i = 0; i < jacobian.length; i++) {
    output[i] = jacobian[i] * lambda;
  }
  return output;

}

function getLambda(effectiveMass, jacobian, velocities, bias) {

  var sum = 0;
  for (var i = 0; i < jacobian.length; i++) {
    sum += jacobian[i] * velocities[i];
  }
  return -effectiveMass * (sum + bias);

}

function getEffectiveMass(jacobian, masses) {

  var sum = 0;
  for (var i = 0; i < jacobian.length; i++) {
    sum += (jacobian[i] / masses[i]) * jacobian[i];
  }
  return 1 / sum;
  
}


Main Loop



function update(t, dt) {

  graphics.clear();
  
  for (var c = 0; c < 1; c += timeStep) {

    // calculate "Volume"
    var volume = 0;
    for (var i = 0; i < springs.length; i++) {
      var s = springs[i];
      var normal = Vec2.normalizeVector({x: s.b.y - s.a.y, y: s.a.x - s.b.x});
      volume += 0.5 * Math.abs(s.a.x - s.b.x) * Math.abs(normal.x) * Distance.getDistance(s.a.x, s.a.y, s.b.x, s.b.y);
    }
    
    for (var i = 0; i < springs.length; i++) {
      var s = springs[i];
      s.update(1 / 60 * timeStep);
    }
    
    for (var i = 0; i < springs.length; i++) {
      var s = springs[i];
      
      var pressure = Distance.getDistance(s.a.x, s.a.y, s.b.x, s.b.y) * Pressure * (1 / volume);
      var normal = Vec2.normalizeVector({x: s.b.y - s.a.y, y: s.a.x - s.b.x});
  
      var relativeVelocity = {x: s.a.vx - s.b.vx, y: s.a.vy - s.b.vy}
      var dampingScalar = Vec2.dot(relativeVelocity, normal) * 0;
      var dampingForce = {x: normal.x * dampingScalar, y: normal.y * dampingScalar };
     
      s.a.vx += normal.x * pressure + dampingForce.x;
      s.a.vy += normal.y * pressure + dampingForce.y;
      s.b.vx += normal.x * pressure + dampingForce.x;
      s.b.vy += normal.y * pressure + dampingForce.y;
    }
    
    for (var i = 0; i < points.length; i++) {
      var p = points[i];
      p.update(1 / 60 * timeStep);
    }
  }
  
  for (var i = 0; i < points.length; i++) {
    var p = points[i];
    p.render();
  }
  
}



var gc = new GameCanvas();

var gravity = {x: 0, y: -9.81};

var timeStep = 0.1;

var points = [];

var springs = [];

var Pressure = 0.2;

var PressureDamping = 0.1;

var Damping = 0.2;

var Stiffness = 1;

CreateBall(0, 0, 1, 30);

loop();

function loop() {

clearScreen();

for (var c = 0; c < 1; c += timeStep) {

var volume = 0;

for (var i = 0; i < springs.length; i++) {

var s = springs[i];

var normal = normalizeVector({

x: s.a.x - s.b.x,

y: s.a.y - s.b.y

});

normal = {x: -normal.y, y: normal.x};

volume += 0.5 Math.abs(s.a.x - s.b.x) Math.abs(normal.x) * getDistance(s.a.x, s.a.y, s.b.x, s.b.y);

}

for (var i = 0; i < springs.length; i++) {

var s = springs[i];

s.update(1 / 60 * timeStep);

}

for (var i = 0; i < springs.length; i++) {

var s = springs[i];

var pressure = getDistance(s.a.x, s.a.y, s.b.x, s.b.y) Pressure (1 / volume);

var normal = normalizeVector({

x: s.a.x - s.b.x,

y: s.a.y - s.b.y

});

normal = {x: -normal.y, y: normal.x};

var relativeVelocity = {

x: s.a.vx - s.b.vx,

y: s.a.vy - s.b.vy

}

var dampingScalar = dot(relativeVelocity, normal) * 0;

var dampingForce = {

x: normal.x * dampingScalar,

y: normal.y * dampingScalar

};

s.a.vx += normal.x * pressure + dampingForce.x;

s.a.vy += normal.y * pressure + dampingForce.y;

s.b.vx += normal.x * pressure + dampingForce.x;

s.b.vy += normal.y * pressure + dampingForce.y;

}

for (var i = 0; i < points.length; i++) {

var p = points[i];

p.update(1 / 60 * timeStep);

}

}

for (var i = 0; i < points.length; i++) {

var p = points[i];

p.render();

}

update();

requestAnimationFrame(loop);

}



Useful References


http://panoramx.ift.uni.wroc.pl/~maq/soft2d/howtosoftbody.html (this blog gives a step by step guide on implementing a soft body pressure model)










2 views0 comments

Recent Posts

See All

p2 naive broadphase

var Broadphase = require('../collision/Broadphase'); module.exports = NaiveBroadphase; /** * Naive broadphase implementation. Does N^2...

sopiro motor constranit

import { Matrix2, Vector2 } from "./math.js"; import { RigidBody } from "./rigidbody.js"; import { Settings } from "./settings.js";...

Comments


記事: Blog2_Post
bottom of page