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:
//  * Object hierarchial structure, where smaller objects are parented to bigger objects. Therefore, when
//    a bigger object is moved, it also moves all its children with it.
//  * Bone joint connections for rotating and moving portions of robotic arm, and forklift.
//  * Movement waypoints for moving boxes along two pre-defined paths on the bands.
//  * Object selection, which uses object's bounding boxes and accompanying voxel representation to
//    determine accurately what object is being pointed at, including approximate 3D location that mouse
//    is pointing on the object's surface.
//  * Object manipulation, where objects can be moved and/or rotated with mouse and keyboard.
//  * "Auto-draw" functionality for rendering objects using automatic instancing, while applying correctly
//    their materials and colors.
//  * Selection highlight effect, where a colored glow outline can be applied to one or more objects.
//  * 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.
//  * Volumetric text in 3D, which is represented as a selectable object with the corresponding mesh.
//  * HDR lighting, ambient occlusion and bloom effect.
//  * Third-person view camera with location-based zoom (zooms in or out where the mouse is pointing at).

interface

uses
  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;

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

    FMeshes: TSceneMeshes;
    FObjects: TObjectModels;
    FLights: TSceneLights;
    FMaterials: TObjectMaterials;

    FTextureCabinet: TTextureCabinet;
    FShadowCastingAtlas: TShadowCastingAtlas;
    FSelectionHighlight: TSelectionHighlight;
    FShadowCasterView: TObjectModelView;

    FSceneDepthNormals: TScene;
    FSceneModeling: TScene;

    FCamera: TActorCamera;
    FTimer: TTimer;
    FViewport: TObjectModelView;
    FProjection: TMatrix4f;

    FSelectionPoint: TVector3f;
    FObjectDragAxis: TVector3f;
    FMousePointAtPlane: TVector3f;
    FObjectRotation: Integer;
    FRotatingView: Boolean;
    FSelected: TObjectModel;
    FMouseRay: TRay;

    FTimerTicks: Integer;
    FDirectionPlaneAngle: Single;
    FDirectionPlaneToggle: Integer;
    FBoxesPRNG: TRandomSequence;
    FDrawCalls: Integer;

    procedure ApplicationIdle(Sender: TObject; var Done: Boolean);
    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 RenderingControlPaint(Sender: TObject);
    procedure RenderObjectHighlight(const AScene: TScene; const ASelected: TObjectModel);
    procedure RenderTextLabels(const AViewProjection: TMatrix4f);
    procedure UpdateMovement;
    procedure ReleaseObjectMovementTracking;
    procedure CreateObjects;
    function CreateObject(const AObjectName, AMeshName: string; const AParent: TObjectModel;
      const APosition: TVector3f; const AMaterial: Integer; const AAngle: Single = 0.0;
      const AAttributes: TModelAttributes = [TModelAttribute.Visible, TModelAttribute.Selectable]): TObjectModel;
    function RandomizeBoxAngle: Single;
    procedure CreateLightsAndMaterials;
    procedure CreateAndLoadMeshes;
    procedure CreateMeshText3D(const ABuffer: TMeshBuffer);
    procedure CreateRenderingControl;
  public
  end;

var
  MainForm: TMainForm;

implementation
{$R *.dfm}

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

const
  VertexElements: array[0..2] of TVertexElement = (
    (Name: 'vertexPosition'; Format: TElementFormat.Float; Count: 3; Channel: 0; Offset: 0),
    (Name: 'vertexNormal'; Format: TElementFormat.Float; Count: 3; Channel: 0; Offset: 0),
    (Name: 'vertexColor'; Format: TElementFormat.UnsignedByte; Count: 4; Channel: 0 or TVertexElement.Normalized; Offset: 0));

  LightPosition: TVector3f = (X: 300.0; Y: 800.0; Z: -300.0);

type
  PObjectMovementTracking = ^TObjectMovementTracking;
  TObjectMovementTracking = record
    Distance: Single;
    Group: Byte;
    Angle: Single;
  end;

procedure TMainForm.FormCreate(Sender: TObject);
var
  LDepthClipNegative: Boolean;
  LAmbientOcclusion: TAmbientOcclusionParameters;
