unit SXlib8;

//Simplified X 取り扱いルーチン for DGCarad8

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  DGCarad8, d3d8, s_mathpack,QDArc;//,s_3DNow;



(* 新SX形式について

    name   | ofs          | length                  | comment
----------------------------------------------------------------------------------------------------
 Signature |            0 |                      16 | 'Simplified_X02  '
 nVextices |           16 |                       2 | number of vertices
 nIndices  |           18 |                       2 | number of indices for vertices
 dwFVF     |           20 |                       4 | flags for flexible vertex format
 VertexSize|           24 |                       4 | required bytes per vertex
 DXVersioin|           28 |                       4 | DirectXVersion(for Identification of FVF)

 (padding)

 Vertices  |          256 |  VertexSize * nVertices | vertices
 Indices   | 256+Vertices |           2 * nIndices  | indices for vertices


 DXVersionは将来のDirectXアップグレードへの布石
 *)


(* SX形式について(古い)

    name   | ofs          | length         | comment
----------------------------------------------------------------------------
 Signature |            0 |             16 | 'Simplified_X00  '
 nVextices |           16 |              2 | number of vertices
 nIndices  |           18 |              2 | number of indices for vertices

 (padding)

 Vertices  |          256 | 40 * nVertices | vertices(TSXVertex)
 Indices   | 256+Vertices |  2 * nIndices  | indices for vertices


　SX01形式は、未公開バージョン、サポートしない
*)


const

//SXファイル識別子
SX00_SIGNATURE:String[16] = 'Simplified_X00  ';  //古いSX
SX02_SIGNATURE:String[16] = 'Simplified_X02  ';  //新SX、フレキシブル頂点フォーマット(DX8)対応

DXVER_FOR_FVF:String[4] = '0800';                //FVF内容識別用のDirectXのバージョン


//想定するテクスチャステージの上限
SX_MAX_TSS = 7;


//TSXVertex用、FVFフラグ
FVF_SXVERTEX:DWord = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_DIFFUSE Or D3DFVF_SPECULAR Or D3DFVF_TEX1);

//TSXVertex、ワンスキン用、FVFフラグ(スペキュラ無し)
FVF_SXV_ONESKIN:DWord = (D3DFVF_XYZB1 Or D3DFVF_NORMAL Or D3DFVF_DIFFUSE Or D3DFVF_TEX1);

//TSXVertex、テクスチャ２枚用(スペキュラ無し)
FVF_SXVERTEX2T:DWord = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_DIFFUSE Or D3DFVF_TEX2);

//定義済みブレンドモード
  sxbDefault = 0;     //普通、透けない
  sxbAdd = 1;         //加算合成
  sxbAlpha = 2;       //半透明
  sxbMulNeg = 3;      //エセ減算合成(反転してからMul)
  sxbMul = 4;         //乗算合成
  sxbColorKey = 5;    //αテストだけ、透けない


type


TSXMesh = class;
TSXFrame = class;
TSXFrameList = class;
TSXScene = class;
TSXRenderingQueue = class;
TSXTextWriter = class;
TSXStateRestorer = class;


//SX形式のための頂点データ
TSXVertex = packed record

  Case Integer of
    0: (
      x,y,z:Single;     //頂点
      nx,ny,nz:Single;  //法線
      diffuse:D3DCOLOR;   //ディフューズ
      specular:D3DCOLOR;  //スペキュラ
      tu,tv:Single;       //テクスチャ座標
      );
    1:(
      pos:D3DVector;
      normal:D3DVector;
      );
end;

PSXVertex = ^TSXVertex;

TSXVertexArray = Array [0..$FFFFFF] of TSXVertex;
PSXVertexArray = ^TSXVertexArray;



//マルチテクスチャ対応SXVertex
TSXVertex2T = packed record
  Case Integer of
    0: (
      x,y,z:Single;     //頂点
      nx,ny,nz:Single;  //法線
      diffuse:D3DCOLOR;   //ディフューズ
      tu0,tv0:Single;       //テクスチャ座標
      tu1,tv1:Single;       //テクスチャ座標(2)
      );
    1:(
      pos:D3DVector;
      normal:D3DVector;
      );
end;

PSXVertex2T = ^TSXVertex2T;

TSXVertexArray2T = Array [0..$FFFFFF] of TSXVertex2T;
PSXVertexArray2T = ^TSXVertexArray2T;


//ワンスキン用頂点データ
TSXVertexOneSkin = packed record

  Case Integer of
    0: (
      x,y,z:Single;     //頂点
      b:Single;         //ベータ
      nx,ny,nz:Single;  //法線
      diffuse:D3DCOLOR;   //ディフューズ
      tu,tv:Single;       //テクスチャ座標
      );
    1:(
      pos:D3DVector;
      beta:Single;
      normal:D3DVector;
      );
end;


//ビルボードのための頂点情報
TSXVertexBB = record
  Case Integer of
    0:(
      dx,dy:Single;     //中心からの相対座標
      color:D3DCOLOR;   //色
      tu,tv:Single;     //テクスチャ座標
      );
    1:(
      size:TVector2D;
      );
end;

//スプライトのための頂点情報
TSXVertexSP = record
  dx,dy:Single;         //中心からの相対座標(スクリーン座標系)
  color:D3DCOLOR;       //色
  tu,tv:Single;         //テクスチャ座標
end;


//αブレンドモード定義


//αブレンドモード(下地の色とこれから描く色をどうブレンドする？)
TSXBlendMode = DWord;

//ブレンドモード定義情報
TSXBlendInfo = record
  Sort:Boolean;               //Zソートする必要がある
  //以下、レンダリングステート設定用
  AlphaTestEnable:Boolean;    //カラーキー処理用、αテスト、FalseならAlphaFuncは無視
  AlphaFunc:D3DCMPFUNC;          //αテスト用比較関数

  AlphaBlendEnable:Boolean;   //もし、このメンバがFalseなら、以下の設定はスキップ
  SrcBlend:D3DBLEND;
  DestBlend:D3DBLEND;
  BlendOp:D3DBLENDOP;              //BlendOpが設定できるビデオカードのみ
end;


//可視属性
TSXVisibility = (
  sxvShow,        //見える
  sxvHide,        //隠れる。子フレームも含めて
  sxvHideMyself   //隠れる。子フレームは隠れない。
);

//メッシュ描画のソート時に何を基準にするか
TSXSortKey = (
  sxsMeshCenter,  //メッシュの中心
  sxsMeshFar,     //メッシュの遠い所
  sxsMeshNear     //メッシュの近い所
);

//座標変換についてのポリシー
TSXFrameStyle = (
  sxfsDefault,      //ふつう
  sxfsMeshContainer //親フレームと常に同じ変換行列を取り、純粋にメッシュ格納庫としての役割を果たす
);


//イベントハンドラ
TSXRenderEvent = procedure (Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad) of object;
TSXQueryZEvent = procedure (Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; var ZValue:Single) of object;
TSXQueryVisibilityEvent = procedure (Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; var Visibility:TSXVisibility) of object;




//クラス群


//メッシュ用クラス
TSXMesh = class
  protected
    FOwner:TDGCarad;                //色々お世話になるDDDDオブジェクト

    FVBuf:TDGVertexBuffer;          //VertexBuffer
    FIBuf:TDGIndexBuffer;           //IndexBuffer
    FVBufName,FIBufName:String;     //デバッグログに吐き出される、VertexBufferとIndexBufferの名前

    FFaceCount:UINT;                //面の数、常にインデクス数/3

    FBox:TBox3D;                    //AABB
    FSphere:TSphere3D;              //外接球
    FCapsule:TCapsule3D;            //外接カプセル

    MaxPrims:UINT;                 //一度に描画できる最大のプリミティブ数
    MaxIndex:UINT;                 //頂点インデクスの最大値
  public

    constructor Create(AOwner:TDGCarad; VBufName:String = ''; IBufName:String = '');
    destructor Destroy; override;

    procedure Draw;                                                 //DrawIndexedPrimitiveで全部描画
    procedure DrawPartial(start:UINT; primCount:UINT);            //DrawIndexedPrimitiveで部分描画

    procedure LoadFromStream(s:TStream);
    procedure LoadFromFile(fileName:String; qdaName:String = '');
    procedure SaveToFile(filename:String);
    procedure SaveToStream(s:TStream);

    procedure SetupBuffers(newVertexCount, newIndexCount:UINT; FVF:DWord;
      VertexSize:UINT; Pool:D3DPOOL);

    procedure Compact;            //同じ頂点をまとめる
    procedure Optimize;           //VCacheが良く利くように最適化する
    procedure UpdateBounds;       //メッシュのAABBと、外接球の半径を計算

    property VBuf:TDGVertexBuffer read FVBuf;
    property IBuf:TDGIndexBuffer read FIbuf;
    property FaceCount:UINT read FFaceCount;

    property Box:TBox3D read FBox;
    property Sphere:TSphere3D read FSphere;
    property Capsule:TCapsule3D read FCapsule;
end;



{
TSXMesh = class(TSXGenMesh)
  private
    function GetVertex(idx: Word): TSXVertex;  override;
    procedure SetVertex(idx: Word; const Value: TSXVertex); override;
  public

    property Vertex[idx:Word]:TSXVertex read GetVertex write SetVertex; //頂点
    function  Lock:PSXVertexArray;
end;
}

//メッシュのリスト
TSXMeshList = class(TList)
  private
    DG:TDGCarad;
    function GetMesh(idx: Integer): TSXMesh;
    procedure SetMesh(idx: Integer; const Value: TSXMesh);
  public
    property Meshes[idx:Integer]:TSXMesh read GetMesh write SetMesh; default;
    constructor Create(ADG:TDGCarad);
    destructor Destroy; override;
    procedure Delete(idx:Integer);
    procedure Clear; override;
    procedure LoadFromStream(Source:TStream);
    procedure LoadFromFile(filename:String; qdaName:String = '');
    procedure SaveToStream(Dest:TStream);
    procedure SaveToFile(filename:String);
end;


//フレーム
TSXFrame = class
  protected
    FMatrix:D3DMATRIX;            //変換行列(自分の座標系→親座標系)
    FMeshMatrix:D3DMATRIX;        //メッシュ描画時の変換行列…メッシュ座標→自分座標
    FTextures:Array[0..SX_MAX_TSS] of TDGTexture; //テクスチャ

    FAncestors:TSXFrameList;  //祖先フレーム達
    FChildren:TSXFrameList;   //子フレーム達
    FParent:TSXFrame;         //親フレーム(Ancestors[Ancestors.Count-1]と同)
    FVisibility: TSXVisibility;   //可視属性

    FEnableMeshT:Boolean;         //ローカル座標→メッシュ座標への変換を行う(MeshMatrix有効)
    FStyle:TSXFrameStyle;         //座標変換についてのポリシー

    FRendered:Boolean;            //最後のRenderメソッド実行時に、カリングされていない
    FValidRenderedMatrix:Boolean; //RenderedMatrixは、最後のRenderメソッド実行時のシーンに一致している

    RenderedMatrix:D3DMATRIX;     //最後にレンダリングされたときのワールド行列(ビルボードの描画に使用)
    RenderedMeshMatrix:D3DMATRIX; //最後にレンダリングされたときのメッシュ行列
    BBAttached:Boolean;           //ビルボードがくっついてる？
    FEnableBS: Boolean;

    function GetWorldMatrix: D3DMATRIX;
    function GetWorldMatrixRot:D3DMATRIX;
    procedure SetParent(const Value: TSXFrame);
    function GetTexture: TDGTexture;
    function GetTextures(idx: Integer): TDGTexture;
    procedure SetTexture(const Value: TDGTexture);
    procedure SetTextures(idx: Integer; const Value: TDGTexture);

  public

    //中身
    Mesh:TSXMesh;                           //書き込むメッシュ
    Material:D3DMATERIAL8;                  //マテリアル

    //レンダリングステートの制御
    BlendMode:TSXBlendMode;                 //描画時のブレンドモード
    Lighting:Boolean;                       //True:ライティングをする, False:頂点の色をそのまま使う
    Specular:Boolean;                       //スペキュラハイライトをつける
    TextureWrap:DWord;                      //ラップの方法
    FillMode:D3DFILLMODE;                   //塗り方(ポイント/ライン/ポリゴン)

    //イベントハンドラ
    OnRender:TSXRenderEvent;                    //オーナードロウ
    OnQueryZ:TSXQueryZEvent;                    //オーナードロウで描かれる物体のZ値、Nilの場合、メッシュのSortKey位置でソート
    OnQueryVisibility:TSXQueryVisibilityEvent;  //カリング判定、Nilの場合、メッシュのバウンディング球でカリング

    //いろいろ
    Bounds:TSphere3D;                       //フレームの外接球、可視判定用
    Tag:LongInt;                            //整数値、なんにでも使って
    RenderAttr:LongInt;                     //整数値、なんにでも使っての２

    UserData:Pointer;                       //何かポインタ

    //ソート制御
    SortKey:TSXSortKey;                     //何を基準にしてソートするか

    //コンストラクタ・デストラクタ
    constructor Create(parentFrame:TSXFrame);
    destructor Destroy; override;

    //親子関係
    property Ancestors:TSXFrameList read FAncestors;
    property Children:TSXFrameList read FChildren;
    property Parent:TSXFrame read FParent write SetParent;
    //property Binds:TSXFrameBinds read FBinds write FBinds;

    //テクスチャ
    property Texture:TDGTexture read GetTexture write SetTexture;      //FTextures[0]を返す
    property Textures[idx:Integer]:TDGTexture read GetTextures write SetTextures;   //各テクスチャ

    //可視属性
    property Visibility:TSXVisibility read FVisibility write FVisibility;
    property EnableBounds:Boolean read FEnableBS write FEnableBS;         //フレームの外接球で、下位フレームまでカリングする
      //可動部分ナシとして、現在の下位フレームまでの状態からバウンディング球を計算
      //onlyMyselfがFalseの時は、下位フレームのバウンディング情報も更新
    procedure UpdateBounds(onlyMyself:Boolean);
    procedure ActivateBounds;     //下位フレームも含めて全部のフレームのBSをEnableに
    procedure DeactivateBounds;   //下位フレームも含めて全部のフレームのBSをDisableに

    //座標変換関係
    property Matrix:D3DMATRIX read FMatrix write FMatrix;
    property MatrixOnRender:D3DMATRIX read RenderedMatrix;               //OnRenderイベント内などで利用する、実際に描画されたときのフレーム→ワールド変換行列
    property WorldMatrix:D3DMATRIX read GetWorldMatrix;                  //自分座標→ワールド座標の変換行列
    property WorldMatrixRot:D3DMATRIX read GetWorldMatrixRot;            //自分座標→ワールド座標の変換行列(回転成分のみ)
    property MeshMatrix:D3DMATRIX read FMeshMatrix write FMeshMatrix;    //メッシュ描画時の変換行列…メッシュ座標→自分座標
    property MeshMatrixOnRender:D3DMATRIX read RenderedMeshMatrix;
    property EnableMeshTransform:Boolean read FEnableMeshT write FEnableMeshT; //メッシュ座標→自分座標の変換を行う
    property Style:TSXFrameStyle read FStyle write FStyle;               //座標変換についてのポリシー
    property Rendered:Boolean read FRendered;
    property ValidMatrixOnRender:Boolean read FValidRenderedMatrix;

    procedure SetTransform(ref:TSXFrame; const mat:D3DMATRIX);
    function  GetTransform(ref:TSXFrame):D3DMATRIX;

    function LocalToWorld(vec:D3DVector):D3DVector;           //フレーム内での座標を、ワールド座標に変換する
    function WorldToLocal(vec:D3DVector):D3DVector;           //ワールド座標をフレーム内の座標に変換する
    function LocalToWorldRot(rotvec:D3DVector):D3DVector;     //フレーム内で向きベクトルを、ワールド座標での向きベクトルに変換
    function WorldToLocalRot(rotvec:D3DVector):D3DVector;     //ワールド座標内の向きベクトルをフレーム内の向きベクトルに変換する

    procedure CalcMatrixOnRender;                             //最後のRender時に即した変換行列を求める。カリングされたフレームの変換行列を知りたい時などに用いる

    //位置
    procedure SetTranslation(ref:TSXFrame; const pos:D3Dvector);
    function GetTranslation(ref:TSXFrame):D3DVector;

    //姿勢
    //…軸ベクトルで設定・取得
    procedure SetOrientation(ref:TSXFrame; const vecZ:D3DVector; const vecY:D3DVector);
    procedure GetOrientation(ref:TSXFrame; var vecZ:D3DVector; var vecY:D3DVector);
    //…マトリクスを与えて姿勢だけ設定
    procedure SetOrientationMatrix(ref:TSXFrame; const mat:D3DMATRIX);
    //…クォータニオンを与えて姿勢だけ設定
    procedure SetOrientationQuat(ref:TSXFrame; const quat:TQuaternion);


    //お便利
    //…Z軸をtargetフレーム内の座標posを向ける。Y軸はworldフレーム内のY軸に向け。Y軸の傾きをbank度とする
    procedure LookAt(target:TSXFrame; const pos:D3DVector; world:TSXFrame; const bank:Integer);
    function ViewMatrix:D3DMatrix;  //このフレームをカメラにした場合のビュー行列を生成

    procedure SetBlendModeRcv(Value:TSXBlendMode); //再帰的にブレンドモードをセットする
    procedure SetLightingRcv(Value:Boolean);
    procedure SetSpecularRcv(Value:Boolean);
    procedure SetTextureWrapRcv(Value:DWord);
    procedure SetFillModeRcv(Value:D3DFILLMODE);
    procedure SetMaterialRcv(Value:D3DMATERIAL8);
    procedure SetTexturesRcv(idx:Integer; Value:TDGTexture);
end;

//フレームのリスト
TSXFrameList = class(TList)
  private
    function GetFrame(idx: Integer): TSXFrame;
    procedure SetFrame(idx: Integer; const Value: TSXFrame);
  public
    property Frames[idx:Integer]:TSXFrame read GetFrame write SetFrame; default;
    constructor Create;
    destructor Destroy; override;
    procedure Assign(source:TSXFrameList);
end;




