program LightingModels;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  GL in 'OpenGL\GL.pas',
  GLu in 'OpenGL\GLu.pas',
  GLut in 'OpenGL\GLut.pas',
  GLext in 'OpenGL\GLext.pas',
  WGLext in 'OpenGL\WGLext.pas',
  GLtools in 'OpenGL\GLtools.pas',
  Math3D in 'Math3D\Math3D.pas',
  VertexArray in 'VertexArray\VertexArray.pas',
  CompiledVertexArray in 'VertexArray\CompiledVertexArray\CompiledVertexArray.pas',
  Images in 'Images\Images.pas',
  TGA in 'Images\TGA\TGA.pas',
  DXT in 'Images\DXT\DXT.pas',
  Textures in 'Textures\Textures.pas',
  Mipmaps in 'Textures\Mipmaps\Mipmaps.pas',
  TextureLODBias in 'Textures\TextureLODBias\TextureLODBias.pas',
  TextureCompression in 'Textures\TextureCompression\TextureCompression.pas',
  S3TC in 'Textures\TextureCompression\S3TC\S3TC.pas',
  AnisotropicFilter in 'Textures\AnisotropicFilter\AnisotropicFilter.pas',
  Texture3D in 'Textures\Texture3D\Texture3D.pas',
  TextureCubeMap in 'Textures\TextureCubeMap\TextureCubeMap.pas',
  TextureRectangle in 'Textures\TextureRectangle\TextureRectangle.pas',
  DepthTexture in 'Textures\DepthTexture\DepthTexture.pas',
  Occlusion in 'Culling\Occlusion\Occlusion.pas',
  ShaderObjects in 'Shaders\GLSL\ShaderObjects\ShaderObjects.pas',
  VertexShaderObject in 'Shaders\GLSL\ShaderObjects\VertexShaderObject\VertexShaderObject.pas',
  FragmentShaderObject in 'Shaders\GLSL\ShaderObjects\FragmentShaderObject\FragmentShaderObject.pas',
  ARBprograms in 'Shaders\ASMShaders\ARBprograms\ARBprograms.pas',
  ARBvertexProgram in 'Shaders\ASMShaders\ARBprograms\ARBvertexProgram\ARBvertexProgram.pas',
  ARBfragmentProgram in 'Shaders\ASMShaders\ARBprograms\ARBfragmentProgram\ARBfragmentProgram.pas',
  TextureCombiners in 'Shaders\TextureCombiners\TextureCombiners.pas',
  Scripts in 'Scripts\Scripts.pas',
  ATIfragmentShader in 'Shaders\ASMShaders\ATIfragmentShader\ATIfragmentShader.pas',
  RegisterCombiners in 'Shaders\RegisterCombiners\RegisterCombiners.pas',
  NV_parse in 'Shaders\NV_parse\NV_parse.pas',
  EnvMapBumpMap in 'Shaders\EnvMapBumpMap\EnvMapBumpMap.pas',
  OffsetTexture in 'Textures\OffsetTexture\OffsetTexture.pas',
  ScissorBox in 'Culling\ScissorBox\ScissorBox.pas',
  Models in 'Models\Models.pas',
  MDF in 'Models\MDF\MDF.pas',
  VertexArrayObject in 'VertexArray\VertexArrayObject\VertexArrayObject.pas',
  TruForm in 'VertexArray\TruForm\TruForm.pas',
  FragmentCombiners in 'Shaders\FragmentCombiners\FragmentCombiners.pas',
  Fonts in 'Fonts\Fonts.pas',
  VertexBufferObject in 'VertexArray\VertexBufferObject\VertexBufferObject.pas',
  Shaders in 'Shaders\Shaders.pas',
  FrameBuffers in 'FrameBuffers\FrameBuffers.pas',
  Camera in 'Camera\Camera.pas',
  FirstPersonCamera in 'Camera\FirstPersonCamera\FirstPersonCamera.pas',
  NewtonImport in 'Newton\NewtonImport.pas',
  LightingModelsPanel in 'LightingModelsPanel.pas' {LightingModelPanel};

