Spr1の使い方/マニュアル

目次

準備

ソースを読むために,基本のクラスをいくつか覚える必要があります.

UTRef<T> 参照カウンタと参照ポインタ

参照カウンタ式のスマートポインタ. UTRef<クラス名>が参照ポインタ.UTRefCountが参照カウンタです. UTRefCountを基本クラスにもつクラスAがあるとして,次にように使えます.

UTRef<A> p;
for(int i=0; i10; ++i){
  p = new A;
  p->Aのメソッド()
}

new したのにdeleteしていません.さらに,pに10回もnewして代入しています. でもメモリリークは起きません. 詳しくは参照カウンタを見てください.

線形代数

Spr::Vec3f ( Spr::TVec3<float> ) / Spr::Vec3d ( Spr::TVec3<double> )

  • 3次元のベクトル。
  • DirectX周りではVec3f、物理エンジン周りではVec3dが使われてる.物理には精度が必要なんだ.
Spr::Vec3f vec(x, y, z); //初期化。
Spr::Vec3f vec= Spr::Vec3f(x, y, z); //上と同じ意味。
vec[0]= x2; //各要素に代入。添え字は0,1,2。
Spr::Vec3f vec2= vec+Spr::Vec3f(x, y, z); //足し算とか。

size()はベクトルの次元.norm()がノルム(大きさ).square()がノルム^2. unit()で正規化(0割注意)..X() .Y() .Z() で要素にアクセス.
実は任意サイズのベクトルclass PTM::TVector<N, T>の親戚.他にもVVectorとかEVectorがある.

Spr::Affinef ( Spr::TAffine<float> )

  • Affine行列(4*4行列)。
Spr::Affinef trn= Spr::Affinef::Trn(x, y, z); //平行移動を表すAffine行列。
Spr::Affinef rot= Spr::Affinef::Rot(rad, 'x'); //X軸周りの回転を表すAffine行列。
Spr::Affinef aff= trn*rot; //Affine行列同士の掛け算。
aff.Pos()= Spr::Vec3f(x, y, z); //平行移動成分に代入。
aff.Rot()= Spr::Matrix3f(xx,xy,xz,yx,yy,yz,zx,zy,zz); //回転成分の代入.
aff[0][0]= 1.0; //各要素に代入。添え字は0,1,2,3.
aff.Ex() = Vec3f(1,0,0); 回転行列部のx軸基底(縦ベクトル)に代入.
aff.ExX() = 2.0f; Ex().X() と同じ.
Spr::Vec3f vec1= …;
Spr::Vec3f vec2= aff*vec1; //	3次ベクトルをAffine変換.vec1[3]は1と仮定して演算.
LookAt(to), LookAt(to, up), LookAtGL(to), LookAtGL(to, up)
toを見て,upが上になるような視点行列になるように自分を回転させる.
static Unit(), Trn (px, py, pz), Rot(th, axis), Scale (sx, sy, sz)
単位行列,平行移動行列,回転行列,拡大行列を返す.Affinef af=Affinef::Trn(1,2,3) のように使う.
static ProjectionGL (Vec3f screen, Vec2f size, front=1.0f, back=10000.0f), ProjectionD3D(..)
OpenGL, Direct3Dのプロジェクション行列を返す.screenが視点から見たスクリーンの位置,sizeがスクリーンのサイズ.front, back はクリッピングプレーン.

逆行列inv(), 転置trans(), 行列式det(), 方程式の解 solve(), ガウスの消去法 gauss(), コレスキー法 cholesky()
実は任意サイズの行列PTM::TMatrixCol< H, W, T, Z, U >の親戚.他にもVMatrixColEMatrixColがある.

Spr::Quaternionf (TQuaternion<float>)

  • Quaternion 4元数.
  • 回転をあらわす方法の一つ.Matrix3fの変わりに使える.
    Qutaternionf qt;
    qt.from_matrix(Matrix3f::Rot(Rad(30), 'y')); // 行列から初期化できる.
    Vec3f r = qt.rotation(); // 回転ベクトル(unit:回転軸, norm:回転角)
    Vec3f p = qt * Vec3f(1,0,0); // 位置ベクトルを回転させる.

4次元ベクトル TVector<4, float> の親戚.

