{
    This file is part of Chentrah,
    Copyright (C) 2004-2008 Anton Rzheshevski (chebmaster@mail.ru).

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/

 **********************************************************************

    This file  contains source of the image postprocessing routines.

 **********************************************************************}
 
unit mo_gamerender;

interface

 uses
  SysUtils,
  mo_hub,
  chepersy,
  mo_resources

  ;
{
procedure HalvAndBlurImage (tex1, tex2: glUint;
                            tex1size,
                            imagewidth, imageheight: integer;
                            brightness: glFloat;
                            blurPower: integer);
                            
procedure PostprocessOutput(tex1, tex2: glUint;
                            tex1size, tex2size,
                            image1width, image1height,
                            image2width, image2height: integer;
                            brightness1, brightness2,
                            blur1, blur2: glFloat);
 }
Const
   BLUR_OFFSET = 1.0;

Type
  { TDynamicTexture }
{
  TDynamicTexture = class (TTexture)
  public
    constructor Create(width, height, internalformat: glInt);
  end;
}
  
  { TPostProcessFilter }

  { TAbstractGameRender }

  TAbstractGameRender = class (TTrulypersistent)
  protected
    _QMod, tex_width, tex_height: integer;
{    tex_full, tex_halv, tex_gray, tex_noise1, tex_noise2: TTexture;}
    procedure Cleanup();
    procedure ReInit();
    procedure DrawQuad(width, height, s1, s2, t1, t2: glFloat);
    procedure Prepare(); virtual;
    procedure PostProcess(); virtual;
  public
    constructor Create();
    destructor Destroy(); override;
    procedure RegisterFields(); override;

    property QualityScale: integer read _QMod;
    function Execute(): boolean; virtual;
    
    function RenderScene(): boolean; virtual; abstract;
  end;

implementation
uses
  {$ifdef fpc}
    mo_module in '../../cge/src/mo_module.pas'
  {$else}
    mo_module
  {$endif}
 ;

// --------------------------------------------------------------------------//
 {
  procedure HalvAndBlurImage (tex1, tex2: glUint;
                              tex1size,
                              imagewidth, imageheight: integer;
                              brightness: glFloat;
                              blurPower: integer);
  var
    d, xo, yo, dx, dy: glFloat;
    i: integer;
  begin
    glColor4f(brightness/blurPower, brightness/blurPower, brightness/blurPower, 1);
    glViewPort(0,0,imagewidth, imageheight);
   // glClearColor(0,0,0,0);
   // glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glOrtho(0, imagewidth, 0, imageheight, 0 ,1);
    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex1);
    glDisable(GL_BLEND);
    glDisable(GL_ALPHA_TEST);
    d:=(1/tex1size);
    xo:=imagewidth/tex1size;
    yo:=imageheight/tex1size;
    For i:=1 to BlurPower do begin
      if i > 1 then begin
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
      end;
      case i of
        1: begin
            if BlurPower = 2 then begin
              dx:=-d * BLUR_OFFSET; dy:=-d * BLUR_OFFSET;
            end
            else begin
              dx:=0; dy:=0;
            end;
           end;
        2: begin dx:=d * BLUR_OFFSET; dy:=d * BLUR_OFFSET end;
        3: begin dx:=-d * BLUR_OFFSET; dy:=-d * BLUR_OFFSET end;
        4: begin dx:=d * BLUR_OFFSET; dy:=-d * BLUR_OFFSET end;
        5: begin dx:=-d * BLUR_OFFSET; dy:=d  * BLUR_OFFSET end;
      end;
      glBegin(GL_QUADS);
        glTexCoord2f(0 + d + dx, 0 - d +dy);
        glVertex2f(0, 0); //bottom left corner
        glTexCoord2f(0 + d + dx, yo - d +dy);
        glVertex2f(0, imageheight div 2);
        glTexCoord2f(xo + d+ dx, yo - d +dy);
        glVertex2f(imagewidth div 2, imageheight div 2);
        glTexCoord2f(xo + d+ dx, 0 - d +dy);
        glVertex2f(imagewidth div 2, 0);
      glEnd;
    end;

    glBindTexture(GL_TEXTURE_2D, tex2);
    glCopyTexSubImage2d(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
                                          imagewidth div 2, imageheight div 2);
    glDepthMask(GL_TRUE);
  end;
  
