
{*******************************************************}
{                                                       }
{       Układ planetarny - Delphi                       }
{       Wykorzystanie komponentu GlBox i OpenGL         }
{                                                       }
{       autor: Waldemara Pokuta - 2003                  }
{                                                       }
{*******************************************************}

unit Unit1;

interface

uses
  Windows, SysUtils, Classes, Forms, Dialogs, GLBox, OpenGl, ExtCtrls,
  Controls, StdCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    ScrollBar1: TScrollBar;
    ScrollBar2: TScrollBar;
    ScrollBar3: TScrollBar;
    Timer1: TTimer;
    gl: TGLBox;
    procedure UstawOswietlenie;
    procedure UstalRozmiar;
    procedure glRender(Sender: TObject);
    procedure glSetupRC(Sender: TObject);
    procedure glResize(Sender: TObject);
    procedure ScrollBar1Change(Sender: TObject);
    procedure ScrollBar2Change(Sender: TObject);
    procedure ScrollBar3Change(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  procedure Szescian(rozm: real);
  procedure Czworoscian(rozm: real);
  procedure Osmioscian(rozm: real);
  procedure Czternastoscian(rozm: real);
  procedure RysujSlonce;
  procedure RysujWenus;
  procedure RysujKsiezyc;
  procedure RysujZiemia;
var
  Form1: TForm1;
  katx : real = 0;
  katy : real = 0;
  katz : real = 0;
implementation

{$R *.DFM}

procedure Szescian(rozm: real);
var
  i: integer;
begin
  glPushMatrix;
  for i := 0 to 5 do begin
    glBegin(GL_QUADS);
      glNormal3f(    0,    0,    1);
      glVertex3f(-rozm, rozm, rozm);
      glVertex3f(-rozm,-rozm, rozm);
      glVertex3f( rozm,-rozm, rozm);
      glVertex3f( rozm, rozm, rozm);
    glEnd;
    // Obrót na kolejną ścianę
    if odd(i) then glRotatef(90, 0, 1, 0) else glRotatef(90, 1, 0, 0);
  end;
  glPopMatrix;
end;

procedure Czworoscian(rozm: real);
const
  wsp = 0.6436;
var
  i: integer;
begin
  glPushMatrix;
  for i := 0 to 3 do begin
    glBegin(GL_TRIANGLES);
      glNormal3f(  wsp,  wsp,  wsp);
      glVertex3f(-rozm, rozm, rozm);
      glVertex3f( rozm, rozm,-rozm);
      glVertex3f( rozm,-rozm, rozm);
    glEnd;
    // Obrót na kolejną ścianę
    if odd(i) then glRotatef(180, 1, 0, 0) else glRotatef(180, 0, 1, 0);
  end;
  glPopMatrix;
end;

procedure Osmioscian(rozm: real);
const
  wsp = 0.6436;
var
  i: integer;
begin
  glPushMatrix;
  for i := 0 to 7 do begin
    glBegin(GL_TRIANGLES);
      glNormal3f(  wsp,  wsp,  wsp);
      glVertex3f(    0,    0, rozm);
      glVertex3f( rozm,    0,    0);
      glVertex3f(    0, rozm,    0);
    glEnd;
    // Obrót na kolejną ścianę
    glRotatef(90, 1, 0, 0);
    // Po czterech ścianach odwracamy figurę o 180 stopni
    if i = 3 then glRotatef(180, 0, 1, 0);
  end;
  glPopMatrix;
end;

procedure Czternastoscian(rozm: real);
const
  wsp = 0.6436;
var
  i: integer;
begin
  // Ściany trójkątne
  glPushMatrix;
  for i := 0 to 7 do begin
    glBegin(GL_TRIANGLES);
      glNormal3f(  wsp,  wsp,  wsp);
      glVertex3f(    0, rozm, rozm);
      glVertex3f( rozm, rozm,    0);
      glVertex3f( rozm,    0, rozm);
    glEnd;
    // Obrót na kolejną ścianę
    glRotatef(90, 1, 0, 0);
    // Po czterech ścianach odwracamy figurę o 180 stopni
    if i = 3 then glRotatef(180, 0, 1, 0);
  end;
  glPopMatrix;
  // Ściany kwadratowe
  glPushMatrix;
  for i := 0 to 5 do begin
    glBegin(GL_QUADS);
      glNormal3f(    0,    0,    1);
      glVertex3f(    0, rozm, rozm);
      glVertex3f( rozm,    0, rozm);
      glVertex3f(    0,-rozm, rozm);
      glVertex3f(-rozm,    0, rozm);
    glEnd;
    // Obrót na kolejną ścianę
    if odd(i) then glRotatef(90, 0, 1, 0) else glRotatef(90, 1, 0, 0);
  end;
  glPopMatrix;
end;

procedure TForm1.UstawOswietlenie;
const
  ambient: TGLArrayf4 = ( 0.3, 0.3, 0.3, 1.0);
  l0amb  : TGLArrayf4 = ( 0.0, 0.0, 0.0, 1.0);
  l0dif  : TGLArrayf4 = ( 0.9, 0.9, 0.9, 1.0);
  l0spec : TGLArrayf4 = ( 1.0, 1.0, 1.0, 1.0);
  l0pos  : TGLArrayf4 = ( 0.0, 0.0, 0.0, 1.0);
  l1amb  : TGLArrayf4 = ( 0.0, 0.0, 0.0, 1.0);
  l1dif  : TGLArrayf4 = ( 0.5, 0.5, 0.5, 1.0);
  l1spec : TGLArrayf4 = ( 0.5, 0.2, 0.5, 1.0);
  l1pos  : TGLArrayf4 = (-200,-250,-300, 1.0);
  l2amb  : TGLArrayf4 = ( 0.0, 0.0, 0.0, 1.0);
  l2dif  : TGLArrayf4 = ( 0.5, 0.5, 0.5, 1.0);
  l2spec : TGLArrayf4 = ( 1.0, 1.0, 1.0, 1.0);
  l2pos  : TGLArrayf4 = ( 200, 400, 350, 1.0);
begin
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @ambient);
  glLightfv(GL_LIGHT0,  GL_AMBIENT,  @l0amb);
  glLightfv(GL_LIGHT0,  GL_DIFFUSE,  @l0dif);
  glLightfv(GL_LIGHT0, GL_SPECULAR, @l0spec);
  glLightfv(GL_LIGHT0, GL_POSITION,  @l0pos);
  glEnable (GL_LIGHT0);
  glLightfv(GL_LIGHT1,  GL_AMBIENT,  @l1amb);
  glLightfv(GL_LIGHT1,  GL_DIFFUSE,  @l1dif);
  glLightfv(GL_LIGHT1, GL_SPECULAR, @l1spec);
  glLightfv(GL_LIGHT1, GL_POSITION,  @l1pos);
  glEnable (GL_LIGHT1);
  glLightfv(GL_LIGHT2,  GL_AMBIENT,  @l2amb);
  glLightfv(GL_LIGHT2,  GL_DIFFUSE,  @l2dif);
  glLightfv(GL_LIGHT2, GL_SPECULAR, @l2spec);
  glLightfv(GL_LIGHT2, GL_POSITION,  @l2pos);
  glEnable (GL_LIGHT2);
