Statistics
| Branch: | Tag: | Revision:

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