LZScene

Форк
0
/
GLHeightData.pas 
2292 строки · 67.5 Кб
1
//
2
// This unit is part of the GLScene Engine https://github.com/glscene
3
//
4
{
5
  Classes for height data access.
6

7
  The components and classes in the unit are the core data providers for
8
  height-based objects (terrain rendering mainly), they are independant
9
  from the rendering stage.
10

11
  In short: access to raw height data is performed by a TGLHeightDataSource
12
  subclass, that must take care of performing all necessary data access,
13
  cacheing and manipulation to provide TGLHeightData objects. A TGLHeightData
14
  is basicly a square, power of two dimensionned raster heightfield, and
15
  holds the data a renderer needs.
16

17
   History :  
18
   10/01/13 - PW - Added CPP compatibility: considered sensitivity to upper case characters in identifiers
19
   18/07/10 - Yar - Improved FPC compatibility (thanks to Rustam Asmandiarov aka Predator)
20
   17/07/07 - LIN - Bugfix: hdsNone tiles were not being released. (Now also deletes Queued tiles that are no longer needed).
21
   17/07/07 - LIN - Reversed the order in which Queued tiles are prepared.
22
   03/04/07 - DaStr - Commented out lines that caused compiler hints
23
  Added more explicit pointer dereferencing
24
  Renamed GLS_DELPHI_5_UP to GLS_DELPHI_4_DOWN for
25
  FPC compatibility (thanks Burkhard Carstens)
26
   27/03/07 - LIN- Data is now prepared in 3 stages, to prevent multi-threading issues:
27
  -BeforePreparingData : (Main Thread) - Create empty data structures and textures here.
28
  -PreparingData       : (Sub-Thread)  - Fill in the empty structures (MUST be thread safe)
29
  -AfterPreparingData  : (Main Thread) - Perform any cleanup, which cant be done from a sub-thread
30
   17/03/07 - DaStr - Dropped Kylix support in favor of FPC (BugTracekrID=1681585)
31
   14/03/07 - DaStr - Added explicit pointer dereferencing
32
  (thanks Burkhard Carstens) (Bugtracker ID = 1678644)
33
   13/02/07 - LIN- Added TGLHeightDataSource.TextureCoordinates -
34
  Called from TGLBitmapHDS and TGLHeightTileFileHDS
35
  Many tweaks and changes to threading. (I hope I havent broken anything)
36
   02/02/07 - LIN- Added TGLHeightDataSourceFilter
37
   30/01/07 - LIN- Added GLHeightData.LibMaterial. (Use instead of MaterialName)
38
  GLHeightData is now derived from TGLUpdateAbleObject
39
  GLHeightData is now compatible with TGLLibMaterials.DeleteUnusedMaterials
40
   19/01/07 - LIN- Added 'Inverted' property to TGLBitmapHDS
41
   10/08/04 - SG - TGLHeightData.InterpolatedHeight fix (Alan Rose)
42
   03/07/04 - LR - Corrections for Linux compatibility
43
  CreateMonochromeBitmap NOT implemented for Linux
44
   12/07/03 - EG - Further InterpolatedHeight fixes
45
   26/06/03 - EG - Fixed InterpolatedHeight HDS selection
46
   06/02/03 - EG - Added Hash index to HeightDataSource, HeightMin/Max
47
   24/01/03 - EG - Fixed ByteHeight normalization scaling
48
   07/01/03 - JJ - fixed InterpolatedHeight... Old code left in comment...
49
   03/12/02 - EG - Added hdtDefault, InterpolatedHeight/Dirty fix (Phil Scadden)
50
   25/08/02 - EG - TGLHeightData.MarkData/Release fix (Phil Scadden)
51
   10/07/02 - EG - Support for non-wrapping TGLBitmapHDS
52
   16/06/02 - EG - Changed HDS destruction sequence (notification-safe),
53
  TGLHeightData now has a MaterialName property
54
   24/02/02 - EG - Faster Cleanup & cache management
55
   21/02/02 - EG - hdtWord replaced by hdtSmallInt, added MarkDirty
56
   04/02/02 - EG - CreateMonochromeBitmap now shielded against Jpeg "Change" oddity
57
   10/09/01 - EG - Added TGLTerrainBaseHDS
58
   04/03/01 - EG - Added InterpolatedHeight
59
   11/02/01 - EG - Creation
60
   
61
}
62
unit GLHeightData;
63

64
interface
65

66
{$I GLScene.inc}
67

68
uses
69
  Classes, SysUtils,
70
{$IFDEF MSWINDOWS}
71
  Windows, // for CreateMonochromeBitmap
72
{$ENDIF}
73
  IntfGraphics,
74
  GLApplicationFileIO, GLUtils,
75
  GLVectorGeometry, GLCrossPlatform, GLMaterial, GLBaseClasses;
76

77
type
78
  TByteArray = array [0 .. MaxInt div (2 * SizeOf(Byte))] of Byte;
79
  TByteRaster = array [0 .. MaxInt div (2 * SizeOf(Pointer))] of PByteArray;
80
  PByteRaster = ^TByteRaster;
81
  TSmallintArray = array [0 .. MaxInt div (2 * SizeOf(SmallInt))] of SmallInt;
82
  PSmallIntArray = ^TSmallintArray;
83
  TSmallIntRaster = array [0 .. MaxInt div (2 * SizeOf(Pointer))
84
    ] of PSmallIntArray;
85
  PSmallIntRaster = ^TSmallIntRaster;
86
  TSingleRaster = array [0 .. MaxInt div (2 * SizeOf(Pointer))] of PSingleArray;
87
  PSingleRaster = ^TSingleRaster;
88

89
  TGLHeightData = class;
90
  TGLHeightDataClass = class of TGLHeightData;
91

92
  // TGLHeightDataType
93
  //
94
  { : Determines the type of data stored in a TGLHeightData.
95
    There are 3 data types (8 bits unsigned, signed 16 bits and 32 bits).
96
    Conversions: (128*(ByteValue-128)) = SmallIntValue = Round(SingleValue).
97
    The 'hdtDefault' type is used for request only, and specifies that the
98
    default type for the source should be used. }
99
  TGLHeightDataType = (hdtByte, hdtSmallInt, hdtSingle, hdtDefault);
100

101
  // TGLHeightDataSource
102
  //
103
  { : Base class for height datasources.
104
    This class is abstract and presents the standard interfaces for height
105
    data retrieval (TGLHeightData objects). The class offers the following
106
    features (that a subclass may decide to implement or not, what follow
107
    is the complete feature set, check subclass doc to see what is actually
108
    supported): 
109
     Pooling / Cacheing (return a TGLHeightData with its "Release" method)
110
     Pre-loading : specify a list of TGLHeightData you want to preload
111
     Multi-threaded preload/queueing : specified list can be loaded in
112
    a background task.
113
    </p> }
114

115
  TGLHeightDataSource = class(TComponent)
116
  private
117
     
118
    FData: TThreadList; // stores all TGLHeightData, whatever their state/type
119
    FDataHash: array [0 .. 255] of TList; // X/Y hash references for HeightDatas
120
    FThread: TThread; // queue manager
121
    FMaxThreads: Integer;
122
    FMaxPoolSize: Integer;
123
    FHeightDataClass: TGLHeightDataClass;
124
    // FReleaseLatency : TDateTime;      //Not used anymore???
125
    FDefaultHeight: Single;
126
  protected
127
     
128
    procedure SetMaxThreads(const Val: Integer);
129

130
    function HashKey(XLeft, YTop: Integer): Integer;
131

132
    { : Adjust this property in you subclasses. }
133
    property HeightDataClass: TGLHeightDataClass read FHeightDataClass
134
      write FHeightDataClass;
135

136
    { : Looks up the list and returns the matching TGLHeightData, if any. }
137
    function FindMatchInList(XLeft, YTop, size: Integer;
138
      DataType: TGLHeightDataType): TGLHeightData;
139
  public
140
     
141

142
    constructor Create(AOwner: TComponent); override;
143
    destructor Destroy; override;
144

145
    { : Access to currently pooled TGLHeightData objects, and Thread locking }
146
    property Data: TThreadList read FData;
147

148
    { : Empties the Data list, terminating thread if necessary.
149
      If some TGLHeightData are hdsInUse, triggers an exception and does
150
      nothing. }
151
    procedure Clear;
152
    { : Removes less used TDataHeight objects from the pool.
153
      Only removes objects whose state is hdsReady and UseCounter is zero,
154
      starting from the end of the list until total data size gets below
155
      MaxPoolSize (or nothing can be removed). }
156
    procedure CleanUp;
157

158
    { : Base TGLHeightData requester method.
159
      Returns (by rebuilding it or from the cache) a TGLHeightData
160
      corresponding to the given area. Size must be a power of two.
161
      Subclasses may choose to publish it or just publish datasource-
162
      specific requester method using specific parameters. }
163
    function GetData(XLeft, YTop, size: Integer; DataType: TGLHeightDataType)
164
      : TGLHeightData; virtual;
165
    { : Preloading request.
166
      See GetData for details. }
167
    function PreLoad(XLeft, YTop, size: Integer; DataType: TGLHeightDataType)
168
      : TGLHeightData; virtual;
169

170
    { : Replacing dirty tiles. }
171
    procedure PreloadReplacement(aHeightData: TGLHeightData);
172

173
    { : Notification that the data is no longer used by the renderer.
174
      Default behaviour is just to change DataState to hdsReady (ie. return
175
      the data to the pool) }
176
    procedure Release(aHeightData: TGLHeightData); virtual;
177
    { : Marks the given area as "dirty" (ie source data changed).
178
      All loaded and in-cache tiles overlapping the area are flushed. }
179
    procedure MarkDirty(const Area: TGLRect); overload; virtual;
180
    procedure MarkDirty(XLeft, YTop, xRight, yBottom: Integer); overload;
181
    procedure MarkDirty; overload;
182

183
    { : Maximum number of background threads.
184
      If 0 (zero), multithreading is disabled and StartPreparingData
185
      will be called from the mainthread, and all preload requirements
186
      (queued TGLHeightData objects) will be loaded in sequence from
187
      the main thread.
188
      If 1, basic multithreading and queueing gets enabled,
189
      ie. StartPreparingData will be called from a thread, but from one
190
      thread only (ie. there is no need to implement a TGLHeightDataThread,
191
      just make sure StartPreparingData code is thread-safe).
192
      Other values (2 and more) are relevant only if you implement
193
      a TGLHeightDataThread subclass and fire it in StartPreparingData. }
194
    property MaxThreads: Integer read FMaxThreads write SetMaxThreads;
195
    { : Maximum Size of TDataHeight pool in bytes.
196
      The pool (cache) can actually get larger if more data than the pool
197
      can accomodate is used, but as soon as data gets released and returns
198
      to the pool, TDataHeight will be freed until total pool Size gets
199
      below this figure. 
200
      The pool manager frees TDataHeight objects who haven't been requested
201
      for the longest time first.
202
      The default value of zero effectively disables pooling. }
