GXS.HeightData.pas 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.HeightData;
  5. (*
  6. Classes for height data access.
  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. In short: access to raw height data is performed by a TgxHeightDataSource
  11. subclass, that must take care of performing all necessary data access,
  12. cacheing and manipulation to provide TgxHeightData objects. A TgxHeightData
  13. is basicly a square, power of two dimensionned raster heightfield, and
  14. holds the data a renderer needs.
  15. *)
  16. interface
  17. {$I Stage.Defines.inc}
  18. uses
  19. Winapi.Windows, // for CreateMonochromeBitmap
  20. System.Classes,
  21. System.SysUtils,
  22. FMX.Objects,
  23. FMX.Graphics,
  24. Stage.VectorGeometry,
  25. Stage.Utils,
  26. GXS.BaseClasses,
  27. GXS.ApplicationFileIO,
  28. GXS.ImageUtils,
  29. GXS.Material;
  30. type
  31. TByteArray = array [0 .. MaxInt div (2 * SizeOf(Byte))] of Byte;
  32. TByteRaster = array [0 .. MaxInt div (2 * SizeOf(Pointer))] of PByteArray;
  33. PByteRaster = ^TByteRaster;
  34. TSmallintArray = array [0 .. MaxInt div (2 * SizeOf(SmallInt))] of SmallInt;
  35. PSmallIntArray = ^TSmallintArray;
  36. TSmallIntRaster = array [0 .. MaxInt div (2 * SizeOf(Pointer))
  37. ] of PSmallIntArray;
  38. PSmallIntRaster = ^TSmallIntRaster;
  39. TSingleRaster = array [0 .. MaxInt div (2 * SizeOf(Pointer))] of PSingleArray;
  40. PSingleRaster = ^TSingleRaster;
  41. TgxHeightData = class;
  42. TgxHeightDataClass = class of TgxHeightData;
  43. (* Determines the type of data stored in a TgxHeightData.
  44. There are 3 data types (8 bits unsigned, signed 16 bits and 32 bits).
  45. Conversions: (128*(ByteValue-128)) = SmallIntValue = Round(SingleValue).
  46. The 'hdtDefault' type is used for request only, and specifies that the
  47. default type for the source should be used. *)
  48. TgxHeightDataType = (hdtByte, hdtSmallInt, hdtSingle, hdtDefault);
  49. (* Base class for height datasources.
  50. This class is abstract and presents the standard interfaces for height
  51. data retrieval (TgxHeightData objects). The class offers the following
  52. features (that a subclass may decide to implement or not, what follow
  53. is the complete feature set, check subclass doc to see what is actually
  54. supported):
  55. Pooling / Cacheing (return a TgxHeightData with its "Release" method)
  56. Pre-loading : specify a list of TgxHeightData you want to preload
  57. Multi-threaded preload/queueing : specified list can be loaded in
  58. a background task *)
  59. TgxHeightDataSource = class(TComponent)
  60. private
  61. FData: TThreadList; // stores all TgxHeightData, whatever their state/type
  62. FDataHash: array [0 .. 255] of TList; // X/Y hash references for HeightDatas
  63. FThread: TThread; // queue manager
  64. FMaxThreads: Integer;
  65. FMaxPoolSize: Integer;
  66. FHeightDataClass: TgxHeightDataClass;
  67. // FReleaseLatency : TDateTime; //Not used anymore???
  68. FDefaultHeight: Single;
  69. protected
  70. procedure SetMaxThreads(const Val: Integer);
  71. function HashKey(XLeft, YTop: Integer): Integer;
  72. // Adjust this property in you subclasses.
  73. property HeightDataClass: TgxHeightDataClass read FHeightDataClass
  74. write FHeightDataClass;
  75. (* Looks up the list and returns the matching TgxHeightData, if any. *)
  76. function FindMatchInList(XLeft, YTop, size: Integer;
  77. DataType: TgxHeightDataType): TgxHeightData;
  78. public
  79. constructor Create(AOwner: TComponent); override;
  80. destructor Destroy; override;
  81. (* Access to currently pooled TgxHeightData objects, and Thread locking *)
  82. property Data: TThreadList read FData;
  83. (* Empties the Data list, terminating thread if necessary.
  84. If some TgxHeightData are hdsInUse, triggers an exception and does
  85. nothing. *)
  86. procedure Clear;
  87. (* Removes less used TDataHeight objects from the pool.
  88. Only removes objects whose state is hdsReady and UseCounter is zero,
  89. starting from the end of the list until total data size gets below
  90. MaxPoolSize (or nothing can be removed). *)
  91. procedure CleanUp;
  92. (* Base TgxHeightData requester method.
  93. Returns (by rebuilding it or from the cache) a TgxHeightData
  94. corresponding to the given area. Size must be a power of two.
  95. Subclasses may choose to publish it or just publish datasource-
  96. specific requester method using specific parameters. *)
  97. function GetData(XLeft, YTop, size: Integer; DataType: TgxHeightDataType)
  98. : TgxHeightData; virtual;
  99. // Preloading request. See GetData for details.
  100. function PreLoad(XLeft, YTop, size: Integer; DataType: TgxHeightDataType)
  101. : TgxHeightData; virtual;
  102. // Replacing dirty tiles.
  103. procedure PreloadReplacement(aHeightData: TgxHeightData);
  104. (* Notification that the data is no longer used by the renderer.
  105. Default behaviour is just to change DataState to hdsReady (ie. return
  106. the data to the pool) *)
  107. procedure Release(aHeightData: TgxHeightData); virtual;
  108. (* Marks the given area as "dirty" (ie source data changed).
  109. All loaded and in-cache tiles overlapping the area are flushed. *)
  110. procedure MarkDirty(const Area: TRect); overload; virtual;
  111. procedure MarkDirty(XLeft, YTop, xRight, yBottom: Integer); overload;
  112. procedure MarkDirty; overload;
  113. (* Maximum number of background threads.
  114. If 0 (zero), multithreading is disabled and StartPreparingData
  115. will be called from the mainthread, and all preload requirements
  116. (queued TgxHeightData objects) will be loaded in sequence from
  117. the main thread.
  118. If 1, basic multithreading and queueing gets enabled,
  119. ie. StartPreparingData will be called from a thread, but from one
  120. thread only (ie. there is no need to implement a TgxHeightDataThread,
  121. just make sure StartPreparingData code is thread-safe).
  122. Other values (2 and more) are relevant only if you implement
  123. a TgxHeightDataThread subclass and fire it in StartPreparingData. *)
  124. property MaxThreads: Integer read FMaxThreads write SetMaxThreads;
  125. (* Maximum Size of TDataHeight pool in bytes.
  126. The pool (cache) can actually get larger if more data than the pool
  127. can accomodate is used, but as soon as data gets released and returns
  128. to the pool, TDataHeight will be freed until total pool Size gets
  129. below this figure.
  130. The pool manager frees TDataHeight objects who haven't been requested
  131. for the longest time first.
  132. The default value of zero effectively disables pooling. *)
  133. property MaxPoolSize: Integer read FMaxPoolSize write FMaxPoolSize;
  134. // Height to return for undefined tiles.
  135. property DefaultHeight: Single read FDefaultHeight write FDefaultHeight;
  136. // Interpolates height for the given point.
  137. function InterpolatedHeight(x, y: Single; tileSize: Integer)
  138. : Single; virtual;
  139. function Width: Integer; virtual; abstract;
  140. function Height: Integer; virtual; abstract;
  141. procedure ThreadIsIdle; virtual;
  142. (* This is called BEFORE StartPreparing Data, but always from the main thread. *)
  143. procedure BeforePreparingData(HeightData: TgxHeightData); virtual;
  144. (* Request to start preparing data.
  145. If your subclass is thread-enabled, this is here that you'll create
  146. your thread and fire it (don't forget the requirements), if not,
  147. that'll be here you'll be doing your work.
  148. Either way, you are responsible for adjusting the DataState to
  149. hdsReady when you're done (DataState will be hdsPreparing when this
  150. method will be invoked). *)
  151. procedure StartPreparingData(HeightData: TgxHeightData); virtual;
  152. (* This is called After "StartPreparingData", but always from the main thread. *)
  153. procedure AfterPreparingData(HeightData: TgxHeightData); virtual;
  154. procedure TextureCoordinates(HeightData: TgxHeightData;
  155. Stretch: boolean = false);
  156. end;
  157. THDTextureCoordinatesMode = (tcmWorld, tcmLocal);
  158. (* Possible states for a TgxHeightData.
  159. hdsQueued : the data has been queued for loading
  160. hdsPreparing : the data is currently loading or being prepared for use
  161. hdsReady : the data is fully loaded and ready for use
  162. hdsNone : the height data does not exist for this tile *)
  163. TgxHeightDataState = (hdsQueued, hdsPreparing, hdsReady, hdsNone);
  164. TgxHeightDataThread = class;
  165. TOnHeightDataDirtyEvent = procedure(sender: TgxHeightData) of object;
  166. TgxHeightDataUser = record
  167. user: TObject;
  168. event: TOnHeightDataDirtyEvent;
  169. end;
  170. (* Base class for height data, stores a height-field raster.
  171. The raster is a square, whose Size must be a power of two. Data can be
  172. accessed through a base pointer ("ByteData[n]" f.i.), or through pointer
  173. indirections ("ByteRaster[y][x]" f.i.), this are the fastest way to access
  174. height data (and the most unsecure).
  175. Secure (with range checking) data access is provided by specialized
  176. methods (f.i. "ByteHeight"), in which coordinates (x & y) are always
  177. considered relative (like in raster access).
  178. The class offers conversion facility between the types (as a whole data
  179. conversion), but in any case, the TgxHeightData should be directly requested
  180. from the TgxHeightDataSource with the appropriate format.
  181. Though this class can be instantiated, you will usually prefer to subclass
  182. it in real-world cases, f.i. to add texturing data. *)
  183. /// TgxHeightData = class (TObject)
  184. TgxHeightData = class(TgxUpdateAbleObject)
  185. private
  186. FUsers: array of TgxHeightDataUser;
  187. FOwner: TgxHeightDataSource;
  188. FDataState: TgxHeightDataState;
  189. FSize: Integer;
  190. FXLeft, FYTop: Integer;
  191. FUseCounter: Integer;
  192. FDataType: TgxHeightDataType;
  193. FDataSize: Integer;
  194. FByteData: PByteArray;
  195. FByteRaster: PByteRaster;
  196. FSmallIntData: PSmallIntArray;
  197. FSmallIntRaster: PSmallIntRaster;
  198. FSingleData: PSingleArray;
  199. FSingleRaster: PSingleRaster;
  200. FTextureCoordinatesMode: THDTextureCoordinatesMode;
  201. FTCOffset, FTCScale: TTexPoint;
  202. FMaterialName: String; // Unsafe. Use FLibMaterial instead
  203. FLibMaterial: TgxLibMaterial;
  204. FObjectTag: TObject;
  205. FTag, FTag2: Integer;
  206. FOnDestroy: TNotifyEvent;
  207. FDirty: boolean;
  208. FHeightMin, FHeightMax: Single;
  209. procedure BuildByteRaster;
  210. procedure BuildSmallIntRaster;
  211. procedure BuildSingleRaster;
  212. procedure ConvertByteToSmallInt;
  213. procedure ConvertByteToSingle;
  214. procedure ConvertSmallIntToByte;
  215. procedure ConvertSmallIntToSingle;
  216. procedure ConvertSingleToByte;
  217. procedure ConvertSingleToSmallInt;
  218. protected
  219. FThread: TgxHeightDataThread;
  220. // thread used for multi-threaded processing (if any)
  221. procedure SetDataType(const Val: TgxHeightDataType);
  222. procedure SetMaterialName(const MaterialName: string);
  223. procedure SetLibMaterial(LibMaterial: TgxLibMaterial);
  224. function GetHeightMin: Single;
  225. function GetHeightMax: Single;
  226. public
  227. OldVersion: TgxHeightData; // previous version of this tile
  228. NewVersion: TgxHeightData; // the replacement tile
  229. DontUse: boolean; // Tells TerrainRenderer which version to use
  230. // constructor Create(AOwner : TComponent); override;
  231. constructor Create(AOwner: TgxHeightDataSource; aXLeft, aYTop, aSize: Integer;
  232. aDataType: TgxHeightDataType); reintroduce; virtual;
  233. destructor Destroy; override;
  234. { The component who created and maintains this data. }
  235. property Owner: TgxHeightDataSource read FOwner;
  236. { Fired when the object is destroyed. }
  237. property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
  238. { Counter for use registration.
  239. A TgxHeightData is not returned to the pool until this counter reaches
  240. a value of zero. }
  241. property UseCounter: Integer read FUseCounter;
  242. { Increments UseCounter.
  243. User objects should implement a method that will be notified when
  244. the data becomes dirty, when invoked they should release the heightdata
  245. immediately after performing their own cleanups. }
  246. procedure RegisterUse;
  247. { Allocate memory and prepare lookup tables for current datatype.
  248. Fails if already allocated. Made Dynamic to allow descendants }
  249. procedure Allocate(const Val: TgxHeightDataType); virtual;
  250. { Decrements UseCounter.
  251. When the counter reaches zero, notifies the Owner TgxHeightDataSource
  252. that the data is no longer used.
  253. The renderer should call Release when it no longer needs a THeighData,
  254. and never free/destroy the object directly. }
  255. procedure Release;
  256. { Marks the tile as dirty.
  257. The immediate effect is currently the destruction of the tile. }
  258. procedure MarkDirty;
  259. { World X coordinate of top left point. }
  260. property XLeft: Integer read FXLeft;
  261. { World Y coordinate of top left point. }
  262. property YTop: Integer read FYTop;
  263. { Type of the data.
  264. Assigning a new datatype will result in the data being converted. }
  265. property DataType: TgxHeightDataType read FDataType write SetDataType;
  266. { Current state of the data. }
  267. property DataState: TgxHeightDataState read FDataState write FDataState;
  268. { Size of the data square, in data units. }
  269. property Size: Integer read FSize;
  270. { True if the data is dirty (ie. no longer up-to-date). }
  271. property Dirty: boolean read FDirty write FDirty;
  272. { Memory Size of the raw data in bytes. }
  273. property DataSize: Integer read FDataSize;
  274. { Access to data as a byte array (n = y*Size+x).
  275. If TgxHeightData is not of type hdtByte, this value is nil. }
  276. property ByteData: PByteArray read FByteData;
  277. { Access to data as a byte raster (y, x).
  278. If TgxHeightData is not of type hdtByte, this value is nil. }
  279. property ByteRaster: PByteRaster read FByteRaster;
  280. { Access to data as a SmallInt array (n = y*Size+x).
  281. If TgxHeightData is not of type hdtSmallInt, this value is nil. }
  282. property SmallIntData: PSmallIntArray read FSmallIntData;
  283. { Access to data as a SmallInt raster (y, x).
  284. If TgxHeightData is not of type hdtSmallInt, this value is nil. }
  285. property SmallIntRaster: PSmallIntRaster read FSmallIntRaster;
  286. { Access to data as a Single array (n = y*Size+x).
  287. If TgxHeightData is not of type hdtSingle, this value is nil. }
  288. property SingleData: PSingleArray read FSingleData;
  289. { Access to data as a Single raster (y, x).
  290. If TgxHeightData is not of type hdtSingle, this value is nil. }
  291. property SingleRaster: PSingleRaster read FSingleRaster;
  292. { Name of material for the tile (if terrain uses multiple materials). }
  293. // property MaterialName : String read FMaterialName write FMaterialName;
  294. // (WARNING: Unsafe when deleting textures! If possible, rather use LibMaterial.)
  295. property MaterialName: String read FMaterialName write SetMaterialName;
  296. // property LibMaterial : Links directly to the tile's TgxLibMaterial.
  297. // Unlike 'MaterialName', this property also registers the tile as
  298. // a user of the texture.
  299. // This prevents TgxLibMaterials.DeleteUnusedTextures from deleting the
  300. // used texture by mistake and causing Access Violations.
  301. // Use this instead of the old MaterialName property, to prevent AV's.
  302. property LibMaterial: TgxLibMaterial read FLibMaterial write SetLibMaterial;
  303. { Texture coordinates generation mode.
  304. Default is tcmWorld coordinates. }
  305. property TextureCoordinatesMode: THDTextureCoordinatesMode
  306. read FTextureCoordinatesMode write FTextureCoordinatesMode;
  307. property TextureCoordinatesOffset: TTexPoint read FTCOffset write FTCOffset;
  308. property TextureCoordinatesScale: TTexPoint read FTCScale write FTCScale;
  309. { Height of point x, y as a Byte. }
  310. function ByteHeight(x, y: Integer): Byte;
  311. { Height of point x, y as a SmallInt. }
  312. function SmallIntHeight(x, y: Integer): SmallInt;
  313. { Height of point x, y as a Single. }
  314. function SingleHeight(x, y: Integer): Single;
  315. { Interopolated height of point x, y as a Single. }
  316. function InterpolatedHeight(x, y: Single): Single;
  317. { Minimum height in the tile.
  318. DataSources may assign a value to prevent automatic computation
  319. if they have a faster/already computed value. }
  320. property HeightMin: Single read GetHeightMin write FHeightMin;
  321. { Maximum height in the tile.
  322. DataSources may assign a value to prevent automatic computation
  323. if they have a faster/already computed value. }
  324. property HeightMax: Single read GetHeightMax write FHeightMax;
  325. { Returns the height as a single, whatever the DataType (slow). }
  326. function Height(x, y: Integer): Single;
  327. { Calculates and returns the normal for vertex point x, y.
  328. Sub classes may provide normal cacheing, the default implementation
  329. being rather blunt. }
  330. function Normal(x, y: Integer; const scale: TAffineVector)
  331. : TAffineVector; virtual;
  332. { Calculates and returns the normal for cell x, y.(between vertexes) }
  333. function NormalAtNode(x, y: Integer; const scale: TAffineVector)
  334. : TAffineVector; virtual;
  335. { Returns True if the data tile overlaps the area. }
  336. function OverlapsArea(const Area: TRect): boolean;
  337. { Reserved for renderer use. }
  338. property ObjectTag: TObject read FObjectTag write FObjectTag;
  339. { Reserved for renderer use. }
  340. property Tag: Integer read FTag write FTag;
  341. { Reserved for renderer use. }
  342. property Tag2: Integer read FTag2 write FTag2;
  343. { Used by perlin HDS. }
  344. property Thread: TgxHeightDataThread read FThread write FThread;
  345. end;
  346. { A thread specialized for processing TgxHeightData in background.
  347. Requirements:
  348. must have FreeOnTerminate set to true,
  349. must check and honour Terminated swiftly }
  350. TgxHeightDataThread = class(TThread)
  351. protected
  352. FHeightData: TgxHeightData;
  353. public
  354. destructor Destroy; override;
  355. { The Height Data the thread is to prepare. }
  356. property HeightData: TgxHeightData read FHeightData write FHeightData;
  357. end;
  358. { Bitmap-based Height Data Source.
  359. The image is automatically wrapped if requested data is out of picture Size,
  360. or if requested data is larger than the picture.
  361. The internal format is an 8 bit bitmap whose dimensions are a power of two,
  362. if the original image does not comply, it is StretchDraw'ed on a monochrome
  363. (gray) bitmap. }
  364. TgxBitmapHDS = class(TgxHeightDataSource)
  365. private
  366. FScanLineCache: array of PByteArray;
  367. FBitmap: TBitmap;
  368. FPicture: TImage;
  369. FInfiniteWrap: boolean;
  370. FInverted: boolean;
  371. protected
  372. procedure SetPicture(const Val: TImage);
  373. procedure OnPictureChanged(sender: TObject);
  374. procedure SetInfiniteWrap(Val: boolean);
  375. procedure SetInverted(Val: boolean);
  376. procedure CreateMonochromeBitmap(size: Integer);
  377. procedure FreeMonochromeBitmap;
  378. function GetScanLine(y: Integer): PByteArray;
  379. public
  380. constructor Create(AOwner: TComponent); override;
  381. destructor Destroy; override;
  382. procedure StartPreparingData(HeightData: TgxHeightData); override;
  383. procedure MarkDirty(const Area: TRect); override;
  384. function Width: Integer; override;
  385. function Height: Integer; override;
  386. published
  387. { The picture serving as Height field data reference.
  388. The picture is (if not already) internally converted to a 8 bit
  389. bitmap (grayscale). For better performance and to save memory,
  390. feed it this format! }
  391. property Picture: TImage read FPicture write SetPicture;
  392. { If true the height field is wrapped indefinetely. }
  393. property InfiniteWrap: boolean read FInfiniteWrap write SetInfiniteWrap
  394. default True;
  395. { If true, the rendered terrain is a mirror image of the input data. }
  396. property Inverted: boolean read FInverted write SetInverted default True;
  397. property MaxPoolSize;
  398. end;
  399. TStartPreparingDataEvent = procedure(HeightData: TgxHeightData) of object;
  400. TMarkDirtyEvent = procedure(const Area: TRect) of object;
  401. // TTexturedHeightDataSource = class (TgxTexturedHeightDataSource)
  402. { An Height Data Source for custom use.
  403. Provides event handlers for the various requests to be implemented
  404. application-side (for application-specific needs). }
  405. TgxCustomHDS = class(TgxHeightDataSource)
  406. private
  407. FOnStartPreparingData: TStartPreparingDataEvent;
  408. FOnMarkDirty: TMarkDirtyEvent;
  409. public
  410. constructor Create(AOwner: TComponent); override;
  411. destructor Destroy; override;
  412. procedure StartPreparingData(HeightData: TgxHeightData); override;
  413. procedure MarkDirty(const Area: TRect); override;
  414. published
  415. property MaxPoolSize;
  416. property OnStartPreparingData: TStartPreparingDataEvent
  417. read FOnStartPreparingData write FOnStartPreparingData;
  418. property OnMarkDirtyEvent: TMarkDirtyEvent read FOnMarkDirty
  419. write FOnMarkDirty;
  420. end;
  421. { TerrainBase-based Height Data Source.
  422. This component takes its data from the TerrainBase Gobal Terrain Model.
  423. Though it can be used directly, the resolution of the TerrainBase dataset
  424. isn't high enough for accurate short-range representation and the data
  425. should rather be used as basis for further (fractal) refinement.
  426. TerrainBase is freely available from the National Geophysical Data Center
  427. and World Data Center web site (http://ngdc.noaa.com).
  428. (this component expects to find "tbase.bin" in the current directory). }
  429. TgxTerrainBaseHDS = class(TgxHeightDataSource)
  430. public
  431. constructor Create(AOwner: TComponent); override;
  432. destructor Destroy; override;
  433. procedure StartPreparingData(HeightData: TgxHeightData); override;
  434. published
  435. property MaxPoolSize;
  436. end;
  437. TgxHeightDataSourceFilter = Class;
  438. TSourceDataFetchedEvent = procedure(sender: TgxHeightDataSourceFilter;
  439. HeightData: TgxHeightData) of object;
  440. { Height Data Source Filter.
  441. This component sits between the TgxTerrainRenderer, and a real TgxHeightDataSource.
  442. i.e. TgxTerrainRenderer links to this. This links to the real TgxHeightDataSource.
  443. Use the 'HeightDataSource' property, to link to a source HDS.
  444. The 'OnSourceDataFetched' event then gives you the opportunity to make any changes,
  445. or link in a texture to the TgxHeightData object, BEFORE it is cached.
  446. It bypasses the cache of the source HDS, by calling the source's StartPreparingData procedure directly.
  447. The TgxHeightData objects are then cached by THIS component, AFTER you have made your changes.
  448. This eliminates the need to copy and release the TgxHeightData object from the Source HDS's cache,
  449. before linking your texture. See the new version of TgxBumpmapHDS for an example. (LIN)
  450. To create your own HDSFilters, Derive from this component, and override the PreparingData procedure. }
  451. TgxHeightDataSourceFilter = Class(TgxHeightDataSource)
  452. private
  453. FHDS: TgxHeightDataSource;
  454. FOnSourceDataFetched: TSourceDataFetchedEvent;
  455. FActive: boolean;
  456. protected
  457. { PreparingData:
  458. Override this function in your filter subclasses, to make any
  459. updates/changes to HeightData, before it goes into the cache.
  460. Make sure any code in this function is thread-safe, in case TAsyncHDS was used. }
  461. procedure PreparingData(HeightData: TgxHeightData); virtual; abstract;
  462. { SetHDS - Set HeightDataSource property }
  463. procedure SetHDS(Val: TgxHeightDataSource);
  464. public
  465. constructor Create(AOwner: TComponent); override;
  466. destructor Destroy; override;
  467. procedure Release(aHeightData: TgxHeightData); override;
  468. procedure StartPreparingData(HeightData: TgxHeightData); override;
  469. procedure Notification(AComponent: TComponent;
  470. Operation: TOperation); override;
  471. function Width: Integer; override;
  472. function Height: Integer; override;
  473. property OnSourceDataFetched: TSourceDataFetchedEvent
  474. read FOnSourceDataFetched write FOnSourceDataFetched;
  475. published
  476. property MaxPoolSize;
  477. property HeightDataSource: TgxHeightDataSource read FHDS write SetHDS;
  478. property Active: boolean read FActive write FActive;
  479. // If Active=False, height data passes through unchanged
  480. end;
  481. // ------------------------------------------------------------------
  482. implementation
  483. // ------------------------------------------------------------------
  484. // ------------------
  485. // ------------------ TgxHeightDataSourceThread ------------------
  486. // ------------------
  487. type
  488. TgxHeightDataSourceThread = class(TThread)
  489. FOwner: TgxHeightDataSource;
  490. procedure Execute; override;
  491. function WaitForTile(HD: TgxHeightData; seconds: Integer): boolean;
  492. procedure HDSIdle;
  493. end;
  494. // Execute
  495. //
  496. procedure TgxHeightDataSourceThread.Execute;
  497. var
  498. i: Integer;
  499. lst: TList;
  500. HD: TgxHeightData;
  501. max: Integer;
  502. TdCtr: Integer;
  503. begin
  504. while not Terminated do
  505. begin
  506. max := FOwner.MaxThreads;
  507. lst := FOwner.FData.LockList;
  508. // --count active threads--
  509. i := 0;
  510. TdCtr := 0;
  511. while (i < lst.Count) and (TdCtr < max) do
  512. begin
  513. if TgxHeightData(lst.Items[i]).FThread <> nil then
  514. Inc(TdCtr);
  515. Inc(i);
  516. end;
  517. // ------------------------
  518. // --Find the queued tiles, and Start preparing them--
  519. i := 0;
  520. While ((i < lst.Count) and (TdCtr < max)) do
  521. begin
  522. HD := TgxHeightData(lst.Items[i]);
  523. if HD.DataState = hdsQueued then
  524. begin
  525. FOwner.StartPreparingData(HD); // prepare
  526. Inc(TdCtr);
  527. end;
  528. Inc(i);
  529. end;
  530. // ---------------------------------------------------
  531. FOwner.FData.UnlockList;
  532. if (TdCtr = 0) then
  533. synchronize(HDSIdle);
  534. if (TdCtr = 0) then
  535. sleep(10)
  536. else
  537. sleep(0); // sleep longer if no Queued tiles were found
  538. end;
  539. end;
  540. // WaitForTile
  541. //
  542. // When Threading, wait a specified time, for the tile to finish preparing
  543. function TgxHeightDataSourceThread.WaitForTile(HD: TgxHeightData;
  544. seconds: Integer): boolean;
  545. var
  546. // i:integer;
  547. eTime: TDateTime;
  548. begin
  549. eTime := now + (1000 * seconds);
  550. while (HD.FThread <> nil) and (now < eTime) do
  551. begin
  552. sleep(0);
  553. end;
  554. Result := (HD.FThread = nil); // true if the thread has finished
  555. end;
  556. // HDSIdle
  557. //
  558. // When using threads, HDSIdle is called in the main thread,
  559. // whenever all HDS threads have finished, AND no queued tiles were found.
  560. // (GXS.AsyncHDS uses this for the OnIdle event.)
  561. procedure TgxHeightDataSourceThread.HDSIdle;
  562. begin
  563. self.FOwner.ThreadIsIdle;
  564. end;
  565. // ------------------
  566. // ------------------ TgxHeightDataSource ------------------
  567. // ------------------
  568. // Create
  569. //
  570. constructor TgxHeightDataSource.Create(AOwner: TComponent);
  571. var
  572. i: Integer;
  573. begin
  574. inherited Create(AOwner);
  575. FHeightDataClass := TgxHeightData;
  576. FData := TThreadList.Create;
  577. for i := 0 to High(FDataHash) do
  578. FDataHash[i] := TList.Create;
  579. // FReleaseLatency:=15/(3600*24);
  580. FThread := TgxHeightDataSourceThread.Create(True);
  581. FThread.FreeOnTerminate := false;
  582. TgxHeightDataSourceThread(FThread).FOwner := self;
  583. if self.MaxThreads > 0 then
  584. FThread.Start;
  585. end;
  586. // Destroy
  587. //
  588. destructor TgxHeightDataSource.Destroy;
  589. var
  590. i: Integer;
  591. begin
  592. inherited Destroy;
  593. if Assigned(FThread) then
  594. begin
  595. FThread.Terminate;
  596. FThread.Start;
  597. FThread.WaitFor;
  598. FThread.Free;
  599. end;
  600. Clear;
  601. FData.Free;
  602. for i := 0 to High(FDataHash) do
  603. FDataHash[i].Free;
  604. end;
  605. // Clear
  606. //
  607. procedure TgxHeightDataSource.Clear;
  608. var
  609. i: Integer;
  610. begin
  611. with FData.LockList do
  612. begin
  613. try
  614. for i := 0 to Count - 1 do
  615. if TgxHeightData(Items[i]).UseCounter > 0 then
  616. if not(csDestroying in ComponentState) then
  617. raise Exception.Create('ERR: HeightData still in use');
  618. for i := 0 to Count - 1 do
  619. begin
  620. TgxHeightData(Items[i]).FOwner := nil;
  621. TgxHeightData(Items[i]).Free;
  622. end;
  623. for i := 0 to High(FDataHash) do
  624. FDataHash[i].Clear;
  625. Clear;
  626. finally
  627. FData.UnlockList;
  628. end;
  629. end;
  630. end;
  631. // HashKey
  632. //
  633. function TgxHeightDataSource.HashKey(XLeft, YTop: Integer): Integer;
  634. begin
  635. Result := (XLeft + (XLeft shr 8) + (YTop shl 1) + (YTop shr 7)) and
  636. High(FDataHash);
  637. end;
  638. // FindMatchInList
  639. //
  640. function TgxHeightDataSource.FindMatchInList(XLeft, YTop, size: Integer;
  641. DataType: TgxHeightDataType): TgxHeightData;
  642. var
  643. i: Integer;
  644. HD: TgxHeightData;
  645. begin
  646. Result := nil;
  647. FData.LockList;
  648. try
  649. with FDataHash[HashKey(XLeft, YTop)] do
  650. for i := 0 to Count - 1 do
  651. begin
  652. HD := TgxHeightData(Items[i]);
  653. // if (not hd.Dirty) and (hd.XLeft=xLeft) and (hd.YTop=YTop) and (hd.Size=Size) and (hd.DataType=DataType) then begin
  654. if (HD.XLeft = XLeft) and (HD.YTop = YTop) and (HD.size = size) and
  655. (HD.DataType = DataType) and (HD.DontUse = false) then
  656. begin
  657. Result := HD;
  658. Break;
  659. end;
  660. end;
  661. finally
  662. FData.UnlockList;
  663. end;
  664. end;
  665. // GetData
  666. //
  667. function TgxHeightDataSource.GetData(XLeft, YTop, size: Integer;
  668. DataType: TgxHeightDataType): TgxHeightData;
  669. begin
  670. Result := FindMatchInList(XLeft, YTop, size, DataType);
  671. if not Assigned(Result) then
  672. Result := PreLoad(XLeft, YTop, size, DataType)
  673. else
  674. with FData.LockList do
  675. begin
  676. try
  677. Move(IndexOf(Result), 0); // Moves item to the beginning of the list.
  678. finally
  679. FData.UnlockList;
  680. end;
  681. end;
  682. // got one... can be used ?
  683. // while not (Result.DataState in [hdsReady, hdsNone]) do Sleep(0);
  684. end;
  685. // PreLoad
  686. //
  687. function TgxHeightDataSource.PreLoad(XLeft, YTop, size: Integer;
  688. DataType: TgxHeightDataType): TgxHeightData;
  689. begin
  690. Result := HeightDataClass.Create(self, XLeft, YTop, size, DataType);
  691. with FData.LockList do
  692. try
  693. Add(Result);
  694. BeforePreparingData(Result);
  695. FDataHash[HashKey(XLeft, YTop)].Add(Result);
  696. finally
  697. FData.UnlockList;
  698. end;
  699. // -- When NOT using Threads, fully prepare the tile immediately--
  700. if MaxThreads = 0 then
  701. begin
  702. StartPreparingData(Result);
  703. AfterPreparingData(Result);
  704. end;
  705. // ---------------------------------------------------------------
  706. end;
  707. // When Multi-threading, this queues a replacement for a dirty tile
  708. // The Terrain renderer will continue to use the dirty tile, until the replacement is complete
  709. procedure TgxHeightDataSource.PreloadReplacement(aHeightData: TgxHeightData);
  710. var
  711. HD: TgxHeightData;
  712. NewHD: TgxHeightData;
  713. begin
  714. Assert(MaxThreads > 0);
  715. HD := aHeightData;
  716. NewHD := HeightDataClass.Create(self, HD.XLeft, HD.YTop, HD.size,
  717. HD.DataType);
  718. with FData.LockList do
  719. try
  720. Add(NewHD);
  721. NewHD.OldVersion := HD; // link
  722. HD.NewVersion := NewHD; // link
  723. NewHD.DontUse := True;
  724. BeforePreparingData(NewHD);
  725. FDataHash[HashKey(HD.XLeft, HD.YTop)].Add(NewHD);
  726. finally
  727. FData.UnlockList;
  728. end;
  729. end;
  730. procedure TgxHeightDataSource.Release(aHeightData: TgxHeightData);
  731. begin
  732. // nothing, yet
  733. end;
  734. procedure TgxHeightDataSource.MarkDirty(const Area: TRect);
  735. var
  736. i: Integer;
  737. HD: TgxHeightData;
  738. begin
  739. with FData.LockList do
  740. begin
  741. try
  742. for i := Count - 1 downto 0 do
  743. begin
  744. HD := TgxHeightData(Items[i]);
  745. if HD.OverlapsArea(Area) then
  746. HD.MarkDirty;
  747. end;
  748. finally
  749. FData.UnlockList;
  750. end;
  751. end;
  752. end;
  753. procedure TgxHeightDataSource.MarkDirty(XLeft, YTop, xRight, yBottom: Integer);
  754. var
  755. r: TRect;
  756. begin
  757. r.Left := XLeft;
  758. r.Top := YTop;
  759. r.Right := xRight;
  760. r.Bottom := yBottom;
  761. MarkDirty(r);
  762. end;
  763. procedure TgxHeightDataSource.MarkDirty;
  764. const
  765. m = MaxInt - 1;
  766. begin
  767. MarkDirty(-m, -m, m, m);
  768. end;
  769. // CleanUp
  770. //
  771. procedure TgxHeightDataSource.CleanUp;
  772. var
  773. packList: boolean;
  774. i, k: Integer;
  775. usedMemory: Integer;
  776. HD: TgxHeightData;
  777. ReleaseThis: boolean;
  778. begin
  779. with FData.LockList do
  780. begin
  781. try
  782. usedMemory := 0;
  783. packList := false;
  784. // Cleanup dirty tiles and compute used memory
  785. for i := Count - 1 downto 0 do
  786. begin
  787. HD := TgxHeightData(Items[i]);
  788. if HD <> nil then
  789. with HD do
  790. begin
  791. // --Release criteria for dirty tiles--
  792. ReleaseThis := false;
  793. if HD.Dirty then
  794. begin // Only release dirty tiles
  795. if (MaxThreads = 0) then
  796. ReleaseThis := True
  797. // when not threading, delete ALL dirty tiles
  798. else if (HD.DataState <> hdsPreparing) then
  799. begin // Dont release Preparing tiles
  800. if (HD.UseCounter = 0) then
  801. ReleaseThis := True; // This tile is unused
  802. if (HD.NewVersion = nil) then
  803. ReleaseThis := True
  804. // This tile has no queued replacement to wait for
  805. else if (HD.DontUse) then
  806. ReleaseThis := True; // ??This tile has already been replaced.
  807. end;
  808. end;
  809. // ------------------------------------
  810. // if Dirty then ReleaseThis:=true;
  811. if ReleaseThis then
  812. begin
  813. FDataHash[HashKey(HD.XLeft, HD.YTop)].Remove(HD);
  814. Items[i] := nil;
  815. FOwner := nil;
  816. Free;
  817. packList := True;
  818. end
  819. else
  820. usedMemory := usedMemory + HD.DataSize;
  821. end;
  822. end;
  823. // If MaxPoolSize exceeded, release all that may be, and pack the list
  824. k := 0;
  825. if usedMemory > MaxPoolSize then
  826. begin
  827. for i := 0 to Count - 1 do
  828. begin
  829. HD := TgxHeightData(Items[i]);
  830. if HD <> nil then
  831. with HD do
  832. begin
  833. if (DataState <> hdsPreparing) and (UseCounter = 0) and
  834. (OldVersion = nil)
  835. // if (DataState=hdsReady)and(UseCounter=0)and(OldVersion=nil)
  836. then
  837. begin
  838. FDataHash[HashKey(HD.XLeft, HD.YTop)].Remove(HD);
  839. Items[i] := nil;
  840. FOwner := nil;
  841. Free;
  842. // packList:=True;
  843. end
  844. else
  845. begin
  846. Items[k] := HD;
  847. Inc(k);
  848. end;
  849. end;
  850. end;
  851. Count := k;
  852. end
  853. else if packList then
  854. begin
  855. for i := 0 to Count - 1 do
  856. if Items[i] <> nil then
  857. begin
  858. Items[k] := Items[i];
  859. Inc(k);
  860. end;
  861. Count := k;
  862. end;
  863. finally
  864. FData.UnlockList;
  865. end;
  866. end;
  867. end;
  868. // SetMaxThreads
  869. //
  870. procedure TgxHeightDataSource.SetMaxThreads(const Val: Integer);
  871. begin
  872. if (Val <= 0) then
  873. FMaxThreads := 0
  874. else
  875. begin
  876. // If we didn't do threading, but will now
  877. // resume our thread
  878. if (FMaxThreads <= 0) then
  879. FThread.Start;
  880. FMaxThreads := Val;
  881. end;
  882. end;
  883. // BeforePreparingData
  884. // Called BEFORE StartPreparingData, but always from the MAIN thread.
  885. // Override this in subclasses, to prepare for Threading.
  886. //
  887. procedure TgxHeightDataSource.BeforePreparingData(HeightData: TgxHeightData);
  888. begin
  889. //
  890. end;
  891. // StartPreparingData
  892. // When Threads are used, this runs from the sub-thread, so this MUST be thread-safe.
  893. // Any Non-thread-safe code should be placed in "BeforePreparingData"
  894. //
  895. procedure TgxHeightDataSource.StartPreparingData(HeightData: TgxHeightData);
  896. begin
  897. // Only the tile Owner may set the preparing tile to ready
  898. if (HeightData.Owner = self) and (HeightData.DataState = hdsPreparing) then
  899. HeightData.FDataState := hdsReady;
  900. end;
  901. // AfterPreparingData
  902. // Called AFTER StartPreparingData, but always from the MAIN thread.
  903. // Override this in subclasses, if needed.
  904. //
  905. procedure TgxHeightDataSource.AfterPreparingData(HeightData: TgxHeightData);
  906. begin
  907. //
  908. end;
  909. // ThreadIsIdle
  910. //
  911. procedure TgxHeightDataSource.ThreadIsIdle;
  912. begin
  913. // TgxAsyncHDS overrides this
  914. end;
  915. // TextureCoordinates
  916. // Calculates texture World texture coordinates for the current tile.
  917. // Use Stretch for OpenGL1.1, to hide the seams when using linear filtering.
  918. procedure TgxHeightDataSource.TextureCoordinates(HeightData: TgxHeightData;
  919. Stretch: boolean = false);
  920. var
  921. w, h, size: Integer;
  922. scaleS, scaleT: Single;
  923. offsetS, offsetT: Single;
  924. HD: TgxHeightData;
  925. halfpixel: Single;
  926. begin
  927. HD := HeightData;
  928. w := self.Width;
  929. h := self.Height;
  930. size := HD.FSize;
  931. // if GL_VERSION_1_2 then begin //OpenGL1.2 supports texture clamping, so seams dont show.
  932. if Stretch = false then
  933. begin // These are the real Texture coordinates
  934. scaleS := w / (size - 1);
  935. scaleT := h / (size - 1);
  936. offsetS := -((HD.XLeft / w) * scaleS);
  937. offsetT := -(h - (HD.YTop + size - 1)) / (size - 1);
  938. end
  939. else
  940. begin // --Texture coordinates: Stretched by 1 pixel, to hide seams on OpenGL-1.1(no Clamping)--
  941. scaleS := w / size;
  942. scaleT := h / size;
  943. halfpixel := 1 / (size shr 1);
  944. offsetS := -((HD.XLeft / w) * scaleS) + halfpixel;
  945. offsetT := -(h - (HD.YTop + size)) / size - halfpixel;
  946. end;
  947. HD.FTCScale.S := scaleS;
  948. HD.FTCScale.T := scaleT;
  949. HD.FTCOffset.S := offsetS;
  950. HD.FTCOffset.T := offsetT;
  951. end;
  952. // InterpolatedHeight
  953. //
  954. function TgxHeightDataSource.InterpolatedHeight(x, y: Single;
  955. tileSize: Integer): Single;
  956. var
  957. i: Integer;
  958. HD, foundHd: TgxHeightData;
  959. begin
  960. with FData.LockList do
  961. begin
  962. try
  963. // first, lookup data list to find if aHeightData contains our point
  964. foundHd := nil;
  965. for i := 0 to Count - 1 do
  966. begin
  967. HD := TgxHeightData(Items[i]);
  968. if (HD.XLeft <= x) and (HD.YTop <= y) and (HD.XLeft + HD.size - 1 > x)
  969. and (HD.YTop + HD.size - 1 > y) then
  970. begin
  971. foundHd := HD;
  972. Break;
  973. end;
  974. end;
  975. finally
  976. FData.UnlockList;
  977. end;
  978. end;
  979. if (foundHd = nil) or foundHd.Dirty then
  980. begin
  981. // not found, request one... slowest mode (should be avoided)
  982. if tileSize > 1 then
  983. foundHd := GetData(Round(x / (tileSize - 1) - 0.5) * (tileSize - 1),
  984. Round(y / (tileSize - 1) - 0.5) * (tileSize - 1), tileSize, hdtDefault)
  985. else
  986. begin
  987. Result := DefaultHeight;
  988. Exit;
  989. end;
  990. end
  991. else
  992. begin
  993. // request it using "standard" way (takes care of threads)
  994. foundHd := GetData(foundHd.XLeft, foundHd.YTop, foundHd.size,
  995. foundHd.DataType);
  996. end;
  997. if foundHd.DataState = hdsNone then
  998. Result := DefaultHeight
  999. else
  1000. Result := foundHd.InterpolatedHeight(x - foundHd.XLeft, y - foundHd.YTop);
  1001. end;
  1002. // ------------------
  1003. // ------------------ TgxHeightData ------------------
  1004. // ------------------
  1005. // Create
  1006. //
  1007. constructor TgxHeightData.Create(AOwner: TgxHeightDataSource;
  1008. aXLeft, aYTop, aSize: Integer; aDataType: TgxHeightDataType);
  1009. begin
  1010. inherited Create(AOwner);
  1011. SetLength(FUsers, 0);
  1012. FOwner := AOwner;
  1013. FXLeft := aXLeft;
  1014. FYTop := aYTop;
  1015. FSize := aSize;
  1016. FTextureCoordinatesMode := tcmWorld;
  1017. FTCScale := XYTexPoint;
  1018. FDataType := aDataType;
  1019. FDataState := hdsQueued;
  1020. FHeightMin := 1E30;
  1021. FHeightMax := 1E30;
  1022. OldVersion := nil;
  1023. NewVersion := nil;
  1024. DontUse := false;
  1025. end;
  1026. // Destroy
  1027. //
  1028. destructor TgxHeightData.Destroy;
  1029. begin
  1030. Assert(Length(FUsers) = 0,
  1031. 'You should *not* free a TgxHeightData, use "Release" instead');
  1032. Assert(not Assigned(FOwner),
  1033. 'You should *not* free a TgxHeightData, use "Release" instead');
  1034. if Assigned(FThread) then
  1035. begin
  1036. FThread.Terminate;
  1037. if FThread.Suspended then
  1038. FThread.Start;
  1039. FThread.WaitFor;
  1040. end;
  1041. if Assigned(FOnDestroy) then
  1042. FOnDestroy(self);
  1043. case DataType of
  1044. hdtByte:
  1045. begin
  1046. FreeMem(FByteData);
  1047. FreeMem(FByteRaster);
  1048. end;
  1049. hdtSmallInt:
  1050. begin
  1051. FreeMem(FSmallIntData);
  1052. FreeMem(FSmallIntRaster);
  1053. end;
  1054. hdtSingle:
  1055. begin
  1056. FreeMem(FSingleData);
  1057. FreeMem(FSingleRaster);
  1058. end;
  1059. hdtDefault:
  1060. ; // nothing
  1061. else
  1062. Assert(false);
  1063. end;
  1064. // ----------------------
  1065. self.LibMaterial := nil; // release a used material
  1066. // --Break any link with a new/old version of this tile--
  1067. if Assigned(self.OldVersion) then
  1068. begin
  1069. self.OldVersion.NewVersion := nil;
  1070. self.OldVersion := nil;
  1071. end;
  1072. if Assigned(self.NewVersion) then
  1073. begin
  1074. self.NewVersion.OldVersion := nil;
  1075. self.NewVersion := nil;
  1076. end;
  1077. // ------------------------------------------------------
  1078. // ----------------------
  1079. inherited Destroy;
  1080. end;
  1081. // RegisterUse
  1082. //
  1083. procedure TgxHeightData.RegisterUse;
  1084. begin
  1085. Inc(FUseCounter);
  1086. end;
  1087. // Release
  1088. //
  1089. procedure TgxHeightData.Release;
  1090. begin
  1091. if FUseCounter > 0 then
  1092. Dec(FUseCounter);
  1093. if FUseCounter = 0 then
  1094. begin
  1095. Owner.Release(self); // ???
  1096. end;
  1097. end;
  1098. // MarkDirty
  1099. //
  1100. // Release Dirty tiles, unless threading, and the tile is being used.
  1101. // In that case, start building a replacement tile instead.
  1102. procedure TgxHeightData.MarkDirty;
  1103. begin
  1104. with Owner.Data.LockList do
  1105. try
  1106. if (not Dirty) and (DataState <> hdsQueued) then
  1107. begin // dont mark queued tiles as dirty
  1108. FDirty := True;
  1109. if (Owner.MaxThreads > 0) and (FUseCounter > 0) then
  1110. Owner.PreloadReplacement(self)
  1111. else
  1112. begin
  1113. FUseCounter := 0;
  1114. Owner.Release(self);
  1115. end;
  1116. end;
  1117. finally
  1118. Owner.Data.UnlockList;
  1119. end;
  1120. end;
  1121. // Allocate
  1122. //
  1123. procedure TgxHeightData.Allocate(const Val: TgxHeightDataType);
  1124. begin
  1125. Assert(FDataSize = 0);
  1126. case Val of
  1127. hdtByte:
  1128. begin
  1129. FDataSize := size * size * SizeOf(Byte);
  1130. GetMem(FByteData, FDataSize);
  1131. BuildByteRaster;
  1132. end;
  1133. hdtSmallInt:
  1134. begin
  1135. FDataSize := size * size * SizeOf(SmallInt);
  1136. GetMem(FSmallIntData, FDataSize);
  1137. BuildSmallIntRaster;
  1138. end;
  1139. hdtSingle:
  1140. begin
  1141. FDataSize := size * size * SizeOf(Single);
  1142. GetMem(FSingleData, FDataSize);
  1143. BuildSingleRaster;
  1144. end;
  1145. else
  1146. Assert(false);
  1147. end;
  1148. FDataType := Val;
  1149. end;
  1150. // WARNING: SetMaterialName does NOT register the tile as a user of this texture.
  1151. // So, TgxLibMaterials.DeleteUnusedMaterials may see this material as unused, and delete it.
  1152. // This may lead to AV's the next time this tile is rendered.
  1153. // To be safe, rather assign the new TgxHeightData.LibMaterial property
  1154. procedure TgxHeightData.SetMaterialName(const MaterialName: string);
  1155. begin
  1156. SetLibMaterial(nil);
  1157. FMaterialName := MaterialName;
  1158. end;
  1159. procedure TgxHeightData.SetLibMaterial(LibMaterial: TgxLibMaterial);
  1160. begin
  1161. if Assigned(FLibMaterial) then
  1162. FLibMaterial.UnregisterUser(self); // detach from old texture
  1163. FLibMaterial := LibMaterial; // Attach new Material
  1164. if Assigned(LibMaterial) then
  1165. begin
  1166. LibMaterial.RegisterUser(self); // Mark new Material as 'used'
  1167. FMaterialName := LibMaterial.Name; // sync up MaterialName property
  1168. end
  1169. else
  1170. FMaterialName := '';
  1171. end;
  1172. // SetDataType
  1173. //
  1174. procedure TgxHeightData.SetDataType(const Val: TgxHeightDataType);
  1175. begin
  1176. if (Val <> FDataType) and (Val <> hdtDefault) then
  1177. begin
  1178. if DataState <> hdsNone then
  1179. begin
  1180. case FDataType of
  1181. hdtByte:
  1182. case Val of
  1183. hdtSmallInt:
  1184. ConvertByteToSmallInt;
  1185. hdtSingle:
  1186. ConvertByteToSingle;
  1187. else
  1188. Assert(false);
  1189. end;
  1190. hdtSmallInt:
  1191. case Val of
  1192. hdtByte:
  1193. ConvertSmallIntToByte;
  1194. hdtSingle:
  1195. ConvertSmallIntToSingle;
  1196. else
  1197. Assert(false);
  1198. end;
  1199. hdtSingle:
  1200. case Val of
  1201. hdtByte:
  1202. ConvertSingleToByte;
  1203. hdtSmallInt:
  1204. ConvertSingleToSmallInt;
  1205. else
  1206. Assert(false);
  1207. end;
  1208. hdtDefault:
  1209. ; // nothing, assume StartPreparingData knows what it's doing
  1210. else
  1211. Assert(false);
  1212. end;
  1213. end;
  1214. FDataType := Val;
  1215. end;
  1216. end;
  1217. // BuildByteRaster
  1218. //
  1219. procedure TgxHeightData.BuildByteRaster;
  1220. var
  1221. i: Integer;
  1222. begin
  1223. GetMem(FByteRaster, size * SizeOf(PByteArray));
  1224. for i := 0 to size - 1 do
  1225. FByteRaster^[i] := @FByteData[i * size]
  1226. end;
  1227. // BuildSmallIntRaster
  1228. //
  1229. procedure TgxHeightData.BuildSmallIntRaster;
  1230. var
  1231. i: Integer;
  1232. begin
  1233. GetMem(FSmallIntRaster, size * SizeOf(PSmallIntArray));
  1234. for i := 0 to size - 1 do
  1235. FSmallIntRaster^[i] := @FSmallIntData[i * size]
  1236. end;
  1237. // BuildSingleRaster
  1238. //
  1239. procedure TgxHeightData.BuildSingleRaster;
  1240. var
  1241. i: Integer;
  1242. begin
  1243. GetMem(FSingleRaster, size * SizeOf(PSingleArray));
  1244. for i := 0 to size - 1 do
  1245. FSingleRaster^[i] := @FSingleData[i * size]
  1246. end;
  1247. // ConvertByteToSmallInt
  1248. //
  1249. procedure TgxHeightData.ConvertByteToSmallInt;
  1250. var
  1251. i: Integer;
  1252. begin
  1253. FreeMem(FByteRaster);
  1254. FByteRaster := nil;
  1255. FDataSize := size * size * SizeOf(SmallInt);
  1256. GetMem(FSmallIntData, FDataSize);
  1257. for i := 0 to size * size - 1 do
  1258. FSmallIntData^[i] := (FByteData^[i] - 128) shl 7;
  1259. FreeMem(FByteData);
  1260. FByteData := nil;
  1261. BuildSmallIntRaster;
  1262. end;
  1263. // ConvertByteToSingle
  1264. //
  1265. procedure TgxHeightData.ConvertByteToSingle;
  1266. var
  1267. i: Integer;
  1268. begin
  1269. FreeMem(FByteRaster);
  1270. FByteRaster := nil;
  1271. FDataSize := size * size * SizeOf(Single);
  1272. GetMem(FSingleData, FDataSize);
  1273. for i := 0 to size * size - 1 do
  1274. FSingleData^[i] := (FByteData^[i] - 128) shl 7;
  1275. FreeMem(FByteData);
  1276. FByteData := nil;
  1277. BuildSingleRaster;
  1278. end;
  1279. // ConvertSmallIntToByte
  1280. //
  1281. procedure TgxHeightData.ConvertSmallIntToByte;
  1282. var
  1283. i: Integer;
  1284. begin
  1285. FreeMem(FSmallIntRaster);
  1286. FSmallIntRaster := nil;
  1287. FByteData := Pointer(FSmallIntData);
  1288. for i := 0 to size * size - 1 do
  1289. FByteData^[i] := (FSmallIntData^[i] div 128) + 128;
  1290. FDataSize := size * size * SizeOf(Byte);
  1291. ReallocMem(FByteData, FDataSize);
  1292. FSmallIntData := nil;
  1293. BuildByteRaster;
  1294. end;
  1295. // ConvertSmallIntToSingle
  1296. //
  1297. procedure TgxHeightData.ConvertSmallIntToSingle;
  1298. var
  1299. i: Integer;
  1300. begin
  1301. FreeMem(FSmallIntRaster);
  1302. FSmallIntRaster := nil;
  1303. FDataSize := size * size * SizeOf(Single);
  1304. GetMem(FSingleData, FDataSize);
  1305. for i := 0 to size * size - 1 do
  1306. FSingleData^[i] := FSmallIntData^[i];
  1307. FreeMem(FSmallIntData);
  1308. FSmallIntData := nil;
  1309. BuildSingleRaster;
  1310. end;
  1311. // ConvertSingleToByte
  1312. //
  1313. procedure TgxHeightData.ConvertSingleToByte;
  1314. var
  1315. i: Integer;
  1316. begin
  1317. FreeMem(FSingleRaster);
  1318. FSingleRaster := nil;
  1319. FByteData := Pointer(FSingleData);
  1320. for i := 0 to size * size - 1 do
  1321. FByteData^[i] := (Round(FSingleData^[i]) div 128) + 128;
  1322. FDataSize := size * size * SizeOf(Byte);
  1323. ReallocMem(FByteData, FDataSize);
  1324. FSingleData := nil;
  1325. BuildByteRaster;
  1326. end;
  1327. // ConvertSingleToSmallInt
  1328. //
  1329. procedure TgxHeightData.ConvertSingleToSmallInt;
  1330. var
  1331. i: Integer;
  1332. begin
  1333. FreeMem(FSingleRaster);
  1334. FSingleRaster := nil;
  1335. FSmallIntData := Pointer(FSingleData);
  1336. for i := 0 to size * size - 1 do
  1337. FSmallIntData^[i] := Round(FSingleData^[i]);
  1338. FDataSize := size * size * SizeOf(SmallInt);
  1339. ReallocMem(FSmallIntData, FDataSize);
  1340. FSingleData := nil;
  1341. BuildSmallIntRaster;
  1342. end;
  1343. // ByteHeight
  1344. //
  1345. function TgxHeightData.ByteHeight(x, y: Integer): Byte;
  1346. begin
  1347. Assert((Cardinal(x) < Cardinal(size)) and (Cardinal(y) < Cardinal(size)));
  1348. Result := ByteRaster^[y]^[x];
  1349. end;
  1350. // SmallIntHeight
  1351. //
  1352. function TgxHeightData.SmallIntHeight(x, y: Integer): SmallInt;
  1353. begin
  1354. Assert((Cardinal(x) < Cardinal(size)) and (Cardinal(y) < Cardinal(size)));
  1355. Result := SmallIntRaster^[y]^[x];
  1356. end;
  1357. // SingleHeight
  1358. //
  1359. function TgxHeightData.SingleHeight(x, y: Integer): Single;
  1360. begin
  1361. Assert((Cardinal(x) < Cardinal(size)) and (Cardinal(y) < Cardinal(size)));
  1362. Result := SingleRaster^[y]^[x];
  1363. end;
  1364. // InterpolatedHeight
  1365. //
  1366. function TgxHeightData.InterpolatedHeight(x, y: Single): Single;
  1367. var
  1368. ix, iy, ixn, iyn: Integer;
  1369. h1, h2, h3: Single;
  1370. begin
  1371. if FDataState = hdsNone then
  1372. Result := 0
  1373. else
  1374. begin
  1375. ix := Trunc(x);
  1376. x := Frac(x);
  1377. iy := Trunc(y);
  1378. y := Frac(y);
  1379. ixn := ix + 1;
  1380. if ixn >= size then
  1381. ixn := ix;
  1382. iyn := iy + 1;
  1383. if iyn >= size then
  1384. iyn := iy;
  1385. if x > y then
  1386. begin
  1387. // top-right triangle
  1388. h1 := Height(ixn, iy);
  1389. h2 := Height(ix, iy);
  1390. h3 := Height(ixn, iyn);
  1391. Result := h1 + (h2 - h1) * (1 - x) + (h3 - h1) * y;
  1392. end
  1393. else
  1394. begin
  1395. // bottom-left triangle
  1396. h1 := Height(ix, iyn);
  1397. h2 := Height(ixn, iyn);
  1398. h3 := Height(ix, iy);
  1399. Result := h1 + (h2 - h1) * (x) + (h3 - h1) * (1 - y);
  1400. end;
  1401. end;
  1402. end;
  1403. // Height
  1404. //
  1405. function TgxHeightData.Height(x, y: Integer): Single;
  1406. begin
  1407. case DataType of
  1408. hdtByte:
  1409. Result := (ByteHeight(x, y) - 128) shl 7;
  1410. hdtSmallInt:
  1411. Result := SmallIntHeight(x, y);
  1412. hdtSingle:
  1413. Result := SingleHeight(x, y);
  1414. else
  1415. Result := 0;
  1416. Assert(false);
  1417. end;
  1418. end;
  1419. // GetHeightMin
  1420. //
  1421. function TgxHeightData.GetHeightMin: Single;
  1422. var
  1423. i: Integer;
  1424. b: Byte;
  1425. sm: SmallInt;
  1426. si: Single;
  1427. begin
  1428. if FHeightMin = 1E30 then
  1429. begin
  1430. if DataState = hdsReady then
  1431. begin
  1432. case DataType of
  1433. hdtByte:
  1434. begin
  1435. b := FByteData^[0];
  1436. for i := 1 to size * size - 1 do
  1437. if FByteData^[i] < b then
  1438. b := FByteData^[i];
  1439. FHeightMin := ((Integer(b) - 128) shl 7);
  1440. end;
  1441. hdtSmallInt:
  1442. begin
  1443. sm := FSmallIntData^[0];
  1444. for i := 1 to size * size - 1 do
  1445. if FSmallIntData^[i] < sm then
  1446. sm := FSmallIntData^[i];
  1447. FHeightMin := sm;
  1448. end;
  1449. hdtSingle:
  1450. begin
  1451. si := FSingleData^[0];
  1452. for i := 1 to size * size - 1 do
  1453. if FSingleData^[i] < si then
  1454. si := FSingleData^[i];
  1455. FHeightMin := si;
  1456. end;
  1457. else
  1458. FHeightMin := 0;
  1459. end;
  1460. end
  1461. else
  1462. FHeightMin := 0;
  1463. end;
  1464. Result := FHeightMin;
  1465. end;
  1466. // GetHeightMax
  1467. //
  1468. function TgxHeightData.GetHeightMax: Single;
  1469. var
  1470. i: Integer;
  1471. b: Byte;
  1472. sm: SmallInt;
  1473. si: Single;
  1474. begin
  1475. if FHeightMax = 1E30 then
  1476. begin
  1477. if DataState = hdsReady then
  1478. begin
  1479. case DataType of
  1480. hdtByte:
  1481. begin
  1482. b := FByteData^[0];
  1483. for i := 1 to size * size - 1 do
  1484. if FByteData^[i] > b then
  1485. b := FByteData^[i];
  1486. FHeightMax := ((Integer(b) - 128) shl 7);
  1487. end;
  1488. hdtSmallInt:
  1489. begin
  1490. sm := FSmallIntData^[0];
  1491. for i := 1 to size * size - 1 do
  1492. if FSmallIntData^[i] > sm then
  1493. sm := FSmallIntData^[i];
  1494. FHeightMax := sm;
  1495. end;
  1496. hdtSingle:
  1497. begin
  1498. si := FSingleData^[0];
  1499. for i := 1 to size * size - 1 do
  1500. if FSingleData^[i] > si then
  1501. si := FSingleData^[i];
  1502. FHeightMax := si;
  1503. end;
  1504. else
  1505. FHeightMax := 0;
  1506. end;
  1507. end
  1508. else
  1509. FHeightMax := 0;
  1510. end;
  1511. Result := FHeightMax;
  1512. end;
  1513. // Normal
  1514. //
  1515. // Calculates the normal at a vertex
  1516. function TgxHeightData.Normal(x, y: Integer; const scale: TAffineVector)
  1517. : TAffineVector;
  1518. var
  1519. dx, dy: Single;
  1520. begin
  1521. if x > 0 then
  1522. if x < size - 1 then
  1523. dx := (Height(x + 1, y) - Height(x - 1, y))
  1524. else
  1525. dx := (Height(x, y) - Height(x - 1, y))
  1526. else
  1527. dx := (Height(x + 1, y) - Height(x, y));
  1528. if y > 0 then
  1529. if y < size - 1 then
  1530. dy := (Height(x, y + 1) - Height(x, y - 1))
  1531. else
  1532. dy := (Height(x, y) - Height(x, y - 1))
  1533. else
  1534. dy := (Height(x, y + 1) - Height(x, y));
  1535. Result.X := dx * scale.Y * scale.Z;
  1536. Result.Y := dy * scale.X * scale.Z;
  1537. Result.Z := scale.X * scale.Y;
  1538. NormalizeVector(Result);
  1539. end;
  1540. // Calculates the normal at a surface cell (Between vertexes)
  1541. function TgxHeightData.NormalAtNode(x, y: Integer; const scale: TAffineVector)
  1542. : TAffineVector;
  1543. var
  1544. dx, dy, Hxy: Single;
  1545. begin
  1546. MinInteger(MaxInteger(x, 0), size - 2); // clamp x to 0 -> Size-2
  1547. MinInteger(MaxInteger(y, 0), size - 2); // clamp x to 0 -> Size-2
  1548. Hxy := Height(x, y);
  1549. dx := Height(x + 1, y) - Hxy;
  1550. dy := Height(x, y + 1) - Hxy;
  1551. Result.X := dx * scale.Y * scale.Z; // Result[0]:=dx/scale[0];
  1552. Result.Y := dy * scale.X * scale.Z; // Result[1]:=dy/scale[1];
  1553. Result.Z := 1 * scale.X * scale.Y; // Result[2]:=1 /scale[2];
  1554. NormalizeVector(Result);
  1555. end;
  1556. function TgxHeightData.OverlapsArea(const Area: TRect): boolean;
  1557. begin
  1558. Result := (XLeft <= Area.Right) and (YTop <= Area.Bottom) and
  1559. (XLeft + size > Area.Left) and (YTop + size > Area.Top);
  1560. end;
  1561. // ------------------
  1562. // ------------------ TgxHeightDataThread ------------------
  1563. // ------------------
  1564. destructor TgxHeightDataThread.Destroy;
  1565. begin
  1566. if Assigned(FHeightData) then
  1567. FHeightData.FThread := nil;
  1568. inherited;
  1569. end;
  1570. // ------------------
  1571. // ------------------ TgxBitmapHDS ------------------
  1572. // ------------------
  1573. constructor TgxBitmapHDS.Create(AOwner: TComponent);
  1574. begin
  1575. inherited Create(AOwner);
  1576. FPicture := TImage.Create(AOwner);
  1577. FPicture.OnDblClick := OnPictureChanged;
  1578. FInfiniteWrap := True;
  1579. FInverted := True;
  1580. end;
  1581. destructor TgxBitmapHDS.Destroy;
  1582. begin
  1583. inherited Destroy;
  1584. FreeMonochromeBitmap;
  1585. FPicture.Free;
  1586. end;
  1587. procedure TgxBitmapHDS.SetPicture(const Val: TImage);
  1588. begin
  1589. FPicture.Assign(Val);
  1590. end;
  1591. procedure TgxBitmapHDS.OnPictureChanged(sender: TObject);
  1592. var
  1593. oldPoolSize, size: Integer;
  1594. begin
  1595. // cleanup pool
  1596. oldPoolSize := MaxPoolSize;
  1597. MaxPoolSize := 0;
  1598. CleanUp;
  1599. MaxPoolSize := oldPoolSize;
  1600. // prepare MonoChromeBitmap
  1601. FreeMonochromeBitmap;
  1602. size := Round(Picture.Width);
  1603. if size > 0 then
  1604. CreateMonochromeBitmap(size);
  1605. end;
  1606. procedure TgxBitmapHDS.SetInfiniteWrap(Val: boolean);
  1607. begin
  1608. if FInfiniteWrap <> Val then
  1609. begin
  1610. FInfiniteWrap := Val;
  1611. MarkDirty;
  1612. end;
  1613. end;
  1614. procedure TgxBitmapHDS.SetInverted(Val: boolean);
  1615. begin
  1616. if FInverted = Val then
  1617. Exit;
  1618. FInverted := Val;
  1619. MarkDirty;
  1620. end;
  1621. procedure TgxBitmapHDS.MarkDirty(const Area: TRect);
  1622. begin
  1623. inherited;
  1624. FreeMonochromeBitmap;
  1625. if Picture.Width > 0 then
  1626. CreateMonochromeBitmap(Round(Picture.Width));
  1627. end;
  1628. procedure TgxBitmapHDS.CreateMonochromeBitmap(size: Integer);
  1629. type
  1630. TPaletteEntryArray = array [0 .. 255] of TPaletteEntry;
  1631. PPaletteEntryArray = ^TPaletteEntryArray;
  1632. TLogPal = record
  1633. lpal: TLogPalette;
  1634. pe: TPaletteEntryArray;
  1635. end;
  1636. var
  1637. x: Integer;
  1638. logpal: TLogPal;
  1639. hPal: HPalette;
  1640. begin
  1641. size := RoundUpToPowerOf2(size);
  1642. FBitmap := TBitmap.Create;
  1643. { TODO -oPW : E2129 Cannot assign to a read-only property }
  1644. (*FBitmap.PixelFormat := glpf8bit;*)
  1645. FBitmap.Width := size;
  1646. FBitmap.Height := size;
  1647. for x := 0 to 255 do
  1648. with PPaletteEntryArray(@logpal.lpal.palPalEntry[0])[x] do
  1649. begin
  1650. peRed := x;
  1651. peGreen := x;
  1652. peBlue := x;
  1653. peFlags := 0;
  1654. end;
  1655. with logpal.lpal do
  1656. begin
  1657. palVersion := $300;
  1658. palNumEntries := 256;
  1659. end;
  1660. hPal := CreatePalette(logpal.lpal);
  1661. Assert(hPal <> 0);
  1662. { TODO -oPW : E2003 Undeclared identifier: 'Palette' }
  1663. (*FBitmap.Palette := hPal;*)
  1664. // some picture formats trigger a "change" when drawed
  1665. Picture.Bitmap.OnChange := nil;
  1666. try
  1667. { TODO -oPW : E2003 Undeclared identifier: 'StretchDraw' }
  1668. (*FBitmap.StretchDraw(Rect(0, 0, size, size), Picture.Bitmap);*)
  1669. finally
  1670. Picture.Bitmap.OnChange := OnPictureChanged;
  1671. end;
  1672. SetLength(FScanLineCache, 0); // clear the cache
  1673. SetLength(FScanLineCache, size);
  1674. end;
  1675. procedure TgxBitmapHDS.FreeMonochromeBitmap;
  1676. begin
  1677. SetLength(FScanLineCache, 0);
  1678. FBitmap.Free;
  1679. FBitmap := nil;
  1680. end;
  1681. function TgxBitmapHDS.GetScanLine(y: Integer): PByteArray;
  1682. begin
  1683. Result := FScanLineCache[y];
  1684. if not Assigned(Result) then
  1685. begin
  1686. Result := BitmapScanLine(FBitmap, y); // FBitmap.ScanLine[y];
  1687. FScanLineCache[y] := Result;
  1688. end;
  1689. end;
  1690. procedure TgxBitmapHDS.StartPreparingData(HeightData: TgxHeightData);
  1691. var
  1692. y, x: Integer;
  1693. bmpSize, wrapMask: Integer;
  1694. bitmapLine, rasterLine: PByteArray;
  1695. oldType: TgxHeightDataType;
  1696. b: Byte;
  1697. YPos: Integer;
  1698. begin
  1699. if FBitmap = nil then
  1700. Exit;
  1701. HeightData.FDataState := hdsPreparing;
  1702. bmpSize := FBitmap.Width;
  1703. wrapMask := bmpSize - 1;
  1704. // retrieve data
  1705. with HeightData do
  1706. begin
  1707. if (not InfiniteWrap) and ((XLeft >= bmpSize) or (XLeft < 0) or
  1708. (YTop >= bmpSize) or (YTop < 0)) then
  1709. begin
  1710. HeightData.FDataState := hdsNone;
  1711. Exit;
  1712. end;
  1713. oldType := DataType;
  1714. Allocate(hdtByte);
  1715. if Inverted then
  1716. YPos := YTop
  1717. else
  1718. YPos := 1 - size - YTop;
  1719. for y := 0 to size - 1 do
  1720. begin
  1721. bitmapLine := GetScanLine((y + YPos) and wrapMask);
  1722. if Inverted then
  1723. rasterLine := ByteRaster^[y]
  1724. else
  1725. rasterLine := ByteRaster^[size - 1 - y];
  1726. // *BIG CAUTION HERE* : Don't remove the intermediate variable here!!!
  1727. // or Delphi compiler will "optimize" to 32 bits access with clamping
  1728. // Resulting in possible reads of stuff beyon bitmapLine length!!!!
  1729. for x := XLeft to XLeft + size - 1 do
  1730. begin
  1731. b := bitmapLine^[x and wrapMask];
  1732. rasterLine^[x - XLeft] := b;
  1733. end;
  1734. end;
  1735. if (oldType <> hdtByte) and (oldType <> hdtDefault) then
  1736. DataType := oldType;
  1737. end;
  1738. TextureCoordinates(HeightData);
  1739. inherited;
  1740. end;
  1741. function TgxBitmapHDS.Width: Integer;
  1742. begin
  1743. if Assigned(self.FBitmap) then
  1744. Result := self.FBitmap.Width
  1745. else
  1746. Result := 0;
  1747. end;
  1748. function TgxBitmapHDS.Height: Integer;
  1749. begin
  1750. if Assigned(self.FBitmap) then
  1751. Result := self.FBitmap.Height
  1752. else
  1753. Result := 0;
  1754. end;
  1755. // ------------------
  1756. // ------------------ TgxCustomHDS ------------------
  1757. // ------------------
  1758. constructor TgxCustomHDS.Create(AOwner: TComponent);
  1759. begin
  1760. inherited Create(AOwner);
  1761. end;
  1762. destructor TgxCustomHDS.Destroy;
  1763. begin
  1764. inherited Destroy;
  1765. end;
  1766. procedure TgxCustomHDS.MarkDirty(const Area: TRect);
  1767. begin
  1768. inherited;
  1769. if Assigned(FOnMarkDirty) then
  1770. FOnMarkDirty(Area);
  1771. end;
  1772. procedure TgxCustomHDS.StartPreparingData(HeightData: TgxHeightData);
  1773. begin
  1774. if Assigned(FOnStartPreparingData) then
  1775. FOnStartPreparingData(HeightData);
  1776. if HeightData.DataState <> hdsNone then
  1777. HeightData.DataState := hdsReady;
  1778. end;
  1779. // ------------------
  1780. // ------------------ TgxTerrainBaseHDS ------------------
  1781. // ------------------
  1782. constructor TgxTerrainBaseHDS.Create(AOwner: TComponent);
  1783. begin
  1784. inherited Create(AOwner);
  1785. end;
  1786. destructor TgxTerrainBaseHDS.Destroy;
  1787. begin
  1788. inherited Destroy;
  1789. end;
  1790. procedure TgxTerrainBaseHDS.StartPreparingData(HeightData: TgxHeightData);
  1791. const
  1792. cTBWidth: Integer = 4320;
  1793. cTBHeight: Integer = 2160;
  1794. var
  1795. y, x, offset: Integer;
  1796. rasterLine: PSmallIntArray;
  1797. oldType: TgxHeightDataType;
  1798. b: SmallInt;
  1799. fs: TStream;
  1800. begin
  1801. if not FileExists('tbase.bin') then
  1802. Exit;
  1803. fs := TFileStream.Create('tbase.bin', fmOpenRead + fmShareDenyNone);
  1804. try
  1805. // retrieve data
  1806. with HeightData do
  1807. begin
  1808. oldType := DataType;
  1809. Allocate(hdtSmallInt);
  1810. for y := YTop to YTop + size - 1 do
  1811. begin
  1812. offset := (y mod cTBHeight) * (cTBWidth * 2);
  1813. rasterLine := SmallIntRaster^[y - YTop];
  1814. for x := XLeft to XLeft + size - 1 do
  1815. begin
  1816. fs.Seek(offset + (x mod cTBWidth) * 2, soFromBeginning);
  1817. fs.Read(b, 2);
  1818. if b < 0 then
  1819. b := 0;
  1820. rasterLine^[x - XLeft] := SmallInt(b);
  1821. end;
  1822. end;
  1823. if oldType <> hdtSmallInt then
  1824. DataType := oldType;
  1825. end;
  1826. inherited;
  1827. finally
  1828. fs.Free;
  1829. end;
  1830. end;
  1831. // ------------------
  1832. // ------------------ TgxHeightDataSourceFilter ------------------
  1833. // ------------------
  1834. constructor TgxHeightDataSourceFilter.Create(AOwner: TComponent);
  1835. begin
  1836. inherited Create(AOwner);
  1837. FActive := True;
  1838. end;
  1839. destructor TgxHeightDataSourceFilter.Destroy;
  1840. begin
  1841. HeightDataSource := nil;
  1842. inherited Destroy;
  1843. end;
  1844. procedure TgxHeightDataSourceFilter.Release(aHeightData: TgxHeightData);
  1845. begin
  1846. if Assigned(HeightDataSource) then
  1847. HeightDataSource.Release(aHeightData);
  1848. end;
  1849. procedure TgxHeightDataSourceFilter.Notification(AComponent: TComponent;
  1850. Operation: TOperation);
  1851. begin
  1852. if Operation = opRemove then
  1853. begin
  1854. if AComponent = FHDS then
  1855. HeightDataSource := nil
  1856. end;
  1857. inherited;
  1858. end;
  1859. procedure TgxHeightDataSourceFilter.SetHDS(Val: TgxHeightDataSource);
  1860. begin
  1861. if Val = self then
  1862. Val := nil; // prevent self-referencing
  1863. if Val <> FHDS then
  1864. begin
  1865. if Assigned(FHDS) then
  1866. FHDS.RemoveFreeNotification(self);
  1867. FHDS := Val;
  1868. if Assigned(FHDS) then
  1869. FHDS.FreeNotification(self);
  1870. // MarkDirty;
  1871. self.Clear; // when removing the HDS, also remove all tiles from the cache
  1872. end;
  1873. end;
  1874. function TgxHeightDataSourceFilter.Width: Integer;
  1875. begin
  1876. if Assigned(FHDS) then
  1877. Result := FHDS.Width
  1878. else
  1879. Result := 0;
  1880. end;
  1881. function TgxHeightDataSourceFilter.Height: Integer;
  1882. begin
  1883. if Assigned(FHDS) then
  1884. Result := FHDS.Height
  1885. else
  1886. Result := 0;
  1887. end;
  1888. procedure TgxHeightDataSourceFilter.StartPreparingData(HeightData: TgxHeightData);
  1889. begin
  1890. // ---if there is no linked HDS then return an empty tile--
  1891. if not Assigned(FHDS) then
  1892. begin
  1893. HeightData.Owner.Data.LockList;
  1894. HeightData.DataState := hdsNone;
  1895. HeightData.Owner.Data.UnlockList;
  1896. Exit;
  1897. end;
  1898. // ---Use linked HeightDataSource to prepare height data--
  1899. if HeightData.DataState = hdsQueued then
  1900. begin
  1901. HeightData.Owner.Data.LockList;
  1902. HeightData.DataState := hdsPreparing;
  1903. HeightData.Owner.Data.UnlockList;
  1904. end;
  1905. FHDS.StartPreparingData(HeightData);
  1906. if Assigned(FOnSourceDataFetched) then
  1907. FOnSourceDataFetched(self, HeightData);
  1908. if HeightData.DataState = hdsNone then
  1909. Exit;
  1910. if FActive then
  1911. PreparingData(HeightData);
  1912. inherited; // HeightData.DataState:=hdsReady;
  1913. end;
  1914. // ------------------------------------------------------------------
  1915. initialization
  1916. // ------------------------------------------------------------------
  1917. RegisterClasses([TgxBitmapHDS, TgxCustomHDS, TgxHeightDataSourceFilter]);
  1918. end.