//-----------------------------------------------------------------------------
// File: SkinnedMesh.cpp
//
// Desc: Example code showing how to use animated models with skinning.
//
// Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
(*----------------------------------------------------------------------------*
 *  Direct3D sample from DirectX 8.1 SDK Delphi adaptation by Alexey Barkovoy *
 *  E-Mail: clootie@reactor.ru                                                *
 *                                                                            *
 *  Modified: 05-Oct-2002                                                     *
 *                                                                            *
 *  Latest version can be downloaded from:                                    *
 *     http://clootie.narod.ru/delphi                                         *
 *----------------------------------------------------------------------------*)
{
// (c) Мега Информатик 2006-2008

//  ---------------------------------------
//  http://www.megainformatic.boom.ru
//  http://www.megainfromaticsite.pochta.ru
//  ---------------------------------------

   //эксперименты по использованию анимации
   //10-11.01.2008

}


unit SkinnedMeshUnit;

interface

//{$I DirectX.inc}

uses
  Windows, Messages, SysUtils, CommDlg,
  D3DX8, DXFile,
  D3DApp, D3DFont, D3DUtil, DXUtil,
  Direct3D8, DX8_DIUtil8, D3DAbstractApp, D3DFile,
  Resource;


///////////////////////////////////////////////////////////////////////////////
//
// File: mview.h
//
// Copyright (C) 2000-2001 Microsoft Corporation. All Rights Reserved.
//
//
///////////////////////////////////////////////////////////////////////////////

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;
    matRot:             TD3DXMatrix;
    matTrans:           TD3DXMatrix;
    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
    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;

function CalculateBoundingSphere(pdeCur: SDrawElement): HResult;

const
  CameraLeft = isLeft;
  CameraRight = isRight;
  CameraForward = isUp;
  CameraBackward = isDown;

  GameCommand = isButton1;
  GameContextCommand = isButton2;
  CameraZoomIn = isButton4;
  CameraZoomOut = isButton8;

  CameraRotateCC = isButton5;
  CameraRotateCW = isButton9;
  CameraRotateUp = isButton6;
  CameraRotateDown = isButton7;

  GamePause = isButton10;
  GameCheat = isButton11;

  GameMenu = isButton12;

  CameraUp = isButton13;
  CameraDown = isButton14;

  CameraChangeView = isButton15;

  ToggleShowFPS = isButton16;



  PlanetModel = 'Media\Models\nanozone1.x';

  D3DFVF_MODELVERTEX = D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_TEX1;

  
type

  TMODELVERTEX = record
    p: TD3DXVECTOR3;
    n: TD3DXVECTOR3;
    tu, tv: Single;
  end;

  PModelVertexArray = ^TModelVertexArray;
  TModelVertexArray = array[0..0] of TModelVertex;


  // Struct to store the current input state
  TUserInput = record
    fAxisMoveUD: Single;
    fAxisRotateLR: Single;
    bButtonFireWeapons: Boolean;
    bButtonEnableShield: Boolean;

    bButtonRotateLeft, bButtonRotateRight,
    bButtonForwardThrust, bButtonReverseThrust: Boolean;

    bEnterMenu: Boolean;
    // One-shot variables
    bDoChangeView: Boolean;

    // Menu input variables
    bDoMenuUp: Boolean;
    bDoMenuDown: Boolean;
    bDoMenuSelect: Boolean;
    bDoMenuQuit: Boolean;

    bDoNextLevel: Boolean; //for debug purpose
  end;

  PUserInput = ^TUserInput;

  TAbstractApp = class(TD3DAbstractApp)
  public
    constructor Create;
    destructor Destroy; override;

    procedure WindowProcedure(var Message: TMessage); override;
  end;

  TPlayer = class(TObject)
  public
    fRoll: Single;

    fAngle: Single;

    bExploded: Boolean;
    fShowDelay: Single;

    vPos, vVel: TD3DXVECTOR3;    // Position, Velocity

    constructor Create(p: TD3DVECTOR);
  end;


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

  TAnimatedModel = class
  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;

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

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

    m_szPath:           array [0..MAX_PATH-1] of Char;
    m_pBoneMatrices:    array of TD3DXMatrix;
    m_maxBones:         DWord;

    m_pd3dDevice: IDirect3DDevice8;
    m_method: METHOD;

    m_matWorld: TD3DXMatrixA16;

    //m_ArcBall:  CD3DArcBall;
    //m_Camera: CD3DCamera;
    m_matRotation, m_matTranslation: TD3DXMatrixA16;

    Pos: TD3DXVector3; 

    constructor Create;
    destructor Destroy; override;
    function Render: HResult;
    function FrameMove: HResult;
  end;