シーングラフ上のオブジェクト

SpringheadのSceneGraphは次のような構造になっている.

+Scene         //  シーンオブジェクト(FWD3DApp app; (Viewが持っています)のメンバ)
 +Frame        //  物のデータ
  +Frame
   +CDMesh     //  衝突判定用
   +D3Mesh     //  描画用
   +GRMesh     //  元のメッシュデータ
  +Light       //  光源
 +BehaviorEngines      //  物の振る舞いについての記述
  +PHSolidContainer    //  剛体の入れ物,自由物体の積分もここでやっています.
   +PHSolid solid1
   +PHSolid solid2
   +PHSolid solid3
  +PHContactEngine     //  接触エンジン
   +floor
   +solid1
   +solid2
   +solid3
  +PHJointEngine       //  ジョイントエンジン
   +PHJoint root       //  ルートジョイント
    +PHJoint joint1    //  ジョイント1
     +solid1
     +joint2
      +solid2
      +joint2
       +solid3

これらへのポインタを手に入れて,メンバメソッドを呼び出したり,メンバ変数を 書き換えることで,物を動かしたり,関節に力を加えたり出来る.

剛体に力を加える.

力を加えるときは剛体に力を加えます.

Vec3d f, Vec3d r;
PHSolid* so;
scene->FindObject(so, "solid1"); // 名前からオブジェクトのポインタを得る
if (so) so->AddForec(f, r); //< 力fを 位置r(World系) に加える

どこで呼び出すか

BehaviorEngineには,実行の優先順位があり,SceneGraph/SGBehaviorEngine.h にあるように,

   enum SGBehaviorPriority{
       //初期化モジュール
       SGBP_INITIALIZER            = 100,
       SGBP_CLEARFORCE,
       //力生成モジュール
       SGBP_FORCEGENERATOR         = 200,
           SGBP_GRAVITYENGINE,
           SGBP_COLLISIONENGINE,
           SGBP_PENALTYENGINE,
       //積分の対象
       SGBP_DYNAMICALSYSTEM        = 300,
           SGBP_JOINTENGINE,
           SGBP_SOLIDCONTAINER,
       //
       SGBP_MOTION                 = 400,
           SGBP_CHANGEOBJECT,
           SGBP_CONSTRAINT,
       //他のエンジンのリスナ
       SGBP_LISTENER               = 500,
           SGBP_CONTACTENGINE,
           SGBP_STICKENGINE,
           SGBP_WARPENGINE,
   };

となっています.scene には,全部のエンジンを呼び出す

   /// シーンの時刻を進める ClearForce(); GenerateForce(); Integrate(); と同じ
   void Step();

以外に,個別に呼び出す

   /// シーンの時刻を進める(力のクリア)
   void ClearForce();
   /// シーンの時刻を進める(力の生成)
   void GenerateForce();
   /// シーンの時刻を進める(力と速度を積分して,速度と位置を更新)
   void Integrate();

があります.プログラムで力を加えるためには,個別に呼び出して, タイミングよく(たとえばGenerateForce()の直後で)力を加えなければなりません.

DynaHapticが使っている,FWD3DApp app の app.Step()は, scene->Step()を呼び出してしまいます. FWD3DAppを派生させてMyAppを作ってFWD3DAppの代わりに使い, MyApp::Step()の中で,個別に呼び出したり,力を加えるのが良いと思います.

BehaviorEngineのポインタの取得

PHContactEngine* ce=NULL;
scene->GetBehaviors().Find(ce);
if(ce) ce->bDraw = true;

これで,ce にシーングラフのなかの PHContactEngineを取得して,bDraw フラグを true にしています.

関節エンジンも,

PHJointEngine* je=NULL;
scene->GetBehaviors().Find(je);

でとれますが,目当てのJointへのポインタを手に入れるためには,そのあと階層を追って, GetChildObject(int)を呼び出さなければなりません.

こんなときは,名前をつけておくと簡単です.特定の名前のジョイントと接続するには,

PHJoint1D* jnt1;
scene->FindObject(jnt1, "jnt1");
PHJointPid* pid1 = PHJointPid::Find(jnt1, scene);
static double th=0.0;
th = th += 0.1;
pid1->goal = Rad(th);

のようにすればできます.