unit Planetoid;

interface

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

const
  MAX_PLANETOIDS = 12;

type
        
TPlanetoidMaterial = class (TMaterial)
  private
    TextureId: Cardinal;
  public
    constructor Create;
    procedure Use; override;
end;

TPlanetoid = class(TComponent)
  private
    FMesh: TMesh;
    FRadius: Single;
    FCurrentColor,
    FNextColor,
    FChangeColot: TVector3;
    FChangeColorTime: Single;
    procedure ReColor(StartColor, EndColor, MoveColor: TVector3; Progress: Single; DetailsNum: Byte = 20);
  public
    constructor Create(GameObject: TGameObject; Radius: Single);
    procedure ChangeColor(StartColor, EndColor, MoveColor: TVector3);
    procedure OnStart; override;
    procedure OnUpdate(const Delta: Double); override;
    procedure OnDestroy; override;
    property Radius: Single read FRadius;
  public
    class var Instance: TPlanetoid; 
    class function CreatePlanetoidMesh(Radius: Single; DetailsNum: Byte = 20): TMesh;
  end;

implementation   
 
{$REGION 'PlanetoidMaterial'}
{$REGION 'Generate Planetoid Texture'}
procedure GeneratePlanetoidTexture(var TextureIndex: Cardinal);
var
  i, j: Word;   
//  GreenBrightness: Byte;
//  BlueBrightness: Byte;
//  RedBrightness: Byte;
  Brightness: Byte;
  TextureBit: array of Byte;
  PTexture: Pointer;
const
  TEXTURE_W = 1024;
  TEXTURE_H = 1024;
  PIXEL_BYTES = 4;
begin
  SetLength(TextureBit, TEXTURE_W * TEXTURE_H * PIXEL_BYTES);
  For i := 0 to TEXTURE_W - 1 do
    For j := 0 to TEXTURE_H - 1 do
      begin
        Brightness := 255 - 200 * ((trunc(i / 64) + trunc(j / 64)) mod 2);
        TextureBit[i * 4 + (j) * TEXTURE_W * 4 + 0]:= Trunc(((Brightness) * Sin(i / 64 * Pi) * Sin( j / 64 * Pi) + 255) * 0.25 + Brightness * 0.5);//GreenBrightness;
        TextureBit[i * 4 + (j) * TEXTURE_W * 4 + 1]:= Trunc(((Brightness) * Sin(i / 64 * Pi) * Sin( j / 64 * Pi) + 255) * 0.25 + Brightness * 0.5);//GreenBrightness;
        TextureBit[i * 4 + (j) * TEXTURE_W * 4 + 2]:= Trunc(((Brightness) * Sin(i / 64 * Pi) * Sin( j / 64 * Pi) + 255) * 0.25 + Brightness * 0.5);//GreenBrightness;
        TextureBit[i * 4 + (j) * TEXTURE_W * 4 + 3]:= 255;
      end;
  PTexture := TextureBit;
  TextureIndex := GenerateTexture(PTexture, TEXTURE_W, TEXTURE_H );
//  glBindTexture(GL_TEXTURE_2D, TextureIndex);
 // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Result:= TextureIndex;
end;
{$ENDREGION}

constructor TPlanetoidMaterial.Create;
begin
  GeneratePlanetoidTexture(TextureId);
end;

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

{$REGION 'CreatePlanetiodMesh'}
class function TPlanetoid.CreatePlanetoidMesh(Radius: Single; DetailsNum: Byte = 20): TMesh;
var
  Vertex:   TVertexes;
  Normals:  TNormals;
  Colors:   TColors;
  Textures: TTextures;
  Faces: TFaces;
  S: Single;
  i, j, z: Integer;
  F, H: Integer;
