Statistics
| Branch: | Tag: | Revision:

root / Client / ULandscape.pas @ 119:66352054ce4d

History | View | Annotate | Download (47.1 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 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
  Result := nil;
501
end;
502
503
function TSeperatedStaticBlock.GetSize: Integer;
504
begin
505
  RebuildList;
506
  Result := inherited GetSize;
507
end;
508
509
procedure TSeperatedStaticBlock.RebuildList;
510
var
511
  i, j, solver: Integer;
512
begin
513
  FItems.Clear;
514
  solver := 0;
515
  for i := 0 to 63 do
516
  begin
517
    if Cells[i] <> nil then
518
    begin
519
      for j := 0 to Cells[i].Count - 1 do
520
      begin
521
        FItems.Add(Cells[i].Items[j]);
522
        TStaticItem(Cells[i].Items[j]).UpdatePriorities(
523
          ResMan.Tiledata.StaticTiles[TStaticItem(Cells[i].Items[j]).TileID],
524
          solver);
525
        Inc(solver);
526
      end;
527
    end;
528
  end;
529
  Sort;
530
end;
531
532
{ TBlock }
533
534
constructor TBlock.Create(AMap: TMapBlock; AStatics: TStaticBlock);
535
begin
536
  inherited Create;
537
  FMapBlock := AMap;
538
  FStaticBlock := AStatics;
539
end;
540
541
destructor TBlock.Destroy;
542
begin
543
  if FMapBlock <> nil then FreeAndNil(FMapBlock);
544
  if FStaticBlock <> nil then FreeAndNil(FStaticBlock);
545
  inherited Destroy;
546
end;
547
548
procedure TBlock.UpdateBlockAcess(ALandscape: TLandscape);
549
var
550
  staticItem: TStaticItem;
551
  i: Integer;
552
begin
553
  for i := Low(FMapBlock.Cells) to High(FMapBlock.Cells) do
554
  begin
555
    FMapBlock.Cells[i].CanBeEdited := ALandscape.CanWrite(FMapBlock.Cells[i].X,
556
      FMapBlock.Cells[i].Y);
557
  end;
558
559
  if FStaticBlock is TSeperatedStaticBlock then
560
    TSeperatedStaticBlock(FStaticBlock).RebuildList; //fill items
561
562
  for i := 0 to FStaticBlock.Items.Count - 1 do
563
  begin
564
    staticItem := FStaticBlock.Items[i];
565
    staticItem.CanBeEdited := ALandscape.CanWrite(staticItem.X,
566
      staticItem.Y);
567
  end;
568
end;
569
570
{ TLandscape }
571
572
constructor TLandscape.Create(AWidth, AHeight: Word);
573
var
574
  i: Integer;
575
begin
576
  inherited Create;
577
  FWidth := AWidth;
578
  FHeight := AHeight;
579
  FCellWidth := FWidth * 8;
580
  FCellHeight := FHeight * 8;
581
  FBlockCache := TBlockCache.Create(256);
582
  FBlockCache.OnRemoveObject := @OnRemoveCachedObject;
583
584
  FOnChange := nil;
585
  FOnNewBlock := nil;
586
  FOnStaticDeleted := nil;
587
  FOnStaticElevated := nil;
588
  FOnStaticHued := nil;
589
  FOnStaticInserted := nil;
590
591
  FOpenRequests := TBits.Create(FWidth * FHeight);
592
  FOpenRequests.Clearall; //set all to 0
593
  FWriteMap := TBits.Create(FCellWidth * FCellHeight);
594
  for i := 0 to FWriteMap.Size - 1 do
595
    FWriteMap[i] := True;
596
597
  RegisterPacketHandler($04, TPacketHandler.Create(0, @OnBlocksPacket));
598
  RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket));
599
  RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket));
600
  RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket));
601
  RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket));
602
  RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket));
603
  RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket));
604
end;
605
606
destructor TLandscape.Destroy;
607
begin
608
  if FBlockCache <> nil then
609
  begin
610
    FBlockCache.OnRemoveObject := nil;
611
    FreeAndNil(FBlockCache);
612
  end;
613
614
  FreeAndNil(FOpenRequests);
615
  FreeAndNil(FWriteMap);
616
  
617
  RegisterPacketHandler($04, nil);
618
  RegisterPacketHandler($06, nil);
619
  RegisterPacketHandler($07, nil);
620
  RegisterPacketHandler($08, nil);
621
  RegisterPacketHandler($09, nil);
622
  RegisterPacketHandler($0A, nil);
623
  RegisterPacketHandler($0B, nil);
624
  
625
  inherited Destroy;
626
end;
627
628
function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock;
629
var
630
  block: TBlock;
631
begin
632
  Result := nil;
633
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
634
  begin
635
    if FBlockCache.QueryID(GetID(AX, AY), block) then
636
      Result := block.Map;
637
  end;
