unit ex1;

{$mode objfpc}{$H+}

interface

{ This is a simple 3D objet, a pyramid with a hexagonal base. The object is designed in the constructor.
  The CreateObject method returns an interface to an empty object. An interface is similar to a class
  except you don't have to call Free.
  
  A 3D object has a MainPart property, which contains the vertices describing the objet. It can contains subparts,
  but here it is not the case. Parts can be rotated and scaled relative to their container. The MainPart is rotated
  and scaled relative to the whole scene.
  
  Faces are created using vertices, so that they will follow the movements of these vertices. Here the default color
  SandColor is defined for the whole object.
  
  In order to make it attractive, a lighting is defined. The simplest way is to use a directional light, so that
  you don't have to bother with the coordinates of the light source. 
  
  The background is filled with a gradient. Note that it is a vertical gradient which is very fast to draw, because
  each scanline is filled with one color. }

uses
  Classes, SysUtils, BGRAScene3D, BGRABitmapTypes
  {$IFNDEF NO_OPENGL_SURFACE}, BGRAOpenGL, BGRAOpenGL3D{$ENDIF};

type

  { TExample1 }

  TExample1 = class({$IFNDEF NO_OPENGL_SURFACE}TBGLScene3D{$ELSE}TBGRAScene3D{$ENDIF})
    SandColor: TBGRAPixel;
    constructor Create;
    procedure Render; override;
    {$IFNDEF NO_OPENGL_SURFACE}
    procedure RenderGL(ACanvas: TBGLCustomCanvas; AMaxZ: single=1000); override;
    {$ENDIF}
  end;

implementation

{ TExample1 }

constructor TExample1.Create;
var
  base: array of IBGRAVertex3D;
  top: IBGRAVertex3D;
begin
  inherited Create;

  SandColor := BGRA(255,240,128);

  //create a pyramid
  with CreateObject(SandColor) do
  begin
    top := MainPart.Add(0,-15,0);
    //pyramid base is in a clockwise order if we look the pyramid from under
    base := MainPart.Add([-20,15,-20, 0,15,-30, 20,15,-20, 20,15,20, 0,15,30, -20,15,20]);
    AddFace(base);
    //add four faces, the three vertices are in a clockwise order
    AddFace([base[0],top,base[1]]);
    AddFace([base[1],top,base[2]]);
    AddFace([base[2],top,base[3]]);
    AddFace([base[3],top,base[4]]);
    AddFace([base[4],top,base[5]]);
    AddFace([base[5],top,base[0]]);

    MainPart.Scale(1.3);
    MainPart.RotateYDeg(30);
    MainPart.RotateXDeg(20);
    MainPart.Translate(0,-5,0);
  end;

  //set ambiant lightness to dark (1 is normal lightness, 2 is complete whiteness)
  AmbiantLightness := 0.5;
  //add a directional light from top-left, maximum lightness will be 0.5 + 1 = 1.5
  AddDirectionalLight(Point3D(1,1,1),0.5);

  //we can have high quality antialiasing because it is a simple scene
  RenderingOptions.PerspectiveMode := pmLinearMapping;
end;

procedure TExample1.Render;
begin
  //fill background
  Surface.GradientFill(0,0,Surface.Width,Surface.Height,
          MergeBGRA(SandColor,1,BGRABlack,1),
          MergeBGRA(SandColor,1,BGRABlack,2),
          gtLinear,PointF(0,0),PointF(0,Surface.Height),dmSet);

  inherited Render;
end;

{$IFNDEF NO_OPENGL_SURFACE}
procedure TExample1.RenderGL(ACanvas: TBGLCustomCanvas; AMaxZ: single);
begin
  //fill background
  ACanvas.FillRectLinearColor(0,0,BGLCanvas.Width,BGLCanvas.Height,
          MergeBGRA(SandColor,1,BGRABlack,1),MergeBGRA(SandColor,1,BGRABlack,1),
          MergeBGRA(SandColor,1,BGRABlack,2),MergeBGRA(SandColor,1,BGRABlack,2),
          False);

  inherited RenderGL(ACanvas, AMaxZ);
end;
{$ENDIF}

end.

