unit SXView1;

interface

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

type
  TForm1 = class(TForm)
    QD: TDDDD;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    R1: TMenuItem;
    Exit1: TMenuItem;
    OpenDialog1: TOpenDialog;
    N1: TMenuItem;
    N2: TMenuItem;
    Property1: TMenuItem;
    S1: TMenuItem;
    F1: TMenuItem;
    G1: TMenuItem;
    N3: TMenuItem;
    N4: TMenuItem;
    procedure QDInit(Sender: TObject; fNeedChange: Boolean);
    procedure Exit1Click(Sender: TObject);
    procedure R1Click(Sender: TObject);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Property1Click(Sender: TObject);
    procedure F1Click(Sender: TObject);
    procedure G1Click(Sender: TObject);
    procedure N3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    FrRoot,FrMesh,FrCamera:TSXFrame; //フレーム
    Lights:TSXLightGroup;   //ライト
    Mtrl:TSXMaterial;       //マテリアル
    Scene:TSXScene;         //全体を統括するオブジェクト

    Mesh:TSXMesh;           //表示するメッシュ

    procedure LoadSX(fileName:String);
    procedure AppIdle(Sender:TObject; var Done:Boolean);
  end;


var
  Form1: TForm1;

implementation

{$R *.DFM}

//  SXの読み込み
procedure TForm1.LoadSX(fileName:String);
begin
  Mesh:=TSXMesh.Create(QD);
  Mesh.LoadFromFile(fileName);
  Mesh.Optimize;

  //メッシュの取り付け
  FrMesh.Mesh:=Mesh;

  //初期位置に戻す
  FrMesh.SetOrientation(FrRoot, Vector(0,0,1), Vector(0,1,0));
  FrCamera.SetTranslation(FrRoot, Vector(0,0,-10));
end;


var
  PrevT:DWord = 0;

procedure TForm1.AppIdle(Sender:TObject; var Done:Boolean);
var
  t:DWord;
begin

  if Mesh = Nil then
    exit;

  //ベンチマーク
  t:=TimeGetTime;
  Caption:=Format('LapTime:%d[ms]',[t - PrevT]);
  PrevT:=t;


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


  asm FINIT; end;
  QD.D3DDevice.BeginScene;
  asm FINIT; end;


  Scene.Render(FrRoot);   //FrRoot以下を描画


  asm FINIT; end;
  QD.D3DDevice.EndScene;
  asm FINIT; end;


  QD.Flip;

  Done:=False;
end;


//初期化
procedure TForm1.QDInit(Sender: TObject; fNeedChange: Boolean);
begin
  NowInit;

  //各オブジェクトの生成
  Scene:=TSXScene.Create(QD);           //シーンオブジェクト
  Mtrl:=TSXMaterial.Create(QD);         //マテリアルお便利設定オブジェクト
  Lights:=TSXLightGroup.Create(QD, 1);  //ライト管理オブジェクト、ライトは一個しかつけない。

  FrRoot:=TSXFrame.Create(Nil);         //一番根っこのフレーム
  FrCamera:=TSXFrame.Create(Nil);       //カメラ
  FrMesh:=TSXFrame.Create(FrRoot);      //メッシュを格納するフレーム



  //メッシュはロードされるまでNil
  Mesh:=Nil;



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

  //ビューボリュームに関する情報の設定、画角は横120度、縦90度
  Scene.SetProjection(Pi/4, ClientHeight/ClientWidth, 1.0, 5000.0);

  //カメラフレームの設定
  Scene.CameraFrame:=FrCamera;
  FrCamera.SetTranslation(FrRoot, Vector(0,0,-10)); //後ろに10下がる

  //メッシュフレームの設定…特になし



  //マテリアルの設定
  Mtrl.SetupSpecularPower(3.0); //スペキュラ強度だけ設定、あとはデフォの0.0

  //ライトの設定
  Lights[0].SetupDirectional(Vector(0,0,1));                      //平行光源、Z+の向きに照射
  Lights[0].SetupColors(1.0,1.0,1.0, 0.5,0.5,0.3, 0.0,0.0,0.0);   //黄色っぽいスペキュラ
  Lights[0].Enabled:=True;


  //レンダリングステートの設定
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_LIGHTING, Integer(True));
  QD.D3DDevice.SetRenderState( D3DRENDERSTATE_AMBIENT, $FF808080);
  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_DITHERENABLE, Integer(True));


  Application.OnIdle:=AppIdle;