203
    property MaxPoolSize: Integer read FMaxPoolSize write FMaxPoolSize;
204
    { : Height to return for undefined tiles. }
205
    property DefaultHeight: Single read FDefaultHeight write FDefaultHeight;
206

207
    { : Interpolates height for the given point. }
208
    function InterpolatedHeight(x, y: Single; tileSize: Integer)
209
      : Single; virtual;
210

211
    function Width: Integer; virtual; abstract;
212
    function Height: Integer; virtual; abstract;
213
    procedure ThreadIsIdle; virtual;
214

215
    { : This is called BEFORE StartPreparing Data, but always from the main thread. }
216
    procedure BeforePreparingData(HeightData: TGLHeightData); virtual;
217

218
    { : Request to start preparing data.
219
      If your subclass is thread-enabled, this is here that you'll create
220
      your thread and fire it (don't forget the requirements), if not,
221
      that'll be here you'll be doing your work. 
222
      Either way, you are responsible for adjusting the DataState to
223
      hdsReady when you're done (DataState will be hdsPreparing when this
224
      method will be invoked). }
225
    procedure StartPreparingData(HeightData: TGLHeightData); virtual;
226

227
    { : This is called After "StartPreparingData", but always from the main thread. }
228
    procedure AfterPreparingData(HeightData: TGLHeightData); virtual;
229

230
    procedure TextureCoordinates(HeightData: TGLHeightData;
231
      Stretch: boolean = false);
232
  end;
233

234
  // THDTextureCoordinatesMode
235
  //
236
  THDTextureCoordinatesMode = (tcmWorld, tcmLocal);
237

238
  // TGLHeightDataState
239
  //
240
  { : Possible states for a TGLHeightData.
241
     
242
     hdsQueued : the data has been queued for loading
243
     hdsPreparing : the data is currently loading or being prepared for use
244
     hdsReady : the data is fully loaded and ready for use
245
     hdsNone : the height data does not exist for this tile
246
      }
247
  TGLHeightDataState = (hdsQueued, hdsPreparing, hdsReady, hdsNone);
248

249
  TGLHeightDataThread = class;
250
  TOnHeightDataDirtyEvent = procedure(sender: TGLHeightData) of object;
251

252
  TGLHeightDataUser = record
253
    user: TObject;
254
    event: TOnHeightDataDirtyEvent;
255
  end;
256

257
  // TGLHeightData
258
  //
259
  { : Base class for height data, stores a height-field raster.
260
    The raster is a square, whose Size must be a power of two. Data can be
261
    accessed through a base pointer ("ByteData[n]" f.i.), or through pointer
262
    indirections ("ByteRaster[y][x]" f.i.), this are the fastest way to access
263
    height data (and the most unsecure). 
264
    Secure (with range checking) data access is provided by specialized
265
    methods (f.i. "ByteHeight"), in which coordinates (x & y) are always
266
    considered relative (like in raster access).
267
    The class offers conversion facility between the types (as a whole data
268
    conversion), but in any case, the TGLHeightData should be directly requested
269
    from the TGLHeightDataSource with the appropriate format.
270
    Though this class can be instantiated, you will usually prefer to subclass
271
    it in real-world cases, f.i. to add texturing data. }
272
  // TGLHeightData = class (TObject)
273
  TGLHeightData = class(TGLUpdateAbleObject)
274
  private
275
     
276
    FUsers: array of TGLHeightDataUser;
277
    FOwner: TGLHeightDataSource;
278
    FDataState: TGLHeightDataState;
279
    FSize: Integer;
280
    FXLeft, FYTop: Integer;
281
    FUseCounter: Integer;
282
    FDataType: TGLHeightDataType;
283
    FDataSize: Integer;
284
    FByteData: PByteArray;
285
    FByteRaster: PByteRaster;
286
    FSmallIntData: PSmallIntArray;
287
    FSmallIntRaster: PSmallIntRaster;
288
    FSingleData: PSingleArray;
289
    FSingleRaster: PSingleRaster;
290
    FTextureCoordinatesMode: THDTextureCoordinatesMode;
291
    FTCOffset, FTCScale: TTexPoint;
292
    FMaterialName: String; // Unsafe. Use FLibMaterial instead
293
    FLibMaterial: TGLLibMaterial;
294
    FObjectTag: TObject;
295
    FTag, FTag2: Integer;
296
    FOnDestroy: TNotifyEvent;
297
    FDirty: boolean;
298
    FHeightMin, FHeightMax: Single;
299

300
    procedure BuildByteRaster;
301
    procedure BuildSmallIntRaster;
302
    procedure BuildSingleRaster;
303

304
    procedure ConvertByteToSmallInt;
305
    procedure ConvertByteToSingle;
306
    procedure ConvertSmallIntToByte;
307
    procedure ConvertSmallIntToSingle;
308
    procedure ConvertSingleToByte;
309
    procedure ConvertSingleToSmallInt;
310

311
  protected
312
     
313
    FThread: TGLHeightDataThread;
314
    // thread used for multi-threaded processing (if any)
315

316
    procedure SetDataType(const Val: TGLHeightDataType);
317
    procedure SetMaterialName(const MaterialName: string);
318
    procedure SetLibMaterial(LibMaterial: TGLLibMaterial);
319

320
    function GetHeightMin: Single;
321
    function GetHeightMax: Single;
322

323
  public
324
    OldVersion: TGLHeightData; // previous version of this tile
325
    NewVersion: TGLHeightData; // the replacement tile
326
    DontUse: boolean; // Tells TerrainRenderer which version to use
327

328
     
329

330
    // constructor Create(AOwner : TComponent); override;
331
    constructor Create(AOwner: TGLHeightDataSource; aXLeft, aYTop, aSize: Integer;
332
      aDataType: TGLHeightDataType); reintroduce; virtual;
333
    destructor Destroy; override;
334

335
    { : The component who created and maintains this data. }
336
    property Owner: TGLHeightDataSource read FOwner;
337

338
    { : Fired when the object is destroyed. }
339
    property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
340

341
    { : Counter for use registration.
342
      A TGLHeightData is not returned to the pool until this counter reaches
343
      a value of zero. }
344
    property UseCounter: Integer read FUseCounter;
345
    { : Increments UseCounter.
346
      User objects should implement a method that will be notified when
347
      the data becomes dirty, when invoked they should release the heightdata
348
      immediately after performing their own cleanups. }
349
    procedure RegisterUse;
350
    { : Allocate memory and prepare lookup tables for current datatype.
351
      Fails if already allocated. Made Dynamic to allow descendants }
352
    procedure Allocate(const Val: TGLHeightDataType); dynamic;
353
    { : Decrements UseCounter.
354
      When the counter reaches zero, notifies the Owner TGLHeightDataSource
355
      that the data is no longer used.
356
      The renderer should call Release when it no longer needs a THeighData,
357
      and never free/destroy the object directly. }
358
    procedure Release;
359
    { : Marks the tile as dirty.
360
      The immediate effect is currently the destruction of the tile. }
361
    procedure MarkDirty;
362

363
    { : World X coordinate of top left point. }
364
    property XLeft: Integer read FXLeft;
365
    { : World Y coordinate of top left point. }
366
    property YTop: Integer read FYTop;
367
    { : Type of the data.
368
      Assigning a new datatype will result in the data being converted. }
369
    property DataType: TGLHeightDataType read FDataType write SetDataType;
370
    { : Current state of the data. }
371
    property DataState: TGLHeightDataState read FDataState write FDataState;
372
    { : Size of the data square, in data units. }
373
    property Size: Integer read FSize;
374
    { : True if the data is dirty (ie. no longer up-to-date). }
375
    property Dirty: boolean read FDirty write FDirty;
376

377
    { : Memory Size of the raw data in bytes. }
378
    property DataSize: Integer read FDataSize;
379

380
    { : Access to data as a byte array (n = y*Size+x).
381
      If TGLHeightData is not of type hdtByte, this value is nil. }
382
    property ByteData: PByteArray read FByteData;
383
    { : Access to data as a byte raster (y, x).
384
      If TGLHeightData is not of type hdtByte, this value is nil. }
385
    property ByteRaster: PByteRaster read FByteRaster;
386
    { : Access to data as a SmallInt array (n = y*Size+x).
387
      If TGLHeightData is not of type hdtSmallInt, this value is nil. }
388
    property SmallIntData: PSmallIntArray read FSmallIntData;
389
    { : Access to data as a SmallInt raster (y, x).
390
      If TGLHeightData is not of type hdtSmallInt, this value is nil. }
391
    property SmallIntRaster: PSmallIntRaster read FSmallIntRaster;
392
    { : Access to data as a Single array (n = y*Size+x).
393
      If TGLHeightData is not of type hdtSingle, this value is nil. }
394
    property SingleData: PSingleArray read FSingleData;
395
    { : Access to data as a Single raster (y, x).
396
      If TGLHeightData is not of type hdtSingle, this value is nil. }
397
    property SingleRaster: PSingleRaster read FSingleRaster;
398

399
    { : Name of material for the tile (if terrain uses multiple materials). }
400
    // property MaterialName : String read FMaterialName write FMaterialName;
401
    // (WARNING: Unsafe when deleting textures! If possible, rather use LibMaterial.)
402
    property MaterialName: String read FMaterialName write SetMaterialName;
403
    // property LibMaterial : Links directly to the tile's TGLLibMaterial.
404
    // Unlike 'MaterialName', this property also registers the tile as
405
    // a user of the texture.
406
    // This prevents TGLLibMaterials.DeleteUnusedTextures from deleting the
407
    // used texture by mistake and causing Access Violations.
408
    // Use this instead of the old MaterialName property, to prevent AV's.
409
    property LibMaterial: TGLLibMaterial read FLibMaterial write SetLibMaterial;
410
    { : Texture coordinates generation mode.
411
      Default is tcmWorld coordinates. }
412
    property TextureCoordinatesMode: THDTextureCoordinatesMode
413
      read FTextureCoordinatesMode write FTextureCoordinatesMode;
414
    property TextureCoordinatesOffset: TTexPoint read FTCOffset write FTCOffset;
415
    property TextureCoordinatesScale: TTexPoint read FTCScale write FTCScale;
416
    { : Height of point x, y as a Byte. }
417
    function ByteHeight(x, y: Integer): Byte;
418
    { : Height of point x, y as a SmallInt. }
419
    function SmallIntHeight(x, y: Integer): SmallInt;
420
    { : Height of point x, y as a Single. }
421
    function SingleHeight(x, y: Integer): Single;
422
    { : Interopolated height of point x, y as a Single. }
423
    function InterpolatedHeight(x, y: Single): Single;
424

425
    { : Minimum height in the tile.
426
      DataSources may assign a value to prevent automatic computation
427
      if they have a faster/already computed value. }
428
    property HeightMin: Single read GetHeightMin write FHeightMin;
429
    { : Maximum height in the tile.
430
      DataSources may assign a value to prevent automatic computation
431
      if they have a faster/already computed value. }
