日常の進捗

主に自分のための,行為とその習慣化の記録

Mod:Coding Challenge #20: 3D Cloth with toxiclibs

f:id:takawo:20170919093424g:plain

物理世界のシミュレーションを外部ライブラリtoxiclibsをつかって行う。toxiclibsは知ってたけどぶっちゃけ複雑そうで、なんやかんや手を付けないでいた。書き方を少し覚える必要があって、何度か書き直したり、他のサンプルコードを写経したりサンプルコードを読んだりドキュメントを読んだりしていた。

toxiclibs.org

f:id:takawo:20170919093935g:plain

2Dの布のようなものがはためく様子。動画では二次元の配列を使っていたけどArrayListを2次元で使った。その他動きの要素として、重力以外にConstantForceBehavior2Dというクラスを使って風を追加した。draw関数のなかでリセットしながらノイズで風力を作っている。

これも2Dが出来たらあとは3Dにするだけという感じ。

プログラムとしては、キモはsetup関数で書いてるスプリングの連結の書き方。横と縦の方向で連結させながら布の終端である最後の部分をcols-1rows-1みたいな形で処理して縦横の糸を編んでいる。

  for (int j = 0; j < rows; j++) {
    for (int i = 0; i < cols; i++) {
      Particle a = particles.get(i).get(j);
      if (i != cols - 1) {
        Particle b1 = particles.get(i+1).get(j);
        Spring s1 = new Spring(a, b1);
        springs.add(s1);
        physics.addSpring(s1);
      }
      if (j != rows - 1) {
        Particle b2 = particles.get(i).get(j+1);
        Spring s2 = new Spring(a, b2);
        springs.add(s2);
        physics.addSpring(s2);
      }
    }
  }

以下が2D版と3D版のコード。

コード2D

import toxi.geom.*;
import toxi.physics2d.*;
import toxi.physics2d.behaviors.*;
import toxi.physics2d.constraints.*;

ArrayList<ArrayList<Particle>> particles;
ArrayList<Spring> springs;

VerletPhysics2D physics;

int cols = 40;
int rows = 40;

float w = 10;

void setup() {
  size(960, 540);
  colorMode(HSB, 360, 100, 100);

  particles = new ArrayList<ArrayList<Particle>>();
  springs = new ArrayList<Spring>();

  physics = new VerletPhysics2D();
  Vec2D gravity = new Vec2D(0, 1);
  GravityBehavior2D gb = new GravityBehavior2D(gravity);
  physics.addBehavior(gb);

  for (int j = 0; j < rows; j++) {
    ArrayList<Particle> ps = new ArrayList<Particle>();
    for (int i = 0; i < cols; i++) {
      float x = map(i, 0, cols, 50, width-50);
      float y = map(j, 0, rows, 50, height-50);
      Particle p = new Particle(x, y);
      ps.add(p);
      physics.addParticle(p);
    }
    particles.add(ps);
  }
  for (int j = 0; j < rows; j++) {
    for (int i = 0; i < cols; i++) {
      Particle a = particles.get(i).get(j);
      if (i != cols - 1) {
        Particle b1 = particles.get(i+1).get(j);
        Spring s1 = new Spring(a, b1);
        springs.add(s1);
        physics.addSpring(s1);
      }
      if (j != rows - 1) {
        Particle b2 = particles.get(i).get(j+1);
        Spring s2 = new Spring(a, b2);
        springs.add(s2);
        physics.addSpring(s2);
      }
    }
  }
  particles.get(0).get(0).lock();
  particles.get(0).get(cols-1).lock();
}

void draw() {
  background(220, 20, 20);
  physics.update();
  for (ArrayList<Particle> ps : particles) {
    for (Particle p : ps) {
      //p.display();
    }
  }
  for (Spring s : springs) {
    s.display();
  }
}

class Particle extends VerletParticle2D {
  Particle(float x, float y) {
    super(x, y);
  }

