{
Demo of GLEParticleMasksManager unit.

Version History:
  29/01/2007 - DaStr - Initial version (a bit modified demo by Kenneth Poulter) 

}
unit uParticleMasking;

interface

uses
  SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, GLBitmapFont, GLWindowsFont, GLTexture,
  GLCadencer, GLLCLViewer, GLScene, StdCtrls, GLObjects,
  GLParticleFX, GLVectorGeometry, GLVectorTypes, GLEParticleMasksManager,
  GLGeomObjects, GLAsyncTimer, GLCrossPlatform, GLMaterial, GLCoordinates,
  GLBaseClasses;

const

  PlaneHeights = 5;
  PlaneWidths  = 5;
  PlaneDepths  = 5;
  PlaneOffsets = 3;

type
  TForm1 = class(TForm)
    MaskBox: TGroupBox;
    GLScene: TGLScene;
    SceneViewer: TGLSceneViewer;
    GLCadencer: TGLCadencer;
    MatLib: TGLMaterialLibrary;
    WinFont: TGLWindowsBitmapFont;
    XImage: TImage;
    Splitter1: TSplitter;
    XLabel: TLabel;
    YLabel: TLabel;
    ZLabel: TLabel;
    YImage: TImage;
    ZImage: TImage;
    Camera: TGLCamera;
    Target: TGLDummyCube;
    XPlane: TGLPlane;
    YPlane: TGLPlane;
    ZPlane: TGLPlane;
    Light: TGLLightSource;
    PLManager: TGLPointLightPFXManager;
    PFXRenderer: TGLParticleFXRenderer;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    GLEParticleMasksManager1: TGLEParticleMasksManager;
    Panel1: TPanel;
    Label1: TLabel;
    Label2: TLabel;
    Button4: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    Sphere: TGLSphere;
    CheckBox1: TCheckBox;
    GLArrowLine1: TGLArrowLine;
    AsyncTimer1: TGLAsyncTimer;
    procedure GLCadencerProgress(Sender: TObject; const DeltaTime, newTime: Double);
    procedure FormCreate(Sender: TObject);
    procedure SceneViewerMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure PLManagerCreateParticle(Sender: TObject; aParticle: TGLParticle);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Edit3Change(Sender: TObject);
    procedure Edit4Change(Sender: TObject);
    procedure Edit5Change(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure AsyncTimer1Timer(Sender: TObject);
    procedure Edit2Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    mx, my: Integer;
    procedure RefreshMask;
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

procedure TForm1.GLCadencerProgress(Sender: TObject; const DeltaTime, newTime: Double);
begin
  Sphere.TurnAngle := -newTime * 20;
  Sphere.Position.X := Cos(DegToRad(newTime * 20)) * 1.5;
  Sphere.Position.Z := Sin(DegToRad(newTime * 20)) * 1.5;
  SceneViewer.Invalidate;
end;

// with formcreate, we are just drawing some pre-rendered masks

procedure TForm1.FormCreate(Sender: TObject);
begin
  RefreshMask;
end;

procedure TForm1.SceneViewerMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if ssLeft in Shift then
    Camera.MoveAroundTarget(my - Y, mx - X);

  mx := X;
  my := Y;
end;

procedure TForm1.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  Camera.FocalLength := Camera.FocalLength + (WheelDelta / 150);
  Handled := True;
end;

procedure TForm1.PLManagerCreateParticle(Sender: TObject; aParticle: TGLParticle);
var
  I: Integer;
  Particle: TGLParticle;

begin
  // using point lights just coz it is easier to see the effect
  if PLManager.Particles.ItemCount > 0 then
    for I := 0 to PLManager.Particles.ItemCount - 1 do
    begin
      Particle := PLManager.Particles.Items[I];
      // first we find the particles that are tagged (since before particle creation, the position is overridden)
      if Particle.Tag = 1 then
      begin
        if CheckBox1.Checked then
          GLEParticleMasksManager1.SetParticlePositionFromMaskTarget(Particle, 'mask', Sphere)
        else
          GLEParticleMasksManager1.SetParticlePositionFromMask(Particle, 'mask');
        Particle.Tag := 0;
      end;
    end;
  // we tag the new particle for when another particle is made so we know this one needs updating aswell
  aParticle.Tag := 1;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Camera.Position.Z := 0;
  Camera.Position.Y := 0;
  Camera.Position.X := 4;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Camera.Position.X := 0;
  Camera.Position.Z := 0.01;
  Camera.Position.Y := 4;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Camera.Position.X := 0;
  Camera.Position.Y := 0;
  Camera.Position.Z := 4;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  FormCreate(Sender);
end;

procedure TForm1.Edit3Change(Sender: TObject);
begin
  GLEParticleMasksManager1.ParticleMaskByName('mask').PitchAngle := StrToFloatDef(Edit3.Text, 0);
end;

procedure TForm1.Edit4Change(Sender: TObject);
begin
  GLEParticleMasksManager1.ParticleMaskByName('mask').RollAngle := StrToFloatDef(Edit4.Text, 0);
end;

procedure TForm1.Edit5Change(Sender: TObject);
begin
  GLEParticleMasksManager1.ParticleMaskByName('mask').TurnAngle := StrToFloatDef(Edit5.Text, 0);
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  Sphere.Visible := CheckBox1.Checked;
end;

procedure TForm1.AsyncTimer1Timer(Sender: TObject);
begin
  Caption := FormatFloat('FPS: 0.0', SceneViewer.FramesPerSecond) + ' Particle Count: ' + IntToStr(PLManager.ParticleCount);
  SceneViewer.ResetPerformanceMonitor;
end;

procedure TForm1.RefreshMask;
var
  Rect:   TRect;
  Mat:    TGLLibMaterial;
  Letter: Char;
  Depth:  Integer;

begin

  Letter := Edit2.Text[1];
  Depth := StrToIntDef(Edit1.Text, 50);

  XPlane.Position.X := -PlaneOffSets;
  XPlane.Height := PlaneHeights;
  XPlane.Width := PlaneWidths;

  YPlane.Position.Y := -PlaneOffSets;
  YPlane.Height := PlaneHeights;
  YPlane.Width := PlaneWidths;

  ZPlane.Position.Z := -PlaneOffSets;
  ZPlane.Height := PlaneHeights;
  ZPlane.Width := PlaneWidths;

  Rect.Left := 0;
  Rect.Top := 0;
  Rect.Bottom := XImage.Height;
  Rect.Right := XImage.Width;

  XImage.Canvas.Font.Name := 'Arial';
  XImage.Canvas.Font.Size := 180;
  XImage.Canvas.Font.Color := clWhite;
  XImage.Canvas.Pen.Color := clBlack;
  XImage.Canvas.Pen.Style := psSolid;
  XImage.Canvas.Brush.Color := clBlack;
  XImage.Canvas.Brush.Style := bsSolid;

  XImage.Canvas.FillRect(Rect);

  XImage.Canvas.TextOut(round((XImage.Width - XImage.Canvas.TextWidth(Letter)) / 2), round((XImage.Height - XImage.Canvas.TextHeight(Letter)) / 2), Letter);

  Mat := MatLib.LibMaterialByName('XMask');
  with Mat.Material.Texture.Image as TGLPersistentImage do
  begin
    Picture.Bitmap.Height := XImage.Height;
    Picture.Bitmap.Width := XImage.Width;
    Picture.Bitmap.Canvas.Draw(0, 0, XImage.Picture.Graphic);
  end;

  // this is a very recent implementation, the ability to generate other masks from 1 mask, so it satisfies
  // the requirements for the particle mask manager. useful for text and making basic shapes (cylinders etc)

  GLEParticleMasksManager1.ParticleMaskByName('mask').GenerateMaskFromProjection(pptXMask, pptYMask, Depth);
  GLEParticleMasksManager1.ParticleMaskByName('mask').GenerateMaskFromProjection(pptXMask, pptZMask, Depth);

  Mat := MatLib.LibMaterialByName('YMask');
  with Mat.Material.Texture.Image as TGLPersistentImage do
    YImage.Canvas.Draw(0, 0, Picture.Graphic);

  Mat := MatLib.LibMaterialByName('ZMask');
  with Mat.Material.Texture.Image as TGLPersistentImage do
    ZImage.Canvas.Draw(0, 0, Picture.Graphic);
end;

procedure TForm1.Edit2Change(Sender: TObject);
begin
  RefreshMask;
end;

end.
