スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

DXライブラリでBullet2

こんにちは。
ブログをメモ帳としか思ってないペチコです。

だって漢字とか思い出せないし紙ってかさばるんだもん。


前回のDXライブラリで物理演算エンジンBulletで遊ぼうの続き。
タイトルとか全然違う気がしますが確認することすら億劫な重度のめんどくさがり屋ペチコです。


前回の"Hello World"(文字描画してませんがHello Worldなのです)ではさすがに物理演算って結果にはならなかったので少し拡張してみました。



結局ただサイコロが降ってくるだけですが、なんとなく物理演算!ってカンジですね。

取り敢えずコードです。

#include "DxLib.h"
#include <btBulletDynamicsCommon.h>

//プラグマ宣言リンカ
#pragma comment( lib, "BulletCollision.lib" )
#pragma comment( lib, "BulletDynamics.lib" )
#pragma comment( lib, "BulletSoftBody.lib" )
#pragma comment( lib, "LinearMath.lib" )

//右手→左手位置変換
VECTOR btVGet(const btVector3 &inv){return VGet(inv.getX(), inv.getY(), -inv.getZ());}
//右手→左手回転変換
VECTOR btRotVGet(const btVector3 &inv){return VGet(-inv.getX(), -inv.getY(), inv.getZ());}

//箱追加関数プロトタイプ宣言
void btAddBox();

//ダイナミクスワールド
btDiscreteDynamicsWorld* dynamicsWorld;
//箱コレクション
btAlignedObjectArray<btCollisionShape*> fallShapes;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
//モデルハンドル
int Model_Handle ;

//カメラポジション
VECTOR CameraPos ;

// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;

// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}

// 画面モードの変更
SetGraphMode( 1024 , 768 , 32 ) ;

// 剛体メッシュモデルを読み込む
Model_Handle = MV1LoadModel( "dice.mv1" ) ;

// カメラの座標を初期化
CameraPos.x = 0.0f ;
CameraPos.y = 20.0f ;
CameraPos.z = -20.0f ;

//モデル構造体
struct MODEL_DICE
{
VECTOR pos ;
} ;

// モデル情報の実体宣言
MODEL_DICE mdl ;
mdl.pos.x = 0.0f ;
mdl.pos.y = 0.0f;
mdl.pos.z = 0.0f ;

// ワールドの広さ
btVector3 worldAabbMin(-10000,-10000,-10000);
btVector3 worldAabbMax(10000,10000,10000);
// プロキシの最大数(衝突物体のようなもの)
int maxProxies = 1024;
// broadphaseの作成(SAP法)
btAxisSweep3* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);

// デフォルトの衝突設定とディスパッチャの作成
btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);

// 衝突解決ソルバ
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
// 離散動的世界の作成
dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);

// 重力の設定
dynamicsWorld->setGravity(btVector3(0,-10,0));

// 地面の衝突形状の作成(btStaticPlaneShape:無限平面)
btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);
// 地面のMotionStateの設定
btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));

// 地面の初期情報を設定
btRigidBody::btRigidBodyConstructionInfo
groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
// 地面の剛体の作成
btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
// ワールドに地面の剛体を追加
dynamicsWorld->addRigidBody(groundRigidBody);

// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;

//クリップ距離
SetCameraNearFar( 1.0f, 1000.0f ) ;
// カメラの位置と注視点をセット
SetCameraPositionAndTarget_UpVecY( CameraPos, VGet( 0.0f, 0.0f, 0.0f ) ) ;

int DisplayFPS ;
int FPSCount ;
LONGLONG StartTime ;
LONGLONG NowTime ;

// フレームレート表示関係の初期化
StartTime = GetNowHiPerformanceCount() ;
DisplayFPS = 0 ;
FPSCount = 0 ;

// 総フレーム数
int allFrame = 0 ;

// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{

// 画面を初期化
ClearDrawScreen() ;

// シミュレーションを進める。間隔は60Hz
dynamicsWorld->stepSimulation(1/60.f,10);

//総フレーム
allFrame++;
//60フレームに1回箱を追加
if(allFrame % 60 == 0){
btAddBox();
}

// 箱の位置姿勢情報の取得
btTransform trans;
for (int i=1;i<dynamicsWorld->getNumCollisionObjects();i++)
{
btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
btRigidBody* fallRigidBody = btRigidBody::upcast(obj);
if (fallRigidBody && fallRigidBody->getMotionState())
{
fallRigidBody->getMotionState()->getWorldTransform(trans);
// 位置取得
mdl.pos = btVGet(trans.getOrigin()) ;
MV1SetPosition( Model_Handle, mdl.pos ) ;

// 角度取得
btMatrix3x3 rot = trans.getBasis();
btVector3 euler;
rot.getEulerZYX(euler[2], euler[1], euler[0]);
MV1SetRotationXYZ(Model_Handle, btRotVGet(euler));

// モデルを描画
MV1DrawModel( Model_Handle ) ;
}
}