begin
  CreateRenderingControl;
  FDisplaySize := Point2i(FRenderingControl.Width, FRenderingControl.Height);
  FDisplayScale := PixelsPerInch / 96.0;
  LDepthClipNegative := FDevice.Behavior and TDeviceBehavior.DepthClipNegative <> 0;
  try
    FDevice := TDevice.Create;
    FSwapChain := TSwapChain.Create(FDevice, FRenderingControl.WindowHandle, FDisplaySize);
    FCanvas := TCanvas.Create(FDevice);
    FTextRenderer := TTextRenderer.Create(FCanvas, Point2i(256, 256), TPixelFormat.LA8);

    FMeshes := TSceneMeshes.Create(FDevice);
    FObjects := TObjectModels.Create(FMeshes);
    FLights := TSceneLights.Create(FDevice);
    FMaterials := TObjectMaterials.Create;

    FTextureCabinet := TTextureCabinet.Create(FDevice, FDisplaySize, TTextureFidelity.High, 8);
    // Configure Ambient Occlusion effect.
    LAmbientOcclusion := FTextureCabinet.AmbientOcclusion;
    LAmbientOcclusion.KernelBias := 0.05;
    LAmbientOcclusion.Strength := 1.0;
    FTextureCabinet.AmbientOcclusion := LAmbientOcclusion;

    FShadowCastingAtlas := TShadowCastingAtlas.Create(FDevice, Point2i(256, 256), TTechniqueShadows.EVSM, 8);
    FSelectionHighlight := TSelectionHighlight.Create(FDevice, FDisplaySize);

    FSceneDepthNormals := TScene.CreateDepthNormals(FDevice);
    FSceneDepthNormals.SetVertexElements(0, VertexElements);

    FSceneModeling := TScene.CreateModeling(FDevice);
    FSceneModeling.SetVertexElements(0, VertexElements);

    FViewport := FObjects.CreateView;
    FShadowCasterView := FObjects.CreateView;
    FShadowCasterView.View := TMatrix4f.LookAt(LightPosition, TVector3f.Zero, TVector3f.AxisY);
    FShadowCasterView.SetProjection(TMatrix4f.PerspectiveFOVY(Pi * 0.3, 1.0, 200.0, 1400.0,
      LDepthClipNegative), LDepthClipNegative);

    FShadowCastingAtlas.Add(Point2i(256, 256));

    FCamera := TActorCamera.Create(Vector3f(-80.0, 0.0, 0.0), Vector3f(0.906, -0.0192, 0.0), 700.0);
    FCamera.Constraints := TCameraConstraints.Create(
      Vector4f(-400.0, -100.0, -400.0,  200.0),
      Vector4f( 400.0,  100.0,  400.0, 1000.0),
      Vector4f(Pi * 0.01, 0.0, 0.0, 0.0),
      Vector4f(Pi * 0.49, 0.0, 0.0, 0.0));

    FTimer := TTimer.Create;

    CreateAndLoadMeshes;
    CreateLightsAndMaterials;
    CreateObjects;
  except
    on E: EAfterwarpException do
    begin
      MessageDlg(E.Message, mtError, [mbOK], 0);
      Application.Terminate;
      Exit;
    end;
  end;
  Application.OnIdle := ApplicationIdle;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FTimer.Free;
  FCamera.Free;
  ReleaseObjectMovementTracking;
  FObjects.Free;
  FMeshes.Free;
  FMaterials.Free;

  FSceneModeling.Free;
  FSceneDepthNormals.Free;
  FTextureCabinet.Free;
  FLights.Free;
  FSelectionHighlight.Free;
  FShadowCastingAtlas.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;
    FSelectionHighlight.Size := FDisplaySize;
    FRenderingControl.Invalidate;
  end;
end;

procedure TMainForm.RenderingControlPaint(Sender: TObject);
var
  LDepthClipNegative: Boolean;
  LCabinetAttributes: TTextureCabinetAttributes;
  LAttributes: TSceneAttributes;
  LFontParams: TFontParameters;
  LTechString: string;
  LDistance: Single;
  LShadowCaster: TShadowCaster;
