unit MainFm;
(*
 * Copyright (c) 2015 - 2025 Dr. Yuriy Kotsarenko. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 *)

// This example illustrates the following features:
//  * Signed Distance Field (SDF) text rendering in 3D: a technique to draw good looking text in 3D with
//    any zoom factor and an optional outline.
//  * Grapher module, which allows drawing lines, arrows and other simple shapes in 3D.
//  * Optional real-time 3D volumetric text.
//  * Third-person view camera with optional location-based zoom, which zooms in or out where the mouse is
//    pointing at.

interface

uses
  Winapi.Windows, System.Types, System.Classes, Vcl.Graphics, Vcl.Forms, Vcl.Dialogs, Vcl.Controls,
  PXT.Types, PXT.Graphics, PXT.Controls;

type
  TMainForm = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FRenderingControl: TRenderingControl;

    FDisplaySize: TPoint2i;
    FDisplayScale: Single;
    FMousePos: TPoint2i;
    FLocation: TVector3f;
    FLocationLocked: Boolean;
    FVolumetricText: Boolean;

    FDevice: TDevice;
    FSwapChain: TSwapChain;
    FCanvas: TCanvas;
    FTextRenderer: TTextRenderer;
    FTextModeller: TTextModeller;

    FTextureCabinet: TTextureCabinet;
    FScene: TScene;
    FSceneLights: TSceneLights;

    FGrapher: TGrapher;
    FModelPlane: TMeshModel;
    FModelCube: TMeshModel;

    FCamera: TActorCamera;

    FMouseRay: TRay;
    FView: TMatrix4f;
    FProjection: TMatrix4f;
    FViewProjection: TMatrix4f;
    FViewInverse: TMatrix4f;

    FAxisLength: Single;
    FAxisFinal: array[0..2] of TVector3f;
    FAxisThickness: Single;

    procedure RenderingControlResize(Sender: TObject);
    procedure RenderingControlMouseDown(Sender: TObject; Button: Vcl.Controls.TMouseButton; 
      Shift: TShiftState; X, Y: Integer);
    procedure RenderingControlMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure RenderingControlMouseUp(Sender: TObject; Button: Vcl.Controls.TMouseButton; Shift: TShiftState;
      X, Y: Integer);
    procedure RenderingControlMouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint;
      var Handled: Boolean);
    procedure RenderingControlMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint;
      var Handled: Boolean);
    procedure RenderingControlKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure RenderingControlPaint(Sender: TObject);
    procedure RenderText;
    procedure RenderText3D;
    procedure RenderChart;
    procedure RenderCubes;
    procedure SetupPlotting;
    procedure CreateModels;
    procedure CreateRenderingControl;
  public
  end;

var
  MainForm: TMainForm;

implementation
{$R *.dfm}

uses
  System.SysUtils, System.Math, PXT.Headers, PXT.Graphics.Consts;

const
  VertexElements: array[0..1] of TVertexElement = (
    (Name: 'vertexPosition'; Format: TElementFormat.Float; Count: 3; Channel: 0; Offset: 0),
    (Name: 'vertexNormal'; Format: TElementFormat.Float; Count: 3; Channel: 0; Offset: 0));

procedure TMainForm.FormCreate(Sender: TObject);
var
  LConstraints: TCameraConstraints;
  LSceneLight: PSceneLight;
  LOcclusion: TAmbientOcclusionParameters;