//シーン全体
TSXScene = class
  protected
    FStateRestorer:TSXStateRestorer;  //レンダリングステートお任せクン
    FCamera:TSXFrame;
    DG:TDGCarad;
    ResID:UINT;                     //DGCaradに管理されるときのリソースID
    FDebugName:String;              //デバッグログに出力される名前

    FProjectionMatrix:D3DMATRIX;
    FScreenMatrix:D3DMATRIX;
    FVP:D3DVIEWPORT8;
    FFovH,FFovV,FAspectRatio,FNearZ,FFarZ:Single;    //射影行列のパラメータ

    TopQueue:TSXRenderingQueue;    //常に一番上に描画される物体のためのキュー、半透明体のソート等はしない
    Queue:TSXRenderingQueue;       //ソートしない(一般に、不透明体用のキュー)
    SortQueue:TSXRenderingQueue;   //ソートする(一般に透明体用のキュー)

    FBlendOpEnable:Boolean;             //この環境でBLENDOPの設定ができるか？
    FSortAlpha:Boolean;                 //半透明体のソートを行う
    FBlendInfos:Array of TSXBlendInfo;  //ブレンド情報格納用

    procedure InspectDevCaps;          //DEVCAPSに関係する情報の更新
    procedure Recover;              //ビューポートなどの状態をリストア
  public
    //コンストラクタ・デストラクタ
    constructor Create(ADG:TDGCarad; debugName:String = '');
    destructor Destroy; override;

    //描画
    procedure Render(rootFrame:TSXFrame);                                  //rootFrame以下を描画
    procedure Clear(dwFlags:DWord; color:DWORD; z:Single; stencil:DWord);  //バックバッファのクリア

    //一般設定
    property SortAlpha:Boolean read FSortAlpha write FSortAlpha;
    function RegisterBlendMode(info:TSXBlendInfo):TSXBlendMode;  //新しいブレンドモードを追加する

    //変換周りの設定
    property CameraFrame:TSXFrame read FCamera write FCamera;  //視点を置くフレーム
    procedure SetProjection(fovH,aspect, nearZ,farZ:Single);    //投影変換の設定
    procedure SetViewPort(left,top,right,bottom:DWord);        //ビューポート

    //↑の設定によって決定されるプロパティ
    property FarZ:Single read FFarZ;                           //SetProjectionで決定される値、以下４つもそう。
    property NearZ:Single read FNearZ;
    property FovH:Single read FFovH;                             //Fovの単位はラジアン
    property FovV:Single read FFovV;                             //Fovの単位はラジアン
    property AspectRatio:Single read FAspectRatio;
    property ProjectionMatrix:D3DMatrix read FProjectionMatrix;
    property ScreenMatrix:D3DMatrix read FScreenMatrix;  //スクリーン変換行列

    //ビルボード・スプライト
    procedure PushBillboard (ref:TSXFrame; pos:D3DVector;
      points:Array of TSXVertexBB; tex:TDGTexture;
      blendMode:TSXBlendMode = sxbColorKey; scaling:Boolean = True;
      filter:Boolean = True; topMost:Boolean = False);
    procedure PushSprite(pos:D3DVector; points:Array of TSXVertexSP;
      tex:TDGTexture; blendMode:TSXBlendMode = sxbColorKey;
      filter:Boolean = True; topMost:Boolean = False);

    //可視判定ユーティリティ
    //refフレーム内にある、球は見えるか、但し、距離がdepth以上なら見えないものとする
    function SphereVisibility(ref:TSXFrame; sphere:TSphere3D; depth:Single):Boolean; overload;
    //↑と同じ、ただし、ref→ワールド座標までのマトリクスが求まっているとする
    function SphereVisibility(worldM:D3DMATRIX; sphere:TSphere3D; depth:Single):Boolean; overload;

    //レンダリングステート記憶装置
    property StateRestorer:TSXStateRestorer read FStateRestorer;

    //その他
    property BlendOpEnable:Boolean read FBlendOpEnable; //ブレンドモードを追加する際の指針
end;


//ライト
TSXLight = class
  protected
    DG:TDGCarad;
    FIndex:DWord;       //SetLightに渡す、インデクス
    FEnabled:Boolean;
    FUpdate:Boolean;    //パラメータの変更を、即反映
    procedure SetEnabled(const Value: Boolean);
  public
    Params:D3DLIGHT8;

    constructor Create(DDCompo:TDGCarad; index:DWord);
    destructor  Destroy; override;

    procedure BeginUpdate;   //Paramsの内容を、Direct3Dに伝えなくする
    procedure EndUpdate;     //Paramsの内容を、Direct3Dに伝えなくした状態を解除

    property Enabled:Boolean read FEnabled write SetEnabled;

    //お便利ルーチン
    procedure SetupDiffuse (_R,_G,_B:Single);
    procedure SetupSpecular(_R,_G,_B:Single);
    procedure SetupAmbient (_R,_G,_B:Single);

    procedure SetupColors(difR,difG,difB, specR,specG,specB, ambR,ambG,ambB:Single);
    procedure SetupRanges(range, att0,att1,att2:Single);

    procedure SetupDirectional(dir:D3DVector);
    procedure SetupPoint(pos:D3DVector);
    procedure SetupSpot(pos,dir:D3DVector; theta,phi,falloff:Single);

    procedure FitFrame(target:TSXFrame);  //フレームの位置・向きにセットする
end;


//複数のライトを管理するオブジェクト
TSXLightGroup = class(TList)
  protected
    DG:TDGCarad;
    ResID:UINT;             //DGCaradに管理される時のリソースID
    FDebugName:String;      //デバッグログに出力される名前
    FCapacity:DWord;        //管理するライトの数
    FLights:Array of TSXLight;
    function GetLights(idx: DWord): TSXLight;
    function GetUnusedLight: TSXLight;
    procedure Recover;      //解像度を変えた後などのリカバリ
  public
    constructor Create(ADG:TDGCarad; capacity:DWord; debugName:String = '');
    destructor  Destroy; override;

    property Lights[idx:DWord]:TSXLight read GetLights; default;
    property UnusedLight:TSXLight read GetUnusedLight;       //Enabledになってない、最初のライトを返す

    procedure EnableAll;    //全部点ける
    procedure DisableAll;   //全部消す

end;



TSXMaterial = class
  private
    DG:TDGCarad;
    FUpdate:Boolean;
  public
    Params:D3DMATERIAL8;
    constructor Create(ADG:TDGCarad);

    procedure BeginUpdate;   //Paramsの内容を、Direct3Dに伝えなくする
    procedure EndUpdate;     //Paramsの内容を、Direct3Dに伝えなくした状態を解除

    procedure SetupDiffuse (_R,_G,_B:Single);
    procedure SetupSpecular(_R,_G,_B:Single);
    procedure SetupAmbient (_R,_G,_B:Single);
    procedure SetupEmissive (_R,_G,_B:Single);
    procedure SetupSpecularPower(pow:Single);

    procedure SetupColors(difR,difG,difB, specR,specG,specB, ambR,ambG,ambB, emsR,emsG,emsB, pow:Single);
end;



//メッシュ描画キューに入れる情報
TSXMeshInfo = record
  frame:TSXFrame;     //そのメッシュを描こうとしたフレームはどれ？
//  mat:D3DMatrix;      //変換行列(モデル→ワールド)
end;

//ビルボード描画キューに入れる情報
TSXBillboardInfo = record
  scaling:Boolean;      //視点からの距離でスケーリングする？
  filter:Boolean;       //フィルタリングする？
  blend:TSXBlendMode;
  ref:TSXFrame;         //基準フレーム
  pos:THgVector;        //基準フレーム内での位置(計算用にも使う)
  tex:TDGTexture;       //テクスチャ
  pts:Array[0..3] of TSXVertexBB;  //頂点データ
end;

//2Dスプライトキューに入れる情報
TSXSpriteInfo = record
  filter:Boolean;       //フィルタリングする？
  blend:TSXBlendMode;
  tex:TDGTexture;
  pos:D3DVector;                    //中心点(x,yは画面座標、zは視点座標系のz)
  pts:Array[0..3] of TSXVertexSP;  //頂点データ
end;

//レンダリングの順序を保つためのバッファ、透明体かそうでないかで順序を変える。
TSXRenderingQueue = class
  private
    FOwner:TDGCarad;   //色々お世話になるDGCaradオブジェクト
    Scene:TSXScene;

    MeshQueue:Array of TSXMeshInfo;
    BillboardQueue:Array of TSXBillboardInfo;
    SpriteQueue:Array of TSXSpriteInfo;

    OrderTable:Array of Integer;        //描画順序テーブル
    Zs:Array of Single;                 //Z座標ソート用

    FMeshCapacity:Integer;              //メッシュ情報を蓄える容量
    FBillboardCapacity:Integer;
    FSpriteCapacity:Integer;

    FMeshCount:Integer;                 //キューに入ってる数
    FBillboardCount:Integer;
    FSpriteCount:Integer;

    FVisibleBillboardCount:Integer;     //画面に入ってるビルボードの数(ソート時に決定)
    FVisibleSpriteCount:Integer;        //画面に入ってるスプライトの数(ソート時に決定)

    procedure Sort(viewParam:D3DVIEWPORT8);   //レンダリング前ソート
    procedure SetBillBoardCapacity(const Value: Integer);
    procedure SetMeshCapacity(const Value: Integer);
    procedure SetSpriteCapacity(const Value: Integer);
  public
    constructor Create(AOwner:TDGCarad; _Scene:TSXScene);
    destructor Destroy; override;
    procedure PushMesh(frame:TSXFrame); //フレームにつっついたメッシュ //(blendMode:TSXBlendMode; mesh:TSXMesh; frame:TSXFrame; mat:D3DMatrix; tex:TDGTexture; mtrl:D3DMATERIAL8;  event:TSXRenderEvent; ZEvent:TSXQueryZEvent);
    procedure PushBillboard(scaling:Boolean; filter:Boolean; blendMode:TSXBlendMode; ref:TSXFrame; pos:D3DVector; tex:TDGTexture; pts:Array of TSXVertexBB); //ビルボード
    procedure PushSprite(filter:Boolean; blendMode:TSXBlendmode; pos:D3DVector; tex:TDGTexture; pts:Array of TSXVertexSP); //スプライト

    procedure Render(needSort:Boolean; viewMat:D3DMATRIX; viewParam:D3DVIEWPORT8); //レンダリング
    procedure Flush;  //レンダリングせず、バッファの中を空に

    property MeshCapacity:Integer read FMeshCapacity write SetMeshCapacity;
    property BillboardCapacity:Integer read FBillboardCapacity write SetBillBoardCapacity;
    property SpriteCapacity:Integer read FSpriteCapacity write SetSpriteCapacity;
    property MeshCount:Integer read FMeshCount;
    property BillboardCount:Integer read FBillboardCount;
    property SpriteCount:Integer read FSpriteCount;
end;


//てきすらいたー
TSXTextWriter = class
  private
    DG:TDGCarad;
    function GetFont: TFont;
    procedure SetFont(const Value: TFont);
    function GetBrush: TBrush;
    procedure SetBrush(const Value: TBrush);
  protected
    SourceBMP:TBitmap;                  //原画ビットマップ
  public
    constructor Create(ADG:TDGCarad);
    destructor Destroy; override;
    procedure Write(x,y:Integer; str:String; Target:TDGTexture);      //テクスチャに文字列を書き込む
    function CreateStringTexture(str:String; fmt:Array of D3DFORMAT; debugStr:String = ''):TDGTexture;  //文字列を書き込んだテクスチャを作る
    function TextWidth(str:String):Integer;
    function TextHeight(str:String):Integer;
    property Font:TFont read GetFont write SetFont;     //白以外にしても良い事ないよ(w
    property Brush:TBrush read GetBrush write SetBrush; //黒以外にするのは、sxbMulNegの時くらいかな
end;


//レンダリングステート記憶装置

TSXStateRestorer = class
  private
    DG:TDGCarad;

    SavedRS:Array[0..255] of DWord;  //レンダリングステートバッファ
    RSFlag:Set of Byte;         //各レンダリングステートが更新されたかのフラグ
    RSList:Array[0..255] of D3DRENDERSTATETYPE; //更新されたレンダリングステートリスト
    RSCount:Integer;     //更新されたレンダリングステートの種類のカウント

    SavedTSS:Array[0..SX_MAX_TSS, 0..63] of DWord;  //TSSバッファ
    TSSFlag:Array[0..SX_MAX_TSS] of Set of Byte;    //TSSが更新されたかどうか
    TSSList:Array[0..SX_MAX_TSS, 0..63] of D3DTEXTURESTAGESTATETYPE;
    TSSCount:Array[0..SX_MAX_TSS] of Integer;
  public
    constructor Create(ADG:TDGCarad);
    function SetRS(RSType:D3DRENDERSTATETYPE; Value:DWORD):HRESULT;
    function SetTSS(Stage:UINT; TSSType:D3DTEXTURESTAGESTATETYPE; Value:DWORD):HRESULT;
    procedure Memory;   //今のステートを覚えておく
    procedure Restore;  //最後に覚えたステートに戻す
end;



//TSXFrameを継承して色々便利だぜサンプル
TSXFrameEnvMapped = class(TSXFrame)
  private
    DG:TDGCarad;
    FSinglePathable:Boolean;  //シングルパスでレンダリングできそう
    procedure RenderEvent(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad);
  public
    EnvMap:TDGTexture;
    constructor Create(parent:TSXFrame; ADG:TDGCarad);
end;

procedure SXRenderEnvMapped(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; envOp:DWord = Ord(D3DTOP_ADDSMOOTH));
procedure SXRenderNormalMapped(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; lightDir:D3Dvector);
procedure SXRenderBumpEnvMapped(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; strength:Single = 1.0; envOp:DWord = Ord(D3DTOP_ADDSMOOTH));
procedure SXRenderToon(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; lightDir:D3Dvector);



//SXVertex作成
function SXVertex(x,y,z, nx,ny,nz:Single; dif,spec:D3DCOLOR; tu,tv:Single):TSXVertex;
function SXVertex2T(x,y,z, nx,ny,nz:Single; dif:D3DCOLOR; tu0,tv0, tu1,tv1:Single):TSXVertex2T;
function SXVertexOneSkin(x,y,z, beta, nx,ny,nz:Single; dif:D3DCOLOR; tu,tv:Single):TSXVertexOneSkin;

//SXVertexBB作成
function SXVertexBB(dx,dy:Single; col:D3DCOLOR; tu,tv:Single):TSXVertexBB;

//SXVertexSP作成
function SXVertexSP(dx,dy:Single; col:D3DCOLOR; tu,tv:Single):TSXVertexSP;

//マテリアル設定の便宜を図る関数
function MakeMaterial(difR,difG,difB, specR,specG,specB, ambR,ambG,ambB, emsR,emsG,emsB, pow:Single):D3DMATERIAL8; overload;
function MakeMaterial(dif, spec, amb, ems:D3DCOLOR; pow:Single):D3DMATERIAL8; overload; //α指定可、D3DCOLOR版

//ブレンドモード情報の代入
function BlendInfo(_sort, _alphaTest:Boolean; _alphaFunc:D3DCMPFUNC; _alphaBlend:Boolean; _srcBlend,_destBlend:D3DBLEND; _blendOp:D3DBLENDOP):TSXBlendInfo;

implementation

type

  TSXTLVertexMT = packed record
  Case Integer of
    0: (
      x,y,z,rhw:Single;   //頂点
      diffuse:D3DCOLOR;   //ディフューズ
      specular:D3DCOLOR;  //スペキュラ
      tu0,tv0:Single;     //テクスチャ座標
      tu1,tv1:Single;     //テクスチャ座標2
      );
    1:(
      pos:D3DVector;
      );
    2:(
      hgPos:THgVector;
      );
  end;

const
  FVF_SXTLVertexMT:DWord = (D3DFVF_XYZRHW Or D3DFVF_DIFFUSE Or D3DFVF_SPECULAR Or D3DFVF_TEX2);





{ ヘルパー関数 }

function SXVertex(x,y,z, nx,ny,nz:Single; dif,spec:D3DCOLOR; tu,tv:Single):TSXVertex;
begin
  result.x :=x;
  result.y :=y;
  result.z :=z;
  result.nx:=nx;
  result.ny:=ny;
  result.nz:=nz;

  result.diffuse:=dif;
  result.specular:=spec;

  result.tu:=tu;
  result.tv:=tv;

end;

function SXVertex2T(x,y,z, nx,ny,nz:Single; dif:D3DCOLOR; tu0,tv0, tu1,tv1:Single):TSXVertex2T;
begin
  result.x :=x;
  result.y :=y;
  result.z :=z;

  result.nx:=nx;
  result.ny:=ny;
  result.nz:=nz;

  result.diffuse:=dif;

  result.tu0:=tu0;
  result.tv0:=tv0;

  result.tu1:=tu1;
  result.tv1:=tv1;
end;

function SXVertexOneSkin(x,y,z, beta, nx,ny,nz:Single; dif:D3DCOLOR; tu,tv:Single):TSXVertexOneSkin;
begin
  result.x :=x;
  result.y :=y;
  result.z :=z;
  Result.b:=beta;
  result.nx:=nx;
  result.ny:=ny;
  result.nz:=nz;

  result.diffuse:=dif;

  result.tu:=tu;
  result.tv:=tv;

end;

function SXVertexBB(dx,dy:Single; col:D3DCOLOR; tu,tv:Single):TSXVertexBB;
begin
  result.dx :=dx;
  result.dy :=dy;

  result.color:=col;

  result.tu:=tu;
  result.tv:=tv;
end;



//SXVertexSP作成
function SXVertexSP(dx,dy:Single; col:D3DCOLOR; tu,tv:Single):TSXVertexSP;
begin
  result.dx :=dx;
  result.dy :=dy;

  result.color:=col;

  result.tu:=tu;
  result.tv:=tv;
end;

function BlendInfo(_sort, _alphaTest:Boolean; _alphaFunc:D3DCMPFUNC; _alphaBlend:Boolean; _srcBlend,_destBlend:D3DBLEND; _blendOp:D3DBLENDOP):TSXBlendInfo;
begin
  with result do begin
    Sort:=_sort;
    AlphaTestEnable:=_alphaTest;
    AlphaFunc:=_alphaFunc;
    AlphaBlendEnable:=_alphaBlend;
    SrcBlend:=_srcBlend;
    DestBlend:=_destBlend;
    BlendOp:=_blendOp;
  end;
end;


{ TSXMesh }

//コンストラクタ
constructor TSXMesh.Create(AOwner:TDGCarad; VBufName:String = ''; IBufName:String = '');
var
  caps:D3DCAPS8;
begin
  inherited Create;

  FOwner:=AOwner;
  FVBuf:=Nil;
  FIBuf:=Nil;

  FVBufName:=VBufName;
  FIBufName:=IBufName;

  //デバイスの能力ゲット
  FOwner.D3D.GetDeviceCaps(FOwner.Adapter, FOwner.DeviceType, caps);
  MaxPrims:=caps.MaxPrimitiveCount;
  MaxIndex:=caps.MaxVertexIndex;
end;


//デストラクタ
destructor TSXMesh.Destroy;
begin
  if FVBuf <> Nil then
    FVBuf.Free;
  if FIBuf <> Nil then
    FIBuf.Free;


  inherited;
end;



//DPで描画
procedure TSXMesh.Draw;
var
  ofs,primCount:UINT;
begin


  FOwner.D3DDevice.SetVertexShader(FVBuf.FVF);
  FOwner.D3DDevice.SetIndices(FIBuf.IndexBuffer,0);
  FOWner.D3DDevice.SetStreamSource(0, FVBuf.VertexBuffer, FVBuf.VertexSize);

  //primCountずつ分割して描画
  ofs:=0;
  while ofs < (FIBuf.Count-2) do begin  //なんで-2してるかというと、１プリミティブは最低３頂点だから

    primCount := (FIBuf.Count - ofs) div 3;
    if primCount > MaxPrims then
      primCount:=MaxPrims;

    FOwner.D3DDevice.DrawIndexedPrimitive(
      D3DPT_TRIANGLELIST, 0, FVBuf.Count, ofs, primCount);

    Inc(ofs, primCount*3);
  end;

end;