begin
  UpdateMovement;

  FDrawCalls := 0;
  LDepthClipNegative := FDevice.Behavior and TDeviceBehavior.DepthClipNegative <> 0;
  FDisplayScale := PixelsPerInch / 96.0;

  FProjection := TMatrix4f.PerspectiveFOVY(Pi * 0.25, FDisplaySize.X / FDisplaySize.Y,
    10.0, 2000.0, LDepthClipNegative);

  FViewport.View := FCamera.View;
  FViewport.SetProjection(FProjection, LDepthClipNegative);

  FViewport.Update;

  // Update texture cabinet's attributes.
  LCabinetAttributes := [TTextureCabinetAttribute.Bloom
    , TTextureCabinetAttribute.BloomCubic
    , TTextureCabinetAttribute.AmbientOcclusion
  ];

  if FDisplayScale >= 2.0 then
  begin // On 4K display, apply downscale for bloom and AO.
    LCabinetAttributes := LCabinetAttributes + [
      TTextureCabinetAttribute.BloomDownscale,
      TTextureCabinetAttribute.AmbientOcclusionDownscale];
  end;

  FTextureCabinet.Attributes := LCabinetAttributes;

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

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

  FLights.Execute(FCamera.View, FProjection);

  // Perform object selection.
  FMouseRay := TRay.Create(FMousePos, FDisplaySize, FCamera.View.Inverse, FProjection);

  if (FObjectRotation = 0) and FObjectDragAxis.Empty then
  begin
    FSelected := TObjectModel(NullObject);

    if not FRotatingView then
    begin
      FSelected := FViewport.Select(FMouseRay, LDistance);
      if FSelected.Handle <> nil then
        FSelectionPoint := FMouseRay.Origin + FMouseRay.Direction * LDistance;
    end;
  end;

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

  // Render shadow map.
  FShadowCastingAtlas.BorderFill;

  LShadowCaster := FShadowCastingAtlas.Casters[0];
  LShadowCaster.ViewProjection := FShadowCasterView.ViewProjection;

  FShadowCasterView.Update;
  LShadowCaster.Clear;
  LShadowCaster.BeginScene;
  try
    FSceneDepthNormals.Attributes := [];
    FSceneDepthNormals.View := FShadowCasterView.View;
    FSceneDepthNormals.Projection := FShadowCasterView.Projection;

    FSceneDepthNormals.BeginScene;
    try
      // Use "auto-draw" helper to automatically draw all objects visible within the view frustum of
      // shadow caster.
      FShadowCasterView.AutoDraw(FSceneDepthNormals, 0, FDrawCalls);
    finally
      FSceneDepthNormals.EndScene;
    end;
  finally
    LShadowCaster.EndScene;
  end;
  LShadowCaster.Filter;

  // Render main scene.
  FTextureCabinet.Clear(TTextureCabinetPass.Color, FloatColor($FFE0E0E0));
  FTextureCabinet.BeginScene(TTextureCabinetPass.Color);
  try
    FSceneModeling.Attributes := LAttributes;
    FSceneModeling.View := FCamera.View;
    FSceneModeling.Projection := FProjection;

    FSceneModeling.Lights := FLights;
    FSceneModeling.ShadowCastingAtlas := FShadowCastingAtlas;
    FSceneModeling.TextureCabinet := FTextureCabinet;

    FSceneModeling.Prepare;
    FSceneModeling.BeginScene;
    try
      // Use "auto-draw" helper to automatically draw all objects visible within the camera's view frustum.
      FViewport.AutoDraw(FSceneModeling, FMaterials, 0, FDrawCalls);
    finally
      FSceneModeling.EndScene;
    end;
  finally
    FTextureCabinet.EndScene;
  end;
  FTextureCabinet.Filter(TTextureCabinetFilterType.Occlusion, FProjection);

  // Render text labels.
  FTextureCabinet.BeginScene(TTextureCabinetPass.Color);
  try
    FCanvas.BeginScene;
    try
      RenderTextLabels(FCamera.View * FProjection);
    finally
      FCanvas.EndScene;
    end;
  finally
    FTextureCabinet.EndScene;
  end;
  FTextureCabinet.Filter(TTextureCabinetFilterType.Bloom);

  // Render selection highlight effect.
  if FSelected.Handle <> nil then
  begin
    FSelectionHighlight.Clear;
    FSelectionHighlight.BeginScene;
    try
      FSceneModeling.Lights := TSceneLights(NullObject);
      FSceneModeling.ShadowCastingAtlas := TShadowCastingAtlas(NullObject);
      FSceneModeling.TextureCabinet := TTextureCabinet(NullObject);

      FSceneModeling.Attributes := [TSceneAttribute.Normals];

      FSceneModeling.Prepare;
      FSceneModeling.BeginScene;
      try
        RenderObjectHighlight(FSceneModeling, FSelected);
      finally
        FSceneModeling.EndScene;
      end;
    finally
      FSelectionHighlight.EndScene;
    end;
    FSelectionHighlight.Filter;
  end;

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

    FCanvas.BeginScene;
    try
      FCanvas.Attributes := [];
      FCanvas.ContextState := TCanvasContextState.FlatScene;
      FCanvas.SamplerState := TCanvasSamplerState.Default;

      if FSelected.Handle <> nil then
      begin // Render selection highlight on top of the scene.
        FCanvas.Quad(FSelectionHighlight.Texture[TSelectionHighlightTextureType.Highlight],
          Quad(0.0, 0.0, FDisplaySize.X, FDisplaySize.Y), FSelectionHighlight.TextureCoordinates, $FFFFFFFF);

        FCanvas.Quad(FSelectionHighlight.Texture[TSelectionHighlightTextureType.HighlightMask],
          Quad(0.0, 0.0, FDisplaySize.X, FDisplaySize.Y), FSelectionHighlight.TextureCoordinates, $80FFFFFF);
      end;

      // Render information text.
      FCanvas.ContextState := TCanvasContextState.FlatText;

      LFontParams := TFontParameters.Create('Arial', 14.0 * FDisplayScale, TFontWeight.SemiBold);
      LFontParams.Effect.FillBrightness := 0.0;
      LFontParams.Effect.BorderBrightness := 1.0;
      LFontParams.Effect.BorderOpacity := 0.75;
      LFontParams.Effect.BorderType := TFontBorder.SemiHeavy;
      LFontParams.Effect.ShadowBrightness := 0.85;
      LFontParams.Effect.ShadowOpacity := 0.4;
      FTextRenderer.FontParameters := LFontParams;

      FTextRenderer.DrawAligned(Point2f(FDisplaySize.X * 0.5, 4.0 * FDisplayScale),
        'Shift/Alt + Left/Right mouse buttons: move/rotate objects.',
        ColorPair($FFA5E7B8, $FF77FFCE), TTextAlignment.Middle, TTextAlignment.Start);

      FTextRenderer.DrawAligned(Point2f(FDisplaySize.X * 0.5, FDisplaySize.Y - 4.0 * FDisplayScale),
        'Left mouse button: rotate scene, right button: move scene, wheel: zoom.',
        ColorPair($FFA5E7B8, $FF77FFCE), TTextAlignment.Middle, TTextAlignment.Ending);

      LFontParams := TFontParameters.Create('Segoe UI', 14.0 * FDisplayScale, TFontWeight.SemiBold);
      LFontParams.Effect.FillBrightness := 0.0;
      LFontParams.Effect.BorderBrightness := 1.0;
      LFontParams.Effect.BorderType := TFontBorder.SemiHeavy;
      LFontParams.Effect.ShadowBrightness := 0.75;
      LFontParams.Effect.ShadowOpacity := 1.0;
      FTextRenderer.FontParameters := LFontParams;

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

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

      FTextRenderer.DrawAligned(Point2f(8.0 * FDisplayScale, 4.0 * FDisplayScale), Utf8String(LTechString),
        ColorPair($A0FFFFFF), TTextAlignment.Start, TTextAlignment.Start);

      FTextRenderer.DrawAligned(Point2f(8.0 * FDisplayScale, FDisplaySize.Y - 8.0 * FDisplayScale),
        Utf8String('Frame rate: ' + IntToStr(Round(FTimer.FrameRate)) + ', draw calls: ' +
        IntToStr(FDrawCalls)), ColorPair($A0FFE0E0, $A0FF80A0), 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
  FMousePos := Point2i(X, Y);
  LPosition := Vector3f(FMousePos.X, FMousePos.Y, 0.0);

  if (ssShift in Shift) and (FSelected.Handle <> nil) then
  begin
    if Button = mbLeft then
      FObjectDragAxis := TVector3f.AxisY // Move horizontally.
    else if Abs(FMouseRay.Direction.Dot(TVector3f.AxisX)) >
      Abs(FMouseRay.Direction.Dot(TVector3f.AxisZ)) then
      FObjectDragAxis := TVector3f.AxisX
    else
      FObjectDragAxis := TVector3f.AxisZ;

    if not FMouseRay.IntersectPlane(FSelectionPoint, FObjectDragAxis, FMousePointAtPlane) then
      FObjectDragAxis := TVector3f.Zero;
  end
  else if (ssCtrl in Shift) and (FSelected.Handle <> nil) then
  begin
    if Button = mbLeft then
      FObjectRotation := 2
    else if Abs(FMouseRay.Direction.Dot(TVector3f.AxisX)) >
      Abs(FMouseRay.Direction.Dot(TVector3f.AxisZ)) then
      FObjectRotation := 1
    else
      FObjectRotation := 3;
  end
  else
  begin
    if Button = mbLeft then
      FCamera.Command(TCameraCommand.Rotation, LPosition, FDisplaySize)
    else if Button = mbRight then
      FCamera.Command(TCameraCommand.Dragging, LPosition, FDisplaySize);

    FRotatingView := (Button = mbLeft) or (Button = mbRight);
  end;
end;

procedure TMainForm.RenderingControlMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  LPosition, LMovePoint, LOffset: TVector3f;
  LPrevMousePos: TPoint2i;
  LRotation: TMatrix4f;
  LParent: TObjectModel;
  LAngle: Single;
begin
  LPrevMousePos := FMousePos;
  FMousePos := Point2i(X, Y);
  LPosition := Vector3f(FMousePos.X, FMousePos.Y, 0.0);

  if not FObjectDragAxis.Empty then
  begin
    if FMouseRay.IntersectPlane(FMousePointAtPlane, FObjectDragAxis, LMovePoint) then
    begin
      LOffset := LMovePoint - FMousePointAtPlane;

      // Adjust offset to ensure movement respective viewer's direction.
      LParent := FSelected.Parent;
      if LParent.Handle <> nil then
        LOffset := (Vector4f(LOffset, 0.0) * LParent.Transform[TModelTransform.Local].Inverse).GetXYZ;

      FSelected.Transform[TModelTransform.Local] := FSelected.Transform[TModelTransform.Local] *
        TMatrix4f.Translate(LOffset);

      FMousePointAtPlane := LMovePoint;
    end;
  end
  else if FObjectRotation <> 0 then
  begin
    LAngle := (LPrevMousePos.X - FMousePos.X) * 2.5 / FDisplaySize.X;

    case FObjectRotation of
      1: LRotation := TMatrix4f.RotateX(LAngle);
      3: LRotation := TMatrix4f.RotateZ(LAngle);
    else
      LRotation := TMatrix4f.RotateY(LAngle);
    end;

    FSelected.Transform[TModelTransform.Local] := LRotation * FSelected.Transform[TModelTransform.Local];
  end
  else
    FCamera.Command(TCameraCommand.Update, LPosition, FDisplaySize);
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);
  FObjectDragAxis := TVector3f.Zero;
  FObjectRotation := 0;
  FRotatingView := False;
