{***************************************************************
  Проект игры Planet "MK"

  http://www.megainformatic.boom.ru
  http://www.megainformaticsite.pochta.ru

  26/07/2007, 15/10/2007, 14/12/2007
  
****************************************************************}
unit Main;

interface

uses
  Windows, Messages, SysUtils, Direct3D8, D3DAbstractApp, D3DUtil, DX8_DIUtil8, D3DFont,
  DXUtil, D3DX8, D3DFile, D3DGUI, DX8Mesh;

const
  // Player view mode
  NUMVIEWMODES = 3;

  // Error codes
  GAME_ERR_NODIRECT3D    = $00000001;
  GAME_ERR_NOD3DDEVICE   = $00000002;
  GAME_ERR_NOTEXTURES    = $00000003;
  GAME_ERR_NOGEOMETRY    = $00000004;
  GAME_ERR_NO3DRESOURCES = $00000005;
  GAME_ERR_NOINPUT       = $00000006;

  {**** Game Actions ****

   соответствие между состояниями DXInput.State и игровыми действиями
   (для жанра: стратегия

    isLeft    - <CameraLeft>     (at X_Axis)
    isRight   - <CameraRight>    (at X_Axis)
    isUp      - <CameraForward>  (at Z_Axis)
    isDown    - <CameraBackward> (at Z_Axis)

    Button1 (Left mouse Button)  - <Command>
    Button2 (Right mouse Button) - <ContextCommand> (Move to)
    Button3 (Press Mouse Wheel) - <Rotate Camera in YZ, XZ planes>
    Mouse Wheel Rotate [DXInput.Mouse.Z] - (Button6/7)

    Button4 - <CameraZoomIn>      (about Z_Axis, decrese distance
                                   between LookAt and EyePt)
    Button8 - <CameraZoomOut>     (about Z_Axis, inc distance - see under)
    Button5 - <CameraRotateCC> (CounterClockwise)  (about Y_Axis)
    Button9 - <CameraRotateCW> (Clockwise) (about Y_Axis)
    Button6 - <CameraRotateUp>   (about X_Axis)
    Button7 - <CameraRotateDown> (about X_Axis)

    Button10 - <Pause>
    Button11 - <Cheat> Enter cheat mode - ready for entering cheat codes!!!

    for game debug only -
    Button12, Button13 - Camera Up/Down upon Y axis

    
    }

  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;

  // Models

  PlanetModel = 'Media\Models\seafloor.x';//'Media\Models\planet_ki.x';
  PlayerModel = 'Media\Models\fedja.x';
  PlayerAnimModel = 'Media\Models\fedja_go.x';
  //'Media\Models\fedja.x';//'Media\Models\ship.x';//

  // Textures
  SplashTex = 'Media\Textures\banner_logo512.jpg';

  // Special constants
  SplashDelay = 12;//6;

  MainMenuCursorImage = 'Media\Cursors\mainmenu.ico';

type
  // States the app can be in
  TAppStates = (asActive, asBeginTitleScene,
    asDisplayTitleScene);

  // Game object types
  TGameObject = ( goPlayer );

  TSCREENVERTEX = record
    p: TD3DXVECTOR4;
    color: Cardinal;
  end;

  PScreenVertexArray = ^TScreenVertexArray;
  TScreenVertexArray = array[0..0] of TSCREENVERTEX;

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

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

  TSPRITEVERTEX = record
    p: TD3DXVECTOR3;
    color: Cardinal;
    tu, tv: Single;
  end;

  PSpriteVertexArray = ^TSpriteVertexArray;
  TSpriteVertexArray = array[0..0] of TSPRITEVERTEX;

  TSplashVertex = record
    p: TD3DXVector4;
    color: Cardinal;
    tu, tv: Single;
  end;

  PSplashVertexArray = ^TSplashVertexArray;
  TSplashVertexArray = array[0..0] of TSplashVertex;

const
  D3DFVF_SCREENVERTEX = D3DFVF_XYZRHW or D3DFVF_DIFFUSE;
  D3DFVF_MODELVERTEX = D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_TEX1;
  D3DFVF_SPRITEVERTEX = D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_TEX1;
  D3DFVF_SplashVertex = D3DFVF_XYZRHW or D3DFVF_DIFFUSE or D3DFVF_TEX1;

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

  TDisplayObject = class
  private
    pNext, pPrev: TDisplayObject;          // Link to next object
                                           // Link to previous object

    dwType: TGameObject;            // Type of object
    bVisible: Boolean;           // Whether the object is visible
    vPos, vVel: TD3DXVECTOR3;    // Position
                                 // Velocity
    fSize: Single;
  public
    // Constructor
    constructor Create(GameObj: TGameObject; p, v: TD3DVECTOR);
  end;

  TPlayer = class(TDisplayObject)
  public
    fRoll: Single;

    fAngle: Single;

    bExploded: Boolean;
    fShowDelay: Single;

    constructor Create(p: TD3DVECTOR);
  end;

  TD3DGameApp = class(TD3DAbstractApp)
  protected
    FOldWndProc: Pointer;
    m_strAppName: PChar;
    m_hWndMain: HWND;                // Main window
    m_dwScreenWidth,                 // Dimensions for fullscreen modes
    m_dwScreenHeight: Cardinal;

    m_dwWindowStyle: Cardinal;
    m_rcWindowBounds: TRect;               // Saved window bounds for mode switches
    m_rcWindowClient: TRect;

    m_DesktopMode: D3DDISPLAYMODE;
    m_d3dfmtFullscreen: D3DFORMAT;        // Pixel format for fullscreen modes
    m_d3dfmtTexture: D3DFORMAT;           // Pixel format for textures
    m_bFullScreen,                        // Whether app is fullscreen (or windowed)
    m_bIsActive,                          // Whether app is active
    m_bDisplayReady,                      // Whether display class is initialized
    m_bMouseVisible: Boolean;             // Whether mouse is visible

    m_dwAppState: TAppStates;             // Current state the app is in

    m_Camera: CD3DCamera;                  // Camera used for 3D scene

    //** для целей отладки и портирования SkinnedMesh
      m_ArcBall:  CD3DArcBall;
    //**

    m_dwViewMode: Cardinal;                // Which view mode is being used
    m_fViewTransition: Single;             // Amount used to transition views
    m_bAnimatingViewChange: Boolean;       // Whether view is transitioning
    m_bFirstPersonView: Boolean;           // Whether view is first-person

    // DirectDraw/Direct3D objects
    m_pd3dDevice: IDIRECT3DDEVICE8;           // Class to handle D3D device
    m_d3dpp: D3DPRESENT_PARAMETERS;
    m_pViewportVB: IDIRECT3DVERTEXBUFFER8;

    m_pGameFont: CD3DFont;

    // Variables for timing
    m_fFPS: Single;                         // Instanteous frame rate
    m_strFrameStats: array[0..89] of Char;  // String to hold frame stats
    m_bShowFPSOn: Boolean;

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

    m_bPaused: Boolean;

    PlanetMesh: CD3DMesh;
    PlayerMesh: CD3DMesh;
    PlayerAnimMesh: TD3DAnimatedMesh;

    SplashGameGUI: TD3DSprite;
    GoingMan: TD3DSprite;
    

    hGameCursor: HICON;

    procedure CountFPS;

    function LoadTerrain: HResult;
    function LoadSplash: HResult;
    function LoadPlayerMesh: HResult;
    function LoadPlayerAnimMesh: HResult;

    function OneTimeSceneInit(hWindow: HWND): HRESULT;
    function FrameMove: HRESULT;
    function Render: HRESULT;
    function FinalCleanup: HResult;

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

    // Display functions
    function CreateDisplayObjects(h_Wnd: HWND): HRESULT;
    function RestoreDisplayObjects: HRESULT;
    function InvalidateDisplayObjects: HRESULT;
    function DestroyDisplayObjects: HRESULT;
    function SwitchDisplayModes(bFullScreen: Boolean;
      dwWidth, dwHeight: Cardinal): HRESULT;

    procedure DrawDisplayList;
    procedure UpdateDisplayList;

    // Rendering functions
    procedure ShowFrame;
    procedure DarkenScene(fAmount: Single);

    procedure RenderFieryText(var pFont: CD3DFont; strText:
      PAnsiChar);

    // Draw GUI functions
    procedure DrawScreenSplash(ShowSplash: Boolean = False);
    

    // Misc game functions
    procedure AdvanceLevel;
    function IsDisplayListEmpty: Boolean;

    function LoadCursor: HResult;
    procedure ShowCursor;

    // Error handling
    procedure CleanupAndDisplayError(dwError: Cardinal);

  public
    // Display list and player
    m_pDisplayList: TDisplayObject;   // Global display list
    m_pPlayer: TPlayer;                   // Player's display object

    function Create_(hInstance: LongWord): HResult;
    function Run: Integer;
    function MsgProc(hWnd:HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
    constructor Create;
    procedure WindowProcedure(var Message: TMessage); override;
  end;


  function WinMain(hInst, hPrevInst: LongWord; pCmdLine: PChar;
    nCmdShow: Integer): Integer;
  function DefineDefaultKeys: TKeyAssignList;
  //function rnd(low: Single =-1.0; high: Single =1.0): Single;  

var
  g_hInst: LongWord;
  g_d3dApp: TD3DGameApp;

implementation

uses
  AppRes;

function WinMain(hInst, hPrevInst: LongWord; pCmdLine: PChar;
  nCmdShow: Integer): Integer;
begin
  g_hInst := hInst;

  g_d3dApp := TD3DGameApp.Create;
  if not Assigned(g_d3dApp) then  begin
    result := 0;
    Exit;
  end;

  if Failed(g_d3dApp.Create_(g_hInst)) then begin
    result := 0;
    Exit;
  end;

  result := g_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;

const
  RAND_MAX = $7FFF;

// Simple function for generating random numbers
function rnd(low: Single =-1.0; high: Single =1.0): Single;
begin
  Randomize;
  result := low + ( high - low ) * ( random(RAND_MAX) / RAND_MAX );
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;

constructor TD3DGameApp.Create;
begin
  WindowProc              := WindowProcedure;
  FOldWndProc             := nil;

  m_strAppName            := 'D3D Game';
  m_hWndMain              := 0;
  m_hWnd                  := m_hWndMain;
  m_dwScreenWidth         := 800;
  m_dwScreenHeight        := 600;
  m_bFullScreen           := False;
  m_bIsActive             := False;
  m_bDisplayReady         := False;
  m_bMouseVisible         := True;
  
  m_dwAppState            := asBeginTitleScene;

  m_dwViewMode            := 2;
  m_fViewTransition       := 0.0;
  m_bAnimatingViewChange  := False;
  m_bFirstPersonView      := True;

  m_pd3dDevice            := nil;

  m_pViewportVB           := nil;
  

  m_pGameFont             := nil;
  
  m_DXInput := nil;
  m_bPaused               := False;

  m_pPlayer := nil;
  PlayerMesh := nil;

  PlanetMesh := nil;

  SplashGameGUI := nil;
  GoingMan := nil;

  m_bShowFPSOn := True;

  PlayerAnimMesh := nil;

  //**
    m_ArcBall             := CD3DArcBall.Create;
  //**

  m_bMouseVisible := True;
end;

procedure TD3DGameApp.WindowProcedure(var Message: TMessage);
begin
  MsgProc(m_hwnd, Message.Msg, Message.WParam, Message.LParam);
end;

function WndProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  Result:= g_d3dApp.MsgProc(hWnd, uMsg, wParam, lParam);
end;

var
  wndClass: TWndClass = (
    style: CS_DBLCLKS;
    lpfnWndProc: @WndProc;
    cbClsExtra: 0;
    cbWndExtra: 0;
    hInstance: 0;
    hIcon: 0;
    hCursor: 0;
    hbrBackground: 0;
    lpszMenuName: nil;
    lpszClassName: 'D3D Window');

function TD3DGameApp.Create_(hInstance: LongWord): HRESULT;
var
  rc: TRect;
begin
  // Unless a substitute hWnd has been specified, create a window to
  // render into
  if (m_hWndMain = 0) then
  begin
    // Register the windows class
    with wndClass do
    begin
      hInstance:= SysInit.hInstance;
      hIcon:= LoadIcon(hInstance, MAKEINTRESOURCE(MAIN_ICON));
      hCursor:= Windows.LoadCursor(0, IDC_ARROW);
      hbrBackground:= GetStockObject(BLACK_BRUSH);
    end;
    Windows.RegisterClass(wndClass);

    // Set the window's initial style
    m_dwWindowStyle:= WS_VISIBLE or WS_POPUP or WS_CAPTION or WS_SYSMENU;

    // Set the window's initial width
    SetRect(rc, 0, 0, m_dwScreenWidth, m_dwScreenHeight);
    AdjustWindowRect(rc, m_dwWindowStyle, True);

    // Create the render window
    m_hWndMain := CreateWindow('D3D Window', m_strAppName, m_dwWindowStyle,
                          Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT),
                          (rc.right-rc.left), (rc.bottom-rc.top), 0, 0,
                          hInstance, nil);
    if m_hWndMain = 0 then begin
      result := E_FAIL;
      Exit;
    end;
    m_hWnd := m_hWndMain;
  end else
  begin
    FOldWndProc := Pointer(SetWindowLong(m_hWndMain, GWL_WndProc, Longint(@WndProc)));
    m_hWnd := m_hWndMain;
  end;

  SetWindowText(m_hWnd, m_strAppName);

  // The focus window can be a specified to be a different window than the
  // device window.  If not, use the device window as the focus window.
  if m_hWndMain = 0 then
    m_hWndMain:= m_hWnd;

  // Save window properties
  m_dwWindowStyle:= GetWindowLong(m_hWnd, GWL_STYLE);
  GetWindowRect(m_hWnd, m_rcWindowBounds);
  GetClientRect(m_hWnd, m_rcWindowClient);

  UpdateWindow(m_hWndMain);

  // Create the game objects (display objects, sounds, input devices,
  // menus, etc.)

  if FAILED(OneTimeSceneInit(m_hWndMain)) then
  begin
    DestroyWindow(m_hWndMain);
    result := E_FAIL;
    Exit;
  end;


  Result:= S_OK;
end;

function TD3DGameApp.Run: Integer;
var
  bGotMsg: BOOL;
  msg: TMsg;
begin
  // Now we're ready to recieve and process Windows messages.
  msg.message:= WM_NULL;
  PeekMessage(msg, 0, 0, 0, PM_NOREMOVE);

  while (WM_QUIT <> msg.message) do
  begin
    // Use PeekMessage() if the app is active, so we can use idle time to
    // render the scene. Else, use GetMessage() to avoid eating CPU time.
    if (m_bIsActive)
      then bGotMsg:= PeekMessage(msg, 0, 0, 0, PM_REMOVE)
      else bGotMsg:= GetMessage(msg, 0, 0, 0);

    if bGotMsg then
    begin
      // Translate and dispatch the message
      TranslateMessage(msg);
      DispatchMessage(msg);
    end else
    begin
      // Render a frame during idle time (no messages are waiting)
      if m_bDisplayReady then
      begin
        if Failed(FrameMove) then
          SendMessage(m_hWndMain, WM_CLOSE, 0, 0);

        if Failed(Render) then
          SendMessage(m_hWndMain, WM_CLOSE, 0, 0);
      end;
    end;
  end;

  Result:= msg.wParam;
end;

function TD3DGameApp.MsgProc(hWnd:HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
begin
//**
  // Pass mouse messages to the ArcBall so it can build internal matrices
  m_ArcBall.HandleMouseMessages(hWnd, uMsg, wParam, lParam);
//**

    case uMsg of

        WM_ACTIVATEAPP: begin
            m_bIsActive := BOOL(wParam);

            if m_bIsActive then
            begin
                //** m_bMouseVisible   := FALSE;
                DXUtil_Timer( TIMER_START );
            end
            else
            begin
                //** m_bMouseVisible := TRUE;
                DXUtil_Timer( TIMER_STOP );
            end;
        end;

        WM_GETMINMAXINFO: begin
            PMinMaxInfo(lParam)^.ptMinTrackSize.x := 320;
            PMinMaxInfo(lParam)^.ptMinTrackSize.y := 200;
        end;

        WM_SETCURSOR: begin
            if m_bMouseVisible then begin
              SetCursor(0);
              ShowCursor;
            end
            else begin
              SetCursor(Windows.LoadCursor(0, IDC_ARROW));
              m_pd3dDevice.ShowCursor(False);
            end;



            result := Integer(True);
            Exit;
        end;

        WM_SYSCOMMAND: begin
            // Prevent moving/sizing and power loss
            case wParam of
              SC_SIZE,
              SC_MOVE,
              SC_MAXIMIZE,
              SC_KEYMENU,
              SC_MONITORPOWER: begin 
                result := 1;
                Exit;
              end;

            end;            
        end;

        WM_SYSKEYDOWN: begin
          // Handle Alt+Enter to do mode-switching
          if VK_RETURN = wParam  then
            SwitchDisplayModes(not m_bFullScreen, m_dwScreenWidth,
              m_dwScreenHeight);
        end;

        WM_KEYDOWN: ;

        WM_PAINT: begin
          if m_bDisplayReady then
          begin
            //DrawDisplayList;
            ShowFrame;
          end;        
        end;

        WM_DESTROY: begin
            FinalCleanup;
            PostQuitMessage(0);
            m_bDisplayReady := False;
        end;
    end;

  if Assigned(FOldWndProc) then Result :=
    CallWindowProc(FOldWndProc, hWnd, uMsg, wParam, lParam)
  else
    Result := DefWindowProc (   hWnd, uMsg, wParam, lParam);
end;

function TD3DGameApp.LoadTerrain: HResult;
var
  pVB: IDIRECT3DVERTEXBUFFER8;
  dwNumVertices: Cardinal;
  pVertices: PModelVertexArray;
  {vCenter: TD3DXVECTOR3;
  fRadius: Single;}
  i: Cardinal;
begin
  SAFE_DELETE(PlanetMesh);

  PlanetMesh := CD3DMesh.Create;
  if Failed(PlanetMesh.Create_(m_pd3dDevice, PlanetModel)) then begin
    CleanupAndDisplayError(GAME_ERR_NOGEOMETRY);
    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 );


  // Scale the new object to a standard size
  {D3DXComputeBoundingSphere( Pointer(pVertices)^, dwNumVertices,
    D3DFVF_MODELVERTEX, vCenter, fRadius );
  for i:=0 to dwNumVertices-1 do
    pVertices[i].p := D3DXVec3Scale(pVertices[i].p, pVertices[i].p,
      1/(0.5*fRadius))^;}
      
  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);

  result := S_OK;
