{***************************************************************
  Delphi DirectX 8.1
  (c) Mega Informatic 2006-2007

  Расширение возможностей стандартных классов и типов
  необходимых в разработке игры

  unit DX8Mesh.pas -
    Работа с простыми и анимированными мешами (Meshes)

  Обновления смотри на сайтах -

  http://www.megainformatic.boom.ru
  http://www.megainformaticsite.pochta.ru
//  модификации кода данного модуля: 8/02/2007 - 9/02/2007,
    15/10/2007

    Содержимое модуля:

    - TD3D8Mesh производный от CD3DMesh класс
    для работы со статичными мешами

    - TD3DAnimatedMesh - класс для объектной и скелетной анимации
      - SDrawElement, SFrame, SMeshContainer - вспомогательные классы
        используемые данным классом.

    пояснения по работе см. ниже    

****************************************************************}




unit DX8Mesh;

interface

uses
  Windows, SysUtils, Direct3D8, D3DX8, DXUtil, D3DFile, DXFile, DXErr8,
  DX8CommonTypes;

const
  Unitname = 'DX8Mesh.pas'; 


type
  PVertex = ^TVertex;
  TVertex = packed record
    vPos: TD3DXVector3;
    vNorm: TD3DXVector3;
    fTex: array[0..1] of Single;
  end;

const
  D3DFVF_MESHVERTEX = (D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_TEX1);

type
  { Класс расширенного меша для облегчения вывода объектов
    с улучшенными характеристиками: текстурирование,
    доступность вершинного и индексного буферов и т.п.
  }

  TD3D8Mesh = class(CD3DMesh)
  private
    FPD3DDevice: IDirect3DDevice8;
    FWorldMatrix: TD3DMatrix;
    FTiled: Boolean;
    FMeshVB: IDirect3DVertexBuffer8;
    FMeshIB: IDirect3DIndexBuffer8;
    FNumVertices: Cardinal;
    FNumFaces: Cardinal;
  protected
    function Render: HResult; overload; virtual;
    function FrameMove: HResult; virtual;
  public
    constructor Create(var pd3dDevice: IDirect3DDevice8;
      var hr: HResult);
    destructor Destroy; override;
    function LoadMeshFromXFile(FileName: PChar; VertexFormat: Cardinal): HResult;
    procedure Tile(TileCount: Single);
    property d3dDevice: IDirect3DDevice8 read FPD3DDevice write FPD3DDevice;
    property WorldMatrix: TD3DMatrix read FWorldMatrix write FWorldMatrix;
    property MeshVB: IDirect3DVertexBuffer8 read FMeshVB write FMeshVB;
    property MeshIB: IDirect3DIndexBuffer8 read FMeshIB write FMeshIB;
    property NumVertices: Cardinal read FNumVertices write FNumVertices;
    property NumFaces: Cardinal read FNumFaces write FNumFaces;
  end;

  {
  
  ==========================================================================
   TD3DAnimatedMesh - класс для вывода анимированных мешей - поддерживается
     анимация простых трансформаций примитивов (объектная);
     скелетная анимация.

   + вспомогательные типы и классы для работы с анимацией

  ==========================================================================

   Правила использования -


   - в методе создания приложения создать экземпляр класса
     SomeAnimMesh := TD3DAnimatedMesh.Create('filename.x');

   - в методе инициализации 3D-сцены приложения (InitDeviceObjects, CreateDeviceObjects)
       вызвать метод инициализации экземпляра класса

     SomeAnimMesh.InitDeviceObjects(pd3dDevice);
     // pd3dDevice - указатель на IDirect3DDevice8 используемый в приложении

   - в методе RestoreDeviceObjects приложения вызвать метод
     SomeAnimMesh.RestoreDeviceObjects;

   - в методе InvalidateDeviceObjects приложения
     SomeAnimMesh.InvalidateDeviceObjects;

   - в методе DestroyDeviceObjects приложения
     SomeAnimMesh.DeleteDeviceObjects;

   - Если предыдущий метод не вызывается в методе уничтожения приложения, то
     в методе уничтожения приложения -
     SAFE_DELETE(SomeAnimMesh);

   В методах отрисовки сцены (Render и FrameMove) вызвать соответствующие
   методы класса, т.е.
     SomeAnimMesh.Render;
     ...
     SomeAnimMesh.FrameMove;

  ==========================================================================

  
  }

type
  METHOD = (
    D3DNONINDEXED,
    D3DINDEXED,
    SOFTWARE,

    D3DINDEXEDVS,
    NONE
  );

  PD3DXMatrixArray = ^TD3DXMatrixArray;
  TD3DXMatrixArray = array [0..0] of TD3DXMatrix;

  SMeshContainer = class
  private
    pMesh:              ID3DXMesh;
    rgMaterials:        array of TD3DMaterial8;
    pTextures:          array of IDirect3DTexture8;
    cpattr:             DWord;
    cMaterials:         DWord;
    iAttrSplit:         DWord;

    pmcNext:            SMeshContainer;

    szName:             String;

    // Skin info
    m_pSkinMesh:        ID3DXSkinMesh;
    m_pAttrTable:       array of TD3DXAttributeRange;
    m_pBoneMatrix:      array of PD3DXMatrix;
    m_pBoneNamesBuf:    ID3DXBuffer;
    m_pBoneOffsetBuf:   ID3DXBuffer;
    m_pBoneOffsetMat:   PD3DXMatrixArray; // It's used as pointer to "ID3DXBuffer.GetByfferPointer" :BAA
    m_rgiAdjacency:     array of DWord;
    // m_numBoneComb:      DWord; // not used anyway :BAA
    m_maxFaceInfl:      DWord;
    m_pBoneCombinationBuf: ID3DXBuffer;

    m_Method:           METHOD;
    m_paletteSize:      DWord;
    m_bUseSW:           Bool;
  public
    constructor Create;
    destructor Destroy; override;
  end;

  // X File formation rotate key
  PSRotateKeyXFile = ^SRotateKeyXFile;
  SRotateKeyXFile = record
    dwTime:     DWord;
    dwFloats:   DWord;
    w, x, y, z: Single;
  end;

  PSScaleKeyXFile = ^SScaleKeyXFile;
  SScaleKeyXFile = record
    dwTime:     DWord;
    dwFloats:   DWord;
    vScale:     TD3DXVector3;
  end;


  PSPositionKeyXFile = ^SPositionKeyXFile;
  SPositionKeyXFile = record
    dwTime:     DWord;
    dwFloats:   DWord;
    vPos:       TD3DXVector3;
  end;

  PSMatrixKeyXFile = ^SMatrixKeyXFile;
  SMatrixKeyXFile = record
    dwTime:     DWord;
    dwFloats:   DWord;
    mat:        TD3DXMatrix;
  end;

  // in memory versions
  PSRotateKey = ^SRotateKey;
  SRotateKey = record
    dwTime:     DWord;
    quatRotate: TD3DXQuaternion;
  end;

  PSPositionKey = ^SPositionKey;
  SPositionKey = record
    dwTime:     DWord;
    vPos:       TD3DXVector3;
  end;

  PSScaleKey = ^SScaleKey;
  SScaleKey = record
    dwTime:     DWord;
    vScale:     TD3DXVector3;
  end;

  PSMatrixKey = ^SMatrixKey;
  SMatrixKey = record
    dwTime:     DWord;
    mat:        TD3DXMatrix;
  end;

  SFrame = class
  private
    pmcMesh:            SMeshContainer;

    matRotOrig:         TD3DXMatrix;
    matCombined:        TD3DXMatrix;

    // animation information

    m_pPositionKeys:    array of SPositionKey;
    m_cPositionKeys:    LongWord;
    m_pRotateKeys:      array of SRotateKey;
    m_cRotateKeys:      LongWord;
    m_pScaleKeys:       array of SScaleKey;
    m_cScaleKeys:       LongWord;
    m_pMatrixKeys:      array of SMatrixKey;
    m_cMatrixKeys:      LongWord;

    pframeAnimNext:     SFrame;
    pframeToAnimate:    SFrame;

    pframeSibling:      SFrame;
    pframeFirstChild:   SFrame;

    bAnimationFrame:    BOOL;
    szName:             String;
    procedure AddFrame(pframe: SFrame);
    procedure AddMesh(pmc: SMeshContainer);

  public
    matRot:             TD3DXMatrix;
    matTrans:           TD3DXMatrix;

    constructor Create;
    destructor Destroy; override;
    procedure SetTime(fGlobalTime: Single);
    function FindFrame(szFrame: String): SFrame;
    procedure ResetMatrix;
  end;


  SDrawElement = class
    pframeRoot: SFrame;

    vCenter: TD3DXVector3;
    fRadius: Single;

    // name of element for selection purposes
    szName: String;

    // animation list
    pframeAnimHead: SFrame;

	// next element in list

    pdeNext: SDrawElement;

    fCurTime: Single;
    fMaxTime: Single;

    constructor Create;
    destructor Destroy; override;

    procedure AddAnimationFrame(pframeAnim: SFrame);
  private
    function FindFrame(szName: String): SFrame;
  end;

  TD3DAnimatedMesh = class(TObject)
    m_pd3dDevice: IDirect3DDevice8;
    m_d3dsdBackBuffer: TD3DSurfaceDesc;   // Surface desc of the backbuffer
    m_d3dCaps: TD3DCaps8;                 // Caps for the device

    m_method:   METHOD;
    m_dwFVF:    DWord;

    m_pmcSelectedMesh:  SMeshContainer;
    m_pframeSelected:   SFrame;
    m_pdeSelected:      SDrawElement;
    m_pdeHead:          SDrawElement;

    m_szPath:           TD3DFilename;
    m_pBoneMatrices:    array of TD3DXMatrix;
    m_maxBones:         DWord;

    m_dwIndexedVertexShader: array[0..3] of DWord;
    m_mView:            TD3DXMatrixA16;

    //orientation
    m_matRotation, m_matTranslation: TD3DXMatrix;
  private
    // Stateblocks for setting and restoring render states
    m_dwSavedStateBlock: DWORD;
    m_dwD3DAnimatedMeshStateBlock: DWORD;

  protected
    function FindBones(pframeCur: SFrame; pde: SDrawElement): HResult;
    function LoadMeshHierarchy: HResult;
    function LoadAnimationSet(pxofobjCur: IDirectXFileData; pde: SDrawElement;
      options: DWord; fvf: DWord; pD3DDevice: IDirect3DDevice8;
      pframeParent: SFrame): HResult;
    function LoadAnimation(pxofobjCur: IDirectXFileData; pde: SDrawElement;
      options: DWord; fvf: DWord; pD3DDevice: IDirect3DDevice8;
      pframeParent: SFrame): HResult;
    function LoadFrames(pxofobjCur: IDirectXFileData; pde: SDrawElement;
      options: DWord; fvf: DWord; pD3DDevice: IDirect3DDevice8;
      pframeParent: SFrame): HResult;
    function LoadMesh(pxofobjCur: IDirectXFileData;
      options: DWord; fvf: DWord; pD3DDevice: IDirect3DDevice8;
      pframeParent: SFrame): HResult;
    function SetProjectionMatrix: HResult;
    function DeleteSelectedMesh: HResult;
    function DrawMeshContainer(pmcMesh: SMeshContainer): HResult;
    function UpdateFrames(pframeCur: SFrame; var matCur: TD3DXMatrix): HResult;
    function GenerateMesh(pmcMesh: SMeshContainer): HResult;
    function DrawFrames(pframeCur: SFrame; var cTriangles: UINT): HResult;

  public
    constructor Create(MeshName: TD3DFileName);
    destructor Destroy; override;

    function InitDeviceObjects(pd3dDevice: IDirect3DDevice8): HResult;
    function RestoreDeviceObjects: HResult;
    function InvalidateDeviceObjects: HResult;
    function DeleteDeviceObjects: HResult;

    function Render: HResult;
    function FrameMove: HResult;
  end;

  function CalculateBoundingSphere(pdeCur: SDrawElement): HResult;
  function EqualGUID(const G1,G2: TGUID): Boolean;
  function CalculateRadius(pframe: SFrame; const pmatCur: TD3DXMatrix;
    var pvCenter: TD3DXVector3; var pfRadiusSq: Single): HResult;
  function CalculateSum(pframe: SFrame; const pmatCur: TD3DXMatrix;
    var pvCenter: TD3DXVector3; var pcVertices: UINT): HResult;
  procedure ReleaseDeviceDependentMeshes(pframe: SFrame);