end;

procedure TMainForm.RenderingControlMouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint;
  var Handled: Boolean);
begin
  FCamera.Zoom(FMousePos, FDisplaySize, FProjection, -16.0);
end;

procedure TMainForm.RenderingControlMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint;
  var Handled: Boolean);
begin
  FCamera.Zoom(FMousePos, FDisplaySize, FProjection, 16.0);
end;

procedure TMainForm.ApplicationIdle(Sender: TObject; var Done: Boolean);
begin
  FRenderingControl.Invalidate;
  Done := False;
end;

procedure TMainForm.RenderObjectHighlight(const AScene: TScene; const ASelected: TObjectModel);
var
  LMeshName: string;
  LMesh: TSceneMesh;
  LColor: TIntColor;
begin
  LMesh := ASelected.Mesh;
  if LMesh.Handle = nil then
    Exit;

  AScene.World := ASelected.Transform[TModelTransform.GlobalModel];

  LColor := $FFFFFFFF;
  LMeshName := ASelected.MeshName;

  if SameText(LMeshName, 'Box') then
    LColor := $FF00FF00
  else if Pos('RoboticArm', LMeshName) <> 0 then
    LColor := $FF0080FF
  else if Pos('Forklift', LMeshName) <> 0 then
    LColor := $FF10E0A0
  else if SameText(LMeshName, 'Text3D') then
    LColor := $FFFF0000
  else if SameText(LMeshName, 'Pallet') then
    LColor := $FF4080A0
  else if SameText(LMeshName, 'MultipleBands') or SameText(LMeshName, 'DirectionPlane') then
    LColor := $FF0000FF
  else if SameText(LMeshName, 'Fence') or SameText(LMeshName, 'FenceThick') then
    LColor := $FFFF4040;

  Inc(FDrawCalls, LMesh.AutoDrawSliced(AScene, FloatColor(LColor), TAutoDrawOption.ObjectHighlighted));
end;

procedure TMainForm.RenderTextLabels(const AViewProjection: TMatrix4f);
var
  LRenderingState: TRenderingState;
  LFontParameters: TFontParameters;
  LModifiers: TTextRenderModifiers;
  LRoboticArm, LBands: TObjectModel;
  LTransform: TMatrix4f;
begin
  // Configure rendering state to avoid depth fighting and disable depth writes.
  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 := AViewProjection;

  // Prepare SDF font.
  LFontParameters := TFontParameters.Create('Cambria', 32.0, TFontWeight.SemiBold);
  LFontParameters.Effect.SignedFieldDistance := 1.0;
  FTextRenderer.FontParameters := LFontParameters;

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

  // Label describing robotic arm.
  LRoboticArm := FObjects['RoboticArm:Piece0'];
  LTransform := LRoboticArm.Transform[TModelTransform.Global];

  FCanvas.World := TMatrix4f.RotateX(Pi * 0.5) * TMatrix4f.RotateY(Pi * -0.5) *
    TMatrix4f.Translate(40.0, 0.0, 0.0) * LTransform;

  FTextRenderer.DrawAligned(TPoint2f.Zero, 'Robotic Arm', TColorPair.Black, TTextAlignment.Middle,
    TTextAlignment.Middle, 1.0, false, @LModifiers);

  // Labels describing the bands.
  LBands := FObjects['MultipleBands'];
  LTransform := LBands.Transform[TModelTransform.Global];

  FCanvas.World := TMatrix4f.RotateX(Pi * 0.5) * LTransform * TMatrix4f.Translate(70.0, 0.0, 20.0);

  FTextRenderer.DrawAligned(TPoint2f.Zero, 'Band for outcoming packages', TColorPair.Black,
    TTextAlignment.Middle, TTextAlignment.Middle, 1.0, false, @LModifiers);

  FCanvas.World := TMatrix4f.RotateX(Pi * 0.5) * TMatrix4f.RotateY(Pi * -0.5) * LTransform *
    TMatrix4f.Translate(-200.0, 0.0, -140.0);

  FTextRenderer.DrawAligned(TPoint2f.Zero, 'Bands for incoming packages', TColorPair.Black,
    TTextAlignment.Middle, TTextAlignment.Middle, 1.0, false, @LModifiers);