end;

function TD3DGameApp.LoadPlayerMesh: HResult;
var
  pVB: IDIRECT3DVERTEXBUFFER8;
  dwNumVertices: Cardinal;
  pVertices: PModelVertexArray;
  vCenter: TD3DXVECTOR3;
  fRadius: Single;
  i: Cardinal;
begin
  SAFE_DELETE(PlayerMesh);

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

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


  // Scale the new object to a standard size
  D3DXComputeBoundingSphere( Pointer(pVertices)^, dwNumVertices,
    D3DFVF_MODELVERTEX, vCenter, fRadius );
  for i:=0 to dwNumVertices-1 do
    pVertices[i].p := D3DXVec3Scale(pVertices[i].p, pVertices[i].p,
      1/(12*fRadius))^;


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

  result := S_OK;
end;

function TD3DGameApp.LoadPlayerAnimMesh: HResult;
begin
  PlayerAnimMesh := TD3DAnimatedMesh.Create(PlayerAnimModel);
  Result := PlayerAnimMesh.InitDeviceObjects(m_pd3dDevice);
end;


function TD3DGameApp.LoadSplash: HResult;
begin
  result := E_FAIL;

  SplashGameGUI := TD3DSprite.Create(m_d3dfmtTexture);
  if SplashGameGUI = nil then Exit;
  result := SplashGameGUI.InitDeviceObjects(m_pd3dDevice, SplashTex);
  SplashGameGUI.Width :=  512;
  SplashGameGUI.Height := 90;

  GoingMan := TD3DSprite.Create(m_d3dfmtTexture);
  if GoingMan = nil then Exit;
  result := GoingMan.InitDeviceObjects(m_pd3dDevice, SplashTex);
  GoingMan.Width :=  64;
  GoingMan.Height := 64;
  GoingMan.Position.x := 0.2;