638
end;
639
640
function TLandscape.GetMapCell(AX, AY: Word): TMapCell;
641
var
642
  block: TMapBlock;
643
begin
644
  Result := nil;
645
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
646
  begin
647
    block := GetMapBlock(AX div 8, AY div 8);
648
    if block <> nil then
649
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
650
  end;
651
end;
652
653
function TLandscape.GetNormals(AX, AY: Word): TNormals;
654
begin
655
  GetNormals(AX, AY, Result);
656
end;
657
658
function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
659
var
660
  block: TBlock;
661
begin
662
  Result := nil;
663
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
664
  begin
665
    if FBlockCache.QueryID(GetID(AX, AY), block) then
666
      Result := TSeperatedStaticBlock(block.Static);
667
  end;
668
end;
669
670
function TLandscape.GetStaticList(AX, AY: Word): TStaticItemList;
671
var
672
  block: TSeperatedStaticBlock;
673
begin
674
  Result := nil;
675
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
676
  begin
677
    block := GetStaticBlock(AX div 8, AY div 8);
678
    if block <> nil then
679
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
680
  end;
681
end;
682
683
procedure TLandscape.OnRemoveCachedObject(ABlock: TBlock);
684
begin
685
  if ABlock <> nil then
686
    dmNetwork.Send(TFreeBlockPacket.Create(ABlock.Map.X, ABlock.Map.Y));
687
end;
688
689
procedure TLandscape.OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
690
var
691
  index: TGenericIndex;
692
  map: TMapBlock;
693
  statics: TStaticBlock;
694
  coords: TBlockCoords;
695
  count: Word;
696
  id: Integer;
697
  block: TBlock;
698
begin
699
  index := TGenericIndex.Create(nil);
700
  while ABuffer.Position < ABuffer.Size do
701
  begin
702
    ABuffer.Read(coords, SizeOf(TBlockCoords));
703
    id := GetID(coords.X, coords.Y);
704
705
    map := TMapBlock.Create(ABuffer, coords.X, coords.Y);
706
    count := ABuffer.ReadWord;
707
    if count > 0 then
708
      index.Lookup := ABuffer.Position
709
    else
710
      index.Lookup := -1;
711
    index.Size := count * 7;
712
    statics := TSeperatedStaticBlock.Create(ABuffer, index, coords.X, coords.Y);
713
714
    FBlockCache.RemoveID(id);
715
    block := TBlock.Create(map, statics);
716
    block.UpdateBlockAcess(Self);
717
    FBlockCache.StoreID(id, block);
718
719
    FOpenRequests[coords.Y * FWidth + coords.X] := False;
720
721
    if Assigned(FOnNewBlock) then FOnNewBlock(block);
722
  end;
723
  index.Free;
724
end;
725
726
procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
727
var
728
  x, y: Word;
729
  cell: TMapCell;
730
begin
731
  x := ABuffer.ReadWord;
732
  y := ABuffer.ReadWord;
733
  cell := GetMapCell(x, y);
734
  if cell <> nil then
735
  begin
736
    cell.Altitude := ABuffer.ReadShortInt;
737
    cell.TileID := ABuffer.ReadWord;
738
    if Assigned(FOnMapChanged) then FOnMapChanged(cell);
739
  end;
740
end;
741
742
procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
743
var
744
  x, y: Word;
745
  block: TSeperatedStaticBlock;
746
  staticItem: TStaticItem;
747
  targetStaticList: TStaticItemList;
748
  i: Integer;
749
begin
750
  x := ABuffer.ReadWord;
751
  y := ABuffer.ReadWord;
752
  block := GetStaticBlock(x div 8, y div 8);
753
  if block <> nil then
754
  begin
755
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
756
    staticItem.X := x;
757
    staticItem.Y := y;
758
    staticItem.Z := ABuffer.ReadShortInt;
759
    staticItem.TileID := ABuffer.ReadWord;
760
    staticItem.Hue := ABuffer.ReadWord;
761
    targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8];
762
    targetStaticList.Add(staticItem);
763
    for i := 0 to targetStaticList.Count - 1 do
764
      targetStaticList.Items[i].UpdatePriorities(
765
        ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
766
        i);
767
    targetStaticList.Sort(@CompareStaticItems);
768
    staticItem.Owner := block;
769
    staticItem.CanBeEdited := CanWrite(x, y);
770
771
    if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
772
  end;
773
end;
774
775
procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
776
var
777
  block: TSeperatedStaticBlock;
778
  i: Integer;
779
  statics: TStaticItemList;
780
  staticInfo: TStaticInfo;
781
  staticItem: TStaticItem;
782
begin
783
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
784
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
785
  if block <> nil then
786
  begin
787
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
788
    for i := 0 to statics.Count - 1 do
789
    begin
790
      staticItem := statics.Items[i];
791
      if (staticItem.Z = staticInfo.Z) and