const
  OREN_NAYAR_SIGMA                  = 0.5;
  MINNAERT_M                        = 1.5;
  PHONG_N                           = 32;
  BLINN_PHONG_N                     = 64;
  SCHLICK_N                         = 32;
  WARD_ISO_SIGMA                    = 0.1;
  WARD_ANISO_SIGMAX                 = 0.5;
  WARD_ANISO_SIGMAY                 = 0.1;

var
  lx, ly: integer;
  rx, ry, rz: single;
  i: integer;

  vs: array[1..8] of TVertexShaderObject;
  fs: array[1..8] of TFragmentShaderObject;
  po: array[1..8] of TProgramObject;
  uo: array[1..8] of array of TUniformObject;
  name: array[1..8] of string = ('lambert', 'oren_nayar', 'minnaert', 'phong', 'blinn_phong', 'schlick', 'ward_iso', 'ward_aniso');

  lp: TVector4;

procedure display; cdecl;
begin
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  glLoadIdentity;
  glTranslatef(0, 0, -2);
  glRotatef(rx, 1, 0, 0);
  glRotatef(ry, 0, 1, 0);

  glColor3f(1, 0, 0);

  po[LightingModel].Enable;

  lp := Vector4(0, 3, 4, 1);
  VecRotateY4(lp, rz);
  if AnimateLight then rz := rz + 0.005;
  glLightfv(GL_LIGHT0, GL_POSITION, @lp);

  case LightingModel of
    LM_LAMBERT:
      begin
      end;
    LM_OREN_NAYAR:
      begin
        uo[2][0].Value1f := 1 - 0.5 * (sqr(OREN_NAYAR_SIGMA) / sqr(OREN_NAYAR_SIGMA) + 0.33);
        uo[2][1].Value1f := 0.45 * (sqr(OREN_NAYAR_SIGMA) / sqr(OREN_NAYAR_SIGMA) + 0.99);
      end;
    LM_MINNAERT:
      begin
        uo[3][0].Value1f := MINNAERT_M - 1;
      end;
    LM_PHONG:
      begin
        uo[4][0].Value1f := PHONG_N;
      end;
    LM_BLINN_PHONG:
      begin
        uo[5][0].Value1f := BLINN_PHONG_N;
      end;
    LM_SCHLICK:
      begin
        uo[6][0].Value1f := SCHLICK_N;
        uo[6][1].Value1f := SCHLICK_N - 1;
      end;
    LM_WARD_ISOTROPIC:
      begin
        uo[7][0].Value1f := 1 / sqr(WARD_ISO_SIGMA);
        uo[7][1].Value1f := 1 / (2*Pi * sqr(WARD_ISO_SIGMA));
      end;
    LM_WARD_ANISOTROPIC:
      begin
        uo[8][0].Value1f := 1 / sqr(WARD_ANISO_SIGMAX);
        uo[8][1].Value1f := 1 / sqr(WARD_ANISO_SIGMAY);
        uo[8][2].Value1f := 1 / (4*Pi * WARD_ANISO_SIGMAX * WARD_ANISO_SIGMAY);
        uo[8][3].Value3f := Vector3(0, 0, 1);
      end;
  end;

  glutSolidTeapot(0.4);
  glutSwapBuffers;
end;

procedure reshape(w, h: Integer); cdecl;
begin
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45, w/h, 0.1, 100);
  glMatrixMode(GL_MODELVIEW);
end;

procedure keyboard(key: byte; x, y: Integer); cdecl;
begin
  if key = 27 then halt;
end;

procedure special(key, x, y: Integer); cdecl;
begin
end;

procedure mouse(button, state, x, y: Integer); cdecl;
begin
  lx := x;
  ly := y;
end;

procedure motion(x, y: Integer); cdecl;
begin
  rx := rx + (y-ly)*0.5;
  ry := ry + (x-lx)*0.5;
  lx := x;
  ly := y;
end;