begin
  CreateRenderingControl;
  FDisplaySize := Point2i(ClientWidth, ClientHeight);
  FDisplayScale := PixelsPerInch / 96.0;
  try
    FDevice := TDevice.Create;
    FSwapChain := TSwapChain.Create(FDevice, FRenderingControl.WindowHandle, FDisplaySize,
      TPixelFormat.RGBA8, TPixelFormat.Unknown, 4, False);

    FCanvas := TCanvas.Create(FDevice);
    FTextRenderer := TTextRenderer.Create(FCanvas, Point2i(512, 512), TPixelFormat.LA8);
    FTextModeller := TTextModeller.Create(FDevice);
    FGrapher := TGrapher.Create(FDevice);
    FTextureCabinet := TTextureCabinet.Create(FDevice, FDisplaySize, TTextureFidelity.Medium, 8);

    // Change the strength and bias of ambient occlusion.
    LOcclusion := FTextureCabinet.AmbientOcclusion;
    LOcclusion.KernelBias := 0.05;
    LOcclusion.Strength := 1.0;
    LOcclusion.Radius := 6.0;
    FTextureCabinet.AmbientOcclusion := LOcclusion;

    FScene := TScene.CreateModeling(FDevice);
    FScene.SetVertexElements(0, VertexElements);
    FScene.SetVertexElementsFromTextModeller(1);

    FSceneLights := TSceneLights.Create(FDevice);

    // Create 3D mesh models.
    CreateModels;

    // Create camera and timer.
    FCamera := TActorCamera.Create(Vector3f(3.0, -2.5, -0.15), Vector3f(0.584, -0.585, 0.0), 320.0);

    // Limit camera position and rotation to a reasonable range.
    FillChar(LConstraints, SizeOf(TCameraConstraints), 0);
    LConstraints.PositionMin := Vector4f(-100.0, -100.0, -100.0, 150.0);
    LConstraints.PositionMax := Vector4f(100.0, 100.0, 100.0, 600.0);
    LConstraints.RotationMin.X := Pi * 0.1;
    LConstraints.RotationMax.X := Pi * 0.4;
    FCamera.Constraints := LConstraints;

    // Add some light.
    LSceneLight := FSceneLights.Add;
    LSceneLight.AmbientColor := FloatColorRGB($FFFFFF);
    LSceneLight.AlbedoColor := FloatColorRGB($FFFFFF);
    LSceneLight.SpecularColor := FloatColorRGB($FFFFFF);
    LSceneLight.Position := Vector3f(200.0, 200.0, -400.0);
  except
    on E: EAfterwarpException do
    begin
      MessageDlg(E.Message, mtError, [mbOK], 0);
      Application.Terminate;
      Exit;
    end;
  end;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FCamera.Free;
  FModelCube.Free;
  FModelPlane.Free;
  FSceneLights.Free;
  FScene.Free;
  FTextureCabinet.Free;
  FGrapher.Free;
  FTextModeller.Free;
  FTextRenderer.Free;
  FCanvas.Free;
  FSwapChain.Free;
  FDevice.Free;
end;

procedure TMainForm.RenderingControlResize(Sender: TObject);
begin
  if FSwapChain.Handle <> nil then
  begin
    FDisplaySize := Point2i(FRenderingControl.Width, FRenderingControl.Height);
    FSwapChain.Resize(FDisplaySize);
    FTextureCabinet.Size := FDisplaySize;
  end;
end;

procedure TMainForm.RenderingControlPaint(Sender: TObject);
var
  LSceneAttributes: TSceneAttributes;
  LFontParams: TFontParameters;
  LText: string;