//DPで描画
procedure TSXMesh.DrawPartial(start:UINT; primCount:UINT);
var
  ofs,curPrims,endOfs:UINT;
begin
  FOwner.D3DDevice.SetVertexShader(FVBuf.FVF);
  FOwner.D3DDevice.SetIndices(FIBuf.IndexBuffer,0);
  FOWner.D3DDevice.SetStreamSource(0, FVBuf.VertexBuffer, FVBuf.VertexSize);


  //primCountずつ分割して描画

  //開始頂点インデクスの位置と、終了頂点インデクスの位置を確定
  ofs:=start;
  endOfs:=start + primCount*3;
  if endOfs > IBuf.Count then
    endOfs:=IBuf.Count;

  //終了頂点インデクスまで、MaxPrimsずつちまちま描く
  while ofs < endOfs-2 do begin

    curPrims := (endOfs - ofs) div 3;
    if curPrims > MaxPrims then
      curPrims:=MaxPrims;

    FOwner.D3DDevice.DrawIndexedPrimitive(
      D3DPT_TRIANGLELIST, 0, FVBuf.Count, ofs, curPrims);

    Inc(ofs, curPrims*3);
  end;
end;


//ストリームから読み込み
procedure TSXMesh.LoadFromStream(s:TStream);
var
  sign:Array [0..16] of Char;
  nVertices,nIndices:Word;
  StartPos:Integer;
  FVF:DWord;
  VSize:DWord;
  pLock:PByte;
begin

  StartPos:=s.Position;

  //シグネチャの確認
  sign[16]:=Chr(0);
  s.ReadBuffer(sign, 16);

  if StrPas(sign) = SX00_SIGNATURE then begin
    //SX形式
    FVF:=FVF_SXVERTEX;
    VSize:=Sizeof(TSXVertex);
    //頂点数・頂点インデクス数の設定
    s.ReadBuffer(nVertices, Sizeof(Word));
    s.ReadBuffer(nIndices,  Sizeof(Word));
  end else if StrPas(sign) = SX02_SIGNATURE then begin
    //頂点数・頂点インデクス数の設定
    s.ReadBuffer(nVertices, Sizeof(Word));
    s.ReadBuffer(nIndices,  Sizeof(Word));
    //SXFlexible形式
    s.ReadBuffer(FVF, Sizeof(DWord));
    s.ReadBuffer(VSize, Sizeof(DWord));
  end else begin
    DGCarad_PutDebugMessage('SXLib: 不正なSXファイルです');
    Halt;
  end;

  SetupBuffers(nVertices, nIndices, FVF, VSize, D3DPOOL_MANAGED);

  //ヘッダのスキップ
  s.Position:=StartPos + 256;

  try
    //頂点のロード
    pLock:=VBuf.Lock(0,0, 0);
    s.ReadBuffer(pLock^, VSize * nVertices);
    VBuf.Unlock;

    //頂点インデクスのロード
    pLock:=Pointer(IBuf.Lock(0,0, 0));
    s.ReadBuffer(pLock^, Sizeof(Word) * nIndices);
    IBuf.Unlock;
  except
    DGCarad_PutDebugMessage('SXLib: ストリームからの読み込み中にエラーが発生しました');
    Halt;
  end;

  //バウンディングボリューム情報更新
  UpdateBounds;
end;

//ファイルから読み込み
procedure TSXMesh.LoadFromFile(fileName: String; qdaName:String = '');
var
  fs:TFileStream;
  ms:TMemoryStream;
begin
  if qdaName = '' then begin
    //ファイルから読み込み
    fs:=Nil;

    try
      fs:=TFileStream.Create(filename, fmOpenRead);
    except
      DGCarad_PutDebugMessage('SXLib: ' + filename + ' が、開けません');
      Halt;
    end;

    LoadFromStream(fs);
    fs.Free;
  end else begin
    //アーカイブから読み込み
    ms:=Nil;

    try
      ms:=ExtractFromQDAFile(QDAName, filename);
    except
      DGCarad_PutDebugMessage('SXLib: ' + QDAname + ' 内の、' + filename + 'が抽出できません');
      Halt;
    end;

    LoadFromStream(ms);
    ms.Free;
  end;
end;



//ファイルに保存
procedure TSXMesh.SaveToFile(filename: String);
var
  src:TFileStream;
begin
  src:=TFileStream.Create(filename, fmCreate);
  SaveToStream(src);
  src.Free;
end;

//ストリームに保存
procedure TSXMesh.SaveToStream(s: TStream);
var
  headPos:Int64;
  sign:Array[0..16] of Char;
  pad:Array[1..224] of Byte;
  DXVer:Array[0..4] of Char;
  p:PByte;
begin
  headPos:=s.Position;


  {ヘッダ}

  //シグネチャ
  StrPCopy(sign, SX02_SIGNATURE);
  s.WriteBuffer(sign, 16);

  s.WriteBuffer(VBuf.Count, 2);
  s.WriteBuffer(IBuf.Count, 2);
  s.WriteBuffer(VBuf.FVF, 4);
  s.WriteBuffer(VBuf.VertexSize, 4);
  StrPCopy(DXVer, DXVER_FOR_FVF);
  s.WriteBuffer(DXVer, 4);
  ZeroMemory(@pad, sizeof(pad));
  s.WriteBuffer(pad, Sizeof(pad));

  s.Position:=headPos + 256;

  {本体}

  //VBuf
  p:=VBuf.Lock(0,0,0);
  s.WriteBuffer(p^, VBuf.VertexSize*VBuf.Count);
  VBuf.Unlock;

  //IBuf
  p:=Pointer(IBuf.Lock(0,0,0));
  s.WriteBuffer(p^, 2 * IBuf.Count);
  IBuf.Unlock;

end;


//同じ頂点をまとめる
procedure TSXMesh.Compact;
var
  i,j:UINT;
  tbl:Array of UINT; //頂点番号変換テーブル
  compacted:Boolean;    //着目してた頂点は、まとめの対象になったか
  nCompacted:UINT;      //今までまとめられた点の数
  pV,pV1,pV2:PByte;
  copyBuf:Array of Byte;
  newVertexCount:UINT;   //コンパクション後の頂点数

  function IsSameVs(v1,v2:UINT):Boolean;
  var
    pV1,pV2:PByte;
  begin
    pV1:=pV;  Inc(pV1, VBuf.VertexSize*v1);
    pV2:=pV;  Inc(pV2, VBuf.VertexSize*v2);

    result:=CompareMem(pV1, pV2, VBuf.VertexSize);
  end;
begin

  SetLength(tbl, VBuf.Count);

  pV:=VBuf.Lock(0,0,0);

  //まず、コンパクション後の頂点が、コンパクション前のどの頂点にあたるのかを調べる
  //コンパクション後のi番の頂点は、コンパクション前のtbl[i]番の頂点に相当する
  nCompacted:=0;
  tbl[0]:=0;
  For i:=1 to VBuf.Count-1 do begin

    compacted:=False;
    for j:=0 to i-1 do begin
      //以前に探索した点と同じ座標、同じ色なら、まとめる
      if IsSameVs(i,j) then begin
        compacted:=True;
        tbl[i]:=tbl[j];
        Inc(nCompacted);
        break;
      end;
    end;

    if not compacted then
      tbl[i]:=i - nCompacted;

  end;
  newVertexCount:=VBuf.Count - nCompacted;


  {コンパクション}

  //パッファ作成
  SetLength(copyBuf, newVertexCount * VBuf.VertexSize);

  //上書きしていく
  for i:=0 to VBuf.Count-1 do begin
    //Vertices[tbl[i]]:=Vertices[i];
    pV1:=pV;  Inc(pV1,tbl[i] * VBuf.VertexSize);
    pV2:=pV;  Inc(pV2,i * VBuf.VertexSize);

    Move(pV2^,pV1^, VBuf.VertexSize);
  end;

  //バッファに退避
  Move(pV^, copyBuf[0], newVertexCount * VBuf.VertexSize);


  //VBの再構築
  VBuf.Unlock;
  VBuf.Count:=newVertexCount;

  //コピー
  pV:=Vbuf.Lock(0,0,0);
  Move(copyBuf[0], pV^, newVertexCount * VBuf.VertexSize);
  VBuf.Unlock;


  //インデクスバッファの中身を書き換え
  IBuf.Lock(0,0,0);
  For i:=0 to IBuf.Count-1 do begin
    IBuf.Indices[i]:=tbl[IBuf.Indices[i]];
  end;
  IBuf.Unlock;



  copyBuf:=Nil;
  tbl:=Nil;
end;


//VCacheが良く利くように最適化する
procedure TSXMesh.Optimize;
var
  i,j:Integer;
  nCacheEntry:Integer;  //VCacheのエントリ
  Indices:Array of Word;
  nOrdered:Integer;               //並び替えの終わった頂点インデクスの数
  //nHit:Integer;
  goodFace,betterFace:Integer;    //探索中に見つかった、１ヒット、2ヒット面のインデクス
  swapped:Boolean;

  //maxHit,maxHitter:Integer;

  procedure GetFace(dest:PWord; faceIdx:Word);
  begin
    dest^:=Indices[faceIdx*3];
    Inc(dest);
    dest^:=Indices[faceIdx*3+1];
    Inc(dest);
    dest^:=Indices[faceIdx*3+2];
  end;

  //その面はキャッシュヒットしてる？(最近nCacheEntryまでのインデクスと同じ？)
  //面のうちキャッシュヒットしてる頂点の数を返す
  function HitCount(faceIdx:Word):Integer;
  var
    i,j:Integer;
  begin
    result:=0;

    for i:=0 to 2 do begin
      //面を構成するそれぞれの点について頂点キャッシュをさらう
      for j:=1 to nCacheEntry do begin
        if nOrdered < j then
          break;
        if Indices[nOrdered-j] = Indices[faceIdx*3+i] then begin
          Inc(result);
          break;
        end;
      end;
    end;

  end;

  function TotalHitCount(faceIdx:Word):Integer;
  var
    i,j:Integer;
  begin
    result:=0;

    for i:=0 to 2 do begin
      //面を構成するそれぞれの点について頂点キャッシュをさらう
      for j:=1 to nCacheEntry-i do begin
        if nOrdered < j then
          break;
        if Indices[nOrdered-j] = Indices[faceIdx*3+i] then begin
          Inc(result);
        end;
      end;
    end;

  end;

  procedure SwapFace(a,b:Word);
  var
    i,tmp:Word;
  begin
    For i:=0 to 2 do begin
      tmp:=Indices[a*3+i];
      Indices[a*3+i]:=Indices[b*3+i];
      Indices[b*3+i]:=tmp;
    end;
  end;

begin

  if FaceCount = 0 then
    exit;


  //GeForce1/2にフィットするように、キャッシュのエントリは16としておく
  nCacheEntry:=16;

  //インデクスバッファをコピー
  SetLength(Indices, IBuf.Count);
  IBuf.Lock(0,0,0);
  For i:=0 to IBuf.Count-1 do
    Indices[i]:=IBuf.Indices[i];
  IBuf.Unlock;

  //デバッグ用、キャッシュヒット率を求める
  {
  nOrdered:=3;
  nHit:=0;
  for i:=1 to FaceCount-1 do begin
    Inc(nHit, HitCount(i));
    Inc(nOrdered,3);
  end;
  DGCarad_PutTestMessage(Format('使用前 : インデクス数:%d, キャッシュヒット件数:%d ヒット率:%f',
    [IBuf.Count, nHit, nHit / iBuf.Count]));
  }


  //最初の一個の面はそのまま
  nOrdered:=3;

  //面ごとにインデクスをソートしてく
  For i:=1 to FaceCount-1 do begin
    goodFace:=-1;
    betterFace:=-1;

    //できれば３つともキャッシュヒットしてる頂点を持ってくる
    swapped:=False;
    For j:=i+1 to FaceCount-1 do begin
      Case HitCount(j) of
        1: if goodFace = -1 then goodFace:=j;
        2: if betterFace = -1 then betterFace:=j;
        3: begin
          SwapFace(i, j);
          swapped:=True;
          break;
        end;
      end;
    end;

    //無かったら、２つか１つキャッシュヒットしてる面を持ってくる
    if not swapped then begin
      if betterFace <> -1 then
        SwapFace(i,betterFace)
      else if goodFace <> -1 then
        SwapFace(i, goodFace);
    end;

    {
    //キャッシュにヒットする頂点がたっぷり入っているやつを取ってくる
    maxHit:=0;
    maxHitter:=i;
    For j:=i+1 to FaceCount-1 do begin
      nHit:= HitCount(j);
      if nHit > maxHit then begin
        maxHit:=nHit;
        maxHitter:=j;
      end;
    end;

    SwapFace(i, maxHitter);
    }

    Inc(nOrdered,3);
  end;


  //デバッグ用、キャッシュヒット率を求める
  {
  nOrdered:=3;
  nHit:=0;
  for i:=1 to FaceCount-1 do begin
    Inc(nHit, HitCount(i));
    Inc(nOrdered,3);
  end;
  DGCarad_PutTestMessage(Format('使用後 インデクス数:%d, キャッシュヒット件数:%d ヒット率:%f',
    [IBuf.Count, nHit, nHit / iBuf.Count]));
  }


  //コピー戻し
  IBuf.Lock(0,0,0);
  For i:=0 to IBuf.Count-1 do
    IBuf.Indices[i]:=Indices[i];
  IBuf.Unlock;


  Indices:=Nil;
end;


//外接箱、球についての情報ゲット
procedure TSXMesh.UpdateBounds;
var
  i:Integer;
  min,max,dif,center:D3Dvector;
  pV:pByte;
  pP:^D3Dvector;
begin
  //読み取り専用でロック
  pV:=VBuf.Lock(0,0,D3DLOCK_READONLY);

  pP:=Pointer(pV);

  min.x:=pP^.x;
  min.y:=pP^.y;
  min.z:=pP^.z;
  max:=min;

  Inc(pV, VBuf.VertexSize);

  For i:=1 to VBuf.Count-1 do begin
    pP:=Pointer(pV);

    if min.x > pP^.x then
      min.x:=pP^.x
    else if max.x < pP^.x then
      max.x:=pP^.x;

    if min.y > pP^.y then
      min.y:=pP^.y
    else if max.y < pP^.y then
      max.y:=pP^.y;

    if min.z > pP^.z then
      min.z:=pP^.z
    else if max.z < pP^.z then
      max.z:=pP^.z;


    Inc(pV, VBuf.VertexSize);
  end;

  VBuf.Unlock;

  //結果の格納
  FBox.Min:=min;
  FBox.Max:=max;

  center:=NowScale( NowAdd(FBox.Min,FBox.Max), 0.5);
  dif:=NowSub(FBox.Max, FBox.Min);

  //まだいいかげんながら、カプセルの計算  

  if (dif.x > dif.y) And (dif.x > dif.z) then begin
    //軸をX方向に取る
    FCapsule.R:=Sqrt( Sqr(dif.y) + Sqr(dif.z) ) * 0.5;
    FCapsule.A:=Vector(FBox.Min.x + FCapsule.R, center.y, center.z);
    FCapsule.B:=Vector(FBox.Max.x - FCapsule.R, center.y, center.z);
  end else if (dif.y > dif.x) And (dif.y > dif.z) then begin
    //軸をY方向に取る
    FCapsule.A:=Vector(center.x, FBox.Min.y + FCapsule.R, center.z);
    FCapsule.B:=Vector(center.x, FBox.Max.y - FCapsule.R, center.z);
    FCapsule.R:=Sqrt( Sqr(dif.x) + Sqr(dif.z) ) * 0.5;
  end else begin
    //軸をZ方向に取る
    FCapsule.A:=Vector(center.x, center.y, FBox.Min.z + FCapsule.R);
    FCapsule.B:=Vector(center.x, center.y, FBox.Max.z - FCapsule.R);
    FCapsule.R:=Sqrt( Sqr(dif.x) + Sqr(dif.y) ) * 0.5;
  end;

  //半径をちょっと補正(インチキ版)
  //FCapsule.R:=FCapsule.R * 0.5;


  FSphere.Center:=center;
  FSphere.Radius:=NowLength(dif) * 0.5;

end;


//頂点配列とインデクス配列のサイズを再設定
procedure TSXMesh.SetupBuffers(newVertexCount, newIndexCount: UINT; FVF,
  VertexSize: DWord; Pool:D3DPOOL);
begin
  FFaceCount:=newIndexCount div 3;

  //まず、既存のVertexBufferの解放
  if FVBuf <> Nil then
    FVBuf.Free;

  if FIBuf <> Nil then
    FIBuf.Free;

  if newVertexCount > MaxIndex then begin
    DGCarad_PutDebugMessage(Format('SXLib : このハードウェアでは、最大 %d までしか頂点インデクスを振る事ができません',[maxIndex]));
  end;

  //VertexBufferの生成
  FVBuf:=TDGVertexBuffer.Create(FOwner, newVertexCount, VertexSize, 0, FVF, Pool, FVBufName);

  //IndexBufferの作成
  FIBuf:=TDGIndexBuffer.Create(FOwner, newIndexCount, 0, Pool, FIBufName);

end;







{ TSXFrame }

constructor TSXFrame.Create(parentFrame:TSXFrame);
var
  i:Integer;
begin
  inherited Create;

  //必須オブジェクトの生成

  //親子関係の初期化
  FAncestors:=TSXFrameList.Create;
  if ParentFrame <> Nil then begin
    FAncestors.Assign(parentFrame.Ancestors);   //先祖リストのコピー
    FAncestors.Add(parentFrame);                //先祖リストの末尾に、親を入れる

    ParentFrame.Children.Add(Self);                 //親の子リストに自分をつける
  end;

  FChildren:=TSXFrameList.Create;
  FParent:=parentFrame;

  //FBinds:=[];

  Style:=sxfsDefault;
  FEnableMeshT:=True;

  SortKey:=sxsMeshCenter;


  //変数とかの初期化
  FMatrix:=NowIdentityMatrix;
  FMeshMatrix:=NowIdentityMatrix;

  BlendMode:=sxbDefault;
  Material:=MakeMaterial(1.0,1.0,1.0, 1.0,1.0,1.0, 0.0,0.0,0.0, 0.0,0.0,0.0, 10.0);
  Lighting:=True;
  Specular:=True;
  FillMode:=D3DFILL_SOLID;

  Visibility:=sxvShow;
  EnableBounds:=False;
  Bounds.Center:=Vector(0,0,0);
  Bounds.Radius:=1.0;

  OnRender:=Nil;
  OnQueryZ:=Nil;
  OnQueryVisibility:=Nil;

  Mesh:=Nil;

  For i:=0 to High(FTextures) do
    Textures[i]:=Nil;

end;

destructor TSXFrame.Destroy;
begin

  while Children.Count > 0 do begin
    Children[0].Free;   //これで、再帰的に子フレームがFreeされる
  end;

  //リストオブジェクトの解放
  FChildren.Free;
  FAncestors.Free;

  //親のリストから自分をはずす
  if Parent <> Nil then
    Parent.Children.Delete(Parent.Children.IndexOf(Self));

  inherited;
end;


//自分の座標→ワールド座標　のための変換行列を得る
function TSXFrame.GetWorldMatrix: D3DMATRIX;
var
  mat:D3DMATRIX;
  i:Integer;
