Revision 157:0b95089e72d4 Client/ULightManager.pas
| b/Client/ULightManager.pas | ||
|---|---|---|
| 1 |
(* |
|
| 2 |
* CDDL HEADER START |
|
| 3 |
* |
|
| 4 |
* The contents of this file are subject to the terms of the |
|
| 5 |
* Common Development and Distribution License, Version 1.0 only |
|
| 6 |
* (the "License"). You may not use this file except in compliance |
|
| 7 |
* with the License. |
|
| 8 |
* |
|
| 9 |
* You can obtain a copy of the license at |
|
| 10 |
* http://www.opensource.org/licenses/cddl1.php. |
|
| 11 |
* See the License for the specific language governing permissions |
|
| 12 |
* and limitations under the License. |
|
| 13 |
* |
|
| 14 |
* When distributing Covered Code, include this CDDL HEADER in each |
|
| 15 |
* file and include the License file at |
|
| 16 |
* http://www.opensource.org/licenses/cddl1.php. If applicable, |
|
| 17 |
* add the following below this CDDL HEADER, with the fields enclosed |
|
| 18 |
* by brackets "[]" replaced with your own identifying * information: |
|
| 19 |
* Portions Copyright [yyyy] [name of copyright owner] |
|
| 20 |
* |
|
| 21 |
* CDDL HEADER END |
|
| 22 |
* |
|
| 23 |
* |
|
| 24 |
* Portions Copyright 2009 Andreas Schneider |
|
| 25 |
*) |
|
| 26 |
unit ULightManager; |
|
| 27 |
|
|
| 28 |
{$mode objfpc}{$H+}
|
|
| 29 |
|
|
| 30 |
interface |
|
| 31 |
|
|
| 32 |
uses |
|
| 33 |
Classes, SysUtils, Imaging, ImagingTypes, ImagingClasses, ImagingCanvases, |
|
| 34 |
ImagingOpenGL, GL, GLu, GLext, fgl, ULandscape, UWorldItem, UCacheManager; |
|
| 35 |
|
|
| 36 |
type |
|
| 37 |
|
|
| 38 |
TCalculateOffset = procedure(AX, AY: Integer; out DrawX, DrawY: Integer) of object; |
|
| 39 |
|
|
| 40 |
{ TLightMaterial }
|
|
| 41 |
|
|
| 42 |
TLightMaterial = class(TSimpleMaterial) |
|
| 43 |
constructor Create(AGraphic: TBaseImage); |
|
| 44 |
destructor Destroy; override; |
|
| 45 |
protected |
|
| 46 |
FCanvas: TFastARGB32Canvas; |
|
| 47 |
public |
|
| 48 |
property Canvas: TFastARGB32Canvas read FCanvas; |
|
| 49 |
end; |
|
| 50 |
|
|
| 51 |
TLightCache = specialize TCacheManager<TLightMaterial>; |
|
| 52 |
|
|
| 53 |
TLightManager = class; |
|
| 54 |
|
|
| 55 |
{ TLightSource }
|
|
| 56 |
|
|
| 57 |
TLightSource = class |
|
| 58 |
constructor Create(AManager: TLightManager; AWorldItem: TWorldItem); |
|
| 59 |
destructor Destroy; override; |
|
| 60 |
protected |
|
| 61 |
FX: Integer; |
|
| 62 |
FY: Integer; |
|
| 63 |
FZ: SmallInt; |
|
| 64 |
FMaterial: TLightMaterial; |
|
| 65 |
public |
|
| 66 |
property X: Integer read FX; |
|
| 67 |
property Y: Integer read FY; |
|
| 68 |
property Z: SmallInt read FZ; |
|
| 69 |
property Material: TLightMaterial read FMaterial; |
|
| 70 |
end; |
|
| 71 |
|
|
| 72 |
TLightSources = specialize TFPGObjectList<TLightSource>; |
|
| 73 |
|
|
| 74 |
{ TLightManager }
|
|
| 75 |
|
|
| 76 |
TLightManager = class |
|
| 77 |
constructor Create(ACalculateOffset: TCalculateOffset); |
|
| 78 |
destructor Destroy; override; |
|
| 79 |
protected |
|
| 80 |
FLightSources: TLightSources; |
|
| 81 |
FOverlay: TSingleImage; |
|
| 82 |
FOverlayTexture: GLuint; |
|
| 83 |
FLightLevel: Byte; |
|
| 84 |
FValid: Boolean; |
|
| 85 |
FCalculateOffset: TCalculateOffset; |
|
| 86 |
FLightCache: TLightCache; |
|
| 87 |
FUseFBO: Boolean; |
|
| 88 |
FInitialized: Boolean; |
|
| 89 |
function GetLight(AID: Integer): TLightMaterial; |
|
| 90 |
procedure SetLightLevel(AValue: Byte); |
|
| 91 |
procedure UpdateOverlay(AScreenRect: TRect); |
|
| 92 |
public |
|
| 93 |
property LightLevel: Byte read FLightLevel write SetLightLevel; |
|
| 94 |
procedure InitGL; |
|
| 95 |
procedure UpdateLightMap(ALeft, AWidth, ATop, AHeight: Integer; |
|
| 96 |
AScreenBuffer: TScreenBuffer); |
|
| 97 |
procedure Draw(AScreenRect: TRect); |
|
| 98 |
end; |
|
| 99 |
|
|
| 100 |
implementation |
|
| 101 |
|
|
| 102 |
uses |
|
| 103 |
UGameResources, UTiledata, UStatics, UMap, ULight, Logging; |
|
| 104 |
|
|
| 105 |
{ TLightManager }
|
|
| 106 |
|
|
| 107 |
constructor TLightManager.Create(ACalculateOffset: TCalculateOffset); |
|
| 108 |
begin |
|
| 109 |
FCalculateOffset := ACalculateOffset; |
|
| 110 |
FLightSources := TLightSources.Create(True); |
|
| 111 |
FLightLevel := 0; |
|
| 112 |
FLightCache := TLightCache.Create(32); |
|
| 113 |
FInitialized := False; |
|
| 114 |
end; |
|
| 115 |
|
|
| 116 |
destructor TLightManager.Destroy; |
|
| 117 |
begin |
|
| 118 |
FreeAndNil(FLightSources); |
|
| 119 |
FreeAndNil(FOverlay); |
|
| 120 |
FreeAndNil(FLightCache); |
|
| 121 |
glDeleteTextures(1, @FOverlayTexture); |
|
| 122 |
inherited Destroy; |
|
| 123 |
end; |
|
| 124 |
|
|
| 125 |
function TLightManager.GetLight(AID: Integer): TLightMaterial; |
|
| 126 |
var |
|
| 127 |
light: TLight; |
|
| 128 |
begin |
|
| 129 |
Result := nil; |
|
| 130 |
if not FLightCache.QueryID(AID, Result) then |
|
| 131 |
begin |
|
| 132 |
if ResMan.Lights.Exists(AID) then |
|
| 133 |
begin |
|
| 134 |
light := ResMan.Lights.GetLight(AID); |
|
| 135 |
Result := TLightMaterial.Create(light.Graphic); |
|
| 136 |
FLightCache.StoreID(AID, Result); |
|
| 137 |
light.Free; |
|
| 138 |
end; |
|
| 139 |
end; |
|
| 140 |
end; |
|
| 141 |
|
|
| 142 |
procedure TLightManager.SetLightLevel(AValue: Byte); |
|
| 143 |
begin |
|
| 144 |
FLightLevel := AValue; |
|
| 145 |
FValid := False; |
|
| 146 |
end; |
|
| 147 |
|
|
| 148 |
procedure TLightManager.UpdateOverlay(AScreenRect: TRect); |
|
| 149 |
var |
|
| 150 |
canvas: TFastARGB32Canvas; |
|
| 151 |
color: TColor32Rec; |
|
| 152 |
i: Integer; |
|
| 153 |
lightMaterial: TLightMaterial; |
|
| 154 |
colorGL: GLclampf; |
|
| 155 |
fbo: GLuint; |
|
| 156 |
begin |
|
| 157 |
glDeleteTextures(1, @FOverlayTexture); |
|
| 158 |
if FUseFBO then |
|
| 159 |
begin |
|
| 160 |
glGenTextures(1, @FOverlayTexture); |
|
| 161 |
glBindTexture(GL_TEXTURE_2D, FOverlayTexture); |
|
| 162 |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
|
| 163 |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
|
| 164 |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
|
| 165 |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
|
| 166 |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, AScreenRect.Right, |
|
| 167 |
AScreenRect.Bottom, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); |
|
| 168 |
glBindTexture(GL_TEXTURE_2D, 0); |
|
| 169 |
|
|
| 170 |
glGenFramebuffersEXT(1, @fbo); |
|
| 171 |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); |
|
| 172 |
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, |
|
| 173 |
GL_TEXTURE_2D, FOverlayTexture, 0); |
|
| 174 |
|
|
| 175 |
colorGL :=(32 - lightLevel) / 32; |
|
| 176 |
glClearColor(colorGL, colorGL, colorGL, 1); |
|
| 177 |
glClear(GL_COLOR_BUFFER_BIT); |
|
| 178 |
|
|
| 179 |
glBlendFunc(GL_ONE, GL_ONE); |
|
| 180 |
for i := 0 to FLightSources.Count - 1 do |
|
| 181 |
begin |
|
| 182 |
lightMaterial := FLightSources[i].Material; |
|
| 183 |
if lightMaterial <> nil then |
|
| 184 |
begin |
|
| 185 |
glBindTexture(GL_TEXTURE_2D, lightMaterial.Texture); |
|
| 186 |
glBegin(GL_QUADS); |
|
| 187 |
glTexCoord2i(0, 0); |
|
| 188 |
glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2, |
|
| 189 |
FLightSources[i].FY - lightMaterial.RealHeight div 2); |
|
| 190 |
glTexCoord2i(0, 1); |
|
| 191 |
glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2, |
|
| 192 |
FLightSources[i].FY - lightMaterial.RealHeight div 2 + |
|
| 193 |
lightMaterial.Height); |
|
| 194 |
glTexCoord2i(1, 1); |
|
| 195 |
glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2 + |
|
| 196 |
lightMaterial.Width, FLightSources[i].FY - |
|
| 197 |
lightMaterial.RealHeight div 2 + lightMaterial.Height); |
|
| 198 |
glTexCoord2i(1, 0); |
|
| 199 |
glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2 + |
|
| 200 |
lightMaterial.Width, |
|
| 201 |
FLightSources[i].FY - lightMaterial.RealHeight div 2); |
|
| 202 |
glEnd; |
|
| 203 |
end; |
|
| 204 |
end; |
|
| 205 |
|
|
| 206 |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); |
|
| 207 |
glDeleteFramebuffersEXT(1, @fbo); |
|
| 208 |
end else |
|
| 209 |
begin |
|
| 210 |
FOverlay.Free; |
|
| 211 |
|
|
| 212 |
color.A := $FF; |
|
| 213 |
color.R := ((32 - FLightLevel) * 255) div 32; |
|
| 214 |
color.G := color.R; |
|
| 215 |
color.B := color.R; |
|
| 216 |
|
|
| 217 |
FOverlay := TSingleImage.CreateFromParams(AScreenRect.Right, |
|
| 218 |
AScreenRect.Bottom, ifA8R8G8B8); |
|
| 219 |
canvas := TFastARGB32Canvas.CreateForImage(FOverlay); |
|
| 220 |
try |
|
| 221 |
canvas.FillColor32 := color.Color; |
|
| 222 |
canvas.FillRect(AScreenRect); |
|
| 223 |
|
|
| 224 |
for i := 0 to FLightSources.Count - 1 do |
|
| 225 |
begin |
|
| 226 |
lightMaterial := FLightSources[i].Material; |
|
| 227 |
if lightMaterial <> nil then |
|
| 228 |
begin |
|
| 229 |
lightMaterial.Canvas.DrawAdd(lightMaterial.Canvas.ClipRect, canvas, |
|
| 230 |
FLightSources[i].FX - lightMaterial.RealWidth div 2, |
|
| 231 |
FLightSources[i].FY - lightMaterial.RealHeight div 2); |
|
| 232 |
end; |
|
| 233 |
end; |
|
| 234 |
finally |
|
| 235 |
canvas.Free; |
|
| 236 |
end; |
|
| 237 |
|
|
| 238 |
FOverlayTexture := CreateGLTextureFromImage(FOverlay.ImageDataPointer^); |
|
| 239 |
end; |
|
| 240 |
|
|
| 241 |
FValid := True; |
|
| 242 |
end; |
|
| 243 |
|
|
| 244 |
procedure TLightManager.InitGL; |
|
| 245 |
begin |
|
| 246 |
FUseFBO := Load_GL_EXT_framebuffer_object; |
|
| 247 |
end; |
|
| 248 |
|
|
| 249 |
procedure TLightManager.UpdateLightMap(ALeft, AWidth, ATop, AHeight: Integer; |
|
| 250 |
AScreenBuffer: TScreenBuffer); |
|
| 251 |
var |
|
| 252 |
blockInfo: PBlockInfo; |
|
| 253 |
lights: TWorldItemList; |
|
| 254 |
i, x, y, tileID: Integer; |
|
| 255 |
tileData: TTiledata; |
|
| 256 |
tileMap: array of array of TWorldItem; |
|
| 257 |
begin |
|
| 258 |
//Logger.EnterMethod([lcClient, lcDebug], 'UpdateLightMap'); |
|
| 259 |
FLightSources.Clear; |
|
| 260 |
{Logger.Send([lcClient, lcDebug], 'AWidth', AWidth);
|
|
| 261 |
Logger.Send([lcClient, lcDebug], 'AHeight', AHeight);} |
|
| 262 |
lights := TWorldItemList.Create(False); |
|
| 263 |
SetLength(tileMap, AWidth, AHeight); |
|
| 264 |
for x := 0 to AWidth - 1 do |
|
| 265 |
for y := 0 to AHeight - 1 do |
|
| 266 |
tileMap[x,y] := nil; |
|
| 267 |
|
|
| 268 |
blockInfo := nil; |
|
| 269 |
while AScreenBuffer.Iterate(blockInfo) do |
|
| 270 |
begin |
|
| 271 |
if blockInfo^.State = ssNormal then |
|
| 272 |
begin |
|
| 273 |
if blockInfo^.Item is TStaticItem then |
|
| 274 |
tileID := blockInfo^.Item.TileID + $4000 |
|
| 275 |
else |
|
| 276 |
tileID := blockInfo^.Item.TileID; |
|
| 277 |
tileData := ResMan.Tiledata.TileData[tileID]; |
|
| 278 |
|
|
| 279 |
if tdfLightSource in tileData.Flags then |
|
| 280 |
lights.Add(blockInfo^.Item) |
|
| 281 |
else |
|
| 282 |
tileMap[blockInfo^.Item.X - ALeft, blockInfo^.Item.Y - ATop] := |
|
| 283 |
blockInfo^.Item; |
|
| 284 |
end; |
|
| 285 |
end; |
|
| 286 |
|
|
| 287 |
for i := 0 to lights.Count - 1 do |
|
| 288 |
begin |
|
| 289 |
x := lights[i].X + 1 - ALeft; |
|
| 290 |
y := lights[i].Y + 1 - ATop; |
|
| 291 |
if (x = AWidth) or (y = AHeight) or (tileMap[x,y] = nil) or |
|
| 292 |
(tileMap[x,y].Z < lights[i].Z + 5) then |
|
| 293 |
FLightSources.Add(TLightSource.Create(Self, lights[i])); |
|
| 294 |
end; |
|
| 295 |
|
|
| 296 |
lights.Free; |
|
| 297 |
|
|
| 298 |
FValid := False; |
|
| 299 |
//Logger.ExitMethod([lcClient, lcDebug], 'UpdateLightMap'); |
|
| 300 |
end; |
|
| 301 |
|
|
| 302 |
procedure TLightManager.Draw(AScreenRect: TRect); |
|
| 303 |
begin |
|
| 304 |
if not FInitialized then |
|
| 305 |
InitGL; |
|
| 306 |
|
|
| 307 |
if not FValid then |
|
| 308 |
UpdateOverlay(AScreenRect); |
|
| 309 |
|
|
| 310 |
glBindTexture(GL_TEXTURE_2D, FOverlayTexture); |
|
| 311 |
glBlendFunc(GL_ZERO, GL_SRC_COLOR); |
|
| 312 |
glBegin(GL_QUADS); |
|
| 313 |
if FUseFBO then |
|
| 314 |
begin |
|
| 315 |
glTexCoord2i(0, 1); |
|
| 316 |
glVertex2i(AScreenRect.Left, AScreenRect.Top); |
|
| 317 |
glTexCoord2i(0, 0); |
|
| 318 |
glVertex2i(AScreenRect.Left, AScreenRect.Bottom); |
|
| 319 |
glTexCoord2i(1, 0); |
|
| 320 |
glVertex2i(AScreenRect.Right, AScreenRect.Bottom); |
|
| 321 |
glTexCoord2i(1, 1); |
|
| 322 |
glVertex2i(AScreenRect.Right, AScreenRect.Top); |
|
| 323 |
end else |
|
| 324 |
begin |
|
| 325 |
glTexCoord2i(0, 0); |
|
| 326 |
glVertex2i(AScreenRect.Left, AScreenRect.Top); |
|
| 327 |
glTexCoord2i(0, 1); |
|
| 328 |
glVertex2i(AScreenRect.Left, AScreenRect.Bottom); |
|
| 329 |
glTexCoord2i(1, 1); |
|
| 330 |
glVertex2i(AScreenRect.Right, AScreenRect.Bottom); |
|
| 331 |
glTexCoord2i(1, 0); |
|
| 332 |
glVertex2i(AScreenRect.Right, AScreenRect.Top); |
|
| 333 |
end; |
|
| 334 |
glEnd; |
|
| 335 |
end; |
|
| 336 |
|
|
| 337 |
{ TLightSource }
|
|
| 338 |
|
|
| 339 |
constructor TLightSource.Create(AManager: TLightManager; AWorldItem: TWorldItem); |
|
| 340 |
var |
|
| 341 |
lightID: Byte; |
|
| 342 |
begin |
|
| 343 |
lightID := ResMan.Tiledata.StaticTiles[AWorldItem.TileID].Quality; |
|
| 344 |
FMaterial := AManager.GetLight(lightID); |
|
| 345 |
if FMaterial <> nil then |
|
| 346 |
begin |
|
| 347 |
AManager.FCalculateOffset(AWorldItem.X, AWorldItem.Y, FX, FY); |
|
| 348 |
FZ := AWorldItem.Z * 4; |
|
| 349 |
FY := FY + 22 - FZ; |
|
| 350 |
FMaterial.AddRef; |
|
| 351 |
end; |
|
| 352 |
end; |
|
| 353 |
|
|
| 354 |
destructor TLightSource.Destroy; |
|
| 355 |
begin |
|
| 356 |
if FMaterial <> nil then |
|
| 357 |
FMaterial.DelRef; |
|
| 358 |
inherited Destroy; |
|
| 359 |
end; |
|
| 360 |
|
|
| 361 |
{ TLightMaterial }
|
|
| 362 |
|
|
| 363 |
constructor TLightMaterial.Create(AGraphic: TBaseImage); |
|
| 364 |
begin |
|
| 365 |
inherited Create(AGraphic); |
|
| 366 |
FCanvas := TFastARGB32Canvas.CreateForImage(FGraphic); |
|
| 367 |
end; |
|
| 368 |
|
|
| 369 |
destructor TLightMaterial.Destroy; |
|
| 370 |
begin |
|
| 371 |
FreeAndNil(FCanvas); |
|
| 372 |
inherited Destroy; |
|
| 373 |
end; |
|
| 374 |
|
|
| 375 |
end. |
|
| 376 |
|
|
| 1 |
(* |
|
| 2 |
* CDDL HEADER START |
|
| 3 |
* |
|
| 4 |
* The contents of this file are subject to the terms of the |
|
| 5 |
* Common Development and Distribution License, Version 1.0 only |
|
| 6 |
* (the "License"). You may not use this file except in compliance |
|
| 7 |
* with the License. |
|
| 8 |
* |
|
| 9 |
* You can obtain a copy of the license at |
|
| 10 |
* http://www.opensource.org/licenses/cddl1.php. |
|
| 11 |
* See the License for the specific language governing permissions |
|
| 12 |
* and limitations under the License. |
|
| 13 |
* |
|
| 14 |
* When distributing Covered Code, include this CDDL HEADER in each |
|
| 15 |
* file and include the License file at |
|
| 16 |
* http://www.opensource.org/licenses/cddl1.php. If applicable, |
|
| 17 |
* add the following below this CDDL HEADER, with the fields enclosed |
|
| 18 |
* by brackets "[]" replaced with your own identifying * information: |
|
| 19 |
* Portions Copyright [yyyy] [name of copyright owner] |
|
| 20 |
* |
|
| 21 |
* CDDL HEADER END |
|
| 22 |
* |
|
| 23 |
* |
|
| 24 |
* Portions Copyright 2009 Andreas Schneider |
|
| 25 |
*) |
|
| 26 |
unit ULightManager; |
|
| 27 |
|
|
| 28 |
{$mode objfpc}{$H+}
|
|
| 29 |
|
|
| 30 |
interface |
|
| 31 |
|
|
| 32 |
uses |
|
| 33 |
Classes, SysUtils, Imaging, ImagingTypes, ImagingClasses, ImagingCanvases, |
|
| 34 |
ImagingOpenGL, GL, GLu, GLext, fgl, ULandscape, UWorldItem, UCacheManager; |
|
| 35 |
|
|
| 36 |
type |
|
| 37 |
|
|
| 38 |
TCalculateOffset = procedure(AX, AY: Integer; out DrawX, DrawY: Integer) of object; |
|
| 39 |
|
|
| 40 |
{ TLightMaterial }
|
|
| 41 |
|
|
| 42 |
TLightMaterial = class(TSimpleMaterial) |
|
| 43 |
constructor Create(AGraphic: TBaseImage); |
|
| 44 |
destructor Destroy; override; |
|
| 45 |
protected |
|
| 46 |
FCanvas: TFastARGB32Canvas; |
|
| 47 |
public |
|
| 48 |
property Canvas: TFastARGB32Canvas read FCanvas; |
|
| 49 |
end; |
|
| 50 |
|
|
| 51 |
TLightCache = specialize TCacheManager<TLightMaterial>; |
|
| 52 |
|
|
| 53 |
TLightManager = class; |
|
| 54 |
|
|
| 55 |
{ TLightSource }
|
|
| 56 |
|
|
| 57 |
TLightSource = class |
|
| 58 |
constructor Create(AManager: TLightManager; AWorldItem: TWorldItem); |
|
| 59 |
destructor Destroy; override; |
|
| 60 |
protected |
|
| 61 |
FX: Integer; |
|
| 62 |
FY: Integer; |
|
| 63 |
FZ: SmallInt; |
|
| 64 |
FMaterial: TLightMaterial; |
|
| 65 |
public |
|
| 66 |
property X: Integer read FX; |
|
| 67 |
property Y: Integer read FY; |
|
| 68 |
property Z: SmallInt read FZ; |
|
| 69 |
property Material: TLightMaterial read FMaterial; |
|
| 70 |
end; |
|
| 71 |
|
|
| 72 |
TLightSources = specialize TFPGObjectList<TLightSource>; |
|
| 73 |
|
|
| 74 |
{ TLightManager }
|
|
| 75 |
|
|
| 76 |
TLightManager = class |
|
| 77 |
constructor Create(ACalculateOffset: TCalculateOffset); |
|
| 78 |
destructor Destroy; override; |
|
| 79 |
protected |
|
| 80 |
FLightSources: TLightSources; |
|
| 81 |
FOverlay: TSingleImage; |
|
| 82 |
FOverlayTexture: GLuint; |
|
| 83 |
FLightLevel: Byte; |
|
| 84 |
FValid: Boolean; |
|
| 85 |
FCalculateOffset: TCalculateOffset; |
|
| 86 |
FLightCache: TLightCache; |
|
| 87 |
FUseFBO: Boolean; |
|
| 88 |
FInitialized: Boolean; |
|
| 89 |
function GetLight(AID: Integer): TLightMaterial; |
|
| 90 |
procedure SetLightLevel(AValue: Byte); |
|
| 91 |
procedure UpdateOverlay(AScreenRect: TRect); |
|
| 92 |
public |
|
| 93 |
property LightLevel: Byte read FLightLevel write SetLightLevel; |
|
| 94 |
procedure InitGL; |
|
| 95 |
procedure UpdateLightMap(ALeft, AWidth, ATop, AHeight: Integer; |
|
| 96 |
AScreenBuffer: TScreenBuffer); |
|
| 97 |
procedure Draw(AScreenRect: TRect); |
|
| 98 |
end; |
|
| 99 |
|
|
| 100 |
implementation |
|
| 101 |
|
|
| 102 |
uses |
|
| 103 |
UGameResources, UTiledata, UStatics, UMap, ULight, Logging; |
|
| 104 |
|
|
| 105 |
{ TLightManager }
|
|
| 106 |
|
|
| 107 |
constructor TLightManager.Create(ACalculateOffset: TCalculateOffset); |
|
| 108 |
begin |
|
| 109 |
FCalculateOffset := ACalculateOffset; |
|
| 110 |
FLightSources := TLightSources.Create(True); |
|
| 111 |
FLightLevel := 0; |
|
| 112 |
FLightCache := TLightCache.Create(32); |
|
| 113 |
FInitialized := False; |
|
| 114 |
end; |
|
| 115 |
|
|
| 116 |
destructor TLightManager.Destroy; |
|
| 117 |
begin |
|
| 118 |
FreeAndNil(FLightSources); |
|
| 119 |
FreeAndNil(FOverlay); |
|
| 120 |
FreeAndNil(FLightCache); |
|
| 121 |
glDeleteTextures(1, @FOverlayTexture); |
|
| 122 |
inherited Destroy; |
|
| 123 |
end; |
|
| 124 |
|
|
| 125 |
function TLightManager.GetLight(AID: Integer): TLightMaterial; |
|
| 126 |
var |
|
| 127 |
light: TLight; |
|
| 128 |
begin |
|
| 129 |
Result := nil; |
|
| 130 |
if not FLightCache.QueryID(AID, Result) then |
|
| 131 |
begin |
|
| 132 |
if ResMan.Lights.Exists(AID) then |
|
| 133 |
begin |
|
| 134 |
light := ResMan.Lights.GetLight(AID); |
|
| 135 |
Result := TLightMaterial.Create(light.Graphic); |
|
| 136 |
FLightCache.StoreID(AID, Result); |
|
| 137 |
light.Free; |
|
| 138 |
end; |
|
| 139 |
end; |
|
| 140 |
end; |
|
| 141 |
|
|
| 142 |
procedure TLightManager.SetLightLevel(AValue: Byte); |
|
| 143 |
begin |
|
| 144 |
FLightLevel := AValue; |
|
| 145 |
FValid := False; |
|
| 146 |
end; |
|
| 147 |
|
|
| 148 |
procedure TLightManager.UpdateOverlay(AScreenRect: TRect); |
|
| 149 |
var |
|
| 150 |
canvas: TFastARGB32Canvas; |
|
| 151 |
color: TColor32Rec; |
|
| 152 |
i: Integer; |
|
| 153 |
lightMaterial: TLightMaterial; |
|
| 154 |
colorGL: GLclampf; |
|
| 155 |
fbo: GLuint; |
|
| 156 |
begin |
|
| 157 |
glDeleteTextures(1, @FOverlayTexture); |
|
| 158 |
if FUseFBO then |
|
| 159 |
begin |
|
| 160 |
glGenTextures(1, @FOverlayTexture); |
|
| 161 |
glBindTexture(GL_TEXTURE_2D, FOverlayTexture); |
|
| 162 |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
|
| 163 |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
|
| 164 |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
|
| 165 |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
|
| 166 |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, AScreenRect.Right, |
|
| 167 |
AScreenRect.Bottom, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); |
|
| 168 |
glBindTexture(GL_TEXTURE_2D, 0); |
|
| 169 |
|
|
| 170 |
glGenFramebuffersEXT(1, @fbo); |
|
| 171 |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); |
|
| 172 |
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, |
|
| 173 |
GL_TEXTURE_2D, FOverlayTexture, 0); |
|
| 174 |
|
|
| 175 |
colorGL :=(32 - lightLevel) / 32; |
|
| 176 |
glClearColor(colorGL, colorGL, colorGL, 1); |
|
| 177 |
glClear(GL_COLOR_BUFFER_BIT); |
|
| 178 |
|
|
| 179 |
glBlendFunc(GL_ONE, GL_ONE); |
|
| 180 |
for i := 0 to FLightSources.Count - 1 do |
|
| 181 |
begin |
|
| 182 |
lightMaterial := FLightSources[i].Material; |
|
| 183 |
if lightMaterial <> nil then |
|
| 184 |
begin |
|
| 185 |
glBindTexture(GL_TEXTURE_2D, lightMaterial.Texture); |
|
| 186 |
glBegin(GL_QUADS); |
|
| 187 |
glTexCoord2i(0, 0); |
|
| 188 |
glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2, |
|
| 189 |
FLightSources[i].FY - lightMaterial.RealHeight div 2); |
|
| 190 |
glTexCoord2i(0, 1); |
|
| 191 |
glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2, |
|
| 192 |
FLightSources[i].FY - lightMaterial.RealHeight div 2 + |
|
| 193 |
lightMaterial.Height); |
|
| 194 |
glTexCoord2i(1, 1); |
|
| 195 |
glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2 + |
|
| 196 |
lightMaterial.Width, FLightSources[i].FY - |
|
| 197 |
lightMaterial.RealHeight div 2 + lightMaterial.Height); |
|
| 198 |
glTexCoord2i(1, 0); |
|
| 199 |
glVertex2i(FLightSources[i].FX - lightMaterial.RealWidth div 2 + |
|
| 200 |
lightMaterial.Width, |
|
| 201 |
FLightSources[i].FY - lightMaterial.RealHeight div 2); |
|
| 202 |
glEnd; |
|
| 203 |
end; |
|
| 204 |
end; |
|
| 205 |
|
|
| 206 |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); |
|
| 207 |
glDeleteFramebuffersEXT(1, @fbo); |
|
| 208 |
end else |
|
| 209 |
begin |
|
| 210 |
FOverlay.Free; |
|
| 211 |
|
|
| 212 |
color.A := $FF; |
|
| 213 |
color.R := ((32 - FLightLevel) * 255) div 32; |
|
| 214 |
color.G := color.R; |
|
| 215 |
color.B := color.R; |
|
| 216 |
|
|
| 217 |
FOverlay := TSingleImage.CreateFromParams(AScreenRect.Right, |
|
| 218 |
AScreenRect.Bottom, ifA8R8G8B8); |
|
| 219 |
canvas := TFastARGB32Canvas.CreateForImage(FOverlay); |
|
| 220 |
try |
|
| 221 |
canvas.FillColor32 := color.Color; |
|
| 222 |
canvas.FillRect(AScreenRect); |
|
| 223 |
|
|
| 224 |
for i := 0 to FLightSources.Count - 1 do |
|
| 225 |
begin |
|
| 226 |
lightMaterial := FLightSources[i].Material; |
|
| 227 |
if lightMaterial <> nil then |
|
| 228 |
begin |
|
| 229 |
lightMaterial.Canvas.DrawAdd(lightMaterial.Canvas.ClipRect, canvas, |
|
| 230 |
FLightSources[i].FX - lightMaterial.RealWidth div 2, |
|
| 231 |
FLightSources[i].FY - lightMaterial.RealHeight div 2); |
|
| 232 |
end; |
|
| 233 |
end; |
|
| 234 |
finally |
|
| 235 |
canvas.Free; |
|
| 236 |
end; |
|
| 237 |
|
|
| 238 |
FOverlayTexture := CreateGLTextureFromImage(FOverlay.ImageDataPointer^); |
|
| 239 |
end; |
|
| 240 |
|
|
| 241 |
FValid := True; |
|
| 242 |
end; |
|
| 243 |
|
|
| 244 |
procedure TLightManager.InitGL; |
|
| 245 |
begin |
|
| 246 |
FUseFBO := Load_GL_EXT_framebuffer_object; |
|
| 247 |
end; |
|
| 248 |
|
|
| 249 |
procedure TLightManager.UpdateLightMap(ALeft, AWidth, ATop, AHeight: Integer; |
|
| 250 |
AScreenBuffer: TScreenBuffer); |
|
| 251 |
var |
|
| 252 |
blockInfo: PBlockInfo; |
|
| 253 |
lights: TWorldItemList; |
|
| 254 |
i, x, y, tileID: Integer; |
|
| 255 |
tileData: TTiledata; |
|
| 256 |
tileMap: array of array of TWorldItem; |
|
| 257 |
begin |
|
| 258 |
//Logger.EnterMethod([lcClient, lcDebug], 'UpdateLightMap'); |
|
| 259 |
FLightSources.Clear; |
|
| 260 |
{Logger.Send([lcClient, lcDebug], 'AWidth', AWidth);
|
|
| 261 |
Logger.Send([lcClient, lcDebug], 'AHeight', AHeight);} |
|
| 262 |
lights := TWorldItemList.Create(False); |
|
| 263 |
SetLength(tileMap, AWidth, AHeight); |
|
| 264 |
for x := 0 to AWidth - 1 do |
|
| 265 |
for y := 0 to AHeight - 1 do |
|
| 266 |
tileMap[x,y] := nil; |
|
| 267 |
|
|
| 268 |
blockInfo := nil; |
|
| 269 |
while AScreenBuffer.Iterate(blockInfo) do |
|
| 270 |
begin |
|
| 271 |
if blockInfo^.State = ssNormal then |
|
| 272 |
begin |
|
| 273 |
if blockInfo^.Item is TStaticItem then |
|
| 274 |
tileID := blockInfo^.Item.TileID + $4000 |
|
| 275 |
else |
|
| 276 |
tileID := blockInfo^.Item.TileID; |
|
| 277 |
tileData := ResMan.Tiledata.TileData[tileID]; |
|
| 278 |
|
|
| 279 |
if tdfLightSource in tileData.Flags then |
|
| 280 |
lights.Add(blockInfo^.Item) |
|
| 281 |
else |
|
| 282 |
tileMap[blockInfo^.Item.X - ALeft, blockInfo^.Item.Y - ATop] := |
|
| 283 |
blockInfo^.Item; |
|
| 284 |
end; |
|
| 285 |
end; |
|
| 286 |
|
|
| 287 |
for i := 0 to lights.Count - 1 do |
|
| 288 |
begin |
|
| 289 |
x := lights[i].X + 1 - ALeft; |
|
| 290 |
y := lights[i].Y + 1 - ATop; |
|
| 291 |
if (x = AWidth) or (y = AHeight) or (tileMap[x,y] = nil) or |
|
| 292 |
(tileMap[x,y].Z < lights[i].Z + 5) then |
|
| 293 |
FLightSources.Add(TLightSource.Create(Self, lights[i])); |
|
| 294 |
end; |
|
| 295 |
|
|
| 296 |
lights.Free; |
|
| 297 |
|
|
| 298 |
FValid := False; |
|
| 299 |
//Logger.ExitMethod([lcClient, lcDebug], 'UpdateLightMap'); |
|
| 300 |
end; |
|
| 301 |
|
|
| 302 |
procedure TLightManager.Draw(AScreenRect: TRect); |
|
| 303 |
begin |
|
| 304 |
if not FInitialized then |
|
| 305 |
InitGL; |
|
| 306 |
|
|
| 307 |
glColor4f(1, 1, 1, 1); |
|
| 308 |
|
|
| 309 |
if not FValid then |
|
| 310 |
UpdateOverlay(AScreenRect); |
|
| 311 |
|
|
| 312 |
glBindTexture(GL_TEXTURE_2D, FOverlayTexture); |
|
| 313 |
glBlendFunc(GL_ZERO, GL_SRC_COLOR); |
|
| 314 |
glBegin(GL_QUADS); |
|
| 315 |
if FUseFBO then |
|
| 316 |
begin |
|
| 317 |
glTexCoord2i(0, 1); |
|
| 318 |
glVertex2i(AScreenRect.Left, AScreenRect.Top); |
|
| 319 |
glTexCoord2i(0, 0); |
|
| 320 |
glVertex2i(AScreenRect.Left, AScreenRect.Bottom); |
|
| 321 |
glTexCoord2i(1, 0); |
|
| 322 |
glVertex2i(AScreenRect.Right, AScreenRect.Bottom); |
|
| 323 |
glTexCoord2i(1, 1); |
|
| 324 |
glVertex2i(AScreenRect.Right, AScreenRect.Top); |
|
| 325 |
end else |
|
| 326 |
begin |
|
| 327 |
glTexCoord2i(0, 0); |
|
| 328 |
glVertex2i(AScreenRect.Left, AScreenRect.Top); |
|
| 329 |
glTexCoord2i(0, 1); |
|
| 330 |
glVertex2i(AScreenRect.Left, AScreenRect.Bottom); |
|
| 331 |
glTexCoord2i(1, 1); |
|
| 332 |
glVertex2i(AScreenRect.Right, AScreenRect.Bottom); |
|
| 333 |
glTexCoord2i(1, 0); |
|
| 334 |
glVertex2i(AScreenRect.Right, AScreenRect.Top); |
|
| 335 |
end; |
|
| 336 |
glEnd; |
|
| 337 |
end; |
|
| 338 |
|
|
| 339 |
{ TLightSource }
|
|
| 340 |
|
|
| 341 |
constructor TLightSource.Create(AManager: TLightManager; AWorldItem: TWorldItem); |
|
| 342 |
var |
|
| 343 |
lightID: Byte; |
|
| 344 |
begin |
|
| 345 |
lightID := ResMan.Tiledata.StaticTiles[AWorldItem.TileID].Quality; |
|
| 346 |
FMaterial := AManager.GetLight(lightID); |
|
| 347 |
if FMaterial <> nil then |
|
| 348 |
begin |
|
| 349 |
AManager.FCalculateOffset(AWorldItem.X, AWorldItem.Y, FX, FY); |
|
| 350 |
FZ := AWorldItem.Z * 4; |
|
| 351 |
FY := FY + 22 - FZ; |
|
| 352 |
FMaterial.AddRef; |
|
| 353 |
end; |
|
| 354 |
end; |
|
| 355 |
|
|
| 356 |
destructor TLightSource.Destroy; |
|
| 357 |
begin |
|
| 358 |
if FMaterial <> nil then |
|
| 359 |
FMaterial.DelRef; |
|
| 360 |
inherited Destroy; |
|
| 361 |
end; |
|
| 362 |
|
|
| 363 |
{ TLightMaterial }
|
|
| 364 |
|
|
| 365 |
constructor TLightMaterial.Create(AGraphic: TBaseImage); |
|
| 366 |
begin |
|
| 367 |
inherited Create(AGraphic); |
|
| 368 |
FCanvas := TFastARGB32Canvas.CreateForImage(FGraphic); |
|
| 369 |
end; |
|
| 370 |
|
|
| 371 |
destructor TLightMaterial.Destroy; |
|
| 372 |
begin |
|
| 373 |
FreeAndNil(FCanvas); |
|
| 374 |
inherited Destroy; |
|
| 375 |
end; |
|
| 376 |
|
|
| 377 |
end. |
|
| 378 |
|
|
Also available in: Unified diff