end;

function TD3DGameApp.OneTimeSceneInit(hWindow: HWND): HRESULT;
var
  hr: HRESULT;
begin
  // Initialize the DirectInput stuff

  CreateInputObjects;



  // Initialize the DirectSound stuff. Note: if this call fails, we can
  // continue with no sound.

  //CreateSoundObjects( m_hWndMain );


  // Create the display objects
  hr := CreateDisplayObjects( m_hWndMain );
  if FAILED(hr) then begin
    result := hr;
    Exit;
  end;

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

  m_pDisplayList := m_pPlayer;

  // Initial program state is to display the splash screen
  m_dwAppState := asBeginTitleScene;

  result := S_OK;
end;

function TD3DGameApp.FinalCleanup: HResult;
begin
  DestroyDisplayObjects;
  //DestroySoundObjects;
  DestroyInputObjects;
  result := S_OK;
end;

procedure TD3DGameApp.AdvanceLevel;
begin
  Randomize;

  // Delay for 2 seconds before displaying ship
  m_pPlayer.vPos       := D3DXVECTOR3( 0.0, 0.0, 0.0 );
  m_pPlayer.vVel       := D3DXVECTOR3( 0.0, 0.0, 0.0 );
  m_pPlayer.bVisible   := FALSE;
  m_pPlayer.bExploded  := FALSE;
  m_pPlayer.fShowDelay := 2.0;
end;  

function TD3DGameApp.IsDisplayListEmpty: Boolean;
begin
  result := False;
  // for debug purpose
  if m_UserInput.bDoNextLevel then begin
    m_UserInput.bDoNextLevel := False;
    result := True;
  end;
end;

function TD3DGameApp.FrameMove: HRESULT;
var
  fTimeElapsed: Single;
begin
  case m_dwAppState of
  
    asActive: begin
      UpdateDisplayList;

      if IsDisplayListEmpty then
      begin
        AdvanceLevel;
        m_dwAppState := asBeginTitleScene;
      end;
    end;

    asBeginTitleScene: begin
      DXUtil_Timer( TIMER_RESET );
      m_dwAppState := asDisplayTitleScene;
    end;

    asDisplayTitleScene: begin
      fTimeElapsed := DXUtil_Timer( TIMER_GETELAPSEDTIME );
      SplashGameGUI.FrameMove(fTimeElapsed);
      GoingMan.FrameMove(fTimeElapsed);
      GoingMan.Position.x := GoingMan.Position.x - 0.05*fTimeElapsed;
      // Only show the Level intro screen for SplashDelay seconds
      if DXUtil_Timer( TIMER_GETAPPTIME ) > SplashDelay then begin
        m_dwAppState := asActive;
      end;  
    end;
  end;

  result := S_OK;
end;

function TD3DGameApp.Render: HRESULT;
var
  hr: HRESULT;
begin
  // Test cooperative level
  
  hr := m_pd3dDevice.TestCooperativeLevel;
  // Test the cooperative level to see if it's okay to render
  if FAILED(hr) then
  begin
    // If the device was lost, do not render until we get it back
    if hr = D3DERR_DEVICELOST then begin
      result := S_OK;
      Exit;
    end;

    // Check if the device needs to be resized.
    if hr = D3DERR_DEVICENOTRESET then
    begin
      m_bDisplayReady := FALSE;

      InvalidateDisplayObjects;

      // Resize the device
      if SUCCEEDED( m_pd3dDevice.Reset( m_d3dpp )) then
      begin
        // Initialize the app's device-dependent objects
        if SUCCEEDED(RestoreDisplayObjects) then
        begin
          m_bDisplayReady := TRUE;
          result := S_OK;
          Exit;
        end;
      end;

      PostMessage( m_hWndMain, WM_CLOSE, 0, 0 );
    end;
    result := hr;
    Exit;
  end;

  // Render the scene based on current state of the app
  case m_dwAppState of

    asBeginTitleScene: ;
        // Nothing to render while some doings


    asDisplayTitleScene: begin
      DrawScreenSplash(True);
      ShowFrame;
    end;

    asActive: begin
      DrawDisplayList;
      ShowFrame;
    end;
  end;

  result := S_OK;