// FPSを描画
DrawFormatString( 0, 0, GetColor( 255,255,255 ), "FPS:%d", DisplayFPS ) ;
DrawFormatString( 0, 16, GetColor( 255,255,255 ), "BoxNum:%d", allFrame/10 ) ;

// 裏画面の内容を表画面に反映させる
ScreenFlip() ;

// フレームレート処理
FPSCount ++ ;
NowTime = GetNowHiPerformanceCount() ;
if( NowTime - StartTime > 1000000 )
{
DisplayFPS = FPSCount ;
FPSCount = 0 ;
StartTime = NowTime ;
}
}

// 読み込んだモデルの削除
MV1DeleteModel( Model_Handle ) ;

// 地面の剛体を削除
dynamicsWorld->removeRigidBody(groundRigidBody);
delete groundRigidBody->getMotionState();
delete groundRigidBody;

// 箱の剛体を削除
for (int i=0;i<dynamicsWorld->getNumCollisionObjects();i++)
{
btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
btRigidBody* fallRigidBody = btRigidBody::upcast(obj);
if (fallRigidBody && fallRigidBody->getMotionState())
{
delete fallRigidBody->getMotionState();
}
dynamicsWorld->removeCollisionObject( obj );
delete obj;
}

// 箱の衝突形状を削除
for (int i=0;i<fallShapes.size();i++)
{
btCollisionShape* fallShape = fallShapes[i];
delete fallShape;
}
fallShapes.clear();

// 地面の衝突形状を削除
delete groundShape;
// ワールド・ソルバ・その他もろもろの削除
delete dynamicsWorld;
delete solver;
delete collisionConfiguration;
delete dispatcher;
delete broadphase;

// DXライブラリの後始末
DxLib_End() ;

// ソフトの終了
return 0 ;
}

void btAddBox()
{
// 落下する箱の衝突形状の作成
btCollisionShape* fallShape = new btBoxShape(btVector3(0.5f, 0.5f, 0.5f));
// 箱のMotionStateの設定
btDefaultMotionState* fallMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,15,0)));
// 質量、慣性の初期化
btScalar mass = 1;
btVector3 fallInertia(0,0,0);
fallShape->calculateLocalInertia(mass,fallInertia);
// 箱の初期情報を設定
btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
// 箱の剛体の作成
btRigidBody* fallRigidBody = new btRigidBody(fallRigidBodyCI);
// ワールドに箱の剛体を追加
dynamicsWorld->addRigidBody(fallRigidBody);
}

一応のサイコロ(mv1ファイル)置いときます。

毎度改行が反映されずすごく詰まってます。
※追記:よく見たら"<"とかちゃんと&lt;&gt;で置き換えないと表示されてなかったですね
   一括置換してないのでまだ変なとこあるかもです

コード自体は前回とあまり違いはありません。
箱の追加部分を関数にしましたが、個人的にあっちこっち飛ぶのは見にくいので後は流れのままです。
実際もっと複雑になってくると一本化は見にくくてしょうがなくなりますけど。

簡単な解説。

冒頭部分にプラグマ宣言してますが、リンカを設定している場合は不要です。
その辺は"Bullet インストール"等で調べていただくと、詳しく解説されているサイト様が見つかります。

//ダイナミクスワールド
btDiscreteDynamicsWorld* dynamicsWorld;
//箱配列
btAlignedObjectArray<btCollisionShape*> fallShapes;

箱の追加を関数で分けたのでダイナミクスワールドをパブリックで持ってます。
ヘッダーで記述する時はC++だと確かexternですね。

btAlignedObjectArray<T>はコレクションクラスで、前回一つだった箱のシェイプを複数格納するために使用します。
箱シェイプが"fallShape"でコレクションが"fallShapes"とか我ながらもの凄く紛らわしいです。
もうちょっと考えて命名しましょうというお話でした。


後は前回のものから箱の追加部分を抜いただけですね。
FPS描画用の変数がありますがどの位の負荷か見たかったのです。

// 重力の設定
dynamicsWorld->setGravity(btVector3(0,-10,0));

重力加速度は9.8ですがキリが良い方がよいので10にしてます。
そんな理由で物理世界を歪めてしまうゴッドペチコなのです。


メインループ部分。
//総フレーム
allFrame++;
//60フレームに1回箱を追加
if(allFrame % 60 == 0){
btAddBox();
}


