Javaから物理演算エンジンBulletを呼び出すライブラリで遊ぶ最終回。今回はSoftBodyです。日本語だと「軟体」なんかなぁ?最後なので今回は大盤振る舞い。元々のBulletライブラリのデモの中から一挙に5つのデモを実装します。
- ロープがプランプラン揺れるデモ
- ロープにぶら下がった直方体が揺れるデモ
- 布にぶら下がった直方体が揺れるデモ
- 柔らかい球体が階段を転がり落ちるデモ
- 柔らかい車輪が落下するデモ
しかも1つのアプリに詰め込んじゃいます。まずはメイン部分のソースから。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
public class MainActivity extends GLBulletActivity { private static final float PI = (float)Math.PI; /** * number of demo */ private final int MAX_DEMO = 5; /** * current demo type [0-MAX_DEMO) */ private int demoNo = 0; private float prevTime; /** * intervals to change demo[seconds] */ private float demoTime = 20f; private MotorControl mMotorControl; @Override protected void onResume() { super.onResume(); setDebugMode(0); } @Override public PhysicsWorld initPhysics(GLBulletWorldSurfaceView view) { // set distance between camera and target position setCamDistance(50f); // create rigid and softbody physics world with -9.8 of gravity return new PhysicsWorld( new SoftBodyRigidBodyCollisionConfiguration(), new DefaultCollisionDispatcher(), new SequentialImpulseConstraintSolver(), new AxisSweepBroadphase(), new SoftRigidDynamicsWorld(), new Vector3(0f, -9.8f, 0f) ); } @Override public void createObjects(PhysicsWorld world) { // create ground plane(use top surface of box, static object) new RigidBody(world, new BoxShape(world, new Vector3(50f, 0.1f, 50f)), 0f, new MotionState(0f, -20.f, 0f) ); // create softbody switch (demoNo) { case 0: initRope(world); break; case 1: initRopeAttach(world); break; case 2: initClothAttach(world); break; case 3: initPressure(world); break; case 4: initClusterCombine(world); break; } demoNo = (demoNo + 1) % MAX_DEMO; prevTime = System.nanoTime() / 1000000000.f; // as seconds } @Override public void destroyObjects(PhysicsWorld world) { if (mMotorControl != null) { mMotorControl.dispose(); mMotorControl = null; } } @Override public void renderFrame(GL10 gl) { final float t = System.nanoTime() / 1000000000.f; if (t - prevTime > demoTime) { resetWorld(); } } ... |
だいたいの流れは前回までのRigidBodyの時と同じですが、大きく違うところが2つあります。
1つ目は、前回まではRigidBodyだけを使っていたので、#initPhysicsでは単に重力加速度を-9.8fに設定してPhysicsWorldを生成するだけでした。しかし今回は6個のオブジェクトを引数として渡してPhysicsWorldを生成しています。それ以外は…SoftBodyを使うときにはこうするもんだと思ってくださいm(__)m。ちなみに、一番最後のVector3オブジェクトで(重力)加速度を設定しています。
2つ目は、#renderFrameです。このメソッドは描画を行う際に呼び出されます。本来はここで、ライブラリでサポートしていないオブジェクトの描画を独自に行うのですが、今回は、定期的に呼び出されることを利用してデモの自動切り替えのチェックを行っています(ただし等間隔で呼び出されるとは限りません)。
デモの切り替えは#resetWorldを呼び出しています。このメソッドを呼び出すと、#destroyObjectsが呼び出されオブジェクトが破棄された後、#createObjectsが呼び出されて再生成されます。
#createObjectsではいつもどおり地面を生成してから、個別のデモを生成します。
ちなみに、シミュレーション上は地面は必須ではありませんが、自由落下する物体は地面に落とすようにした方が良いです。地面にぶつかって静止すればシミュレーション計算の負荷が軽くなるからです。
あと、いつも平らな地面じゃつまらないという人は、三角メッシュで地面を生成してみてください。
普通はモデリングツールとかで作った三角メッシュを読み込むのですが、計算で生成することも出来ます。そのためのヘルパークラスも用意しています。
例えば、上のソースの44-48行を次の1行に変更するとパラメータから三角メッシュを計算して地面を生成します。ただし、描画の負荷が高くなるので、非力な端末ではあまりお勧めしません。
1 |
MeshHelperTriangle.createGround(world, 8f, 20, 20, 10, 0.5f); |
実際のデモの内容は50行目から始まるswitch文で切り替えて生成します。
次は、それぞれのデモの内容です。