end;

procedure TD3DGameApp.DarkenScene(fAmount: Single);
var
  dwAlpha, i: Cardinal;
  v: PScreenVertexArray;

begin
  if not Assigned(m_pd3dDevice) then Exit;

  // Setup a dark square to cover the scene
  if fAmount<1.0 then
    dwAlpha := (Round(255*fAmount))shl 24
  else
    dwAlpha := $ff000000;

  m_pViewportVB.Lock( 0, 0, PBYTE(v), 0 );
  for i:= 0 to 3 do
    v[i].color := dwAlpha;

  m_pViewportVB.Unlock;

  // Set renderstates
  m_pd3dDevice.SetRenderState( D3DRS_ALPHABLENDENABLE, Cardinal(TRUE) );
  m_pd3dDevice.SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
  m_pd3dDevice.SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
  m_pd3dDevice.SetRenderState( D3DRS_ZENABLE,   Cardinal(FALSE) );
  m_pd3dDevice.SetTexture( 0, nil );

  // Draw a big, gray square
  m_pd3dDevice.SetVertexShader( D3DFVF_SCREENVERTEX );
  m_pd3dDevice.SetStreamSource( 0, m_pViewportVB, sizeof(TSCREENVERTEX) );
  m_pd3dDevice.DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

  // Restore states
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
end;

var
  Alpha: Byte = 0;

procedure TD3DGameApp.DrawScreenSplash(ShowSplash: Boolean);
var
  Phase, Period: Single;

begin
  if not Assigned(m_pd3dDevice) then Exit;

  m_pd3dDevice.SetRenderState( D3DRS_LIGHTING, Cardinal(FALSE) );

  // Begin the scene
  if SUCCEEDED(m_pd3dDevice.BeginScene) then
  begin
    // Erase the screen
    m_pd3dDevice.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, $FF011EFF,
     1.0, 0 );


    //Alpha := 255 - Round(Abs(255*(cos(D3DX_PI*DXUtil_Timer( TIMER_GETAPPTIME )/SplashDelay))));
    Period := SplashDelay/3;
    Phase := D3DX_PI/2*DXUtil_Timer(TIMER_GETAPPTIME)/Period;

    if DXUtil_Timer(TIMER_GETAPPTIME) < Period then
      Alpha := 255 - Round(Abs(255*(cos(Phase))));
    if (DXUtil_Timer(TIMER_GETAPPTIME) > Period) and (DXUtil_Timer(TIMER_GETAPPTIME) < 2*Period) then
      Alpha := 255;
    if DXUtil_Timer(TIMER_GETAPPTIME) > 2*Period then
      Alpha := 255 - Round(Abs(255*(sin(Phase))));

    SplashGameGUI.FramesPerLine := 1;
    SplashGameGUI.Delay := 0.2;
    SplashGameGUI.fMaxFrame := 3;
    SplashGameGUI.TextureOffsetX := 0;
    SplashGameGUI.TextureOffsetY := 0;

    SplashGameGUI.DrawScaled(0.0, -0.06, 0.9, 0.36, 0.08,
      DrawD3DSprite_Filtered, Alpha);

    GoingMan.FramesPerLine := 7;
    GoingMan.Delay := 15;
    GoingMan.fMaxFrame := 18;
    GoingMan.TextureOffsetX := 0;
    GoingMan.TextureOffsetY := 270;

    GoingMan.fSize := 0.05;
    GoingMan.Draw(GoingMan.Position.x, -0.2, 0.9,
      DrawD3DSprite_Filtered, Alpha);

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


procedure TD3DGameApp.RenderFieryText(var pFont: CD3DFont; strText:
  PAnsiChar);
var
  i, r, g, b, a, dwColor: Cardinal;
  x, y, v1, red1, grn1, blu1, a1, r1: Single;

begin
  if not Assigned(pFont) or not Assigned(@strText) then Exit;

  if not Assigned(m_pd3dDevice) then Exit;

  // Begin the scene
  if SUCCEEDED(m_pd3dDevice.BeginScene) then
  begin
    // Erase the screen
    m_pd3dDevice.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, 0,
     1.0, 0 );

    // Render the fiery portion of the text
    for i:=0 to 19 do
    begin
      x := -0.5;
      y :=  1.8;

      v1 := rnd(0.0, 1.0);
      red1 := v1*v1*v1;
      grn1 := v1*v1;
      blu1 := v1;


      a1 := rnd(0.0, 2*D3DX_PI);
      r1 := v1 * 0.05;

      x := x + r1*sin(a1);
      y := y + r1*cos(a1);

      if cos(a1) < 0.0 then
        y := y - 2*r1*cos(a1)*cos(a1);

      r := Round((1.0-red1)*256);
      g := Round((1.0-grn1)*256);
      b := Round((1.0-blu1)*256);
      a := 255;
      dwColor := (a shl 24) + (r shl 16) + (g shl 8) + b;

      m_pd3dDevice.SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_ONE );
      m_pd3dDevice.SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

      pFont.DrawTextScaled( x, y, 0.9, 0.25, 0.25, dwColor, strText,
        D3DFONT_FILTERED );
    end;

    // Render the plain, black portion of the text
    m_pd3dDevice.SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    m_pd3dDevice.SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    x := -0.5;
    y :=  1.8;
    pFont.DrawTextScaled( x, y, 0.9, 0.25, 0.25, $ff000000, strText,
      D3DFONT_FILTERED );


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

