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.