//DJXMeshes v1.5
unit DJXMeshes;

interface

uses
  Windows,
  Messages,
  SysUtils,
  Classes,
  Graphics,
  MMSystem,
  Math,
  DJX,
  DJXRender,
  DJXClasses,
  DJXTextures,
  DJXMaterials,
  DJXUtils,
  DJXShaders,
  Direct3D9,
  D3DX9;

Type
  TDJXMesh = class;
  TDJXMeshStatic = class;
  TDJXMeshSkinned = class;
  TDJXMeshEx = class;
  TDJXMeshHierarchy = class;
  PDJXFrameDerived = ^TDJXFrameDerived;
  PDJXMeshContainerDerived = ^TDJXMeshContainerDerived;
  TDJXShadowVolume = class;

  TDJXMeshList = class(TDJXComponent)
  protected
    m_Meshes: Tlist;
    function getItem(Index: integer): TDJXMesh;
    procedure setItem(Index: integer; Value: TDJXMesh);
    function getCount: integer;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property DanJetX: Pointer read m_DanJetX write m_DanJetX;
    property Meshes[Index: integer]: TDJXMesh read getItem write setItem;
    property Count: integer read getCount;
    function Find(name: string): TDJXMesh;
    function FindIndex(name: string): integer;
    function Add(Mesh: TDJXMesh): TDJXMesh;
    function Remove(Mesh: TDJXMesh): TDJXMesh;
    function RemoveName(Name: string): TDJXMesh;
    function Delete(Index: integer): TDJXMesh;
    procedure FreeAndRemove(Mesh: TDJXMesh);
    procedure FreeAndRemoveName(Name: string);
    procedure FreeAndDelete(Index: integer);
    procedure Clear;
    procedure FreeMeshes;
    function CreateMeshStatic(MeshName: string): TDJXMeshStatic;
    function CreateMeshSkinned(MeshName: string): TDJXMeshSkinned;
    function CreateMeshEx(MeshName: string): TDJXMeshEx;
    function CreateMeshFromFile(f: string; MeshName: string): TDJXMeshStatic;
    function CreateMeshFromStream(Stream: TStream; MeshName: string): TDJXMeshStatic;
    function CreateMeshFromRes(ResName, Folder, Res: string; MeshName: string): TDJXMeshStatic;
    function CreateMeshFromFileAnim(f: string; MeshName: string): TDJXMeshSkinned;
    function CreateMeshFromStreamAnim(Stream: TStream; MeshName: string): TDJXMeshSkinned;
    function CreateMeshFromResAnim(ResName, Folder, Res: string; MeshName: string): TDJXMeshSkinned;
    function CreateMeshFromFileEx(f: string; MeshName: string): TDJXMeshEx;
    function CreateMeshFromStreamEx(Stream: TStream; MeshName: string): TDJXMeshEx;
    function CreateMeshFromResEx(ResName, Folder, Res: string; MeshName: string): TDJXMeshEx;
    function CreateMeshBox(MeshName: string; Width, Height, Depth: single): TDJXMeshStatic;
    function CreateMeshCylinder(
      MeshName: string;
      Radius1, Radius2, Length: Single;
      Slices, Stacks: LongWord
    ): TDJXMeshStatic;
    function CreateMeshPolygon(MeshName: string; Length: Single; Sides: LongWord): TDJXMeshStatic;
    function CreateMeshSphere(MeshName: string; Radius: Single; Slices, Stacks: LongWord): TDJXMeshStatic;
    function CreateMeshTorus(
      MeshName: string;
      InnerRadius, OuterRadius: Single;
      Sides, Rings: LongWord
    ): TDJXMeshStatic;
    function CreateMeshTeapot(MeshName: string): TDJXMeshStatic;
    function CreateMeshText(
      MeshName: string;
      Text: string;
      FontName: string;
      Bold, Italic: Boolean;
      Extrusion: Single
    ): TDJXMeshStatic;
    procedure LoadFromFile(f: string);
    procedure Init(DanJetX: Pointer); override;
    procedure OnLostDevice; override;
    procedure OnResetDevice; override;
  end;

  TDJXMesh = class
  private
    m_DanJetX: Pointer;
    m_Name: string;
    m_MinVec: TD3DXVector3;
    m_MaxVec: TD3DXVector3;
    m_SizeX: single;
    m_SizeY: single;
    m_SizeZ: single;
    m_CurFrame: single;
  protected
    procedure CalculateSize;
    procedure SetFrame(Value: single); virtual;
  public
    property Name: string read m_Name write m_Name;
    property SizeX: single read m_SizeX;
    property SizeY: single read m_SizeY;
    property SizeZ: single read m_SizeZ;
    property MinBounds: TD3DXVector3 read m_MinVec;
    property MaxBounds: TD3DXVector3 read m_MaxVec;
    property Frame: single read m_CurFrame write SetFrame;
    constructor Create(Name: string; DanJetX: Pointer); virtual;
    destructor Destroy; override;
    procedure LoadFromFile(FileName: string); virtual;
    procedure LoadFromStream(Stream: TStream); virtual;
    procedure LoadFromRes(
      const ResName: string;
      const Folder: string;
      const Res: string
    );
    procedure Render(
      AnimPos: single;
      ApplyTexture: boolean = true;
      ApplyMaterial: boolean = true
    ); overload; virtual;
    procedure Render; overload; virtual;
    procedure Release; virtual;
    procedure GetBounds(out minVec, maxVec: TD3DXVector3); virtual;
    procedure GetBoundsAA(out minVec, maxVec: TD3DXVector3);
    procedure SetTexture(Texture: TDJXTexture); virtual;
    procedure SetMaterial(Material: TDJXMaterial); virtual;
    procedure SetMaterialAlpha(Alpha: byte); virtual;
    function IsInFrustum(Frustum: TDJXFrustum = nil): boolean;
    function IsPicked(var dist: single; var HitPos: TD3DXVector3): boolean; virtual;
  end;

  PDJXMXHeader = ^TDJXMXHeader;
  TDJXMXHeader = packed record
    VertexCount: DWORD;
    IndexCount: DWORD;
    BoneCount: DWORD;
    FrameCount: DWORD;
  end;

  PDJXMXVertex = ^TDJXMXVertex;
  TDJXMXVertex = packed record
    BoneCount: DWORD;
    Pos: TD3DXVector3;
    Normal: TD3DXVector3;
    TexCoords: TD3DXVector3;
    Bones: array of DWORD;
    Weights: array of single;
  end;

  PDJXMXBone = ^TDJXMXBone;
  TDJXMXBone = packed record
    ChildrenCount: DWORD;
    Name: string;
    Position: TD3DXVector3;
    PositionEnd: TD3DXVector3;
    Parent: DWORD;
    Children: array of DWORD;
    Matrix: TD3DXMatrix;
    RenderMatrix: TD3DXMatrix;
  end;

  PDJXMXFrameAnimation = ^TDJXMXFrameAnimation;
  TDJXMXFrameAnimation = record
    Name: string;
    BonePositions: array of TD3DXVector3;
    Matrices: array of array of TD3DXMatrix;
  end;

  TDJXMXRenderMode = (
    rmBasic,
    rmNoPSLight,
    rmNoPSLightSmooth,
    rmLight,
    rmLightSpec,
    rmBump,
    rmBumpSpec,
    rmBumpSpecRef,
    rmParalaxBump,
    rmParalaxBumpSpec,
    rmParalaxBumpSpecRef,
    rmToon,
    rmToonLight,
    rmToonLightSpec,
    rmToonBump,
    rmToonBumpSpec
  );

  TDJXMeshEx = class (TDJXMesh)
  private
    m_Skinned: boolean;
    m_Mesh: ID3DXMesh;
    m_Shadow: ID3DXMesh;
    m_ShadowRender: ID3DXMesh;
    m_Header: TDJXMXHeader;
    m_Vertices: array of TDJXMXVertex;
    m_Indices: array of DWORD;
    m_Bones: array of TDJXMXBone;
    m_Animations: array of TDJXMXFrameAnimation;
    m_CurAnim: DWORD;
    m_LightPos: TD3DXVector3;
    m_LightFalloff: single;
    m_LightColor: TDJXColor;
    m_SpecularPower: single;
    m_ParalaxBias: single;
    m_Texture: TDJXTexture;
    m_Normals: TDJXTexture;
    m_Mask: TDJXTexture;
    m_Reflection: IDirect3DCubeTexture9;
    m_Toon: TDJXTexture;
    m_RenderMode: TDJXMXRenderMode;
  protected
    procedure ResetMesh(FCount, VCount: DWORD);
    procedure ResetShadow(FCount, VCount: DWORD);
    procedure LoadMeshBuffers;
    procedure LoadShadowBuffers;
    procedure ComputeRenderMatrix(Index: integer);
    procedure RefineAnimation(Index: integer);
    procedure SetSkinned(Value: boolean);
    procedure SetMatrix(Index: integer; Value: TD3DXMatrix);
    function GetMatrix(Index: integer): TD3DXMatrix;
    function GetRenderMatrix(Index: integer): TD3DXMatrix;
    function GetBone(Index: integer): PDJXMXBone;
    function GetHeader: PDJXMXHeader;
    function GetFrameAnimation(Index: integer): PDJXMXFrameAnimation;
    function GetAnimationCount: integer;
    function GetVertex(Index: integer): TDJXMXVertex;
    function GetIndex(Index: integer): DWORD;
    procedure SetAnimation(Value: DWORD);
    procedure SetFrame(Value: single); override;
    procedure SetMeshShaderConstants;
    procedure SetShadowShaderConstants;
  public
    property Header: PDJXMXHeader read GetHeader;
    property Vertices[Index: integer]: TDJXMXVertex read GetVertex;
    property Indices[Index: integer]: DWORD read GetIndex;
    property Mesh: ID3DXMesh read m_Mesh write m_Mesh;
    property Skinned: boolean read m_Skinned write SetSkinned;
    property Matrix[Index: integer]: TD3DXMatrix read GetMatrix write SetMatrix;
    property RenderMatrix[Index: integer]: TD3DXMatrix read GetRenderMatrix;
    property Bones[Index: integer]: PDJXMXBone read GetBone;
    property Animations[Index: integer]: PDJXMXFrameAnimation read GetFrameAnimation;
    property AnimationCount: integer read GetAnimationCount;
    property Frame: single read m_CurFrame write SetFrame;
    property Animation: DWORD read m_CurAnim write SetAnimation;
    property LightPos: TD3DXVector3 read m_LightPos write m_LightPos;
    property LightFalloff: single read m_LightFalloff write m_LightFalloff;
    property LightColor: TDJXColor read m_LightColor write m_LightColor;
    property SpecularPower: single read m_SpecularPower write m_SpecularPower;
    property ParalaxBias: single read m_ParalaxBias write m_ParalaxBias;
    property Texture: TDJXTexture read m_Texture write m_Texture;
    property Normals: TDJXTexture read m_Normals write m_Normals;
    property Mask: TDJXTexture read m_Mask write m_Mask;
    property Reflection: IDirect3DCubeTexture9 read m_Reflection write m_Reflection;
    property Toon: TDJXTexture read m_Toon write m_Toon;
    property RenderMode: TDJXMXRenderMode read m_RenderMode write m_RenderMode;
    constructor Create(Name: string; DanJetX: Pointer);
    destructor Destroy; override;
    procedure LoadFromFile(f: string); override;
    procedure LoadFromStream(Stream: TStream); override;
    function AddAnimation(
      Name: string;
      FrameCount: DWORD;
      BoneCount: DWORD
    ): DWORD;
    function AddAnimationFromFile(
      f: string; Name: string;
      AssignByBoneNames: boolean = true;
      Transform: PD3DXMatrix = nil
    ): DWORD;
    function AddAnimationFromStream(
        s: TStream; Name: string;
        AssignByBoneNames: boolean = true;
        Transform: PD3DXMatrix = nil
    ): DWORD;
    procedure RemoveAnimation(Index: integer);
    procedure RemoveAnimationName(Name: string);
    procedure SetAnimationName(Name: string);
    function GetAnimationIndex(Name: string): DWORD;
    procedure Render(
      AnimPos: single;
      ApplyTexture: boolean = true;
      ApplyMaterial: boolean = true
    ); overload; override;
    procedure Render; overload; override;
    procedure RenderShadow(Extrusion: single; ShadowColor: TDJXColor; RenderVolume: boolean = false);
    procedure Release; override;
    procedure GetBounds(out minVec, maxVec: TD3DXVector3); override;
    function IsPicked(var dist: single; var HitPos: TD3DXVector3): boolean; override;
    function MatrixIndex(Name: string): integer;
    procedure UpdateRenderMatrices;
    procedure InterpolateFrames(Frame1, Frame2: single; Grad: single);
    procedure InterpolateAnimations(
      Anim1: string; Frame1: single;
      Anim2: string; Frame2: single;
      Grad: single
    );
    procedure TransformMesh(const m: TD3DXMatrix);
    procedure SetBoneConstants(FirstRegister: DWORD; FirstBoneConstant: DWORD = 20);
    procedure GenerateShadow;
    procedure UpdateShadow;
  end;

  TDJXMeshStatic = class(TDJXMesh)
  private
    m_Mesh: ID3DXMesh;
    m_MatCount: integer;
    m_MeshMaterials: PDJXMaterial9Array;
    m_MeshTextures: PDJXTexture9Array;
    m_ShadowVolume: TDJXShadowVolume;
  protected
    procedure SetDefaultMaterial;
  public
    property Mesh: ID3DXMesh read m_Mesh write m_Mesh;
    property ShadowVolume: TDJXShadowVolume read m_ShadowVolume write m_ShadowVolume;
    property MatrialCount: integer read m_MatCount write m_MatCount;
    property Materials: PDJXMaterial9Array read m_MeshMaterials write m_MeshMaterials;
    property Textures: PDJXTexture9Array read m_MeshTextures write m_MeshTextures;
    constructor Create(Name: string; DanJetX: Pointer); override;
    destructor Destroy; override;
    procedure LoadFromFile(FileName: string); override;
    procedure LoadFromStream(Stream: TStream); override;
    procedure Render(
      AnimPos: single;
      ApplyTexture: boolean = true;
      ApplyMaterial: boolean = true
    ); override;
    procedure Release; override;
    procedure GetBounds(out minVec, maxVec: TD3DXVector3); override;
    procedure SetTexture(Texture: TDJXTexture); override;
    procedure SetMaterial(Material: TDJXMaterial); override;
    procedure SetMaterialAlpha(Alpha: byte); override;
    function IsPicked(var dist: single; var HitPos: TD3DXVector3): boolean; override;
    procedure Convert(FVF: DWORD); overload;
    procedure Convert(pDeclaration: PD3DVertexElement9); overload;
    procedure ComputeNormals;
    procedure Update;
    procedure MakeBox(Width, Height, Depth: single);
    procedure MakeCylinder(Radius1, Radius2, Length: Single; Slices, Stacks: LongWord);
    procedure MakePolygon(Length: Single; Sides: LongWord);
    procedure MakeSphere(Radius: Single; Slices, Stacks: LongWord);
    procedure MakeTorus(InnerRadius, OuterRadius: Single; Sides, Rings: LongWord);
    procedure MakeTeapot;
    procedure MakeText(Text: string; FontName: WideString; Bold, Italic: Boolean; Extrusion: Single);
    procedure MakeMesh(NumFaces, NumVertices, Options, FVF: DWORD);
  end;

  TDJXMeshSkinned = class(TDJXMesh)
  private
    m_AnimationController: ID3DXAnimationController;
    m_HFrame: PD3DXFrame;
    m_ObjectCenter: TD3DXVector3;
    m_ObjectRadius: single;
  protected
    procedure DrawMeshContainer(pMeshContainerBase: PD3DXMeshContainer; pFrameBase: PD3DXFrame);
    procedure DrawFrame(pFrame: PD3DXFrame);
    function SetupBoneMatrixPointersOnMesh(pMeshContainerBase: PD3DXMeshContainer): HRESULT;
    function SetupBoneMatrixPointers(pFrame: PD3DXFrame): HRESULT;
    procedure UpdateFrameMatrices(
      pFrameBase: PD3DXFrame;
      pParentMatrix: PD3DXMatrix
    );
  public
    constructor Create(Name: string; DanJetX: Pointer); override;
    destructor Destroy; override;
    procedure LoadFromFile(FileName: string); override;
    procedure LoadFromStream(Stream: TStream); override;
    procedure Render(
      AnimPos: single;
      ApplyTexture: boolean = true;
      ApplyMaterial: boolean = true
    ); override;
    procedure Release; override;
    procedure GetBounds(out minVec, maxVec: TD3DXVector3); override;
    procedure SetTexture(Texture: TDJXTexture); override;
    procedure SetMaterial(Material: TDJXMaterial); override;
    procedure SetMaterialAlpha(Alpha: byte); override;
  end;

  TDJXMeshHierarchy = class(ID3DXAllocateHierarchy)
  public
    function CreateFrame(Name: PAnsiChar; out ppNewFrame: PD3DXFrame): HResult; override;
    function CreateMeshContainer(
        Name: PAnsiChar;
        const pMeshData: TD3DXMeshData;
        pMaterials: PD3DXMaterial;
        pEffectInstances: PD3DXEffectInstance;
        NumMaterials: DWORD;
        pAdjacency: PDWORD;
        pSkinInfo: ID3DXSkinInfo;
        out ppMeshContainerOut: PD3DXMeshContainer
      ): HResult; override;
    function DestroyFrame(pFrameToFree: PD3DXFrame): HResult; override;
    function DestroyMeshContainer(pMeshContainerToFree: PD3DXMeshContainer): HResult; override;
  end;


  PD3DXFrameDerived = ^TDJXFrameDerived;
  TDJXFrameDerived = packed record
    Name: PAnsiChar;
    TransformationMatrix: TD3DXMatrix;
    pMeshContainer: PD3DXMeshContainer;
    pFrameSibling: PD3DXFrame;
    pFrameFirstChild: PD3DXFrame;
    ////////////////////////////////////////////
    CombinedTransformationMatrix: TD3DXMatrixA16;
  end;

  
  TDJXMeshContainerDerived = packed record
    Name: PAnsiChar;
    MeshData: TD3DXMeshData;
    pMaterials: PDJXMaterialArray;
    pEffects: PD3DXEffectInstance;
    NumMaterials: DWORD;
    pAdjacency: PDWORD;
    pSkinInfo: ID3DXSkinInfo;
    pNextMeshContainer: PD3DXMeshContainer;
    ////////////////////////////////////////////
    ppTextures: PDJXTexture9Array;
    pOrigMesh: ID3DXMesh;
    pAttributeTable: array of TD3DXAttributeRange;
    NumAttributeGroups: DWORD;
    NumInfl: DWORD;
    pBoneCombinationBuf: ID3DXBuffer;
    ppBoneMatrixPtrs: PDJXMatrixPointerArray;
    pBoneOffsetMatrices: PDJXMatrixArray;
    NumPaletteEntries: DWORD;
    UseSoftwareVP: Boolean;
    iAttributeSW: DWORD;
  end;

  TEdge = record
    v1: TD3DXVector3;
    v2: TD3DXVector3;
    n: TD3DXVector3;
    opposite: int64;
  end;
  TDJXShadowVolume = class
  private
    m_DJX: Pointer;
    m_Mesh: TDJXMeshStatic;
    m_Edges: array of TEdge;
    m_EdgesMod: array of TEdge;
    m_ContEdges: array of array of DWORD;
    m_Shadow: TDJXVB3D;
    m_EdgeCount: cardinal;
    GetIndex: TGetIndexProc;
  public
    constructor Create(DanJetX: Pointer; Mesh: TDJXMeshStatic);
    destructor Destroy; override;
    function GenerateEdges: boolean;
    procedure GenerateShadow(Light: TD3DXVector3; Distance: single);
    procedure Render(ShadowColor: TDJXColor);
    procedure RenderVolume;
  end;

const
  UINT_MAX = High(LongWord);

implementation

uses
  DXTypes;

function GenerateSkinnedMesh(
      const pd3dDevice: IDirect3DDevice9;
      pMeshContainer: PDJXMeshContainerDerived
    ): HRESULT;