procedure TD3DGameApp.DrawDisplayList;
var
  matWorld: TD3DXMatrix;
  strBuffer: array [0..MAX_PATH] of Char;
  vPlayerPt, vForwardPt, vSidePt, vForwardDir, vSideDir, vNormalDir, vTmp:
    TD3DXVECTOR3;
  matLookAt, matRotateZ: TD3DXMATRIX;

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

    pdeCur.pframeRoot.matRot:=   m_ArcBall.GetRotationMatrix^;
    pdeCur.pframeRoot.matTrans:= m_ArcBall.GetTranslationMatrix^;

    pdeCur:= pdeCur.pdeNext;
  end;

  //**

  PlayerAnimMesh.Render;


  D3DXMatrixIdentity( matWorld );
  m_pd3dDevice.SetTransform( D3DTS_WORLD, matWorld );

  // Set the app view matrix for normal viewing
  m_pd3dDevice.SetTransform( D3DTS_VIEW, m_Camera.GetViewMatrix );

  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
  m_pd3dDevice.SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
  m_pd3dDevice.SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
  m_pd3dDevice.SetRenderState( D3DRS_ALPHABLENDENABLE, Cardinal(FALSE) );
  m_pd3dDevice.SetRenderState( D3DRS_DITHERENABLE, Cardinal(FALSE) );
  m_pd3dDevice.SetRenderState( D3DRS_ZENABLE, Cardinal(TRUE) );
  m_pd3dDevice.SetRenderState( D3DRS_LIGHTING, Cardinal(TRUE) );
  m_pd3dDevice.SetRenderState( D3DRS_AMBIENT, $33333333 );

  //m_pd3dDevice.SetTexture( 0, nil);

  // Begin the scene
  if SUCCEEDED(m_pd3dDevice.BeginScene) then
  begin
    // Clear the display
    m_pd3dDevice.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, $FF000000, 1.0,
      0 );

    // Draw PlanetMesh model
    //** PlanetMesh.Render( m_pd3dDevice );

    // Render the player mesh
    if m_pPlayer.bVisible and not(m_bFirstPersonView) then
    begin
      // Point of player, on terrain
      vPlayerPt.x :=  m_pPlayer.vPos.x;
      vPlayerPt.z := -m_pPlayer.vPos.y;
      vPlayerPt.y := 0.1 + HeightField( vPlayerPt.x, vPlayerPt.z );

      // Point ahead of player, on terrain
      vForwardPt.x := vPlayerPt.x+sin(m_pPlayer.fAngle);
      vForwardPt.z := vPlayerPt.z+cos(m_pPlayer.fAngle);
      vForwardPt.y := 0.1 + HeightField( vForwardPt.x, vForwardPt.z );

      // Point to side of player, on terrain
      vSidePt.x := vPlayerPt.x+sin(m_pPlayer.fAngle + D3DX_PI/2.0);
      vSidePt.z := vPlayerPt.z+cos(m_pPlayer.fAngle + D3DX_PI/2.0);
      vSidePt.y := 0.1 + HeightField( vSidePt.x, vSidePt.z );

      // Compute vectors of the player's orientation
      vForwardDir := D3DXVec3Subtract(vForwardPt, vForwardPt, vPlayerPt)^;
      vSideDir    := D3DXVec3Subtract(vSideDir, vSidePt, vPlayerPt)^;
      D3DXVec3Cross( vNormalDir, vForwardDir, vSideDir );

      // Construct matrix to orient player
      D3DXMatrixRotationZ( matRotateZ, m_pPlayer.fRoll );
      D3DXVec3Subtract(vTmp, vPlayerPt, vForwardDir);
      D3DXMatrixLookAtLH( matLookAt, vPlayerPt, vTmp, vNormalDir );
      D3DXMatrixInverse( matLookAt, nil, matLookAt );
      D3DXMatrixIdentity( matWorld );
      D3DXMatrixMultiply( matWorld, matWorld, matRotateZ );
      D3DXMatrixMultiply( matWorld, matWorld, matLookAt );

      // Set renderstates for rendering the player
      m_pd3dDevice.SetTransform( D3DTS_WORLD, matWorld );
      m_pd3dDevice.SetRenderState( D3DRS_LIGHTING, Cardinal(TRUE) );
      m_pd3dDevice.SetRenderState( D3DRS_NORMALIZENORMALS, Cardinal(TRUE));
      m_pd3dDevice.SetRenderState( D3DRS_ALPHABLENDENABLE, Cardinal(FALSE));
      m_pd3dDevice.SetRenderState( D3DRS_ALPHATESTENABLE, Cardinal(FALSE));

      // Render the player - opaque parts
      PlayerMesh.Render( m_pd3dDevice, TRUE, FALSE );

      // Render the player - transparent parts
      m_pd3dDevice.SetRenderState( D3DRS_ALPHABLENDENABLE, Cardinal(TRUE) );
      m_pd3dDevice.SetRenderState( D3DRS_ZWRITEENABLE, Cardinal(FALSE) );
      m_pd3dDevice.SetRenderState( D3DRS_SRCBLEND,     D3DBLEND_ONE );
      m_pd3dDevice.SetRenderState( D3DRS_DESTBLEND,    D3DBLEND_ONE );
      PlayerMesh.Render( m_pd3dDevice, FALSE, TRUE );
      m_pd3dDevice.SetRenderState( D3DRS_ZWRITEENABLE, Cardinal(TRUE) );


    end;


    // Display help
    strBuffer := 'Press Escape for quit';
    //m_pGameFont.DrawTextScaled( -1.0, 1.1, 0.9, 0.05, 0.05,
    //  $ffffff00, strBuffer, D3DFONT_FILTERED );

    m_pGameFont.DrawText(0, 0, $FFFFFF00, strBuffer);

    CountFPS;

    m_pd3dDevice.EndScene;
  end;
  
end;

procedure TD3DGameApp.UpdateDisplayList;
var
  fTimeLapsed: Single;
  start, aend: Cardinal;
  vEyePt, vLookatPt, vUpVec: array[0..NUMVIEWMODES-1] 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 );

  UpdateInput;

  if m_bPaused then Exit;

  if m_pPlayer.fShowDelay > 0.0 then
  begin
    m_pPlayer.fShowDelay := m_pPlayer.fShowDelay - fTimeLapsed;

    if m_pPlayer.fShowDelay <= 0.0 then
    begin
      m_pPlayer.vVel       := D3DXVECTOR3( 0.0, 0.0, 0.0 );
      m_pPlayer.fShowDelay := 0.0;
      m_pPlayer.bVisible   := True;
      m_pPlayer.bExploded  := False;
    end;
  end;

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

  // Update the view
  if m_UserInput.bDoChangeView then
  begin
    m_bAnimatingViewChange := TRUE;
    m_UserInput.bDoChangeView := FALSE;
  end;

  if m_bAnimatingViewChange then
  begin
    m_fViewTransition := m_fViewTransition + fTimeLapsed;

    if m_fViewTransition >= 1.0 then
    begin
      Inc(m_dwViewMode);
      if m_dwViewMode >= NUMVIEWMODES then
        m_dwViewMode := 0;

      m_fViewTransition      := 0.0;
      m_bAnimatingViewChange := FALSE;
    end;
  end;

  fX :=  m_pPlayer.vPos.x;
  fZ := -m_pPlayer.vPos.y;
  fY := 0.1 + HeightField( fX, fZ );

  // 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 := 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 < (NUMVIEWMODES-1) 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)^)^;

  PlayerAnimMesh.FrameMove;  

  // Shake screen if ship exploded
  if m_pPlayer.bExploded then
    vEyePt0 := D3DXVec3Add(vEyePt0, vEyePt0,
      D3DXVec3Scale(vTmp, D3DXVECTOR3(rnd, rnd, rnd),
        m_pPlayer.fShowDelay / 50.0)^)^;


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


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

procedure TD3DGameApp.CreateInputObjects;
begin
  SetDefaultDefKeys;
  m_DXInput := TDXInput.Create(Self);
end;

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

procedure TD3DGameApp.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_hWndMain, 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;
end;

function TD3DGameApp.CreateDisplayObjects(h_Wnd: HWND): HRESULT;
const
  fmtFullscreenArray: array[0..4] of TD3DFORMAT =
  (
      D3DFMT_R5G6B5,
      D3DFMT_X1R5G5B5,
      D3DFMT_A1R5G5B5,
      D3DFMT_X8R8G8B8,
      D3DFMT_A8R8G8B8
  );
  numFullscreenFmts: Integer = sizeof(fmtFullscreenArray) div
    sizeof(fmtFullscreenArray[0]);
  
  fmtTextureArray: array[0..2] of TD3DFORMAT =
  (
      D3DFMT_A1R5G5B5,
      D3DFMT_A4R4G4B4,
      D3DFMT_A8R8G8B8
  );
  numTextureFmts: Integer = sizeof(fmtTextureArray) div
    sizeof(fmtTextureArray[0]);
var
  iFmt: Integer;
  pD3D: IDIRECT3D8;
  hr: HResult;