//-----------------------------------------------------------------------------
// Name: class CMyD3DApplication
// Desc: Application class. The base class (CD3DApplication) provides the
//       generic functionality needed in all Direct3D samples. CMyD3DApplication
//       adds functionality specific to this sample program.
//-----------------------------------------------------------------------------
  CMyD3DApplication = class(CD3DApplication)
    m_pFont:    CD3DFont;

    m_method:   METHOD;

    m_dwFVF:    DWord;

    m_ArcBall:  CD3DArcBall;

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

    m_szPath:           array [0..MAX_PATH-1] of Char;
    m_pBoneMatrices:    array of TD3DXMatrix;
    m_maxBones:         DWord;

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

    SomeModel: TAnimatedModel;

    {m_Camera //для экспериментов по портированию}
    //m_Camera: CD3DCamera;                  // Camera used for 3D scene

    m_DXInput: TDXInput;     //управление игрой через DirectInput
    m_UserInput: TUserInput; // Struct for storing user input
    m_bShowFPSOn: Boolean;

    AbstractApp: TD3DAbstractApp;

    m_dwViewMode: Cardinal;
    m_fViewTransition: Single;
    m_bFirstPersonView: Boolean;

    PlanetMesh: CD3DMesh;
    PlanetMeshWorldMatrix,
    PlanetMeshViewMatrix,
    PlanetMeshProjMatrix: TD3DXMatrix;

  protected
    function OneTimeSceneInit: HResult; override;
    function InitDeviceObjects: HResult; override;
    function RestoreDeviceObjects: HResult; override;
    function InvalidateDeviceObjects: HResult; override;
    function DeleteDeviceObjects: HResult; override;
    function Render: HResult; override;
    function FrameMove: HResult; override;
    function FinalCleanup: HResult; override;
    function ConfirmDevice(var pCaps: TD3DCaps8; dwBehavior: DWord; Format: TD3DFormat): HResult; override;





    // Input functions
    procedure SetDefaultDefKeys;
    procedure CreateInputObjects;
    procedure DestroyInputObjects;
    procedure UpdateInput;

    procedure UpdateCameraView;
    function LoadTerrain: HResult;

  public
    m_pPlayer: TPlayer;                   // Player's display object

    constructor Create;
    function MsgProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM ): LRESULT; override;
  end;

function EqualGUID(const G1,G2: TGUID): Boolean;
procedure WinMain;

implementation

uses Math;


var
  d3dApp: CMyD3DApplication;

  //**
  OpenMenuItemSel: Boolean = False;
  //**



const
  UINT_MAX      = $FFFFFFFF;

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


procedure WinMain;
begin
  d3dApp:= CMyD3DApplication.Create;
  // "Create_" isn't a constructor is regular function
  if FAILED(d3dApp.Create_(HInstance)) then Exit;
  d3dApp.Run;
end;

// Simple function to define "hilliness" for terrain
function HeightField(x, z: Single): Single;
begin
  result := (cos(x/2.0+0.2)*cos(z/1.5-0.2)+1.0) - 2.0;
end;

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


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



///////////////////////////////////////////////////////////////////////////////
//  CMyD3DApplication  ////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
// Name: CMyD3DApplication()
// Desc: Application constructor. Sets attributes for the app.
//-----------------------------------------------------------------------------
constructor CMyD3DApplication.Create;
begin
  inherited Create(0);
  m_strWindowTitle   := 'Skinned Mesh';
  m_bUseDepthBuffer  := TRUE;
  m_bShowCursorWhenFullscreen := TRUE;

  m_pFont               := CD3DFont.Create( 'Arial', 8);
  m_ArcBall             := CD3DArcBall.Create;
  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;

  m_szPath:= #0;

  m_bShowFPSOn := True;
  m_DXInput := nil;
  m_pPlayer := nil;

  PlanetMesh := nil;

  SomeModel := TAnimatedModel.Create;
end;

//-----------------------------------------------------------------------------
// Name: ConfirmDevice()
// Desc: Called during device intialization, this code checks the device
//       for some minimum set of capabilities
//-----------------------------------------------------------------------------
function CMyD3DApplication.ConfirmDevice;
begin
  // This sample wants mixed vertex processing rather than hardware
  // vertex processing so it can fallback to sw processing if the
  // device supports fewer than three matrices when skinning.
  if (dwBehavior and D3DCREATE_HARDWARE_VERTEXPROCESSING) <> 0 then
  begin
    Result:= E_FAIL;
    Exit;
  end;

  Result:= S_OK;
end;

//-----------------------------------------------------------------------------
// Name: OneTimeSceneInit()
// Desc: Called during initial app startup, this function performs all the
//       permanent initialization.
//-----------------------------------------------------------------------------
function CMyD3DApplication.OneTimeSceneInit;
begin
  // Set cursor to indicate that user can move the object with the mouse
  SetClassLong(m_hWnd, GCL_HCURSOR, LoadCursor(0, IDC_SIZEALL));

  AbstractApp := TAbstractApp.Create;
  AbstractApp.m_hWnd := m_hWnd;

  CreateInputObjects;

  // Add a ship to the displaylist
  m_pPlayer := TPlayer.Create(D3DXVECTOR3(0.0, 0.0, 0.0) );

  Result:= S_OK;  
end;

//-----------------------------------------------------------------------------
// Name: FrameMove()
// Desc: Called once per frame, the call is the entry point for animating
//       the scene.
//-----------------------------------------------------------------------------
function CMyD3DApplication.FrameMove;
var
  //**
    vEyePt, vLookatPt, vUpVec: TD3DXVECTOR3;
  //**
begin
  UpdateInput;

  SomeModel.FrameMove;

  //**
    // View mode 0 (third person)
    with vEyePt do begin
     x := 1.5;
     y := 1.56;
     z := 1.5;
    end;
    with vLookatPt do begin
      x := 0;
      y := 0.06;
      z := 0;
    end;
    vUpVec := D3DXVECTOR3( 0.0, 1.0, 0.0 );

    //m_Camera.SetViewParams( vEyePt, vLookatPt, vUpVec );
  //**

  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 CMyD3DApplication.Render;
var
  matRot, matTrans, matScale: TD3DXMatrix;

  strBuffer: array[0..MAX_PATH] of Char;  

