Revision 119:66352054ce4d Client/ULandscape.pas

b/Client/ULandscape.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 ULandscape;
27

  
28
{$mode objfpc}{$H+}
29

  
30
interface
31

  
32
uses
33
  SysUtils, Classes, math, LCLIntf, GL, GLu, ImagingOpenGL, Imaging,
34
  ImagingClasses, ImagingTypes, ImagingUtility,
35
  UGenericIndex, UMap, UStatics, UArt, UTexture, UTiledata, UHue, UWorldItem,
36
  UMulBlock, UAnimData,
37
  UVector, UEnhancedMemoryStream, UGLFont,
38
  UCacheManager;
39

  
40
type
41
  PNormals = ^TNormals;
42
  TNormals = array[0..3] of TVector;
43
  PRadarBlock = ^TRadarBlock;
44
  TRadarBlock = array[0..7, 0..7] of Word;
45
  
46
  { TMaterial }
47
  
48
  TMaterial = class(ICacheable)
49
    constructor Create;
50
    destructor Destroy; override;
51
  protected
52
    FRefCount: Integer;
53
    FWidth: Integer;
54
    FHeight: Integer;
55
    FRealWidth: Integer;
56
    FRealHeight: Integer;
57
    FGraphic: TMultiImage;
58
    procedure CalculateTextureDimensions(ACaps: TGLTextureCaps; ARealWidth,
59
      ARealHeight: Integer; out AWidth, AHeight: Integer);
60
    function GenerateTexture(AImage: TBaseImage): TGLuint;
61
    function GetTexture: GLuint; virtual; abstract;
62
  public
63
    property Width: Integer read FWidth;
64
    property Height: Integer read FHeight;
65
    property RealWidth: Integer read FRealWidth;
66
    property RealHeight: Integer read FRealHeight;
67
    property Texture: GLuint read GetTexture;
68

  
69
    procedure AddRef;
70
    procedure DelRef;
71
    function HitTest(AX, AY: Integer): Boolean;
72

  
73
    {ICacheable}
74
    function CanBeRemoved: Boolean;
75
    procedure RemoveFromCache;
76
  end;
77

  
78
  { TSimpleMaterial }
79

  
80
  TSimpleMaterial = class(TMaterial)
81
    constructor Create(AGraphic: TBaseImage);
82
    destructor Destroy; override;
83
  protected
84
    FTexture: TGLuint;
85
    function GetTexture: GLuint; override;
86
  end;
87

  
88
  { TAnimMaterial }
89

  
90
  TAnimMaterial = class(TMaterial)
91
    constructor Create(ABaseID: Word; AAnimData: TAnimData; AHue: THue = nil;
92
      APartialHue: Boolean = False);
93
    destructor Destroy; override;
94
  protected
95
    FActiveFrame: Byte;
96
    FNextChange: DWord;
97
    FAnimData: TAnimData;
98
    FTextures: array of TGLuint;
99
    function GetTexture: GLuint; override;
100
  end;
101

  
102
  TMaterialCache = specialize TCacheManager<TMaterial>;
103
  
104
  { TLandTextureManager }
105
  
106
  TLandTextureManager = class
107
    constructor Create;
108
    destructor Destroy; override;
109
  protected
110
    FArtCache: TMaterialCache;
111
    FTexCache: TMaterialCache;
112
    FAnimCache: TMaterialCache;
113
    FUseAnims: Boolean;
114
  public
115
    property UseAnims: Boolean read FUseAnims write FUseAnims;
116
    function GetArtMaterial(ATileID: Word): TMaterial; overload;
117
    function GetArtMaterial(ATileID: Word; AHue: THue;
118
      APartialHue: Boolean): TMaterial; overload;
119
    function GetStaticMaterial(AStaticItem: TStaticItem;
120
      AOverrideHue: Integer = -1): TMaterial;
121
    function GetTexMaterial(ATileID: Word): TMaterial;
122
  end;
123

  
124
 { TSeperatedStaticBlock }
125

  
126
  TSeperatedStaticBlock = class(TStaticBlock)
127
    constructor Create(AData: TStream; AIndex: TGenericIndex; AX, AY: Word); overload;
128
    constructor Create(AData: TStream; AIndex: TGenericIndex); overload;
129
    destructor Destroy; override;
130
  public
131
    Cells: array[0..63] of TStaticItemList;
132
    { Methods }
133
    function Clone: TSeperatedStaticBlock; override;
134
    function GetSize: Integer; override;
135
    procedure RebuildList;
136
  end;
137

  
138
  TLandscape = class;
139
  
140
  { TBlock }
141

  
142
  TBlock = class
143
    constructor Create(AMap: TMapBlock; AStatics: TStaticBlock);
144
    destructor Destroy; override;
145
  protected
146
    { Fields }
147
    FMapBlock: TMapBlock;
148
    FStaticBlock: TStaticBlock;
149
  public
150
    { Fields }
151
    property Map: TMapBlock read FMapBlock;
152
    property Static: TStaticBlock read FStaticBlock;
153
    { Methods }
154
    procedure UpdateBlockAcess(ALandscape: TLandscape);
155
  end;
156
  
157
  TLandscapeChangeEvent = procedure of object;
158
  TMapChangedEvent = procedure(AMapCell: TMapCell) of object;
159
  TNewBlockEvent = procedure(ABlock: TBlock) of object;
160
  TStaticChangedEvent = procedure(AStaticItem: TStaticItem) of object;
161

  
162
  TScreenBuffer = class;
163
  TBlockCache = specialize TCacheManager<TBlock>;
164

  
165
  { TLandscape }
166

  
167
  TLandscape = class
168
    constructor Create(AWidth, AHeight: Word);
169
    destructor Destroy; override;
170
  protected
171
    { Members }
172
    FWidth: Word;
173
    FHeight: Word;
174
    FCellWidth: Word;
175
    FCellHeight: Word;
176
    FBlockCache: TBlockCache;
177
    FOnChange: TLandscapeChangeEvent;
178
    FOnMapChanged: TMapChangedEvent;
179
    FOnNewBlock: TNewBlockEvent;
180
    FOnStaticInserted: TStaticChangedEvent;
181
    FOnStaticDeleted: TStaticChangedEvent;
182
    FOnStaticElevated: TStaticChangedEvent;
183
    FOnStaticHued: TStaticChangedEvent;
184
    FOpenRequests: TBits;
185
    FWriteMap: TBits;
186
    { Methods }
187
    function GetMapBlock(AX, AY: Word): TMapBlock;
188
    function GetMapCell(AX, AY: Word): TMapCell;
189
    function GetNormals(AX, AY: Word): TNormals;
190
    function GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
191
    function GetStaticList(AX, AY: Word): TStaticItemList;
192
    { Events }
