GXS.Skydome.pas 47 KB

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