implementation

uses Resource, Math;  

{  TD3D8Mesh  }

constructor TD3D8Mesh.Create(var pd3dDevice: IDirect3DDevice8;
  var hr: HResult);
begin
  if pd3dDevice = nil then begin
    hr := E_FAIL;
    Exit;
  end;
  inherited Create;
  FPD3DDevice := pd3dDevice;
  D3DXMatrixIdentity(FWorldMatrix);
  hr := S_OK;  
end;

destructor TD3D8Mesh.Destroy;
begin
  SAFE_RELEASE(FMeshVB);
  SAFE_RELEASE(FMeshIB);
  FPD3DDevice := nil;
  inherited Destroy;
end;

function TD3D8Mesh.LoadMeshFromXFile(FileName: PChar;
  VertexFormat: Cardinal): HResult;
begin
  Result := Create_(FPD3DDevice, FileName);
  if Succeeded(Result) then begin
    if VertexFormat <> 0 then
      SetFVF(FPD3DDevice, VertexFormat)
    else
      SetFVF(FPD3DDevice, D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_TEX1);
    FNumVertices := GetSysMemMesh.GetNumVertices;
    FNumFaces := GetSysMemMesh.GetNumFaces;
    GetSysMemMesh.GetVertexBuffer(FMeshVB);
    GetSysMemMesh.GetIndexBuffer(FMeshIB);
  end;  
end;

{ Заполнение текстурой из заданного количества тайлов
}
procedure TD3D8Mesh.Tile(TileCount: Single);
var
  Vx: PVertex;
  NumVx, i: Cardinal;
  hr: HResult;
begin
  if FTiled then begin
    hr := RestoreDeviceObjects(FPD3DDevice);
    if Failed(hr) then Exit;
    Exit;
  end
  else
    FTiled := True;

  NumVx := m_pSysMemMesh.GetNumVertices;

  // Initialize its texture coordinates
  hr := m_pSysMemMesh.LockVertexBuffer(0, PByte(Vx));
  if FAILED(hr) then Exit;

  for i := 0 to NumVx - 1 do
  begin
    Vx.fTex[0] := Vx.fTex[0] * TileCount;
    Vx.fTex[1] := Vx.fTex[1] * TileCount;

    Inc(Vx);
  end;

  m_pSysMemMesh.UnlockVertexBuffer;

  RestoreDeviceObjects(FPD3DDevice);
end;

{  Виртуальные методы для реализации в наследуемых классах
}

function TD3D8Mesh.Render: HResult;
begin
  result := S_OK;
end;

function TD3D8Mesh.FrameMove: HResult;
begin
  result := S_OK;
end;

{ end of TD3D8Mesh  }

{   SMeshContainer   }

constructor SMeshContainer.Create;
begin
  m_Method:= NONE;
  m_bUseSW:= False;
end;

destructor SMeshContainer.Destroy;
var
  i: DWord;
begin
  if Assigned(pTextures) then
    for i:= 0 to cMaterials - 1 do pTextures[i]:= nil;

  pmcNext.Free;
end;


{  SFrame  }

constructor SFrame.Create;
begin
  D3DXMatrixIdentity(matRot);
  D3DXMatrixIdentity(matRotOrig);
  D3DXMatrixIdentity(matTrans);
end;

destructor SFrame.Destroy;
begin
  pmcMesh.Free;
  pframeFirstChild.Free;
  pframeSibling.Free;

  // do NOT delete pframeAnimNext
  // do NOT delete pframeToAnimate

  inherited;
end;

function SFrame.FindFrame(szFrame: String): SFrame;
begin
  if (szName <> '') and (szName = szFrame) then
  begin
    Result:= Self;
    Exit;
  end;

  if (pframeFirstChild <> nil) then
  begin
    Result:= pframeFirstChild.FindFrame(szFrame);
    if (Result <> nil) then Exit;
  end;

  if (pframeSibling <> nil) then
  begin
    Result:= pframeSibling.FindFrame(szFrame);
    if (Result <> nil) then Exit;
  end;

  Result:= nil;
end;

procedure SFrame.ResetMatrix;
begin
  matRot:= matRotOrig;
  D3DXMatrixIdentity(matTrans);

  if (pframeFirstChild <> nil)  then pframeFirstChild.ResetMatrix;

  if (pframeSibling <> nil)     then pframeSibling.ResetMatrix;
end;

procedure SFrame.AddFrame(pframe: SFrame);
begin
  if (pframeFirstChild = nil)
    then pframeFirstChild:= pframe else
  begin
    pframe.pframeSibling:= pframeFirstChild.pframeSibling;
    pframeFirstChild.pframeSibling:= pframe;
  end;
end;

procedure SFrame.AddMesh(pmc: SMeshContainer);
begin
  pmc.pmcNext:= pmcMesh;
  pmcMesh:= pmc;
end;

procedure SFrame.SetTime(fGlobalTime: Single);
 function FMod(A, B: Single): Single;
 begin // returns remainder of floating divide
   if (B = 0) then Result:= 0
   //This is 99% correct substitute for the next line
   else Result:= (A/B - Round(A/B-0.499))*B;
// else Result:= B * Frac(A / B); // Floating exception raised in D3D fcomp mode 
 end;
var
  iKey: UINT;
  dwp2: UINT;
  dwp3: UINT;
  i0, i1, i2, i3: Integer;  
  matResult: TD3DXMatrixA16;
  matTemp: TD3DXMatrixA16;
  fTime1: Single;
  fTime2: Single;
  fLerpValue: Single;
  vScale: TD3DXVector3;
  vPos: TD3DXVector3;
  quat: TD3DXQuaternion;
  bAnimate: BOOL;
  fTime: Single;

  qA, qB, qC: TD3DXQuaternion;
begin
  bAnimate := False;

  dwp2:= 0; // new :BAA
  dwp3:= 0; // new :BAA

  if (m_pMatrixKeys <> nil) then
  begin
    fTime:= fmod(fGlobalTime, m_pMatrixKeys[m_cMatrixKeys-1].dwTime);

    for iKey:= 0 to m_cMatrixKeys - 1 do
    begin
      if (m_pMatrixKeys[iKey].dwTime > fTime) then
      begin
        dwp3:= iKey;

        if (iKey > 0) then
        begin
          dwp2:= iKey - 1;
        end
        else  // when iKey == 0, then dwp2 == 0
        begin
          dwp2:= iKey;
        end;

        Break;
      end;
    end;
    fTime1:= m_pMatrixKeys[dwp2].dwTime;
    fTime2:= m_pMatrixKeys[dwp3].dwTime;

    if ((fTime2 - fTime1) = 0)
      then fLerpValue := 0
      else fLerpValue:= (fTime - fTime1) / (fTime2 - fTime1);

    if (fLerpValue > 0.5) then
    begin
      iKey:= dwp3;
    end
    else
    begin
      iKey:= dwp2;
    end;

    pframeToAnimate.matRot:= m_pMatrixKeys[iKey].mat;
  end
  else
  begin
    D3DXMatrixIdentity(matResult);

    if (m_pScaleKeys <> nil) then
    begin
      dwp2:= 0;
      dwp3:= 0;

      fTime:= fmod(fGlobalTime, m_pScaleKeys[m_cScaleKeys-1].dwTime);

      for iKey:= 0 to m_cScaleKeys - 1 do
      begin
        if (m_pScaleKeys[iKey].dwTime > fTime) then
        begin
          dwp3:= iKey;

          if (iKey > 0) then
          begin
            dwp2:= iKey - 1;
          end
          else  // when iKey == 0, then dwp2 == 0
          begin
            dwp2:= iKey;
          end;

          Break;
        end;
      end;
      fTime1:= m_pScaleKeys[dwp2].dwTime;
      fTime2:= m_pScaleKeys[dwp3].dwTime;

      if ((fTime2 - fTime1) = 0)
        then fLerpValue:= 0
        else fLerpValue:= (fTime - fTime1) / (fTime2 - fTime1);

      D3DXVec3Lerp(vScale,
              m_pScaleKeys[dwp2].vScale,
              m_pScaleKeys[dwp3].vScale,
              fLerpValue);

      D3DXMatrixScaling(matTemp, vScale.x, vScale.y, vScale.z);

      D3DXMatrixMultiply(matResult, matResult, matTemp);

      bAnimate:= True;
    end;

    //check rot keys
    if (m_pRotateKeys <> nil) then
    begin
      i1 := 0;
      i2 := 0;

      fTime:= fmod(fGlobalTime, m_pRotateKeys[m_cRotateKeys-1].dwTime);

      for iKey:= 0 to m_cRotateKeys - 1 do
      begin
        if (m_pRotateKeys[iKey].dwTime > fTime) then
        begin
          if (iKey > 0) then i1:= iKey - 1 else i1:= 0;
          i2:= iKey;
          Break;
        end;
      end;
      fTime1:= m_pRotateKeys[i1].dwTime;
      fTime2:= m_pRotateKeys[i2].dwTime;

      if ((fTime2 - fTime1) = 0)
        then fLerpValue:= 0
        else fLerpValue:= (fTime - fTime1) / (fTime2 - fTime1);

{$define USE_SQUAD}
  {$ifdef USE_SQUAD}
      i0 := i1 - 1;
      i3 := i2 + 1;

      if (i0 < 0) then
        i0:= i0 + Integer(m_cRotateKeys);

      if (i3 >= Integer(m_cRotateKeys)) then
        i3:= i3 - Integer(m_cRotateKeys);

      D3DXQuaternionSquadSetup(qA, qB, qC,
          m_pRotateKeys[i0].quatRotate, m_pRotateKeys[i1].quatRotate,
          m_pRotateKeys[i2].quatRotate, m_pRotateKeys[i3].quatRotate);

      D3DXQuaternionSquad(quat, m_pRotateKeys[i1].quatRotate, qA, qB, qC, fLerpValue);
  {$else}
      D3DXQuaternionSlerp(quat, m_pRotateKeys[i1].quatRotate, m_pRotateKeys[i2].quatRotate, fLerpValue); 
  {$endif}

      quat.w := -quat.w;
      D3DXMatrixRotationQuaternion(matTemp, quat);
      D3DXMatrixMultiply(matResult, matResult, matTemp);

      bAnimate:= True;
    end;

    if (m_pPositionKeys <> nil) then
    begin
      dwp2:= 0;
      dwp3:= 0;

      fTime:= fmod(fGlobalTime, m_pPositionKeys[m_cPositionKeys-1].dwTime);

      for iKey:= 0 to m_cPositionKeys - 1 do
      begin
        if (m_pPositionKeys[iKey].dwTime > fTime) then
        begin
          dwp3:= iKey;

          if (iKey > 0) then
          begin
            dwp2:= iKey - 1;
          end
          else  // when iKey == 0, then dwp2 == 0
          begin
            dwp2:= iKey;
          end;

          Break;
        end;
      end;
      fTime1:= m_pPositionKeys[dwp2].dwTime;
      fTime2:= m_pPositionKeys[dwp3].dwTime;

      if ((fTime2 - fTime1) = 0)
        then fLerpValue:= 0
        else fLerpValue:= (fTime - fTime1) / (fTime2 - fTime1);

      D3DXVec3Lerp(vPos,
                   m_pPositionKeys[dwp2].vPos,
                   m_pPositionKeys[dwp3].vPos,
                   fLerpValue);

      D3DXMatrixTranslation(matTemp, vPos.x, vPos.y, vPos.z);

      D3DXMatrixMultiply(matResult, matResult, matTemp);
      bAnimate:= True;
    end
    else
    begin
      D3DXMatrixTranslation(matTemp, pframeToAnimate.matRotOrig._41, pframeToAnimate.matRotOrig._42, pframeToAnimate.matRotOrig._43);

      D3DXMatrixMultiply(matResult, matResult, matTemp);
    end;

    if (bAnimate) then
    begin
      pframeToAnimate.matRot:= matResult;
    end;
  end;
end;


{    SDrawElement    }

constructor SDrawElement.Create;
begin
  vCenter:= D3DXVector3Zero;
  fRadius:= 1.0;
end;

destructor SDrawElement.Destroy;
begin
  pframeRoot.Free;
  pdeNext.Free;

  // do NOT delete pframeAnimHead;

  inherited;
