1870 lines
51 KiB
Plaintext
1870 lines
51 KiB
Plaintext
(*
|
||
* CDDL HEADER START
|
||
*
|
||
* The contents of this file are subject to the terms of the
|
||
* Common Development and Distribution License, Version 1.0 only
|
||
* (the "License"). You may not use this file except in compliance
|
||
* with the License.
|
||
*
|
||
* You can obtain a copy of the license at
|
||
* http://www.opensource.org/licenses/cddl1.php.
|
||
* See the License for the specific language governing permissions
|
||
* and limitations under the License.
|
||
*
|
||
* When distributing Covered Code, include this CDDL HEADER in each
|
||
* file and include the License file at
|
||
* http://www.opensource.org/licenses/cddl1.php. If applicable,
|
||
* add the following below this CDDL HEADER, with the fields enclosed
|
||
* by brackets "[]" replaced with your own identifying * information:
|
||
* Portions Copyright [yyyy] [name of copyright owner]
|
||
*
|
||
* CDDL HEADER END
|
||
*
|
||
*
|
||
* Portions Copyright 2009 Andreas Schneider
|
||
*)
|
||
unit ULandscape;
|
||
|
||
{$mode objfpc}{$H+}
|
||
|
||
interface
|
||
|
||
uses
|
||
SysUtils, Classes, math, matrix, LCLIntf, GL, GLu, ImagingOpenGL, Imaging,
|
||
ImagingClasses, ImagingTypes, ImagingUtility, DOM, XMLRead,
|
||
UGenericIndex, UMap, UStatics, UArt, UTexture, UTiledata, UHue, UWorldItem,
|
||
UMulBlock, UAnimData, UfrmInitialize,
|
||
UEnhancedMemoryStream, UGLFont,
|
||
UCacheManager;
|
||
|
||
type
|
||
TGlVector3f = array[0..2] of GLfloat;
|
||
PNormals = ^TNormals;
|
||
TNormals = array[0..3] of TGlVector3f;
|
||
PRadarBlock = ^TRadarBlock;
|
||
TRadarBlock = array[0..7, 0..7] of Word;
|
||
|
||
{ TMaterial }
|
||
|
||
TMaterial = class(ICacheable)
|
||
constructor Create;
|
||
destructor Destroy; override;
|
||
protected
|
||
FRefCount: Integer;
|
||
FWidth: Integer;
|
||
FHeight: Integer;
|
||
FRealWidth: Integer;
|
||
FRealHeight: Integer;
|
||
FGraphic: TMultiImage;
|
||
class procedure CalculateTextureDimensions(ACaps: TGLTextureCaps; ARealWidth,
|
||
ARealHeight: Integer; out AWidth, AHeight: Integer);
|
||
function GenerateTexture(AImage: TBaseImage): TGLuint;
|
||
function GetTexture: GLuint; virtual; abstract;
|
||
public
|
||
property Width: Integer read FWidth;
|
||
property Height: Integer read FHeight;
|
||
property RealWidth: Integer read FRealWidth;
|
||
property RealHeight: Integer read FRealHeight;
|
||
property Texture: GLuint read GetTexture;
|
||
|
||
procedure AddRef;
|
||
procedure DelRef;
|
||
function HitTest(AX, AY: Integer): Boolean;
|
||
function HitTest(AX, AY: Integer; Zoom: Single): Boolean;
|
||
|
||
{ICacheable}
|
||
function CanBeRemoved: Boolean;
|
||
procedure RemoveFromCache;
|
||
end;
|
||
|
||
{ TSimpleMaterial }
|
||
|
||
TSimpleMaterial = class(TMaterial)
|
||
constructor Create(AGraphic: TBaseImage);
|
||
destructor Destroy; override;
|
||
protected
|
||
FTexture: TGLuint;
|
||
function GetTexture: GLuint; override;
|
||
end;
|
||
|
||
{ TAnimMaterial }
|
||
|
||
TAnimMaterial = class(TMaterial)
|
||
constructor Create(ABaseID: LongWord; AAnimData: TAnimData; AHue: THue = nil;
|
||
APartialHue: Boolean = False);
|
||
destructor Destroy; override;
|
||
protected
|
||
FActiveFrame: Byte;
|
||
FNextChange: DWord;
|
||
FAnimData: TAnimData;
|
||
FTextures: array of TGLuint;
|
||
function GetTexture: GLuint; override;
|
||
end;
|
||
|
||
TMaterialCache = specialize TCacheManager<TMaterial>;
|
||
|
||
{ TLandTextureManager }
|
||
|
||
TLandTextureManager = class
|
||
constructor Create;
|
||
destructor Destroy; override;
|
||
protected
|
||
FArtCache: TMaterialCache;
|
||
FTexCache: TMaterialCache;
|
||
FAnimCache: TMaterialCache;
|
||
FUseAnims: Boolean;
|
||
public
|
||
property UseAnims: Boolean read FUseAnims write FUseAnims;
|
||
function GetArtMaterial(ATileID: LongWord): TMaterial; overload;
|
||
function GetArtMaterial(ATileID: LongWord; AHue: THue;
|
||
APartialHue: Boolean): TMaterial; overload;
|
||
function GetStaticMaterial(AStaticItem: TStaticItem;
|
||
AOverrideHue: Integer = -1): TMaterial;
|
||
function GetTexMaterial(ATileID: LongWord): TMaterial;
|
||
end;
|
||
|
||
{ TSeperatedStaticBlock }
|
||
|
||
TSeperatedStaticBlock = class(TStaticBlock)
|
||
constructor Create(AData: TStream; AIndex: TGenericIndex; AX, AY: Word); overload;
|
||
constructor Create(AData: TStream; AIndex: TGenericIndex); overload;
|
||
destructor Destroy; override;
|
||
public
|
||
Cells: array[0..63] of TStaticItemList;
|
||
{ Methods }
|
||
function Clone: TSeperatedStaticBlock; override;
|
||
function GetSize: Integer; override;
|
||
procedure RebuildList;
|
||
end;
|
||
|
||
TLandscape = class;
|
||
|
||
{ TBlock }
|
||
|
||
TBlock = class
|
||
constructor Create(AMap: TMapBlock; AStatics: TStaticBlock);
|
||
destructor Destroy; override;
|
||
protected
|
||
{ Fields }
|
||
FMapBlock: TMapBlock;
|
||
FStaticBlock: TStaticBlock;
|
||
public
|
||
{ Fields }
|
||
property Map: TMapBlock read FMapBlock;
|
||
property Static: TStaticBlock read FStaticBlock;
|
||
{ Methods }
|
||
procedure UpdateBlockAcess(ALandscape: TLandscape);
|
||
end;
|
||
|
||
TLandscapeChangeEvent = procedure of object;
|
||
TMapChangedEvent = procedure(AMapCell: TMapCell) of object;
|
||
TNewBlockEvent = procedure(ABlock: TBlock) of object;
|
||
TStaticChangedEvent = procedure(AStaticItem: TStaticItem) of object;
|
||
|
||
TScreenBuffer = class;
|
||
TBlockCache = specialize TCacheManager<TBlock>;
|
||
|
||
{ TLandscape }
|
||
|
||
TLandscape = class
|
||
constructor Create(AWidth, AHeight: Word);
|
||
destructor Destroy; override;
|
||
protected
|
||
{ Members }
|
||
FWidth: Word;
|
||
FHeight: Word;
|
||
FCellWidth: Word;
|
||
FCellHeight: Word;
|
||
FBlockCache: TBlockCache;
|
||
FBlockCacheBak: TBlockCache;
|
||
FOnChange: TLandscapeChangeEvent;
|
||
FOnMapChanged: TMapChangedEvent;
|
||
FOnNewBlock: TNewBlockEvent;
|
||
FOnStaticInserted: TStaticChangedEvent;
|
||
FOnStaticDeleted: TStaticChangedEvent;
|
||
FOnStaticElevated: TStaticChangedEvent;
|
||
FOnStaticHued: TStaticChangedEvent;
|
||
FOpenRequests: TBits;
|
||
FWriteMap: TBits;
|
||
FDrawMap: TBits;
|
||
FMaxStaticID: Cardinal;
|
||
BlockCacheSize: Integer;
|
||
{ Methods }
|
||
function GetMapBlock(AX, AY: Word): TMapBlock;
|
||
function GetMapCell(AX, AY: Integer): TMapCell;
|
||
function GetNormals(AX, AY: Integer): TNormals;
|
||
function GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
|
||
function GetStaticList(AX, AY: Word): TStaticItemList;
|
||
{ Events }
|
||
procedure OnRemoveCachedObject(ABlock: TBlock);
|
||
procedure OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
|
||
procedure OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
|
||
procedure OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
procedure OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
procedure OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
procedure OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
procedure OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
public
|
||
{ Fields }
|
||
property Width: Word read FWidth;
|
||
property Height: Word read FHeight;
|
||
property CellWidth: Word read FCellWidth;
|
||
property CellHeight: Word read FCellHeight;
|
||
property MapCell[X, Y: Integer]: TMapCell read GetMapCell;
|
||
property StaticList[X, Y: Word]: TStaticItemList read GetStaticList;
|
||
property Normals[X, Y: Integer]: TNormals read GetNormals;
|
||
property MaxStaticID: Cardinal read FMaxStaticID;
|
||
property OnChange: TLandscapeChangeEvent read FOnChange write FOnChange;
|
||
property OnMapChanged: TMapChangedEvent read FOnMapChanged write FOnMapChanged;
|
||
property OnNewBlock: TNewBlockEvent read FOnNewBlock write FOnNewBlock;
|
||
property OnStaticInserted: TStaticChangedEvent read FOnStaticInserted
|
||
write FOnStaticInserted;
|
||
property OnStaticDeleted: TStaticChangedEvent read FOnStaticDeleted
|
||
write FOnStaticDeleted;
|
||
property OnStaticElevated: TStaticChangedEvent read FOnStaticElevated
|
||
write FOnStaticElevated;
|
||
property OnStaticHued: TStaticChangedEvent read FOnStaticHued
|
||
write FOnStaticHued;
|
||
{ Methods }
|
||
function CanWrite(AX, AY: Word): Boolean;
|
||
procedure FillDrawList(ADrawList: TScreenBuffer; AX, AY, AWidth, AHeight: Word;
|
||
AMap, AStatics, ATDWalls, ATDBridges, ATDRoofs, ATDSurfaces, ATDFoliage, ATDWet: Boolean;
|
||
ANoDraw: Boolean; AAdditionalTiles: TWorldItemList = nil);
|
||
function GetEffectiveAltitude(ATile: TMapCell): ShortInt;
|
||
function GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
|
||
procedure GetNormals(AX, AY: Word; var ANormals: TNormals);
|
||
procedure LoadNoDrawMap(AFileName: String);
|
||
procedure MoveStatic(AStatic: TStaticItem; AX, AY: Word);
|
||
procedure PrepareBlocks(AX1, AY1, AX2, AY2: Word);
|
||
procedure UpdateBlockAccess;
|
||
procedure UpdateWriteMap(AStream: TEnhancedMemoryStream);
|
||
procedure ResizeBlockCache(Count: Integer);
|
||
end;
|
||
|
||
{ TGLText }
|
||
|
||
TGLText = class
|
||
constructor Create(AFont: TGLFont; AText: String);
|
||
protected
|
||
FFont: TGLFont;
|
||
FText: String;
|
||
FWidth: Integer;
|
||
FHeight: Integer;
|
||
public
|
||
procedure Render(AScreenRect: TRect);
|
||
end;
|
||
|
||
TScreenState = (ssNormal, ssFiltered, ssGhost);
|
||
TWalkRestriction = (wrNone, wrCanWalk, wrCannotWalk);
|
||
|
||
PBlockInfo = ^TBlockInfo;
|
||
TBlockInfo = record
|
||
ScreenRect: TRect;
|
||
DrawQuad: array[0..3,0..1] of TGLint;
|
||
RealQuad: array[0..3,0..1] of TGLint;
|
||
LineWidth: array[0..2] of GLfloat;
|
||
LineDraw: array[0..2,0..1,0..1] of TGLint;
|
||
Item: TWorldItem;
|
||
HighRes: TMaterial;
|
||
LowRes: TMaterial;
|
||
Normals: PNormals;
|
||
State: TScreenState;
|
||
Highlighted: Boolean;
|
||
HueOverride: Boolean;
|
||
Hue: Word;
|
||
CheckRealQuad: Boolean;
|
||
Translucent: Boolean;
|
||
WalkRestriction: TWalkRestriction;
|
||
Text: TGLText;
|
||
Next: PBlockInfo;
|
||
end;
|
||
|
||
{ TScreenBuffer }
|
||
|
||
TScreenBuffer = class
|
||
constructor Create; virtual;
|
||
destructor Destroy; override;
|
||
protected
|
||
{ Members }
|
||
FCount: Cardinal;
|
||
FShortCuts: array[-1..10] of PBlockInfo; //-1 = last, 0 = first, 1..10 = other shortcuts
|
||
FShortCutsValid: Boolean;
|
||
FSerial: Cardinal;
|
||
public
|
||
{ Methods }
|
||
function Add(AItem: TWorldItem): PBlockInfo;
|
||
procedure Clear;
|
||
procedure Delete(AItem: TWorldItem);
|
||
function Find(AScreenPosition: TPoint; Zoom: Single): PBlockInfo;
|
||
function Find(AX, AY: Word): PBlockInfo;
|
||
function GetSerial: Cardinal;
|
||
function Insert(AItem: TWorldItem): PBlockInfo;
|
||
function Iterate(var ABlockInfo: PBlockInfo): Boolean;
|
||
procedure UpdateShortcuts;
|
||
function UpdateSortOrder(AItem: TWorldItem): PBlockInfo;
|
||
{ Events }
|
||
procedure OnTileRemoved(ATile: TMulBlock);
|
||
end;
|
||
|
||
TStaticInfo = packed record
|
||
X: Word;
|
||
Y: Word;
|
||
Z: ShortInt;
|
||
TileID: Word;
|
||
Hue: Word;
|
||
end;
|
||
|
||
implementation
|
||
|
||
uses
|
||
UGameResources, UdmNetwork, UPackets, UPacketHandlers, Logging;
|
||
|
||
function GetID(AX, AY: Word): Integer; inline;
|
||
begin
|
||
Result := (AX shl 16) or AY;
|
||
end;
|
||
|
||
operator := (AVector: Tvector3_single) GLVector: TGlVector3f;
|
||
begin
|
||
GLVector[0] := AVector.data[0];
|
||
GLVector[1] := AVector.data[1];
|
||
GLVector[2] := AVector.data[2];
|
||
end;
|
||
|
||
{ TLandTextureManager }
|
||
|
||
constructor TLandTextureManager.Create;
|
||
begin
|
||
inherited Create;
|
||
FArtCache := TMaterialCache.Create(1024);
|
||
FTexCache := TMaterialCache.Create(128);
|
||
FAnimCache := TMaterialCache.Create(128);
|
||
FUseAnims := True;
|
||
end;
|
||
|
||
destructor TLandTextureManager.Destroy;
|
||
begin
|
||
FreeAndNil(FArtCache);
|
||
FreeAndNil(FTexCache);
|
||
FreeAndNil(FAnimCache);
|
||
inherited Destroy;
|
||
end;
|
||
|
||
function TLandTextureManager.GetArtMaterial(ATileID: LongWord): TMaterial;
|
||
var
|
||
artEntry: TArt;
|
||
animData: TAnimData;
|
||
begin
|
||
Result := nil;
|
||
|
||
if FUseAnims and (ATileID >= $4000) and (tdfAnimation in
|
||
ResMan.Tiledata.StaticTiles[ATileID - $4000].Flags) then
|
||
begin
|
||
animData := ResMan.Animdata.AnimData[ATileID - $4000];
|
||
if (animData.FrameCount > 0) and not FAnimCache.QueryID(ATileID, Result) then
|
||
begin
|
||
Result := TAnimMaterial.Create(ATileID, animData);
|
||
FAnimCache.StoreID(ATileID, Result);
|
||
end;
|
||
end;
|
||
|
||
if (Result = nil) and not FArtCache.QueryID(ATileID, Result) then
|
||
begin
|
||
artEntry := TArt(ResMan.Art.Block[ATileID]);
|
||
|
||
Result := TSimpleMaterial.Create(artEntry.Graphic);
|
||
FArtCache.StoreID(ATileID, Result);
|
||
|
||
artEntry.Free;
|
||
end;
|
||
|
||
Result.AddRef;
|
||
end;
|
||
|
||
function TLandTextureManager.GetArtMaterial(ATileID: LongWord; AHue: THue;
|
||
APartialHue: Boolean): TMaterial;
|
||
var
|
||
artEntry: TArt;
|
||
animData: TAnimData;
|
||
id: Integer;
|
||
begin
|
||
if AHue = nil then
|
||
begin
|
||
Result := GetArtMaterial(ATileID);
|
||
end else
|
||
begin
|
||
Result := nil;
|
||
id := ATileID or (((AHue.ID + 1) and $3FFF) shl 16) or (Byte(APartialHue) shl 30);
|
||
|
||
if FUseAnims and (ATileID >= $4000) and (tdfAnimation in
|
||
ResMan.Tiledata.StaticTiles[ATileID - $4000].Flags) then
|
||
begin
|
||
animData := ResMan.Animdata.AnimData[ATileID - $4000];
|
||
if (animData.FrameCount > 0) and not FAnimCache.QueryID(id, Result) then
|
||
begin
|
||
Result := TAnimMaterial.Create(ATileID, animData, AHue, APartialHue);
|
||
FAnimCache.StoreID(id, Result);
|
||
end;
|
||
end;
|
||
|
||
if (Result = nil) and not FArtCache.QueryID(id, Result) then
|
||
begin
|
||
artEntry := ResMan.Art.GetArt(ATileID, 0, AHue, APartialHue);
|
||
|
||
Result := TSimpleMaterial.Create(artEntry.Graphic);
|
||
FArtCache.StoreID(id, Result);
|
||
|
||
artEntry.Free;
|
||
end;
|
||
Result.AddRef;
|
||
end;
|
||
end;
|
||
|
||
function TLandTextureManager.GetStaticMaterial(AStaticItem: TStaticItem;
|
||
AOverrideHue: Integer = -1): TMaterial;
|
||
var
|
||
staticTiledata: TStaticTiledata;
|
||
hue: THue;
|
||
begin
|
||
staticTiledata := ResMan.Tiledata.StaticTiles[AStaticItem.TileID];
|
||
if AOverrideHue < 0 then
|
||
AOverrideHue := AStaticItem.Hue;
|
||
|
||
if AOverrideHue > 0 then
|
||
hue := ResMan.Hue.Hues[AOverrideHue - 1]
|
||
else
|
||
hue := nil;
|
||
|
||
Result := GetArtMaterial($4000 + AStaticItem.TileID, hue,
|
||
tdfPartialHue in staticTiledata.Flags);
|
||
end;
|
||
|
||
function TLandTextureManager.GetTexMaterial(ATileID: LongWord): TMaterial;
|
||
var
|
||
texEntry: TTexture;
|
||
texID: Integer;
|
||
begin
|
||
if not FTexCache.QueryID(ATileID, Result) then
|
||
begin
|
||
texID := ResMan.Tiledata.LandTiles[ATileID].TextureID;
|
||
if texID > 0 then
|
||
begin
|
||
texEntry := TTexture(ResMan.Texmaps.Block[texID]);
|
||
|
||
Result := TSimpleMaterial.Create(texEntry.Graphic);
|
||
FTexCache.StoreID(ATileID, Result);
|
||
|
||
texEntry.Free;
|
||
end else
|
||
Result := nil;
|
||
end;
|
||
|
||
if Result <> nil then
|
||
Result.AddRef;
|
||
end;
|
||
|
||
{ TSeperatedStaticBlock }
|
||
|
||
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex;
|
||
AX, AY: Word);
|
||
var
|
||
i, ts: Integer;
|
||
item: TStaticItem;
|
||
block: TMemoryStream;
|
||
begin
|
||
inherited Create;
|
||
FItems := TStaticItemList.Create(False);
|
||
|
||
FX := AX;
|
||
FY := AY;
|
||
|
||
for i := 0 to 63 do
|
||
Cells[i] := TStaticItemList.Create;
|
||
|
||
if not UseStaticsOldFormat
|
||
then ts := 7
|
||
else ts := 11;
|
||
|
||
if (AData <> nil) and (AIndex.Lookup > 0) and (AIndex.Size > 0) then
|
||
begin
|
||
AData.Position := AIndex.Lookup;
|
||
block := TMemoryStream.Create;
|
||
block.CopyFrom(AData, AIndex.Size);
|
||
block.Position := 0;
|
||
|
||
for i := 1 to (AIndex.Size div ts) do
|
||
begin
|
||
item := TStaticItem.Create(Self, block, AX, AY);
|
||
Cells[(item.Y mod 8) * 8 + (item.X mod 8)].Add(item);
|
||
end;
|
||
block.Free;
|
||
end;
|
||
end;
|
||
|
||
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex);
|
||
begin
|
||
Create(AData, AIndex, 0, 0);
|
||
end;
|
||
|
||
destructor TSeperatedStaticBlock.Destroy;
|
||
var
|
||
i: Integer;
|
||
begin
|
||
FreeAndNil(FItems);
|
||
|
||
for i := 0 to 63 do
|
||
begin
|
||
if Cells[i] <> nil then
|
||
FreeAndNil(Cells[i]);
|
||
end;
|
||
|
||
inherited Destroy;
|
||
end;
|
||
|
||
function TSeperatedStaticBlock.Clone: TSeperatedStaticBlock;
|
||
begin
|
||
raise Exception.Create('TSeperatedStaticBlock.Clone is not implemented (yet).');
|
||
Result := nil;
|
||
end;
|
||
|
||
function TSeperatedStaticBlock.GetSize: Integer;
|
||
begin
|
||
RebuildList;
|
||
Result := inherited GetSize;
|
||
end;
|
||
|
||
procedure TSeperatedStaticBlock.RebuildList;
|
||
var
|
||
i, j, solver: Integer;
|
||
begin
|
||
FItems.Clear;
|
||
solver := 0;
|
||
for i := 0 to 63 do
|
||
begin
|
||
if Cells[i] <> nil then
|
||
begin
|
||
for j := 0 to Cells[i].Count - 1 do
|
||
begin
|
||
FItems.Add(Cells[i].Items[j]);
|
||
TStaticItem(Cells[i].Items[j]).UpdatePriorities(
|
||
ResMan.Tiledata.StaticTiles[TStaticItem(Cells[i].Items[j]).TileID],
|
||
solver);
|
||
Inc(solver);
|
||
end;
|
||
end;
|
||
end;
|
||
Sort;
|
||
end;
|
||
|
||
{ TBlock }
|
||
|
||
constructor TBlock.Create(AMap: TMapBlock; AStatics: TStaticBlock);
|
||
begin
|
||
inherited Create;
|
||
FMapBlock := AMap;
|
||
FStaticBlock := AStatics;
|
||
end;
|
||
|
||
destructor TBlock.Destroy;
|
||
begin
|
||
if FMapBlock <> nil then FreeAndNil(FMapBlock);
|
||
if FStaticBlock <> nil then FreeAndNil(FStaticBlock);
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TBlock.UpdateBlockAcess(ALandscape: TLandscape);
|
||
var
|
||
staticItem: TStaticItem;
|
||
i: Integer;
|
||
begin
|
||
for i := Low(FMapBlock.Cells) to High(FMapBlock.Cells) do
|
||
begin
|
||
FMapBlock.Cells[i].CanBeEdited := ALandscape.CanWrite(FMapBlock.Cells[i].X,
|
||
FMapBlock.Cells[i].Y);
|
||
end;
|
||
|
||
if FStaticBlock is TSeperatedStaticBlock then
|
||
TSeperatedStaticBlock(FStaticBlock).RebuildList; //fill items
|
||
|
||
for i := 0 to FStaticBlock.Items.Count - 1 do
|
||
begin
|
||
staticItem := FStaticBlock.Items[i];
|
||
staticItem.CanBeEdited := ALandscape.CanWrite(staticItem.X,
|
||
staticItem.Y);
|
||
end;
|
||
end;
|
||
|
||
{ TLandscape }
|
||
|
||
constructor TLandscape.Create(AWidth, AHeight: Word);
|
||
var
|
||
i: Integer;
|
||
begin
|
||
inherited Create;
|
||
FWidth := AWidth;
|
||
FHeight := AHeight;
|
||
FCellWidth := FWidth * 8;
|
||
FCellHeight := FHeight * 8;
|
||
BlockCacheSize := 256;
|
||
FBlockCache := TBlockCache.Create(BlockCacheSize); //856
|
||
FBlockCache.OnRemoveObject := @OnRemoveCachedObject;
|
||
|
||
FOnChange := nil;
|
||
FOnNewBlock := nil;
|
||
FOnStaticDeleted := nil;
|
||
FOnStaticElevated := nil;
|
||
FOnStaticHued := nil;
|
||
FOnStaticInserted := nil;
|
||
|
||
FOpenRequests := TBits.Create(FWidth * FHeight);
|
||
FOpenRequests.Clearall; //set all to 0
|
||
FWriteMap := TBits.Create(FCellWidth * FCellHeight);
|
||
for i := 0 to FWriteMap.Size - 1 do
|
||
FWriteMap[i] := True;
|
||
|
||
//FMaxStaticID := $1FFDC;
|
||
FMaxStaticID := Min(Min(ResMan.Animdata.AnimCount, ResMan.Tiledata.StaticCount),
|
||
ResMan.Art.EntryCount - $4000);
|
||
Logger.Send([lcClient, lcInfo], 'Landscape recognizes $%x StaticTile IDs.',
|
||
[FMaxStaticId]);
|
||
|
||
FDrawMap := TBits.Create($4000 + FMaxStaticID);
|
||
for i := 0 to FDrawMap.Size - 1 do
|
||
FDrawMap[i] := True;
|
||
|
||
RegisterPacketHandler($04, TPacketHandler.Create(0, @OnBlocksPacket));
|
||
RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket));
|
||
RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket));
|
||
RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket));
|
||
RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket));
|
||
RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket));
|
||
RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket));
|
||
end;
|
||
|
||
destructor TLandscape.Destroy;
|
||
begin
|
||
if (FBlockCacheBak <> nil) then
|
||
begin
|
||
FBlockCacheBak.OnRemoveObject := nil;
|
||
FreeAndNil(FBlockCacheBak);
|
||
end;
|
||
if FBlockCache <> nil then
|
||
begin
|
||
FBlockCache.OnRemoveObject := nil;
|
||
FreeAndNil(FBlockCache);
|
||
end;
|
||
|
||
FreeAndNil(FOpenRequests);
|
||
FreeAndNil(FWriteMap);
|
||
FreeAndNil(FDrawMap);
|
||
|
||
RegisterPacketHandler($04, nil);
|
||
RegisterPacketHandler($06, nil);
|
||
RegisterPacketHandler($07, nil);
|
||
RegisterPacketHandler($08, nil);
|
||
RegisterPacketHandler($09, nil);
|
||
RegisterPacketHandler($0A, nil);
|
||
RegisterPacketHandler($0B, nil);
|
||
|
||
inherited Destroy;
|
||
end;
|
||
|
||
function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock;
|
||
var
|
||
block: TBlock;
|
||
begin
|
||
Result := nil;
|
||
if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
|
||
begin
|
||
if FBlockCache.QueryID(GetID(AX, AY), block) then
|
||
Result := block.Map;
|
||
end;
|
||
end;
|
||
|
||
function TLandscape.GetMapCell(AX, AY: Integer): TMapCell;
|
||
var
|
||
block: TMapBlock;
|
||
begin
|
||
Result := nil;
|
||
if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
|
||
begin
|
||
block := GetMapBlock(AX div 8, AY div 8);
|
||
if block <> nil then
|
||
Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
|
||
end;
|
||
end;
|
||
|
||
function TLandscape.GetNormals(AX, AY: Integer): TNormals;
|
||
begin
|
||
GetNormals(AX, AY, Result);
|
||
end;
|
||
|
||
function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
|
||
var
|
||
block: TBlock;
|
||
begin
|
||
Result := nil;
|
||
if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
|
||
begin
|
||
if FBlockCache.QueryID(GetID(AX, AY), block) then
|
||
Result := TSeperatedStaticBlock(block.Static);
|
||
end;
|
||
end;
|
||
|
||
function TLandscape.GetStaticList(AX, AY: Word): TStaticItemList;
|
||
var
|
||
block: TSeperatedStaticBlock;
|
||
begin
|
||
Result := nil;
|
||
if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
|
||
begin
|
||
block := GetStaticBlock(AX div 8, AY div 8);
|
||
if block <> nil then
|
||
Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
|
||
end;
|
||
end;
|
||
|
||
procedure TLandscape.OnRemoveCachedObject(ABlock: TBlock);
|
||
begin
|
||
if ABlock <> nil then
|
||
dmNetwork.Send(TFreeBlockPacket.Create(ABlock.Map.X, ABlock.Map.Y));
|
||
end;
|
||
|
||
procedure TLandscape.OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
|
||
var
|
||
index: TGenericIndex;
|
||
map: TMapBlock;
|
||
statics: TStaticBlock;
|
||
coords: TBlockCoords;
|
||
count: Word;
|
||
id: Integer;
|
||
block: TBlock;
|
||
begin
|
||
index := TGenericIndex.Create(nil);
|
||
while ABuffer.Position < ABuffer.Size do
|
||
begin
|
||
ABuffer.Read(coords, SizeOf(TBlockCoords));
|
||
id := GetID(coords.X, coords.Y);
|
||
|
||
map := TMapBlock.Create(ABuffer, coords.X, coords.Y);
|
||
count := ABuffer.ReadWord;
|
||
if count > 0 then
|
||
index.Lookup := ABuffer.Position
|
||
else
|
||
index.Lookup := -1;
|
||
if not UseStaticsOldFormat
|
||
then index.Size := count * 7
|
||
else index.Size := count * 11;
|
||
statics := TSeperatedStaticBlock.Create(ABuffer, index, coords.X, coords.Y);
|
||
|
||
FBlockCache.RemoveID(id);
|
||
block := TBlock.Create(map, statics);
|
||
block.UpdateBlockAcess(Self);
|
||
FBlockCache.StoreID(id, block);
|
||
|
||
FOpenRequests[coords.Y * FWidth + coords.X] := False;
|
||
|
||
if Assigned(FOnNewBlock) then FOnNewBlock(block);
|
||
end;
|
||
index.Free;
|
||
end;
|
||
|
||
procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
|
||
var
|
||
x, y: Word;
|
||
cell: TMapCell;
|
||
begin
|
||
x := ABuffer.ReadWord;
|
||
y := ABuffer.ReadWord;
|
||
cell := GetMapCell(x, y);
|
||
if cell <> nil then
|
||
begin
|
||
cell.Altitude := ABuffer.ReadShortInt;
|
||
cell.TileID := ABuffer.ReadWord;
|
||
if Assigned(FOnMapChanged) then FOnMapChanged(cell);
|
||
end;
|
||
end;
|
||
|
||
procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
var
|
||
x, y: Word;
|
||
block: TSeperatedStaticBlock;
|
||
staticItem: TStaticItem;
|
||
targetStaticList: TStaticItemList;
|
||
i: Integer;
|
||
begin
|
||
x := ABuffer.ReadWord;
|
||
y := ABuffer.ReadWord;
|
||
block := GetStaticBlock(x div 8, y div 8);
|
||
if block <> nil then
|
||
begin
|
||
staticItem := TStaticItem.Create(nil, nil, 0, 0);
|
||
staticItem.X := x;
|
||
staticItem.Y := y;
|
||
staticItem.Z := ABuffer.ReadShortInt;
|
||
staticItem.TileID := ABuffer.ReadWord;
|
||
staticItem.Hue := ABuffer.ReadWord;
|
||
targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8];
|
||
targetStaticList.Add(staticItem);
|
||
for i := 0 to targetStaticList.Count - 1 do
|
||
targetStaticList.Items[i].UpdatePriorities(
|
||
ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
|
||
i);
|
||
targetStaticList.Sort(@CompareStaticItems);
|
||
staticItem.Owner := block;
|
||
staticItem.CanBeEdited := CanWrite(x, y);
|
||
|
||
if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
|
||
end;
|
||
end;
|
||
|
||
procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
var
|
||
block: TSeperatedStaticBlock;
|
||
i: Integer;
|
||
statics: TStaticItemList;
|
||
staticInfo: TStaticInfo;
|
||
staticItem: TStaticItem;
|
||
begin
|
||
ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
|
||
block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
|
||
if block <> nil then
|
||
begin
|
||
statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
|
||
for i := 0 to statics.Count - 1 do
|
||
begin
|
||
staticItem := statics.Items[i];
|
||
if (staticItem.Z = staticInfo.Z) and
|
||
(staticItem.TileID = staticInfo.TileID) and
|
||
(staticItem.Hue = staticInfo.Hue) then
|
||
begin
|
||
if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
|
||
staticItem.Delete;
|
||
statics.Delete(i);
|
||
|
||
Break;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
var
|
||
block: TSeperatedStaticBlock;
|
||
i,j : Integer;
|
||
statics: TStaticItemList;
|
||
staticInfo: TStaticInfo;
|
||
staticItem: TStaticItem;
|
||
begin
|
||
ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
|
||
block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
|
||
if block <> nil then
|
||
begin
|
||
statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
|
||
for i := 0 to statics.Count - 1 do
|
||
begin
|
||
staticItem := statics.Items[i];
|
||
if (staticItem.Z = staticInfo.Z) and
|
||
(staticItem.TileID = staticInfo.TileID) and
|
||
(staticItem.Hue = staticInfo.Hue) then
|
||
begin
|
||
staticItem.Z := ABuffer.ReadShortInt;
|
||
for j := 0 to statics.Count - 1 do
|
||
statics.Items[j].UpdatePriorities(
|
||
ResMan.Tiledata.StaticTiles[statics.Items[j].TileID],
|
||
j);
|
||
statics.Sort(@CompareStaticItems);
|
||
|
||
if Assigned(FOnStaticElevated) then FOnStaticElevated(staticItem);
|
||
|
||
Break;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
var
|
||
sourceBlock, targetBlock: TSeperatedStaticBlock;
|
||
i: Integer;
|
||
statics: TStaticItemList;
|
||
staticInfo: TStaticInfo;
|
||
staticItem: TStaticItem;
|
||
newX, newY: Word;
|
||
begin
|
||
staticItem := nil;
|
||
ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
|
||
newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1);
|
||
newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1);
|
||
|
||
sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
|
||
targetBlock := GetStaticBlock(newX div 8, newY div 8);
|
||
if sourceBlock <> nil then
|
||
begin
|
||
statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
|
||
i := 0;
|
||
while (i < statics.Count) and (staticItem = nil) do
|
||
begin
|
||
staticItem := statics.Items[i];
|
||
if (staticItem.Z <> staticInfo.Z) or
|
||
(staticItem.TileID <> staticInfo.TileID) or
|
||
(staticItem.Hue <> staticInfo.Hue) then
|
||
begin
|
||
staticItem := nil;
|
||
end;
|
||
Inc(i);
|
||
end;
|
||
|
||
if staticItem <> nil then
|
||
begin
|
||
if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
|
||
staticItem.Delete;
|
||
statics.Remove(staticItem);
|
||
end;
|
||
end;
|
||
|
||
if targetBlock <> nil then
|
||
begin
|
||
staticItem := TStaticItem.Create(nil, nil, 0, 0);
|
||
staticItem.X := newX;
|
||
staticItem.Y := newY;
|
||
staticItem.Z := staticInfo.Z;
|
||
staticItem.TileID := staticInfo.TileID;
|
||
staticItem.Hue := staticInfo.Hue;
|
||
statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8];
|
||
statics.Add(staticItem);
|
||
for i := 0 to statics.Count - 1 do
|
||
TStaticItem(statics.Items[i]).UpdatePriorities(
|
||
ResMan.Tiledata.StaticTiles[TStaticItem(statics.Items[i]).TileID],
|
||
i);
|
||
statics.Sort(@CompareStaticItems);
|
||
staticItem.Owner := targetBlock;
|
||
staticItem.CanBeEdited := CanWrite(newX, newY);
|
||
|
||
if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
|
||
end;
|
||
end;
|
||
|
||
procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
|
||
var
|
||
block: TSeperatedStaticBlock;
|
||
i : Integer;
|
||
statics: TStaticItemList;
|
||
staticInfo: TStaticInfo;
|
||
staticItem: TStaticItem;
|
||
begin
|
||
ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
|
||
block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
|
||
if block <> nil then
|
||
begin
|
||
statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
|
||
for i := 0 to statics.Count - 1 do
|
||
begin
|
||
staticItem := statics.Items[i];
|
||
if (staticItem.Z = staticInfo.Z) and
|
||
(staticItem.TileID = staticInfo.TileID) and
|
||
(staticItem.Hue = staticInfo.Hue) then
|
||
begin
|
||
staticItem.Hue := ABuffer.ReadWord;
|
||
if Assigned(FOnStaticHued) then FOnStaticHued(staticItem);
|
||
Break;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function TLandscape.CanWrite(AX, AY: Word): Boolean;
|
||
begin
|
||
Result := FWriteMap[AX * FCellHeight + AY];
|
||
end;
|
||
|
||
procedure TLandscape.FillDrawList(ADrawList: TScreenBuffer; AX, AY, AWidth,
|
||
AHeight: Word; AMap, AStatics, ATDWalls, ATDBridges, ATDRoofs, ATDSurfaces, ATDFoliage, ATDWet: Boolean;
|
||
ANoDraw: Boolean; AAdditionalTiles: TWorldItemList = nil);
|
||
var
|
||
drawMapCell: TMapCell;
|
||
drawStatics: TStaticItemList;
|
||
i, x, y: Integer;
|
||
tempDrawList: TWorldItemList;
|
||
staticTileData: TStaticTiledata;
|
||
begin
|
||
ADrawList.Clear;
|
||
tempDrawList := TWorldItemList.Create(False);
|
||
for x := AX to AX + AWidth do
|
||
begin
|
||
for y := AY to AY + AWidth do
|
||
begin
|
||
if AMap then
|
||
begin
|
||
drawMapCell := GetMapCell(x, y);
|
||
if (drawMapCell <> nil) and (ANoDraw or FDrawMap[drawMapCell.TileID]) then
|
||
begin
|
||
drawMapCell.Priority := GetEffectiveAltitude(drawMapCell);
|
||
drawMapCell.PriorityBonus := 0;
|
||
drawMapCell.PrioritySolver := 0;
|
||
tempDrawList.Add(drawMapCell);
|
||
end;
|
||
end;
|
||
|
||
if AStatics then
|
||
begin
|
||
drawStatics := GetStaticList(x, y);
|
||
if drawStatics <> nil then
|
||
for i := 0 to drawStatics.Count - 1 do
|
||
begin
|
||
staticTileData := ResMan.Tiledata.StaticTiles[drawStatics[i].TileID];
|
||
if ANoDraw or FDrawMap[drawStatics[i].TileID + $4000] then
|
||
begin
|
||
if not ATDWalls and ((tdfWall in staticTileData.Flags) or (tdfWindow in staticTileData.Flags)) then continue;
|
||
if not ATDBridges and ((tdfBridge in staticTileData.Flags) or (tdfStairBack in staticTileData.Flags) or (tdfStairRight in staticTileData.Flags)) then continue;
|
||
if not ATDRoofs and (tdfRoof in staticTileData.Flags) then continue;
|
||
if not ATDSurfaces and (tdfSurface in staticTileData.Flags) then continue;
|
||
if not ATDFoliage and (tdfFoliage in staticTileData.Flags) then continue;
|
||
if not ATDWet and (tdfWet in staticTileData.Flags) then continue;
|
||
drawStatics[i].UpdatePriorities(staticTileData, ADrawList.GetSerial);
|
||
tempDrawList.Add(drawStatics[i]);
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
for i := 0 to AAdditionalTiles.Count - 1 do
|
||
tempDrawList.Add(AAdditionalTiles[i]);
|
||
|
||
tempDrawList.Sort(@CompareWorldItems);
|
||
for i := 0 to tempDrawList.Count - 1 do
|
||
ADrawList.Add(TWorldItem(tempDrawList[i]));
|
||
tempDrawList.Free;
|
||
end;
|
||
|
||
function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt;
|
||
var
|
||
north, west, south, east: ShortInt;
|
||
begin
|
||
north := ATile.Altitude;
|
||
west := GetLandAlt(ATile.X, ATile.Y + 1, north);
|
||
south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north);
|
||
east := GetLandAlt(ATile.X + 1, ATile.Y, north);
|
||
|
||
if Abs(north - south) >= Abs(west - east) then
|
||
Result := Min(north, south) + Abs(west - east) div 2
|
||
else
|
||
Result := Min(north, south) + Abs(north - south) div 2;
|
||
end;
|
||
|
||
function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
|
||
var
|
||
cell: TMapCell;
|
||
begin
|
||
cell := MapCell[AX, AY];
|
||
if cell <> nil then
|
||
Result := cell.Altitude
|
||
else
|
||
Result := ADefault;
|
||
end;
|
||
|
||
procedure TLandscape.GetNormals(AX, AY: Word; var ANormals: TNormals);
|
||
type
|
||
_Normals = array[0..3] of Tvector3_single;
|
||
var
|
||
cells: array[0..2, 0..2] of _Normals;
|
||
north, west, south, east: Tvector3_single;
|
||
i, j: Integer;
|
||
|
||
function Normalize(const AVector: Tvector3_single): Tvector3_single; inline;
|
||
begin
|
||
Result := AVector / AVector.length;
|
||
end;
|
||
|
||
function GetPlainNormals(X, Y: SmallInt): _Normals;
|
||
var
|
||
cell: TMapCell;
|
||
north, west, south, east: ShortInt;
|
||
u, v: Tvector3_single;
|
||
begin
|
||
cell := GetMapCell(X, Y);
|
||
if cell <> nil then
|
||
begin
|
||
north := cell.Altitude;
|
||
west := GetLandAlt(cell.X, cell.Y + 1, north);
|
||
south := GetLandAlt(cell.X + 1, cell.Y + 1, north);
|
||
east := GetLandAlt(cell.X + 1, cell.Y, north);
|
||
end else
|
||
begin
|
||
north := 0;
|
||
west := 0;
|
||
east := 0;
|
||
south := 0;
|
||
end;
|
||
|
||
if (north = west) and (west = east) and (north = south) then
|
||
begin
|
||
Result[0].init(0, 0, 1);
|
||
Result[1].init(0, 0, 1);
|
||
Result[2].init(0, 0, 1);
|
||
Result[3].init(0, 0, 1);
|
||
end else
|
||
begin
|
||
u.init(-22, 22, (north - east) * 4);
|
||
v.init(-22, -22, (west - north) * 4);
|
||
Result[0] := Normalize(u >< v);
|
||
|
||
u.init(22, 22, (east - south) * 4);
|
||
v.init(-22, 22, (north - east) * 4);
|
||
Result[1] := Normalize(u >< v);
|
||
|
||
u.init(22, -22, (south - west) * 4);
|
||
v.init(22, 22, (east - south) * 4);
|
||
Result[2] := Normalize(u >< v);
|
||
|
||
u.init(-22, -22, (west - north) * 4);
|
||
v.init(22, -22, (south - west) * 4);
|
||
Result[3] := Normalize(u >< v);
|
||
end;
|
||
end;
|
||
begin
|
||
for i := 0 to 2 do
|
||
for j := 0 to 2 do
|
||
cells[i, j] := GetPlainNormals(AX - 1 + i, AY - 1 + j);
|
||
|
||
north := cells[0, 0][2];
|
||
west := cells[0, 1][1];
|
||
east := cells[1, 0][3];
|
||
south := cells[1, 1][0];
|
||
ANormals[0] := Normalize(north + west + east + south);
|
||
|
||
north := cells[1, 0][2];
|
||
west := cells[1, 1][1];
|
||
east := cells[2, 0][3];
|
||
south := cells[2, 1][0];
|
||
ANormals[1] := Normalize(north + west + east + south);
|
||
|
||
north := cells[1, 1][2];
|
||
west := cells[1, 2][1];
|
||
east := cells[2, 1][3];
|
||
south := cells[2, 2][0];
|
||
ANormals[2] := Normalize(north + west + east + south);
|
||
|
||
north := cells[0, 1][2];
|
||
west := cells[0, 2][1];
|
||
east := cells[1, 1][3];
|
||
south := cells[1, 2][0];
|
||
ANormals[3] := Normalize(north + west + east + south);
|
||
end;
|
||
|
||
procedure TLandscape.LoadNoDrawMap(AFileName: String);
|
||
var
|
||
XMLDoc: TXMLDocument;
|
||
iNode, node: TDOMNode;
|
||
s: string;
|
||
i, id: Integer;
|
||
|
||
begin
|
||
frmInitialize.SetStatusLabel(Format(frmInitialize.SplashLoading, ['VirtualTiles.xml']));
|
||
// Читаем xml файл с жесткого диска
|
||
ReadXMLFile(XMLDoc, AFileName);
|
||
if LowerCase(XMLDoc.DocumentElement.NodeName) = 'virtualtiles' then
|
||
begin
|
||
iNode := XMLDoc.DocumentElement.FirstChild;
|
||
while iNode <> nil do
|
||
begin
|
||
if LowerCase(iNode.NodeName) = 'nodraw' then
|
||
begin
|
||
node := iNode.FirstChild;
|
||
while node <> nil do
|
||
begin
|
||
s := LowerCase(node.NodeName);
|
||
if (s = 'tile') or (s = 'land') or (s = 'item') then
|
||
for i := node.Attributes.Length - 1 downto 0 do
|
||
if LowerCase(node.Attributes[i].NodeName) = 'id' then
|
||
if TryStrToInt(node.Attributes[i].NodeValue, id) then
|
||
begin
|
||
if s = 'item'
|
||
then Inc(id, $4000);
|
||
if (id >= 0) and (id < FDrawMap.Size)
|
||
then FDrawMap[id] := False;
|
||
end;
|
||
node := node.NextSibling;
|
||
end;
|
||
end;
|
||
iNode := iNode.NextSibling;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TLandscape.MoveStatic(AStatic: TStaticItem; AX, AY: Word);
|
||
var
|
||
sourceBlock, targetBlock: TSeperatedStaticBlock;
|
||
targetStaticList: TStaticItemList;
|
||
i: Integer;
|
||
begin
|
||
if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
|
||
begin
|
||
sourceBlock := AStatic.Owner as TSeperatedStaticBlock;
|
||
targetBlock := GetStaticBlock(AX div 8, AY div 8);
|
||
if (sourceBlock <> nil) and (targetBlock <> nil) then
|
||
begin
|
||
sourceBlock.Cells[(AStatic.Y mod 8) * 8 + AStatic.X mod 8].Remove(AStatic);
|
||
targetStaticList := targetBlock.Cells[(AY mod 8) * 8 + AX mod 8];
|
||
targetStaticList.Add(AStatic);
|
||
for i := 0 to targetStaticList.Count - 1 do
|
||
targetStaticList.Items[i].UpdatePriorities(
|
||
ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
|
||
i);
|
||
targetStaticList.Sort(@CompareStaticItems);
|
||
AStatic.UpdatePos(AX, AY, AStatic.Z);
|
||
AStatic.Owner := targetBlock;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TLandscape.PrepareBlocks(AX1, AY1, AX2, AY2: Word);
|
||
var
|
||
x, y, i: Integer;
|
||
coords: TBlockCoordsArray;
|
||
block: TBlock;
|
||
begin
|
||
AX1 := EnsureRange(AX1, 0, FWidth - 1);
|
||
AY1 := EnsureRange(AY1, 0, FHeight - 1);
|
||
AX2 := EnsureRange(AX2, 0, FWidth - 1);
|
||
AY2 := EnsureRange(AY2, 0, FHeight - 1);
|
||
|
||
SetLength(coords, 0);
|
||
for x := AX1 to AX2 do
|
||
begin
|
||
for y := AY1 to AY2 do
|
||
begin
|
||
if (not FOpenRequests[y * FWidth + x]) and
|
||
(not FBlockCache.QueryID(GetID(x, y), block)) then
|
||
begin
|
||
SetLength(coords, Length(coords) + 1);
|
||
i := High(coords);
|
||
coords[i].X := x;
|
||
coords[i].Y := y;
|
||
FOpenRequests[y * FWidth + x] := True;
|
||
end;
|
||
end;
|
||
end;
|
||
if Length(coords) > 0 then
|
||
dmNetwork.Send(TRequestBlocksPacket.Create(coords));
|
||
end;
|
||
|
||
procedure TLandscape.UpdateBlockAccess;
|
||
var
|
||
cacheEntry: TBlockCache.PCacheEntry;
|
||
begin
|
||
cacheEntry := nil;
|
||
while FBlockCache.Iterate(cacheEntry) do
|
||
if cacheEntry^.Obj <> nil then
|
||
cacheEntry^.Obj.UpdateBlockAcess(Self);
|
||
end;
|
||
|
||
procedure TLandscape.UpdateWriteMap(AStream: TEnhancedMemoryStream);
|
||
var
|
||
x1, y1, x2, y2: Word;
|
||
i, areaCount, cellX, cellY: Integer;
|
||
begin
|
||
Logger.EnterMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
|
||
|
||
areaCount := AStream.ReadWord;
|
||
Logger.Send([lcLandscape, lcDebug], 'AreaCount', areaCount);
|
||
|
||
if areaCount > 0 then
|
||
begin
|
||
FWriteMap.Clearall;
|
||
for i := 0 to areaCount - 1 do
|
||
begin
|
||
x1 := AStream.ReadWord;
|
||
y1 := AStream.ReadWord;
|
||
x2 := AStream.ReadWord;
|
||
y2 := AStream.ReadWord;
|
||
for cellX := x1 to x2 do
|
||
for cellY := y1 to y2 do
|
||
FWriteMap[cellX * FCellHeight + cellY] := True;
|
||
end;
|
||
end else
|
||
for i := 0 to FWriteMap.Size - 1 do
|
||
FWriteMap[i] := True;
|
||
|
||
Logger.Send([lcLandscape, lcDebug], 'WriteMap @ 0,0', FWriteMap[0]);
|
||
|
||
UpdateBlockAccess;
|
||
Logger.ExitMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
|
||
end;
|
||
|
||
procedure TLandscape.ResizeBlockCache(Count: Integer);
|
||
begin
|
||
if Count < 0 then Count:= 256;
|
||
if FBlockCache <> nil then
|
||
begin
|
||
if BlockCacheSize = Count then exit;
|
||
|
||
FBlockCache.OnRemoveObject := nil;
|
||
if (FBlockCacheBak <> nil) then begin
|
||
FBlockCacheBak.Clear;
|
||
FreeAndNil(FBlockCacheBak);
|
||
FBlockCacheBak := nil;
|
||
end;
|
||
if Count = 0 then exit;
|
||
FBlockCacheBak := FBlockCache;
|
||
|
||
BlockCacheSize:= Count;
|
||
FBlockCache := TBlockCache.Create(BlockCacheSize); //256
|
||
FBlockCache.OnRemoveObject := @OnRemoveCachedObject;
|
||
end;
|
||
end;
|
||
|
||
{ TMaterial }
|
||
|
||
constructor TMaterial.Create;
|
||
begin
|
||
FRefCount := 1;
|
||
end;
|
||
|
||
destructor TMaterial.Destroy;
|
||
begin
|
||
FreeAndNil(FGraphic);
|
||
inherited Destroy;
|
||
end;
|
||
|
||
class procedure TMaterial.CalculateTextureDimensions(ACaps: TGLTextureCaps;
|
||
ARealWidth, ARealHeight: Integer; out AWidth, AHeight: Integer);
|
||
begin
|
||
if ACaps.NonPowerOfTwo then
|
||
begin
|
||
AWidth := ARealWidth;
|
||
AHeight := ARealHeight;
|
||
end else
|
||
begin
|
||
if IsPow2(ARealWidth) then
|
||
AWidth := ARealWidth
|
||
else
|
||
AWidth := NextPow2(ARealWidth);
|
||
|
||
if IsPow2(ARealHeight) then
|
||
AHeight := ARealHeight
|
||
else
|
||
AHeight := NextPow2(ARealHeight);
|
||
end;
|
||
end;
|
||
|
||
function TMaterial.GenerateTexture(AImage: TBaseImage): TGLuint;
|
||
begin
|
||
Result := CreateGLTextureFromImage(AImage.ImageDataPointer^);
|
||
glBindTexture(GL_TEXTURE_2D, Result);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||
end;
|
||
|
||
procedure TMaterial.AddRef;
|
||
begin
|
||
Inc(FRefCount);
|
||
end;
|
||
|
||
procedure TMaterial.DelRef;
|
||
begin
|
||
Dec(FRefCount);
|
||
if FRefCount < 1 then
|
||
Free;
|
||
end;
|
||
|
||
function TMaterial.HitTest(AX, AY: Integer; Zoom: Single): Boolean;
|
||
begin
|
||
AX := Trunc(AX / Zoom);
|
||
AY := Trunc(AY / Zoom);
|
||
Result := HitTest(AX, AY);
|
||
end;
|
||
|
||
function TMaterial.HitTest(AX, AY: Integer): Boolean;
|
||
var
|
||
pixel: TColor32Rec;
|
||
begin
|
||
Result := False;
|
||
if InRange(AX, 0, FGraphic.Width - 1) and
|
||
InRange(AY, 0, FGraphic.Height - 1) then
|
||
begin
|
||
pixel := GetPixel32(FGraphic.ImageDataPointer^, AX, AY);
|
||
if pixel.A > 0 then
|
||
Result := True;
|
||
end;
|
||
end;
|
||
|
||
function TMaterial.CanBeRemoved: Boolean;
|
||
begin
|
||
Result := FRefCount <= 1;
|
||
end;
|
||
|
||
procedure TMaterial.RemoveFromCache;
|
||
begin
|
||
DelRef;
|
||
end;
|
||
|
||
{ TScreenBuffer }
|
||
|
||
constructor TScreenBuffer.Create;
|
||
begin
|
||
inherited Create;
|
||
FCount := 0;
|
||
FSerial := 0;
|
||
UpdateShortcuts;
|
||
end;
|
||
|
||
destructor TScreenBuffer.Destroy;
|
||
begin
|
||
Clear;
|
||
inherited Destroy;
|
||
end;
|
||
|
||
function TScreenBuffer.Add(AItem: TWorldItem): PBlockInfo;
|
||
begin
|
||
New(Result);
|
||
AItem.Locked := True;
|
||
AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
|
||
Result^.Item := AItem;
|
||
Result^.HighRes := nil;
|
||
Result^.LowRes := nil;
|
||
Result^.Normals := nil;
|
||
Result^.State := ssNormal;
|
||
Result^.Highlighted := False;
|
||
Result^.Translucent := False;
|
||
Result^.Text := nil;
|
||
Result^.Next := nil;
|
||
|
||
if FShortCuts[0] = nil then //First element
|
||
begin
|
||
FShortCuts[0] := Result;
|
||
FShortCuts[-1] := Result; //Last element
|
||
end else
|
||
begin
|
||
FShortCuts[-1]^.Next := Result;
|
||
FShortCuts[-1] := Result;
|
||
end;
|
||
|
||
Inc(FCount);
|
||
end;
|
||
|
||
procedure TScreenBuffer.Clear;
|
||
var
|
||
current, next: PBlockInfo;
|
||
begin
|
||
current := FShortCuts[0];
|
||
while current <> nil do
|
||
begin
|
||
next := current^.Next;
|
||
current^.Item.Locked := False;
|
||
current^.Item.OnDestroy.UnregisterEvent(@OnTileRemoved);
|
||
if current^.Normals <> nil then Dispose(current^.Normals);
|
||
if current^.HighRes <> nil then current^.HighRes.DelRef;
|
||
if current^.LowRes <> nil then current^.LowRes.DelRef;
|
||
current^.Text.Free;
|
||
Dispose(current);
|
||
current := next;
|
||
end;
|
||
FShortCuts[0] := nil;
|
||
FShortCuts[-1] := nil;
|
||
|
||
FCount := 0;
|
||
FSerial := 0;
|
||
|
||
UpdateShortcuts;
|
||
end;
|
||
|
||
procedure TScreenBuffer.Delete(AItem: TWorldItem);
|
||
var
|
||
current, last, next: PBlockInfo;
|
||
begin
|
||
last := nil;
|
||
current := FShortCuts[0];
|
||
while current <> nil do
|
||
begin
|
||
if current^.Item = AItem then
|
||
begin
|
||
if FShortCuts[-1] = current then FShortCuts[-1] := last;
|
||
if FShortCuts[0] = current then FShortCuts[0] := current^.Next;
|
||
if last <> nil then last^.Next := current^.Next;
|
||
|
||
if current^.Normals <> nil then Dispose(current^.Normals);
|
||
if current^.HighRes <> nil then current^.HighRes.DelRef;
|
||
if current^.LowRes <> nil then current^.LowRes.DelRef;
|
||
current^.Text.Free;
|
||
|
||
Dispose(current);
|
||
Dec(FCount);
|
||
FShortCutsValid := False;
|
||
next := nil;
|
||
end else
|
||
next := current^.Next;
|
||
|
||
last := current;
|
||
current := next;
|
||
end;
|
||
end;
|
||
|
||
function TScreenBuffer.Find(AScreenPosition: TPoint; Zoom: Single): PBlockInfo;
|
||
var
|
||
current: PBlockInfo;
|
||
buff: array[0..3] of GLuint;
|
||
begin
|
||
Result := nil;
|
||
current := FShortCuts[0];
|
||
while current <> nil do //search the last matching tile
|
||
begin
|
||
if (current^.State = ssNormal) and
|
||
PtInRect(current^.ScreenRect, AScreenPosition)then
|
||
begin
|
||
if current^.CheckRealQuad then
|
||
begin
|
||
//OpenGL hit test
|
||
//We use the "real quad" here to prevent the draw-preview from
|
||
//intercepting with our actual tiles (which are "hidden" then).
|
||
glSelectBuffer(4, @buff[0]);
|
||
glViewport(current^.ScreenRect.Left, current^.ScreenRect.Top,
|
||
current^.ScreenRect.Right, current^.ScreenRect.Bottom);
|
||
glRenderMode(GL_SELECT);
|
||
glInitNames;
|
||
glPushName(0);
|
||
|
||
glPushMatrix;
|
||
glMatrixMode(GL_PROJECTION);
|
||
glLoadIdentity;
|
||
gluOrtho2D(AScreenPosition.x, AScreenPosition.x + 1,
|
||
AScreenPosition.y + 1, AScreenPosition.y);
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glLoadIdentity;
|
||
|
||
glBegin(GL_QUADS);
|
||
glVertex2iv(@current^.RealQuad[0]);
|
||
glVertex2iv(@current^.RealQuad[3]);
|
||
glVertex2iv(@current^.RealQuad[2]);
|
||
glVertex2iv(@current^.RealQuad[1]);
|
||
glEnd;
|
||
glPopMatrix;
|
||
glFlush;
|
||
|
||
if glRenderMode(GL_RENDER) > 0 then //glRenderMode now returns the number of hits
|
||
Result := current;
|
||
end else
|
||
if current^.LowRes.HitTest(AScreenPosition.x - current^.ScreenRect.Left,
|
||
AScreenPosition.y - current^.ScreenRect.Top, Zoom) then
|
||
Result := current;
|
||
end;
|
||
current := current^.Next;
|
||
end;
|
||
end;
|
||
|
||
function TScreenBuffer.Find(AX, AY: Word): PBlockInfo;
|
||
var
|
||
current: PBlockInfo;
|
||
tile: TWorldItem;
|
||
staticTileData: TStaticTiledata;
|
||
tileZ: Integer;
|
||
begin
|
||
Result := nil;
|
||
current := FShortCuts[0];
|
||
while current <> nil do //search the last matching tile
|
||
begin
|
||
if current^.State = ssNormal then begin
|
||
tile := current^.Item;
|
||
if (tile.X = AX) and (tile.Y = AY) then begin
|
||
|
||
tileZ := $FFFF;
|
||
if tile is TStaticItem then begin
|
||
staticTileData := ResMan.Tiledata.StaticTiles[tile.TileID];
|
||
if tdfSurface in staticTileData.Flags then
|
||
tileZ := tile.Z + staticTileData.Height;
|
||
end else if tile is TMapCell then begin
|
||
tileZ := ResMan.Landscape.GetEffectiveAltitude(tile as TMapCell)
|
||
end else if tile is TVirtualTile then
|
||
tileZ := tile.Z;
|
||
|
||
if (tileZ <> $FFFF) and ((Result = nil) or (tile.Z > Result^.Item.Z)) then
|
||
Result := current;
|
||
end;
|
||
end;
|
||
current := current^.Next;
|
||
end;
|
||
end;
|
||
|
||
function TScreenBuffer.GetSerial: Cardinal;
|
||
begin
|
||
Result := FSerial;
|
||
Inc(FSerial);
|
||
end;
|
||
|
||
function TScreenBuffer.Insert(AItem: TWorldItem): PBlockInfo;
|
||
var
|
||
current: PBlockInfo;
|
||
shortcut: Integer;
|
||
begin
|
||
if not FShortCutsValid then
|
||
UpdateShortcuts;
|
||
|
||
New(Result);
|
||
AItem.Locked := True;
|
||
AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
|
||
Result^.Item := AItem;
|
||
Result^.HighRes := nil;
|
||
Result^.LowRes := nil;
|
||
Result^.Normals := nil;
|
||
Result^.State := ssNormal;
|
||
Result^.Highlighted := False;
|
||
Result^.Translucent := False;
|
||
Result^.Text := nil;
|
||
|
||
if (FShortCuts[0] = nil) or (CompareWorldItems(AItem, FShortCuts[0]^.Item) < 0) then
|
||
begin
|
||
if FShortCuts[0] = nil then
|
||
FShortCuts[-1] := Result; //Update last item
|
||
|
||
Result^.Next := FShortCuts[0];
|
||
FShortCuts[0] := Result;
|
||
end else
|
||
begin
|
||
//find best entry point
|
||
shortcut := 0;
|
||
while (shortcut <= 10) and (FShortCuts[shortcut] <> nil) and
|
||
(CompareWorldItems(AItem, FShortCuts[shortcut]^.Item) >= 0) do
|
||
begin
|
||
current := FShortCuts[shortcut];
|
||
Inc(shortcut);
|
||
end;
|
||
|
||
//now find the real match
|
||
while (current^.Next <> nil) and
|
||
(CompareWorldItems(AItem, current^.Next^.Item) > 0) do
|
||
begin
|
||
current := current^.Next;
|
||
end;
|
||
|
||
if FShortCuts[-1] = current^.Next then
|
||
FShortCuts[-1] := Result; //Update last item
|
||
|
||
Result^.Next := current^.Next;
|
||
current^.Next := Result;
|
||
end;
|
||
|
||
Inc(FCount);
|
||
end;
|
||
|
||
function TScreenBuffer.Iterate(var ABlockInfo: PBlockInfo): Boolean;
|
||
begin
|
||
if ABlockInfo = nil then
|
||
ABlockInfo := FShortCuts[0]
|
||
else
|
||
ABlockInfo := ABlockInfo^.Next;
|
||
Result := ABlockInfo <> nil;
|
||
end;
|
||
|
||
procedure TScreenBuffer.UpdateShortcuts;
|
||
var
|
||
shortcut, step, nextStep, stepSize: Integer;
|
||
blockInfo: PBlockInfo;
|
||
begin
|
||
if FCount < 10 then
|
||
begin
|
||
for shortcut := 1 to 10 do
|
||
FShortCuts[shortcut] := nil;
|
||
end
|
||
else if FShortCuts[0] <> nil then
|
||
begin
|
||
stepSize := FCount div 10;
|
||
nextStep := stepSize;
|
||
step := 0;
|
||
shortcut := 1;
|
||
blockInfo := FShortCuts[0];
|
||
repeat
|
||
if (step = nextStep) and (shortcut <= 10) then
|
||
begin
|
||
FShortCuts[shortcut] := blockInfo;
|
||
Inc(shortcut);
|
||
Inc(nextStep, stepSize);
|
||
end;
|
||
|
||
Inc(step);
|
||
|
||
FShortCuts[-1] := blockInfo; //update last known item
|
||
blockInfo := blockInfo^.Next;
|
||
until (blockInfo = nil);
|
||
end;
|
||
FShortCutsValid := True;
|
||
end;
|
||
|
||
function TScreenBuffer.UpdateSortOrder(AItem: TWorldItem): PBlockInfo;
|
||
var
|
||
newNodePosition, oldNode, oldNodePrev, current: PBlockInfo;
|
||
begin
|
||
newNodePosition := nil;
|
||
oldNode := nil;
|
||
oldNodePrev := nil;
|
||
current := FShortCuts[0];
|
||
|
||
while (current <> nil) and ((oldNode = nil) or (newNodePosition = nil)) do
|
||
begin
|
||
if current^.Item = AItem then
|
||
oldNode := current
|
||
else if oldNode = nil then
|
||
oldNodePrev := current;
|
||
|
||
if newNodePosition = nil then
|
||
begin
|
||
if (current^.Next = nil) or (CompareWorldItems(AItem, current^.Next^.Item) < 0) then
|
||
newNodePosition := current;
|
||
end;
|
||
|
||
current := current^.Next;
|
||
end;
|
||
|
||
//oldNode = nil, if the change happend out-of-screen
|
||
if (oldNode <> nil ) and (oldNode <> newNodePosition) then
|
||
begin
|
||
if oldNodePrev <> oldNode then
|
||
begin
|
||
if oldNodePrev = nil then
|
||
FShortCuts[0] := oldNode^.Next
|
||
else
|
||
oldNodePrev^.Next := oldNode^.Next;
|
||
end;
|
||
|
||
if (newNodePosition = FShortCuts[0]) and (CompareWorldItems(AItem, FShortCuts[0]^.Item) < 0) then
|
||
begin
|
||
oldNode^.Next := FShortCuts[0];
|
||
FShortCuts[0] := oldNode;
|
||
end else
|
||
begin
|
||
oldNode^.Next := newNodePosition^.Next;
|
||
newNodePosition^.Next := oldNode;
|
||
end;
|
||
end;
|
||
|
||
Result := oldNode;
|
||
end;
|
||
|
||
procedure TScreenBuffer.OnTileRemoved(ATile: TMulBlock);
|
||
begin
|
||
Delete(TWorldItem(ATile));
|
||
end;
|
||
|
||
{ TGLText }
|
||
|
||
constructor TGLText.Create(AFont: TGLFont; AText: String);
|
||
begin
|
||
FFont := AFont;
|
||
FText := AText;
|
||
FWidth := FFont.GetTextWidth(AText);
|
||
FHeight := FFont.GetTextHeight('A');
|
||
end;
|
||
|
||
procedure TGLText.Render(AScreenRect: TRect);
|
||
var
|
||
x, y: Integer;
|
||
begin
|
||
y := AScreenRect.Top + (AScreenRect.Bottom - AScreenRect.Top - FHeight) div 2;
|
||
x := AScreenRect.Left + (AScreenRect.Right - AScreenRect.Left - FWidth) div 2;
|
||
FFont.DrawText(x, y, FText);
|
||
end;
|
||
|
||
{ TSimpleMaterial }
|
||
|
||
constructor TSimpleMaterial.Create(AGraphic: TBaseImage);
|
||
var
|
||
caps: TGLTextureCaps;
|
||
begin
|
||
inherited Create;
|
||
FRealWidth := AGraphic.Width;
|
||
FRealHeight := AGraphic.Height;
|
||
|
||
GetGLTextureCaps(caps);
|
||
CalculateTextureDimensions(caps, FRealWidth, FRealHeight, FWidth, FHeight);
|
||
FGraphic := TMultiImage.CreateFromParams(FWidth, FHeight, ifA8R8G8B8, 1);
|
||
AGraphic.CopyTo(0, 0, FRealWidth, FRealHeight, FGraphic, 0, 0);
|
||
FTexture := GenerateTexture(FGraphic);
|
||
end;
|
||
|
||
destructor TSimpleMaterial.Destroy;
|
||
begin
|
||
if FTexture <> 0 then glDeleteTextures(1, @FTexture);
|
||
inherited Destroy;
|
||
end;
|
||
|
||
function TSimpleMaterial.GetTexture: GLuint;
|
||
begin
|
||
Result := FTexture;
|
||
end;
|
||
|
||
{ TAnimMaterial }
|
||
|
||
constructor TAnimMaterial.Create(ABaseID: LongWord; AAnimData: TAnimData;
|
||
AHue: THue = nil; APartialHue: Boolean = False);
|
||
var
|
||
i: Integer;
|
||
art: array of TArt;
|
||
caps: TGLTextureCaps;
|
||
begin
|
||
inherited Create;
|
||
|
||
FAnimData := AAnimData;
|
||
|
||
FRealWidth := 0;
|
||
FRealHeight := 0;
|
||
|
||
SetLength(FTextures, AAnimData.FrameCount);
|
||
SetLength(art, AAnimData.FrameCount);
|
||
|
||
for i := 0 to AAnimData.FrameCount - 1 do
|
||
begin
|
||
art[i] := ResMan.Art.GetArt(ABaseID + AAnimData.FrameData[i], 0, AHue,
|
||
APartialHue);
|
||
|
||
if art[i].Graphic.Width > FRealWidth then
|
||
FRealWidth := art[i].Graphic.Width;
|
||
if art[i].Graphic.Height > FRealHeight then
|
||
FRealHeight := art[i].Graphic.Height;
|
||
end;
|
||
|
||
GetGLTextureCaps(caps);
|
||
CalculateTextureDimensions(caps, FRealWidth, FRealHeight, FWidth, FHeight);
|
||
FGraphic := TMultiImage.CreateFromParams(FWidth, FHeight, ifA8R8G8B8,
|
||
AAnimData.FrameCount);
|
||
|
||
for i := 0 to AAnimData.FrameCount - 1 do
|
||
begin
|
||
FGraphic.ActiveImage := i;
|
||
art[i].Graphic.CopyTo(0, 0, art[i].Graphic.Width, art[i].Graphic.Height,
|
||
FGraphic, 0, 0);
|
||
FTextures[i] := GenerateTexture(FGraphic);
|
||
art[i].Free;
|
||
end;
|
||
|
||
FGraphic.ActiveImage := 0;
|
||
FActiveFrame := 0;
|
||
FNextChange := GetTickCount + AAnimData.FrameStart * 100;
|
||
end;
|
||
|
||
destructor TAnimMaterial.Destroy;
|
||
begin
|
||
glDeleteTextures(Length(FTextures), @FTextures[0]);
|
||
inherited Destroy;
|
||
end;
|
||
|
||
function TAnimMaterial.GetTexture: GLuint;
|
||
begin
|
||
if FNextChange <= GetTickCount then
|
||
begin
|
||
FActiveFrame := (FActiveFrame + 1) mod FAnimData.FrameCount;
|
||
FGraphic.ActiveImage := FActiveFrame;
|
||
|
||
if FActiveFrame = 0 then
|
||
FNextChange := GetTickCount + FAnimData.FrameStart * 100
|
||
else
|
||
FNextChange:= GetTickCount + FAnimData.FrameInterval * 100;
|
||
end;
|
||
|
||
Result := FTextures[FActiveFrame];
|
||
end;
|
||
|
||
end.
|
||
|