// --------------------------------------------------------------------------//


  procedure PostprocessOutput(tex1, tex2: glUint;
                              tex1size, tex2size,
                              image1width, image1height,
                              image2width, image2height: integer;
                              brightness1, brightness2,
                              blur1, blur2: glFloat);
  var
    i: integer;
    d, xo, yo: glFloat;
    procedure DrawQuad;
    begin
      glBegin(GL_QUADS);
        glTexCoord2f(0 + d, 0 - d);
        glVertex2f(0, 0); //bottom left corner
        glTexCoord2f(0 + d, yo - d);
        glVertex2f(0, DisplayHeight());
        glTexCoord2f(xo + d, yo - d);
        glVertex2f(DisplayWidth(), DisplayHeight());
        glTexCoord2f(xo + d, 0 - d);
        glVertex2f(DisplayWidth(), 0);
      glEnd;
    end;
  begin
    glViewport(0, 0, DisplayWidth, DisplayHeight);
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
    glOrtho(0, DisplayWidth(), 0, DisplayHeight(), 0 ,1);
    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);
    glDisable(GL_BLEND);
    glDisable(GL_ALPHA_TEST);
    
    glColor4f(brightness1, brightness1, brightness1, 1);
    glBindTexture(GL_TEXTURE_2D, tex1);
    d:=blur1 * 1/tex1size;
    xo:=image1width/tex1size;
    yo:=image1height/tex1size;
    DrawQuad();

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);
    glColor4f(brightness2, brightness2, brightness2, brightness2);
    glBindTexture(GL_TEXTURE_2D, tex2);
    d:=blur2 * 1/tex2size;
    xo:=image2width/tex2size;
    yo:=image2height/tex2size;
    DrawQuad();
  end;
       }
// --------------------------------------------------------------------------//



procedure TAbstractGameRender.Cleanup;
begin
{
  tex_full.free;
  tex_halv.free;
  tex_gray.free;
}
end;

procedure TAbstractGameRender.ReInit;
var
  format: glInt;
begin
  format:=GL_RGBA;
{
  tex_full:=TDynamicTexture.Create(tex_width, tex_height, format);
  tex_halv:=TDynamicTexture.Create(tex_width div 2, tex_height div 2, format);
  tex_gray:=TDynamicTexture.Create(256, 256, GL_LUMINANCE);
}
end;


procedure TAbstractGameRender.DrawQuad(width, height, s1, s2, t1, t2: glFloat);
begin
  glBegin(GL_QUADS);
    glTexCoord2f(s1, t1);
    glVertex2f(0, 0); //bottom left corner
    glTexCoord2f(s1, t2);
    glVertex2f(0, height);
    glTexCoord2f(s2, t2);
    glVertex2f(width, height);
    glTexCoord2f(s2, t1);
    glVertex2f(width, 0);
  glEnd;
end;


procedure TAbstractGameRender.Prepare();
begin

end;

procedure TAbstractGameRender.PostProcess();
begin

end;

function TAbstractGameRender.Execute(): boolean;
begin
  Prepare();
  Result:= RenderScene();
  if Result then PostProcess();
end;

procedure TAbstractGameRender.REgisterFields;
begin
//  RegClass(TDynamicTexture);
  inherited;
  ListFields([
    '_QMod', @_QMod, typeinfo(integer),
    'tex_width', @tex_width, typeinfo(integer),
    'tex_height', @tex_height, typeinfo(integer)
  ]);
{
  RegField('tex_full', @tex_full, typeinfo(TDynamicTexture));
  RegField('tex_halv', @tex_halv, typeinfo(TDynamicTexture));
  RegField('tex_gray', @tex_gray, typeinfo(TDynamicTexture));
  RegField('tex_noise1', @tex_noise1, typeinfo(TDynamicTexture));
  RegField('tex_noise2', @tex_noise2, typeinfo(TDynamicTexture));
  }