end;

procedure SDrawElement.AddAnimationFrame(pframeAnim: SFrame);
begin
  pframeAnim.pframeAnimNext:= pframeAnimHead;
  pframeAnimHead:= pframeAnim;
end;

function SDrawElement.FindFrame(szName: String): SFrame;
begin
  if (pframeRoot = nil) then Result:= nil
    else Result:= pframeRoot.FindFrame(szName);
end;

{  utilized  proc's}

const
  UINT_MAX      = $FFFFFFFF;

function EqualGUID(const G1,G2: TGUID): Boolean;
begin
  Result := CompareMem(@G1,@G2,SizeOf(TGUID));
end;

function CalculateSum(pframe: SFrame; const pmatCur: TD3DXMatrix;
  var pvCenter: TD3DXVector3; var pcVertices: UINT): HResult;
var
  pbPoints: PByte;
  cVerticesLocal: UINT;
  pbCur: PByte;
  pvCur: PD3DXVector3;
  vTransformedCur: TD3DXVector3;
  iPoint: UINT;
  pmcCur: SMeshContainer;
  pframeCur: SFrame;
  cVertices: UINT;
  matLocal: TD3DXMatrixA16;

  fvfsize: DWord;
begin
  Result:= S_OK;
  pbPoints:= nil;
  cVerticesLocal:= 0;

  D3DXMatrixMultiply(matLocal, pframe.matRot, pmatCur);

  pmcCur:= pframe.pmcMesh;
  try
    while (pmcCur <> nil) do
    begin
      fvfsize:= D3DXGetFVFVertexSize(pmcCur.pMesh.GetFVF);

      cVertices:= pmcCur.pMesh.GetNumVertices;

      Result:= pmcCur.pMesh.LockVertexBuffer(0, pbPoints);
      if FAILED(Result) then Exit;

      // for( iPoint=0, pbCur = pbPoints; iPoint < cVertices; iPoint++, pbCur += fvfsize)
      pbCur:= pbPoints;
      for iPoint:= 0 to cVertices - 1 do
      begin
        pvCur:= PD3DXVector3(pbCur);

        if ((pvCur.x <> 0.0) or (pvCur.y <> 0.0) or (pvCur.z <> 0.0)) then
        begin
          Inc(cVerticesLocal);

          D3DXVec3TransformCoord(vTransformedCur, pvCur^, matLocal);

          pvCenter.x:= pvCenter.x + vTransformedCur.x;
          pvCenter.y:= pvCenter.y + vTransformedCur.y;
          pvCenter.z:= pvCenter.z + vTransformedCur.z;
        end;

        // "for" cycle appendix :BAA
        Inc(pbCur, fvfsize);
      end;

      pmcCur.pMesh.UnlockVertexBuffer;
      pbPoints:= nil;

      pmcCur:= pmcCur.pmcNext;
    end;

    Inc(pcVertices, cVerticesLocal);

    pframeCur:= pframe.pframeFirstChild;
    while (pframeCur <> nil) do
    begin
      Result:= CalculateSum(pframeCur, matLocal, pvCenter, pcVertices);
      if FAILED(Result) then Exit;

      pframeCur:= pframeCur.pframeSibling;
    end;

  finally
    if (pbPoints <> nil) then
    begin
      pmcCur.pMesh.UnlockVertexBuffer;
    end;
  end;
end;

function CalculateRadius(pframe: SFrame; const pmatCur: TD3DXMatrix;
  var pvCenter: TD3DXVector3; var pfRadiusSq: Single): HResult;
var
  pbPoints: PByte;
  pbCur: PByte;
  pvCur: PD3DXVector3;
  vDist: TD3DXVector3;
  iPoint: UINT;
  cVertices: UINT;
  pmcCur: SMeshContainer;
  pframeCur: SFrame;
  fRadiusLocalSq: Single;
  fDistSq: Single;
  matLocal: TD3DXMatrixA16;

  fvfsize: DWord;
begin
  Result:= S_OK;
  pbPoints:= nil;

  D3DXMatrixMultiply(matLocal, pframe.matRot, pmatCur);

  pmcCur:= pframe.pmcMesh;
  try
    fRadiusLocalSq:= pfRadiusSq;
    while (pmcCur <> nil) do
    begin
      fvfsize:= D3DXGetFVFVertexSize(pmcCur.pMesh.GetFVF);

      cVertices:= pmcCur.pMesh.GetNumVertices;

      Result:= pmcCur.pMesh.LockVertexBuffer(0, pbPoints);
      if FAILED(Result) then Exit;

      // for( iPoint=0, pbCur = pbPoints; iPoint < cVertices; iPoint++, pbCur += fvfsize )
      pbCur:= pbPoints;
      for iPoint:=0 to cVertices - 1 do
      begin
        pvCur:= PD3DXVector3(pbCur);

        if ((pvCur.x = 0.0) and (pvCur.y = 0.0) and (pvCur.z = 0.0))
        then Continue;

        D3DXVec3TransformCoord(vDist, pvCur^, matLocal);

        // vDist -= *pvCenter;
        D3DXVec3Subtract(vDist, vDist, pvCenter);

        fDistSq:= D3DXVec3LengthSq(vDist);

        if (fDistSq > fRadiusLocalSq) then fRadiusLocalSq:= fDistSq;

        // "for" cycle appendix :BAA
        Inc(pbCur, fvfsize);
      end;

      pmcCur.pMesh.UnlockVertexBuffer;
      pbPoints:= nil;

      pmcCur:= pmcCur.pmcNext;
    end;

    pfRadiusSq:= fRadiusLocalSq;

    pframeCur:= pframe.pframeFirstChild;
    while (pframeCur <> nil) do
    begin
      Result:= CalculateRadius(pframeCur, matLocal, pvCenter, pfRadiusSq);
      if FAILED(Result) then Exit;

      pframeCur:= pframeCur.pframeSibling;
    end;

  finally
    if (pbPoints <> nil) then pmcCur.pMesh.UnlockVertexBuffer;
  end;
end;

function CalculateBoundingSphere(pdeCur: SDrawElement): HResult;
var
  vCenter: TD3DXVector3;
  cVertices: UINT;
  fRadiusSq: Single;
  matCur: TD3DXMatrixA16;
begin
  vCenter:= D3DXVector3Zero;
  cVertices:= 0;
  fRadiusSq:= 0;

  D3DXMatrixIdentity(matCur);
  Result:= CalculateSum(pdeCur.pframeRoot, matCur, vCenter, cVertices);
  if FAILED(Result) then Exit;

  if (cVertices > 0) then
  begin
    // vCenter /= (float)cVertices;
    D3DXVec3Scale(vCenter, vCenter, 1/cVertices);

    D3DXMatrixIdentity(matCur);
    Result:= CalculateRadius(pdeCur.pframeRoot, matCur, vCenter, fRadiusSq);
    if FAILED(Result) then Exit;
  end;

  pdeCur.fRadius:= sqrt(fRadiusSq);
  pdeCur.vCenter:= vCenter;
end;

//-----------------------------------------------------------------------------
// Name: ReleaseDeviceDependentMeshes()
// Desc:
//-----------------------------------------------------------------------------
procedure ReleaseDeviceDependentMeshes(pframe: SFrame);
var
  pmcCurr: SMeshContainer;
begin
  if (pframe.pmcMesh <> nil) then
  begin
    // for (SMeshContainer* pmcCurr = pframe->pmcMesh; pmcCurr != NULL; pmcCurr = pmcCurr->pmcNext)
    pmcCurr:= pframe.pmcMesh;
    while (pmcCurr <> nil) do
    begin
      if (pmcCurr.m_pSkinMesh <> nil) then
      begin
        pmcCurr.pMesh:= nil;

        pmcCurr.m_Method:= NONE;
      end;
      pmcCurr:= pmcCurr.pmcNext;
    end;
  end;

  if (pframe.pframeFirstChild <> nil) then
      ReleaseDeviceDependentMeshes(pframe.pframeFirstChild);

  if (pframe.pframeSibling <> nil) then
      ReleaseDeviceDependentMeshes(pframe.pframeSibling);
end;

{  TD3DAnimatedMesh   }

constructor TD3DAnimatedMesh.Create(MeshName: TD3DFileName);
begin
  inherited Create;

  m_dwSavedStateBlock := 0;
  m_dwD3DAnimatedMeshStateBlock := 0;

  m_pmcSelectedMesh     := nil;
  m_pframeSelected      := nil;
  m_pdeHead             := nil;
  m_pdeSelected         := nil;

  m_dwFVF := D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_NORMAL or D3DFVF_TEX1;

  m_method := D3DNONINDEXED;

  m_pBoneMatrices:= nil;
  m_maxBones:= 0;

  StrECopy(m_szPath, MeshName);
  if not Assigned(@m_szPath) then m_szPath := 'undefined.x';
end;

destructor TD3DAnimatedMesh.Destroy;
begin
  InvalidateDeviceObjects;
  DeleteDeviceObjects;
  inherited Destroy;
end;

function TD3DAnimatedMesh.InitDeviceObjects(pd3dDevice: IDirect3DDevice8): HResult;
begin
  m_pd3dDevice := pd3dDevice;
  if not Assigned(m_pd3dDevice) then begin
    Result := E_POINTER;
    DXTrace(Unitname, $FFFFFFFF, Result, 'pointer to IDirect3DDevice8 interface is nil!!!');
    Exit;
  end;
  
  Result:= LoadMeshHierarchy;
end;

//-----------------------------------------------------------------------------
// Name: RestoreDeviceObjects()
// Desc: Initialize scene objects.
//-----------------------------------------------------------------------------
function TD3DAnimatedMesh.RestoreDeviceObjects;
type
 TVectDecl = array [0..6] of DWord;
var
  which: Integer;

  dwIndexedVertexDecl1,
  dwIndexedVertexDecl2,
  dwIndexedVertexDecl3,
  dwIndexedVertexDecl4: TVectDecl;
  dwIndexedVertexDecl: array [0..3] of ^TVectDecl;

  pCode: ID3DXBuffer;
  bUseSW: DWord;
  i: DWord;

  light: TD3DLight8;
  vLightDir: TD3DXVector4;
  shaderFileName: string;

begin
  if (m_pdeSelected <> nil) then SetProjectionMatrix;

  // Create the state blocks for rendering animated mesh
  for which:= 0 to 1 do
  begin
    m_pd3dDevice.BeginStateBlock;

    m_pd3dDevice.SetRenderState(D3DRS_ZENABLE, iTrue);

    m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, 1);
    m_pd3dDevice.SetRenderState(D3DRS_SRCBLEND,         D3DBLEND_SRCALPHA);
    m_pd3dDevice.SetRenderState(D3DRS_DESTBLEND,        D3DBLEND_INVSRCALPHA);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHATESTENABLE,  1);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHAREF,         $08);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHAFUNC,        D3DCMP_GREATEREQUAL);
    m_pd3dDevice.SetRenderState(D3DRS_FILLMODE,         D3DFILL_SOLID);
    m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,         D3DCULL_CCW);
    m_pd3dDevice.SetRenderState(D3DRS_STENCILENABLE,    0);
    m_pd3dDevice.SetRenderState(D3DRS_CLIPPING,         1);
    m_pd3dDevice.SetRenderState(D3DRS_EDGEANTIALIAS,    0);
    m_pd3dDevice.SetRenderState(D3DRS_CLIPPLANEENABLE,  0);
    m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND,      0);
    m_pd3dDevice.SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, 0);
    m_pd3dDevice.SetRenderState(D3DRS_FOGENABLE,        0);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_NONE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    m_pd3dDevice.SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_DISABLE);
    m_pd3dDevice.SetTextureStageState(1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE);

    m_pd3dDevice.SetRenderState(D3DRS_DITHERENABLE,       iTRUE);

    m_pd3dDevice.SetRenderState(D3DRS_SPECULARENABLE,     iFalse);
    m_pd3dDevice.SetRenderState(D3DRS_NORMALIZENORMALS,   iTRUE);

    m_pd3dDevice.SetRenderState(D3DRS_LIGHTING,           iTRUE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);

    m_pd3dDevice.SetRenderState(D3DRS_COLORVERTEX,        iFALSE);

    if (which = 0) then
      m_pd3dDevice.EndStateBlock(m_dwSavedStateBlock)
    else
      m_pd3dDevice.EndStateBlock(m_dwD3DAnimatedMeshStateBlock);
  end;

  // Create vertex shader for the indexed skinning
  dwIndexedVertexDecl1[0]:=    D3DVSD_STREAM(0);
  dwIndexedVertexDecl1[1]:=    D3DVSD_REG(0, D3DVSDT_FLOAT3); // Position of first mesh
  dwIndexedVertexDecl1[2]:=    D3DVSD_REG(2, D3DVSDT_D3DCOLOR); // Blend indices
