Edited at

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

More than 5 years have 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を使う場合は、どこかで回転方向を逆にしないとうまく動かないかも。