var
  d3dCaps: TD3DCaps9;
  rgBoneCombinations: PD3DXBoneCombination;
  cInfl: DWORD;
  i: Integer;
  iInfl: Integer;
  pMeshTmp: ID3DXMesh;
begin
  Result := S_OK;
  pd3dDevice.GetDeviceCaps(d3dCaps);
  if (pMeshContainer.pSkinInfo = nil) then Exit;
  pMeshContainer.MeshData.pMesh := nil;
  pMeshContainer.pBoneCombinationBuf := nil;
  try
    begin
      Result := pMeshContainer.pSkinInfo.ConvertToBlendedMesh(
        pMeshContainer.pOrigMesh,
        D3DXMESH_MANAGED or D3DXMESHOPT_VERTEXCACHE,
        pMeshContainer.pAdjacency,
        nil, nil, nil,
        @pMeshContainer.NumInfl,
        pMeshContainer.NumAttributeGroups,
        pMeshContainer.pBoneCombinationBuf,
        ID3DXMesh(pMeshContainer.MeshData.pMesh)
      );
      if FAILED(Result) then Exit;
      rgBoneCombinations  := PD3DXBoneCombination(pMeshContainer.pBoneCombinationBuf.GetBufferPointer);
      for i := 0 to pMeshContainer.NumAttributeGroups - 1 do
      begin
        cInfl := 0;
        pMeshContainer.iAttributeSW := i;
        for iInfl := 0 to pMeshContainer.NumInfl - 1 do
        begin
          if (rgBoneCombinations.BoneId[iInfl] <> UINT_MAX) then Inc(cInfl);
        end;
        if (cInfl > d3dCaps.MaxVertexBlendMatrices) then Break;
        Inc(rgBoneCombinations);
      end;
      if (pMeshContainer.iAttributeSW < pMeshContainer.NumAttributeGroups) then
      begin
        Result := ID3DXMesh(
          pMeshContainer.MeshData.pMesh
        ).CloneMeshFVF(
          D3DXMESH_SOFTWAREPROCESSING
          or
          ID3DXMesh(pMeshContainer.MeshData.pMesh).GetOptions,
          ID3DXMesh(pMeshContainer.MeshData.pMesh).GetFVF,
          pd3dDevice, pMeshTmp
        );
        if FAILED(Result) then Exit;
        pMeshContainer.MeshData.pMesh := pMeshTmp;
        pMeshTmp := nil;
      end;
    end;
  except
    on EOutOfMemory do
    Result := E_OUTOFMEMORY;
  end;
end;

//TDJXMeshHierarchy BEGIN
function TDJXMeshHierarchy.CreateFrame(
      Name: PAnsiChar;
      out ppNewFrame: PD3DXFrame
    ): HResult;
var
  pFrame: PDJXFrameDerived;
begin
  Result := S_OK;
  ppNewFrame := nil;
  New(pFrame);
  try
    try
      ZeroMemory(pFrame, SizeOf(TDJXFrameDerived));
      pFrame.Name:= StrNew(Name);
      D3DXMatrixIdentity(pFrame.TransformationMatrix);
      D3DXMatrixIdentity(pFrame.CombinedTransformationMatrix);
      ppNewFrame := PD3DXFrame(pFrame);
      pFrame := nil;
    except
      on EOutOfMemory do Result:= E_OUTOFMEMORY;
      else
      raise;
    end;
  finally
    Dispose(pFrame);
  end;
end;
function TDJXMeshHierarchy.CreateMeshContainer(
      Name: PAnsiChar;
      const pMeshData: TD3DXMeshData; pMaterials: PD3DXMaterial;
      pEffectInstances: PD3DXEffectInstance; NumMaterials: DWORD;
      pAdjacency: PDWORD; pSkinInfo: ID3DXSkinInfo;
      out ppMeshContainerOut: PD3DXMeshContainer
    ): HResult;
var
  pNewMeshContainer: PDJXMeshContainerDerived;
  pNewBoneOffsetMatrices: PDJXMatrixArray;

  pd3dDevice: IDirect3DDevice9;

  pMesh: ID3DXMesh;
  NumFaces: LongWord;
  iMaterial: Integer;
  NumBones: DWORD;
  iBone: Integer;
begin
  pNewMeshContainer := nil;
  pNewBoneOffsetMatrices := nil;
  ppMeshContainerOut := nil;
  Result := E_FAIL;
  try
    try
      if (pMeshData._Type <> D3DXMESHTYPE_MESH) then Exit;
      pMesh := pMeshData.pMesh as ID3DXMesh;
      if (pMesh.GetFVF = 0) then Exit;
      New(pNewMeshContainer);
      FillChar(pNewMeshContainer^, 0, SizeOf(TDJXMeshContainerDerived));

      pNewMeshContainer.Name := StrNew(Name);

      Result := pMesh.GetDevice(pd3dDevice);
      if FAILED(Result) then Exit;
      NumFaces := pMesh.GetNumFaces;

      if (pMesh.GetFVF and D3DFVF_NORMAL = 0) then
      begin
        pNewMeshContainer.MeshData._Type := D3DXMESHTYPE_MESH;
        Result := pMesh.CloneMeshFVF(pMesh.GetOptions,
                                   pMesh.GetFVF or D3DFVF_NORMAL,
                                   pd3dDevice, ID3DXMesh(pNewMeshContainer.MeshData.pMesh));
        if FAILED(Result) then Exit;
        pMesh := pNewMeshContainer.MeshData.pMesh as ID3DXMesh;
        D3DXComputeNormals(pMesh, nil);
      end
      else
      begin
        pNewMeshContainer.MeshData.pMesh := pMesh;
        pNewMeshContainer.MeshData._Type := D3DXMESHTYPE_MESH;
      end;
      pNewMeshContainer.NumMaterials := max(1, NumMaterials);
      GetMem(pNewMeshContainer.pMaterials, SizeOf(TD3DXMaterial) * pNewMeshContainer.NumMaterials);
      GetMem(pNewMeshContainer.ppTextures, SizeOf(IDirect3DTexture9) * pNewMeshContainer.NumMaterials);

      GetMem(pNewMeshContainer.pAdjacency, SizeOf(DWORD) * NumFaces * 3);
      CopyMemory(pNewMeshContainer.pAdjacency, pAdjacency, SizeOf(DWORD) * NumFaces * 3);
      ZeroMemory(pNewMeshContainer.ppTextures, SizeOf(IDirect3DTexture9) * pNewMeshContainer.NumMaterials);

      if (NumMaterials > 0) then
      begin
        CopyMemory(pNewMeshContainer.pMaterials, pMaterials, SizeOf(TD3DXMaterial) * NumMaterials);

        for iMaterial := 0 to NumMaterials - 1 do
        begin
          pNewMeshContainer.pMaterials[iMaterial].MatD3D.Ambient.r := 1;
          pNewMeshContainer.pMaterials[iMaterial].MatD3D.Ambient.g := 1;
          pNewMeshContainer.pMaterials[iMaterial].MatD3D.Ambient.b := 1;
          pNewMeshContainer.pMaterials[iMaterial].MatD3D.Ambient.a := 1;
          if (pNewMeshContainer.pMaterials[iMaterial].pTextureFilename <> nil) then
          begin
            if FAILED(
              D3DXCreateTextureFromFileW(
                pd3dDevice,
                PWideChar(pNewMeshContainer.pMaterials[iMaterial].pTextureFilename),
                pNewMeshContainer.ppTextures[iMaterial]
              )
            )
            then
            pNewMeshContainer.ppTextures[iMaterial] := nil;
            pNewMeshContainer.pMaterials[iMaterial].pTextureFilename := nil;
          end;
        end;
      end
      else
      begin
        pNewMeshContainer.pMaterials[0].pTextureFilename := nil;
        ZeroMemory(@pNewMeshContainer.pMaterials[0].MatD3D, SizeOf(TD3DMaterial9));
        pNewMeshContainer.pMaterials[0].MatD3D.Diffuse.r := 0.5;
        pNewMeshContainer.pMaterials[0].MatD3D.Diffuse.g := 0.5;
        pNewMeshContainer.pMaterials[0].MatD3D.Diffuse.b := 0.5;
        pNewMeshContainer.pMaterials[0].MatD3D.Specular := pNewMeshContainer.pMaterials[0].MatD3D.Diffuse;
      end;

      if (pSkinInfo <> nil) then
      begin
        pNewMeshContainer.pSkinInfo := pSkinInfo;
        pNewMeshContainer.pOrigMesh := pMesh;
        NumBones := pSkinInfo.GetNumBones;
        GetMem(pNewBoneOffsetMatrices, SizeOf(TD3DXMatrix)*NumBones);
        for iBone := 0 to NumBones - 1 do
        begin
          pNewBoneOffsetMatrices[iBone] := pSkinInfo.GetBoneOffsetMatrix(iBone)^;
        end;
        pNewMeshContainer.pBoneOffsetMatrices := pNewBoneOffsetMatrices;
        pNewBoneOffsetMatrices := nil;
        Result := GenerateSkinnedMesh(pd3dDevice, pNewMeshContainer);
        if FAILED(Result) then Exit;
      end
      else
      pNewMeshContainer.pBoneOffsetMatrices := nil;
      ppMeshContainerOut := PD3DXMeshContainer(pNewMeshContainer);
      pNewMeshContainer := nil;
      Result := S_OK;
    except
      on EOutOfMemory do Result:= E_OUTOFMEMORY;
      else
      raise;
    end;
  finally
    FreeMem(pNewBoneOffsetMatrices);
    pd3dDevice := nil;
    if (pNewMeshContainer <> nil) then
    DestroyMeshContainer(PD3DXMeshContainer(pNewMeshContainer));
  end;
end;
function TDJXMeshHierarchy.DestroyFrame(pFrameToFree: PD3DXFrame): HResult;
begin
  StrDispose(pFrameToFree.Name);
  Dispose(pFrameToFree);
  Result:= S_OK;
end;
function TDJXMeshHierarchy.DestroyMeshContainer(
      pMeshContainerToFree: PD3DXMeshContainer
    ): HResult;
var
  iMaterial: LongWord;
  pMeshContainer: PDJXMeshContainerDerived;
begin
  pMeshContainer := PDJXMeshContainerDerived(pMeshContainerToFree);

  StrDispose(pMeshContainer.Name);
  if pMeshContainer.pAdjacency <> nil then
  FreeMem(pMeshContainer.pAdjacency);
  if pMeshContainer.pMaterials <> nil then
  FreeMem(pMeshContainer.pMaterials);
  if pMeshContainer.pBoneOffsetMatrices <> nil then
  FreeMem(pMeshContainer.pBoneOffsetMatrices);

  if (pMeshContainer.ppTextures <> nil) then
  for iMaterial := 0 to pMeshContainer.NumMaterials - 1 do
  pMeshContainer.ppTextures[iMaterial] := nil;

  FreeMem(pMeshContainer.ppTextures);
  FreeMem(pMeshContainer.ppBoneMatrixPtrs);
  SAFE_RELEASE(pMeshContainer.pBoneCombinationBuf);
  SAFE_RELEASE(pMeshContainer.MeshData.pMesh);
  SAFE_RELEASE(pMeshContainer.pSkinInfo);
  SAFE_RELEASE(pMeshContainer.pOrigMesh);
  Dispose(pMeshContainer);
  Result := S_OK;
end;
//TDJXMeshHierarchy END

//TDJXMeshList BEGIN
constructor TDJXMeshList.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;
destructor TDJXMeshList.Destroy;
begin
  if m_Meshes <> nil then
  begin
    FreeMeshes;
    m_Meshes.Free;
  end;
  inherited Destroy;
end;
procedure TDJXMeshList.Init(DanJetX: Pointer);
begin
  m_Meshes := Tlist.Create;
  inherited Init(DanJetX);
end;
procedure TDJXMeshList.OnLostDevice;
begin

end;
procedure TDJXMeshList.OnResetDevice;
begin

end;
function TDJXMeshList.Add(Mesh: TDJXMesh): TDJXMesh;
begin
  m_Meshes.Add(Mesh);
  Result := Mesh;
end;
function TDJXMeshList.Remove(Mesh: TDJXMesh): TDJXMesh;
begin
  m_Meshes.Remove(Mesh);
  Result := Mesh;
end;
function TDJXMeshList.RemoveName(Name: string): TDJXMesh;
var
  Mesh: TDJXMesh;
begin
  Mesh := Find(Name);
  m_Meshes.Remove(Mesh);
  Result := Mesh;
end;
function TDJXMeshList.Delete(Index: integer): TDJXMesh;
begin
  Result := Meshes[Index];
  m_Meshes.Delete(Index);
end;
procedure TDJXMeshList.FreeAndRemove(Mesh: TDJXMesh);
begin
  Remove(Mesh).Free;
end;
procedure TDJXMeshList.FreeAndRemoveName(Name: string);
begin
  RemoveName(Name).Free;
end;
procedure TDJXMeshList.FreeAndDelete(Index: integer);
begin
  Delete(Index).Free;
end;
procedure TDJXMeshList.FreeMeshes;
begin
  if m_Meshes <> nil then
  begin
    while m_Meshes.Count > 0 do
    begin
      TDJXMesh(m_Meshes[0]).Free;
      m_Meshes.Delete(0);
    end;
  end;
end;
procedure TDJXMeshList.Clear;
begin
  if m_Meshes <> nil then
  begin
    while m_Meshes.Count > 0 do
    m_Meshes.Delete(0);
  end;
end;
function TDJXMeshList.Find(Name: string): TDJXMesh;
var
  i: integer;
begin
  for i := 0 to m_Meshes.Count - 1 do
  if TDJXMesh(m_Meshes.Items[i]).Name = Name then
  begin
    result := TDJXMesh(m_Meshes.Items[i]);
    exit;
  end;
  result := nil;
end;
function TDJXMeshList.FindIndex(name: string): integer;
var
  i: integer;
begin
  for i := 0 to m_Meshes.Count - 1 do
  if TDJXMesh(m_Meshes.Items[i]).Name = name then
  begin
    result := i;
    exit;
  end;
  result := -1;
end;
function TDJXMeshList.getItem(Index: integer): TDJXMesh;
begin
  result := TDJXMesh(m_Meshes.items[Index]);
end;
procedure TDJXMeshList.setItem(Index: integer; Value: TDJXMesh);
begin
  m_Meshes.items[Index]:=Value;
end;
function TDJXMeshList.getCount: integer;
begin
  result := m_Meshes.Count;
end;
function TDJXMeshList.CreateMeshStatic(MeshName: string): TDJXMeshStatic;
var
  Mesh: TDJXMeshStatic;
begin
  Mesh := TDJXMeshStatic.Create(MeshName, m_DanJetX);
  Add(Mesh);
  Result := Mesh;
end;
function TDJXMeshList.CreateMeshSkinned(MeshName: string): TDJXMeshSkinned;
var
  Mesh: TDJXMeshSkinned;
begin
  Mesh := TDJXMeshSkinned.Create(MeshName, m_DanJetX);
  Add(Mesh);
  Result := Mesh;
end;
function TDJXMeshList.CreateMeshEx(MeshName: string): TDJXMeshEx;
var
  Mesh: TDJXMeshEx;
begin
  Mesh := TDJXMeshEx.Create(MeshName, m_DanJetX);
  Add(Mesh);
  Result := Mesh;
end;
function TDJXMeshList.CreateMeshFromFile(f: string; MeshName: string): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.LoadFromFile(f);
end;
function TDJXMeshList.CreateMeshFromStream(Stream: TStream; MeshName: string): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.LoadFromStream(Stream);
end;
function TDJXMeshList.CreateMeshFromRes(ResName, Folder, Res: string; MeshName: string): TDJXMeshStatic;
begin
  Result := CreateMeshStatic(MeshName);
  Result.LoadFromRes(ResName, Folder, Res);
end;
function TDJXMeshList.CreateMeshFromFileAnim(f: string; MeshName: string): TDJXMeshSkinned;
begin
  result := CreateMeshSkinned(MeshName);
  result.LoadFromFile(f);
end;
function TDJXMeshList.CreateMeshFromStreamAnim(Stream: TStream; MeshName: string): TDJXMeshSkinned;
begin
  result := CreateMeshSkinned(MeshName);
  result.LoadFromStream(Stream);
end;
function TDJXMeshList.CreateMeshFromResAnim(ResName, Folder, Res: string; MeshName: string): TDJXMeshSkinned;
begin
  Result := CreateMeshSkinned(MeshName);
  Result.LoadFromRes(ResName, Folder, Res);
end;
function TDJXMeshList.CreateMeshFromFileEx(f: string; MeshName: string): TDJXMeshEx;
begin
  result := CreateMeshEx(MeshName);
  result.LoadFromFile(f);
end;
function TDJXMeshList.CreateMeshFromStreamEx(Stream: TStream; MeshName: string): TDJXMeshEx;
begin
  result := CreateMeshEx(MeshName);
  result.LoadFromStream(Stream);
end;
function TDJXMeshList.CreateMeshFromResEx(ResName, Folder, Res: string; MeshName: string): TDJXMeshEx;
begin
  Result := CreateMeshEx(MeshName);
  Result.LoadFromRes(ResName, Folder, Res);
end;
function TDJXMeshList.CreateMeshBox(MeshName: string; Width, Height, Depth: single): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.MakeBox(Width, Height, Depth);
end;
function TDJXMeshList.CreateMeshCylinder(
      MeshName: string;
      Radius1, Radius2, Length: Single;
      Slices, Stacks: LongWord
    ): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.MakeCylinder(Radius1, Radius2, Length, Slices, Stacks);
end;
function TDJXMeshList.CreateMeshPolygon(MeshName: string; Length: Single; Sides: LongWord): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.MakePolygon(Length, Sides);
end;
function TDJXMeshList.CreateMeshSphere(
      MeshName: string;
      Radius: Single;
      Slices, Stacks: LongWord
    ): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.MakeSphere(Radius, Slices, Stacks);
end;
function TDJXMeshList.CreateMeshTorus(
      MeshName: string;
      InnerRadius, OuterRadius: Single;
      Sides, Rings: LongWord
    ): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.MakeTorus(InnerRadius, OuterRadius, Sides, Rings);
end;
function TDJXMeshList.CreateMeshTeapot(MeshName: string): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.MakeTeapot;
end;
function TDJXMeshList.CreateMeshText(
      MeshName: string;
      Text: string;
      FontName: string;
      Bold, Italic: Boolean;
      Extrusion: Single
    ): TDJXMeshStatic;
begin
  result := CreateMeshStatic(MeshName);
  result.MakeText(Text, FontName, Bold, Italic, Extrusion);
end;
procedure TDJXMeshList.LoadFromFile(f: string);
type
  TFileHeader = record
    caption: string[16];
    MeshCount: integer;
  end;
  TMeshInfo = record
    name: string[32];
    offset: int64;
    animated: boolean;
  end;
var
  FileHeader: TFileHeader;
  MeshInfo: TMeshInfo;
  Stream: TFileStream;
  MStream: TStream;
  i: integer;
begin
  if not fileExists(f) then exit;
  Stream:=TFileStream.Create(f,fmOpenRead);
  Stream.Position:=0;
  Stream.ReadBuffer(FileHeader,sizeof(FileHeader));
  for i:=0 to FileHeader.MeshCount-1 do
  begin
    MStream:=TMemoryStream.Create;
    MStream.Position:=0;
    Stream.ReadBuffer(MeshInfo,sizeof(MeshInfo));
    MStream.CopyFrom(Stream,MeshInfo.offset);
    if MeshInfo.animated then
    CreateMeshFromStreamAnim(MStream,MeshInfo.name)
    else
    CreateMeshFromStream(MStream,MeshInfo.name);
    MStream.Free;
  end;
  Stream.Free;
end;
//TDJXMeshList END

//TDJXMesh BEGIN
constructor TDJXMesh.Create(Name: string; DanJetX: Pointer);
begin
  inherited Create;
  m_Name := Name;
  m_DanJetX := DanJetX;
end;
destructor TDJXMesh.Destroy;
begin
  Release;
  inherited Destroy;
end;

procedure TDJXMesh.CalculateSize;
begin
  m_SizeX := m_MaxVec.x - m_MinVec.x;
  m_SizeY := m_MaxVec.y - m_MinVec.y;
  m_SizeZ := m_MaxVec.z - m_MinVec.z;