//  dwIndexedVertexDecl1[2]:=      D3DVSD_REG( 2, D3DVSDT_UBYTE4); // Blend indices
  dwIndexedVertexDecl1[3]:=    D3DVSD_REG(3, D3DVSDT_FLOAT3); // Normal
  dwIndexedVertexDecl1[4]:=    D3DVSD_REG(4, D3DVSDT_FLOAT2); // Tex coords
  dwIndexedVertexDecl1[5]:=    D3DVSD_END;

  dwIndexedVertexDecl2[0]:=    D3DVSD_STREAM(0);
  dwIndexedVertexDecl2[1]:=    D3DVSD_REG(0, D3DVSDT_FLOAT3); // Position of first mesh
  dwIndexedVertexDecl2[2]:=    D3DVSD_REG(1, D3DVSDT_FLOAT1); // Blend weights
  dwIndexedVertexDecl2[3]:=    D3DVSD_REG(2, D3DVSDT_D3DCOLOR); // Blend indices
//  dwIndexedVertexDecl2[3]:=      D3DVSD_REG(2, D3DVSDT_UBYTE4); // Blend indices
  dwIndexedVertexDecl2[4]:=    D3DVSD_REG(3, D3DVSDT_FLOAT3); // Normal
  dwIndexedVertexDecl2[5]:=    D3DVSD_REG(4, D3DVSDT_FLOAT2); // Tex coords
  dwIndexedVertexDecl2[6]:=    D3DVSD_END;

  dwIndexedVertexDecl3[0]:=    D3DVSD_STREAM(0);
  dwIndexedVertexDecl3[1]:=    D3DVSD_REG(0, D3DVSDT_FLOAT3); // Position of first mesh
  dwIndexedVertexDecl3[2]:=    D3DVSD_REG(1, D3DVSDT_FLOAT2); // Blend weights
  dwIndexedVertexDecl3[3]:=    D3DVSD_REG(2, D3DVSDT_D3DCOLOR); // Blend indices
//  dwIndexedVertexDecl3[3]:=      D3DVSD_REG( 2, D3DVSDT_UBYTE4); // Blend indices
  dwIndexedVertexDecl3[4]:=    D3DVSD_REG(3, D3DVSDT_FLOAT3); // Normal
  dwIndexedVertexDecl3[5]:=    D3DVSD_REG(4, D3DVSDT_FLOAT2); // Tex coords
  dwIndexedVertexDecl3[6]:=    D3DVSD_END;

  dwIndexedVertexDecl4[0]:=    D3DVSD_STREAM(0);
  dwIndexedVertexDecl4[1]:=    D3DVSD_REG(0, D3DVSDT_FLOAT3); // Position of first mesh
  dwIndexedVertexDecl4[2]:=    D3DVSD_REG(1, D3DVSDT_FLOAT3); // Blend weights
  dwIndexedVertexDecl4[3]:=    D3DVSD_REG(2, D3DVSDT_D3DCOLOR); // Blend indices
//  dwIndexedVertexDecl4[3]:=      D3DVSD_REG( 2, D3DVSDT_UBYTE4); // Blend indices
  dwIndexedVertexDecl4[4]:=    D3DVSD_REG(3, D3DVSDT_FLOAT3); // Normal
  dwIndexedVertexDecl4[5]:=    D3DVSD_REG(4, D3DVSDT_FLOAT2); // Tex coords
  dwIndexedVertexDecl4[6]:=    D3DVSD_END;

  // DWORD* dwIndexedVertexDecl[] = {dwIndexedVertexDecl1, dwIndexedVertexDecl2, dwIndexedVertexDecl3, dwIndexedVertexDecl4};
  dwIndexedVertexDecl[0]:= @dwIndexedVertexDecl1;
  dwIndexedVertexDecl[1]:= @dwIndexedVertexDecl2;
  dwIndexedVertexDecl[2]:= @dwIndexedVertexDecl3;
  dwIndexedVertexDecl[3]:= @dwIndexedVertexDecl4;

  bUseSW:= D3DUSAGE_SOFTWAREPROCESSING;
  // Store device Caps
  if not Assigned(m_pd3dDevice) then begin
    Result := E_POINTER;
    DXTrace(Unitname, $FFFFFFFF, Result, 'pointer to IDirect3DDevice8 interface is nil!!!');
    Exit;
  end;
  m_pd3dDevice.GetDeviceCaps(m_d3dCaps);
  if (m_d3dCaps.VertexShaderVersion >= D3DVS_VERSION(1, 1)) then
  begin
    bUseSW := 0;
  end;

  for i:= 0 to 3 do
  begin
    // Assemble the vertex shader file
    //Result:= D3DXAssembleShaderFromResource(0, MAKEINTRESOURCE(IDD_SHADER1 + i), 0, nil, @pCode, nil);
    ShaderFileName := 'Media\Shaders\skinmesh'+ IntToStr(i+1)+'.vsh';
    Result:= D3DXAssembleShaderFromFile(PChar(ShaderFileName), 0, nil, @pCode, nil);
    if FAILED(Result) then Exit;

    // Create the vertex shader
    Result:= m_pd3dDevice.CreateVertexShader(@dwIndexedVertexDecl[i][0],
                                             pCode.GetBufferPointer,
                                             m_dwIndexedVertexShader[i], bUseSW);

    if FAILED(Result) then Exit;

    pCode:= nil;
  end;

  FillChar(light, SizeOf(light), 0);
  light._Type:= D3DLIGHT_DIRECTIONAL;

  light.Diffuse.r:= 1.0;
  light.Diffuse.g:= 1.0;
  light.Diffuse.b:= 1.0;
  light.Specular.r:= 0;
  light.Specular.g:= 0;
  light.Specular.b:= 0;
  light.Ambient.r:= 0.25;
  light.Ambient.g:= 0.25;
  light.Ambient.b:= 0.25;

  light.Direction:= D3DXVector3(0.0, 0.0, -1.0);

  Result:= m_pd3dDevice.SetLight(0, light);
  if FAILED(Result) then Exit;

  Result:= m_pd3dDevice.LightEnable(0, TRUE);
  if FAILED(Result) then Exit;

  // Set Light for vertex shader
  vLightDir:= D3DXVector4(0.0, 0.0, 1.0, 0.0);
  m_pd3dDevice.SetVertexShaderConstant(1, vLightDir, 1);

  Result:= S_OK;
end;

//-----------------------------------------------------------------------------
// Name: InvalidateDeviceObjects()
// Desc: Called when the app is exiting, or the device is being changed,
//       this function deletes any device dependent objects.
//-----------------------------------------------------------------------------

function TD3DAnimatedMesh.InvalidateDeviceObjects;
var
  pdeCurr: SDrawElement;
begin
  // Delete the state blocks
  if (m_pd3dDevice <> nil) then
  begin
    if (m_dwSavedStateBlock <> 0) then
      m_pd3dDevice.DeleteStateBlock(m_dwSavedStateBlock);
    if (m_dwD3DAnimatedMeshStateBlock <> 0) then
      m_pd3dDevice.DeleteStateBlock(m_dwD3DAnimatedMeshStateBlock);
  end;

  m_dwSavedStateBlock    := 0;
  m_dwD3DAnimatedMeshStateBlock := 0;

  // for (SDrawElement* pdeCurr = m_pdeHead; pdeCurr != NULL; pdeCurr = pdeCurr->pdeNext)
  pdeCurr:= m_pdeHead;
  while (pdeCurr <> nil) do
  begin
    ReleaseDeviceDependentMeshes(pdeCurr.pframeRoot);
    pdeCurr:= pdeCurr.pdeNext;
  end;

  Result:= S_OK;
end;


//-----------------------------------------------------------------------------
// Name: DeleteDeviceObjects()
// Desc: Called when the app is exiting, or the device is being changed,
//       this function deletes any device dependent objects.
//-----------------------------------------------------------------------------
function TD3DAnimatedMesh.DeleteDeviceObjects;
begin
  m_pd3dDevice:= nil;

  if (m_pdeSelected = m_pdeHead) then m_pdeSelected:= nil;

  m_pdeHead.Free;
  m_pdeHead:= nil;

  Result:=S_OK;
end;


//-----------------------------------------------------------------------------
// Name: FrameMove()
// Desc: Called once per frame, the call is the entry point for animating
//       the scene.
//-----------------------------------------------------------------------------
function TD3DAnimatedMesh.FrameMove;
var
  pdeCur: SDrawElement;
  pframeCur: SFrame;
  m_fElapsedTime: Single;

begin
  m_fElapsedTime := DXUtil_Timer(TIMER_GETELAPSEDTIME);

  pdeCur:= m_pdeHead;
  while (pdeCur <> nil) do
  begin
    pdeCur.fCurTime:= pdeCur.fCurTime + m_fElapsedTime * 4800;
    if (pdeCur.fCurTime > 1.0e15) then pdeCur.fCurTime:= 0;

    pframeCur:= pdeCur.pframeAnimHead;
    while (pframeCur <> nil) do
    begin
      pframeCur.SetTime(pdeCur.fCurTime);
      pframeCur:= pframeCur.pframeAnimNext;
    end;

    pdeCur:= pdeCur.pdeNext;
  end;

  Result:= S_OK;
end;

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Called once per frame, the call is the entry point for 3d
//       rendering. This function sets up render states, clears the
//       viewport, and renders the scene.
//-----------------------------------------------------------------------------
function TD3DAnimatedMesh.Render;
var
  pdeCur: SDrawElement;

  cTriangles: UINT;
  mCur: TD3DXMatrixA16;
begin
  if (m_pd3dDevice = nil) then
  begin
    Result:= E_FAIL;
    Exit;
  end;

  // Set up viewing position
  pdeCur := m_pdeHead;
  while (pdeCur <> nil) do
  begin
    pdeCur.pframeRoot.matRot:= D3DXMatrixIdentity(m_matRotation)^;
    pdeCur.pframeRoot.matTrans:= D3DXMatrixIdentity(m_matTranslation)^;
    pdeCur:= pdeCur.pdeNext;
  end;


  if (m_pdeHead = nil) then
  begin
    Result:= S_OK;
    Exit;
  end;

  // Setup renderstate
  m_pd3dDevice.CaptureStateBlock(m_dwSavedStateBlock);
  m_pd3dDevice.ApplyStateBlock(m_dwD3DAnimatedMeshStateBlock);

  // Begin the scene
  if SUCCEEDED(m_pd3dDevice.BeginScene) then
  begin
    cTriangles := 0;

    D3DXMatrixTranslation(m_mView, 0, 0, -m_pdeSelected.fRadius * 2.8);

    Result:= m_pd3dDevice.SetTransform(D3DTS_VIEW, m_mView);
    if FAILED(Result) then Exit;

    pdeCur:= m_pdeHead;
    while (pdeCur <> nil) do
    begin
      D3DXMatrixIdentity(mCur);

      Result:= UpdateFrames(pdeCur.pframeRoot, mCur);
      if FAILED(Result) then Exit;
      Result:= DrawFrames(pdeCur.pframeRoot, cTriangles);
      if FAILED(Result) then Exit;

      pdeCur:= pdeCur.pdeNext;
    end;

    // End the scene.
    m_pd3dDevice.EndScene;
  end;

  // Restore the modified renderstates
  m_pd3dDevice.ApplyStateBlock(m_dwSavedStateBlock);

  Result:= S_OK;
end;

function TD3DAnimatedMesh.FindBones(pframeCur: SFrame; pde: SDrawElement): HResult;
type
  APChar = array[0..0] of PChar;
var
  pmcMesh: SMeshContainer;
  pframeChild: SFrame;

  pBoneName: ^APChar;
  i: DWord;
  pFrame: SFrame;
