GLS.SkyDome.pas 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.SkyDome;
  5. (* Skydome object *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. System.SysUtils,
  12. System.UITypes,
  13. System.Math,
  14. Vcl.Graphics,
  15. GLS.OpenGLTokens,
  16. GLS.Scene,
  17. GLS.VectorGeometry,
  18. GLS.Graphics,
  19. GLS.VectorTypes,
  20. GLS.Color,
  21. GLS.Material,
  22. GLS.RenderContextInfo;
  23. type
  24. TGLStarRecord = packed record
  25. RA : Word; // x100 builtin factor, degrees
  26. DEC : SmallInt; // x100 builtin factor, degrees
  27. BVColorIndex : Byte; // x100 builtin factor
  28. VMagnitude : Byte; // x10 builtin factor
  29. end;
  30. PGLStarRecord = ^TGLStarRecord;
  31. // ------------------------- SkyBox class -------------------------
  32. TGLSkyBoxStyle = (sbsFull, sbsTopHalf, sbsBottomHalf, sbTopTwoThirds,
  33. sbsTopHalfClamped);
  34. TGLSkyBox = class(TGLCameraInvariantObject, IGLMaterialLibrarySupported)
  35. private
  36. FMatNameTop: string;
  37. FMatNameRight: string;
  38. FMatNameFront: string;
  39. FMatNameLeft: string;
  40. FMatNameBack: string;
  41. FMatNameBottom: string;
  42. FMatNameClouds: string;
  43. FMaterialLibrary: TGLMaterialLibrary;
  44. FCloudsPlaneOffset: Single;
  45. FCloudsPlaneSize: Single;
  46. FStyle: TGLSkyBoxStyle;
  47. //implementing IGLMaterialLibrarySupported
  48. function GetMaterialLibrary: TGLAbstractMaterialLibrary;
  49. protected
  50. procedure SetMaterialLibrary(const Value: TGLMaterialLibrary);
  51. procedure SetMatNameBack(const Value: string);
  52. procedure SetMatNameBottom(const Value: string);
  53. procedure SetMatNameFront(const Value: string);
  54. procedure SetMatNameLeft(const Value: string);
  55. procedure SetMatNameRight(const Value: string);
  56. procedure SetMatNameTop(const Value: string);
  57. procedure SetMatNameClouds(const Value: string);
  58. procedure SetCloudsPlaneOffset(const Value: single);
  59. procedure SetCloudsPlaneSize(const Value: single);
  60. procedure SetStyle(const value: TGLSkyBoxStyle);
  61. public
  62. constructor Create(AOwner: TComponent); override;
  63. destructor Destroy; override;
  64. procedure DoRender(var ARci: TGLRenderContextInfo;
  65. ARenderSelf, ARenderChildren: Boolean); override;
  66. procedure BuildList(var ARci: TGLRenderContextInfo); override;
  67. procedure Notification(AComponent: TComponent; Operation: TOperation);
  68. override;
  69. published
  70. property MaterialLibrary: TGLMaterialLibrary read FMaterialLibrary write
  71. SetMaterialLibrary;
  72. property MatNameTop: TGLLibMaterialName read FMatNameTop write
  73. SetMatNameTop;
  74. property MatNameBottom: TGLLibMaterialName read FMatNameBottom write
  75. SetMatNameBottom;
  76. property MatNameLeft: TGLLibMaterialName read FMatNameLeft write
  77. SetMatNameLeft;
  78. property MatNameRight: TGLLibMaterialName read FMatNameRight write
  79. SetMatNameRight;
  80. property MatNameFront: TGLLibMaterialName read FMatNameFront write
  81. SetMatNameFront;
  82. property MatNameBack: TGLLibMaterialName read FMatNameBack write
  83. SetMatNameBack;
  84. property MatNameClouds: TGLLibMaterialName read FMatNameClouds write
  85. SetMatNameClouds;
  86. property CloudsPlaneOffset: Single read FCloudsPlaneOffset write
  87. SetCloudsPlaneOffset;
  88. property CloudsPlaneSize: Single read FCloudsPlaneSize write
  89. SetCloudsPlaneSize;
  90. property Style: TGLSkyBoxStyle read FStyle write FStyle default sbsFull;
  91. end;
  92. //--------------------- SkyDome classes -----------------------------
  93. TGLSkyDomeBand = class(TCollectionItem)
  94. private
  95. FStartAngle: Single;
  96. FStopAngle: Single;
  97. FStartColor: TGLColor;
  98. FStopColor: TGLColor;
  99. FSlices: Integer;
  100. FStacks: Integer;
  101. protected
  102. function GetDisplayName: string; override;
  103. procedure SetStartAngle(const val: Single);
  104. procedure SetStartColor(const val: TGLColor);
  105. procedure SetStopAngle(const val: Single);
  106. procedure SetStopColor(const val: TGLColor);
  107. procedure SetSlices(const val: Integer);
  108. procedure SetStacks(const val: Integer);
  109. procedure OnColorChange(sender: TObject);
  110. public
  111. constructor Create(Collection: TCollection); override;
  112. destructor Destroy; override;
  113. procedure Assign(Source: TPersistent); override;
  114. procedure BuildList(var rci: TGLRenderContextInfo);
  115. published
  116. property StartAngle: Single read FStartAngle write SetStartAngle;
  117. property StartColor: TGLColor read FStartColor write SetStartColor;
  118. property StopAngle: Single read FStopAngle write SetStopAngle;
  119. property StopColor: TGLColor read FStopColor write SetStopColor;
  120. property Slices: Integer read FSlices write SetSlices default 12;
  121. property Stacks: Integer read FStacks write SetStacks default 1;
  122. end;
  123. TGLSkyDomeBands = class(TCollection)
  124. protected
  125. owner: TComponent;
  126. function GetOwner: TPersistent; override;
  127. procedure SetItems(index: Integer; const val: TGLSkyDomeBand);
  128. function GetItems(index: Integer): TGLSkyDomeBand;
  129. public
  130. constructor Create(AOwner: TComponent);
  131. function Add: TGLSkyDomeBand;
  132. function FindItemID(ID: Integer): TGLSkyDomeBand;
  133. property Items[index: Integer]: TGLSkyDomeBand read GetItems write SetItems;
  134. default;
  135. procedure NotifyChange;
  136. procedure BuildList(var rci: TGLRenderContextInfo);
  137. end;
  138. TGLSkyDomeStar = class(TCollectionItem)
  139. private
  140. FRA, FDec: Single;
  141. FMagnitude: Single;
  142. FColor: TColor;
  143. FCacheCoord: TAffineVector; // cached cartesian coordinates
  144. protected
  145. function GetDisplayName: string; override;
  146. public
  147. constructor Create(Collection: TCollection); override;
  148. destructor Destroy; override;
  149. procedure Assign(Source: TPersistent); override;
  150. published
  151. // Right Ascension, in degrees.
  152. property RA: Single read FRA write FRA;
  153. // Declination, in degrees.
  154. property Dec: Single read FDec write FDec;
  155. // Absolute magnitude.
  156. property Magnitude: Single read FMagnitude write FMagnitude;
  157. // Color of the star.
  158. property Color: TColor read FColor write FColor;
  159. end;
  160. TGLSkyDomeStars = class(TCollection)
  161. protected
  162. owner: TComponent;
  163. function GetOwner: TPersistent; override;
  164. procedure SetItems(index: Integer; const val: TGLSkyDomeStar);
  165. function GetItems(index: Integer): TGLSkyDomeStar;
  166. procedure PrecomputeCartesianCoordinates;
  167. public
  168. constructor Create(AOwner: TComponent);
  169. function Add: TGLSkyDomeStar;
  170. function FindItemID(ID: Integer): TGLSkyDomeStar;
  171. property Items[index: Integer]: TGLSkyDomeStar read GetItems write SetItems;
  172. default;
  173. procedure BuildList(var rci: TGLRenderContextInfo; twinkle: Boolean);
  174. (* Adds nb random stars of the given color.
  175. Stars are homogenously scattered on the complete sphere, not only the band defined or visible dome. *)
  176. procedure AddRandomStars(const nb: Integer; const color: TColor; const limitToTopDome: Boolean = False); overload;
  177. procedure AddRandomStars(const nb: Integer; const ColorMin, ColorMax:TVector3b;
  178. const Magnitude_min, Magnitude_max: Single;const limitToTopDome: Boolean = False); overload;
  179. (* Load a 'stars' file, which is made of TGLStarRecord.
  180. Not that '.stars' files should already be sorted by magnitude and color. *)
  181. procedure LoadStarsFile(const starsFileName: string);
  182. end;
  183. TGLSkyDomeOption = (sdoTwinkle);
  184. TGLSkyDomeOptions = set of TGLSkyDomeOption;
  185. (* Renders a sky dome always centered on the camera.
  186. If you use this object make sure it is rendered *first*, as it ignores
  187. depth buffering and overwrites everything. All children of a skydome
  188. are rendered in the skydome's coordinate system.
  189. The skydome is described by "bands", each "band" is an horizontal cut
  190. of a sphere, and you can have as many bands as you wish *)
  191. TGLSkyDome = class(TGLCameraInvariantObject)
  192. private
  193. FOptions: TGLSkyDomeOptions;
  194. FBands: TGLSkyDomeBands;
  195. FStars: TGLSkyDomeStars;
  196. protected
  197. procedure SetBands(const val: TGLSkyDomeBands);
  198. procedure SetStars(const val: TGLSkyDomeStars);
  199. procedure SetOptions(const val: TGLSkyDomeOptions);
  200. public
  201. constructor Create(AOwner: TComponent); override;
  202. destructor Destroy; override;
  203. procedure Assign(Source: TPersistent); override;
  204. procedure BuildList(var rci: TGLRenderContextInfo); override;
  205. published
  206. property Bands: TGLSkyDomeBands read FBands write SetBands;
  207. property Stars: TGLSkyDomeStars read FStars write SetStars;
  208. property Options: TGLSkyDomeOptions read FOptions write SetOptions default [];
  209. end;
  210. TEarthSkydomeOption = (esoFadeStarsWithSun, esoRotateOnTwelveHours, esoDepthTest);
  211. TEarthSkydomeOptions = set of TEarthSkydomeOption;
  212. (* Render a skydome like what can be seen on earth.
  213. Color is based on sun position and turbidity, to "mimic" atmospheric
  214. Rayleigh and Mie scatterings. The colors can be adjusted to render
  215. weird/exoplanet atmospheres too.
  216. The default slices/stacks values make for an average quality rendering,
  217. for a very clean rendering, use 64/64 (more is overkill in most cases).
  218. The complexity is quite high though, making a T&L 3D board a necessity
  219. for using TGLEarthSkyDome. *)
  220. TGLEarthSkyDome = class(TGLSkyDome)
  221. private
  222. FSunElevation: Single;
  223. FTurbidity: Single;
  224. FCurSunColor, FCurSkyColor, FCurHazeColor: TGLColorVector;
  225. FCurHazeTurbid, FCurSunSkyTurbid: Single;
  226. FSunZenithColor: TGLColor;
  227. FSunDawnColor: TGLColor;
  228. FHazeColor: TGLColor;
  229. FSkyColor: TGLColor;
  230. FNightColor: TGLColor;
  231. FDeepColor: TGLColor;
  232. FSlices, FStacks: Integer;
  233. FExtendedOptions: TEarthSkydomeOptions;
  234. FMorning: boolean;
  235. protected
  236. procedure Loaded; override;
  237. procedure SetSunElevation(const val: Single);
  238. procedure SetTurbidity(const val: Single);
  239. procedure SetSunZenithColor(const val: TGLColor);
  240. procedure SetSunDawnColor(const val: TGLColor);
  241. procedure SetHazeColor(const val: TGLColor);
  242. procedure SetSkyColor(const val: TGLColor);
  243. procedure SetNightColor(const val: TGLColor);
  244. procedure SetDeepColor(const val: TGLColor);
  245. procedure SetSlices(const val: Integer);
  246. procedure SetStacks(const val: Integer);
  247. procedure OnColorChanged(Sender: TObject);
  248. procedure PreCalculate;
  249. procedure RenderDome;
  250. function CalculateColor(const theta, cosGamma: Single): TGLColorVector;
  251. public
  252. constructor Create(AOwner: TComponent); override;
  253. destructor Destroy; override;
  254. procedure Assign(Source: TPersistent); override;
  255. procedure BuildList(var rci: TGLRenderContextInfo); override;
  256. procedure SetSunAtTime(HH, MM: Single);
  257. published
  258. // Elevation of the sun, measured in degrees
  259. property SunElevation: Single read FSunElevation write SetSunElevation;
  260. // Expresses the purity of air. Value range is from 1 (pure athmosphere) to 120 (very nebulous)
  261. property Turbidity: Single read FTurbidity write SetTurbidity;
  262. property SunZenithColor: TGLColor read FSunZenithColor write SetSunZenithColor;
  263. property SunDawnColor: TGLColor read FSunDawnColor write SetSunDawnColor;
  264. property HazeColor: TGLColor read FHazeColor write SetHazeColor;
  265. property SkyColor: TGLColor read FSkyColor write SetSkyColor;
  266. property NightColor: TGLColor read FNightColor write SetNightColor;
  267. property DeepColor: TGLColor read FDeepColor write SetDeepColor;
  268. property ExtendedOptions: TEarthSkydomeOptions read FExtendedOptions write FExtendedOptions;
  269. property Slices: Integer read FSlices write SetSlices default 24;
  270. property Stacks: Integer read FStacks write SetStacks default 48;
  271. end;
  272. // Computes position on the unit sphere of a star record (Z=up)
  273. function StarRecordPositionZUp(const starRecord: TGLStarRecord): TAffineVector;
  274. // Computes position on the unit sphere of a star record (Y=up)
  275. function StarRecordPositionYUp(const starRecord: TGLStarRecord): TAffineVector;
  276. // Computes star color from BV index (RGB) and magnitude (alpha)
  277. function StarRecordColor(const starRecord: TGLStarRecord; bias: Single): TGLVector;
  278. // ------------------------------------------------------------------
  279. implementation
  280. // ------------------------------------------------------------------
  281. uses
  282. GLS.Context,
  283. GLS.State;
  284. // ------------------
  285. // ------------------ TGLSkyBox ------------------
  286. // ------------------
  287. constructor TGLSkyBox.Create(AOwner: TComponent);
  288. begin
  289. inherited Create(AOwner);
  290. CamInvarianceMode := cimPosition;
  291. ObjectStyle := ObjectStyle + [osDirectDraw, osNoVisibilityCulling];
  292. FCloudsPlaneOffset := 0.2;
  293. // this should be set far enough to avoid near plane clipping
  294. FCloudsPlaneSize := 32;
  295. // the bigger, the more this extends the clouds cap to the horizon
  296. end;
  297. destructor TGLSkyBox.Destroy;
  298. begin
  299. inherited;
  300. end;
  301. function TGLSkyBox.GetMaterialLibrary: TGLAbstractMaterialLibrary;
  302. begin
  303. Result := FMaterialLibrary;
  304. end;
  305. procedure TGLSkyBox.Notification(AComponent: TComponent; Operation: TOperation);
  306. begin
  307. if (Operation = opRemove) and (AComponent = FMaterialLibrary) then
  308. MaterialLibrary := nil;
  309. inherited;
  310. end;
  311. procedure TGLSkyBox.DoRender(var ARci: TGLRenderContextInfo; ARenderSelf,
  312. ARenderChildren: Boolean);
  313. begin
  314. // We want children of the sky box to appear far away too
  315. // (note: simply not writing to depth buffer may not make this not work,
  316. // child objects may need the depth buffer to render themselves properly,
  317. // this may require depth buffer cleared after that. - DanB)
  318. Arci.GLStates.DepthWriteMask := False;
  319. Arci.ignoreDepthRequests := true;
  320. inherited;
  321. Arci.ignoreDepthRequests := False;
  322. end;
  323. procedure TGLSkyBox.BuildList(var ARci: TGLRenderContextInfo);
  324. var
  325. f, cps, cof1: Single;
  326. oldStates: TGLStates;
  327. libMat: TGLLibMaterial;
  328. begin
  329. if FMaterialLibrary = nil then
  330. Exit;
  331. with ARci.GLStates do
  332. begin
  333. oldStates := States;
  334. Disable(stDepthTest);
  335. Disable(stLighting);
  336. Disable(stFog);
  337. end;
  338. gl.PushMatrix;
  339. f := ARci.rcci.farClippingDistance * 0.5;
  340. gl.Scalef(f, f, f);
  341. try
  342. case Style of
  343. sbsFull: ;
  344. sbsTopHalf, sbsTopHalfClamped:
  345. begin
  346. gl.Translatef(0, 0.5, 0);
  347. gl.Scalef(1, 0.5, 1);
  348. end;
  349. sbsBottomHalf:
  350. begin
  351. gl.Translatef(0, -0.5, 0);
  352. gl.Scalef(1, 0.5, 1);
  353. end;
  354. sbTopTwoThirds:
  355. begin
  356. gl.Translatef(0, 1 / 3, 0);
  357. gl.Scalef(1, 2 / 3, 1);
  358. end;
  359. end;
  360. // FRONT
  361. libMat := MaterialLibrary.LibMaterialByName(FMatNameFront);
  362. if libMat <> nil then
  363. begin
  364. libMat.Apply(ARci);
  365. repeat
  366. gl.Begin_(GL_QUADS);
  367. xgl.TexCoord2f(0.002, 0.998);
  368. gl.Vertex3f(-1, 1, -1);
  369. xgl.TexCoord2f(0.002, 0.002);
  370. gl.Vertex3f(-1, -1, -1);
  371. xgl.TexCoord2f(0.998, 0.002);
  372. gl.Vertex3f(1, -1, -1);
  373. xgl.TexCoord2f(0.998, 0.998);
  374. gl.Vertex3f(1, 1, -1);
  375. if Style = sbsTopHalfClamped then
  376. begin
  377. xgl.TexCoord2f(0.002, 0.002);
  378. gl.Vertex3f(-1, -1, -1);
  379. xgl.TexCoord2f(0.002, 0.002);
  380. gl.Vertex3f(-1, -3, -1);
  381. xgl.TexCoord2f(0.998, 0.002);
  382. gl.Vertex3f(1, -3, -1);
  383. xgl.TexCoord2f(0.998, 0.002);
  384. gl.Vertex3f(1, -1, -1);
  385. end;
  386. gl.End_;
  387. until not libMat.UnApply(ARci);
  388. end;
  389. // BACK
  390. libMat := MaterialLibrary.LibMaterialByName(FMatNameBack);
  391. if libMat <> nil then
  392. begin
  393. libMat.Apply(ARci);
  394. repeat
  395. gl.Begin_(GL_QUADS);
  396. xgl.TexCoord2f(0.002, 0.998);
  397. gl.Vertex3f(1, 1, 1);
  398. xgl.TexCoord2f(0.002, 0.002);
  399. gl.Vertex3f(1, -1, 1);
  400. xgl.TexCoord2f(0.998, 0.002);
  401. gl.Vertex3f(-1, -1, 1);
  402. xgl.TexCoord2f(0.998, 0.998);
  403. gl.Vertex3f(-1, 1, 1);
  404. if Style = sbsTopHalfClamped then
  405. begin
  406. xgl.TexCoord2f(0.002, 0.002);
  407. gl.Vertex3f(1, -1, 1);
  408. xgl.TexCoord2f(0.002, 0.002);
  409. gl.Vertex3f(1, -3, 1);
  410. xgl.TexCoord2f(0.998, 0.002);
  411. gl.Vertex3f(-1, -3, 1);
  412. xgl.TexCoord2f(0.998, 0.002);
  413. gl.Vertex3f(-1, -1, 1);
  414. end;
  415. gl.End_;
  416. until not libMat.UnApply(ARci);
  417. end;
  418. // TOP
  419. libMat := MaterialLibrary.LibMaterialByName(FMatNameTop);
  420. if libMat <> nil then
  421. begin
  422. libMat.Apply(ARci);
  423. repeat
  424. gl.Begin_(GL_QUADS);
  425. xgl.TexCoord2f(0.002, 0.998);
  426. gl.Vertex3f(-1, 1, 1);
  427. xgl.TexCoord2f(0.002, 0.002);
  428. gl.Vertex3f(-1, 1, -1);
  429. xgl.TexCoord2f(0.998, 0.002);
  430. gl.Vertex3f(1, 1, -1);
  431. xgl.TexCoord2f(0.998, 0.998);
  432. gl.Vertex3f(1, 1, 1);
  433. gl.End_;
  434. until not libMat.UnApply(ARci);
  435. end;
  436. // BOTTOM
  437. libMat := MaterialLibrary.LibMaterialByName(FMatNameBottom);
  438. if libMat <> nil then
  439. begin
  440. libMat.Apply(ARci);
  441. repeat
  442. gl.Begin_(GL_QUADS);
  443. xgl.TexCoord2f(0.002, 0.998);
  444. gl.Vertex3f(-1, -1, -1);
  445. xgl.TexCoord2f(0.002, 0.002);
  446. gl.Vertex3f(-1, -1, 1);
  447. xgl.TexCoord2f(0.998, 0.002);
  448. gl.Vertex3f(1, -1, 1);
  449. xgl.TexCoord2f(0.998, 0.998);
  450. gl.Vertex3f(1, -1, -1);
  451. gl.End_;
  452. until not libMat.UnApply(ARci);
  453. end;
  454. // LEFT
  455. libMat := MaterialLibrary.LibMaterialByName(FMatNameLeft);
  456. if libMat <> nil then
  457. begin
  458. libMat.Apply(ARci);
  459. repeat
  460. gl.Begin_(GL_QUADS);
  461. xgl.TexCoord2f(0.002, 0.998);
  462. gl.Vertex3f(-1, 1, 1);
  463. xgl.TexCoord2f(0.002, 0.002);
  464. gl.Vertex3f(-1, -1, 1);
  465. xgl.TexCoord2f(0.998, 0.002);
  466. gl.Vertex3f(-1, -1, -1);
  467. xgl.TexCoord2f(0.998, 0.998);
  468. gl.Vertex3f(-1, 1, -1);
  469. if Style = sbsTopHalfClamped then
  470. begin
  471. xgl.TexCoord2f(0.002, 0.002);
  472. gl.Vertex3f(-1, -1, 1);
  473. xgl.TexCoord2f(0.002, 0.002);
  474. gl.Vertex3f(-1, -3, 1);
  475. xgl.TexCoord2f(0.998, 0.002);
  476. gl.Vertex3f(-1, -3, -1);
  477. xgl.TexCoord2f(0.998, 0.002);
  478. gl.Vertex3f(-1, -1, -1);
  479. end;
  480. gl.End_;
  481. until not libMat.UnApply(ARci);
  482. end;
  483. // RIGHT
  484. libMat := MaterialLibrary.LibMaterialByName(FMatNameRight);
  485. if libMat <> nil then
  486. begin
  487. libMat.Apply(ARci);
  488. repeat
  489. gl.Begin_(GL_QUADS);
  490. xgl.TexCoord2f(0.002, 0.998);
  491. gl.Vertex3f(1, 1, -1);
  492. xgl.TexCoord2f(0.002, 0.002);
  493. gl.Vertex3f(1, -1, -1);
  494. xgl.TexCoord2f(0.998, 0.002);
  495. gl.Vertex3f(1, -1, 1);
  496. xgl.TexCoord2f(0.998, 0.998);
  497. gl.Vertex3f(1, 1, 1);
  498. if Style = sbsTopHalfClamped then
  499. begin
  500. xgl.TexCoord2f(0.002, 0.002);
  501. gl.Vertex3f(1, -1, -1);
  502. xgl.TexCoord2f(0.002, 0.002);
  503. gl.Vertex3f(1, -3, -1);
  504. xgl.TexCoord2f(0.998, 0.002);
  505. gl.Vertex3f(1, -3, 1);
  506. xgl.TexCoord2f(0.998, 0.002);
  507. gl.Vertex3f(1, -1, 1);
  508. end;
  509. gl.End_;
  510. until not libMat.UnApply(ARci);
  511. end;
  512. // CLOUDS CAP PLANE
  513. libMat := MaterialLibrary.LibMaterialByName(FMatNameClouds);
  514. if libMat <> nil then
  515. begin
  516. // pre-calculate possible values to speed up
  517. cps := FCloudsPlaneSize * 0.5;
  518. cof1 := FCloudsPlaneOffset;
  519. libMat.Apply(ARci);
  520. repeat
  521. gl.Begin_(GL_QUADS);
  522. xgl.TexCoord2f(0, 1);
  523. gl.Vertex3f(-cps, cof1, cps);
  524. xgl.TexCoord2f(0, 0);
  525. gl.Vertex3f(-cps, cof1, -cps);
  526. xgl.TexCoord2f(1, 0);
  527. gl.Vertex3f(cps, cof1, -cps);
  528. xgl.TexCoord2f(1, 1);
  529. gl.Vertex3f(cps, cof1, cps);
  530. gl.End_;
  531. until not libMat.UnApply(ARci);
  532. end;
  533. gl.PopMatrix;
  534. if stLighting in oldStates then
  535. ARci.GLStates.Enable(stLighting);
  536. if stFog in oldStates then
  537. ARci.GLStates.Enable(stFog);
  538. if stDepthTest in oldStates then
  539. ARci.GLStates.Enable(stDepthTest);
  540. finally
  541. end;
  542. end;
  543. procedure TGLSkyBox.SetCloudsPlaneOffset(const Value: single);
  544. begin
  545. FCloudsPlaneOffset := Value;
  546. StructureChanged;
  547. end;
  548. procedure TGLSkyBox.SetCloudsPlaneSize(const Value: single);
  549. begin
  550. FCloudsPlaneSize := Value;
  551. StructureChanged;
  552. end;
  553. procedure TGLSkyBox.SetStyle(const value: TGLSkyBoxStyle);
  554. begin
  555. FStyle := value;
  556. StructureChanged;
  557. end;
  558. procedure TGLSkyBox.SetMaterialLibrary(const value: TGLMaterialLibrary);
  559. begin
  560. FMaterialLibrary := value;
  561. StructureChanged;
  562. end;
  563. procedure TGLSkyBox.SetMatNameBack(const Value: string);
  564. begin
  565. FMatNameBack := Value;
  566. StructureChanged;
  567. end;
  568. procedure TGLSkyBox.SetMatNameBottom(const Value: string);
  569. begin
  570. FMatNameBottom := Value;
  571. StructureChanged;
  572. end;
  573. procedure TGLSkyBox.SetMatNameClouds(const Value: string);
  574. begin
  575. FMatNameClouds := Value;
  576. StructureChanged;
  577. end;
  578. procedure TGLSkyBox.SetMatNameFront(const Value: string);
  579. begin
  580. FMatNameFront := Value;
  581. StructureChanged;
  582. end;
  583. procedure TGLSkyBox.SetMatNameLeft(const Value: string);
  584. begin
  585. FMatNameLeft := Value;
  586. StructureChanged;
  587. end;
  588. procedure TGLSkyBox.SetMatNameRight(const Value: string);
  589. begin
  590. FMatNameRight := Value;
  591. StructureChanged;
  592. end;
  593. procedure TGLSkyBox.SetMatNameTop(const Value: string);
  594. begin
  595. FMatNameTop := Value;
  596. StructureChanged;
  597. end;
  598. //--------------------- SkyDome Region ------------------------------
  599. function StarRecordPositionYUp(const starRecord: TGLStarRecord): TAffineVector;
  600. var
  601. f: Single;
  602. begin
  603. SinCosine(starRecord.DEC * (0.01 * PI / 180), Result.Y, f);
  604. SinCosine(starRecord.RA * (0.01 * PI / 180), f, Result.X, Result.Z);
  605. end;
  606. function StarRecordPositionZUp(const starRecord: TGLStarRecord): TAffineVector;
  607. var
  608. f: Single;
  609. begin
  610. SinCosine(starRecord.DEC * (0.01 * PI / 180), Result.Z, f);
  611. SinCosine(starRecord.RA * (0.01 * PI / 180), f, Result.X, Result.Y);
  612. end;
  613. function StarRecordColor(const starRecord: TGLStarRecord; bias: Single)
  614. : TGLVector;
  615. const
  616. // very *rough* approximation
  617. cBVm035: TGLVector = (X: 0.7; Y: 0.8; Z: 1.0; W: 1);
  618. cBV015: TGLVector = (X: 1.0; Y: 1.0; Z: 1.0; W: 1);
  619. cBV060: TGLVector = (X: 1.0; Y: 1.0; Z: 0.7; W: 1);
  620. cBV135: TGLVector = (X: 1.0; Y: 0.8; Z: 0.7; W: 1);
  621. var
  622. bvIndex100: Integer;
  623. begin
  624. bvIndex100 := starRecord.BVColorIndex - 50;
  625. // compute RGB color for B&V index
  626. if bvIndex100 < -035 then
  627. Result := cBVm035
  628. else if bvIndex100 < 015 then
  629. VectorLerp(cBVm035, cBV015, (bvIndex100 + 035) * (1 / (015 + 035)), Result)
  630. else if bvIndex100 < 060 then
  631. VectorLerp(cBV015, cBV060, (bvIndex100 - 015) * (1 / (060 - 015)), Result)
  632. else if bvIndex100 < 135 then
  633. VectorLerp(cBV060, cBV135, (bvIndex100 - 060) * (1 / (135 - 060)), Result)
  634. else
  635. Result := cBV135;
  636. // compute transparency for VMag
  637. // the actual factor is 2.512, and not used here
  638. Result.W := PowerSingle(1.2, -(starRecord.VMagnitude * 0.1 - bias));
  639. end;
  640. // ------------------
  641. // ------------------ TGLSkyDomeBand ------------------
  642. // ------------------
  643. constructor TGLSkyDomeBand.Create(Collection: TCollection);
  644. begin
  645. inherited Create(Collection);
  646. FStartColor := TGLColor.Create(Self);
  647. FStartColor.Initialize(clrBlue);
  648. FStartColor.OnNotifyChange := OnColorChange;
  649. FStopColor := TGLColor.Create(Self);
  650. FStopColor.Initialize(clrBlue);
  651. FStopColor.OnNotifyChange := OnColorChange;
  652. FSlices := 12;
  653. FStacks := 1;
  654. end;
  655. destructor TGLSkyDomeBand.Destroy;
  656. begin
  657. FStartColor.Free;
  658. FStopColor.Free;
  659. inherited Destroy;
  660. end;
  661. procedure TGLSkyDomeBand.Assign(Source: TPersistent);
  662. begin
  663. if Source is TGLSkyDomeBand then
  664. begin
  665. FStartAngle := TGLSkyDomeBand(Source).FStartAngle;
  666. FStopAngle := TGLSkyDomeBand(Source).FStopAngle;
  667. FStartColor.Assign(TGLSkyDomeBand(Source).FStartColor);
  668. FStopColor.Assign(TGLSkyDomeBand(Source).FStopColor);
  669. FSlices := TGLSkyDomeBand(Source).FSlices;
  670. FStacks := TGLSkyDomeBand(Source).FStacks;
  671. end;
  672. inherited Destroy;
  673. end;
  674. function TGLSkyDomeBand.GetDisplayName: string;
  675. begin
  676. Result := Format('%d: %.1f° - %.1f°', [Index, StartAngle, StopAngle]);
  677. end;
  678. procedure TGLSkyDomeBand.SetStartAngle(const val: Single);
  679. begin
  680. FStartAngle := ClampValue(val, -90, 90);
  681. if FStartAngle > FStopAngle then
  682. FStopAngle := FStartAngle;
  683. TGLSkyDomeBands(Collection).NotifyChange;
  684. end;
  685. procedure TGLSkyDomeBand.SetStartColor(const val: TGLColor);
  686. begin
  687. FStartColor.Assign(val);
  688. end;
  689. procedure TGLSkyDomeBand.SetStopAngle(const val: Single);
  690. begin
  691. FStopAngle := ClampValue(val, -90, 90);
  692. if FStopAngle < FStartAngle then
  693. FStartAngle := FStopAngle;
  694. TGLSkyDomeBands(Collection).NotifyChange;
  695. end;
  696. procedure TGLSkyDomeBand.SetStopColor(const val: TGLColor);
  697. begin
  698. FStopColor.Assign(val);
  699. end;
  700. procedure TGLSkyDomeBand.SetSlices(const val: Integer);
  701. begin
  702. if val < 3 then
  703. FSlices := 3
  704. else
  705. FSlices := val;
  706. TGLSkyDomeBands(Collection).NotifyChange;
  707. end;
  708. procedure TGLSkyDomeBand.SetStacks(const val: Integer);
  709. begin
  710. if val < 1 then
  711. FStacks := 1
  712. else
  713. FStacks := val;
  714. TGLSkyDomeBands(Collection).NotifyChange;
  715. end;
  716. procedure TGLSkyDomeBand.OnColorChange(sender: TObject);
  717. begin
  718. TGLSkyDomeBands(Collection).NotifyChange;
  719. end;
  720. procedure TGLSkyDomeBand.BuildList(var rci: TGLRenderContextInfo);
  721. // coordinates system note: X is forward, Y is left and Z is up
  722. // always rendered as sphere of radius 1
  723. procedure RenderBand(start, stop: Single;
  724. const colStart, colStop: TGLColorVector);
  725. var
  726. i: Integer;
  727. f, r, r2: Single;
  728. vertex1, vertex2: TGLVector;
  729. begin
  730. vertex1.W := 1;
  731. if start = -90 then
  732. begin
  733. // triangle fan with south pole
  734. gl.Begin_(GL_TRIANGLE_FAN);
  735. gl.Color4fv(@colStart);
  736. gl.Vertex3f(0, 0, -1);
  737. f := 2 * PI / Slices;
  738. SinCosine(DegToRadian(stop), vertex1.Z, r);
  739. gl.Color4fv(@colStop);
  740. for i := 0 to Slices do
  741. begin
  742. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  743. gl.Vertex4fv(@vertex1);
  744. end;
  745. gl.End_;
  746. end
  747. else if stop = 90 then
  748. begin
  749. // triangle fan with north pole
  750. gl.Begin_(GL_TRIANGLE_FAN);
  751. gl.Color4fv(@colStop);
  752. gl.Vertex3fv(@ZHmgPoint);
  753. f := 2 * PI / Slices;
  754. SinCosine(DegToRadian(start), vertex1.Z, r);
  755. gl.Color4fv(@colStart);
  756. for i := Slices downto 0 do
  757. begin
  758. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  759. gl.Vertex4fv(@vertex1);
  760. end;
  761. gl.End_;
  762. end
  763. else
  764. begin
  765. vertex2.W := 1;
  766. // triangle strip
  767. gl.Begin_(GL_TRIANGLE_STRIP);
  768. f := 2 * PI / Slices;
  769. SinCosine(DegToRadian(start), vertex1.Z, r);
  770. SinCosine(DegToRadian(stop), vertex2.Z, r2);
  771. for i := 0 to Slices do
  772. begin
  773. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  774. gl.Color4fv(@colStart);
  775. gl.Vertex4fv(@vertex1);
  776. SinCosine(i * f, r2, vertex2.Y, vertex2.X);
  777. gl.Color4fv(@colStop);
  778. gl.Vertex4fv(@vertex2);
  779. end;
  780. gl.End_;
  781. end;
  782. end;
  783. var
  784. n: Integer;
  785. t, t2: Single;
  786. begin
  787. if StartAngle = StopAngle then
  788. Exit;
  789. for n := 0 to Stacks - 1 do
  790. begin
  791. t := n / Stacks;
  792. t2 := (n + 1) / Stacks;
  793. RenderBand(Lerp(StartAngle, StopAngle, t), Lerp(StartAngle, StopAngle, t2),
  794. VectorLerp(StartColor.Color, StopColor.Color, t),
  795. VectorLerp(StartColor.Color, StopColor.Color, t2));
  796. end;
  797. end;
  798. // ------------------
  799. // ------------------ TGLSkyDomeBands ------------------
  800. // ------------------
  801. constructor TGLSkyDomeBands.Create(AOwner: TComponent);
  802. begin
  803. owner := AOwner;
  804. inherited Create(TGLSkyDomeBand);
  805. end;
  806. function TGLSkyDomeBands.GetOwner: TPersistent;
  807. begin
  808. Result := owner;
  809. end;
  810. procedure TGLSkyDomeBands.SetItems(index: Integer; const val: TGLSkyDomeBand);
  811. begin
  812. inherited Items[index] := val;
  813. end;
  814. function TGLSkyDomeBands.GetItems(index: Integer): TGLSkyDomeBand;
  815. begin
  816. Result := TGLSkyDomeBand(inherited Items[index]);
  817. end;
  818. function TGLSkyDomeBands.Add: TGLSkyDomeBand;
  819. begin
  820. Result := (inherited Add) as TGLSkyDomeBand;
  821. end;
  822. function TGLSkyDomeBands.FindItemID(ID: Integer): TGLSkyDomeBand;
  823. begin
  824. Result := (inherited FindItemID(ID)) as TGLSkyDomeBand;
  825. end;
  826. procedure TGLSkyDomeBands.NotifyChange;
  827. begin
  828. if Assigned(owner) and (owner is TGLBaseSceneObject) then
  829. TGLBaseSceneObject(owner).StructureChanged;
  830. end;
  831. procedure TGLSkyDomeBands.BuildList(var rci: TGLRenderContextInfo);
  832. var
  833. i: Integer;
  834. begin
  835. for i := 0 to Count - 1 do
  836. Items[i].BuildList(rci);
  837. end;
  838. // ------------------
  839. // ------------------ TGLSkyDomeStar ------------------
  840. // ------------------
  841. constructor TGLSkyDomeStar.Create(Collection: TCollection);
  842. begin
  843. inherited Create(Collection);
  844. end;
  845. destructor TGLSkyDomeStar.Destroy;
  846. begin
  847. inherited Destroy;
  848. end;
  849. procedure TGLSkyDomeStar.Assign(Source: TPersistent);
  850. begin
  851. if Source is TGLSkyDomeStar then
  852. begin
  853. FRA := TGLSkyDomeStar(Source).FRA;
  854. FDec := TGLSkyDomeStar(Source).FDec;
  855. FMagnitude := TGLSkyDomeStar(Source).FMagnitude;
  856. FColor := TGLSkyDomeStar(Source).FColor;
  857. SetVector(FCacheCoord, TGLSkyDomeStar(Source).FCacheCoord);
  858. end;
  859. inherited Destroy;
  860. end;
  861. function TGLSkyDomeStar.GetDisplayName: string;
  862. begin
  863. Result := Format('RA: %5.1f / Dec: %5.1f', [RA, DEC]);
  864. end;
  865. // ------------------
  866. // ------------------ TGLSkyDomeStars ------------------
  867. // ------------------
  868. constructor TGLSkyDomeStars.Create(AOwner: TComponent);
  869. begin
  870. owner := AOwner;
  871. inherited Create(TGLSkyDomeStar);
  872. end;
  873. function TGLSkyDomeStars.GetOwner: TPersistent;
  874. begin
  875. Result := owner;
  876. end;
  877. procedure TGLSkyDomeStars.SetItems(index: Integer; const val: TGLSkyDomeStar);
  878. begin
  879. inherited Items[index] := val;
  880. end;
  881. function TGLSkyDomeStars.GetItems(index: Integer): TGLSkyDomeStar;
  882. begin
  883. Result := TGLSkyDomeStar(inherited Items[index]);
  884. end;
  885. function TGLSkyDomeStars.Add: TGLSkyDomeStar;
  886. begin
  887. Result := (inherited Add) as TGLSkyDomeStar;
  888. end;
  889. function TGLSkyDomeStars.FindItemID(ID: Integer): TGLSkyDomeStar;
  890. begin
  891. Result := (inherited FindItemID(ID)) as TGLSkyDomeStar;
  892. end;
  893. procedure TGLSkyDomeStars.PrecomputeCartesianCoordinates;
  894. var
  895. i: Integer;
  896. star: TGLSkyDomeStar;
  897. raC, raS, decC, decS: Single;
  898. begin
  899. // to be enhanced...
  900. for i := 0 to Count - 1 do
  901. begin
  902. star := Items[i];
  903. SinCosine(star.DEC * cPIdiv180, decS, decC);
  904. SinCosine(star.RA * cPIdiv180, decC, raS, raC);
  905. star.FCacheCoord.X := raC;
  906. star.FCacheCoord.Y := raS;
  907. star.FCacheCoord.Z := decS;
  908. end;
  909. end;
  910. procedure TGLSkyDomeStars.BuildList(var rci: TGLRenderContextInfo;
  911. twinkle: Boolean);
  912. var
  913. i, n: Integer;
  914. star: TGLSkyDomeStar;
  915. lastColor: TColor;
  916. lastPointSize10, pointSize10: Integer;
  917. Color, twinkleColor: TGLColorVector;
  918. procedure DoTwinkle;
  919. begin
  920. if (n and 63) = 0 then
  921. begin
  922. twinkleColor := VectorScale(Color, Random * 0.6 + 0.4);
  923. gl.Color3fv(@twinkleColor.X);
  924. n := 0;
  925. end
  926. else
  927. Inc(n);
  928. end;
  929. begin
  930. if Count = 0 then
  931. Exit;
  932. PrecomputeCartesianCoordinates;
  933. lastColor := -1;
  934. n := 0;
  935. lastPointSize10 := -1;
  936. rci.GLStates.Enable(stPointSmooth);
  937. rci.GLStates.Enable(stAlphaTest);
  938. rci.GLStates.SetGLAlphaFunction(cfNotEqual, 0.0);
  939. rci.GLStates.Enable(stBlend);
  940. rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
  941. gl.Begin_(GL_POINTS);
  942. for i := 0 to Count - 1 do
  943. begin
  944. star := Items[i];
  945. pointSize10 := Round((4.5 - star.Magnitude) * 10);
  946. if pointSize10 <> lastPointSize10 then
  947. begin
  948. if pointSize10 > 15 then
  949. begin
  950. gl.End_;
  951. lastPointSize10 := pointSize10;
  952. rci.GLStates.PointSize := pointSize10 * 0.1;
  953. gl.Begin_(GL_POINTS);
  954. end
  955. else if lastPointSize10 <> 15 then
  956. begin
  957. gl.End_;
  958. lastPointSize10 := 15;
  959. rci.GLStates.PointSize := 1.5;
  960. gl.Begin_(GL_POINTS);
  961. end;
  962. end;
  963. if lastColor <> star.FColor then
  964. begin
  965. Color := ConvertWinColor(star.FColor);
  966. if twinkle then
  967. begin
  968. n := 0;
  969. DoTwinkle;
  970. end
  971. else
  972. gl.Color3fv(@Color.X);
  973. lastColor := star.FColor;
  974. end
  975. else if twinkle then
  976. DoTwinkle;
  977. gl.Vertex3fv(@star.FCacheCoord.X);
  978. end;
  979. gl.End_;
  980. // restore default AlphaFunc
  981. rci.GLStates.SetGLAlphaFunction(cfGreater, 0);
  982. end;
  983. //------------------------------------------------------------
  984. procedure TGLSkyDomeStars.AddRandomStars(const nb: Integer; const Color: TColor;
  985. const limitToTopDome: Boolean = False);
  986. var
  987. i: Integer;
  988. coord: TAffineVector;
  989. star: TGLSkyDomeStar;
  990. begin
  991. for i := 1 to nb do
  992. begin
  993. star := Add;
  994. // pick a point in the half-cube
  995. if limitToTopDome then
  996. coord.Z := Random
  997. else
  998. coord.Z := Random * 2 - 1;
  999. // calculate RA and Dec
  1000. star.DEC := ArcSin(coord.Z) * c180divPI;
  1001. star.RA := Random * 360 - 180;
  1002. // pick a color
  1003. star.Color := Color;
  1004. // pick a magnitude
  1005. star.Magnitude := 3;
  1006. end;
  1007. end;
  1008. //------------------------------------------------------------
  1009. procedure TGLSkyDomeStars.AddRandomStars(const nb: Integer;
  1010. const ColorMin, ColorMax: TVector3b;
  1011. const Magnitude_min, Magnitude_max: Single;
  1012. const limitToTopDome: Boolean = False);
  1013. function RandomTT(Min, Max: Byte): Byte;
  1014. begin
  1015. Result := Min + Random(Max - Min);
  1016. end;
  1017. var
  1018. i: Integer;
  1019. coord: TAffineVector;
  1020. star: TGLSkyDomeStar;
  1021. begin
  1022. for i := 1 to nb do
  1023. begin
  1024. star := Add;
  1025. // pick a point in the half-cube
  1026. if limitToTopDome then
  1027. coord.Z := Random
  1028. else
  1029. coord.Z := Random * 2 - 1;
  1030. // calculate RA and Dec
  1031. star.DEC := ArcSin(coord.Z) * c180divPI;
  1032. star.RA := Random * 360 - 180;
  1033. // pick a color
  1034. star.Color := RGB2Color(RandomTT(ColorMin.X, ColorMax.X),
  1035. RandomTT(ColorMin.Y, ColorMax.Y), RandomTT(ColorMin.Z, ColorMax.Z));
  1036. // pick a magnitude
  1037. star.Magnitude := Magnitude_min + Random * (Magnitude_max - Magnitude_min);
  1038. end;
  1039. end;
  1040. procedure TGLSkyDomeStars.LoadStarsFile(const starsFileName: string);
  1041. var
  1042. fs: TFileStream;
  1043. sr: TGLStarRecord;
  1044. colorVector: TGLColorVector;
  1045. begin
  1046. fs := TFileStream.Create(starsFileName, fmOpenRead + fmShareDenyWrite);
  1047. try
  1048. while fs.Position < fs.Size do
  1049. begin
  1050. fs.Read(sr, SizeOf(sr));
  1051. with Add do
  1052. begin
  1053. RA := sr.RA * 0.01;
  1054. DEC := sr.DEC * 0.01;
  1055. colorVector := StarRecordColor(sr, 3);
  1056. Magnitude := sr.VMagnitude * 0.1;
  1057. if sr.VMagnitude > 35 then
  1058. Color := ConvertColorVector(colorVector, colorVector.W)
  1059. else
  1060. Color := ConvertColorVector(colorVector);
  1061. end;
  1062. end;
  1063. finally
  1064. fs.Free;
  1065. end;
  1066. end;
  1067. // ------------------
  1068. // ------------------ TGLSkyDome ------------------
  1069. // ------------------
  1070. constructor TGLSkyDome.Create(AOwner: TComponent);
  1071. begin
  1072. inherited Create(AOwner);
  1073. CamInvarianceMode := cimPosition;
  1074. ObjectStyle := ObjectStyle + [osDirectDraw, osNoVisibilityCulling];
  1075. FBands := TGLSkyDomeBands.Create(Self);
  1076. with FBands.Add do
  1077. begin
  1078. StartAngle := 0;
  1079. StartColor.Color := clrWhite;
  1080. StopAngle := 15;
  1081. StopColor.Color := clrBlue;
  1082. end;
  1083. with FBands.Add do
  1084. begin
  1085. StartAngle := 15;
  1086. StartColor.Color := clrBlue;
  1087. StopAngle := 90;
  1088. Stacks := 4;
  1089. StopColor.Color := clrNavy;
  1090. end;
  1091. FStars := TGLSkyDomeStars.Create(Self);
  1092. end;
  1093. destructor TGLSkyDome.Destroy;
  1094. begin
  1095. FStars.Free;
  1096. FBands.Free;
  1097. inherited Destroy;
  1098. end;
  1099. procedure TGLSkyDome.Assign(Source: TPersistent);
  1100. begin
  1101. if Source is TGLSkyDome then
  1102. begin
  1103. FBands.Assign(TGLSkyDome(Source).FBands);
  1104. FStars.Assign(TGLSkyDome(Source).FStars);
  1105. end;
  1106. inherited;
  1107. end;
  1108. procedure TGLSkyDome.SetBands(const val: TGLSkyDomeBands);
  1109. begin
  1110. FBands.Assign(val);
  1111. StructureChanged;
  1112. end;
  1113. procedure TGLSkyDome.SetStars(const val: TGLSkyDomeStars);
  1114. begin
  1115. FStars.Assign(val);
  1116. StructureChanged;
  1117. end;
  1118. procedure TGLSkyDome.SetOptions(const val: TGLSkyDomeOptions);
  1119. begin
  1120. if val <> FOptions then
  1121. begin
  1122. FOptions := val;
  1123. if sdoTwinkle in FOptions then
  1124. ObjectStyle := ObjectStyle + [osDirectDraw]
  1125. else
  1126. begin
  1127. ObjectStyle := ObjectStyle - [osDirectDraw];
  1128. DestroyHandle;
  1129. end;
  1130. StructureChanged;
  1131. end;
  1132. end;
  1133. procedure TGLSkyDome.BuildList(var rci: TGLRenderContextInfo);
  1134. var
  1135. f: Single;
  1136. begin
  1137. // setup states
  1138. with rci.GLStates do
  1139. begin
  1140. Disable(stLighting);
  1141. Disable(stDepthTest);
  1142. Disable(stFog);
  1143. Disable(stCullFace);
  1144. Disable(stBlend);
  1145. DepthWriteMask := False;
  1146. PolygonMode := pmFill;
  1147. end;
  1148. f := rci.rcci.farClippingDistance * 0.90;
  1149. gl.Scalef(f, f, f);
  1150. Bands.BuildList(rci);
  1151. Stars.BuildList(rci, (sdoTwinkle in FOptions));
  1152. end;
  1153. // ------------------
  1154. // ------------------ TGLEarthSkyDome ------------------
  1155. // ------------------
  1156. constructor TGLEarthSkyDome.Create(AOwner: TComponent);
  1157. begin
  1158. inherited Create(AOwner);
  1159. FMorning := true;
  1160. Bands.Clear;
  1161. FSunElevation := 75;
  1162. FTurbidity := 15;
  1163. FSunZenithColor := TGLColor.CreateInitialized(Self, clrWhite, OnColorChanged);
  1164. FSunDawnColor := TGLColor.CreateInitialized(Self, Vectormake(1, 0.5, 0, 0),
  1165. OnColorChanged);
  1166. FHazeColor := TGLColor.CreateInitialized(Self, Vectormake(0.9, 0.95, 1, 0),
  1167. OnColorChanged);
  1168. FSkyColor := TGLColor.CreateInitialized(Self, Vectormake(0.45, 0.6, 0.9, 0),
  1169. OnColorChanged);
  1170. FNightColor := TGLColor.CreateInitialized(Self, clrTransparent,
  1171. OnColorChanged);
  1172. FDeepColor := TGLColor.CreateInitialized(Self, Vectormake(0, 0.2, 0.4, 0));
  1173. FStacks := 24;
  1174. FSlices := 48;
  1175. PreCalculate;
  1176. end;
  1177. destructor TGLEarthSkyDome.Destroy;
  1178. begin
  1179. FSunZenithColor.Free;
  1180. FSunDawnColor.Free;
  1181. FHazeColor.Free;
  1182. FSkyColor.Free;
  1183. FNightColor.Free;
  1184. FDeepColor.Free;
  1185. inherited Destroy;
  1186. end;
  1187. procedure TGLEarthSkyDome.Assign(Source: TPersistent);
  1188. begin
  1189. if Source is TGLSkyDome then
  1190. begin
  1191. FSunElevation := TGLEarthSkyDome(Source).SunElevation;
  1192. FTurbidity := TGLEarthSkyDome(Source).Turbidity;
  1193. FSunZenithColor.Assign(TGLEarthSkyDome(Source).FSunZenithColor);
  1194. FSunDawnColor.Assign(TGLEarthSkyDome(Source).FSunDawnColor);
  1195. FHazeColor.Assign(TGLEarthSkyDome(Source).FHazeColor);
  1196. FSkyColor.Assign(TGLEarthSkyDome(Source).FSkyColor);
  1197. FNightColor.Assign(TGLEarthSkyDome(Source).FNightColor);
  1198. FSlices := TGLEarthSkyDome(Source).FSlices;
  1199. FStacks := TGLEarthSkyDome(Source).FStacks;
  1200. PreCalculate;
  1201. end;
  1202. inherited;
  1203. end;
  1204. procedure TGLEarthSkyDome.Loaded;
  1205. begin
  1206. inherited;
  1207. PreCalculate;
  1208. end;
  1209. procedure TGLEarthSkyDome.SetSunElevation(const val: Single);
  1210. var
  1211. newVal: Single;
  1212. begin
  1213. newVal := ClampValue(val, -90, 90);
  1214. if FSunElevation <> newVal then
  1215. begin
  1216. FSunElevation := newVal;
  1217. PreCalculate;
  1218. end;
  1219. end;
  1220. procedure TGLEarthSkyDome.SetTurbidity(const val: Single);
  1221. begin
  1222. FTurbidity := ClampValue(val, 1, 120);
  1223. PreCalculate;
  1224. end;
  1225. procedure TGLEarthSkyDome.SetSunZenithColor(const val: TGLColor);
  1226. begin
  1227. FSunZenithColor.Assign(val);
  1228. PreCalculate;
  1229. end;
  1230. procedure TGLEarthSkyDome.SetSunDawnColor(const val: TGLColor);
  1231. begin
  1232. FSunDawnColor.Assign(val);
  1233. PreCalculate;
  1234. end;
  1235. procedure TGLEarthSkyDome.SetHazeColor(const val: TGLColor);
  1236. begin
  1237. FHazeColor.Assign(val);
  1238. PreCalculate;
  1239. end;
  1240. procedure TGLEarthSkyDome.SetSkyColor(const val: TGLColor);
  1241. begin
  1242. FSkyColor.Assign(val);
  1243. PreCalculate;
  1244. end;
  1245. procedure TGLEarthSkyDome.SetNightColor(const val: TGLColor);
  1246. begin
  1247. FNightColor.Assign(val);
  1248. PreCalculate;
  1249. end;
  1250. procedure TGLEarthSkyDome.SetDeepColor(const val: TGLColor);
  1251. begin
  1252. FDeepColor.Assign(val);
  1253. PreCalculate;
  1254. end;
  1255. procedure TGLEarthSkyDome.SetSlices(const val: Integer);
  1256. begin
  1257. if val > 6 then
  1258. FSlices := val
  1259. else
  1260. FSlices := 6;
  1261. StructureChanged;
  1262. end;
  1263. procedure TGLEarthSkyDome.SetStacks(const val: Integer);
  1264. begin
  1265. if val > 1 then
  1266. FStacks := val
  1267. else
  1268. FStacks := 1;
  1269. StructureChanged;
  1270. end;
  1271. procedure TGLEarthSkyDome.BuildList(var rci: TGLRenderContextInfo);
  1272. var
  1273. f: Single;
  1274. begin
  1275. // setup states
  1276. with rci.GLStates do
  1277. begin
  1278. CurrentProgram := 0;
  1279. Disable(stLighting);
  1280. if esoDepthTest in FExtendedOptions then
  1281. begin
  1282. Enable(stDepthTest);
  1283. DepthFunc := cfLEqual;
  1284. end
  1285. else
  1286. Disable(stDepthTest);
  1287. Disable(stFog);
  1288. Disable(stCullFace);
  1289. Disable(stBlend);
  1290. Disable(stAlphaTest);
  1291. DepthWriteMask := False;
  1292. PolygonMode := pmFill;
  1293. end;
  1294. f := rci.rcci.farClippingDistance * 0.95;
  1295. gl.Scalef(f, f, f);
  1296. RenderDome;
  1297. Bands.BuildList(rci);
  1298. Stars.BuildList(rci, (sdoTwinkle in FOptions));
  1299. // restore
  1300. rci.GLStates.DepthWriteMask := true;
  1301. end;
  1302. procedure TGLEarthSkyDome.OnColorChanged(sender: TObject);
  1303. begin
  1304. PreCalculate;
  1305. end;
  1306. procedure TGLEarthSkyDome.SetSunAtTime(HH, MM: Single);
  1307. const
  1308. cHourToElevation1: array [0 .. 23] of Single = (-45, -67.5, -90, -57.5, -45,
  1309. -22.5, 0, 11.25, 22.5, 33.7, 45, 56.25, 67.5, 78.75, 90, 78.75, 67.5, 56.25,
  1310. 45, 33.7, 22.5, 11.25, 0, -22.5);
  1311. cHourToElevation2: array [0 .. 23] of Single = (-0.375, -0.375, 0.375, 0.375,
  1312. 0.375, 0.375, 0.1875, 0.1875, 0.1875, 0.1875, 0.1875, 0.1875, 0.1875,
  1313. 0.1875, -0.1875, -0.1875, -0.1875, -0.1875, -0.1875, -0.1875, -0.1875,
  1314. -0.1875, -0.375, -0.375);
  1315. var
  1316. ts: Single;
  1317. fts: Single;
  1318. i: Integer;
  1319. Color: TColor;
  1320. begin
  1321. HH := Round(HH);
  1322. if HH < 0 then
  1323. HH := 0;
  1324. if HH > 23 then
  1325. HH := 23;
  1326. if MM < 0 then
  1327. MM := 0;
  1328. if MM >= 60 then
  1329. begin
  1330. MM := 0;
  1331. HH := HH + 1;
  1332. if HH > 23 then
  1333. HH := 0;
  1334. end;
  1335. FSunElevation := cHourToElevation1[Round(HH)] + cHourToElevation2
  1336. [Round(HH)] * MM;
  1337. ts := DegToRadian(90 - FSunElevation);
  1338. // Mix base colors
  1339. fts := exp(-6 * (PI / 2 - ts));
  1340. VectorLerp(SunZenithColor.Color, SunDawnColor.Color, fts, FCurSunColor);
  1341. fts := IntPower(1 - cos(ts - 0.5), 2);
  1342. VectorLerp(HazeColor.Color, NightColor.Color, fts, FCurHazeColor);
  1343. VectorLerp(SkyColor.Color, NightColor.Color, fts, FCurSkyColor);
  1344. // Precalculate Turbidity factors
  1345. FCurHazeTurbid := -sqrt(121 - Turbidity) * 2;
  1346. FCurSunSkyTurbid := -(121 - Turbidity);
  1347. // fade stars if required
  1348. if SunElevation > -40 then
  1349. ts := PowerInteger(1 - (SunElevation + 40) / 90, 11)
  1350. else
  1351. ts := 1;
  1352. Color := RGB2Color(Round(ts * 255), Round(ts * 255), Round(ts * 255));
  1353. if esoFadeStarsWithSun in ExtendedOptions then
  1354. for i := 0 to Stars.Count - 1 do
  1355. Stars[i].Color := Color;
  1356. if esoRotateOnTwelveHours in ExtendedOptions then // spining around blue orb
  1357. begin
  1358. if (HH >= 14) and (FMorning) then
  1359. begin
  1360. roll(180);
  1361. for i := 0 to Stars.Count - 1 do
  1362. Stars[i].RA := Stars[i].RA + 180;
  1363. FMorning := False;
  1364. end;
  1365. if (HH >= 2) and (HH < 14) and (not FMorning) then
  1366. begin
  1367. roll(180);
  1368. for i := 0 to Stars.Count - 1 do
  1369. Stars[i].RA := Stars[i].RA + 180;
  1370. FMorning := true;
  1371. end;
  1372. end;
  1373. StructureChanged;
  1374. end;
  1375. procedure TGLEarthSkyDome.PreCalculate;
  1376. var
  1377. ts: Single;
  1378. fts: Single;
  1379. i: Integer;
  1380. Color: TColor;
  1381. begin
  1382. ts := DegToRadian(90 - SunElevation);
  1383. // Precompose base colors
  1384. fts := exp(-6 * (PI / 2 - ts));
  1385. VectorLerp(SunZenithColor.Color, SunDawnColor.Color, fts, FCurSunColor);
  1386. fts := PowerInteger(1 - cos(ts - 0.5), 2);
  1387. VectorLerp(HazeColor.Color, NightColor.Color, fts, FCurHazeColor);
  1388. VectorLerp(SkyColor.Color, NightColor.Color, fts, FCurSkyColor);
  1389. // Precalculate Turbidity factors
  1390. FCurHazeTurbid := -sqrt(121 - Turbidity) * 2;
  1391. FCurSunSkyTurbid := -(121 - Turbidity);
  1392. // fade stars if required
  1393. if SunElevation > -40 then
  1394. ts := PowerInteger(1 - (SunElevation + 40) / 90, 11)
  1395. else
  1396. ts := 1;
  1397. Color := RGB2Color(Round(ts * 255), Round(ts * 255), Round(ts * 255));
  1398. if esoFadeStarsWithSun in ExtendedOptions then
  1399. for i := 0 to Stars.Count - 1 do
  1400. Stars[i].Color := Color;
  1401. if esoRotateOnTwelveHours in ExtendedOptions then
  1402. begin
  1403. if SunElevation = 90 then
  1404. begin
  1405. roll(180);
  1406. for i := 0 to Stars.Count - 1 do
  1407. Stars[i].RA := Stars[i].RA + 180;
  1408. end
  1409. else if SunElevation = -90 then
  1410. begin
  1411. roll(180);
  1412. for i := 0 to Stars.Count - 1 do
  1413. Stars[i].RA := Stars[i].RA + 180;
  1414. end;
  1415. end;
  1416. StructureChanged;
  1417. end;
  1418. function TGLEarthSkyDome.CalculateColor(const theta, cosGamma: Single)
  1419. : TGLColorVector;
  1420. var
  1421. t: Single;
  1422. begin
  1423. t := PI / 2 - theta;
  1424. // mix to get haze/sky
  1425. VectorLerp(FCurSkyColor, FCurHazeColor, ClampValue(exp(FCurHazeTurbid * t), 0,
  1426. 1), Result);
  1427. // then mix sky with sun
  1428. VectorLerp(Result, FCurSunColor,
  1429. ClampValue(exp(FCurSunSkyTurbid * cosGamma * (1 + t)) * 1.1, 0, 1), Result);
  1430. end;
  1431. procedure TGLEarthSkyDome.RenderDome;
  1432. var
  1433. ts: Single;
  1434. steps: Integer;
  1435. sunPos: TAffineVector;
  1436. sinTable, cosTable: PFloatArray;
  1437. // coordinates system note: X is forward, Y is left and Z is up
  1438. // always rendered as sphere of radius 1
  1439. function CalculateCosGamma(const p: TGLVector): Single;
  1440. begin
  1441. Result := 1 - VectorAngleCosine(PAffineVector(@p)^, sunPos);
  1442. end;
  1443. procedure RenderDeepBand(stop: Single);
  1444. var
  1445. i: Integer;
  1446. r, thetaStart: Single;
  1447. vertex1: TGLVector;
  1448. Color: TGLColorVector;
  1449. begin
  1450. r := 0;
  1451. vertex1.W := 1;
  1452. // triangle fan with south pole
  1453. gl.Begin_(GL_TRIANGLE_FAN);
  1454. Color := CalculateColor(0, CalculateCosGamma(ZHmgPoint));
  1455. gl.Color4fv(DeepColor.AsAddress);
  1456. gl.Vertex3f(0, 0, -1);
  1457. SinCosine(DegToRadian(stop), vertex1.Z, r);
  1458. thetaStart := DegToRadian(90 - stop);
  1459. for i := 0 to steps - 1 do
  1460. begin
  1461. vertex1.X := r * cosTable[i];
  1462. vertex1.Y := r * sinTable[i];
  1463. Color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1464. gl.Color4fv(@Color);
  1465. gl.Vertex4fv(@vertex1);
  1466. end;
  1467. gl.End_;
  1468. end;
  1469. procedure RenderBand(start, stop: Single);
  1470. var
  1471. i: Integer;
  1472. r, r2, thetaStart, thetaStop: Single;
  1473. vertex1, vertex2: TGLVector;
  1474. Color: TGLColorVector;
  1475. begin
  1476. vertex1.W := 1;
  1477. if stop = 90 then
  1478. begin
  1479. // triangle fan with north pole
  1480. gl.Begin_(GL_TRIANGLE_FAN);
  1481. Color := CalculateColor(0, CalculateCosGamma(ZHmgPoint));
  1482. gl.Color4fv(@Color);
  1483. gl.Vertex4fv(@ZHmgPoint);
  1484. SinCosine(DegToRadian(start), vertex1.Z, r);
  1485. thetaStart := DegToRadian(90 - start);
  1486. for i := 0 to steps - 1 do
  1487. begin
  1488. vertex1.X := r * cosTable[i];
  1489. vertex1.Y := r * sinTable[i];
  1490. Color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1491. gl.Color4fv(@Color);
  1492. gl.Vertex4fv(@vertex1);
  1493. end;
  1494. gl.End_;
  1495. end
  1496. else
  1497. begin
  1498. vertex2.W := 1;
  1499. // triangle strip
  1500. gl.Begin_(GL_TRIANGLE_STRIP);
  1501. SinCosine(DegToRadian(start), vertex1.Z, r);
  1502. SinCosine(DegToRadian(stop), vertex2.Z, r2);
  1503. thetaStart := DegToRadian(90 - start);
  1504. thetaStop := DegToRadian(90 - stop);
  1505. for i := 0 to steps - 1 do
  1506. begin
  1507. vertex1.X := r * cosTable[i];
  1508. vertex1.Y := r * sinTable[i];
  1509. Color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1510. gl.Color4fv(@Color);
  1511. gl.Vertex4fv(@vertex1);
  1512. vertex2.X := r2 * cosTable[i];
  1513. vertex2.Y := r2 * sinTable[i];
  1514. Color := CalculateColor(thetaStop, CalculateCosGamma(vertex2));
  1515. gl.Color4fv(@Color);
  1516. gl.Vertex4fv(@vertex2);
  1517. end;
  1518. gl.End_;
  1519. end;
  1520. end;
  1521. var
  1522. n, i, sdiv2: Integer;
  1523. t, t2, p, fs: Single;
  1524. begin
  1525. ts := DegToRadian(90 - SunElevation);
  1526. SetVector(sunPos, sin(ts), 0, cos(ts));
  1527. // prepare sin/cos LUT, with a higher sampling around 0Ѝ
  1528. n := Slices div 2;
  1529. steps := 2 * n + 1;
  1530. GetMem(sinTable, steps * SizeOf(Single));
  1531. GetMem(cosTable, steps * SizeOf(Single));
  1532. for i := 1 to n do
  1533. begin
  1534. p := (1 - sqrt(cos((i / n) * cPIdiv2))) * PI;
  1535. SinCosine(p, sinTable[n + i], cosTable[n + i]);
  1536. sinTable[n - i] := -sinTable[n + i];
  1537. cosTable[n - i] := cosTable[n + i];
  1538. end;
  1539. // these are defined by hand for precision issue: the dome must wrap exactly
  1540. sinTable[n] := 0;
  1541. cosTable[n] := 1;
  1542. sinTable[0] := 0;
  1543. cosTable[0] := -1;
  1544. sinTable[steps - 1] := 0;
  1545. cosTable[steps - 1] := -1;
  1546. fs := SunElevation / 90;
  1547. // start render
  1548. t := 0;
  1549. sdiv2 := Stacks div 2;
  1550. for n := 0 to Stacks - 1 do
  1551. begin
  1552. if fs > 0 then
  1553. begin
  1554. if n < sdiv2 then
  1555. t2 := fs - fs * Sqr((sdiv2 - n) / sdiv2)
  1556. else
  1557. t2 := fs + Sqr((n - sdiv2) / (sdiv2 - 1)) * (1 - fs);
  1558. end
  1559. else
  1560. t2 := (n + 1) / Stacks;
  1561. RenderBand(Lerp(1, 90, t), Lerp(1, 90, t2));
  1562. t := t2;
  1563. end;
  1564. RenderDeepBand(1);
  1565. FreeMem(sinTable);
  1566. FreeMem(cosTable);
  1567. end;
  1568. // -------------------------------------------------------------
  1569. initialization
  1570. // -------------------------------------------------------------
  1571. RegisterClasses([TGLSkyBox, TGLSkyDome, TGLEarthSkyDome]);
  1572. end.