begin

  // Clear the viewport
  m_pd3dDevice.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER,
    $FF000000, 1.0, 0 );

  // Begin the scene
  if SUCCEEDED(m_pd3dDevice.BeginScene) then
  begin
    matRot:=   m_ArcBall.GetRotationMatrix^;
    matTrans:= m_ArcBall.GetTranslationMatrix^;

    D3DXMatrixScaling(matScale, 1, 1, 1);

    D3DXMatrixIdentity(PlanetMeshWorldMatrix);
    D3DXMatrixIdentity(PlanetMeshViewMatrix);

    D3DXMatrixMultiply(PlanetMeshWorldMatrix, PlanetMeshWorldMatrix, matScale);

    D3DXMatrixMultiply(PlanetMeshViewMatrix, PlanetMeshViewMatrix, matRot);
    D3DXMatrixMultiply(PlanetMeshViewMatrix, PlanetMeshViewMatrix, matTrans);

    D3DXMatrixMultiply(PlanetMeshViewMatrix, PlanetMeshViewMatrix,
      PlanetMeshWorldMatrix);



    m_pd3dDevice.SetTransform(D3DTS_WORLD, PlanetMeshWorldMatrix);
    m_pd3dDevice.SetTransform(D3DTS_VIEW, PlanetMeshViewMatrix);
    
    //m_pd3dDevice.SetTransform(D3DTS_PROJECTION, PlanetMeshProjMatrix);
    //m_pd3dDevice.SetTransform(D3DTS_VIEW, m_mView);


    PlanetMesh.Render(m_pd3dDevice);

    {D3DXMatrixTranslation(matTrans, 0, 30, 0);
    SomeModel.m_matTranslation := matTrans;}

    SomeModel.Render;


    StrFmt(strBuffer, '%.04f', [SomeModel.Pos.z]);
    m_pFont.DrawText(0, 40, $FFFF0000, strBuffer);

    StrFmt(strBuffer, '%.04f %.04f %.04f', [matTrans._41, matTrans._42,
      matTrans._43]);
    m_pFont.DrawText(0, 52, $FF0000FF, strBuffer);

    StrFmt(strBuffer, '%.04f %.04f %.04f %.04f %.04f %.04f %.04f %.04f %.04f',
      [matRot._11, matRot._12, matRot._13,
       matRot._21, matRot._22, matRot._23,
       matRot._31, matRot._32, matRot._33]);
    m_pFont.DrawText(0, 62, $FF0000FF, strBuffer);

    // Show frame rate
    if m_bShowFPSOn then begin
      m_pFont.DrawText( 2,  0, D3DCOLOR_ARGB(255,255,255,0), m_strFrameStats);
      m_pFont.DrawText( 2, 20, D3DCOLOR_ARGB(255,255,255,0), m_strDeviceStats);
    end;

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

  Result:= S_OK;
end;




//-----------------------------------------------------------------------------
// Name: InitDeviceObjects()
// Desc: Initialize scene objects.
//-----------------------------------------------------------------------------
function CMyD3DApplication.InitDeviceObjects;
var
  hr: HResult;
begin
  // Restore the fonts
  m_pFont.InitDeviceObjects(m_pd3dDevice);

  //m_szPath := 'Media\Models\nanoco_go.x';
  SomeModel.m_szPath := //'Media\Models\nanozone1.x';
    'Media\Models\nanoco_go.x';
  //SomeModel.LoadMeshHierarchy;
  SomeModel.InitDeviceObjects(m_pd3dDevice);

  hr := LoadTerrain;
  if Failed(hr) then begin
    result := hr;
    Exit;
  end;  

  Result:= S_OK;
end;

//-----------------------------------------------------------------------------
// Name: RestoreDeviceObjects()
// Desc: Initialize scene objects.
//-----------------------------------------------------------------------------
function CMyD3DApplication.RestoreDeviceObjects;
type
 TVectDecl = array [0..6] of DWord;
var
  light: TD3DLight8;
  dwIndexedVertexDecl1,
  dwIndexedVertexDecl2,
  dwIndexedVertexDecl3,
  dwIndexedVertexDecl4: TVectDecl;
  dwIndexedVertexDecl: array [0..3] of ^TVectDecl;

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

  vLightDir: TD3DXVector4;

  ShaderFileName: string;

  //mat: TD3DMatrix;
  //fAspect: Single;
begin
  // Restore the fonts
  m_pFont.RestoreDeviceObjects;

  m_ArcBall.SetWindow(m_d3dsdBackBuffer.Width, m_d3dsdBackBuffer.Height, 2.0);

  SomeModel.RestoreDeviceObjects;

  m_ArcBall.m_matTranslation._41 := -1.3090;
  m_ArcBall.m_matTranslation._42 := -0.0004;
  m_ArcBall.m_matTranslation._43 := -10.9123;

  m_ArcBall.m_matRotation._11 := -0.9961;
  m_ArcBall.m_matRotation._12 := -0.0534;
  m_ArcBall.m_matRotation._13 :=  0.0701;

  m_ArcBall.m_matRotation._21 := -0.0224;
  m_ArcBall.m_matRotation._22 :=  0.9226;
  m_ArcBall.m_matRotation._23 :=  0.3851;

  m_ArcBall.m_matRotation._31 := -0.0852;
  m_ArcBall.m_matRotation._32 :=  0.3820;
  m_ArcBall.m_matRotation._33 := -0.9202;

  D3DXQuaternionRotationMatrix(m_ArcBall.m_qNow, m_ArcBall.m_matRotation);
  D3DXQuaternionRotationMatrix(m_ArcBall.m_qDown, m_ArcBall.m_matRotation);

  //**
    // Set up the camera
    {m_Camera := CD3DCamera.Create;

    m_Camera.SetViewParams( D3DXVECTOR3(0.0, 0.0, 0.0),
      D3DXVECTOR3(0.0, 0.0, 1.0), D3DXVECTOR3(0.0, 1.0, 0.0) );
    fAspect:= m_d3dsdBackBuffer.Width / m_d3dsdBackBuffer.Height;
    m_Camera.SetProjParams( 0.25*D3DX_PI, fAspect, 0.1, 1000.0 );

    // Set up default matrices (using the CD3DCamera class)

    m_pd3dDevice.SetTransform( D3DTS_VIEW,       m_Camera.GetViewMatrix );}
    //m_pd3dDevice.SetTransform( D3DTS_PROJECTION, m_Camera.GetProjMatrix );
  //**

  //** if (m_pdeSelected <> nil) then SetProjectionMatrix;
  // Set Projection Matrix for vertex shader

  //D3DXMatrixPerspectiveFovLH(mat, 0.25*D3DX_PI, fAspect, 0.1, 1000.0 );
  //mat := m_Camera.GetProjMatrix;
  
  //m_pd3dDevice.SetTransform( D3DTS_PROJECTION, mat );
  {D3DXMatrixTranspose(mat, mat);
  Result:= m_pd3dDevice.SetVertexShaderConstant(2, mat, 4);
  if Failed(Result) then Exit;}
  //**

  


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

  m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,           D3DCULL_CW );
  
  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);

  // 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;
  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(1.0, -1.0, 1.0);//D3DXVector3(0.0, 0.0, -1.0);