begin

  mat:=FMatrix;

  //祖先のマトリクスを 親→祖父… の順に乗じていく。
  //Ancestors[0] はNilであり、途中にNilは入らないという前提の下に計算
  For i:=Ancestors.Count-1 downto 1 do begin
    //MeshContainerの場合、マトリクスは常にIdentity
    if Ancestors[i].Style <> sxfsMeshContainer then
      mat:=NowCompositeMatrix(mat,Ancestors[i].FMatrix);
  end;

  result:=mat;
end;

//自分の座標→ワールド座標(回転成分のみ)　のための変換行列を得る
function TSXFrame.GetWorldMatrixRot:D3DMATRIX;
var
  mat:D3DMATRIX;
  i:Integer;
begin

  mat:=FMatrix;

  //祖先のマトリクスを 親→祖父… の順に乗じていく。
  //Ancestors[0] はNilであり、途中にNilは入らないという前提の下に計算
  For i:=Ancestors.Count-1 downto 1 do begin
    //MeshContainerの場合、マトリクスは常にIdentity
    if Ancestors[i].Style <> sxfsMeshContainer then
      mat:=NowCompositeRotMatrix(mat,Ancestors[i].FMatrix);
  end;

  result:=mat;
end;

//…Z軸をtargetフレーム内の座標posを向ける。worldフレームのY軸をupベクトルに指定し、Y軸のZ軸回りの傾きをbank度とする
procedure TSXFrame.LookAt(target:TSXFrame; const pos:D3DVector; world:TSXFrame; const bank:Integer);
var
  vRel:D3DVECTOR;     //Targetとの相対座標
  vecCeil:D3Dvector;  //天井のベクトル
  vecX,vecY,vecZ:D3DVECTOR;
  normX:Single;       //X軸の長さ(天井とZが平行かどうかのチェック用)
begin

  {
  //targetのposは、自分の座標系でどこなのか
  vRel:=WorldToLocal(target.LocalToWorld(pos));

  //向き = Z軸ベクトル
  vecZ:=NowNormalize(vRel);

  //YZ平面にvRelを射影して、X軸と外積を取って新しいY軸を得る
  vecY:=NowCrossProduct(Vector(0,vecZ.Y,vecZ.Z), Vector(1,0,0));

  vecY:=NowNormalize(vecY);

  SetOrientation(Self, vecZ, vecY);
  }


  //targetのposは、自分の座標系でどこなのか
  vRel:=WorldToLocal(target.LocalToWorld(pos));

  //向き = Z軸ベクトル
  vecZ:=NowNormalize(vRel);

  //天井の向き = worldのY軸
  vecCeil:=NowSub(WorldToLocal(world.LocalToWorld(Vector(0,1,0))),WorldToLocal(world.LocalToWorld(Vector(0,0,0))));
  vecCeil:=NowNormalize(vecCeil);

  //天井とZで外積を取って、Xの向きとする
  vecX:=NowCrossProduct(vecCeil, vecZ);

  //天井とZが平行な場合、右耳の方向をworldのX軸にする
  normX:=NowLength(vecX);
  if normX < 0.0001 then
    vecX:=NowSub(WorldToLocal(world.LocalToWorld(Vector(1,0,0))),WorldToLocal(world.LocalToWorld(Vector(0,0,0))));

  vecX:=NowNormalize(vecX);

  //XとZで外積を取って、Yの向きとする
  vecY:=NowNormalize(NowCrossProduct(vecZ, vecX));



  //以上で姿勢の計算完了〜セット
  SetOrientation(Self, vecZ,vecY);

  //バンク
  if bank <> 0 then
    SetTransform(Self, NowRotZMatrix(bank));

end;




//refに対する変換行列の設定
procedure TSXFrame.SetTransform(ref: TSXFrame; const mat: D3DMATRIX);
begin

  if ref = Self then begin
    //自分への変換行列
    FMatrix:=NowCompositeMatrix(mat, FMatrix);

  end else if ref = Parent then begin
    //親への変換行列
    FMatrix:=mat;

  end else begin

    //自分→ref→ワールド→親
    if Parent <> Nil then begin
      if ref <> Nil then
        FMatrix:=NowCompositeMatrix(
                  NowCompositeMatrix(
                    mat, ref.WorldMatrix),NowInvMatrix(Parent.WorldMatrix))
      else
        FMatrix:=NowCompositeMatrix(mat, NowInvMatrix(Parent.WorldMatrix));
    end else begin
      if ref <> Nil then
        FMatrix:=NowCompositeMatrix(
                    mat, ref.WorldMatrix)
      else
        FMatrix:=mat;
    end;

  end;

end;

//refに対する変換行列の取得
function TSXFrame.GetTransform(ref: TSXFrame): D3DMATRIX;
begin

  if ref = Self then begin
    //自分への変換を得る…単位行列に決まってる。
    result:=NowIdentityMatrix;

  end else if ref = Parent then begin

    //親へのオリエンテーションを得る
    result:=FMatrix;

  end else begin

    //自分→ワールド→ref
    if ref <> Nil then
      result:=NowCompositeMatrix(WorldMatrix, NowInvMatrix(ref.WorldMatrix))
    else
      result:=WorldMatrix;
  end;

end;


//姿勢の設定
procedure TSXFrame.SetOrientation(ref: TSXFrame; const vecZ: D3DVector;
  const vecY: D3DVector);
var
  tmpMat:D3DMATRIX;             //refフレームとの相対変換行列
  vTrans:Array[0..3] of Single; //平行移動成分の保存用
  vecX:D3DVECTOR;
begin

  vecX:=NowNormalize(NowCrossProduct(vecY, vecZ));

  if ref = Parent then begin

    //親への姿勢を作るだけなら、簡単
    with FMatrix do begin
      _11:=vecX.X;  _12:=vecX.Y;  _13:=vecX.Z;
      _21:=vecY.X;  _22:=vecY.Y;  _23:=vecY.Z;
      _31:=vecZ.X;  _32:=vecZ.Y;  _33:=vecZ.Z;
    end;

  end else begin

    //平行移動成分の保存
    vTrans[0]:=FMatrix._41;
    vTrans[1]:=FMatrix._42;
    vTrans[2]:=FMatrix._43;

    //変換
    with tmpMat do begin
      _11:=vecX.X;  _12:=vecX.Y;  _13:=vecX.Z;  _14:=0;
      _21:=vecY.X;  _22:=vecY.Y;  _23:=vecY.Z;  _24:=0;
      _31:=vecZ.X;  _32:=vecZ.Y;  _33:=vecZ.Z;  _34:=0;
      _41:=vTrans[0]; _42:=vTrans[1]; _43:=vTrans[2]; _44:=1;
    end;

    SetTransform(ref, tmpMat);

    //平行移動成分の復元
    with FMatrix do begin
      _41:=vTrans[0]; _42:=vTrans[1]; _43:=vTrans[2];
    end;

  end;

end;

//位置の設定
procedure TSXFrame.SetTranslation(ref: TSXFrame; const pos: D3Dvector);
var
  tmpMat:D3DMATRIX; //refフレームとの相対変換行列
  origMat:D3DMATRIX; //姿勢保持用
begin

  if ref = Parent then begin

    //親へのトランスレーションを作るだけなら、簡単
    with FMatrix do begin
      _41:=pos.X;   _42:=pos.Y;   _43:=pos.Z;
    end;

  end else begin

    origMat:=FMatrix;
    tmpMat:=FMatrix;

    with tmpMat do begin
      _41:=pos.X;   _42:=pos.Y;   _43:=pos.Z;
    end;

    SetTransform(ref, tmpMat);

    with FMatrix do begin
      _11:=OrigMat._11;  _12:=OrigMat._12;  _13:=OrigMat._13;
      _21:=OrigMat._21;  _22:=OrigMat._22;  _23:=OrigMat._23;
      _31:=OrigMat._31;  _32:=OrigMat._32;  _33:=OrigMat._33;
    end;

  end;

end;


procedure TSXFrame.GetOrientation(ref: TSXFrame; var vecZ,
  vecY: D3DVector);
var
  tmpMat:D3DMATRIX;   //結果算出用バッファ
begin

  tmpMat:=GetTransform(ref);

  with vecY, tmpMat do begin
    X:=_21; Y:=_22; Z:=_23;
  end;

  with vecZ, tmpMat do begin
    X:=_31; Y:=_32; Z:=_33;
  end;

end;


//姿勢だけをマトリクスで与える
procedure TSXFrame.SetOrientationMatrix(ref: TSXFrame;
  const mat: D3DMATRIX);
var
  pushedV:D3DVector;  //平行移動分保存用
begin

  //保存
  with pushedV do begin
    x:=FMatrix._41;  y:=FMatrix._42;  z:=FMatrix._43;
  end;

  if ref = Self then begin
    //自分への変換行列
    FMatrix:=NowCompositeMatrix(mat, FMatrix);

  end else if ref = Parent then begin
    //親への変換行列
    FMatrix:=mat;

  end else begin

    //自分→ref→ワールド→親
    if Parent <> Nil then begin
      if ref <> Nil then
        FMatrix:=NowCompositeMatrix(
                  NowCompositeMatrix(
                    mat, ref.WorldMatrix),NowInvMatrix(Parent.WorldMatrix))
      else
        FMatrix:=NowCompositeMatrix(mat, NowInvMatrix(Parent.WorldMatrix));
    end else begin
      if ref <> Nil then
        FMatrix:=NowCompositeMatrix(
                    mat, ref.WorldMatrix)
      else
        FMatrix:=mat;
    end;

  end;

  //復元
  with pushedV do begin
    FMatrix._41:=x;  FMatrix._42:=y;  FMatrix._43:=z;
  end;
end;


procedure TSXFrame.SetOrientationQuat(ref: TSXFrame;
  const quat: TQuaternion);
begin
  SetOrientationMatrix(ref, NowMatrixFromQuat(quat));
end;



function TSXFrame.GetTranslation(ref: TSXFrame):D3DVECTOR;
var
  tmpMat:D3DMATRIX;   //結果算出用バッファ
begin

  if ref = Parent then begin
    result.X:=FMatrix._41;
    result.Y:=FMatrix._42;
    result.Z:=FMatrix._43;
  end else begin
    tmpMat:=GetTransform(ref);
    result.X:=tmpMat._41;
    result.Y:=tmpMat._42;
    result.Z:=tmpMat._43;
  end;

end;

//フレーム内の座標→ワールド座標
function TSXFrame.LocalToWorld(vec: D3DVector): D3DVector;
begin
  result:=NowTransform(vec, WorldMatrix);
end;

//ワールド座標→フレーム内の座標
function TSXFrame.WorldToLocal(vec: D3DVector): D3DVector;
begin
  result:=NowTransform(vec, NowInvMatrix(WorldMatrix));
end;

//フレーム内での向きベクトルを、ワールド座標での向きベクトルに変換
function TSXFrame.LocalToWorldRot(rotvec:D3DVector):D3DVector;
begin
  result:=NowTransform(rotvec, WorldMatrixRot);
end;

//ワールド座標内の向きベクトルをフレーム内の向きベクトルに変換する
function TSXFrame.WorldToLocalRot(rotvec:D3DVector):D3DVector;
begin
  result:=NowTransform(rotvec, NowInvMatrixRot(WorldMatrixRot));
end;





//親フレームの変更
procedure TSXFrame.SetParent(const Value: TSXFrame);

  //先祖リストを再帰的に修正する
  procedure RebuildAncestors(me:TSXFrame);
  var
    i:Integer;
  begin
    For i:=0 to me.Children.Count-1 do begin
      //ヲレの先祖を先祖と崇めなさい
      me.Children[i].Ancestors.Assign(me.Ancestors);
      //ヲレを親と崇めなさい
      me.Children[i].Ancestors.Add(me);
      //子々孫々に渡ってそうしたまへ
      RebuildAncestors(me.Children[i]);
    end;
  end;

begin
  //元の親のリストから外す
  if FParent <> Nil then
    FParent.Children.Delete(FParent.Children.IndexOf(Self));


  if Value <> Nil then begin
    //新しい親のリストに加える
    Value.Children.Add(Self);
    //先祖リストを変える
    Ancestors.Assign(Value.Ancestors);
    Ancestors.Add(Value);
  end else begin
    Ancestors.Clear;
  end;

  //自分に子供がいるなら、それらの先祖リストも変える
  RebuildAncestors(Self);

  FParent := Value;
end;

//このフレームをカメラにした場合のビュー行列を計算
function TSXFrame.ViewMatrix: D3DMatrix;
begin
  result:=NowInvMatrix(WorldMatrix);
end;



//可動部分ナシを前提にして、下位フレームまですっぽり包むようなバウンディング球の計算
//注：オーナードロウやビルボードは考慮していない
procedure TSXFrame.UpdateBounds(onlyMyself:Boolean);

  function MergeBS(a,b:TSphere3D):TSphere3D;
  var
    d:Single; //中心間の距離
    mid,ofs:D3DVector;
  begin
    d:=NowDistance(a.Center, b.Center);

    if (d + b.R <= a.R) Or (b.R <= 0) then begin
      //aがbを完全に包んでいるか、bは存在しない
      result:=a;
    end else if (d + a.R <= b.R) Or (a.R <= 0) then begin
      //bがaを完全に包んでいるか、aは存在しない
      result:=b;
    end else begin
      //↑2つの条件は、d=0 の場合を含んでいる
      //以下、どちらも含んだり含まれたりしてない場合
      // (Ca + Cb)/2  +  ((d + r2 - r1) / 2d) * (C2 - C1)
      mid:=NowScale( NowAdd(a.Center, b.Center), 0.5);
      ofs:=NowScale( NowSub( b.Center, a.Center), (d + b.R - a.R) / (2.0*d));
      result.Center:=NowAdd(mid, ofs);

      result.Radius:=(d + a.R + b.R) / 2.0;
    end;

  end;

  //自フレームのバウンディング球と子孫フレームのバウンディング球をマージ
  function CalcBS(me:TSXFrame; worldM:D3DMatrix):TSphere3D;
  var
    i:Integer;
    meToWorldM,meshM:D3DMatrix;
    m33:TMatrix33;
  begin

    if me.Visibility = sxvHide then begin
      //子孫も含めてHideになってるなら、このフレーム以下は考慮しない
      result.R:=-1;
      result.Center:=Vector(0,0,0);
    end else begin
      meToWorldM:=NowCompositeMatrix(me.Matrix,worldM);

      if (me.Visibility = sxvHideMyself) then begin
        //自分だけ見えない
        result.R:=-1;
        result.Center:=Vector(0,0,0);
        For i:=0 to Children.Count-1 do begin
          result:=MergeBS(CalcBS(me.Children[i], meToWorldM), result);
        end;
      end else begin
        //見える
        if me.EnableBounds then begin
          //バウンディング球が指定されているなら、それを返す
          result.Center:=NowTransform(me.Bounds.Center, meToWorldM);
          result.R:=me.Bounds.R;
        end else begin
          //メッシュのバウンディング球と子孫フレームのバウンディング球をマージして返す
          if me.Mesh <> Nil then begin
            if me.EnableMeshTransform then begin
              meshM:=NowCompositeMatrix(me.MeshMatrix, meToWorldM);
              m33:=NowMatrix44To33(meshM);
              result.Center:=NowTransform(me.Mesh.Sphere.Center, meshM);
              result.R:=NowDeterminant33(m33) * me.Mesh.Sphere.R;
            end else begin
              result.Center:=NowTransform(me.Mesh.Sphere.Center, meToWorldM);
              result.R:=me.Mesh.Sphere.R;
            end;
          end else begin
            result.Center:=Vector(0,0,0);
            result.R:=-1;
          end;

          For i:=0 to me.Children.Count-1 do begin
            result:=MergeBS(CalcBS(me.Children[i], meToWorldM), result);
          end;
        end;
      end;

    end;

    if not onlyMyself then begin
      me.Bounds:=result;
      me.Bounds.Center:=NowTransform(me.Bounds.Center, NowInvMatrix(meToWorldM));
      // me.WorldToLocal(me.Bounds.Center);
    end;

  end;

begin
  if Parent = Nil then
    Bounds:=CalcBS(Self, NowIdentityMatrix)
  else
    Bounds:=CalcBS(Self, Parent.WorldMatrix);

  //ゲットしてきたのは、ワールド座標表記なので、翻訳する
  Bounds.Center:=WorldToLocal(Bounds.Center);
end;

//下位フレームも含めて全部のフレームのBSをEnableに
procedure TSXFrame.ActivateBounds;

  procedure ActivateBS(me:TSXFrame);
  var
    i:Integer;
  begin
    me.EnableBounds:=True;
    For i:=0 to me.Children.Count-1 do begin
      ActivateBS(me.Children[i]);
    end;
  end;
begin
  ActivateBS(Self);
end;

//下位フレームも含めて全部のフレームのBSをDisableに
procedure TSXFrame.DeactivateBounds;

  procedure DeactivateBS(me:TSXFrame);
  var
    i:Integer;
  begin
    me.EnableBounds:=False;
    For i:=0 to me.Children.Count-1 do begin
      DeactivateBS(me.Children[i]);
    end;
  end;
begin
  DeactivateBS(Self);
end;

//最初のテクスチャゲット
function TSXFrame.GetTexture: TDGTexture;
begin
  result:=FTextures[0];
end;

//最初のテクスチャセット
procedure TSXFrame.SetTexture(const Value: TDGTexture);
begin
  FTextures[0]:=Value;
end;

//テクスチャゲット
function TSXFrame.GetTextures(idx: Integer): TDGTexture;
begin
  result:=FTextures[idx];
end;

//テクスチャセット
procedure TSXFrame.SetTextures(idx: Integer; const Value: TDGTexture);
begin
  FTextures[idx]:=Value;
end;





{ TSXFrameList }

//コピー
procedure TSXFrameList.Assign(source: TSXFrameList);
var
  i:Integer;
begin
  Self.Clear;

  for i:=0 to source.Count-1 do begin
    Self.Add(Pointer(source.Frames[i]));
  end;
end;

constructor TSXFrameList.Create;
begin
  inherited;
end;

destructor TSXFrameList.Destroy;
begin
  inherited;
end;

function TSXFrameList.GetFrame(idx: Integer): TSXFrame;
begin
  result:=Items[idx];
end;

procedure TSXFrameList.SetFrame(idx: Integer; const Value: TSXFrame);
begin
  Items[idx]:=Pointer(Value);

end;


//最後のRender時に即した変換行列を求める。カリングされたフレームの変換行列を知りたい時などに用いる
procedure TSXFrame.CalcMatrixOnRender;
var
  i:Integer;
  startG:Integer; //何世代前から変換を開始すればいいか？

  //変換済みの親フレームの行列から、frameの行列を算出
  procedure CalcMatrix(frame:TSXFrame);
  begin
    with frame do begin
      RenderedMatrix:=NowCompositeMatrix(Matrix, Parent.MatrixOnRender);

      if EnableMeshTransform then
        RenderedMeshMatrix:=NowCompositeMatrix(MeshMatrix, RenderedMatrix)
      else
        RenderedMeshMatrix:=RenderedMatrix;

      //検査完了サインを入れる
      FValidRenderedMatrix:=True;
    end;
  end;

