From f6ca47eeb2c217505d9c1babe79d46b6668d3881 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Sat, 31 Jan 2015 16:47:40 +0100 Subject: [PATCH] * updated glBitmap * implemented vertex array objects * added examples --- examples/simple/project1.lpi | 82 ++++++++++++++ examples/simple/project1.lpr | 21 ++++ examples/simple/shader.glsl | 19 ++++ examples/simple/uMainForm.lfm | 35 ++++++ examples/simple/uMainForm.pas | 122 ++++++++++++++++++++ examples/texture/data/texture.png | Bin 0 -> 15241 bytes examples/texture/project1.lpi | 82 ++++++++++++++ examples/texture/project1.lpr | 21 ++++ examples/texture/shader.glsl | 23 ++++ examples/texture/uMainForm.lfm | 35 ++++++ examples/texture/uMainForm.pas | 156 +++++++++++++++++++++++++ examples/vertexarrayobject/data/texture.png | Bin 0 -> 15241 bytes examples/vertexarrayobject/project1.lpi | 91 +++++++++++++++ examples/vertexarrayobject/project1.lpr | 21 ++++ examples/vertexarrayobject/shader.glsl | 23 ++++ examples/vertexarrayobject/uMainForm.lfm | 35 ++++++ examples/vertexarrayobject/uMainForm.pas | 170 ++++++++++++++++++++++++++++ glBitmapConf.inc | 61 ++++++++++ uglcBitmap.pas | 7 +- uglcVertexArrayObject.pas | 84 ++++++++++++++ 20 files changed, 1085 insertions(+), 3 deletions(-) create mode 100644 examples/simple/project1.lpi create mode 100644 examples/simple/project1.lpr create mode 100644 examples/simple/shader.glsl create mode 100644 examples/simple/uMainForm.lfm create mode 100644 examples/simple/uMainForm.pas create mode 100644 examples/texture/data/texture.png create mode 100644 examples/texture/project1.lpi create mode 100644 examples/texture/project1.lpr create mode 100644 examples/texture/shader.glsl create mode 100644 examples/texture/uMainForm.lfm create mode 100644 examples/texture/uMainForm.pas create mode 100644 examples/vertexarrayobject/data/texture.png create mode 100644 examples/vertexarrayobject/project1.lpi create mode 100644 examples/vertexarrayobject/project1.lpr create mode 100644 examples/vertexarrayobject/shader.glsl create mode 100644 examples/vertexarrayobject/uMainForm.lfm create mode 100644 examples/vertexarrayobject/uMainForm.pas create mode 100644 glBitmapConf.inc create mode 100644 uglcVertexArrayObject.pas diff --git a/examples/simple/project1.lpi b/examples/simple/project1.lpi new file mode 100644 index 0000000..7e49921 --- /dev/null +++ b/examples/simple/project1.lpi @@ -0,0 +1,82 @@ + + + + + + + + + + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="1"> + <Item1> + <PackageName Value="LCL"/> + </Item1> + </RequiredPackages> + <Units Count="2"> + <Unit0> + <Filename Value="project1.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="uMainForm.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="MainForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="uMainForm"/> + </Unit1> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="project1"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir);..\.."/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/examples/simple/project1.lpr b/examples/simple/project1.lpr new file mode 100644 index 0000000..5ca8fd0 --- /dev/null +++ b/examples/simple/project1.lpr @@ -0,0 +1,21 @@ +program project1; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, uMainForm + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource := True; + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. + diff --git a/examples/simple/shader.glsl b/examples/simple/shader.glsl new file mode 100644 index 0000000..5993f08 --- /dev/null +++ b/examples/simple/shader.glsl @@ -0,0 +1,19 @@ +/* ShaderObject: GL_VERTEX_SHADER */ +#version 330 +uniform mat4 uModelViewProjMat; +layout(location = 0) in vec3 inPos; + +void main(void) +{ + gl_Position = vec4(inPos, 1.0); +} + +/* ShaderObject: GL_FRAGMENT_SHADER */ +#version 330 + +out vec4 outColor; // ausgegebene Farbe + +void main(void) +{ + outColor = vec4(1.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/examples/simple/uMainForm.lfm b/examples/simple/uMainForm.lfm new file mode 100644 index 0000000..712be2c --- /dev/null +++ b/examples/simple/uMainForm.lfm @@ -0,0 +1,35 @@ +object MainForm: TMainForm + Left = 465 + Height = 451 + Top = 217 + Width = 411 + Caption = 'MainForm' + ClientHeight = 451 + ClientWidth = 411 + OnCreate = FormCreate + OnDestroy = FormDestroy + LCLVersion = '1.3' + object RenderPanel: TPanel + Left = 0 + Height = 371 + Top = 0 + Width = 411 + Align = alClient + TabOrder = 0 + OnResize = RenderPanelResize + end + object LogLB: TListBox + Left = 0 + Height = 80 + Top = 371 + Width = 411 + Align = alBottom + ItemHeight = 0 + TabOrder = 1 + end + object ApplicationProperties: TApplicationProperties + OnIdle = ApplicationPropertiesIdle + left = 64 + top = 24 + end +end diff --git a/examples/simple/uMainForm.pas b/examples/simple/uMainForm.pas new file mode 100644 index 0000000..1179a67 --- /dev/null +++ b/examples/simple/uMainForm.pas @@ -0,0 +1,122 @@ +unit uMainForm; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, + uglcContext, uglcShader, uglcArrayBuffer, uglcTypes; + +type + TMainForm = class(TForm) + ApplicationProperties: TApplicationProperties; + LogLB: TListBox; + RenderPanel: TPanel; + procedure ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure RenderPanelResize(Sender: TObject); + private + fContext: TglcContext; + fShader: TglcShaderProgram; + fVBO: TglcArrayBuffer; + procedure Log(aSender: TObject; const aMsg: String); + procedure Render; + public + { public declarations } + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.lfm} + +uses + dglOpenGL, ugluVector; + +const + SHADER_FILE = 'shader.glsl'; + + LAYOUT_LOCATION_POS = 0; + +procedure TMainForm.FormCreate(Sender: TObject); +type + TVertex = packed record + pos: TgluVector3f; + end; + PVertex = ^TVertex; +var + pf: TglcContextPixelFormatSettings; + p: PVertex; +begin + pf := TglcContext.MakePF(); + fContext := TglcContext.GetPlatformClass.Create(RenderPanel, pf); + fContext.BuildContext; + + fShader := TglcShaderProgram.Create(@Log); + fShader.LoadFromFile(ExtractFilePath(Application.ExeName) + SHADER_FILE); + fShader.Compile; + + fVBO := TglcArrayBuffer.Create(TglcBufferTarget.btArrayBuffer); + fVBO.BufferData(4, sizeof(TVertex), TglcBufferUsage.buStaticDraw, nil); + p := fVBO.MapBuffer(TglcBufferAccess.baWriteOnly); + try + p^.pos := gluVector3f(-0.5, -0.5, 0); inc(p); + p^.pos := gluVector3f( 0.5, -0.5, 0); inc(p); + p^.pos := gluVector3f( 0.5, 0.5, 0); inc(p); + p^.pos := gluVector3f(-0.5, 0.5, 0); inc(p); + finally + fVBO.UnmapBuffer; + end; +end; + +procedure TMainForm.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); +begin + Render; + Done := false; +end; + +procedure TMainForm.FormDestroy(Sender: TObject); +begin + FreeAndNil(fVBO); + FreeAndNil(fShader); + FreeAndNil(fContext); +end; + +procedure TMainForm.RenderPanelResize(Sender: TObject); +begin + if Assigned(fContext) then begin + glViewport(0, 0, RenderPanel.ClientWidth, RenderPanel.ClientHeight); + end; +end; + +procedure TMainForm.Log(aSender: TObject; const aMsg: String); +begin + LogLB.Items.Add(aMsg); +end; + +procedure TMainForm.Render; +begin + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + + fVBO.Bind; + fShader.Enable; + + glEnableVertexAttribArray(LAYOUT_LOCATION_POS); + glVertexAttribPointer(LAYOUT_LOCATION_POS, 3, GL_FLOAT, False, 0, nil); + + glDrawArrays(GL_QUADS, 0, fVBO.DataCount); + + glDisableVertexAttribArray(LAYOUT_LOCATION_POS); + + fShader.Disable; + fVBO.Unbind; + + fContext.SwapBuffers; +end; + +end. + diff --git a/examples/texture/data/texture.png b/examples/texture/data/texture.png new file mode 100644 index 0000000000000000000000000000000000000000..edd43daf9fcfc3b195b64fd613cc23db5618af2b GIT binary patch literal 15241 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 Z@}TrJ!}&3-hY;|Orkd{MoJ%$#{}0Glr;h*t literal 0 HcmV?d00001 diff --git a/examples/texture/project1.lpi b/examples/texture/project1.lpi new file mode 100644 index 0000000..7e49921 --- /dev/null +++ b/examples/texture/project1.lpi @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="project1"/> + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="1"> + <Item1> + <PackageName Value="LCL"/> + </Item1> + </RequiredPackages> + <Units Count="2"> + <Unit0> + <Filename Value="project1.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="uMainForm.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="MainForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="uMainForm"/> + </Unit1> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="project1"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir);..\.."/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/examples/texture/project1.lpr b/examples/texture/project1.lpr new file mode 100644 index 0000000..5ca8fd0 --- /dev/null +++ b/examples/texture/project1.lpr @@ -0,0 +1,21 @@ +program project1; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, uMainForm + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource := True; + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. + diff --git a/examples/texture/shader.glsl b/examples/texture/shader.glsl new file mode 100644 index 0000000..c70ab11 --- /dev/null +++ b/examples/texture/shader.glsl @@ -0,0 +1,23 @@ +/* ShaderObject: GL_VERTEX_SHADER */ +#version 330 +uniform mat4 uModelViewProjMat; +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec2 inTexCoord; +out vec2 vTexCoord; + +void main(void) +{ + gl_Position = vec4(inPos, 1.0); + vTexCoord = inTexCoord; +} + +/* ShaderObject: GL_FRAGMENT_SHADER */ +#version 330 +uniform sampler2D uTexture; +in vec2 vTexCoord; +out vec4 outColor; + +void main(void) +{ + outColor = texture(uTexture, vTexCoord); +} \ No newline at end of file diff --git a/examples/texture/uMainForm.lfm b/examples/texture/uMainForm.lfm new file mode 100644 index 0000000..712be2c --- /dev/null +++ b/examples/texture/uMainForm.lfm @@ -0,0 +1,35 @@ +object MainForm: TMainForm + Left = 465 + Height = 451 + Top = 217 + Width = 411 + Caption = 'MainForm' + ClientHeight = 451 + ClientWidth = 411 + OnCreate = FormCreate + OnDestroy = FormDestroy + LCLVersion = '1.3' + object RenderPanel: TPanel + Left = 0 + Height = 371 + Top = 0 + Width = 411 + Align = alClient + TabOrder = 0 + OnResize = RenderPanelResize + end + object LogLB: TListBox + Left = 0 + Height = 80 + Top = 371 + Width = 411 + Align = alBottom + ItemHeight = 0 + TabOrder = 1 + end + object ApplicationProperties: TApplicationProperties + OnIdle = ApplicationPropertiesIdle + left = 64 + top = 24 + end +end diff --git a/examples/texture/uMainForm.pas b/examples/texture/uMainForm.pas new file mode 100644 index 0000000..5b325a7 --- /dev/null +++ b/examples/texture/uMainForm.pas @@ -0,0 +1,156 @@ +unit uMainForm; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, + uglcContext, uglcShader, uglcArrayBuffer, uglcTypes, uglcBitmap; + +type + TMainForm = class(TForm) + ApplicationProperties: TApplicationProperties; + LogLB: TListBox; + RenderPanel: TPanel; + procedure ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure RenderPanelResize(Sender: TObject); + private + fContext: TglcContext; + fShader: TglcShaderProgram; + fVBO: TglcArrayBuffer; + fTexture: TglcBitmap2D; + procedure Log(aSender: TObject; const aMsg: String); + procedure Render; + public + { public declarations } + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.lfm} + +uses + dglOpenGL, ugluVector; + +const + SHADER_FILE = 'shader.glsl'; + TEXTURE_FILE = 'data\texture.png'; + + LAYOUT_LOCATION_POS = 0; + LAYOUT_LOCATION_TEX = 1; + + UNIFORM_NAME_TEXTURE = 'uTexture'; + +type + TVertex = packed record + pos: TgluVector3f; + tex: TgluVector2f; + end; + PVertex = ^TVertex; + +procedure TMainForm.FormCreate(Sender: TObject); +var + pf: TglcContextPixelFormatSettings; + p: PVertex; + texData: TglcBitmapData; +begin + pf := TglcContext.MakePF(); + fContext := TglcContext.GetPlatformClass.Create(RenderPanel, pf); + fContext.BuildContext; + + fShader := TglcShaderProgram.Create(@Log); + fShader.LoadFromFile(ExtractFilePath(Application.ExeName) + SHADER_FILE); + fShader.Compile; + fShader.Uniform1i(UNIFORM_NAME_TEXTURE, 0); + + fVBO := TglcArrayBuffer.Create(TglcBufferTarget.btArrayBuffer); + fVBO.BufferData(4, sizeof(TVertex), TglcBufferUsage.buStaticDraw, nil); + p := fVBO.MapBuffer(TglcBufferAccess.baWriteOnly); + try + p^.pos := gluVector3f(-0.5, -0.5, 0); + p^.tex := gluVector2f( 0.0, 1.0); + inc(p); + + p^.pos := gluVector3f( 0.5, -0.5, 0); + p^.tex := gluVector2f( 1.0, 1.0); + inc(p); + + p^.pos := gluVector3f( 0.5, 0.5, 0); + p^.tex := gluVector2f( 1.0, 0.0); + inc(p); + + p^.pos := gluVector3f(-0.5, 0.5, 0); + p^.tex := gluVector2f( 0.0, 0.0); + inc(p); + finally + fVBO.UnmapBuffer; + end; + + fTexture := TglcBitmap2D.Create; + texData := TglcBitmapData.Create; + try + texData.LoadFromFile(ExtractFilePath(Application.ExeName) + TEXTURE_FILE); + fTexture.UploadData(texData); + finally + FreeAndNil(texData); + end; +end; + +procedure TMainForm.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); +begin + Render; + Done := false; +end; + +procedure TMainForm.FormDestroy(Sender: TObject); +begin + FreeAndNil(fVBO); + FreeAndNil(fShader); + FreeAndNil(fContext); +end; + +procedure TMainForm.RenderPanelResize(Sender: TObject); +begin + if Assigned(fContext) then begin + glViewport(0, 0, RenderPanel.ClientWidth, RenderPanel.ClientHeight); + end; +end; + +procedure TMainForm.Log(aSender: TObject; const aMsg: String); +begin + LogLB.Items.Add(aMsg); +end; + +procedure TMainForm.Render; +begin + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + fVBO.Bind; + fTexture.Bind; + fShader.Enable; + + glEnableVertexAttribArray(LAYOUT_LOCATION_POS); + glVertexAttribPointer(LAYOUT_LOCATION_POS, 3, GL_FLOAT, False, SizeOf(TVertex), @PVertex(nil)^.pos); + + glEnableVertexAttribArray(LAYOUT_LOCATION_TEX); + glVertexAttribPointer(LAYOUT_LOCATION_TEX, 2, GL_FLOAT, False, SizeOf(TVertex), @PVertex(nil)^.tex); + + glDrawArrays(GL_QUADS, 0, fVBO.DataCount); + + glDisableVertexAttribArray(LAYOUT_LOCATION_POS); + glDisableVertexAttribArray(LAYOUT_LOCATION_TEX); + + fShader.Disable; + fTexture.Unbind; + fVBO.Unbind; + + fContext.SwapBuffers; +end; + +end. + diff --git a/examples/vertexarrayobject/data/texture.png b/examples/vertexarrayobject/data/texture.png new file mode 100644 index 0000000000000000000000000000000000000000..edd43daf9fcfc3b195b64fd613cc23db5618af2b GIT binary patch literal 15241 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 Z@}TrJ!}&3-hY;|Orkd{MoJ%$#{}0Glr;h*t literal 0 HcmV?d00001 diff --git a/examples/vertexarrayobject/project1.lpi b/examples/vertexarrayobject/project1.lpi new file mode 100644 index 0000000..42d2560 --- /dev/null +++ b/examples/vertexarrayobject/project1.lpi @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="project1"/> + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="1"> + <Item1> + <PackageName Value="LCL"/> + </Item1> + </RequiredPackages> + <Units Count="3"> + <Unit0> + <Filename Value="project1.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="uMainForm.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="MainForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="uMainForm"/> + </Unit1> + <Unit2> + <Filename Value="..\..\uglcVertexArrayObject.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="uglcVertexArrayObject"/> + </Unit2> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="project1"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir);..\.."/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Debugging> + <UseHeaptrc Value="True"/> + <UseExternalDbgSyms Value="True"/> + </Debugging> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/examples/vertexarrayobject/project1.lpr b/examples/vertexarrayobject/project1.lpr new file mode 100644 index 0000000..66fcec0 --- /dev/null +++ b/examples/vertexarrayobject/project1.lpr @@ -0,0 +1,21 @@ +program project1; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, uMainForm, uglcVertexArrayObject + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource := True; + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. + diff --git a/examples/vertexarrayobject/shader.glsl b/examples/vertexarrayobject/shader.glsl new file mode 100644 index 0000000..c70ab11 --- /dev/null +++ b/examples/vertexarrayobject/shader.glsl @@ -0,0 +1,23 @@ +/* ShaderObject: GL_VERTEX_SHADER */ +#version 330 +uniform mat4 uModelViewProjMat; +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec2 inTexCoord; +out vec2 vTexCoord; + +void main(void) +{ + gl_Position = vec4(inPos, 1.0); + vTexCoord = inTexCoord; +} + +/* ShaderObject: GL_FRAGMENT_SHADER */ +#version 330 +uniform sampler2D uTexture; +in vec2 vTexCoord; +out vec4 outColor; + +void main(void) +{ + outColor = texture(uTexture, vTexCoord); +} \ No newline at end of file diff --git a/examples/vertexarrayobject/uMainForm.lfm b/examples/vertexarrayobject/uMainForm.lfm new file mode 100644 index 0000000..712be2c --- /dev/null +++ b/examples/vertexarrayobject/uMainForm.lfm @@ -0,0 +1,35 @@ +object MainForm: TMainForm + Left = 465 + Height = 451 + Top = 217 + Width = 411 + Caption = 'MainForm' + ClientHeight = 451 + ClientWidth = 411 + OnCreate = FormCreate + OnDestroy = FormDestroy + LCLVersion = '1.3' + object RenderPanel: TPanel + Left = 0 + Height = 371 + Top = 0 + Width = 411 + Align = alClient + TabOrder = 0 + OnResize = RenderPanelResize + end + object LogLB: TListBox + Left = 0 + Height = 80 + Top = 371 + Width = 411 + Align = alBottom + ItemHeight = 0 + TabOrder = 1 + end + object ApplicationProperties: TApplicationProperties + OnIdle = ApplicationPropertiesIdle + left = 64 + top = 24 + end +end diff --git a/examples/vertexarrayobject/uMainForm.pas b/examples/vertexarrayobject/uMainForm.pas new file mode 100644 index 0000000..02887d3 --- /dev/null +++ b/examples/vertexarrayobject/uMainForm.pas @@ -0,0 +1,170 @@ +unit uMainForm; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, + uglcContext, uglcShader, uglcArrayBuffer, uglcTypes, uglcBitmap, uglcVertexArrayObject; + +type + TMainForm = class(TForm) + ApplicationProperties: TApplicationProperties; + LogLB: TListBox; + RenderPanel: TPanel; + procedure ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure RenderPanelResize(Sender: TObject); + private + fContext: TglcContext; + fShader: TglcShaderProgram; + fVAO: TglcVertexArrayObject; + fTexture: TglcBitmap2D; + procedure Log(aSender: TObject; const aMsg: String); + procedure Render; + public + { public declarations } + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.lfm} + +uses + dglOpenGL, ugluVector; + +const + SHADER_FILE = 'shader.glsl'; + TEXTURE_FILE = 'data\texture.png'; + + LAYOUT_LOCATION_POS = 0; + LAYOUT_LOCATION_TEX = 1; + + UNIFORM_NAME_TEXTURE = 'uTexture'; + +type + TVertex = packed record + pos: TgluVector3f; + tex: TgluVector2f; + end; + PVertex = ^TVertex; + +procedure CheckGlError; +var + err: GLenum; +begin + err := glGetError(); + if (err <> 0) then begin + ShowMessage('ERROR: 0x' + IntToHex(err, 16)); + halt; + end; +end; + +procedure TMainForm.FormCreate(Sender: TObject); +var + pf: TglcContextPixelFormatSettings; + p: PVertex; + texData: TglcBitmapData; + vbo: TglcArrayBuffer; +begin + pf := TglcContext.MakePF(); + fContext := TglcContext.GetPlatformClass.Create(RenderPanel, pf); + fContext.BuildContext; + + Log(self, glGetString(GL_VERSION)); + + fShader := TglcShaderProgram.Create(@Log); + fShader.LoadFromFile(ExtractFilePath(Application.ExeName) + SHADER_FILE); + fShader.Compile; + fShader.Uniform1i(UNIFORM_NAME_TEXTURE, 0); + + vbo := TglcArrayBuffer.Create(TglcBufferTarget.btArrayBuffer); + vbo.BufferData(4, sizeof(TVertex), TglcBufferUsage.buStaticDraw, nil); + p := vbo.MapBuffer(TglcBufferAccess.baWriteOnly); + try + p^.pos := gluVector3f(-0.5, -0.5, 0); + p^.tex := gluVector2f( 0.0, 1.0); + inc(p); + + p^.pos := gluVector3f( 0.5, -0.5, 0); + p^.tex := gluVector2f( 1.0, 1.0); + inc(p); + + p^.pos := gluVector3f( 0.5, 0.5, 0); + p^.tex := gluVector2f( 1.0, 0.0); + inc(p); + + p^.pos := gluVector3f(-0.5, 0.5, 0); + p^.tex := gluVector2f( 0.0, 0.0); + inc(p); + finally + vbo.UnmapBuffer; + end; + + fVAO := TglcVertexArrayObject.Create; + fVAO.BindArrayBuffer(vbo, true); + fVAO.VertexAttribPointer(LAYOUT_LOCATION_POS, 3, GL_FLOAT, False, SizeOf(TVertex), GLint(@PVertex(nil)^.pos)); + fVAO.VertexAttribPointer(LAYOUT_LOCATION_TEX, 2, GL_FLOAT, False, SizeOf(TVertex), GLint(@PVertex(nil)^.tex)); + + fTexture := TglcBitmap2D.Create; + texData := TglcBitmapData.Create; + try + texData.LoadFromFile(ExtractFilePath(Application.ExeName) + TEXTURE_FILE); + fTexture.UploadData(texData); + finally + FreeAndNil(texData); + end; +end; + +procedure TMainForm.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); +begin + Render; + Done := false; +end; + +procedure TMainForm.FormDestroy(Sender: TObject); +begin + FreeAndNil(fTexture); + FreeAndNil(fVAO); + FreeAndNil(fShader); + FreeAndNil(fContext); +end; + +procedure TMainForm.RenderPanelResize(Sender: TObject); +begin + if Assigned(fContext) then begin + glViewport(0, 0, RenderPanel.ClientWidth, RenderPanel.ClientHeight); + end; +end; + +procedure TMainForm.Log(aSender: TObject; const aMsg: String); +begin + LogLB.Items.Add(aMsg); +end; + +procedure TMainForm.Render; +begin + CheckGlError; + + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + + fTexture.Bind; + fShader.Enable; + fVAO.Bind; + + glDrawArrays(GL_QUADS, 0, 4); + + fVAO.Unbind; + fShader.Disable; + fTexture.Unbind; + + fContext.SwapBuffers; +end; + +end. + diff --git a/glBitmapConf.inc b/glBitmapConf.inc new file mode 100644 index 0000000..a438dcd --- /dev/null +++ b/glBitmapConf.inc @@ -0,0 +1,61 @@ +{ this is the default configuration file of the glBitmap.pas + please uncomment you preferences below, rename this file to glBitmapConf.inc + and make it available to your compilers include paths } + + +// enable support for OpenGL ES 1.1 +{.$DEFINE OPENGL_ES_1_1} + +// enable support for OpenGL ES 2.0 +{.$DEFINE OPENGL_ES_2_0} + +// enable support for OpenGL ES 3.0 +{.$DEFINE OPENGL_ES_3_0} + +// enable support for all OpenGL ES extensions +{.$DEFINE OPENGL_ES_EXT} + + + +// activate to enable the support for SDL_surfaces +{.$DEFINE GLB_SDL} + +// activate to enable the support for Delphi (including support for Delphi's (not Lazarus') TBitmap) +{.$DEFINE GLB_DELPHI} + +// activate to enable the support for TLazIntfImage from Lazarus +{$DEFINE GLB_LAZARUS} + + + +// activate to enable the support of SDL_image to load files. (READ ONLY) +// If you enable SDL_image all other libraries will be ignored! +{.$DEFINE GLB_SDL_IMAGE} + + + +// activate to enable Lazarus TPortableNetworkGraphic support +// if you enable this pngImage and libPNG will be ignored +{$DEFINE GLB_LAZ_PNG} + +// activate to enable png support with the unit pngimage -> http://pngdelphi.sourceforge.net/ +// if you enable pngimage the libPNG will be ignored +{.$DEFINE GLB_PNGIMAGE} + +// activate to use the libPNG -> http://www.libpng.org/ +// You will need an aditional header -> http://www.opengl24.de/index.php?cat=header&file=libpng +{.$DEFINE GLB_LIB_PNG} + + + +// activate to enable Lazarus TJPEGImage support +// if you enable this delphi jpegs and libJPEG will be ignored +{$DEFINE GLB_LAZ_JPEG} + +// if you enable delphi jpegs the libJPEG will be ignored +{.$DEFINE GLB_DELPHI_JPEG} + +// activate to use the libJPEG -> http://www.ijg.org/ +// You will need an aditional header -> http://www.opengl24.de/index.php?cat=header&file=libjpeg +{.$DEFINE GLB_LIB_JPEG} + \ No newline at end of file diff --git a/uglcBitmap.pas b/uglcBitmap.pas index 0388f46..7d53733 100644 --- a/uglcBitmap.pas +++ b/uglcBitmap.pas @@ -22,7 +22,7 @@ - download texture data from video card - manipulate texture data (e.g. add alpha, remove alpha, convert to other format, switch RGB, ...) } -unit glBitmap; +unit uglcBitmap; {$I glBitmapConf.inc} @@ -1207,6 +1207,7 @@ type TglcBitmapFormat = TglBitmapFormat; TglcBitmap2D = TglBitmap2D; + TglcBitmapData = TglBitmapData; {$IF NOT DEFINED(OPENGL_ES)} TglcBitmap1D = TglBitmap1D; TglcBitmapCubeMap = TglBitmapCubeMap; @@ -8164,14 +8165,14 @@ begin aBuildWithGlu := false; if (MipMap = mmMipmap) then begin if (GL_VERSION_1_4 or GL_SGIS_generate_mipmap) then - glTexParameteri(Target, GL_GENERATE_MIPMAP, GL_TRUE) + glTexParameteri(Target, GL_GENERATE_MIPMAP, GLint(GL_TRUE)) else aBuildWithGlu := true; end else if (MipMap = mmMipmapGlu) then aBuildWithGlu := true; {$ELSE} if (MipMap = mmMipmap) then - glTexParameteri(Target, GL_GENERATE_MIPMAP, GL_TRUE); + glTexParameteri(Target, GL_GENERATE_MIPMAP, GLint(GL_TRUE)); {$ENDIF} end; diff --git a/uglcVertexArrayObject.pas b/uglcVertexArrayObject.pas new file mode 100644 index 0000000..89e7ac2 --- /dev/null +++ b/uglcVertexArrayObject.pas @@ -0,0 +1,84 @@ +unit uglcVertexArrayObject; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, contnrs, dglOpenGL, uglcArrayBuffer; + +type + EglcVertexArrayObject = class(Exception); + TglcVertexArrayObject = class(TObject) + private + fID: GLuint; + fArrayBuffers: TObjectList; + public + property ID: GLuint read fID; + + procedure BindArrayBuffer(const aBuffer: TglcArrayBuffer; const aOwnsObject: Boolean); + procedure VertexAttribPointer(const aIndex: GLuint; const aSize: GLint; const aType: GLenum; + const aNormalized: GLboolean; const aStride: GLsizei; const aOffset: GLint); + + procedure Bind; + procedure Unbind; + + constructor Create; + destructor Destroy; override; + end; + +implementation + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//TglcVertexArrayObject////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +procedure TglcVertexArrayObject.BindArrayBuffer(const aBuffer: TglcArrayBuffer; const aOwnsObject: Boolean); +begin + Bind; + aBuffer.Bind; + if aOwnsObject and (fArrayBuffers.IndexOf(aBuffer) < 0) then + fArrayBuffers.Add(aBuffer); +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +procedure TglcVertexArrayObject.VertexAttribPointer(const aIndex: GLuint; const aSize: GLint; const aType: GLenum; + const aNormalized: GLboolean; const aStride: GLsizei; const aOffset: GLint); +begin + Bind; + glEnableVertexAttribArray(aIndex); + glVertexAttribPointer(aIndex, aSize, aType, aNormalized, aStride, Pointer(aOffset)); +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +procedure TglcVertexArrayObject.Bind; +begin + glBindVertexArray(fID); +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +procedure TglcVertexArrayObject.Unbind; +begin + glBindVertexArray(0); +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +constructor TglcVertexArrayObject.Create; +begin + inherited Create; + if not GL_VERSION_3_0 and + not GL_ARB_vertex_array_object then + raise EglcVertexArrayObject.Create('vertex array objects are not supported by video vard'); + glGenVertexArrays(1, @fID); + fArrayBuffers := TObjectList.Create(true); +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +destructor TglcVertexArrayObject.Destroy; +begin + glDeleteVertexArrays(1, @fID); + FreeAndNil(fArrayBuffers); + inherited Destroy; +end; + +end. + -- 2.1.4