From 841061abbbaf62240d55a42e9eafafc6b15b2417 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Tue, 22 Dec 2009 09:36:35 +0100 Subject: [PATCH] - Added FBO based light source rendering - Fixed memory leak in TLightManager.UpdateLightMap --- Client/CentrED.lpi | 4 +- Client/ULandscape.pas | 4 +- Client/ULightManager.pas | 165 +++++++++++++++++++++++++-------------- 3 files changed, 110 insertions(+), 63 deletions(-) diff --git a/Client/CentrED.lpi b/Client/CentrED.lpi index f33ca53..be45733 100644 --- a/Client/CentrED.lpi +++ b/Client/CentrED.lpi @@ -350,14 +350,14 @@ - + + - diff --git a/Client/ULandscape.pas b/Client/ULandscape.pas index 6e009a8..60fb1c1 100644 --- a/Client/ULandscape.pas +++ b/Client/ULandscape.pas @@ -56,7 +56,7 @@ type FRealWidth: Integer; FRealHeight: Integer; FGraphic: TMultiImage; - procedure CalculateTextureDimensions(ACaps: TGLTextureCaps; ARealWidth, + class procedure CalculateTextureDimensions(ACaps: TGLTextureCaps; ARealWidth, ARealHeight: Integer; out AWidth, AHeight: Integer); function GenerateTexture(AImage: TBaseImage): TGLuint; function GetTexture: GLuint; virtual; abstract; @@ -1283,7 +1283,7 @@ begin inherited Destroy; end; -procedure TMaterial.CalculateTextureDimensions(ACaps: TGLTextureCaps; +class procedure TMaterial.CalculateTextureDimensions(ACaps: TGLTextureCaps; ARealWidth, ARealHeight: Integer; out AWidth, AHeight: Integer); begin if ACaps.NonPowerOfTwo then diff --git a/Client/ULightManager.pas b/Client/ULightManager.pas index 0a2d3c0..a46f85e 100644 --- a/Client/ULightManager.pas +++ b/Client/ULightManager.pas @@ -31,8 +31,7 @@ interface uses Classes, SysUtils, Imaging, ImagingTypes, ImagingClasses, ImagingCanvases, - ImagingOpenGL, GL, fgl, ULandscape, UWorldItem, UCacheManager, - ImagingUtility; + ImagingOpenGL, GL, GLu, GLext, fgl, ULandscape, UWorldItem, UCacheManager; type @@ -40,22 +39,13 @@ type { TLightMaterial } - TLightMaterial = class(ICacheable) + TLightMaterial = class(TSimpleMaterial) constructor Create(AGraphic: TBaseImage); destructor Destroy; override; protected - FRefCount: Integer; - FGraphic: TSingleImage; FCanvas: TFastARGB32Canvas; public - property Graphic: TSingleImage read FGraphic; property Canvas: TFastARGB32Canvas read FCanvas; - procedure AddRef; - procedure DelRef; - - {ICacheable} - function CanBeRemoved: Boolean; - procedure RemoveFromCache; end; TLightCache = specialize TCacheManager; @@ -94,11 +84,14 @@ type FValid: Boolean; FCalculateOffset: TCalculateOffset; FLightCache: TLightCache; + FUseFBO: Boolean; + FInitialized: Boolean; function GetLight(AID: Integer): TLightMaterial; procedure SetLightLevel(AValue: Byte); procedure UpdateOverlay(AScreenRect: TRect); public property LightLevel: Byte read FLightLevel write SetLightLevel; + procedure InitGL; procedure UpdateLightMap(ALeft, AWidth, ATop, AHeight: Integer; AScreenBuffer: TScreenBuffer); procedure Draw(AScreenRect: TRect); @@ -117,6 +110,7 @@ begin FLightSources := TLightSources.Create(True); FLightLevel := 0; FLightCache := TLightCache.Create(32); + FInitialized := False; end; destructor TLightManager.Destroy; @@ -157,42 +151,101 @@ var color: TColor32Rec; i: Integer; lightMaterial: TLightMaterial; + colorGL: GLclampf; + fbo: GLuint; begin - FOverlay.Free; glDeleteTextures(1, @FOverlayTexture); + if FUseFBO then + begin + glGenTextures(1, @FOverlayTexture); + glBindTexture(GL_TEXTURE_2D, FOverlayTexture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, AScreenRect.Right, + AScreenRect.Bottom, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); + glBindTexture(GL_TEXTURE_2D, 0); - color.A := $FF; - color.R := ((32 - FLightLevel) * 255) div 32; - color.G := color.R; - color.B := color.R; + glGenFramebuffersEXT(1, @fbo); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, FOverlayTexture, 0); - FOverlay := TSingleImage.CreateFromParams(AScreenRect.Right, - AScreenRect.Bottom, ifA8R8G8B8); - canvas := TFastARGB32Canvas.CreateForImage(FOverlay); - try - canvas.FillColor32 := color.Color; - canvas.FillRect(AScreenRect); + colorGL :=(32 - lightLevel) / 32; + glClearColor(colorGL, colorGL, colorGL, 1); + glClear(GL_COLOR_BUFFER_BIT); + glBlendFunc(GL_ONE, GL_ONE); for i := 0 to FLightSources.Count - 1 do - begin - lightMaterial := FLightSources[i].Material; - if lightMaterial <> nil then begin - lightMaterial.Canvas.DrawAdd(lightMaterial.Canvas.ClipRect, canvas, - FLightSources[i].FX - lightMaterial.Graphic.Width div 2, - FLightSources[i].FY - lightMaterial.Graphic.Height div 2); + lightMaterial := FLightSources[i].Material; + if lightMaterial <> nil then + begin + glBindTexture(GL_TEXTURE_2D, lightMaterial.Texture); + glBegin(GL_QUADS); + glTexCoord2i(0, 0); + glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2, + FLightSources[i].FY - lightMaterial.RealHeight div 2); + glTexCoord2i(0, 1); + glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2, + FLightSources[i].FY - lightMaterial.RealHeight div 2 + + lightMaterial.Height); + glTexCoord2i(1, 1); + glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2 + + lightMaterial.Width, FLightSources[i].FY - + lightMaterial.RealHeight div 2 + lightMaterial.Height); + glTexCoord2i(1, 0); + glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2 + + lightMaterial.Width, + FLightSources[i].FY - lightMaterial.RealHeight div 2); + glEnd; + end; end; + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glDeleteFramebuffersEXT(1, @fbo); + end else + begin + FOverlay.Free; + + color.A := $FF; + color.R := ((32 - FLightLevel) * 255) div 32; + color.G := color.R; + color.B := color.R; + + FOverlay := TSingleImage.CreateFromParams(AScreenRect.Right, + AScreenRect.Bottom, ifA8R8G8B8); + canvas := TFastARGB32Canvas.CreateForImage(FOverlay); + try + canvas.FillColor32 := color.Color; + canvas.FillRect(AScreenRect); + + for i := 0 to FLightSources.Count - 1 do + begin + lightMaterial := FLightSources[i].Material; + if lightMaterial <> nil then + begin + lightMaterial.Canvas.DrawAdd(lightMaterial.Canvas.ClipRect, canvas, + FLightSources[i].FX - lightMaterial.RealWidth div 2, + FLightSources[i].FY - lightMaterial.RealHeight div 2); + end; + end; + finally + canvas.Free; end; - finally - canvas.Free; + + FOverlayTexture := CreateGLTextureFromImage(FOverlay.ImageDataPointer^); end; - //TODO : PowerOfTwo!!! - FOverlayTexture := CreateGLTextureFromImage(FOverlay.ImageDataPointer^); - FValid := True; end; +procedure TLightManager.InitGL; +begin + FUseFBO := Load_GL_EXT_framebuffer_object; +end; + procedure TLightManager.UpdateLightMap(ALeft, AWidth, ATop, AHeight: Integer; AScreenBuffer: TScreenBuffer); var @@ -249,18 +302,35 @@ begin for i := 0 to lights.Count - 1 do FLightSources.Add(TLightSource.Create(Self, lights[i])); + lights.Free; + FValid := False; //Logger.ExitMethod([lcClient, lcDebug], 'UpdateLightMap'); end; procedure TLightManager.Draw(AScreenRect: TRect); begin + if not FInitialized then + InitGL; + if not FValid then UpdateOverlay(AScreenRect); glBindTexture(GL_TEXTURE_2D, FOverlayTexture); glBlendFunc(GL_ZERO, GL_SRC_COLOR); glBegin(GL_QUADS); + if FUseFBO then + begin + glTexCoord2i(0, 1); + glVertex2i(AScreenRect.Left, AScreenRect.Top); + glTexCoord2i(0, 0); + glVertex2i(AScreenRect.Left, AScreenRect.Bottom); + glTexCoord2i(1, 0); + glVertex2i(AScreenRect.Right, AScreenRect.Bottom); + glTexCoord2i(1, 1); + glVertex2i(AScreenRect.Right, AScreenRect.Top); + end else + begin glTexCoord2i(0, 0); glVertex2i(AScreenRect.Left, AScreenRect.Top); glTexCoord2i(0, 1); @@ -269,6 +339,7 @@ begin glVertex2i(AScreenRect.Right, AScreenRect.Bottom); glTexCoord2i(1, 0); glVertex2i(AScreenRect.Right, AScreenRect.Top); + end; glEnd; end; @@ -300,39 +371,15 @@ end; constructor TLightMaterial.Create(AGraphic: TBaseImage); begin - FRefCount := 1; - FGraphic := TSingleImage.CreateFromImage(AGraphic); + inherited Create(AGraphic); FCanvas := TFastARGB32Canvas.CreateForImage(FGraphic); end; destructor TLightMaterial.Destroy; begin FreeAndNil(FCanvas); - FreeAndNil(FGraphic); inherited Destroy; end; -procedure TLightMaterial.AddRef; -begin - Inc(FRefCount); -end; - -procedure TLightMaterial.DelRef; -begin - Dec(FRefCount); - if FRefCount < 1 then - Free; -end; - -function TLightMaterial.CanBeRemoved: Boolean; -begin - Result := (FRefCount <= 1); -end; - -procedure TLightMaterial.RemoveFromCache; -begin - DelRef; -end; - end.