begin
  //変換済みのフレームがないか、先祖をさかのぼって調べる
  StartG:=1;
  For i:=Ancestors.Count-1 downto 0 do
    if Ancestors[i].ValidMatrixOnRender then
      startG:=i+1;

  //先祖→…→親までの変換行列を計算する
  for i:=startG to Ancestors.Count-1 do
    CalcMatrix(Ancestors[i]);

  //自分の変換行列の計算
  CalcMatrix(Self);

end;

//再帰的に自分のフレーム以下のブレンドモードをセット
procedure TSXFrame.SetBlendModeRcv(Value: TSXBlendMode);
  procedure traverse(frame:TSXFrame);
  var
    i:Integer;
  begin
    frame.BlendMode:=Value;
    for i:=0 to frame.Children.Count-1 do
      traverse(frame.Children[i]);
  end;
begin
  traverse(Self);  
end;

procedure TSXFrame.SetFillModeRcv(Value:D3DFILLMODE);
  procedure traverse(frame:TSXFrame);
  var
    i:Integer;
  begin
    frame.FillMode:=Value;
    for i:=0 to frame.Children.Count-1 do
      traverse(frame.Children[i]);
  end;
begin
  traverse(Self);
end;

procedure TSXFrame.SetLightingRcv(Value: Boolean);
  procedure traverse(frame:TSXFrame);
  var
    i:Integer;
  begin
    frame.Lighting:=Value;
    for i:=0 to frame.Children.Count-1 do
      traverse(frame.Children[i]);
  end;
begin
  traverse(Self);
end;

procedure TSXFrame.SetSpecularRcv(Value: Boolean);
  procedure traverse(frame:TSXFrame);
  var
    i:Integer;
  begin
    frame.Specular:=Value;
    for i:=0 to frame.Children.Count-1 do
      traverse(frame.Children[i]);
  end;
begin
  traverse(Self);
end;

procedure TSXFrame.SetTextureWrapRcv(Value: DWord);
  procedure traverse(frame:TSXFrame);
  var
    i:Integer;
  begin
    frame.TextureWrap:=Value;
    for i:=0 to frame.Children.Count-1 do
      traverse(frame.Children[i]);
  end;
begin
  traverse(Self);
end;



procedure TSXFrame.SetMaterialRcv(Value: D3DMATERIAL8);
  procedure traverse(frame:TSXFrame);
  var
    i:Integer;
  begin
    frame.Material:=Value;
    for i:=0 to frame.Children.Count-1 do
      traverse(frame.Children[i]);
  end;
begin
  traverse(Self);
end;

procedure TSXFrame.SetTexturesRcv(idx:Integer; Value:TDGTexture);
  procedure traverse(frame:TSXFrame);
  var
    i:Integer;
  begin
    frame.Textures[idx]:=Value;
    for i:=0 to frame.Children.Count-1 do
      traverse(frame.Children[i]);
  end;
begin
  traverse(Self);
end;

{ TSXScene }

constructor TSXScene.Create(ADG:TDGCarad; debugName:String = '');
begin
  inherited Create;

  Queue:=TSXRenderingQueue.Create(ADG, Self);
  SortQueue:=TSXRenderingQueue.Create(ADG, Self);
  TopQueue:=TSXRenderingQueue.Create(ADG, Self);

  FStateRestorer:=TSXStateRestorer.Create(ADG);

  //DGCaradにリソースとして登録
  ResID:=ADG.RegisterResouce(Self, Nil, Recover, Nil, Recover, debugName);
  FDebugName:=debugName;

  //ブレンドモードの登録
  {
  sxbDefault = 0;     //普通、透けない
  sxbAdd = 1;         //加算合成
  sxbAlpha = 2;       //半透明
  sxbMulNeg = 3;      //エセ減算合成(反転してからMul)
  sxbMul = 4;         //乗算合成
  sxbColorKey = 5;    //αテストだけ、透けない
  }
  RegisterBlendMode(BlendInfo(False, False, D3DCMP_ALWAYS, False, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLENDOP_ADD));
  RegisterBlendMode(BlendInfo(True, False, D3DCMP_ALWAYS, True, D3DBLEND_SRCALPHA, D3DBLEND_ONE, D3DBLENDOP_ADD));
  RegisterBlendMode(BlendInfo(True, False, D3DCMP_ALWAYS, True, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLENDOP_ADD));
  RegisterBlendMode(BlendInfo(True, False, D3DCMP_ALWAYS, True, D3DBLEND_ZERO, D3DBLEND_INVSRCCOLOR, D3DBLENDOP_ADD));
  RegisterBlendMode(BlendInfo(True, False, D3DCMP_ALWAYS, True, D3DBLEND_ZERO, D3DBLEND_SRCCOLOR, D3DBLENDOP_ADD));
  RegisterBlendMode(BlendInfo(False, True, D3DCMP_GREATER, False, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLENDOP_ADD));


  FCamera:=Nil;
  SortAlpha:=True;
  DG:=ADG;

  InspectDevCaps;
end;

destructor TSXScene.Destroy;
begin
  DG.UnregisterResource(ResID);

  SortQueue.Free;
  Queue.Free;
  TopQueue.Free;

  inherited;
end;


//描画
procedure TSXScene.Render(rootFrame: TSXFrame);
var
  viewMat:D3DMATRIX;
  //curTexture:TDGTexture;
  //curMaterial:D3DMATERIAL8;
  //curBlendMode:TSXBlendMode;

  sphere:TSphere3D;

  procedure QueingFrame(frame:TSXFrame; const mat:D3DMATRIX{; parentsBinds:TSXFrameBinds; hidden:Boolean});
  var
    i:Integer;
    visibility:TSXVisibility;
  begin
    frame.FRendered:=False;
    frame.FValidRenderedMatrix:=False;

    //可視性のチェック
    if Assigned(frame.OnQueryVisibility) then begin
      //イベントが設定してあるなら、それに任せる
      frame.OnQueryVisibility(frame, Self, DG, visibility);
    end else begin
      //デフォルトでは、Visibilityプロパティに従う
      visibility:=frame.Visibility;

      //ただし、BoundingSphereを使うようなら、それに従う
      if visibility <> sxvHide then begin
        if frame.EnableBounds then begin
          if not SphereVisibility(NowCompositeMatrix(frame.Matrix, mat), frame.Bounds, FFarZ) then begin
            visibility:=sxvHide;
          end;
        end;
      end;
    end;

    //sxvHide(子孫も含めて隠す)なら何もしない
    if visibility = sxvHide then
      exit;

    //子フレーム無し、かつ、描画する必要なしなら、何もしない
    if frame.Children.Count = 0 then
      if ((frame.Mesh = Nil) And (not Assigned(frame.OnRender))) Or (visibility = sxvHideMySelf) then
        exit;



    //変換行列の計算
    if frame.Style = sxfsMeshContainer then
      frame.RenderedMatrix:=mat
    else
      frame.RenderedMatrix:=NowCompositeMatrix(frame.Matrix, mat);

    if frame.EnableMeshTransform then
      frame.RenderedMeshMatrix:=NowCompositeMatrix(frame.MeshMatrix, frame.RenderedMatrix)
    else
      frame.RenderedMeshMatrix:=frame.RenderedMatrix;

    frame.FValidRenderedMatrix:=True;

    //キューに放り込む

    if visibility = sxvShow then begin

      if (frame.Mesh <> Nil) And (not Assigned(frame.OnQueryVisibility)) then begin
        //Meshが付いている場合

        //Meshのバウンディング球でカリングを試みる
        if frame.EnableMeshTransform then begin
          sphere.Center:=NowAdd(frame.Mesh.Sphere.Center, Vector(frame.MeshMatrix._41, frame.MeshMatrix._42, frame.MeshMatrix._43));
          sphere.R:=NowDeterminant33(NowMatrix44To33(frame.MeshMatrix)) * frame.Mesh.Sphere.R;
        end else begin
          sphere:=frame.Mesh.Sphere;
        end;

        if SphereVisibility(frame.RenderedMatrix, frame.Mesh.Sphere,FFarZ) then begin

          if FBlendInfos[frame.BlendMode].Sort then begin
            SortQueue.PushMesh(frame); //(frame.BlendMode, frame.Mesh, frame, meshM, curTexture, curMaterial, frame.OnRender);
          end else begin
            Queue.PushMesh(frame);//(frame.BlendMode, frame.Mesh, frame, meshM, curTexture, curMaterial, frame.OnRender);
          end;
        end;

        frame.FRendered:=True;
      end else if Assigned(frame.OnRender) then begin
        //メッシュが付いていなくても、オーナードロウの場合
        //メッシュが付いていても、QueryVisibilityで判定した場合
        //カリングしない(しようが無い)で、そのままGo!

        //ただし、オーナードロウなのにメッシュが付いてなくて、QueryZも無いなら前後関係の判定のしようがないので、何も描かない
        if (frame.mesh <> Nil) Or (Assigned(frame.OnQueryZ)) then begin

          if FBlendInfos[frame.BlendMode].Sort then begin
            SortQueue.PushMesh(frame);//(frame.BlendMode, frame.Mesh, frame, meshM, curTexture, curMaterial, frame.OnRender);
          end else begin
            Queue.PushMesh(frame); //(frame.BlendMode, frame.Mesh, frame, meshM, curTexture, curMaterial, frame.OnRender);
          end;
        end;

        frame.FRendered:=True;
      end;
    end;

    //子供の描画
    For i:=0 to frame.Children.Count-1 do begin
      QueingFrame(frame.Children[i], frame.RenderedMatrix{, parentsBinds + frame.Binds});
    end;

  end;

begin

  if FCamera=Nil then begin
    DGCarad_PutDebugMessage('SXLib: カメラフレームが設定されていません');
    exit;
  end;


  //ビュー行列の作成…(ワールド座標系でのカメラフレームの姿勢)の逆
  viewMat := CameraFrame.ViewMatrix;

  DG.D3DDevice.SetTransform(D3DTS_VIEW, viewMat);


  //再帰的にキューに入れる
  if rootFrame.Parent = Nil then
    QueingFrame(rootFrame, NowIdentityMatrix{, []})
  else
    QueingFrame(rootFrame, rootFrame.Parent.WorldMatrix{, []});


  { キューに入ってる物体を描く }
  Queue.Render(False, viewMat, FVP);
  SortQueue.Render(SortAlpha, viewMat, FVP);
  TopQueue.Render(False,viewMat, FVP);


  //テクスチャに何も指定しない状態で、一連のレンダリングを終える
  DG.D3DDevice.SetTexture(0, Nil);
end;

procedure TSXScene.Clear(dwFlags:DWord; color:DWORD; z:Single; stencil:DWord);
begin
  DG.D3DDevice.Clear(1, Nil, dwFlags,color,z,stencil);
end;

//透視変換の設定
procedure TSXScene.SetProjection(fovH, aspect, nearZ,farZ:Single);    //透視変換の設定
var
  q,w,h:Single;
begin

  q:=farZ / (farZ-nearZ);
  w:=aspect * Cos(fovH*0.5)/Sin(fovH*0.5);
  h:=Cos(fovH*0.5)/Sin(fovH*0.5);

  FProjectionMatrix:=NowIdentityMatrix;

  FProjectionMatrix._11 :=  w;                              //
  FProjectionMatrix._22 :=  h;                              //
  FProjectionMatrix._33 :=  q;                              //   Q = Zf / (Zf-Zn)
  FProjectionMatrix._34 :=  1.0;
  FProjectionMatrix._43 := -q*nearZ;                        //   -QZn
  FProjectionMatrix._44 :=  0.0;

  DG.D3DDevice.SetTransform( D3DTS_PROJECTION, FProjectionMatrix );

  FFarZ:=farZ;
  FNearZ:=nearZ;
  FFovH:=fovH;
  FFovV:=ArcTan(1.0/w);
  FAspectRatio:=aspect;
end;



procedure TSXScene.SetViewPort(left,top,right,bottom:DWord);
begin
  ZeroMemory(@FVP, sizeof(FVP));

  with FVP do begin
    X:=left;
    Y:=top;
    Width:=right - left;
    Height:=bottom - top;
    MinZ:=0;
    MaxZ:=1.0;
  end;

  FScreenMatrix:=NowIdentityMatrix;
  with FScreenMatrix do begin
    _11:=FVP.Width/2.0;
    _22:=-(FVP.Height/2.0);
    _33:=FVP.MaxZ-FVP.MinZ;
    _41:=FVP.X + FVP.Width/2.0;
    _42:=FVP.Y + FVP.Height/2.0;
    _43:=FVP.MinZ;
  end;


  DG.D3DDevice.SetViewport(FVP);
end;

//D3D解放(解像度変更)からのリカバリ
procedure TSXScene.Recover;
begin
  DG.D3DDevice.SetViewport(FVP);
  DG.D3DDevice.SetTransform( D3DTS_PROJECTION, FProjectionMatrix );

  //解像度とかドライバ変更にともなって、デバイスCapsが変わったかもしれんので、再調査
  InspectDevCaps;
end;


//ビルボードを押し込む
procedure TSXScene.PushBillboard (ref:TSXFrame; pos:D3DVector;
  points:Array of TSXVertexBB; tex:TDGTexture;
  blendMode:TSXBlendMode = sxbColorKey; scaling:Boolean = True;
  filter:Boolean = True; topMost:Boolean = False);
begin
  if topMost then begin
    TopQueue.PushBillboard(scaling, filter, blendMode, ref, pos, tex, points);
  end else begin
    if FBlendInfos[blendMode].Sort then begin
      SortQueue.PushBillboard(scaling, filter, blendMode, ref, pos, tex, points);
    end else begin
      Queue.PushBillboard(scaling, filter,  blendMode, ref, pos, tex, points);
    end;
  end;
  //フラグを立てる
  ref.BBAttached:=True;

end;

//スプライトを押し込む
procedure TSXScene.PushSprite(pos:D3DVector; points:Array of TSXVertexSP;
  tex:TDGTexture; blendMode:TSXBlendMode = sxbColorKey;
  filter:Boolean = True; topMost:Boolean = False);
var
  ppos:D3Dvector;
begin

  if topMost then begin
    ppos:=pos;
    ppos.z:=FNearZ;
    TopQueue.PushSprite(filter, blendMode, ppos, tex, points);
  end else begin
    if FBlendInfos[blendMode].Sort then begin
      SortQueue.PushSprite(filter, blendMode, pos, tex, points);
    end else begin
      Queue.PushSprite(filter, blendMode, pos, tex, points);
    end;
  end;

end;


//球体の可視判定
function TSXScene.SphereVisibility(ref: TSXFrame; sphere:TSphere3D; depth:Single): Boolean;
begin
  result:=SphereVisibility(ref.worldMatrix,  sphere, depth);
end;


function TSXScene.SphereVisibility(worldM:D3DMATRIX; sphere: TSphere3D;
  depth: Single): Boolean;
var
  p:D3Dvector;
  viewM,projM:D3DMATRIX;
  fH,fV:Extended;
  SinFovH,CosFovH,SinFovV,CosFovV:Single;
begin

  result:=False;

  //球の半径が０以下なら、見えるはずがない
  if sphere.R <=0 then begin
    exit;
  end;

  viewM:=NowCompositeMatrix(worldM, CameraFrame.ViewMatrix);
  p:=NowTransform(sphere.Center, viewM);

  //視界の後方や、depthより遠くにあるってのは論外
  if (p.z < -sphere.R) Or (p.z > depth+sphere.R) then begin
    exit;
  end;

  //縦・横の画角を求める
  DG.D3DDevice.GetTransform(D3DTS_PROJECTION, projM);
  fH:=FFovH;
  fV:=FFovV;

  //画角のSin,Cosを出しておく
  SinFovH:=Sin(fH);   CosFovH:=Cos(fH);
  SinFovV:=Sin(fV);   CosFovV:=Cos(fV);

  //XZ平面での判定…画角分だけ球の中心を回転させた時、コーンの中心から球の中心までが、球の半径の値以下になるかを見る
  if ((p.x*CosFovH - p.z*SinFovH) > sphere.R)  Or  ((p.x*CosFovH + p.z*SinFovH) < -sphere.R) then begin
    exit;
  end;

  //YZ平面での判定
  if ((p.y*CosfovV - p.z*SinfovV) > sphere.R)  Or  ((p.y*CosfovV + p.z*SinfovV) < -sphere.R) then begin
    exit;
  end;

  result:=True;

end;


//ブレンドモードを登録し、ブレンドモード番号を返す
function TSXScene.RegisterBlendMode(info: TSXBlendInfo): TSXBlendMode;
begin
  SetLength(FBlendInfos, Length(FBlendInfos) + 1);
  FBlendInfos[High(FBlendInfos)]:=info;

  result:=High(FBlendInfos);
end;

//DevCapsに関係する情報の更新
procedure TSXScene.InspectDevCaps;
var
  caps:D3DCAPS8;
begin
  DG.D3D.GetDeviceCaps(DG.Adapter, DG.DeviceType, caps);

  FBlendOpEnable:=(caps.PrimitiveMiscCaps And D3DPMISCCAPS_BLENDOP) <> 0;
end;

{ TSXLight }

constructor TSXLight.Create(DDCompo:TDGCarad; index:DWord);
begin
  inherited Create;

  DG:=DDCompo;
  FIndex:=index;
  FEnabled:=False;
  FUpdate:=True;

  //デフォ値の設定
  ZeroMemory(@Params, Sizeof(Params));
  with Params do begin
    Type_:=D3DLIGHT_DIRECTIONAL;

    Diffuse.r:=1.0;
    Diffuse.g:=1.0;
    Diffuse.b:=1.0;

    Direction:=Vector(0,0,1);
  end;
end;

destructor TSXLight.Destroy;
begin
  inherited;
end;

//D3Dにライトを登録
procedure TSXLight.SetEnabled(const Value: Boolean);
begin
  FEnabled := Value;

  DG.D3DDevice.SetLight(FIndex, Params);
  DG.D3DDevice.LightEnable(FIndex, FEnabled);

end;


//お便利るーちんず
procedure TSXLight.SetupDiffuse(_R, _G, _B: Single);
begin
  with Params.Diffuse do begin
    R:=_R;
    G:=_G;
    B:=_B;
  end;

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;

procedure TSXLight.SetupSpecular(_R, _G, _B: Single);
begin
  with Params.Specular do begin
    R:=_R;
    G:=_G;
    B:=_B;
  end;

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;

procedure TSXLight.SetupAmbient(_R, _G, _B: Single);
begin
  with Params.Ambient do begin
    R:=_R;
    G:=_G;
    B:=_B;
  end;

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;

procedure TSXLight.SetupColors(difR, difG, difB, specR, specG, specB, ambR,
  ambG, ambB: Single);
begin
  with Params.Diffuse do begin
    R:=difR;
    G:=difG;
    B:=difB;
  end;
  with Params.Specular do begin
    R:=specR;
    G:=specG;
    B:=specB;
  end;
  with Params.Ambient do begin
    R:=ambR;
    G:=ambG;
    B:=ambB;
  end;

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;

procedure TSXLight.SetupRanges(range, att0, att1, att2: Single);
begin
  with Params do begin
    Range:=range;
    Attenuation0:=att0;
    Attenuation1:=att1;
    Attenuation2:=att2;
  end;

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;

