unit SXlib;

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

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  dddd, DDraw, d3d,d3dcaps,d3dtypes,s_mathpack,QDArc;//,s_3DNow;



(* SXF形式について

    name   | ofs          | length                  | comment
----------------------------------------------------------------------------------------------------
 Signature |            0 |                      16 | 'Simplified_X01  '
 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

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

 *)


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

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

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

*)


const

//SXファイル識別子
SX_SIGNATURE:String[16] = 'Simplified_X00  ';   //SXFlexible
SXF_SIGNATURE:String[16] = 'Simplified_X01  ';  //古いSX

//メッシュを描画するときに一度にDirect3Dに流し込む頂点の最大数(必ず3の倍数)
DIV_VERTICES:Word = 65535;



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

//TSXVertexMT用
FVF_SXVERTEXMT:DWord = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_DIFFUSE Or D3DFVF_SPECULAR Or D3DFVF_TEX2);



type


TSXMesh = class;
TSXFrame = class;
TSXFrameList = class;
TSXScene = class;
TSXRenderingQueue = 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
TSXVertexMT = packed record
  Case Integer of
    0: (
      x,y,z:Single;     //頂点
      nx,ny,nz:Single;  //法線
      diffuse:D3DCOLOR;   //ディフューズ
      specular:D3DCOLOR;  //スペキュラ
      tu0,tv0:Single;       //テクスチャ座標
      tu1,tv1:Single;       //テクスチャ座標(2)
      );
    1:(
      pos:D3DVector;
      normal:D3DVector;
      );
end;

PSXVertexMT = ^TSXVertexMT;

TSXVertexArrayMT = Array [0..$FFFFFF] of TSXVertexMT;
PSXVertexArrayMT = ^TSXVertexArrayMT;




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


//イベントハンドラ
TSXRenderMeshEvent = procedure (Sender:TSXFrame) of object;


//ブレンドモード
TSXBlendMode = (
  sxbDefault,   //普通、透けない
  sxbAdd,       //加算合成
  sxbAlpha,     //半透明
  sxbSub        //減算合成
);

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






//クラス群


//メッシュ用クラス
TSXMesh = class
  private
    procedure SetVertexSize(const Value: DWord);
  protected
    FOwner:TDDDD;                   //色々お世話になるDDDDオブジェクト

    FVBuf:IDirect3DVertexBuffer7;   //VertexBuffer
    FIndexCount:Word;               //頂点インデクスの数
    FVertexCount:Word;              //頂点の数
    FFaceCount:Word;                //面の数、常に頂点数/3

    FVertexSize:DWord;               //１個の頂点あたりのサイズ
    FVFFlags:DWord;                 //描画させるときのフラグ

    VertexKeepBuf:Pointer;        //頂点の配列を入れる。Keep用のバッファとして使う
    FIndices:Array of Word;         //頂点インデクス

    FLocked:Boolean;                //ロック中？
    FPVertex:Pointer;               //頂点バッファへのポインタ

    FRecovery:Boolean;              //リカバリ中？


    function GetIndex(idx: Word): Word;
    function GetVertex(idx: Word): Pointer;
    procedure SetIndex(idx: Word; const Value: Word);
    procedure SetVertex(idx: Word; const Value: Pointer);
  public

    constructor Create(AOwner:TDDDD);
    destructor Destroy; override;

    procedure KeepVB;     //画面が初期化される時の事を考えて、現在のVBの状態を保持
    procedure RecoverVB;  //保持したVBを復元

    property VertexBuffer:IDirect3DVertexBuffer7 read FVBuf;  //VertexBuffer
    property IndexCount:Word read FIndexCount;                //頂点インデクスの数
    property VertexCount:Word read FVertexCount;              //頂点の数
    property FaceCount:Word read FFaceCount;                  //面の数(頂点の数 / 3

    property Vertex[idx:Word]:Pointer read GetVertex write SetVertex;   //頂点への参照
    property Index[idx:Word]:Word read GetIndex write SetIndex;         //頂点インデクス

    property FVF:DWord read FVFFlags write FVFFlags;   //FlexibleVertexFormatに関するフラグ
    property VertexSize:DWord read FVertexSize write SetVertexSize;

    procedure Draw(dwFlags:DWord);                                  //DrawIndexedPrimitiveVBで全部描画
    procedure DrawPartial(start:Word; count:Word; dwFlags:DWord);   //DrawIndexedPrimitiveVBで部分描画
    procedure LoadFromStream(s:TStream);
    procedure LoadFromFile(fileName:String);
    procedure LoadFromQDA(QDAName,QDAID:String);
    function  Lock:Pointer;
    procedure Unlock;
    procedure SetSize(newVertexCount, newIndexCount:Word);  //バッファの大きさを再設定する、中身は消える
    procedure Optimize;
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;
}


