{$MODE OBJFPC}
{$MODESWITCH AUTODEREF}
// TODO move unit to opengl profile and remove it
{$define DENG_USE_DRAW_OPENGL}
unit DSkybox;

interface

uses
  {$IFDEF DENG_USE_DRAW_OPENGL}{$IFDEF ANDROID}GLES,{$ELSE}GL, GLU, glext,{$ENDIF}{$ENDIF}
  dvector3f,
  dstream,
  dstreamfile,
  dtexture2d,
  DEng,
  DDrawVertexBuffer;

type
PSkybox = ^TSkybox;
TSkybox = object
private
  FEng: TEngine;
  {$IFDEF DENG_USE_DRAW_OPENGL}FId: GLuint;{$ENDIF}
public
  constructor Init(Man: Pointer; Eng: TEngine);
  constructor Init(Man: Pointer; Eng: TEngine; const Dir: AnsiString);
  constructor Init(Man: Pointer; Eng: TEngine; Bk, Dn, Ft, Lf, Rt, Up: TData);
  destructor Done; virtual;
  procedure Render;
end;

implementation

constructor TSkybox.Init(Man: Pointer; Eng: TEngine);
begin
  FEng := Eng;
  FId := 0;
end;

constructor TSkybox.Init(Man: Pointer; Eng: TEngine; const Dir: AnsiString);
begin
  Init(Man, Eng,
       Dir + '/bk.tga',
       Dir + '/dn.tga',
       Dir + '/ft.tga',
       Dir + '/lf.tga',
       Dir + '/rt.tga',
       Dir + '/up.tga');
end;

constructor TSkybox.Init(Man: Pointer; Eng: TEngine; Bk, Dn, Ft, Lf, Rt, Up: TData);
  procedure LoadSide(E: GLenum; Data: TData);
  var
    Texture: TTexture2dRgba;
  begin
    if LoadTexture2d(Data, Texture) then begin
      glTexImage2D(E, 0, GL_RGBA, Texture.Width, Texture.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Texture.Data);
      Texture.Done;
    end;
  end;
begin
  FEng := Eng;

  glGenTextures(1, @FId);
  glEnable(GL_TEXTURE_CUBE_MAP);
  glBindTexture(GL_TEXTURE_CUBE_MAP, FId);
  //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // set 1-byte alignment

  // set texture to repeat mode
  //glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_REPEAT);
  //glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_REPEAT);

  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  //glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0);
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  LoadSide(GL_TEXTURE_CUBE_MAP_POSITIVE_X, Ft);
  LoadSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, Bk);
  LoadSide(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, Up);
  LoadSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, Dn);
  LoadSide(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, Rt);
  LoadSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, Lf);

  glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
  glDisable(GL_TEXTURE_CUBE_MAP);
end;

destructor TSkybox.Done;
begin
  if FId <> 0 then begin
    glDeleteTextures(1, @FId);
  end;
end;

procedure TSkybox.Render;
const
  CubeVertices: array[0 .. 8 - 1] of TVec3f = (
    (x: -100; y:  100; z:  100),
    (x: -100; y: -100; z:  100),
    (x:  100; y: -100; z:  100),
    (x:  100; y:  100; z:  100),
    (x: -100; y:  100; z: -100),
    (x: -100; y: -100; z: -100),
    (x:  100; y: -100; z: -100),
    (x:  100; y:  100; z: -100)
  );
  CubeTexCoords: array[0 .. 8 - 1] of TVec3f = (
    (x: -1; y:  1; z:  1),
    (x: -1; y: -1; z:  1),
    (x:  1; y: -1; z:  1),
    (x:  1; y:  1; z:  1),
    (x: -1; y:  1; z: -1),
    (x: -1; y: -1; z: -1),
    (x:  1; y: -1; z: -1),
    (x:  1; y:  1; z: -1)
  );
  CubeIndices: array[0 .. 6 * 4 - 1] of TIndexBuffer.TIndex = (
    0, 1, 2, 3,
    3, 2, 6, 7,
    7, 6, 5, 4,
    4, 5, 1, 0,
    0, 3, 7, 4,
    1, 2, 6, 5
  );
var
  V: TVertexBuffer;
  I: TIndexBuffer;
begin
  if FId <> 0 then begin
    V.Init(CubeVertices);
    V.SetTexCoords(CubeTexCoords);
    I.Init(CubeIndices);
 
    glEnable(GL_TEXTURE_CUBE_MAP);
    glDisable(GL_DEPTH_TEST);
    glBindTexture(GL_TEXTURE_CUBE_MAP, FId);
    V.DrawQuads(I);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_CUBE_MAP);
 
    I.Done;
    V.Done;
  end;
end;

end.