procedure TSXLight.SetupDirectional(dir: D3DVector);
begin
  Params.Type_:=D3DLIGHT_DIRECTIONAL;
  Params.Direction:=dir;

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;

procedure TSXLight.SetupPoint(pos: D3DVector);
begin
  Params.Type_:=D3DLIGHT_POINT;
  Params.Position:=pos;

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;


procedure TSXLight.SetupSpot(pos, dir: D3DVector; theta, phi,
  falloff: Single);
begin
  Params.Type_:=D3DLIGHT_SPOT;
  Params.Direction:=dir;

  Params.Theta:=theta;
  Params.Phi:=phi;
  Params.Falloff:=falloff;

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;


//フレームの位置・向きにセット
procedure TSXLight.FitFrame(target: TSXFrame);
var
  mat:D3DMATRIX;
begin

  if target = Nil then
    mat:=NowIdentityMatrix
  else
    mat:=target.WorldMatrix;

  Params.Direction:=Vector(mat._31, mat._32, mat._33);
  Params.Position:=Vector(mat._41, mat._42, mat._43);

  if FUpdate then
    DG.D3DDevice.SetLight(FIndex, Params);
end;


procedure TSXLight.BeginUpdate;
begin
  FUpdate:=False;
end;

procedure TSXLight.EndUpdate;
begin
  FUpdate:=True;
  DG.D3DDevice.SetLight(FIndex, Params);
end;


{ TSXLightGroup }

constructor TSXLightGroup.Create(ADG:TDGCarad; capacity:DWord; debugName:String = '');
var
  i:Integer;
begin
  inherited Create;

  DG:=ADG;
  ResID:=DG.RegisterResouce(Self, Nil, Recover, Nil, Recover, debugName);
  FDebugName:=debugName;

  FCapacity:=capacity;
  SetLength(FLights, capacity);

  For i:=0 to capacity-1 do begin
    FLights[i]:=TSXLight.Create(DG, i);
  end;
end;

destructor TSXLightGroup.Destroy;
var
  i:Integer;
begin

  DG.UnregisterResource(ResID);

  For i:=0 to FCapacity-1 do begin
    FLights[i].Free;
  end;

  FLights:=Nil;

  inherited;
end;

procedure TSXLightGroup.DisableAll;
var
  i:Integer;
begin
  For i:=0 to FCapacity-1 do begin
    FLights[i].Enabled:=True;
  end;
end;

procedure TSXLightGroup.EnableAll;
var
  i:Integer;
begin
  For i:=0 to FCapacity-1 do begin
    FLights[i].Enabled:=False;
  end;
end;

function TSXLightGroup.GetLights(idx: DWord): TSXLight;
begin
  //範囲チェック
  if idx >= FCapacity then begin
    DGCarad_PutDebugMessage('TSXLightGroup @ SXLib: 範囲外のライトを参照しようとしました');
    result:=Nil;
    exit;
  end;

  result:=FLights[idx];
end;


//Enabledじゃないライトを探索
function TSXLightGroup.GetUnusedLight: TSXLight;
var
  i:Integer;
begin

  For i:=0 to FCapacity-1 do begin
    if not FLights[i].Enabled then begin
      result:=FLights[i];
      exit;
    end;
  end;

  result:=Nil;
end;


//スペキュラ強度の設定
{
procedure TSXLightGroup.SetSpecularPower(const Value: Single);
var
  mtrl:D3DMATERIAL7;
begin
  FSpecularPower := Value;

  ZeroMemory(@mtrl, Sizeof(mtrl));
  mtrl.power:=FSpecularPower;

  DG.D3DDevice.SetMaterial(@mtrl);
end;
}

procedure TSXLightGroup.Recover;
var
  i:Integer;
begin

  For i:=0 to FCapacity-1 do begin
    if FLights[i].Enabled then
      FLights[i].Enabled:=True;
  end;

//  SpecularPower:=FSpecularPower;
end;




{ TSXMatetial }

constructor TSXMaterial.Create(ADG: TDGCarad);
begin
  ZeroMemory(@Params, Sizeof(Params));
  DG:=ADG;
  FUpdate:=True;
end;

procedure TSXMaterial.SetupAmbient(_R, _G, _B: Single);
begin
  with Params.Ambient do begin
    R:=_R;  G:=_G;  B:=_B;
  end;

  if FUpdate then
    DG.D3DDevice.SetMaterial(Params);
end;

procedure TSXMaterial.SetupDiffuse(_R, _G, _B: Single);
begin
  with Params.Diffuse do begin
    R:=_R;  G:=_G;  B:=_B;
  end;

  if FUpdate then
    DG.D3DDevice.SetMaterial(Params);
end;

procedure TSXMaterial.SetupEmissive(_R, _G, _B: Single);
begin
  with Params.Emissive do begin
    R:=_R;  G:=_G;  B:=_B;
  end;

  if FUpdate then
    DG.D3DDevice.SetMaterial(Params);
end;

procedure TSXMaterial.SetupSpecular(_R, _G, _B: Single);
begin
  with Params.Specular do begin
    R:=_R;  G:=_G;  B:=_B;
  end;

  if FUpdate then
    DG.D3DDevice.SetMaterial(Params);
end;

procedure TSXMaterial.SetupSpecularPower(pow: Single);
begin
  with Params do begin
    Power:=pow;
  end;

  if FUpdate then
    DG.D3DDevice.SetMaterial(Params);
end;


procedure TSXMaterial.SetupColors(difR, difG, difB, specR, specG, specB,
  ambR, ambG, ambB, emsR, emsG, emsB, pow: Single);
begin

  with Params.Diffuse do begin
    R:=difR;
    G:=difG;
    B:=difB;
  end;
  with Params.Specular do begin
    R:=specR;
    G:=specG;
    B:=specB;
  end;
  with Params.Ambient do begin
    R:=ambR;
    G:=ambG;
    B:=ambB;
  end;
  with Params.Emissive do begin
    R:=emsR;
    G:=emsG;
    B:=emsB;
  end;

  Params.Power:=pow;

  if FUpdate then
    DG.D3DDevice.SetMaterial(Params);

end;


procedure TSXMaterial.BeginUpdate;
begin
  FUpdate:=False;
end;

procedure TSXMaterial.EndUpdate;
begin
  FUpdate:=True;
  DG.D3DDevice.SetMaterial(Params);
end;





{ ヘルパー関数 }

function MakeMaterial(difR,difG,difB, specR,specG,specB, ambR,ambG,ambB, emsR,emsG,emsB, pow:Single):D3DMATERIAL8;
begin
  with Result.Diffuse do begin
    R:=difR;
    G:=difG;
    B:=difB;
    A:=1.0;
  end;
  with Result.Specular do begin
    R:=specR;
    G:=specG;
    B:=specB;
    A:=1.0;
  end;
  with Result.Ambient do begin
    R:=ambR;
    G:=ambG;
    B:=ambB;
    A:=1.0;
  end;
  with Result.Emissive do begin
    R:=emsR;
    G:=emsG;
    B:=emsB;
    A:=1.0;
  end;

  Result.Power:=pow;
end;


//α指定可、D3DCOLOR版のマテリアル作成
function MakeMaterial(dif, spec, amb, ems:D3DCOLOR; pow:Single):D3DMATERIAL8;
begin
  with Result.Diffuse do begin
    A:=dif shr 24;
    R:=dif shr 16 and $FF;
    G:=dif shr 8 and $FF;
    B:=dif and 255;
  end;
  with Result.Specular do begin
    A:=spec shr 24;
    R:=spec shr 16 and $FF;
    G:=spec shr 8 and $FF;
    B:=spec and 255;
  end;
  with Result.Ambient do begin
    A:=amb shr 24;
    R:=amb shr 16 and $FF;
    G:=amb shr 8 and $FF;
    B:=amb and 255;
  end;
  with Result.Emissive do begin
    A:=ems shr 24;
    R:=ems shr 16 and $FF;
    G:=ems shr 8 and $FF;
    B:=ems and 255;
  end;
  Result.Power:=pow;
end;


{ TSXRenderingQueue }

//コンストラクタ
constructor TSXRenderingQueue.Create(AOwner: TDGCarad; _Scene:TSXScene);
begin
  FOwner:=AOwner;
  Scene:=_Scene;

  //デフォ値の代入
  BillboardCapacity:=32;
  MeshCapacity:=32;
  SpriteCapacity:=32;

  FBillboardCount:=0;
  FMeshCount:=0;
  FSpriteCount:=0;
end;

//デストラクタ
destructor TSXRenderingQueue.Destroy;
begin
  BillboardQueue:=Nil;
  MeshQueue:=Nil;
  SpriteQueue:=Nil;

  OrderTable:=Nil;
  Zs:=Nil;

  inherited;
end;

//何もせずに、中身だけ消す
procedure TSXRenderingQueue.Flush;
begin
  FBillboardCount:=0;
  FMeshCount:=0;  
  FSpriteCount:=0;
end;

//ビルボードを入れる
procedure TSXRenderingQueue.PushBillboard(scaling:Boolean; filter:Boolean;
 blendMode: TSXBlendMode; ref:TSXFrame; pos:D3DVector; tex:TDGTexture;
 pts:Array of TSXVertexBB); //ビルボード
begin
  //必要なら拡張する
  if BillboardCount >= BillboardCapacity then
    BillboardCapacity:=BillboardCapacity * 2;


  BillboardQueue[FBillboardCount].scaling:=scaling;
  BillboardQueue[FBillboardCount].filter:=filter;
  BillboardQueue[FBillboardCount].blend:=blendMode;
  BillboardQueue[FBillboardCount].tex:=tex;
  BillboardQueue[FBillboardCount].ref:=ref;
  BillboardQueue[FBillboardCount].pos:=NowHomoginize(pos);
  Move(pts[0], BillboardQueue[FBillboardCount].Pts[0], Sizeof(TSXVertexBB)*4);  //4頂点分コピー

  Inc(FBillboardCount);
end;

//スプライトを入れる
procedure TSXRenderingQueue.PushSprite(filter:Boolean;
 blendMode:TSXBlendmode; pos:D3Dvector; tex:TDGTexture; pts:Array of TSXVertexSP);
begin
  //必要なら拡張する
  if SpriteCount >= SpriteCapacity then
    SpriteCapacity:=SpriteCapacity * 2;

  SpriteQueue[FSpriteCount].filter:=filter;
  SpriteQueue[FSpriteCount].blend:=blendMode;
  SpriteQueue[FSpriteCount].tex:=tex;
  SpriteQueue[FSpriteCount].pos:=pos;
  Move(pts[0], SpriteQueue[FSpriteCount].pts[0], Sizeof(TSXVertexSP)*4);  //4頂点分コピー

  Inc(FSpriteCount);
end;

//メッシュを入れる
{
procedure TSXRenderingQueue.PushMesh(blendMode:TSXBlendMode; mesh: TSXMesh;
frame:TSXFrame; mat: D3DMatrix; tex: TDGTexture; mtrl: D3DMATERIAL8;
 event:TSXRenderEvent);
}
procedure TSXRenderingQueue.PushMesh(frame:TSXFrame);
begin
  //必要なら拡張する
  if MeshCount >= MeshCapacity then
    MeshCapacity:=MeshCapacity * 2;

  MeshQueue[FMeshCount].frame:=frame;

  {
  MeshQueue[FMeshCount].mat:=meshM;
  MeshQueue[FmeshCount].blend:=blendMode;
  MeshQueue[FmeshCount].mat:=mat;
  MeshQueue[FmeshCount].mesh:=mesh;
  MeshQueue[FmeshCount].mtrl:=mtrl;
  MeshQueue[FmeshCount].tex:=tex;
  MeshQueue[FmeshCount].OnRender:=event;
  MeshQueue[FMeshCount].frame:=frame;
  }

  Inc(FMeshCount);
end;

//ビルボード用の情報入れをサイズを変える
procedure TSXRenderingQueue.SetBillBoardCapacity(const Value: Integer);
begin
  FBillboardCapacity := Value;
  SetLength(BillboardQueue, Value);

  SetLength(OrderTable, BillboardCapacity + MeshCapacity + SpriteCapacity);
  SetLength(Zs, BillboardCapacity + MeshCapacity + SpriteCapacity);
end;

//メッシュ用の情報入れをサイズを変える
procedure TSXRenderingQueue.SetMeshCapacity(const Value: Integer);
begin
  FMeshCapacity := Value;
  SetLength(MeshQueue, Value);

  SetLength(OrderTable, BillboardCapacity + MeshCapacity + SpriteCapacity);
  SetLength(Zs, BillboardCapacity + MeshCapacity + SpriteCapacity);
end;

//スプライト用の情報入れをサイズを変える
procedure TSXRenderingQueue.SetSpriteCapacity(const Value: Integer);
begin
  FSpriteCapacity := Value;
  SetLength(SpriteQueue, Value);

  SetLength(OrderTable, BillboardCapacity + MeshCapacity + SpriteCapacity);
  SetLength(Zs, BillboardCapacity + MeshCapacity + SpriteCapacity);
end;



//ソート
procedure TSXRenderingQueue.Sort(viewParam:D3DVIEWPORT8);
var
  i:Integer;
  mtrx:D3DMatrix;
  viewM,projM,screenM,viewProjScreenM:D3DMatrix;  //ビュー行列・射影行列・スクリーン行列←３つの合成
  z:Single;
  zC,zMax,zMin:Single;         //メッシュの遠方・近方での比較用
  prevBBFrame:TSXFrame; //前回かかれたビルボードの収まっていたフレーム 

  //vを変換して、Z値だけ返す
  function TransformedZ(v:D3DVector; worldM:D3DMatrix):Single;
  begin
    //射影行列まで通すので変換した際に、カメラからのZ値がWに返る
    result:=NowTransform(NowHomoginize(v), NowCompositeMatrix(worldM, viewProjScreenM)).W;
  end;


  //くいっくそーと
  procedure QSort(left,right:Integer);
  var
    i,j,tmp:Integer;
    s,t:Single;
  begin
    if (left<right) then begin
      s:=Zs[(left+right) Shr 1];
      i:=left-1;
      j:=right+1;
      while True do begin

        repeat
          Inc(i);
        until (Zs[i]<=s);

        repeat
          Dec(j);
        until (Zs[j]>=s);

        if (i>=j) then
          break;

        t:=Zs[i];
        Zs[i]:=Zs[j];
        Zs[j]:=t;

        tmp:=OrderTable[i];
        OrderTable[i]:=OrderTable[j];
        OrderTable[j]:=tmp;
      end;
      QSort(left,i-1);
      QSort(j+1,right);
    end;
  end;

begin
  //オーダリングテーブル初期化
  // メッシュの分だけ、それ以外は各カテゴリのZ座標計算と一緒に作る
  For i:=0 to MeshCount - 1 do
    OrderTable[i]:=i;


  { まず、全部のメッシュ・BBのZ値を計算}
  FOwner.D3DDevice.GetTransform(D3DTS_PROJECTION, projM);
  FOwner.D3DDevice.GetTransform(D3DTS_VIEW, viewM);

  ScreenM:=Scene.ScreenMatrix;

  viewProjScreenM:=NowCompositeMatrix(NowCompositeMatrix(viewM, projM), screenM);


  //メッシュのソート用Z値を求める
  For i:=0 to MeshCount-1 do begin

    if Assigned(MeshQueue[i].frame.OnQueryZ) then begin
      //カスタムZ計算におまかせ
      MeshQueue[i].frame.OnQueryZ(MeshQueue[i].frame, Scene, FOwner, Zs[i]);

    end else begin

      //SortKeyを頼りに求める
      z:=0;
      Case MeshQueue[i].frame.SortKey of

        //メッシュの中心でソート
        sxsMeshCenter:
          z:=TransformedZ(MeshQueue[i].frame.mesh.Sphere.Center, MeshQueue[i].frame.MeshMatrixOnRender);

        //メッシュの遠いほうでソート
        sxsMeshFar: begin
          mtrx:=NowCompositeMatrix(MeshQueue[i].frame.MeshMatrixOnRender, viewProjScreenM);
          zC:=NowTransform(NowHomoginize(MeshQueue[i].frame.mesh.Sphere.Center), mtrx).W;
          zMax:=NowTransform(NowHomoginize(MeshQueue[i].frame.mesh.Box.Max), mtrx).W;
          zMin:=NowTransform(NowHomoginize(MeshQueue[i].frame.mesh.Box.Min), mtrx).W;

          z:=zC + Abs(zMax-zMin)/2;
        end;

        //メッシュの近いところでソート
        sxsMeshNear: begin
          mtrx:=NowCompositeMatrix(MeshQueue[i].frame.MeshMatrixOnRender, viewProjScreenM);
          //中心の変換後の位置と、AABBのエッジのZ方向の長さを求める
          zC:=NowTransform(NowHomoginize(MeshQueue[i].frame.mesh.Sphere.Center), mtrx).W;
          zMax:=NowTransform(NowHomoginize(MeshQueue[i].frame.Mesh.Box.Max), mtrx).W;
          zMin:=NowTransform(NowHomoginize(MeshQueue[i].frame.mesh.Box.Min), mtrx).W;

          z:=zC - Abs(zMax-zMin)/2;
        end;
      end;

      Zs[i]:=z;
    end;

  end;


  //ビルボードのZ値を求める
  prevBBFrame:=Nil;
  FVisibleBillboardCount:=0;
  For i:=0 to BillboardCount-1 do begin
    //mtrxに変換行列を代入
    if prevBBFrame <> BillboardQueue[i].ref then begin
      if not BillboardQueue[i].ref.ValidMatrixOnRender then
        BillboardQueue[i].ref.CalcMatrixOnRender;

      mtrx:=NowCompositeMatrix(BillboardQueue[i].ref.RenderedMatrix, viewProjScreenM);
      prevBBFrame:=BillboardQueue[i].ref;
    end;

    BillboardQueue[i].pos:=NowTransform(BillboardQueue[i].pos, mtrx);

    //W<0なら、描画しない…ソートの対象が減って軽くなる
    if BillboardQueue[i].pos.W > 0 then begin
      Zs[FVisibleBillboardCount+MeshCount]:=BillboardQueue[i].pos.W;
      OrderTable[FVisibleBillboardCount+MeshCount]:=i + MeshCount;
      Inc(FVisibleBillboardCount);
    end;
  end;

  //スプライトのZ値を求める
  FVisibleSpriteCount:=0;
  For i:=0 to SpriteCount-1 do begin
    if SpriteQueue[i].pos.z > 0 then begin
      Zs[MeshCount+FVisibleBillboardCount+FVisibleSpriteCount]:=SpriteQueue[i].pos.z;
      OrderTable[MeshCount+FVisibleBillboardCount+FVisibleSpriteCount]:=i + MeshCount + BillboardCount;
      Inc(FVisibleSpriteCount);
    end;
  end;


  //クイックソート…ヘタにハッシュとかするより、あまりビルボードとかが多くないうちはこちらの方がトク
  QSort(0,MeshCount + FVisibleBillboardCount + FVisibleSpriteCount - 1);


  //以上でOrderTable作成完了
end;