end;

procedure TMainForm.UpdateMovement;
const
  BoxRollingSpeed: Single = 70.0;

  procedure ObjectMove(const AObjectName: string; const ATransform: TMatrix4f);
  begin
    FObjects[AObjectName].Transform[TModelTransform.Local] := ATransform;
  end;

var
  LLatency, LTiming, LGripperTheta: Double;
  LDistance, LScale: Single;
  LObject, LParent: TObjectModel;
  LTracking: PObjectMovementTracking;
begin
  FTimer.Update;

  // Retrieve current latency and update timer ticks.
  LLatency := FTimer.Latency / 1000000;
  FTimerTicks := FTimerTicks + FTimer.Tokens;
  LTiming := FTimerTicks / 60.0;

  // Move boxes over the bands.
  for LObject in FObjects do
  begin
    LTracking := LObject.Payload;
    if LTracking = nil then
      Continue;

    LParent := LObject.Parent;
    LDistance := LParent.GetWaypointDistance(LTracking.Group);

    LTracking.Distance := LTracking.Distance + LLatency * BoxRollingSpeed;
    if LTracking.Distance >= LDistance then
    begin
      LTracking.Distance := 0.0;
      LTracking.Group := LTracking.Group xor 1;
      LTracking.Angle := RandomizeBoxAngle;

      FDirectionPlaneToggle:= LTracking.Group;
      // Note: distance is different between tracking groups, but object is at zero size anyway.
    end;

    LScale := 1.0;
    if LTracking.Distance < 10.0 then
      LScale := LTracking.Distance / 10.0
    else if LTracking.Distance > LDistance - 10.0 then
    LScale := (LDistance - LTracking.Distance) / 10.0;

    LObject.Transform[TModelTransform.Local] := TMatrix4f.Scale(LScale) * TMatrix4f.RotateY(
      LTracking.Angle) * LParent.GetLatchWaypointCouple(LTracking.Distance, LTracking.Group);
  end;

  // Animate the angle of direction plane.
  if FDirectionPlaneToggle = 1 then
    FDirectionPlaneAngle := Min(FDirectionPlaneAngle + LLatency * 0.5, 1.0)
  else
    FDirectionPlaneAngle := Max(FDirectionPlaneAngle - LLatency * 0.5, 0.0);

  // Move direction plane and all pieces of robotic arm.
  LGripperTheta := Sin(LTiming * 1.2816) * 0.7;

  ObjectMove('RoboticArm:Piece1', TMatrix4f.RotateY(Sin(LTiming * 0.5823) * Pi * 0.25));
  ObjectMove('RoboticArm:Piece2', TMatrix4f.RotateZ((Sin(LTiming * 0.7921) - 0.5) * Pi * 0.1));
  ObjectMove('RoboticArm:Piece3', TMatrix4f.RotateZ((Sin(LTiming * 1.184) + 0.25) * Pi * 0.1));
  ObjectMove('RoboticArm:Piece4', TMatrix4f.RotateZ((Cos(LTiming * 1.037) + 0.75) * Pi * 0.15));
  ObjectMove('RoboticArm:Piece5', TMatrix4f.RotateZ(Sin(LTiming * 0.8) * Pi * 0.15));
  ObjectMove('RoboticArm:Piece6', TMatrix4f.RotateY(Cos(LTiming * 0.681) * Pi * 0.5));
  ObjectMove('RoboticArm:Piece6', TMatrix4f.RotateY(Cos(LTiming * 0.681) * Pi * 0.5));
  ObjectMove('RoboticArm:Gripper0', TMatrix4f.Translate(0.0, 0.0, -9.8 + LGripperTheta));
  ObjectMove('RoboticArm:Gripper1', TMatrix4f.Translate(0.0, 0.0, 9.8 - LGripperTheta));

  ObjectMove('DirectionPlane', TMatrix4f.RotateY((SineTransform(FDirectionPlaneAngle) - 0.5) * Pi * 0.2));

  // Move lifter and grippers of the forklift.
  LGripperTheta := Cos(LTiming * 0.773) * 9.0;

  ObjectMove('Forklift:Forklift', TMatrix4f.RotateY(Pi * 0.5) *
    TMatrix4f.Translate(-320.0, 0.0, -160.0 + Sin(LTiming * 1.6102) * 24.0));

  ObjectMove('Forklift:Lifter', TMatrix4f.Translate(0.0, Sin(LTiming * 0.6102) * 12.0 + 12.0, 0.0));
  ObjectMove('Forklift:GripperL', TMatrix4f.Translate(0.0, 0.0, -12.0 + LGripperTheta));
  ObjectMove('Forklift:GripperR', TMatrix4f.Translate(0.0, 0.0, 12.0 - LGripperTheta));
end;

procedure TMainForm.ReleaseObjectMovementTracking;
var
  LObject: TObjectModel;
  LTracking: PObjectMovementTracking;
begin
  for LObject in FObjects do
  begin
    LTracking := LObject.Payload;
    if LTracking <> nil then
      Dispose(LTracking);
  end;
end;

procedure TMainForm.CreateObjects;
const
  MaterialPieces: Integer = 3;
  AttributesMoving: TModelAttributes = [TModelAttribute.Visible, TModelAttribute.Selectable,
    TModelAttribute.Hierarchyless];
