Einzelnen Beitrag anzeigen

oXmoX

Registriert seit: 8. Jun 2005
85 Beiträge
 
#1

Projektion mit Direct3D berechnen

  Alt 27. Jul 2005, 21:39
Hallo!

Ich möchte mit Direct3D einen 2D-Schriftzug auf dem Bildschirm genau dort zeichnen, wo sich die Kante eines Würfels befindet.

Hier erstmal mein Quellcode

Code:
unit RendererD3D;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Direct3D8, d3dx8, ExtCtrls, ComCtrls;

const
  Pi180      = Pi/180.0;
  Fovy      = Pi/4;     // Öffnungswinkel des Sichtkegels (Field of view)
  ViewDist  = 15;       // Abstand des Betrachters vom Nullpunkt
  ViewHeight = 5;        // Höhe des Betrachters
  Delta     = 0.01;     // Winkelinkrement für Animation

  CubeCount = 36;

type
  TMyVertex = record
    x,y,z    : single;
    color    : dword;
    end;

  // Vertexliste für Würfel
  TCube = array [0..CubeCount-1] of TMyVertex;

const
  D3D8T_CUSTOMVERTEX =D3DFVF_XYZ or D3DFVF_DIFFUSE;

  // Standardwürfel (Seitenlänge 2)
  NormCube : TCube = ( 
    (x :-1.0; y :-1.0; z :-1.0; color : $000000FF), // Vorn
    (x :-1.0; y : 1.0; z :-1.0; color : $000000FF),
    (x : 1.0; y : 1.0; z :-1.0; color : $000000FF),
    (x : 1.0; y : 1.0; z :-1.0; color : $000000FF),
    (x : 1.0; y :-1.0; z :-1.0; color : $000000FF),
    (x :-1.0; y :-1.0; z :-1.0; color : $000000FF),

    (x :-1.0; y :-1.0; z : 1.0; color : $00FF00FF), // Links
    (x :-1.0; y : 1.0; z : 1.0; color : $00FF00FF),
    (x :-1.0; y : 1.0; z :-1.0; color : $00FF00FF),
    (x :-1.0; y : 1.0; z :-1.0; color : $00FF00FF),
    (x :-1.0; y :-1.0; z :-1.0; color : $00FF00FF),
    (x :-1.0; y :-1.0; z : 1.0; color : $00FF00FF),

    (x : 1.0; y :-1.0; z : 1.0; color : $0000FF00), // Hinten
    (x : 1.0; y : 1.0; z : 1.0; color : $0000FF00),
    (x :-1.0; y : 1.0; z : 1.0; color : $0000FF00),
    (x :-1.0; y : 1.0; z : 1.0; color : $0000FF00),
    (x :-1.0; y :-1.0; z : 1.0; color : $0000FF00),
    (x : 1.0; y :-1.0; z : 1.0; color : $0000FF00),

    (x : 1.0; y :-1.0; z :-1.0; color : $0000FFFF), // Rechts
    (x : 1.0; y : 1.0; z :-1.0; color : $0000FFFF),
    (x : 1.0; y : 1.0; z : 1.0; color : $0000FFFF),
    (x : 1.0; y : 1.0; z : 1.0; color : $0000FFFF),
    (x : 1.0; y :-1.0; z : 1.0; color : $0000FFFF),
    (x : 1.0; y :-1.0; z :-1.0; color : $0000FFFF),

    (x :-1.0; y : 1.0; z :-1.0; color : $00FF0000), // Oben
    (x :-1.0; y : 1.0; z : 1.0; color : $00FF0000),
    (x : 1.0; y : 1.0; z : 1.0; color : $00FF0000),
    (x : 1.0; y : 1.0; z : 1.0; color : $00FF0000),
    (x : 1.0; y : 1.0; z :-1.0; color : $00FF0000),
    (x :-1.0; y : 1.0; z :-1.0; color : $00FF0000),

    (x : 1.0; y :-1.0; z :-1.0; color : $00FFFF00), // Unten
    (x : 1.0; y :-1.0; z : 1.0; color : $00FFFF00),
    (x :-1.0; y :-1.0; z : 1.0; color : $00FFFF00),
    (x :-1.0; y :-1.0; z : 1.0; color : $00FFFF00),
    (x :-1.0; y :-1.0; z :-1.0; color : $00FFFF00),
    (x : 1.0; y :-1.0; z :-1.0; color : $00FFFF00));

type
  TRendererD3D = class
    procedure StartScene(Handle: HWND);
    procedure StopScene;
  private
    lpd3d           : IDIRECT3D8;
    lpd3ddevice     : IDirect3DDevice8;
    D3dDevCaps      : TD3DCaps8;

    // Vertexbuffer für Würfel
    CubeVB : IDirect3DVertexBuffer8;

    // Rotationswinkel für den Beobachter in Grad
    RotY  : single ;

    HwVertexProcess,
    FullScreen,
    Animate         : boolean;

    FFont:  TFont;
    FDXFont: ID3DXFont;

    procedure D3DInit(Handle: HWND);
    procedure D3DShutdown;
    procedure D3DInitScene;
    procedure D3DKillScene;
    procedure D3DRender;
    procedure MyIdleHandler (Sender: TObject; var Done: Boolean);
  public
    constructor Create;
  end;

