unit UtilTextures;

interface

uses GL, GLu, GLext, Textures, Texture3D, Mipmaps, TextureCubeMap, Math3D, Math;

function Create3DAttenuationMap(size: integer): TTexture3D;
function Create3DAttenuationMapATI(size: integer): TTexture3D;
function CreateNormalCubeMap(size: integer): TTextureCubeMap;
function CreateSpecularLookupTable(size: integer): TTexture2D;

implementation

function Create3DAttenuationMap;
var
  buf: array of byte;
  i, j, k: cardinal;
  x, y, z, p: single;
begin
  result := TTexture3D.Create;
  SetLength(buf, size*size*size);
  for k := 0 to size-1 do
  for j := 0 to size-1 do
  for i := 0 to size-1 do
  begin
    if (i = 0) or (i = size-1) or
       (j = 0) or (j = size-1) or
       (k = 0) or (k = size-1) then
    begin
      p := 1;
    end else
    begin
      x := (i / size)*2 - 1;
      y := (j / size)*2 - 1;
      z := (k / size)*2 - 1;
      p := x*x + y*y + z*z;
      if p > 1 then p := 1;
    end;
    buf[k*size*size + j*size + i] := 255 - trunc(p * 255);
  end;
  result.Bind;
  result.WrapModeS := GL_CLAMP_TO_EDGE;
  result.WrapModeT := GL_CLAMP_TO_EDGE;
  result.MagFilter := GL_LINEAR;
  result.MinFilter := GL_LINEAR_MIPMAP_NEAREST;
  GenerateMipmap(result, GL_TRUE);
  glTexImage3DEXT(GL_TEXTURE_3D, 0, GL_LUMINANCE8, size, size, size, 0,
                  GL_RED, GL_UNSIGNED_BYTE, @buf[0]);
end;

function Create3DAttenuationMapATI;
var
  buf: array of byte;
  i, j, k: cardinal;
  x, y, z, p: single;
begin
  result := TTexture3D.Create;
  size := size shr 1;
  SetLength(buf, size*size*size);
  for k := 0 to size-1 do
  for j := 0 to size-1 do
  for i := 0 to size-1 do
  begin
    if (i = size-1) or
       (j = size-1) or
       (k = size-1) then
    begin
      p := 1;
    end else
    begin
      x := i / size;
      y := j / size;
      z := k / size;
      p := x*x + y*y + z*z;
      if p > 1 then p := 1;
    end;
    buf[k*size*size + j*size + i] := 255 - trunc(p * 255);
  end;
  result.Bind;
  result.WrapModeS := GL_MIRROR_CLAMP_TO_EDGE_ATI;
  result.WrapModeT := GL_MIRROR_CLAMP_TO_EDGE_ATI;
  result.MagFilter := GL_LINEAR;
  result.MinFilter := GL_LINEAR_MIPMAP_NEAREST;
  GenerateMipmap(result, GL_TRUE);
  glTexImage3DEXT(GL_TEXTURE_3D, 0, GL_LUMINANCE8, size, size, size, 0,
                  GL_RED, GL_UNSIGNED_BYTE, @buf[0]);
end;

function CreateNormalCubeMap;

  function GetCubeVector(side, cubesize, x, y: integer): TVector3;
  var
    s, t, sc, tc: Single;
  begin
    s := (x + 0.5) / cubesize;
    t := (y + 0.5) / cubesize;
    sc := s*2 - 1;
    tc := t*2 - 1;
    case side of
      GL_TEXTURE_CUBE_MAP_POSITIVE_X:
         begin
           result.x := 1;
           result.y := -tc;
           result.z := -sc;
         end;
      GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
         begin
           result.x := -1;
           result.y := -tc;
           result.z := sc;
         end;
      GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
         begin
           result.x := sc;
           result.y := 1;
           result.z := tc;
         end;
      GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
         begin
           result.x := sc;
           result.y := -1;
           result.z := -tc;
         end;
      GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
         begin
           result.x := sc;
           result.y := -tc;
           result.z := 1;
         end;
      GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
         begin
           result.x := -sc;
           result.y := -tc;
           result.z := -1;
         end;
    end;
    VecNormalize3(result);
  end;

var
  vec: TVector3;
  i, x, y: integer;
  pixels: array of word;
begin
  result := TTextureCubeMap.Create;
  result.Bind;
  result.WrapModeS := GL_CLAMP_TO_EDGE;
  result.WrapModeT := GL_CLAMP_TO_EDGE;
  result.WrapModeR := GL_CLAMP_TO_EDGE;
  result.MagFilter := GL_LINEAR;
  result.MinFilter := GL_LINEAR_MIPMAP_NEAREST;
  GenerateMipmap(result, GL_TRUE);
  SetLength(pixels, size*size*3);
  for i := GL_TEXTURE_CUBE_MAP_POSITIVE_X to GL_TEXTURE_CUBE_MAP_NEGATIVE_Z do
  begin
    for y := 0 to size-1 do
    begin
      for x := 0 to size-1 do
      begin
        vec := GetCubeVector(i, size, x, y);
        pixels[(y*size + x)*3 + 0] := Trunc(32768 + (vec.x*32767));
        pixels[(y*size + x)*3 + 1] := Trunc(32768 + (vec.y*32767));
        pixels[(y*size + x)*3 + 2] := Trunc(32768 + (vec.z*32767));
      end;
    end;
    glTexImage2D(i, 0, GL_RGB16, size, size, 0,
                 GL_RGB, GL_UNSIGNED_SHORT, @pixels[0]);
  end;
end;

function CreateSpecularLookupTable;
var
  i, j: integer;
  dif, roug: double;
  buf: array of byte;
begin
  result := TTexture2D.Create;
  result.Bind;
  result.WrapModeS := GL_CLAMP_TO_EDGE;
  result.WrapModeT := GL_CLAMP_TO_EDGE;
  result.MagFilter := GL_LINEAR;
  result.MinFilter := GL_LINEAR_MIPMAP_NEAREST;
  GenerateMipmap(result, GL_TRUE);
  SetLength(buf, size*size);
  dif := 10 / size;
  roug := 0;
  for i := 0 to size-1 do
  begin
    for j := 0 to size-1 do
      buf[i*size+j] := Trunc(power((j+1)/size, power(2, roug)) * 255);
    roug := roug + dif;
  end;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, size, size, 0, GL_RED, GL_UNSIGNED_BYTE, @buf[0]);
end;

end.