193
    procedure OnRemoveCachedObject(ABlock: TBlock);
194
    procedure OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
195
    procedure OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
196
    procedure OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
197
    procedure OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
198
    procedure OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
199
    procedure OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
200
    procedure OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
201
  public
202
    { Fields }
203
    property Width: Word read FWidth;
204
    property Height: Word read FHeight;
205
    property CellWidth: Word read FCellWidth;
206
    property CellHeight: Word read FCellHeight;
207
    property MapCell[X, Y: Word]: TMapCell read GetMapCell;
208
    property StaticList[X, Y: Word]: TStaticItemList read GetStaticList;
209
    property Normals[X, Y: Word]: TNormals read GetNormals;
210
    property OnChange: TLandscapeChangeEvent read FOnChange write FOnChange;
211
    property OnMapChanged: TMapChangedEvent read FOnMapChanged write FOnMapChanged;
212
    property OnNewBlock: TNewBlockEvent read FOnNewBlock write FOnNewBlock;
213
    property OnStaticInserted: TStaticChangedEvent read FOnStaticInserted
214
      write FOnStaticInserted;
215
    property OnStaticDeleted: TStaticChangedEvent read FOnStaticDeleted
216
      write FOnStaticDeleted;
217
    property OnStaticElevated: TStaticChangedEvent read FOnStaticElevated
218
      write FOnStaticElevated;
219
    property OnStaticHued: TStaticChangedEvent read FOnStaticHued
220
      write FOnStaticHued;
221
    { Methods }
222
    function CanWrite(AX, AY: Word): Boolean;
223
    procedure FillDrawList(ADrawList: TScreenBuffer; AX, AY, AWidth,
224
      AHeight: Word; AMap, AStatics: Boolean; ANoDraw: Boolean;
225
      AAdditionalTiles: TWorldItemList = nil);
226
    function GetEffectiveAltitude(ATile: TMapCell): ShortInt;
227
    function GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
228
    procedure GetNormals(AX, AY: Word; var ANormals: TNormals);
229
    procedure MoveStatic(AStatic: TStaticItem; AX, AY: Word);
230
    procedure PrepareBlocks(AX1, AY1, AX2, AY2: Word);
231
    procedure UpdateBlockAccess;
232
    procedure UpdateWriteMap(AStream: TEnhancedMemoryStream);
233
  end;
234

  
235
  { TGLText }
236

  
237
  TGLText = class
238
    constructor Create(AFont: TGLFont; AText: String);
239
  protected
240
    FFont: TGLFont;
241
    FText: String;
242
    FWidth: Integer;
243
    FHeight: Integer;
244
  public
245
    procedure Render(AScreenRect: TRect);
246
  end;
247

  
248
  TScreenState = (ssNormal, ssFiltered, ssGhost);
249

  
250
  PBlockInfo = ^TBlockInfo;
251
  TBlockInfo = record
252
    ScreenRect: TRect;
253
    DrawQuad: array[0..3,0..1] of TGLint;
254
    RealQuad: array[0..3,0..1] of TGLint;
255
    Item: TWorldItem;
256
    HighRes: TMaterial;
257
    LowRes: TMaterial;
258
    Normals: PNormals;
259
    State: TScreenState;
260
    Highlighted: Boolean;
261
    HueOverride: Boolean;
262
    CheckRealQuad: Boolean;
263
    Translucent: Boolean;
264
    Text: TGLText;
265
    Next: PBlockInfo;
266
  end;
267

  
268
  { TScreenBuffer }
269

  
270
  TScreenBuffer = class
271
    constructor Create; virtual;
272
    destructor Destroy; override;
273
  protected
274
    { Members }
275
    FCount: Cardinal;
276
    FShortCuts: array[-1..10] of PBlockInfo; //-1 = last, 0 = first, 1..10 = other shortcuts
277
    FShortCutsValid: Boolean;
278
    FSerial: Cardinal;
279
  public
280
    { Methods }
281
    function Add(AItem: TWorldItem): PBlockInfo;
282
    procedure Clear;
283
    procedure Delete(AItem: TWorldItem);
284
    function Find(AScreenPosition: TPoint): PBlockInfo;
285
    function GetSerial: Cardinal;
286
    function Insert(AItem: TWorldItem): PBlockInfo;
287
    function Iterate(var ABlockInfo: PBlockInfo): Boolean;
288
    procedure UpdateShortcuts;
289
    function UpdateSortOrder(AItem: TWorldItem): PBlockInfo;
290
    { Events }
291
    procedure OnTileRemoved(ATile: TMulBlock);
292
  end;
293
  
294
  TStaticInfo = packed record
295
    X: Word;
296
    Y: Word;
297
    Z: ShortInt;
298
    TileID: Word;
299
    Hue: Word;
300
  end;
301

  
302
implementation
303

  
304
uses
305
  UGameResources, UdmNetwork, UPackets, UPacketHandlers, Logging;
306

  
307
function GetID(AX, AY: Word): Integer; inline;
308
begin
309
  Result := (AX shl 16) or AY;
310
end;
311

  
312
{ TLandTextureManager }
313

  
314
constructor TLandTextureManager.Create;
315
begin
316
  inherited Create;
317
  FArtCache := TMaterialCache.Create(1024);
318
  FTexCache := TMaterialCache.Create(128);
319
  FAnimCache := TMaterialCache.Create(128);
320
  FUseAnims := True;
321
end;
322

  
323
destructor TLandTextureManager.Destroy;
324
begin
325
  FreeAndNil(FArtCache);
326
  FreeAndNil(FTexCache);
327
  FreeAndNil(FAnimCache);
328
  inherited Destroy;
329
end;
330

  
331
function TLandTextureManager.GetArtMaterial(ATileID: Word): TMaterial;
332
var
333
  artEntry: TArt;
334
  animData: TAnimData;
335
begin
336
  Result := nil;
337

  
338
  if FUseAnims and (ATileID >= $4000) and (tdfAnimation in
339
      ResMan.Tiledata.StaticTiles[ATileID -$4000].Flags) then
340
  begin
341
    animData := ResMan.Animdata.AnimData[ATileID - $4000];
342
    if (animData.FrameCount > 0) and not FAnimCache.QueryID(ATileID, Result) then
343
    begin
344
      Result := TAnimMaterial.Create(ATileID, animData);
345
      FAnimCache.StoreID(ATileID, Result);
346
    end;
347
  end;
348

  
349
  if (Result = nil) and not FArtCache.QueryID(ATileID, Result) then
350
  begin
351
    artEntry := TArt(ResMan.Art.Block[ATileID]);
352

  
353
    Result := TSimpleMaterial.Create(artEntry.Graphic);
354
    FArtCache.StoreID(ATileID, Result);
355

  
356
    artEntry.Free;
357
  end;
358

  
359
  Result.AddRef;