//  light.

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

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

  m_pd3dDevice.SetRenderState( D3DRS_LIGHTING, Cardinal(TRUE) );
  m_pd3dDevice.SetRenderState( D3DRS_AMBIENT, $33333333 );

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

  PlanetMesh.RestoreDeviceObjects(m_pd3dDevice);

  Result:= S_OK;
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;


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

begin
  m_pFont.InvalidateDeviceObjects;

  SomeModel.InvalidateDeviceObjects;

  PlanetMesh.InvalidateDeviceObjects;

  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 CMyD3DApplication.DeleteDeviceObjects;
begin
  m_pFont.DeleteDeviceObjects;

  SomeModel.Free;

  SAFE_DELETE(PlanetMesh);

  Result:=S_OK;
end;


//-----------------------------------------------------------------------------
// Name: FinalCleanup()
// Desc: Called before the app exits, this function gives the app the chance
//       to cleanup after itself.
//-----------------------------------------------------------------------------
function CMyD3DApplication.FinalCleanup;
begin
  m_pFont.Free; m_pFont:= nil;
  Result:= S_OK;
end;


//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: Message proc function to handle key and menu input
//-----------------------------------------------------------------------------
function CMyD3DApplication.MsgProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM;
  lParam: LPARAM ): LRESULT;
var
  ofn: TOpenFilename;
{$WRITEABLECONST ON}
const
  _file:        array [0..255] of Char = '';
  szFilepath:   array [0..255] of Char = '';
  fileTitle:    array [0..255] of Char = '';
  filter: PChar = 'X files (*.x)'#0'*.x'#0 +
                  'All Files (*.*)'#0'*.*'#0;
{$WRITEABLECONST OFF}
var
  dwErr: DWord;
  hr: HResult;
begin
  // Pass mouse messages to the ArcBall so it can build internal matrices
  m_ArcBall.HandleMouseMessages(hWnd, uMsg, wParam, lParam);

  case uMsg of
    WM_COMMAND:
      if (0 = HIWORD(wParam)) then
      begin
        case LOWORD(wParam) of
          ID_FILE_OPENMESHHEIRARCHY:
           begin
            FillChar(ofn, SizeOf(ofn), 0);
            _file:= '';
            fileTitle:= '';

            ofn.lStructSize       := SizeOf(ofn);
            ofn.hwndOwner         := m_hWnd;
            ofn.hInstance         := 0; //m_hInstance;
            ofn.lpstrFilter       := filter;
            ofn.lpstrCustomFilter := nil;
            ofn.nMaxCustFilter    := 0;
            ofn.nFilterIndex      := 1;
            ofn.lpstrFile         := _file;
            ofn.nMaxFile          := SizeOf(_file);
            ofn.lpstrFileTitle    := fileTitle;
            ofn.nMaxFileTitle     := SizeOf(fileTitle);
            ofn.lpstrInitialDir   := nil;
            ofn.nFileOffset       := 0;
            ofn.nFileExtension    := 0;
            ofn.lpstrDefExt       := '*.x';
            ofn.lCustData         := 0;

            {$IFDEF COMPILER6_UP}
            // Handle Delphi6 case of extended OFN structure
            // (new members are invalid for Win98SE & below OS'es)
            ofn.lStructSize:= 76;

            {$ENDIF}
            ofn.Flags := OFN_PATHMUSTEXIST or OFN_FILEMUSTEXIST or OFN_HIDEREADONLY;
            if (not GetOpenFileName(ofn)) then
            begin
              dwErr:= CommDlgExtendedError;
              if (0 <> dwErr) then
              begin
                MessageBox(m_hWnd,
                  PChar(Format('GetOpenFileName failed with %x', [dwErr])),
                  'TexWin', MB_OK or MB_SYSTEMMODAL);
              end;
              Result:= 0;
              Exit;
            end;
            lstrcpy(m_szPath, ofn.lpstrFile);
            FreeAndNil(SomeModel);
            OpenMenuItemSel := True;
            SomeModel := TAnimatedModel.Create;
            lstrcpy(SomeModel.m_szPath, m_szPath);
            //hr:= SomeModel.LoadMeshHierarchy;
            hr:= SomeModel.InitDeviceObjects(m_pd3dDevice);
            if FAILED(hr) then
              MessageBox(0, 'Could not open file or incorrect file type',
                'Error loading file', MB_OK);

            Result:= 0;
            Exit;
           end;
          ID_OPTIONS_D3DINDEXED:
           begin
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DINDEXED, MF_CHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DNONINDEXED, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_SOFTWARESKINNING, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DINDEXEDVS, MF_UNCHECKED);
            m_method := D3DINDEXED;
           end;
          ID_OPTIONS_D3DINDEXEDVS:
           begin
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DINDEXED, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DNONINDEXED, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_SOFTWARESKINNING, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DINDEXEDVS, MF_CHECKED);
            m_method := D3DINDEXEDVS;
           end;
          ID_OPTIONS_D3DNONINDEXED:
           begin
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DINDEXED, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DNONINDEXED, MF_CHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_SOFTWARESKINNING, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DINDEXEDVS, MF_UNCHECKED);
            m_method := D3DNONINDEXED;
           end;
          ID_OPTIONS_SOFTWARESKINNING:
           begin
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DINDEXED, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DNONINDEXED, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_SOFTWARESKINNING, MF_CHECKED);
            CheckMenuItem(GetMenu(hWnd), ID_OPTIONS_D3DINDEXEDVS, MF_UNCHECKED);
            m_method := SOFTWARE;
           end;
        end;
      end;
  end;

  Result:= inherited MsgProc(hWnd, uMsg, wParam, lParam);
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 rased 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;