begin
  // Configure 3D graph and scene matrices.
  SetupPlotting;

  // Enable bloom effect in texture cabinet.
  FTextureCabinet.Attributes := [TTextureCabinetAttribute.Bloom, TTextureCabinetAttribute.AmbientOcclusion];

  // Perform light assignment for 3D scene.
  FSceneLights.ViewSize := FDisplaySize;

  // Only one light is used for the entire scene, so only minimal light culling is required.
  FSceneLights.ClustersCullingMode := TClustersCullingMode.Performance;
  FSceneLights.ClusterSize := 512;
  FSceneLights.DepthSlices := 1;

  FSceneLights.Execute(FView, FProjection);

  // Prepare scene's attributes.
  LSceneAttributes := [TSceneAttribute.Normals, TSceneAttribute.SinglePass];

  // Clear texture cabinet and start rendering.
  FTextureCabinet.Clear(TTextureCabinetPass.Color, FloatColor($FFFFFFFF));
  FTextureCabinet.BeginScene(TTextureCabinetPass.Color);
  try
    FScene.Lights := FSceneLights;
    FScene.Prepare;

    if FVolumetricText then
    begin
      FScene.ActiveVertexElements := 1;
      FScene.Attributes := LSceneAttributes + [TSceneAttribute.Coloring];
      FScene.BeginScene;
      try
        RenderText3D;
      finally
        FScene.EndScene;
      end;
    end;

    FScene.ActiveVertexElements := 0;
    FScene.Attributes := LSceneAttributes;
    FScene.BeginScene;
    try
      RenderCubes;
    finally
      FScene.EndScene;
    end;

    // Render chart using Grapher module.
    RenderChart;

    // Render flat text in 3D using Canvas "projected" mode.
    FCanvas.BeginScene;
    try
      RenderText;
    finally
      FCanvas.EndScene;
    end;
  finally
    FTextureCabinet.EndScene;
  end;
  FTextureCabinet.Filter(TTextureCabinetFilterType.Occlusion, FProjection);
  FTextureCabinet.Filter(TTextureCabinetFilterType.Bloom);

  // Render on the form.
  FSwapChain.BeginScene;
  try
    FDevice.Clear([TClearLayer.Color], FloatColor($FF000000));
    FTextureCabinet.Present;

    FCanvas.BeginScene;
    try
      FCanvas.SamplerState := TCanvasSamplerState.Create(TTextureFilter.Nearest, TTextureFilter.Nearest);
      FCanvas.ContextState := TCanvasContextState.FlatText;
      FCanvas.Attributes := [];

      LFontParams := TFontParameters.Create('Segoe UI', 14.0 * FDisplayScale, TFontWeight.SemiBold);
      LFontParams.Effect.FillBrightness := 0.0;
      LFontParams.Effect.BorderBrightness := 1.0;
      LFontParams.Effect.BorderOpacity := 0.75;
      LFontParams.Effect.BorderThickness := 1.0;
      LFontParams.Effect.BorderType := TFontBorder.SemiHeavy;
      LFontParams.Effect.ShadowBrightness := 0.85;
      LFontParams.Effect.ShadowOpacity := 0.75;
      LFontParams.Effect.ShadowDistance := Point2f(2.0, 2.0);
      LFontParams.Effect.ShadowSmoothness := 2;
      FTextRenderer.FontParameters := LFontParams;

      if FDevice.Technology = TDeviceTechnology.Direct3D then
        LText := 'Direct3D (' + IntToStr(FDevice.TechVersion shr 8) + '.' +
          IntToStr((FDevice.TechVersion shr 4) and $0F) + '), features: '
      else
        LText := 'OpenGL (' + IntToStr(FDevice.TechVersion shr 8) + '.' +
          IntToStr((FDevice.TechVersion shr 4) and $0F) + '+), features: ';

      LText := LText + IntToStr(FDevice.TechFeatureVersion shr 8) + '.' +
        IntToStr((FDevice.TechFeatureVersion shr 4) and $0F);

      FTextRenderer.DrawAligned(Point2f(FDisplaySize.X - 8.0 * FDisplayScale, 4.0 * FDisplayScale),
        LText, ColorPair($A0FFFFFF), TTextAlignment.Ending, TTextAlignment.Start);

      if FVolumetricText then
        LText := '3D volumetric text. Press Space to change.'
      else
        LText := 'SDF text in 3D. Press Space to change.';

      FTextRenderer.DrawAligned(Point2f(8.0 * FDisplayScale, FDisplaySize.Y - 4.0 * FDisplayScale),
        LText, ColorPair($A0FFFFFF), TTextAlignment.Start, TTextAlignment.Ending);
    finally
      FCanvas.EndScene;
    end;
  finally
    FSwapChain.EndScene;
  end;
end;

procedure TMainForm.RenderingControlMouseDown(Sender: TObject; Button: Vcl.Controls.TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  LPosition: TVector3f;
begin
  LPosition := Vector3f(FMousePos.X, FMousePos.Y, 0.0);

  if (Button = mbLeft) and (ssShift in Shift) then
  begin
    if FLocationLocked then
    begin
      FLocationLocked := False;
      FLocation := TVector3f.Zero;
    end
    else if FLocation <> TVector3f.Zero then
      FLocationLocked := True;
  end
  else if Button = mbLeft then
    FCamera.Command(TCameraCommand.Rotation, LPosition, FDisplaySize)
  else if Button = mbRight then
    FCamera.Command(TCameraCommand.Dragging, LPosition, FDisplaySize);
end;

procedure TMainForm.RenderingControlMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  LPosition: TVector3f;
  LValid: Boolean;
begin
  FMousePos := Point2i(X, Y);
  FMouseRay := TRay.Create(FMousePos, FDisplaySize, FViewInverse, FProjection);

  if not FLocationLocked then
  begin
    LValid := False;

    if FMouseRay.IntersectPlane(TVector3f.Zero, TVector3f.AxisY, FLocation) and (FLocation.X >= 0.0) and
      (FLocation.Z <= 0.0) and (FLocation.X < FAxisLength - 14.0) and
      (FLocation.Z > -(FAxisLength - 12.0)) then
      LValid := True
    else if FMouseRay.IntersectPlane(TVector3f.Zero, TVector3f.AxisX, FLocation) and
      (FLocation.Y >= 0.0) and (FLocation.Z <= 0.0) and (FLocation.Y < FAxisLength - 12.0) and
      (FLocation.Z > -(FAxisLength - 12.0)) then
      LValid := True
    else if FMouseRay.IntersectPlane(TVector3f.Zero, -TVector3f.AxisZ, FLocation) and
      (FLocation.X >= 0.0) and (FLocation.Y >= 0.0) and (FLocation.X < FAxisLength - 14.0) and
      (FLocation.Y < FAxisLength - 12.0) then
      LValid := True;

    if not LValid then
      FLocation := TVector3f.Zero;
  end;

  LPosition := Vector3f(FMousePos.X, FMousePos.Y, 0.0);
  FCamera.Command(TCameraCommand.Update, LPosition, FDisplaySize);
  FRenderingControl.Invalidate;
end;

procedure TMainForm.RenderingControlMouseUp(Sender: TObject; Button: Vcl.Controls.TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  LPosition: TVector3f;
begin
  LPosition := Vector3f(FMousePos.X, FMousePos.Y, 0.0);
  FCamera.Command(TCameraCommand.Stop, LPosition, FDisplaySize);
end;

procedure TMainForm.RenderingControlMouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint;
  var Handled: Boolean);
begin
  FCamera.Distance := FCamera.Distance - 16.0;
  FRenderingControl.Invalidate;
end;

procedure TMainForm.RenderingControlMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint;
  var Handled: Boolean);