var
  LPiece0, LPiece1, LPiece2, LPiece3, LPiece4, LPiece5, LPiece6, LGripper0, LGripper1: TObjectModel;
  LForklift, LLifter, LMultipleBands, LDirectionPlane: TObjectModel;
  LFloor, LPallet, LDummy, LBox, LStand: TObjectModel;
  LSequence: TRandomSequence;
  I, J, K, KZ, KX: Integer;
  LHeight, LDistance: Single;
  LBoxes: array[0..2, 0..2, 0..6] of Boolean;
  LPalletSize, LBoxSize: TVector3f;
  LTracking: PObjectMovementTracking;
begin
  LFloor := CreateObject(string.Empty, 'Plane', TObjectModel(NullObject), Vector3f(0.0, -0.01, 0.0), 0);
  LFloor.Attributes := [TModelAttribute.Visible, TModelAttribute.Hierarchyless];
  LFloor.Color := FloatColor($FFE0E0E0);

  LPiece0 := CreateObject('RoboticArm:Piece0', 'RoboticArm_Piece0', TObjectModel(NullObject),
    Vector3f(-60.0, 0.0, -160.0), MaterialPieces, Pi * 0.25, AttributesMoving);

  LPiece1 := CreateObject('RoboticArm:Piece1', 'RoboticArm_Piece1', LPiece0,
    TVector3f.Zero, MaterialPieces, 0.0, AttributesMoving);

  LPiece2 := CreateObject('RoboticArm:Piece2', 'RoboticArm_Piece2', LPiece1,
    TVector3f.Zero, MaterialPieces, 0.0, AttributesMoving);

  LPiece3 := CreateObject('RoboticArm:Piece3', 'RoboticArm_Piece3', LPiece2,
    TVector3f.Zero, MaterialPieces, 0.0, AttributesMoving);

  LPiece4 := CreateObject('RoboticArm:Piece4', 'RoboticArm_Piece4', LPiece3,
    TVector3f.Zero, MaterialPieces, 0.0, AttributesMoving);

  LPiece5 := CreateObject('RoboticArm:Piece5', 'RoboticArm_Piece5', LPiece4,
    TVector3f.Zero, MaterialPieces, 0.0, AttributesMoving);

  LPiece6 := CreateObject('RoboticArm:Piece6', 'RoboticArm_Piece6', LPiece5,
    TVector3f.Zero, MaterialPieces, 0.0, AttributesMoving);

  LGripper0 := CreateObject('RoboticArm:Gripper0', 'RoboticArm_Gripper0', LPiece6,
    TVector3f.Zero, MaterialPieces, 0.0, AttributesMoving);

  LGripper1 := CreateObject('RoboticArm:Gripper1', 'RoboticArm_Gripper1', LPiece6,
    TVector3f.Zero, MaterialPieces, 0.0, AttributesMoving);

  // Connect each pieces of robotic arm together by using "latches". An object attached to its parent by
  // latch mechanism places and orients the object so that its and parent latches coincide. This enables
  // precise placement and orientation of pieces together. The positions and orientations are calculated
  // according to parent dependency chain. Therefore, moving the base piece (which has no parent) will
  // also move accordingly all its codependent child pieces.
  LPiece1.ConnectLatches('Out', 'In');
  LPiece2.ConnectLatches('Out', 'In');
  LPiece3.ConnectLatches('Out', 'In');
  LPiece4.ConnectLatches('Out', 'In');
  LPiece5.ConnectLatches('Out', 'In');
  LPiece6.ConnectLatches('Out', 'In');
  LGripper0.ConnectLatches('Out', 'In');
  LGripper1.ConnectLatches('Out', 'In');

  // Forklift with separate lifter and grippers
  LForklift := CreateObject('Forklift:Forklift', 'Forklift_Forklift', TObjectModel(NullObject),
    Vector3f(-320.0, 0.0, -160.0), 1, Pi * 0.5, AttributesMoving);

  LLifter := CreateObject('Forklift:Lifter', 'Forklift_Lifter', LForklift,
    TVector3f.Zero, 3, 0.0, AttributesMoving);

  LLifter.ConnectLatches('Lifter', 'Base');

  LGripper0 := CreateObject('Forklift:GripperL', 'Forklift_GripperL', LLifter,
    Vector3f(0.0, 0.0, -10.0), 3, 0.0, AttributesMoving);

  LGripper0.ConnectLatches('Gripper', 'Lifter');

  LGripper1 := CreateObject('Forklift:GripperR', 'Forklift_GripperR', LLifter,
    Vector3f(0.0, 0.0, 10.0), 3, 0.0, AttributesMoving);

  LGripper1.ConnectLatches('Gripper', 'Lifter');

  // Multiple bands with direction plane
  LMultipleBands := CreateObject('MultipleBands', 'MultipleBands', TObjectModel(NullObject),
    Vector3f(0.0, 0.0, 40.0), 1);

  LDirectionPlane := CreateObject('DirectionPlane', 'DirectionPlane', LMultipleBands,
    Vector3f(0.0, -2.0, 0.0), 1, Pi * 0.17, AttributesMoving);

  LDirectionPlane.ConnectLatches('DirectionPlane', 'Base');

  // Create boxes that will be moved over the bands.
  LDistance := LMultipleBands.GetWaypointDistance;
  FBoxesPRNG := TRandomSequence.Create;

  for I := 0 to 19 do
  begin
    New(LTracking);
    LTracking.Distance := LDistance * I / 32;
    LTracking.Group := 0;
    LTracking.Angle := RandomizeBoxAngle;

    LBox := FObjects.Add('MovingBox_' + IntToStr(I), LTracking, AttributesMoving);
    LBox.Parent := LMultipleBands;
    LBox.MeshName := 'Box';
    LBox.Material := 2;
  end;

  // Fences
  CreateObject(string.Empty, 'Fence', TObjectModel(NullObject), Vector3f(20.0, 0.0, 220.0), 3);
  CreateObject(string.Empty, 'FenceThick', TObjectModel(NullObject), Vector3f(-280.0, 0.0, -60.0), 3,
    Pi * 0.5);

  // Stack of pallets behind the fence.
  LHeight := 0.0;

  for I := 0 to 6 do
  begin
    LPallet := CreateObject(string.Empty, 'Pallet', TObjectModel(NullObject),
      Vector3f(-340.0, LHeight, -28.0), 2);
    LHeight := LHeight + LPallet.Size.Y;
  end;

  // Stack of boxes at the backyard.
  CreateObject(string.Empty, 'Box', TObjectModel(NullObject), Vector3f(-320.0, 0.0, 40.0), 2);
  CreateObject(string.Empty, 'Box', TObjectModel(NullObject), Vector3f(-315.0, 0.0, 72.0), 2, Pi * 0.1);
  CreateObject(string.Empty, 'Box', TObjectModel(NullObject), Vector3f(-345.0, 0.0, 65.0), 2, Pi * -0.1);

  // Text describing the boxes.
  CreateObject('Text3D', 'Text3D', TObjectModel(NullObject), Vector3f(135.0, 0.0, -135.0), 3, Pi * -0.24);

  // Stack of boxes near the arm.
  FillChar(LBoxes, SizeOf(LBoxes), 0);
  LSequence := TRandomSequence.Create(4002);

  LDummy := CreateObject(string.Empty, string.Empty, TObjectModel(NullObject), Vector3f(105.0, 0.0, -105.0),
    0, Pi * -0.25);

  for K := 0 to 2 do
    for J := 0 to 2 do
      for I := 0 to 6 do
        if (LSequence.Ranged(4) <> 1) and ((K = 0) or LBoxes[K - 1, J, I]) then
        begin
          LBox := FObjects.Add;
          LBox.Parent := LDummy;
          LBox.MeshName := 'Box';
          LBox.Material := 2;

          LBoxSize := LBox.Size;

          LBox.Transform[TModelTransform.Local] := TMatrix4f.Translate(
            (I - 3) * (LBoxSize.X + 1.0), (LBoxSize.Y + 0.5) * K, (J - 1) * (LBoxSize.Z + 1.0));

          LBoxes[K, J, I] := True;
        end;

  // Stand with pallets and boxes.
  LSequence := TRandomSequence.Create(5004);
  LStand := CreateObject(string.Empty, 'Stand', TObjectModel(NullObject), Vector3f(60.0, 0.0, 200.0), 1);

  for J := 0 to 2 do
    for I := 0 to 4 do
    begin
      LPallet := FObjects.Add;
      LPallet.Parent := LStand;
      LPallet.MeshName := 'Pallet';
      LPallet.Material := 2;

      LHeight := J * 46.0;
      if J = 2 then
        LHeight := 86.5;

      LPalletSize := LPallet.Size;

      LPallet.Transform[TModelTransform.Local] := TMatrix4f.Translate((I - 2) * (LPalletSize.X + 7.0),
        LHeight, 0.0);

      for KZ := 0 to 2 do
        for KX := 0 to 2 do
          if LSequence.Ranged(2) = 1 then
          begin
            LBox := FObjects.Add;
            LBox.Parent := LPallet;
            LBox.MeshName := 'Box';
            LBox.Material := 2;

            LBoxSize := LBox.Size;

            LBox.Transform[TModelTransform.Local] := TMatrix4f.RotateY(LSequence.Ranged(4) * Pi * 0.5) *
              TMatrix4f.Translate(
                (KX - 1) * (LBoxSize.X + 5.0), LPalletSize.Y, (KZ - 1) * (LBoxSize.Z + 2.0));
          end;
    end;