/////========================== mload.cpp ================================/////

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;





procedure CMyD3DApplication.UpdateInput;
var
  CurrentState: TUserInput;
begin
  if not Assigned(m_DXInput) then Exit;

  m_DXInput.Update;

  CurrentState := m_UserInput;

  CurrentState.fAxisRotateLR := m_DXInput.Mouse.X / 10;
  CurrentState.fAxisMoveUD := -(m_DXInput.Mouse.Y / 10);

  if GameMenu in m_DXInput.States then PostMessage( m_hWndFocus, WM_CLOSE, 0, 0 );

  //if GameCheat in m_DXInput.States then m_UserInput.bDoNextLevel := True;

  if CameraForward in m_DXInput.States then CurrentState.fAxisMoveUD := 0.5;
  if CameraBackward in m_DXInput.States then CurrentState.fAxisMoveUD := -0.5;

  if CameraLeft in m_DXInput.States then
    CurrentState.bButtonRotateLeft := True;

  if CameraRight in m_DXInput.States then
    CurrentState.bButtonRotateRight := True;

  if CameraChangeView in m_DXInput.States then begin
    m_UserInput.bDoChangeView := TRUE;
  end;

  if ToggleShowFPS in m_DXInput.States then begin
    m_bShowFPSOn := not m_bShowFPSOn;
    m_DXInput.States := m_DXInput.States - [ToggleShowFPS];
  end;



  m_UserInput.fAxisRotateLR := 0.0;
  m_UserInput.fAxisMoveUD   := 0.0;

  if Abs(CurrentState.fAxisRotateLR) > Abs(m_UserInput.fAxisRotateLR) then
      m_UserInput.fAxisRotateLR := CurrentState.fAxisRotateLR;

    if Abs(CurrentState.fAxisMoveUD) > Abs(m_UserInput.fAxisMoveUD) then
      m_UserInput.fAxisMoveUD := CurrentState.fAxisMoveUD;

    // Process the button data
    if CurrentState.bButtonRotateLeft then
      m_UserInput.fAxisRotateLR := -1.0
    else
      if CurrentState.bButtonRotateRight then
        m_UserInput.fAxisRotateLR := 1.0;

    if CurrentState.bButtonForwardThrust then
      m_UserInput.fAxisMoveUD := -1.0
    else
      if CurrentState.bButtonReverseThrust then
        m_UserInput.fAxisMoveUD := 1.0;

  UpdateCameraView;      
end;


function DefineDefaultKeys: TKeyAssignList;
begin
  FillChar(Result, SizeOf(Result), 0);

  AssignKey(Result, CameraForward,  [Ord('W'), VK_UP, VK_NUMPAD8]);
  AssignKey(Result, CameraBackward, [Ord('S'), VK_DOWN, VK_NUMPAD2]);
  AssignKey(Result, CameraLeft,     [Ord('A'), VK_LEFT, VK_NUMPAD4]);
  AssignKey(Result, CameraRight,    [Ord('D'), VK_RIGHT, VK_NUMPAD6]);

  AssignKey(Result, CameraZoomIn,     [VK_NUMPAD0, VK_INSERT]);
  AssignKey(Result, CameraRotateCC,   [VK_DECIMAL, VK_DELETE]);
  AssignKey(Result, CameraRotateUp,   [VK_NUMPAD7, VK_HOME]);
  AssignKey(Result, CameraRotateDown, [VK_NUMPAD1, VK_END]);

  AssignKey(Result, CameraZoomOut,   [VK_NUMPAD9, VK_PRIOR]);
  AssignKey(Result, CameraRotateCW,   [VK_NUMPAD3, VK_NEXT]);
  AssignKey(Result, GamePause,   [VK_SPACE]);
  AssignKey(Result, GameCheat,   [VK_RETURN]);

  AssignKey(Result, GameMenu, [VK_ESCAPE]);

  AssignKey(Result, CameraUp, [VK_SUBTRACT]);
  AssignKey(Result, CameraDown, [VK_ADD]);
  AssignKey(Result, CameraChangeView, [Ord('V')]);
  AssignKey(Result, ToggleShowFPS, [Ord('F')]);
end;

procedure CMyD3DApplication.SetDefaultDefKeys;
begin
  FCustomKeyAssign := DefineDefaultKeys;
end;

procedure CMyD3DApplication.CreateInputObjects;
begin
  SetDefaultDefKeys;
  m_DXInput := TDXInput.Create(AbstractApp);
end;

procedure CMyD3DApplication.DestroyInputObjects;
begin
  FreeAndNil(m_DXInput);
end;