begin
  pmcMesh:= pframeCur.pmcMesh;
  while (pmcMesh <> nil) do
  begin
    if (pmcMesh.m_pSkinMesh <> nil) then
    begin
      // char** pBoneName:= static_cast<char**>(pmcMesh.m_pBoneNamesBuf.GetBufferPointer());
      pBoneName:= pmcMesh.m_pBoneNamesBuf.GetBufferPointer;
      for i:= 0 to pmcMesh.m_pSkinMesh.GetNumBones - 1 do
      begin
        pFrame:= pde.FindFrame(pBoneName[i]);
        pmcMesh.m_pBoneMatrix[i]:= @pFrame.matCombined;
      end;
    end;
    pmcMesh:= pmcMesh.pmcNext;
  end;

  pframeChild:= pframeCur.pframeFirstChild;
  while (pframeChild <> nil) do
  begin
    Result:= FindBones(pframeChild, pde);
    if FAILED(Result) then Exit;

    pframeChild:= pframeChild.pframeSibling;
  end;

  Result:= S_OK;
end;

function TD3DAnimatedMesh.LoadMeshHierarchy: HResult;
var
  pszFile: PChar;
  pdeMesh: SDrawElement;
  pxofapi: IDirectXFile;
  pxofenum: IDirectXFileEnumObject;
  pxofobjCur: IDirectXFileData;
  dwOptions: DWord;
  cchFileName: Integer;
