約 3,606,317 件
https://w.atwiki.jp/yumenonakanosoto/pages/158.html
# coding UTF-8 # ボールがボックス状の壁の中でバウンドする # [../20110528.004.Jpg] # 参考 vpythonの # E \Python26\Lib\site-packages\visual\examples\bounce2.py # 上のものをpyodeにて実現 # ファイル名 # C \myprg_main\python_my_prg\ode_prg\box_geom2.py # 2011/3/3 10 22~ import random import ode from visual import * boxList = [] ballList = [] box_kosuu=5 ball_kosuu=100 class Field scene = display(autoscale=0, forward=norm((-2.0,-1.0,-1.0))) #物理世界を作成 world=ode.World() world.setGravity((0, -9.81, 0)) #自由落下させたいなら worldのコメントアウトのほうを生かし # 最後の方の ball.body.addForce((0,-1000,0)) を殺す #world.setGravity((0, 0, 0)) #地平面を作成? space=ode.Space() #衝突関係 jointgroup=ode.JointGroup() ode_floor=ode.GeomPlane(space,(0,1,0),0) #()なかの引数 上の例ではy軸方向に重力が発生 #多分 spaceの最後の引数が y軸の座標を示す ode_floor.setCategoryBits(1) ode_floor.setCollideBits(3) ode_floor.viz=box( pos=(0,-0.03,0), width=20,length=20,height=0.06,color=(0.5,0.5,1.0)) vball_x=sphere( pos=(5, 0, 0), radius=0.2, color=color.red) #x軸 vball_y=sphere( pos=(0, 5, 0), radius=0.2, color=color.white)#y軸 vball_z=sphere( pos=(0, 0, 5), radius=0.2, color=color.yellow) #z軸 target_fps=30 #target_fps=30だから1秒間に30回の処理をする dt=1.0/target_fps def near_callback(self,args,geom1,geom2) #isBall = ((ground == o1) || (ground == o2)); #if (isGround)//地面だけと衝突判定 print geom1 for c in ode.collide(geom1,geom2) c.setBounce(0.0) c.setMu(50)#摩擦 c.setMu2(50) #random.seed(1) j=ode.ContactJoint(self.world,self.jointgroup,c) j.attach(geom1.getBody(),geom2.getBody()) def tick(self) for i in range(ball_kosuu) ballList[i].update() box.update() print time.sleep(0.1) self.space.collide((),self.near_callback) self.world.step(self.dt) self.jointgroup.empty() return True class Ball def __init__( self,field, m_density=0.1, m_radius=0.3, g_radius=0.3, v_radius=0.3, b_pos=vector(-4,1,0), b_sp =vector(10,0,0), v_color=color.cyan ) self.body=ode.Body(field.world) M=ode.Mass() M.setSphere(m_density, m_radius) #(密度,半径) self.body.setMass(M) self.body.setPosition(b_pos) #self.body.addForce(b_sp) self.geom=ode.GeomSphere( space=field.space, radius=g_radius ) #このradiusが実際のバウンドを決める #多分 sphereの座標が0で radiusが0.3だから #posが0.3ぐらいのバウンド点になる self.geom.setCategoryBits(1) self.geom.setCollideBits(0) #ボール同士の衝突を避ける #上は Boxのジオメトリを作成 self.geom.setBody(self.body) # 剛体にジオメトリをセット self.vball=sphere( radius=v_radius, color=v_color ) def update(self) pos=self.geom.getPosition() #boxのジオメトリの位置を得る self.vball.pos=pos #print pos, class MetaBall def __init__(self, field) posList= [] for i in range(ball_kosuu) #posをランダムに発生 while True pos=vector(0,0,0) pos.x=random.randint(-4,4) pos.y=random.randint(4,4) pos.z=random.randint(-4,4) if pos not in posList posList.append(pos) only_pos=pos break #posのx,y,zの座標を()の中の範囲でランダムに発生 #if文の中で同じposがsetPositionされないようにしている #posがないなら posを集合型の要素として 加える else print 個数が多すぎる #print posList #初速度をランダムに sp=vector(0,0,0) sp.x=random.randint(-50,50) sp.y=random.randint(-50,50) sp.z=random.randint(-50,50) ballList.append(Ball(field,b_pos=only_pos,b_sp=sp)) class Box #多分pyodeとvpythonの座標系は一緒だったな def __init__(self, field) self.body = ode.Body(field.world) M = ode.Mass() M.setBox( 1, 1, 3, 7)#密度,w,h,l self.body.setMass(M) self.body.setPosition((0, 1.5, 0)) self.geom = ode.GeomBox( space=field.space, lengths=(1, 3, 7))#w,h,l self.geom.setBody(self.body) #self.frame = frame() #self.vbox = box( #frame=self.frame, width= 1, height= 2, length= 5) self.vbox = box( width=1, height=3, length=7) #self.vbox.pos = (0, 1, 0) #方向w z h y l x #self.visual.rotate(angle=visual.pi/2.0, axis=(0,1,0)) def update(self) self.body.setPosition((0, 1.5, 0)) pos = self.body.getPosition() self.vbox.pos=pos #mat = self.body.getRotation() #self.vbox.pos = pos #self.frame.pos = pos #self.frame.pos = pos #self.frame.axis = mat[0], mat[3], mat[6] #self.frame.up = mat[1], mat[4], mat[7] if __name__== __main__ field = Field() metaball = MetaBall(field) #metabox = MetaBox(field) box=Box(field) #box.body.setPosition((0,1.5,0)) while True field.tick()
https://w.atwiki.jp/bambooflow/pages/95.html
ODE概要 ODE概要ODEとは 概要 オブジェクトの種類World"世界"(動力学シミュレーション空間) Space"空間"(衝突検出空間) シミュレーションの流れ まとめ ODEとは Open Dynamics Engine (ODE) はフリーの剛体力学をシミュレートするためのCライブラリです。 精度の方はよくないみたいなんですが高速にシミュレーションできるので、ゲーム作りや研究でロボット動作のシミュレーション等を扱うには十分に役立つツールだと思います。 流体力学とかはないので、飛行機を飛ばしたり船を走らせたりのシミュレーションは難しいかもしれません。 概要 ODEは次の2つの主要なコンポーネントを持っています。 dynamics simulation engine (動力学シミュレーション・エンジン ) collision detection engine (衝突検出エンジン) オブジェクトの種類 ODEで扱うオブジェクトは次のものがあります。 オブジェクト ID 説明 world dWorldID 剛体とジョイントのためのコンテナ space dSpaceID 衝突検出するための空間 rigid body dBodyID 剛体 geometry dGeomID 衝突のためのジオメトリ joint dJointID 剛体同士を接続するためのジョイント group of joint dJointGroupID ジョイントグループ World"世界"(動力学シミュレーション空間) worldは動力学シミュレーションのための空間です。 物体が落ちたり物体同士を繋げたりすることを扱います。 次の情報が計算されます。 重力(gravity) 力(force) 速度(velocity) 剛体(rigid body)位置(position) 方位(orientation) 質量(mass) ジョイント回転力 回転速度 etc. ここでは、剛体がどの位置にどのような状態で剛体の大きさに関する情報しか持ちません。なので、その剛体が球体であれ箱型であれ同じ扱いになります。 Space"空間"(衝突検出空間) spaceは衝突検出のための空間です。 物体同士が衝突したり転がったりする動作を作るために必要になります。 次の情報を計算します。 物体(geom)位置(position) 方位(orientation) 形状、大きさ etc. ここでは、物体の衝突検出のみを計算します。物体同士が衝突した後に反発するような動作をさせるときに必要な力の計算はここでは行いません。物体の衝突は、contact jointによって表現するようです。 衝突検出されたら、contact jointを生成して衝突した物体同士を接続します。次に衝突した物体に力を与えるための計算します。計算したらcontact jointを破棄して次のステップに移ります。 衝突検出は常に行われるとシミュレーションにかなり負荷がかかります。ODEは物体同士がある程度距離が近付いたときに計算するような工夫がなされています。 シミュレーションの流れ ODEのシミュレーションは次のような手順で実行します。 動力学世界を生成する 動力学世界に剛体を生成する すべての剛体に状態(位置など)を設定する 動力学世界にジョイントを生成する 剛体にジョイントを繋げる すべてのジョイントのパラメータを設定する 必要なときに、衝突世界および衝突ジオメトリオブジェクトを生成する 接触ジョイントを適用するためにジョイントグループを生成する 繰り返し処理:while(true) 必要ならば剛体に力を加える 必要ならばジョイントパラメータを調節する 衝突検出を呼び出す すべての衝突点に接触点を生成し、接触ジョイントグループに加える シミュレーションを1ステップ進める 接触ジョイントグループのすべてのジョイントを削除する 動力学世界と衝突世界を破棄する 手順がおかしかったりすると正常に動作しないので注意が必要になります。 まとめ 自分がODEを触ってみて現実世界とシミュレーションの世界で違いを感じたことは、 シミュレーションエンジンには動力学シミュレーションと衝突検出の2種類がある 衝突にはジョイントで表現される です。 ODEは概念や設定を覚えてしまえば、APIが揃っているので手軽にシミュレーションできます。 なので、一から動力学シミュレーションや衝突検出のプログラムを作るよりも簡単にシミュレーション環境を構築できるところがいいところです。 以上
https://w.atwiki.jp/bambooflow/pages/238.html
歯車モデル ODEで歯車みたいに1つのホイール回転をもう一方のホイールに伝達させるようなモデルを作るにどうすればいいのだろうと常々考えていました。 ようやく1つの解が見つかったので紹介します。 メモ書きです。 <<執筆中>> 歯車モデルソースコード ポイント まとめ 本当の歯車みたいに歯は付いてないですけど、モデルということで。 test_gear_model.zip 左がwheel1で右がwheel2です。 wheel1のみ、トルクを与えることができます。 "a":トルク加算(+) "d":トルク減算(-) ソースコード #ifdef WIN32 #include windows.h #endif #include ode/ode.h #include drawstuff/drawstuff.h #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 #endif #ifdef dDOUBLE #define dsDrawBox dsDrawBoxD #define dsDrawSphere dsDrawSphereD #define dsDrawCylinder dsDrawCylinderD #endif static dWorldID world; static dSpaceID space; static dBodyID body_box1; // fixed static dBodyID body_box2; // fixed static dBodyID body_wheel1; static dJointID joint_hinge1; static dGeomID geom_wheel1; static dBodyID body_wheel2; static dJointID joint_hinge2; static dGeomID geom_wheel2; dReal box_size[3] = { 1.0, 1.0, 1.0 }; dReal wheel1_r = 1.0; dReal wheel1_w = 0.1; dReal wheel2_r = 2.0; dReal wheel2_w = 0.1; #define STEP_SIZE 0.01 double time = 0; double torque = 0.0; // start simulation - set viewpoint static void start() { static float xyz[3] = { 2.f, -7.f, 12.f }; static float hpr[3] = { 90.f, -20.f, 0.f }; dsSetViewpoint( xyz, hpr ); } // called when a key pressed static void command (int cmd) { if (cmd == a ) { torque += 1.0; printf( "torque = %f\n", torque ); } else if (cmd == d ) { torque -= 1.0; printf( "torque = %f\n", torque ); } } // simulation loop static void simLoop( int pause ) { // Ctl+p が押されたらifに入らない if (!pause) { dJointAddHingeTorque( joint_hinge1, torque ); // wheel1のみにトルクを与える dWorldStep( world, STEP_SIZE ); time += STEP_SIZE; } dsSetColor( 1.0f, 1.0f, 1.0f ); dsDrawBox( dBodyGetPosition( body_box1 ), dBodyGetRotation( body_box1 ), box_size ); dsSetColor( 1.0f, 1.0f, 1.0f ); dsDrawBox( dBodyGetPosition( body_box2 ), dBodyGetRotation( body_box2 ), box_size ); dsSetColor( 0.0f, 1.0f, 1.0f ); dsDrawCylinder( dBodyGetPosition( body_wheel1 ), dBodyGetRotation( body_wheel1 ), wheel1_w, wheel1_r ); dsSetColor( 1.0f, 0.0f, 1.0f ); dsDrawCylinder( dBodyGetPosition( body_wheel2 ), dBodyGetRotation( body_wheel2 ), wheel2_w, wheel2_r ); } int main( int argc, char* argv[] ) { dInitODE(); // setup pointers to drawstuff callback functions dsFunctions fn; fn.version = DS_VERSION; fn.start = start; fn.step = simLoop; fn.command = command; fn.stop = 0; fn.path_to_textures = "../drawstuff/textures"; world = dWorldCreate(); //dWorldSetGravity( world, 0.0, 0.0, -9.8 ); space = dHashSpaceCreate( 0 ); // box creating { body_box1 = dBodyCreate( world ); dReal pos[3] = { 0.0, 0.0, 10.0 }; dBodySetPosition( body_box1, pos[0], pos[1], pos[2] ); // boxの固定 dJointID fixed; fixed = dJointCreateFixed( world, 0 ); dJointAttach( fixed, NULL, body_box1 ); dJointSetFixed( fixed ); } // wheel1 creating { dReal pos[3] = { 0.0, -1.0, 10.0 }; body_wheel1 = dBodyCreate( world ); dBodySetPosition( body_wheel1, pos[0], pos[1], pos[2] ); geom_wheel1 = dCreateCylinder( space, wheel1_r, wheel1_w ); dGeomSetBody( geom_wheel1, body_wheel1 ); dMatrix3 R; dRFromAxisAndAngle( R, 1.0, 0.0, 0.0, M_PI/180.0*90.0 ); dGeomSetRotation( geom_wheel1, R ); // ヒンジジョイント(wheel1回転軸) joint_hinge1 = dJointCreateHinge( world, 0 ); dJointAttach( joint_hinge1, body_box1, body_wheel1 ); dJointSetHingeAnchor( joint_hinge1, pos[0], pos[1], pos[2] ); dJointSetHingeAxis( joint_hinge1, 0.0, 1.0, 0.0 ); } // box creating { body_box2 = dBodyCreate( world ); dReal pos[3] = { 3.0, 0.0, 10.0 }; dBodySetPosition( body_box2, pos[0], pos[1], pos[2] ); // boxの固定 dJointID fixed; fixed = dJointCreateFixed( world, 0 ); dJointAttach( fixed, NULL, body_box2 ); dJointSetFixed( fixed ); } // wheel2 creating { dReal pos[3] = { 3.0, -1.0, 10.0 }; body_wheel2 = dBodyCreate( world ); dBodySetPosition( body_wheel2, pos[0], pos[1], pos[2] ); geom_wheel2 = dCreateCylinder( space, wheel2_r, wheel2_w ); dGeomSetBody( geom_wheel2, body_wheel2 ); dMatrix3 R; dRFromAxisAndAngle( R, 1.0, 0.0, 0.0, M_PI/180.0*90.0 ); dGeomSetRotation( geom_wheel2, R ); // ヒンジジョイント(wheel2回転軸) joint_hinge2 = dJointCreateHinge( world, 0 ); dJointAttach( joint_hinge2, body_box2, body_wheel2 ); dJointSetHingeAnchor( joint_hinge2, pos[0], pos[1], pos[2] ); // boxと同じ座標 dJointSetHingeAxis( joint_hinge2, 0.0, 1.0, 0.0 ); } // 接触点の生成 { dContact contact; contact.geom.depth = 0.0; contact.geom.pos[0] = 1.0; contact.geom.pos[1] = 0.0; contact.geom.pos[2] = 10.0; contact.geom.normal[0] = 1.0; contact.geom.normal[1] = 0.0; contact.geom.normal[2] = 0.0; contact.surface.mode = dContactBounce; contact.surface.mu = dInfinity; contact.surface.bounce = 0.0; dJointID c = dJointCreateContact( world, 0, contact ); dJointAttach( c, body_wheel1, body_wheel2 ); } dsSimulationLoop( argc, argv, 320, 240, fn ); dWorldDestroy( world ); dCloseODE(); return 0; } ポイント ポイントはdJointCreateContactを静的に作るところです。 { dContact contact; contact.geom.depth = 0.0; contact.geom.pos[0] = 1.0; contact.geom.pos[1] = 0.0; contact.geom.pos[2] = 10.0; contact.geom.normal[0] = 1.0; contact.geom.normal[1] = 0.0; contact.geom.normal[2] = 0.0; contact.surface.mode = dContactBounce; contact.surface.mu = dInfinity; contact.surface.bounce = 0.0; dJointID c = dJointCreateContact( world, 0, contact ); dJointAttach( c, body_wheel1, body_wheel2 ); } depth:接触点の深さ(ここではホイールの面と面で接触していると仮定して0を設定) pos:接触点の位置 normal:衝突の法線ベクトル(垂直なベクトル) surface:摩擦とかすべりとかを設定 上記の部分をコメントして一方のホイールを回転させてみると、もう片方は回らないと思います。 注意 現状(ODEv0.11.1)ではCylinder同士の衝突はサポートしていないようです。よって、dCollideによる衝突検出で自動でposやnormalを求めるといったやり方はできなさそうです。 (かなり残念) そのうちサポートされることを祈ります。 まとめ 通常は衝突に使うコンタクトジョイントを反則的?にホイール同士の接触に適用することでホイール回転の伝達が可能になりました。 この方法が正攻法であれば、いろいろと応用が利きそうです。 こういうのを発見するのは楽しいですね。
https://w.atwiki.jp/bambooflow/pages/240.html
ライントレーサ 高校のとき、ライントレーサというロボットを作ったことがあります。 それがプログラムを知ったきっかけなんですが、今思うと懐かしいです。 懐かしついでにODEでも作れないかと思ってがんばってみました。 ODEならお金かからないしね。 メモ書きです。 ライントレーサ説明(ポイント) まとめ test_linetracer.zip ライントレーサは線の上をたどるロボットです。 ロボットの中央の下にプレート(黄)があります。 そのプレートには、左右両側にセンサ(赤と緑)が付いています。 センサは白い線に反応します。(実機では赤外線センサが付いていて白い線の上では赤外線が反射することで白線の存在を感知します。) センサが反応すると、対応したモータが止まるよう制御がかかります。 今回はシンプルなつくりにするために、センサは左右に1つずつです。 高校のときのやつは中央付近に2つ付いていてあわせて4つで制御していました。 十字路でうまく動かなくて苦労したのを今でも記憶に残っています。 説明(ポイント) このプログラムのポイントは、白線の検出のところです。 白線の検出には、Rayを使用しています。 Rayの使い方は、レイによる距離測定を参照してください。 Rayを使うとその半直線に接触した物体を感知し、その距離を知ることができます。 今回は、物体の感知のために使用しました。 nearCallback関数内 // センサー情報を取得 if (sensor_l) { if (o1 == sensor_l- geom || o2 == sensor_l- geom) { dContactGeom c; int numc = dCollide( o1, o2, 1, c, sizeof(dContactGeom) ); if (numc 0) { if (o1 == w_line- geom || o2 == w_line- geom) { hit_l = 1; printf( "left sensor hit!! %f\n", c.depth ); } } return; } } if (sensor_r) { if (o1 == sensor_r- geom || o2 == sensor_r- geom) { dContactGeom c; int numc = dCollide( o1, o2, 1, c, sizeof(dContactGeom) ); if (numc 0) { if (o1 == w_line- geom || o2 == w_line- geom) { hit_r = 1; printf( "right sensor hit!! %f\n", c.depth ); } } return; } } センサとなるRayに白線が接触することで、hit_l(int型)もしくはhit_r(int型)に1を代入します。 "category"と"collide"ビットフィールド制御すればもう少しかっこよくかけるかもしれません。 simLoop関数内 // モータ制御 if (hit_l == 0) { dJointSetHingeParam( dBodyGetJoint(car_wheel_l- body, 0), dParamVel, 10.0 ); } else { dJointSetHingeParam( dBodyGetJoint(car_wheel_l- body, 0), dParamVel, -5.0 ); } if (hit_r == 0) { dJointSetHingeParam( dBodyGetJoint(car_wheel_r- body, 0), dParamVel, 11.0 ); } else { dJointSetHingeParam( dBodyGetJoint(car_wheel_r- body, 0), dParamVel, -5.0 ); } hit_lもしくは、hit_rが1となると、メインループ内でモータを止めるような制御を行っています。 ちょっと失敗したのは、ヒンジジョイントをグローバル変数で用意しなかったためにアクセスがちょっと複雑になってしまったところです。 まとめ プログラムはべた書きでかなり格好わるいです。さらにごまかしで作ってしまったところもあるのでもうちょっとしっかりと作りたいかな。 オブジェクト指向(コンポーネント指向)っていうのをODEでも書きやすいとよいのですが。。。 あと、白線は今回はシンプルに四角形の形にしました。 でも本当は配列マップから任意のコースを作るような仕組みを作りたかったんですが根気が足りず途中で投げてしまいました。 気合があれば次こそは。。。
https://w.atwiki.jp/bambooflow/pages/173.html
ODEテンプレート #include ode/ode.h #include drawstuff/drawstuff.h #include vector #define MAX_CONTACTS 4 #ifdef dDOUBLE # define dsDrawSphere dsDrawSphereD # define dsDrawBox dsDrawBoxD # define dsDrawCapsule dsDrawCapsuleD # define dsDrawCylinder dsDrawCylinderD #endif struct GeomDataT { dReal color[4]; GeomDataT() { color[0] = color[1] = color[2] = color[3] = 1.0; } void setColor( const dReal r, const dReal g, const dReal b, const dReal a=1.0 ) { color[0] = r; color[1] = g; color[2] = b; color[3] = a; } const dReal* getColor() { return color; } }; static dWorld *world; static dSpace *space; static dJointGroup contactgroup; static std vector dBody* body_vec; static std vector dGeom* geom_vec; static void nearCallback( void *data, dGeomID o1, dGeomID o2 ) { dBodyID b1 = dGeomGetBody( o1 ); dBodyID b2 = dGeomGetBody( o2 ); if ( b1 b2 dAreConnectedExcluding( b1, b2, dJointTypeContact ) ) return; dContact contact[MAX_CONTACTS]; for ( int i=0; i MAX_CONTACTS; i++ ) { //contact[i].surface.mode = dContactBounce | dContactSoftERP | dContactSoftCFM; contact[i].surface.mode = dContactBounce | dContactApprox1; contact[i].surface.mu = 1.0; // dInfinity; contact[i].surface.bounce = 0.8; contact[i].surface.soft_erp = 0.2; contact[i].surface.soft_cfm = 0.00001; } int numc = dCollide( o1, o2, MAX_CONTACTS, contact[0].geom, sizeof( dContact ) ); if ( numc 0 ) { for ( int i=0; i numc; i++ ) { dJointID c = dJointCreateContact( *world, contactgroup, contact+i ); dJointAttach( c, b1, b2 ); } } } // start simulation - set viewpoint static void start() { static float xyz[3] = { -7.0f, 0.0f, 5.0f }; static float hpr[3] = { 0.f, -15.f, 0.f }; dAllocateODEDataForThread(dAllocateMaskAll); dsSetViewpoint( xyz, hpr ); } static void drawGeom( const dGeom* g ) { if (!g) return; GeomDataT* data = (GeomDataT*)g- getData(); if (data) { const dReal* color = data- getColor(); dsSetColorAlpha( color[0], color[1], color[2], color[3] ); } else { dsSetColor( 1.0, 1.0, 0.0 ); } switch (g- getClass()) { case dSphereClass { dsDrawSphere( dGeomGetPosition( *g ), dGeomGetRotation( *g ), ((dSphere*)g)- getRadius() ); } break; case dBoxClass { dVector3 len; ((dBox*)g)- getLengths( len ); dsDrawBox( dGeomGetPosition( *g ), dGeomGetRotation( *g ), len ); } break; case dCapsuleClass { dReal radius, length; ((dCapsule*)g)- getParams( radius, length ); dsDrawCapsule( dGeomGetPosition( *g ), dGeomGetRotation( *g ), length, radius ); } break; case dCylinderClass { dReal radius, length; ((dCylinder*)g)- getParams( radius, length ); dsDrawCylinder( dGeomGetPosition( *g ), dGeomGetRotation( *g ), length, radius ); } break; //case dPlaneClass //case dRayClass } } // simulation loop static void simLoop( int pause ) { space- collide( 0, nearCallback ); if (!pause) { world- step( 0.005 ); } dJointGroupEmpty( contactgroup ); /* printf( "x=%.5f, y=%.5f, z=%.5f\n", pos[0], pos[1], pos[2] ); */ for (unsigned i=0; i geom_vec.size(); i++) { drawGeom( geom_vec[i] ); } } int main( int argc, char* argv[] ) { std vector GeomDataT* data_vec; // setup pointers to drawstuff callback functions dsFunctions fn; fn.version = DS_VERSION; fn.start = start; fn.step = simLoop; fn.command = 0; fn.stop = 0; fn.path_to_textures = "../../drawstuff/textures"; dInitODE2(0); world = new dWorld(); world- setGravity( 0, 0, -10.0f ); world- setERP( 0.5 ); world- setCFM( 1e-8f ); //world- setLinearDamping( 0.00001f ); //world- setAngularDamping( 0.0001f ); space = new dHashSpace( 0 ); dPlane *floor = new dPlane(*space, 0, 0, 1, 0 ); dJointGroupEmpty (contactgroup); { // Sphere dReal pos[3] = { 0.0, 0.0, 5.0 }; dReal radius = 0.5; dBody *body = new dBody( *world ); dMass m; m.setSphereTotal( 1.0, radius ); body- setMass( m ); body- setPosition( pos[0], pos[1], pos[2] ); dGeom *geom = new dSphere( *space, radius ); geom- setBody( *body ); GeomDataT* data = new GeomDataT; data- setColor( 1.0, 1.0, 0.0 ); geom- setData( (GeomDataT*)data ); body_vec.push_back( body ); geom_vec.push_back( geom ); data_vec.push_back( data ); } { // Box dReal pos[3] = { 0.0, 2.0, 5.0 }; dReal size[3] = { 1.0, 1.0, 1.0 }; dBody *body = new dBody( *world ); dMass m; m.setBoxTotal( 1.0, size[0], size[1], size[2] ); body- setMass( m ); body- setPosition( pos[0], pos[1], pos[2] ); dGeom *geom = new dBox( *space, size[0], size[1], size[2] ); geom- setBody( *body ); GeomDataT* data = new GeomDataT; data- setColor( 1.0, 1.0, 1.0 ); geom- setData( (GeomDataT*)data ); data_vec.push_back( data ); body_vec.push_back( body ); geom_vec.push_back( geom ); } { // Capsule dReal pos[3] = { 2.0, 0.0, 5.0 }; dReal radius = 0.5; dReal length = 1.0; dBody *body = new dBody( *world ); dMass m; m.setCapsuleTotal( 1.0, 1, radius, length ); body- setMass( m ); body- setPosition( pos[0], pos[1], pos[2] ); dGeom *geom = new dCapsule( *space, radius, length ); geom- setBody( *body ); GeomDataT* data = new GeomDataT; data- setColor( 1.0, 0.0, 1.0 ); geom- setData( (GeomDataT*)data ); data_vec.push_back( data ); body_vec.push_back( body ); geom_vec.push_back( geom ); } { // Cylinder dReal pos[3] = { 2.0, 2.0, 5.0 }; dReal radius = 0.5; dReal length = 1.0; dBody *body = new dBody( *world ); dMass m; m.setCylinder( 1.0, 1, radius, length ); body- setMass( m ); body- setPosition( pos[0], pos[1], pos[2] ); dGeom *geom = new dCylinder( *space, radius, length ); geom- setBody( *body ); GeomDataT* data = new GeomDataT; data- setColor( 0.0, 0.0, 1.0 ); geom- setData( (GeomDataT*)data ); data_vec.push_back( data ); body_vec.push_back( body ); geom_vec.push_back( geom ); } ////////////////////////////////////////////////////////////////// // simulation ////////////////////////////////////////////////////////////////// dsSimulationLoop( argc, argv, 320, 240, fn ); ////////////////////////////////////////////////////////////////// // destroy ////////////////////////////////////////////////////////////////// contactgroup.empty(); for (unsigned i=0; i body_vec.size(); i++) { delete body_vec[i]; } for (unsigned i=0; i geom_vec.size(); i++) { delete geom_vec[i]; } for (unsigned i=0; i data_vec.size(); i++) { delete data_vec[i]; } delete floor; delete space; delete world; return 0; }
https://w.atwiki.jp/bambooflow/pages/222.html
ODEでSTLフォーマット(3D)の物体を作る方法について ODEでSTLフォーマット(3D)の物体を作る方法についてSTL読み込みクラス ODE記述 drawstuff描画 参考 3D形状フォーマットにSTLというのがあるみたいです。 それを読み込んでODE上に生成する方法をメモします。 STLはアスキーとバイナリの2種類あるようですが、ここではバイナリのみについてです。 STLは三角形メッシュソリッドなので、ODEのAPITriMesh*で簡単に扱えます。 とりあえず、STL(バイナリ)フォーマットファイルを読み込むクラスを作ってみました。 ODE記述は、demo_moving_trimesh.cppを参考にしました。 サンプル(試しにBlenderのMonkeyを描画) draw_stl_20091202.zip STL読み込みクラス あんまりデバッグしてません。バグったらごめんなさい。 ヘッダ #include "StlReader.h" namespaceは、ode_utilsです。(using namespace ode_utils;) クラス ode_util StlReader V_TYPE,IDX_TYPE V_TYPEは、頂点座標の型(floatもしくはdouble) IDX_TYPEは、頂点座標番号の型(intもしくはdTriIndex) API StlReader( const char* file_name ) コンストラクタ()file_nameは、読み込むSTL(バイナリ)ファイル名 bool isCompleted() 処理が正しく行われるとtrueを返す const char* message() STLファイル処理の正常/エラーメッセージを返す const vertex_type* getVertices() 頂点座標配列の先頭ポインタを返す const index_type* getIndices() 頂点座標番号配列の先頭ポインタを返す int getVertexCount() 頂点座標の数を返す。 int getIndexCount() 頂点座標番号の数を返す。 vertex_typeはV_TYPEで与えたもの。 index_typeはODX_TYPEで与えたもの。 ODE記述 ode_util StlReader float,dTriIndex *stl; static dBodyID body; static dGeomID geom; void createMonkey( dWorldID world, dSpaceID space, dReal mass) { stl = new ode_util StlReader float,dTriIndex ("monkey_binary.stl"); body = dBodyCreate( world ); dTriMeshDataID data; data = dGeomTriMeshDataCreate(); dGeomTriMeshDataBuildSingle(data, stl- getVertices(), 3*sizeof(float), stl- getVertexCount(), stl- getIndices(), stl- getIndexCount(), 3*sizeof(dTriIndex)); geom = dCreateTriMesh(space, data, 0, 0, 0); dGeomSetData( geom, data ); dMass m; dMassSetTrimesh( m, mass, geom ); dGeomSetPosition( geom, -m.c[0], -m.c[1], -m.c[2] ); dMassTranslate( m, -m.c[0], -m.c[1], -m.c[2] ); dBodySetMass( body, m ); dGeomSetBody( geom, body ); } drawstuff描画 { const dReal* pos = dGeomGetPosition(geom); const dReal* rot = dGeomGetRotation(geom); for (int ii = 0; ii stl- getIndexCount()/3; ii++) { const dReal v[9] = { stl- getVertices()[stl- getIndices()[ii*3+0]*3 + 0], stl- getVertices()[stl- getIndices()[ii*3+0]*3 + 1], stl- getVertices()[stl- getIndices()[ii*3+0]*3 + 2], stl- getVertices()[stl- getIndices()[ii*3+1]*3 + 0], stl- getVertices()[stl- getIndices()[ii*3+1]*3 + 1], stl- getVertices()[stl- getIndices()[ii*3+1]*3 + 2], stl- getVertices()[stl- getIndices()[ii*3+2]*3 + 0], stl- getVertices()[stl- getIndices()[ii*3+2]*3 + 1], stl- getVertices()[stl- getIndices()[ii*3+2]*3 + 2] }; dsDrawTriangle( pos, rot, v[0], v[3], v[6], 1 ); } } 参考 http //www.hiramine.com/programming/3dmodelfileformat/stlfileformat.html
https://w.atwiki.jp/bambooflow/pages/172.html
コマを回す 文字どおり、コマ(独楽)をODE上で回してみました。 ジャイロ効果でコマが倒れません。 ODEでは、こういうのもシミュレーションできるみたいです。 さすがに、シミュレーションなので本物とはちょっと動作が違う感じはしますが。。。 demoプログラムにも demo_gyroscopic.cpp があります。 説明 キー a を押すと、コマが回転します。 キー b を押すと、コマを倒そうとする力を与えます。 プログラムを実行すると、コマが出現します。 この時点では、コマは垂直になっているので倒れません。 キー a を何回か押して、コマを回転させます。 次にキー b を押して、コマに横方向の力を与えてみてください。 コマを回さずにキー b を押すと、当然ながらそのまま倒れます。 コマの回転速度は10程度まで上げると、安定して倒れません。 全ソースコード #include ode/ode.h #include drawstuff/drawstuff.h #define MAX_CONTACTS 4 static dWorldID world; static dSpaceID space; static dJointGroupID contactgroup; // コマ static dBodyID body_koma; // Body コマ本体 static dGeomID geom_body; // Geom コマの胴体部分 static dGeomID geom_axis; // Geom 軸 static dReal body_mass = 10.0; // 質量 static dReal body_radius = 3.0; // 胴体の半径 static dReal body_length = 0.5; // 胴体の高さ static dReal axis_radius = 0.1; // 軸の半径 static dReal axis_length = 4.0; // 軸の長さ #ifdef dDOUBLE # define dsDrawCylinder dsDrawCylinderD # define dsDrawCapsule dsDrawCapsuleD #endif static void nearCallback( void *data, dGeomID o1, dGeomID o2 ) { dBodyID b1 = dGeomGetBody( o1 ); dBodyID b2 = dGeomGetBody( o2 ); if ( b1 b2 dAreConnectedExcluding( b1, b2, dJointTypeContact ) ) return; dContact contact[MAX_CONTACTS]; for ( int i=0; i MAX_CONTACTS; i++ ) { contact[i].surface.mode = dContactBounce | dContactSoftERP | dContactSoftCFM | dContactApprox1 | dContactSlip1 | dContactSlip2; //contact[i].surface.mode = dContactBounce | dContactApprox1; contact[i].surface.mu = 1.0; // dInfinity; contact[i].surface.bounce = 0.5; contact[i].surface.soft_erp = 0.2; contact[i].surface.soft_cfm = 1.0e-5; contact[i].surface.slip1 = 0.01; contact[i].surface.slip2 = 0.01; } int numc = dCollide( o1, o2, MAX_CONTACTS, contact[0].geom, sizeof( dContact ) ); if ( numc 0 ) { for ( int i=0; i numc; i++ ) { dJointID c = dJointCreateContact( world, contactgroup, contact+i ); dJointAttach( c, b1, b2 ); } } } // start simulation - set viewpoint static void start() { static float xyz[3] = { 0.0f, 8.0f, 5.0f }; static float hpr[3] = { -90.f, -20.f, 0.f }; //dAllocateODEDataForThread(dAllocateMaskAll); dsSetViewpoint( xyz, hpr ); } static void command( int cmd ) { if (cmd == a ) { // コマを回転させる printf( "a pushed\n" ); dBodyAddTorque( body_koma, 0.0, 0.0, 1000.0 ); //const dReal *rot = dBodyGetRotation( body_koma ); //printf( "a pushed %f, %f, %f\n", rot[0], rot[1], rot[2] ); } else if (cmd == b ) { // コマを倒す力をあたえる printf( "b pushed\n" ); dBodyAddTorque( body_koma, 1000.0, 0.0, 0.0 ); } } // simulation loop static void simLoop( int pause ) { dSpaceCollide( space, 0, nearCallback ); if (!pause) { dWorldStep( world, 0.005 ); } dJointGroupEmpty( contactgroup ); dsSetColor( 1.0, 1.0, 0.0 ); dsDrawCylinder( dGeomGetPosition( geom_body ), dGeomGetRotation( geom_body ), body_length, body_radius ); dsSetColor( 0.5, 0.5, 0.5 ); dsDrawCapsule( dGeomGetPosition( geom_axis ), dGeomGetRotation( geom_axis ), axis_length, axis_radius ); /* const dReal* pos = dBodyGetPosition( b_sphere ); printf( "x=%.5f, y=%.5f, z=%.5f\n", pos[0], pos[1], pos[2] ); */ const dReal *vel = dBodyGetAngularVel( body_koma ); printf( "v0=%.5f, v1=%.5f, v2=%.5f\n", vel[0], vel[1], vel[2] ); } int main( int argc, char* argv[] ) { // setup pointers to drawstuff callback functions dsFunctions fn; fn.version = DS_VERSION; fn.start = start; fn.step = simLoop; fn.command = command; fn.stop = 0; fn.path_to_textures = "../../drawstuff/textures"; dInitODE2(0); // creating world world = dWorldCreate(); space = dHashSpaceCreate( 0 ); contactgroup = dJointGroupCreate( 0 ); dCreatePlane( space, 0, 0, 1, 0 ); dWorldSetGravity( world, 0.0, 0.0, -9.8 ); dWorldSetERP( world, 0.8 ); dWorldSetCFM( world, 0.00001 ); // creating Koma body_koma = dBodyCreate( world ); dReal pos[3] = { 0.0, 0.0, 5.0 }; dBodySetPosition( body_koma, pos[0], pos[1], pos[2] ); // 胴(重り)設定 dMass m; dMassSetCylinder( m, 1.0, 1, body_radius, body_length ); dMassAdjust( m, body_mass ); dBodySetMass( body_koma, m ); geom_body = dCreateCylinder( space, body_radius, body_length ); dGeomSetBody( geom_body, body_koma ); // 軸設定 geom_axis = dCreateCapsule( space, axis_radius, axis_length ); dGeomSetBody( geom_axis, body_koma ); // starting simulation dsSimulationLoop( argc, argv, 320, 240, fn ); dJointGroupDestroy( contactgroup ); dSpaceDestroy( space ); dWorldDestroy( world ); return 0; }
https://w.atwiki.jp/vsync/pages/6.html
設計的な概念 物理計算の結果だけ所得し描画は他の自前のエンジンを使うのが、 多分本来の使い方。 はじめに付いてくるDrawstuffは座標系が特殊、 Zが画面上下方向になっている。 (重力方向と、カメラを変更すれば問題なし) ODEでのデータの扱い 大きく2つに分ける 1、動力学 body 2、衝突検出 geometry 1の動力学は質量、位置、姿勢か加速度等の動的なパラメータ。 2の衝突検出は幾何学形状、位置姿勢の静的な情報を持つ。 1,2の意味合いがちょっと混乱しやすい 感覚的に日本語と英語の意味合いがダブって見えるからか。 ヘッダファイルの定義を見た方がスッキリする。 struct dxBody; /* rigid body (dynamics object) */ struct dxGeom; /* geometry (collision object) */ それぞれ、データの所属する空間の呼び名がある。 1、World 2、Space
https://w.atwiki.jp/bambooflow/pages/224.html
ODEでXファイルからポリゴンを表示する ODEでXファイルからポリゴンを表示する使い方 ODE記述 drawstuff記述例 Xファイルは、DirectXの3Dフォーマットのようです。 多くの3Dモデリングツールがサポートしています。 たとえば、BlenderやMetasequoiaといったもの。 ここでは、Xファイル(アスキー形式)を読み込むためのクラスを作ってみました。 デバッグはそこそこしかしてません。 バグったらごめんなさい。 とりあえず、BleanderとMetasequoiaのXファイルは読めました。 BlenderでXファイルのエクスポート時、"Flip z"が有効になっているとポリゴンの表裏が反転してしまうので注意してください。 Metasequoia(無料版)の基本図形の1つ(立方体)を読み込んで表示したところ。 2つ生成して重ねています。 draw_x.zip 以前作成したSTL用のものとAPIを合わせて作っています。 使い方 ヘッダ #include "XReader.h" namespaceは、ode_utilsです。(using namespace ode_utils;) クラス ode_util XReader V_TYPE,IDX_TYPE V_TYPEは、頂点座標の型(floatもしくはdouble) IDX_TYPEは、頂点座標番号の型(intもしくはdTriIndex) API XReader( const char* file_name ) コンストラクタ、file_nameは、読み込むX(アスキー)ファイル名 bool isCompleted() Xファイルが正常に読み込まれるとtrueを返す const char* message() ファイル処理時の完了/エラーメッセージを返す const vertex_type* getVertices() 頂点座標配列の先頭ポインタを返す const index_type* getIndices() 頂点座標番号配列の先頭ポインタを返す int getVertexCount() 頂点座標の数を返す。 int getIndexCount() 頂点座標番号の数を返す。 vertex_typeはV_TYPEで与えたもの。 index_typeはODX_TYPEで与えたもの。 XReaderの仕様 Xファイル内で、 Mesh { ・・・ } のvertex数、頂点データ、index数、頂点インデックスのみを取得します。 template{}はすべて無視します。 MeshMaterialList、Material、Normalといったものもすべて無視です。 頂点インデックスは、3つ(三角形)もくしは4つ(四角形)の番号がセットになる場合があります。ODEのTriMeshは4つには対応していないので、4つセットのものは3つに分割しています。 字句解析には、無駄にre2cを使っています。(勉強がてら) なのでクラスの中は一部人には読めないものになっています。 ODE記述 #include "XReader.h" static dBodyID body1, body2; static dGeomID geom1, geom2; ode_utils XReader float,dTriIndex *mesh; mesh = new ode_utils XReader float,dTriIndex ("cube2.x"); if (!mesh- isCompleted()) { printf( "mesh error\n"); printf( "msg \n%s", mesh- message() ); printf("vertexCount=%d\n", mesh- getVertexCount()); printf("getIndexCount=%d\n", mesh- getIndexCount()); printf("vertices pointer=%d\n", mesh- getVertices()); printf("indices pointer=%d\n", mesh- getIndices()); exit(1); } body = dBodyCreate( world ); dTriMeshDataID data; data = dGeomTriMeshDataCreate(); dGeomTriMeshDataBuildSingle(data, mesh- getVertices(), 3*sizeof(float), mesh- getVertexCount(), mesh- getIndices(), mesh- getIndexCount(), 3*sizeof(dTriIndex)); geom = dCreateTriMesh(space, data, 0, 0, 0); dGeomSetData( geom, data ); dMass m; dMassSetTrimesh( m, mass, geom ); dGeomSetPosition( geom, -m.c[0], -m.c[1], -m.c[2] ); dMassTranslate( m, -m.c[0], -m.c[1], -m.c[2] ); dBodySetMass( body, m ); dGeomSetBody( geom, body ); drawstuff記述例 dsSetColor( 1, 1, 0 ); { const dReal* pos = dGeomGetPosition(geom1); const dReal* rot = dGeomGetRotation(geom1); const dTriIndex *index = mesh- getIndices(); for (int ii = 0; ii mesh- getIndexCount()/3; ii++) { const dReal v[9] = { mesh- getVertices()[index[ii*3+0]*3 + 0], mesh- getVertices()[index[ii*3+0]*3 + 1], mesh- getVertices()[index[ii*3+0]*3 + 2], mesh- getVertices()[index[ii*3+1]*3 + 0], mesh- getVertices()[index[ii*3+1]*3 + 1], mesh- getVertices()[index[ii*3+1]*3 + 2], mesh- getVertices()[index[ii*3+2]*3 + 0], mesh- getVertices()[index[ii*3+2]*3 + 1], mesh- getVertices()[index[ii*3+2]*3 + 2] }; dsDrawTriangle( pos, rot, v[0], v[3], v[6], 1 ); } }
https://w.atwiki.jp/bambooflow/pages/226.html
ジオメトリクラス ODE v0.11.1 ジオメトリクラスSphere(球体) Box(箱) Plane(面) Capsule(カプセル状) Cylinder(円柱) Ray(光線) Convex(凸型メッシュ) Triangle Mesh (TriMesh) Heightfield(ハイトフィールド) Geometry Transform 衝突を表現できる形状。 (ここでは、縦方向をzとして説明。) Sphere(球体) ボール上の物体。 半径の情報を持ちます。 dGeomID dCreateSphere (dSpaceID space, dReal radius); void dGeomSphereSetRadius (dGeomID sphere, dReal radius); dReal dGeomSphereGetRadius (dGeomID sphere); dReal dGeomSpherePointDepth (dGeomID sphere, dReal x, dReal y, dReal z); radius:半径 Box(箱) 縦、横、高さの大きさを持つ箱形状のもの。 dGeomID dCreateBox (dSpaceID space, dReal lx, dReal ly, dReal lz); void dGeomBoxSetLengths (dGeomID box, dReal lx, dReal ly, dReal lz); void dGeomBoxGetLengths (dGeomID box, dVector3 result); dReal dGeomBoxPointDepth (dGeomID box, dReal x, dReal y, dReal z); Plane(面) 地面や床を表現できます。 Capsule(カプセル状) カプセル状の物体。 円柱の長さと両側の球体の半径の情報を持ちます。 前のバージョン(v0.6より前)では、名前はCapped Cylinder (CCylinder)でした。 dGeomID dCreateCapsule (dSpaceID space, dReal radius, dReal length); void dGeomCapsuleSetParams (dGeomID capsule, dReal radius, dReal length); void dGeomCapsuleGetParams (dGeomID capsule, dReal *radius, dReal *length); dReal dGeomCapsulePointDepth (dGeomID capsule, dReal x, dReal y, dReal z); Cylinder(円柱) 円柱、もしくはタイヤ形状のもの。 長さと半径の情報を持ちます。 ODE0.11.1は、CapsuleとCylinderとは衝突しないようなので注意してください。 dGeomID dCreateCylinder (dSpaceID space, dReal radius, dReal length); void dGeomCylinderSetParams (dGeomID cylinder, dReal radius, dReal length); void dGeomCylinderGetParams (dGeomID cylinder, dReal *radius, dReal *length); Ray(光線) 線状の物体。 これは、ほかのジオメトリと少し異なります。 dGeomID dCreateRay (dSpaceID space, dReal length); void dGeomRaySetLength (dGeomID ray, dReal length); dReal dGeomRayGetLength (dGeomID ray); void dGeomRaySet (dGeomID ray, dReal px, dReal py, dReal pz, dReal dx, dReal dy, dReal dz); void dGeomRayGet (dGeomID ray, dVector3 start, dVector3 dir); void dGeomRaySetParams( dGeomID ray, int FirstContact, int BackfaceCull ); void dGeomRayGetParams( dGeomID ray, int *FirstContact, int *BackfaceCull ); void dGeomRaySetClosestHit( dGeomID ray, int ClosestHit ); int dGeomRayGetClosestHit( dGeomID ray ); レイキャストは、レイによって衝突を検出するメカニズムです。 Convex(凸型メッシュ) 凸型メッシュは、 すべての頂点の内角は180度未満 任意の直線との交点は2つ 頂点順でポリゴンを複数の三角メッシュに分割可 という特徴を持ちます。 凹型のメッシュの衝突検出はとても処理が重くなるので、ゲームのようにリアルタイムで衝突検出をさせる場合は、凸型メッシュ(コンベックス)に置き換えることをします。 ODE0.11.1現在では、衝突できるプリミティブは限られています。 dGeomID dCreateConvex (dSpaceID space, dReal *planes, unsigned planecount, dReal *points, unsigned pointcount, unsigned *polygons); void dGeomSetConvex (dGeomID g, dReal *planes, unsigned planecount, dReal *points, unsigned pointcount, unsigned *polygons); Triangle Mesh (TriMesh) 複数の三角形のポリゴン面を使って表現したもの。 凹型のメッシュ。 ↑demo_moving_trimeshより dTriMeshDataID dGeomTriMeshDataCreate(); void dGeomTriMeshDataDestroy (dTriMeshDataID g); void dGeomTriMeshDataBuild (dTriMeshDataID g, const void* Vertices, int VertexStride, int VertexCount, const void* Indices, int IndexCount, int TriStride, const void* Normals); 八面体の場合 VertexStride = 3*sizeof(dReal) TriStride = 3*sizeof(unsigned int) IndexCountはなぜか頂点数x3。頂点数じゃないの? 間違ってたらごめんなさい。 応用ポリゴン表示 Heightfield(ハイトフィールド) 三角メッシュと似ていますが、縦横の分割数と高さのみの情報を持つ立体形状を表現した図形。 ハイトフィールドを使うと、リアルな山やほかの地形を簡単に作成することができます。 ↑demo_heightfieldより dHeightfieldDataID dGeomHeightfieldDataCreate (); void dGeomHeightfieldDataDestroy (dHeightfieldDataID d) void dGeomHeightfieldDataBuildByte (dHeightfieldDataID d, const unsigned char *pHeightData, int bCopyHeightData, dReal width, dReal depth, int widthSamples, int depthSamples, dReal scale, dReal offset, dReal thickness, int bWrap); void dGeomHeightfieldDataBuildShort (dHeightfieldDataID d, const short *pHeightData, int bCopyHeightData, dReal width, dReal depth, int widthSamples, int depthSamples, dReal scale, dReal offset, dReal thickness, int bWrap); void dGeomHeightfieldDataBuildSingle (dHeightfieldDataID d, const float *pHeightData, int bCopyHeightData, dReal width, dReal depth, int widthSamples, int depthSamples, dReal scale, dReal offset, dReal thickness, int bWrap); void dGeomHeightfieldDataBuildDouble (dHeightfieldDataID d, const double *pHeightData, int bCopyHeightData, dReal width, dReal depth, int widthSamples, int depthSamples, dReal scale, dReal offset, dReal thickness, int bWrap); pHeightDataは、実際に使うときは1次元配列にしたほうがよいかも。 間違ってたらごめんなさい。 Geometry Transform ジオメトリのオフセットに使用できます。 dGeomGetPosition()で取得できる位置に対してジオメトリをずらすことができます。 The geom transform classes are deprecated. Use geom offsets instead. これはあまりお奨めしません。 もし物体を結合させるようなモデルを作りたいのであればdJointFixedを使えばいいし、 単にジオメトリの位置をずらしたいのであればdGeomOffsetを使ったほうが簡単だと思います。 描画するときオフセット分ずらして表示しないといけないので面倒です。 (個人的意見も入ってます) dGeomID dCreateGeomTransform (dSpaceID space); void dGeomTransformSetGeom (dGeomID g, dGeomID obj); dGeomID dGeomTransformGetGeom (dGeomID g); void dGeomTransformSetInfo (dGeomID g, int mode); int dGeomTransformGetInfo (dGeomID g);