procedure CMyD3DApplication.UpdateCameraView;
var
  fTimeLapsed: Single;
  start, aend: Cardinal;
  vEyePt, vLookatPt, vUpVec: array[0..2] of TD3DXVECTOR3;
  fX, fY, fZ, fX2, fY2, fZ2: Single;
  vEyePt0, vLookatPt0, vUpVec0, vTmp: TD3DXVECTOR3;

begin
  // Get the time lapsed since the last frame
  fTimeLapsed := DXUtil_Timer( TIMER_GETELAPSEDTIME );

  // Update the ship
  //if m_pPlayer.bVisible then
    m_pPlayer.vPos := D3DXVec3Add(m_pPlayer.vPos, m_pPlayer.vPos,
      D3DXVec3Scale(vTmp, m_pPlayer.vVel, fTimeLapsed)^)^;

  // Apply banking motion
  m_pPlayer.fRoll := m_pPlayer.fRoll + m_UserInput.fAxisRotateLR * 1.0 *
    fTimeLapsed;
  if m_pPlayer.fRoll > 0.5 then
    m_pPlayer.fRoll := 0.5;
  if m_pPlayer.fRoll < -0.5 then
    m_pPlayer.fRoll := -0.5;

  m_pPlayer.fAngle := m_pPlayer.fAngle + 5 * m_pPlayer.fRoll * fTimeLapsed;

  if (m_UserInput.fAxisRotateLR < 0.2) and (m_UserInput.fAxisRotateLR > -0.2)
  then
    m_pPlayer.fRoll := m_pPlayer.fRoll * 0.95;

  // Slow the ship down
  m_pPlayer.vVel.x := m_pPlayer.vVel.x * 0.97;
  m_pPlayer.vVel.y := m_pPlayer.vVel.y * 0.97;

  // Apply thrust
  m_pPlayer.vVel.x := m_pPlayer.vVel.x + sin( m_pPlayer.fAngle )
    * m_UserInput.fAxisMoveUD * 5.0 * fTimeLapsed;
  m_pPlayer.vVel.y := m_pPlayer.vVel.y + -cos( m_pPlayer.fAngle )
    * m_UserInput.fAxisMoveUD * 5.0 * fTimeLapsed;

  fX :=  m_pPlayer.vPos.x;
  fZ := -m_pPlayer.vPos.y;
  fY := 1;

  // View mode 0 (third person)
  vEyePt[0]      := D3DXVECTOR3(fX-sin(m_pPlayer.fAngle)/2, fY+0.2,
    fZ-cos(m_pPlayer.fAngle)/2 );
  vLookatPt[0]   := D3DXVECTOR3(fX+sin(m_pPlayer.fAngle)/2, fY,
    fZ+cos(m_pPlayer.fAngle)/2 );
  vUpVec[0]      := D3DXVECTOR3( 0.0, 1.0, 0.0 );

  // View mode 1 (first person)
  fX2 := fX+sin(m_pPlayer.fAngle);
  fZ2 := fZ+cos(m_pPlayer.fAngle);
  fY2 := 1; //0.1 + HeightField( fX2, fZ2 );
  vEyePt[1]    := D3DXVECTOR3( fX, fY+0.1, fZ );
  vLookatPt[1] := D3DXVECTOR3( fX2, fY2+0.1, fZ2 );
  vUpVec[1]    := D3DXVECTOR3( 0.0, 1.0, 0.0 );

  // View mode 2 (top down view)
  vEyePt[2]    := D3DXVECTOR3( fX+1.5, fY+1.5, fZ+1.5 );
  vLookatPt[2] := D3DXVECTOR3( fX, fY, fZ );
  vUpVec[2]    := D3DXVECTOR3( 0.0, 1.0, 0.0 );

  start := m_dwViewMode;
  if start < 2 then
    aend := m_dwViewMode+1
  else
    aend := 0;

  if (start = 1) and (m_fViewTransition < 0.2) then
    m_bFirstPersonView := TRUE
  else
    m_bFirstPersonView := FALSE;

  vTmp := D3DXVec3Scale(vTmp, vEyePt[start], (1.0-m_fViewTransition))^;
  vEyePt0    := D3DXvec3Add(vTmp, vTmp,
    D3DXVec3Scale(vEyePt[aend], vEyePt[aend], m_fViewTransition)^)^;
  vTmp := D3DXVec3Scale(vTmp, vLookatPt[start], (1.0-m_fViewTransition))^;
  vLookatPt0 := D3DXvec3Add(vTmp, vTmp,
    D3DXVec3Scale(vLookatPt[aend], vLookatPt[aend], m_fViewTransition)^)^;
  vTmp := D3DXVec3Scale(vTmp, vUpVec[start], (1.0-m_fViewTransition))^;
  vUpVec0 := D3DXVec3Add(vTmp, vTmp, D3DXVec3Scale(vUpVec[aend], vUpVec[aend],
    m_fViewTransition)^)^;

  //m_Camera.SetViewParams( vEyePt0, vLookatPt0, vUpVec0 );
end;

function CMyD3DApplication.LoadTerrain: HResult;
var
  pVB: IDIRECT3DVERTEXBUFFER8;
  dwNumVertices: Cardinal;
  pVertices: PModelVertexArray;
  i: Cardinal;