implementation

constructor TRendererD3D.Create;
begin
  // Font erzeugen
  Self.FFont := TFont.Create;
  with Self.FFont do
  begin
    Style := [fsBold];
    Size := 10;
    Name := 'Arial';
  end;

  lpd3d:=nil;
  lpd3ddevice:=nil;
  CubeVB:=nil;

  RotY:=0;
  Animate:=false;
  Application.OnIdle:= MyIdleHandler;
  end;

procedure TRendererD3D.D3DInit(Handle: HWND);
var
  hr   : HRESULT;
  d3dpp : TD3DPRESENTPARAMETERS;
  d3ddm : TD3DDISPLAYMODE;
  vp   : integer;
begin
  lpd3d:=Direct3DCreate8(D3D_SDK_VERSION);
  if(lpd3d=nil) then
    raise Exception.Create('Fehler beim Erstellen von Direct3D!');

  // Setze zunächst alle D3DPRESENT_PARAMETERS auf 0 
  ZeroMemory(@d3dpp,sizeof(d3dpp));
  with d3dpp do begin
    SwapEffect:=D3DSWAPEFFECT_DISCARD;
    hDeviceWindow:=Handle;     // Dies ist unser HWND von TForm

    // Wir brauche einen Z-Buffer also schalten wir ihn ein
    EnableAutoDepthStencil := TRUE;
    AutoDepthStencilFormat := D3DFMT_D16;

    // Initialisieren des Backbuffers
    Windowed:=not FullScreen;

    hr:=lpd3d.GetAdapterDisplayMode(D3DADAPTER_DEFAULT,d3ddm);
    if failed(hr) then
      raise Exception.Create('Fehler beim Ermitteln des Dislaymodes');
    BackBufferFormat := d3ddm.Format;

  end;

  // Hardware T&L? 
  hr:=lpd3d.GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,D3dDevCaps);
  if FAILED(hr) then
    raise Exception.Create('Fehler beim Abfragen der DevCaps');
  HwVertexProcess:=D3dDevCaps.DevCaps and D3DDEVCAPS_HWTRANSFORMANDLIGHT <> 0;

  if HwVertexProcess then vp:=D3DCREATE_HARDWARE_VERTEXPROCESSING
  else vp:=D3DCREATE_SOFTWARE_VERTEXPROCESSING;

  //Erstellen des D3D-Device
  hr:=lpd3d.CreateDevice(D3DADAPTER_DEFAULT,
                         D3DDEVTYPE_HAL,
                         Handle,
                         vp,
                         d3dpp,
                         lpd3ddevice);
  if FAILED(hr) then
    raise Exception.Create('Fehler beim Erzeugen des 3D-Device');
  end;

// *** D3DShutdown hier werden die Resourcen von D3D wieder freigegeben
procedure TRendererD3D.D3DShutdown;
begin
  if assigned(lpd3ddevice) then lpd3ddevice:=nil;
  if assigned(lpd3d) then lpd3d:=nil;
  end;

procedure TRendererD3D.D3DInitScene;
var
  hr           : HRESULT;
  vbVertices   : pByte;
  ProjMatrix   : TD3DXMATRIX;

begin
  HR := D3DXCreateFont(Self.lpd3ddevice, Self.FFont.Handle, Self.FDXFont);
  if HR <> S_OK then
    raise Exception.Create('Fehler beim Erzeugen des Font.');

  if assigned(lpd3ddevice) then with lpd3ddevice do begin
    // Vertex Buffer für Würfel
    hr:=CreateVertexBuffer (sizeof(TCube),
                            D3DUSAGE_WRITEONLY, // Nur Schreibzugriffe
                            D3D8T_CUSTOMVERTEX, // Unser Vertex
                            D3DPOOL_MANAGED,
                            CubeVB);            // Pointer zu unserem Buffer

    if FAILED(hr) then
      raise Exception.Create('Fehler beim Erstellen des Vertex Buffers');

    // Nun kopieren wir unsere Vertizes in den Buffer
    // Wir müssen es zuvor mit Lock festhalten, um es bearbeiten zu können
    with CubeVB do begin
      hr:=Lock(0, // Offset, an dem wir beginnen
               0, // Größe des locks ( 0 für alles )
               vbVertices, // Wenn erfolgreich dann hier ablegen
               0); // sonstige Flags
      if FAILED(hr) then
        raise Exception.Create('Fehler beim Locken des Vertex-Buffers');
      // Hier wird der Vertexbuffer kopiert.
      Move(NormCube,vbVertices^,SizeOf(TCube));
      // Und wieder loslassen
      Unlock;
      end;

    // Lighting abschalten
    SetRenderState(D3DRS_LIGHTING,0);
    // Z-Buffer beim Rendern einschalten
    SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE);

    // Erstelle eine Projektionsmatrix
    D3DXMatrixPerspectiveFovLH(ProjMatrix, // Resultierende Matrix
                               Fovy,       // Öffnungswinkel des Sichtkegels
                               640/480,    // Seitenverhältnis der Ansicht
                               1.0,        // Mindeste Nähe
                               1000.0);    // Maximal sichtbare Entfernung
    SetTransform(D3DTS_PROJECTION,ProjMatrix );
    end;
  Animate:=true;
  end;