432
    property HeightMax: Single read GetHeightMax write FHeightMax;
433

434
    { : Returns the height as a single, whatever the DataType (slow). }
435
    function Height(x, y: Integer): Single;
436

437
    { : Calculates and returns the normal for vertex point x, y.
438
      Sub classes may provide normal cacheing, the default implementation
439
      being rather blunt. }
440
    function Normal(x, y: Integer; const scale: TAffineVector)
441
      : TAffineVector; virtual;
442

443
    { : Calculates and returns the normal for cell x, y.(between vertexes)  }
444
    function NormalAtNode(x, y: Integer; const scale: TAffineVector)
445
      : TAffineVector; virtual;
446

447
    { : Returns True if the data tile overlaps the area. }
448
    function OverlapsArea(const Area: TGLRect): boolean;
449

450
    { : Reserved for renderer use. }
451
    property ObjectTag: TObject read FObjectTag write FObjectTag;
452
    { : Reserved for renderer use. }
453
    property Tag: Integer read FTag write FTag;
454
    { : Reserved for renderer use. }
455
    property Tag2: Integer read FTag2 write FTag2;
456
    { : Used by perlin HDS. }
457
    property Thread: TGLHeightDataThread read FThread write FThread;
458
  end;
459

460
  // TGLHeightDataThread
461
  //
462
  { : A thread specialized for processing TGLHeightData in background.
463
    Requirements: 
464
     must have FreeOnTerminate set to true,
465
     must check and honour Terminated swiftly
466
      }
467
  TGLHeightDataThread = class(TThread)
468
  protected
469
     
470
    FHeightData: TGLHeightData;
471

472
  public
473
     
474
    destructor Destroy; override;
475
    { : The Height Data the thread is to prepare. }
476
    property HeightData: TGLHeightData read FHeightData write FHeightData;
477

478
  end;
479

480
  // TGLBitmapHDS
481
  //
482
  { : Bitmap-based Height Data Source.
483
    The image is automatically wrapped if requested data is out of picture Size,
484
    or if requested data is larger than the picture.
485
    The internal format is an 8 bit bitmap whose dimensions are a power of two,
486
    if the original image does not comply, it is StretchDraw'ed on a monochrome
487
    (gray) bitmap. }
488
  TGLBitmapHDS = class(TGLHeightDataSource)
489
  private
490
     
491
    FScanLineCache: array of PByteArray;
492
    FBitmap: TGLBitmap;
493

494
    IntfImg1: TLazIntfImage;
495

496
    FPicture: TGLPicture;
497
    FInfiniteWrap: boolean;
498
    FInverted: boolean;
499

500
  protected
501
     
502
    procedure SetPicture(const Val: TGLPicture);
503
    procedure OnPictureChanged(sender: TObject);
504
    procedure SetInfiniteWrap(Val: boolean);
505
    procedure SetInverted(Val: boolean);
506

507
    procedure CreateMonochromeBitmap(size: Integer);
508
    procedure FreeMonochromeBitmap;
509
    function GetScanLine(y: Integer): PByteArray;
510
  public
511
     
512
    constructor Create(AOwner: TComponent); override;
513
    destructor Destroy; override;
514

515
    procedure StartPreparingData(HeightData: TGLHeightData); override;
516
    procedure MarkDirty(const Area: TGLRect); override;
517
    function Width: Integer; override;
518
    function Height: Integer; override;
519

520
  published
521
     
522
    { : The picture serving as Height field data reference.
523
      The picture is (if not already) internally converted to a 8 bit
524
      bitmap (grayscale). For better performance and to save memory,
525
      feed it this format! }
526
    property Picture: TGLPicture read FPicture write SetPicture;
527
    { : If true the height field is wrapped indefinetely. }
528
    property InfiniteWrap: boolean read FInfiniteWrap write SetInfiniteWrap
529
      default True;
530
    { : If true, the rendered terrain is a mirror image of the input data. }
531
    property Inverted: boolean read FInverted write SetInverted default True;
532

533
    property MaxPoolSize;
534
  end;
535

536
  TStartPreparingDataEvent = procedure(HeightData: TGLHeightData) of object;
537
  TMarkDirtyEvent = procedure(const Area: TGLRect) of object;
538

539
  // TTexturedHeightDataSource = class (TGLTexturedHeightDataSource)
540

541
  // TGLCustomHDS
542
  //
543
  { : An Height Data Source for custom use.
544
    Provides event handlers for the various requests to be implemented
545
    application-side (for application-specific needs). }
546
  TGLCustomHDS = class(TGLHeightDataSource)
547
  private
548
     
549
    FOnStartPreparingData: TStartPreparingDataEvent;
550
    FOnMarkDirty: TMarkDirtyEvent;
551

552
  protected
553
     
554

555
  public
556
     
557
    constructor Create(AOwner: TComponent); override;
558
    destructor Destroy; override;
559
    procedure StartPreparingData(HeightData: TGLHeightData); override;
560

561
    procedure MarkDirty(const Area: TGLRect); override;
562

563
  published
564
     
565
    property MaxPoolSize;
566

567
    property OnStartPreparingData: TStartPreparingDataEvent
568
      read FOnStartPreparingData write FOnStartPreparingData;
569
    property OnMarkDirtyEvent: TMarkDirtyEvent read FOnMarkDirty
570
      write FOnMarkDirty;
571
  end;
572

573
  // TGLTerrainBaseHDS
574
  //