360
end;
361

  
362
function TLandTextureManager.GetArtMaterial(ATileID: Word; AHue: THue;
363
  APartialHue: Boolean): TMaterial;
364
var
365
  artEntry: TArt;
366
  animData: TAnimData;
367
  id: Integer;
368
begin
369
  if AHue = nil then
370
  begin
371
    Result := GetArtMaterial(ATileID);
372
  end else
373
  begin
374
    Result := nil;
375
    id := ATileID or ((AHue.ID and $3FFF) shl 16) or (Byte(APartialHue) shl 30);
376

  
377
    if FUseAnims and (ATileID >= $4000) and (tdfAnimation in
378
      ResMan.Tiledata.StaticTiles[ATileID -$4000].Flags) then
379
    begin
380
      animData := ResMan.Animdata.AnimData[ATileID - $4000];
381
      if (animData.FrameCount > 0) and not FAnimCache.QueryID(id, Result) then
382
      begin
383
        Result := TAnimMaterial.Create(ATileID, animData, AHue, APartialHue);
384
        FAnimCache.StoreID(id, Result);
385
      end;
386
    end;
387

  
388
    if (Result = nil) and not FArtCache.QueryID(id, Result) then
389
    begin
390
      artEntry := ResMan.Art.GetArt(ATileID, 0, AHue, APartialHue);
391

  
392
      Result := TSimpleMaterial.Create(artEntry.Graphic);
393
      FArtCache.StoreID(id, Result);
394

  
395
      artEntry.Free;
396
    end;
397
    Result.AddRef;
398
  end;
399
end;
400

  
401
function TLandTextureManager.GetStaticMaterial(AStaticItem: TStaticItem;
402
  AOverrideHue: Integer = -1): TMaterial;
403
var
404
  staticTiledata: TStaticTiledata;
405
  hue: THue;
406
begin
407
  staticTiledata := ResMan.Tiledata.StaticTiles[AStaticItem.TileID];
408
  if AOverrideHue < 0 then
409
    AOverrideHue := AStaticItem.Hue;
410

  
411
  if AOverrideHue > 0 then
412
    hue := ResMan.Hue.Hues[AOverrideHue - 1]
413
  else
414
    hue := nil;
415

  
416
  Result := GetArtMaterial($4000 + AStaticItem.TileID, hue,
417
    tdfPartialHue in staticTiledata.Flags);
418
end;
419

  
420
function TLandTextureManager.GetTexMaterial(ATileID: Word): TMaterial;
421
var
422
  texEntry: TTexture;
423
  texID: Integer;
424
begin
425
  if not FTexCache.QueryID(ATileID, Result) then
426
  begin
427
    texID := ResMan.Tiledata.LandTiles[ATileID].TextureID;
428
    if texID > 0 then
429
    begin
430
      texEntry := TTexture(ResMan.Texmaps.Block[texID]);
431

  
432
      Result := TSimpleMaterial.Create(texEntry.Graphic);
433
      FTexCache.StoreID(ATileID, Result);
434

  
435
      texEntry.Free;
436
    end else
437
      Result := nil;
438
  end;
439

  
440
  if Result <> nil then
441
    Result.AddRef;
442
end;
443

  
444
{ TSeperatedStaticBlock }
445

  
446
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex;
447
  AX, AY: Word);
448
var
449
  i: Integer;
450
  item: TStaticItem;
451
  block: TMemoryStream;
452
begin
453
  inherited Create;
454
  FItems := TStaticItemList.Create(False);
455

  
456
  FX := AX;
457
  FY := AY;
458

  
459
  for i := 0 to 63 do
460
    Cells[i] := TStaticItemList.Create;
461

  
462
  if (AData <> nil) and (AIndex.Lookup > 0) and (AIndex.Size > 0) then
463
  begin
464
    AData.Position := AIndex.Lookup;
465
    block := TMemoryStream.Create;
466
    block.CopyFrom(AData, AIndex.Size);
467
    block.Position := 0;
468
    for i := 1 to (AIndex.Size div 7) do
469
    begin
470
      item := TStaticItem.Create(Self, block, AX, AY);
471
      Cells[(item.Y mod 8) * 8 + (item.X mod 8)].Add(item);
472
    end;
473
    block.Free;
474
  end;
475
end;
476

  
477
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex);
478
begin
479
  Create(AData, AIndex, 0, 0);
480
end;
481

  
482
destructor TSeperatedStaticBlock.Destroy;
483
var
484
  i: Integer;
485
begin
486
  FreeAndNil(FItems);
487

  
488
  for i := 0 to 63 do
489
  begin
490
    if Cells[i] <> nil then
491
      FreeAndNil(Cells[i]);
492
  end;
493

  
494
  inherited Destroy;
495
end;
496

  
497
function TSeperatedStaticBlock.Clone: TSeperatedStaticBlock;
498
begin
499
  raise Exception.Create('TSeperatedStaticBlock.Clone is not implemented (yet).');
500
end;
501

  
502
function TSeperatedStaticBlock.GetSize: Integer;
503
begin
504
  RebuildList;
505
  Result := inherited GetSize;
506
end;
507

  
508
procedure TSeperatedStaticBlock.RebuildList;
509
var
510
  i, j, solver: Integer;
511
begin
512
  FItems.Clear;
513
  solver := 0;
514
  for i := 0 to 63 do
515
  begin
516
    if Cells[i] <> nil then
517
    begin
518
      for j := 0 to Cells[i].Count - 1 do
519
      begin
520
        FItems.Add(Cells[i].Items[j]);
521
        TStaticItem(Cells[i].Items[j]).UpdatePriorities(
522
          ResMan.Tiledata.StaticTiles[TStaticItem(Cells[i].Items[j]).TileID],
523
          solver);
524
        Inc(solver);
525
      end;
526
    end;
527
  end;
528
  Sort;
529
end;
530

  
531
{ TBlock }
532

  
533
constructor TBlock.Create(AMap: TMapBlock; AStatics: TStaticBlock);
534
begin
535
  inherited Create;
536
  FMapBlock := AMap;
537
  FStaticBlock := AStatics;
538
end;
539

  
540
destructor TBlock.Destroy;
541
begin
542
  if FMapBlock <> nil then FreeAndNil(FMapBlock);
543
  if FStaticBlock <> nil then FreeAndNil(FStaticBlock);
544
  inherited Destroy;
545
end;
546

  
547
procedure TBlock.UpdateBlockAcess(ALandscape: TLandscape);
548
var
549
  staticItem: TStaticItem;
550
  i: Integer;
551
begin
552
  for i := Low(FMapBlock.Cells) to High(FMapBlock.Cells) do
553
  begin
554
    FMapBlock.Cells[i].CanBeEdited := ALandscape.CanWrite(FMapBlock.Cells[i].X,
555
      FMapBlock.Cells[i].Y);