792
         (staticItem.TileID = staticInfo.TileID) and
793
         (staticItem.Hue = staticInfo.Hue) then
794
      begin
795
        if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
796
        staticItem.Delete;
797
        statics.Delete(i);
798
799
        Break;
800
      end;
801
    end;
802
  end;
803
end;
804
805
procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
806
var
807
  block: TSeperatedStaticBlock;
808
  i,j : Integer;
809
  statics: TStaticItemList;
810
  staticInfo: TStaticInfo;
811
  staticItem: TStaticItem;
812
begin
813
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
814
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
815
  if block <> nil then
816
  begin
817
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
818
    for i := 0 to statics.Count - 1 do
819
    begin
820
      staticItem := statics.Items[i];
821
      if (staticItem.Z = staticInfo.Z) and
822
         (staticItem.TileID = staticInfo.TileID) and
823
         (staticItem.Hue = staticInfo.Hue) then
824
      begin
825
        staticItem.Z := ABuffer.ReadShortInt;
826
        for j := 0 to statics.Count - 1 do
827
          statics.Items[j].UpdatePriorities(
828
            ResMan.Tiledata.StaticTiles[statics.Items[j].TileID],
829
            j);
830
        statics.Sort(@CompareStaticItems);
831
832
        if Assigned(FOnStaticElevated) then FOnStaticElevated(staticItem);
833
834
        Break;
835
      end;
836
    end;
837
  end;
838
end;
839
840
procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
841
var
842
  sourceBlock, targetBlock: TSeperatedStaticBlock;
843
  i: Integer;
844
  statics: TStaticItemList;
845
  staticInfo: TStaticInfo;
846
  staticItem: TStaticItem;
847
  newX, newY: Word;
848
begin
849
  staticItem := nil;
850
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
851
  newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1);
852
  newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1);
853
854
  sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
855
  targetBlock := GetStaticBlock(newX div 8, newY div 8);
856
  if sourceBlock <> nil then
857
  begin
858
    statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
859
    i := 0;
860
    while (i < statics.Count) and (staticItem = nil) do
861
    begin
862
      staticItem := statics.Items[i];
863
      if (staticItem.Z <> staticInfo.Z) or
864
         (staticItem.TileID <> staticInfo.TileID) or
865
         (staticItem.Hue <> staticInfo.Hue) then
866
      begin
867
        staticItem := nil;
868
      end;
869
      Inc(i);
870
    end;
871
872
    if staticItem <> nil then
873
    begin
874
      if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
875
      staticItem.Delete;
876
      statics.Remove(staticItem);
877
    end;
878
  end;
879
880
  if targetBlock <> nil then
881
  begin
882
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
883
    staticItem.X := newX;
884
    staticItem.Y := newY;
885
    staticItem.Z := staticInfo.Z;
886
    staticItem.TileID := staticInfo.TileID;
887
    staticItem.Hue := staticInfo.Hue;
888
    statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8];
889
    statics.Add(staticItem);
890
    for i := 0 to statics.Count - 1 do
891
      TStaticItem(statics.Items[i]).UpdatePriorities(
892
        ResMan.Tiledata.StaticTiles[TStaticItem(statics.Items[i]).TileID],
893
        i);
894
    statics.Sort(@CompareStaticItems);
895
    staticItem.Owner := targetBlock;
896
    staticItem.CanBeEdited := CanWrite(newX, newY);
897
898
    if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
899
  end;
900
end;
901
902
procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
903
var
904
  block: TSeperatedStaticBlock;
905
  i : Integer;
906
  statics: TStaticItemList;
907
  staticInfo: TStaticInfo;
908
  staticItem: TStaticItem;
909
begin
910
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
911
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
912
  if block <> nil then
913
  begin
914
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
915
    for i := 0 to statics.Count - 1 do
916
    begin
917
      staticItem := statics.Items[i];
918
      if (staticItem.Z = staticInfo.Z) and
919
         (staticItem.TileID = staticInfo.TileID) and
920
         (staticItem.Hue = staticInfo.Hue) then
921
      begin
922
        staticItem.Hue := ABuffer.ReadWord;
923
        if Assigned(FOnStaticHued) then FOnStaticHued(staticItem);
924
        Break;
925
      end;
926
    end;
927
  end;
928
end;
929
930
function TLandscape.CanWrite(AX, AY: Word): Boolean;
931
begin
932
  Result := FWriteMap[AX * FCellHeight + AY];
933
end;
934
935
procedure TLandscape.FillDrawList(ADrawList: TScreenBuffer; AX, AY, AWidth,
936
  AHeight: Word; AMap, AStatics: Boolean; ANoDraw: Boolean;
937
  AAdditionalTiles: TWorldItemList = nil);
938
var
939
  drawMapCell: TMapCell;
940
  drawStatics: TStaticItemList;
941
  i, x, y: Integer;