begin
  Result:= S_OK;
  pszFile:= m_szPath;

  if (pszFile = '') then
  begin
    Result:= E_INVALIDARG;
    Exit;
  end;

  pdeMesh:= SDrawElement.Create;
  try
    pdeMesh.pframeRoot.Free;
    pdeMesh.pframeAnimHead:= nil;

    try
      pdeMesh.pframeRoot:= SFrame.Create;
    except
      Result:= E_OUTOFMEMORY;
      Exit;
    end;

    dwOptions:= 0;

    cchFileName:= strlen(pszFile);
    if (cchFileName < 2) then
    begin
      Result:= E_FAIL;
      Exit;
    end;

    Result:= DirectXFileCreate(pxofapi);
    if FAILED(Result) then Exit;

    // Register templates for d3drm.
    Result:= pxofapi.RegisterTemplates(@D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
    if FAILED(Result) then Exit;

    // Create enum object.
    Result:= pxofapi.CreateEnumObject(pszFile, DXFILELOAD_FROMFILE, pxofenum);
    if FAILED(Result) then Exit;


    // Enumerate top level objects.
    // Top level objects are always data object.
    while (SUCCEEDED(pxofenum.GetNextDataObject(pxofobjCur))) do
    begin
      Result:= LoadFrames(pxofobjCur, pdeMesh, dwOptions, m_dwFVF,
          m_pd3dDevice,
          pdeMesh.pframeRoot);
      pxofobjCur:= nil;

      if FAILED(Result) then Exit;
    end;

    Result:= FindBones(pdeMesh.pframeRoot, pdeMesh);
    if FAILED(Result) then Exit;

    pdeMesh.szName:= pszFile; // instead of:
    (*
    delete []pdeMesh->szName;
    pdeMesh->szName = new char[cchFileName+1];
    if (pdeMesh->szName == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }
    memcpy(pdeMesh->szName, pszFile, cchFileName+1);
    *)

    // delete the current mesh, now that the load has succeeded
    DeleteSelectedMesh;

    // link into the draw list
    pdeMesh.pdeNext:= m_pdeHead;
    m_pdeHead:= pdeMesh;

    m_pdeSelected:= pdeMesh;
    m_pmcSelectedMesh:= pdeMesh.pframeRoot.pmcMesh;


    m_pframeSelected:= pdeMesh.pframeRoot;

    Result:= CalculateBoundingSphere(pdeMesh);
    if FAILED(Result) then Exit;

    SetProjectionMatrix;

    m_pdeSelected.fCurTime:= 0.0;
    m_pdeSelected.fMaxTime:= 200.0;

    D3DXMatrixTranslation(m_pdeSelected.pframeRoot.matRot,
        -pdeMesh.vCenter.x, -pdeMesh.vCenter.y, -pdeMesh.vCenter.z);
    m_pdeSelected.pframeRoot.matRotOrig:= m_pdeSelected.pframeRoot.matRot;

  finally
    if FAILED(Result) then pdeMesh.Free;
  end;
end;

function TD3DAnimatedMesh.LoadAnimation(
  pxofobjCur: IDirectXFileData; pde: SDrawElement; options, fvf: DWord;
  pD3DDevice: IDirect3DDevice8; pframeParent: SFrame): HResult;
var
  pFileRotateKey: PSRotateKeyXFile;
  pFileScaleKey: PSScaleKeyXFile;
  pFilePosKey: PSPositionKeyXFile;
  pFileMatrixKey: PSMatrixKeyXFile;
  pframeCur: SFrame;
  pxofobjChild: IDirectXFileData;
  pxofChild: IDirectXFileObject;
  pxofobjChildRef: IDirectXFileDataReference;
  dwSize: DWord;
  pData: PByteArray;
  dwKeyType: DWord;
  cKeys: DWord;
  iKey: DWord;
  cchName: DWord;
  szFrameName: String;
{$WRITEABLECONST ON}
const
  _type: PGUID = nil;
{$WRITEABLECONST OFF}
begin
  Result:= S_OK;

  try
    try
      pframeCur:= SFrame.Create;
    except
      Result:= E_OUTOFMEMORY;
      Exit;
    end;
    pframeCur.bAnimationFrame:= True;

    pframeParent.AddFrame(pframeCur);
    pde.AddAnimationFrame(pframeCur);

    // Enumerate child objects.
    // Child object can be data, data reference or binary.
    // Use QueryInterface() to find what type of object a child is.
    while (SUCCEEDED(pxofobjCur.GetNextObject(pxofChild))) do
    begin
      // Query the child for it's FileDataReference
      Result:= pxofChild.QueryInterface(IID_IDirectXFileDataReference,
          pxofobjChildRef);
      if SUCCEEDED(Result) then
      begin
        Result:= pxofobjChildRef.Resolve(pxofobjChild);
        if SUCCEEDED(Result) then
        begin
          Result:= pxofobjChild.GetType(_type);
          if FAILED(Result) then Exit;

          if EqualGUID(TID_D3DRMFrame, _type^) then
          begin
            if (pframeCur.pframeToAnimate <> nil) then 
            begin
              Result:= E_INVALIDARG;
              Exit;
            end;

            Result:= pxofobjChild.GetName(nil, cchName);
            if FAILED(Result) then Exit;

            if (cchName = 0) then
            begin
              Result:= E_INVALIDARG;
              Exit;
            end;

            // "_alloca": Allocates temporary stack space. - can't be used in Delphi
            // So using usial Delphi "long" strings
            SetLength(szFrameName, cchName - 1);

            Result:= pxofobjChild.GetName(PChar(szFrameName), cchName);
            if FAILED(Result) then Exit;

            pframeCur.pframeToAnimate:= pde.FindFrame(szFrameName);
            if (pframeCur.pframeToAnimate = nil) then
            begin
              Result:= E_INVALIDARG;
              Exit;
            end;
          end;

          pxofobjChild:= nil;
        end;

        pxofobjChildRef:= nil;
      end
      else
      begin

        // Query the child for it's FileData
        Result:= pxofChild.QueryInterface(IID_IDirectXFileData, pxofobjChild);
        if SUCCEEDED(Result) then
        begin
          Result:= pxofobjChild.GetType(_type);
          if FAILED(Result) then Exit;

          if EqualGUID(TID_D3DRMFrame, _type^) then
          begin
            Result:= LoadFrames(pxofobjChild, pde, options, fvf, pD3DDevice, pframeCur);
            if FAILED(Result) then Exit;
          end
          else if EqualGUID(TID_D3DRMAnimationOptions, _type^) then
          begin
                 // This is commented in original code :BAA
            //ParseAnimOptions(pChildData,pParentFrame);
            //i=2;
          end
          else if EqualGUID(TID_D3DRMAnimationKey, _type^) then
          begin
            Result:= pxofobjChild.GetData(nil, dwSize, Pointer(pData));
            if FAILED(Result) then Exit;

            dwKeyType:= PDWord(pData)^;
            // cKeys:= ((DWORD*)pData)[1];
            cKeys:= PDWord(@pData[4])^;


            if (dwKeyType = 0) then
            begin
              if (pframeCur.m_pRotateKeys <> nil) then
              begin
                Result:= E_INVALIDARG;
                Exit;
              end;

              SetLength(pframeCur.m_pRotateKeys, cKeys);
              if (pframeCur.m_pRotateKeys = nil) then
              begin
                Result:= E_OUTOFMEMORY;
                Exit;
              end;

              pframeCur.m_cRotateKeys:= cKeys;
              //NOTE x files are w x y z and QUATERNIONS are x y z w

              pFileRotateKey:= PSRotateKeyXFile((DWord(pData) + (SizeOf(DWord)*2)));
              for iKey:= 0 to cKeys - 1 do
              begin
                pframeCur.m_pRotateKeys[iKey].dwTime:= pFileRotateKey.dwTime;
                pframeCur.m_pRotateKeys[iKey].quatRotate.x:= pFileRotateKey.x;
                pframeCur.m_pRotateKeys[iKey].quatRotate.y:= pFileRotateKey.y;
                pframeCur.m_pRotateKeys[iKey].quatRotate.z:= pFileRotateKey.z;
                pframeCur.m_pRotateKeys[iKey].quatRotate.w:= pFileRotateKey.w;

                Inc(pFileRotateKey);
              end;
            end
            else if (dwKeyType = 1) then
            begin
              if (pframeCur.m_pScaleKeys <> nil) then
              begin
                Result:= E_INVALIDARG;
                Exit;
              end;

              SetLength(pframeCur.m_pScaleKeys, cKeys);
              if (pframeCur.m_pScaleKeys = nil) then
              begin
                Result:= E_OUTOFMEMORY;
                Exit;
              end;

              pframeCur.m_cScaleKeys:= cKeys;

              pFileScaleKey:= PSScaleKeyXFile(DWord(pData) + (SizeOf(DWORD)*2));
              for iKey:= 0 to cKeys - 1 do
              begin
                pframeCur.m_pScaleKeys[iKey].dwTime:= pFileScaleKey.dwTime;
                pframeCur.m_pScaleKeys[iKey].vScale:= pFileScaleKey.vScale;

                Inc(pFileScaleKey);
              end;
            end
            else if (dwKeyType = 2) then
            begin
              if (pframeCur.m_pPositionKeys <> nil) then
              begin
                Result:= E_INVALIDARG;
                Exit;
              end;

              SetLength(pframeCur.m_pPositionKeys, cKeys);
              if (pframeCur.m_pPositionKeys = nil) then
              begin
                Result:= E_OUTOFMEMORY;
                Exit;
              end;

              pframeCur.m_cPositionKeys:= cKeys;

              pFilePosKey:= PSPositionKeyXFile(DWord(pData) + (SizeOf(DWORD)*2));
              for iKey:= 0 to cKeys - 1 do
              begin
                pframeCur.m_pPositionKeys[iKey].dwTime:= pFilePosKey.dwTime;
                pframeCur.m_pPositionKeys[iKey].vPos:= pFilePosKey.vPos;

                Inc(pFilePosKey);
              end;
            end
            else if (dwKeyType = 4) then
            begin
              if (pframeCur.m_pMatrixKeys <> nil) then
              begin
                Result:= E_INVALIDARG;
                Exit;
              end;

              SetLength(pframeCur.m_pMatrixKeys, cKeys);
              if (pframeCur.m_pMatrixKeys = nil) then
              begin
                Result:= E_OUTOFMEMORY;
                Exit;
              end;

              pframeCur.m_cMatrixKeys:= cKeys;

              pFileMatrixKey:= PSMatrixKeyXFile(DWord(pData) + (SizeOf(DWord)*2));
              for iKey:= 0 to cKeys - 1 do
              begin
                pframeCur.m_pMatrixKeys[iKey].dwTime:= pFileMatrixKey.dwTime;
                pframeCur.m_pMatrixKeys[iKey].mat:= pFileMatrixKey.mat;

                Inc(pFileMatrixKey);
              end;
            end
            else
            begin
              Result:= E_INVALIDARG;
              Exit;
            end;
          end;

          pxofobjChild:= nil;
        end;
      end;

      pxofChild:= nil;
    end;

  except
    on EOutOfMemory do Result:= E_OUTOFMEMORY;
   else raise;
  end;
end;

function TD3DAnimatedMesh.LoadAnimationSet(pxofobjCur: IDirectXFileData;
  pde: SDrawElement; options: DWord; fvf: DWord; pD3DDevice: IDirect3DDevice8;
  pframeParent: SFrame): HResult;
var
  pframeCur: SFrame;
  _type: PGUID;
  pxofobjChild: IDirectXFileData;
  pxofChild: IDirectXFileObject;
  cchName: DWord;
begin
  pframeCur:= SFrame.Create;
  if (pframeCur = nil) then
  begin
    Result:= E_OUTOFMEMORY;
    Exit;
  end;
  pframeCur.bAnimationFrame:= True;

  pframeParent.AddFrame(pframeCur);

  Result:= pxofobjCur.GetName(nil, cchName);
  if FAILED(Result) then Exit;

  if (cchName > 0) then
  begin
    SetLength(pframeCur.szName, cchName - 1);

    Result:= pxofobjCur.GetName(PChar(pframeCur.szName), cchName);
    if FAILED(Result) then Exit;
  end;

  // Enumerate child objects.
  // Child object can be data, data reference or binary.
  // Use QueryInterface() to find what type of object a child is.
  while SUCCEEDED(pxofobjCur.GetNextObject(pxofChild)) do
  begin
    // Query the child for it's FileData
    Result:= pxofChild.QueryInterface(IID_IDirectXFileData, pxofobjChild);
    if SUCCEEDED(Result) then
    begin
      Result:= pxofobjChild.GetType(_type);
      if FAILED(Result) then Exit;

      if EqualGUID(TID_D3DRMAnimation, _type^) then
      begin
        Result:= LoadAnimation(pxofobjChild, pde, options, fvf, pD3DDevice, pframeCur);
        if FAILED(Result) then Exit;
      end;

      pxofobjChild:= nil;
    end;

    pxofChild:= nil;
  end;
end;

function TD3DAnimatedMesh.LoadMesh(pxofobjCur: IDirectXFileData;
  options: DWord; fvf: DWord; pD3DDevice: IDirect3DDevice8;
  pframeParent: SFrame): HResult;
type
  PD3DXMaterialArray = ^TD3DXMaterialArray;
  TD3DXMaterialArray = array [0..0] of TD3DXMaterial;
var
  pmcMesh: SMeshContainer;
  pbufMaterials: ID3DXBuffer;
  pbufAdjacency: ID3DXBuffer;
  cchName: DWord;
  cFaces: UINT;
  iMaterial: UINT;
  m_pDevice: IDirect3DDevice8;
  pAdjacencyIn: PDWord;

  pMaterials: PD3DXMaterialArray;
  szPath: array [0..MAX_PATH - 1] of Char;
begin
  m_pDevice:= m_pd3dDevice;

  try
  pmcMesh:= SMeshContainer.Create;
  try

    Result:= pxofobjCur.GetName(nil, cchName);
    if FAILED(Result) then Exit;

    if (cchName > 0) then
    begin
      SetLength(pmcMesh.szName, cchName - 1);

      Result:= pxofobjCur.GetName(PChar(pmcMesh.szName), cchName);
      if FAILED(Result) then Exit;
    end;

    Result:= D3DXLoadSkinMeshFromXof(pxofobjCur, options, pD3DDevice,
               @pbufAdjacency, @pbufMaterials, @pmcMesh.cMaterials,
               @pmcMesh.m_pBoneNamesBuf, @pmcMesh.m_pBoneOffsetBuf,
               ID3DXMesh(pmcMesh.m_pSkinMesh));
    if FAILED(Result) then
    begin
      if (Result = D3DXERR_LOADEDMESHASNODATA) then Result := S_OK;
      Exit;
    end;

    cFaces:= pmcMesh.m_pSkinMesh.GetNumFaces;

    pAdjacencyIn:= PDWord(pbufAdjacency.GetBufferPointer);

    SetLength(pmcMesh.m_rgiAdjacency, cFaces * 3);

    // memcpy(pmcMesh->m_rgiAdjacency, pAdjacencyIn, cFaces * 3 * sizeof(DWORD));
    Move(pAdjacencyIn^, pmcMesh.m_rgiAdjacency[0], cFaces * 3 * SizeOf(DWORD));

    // Process skinning data
    if (pmcMesh.m_pSkinMesh.GetNumBones <> 0) then
    begin
      // Update max bones of any mesh in the app
      m_maxBones:= max(pmcMesh.m_pSkinMesh.GetNumBones, m_maxBones);

      SetLength(pmcMesh.m_pBoneMatrix, pmcMesh.m_pSkinMesh.GetNumBones);
      pmcMesh.m_pBoneOffsetMat:= PD3DXMatrixArray(pmcMesh.m_pBoneOffsetBuf.GetBufferPointer);

      Result:= GenerateMesh(pmcMesh);
    end
    else
    begin
      pmcMesh.m_pSkinMesh.GetOriginalMesh(pmcMesh.pMesh);
      pmcMesh.m_pSkinMesh:= nil;
      pmcMesh.cpattr:= pmcMesh.cMaterials;
    end;

    if ((pbufMaterials = nil) or (pmcMesh.cMaterials = 0)) then
    begin
      SetLength(pmcMesh.rgMaterials, 1);
      SetLength(pmcMesh.pTextures, 1);

      FillChar(pmcMesh.rgMaterials[0], SizeOf(TD3DMaterial8), 0);
      pmcMesh.rgMaterials[0].Diffuse.r:= 0.5;
      pmcMesh.rgMaterials[0].Diffuse.g:= 0.5;
      pmcMesh.rgMaterials[0].Diffuse.b:= 0.5;
      pmcMesh.rgMaterials[0].Specular:= pmcMesh.rgMaterials[0].Diffuse;
      pmcMesh.pTextures[0]:= nil;
    end
    else
    begin
      SetLength(pmcMesh.rgMaterials, pmcMesh.cMaterials);
      SetLength(pmcMesh.pTextures, pmcMesh.cMaterials);

      pMaterials:= PD3DXMaterialArray(pbufMaterials.GetBufferPointer);

      for iMaterial:= 0 to pmcMesh.cMaterials - 1 do
      begin
        pmcMesh.rgMaterials[iMaterial]:= pMaterials[iMaterial].MatD3D;

        pmcMesh.pTextures[iMaterial]:= nil;
        if (pMaterials[iMaterial].pTextureFilename <> nil) then
        begin
          DXUtil_FindMediaFile(szPath, pMaterials[iMaterial].pTextureFilename);

          Result:= D3DXCreateTextureFromFile(m_pDevice, szPath, pmcMesh.pTextures[iMaterial]);
          if FAILED(Result) then Exit;
        end;
      end;
    end;

    // add the mesh to the parent frame
    pframeParent.AddMesh(pmcMesh);
    pmcMesh:= nil;

  finally
    pmcMesh.Free;
  end;
  except
    on EOutOfMemory do Result:= E_OUTOFMEMORY;
   else raise;
  end;
end;

function TD3DAnimatedMesh.LoadFrames(pxofobjCur: IDirectXFileData; pde: SDrawElement;
      options: DWord; fvf: DWord; pD3DDevice: IDirect3DDevice8;
      pframeParent: SFrame): HResult;
var
  pxofobjChild: IDirectXFileData;
  pxofChild: IDirectXFileObject;
  cbSize: DWord;
  pmatNew: PD3DXMatrix;
  pframeCur: SFrame;
  cchName: DWord;
  _type: PGUID;
begin
  // Get the type of the object
  _type:= nil;
  Result:= pxofobjCur.GetType(_type);
  if FAILED(Result) then Exit;

  try
    if EqualGUID(_type^, TID_D3DRMMesh) then
    begin
      Result:= LoadMesh(pxofobjCur, options, fvf, pD3DDevice, pframeParent);
      if FAILED(Result) then Exit;
    end
    else if EqualGUID(_type^, TID_D3DRMFrameTransformMatrix) then
    begin
      Result:= pxofobjCur.GetData(nil, cbSize, Pointer(pmatNew));
      if FAILED(Result) then Exit;

      // update the parents matrix with the new one
      pframeParent.matRot:= pmatNew^;
      pframeParent.matRotOrig:= pmatNew^;
    end
    else if EqualGUID(_type^, TID_D3DRMAnimationSet) then
    begin
      LoadAnimationSet(pxofobjCur, pde, options, fvf, pD3DDevice, pframeParent);
    end
    else if EqualGUID(_type^, TID_D3DRMAnimation) then
    begin
      LoadAnimation(pxofobjCur, pde, options, fvf, pD3DDevice, pframeParent);
    end
    else if EqualGUID(_type^, TID_D3DRMFrame) then
    begin
      pframeCur:= SFrame.Create;

      Result:= pxofobjCur.GetName(nil, cchName);
      if FAILED(Result) then Exit;

      if (cchName > 0) then
      begin
        SetLength(pframeCur.szName, cchName - 1);

        Result:= pxofobjCur.GetName(PChar(pframeCur.szName), cchName);
        if FAILED(Result) then Exit;
      end;

      pframeParent.AddFrame(pframeCur);

      // Enumerate child objects.
      // Child object can be data, data reference or binary.
      // Use QueryInterface() to find what type of object a child is.
      while (SUCCEEDED(pxofobjCur.GetNextObject(pxofChild))) do 
      begin
        // Query the child for it's FileData
        Result:= pxofChild.QueryInterface(IID_IDirectXFileData, pxofobjChild);
        if SUCCEEDED(Result) then
        begin
          Result:= LoadFrames(pxofobjChild, pde, options, fvf, pD3DDevice, pframeCur);
          if FAILED(Result) then Exit;

          pxofobjChild:= nil;
        end;
        
        pxofChild:= nil;
      end;
    end;
  except
    on EOutOfMemory do Result:= E_OUTOFMEMORY;
   else raise;
  end;
end;

function TD3DAnimatedMesh.SetProjectionMatrix;
var
  mat: TD3DXMatrix;
  fAspect: Single;

  pBackBuffer: IDirect3DSurface8;

begin
  if (m_pdeHead = nil) then
  begin
    Result:= S_OK;
    Exit;
  end;

  // Store render target surface desc
  if not Assigned(m_pd3dDevice) then begin
    Result := E_POINTER;
    DXTrace(Unitname, $FFFFFFFF, Result, 'pointer to IDirect3DDevice8 interface is nil!!!');
    Exit;
  end;
  m_pd3dDevice.GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, pBackBuffer);
  pBackBuffer.GetDesc(m_d3dsdBackBuffer);
  SAFE_RELEASE(pBackBuffer);

  fAspect:= m_d3dsdBackBuffer.Width / m_d3dsdBackBuffer.Height;
  D3DXMatrixPerspectiveFovRH(mat, 0.25 * 3.141592654, fAspect,
    m_pdeSelected.fRadius / 64, m_pdeSelected.fRadius * 200);
  Result:= m_pd3dDevice.SetTransform(D3DTS_PROJECTION, mat);
  if FAILED(Result) then Exit;
  // Set Projection Matrix for vertex shader
  D3DXMatrixTranspose(mat, mat);
  Result:= m_pd3dDevice.SetVertexShaderConstant(2, mat, 4);
end;

type
  PD3DXBoneCombinationArray = ^TD3DXBoneCombinationArray;
  TD3DXBoneCombinationArray = array [0..0] of TD3DXBoneCombination;

function TD3DAnimatedMesh.DrawMeshContainer(pmcMesh: SMeshContainer): HResult;
var
  ipattr: UINT;
  pBoneComb: PD3DXBoneCombinationArray;
  numBlend: DWord;
  i: DWord;
  matid: DWord;
  Identity: TD3DXMatrix;
  cBones: DWord;
  iBone: DWord;

  AttribIdPrev: DWord;
  vConst: TD3DXVector4;
  pVB: IDirect3DVertexBuffer8;
  pIB: IDirect3DIndexBuffer8;
  mat: TD3DXMatrixA16;
  ambEmm: TD3DXColor;