  void display() {
    noStroke();
    ellipse(x, y, 10, 10);
  }
}

class Spring extends VerletSpring2D {
  Spring(Particle a, Particle b) {
    super(a, b, w, 0.5);
  }

  void display() {
    stroke(0, 0, 100);
    strokeWeight(2);
    line(a.x, a.y, b.x, b.y);
  }
}

コード(3D)

import peasy.*;
import peasy.org.apache.commons.math.*;
import peasy.org.apache.commons.math.geometry.*;

import toxi.geom.*;
import toxi.physics3d.*;
import toxi.physics3d.behaviors.*;
import toxi.physics3d.constraints.*;

ArrayList<ArrayList<Particle>> particles;
ArrayList<Spring> springs;

VerletPhysics3D physics;

int cols = 40;
int rows = 40;

float w = 10;

Vec3D prev_wind = new Vec3D(1, 0, 0);
ConstantForceBehavior3D prev_cb = new ConstantForceBehavior3D(prev_wind);

PeasyCam cam;

void setup() {
  size(960, 540, P3D);
  colorMode(HSB, 360, 100, 100);

  particles = new ArrayList<ArrayList<Particle>>();
  springs = new ArrayList<Spring>();

  physics = new VerletPhysics3D();
  Vec3D gravity = new Vec3D(0, 1, 0);
  GravityBehavior3D gb = new GravityBehavior3D(gravity);
  physics.addBehavior(gb);

  for (int j = 0; j < rows; j++) {
    ArrayList<Particle> ps = new ArrayList<Particle>();
    for (int i = 0; i < cols; i++) {
      float x = map(i, 0, cols, 150-width/2, width/2-150);
      float y = map(j, 0, rows, 150-height/2, height/2-150);
      Particle p = new Particle(x, y, 0);
      ps.add(p);
      physics.addParticle(p);
    }
    particles.add(ps);
  }
  for (int j = 0; j < rows; j++) {
    for (int i = 0; i < cols; i++) {
      Particle a = particles.get(i).get(j);
      if (i != cols - 1) {
        Particle b1 = particles.get(i+1).get(j);
        Spring s1 = new Spring(a, b1);
        springs.add(s1);
        physics.addSpring(s1);
      }
      if (j != rows - 1) {
        Particle b2 = particles.get(i).get(j+1);
        Spring s2 = new Spring(a, b2);
        springs.add(s2);
        physics.addSpring(s2);
      }
    }
  }
  particles.get(0).get(0).lock();
  particles.get(0).get(cols-1).lock();

  cam = new PeasyCam(this, 500);
}

void draw() {
  background(220, 20, 20);
  physics.update();
  for (ArrayList<Particle> ps : particles) {
    for (Particle p : ps) {
      //p.display();
    }
  }
  for (Spring s : springs) {
    s.display();
  }


  physics.removeBehavior(prev_cb);
  float nx = map(noise(frameCount*0.05), 0, 1, -1, 1);
  float ny = map(noise(frameCount*0.035), 0, 1, -1, 1);
  float nz = map(noise(frameCount*0.01), 0, 1, -1, 1);
  Vec3D wind = new Vec3D(nx, ny, nz);
  ConstantForceBehavior3D cb = new ConstantForceBehavior3D(wind);
  physics.addBehavior(cb);
  prev_wind = wind.copy();
  prev_cb = cb;
}

class Particle extends VerletParticle3D {
  Particle(float x, float y, float z) {
    super(x, y, z);
  }

  void display() {
    noStroke();
    ellipse(x, y, 10, 10);
  }
}

class Spring extends VerletSpring3D {
  Spring(Particle a, Particle b) {
    super(a, b, w, 1);
  }

  void display() {
    stroke(0, 0, 100);
    strokeWeight(2);
    line(a.x, a.y, a.z, b.x, b.y, b.z);
  }
}

リファレンス