begin
  F := DetailsNum;
  S := 1.5;
  H := (F + 1) * (F + 1);
  SetLength(Vertex, H * 6);
  SetLength(Colors, H * 6);
  SetLength(Normals, H * 6);
  SetLength(Textures, H * 6);
  SetLength(Faces, F * F * 2 * 6);
  for i := 0 to F do
    for j := 0 to F do
      begin
        Normals[i + j * (F + 1) + 0 * (H)] := Vector3( 1 - 2 * i / (F), 1 - 2 * j / (F), 1).Normalize;
        Normals[i + j * (F + 1) + 3 * (H)] := Vector3(-1 + 2 * i / (F), 1 - 2 * j / (F),-1).Normalize;
        Normals[i + j * (F + 1) + 1 * (H)] := Vector3(-1 + 2 * j / (F),-1, 1 - 2 * i / (F)).Normalize;
        Normals[i + j * (F + 1) + 4 * (H)] := Vector3(-1 + 2 * j / (F), 1,-1 + 2 * i / (F)).Normalize;
        Normals[i + j * (F + 1) + 2 * (H)] := Vector3( 1, 1 - 2 * i / (F), 1 - 2 * j / (F)).Normalize;
        Normals[i + j * (F + 1) + 5 * (H)] := Vector3(-1,-1 + 2 * i / (F), 1 - 2 * j / (F)).Normalize;
      end;

  for z := 0 to 5 do
    for i := 0 to F do
      for j := 0 to F do
      begin
        Vertex[i + j * (F + 1) + z * (H)] := Normals[i + j * (F + 1) + z * (H)] * Radius;      
        Colors[i + j * (F + 1) + z * (H)] := Vector4(1, 1, 1, 1);
        Textures[i + j * (F + 1) + z * (H)] := Vector2(1 * i / (F), 1 * j / (F));// + TexShift[z];  
        //Textures[i + j * (F + 1) + z * (H)].X := Textures[i + j * (F + 1) + z * (H)].X * 0.5;
        //Textures[i + j * (F + 1) + z * (H)].Y := Textures[i + j * (F + 1) + z * (H)].Y * 0.25;
      end;

  for z := 0 to 5 do
    for i := 0 to F - 1 do
      for j := 0 to F - 1 do
      begin
        Faces[i + j * F + (z * 2 + 0) * (F * F)] := Face(
          i + j * (F + 1) + z * (H),
          i + 1 + (j) * (F + 1) + z * (H),
          i + (j + 1) * (F + 1) + z * (H));
        Faces[i + j * F + (z * 2 + 1) * (F * F)] := Face(
          i + (j + 1) * (F + 1) + z * (H),
          i + 1 + (j) * (F + 1) + z * (H),
          i + 1 + (j + 1) * (F + 1) + z * (H));
      end;
      
  Result := TMesh.Create(Faces, Vertex, Textures, Normals, Colors);
end;  
{$ENDREGION}

{$REGION 'TPlanetoid'}
procedure TPlanetoid.ReColor(StartColor, EndColor, MoveColor: TVector3; Progress: Single; DetailsNum: Byte = 20);
var              
  i, j, z: Integer;
  F, H: Integer;
  S, L: Single;
  ResultColor: TVector3;
begin
  F := DetailsNum;
  H := (F + 1) * (F + 1);
  for z := 0 to 5 do
    for i := 0 to F do
      for j := 0 to F do
      begin
        S := (FMesh.Vertex[i + j * (F + 1) + z * (H)].Z / FRadius) * 10 + Progress;//
        if (S < 0) then
          S := 0;
        if (S > 6 * PI) then
          S := 6 * PI;
        L := 1 - S / (6 * PI);
        S := (Cos(S) + 1) * 0.5;
        ResultColor := (StartColor * L + EndColor * (1 - L)) * S + MoveColor * (1 - S);
        FMesh.Colors[i + j * (F + 1) + z * (H)] := Vector4(ResultColor.R, ResultColor.G, ResultColor.B, 1);
      end;
end;

constructor TPlanetoid.Create(GameObject: TGameObject; Radius: Single);
begin
  FRadius := Radius;
  SetGameObject(GameObject);
end;

procedure  TPlanetoid.ChangeColor(StartColor, EndColor, MoveColor: TVector3);
begin
  FCurrentColor := StartColor;
  FNextColor := EndColor;
  FChangeColot := MoveColor;
  FChangeColorTime := - 35;
end;

procedure TPlanetoid.OnStart;
var
   MeshRenderer: TMeshRenderer;
begin
  Instance := Self;
  if FMesh = nil then
    FMesh := CreatePlanetoidMesh(FRadius);
  MeshRenderer := TMeshRenderer.Create(GameObject);
  MeshRenderer.Mesh := FMesh;
  MeshRenderer.Material := TPlanetoidMaterial.Create;  
  ChangeColor(Vector3(0.05,0.3,0.05), Vector3(0.1,1,0.1), Vector3(0.1,0.2,0.1));
end;

procedure TPlanetoid.OnUpdate(const Delta: Double);
begin
  if (FChangeColorTime < 6 * PI + 25)  then
  begin
    FChangeColorTime := FChangeColorTime + Delta * 5;
    ReColor(FCurrentColor, FNextColor, FChangeColot, FChangeColorTime);
  end;
end;

procedure TPlanetoid.OnDestroy;
begin

end;
{$ENDREGION}

end.