procedure TRendererD3D.D3DKillScene;
begin
  CubeVB:=nil;
  end;

// Rendern der Szene
procedure TRendererD3D.D3DRender;
var
  WorldMatrix,
  ViewMatrix,
  ProjMatrix,
  TempMatrix    : TD3DXMATRIX;
  WorldPoint    : TD3DXVector3;
  ScrnPoint     : TD3DXVector4;
  TXTRect       : TRect;
begin
  RotY:=RotY+Delta;   // Rotation des Beobachters

  if assigned(lpd3ddevice) then with lpd3ddevice do begin
    Clear(0,          // Wieviel Rechtecke löschen? 0 Löscht alle
          nil,        // Pointer zu den Rechtecken. nil = Ganzer Bildschirm
          D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER,
          D3DCOLOR_XRGB(0,0,0), //Hintergrundfarbe schwarz
          1,          // Lösche ZBuffer ( Wir haben momentan noch keinen )
          0 );

    if SUCCEEDED(BeginScene) then begin

      D3DXMatrixLookAtLH (ViewMatrix,D3DXVECTOR3(ViewDist*sin(Pi180*RotY),
                                                 ViewHeight,
                                                 ViewDist*cos(Pi180*RotY)),
                                     D3DXVECTOR3(0.0,0.0,0.0),
                                     D3DXVECTOR3(0.0,1.0,0.0));

      SetTransform(D3DTS_VIEW,ViewMatrix);

      // Vertex Typ einstellen
      SetVertexShader(D3D8T_CUSTOMVERTEX);

      // Stream auf Vertexbuffer für Würfel setzen
      SetStreamSource(0,CubeVB,sizeof(TMyVertex));

      SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);
      // Setze die Welt-Matrix für die Kiste etwas nach oben
      D3DXMatrixTranslation(WorldMatrix,0,0,0);
      SetTransform(D3DTS_WORLD,WorldMatrix);

      // Zeichnen der Kiste
      DrawPrimitive(D3DPT_TRIANGLELIST,0,12);
      EndScene;
    end;

// Hier wird die Beschriftung gezeichnet =======================================

    GetTransform(D3DTS_WORLD, WorldMatrix);
    GetTransform(D3DTS_PROJECTION, ProjMatrix);
    GetTransform(D3DTS_VIEW,      ViewMatrix);

    D3DXMatrixMultiply(TempMatrix, WorldMatrix, ViewMatrix);
    D3DXMatrixMultiply(TempMatrix, TempMatrix, ProjMatrix);

    WorldPoint.x := NormCube[1].x;
    WorldPoint.y := NormCube[1].y;
    WorldPoint.z := NormCube[1].z;
    D3DXVec3Transform(ScrnPoint, WorldPoint, TempMatrix);

    TxtRect := Rect(Round(ScrnPoint.x), Round(ScrnPoint.y), 0, 0);
    Self.FDXFont.DrawTextA('Hallo', -1, TxtRect, DT_CALCRECT, 0);
    Self.FDXFont.DrawTextA('Hallo', -1, TxtRect, DT_LEFT, $FFFFFFFF);

    SetRenderState(D3DRS_ALPHATESTENABLE, 1);
    SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

// =============================================================================

    // Zeige Resultate auf dem Bildschirm
    Present(nil,nil,0,nil);
  end;
end;

procedure TRendererD3D.StartScene(Handle: HWND);
begin
  D3DInit(Handle);
  D3DInitScene;
  D3DRender;
end;

procedure TRendererD3D.StopScene;
begin
  D3DKillScene;
  D3DShutdown;
end;

procedure TRendererD3D.MyIdleHandler (Sender: TObject; var Done: Boolean);
begin
  if Animate then D3DRender;
  Done:=false;
end;

end.
Leider funktioniert das ganze nicht so, wie ich mir das vorstelle. Der Schirftzug "Hallo" irgendowo im oberen linken Fenster-Bereich gezeichnet ...und nicht etwa auf der Würfelkante.

Dabei müsste ich doch die Bildschirmkoordinaten rausbekommen, wenn ich

Wordlmatrix * Viewmatrix * Projektionmatrix

rechne, oder?

Gruß,
oXmoX
  Mit Zitat antworten Zitat