end;

procedure RysujSlonce;
const
  kat_sl: real = 0;
begin
  glPushMatrix;
  // Obrot wokół wektora (0.5, 1, 0) - wokół własnej osi
  glRotatef(kat_sl, 0.5, 1, 0);
  // Rysowanie Słońca
  Czternastoscian(20);
  kat_sl := kat_sl+1;      // Zmiana kąta wokół własnej osi
  glPopMatrix;
end;

procedure RysujWenus;
const
  kat_we_sl: real = 0;
  kat_we   : real = 0;
begin
  glPushMatrix;
  // Przesunięcie na orbitę o promieniu 50 pod katem kat_we_sl
  glRotatef(kat_we_sl, 0, 1, 0);
  glTranslatef(50, 0, 0);
  glRotatef(-kat_we_sl, 0, 1, 0);
  // Obrot wokół wektora (1, 0.5, 0.2) - wokół własnej osi
  glRotatef(kat_we, 1, 0.5, 0.2);
  // Rysowanie Wenus
  Osmioscian(10);
  kat_we_sl := kat_we_sl+1.5;    // Zmiana kąta na orbicie
  kat_we    := kat_we-5;         // Zmiana kąta wokół własnej osi
  glPopMatrix;
end;

procedure RysujKsiezyc;
const
  kat_ks_zi: real = 0;
  kat_ks   : real = 0;
begin
  glPushMatrix;
  // Odchylenie od orbity Ziemi o 75 stopni
  glRotatef(75, 1, 0, 1);
  // Przesunięcie na orbitę o promieniu 30 pod katem kat_ks_zi
  glRotatef(kat_ks_zi, 0, 1, 0);
  glTranslatef(30, 0, 0);
  glRotatef(-kat_ks_zi, 0, 1, 0);
  // Obrot wokół wektora (1, 0.5, 0.2) - wokół własnej osi
  glRotatef(kat_ks, 1, 0.5, 0.2);
  // Rysowanie Księżyca
  Czworoscian(5);
  kat_ks_zi := kat_ks_zi+7;    // Zmiana kąta na orbicie
  kat_ks    := kat_ks-5;       // Zmiana kąta wokół własnej osi
  glPopMatrix;