end;
procedure TDJXMesh.SetFrame(Value: single);
begin
  //Reserved
end;

procedure TDJXMesh.LoadFromFile(FileName: string);
begin
  //Reserved
end;
procedure TDJXMesh.LoadFromStream(Stream: TStream);
begin
  //Reserved
end;
procedure TDJXMesh.LoadFromRes(
      const ResName: string;
      const Folder: string;
      const Res: string
    );
var
  R: TDJXRes;
  ResBuffer: TDJXResBuffer;
  ms: TMemoryStream;
begin
  R := TDanJetX(m_DanJetX).Resources.Find(ResName);
  if Assigned(R) then
  begin
    ResBuffer := R.GetResBuffer(Folder, Res);
    ms := TMemoryStream.Create;
    ms.Write(ResBuffer.Data^, ResBuffer.Size);
    ms.Position := 0;
    LoadFromStream(ms);
    ms.Free;
  end;
end;
procedure TDJXMesh.Render(
      AnimPos: single;
      ApplyTexture: boolean = true;
      ApplyMaterial: boolean = true
    );
begin
  //Reserved
end;
procedure TDJXMesh.Render;
begin
  Render(0);
end;
procedure TDJXMesh.Release;
begin
  //Reserved
end;
procedure TDJXMesh.GetBounds(out minVec, maxVec: TD3DXVector3);
begin
  //Reserved
end;
procedure TDJXMesh.GetBoundsAA(out MinVec, MaxVec: TD3DXVector3);
var
  OVecs: array[0..7] of TD3DXVector3;
  MinVecTr, MaxVecTr: TD3DXVector3;
  i: integer;
  NewMin, NewMax: TD3DXVector3;
begin
  OVecs[0].x := m_MinVec.x;
  OVecs[0].y := m_MinVec.y;
  OVecs[0].z := m_MinVec.z;

  OVecs[1].x := m_MinVec.x;
  OVecs[1].y := m_MinVec.y;
  OVecs[1].z := m_MaxVec.z;

  OVecs[2].x := m_MaxVec.x;
  OVecs[2].y := m_MinVec.y;
  OVecs[2].z := m_MaxVec.z;

  OVecs[3].x := m_MaxVec.x;
  OVecs[3].y := m_MinVec.y;
  OVecs[3].z := m_MinVec.z;

  OVecs[4].x := m_MinVec.x;
  OVecs[4].y := m_MaxVec.y;
  OVecs[4].z := m_MinVec.z;

  OVecs[5].x := m_MinVec.x;
  OVecs[5].y := m_MaxVec.y;
  OVecs[5].z := m_MaxVec.z;

  OVecs[6].x := m_MaxVec.x;
  OVecs[6].y := m_MaxVec.y;
  OVecs[6].z := m_MaxVec.z;

  OVecs[7].x := m_MaxVec.x;
  OVecs[7].y := m_MaxVec.y;
  OVecs[7].z := m_MinVec.z;

  for i := 0 to 7 do
  D3DXVec3TransformCoord(
    OVecs[i], OVecs[i],
    TDJXRender(TDanJetX(m_DanJetX).Render).MatWorld^
  );

  D3DXVec3TransformCoord(
    MinVecTr, m_MinVec,
    TDJXRender(TDanJetX(m_DanJetX).Render).MatWorld^
  );
  D3DXVec3TransformCoord(
    MaxVecTr, m_MaxVec,
    TDJXRender(TDanJetX(m_DanJetX).Render).MatWorld^
  );

  NewMin.x := (MinVecTr.x + MaxVecTr.x) / 2;
  NewMin.y := (MinVecTr.y + MaxVecTr.y) / 2;
  NewMin.z := (MinVecTr.z + MaxVecTr.z) / 2;

  NewMax.x := NewMin.x;
  NewMax.y := NewMin.y;
  NewMax.z := NewMin.z;

  for i := 0 to 7 do
  begin
    if OVecs[i].x < NewMin.x then NewMin.x := OVecs[i].x;
    if OVecs[i].y < NewMin.y then NewMin.y := OVecs[i].y;
    if OVecs[i].z < NewMin.z then NewMin.z := OVecs[i].z;
    if OVecs[i].x > NewMax.x then NewMax.x := OVecs[i].x;
    if OVecs[i].y > NewMax.y then NewMax.y := OVecs[i].y;
    if OVecs[i].z > NewMax.z then NewMax.z := OVecs[i].z;
  end;
  MinVec := NewMin;
  MaxVec := NewMax;
end;
procedure TDJXMesh.SetTexture(Texture: TDJXTexture);
begin
  //Reserved
end;
procedure TDJXMesh.SetMaterial(Material: TDJXMaterial);
begin
  //Reserved
end;
procedure TDJXMesh.SetMaterialAlpha(Alpha: byte);
begin
  //Reserved
end;
function TDJXMesh.IsInFrustum(Frustum: TDJXFrustum = nil): boolean;
var
  minVecW, maxVecW: TD3DXVector3;
begin
  GetBoundsAA(minVecW,maxVecW);
  result := TDanJetX(m_DanJetX).Camera.BoxInFrustum(minVecW, maxVecW, Frustum);
end;
function TDJXMesh.IsPicked(var dist: single; var HitPos: TD3DXVector3): boolean;
begin
  //Reserved
end;
//TDJXMesh END

//TDJXMeshEx BEGIN
constructor TDJXMeshEx.Create(Name: string; DanJetX: Pointer);
begin
  inherited Create(Name, DanJetX);
  m_Skinned := false;
  m_LightColor := $ffffffff;
  m_LightPos := D3DXVector3(0, 0, 0);
  m_LightFalloff := 50;
  m_SpecularPower := 15;
  m_ParalaxBias := 0.003;
  m_RenderMode := rmBasic;
  m_CurAnim := 0;
end;
destructor TDJXMeshEx.Destroy;
begin
  Release;
  inherited Destroy;
end;

procedure TDJXMeshEx.ResetMesh(FCount, VCount: DWORD);
var
  Decl: TFVFDeclaration;
begin
  safe_release(m_Mesh);
  if (m_Header.VertexCount <= 0) or (m_Header.IndexCount <= 0) then Exit;
  Decl[0] := D3DVertexElement(0, 4 * 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0);
  Decl[1] := D3DVertexElement(0, 4 * 3, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0);
  Decl[2] := D3DVertexElement(0, 4 * 6, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0);
  Decl[3] := D3DVertexElement(0, 4 * 9, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0);
  Decl[4] := D3DVertexElement(0, 4 * 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0);
  Decl[5] := D3DVertexElement(0, 4 * 15, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0);
  Decl[6] := D3DVertexElement(0, 4 * 19, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0);
  Decl[7] := D3DDECL_END;
  D3DXCreateMesh(
    FCount,
    VCount,
    D3DXMESH_MANAGED or D3DXMESH_32BIT,
    @Decl[0],
    TDanJetX(m_DanJetX).Device,
    m_Mesh
  );
end;

procedure TDJXMeshEx.ResetShadow(FCount, VCount: DWORD);
var
  Decl: TFVFDeclaration;
begin
  safe_release(m_Shadow);
  Decl[0] := D3DVertexElement(0, 4 * 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0);
  Decl[1] := D3DVertexElement(0, 4 * 3, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0);
  Decl[2] := D3DVertexElement(0, 4 * 6, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0);
  Decl[3] := D3DVertexElement(0, 4 * 10, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0);
  Decl[4] := D3DDECL_END;
  D3DXCreateMesh(
    FCount,
    VCount,
    D3DXMESH_MANAGED or D3DXMESH_32BIT or D3DXMESH_SYSTEMMEM,
    @Decl[0],
    TDanJetX(m_DanJetX).Device,
    m_Shadow
  );
  safe_release(m_ShadowRender);
  Decl[0] := D3DVertexElement(0, 4 * 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0);
  Decl[1] := D3DVertexElement(0, 4 * 3, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0);
  Decl[2] := D3DDECL_END;
  D3DXCreateMesh(
    FCount,
    VCount,
    D3DXMESH_MANAGED or D3DXMESH_32BIT,
    @Decl[0],
    TDanJetX(m_DanJetX).Device,
    m_ShadowRender
  );
end;

procedure TDJXMeshEx.LoadMeshBuffers;
type
  PMeshVertex = ^TMeshVertex;
  TMeshVertex = packed record
    Position: TD3DXVector3;
    Normal: TD3DXVector3;
    Tangent: TD3DXVector3;
    Binormal: TD3DXVector3;
    TexCoords: TD3DXVector3;
    BoneWeights: TD3DXVector4;
    BoneIndices: TD3DXVector4;
  end;
  TMeshVertexArr = array[0..0] of TMeshVertex;
  PMeshVertexArr = ^TMeshVertexArr;
var
  Ptr: Pointer;
  i, j: integer;
  MeshVertex: TMeshVertex;
  TmpMesh: ID3DXMesh;
  m: TD3DXMatrix;
  Adj: array of DWORD;
begin
  ResetMesh(m_Header.IndexCount div 3, m_Header.VertexCount);

  if not Assigned(m_Mesh) then Exit;

  m_Mesh.LockVertexBuffer(D3DLOCK_DISCARD, Ptr);
  for i := 0 to m_Header.VertexCount - 1 do
  begin
    MeshVertex.Position := m_Vertices[i].Pos;
    MeshVertex.Normal := m_Vertices[i].Normal;
    MeshVertex.TexCoords := m_Vertices[i].TexCoords;
    MeshVertex.Tangent := D3DXVector3(0,0,0);
    MeshVertex.Binormal := D3DXVector3(0,0,0);
    MeshVertex.BoneWeights := D3DXVector4(0,0,0,0);
    MeshVertex.BoneIndices := D3DXVector4(0,0,0,0);
    
    if m_Vertices[i].BoneCount > 0 then
    MeshVertex.BoneWeights.x := m_Vertices[i].Weights[0]
    else MeshVertex.BoneWeights.x := 0;
    if m_Vertices[i].BoneCount > 1 then
    MeshVertex.BoneWeights.y := m_Vertices[i].Weights[1]
    else MeshVertex.BoneWeights.y := 0;
    if m_Vertices[i].BoneCount > 2 then
    MeshVertex.BoneWeights.z := m_Vertices[i].Weights[2]
    else MeshVertex.BoneWeights.z := 0;
    if m_Vertices[i].BoneCount > 3 then
    MeshVertex.BoneWeights.w := m_Vertices[i].Weights[3]
    else MeshVertex.BoneWeights.w := 0;

    if m_Vertices[i].BoneCount > 0 then
    MeshVertex.BoneIndices.x := m_Vertices[i].Bones[0]
    else MeshVertex.BoneIndices.x := 0;
    if m_Vertices[i].BoneCount > 1 then
    MeshVertex.BoneIndices.y := m_Vertices[i].Bones[1]
    else MeshVertex.BoneIndices.y := 0;
    if m_Vertices[i].BoneCount > 2 then
    MeshVertex.BoneIndices.z := m_Vertices[i].Bones[2]
    else MeshVertex.BoneIndices.z := 0;
    if m_Vertices[i].BoneCount > 3 then
    MeshVertex.BoneIndices.w := m_Vertices[i].Bones[3]
    else MeshVertex.BoneIndices.w := 0;
    
    move(MeshVertex, Pointer(cardinal(Ptr) + i * sizeof(TMeshVertex))^, sizeof(TMeshVertex));
  end;
  m_Mesh.UnlockVertexBuffer;
  m_Mesh.LockIndexBuffer(D3DLOCK_DISCARD, Ptr);
  move(m_Indices[0], Ptr^, sizeof(DWORD) * m_Header.IndexCount);
  m_Mesh.UnlockIndexBuffer;
  D3DXComputeTangentFrameEx(
    m_Mesh,
    DWORD(D3DDECLUSAGE_TEXCOORD), 0,
    DWORD(D3DDECLUSAGE_BINORMAL), 0,
    DWORD(D3DDECLUSAGE_TANGENT), 0,
    DWORD(D3DDECLUSAGE_NORMAL), 0,
    0,
    nil,
    0.01, 0.25, 0.01,
    TmpMesh,
    nil
  );
  safe_release(m_Mesh);
  m_Mesh := TmpMesh;
  safe_release(TmpMesh);
  SetLength(Adj, m_Mesh.GetNumFaces * 3);
  m_Mesh.GenerateAdjacency(0, @Adj[0]);
  m_Mesh.OptimizeInplace(
    D3DXMESHOPT_COMPACT
    or D3DXMESHOPT_VERTEXCACHE,
    @Adj[0], nil, nil, nil
  );
end;

procedure TDJXMeshEx.LoadShadowBuffers;
type
  PMeshVertex = ^TMeshVertex;
  TMeshVertex = packed record
    Position: TD3DXVector3;
    Normal: TD3DXVector3;
    Weights: TD3DXVector4;
    Indices: TD3DXVector4;
  end;
  PMeshVertexArr = ^TMeshVertexArr;
  TMeshVertexArr = array[0..0] of TMeshVertex;
  PShadowVertex = ^TShadowVertex;
  TShadowVertex = packed record
    Position: TD3DXVector3;
    Normal: TD3DXVector3;
  end;
  PShadowVertexArr = ^TShadowVertexArr;
  TShadowVertexArr = array[0..0] of TShadowVertex;
var
  ShadowVertices: array of TDJXMXVertex;
  VCount, FCount: integer;
  ShadowVCount: integer;
  ShadowICount: integer;
  ShadowIndices: array of DWORD;
  b: boolean;
  Ptr1, Ptr2, Ptr3: Pointer;
  MeshVertexArrPtr: PMeshVertexArr;
  ShadowVertex: TShadowVertex;
  ShadowVertexArrPtr: PShadowVertexArr;
  TmpMesh: ID3DXMesh;
  Options: DWORD;
  WORDArrPtr: PWordArray;
  DWORDArrPtr1, DWORDArrPtr2: PDWordArray;
  procedure SetBones(var v: TMeshVertex);
  var
    i, j: integer;
    pos: TD3DXVector3;
    weights, indices: TD3DXVector4;
  begin
    for i := 0 to VCount - 1 do
    if (v.Position.x = ShadowVertices[i].Pos.x)
    and(v.Position.y = ShadowVertices[i].Pos.y)
    and(v.Position.z = ShadowVertices[i].Pos.z) then
    begin
      v.Weights := D3DXVector4(0, 0, 0, 0);
      v.Indices := D3DXVector4(0, 0, 0, 0);
      if ShadowVertices[i].BoneCount > 0 then
      begin
        v.Indices.x := ShadowVertices[i].Bones[0];
        v.Weights.x := ShadowVertices[i].Weights[0];
      end;
      if ShadowVertices[i].BoneCount > 1 then
      begin
        v.Indices.y := ShadowVertices[i].Bones[1];
        v.Weights.y := ShadowVertices[i].Weights[1];
      end;
      if ShadowVertices[i].BoneCount > 2 then
      begin
        v.Indices.z := ShadowVertices[i].Bones[2];
        v.Weights.z := ShadowVertices[i].Weights[2];
      end;
      if ShadowVertices[i].BoneCount > 3 then
      begin
        v.Indices.w := ShadowVertices[i].Bones[3];
        v.Weights.w := ShadowVertices[i].Weights[3];
      end;
      break;
    end;
  end;
var
  i, j: integer;
begin
  VCount := 0;
  SetLength(ShadowIndices, m_Header.IndexCount);
  SetLength(ShadowVertices, m_Header.VertexCount);
  for i := 0 to m_Header.IndexCount - 1 do
  begin
    b := true;
    for j := 0 to VCount - 1 do
    begin
      if (m_Vertices[m_Indices[i]].Pos.x = ShadowVertices[j].Pos.x)
      and (m_Vertices[m_Indices[i]].Pos.y = ShadowVertices[j].Pos.y)
      and (m_Vertices[m_Indices[i]].Pos.z = ShadowVertices[j].Pos.z) then
      begin
        ShadowIndices[i] := j;
        D3DXVec3Add(ShadowVertices[j].Normal, ShadowVertices[j].Normal, m_Vertices[m_Indices[i]].Normal);
        b := false;
        break;
      end;
    end;
    if b then
    begin
      ShadowVertices[VCount].Pos.x := m_Vertices[m_Indices[i]].Pos.x;
      ShadowVertices[VCount].Pos.y := m_Vertices[m_Indices[i]].Pos.y;
      ShadowVertices[VCount].Pos.z := m_Vertices[m_Indices[i]].Pos.z;
      ShadowVertices[VCount].Normal.x := m_Vertices[m_Indices[i]].Normal.x;
      ShadowVertices[VCount].Normal.y := m_Vertices[m_Indices[i]].Normal.y;
      ShadowVertices[VCount].Normal.z := m_Vertices[m_Indices[i]].Normal.z;
      ShadowVertices[VCount].BoneCount := m_Vertices[m_Indices[i]].BoneCount;
      SetLength(ShadowVertices[VCount].Weights, m_Vertices[m_Indices[i]].BoneCount);
      SetLength(ShadowVertices[VCount].Bones, m_Vertices[m_Indices[i]].BoneCount);
      move(
        m_Vertices[m_Indices[i]].Weights[0],
        ShadowVertices[VCount].Weights[0],
        m_Vertices[m_Indices[i]].BoneCount * sizeof(single)
      );
      move(
        m_Vertices[m_Indices[i]].Bones[0],
        ShadowVertices[VCount].Bones[0],
        m_Vertices[m_Indices[i]].BoneCount * sizeof(DWORD)
      );
      ShadowIndices[i] := VCount;
      VCount := VCount + 1;
    end;
  end;

  D3DXCreateMeshFVF(
    m_Header.IndexCount div 3,
    VCount,
    D3DXMESH_MANAGED or D3DXMESH_32BIT,
    D3DFVF_XYZ or D3DFVF_NORMAL,
    TDanJetX(m_DanJetX).Device,
    TmpMesh
  );

  TmpMesh.LockVertexBuffer(D3DLOCK_DISCARD, Ptr1);

  for i := 0 to VCount - 1 do
  begin
    D3DXVec3Normalize(ShadowVertices[i].Normal, ShadowVertices[i].Normal);

    ShadowVertex.Position := ShadowVertices[i].Pos;
    ShadowVertex.Normal := ShadowVertices[i].Normal;

    move(ShadowVertex, Pointer(cardinal(Ptr1) + i * sizeof(TShadowVertex))^, sizeof(TShadowVertex));
  end;
  TmpMesh.UnlockVertexBuffer;
  TmpMesh.LockIndexBuffer(D3DLOCK_DISCARD, Ptr1);
  move(ShadowIndices[0], Ptr1^, sizeof(DWORD) * m_Header.IndexCount);
  TmpMesh.UnlockIndexBuffer;
  D3DXComputeNormals(TmpMesh, nil);
  
  m_Shadow := TmpMesh;
  safe_release(TmpMesh);
  GenerateShadowMesh(TDanJetX(m_DanJetX).Device, m_Shadow, TmpMesh);
  safe_release(m_Shadow);
  
  ResetShadow(TmpMesh.GetNumFaces, TmpMesh.GetNumVertices);

  ShadowICount := TmpMesh.GetNumFaces * 3;
  Options := TmpMesh.GetOptions;
  TmpMesh.LockIndexBuffer(D3DLOCK_READONLY, Ptr1);
  m_Shadow.LockIndexBuffer(D3DLOCK_DISCARD, Ptr2);
  m_ShadowRender.LockIndexBuffer(D3DLOCK_DISCARD, Ptr3);
  if Options and D3DXMESH_32BIT > 0 then
  begin
    move(Ptr1^, Ptr2^, sizeof(DWORD) * ShadowICount);
    move(Ptr1^, Ptr3^, sizeof(DWORD) * ShadowICount);
  end
  else
  begin
    WORDArrPtr := Ptr1;
    DWORDArrPtr1 := Ptr2;
    DWORDArrPtr2 := Ptr3;
    for i := 0 to ShadowICount - 1 do
    begin
      DWORDArrPtr1[i] := WORDArrPtr[i];
      DWORDArrPtr2[i] := WORDArrPtr[i];
    end;
  end;
  TmpMesh.UnlockIndexBuffer;
  m_Shadow.UnlockIndexBuffer;
  m_ShadowRender.UnlockIndexBuffer;

  ShadowVCount := TmpMesh.GetNumVertices;

  TmpMesh.LockVertexBuffer(D3DLOCK_READONLY, Ptr1);
  m_Shadow.LockVertexBuffer(D3DLOCK_DISCARD, Ptr2);
  ShadowVertexArrPtr := Ptr1;
  MeshVertexArrPtr := Ptr2;
  for i := 0 to ShadowVCount - 1 do
  begin
    move(ShadowVertexArrPtr^[i].Position, MeshVertexArrPtr^[i].Position, sizeof(TD3DXVector3));
    move(ShadowVertexArrPtr^[i].Normal, MeshVertexArrPtr^[i].Normal, sizeof(TD3DXVector3));
    SetBones(MeshVertexArrPtr^[i]);
  end;
  m_Shadow.UnlockVertexBuffer;
  TmpMesh.UnlockVertexBuffer;
  safe_release(TmpMesh);
