(********************************************************)
(*                                                      *)
(*  Codebot Object Library @ codebot.org/delphi         *)
(*                                                      *)
(*  2.00.00 Open Source Released 2008 (demo build)      *)
(*                                                      *)
(********************************************************)

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Menus,
  { TRxTrayIcon }
  RXShell,
  { Below are units from the open source codebot library }
  StyleMenus, BareOpenGL, BareOpenGLExt, BareGraphics, GraphTools, GLCtrls;

type
  TAnimatedForm = class(TForm)
    TrayIcon: TRxTrayIcon;
    PopupMenu: TPopupMenu;
    ExitMenuItem: TMenuItem;
    AboutMenuItem: TMenuItem;
    N1: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ExitMenuItemClick(Sender: TObject);
  private
    { Our offscreen bitmap }
    FBitmap: TFastBitmap;
    { Our rendering context }
    FBuffer: TOpenGLBuffer;
    { Our textures }
    FTex: array[0..3] of GLuint;
    { Called once at startup }
    procedure BufferLoad(Sender: TObject);
    { Called once at shutdown }
    procedure BufferUnload(Sender: TObject);
    { Called every frame }
    procedure BufferDraw(Sender: TObject);
    procedure UpdateLayers;
    procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
  end;

var
  AnimatedForm: TAnimatedForm;

implementation

{$R *.dfm}
{ Texture resources }
{$R images.res}

procedure TAnimatedForm.FormCreate(Sender: TObject);
begin
  { Create the offscreen bitmap  }
  FBitmap := CreateFastBitmap(200, 200, pd32);
  { Create the offscreen opengl rendering context }
  FBuffer := TOpenGLBuffer.Create(Self);
  FBuffer.Height := FBitmap.Height;
  FBuffer.Width := FBitmap.Width;
  FBuffer.OnLoad := BufferLoad;
  FBuffer.OnUnload := BufferUnload;
  FBuffer.OnDraw := BufferDraw;
  FBuffer.RefreshKind := rkThread;
  { Update the form }
  Width := FBitmap.Width;
  Height := FBitmap.Height;
  UpdateLayers;
  { Draw nice menus }
  TMenuStylizer.Create(Self);
end;

procedure TAnimatedForm.FormDestroy(Sender: TObject);
begin
  { Free the offscreen bitmap }
  DestroyFastBitmap(FBitmap);
end;

procedure TAnimatedForm.BufferLoad(Sender: TObject);
begin
  { Enable texture blending }
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  { Drawing color is opaque white }
  glColor4f(1, 1, 1, 1);
  { Clear color is transparent black }
  glClearColor(0, 0, 0, 0);
  { Load textures from embedded resource }
  glGenTextures(4, @FTex);
  BindTexture(1500, FTex[0], tfPng);
  BindTexture(1501, FTex[1], tfPng);
  BindTexture(1502, FTex[2], tfPng);
  BindTexture(1503, FTex[3], tfPng);
end;


procedure TAnimatedForm.BufferUnload(Sender: TObject);
begin
  { Delete textures }
  glDeleteTextures(4, @FTex);
end;

procedure TAnimatedForm.BufferDraw(Sender: TObject);

  { Simple immediate mode opengl rendering ... no shaders, no vertex arrays, but
    runs on all computer hardware }

  procedure DrawBox(Angle: GLfloat; Texture: GLuint);
  const
    X = 64;
  begin
    glPushMatrix;
    glRotate(Angle, 0, 0, 1);
    glBindTexture(GL_TEXTURE_2D, Texture);
    { Draw the geometry }
    glBegin(GL_QUADS);
    glTexCoord(0, 0);
    glVertex(-X, X);
    glTexCoord(0, 1);
    glVertex(-X, -X);
    glTexCoord(1, 1);
    glVertex(X, -X);
    glTexCoord(1, 0);
    glVertex(X, X);
    glEnd;
    glPopMatrix;
  end;

begin
  { Clear the opengl render buffer }
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity;
  { Let's draw using orthographic projection (no foreshortening) }
  glxBeginOrtho(200, 200);
  glTranslate(100, 100, 0);
  { Draw the paper }
  DrawBox(Sin(FBuffer.Timer.Time) * 10 - 190, FTex[0]);
  { Draw arrow 1 }
  DrawBox(FBuffer.Timer.Time * -80 + 90, FTex[1]);
  { Draw arrow 2 }
  DrawBox(FBuffer.Timer.Time * 80 +  90, FTex[2]);
  { Draw arrow 3 }
  DrawBox(FBuffer.Timer.Time * 80 * 2 + 90, FTex[3]);
  { Finish the orthographic projection }
  glxEndOrtho;
  { Copy what we've drawn into a bitmap }
  glReadPixels(0, 0, FBitmap.Width, FBitmap.Height, GL_BGRA, GL_UNSIGNED_BYTE, FBitmap.Bits);
  { Set the windows pixels to the bitmap }
  UpdateLayers;
end;

procedure TAnimatedForm.UpdateLayers;
var
  Blend: TBlendFunction;
  P: TPoint;
  S: TSize;
  DC: HDC;
begin
  { Change the form to allow layered output }
  SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);
  with Blend do
  begin
    BlendOp := AC_SRC_OVER;
    BlendFlags := 0;
    SourceConstantAlpha := $FF;
    AlphaFormat := AC_SRC_ALPHA;
  end;
  DC := GetDC(0);
  P := Point(0, 0);
  S.cx := FBitmap.Width;
  S.cy := FBitmap.Height;
  { Update the window pixels from the offscreen bitmap }
  UpdateLayeredWindow(Handle, DC, nil, @S, FBitmap.DC, @P, 0, @Blend, ULW_ALPHA);
  ReleaseDC(0, DC);
end;

procedure TAnimatedForm.WMNCHitTest(var Message: TWMNCHitTest);
begin
  { Allow the image to be dragged with the mouse }
  Message.Result := HTCAPTION;
end;

procedure TAnimatedForm.ExitMenuItemClick(Sender: TObject);
begin
  { Terminate }
  Close;
end;

end.