942
  tempDrawList: TWorldItemList;
943
begin
944
  ADrawList.Clear;
945
  tempDrawList := TWorldItemList.Create(False);;
946
  for x := AX to AX + AWidth do
947
  begin
948
    for y := AY to AY + AWidth do
949
    begin
950
      if AMap then
951
      begin
952
        drawMapCell := GetMapCell(x, y);
953
        if (drawMapCell <> nil) and (ANoDraw or (drawMapCell.TileID > 2)) then
954
        begin
955
          drawMapCell.Priority := GetEffectiveAltitude(drawMapCell);
956
          drawMapCell.PriorityBonus := 0;
957
          drawMapCell.PrioritySolver := 0;
958
          tempDrawList.Add(drawMapCell);
959
        end;
960
      end;
961
962
      if AStatics then
963
      begin
964
        drawStatics := GetStaticList(x, y);
965
        if drawStatics <> nil then
966
          for i := 0 to drawStatics.Count - 1 do
967
          begin
968
            drawStatics[i].UpdatePriorities(
969
              ResMan.Tiledata.StaticTiles[drawStatics[i].TileID],
970
              ADrawList.GetSerial);
971
            tempDrawList.Add(drawStatics[i]);
972
          end;
973
      end;
974
    end;
975
  end;
976
977
  for i := 0 to AAdditionalTiles.Count - 1 do
978
    tempDrawList.Add(AAdditionalTiles[i]);
979
980
  tempDrawList.Sort(@CompareWorldItems);
981
  for i := 0 to tempDrawList.Count - 1 do
982
    ADrawList.Add(TWorldItem(tempDrawList[i]));
983
  tempDrawList.Free;
984
end;
985
986
function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt;
987
var
988
  north, west, south, east: ShortInt;
989
begin
990
  north := ATile.Altitude;
991
  west := GetLandAlt(ATile.X, ATile.Y + 1, north);
992
  south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north);
993
  east := GetLandAlt(ATile.X + 1, ATile.Y, north);
994
995
  if Abs(north - south) >= Abs(west - east) then
996
    Result := Min(north, south) + Abs(west - east) div 2
997
  else
998
    Result := Min(north, south) + Abs(north - south) div 2;
999
end;
1000
1001
function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
1002
var
1003
  cell: TMapCell;
1004
begin
1005
  cell := MapCell[AX, AY];
1006
  if cell <> nil then
1007
    Result := cell.Altitude
1008
  else
1009
    Result := ADefault;
1010
end;
1011
1012
procedure TLandscape.GetNormals(AX, AY: Word; var ANormals: TNormals);
1013
var
1014
  cells: array[0..2, 0..2] of TNormals;
1015
  north, west, south, east: TVector;
1016
  i, j: Integer;
1017
1018
  function GetPlainNormals(X, Y: SmallInt): TNormals;
1019
  var
1020
    cell: TMapCell;
1021
    north, west, south, east: ShortInt;
1022
    u, v: TVector;
1023
  begin
1024
    cell := GetMapCell(X, Y);
1025
    if cell <> nil then
1026
    begin
1027
      north := cell.Altitude;
1028
      west := GetLandAlt(cell.X, cell.Y + 1, north);
1029
      south := GetLandAlt(cell.X + 1, cell.Y + 1, north);
1030
      east := GetLandAlt(cell.X + 1, cell.Y, north);
1031
    end else
1032
    begin
1033
      north := 0;
1034
      west := 0;
1035
      east := 0;
1036
      south := 0;
1037
    end;
1038
1039
    if (north = west) and (west = east) and (north = south) then
1040
    begin
1041
      Result[0] := Vector(0, 0, 1);
1042
      Result[1] := Vector(0, 0, 1);
1043
      Result[2] := Vector(0, 0, 1);
1044
      Result[3] := Vector(0, 0, 1);
1045
    end else
1046
    begin
1047
      u := Vector(-22, 22, (north - east) * 4);
1048
      v := Vector(-22, -22, (west - north) * 4);
1049
      Result[0] := VectorNorm(VectorCross(u, v));
1050
1051
      u := Vector(22, 22, (east - south) * 4);
1052
      v := Vector(-22, 22, (north - east) * 4);
1053
      Result[1] := VectorNorm(VectorCross(u, v));
1054
1055
      u := Vector(22, -22, (south - west) * 4);
1056
      v := Vector(22, 22, (east - south) * 4);
1057
      Result[2] := VectorNorm(VectorCross(u, v));
1058
1059
      u := Vector(-22, -22, (west - north) * 4);
1060
      v := Vector(22, -22, (south - west) * 4);
1061
      Result[3] := VectorNorm(VectorCross(u, v));
1062
    end;
1063
  end;
1064
begin
1065
  for i := 0 to 2 do
1066
    for j := 0 to 2 do
1067
      cells[i, j] := GetPlainNormals(AX - 1 + i, AY - 1 + j);
