offline
- Srki_82
- Moderator foruma
- Srđan Tot
- Am I evil? I am man, yes I am.
- Pridružio: 12 Jul 2005
- Poruke: 2483
- Gde živiš: Ljubljana
|
Napisano: 23 Avg 2010 22:35
Od tipova objekata cemo podrzati tacke, linije i trouglove jer od njih moze da se napravi skoro sve, a podrzavaju ih i DirectX i OpenGL. Prvo cemo dodati tip podatka koji definise te tipove objekata:
type
TSGE_PrimitiveType = (
ESGE_PT_POINTLIST,
ESGE_PT_LINELIST,
ESGE_PT_LINESTRIP,
ESGE_PT_TRIANGLELIST,
ESGE_PT_TRIANGLESTRIP,
ESGE_PT_TRIANGLEFAN
);
Point list znaci da ce drajver buffer citati kao niz tacaka i crtace onoliko tacaka koliko je vertex-a u buffer-u. Line list znaci da ce drajver crtati linije, a svaka linija je definisana uz pomoc dva vertex-a, sto znaci da za3 linije moramo da imamo 6 vertex-a u buffer-u. Line strip takodje crta linije, ali s tom razlikom da osim prve svaka sledeca linija pocinje tamo gde je prethodna zavrsena i zbog toga za 6 linija nam treba samo 7 vertex-a (2 vertex-a za prvu i po jedan za svaku sledecu liniju). Triangle list znaci da ce drajver crtati trouglove, a svaki trougao je definisan uz pomoc 3 vertex-a i za 6 trouglova nam treba 18 vertex-a. Triangle strip crta trouglove, ali se svaki sledeci trougao nastavlja na 2 vertex-a prethodnog, sto znaci da nam za prvi trougao trebaju 3 vertex-a, a za svaki sledeci samo po 1. Triangle fan je slican kao i triangle strip s razlikom da je prvi vertex centar oko kojeg se kreiraju trouglovi.
Ok... sada moramo dodati taj parametar u RenderOperation klasu preko jedne privatne promenljive i funkcije za citanje i postavljanje:
type
TRenderOperation = class(TSGERenderOperation)
private
FPrimitiveType: TSGE_PrimitiveType;
protected
function GetPrimitiveType: TSGE_PrimitiveType; override;
procedure SetPrimitiveType(APrimitiveType: TSGE_PrimitiveType); override;
public
property PrimitiveType: TSGE_PrimitiveType read GetPrimitiveType write SetPrimitiveType;
end;
function TRenderOperation.GetPrimitiveType: TSGE_PrimitiveType;
begin
Result := FPrimitiveType;
end;
procedure TRenderOperation.SetPrimitiveType(APrimitiveType: TSGE_PrimitiveType
);
begin
FPrimitiveType := APrimitiveType;
end;
Zatim moramo u support klasama dodati funkcije koje ce nas tip prevesti u tip za DirectX i OpenGL. Prvo cemo obraditi OpenGL jer je laksi:
function TOGLSupport.GetPrimitiveType(APrimitiveType: TSGE_PrimitiveType
): Cardinal;
begin
case APrimitiveType of
ESGE_PT_POINTLIST:
Result := GL_POINTS;
ESGE_PT_LINELIST:
Result := GL_LINES;
ESGE_PT_LINESTRIP:
Result := GL_LINE_STRIP;
ESGE_PT_TRIANGLELIST:
Result := GL_TRIANGLES;
ESGE_PT_TRIANGLESTRIP:
Result := GL_TRIANGLE_STRIP;
ESGE_PT_TRIANGLEFAN:
Result := GL_TRIANGLE_FAN;
else
Result := GL_TRIANGLES;
end;
end;
Funkcija direktno mapira nas tip na OpenGL tip i vraca rezultat. Render funkcija mora uzeti u obzir tip objekta i zato sada izgleda ovako:
procedure TOGLRenderer.Render(ARenderOperation: TSGERenderOperation);
var
I: Integer;
VertexDeclaration: TSGEVertexDeclaration;
VertexBufferBinding: TSGEVertexBufferBinding;
VertexBuffer: TOGLVertexBuffer;
Data: PByte;
begin
inherited Render(ARenderOperation);
VertexDeclaration := ARenderOperation.VertexDeclaration;
VertexBufferBinding := ARenderOperation.VertexBufferBinding;
for I := 0 to VertexDeclaration.Count - 1 do
begin
VertexBuffer := TOGLVertexBuffer(VertexBufferBinding.VertexBuffers[VertexDeclaration.Elements[I].ElementSource]);
if VertexBuffer <> nil then
begin
if GetSupport.HardwareBuffers then
begin
glBindBufferARB(GL_ARRAY_BUFFER_ARB, VertexBuffer.HWVertexBuffer);
Data := PByte(VertexDeclaration.Elements[I].ElementOffset);
end
else
Data := VertexBuffer.SWVertexBuffer + VertexDeclaration.Elements[I].ElementOffset;
if ARenderOperation.VertexOffset <> 0 then
Data := Data + (VertexBuffer.VertexSize * ARenderOperation.VertexOffset);
case VertexDeclaration.Elements[I].ElementUsage of
ESGE_VEU_POSITION:
begin
glVertexPointer(GetSupport.GetElementTypeSize(VertexDeclaration.Elements[I].ElementType),
GetSupport.GetElementType(VertexDeclaration.Elements[I].ElementType),
VertexBuffer.VertexSize, Data);
glEnableClientState(GL_VERTEX_ARRAY);
end;
ESGE_VEU_NORMAL:
begin
glNormalPointer(GetSupport.GetElementType(VertexDeclaration.Elements[I].ElementType),
VertexBuffer.VertexSize, Data);
glEnableClientState(GL_NORMAL_ARRAY);
end;
ESGE_VEU_DIFFUSE:
begin
glColorPointer(GetSupport.GetElementTypeSize(VertexDeclaration.Elements[I].ElementType),
GetSupport.GetElementType(VertexDeclaration.Elements[I].ElementType),
VertexBuffer.VertexSize, Data);
glEnableClientState(GL_COLOR_ARRAY);
end;
ESGE_VEU_TEXTURE_COORDINATES:
begin
if GetSupport.Multitexture then
glClientActiveTextureARB(GL_TEXTURE0_ARB + VertexDeclaration.Elements[I].ElementIndex)
else
if VertexDeclaration.Elements[I].ElementIndex <> 0 then
Continue;
glTexCoordPointer(GetSupport.GetElementTypeSize(VertexDeclaration.Elements[I].ElementType),
GetSupport.GetElementType(VertexDeclaration.Elements[I].ElementType),
VertexBuffer.VertexSize, Data);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
end;
end;
end;
end;
glDrawArrays(GetSupport.GetPrimitiveType(ARenderOperation.PrimitiveType), 0, ARenderOperation.VertexCount);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
if GetSupport.Multitexture then
begin
for I := 0 to VertexDeclaration.Count - 1 do
if VertexDeclaration.Elements[I].ElementUsage = ESGE_VEU_TEXTURE_COORDINATES then
begin
glClientActiveTextureARB(GL_TEXTURE0_ARB + VertexDeclaration.Elements[I].ElementIndex);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
end;
end
else
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
end;
Kao sto vidite, jedina razlika u odnosu na prethodnu verziju je da glDrawArrays funkcija kao prvi parametar uzima tip objekta koji zelimo, sve ostalo je ostalo isto.
DirectX za razliku od OpenGL-a u funkciji za crtanje ne trazi broj vertex-a koje zelimo da iskoristimo za crtanje, nego broj objekata koje zelimo da nacrtamo pa cemo zato morati da napisemo funkciju koja ce vratiti tip objekta i funkciju koja ce na osnovu tipa objekta i broja vertex-a vratiti broj objekata za crtanje (na pocetku odgovora sam objasnio u kakvom odnosu su broj vertex-a i broj objekata za koji tip):
function TDX9Support.GetPrimitiveType(APrimitiveType: TSGE_PrimitiveType
): TD3DPrimitiveType;
begin
case APrimitiveType of
ESGE_PT_POINTLIST:
Result := D3DPT_POINTLIST;
ESGE_PT_LINELIST:
Result := D3DPT_LINELIST;
ESGE_PT_LINESTRIP:
Result := D3DPT_LINESTRIP;
ESGE_PT_TRIANGLELIST:
Result := D3DPT_TRIANGLELIST;
ESGE_PT_TRIANGLESTRIP:
Result := D3DPT_TRIANGLESTRIP;
ESGE_PT_TRIANGLEFAN:
Result := D3DPT_TRIANGLEFAN;
else
Result := D3DPT_TRIANGLELIST;
end;
end;
function TDX9Support.GetPrimitiveCount(APrimitiveType: TSGE_PrimitiveType;
AVertexCount: Integer): Integer;
begin
case APrimitiveType of
ESGE_PT_POINTLIST:
Result := AVertexCount;
ESGE_PT_LINELIST:
Result := AVertexCount div 2;
ESGE_PT_LINESTRIP:
Result := AVertexCount - 1;
ESGE_PT_TRIANGLELIST:
Result := AVertexCount div 3;
ESGE_PT_TRIANGLESTRIP:
Result := AVertexCount - 2;
ESGE_PT_TRIANGLEFAN:
Result := AVertexCount - 2;
else
Result := AVertexCount div 3;
end;
end;
Sad kad imamo te dve pomocne funkcije, mozemo popraviti kod za crtanje:
procedure TDX9Renderer.Render(ARenderOperation: TSGERenderOperation);
var
Device: IDirect3DDevice9;
VertexBufferBinding: TSGEVertexBufferBinding;
I, LastVertexBufferIndex: Integer;
begin
inherited Render(ARenderOperation);
Device := GetDevice;
Device.SetVertexDeclaration(TDX9VertexDeclaration(ARenderOperation.VertexDeclaration).D3DVertexDeclaration);
LastVertexBufferIndex := -1;
VertexBufferBinding := ARenderOperation.VertexBufferBinding;
for I := 0 to VertexBufferBinding.Count - 1 do
if VertexBufferBinding.VertexBuffers[I] <> nil then
begin
Device.SetStreamSource(I, TDX9VertexBuffer(VertexBufferBinding.VertexBuffers[I]).D3DVertexBuffer,
0, VertexBufferBinding.VertexBuffers[I].VertexSize);
LastVertexBufferIndex := I;
end
else
Device.SetStreamSource(I, nil, 0, 0);
for I := VertexBufferBinding.Count to FLastVertexBufferIndex do
Device.SetStreamSource(I, nil, 0, 0);
FLastVertexBufferIndex := LastVertexBufferIndex;
Device.DrawPrimitive(GetSupport.GetPrimitiveType(ARenderOperation.PrimitiveType),
ARenderOperation.VertexOffset,
GetSupport.GetPrimitiveCount(ARenderOperation.PrimitiveType, ARenderOperation.VertexCount));
end;
Kao i u OpenGL verziji, razlika je samo u zvanju funkcije za crtanje koja sada uzima tip i broj objekata za crtanje na osnovu PrimitiveType parametra iz RenderOperation-a.
Sada u glavnom programu mozete malo da eksperimentisete raznim nacinima crtanja... potrebno je samo postaviti tip objekta:
program HelloWorld;
{$I SGE.inc}
{$IFDEF SGE_Delphi}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
{$IFDEF SGE_Windows}
Windows,
{$ENDIF}
SysUtils,
SGETypes in '..\..\Common\SGETypes.pas',
HTMLLogger in 'HTMLLogger.pas',
{$IFDEF SGE_Windows}
DX9Renderer in '..\..\Source\Renderers\DX9Renderer\DX9Renderer.pas',
{$ENDIF}
OGLRenderer in '..\..\Source\Renderers\OGLRenderer\OGLRenderer.pas';
type
TMyVertex = packed record
X, Y, Z: Single;
R, G, B, A: Single;
end;
const
Vertices: array[0..2] of TMyVertex = (
(X: 0; Y: 1; Z: 0; R: 1; G: 0; B: 0; A: 1),
(X: 1; Y: -1; Z: 0; R: 0; G: 1; B: 0; A: 1),
(X: -1; Y: -1; Z: 0; R: 0; G: 0; B: 1; A: 1));
var
Engine: TSGEEngine = nil;
HTML: THTMLLogger = nil;
{$IFDEF SGE_Windows}
DX9Rendr: TDX9Renderer = nil;
{$ENDIF}
OGLRendr: TOGLRenderer = nil;
RendrIndex: String;
I: Integer;
VertexBuffer: TSGEVertexBuffer;
VertexBufferData: Pointer;
VertexDeclaration: TSGEVertexDeclaration;
VertexBufferBinding: TSGEVertexBufferBinding;
RenderOperation: TSGERenderOperation;
begin
try
try
HTML := THTMLLogger.Create('SGE.html');
Engine := CreateSGEEngine('', HTML.Log);
Engine.Logger.LogLevel := ESGE_LL_INFO;
{$IFDEF SGE_Windows}
DX9Rendr := TDX9Renderer.Create(Engine);
Engine.RegisterRenderer(DX9Rendr);
{$ENDIF}
OGLRendr := TOGLRenderer.Create(Engine);
Engine.RegisterRenderer(OGLRendr);
WriteLn('Registered renderers:');
for I := 0 to Engine.RendererCount - 1 do
WriteLn(Format(' %d. %s', [I + 1, Engine.Renderers[I].Name]));
WriteLn;
Write('Select renderer: ');
ReadLn(RendrIndex);
I := StrToIntDef(RendrIndex, 0) - 1;
if (I < 0) or (I >= Engine.RendererCount) then
Exit;
Engine.Initialize(Engine.Renderers[I]);
Engine.Renderer.CreateWindow('SGE using ' + Engine.Renderers[I].Name);
Engine.Renderer.Matrix[ESGE_MT_PROJECTION] := SGEMatrixPerspective(45,
Engine.Renderer.RenderWindow.Width / Engine.Renderer.RenderWindow.Height, 1, 1000);
Engine.Renderer.Matrix[ESGE_MT_VIEW] := SGEMatrixLookAt(
SGEVector3(0, 3, 5), SGEVector3(0, 0, 0), SGEVector3(0, 1, 0));
VertexBuffer := Engine.Renderer.CreateVertexBuffer(7 * SizeOf(Single), 3, ESGE_BU_STATIC);
VertexBufferData := VertexBuffer.Lock(0, 0, ESGE_LT_NORMAL);
Move(Vertices, VertexBufferData^, VertexBuffer.BufferSize);
VertexBuffer.Unlock;
VertexDeclaration := Engine.Renderer.CreateVertexDeclaration;
VertexDeclaration.AddElement(0, 0, ESGE_VET_FLOAT3, ESGE_VEU_POSITION);
VertexDeclaration.AddElement(0, 3 * SizeOf(Single), ESGE_VET_COLOUR, ESGE_VEU_DIFFUSE);
VertexBufferBinding := Engine.Renderer.CreateVertexBufferBinding;
VertexBufferBinding.AddVertexBuffer(VertexBuffer);
RenderOperation := Engine.Renderer.CreateRenderOperation;
RenderOperation.VertexDeclaration := VertexDeclaration;
RenderOperation.VertexBufferBinding := VertexBufferBinding;
RenderOperation.VertexCount := 3;
RenderOperation.PrimitiveType := ESGE_PT_TRIANGLELIST;
while Engine.ProcessMessages do
begin
Engine.Renderer.BeginFrame;
Engine.Renderer.Clear([ESGE_FBT_COLOR], SGEColor(0.4, 0.4, 1, 0));
Engine.Renderer.Matrix[ESGE_MT_WORLD] := SGEMatrixRotate(Engine.Timer.Time / 20, 0, 1, 0);
Engine.Renderer.Render(RenderOperation);
Engine.Renderer.EndFrame;
Engine.Renderer.SwapBuffers;
end;
RenderOperation.Drop;
VertexBufferBinding.Drop;
VertexDeclaration.Drop;
VertexBuffer.Drop;
Engine.Finalize;
except
on E: Exception do
begin
{$IFDEF SGE_Windows}
MessageBox(0, PChar(E.Message), PChar(ExtractFileName(ParamStr(0))), 0);
{$ELSE}
WriteLn(ExtractFileName(ParamStr(0)) + ': ' + E.Message);
{$ENDIF}
end;
end;
finally
{$IFDEF SGE_Windows}
DX9Rendr.Free;
{$ENDIF}
OGLRendr.Free;
Engine.Free;
HTML.Free;
end;
end.
Imajte na umu da za sada point list crta tacke velicine 1 pixel-a i dosta ih je tesko videti... taj problem cemo resiti kad budemo napravili materijale koji ce sluziti za postavljanje raznih opcija za crtanje, od toga da li svetlo utice na crtanje, preko tekstura, do magle i drugih efekata.
U ovoj arhivi su popravljene greske iz prethodne i program ce raditi kako treba: https://www.mycity.rs/must-login.png
za sledeci tutorijal bi mogli da uzmemo teksture
Dopuna: 24 Avg 2010 20:10
Engine ce morati malo da poceka jer sam dobio neki projekat da uradim... ali, cim vreme dozvoli nastavljamo dalje
Dopuna: 29 Sep 2010 23:18
S vremena na vreme radim malo na engine-u... malo sam promenio arhitekturu da vise lici na Pascal jezik. Dodao sam teksturice, mogucnost za kreiranje fullscreen prozora, mogucnost promene rezolucije u toku izvrsavanja programa, poceo sam sa pravljenjem objekata koji ce se koristiti za crtanje (nema smisla rucno puniti buffer-e i opisne klase), tu je jos i jednostavna klasa za ulazne podatke sa tastature i misa (dzojstici ce posle doci na red), i jos svasta nesto... sve u svemu, cim budem imao malo vise vremena, pocecu ponovo da pisem i kako se sve to pravi. Do tada evo jednog malog primera koji koristi neke nove stvari (teksture, promenu rezolucije, "manual object" i materijale za crtanje, FSAA, ulaz sa tastature... i mislim da je to to):
program PGETest;
{$I PGE.inc}
uses
SysUtils,
{$IFDEF PGE_Windows}
Windows,
{$ENDIF}
PGETypes;
type
TPGETest = class
private
FResized: Boolean;
FManualObject: TPGEManualObject;
public
constructor Create(ADevice: TPGEDevice);
destructor Destroy; override;
procedure OnRender(ADevice: TPGEDevice; ATime, ADeltaTime: Cardinal);
procedure OnUpdate(ADevice: TPGEDevice; ATime, ADeltaTime: Cardinal);
procedure OnKeyPress(ADevice: TPGEDevice; AKeyCode: TPGE_KeyCodes;
AChar: Char);
end;
{ TPGETest }
constructor TPGETest.Create(ADevice: TPGEDevice);
begin
FResized := False;
ADevice.Renderer.Matrices[EPGE_MT_PROJECTION] := PGEMatrixPerspective
(45, ADevice.Renderer.RenderWindow.Width /
ADevice.Renderer.RenderWindow.Height, 1, 1000);
ADevice.Renderer.Matrices[EPGE_MT_VIEW] := PGEMatrixLookAt
(PGEVector3(0, 3, 5), PGEVector3(0, 0, 0), PGEVector3(0, 1, 0));
ADevice.Renderer.ClearColor := PGEColor(0.4, 0.4, 1, 0);
FManualObject := ADevice.SceneManager.CreateManualObject;
FManualObject.BeginObject(EPGE_PT_TRIANGLESTRIP);
FManualObject.TextureCoord(1, 1);
FManualObject.Vertex(1, -1, 0);
FManualObject.TextureCoord(1, 0);
FManualObject.Vertex(1, 1, 0);
FManualObject.TextureCoord(0, 1);
FManualObject.Vertex(-1, -1, 0);
FManualObject.TextureCoord(0, 0);
FManualObject.Vertex(-1, 1, 0);
FManualObject.EndObject;
FManualObject.Material := ADevice.Renderer.CreateMaterial;
FManualObject.Material.Textures[0] := ADevice.SceneManager.GetTexture
('rocker.png');
FManualObject.Material.CullFace := EPGE_CF_NONE;
FManualObject.Material.LightingEnabled := False;
end;
destructor TPGETest.Destroy;
begin
FManualObject.Material.Free;
FManualObject.Free;
inherited;
end;
procedure TPGETest.OnKeyPress(ADevice: TPGEDevice; AKeyCode: TPGE_KeyCodes;
AChar: Char);
begin
if AKeyCode = EPGE_KC_ESCAPE then
ADevice.StopRendering;
end;
procedure TPGETest.OnRender(ADevice: TPGEDevice; ATime, ADeltaTime: Cardinal);
begin
if ADevice.Renderer.BeginScene then
begin
ADevice.Renderer.Clear;
ADevice.Renderer.Matrices[EPGE_MT_WORLD] := PGEMatrixRotate
(ATime / 20, 0, 1, 0);
FManualObject.Render;
ADevice.Renderer.EndScene;
end;
end;
procedure TPGETest.OnUpdate(ADevice: TPGEDevice; ATime, ADeltaTime: Cardinal);
begin
if ATime > 10000 then
if not FResized then
begin
FResized := True;
ADevice.Renderer.RenderWindow.SetWindowMode(VideoMode(1024, 768, 32),
ZBufferFormat(24, 8), WindowSettings(False, True, 2));
end;
end;
var
Device: TPGEDevice = nil;
RendererClass: TPGERendererClass;
RenderWindow: TPGERenderWindow = nil;
Test: TPGETest = nil;
begin
try
Device := CreateDevice;
if Device <> nil then
begin
RendererClass := Device.RegisteredRenderers[0];
Device.Initialize(RendererClass, 'PGE using ' + RendererClass.GetName,
VideoMode(800, 600, 32), ZBufferFormat(0, 0), WindowSettings
(False, False, 2));
Test := TPGETest.Create(Device);
Device.OnRender := Test.OnRender;
Device.OnUpdate := Test.OnUpdate;
Device.OnKeyPress := Test.OnKeyPress;
Device.StartRendering;
Test.Free;
Device.Free;
end;
except
on E: Exception do
begin
{$IFDEF PGE_Windows}
MessageBox(0, PChar(E.Message), PChar(ExtractFileName(ParamStr(0))), 0);
{$ELSE}
WriteLn(ExtractFileName(ParamStr(0)) + ': ' + E.Message);
{$ENDIF}
end;
end;
end.
U zip-u su samo exe fajl i tekstura. Program je napravljen tako da ucita teksturicu, vrti je na ekranu, posle 10 sekundi promeni rezoluciju i onda nastavlja da vrti teksturu. Izlazi se pritiskom na taster Esc.
https://www.mycity.rs/must-login.png
Dopuna: 02 Okt 2010 17:04
Zaboravih da kazem... preimenovao sam engine iz SGE (Simple Graphics Engine) u PGE (Pascal Graphics Engine) jer polako, ali sigurno prestaje da bude "simple"
Ovde je najnovija verzija engine-a u kojoj je skoro sve od funkcija koje sam zamislio napravljeno za DirectX renderer i Windows platformu. Posto je fajl malo veci, morao sam da ga stavim na eksterni sajt: http://www.zshare.net/download/810305139a30f296/
|