Mod:Coding Challenge #20: 3D Cloth with toxiclibs
物理世界のシミュレーションを外部ライブラリtoxiclibsをつかって行う。toxiclibsは知ってたけどぶっちゃけ複雑そうで、なんやかんや手を付けないでいた。書き方を少し覚える必要があって、何度か書き直したり、他のサンプルコードを写経したりサンプルコードを読んだりドキュメントを読んだりしていた。
2Dの布のようなものがはためく様子。動画では二次元の配列を使っていたけどArrayListを2次元で使った。その他動きの要素として、重力以外にConstantForceBehavior2D
というクラスを使って風を追加した。draw関数のなかでリセットしながらノイズで風力を作っている。
これも2Dが出来たらあとは3Dにするだけという感じ。
プログラムとしては、キモはsetup関数で書いてるスプリングの連結の書き方。横と縦の方向で連結させながら布の終端である最後の部分をcols-1
やrows-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); } }