556
  end;
557

  
558
  if FStaticBlock is TSeperatedStaticBlock then
559
    TSeperatedStaticBlock(FStaticBlock).RebuildList; //fill items
560

  
561
  for i := 0 to FStaticBlock.Items.Count - 1 do
562
  begin
563
    staticItem := FStaticBlock.Items[i];
564
    staticItem.CanBeEdited := ALandscape.CanWrite(staticItem.X,
565
      staticItem.Y);
566
  end;
567
end;
568

  
569
{ TLandscape }
570

  
571
constructor TLandscape.Create(AWidth, AHeight: Word);
572
var
573
  i: Integer;
574
begin
575
  inherited Create;
576
  FWidth := AWidth;
577
  FHeight := AHeight;
578
  FCellWidth := FWidth * 8;
579
  FCellHeight := FHeight * 8;
580
  FBlockCache := TBlockCache.Create(256);
581
  FBlockCache.OnRemoveObject := @OnRemoveCachedObject;
582

  
583
  FOnChange := nil;
584
  FOnNewBlock := nil;
585
  FOnStaticDeleted := nil;
586
  FOnStaticElevated := nil;
587
  FOnStaticHued := nil;
588
  FOnStaticInserted := nil;
589

  
590
  FOpenRequests := TBits.Create(FWidth * FHeight);
591
  FOpenRequests.Clearall; //set all to 0
592
  FWriteMap := TBits.Create(FCellWidth * FCellHeight);
593
  for i := 0 to FWriteMap.Size - 1 do
594
    FWriteMap[i] := True;
595

  
596
  RegisterPacketHandler($04, TPacketHandler.Create(0, @OnBlocksPacket));
597
  RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket));
598
  RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket));
599
  RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket));
600
  RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket));
601
  RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket));
602
  RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket));
603
end;
604

  
605
destructor TLandscape.Destroy;
606
begin
607
  if FBlockCache <> nil then
608
  begin
609
    FBlockCache.OnRemoveObject := nil;
610
    FreeAndNil(FBlockCache);
611
  end;
612

  
613
  FreeAndNil(FOpenRequests);
614
  FreeAndNil(FWriteMap);
615
  
616
  RegisterPacketHandler($04, nil);
617
  RegisterPacketHandler($06, nil);
618
  RegisterPacketHandler($07, nil);
619
  RegisterPacketHandler($08, nil);
620
  RegisterPacketHandler($09, nil);
621
  RegisterPacketHandler($0A, nil);
622
  RegisterPacketHandler($0B, nil);
623
  
624
  inherited Destroy;
625
end;
626

  
627
function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock;
628
var
629
  block: TBlock;
630
begin
631
  Result := nil;
632
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
633
  begin
634
    if FBlockCache.QueryID(GetID(AX, AY), block) then
635
      Result := block.Map;
636
  end;
637
end;
638

  
639
function TLandscape.GetMapCell(AX, AY: Word): TMapCell;
640
var
641
  block: TMapBlock;
642
begin
643
  Result := nil;
644
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
645
  begin
646
    block := GetMapBlock(AX div 8, AY div 8);
647
    if block <> nil then
648
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
649
  end;
650
end;
651

  
652
function TLandscape.GetNormals(AX, AY: Word): TNormals;
653
begin
654
  GetNormals(AX, AY, Result);
655
end;
656

  
657
function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
658
var
659
  block: TBlock;
660
begin
661
  Result := nil;
662
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
663
  begin
664
    if FBlockCache.QueryID(GetID(AX, AY), block) then
665
      Result := TSeperatedStaticBlock(block.Static);
666
  end;
667
end;
668

  
669
function TLandscape.GetStaticList(AX, AY: Word): TStaticItemList;
670
var
671
  block: TSeperatedStaticBlock;
672
begin
673
  Result := nil;
674
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
675
  begin
676
    block := GetStaticBlock(AX div 8, AY div 8);
677
    if block <> nil then
678
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
679
  end;
680
end;
681

  
682
procedure TLandscape.OnRemoveCachedObject(ABlock: TBlock);
683
begin
684
  if ABlock <> nil then
685
    dmNetwork.Send(TFreeBlockPacket.Create(ABlock.Map.X, ABlock.Map.Y));
686
end;
687

  
688
procedure TLandscape.OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
689
var
690
  index: TGenericIndex;
691
  map: TMapBlock;
692
  statics: TStaticBlock;
693
  coords: TBlockCoords;
694
  count: Word;
695
  id: Integer;
696
  block: TBlock;
697
begin
698
  index := TGenericIndex.Create(nil);
699
  while ABuffer.Position < ABuffer.Size do
700
  begin
701
    ABuffer.Read(coords, SizeOf(TBlockCoords));
702
    id := GetID(coords.X, coords.Y);
703

  
704
    map := TMapBlock.Create(ABuffer, coords.X, coords.Y);
705
    count := ABuffer.ReadWord;
706
    if count > 0 then
707
      index.Lookup := ABuffer.Position
708
    else
709
      index.Lookup := -1;
710
    index.Size := count * 7;
711
    statics := TSeperatedStaticBlock.Create(ABuffer, index, coords.X, coords.Y);
712

  
713
    FBlockCache.RemoveID(id);
714
    block := TBlock.Create(map, statics);
715
    block.UpdateBlockAcess(Self);
716
    FBlockCache.StoreID(id, block);
717

  
718
    FOpenRequests[coords.Y * FWidth + coords.X] := False;
719

  
720
    if Assigned(FOnNewBlock) then FOnNewBlock(block);
721
  end;
722
  index.Free;
723
end;
724

  
725
procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
726
var
727
  x, y: Word;
728
  cell: TMapCell;
729
begin
730
  x := ABuffer.ReadWord;
731
  y := ABuffer.ReadWord;
732
  cell := GetMapCell(x, y);
733
  if cell <> nil then
734
  begin
735
    cell.Altitude := ABuffer.ReadShortInt;
736
    cell.TileID := ABuffer.ReadWord;
737
    if Assigned(FOnMapChanged) then FOnMapChanged(cell);
738
  end;
739
end;
740

  
741
procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
742
var
743
  x, y: Word;
744
  block: TSeperatedStaticBlock;
745
  staticItem: TStaticItem;
746
  targetStaticList: TStaticItemList;
747
  i: Integer;
748
begin
749
  x := ABuffer.ReadWord;
750
  y := ABuffer.ReadWord;
751
  block := GetStaticBlock(x div 8, y div 8);
752
  if block <> nil then
753
  begin
754
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
755
    staticItem.X := x;
756
    staticItem.Y := y;
757
    staticItem.Z := ABuffer.ReadShortInt;