begin
  FCamera.Distance := FCamera.Distance + 16.0;
  FRenderingControl.Invalidate;
end;

procedure TMainForm.RenderingControlKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if Key = VK_SPACE then
  begin
    FVolumetricText := not FVolumetricText;
    FRenderingControl.Invalidate;
  end;
end;

procedure TMainForm.RenderText;
var
  LRenderingState: TRenderingState;
  LTextModifiers: TTextRenderModifiers;
  LFontParams: TFontParameters;
  LNumberCount: Integer;
  I: Integer;
begin
  // How many numbers to show over each axis.
  LNumberCount := Floor(FAxisLength / 20.0) - 1;

  // Enable depth bias to prevent depth fighting with plane.
  LRenderingState := FDevice.RenderingState;
  LRenderingState.States := LRenderingState.States and (not TRenderingState.State.DepthWrite);
  LRenderingState.DepthBias := 5.0;
  LRenderingState.SlopeDepthBias := 5.0;
  FDevice.RenderingState := LRenderingState;

  // Configure canvas for 3D projected SDF rendering.
  FCanvas.SignedDistanceField := TSignedDistanceField.Create(TSuperSampleSDF.SuperSample16x, 2.0,
    TVector3f.AxisY, 0.15, 0.45);

  FCanvas.Attributes := [TCanvasAttribute.Projected, TCanvasAttribute.SDF, TCanvasAttribute.Cubic];
  FCanvas.ContextState := TCanvasContextState.Projected;
  FCanvas.SamplerState := TCanvasSamplerState.Create(TTextureFilter.Linear, TTextureFilter.Linear);
  FCanvas.ViewProjection := FViewProjection;

  // Prepare SDF font.
  LFontParams := TFontParameters.Create('Verdana', 32, TFontWeight.SemiBold);
  LFontParams.Effect.SignedFieldDistance := 2.0;
  FTextRenderer.FontParameters := LFontParams;

  // Prepare SDF font modifiers.
  FillChar(LTextModifiers, SizeOf(TTextRenderModifiers), 0);
  LTextModifiers.Scale := 0.2;

  // "axis1"
  FCanvas.World := TMatrix4f.RotateX(Pi * 0.5);

  FTextRenderer.DrawAligned(Point2f(FAxisLength, -(FAxisThickness * 0.5 + 1.0)), 'axis1',
    ColorPair($FF000000), TTextAlignment.Middle, TTextAlignment.Ending, 1.0, False, @LTextModifiers);

  // numbers for "axis2"
  for I := 0 to LNumberCount - 1 do
    FTextRenderer.DrawAligned(Point2f(FAxisLength + (FAxisThickness * 0.5 + 1.0), (I + 1) * 20.0),
      IntToStr(I + 1), ColorPair($FF505050), TTextAlignment.Start, TTextAlignment.Middle,
      1.0, False, @LTextModifiers);

  // Mouse pointer values.
  if (not FVolumetricText) and (FLocation <> TVector3f.Zero) then
  begin
    if Abs(FLocation.Y) <= 0.01 then
      FTextRenderer.DrawAligned(Point2f(-(FAxisThickness * 0.5 + 1.0), -FLocation.Z),
        Format('%1.2f', [-FLocation.Z / 20.0]), ColorPair($FF505050), TTextAlignment.Ending,
        TTextAlignment.Middle, 1.0, False, @LTextModifiers);

    if Abs(FLocation.X) <= 0.01 then
      FTextRenderer.DrawAligned(Point2f(FAxisThickness * 0.5 + 1.0, -FLocation.Z),
        Format('%1.2f', [-FLocation.Z / 20.0]), ColorPair($FF505050), TTextAlignment.Start,
        TTextAlignment.Middle, 1.0, False, @LTextModifiers);
  end;

  // "axis2"
  FCanvas.World := TMatrix4f.RotateX(Pi * 0.5) * TMatrix4f.RotateY(-Pi * 0.5);

  FTextRenderer.DrawAligned(Point2f(-FAxisLength, -(FAxisThickness * 0.5 + 1.0)), 'axis2',
    ColorPair($FF000000), TTextAlignment.Middle, TTextAlignment.Ending, 1.0, False, @LTextModifiers);

  // numbers for "axis1"
  for I := 0 to LNumberCount - 1 do
    FTextRenderer.DrawAligned(Point2f(-FAxisLength - (FAxisThickness * 0.5 + 1.0), (I + 1) * 20.0),
      IntToStr((I + 1) * 5), ColorPair($FF505050), TTextAlignment.Ending, TTextAlignment.Middle,
      1.0, False, @LTextModifiers);

  // Mouse pointer values.
  if (not FVolumetricText) and (FLocation <> TVector3f.Zero) then
  begin
    if Abs(FLocation.Y) <= 0.01 then
      FTextRenderer.DrawAligned(Point2f(FAxisThickness * 0.5 + 1.0, FLocation.X),
        Format('%1.1f', [(FLocation.X / 20.0) * 5.0]), ColorPair($FF505050), TTextAlignment.Start,
        TTextAlignment.Middle, 1.0, False, @LTextModifiers);

    if Abs(FLocation.Z) <= 0.01 then
      FTextRenderer.DrawAligned(Point2f(-(FAxisThickness * 0.5 + 1.0), FLocation.X),
        Format('%1.1f', [(FLocation.X / 20.0) * 5.0]), ColorPair($FF505050), TTextAlignment.Ending,
        TTextAlignment.Middle, 1.0, False, @LTextModifiers);
  end;

  // "axis3"
  FCanvas.World := TMatrix4f.RotateZ(Pi * 0.5);

  FTextRenderer.DrawAligned(Point2f(FAxisLength, -(FAxisThickness * 0.5 + 1.0)), 'axis3',
    ColorPair($FF000000), TTextAlignment.Middle, TTextAlignment.Ending, 1.0, False, @LTextModifiers);

  // numbers for "axis3"
  FCanvas.World := TMatrix4f.Identity;

  for I := 0 to LNumberCount - 1 do
    FTextRenderer.DrawAligned(Point2f(-(FAxisThickness * 0.5 + 1.0), -(I + 1) * 20.0),
      IntToStr(Sqr(I + 11) * 37), ColorPair($FF505050), TTextAlignment.Ending, TTextAlignment.Middle,
      1.0, False, @LTextModifiers);

  // Mouse pointer values (on 'axis3' but rotated).
  if (not FVolumetricText) and (FLocation <> TVector3f.Zero) then
  begin
    FCanvas.World := TMatrix4f.RotateY(-Pi * 0.5);

    if Abs(FLocation.X) <= 0.01 then
      FTextRenderer.DrawAligned(Point2f(FAxisThickness * 0.5 + 1.0, -FLocation.Y),
        IntToStr(Round(Sqr((FLocation.Y / 20.0) + 10.0) * 37.0)), ColorPair($FF505050), TTextAlignment.Start,
        TTextAlignment.Middle, 1.0, False, @LTextModifiers);

    if Abs(FLocation.Z) <= 0.01 then
      FTextRenderer.DrawAligned(Point2f(FAxisThickness * 0.5 + 1.0, -FLocation.Y),
        IntToStr(Round(Sqr((FLocation.Y / 20.0) + 10.0) * 37.0)), ColorPair($FF505050), TTextAlignment.Start,
        TTextAlignment.Middle, 1.0, False, @LTextModifiers);
  end;

  if not FVolumetricText then
  begin
    // Enable Outline in SDF settings.
    FCanvas.SignedDistanceField := TSignedDistanceField.Create(TSuperSampleSDF.SuperSample16x, 2.0,
      Vector3f(-0.5, 1.0, -1.0), 0.15, 0.45);

    FCanvas.Attributes := [TCanvasAttribute.Projected, TCanvasAttribute.SDF, TCanvasAttribute.Outline,
      TCanvasAttribute.Cubic];

    // Update SDF font modifiers for Outline.
    LTextModifiers.Scale := 0.3;

    // "chart 2"
    FCanvas.World := TMatrix4f.RotateX(Pi * 0.5);

    FTextRenderer.DrawAligned(Point2f(40.0, 65.0), 'Chart 2', ColorPair($FF29D48A),
      TTextAlignment.Middle, TTextAlignment.Start, 1.0, False, @LTextModifiers);

    // Change SDF outline color.
    FCanvas.SignedDistanceField := TSignedDistanceField.Create(TSuperSampleSDF.SuperSample16x, 2.0,
      Vector3f(-0.5, 1.0, 1.0), 0.15, 0.45);

    // "chart 1"
    FCanvas.World := TMatrix4f.Identity;

    FTextRenderer.DrawAligned(Point2f(40.0, -80.0), 'Chart 1', ColorPair($FF0177FF),
      TTextAlignment.Middle, TTextAlignment.Ending, 1.0, False, @LTextModifiers);
  end;