//フレーム
TSXFrame = class
  private
    FMatrix:D3DMATRIX;        //変換行列(自分の座標系→親座標系)
    FMeshMatrix:D3DMATRIX;    //メッシュ描画時の変換行列…メッシュ座標→自分座標


    FAncestors:TSXFrameList;  //祖先フレーム達
    FChildren:TSXFrameList;   //子フレーム達
    FParent:TSXFrame;         //親フレーム(Ancestors[Ancestors.Count-1]と同)
    FVisibility: TSXVisibility;   //可視属性
    FBindRenderState: Boolean;    //自分の子孫のレンダリングステートを、強制的に自分のにする
    FBindTexture: Boolean;        //自分の子孫のテクスチャを、強制的に自分のにする
    FBindMaterial: Boolean;       //自分の子孫のマテリアルを、強制的に自分のにする

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

    function GetWorldMatrix: D3DMATRIX;
    procedure SetParent(const Value: TSXFrame);

  public

    //中身
    Texture:TDDDDTexture;                   //メッシュに貼り付けるテクスチャ
    Mesh:TSXMesh;                           //書き込むメッシュ
    Material:D3DMATERIAL7;                  //マテリアル

    //レンダリングステートの制御
    Lighting:Boolean;                       //True:ライティングをする, False:頂点の色をそのまま使う
    Specular:Boolean;                       //スペキュラハイライトをつける
    BlendMode:TSXBlendMode;                 //描画時のブレンドモード

    //イベントハンドラ
    OnRenderMesh:TSXRenderMeshEvent;  //メッシュをこれから書きますですイベント

    //いろいろ
    Tag:DWord;                                              //整数値、なんにでも使って
    RenderAttr:Integer;                                     //整数値、OnRenderMeshで、マテリアル番号代わりにどうぞ

    //コンストラクタ・デストラクタ
    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 BindRenderState:Boolean read FBindRenderState write FBindRenderState;
    property BindTexture:Boolean read FBindTexture write FBindTexture;
    property BindMaterial:Boolean read FBindMaterial write FBindMaterial;

    //可視属性
    property Visibility:TSXVisibility read FVisibility write FVisibility;

    //座標変換関係
    property Matrix:D3DMATRIX read FMatrix write FMatrix;
    property WorldMatrix:D3DMATRIX read GetWorldMatrix;                  //自分座標→ワールド座標の変換行列
    property MeshMatrix:D3DMATRIX read FMeshMatrix write FMeshMatrix;    //メッシュ描画時の変換行列…メッシュ座標→自分座標

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

    function LocalToWorld(vec:D3DVector):D3DVector;    //フレーム内での座標を、ワールド座標に変換する
    function WorldToLocal(vec:D3DVector):D3DVector;    //ワールド座標内の座標をフレーム内の座標に変換する

    //位置
    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);

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

    FProjectionMatrix:D3DMATRIX;
    FVP:D3DVIEWPORT7;

    OpaqueQueue:TSXRenderingQueue;  //不透明体用のキュー
    AlphaQueue:TSXRenderingQueue;   //半透明透明体用のキュー
    AddQueue:TSXRenderingQueue;     //加算半透明体用のキュー
    SubQueue:TSXRenderingQueue;     //減算半透明体用のキュー

    //ProcessVertices用
    //MeshProcessor:TSXMesh;          //Lightingをしないとき・環境マップする時用
    //MeshProcessorMT:TSXMesh;        //同上、ただしマルチテクスチャ(2ステージ)版
  public
    constructor Create(DDCompo:TDDDD);
    destructor Destroy; override;

    property CameraFrame:TSXFrame read FCamera write FCamera;  //視点を置くフレーム

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

    procedure SetProjection(fov,aspect, nearZ,farZ:Single);                 //透視変換の設定
    procedure SetViewPort(left,top,right,bottom:DWord);                    //ビューポート

    procedure Recover;                                      //ビューポートなどの状態をリストア

    procedure PushBillboard(blendMode:TSXBlendMode; ref:TSXFrame; pos:D3DVector; points:Array of TSXVertexBB; tex:TDDDDTexture);
    function SphereVisibility(ref:TSXFrame; pos:D3DVector; radius:Single; depth:Single):Boolean; //refフレーム内にある、中心pos、半径radiusの球は見えるか、但し、距離がdepth以上なら見えないものとする
end;


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

    constructor Create(DDCompo:TDDDD; 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)
  private
    QD:TDDDD;
    FCapacity:DWord;                                         //管理するライトの数
    FLights:Array of TSXLight;
    //FSpecularPower:Single;                                   //スペキュラ強度
    function GetLights(idx: DWord): TSXLight;
    function GetUnusedLight: TSXLight;
//    procedure SetSpecularPower(const Value: Single);
  public
    constructor Create(DDCompo:TDDDD; capacity:DWord);
    destructor  Destroy; override;

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

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

    procedure Recover;      //解像度を変えた後などのリカバリ
end;