begin
  // Construct a new display
  pD3D := Direct3DCreate8( D3D_SDK_VERSION );
  if not Assigned(pD3D) then
  begin
    CleanupAndDisplayError( GAME_ERR_NODIRECT3D );
    result := E_FAIL;
    Exit;
  end;

  // Get the current desktop format
  pD3D.GetAdapterDisplayMode( D3DADAPTER_DEFAULT, m_DesktopMode );



  // Find a pixel format that will be good for fullscreen back buffers
  for iFmt := 0 to numFullscreenFmts-1 do
  begin
    if SUCCEEDED(pD3D.CheckDeviceType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
      fmtFullscreenArray[iFmt], fmtFullscreenArray[iFmt], FALSE)) then
    begin
      m_d3dfmtFullscreen := fmtFullscreenArray[iFmt];
      break;
    end;
  end;



  // Find a format that is supported as a texture map for the current mode
  for iFmt := 0 to numTextureFmts-1 do
  begin
    if SUCCEEDED(pD3D.CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
      m_DesktopMode.Format, 0, D3DRTYPE_TEXTURE, fmtTextureArray[iFmt])) then
    begin
      m_d3dfmtTexture := fmtTextureArray[iFmt];
      break;
    end;
  end;

  // Set up presentation parameters for the display
  ZeroMemory(@m_d3dpp, sizeof(m_d3dpp) );
  m_d3dpp.Windowed         := not m_bFullScreen;
  m_d3dpp.BackBufferCount  := 1;
  m_d3dpp.SwapEffect       := D3DSWAPEFFECT_DISCARD;
  m_d3dpp.EnableAutoDepthStencil := TRUE;
  m_d3dpp.AutoDepthStencilFormat := D3DFMT_D16;
  if m_bFullScreen then
  begin
    m_d3dpp.hDeviceWindow    := h_Wnd;
    m_d3dpp.BackBufferWidth  := m_dwScreenWidth;
    m_d3dpp.BackBufferHeight := m_dwScreenHeight;
    m_d3dpp.BackBufferFormat := m_d3dfmtFullscreen;
  end
  else
    m_d3dpp.BackBufferFormat := m_DesktopMode.Format;


  // Create the device
  hr := pD3D.CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, h_Wnd,
    D3DCREATE_SOFTWARE_VERTEXPROCESSING, m_d3dpp, m_pd3dDevice );
  SAFE_RELEASE(pD3D);

  if FAILED(hr) then
  begin
    CleanupAndDisplayError( GAME_ERR_NOD3DDEVICE );
    result := E_FAIL;
    Exit;
  end;

  // Create some game fonts
  m_pGameFont := CD3DFont.Create('MS Sans Serif', 8, 0 );
  m_pGameFont.InitDeviceObjects( m_pd3dDevice );

  // Create a viewport covering sqaure
  if FAILED(m_pd3dDevice.CreateVertexBuffer( 4*sizeof(TSCREENVERTEX),
    D3DUSAGE_WRITEONLY, D3DFVF_SCREENVERTEX, D3DPOOL_MANAGED, m_pViewportVB)) then
  begin
    CleanupAndDisplayError(GAME_ERR_NO3DRESOURCES);
    result := E_FAIL;
    Exit;
  end;

  {hr := LoadCursor;
  if Failed(hr) then begin
    result := hr;
    Exit;
  end;}

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

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

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

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

  //**
  if (PlayerAnimMesh.m_pdeHead <> nil) then
    m_ArcBall.SetRadius(PlayerAnimMesh.m_pdeHead.fRadius);
  m_ArcBall.SetRightHanded(TRUE);
  //**

  // Now that all the display objects are created, "restore" them (create
  // local mem objects and set state)

  if FAILED( RestoreDisplayObjects ) then begin
    result := E_FAIL;
    Exit;
  end;     

  // The display is now ready
  m_bDisplayReady := TRUE;
  result := S_OK;
end;

function TD3DGameApp.RestoreDisplayObjects: HRESULT;
var
  dwStyle, i: Cardinal;
  rc, rcWork: TRECT;
  vp: TD3DVIEWPORT8;
  sx, sy: Single;
  v: PScreenVertexArray;
  mtrl: TD3DMATERIAL8;
  light: TD3DLIGHT8;
begin
  if not m_bFullScreen then
  begin
    // If we are still a WS_POPUP window we should convert to a normal app
    // window so we look like a windows app.
    dwStyle  := GetWindowLong(m_hWndMain, GWL_STYLE);
    dwStyle := dwStyle and not WS_POPUP;
    dwStyle := dwStyle or WS_OVERLAPPED or WS_CAPTION or WS_THICKFRAME
      or WS_MINIMIZEBOX;
    SetWindowLong(m_hWndMain, GWL_STYLE, dwStyle );

    // Set window size

    SetRect( rc, 0, 0, m_dwScreenWidth, m_dwScreenHeight);

    AdjustWindowRectEx( rc, dwStyle, GetMenu(m_hWndMain) <> 0,
      GetWindowLong(m_hWndMain, GWL_EXSTYLE) );

    SetWindowPos(m_hWndMain, 0, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
      SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE );

    SetWindowPos(m_hWndMain, HWND_NOTOPMOST, 0, 0, 0, 0,
      SWP_NOSIZE or SWP_NOMOVE or SWP_NOACTIVATE );

    //  Make sure our window does not hang outside of the work area
    SystemParametersInfo( SPI_GETWORKAREA, 0, @rcWork, 0 );
    GetWindowRect(m_hWndMain, rc );
    if rc.left < rcWork.left then rc.left := rcWork.left;
    if rc.top  < rcWork.top then rc.top  := rcWork.top;
    SetWindowPos(m_hWndMain, 0, rc.left, rc.top, 0, 0,
      SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE );
  end;
  

  // Create the device-dependent objects for the file-based mesh objects
  m_pGameFont.RestoreDeviceObjects;

  // Get viewport dimensions
  m_pd3dDevice.GetViewport(vp);
  sx := vp.Width;
  sy := vp.Height;

  // Setup dimensions for the viewport covering sqaure
  m_pViewportVB.Lock( 0, 0, PBYTE(v), 0 );
  i := 0;
  v[i].p := D3DXVECTOR4( 0, sy, 0.0, 1.0);
  v[i+1].p := D3DXVECTOR4( 0, 0, 0.0, 1.0);
  v[i+2].p := D3DXVECTOR4(sx, sy, 0.0, 1.0);
  v[i+3].p := D3DXVECTOR4(sx, 0, 0.0, 1.0);
  m_pViewportVB.Unlock;

  PlanetMesh.RestoreDeviceObjects(m_pd3dDevice);
  PlayerMesh.RestoreDeviceObjects(m_pd3dDevice);

  //**
    
    m_ArcBall.SetWindow(m_d3dpp.BackBufferWidth, m_d3dpp.BackBufferHeight, 2.0);
  //**
  PlayerAnimMesh.RestoreDeviceObjects;

  // 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) );
  m_Camera.SetProjParams( D3DX_PI/4, 1.0, 0.1, 100.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 );

  // Setup a material
  D3DUtil_InitMaterial( mtrl, 1.0, 1.0, 1.0 );
  m_pd3dDevice.SetMaterial( mtrl );

  // Set up lighting states
  D3DUtil_InitLight( light, D3DLIGHT_DIRECTIONAL, 1.0, -1.0, 1.0 );
  m_pd3dDevice.SetLight( 0, light );
  m_pd3dDevice.LightEnable( 0, TRUE );

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

  // Set miscellaneous render states
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_POINT );
  m_pd3dDevice.SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_POINT );
  m_pd3dDevice.SetRenderState( D3DRS_ZENABLE, Cardinal(TRUE) );

  
  SplashGameGUI.RestoreDeviceObjects(m_Camera);
  GoingMan.RestoreDeviceObjects(m_Camera);

  result := S_OK;
end;

function TD3DGameApp.InvalidateDisplayObjects: HRESULT;
begin
  m_pGameFont.InvalidateDeviceObjects;

  PlanetMesh.InvalidateDeviceObjects;
  PlayerMesh.InvalidateDeviceObjects;
  PlayerAnimMesh.InvalidateDeviceObjects;

  SplashGameGUI.InvalidateDeviceObjects;
  GoingMan.InvalidateDeviceObjects;

  result := S_OK;
end;

function TD3DGameApp.DestroyDisplayObjects: HResult;
begin
  SAFE_DELETE(m_pGameFont);

  SAFE_DELETE(PlanetMesh);
  SAFE_DELETE(PlayerMesh);
  SAFE_DELETE(PlayerAnimMesh);

  SAFE_RELEASE(m_pViewportVB);
  
  SAFE_DELETE(SplashGameGUI);
  SAFE_DELETE(GoingMan);

  result := S_OK;
end;

function TD3DGameApp.SwitchDisplayModes(bFullScreen: Boolean;
  dwWidth, dwHeight: Cardinal): HRESULT;
var
  hr: HResult;  