end;

procedure TMainForm.RenderText3D;
var
  LMaterial: TObjectMaterial;
  LModifiers: TTextRenderModifiers;
begin
  FScene.World := TMatrix4f.Identity;

  LMaterial := TObjectMaterial.Default;
  LMaterial.AmbientColor := FloatColorRGB($00404040);
  LMaterial.AlbedoColor := FloatColor($D0FFFFFF);
  LMaterial.SpecularColor := FloatColorRGB($00FFFFFF);
  LMaterial.SpecularExponent := 8.0;
  FScene.Material := LMaterial;

  FTextModeller.Clear;
  FTextModeller.SetFontParameters(TFontParameters.Create('Verdana', 32.0, TFontWeight.SemiBold));

  FillChar(LModifiers, SizeOf(TTextRenderModifiers), 0);
  LModifiers.Scale := 0.3;

  FTextModeller.Transform := TMatrix4f.Identity;
  FTextModeller.Draw(Vector3f(40.0, 0.0, -80.0), 'Chart 2', 10.0, $FF29D48A, TTextAlignment.Middle,
    TTextAlignment.Ending, TTextAlignment.Start, False, @LModifiers);

  FTextModeller.Transform := TMatrix4f.RotateX(Pi * -0.5);
  FTextModeller.Draw(Vector3f(40.0, 0.0, 80.0), 'Chart 1', 10.0, $FF0177FF, TTextAlignment.Middle,
    TTextAlignment.Ending, TTextAlignment.Start, False, @LModifiers);

  FTextModeller.Prepare;
  FTextModeller.Render(FScene.SceneProgram);
