2
0

GLSkydome.pas 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLSkydome;
  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. OpenGLTokens,
  16. GLScene,
  17. GLContext,
  18. GLState,
  19. GLVectorGeometry,
  20. GLGraphics,
  21. GLVectorTypes,
  22. GLColor,
  23. GLRenderContextInfo;
  24. type
  25. TGLStarRecord = packed record
  26. RA : Word; // x100 builtin factor, degrees
  27. DEC : SmallInt; // x100 builtin factor, degrees
  28. BVColorIndex : Byte; // x100 builtin factor
  29. VMagnitude : Byte; // x10 builtin factor
  30. end;
  31. PGLStarRecord = ^TGLStarRecord;
  32. TGLSkyDomeBand = class(TCollectionItem)
  33. private
  34. FStartAngle: Single;
  35. FStopAngle: Single;
  36. FStartColor: TGLColor;
  37. FStopColor: TGLColor;
  38. FSlices: Integer;
  39. FStacks: Integer;
  40. protected
  41. function GetDisplayName: string; override;
  42. procedure SetStartAngle(const val: Single);
  43. procedure SetStartColor(const val: TGLColor);
  44. procedure SetStopAngle(const val: Single);
  45. procedure SetStopColor(const val: TGLColor);
  46. procedure SetSlices(const val: Integer);
  47. procedure SetStacks(const val: Integer);
  48. procedure OnColorChange(sender: TObject);
  49. public
  50. constructor Create(Collection: TCollection); override;
  51. destructor Destroy; override;
  52. procedure Assign(Source: TPersistent); override;
  53. procedure BuildList(var rci: TGLRenderContextInfo);
  54. published
  55. property StartAngle: Single read FStartAngle write SetStartAngle;
  56. property StartColor: TGLColor read FStartColor write SetStartColor;
  57. property StopAngle: Single read FStopAngle write SetStopAngle;
  58. property StopColor: TGLColor read FStopColor write SetStopColor;
  59. property Slices: Integer read FSlices write SetSlices default 12;
  60. property Stacks: Integer read FStacks write SetStacks default 1;
  61. end;
  62. TGLSkyDomeBands = class(TCollection)
  63. protected
  64. owner: TComponent;
  65. function GetOwner: TPersistent; override;
  66. procedure SetItems(index: Integer; const val: TGLSkyDomeBand);
  67. function GetItems(index: Integer): TGLSkyDomeBand;
  68. public
  69. constructor Create(AOwner: TComponent);
  70. function Add: TGLSkyDomeBand;
  71. function FindItemID(ID: Integer): TGLSkyDomeBand;
  72. property Items[index: Integer]: TGLSkyDomeBand read GetItems write SetItems;
  73. default;
  74. procedure NotifyChange;
  75. procedure BuildList(var rci: TGLRenderContextInfo);
  76. end;
  77. TGLSkyDomeStar = class(TCollectionItem)
  78. private
  79. FRA, FDec: Single;
  80. FMagnitude: Single;
  81. FColor: TColor;
  82. FCacheCoord: TAffineVector; // cached cartesian coordinates
  83. protected
  84. function GetDisplayName: string; override;
  85. public
  86. constructor Create(Collection: TCollection); override;
  87. destructor Destroy; override;
  88. procedure Assign(Source: TPersistent); override;
  89. published
  90. {Right Ascension, in degrees. }
  91. property RA: Single read FRA write FRA;
  92. {Declination, in degrees. }
  93. property Dec: Single read FDec write FDec;
  94. {Absolute magnitude. }
  95. property Magnitude: Single read FMagnitude write FMagnitude;
  96. {Color of the star. }
  97. property Color: TColor read FColor write FColor;
  98. end;
  99. TGLSkyDomeStars = class(TCollection)
  100. protected
  101. owner: TComponent;
  102. function GetOwner: TPersistent; override;
  103. procedure SetItems(index: Integer; const val: TGLSkyDomeStar);
  104. function GetItems(index: Integer): TGLSkyDomeStar;
  105. procedure PrecomputeCartesianCoordinates;
  106. public
  107. constructor Create(AOwner: TComponent);
  108. function Add: TGLSkyDomeStar;
  109. function FindItemID(ID: Integer): TGLSkyDomeStar;
  110. property Items[index: Integer]: TGLSkyDomeStar read GetItems write SetItems;
  111. default;
  112. procedure BuildList(var rci: TGLRenderContextInfo; twinkle: Boolean);
  113. (* Adds nb random stars of the given color.
  114. Stars are homogenously scattered on the complete sphere, not only the band defined or visible dome. *)
  115. procedure AddRandomStars(const nb: Integer; const color: TColor; const limitToTopDome: Boolean = False); overload;
  116. procedure AddRandomStars(const nb: Integer; const ColorMin, ColorMax:TVector3b; const Magnitude_min, Magnitude_max: Single;const limitToTopDome: Boolean = False); overload;
  117. (* Load a 'stars' file, which is made of TGLStarRecord.
  118. Not that '.stars' files should already be sorted by magnitude and color. *)
  119. procedure LoadStarsFile(const starsFileName: string);
  120. end;
  121. TGLSkyDomeOption = (sdoTwinkle);
  122. TGLSkyDomeOptions = set of TGLSkyDomeOption;
  123. (* Renders a sky dome always centered on the camera.
  124. If you use this object make sure it is rendered *first*, as it ignores
  125. depth buffering and overwrites everything. All children of a skydome
  126. are rendered in the skydome's coordinate system.
  127. The skydome is described by "bands", each "band" is an horizontal cut
  128. of a sphere, and you can have as many bands as you wish.
  129. Estimated CPU cost (K7-500, GeForce SDR, default bands):
  130. 800x600 fullscreen filled: 4.5 ms (220 FPS, worst case)
  131. Geometry cost (0% fill): 0.7 ms (1300 FPS, best case) *)
  132. TGLSkyDome = class(TGLCameraInvariantObject)
  133. private
  134. FOptions: TGLSkyDomeOptions;
  135. FBands: TGLSkyDomeBands;
  136. FStars: TGLSkyDomeStars;
  137. protected
  138. procedure SetBands(const val: TGLSkyDomeBands);
  139. procedure SetStars(const val: TGLSkyDomeStars);
  140. procedure SetOptions(const val: TGLSkyDomeOptions);
  141. public
  142. constructor Create(AOwner: TComponent); override;
  143. destructor Destroy; override;
  144. procedure Assign(Source: TPersistent); override;
  145. procedure BuildList(var rci: TGLRenderContextInfo); override;
  146. published
  147. property Bands: TGLSkyDomeBands read FBands write SetBands;
  148. property Stars: TGLSkyDomeStars read FStars write SetStars;
  149. property Options: TGLSkyDomeOptions read FOptions write SetOptions default [];
  150. end;
  151. TEarthSkydomeOption = (esoFadeStarsWithSun, esoRotateOnTwelveHours, esoDepthTest);
  152. TEarthSkydomeOptions = set of TEarthSkydomeOption;
  153. (* Render a skydome like what can be seen on earth.
  154. Color is based on sun position and turbidity, to "mimic" atmospheric
  155. Rayleigh and Mie scatterings. The colors can be adjusted to render
  156. weird/extra-terrestrial atmospheres too.
  157. The default slices/stacks values make for an average quality rendering,
  158. for a very clean rendering, use 64/64 (more is overkill in most cases).
  159. The complexity is quite high though, making a T&L 3D board a necessity
  160. for using TGLEarthSkyDome. *)
  161. TGLEarthSkyDome = class(TGLSkyDome)
  162. private
  163. FSunElevation: Single;
  164. FTurbidity: Single;
  165. FCurSunColor, FCurSkyColor, FCurHazeColor: TColorVector;
  166. FCurHazeTurbid, FCurSunSkyTurbid: Single;
  167. FSunZenithColor: TGLColor;
  168. FSunDawnColor: TGLColor;
  169. FHazeColor: TGLColor;
  170. FSkyColor: TGLColor;
  171. FNightColor: TGLColor;
  172. FDeepColor: TGLColor;
  173. FSlices, FStacks: Integer;
  174. FExtendedOptions: TEarthSkydomeOptions;
  175. FMorning: boolean;
  176. protected
  177. procedure Loaded; override;
  178. procedure SetSunElevation(const val: Single);
  179. procedure SetTurbidity(const val: Single);
  180. procedure SetSunZenithColor(const val: TGLColor);
  181. procedure SetSunDawnColor(const val: TGLColor);
  182. procedure SetHazeColor(const val: TGLColor);
  183. procedure SetSkyColor(const val: TGLColor);
  184. procedure SetNightColor(const val: TGLColor);
  185. procedure SetDeepColor(const val: TGLColor);
  186. procedure SetSlices(const val: Integer);
  187. procedure SetStacks(const val: Integer);
  188. procedure OnColorChanged(Sender: TObject);
  189. procedure PreCalculate;
  190. procedure RenderDome;
  191. function CalculateColor(const theta, cosGamma: Single): TColorVector;
  192. public
  193. constructor Create(AOwner: TComponent); override;
  194. destructor Destroy; override;
  195. procedure Assign(Source: TPersistent); override;
  196. procedure BuildList(var rci: TGLRenderContextInfo); override;
  197. procedure SetSunAtTime(HH, MM: Single);
  198. published
  199. // Elevation of the sun, measured in degrees
  200. property SunElevation: Single read FSunElevation write SetSunElevation;
  201. // Expresses the purity of air. Value range is from 1 (pure athmosphere) to 120 (very nebulous)
  202. property Turbidity: Single read FTurbidity write SetTurbidity;
  203. property SunZenithColor: TGLColor read FSunZenithColor write SetSunZenithColor;
  204. property SunDawnColor: TGLColor read FSunDawnColor write SetSunDawnColor;
  205. property HazeColor: TGLColor read FHazeColor write SetHazeColor;
  206. property SkyColor: TGLColor read FSkyColor write SetSkyColor;
  207. property NightColor: TGLColor read FNightColor write SetNightColor;
  208. property DeepColor: TGLColor read FDeepColor write SetDeepColor;
  209. property ExtendedOptions: TEarthSkydomeOptions read FExtendedOptions write FExtendedOptions;
  210. property Slices: Integer read FSlices write SetSlices default 24;
  211. property Stacks: Integer read FStacks write SetStacks default 48;
  212. end;
  213. // Computes position on the unit sphere of a star record (Z=up)
  214. function StarRecordPositionZUp(const starRecord: TGLStarRecord): TAffineVector;
  215. // Computes position on the unit sphere of a star record (Y=up)
  216. function StarRecordPositionYUp(const starRecord: TGLStarRecord): TAffineVector;
  217. // Computes star color from BV index (RGB) and magnitude (alpha)
  218. function StarRecordColor(const starRecord: TGLStarRecord; bias: Single): TVector;
  219. // ------------------------------------------------------------------
  220. implementation
  221. // ------------------------------------------------------------------
  222. function StarRecordPositionYUp(const starRecord : TGLStarRecord) : TAffineVector;
  223. var
  224. f : Single;
  225. begin
  226. SinCosine(starRecord.DEC*(0.01*PI/180), Result.Y, f);
  227. SinCosine(starRecord.RA*(0.01*PI/180), f, Result.X, Result.Z);
  228. end;
  229. function StarRecordPositionZUp(const starRecord : TGLStarRecord) : TAffineVector;
  230. var
  231. f : Single;
  232. begin
  233. SinCosine(starRecord.DEC*(0.01*PI/180), Result.Z, f);
  234. SinCosine(starRecord.RA*(0.01*PI/180), f, Result.X, Result.Y);
  235. end;
  236. function StarRecordColor(const starRecord : TGLStarRecord; bias : Single) : TVector;
  237. const
  238. // very *rough* approximation
  239. cBVm035 : TVector = (X:0.7; Y:0.8; Z:1.0; W:1);
  240. cBV015 : TVector = (X:1.0; Y:1.0; Z:1.0; W:1);
  241. cBV060 : TVector = (X:1.0; Y:1.0; Z:0.7; W:1);
  242. cBV135 : TVector = (X:1.0; Y:0.8; Z:0.7; W:1);
  243. var
  244. bvIndex100 : Integer;
  245. begin
  246. bvIndex100:=starRecord.BVColorIndex-50;
  247. // compute RGB color for B&V index
  248. if bvIndex100<-035 then
  249. Result:=cBVm035
  250. else if bvIndex100<015 then
  251. VectorLerp(cBVm035, cBV015, (bvIndex100+035)*(1/(015+035)), Result)
  252. else if bvIndex100<060 then
  253. VectorLerp(cBV015, cBV060, (bvIndex100-015)*(1/(060-015)), Result)
  254. else if bvIndex100<135 then
  255. VectorLerp(cBV060, cBV135, (bvIndex100-060)*(1/(135-060)), Result)
  256. else Result:=cBV135;
  257. // compute transparency for VMag
  258. // the actual factor is 2.512, and not used here
  259. Result.W:=PowerSingle(1.2, -(starRecord.VMagnitude*0.1-bias));
  260. end;
  261. // ------------------
  262. // ------------------ TGLSkyDomeBand ------------------
  263. // ------------------
  264. constructor TGLSkyDomeBand.Create(Collection: TCollection);
  265. begin
  266. inherited Create(Collection);
  267. FStartColor := TGLColor.Create(Self);
  268. FStartColor.Initialize(clrBlue);
  269. FStartColor.OnNotifyChange := OnColorChange;
  270. FStopColor := TGLColor.Create(Self);
  271. FStopColor.Initialize(clrBlue);
  272. FStopColor.OnNotifyChange := OnColorChange;
  273. FSlices := 12;
  274. FStacks := 1;
  275. end;
  276. destructor TGLSkyDomeBand.Destroy;
  277. begin
  278. FStartColor.Free;
  279. FStopColor.Free;
  280. inherited Destroy;
  281. end;
  282. procedure TGLSkyDomeBand.Assign(Source: TPersistent);
  283. begin
  284. if Source is TGLSkyDomeBand then
  285. begin
  286. FStartAngle := TGLSkyDomeBand(Source).FStartAngle;
  287. FStopAngle := TGLSkyDomeBand(Source).FStopAngle;
  288. FStartColor.Assign(TGLSkyDomeBand(Source).FStartColor);
  289. FStopColor.Assign(TGLSkyDomeBand(Source).FStopColor);
  290. FSlices := TGLSkyDomeBand(Source).FSlices;
  291. FStacks := TGLSkyDomeBand(Source).FStacks;
  292. end;
  293. inherited Destroy;
  294. end;
  295. function TGLSkyDomeBand.GetDisplayName: string;
  296. begin
  297. Result := Format('%d: %.1f° - %.1f°', [Index, StartAngle, StopAngle]);
  298. end;
  299. procedure TGLSkyDomeBand.SetStartAngle(const val: Single);
  300. begin
  301. FStartAngle := ClampValue(val, -90, 90);
  302. if FStartAngle > FStopAngle then FStopAngle := FStartAngle;
  303. TGLSkyDomeBands(Collection).NotifyChange;
  304. end;
  305. procedure TGLSkyDomeBand.SetStartColor(const val: TGLColor);
  306. begin
  307. FStartColor.Assign(val);
  308. end;
  309. procedure TGLSkyDomeBand.SetStopAngle(const val: Single);
  310. begin
  311. FStopAngle := ClampValue(val, -90, 90);
  312. if FStopAngle < FStartAngle then
  313. FStartAngle := FStopAngle;
  314. TGLSkyDomeBands(Collection).NotifyChange;
  315. end;
  316. procedure TGLSkyDomeBand.SetStopColor(const val: TGLColor);
  317. begin
  318. FStopColor.Assign(val);
  319. end;
  320. procedure TGLSkyDomeBand.SetSlices(const val: Integer);
  321. begin
  322. if val < 3 then
  323. FSlices := 3
  324. else
  325. FSlices := val;
  326. TGLSkyDomeBands(Collection).NotifyChange;
  327. end;
  328. procedure TGLSkyDomeBand.SetStacks(const val: Integer);
  329. begin
  330. if val < 1 then
  331. FStacks := 1
  332. else
  333. FStacks := val;
  334. TGLSkyDomeBands(Collection).NotifyChange;
  335. end;
  336. procedure TGLSkyDomeBand.OnColorChange(sender: TObject);
  337. begin
  338. TGLSkyDomeBands(Collection).NotifyChange;
  339. end;
  340. procedure TGLSkyDomeBand.BuildList(var rci: TGLRenderContextInfo);
  341. // coordinates system note: X is forward, Y is left and Z is up
  342. // always rendered as sphere of radius 1
  343. procedure RenderBand(start, stop: Single; const colStart, colStop:
  344. TColorVector);
  345. var
  346. i: Integer;
  347. f, r, r2: Single;
  348. vertex1, vertex2: TVector;
  349. begin
  350. vertex1.W := 1;
  351. if start = -90 then
  352. begin
  353. // triangle fan with south pole
  354. gl.Begin_(GL_TRIANGLE_FAN);
  355. gl.Color4fv(@colStart);
  356. gl.Vertex3f(0, 0, -1);
  357. f := 2 * PI / Slices;
  358. SinCosine(DegToRadian(stop), vertex1.Z, r);
  359. gl.Color4fv(@colStop);
  360. for i := 0 to Slices do
  361. begin
  362. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  363. gl.Vertex4fv(@vertex1);
  364. end;
  365. gl.End_;
  366. end
  367. else if stop = 90 then
  368. begin
  369. // triangle fan with north pole
  370. gl.Begin_(GL_TRIANGLE_FAN);
  371. gl.Color4fv(@colStop);
  372. gl.Vertex3fv(@ZHmgPoint);
  373. f := 2 * PI / Slices;
  374. SinCosine(DegToRadian(start), vertex1.Z, r);
  375. gl.Color4fv(@colStart);
  376. for i := Slices downto 0 do
  377. begin
  378. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  379. gl.Vertex4fv(@vertex1);
  380. end;
  381. gl.End_;
  382. end
  383. else
  384. begin
  385. vertex2.W := 1;
  386. // triangle strip
  387. gl.Begin_(GL_TRIANGLE_STRIP);
  388. f := 2 * PI / Slices;
  389. SinCosine(DegToRadian(start), vertex1.Z, r);
  390. SinCosine(DegToRadian(stop), vertex2.Z, r2);
  391. for i := 0 to Slices do
  392. begin
  393. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  394. gl.Color4fv(@colStart);
  395. gl.Vertex4fv(@vertex1);
  396. SinCosine(i * f, r2, vertex2.Y, vertex2.X);
  397. gl.Color4fv(@colStop);
  398. gl.Vertex4fv(@vertex2);
  399. end;
  400. gl.End_;
  401. end;
  402. end;
  403. var
  404. n: Integer;
  405. t, t2: Single;
  406. begin
  407. if StartAngle = StopAngle then
  408. Exit;
  409. for n := 0 to Stacks - 1 do
  410. begin
  411. t := n / Stacks;
  412. t2 := (n + 1) / Stacks;
  413. RenderBand(Lerp(StartAngle, StopAngle, t),
  414. Lerp(StartAngle, StopAngle, t2),
  415. VectorLerp(StartColor.Color, StopColor.Color, t),
  416. VectorLerp(StartColor.Color, StopColor.Color, t2));
  417. end;
  418. end;
  419. // ------------------
  420. // ------------------ TGLSkyDomeBands ------------------
  421. // ------------------
  422. constructor TGLSkyDomeBands.Create(AOwner: TComponent);
  423. begin
  424. Owner := AOwner;
  425. inherited Create(TGLSkyDomeBand);
  426. end;
  427. function TGLSkyDomeBands.GetOwner: TPersistent;
  428. begin
  429. Result := Owner;
  430. end;
  431. procedure TGLSkyDomeBands.SetItems(index: Integer; const val: TGLSkyDomeBand);
  432. begin
  433. inherited Items[index] := val;
  434. end;
  435. function TGLSkyDomeBands.GetItems(index: Integer): TGLSkyDomeBand;
  436. begin
  437. Result := TGLSkyDomeBand(inherited Items[index]);
  438. end;
  439. function TGLSkyDomeBands.Add: TGLSkyDomeBand;
  440. begin
  441. Result := (inherited Add) as TGLSkyDomeBand;
  442. end;
  443. function TGLSkyDomeBands.FindItemID(ID: Integer): TGLSkyDomeBand;
  444. begin
  445. Result := (inherited FindItemID(ID)) as TGLSkyDomeBand;
  446. end;
  447. procedure TGLSkyDomeBands.NotifyChange;
  448. begin
  449. if Assigned(owner) and (owner is TGLBaseSceneObject) then TGLBaseSceneObject(owner).StructureChanged;
  450. end;
  451. procedure TGLSkyDomeBands.BuildList(var rci: TGLRenderContextInfo);
  452. var
  453. i: Integer;
  454. begin
  455. for i := 0 to Count - 1 do Items[i].BuildList(rci);
  456. end;
  457. // ------------------
  458. // ------------------ TGLSkyDomeStar ------------------
  459. // ------------------
  460. constructor TGLSkyDomeStar.Create(Collection: TCollection);
  461. begin
  462. inherited Create(Collection);
  463. end;
  464. destructor TGLSkyDomeStar.Destroy;
  465. begin
  466. inherited Destroy;
  467. end;
  468. procedure TGLSkyDomeStar.Assign(Source: TPersistent);
  469. begin
  470. if Source is TGLSkyDomeStar then
  471. begin
  472. FRA := TGLSkyDomeStar(Source).FRA;
  473. FDec := TGLSkyDomeStar(Source).FDec;
  474. FMagnitude := TGLSkyDomeStar(Source).FMagnitude;
  475. FColor := TGLSkyDomeStar(Source).FColor;
  476. SetVector(FCacheCoord, TGLSkyDomeStar(Source).FCacheCoord);
  477. end;
  478. inherited Destroy;
  479. end;
  480. function TGLSkyDomeStar.GetDisplayName: string;
  481. begin
  482. Result := Format('RA: %5.1f / Dec: %5.1f', [RA, Dec]);
  483. end;
  484. // ------------------
  485. // ------------------ TGLSkyDomeStars ------------------
  486. // ------------------
  487. constructor TGLSkyDomeStars.Create(AOwner: TComponent);
  488. begin
  489. Owner := AOwner;
  490. inherited Create(TGLSkyDomeStar);
  491. end;
  492. function TGLSkyDomeStars.GetOwner: TPersistent;
  493. begin
  494. Result := Owner;
  495. end;
  496. procedure TGLSkyDomeStars.SetItems(index: Integer; const val: TGLSkyDomeStar);
  497. begin
  498. inherited Items[index] := val;
  499. end;
  500. function TGLSkyDomeStars.GetItems(index: Integer): TGLSkyDomeStar;
  501. begin
  502. Result := TGLSkyDomeStar(inherited Items[index]);
  503. end;
  504. function TGLSkyDomeStars.Add: TGLSkyDomeStar;
  505. begin
  506. Result := (inherited Add) as TGLSkyDomeStar;
  507. end;
  508. function TGLSkyDomeStars.FindItemID(ID: Integer): TGLSkyDomeStar;
  509. begin
  510. Result := (inherited FindItemID(ID)) as TGLSkyDomeStar;
  511. end;
  512. procedure TGLSkyDomeStars.PrecomputeCartesianCoordinates;
  513. var
  514. i: Integer;
  515. star: TGLSkyDomeStar;
  516. raC, raS, decC, decS: Single;
  517. begin
  518. // to be enhanced...
  519. for i := 0 to Count - 1 do
  520. begin
  521. star := Items[i];
  522. SinCosine(star.DEC * cPIdiv180, decS, decC);
  523. SinCosine(star.RA * cPIdiv180, decC, raS, raC);
  524. star.FCacheCoord.X := raC;
  525. star.FCacheCoord.Y := raS;
  526. star.FCacheCoord.Z := decS;
  527. end;
  528. end;
  529. procedure TGLSkyDomeStars.BuildList(var rci: TGLRenderContextInfo; twinkle:
  530. Boolean);
  531. var
  532. i, n: Integer;
  533. star: TGLSkyDomeStar;
  534. lastColor: TColor;
  535. lastPointSize10, pointSize10: Integer;
  536. color, twinkleColor: TColorVector;
  537. procedure DoTwinkle;
  538. begin
  539. if (n and 63) = 0 then
  540. begin
  541. twinkleColor := VectorScale(color, Random * 0.6 + 0.4);
  542. gl.Color3fv(@twinkleColor.X);
  543. n := 0;
  544. end
  545. else
  546. Inc(n);
  547. end;
  548. begin
  549. if Count = 0 then
  550. Exit;
  551. PrecomputeCartesianCoordinates;
  552. lastColor := -1;
  553. n := 0;
  554. lastPointSize10 := -1;
  555. rci.GLStates.Enable(stPointSmooth);
  556. rci.GLStates.Enable(stAlphaTest);
  557. rci.GLStates.SetGLAlphaFunction(cfNotEqual, 0.0);
  558. rci.GLStates.Enable(stBlend);
  559. rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
  560. gl.Begin_(GL_POINTS);
  561. for i := 0 to Count - 1 do
  562. begin
  563. star := Items[i];
  564. pointSize10 := Round((4.5 - star.Magnitude) * 10);
  565. if pointSize10 <> lastPointSize10 then
  566. begin
  567. if pointSize10 > 15 then
  568. begin
  569. gl.End_;
  570. lastPointSize10 := pointSize10;
  571. rci.GLStates.PointSize := pointSize10 * 0.1;
  572. gl.Begin_(GL_POINTS);
  573. end
  574. else if lastPointSize10 <> 15 then
  575. begin
  576. gl.End_;
  577. lastPointSize10 := 15;
  578. rci.GLStates.PointSize := 1.5;
  579. gl.Begin_(GL_POINTS);
  580. end;
  581. end;
  582. if lastColor <> star.FColor then
  583. begin
  584. color := ConvertWinColor(star.FColor);
  585. if twinkle then
  586. begin
  587. n := 0;
  588. DoTwinkle;
  589. end
  590. else
  591. gl.Color3fv(@color.X);
  592. lastColor := star.FColor;
  593. end
  594. else if twinkle then
  595. DoTwinkle;
  596. gl.Vertex3fv(@star.FCacheCoord.X);
  597. end;
  598. gl.End_;
  599. // restore default AlphaFunc
  600. rci.GLStates.SetGLAlphaFunction(cfGreater, 0);
  601. end;
  602. procedure TGLSkyDomeStars.AddRandomStars(const nb: Integer; const color: TColor;
  603. const limitToTopDome: Boolean = False);
  604. var
  605. i: Integer;
  606. coord: TAffineVector;
  607. star: TGLSkyDomeStar;
  608. begin
  609. for i := 1 to nb do
  610. begin
  611. star := Add;
  612. // pick a point in the half-cube
  613. if limitToTopDome then
  614. coord.Z := Random
  615. else
  616. coord.Z := Random * 2 - 1;
  617. // calculate RA and Dec
  618. star.Dec := ArcSin(coord.Z) * c180divPI;
  619. star.Ra := Random * 360 - 180;
  620. // pick a color
  621. star.Color := color;
  622. // pick a magnitude
  623. star.Magnitude := 3;
  624. end;
  625. end;
  626. procedure TGLSkyDomeStars.AddRandomStars(const nb: Integer; const ColorMin,
  627. ColorMax: TVector3b;
  628. const Magnitude_min, Magnitude_max: Single;
  629. const limitToTopDome: Boolean = False);
  630. function RandomTT(Min, Max: Byte): Byte;
  631. begin
  632. Result := Min + Random(Max - Min);
  633. end;
  634. var
  635. i: Integer;
  636. coord: TAffineVector;
  637. star: TGLSkyDomeStar;
  638. begin
  639. for i := 1 to nb do
  640. begin
  641. star := Add;
  642. // pick a point in the half-cube
  643. if limitToTopDome then
  644. coord.Z := Random
  645. else
  646. coord.Z := Random * 2 - 1;
  647. // calculate RA and Dec
  648. star.Dec := ArcSin(coord.Z) * c180divPI;
  649. star.Ra := Random * 360 - 180;
  650. // pick a color
  651. star.Color := RGB2Color(RandomTT(ColorMin.X, ColorMax.X),
  652. RandomTT(ColorMin.Y, ColorMax.Y),
  653. RandomTT(ColorMin.Z, ColorMax.Z));
  654. // pick a magnitude
  655. star.Magnitude := Magnitude_min + Random * (Magnitude_max - Magnitude_min);
  656. end;
  657. end;
  658. procedure TGLSkyDomeStars.LoadStarsFile(const starsFileName: string);
  659. var
  660. fs: TFileStream;
  661. sr: TGLStarRecord;
  662. colorVector: TColorVector;
  663. begin
  664. fs := TFileStream.Create(starsFileName, fmOpenRead + fmShareDenyWrite);
  665. try
  666. while fs.Position < fs.Size do
  667. begin
  668. fs.Read(sr, SizeOf(sr));
  669. with Add do
  670. begin
  671. RA := sr.RA * 0.01;
  672. DEC := sr.DEC * 0.01;
  673. colorVector := StarRecordColor(sr, 3);
  674. Magnitude := sr.VMagnitude * 0.1;
  675. if sr.VMagnitude > 35 then
  676. Color := ConvertColorVector(colorVector, colorVector.W)
  677. else
  678. Color := ConvertColorVector(colorVector);
  679. end;
  680. end;
  681. finally
  682. fs.Free;
  683. end;
  684. end;
  685. // ------------------
  686. // ------------------ TGLSkyDome ------------------
  687. // ------------------
  688. constructor TGLSkyDome.Create(AOwner: TComponent);
  689. begin
  690. inherited Create(AOwner);
  691. CamInvarianceMode := cimPosition;
  692. ObjectStyle := ObjectStyle + [osDirectDraw, osNoVisibilityCulling];
  693. FBands := TGLSkyDomeBands.Create(Self);
  694. with FBands.Add do
  695. begin
  696. StartAngle := 0;
  697. StartColor.Color := clrWhite;
  698. StopAngle := 15;
  699. StopColor.Color := clrBlue;
  700. end;
  701. with FBands.Add do
  702. begin
  703. StartAngle := 15;
  704. StartColor.Color := clrBlue;
  705. StopAngle := 90;
  706. Stacks := 4;
  707. StopColor.Color := clrNavy;
  708. end;
  709. FStars := TGLSkyDomeStars.Create(Self);
  710. end;
  711. destructor TGLSkyDome.Destroy;
  712. begin
  713. FStars.Free;
  714. FBands.Free;
  715. inherited Destroy;
  716. end;
  717. procedure TGLSkyDome.Assign(Source: TPersistent);
  718. begin
  719. if Source is TGLSkyDome then
  720. begin
  721. FBands.Assign(TGLSkyDome(Source).FBands);
  722. FStars.Assign(TGLSkyDome(Source).FStars);
  723. end;
  724. inherited;
  725. end;
  726. procedure TGLSkyDome.SetBands(const val: TGLSkyDomeBands);
  727. begin
  728. FBands.Assign(val);
  729. StructureChanged;
  730. end;
  731. procedure TGLSkyDome.SetStars(const val: TGLSkyDomeStars);
  732. begin
  733. FStars.Assign(val);
  734. StructureChanged;
  735. end;
  736. procedure TGLSkyDome.SetOptions(const val: TGLSkyDomeOptions);
  737. begin
  738. if val <> FOptions then
  739. begin
  740. FOptions := val;
  741. if sdoTwinkle in FOptions then
  742. ObjectStyle := ObjectStyle + [osDirectDraw]
  743. else
  744. begin
  745. ObjectStyle := ObjectStyle - [osDirectDraw];
  746. DestroyHandle;
  747. end;
  748. StructureChanged;
  749. end;
  750. end;
  751. procedure TGLSkyDome.BuildList(var rci: TGLRenderContextInfo);
  752. var
  753. f: Single;
  754. begin
  755. // setup states
  756. with rci.GLStates do
  757. begin
  758. Disable(stLighting);
  759. Disable(stDepthTest);
  760. Disable(stFog);
  761. Disable(stCullFace);
  762. Disable(stBlend);
  763. DepthWriteMask := False;
  764. PolygonMode := pmFill;
  765. end;
  766. f := rci.rcci.farClippingDistance * 0.90;
  767. gl.Scalef(f, f, f);
  768. Bands.BuildList(rci);
  769. Stars.BuildList(rci, (sdoTwinkle in FOptions));
  770. end;
  771. // ------------------
  772. // ------------------ TGLEarthSkyDome ------------------
  773. // ------------------
  774. constructor TGLEarthSkyDome.Create(AOwner: TComponent);
  775. begin
  776. inherited Create(AOwner);
  777. FMorning:=true;
  778. Bands.Clear;
  779. FSunElevation := 75;
  780. FTurbidity := 15;
  781. FSunZenithColor := TGLColor.CreateInitialized(Self, clrWhite, OnColorChanged);
  782. FSunDawnColor := TGLColor.CreateInitialized(Self, Vectormake(1, 0.5, 0, 0),OnColorChanged);
  783. FHazeColor := TGLColor.CreateInitialized(Self, VectorMake(0.9, 0.95, 1, 0),OnColorChanged);
  784. FSkyColor := TGLColor.CreateInitialized(Self, VectorMake(0.45, 0.6, 0.9, 0),OnColorChanged);
  785. FNightColor := TGLColor.CreateInitialized(Self, clrTransparent,OnColorChanged);
  786. FDeepColor := TGLColor.CreateInitialized(Self, VectorMake(0, 0.2, 0.4, 0));
  787. FStacks := 24;
  788. FSlices := 48;
  789. PreCalculate;
  790. end;
  791. destructor TGLEarthSkyDome.Destroy;
  792. begin
  793. FSunZenithColor.Free;
  794. FSunDawnColor.Free;
  795. FHazeColor.Free;
  796. FSkyColor.Free;
  797. FNightColor.Free;
  798. FDeepColor.Free;
  799. inherited Destroy;
  800. end;
  801. procedure TGLEarthSkyDome.Assign(Source: TPersistent);
  802. begin
  803. if Source is TGLSkyDome then
  804. begin
  805. FSunElevation := TGLEarthSkyDome(Source).SunElevation;
  806. FTurbidity := TGLEarthSkyDome(Source).Turbidity;
  807. FSunZenithColor.Assign(TGLEarthSkyDome(Source).FSunZenithColor);
  808. FSunDawnColor.Assign(TGLEarthSkyDome(Source).FSunDawnColor);
  809. FHazeColor.Assign(TGLEarthSkyDome(Source).FHazeColor);
  810. FSkyColor.Assign(TGLEarthSkyDome(Source).FSkyColor);
  811. FNightColor.Assign(TGLEarthSkyDome(Source).FNightColor);
  812. FSlices := TGLEarthSkyDome(Source).FSlices;
  813. FStacks := TGLEarthSkyDome(Source).FStacks;
  814. PreCalculate;
  815. end;
  816. inherited;
  817. end;
  818. procedure TGLEarthSkyDome.Loaded;
  819. begin
  820. inherited;
  821. PreCalculate;
  822. end;
  823. procedure TGLEarthSkyDome.SetSunElevation(const val: Single);
  824. var
  825. newVal: single;
  826. begin
  827. newval := clampValue(val, -90, 90);
  828. if FSunElevation <> newval then
  829. begin
  830. FSunElevation := newval;
  831. PreCalculate;
  832. end;
  833. end;
  834. procedure TGLEarthSkyDome.SetTurbidity(const val: Single);
  835. begin
  836. FTurbidity := ClampValue(val, 1, 120);
  837. PreCalculate;
  838. end;
  839. procedure TGLEarthSkyDome.SetSunZenithColor(const val: TGLColor);
  840. begin
  841. FSunZenithColor.Assign(val);
  842. PreCalculate;
  843. end;
  844. procedure TGLEarthSkyDome.SetSunDawnColor(const val: TGLColor);
  845. begin
  846. FSunDawnColor.Assign(val);
  847. PreCalculate;
  848. end;
  849. procedure TGLEarthSkyDome.SetHazeColor(const val: TGLColor);
  850. begin
  851. FHazeColor.Assign(val);
  852. PreCalculate;
  853. end;
  854. procedure TGLEarthSkyDome.SetSkyColor(const val: TGLColor);
  855. begin
  856. FSkyColor.Assign(val);
  857. PreCalculate;
  858. end;
  859. procedure TGLEarthSkyDome.SetNightColor(const val: TGLColor);
  860. begin
  861. FNightColor.Assign(val);
  862. PreCalculate;
  863. end;
  864. procedure TGLEarthSkyDome.SetDeepColor(const val: TGLColor);
  865. begin
  866. FDeepColor.Assign(val);
  867. PreCalculate;
  868. end;
  869. procedure TGLEarthSkyDome.SetSlices(const val: Integer);
  870. begin
  871. if val>6 then FSlices:=val else FSlices:=6;
  872. StructureChanged;
  873. end;
  874. procedure TGLEarthSkyDome.SetStacks(const val: Integer);
  875. begin
  876. if val>1 then FStacks:=val else FStacks:=1;
  877. StructureChanged;
  878. end;
  879. procedure TGLEarthSkyDome.BuildList(var rci: TGLRenderContextInfo);
  880. var
  881. f: Single;
  882. begin
  883. // setup states
  884. with rci.GLStates do
  885. begin
  886. CurrentProgram := 0;
  887. Disable(stLighting);
  888. if esoDepthTest in FExtendedOptions then
  889. begin
  890. Enable(stDepthTest);
  891. DepthFunc := cfLEqual;
  892. end
  893. else
  894. Disable(stDepthTest);
  895. Disable(stFog);
  896. Disable(stCullFace);
  897. Disable(stBlend);
  898. Disable(stAlphaTest);
  899. DepthWriteMask := False;
  900. PolygonMode := pmFill;
  901. end;
  902. f := rci.rcci.farClippingDistance * 0.95;
  903. gl.Scalef(f, f, f);
  904. RenderDome;
  905. Bands.BuildList(rci);
  906. Stars.BuildList(rci, (sdoTwinkle in FOptions));
  907. // restore
  908. rci.GLStates.DepthWriteMask := True;
  909. end;
  910. procedure TGLEarthSkyDome.OnColorChanged(Sender: TObject);
  911. begin
  912. PreCalculate;
  913. end;
  914. procedure TGLEarthSkyDome.SetSunAtTime(HH, MM: Single);
  915. const
  916. cHourToElevation1: array[0..23] of Single =
  917. (-45, -67.5, -90, -57.5, -45, -22.5, 0, 11.25, 22.5, 33.7, 45, 56.25, 67.5,
  918. 78.75, 90, 78.75, 67.5, 56.25, 45, 33.7, 22.5, 11.25, 0, -22.5);
  919. cHourToElevation2: array[0..23] of Single =
  920. (-0.375, -0.375, 0.375, 0.375, 0.375, 0.375, 0.1875, 0.1875, 0.1875, 0.1875,
  921. 0.1875, 0.1875, 0.1875, 0.1875, -0.1875, -0.1875, -0.1875, -0.1875, -0.1875,
  922. -0.1875, -0.1875, -0.1875, -0.375, -0.375);
  923. var
  924. ts:Single;
  925. fts:Single;
  926. i:integer;
  927. color:TColor;
  928. begin
  929. HH:=Round(HH);
  930. if HH<0 then HH:=0;
  931. if HH>23 then HH:=23;
  932. if MM<0 then MM:=0;
  933. if MM>=60 then
  934. begin
  935. MM:=0;
  936. HH:=HH+1;
  937. if HH>23 then HH:=0;
  938. end;
  939. FSunElevation := cHourToElevation1[Round(HH)] + cHourToElevation2[Round(HH)]*MM;
  940. ts := DegToRadian(90 - FSunElevation);
  941. // Mix base colors
  942. fts := exp(-6 * (PI / 2 - ts));
  943. VectorLerp(SunZenithColor.Color, SunDawnColor.Color, fts, FCurSunColor);
  944. fts := IntPower(1 - cos(ts - 0.5), 2);
  945. VectorLerp(HazeColor.Color, NightColor.Color, fts, FCurHazeColor);
  946. VectorLerp(SkyColor.Color, NightColor.Color, fts, FCurSkyColor);
  947. // Precalculate Turbidity factors
  948. FCurHazeTurbid := -sqrt(121 - Turbidity) * 2;
  949. FCurSunSkyTurbid := -(121 - Turbidity);
  950. //fade stars if required
  951. if SunElevation>-40 then ts:=PowerInteger(1-(SunElevation+40)/90,11)else ts:=1;
  952. color := RGB2Color(round(ts * 255), round(ts * 255), round(ts * 255));
  953. if esoFadeStarsWithSun in ExtendedOptions then for i:=0 to Stars.Count-1 do stars[i].Color:=color;
  954. if esoRotateOnTwelveHours in ExtendedOptions then // spining around blue orb
  955. begin
  956. if (HH>=14) and (FMorning) then
  957. begin
  958. roll(180);
  959. for i:=0 to Stars.Count-1 do stars[i].RA:=Stars[i].RA+180;
  960. FMorning:=false;
  961. end;
  962. if (HH>=2) and (HH<14) and (not FMorning) then
  963. begin
  964. roll(180);
  965. for i:=0 to Stars.Count-1 do stars[i].RA:=Stars[i].RA+180;
  966. FMorning:=true;
  967. end;
  968. end;
  969. StructureChanged;
  970. end;
  971. procedure TGLEarthSkyDome.PreCalculate;
  972. var
  973. ts: Single;
  974. fts: Single;
  975. i: integer;
  976. color: TColor;
  977. begin
  978. ts := DegToRadian(90 - SunElevation);
  979. // Precompose base colors
  980. fts := exp(-6 * (PI / 2 - ts));
  981. VectorLerp(SunZenithColor.Color, SunDawnColor.Color, fts, FCurSunColor);
  982. fts := PowerInteger(1 - cos(ts - 0.5), 2);
  983. VectorLerp(HazeColor.Color, NightColor.Color, fts, FCurHazeColor);
  984. VectorLerp(SkyColor.Color, NightColor.Color, fts, FCurSkyColor);
  985. // Precalculate Turbidity factors
  986. FCurHazeTurbid := -sqrt(121 - Turbidity) * 2;
  987. FCurSunSkyTurbid := -(121 - Turbidity);
  988. //fade stars if required
  989. if SunElevation>-40 then
  990. ts := PowerInteger(1 - (SunElevation+40) / 90, 11)
  991. else
  992. ts := 1;
  993. color := RGB2Color(round(ts * 255), round(ts * 255), round(ts * 255));
  994. if esoFadeStarsWithSun in ExtendedOptions then
  995. for i := 0 to Stars.Count - 1 do
  996. stars[i].Color := color;
  997. if esoRotateOnTwelveHours in ExtendedOptions then
  998. begin
  999. if SunElevation = 90 then
  1000. begin
  1001. roll(180);
  1002. for i := 0 to Stars.Count - 1 do
  1003. stars[i].RA := Stars[i].RA + 180;
  1004. end
  1005. else if SunElevation = -90 then
  1006. begin
  1007. roll(180);
  1008. for i := 0 to Stars.Count - 1 do
  1009. stars[i].RA := Stars[i].RA + 180;
  1010. end;
  1011. end;
  1012. StructureChanged;
  1013. end;
  1014. function TGLEarthSkyDome.CalculateColor(const theta, cosGamma: Single):
  1015. TColorVector;
  1016. var
  1017. t: Single;
  1018. begin
  1019. t := PI / 2 - theta;
  1020. // mix to get haze/sky
  1021. VectorLerp(FCurSkyColor, FCurHazeColor, ClampValue(exp(FCurHazeTurbid * t), 0,
  1022. 1), Result);
  1023. // then mix sky with sun
  1024. VectorLerp(Result, FCurSunColor, ClampValue(exp(FCurSunSkyTurbid * cosGamma *
  1025. (1 + t)) * 1.1, 0, 1), Result);
  1026. end;
  1027. procedure TGLEarthSkyDome.RenderDome;
  1028. var
  1029. ts: Single;
  1030. steps: Integer;
  1031. sunPos: TAffineVector;
  1032. sinTable, cosTable: PFloatArray;
  1033. // coordinates system note: X is forward, Y is left and Z is up
  1034. // always rendered as sphere of radius 1
  1035. function CalculateCosGamma(const p: TVector): Single;
  1036. begin
  1037. Result := 1 - VectorAngleCosine(PAffineVector(@p)^, sunPos);
  1038. end;
  1039. procedure RenderDeepBand(stop: Single);
  1040. var
  1041. i: Integer;
  1042. r, thetaStart: Single;
  1043. vertex1: TVector;
  1044. color: TColorVector;
  1045. begin
  1046. r := 0;
  1047. vertex1.W := 1;
  1048. // triangle fan with south pole
  1049. gl.Begin_(GL_TRIANGLE_FAN);
  1050. color := CalculateColor(0, CalculateCosGamma(ZHmgPoint));
  1051. gl.Color4fv(DeepColor.AsAddress);
  1052. gl.Vertex3f(0, 0, -1);
  1053. SinCosine(DegToRadian(stop), vertex1.Z, r);
  1054. thetaStart := DegToRadian(90 - stop);
  1055. for i := 0 to steps - 1 do
  1056. begin
  1057. vertex1.X := r * cosTable[i];
  1058. vertex1.Y := r * sinTable[i];
  1059. color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1060. gl.Color4fv(@color);
  1061. gl.Vertex4fv(@vertex1);
  1062. end;
  1063. gl.End_;
  1064. end;
  1065. procedure RenderBand(start, stop: Single);
  1066. var
  1067. i: Integer;
  1068. r, r2, thetaStart, thetaStop: Single;
  1069. vertex1, vertex2: TVector;
  1070. color: TColorVector;
  1071. begin
  1072. vertex1.W := 1;
  1073. if stop = 90 then
  1074. begin
  1075. // triangle fan with north pole
  1076. gl.Begin_(GL_TRIANGLE_FAN);
  1077. color := CalculateColor(0, CalculateCosGamma(ZHmgPoint));
  1078. gl.Color4fv(@color);
  1079. gl.Vertex4fv(@ZHmgPoint);
  1080. SinCosine(DegToRadian(start), vertex1.Z, r);
  1081. thetaStart := DegToRadian(90 - start);
  1082. for i := 0 to steps - 1 do
  1083. begin
  1084. vertex1.X := r * cosTable[i];
  1085. vertex1.Y := r * sinTable[i];
  1086. color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1087. gl.Color4fv(@color);
  1088. gl.Vertex4fv(@vertex1);
  1089. end;
  1090. gl.End_;
  1091. end
  1092. else
  1093. begin
  1094. vertex2.W := 1;
  1095. // triangle strip
  1096. gl.Begin_(GL_TRIANGLE_STRIP);
  1097. SinCosine(DegToRadian(start), vertex1.Z, r);
  1098. SinCosine(DegToRadian(stop), vertex2.Z, r2);
  1099. thetaStart := DegToRadian(90 - start);
  1100. thetaStop := DegToRadian(90 - stop);
  1101. for i := 0 to steps - 1 do
  1102. begin
  1103. vertex1.X := r * cosTable[i];
  1104. vertex1.Y := r * sinTable[i];
  1105. color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1106. gl.Color4fv(@color);
  1107. gl.Vertex4fv(@vertex1);
  1108. vertex2.X := r2 * cosTable[i];
  1109. vertex2.Y := r2 * sinTable[i];
  1110. color := CalculateColor(thetaStop, CalculateCosGamma(vertex2));
  1111. gl.Color4fv(@color);
  1112. gl.Vertex4fv(@vertex2);
  1113. end;
  1114. gl.End_;
  1115. end;
  1116. end;
  1117. var
  1118. n, i, sdiv2: Integer;
  1119. t, t2, p, fs: Single;
  1120. begin
  1121. ts := DegToRadian(90 - SunElevation);
  1122. SetVector(sunPos, sin(ts), 0, cos(ts));
  1123. // prepare sin/cos LUT, with a higher sampling around 0Ѝ
  1124. n := Slices div 2;
  1125. steps := 2 * n + 1;
  1126. GetMem(sinTable, steps * SizeOf(Single));
  1127. GetMem(cosTable, steps * SizeOf(Single));
  1128. for i := 1 to n do
  1129. begin
  1130. p := (1 - Sqrt(Cos((i / n) * cPIdiv2))) * PI;
  1131. SinCosine(p, sinTable[n + i], cosTable[n + i]);
  1132. sinTable[n - i] := -sinTable[n + i];
  1133. cosTable[n - i] := cosTable[n + i];
  1134. end;
  1135. // these are defined by hand for precision issue: the dome must wrap exactly
  1136. sinTable[n] := 0;
  1137. cosTable[n] := 1;
  1138. sinTable[0] := 0;
  1139. cosTable[0] := -1;
  1140. sinTable[steps - 1] := 0;
  1141. cosTable[steps - 1] := -1;
  1142. fs := SunElevation / 90;
  1143. // start render
  1144. t := 0;
  1145. sdiv2 := Stacks div 2;
  1146. for n := 0 to Stacks - 1 do
  1147. begin
  1148. if fs > 0 then
  1149. begin
  1150. if n < sdiv2 then
  1151. t2 := fs - fs * Sqr((sdiv2 - n) / sdiv2)
  1152. else
  1153. t2 := fs + Sqr((n - sdiv2) / (sdiv2 - 1)) * (1 - fs);
  1154. end
  1155. else
  1156. t2 := (n + 1) / Stacks;
  1157. RenderBand(Lerp(1, 90, t), Lerp(1, 90, t2));
  1158. t := t2;
  1159. end;
  1160. RenderDeepBand(1);
  1161. FreeMem(sinTable);
  1162. FreeMem(cosTable);
  1163. end;
  1164. //-------------------------------------------------------------
  1165. initialization
  1166. //-------------------------------------------------------------
  1167. RegisterClasses([TGLSkyDome, TGLEarthSkyDome]);
  1168. end.