begin
  if not m_bIsActive or not m_bDisplayReady then begin
    result := S_OK;
    Exit;
  end;

  // Check to see if a change was actually requested
  if bFullScreen then
  begin
    if ((m_dwScreenWidth = dwWidth) and (m_dwScreenHeight = dwHeight)) and
      (m_bFullScreen = bFullScreen ) then
    begin
      result := S_OK;
      Exit;
    end;
  end
  else
  begin
    if not m_bFullScreen then begin
      result := S_OK;
      Exit;
    end;  
  end;

  // Invalidate the old display objects
  m_bDisplayReady := FALSE;
  InvalidateDisplayObjects;

  // Set up the new presentation paramters
  if bFullScreen then
  begin
    m_d3dpp.Windowed         := FALSE;
    m_d3dpp.hDeviceWindow    := m_hWndMain;
    m_dwScreenWidth  := dwWidth;
    m_d3dpp.BackBufferWidth  := dwWidth;
    m_dwScreenHeight := dwHeight;
    m_d3dpp.BackBufferHeight := dwHeight;
    m_d3dpp.BackBufferFormat := m_d3dfmtFullscreen;

    //m_d3dpp.FullScreen_RefreshRateInHz := D3DPRESENT_RATE_UNLIMITED;//m_DesktopMode.RefreshRate;
    //D3DPRESENT_RATE_DEFAULT;
  end
  else
  begin
    m_d3dpp.Windowed         := TRUE;
    m_d3dpp.hDeviceWindow    := 0;
    m_d3dpp.BackBufferWidth  := 0;
    m_d3dpp.BackBufferHeight := 0;

    m_d3dpp.BackBufferFormat := m_DesktopMode.Format;

    //m_d3dpp.FullScreen_RefreshRateInHz := 0;
  end;

  // Reset the device
  hr := m_pd3dDevice.Reset( m_d3dpp );
  if SUCCEEDED(hr) then
  begin
    m_bFullScreen   := bFullScreen;
    hr := RestoreDisplayObjects;
    if SUCCEEDED(hr) then
    begin
      m_bDisplayReady := TRUE;
      result := S_OK;
      Exit;
    end;
  end;

  // If we get here, a fatal error occurred
  PostMessage( m_hWndMain, WM_CLOSE, 0, 0 );
  result := E_FAIL;
end;

procedure TD3DGameApp.ShowFrame;
begin
  if not Assigned(m_pd3dDevice) then Exit;

  // Present the backbuffer contents to the front buffer
  m_pd3dDevice.Present( nil, nil, 0, nil );
end;


procedure TD3DGameApp.CleanupAndDisplayError(dwError: Cardinal);
var
  strDbgOut, strMsgBox: PChar;
begin
  strDbgOut := nil;
  strMsgBox := nil;
  // Cleanup the app
  FinalCleanup;

  // Make the cursor visible
  SetCursor(Windows.LoadCursor( 0, IDC_ARROW ) );
  m_bMouseVisible := TRUE;

  // Get the appropriate error strings
  case dwError of

    GAME_ERR_NODIRECT3D: begin
      strDbgOut := 'Could not create Direct3D\n';
      strMsgBox := 'Could not create Direct3D.\n\n' +
                  'Please make sure you have the latest DirectX\n' +
                  '.dlls installed on your system.';
    end;
    GAME_ERR_NOD3DDEVICE: begin
        strDbgOut := 'Could not create a Direct3D device\n';
        strMsgBox := 'Could not create a Direct3D device. Your\n' +
                    'graphics accelerator is not sufficient to\n' +
                    'run this demo, or your desktop is using\n' +
                    'a color format that cannot be accelerated by\n' +
                    'your graphics card (try 16-bit mode).';
    end;
    GAME_ERR_NOTEXTURES: begin
        strDbgOut := 'Could not load textures\n';
        strMsgBox := 'Couldn`t load game textures.\n\n' +
                    'Either your graphics hardware does not have\n' +
                    'sufficient resources, or the DirectX SDK was\n' +
                    'not properly installed.';
    end;
    GAME_ERR_NOGEOMETRY: begin
        strDbgOut := 'Could not load .x models\n';
        strMsgBox := 'Couldn`t load game geometry.\n\n' +
                     'Either your graphics hardware does not have\n' +
                     'sufficient resources, or the DirectX SDK was\n' +
                     'not properly installed.';
    end;
    GAME_ERR_NO3DRESOURCES: begin
        strDbgOut := 'Couldn`t load create a d3d object\n';
        strMsgBox := 'Couldn`t create display objects.\n' +
                    'Yourr graphics hardware does not have\n' +
                    'sufficient resources to run this app.';
    end;
    GAME_ERR_NOINPUT: begin
        strDbgOut := 'Could not create input objects\n';
        strMsgBox := 'Could not create input objects.';
    end;
  end;

  // Output the error strings
  OutputDebugString( strDbgOut );
  MessageBox(m_hWndMain, strMsgBox, PChar('Error'), MB_OK);
end;

procedure TD3DGameApp.CountFPS;
{$WRITEABLECONST ON}
const
  fLastTime: Single = 0.0;
  dwFrames: DWORD = 0;
{$WRITEABLECONST OFF}
var
  fTime: Single;
  
begin
  if not m_bShowFPSOn then Exit;

  fTime := DXUtil_Timer(TIMER_GETABSOLUTETIME);
  Inc(dwFrames);

  // Update the scene stats once per second
  if ((fTime - fLastTime) > 1.0) then
  begin
    m_fFPS    := dwFrames / (fTime - fLastTime);
    fLastTime := fTime;
    dwFrames  := 0;

    StrFmt(m_strFrameStats, '%.02f fps', [m_fFPS]);
  end;

  // Output statistics
  m_pGameFont.DrawText(0,  20, D3DCOLOR_ARGB(255, 0, 0,255), m_strFrameStats);
end;



//-----------------------------------------------------------------------------
// Name: remaked D3DUtil_SetColorKey()
// Desc: Changes all pixels matching the colorkey to transparent, black.
//-----------------------------------------------------------------------------
function SetSurfaceColorKey(var pSurface: IDirect3DSurface8; dwColorKey: DWORD): HRESULT;
var
  r,g,b: DWORD;
  d3dsd: TD3DSurfaceDesc;
  d3dlr: TD3DLockedRect;
  x, y: DWORD;
begin
  // Get colorkey's red, green, and blue components
  r := ((dwColorKey and $00ff0000) shr 16);
  g := ((dwColorKey and $0000ff00) shr 8);
  b := ((dwColorKey and $000000ff) shr 0);

  // Put the colorkey in the texture's native format
  //pSurface.GetLevelDesc(0, d3dsd);
  pSurface.GetDesc(d3dsd);
  
  if (d3dsd.Format = D3DFMT_A4R4G4B4) then
      dwColorKey := $f000 + ((r shr 4) shl 8) + ((g shr 4) shl 4) + (b shr 4)
  else if (d3dsd.Format = D3DFMT_A1R5G5B5) then
      dwColorKey := $8000 + ((r shr 3) shl 10) + ((g shr 3) shl 5) + (b shr 3)
  else if (d3dsd.Format <> D3DFMT_A8R8G8B8) then
      begin Result:= E_FAIL; Exit; end;

  // Lock the texture
  if FAILED(pSurface.LockRect(d3dlr, nil, 0)) then
  begin
    Result:= E_FAIL;
    Exit;
  end;

  // Scan through each pixel, looking for the colorkey to replace
  for y:= 0 to d3dsd.Height - 1 do
  begin
    for x:= 0 to d3dsd.Width - 1 do
    begin
      if (d3dsd.Format = D3DFMT_A8R8G8B8) then
      begin
        // Handle 32-bit formats
        if PDWord(DWord(d3dlr.pBits) + 4*(d3dsd.Width*y+x))^ = dwColorKey then
           PDWord(DWord(d3dlr.pBits) + 4*(d3dsd.Width*y+x))^:= 00000000;
      end else
      begin
        // Handle 16-bit formats
        if TWordArray(d3dlr.pBits^)[d3dsd.Width*y+x] = dwColorKey then
           TWordArray(d3dlr.pBits^)[d3dsd.Width*y+x]:= $0000;
      end;
    end;
  end;

  // Unlock the texture and return OK.
  pSurface.UnlockRect;
  result:= S_OK;
end;


//-----------------------------------------------------------------------------
// Name: remaked D3DUtil_SetDeviceCursor
// Desc: Gives the D3D device a cursor with image and hotspot from hCursor.
//-----------------------------------------------------------------------------
function SetDeviceCursor(pd3dDevice: IDirect3DDevice8; hCursor: HCURSOR;
  bAddWatermark: BOOL): HRESULT;
const
  wMask: array [0..4] of Word = ($ccc0, $a2a0, $a4a0, $a2a0, $ccc0);