end;

procedure TMainForm.RenderChart;
const
  SelectColor = $FF808080;
var
  LRenderingState, LInitialState: TRenderingState;
  LAxisFinal: array[0..2] of TVector3f;
  I, LLineCount, LLine, LAxis, LAxisNext: Integer;
  LLineTheta: Single;
  LPosition1, LPosition2: TVector3f;
begin
  // Calculate number of lines in 3D graph.
  LLineCount := Ceil(FAxisLength / 20.0);

  // Make own version of axes that is multiple of line count.
  for I := 0 to 2 do
    LAxisFinal[I] := FAxisFinal[I].Normalize * (LLineCount * 20.0);

  // Enable depth bias to prevent depth fighting with plane.
  LInitialState := FDevice.RenderingState;
  LRenderingState := LInitialState;
  LRenderingState.States := LRenderingState.States and (not TRenderingState.State.DepthWrite);
  LRenderingState.DepthBias := 5.0;
  LRenderingState.SlopeDepthBias := 5.0;
  FDevice.RenderingState := LRenderingState;

  FGrapher.Transform := FViewProjection;
  FGrapher.BeginScene;
  try
    for I := 0 to 2 do
      FGrapher.Arrow(FDisplaySize, TVector3f.Zero, FAxisFinal[I], $FF000000, FAxisThickness,
        FAxisThickness * 3.0, TLineCaps.Butt);

    for LLine := 1 to LLineCount - 1 do
      for LAxis := 0 to 2 do
      begin
        LLineTheta := 1.0 - (LLine / LLineCount);
        LAxisNext := (LAxis + 2) mod 3;

        LPosition1 := LAxisFinal[LAxisNext].Lerp(TVector3f.Zero, LLineTheta);
        LPosition2 := LPosition1 + LAxisFinal[LAxis].Normalize * FAxisLength;

        FGrapher.DottedLine(LPosition2, LPosition1, $FF808080, $FF808080, FAxisThickness * 0.75,
          FAxisThickness * 0.75, 4.0, TPointShape.Round, Pi * 0.15);

        LPosition1 := LAxisFinal[LAxis].Lerp(TVector3f.Zero, LLineTheta);
        LPosition2 := LPosition1 + LAxisFinal[LAxisNext].Normalize * FAxisLength;

        FGrapher.DottedLine(LPosition2, LPosition1, $FF808080, $FF808080, FAxisThickness * 0.75,
          FAxisThickness * 0.75, 4.0, TPointShape.Round, Pi * 0.15);
      end;

    // Show selection markers for mouse pointer.
    if (not FVolumetricText) and (FLocation <> TVector3f.Zero) then
    begin
      if Abs(FLocation.Y) <= 0.01 then
      begin
        FGrapher.Line(Vector3f(0.0, 0.0, FLocation.Z), Vector3f(FLocation.X, 0.0, FLocation.Z),
          SelectColor, SelectColor, FAxisThickness * 0.75, FAxisThickness * 0.75 );

        FGrapher.Line(Vector3f(FLocation.X, 0.0, 0.0), Vector3f(FLocation.X, 0.0, FLocation.Z),
          SelectColor, SelectColor, FAxisThickness * 0.75, FAxisThickness * 0.75 );
      end
      else if Abs(FLocation.X) <= 0.01 then
      begin
        FGrapher.Line(Vector3f(0.0, 0.0, FLocation.Z), Vector3f(0.0, FLocation.Y, FLocation.Z),
          SelectColor, SelectColor, FAxisThickness * 0.75, FAxisThickness * 0.75);

        FGrapher.Line(Vector3f(0.0, FLocation.Y, 0.0), Vector3f(0.0, FLocation.Y, FLocation.Z),
          SelectColor, SelectColor, FAxisThickness * 0.75, FAxisThickness * 0.75 );
      end
      else if Abs(FLocation.Z) <= 0.01 then
      begin
        FGrapher.Line(Vector3f(0.0, FLocation.Y, 0.0), Vector3f(FLocation.X, FLocation.Y, 0.0),
          SelectColor, SelectColor, FAxisThickness * 0.75, FAxisThickness * 0.75);

        FGrapher.Line(Vector3f(FLocation.X, 0.0, 0.0), Vector3f(FLocation.X, FLocation.Y, 0.0),
          SelectColor, SelectColor, FAxisThickness * 0.75, FAxisThickness * 0.75 );
      end;

      FGrapher.Point(FLocation, SelectColor, FAxisThickness * 4.0, TPointShape.Round);
    end;
  finally
    FGrapher.EndScene;
  end;
  FDevice.RenderingState := LInitialState;