end;

procedure TDJXMeshEx.ComputeRenderMatrix(Index: integer);
var
  m: TD3DXMatrix;
  i: integer;
begin
  D3DXMatrixIdentity(m_Bones[Index].RenderMatrix);
  D3DXMatrixTranslation(
    m_Bones[Index].RenderMatrix,
    -m_Bones[Index].Position.x,
    -m_Bones[Index].Position.y,
    -m_Bones[Index].Position.z
  );
  D3DXMatrixMultiply(m_Bones[Index].RenderMatrix, m_Bones[Index].RenderMatrix, m_Bones[Index].Matrix);
  D3DXMatrixTranslation(
    m,
    m_Bones[Index].Position.x,
    m_Bones[Index].Position.y,
    m_Bones[Index].Position.z
  );
  D3DXMatrixMultiply(m_Bones[Index].RenderMatrix, m_Bones[Index].RenderMatrix, m);
  if m_Bones[Index].Parent <> Index then
  D3DXMatrixMultiply(
    m_Bones[Index].RenderMatrix,
    m_Bones[Index].RenderMatrix,
    m_Bones[m_Bones[Index].Parent].RenderMatrix
  );
  for i := 0 to m_Bones[Index].ChildrenCount - 1 do
  ComputeRenderMatrix(m_Bones[Index].Children[i]);
end;
procedure TDJXMeshEx.RefineAnimation(Index: integer);
var
  i, j: integer;
  m: TD3DXMatrix;
  Matrices: array of TD3DXMatrix;
begin
  SetLength(Matrices, Length(m_Animations[Index].Matrices[0]));
  for i := 0 to High(m_Animations[Index].Matrices) do
  begin
    for j := 0 to High(m_Animations[Index].Matrices[0]) do
    begin
      if m_Bones[j].Parent <> j then
      begin
        D3DXMatrixInverse(m, nil, m_Animations[Index].Matrices[i][m_Bones[j].Parent]);
        D3DXMatrixMultiply(Matrices[j], m_Animations[Index].Matrices[i][j], m);
        D3DXMatrixTranslation(
          m,
          m_Animations[Index].BonePositions[j].x,
          m_Animations[Index].BonePositions[j].y,
          m_Animations[Index].BonePositions[j].z
        );
        D3DXMatrixInverse(m, nil, m);
        D3DXMatrixMultiply(Matrices[j], Matrices[j], m);
        D3DXMatrixTranslation(
          m,
          -m_Animations[Index].BonePositions[j].x,
          -m_Animations[Index].BonePositions[j].y,
          -m_Animations[Index].BonePositions[j].z
        );
        D3DXMatrixInverse(m, nil, m);
        D3DXMatrixMultiply(Matrices[j], m, Matrices[j]);
      end
      else
      Move(m_Animations[Index].Matrices[i][j], Matrices[j], SizeOf(TD3DXMatrix));
    end;
    for j := 0 to High(m_Animations[Index].Matrices[0]) do
    Move(Matrices[j], m_Animations[Index].Matrices[i][j], SizeOf(TD3DXMatrix));
  end;
end;
procedure TDJXMeshEx.SetSkinned(Value: boolean);
begin
  m_Skinned := Value and (m_Header.BoneCount > 0);
end;
procedure TDJXMeshEx.SetMatrix(Index: integer; Value: TD3DXMatrix);
begin
  if Index > m_Header.BoneCount - 1 then exit;
  move(Value, m_Bones[Index].Matrix, sizeof(TD3DXMatrix));
  ComputeRenderMatrix(Index);
end;
function TDJXMeshEx.GetMatrix(Index: integer): TD3DXMatrix;
begin
  if Index > m_Header.BoneCount - 1 then exit;
  result := m_Bones[Index].Matrix;
end;
function TDJXMeshEx.GetRenderMatrix(Index: integer): TD3DXMatrix;
begin
  if Index > m_Header.BoneCount - 1 then exit;
  result := m_Bones[Index].RenderMatrix;
end;
function TDJXMeshEx.GetBone(Index: integer): PDJXMXBone;
begin
  if Index > m_Header.BoneCount - 1 then exit;
  result := @m_Bones[Index];
end;

function TDJXMeshEx.GetHeader: PDJXMXHeader;
begin
  Result := PDJXMXHeader(@m_Header);
end;

function TDJXMeshEx.GetFrameAnimation(Index: integer): PDJXMXFrameAnimation;
begin
  if (Index >= 0) and (Index <= High(m_Animations)) then
  Result := PDJXMXFrameAnimation(@m_Animations[Index]);
end;

function TDJXMeshEx.GetAnimationCount: integer;
begin
  Result := Length(m_Animations);
end;

function TDJXMeshEx.GetVertex(Index: integer): TDJXMXVertex;
begin
  Result := m_Vertices[Index];
end;

function TDJXMeshEx.GetIndex(Index: integer): DWORD;
begin
  Result := m_Indices[Index];
end;

procedure TDJXMeshEx.SetAnimation(Value: DWORD);
begin
  if Value > High(m_Animations) then
  m_CurAnim := High(m_Animations)
  else
  m_CurAnim := Value;
  m_Header.FrameCount := Length(m_Animations[m_CurAnim].Matrices);
end;

procedure TDJXMeshEx.SetFrame(Value: single);
var
  Frame1, Frame2: integer;
  grad: single;
  i: integer;
  m: TD3DXMatrix;
  mr: TD3DXQuaternion;
  ms: TD3DXVector3;
  mt: TD3DXVector3;
begin
  if m_Header.FrameCount = 0 then
  begin
    m_CurFrame := 0;
    exit;
  end
  else
  m_CurFrame := Value;
  Frame1 := trunc(Value) mod m_Header.FrameCount;
  Frame2 := (Frame1 + 1) mod m_Header.FrameCount;
  grad := Value - trunc(Value);
  for i := 0 to m_Header.BoneCount - 1 do
  MatrixInterpolate(
    m_Bones[i].Matrix,
    m_Animations[m_CurAnim].Matrices[Frame1][i],
    m_Animations[m_CurAnim].Matrices[Frame2][i],
    grad
  );
  for i := 0 to m_Header.BoneCount - 1 do
  if m_Bones[i].Parent = i then ComputeRenderMatrix(i);
end;
procedure TDJXMeshEx.SetMeshShaderConstants;
var
  //vs
  m4x4vs_WVPt: TD3DXMatrix;
  m4x4vs_Wt: TD3DXMatrix;
  v4vs_LightPos: TD3DXVector4;
  v4vs_Camera: TD3DXVector4;
  v4vs_Fog: TD3DXVector4;
  bvs_FogEnable: bool;
  bvs_Skinned: bool;
  //ps
  v4ps_LightMisc: TD3DXVector4;
  v4_LightColor: TD3DXVector4;
  v4_Ambient: TD3DXVector4;
begin
  m4x4vs_WVPt := TDanJetX(m_DanJetX).Matrices.WVPt;
  m4x4vs_Wt := TDanJetX(m_DanJetX).Matrices.Wt;
  v4vs_LightPos := D3DXVector4(m_LightPos, 0);
  v4vs_Camera := D3DXVector4(
    TDanJetX(m_DanJetX).Camera.EyePtX,
    TDanJetX(m_DanJetX).Camera.EyePtY,
    TDanJetX(m_DanJetX).Camera.EyePtZ,
    0
  );
  v4vs_Fog := D3DXVector4(
    TDanJetX(m_DanJetX).States.RenderStates.FogStart,
    TDanJetX(m_DanJetX).States.RenderStates.FogEnd,
    TDanJetX(m_DanJetX).States.RenderStates.FogEnd - TDanJetX(m_DanJetX).States.RenderStates.FogStart,
    0
  );
  bvs_FogEnable := TDanJetX(m_DanJetX).States.RenderStates.FogEnable;
  bvs_Skinned := m_Skinned;

  v4ps_LightMisc := D3DXVector4(
    Power(m_LightFalloff, 2),
    m_SpecularPower,
    m_ParalaxBias * 0.5,
    -m_ParalaxBias * 0.5
  );
  v4_LightColor := D3DXVector4(
    DJXColorR(m_LightColor) / $ff,
    DJXColorG(m_LightColor) / $ff,
    DJXColorB(m_LightColor) / $ff,
    1
  );

  v4_Ambient := D3DXVector4(
    (DJXColorR(TDanJetX(m_DanJetX).Ambient) / $ff),
    (DJXColorG(TDanJetX(m_DanJetX).Ambient) / $ff),
    (DJXColorB(TDanJetX(m_DanJetX).Ambient) / $ff),
    1
  );
  
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(0, @m4x4vs_WVPt, 4);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(4, @m4x4vs_Wt, 4);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(8, @v4vs_LightPos, 1);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(9, @v4vs_Camera, 1);
  if bvs_FogEnable then
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(10, @v4vs_Fog, 1);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(11, @v4_Ambient, 1);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(12, @v4_LightColor, 1);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantB(0, @bvs_FogEnable, 1);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantB(1, @bvs_Skinned, 1);

  TDanJetX(m_DanJetX).Device.SetPixelShaderConstantF(2, @v4ps_LightMisc, 1);
  TDanJetX(m_DanJetX).Device.SetPixelShaderConstantF(3, @v4_LightColor, 1);
  TDanJetX(m_DanJetX).Device.SetPixelShaderConstantF(4, @v4_Ambient, 1);

  if bvs_Skinned then
  SetBoneConstants(17, 20);
end;

procedure TDJXMeshEx.SetShadowShaderConstants;
var
  Wt, VPt, m: TD3DXMatrix;
  v3: TD3DXVector3;
  v4_LightPos, v4_LightDir: TD3DXVector4;
  i: integer;
begin
  Wt := TDanJetX(m_DanJetX).Matrices.Wt;
  VPt := TDanJetX(m_DanJetX).Matrices.VPt;
  v4_LightPos := D3DXVector4(m_LightPos, 1);

  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(0, @Wt, 4);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(4, @VPt, 4);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(8, @v4_LightPos, 1);

  SetBoneConstants(19, 20);
end;

procedure TDJXMeshEx.LoadFromFile(f: string);
var
  fs: TFileStream;
  Ptr: Pointer;
  i, j, x, y: integer;
  tmp: DWORD;
  m4x3: array[0..3, 0..2] of single;
begin
  if not FileExists(f) then exit;
  fs := TFileStream.Create(f, fmOpenRead);
  LoadFromStream(fs);
  fs.Free;
end;
procedure TDJXMeshEx.LoadFromStream(Stream: TStream);
var
  i, j, x, y: integer;
  tmp: DWORD;
  m4x3: array[0..3, 0..2] of single;
begin
  Release;
  // Header
  Stream.ReadBuffer(m_Header, SizeOf(m_Header));
  SetLength(m_Vertices, m_Header.VertexCount);
  SetLength(m_Indices, m_Header.IndexCount);
  SetLength(m_Bones, m_Header.BoneCount);
  m_CurAnim := AddAnimation('Default', m_Header.FrameCount, m_Header.BoneCount);

  // Vertices
  for i := 0 to m_Header.VertexCount - 1 do
  begin
    Stream.ReadBuffer(m_Vertices[i].BoneCount, sizeof(DWORD));
    Stream.ReadBuffer(m_Vertices[i].Pos, sizeof(TD3DXVector3));
    Stream.ReadBuffer(m_Vertices[i].Normal, sizeof(TD3DXVector3));
    Stream.ReadBuffer(m_Vertices[i].TexCoords, sizeof(TD3DXVector3));
    if m_Vertices[i].BoneCount > 0 then
    begin
      SetLength(m_Vertices[i].Bones, m_Vertices[i].BoneCount);
      Stream.ReadBuffer(m_Vertices[i].Bones[0], sizeof(DWORD) * m_Vertices[i].BoneCount);
      SetLength(m_Vertices[i].Weights, m_Vertices[i].BoneCount);
      Stream.ReadBuffer(m_Vertices[i].Weights[0], sizeof(single) * m_Vertices[i].BoneCount);
    end;
  end;

  // Indices
  Stream.ReadBuffer(m_Indices[0], sizeof(DWORD) * m_Header.IndexCount);

  //Bones
  for i := 0 to m_Header.BoneCount - 1 do
  begin
    Stream.ReadBuffer(m_Bones[i].ChildrenCount, sizeof(DWORD));
    Stream.ReadBuffer(tmp, sizeof(DWORD));
    SetLength(m_Bones[i].Name, tmp);
    Stream.ReadBuffer(m_Bones[i].Name[1], tmp);
    Stream.Position := Stream.Position + 1;
    Stream.ReadBuffer(m_Bones[i].Position, sizeof(TD3DXVector3));
    Stream.ReadBuffer(m_Bones[i].PositionEnd, sizeof(TD3DXVector3));
    Stream.ReadBuffer(m_Bones[i].Parent, sizeof(DWORD));
    SetLength(m_Bones[i].Children, m_Bones[i].ChildrenCount);
    Stream.ReadBuffer(m_Bones[i].Children[0], sizeof(DWORD) * m_Bones[i].ChildrenCount);
    D3DXMatrixIdentity(m_Bones[i].Matrix);
    D3DXMatrixIdentity(m_Bones[i].RenderMatrix);
  end;

  //Frames
  for i := 0 to m_Header.FrameCount - 1 do
  begin
    for j := 0 to m_Header.BoneCount - 1 do
    begin
      m_Animations[m_CurAnim].BonePositions[j] := m_Bones[j].Position;
      Stream.ReadBuffer(m4x3, sizeof(m4x3));
      D3DXMatrixIdentity(m_Animations[m_CurAnim].Matrices[i][j]);
      for x := 0 to 3 do
      for y := 0 to 2 do
      m_Animations[m_CurAnim].Matrices[i][j].m[x,y] := m4x3[x,y];
    end;
  end;
  RefineAnimation(m_CurAnim);

  m_CurFrame := 0;

  m_Skinned := m_Header.BoneCount > 0;

  LoadMeshBuffers;

  GetBounds(m_minVec, m_maxVec);
  CalculateSize;
end;

function TDJXMeshEx.AddAnimation(
      Name: string;
      FrameCount: DWORD;
      BoneCount: DWORD
    ): DWORD;
begin
  Result := Length(m_Animations);
  SetLength(m_Animations, Result + 1);
  SetLength(m_Animations[Result].BonePositions, BoneCount);
  SetLength(m_Animations[Result].Matrices, FrameCount, BoneCount);
  m_Animations[Result].Name := Name;
end;

function TDJXMeshEx.AddAnimationFromFile(
      f: string; Name: string;
      AssignByBoneNames: boolean = true;
      Transform: PD3DXMatrix = nil
    ): DWORD;
var
  fs: TFileStream;
begin
  fs := TFileStream.Create(f, fmOpenRead);
  Result := AddAnimationFromStream(fs, Name, AssignByBoneNames, Transform);
  fs.Free;
end;

function TDJXMeshEx.AddAnimationFromStream(
      s: TStream; Name: string;
      AssignByBoneNames: boolean = true;
      Transform: PD3DXMatrix = nil
    ): DWORD;
type
  TAnimationHeader = packed record
    BoneCount: DWORD;
    FrameCount: DWORD;
  end;
  TAnimationBone = packed record
    Name: string;
    Position: TD3DXVector3;
  end;
var
  AnimHeader: TAnimationHeader;
  AnimBones: array of TAnimationBone;
  i, j, x, y, b: integer;
  tmp: integer;
  m4x3: array[0..3, 0..2] of single;
  Translation: TD3DXVector3;
  Rotation: TD3DXQuaternion;
  Scale: TD3DXVector3;
  m, mi: TD3DXMatrix;
begin
  s.ReadBuffer(AnimHeader, SizeOf(AnimHeader));
  Result := AddAnimation(Name, AnimHeader.FrameCount, AnimHeader.BoneCount);
  SetLength(AnimBones, AnimHeader.BoneCount);
  for i := 0 to AnimHeader.BoneCount - 1 do
  begin
    s.Read(tmp, SizeOf(DWORD));
    SetLength(AnimBones[i].Name, tmp);
    s.Read(AnimBones[i].Name[1], tmp);
    s.Position := s.Position + 1;
    s.Read(AnimBones[i].Position, SizeOf(TD3DXVector3));
  end;
  for i := 0 to AnimHeader.BoneCount - 1 do
  begin
    if AssignByBoneNames
    and (m_Bones[i].Name <> AnimBones[i].Name) then
    begin
      tmp := -1;
      for b := 0 to m_Header.BoneCount - 1 do
      if m_Bones[b].Name = AnimBones[i].Name then
      begin
        tmp := b;
        break;
      end;
    end
    else
    tmp := i;
    if tmp > -1 then
    Move(
      AnimBones[i].Position,
      m_Animations[Result].BonePositions[tmp],
      SizeOf(TD3DXVector3)
    );
  end;
  for i := 0 to AnimHeader.FrameCount - 1 do
  for j := 0 to AnimHeader.BoneCount - 1 do
  begin
    //D3DXMatrixIdentity(m_Animations[Result].Matrices[i][j]);
    s.Read(m4x3, SizeOf(m4x3));
    if AssignByBoneNames
    and (m_Bones[j].Name <> AnimBones[j].Name) then
    begin
      tmp := -1;
      for b := 0 to m_Header.BoneCount - 1 do
      if m_Bones[b].Name = AnimBones[j].Name then
      begin
        tmp := b;
        break;
      end;
    end
    else
    tmp := j;
    if tmp > -1 then
    begin
      D3DXMatrixIdentity(m_Animations[Result].Matrices[i][tmp]);
      for x := 0 to 3 do
      for y := 0 to 2 do
      m_Animations[Result].Matrices[i][tmp].m[x, y] := m4x3[x, y];
    end;
  end;
  if Transform <> nil then
  begin
    D3DXMatrixDecompose(@Scale, @Rotation, @Translation, Transform^);
    D3DXMatrixRotationQuaternion(m, Rotation);
    D3DXMatrixInverse(mi, nil, m);
    for i := 0 to High(m_Animations[Result].BonePositions) do
    D3DXVec3TransformCoord(
      m_Animations[Result].BonePositions[i],
      m_Animations[Result].BonePositions[i],
      m
    );
    for i := 0 to High(m_Animations[Result].Matrices) do
    for j := 0 to High(m_Animations[Result].Matrices[i]) do
    begin
      D3DXMatrixMultiply(
        m_Animations[Result].Matrices[i][j],
        mi,
        m_Animations[Result].Matrices[i][j]
      );
      D3DXMatrixMultiply(
        m_Animations[Result].Matrices[i][j],
        m_Animations[Result].Matrices[i][j],
        m
      );
    end;
  end;
  RefineAnimation(Result);
end;

procedure TDJXMeshEx.RemoveAnimation(Index: integer);
var
  i, j: integer;