TSXMaterial = class
  private
    QD:TDDDD;
    FUpdate:Boolean;
  public
    Params:D3DMATERIAL7;
    constructor Create(DDCompo:TDDDD);

    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;     //そのメッシュを描こうとしたフレームはどれ？
  mesh:TSXMesh;       //メッシュ
  mat:D3DMatrix;      //変換行列
  tex:TDDDDTexture;   //テクスチャ
  mtrl:D3DMATERIAL7;  //マテリアル
  lighting:Boolean;   //光源計算する/しない
  OnRender:TSXRenderMeshEvent;
end;

//ビルボード描画キューに入れる情報
TSXBillboardInfo = record
  ref:TSXFrame;           //基準フレーム
  pos:D3DVector;          //基準フレーム内での位置
  tex:TDDDDTexture;       //テクスチャ
  pts:Array[0..3] of TSXVertexBB;  //頂点データ
end;

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

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

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

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

    procedure SetBillBoardCapacity(const Value: Integer);
    procedure SetMeshCapacity(const Value: Integer); //ビルボードについての情報を蓄える容量
  public
    constructor Create(AOwner:TDDDD; _Scene:TSXScene);
    destructor Destroy; override;
    procedure PushMesh(mesh:TSXMesh; frame:TSXFrame; mat:D3DMatrix; tex:TDDDDTexture; mtrl:D3DMATERIAL7; event:TSXRenderMeshEvent);  //メッシュ
    procedure PushBillboard(ref:TSXFrame; pos:D3DVector; tex:TDDDDTexture; pts:Array of TSXVertexBB); //ビルボード
    procedure Render(viewMat:D3DMATRIX; viewParam:D3DVIEWPORT7); //レンダリング
    procedure Flush;  //レンダリングせず、バッファの中を空に

    property MeshCapacity:Integer read FMeshCapacity write SetMeshCapacity;
    property BillboardCapacity:Integer read FBillboardCapacity write SetBillBoardCapacity;
    property MeshCount:Integer read FMeshCount;
    property BillboardCount:Integer read FBillboardCount;
end;



//SXVertex作成
function SXVertex(x,y,z, nx,ny,nz:Single; dif,spec:D3DCOLOR; tu,tv:Single):TSXVertex;

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

//マテリアル設定の便宜を図る関数
function MakeMaterial(difR,difG,difB, specR,specG,specB, ambR,ambG,ambB, emsR,emsG,emsB, pow:Single):D3DMATERIAL7;



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 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;







{ TSXMesh }

//コンストラクタ
constructor TSXMesh.Create(AOwner:TDDDD);
begin
  inherited Create;

  FOwner:=AOwner;
  FVBuf:=Nil;
  SetLength(FIndices,  0);
  FIndexCount:=0;
  FFaceCount:=0;
  FVertexCount:=0;
  VertexKeepBuf:=Nil;

  FVertexSize:=sizeof(TSXVertex);
  FVFFlags:=FVF_SXVERTEX;

  FLocked:=False;
  FRecovery:=False;

end;


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

  if VertexKeepBuf <> Nil then
    FreeMem(VertexKeepBuf);

  FIndices:=Nil;

  inherited;
end;


//現在のVertexBufferの状態を保持
procedure TSXMesh.KeepVB;
begin
  if FVBuf<> Nil then begin
    //バッファサイズの変更
    if VertexKeepBuf <> Nil then
      FreeMem(VertexKeepBuf);
    VertexKeepBuf:=AllocMem(VertexSize * FVertexCount);

    Self.Lock;
    Move(FPVertex^, VertexKeepBuf^, VertexSize * FVertexCount);
    Self.Unlock;
  end;
end;

//保存したVertexBufferの状態を復元
procedure TSXMesh.RecoverVB;
begin

  if FVBuf<> Nil then begin
    //リカバリ期間・開始
    FRecovery:=True;
    FVBuf:=Nil;

    SetSize(FVertexCount,FIndexCount);

    Self.Lock;
    Move(VertexKeepBuf^, FPVertex^, VertexSize * FVertexCount);
    Self.Unlock;

    FreeMem(VertexKeepBuf);
    VertexKeepBuf:=Nil;
    FRecovery:=False;
    //リカバリ期間・終了
  end;
end;


//DPで描画
procedure TSXMesh.Draw(dwFlags:DWord);
var
  ofs:Word;
  nIs:Word;
begin

  ofs:=0;
  while ofs < FIndexCount do begin

    nIs := FIndexCount - ofs;
    if nIs > DIV_VERTICES then
      nIs:=DIV_VERTICES;

    FOwner.D3DDevice.DrawIndexedPrimitiveVB(
      D3DPT_TRIANGLELIST, FVBuf, 0, FVertexCount, @(FIndices[ofs]), nIs, dwFlags);

    Inc(ofs, nIs);
  end;

end;

