fEarth.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. unit fEarth;
  2. interface
  3. uses
  4. Winapi.OpenGL,
  5. Winapi.OpenGLext,
  6. System.SysUtils,
  7. System.Classes,
  8. System.Types,
  9. System.Math,
  10. Vcl.Graphics,
  11. Vcl.Controls,
  12. Vcl.Forms,
  13. Vcl.ExtCtrls,
  14. Vcl.Imaging.Jpeg,
  15. GLS.VectorTypes,
  16. GLS.VectorGeometry,
  17. GLS.Material,
  18. GLS.Cadencer,
  19. GLS.LensFlare,
  20. GLS.Scene,
  21. GLS.Objects,
  22. GLS.Coordinates,
  23. GLS.SkyDome,
  24. GLS.SceneViewer,
  25. GLS.Texture,
  26. GLS.RenderContextInfo,
  27. GLS.Color,
  28. GLS.State,
  29. GLS.Utils,
  30. GLS.Context,
  31. GLS.TextureFormat,
  32. GLSL.TextureShaders,
  33. GLS.BaseClasses;
  34. type
  35. TForm1 = class(TForm)
  36. GLScene: TGLScene;
  37. GLSceneViewer: TGLSceneViewer;
  38. GLCamera: TGLCamera;
  39. SPEarth: TGLSphere;
  40. LSSun: TGLLightSource;
  41. GLDirectOpenGL1: TGLDirectOpenGL;
  42. GLCadencer: TGLCadencer;
  43. Timer1: TTimer;
  44. SPMoon: TGLSphere;
  45. DCEarthSystem: TGLDummyCube;
  46. DCMoon: TGLDummyCube;
  47. GLLensFlare1: TGLLensFlare;
  48. GLMaterialLibrary: TGLMaterialLibrary;
  49. EarthCombiner: TGLTexCombineShader;
  50. GLCameraControler: TGLCamera;
  51. GLSkyDome: TGLSkyDome;
  52. ConstellationLines: TGLLines;
  53. procedure FormCreate(Sender: TObject);
  54. procedure GLDirectOpenGL1Render(Sender: TObject; var rci: TGLRenderContextInfo);
  55. procedure Timer1Timer(Sender: TObject);
  56. procedure GLCadencerProgress(Sender: TObject; const deltaTime,
  57. newTime: Double);
  58. procedure GLSceneViewerMouseDown(Sender: TObject;
  59. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  60. procedure GLSceneViewerMouseMove(Sender: TObject; Shift: TShiftState;
  61. X, Y: Integer);
  62. procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
  63. WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  64. procedure GLSceneViewerDblClick(Sender: TObject);
  65. procedure FormKeyPress(Sender: TObject; var Key: Char);
  66. procedure GLSceneViewerBeforeRender(Sender: TObject);
  67. private
  68. procedure LoadConstellationLines;
  69. function AtmosphereColor(const rayStart, rayEnd: TGLVector): TColorVector;
  70. public
  71. ConstellationsAlpha: Single;
  72. TimeMultiplier: Single;
  73. mx, my, dmx, dmy: Integer;
  74. HighResResourcesLoaded: Boolean;
  75. CameraTimeSteps: Single;
  76. radius, invAtmosphereHeight: Single;
  77. sunPos, eyePos, lightingVector: TGLVector;
  78. diskNormal, diskRight, diskUp: TGLVector;
  79. end;
  80. var
  81. Form1: TForm1;
  82. const
  83. cOpacity: Single = 5;
  84. // unrealisticly thick atmospheres look better :)
  85. cAtmosphereRadius: Single = 0.55;
  86. // use value slightly lower than actual radius, for antialiasing effect
  87. cPlanetRadius: Single = 0.495;
  88. cLowAtmColor: TColorVector = (X:1; Y:1; Z:1; W:1);
  89. cHighAtmColor: TColorVector = (X:0; Y:0; Z:1; W:1);
  90. cIntDivTable: array[2..20] of Single =
  91. (1 / 2, 1 / 3, 1 / 4, 1 / 5, 1 / 6, 1 / 7, 1 / 8, 1 / 9, 1 / 10,
  92. 1 / 11, 1 / 12, 1 / 13, 1 / 14, 1 / 15, 1 / 16, 1 / 17, 1 / 18, 1 / 19, 1 / 20);
  93. //-----------------------------------------
  94. implementation
  95. //-----------------------------------------
  96. {$R *.dfm}
  97. uses
  98. // accurate movements left for later... or the astute reader
  99. USolarSystem;
  100. procedure TForm1.FormCreate(Sender: TObject);
  101. var
  102. FileName: String;
  103. begin
  104. SetCurrentDir(ExtractFilePath(ParamStr(0)));
  105. FileName := 'Data\Yale_BSC.stars';
  106. GLSkyDome.Bands.Clear;
  107. if FileExists(FileName) then
  108. GLSkyDome.Stars.LoadStarsFile(FileName);
  109. LoadConstellationLines;
  110. TimeMultiplier := 1;
  111. end;
  112. procedure TForm1.GLSceneViewerBeforeRender(Sender: TObject);
  113. begin
  114. GLLensFlare1.PreRender(Sender as TGLSceneBuffer);
  115. // if no multitexturing or no combiner support, turn off city lights
  116. GLMaterialLibrary.Materials[0].Shader := EarthCombiner;
  117. GLMaterialLibrary.Materials[0].Texture2Name := 'earthNight';
  118. end;
  119. function TForm1.AtmosphereColor(const rayStart, rayEnd: TGLVector)
  120. : TColorVector;
  121. var
  122. i, n: Integer;
  123. atmPoint, normal: TGLVector;
  124. altColor: TColorVector;
  125. alt, rayLength, contrib, decay, intensity, invN: Single;
  126. begin
  127. Result := clrTransparent;
  128. rayLength := VectorDistance(rayStart, rayEnd);
  129. n := Round(3 * rayLength * invAtmosphereHeight) + 2;
  130. if n > 10 then
  131. n := 10;
  132. invN := cIntDivTable[n]; // 1/n;
  133. contrib := rayLength * invN * cOpacity;
  134. decay := 1 - contrib * 0.5;
  135. contrib := contrib * (1 / 1.1);
  136. for i := n - 1 downto 0 do
  137. begin
  138. VectorLerp(rayStart, rayEnd, i * invN, atmPoint);
  139. // diffuse lighting normal
  140. normal := VectorNormalize(atmPoint);
  141. // diffuse lighting intensity
  142. intensity := VectorDotProduct(normal, lightingVector) + 0.1;
  143. if PInteger(@intensity)^ > 0 then
  144. begin
  145. // sample on the lit side
  146. intensity := intensity * contrib;
  147. alt := (VectorLength(atmPoint) - cPlanetRadius) * invAtmosphereHeight;
  148. VectorLerp(cLowAtmColor, cHighAtmColor, alt, altColor);
  149. Result.X := Result.X * decay + altColor.X * intensity;
  150. Result.Y := Result.Y * decay + altColor.Y * intensity;
  151. Result.Z := Result.Z * decay + altColor.Z * intensity;
  152. end
  153. else
  154. begin
  155. // sample on the dark sid
  156. Result.X := Result.X * decay;
  157. Result.Y := Result.Y * decay;
  158. Result.Z := Result.Z * decay;
  159. end;
  160. end;
  161. Result.W := n * contrib * cOpacity * 0.1;
  162. end;
  163. procedure TForm1.GLDirectOpenGL1Render(Sender: TObject; var rci: TGLRenderContextInfo);
  164. function ComputeColor(var rayDest: TGLVector; mayHitGround: Boolean): TColorVector;
  165. var
  166. ai1, ai2, pi1, pi2: TGLVector;
  167. rayVector: TGLVector;
  168. begin
  169. rayVector := VectorNormalize(VectorSubtract(rayDest, eyePos));
  170. if RayCastSphereIntersect(eyePos, rayVector, NullHmgPoint, cAtmosphereRadius, ai1, ai2) > 1 then
  171. begin
  172. // atmosphere hit
  173. if mayHitGround and (RayCastSphereIntersect(eyePos, rayVector, NullHmgPoint, cPlanetRadius, pi1, pi2) > 0) then
  174. begin
  175. // hit ground
  176. Result := AtmosphereColor(ai1, pi1);
  177. end
  178. else
  179. begin
  180. // through atmosphere only
  181. Result := AtmosphereColor(ai1, ai2);
  182. end;
  183. rayDest := ai1;
  184. end
  185. else
  186. Result := clrTransparent;
  187. end;
  188. const
  189. cSlices = 60;
  190. var
  191. i, j, k0, k1: Integer;
  192. cosCache, sinCache: array[0..cSlices] of Single;
  193. pVertex, pColor: PVectorArray;
  194. begin
  195. sunPos := LSSun.AbsolutePosition;
  196. eyepos := GLCamera.AbsolutePosition;
  197. diskNormal := VectorNegate(eyePos);
  198. NormalizeVector(diskNormal);
  199. diskRight := VectorCrossProduct(GLCamera.AbsoluteUp, diskNormal);
  200. NormalizeVector(diskRight);
  201. diskUp := VectorCrossProduct(diskNormal, diskRight);
  202. NormalizeVector(diskUp);
  203. invAtmosphereHeight := 1 / (cAtmosphereRadius - cPlanetRadius);
  204. lightingVector := VectorNormalize(sunPos); // sun at infinity
  205. PrepareSinCosCache(sinCache, cosCache, 0, 360);
  206. GetMem(pVertex, 2 * (cSlices + 1) * SizeOf(TGLVector));
  207. GetMem(pColor, 2 * (cSlices + 1) * SizeOf(TGLVector));
  208. rci.GLStates.DepthWriteMask := False;
  209. rci.GLStates.Disable(stLighting);
  210. rci.GLStates.Enable(stBlend);
  211. rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  212. for i := 0 to 13 do
  213. begin
  214. if i < 5 then
  215. radius := cPlanetRadius * Sqrt(i * (1 / 5))
  216. else
  217. radius := cPlanetRadius + (i - 5.1) * (cAtmosphereRadius - cPlanetRadius) * (1 / 6.9);
  218. radius := SphereVisibleRadius(VectorLength(eyePos), radius);
  219. k0 := (i and 1) * (cSlices + 1);
  220. k1 := (cSlices + 1) - k0;
  221. for j := 0 to cSlices do
  222. begin
  223. VectorCombine(diskRight, diskUp,
  224. cosCache[j] * radius, sinCache[j] * radius,
  225. pVertex[k0 + j]);
  226. if i < 13 then
  227. pColor[k0 + j] := ComputeColor(pVertex[k0 + j], i <= 7);
  228. if i = 0 then
  229. Break;
  230. end;
  231. if i > 1 then
  232. begin
  233. if i = 13 then
  234. begin
  235. glBegin(GL_QUAD_STRIP);
  236. for j := cSlices downto 0 do
  237. begin
  238. glColor4fv(@pColor[k1 + j]);
  239. glVertex3fv(@pVertex[k1 + j]);
  240. glColor4fv(@clrTransparent);
  241. glVertex3fv(@pVertex[k0 + j]);
  242. end;
  243. glEnd;
  244. end
  245. else
  246. begin
  247. glBegin(GL_QUAD_STRIP);
  248. for j := cSlices downto 0 do
  249. begin
  250. glColor4fv(@pColor[k1 + j]);
  251. glVertex3fv(@pVertex[k1 + j]);
  252. glColor4fv(@pColor[k0 + j]);
  253. glVertex3fv(@pVertex[k0 + j]);
  254. end;
  255. glEnd;
  256. end;
  257. end
  258. else if i = 1 then
  259. begin
  260. glBegin(GL_TRIANGLE_FAN);
  261. glColor4fv(@pColor[k1]);
  262. glVertex3fv(@pVertex[k1]);
  263. for j := k0 + cSlices downto k0 do
  264. begin
  265. glColor4fv(@pColor[j]);
  266. glVertex3fv(@pVertex[j]);
  267. end;
  268. glEnd;
  269. end;
  270. end;
  271. rci.GLStates.DepthWriteMask := True;
  272. FreeMem(pVertex);
  273. FreeMem(pColor);
  274. end;
  275. procedure TForm1.LoadConstellationLines;
  276. var
  277. sl, line: TStrings;
  278. pos1, pos2: TAffineVector;
  279. function LonLatToPos(lon, lat: Single): TAffineVector;
  280. var
  281. f: Single;
  282. begin
  283. SinCosine(lat * (PI / 180), Result.Y, f);
  284. SinCosine(lon * (360 / 24 * PI / 180), f,
  285. Result.X, Result.Z);
  286. end;
  287. var
  288. i: Integer;
  289. begin
  290. sl := TStringList.Create;
  291. line := TStringList.Create;
  292. sl.LoadFromFile('Data\Constellations.dat');
  293. for i := 0 to sl.Count - 1 do
  294. begin
  295. line.CommaText := sl[i];
  296. pos1 := LonLatToPos(StrToFloatDef(line[0], 0), StrToFloatDef(line[1], 0));
  297. ConstellationLines.AddNode(pos1);
  298. pos2 := LonLatToPos(StrToFloatDef(line[2], 0), StrToFloatDef(line[3], 0));
  299. ConstellationLines.AddNode(pos2);
  300. end;
  301. sl.Free;
  302. line.Free;
  303. end;
  304. procedure TForm1.Timer1Timer(Sender: TObject);
  305. begin
  306. Caption := Format('Earth ' + '%.1f FPS', [GLSceneViewer.FramesPerSecond]);
  307. GLSceneViewer.ResetPerformanceMonitor;
  308. end;
  309. procedure TForm1.GLCadencerProgress(Sender: TObject; const deltaTime,
  310. newTime: Double);
  311. //var
  312. // d : Double;
  313. // p : TAffineVector;
  314. begin
  315. // d:=GMTDateTimeToJulianDay(Now-2+newTime*timeMultiplier);
  316. // make earth rotate
  317. SPEarth.TurnAngle := SPEarth.TurnAngle + deltaTime * timeMultiplier;
  318. { p:=ComputePlanetPosition(cSunOrbitalElements, d);
  319. ScaleVector(p, 0.5*cAUToKilometers*(1/cEarthRadius));
  320. LSSun.Position.AsAffineVector:=p; }
  321. // moon rotates on itself and around earth (not sure about the rotation direction!)
  322. { p:=ComputePlanetPosition(cMoonOrbitalElements, d);
  323. ScaleVector(p, 0.5*cAUToKilometers*(1/cEarthRadius)); }
  324. DCMoon.TurnAngle := DCMoon.TurnAngle + deltaTime * timeMultiplier / 29.5;
  325. SPMoon.TurnAngle := 180 - DCMoon.TurnAngle;
  326. // honour camera movements
  327. if (dmy <> 0) or (dmx <> 0) then
  328. begin
  329. GLCameraControler.MoveAroundTarget(ClampValue(dmy * 0.3, -5, 5),
  330. ClampValue(dmx * 0.3, -5, 5));
  331. dmx := 0;
  332. dmy := 0;
  333. end;
  334. // this gives us smoother camera movements
  335. cameraTimeSteps := cameraTimeSteps + deltaTime;
  336. while cameraTimeSteps > 0.005 do
  337. begin
  338. GLCamera.Position.AsVector := VectorLerp(GLCamera.Position.AsVector,
  339. GLCameraControler.Position.AsVector, 0.05);
  340. cameraTimeSteps := cameraTimeSteps - 0.005;
  341. end;
  342. // smooth constellation appearance/disappearance
  343. with ConstellationLines.LineColor do
  344. if Alpha <> constellationsAlpha then
  345. begin
  346. Alpha := ClampValue(Alpha + Sign(constellationsAlpha - Alpha) * deltaTime, 0, 0.5);
  347. ConstellationLines.Visible := (Alpha > 0);
  348. end;
  349. end;
  350. procedure TForm1.GLSceneViewerMouseDown(Sender: TObject;
  351. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  352. begin
  353. mx := x;
  354. my := y;
  355. end;
  356. procedure TForm1.GLSceneViewerMouseMove(Sender: TObject;
  357. Shift: TShiftState; X, Y: Integer);
  358. begin
  359. if Shift = [ssLeft] then
  360. begin
  361. dmx := dmx + (mx - x);
  362. dmy := dmy + (my - y);
  363. end
  364. else if Shift = [ssRight] then
  365. GLCamera.FocalLength := GLCamera.FocalLength * Power(1.05, (my - y) * 0.1);
  366. mx := x;
  367. my := y;
  368. end;
  369. procedure TForm1.FormMouseWheel(Sender: TObject; Shift: TShiftState;
  370. WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  371. var
  372. f: Single;
  373. begin
  374. if (WheelDelta > 0) or (GLCameraControler.Position.VectorLength > 0.90) then
  375. begin
  376. f := Power(1.05, WheelDelta * (1 / 120));
  377. GLCameraControler.AdjustDistanceToTarget(f);
  378. end;
  379. Handled := True;
  380. end;
  381. procedure TForm1.GLSceneViewerDblClick(Sender: TObject);
  382. begin
  383. GLSceneViewer.OnMouseMove := nil;
  384. if WindowState = wsMaximized then
  385. begin
  386. WindowState := wsNormal;
  387. BorderStyle := bsSizeToolWin;
  388. end
  389. else
  390. begin
  391. BorderStyle := bsNone;
  392. WindowState := wsMaximized;
  393. end;
  394. GLSceneViewer.OnMouseMove := GLSceneViewerMouseMove;
  395. end;
  396. procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
  397. procedure LoadHighResTexture(libMat: TGLLibMaterial; const fileName: string);
  398. begin
  399. if FileExists(fileName) then
  400. begin
  401. libMat.Material.Texture.Compression := tcStandard;
  402. libMat.Material.Texture.Image.LoadFromFile(fileName);
  403. end;
  404. end;
  405. begin
  406. case Key of
  407. #27: Close;
  408. 'm', 'M':
  409. begin
  410. GLCamera.MoveTo(SPMoon);
  411. GLCameraControler.MoveTo(SPMoon);
  412. GLCamera.TargetObject := SPMoon;
  413. GLCameraControler.TargetObject := SPMoon;
  414. end;
  415. 'e', 'E':
  416. begin
  417. GLCamera.MoveTo(DCEarthSystem);
  418. GLCameraControler.MoveTo(DCEarthSystem);
  419. GLCamera.TargetObject := DCEarthSystem;
  420. GLCameraControler.TargetObject := DCEarthSystem;
  421. end;
  422. 'h': if not highResResourcesLoaded then
  423. begin
  424. GLSceneViewer.Cursor := crHourGlass;
  425. try
  426. if DirectoryExists('Data') then
  427. ChDir('Data');
  428. with GLMaterialLibrary do
  429. begin
  430. LoadHighResTexture(Materials[0], 'land_ocean_ice_4096.jpg');
  431. LoadHighResTexture(Materials[1], 'land_ocean_ice_lights_4096.jpg');
  432. LoadHighResTexture(Materials[2], 'moon_2048.jpg');
  433. end;
  434. if FileExists('Hipparcos_9.0.stars') then
  435. begin
  436. GLSkyDome.Stars.Clear;
  437. GLSkyDome.Stars.LoadStarsFile('Hipparcos_9.0.stars');
  438. GLSkyDome.StructureChanged;
  439. end;
  440. GLSceneViewer.Buffer.AntiAliasing := aa2x;
  441. finally
  442. GLSceneViewer.Cursor := crDefault;
  443. end;
  444. highResResourcesLoaded := True;
  445. end;
  446. 'c': constellationsAlpha := 0.5 - constellationsAlpha;
  447. '0'..'9': timeMultiplier := Power(Integer(Key) - Integer('0'), 3);
  448. end;
  449. end;
  450. end.