procedure passivemotion(x, y: Integer); cdecl;
begin
end;

procedure visibility(state: Integer); cdecl;
begin
end;

procedure idle; cdecl;
begin
end;

procedure timer(value: integer); cdecl;
begin
  glutTimerFunc(15, timer, 0);
  glutPostRedisplay;
end;

begin
  try
    rx := 30;
    ry := -30;
    rz := 0;

    writeln('*****************************');
    writeln('  MEGVILAGITASI MODELL DEMO  ');
    writeln('*****************************');

    write('GLUT inicializalasa...................');

    glutInit;
    glutInitDisplayMode(GLUT_RGBA or GLUT_DOUBLE or GLUT_DEPTH);
    glutInitWindowSize(600, 600);
    glutCreateWindow('Lighting Model Demo');

//    Load_GL_VERSION_1_2;
//    Load_GL_VERSION_1_3;
//    Load_GL_VERSION_1_4;
//    Load_GL_VERSION_1_5;
//    Load_GL_VERSION_2_0;

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutSpecialFunc(special);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);
    glutPassiveMotionFunc(passivemotion);
    glutVisibilityFunc(visibility);
    glutIdleFunc(idle);
    glutTimerFunc(15, timer, 0);

    glEnable(GL_DEPTH_TEST);

    writeln('kesz');

    write('Konfiguracios panel inicializalasa....');

    LightingModelPanel := TLightingModelPanel.Create(nil);
    LightingModelPanel.Show;

    writeln('kesz');

    writeln('Shaderek inicializalasa...............');

    for i := 1 to 8 do
    begin
      write(uppercase(name[i]), ' :: beolvasas :: ');
      vs[i] := TVertexShaderObject.Create;
      fs[i] := TFragmentShaderObject.Create;
      po[i] := TProgramObject.Create;
      vs[i].Load('_shaders\'+name[i]+'.vsh');
      fs[i].Load('_shaders\'+name[i]+'.fsh');
      write('forditas :: ');
      vs[i].Compile;
      fs[i].Compile;
      write('szerkesztes :: ');
      po[i].Attach(vs[i]);
      po[i].Attach(fs[i]);
      po[i].Link;
      write('konfiguralas :: ');
      case i of
        LM_LAMBERT:
          begin
            SetLength(uo[i], 0);
          end;
        LM_OREN_NAYAR:
          begin
            SetLength(uo[i], 2);
            uo[i][0] := po[i].GetUniformObject('A');
            uo[i][1] := po[i].GetUniformObject('B');
          end;
        LM_MINNAERT:
          begin
            SetLength(uo[i], 1);
            uo[i][0] := po[i].GetUniformObject('m_minus_one');
          end;
        LM_PHONG:
          begin
            SetLength(uo[i], 1);
            uo[i][0] := po[i].GetUniformObject('n');
          end;
        LM_BLINN_PHONG:
          begin
            SetLength(uo[i], 1);
            uo[i][0] := po[i].GetUniformObject('n');
          end;
        LM_SCHLICK:
          begin
            SetLength(uo[i], 2);
            uo[i][0] := po[i].GetUniformObject('n');
            uo[i][1] := po[i].GetUniformObject('n_minus_one');
          end;
        LM_WARD_ISOTROPIC:
          begin
            SetLength(uo[i], 2);
            uo[i][0] := po[i].GetUniformObject('sigma2_inverse');
            uo[i][1] := po[i].GetUniformObject('sigma2_mul_2pi_inverse');
          end;
        LM_WARD_ANISOTROPIC:
          begin
            SetLength(uo[i], 4);
            uo[i][0] := po[i].GetUniformObject('sigmax2_inverse');
            uo[i][1] := po[i].GetUniformObject('sigmay2_inverse');
            uo[i][2] := po[i].GetUniformObject('sigmaxy_mul_4pi_inverse');
            uo[i][3] := po[i].GetUniformObject('direction');
          end;
      end;
      writeln('kesz');
    end;

    glutMainLoop;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.