begin
  // Store device Caps
  if not Assigned(m_pd3dDevice) then begin
    Result := E_POINTER;
    DXTrace(Unitname, $FFFFFFFF, Result, 'pointer to IDirect3DDevice8 interface is nil!!!');
    Exit;
  end;
  m_pd3dDevice.GetDeviceCaps(m_d3dCaps);

  if Assigned(pmcMesh.m_pSkinMesh) then
  begin
    if (m_method <> pmcMesh.m_Method) then GenerateMesh(pmcMesh);

    if (m_method = D3DNONINDEXED) then
    begin
      AttribIdPrev := UNUSED32;
      pBoneComb:= PD3DXBoneCombinationArray(pmcMesh.m_pBoneCombinationBuf.GetBufferPointer);
      // Draw using default vtx processing of the device (typically HW)
      for ipattr:= 0 to pmcMesh.cpattr - 1 do
      begin
        numBlend:= 0;

        for i:= 0 to pmcMesh.m_maxFaceInfl - 1 do
          if (pBoneComb[ipattr].BoneId[i] <> UINT_MAX) then numBlend:= i;

        if (m_d3dCaps.MaxVertexBlendMatrices >= numBlend + 1) then
        begin
          for i:= 0 to pmcMesh.m_maxFaceInfl - 1 do
          begin
            matid:= pBoneComb[ipattr].BoneId[i];
            if (matid <> UINT_MAX) then
            begin
              m_pd3dDevice.SetTransform(D3DTS_WORLDMATRIX(i), pmcMesh.m_pBoneMatrix[matid]^);
              m_pd3dDevice.MultiplyTransform(D3DTS_WORLDMATRIX(i), pmcMesh.m_pBoneOffsetMat[matid]);
            end;
          end;

          m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND, numBlend);

          if (AttribIdPrev <> pBoneComb[ipattr].AttribId) or (AttribIdPrev = UNUSED32) then 
          begin
            m_pd3dDevice.SetMaterial(pmcMesh.rgMaterials[pBoneComb[ipattr].AttribId]);
            m_pd3dDevice.SetTexture(0, pmcMesh.pTextures[pBoneComb[ipattr].AttribId]);
            AttribIdPrev := pBoneComb[ipattr].AttribId;
          end;

          Result := pmcMesh.pMesh.DrawSubset(ipattr);
          if FAILED(Result) then Exit;
        end;
      end;

      // If necessary, draw parts that HW could not handle using SW
      if (pmcMesh.iAttrSplit < pmcMesh.cpattr) then
      begin
        AttribIdPrev := UNUSED32;
        m_pd3dDevice.SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, iTRUE);
        for ipattr:= pmcMesh.iAttrSplit to pmcMesh.cpattr - 1 do
        begin
          numBlend:= 0;

          for i:= 0 to pmcMesh.m_maxFaceInfl - 1 do
            if (pBoneComb[ipattr].BoneId[i] <> UINT_MAX) then numBlend:= i;

          if (m_d3dCaps.MaxVertexBlendMatrices < numBlend + 1) then
          begin
            for i:= 0 to pmcMesh.m_maxFaceInfl - 1 do
            begin
              matid := pBoneComb[ipattr].BoneId[i];
              if (matid <> UINT_MAX) then
              begin
                m_pd3dDevice.SetTransform(D3DTS_WORLDMATRIX(i), pmcMesh.m_pBoneMatrix[matid]^);
                m_pd3dDevice.MultiplyTransform(D3DTS_WORLDMATRIX(i), pmcMesh.m_pBoneOffsetMat[matid]);
              end;
            end;

            m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND, numBlend);

            if (AttribIdPrev <> pBoneComb[ipattr].AttribId) or (AttribIdPrev = UNUSED32) then
            begin
              m_pd3dDevice.SetMaterial(pmcMesh.rgMaterials[pBoneComb[ipattr].AttribId]);
              m_pd3dDevice.SetTexture(0, pmcMesh.pTextures[pBoneComb[ipattr].AttribId]);
              AttribIdPrev := pBoneComb[ipattr].AttribId;
            end;

            Result:= pmcMesh.pMesh.DrawSubset(ipattr);
            if FAILED(Result) then Exit;
          end;
        end;
        m_pd3dDevice.SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, iFALSE);
      end;
      m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND, 0);
    end
    else if (m_method = D3DINDEXEDVS) then
    begin
      // Use COLOR instead of UBYTE4 since Geforce3 does not support it
      // vConst.w should be 3, but due to about hack, mul by 255 and add epsilon
      vConst:= D3DXVector4(1.0, 0.0, 0.0, 765.01);

      if pmcMesh.m_bUseSW then
        m_pd3dDevice.SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, iTRUE);

      pmcMesh.pMesh.GetVertexBuffer(pVB);
      pmcMesh.pMesh.GetIndexBuffer(pIB);
      Result:= m_pd3dDevice.SetStreamSource(0, pVB, D3DXGetFVFVertexSize(pmcMesh.pMesh.GetFVF));
      if FAILED(Result) then Exit;
      Result:= m_pd3dDevice.SetIndices(pIB, 0);
      if FAILED(Result) then Exit;
      pVB:= nil;
      pIB:= nil;

      Result:= m_pd3dDevice.SetVertexShader(m_dwIndexedVertexShader[pmcMesh.m_maxFaceInfl - 1]);
      if FAILED(Result) then Exit;

      pBoneComb:= PD3DXBoneCombinationArray(pmcMesh.m_pBoneCombinationBuf.GetBufferPointer);
      for ipattr:= 0 to pmcMesh.cpattr - 1 do
      begin
        for i:= 0 to pmcMesh.m_paletteSize - 1 do
        begin
          matid := pBoneComb[ipattr].BoneId[i];
          if (matid <> UINT_MAX) then
          begin
            D3DXMatrixMultiply(mat, pmcMesh.m_pBoneOffsetMat[matid], pmcMesh.m_pBoneMatrix[matid]^);
            D3DXMatrixMultiplyTranspose(mat, mat, m_mView);
            {$IFDEF DXG_COMPAT}
            m_pd3dDevice.SetVertexShaderConstant(i*3 + 9, @mat, 3);
            {$ELSE}
            m_pd3dDevice.SetVertexShaderConstant(i*3 + 9, mat, 3);
            {$ENDIF}
          end;
        end;

        // Sum of all ambient and emissive contribution
        D3DXColorModulate(ambEmm, TD3DXColor(pmcMesh.rgMaterials[pBoneComb[ipattr].AttribId].Ambient), D3DXColor(0.25, 0.25, 0.25, 1.0));
        D3DXColorAdd(ambEmm, ambEmm, TD3DXColor(pmcMesh.rgMaterials[pBoneComb[ipattr].AttribId].Emissive));
        {$IFDEF DXG_COMPAT}
        m_pd3dDevice.SetVertexShaderConstant(8, @pmcMesh.rgMaterials[pBoneComb[ipattr].AttribId].Diffuse, 1);
        m_pd3dDevice.SetVertexShaderConstant(7, @ambEmm, 1);
        {$ELSE}
        m_pd3dDevice.SetVertexShaderConstant(8, pmcMesh.rgMaterials[pBoneComb[ipattr].AttribId].Diffuse, 1);
        m_pd3dDevice.SetVertexShaderConstant(7, ambEmm, 1);
        {$ENDIF}
        vConst.y := pmcMesh.rgMaterials[pBoneComb[ipattr].AttribId].Power;
        {$IFDEF DXG_COMPAT}
        m_pd3dDevice.SetVertexShaderConstant(0, @vConst, 1);
        {$ELSE}
        m_pd3dDevice.SetVertexShaderConstant(0, vConst, 1);
        {$ENDIF}

        m_pd3dDevice.SetTexture(0, pmcMesh.pTextures[pBoneComb[ipattr].AttribId]);

        Result:= m_pd3dDevice.DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
                                pBoneComb[ipattr].VertexStart, pBoneComb[ipattr].VertexCount,
                                pBoneComb[ipattr].FaceStart * 3, pBoneComb[ipattr].FaceCount);
        if FAILED(Result) then Exit;
      end;

      if (pmcMesh.m_bUseSW) then
        m_pd3dDevice.SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, iFALSE);
    end
    else if (m_method = D3DINDEXED) then
    begin
      if (pmcMesh.m_bUseSW) then
        m_pd3dDevice.SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, DWord(TRUE));

      if (pmcMesh.m_maxFaceInfl = 1)
        then m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_0WEIGHTS)
        else m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND, pmcMesh.m_maxFaceInfl - 1);

      if (pmcMesh.m_maxFaceInfl <> 0) then
        m_pd3dDevice.SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, DWord(TRUE));
      pBoneComb:= PD3DXBoneCombinationArray(pmcMesh.m_pBoneCombinationBuf.GetBufferPointer);
      for ipattr:= 0 to pmcMesh.cpattr - 1 do
      begin
        for i:= 0 to pmcMesh.m_paletteSize - 1 do
        begin
          matid:= pBoneComb[ipattr].BoneId[i];
          if (matid <> UINT_MAX) then
          begin
            m_pd3dDevice.SetTransform(D3DTS_WORLDMATRIX(i), pmcMesh.m_pBoneMatrix[matid]^);
            m_pd3dDevice.MultiplyTransform(D3DTS_WORLDMATRIX(i), pmcMesh.m_pBoneOffsetMat^[matid]);
          end;
        end;

        m_pd3dDevice.SetMaterial(pmcMesh.rgMaterials[pBoneComb[ipattr].AttribId]);
        m_pd3dDevice.SetTexture(0, pmcMesh.pTextures[pBoneComb[ipattr].AttribId]);

        Result:= pmcMesh.pMesh.DrawSubset( ipattr );
        if FAILED(Result) then Exit;
      end;
      m_pd3dDevice.SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, DWord(FALSE));
      m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND, 0);

      if (pmcMesh.m_bUseSW) then
        m_pd3dDevice.SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, DWord(FALSE));
    end
    else if (m_method = SOFTWARE) then
    begin
      cBones:= pmcMesh.m_pSkinMesh.GetNumBones;

      // set up bone transforms
      for iBone:= 0 to cBones - 1 do
      begin
        D3DXMatrixMultiply
        (
          m_pBoneMatrices[iBone],                 // output
          pmcMesh.m_pBoneOffsetMat^[iBone],
          pmcMesh.m_pBoneMatrix[iBone]^
        );
      end;

      // set world transform
      D3DXMatrixIdentity(Identity);
      Result:= m_pd3dDevice.SetTransform(D3DTS_WORLD, Identity);
      if FAILED(Result) then Exit;

      // generate skinned mesh
      Result:= pmcMesh.m_pSkinMesh.UpdateSkinnedMesh(m_pBoneMatrices[0], nil, pmcMesh.pMesh);
      if FAILED(Result) then Exit;

      for ipattr:= 0 to pmcMesh.cpattr - 1 do
      begin
        m_pd3dDevice.SetMaterial(pmcMesh.rgMaterials[pmcMesh.m_pAttrTable[ipattr].AttribId]);
        m_pd3dDevice.SetTexture(0, pmcMesh.pTextures[pmcMesh.m_pAttrTable[ipattr].AttribId]);
        Result:= pmcMesh.pMesh.DrawSubset(pmcMesh.m_pAttrTable[ipattr].AttribId);
        if FAILED(Result) then Exit;
      end;
      Exit;
    end;
  end
  else // if Assigned(pmcMesh.m_pSkinMesh) -> assigned non-skinned mesh
  begin
    for ipattr:= 0 to pmcMesh.cpattr - 1 do
    begin
      m_pd3dDevice.SetMaterial(pmcMesh.rgMaterials[ipattr]);
      m_pd3dDevice.SetTexture(0, pmcMesh.pTextures[ipattr]);
      Result:= pmcMesh.pMesh.DrawSubset(ipattr);
      if FAILED(Result) then Exit;
    end;
  end;

  Result:= S_OK;
end;



function TD3DAnimatedMesh.DeleteSelectedMesh: HResult;
var
  pdeCur:  SDrawElement;
  pdePrev: SDrawElement;