手抜きして60フレームに1回箱を追加するようにしてます。
btAddBox()が箱を追加する関数です。

関数部分。
void btAddBox()
{
// 落下する箱の衝突形状の作成
btCollisionShape* fallShape = new btBoxShape(btVector3(0.5f, 0.5f, 0.5f));
// 箱のMotionStateの設定
btDefaultMotionState* fallMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,15,0)));
// 質量、慣性の初期化
btScalar mass = 1;
btVector3 fallInertia(0,0,0);
fallShape->calculateLocalInertia(mass,fallInertia);
// 箱の初期情報を設定
btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
// 箱の剛体の作成
btRigidBody* fallRigidBody = new btRigidBody(fallRigidBodyCI);
// ワールドに箱の剛体を追加
dynamicsWorld->addRigidBody(fallRigidBody);
}


シェイプは使い回しできるのでここに書かなくても良かったのかもしれません。
やってることは箱が1つの場合と同様です。

btBoxShape(btVector3(0.5f, 0.5f, 0.5f))で箱の大きさ(数字は1辺の半分)
ここではサイコロのため直方体シェイプを使ってますが、他にも色々あるので用途に合わせて使い分けると良さそうです。

箱のMotionStateの設定でbtVector3(0,15,0)とあるのは箱の初期位置です。
Bulletの推奨が1.0f=1mなので15mの高さから1辺が0.5f*2=1mの箱を落下させていることになります。
ここでは同じ場所からにしていますが、ここも用途に合わせて変えていくと面白そうです。
btQuaternionは初期角度です。

btScalar mass = 1は質量です。1kgです。
0にすると動かない物体になります。
地面追加の際は、groundRigidBodyCI(0,~として0を直接入れていますね。
その後のfallInertiaで定義してる部分に慣性などが含まれてるようですが、まだ調べてないので前回サンプルのままです。

取り敢えずこれで箱の追加ができました。


描画部分。
// 箱の位置姿勢情報の取得
btTransform trans;
for (int i=1;i<dynamicsWorld->getNumCollisionObjects();i++)
{
btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
btRigidBody* fallRigidBody = btRigidBody::upcast(obj);
//ボディ数分ループ
if (fallRigidBody && fallRigidBody->getMotionState())
{
fallRigidBody->getMotionState()->getWorldTransform(trans);
// 位置取得
mdl.pos = btVGet(trans.getOrigin()) ;
MV1SetPosition( Model_Handle, mdl.pos ) ;

// 角度取得
btMatrix3x3 rot = trans.getBasis();
btVector3 euler;
rot.getEulerZYX(euler[2], euler[1], euler[0]);
MV1SetRotationXYZ(Model_Handle, btRotVGet(euler));

// モデルを描画
MV1DrawModel( Model_Handle ) ;
}
}

箱情報を取り出して実際の描画部分になります。

前回の描画部分をそのままループに入れただけですね。

for (int i=1;i<dynamicsWorld->getNumCollisionObjects();i++)
がワールドにあるオブジェクトの総当たりループです。
i=1にしているのは最初に追加した地面オブジェクトがi=0に入っているため。
力業すぎて、もうちょっと上手い方法があるのだと思いますが、めんどくさいのでコレで。

使い回しのためここではモデル構造体がパブリックにありますが、個別に位置取得保存しない場合はループ内で宣言してよいですね。

後はオブジェクト削除の時もループ回さないといけません。
ここでもi=1にするか箱を先に削除しておかないと上の理由でエラーになります。
ハンドルがどのオブジェクトかどうかの判定ができるのでしょうが、まだよくわかってないので力業でねじ伏せてます。


こんなカンジです。
最初に設定したプロキシの最大数を超えるとエラーが出ます。
私の環境ですと、サイコロが300を超えた辺りから急激にパフォーマンスが低下しました。
ただDrawModelしているだけですが、通常描画だと2000はいけるので、この辺がペチコPCには限界のようです。


DXライブラリでBullet

放置してお風呂に入ってたらすごく重くなってました。
サイコロが1000を超えてもずっとFPSが12だったのはなぜだろう。

ちなみに動画は30フレームに1回落ちるようにしてます。
ここの間隔を短くしたり、横にも重力が働くようにしてみたりして遊んでると花火のように飛び散ったりで、物理演算を堪能できてなかなか面白かったです。


実用化?めんどくさいです。



プロフィール

ペチコートさん

Author:ペチコートさん
大航海時代オンライン
Zephyros、Eurosサーバーに潜伏中

偽ペチコ1号・2号
ペチコの手下達
Zephyrosサーバーに潜伏中

最新記事
月別アーカイブ
カテゴリ
リンク
最新コメント
検索フォーム
QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。