begin
  if (m_Animations[Index].Name = 'Default') then Exit;
  if m_CurAnim = Index then SetAnimationName('Default');
  for i := Index + 1 to High(m_Animations) do
  begin
    m_Animations[i - 1].Name := m_Animations[i].Name;
    SetLength(
      m_Animations[i - 1].BonePositions,
      Length(m_Animations[i].BonePositions)
    );
    SetLength(
      m_Animations[i - 1].Matrices,
      Length(m_Animations[i].Matrices),
      Length(m_Animations[i].Matrices[0])
    );
    Move(
      m_Animations[i].BonePositions[0],
      m_Animations[i - 1].BonePositions[0],
      Length(m_Animations[i].BonePositions) * SizeOf(TD3DXVector3)
    );
    for j := 0 to High(m_Animations[i].Matrices) do
    Move(
      m_Animations[i].Matrices[j][0],
      m_Animations[i - 1].Matrices[j][0],
      Length(m_Animations[i].Matrices[j]) * SizeOf(TD3DXMatrix)
    );
  end;
  SetLength(m_Animations, Length(m_Animations) - 1);
end;

procedure TDJXMeshEx.RemoveAnimationName(Name: string);
var
  i: integer;
begin
  for i := 0 to High(m_Animations) do
  if m_Animations[i].Name = Name then
  begin
    RemoveAnimation(i);
    Break;
  end;
end;

procedure TDJXMeshEx.SetAnimationName(Name: string);
var
  i: DWORD;
begin
  Animation := GetAnimationIndex(Name);
end;

function TDJXMeshEx.GetAnimationIndex(Name: string): DWORD;
var
  i: DWORD;
begin
  for i := 0 to High(m_Animations) do
  if m_Animations[i].Name = Name then
  begin
    Result := i;
    Break;
  end;
end;

procedure TDJXMeshEx.Render(
      AnimPos: single;
      ApplyTexture: boolean = true;
      ApplyMaterial: boolean = true
    );
begin
  Frame := AnimPos;
  Render;
end;
procedure TDJXMeshEx.Render;
var
  PrevAddressV2: DWORD;
  PrevAddressU2: DWORD;
  PrevAddressV1: DWORD;
  PrevAddressU1: DWORD;
  PrevTex0, PrevTex1, PrevTex2, PrevTex3, PrevTex4: IDirect3DBaseTexture9;
  PrevVS: IDirect3DVertexShader9;
  PrevPS: IDirect3DPixelShader9;
  VS: TDJXVertexShader;
  PS: TDJXPixelShader;
begin
  SetMeshShaderConstants;

  if Assigned(m_Mesh) then
  case m_RenderMode of
    rmBasic:
    begin
      if TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      if (m_Texture <> nil) and (PrevTex0 <> m_Texture.Texture) then m_Texture.SetToDevice(0);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXBasic_VS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);

      if (m_Texture <> nil) and (PrevTex0 <> m_Texture.Texture) then
      TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
    end;
    rmNoPSLight:
    begin
      if TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      if (m_Texture <> nil) and (PrevTex0 <> m_Texture.Texture) then m_Texture.SetToDevice(0);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXNoPSLight_VS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);

      if (m_Texture <> nil) and (PrevTex0 <> m_Texture.Texture) then
      TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
    end;
    rmNoPSLightSmooth:
    begin
      if TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      if (m_Texture <> nil) and (PrevTex0 <> m_Texture.Texture) then m_Texture.SetToDevice(0);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXNoPSLightSmooth_VS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);

      if (m_Texture <> nil) and (PrevTex0 <> m_Texture.Texture) then
      TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
    end;
    rmLight:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      if (m_Texture <> nil) and (PrevTex0 <> m_Texture.Texture) then m_Texture.SetToDevice(0);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXLight_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXLight_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if (m_Texture <> nil) and (PrevTex0 <> m_Texture.Texture) then
      TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
    end;
    rmLightSpec:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Mask.Texture then m_Mask.SetToDevice(1);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXLightSpec_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXLightSpec_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Mask.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
    end;
    rmBump:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Normals.Texture then m_Normals.SetToDevice(1);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXBump_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXBump_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Normals.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
    end;
    rmBumpSpec:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      TDanJetX(m_DanJetX).Device.GetTexture(2, PrevTex2);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Normals.Texture then m_Normals.SetToDevice(1);
      if PrevTex2 <> m_Mask.Texture then m_Mask.SetToDevice(2);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXBumpSpec_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXBumpSpec_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Normals.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
      if PrevTex2 <> m_Mask.Texture then TDanJetX(m_DanJetX).Device.SetTexture(2, PrevTex2);
    end;
    rmBumpSpecRef:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      TDanJetX(m_DanJetX).Device.GetTexture(2, PrevTex2);
      TDanJetX(m_DanJetX).Device.GetTexture(3, PrevTex3);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Normals.Texture then m_Normals.SetToDevice(1);
      if PrevTex2 <> m_Mask.Texture then m_Mask.SetToDevice(2);
      if PrevTex3 <> m_Reflection then TDanJetX(m_DanJetX).Device.SetTexture(3, m_Reflection);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXBumpSpecRef_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXBumpSpecRef_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Normals.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
      if PrevTex2 <> m_Mask.Texture then TDanJetX(m_DanJetX).Device.SetTexture(2, PrevTex2);
      if PrevTex3 <> m_Reflection then TDanJetX(m_DanJetX).Device.SetTexture(3, PrevTex3);
    end;
    rmParalaxBump:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      TDanJetX(m_DanJetX).Device.GetTexture(2, PrevTex2);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Normals.Texture then m_Normals.SetToDevice(1);
      if PrevTex2 <> m_Mask.Texture then m_Mask.SetToDevice(2);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXParalaxBump_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXParalaxBump_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);
      
      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
      if PrevTex2 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(2, PrevTex2);
    end;
    rmParalaxBumpSpec:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      TDanJetX(m_DanJetX).Device.GetTexture(2, PrevTex2);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Normals.Texture then m_Normals.SetToDevice(1);
      if PrevTex2 <> m_Mask.Texture then m_Mask.SetToDevice(2);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXParalaxBumpSpec_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXParalaxBumpSpec_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);
      
      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
      if PrevTex2 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(2, PrevTex2);
    end;
    rmParalaxBumpSpecRef:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      TDanJetX(m_DanJetX).Device.GetTexture(2, PrevTex2);
      TDanJetX(m_DanJetX).Device.GetTexture(3, PrevTex3);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Normals.Texture then m_Normals.SetToDevice(1);
      if PrevTex2 <> m_Mask.Texture then m_Mask.SetToDevice(2);
      if PrevTex3 <> m_Reflection then TDanJetX(m_DanJetX).Device.SetTexture(3, m_Reflection);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXParalaxBumpSpecRef_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXParalaxBumpSpecRef_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      m_Mesh.DrawSubset(0);

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);
      
      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
      if PrevTex2 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(2, PrevTex2);
      if PrevTex3 <> m_Reflection then TDanJetX(m_DanJetX).Device.SetTexture(3, PrevTex3);
    end;
    rmToon:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Toon.Texture then m_Toon.SetToDevice(1);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToon_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToon_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      PrevAddressU1 := TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1];
      PrevAddressV1 := TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1];
      if PrevAddressU1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1] := D3DTADDRESS_CLAMP;
      if PrevAddressV1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1] := D3DTADDRESS_CLAMP;

      m_Mesh.DrawSubset(0);

      if PrevAddressU1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1] := PrevAddressU1;
      if PrevAddressV1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1] := PrevAddressV1;

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Toon.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
    end;
    rmToonLight:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;

      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Toon.Texture then m_Toon.SetToDevice(1);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToonLight_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToonLight_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      PrevAddressU1 := TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1];
      PrevAddressV1 := TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1];
      if PrevAddressU1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1] := D3DTADDRESS_CLAMP;
      if PrevAddressV1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1] := D3DTADDRESS_CLAMP;

      m_Mesh.DrawSubset(0);

      if PrevAddressU1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1] := PrevAddressU1;
      if PrevAddressV1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1] := PrevAddressV1;

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Toon.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
    end;
    rmToonLightSpec:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;
      
      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      TDanJetX(m_DanJetX).Device.GetTexture(2, PrevTex2);
      TDanJetX(m_DanJetX).Device.GetTexture(4, PrevTex3);
      TDanJetX(m_DanJetX).Device.GetTexture(5, PrevTex4);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Toon.Texture then m_Toon.SetToDevice(1);
      if PrevTex2 <> m_Mask.Texture then m_Mask.SetToDevice(2);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToonLightSpec_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToonLightSpec_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      PrevAddressU1 := TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1];
      PrevAddressV1 := TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1];
      if PrevAddressU1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1] := D3DTADDRESS_CLAMP;
      if PrevAddressV1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1] := D3DTADDRESS_CLAMP;

      m_Mesh.DrawSubset(0);

      if PrevAddressU1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[1] := PrevAddressU1;
      if PrevAddressV1 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[1] := PrevAddressV1;

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Toon.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
      if PrevTex2 <> m_Mask.Texture then TDanJetX(m_DanJetX).Device.SetTexture(2, PrevTex2);
    end;
    rmToonBump:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;
      
      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      TDanJetX(m_DanJetX).Device.GetTexture(2, PrevTex2);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Normals.Texture then m_Normals.SetToDevice(1);
      if PrevTex2 <> m_Toon.Texture then m_Toon.SetToDevice(2);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToonBump_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToonBump_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      PrevAddressU2 := TDanJetX(m_DanJetX).States.SamplerStates.AddressU[2];
      PrevAddressV2 := TDanJetX(m_DanJetX).States.SamplerStates.AddressV[2];
      if PrevAddressU2 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[2] := D3DTADDRESS_CLAMP;
      if PrevAddressV2 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[2] := D3DTADDRESS_CLAMP;

      m_Mesh.DrawSubset(0);

      if PrevAddressU2 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[2] := PrevAddressU2;
      if PrevAddressV2 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[2] := PrevAddressV2;

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Normals.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
      if PrevTex2 <> m_Toon.Texture then TDanJetX(m_DanJetX).Device.SetTexture(2, PrevTex2);
    end;
    rmToonBumpSpec:
    begin
      if (TDanJetX(m_DanJetX).DeviceCaps.VertexShaderVersion < D3DVS_VERSION(2, 0))
      or (TDanJetX(m_DanJetX).DeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) then
      exit;
      
      TDanJetX(m_DanJetX).Device.GetTexture(0, PrevTex0);
      TDanJetX(m_DanJetX).Device.GetTexture(1, PrevTex1);
      TDanJetX(m_DanJetX).Device.GetTexture(2, PrevTex2);
      TDanJetX(m_DanJetX).Device.GetTexture(3, PrevTex3);
      if PrevTex0 <> m_Texture.Texture then m_Texture.SetToDevice(0);
      if PrevTex1 <> m_Normals.Texture then m_Normals.SetToDevice(1);
      if PrevTex2 <> m_Toon.Texture then m_Toon.SetToDevice(2);
      if PrevTex3 <> m_Mask.Texture then m_Mask.SetToDevice(3);

      TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
      TDanJetX(m_DanJetX).Device.GetPixelShader(PrevPS);
      VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToonBumpSpec_VS'));
      PS := TDJXPixelShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXToonBumpSpec_PS'));
      if PrevVS <> VS.Shader then VS.SetToDevice;
      if PrevPS <> PS.Shader then PS.SetToDevice;

      PrevAddressU2 := TDanJetX(m_DanJetX).States.SamplerStates.AddressU[2];
      PrevAddressV2 := TDanJetX(m_DanJetX).States.SamplerStates.AddressV[2];
      if PrevAddressU2 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[2] := D3DTADDRESS_CLAMP;
      if PrevAddressV2 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[2] := D3DTADDRESS_CLAMP;

      m_Mesh.DrawSubset(0);

      if PrevAddressU2 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressU[2] := PrevAddressU2;
      if PrevAddressV2 <> D3DTADDRESS_CLAMP then
      TDanJetX(m_DanJetX).States.SamplerStates.AddressV[2] := PrevAddressV2;

      if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
      if PrevPS <> PS.Shader then TDanJetX(m_DanJetX).Device.SetPixelShader(PrevPS);

      if PrevTex0 <> m_Texture.Texture then TDanJetX(m_DanJetX).Device.SetTexture(0, PrevTex0);
      if PrevTex1 <> m_Normals.Texture then TDanJetX(m_DanJetX).Device.SetTexture(1, PrevTex1);
      if PrevTex2 <> m_Toon.Texture then TDanJetX(m_DanJetX).Device.SetTexture(2, PrevTex2);
      if PrevTex3 <> m_Mask.Texture then TDanJetX(m_DanJetX).Device.SetTexture(3, PrevTex3);
    end;
  end;
end;
procedure TDJXMeshEx.RenderShadow(Extrusion: single; ShadowColor: TDJXColor; RenderVolume: boolean = false);
var
  v4: TD3DXVector4;
  m: TD3DXMatrix;
  prev_zbuffer: boolean;
  prev_alphatest: boolean;
  prev_srcblend: DWORD;
  prev_destblend: DWORD;
  prev_zfunc: DWORD;
  decl: TFVFDeclaration;
  PrevVS: IDirect3DVertexShader9;
  VS: TDJXVertexShader;
begin
  SetShadowShaderConstants;
  v4 := D3DXVector4(Extrusion, 0.0, 0, 0);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(9, @v4, 1);

  VS := TDJXVertexShader(TDJXRender(TDanJetX(m_DanJetX).Render).Shaders.Find('DJXShadow_VS'));
  TDanJetX(m_DanJetX).Device.GetVertexShader(PrevVS);
  if PrevVS <> VS.Shader then VS.SetToDevice;
  
  if RenderVolume then
  begin
    TDanJetX(m_DanJetX).CullMode := cullCW;
    m_ShadowRender.DrawSubset(0);
    TDanJetX(m_DanJetX).CullMode := cullCCW;
  end
  else
  begin
    TDanJetX(m_DanJetX).States.RenderStates.DepthBias := 0.00001;
    prev_zfunc := TDanJetX(m_DanJetX).States.RenderStates.ZFunc;
    prev_zbuffer := TDanJetX(m_DanJetX).Zbuffer;
    prev_alphatest := TDanJetX(m_DanJetX).States.RenderStates.AlphaTestEnable;
    prev_srcblend := TDanJetX(m_DanJetX).States.RenderStates.SrcBlend;
    prev_destblend := TDanJetX(m_DanJetX).States.RenderStates.DestBlend;
    if TDanJetX(m_DanJetX).DeviceCaps.StencilCaps and D3DSTENCILCAPS_TWOSIDED > 0 then
    begin
      TDanJetX(m_DanJetX).ClearStencil;
      TDanJetX(m_DanJetX).Zbuffer := true;
      TDanJetX(m_DanJetX).States.RenderStates.AlphaTestEnable := false;
      TDanJetX(m_DanJetX).States.RenderStates.ZWriteEnable := false;
      TDanJetX(m_DanJetX).States.RenderStates.TwoSidedStencilMode := true;
      TDanJetX(m_DanJetX).States.RenderStates.StencilEnable := true;
      TDanJetX(m_DanJetX).States.RenderStates.SrcBlend := D3DBLEND_ZERO;
      TDanJetX(m_DanJetX).States.RenderStates.DestBlend := D3DBLEND_ONE;
      TDanJetX(m_DanJetX).States.RenderStates.ShadeMode := D3DSHADE_FLAT;
      TDanJetX(m_DanJetX).States.RenderStates.StencilFunc := D3DCMP_ALWAYS;
      TDanJetX(m_DanJetX).States.RenderStates.CCW_StencilFunc := D3DCMP_ALWAYS;
      TDanJetX(m_DanJetX).States.RenderStates.StencilRef := 1;
      TDanJetX(m_DanJetX).States.RenderStates.CCW_StencilPass := D3DSTENCILOP_KEEP;
      TDanJetX(m_DanJetX).States.RenderStates.CCW_StencilFail := D3DSTENCILOP_KEEP;
      TDanJetX(m_DanJetX).States.RenderStates.CCW_StencilZFail := D3DSTENCILOP_INCR;
      TDanJetX(m_DanJetX).States.RenderStates.StencilPass := D3DSTENCILOP_KEEP;
      TDanJetX(m_DanJetX).States.RenderStates.StencilFail := D3DSTENCILOP_KEEP;
      TDanJetX(m_DanJetX).States.RenderStates.StencilZFail := D3DSTENCILOP_DECR;
      TDanJetX(m_DanJetX).CullMode := cullNone;
      m_ShadowRender.DrawSubset(0);
      TDanJetX(m_DanJetX).States.RenderStates.TwoSidedStencilMode := false;
    end
    else
    begin
      TDanJetX(m_DanJetX).ClearStencil;
      TDanJetX(m_DanJetX).Zbuffer := true;
      TDanJetX(m_DanJetX).States.RenderStates.AlphaTestEnable := false;
      TDanJetX(m_DanJetX).States.RenderStates.ZWriteEnable := false;
      TDanJetX(m_DanJetX).States.RenderStates.StencilEnable := true;
      TDanJetX(m_DanJetX).States.RenderStates.SrcBlend := D3DBLEND_ZERO;
      TDanJetX(m_DanJetX).States.RenderStates.DestBlend := D3DBLEND_ONE;
      TDanJetX(m_DanJetX).States.RenderStates.ShadeMode := D3DSHADE_FLAT;
      TDanJetX(m_DanJetX).States.RenderStates.StencilFunc := D3DCMP_ALWAYS;
      TDanJetX(m_DanJetX).States.RenderStates.StencilRef := 1;
      TDanJetX(m_DanJetX).States.RenderStates.StencilPass := D3DSTENCILOP_KEEP;
      TDanJetX(m_DanJetX).States.RenderStates.StencilFail := D3DSTENCILOP_KEEP;
      TDanJetX(m_DanJetX).States.RenderStates.StencilZFail := D3DSTENCILOP_INCRSAT;
      TDanJetX(m_DanJetX).CullMode := cullCCW;
      m_ShadowRender.DrawSubset(0);
      TDanJetX(m_DanJetX).States.RenderStates.StencilZFail := D3DSTENCILOP_DECRSAT;
      TDanJetX(m_DanJetX).CullMode := cullCW;
      m_ShadowRender.DrawSubset(0);
    end;
    TDanJetX(m_DanJetX).States.RenderStates.SrcBlend := prev_srcblend;
    TDanJetX(m_DanJetX).States.RenderStates.DestBlend := prev_destblend;
    TDanJetX(m_DanJetX).CullMode := cullCCW;
    TDanJetX(m_DanJetX).States.RenderStates.StencilPass := D3DSTENCILOP_KEEP;
    TDanJetX(m_DanJetX).States.RenderStates.StencilFail := D3DSTENCILOP_KEEP;
    TDanJetX(m_DanJetX).States.RenderStates.StencilZFail := D3DSTENCILOP_KEEP;
    TDanJetX(m_DanJetX).States.RenderStates.StencilFunc := D3DCMP_NOTEQUAL;
    TDanJetX(m_DanJetX).States.RenderStates.StencilRef := 0;
    TDanJetX(m_DanJetX).Zbuffer := false;
    TDanJetX(m_DanJetX).Primitives2D.Rectangle(
      0,0,
      TDanJetX(m_DanJetX).ViewPort.Width,
      TDanJetX(m_DanJetX).ViewPort.Height,
      ShadowColor
    );
    TDanJetX(m_DanJetX).States.RenderStates.StencilEnable := false;
    TDanJetX(m_DanJetX).States.RenderStates.ZWriteEnable := true;
    TDanJetX(m_DanJetX).States.RenderStates.ShadeMode := D3DSHADE_GOURAUD;
    TDanJetX(m_DanJetX).States.RenderStates.StencilFunc := D3DCMP_ALWAYS;
    TDanJetX(m_DanJetX).States.RenderStates.AlphaTestEnable := prev_alphatest;
    TDanJetX(m_DanJetX).Zbuffer := prev_zbuffer;
    TDanJetX(m_DanJetX).States.RenderStates.ZFunc := prev_zfunc;
    TDanJetX(m_DanJetX).States.RenderStates.DepthBias := 0.0;
  end;
  if PrevVS <> VS.Shader then TDanJetX(m_DanJetX).Device.SetVertexShader(PrevVS);
end;
procedure TDJXMeshEx.Release;
begin
  SetLength(m_Vertices, 0);
  SetLength(m_Indices, 0);
  SetLength(m_Bones, 0);
  SetLength(m_Animations, 0);
  safe_release(m_Mesh);
  safe_release(m_Shadow);