1068
1069
  north := cells[0, 0][2];
1070
  west := cells[0, 1][1];
1071
  east := cells[1, 0][3];
1072
  south := cells[1, 1][0];
1073
  ANormals[0] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
1074
1075
  north := cells[1, 0][2];
1076
  west := cells[1, 1][1];
1077
  east := cells[2, 0][3];
1078
  south := cells[2, 1][0];
1079
  ANormals[1] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
1080
1081
  north := cells[1, 1][2];
1082
  west := cells[1, 2][1];
1083
  east := cells[2, 1][3];
1084
  south := cells[2, 2][0];
1085
  ANormals[2] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
1086
1087
  north := cells[0, 1][2];
1088
  west := cells[0, 2][1];
1089
  east := cells[1, 1][3];
1090
  south := cells[1, 2][0];
1091
  ANormals[3] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
1092
end;
1093
1094
procedure TLandscape.MoveStatic(AStatic: TStaticItem; AX, AY: Word);
1095
var
1096
  sourceBlock, targetBlock: TSeperatedStaticBlock;
1097
  targetStaticList: TStaticItemList;
1098
  i: Integer;
1099
begin
1100
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
1101
  begin
1102
    sourceBlock := AStatic.Owner as TSeperatedStaticBlock;
1103
    targetBlock := GetStaticBlock(AX div 8, AY div 8);
1104
    if (sourceBlock <> nil) and (targetBlock <> nil) then
1105
    begin
1106
      sourceBlock.Cells[(AStatic.Y mod 8) * 8 + AStatic.X mod 8].Remove(AStatic);
1107
      targetStaticList := targetBlock.Cells[(AY mod 8) * 8 + AX mod 8];
1108
      targetStaticList.Add(AStatic);
1109
      for i := 0 to targetStaticList.Count - 1 do
1110
        targetStaticList.Items[i].UpdatePriorities(
1111
          ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
1112
          i);
1113
      targetStaticList.Sort(@CompareStaticItems);
1114
      AStatic.UpdatePos(AX, AY, AStatic.Z);
1115
      AStatic.Owner := targetBlock;
1116
    end;
1117
  end;
1118
end;
1119
1120
procedure TLandscape.PrepareBlocks(AX1, AY1, AX2, AY2: Word);
1121
var
1122
  x, y, i: Integer;
1123
  coords: TBlockCoordsArray;
1124
  block: TBlock;
1125
begin
1126
  AX1 := EnsureRange(AX1, 0, FWidth - 1);
1127
  AY1 := EnsureRange(AY1, 0, FHeight - 1);
1128
  AX2 := EnsureRange(AX2, 0, FWidth - 1);
1129
  AY2 := EnsureRange(AY2, 0, FHeight - 1);
1130
1131
  SetLength(coords, 0);
1132
  for x := AX1 to AX2 do
1133
  begin
1134
    for y := AY1 to AY2 do
1135
    begin
1136
      if (not FOpenRequests[y * FWidth + x]) and
1137
         (not FBlockCache.QueryID(GetID(x, y), block)) then
1138
      begin
1139
        SetLength(coords, Length(coords) + 1);
1140
        i := High(coords);
1141
        coords[i].X := x;
1142
        coords[i].Y := y;
1143
        FOpenRequests[y * FWidth + x] := True;
1144
      end;
1145
    end;
1146
  end;
1147
  if Length(coords) > 0 then
1148
    dmNetwork.Send(TRequestBlocksPacket.Create(coords));
1149
end;
1150
1151
procedure TLandscape.UpdateBlockAccess;
1152
var
1153
  cacheEntry: TBlockCache.PCacheEntry;
1154
begin
1155
  cacheEntry := nil;
1156
  while FBlockCache.Iterate(cacheEntry) do
1157
    if cacheEntry^.Obj <> nil then
1158
      cacheEntry^.Obj.UpdateBlockAcess(Self);
1159
end;
1160
1161
procedure TLandscape.UpdateWriteMap(AStream: TEnhancedMemoryStream);
1162
var
1163
  x1, y1, x2, y2: Word;
1164
  i, areaCount, cellX, cellY: Integer;
1165
begin
1166
  Logger.EnterMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
1167
1168
  areaCount := AStream.ReadWord;
1169
  Logger.Send([lcLandscape, lcDebug], 'AreaCount', areaCount);
1170
1171
  if areaCount > 0 then
1172
  begin
1173
    FWriteMap.Clearall;
1174
    for i := 0 to areaCount - 1 do
1175
    begin
1176
      x1 := AStream.ReadWord;
1177
      y1 := AStream.ReadWord;
1178
      x2 := AStream.ReadWord;
1179
      y2 := AStream.ReadWord;
1180
      for cellX := x1 to x2 do
1181
        for cellY := y1 to y2 do
1182
          FWriteMap[cellX * FCellHeight + cellY] := True;
