root / Client / ULightManager.pas @ 157:0b95089e72d4
History | View | Annotate | Download (10.4 kB)
| 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 |