//描画
procedure TSXRenderingQueue.Render(needSort:Boolean; viewMat:D3DMATRIX; viewParam:D3DVIEWPORT8);
var
  i:Integer;

  prevTex:TDGTexture;   //前回使ったテクスチャへの参照
  prevSpec:Boolean;     //前回描画したものは、スペキュラを計算したか
  prevLit:Boolean;      //前回描画したものは、ライティングの計算をしたか
  prevFrame:TSXFrame;   //直前に描画したビルボードの入ってたフレーム
  prevBlend:TSXBlendMode;
  prevWrap:DWord;       //テクスチャラップ
  prevFill:D3DFILLMODE;      //フィルモード
  prevFilter:Boolean;
  prevDrawnIsMesh:Boolean;


  storedFilterMag,storedFilterMin:D3DTEXTUREFILTERTYPE;

  center:THgVector;
  pts:Array[0..3] of D3DTLVERTEX;
  m:D3DMATRIX;
  projM,screenM,viewProjScreenM:D3DMATRIX;

  zF,zN:Single; //遠近クリッピング面までの距離(スプライトのZバッファへの書き込み値算出用)
  zMax,zMin:Single;

  ratio_dx,ratio_dy:Single; //ビューポートの半分のサイズ、ビルボード描画時の補正値として

  //ブレンドモードの変更
  procedure SetBlendMode(bm:TSXBlendMode);
  begin
    //今までのと同じだったら何もしなくて良い
    if prevBlend = bm then
      exit;

    with Scene.FBlendInfos[bm], FOwner.D3DDevice do begin

      if AlphaBlendEnable then begin
        //αブレンドあり
        SetRenderState(D3DRS_ALPHABLENDENABLE, DWord(True));

        //Z書き込みなし(比較は有り)
        SetRenderState(D3DRS_ZWRITEENABLE, DWord(False));

        SetRenderState(D3DRS_SRCBLEND, DWord(SrcBlend));
        SetRenderState(D3DRS_DESTBLEND, DWord(DestBlend));
        if Scene.FBlendOpEnable then
          SetRenderState(D3DRS_BLENDOP, DWord(BlendOp));
      end else begin
        //αブレンドなし
        SetRenderState(D3DRS_ALPHABLENDENABLE, DWord(False));

        //Z書き込みあり
        FOwner.D3DDevice.SetRenderState(D3DRS_ZWRITEENABLE, DWord(True));
      end;

      if AlphaTestEnable then begin
        SetRenderState(D3DRS_ALPHATESTENABLE, DWord(AlphaTestEnable));
        SetRenderState(D3DRS_ALPHAFUNC, DWord(AlphaFunc));
      end;
    end;
    prevBlend:=bm;

  end;

  //フィルタの設定(BB,スプライト用)
  procedure SetFilterBBSP(enable:Boolean);
  begin
    if prevDrawnIsMesh then begin
      //前にメッシュ描いてたなら、フィルタ状態保存
      FOwner.D3DDevice.GetTextureStageState(0, D3DTSS_MAGFILTER, DWord(storedFilterMag));
      FOwner.D3DDevice.GetTextureStageState(0, D3DTSS_MINFILTER, DWord(storedFilterMin));
      prevDrawnIsMesh:=False;
    end else begin
      //BB / スプライトなら、その状態から判定
      if enable = prevFilter then
        exit;
    end;

    if enable then begin
      FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, Ord(D3DTEXF_LINEAR));
      FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_MINFILTER, Ord(D3DTEXF_LINEAR));
    end else begin
      FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, Ord(D3DTEXF_POINT));
      FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_MINFILTER, Ord(D3DTEXF_POINT));
    end;
    prevFilter:=enable;
  end;

  procedure RestoreFilter;
  begin
    FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, DWord(StoredFilterMag));
    FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_MINFILTER, DWord(StoredFilterMin));
  end;

  //レンダリングステート周りの設定
  procedure SetDevs(blend:TSXBlendMode; tex:TDGTexture; spec,lit:Boolean; wrap:DWord; fill:D3DFILLMODE);
  begin
    SetBlendMode(blend);

    if prevTex <> tex then begin
      if tex = Nil then
        FOwner.D3DDevice.SetTexture(0, Nil)
      else
        FOwner.D3DDevice.SetTexture(0, tex.Texture);
      prevTex:=tex;
    end;

    if prevSpec <> spec then begin
      FOwner.D3DDevice.SetRenderState(D3DRS_SPECULARENABLE, DWord(spec));
      prevSpec:=spec;
    end;

    if prevLit <> lit then begin
      FOwner.D3DDevice.SetRenderState(D3DRS_LIGHTING, DWord(lit));
      prevLit:=lit;
    end;

    if prevWrap <> wrap then begin
      FOwner.D3DDevice.SetRenderState(D3DRS_WRAP0, wrap);
      prevWrap:=wrap;
    end;

    if prevFill <> fill then begin
      FOwner.D3DDevice.SetRenderState(D3DRS_FILLMODE, Ord(fill));
      prevFill:=fill;
    end;

  end;

  procedure DrawMesh(idx:Integer);
  begin
    //フィルタ
    if not prevDrawnIsMesh then begin
      RestoreFilter;
      prevDrawnIsMesh:=True;
    end;

    with MeshQueue[idx].frame do
      SetDevs(BlendMode, Texture, Specular, Lighting, TextureWrap, FillMode);

    //行列の設定
    FOwner.D3DDevice.SetTransform(D3DTS_WORLD,(MeshQueue[idx].frame.MeshMatrixOnRender));

    //マテリアルの設定
    FOwner.D3DDevice.SetMaterial(MeshQueue[idx].Frame.Material);

    //オーナードロウなら、イベントハンドラを呼ぶ
    if Assigned(MeshQueue[idx].frame.OnRender) then begin
      //現在のステートをキャプチャしとく
      //FOwner.D3DDevice.CaptureStateBlock(token);

      //イベントを呼んでオーナードロウ
      MeshQueue[idx].frame.OnRender(MeshQueue[idx].Frame, Scene, FOwner);

      //テクスチャ戻す
      if prevTex <> Nil then
        FOwner.D3DDevice.SetTexture(0, prevTex.Texture)
      else
        FOwner.D3DDevice.SetTexture(0, Nil);

      //ステートを戻す
      //FOwner.D3DDevice.ApplyStateBlock(token);
    end else begin
      //描画
      MeshQueue[idx].frame.Mesh.Draw;
    end;

  end;


  //ビルボードの描画
  procedure DrawBB(idx:Integer);
  var
    j:Integer;
  begin
    //フィルタ
    SetFilterBBSP(BillboardQueue[idx].filter);

    //レンダリングステートとかの設定
    with BillboardQueue[idx] do
      SetDevs(blend, tex, False, False, 0, D3DFILL_SOLID);

    //ビルボードの中心の視野座標を求める
    if not needSort then begin
      if prevFrame <> BillboardQueue[idx].ref then begin
        if BillboardQueue[idx].ref = Nil then
          m:=viewProjScreenM
        else begin
          if not BillboardQueue[idx].ref.ValidMatrixOnRender then
            BillboardQueue[idx].ref.CalcMatrixOnRender;
          m:=NowCompositeMatrix(BillboardQueue[idx].ref.RenderedMatrix,viewProjScreenM);
        end;

        prevFrame:=BillboardQueue[idx].ref;
      end;

      center:=BillboardQueue[idx].pos;
      center:=NowTransform(center,m);
    end else begin
      //ソート済みの場合は、ソートの段階で変換されている
      center:=BillboardQueue[idx].pos;
    end;

    //W値が正なら描画


    if center.W > 0 then begin
      //ビルボードの中心の画面での座標を求める
      center:=NowViewFrustumToScreen(center);

      if BillboardQueue[idx].scaling then begin
        //スケーリングあり
        For j:=0 to 3 do begin
          with BillboardQueue[idx].Pts[j] do begin
            Pts[j].sx:=center.x + BillboardQueue[idx].Pts[j].dx * ratio_dx * center.W;
            Pts[j].sy:=center.y + BillboardQueue[idx].Pts[j].dy * ratio_dx * center.W;
            Pts[j].sz:=center.z;
            Pts[j].rhw:=center.w;
            Pts[j].color:=color;
            Pts[j].specular:=$00000000;
            Pts[j].tu:=tu;
            Pts[j].tv:=tv;
          end;
        end;
      end else begin
        //スケーリングなし
        For j:=0 to 3 do begin
          with BillboardQueue[idx].Pts[j] do begin
            Pts[j].sx:=center.x + BillboardQueue[idx].Pts[j].dx;
            Pts[j].sy:=center.y + BillboardQueue[idx].Pts[j].dy;
            Pts[j].sz:=center.z;
            Pts[j].rhw:=center.w;
            Pts[j].color:=color;
            Pts[j].specular:=$00000000;
            Pts[j].tu:=tu;
            Pts[j].tv:=tv;
          end;
        end;
      end;

      FOwner.D3DDevice.SetVertexShader(D3DFVF_TLVERTEX);
      FOwner.D3DDevice.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, @Pts, Sizeof(D3DTLVertex));
    end;


    //参照フレームに付いたフラグを消す
    if BillboardQueue[idx].ref <> Nil then
      BillboardQueue[idx].ref.BBAttached:=False;
  end;

  //スプライトの描画
  procedure DrawSP(idx:Integer);
  var
    i:Integer;
    bz:Single;
  begin
    //フィルタ
    SetFilterBBSP(SpriteQueue[idx].filter);

    with SpriteQueue[idx] do
      SetDevs(blend, tex, False, False, 0, D3DFILL_SOLID);

    //Z値が正なら描画
    if SpriteQueue[idx].pos.z > 0 then begin
      bz:=(SpriteQueue[idx].pos.z - zN) / (zF - zN) * (zF / SpriteQueue[idx].pos.z) *  (zMax-zMin) + zMin;

      For i:=0 to 3 do begin
        with SpriteQueue[idx].Pts[i] do begin
            Pts[i].sx:=SpriteQueue[idx].pos.x + dx;
            Pts[i].sy:=SpriteQueue[idx].pos.y + dy;
            Pts[i].sz:=bz;
            Pts[i].rhw:=0.5;        //てきとー
            Pts[i].color:=color;
            Pts[i].specular:=$00000000;
            Pts[i].tu:=tu;
            Pts[i].tv:=tv;
        end;
      end;

      FOwner.D3DDevice.SetVertexShader(D3DFVF_TLVERTEX);
      FOwner.D3DDevice.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, @Pts, Sizeof(D3DTLVertex));
    end;
  end;

begin

  //ビルボード用、視野座標→画面 の変換行列の計算
  FOwner.D3DDevice.GetTransform(D3DTS_PROJECTION, projM);
  ScreenM:=Scene.ScreenMatrix;

  viewProjScreenM:=NowCompositeMatrix(viewMat,projM);
  viewProjScreenM:=NowCompositeMatrix(viewProjScreenM,ScreenM);

  //スプライト用、視点座標系Z→Zバッファ変換用係数の計算
  zN:=Scene.NearZ;
  zF:=Scene.FarZ;
  zMin:=Scene.FVP.MinZ;
  zMax:=Scene.FVP.MaxZ;

  //ビルボード用、ビューポートのサイズ
  //W = 1のビルボードは、このサイズで表示される
  ratio_dx:=viewParam.Width / 2.0;
  ratio_dy:=-viewParam.Height / 2.0;

  //変数の初期化
  prevFrame:=Nil;

  prevTex:=Nil;
  FOwner.D3DDevice.SetTexture(0,Nil);

  prevSpec:=True;
  FOwner.D3DDevice.SetRenderState(D3DRS_SPECULARENABLE, DWord(True));

  prevLit:=True;
  FOwner.D3DDevice.SetRenderState(D3DRS_LIGHTING, DWord(True));

  prevFill:=D3DFILL_SOLID;
  FOwner.D3DDevice.SetRenderState(D3DRS_FILLMODE, Ord(D3DFILL_SOLID));

  prevWrap:=0;
  FOwner.D3DDevice.SetRenderState(D3DRS_WRAP0, 0);

  prevDrawnIsMesh:=True;

  //不透明体のαブレンド設定
  prevBlend:=sxbDefault;
  FOwner.D3DDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, DWord(False));  //ブレンドしない
  FOwner.D3DDevice.SetRenderState(D3DRS_ZENABLE, DWord(True));            //Z比較あり
  FOwner.D3DDevice.SetRenderState(D3DRS_ZWRITEENABLE, DWord(True));       //Z書き込みあり

  //デフォルトのテクスチャブレンド設定
  FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_COLORARG1, DWord(D3DTA_TEXTURE));
  FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_COLORARG2, DWord(D3DTA_DIFFUSE));
  FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_COLOROP,   DWord(D3DTOP_MODULATE));

  FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_ALPHAARG1, DWord(D3DTA_TEXTURE));
  FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_ALPHAARG2, DWord(D3DTA_DIFFUSE));
  FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_ALPHAOP,   DWord(D3DTOP_MODULATE));


  //フィルタの設定…はしなくていい(ビルボード・スプライトは個々に持っていて、メッシュはRenderStateに準じるから)
  {
  prevFilter:=False;
  FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_MINFILTER, Ord(D3DTEXF_POINT));
  FOwner.D3DDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, Ord(D3DTEXF_POINT));
  }



  if not needSort then begin
    //メッシュ描画
    For i:=0 to MeshCount-1 do begin
      DrawMesh(i);
    end;
    //ビルボード描画
    For i:=0 to BillboardCount-1 do begin
      DrawBB(i);
    end;
    //スプライト描画
    For i:=0 to SpriteCount-1 do begin
      DrawSP(i);
    end;
  end else begin
    //ソートして、オーダリングテーブルに書き込まれた順で描画
    Sort(viewParam);

    //ソートの段階でビルボードについては可視・不可視が判定済み
    For i:=0 to MeshCount + FVisibleBillboardCount + FVisibleSpriteCount -1 do begin
      if OrderTable[i] < MeshCount then begin
        DrawMesh(OrderTable[i]);
      end else if OrderTable[i] < MeshCount + BillboardCount then begin
        DrawBB(OrderTable[i] - MeshCount);
      end else begin
        DrawSP(OrderTable[i] - MeshCount - BillboardCount);
      end;
    end;
  end;

  //キューを空に
  FMeshCount:=0;
  FBillBoardCount:=0;
  FSpriteCount:=0;
end;





{ TSXMeshList }

procedure TSXMeshList.Clear;
begin
  While Count <> 0 do begin
    Delete(0);
  end;
  Inherited;
end;

constructor TSXMeshList.Create(ADG:TDGCarad);
begin
  inherited Create;

  DG:=ADG;
end;

procedure TSXMeshList.Delete(idx:Integer);
begin
  Meshes[idx].Free;
  Inherited Delete(idx);
end;


//追加されているメッシュを全部解放する
destructor TSXMeshList.Destroy;
begin
  Clear;

  inherited;
end;

function TSXMeshList.GetMesh(idx: Integer): TSXMesh;
begin
  result:=Items[idx];
end;

procedure TSXMeshList.SetMesh(idx: Integer; const Value: TSXMesh);
begin
  Items[idx]:=Value;
end;

//ロード
procedure TSXMeshList.LoadFromStream(Source: TStream);
var
  i,cnt:Integer;
  flag:Byte;
  newMesh:TSXMesh;
begin
  //まず中身を消す
  Clear;


  //項目数
  Source.ReadBuffer(cnt, 4);

  For i:=0 to cnt-1 do begin
    Source.ReadBuffer(flag, 1);
    if flag = 0 then begin
      Add(Nil);
    end else begin
      newMesh:=TSXMesh.Create(DG);
      newMesh.LoadFromStream(Source);
      Add(newMesh);
    end;
  end;
end;

procedure TSXMeshList.LoadFromFile(filename: String; qdaName:String = '');
var
  src:TFileStream;
  mem:TMemoryStream;
begin
  if qdaName = '' then begin
    //ファイルから読み込み
    src:=TFileStream.Create(filename, fmOpenRead);
    LoadFromStream(src);
    src.Free;
  end else begin
    //アーカイブ読み込み
    mem:=ExtractFromQDAFile(QDAName, filename);
    LoadFromStream(mem);
    mem.Free;
  end;
end;


//セーブ
procedure TSXMeshList.SaveToStream(Dest: TStream);
var
  i:Integer;
  cnt:Integer;
  zero,one:Byte;
begin
  //項目数
  cnt:=Count;
  Dest.WriteBuffer(cnt, Sizeof(Integer));

  //メッシュの保存、もし、メッシュがNilなら、バイト値 0 を1バイト入れる
  //そうじゃないなら、バイト値1を入れる
  zero:=0;
  one:=1;
  For i:=0 to Count-1 do begin
    if Meshes[i] = Nil then begin
      Dest.WriteBuffer(zero, 1);
    end else begin
      Dest.WriteBuffer(one, 1);
      Meshes[i].SaveToStream(dest);
    end;
  end;
end;

procedure TSXMeshList.SaveToFile(filename: String);
var
  dst:TFileStream;
begin
  dst:=TFileStream.Create(filename, fmCreate);
  SaveToStream(dst);
  dst.Free;
end;






{ TSXTextWriter }

//コンストラクタ
constructor TSXTextWriter.Create(ADG:TDGCarad);
begin
  DG:=ADG;

  //下絵ビットマップ
  SourceBMP:=TBitmap.Create;
  SourceBMP.Canvas.Font.Color:=clWhite;
  SourceBMP.Canvas.Brush.Color:=clBlack;
  SourceBMP.Canvas.Brush.Style:=bsSolid;
  SourceBMP.PixelFormat:=pf24bit;
end;

//デストラクタ
destructor TSXTextWriter.Destroy;
begin
  SourceBMP.Free;
  inherited;
end;

//カキコ
procedure TSXTextWriter.Write(x,y:Integer; str: String; Target: TDGTexture);
var
  ext:TSize;
  lr:D3DLOCKED_RECT;
  ddsd:D3DSURFACE_DESC;
  src:TMemoryStream;
  tx,ty:Integer;      //BMPへのカキコ位置
  b,r:Integer;        //ロックする右下座標
begin
  //やる気なくした、帰る
  if (x >= Target.Width) Or (y >= Target.Height) then
    exit;

  //サイズゲット
  ext:=SourceBMP.Canvas.TextExtent(str);

  //文字カキカキ
  SourceBMP.Width:=ext.cx;
  SourceBMP.Height:=ext.cy;

  if x < 0 then begin
    tx:=x;
  end else begin
    tx:=0;
  end;

  if y < 0 then begin
    ty:=y;
  end else begin
    ty:=0;
  end;

  SourceBMP.Canvas.TextOut(tx,ty,str);

  src:=TMemoryStream.Create;
  SourceBMP.SaveToStream(src);
  src.Position:=0;

  //転送
  if ext.cx + x < Target.Width then
    r:=ext.cx + x
  else
    r:=Target.Width;

  if ext.cy + y < Target.Height then
    b:=ext.cy + y
  else
    b:=Target.Height;

  lr:=Target.LockRect(0, Rect(x,y,r,b), 0);

  Target.Texture.GetLevelDesc(0, ddsd);
  LoadBMPStreamToLockedRect(lr, Rect(x,y,r,b), ddsd, src);

  Target.UnlockRect;
  Target.BlueToAlpha(Rect(x,y,r,b));
  Target.Fill(Rect(x,y,r,b), $FFFFFFFF, True);

  src.Free;