end;
procedure TDJXMeshEx.GetBounds(out minVec, maxVec: TD3DXVector3);
var
  pVertices: Pointer;
  Decl: TFVFDeclaration;
  i: integer;
begin
  if not Assigned(m_Mesh) then
  begin
    if m_Header.BoneCount > 0 then
    begin
      MinVec := m_Bones[0].Position;
      MaxVec := m_Bones[0].Position;
      for i := 1 to m_Header.BoneCount - 1 do
      begin
        if m_Bones[i].Position.x < MinVec.x then MinVec.x := m_Bones[i].Position.x;
        if m_Bones[i].Position.y < MinVec.y then MinVec.y := m_Bones[i].Position.y;
        if m_Bones[i].Position.z < MinVec.z then MinVec.z := m_Bones[i].Position.z;
        if m_Bones[i].Position.x > MaxVec.x then MaxVec.x := m_Bones[i].Position.x;
        if m_Bones[i].Position.y > MaxVec.y then MaxVec.y := m_Bones[i].Position.y;
        if m_Bones[i].Position.z > MaxVec.z then MaxVec.z := m_Bones[i].Position.z;
      end;
    end
    else
    begin
      MinVec := D3DXVector3(0, 0, 0);
      MaxVec := D3DXVector3(0, 0, 0);
    end;
  end
  else
  begin
    m_Mesh.GetDeclaration(Decl);
    m_Mesh.LockVertexBuffer(D3DLOCK_READONLY, pVertices);
    D3DXComputeBoundingBox(
      PD3DXVector3(pVertices),
      m_Mesh.GetNumVertices,
      D3DXGetDeclVertexSize(@Decl[0], 0),
      MinVec,
      MaxVec
    );
    m_Mesh.UnlockVertexBuffer;
  end;
end;
function TDJXMeshEx.IsPicked(var dist: single; var HitPos: TD3DXVector3): boolean;
type
  PMeshVertexArr = ^TMeshVertexArr;
  TMeshVertexArr = array [0..0] of TD3DXVector3;
var
  i, j: integer;
  matInverse: TD3DXMatrix;
  Orig, Dir: TD3DXVector3;
  hit: longbool;
  TmpMesh: ID3DXMesh;
  MeshVertexArr: PMeshVertexArr;
  Ptr: Pointer;
  v: TD3DXVector3;
begin
  D3DXMatrixInverse(matInverse, nil, TDJXRender(TDanJetX(m_DanJetX).Render).MatWorld^);

  TDanJetX(m_DanJetX).Camera.MousePosToVector(Orig, Dir);

  D3DXVec3TransformCoord(Orig, Orig, matInverse);
  D3DXVec3TransformNormal(Dir, Dir, matInverse);
  D3DXVec3Normalize(Dir, Dir);

  if m_Skinned then
  begin
    D3DXCreateMeshFVF(
      m_Header.IndexCount div 3,
      m_Header.VertexCount,
      D3DXMESH_SYSTEMMEM or D3DXMESH_32BIT,
      D3DFVF_XYZ,
      TDanJetX(m_DanJetX).Device,
      TmpMesh
    );
    TmpMesh.LockVertexBuffer(D3DLOCK_DISCARD, Pointer(MeshVertexArr));
    for i := 0 to m_Header.VertexCount - 1 do
    begin
      MeshVertexArr^[i] := D3DXVector3(0, 0, 0);
      for j := 0 to 3 do
      if length(m_Vertices[i].Bones) > j then
      begin
        D3DXVec3TransformCoord(
          v,
          m_Vertices[i].Pos,
          m_Bones[m_Vertices[i].Bones[j]].RenderMatrix
        );
        v.x := v.x * m_Vertices[i].Weights[j];
        v.y := v.y * m_Vertices[i].Weights[j];
        v.z := v.z * m_Vertices[i].Weights[j];
        MeshVertexArr^[i].x := MeshVertexArr^[i].x + v.x;
        MeshVertexArr^[i].y := MeshVertexArr^[i].y + v.y;
        MeshVertexArr^[i].z := MeshVertexArr^[i].z + v.z;
      end;
    end;
    TmpMesh.UnlockVertexBuffer;
    TmpMesh.LockIndexBuffer(D3DLOCK_DISCARD, Ptr);
    move(m_Indices[0], Ptr^, m_Header.IndexCount * sizeof(DWORD));
    TmpMesh.UnlockIndexBuffer;
    D3DXIntersect(
      TmpMesh,
      Orig,
      Dir,
      hit,
      nil, nil, nil,
      @dist,
      nil, nil
    );
    safe_release(TmpMesh);
  end
  else
  D3DXIntersect(
    m_Mesh,
    Orig,
    Dir,
    hit,
    nil, nil, nil,
    @dist,
    nil, nil
  );

  if hit then
  HitPos := D3DXVector3(Orig.x + Dir.x * dist, Orig.y + Dir.y * dist, Orig.z + Dir.z * dist)
  else
  HitPos := D3DXVector3(0, 0, 0);

  result := hit;
end;
function TDJXMeshEx.MatrixIndex(Name: string): integer;
var
  i: integer;
begin
  result := -1;
  for i := 0 to m_Header.BoneCount - 1 do
  if lowercase(m_Bones[i].Name) = lowercase(Name) then
  begin
    result := i;
    exit;
  end;
end;
procedure TDJXMeshEx.UpdateRenderMatrices;
var
  i: integer;
begin
  for i := 0 to m_Header.BoneCount - 1 do
  if m_Bones[i].Parent = i then
  ComputeRenderMatrix(i);
end;
procedure TDJXMeshEx.InterpolateFrames(Frame1, Frame2: single; Grad: single);
var
  i: integer;
  f1g1, f1g2, f2g1, f2g2: DWORD;
  g1, g2: single;
  f1m: TD3DXMatrix;
  f2m: TD3DXMatrix;
begin
  f1g1 := Trunc(Frame1) mod m_Header.FrameCount;
  f1g2 := (f1g1 + 1) mod m_Header.FrameCount;
  f2g1 := Trunc(Frame2) mod m_Header.FrameCount;
  f2g2 := (f2g1 + 1) mod m_Header.FrameCount;
  g1 := Frac(Frame1);
  g2 := Frac(Frame2);
  m_CurFrame := Frame1 - Round(Grad) * (Frame1 + Frame2);
  for i := 0 to m_Header.BoneCount - 1 do
  begin
    MatrixInterpolate(
      f1m,
      m_Animations[m_CurAnim].Matrices[f1g1][i],
      m_Animations[m_CurAnim].Matrices[f1g2][i],
      g1
    );
    MatrixInterpolate(
      f2m,
      m_Animations[m_CurAnim].Matrices[f2g1][i],
      m_Animations[m_CurAnim].Matrices[f2g2][i],
      g2
    );
    MatrixInterpolate(
      m_Bones[i].Matrix,
      f1m,
      f2m,
      Grad
    );
  end;
  for i := 0 to m_Header.BoneCount - 1 do
  if m_Bones[i].Parent = i then ComputeRenderMatrix(i);
end;

procedure TDJXMeshEx.InterpolateAnimations(
      Anim1: string; Frame1: single;
      Anim2: string; Frame2: single;
      Grad: single
    );
var
  i: integer;
  A1, A2: DWORD;
  f1g1, f1g2, f2g1, f2g2: DWORD;
  g1, g2: single;
  f1m: TD3DXMatrix;
  f2m: TD3DXMatrix;
begin
  A1 := GetAnimationIndex(Anim1);
  A2 := GetAnimationIndex(Anim2);
  f1g1 := Trunc(Frame1) mod Length(m_Animations[A1].Matrices);
  f1g2 := (f1g1 + 1) mod Length(m_Animations[A1].Matrices);
  f2g1 := Trunc(Frame2) mod Length(m_Animations[A2].Matrices);
  f2g2 := (f2g1 + 1) mod Length(m_Animations[A2].Matrices);
  g1 := Frac(Frame1);
  g2 := Frac(Frame2);
  for i := 0 to m_Header.BoneCount - 1 do
  begin
    MatrixInterpolate(
      f1m,
      m_Animations[A1].Matrices[f1g1][i],
      m_Animations[A1].Matrices[f1g2][i],
      g1
    );
    MatrixInterpolate(
      f2m,
      m_Animations[A2].Matrices[f2g1][i],
      m_Animations[A2].Matrices[f2g2][i],
      g2
    );
    MatrixInterpolate(
      m_Bones[i].Matrix,
      f1m,
      f2m,
      Grad
    );
  end;
  for i := 0 to m_Header.BoneCount - 1 do
  if m_Bones[i].Parent = i then ComputeRenderMatrix(i);
end;

procedure TDJXMeshEx.TransformMesh(const m: TD3DXMatrix);
type
  PMeshVertex = ^TMeshVertex;
  TMeshVertex = packed record
    Position: TD3DXVector3;
    Normal: TD3DXVector3;
    Tangent: TD3DXVector3;
    Binormal: TD3DXVector3;
    TexCoords: TD3DXVector3;
    BoneWeights: TD3DXVector4;
    BoneIndices: TD3DXVector4;
  end;
  PMeshVertexArr = ^TMeshVertexArr;
  TMeshVertexArr = array[0..0] of TMeshVertex;
var
  VArr: PMeshVertexArr;
  i: integer;
begin
  for i := 0 to m_Header.VertexCount - 1 do
  begin
    D3DXVec3TransformCoord(m_Vertices[i].Pos, m_Vertices[i].Pos, m);
    D3DXVec3TransformNormal(m_Vertices[i].Normal, m_Vertices[i].Normal, m);
  end;
  m_Mesh.LockVertexBuffer(0, Pointer(VArr));
  for i := 0 to m_Mesh.GetNumVertices - 1 do
  begin
    D3DXVec3TransformCoord(Varr^[i].Position, Varr^[i].Position, m);
    D3DXVec3TransformNormal(Varr^[i].Normal, Varr^[i].Normal, m);
    D3DXVec3TransformNormal(Varr^[i].Tangent, Varr^[i].Tangent, m);
    D3DXVec3TransformNormal(Varr^[i].Binormal, Varr^[i].Binormal, m);
  end;
  m_Mesh.UnlockVertexBuffer;
  for i := 0 to m_Header.BoneCount - 1 do
  begin
    D3DXVec3TransformCoord(m_Bones[i].Position, m_Bones[i].Position, m);
    D3DXVec3TransformCoord(m_Bones[i].PositionEnd, m_Bones[i].PositionEnd, m);
  end;
end;
procedure TDJXMeshEx.SetBoneConstants(FirstRegister: DWORD; FirstBoneConstant: DWORD = 20);
var
  FirstMatrix: TD3DXVector4;
  i: integer;
  m: TD3DXMatrix;
begin
  FirstMatrix := D3DXVector4(FirstBoneConstant, 3, 0, 0);
  TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(FirstRegister, @FirstMatrix, 1);
  for i := 0 to m_Header.BoneCount - 1 do
  begin
    D3DXMatrixTranspose(m, m_Bones[i].RenderMatrix);
    TDanJetX(m_DanJetX).Device.SetVertexShaderConstantF(
      round(FirstMatrix.x) + round(FirstMatrix.y) * i, @m,
      round(FirstMatrix.y)
    );
  end;
end;
procedure TDJXMeshEx.GenerateShadow;
begin
  LoadShadowBuffers;
  UpdateShadow;
end;
procedure TDJXMeshEx.UpdateShadow;
type
  PMeshVertex = ^TMeshVertex;
  TMeshVertex = packed record
    Position: TD3DXVector3;
    Normal: TD3DXVector3;
    Weights: TD3DXVector4;
    Indices: TD3DXVector4;
  end;
  PMeshVertexArr = ^TMeshVertexArr;
  TMeshVertexArr = array[0..0] of TMeshVertex;
  PShadowVertex = ^TShadowVertex;
  TShadowVertex = packed record
    Position: TD3DXVector3;
    Normal: TD3DXVector3;
  end;
  PShadowVertexArr = ^TShadowVertexArr;
  TShadowVertexArr = array[0..0] of TShadowVertex;
var
  ShadowVertexArrPtr: PShadowVertexArr;
  MeshVertexArrPtr: PMeshVertexArr;
  i, j, VCount, FCount: integer;
  v: TD3DXVector3;
  DWORDArrPtr: PDWordArray;
begin
  m_Shadow.LockVertexBuffer(D3DLOCK_READONLY, Pointer(MeshVertexArrPtr));
  m_ShadowRender.LockVertexBuffer(D3DLOCK_DISCARD, Pointer(ShadowVertexArrPtr));
  VCount := m_Shadow.GetNumVertices;
  for i := 0 to VCount - 1 do
  begin
    if m_Skinned then
    begin
      ShadowVertexArrPtr^[i].Position := D3DXVector3(0, 0, 0);
      for j := 0 to 3 do
      if psingle(cardinal(@(MeshVertexArrPtr^[i].Weights.x)) + j * sizeof(single))^ > 0 then
      begin
        D3DXVec3TransformCoord(
          v,
          MeshVertexArrPtr^[i].Position,
          m_Bones[round(psingle(cardinal(@(MeshVertexArrPtr^[i].Indices.x)) + j * sizeof(single))^)].RenderMatrix
        );
        v.x := v.x * psingle(cardinal(@(MeshVertexArrPtr^[i].Weights.x)) + j * sizeof(single))^;
        v.y := v.y * psingle(cardinal(@(MeshVertexArrPtr^[i].Weights.x)) + j * sizeof(single))^;
        v.z := v.z * psingle(cardinal(@(MeshVertexArrPtr^[i].Weights.x)) + j * sizeof(single))^;
        ShadowVertexArrPtr^[i].Position.x := ShadowVertexArrPtr^[i].Position.x + v.x;
        ShadowVertexArrPtr^[i].Position.y := ShadowVertexArrPtr^[i].Position.y + v.y;
        ShadowVertexArrPtr^[i].Position.z := ShadowVertexArrPtr^[i].Position.z + v.z;
      end;
    end
    else
    begin
      ShadowVertexArrPtr^[i].Position.x := MeshVertexArrPtr^[i].Position.x;
      ShadowVertexArrPtr^[i].Position.y := MeshVertexArrPtr^[i].Position.y;
      ShadowVertexArrPtr^[i].Position.z := MeshVertexArrPtr^[i].Position.z;
    end;
  end;
  m_Shadow.LockIndexBuffer(D3DLOCK_READONLY, Pointer(DWORDArrPtr));
  FCount := m_ShadowRender.GetNumFaces;
  for i := 0 to FCount - 1 do
  begin
    v := GetTriangleNormal(
      ShadowVertexArrPtr^[DWORDArrPtr^[i * 3 + 0]].Position,
      ShadowVertexArrPtr^[DWORDArrPtr^[i * 3 + 1]].Position,
      ShadowVertexArrPtr^[DWORDArrPtr^[i * 3 + 2]].Position
    );
    if D3DXVec3Length(v) > 0 then
    begin
      move(v, ShadowVertexArrPtr^[DWORDArrPtr^[i * 3 + 0]].Normal, sizeof(TD3DXVector3));
      move(v, ShadowVertexArrPtr^[DWORDArrPtr^[i * 3 + 1]].Normal, sizeof(TD3DXVector3));
      move(v, ShadowVertexArrPtr^[DWORDArrPtr^[i * 3 + 2]].Normal, sizeof(TD3DXVector3));
    end;
  end;
  m_Shadow.UnlockIndexBuffer;
  m_ShadowRender.UnlockVertexBuffer;
  m_Shadow.UnlockVertexBuffer; 
end;
//TDJXMeshEx END

//TDJXMeshStatic BEGIN
constructor TDJXMeshStatic.Create(Name: string; DanJetX: Pointer);
begin
  inherited Create(Name, DanJetX);
  m_ShadowVolume := TDJXShadowVolume.Create(DanJetX, Self);
  m_MatCount := 0;
end;
destructor TDJXMeshStatic.Destroy;
begin
  FreeAndNil(m_ShadowVolume);
  inherited Destroy;
end;

procedure TDJXMeshStatic.SetDefaultMaterial;
var
  i: integer;
  mat: TD3DMaterial9;
begin
  ZeroMemory(@mat, sizeof(mat));
  mat.Diffuse.r := 1;
  mat.Diffuse.g := 1;
  mat.Diffuse.b := 1;
  mat.Diffuse.a := 1;
  mat.Ambient.r := 1;
  mat.Ambient.g := 1;
  mat.Ambient.b := 1;
  mat.Ambient.a := 1;
  if m_MatCount = 0 then
  begin
    if (m_MeshMaterials <> nil) then
    begin
      FreeMem(m_MeshMaterials);
      Pointer(m_MeshMaterials) := nil;
    end;
    m_MatCount := 1;
    GetMem(m_MeshMaterials, SizeOf(TD3DMaterial9) * m_MatCount);
    if m_MeshTextures = nil then
    begin
      GetMem(m_MeshTextures, SizeOf(IDirect3DTexture9) * m_MatCount);
      ZeroMemory(m_MeshTextures, SizeOf(IDirect3DTexture9) * m_MatCount);
    end;
  end;
  for i := 0 to m_MatCount - 1 do
  m_MeshMaterials[i] := mat;
end;

procedure TDJXMeshStatic.LoadFromFile(FileName: string);
var
  MtrlBuffer: ID3DXBuffer;
  AdjacencyBuffer: ID3DXBuffer;
  Materials: PDJXMaterialArray;
  i: integer;