//DPで描画
procedure TSXMesh.DrawPartial(start:Word; count:Word; dwFlags:DWord);
begin
  FOwner.D3DDevice.DrawIndexedPrimitiveVB(
    D3DPT_TRIANGLELIST, FVBuf, 0, FVertexCount, @(FIndices[start]), count, dwFlags);
end;


//インデクス操作
function TSXMesh.GetIndex(idx: Word): Word;
begin
  if idx >= FIndexCount then begin
    DDDD_PutDebugMessage('SXLib: 範囲外の頂点インデクスを参照しようとしました');
    result:=0;
    exit;
  end;

  result:=FIndices[idx];
end;

procedure TSXMesh.SetIndex(idx: Word; const Value: Word);
begin
  if idx >= FIndexCount then begin
    DDDD_PutDebugMessage('SXLib: 範囲外の頂点インデクスを設定しようとしました');
    exit;
  end;

  FIndices[idx]:=Value;
end;


//頂点操作
function TSXMesh.GetVertex(idx: Word): Pointer;
var
  pV:Pointer;
  ofs:DWord;
begin
  //範囲チェック
  if idx >= FVertexCount then begin
    DDDD_PutDebugMessage('SXLib: 範囲外の頂点を参照しようとしました');
    result:=Nil;
    exit;
  end;

  //ロックされていないなら、ロック。ロックされてるなら、前に得たポインタをゲット
  if not FLocked then begin
    if FVBuf.Lock(DDLOCK_READONLY Or DDLOCK_WAIT, Pointer(pV), Nil) <> DD_OK then begin
      DDDD_PutDebugMessage('SXLib: VertexBufferがロックできません');
      Halt;
    end;
  end else
    pV:=FPVertex;

  //返り値に格納
  ofs:=idx*VertexSize;
  asm
    mov   eax,ofs;
    add   pV,eax;
  end;
  result:=pV;

  //アンロック
  if not FLocked then begin
    if FVBuf.Unlock <> DD_OK then begin
      DDDD_PutDebugMessage('SXLib: VertexBufferがアンロックできません');
      Halt;
    end;
  end;

end;

procedure TSXMesh.SetVertex(idx: Word; const Value: Pointer);
var
  ofs:DWord;
  pV:^TSXVertex;
begin
  //範囲チェック
  if idx >= FVertexCount then begin
    DDDD_PutDebugMessage('SXLib: 範囲外の頂点を変更しようとしました');
    exit;
  end;

  //ロックされていないなら、ロック。ロックされてるなら、前に得たポインタをゲット
  if not FLocked then begin
    if FVBuf.Lock(DDLOCK_WRITEONLY Or DDLOCK_WAIT, Pointer(pV), Nil) <> DD_OK then begin
      DDDD_PutDebugMessage('SXLib: VertexBufferがロックできません');
      Halt;
    end;
  end else
    pV:=FPVertex;

  //格納するっす
  ofs:=idx*VertexSize;
  asm
    mov   eax,ofs;
    add   pV,eax;
  end;
  Move(Value^,pV^,VertexSize);

  //アンロック
  if not FLocked then begin
    if FVBuf.Unlock <> DD_OK then begin
      DDDD_PutDebugMessage('SXLib: VertexBufferがアンロックできません');
      Halt;
    end;
  end;

end;

//1Vertexあたりのサイズを変更
procedure TSXMesh.SetVertexSize(const Value: DWord);
begin
  FVertexSize := Value;
  SetSize(FVertexCount, FIndexCount);
end;



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

  StartPos:=s.Position;

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

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

  SetSize(nVertices, nIndices);

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

  try
    //頂点のロード
    Lock;
    s.ReadBuffer(FPVertex^, VertexSize * nVertices);
    Unlock;

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

end;

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

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

  LoadFromStream(fs);
  fs.Free;
end;

//QDAから読み込み
procedure TSXMesh.LoadFromQDA(QDAName,QDAID:String);
var
  ms:TMemoryStream;
begin
  ms:=Nil;

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

  LoadFromStream(ms);
  ms.Free;
end;



//VertexBufferのロック
function TSXMesh.Lock:Pointer;
begin
  if FVBuf.Lock(DDLOCK_WAIT, Pointer(FPVertex), Nil) <> DD_OK then begin
    DDDD_PutDebugMessage('SXLib: VertexBufferがロックできません');
    Halt;
  end;

  result:=FPVertex;
  FLocked:=True;
end;

//VertexBufferのアンロック
procedure TSXMesh.Unlock;
begin
  if FVBuf.Unlock <> DD_OK then begin
    DDDD_PutDebugMessage('SXLib: VertexBufferがアンロックできません');
    Halt;
  end;

  FLocked:=False;
end;

//頂点配列とインデクス配列のサイズを再設定
procedure TSXMesh.SetSize(newVertexCount, newIndexCount: Word);
var
  vbdesc:D3DVERTEXBUFFERDESC;