label
  End_;
type
  PACOLORREF = ^ACOLORREF;
  ACOLORREF = array[0..0] of COLORREF;
type
  pImg = ^img;
  img = array[0..16000] of DWORD;
var
  hr: HRESULT;
  iconinfo_: TIconInfo;
  bBWCursor: BOOL;
  pCursorBitmap: IDirect3DSurface8;
  hdcColor: HDC;
  hdcMask: HDC;
  hdcScreen: HDC;
  bm: TBitmap;
  dwWidth: DWORD;
  dwHeightSrc: DWORD;
  dwHeightDest: DWORD;
  crColor: COLORREF;
  crMask: COLORREF;
  x,y: Cardinal;
  bmi: TBitmapInfo;
  pcrArrayColor: PACOLORREF;
  pcrArrayMask: PACOLORREF;
  pBitmap: pImg;
  hgdiobjOld: HGDIOBJ;
  lr: TD3DLockedRect;
begin
  hr := E_FAIL;
  pCursorBitmap := nil;
  hdcColor := 0;
  hdcMask := 0;
  hdcScreen := 0;
  pcrArrayColor := nil;
  pcrArrayMask := nil;

  ZeroMemory(@iconinfo_, SizeOf(TIconInfo));
  if not GetIconInfo(hCursor, iconinfo_) then
    goto End_;

  if (0 = GetObject(iconinfo_.hbmMask, SizeOf(TBitmap), @bm)) then
    goto End_;
  dwWidth := bm.bmWidth;
  dwHeightSrc := bm.bmHeight;

  if (iconinfo_.hbmColor = 0) then
  begin
    bBWCursor := TRUE;
    dwHeightDest := dwHeightSrc div 2;
  end else
  begin
    bBWCursor := FALSE;
    dwHeightDest := dwHeightSrc;
  end;

  // Create a surface for the fullscreen cursor
  hr:= pd3dDevice.CreateImageSurface(dwWidth, dwHeightDest, D3DFMT_A8R8G8B8,
                                     pCursorBitmap);
  if FAILED(hr) then
    goto End_;

  // pcrArrayMask = new DWORD[dwWidth * dwHeightSrc];
  GetMem(pcrArrayMask, SizeOf(DWORD)*(dwWidth * dwHeightSrc));

  ZeroMemory(@bmi, sizeof(bmi));
  bmi.bmiHeader.biSize := sizeof(bmi.bmiHeader);
  bmi.bmiHeader.biWidth := dwWidth;
  bmi.bmiHeader.biHeight := dwHeightSrc;
  bmi.bmiHeader.biPlanes := 1;
  bmi.bmiHeader.biBitCount := 32;
  bmi.bmiHeader.biCompression := BI_RGB;

  hdcScreen := GetDC(0);
  hdcMask := CreateCompatibleDC(hdcScreen);
  if (hdcMask = 0) then
  begin
    hr := E_FAIL;
    goto End_;
  end;
  hgdiobjOld := SelectObject(hdcMask, iconinfo_.hbmMask);
  GetDIBits(hdcMask, iconinfo_.hbmMask, 0, dwHeightSrc, pcrArrayMask, bmi,
    DIB_RGB_COLORS);
  SelectObject(hdcMask, hgdiobjOld);

  if (not bBWCursor) then
  begin
    // pcrArrayColor = new DWORD[dwWidth * dwHeightDest];
    GetMem(pcrArrayColor, SizeOf(DWORD)*(dwWidth * dwHeightDest));
    hdcColor := CreateCompatibleDC(GetDC(0));
    if (hdcColor = 0) then
    begin
      hr := E_FAIL;
      goto End_;
    end;
    SelectObject(hdcColor, iconinfo_.hbmColor);
    GetDIBits(hdcColor, iconinfo_.hbmColor, 0, dwHeightDest, pcrArrayColor, bmi,
      DIB_RGB_COLORS);
  end;

  // Transfer cursor image into the surface
  pCursorBitmap.LockRect(lr, nil, 0);
  pBitmap := lr.pBits;
  for y:= 0 to dwHeightDest - 1 do
  begin
    for x:= 0 to dwWidth - 1 do
    begin
      if bBWCursor then
      begin
        crColor := pcrArrayMask^[dwWidth*(dwHeightDest-1-y) + x];
        crMask := pcrArrayMask^[dwWidth*(dwHeightSrc-1-y) + x];
      end else
      begin
        crColor := pcrArrayColor^[dwWidth*(dwHeightDest-1-y) + x];
        crMask := pcrArrayMask^[dwWidth*(dwHeightDest-1-y) + x];
      end;
      if (crMask = 0) then
        pBitmap^[dwWidth*y + x] := $ff000000 or crColor
      else
        pBitmap^[dwWidth*y + x] := $00000000;

      // It may be helpful to make the D3D cursor look slightly
      // different from the Windows cursor so you can distinguish
      // between the two when developing/testing code.  When
      // bAddWatermark is TRUE, the following code adds some
      // small grey "D3D" characters to the upper-left corner of
      // the D3D cursor image.

      //if( bAddWatermark && x < 12 && y < 5 )
      if bAddWatermark and (x < 12) and (y < 5) then
      begin
          // 11.. 11.. 11.. .... CCC0
          // 1.1. ..1. 1.1. .... A2A0
          // 1.1. .1.. 1.1. .... A4A0
          // 1.1. ..1. 1.1. .... A2A0
          // 11.. 11.. 11.. .... CCC0

          // if( wMask[y] & (1 << (15 - x)) )
          if (wMask[y] and (1 shl (15 - x)) <> 0) then
          begin
            pBitmap[dwWidth*y + x]:= pBitmap[dwWidth*y + x] or $ff808080;
          end;
      end;
    end;
  end;
  pCursorBitmap.UnlockRect;

  SetSurfaceColorKey(pCursorBitmap, $FF000000);

  // Set the device cursor
  hr := pd3dDevice.SetCursorProperties(iconinfo_.xHotspot,
      iconinfo_.yHotspot, pCursorBitmap);
  if FAILED(hr) then
    goto End_;

  hr := S_OK;

End_:
  if (iconinfo_.hbmMask <> 0)  then DeleteObject(iconinfo_.hbmMask);
  if (iconinfo_.hbmColor <> 0) then DeleteObject(iconinfo_.hbmColor);
  if (hdcScreen <> 0)          then ReleaseDC(0, hdcScreen);
  if (hdcColor <> 0)           then DeleteDC(hdcColor);
  if (hdcMask <> 0)            then DeleteDC(hdcMask);
  // SAFE_DELETE_ARRAY(pcrArrayColor);
  FreeMem(pcrArrayColor);
  // SAFE_DELETE_ARRAY(pcrArrayMask);
  FreeMem(pcrArrayMask);
  SAFE_RELEASE(pCursorBitmap);
  Result:= hr;
end;


function TD3DGameApp.LoadCursor: HResult;
begin
  hGameCursor := LoadImage(g_hInst, MainMenuCursorImage, IMAGE_ICON,
    0, 0, LR_LOADFROMFILE);

  result := SetDeviceCursor(m_pd3dDevice, hGameCursor, False);
end;


procedure TD3DGameApp.ShowCursor;
var
  hr: HResult;
begin
  if m_pd3dDevice = nil then Exit;

  hr := LoadCursor;
  if Failed(hr) then MessageBox(g_d3dApp.m_hWndMain, 'Error loading cursor file!',
    'Cursor Error', MB_OK);

  m_pd3dDevice.ShowCursor(True);
end;


{  TDisplayObject  }

constructor TDisplayObject.Create(GameObj: TGameObject; p, v: TD3DVECTOR);
begin
  // Set object attributes
  pNext    := nil;
  pPrev    := nil;
  bVisible := TRUE;
  dwType   := GameObj;
  vPos     := p;
  vVel     := v;
end;

{  TPlayer  }

constructor TPlayer.Create(p: TD3DVECTOR);
begin
  inherited Create( goPlayer, p, D3DXVECTOR3(0, 0, 0) );
  fSize           := 10.0 / 256.0;
  bExploded       := False;
  fShowDelay      := 0.0;

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



end.