end;

constructor TAbstractGameRender.Create;
begin
//  tex_noise1:=TDynamicTexture.Create(64, 64, GL_LUMINANCE);
//  tex_noise2:=TDynamicTexture.Create(64, 64, GL_LUMINANCE);
end;

destructor TAbstractGameRender.Destroy;
begin
  Cleanup;
//  tex_noise1.free;
//  tex_noise2.free;
  inherited Destroy;
end;

{
procedure TPostProcessFilter.PrepareToRenderScene();
begin
  PrepareToRenderScene(Method, Quality, Shader);
end;

procedure TPostProcessFilter.PrepareToRenderScene(
  pp_Quality: TPostProcessingQuality);
begin
  PrepareToRenderScene(Method, pp_Quality, Shader);
end;

procedure TPostProcessFilter.PrepareToRenderScene(
  pp_Method: TPostProcessingMethod;
  pp_Quality: TPostProcessingQuality;
  pp_Shader: TPostProcessingShader);
begin
  case pp_Method of
    PPM_SIMPLEST: begin
      if (DisplayWidth() > 1024) or (DisplayHeight() > 1024)
        then CrashRender(RuEn(
          '������� ����� ����-��������� �� ������������ ���������� ���� 1024!'
         ,'Current post-processing method doesn''t support resolutions higher than 1024!'));

      if Method <> pp_Method then begin
        if VerboseLog then AddLog('Switching the post-processing method...');
        Cleanup;
        Method:=pp_Method;
        ReInit;
        if VerboseLog then AddLogOk;
      end;
      Quality:=pp_Quality;
      case Qulality of
        PPQ_LOW:
          glViewport(0, 0, DisplayWidth() div 2, DisplayHeight() div 2);
        PPQ_MEDIUM, PPQ_HIGH:
          glViewport(0, 0, DisplayWidth(), DisplayHeight());
      end;
    end;
  else
    DieBaka('unknown PostProcessingMethod in TPostProcessFilter.PrepareToRenderScene()');
  end;
end;


function TPostProcessFilter.ProcessGraymap(
  EyeSensivity, NightVisionBrightness: glFloat): glFloat;
var
  GrayMap: packed array[-2..257, 0..255] of byte;
  ScaleFactor: array[0..255] of glFloat;
  Weight
  w, h: integer;
begin
  w:=DisplayWidth() div 4;
  h:=DisplayHeight() div 4;
  glReadPixels(0,0, w, h, GL_LUMINANCE, GL_UNSIGNED_BYTE, @GrayMap[0,0]);
  
               
end;

function TPostProcessFilter.Postprocess(
  EyeSensivity, NightVisionBrightness, SceneBlurFactor: glFloat): glFloat;
begin
  case Method of
    PPM_SIMPLEST: begin
      if (DisplayWidth() > 1024) or (DisplayHeight() > 1024)
        then CrashRender(RuEn(
          '������� ����� ����-��������� �� ������������ ���������� ���� 1024!'
         ,'Current post-processing method doesn''t support resolutions higher than 1024!'));

      //first, we copy the framebuffer to a texture;
      case Qulality of
        PPQ_LOW: begin
          //half size, copy directly to the half texture:
        end;
        PPQ_MEDIUM, PPQ_HIGH: begin
          tex_full.Bind();
          glCopyTexSubImage2d(GL_TEXTURE_2D, 0, 0, 0, 0, 0, DisplayWidth(), DisplayHeight());
        end;
      end;
      
      //second, we downsample the image to 1/2th..
      case Qulality of
        PPQ_LOW:;//already there
        PPQ_MEDIUM, PPQ_HIGH: begin
          glViewport(0, 0, DisplayWidth() div 2, DisplayHeight() div 2);
          tex_full.Bind();
          DrawQuad(DisplayWidth() div 2, DisplayHeight() div 2,
                   0, 0, 1024/DisplayWidth(), 1024/DisplayHeight());
          tex_halv.Bind();
          glCopyTexSubImage2d(GL_TEXTURE_2D, 0, 0, 0, 0, 0, DisplayWidth() div 2, DisplayHeight() div 2);
        end;
      end;
      
      //..and the same to 1/4th:
      glViewport(0, 0, DisplayWidth() div 4, DisplayHeight() div 4);
      tex_halv.Bind();
      DrawQuad(DisplayWidth() div 4, DisplayHeight() div 4,
               0, 0, 1024/DisplayWidth(), 1024/DisplayHeight());
      tex_gray.Bind();

      //add the noise:
      glColor3f()



      //glCopyTexSubImage2d(GL_TEXTURE_2D, 0, 0, 0, 0, 0, DisplayWidth() div 4, DisplayHeight() div 4);

      //next, we process the gray map procedurally
      Result:=ProcessGraymap(EyeSensivity, NightVisionBrightness);

      //fourth, we blend the scene and the gray map:
      case Qulality of
        PPQ_LOW: begin
          //half size, the half texture is used:
        end;
        PPQ_MEDIUM, PPQ_HIGH: begin

        end;
      end;
      
    end;
  else
    DieBaka('unknown PostProcessingMethod in TPostProcessFilter.PrepareToRenderScene()');
  end;
end;  }