begin
  FVertexCount:=newVertexCount;
  FIndexCount:=newIndexCount;
  FFaceCount:=newIndexCount div 3;

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

  //VertexBufferの生成
  ZeroMemory(@vbdesc,Sizeof(vbdesc));
  with vbdesc do begin
    dwSize:=Sizeof(vbdesc);

    if FOwner.D3DDeviceType <> dtTnLHAL then
      dwCaps:=D3DVBCAPS_SYSTEMMEMORY
    else
      dwCaps:=0;

    dwFVF:=FVFFlags;
    dwNumVertices:=newVertexCount;
  end;
  FOwner.D3D.CreateVertexBuffer(@vbdesc, FVBuf, 0);

  //頂点インデクス配列の設定
  if not FRecovery then
    SetLength(FIndices, newIndexCount);

end;



//最適かするっ。ロックとかはできなくなるよ
procedure TSXMesh.Optimize;
begin
  FVBuf.Optimize(FOwner.D3DDevice,0);
end;




{ TSXFrame }

constructor TSXFrame.Create(parentFrame:TSXFrame);
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;

  FBindMaterial:=False;
  FBindRenderState:=False;
  FBindTexture:=False;


  //変数とかの初期化
  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);

  Visibility:=sxvShow;
  Lighting:=True;
  Specular:=True;


  OnRenderMesh:=Nil;
  Mesh:=Nil;
  Texture:=Nil;

end;

destructor TSXFrame.Destroy;
begin
  inherited;

  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));

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
    mat:=NowCompositeMatrix(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軸にする
  NowLength(normX, 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;


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:=NowHeteroginize(
    NowTransform(NowHomoginize(vec), WorldMatrix));
end;

//ワールド座標→フレーム内の座標
function TSXFrame.WorldToLocal(vec: D3DVector): D3DVector;
begin
  result:=NowHeteroginize(
    NowTransform(NowHomoginize(vec), NowInvMatrix(WorldMatrix)));
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;



{ 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;






{ TSXScene }

constructor TSXScene.Create(DDCompo:TDDDD);
begin
  inherited Create;

  AlphaQueue:=TSXRenderingQueue.Create(DDCompo, Self);
  OpaqueQueue:=TSXRenderingQueue.Create(DDCompo, Self);
  AddQueue:=TSXRenderingQueue.Create(DDCompo, Self);
  SubQueue:=TSXRenderingQueue.Create(DDCompo, Self);

  {
  MeshProcessor:=TSXMesh.Create(DDCompo);
  MeshProcessor.VertexSize:=Sizeof(D3DTLVERTEX);
  MeshProcessor.FVF:=D3DFVF_TLVERTEX;
  MeshProcessor.SetSize(65535,65535); //(^^;)

  MeshProcessorMT:=TSXMesh.Create(DDCompo);
  MeshProcessorMT.VertexSize:=Sizeof(TSXTLVertexMT);
  MeshProcessorMT.FVF:=FVF_SXTLVertexMT;
  MeshProcessorMT.SetSize(65535,65535);
  }

  FCamera:=Nil;
  QD:=DDCompo;
end;

destructor TSXScene.Destroy;
begin

  AlphaQueue.Free;
  OpaqueQueue.Free;
  AddQueue.Free;
  SubQueue.Free;

  //MeshProcessor.Free;
  //MeshProcessorMT.Free;

  inherited;
end;


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

  procedure QueingFrame(frame:TSXFrame; const mat:D3DMATRIX; RSBind,TexBind,MatBind:Boolean);
  var
    i:Integer;
    meshM:D3DMATRIX;
  begin

    //子フレーム無し、かつ、描画するメッシュなしなら、何もしない
    //Visibilityが「子供もひっくるめて隠す」でも何もしない
    //ただし、ビルボードがくっついてる時は、マトリクスの合成だけ行う
    if (not frame.BBAttached) then
      if ((frame.Children.Count = 0) And (frame.Mesh = Nil)) Or (frame.Visibility = sxvHide) then
        exit;

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


    //メッシュの描画
    if frame.Mesh <> Nil then begin

      meshM:=NowCompositeMatrix(frame.MeshMatrix,frame.RenderedMatrix);

      //テクスチャがあるなら、貼り付ける。「親のテクスチャ使用」を強制されてないなら
      if not TexBind then
        curTexture:=frame.Texture;

      //マテリアルを設定する
      if not MatBind then
        curMaterial:=frame.Material;

      //レンダリングステートの設定
      if not RSBind then
        curBlendMode:=frame.BlendMode;


      //キューに放り込む
      if frame.Visibility = sxvShow then begin
        Case curBlendMode of
          sxbAdd:AddQueue.PushMesh(frame.Mesh, frame, meshM, curTexture, curMaterial, frame.OnRenderMesh);
          sxbAlpha:AlphaQueue.PushMesh(frame.Mesh, frame, meshM, curTexture, curMaterial, frame.OnRenderMesh);
          sxbDefault:OpaqueQueue.PushMesh(frame.Mesh, frame, meshM, curTexture, curMaterial, frame.OnRenderMesh);
          sxbSub:SubQueue.PushMesh(frame.Mesh, frame, meshM, curTexture, curMaterial, frame.OnRenderMesh);
        end;
      end;

    end;

    //子供の描画
    For i:=0 to frame.Children.Count-1 do begin
      QueingFrame(frame.Children[i], frame.RenderedMatrix, RSBind Or frame.BindRenderState, TexBind Or frame.BindTexture, MatBind Or frame.BindMaterial);
    end;

  end;

begin

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


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

  QD.D3DDevice.SetTransform(D3DTRANSFORMSTATE_VIEW, @viewMat);


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


  { キューに入ってる物体を描く }

  /////不透明体

  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_ZENABLE, DWord(True));  //Z比較あり
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, DWord(True));  //Z書き込みあり

  //ブレンドなし
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_BLENDENABLE, DWord(False));

  OpaqueQueue.Render(viewMat, FVP);


  /////加算合成体
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, DWord(False));  //Z書き込みなし

  //ブレンドの設定
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_BLENDENABLE, DWord(True));

  QD.D3DDevice.SetTextureStageState(0, D3DTSS_COLORARG1, DWord(D3DTA_TEXTURE));
  QD.D3DDevice.SetTextureStageState(0, D3DTSS_COLORARG2, DWord(D3DTA_DIFFUSE));
  QD.D3DDevice.SetTextureStageState(0, D3DTSS_COLOROP,   DWord(D3DTOP_MODULATE));

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

  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_SRCBLEND, DWord(D3DBLEND_ONE));
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_DESTBLEND, DWord(D3DBLEND_ONE));

  AddQueue.Render(viewMat, FVP);


  //半透明体
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_SRCBLEND, DWord(D3DBLEND_SRCALPHA));
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_DESTBLEND, DWord(D3DBLEND_INVSRCALPHA));

  AlphaQueue.Render(viewMat, FVP);


  //減算合成体…減算はニセ減算だけど(^^;)
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_SRCBLEND, DWord(D3DBLEND_ZERO));
  QD.D3DDevice.SetRenderState(D3DRENDERSTATE_DESTBLEND, DWord(D3DBLEND_INVSRCCOLOR));

  SubQueue.Render(viewMat, FVP);


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