end;

procedure TMainForm.RenderCubes;
const
  Heights: array[0..1, 0..3] of Integer = ((20, 60, 40, 80), (10, 30, 60, 40));
var
  LMaterial: TObjectMaterial;
  I: Integer;
begin
  LMaterial := TObjectMaterial.Default;
  LMaterial.AmbientColor := FloatColorRGB($203D53);
  LMaterial.AlbedoColor := FloatColor($D00083E4);
  LMaterial.SpecularColor := FloatColorRGB($FFFFFF);
  LMaterial.SpecularExponent := 8.0;
  FScene.Material := LMaterial;

  // Draw first row of cubes.
  for I := 0 to 3 do
  begin
    FScene.World :=
      TMatrix4f.Scale(20.0, Heights[0, I], 20.0) *
      TMatrix4f.Translate(10.0 + I * 20.0, Heights[0, I] * 0.5, -10.0);

    FModelCube.Draw(FScene.SceneProgram);
  end;

  // Draw second row of cubes.
  LMaterial.AmbientColor := FloatColorRGB($FF235240);
  LMaterial.AlbedoColor := FloatColor($D000D784);
  FScene.Material := LMaterial;

  for I := 0 to 3 do
  begin
    FScene.World :=
      TMatrix4f.Scale(20.0, Heights[1, I], 20.0) *
      TMatrix4f.Translate(10.0 + I * 20.0, Heights[1, I] * 0.5, -50.0);

    FModelCube.Draw(FScene.SceneProgram);
  end;

  // Draw a plane on bottom to make ambient occlusion more apparent.
  LMaterial.AmbientColor := FloatColorRGB($FFFFFFFF);
  LMaterial.AlbedoColor := TFloatColor.TranslucentBlack;
  LMaterial.SpecularColor := TFloatColorRGB.Black;
  FScene.Material := LMaterial;

  FScene.World := TMatrix4f.Scale(400.0, 1.0, 400.0) * TMatrix4f.Translate(0.0, -0.1, 0.0);
  FModelPlane.Draw(FScene.SceneProgram);

  if FVolumetricText then
  begin
    FScene.World := TMatrix4f.Scale(90.0, 1.0, 100.0) * TMatrix4f.RotateX(Pi * -0.5) *
      TMatrix4f.Translate(45.0, 50.0, 0.5);
    FModelPlane.Draw(FScene.SceneProgram);
  end;