begin
  SAFE_DELETE(PlanetMesh);

  PlanetMesh := CD3DMesh.Create;
  if Failed(PlanetMesh.Create_(m_pd3dDevice, PlanetModel)) then begin
    result := E_FAIL;
    Exit;
  end;
  // Set the FVF to a reasonable type
  PlanetMesh.SetFVF( m_pd3dDevice, D3DFVF_MODELVERTEX );

  // Gain access to the model's vertices
  PlanetMesh.GetSysMemMesh.GetVertexBuffer( pVB );
  dwNumVertices := PlanetMesh.GetSysMemMesh.GetNumVertices;
  pVB.Lock( 0, 0, PBYTE(pVertices), 0 );

  for i:=0 to dwNumVertices-1 do
  begin
    pVertices[i].p.x := pVertices[i].p.x * 0.1;
    pVertices[i].p.z := pVertices[i].p.z * 0.1;
    pVertices[i].p.y := HeightField( pVertices[i].p.x, pVertices[i].p.z );
  end;

  // Done with the vertex buffer
  pVB.Unlock;
  SAFE_RELEASE(pVB);

  D3DXMatrixIdentity(PlanetMeshWorldMatrix);
  D3DXMatrixIdentity(PlanetMeshViewMatrix);
  D3DXMatrixIdentity(PlanetMeshProjMatrix);

  result := S_OK;
end;

constructor TAbstractApp.Create;
begin
  inherited Create;
  WindowProc := WindowProcedure;
end;

destructor TAbstractApp.Destroy;
begin
  WindowProc := nil;

  inherited Destroy;
end;

procedure TAbstractApp.WindowProcedure(var Message: TMessage);
begin
  if d3dApp = nil then Exit;
  d3dApp.MsgProc(m_hwnd, Message.Msg, Message.WParam, Message.LParam);
end;

{  TPlayer  }

constructor TPlayer.Create(p: TD3DVECTOR);
begin
  //inherited Create( goPlayer, p, D3DXVECTOR3(0, 0, 0) );
  with vPos do begin
    x := p.x;
    y := p.y;
    z := p.z;
  end;


  //fSize           := 10.0 / 256.0;
  bExploded       := False;
  fShowDelay      := 0.0;

  fRoll           := 0.0;
  fAngle          := 0.0;
end;


{  TAnimatedModel  }
constructor TAnimatedModel.Create;
begin
  inherited Create;

  m_dwFVF := D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_NORMAL or D3DFVF_TEX1;

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

  m_szPath:= #0;
  m_pBoneMatrices := nil;
  m_maxBones := 0;

  m_method := D3DNONINDEXED;

  D3DXMatrixIdentity(m_matRotation);
  D3DXMatrixIdentity(m_matTranslation);
  D3DXMatrixMultiply(m_matWorld, m_matRotation, m_matTranslation);

  Pos := D3DXVector3(0, 0, 0);
end;

destructor TAnimatedModel.Destroy;
begin
  InvalidateDeviceObjects;
  DeleteDeviceObjects;

  inherited Destroy;
end;


function TAnimatedModel.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 TAnimatedModel.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 TAnimatedModel.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 TAnimatedModel.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 TAnimatedModel.GenerateMesh(pmcMesh: SMeshContainer): HResult;
var
  pDevice: IDirect3DDevice8;
  // cFaces: DWORD; //BAA: not used anyway
  rgBoneCombinations: PD3DXBoneCombinationArray;
  cInfl: DWORD;
  iInfl: DWORD;
  pMeshTmp: ID3DXMesh;
  flags: DWORD;
  newFVF: DWORD;
  pMesh: ID3DXMesh;
  maxFaceInfl: DWORD;
label
  e_Exit;