758
    staticItem.TileID := ABuffer.ReadWord;
759
    staticItem.Hue := ABuffer.ReadWord;
760
    targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8];
761
    targetStaticList.Add(staticItem);
762
    for i := 0 to targetStaticList.Count - 1 do
763
      targetStaticList.Items[i].UpdatePriorities(
764
        ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
765
        i);
766
    targetStaticList.Sort(@CompareStaticItems);
767
    staticItem.Owner := block;
768
    staticItem.CanBeEdited := CanWrite(x, y);
769

  
770
    if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
771
  end;
772
end;
773

  
774
procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
775
var
776
  block: TSeperatedStaticBlock;
777
  i: Integer;
778
  statics: TStaticItemList;
779
  staticInfo: TStaticInfo;
780
  staticItem: TStaticItem;
781
begin
782
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
783
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
784
  if block <> nil then
785
  begin
786
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
787
    for i := 0 to statics.Count - 1 do
788
    begin
789
      staticItem := statics.Items[i];
790
      if (staticItem.Z = staticInfo.Z) and
791
         (staticItem.TileID = staticInfo.TileID) and
792
         (staticItem.Hue = staticInfo.Hue) then
793
      begin
794
        if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
795
        staticItem.Delete;
796
        statics.Delete(i);
797

  
798
        Break;
799
      end;
800
    end;
801
  end;
802
end;
803

  
804
procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
805
var
806
  block: TSeperatedStaticBlock;
807
  i,j : Integer;
808
  statics: TStaticItemList;
809
  staticInfo: TStaticInfo;
810
  staticItem: TStaticItem;
811
begin
812
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
813
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
814
  if block <> nil then
815
  begin
816
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
817
    for i := 0 to statics.Count - 1 do
818
    begin
819
      staticItem := statics.Items[i];
820
      if (staticItem.Z = staticInfo.Z) and
821
         (staticItem.TileID = staticInfo.TileID) and
822
         (staticItem.Hue = staticInfo.Hue) then
823
      begin
824
        staticItem.Z := ABuffer.ReadShortInt;
825
        for j := 0 to statics.Count - 1 do
826
          statics.Items[j].UpdatePriorities(
827
            ResMan.Tiledata.StaticTiles[statics.Items[j].TileID],
828
            j);
829
        statics.Sort(@CompareStaticItems);
830

  
831
        if Assigned(FOnStaticElevated) then FOnStaticElevated(staticItem);
832

  
833
        Break;
834
      end;
835
    end;
836
  end;
837
end;
838

  
839
procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
840
var
841
  sourceBlock, targetBlock: TSeperatedStaticBlock;
842
  i: Integer;
843
  statics: TStaticItemList;
844
  staticInfo: TStaticInfo;
845
  staticItem: TStaticItem;
846
  newX, newY: Word;
847
begin
848
  staticItem := nil;
849
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
850
  newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1);
851
  newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1);
852

  
853
  sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
854
  targetBlock := GetStaticBlock(newX div 8, newY div 8);
855
  if sourceBlock <> nil then
856
  begin
857
    statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
858
    i := 0;
859
    while (i < statics.Count) and (staticItem = nil) do
860
    begin
861
      staticItem := statics.Items[i];
862
      if (staticItem.Z <> staticInfo.Z) or
863
         (staticItem.TileID <> staticInfo.TileID) or
864
         (staticItem.Hue <> staticInfo.Hue) then
865
      begin
866
        staticItem := nil;
867
      end;
868
      Inc(i);
869
    end;
870

  
871
    if staticItem <> nil then
872
    begin
873
      if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
874
      staticItem.Delete;
875
      statics.Remove(staticItem);
876
    end;
877
  end;
878

  
879
  if targetBlock <> nil then
880
  begin
881
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
882
    staticItem.X := newX;
883
    staticItem.Y := newY;
884
    staticItem.Z := staticInfo.Z;
885
    staticItem.TileID := staticInfo.TileID;
886
    staticItem.Hue := staticInfo.Hue;
887
    statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8];
888
    statics.Add(staticItem);
889
    for i := 0 to statics.Count - 1 do
890
      TStaticItem(statics.Items[i]).UpdatePriorities(
891
        ResMan.Tiledata.StaticTiles[TStaticItem(statics.Items[i]).TileID],
892
        i);
893
    statics.Sort(@CompareStaticItems);
894
    staticItem.Owner := targetBlock;
895
    staticItem.CanBeEdited := CanWrite(newX, newY);
896

  
897
    if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
898
  end;
899
end;
900

  
901
procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
902
var
903
  block: TSeperatedStaticBlock;
904
  i : Integer;
905
  statics: TStaticItemList;
906
  staticInfo: TStaticInfo;
907
  staticItem: TStaticItem;
908
begin
909
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
910
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
911
  if block <> nil then
912
  begin
913
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
914
    for i := 0 to statics.Count - 1 do
915
    begin
916
      staticItem := statics.Items[i];
917
      if (staticItem.Z = staticInfo.Z) and
918
         (staticItem.TileID = staticInfo.TileID) and
919
         (staticItem.Hue = staticInfo.Hue) then
920
      begin
921
        staticItem.Hue := ABuffer.ReadWord;
922
        if Assigned(FOnStaticHued) then FOnStaticHued(staticItem);
923
        Break;
924
      end;
925
    end;
926
  end;
927
end;
928

  
929
function TLandscape.CanWrite(AX, AY: Word): Boolean;
930
begin
931
  Result := FWriteMap[AX * FCellHeight + AY];
932
end;
933

  
934
procedure TLandscape.FillDrawList(ADrawList: TScreenBuffer; AX, AY, AWidth,
935
  AHeight: Word; AMap, AStatics: Boolean; ANoDraw: Boolean;
936
  AAdditionalTiles: TWorldItemList = nil);
937
var
938
  drawMapCell: TMapCell;
939
  drawStatics: TStaticItemList;
940
  i, x, y: Integer;
941
  tempDrawList: TWorldItemList;
942
begin
943
  ADrawList.Clear;
944
  tempDrawList := TWorldItemList.Create(False);;
945
  for x := AX to AX + AWidth do
946
  begin
947
    for y := AY to AY + AWidth do
948
    begin
949
      if AMap then
950
      begin
951
        drawMapCell := GetMapCell(x, y);
952
        if (drawMapCell <> nil) and (ANoDraw or (drawMapCell.TileID > 2)) then
953
        begin
954
          drawMapCell.Priority := GetEffectiveAltitude(drawMapCell);
955
          drawMapCell.PriorityBonus := 0;
956
          drawMapCell.PrioritySolver := 0;
957
          tempDrawList.Add(drawMapCell);
958
        end;
959
      end;
960

  
961
      if AStatics then
962
      begin
963
        drawStatics := GetStaticList(x, y);
