GLS.SkyDome.pas 48 KB

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