unit SXQuaternion1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  dddd,
  DDraw, OLE2, d3d,d3dcaps,d3dtypes,d3drm,d3drmobj,d3drmwin,d3drmdef,
  SXLib, s_mathpack, Menus, Ddidex;

type
  TForm1 = class(TForm)
    QD: TDDDD;
    Joy: TDDIDEX;
    procedure QDInit(Sender: TObject; fNeedChange: Boolean);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    Scene:TSXScene;   //シーンオブジェクト

    Mesh:TSXMesh;     //メッシュ・・・メカ

    Root:TSXFrame;        //ルートフレーム
    MechFrame:TSXFrame;   //左のメカ格納用フレーム
    RefFrame:TSXFrame;    //右のメカ = 回転後のイメージ表示用
    Camera:TSXFrame;      //カメラ

    Lights:TSXLightGroup;

    RotX,RotY:Integer;    //各軸にどれだけメックをまわしたか
    QuatMech,QuatRef:TQuaternion; //左右のメカの姿勢
    QuatMechAtStart:TQuaternion;  //補完開始時のメカの姿勢

    Slerping:Boolean;     //姿勢補間中
    StartSlerpT:Int64;    //Slerpの開始時刻
    procedure AppIdle(Sender:TObject; var Done:Boolean);
  end;

var
  Form1: TForm1;
  Freq:Int64;      //パフォーマンスカウンタの周波数
  PrevT:Int64 = 0;

implementation

{$R *.DFM}

//メインループ
procedure TForm1.AppIdle(Sender: TObject; var Done: Boolean);
var
  t:Int64;  //現在の時刻
  e:Integer; //現在の時刻[ms単位]
  matRef,MatMech:D3DMATRIX;
begin
  //ベンチマーク(説明書きを出したいので、表示しない)
  QueryPerformanceCounter(t);
  e:=t * 1000 div Freq;
  //Caption:=Format('LapTime:%d[ms]',[(t - PrevT) * 1000 div Freq]);
  PrevT:=t;


  {マウスの移動にあわせて回転}

  Joy.Scan(DI_MOUSE);

  //マウスが横にドラッグされたら、Y軸回り
  RotY:=RotY-Joy.AStick.X*20;
  QuatRef:=NowQuatFromRotation(Vector(0,1,0), RotY);
  //マウスが縦にドラッグされたら、X軸回り
  RotX:=RotX-Joy.AStick.Y*20;
  QuatRef:=NowNormalizeQuat(NowMulQuat(QuatRef, NowQuatFromRotation(Vector(1,0,0), RotX)));


  //クォータニオンから行列を作って、セットする
  RefFrame.SetOrientationMatrix(Root, NowMatrixFromQuat(QuatRef));



  { 左ボタンがクリックさせてたら、Slerpによって右側のメックの姿勢を滑らかに変更 }
  if Joy.Buttons[0] And (not Slerping) then begin
    Slerping:=True;
    StartSlerpT:=t;
    QuatMechAtStart:=QuatMech;
  end;

  //姿勢の補間…１秒で完了
  if Slerping then begin

    if t - StartSlerpT < Freq then begin
      //Slerpで補間
      //QuatMech:=NowSlerpQuat(QuatMechAtStart,QuatRef, (t - StartSlerpT) / Freq);

      //各要素を普通に線形補間…なんかこれでもちゃんと補間できるんスけど…(^^;)
      NowLerp(Quatmech.W, QuatMechAtStart.W, QuatRef.W, (t - StartSlerpT) / Freq);
      NowLerp(Quatmech.X, QuatMechAtStart.X, QuatRef.X, (t - StartSlerpT) / Freq);
      NowLerp(Quatmech.Y, QuatMechAtStart.Y, QuatRef.Y, (t - StartSlerpT) / Freq);
      NowLerp(Quatmech.Z, QuatMechAtStart.Z, QuatRef.Z, (t - StartSlerpT) / Freq);
      QuatMech:=NowNormalizeQuat(QuatMech);

      //クォータニオンから行列を作って、セットする
      MechFrame.SetOrientationMatrix(Root, NowMatrixFromQuat(QuatMech));
    end else begin
      //時間切れ
      Slerping:=False;
    end;

  end;


  //クリア
  Scene.Clear(D3DCLEAR_TARGET + D3DCLEAR_ZBUFFER, $FF000000, 1.0, 0);


  //描画
  QD.D3DDevice.BeginScene;

  Scene.Render(Root);

  QD.D3DDevice.EndScene;


  //デバッグ
  Caption:=Format('Start:(%f,%f,%f,%f) - Curr:(%f,%f,%f,%f)',[
    QuatMechAtStart.w, QuatMechAtStart.x, QuatMechAtStart.y, QuatMechAtStart.z,
    QuatRef.w, QuatRef.x, QuatRef.y, QuatRef.z]);



  QD.Flip;

  Done:=False;