964
        if drawStatics <> nil then
965
          for i := 0 to drawStatics.Count - 1 do
966
          begin
967
            drawStatics[i].UpdatePriorities(
968
              ResMan.Tiledata.StaticTiles[drawStatics[i].TileID],
969
              ADrawList.GetSerial);
970
            tempDrawList.Add(drawStatics[i]);
971
          end;
972
      end;
973
    end;
974
  end;
975

  
976
  for i := 0 to AAdditionalTiles.Count - 1 do
977
    tempDrawList.Add(AAdditionalTiles[i]);
978

  
979
  tempDrawList.Sort(@CompareWorldItems);
980
  for i := 0 to tempDrawList.Count - 1 do
981
    ADrawList.Add(TWorldItem(tempDrawList[i]));
982
  tempDrawList.Free;
983
end;
984

  
985
function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt;
986
var
987
  north, west, south, east: ShortInt;
988
begin
989
  north := ATile.Altitude;
990
  west := GetLandAlt(ATile.X, ATile.Y + 1, north);
991
  south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north);
992
  east := GetLandAlt(ATile.X + 1, ATile.Y, north);
993

  
994
  if Abs(north - south) >= Abs(west - east) then
995
    Result := Min(north, south) + Abs(west - east) div 2
996
  else
997
    Result := Min(north, south) + Abs(north - south) div 2;
998
end;
999

  
1000
function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
1001
var
1002
  cell: TMapCell;
1003
begin
1004
  cell := MapCell[AX, AY];
1005
  if cell <> nil then
1006
    Result := cell.Altitude
1007
  else
1008
    Result := ADefault;
1009
end;
1010

  
1011
procedure TLandscape.GetNormals(AX, AY: Word; var ANormals: TNormals);
1012
var
1013
  cells: array[0..2, 0..2] of TNormals;
1014
  north, west, south, east: TVector;
1015
  i, j: Integer;
1016

  
1017
  function GetPlainNormals(X, Y: SmallInt): TNormals;
1018
  var
1019
    cell: TMapCell;
1020
    north, west, south, east: ShortInt;
1021
    u, v: TVector;
1022
  begin
1023
    cell := GetMapCell(X, Y);
1024
    if cell <> nil then
1025
    begin
1026
      north := cell.Altitude;
1027
      west := GetLandAlt(cell.X, cell.Y + 1, north);
1028
      south := GetLandAlt(cell.X + 1, cell.Y + 1, north);
1029
      east := GetLandAlt(cell.X + 1, cell.Y, north);
1030
    end else
1031
    begin
1032
      north := 0;
1033
      west := 0;
1034
      east := 0;
1035
      south := 0;
1036
    end;
1037

  
1038
    if (north = west) and (west = east) and (north = south) then
1039
    begin
1040
      Result[0] := Vector(0, 0, 1);
1041
      Result[1] := Vector(0, 0, 1);
1042
      Result[2] := Vector(0, 0, 1);
1043
      Result[3] := Vector(0, 0, 1);
1044
    end else
1045
    begin
1046
      u := Vector(-22, 22, (north - east) * 4);
1047
      v := Vector(-22, -22, (west - north) * 4);
1048
      Result[0] := VectorNorm(VectorCross(u, v));
1049

  
1050
      u := Vector(22, 22, (east - south) * 4);
1051
      v := Vector(-22, 22, (north - east) * 4);
1052
      Result[1] := VectorNorm(VectorCross(u, v));
1053

  
1054
      u := Vector(22, -22, (south - west) * 4);
1055
      v := Vector(22, 22, (east - south) * 4);
1056
      Result[2] := VectorNorm(VectorCross(u, v));
1057

  
1058
      u := Vector(-22, -22, (west - north) * 4);
1059
      v := Vector(22, -22, (south - west) * 4);
1060
      Result[3] := VectorNorm(VectorCross(u, v));
1061
    end;
1062
  end;
1063
begin
1064
  for i := 0 to 2 do
1065
    for j := 0 to 2 do
1066
      cells[i, j] := GetPlainNormals(AX - 1 + i, AY - 1 + j);
1067

  
1068
  north := cells[0, 0][2];
1069
  west := cells[0, 1][1];
1070
  east := cells[1, 0][3];
1071
  south := cells[1, 1][0];
1072
  ANormals[0] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
1073

  
1074
  north := cells[1, 0][2];
1075
  west := cells[1, 1][1];
1076
  east := cells[2, 0][3];
1077
  south := cells[2, 1][0];
1078
  ANormals[1] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
1079

  
1080
  north := cells[1, 1][2];
1081
  west := cells[1, 2][1];
1082
  east := cells[2, 1][3];
1083
  south := cells[2, 2][0];
1084
  ANormals[2] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
1085

  
1086
  north := cells[0, 1][2];
1087
  west := cells[0, 2][1];
1088
  east := cells[1, 1][3];
1089
  south := cells[1, 2][0];
1090
  ANormals[3] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
1091
end;
1092

  
1093
procedure TLandscape.MoveStatic(AStatic: TStaticItem; AX, AY: Word);
1094
var
1095
  sourceBlock, targetBlock: TSeperatedStaticBlock;
1096
  targetStaticList: TStaticItemList;
1097
  i: Integer;
1098
begin
1099
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
1100
  begin
1101
    sourceBlock := AStatic.Owner as TSeperatedStaticBlock;
1102
    targetBlock := GetStaticBlock(AX div 8, AY div 8);
1103
    if (sourceBlock <> nil) and (targetBlock <> nil) then
1104
    begin
1105
      sourceBlock.Cells[(AStatic.Y mod 8) * 8 + AStatic.X mod 8].Remove(AStatic);
1106
      targetStaticList := targetBlock.Cells[(AY mod 8) * 8 + AX mod 8];
1107
      targetStaticList.Add(AStatic);
1108
      for i := 0 to targetStaticList.Count - 1 do
1109
        targetStaticList.Items[i].UpdatePriorities(
1110
          ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
1111
          i);
1112
      targetStaticList.Sort(@CompareStaticItems);
1113
      AStatic.UpdatePos(AX, AY, AStatic.Z);
1114
      AStatic.Owner := targetBlock;
1115
    end;
1116
  end;
1117
end;
1118

  
1119
procedure TLandscape.PrepareBlocks(AX1, AY1, AX2, AY2: Word);
1120
var
1121
  x, y, i: Integer;
1122
  coords: TBlockCoordsArray;
1123
  block: TBlock;
1124
begin
1125
  AX1 := EnsureRange(AX1, 0, FWidth - 1);
1126
  AY1 := EnsureRange(AY1, 0, FHeight - 1);
1127
  AX2 := EnsureRange(AX2, 0, FWidth - 1);
1128
  AY2 := EnsureRange(AY2, 0, FHeight - 1);
