GLS.SkyDome.pas 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031
  1. //
  2. // The graphics engine GLScene https://github.com/glscene
  3. //
  4. unit GLS.SkyDome;
  5. (* Skydome classes with celestial grids and routine functions *)
  6. interface
  7. {$I GLScene.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. System.SysUtils,
  12. System.UITypes,
  13. System.Math,
  14. Vcl.Graphics,
  15. GLScene.OpenGLTokens,
  16. GLScene.VectorTypes,
  17. GLScene.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; // Right Ascension, x100 builtin factor, degrees
  26. DEC : SmallInt; // Declination, x100 builtin factor, degrees
  27. BVColorIndex : Byte; // ColorIndex, x100 builtin factor
  28. VMagnitude : Byte; // Magnitude, 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); override;
  67. published
  68. property MaterialLibrary: TGLMaterialLibrary read FMaterialLibrary write SetMaterialLibrary;
  69. property MatNameTop: TGLLibMaterialName read FMatNameTop write SetMatNameTop;
  70. property MatNameBottom: TGLLibMaterialName read FMatNameBottom write SetMatNameBottom;
  71. property MatNameLeft: TGLLibMaterialName read FMatNameLeft write SetMatNameLeft;
  72. property MatNameRight: TGLLibMaterialName read FMatNameRight write SetMatNameRight;
  73. property MatNameFront: TGLLibMaterialName read FMatNameFront write SetMatNameFront;
  74. property MatNameBack: TGLLibMaterialName read FMatNameBack write SetMatNameBack;
  75. property MatNameClouds: TGLLibMaterialName read FMatNameClouds write SetMatNameClouds;
  76. property CloudsPlaneOffset: Single read FCloudsPlaneOffset write SetCloudsPlaneOffset;
  77. property CloudsPlaneSize: Single read FCloudsPlaneSize write SetCloudsPlaneSize;
  78. property Style: TGLSkyBoxStyle read FStyle write FStyle default sbsFull;
  79. end;
  80. //--------------------- SkyDome classes -----------------------------
  81. TGLSkyDomeBand = class(TCollectionItem)
  82. private
  83. FStartAngle: Single;
  84. FStopAngle: Single;
  85. FStartColor: TGLColor;
  86. FStopColor: TGLColor;
  87. FSlices: Integer;
  88. FStacks: Integer;
  89. protected
  90. function GetDisplayName: string; override;
  91. procedure SetStartAngle(const val: Single);
  92. procedure SetStartColor(const val: TGLColor);
  93. procedure SetStopAngle(const val: Single);
  94. procedure SetStopColor(const val: TGLColor);
  95. procedure SetSlices(const val: Integer);
  96. procedure SetStacks(const val: Integer);
  97. procedure OnColorChange(sender: TObject);
  98. public
  99. constructor Create(Collection: TCollection); override;
  100. destructor Destroy; override;
  101. procedure Assign(Source: TPersistent); override;
  102. procedure BuildList(var rci: TGLRenderContextInfo);
  103. published
  104. property StartAngle: Single read FStartAngle write SetStartAngle;
  105. property StartColor: TGLColor read FStartColor write SetStartColor;
  106. property StopAngle: Single read FStopAngle write SetStopAngle;
  107. property StopColor: TGLColor read FStopColor write SetStopColor;
  108. property Slices: Integer read FSlices write SetSlices default 12;
  109. property Stacks: Integer read FStacks write SetStacks default 1;
  110. end;
  111. TGLSkyDomeBands = class(TCollection)
  112. protected
  113. owner: TComponent;
  114. function GetOwner: TPersistent; override;
  115. procedure SetItems(index: Integer; const val: TGLSkyDomeBand);
  116. function GetItems(index: Integer): TGLSkyDomeBand;
  117. public
  118. constructor Create(AOwner: TComponent);
  119. function Add: TGLSkyDomeBand;
  120. function FindItemID(ID: Integer): TGLSkyDomeBand;
  121. property Items[index: Integer]: TGLSkyDomeBand read GetItems write SetItems; default;
  122. procedure NotifyChange;
  123. procedure BuildList(var rci: TGLRenderContextInfo);
  124. end;
  125. TGLSkyDomeStar = class(TCollectionItem)
  126. private
  127. FRA, FDec: Single;
  128. FMagnitude: Single;
  129. FColor: TColor;
  130. FCacheCoord: TAffineVector; // cached cartesian coordinates
  131. protected
  132. function GetDisplayName: string; override;
  133. public
  134. constructor Create(Collection: TCollection); override;
  135. destructor Destroy; override;
  136. procedure Assign(Source: TPersistent); override;
  137. published
  138. // Right Ascension, in degrees.
  139. property RA: Single read FRA write FRA;
  140. // Declination, in degrees.
  141. property DEC: Single read FDec write FDec;
  142. // Absolute magnitude.
  143. property Magnitude: Single read FMagnitude write FMagnitude;
  144. // Color of the star.
  145. property Color: TColor read FColor write FColor;
  146. end;
  147. TGLSkyDomeStars = class(TCollection)
  148. protected
  149. owner: TComponent;
  150. function GetOwner: TPersistent; override;
  151. procedure SetItems(index: Integer; const val: TGLSkyDomeStar);
  152. function GetItems(index: Integer): TGLSkyDomeStar;
  153. procedure PrecomputeCartesianCoordinates;
  154. public
  155. constructor Create(AOwner: TComponent);
  156. function Add: TGLSkyDomeStar;
  157. function FindItemID(ID: Integer): TGLSkyDomeStar;
  158. property Items[index: Integer]: TGLSkyDomeStar read GetItems write SetItems; default;
  159. procedure BuildList(var rci: TGLRenderContextInfo; twinkle: Boolean);
  160. (* Adds nb random stars of the given color.
  161. Stars are homogenously scattered on the complete sphere, not only the band defined or visible dome. *)
  162. procedure AddRandomStars(const nb: Integer; const color: TColor; const LimitToTopDome: Boolean = False); overload;
  163. procedure AddRandomStars(const nb: Integer; const ColorMin, ColorMax:TVector3b;
  164. const Magnitude_min, Magnitude_max: Single;
  165. 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 = (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 a 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. (* Coordinates system note: X is forward, Y is left and Z is up
  236. always rendered as sphere of radius 1 *)
  237. procedure RenderDome;
  238. function CalculateColor(const theta, cosGamma: Single): TGLColorVector;
  239. public
  240. constructor Create(AOwner: TComponent); override;
  241. destructor Destroy; override;
  242. procedure Assign(Source: TPersistent); override;
  243. procedure BuildList(var rci: TGLRenderContextInfo); override;
  244. procedure SetSunAtTime(HH, MM: Single);
  245. published
  246. // Elevation of the sun, measured in degrees
  247. property SunElevation: Single read FSunElevation write SetSunElevation;
  248. // Expresses the purity of air. Value range is from 1 (pure atmosphere) to 120 (very nebulous)
  249. property Turbidity: Single read FTurbidity write SetTurbidity;
  250. property SunZenithColor: TGLColor read FSunZenithColor write SetSunZenithColor;
  251. property SunDawnColor: TGLColor read FSunDawnColor write SetSunDawnColor;
  252. property HazeColor: TGLColor read FHazeColor write SetHazeColor;
  253. property SkyColor: TGLColor read FSkyColor write SetSkyColor;
  254. property NightColor: TGLColor read FNightColor write SetNightColor;
  255. property DeepColor: TGLColor read FDeepColor write SetDeepColor;
  256. property ExtendedOptions: TGLEarthSkydomeOptions read FExtendedOptions write FExtendedOptions;
  257. property Slices: Integer read FSlices write SetSlices default 24;
  258. property Stacks: Integer read FStacks write SetStacks default 48;
  259. end;
  260. type
  261. TGLStarSysData = record
  262. NbStar: byte; // Only 1 allowed so far
  263. NbPlanet: byte;
  264. NbAsteroid: byte;
  265. NbComet: byte;
  266. NbDebris: byte;
  267. end;
  268. TGLStarData = record // Also equal to material texture
  269. (* ONLY .jpg; Expected: .bmp .tif .tga .png ignored *)
  270. StarName: String[255];
  271. Radius: Double;
  272. ObjectRotation: Double;
  273. AxisTilt: Double;
  274. nbS3ds: byte;
  275. DocIndex: byte;
  276. StarSysScale: Double;
  277. StarDistanceScale: Double;
  278. end;
  279. TGLPlanetData = record
  280. // Planet.jpg .. Planet_bump.jpg
  281. Name: String[255];
  282. Radius: Double;
  283. ObjectRotation: Double;
  284. AxisTilt: Double;
  285. nbRings: byte;
  286. nbMoons: byte;
  287. nbS3ds: byte;
  288. DocIndex: byte;
  289. Albedo, OrbitRotation: Double;
  290. aDistance, aDistanceVar: Double; // aConstEdit aVarEdit
  291. Inclination, InclinationVar: Double; // iConstEdit iVarEdit
  292. Eccentricity, EVar, EMax: Double; // eConstEdit eVarEdit EMaxEdit
  293. nLongitude, nLongitudeVar: Double; // NConstEdit NVarEdit
  294. wPerihelion, wPerihelionVar: Double; // wConstEdit wVarEdit
  295. mAnomaly, mAnomalyVar: Double; // MConstEdit MVarEdit
  296. Mass, Density: Double;
  297. Atmosphere, VelocityType: byte;
  298. (* Which way does the wind blow Given a direction
  299. 0 to 100 or 1 to 100 is that 100 or 101 *)
  300. Velocity, VelocityDir: Double;
  301. end;
  302. // 3ds files and DebrisAsteroid too
  303. TGLMoonRingData = record
  304. Name: String[255];
  305. Radius: Double;
  306. ObjectRotation: Double;
  307. AxisTilt: Double;
  308. S3dsTex: Boolean;
  309. DocIndex: byte;
  310. Mass, Density: Double;
  311. RCDType, RCDCount: Integer;
  312. RCDXYSize, RCDZSize, RCDPosition: Double;
  313. Albedo, OrbitRotation: Double;
  314. aDistance, aDistanceVar: Double;
  315. Inclination, InclinationVar: Double;
  316. Eccentricity, EVar, EMax: Double;
  317. nLongitude, nLongitudeVar: Double;
  318. wPerihelion, wPerihelionVar: Double;
  319. mAnomaly, mAnomalyVar: Double;
  320. Atmosphere, VelocityType: byte;
  321. Velocity, VelocityDir: Double;
  322. end;
  323. // Asteroid Comet spheres..NOT DebrisAsteroid
  324. TGLAsteroidData = record
  325. Name: String[255];
  326. Radius: Double;
  327. ObjectRotation: Double;
  328. AxisTilt: Double;
  329. nbS3ds: byte;
  330. DocIndex: byte;
  331. RCDType, RCDCount: Integer;
  332. RCDXYSize, RCDZSize, RCDPosition: Double;
  333. Albedo, OrbitRotation: Double;
  334. aDistance, aDistanceVar: Double;
  335. Inclination, InclinationVar: Double;
  336. Eccentricity, EVar, EMax: Double;
  337. nLongitude, nLongitudeVar: Double;
  338. wPerihelion, wPerihelionVar: Double;
  339. mAnomaly, mAnomalyVar: Double;
  340. Mass, Density: Double;
  341. Atmosphere, VelocityType: byte;
  342. Velocity, VelocityDir: Double;
  343. end;
  344. // For recomputation of TOrbitalElementsData
  345. TGLOrbitalElements = record
  346. N: Double; // longitude of the ascending node
  347. i: Double; // inclination to the ecliptic (plane of the Earth's orbit)
  348. w: Double; // argument of perihelion
  349. a: Double; // semi-major axis, or mean distance from Sun
  350. e: Double; // eccentricity (0=circle, 0-1=ellipse, 1=parabola)
  351. M: Double; // mean anomaly (0 at perihelion; increases uniformly with time)
  352. end;
  353. TOrbitalElementsData = record
  354. NConst, NVar: Double; // longitude of the ascending node
  355. iConst, iVar: Double; // inclination to the ecliptic (plane of the Earth's orbit)
  356. wConst, wVar: Double; // argument of perihelion
  357. aConst, aVar: Double; // semi-major axis, or mean distance from Sun
  358. eConst, eVar: Double; // eccentricity (0=circle, 0-1=ellipse, 1=parabola)
  359. MConst, MVar: Double; // mean anomaly (0 at perihelion; increases uniformly with time)
  360. end;
  361. const
  362. // geocentric sun elements
  363. cSunOrbitalElements: TOrbitalElementsData = (NConst: 0.0; NVar: 0.0;
  364. iConst: 0.0; iVar: 0.0; wConst: 282.9404; wVar: 4.70935E-5;
  365. aConst: 1.000000; aVar: 0.0; // (AU)
  366. eConst: 0.016709; eVar: - 1.151E-9; MConst: 356.0470; MVar: 0.9856002585);
  367. // geocentric moon elements
  368. cMoonOrbitalElements: TOrbitalElementsData = (NConst: 125.1228;
  369. NVar: - 0.0529538083; iConst: 5.1454; iVar: 0.0; wConst: 318.0634;
  370. wVar: 0.1643573223; aConst: 60.2666; aVar: 0.0; // (Earth radii)
  371. eConst: 0.054900; eVar: 0.0; MConst: 115.3654; MVar: 13.0649929509);
  372. // heliocentric mercury elements
  373. cMercuryOrbitalElements: TOrbitalElementsData = (NConst: 48.3313;
  374. NVar: 3.24587E-5; iConst: 7.0047; iVar: 5.00E-8; wConst: 29.1241;
  375. wVar: 1.01444E-5; aConst: 0.387098; aVar: 0.0; // (AU)
  376. eConst: 0.205635; eVar: 5.59E-10; MConst: 168.6562; MVar: 4.0923344368);
  377. // heliocentric venus elements
  378. cVenusOrbitalElements: TOrbitalElementsData = (NConst: 76.6799;
  379. NVar: 2.46590E-5; iConst: 3.3946; iVar: 2.75E-8; wConst: 54.8910;
  380. wVar: 1.38374E-5; aConst: 0.723330; aVar: 0.0; // (AU)
  381. eConst: 0.006773; eVar: - 1.302E-9; MConst: 48.0052; MVar: 1.6021302244);
  382. // heliocentric mars elements
  383. cMarsOrbitalElements: TOrbitalElementsData = (NConst: 49.5574;
  384. NVar: 2.11081E-5; iConst: 1.8497; iVar: - 1.78E-8; wConst: 286.5016;
  385. wVar: 2.92961E-5; aConst: 1.523688; aVar: 0.0; // (AU)
  386. eConst: 0.093405; eVar: 2.516E-9; MConst: 18.6021; MVar: 0.5240207766);
  387. // heliocentric jupiter elements
  388. cJupiterOrbitalElements: TOrbitalElementsData = (NConst: 100.4542;
  389. NVar: 2.76854E-5; iConst: 1.3030; iVar: - 1.557E-7; wConst: 273.8777;
  390. wVar: 1.64505E-5; aConst: 5.20256; aVar: 0.0; // (AU)
  391. eConst: 0.048498; eVar: 4.469E-9; MConst: 19.8950; MVar: 0.0830853001);
  392. // heliocentric saturn elements
  393. cSaturnOrbitalElements: TOrbitalElementsData = (NConst: 113.6634;
  394. NVar: 2.38980E-5; iConst: 2.4886; iVar: - 1.081E-7; wConst: 339.3939;
  395. wVar: 2.97661E-5; aConst: 9.55475; aVar: 0.0; // (AU)
  396. eConst: 0.055546; eVar: - 9.499E-9; MConst: 316.9670; MVar: 0.0334442282);
  397. // heliocentric uranus elements
  398. cUranusOrbitalElements: TOrbitalElementsData = (NConst: 74.0005;
  399. NVar: 1.3978E-5; iConst: 0.7733; iVar: 1.9E-8; wConst: 96.6612;
  400. wVar: 3.0565E-5; aConst: 19.18171; aVar: - 1.55E-8; // (AU)
  401. eConst: 0.047318; eVar: 7.45E-9; MConst: 142.5905; MVar: 0.011725806);
  402. // heliocentric neptune elements
  403. cNeptuneOrbitalElements: TOrbitalElementsData = (NConst: 131.7806;
  404. NVar: 3.0173E-5; iConst: 1.7700; iVar: - 2.55E-7; wConst: 272.8461;
  405. wVar: - 6.027E-6; aConst: 30.05826; aVar: 3.313E-8; // (AU)
  406. eConst: 0.008606; eVar: 2.15E-9; MConst: 260.2471; MVar: 0.005995147);
  407. // heliocentric pluto elements
  408. cPlutoOrbitalElements: TOrbitalElementsData = (NConst: 162.7806;
  409. NVar: 3.0173E-5; iConst: 1.7700; iVar: - 2.55E-7; wConst: 272.8461;
  410. wVar: - 6.027E-6; aConst: 30.05826; aVar: 3.313E-8; // (AU)
  411. eConst: 0.008606; EVar: 2.15E-9; MConst: 260.2471; MVar: 0.005995147);
  412. cAUToKilometers = 149.6E6; // astronomical units to kilometers
  413. cEarthRadius = 6371; // average earth radius in kilometers
  414. // Converts a TDateTime (GMT+0) into the Julian day used for computations.
  415. function GMTDateTimeToJulianDay(const dt: TDateTime): Double;
  416. // Compute orbital elements for given Julian day.
  417. function ComputeOrbitalElements(const oeData: TOrbitalElementsData;
  418. const d: Double): TGLOrbitalElements;
  419. // Compute the planet position for given Julian day (in AU).
  420. function ComputePlanetPosition(const orbitalElements: TGLOrbitalElements)
  421. : TAffineVector; overload;
  422. function ComputePlanetPosition(const orbitalElementsData: TOrbitalElementsData;
  423. const d: Double): TAffineVector; overload;
  424. // Computes position on the unit sphere of a star record (Z=up)
  425. function StarRecordPositionZUp(const starRecord: TGLStarRecord): TAffineVector;
  426. // Computes position on the unit sphere of a star record (Y=up)
  427. function StarRecordPositionYUp(const starRecord: TGLStarRecord): TAffineVector;
  428. // Computes position on the unit sphere of a star using Longitude and Latitude
  429. function LonLatToPos(Lon, Lat: Single): TAffineVector;
  430. // Computes star color from BV index (RGB) and magnitude (alpha)
  431. function StarRecordColor(const starRecord: TGLStarRecord; bias: Single): TVector4f;
  432. //----------------------------------------------------------------------
  433. implementation
  434. //----------------------------------------------------------------------
  435. uses
  436. GLS.Context,
  437. GLS.State;
  438. //--------------------------- Functions --------------------------------
  439. function GMTDateTimeToJulianDay(const dt: TDateTime): Double;
  440. begin
  441. Result := dt - EncodeDate(2000, 1, 1);
  442. end;
  443. //--------------------------------------------------------------------------------
  444. function ComputeOrbitalElements(const oeData: TOrbitalElementsData;
  445. const d: Double): TGLOrbitalElements;
  446. begin
  447. with Result, oeData do
  448. begin
  449. N := NConst + NVar * d;
  450. i := iConst + iVar * d;
  451. w := wConst + wVar * d;
  452. a := aConst + aVar * d;
  453. e := eConst + eVar * d;
  454. M := MConst + MVar * d;
  455. end;
  456. end;
  457. //--------------------------------------------------------------------------------
  458. function ComputePlanetPosition(const orbitalElements: TGLOrbitalElements)
  459. : TAffineVector;
  460. var
  461. eccentricAnomaly, eA0: Double;
  462. sm, cm, se, ce, si, ci, cn, sn, cvw, svw: Double;
  463. xv, yv, v, r: Double;
  464. nn: Integer; // numerical instability bailout
  465. begin
  466. with orbitalElements do
  467. begin
  468. // E = M + e*(180/pi) * sin(M) * ( 1.0 + e * cos(M) )
  469. SinCos(M * cPIdiv180, sm, cm);
  470. eccentricAnomaly := M + e * c180divPI * sm * (1.0 + e * cm);
  471. nn := 0;
  472. repeat
  473. eA0 := eccentricAnomaly;
  474. // E1 = E0 - ( E0 - e*(180/pi) * sin(E0) - M ) / ( 1 - e * cos(E0) )
  475. SinCos(eA0 * cPIdiv180, se, ce);
  476. eccentricAnomaly := eA0 - (eA0 - e * c180divPI * se - M) / (1 - e * ce);
  477. Inc(nn);
  478. until (nn > 10) or (Abs(eccentricAnomaly - eA0) < 1E-4);
  479. SinCos(eccentricAnomaly * cPIdiv180, se, ce);
  480. xv := a * (ce - e);
  481. yv := a * (Sqrt(1.0 - e * e) * se);
  482. v := ArcTan2(yv, xv) * c180divPI;
  483. r := Sqrt(xv * xv + yv * yv);
  484. SinCos(i * cPIdiv180, si, ci);
  485. SinCos(N * cPIdiv180, sn, cn);
  486. SinCos((v + w) * cPIdiv180, svw, cvw);
  487. end;
  488. // xh = r * ( cos(N) * cos(v+w) - sin(N) * sin(v+w) * cos(i) )
  489. Result.X := r * (cn * cvw - sn * svw * ci);
  490. // yh = r * ( sin(N) * cos(v+w) + cos(N) * sin(v+w) * cos(i) )
  491. Result.Y := r * (sn * cvw + cn * svw * ci);
  492. // zh = r * ( sin(v+w) * sin(i) )
  493. Result.Z := r * (svw * si);
  494. end;
  495. //--------------------------------------------------------------------------------
  496. function ComputePlanetPosition(const orbitalElementsData: TOrbitalElementsData;
  497. const d: Double): TAffineVector;
  498. var
  499. oe: TGLOrbitalElements;
  500. begin
  501. oe := ComputeOrbitalElements(orbitalElementsData, d);
  502. Result := ComputePlanetPosition(oe);
  503. end;
  504. //----------------------------------------------------------------------
  505. function StarRecordPositionYUp(const starRecord: TGLStarRecord): TAffineVector;
  506. var
  507. f: Single;
  508. begin
  509. SinCosine(starRecord.DEC * (0.01 * PI / 180), Result.Y, f);
  510. SinCosine(starRecord.RA * (0.01 * PI / 180), f, Result.X, Result.Z);
  511. end;
  512. //----------------------------------------------------------------------
  513. function StarRecordPositionZUp(const starRecord: TGLStarRecord): TAffineVector;
  514. var
  515. f: Single;
  516. begin
  517. SinCosine(starRecord.DEC * (0.01 * PI / 180), Result.Z, f);
  518. SinCosine(starRecord.RA * (0.01 * PI / 180), f, Result.X, Result.Y);
  519. end;
  520. //----------------------------------------------------------------------
  521. function LonLatToPos(Lon, Lat: Single): TAffineVector;
  522. var
  523. f: Single;
  524. begin
  525. SinCosine(Lat * (PI / 180), Result.Y, f);
  526. SinCosine(Lon * (360 / 24 * PI / 180), f, Result.X, Result.Z);
  527. end;
  528. //----------------------------------------------------------------------
  529. function StarRecordColor(const starRecord: TGLStarRecord; bias: Single): TVector4f;
  530. const
  531. // very *rough* approximation
  532. cBVm035: TVector4f = (X: 0.7; Y: 0.8; Z: 1.0; W: 1);
  533. cBV015: TVector4f = (X: 1.0; Y: 1.0; Z: 1.0; W: 1);
  534. cBV060: TVector4f = (X: 1.0; Y: 1.0; Z: 0.7; W: 1);
  535. cBV135: TVector4f = (X: 1.0; Y: 0.8; Z: 0.7; W: 1);
  536. var
  537. bvIndex100: Integer;
  538. begin
  539. bvIndex100 := starRecord.BVColorIndex - 50;
  540. // compute RGB color for B&V index
  541. if bvIndex100 < -035 then
  542. Result := cBVm035
  543. else if bvIndex100 < 015 then
  544. VectorLerp(cBVm035, cBV015, (bvIndex100 + 035) * (1 / (015 + 035)), Result)
  545. else if bvIndex100 < 060 then
  546. VectorLerp(cBV015, cBV060, (bvIndex100 - 015) * (1 / (060 - 015)), Result)
  547. else if bvIndex100 < 135 then
  548. VectorLerp(cBV060, cBV135, (bvIndex100 - 060) * (1 / (135 - 060)), Result)
  549. else
  550. Result := cBV135;
  551. // compute transparency for VMag
  552. // the actual factor is 2.512, and not used here
  553. Result.W := PowerSingle(1.2, -(starRecord.VMagnitude * 0.1 - bias));
  554. end;
  555. // ------------------
  556. // ------------------ TGLSkyBox ------------------
  557. // ------------------
  558. constructor TGLSkyBox.Create(AOwner: TComponent);
  559. begin
  560. inherited Create(AOwner);
  561. CamInvarianceMode := cimPosition;
  562. ObjectStyle := ObjectStyle + [osDirectDraw, osNoVisibilityCulling];
  563. FCloudsPlaneOffset := 0.2;
  564. // this should be set far enough to avoid near plane clipping
  565. FCloudsPlaneSize := 32;
  566. // the bigger, the more this extends the clouds cap to the horizon
  567. end;
  568. destructor TGLSkyBox.Destroy;
  569. begin
  570. inherited;
  571. end;
  572. function TGLSkyBox.GetMaterialLibrary: TGLAbstractMaterialLibrary;
  573. begin
  574. Result := FMaterialLibrary;
  575. end;
  576. procedure TGLSkyBox.Notification(AComponent: TComponent; Operation: TOperation);
  577. begin
  578. if (Operation = opRemove) and (AComponent = FMaterialLibrary) then
  579. MaterialLibrary := nil;
  580. inherited;
  581. end;
  582. procedure TGLSkyBox.DoRender(var ARci: TGLRenderContextInfo; ARenderSelf,
  583. ARenderChildren: Boolean);
  584. begin
  585. // We want children of the sky box to appear far away too
  586. // (note: simply not writing to depth buffer may not make this not work,
  587. // child objects may need the depth buffer to render themselves properly,
  588. // this may require depth buffer cleared after that. - DanB)
  589. Arci.GLStates.DepthWriteMask := False;
  590. Arci.ignoreDepthRequests := true;
  591. inherited;
  592. Arci.ignoreDepthRequests := False;
  593. end;
  594. procedure TGLSkyBox.BuildList(var ARci: TGLRenderContextInfo);
  595. var
  596. f, cps, cof1: Single;
  597. oldStates: TGLStates;
  598. libMat: TGLLibMaterial;
  599. begin
  600. if FMaterialLibrary = nil then
  601. Exit;
  602. with ARci.GLStates do
  603. begin
  604. oldStates := States;
  605. Disable(stDepthTest);
  606. Disable(stLighting);
  607. Disable(stFog);
  608. end;
  609. gl.PushMatrix;
  610. f := ARci.rcci.farClippingDistance * 0.5;
  611. gl.Scalef(f, f, f);
  612. try
  613. case Style of
  614. sbsFull: ;
  615. sbsTopHalf, sbsTopHalfClamped:
  616. begin
  617. gl.Translatef(0, 0.5, 0);
  618. gl.Scalef(1, 0.5, 1);
  619. end;
  620. sbsBottomHalf:
  621. begin
  622. gl.Translatef(0, -0.5, 0);
  623. gl.Scalef(1, 0.5, 1);
  624. end;
  625. sbTopTwoThirds:
  626. begin
  627. gl.Translatef(0, 1 / 3, 0);
  628. gl.Scalef(1, 2 / 3, 1);
  629. end;
  630. end;
  631. // FRONT
  632. libMat := MaterialLibrary.LibMaterialByName(FMatNameFront);
  633. if libMat <> nil then
  634. begin
  635. libMat.Apply(ARci);
  636. repeat
  637. gl.Begin_(GL_QUADS);
  638. xgl.TexCoord2f(0.002, 0.998);
  639. gl.Vertex3f(-1, 1, -1);
  640. xgl.TexCoord2f(0.002, 0.002);
  641. gl.Vertex3f(-1, -1, -1);
  642. xgl.TexCoord2f(0.998, 0.002);
  643. gl.Vertex3f(1, -1, -1);
  644. xgl.TexCoord2f(0.998, 0.998);
  645. gl.Vertex3f(1, 1, -1);
  646. if Style = sbsTopHalfClamped then
  647. begin
  648. xgl.TexCoord2f(0.002, 0.002);
  649. gl.Vertex3f(-1, -1, -1);
  650. xgl.TexCoord2f(0.002, 0.002);
  651. gl.Vertex3f(-1, -3, -1);
  652. xgl.TexCoord2f(0.998, 0.002);
  653. gl.Vertex3f(1, -3, -1);
  654. xgl.TexCoord2f(0.998, 0.002);
  655. gl.Vertex3f(1, -1, -1);
  656. end;
  657. gl.End_;
  658. until not libMat.UnApply(ARci);
  659. end;
  660. // BACK
  661. libMat := MaterialLibrary.LibMaterialByName(FMatNameBack);
  662. if libMat <> nil then
  663. begin
  664. libMat.Apply(ARci);
  665. repeat
  666. gl.Begin_(GL_QUADS);
  667. xgl.TexCoord2f(0.002, 0.998);
  668. gl.Vertex3f(1, 1, 1);
  669. xgl.TexCoord2f(0.002, 0.002);
  670. gl.Vertex3f(1, -1, 1);
  671. xgl.TexCoord2f(0.998, 0.002);
  672. gl.Vertex3f(-1, -1, 1);
  673. xgl.TexCoord2f(0.998, 0.998);
  674. gl.Vertex3f(-1, 1, 1);
  675. if Style = sbsTopHalfClamped then
  676. begin
  677. xgl.TexCoord2f(0.002, 0.002);
  678. gl.Vertex3f(1, -1, 1);
  679. xgl.TexCoord2f(0.002, 0.002);
  680. gl.Vertex3f(1, -3, 1);
  681. xgl.TexCoord2f(0.998, 0.002);
  682. gl.Vertex3f(-1, -3, 1);
  683. xgl.TexCoord2f(0.998, 0.002);
  684. gl.Vertex3f(-1, -1, 1);
  685. end;
  686. gl.End_;
  687. until not libMat.UnApply(ARci);
  688. end;
  689. // TOP
  690. libMat := MaterialLibrary.LibMaterialByName(FMatNameTop);
  691. if libMat <> nil then
  692. begin
  693. libMat.Apply(ARci);
  694. repeat
  695. gl.Begin_(GL_QUADS);
  696. xgl.TexCoord2f(0.002, 0.998);
  697. gl.Vertex3f(-1, 1, 1);
  698. xgl.TexCoord2f(0.002, 0.002);
  699. gl.Vertex3f(-1, 1, -1);
  700. xgl.TexCoord2f(0.998, 0.002);
  701. gl.Vertex3f(1, 1, -1);
  702. xgl.TexCoord2f(0.998, 0.998);
  703. gl.Vertex3f(1, 1, 1);
  704. gl.End_;
  705. until not libMat.UnApply(ARci);
  706. end;
  707. // BOTTOM
  708. libMat := MaterialLibrary.LibMaterialByName(FMatNameBottom);
  709. if libMat <> nil then
  710. begin
  711. libMat.Apply(ARci);
  712. repeat
  713. gl.Begin_(GL_QUADS);
  714. xgl.TexCoord2f(0.002, 0.998);
  715. gl.Vertex3f(-1, -1, -1);
  716. xgl.TexCoord2f(0.002, 0.002);
  717. gl.Vertex3f(-1, -1, 1);
  718. xgl.TexCoord2f(0.998, 0.002);
  719. gl.Vertex3f(1, -1, 1);
  720. xgl.TexCoord2f(0.998, 0.998);
  721. gl.Vertex3f(1, -1, -1);
  722. gl.End_;
  723. until not libMat.UnApply(ARci);
  724. end;
  725. // LEFT
  726. libMat := MaterialLibrary.LibMaterialByName(FMatNameLeft);
  727. if libMat <> nil then
  728. begin
  729. libMat.Apply(ARci);
  730. repeat
  731. gl.Begin_(GL_QUADS);
  732. xgl.TexCoord2f(0.002, 0.998);
  733. gl.Vertex3f(-1, 1, 1);
  734. xgl.TexCoord2f(0.002, 0.002);
  735. gl.Vertex3f(-1, -1, 1);
  736. xgl.TexCoord2f(0.998, 0.002);
  737. gl.Vertex3f(-1, -1, -1);
  738. xgl.TexCoord2f(0.998, 0.998);
  739. gl.Vertex3f(-1, 1, -1);
  740. if Style = sbsTopHalfClamped then
  741. begin
  742. xgl.TexCoord2f(0.002, 0.002);
  743. gl.Vertex3f(-1, -1, 1);
  744. xgl.TexCoord2f(0.002, 0.002);
  745. gl.Vertex3f(-1, -3, 1);
  746. xgl.TexCoord2f(0.998, 0.002);
  747. gl.Vertex3f(-1, -3, -1);
  748. xgl.TexCoord2f(0.998, 0.002);
  749. gl.Vertex3f(-1, -1, -1);
  750. end;
  751. gl.End_;
  752. until not libMat.UnApply(ARci);
  753. end;
  754. // RIGHT
  755. libMat := MaterialLibrary.LibMaterialByName(FMatNameRight);
  756. if libMat <> nil then
  757. begin
  758. libMat.Apply(ARci);
  759. repeat
  760. gl.Begin_(GL_QUADS);
  761. xgl.TexCoord2f(0.002, 0.998);
  762. gl.Vertex3f(1, 1, -1);
  763. xgl.TexCoord2f(0.002, 0.002);
  764. gl.Vertex3f(1, -1, -1);
  765. xgl.TexCoord2f(0.998, 0.002);
  766. gl.Vertex3f(1, -1, 1);
  767. xgl.TexCoord2f(0.998, 0.998);
  768. gl.Vertex3f(1, 1, 1);
  769. if Style = sbsTopHalfClamped then
  770. begin
  771. xgl.TexCoord2f(0.002, 0.002);
  772. gl.Vertex3f(1, -1, -1);
  773. xgl.TexCoord2f(0.002, 0.002);
  774. gl.Vertex3f(1, -3, -1);
  775. xgl.TexCoord2f(0.998, 0.002);
  776. gl.Vertex3f(1, -3, 1);
  777. xgl.TexCoord2f(0.998, 0.002);
  778. gl.Vertex3f(1, -1, 1);
  779. end;
  780. gl.End_;
  781. until not libMat.UnApply(ARci);
  782. end;
  783. // CLOUDS CAP PLANE
  784. libMat := MaterialLibrary.LibMaterialByName(FMatNameClouds);
  785. if libMat <> nil then
  786. begin
  787. // pre-calculate possible values to speed up
  788. cps := FCloudsPlaneSize * 0.5;
  789. cof1 := FCloudsPlaneOffset;
  790. libMat.Apply(ARci);
  791. repeat
  792. gl.Begin_(GL_QUADS);
  793. xgl.TexCoord2f(0, 1);
  794. gl.Vertex3f(-cps, cof1, cps);
  795. xgl.TexCoord2f(0, 0);
  796. gl.Vertex3f(-cps, cof1, -cps);
  797. xgl.TexCoord2f(1, 0);
  798. gl.Vertex3f(cps, cof1, -cps);
  799. xgl.TexCoord2f(1, 1);
  800. gl.Vertex3f(cps, cof1, cps);
  801. gl.End_;
  802. until not libMat.UnApply(ARci);
  803. end;
  804. gl.PopMatrix;
  805. if stLighting in oldStates then
  806. ARci.GLStates.Enable(stLighting);
  807. if stFog in oldStates then
  808. ARci.GLStates.Enable(stFog);
  809. if stDepthTest in oldStates then
  810. ARci.GLStates.Enable(stDepthTest);
  811. finally
  812. end;
  813. end;
  814. procedure TGLSkyBox.SetCloudsPlaneOffset(const Value: single);
  815. begin
  816. FCloudsPlaneOffset := Value;
  817. StructureChanged;
  818. end;
  819. procedure TGLSkyBox.SetCloudsPlaneSize(const Value: single);
  820. begin
  821. FCloudsPlaneSize := Value;
  822. StructureChanged;
  823. end;
  824. procedure TGLSkyBox.SetStyle(const value: TGLSkyBoxStyle);
  825. begin
  826. FStyle := value;
  827. StructureChanged;
  828. end;
  829. procedure TGLSkyBox.SetMaterialLibrary(const value: TGLMaterialLibrary);
  830. begin
  831. FMaterialLibrary := value;
  832. StructureChanged;
  833. end;
  834. procedure TGLSkyBox.SetMatNameBack(const Value: string);
  835. begin
  836. FMatNameBack := Value;
  837. StructureChanged;
  838. end;
  839. procedure TGLSkyBox.SetMatNameBottom(const Value: string);
  840. begin
  841. FMatNameBottom := Value;
  842. StructureChanged;
  843. end;
  844. procedure TGLSkyBox.SetMatNameClouds(const Value: string);
  845. begin
  846. FMatNameClouds := Value;
  847. StructureChanged;
  848. end;
  849. procedure TGLSkyBox.SetMatNameFront(const Value: string);
  850. begin
  851. FMatNameFront := Value;
  852. StructureChanged;
  853. end;
  854. procedure TGLSkyBox.SetMatNameLeft(const Value: string);
  855. begin
  856. FMatNameLeft := Value;
  857. StructureChanged;
  858. end;
  859. procedure TGLSkyBox.SetMatNameRight(const Value: string);
  860. begin
  861. FMatNameRight := Value;
  862. StructureChanged;
  863. end;
  864. procedure TGLSkyBox.SetMatNameTop(const Value: string);
  865. begin
  866. FMatNameTop := Value;
  867. StructureChanged;
  868. end;
  869. //--------------------- SkyDome Region ------------------------------
  870. // ------------------
  871. // ------------------ TGLSkyDomeBand ------------------
  872. // ------------------
  873. constructor TGLSkyDomeBand.Create(Collection: TCollection);
  874. begin
  875. inherited Create(Collection);
  876. FStartColor := TGLColor.Create(Self);
  877. FStartColor.Initialize(clrBlue);
  878. FStartColor.OnNotifyChange := OnColorChange;
  879. FStopColor := TGLColor.Create(Self);
  880. FStopColor.Initialize(clrBlue);
  881. FStopColor.OnNotifyChange := OnColorChange;
  882. FSlices := 12;
  883. FStacks := 1;
  884. end;
  885. destructor TGLSkyDomeBand.Destroy;
  886. begin
  887. FStartColor.Free;
  888. FStopColor.Free;
  889. inherited Destroy;
  890. end;
  891. //----------------------------------------------------------------------
  892. procedure TGLSkyDomeBand.Assign(Source: TPersistent);
  893. begin
  894. if Source is TGLSkyDomeBand then
  895. begin
  896. FStartAngle := TGLSkyDomeBand(Source).FStartAngle;
  897. FStopAngle := TGLSkyDomeBand(Source).FStopAngle;
  898. FStartColor.Assign(TGLSkyDomeBand(Source).FStartColor);
  899. FStopColor.Assign(TGLSkyDomeBand(Source).FStopColor);
  900. FSlices := TGLSkyDomeBand(Source).FSlices;
  901. FStacks := TGLSkyDomeBand(Source).FStacks;
  902. end;
  903. inherited Destroy;
  904. end;
  905. function TGLSkyDomeBand.GetDisplayName: string;
  906. begin
  907. Result := Format('%d: %.1f° - %.1f°', [Index, StartAngle, StopAngle]);
  908. end;
  909. procedure TGLSkyDomeBand.SetStartAngle(const val: Single);
  910. begin
  911. FStartAngle := ClampValue(val, -90, 90);
  912. if FStartAngle > FStopAngle then
  913. FStopAngle := FStartAngle;
  914. TGLSkyDomeBands(Collection).NotifyChange;
  915. end;
  916. procedure TGLSkyDomeBand.SetStartColor(const val: TGLColor);
  917. begin
  918. FStartColor.Assign(val);
  919. end;
  920. //----------------------------------------------------------------------
  921. procedure TGLSkyDomeBand.SetStopAngle(const val: Single);
  922. begin
  923. FStopAngle := ClampValue(val, -90, 90);
  924. if FStopAngle < FStartAngle then
  925. FStartAngle := FStopAngle;
  926. TGLSkyDomeBands(Collection).NotifyChange;
  927. end;
  928. procedure TGLSkyDomeBand.SetStopColor(const val: TGLColor);
  929. begin
  930. FStopColor.Assign(val);
  931. end;
  932. procedure TGLSkyDomeBand.SetSlices(const val: Integer);
  933. begin
  934. if val < 3 then
  935. FSlices := 3
  936. else
  937. FSlices := val;
  938. TGLSkyDomeBands(Collection).NotifyChange;
  939. end;
  940. procedure TGLSkyDomeBand.SetStacks(const val: Integer);
  941. begin
  942. if val < 1 then
  943. FStacks := 1
  944. else
  945. FStacks := val;
  946. TGLSkyDomeBands(Collection).NotifyChange;
  947. end;
  948. procedure TGLSkyDomeBand.OnColorChange(sender: TObject);
  949. begin
  950. TGLSkyDomeBands(Collection).NotifyChange;
  951. end;
  952. //----------------------------------------------------------------------
  953. procedure TGLSkyDomeBand.BuildList(var rci: TGLRenderContextInfo);
  954. // coordinates system note: X is forward, Y is left and Z is up
  955. // always rendered as sphere of radius 1
  956. procedure RenderBand(start, stop: Single;
  957. const colStart, colStop: TGLColorVector);
  958. var
  959. i: Integer;
  960. f, r, r2: Single;
  961. vertex1, vertex2: TGLVector;
  962. begin
  963. vertex1.W := 1;
  964. if start = -90 then
  965. begin
  966. // triangle fan with south pole
  967. gl.Begin_(GL_TRIANGLE_FAN);
  968. gl.Color4fv(@colStart);
  969. gl.Vertex3f(0, 0, -1);
  970. f := 2 * PI / Slices;
  971. SinCosine(DegToRadian(stop), vertex1.Z, r);
  972. gl.Color4fv(@colStop);
  973. for i := 0 to Slices do
  974. begin
  975. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  976. gl.Vertex4fv(@vertex1);
  977. end;
  978. gl.End_;
  979. end
  980. else if stop = 90 then
  981. begin
  982. // triangle fan with north pole
  983. gl.Begin_(GL_TRIANGLE_FAN);
  984. gl.Color4fv(@colStop);
  985. gl.Vertex3fv(@ZHmgPoint);
  986. f := 2 * PI / Slices;
  987. SinCosine(DegToRadian(start), vertex1.Z, r);
  988. gl.Color4fv(@colStart);
  989. for i := Slices downto 0 do
  990. begin
  991. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  992. gl.Vertex4fv(@vertex1);
  993. end;
  994. gl.End_;
  995. end
  996. else
  997. begin
  998. vertex2.W := 1;
  999. // triangle strip
  1000. gl.Begin_(GL_TRIANGLE_STRIP);
  1001. f := 2 * PI / Slices;
  1002. SinCosine(DegToRadian(start), vertex1.Z, r);
  1003. SinCosine(DegToRadian(stop), vertex2.Z, r2);
  1004. for i := 0 to Slices do
  1005. begin
  1006. SinCosine(i * f, r, vertex1.Y, vertex1.X);
  1007. gl.Color4fv(@colStart);
  1008. gl.Vertex4fv(@vertex1);
  1009. SinCosine(i * f, r2, vertex2.Y, vertex2.X);
  1010. gl.Color4fv(@colStop);
  1011. gl.Vertex4fv(@vertex2);
  1012. end;
  1013. gl.End_;
  1014. end;
  1015. end;
  1016. var
  1017. n: Integer;
  1018. t, t2: Single;
  1019. begin
  1020. if StartAngle = StopAngle then
  1021. Exit;
  1022. for n := 0 to Stacks - 1 do
  1023. begin
  1024. t := n / Stacks;
  1025. t2 := (n + 1) / Stacks;
  1026. RenderBand(Lerp(StartAngle, StopAngle, t), Lerp(StartAngle, StopAngle, t2),
  1027. VectorLerp(StartColor.Color, StopColor.Color, t),
  1028. VectorLerp(StartColor.Color, StopColor.Color, t2));
  1029. end;
  1030. end;
  1031. // ------------------
  1032. // ------------------ TGLSkyDomeBands ------------------
  1033. // ------------------
  1034. constructor TGLSkyDomeBands.Create(AOwner: TComponent);
  1035. begin
  1036. owner := AOwner;
  1037. inherited Create(TGLSkyDomeBand);
  1038. end;
  1039. function TGLSkyDomeBands.GetOwner: TPersistent;
  1040. begin
  1041. Result := owner;
  1042. end;
  1043. //----------------------------------------------------------------------
  1044. procedure TGLSkyDomeBands.SetItems(index: Integer; const val: TGLSkyDomeBand);
  1045. begin
  1046. inherited Items[index] := val;
  1047. end;
  1048. function TGLSkyDomeBands.GetItems(index: Integer): TGLSkyDomeBand;
  1049. begin
  1050. Result := TGLSkyDomeBand(inherited Items[index]);
  1051. end;
  1052. function TGLSkyDomeBands.Add: TGLSkyDomeBand;
  1053. begin
  1054. Result := (inherited Add) as TGLSkyDomeBand;
  1055. end;
  1056. function TGLSkyDomeBands.FindItemID(ID: Integer): TGLSkyDomeBand;
  1057. begin
  1058. Result := (inherited FindItemID(ID)) as TGLSkyDomeBand;
  1059. end;
  1060. procedure TGLSkyDomeBands.NotifyChange;
  1061. begin
  1062. if Assigned(owner) and (owner is TGLBaseSceneObject) then
  1063. TGLBaseSceneObject(owner).StructureChanged;
  1064. end;
  1065. procedure TGLSkyDomeBands.BuildList(var rci: TGLRenderContextInfo);
  1066. var
  1067. i: Integer;
  1068. begin
  1069. for i := 0 to Count - 1 do
  1070. Items[i].BuildList(rci);
  1071. end;
  1072. // ------------------
  1073. // ------------------ TGLSkyDomeStar ------------------
  1074. // ------------------
  1075. constructor TGLSkyDomeStar.Create(Collection: TCollection);
  1076. begin
  1077. inherited Create(Collection);
  1078. end;
  1079. destructor TGLSkyDomeStar.Destroy;
  1080. begin
  1081. inherited Destroy;
  1082. end;
  1083. procedure TGLSkyDomeStar.Assign(Source: TPersistent);
  1084. begin
  1085. if Source is TGLSkyDomeStar then
  1086. begin
  1087. FRA := TGLSkyDomeStar(Source).FRA;
  1088. FDec := TGLSkyDomeStar(Source).FDec;
  1089. FMagnitude := TGLSkyDomeStar(Source).FMagnitude;
  1090. FColor := TGLSkyDomeStar(Source).FColor;
  1091. SetVector(FCacheCoord, TGLSkyDomeStar(Source).FCacheCoord);
  1092. end;
  1093. inherited Destroy;
  1094. end;
  1095. //----------------------------------------------------------------------
  1096. function TGLSkyDomeStar.GetDisplayName: string;
  1097. begin
  1098. Result := Format('RA: %5.1f / Dec: %5.1f', [RA, DEC]);
  1099. end;
  1100. // ------------------
  1101. // ------------------ TGLSkyDomeStars ------------------
  1102. // ------------------
  1103. constructor TGLSkyDomeStars.Create(AOwner: TComponent);
  1104. begin
  1105. owner := AOwner;
  1106. inherited Create(TGLSkyDomeStar);
  1107. end;
  1108. function TGLSkyDomeStars.GetOwner: TPersistent;
  1109. begin
  1110. Result := owner;
  1111. end;
  1112. procedure TGLSkyDomeStars.SetItems(index: Integer; const val: TGLSkyDomeStar);
  1113. begin
  1114. inherited Items[index] := val;
  1115. end;
  1116. function TGLSkyDomeStars.GetItems(index: Integer): TGLSkyDomeStar;
  1117. begin
  1118. Result := TGLSkyDomeStar(inherited Items[index]);
  1119. end;
  1120. function TGLSkyDomeStars.Add: TGLSkyDomeStar;
  1121. begin
  1122. Result := (inherited Add) as TGLSkyDomeStar;
  1123. end;
  1124. function TGLSkyDomeStars.FindItemID(ID: Integer): TGLSkyDomeStar;
  1125. begin
  1126. Result := (inherited FindItemID(ID)) as TGLSkyDomeStar;
  1127. end;
  1128. //----------------------------------------------------------------------
  1129. procedure TGLSkyDomeStars.PrecomputeCartesianCoordinates;
  1130. var
  1131. i: Integer;
  1132. star: TGLSkyDomeStar;
  1133. raC, raS, decC, decS: Single;
  1134. begin
  1135. // to be enhanced...
  1136. for i := 0 to Count - 1 do
  1137. begin
  1138. star := Items[i];
  1139. SinCosine(star.DEC * cPIdiv180, decS, decC);
  1140. SinCosine(star.RA * cPIdiv180, decC, raS, raC);
  1141. star.FCacheCoord.X := raC;
  1142. star.FCacheCoord.Y := raS;
  1143. star.FCacheCoord.Z := decS;
  1144. end;
  1145. end;
  1146. //----------------------------------------------------------------------
  1147. procedure TGLSkyDomeStars.BuildList(var rci: TGLRenderContextInfo;
  1148. twinkle: Boolean);
  1149. var
  1150. i, n: Integer;
  1151. star: TGLSkyDomeStar;
  1152. lastColor: TColor;
  1153. lastPointSize10, pointSize10: Integer;
  1154. Color, twinkleColor: TGLColorVector;
  1155. (*sub*)procedure DoTwinkle;
  1156. begin
  1157. if (n and 63) = 0 then
  1158. begin
  1159. twinkleColor := VectorScale(Color, Random * 0.6 + 0.4);
  1160. gl.Color3fv(@twinkleColor.X);
  1161. n := 0;
  1162. end
  1163. else
  1164. Inc(n);
  1165. end;
  1166. begin
  1167. if Count = 0 then
  1168. Exit;
  1169. PrecomputeCartesianCoordinates;
  1170. lastColor := -1;
  1171. n := 0;
  1172. lastPointSize10 := -1;
  1173. rci.GLStates.Enable(stPointSmooth);
  1174. rci.GLStates.Enable(stAlphaTest);
  1175. rci.GLStates.SetGLAlphaFunction(cfNotEqual, 0.0);
  1176. rci.GLStates.Enable(stBlend);
  1177. rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
  1178. gl.Begin_(GL_POINTS);
  1179. for i := 0 to Count - 1 do
  1180. begin
  1181. star := Items[i];
  1182. pointSize10 := Round((4.5 - star.Magnitude) * 10);
  1183. if pointSize10 <> lastPointSize10 then
  1184. begin
  1185. if pointSize10 > 15 then
  1186. begin
  1187. gl.End_;
  1188. lastPointSize10 := pointSize10;
  1189. rci.GLStates.PointSize := pointSize10 * 0.1;
  1190. gl.Begin_(GL_POINTS);
  1191. end
  1192. else if lastPointSize10 <> 15 then
  1193. begin
  1194. gl.End_;
  1195. lastPointSize10 := 15;
  1196. rci.GLStates.PointSize := 1.5;
  1197. gl.Begin_(GL_POINTS);
  1198. end;
  1199. end;
  1200. if lastColor <> star.FColor then
  1201. begin
  1202. Color := ConvertWinColor(star.FColor);
  1203. if twinkle then
  1204. begin
  1205. n := 0;
  1206. DoTwinkle;
  1207. end
  1208. else
  1209. gl.Color3fv(@Color.X);
  1210. lastColor := star.FColor;
  1211. end
  1212. else if twinkle then
  1213. DoTwinkle;
  1214. gl.Vertex3fv(@star.FCacheCoord.X);
  1215. end;
  1216. gl.End_;
  1217. // restore default AlphaFunc
  1218. rci.GLStates.SetGLAlphaFunction(cfGreater, 0);
  1219. end;
  1220. //----------------------------------------------------------------------
  1221. procedure TGLSkyDomeStars.AddRandomStars(const nb: Integer; const Color: TColor;
  1222. const LimitToTopDome: Boolean = False);
  1223. var
  1224. i: Integer;
  1225. coord: TAffineVector;
  1226. star: TGLSkyDomeStar;
  1227. begin
  1228. for i := 1 to nb do
  1229. begin
  1230. star := Add;
  1231. // pick a point in the half-cube
  1232. if LimitToTopDome then
  1233. coord.Z := Random
  1234. else
  1235. coord.Z := Random * 2 - 1;
  1236. // calculate RA and Dec
  1237. star.DEC := ArcSin(coord.Z) * c180divPI;
  1238. star.RA := Random * 360 - 180;
  1239. // pick a color
  1240. star.Color := Color;
  1241. // pick a magnitude
  1242. star.Magnitude := 3;
  1243. end;
  1244. end;
  1245. //-----------------------------------------------------------------------
  1246. procedure TGLSkyDomeStars.AddRandomStars(const nb: Integer;
  1247. const ColorMin, ColorMax: TVector3b;
  1248. const Magnitude_min, Magnitude_max: Single;
  1249. const LimitToTopDome: Boolean = False);
  1250. function RandomTT(Min, Max: Byte): Byte;
  1251. begin
  1252. Result := Min + Random(Max - Min);
  1253. end;
  1254. var
  1255. i: Integer;
  1256. coord: TAffineVector;
  1257. star: TGLSkyDomeStar;
  1258. begin
  1259. for i := 1 to nb do
  1260. begin
  1261. star := Add;
  1262. // pick a point in the half-cube
  1263. if LimitToTopDome then
  1264. coord.Z := Random
  1265. else
  1266. coord.Z := Random * 2 - 1;
  1267. // calculate RA and Dec
  1268. star.DEC := ArcSin(coord.Z) * c180divPI;
  1269. star.RA := Random * 360 - 180;
  1270. // pick a color
  1271. star.Color := RGB2Color(RandomTT(ColorMin.X, ColorMax.X),
  1272. RandomTT(ColorMin.Y, ColorMax.Y), RandomTT(ColorMin.Z, ColorMax.Z));
  1273. // pick a magnitude
  1274. star.Magnitude := Magnitude_min + Random * (Magnitude_max - Magnitude_min);
  1275. end;
  1276. end;
  1277. //----------------------------------------------------------------------
  1278. procedure TGLSkyDomeStars.LoadStarsFile(const starsFileName: string);
  1279. var
  1280. fs: TFileStream;
  1281. sr: TGLStarRecord;
  1282. colorVector: TGLColorVector;
  1283. begin
  1284. fs := TFileStream.Create(starsFileName, fmOpenRead + fmShareDenyWrite);
  1285. try
  1286. while fs.Position < fs.Size do
  1287. begin
  1288. fs.Read(sr, SizeOf(sr));
  1289. with Add do
  1290. begin
  1291. RA := sr.RA * 0.01;
  1292. DEC := sr.DEC * 0.01;
  1293. colorVector := StarRecordColor(sr, 3);
  1294. Magnitude := sr.VMagnitude * 0.05; // default 0.1
  1295. if sr.VMagnitude > 35 then
  1296. Color := ConvertColorVector(colorVector, colorVector.W)
  1297. else
  1298. Color := ConvertColorVector(colorVector);
  1299. end;
  1300. end;
  1301. finally
  1302. fs.Free;
  1303. end;
  1304. end;
  1305. // ------------------
  1306. // ------------------ TGLSkyDome ------------------
  1307. // ------------------
  1308. constructor TGLSkyDome.Create(AOwner: TComponent);
  1309. begin
  1310. inherited Create(AOwner);
  1311. CamInvarianceMode := cimPosition;
  1312. ObjectStyle := ObjectStyle + [osDirectDraw, osNoVisibilityCulling];
  1313. FBands := TGLSkyDomeBands.Create(Self);
  1314. with FBands.Add do
  1315. begin
  1316. StartAngle := 0;
  1317. StartColor.Color := clrWhite;
  1318. StopAngle := 15;
  1319. StopColor.Color := clrBlue;
  1320. end;
  1321. with FBands.Add do
  1322. begin
  1323. StartAngle := 15;
  1324. StartColor.Color := clrBlue;
  1325. StopAngle := 90;
  1326. Stacks := 4;
  1327. StopColor.Color := clrNavy;
  1328. end;
  1329. FStars := TGLSkyDomeStars.Create(Self);
  1330. end;
  1331. destructor TGLSkyDome.Destroy;
  1332. begin
  1333. FStars.Free;
  1334. FBands.Free;
  1335. inherited Destroy;
  1336. end;
  1337. //----------------------------------------------------------------------
  1338. procedure TGLSkyDome.Assign(Source: TPersistent);
  1339. begin
  1340. if Source is TGLSkyDome then
  1341. begin
  1342. FBands.Assign(TGLSkyDome(Source).FBands);
  1343. FStars.Assign(TGLSkyDome(Source).FStars);
  1344. end;
  1345. inherited;
  1346. end;
  1347. //----------------------------------------------------------------------
  1348. procedure TGLSkyDome.SetBands(const val: TGLSkyDomeBands);
  1349. begin
  1350. FBands.Assign(val);
  1351. StructureChanged;
  1352. end;
  1353. procedure TGLSkyDome.SetStars(const val: TGLSkyDomeStars);
  1354. begin
  1355. FStars.Assign(val);
  1356. StructureChanged;
  1357. end;
  1358. //--------- Options to draw grids and twinkle stars --------
  1359. procedure TGLSkyDome.SetOptions(const val: TGLSkyDomeOptions);
  1360. begin
  1361. if val <> FOptions then
  1362. begin
  1363. FOptions := val;
  1364. if sdoTwinkle in FOptions then
  1365. ObjectStyle := ObjectStyle + [osDirectDraw]
  1366. else
  1367. begin
  1368. ObjectStyle := ObjectStyle - [osDirectDraw];
  1369. DestroyHandle;
  1370. end;
  1371. StructureChanged;
  1372. end;
  1373. end;
  1374. //----------------------------------------------------------------------
  1375. procedure TGLSkyDome.BuildList(var rci: TGLRenderContextInfo);
  1376. var
  1377. f: Single;
  1378. begin
  1379. // setup states
  1380. with rci.GLStates do
  1381. begin
  1382. Disable(stLighting);
  1383. Disable(stDepthTest);
  1384. Disable(stFog);
  1385. Disable(stCullFace);
  1386. Disable(stBlend);
  1387. DepthWriteMask := False;
  1388. PolygonMode := pmFill;
  1389. end;
  1390. f := rci.rcci.farClippingDistance * 0.90;
  1391. gl.Scalef(f, f, f);
  1392. Bands.BuildList(rci);
  1393. Stars.BuildList(rci, (sdoTwinkle in FOptions));
  1394. end;
  1395. // ------------------
  1396. // ------------------ TGLEarthSkyDome ------------------
  1397. // ------------------
  1398. constructor TGLEarthSkyDome.Create(AOwner: TComponent);
  1399. begin
  1400. inherited Create(AOwner);
  1401. FMorning := true;
  1402. Bands.Clear;
  1403. FSunElevation := 75;
  1404. FTurbidity := 15;
  1405. FSunZenithColor := TGLColor.CreateInitialized(Self, clrWhite, OnColorChanged);
  1406. FSunDawnColor := TGLColor.CreateInitialized(Self, Vectormake(1, 0.5, 0, 0),
  1407. OnColorChanged);
  1408. FHazeColor := TGLColor.CreateInitialized(Self, Vectormake(0.9, 0.95, 1, 0),
  1409. OnColorChanged);
  1410. FSkyColor := TGLColor.CreateInitialized(Self, Vectormake(0.45, 0.6, 0.9, 0),
  1411. OnColorChanged);
  1412. FNightColor := TGLColor.CreateInitialized(Self, clrTransparent,
  1413. OnColorChanged);
  1414. FDeepColor := TGLColor.CreateInitialized(Self, Vectormake(0, 0.2, 0.4, 0));
  1415. FStacks := 24;
  1416. FSlices := 48;
  1417. PreCalculate;
  1418. end;
  1419. //----------------------------------------------------------------------
  1420. destructor TGLEarthSkyDome.Destroy;
  1421. begin
  1422. FSunZenithColor.Free;
  1423. FSunDawnColor.Free;
  1424. FHazeColor.Free;
  1425. FSkyColor.Free;
  1426. FNightColor.Free;
  1427. FDeepColor.Free;
  1428. inherited Destroy;
  1429. end;
  1430. //----------------------------------------------------------------------
  1431. procedure TGLEarthSkyDome.Assign(Source: TPersistent);
  1432. begin
  1433. if Source is TGLSkyDome then
  1434. begin
  1435. FSunElevation := TGLEarthSkyDome(Source).SunElevation;
  1436. FTurbidity := TGLEarthSkyDome(Source).Turbidity;
  1437. FSunZenithColor.Assign(TGLEarthSkyDome(Source).FSunZenithColor);
  1438. FSunDawnColor.Assign(TGLEarthSkyDome(Source).FSunDawnColor);
  1439. FHazeColor.Assign(TGLEarthSkyDome(Source).FHazeColor);
  1440. FSkyColor.Assign(TGLEarthSkyDome(Source).FSkyColor);
  1441. FNightColor.Assign(TGLEarthSkyDome(Source).FNightColor);
  1442. FSlices := TGLEarthSkyDome(Source).FSlices;
  1443. FStacks := TGLEarthSkyDome(Source).FStacks;
  1444. PreCalculate;
  1445. end;
  1446. inherited;
  1447. end;
  1448. procedure TGLEarthSkyDome.Loaded;
  1449. begin
  1450. inherited;
  1451. PreCalculate;
  1452. end;
  1453. //----------------------------------------------------------------------
  1454. procedure TGLEarthSkyDome.SetSunElevation(const val: Single);
  1455. var
  1456. newVal: Single;
  1457. begin
  1458. newVal := ClampValue(val, -90, 90);
  1459. if FSunElevation <> newVal then
  1460. begin
  1461. FSunElevation := newVal;
  1462. PreCalculate;
  1463. end;
  1464. end;
  1465. procedure TGLEarthSkyDome.SetTurbidity(const val: Single);
  1466. begin
  1467. FTurbidity := ClampValue(val, 1, 120);
  1468. PreCalculate;
  1469. end;
  1470. procedure TGLEarthSkyDome.SetSunZenithColor(const val: TGLColor);
  1471. begin
  1472. FSunZenithColor.Assign(val);
  1473. PreCalculate;
  1474. end;
  1475. procedure TGLEarthSkyDome.SetSunDawnColor(const val: TGLColor);
  1476. begin
  1477. FSunDawnColor.Assign(val);
  1478. PreCalculate;
  1479. end;
  1480. procedure TGLEarthSkyDome.SetHazeColor(const val: TGLColor);
  1481. begin
  1482. FHazeColor.Assign(val);
  1483. PreCalculate;
  1484. end;
  1485. procedure TGLEarthSkyDome.SetSkyColor(const val: TGLColor);
  1486. begin
  1487. FSkyColor.Assign(val);
  1488. PreCalculate;
  1489. end;
  1490. procedure TGLEarthSkyDome.SetNightColor(const val: TGLColor);
  1491. begin
  1492. FNightColor.Assign(val);
  1493. PreCalculate;
  1494. end;
  1495. procedure TGLEarthSkyDome.SetDeepColor(const val: TGLColor);
  1496. begin
  1497. FDeepColor.Assign(val);
  1498. PreCalculate;
  1499. end;
  1500. procedure TGLEarthSkyDome.SetSlices(const val: Integer);
  1501. begin
  1502. if val > 6 then
  1503. FSlices := val
  1504. else
  1505. FSlices := 6;
  1506. StructureChanged;
  1507. end;
  1508. procedure TGLEarthSkyDome.SetStacks(const val: Integer);
  1509. begin
  1510. if val > 1 then
  1511. FStacks := val
  1512. else
  1513. FStacks := 1;
  1514. StructureChanged;
  1515. end;
  1516. //----------------------------------------------------------------------
  1517. procedure TGLEarthSkyDome.BuildList(var rci: TGLRenderContextInfo);
  1518. var
  1519. f: Single;
  1520. begin
  1521. // setup states
  1522. with rci.GLStates do
  1523. begin
  1524. CurrentProgram := 0;
  1525. Disable(stLighting);
  1526. if esoDepthTest in FExtendedOptions then
  1527. begin
  1528. Enable(stDepthTest);
  1529. DepthFunc := cfLEqual;
  1530. end
  1531. else
  1532. Disable(stDepthTest);
  1533. Disable(stFog);
  1534. Disable(stCullFace);
  1535. Disable(stBlend);
  1536. Disable(stAlphaTest);
  1537. DepthWriteMask := False;
  1538. PolygonMode := pmFill;
  1539. end;
  1540. f := rci.rcci.farClippingDistance * 0.95;
  1541. gl.Scalef(f, f, f);
  1542. RenderDome;
  1543. Bands.BuildList(rci);
  1544. Stars.BuildList(rci, (sdoTwinkle in FOptions));
  1545. // restore
  1546. rci.GLStates.DepthWriteMask := true;
  1547. end;
  1548. procedure TGLEarthSkyDome.OnColorChanged(sender: TObject);
  1549. begin
  1550. PreCalculate;
  1551. end;
  1552. //----------------------------------------------------------------------
  1553. procedure TGLEarthSkyDome.SetSunAtTime(HH, MM: Single);
  1554. const
  1555. cHourToElevation1: array [0 .. 23] of Single = (-45, -67.5, -90, -57.5, -45,
  1556. -22.5, 0, 11.25, 22.5, 33.7, 45, 56.25, 67.5, 78.75, 90, 78.75, 67.5, 56.25,
  1557. 45, 33.7, 22.5, 11.25, 0, -22.5);
  1558. cHourToElevation2: array [0 .. 23] of Single = (-0.375, -0.375, 0.375, 0.375,
  1559. 0.375, 0.375, 0.1875, 0.1875, 0.1875, 0.1875, 0.1875, 0.1875, 0.1875,
  1560. 0.1875, -0.1875, -0.1875, -0.1875, -0.1875, -0.1875, -0.1875, -0.1875,
  1561. -0.1875, -0.375, -0.375);
  1562. var
  1563. ts: Single;
  1564. fts: Single;
  1565. i: Integer;
  1566. Color: TColor;
  1567. begin
  1568. HH := Round(HH);
  1569. if HH < 0 then
  1570. HH := 0;
  1571. if HH > 23 then
  1572. HH := 23;
  1573. if MM < 0 then
  1574. MM := 0;
  1575. if MM >= 60 then
  1576. begin
  1577. MM := 0;
  1578. HH := HH + 1;
  1579. if HH > 23 then
  1580. HH := 0;
  1581. end;
  1582. FSunElevation := cHourToElevation1[Round(HH)] + cHourToElevation2
  1583. [Round(HH)] * MM;
  1584. ts := DegToRadian(90 - FSunElevation);
  1585. // Mix base colors
  1586. fts := exp(-6 * (PI / 2 - ts));
  1587. VectorLerp(SunZenithColor.Color, SunDawnColor.Color, fts, FCurSunColor);
  1588. fts := IntPower(1 - cos(ts - 0.5), 2);
  1589. VectorLerp(HazeColor.Color, NightColor.Color, fts, FCurHazeColor);
  1590. VectorLerp(SkyColor.Color, NightColor.Color, fts, FCurSkyColor);
  1591. // Precalculate Turbidity factors
  1592. FCurHazeTurbid := -sqrt(121 - Turbidity) * 2;
  1593. FCurSunSkyTurbid := -(121 - Turbidity);
  1594. // fade stars if required
  1595. if SunElevation > -40 then
  1596. ts := PowerInteger(1 - (SunElevation + 40) / 90, 11)
  1597. else
  1598. ts := 1;
  1599. Color := RGB2Color(Round(ts * 255), Round(ts * 255), Round(ts * 255));
  1600. if esoFadeStarsWithSun in ExtendedOptions then
  1601. for i := 0 to Stars.Count - 1 do
  1602. Stars[i].Color := Color;
  1603. if esoRotateOnTwelveHours in ExtendedOptions then // spining around blue orb
  1604. begin
  1605. if (HH >= 14) and (FMorning) then
  1606. begin
  1607. roll(180);
  1608. for i := 0 to Stars.Count - 1 do
  1609. Stars[i].RA := Stars[i].RA + 180;
  1610. FMorning := False;
  1611. end;
  1612. if (HH >= 2) and (HH < 14) and (not FMorning) then
  1613. begin
  1614. roll(180);
  1615. for i := 0 to Stars.Count - 1 do
  1616. Stars[i].RA := Stars[i].RA + 180;
  1617. FMorning := true;
  1618. end;
  1619. end;
  1620. StructureChanged;
  1621. end;
  1622. //----------------------------------------------------------------------
  1623. procedure TGLEarthSkyDome.PreCalculate;
  1624. var
  1625. ts: Single;
  1626. fts: Single;
  1627. i: Integer;
  1628. Color: TColor;
  1629. begin
  1630. ts := DegToRadian(90 - SunElevation);
  1631. // Precompose base colors
  1632. fts := exp(-6 * (PI / 2 - ts));
  1633. VectorLerp(SunZenithColor.Color, SunDawnColor.Color, fts, FCurSunColor);
  1634. fts := PowerInteger(1 - cos(ts - 0.5), 2);
  1635. VectorLerp(HazeColor.Color, NightColor.Color, fts, FCurHazeColor);
  1636. VectorLerp(SkyColor.Color, NightColor.Color, fts, FCurSkyColor);
  1637. // Precalculate Turbidity factors
  1638. FCurHazeTurbid := -sqrt(121 - Turbidity) * 2;
  1639. FCurSunSkyTurbid := -(121 - Turbidity);
  1640. // fade stars if required
  1641. if SunElevation > -40 then
  1642. ts := PowerInteger(1 - (SunElevation + 40) / 90, 11)
  1643. else
  1644. ts := 1;
  1645. Color := RGB2Color(Round(ts * 255), Round(ts * 255), Round(ts * 255));
  1646. if esoFadeStarsWithSun in ExtendedOptions then
  1647. for i := 0 to Stars.Count - 1 do
  1648. Stars[i].Color := Color;
  1649. if esoRotateOnTwelveHours in ExtendedOptions then
  1650. begin
  1651. if SunElevation = 90 then
  1652. begin
  1653. roll(180);
  1654. for i := 0 to Stars.Count - 1 do
  1655. Stars[i].RA := Stars[i].RA + 180;
  1656. end
  1657. else if SunElevation = -90 then
  1658. begin
  1659. roll(180);
  1660. for i := 0 to Stars.Count - 1 do
  1661. Stars[i].RA := Stars[i].RA + 180;
  1662. end;
  1663. end;
  1664. StructureChanged;
  1665. end;
  1666. //----------------------------------------------------------------------
  1667. function TGLEarthSkyDome.CalculateColor(const theta, cosGamma: Single)
  1668. : TGLColorVector;
  1669. var
  1670. t: Single;
  1671. begin
  1672. t := PI / 2 - theta;
  1673. // mix to get haze/sky
  1674. VectorLerp(FCurSkyColor, FCurHazeColor, ClampValue(exp(FCurHazeTurbid * t), 0,
  1675. 1), Result);
  1676. // then mix sky with sun
  1677. VectorLerp(Result, FCurSunColor,
  1678. ClampValue(exp(FCurSunSkyTurbid * cosGamma * (1 + t)) * 1.1, 0, 1), Result);
  1679. end;
  1680. //----------------------------------------------------------------------
  1681. procedure TGLEarthSkyDome.RenderDome;
  1682. var
  1683. ts: Single;
  1684. steps: Integer;
  1685. sunPos: TAffineVector;
  1686. sinTable, cosTable: PFloatArray;
  1687. (*sub*)function CalculateCosGamma(const p: TGLVector): Single;
  1688. begin
  1689. Result := 1 - VectorAngleCosine(PAffineVector(@p)^, sunPos);
  1690. end;
  1691. (*sub*)procedure RenderDeepBand(stop: Single);
  1692. var
  1693. i: Integer;
  1694. r, thetaStart: Single;
  1695. vertex1: TGLVector;
  1696. Color: TGLColorVector;
  1697. begin
  1698. r := 0;
  1699. vertex1.W := 1;
  1700. // triangle fan with south pole
  1701. gl.Begin_(GL_TRIANGLE_FAN);
  1702. Color := CalculateColor(0, CalculateCosGamma(ZHmgPoint));
  1703. gl.Color4fv(DeepColor.AsAddress);
  1704. gl.Vertex3f(0, 0, -1);
  1705. SinCosine(DegToRadian(stop), vertex1.Z, r);
  1706. thetaStart := DegToRadian(90 - stop);
  1707. for i := 0 to steps - 1 do
  1708. begin
  1709. vertex1.X := r * cosTable[i];
  1710. vertex1.Y := r * sinTable[i];
  1711. Color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1712. gl.Color4fv(@Color);
  1713. gl.Vertex4fv(@vertex1);
  1714. end;
  1715. gl.End_;
  1716. end;
  1717. (*sub*)procedure RenderBand(start, stop: Single);
  1718. var
  1719. i: Integer;
  1720. r, r2, thetaStart, thetaStop: Single;
  1721. vertex1, vertex2: TGLVector;
  1722. Color: TGLColorVector;
  1723. begin
  1724. vertex1.W := 1;
  1725. if stop = 90 then
  1726. begin
  1727. // triangle fan with north pole
  1728. gl.Begin_(GL_TRIANGLE_FAN);
  1729. Color := CalculateColor(0, CalculateCosGamma(ZHmgPoint));
  1730. gl.Color4fv(@Color);
  1731. gl.Vertex4fv(@ZHmgPoint);
  1732. SinCosine(DegToRadian(start), vertex1.Z, r);
  1733. thetaStart := DegToRadian(90 - start);
  1734. for i := 0 to steps - 1 do
  1735. begin
  1736. vertex1.X := r * cosTable[i];
  1737. vertex1.Y := r * sinTable[i];
  1738. Color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1739. gl.Color4fv(@Color);
  1740. gl.Vertex4fv(@vertex1);
  1741. end;
  1742. gl.End_;
  1743. end
  1744. else
  1745. begin
  1746. vertex2.W := 1;
  1747. // triangle strip
  1748. gl.Begin_(GL_TRIANGLE_STRIP);
  1749. SinCosine(DegToRadian(start), vertex1.Z, r);
  1750. SinCosine(DegToRadian(stop), vertex2.Z, r2);
  1751. thetaStart := DegToRadian(90 - start);
  1752. thetaStop := DegToRadian(90 - stop);
  1753. for i := 0 to steps - 1 do
  1754. begin
  1755. vertex1.X := r * cosTable[i];
  1756. vertex1.Y := r * sinTable[i];
  1757. Color := CalculateColor(thetaStart, CalculateCosGamma(vertex1));
  1758. gl.Color4fv(@Color);
  1759. gl.Vertex4fv(@vertex1);
  1760. vertex2.X := r2 * cosTable[i];
  1761. vertex2.Y := r2 * sinTable[i];
  1762. Color := CalculateColor(thetaStop, CalculateCosGamma(vertex2));
  1763. gl.Color4fv(@Color);
  1764. gl.Vertex4fv(@vertex2);
  1765. end;
  1766. gl.End_;
  1767. end;
  1768. end;
  1769. var
  1770. n, i, sdiv2: Integer;
  1771. t, t2, p, fs: Single;
  1772. begin
  1773. ts := DegToRadian(90 - SunElevation);
  1774. SetVector(sunPos, sin(ts), 0, cos(ts));
  1775. // prepare sin/cos LUT, with a higher sampling around 0Ѝ
  1776. n := Slices div 2;
  1777. steps := 2 * n + 1;
  1778. GetMem(sinTable, steps * SizeOf(Single));
  1779. GetMem(cosTable, steps * SizeOf(Single));
  1780. for i := 1 to n do
  1781. begin
  1782. p := (1 - sqrt(cos((i / n) * cPIdiv2))) * PI;
  1783. SinCosine(p, sinTable[n + i], cosTable[n + i]);
  1784. sinTable[n - i] := -sinTable[n + i];
  1785. cosTable[n - i] := cosTable[n + i];
  1786. end;
  1787. // these are defined by hand for precision issue: the dome must wrap exactly
  1788. sinTable[n] := 0;
  1789. cosTable[n] := 1;
  1790. sinTable[0] := 0;
  1791. cosTable[0] := -1;
  1792. sinTable[steps - 1] := 0;
  1793. cosTable[steps - 1] := -1;
  1794. fs := SunElevation / 90;
  1795. // start render
  1796. t := 0;
  1797. sdiv2 := Stacks div 2;
  1798. for n := 0 to Stacks - 1 do
  1799. begin
  1800. if fs > 0 then
  1801. begin
  1802. if n < sdiv2 then
  1803. t2 := fs - fs * Sqr((sdiv2 - n) / sdiv2)
  1804. else
  1805. t2 := fs + Sqr((n - sdiv2) / (sdiv2 - 1)) * (1 - fs);
  1806. end
  1807. else
  1808. t2 := (n + 1) / Stacks;
  1809. RenderBand(Lerp(1, 90, t), Lerp(1, 90, t2));
  1810. t := t2;
  1811. end;
  1812. RenderDeepBand(1);
  1813. FreeMem(sinTable);
  1814. FreeMem(cosTable);
  1815. end;
  1816. // -------------------------------------------------------------
  1817. initialization
  1818. // -------------------------------------------------------------
  1819. RegisterClasses([TGLSkyBox, TGLSkyDome, TGLEarthSkyDome]);
  1820. end.