{ TDynamicTexture }

{constructor TDynamicTexture.Create(width, height, internalformat: glInt);
begin
  inherited Create;
  f_target:=GL_TEXTURE_2D;
  Bind();
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexImage2D (GL_TEXTURE_2D, 0, internalformat, width, height, 0, 0, 0, nil);
end;     }

end.


{    for i:=0 to 3 do color1[i]:=brightness/2;
//    color1[3]:=1;

   // glDisable(GL_BLEND);
   // glBlendFunc(GL_ONE, GL_ONE);
glActiveTextureARB (GL_TEXTURE0_ARB);
glEnable (GL_TEXTURE_2D);
glBindTexture (GL_TEXTURE_2D,  tex1);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);

glActiveTextureARB (GL_TEXTURE1_ARB);
glBindTexture (GL_TEXTURE_2D,  tex1);
glEnable (GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_ONE);

    glBegin(GL_QUADS);
      glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0 + d, 0 + d);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0 - d, 0 - d);
      glVertex2f(0, 0); //bottom left corner

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0 + d, yo + d);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0 - d, yo - d);
      glVertex2f(0, imageheight div 2);

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB,xo + d, yo + d);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB,xo - d, yo - d);
      glVertex2f(imagewidth div 2, imageheight div 2);

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB,xo + d, 0 + d);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB,xo - d, 0 - d);
      glVertex2f(imagewidth div 2, 0);
    glEnd;
    glActiveTextureARB(GL_TEXTURE1_ARB);
    glDisable(GL_TEXTURE_2D);
    glActiveTextureARB(GL_TEXTURE0_ARB);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);}
    
    
    
    
    
    
    
    
    

{    glActiveTextureARB(GL_TEXTURE0_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex1);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, @color1[0]);

    glActiveTextureARB(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex2);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, @color2[0]);

    d1:=blur1 * 1/tex1size;
    d2:=blur2 * 1/tex2size;

    xo1:=image1width/tex1size;
    yo1:=image1height/tex1size;

    xo2:=image2width/tex2size;
    yo2:=image2height/tex2size;

    glBegin(GL_QUADS);
      glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0 + d1, 0 + d1);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0 + d2, 0 + d2);
      glVertex2f(0, 0); //bottom left corner

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0 + d1, yo1 + d1);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0 + d2, yo2 + d2);
      glVertex2f(0, DisplayHeight());

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB,xo1 + d1, yo1 + d1);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB,xo2 + d2, yo2 + d2);
      glVertex2f(DisplayWidth(), DisplayHeight());

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB,xo1 + d1, 0 + d1);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB,xo2 + d2, 0 + d2);
      glVertex2f(DisplayWidth(), 0);
    glEnd;

    glActiveTextureARB(GL_TEXTURE1_ARB);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glDisable(GL_TEXTURE_2D);
    glActiveTextureARB(GL_TEXTURE0_ARB);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); }