end;

function TMainForm.CreateObject(const AObjectName, AMeshName: string; const AParent: TObjectModel;
  const APosition: TVector3f; const AMaterial: Integer; const AAngle: Single;
  const AAttributes: TModelAttributes): TObjectModel;
var
  LTransform: TMatrix4f;
begin
  Result := FObjects.Add(Utf8String(AObjectName), nil, AAttributes);

  if AParent.Handle <> nil then
    Result.Parent := AParent;

  if not AMeshName.IsEmpty then
    Result.MeshName := Utf8String(AMeshName);

  Result.Material := AMaterial;

  LTransform := TMatrix4f.Translate(APosition);

  if AAngle <> 0.0 then
    LTransform := TMatrix4f.RotateY(AAngle) * LTransform;

  Result.Transform[TModelTransform.Local] := LTransform;
end;

function TMainForm.RandomizeBoxAngle: Single;
begin
  Result := FBoxesPRNG.Ranged(4) * Pi * 0.5 + (FBoxesPRNG.Value - 0.5) * Pi * 0.12;
end;

procedure TMainForm.CreateLightsAndMaterials;
var
  LLight: PSceneLight;
  LMaterial: PObjectMaterial;
begin
  // Create default light.
  LLight := FLights.Add;
  LLight.AmbientColor := FloatColorRGB($FFFFFF);
  LLight.AlbedoColor := FloatColorRGB($FFFFFF);
  LLight.SpecularColor := FloatColorRGB($FFFFFF);
  LLight.SpecularExponent := 4.0;
  LLight.Position := LightPosition;
  LLight.ShadowCaster := 0;

  // #0 (floor)
  LMaterial := FMaterials.Add;
  LMaterial.AmbientColor := TFloatColorRGB.White;
  LMaterial.AlbedoColor := TFloatColor.TranslucentBlack;
  LMaterial.SpecularColor := TFloatColorRGB.Black;
  LMaterial.Shadows := 0.5;
  LMaterial.Occlusion := 0.5;

  // #1 (generic)
  LMaterial := FMaterials.Add;
  LMaterial.AmbientColor := FloatColorRGB(0.25, 0.25, 0.25);
  LMaterial.AlbedoColor := FloatColor(1.0, 1.0, 1.0, 0.5);
  LMaterial.SpecularColor := FloatColorRGB(1.0, 1.0, 1.0);
  LMaterial.SpecularExponent := 4.0;
  LMaterial.Shadows := 0.5;
  LMaterial.Occlusion := 0.5;

  // #2 (carton)
  LMaterial := FMaterials.Add;
  LMaterial.AmbientColor := FloatColorRGB(0.25, 0.25, 0.25);
  LMaterial.AlbedoColor := FloatColor(1.0, 1.0, 1.0, 0.5);
  LMaterial.SpecularColor := FloatColorRGB(0.5, 0.5, 0.5);
  LMaterial.SpecularExponent := 16.0;
  LMaterial.Shadows := 0.5;
  LMaterial.Occlusion := 0.5;
  LMaterial.Roughness := Vector3f(0.3, 1.2, 0.4);
  LMaterial.Technique := TTechniqueLighting.Minnaert;

  // #3 (metal)
  LMaterial := FMaterials.Add;
  LMaterial.AmbientColor := FloatColorRGB(0.25, 0.25, 0.25);
  LMaterial.AlbedoColor := FloatColor(1.0, 1.0, 1.0, 0.5);
  LMaterial.SpecularColor := FloatColorRGB(1.0, 1.0, 1.0);
  LMaterial.SpecularExponent := 4.0;
  LMaterial.Shadows := 0.5;
  LMaterial.Occlusion := 0.5;
  LMaterial.Roughness := Vector3f(1.8, 1.2, 0.7);
  LMaterial.Technique := TTechniqueLighting.CookTorrance;