1183
    end;
1184
  end else
1185
    for i := 0 to FWriteMap.Size - 1 do
1186
      FWriteMap[i] := True;
1187
1188
  Logger.Send([lcLandscape, lcDebug], 'WriteMap @ 0,0', FWriteMap[0]);
1189
1190
  UpdateBlockAccess;
1191
  Logger.ExitMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
1192
end;
1193
1194
{ TMaterial }
1195
1196
constructor TMaterial.Create;
1197
begin
1198
  FRefCount := 1;
1199
end;
1200
1201
destructor TMaterial.Destroy;
1202
begin
1203
  FreeAndNil(FGraphic);
1204
  inherited Destroy;
1205
end;
1206
1207
procedure TMaterial.CalculateTextureDimensions(ACaps: TGLTextureCaps;
1208
  ARealWidth, ARealHeight: Integer; out AWidth, AHeight: Integer);
1209
begin
1210
  if ACaps.NonPowerOfTwo then
1211
  begin
1212
    AWidth := ARealWidth;
1213
    AHeight := ARealHeight;
1214
  end else
1215
  begin
1216
    if IsPow2(ARealWidth) then
1217
      AWidth := ARealWidth
1218
    else
1219
      AWidth := NextPow2(ARealWidth);
1220
1221
    if IsPow2(ARealHeight) then
1222
      AHeight := ARealHeight
1223
    else
1224
      AHeight := NextPow2(ARealHeight);
1225
  end;
1226
end;
1227
1228
function TMaterial.GenerateTexture(AImage: TBaseImage): TGLuint;
1229
begin
1230
  Result := CreateGLTextureFromImage(AImage.ImageDataPointer^);
1231
  glBindTexture(GL_TEXTURE_2D, Result);
1232
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1233
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1234
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1235
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1236
end;
1237
1238
procedure TMaterial.AddRef;
1239
begin
1240
  Inc(FRefCount);
1241
end;
1242
1243
procedure TMaterial.DelRef;
1244
begin
1245
  Dec(FRefCount);
1246
  if FRefCount < 1 then
1247
    Free;
1248
end;
1249
1250
function TMaterial.HitTest(AX, AY: Integer): Boolean;
1251
var
1252
  pixel: TColor32Rec;
1253
begin
1254
  Result := False;
1255
  if InRange(AX, 0, FGraphic.Width - 1) and
1256
     InRange(AY, 0, FGraphic.Height - 1) then
1257
  begin
1258
    pixel := GetPixel32(FGraphic.ImageDataPointer^, AX, AY);
1259
    if pixel.A > 0 then
1260
      Result := True;
1261
  end;
1262
end;
1263
1264
function TMaterial.CanBeRemoved: Boolean;
1265
begin
1266
  Result := FRefCount <= 1;
1267
end;
1268
1269
procedure TMaterial.RemoveFromCache;
1270
begin
1271
  DelRef;
1272
end;
1273
1274
{ TScreenBuffer }
1275
1276
constructor TScreenBuffer.Create;
1277
begin
1278
  inherited Create;
1279
  FCount := 0;
1280
  FSerial := 0;
1281
  UpdateShortcuts;
1282
end;
1283
1284
destructor TScreenBuffer.Destroy;
1285
begin
1286
  Clear;
1287
  inherited Destroy;
1288
end;
1289
1290
function TScreenBuffer.Add(AItem: TWorldItem): PBlockInfo;
1291
begin
1292
  New(Result);
1293
  AItem.Locked := True;
1294
  AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
1295
  Result^.Item := AItem;
1296
  Result^.HighRes := nil;
1297
  Result^.LowRes := nil;
1298
  Result^.Normals := nil;
1299
  Result^.State := ssNormal;
1300
  Result^.Highlighted := False;
1301
  Result^.Translucent := False;
1302
  Result^.Text := nil;
1303
  Result^.Next := nil;
1304
1305
  if FShortCuts[0] = nil then //First element
1306
  begin
1307
    FShortCuts[0] := Result;
1308
    FShortCuts[-1] := Result; //Last element
1309
  end else
1310
  begin
1311
    FShortCuts[-1]^.Next := Result;
1312
    FShortCuts[-1] := Result;
1313
  end;
1314
1315
  Inc(FCount);
1316
end;
1317
1318
procedure TScreenBuffer.Clear;
1319
var
1320
  current, next: PBlockInfo;
1321
begin
1322
  current := FShortCuts[0];
1323
  while current <> nil do
1324
  begin
1325
    next := current^.Next;
1326
    current^.Item.Locked := False;
1327
    current^.Item.OnDestroy.UnregisterEvent(@OnTileRemoved);
1328
    if current^.Normals <> nil then Dispose(current^.Normals);
1329
    if current^.HighRes <> nil then current^.HighRes.DelRef;
1330
    if current^.LowRes <> nil then current^.LowRes.DelRef;