end;

procedure TMainForm.SetupPlotting;
begin
  FAxisThickness := Max((FDisplaySize.Y * 3.0 / 600.0) * 320.0 / FCamera.Distance, 3.0) * FDisplayScale;
  FAxisLength := Max(110.0 * FCamera.Distance / 320.0, 110.0);

  FAxisFinal[0] := Vector3f(FAxisLength, 0.0, 0.0);
  FAxisFinal[1] := Vector3f(0.0, FAxisLength, 0.0);
  FAxisFinal[2] := Vector3f(0.0, 0.0, -FAxisLength);

  // View matrix is extracted directly from Object manipulation camera.
  FView := FCamera.View;

  // Build projection matrix.
  FProjection := TMatrix4f.PerspectiveFOVY(Pi * 0.25, FDisplaySize.X / FDisplaySize.Y, 10.0, 1400.0);

  // Set the matrices to the modeling scene.
  FScene.View := FView;
  FScene.Projection := FProjection;

  // Precalculate View - Projection matrix.
  FViewProjection := FView * FProjection;

  // Calculate mouse pointing ray.
  FViewInverse := FView.Inverse;
  FMouseRay := TRay.Create(FMousePos, FDisplaySize, FViewInverse, FProjection);
end;

procedure TMainForm.CreateModels;
var
  LMeshBuffer: TMeshBuffer;
begin
  LMeshBuffer := TMeshBuffer.Create;
  try
    LMeshBuffer.Plane(1, 1, TVector3f.Zero, TVector3f.AxisX, TVector3f.AxisZ, TVector3f.AxisY, TPoint2f.Zero,
      TPoint2f.Unity, TFloatColor.White);

    FModelPlane := LMeshBuffer.Model(FDevice, VertexElements);

    LMeshBuffer.Clear;
    LMeshBuffer.Transform := TMatrix4f.RotateX(Pi * 0.5);
    LMeshBuffer.SuperEllipse(32, 32, TVector3f.Zero, TVector3f.Unity * 0.5, 0.0, Pi * 2.0, 0.15, -Pi * 0.5,
      Pi * 0.5, 0.15, TPoint2f.Zero, TPoint2f.Unity, TFloatColor.White);

    FModelCube := LMeshBuffer.Model(FDevice, VertexElements);
  finally
    LMeshBuffer.Free;
  end;
end;

procedure TMainForm.CreateRenderingControl;
begin
  FRenderingControl := TRenderingControl.Create(nil);
  FRenderingControl.Parent := Self;
  FRenderingControl.Align := alClient;
  FRenderingControl.Width := 256;
  FRenderingControl.Height := 256;
  FRenderingControl.OnPaint := RenderingControlPaint;
  FRenderingControl.OnResize := RenderingControlResize;
  FRenderingControl.OnMouseDown := RenderingControlMouseDown;
  FRenderingControl.OnMouseMove := RenderingControlMouseMove;
  FRenderingControl.OnMouseUp := RenderingControlMouseUp;
  FRenderingControl.OnMouseWheelUp := RenderingControlMouseWheelUp;
  FRenderingControl.OnMouseWheelDown := RenderingControlMouseWheelDown;
  FRenderingControl.OnKeyDown := RenderingControlKeyDown;
end;

end.