end;

procedure TMainForm.CreateAndLoadMeshes;
const
  BasePath = '../../../../Media/Warehouse/';
  MeshNames: array[0..8] of string = ('Box', 'DirectionPlane', 'Fence', 'FenceThick', 'Forklift',
    'MultipleBands', 'Pallet', 'RoboticArm', 'Stand');
  MeshVertexColored: array[0..8] of Boolean = (True, True, True, True, False, False, True, False, True);
  MeshScale: Single = 1.5;
  MeshesToSplit: array[0..1] of string = ('RoboticArm', 'Forklift');
var
  LMeshBuffer: TMeshBuffer;
  LSceneMesh, LParent: TSceneMesh;
  LMeshName, LParentMeshName: string;
  LOptions: Cardinal;
  I: Integer;
begin
  // Create geometric meshes.
  LMeshBuffer := TMeshBuffer.Create;
  try
    LMeshBuffer.Plane(1, 1, TVector3f.Zero, Vector3f(1200.0, 0.0, 0.0), Vector3f(0.0, 0.0, 1200.0),
      TVector3f.AxisY, TPoint2f.Zero, TPoint2f.Unity, TFloatColor.White);

    FMeshes.AddFromBuffer('Plane', LMeshBuffer, nil, VertexElements);

    CreateMeshText3D(LMeshBuffer);
  finally
    LMeshBuffer.Free;
  end;

  // Load meshes from disk.
  for I := 0 to High(MeshNames) do
  begin
    if MeshVertexColored[I] then
      LOptions := TMeshLoadingOption.MaterialVertexColor
    else
      LOptions := 0;

    FMeshes.AddFromFile(MeshNames[I], BasePath + MeshNames[I] + '.obj', nil, VertexElements, 0, 0, LOptions,
      MeshScale);
  end;

  // For meshes such as "RoboticArm" and "ForkLift", a split is performed by creating mesh instances
  // that refer to a specific portion of a parent mesh. This is done according to meta-tags that are
  // present in the mesh. An object that refers to one of the sub-meshes will still use original mesh
  // for rendering, while using each sub-mesh latch and voxel mechanisms.
  for I := 0 to High(MeshesToSplit) do
  begin
    LSceneMesh := FMeshes.TryMesh(Utf8String(MeshesToSplit[I]));
    if LSceneMesh.Handle = nil then
      Continue; // Mesh does not exist.

    FMeshes.Slice(LSceneMesh, TMeshMetaTagType.ObjectGroup);
  end;

  for I := 0 to FMeshes.Count - 1 do
  begin
    LSceneMesh := FMeshes.Meshes[I];

    LParent := LSceneMesh.GetSlice;
    if LParent.Handle = nil then
      Continue; // Mesh is not a slice.

    LMeshName := string(LSceneMesh.Name);
    LParentMeshName := string(LParent.Name);

    // Load voxel for a particular segment, if voxel file is present.
    LSceneMesh.Voxel.TryLoadFromFile(BasePath + '/' + LParentMeshName + '/' + LMeshName + '.voxel');

    // Load latches for a particular segment, if latches file is present.
    LSceneMesh.Latches.TryLoadFromFile(BasePath + '/' + LParentMeshName + '/' + LMeshName + '.latch');
  end;
end;

procedure TMainForm.CreateMeshText3D(const ABuffer: TMeshBuffer);
var
  LSceneMesh: TSceneMesh;
  LModeller: TTextModeller;
  LVoxel: TMeshVoxel;
begin
  ABuffer.Clear;
  LModeller := TTextModeller.Create(FDevice);
  try
    LModeller.SetFontParameters(TFontParameters.Create('Cambria', 16.0, TFontWeight.Bold));
    LModeller.DrawCurved(TVector3f.Zero, 'Boxes waiting to be dispatched', -150.0, NAN, 3.0,
      TColorPair.White, TTextAlignment.Start);
    LModeller.CopyToMeshBuffer(ABuffer);

    LVoxel := ABuffer.Voxelize(4);
    try
      LSceneMesh := FMeshes.AddFromBuffer('Text3D', ABuffer, nil, VertexElements);
      LSceneMesh.Voxel.TakeAway(LVoxel);
    finally
      LVoxel.Free;
    end;
  finally
    LModeller.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;
end;

end.