unit SXFrame1;

interface

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


type
  TForm1 = class(TForm)
    QD: TDDDD;
    MainMenu1: TMainMenu;
    D1: TMenuItem;
    HAL1: TMenuItem;
    TnLHAL1: TMenuItem;
    RGBEmu1: TMenuItem;
    procedure QDInit(Sender: TObject; fNeedChange: Boolean);
    procedure TLHAL1Click(Sender: TObject);
    procedure HAL1Click(Sender: TObject);
    procedure RGBEmulation1Click(Sender: TObject);
    procedure QDCleanUp(Sender: TObject);
    procedure QDChangeDisplayMode(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    Scene:TSXScene;   //シーンオブジェクト

    Mesh:TSXMesh;     //メッシュ・・・メカ
    Mesh2:TSXMesh;    //メッシュ・・・ボール

    Root:TSXFrame;        //ルートフレーム
    MechFrame:TSXFrame;   //フレームオブジェクト
    ChildFrame:Array[0..5] of TSXFrame;
    Camera:TSXFrame;      //カメラ

    Lights:TSXLightGroup;

    Tex:TDDDDTexture;     //テクスチャ

    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単位]
  where:Integer;  //どのフレームを向けるかの計算用
begin


  //ベンチマーク
  QueryPerformanceCounter(t);
  e:=t * 1000 div Freq;
  Caption:=Format('LapTime:%d[ms]',[(t - PrevT) * 1000 div Freq]);
  PrevT:=t;

  //メックをまわせ〜
  MechFrame.SetOrientation(Root, NowRotY(Vector(0,0,1), e), Vector(0,1,0));
  MechFrame.SetTranslation(Root, NowAdd(Vector(0,-1,3),NowRotX(Vector(0,0,1), e)));

  //天井に浮いている矢印の向きを変える
  where := (e div 1000) mod 6;
  if where <> 5 then
    ChildFrame[5].LookAt(ChildFrame[where], Vector(0,0,0), MechFrame, 0)
  else
    ChildFrame[5].LookAt(MechFrame, Vector(0,0,0), MechFrame, 0);

  //スポットライトの位置・向きを、上空に浮いてる矢印にあわせる
  Lights[1].FitFrame(ChildFrame[5]);


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


  //描画
  asm FINIT; end;
  QD.D3DDevice.BeginScene;

  asm FINIT; end;
  Scene.Render(Root);


  asm FINIT; end;
  QD.D3DDevice.EndScene;

  asm FINIT; end;





  QD.Flip;

  Done:=False;
end;


//初期化
procedure TForm1.QDInit(Sender: TObject; fNeedChange: Boolean);
var
  i:Integer;

  //デバッグ用
  procedure PrintMatrix(m:D3DMatrix);
  begin
    DDDD_PutDebugMessage(Format('| %f %f %f %f |',[m._11, m._12, m._13, m._14]));
    DDDD_PutDebugMessage(Format('| %f %f %f %f |',[m._21, m._22, m._23, m._24]));
    DDDD_PutDebugMessage(Format('| %f %f %f %f |',[m._31, m._32, m._33, m._34]));
    DDDD_PutDebugMessage(Format('| %f %f %f %f |',[m._41, m._42, m._43, m._44]));
  end;

begin
  //Intialization部だと旨く動いてくれないのさ〜
  NowInit;

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

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

  //シーンの作成
  Root:=TSXFrame.Create(Nil);
  MechFrame:=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));

  //テクスチャの読み込み
  Tex:=TDDDDTexture.Create(QD, 256,256, 0, '');
  Tex.LoadBMP('texture.bmp');
  MechFrame.Texture:=Tex;
  MechFrame.BlendMode:=sxbAdd;  //加色モードで描画


  //矢印の作成
  Mesh2:=TSXMesh.Create(QD);
  Mesh2.LoadFromFile('arrow.sx');
  For i:=0 to 5 do begin
    ChildFrame[i]:=TSXFrame.Create(MechFrame);
    ChildFrame[i].SetTranslation(MechFrame, NowRotY(Vector(0,0,i*0.5+2),i*4096 div 5));
    ChildFrame[i].SetOrientation(MechFrame, NowRotY(Vector(0,0,1),i*4096 div 5), Vector(0,1,0));
    ChildFrame[i].Mesh:=Mesh2;
  end;



  //他のオブジェクトをグルグル指して回るフレームだけ、メックの上空2.0のところに移動
  ChildFrame[5].SetTranslation(MechFrame, Vector(0,2,0));
  //↓のコメントはずすと、上に浮かんでる矢印の姿勢が良く分かります
  //ChildFrame[5].Mesh:=Mesh;

  {
  //Parentの動的変更のテスト
  PrintMatrix(ChildFrame[5].WorldMatrix);
  ChildFrame[5].Parent:=ChildFrame[1];
  PrintMatrix(ChildFrame[5].WorldMatrix);
  PrintMatrix(NowCompositeMatrix(ChildFrame[5].Matrix, ChildFrame[1].WorldMatrix));
  }


  //カメラの設定
  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;    //ここ、忘れがちなので注意

  //スポットライト、上に浮かんでる矢印につける
  Lights[1].Params.dvRange:=10000;
  Lights[1].SetupDiffuse(1.0, 0.0, 0.0);
  Lights[1].SetupSpecular(1.0, 0.0, 0.0);
  Lights[1].SetupSpot(Vector(0,0,0), Vector(0,0,1), Pi/3, Pi/3, 1.0);
  Lights[1].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;


//T&L HALを許可
procedure TForm1.TLHAL1Click(Sender: TObject);
begin

  TMenuItem(Sender).Checked:=True;
  QD.D3DOptions:=[MakeZBuffer];
  QD.WindowMode(640,480);

end;

//HAL許可
procedure TForm1.HAL1Click(Sender: TObject);
begin

  TMenuItem(Sender).Checked:=True;
  QD.D3DOptions:=[MakeZBuffer,DontUseTnLHAL];
  QD.WindowMode(640,480);

end;

//RGBエミュ
procedure TForm1.RGBEmulation1Click(Sender: TObject);
begin
  TMenuItem(Sender).Checked:=True;
  QD.D3DOptions:=[MakeZBuffer,DontUseTnLHAL,DontUseHAL];
  QD.WindowMode(640,480);

end;

//設定の保存
procedure TForm1.QDCleanUp(Sender: TObject);
begin
  Mesh.KeepVB;
  Mesh2.KeepVB;

end;

//設定の再構築
procedure TForm1.QDChangeDisplayMode(Sender: TObject);
begin
  Scene.Recover;
  Mesh.RecoverVB;
  Mesh2.RecoverVB;
  Lights.Recover;

  //レンダリングステートの設定
  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) );

end;

end.
