unit StarField;

interface

uses
  _Geometry,    
  Frost.Renderers,  
  Frost.Engine,
  Frost.GL,
  OpenGL;

const
  STARS = 1200;

type
TStarFieldMaterial = class (TMaterial)
    FTextureId: Cardinal;
  public
    constructor Create;
    procedure Use; override;
  end;

TStarField = class(TComponent)
  private
    FMesh: TMesh;
    FRadius: Single;
  public
    class function CreatePlanetoidMesh(Radius: Single): TMesh;
    constructor Create(GameObject: TGameObject; Radius: Single);
    procedure OnStart; override;
    procedure OnUpdate(const Delta: Double); override;
    procedure OnDestroy; override;
    property Radius: Single read FRadius;
  public
    class var Instance: TStarField;
  end;


implementation
{$REGION 'Material'} 
{$REGION 'Generate StarField Texture'}
procedure GenerateStarFieldTexture(var TextureIndex: Cardinal);
var
  i, j: Word;
  Value: Single;
  Brightness: Byte;
  TextureBit: array of Byte;
  PTexture: Pointer;
const
  TEXTURE_SIZE = 32;
  PIXEL_BYTES = 4;
begin
  SetLength(TextureBit, TEXTURE_SIZE * TEXTURE_SIZE * PIXEL_BYTES);
  For i := 0 to TEXTURE_SIZE - 1 do
    For j := 0 to TEXTURE_SIZE - 1 do
      begin
        Value := sqrt((i - TEXTURE_SIZE / 2) * (i - TEXTURE_SIZE / 2) + (j - TEXTURE_SIZE / 2) * (j - TEXTURE_SIZE / 2));
        Value := (200 + Random(150)) * Value / (TEXTURE_SIZE / 2) * Value / (TEXTURE_SIZE / 2);
        if Value > 255 then 
          Value := 255;
        Brightness := 255 - Trunc( Value);//Cells
        TextureBit[i * 4 + (j) * TEXTURE_SIZE * 4 + 0]:= Brightness;
        TextureBit[i * 4 + (j) * TEXTURE_SIZE * 4 + 1]:= Brightness;
        TextureBit[i * 4 + (j) * TEXTURE_SIZE * 4 + 2]:= Brightness;
        TextureBit[i * 4 + (j) * TEXTURE_SIZE * 4 + 3]:= Brightness;
      end;
  PTexture := TextureBit;
  TextureIndex := GenerateTexture(PTexture, TEXTURE_SIZE, TEXTURE_SIZE );
end;
{$ENDREGION}
constructor TStarFieldMaterial.Create();
begin
  GenerateStarFieldTexture(FTextureId);
end;

procedure TStarFieldMaterial.Use;
begin
  glEnable(GL_TEXTURE_2D);   
  glDisable(GL_LIGHTING);    
  glEnable(GL_LIGHT0);
  glColor3f(1, 1, 1);
  glBindTexture(GL_TEXTURE_2D, FTextureId);
end;
{$ENDREGION}

{$REGION 'TStarField'}
{$REGION 'CreateStarFieldMesh'}
class function TStarField.CreatePlanetoidMesh(Radius: Single): TMesh;
var
  Vertex:   TVertexes;
  Textures: TTextures;
  Faces: TFaces;
  i: Integer;
  Rotation: TMatrix3;
const
  Size = 0.2;
begin
  SetLength(Vertex, STARS * 4);
  SetLength(Textures, STARS * 4);
  SetLength(Faces, STARS * 2);

  for i := 0 to STARS - 1 do
  begin
    Vertex[i * 4 + 0] := Vector3( Size, 0, Radius);
    Vertex[i * 4 + 1] := Vector3( 0, Size, Radius);
    Vertex[i * 4 + 2] := Vector3(-Size, 0, Radius);
    Vertex[i * 4 + 3] := Vector3( 0,-Size, Radius);
    Rotation := Rotate(Random(360), Vector3(0, 0, 1)) * Rotate(Random(360), Vector3(0, 1, 0)) * Rotate(Random(360), Vector3(1, 0, 0));
    Vertex[i * 4 + 0] := Rotation * Vertex[i * 4 + 0];
    Vertex[i * 4 + 1] := Rotation * Vertex[i * 4 + 1];
    Vertex[i * 4 + 2] := Rotation * Vertex[i * 4 + 2];
    Vertex[i * 4 + 3] := Rotation * Vertex[i * 4 + 3];
    Textures[i * 4 + 0] := Vector2( 0, 0);
    Textures[i * 4 + 1] := Vector2( 1, 0);
    Textures[i * 4 + 2] := Vector2( 1, 1);
    Textures[i * 4 + 3] := Vector2( 0, 1);
  end;
         
  for i := 0 to STARS - 1 do
  begin
    Faces[i * 2 + 0] := Face(i * 4 + 0, i * 4 + 2, i * 4 + 1);   
    Faces[i * 2 + 1] := Face(i * 4 + 0, i * 4 + 3, i * 4 + 2);
  end;
      
  Result := TMesh.Create(Faces, Vertex, Textures, nil);
end;          

{$ENDREGION}
                   
constructor TStarField.Create(GameObject: TGameObject; Radius: Single);
begin
  FRadius := Radius;
  SetGameObject(GameObject);
end;

procedure TStarField.OnStart;
var
   MeshRenderer: TMeshRenderer;
begin
  Instance := Self;
  if FMesh = nil then
    FMesh := CreatePlanetoidMesh(FRadius);
  MeshRenderer := TMeshRenderer.Create(GameObject);
  MeshRenderer.Mesh := FMesh;
  MeshRenderer.Material := TStarFieldMaterial.Create;
end;

procedure TStarField.OnUpdate(const Delta: Double);
begin

end;

procedure TStarField.OnDestroy;
begin

end;
{$ENDREGION}
end.