end;


function TSXTextWriter.CreateStringTexture(str: String; fmt:Array of D3DFORMAT; debugStr:String = ''): TDGTexture;
var
  ext:TSize;
begin
  ext:=SourceBMP.Canvas.TextExtent(str);
  result:=TDGTexture.Create(DG, fmt, 0, D3DPOOL_MANAGED, 1, debugStr);
  result.SetSize(ext.cx, ext.cy);
  Write(0,0, str, result);
end;



//プロパティハンドラ
function TSXTextWriter.GetFont: TFont;
begin
  result:=SourceBMP.Canvas.Font;
end;

procedure TSXTextWriter.SetFont(const Value: TFont);
begin
  SourceBMP.Canvas.Font:=Value;
end;

function TSXTextWriter.TextHeight(str: String): Integer;
begin
  result:=SourceBMP.Canvas.TextHeight(str);
end;

function TSXTextWriter.TextWidth(str: String): Integer;
begin
  result:=SourceBMP.Canvas.TextWidth(str);
end;



function TSXTextWriter.GetBrush: TBrush;
begin
  result:=SourceBMP.Canvas.Brush;
end;

procedure TSXTextWriter.SetBrush(const Value: TBrush);
begin
  SourceBMP.Canvas.Brush:=Value;
end;








{ TSXStateRestorer }

constructor TSXStateRestorer.Create(ADG: TDGCarad);
begin
  DG:=ADG;
  Memory; //バッファクリア
end;

//記憶…というよりフラグ消去
procedure TSXStateRestorer.Memory;
var
  i:Integer;
begin
  //フラグ消去、だけ
  RSFlag:=[];
  RSCount:=0;

  For i:=0 to High(TSSFlag) do begin
    TSSFlag[i]:=[];
    TSSCount[i]:=0;
  end;

end;

//巻き戻し
procedure TSXStateRestorer.Restore;
var
  i,j:Integer;
begin
  For i:=0 to RSCount-1 do begin
    DG.D3DDevice.SetRenderState(RSList[i], SavedRS[Ord(RSList[i])]);
  end;

  For i:=0 to High(TSSFlag) do begin
    For j:=0 to TSSCount[i]-1 do begin
      DG.D3DDevice.SetTextureStageState(i, TSSList[i,j], SavedTSS[i,Ord(TSSList[i,j])]);
    end;
  end;
end;

function TSXStateRestorer.SetRS(RSType: D3DRENDERSTATETYPE; Value:DWORD): HRESULT;
begin

  //手をつけられてないフラグなら、保存しておく
  if not (Ord(RSType) in RSFlag) then begin
    RSFlag:=RSFlag + [Ord(RSType)];
    DG.D3DDevice.GetRenderState(RSType, SavedRS[Ord(RSType)]);
    RSList[RSCount]:=RSType;
    Inc(RSCount);
  end;

  result:=DG.D3DDevice.SetRenderState(RSType, DWord(Value));

end;

function TSXStateRestorer.SetTSS(Stage: UINT;
  TSSType: D3DTEXTURESTAGESTATETYPE; Value:DWORD): HRESULT;
begin
  //手をつけられてないフラグなら、保存しておく
  if not (Ord(TSSType) in TSSFlag[Stage]) then begin
    TSSFlag[Stage]:=TSSFlag[Stage] + [Ord(TSSType)];
    DG.D3DDevice.GetTextureStageState(Stage, TSSType, SavedTSS[Stage, Ord(TSSType)]);
    TSSList[Stage, TSSCount[Stage]]:=TSSType;
    Inc(TSSCount[Stage]);
  end;

  result:=DG.D3DDevice.SetTextureStageState(Stage, TSSType, DWord(Value));

end;



{ TSXFrameEnvMapped }

constructor TSXFrameEnvMapped.Create(parent: TSXFrame; ADG:TDGCarad);
var
  caps:D3DCAPS8;
begin
  inherited Create(parent);

  DG:=ADG;
  DG.D3D.GetDeviceCaps(DG.Adapter, DG.DeviceType, caps);

  //シングルパスレンダ可能か調べる
  FSinglePathable:= caps.MaxTextureBlendStages >= 2;

  EnvMap:=Nil;
  OnRender:=RenderEvent;
end;

procedure TSXFrameEnvMapped.RenderEvent(Sender: TSXFrame; Scene: TSXScene;
  DG: TDGCarad);
var
  mtrx:D3DMATRIX;
begin
  Scene.StateRestorer.Memory;

  //テクスチャ座標変換行列をゲット
  mtrx:=NowZeroMatrix;
  with mtrx do begin
    _11:=0.5;
                _22:=-0.5;

    _41:=0.5;   _42:=0.5;
  end;

  if Texture = Nil then
    DG.D3DDevice.SetTexture(0, Nil)
  else
    DG.D3DDevice.SetTexture(0, Texture.Texture);

  //ステージ0(最初)…下地
  //テクスチャと頂点色の掛け算(普通)、なので、略

  if FSinglePathable then begin
    if EnvMap = Nil then
      DG.D3DDevice.SetTexture(1, Nil)
    else
      DG.D3DDevice.SetTexture(1, EnvMap.Texture);

    //ステージ1…反射マップ
    Scene.StateRestorer.SetTSS(1, D3DTSS_TEXTURETRANSFORMFLAGS, Ord(D3DTTFF_COUNT2));
    DG.D3DDevice.SetTransform(D3DTS_TEXTURE1, mtrx);

    //テクスチャ座標には、法線の反射ベクトルを入れる
    Scene.StateRestorer.SetTSS(1, D3DTSS_TEXCOORDINDEX,Ord(D3DTSS_TCI_CAMERASPACENORMAL));
    //RGBはステージ0の結果とテクスチャ色の掛け算(普通)
    Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG1, DWord(D3DTA_CURRENT));
    Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG2, DWord(D3DTA_TEXTURE));
    Scene.StateRestorer.SetTSS(1, D3DTSS_COLOROP,   Ord(D3DTOP_ADD));
    //αはステージ0そのまま
    

    //実際のメッシュの描画はこれ一行でOK
    Sender.Mesh.Draw;
  end else begin
    //まず、ステージ0での結果をレンダ
    Sender.Mesh.Draw;

    //テクスチャ替え
    if EnvMap = Nil then
      DG.D3DDevice.SetTexture(0, Nil)
    else
      DG.D3DDevice.SetTexture(0, EnvMap.Texture);

    //ステージ1…反射マップ
    //テクスチャ座標には、法線の反射ベクトルを入れる
    Scene.StateRestorer.SetTSS(0, D3DTSS_TEXCOORDINDEX,Ord(D3DTSS_TCI_CAMERASPACENORMAL));

    Scene.StateRestorer.SetTSS(0, D3DTSS_TEXTURETRANSFORMFLAGS, Ord(D3DTTFF_COUNT2));
    DG.D3DDevice.SetTransform(D3DTS_TEXTURE0, mtrx);

    Scene.StateRestorer.SetTSS(0, D3DTSS_COLORARG1, DWord(D3DTA_TEXTURE));
    Scene.StateRestorer.SetTSS(0, D3DTSS_COLOROP,   Ord(D3DTOP_SELECTARG1));

    //で、ADDで合成
    Scene.StateRestorer.SetRS(D3DRS_ALPHABLENDENABLE, Ord(True));
    Scene.StateRestorer.SetRS(D3DRS_SRCBLEND, Ord(D3DBLEND_ONE));
    Scene.StateRestorer.SetRS(D3DRS_DESTBLEND, Ord(D3DBLEND_ONE));

    //もう一度描画
    Sender.Mesh.Draw;

    //戻す
    if Texture = Nil then
      DG.D3DDevice.SetTexture(0, Nil)
    else
      DG.D3DDevice.SetTexture(0, Texture.Texture);
  end;

  Scene.StateRestorer.Restore;
end;


//Textures[1]を環境マップ用テクスチャとして、ごにごにょ
//envOpには、環境マップの下地に対する合成方法
procedure SXRenderEnvMapped(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; envOp:DWord);
var
  mtrx:D3DMATRIX; //テクスチャ変換行列
begin
  if Sender.Mesh = Nil then
    exit;

  Scene.StateRestorer.Memory;

  if Sender.Textures[1] = Nil then
    DG.D3DDevice.SetTexture(1, Nil)
  else
    DG.D3DDevice.SetTexture(1, Sender.Textures[1].Texture);

  //ステージ0(最初)…下地
  //テクスチャと頂点色の掛け算(普通)、だから略

  //ステージ1…反射マップ
  //テクスチャ座標には、ビュー座標系での法線ベクトルを(Nx,Ny,Nz)として、(Nx/2+0.5, -Ny/2+0.5)を入れる
  Scene.StateRestorer.SetTSS(1, D3DTSS_TEXCOORDINDEX,Ord(D3DTSS_TCI_CAMERASPACENORMAL));
  Scene.StateRestorer.SetTSS(1, D3DTSS_TEXTURETRANSFORMFLAGS, Ord(D3DTTFF_COUNT2));
  mtrx:=NowZeroMatrix;
  with mtrx do begin
    _11:=0.5;
                _22:=-0.5;

    _41:=0.5;   _42:=0.5;
  end;
  DG.D3DDevice.SetTransform(D3DTS_TEXTURE1, mtrx);

  //RGBはステージ0の結果とテクスチャ色の掛け算(普通)
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG1, DWord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG2, DWord(D3DTA_TEXTURE));
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLOROP,   envOp);

  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAARG1, DWord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAARG2, DWord(D3DTA_TEXTURE));
  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAOP,   DWord(D3DTOP_MODULATE));

  //実際のメッシュの描画はこれ一行でOK
  Sender.Mesh.Draw;

  DG.D3DDevice.SetTexture(1, Nil);

  Scene.StateRestorer.Restore;
end;


//Textures[0]を法線マップ用テクスチャとして、ごにごにょ
//lightDirは、モデル座標系での光線ベクトル
procedure SXRenderNormalMapped(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; lightDir:D3Dvector);

  function Vec2Col(v:D3DVector):DWord;
  var
    r,g,b,a:DWord;
  begin
    r := Round( 127.0 * v.x + 128.0 );
    g := Round( 127.0 * v.y + 128.0 );
    b := Round( 127.0 * v.z + 128.0 );
    a := 255;

    result:=( (a Shl 24) + (r Shl 16) + (g Shl 8) + b );
  end;
begin
  if Sender.Mesh = Nil then
    exit;

  Scene.StateRestorer.Memory;

  //ステージ0(最初)…法線マップ
  //光線ベクトルとの内積を取るに当たって、光線ベクトル反転
  Scene.StateRestorer.SetRS(D3DRS_TEXTUREFACTOR, Vec2Col(NowNeg(lightDir)));

  //法線マップと光源ベクトルの内積
  Scene.StateRestorer.SetTSS(0, D3DTSS_COLORARG1, Ord(D3DTA_TEXTURE));
  Scene.StateRestorer.SetTSS(0, D3DTSS_COLORARG2, Ord(D3DTA_TFACTOR));
  Scene.StateRestorer.SetTSS(0, D3DTSS_COLOROP,   Ord(D3DTOP_DOTPRODUCT3));
  //Scene.StateRestorer.SetTSS(0, D3DTSS_COLOROP,   Ord(D3DTOP_SELECTARG1));

  //αは法線マップから頂きでいいや
  Scene.StateRestorer.SetTSS(0, D3DTSS_ALPHAARG1, Ord(D3DTA_TEXTURE));
  Scene.StateRestorer.SetTSS(0, D3DTSS_ALPHAOP,   Ord(D3DTOP_SELECTARG1));


  //ステージ1…テクスチャ貼り
  //テクスチャ座標は0番のを使う
  Scene.StateRestorer.SetTSS(1, D3DTSS_TEXCOORDINDEX, 0);

  //RGBはステージ0の結果とテクスチャ色の掛け算(普通)
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG1, DWord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG2, DWord(D3DTA_TEXTURE));
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLOROP,   Ord(D3DTOP_MODULATE));

  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAARG1, DWord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAARG2, DWord(D3DTA_TEXTURE));
  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAOP,   DWord(D3DTOP_MODULATE));


  //すてーじ2…頂点色の加味
  Scene.StateRestorer.SetTSS(2, D3DTSS_COLORARG2, Ord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(2, D3DTSS_COLORARG1, Ord(D3DTA_DIFFUSE));
  Scene.StateRestorer.SetTSS(2, D3DTSS_COLOROP,   Ord(D3DTOP_ADDSIGNED));

  Scene.StateRestorer.SetTSS(2, D3DTSS_ALPHAARG1, Ord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(2, D3DTSS_ALPHAARG2, Ord(D3DTA_DIFFUSE));
  Scene.StateRestorer.SetTSS(2, D3DTSS_ALPHAOP,   Ord(D3DTOP_MODULATE));


  //実際のメッシュの描画はこれ一行でOK
  Sender.Mesh.Draw;

  DG.D3DDevice.SetTexture(1, Nil);

  Scene.StateRestorer.Restore;
end;


//Textures[1]をバンプマップ、Textures[2]を環境マップとしてごにょごにょ
procedure SXRenderBumpEnvMapped(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; strength:Single = 1.0; envOp:DWord = Ord(D3DTOP_ADDSMOOTH));
var
  mtrx:D3DMATRIX; //テクスチャ変換行列

  function F2DW(f:Single):DWord;
  asm
    mov eax,f;
  end;
begin
  if Sender.Mesh = Nil then
    exit;

  Scene.StateRestorer.Memory;

  if Sender.Textures[1] = Nil then
    DG.D3DDevice.SetTexture(1, Nil)
  else
    DG.D3DDevice.SetTexture(1, Sender.Textures[1].Texture);

  if Sender.Textures[2] = Nil then
    DG.D3DDevice.SetTexture(2, Nil)
  else
    DG.D3DDevice.SetTexture(2, Sender.Textures[2].Texture);

  //バンプ行列設定
  Scene.StateRestorer.SetTSS(1, D3DTSS_BUMPENVMAT00, F2DW(strength));
  Scene.StateRestorer.SetTSS(1, D3DTSS_BUMPENVMAT01, F2DW(0.0));
  Scene.StateRestorer.SetTSS(1, D3DTSS_BUMPENVMAT10, F2DW(0.0));
  Scene.StateRestorer.SetTSS(1, D3DTSS_BUMPENVMAT11, F2DW(strength));

  //ステージ0…普通のテクスチャを普通に貼る…これは通常の描画と同じだから略

  //ステージ1…バンプマップ
  Scene.StateRestorer.SetTSS(1, D3DTSS_TEXCOORDINDEX, 0);

  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG1, Ord(D3DTA_CURRENT) );
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG2, Ord(D3DTA_TEXTURE) );
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLOROP,   Ord(D3DTOP_BUMPENVMAP));

  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG1, Ord(D3DTA_CURRENT) );
  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAOP,   Ord(D3DTOP_SELECTARG1));

  //ステージ2…環境マップ
  //テクスチャ座標には、ビュー座標系での法線ベクトルを(Nx,Ny,Nz)として、(Nx/2+0.5, Ny/2+0.5)を入れる
  Scene.StateRestorer.SetTSS(2, D3DTSS_TEXCOORDINDEX,Ord(D3DTSS_TCI_CAMERASPACENORMAL));
  Scene.StateRestorer.SetTSS(2, D3DTSS_TEXTURETRANSFORMFLAGS, Ord(D3DTTFF_COUNT2));
  mtrx:=NowZeroMatrix;
  with mtrx do begin
    _11:=0.5;
                _22:=-0.5;

    _41:=0.5;   _42:=0.5;
  end;
  DG.D3DDevice.SetTransform(D3DTS_TEXTURE2, mtrx);

  Scene.StateRestorer.SetTSS(2, D3DTSS_COLORARG1, Ord(D3DTA_CURRENT) );
  Scene.StateRestorer.SetTSS(2, D3DTSS_COLORARG2, Ord(D3DTA_TEXTURE) );
  Scene.StateRestorer.SetTSS(2, D3DTSS_COLOROP,   envOp);

  Scene.StateRestorer.SetTSS(2, D3DTSS_ALPHAARG1, Ord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(2, D3DTSS_ALPHAOP,   Ord(D3DTOP_SELECTARG1));



  //実際のメッシュの描画はこれ一行でOK
  Sender.Mesh.Draw;

  DG.D3DDevice.SetTexture(1, Nil);
  DG.D3DDevice.SetTexture(2,  Nil);

  Scene.StateRestorer.Restore;
end;


//トゥーンレンダ
// Textures[1]をトゥーンレンダ用ディフューズマップとして使う
// 注意 : Lighting = Falseを指定してから行うべし、じゃないと意味ナシ
// lightDirには、ビュー座標系での光線ベクトル
procedure SXRenderToon(Sender:TSXFrame; Scene:TSXScene; DG:TDGCarad; lightDir:D3Dvector);
var
  mtrx:D3DMATRIX;
begin
  if Sender.Mesh = Nil then
    exit;

  Scene.StateRestorer.Memory;


  //ステージ0(最初)…下地の色 x テクスチャ は標準なので特に指定しない


  //ステージ1…トゥーンレンダのキモ
  if Sender.Textures[1] = Nil then
    DG.D3DDevice.SetTexture(1, Nil)
  else
    DG.D3DDevice.SetTexture(1, Sender.Textures[1].Texture);


  //テクスチャ座標に法線ベクトルを入れる
  Scene.StateRestorer.SetTSS(1, D3DTSS_TEXCOORDINDEX, Ord(D3DTSS_TCI_CAMERASPACENORMAL));
  //テクスチャ座標はUだけあればいい
  Scene.StateRestorer.SetTSS(1, D3DTSS_TEXTURETRANSFORMFLAGS, Ord(D3DTTFF_COUNT2));
  //0.0〜1.0の範囲でクランプしてもらう
  Scene.StateRestorer.SetTSS(1, D3DTSS_ADDRESSU, Ord(D3DTADDRESS_CLAMP));


  //光線ベクトルと法線ベクトルで内積(の負の値)を取って、最終的なテクスチャ座標にする
  mtrx:=NowZeroMatrix;
  with mtrx do begin
    _11:=-lightDir.x;
    _21:=-lightDir.y;
    _31:=-lightDir.z;
  end;
  DG.D3DDevice.SetTransform(D3DTS_TEXTURE1, mtrx);



  //ブレンドの仕方はMODULATE2X以外は普通どおり。
  //元のポリの色より明るい所が無いと、ベタ塗り時に比べて暗く見えるのでMODULATE2Xとする
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG1, Ord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLORARG2, Ord(D3DTA_TEXTURE));
  Scene.StateRestorer.SetTSS(1, D3DTSS_COLOROP,   Ord(D3DTOP_MODULATE2X));

  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAARG1, Ord(D3DTA_CURRENT));
  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAARG2, Ord(D3DTA_TEXTURE));
  Scene.StateRestorer.SetTSS(1, D3DTSS_ALPHAOP,   Ord(D3DTOP_MODULATE));


  //実際のメッシュの描画はこれ一行でOK
  Sender.Mesh.Draw;

  DG.D3DDevice.SetTexture(1, Nil);

  Scene.StateRestorer.Restore;
end;

end.