end;

procedure RysujZiemia;
const
  kat_zi_sl: real = 0;
  kat_zi   : real = 0;
begin
  glPushMatrix;
  // Odchylenie od orbity Wenus o 45 stopni
  glRotatef(45, 0.5, 0, 1);
  // Przesunięcie na orbitę o promieniu 100 pod katem kat_zi_sl
  glRotatef(kat_zi_sl, 0, 1, 0);
  glTranslatef(100, 0, 0);
  glRotatef(-kat_zi_sl, 0, 1, 0);
  // Rysowanie Księżyca według odchylonej orbity Ziemi
  RysujKsiezyc;
  // Anulowanie odchylenia po przesunieciu
  glRotatef(-45, 0.5, 0, 1);
  // Obrot wokół wektora (0.7, 0.3, 0.5) - wokół własnej osi
  glRotatef(kat_zi, 0.7, 0.3, 0.5);
  // Rysowanie Ziemi
  Szescian(10);
  kat_zi_sl := kat_zi_sl-1;  // Zmiana kąta na orbicie
  kat_zi    := kat_zi+6;     // Zmiana kąta wokół własnej osi
  glPopMatrix;
end;

procedure TForm1.UstalRozmiar;
var
  w, h: integer;
begin
  // Ustalenie widoku perspektywicznego
  w := gl.Width;
  h := gl.Height;
  if h = 0 then h := 1;
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(60, w/h, 1, 400);
  glMatrixMode(GL_MODELVIEW);
end;

procedure TForm1.glRender(Sender: TObject);
var
  x, y, z: real;
const
  amb_dif: TGLArrayf4 = (0.8,0.8,0.2,1);
  spec   : TGLArrayf4 = (0.5,0.5,0.5,1);
  emis   : TGLArrayf4 = (0.1,0.1,0.1,1);
begin
  glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  // Materiał figur
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, @amb_dif);
  glMaterialfv(GL_FRONT_AND_BACK,            GL_SPECULAR,    @spec);
  glMaterialf (GL_FRONT_AND_BACK,           GL_SHININESS,       80);
  glMaterialfv(GL_FRONT_AND_BACK,            GL_EMISSION,    @emis);
  // Załadowanie przekształcenia tożsamościowego
  glLoadIdentity;
  // Położenie kamery
  glRotatef(katz, 0, 0, 1);
  x :=  200*Sin(katx)*Cos(katy);
  y :=  200*Sin(katy);
  z := -200*Cos(katx)*Cos(katy);
  gluLookAt(x, y, z, 0, 0, 0, 0, 1, 0);
  // Oświetlenie
  UstawOswietlenie;
  // Układ współrzędnych
  glBegin(GL_LINES);
    glVertex3f(  0,  0,-80);
    glVertex3f(  0,  0, 80);
    glVertex3f(  0,-80,  0);
    glVertex3f(  0, 80,  0);
    glVertex3f(-80,  0,  0);
    glVertex3f( 80,  0,  0);
  glEnd;
  // Rysowanie słońca
  RysujSlonce;
  // Rysowanie planet
  RysujWenus;
  RysujZiemia;
end;

procedure TForm1.glSetupRC(Sender: TObject);
begin
  // Wartości początkowe
  glClearColor(0, 0, 0, 1);
  UstalRozmiar;
  glEnable(GL_LIGHTING);
  glEnable(GL_DEPTH_TEST);
  Timer1.Enabled := TRUE;
end;

procedure TForm1.glResize(Sender: TObject);
begin
  UstalRozmiar;
end;

procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
  // Obrót obserwatora wzdłuż równoleżnika - kat w radianach
  katx := ScrollBar1.Position*2*PI/200;
  gl.Invalidate;
end;

procedure TForm1.ScrollBar2Change(Sender: TObject);
begin
  // Obrót obserwatora wzdłuż południka - kat w radianach
  katy := ScrollBar2.Position*PI/200;
  gl.Invalidate;
end;

procedure TForm1.ScrollBar3Change(Sender: TObject);
begin
  // Obrót obserwatora wokół własnej osi - kąt w stopniach
  katz := ScrollBar3.Position*360/200;
  gl.Invalidate;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // Następna klatka
  gl.Invalidate;
end;

end.