575
  { : TerrainBase-based Height Data Source.
576
    This component takes its data from the TerrainBase Gobal Terrain Model. 
577
    Though it can be used directly, the resolution of the TerrainBase dataset
578
    isn't high enough for accurate short-range representation and the data
579
    should rather be used as basis for further (fractal) refinement.
580
    TerrainBase is freely available from the National Geophysical Data Center
581
    and World Data Center web site (http://ngdc.noaa.com).
582
    (this component expects to find "tbase.bin" in the current directory). }
583
  TGLTerrainBaseHDS = class(TGLHeightDataSource)
584
  private
585
     
586

587
  protected
588
     
589

590
  public
591
     
592
    constructor Create(AOwner: TComponent); override;
593
    destructor Destroy; override;
594
    procedure StartPreparingData(HeightData: TGLHeightData); override;
595

596
  published
597
     
598
    property MaxPoolSize;
599
  end;
600

601
  TGLHeightDataSourceFilter = Class;
602
  TSourceDataFetchedEvent = procedure(sender: TGLHeightDataSourceFilter;
603
    HeightData: TGLHeightData) of object;
604

605
  // TGLHeightDataSourceFilter
606
  //
607
  { : Height Data Source Filter.
608
    This component sits between the TGLTerrainRenderer, and a real TGLHeightDataSource.
609
    i.e. TGLTerrainRenderer links to this. This links to the real TGLHeightDataSource.
610
    Use the 'HeightDataSource' property, to link to a source HDS.
611
    The 'OnSourceDataFetched' event then gives you the opportunity to make any changes,
612
    or link in a texture to the TGLHeightData object, BEFORE it is cached.
613
    It bypasses the cache of the source HDS, by calling the source's StartPreparingData procedure directly.
614
    The TGLHeightData objects are then cached by THIS component, AFTER you have made your changes.
615
    This eliminates the need to copy and release the TGLHeightData object from the Source HDS's cache,
616
    before linking your texture.  See the new version of TGLBumpmapHDS for an example. (LIN)
617
    To create your own HDSFilters, Derive from this component, and override the PreparingData procedure.
618
  }
619
  TGLHeightDataSourceFilter = Class(TGLHeightDataSource)
620
  private
621
     
622
    FHDS: TGLHeightDataSource;
623
    FOnSourceDataFetched: TSourceDataFetchedEvent;
624
    FActive: boolean;
625
  protected
626
     
627
    { : PreparingData:  
628
      Override this function in your filter subclasses, to make any
629
      updates/changes to HeightData, before it goes into the cache.
630
      Make sure any code in this function is thread-safe, in case TAsyncHDS was used. }
631
    procedure PreparingData(HeightData: TGLHeightData); virtual; abstract;
632
    procedure SetHDS(Val: TGLHeightDataSource);
633
  public
634
     
635
    constructor Create(AOwner: TComponent); override;
636
    destructor Destroy; override;
637
    procedure Release(aHeightData: TGLHeightData); override;
638
    procedure StartPreparingData(HeightData: TGLHeightData); override;
639
    procedure Notification(AComponent: TComponent;
640
      Operation: TOperation); override;
641
    function Width: Integer; override;
642
    function Height: Integer; override;
643
    property OnSourceDataFetched: TSourceDataFetchedEvent
644
      read FOnSourceDataFetched write FOnSourceDataFetched;
645

646
  published
647
     
648
    property MaxPoolSize;
649
    property HeightDataSource: TGLHeightDataSource read FHDS write SetHDS;
650
    property Active: boolean read FActive write FActive;
651
    // If Active=False, height data passes through unchanged
652
  end;
653

654
  // ------------------------------------------------------------------
655
  // ------------------------------------------------------------------
656
  // ------------------------------------------------------------------
657
implementation
658

659
// ------------------------------------------------------------------
660
// ------------------------------------------------------------------
661
// ------------------------------------------------------------------
662

663
// ------------------
664
// ------------------ TGLHeightDataSourceThread ------------------
665
// ------------------
666

667
type
668
  TGLHeightDataSourceThread = class(TThread)
669
    FOwner: TGLHeightDataSource;
670
    procedure Execute; override;
671
    function WaitForTile(HD: TGLHeightData; seconds: Integer): boolean;
672
    procedure HDSIdle;
673
  end;
674

675
  // Execute
676
  //
677
procedure TGLHeightDataSourceThread.Execute;
678
var
679
  i: Integer;
680
  lst: TList;
681
  HD: TGLHeightData;
682
  max: Integer;
683
  TdCtr: Integer;
684
begin
685
  while not Terminated do
686
  begin
687
    max := FOwner.MaxThreads;
688
    lst := FOwner.FData.LockList;
689

690
    // --count active threads--
691
    i := 0;
692
    TdCtr := 0;
693
    while (i < lst.Count) and (TdCtr < max) do
694
    begin
695
      if TGLHeightData(lst.Items[i]).FThread <> nil then
696
        Inc(TdCtr);
697
      Inc(i);
698
    end;
699
    // ------------------------
700

701
    // --Find the queued tiles, and Start preparing them--
702
    i := 0;
703
    While ((i < lst.Count) and (TdCtr < max)) do
704
    begin
705
      HD := TGLHeightData(lst.Items[i]);
706
      if HD.DataState = hdsQueued then
707
      begin
708
        FOwner.StartPreparingData(HD); // prepare
709
        Inc(TdCtr);
710
      end;
711
      Inc(i);
712
    end;
713
    // ---------------------------------------------------
714

715
    FOwner.FData.UnlockList;
716
    if (TdCtr = 0) then
717
      synchronize(HDSIdle);
718
    if (TdCtr = 0) then
719
      sleep(10)
720
    else
721
      sleep(0); // sleep longer if no Queued tiles were found
722
  end;
723
end;
724

725
// WaitForTile
726
//
727
// When Threading, wait a specified time, for the tile to finish preparing
728
function TGLHeightDataSourceThread.WaitForTile(HD: TGLHeightData;
729
  seconds: Integer): boolean;
730
var
731
  // i:integer;
732
  eTime: TDateTime;
733
begin
734
  eTime := now + (1000 * seconds);
735
  while (HD.FThread <> nil) and (now < eTime) do
736
  begin
737
    sleep(0);
738
  end;
739
  Result := (HD.FThread = nil); // true if the thread has finished
740
end;
741

742
// HDSIdle
743
//
744
// When using threads, HDSIdle is called in the main thread,
745
// whenever all HDS threads have finished, AND no queued tiles were found.
746
// (GLAsyncHDS uses this for the OnIdle event.)
747
procedure TGLHeightDataSourceThread.HDSIdle;
748
begin
749
  self.FOwner.ThreadIsIdle;
750
end;
751

752
// ------------------
753
// ------------------ TGLHeightDataSource ------------------
754
// ------------------
755

756
// Create
757
//
758
constructor TGLHeightDataSource.Create(AOwner: TComponent);
759
var
760
  i: Integer;
761
begin
762
  inherited Create(AOwner);
763
  FHeightDataClass := TGLHeightData;
764
  FData := TThreadList.Create;
765
  for i := 0 to High(FDataHash) do
766
    FDataHash[i] := TList.Create;
767
  // FReleaseLatency:=15/(3600*24);
768
  FThread := TGLHeightDataSourceThread.Create(True);
769
  FThread.FreeOnTerminate := false;
770
  TGLHeightDataSourceThread(FThread).FOwner := self;
771
  if self.MaxThreads > 0 then
772
{$IFDEF GLS_DELPHI_2009_DOWN}
773
    FThread.Resume;
774
{$ELSE}
775
    FThread.Start;
776
{$ENDIF}
777
end;
778

779
// Destroy
780
//
781
destructor TGLHeightDataSource.Destroy;
782
var
783
  i: Integer;
784
begin
785
  inherited Destroy;
786
  if Assigned(FThread) then
787
  begin
788
    FThread.Terminate;
789
{$IFDEF GLS_DELPHI_2009_DOWN}
790
    FThread.Resume;
791
{$ELSE}
792
    FThread.Start;
793
{$ENDIF}
794
    FThread.WaitFor;
795
    FThread.Free;
796
  end;
797
  Clear;
798
  FData.Free;
799
  for i := 0 to High(FDataHash) do
800
    FDataHash[i].Free;
801
end;
802

803
// Clear
804
//
805
procedure TGLHeightDataSource.Clear;
806
var
807
  i: Integer;
808
begin
809
  with FData.LockList do
810
  begin
811
    try
812
      for i := 0 to Count - 1 do
813
        if TGLHeightData(Items[i]).UseCounter > 0 then
814
          if not(csDestroying in ComponentState) then
815
            raise Exception.Create('ERR: HeightData still in use');
816
      for i := 0 to Count - 1 do
817
      begin
818
        TGLHeightData(Items[i]).FOwner := nil;
819
        TGLHeightData(Items[i]).Free;
820
      end;
821
      for i := 0 to High(FDataHash) do
822
        FDataHash[i].Clear;
823
      Clear;
824
    finally
825
      FData.UnlockList;
826
    end;
827
  end;
828
end;
829

830
// HashKey
831
//
832
function TGLHeightDataSource.HashKey(XLeft, YTop: Integer): Integer;
833
begin
834
  Result := (XLeft + (XLeft shr 8) + (YTop shl 1) + (YTop shr 7)) and
835
    High(FDataHash);
836
end;
837

838
// FindMatchInList
839
//
840

841
function TGLHeightDataSource.FindMatchInList(XLeft, YTop, size: Integer;
842
  DataType: TGLHeightDataType): TGLHeightData;
843
var
844
  i: Integer;
845
  HD: TGLHeightData;
846
begin
847
  Result := nil;
848
  FData.LockList;
849
  try
850
    with FDataHash[HashKey(XLeft, YTop)] do
851
      for i := 0 to Count - 1 do
852
      begin
853
        HD := TGLHeightData(Items[i]);
854
        // if (not hd.Dirty) and (hd.XLeft=xLeft) and (hd.YTop=YTop) and (hd.Size=Size) and (hd.DataType=DataType) then begin
855
        if (HD.XLeft = XLeft) and (HD.YTop = YTop) and (HD.size = size) and
856
          (HD.DataType = DataType) and (HD.DontUse = false) then
857
        begin
858
          Result := HD;
859
          Break;
860
        end;
861
      end;
862
  finally
863
    FData.UnlockList;
864
  end;
865
end;
866

867
// GetData
868
//
869
function TGLHeightDataSource.GetData(XLeft, YTop, size: Integer;
870
  DataType: TGLHeightDataType): TGLHeightData;
871
begin
872
  Result := FindMatchInList(XLeft, YTop, size, DataType);
873
  if not Assigned(Result) then
874
    Result := PreLoad(XLeft, YTop, size, DataType)
875
  else
876
    with FData.LockList do
877
    begin
878
      try
879
        Move(IndexOf(Result), 0); // Moves item to the beginning of the list.
880
      finally
881
        FData.UnlockList;
882
      end;
883
    end;
884
  // got one... can be used ?
885
  // while not (Result.DataState in [hdsReady, hdsNone]) do Sleep(0);
886
end;
887

888
// PreLoad
889
//
890
function TGLHeightDataSource.PreLoad(XLeft, YTop, size: Integer;
891
  DataType: TGLHeightDataType): TGLHeightData;
892
begin
893
  Result := HeightDataClass.Create(self, XLeft, YTop, size, DataType);
894
  with FData.LockList do
895
    try
896
      Add(Result);
897
      BeforePreparingData(Result);
898
      FDataHash[HashKey(XLeft, YTop)].Add(Result);
899
    finally
900
      FData.UnlockList;
901
    end;
902

903
  // -- When NOT using Threads, fully prepare the tile immediately--
904
  if MaxThreads = 0 then
905
  begin
906
    StartPreparingData(Result);
907
    AfterPreparingData(Result);
908
  end;
909
  // ---------------------------------------------------------------
910
end;
911

912
// PreloadReplacement
913
//
914
// When Multi-threading, this queues a replacement for a dirty tile
915
// The Terrain renderer will continue to use the dirty tile, until the replacement is complete
916
procedure TGLHeightDataSource.PreloadReplacement(aHeightData: TGLHeightData);
917
var
918
  HD: TGLHeightData;
919
  NewHD: TGLHeightData;
920
begin
921
  Assert(MaxThreads > 0);
922
  HD := aHeightData;
923
  NewHD := HeightDataClass.Create(self, HD.XLeft, HD.YTop, HD.size,
924
    HD.DataType);
925
  with FData.LockList do
926
    try
927
      Add(NewHD);
928
      NewHD.OldVersion := HD; // link
929
      HD.NewVersion := NewHD; // link
930
      NewHD.DontUse := True;
931
      BeforePreparingData(NewHD);
932
      FDataHash[HashKey(HD.XLeft, HD.YTop)].Add(NewHD);
933
    finally
934
      FData.UnlockList;
935
    end;
936
end;
937

938
// Release
939
//
940
procedure TGLHeightDataSource.Release(aHeightData: TGLHeightData);
941
begin
942
  // nothing, yet
943
end;
944

945
// MarkDirty (rect)
946
//
947
procedure TGLHeightDataSource.MarkDirty(const Area: TGLRect);
948
var
949
  i: Integer;
950
  HD: TGLHeightData;
951
begin
952
  with FData.LockList do
953
  begin
954
    try
955
      for i := Count - 1 downto 0 do
956
      begin
957
        HD := TGLHeightData(Items[i]);
958
        if HD.OverlapsArea(Area) then
959
          HD.MarkDirty;
960
      end;
961
    finally
962
      FData.UnlockList;
963
    end;
964
  end;
965
end;
966

967
// MarkDirty (ints)
968
//
969
procedure TGLHeightDataSource.MarkDirty(XLeft, YTop, xRight, yBottom: Integer);
970
var
971
  r: TGLRect;
972
begin
973
  r.Left := XLeft;
974
  r.Top := YTop;
975
  r.Right := xRight;
976
  r.Bottom := yBottom;
977
  MarkDirty(r);
978
end;
979

980
// MarkDirty
981
//
982
procedure TGLHeightDataSource.MarkDirty;
983
const
984
  m = MaxInt - 1;
985
begin
986
  MarkDirty(-m, -m, m, m);
987
end;
988

989
// CleanUp
990
//
991
procedure TGLHeightDataSource.CleanUp;
992
var
993
  packList: boolean;
994
  i, k: Integer;
995
  usedMemory: Integer;
996
  HD: TGLHeightData;
997
  ReleaseThis: boolean;
998
begin
999
  with FData.LockList do
1000
  begin
1001
    try
1002
      usedMemory := 0;
1003
      packList := false;
1004
      // Cleanup dirty tiles and compute used memory
1005
      for i := Count - 1 downto 0 do
1006
      begin
1007
        HD := TGLHeightData(Items[i]);
1008
        if HD <> nil then
1009
          with HD do
1010
          begin
1011
            // --Release criteria for dirty tiles--
1012
            ReleaseThis := false;
1013
            if HD.Dirty then
1014
            begin // Only release dirty tiles
1015
              if (MaxThreads = 0) then
1016
                ReleaseThis := True
1017
                // when not threading, delete ALL dirty tiles
1018
              else if (HD.DataState <> hdsPreparing) then
1019
              begin // Dont release Preparing tiles
1020
                if (HD.UseCounter = 0) then
1021
                  ReleaseThis := True; // This tile is unused
1022
                if (HD.NewVersion = nil) then
1023
                  ReleaseThis := True
1024
                  // This tile has no queued replacement to wait for
1025
                else if (HD.DontUse) then
1026
                  ReleaseThis := True; // ??This tile has already been replaced.
1027
              end;
1028
            end;
1029
            // ------------------------------------
1030
            // if Dirty then ReleaseThis:=true;
1031
            if ReleaseThis then
1032
            begin
1033
              FDataHash[HashKey(HD.XLeft, HD.YTop)].Remove(HD);
1034
              Items[i] := nil;
1035
              FOwner := nil;
1036
              Free;
1037
              packList := True;
1038
            end
1039
            else
1040
              usedMemory := usedMemory + HD.DataSize;
1041
          end;
1042
      end;
1043
      // If MaxPoolSize exceeded, release all that may be, and pack the list
1044
      k := 0;
1045
      if usedMemory > MaxPoolSize then
1046
      begin
1047
        for i := 0 to Count - 1 do
1048
        begin
1049
          HD := TGLHeightData(Items[i]);
1050
          if HD <> nil then
1051
            with HD do
1052
            begin
1053
              if (DataState <> hdsPreparing) and (UseCounter = 0) and
1054
                (OldVersion = nil)
1055
              // if (DataState=hdsReady)and(UseCounter=0)and(OldVersion=nil)
1056
              then
1057
              begin
1058
                FDataHash[HashKey(HD.XLeft, HD.YTop)].Remove(HD);
1059
                Items[i] := nil;
1060
                FOwner := nil;
1061
                Free;
1062
                // packList:=True;
1063
              end
1064
              else
1065
              begin
1066
                Items[k] := HD;
1067
                Inc(k);
1068
              end;
1069
            end;
1070
        end;
1071
        Count := k;
1072
      end
1073
      else if packList then
1074
      begin
1075
        for i := 0 to Count - 1 do
1076
          if Items[i] <> nil then
1077
          begin
1078
            Items[k] := Items[i];
1079
            Inc(k);
1080
          end;
1081
        Count := k;
1082
      end;
1083
    finally
1084
      FData.UnlockList;
1085
    end;
1086
  end;
1087
end;
1088

1089
// SetMaxThreads
1090
//
1091
procedure TGLHeightDataSource.SetMaxThreads(const Val: Integer);
1092
begin
1093
  if (Val <= 0) then
1094
    FMaxThreads := 0
1095
  else
1096
  begin
1097
    // If we didn't do threading, but will now
1098
    // resume our thread
1099
    if (FMaxThreads <= 0) then
1100
{$IFDEF GLS_DELPHI_2009_DOWN}
1101
      FThread.Resume;
1102
{$ELSE}
1103
      FThread.Start;
1104
{$ENDIF}
1105
    FMaxThreads := Val;
1106
  end;
1107
end;
1108

1109
// BeforePreparingData
1110
// Called BEFORE StartPreparingData, but always from the MAIN thread.
1111
// Override this in subclasses, to prepare for Threading.
1112
//
1113
procedure TGLHeightDataSource.BeforePreparingData(HeightData: TGLHeightData);
1114
begin
1115
  //
1116
end;
1117

1118
// StartPreparingData
1119
// When Threads are used, this runs from the sub-thread, so this MUST be thread-safe.
1120
// Any Non-thread-safe code should be placed in "BeforePreparingData"
1121
//
1122
procedure TGLHeightDataSource.StartPreparingData(HeightData: TGLHeightData);
1123
begin
1124
  // Only the tile Owner may set the preparing tile to ready
1125
  if (HeightData.Owner = self) and (HeightData.DataState = hdsPreparing) then
1126
    HeightData.FDataState := hdsReady;
1127
end;
1128

1129
// AfterPreparingData
1130
// Called AFTER StartPreparingData, but always from the MAIN thread.
1131
// Override this in subclasses, if needed.
1132
//
1133
procedure TGLHeightDataSource.AfterPreparingData(HeightData: TGLHeightData);
1134
begin
1135
  //
1136
end;
1137

1138
// ThreadIsIdle
1139
//
1140
procedure TGLHeightDataSource.ThreadIsIdle;
1141
begin
1142
  // TGLAsyncHDS overrides this
1143
end;
1144

1145
// TextureCoordinates
1146
// Calculates texture World texture coordinates for the current tile.
1147
// Use Stretch for OpenGL1.1, to hide the seams when using linear filtering.
1148
procedure TGLHeightDataSource.TextureCoordinates(HeightData: TGLHeightData;
1149
  Stretch: boolean = false);
1150
var
1151
  w, h, size: Integer;
1152
  scaleS, scaleT: Single;
1153
  offsetS, offsetT: Single;
1154
  HD: TGLHeightData;
1155
  halfpixel: Single;
1156
begin
1157
  HD := HeightData;
1158
  w := self.Width;
1159
  h := self.Height;
1160
  size := HD.FSize;
1161
  // if GL_VERSION_1_2 then begin //OpenGL1.2 supports texture clamping, so seams dont show.
1162
  if Stretch = false then
1163
  begin // These are the real Texture coordinates
1164
    scaleS := w / (size - 1);
1165
    scaleT := h / (size - 1);
1166
    offsetS := -((HD.XLeft / w) * scaleS);
1167
    offsetT := -(h - (HD.YTop + size - 1)) / (size - 1);
1168
  end
1169
  else
1170
  begin // --Texture coordinates: Stretched by 1 pixel, to hide seams on OpenGL-1.1(no Clamping)--
1171
    scaleS := w / size;
1172
    scaleT := h / size;
1173
    halfpixel := 1 / (size shr 1);
1174
    offsetS := -((HD.XLeft / w) * scaleS) + halfpixel;
1175
    offsetT := -(h - (HD.YTop + size)) / size - halfpixel;
1176
  end;
1177
  HD.FTCScale.S := scaleS;
1178
  HD.FTCScale.T := scaleT;
1179
  HD.FTCOffset.S := offsetS;
1180
  HD.FTCOffset.T := offsetT;
1181
end;
1182

1183
// InterpolatedHeight
1184
//
1185
function TGLHeightDataSource.InterpolatedHeight(x, y: Single;
1186
  tileSize: Integer): Single;
1187
var
1188
  i: Integer;
1189
  HD, foundHd: TGLHeightData;
1190
begin
1191
  with FData.LockList do
1192
  begin
1193
    try
1194
      // first, lookup data list to find if aHeightData contains our point
1195
      foundHd := nil;
1196
      for i := 0 to Count - 1 do
1197
      begin
1198
        HD := TGLHeightData(Items[i]);
1199
        if (HD.XLeft <= x) and (HD.YTop <= y) and (HD.XLeft + HD.size - 1 > x)
1200
          and (HD.YTop + HD.size - 1 > y) then
1201
        begin
1202
          foundHd := HD;
1203
          Break;
1204
        end;
1205
      end;
1206
    finally
1207
      FData.UnlockList;
1208
    end;
1209
  end;
1210
  if (foundHd = nil) or foundHd.Dirty then
1211
  begin
1212
    // not found, request one... slowest mode (should be avoided)
1213
    if tileSize > 1 then
1214
      foundHd := GetData(Round(x / (tileSize - 1) - 0.5) * (tileSize - 1),
1215
        Round(y / (tileSize - 1) - 0.5) * (tileSize - 1), tileSize, hdtDefault)
1216
    else
1217
    begin
1218
      Result := DefaultHeight;
1219
      Exit;
1220
    end;
1221
  end
1222
  else
1223
  begin
1224
    // request it using "standard" way (takes care of threads)
1225
    foundHd := GetData(foundHd.XLeft, foundHd.YTop, foundHd.size,
1226
      foundHd.DataType);
1227
  end;
1228
  if foundHd.DataState = hdsNone then
1229
    Result := DefaultHeight
1230
  else
1231
    Result := foundHd.InterpolatedHeight(x - foundHd.XLeft, y - foundHd.YTop);
1232
end;
1233

1234
// ------------------
1235
// ------------------ TGLHeightData ------------------
1236
// ------------------
1237

1238
// Create
1239
//
1240
constructor TGLHeightData.Create(AOwner: TGLHeightDataSource;
1241
  aXLeft, aYTop, aSize: Integer; aDataType: TGLHeightDataType);
1242
begin
1243
  inherited Create(AOwner);
1244
  SetLength(FUsers, 0);
1245
  FOwner := AOwner;
1246
  FXLeft := aXLeft;
1247
  FYTop := aYTop;
1248
  FSize := aSize;
1249
  FTextureCoordinatesMode := tcmWorld;
1250
  FTCScale := XYTexPoint;
1251
  FDataType := aDataType;
1252
  FDataState := hdsQueued;
1253
  FHeightMin := 1E30;
1254
  FHeightMax := 1E30;
1255

1256
  OldVersion := nil;
1257
  NewVersion := nil;
1258
  DontUse := false;
1259
end;
1260

1261
// Destroy
1262
//
1263
destructor TGLHeightData.Destroy;
1264
begin
1265
  Assert(Length(FUsers) = 0,
1266
    'You should *not* free a TGLHeightData, use "Release" instead');
1267
  Assert(not Assigned(FOwner),
1268
    'You should *not* free a TGLHeightData, use "Release" instead');
1269
  if Assigned(FThread) then
1270
  begin
1271
    FThread.Terminate;
1272
    if FThread.Suspended then
1273
{$IFDEF GLS_DELPHI_2009_DOWN}
1274
      FThread.Resume;
1275
{$ELSE}
1276
      FThread.Start;
1277
{$ENDIF}
1278
    FThread.WaitFor;
1279
  end;
1280

1281
//  if Assigned(FOnDestroy) then  FOnDestroy(self);   Assertion Occurs
1282
  case DataType of
1283
    hdtByte:
1284
      begin
1285
        FreeMem(FByteData);
1286
        FreeMem(FByteRaster);
1287
      end;
1288
    hdtSmallInt:
1289
      begin
1290
        FreeMem(FSmallIntData);
1291
        FreeMem(FSmallIntRaster);
1292
      end;
1293
    hdtSingle:
1294
      begin
1295
        FreeMem(FSingleData);
1296
        FreeMem(FSingleRaster);
1297
      end;
1298
    hdtDefault:
1299
      ; // nothing
1300
  else
1301
    Assert(false);
1302
  end;
1303
  // ----------------------
1304
  self.LibMaterial := nil; // release a used material
1305

1306
  // --Break any link with a new/old version of this tile--
1307
  if Assigned(self.OldVersion) then
1308
  begin
1309
    self.OldVersion.NewVersion := nil;
1310
    self.OldVersion := nil;
1311
  end;
1312
  if Assigned(self.NewVersion) then
1313
  begin
1314
    self.NewVersion.OldVersion := nil;
1315
    self.NewVersion := nil;
1316
  end;
1317
  // ------------------------------------------------------
1318

1319
  // ----------------------
1320
  inherited Destroy;
1321
end;
1322

1323
// RegisterUse
1324
//
1325
procedure TGLHeightData.RegisterUse;
1326
begin
1327
  Inc(FUseCounter);
1328
end;
1329

1330
// Release
1331
//
1332
procedure TGLHeightData.Release;
1333
begin
1334
  if FUseCounter > 0 then
1335
    Dec(FUseCounter);
1336
  if FUseCounter = 0 then
1337
  begin
1338
    Owner.Release(self); // ???
1339
  end;
1340
end;
1341

1342
// MarkDirty
1343
//
1344
// Release Dirty tiles, unless threading, and the tile is being used.
1345
// In that case, start building a replacement tile instead.
1346

1347
procedure TGLHeightData.MarkDirty;
1348
begin
1349
  with Owner.Data.LockList do
1350
    try
1351
      if (not Dirty) and (DataState <> hdsQueued) then
1352
      begin // dont mark queued tiles as dirty
1353
        FDirty := True;
1354
        if (Owner.MaxThreads > 0) and (FUseCounter > 0) then
1355
          Owner.PreloadReplacement(self)
1356
        else
1357
        begin
1358
          FUseCounter := 0;
1359
          Owner.Release(self);
1360
        end;
1361
      end;
1362
    finally
1363
      Owner.Data.UnlockList;
1364
    end;
1365
end;
1366

1367
// Allocate
1368
//
1369
procedure TGLHeightData.Allocate(const Val: TGLHeightDataType);
1370
begin
1371
  Assert(FDataSize = 0);
1372
  case Val of
1373
    hdtByte:
1374
      begin
1375
        FDataSize := size * size * SizeOf(Byte);
1376
        GetMem(FByteData, FDataSize);
1377
        BuildByteRaster;
1378
      end;
1379
    hdtSmallInt:
1380
      begin
1381
        FDataSize := size * size * SizeOf(SmallInt);
1382
        GetMem(FSmallIntData, FDataSize);
1383
        BuildSmallIntRaster;
1384
      end;
1385
    hdtSingle:
1386
      begin
1387
        FDataSize := size * size * SizeOf(Single);
1388
        GetMem(FSingleData, FDataSize);
1389
        BuildSingleRaster;
1390
      end;
1391
  else
1392
    Assert(false);
1393
  end;
1394
  FDataType := Val;
1395
end;
1396

1397
// WARNING: SetMaterialName does NOT register the tile as a user of this texture.
1398
// So, TGLLibMaterials.DeleteUnusedMaterials may see this material as unused, and delete it.
1399
// This may lead to AV's the next time this tile is rendered.
1400
// To be safe, rather assign the new TGLHeightData.LibMaterial property
1401
procedure TGLHeightData.SetMaterialName(const MaterialName: string);
1402
begin
1403
  SetLibMaterial(nil);
1404
  FMaterialName := MaterialName;
1405
end;
1406

1407
procedure TGLHeightData.SetLibMaterial(LibMaterial: TGLLibMaterial);
1408
begin
1409
  if Assigned(FLibMaterial) then
1410
    FLibMaterial.UnregisterUser(self); // detach from old texture
1411
  FLibMaterial := LibMaterial; // Attach new Material
1412
  if Assigned(LibMaterial) then
1413
  begin
1414
    LibMaterial.RegisterUser(self); // Mark new Material as 'used'
1415
    FMaterialName := LibMaterial.Name; // sync up MaterialName property
1416
  end
1417
  else
1418
    FMaterialName := '';
1419
end;
1420

1421
// SetDataType
1422
//
1423
procedure TGLHeightData.SetDataType(const Val: TGLHeightDataType);
1424
begin
1425
  if (Val <> FDataType) and (Val <> hdtDefault) then
1426
  begin
1427
    if DataState <> hdsNone then
1428
    begin
1429
      case FDataType of
1430
        hdtByte:
1431
          case Val of
1432
            hdtSmallInt:
1433
              ConvertByteToSmallInt;
1434
            hdtSingle:
1435
              ConvertByteToSingle;
1436
          else
1437
            Assert(false);
1438
          end;
1439
        hdtSmallInt:
1440
          case Val of
1441
            hdtByte:
1442
              ConvertSmallIntToByte;
1443
            hdtSingle:
1444
              ConvertSmallIntToSingle;
1445
          else
1446
            Assert(false);
1447
          end;
1448
        hdtSingle:
1449
          case Val of
1450
            hdtByte:
1451
              ConvertSingleToByte;
1452
            hdtSmallInt:
1453
              ConvertSingleToSmallInt;
1454
          else
1455
            Assert(false);
1456
          end;
1457
        hdtDefault:
1458
          ; // nothing, assume StartPreparingData knows what it's doing
1459
      else
1460
        Assert(false);
1461
      end;
1462
    end;
1463
    FDataType := Val;
1464
  end;
1465
end;
1466

1467
// BuildByteRaster
1468
//
1469
procedure TGLHeightData.BuildByteRaster;
1470
var
1471
  i: Integer;
1472
begin
1473
  GetMem(FByteRaster, size * SizeOf(PByteArray));
1474
  for i := 0 to size - 1 do
1475
    FByteRaster^[i] := @FByteData[i * size]
1476
end;
1477

1478
// BuildSmallIntRaster
1479
//
1480
procedure TGLHeightData.BuildSmallIntRaster;
1481
var
1482
  i: Integer;
1483
begin
1484
  GetMem(FSmallIntRaster, size * SizeOf(PSmallIntArray));
1485
  for i := 0 to size - 1 do
1486
    FSmallIntRaster^[i] := @FSmallIntData[i * size]
1487
end;
1488

1489
// BuildSingleRaster
1490
//
1491
procedure TGLHeightData.BuildSingleRaster;
1492
var
1493
  i: Integer;
1494
begin
1495
  GetMem(FSingleRaster, size * SizeOf(PSingleArray));
1496
  for i := 0 to size - 1 do
1497
    FSingleRaster^[i] := @FSingleData[i * size]
1498
end;
1499

1500
// ConvertByteToSmallInt
1501
//
1502
procedure TGLHeightData.ConvertByteToSmallInt;
1503
var
1504
  i: Integer;
1505
begin
1506
  FreeMem(FByteRaster);
1507
  FByteRaster := nil;
1508
  FDataSize := size * size * SizeOf(SmallInt);
1509
  GetMem(FSmallIntData, FDataSize);
1510
  for i := 0 to size * size - 1 do
1511
    FSmallIntData^[i] := (FByteData^[i] - 128) shl 7;
1512
  FreeMem(FByteData);
1513
  FByteData := nil;
1514
  BuildSmallIntRaster;
1515
end;
1516

1517
// ConvertByteToSingle
1518
//
1519
procedure TGLHeightData.ConvertByteToSingle;
1520
var
1521
  i: Integer;
1522
begin
1523
  FreeMem(FByteRaster);
1524
  FByteRaster := nil;
1525
  FDataSize := size * size * SizeOf(Single);
1526
  GetMem(FSingleData, FDataSize);
1527
  for i := 0 to size * size - 1 do
1528
    FSingleData^[i] := (FByteData^[i] - 128) shl 7;
1529
  FreeMem(FByteData);
1530
  FByteData := nil;
1531
  BuildSingleRaster;
1532
end;
1533

1534
// ConvertSmallIntToByte
1535
//
1536
procedure TGLHeightData.ConvertSmallIntToByte;
1537
var
1538
  i: Integer;
1539
begin
1540
  FreeMem(FSmallIntRaster);
1541
  FSmallIntRaster := nil;
1542
  FByteData := Pointer(FSmallIntData);
1543
  for i := 0 to size * size - 1 do
1544
    FByteData^[i] := (FSmallIntData^[i] div 128) + 128;
1545
  FDataSize := size * size * SizeOf(Byte);
1546
  ReallocMem(FByteData, FDataSize);
1547
  FSmallIntData := nil;
1548
  BuildByteRaster;
1549
end;
1550

1551
// ConvertSmallIntToSingle
1552
//
1553
procedure TGLHeightData.ConvertSmallIntToSingle;
1554
var
1555
  i: Integer;
1556
begin
1557
  FreeMem(FSmallIntRaster);
1558
  FSmallIntRaster := nil;
1559
  FDataSize := size * size * SizeOf(Single);
1560
  GetMem(FSingleData, FDataSize);
1561
  for i := 0 to size * size - 1 do
1562
    FSingleData^[i] := FSmallIntData^[i];
1563
  FreeMem(FSmallIntData);
1564
  FSmallIntData := nil;
1565
  BuildSingleRaster;
1566
end;
1567

1568
// ConvertSingleToByte
1569
//
1570
procedure TGLHeightData.ConvertSingleToByte;
1571
var
1572
  i: Integer;
1573
begin
1574
  FreeMem(FSingleRaster);
1575
  FSingleRaster := nil;
1576
  FByteData := Pointer(FSingleData);
1577
  for i := 0 to size * size - 1 do
1578
    FByteData^[i] := (Round(FSingleData^[i]) div 128) + 128;
1579
  FDataSize := size * size * SizeOf(Byte);
1580
  ReallocMem(FByteData, FDataSize);
1581
  FSingleData := nil;
1582
  BuildByteRaster;
1583
end;
1584

1585
// ConvertSingleToSmallInt
1586
//
1587
procedure TGLHeightData.ConvertSingleToSmallInt;
1588
var
1589
  i: Integer;
1590
begin
1591
  FreeMem(FSingleRaster);
1592
  FSingleRaster := nil;
1593
  FSmallIntData := Pointer(FSingleData);
1594
  for i := 0 to size * size - 1 do
1595
    FSmallIntData^[i] := Round(FSingleData^[i]);
1596
  FDataSize := size * size * SizeOf(SmallInt);
1597
  ReallocMem(FSmallIntData, FDataSize);
1598
  FSingleData := nil;
1599
  BuildSmallIntRaster;
1600
end;
1601

1602
// ByteHeight
1603
//
1604
function TGLHeightData.ByteHeight(x, y: Integer): Byte;
1605
begin
1606
  Assert((Cardinal(x) < Cardinal(size)) and (Cardinal(y) < Cardinal(size)));
1607
  Result := ByteRaster^[y]^[x];
1608
end;
1609

1610
// SmallIntHeight
1611
//
1612
function TGLHeightData.SmallIntHeight(x, y: Integer): SmallInt;
1613
begin
1614
  Assert((Cardinal(x) < Cardinal(size)) and (Cardinal(y) < Cardinal(size)));
1615
  Result := SmallIntRaster^[y]^[x];
1616
end;
1617

1618
// SingleHeight
1619
//
1620
function TGLHeightData.SingleHeight(x, y: Integer): Single;
1621
begin
1622
  Assert((Cardinal(x) < Cardinal(size)) and (Cardinal(y) < Cardinal(size)));
1623
  Result := SingleRaster^[y]^[x];
1624
end;
1625

1626
// InterpolatedHeight
1627
//
1628
function TGLHeightData.InterpolatedHeight(x, y: Single): Single;
1629
var
1630
  ix, iy, ixn, iyn: Integer;
1631
  h1, h2, h3: Single;
1632
begin
1633
  if FDataState = hdsNone then
1634
    Result := 0
1635
  else
1636
  begin
1637
    ix := Trunc(x);
1638
    x := Frac(x);
1639
    iy := Trunc(y);
1640
    y := Frac(y);
1641
    ixn := ix + 1;
1642
    if ixn >= size then
1643
      ixn := ix;
1644
    iyn := iy + 1;
1645
    if iyn >= size then
1646
      iyn := iy;
1647
    if x > y then
1648
    begin
1649
      // top-right triangle
1650
      h1 := Height(ixn, iy);
1651
      h2 := Height(ix, iy);
1652
      h3 := Height(ixn, iyn);
1653
      Result := h1 + (h2 - h1) * (1 - x) + (h3 - h1) * y;
1654
    end
1655
    else
1656
    begin
1657
      // bottom-left triangle
1658
      h1 := Height(ix, iyn);
1659
      h2 := Height(ixn, iyn);
1660
      h3 := Height(ix, iy);
1661
      Result := h1 + (h2 - h1) * (x) + (h3 - h1) * (1 - y);
1662
    end;
1663
  end;
1664
end;
1665

1666
// Height
1667
//
1668
function TGLHeightData.Height(x, y: Integer): Single;
1669
begin
1670
  case DataType of
1671
    hdtByte:
1672
      Result := (ByteHeight(x, y) - 128) shl 7;
1673
    hdtSmallInt:
1674
      Result := SmallIntHeight(x, y);
1675
    hdtSingle:
1676
      Result := SingleHeight(x, y);
1677
  else
1678
    Result := 0;
1679
    Assert(false);
1680
  end;
1681
end;
1682

1683
// GetHeightMin
1684
//
1685
function TGLHeightData.GetHeightMin: Single;
1686
var
1687
  i: Integer;
1688
  b: Byte;
1689
  sm: SmallInt;
1690
  si: Single;
1691
begin
1692
  if FHeightMin = 1E30 then
1693
  begin
1694
    if DataState = hdsReady then
1695
    begin
1696
      case DataType of
1697
        hdtByte:
1698
          begin
1699
            b := FByteData^[0];
1700
            for i := 1 to size * size - 1 do
1701
              if FByteData^[i] < b then
1702
                b := FByteData^[i];
1703
            FHeightMin := ((Integer(b) - 128) shl 7);
1704
          end;
1705
        hdtSmallInt:
1706
          begin
1707
            sm := FSmallIntData^[0];
1708
            for i := 1 to size * size - 1 do
1709
              if FSmallIntData^[i] < sm then
1710
                sm := FSmallIntData^[i];
1711
            FHeightMin := sm;
1712
          end;
1713
        hdtSingle:
1714
          begin
1715
            si := FSingleData^[0];
1716
            for i := 1 to size * size - 1 do
1717
              if FSingleData^[i] < si then
1718
                si := FSingleData^[i];
1719
            FHeightMin := si;
1720
          end;
1721
      else
1722
        FHeightMin := 0;
1723
      end;
1724
    end
1725
    else
1726
      FHeightMin := 0;
1727
  end;
1728
  Result := FHeightMin;
1729
end;
1730

1731
// GetHeightMax
1732
//
1733
function TGLHeightData.GetHeightMax: Single;
1734
var
1735
  i: Integer;
1736
  b: Byte;
1737
  sm: SmallInt;
1738
  si: Single;
1739
begin
1740
  if FHeightMax = 1E30 then
1741
  begin
1742
    if DataState = hdsReady then
1743
    begin
1744
      case DataType of
1745
        hdtByte:
1746
          begin
1747
            b := FByteData^[0];
1748
            for i := 1 to size * size - 1 do
1749
              if FByteData^[i] > b then
1750
                b := FByteData^[i];
1751
            FHeightMax := ((Integer(b) - 128) shl 7);
1752
          end;
1753
        hdtSmallInt:
1754
          begin
1755
            sm := FSmallIntData^[0];
1756
            for i := 1 to size * size - 1 do
1757
              if FSmallIntData^[i] > sm then
1758
                sm := FSmallIntData^[i];
1759
            FHeightMax := sm;
1760
          end;
1761
        hdtSingle:
1762
          begin
1763
            si := FSingleData^[0];
1764
            for i := 1 to size * size - 1 do
1765
              if FSingleData^[i] > si then
1766
                si := FSingleData^[i];
1767
            FHeightMax := si;
1768
          end;
1769
      else
1770
        FHeightMax := 0;
1771
      end;
1772
    end
1773
    else
1774
      FHeightMax := 0;
1775
  end;
1776
  Result := FHeightMax;
1777
end;
1778

1779
// Normal
1780
//
1781
// Calculates the normal at a vertex
1782
function TGLHeightData.Normal(x, y: Integer; const scale: TAffineVector) : TAffineVector;
1783
var
1784
  dx, dy: Single;
1785
begin
1786
  if x > 0 then
1787
    if x < size - 1 then
1788
      dx := (Height(x + 1, y) - Height(x - 1, y))
1789
    else
1790
      dx := (Height(x, y) - Height(x - 1, y))
1791
  else
1792
    dx := (Height(x + 1, y) - Height(x, y));
1793
  if y > 0 then
1794
    if y < size - 1 then
1795
      dy := (Height(x, y + 1) - Height(x, y - 1))
1796
    else
1797
      dy := (Height(x, y) - Height(x, y - 1))
1798
  else
1799
    dy := (Height(x, y + 1) - Height(x, y));
1800
  Result.V[0] := dx * scale.V[1] * scale.V[2];
1801
  Result.V[1] := dy * scale.V[0] * scale.V[2];
1802
  Result.V[2] := scale.V[0] * scale.V[1];
1803
  NormalizeVector(Result);
1804
end;
1805

1806
// NormalNode
1807
//
1808
// Calculates the normal at a surface cell (Between vertexes)
1809
function TGLHeightData.NormalAtNode(x, y: Integer; const scale: TAffineVector)
1810
  : TAffineVector;
1811
var
1812
  dx, dy, Hxy: Single;
1813
begin
1814
  MinInteger(MaxInteger(x, 0), size - 2); // clamp x to 0 -> Size-2
1815
  MinInteger(MaxInteger(y, 0), size - 2); // clamp x to 0 -> Size-2
1816
  Hxy := Height(x, y);
1817
  dx := Height(x + 1, y) - Hxy;
1818
  dy := Height(x, y + 1) - Hxy;
1819
  Result.V[0] := dx * scale.V[1] * scale.V[2]; // Result[0]:=dx/scale[0];
1820
  Result.V[1] := dy * scale.V[0] * scale.V[2]; // Result[1]:=dy/scale[1];
1821
  Result.V[2] := 1 * scale.V[0] * scale.V[1]; // Result[2]:=1 /scale[2];
1822
  NormalizeVector(Result);
1823
end;
1824

1825
// OverlapsArea
1826
//
1827
function TGLHeightData.OverlapsArea(const Area: TGLRect): boolean;
1828
begin
1829
  Result := (XLeft <= Area.Right) and (YTop <= Area.Bottom) and
1830
    (XLeft + size > Area.Left) and (YTop + size > Area.Top);
1831
end;
1832

1833
// ------------------
1834
// ------------------ TGLHeightDataThread ------------------
1835
// ------------------
1836

1837
// Destroy
1838
//
1839
destructor TGLHeightDataThread.Destroy;
1840
begin
1841
  if Assigned(FHeightData) then
1842
    FHeightData.FThread := nil;
1843
  inherited;
1844
end;
1845

1846
// ------------------
1847
// ------------------ TGLBitmapHDS ------------------
1848
// ------------------
1849

1850
// Create
1851
//
1852
constructor TGLBitmapHDS.Create(AOwner: TComponent);
1853
begin
1854
  inherited Create(AOwner);
1855
  FPicture := TGLPicture.Create;
1856
  FPicture.OnChange := OnPictureChanged;
1857
  FInfiniteWrap := True;
1858
  FInverted := True;
1859
end;
1860

1861
// Destroy
1862
//
1863
destructor TGLBitmapHDS.Destroy;
1864
begin
1865
  inherited Destroy;
1866
  FreeMonochromeBitmap;
1867
  FPicture.Free;
1868
end;
1869

1870
// SetPicture
1871
//
1872
procedure TGLBitmapHDS.SetPicture(const Val: TGLPicture);
1873
begin
1874
  FPicture.Assign(Val);
1875
end;
1876

1877
// OnPictureChanged
1878
//
1879
procedure TGLBitmapHDS.OnPictureChanged(sender: TObject);
1880
var
1881
  oldPoolSize, size: Integer;
1882
begin
1883
  // cleanup pool
1884
  oldPoolSize := MaxPoolSize;
1885
  MaxPoolSize := 0;
1886
  CleanUp;
1887
  MaxPoolSize := oldPoolSize;
1888
  // prepare MonoChromeBitmap
1889
  FreeMonochromeBitmap;
1890
  size := Picture.Width;
1891
  if size > 0 then
1892
    CreateMonochromeBitmap(size);
1893
end;
1894

1895
// SetInfiniteWrap
1896
//
1897
procedure TGLBitmapHDS.SetInfiniteWrap(Val: boolean);
1898
begin
1899
  if FInfiniteWrap <> Val then
1900
  begin
1901
    FInfiniteWrap := Val;
1902
    MarkDirty;
1903
  end;
1904
end;
1905

1906
// SetInverted
1907
//
1908
procedure TGLBitmapHDS.SetInverted(Val: boolean);
1909
begin
1910
  if FInverted = Val then
1911
    Exit;
1912
  FInverted := Val;
1913
  MarkDirty;
1914
end;
1915

1916
// MarkDirty
1917
//
1918
procedure TGLBitmapHDS.MarkDirty(const Area: TGLRect);
1919
begin
1920
  inherited;
1921
  FreeMonochromeBitmap;
1922
  if Picture.Width > 0 then
1923
    CreateMonochromeBitmap(Picture.Width);
1924
end;
1925

1926
// CreateMonochromeBitmap
1927
//
1928
procedure TGLBitmapHDS.CreateMonochromeBitmap(size: Integer);
1929
{$IFDEF MSWINDOWS}
1930
type
1931
  TPaletteEntryArray = array [0 .. 255] of TPaletteEntry;
1932
  PPaletteEntryArray = ^TPaletteEntryArray;
1933

1934
  TLogPal = record
1935
    lpal: TLogPalette;
1936
    pe: TPaletteEntryArray;
1937
  end;
1938

1939
var
1940
  x: Integer;
1941
  logpal: TLogPal;
1942
  hPal: HPalette;
1943
begin
1944
  size := RoundUpToPowerOf2(size);
1945
  FBitmap := TGLBitmap.Create;
1946
  FBitmap.PixelFormat := glpf8bit;
1947
  FBitmap.Width := size;
1948
  FBitmap.Height := size;
1949
  for x := 0 to 255 do
1950
    with PPaletteEntryArray(@logpal.lpal.palPalEntry[0])[x] do
1951
    begin
1952
      peRed := x;
1953
      peGreen := x;
1954
      peBlue := x;
1955
      peFlags := 0;
1956
    end;
1957
  with logpal.lpal do
1958
  begin
1959
    palVersion := $300;
1960
    palNumEntries := 256;
1961
  end;
1962
  hPal := CreatePalette(logpal.lpal);
1963
  Assert(hPal <> 0);
1964
  FBitmap.Palette := hPal;
1965
  // some picture formats trigger a "change" when drawed
1966
  Picture.OnChange := nil;
1967
  try
1968
    FBitmap.Canvas.StretchDraw(Classes.Rect(0, 0, Size, Size), Picture.Graphic);
1969
  finally
1970
    Picture.OnChange := OnPictureChanged;
1971
  end;
1972

1973
  IntfImg1 := TLazIntfImage.Create(0, 0);
1974
  IntfImg1.LoadFromBitmap(FBitmap.Handle, FBitmap.MaskHandle);
1975

1976
  SetLength(FScanLineCache, 0); // clear the cache
1977
  SetLength(FScanLineCache, size);
1978
end;
1979
{$ENDIF}
1980
{$IFDEF UNIX}
1981
begin
1982
{$MESSAGE Warn 'CreateMonochromeBitmap: Needs to be implemented'}
1983
end;
1984
{$ENDIF}
1985

1986
// FreeMonochromeBitmap
1987
//
1988
procedure TGLBitmapHDS.FreeMonochromeBitmap;
1989
begin
1990
  SetLength(FScanLineCache, 0);
1991
  FBitmap.Free;
1992
  FBitmap := nil;
1993

1994
  IntfImg1.Free;
1995
  IntfImg1 := nil;
1996

1997
end;
1998

1999
// GetScanLine
2000
//
2001
function TGLBitmapHDS.GetScanLine(y: Integer): PByteArray;
2002
begin
2003
  Result := FScanLineCache[y];
2004
  if not Assigned(Result) then
2005
  begin
2006
    Result := IntfImg1.GetDataLineStart(y);
2007
    FScanLineCache[y] := Result;
2008
  end;
2009
end;
2010

2011
// StartPreparingData
2012
//
2013
procedure TGLBitmapHDS.StartPreparingData(HeightData: TGLHeightData);
2014
var
2015
  y, x: Integer;
2016
  bmpSize, wrapMask: Integer;
2017
  bitmapLine, rasterLine: PByteArray;
2018
  oldType: TGLHeightDataType;
2019
  b: Byte;
2020
  YPos: Integer;
2021
begin
2022
  if FBitmap = nil then
2023
    Exit;
2024
  HeightData.FDataState := hdsPreparing;
2025
  bmpSize := FBitmap.Width;
2026
  wrapMask := bmpSize - 1;
2027
  // retrieve data
2028
  with HeightData do
2029
  begin
2030
    if (not InfiniteWrap) and ((XLeft >= bmpSize) or (XLeft < 0) or
2031
      (YTop >= bmpSize) or (YTop < 0)) then
2032
    begin
2033
      HeightData.FDataState := hdsNone;
2034
      Exit;
2035
    end;
2036
    oldType := DataType;
2037
    Allocate(hdtByte);
2038
    if Inverted then
2039
      YPos := YTop
2040
    else
2041
      YPos := 1 - size - YTop;
2042
    for y := 0 to size - 1 do
2043
    begin
2044
      bitmapLine := GetScanLine((y + YPos) and wrapMask);
2045
      if Inverted then
2046
        rasterLine := ByteRaster^[y]
2047
      else
2048
        rasterLine := ByteRaster^[size - 1 - y];
2049
      // *BIG CAUTION HERE* : Don't remove the intermediate variable here!!!
2050
      // or Delphi compiler will "optimize" to 32 bits access with clamping
2051
      // Resulting in possible reads of stuff beyon bitmapLine length!!!!
2052
      for x := XLeft to XLeft + size - 1 do
2053
      begin
2054
        b := bitmapLine^[x and wrapMask];
2055
        rasterLine^[x - XLeft] := b;
2056
      end;
2057
    end;
2058
    if (oldType <> hdtByte) and (oldType <> hdtDefault) then
2059
      DataType := oldType;
2060
  end;
2061
  TextureCoordinates(HeightData);
2062
  inherited;
2063
end;
2064

2065
function TGLBitmapHDS.Width: Integer;
2066
begin
2067
  if Assigned(self.FBitmap) then
2068
    Result := self.FBitmap.Width
2069
  else
2070
    Result := 0;
2071
end;
2072

2073
function TGLBitmapHDS.Height: Integer;
2074
begin
2075
  if Assigned(self.FBitmap) then
2076
    Result := self.FBitmap.Height
2077
  else
2078
    Result := 0;
2079
end;
2080

2081

2082
// ------------------
2083
// ------------------ TGLCustomHDS ------------------
2084
// ------------------
2085

2086
// Create
2087
//
2088
constructor TGLCustomHDS.Create(AOwner: TComponent);
2089
begin
2090
  inherited Create(AOwner);
2091
end;
2092

2093
// Destroy
2094
//
2095
destructor TGLCustomHDS.Destroy;
2096
begin
2097
  inherited Destroy;
2098
end;
2099

2100
// MarkDirty
2101
//
2102
procedure TGLCustomHDS.MarkDirty(const Area: TGLRect);
2103
begin
2104
  inherited;
2105
  if Assigned(FOnMarkDirty) then
2106
    FOnMarkDirty(Area);
2107
end;
2108

2109
// StartPreparingData
2110
//
2111
procedure TGLCustomHDS.StartPreparingData(HeightData: TGLHeightData);
2112
begin
2113
  if Assigned(FOnStartPreparingData) then
2114
    FOnStartPreparingData(HeightData);
2115
  if HeightData.DataState <> hdsNone then
2116
    HeightData.DataState := hdsReady;
2117
end;
2118

2119
// ------------------
2120
// ------------------ TGLTerrainBaseHDS ------------------
2121
// ------------------
2122

2123
// Create
2124
//
2125
constructor TGLTerrainBaseHDS.Create(AOwner: TComponent);
2126
begin
2127
  inherited Create(AOwner);
2128
end;
2129

2130
// Destroy
2131
//
2132
destructor TGLTerrainBaseHDS.Destroy;
2133
begin
2134
  inherited Destroy;
2135
end;
2136

2137
// StartPreparingData
2138
//
2139
procedure TGLTerrainBaseHDS.StartPreparingData(HeightData: TGLHeightData);
2140
const
2141
  cTBWidth: Integer = 4320;
2142
  cTBHeight: Integer = 2160;
2143
var
2144
  y, x, offset: Integer;
2145
  rasterLine: PSmallIntArray;
2146
  oldType: TGLHeightDataType;
2147
  b: SmallInt;
2148
  fs: TStream;
2149
begin
2150
  if not FileExists('tbase.bin') then
2151
    Exit;
2152
  fs := CreateFileStream('tbase.bin', fmOpenRead + fmShareDenyNone);
2153
  try
2154
    // retrieve data
2155
    with HeightData do
2156
    begin
2157
      oldType := DataType;
2158
      Allocate(hdtSmallInt);
2159
      for y := YTop to YTop + size - 1 do
2160
      begin
2161
        offset := (y mod cTBHeight) * (cTBWidth * 2);
2162
        rasterLine := SmallIntRaster^[y - YTop];
2163
        for x := XLeft to XLeft + size - 1 do
2164
        begin
2165
          fs.Seek(offset + (x mod cTBWidth) * 2, soFromBeginning);
2166
          fs.Read(b, 2);
2167
          if b < 0 then
2168
            b := 0;
2169
          rasterLine^[x - XLeft] := SmallInt(b);
2170
        end;
2171
      end;
2172
      if oldType <> hdtSmallInt then
2173
        DataType := oldType;
2174
    end;
2175
    inherited;
2176
  finally
2177
    fs.Free;
2178
  end;
2179
end;
2180

2181

2182
// ------------------
2183
// ------------------ TGLHeightDataSourceFilter ------------------
2184
// ------------------
2185

2186
constructor TGLHeightDataSourceFilter.Create(AOwner: TComponent);
2187
begin
2188
  inherited Create(AOwner);
2189
  FActive := True;
2190
end;
2191

2192
// Destroy
2193
//
2194
destructor TGLHeightDataSourceFilter.Destroy;
2195
begin
2196
  HeightDataSource := nil;
2197
  inherited Destroy;
2198
end;
2199

2200
procedure TGLHeightDataSourceFilter.Release(aHeightData: TGLHeightData);
2201
begin
2202
  if Assigned(HeightDataSource) then
2203
    HeightDataSource.Release(aHeightData);
2204
end;
2205

2206
// Notification
2207
//
2208
procedure TGLHeightDataSourceFilter.Notification(AComponent: TComponent;
2209
  Operation: TOperation);
2210
begin
2211
  if Operation = opRemove then
2212
  begin
2213
    if AComponent = FHDS then
2214
      HeightDataSource := nil
2215
  end;
2216
  inherited;
2217
end;
2218

2219
// SetHDS  - Set HeightDataSource property
2220
//
2221
procedure TGLHeightDataSourceFilter.SetHDS(Val: TGLHeightDataSource);
2222
begin
2223
  if Val = self then
2224
    Val := nil; // prevent self-referencing
2225
  if Val <> FHDS then
2226
  begin
2227
    if Assigned(FHDS) then
2228
      FHDS.RemoveFreeNotification(self);
2229
    FHDS := Val;
2230
    if Assigned(FHDS) then
2231
      FHDS.FreeNotification(self);
2232
    // MarkDirty;
2233
    self.Clear; // when removing the HDS, also remove all tiles from the cache
2234
  end;
2235
end;
2236

2237
function TGLHeightDataSourceFilter.Width: Integer;
2238
begin
2239
  if Assigned(FHDS) then
2240
    Result := FHDS.Width
2241
  else
2242
    Result := 0;
2243
end;
2244

2245
function TGLHeightDataSourceFilter.Height: Integer;
2246
begin
2247
  if Assigned(FHDS) then
2248
    Result := FHDS.Height
2249
  else
2250
    Result := 0;
2251
end;
2252

2253
procedure TGLHeightDataSourceFilter.StartPreparingData(HeightData: TGLHeightData);
2254
begin
2255
  // ---if there is no linked HDS then return an empty tile--
2256
  if not Assigned(FHDS) then
2257
  begin
2258
    HeightData.Owner.Data.LockList;
2259
    HeightData.DataState := hdsNone;
2260
    HeightData.Owner.Data.UnlockList;
2261
    Exit;
2262
  end;
2263
  // ---Use linked HeightDataSource to prepare height data--
2264
  if HeightData.DataState = hdsQueued then
2265
  begin
2266
    HeightData.Owner.Data.LockList;
2267
    HeightData.DataState := hdsPreparing;
2268
    HeightData.Owner.Data.UnlockList;
2269
  end;
2270
  FHDS.StartPreparingData(HeightData);
2271
  if Assigned(FOnSourceDataFetched) then
2272
    FOnSourceDataFetched(self, HeightData);
2273
  if HeightData.DataState = hdsNone then
2274
    Exit;
2275
  if FActive then
2276
    PreparingData(HeightData);
2277
  inherited; // HeightData.DataState:=hdsReady;
2278
end;
2279

2280
// ------------------------------------------------------------------
2281
// ------------------------------------------------------------------
2282
// ------------------------------------------------------------------
2283
initialization
2284

2285
// ------------------------------------------------------------------
2286
// ------------------------------------------------------------------
2287
// ------------------------------------------------------------------
2288

2289
// class registrations
2290
RegisterClasses([TGLBitmapHDS, TGLCustomHDS, TGLHeightDataSourceFilter]);
2291

2292
end.
2293

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.