procedure TSXScene.Clear(dwFlags:DWord; color:DWORD; z:Single; stencil:DWord);
var
  r:D3DRect;
begin
  with r do begin
    x1:=FVP.dwX;
    y1:=FVP.dwY;
    x2:=FVP.dwX+FVP.dwWidth;
    y2:=FVP.dwY+FVP.dwHeight;
  end;

  QD.D3DDevice.Clear(1, @r, dwFlags,color,z,stencil);
end;

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

  q:=farZ / (farZ-nearZ);
  w:=aspect * Cos(fov*0.5)/Sin(fov*0.5);
  h:=Cos(fov*0.5)/Sin(fov*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;

  QD.D3DDevice.SetTransform( D3DTRANSFORMSTATE_PROJECTION, @FProjectionMatrix );
end;



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

  with FVP do begin
    dwX:=left;
    dwY:=top;
    dwWidth:=right - left;
    dwHeight:=bottom - top;
    dvMinZ:=0;
    dvMaxZ:=1.0;
  end;

  QD.D3DDevice.SetViewport(@FVP);
end;

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


//ビルボードを押し込む
procedure TSXScene.PushBillboard(blendMode:TSXBlendMode; ref: TSXFrame;
 pos: D3DVector; points: array of TSXVertexBB; tex:TDDDDTexture);
begin
  Case blendMode of
    sxbAdd:AddQueue.PushBillboard(ref, pos, tex, points);
    sxbAlpha:AlphaQueue.PushBillboard(ref, pos, tex, points);
    sxbDefault:OpaqueQueue.PushBillboard(ref, pos, tex, points);
    sxbSub:SubQueue.PushBillboard(ref, pos, tex, points);
  end;
  //フラグを立てる
  ref.BBAttached:=True;

end;

//球体の可視判定
function TSXScene.SphereVisibility(ref: TSXFrame; pos: D3DVector;
  radius: Single; depth:Single): Boolean;
var
  viewM:D3DMATRIX;  //ref.posを視野座標系に変換する行列
  projM:D3DMATRIX;  //透視変換行列(画角ゲット用)

  p:D3DVector;
  fovH,fovV:Single; //画角
  cosFovH,sinFovH,cosFovV,sinFovV:Single;
begin
  //まず、視野座標系に変換する
  viewM:=NowCompositeMatrix(ref.WorldMatrix, NowInvMatrix(CameraFrame.WorldMatrix));
  p:=NowHeteroginize(NowTransform(NowHomoginize(pos), viewM));

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

  //縦・横の画角を求める
  QD.D3DDevice.GetTransform(D3DTRANSFORMSTATE_PROJECTION, @projM);
  fovH:=ArcTan(1.0/projM._11);
  fovV:=ArcTan(1.0/projM._22);

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

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

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

  result:=True;
end;



{ TSXLight }

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

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

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

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

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

destructor TSXLight.Destroy;
begin
  inherited;
end;

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

  QD.D3DDevice.SetLight(FIndex, @Params);
  QD.D3DDevice.LightEnable(FIndex, FEnabled);

end;


//お便利るーちんず
procedure TSXLight.SetupDiffuse(_R, _G, _B: Single);
begin
  with Params.dcvDiffuse do begin
    dvR:=_R;
    dvG:=_G;
    dvB:=_B;
  end;

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

procedure TSXLight.SetupSpecular(_R, _G, _B: Single);
begin
  with Params.dcvSpecular do begin
    dvR:=_R;
    dvG:=_G;
    dvB:=_B;
  end;

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

procedure TSXLight.SetupAmbient(_R, _G, _B: Single);
begin
  with Params.dcvAmbient do begin
    dvR:=_R;
    dvG:=_G;
    dvB:=_B;
  end;

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

procedure TSXLight.SetupColors(difR, difG, difB, specR, specG, specB, ambR,
  ambG, ambB: Single);
begin
  with Params.dcvDiffuse do begin
    dvR:=difR;
    dvG:=difG;
    dvB:=difB;
  end;
  with Params.dcvSpecular do begin
    dvR:=specR;
    dvG:=specG;
    dvB:=specB;
  end;
  with Params.dcvAmbient do begin
    dvR:=ambR;
    dvG:=ambG;
    dvB:=ambB;
  end;

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

procedure TSXLight.SetupRanges(range, att0, att1, att2: Single);
begin
  with Params do begin
    dvRange:=range;
    dvAttenuation0:=att0;
    dvAttenuation1:=att1;
    dvAttenuation2:=att2;
  end;

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

procedure TSXLight.SetupDirectional(dir: D3DVector);
begin
  Params.dltType:=D3DLIGHT_DIRECTIONAL;
  Params.dvDirection:=dir;

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

procedure TSXLight.SetupPoint(pos: D3DVector);
begin
  Params.dltType:=D3DLIGHT_POINT;
  Params.dvPosition:=pos;

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


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

  Params.dvTheta:=theta;
  Params.dvPhi:=phi;
  Params.dvFalloff:=falloff;

  if FUpdate then
    QD.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.dvDirection:=Vector(mat._31, mat._32, mat._33);
  Params.dvPosition:=Vector(mat._41, mat._42, mat._43);

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


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

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


{ TSXLightGroup }

constructor TSXLightGroup.Create(DDCompo:TDDDD; capacity:DWord);
var
  i:Integer;
begin
  inherited Create;

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

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

destructor TSXLightGroup.Destroy;
var
  i:Integer;
begin

  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
    DDDD_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;

  QD.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(DDCompo: TDDDD);
begin
  ZeroMemory(@Params, Sizeof(Params));
  QD:=DDCompo;
  FUpdate:=True;
end;

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

  if FUpdate then
    QD.D3DDevice.SetMaterial(@Params);
end;

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

  if FUpdate then
    QD.D3DDevice.SetMaterial(@Params);
end;

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

  if FUpdate then
    QD.D3DDevice.SetMaterial(@Params);
end;

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

  if FUpdate then
    QD.D3DDevice.SetMaterial(@Params);
end;

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

  if FUpdate then
    QD.D3DDevice.SetMaterial(@Params);
end;


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

  with Params.dcvDiffuse do begin
    dvR:=difR;
    dvG:=difG;
    dvB:=difB;
  end;
  with Params.dcvSpecular do begin
    dvR:=specR;
    dvG:=specG;
    dvB:=specB;
  end;
  with Params.dcvAmbient do begin
    dvR:=ambR;
    dvG:=ambG;
    dvB:=ambB;
  end;
  with Params.dcvEmissive do begin
    dvR:=emsR;
    dvG:=emsG;
    dvB:=emsB;
  end;

  Params.dvPower:=pow;

  if FUpdate then
    QD.D3DDevice.SetMaterial(@Params);

end;


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

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





{ ヘルパー関数 }

function MakeMaterial(difR,difG,difB, specR,specG,specB, ambR,ambG,ambB, emsR,emsG,emsB, pow:Single):D3DMATERIAL7;
begin
  with Result.dcvDiffuse do begin
    dvR:=difR;
    dvG:=difG;
    dvB:=difB;
    dvA:=1.0;
  end;
  with Result.dcvSpecular do begin
    dvR:=specR;
    dvG:=specG;
    dvB:=specB;
    dvA:=1.0;
  end;
  with Result.dcvAmbient do begin
    dvR:=ambR;
    dvG:=ambG;
    dvB:=ambB;
    dvA:=1.0;
  end;
  with Result.dcvEmissive do begin
    dvR:=emsR;
    dvG:=emsG;
    dvB:=emsB;
    dvA:=1.0;
  end;

  Result.dvPower:=pow;
end;

{ TSXRenderingQueue }

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

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

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

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

  inherited;
end;

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

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


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

  Inc(FBillboardCount);
end;

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

  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);