begin
  if (m_pdeSelected <> nil) then
  begin
    pdeCur:= m_pdeHead;
    pdePrev:= nil;
    while ((pdeCur <> nil) and (pdeCur <> m_pdeSelected)) do
    begin
      pdePrev:= pdeCur;
      pdeCur:= pdeCur.pdeNext;
    end;

    if (pdePrev = nil) then
    begin
      m_pdeHead:= m_pdeHead.pdeNext;
    end
    else
    begin
      pdePrev.pdeNext:= pdeCur.pdeNext;
    end;

    m_pdeSelected.pdeNext:= nil;
    if (m_pdeHead = m_pdeSelected) then m_pdeHead:= nil;
    m_pdeSelected.Free;
    m_pdeSelected:= nil;
  end;

  Result:= S_OK;
end;

function TD3DAnimatedMesh.DrawFrames(pframeCur: SFrame; var cTriangles: UINT): HResult;
var
  pmcMesh: SMeshContainer;
  pframeChild: SFrame;
begin
  if (pframeCur.pmcMesh <> nil) then
  begin
    Result:= m_pd3dDevice.SetTransform(D3DTS_WORLD, pframeCur.matCombined);
    if FAILED(Result) then Exit;
  end;

  pmcMesh:= pframeCur.pmcMesh;
  while (pmcMesh <> nil) do
  begin
    Result:= DrawMeshContainer(pmcMesh);
    if FAILED(Result) then Exit;

    cTriangles:= cTriangles + pmcMesh.pMesh.GetNumFaces;

    pmcMesh:= pmcMesh.pmcNext;
  end;

  pframeChild:= pframeCur.pframeFirstChild;
  while (pframeChild <> nil) do
  begin
    Result:= DrawFrames(pframeChild, cTriangles);
    if FAILED(Result) then Exit;

    pframeChild:= pframeChild.pframeSibling;
  end;

  Result:= S_OK;
end;

function TD3DAnimatedMesh.UpdateFrames(pframeCur: SFrame; var matCur: TD3DXMatrix): HResult;
var
  pframeChild: SFrame;
begin
  pframeCur.matCombined:= matCur;
  D3DXMatrixMultiply(pframeCur.matCombined, pframeCur.matRot, matCur);
  D3DXMatrixMultiply(pframeCur.matCombined, pframeCur.matCombined, pframeCur.matTrans);
  pframeChild:= pframeCur.pframeFirstChild;
  while (pframeChild <> nil) do
  begin
    Result:= UpdateFrames(pframeChild, pframeCur.matCombined);
    if FAILED(Result) then Exit;

    pframeChild := pframeChild.pframeSibling;
  end;
  Result:= S_OK;
end;

function TD3DAnimatedMesh.GenerateMesh(pmcMesh: SMeshContainer): HResult;
var
  pDevice: IDirect3DDevice8;
  rgBoneCombinations: PD3DXBoneCombinationArray;
  cInfl: DWORD;
  iInfl: DWORD;
  pMeshTmp: ID3DXMesh;
  flags: DWORD;
  newFVF: DWORD;
  pMesh: ID3DXMesh;
  maxFaceInfl: DWORD;
label
  e_Exit;
begin
  // ASSUMPTION:  pmcMesh->m_rgiAdjacency contains the current adjacency
  pDevice:= nil;

  Result:= pmcMesh.m_pSkinMesh.GetDevice(pDevice);
  if FAILED(Result) then goto e_Exit;

  pmcMesh.pMesh:= nil;
  m_pBoneMatrices   := nil; // == "delete [] m_pBoneMatrices;"

  pmcMesh.pMesh     := nil;
  m_pBoneMatrices   := nil;

  if (m_method = D3DNONINDEXED) then
  begin
    Result := pmcMesh.m_pSkinMesh.ConvertToBlendedMesh
                (
                 D3DXMESH_MANAGED or D3DXMESHOPT_VERTEXCACHE,
                 @pmcMesh.m_rgiAdjacency[0],
                 nil,
                 pmcMesh.cpattr,
                 pmcMesh.m_pBoneCombinationBuf,
                 nil,
                 nil,
                 pmcMesh.pMesh
                );
    if FAILED(Result) then goto e_Exit;

    // calculate the max face influence count

    if ((pmcMesh.pMesh.GetFVF and D3DFVF_POSITION_MASK) <> D3DFVF_XYZ) then
    begin
      pmcMesh.m_maxFaceInfl := 1 + ((pmcMesh.pMesh.GetFVF and D3DFVF_POSITION_MASK) - D3DFVF_XYZRHW) div 2;
    end else
    begin
      pmcMesh.m_maxFaceInfl := 1;
    end;

    (* If the device can only do 2 matrix blends, ConvertToBlendedMesh cannot approximate all meshes to it
       Thus we split the mesh in two parts: The part that uses at most 2 matrices and the rest. The first is
       drawn using the device's HW vertex processing and the rest is drawn using SW vertex processing. *)
    if (m_d3dCaps.MaxVertexBlendMatrices <= 2) then
    begin
      // calculate the index of the attribute table to split on
      rgBoneCombinations:= PD3DXBoneCombinationArray(pmcMesh.m_pBoneCombinationBuf.GetBufferPointer);

      // for (pmcMesh->iAttrSplit = 0; pmcMesh->iAttrSplit < pmcMesh->cpattr; pmcMesh->iAttrSplit++)
      pmcMesh.iAttrSplit := 0;
      while (pmcMesh.iAttrSplit < pmcMesh.cpattr) do
      begin
        cInfl:= 0;

        for iInfl:= 0 to pmcMesh.m_maxFaceInfl - 1 do
        begin
          if (rgBoneCombinations[pmcMesh.iAttrSplit].BoneId[iInfl] <> UINT_MAX) then
          begin
            Inc(cInfl);
          end;
        end;

        if (cInfl > m_d3dCaps.MaxVertexBlendMatrices) then Break;
        Inc(pmcMesh.iAttrSplit);
      end;

      // if there is both HW and SW, add the Software Processing flag
      if (pmcMesh.iAttrSplit < pmcMesh.cpattr) then
      begin
        Result:= pmcMesh.pMesh.CloneMeshFVF(D3DXMESH_SOFTWAREPROCESSING or pmcMesh.pMesh.GetOptions,
                                             pmcMesh.pMesh.GetFVF,
                                             pDevice, pMeshTmp);
        if FAILED(Result) then goto e_Exit;

        pmcMesh.pMesh := pMeshTmp;
        pMeshTmp := nil;
      end;
    end else
    begin
      pmcMesh.iAttrSplit := pmcMesh.cpattr;
    end;
  end
  else if (m_method = D3DINDEXEDVS) then
  begin
    // Get palette size
    pmcMesh.m_paletteSize := min(28, pmcMesh.m_pSkinMesh.GetNumBones);

    flags := D3DXMESHOPT_VERTEXCACHE;
    if (m_d3dCaps.VertexShaderVersion >= D3DVS_VERSION(1, 1)) then
    begin
      pmcMesh.m_bUseSW := False;
      flags:= flags or D3DXMESH_MANAGED;
    end else
    begin
      pmcMesh.m_bUseSW := True;
      flags:= flags or D3DXMESH_SYSTEMMEM;
    end;

    Result:= pmcMesh.m_pSkinMesh.ConvertToIndexedBlendedMesh(
      flags, @pmcMesh.m_rgiAdjacency[0], pmcMesh.m_paletteSize, nil,
      pmcMesh.cpattr, pmcMesh.m_pBoneCombinationBuf, nil, nil, pmcMesh.pMesh);
    if FAILED(Result) then goto e_Exit;

    if ((pmcMesh.pMesh.GetFVF and D3DFVF_POSITION_MASK) <> D3DFVF_XYZ) then
    begin
      pmcMesh.m_maxFaceInfl := ((pmcMesh.pMesh.GetFVF and D3DFVF_POSITION_MASK) - D3DFVF_XYZRHW) div 2;
    end else
    begin
      pmcMesh.m_maxFaceInfl := 1;
    end;

    // FVF has to match our declarator. Vertex shaders are not as forgiving as FF pipeline
    newFVF:= (pmcMesh.pMesh.GetFVF and D3DFVF_POSITION_MASK) or D3DFVF_NORMAL or D3DFVF_TEX1 or D3DFVF_LASTBETA_UBYTE4;
    if (newFVF <> pmcMesh.pMesh.GetFVF) then
    begin
      Result:= pmcMesh.pMesh.CloneMeshFVF(pmcMesh.pMesh.GetOptions, newFVF, pDevice, pMesh);
      if not FAILED(Result) then
      begin
        pmcMesh.pMesh := pMesh;
        pMesh := nil;
      end;
    end;
  end
  else if (m_method = D3DINDEXED) then
  begin
    flags := D3DXMESHOPT_VERTEXCACHE;

    Result:= pmcMesh.m_pSkinMesh.GetMaxFaceInfluences(maxFaceInfl);
    if FAILED(Result) then goto e_Exit;

    // 12 entry palette guarantees that any triangle (4 independent influences per vertex of a tri)
    // can be handled
    maxFaceInfl:= min(maxFaceInfl, 12);

    if (m_d3dCaps.MaxVertexBlendMatrixIndex + 1 < maxFaceInfl) then
    begin
      // HW does not support indexed vertex blending. Use SW instead
      pmcMesh.m_paletteSize := min(256, pmcMesh.m_pSkinMesh.GetNumBones);
      pmcMesh.m_bUseSW := True;
      flags := flags or D3DXMESH_SYSTEMMEM;
    end else
    begin
      pmcMesh.m_paletteSize := min(m_d3dCaps.MaxVertexBlendMatrixIndex + 1, pmcMesh.m_pSkinMesh.GetNumBones);
      pmcMesh.m_bUseSW := False;
      flags := flags or D3DXMESH_MANAGED;
    end;

    Result := pmcMesh.m_pSkinMesh.ConvertToIndexedBlendedMesh(
      flags, @pmcMesh.m_rgiAdjacency[0], pmcMesh.m_paletteSize, nil,
      pmcMesh.cpattr, pmcMesh.m_pBoneCombinationBuf, nil, nil, pmcMesh.pMesh);
    if FAILED(Result) then goto e_Exit;

    // Here we are talking of max vertex influence which we determine from 
    // the FVF of the returned mesh
    if ((pmcMesh.pMesh.GetFVF and D3DFVF_POSITION_MASK) <> D3DFVF_XYZ) then
    begin
      pmcMesh.m_maxFaceInfl := ((pmcMesh.pMesh.GetFVF and D3DFVF_POSITION_MASK) - D3DFVF_XYZRHW) div 2;
    end else
    begin
      pmcMesh.m_maxFaceInfl := 1;
    end;
  end
  else if (m_method = SOFTWARE) then
  begin
    Result := pmcMesh.m_pSkinMesh.GenerateSkinnedMesh
                               (
                                 D3DXMESH_WRITEONLY,         // options
                                 0.0,                        // minimum bone weight allowed
                                 @pmcMesh.m_rgiAdjacency[0], // adjacency of in-mesh
                                 nil,                        // adjacency of out-mesh
                                 nil,                        // face remap array
                                 nil,                        // vertex remap buffer
                                 pmcMesh.pMesh               // out-mesh
                              );
    if FAILED(Result) then goto e_Exit;

    Result := pmcMesh.pMesh.GetAttributeTable(nil, @pmcMesh.cpattr);
    if FAILED(Result) then goto e_Exit;

    pmcMesh.m_pAttrTable:= nil; // == delete[] pmcMesh->m_pAttrTable;
    SetLength(pmcMesh.m_pAttrTable, pmcMesh.cpattr);
    { // in Delphi Exception will be raised if "OutOfmemory condition" occurs
    if (pmcMesh.m_pAttrTable = nil) then
    begin
        hr := E_OUTOFMEMORY;
        goto e_Exit;
    end;
    }

    Result := pmcMesh.pMesh.GetAttributeTable(@pmcMesh.m_pAttrTable[0], nil);
    if FAILED(Result) then goto e_Exit;

    Result := pmcMesh.m_pSkinMesh.GetMaxFaceInfluences(pmcMesh.m_maxFaceInfl);
    if FAILED(Result) then goto e_Exit;

    // Allocate space for blend matrices
    SetLength(m_pBoneMatrices, m_maxBones);
    { // in Delphi Exception will be raised if "OutOfmemory condition" occurs
    if (m_pBoneMatrices = nil) then
    begin
        Result:= E_OUTOFMEMORY;
        goto e_Exit;
    end;
    }
  end;
  pmcMesh.m_Method := m_method;

e_Exit:

  pDevice:= nil;
end;


end.