1331
    current^.Text.Free;
1332
    Dispose(current);
1333
    current := next;
1334
  end;
1335
  FShortCuts[0] := nil;
1336
  FShortCuts[-1] := nil;
1337
1338
  FCount := 0;
1339
  FSerial := 0;
1340
1341
  UpdateShortcuts;
1342
end;
1343
1344
procedure TScreenBuffer.Delete(AItem: TWorldItem);
1345
var
1346
  current, last, next: PBlockInfo;
1347
begin
1348
  last := nil;
1349
  current := FShortCuts[0];
1350
  while current <> nil do
1351
  begin
1352
    if current^.Item = AItem then
1353
    begin
1354
      if FShortCuts[-1] = current then FShortCuts[-1] := last;
1355
      if FShortCuts[0] = current then FShortCuts[0] := current^.Next;
1356
      if last <> nil then last^.Next := current^.Next;
1357
1358
      if current^.Normals <> nil then Dispose(current^.Normals);
1359
      if current^.HighRes <> nil then current^.HighRes.DelRef;
1360
      if current^.LowRes <> nil then current^.LowRes.DelRef;
1361
      current^.Text.Free;
1362
1363
      Dispose(current);
1364
      Dec(FCount);
1365
      FShortCutsValid := False;
1366
      next := nil;
1367
    end else
1368
      next := current^.Next;
1369
1370
    last := current;
1371
    current := next;
1372
  end;
1373
end;
1374
1375
function TScreenBuffer.Find(AScreenPosition: TPoint): PBlockInfo;
1376
var
1377
  current: PBlockInfo;
1378
  buff: array[0..3] of GLuint;
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;
1498
var
1499
  shortcut, step, nextStep, stepSize: Integer;
1500
  blockInfo: PBlockInfo;
1501
begin
1502
  if FCount < 10 then
1503
  begin
1504
    for shortcut := 1 to 10 do
1505
      FShortCuts[shortcut] := nil;
1506
  end
1507
  else if FShortCuts[0] <> nil then
1508
  begin
1509
    stepSize := FCount div 10;
1510
    nextStep := stepSize;
1511
    step := 0;
1512
    shortcut := 1;
1513
    blockInfo := FShortCuts[0];
1514
    repeat
1515
      if step = nextStep then
1516
      begin
1517
        FShortCuts[shortcut] := blockInfo;
1518
        Inc(shortcut);
1519
        Inc(nextStep, stepSize);
1520
      end;
1521
1522
      Inc(step);
1523
1524
      FShortCuts[-1] := blockInfo; //update last known item
1525
      blockInfo := blockInfo^.Next;
1526
    until (blockInfo = nil);
1527
  end;
1528
  FShortCutsValid := True;
1529
end;
1530
1531
function TScreenBuffer.UpdateSortOrder(AItem: TWorldItem): PBlockInfo;
1532
var
1533
  newNodePosition, oldNode, oldNodePrev, current: PBlockInfo;
1534
begin
1535
  newNodePosition := nil;
1536
  oldNode := nil;
1537
  oldNodePrev := nil;
1538
  current := FShortCuts[0];
1539
1540
  while (current <> nil) and ((oldNode = nil) or (newNodePosition = nil)) do
1541
  begin
1542
    if current^.Item = AItem then
1543
      oldNode := current
1544
    else if oldNode = nil then
1545
      oldNodePrev := current;
1546
1547
    if newNodePosition = nil then
1548
    begin
1549
      if (current^.Next = nil) or (CompareWorldItems(AItem, current^.Next^.Item) < 0) then
1550
        newNodePosition := current;
1551
    end;
1552
1553
    current := current^.Next;
1554
  end;
1555
1556
  //oldNode = nil, if the change happend out-of-screen
1557
  if (oldNode <> nil ) and (oldNode <> newNodePosition) then
1558
  begin
1559
    if oldNodePrev <> oldNode then
1560
    begin
1561
      if oldNodePrev = nil then
1562
        FShortCuts[0] := oldNode^.Next
1563
      else
1564
        oldNodePrev^.Next := oldNode^.Next;
1565
    end;
1566
1567
    if (newNodePosition = FShortCuts[0]) and (CompareWorldItems(AItem, FShortCuts[0]^.Item) < 0) then
1568
    begin
1569
      oldNode^.Next := FShortCuts[0];
1570
      FShortCuts[0] := oldNode;
1571
    end else
1572
    begin
1573
      oldNode^.Next := newNodePosition^.Next;
1574
      newNodePosition^.Next := oldNode;
1575
    end;
1576
  end;
1577
1578
  Result := oldNode;
1579
end;
1580
1581
procedure TScreenBuffer.OnTileRemoved(ATile: TMulBlock);
1582
begin
1583
  Delete(TWorldItem(ATile));
1584
end;
1585
1586
{ TGLText }
1587
1588
constructor TGLText.Create(AFont: TGLFont; AText: String);
1589
begin
1590
  FFont := AFont;