begin
  result := E_FAIL;
  if d3dApp = nil then Exit;

  // ASSUMPTION:  pmcMesh->m_rgiAdjacency contains the current adjacency
  // Result:= S_OK; //BAA: not used anyway
  pDevice:= nil;
  // cFaces:= pmcMesh.m_pSkinMesh.GetNumFaces; //BAA: not used anyway

  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 (d3dApp.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 > d3dApp.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 (d3dApp.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 (d3dApp.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(d3dApp.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;

//**
procedure ExtractFileName2(var FileName: PChar);
const
  Delimeter = '/';
var
  tmpS: PChar;

begin
  tmpS := StrRScan(FileName, Delimeter);
  StrCopy(FileName, @tmpS[1]);
end;

procedure RemakeDelimeter(var AStr: array of Char);
const
  Delimeter = '/';
  Delimeter2= '\';
var
  i: Integer;
begin
  for i:=0 to StrLen(AStr)-1 do
    if AStr[i] = Delimeter2 then AStr[i] := Delimeter;
end;
//**

//-----------------------------------------------------------------------------
// Name: DXUtil_FindMediaFile()
// Desc: Returns a valid path to a DXSDK media file
//-----------------------------------------------------------------------------
function DXUtil_FindMediaFile2(strPath: PChar; strFilename: PChar): HRESULT;
var
  file_: DWord;
  strFullPath: array [0..1023] of Char;
  strShortName: PChar;
  cchPath: DWord;
begin
  if (nil = strFilename) or (nil = strPath) then
  begin
    Result:= E_INVALIDARG;
    Exit;
  end;

  // Build full path name from strFileName (strShortName will be just the leaf filename)
  cchPath := GetFullPathName(strFilename, SizeOf(strFullPath) div SizeOf(Char),
                             strFullPath, strShortName);

  if ((cchPath = 0) or (SizeOf(strFullPath) div SizeOf(Char) <= cchPath)) then
  begin
    Result:= E_FAIL;
    Exit;
  end;

  // first try to find the filename given a full path
  file_:= CreateFile(strFullPath, GENERIC_READ, FILE_SHARE_READ, nil,
                     OPEN_EXISTING, 0, 0);
  if (INVALID_HANDLE_VALUE <> file_) then
  begin
    StrCopy(strPath, strFullPath);
    CloseHandle(file_);
    Result:= S_OK;
    Exit;
  end;

  // next try to find the filename in the current working directory (path stripped)
  file_:= CreateFile(strShortName, GENERIC_READ, FILE_SHARE_READ, nil,
                     OPEN_EXISTING, 0, 0);
  if (INVALID_HANDLE_VALUE <> file_) then
  begin
    StrCopy(strPath, strShortName);
    CloseHandle(file_);
    Result:= S_OK;
    Exit
  end;

  // On failure, just return the file as the path
  StrCopy(strPath, strFilename);
  Result:= E_FAIL;
end;

//**



function TAnimatedModel.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;

  //**
  tempStr: PChar;
  ModulePath: string;
  //**
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
          //**
          ZeroMemory(@szPath, SizeOf(szPath));
          GetMem(tempStr, MAX_PATH-1);
          StrCopy(tempStr, pMaterials[iMaterial].pTextureFilename);

          if OpenMenuItemSel then begin
            ExtractFileName2(tempStr);
            //DXUtil_FindMediaFile2(szPath, tempStr);
            GetModuleFileName(0, szPath, SizeOf(szPath));
            ModulePath := ExtractFilePath(szPath);
            ZeroMemory(@szPath, SizeOf(szPath));
            StrPCopy(szPath, ModulePath);
            RemakeDelimeter(szPath);
            StrCat(szPath, pMaterials[iMaterial].pTextureFilename);
          end;
          
          StrCopy(szPath, tempStr);

          FreeMem(tempStr);
          //**
          //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 TAnimatedModel.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 TAnimatedModel.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;

/////========================== mdraw.cpp ================================/////



function TAnimatedModel.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
  result := E_FAIL;
  if d3dApp = nil then Exit;

  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 (d3dApp.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 (d3dApp.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(d3dApp.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, {d3dApp.m_mView}m_matWorld);
            {$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 TAnimatedModel.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 TAnimatedModel.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;

//-----------------------------------------------------------------------------
// Name: SetProjectionMatrix()
// Desc:
//-----------------------------------------------------------------------------
function TAnimatedModel.SetProjectionMatrix;
var
  mat: TD3DXMatrix;
  fAspect: Single;
begin
  result := E_FAIL;
  if d3dApp = nil then Exit;

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

  fAspect:= d3dApp.m_d3dsdBackBuffer.Width / d3dApp.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;

//-----------------------------------------------------------------------------
// 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 TAnimatedModel.Render;
var
  pdeCur: SDrawElement;

  cTriangles: UINT;
  mCur: TD3DXMatrixA16;
  //matScale: TD3DXMatrix;

begin
  result := E_FAIL;
  if d3dApp = nil then Exit;

  // Set up viewing postion from ArcBall
  pdeCur := m_pdeHead;
  while (pdeCur <> nil) do
  begin
    

    pdeCur.pframeRoot.matRot:=   {d3dApp.m_ArcBall.GetRotationMatrix^}
      m_matRotation;



    pdeCur.pframeRoot.matTrans:= {d3dApp.m_ArcBall.GetTranslationMatrix^}
      m_matTranslation;

    pdeCur:= pdeCur.pdeNext;
  end;

  //D3DXMatrixMultiply(m_matWorld, m_matRotation, m_matTranslation);

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

  // Begin the scene
  {if SUCCEEDED(m_pd3dDevice.BeginScene) then
  begin}
    cTriangles := 0;
    //D3DXMatrixTranslation({d3dApp.m_mView}m_matWorld, 0, 0, -m_pdeSelected.fRadius * 2.8);

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

    pdeCur:= m_pdeHead;
    while (pdeCur <> nil) do
    begin
      D3DXMatrixIdentity(mCur);
      {D3DXMatrixScaling(matScale, 1, 1, 1);
      D3DXMatrixMultiply(m_matWorld, m_matWorld, matScale);

      D3DXMatrixMultiply(m_matWorld, m_matWorld, m_matRotation);
      D3DXMatrixMultiply(m_matWorld, m_matWorld, m_matTranslation);}

      //mCur := m_matWorld;

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

  Result:= S_OK;
end;

function TAnimatedModel.FrameMove: HResult;
var
  pdeCur: SDrawElement;
  pframeCur: SFrame;

  matTrans, matScale: TD3DXMatrix;

begin
  result := E_FAIL;
  if d3dApp = nil then Exit;

  pdeCur:= m_pdeHead;
  while (pdeCur <> nil) do
  begin
    pdeCur.fCurTime:= pdeCur.fCurTime + d3dApp.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;



  if Pos.z > -1200 then Pos.z := Pos.z - d3dApp.m_fElapsedTime * 20
  else
    Pos.z := 0;

  D3DXMatrixTranslation(matTrans, 0, 30, Pos.z);
  D3DXMatrixScaling(matScale, 0.01, 0.01, 0.01);
  D3DXMatrixMultiply(matTrans, matTrans, matScale);

  m_matTranslation := matTrans;


  Result:= S_OK;
end;

function TAnimatedModel.InitDeviceObjects(pd3dDevice: IDirect3DDevice8): HResult;
begin
  result := E_FAIL;
  if d3dApp = nil then Exit;

  m_pd3dDevice := pd3dDevice;

  result := LoadMeshHierarchy;


  if (m_pdeHead <> nil) then
    d3dApp.m_ArcBall.SetRadius(m_pdeHead.fRadius);
  d3dApp.m_ArcBall.SetRightHanded(TRUE);
  
end;

function TAnimatedModel.RestoreDeviceObjects: HResult;
begin
  result := S_OK;

  if (m_pdeSelected <> nil) then SetProjectionMatrix;
end;

function TAnimatedModel.InvalidateDeviceObjects: HResult;
var
  pdeCurr: SDrawElement;
begin
  // 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;

function TAnimatedModel.DeleteDeviceObjects: HResult;
begin
  if (m_pdeSelected = m_pdeHead) then m_pdeSelected := nil;

  FreeAndNil(m_pdeHead);

  result := S_OK;
end;



end.