end;

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

//描画
procedure TSXRenderingQueue.Render(viewMat:D3DMATRIX; viewParam:D3DVIEWPORT7);
var
  i,j:Integer;
  prevTex:TDDDDTexture; //前回使ったテクスチャへの参照
  prevSpec:Boolean;     //前回描画したものは、スペキュラを計算したか
  prevLit:Boolean;      //前回描画したものは、ライティングの計算をしたか
  prevFrame:TSXFrame;   //直前に描画したビルボードの入ってたフレーム

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

  //メッシュの描画

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

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

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

  For i:=0 to MeshCount-1 do begin

    //ライティング
    if prevLit <> MeshQueue[i].Frame.Lighting then begin
      FOwner.D3DDevice.SetRenderState(D3DRENDERSTATE_LIGHTING, DWord(MeshQueue[i].Frame.Lighting));
      prevLit:=MeshQueue[i].Frame.Lighting;
    end;

    //スペキュラつける？
    if prevSpec <> MeshQueue[i].Frame.Specular then begin
      FOwner.D3DDevice.SetRenderState(D3DRENDERSTATE_SPECULARENABLE, DWord(MeshQueue[i].Frame.Specular));
      prevSpec:=MeshQueue[i].Frame.Specular;
    end;

    //行列の設定
    FOwner.D3DDevice.SetTransform(D3DTRANSFORMSTATE_WORLD,@(MeshQueue[i].mat));

    //マテリアルの設定
    FOwner.D3DDevice.SetMaterial(@(MeshQueue[i].Mtrl));

    //テクスチャがあるなら、貼り付ける。
    if MeshQueue[i].Tex <> prevTex then begin
      if MeshQueue[i].Tex <> Nil then
        FOwner.D3DDevice.SetTexture(0,MeshQueue[i].Tex.Surface)
      else
        FOwner.D3DDevice.SetTexture(0,Nil);
    end;

    prevTex:=MeshQueue[i].Tex;

    //イベントハンドラを呼ぶ
    if Assigned(MeshQueue[i].OnRender) then
       MeshQueue[i].OnRender(MeshQueue[i].Frame);


    //描画
    MeshQueue[i].Mesh.Draw(0);

  end;



  //ビルボードの描画

  //ライティングを切る
  FOwner.D3DDevice.SetRenderState(D3DRENDERSTATE_LIGHTING, DWord(False));

  FOwner.D3DDevice.GetTransform(D3DTRANSFORMSTATE_PROJECTION, @projM);
  ScreenM:=NowIdentityMatrix;
  with ScreenM do begin
    _11:=viewParam.dwWidth/2.0;
    _22:=-(viewParam.dwHeight/2.0);
    _33:=viewParam.dvMaxZ-viewParam.dvMinZ;
    _41:=viewParam.dwX + viewParam.dwWidth/2.0;
    _42:=viewParam.dwY + viewParam.dwHeight/2.0;
    _43:=viewParam.dvMinZ;
  end;


  //視野座標→画面 の変換行列
  viewProjScreenM:=NowCompositeMatrix(viewMat,projM);
  viewProjScreenM:=NowCompositeMatrix(viewProjScreenM,ScreenM);
  prevFrame:=Nil;

  For i:=0 to BillboardCount-1 do begin

    if BillboardQueue[i].ref <> Nil then begin
      //テクスチャがあるなら、貼り付ける。
      if BillboardQueue[i].Tex <> prevTex then begin
        if BillboardQueue[i].Tex <> Nil then
          FOwner.D3DDevice.SetTexture(0,BillboardQueue[i].Tex.Surface)
        else
          FOwner.D3DDevice.SetTexture(0,Nil);
      end;
      prevTex:=BillboardQueue[i].Tex;


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

      center:=NowHomoginize(BillboardQueue[i].pos);
      center:=NowTransform(center,m);

      //W値が正なら描画


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

        For j:=0 to 3 do begin
          with BillboardQueue[i].Pts[j] do begin
              Pts[j].sx:=center.x + BillboardQueue[i].Pts[j].dx * center.W;
              Pts[j].sy:=center.y + BillboardQueue[i].Pts[j].dy * center.W;
              Pts[j].sz:=center.z;
              Pts[j].rhw:=center.w;
              Pts[j].color:=color;
              Pts[j].specular:=$FF000000;
              Pts[j].tu:=tu;
              Pts[j].tv:=tv;
          end;
        end;

        FOwner.D3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_TLVERTEX, @Pts, 4, 0);
      end;


      //参照フレームに付いたフラグを消す
      BillboardQueue[i].ref.BBAttached:=False;      
    end;
  end;



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




end.