1129

  
1130
  SetLength(coords, 0);
1131
  for x := AX1 to AX2 do
1132
  begin
1133
    for y := AY1 to AY2 do
1134
    begin
1135
      if (not FOpenRequests[y * FWidth + x]) and
1136
         (not FBlockCache.QueryID(GetID(x, y), block)) then
1137
      begin
1138
        SetLength(coords, Length(coords) + 1);
1139
        i := High(coords);
1140
        coords[i].X := x;
1141
        coords[i].Y := y;
1142
        FOpenRequests[y * FWidth + x] := True;
1143
      end;
1144
    end;
1145
  end;
1146
  if Length(coords) > 0 then
1147
    dmNetwork.Send(TRequestBlocksPacket.Create(coords));
1148
end;
1149

  
1150
procedure TLandscape.UpdateBlockAccess;
1151
var
1152
  cacheEntry: TBlockCache.PCacheEntry;
1153
begin
1154
  cacheEntry := nil;
1155
  while FBlockCache.Iterate(cacheEntry) do
1156
    if cacheEntry^.Obj <> nil then
1157
      cacheEntry^.Obj.UpdateBlockAcess(Self);
1158
end;
1159

  
1160
procedure TLandscape.UpdateWriteMap(AStream: TEnhancedMemoryStream);
1161
var
1162
  x1, y1, x2, y2: Word;
1163
  i, areaCount, cellX, cellY: Integer;
1164
begin
1165
  Logger.EnterMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
1166

  
1167
  areaCount := AStream.ReadWord;
1168
  Logger.Send([lcLandscape, lcDebug], 'AreaCount', areaCount);
1169

  
1170
  if areaCount > 0 then
1171
  begin
1172
    FWriteMap.Clearall;
1173
    for i := 0 to areaCount - 1 do
1174
    begin
1175
      x1 := AStream.ReadWord;
1176
      y1 := AStream.ReadWord;
1177
      x2 := AStream.ReadWord;
1178
      y2 := AStream.ReadWord;
1179
      for cellX := x1 to x2 do
1180
        for cellY := y1 to y2 do
1181
          FWriteMap[cellX * FCellHeight + cellY] := True;
1182
    end;
1183
  end else
1184
    for i := 0 to FWriteMap.Size - 1 do
1185
      FWriteMap[i] := True;
1186

  
1187
  Logger.Send([lcLandscape, lcDebug], 'WriteMap @ 0,0', FWriteMap[0]);
1188

  
1189
  UpdateBlockAccess;
1190
  Logger.ExitMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
1191
end;
1192

  
1193
{ TMaterial }
1194

  
1195
constructor TMaterial.Create;
1196
begin
1197
  FRefCount := 1;
1198
end;
1199

  
1200
destructor TMaterial.Destroy;
1201
begin
1202
  FreeAndNil(FGraphic);
1203
  inherited Destroy;
1204
end;
1205

  
1206
procedure TMaterial.CalculateTextureDimensions(ACaps: TGLTextureCaps;
1207
  ARealWidth, ARealHeight: Integer; out AWidth, AHeight: Integer);
1208
begin
1209
  if ACaps.NonPowerOfTwo then
1210
  begin
1211
    AWidth := ARealWidth;
1212
    AHeight := ARealHeight;
1213
  end else
1214
  begin
1215
    if IsPow2(ARealWidth) then
1216
      AWidth := ARealWidth
1217
    else
1218
      AWidth := NextPow2(ARealWidth);
1219

  
1220
    if IsPow2(ARealHeight) then
1221
      AHeight := ARealHeight
1222
    else
1223
      AHeight := NextPow2(ARealHeight);
1224
  end;
1225
end;
1226

  
1227
function TMaterial.GenerateTexture(AImage: TBaseImage): TGLuint;
1228
begin
1229
  Result := CreateGLTextureFromImage(AImage.ImageDataPointer^);
1230
  glBindTexture(GL_TEXTURE_2D, Result);
1231
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1232
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1233
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1234
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1235
end;
1236

  
1237
procedure TMaterial.AddRef;
1238
begin
1239
  Inc(FRefCount);
1240
end;
1241

  
1242
procedure TMaterial.DelRef;
1243
begin
1244
  Dec(FRefCount);
1245
  if FRefCount < 1 then
1246
    Free;
1247
end;
1248

  
1249
function TMaterial.HitTest(AX, AY: Integer): Boolean;
1250
var
1251
  pixel: TColor32Rec;
1252
begin
1253
  Result := False;
1254
  if InRange(AX, 0, FGraphic.Width - 1) and
1255
     InRange(AY, 0, FGraphic.Height - 1) then
1256
  begin
1257
    pixel := GetPixel32(FGraphic.ImageDataPointer^, AX, AY);
1258
    if pixel.A > 0 then
1259
      Result := True;
1260
  end;
1261
end;
1262

  
1263
function TMaterial.CanBeRemoved: Boolean;
1264
begin
1265
  Result := FRefCount <= 1;
1266
end;
1267

  
1268
procedure TMaterial.RemoveFromCache;
1269
begin
1270
  DelRef;
1271
end;
1272

  
1273
{ TScreenBuffer }
1274

  
1275
constructor TScreenBuffer.Create;
1276
begin
1277
  inherited Create;
1278
  FCount := 0;
1279
  FSerial := 0;
1280
  UpdateShortcuts;
1281
end;
1282

  
1283
destructor TScreenBuffer.Destroy;
1284
begin
1285
  Clear;
1286
  inherited Destroy;
1287
end;
1288

  
1289
function TScreenBuffer.Add(AItem: TWorldItem): PBlockInfo;
1290
begin
1291
  New(Result);
1292
  AItem.Locked := True;
1293
  AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
1294
  Result^.Item := AItem;
1295
  Result^.HighRes := nil;
1296
  Result^.LowRes := nil;
1297
  Result^.Normals := nil;
1298
  Result^.State := ssNormal;
1299
  Result^.Highlighted := False;
1300
  Result^.Translucent := False;
1301
  Result^.Text := nil;
1302
  Result^.Next := nil;
1303

  
1304
  if FShortCuts[0] = nil then //First element
1305
  begin
1306
    FShortCuts[0] := Result;
1307
    FShortCuts[-1] := Result; //Last element
1308
  end else
1309
  begin
1310
    FShortCuts[-1]^.Next := Result;
1311
    FShortCuts[-1] := Result;
1312
  end;
1313

  
1314
  Inc(FCount);
1315
end;
1316

  
1317
procedure TScreenBuffer.Clear;
1318
var
1319
  current, next: PBlockInfo;
1320
begin
1321
  current := FShortCuts[0];
1322
  while current <> nil do
1323
  begin
1324
    next := current^.Next;
1325
    current^.Item.Locked := False;