begin
  Release;

  if FAILED(
    D3DXLoadMeshFromX(
      Pchar(fileName),
      D3DXMESH_MANAGED,
      TDanJetX(m_DanJetX).Device,
      @AdjacencyBuffer,
      @MtrlBuffer,
      nil,
      @m_MatCount,
      m_Mesh
    )
  )
  then
  exit;
  m_Mesh.OptimizeInplace(
    D3DXMESHOPT_ATTRSORT or D3DXMESHOPT_COMPACT
    or D3DXMESHOPT_VERTEXCACHE,
    PDWORD(AdjacencyBuffer.GetBufferPointer), nil, nil, nil
  );     

  Materials := MtrlBuffer.GetBufferPointer;
  GetMem(m_MeshMaterials, SizeOf(TD3DMaterial9) * m_MatCount);
  GetMem(m_MeshTextures, SizeOf(IDirect3DTexture9) * m_MatCount);
  ZeroMemory(m_MeshTextures, SizeOf(IDirect3DTexture9) * m_MatCount);

  i := 0;
  while (i < m_MatCount) do
  begin
    m_MeshMaterials[i] := Materials[i].MatD3D;
    m_MeshMaterials[i].Ambient := m_MeshMaterials[i].Diffuse;
    m_MeshTextures[i] := nil;
    Inc(i);
  end;

  safe_release(MtrlBuffer);
  GetBounds(m_minVec, m_maxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.LoadFromStream(Stream: TStream);
var
  MtrlBuffer: ID3DXBuffer;
  Materials: PDJXMaterialArray;
  i: integer;
begin
  Release;

  if FAILED(
    D3DXLoadMeshFromXInMemory(
      TMemoryStream(Stream).Memory,
      Stream.Size,
      D3DXMESH_MANAGED,
      TDanJetX(m_DanJetX).Device,
      nil,
      @MtrlBuffer,
      nil,
      @m_MatCount,
      m_Mesh
    )
  )
  then
  exit;
  Materials := MtrlBuffer.GetBufferPointer;
  GetMem(m_MeshMaterials, SizeOf(TD3DMaterial9) * m_MatCount);
  GetMem(m_MeshTextures, SizeOf(IDirect3DTexture9) * m_MatCount);
  ZeroMemory(m_MeshTextures, SizeOf(IDirect3DTexture9) * m_MatCount);

  i := 0;
  while (i < m_MatCount) do
  begin
    m_MeshMaterials[i] := Materials[i].MatD3D;
    m_MeshMaterials[i].Ambient := m_MeshMaterials[i].Diffuse;
    m_MeshTextures[i] := nil;
    Inc(i);
  end;

  safe_release(MtrlBuffer);
  GetBounds(m_minVec, m_maxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.Render(
      AnimPos: single;
      ApplyTexture: boolean = true;
      ApplyMaterial: boolean = true
    );
var
  i: integer;
  PrevMtrl: TD3DMaterial9;
begin
  TDanJetX(m_DanJetX).Device.GetMaterial(prevMtrl);
  if m_MatCount = 0 then
  m_Mesh.DrawSubset(0)
  else
  for i := 0 to m_MatCount - 1 do
  begin
    if ApplyMaterial then TDanJetX(m_DanJetX).Device.SetMaterial(m_MeshMaterials[i]);
    if ApplyTexture then TDanJetX(m_DanJetX).Device.SetTexture(0, m_MeshTextures[i]);
    m_Mesh.DrawSubset(i);
  end;
  TDanJetX(m_DanJetX).Device.SetMaterial(prevMtrl);
end;
procedure TDJXMeshStatic.Release;
var
  i: integer;
begin
  if (m_MeshMaterials <> nil) then
  begin
    FreeMem(m_MeshMaterials);
    Pointer(m_MeshMaterials) := nil;
  end;

  if (m_MeshTextures <> nil)  then
  begin
    i := 0;
    while(i < m_MatCount) do
    begin
      safe_release(m_MeshTextures[i]);
      Inc(i);
    end;
    FreeMem(m_MeshTextures);
    Pointer(m_MeshTextures) := nil;
  end;

  Safe_Release(m_Mesh);
end;
procedure TDJXMeshStatic.GetBounds(out minVec, maxVec: TD3DXVector3);
var
  pVertices: Pointer;
begin
  m_Mesh.LockVertexBuffer(D3DLOCK_READONLY, pVertices);
  D3DXComputeBoundingBox(
    PD3DXVector3(pVertices),
    m_Mesh.GetNumVertices,
    D3DXGetFVFVertexSize(m_Mesh.GetFVF),
    MinVec,
    MaxVec
  );
  m_Mesh.UnlockVertexBuffer;
end;
procedure TDJXMeshStatic.SetTexture(Texture: TDJXTexture);
var
  i: integer;
begin
  for i := 0 to m_MatCount - 1 do
  begin
    if Texture = nil then
    m_MeshTextures[i] := nil
    else
    m_MeshTextures[i] := Texture.Texture;
  end;
end;
procedure TDJXMeshStatic.SetMaterial(Material: TDJXMaterial);
var
  i: integer;
begin
  for i := 0 to m_MatCount - 1 do
  if Material <> nil then
  m_MeshMaterials[i] := Material.Material;
end;
procedure TDJXMeshStatic.SetMaterialAlpha(Alpha: byte);
var
  i: integer;
  FloatAlpha: single;
begin
  FloatAlpha := Alpha / 255;
  for i := 0 to m_MatCount - 1 do
  begin
    m_MeshMaterials[i].Diffuse.a := FloatAlpha;
    m_MeshMaterials[i].Ambient.a := FloatAlpha;
    m_MeshMaterials[i].Specular.a := FloatAlpha;
    m_MeshMaterials[i].Emissive.a := FloatAlpha;
  end;
end;
function TDJXMeshStatic.IsPicked(var dist: single; var HitPos: TD3DXVector3): boolean;
var
  matInverse: TD3DXMatrix;
  Orig, Dir: TD3DXVector3;
  hit: longbool;
begin
  D3DXMatrixInverse(matInverse, nil, TDJXRender(TDanJetX(m_DanJetX).Render).MatWorld^);

  TDanJetX(m_DanJetX).Camera.MousePosToVector(Orig, Dir);

  D3DXVec3TransformCoord(Orig, Orig, matInverse);
  D3DXVec3TransformNormal(Dir, Dir, matInverse);
  D3DXVec3Normalize(Dir, Dir);

  D3DXIntersect(
    m_Mesh,
    Orig,
    Dir,
    hit,
    nil, nil, nil,
    @dist,
    nil, nil
  );

  if hit then
  HitPos := D3DXVector3(Orig.x + Dir.x * dist, Orig.y + Dir.y * dist, Orig.z + Dir.z * dist)
  else
  HitPos := D3DXVector3(0, 0, 0);

  result := hit;
end;
procedure TDJXMeshStatic.Convert(FVF: DWORD);
var
  TempMesh: ID3DXMesh;
begin
  if FAILED(
    m_Mesh.CloneMeshFVF(
      m_Mesh.GetOptions, FVF, TDanJetX(m_DanJetX).Device, TempMesh
    )
  )
  then exit;
  safe_release(m_Mesh);
  m_Mesh := TempMesh;
  safe_release(TempMesh);
end;
procedure TDJXMeshStatic.Convert(pDeclaration: PD3DVertexElement9);
var
  TempMesh: ID3DXMesh;
begin
  if FAILED(
    m_Mesh.CloneMesh(
      m_Mesh.GetOptions, pDeclaration, TDanJetX(m_DanJetX).Device, TempMesh
    )
  )
  then exit;
  safe_release(m_Mesh);
  m_Mesh := TempMesh;
  safe_release(TempMesh);
end;
procedure TDJXMeshStatic.ComputeNormals;
begin
  D3DXComputeNormals(m_Mesh, nil);
end;
procedure TDJXMeshStatic.Update;
begin
  GetBounds(m_MinVec, m_MaxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.MakeBox(Width, Height, Depth: single);
begin
  Release;
  D3DXCreateBox(
    TDanJetX(m_DanJetX).Device,
    Width, Height, Depth,
    m_Mesh,
    nil
  );
  SetDefaultMaterial;
  GetBounds(m_MinVec, m_MaxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.MakeCylinder(Radius1, Radius2, Length: Single; Slices, Stacks: LongWord);
begin
  Release;
  D3DXCreateCylinder(
    TDanJetX(m_DanJetX).Device,
    Radius1, Radius2, Length, Slices, Stacks,
    m_Mesh,
    nil
  );
  SetDefaultMaterial;
  GetBounds(m_MinVec, m_MaxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.MakePolygon(Length: Single; Sides: LongWord);
begin
  Release;
  D3DXCreatePolygon(
    TDanJetX(m_DanJetX).Device,
    Length, Sides,
    m_Mesh,
    nil
  );
  SetDefaultMaterial;
  GetBounds(m_MinVec, m_MaxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.MakeSphere(Radius: Single; Slices, Stacks: LongWord);
begin
  Release;
  D3DXCreateSphere(
    TDanJetX(m_DanJetX).Device,
    Radius, Slices, Stacks,
    m_Mesh,
    nil
  );
  SetDefaultMaterial;
  GetBounds(m_MinVec, m_MaxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.MakeTorus(InnerRadius, OuterRadius: Single; Sides, Rings: LongWord);  
begin
  Release;
  D3DXCreateTorus(
    TDanJetX(m_DanJetX).Device,
    InnerRadius, OuterRadius, Sides, Rings,
    m_Mesh,
    nil
  );
  SetDefaultMaterial;
  GetBounds(m_MinVec, m_MaxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.MakeTeapot;                                               
begin
  Release;
  D3DXCreateTeapot(
    TDanJetX(m_DanJetX).Device,
    m_Mesh,
    nil
  );
  SetDefaultMaterial;
  GetBounds(m_MinVec, m_MaxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.MakeText(Text: string; FontName: WideString; Bold, Italic: Boolean; Extrusion: Single);
var
  DC: hDC;
  Font: HFont;
  FontOld: HFont;
  fHeight: integer;
  fWeight: WORD;
begin
  Release;
  DC := CreateCompatibleDC(0);

  fHeight := -MulDiv(32, GetDeviceCaps(DC, LOGPIXELSY), 72);
  if Bold then fWeight := FW_BOLD else fWeight := FW_NORMAL;

  Font := CreateFontW(
    fHeight, 0, 0, 0, fWeight, Cardinal(Italic), iFalse, iFalse, DEFAULT_CHARSET,
    OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
    DEFAULT_PITCH or FF_DONTCARE, PWideChar(FontName)
  );

  FontOld := SelectObject(DC, Font);

  D3DXCreateText(
    TDanJetX(m_DanJetX).Device,
    DC,
    PChar(Text),
    0.001,
    Extrusion,
    m_Mesh,
    nil,
    nil
  );
  SelectObject(DC, FontOld);
  DeleteObject(Font);
  DeleteDC(DC);

  SetDefaultMaterial;
  GetBounds(m_MinVec, m_MaxVec);
  CalculateSize;
end;
procedure TDJXMeshStatic.MakeMesh(NumFaces, NumVertices, Options, FVF: DWORD);
begin
  Release;
  D3DXCreateMeshFVF(
    NumFaces,
    NumVertices,
    Options,
    FVF,
    TDanJetX(m_DanJetX).Device,
    m_Mesh
  );
  SetDefaultMaterial;
end;
//TDJXMeshStatic END

//TDJXMeshSkinned BEGIN
constructor TDJXMeshSkinned.Create(Name: string; DanJetX: Pointer);
begin
  inherited Create(Name, DanJetX);
end;
destructor TDJXMeshSkinned.Destroy;
begin
  inherited Destroy;
end;

procedure TDJXMeshSkinned.DrawFrame(pFrame: PD3DXFrame);
var
  pMeshContainer: PD3DXMeshContainer;
begin
  pMeshContainer := pFrame.pMeshContainer;
  while (pMeshContainer <> nil) do
  begin
    DrawMeshContainer(pMeshContainer, pFrame);
    pMeshContainer := pMeshContainer.pNextMeshContainer;
  end;
  if (pFrame.pFrameSibling <> nil) then DrawFrame(pFrame.pFrameSibling);
  if (pFrame.pFrameFirstChild <> nil) then DrawFrame(pFrame.pFrameFirstChild);
end;
procedure TDJXMeshSkinned.DrawMeshContainer(pMeshContainerBase: PD3DXMeshContainer; pFrameBase: PD3DXFrame);
var
  pMeshContainer: PDJXMeshContainerDerived;
  pFrame: PDJXFrameDerived;
  i, iMaterial, iAttrib: Integer;
  pBoneComb: PD3DXBoneCombination;
  iMatrixIndex: LongWord;
  NumBlend: LongWord;
  AttribIdPrev: DWORD;
  matTemp: TD3DXMatrixA16;
  d3dCaps: TD3DCaps9;
begin
  pMeshContainer := PDJXMeshContainerDerived(pMeshContainerBase);
  pFrame := PDJXFrameDerived(pFrameBase);
  TDanJetX(m_DanJetX).Device.GetDeviceCaps(d3dCaps);
  if pMeshContainer = nil then exit;
  if (pMeshContainer.pSkinInfo <> nil) then
  begin
    begin
      AttribIdPrev := UNUSED32;
      pBoneComb := PD3DXBoneCombination(pMeshContainer.pBoneCombinationBuf.GetBufferPointer);
      for iAttrib := 0 to pMeshContainer.NumAttributeGroups - 1 do
      begin
        NumBlend := 0;
        for i := 0 to pMeshContainer.NumInfl - 1 do
        begin
          if (pBoneComb.BoneId[i] <> UINT_MAX) then NumBlend := i;
        end;
        if (d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1) then
        begin
          for i := 0 to pMeshContainer.NumInfl - 1 do
          begin
            iMatrixIndex := pBoneComb.BoneId[i];
            if (iMatrixIndex <> UINT_MAX) then
            begin
              D3DXMatrixMultiply(matTemp, pMeshContainer.pBoneOffsetMatrices[iMatrixIndex], pMeshContainer.ppBoneMatrixPtrs[iMatrixIndex]^);
              TDanJetX(m_DanJetX).Device.SetTransform(D3DTS_WORLDMATRIX(i), matTemp);
            end;
          end;
          TDanJetX(m_DanJetX).Device.SetRenderState(D3DRS_VERTEXBLEND, NumBlend);
          if (AttribIdPrev <> pBoneComb.AttribId) or (AttribIdPrev = UNUSED32) then
          begin
            TDanJetX(m_DanJetX).Device.SetMaterial(pMeshContainer.pMaterials[pBoneComb.AttribId].MatD3D);
            TDanJetX(m_DanJetX).Device.SetTexture(0, pMeshContainer.ppTextures[pBoneComb.AttribId]);
            AttribIdPrev := pBoneComb.AttribId;
          end;
          ID3DXMesh(pMeshContainer.MeshData.pMesh).DrawSubset(iAttrib);
        end;
        Inc(pBoneComb);
      end;
      pBoneComb := PD3DXBoneCombination(pMeshContainer.pBoneCombinationBuf.GetBufferPointer);
      if (pMeshContainer.iAttributeSW < pMeshContainer.NumAttributeGroups) then
      begin
        AttribIdPrev := UNUSED32;
        TDanJetX(m_DanJetX).Device.SetSoftwareVertexProcessing(True);
        for iAttrib := pMeshContainer.iAttributeSW to pMeshContainer.NumAttributeGroups - 1 do
        begin
          NumBlend := 0;
          for i := 0 to pMeshContainer.NumInfl - 1 do
          begin
            if (pBoneComb.BoneId[i] <> UINT_MAX) then NumBlend := i;
          end;
          if (d3dCaps.MaxVertexBlendMatrices < NumBlend + 1) then
          begin
            for i := 0 to pMeshContainer.NumInfl - 1do
            begin
              iMatrixIndex := pBoneComb.BoneId[i];
              if (iMatrixIndex <> UINT_MAX) then
              begin
                D3DXMatrixMultiply(matTemp, pMeshContainer.pBoneOffsetMatrices[iMatrixIndex], pMeshContainer.ppBoneMatrixPtrs[iMatrixIndex]^);
                TDanJetX(m_DanJetX).Device.SetTransform(D3DTS_WORLDMATRIX(i), matTemp);
              end;
            end;
            TDanJetX(m_DanJetX).Device.SetRenderState(D3DRS_VERTEXBLEND, NumBlend);
            if (AttribIdPrev <> pBoneComb.AttribId) or (AttribIdPrev = UNUSED32) then
            begin
              TDanJetX(m_DanJetX).Device.SetMaterial(pMeshContainer.pMaterials[pBoneComb.AttribId].MatD3D);
              TDanJetX(m_DanJetX).Device.SetTexture(0, pMeshContainer.ppTextures[pBoneComb.AttribId]);
              AttribIdPrev := pBoneComb.AttribId;
            end;
            ID3DXMesh(pMeshContainer.MeshData.pMesh).DrawSubset(iAttrib);
          end;

          Inc(pBoneComb);
        end;
        TDanJetX(m_DanJetX).Device.SetSoftwareVertexProcessing(False);
      end;

      TDanJetX(m_DanJetX).Device.SetRenderState(D3DRS_VERTEXBLEND, 0);
    end;
  end
  else
  begin
    TDanJetX(m_DanJetX).Device.SetTransform(D3DTS_WORLD, pFrame.CombinedTransformationMatrix);
    for iMaterial := 0 to pMeshContainer.NumMaterials - 1 do
    begin
      TDanJetX(m_DanJetX).Device.SetMaterial(pMeshContainer.pMaterials[iMaterial].MatD3D);
      TDanJetX(m_DanJetX).Device.SetTexture(0, pMeshContainer.ppTextures[iMaterial]);
      ID3DXMesh(pMeshContainer.MeshData.pMesh).DrawSubset(iMaterial);
    end;
  end;
end;
function TDJXMeshSkinned.SetupBoneMatrixPointersOnMesh(pMeshContainerBase: PD3DXMeshContainer): HRESULT;
var
  iBone, cBones: Integer;
  pFrame: PDJXFrameDerived;
  pMeshContainer: PDJXMeshContainerDerived;
begin
  pMeshContainer := PDJXMeshContainerDerived(pMeshContainerBase);

  if (pMeshContainer.pSkinInfo <> nil) then
  begin
    cBones := pMeshContainer.pSkinInfo.GetNumBones;

    try
      GetMem(pMeshContainer.ppBoneMatrixPtrs, SizeOf(PD3DXMatrix) * cBones);
    except
      Result := E_OUTOFMEMORY;
      Exit;
    end;

    for iBone := 0 to cBones - 1 do
    begin
      pFrame := PDJXFrameDerived(D3DXFrameFind(m_HFrame, pMeshContainer.pSkinInfo.GetBoneName(iBone)));
      if (pFrame = nil) then
      begin
        Result := E_FAIL;
        Exit;
      end;

      pMeshContainer.ppBoneMatrixPtrs[iBone] := @pFrame.CombinedTransformationMatrix;
    end;
  end;

  Result := S_OK;
end;
function TDJXMeshSkinned.SetupBoneMatrixPointers(pFrame: PD3DXFrame): HRESULT;
begin
  if (pFrame.pMeshContainer <> nil) then
  begin
    Result := SetupBoneMatrixPointersOnMesh(pFrame.pMeshContainer);
    if FAILED(Result) then Exit;
  end;

  if (pFrame.pFrameSibling <> nil) then
  begin
    Result := SetupBoneMatrixPointers(pFrame.pFrameSibling);
    if FAILED(Result) then Exit;
  end;

  if (pFrame.pFrameFirstChild <> nil) then
  begin
    Result := SetupBoneMatrixPointers(pFrame.pFrameFirstChild);
    if FAILED(Result) then Exit;
  end;

  Result := S_OK;
end;
procedure TDJXMeshSkinned.UpdateFrameMatrices(
      pFrameBase: PD3DXFrame;
      pParentMatrix: PD3DXMatrix
    );
var
  pFrame: PDJXFrameDerived;
begin
  pFrame := PDJXFrameDerived(pFrameBase);

  if (pParentMatrix <> nil)
  then D3DXMatrixMultiply(pFrame.CombinedTransformationMatrix, pFrame.TransformationMatrix, pParentMatrix^)
  else pFrame.CombinedTransformationMatrix := pFrame.TransformationMatrix;

  if Assigned(pFrame.pFrameSibling)
  then UpdateFrameMatrices(pFrame.pFrameSibling, pParentMatrix);

  if Assigned(pFrame.pFrameFirstChild)
  then UpdateFrameMatrices(pFrame.pFrameFirstChild, @pFrame.CombinedTransformationMatrix);
end;

procedure TDJXMeshSkinned.LoadFromFile(FileName: string);
var
  Alloc: TDJXMeshHierarchy;
begin
  Release;
  Alloc := TDJXMeshHierarchy.Create;
  if SUCCEEDED(
    D3DXLoadMeshHierarchyFromX(
      PChar(Filename),
      D3DXMESH_MANAGED,
      TDanJetX(m_DanJetX).Device,
      Alloc,
      nil,
      m_HFrame,
      m_AnimationController
    )
  )
  then
  begin
    SetupBoneMatrixPointers(m_HFrame);
    D3DXFrameCalculateBoundingSphere(m_HFrame, m_ObjectCenter, m_ObjectRadius);
    GetBounds(m_MinVec, m_MaxVec);
    CalculateSize;
  end;
  Alloc.Free;
end;
procedure TDJXMeshSkinned.LoadFromStream(Stream: TStream);
var
  Alloc: TDJXMeshHierarchy;
begin
  Release;
  Alloc := TDJXMeshHierarchy.Create;
  if SUCCEEDED(
    D3DXLoadMeshHierarchyFromXInMemory(
      TMemoryStream(Stream).Memory,
      Stream.Size,
      D3DXMESH_MANAGED,
      TDanJetX(m_DanJetX).Device,
      Alloc,
      nil,
      m_HFrame,
      m_AnimationController
    )
  )
  then
  begin
    SetupBoneMatrixPointers(m_HFrame);
    D3DXFrameCalculateBoundingSphere(m_HFrame, m_ObjectCenter, m_ObjectRadius);
    GetBounds(m_MinVec, m_MaxVec);
    CalculateSize;
  end;
  Alloc.Free;
end;
procedure TDJXMeshSkinned.Render(
      AnimPos: single;
      ApplyTexture: boolean = true;
      ApplyMaterial: boolean = true
    );
var
  PrevMtrl: TD3DMaterial9;
begin
  TDanJetX(m_DanJetX).Device.GetMaterial(prevMtrl);
  m_AnimationController.SetTrackPosition(0, animPos * (0.0335));
  m_AnimationController.AdvanceTime(0, nil);
  UpdateFrameMatrices(m_HFrame, @TDJXRender(TDanJetX(m_DanJetX).Render).MatWorld^);
  DrawFrame(m_HFrame);
  TDanJetX(m_DanJetX).Device.SetMaterial(prevMtrl);
end;
procedure TDJXMeshSkinned.Release;
  procedure ReleaseAttributeTable(pFrameBase: PD3DXFrame);
  var
    pFrame: PDJXFrameDerived;
    pMeshContainer: PDJXMeshContainerDerived;
  begin
    pFrame := PDJXFrameDerived(pFrameBase);

    pMeshContainer := PDJXMeshContainerDerived(pFrame.pMeshContainer);

    while (pMeshContainer <> nil) do
    begin
      pMeshContainer.pAttributeTable:= nil;

      pMeshContainer := PDJXMeshContainerDerived(pMeshContainer.pNextMeshContainer);
    end;

    if (pFrame.pFrameSibling <> nil) then
    begin
      ReleaseAttributeTable(pFrame.pFrameSibling);
    end;

    if (pFrame.pFrameFirstChild <> nil) then
    begin
      ReleaseAttributeTable(pFrame.pFrameFirstChild);
    end;
  end;
var
  Alloc: TDJXMeshHierarchy;
begin
  if m_HFrame <> nil then
  begin
    ReleaseAttributeTable(m_HFrame.pFrameFirstChild);
    Alloc := TDJXMeshHierarchy.Create;
    D3DXFrameDestroy(m_HFrame, Alloc);
    safe_release(m_AnimationController);
    Alloc.Free;
  end;
end;
procedure TDJXMeshSkinned.GetBounds(out minVec, maxVec: TD3DXVector3);
var
  FirstCalc: boolean;
  procedure CalculateContainersBounds(Frame: PD3DXFrame);
  var
    pFrame: PD3DXFrame;
    pMeshContainer: PDJXMeshContainerDerived;
    SubMinVec,SubMaxVec: TD3DXVector3;
    mesh: ID3DXMesh;
    pVertices: Pointer;
  begin
    pFrame := frame;
    pMeshContainer := PDJXMeshContainerDerived(pFrame.pMeshContainer);
    while (pMeshContainer <> nil) do
    begin
      if pMeshContainer.MeshData._Type = D3DXMESHTYPE_MESH then
      begin
        mesh := ID3DXMesh(pMeshContainer.MeshData.pMesh);
        mesh.LockVertexBuffer(D3DLOCK_READONLY, pVertices);
        D3DXComputeBoundingBox(
          PD3DXVector3(pVertices),
          mesh.GetNumVertices,
          D3DXGetFVFVertexSize(mesh.GetFVF),
          SubMinVec,
          SubMaxVec
        );
        mesh.UnlockVertexBuffer;
        if FirstCalc then
        begin
          move(SubMinVec, MinVec, sizeof(TD3DXVector3));
          move(SubMaxVec, MaxVec, sizeof(TD3DXVector3));
          FirstCalc := false;
        end
        else
        begin
          if SubMinVec.x < minVec.x then minVec.x := SubMinVec.x;
          if SubMinVec.y < minVec.y then minVec.y := SubMinVec.y;
          if SubMinVec.z < minVec.z then minVec.z := SubMinVec.z;
          if SubMaxVec.x < MaxVec.x then MaxVec.x := SubMaxVec.x;
          if SubMaxVec.y < MaxVec.y then MaxVec.y := SubMaxVec.y;
          if SubMaxVec.z < MaxVec.z then MaxVec.z := SubMaxVec.z;
        end;
      end;
      pMeshContainer := PDJXMeshContainerDerived(pMeshContainer.pNextMeshContainer);
    end;
    if (pFrame.pFrameSibling <> nil) then CalculateContainersBounds(pFrame.pFrameSibling);
    if (pFrame.pFrameFirstChild <> nil) then CalculateContainersBounds(pFrame.pFrameFirstChild);
  end;
begin
  FirstCalc := true;
  CalculateContainersBounds(m_HFrame);
end;
procedure TDJXMeshSkinned.SetTexture(Texture: TDJXTexture);
  procedure SetTextureToContainers(Frame: PD3DXFrame);
  var
    pFrame: PD3DXFrame;
    pMeshContainer: PDJXMeshContainerDerived;
    i: integer;
    Tex: IDirect3DTexture9;
  begin
    if Texture = nil then
    Tex := nil
    else
    Tex := Texture.Texture;
    pFrame := frame;
    pMeshContainer := PDJXMeshContainerDerived(pFrame.pMeshContainer);
    while (pMeshContainer <> nil) do
    begin
      for i := 0 to pMeshContainer.NumMaterials - 1 do
      pMeshContainer.ppTextures[i] := Tex;
      pMeshContainer := PDJXMeshContainerDerived(pMeshContainer.pNextMeshContainer);
    end;
    if (pFrame.pFrameSibling <> nil) then SetTextureToContainers(pFrame.pFrameSibling);
    if (pFrame.pFrameFirstChild <> nil) then SetTextureToContainers(pFrame.pFrameFirstChild);
  end;
begin
  SetTextureToContainers(m_HFrame);
end;
procedure TDJXMeshSkinned.SetMaterial(Material: TDJXMaterial);
  procedure SetMaterialToContainers(Frame: PD3DXFrame);
  var
    pFrame: PD3DXFrame;
    pMeshContainer: PDJXMeshContainerDerived;
    i: integer;
  begin
    pFrame := frame;
    pMeshContainer := PDJXMeshContainerDerived(pFrame.pMeshContainer);
    while (pMeshContainer <> nil) do
    begin
      for i := 0 to pMeshContainer.NumMaterials - 1 do
      pMeshContainer.pMaterials[i].MatD3D := Material.Material;
      pMeshContainer := PDJXMeshContainerDerived(pMeshContainer.pNextMeshContainer);
    end;
    if (pFrame.pFrameSibling <> nil) then SetMaterialToContainers(pFrame.pFrameSibling);
    if (pFrame.pFrameFirstChild <> nil) then SetMaterialToContainers(pFrame.pFrameFirstChild);
  end;
begin
  SetMaterialToContainers(m_HFrame);
end;
procedure TDJXMeshSkinned.SetMaterialAlpha(Alpha: byte);
var
  FloatAlpha: single;
  procedure SetMaterialToContainers(Frame: PD3DXFrame);
  var
    pFrame: PD3DXFrame;
    pMeshContainer: PDJXMeshContainerDerived;
    i: integer;
  begin
    pFrame := Frame;
    pMeshContainer := PDJXMeshContainerDerived(pFrame.pMeshContainer);
    while (pMeshContainer <> nil) do
    begin
      for i := 0 to pMeshContainer.NumMaterials - 1 do
      begin
        pMeshContainer.pMaterials[i].MatD3D.Diffuse.a := FloatAlpha;
        pMeshContainer.pMaterials[i].MatD3D.Ambient.a := FloatAlpha;
        pMeshContainer.pMaterials[i].MatD3D.Specular.a := FloatAlpha;
        pMeshContainer.pMaterials[i].MatD3D.Emissive.a := FloatAlpha;
      end;
      pMeshContainer := PDJXMeshContainerDerived(pMeshContainer.pNextMeshContainer);
    end;
    if (pFrame.pFrameSibling <> nil) then SetMaterialToContainers(pFrame.pFrameSibling);
    if (pFrame.pFrameFirstChild <> nil) then SetMaterialToContainers(pFrame.pFrameFirstChild);
  end;
begin
  FloatAlpha := Alpha / 255;
  SetMaterialToContainers(m_HFrame);
end;
//TDJXMeshSkinned END

//TDJXShadowVolume BEGIN
constructor TDJXShadowVolume.Create(DanJetX: Pointer; Mesh: TDJXMeshStatic);
begin
  inherited Create;
  m_DJX := DanJetX;
  m_Shadow := TDJXVB3D.Create(DanJetX);
  m_Mesh := Mesh;
end;
destructor TDJXShadowVolume.Destroy;
begin
  m_Shadow.Free;
  while length(m_ContEdges)>0 do
  FreeAndNil(m_ContEdges[0]);
  inherited Destroy;
end;

function TDJXShadowVolume.GenerateEdges: boolean;
var
  NumFaces: cardinal;
  NumEdges: cardinal;
  VB: IDirect3DVertexBuffer9;
  IB: IDirect3DIndexBuffer9;
  IBDesc: TD3DIndexBufferDesc;
  Index16Ptr: PIndex16Arr;
  Index32Ptr: PIndex32Arr;
  IndexPtr: Pointer;
  i, j: integer;
  BPV: cardinal;
  VBPtr: Pointer;
  Ind1, Ind2, Ind3: DWORD;
  Pos1, Pos2, Pos3: TD3DXVector3;
  Normal: TD3DXVector3;
  function GetVertexPos(Index: DWORD): TD3DXVector3;
  begin
    result.x := PD3DXVector3(Pointer(cardinal(VBPtr)+BPV*Index))^.x;
    result.y := PD3DXVector3(Pointer(cardinal(VBPtr)+BPV*Index))^.y;
    result.z := PD3DXVector3(Pointer(cardinal(VBPtr)+BPV*Index))^.z;
  end;
  function CompareVectors(v1, v2: TD3DXVector3): boolean;
  begin
    result := (v1.x = v2.x) and (v1.y = v2.y) and (v1.z = v2.z);
  end;
begin
  result := false;
  NumFaces := m_Mesh.Mesh.GetNumFaces;
  NumEdges := NumFaces*3;
  SetLength(m_Edges, NumEdges);
  SetLength(m_EdgesMod, NumEdges);

  m_Mesh.Mesh.GetVertexBuffer(VB);
  m_Mesh.Mesh.GetIndexBuffer(IB);
  IB.GetDesc(IBDesc);
  if IBDesc.Format = D3DFMT_INDEX16 then
  begin
    IB.Lock(0, NumEdges*2, Pointer(Index16Ptr), 0);
    GetIndex := GetIndex16;
    IndexPtr := Index16Ptr;
  end
  else
  if IBDesc.Format = D3DFMT_INDEX32 then
  begin
    IB.Lock(0, NumEdges*4, Pointer(Index32Ptr), 0);
    GetIndex := GetIndex32;
    IndexPtr := Index32Ptr;
  end
  else
  exit;
  BPV := m_Mesh.Mesh.GetNumBytesPerVertex;
  VB.Lock(0, m_Mesh.Mesh.GetNumVertices*BPV, VBPtr, 0);

  for i:=0 to NumFaces-1 do
  begin
    Ind1 := GetIndex(IndexPtr,i*3+0);
    Ind2 := GetIndex(IndexPtr,i*3+1);
    Ind3 := GetIndex(IndexPtr,i*3+2);
    Pos1 := GetVertexPos(Ind1);
    Pos2 := GetVertexPos(Ind2);
    Pos3 := GetVertexPos(Ind3);
    Normal := GetTriangleNormal(Pos1, Pos2, Pos3);

    Move(Pos1, m_Edges[i*3 + 0].v1, sizeof(TD3DXVector3));
    Move(Pos2, m_Edges[i*3 + 0].v2, sizeof(TD3DXVector3));
    Move(Normal, m_Edges[i*3 + 0].n, sizeof(TD3DXVector3));
    m_Edges[i*3 + 0].opposite := -1;

    Move(Pos2, m_Edges[i*3 + 1].v1, sizeof(TD3DXVector3));
    Move(Pos3, m_Edges[i*3 + 1].v2, sizeof(TD3DXVector3));
    Move(Normal, m_Edges[i*3 + 1].n, sizeof(TD3DXVector3));
    m_Edges[i*3 + 1].opposite := -1;

    Move(Pos3, m_Edges[i*3 + 2].v1, sizeof(TD3DXVector3));
    Move(Pos1, m_Edges[i*3 + 2].v2, sizeof(TD3DXVector3));
    Move(Normal, m_Edges[i*3 + 2].n, sizeof(TD3DXVector3));
    m_Edges[i*3 + 2].opposite := -1;
  end;

  for i:=0 to NumEdges-1 do
  begin
    for j:=0 to NumEdges-1 do
    if i<>j then
    begin
      if
      (
        CompareVectors(m_Edges[i].v1, m_Edges[j].v1)
        and
        CompareVectors(m_Edges[i].v2, m_Edges[j].v2)
      )
      or
      (
        CompareVectors(m_Edges[i].v1, m_Edges[j].v2)
        and
        CompareVectors(m_Edges[i].v2, m_Edges[j].v1)
      ) then
      m_Edges[i].opposite := j;
    end;
  end;

  VB.Unlock;
  IB.Unlock;

  m_Shadow.initVB(NumEdges*4,D3DUSAGE_WRITEONLY);
  m_Shadow.initIB(NumEdges*6,D3DUSAGE_WRITEONLY);

  result := true;
end;
procedure TDJXShadowVolume.GenerateShadow(Light: TD3DXVector3; Distance: single);
var
  CurEdge: integer;
  procedure AddEdge(Edge: TEdge);
  var
    LightVec1, LightVec2: TD3DXVector3;
    ShadowPos1, ShadowPos2: TD3DXVector3;
  const
    col = $88000000;
    procedure AddPlane(v1,v2,v3,v4: TD3DXVector3);
    begin
      m_Shadow.Vertices[CurEdge*4 + 0] := DJXVertex3D(v1.x, v1.y, v1.z, col);
      m_Shadow.Vertices[CurEdge*4 + 1] := DJXVertex3D(v2.x, v2.y, v2.z, col);
      m_Shadow.Vertices[CurEdge*4 + 2] := DJXVertex3D(v3.x, v3.y, v3.z, col);
      m_Shadow.Vertices[CurEdge*4 + 3] := DJXVertex3D(v4.x, v4.y, v4.z, col);
      m_Shadow.Indices[CurEdge*6 + 0] := CurEdge*4 + 0;
      m_Shadow.Indices[CurEdge*6 + 1] := CurEdge*4 + 1;
      m_Shadow.Indices[CurEdge*6 + 2] := CurEdge*4 + 3;
      m_Shadow.Indices[CurEdge*6 + 3] := CurEdge*4 + 0;
      m_Shadow.Indices[CurEdge*6 + 4] := CurEdge*4 + 3;
      m_Shadow.Indices[CurEdge*6 + 5] := CurEdge*4 + 2;
    end;
  begin
    D3DXVec3Subtract(LightVec1, Edge.v1, Light);
    D3DXVec3Subtract(LightVec2, Edge.v2, Light);
    D3DXVec3Normalize(LightVec1, LightVec1);
    D3DXVec3Normalize(LightVec2, LightVec2);
    ShadowPos1 := D3DXVector3(Edge.v1.x + LightVec1.x * Distance, Edge.v1.y + LightVec1.y * Distance, Edge.v1.z + LightVec1.z * Distance);
    ShadowPos2 := D3DXVector3(Edge.v2.x + LightVec2.x * Distance, Edge.v2.y + LightVec2.y * Distance, Edge.v2.z + LightVec2.z * Distance);
    AddPlane(Edge.v1, Edge.v2, ShadowPos1, ShadowPos2);
    inc(CurEdge);
  end;
var
  LightVec: TD3DXVector3;
  MidEdge: TD3DXVector3;
  i: integer;
begin
  CurEdge := 0;
  for i := 0 to high(m_Edges) do
  begin
    D3DXVec3TransformCoord(m_EdgesMod[i].v1, m_Edges[i].v1, TDJXRender(TDanJetX(m_DJX).Render).MatWorld^);
    D3DXVec3TransformCoord(m_EdgesMod[i].v2, m_Edges[i].v2, TDJXRender(TDanJetX(m_DJX).Render).MatWorld^);
    D3DXVec3TransformNormal(m_EdgesMod[i].n, m_Edges[i].n, TDJXRender(TDanJetX(m_DJX).Render).MatWorld^);
    m_EdgesMod[i].opposite := m_Edges[i].opposite;
    D3DXVec3Normalize(m_EdgesMod[i].n,m_EdgesMod[i].n);
  end;
  for i := 0 to high(m_Edges) do
  begin
    MidEdge := D3DXVector3(
      (m_EdgesMod[i].v1.x + m_EdgesMod[i].v2.x) / 2,
      (m_EdgesMod[i].v1.y + m_EdgesMod[i].v2.y) / 2,
      (m_EdgesMod[i].v1.z + m_EdgesMod[i].v2.z) / 2
    );
    D3DXVec3Subtract(LightVec, MidEdge, Light);
    D3DXVec3Normalize(LightVec, LightVec);
    if (D3DXVec3Dot(m_EdgesMod[i].n, LightVec) > 0)
    and
    (
      (m_EdgesMod[i].opposite = -1)
      or
      (D3DXVec3Dot(m_EdgesMod[m_EdgesMod[i].opposite].n, LightVec) <= 0)
    ) then
    AddEdge(m_EdgesMod[i]);
  end;
  m_Shadow.setVertices(0, CurEdge * 4);
  m_Shadow.setIndices(0, CurEdge * 6);
  m_EdgeCount := CurEdge;
end;

procedure TDJXShadowVolume.Render(ShadowColor: TDJXColor);
var
  prev_zbuffer: boolean;
  prev_alphatest: boolean;
  prev_srcblend: dword;
  prev_destblend: dword;
begin
  prev_zbuffer := TDanJetX(m_DJX).Zbuffer;
  prev_alphatest := TDanJetX(m_DJX).States.RenderStates.AlphaTestEnable;
  prev_srcblend := TDanJetX(m_DJX).States.RenderStates.SrcBlend;
  prev_destblend := TDanJetX(m_DJX).States.RenderStates.DestBlend;
  TDanJetX(m_DJX).ClearStencil;
  TDanJetX(m_DJX).Zbuffer := true;
  TDanJetX(m_DJX).States.RenderStates.AlphaTestEnable := false;
  TDanJetX(m_DJX).States.RenderStates.ZWriteEnable := false;
  TDanJetX(m_DJX).States.RenderStates.StencilEnable := true;
  TDanJetX(m_DJX).States.RenderStates.ShadeMode := D3DSHADE_FLAT;
  TDanJetX(m_DJX).States.RenderStates.StencilFunc := D3DCMP_ALWAYS;
  TDanJetX(m_DJX).States.RenderStates.StencilRef := 1;
  TDanJetX(m_DJX).States.RenderStates.StencilPass := D3DSTENCILOP_INCRSAT;
  TDanJetX(m_DJX).CullMode := cullCCW;
  TDanJetX(m_DJX).States.RenderStates.SrcBlend := D3DBLEND_ZERO;
  TDanJetX(m_DJX).States.RenderStates.DestBlend := D3DBLEND_ONE;
  m_Shadow.DrawIndexedTriangleList(0, m_EdgeCount * 2);
  TDanJetX(m_DJX).States.RenderStates.StencilPass := D3DSTENCILOP_DECRSAT;
  TDanJetX(m_DJX).CullMode := cullCW;
  m_Shadow.DrawIndexedTriangleList(0, m_EdgeCount * 2);
  TDanJetX(m_DJX).States.RenderStates.SrcBlend := prev_srcblend;
  TDanJetX(m_DJX).States.RenderStates.DestBlend := prev_destblend;
  TDanJetX(m_DJX).CullMode := cullCCW;
  TDanJetX(m_DJX).States.RenderStates.StencilPass := D3DSTENCILOP_KEEP;
  TDanJetX(m_DJX).States.RenderStates.StencilFunc := D3DCMP_LESSEQUAL;
  TDanJetX(m_DJX).States.RenderStates.StencilRef := 1;
  TDanJetX(m_DJX).Zbuffer := false;
  TDanJetX(m_DJX).Primitives2D.Rectangle(
    0,0,
    TDanJetX(m_DJX).ViewPort.Width,
    TDanJetX(m_DJX).ViewPort.Height,
    ShadowColor
  );
  TDanJetX(m_DJX).States.RenderStates.StencilEnable := false;
  TDanJetX(m_DJX).States.RenderStates.ZWriteEnable := true;
  TDanJetX(m_DJX).States.RenderStates.ShadeMode := D3DSHADE_GOURAUD;
  TDanJetX(m_DJX).States.RenderStates.StencilFunc := D3DCMP_ALWAYS;
  TDanJetX(m_DJX).States.RenderStates.AlphaTestEnable := prev_alphatest;
  TDanJetX(m_DJX).Zbuffer := prev_zbuffer;
end;
procedure TDJXShadowVolume.RenderVolume;
begin
  m_Shadow.DrawIndexedTriangleList(0, m_EdgeCount * 2);
end;
//TDJXShadowVolume END



end.