end;

//初期化
procedure TForm1.QDInit(Sender: TObject; fNeedChange: Boolean);
begin
  //Intialization部だと旨く動いてくれないのさ〜
  NowInit;

  //パフォーマンスカウンタの周波数を得る
  QueryPerformanceFrequency(Freq);

  //シーンオブジェクトの生成
  Scene:=TSXScene.Create(QD);

  //シーンの作成
  Root:=TSXFrame.Create(Nil);
  MechFrame:=TSXFrame.Create(Root);
  RefFrame:=TSXFrame.Create(Root);

  //メックの読み込み
  Mesh:=TSXMesh.Create(QD);
  Mesh.LoadFromFile('hero.sx');

  MechFrame.Mesh:=Mesh;
  //メックをちょっと大きく表示
  MechFrame.MeshMatrix:=NowCompositeMatrix(NowRotYMatrix(2048), NowScaleMatrix(1.3,1.3,1.3));

  RefFrame.Mesh:=Mesh;
  RefFrame.MeshMatrix:=MechFrame.MeshMatrix;


  //姿勢制御用のクォータニオンの初期値設定
  QuatMech:=Quaternion(1, 0,0,0);
  QuatRef:=Quaternion(1, 0,0,0);  
  RotX:=0;
  RotY:=0;
  Slerping:=False;

  //メカ格納用フレームと、回転後イメージ表示フレームをずらす
  MechFrame.SetTranslation(Root, Vector(+1.5,0,5));
  RefFrame.SetTranslation(Root, Vector(-1.5,0,5));

  //カメラの設定
  Camera:=TSXFrame.Create(Root);
  Camera.SetTranslation(Root, Vector(0,0,-3));
  Camera.SetOrientation(Root, Vector(0,0,1), Vector(0,1,0));

  Scene.CameraFrame:=Camera;


  //投影行列の設定
  //画角、画面の縦/横比、前方／後方クリッビング面までの距離を指定
  Scene.SetProjection(Pi/4, ClientHeight/ClientWidth,   1.0, 5000.0);

  //ビューポートの設定
  Scene.SetViewPort(0,0,ClientWidth,ClientHeight);



  //ライトの設定
  Lights:=TSXLightGroup.Create(QD, 2);

  //環境光と、正面からの有向光源
  Lights[0].SetupDirectional(Vector(0,0,1));
  Lights[0].SetupDiffuse(0.6, 0.6, 0.6);
  Lights[0].SetupSpecular(1.0, 1.0, 1.0);
  Lights[0].SetupAmbient(0.3,0.3,0.3);
  Lights[0].Enabled:=True;    //ここ、忘れがちなので注意

  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_LIGHTING, Integer(True));






  //レンダリングステートの設定
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_LIGHTING, Integer(True));
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_ZENABLE, Integer(True) );
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_COLORVERTEX, Integer(True) );
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_SPECULARENABLE, Integer(True) );

  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_SPECULARMATERIALSOURCE, Integer(D3DMCS_COLOR2));
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_DIFFUSEMATERIALSOURCE, Integer(D3DMCS_COLOR1));
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_EMISSIVEMATERIALSOURCE, Integer(D3DMCS_MATERIAL));
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_AMBIENTMATERIALSOURCE, Integer(D3DMCS_MATERIAL));

  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_DITHERENABLE, Integer(True));
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_SHADEMODE, Integer(D3DSHADE_GOURAUD));

  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_NORMALIZENORMALS, Integer(True) );

  Application.OnIdle:=AppIdle;


end;

end.
