Objective-C
iOS
3DCG

iOSでCocos3dでNewtonで3D物理演算

More than 1 year has passed since last update.

NewtonGameDynamicsってあんまり日本では使ってる人を見ませんね。
でも、僕は好きです。

Newtonをビルド

おもむろにNewtonの最新版をチェックアウト。

core_Library200/projects/mac/OS10_xcode3.2.5/newton.xcodeproj
を開く。

ターゲットの設定から、BaseSDKをiOSに設定。しかし、このままではビルドが通らない。

なぜかプロジェクトに追加されていないソースファイルがあったので、
core_Library200/source/physics/dgMeshEffect2.cpp
core_Library200/source/physics/dgMeshEffect3.cpp
core_Library200/source/physics/dgMeshEffectSolidTree.cpp
あたりを追加。

さらに、core_Library200/source/newton/Newton.hの中からNewtonSceneCollisionCreateProxy()が宣言されている行を探し、これをコメントアウトする。

自分の環境では、LLVM GCC4.2でビルドするとうまく動かなかった。LLVM 3.1でビルドした。

XcodeのSchemeを編集し、iPhone/iPad実機用と、iOS Simulator用に別々にビルドする。
終わったら、ターミナルでビルドディレクトリを開き、armv7とi386のユニバーサルバイナリを作成する。

lipo -create -output libnewton.a release-iphoneos/libnewton.a release-iossimulator/libnewton.a

Cocos3Dで使う

先ほどビルドしたバイナリを、Cocos3Dプロジェクトに追加する。
core_Library200/source/newton にパスを通す。

core_Library200/projects/mac/iPhoneTranslator/iNewton.h に、Objective-CのNewtonラッパが置いてある。
だがこいつは使えない子。
大人しくNewton.hをインクルードして、通常のCのライブラリとして呼ぶことにする。

CC3Worldを継承したクラスのメンバに、NewtonWorld* newtonを追加して、こんな感じの関数を書く。

MyWorld.m
@implementation MyWorld

- (void)initializeWorld {
    newton = NewtonCreate();
    NewtonSetSolverModel(newton, 1);
}

- (void)dealloc {
    NewtonDestroyAllBodies(newton);
    NewtonDestroy(newton);
    [super dealloc];
}

- (void)updateBeforeTransform:(CC3NodeUpdatingVisitor*)visitor {
    NewtonUpdate(newton, visitor.deltaTime);
}

@end

CC3Nodeを継承したクラスのメンバに、NewtonBody* bodyを追加して、こんな感じの関数を書く。

MyNode.m
static void NewtonGravityCallback(const NewtonBody* body, float timestep, int thredindex) {
    const float GRAVITY = -9.8f;
    float Ixx, Iyy, Izz, mass;

    NewtonBodyGetMassMatrix(body, &mass, &Ixx, &Iyy, &Izz);
    float vector[] = {
        0.0f, GRAVITY * mass, 0.0f
    };
    NewtonBodySetForce(body, vector);
}

static void NewtonTransformCallback(const NewtonBody* body, const float* const matrix, int thredindex) {
    MyNode* node = (MyNode*)NewtonBodyGetUserData(body);
    float matrix[16];
    NewtonBodyGetMatrix(body, matrix);
    float quaternion[4];
    NewtonBodyGetRotation(body, quaternion);

    node.location = CC3VectorMake(matrix[12], matrix[13], matrix[14]);
    node.quaternion = CC3Vector4Make(quaternion[3], quaternion[0], quaternion[1], quaternion[2]);
    // ほんとは行列を直接代入したかったけど、やりかたがよくわからんかった
}

@implementation MyNode

- (void)setupPhysics:(NewtonWorld*)world {
    NewtonCollision* col = NewtonCreateBox(world, 1.0f, 1.0f, 1.0f, 0, NULL);
    body = NewtonCreateBody(world, col, self.transformMatrix.glMatrix);
    NewtonReleaseCollision(world, col);

    NewtonBodySetUserData(body, self);
    NewtonBodySetMassMatrix(body, 1.0f, 1.0f, 1.0f, 1.0f);
    NewtonBodySetForceAndTorqueCallback(body, &NewtonGravityCallback);
    NewtonBodySetTransformCallback(body, &NewtonTransformCallback);
}

@end

できたー!

まとめ

NewtonのMac/iOS対応は適当なので、自分でがんばろう!

追記

Quaternionを使う場合は、どこかで回転方向を逆にしないとうまく動かないかも。