1591
  FText := AText;
1592
  FWidth := FFont.GetTextWidth(AText);
1593
  FHeight := FFont.GetTextHeight('A');
1594
end;
1595
1596
procedure TGLText.Render(AScreenRect: TRect);
1597
var
1598
  x, y: Integer;
1599
begin
1600
  y := AScreenRect.Top + (AScreenRect.Bottom - AScreenRect.Top - FHeight) div 2;
1601
  x := AScreenRect.Left + (AScreenRect.Right - AScreenRect.Left - FWidth) div 2;
1602
  FFont.DrawText(x, y, FText);
1603
end;
1604
1605
{ TSimpleMaterial }
1606
1607
constructor TSimpleMaterial.Create(AGraphic: TBaseImage);
1608
var
1609
  caps: TGLTextureCaps;
1610
begin
1611
  inherited Create;
1612
  FRealWidth := AGraphic.Width;
1613
  FRealHeight := AGraphic.Height;
1614
1615
  GetGLTextureCaps(caps);
1616
  CalculateTextureDimensions(caps, FRealWidth, FRealHeight, FWidth, FHeight);
1617
  FGraphic := TMultiImage.CreateFromParams(FWidth, FHeight, ifA8R8G8B8, 1);
1618
  AGraphic.CopyTo(0, 0, FRealWidth, FRealHeight, FGraphic, 0, 0);
1619
  FTexture := GenerateTexture(FGraphic);
1620
end;
1621
1622
destructor TSimpleMaterial.Destroy;
1623
begin
1624
  if FTexture <> 0 then glDeleteTextures(1, @FTexture);
1625
  inherited Destroy;
1626
end;
1627
1628
function TSimpleMaterial.GetTexture: GLuint;
1629
begin
1630
  Result := FTexture;
1631
end;
1632
1633
{ TAnimMaterial }
1634
1635
constructor TAnimMaterial.Create(ABaseID: Word; AAnimData: TAnimData;
1636
  AHue: THue = nil; APartialHue: Boolean = False);
1637
var
1638
  i: Integer;
1639
  art: array of TArt;
1640
  caps: TGLTextureCaps;
1641
begin
1642
  inherited Create;
1643
1644
  FAnimData := AAnimData;
1645
1646
  FRealWidth := 0;
1647
  FRealHeight := 0;
1648
1649
  SetLength(FTextures, AAnimData.FrameCount);
1650
  SetLength(art, AAnimData.FrameCount);
1651
1652
  for i := 0 to AAnimData.FrameCount - 1 do
1653
  begin
1654
    art[i] := ResMan.Art.GetArt(ABaseID + AAnimData.FrameData[i], 0, AHue,
1655
      APartialHue);
1656
1657
    if art[i].Graphic.Width > FRealWidth then
1658
      FRealWidth := art[i].Graphic.Width;
1659
    if art[i].Graphic.Height > FRealHeight then
1660
      FRealHeight := art[i].Graphic.Height;
1661
  end;
1662
1663
  GetGLTextureCaps(caps);
1664
  CalculateTextureDimensions(caps, FRealWidth, FRealHeight, FWidth, FHeight);
1665
  FGraphic := TMultiImage.CreateFromParams(FWidth, FHeight, ifA8R8G8B8,
1666
    AAnimData.FrameCount);
1667
1668
  for i := 0 to AAnimData.FrameCount - 1 do
1669
  begin
1670
    FGraphic.ActiveImage := i;
1671
    art[i].Graphic.CopyTo(0, 0, art[i].Graphic.Width, art[i].Graphic.Height,
1672
      FGraphic, 0, 0);
1673
    FTextures[i] := GenerateTexture(FGraphic);
1674
    art[i].Free;
1675
  end;
1676
1677
  FGraphic.ActiveImage := 0;
1678
  FActiveFrame := 0;
1679
  FNextChange := GetTickCount + AAnimData.FrameStart * 100;
1680
end;
1681
1682
destructor TAnimMaterial.Destroy;
1683
begin
1684
  glDeleteTextures(Length(FTextures), @FTextures[0]);
1685
  inherited Destroy;
1686
end;
1687
1688
function TAnimMaterial.GetTexture: GLuint;
1689
begin
1690
  if FNextChange <= GetTickCount then
1691
  begin
1692
    FActiveFrame := (FActiveFrame + 1) mod FAnimData.FrameCount;
1693
    FGraphic.ActiveImage := FActiveFrame;
1694
1695
    if FActiveFrame = 0 then
1696
      FNextChange := GetTickCount + FAnimData.FrameStart * 100
1697
    else
1698
      FNextChange:= GetTickCount + FAnimData.FrameInterval * 100;
1699
  end;
1700
1701
  Result := FTextures[FActiveFrame];
1702
end;
1703
1704
end.
1705