end;


//終了
procedure TForm1.Exit1Click(Sender: TObject);
begin
  Application.Terminate;
end;

//ファイルダイアログを開く
procedure TForm1.R1Click(Sender: TObject);
begin
  Application.OnIdle:=Nil;

//  if VBuf <> Nil then
//    VBuf.Release;


  with OpenDialog1 do begin
    if Execute then begin
      if Mesh <> Nil then
        Mesh.Free;
      LoadSX(FileName);
      Property1.Enabled:=True;
    end;
  end;

  Application.OnIdle:=AppIdle;

end;

var
  PrevX,PrevY:Integer;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  dx,dy:Integer;
  mat:D3DMATRIX;
begin
  if (Mesh = Nil) Or (not Assigned(Application.OnIdle)) then
    Exit;

  dx:=X - PrevX;
  dy:=Y - PrevY;

  PrevX:=X;
  PrevY:=Y;

  if ssLeft in Shift then begin
    //カメラを遠く・近く
    FrCamera.SetTranslation(FrCamera, Vector(0,0, dy*0.1));
  end;

  //モデルを回転(Shiftがおされてたら、30度単位で回転)
  if ssRight in Shift then begin
    mat:=FrMesh.GetTransform(FrRoot);
    if ssShift in Shift then begin
      if dx < -10 then
        dx := -4096 div 12
      else if dx > 10 then
        dx := 4096 div 12
      else begin
        PrevX:=PrevX - dx;
        dx := 0;
      end;

      if dy < -10 then
        dy := -4096 div 12
      else if dy > 10 then
        dy := 4096 div 12
      else begin
        PrevY:=PrevY - dy;
        dy := 0;
      end;

      mat:=NowCompositeMatrix(mat,NowCompositeMatrix(NowRotYMatrix(-dx),NowRotXMatrix(-dy)));

    end else
      mat:=NowCompositeMatrix(mat,NowCompositeMatrix(NowRotYMatrix(-dx*4),NowRotXMatrix(-dy*4)));
    
    FrMesh.SetTransform(FrRoot, mat);
  end;

  //原点に戻す
  if (ssMiddle in Shift) Or ( (ssLeft in Shift) And (ssRight in Shift) )then begin
    FrMesh.SetOrientation(FrRoot, Vector(0,0,1), Vector(0,1,0));
    FrCamera.SetTranslation(FrRoot, Vector(0,0,-10));
  end;


end;

//ファイル情報の表示
procedure TForm1.Property1Click(Sender: TObject);
begin
  MessageDlg(
    Format('頂点数 ... %d  頂点インデクス数 ... %d',[Mesh.VertexCount,Mesh.IndexCount]),
    mtInformation,
    [mbOK],
    0);

end;


//フラットシェーディングに
procedure TForm1.F1Click(Sender: TObject);
begin
  TMenuItem(Sender).Checked:=True;
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_SHADEMODE, Integer(D3DSHADE_FLAT));
end;

//グーロシェーディングに
procedure TForm1.G1Click(Sender: TObject);
begin
  TMenuItem(Sender).Checked:=True;
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_SHADEMODE, Integer(D3DSHADE_GOURAUD));
end;

//スペキュラ
procedure TForm1.N3Click(Sender: TObject);
begin
  TMenuItem(Sender).Checked:=not TMenuItem(Sender).Checked;
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_SPECULARENABLE, Integer(TMenuItem(Sender).Checked));
end;

end.