1326
    current^.Item.OnDestroy.UnregisterEvent(@OnTileRemoved);
1327
    if current^.Normals <> nil then Dispose(current^.Normals);
1328
    if current^.HighRes <> nil then current^.HighRes.DelRef;
1329
    if current^.LowRes <> nil then current^.LowRes.DelRef;
1330
    current^.Text.Free;
1331
    Dispose(current);
1332
    current := next;
1333
  end;
1334
  FShortCuts[0] := nil;
1335
  FShortCuts[-1] := nil;
1336

  
1337
  FCount := 0;
1338
  FSerial := 0;
1339

  
1340
  UpdateShortcuts;
1341
end;
1342

  
1343
procedure TScreenBuffer.Delete(AItem: TWorldItem);
1344
var
1345
  current, last, next: PBlockInfo;
1346
begin
1347
  last := nil;
1348
  current := FShortCuts[0];
1349
  while current <> nil do
1350
  begin
1351
    if current^.Item = AItem then
1352
    begin
1353
      if FShortCuts[-1] = current then FShortCuts[-1] := last;
1354
      if FShortCuts[0] = current then FShortCuts[0] := current^.Next;
1355
      if last <> nil then last^.Next := current^.Next;
1356

  
1357
      if current^.Normals <> nil then Dispose(current^.Normals);
1358
      if current^.HighRes <> nil then current^.HighRes.DelRef;
1359
      if current^.LowRes <> nil then current^.LowRes.DelRef;
1360
      current^.Text.Free;
1361

  
1362
      Dispose(current);
1363
      Dec(FCount);
1364
      FShortCutsValid := False;
1365
      next := nil;
1366
    end else
1367
      next := current^.Next;
1368

  
1369
    last := current;
1370
    current := next;
1371
  end;
1372
end;
1373

  
1374
function TScreenBuffer.Find(AScreenPosition: TPoint): PBlockInfo;
1375
var
1376
  current: PBlockInfo;
1377
  buff: array[0..3] of GLuint;
1378
  hits: GLint;
1379
begin
1380
  Result := nil;
1381
  current := FShortCuts[0];
1382
  while current <> nil do //search the last matching tile
1383
  begin
1384
    if (current^.State = ssNormal) and
1385
       PtInRect(current^.ScreenRect, AScreenPosition)then
1386
    begin
1387
      if current^.CheckRealQuad then
1388
      begin
1389
        //OpenGL hit test
1390
        //We use the "real quad" here to prevent the draw-preview from
1391
        //intercepting with our actual tiles (which are "hidden" then).
1392
        glSelectBuffer(4, @buff[0]);
1393
        glViewport(current^.ScreenRect.Left, current^.ScreenRect.Top,
1394
          current^.ScreenRect.Right, current^.ScreenRect.Bottom);
1395
        glRenderMode(GL_SELECT);
1396
        glInitNames;
1397
        glPushName(0);
1398

  
1399
        glPushMatrix;
1400
          glMatrixMode(GL_PROJECTION);
1401
          glLoadIdentity;
1402
          gluOrtho2D(AScreenPosition.x, AScreenPosition.x + 1,
1403
            AScreenPosition.y + 1, AScreenPosition.y);
1404
          glMatrixMode(GL_MODELVIEW);
1405
          glLoadIdentity;
1406

  
1407
          glBegin(GL_QUADS);
1408
            glVertex2iv(@current^.RealQuad[0]);
1409
            glVertex2iv(@current^.RealQuad[3]);
1410
            glVertex2iv(@current^.RealQuad[2]);
1411
            glVertex2iv(@current^.RealQuad[1]);
1412
          glEnd;
1413
        glPopMatrix;
1414
        glFlush;
1415

  
1416
        if glRenderMode(GL_RENDER) > 0 then //glRenderMode now returns the number of hits
1417
          Result := current;
1418
      end else
1419
      if current^.LowRes.HitTest(AScreenPosition.x - current^.ScreenRect.Left,
1420
         AScreenPosition.y - current^.ScreenRect.Top) then
1421
        Result := current;
1422
    end;
1423
    current := current^.Next;
1424
  end;
1425
end;
1426

  
1427
function TScreenBuffer.GetSerial: Cardinal;
1428
begin
1429
  Result := FSerial;
1430
  Inc(FSerial);
1431
end;
1432

  
1433
function TScreenBuffer.Insert(AItem: TWorldItem): PBlockInfo;
1434
var
1435
  current: PBlockInfo;
1436
  shortcut: Integer;
1437
begin
1438
  if not FShortCutsValid then
1439
    UpdateShortcuts;
1440

  
1441
  New(Result);
1442
  AItem.Locked := True;
1443
  AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
1444
  Result^.Item := AItem;
1445
  Result^.HighRes := nil;
1446
  Result^.LowRes := nil;
1447
  Result^.Normals := nil;
1448
  Result^.State := ssNormal;
1449
  Result^.Highlighted := False;
1450
  Result^.Translucent := False;
1451
  Result^.Text := nil;
1452

  
1453
  if (FShortCuts[0] = nil) or (CompareWorldItems(AItem, FShortCuts[0]^.Item) < 0) then
1454
  begin
1455
    if FShortCuts[0] = nil then
1456
      FShortCuts[-1] := Result;  //Update last item
1457

  
1458
    Result^.Next := FShortCuts[0];
1459
    FShortCuts[0] := Result;
1460
  end else
1461
  begin
1462
    //find best entry point
1463
    shortcut := 0;
1464
    while (shortcut <= 10) and (FShortCuts[shortcut] <> nil) and
1465
      (CompareWorldItems(AItem, FShortCuts[shortcut]^.Item) >= 0) do
1466
    begin
1467
      current := FShortCuts[shortcut];
1468
      Inc(shortcut);
1469
    end;
1470

  
1471
    //now find the real match
1472
    while (current^.Next <> nil) and
1473
          (CompareWorldItems(AItem, current^.Next^.Item) > 0) do
1474
    begin
1475
      current := current^.Next;
1476
    end;
1477

  
1478
    if FShortCuts[-1] = current^.Next then
1479
      FShortCuts[-1] := Result;  //Update last item
1480

  
1481
    Result^.Next := current^.Next;
1482
    current^.Next := Result;
1483
  end;
1484

  
1485
  Inc(FCount);
1486
end;
1487

  
1488
function TScreenBuffer.Iterate(var ABlockInfo: PBlockInfo): Boolean;
1489
begin
1490
  if ABlockInfo = nil then
1491
    ABlockInfo := FShortCuts[0]
1492
  else
1493
    ABlockInfo := ABlockInfo^.Next;
1494
  Result := ABlockInfo <> nil;
1495
end;
1496

  
1497
procedure TScreenBuffer.UpdateShortcuts;
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff