unit PXT.Graphics;
(*
 * 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.
 *)
{< High-level wrappers around Afterwarp classes dealing with graphics, timing and math. }
interface

{$INCLUDE PXT.Config.inc}

uses
  SysUtils, PXT.Types, PXT.Headers;

type
  // Array of @link(Cardinal) values.
  TCardinalArray = array of Cardinal;

  // Pointer to @link(TRay).
  PRay = ^TRay;

  // Ray in 3D space.
  TRay = record
    // Ray starting location.
    Origin: TVector3f;

    // Direction of the ray.
    Direction: TVector3f;

    // Calculates whether the current ray intersects with the given triangle and if so, calculates
    // the resulting barycentric coordinates and distance from origin to triangle.
    function IntersectTriangle(const AVertex1, AVertex2, AVertex3: TVector3f; const ABackFacing: Boolean;
      out AIntersection: TPoint2f; out ADistance: Single): Boolean; overload; inline;

    // Calculates whether the current ray intersects with the given front-facing triangle.
    function IntersectTriangle(const AVertex1, AVertex2, AVertex3: TVector3f;
      const ABackFacing: Boolean = False): Boolean; overload; inline;

    // Tests whether the current ray intersects with one of triangles of minimalistic cube volume, whose
    // vertices are first transformed by the given world matrix, returning distance to intersection point.
    function IntersectCubeVolume(const AWorld: TMatrix4f; out ADistance: Single): Boolean; overload; inline;

    // Tests whether the current ray intersects with one of triangles of minimalistic cube volume, whose
    // vertices are first transformed by the given world matrix.
    function IntersectCubeVolume(const AWorld: TMatrix4f): Boolean; overload; inline;

    // Tests whether the current ray intersects with a plane specified by a point on the plane and its
    // surface normal, returning the actual point of intersection and distance from ray's origin.
    function IntersectPlane(const APlanePoint, APlaneNormal: TVector3f; out AIntersection: TVector3f;
      out ADistance: Single): Boolean; overload;

    // Tests whether the current ray intersects with a plane specified by a point on the plane and its
    // surface normal, returning the actual point of intersection.
    function IntersectPlane(const APlanePoint, APlaneNormal: TVector3f;
      out AIntersection: TVector3f): Boolean; overload;

    // Tests whether the current ray intersects with a plane specified by a point on the plane and its
    // surface normal.
    function IntersectPlane(const APlanePoint, APlaneNormal: TVector3f): Boolean; overload;

    // Constructs the ray based on 2D surface position, surface size, inverse of the 3D view matrix and
    // the projection matrix.
    class function Create(const APosition, ASurfaceSize: TPoint2f; const AViewInverse,
      AProjection: TMatrix4f): TRay; static;
  end;

  // Pointer to @link(TVolume).
  PVolume = ^TVolume;

  // Volume and boundary functions.
  TVolume = record
    // Creates a model transformation matrix that centers the mesh and places it on top of zero plane.
    class function BoundsToMatrixModel(const AMinBounds, AMaxBounds: TVector3f;
      const AMeshAligns: TMeshAligns; const AScale: Single = 1.0): TMatrix4f; static; inline;

    // Creates a volume transformation matrix that centers the mesh and places it on top of zero plane.
    class function BoundsToMatrixVolume(const AMinBounds, AMaxBounds: TVector3f;
      const AMeshAligns: TMeshAligns; const AScale: Single = 1.0): TMatrix4f; static; inline;

    /// Calculates an offset to displace from mesh to tag origin.
    class function BoundsTagOffset(const AMeshMinBounds, AMeshMaxBounds, ATagMinBounds,
      ATagMaxBounds: TVector3f): TVector3f; static; inline;

    /// Creates a volume transformation matrix that centers the mesh tag inside mesh volume.
    class function BoundsToMatrixVolumeTag(const AMeshMinBounds, AMeshMaxBounds, ATagMinBounds,
      ATagMaxBounds: TVector3f; const ASizeBias: Single = 0.1): TMatrix4f; static; inline;

    // Calculates near and far planes for a bounding box transformed by the given matrix.
    class procedure NearFarPlanes(const AWorldView: TMatrix4f; out ANearPlane,
      AFarPlane: Single); static; inline;

    // Calculates visible surface frame for a bounding box transformed by the given
    // world/view/projection matrix.
    class function VisibleFrame(const AWorldViewProjection: TMatrix4f;
      const ASurfaceSize: TPoint2f): TFloatRect; static; inline;
  end;

  // Base class wrapping existing framework handle.
  TBaseClass = class
  private
    FHandle: TUntypedObjectHandle;
    function GetInitialized: Boolean; inline;
  public
    // Wrapped object handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Indicates whether the object has been properly initialized.
    property Initialized: Boolean read GetInitialized;
  end;

type
  TCustomGraphicsObject = record
    Handle: TUntypedObjectHandle;
  end;

const
  // Null object.
  NullObject: TCustomGraphicsObject = (Handle: nil);

type
  // Program vertex buffer declaration.
  TVertexElements = array of TVertexElement;

  // Program element declaration.
  TProgramElements = array of TProgramElement;

  // Program variable declaration.
  TProgramVariables = array of TProgramVariable;

  // Array of 4x4 matrices.
  TMatrix4fArray = array of TMatrix4f;

  // Array of floating-point colors.
  TFloatColorArray = array of TFloatColor;

  // Forward declaration of TApplication.
  TApplication = class;

  // Pointer to @link(TTexture).
  PTexture = ^TTexture;

  // Pointer to @link(TDevice).
  PDevice = ^TDevice;

  // Afterwarp-specific exception.
  EAfterwarpException = class(Exception);

  // Device class that handles initialization, rendering, state management and other common tasks.
  TDevice = record
  private
    FHandle: TUntypedObjectHandle;
    function GetPlatformDevice: Pointer; inline;
    function GetAttributes: TDeviceAttributes; inline;
    function GetBehavior: Cardinal; inline;
    function GetLegacyBits: TDeviceLegacyBits; inline;
    function GetTechnology: TDeviceTechnology; inline;
    function GetDevicePlatform: TDevicePlatform; inline;
    function GetTechVersion: Cardinal; inline;
    function GetTechFeatureVersion: Cardinal; inline;
    function GetRenderingState: TRenderingState; inline;
    procedure SetRenderingState(const ARenderingState: TRenderingState); inline;
    function GetViewport: TIntRect; inline;
    procedure SetViewport(const AViewport: TIntRect); inline;
    function GetScissor: TIntRect; inline;
    procedure SetScissor(const AScissor: TIntRect); inline;
  public
    // Creates a new graphics device.
    class function Create(const ATechnology: TDeviceTechnology = TDeviceTechnology.Unknown;
      const AAttributes: TDeviceAttributes = 0): TDevice; overload; static;

    // Creates a new graphics device.
    class function Create(const AApplication: TApplication;
      const ATechnology: TDeviceTechnology = TDeviceTechnology.Unknown;
      const AAttributes: TDeviceAttributes = 0): TDevice; overload; static;

    // Creates a graphics device wrapped around an already initialized graphics context of the given
    // technology and attributes.
    class function CreateWrapped(const ATechnology: TDeviceTechnology = TDeviceTechnology.Unknown;
      const AAttributes: TDeviceAttributes = 0;
      const APlatformDevice: Pointer = nil): TDevice; overload; static;

    // Creates a graphics device wrapped around an already initialized graphics context of the given
    // technology and attributes.
    class function CreateWrapped(const AApplication: TApplication;
      const ATechnology: TDeviceTechnology = TDeviceTechnology.Unknown;
      const AAttributes: TDeviceAttributes = 0;
      const APlatformDevice: Pointer = nil): TDevice; overload; static;

    // Releases an existing graphics device.
    procedure Free; inline;

    // Releases any cached device resources.
    procedure ResetCache; inline;

    // Clears rendering surface that is currently active.
    procedure Clear(const ALayers: TClearLayers; const AColor: TFloatColor; const ADepth: Single = 1.0;
      const AStencil: Cardinal = 0); inline;

    // Checks whether the given technology feature version is currently supported.
    function VersionCheck(const AMajorVersion, AMinorVersion: Integer): Boolean;

    // Issues a memory barrier according to the specified bit flags.
    procedure MemoryBarrier(const ABarrierBits: TDeviceBarrierBits);

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Platform-specific device handle, if applicable.
    property PlatformDevice: Pointer read GetPlatformDevice;

    // Returns attributes with which the device has been created.
    property Attributes: TDeviceAttributes read GetAttributes;

    // Returns device behavior attributes.
    property Behavior: Cardinal read GetBehavior;

    // Returns legacy feature bits that define older hardware features.
    property LegacyBits: TDeviceLegacyBits read GetLegacyBits;

    // Returns graphics technology type that is currently being used.
    property Technology: TDeviceTechnology read GetTechnology;

    // Returns OS platform the device application is currently running on.
    property DevicePlatform: TDevicePlatform read GetDevicePlatform;

    // Returns the version of technology that is currently being used. The values are specified in
    // hexadecimal format. That is, a value of $100 indicates version 1.0, while a value of $247 would
    // indicate version 2.4.7. The value is used in combination with device technology, so if technology
    // is Direct3D, and this function returns $A10, it means that Direct3D 10.1 is being used.
    property TechVersion: Cardinal read GetTechVersion;

    // Retuyrns the feature level version of technology that is currently being used. The difference between
    // this and technology version is that the last one indicates type of technology being used (for example,
    // DirectX 3D), while this one indicates the level of features available (for example, Direct3D 9.0c).
    // The values here are specified in hexadecimal format. That is, a value of $213 would indicate
    // version 2.1.3.
    property TechFeatureVersion: Cardinal read GetTechFeatureVersion;

    // Current device rendering parameters and operation characteristics.
    property RenderingState: TRenderingState read GetRenderingState write SetRenderingState;

    // Current device rendering viewport.
    property Viewport: TIntRect read GetViewport write SetViewport;

    // Current device rendering scissor rectangle.
    property Scissor: TIntRect read GetScissor write SetScissor;
  end;

  // Pointer to @link(TSwapChain).
  PSwapChain = ^TSwapChain;

  // Swap-chain that enables double-buffered rendering to a particular window.
  TSwapChain = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetWindowHandle: TUntypedHandle; inline;
    function GetWidth: Integer; inline;
    function GetHeight: Integer; inline;
    function GetSize: TPoint2i; inline;
    function GetFormat: TPixelFormat; inline;
    function GetDepthStencil: TPixelFormat; inline;
    function GetMultisamples: Integer; inline;
    function GetVSync: Boolean; inline;
  public
    // Creates new instance of graphics swap chain.
    class function Create(const ADevice: TDevice; const AWindowHandle: TUntypedHandle;
      const ASize: TPoint2i; const AFormat: TPixelFormat = TPixelFormat.Unknown;
      const ADepthStencil: TPixelFormat = TPixelFormat.Unknown; const AMultisamples: Integer = 0;
      const AVSync: Boolean = False): TSwapChain; static; inline;

    // Releases the device.
    procedure Free; inline;

    // Resizes the swap-chain.
    procedure Resize(const ASize: TPoint2i); inline;

    // Begins rendering on the swap-chain.
    procedure BeginScene; inline;

    // Finishes rendering on the swap-chain and flips the buffers.
    procedure EndScene; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given swap-chain.
    property Device: TDevice read GetDevice;

    // Handle of the associated window.
    property WindowHandle: TUntypedHandle read GetWindowHandle;

    // Swap-chain width.
    property Width: Integer read GetWidth;

    // Swap-chain height.
    property Height: Integer read GetHeight;

    // Swap-chain size.
    property Size: TPoint2i read GetSize;

    // Pixel format of swap-chain rendering surface.
    property Format: TPixelFormat read GetFormat;

    // Pixel format of swap-chain depth/stencil surface.
    property DepthStencil: TPixelFormat read GetDepthStencil;

    // Number of samples of swap-chain surfaces.
    property Multisamples: Integer read GetMultisamples;

    // Whether to wait for a vertical retrace.
    property VSync: Boolean read GetVSync;
  end;

  // Pointer to @link(TBuffer).
  PBuffer = ^TBuffer;

  // Generic device buffer object that can contain vertices, indices or shader data.
  TBuffer = record
  private
    FHandle: TUntypedObjectHandle;
    function GetPlatformHandle: Pointer; inline;
    function GetDevice: TDevice; inline;
    function GetDataType: TBufferDataType; inline;
    function GetAccessType: TBufferAccessType; inline;
    function GetSize: Cardinal; inline;
    function GetPitch: Cardinal; inline;
    function GetFormat: TPixelFormat; inline;
  public
    // Creates new instance of graphics buffer.
    class function Create(const ADevice: TDevice; const ADataType: TBufferDataType;
      const AAccessType: TBufferAccessType; const ASize: Cardinal;
      const APitch: Cardinal; const AFormat: TPixelFormat = TPixelFormat.Unknown;
      const AInitialData: Pointer = nil): TBuffer; static; inline;

    // Releases the buffer.
    procedure Free; inline;

    // Updates the contents of the buffer with a new data. If @italic(ASize) is left at zero, the remaining
    // portion of buffer starting at given byte offset will be updated. Note that if buffer has been created
    // with an access type that is non-default, then typically a whole buffer should be updated, attempting
    // to update a portion of it may be implementation-dependent.
    procedure Update(const AData: Pointer; const AOffset: Cardinal = 0; const ASize: Cardinal = 0);

    // Retrieves the contents of the buffer. If @italic(ASize) is left at zero, the remaining portion of
    // buffer starting at given byte offset will be retrieved.
    procedure Retrieve(const AData: Pointer; const AOffset: Cardinal = 0; const ASize: Cardinal = 0);

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Platform-specific buffer handle.
    property PlatformHandle: Pointer read GetPlatformHandle;

    // Device associated with the given buffer.
    property Device: TDevice read GetDevice;

    // Type of data that buffer contains.
    property DataType: TBufferDataType read GetDataType;

    // Returns the access type of for buffer data.
    property AccessType: TBufferAccessType read GetAccessType;

    // Buffer size in bytes.
    property Size: Cardinal read GetSize;

    // Number of bytes for offset to move for advancing the pointer from one element to another.
    property Pitch: Cardinal read GetPitch;

    // Returns pixel format that represents the buffer contents.
    property Format: TPixelFormat read GetFormat;
  end;

  // Helper module for TBuffer.
  TBufferHelper = record helper for TBuffer
  public
    // Updates the contents of the buffer with data copied from another buffer. If @italic(ASize) is left at
    // zero, the remaining portion of buffer starting at given byte offset will be copied.
    procedure Copy(const ABuffer: TBuffer; const AOffsetDest: Cardinal = 0;
      const AOffsetSource: Cardinal = 0; const ASize: Cardinal = 0); inline;
  end;

  // Pointer to @link(TProgram).
  PProgram = ^TProgram;

  // Shader program that is typically executed on graphics hardware.
  TProgram = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    procedure SetPatchVertices(const AValue: Integer); inline;
  public
    // Creates new instance of graphics program by specifying each of its parameters.
    class function Create(const ADevice: TDevice; const AVertexElements: TVertexElements;
      const AProgramElements: TProgramElements; const AProgramVariables: TProgramVariables;
      const AVertexShader, AHullShader, ADomainShader, AGeometryShader,
      APixelShader: TBytes): TProgram; overload; static;

    // Creates new instance of graphics program by specifying each of its parameters.
    class function Create(const ADevice: TDevice; const AVertexElements: array of TVertexElement;
      const AProgramElements: array of TProgramElement; const AProgramVariables: array of TProgramVariable;
      const AVertexShader, AHullShader, ADomainShader, AGeometryShader,
      APixelShader: TBytes): TProgram; overload; static;

    // Creates new instance of graphics program from files by specifying each of its parameters.
    class function CreateFromFiles(const ADevice: TDevice; const AVertexElements: TVertexElements;
      const AProgramElements: TProgramElements; const AProgramVariables: TProgramVariables;
      const AVertexShader, AHullShader, ADomainShader, AGeometryShader,
      APixelShader: string): TProgram; overload; static;

    // Creates new instance of graphics program from files by specifying each of its parameters.
    class function CreateFromFiles(const ADevice: TDevice;
      const AVertexElements: array of TVertexElement; const AProgramElements: array of TProgramElement;
      const AProgramVariables: array of TProgramVariable; const AVertexShader, AHullShader, ADomainShader,
      AGeometryShader, APixelShader: string): TProgram; overload; static;

    // Releases the program.
    procedure Free; inline;

    // Updates a portion of a specific variable that corresponds to the given index. The provided size
    // indicates how many bytes should be updated. If the provided size is left at zero, the entire contents
    // of given variable will be updated. This function should only be used inside BeginScene / EndScene call
    // block.
    procedure Update(const AVariableIndex: Integer; const AVariableData: Pointer;
      const ASize: Cardinal = 0); overload; inline;

    // Updates a portion of a specific variable identified by its name (case-sensitive). The provided size
    // indicates how many bytes should be updated. If the provided size is left at zero, the entire contents
    // of given variable will be updated. This function should only be used inside BeginScene / EndScene call
    // block.
    procedure Update(const AVariableName: Utf8String; const AVariableData: Pointer;
      const ASize: Cardinal = 0); overload; inline;

    // Assigns a generic buffer (or a portion of starting from the given offset in bytes) to a particular
    // channel. Vertex and index buffers are associated with input stream channels, whereas constant buffers
    // are associated with shader program channels - each of these being unrelated and dependent on shader
    // vertex and program element declarations. The current bindings can be considered valid until
    // @link(EndScene), @link(ResetBindings) or @link(ResetCache) is called. }
    procedure Bind(const ABuffer: TBuffer; const AChannel: Cardinal = 0; const AOffset: Cardinal = 0); inline;

    // Removes association that was previously made with @link(Bind) between the buffer and the given channel.
    procedure Unbind(const ABuffer: TBuffer; const AChannel: Cardinal = 0); inline;

    // Resets any bindings that were previously made as if @link(Unbind) would be called for each of them.
    procedure ResetBindings; inline;

    // Resets any cache associated with vertex buffers. In case of OpenGL, this resets internal Vertex Array
    // Object (VAO) cache.
    procedure ResetCache; inline;

    // Applies any binding changes to constant and/or vertex buffers, which usually occur when a draw call is
    // issued, to occur immediately.
    procedure Commit; inline;

    // Activates the shader program and prepares for rendering.
    procedure BeginScene; inline;

    // Deactivates the shader program, resets previously active input streams and bindings.
    procedure EndScene; inline;

    // Renders primitives with the given number of vertices.
    procedure Draw(const ATopology: TPrimitiveTopology; const AVertexCount: Cardinal;
      const ABaseVertex: Integer = 0); inline;

    // Renders indexed primitives with the given number of indices.
    procedure DrawIndexed(const ATopology: TPrimitiveTopology; const AIndexCount: Integer;
      const AStartIndex: Integer = 0; const ABaseVertex: Integer = 0); inline;

    // Renders multiple number of instances of the given primitives.
    procedure DrawInstances(const ATopology: TPrimitiveTopology; const AVertexCount,
      AInstanceCount: Integer; const ABaseVertex: Integer = 0); inline;

    // Renders multiple number of instances of the given indexed primitives.
    procedure DrawInstancesIndexed(const ATopology: TPrimitiveTopology; const AIndexCount,
      AInstanceCount: Integer; const AStartIndex: Integer = 0; const ABaseVertex: Integer = 0); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given program.
    property Device: TDevice read GetDevice;

    // Specifies number of vertices in each patch. This only applies when rendering patch topology.
    property PatchVertices: Integer write SetPatchVertices;
  end;

  // Compute shader program for general processing on GPU (GPGPU).
  TComputeProgram = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
  public
    // Creates new instance of compute shader program from its parameters.
    class function Create(const ADevice: TDevice; const AProgramElements: TProgramElements;
      const AShader: TBytes): TComputeProgram; overload; static;

    // Creates new instance of compute shader program from its parameters.
    class function Create(const ADevice: TDevice; const AProgramElements: array of TProgramElement;
      const AShader: TBytes): TComputeProgram; overload; static;

    // Creates new instance of compute shader program loading its code from disk.
    class function CreateFromFile(const ADevice: TDevice; const AProgramElements: TProgramElements;
      const AShader: string): TComputeProgram; overload; static;

    // Creates new instance of compute shader program loading its code from disk.
    class function CreateFromFile(const ADevice: TDevice; const AProgramElements: array of TProgramElement;
      const AShader: string): TComputeProgram; overload; static;

    // Releases the program.
    procedure Free; inline;

    // Assigns a generic buffer (or a portion of starting from the given offset in bytes) to a particular
    // channel. The current bindings can be considered valid until @link(EndScene) or @link(ResetBindings)
    // is called.
    procedure Bind(const ABuffer: TBuffer; const AChannel: Cardinal = 0;
      const AOffset: Cardinal = 0); overload; inline;

    // Removes association that was previously made with @link(Bind) between the buffer and the given channel.
    procedure Unbind(const ABuffer: TBuffer; const AChannel: Cardinal = 0); overload; inline;

    // Assigns texture to a particular channel.
    procedure Bind(const ATexture: PTexture; const ABindFormat: TComputeBindTextureFormat); overload; inline;

    // Removes existing texture association with the particular channel.
    procedure Unbind(const ATexture: PTexture;
      const ABindFormat: TComputeBindTextureFormat); overload; inline;

    // Resets any bindings that were previously made as if @link(Unbind) would be called for each of them.
    procedure ResetBindings; inline;

    // Applies any binding changes to constant buffers, which usually occur when a dispatch call is issued,
    // to occur immediately.
    procedure Commit; inline;

    // Activates the compute shader program.
    procedure BeginScene; inline;

    // Deactivates the compute shader program.
    procedure EndScene; inline;

    // Launches one or more compute shader work groups.
    procedure Dispatch(const AGroupsX, AGroupsY, AGroupsZ: Integer); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given program.
    property Device: TDevice read GetDevice;
  end;

  // Pointer to @link(TSampler).
  PSampler = ^TSampler;

  // Sampler object that defines how texture is read by the shaders.
  TSampler = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetState: TSamplerState; inline;
    procedure SetState(const AState: TSamplerState); inline;
  public
    // Creates new instance of sampler object.
    class function Create(const ADevice: TDevice;
      const ASamplerState: TSamplerState): TSampler; static; inline;

    // Releases the sampler object.
    procedure Free; inline;

    // Binds sampler object to a particular channel.
    procedure Bind(const AChannel: Cardinal = 0);

    // Unbinds sampler object from the particular channel.
    procedure Unbind(const AChannel: Cardinal = 0);

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given sampler.
    property Device: TDevice read GetDevice;

    // Current sampler object parameters.
    property State: TSamplerState read GetState write SetState;
  end;

  // Pointer to @link(TSurface).
  PSurface = ^TSurface;

  // Surface that contains image in memory for pixel manipulation.
  TSurface = record
  private
    FHandle: TUntypedObjectHandle;
    function GetBits: Pointer; inline;
    function GetPitch: Cardinal; inline;
    function GetBytesPerPixel: Integer; inline;
    function GetWidth: Integer; inline;
    function GetHeight: Integer; inline;
    function GetSize: TPoint2i; inline;
    function GetBufferSize: Integer; inline;
    function GetFormat: TPixelFormat; inline;
    function GetPremultipliedAlpha: Boolean; inline;
    procedure SetPremultipliedAlpha(const APremultipliedAlpha: Boolean); inline;
    function GetScanline(const AIndex: Integer): Pointer;
    function GetPixelPtr(const AX, AY: Integer): Pointer;
    function GetEmpty: Boolean; inline;
    function GetPixel(const AX, AY: Integer): TIntColor; inline;
    procedure SetPixel(const AX, AY: Integer; const AColor: TIntColor); inline;
  public
    // Creates new instance of surface with the given dimensions.
    class function Create(const AWidth, AHeight: Integer; const AFormat: TPixelFormat =
      TPixelFormat.Unknown): TSurface; overload; static; inline;

    // Creates new instance of surface containing image loaded from external file. The preference for
    // premultiplied or non-premultiplied alpha-channel is merely a suggestion, which can influence how the
    // image is loaded (depending on underlying implementation). This function typically supports most common
    // file formats such as BMP, PNG, JPEG, but may also support other formats like GIF and TIFF. }
    class function Create(const AFileName: Utf8String; const AFormatRequest: TAlphaFormatRequest =
      TAlphaFormatRequest.DontCare): TSurface; overload; static; inline;

    // Creates new instance of surface containing image loaded from file that has been preloaded into memory.
    // The internal file format is determined by "extension" parameter, which should contain a valid file
    // extension such as ".png" (case-insensitive). The preference for premultiplied or non-premultiplied
    // alpha-channel is merely a suggestion, which can influence how the image is loaded (depending on
    // underlying implementation). This function typically supports most common file formats such as BMP, PNG,
    // JPEG, but may also support other formats like GIF and TIFF.
    class function Create(const AFileContent: Pointer; const AContentSize: Cardinal;
      const AExtension: Utf8String; const AFormatRequest: TAlphaFormatRequest =
      TAlphaFormatRequest.DontCare): TSurface; overload; static; inline;

    // Releases the surface.
    procedure Free; inline;

    // Retrieves pixel from floating-point coordinates, interpolating linearly between four neighbor pixels
    // as necessary to get an accurate match. This can be used for limitless stretching, to get color values
    // that lie between individual pixels and slowly change from one pixel to another.
    function BilinearPixel(const AX, AY: Single): TIntColor; inline;

    /// Redefines surface size to the specified width, height and pixel format, discarding previous contents.
    procedure Resize(const AWidth, AHeight: Integer; const AFormat: TPixelFormat = TPixelFormat.Unknown);

    // Converts surface from its currently set pixel format to the new one. If both format match,
    // the function does nothing.
    procedure ConvertFormat(const AFormat: TPixelFormat); inline;

    // Clears the entire surface with zeros.
    procedure Clear; overload; inline;

    // Clears the entire surface with a given color. This does pixel format conversion when appropriate.
    procedure Clear(const AColor: TIntColor); overload; inline;

    // Processes surface pixels, setting alpha-channel to either fully translucent or fully opaque.
    procedure ResetAlpha(const AOpaque: Boolean = True); inline;

    // Processes the whole surface to determine whether it has meaningful alpha-channel. A surface that has
    // all its pixels with alpha-channel set to fully translucent or fully opaque (but not mixed) is
    // considered lacking alpha-channel. On the other hand, a surface that has at least one pixel with
    // alpha-channel value different than any other pixel, is considered to have alpha-channel. This is
    // useful to determine whether the surface can be stored in one of pixel formats lacking alpha-channel,
    // to avoid losing any transparency information.
    function HasAlphaChannel: Boolean; inline;

    // Processes the whole surface, premultiplying each pixel's red, green and blue values by the
    // corresponding alpha-channel value, resulting in image with premultiplied alpha. Note that this is an
    // irreversible process, during which some color information is lost permanently (smaller alpha values
    // contribute to bigger information loss). This is generally useful to prepare the image for generating
    // mipmaps and/or alpha-blending, to get more accurate visual results.
    procedure PremultiplyAlpha; inline;

    // Processes the whole surface, dividing each pixel by its alpha-value, resulting in image with
    // non-premultiplied alpha. During this process, some color information may be lost due to precision
    // issues. This can be useful to obtain original pixel information from image that has been previously
    // premultiplied; however, this does not recover lost information during premultiplication process.
    // For instance, pixels that had alpha value of zero and were premultiplied, lose all information and
    // cannot be recovered; pixels with alpha value of 128 (that is, 50% opaque) lose half of their precision
    // and after "unpremultiply" process will have values multiples of 2.
    procedure UnpremultiplyAlpha; inline;

    // Mirrors the visible image on surface horizontally.
    procedure Mirror; inline;

    // Flips the visible image on surface vertically.
    procedure Flip; inline;

    // Inverts colors in the surface.
    procedure Invert; inline;

    // Saves the contents of the surface to external file. This function typically supports most common file
    // formats such as BMP, PNG, JPEG, but may also support other formats like GIF and TIFF.
    // @italic(Quality) parameter, in case of formats such as JPEG determines the compression ratio
    // (between 0 and 100).
    procedure SaveToFile(const AFileName: Utf8String; const AQuality: Integer = 0); inline;

    // Saves the contents of surface to a file in memory and returns the size of resulting file.
    // This function will save up to @italic(AFileContentSize) bytes to the provided pointer, even if it
    // means that not a complete file will be saved. If @italic(fileContent) pointer is @nil, then this
    // function will provide an estimate size required to store the file in memory. This function typically
    // supports most common file formats such as BMP, PNG, JPEG, but may also support other formats
    // like GIF and TIFF. @italic(Quality) parameter, in case of formats such as JPEG determines
    // the compression ratio (between 0 and 100).
    function SaveToFileInMemory(const AFileContent: Pointer; const AFileContentSize: Cardinal;
      const AExtension: Utf8String; const AQuality: Integer = 0): Cardinal; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Pointer to top/left corner of pixel data contained by this surface, with horizontal rows arranged
    // linearly from top to bottom.
    property Bits: Pointer read GetBits;

    // Returns number of bytes each horizontal row of pixels occupies. This may differ from an actual
    // calculated number and may include unused or even protected memory locations, which should be
    // considered read-only and be skipped.
    property Pitch: Cardinal read GetPitch;

    // Number of bytes each pixel occupies.
    property BytesPerPixel: Integer read GetBytesPerPixel;

    // Surface width in pixels.
    property Width: Integer read GetWidth;

    // Surface height in pixels.
    property Height: Integer read GetHeight;

    // Surface size in pixels.
    property Size: TPoint2i read GetSize;

    // Surface buffer size in bytes.
    property BufferSize: Integer read GetBufferSize;

    // Pixel format in which pixels are stored.
    property Format: TPixelFormat read GetFormat;

    // Indicates whether the pixels in this surface have their alpha premultiplied or not.
    // This is just an informative parameter; to actually convert pixels from one mode to another,
    // use @link(PremultiplyAlpha) and @link(UnpremultiplyAlpha) methods.
    property PremultipliedAlpha: Boolean read GetPremultipliedAlpha write SetPremultipliedAlpha;

    // Pointer to a first pixel at the given scanline index (that is, row number).
    property Scanline[const AIndex: Integer]: Pointer read GetScanline;

    // Pointer to a first pixel at the given scanline index (that is, row number).
    property PixelPtr[const AX, AY: Integer]: Pointer read GetPixelPtr;

    // Checks whether the surface has width or height set to zero, has undefined pixel format, unknown
    // pitch, unknown bytes per pixel, or access bits set to nil.
    // If this returns false, then the surface can be considered valid.
    property Empty: Boolean read GetEmpty;

    // Access to surface's pixels. Note that this may involve pixel format conversion and from performance
    // point of view might not be the fastest technique (using surface content directly is faster).
    property Pixels[const AX, AY: Integer]: TIntColor read GetPixel write SetPixel;
  end;

  // Helper module for TSurface.
  TSurfaceHelper = record helper for TSurface
  public
    // Copies a portion of source surface to the current one according to specified source rectangle and
    // destination position. If source rectangle is empty (or nil), then the entire source surface will be
    // copied. This function does the appropriate clipping and pixel format conversion. It does not change
    // current surface size or pixel format. Note that "premultiplied alpha" flag of either current or
    // source surfaces is completely ignored.
    procedure CopyRect(const ASurface: TSurface; const ASourceRect: TIntRect;
      const ADestPos: TPoint2i); inline;

    // Copies entire contents from a source surface to the current one. If the current surface has size
    // and/or pixel format not specified, these will be copied from the source surface as well. If current
    // surface is not empty, then its pixel format will not be modified - in this case, pixel format
    // conversion may occur. This function will try to ensure that current surface size matches the source
    // surface and if if this cannot be achieved, it will fail.
    procedure CopyFrom(const ASurface: TSurface); inline;

    // This function works similarly to @link(CopyFrom), except that it produces image with half of size,
    // averaging each four pixels to one. This is specifically useful to generate mipmaps.
    procedure ShrinkFrom(const ASurface: TSurface); inline;

    // This function works similarly to @link(CopyRect), but provides stretching and/or shrinking.
    // That is, it copies source surface rectangle onto destination rectangle with point filtering.
    // Clipping and pixel format conversion is done as necessary.
    procedure StretchRect(const ASurface: TSurface; const ADestRect, ASourceRect: TFloatRect); inline;

    // This function works similarly to @link(CopyRect), but provides bilinear stretching. That is, it copies
    // source surface rectangle onto destination rectangle with linear filtering. Clipping and pixel format
    // conversion is done as necessary. Note that this function is meant for stretching only; shrinking
    // although will also work, but result in inaccurate results as shrinking requires calculating average of
    // variable number of pixels depending on shrink ratio.
    procedure StretchRectBilinear(const ASurface: TSurface; const ADestRect, ASourceRect: TFloatRect); inline;

    // Calculates signed distance field from alpha values of the source.
    procedure MakeSignedDistanceField(const ASurface: TSurface; const ASpread: VectorFloat;
      const ADestPos: TPoint2i; const ASourceRect: TIntRect); inline;
  end;

  // Texture that contains image data typically stored in GPU memory.
  TTexture = record
  private
    FHandle: TUntypedObjectHandle;
    function GetPlatformHandle: Pointer; inline;
    function GetDevice: TDevice; inline;
    function GetParameters: TTextureParameters; inline;
  public
    // Creates new instance of texture.
    class function Create(const ADevice: TDevice;
      const AParameters: TTextureParameters): TTexture; overload; static; inline;

    // Creates a new instance of texture loading its contents from an external file. The preference for
    // premultiplied or non-premultiplied alpha-channel is merely a suggestion, which can influence how the image
    // is loaded (depending on underlying implementation). This function typically supports most common file
    // formats such as BMP, PNG, JPEG, but may also support other formats like GIF and TIFF.
    class function Create(const ADevice: TDevice; const AFileName: Utf8String;
      const AFormat: TPixelFormat = TPixelFormat.Unknown;
      const AAttributes: Cardinal = 0): TTexture; overload; static; inline;

    // Creates a new instance of texture loading its contents from an image file that has been preloaded into
    // memory. The internal file format is determined by "extension" parameter, which should contain a valid
    // file extension such as @italic(.png) (case-insensitive). The preference for premultiplied or
    // non-premultiplied alpha-channel is merely a suggestion, which can influence how the image is loaded
    // (depending on underlying implementation). This function typically supports most common file formats such
    // as @italic(BMP), @italic(PNG), @italic(JPEG), but may also support other formats like @italic(GIF)
    // and @italic(TIFF).
    class function Create(const ADevice: TDevice; const AFileContent: Pointer; const AContentSize: Cardinal;
      const AExtension: Utf8String; const AFormat: TPixelFormat = TPixelFormat.Unknown;
      const AAttributes: Cardinal = 0): TTexture; overload; static; inline;

    // Creates a new instance of texture loading its contents from an existing surface.
    class function Create(const ADevice: TDevice; const ASurface: TSurface;
      const AAttributes: Cardinal = 0): TTexture; overload; static; inline;

    // Creates texture containing displacement map for Parallax Mapping technique from an external file
    // on disk.
    class function CreateFromFileParallax(const ADevice: TDevice; const AFileName: Utf8String;
      const AFormat: TPixelFormat = TPixelFormat.Unknown;
      const AAttributes: Cardinal = 0): TTexture; static; inline;

    // Creates texture containing normal map and occlusion map from external files on disk. Both normal and
    // occlusion map must have equal sizes.
    class function CreateFromFilesNormalsAndOcclusion(const ADevice: TDevice;
      const AFileNameNormalMap, AFileNameOcclusionMap: Utf8String;
      const AAttributes: Cardinal = 0): TTexture; static; inline;

    // Releases the texture.
    procedure Free; inline;

    // Copies content from user pointer to texture's owned memory at the given location. The data is assumed
    // to match texture's pixel format and destination rectangle, if specified, must be within valid bounds
    // (no clipping is performed).
    procedure Update(const AContent: Pointer; const APitch: Cardinal; const ALayer: Integer;
      const ARect: TIntRect; const AMipLevel: Integer = 0); inline;

    // Retrieves content from the given location of texture's owned memory to the user pointer. The data is
    // copied in its original format and source rectangle, if specified, must be within valid bounds
    // (no clipping is performed).
    procedure Retrieve(const AContent: Pointer; const APitch: Cardinal; const ALayer: Integer;
      const ARect: TIntRect; const AMipLevel: Integer = 0); inline;

    // Copies a portion of source surface to the current texture according to specified source rectangle and
    // destination position. If source rectangle is empty, then the entire source surface will be copied.
    // This function does the appropriate clipping and pixel format conversion. Note that
    // @italic(PremultipliedAlpha) attribute of either current or source surface is completely ignored.
    procedure Copy(const ASurface: TSurface; const ALayer: Integer; const ADestPos: TPoint2i;
      const ASourceRect: TIntRect; const AMipLevel: Integer = 0); overload; inline;

    // Copies a portion of current texture to the destination surface according to specified source rectangle
    // and destination position. If source rectangle is empty, then the entire texture surface will be
    // copied. This function does the appropriate clipping and pixel format conversion. Note that
    // @italic(premultipliedAlpha) attribute of either current or source surface is completely ignored.
    procedure Save(const ASurface: TSurface; const ALayer: Integer; const ADestPos: TPoint2i;
      const ASourceRect: TIntRect; const AMipLevel: Integer = 0); inline;

    // Loads image from disk and copies it to the specified location and parameters.
    procedure LoadFromFile(const AFileName: Utf8String; const ALayer: Integer; const ADestPos: TPoint2i;
      const ASourceRect: TIntRect; const AMipLevel: Integer = 0); inline;

    // Saves the appropriate portion of texture to external file.
    procedure SaveToFile(const AFileName: Utf8String; const AQuality: Integer; const ALayer: Integer;
      const ASourceRect: TIntRect; const AMipLevel: Integer = 0); inline;

    // Clears entire texture surface and fills pixels with zeros.
    procedure Clear; overload; inline;

    // Clears texture surface with the given color.
    // This must be called outside of device and texture @link(BeginScene) / @link(EndScene) blocks.
    procedure Clear(const AColor: TFloatColor; const ALayer: Integer = 0;
      const AMipLevel: Integer = 0); overload; inline;

    // Binds texture to the specified rendering channel.
    procedure Bind(const AChannel: Cardinal = 0); inline;

    // Unbinds texture from the specified rendering channel.
    procedure Unbind(const AChannel: Cardinal = 0); inline;

    // Removes all previous drawable texture attachments.
    procedure Detach; inline;

    // Starts rendering on the drawable texture.
    procedure BeginScene(const ALayer: Integer = 0; const AMipLevel: Integer = 0); inline;

    // Finishes rendering on the drawable texture.
    procedure EndScene; inline;

    // Generates texture's mipmaps from its base (level = 0) image.
    procedure GenerateMipMaps; inline;

    // Resets any cache associated with the texture, which means purging any temporary resources.
    // This also removes all existing texture attachments.
    procedure ResetCache; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Platform-specific texture handle.
    property PlatformHandle: Pointer read GetPlatformHandle;

    // Device associated with the given texture.
    property Device: TDevice read GetDevice;

    // Current texture parameters.
    property Parameters: TTextureParameters read GetParameters;
  end;

  // Helper module for TTexture.
  TTextureHelper = record helper for TTexture
  public
    // Copies a portion of source texture to the current one according to specified source rectangle and
    // destination position. If source rectangle is empty, then the entire source texture will be copied.
    // This function does the appropriate clipping and pixel format conversion (albeit at significant
    // performance cost). Note that @italic(PremultipliedAlpha) attribute of either current or source texture
    // is completely ignored.
    procedure Copy(const ATexture: TTexture; const ADestLayer: Integer; const ADestPos: TPoint2i;
      const ASrcLayer: Integer; const ASourceRect: TIntRect; const ADestMipLevel: Integer = 0;
      const ASrcMipLevel: Integer = 0); overload; inline;

    // Attaches another drawable texture with the current texture into a common MRT rendering stack.
    // Drawable textures will be available to shaders in the same order of attachment.
    procedure Attach(const ATexture: TTexture; const ALayer: Integer = 0;
      const AMipLevel: Integer = 0); inline;
  end;

  // Pointer to @link(TImageAtlas).
  PImageAtlas = ^TImageAtlas;

  // Image atlas, sub-images of which can be rendered with canvas.
  TImageAtlas = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetTextureCount: Integer; inline;
    function GetTexture(const ATextureIndex: Integer): TTexture; inline;
    function GetRegionCount: Integer; inline;
    function GetRegion(const ARegionIndex: Integer): TImageRegion; inline;
  public
    // Creates new instance of image atlas.
    class function Create(const ADevice: TDevice): TImageAtlas; static; inline;

    // Releases the image atlas.
    procedure Free; inline;

    // Creates and adds a new region to the list.
    function CreateRegion(const ARect: TIntRect; const ATextureIndex: Integer): Integer; inline;

    // Removes region with given index from the lsit.
    procedure RemoveRegion(const ARegionIndex: Integer); inline;

    // Creates list of regions based on repeated rectangular pattern dimensions.
    procedure MakeRegions(const APatternSize, AVisibleSize: TPoint2i; const APatternCount: Integer); inline;

    // Removes all existing textures.
    procedure ClearTextures; inline;

    // Removes texture with the given index.
    procedure RemoveTexture(const ATextureIndex: Integer); inline;

    // Attempts to create and initialize a texture with the given parameters, which is then added to
    // the list. In case of failure, -1 is returned.
    function CreateTexture(const ASize: TPoint2i; const AFormat: TPixelFormat;
      const AAttributes: Cardinal): Integer; inline;

    // Finds a suitable location to accomodate the given rectangle in existing available space, adds
    // the appropriate region to the list and returns its index. If no space is available, -1 is returned.
    function Pack(const ASize: TPoint2i; const APadding: Integer = 0): Integer; overload; inline;

    // Finds a suitable location to accomodate the given surface in existing available space, adds the
    // appropriate region to the list and copies the contents of surface to the appropriate texture,
    // returning the final region index. If no space is available or copying fails, -1 is returned.
    function Pack(const ASurface: TSurface; const ASourceRect: TIntRect;
      const APadding: Integer = 0): Integer; overload; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given atlas.
    property Device: TDevice read GetDevice;

    // Number of textures contained within the atlas.
    property TextureCount: Integer read GetTextureCount;

    // Texture associated with a particular index or nil object if such is not available.
    property Texture[const ATextureIndex: Integer]: TTexture read GetTexture;

    // Number of regions contained within the atlas.
    property RegionCount: Integer read GetRegionCount;

    // Region information for the given index.
    property Region[const ARegionIndex: Integer]: TImageRegion read GetRegion;
  end;

  // Pointer to @link(TCanvasBuffer).
  PCanvasBuffer = ^TCanvasBuffer;

  // A container for vertices and indices defining one or more triangular meshes that can be suitable for
  // rendering with a 2D canvas.
  TCanvasBuffer = record
  private
    FHandle: TUntypedObjectHandle;
    function GetVertexCount: Integer; inline;
    procedure SetVertexCount(const AVertexCount: Integer); inline;
    function GetIndexCount: Integer; inline;
    procedure SetIndexCount(const AIndexCount: Integer); inline;
    function GetVertices: PPoint2f; inline;
    function GetColors: PIntColor; inline;
    function GetIndices: PLongInt; inline;
    function GetExtents: TFloatRect; inline;
  public
    // Creates new instance of canvas buffer.
    class function Create: TCanvasBuffer; static; inline;

    // Releases the canvas buffer.
    procedure Free; inline;

    // Clears all buffers.
    procedure Clear; inline;

    // Clears all buffers and releases their memory.
    procedure ClearAndShrink; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // A number of vertices in the buffer.
    property VertexCount: Integer read GetVertexCount write SetVertexCount;

    // A number of indices in the buffer.
    property IndexCount: Integer read GetIndexCount write SetIndexCount;

    // Pointer to an existing array of vertices.
    property Vertices: PPoint2f read GetVertices;

    // Pointer to an existing array of colors.
    property Colors: PIntColor read GetColors;

    // Pointer to an existing array of indices.
    property Indices: PLongInt read GetIndices;

    // Calculates the boundaries of the existing vertices.
    property Extents: TFloatRect read GetExtents;
  end;

  // Pointer to @link(TPathBroker).
  PPathBroker = ^TPathBroker;

  // A path processing module that is able to convert paths into triangular 2D meshes that can be rendered
  // with a canvas.
  TPathBroker = record
  private
    FHandle: TUntypedObjectHandle;
  public
    // Creates new instance of path processing module.
    class function Create: TPathBroker; static; inline;

    // Releases the path processing module.
    procedure Free; inline;

    // Pre-renders a fill for the given path. @italic(AQuality) parameter determines how the resulting
    // contours are triangulated.
    procedure Fill(const ABuffer: TCanvasBuffer; const APath: array of TPathElement;
      const AColors: TColorRect; const AQuality: Boolean = True); overload;

    // Pre-renders a fill for the given path. @italic(AQuality) parameter determines how the resulting
    // contours are triangulated.
    procedure Fill(const ABuffer: TCanvasBuffer; const APath: TPathElementArray; const AColors: TColorRect;
      const AQuality: Boolean = True); overload; inline;

    // Pre-renders a stroke for the given path.
    procedure Stroke(const ABuffer: TCanvasBuffer; const APath: array of TPathElement;
      const AThickness: Single; const AColor: TIntColor; const AJoints: TPathJoint = TPathJoint.None;
      const ACaps: TLineCaps = TLineCaps.Butt; const AMiterLimit: Single = 1.0;
      const ASmoothStep: Single = 1.0); overload;

    // Pre-renders a stroke for the given path.
    procedure Stroke(const ABuffer: TCanvasBuffer; const APath: TPathElementArray; const AThickness: Single;
      const AColor: TIntColor; const AJoints: TPathJoint = TPathJoint.None;
      const ACaps: TLineCaps = TLineCaps.Butt; const AMiterLimit: Single = 1.0;
      const ASmoothStep: Single = 1.0); overload; inline;

    // Resets internal cache and releases any allocated memory.
    procedure Reset; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;
  end;

  // Pointer to @link(TCanvas).
  PCanvas = ^TCanvas;

  // Canvas that can render geometry either in 2D or on a particular plane in 3D world.
  TCanvas = record
  public const
    DefaultBlendingEffect = TBlendingEffect.Normal;
    HexagonDelta = 1.154700538;
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetClipRect: TIntRect; inline;
    procedure SetClipRect(const AClipRect: TIntRect); inline;
    function GetWorld: TMatrix4f; inline;
    procedure SetWorld(const AWorld: TMatrix4f); inline;
    function GetViewProjection: TMatrix4f; inline;
    procedure SetViewProjection(const AViewProjection: TMatrix4f); inline;
    function GetTransform: TMatrix3x2f; inline;
    procedure SetTransform(const ATransform: TMatrix3x2f); inline;
    function GetBatchCount: Integer; inline;
    function GetAttributes: TCanvasAttributes; inline;
    procedure SetAttributes(const AAttributes: TCanvasAttributes); inline;
    function GetSignedDistanceField: TSignedDistanceField; inline;
    procedure SetSignedDistanceField(const ASignedDistanceField: TSignedDistanceField); inline;
    function GetContextState: TCanvasContextState; inline;
    procedure SetContextState(const AContextState: TCanvasContextState); inline;
    function GetSamplerState: TCanvasSamplerState; inline;
    procedure SetSamplerState(const ASamplerState: TCanvasSamplerState); inline;
  public
    // Creates new instance of canvas.
    class function Create(const ADevice: TDevice): TCanvas; static; inline;

    // Releases the canvas.
    procedure Free; inline;

    // Starts rendering with the canvas.
    procedure BeginScene; inline;

    // Finishes rendering with the canvas.
    procedure EndScene; inline;

    // Draws pixels with the given positions and colors.
    procedure Pixels(const APositions: PPoint2f; const AColors: PIntColor; const AElementCount: Integer;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws lines with the given vertices, colors and indices.
    procedure Lines(const AVertices: PPoint2f; const AColors: PIntColor; const AIndices: PLongInt;
      const AVertexCount, APrimitiveCount: Integer;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws triangles with the given vertices, colors and indices.
    procedure Triangles(const AVertices: PPoint2f; const AColors: PIntColor; const AIndices: PLongInt;
      const AVertexCount, APrimitiveCount: Integer;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws triangles with the given vertices, texture coordinates, colors and indices.
    procedure Triangles(const ATexture: TTexture; const AVertices, ATexCoords: PPoint2f;
      const AColors: PIntColor; const AIndices: PLongInt; const AVertexCount, APrimitiveCount: Integer;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a pixel with the given position and color.
    procedure Pixel(const APosition: TPoint2f; const AColor: TIntColor;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a line with the given positions and colors.
    procedure Line(const ASrcPos, ADestPos: TPoint2f; const AColors: TColorPair;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a line of varying thickness with the given positions and colors.
    procedure ThickLine(const ASrcPos, ADestPos: TPoint2f; const AColors: TColorPair;
      const AThickness: Single; const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws ellipse with the given origin, radiuses, color and steps using @link(Line) primitive.
    procedure LineEllipse(const AOrigin, ARadius: TPoint2f; const ASteps: Integer; const AColor: TIntColor;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws circle with the given origin, radius, color and steps using @link(Line) primitive.
    procedure LineCircle(const AOrigin: TPoint2f; const ARadius: VectorFloat; const ASteps: Integer;
      const AColor: TIntColor; const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws ellipse of varying thickness with the given origin, radiuses, color and steps.
    procedure ThickLineEllipse(const AOrigin, ARadius: TPoint2f; const ASteps: Integer;
      const AColor: TIntColor; const AThickness: Single;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws circle of varying thickness with the given origin, radius, color and steps.
    procedure ThickLineCircle(const AOrigin: TPoint2f; const ARadius: VectorFloat; const ASteps: Integer;
      const AColor: TIntColor; const AThickness: Single;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws lines between specified triangle vertices and colors.
    procedure LineTriangle(const AVertex1, AVertex2, AVertex3: TPoint2f; const AColor1, AColor2,
      AColor3: TIntColor; const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws line path of varying thickness between specified triangle vertices and colors.
    procedure ThickLineTriangle(const AVertex1, AVertex2, AVertex3: TPoint2f; const AColor1, AColor2,
      AColor3: TIntColor; const AThickness: VectorFloat;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a wireframe quadrilateral with the given vertices and colors using @link(Line) primitive.
    procedure LineQuad(const AVertices: TQuad; const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a wireframe quadrilateral of varying thickness with the given vertices and colors.
    procedure ThickLineQuad(const AVertices: TQuad; const AColors: TColorRect; const AThickness: Single;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws lines between each vertex in a hexagon of unity size centered along axis origin and drawn with
    // gradient of six colors at the corresponding vertices. The bounding width of hexagon is exactly one,
    // which simplifies placement of multiple hexagons. The final size, position and rotation of hexagon can
    // be given using one or a combination of several 3x2 transformation matrices multiplied together.
    // This method uses @link(Line) primitive.
    procedure LineHexagon(const AColor1, AColor2, AColor3, AColor4, AColor5, AColor6: TIntColor;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws lines between each vertex in a hexagon of unity size centered along axis origin and filled with
    // four-color gradient interpolated to the corresponding vertices. The bounding width of hexagon is
    // exactly one, which simplifies placement of multiple hexagons. The final size, position and rotation of
    // hexagon can be given using one or a combination of several 3x2 transformation matrices multiplied
    // together. This method uses @link(Line) primitive.
    procedure LineHexagon(const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws lines of varying thickness between each vertex in a hexagon of unity size centered along axis
    // origin and drawn with gradient of six colors at the corresponding corners. The final size, position
    // and rotation of hexagon can be given using one or a combination of several 3x2 transformation matrices
    // multiplied together.
    procedure ThickLineHexagon(const AColor1, AColor2, AColor3, AColor4, AColor5, AColor6: TIntColor;
      const AThickness: VectorFloat; const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws lines of varying thickness between each vertex in a hexagon of unity size centered along axis
    // origin and filled with four-color gradient interpolated to the corresponding vertices. The final size,
    // position and rotation of hexagon can be given using one or a combination of several 3x2 transformation
    // matrices multiplied together.
    procedure ThickLineHexagon(const AColors: TColorRect; const AThickness: VectorFloat;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws triangle filled with color gradient specified by given positions and colors.
    procedure Triangle(const AVertex1, AVertex2, AVertex3: TPoint2f; const AColor1, AColor2,
      AColor3: TIntColor; const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled quadrilateral with the given vertices and colors.
    procedure Quad(const AVertices: TQuad; const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled rectangle with the given vertices and colors.
    procedure FillRect(const ARect: TFloatRect; const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a rectangle of varying thickness with the given vertices and colors.
    procedure FrameRect(const ARect: TFloatRect; const AColors: TColorRect; const AThickness: Single;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a filled rectangle with the given gradient and round corners.
    procedure FillRoundRect(const ARect: TFloatRect; const AColors: TColorRect; const ARadius: Single;
      const ASteps: Cardinal; const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a round frame for the given rectangle area with varying thickness and gradient.
    procedure FrameRoundRect(const ARect: TFloatRect; const AColors: TColorRect;
      const ARadius: Single; const AThickness: Single; const ASteps: Cardinal;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a filled rectangle with the given gradient and round corners on top.
    procedure FillRoundRectTop(const ARect: TFloatRect; const AColors: TColorRect; const ARadius: Single;
      const ASteps: Integer; const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a filled rectangle with the given gradient and round corners on bottom.
    procedure FillRoundRectBottom(const ARect: TFloatRect; const AColors: TColorRect; const ARadius: Single;
      const ASteps: Integer; const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a filled rectangle with the given gradient and round corners, but top is reverted.
    // This can be used to render rounded window background behind caption.
    procedure FillRoundRectTopInverse(const ARect: TFloatRect; const AColors: TColorRect;
      const ARadius: Single; const ASteps: Integer;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws either a highlight or a shadow around the specified rectangle with the given parameters,
    // mimicking effect of gaussian highlight.
    procedure Highlight(const ARect: TFloatRect; const ACornerRadius, ALuma, ADistance: Single;
      const ASteps: Integer); inline;

    // Draws hexagon of unity size centered along axis origin and filled with gradient of six colors at the
    // corresponding vertices. The bounding width of hexagon is exactly one, which simplifies placement of
    // multiple hexagons. The final size, position and rotation of hexagon can be given using one or a
    // combination of several 3x2 transformation matrices multiplied together.
    procedure Hexagon(const AColor1, AColor2, AColor3, AColor4, AColor5, AColor6: TIntColor;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws hexagon of unity size centered along axis origin and filled with four-color gradient interpolated
    // to the corresponding vertices. The bounding width of hexagon is exactly one, which simplifies placement
    // of multiple hexagons. The final size, position and rotation of hexagon can be given using one or a
    // combination of several 3x2 transformation matrices multiplied together.
    procedure Hexagon(const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled arc with the given origin, radiuses, angles, steps and color gradient going away from
    // the center of the arc.
    procedure Arc(const AOrigin, ARadius: TPoint2f; const AInitAngle, AEndAngle: Single;
      const ASteps: Integer; const AColorInside, AColorOutside: TIntColor;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled arc with the given origin, radiuses, angles, steps and colors.
    procedure Arc(const AOrigin, ARadius: TPoint2f; const AInitAngle, AEndAngle: Single;
      const ASteps: Integer; const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled ellipse with the given origin, radiuses, steps and colors.
    procedure Ellipse(const AOrigin, ARadius: TPoint2f; const ASteps: Integer; const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a filled ribbon between inner and outer radiuses filled with four-color gradient.
    procedure Ribbon(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const ASteps: Integer;
      const AColors: TColorRect; const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled ribbon between inner and outer radiuses filled with continuous three-pair gradient.
    procedure Ribbon(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const ASteps: Integer;
      const AColor1, AColor2, AColor3: TColorPair;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled ribbon between inner and outer radiuses filled with a single color.
    procedure Ribbon(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const ASteps: Integer;
      const AColor: TIntColor; const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled tape between the given radiuses and angles, filled with four-color gradient.
    procedure Tape(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const AInitAngle,
      AEndAngle: Single; const ASteps: Integer; const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled tape between the given radiuses and angles, filled with continuous three-pair gradient.
    procedure Tape(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const AInitAngle,
      AEndAngle: Single; const ASteps: Integer; const AColor1, AColor2, AColor3: TColorPair;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled tape between the given radiuses and angles, filled with a single color.
    procedure Tape(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const AInitAngle,
      AEndAngle: Single; const ASteps: Integer; const AColor: TIntColor;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a filled rectangle with a hole inside of it, filled with the given colors.
    procedure RectWithHole(const ARect: TFloatRect; const AHoleOrigin, AHoleRadius: TPoint2f;
      const AColors: TColorPair; const ASteps: Integer;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws triangles from an existing buffer.
    procedure Draw(const ABuffer: TCanvasBuffer; const AEffect: TBlendingEffect = DefaultBlendingEffect);

    // Draws a textured triangle multiplied with a color gradient specified by given positions, colors and
    // texture coordinates (in range from 0 to 1).
    procedure Triangle(const ATexture: TTexture; const AVertex1, AVertex2, AVertex3, ATexCoord1, ATexCoord2,
      ATexCoord3: TPoint2f; const AColor1, AColor2, AColor3: TIntColor;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a textured triangle multiplied with a color gradient specified by given positions, colors and
    // texture coordinates (in pixels).
    procedure TriangleRegion(const ATexture: TTexture; const AVertex1, AVertex2, AVertex3, ATexCoord1,
      ATexCoord2, ATexCoord3: TPoint2f; const AColor1, AColor2, AColor3: TIntColor;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a textured quadrilateral multiplied with a color gradient specified by the given vertices,
    // colors and texture coordinates (in range from 0 to 1).
    procedure Quad(const ATexture: TTexture; const AVertices, ATexCoords: TQuad; const AColors: TColorRect;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a textured quadrilateral multiplied with a color gradient specified by the given vertices,
    // colors and texture coordinates (in pixels).
    procedure QuadRegion(const ATexture: TTexture; const AVertices, ATexCoords: TQuad;
      const AColors: TColorRect; const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a textured rectangle with round corners and the given gradient and texture coordinates.
    procedure TexturedRoundRect(const ATexture: TTexture; const ARect: TFloatRect; const ATexCoords: TQuad;
      const AColors: TColorRect; const ARadius: Single; const ASteps: Integer;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a textured rectangle with round corners and the given gradient and texture
    // coordinates (in pixels).
    procedure TexturedRoundRectRegion(const ATexture: TTexture; const ARect: TFloatRect;
      const ATexCoords: TQuad; const AColors: TColorRect; const ARadius: Single; const ASteps: Integer;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); inline;

    // Draws a textured quadrilateral from source portion of image atlas, multiplied by a color gradient.
    procedure QuadImage(const AImageAtlas: TImageAtlas; const AVertices: TQuad; const ARegionIndex: Integer;
      const AColors: TColorRect; const ASourceRect: TFloatRect; const AModifiers: TImageAttributes;
      const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Draws a textured quadrilateral from source portion of image atlas, multiplied by a color gradient.
    procedure QuadImage(const AImageAtlas: TImageAtlas; const AVertices: TQuad; const ARegionIndex: Integer;
      const AColors: TColorRect; const AEffect: TBlendingEffect = DefaultBlendingEffect); overload; inline;

    // Resets any canvas sampler state that is currently set.
    procedure ResetSamplerState; inline;

    // Flushes the canvas by rendering primitives that are still in batch buffers.
    procedure Flush; inline;

    // Resets canvas internal resources and releases any non-critical memory buffers.
    procedure Reset; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given canvas.
    property Device: TDevice read GetDevice;

    // Currently set clipping rectangle.
    property ClipRect: TIntRect read GetClipRect write SetClipRect;

    // 3D world transformation matrix.
    property World: TMatrix4f read GetWorld write SetWorld;

    // 3D view/projection transformation matrix.
    property ViewProjection: TMatrix4f read GetViewProjection write SetViewProjection;

    // 2D transformation matrix.
    property Transform: TMatrix3x2f read GetTransform write SetTransform;

    // Number of batches that were rendered between current set of begin/end block. Drawing each individual
    // batch involves some overhead, so when this parameter happens to be considerably high at some point,
    // the rendering code should be revised for better grouping of images, shapes and blending types.
    property BatchCount: Integer read GetBatchCount;

    // Currently set rendering attributes.
    property Attributes: TCanvasAttributes read GetAttributes write SetAttributes;

    // Currently set signed distance field parameters.
    property SignedDistanceField: TSignedDistanceField read GetSignedDistanceField
      write SetSignedDistanceField;

    // Pre-defined device rendering state configuration for specific canvas state.
    property ContextState: TCanvasContextState read GetContextState write SetContextState;

    // Retrieves or updates canvas sampler state.
    property SamplerState: TCanvasSamplerState read GetSamplerState write SetSamplerState;
  end;

  // Pointer to @link(TTextRenderer).
  PTextRenderer = ^TTextRenderer;

  // Text renderer module that can draw text on the canvas.
  TTextRenderer = record
  private
    FHandle: TUntypedObjectHandle;
    function GetFontParameters: TFontParameters; inline;
    procedure SetFontParameters(const AFontParameters: TFontParameters); inline;
    function GetCanvas: TCanvas; inline;
  public
    // Creates new instance of text renderer.
    class function Create(const ACanvas: TCanvas; const ATextureSize: TPoint2i;
      const APixelFormat: TPixelFormat = TPixelFormat.Unknown;
      const AMipMapping: Boolean = False): TTextRenderer; static; inline;

    // Releases the text renderer.
    procedure Free; inline;

    // Returns the logical dimensions that the given text will occupy when rendered.
    function Extent(const AText: Utf8String; const AModifiers: PTextRenderModifiers = nil): TPoint2f; inline;

    // Calculates the actual rectangle in pixels that the given text will occupy when rendered.
    function ExtentByPixels(const AText: Utf8String;
      const AModifiers: PTextRenderModifiers = nil): TFloatRect; inline;

    // Provides information regarding individual character position and sizes for the given text string when
    // rendered. This can be useful for components such as text edit box, for highlighting and selecting
    // different characters. The actual width and height of the text is, optionally, provided as well.
    // Returns actual number of elements returned. Calling this function with @link(ARects) set to @nil would
    // calculate how many elements need to be allocated.
    function Rects(const AText: Utf8String; const AExtent: PPoint2f; const ARects: PTextEntryRect;
      const AModifiers: PTextRenderModifiers = nil): Integer;

    // Draws text at the given position.
    procedure Draw(const APosition: TPoint2f; const AText: Utf8String; const AColors: TColorPair;
      const AAlpha: VectorFloat = 1.0; const AModifiers: PTextRenderModifiers = nil); inline;

    // Draws text at the given position with specific alignment.
    procedure DrawAligned(const APosition: TPoint2f; const AText: Utf8String; const AColors: TColorPair;
      const AHorizAlign, AVertAlign: TTextAlignment; const AAlpha: VectorFloat = 1.0;
      const AAlignToPixels: Boolean = True; const AModifiers: PTextRenderModifiers = nil); inline;

    // Draws text centered around the specified position.
    procedure DrawCentered(const APosition: TPoint2f; const AText: Utf8String; const AColors: TColorPair;
      const AAlpha: VectorFloat = 1.0; const AAlignToPixels: Boolean = True;
      const AModifiers: PTextRenderModifiers = nil); inline;

    // Draws text at the given position with specific alignment by actual visible pixels.
    procedure DrawAlignedByPixels(const APosition: TPoint2f; const AText: Utf8String;
      const AColors: TColorPair; const AHorizAlign, AVertAlign: TTextAlignment;
      const AAlpha: VectorFloat = 1.0; const AAlignToPixels: Boolean = True;
      const AModifiers: PTextRenderModifiers = nil); inline;

    // Draws text centered around the specified position by actual visible pixels.
    procedure DrawCenteredByPixels(const APosition: TPoint2f; const AText: Utf8String;
      const AColors: TColorPair; const AAlpha: VectorFloat = 1.0; const AAlignToPixels: Boolean = True;
      const AModifiers: PTextRenderModifiers = nil); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Currently selected font settings.
    property FontParameters: TFontParameters read GetFontParameters write SetFontParameters;

    // Canvas associated with the text renderer.
    property Canvas: TCanvas read GetCanvas;
  end;

  // Font provider associated with a particular font settings.
  TFontProvider = record
  private
    FHandle: TUntypedObjectHandle;
  public
    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;
  end;

  // Pointer to @link(TTextModeller).
  PTextModeller = ^TTextModeller;

  // 2D and 3D text rendering module.
  TTextModeller = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetFontProvider: TFontProvider; inline;
    procedure SetFontProvider(const AProvider: TFontProvider); inline;
    function GetTransform: TMatrix4f; inline;
    procedure SetTransform(const ATransform: TMatrix4f); inline;
  public
    // Creates new instance of text renderer.
    class function Create(const ADevice: TDevice): TTextModeller; static; inline;

    // Releases the text renderer.
    procedure Free; inline;

    // Searches for an existing font provider with the given parameters and if such is not found, creates a
    // new one. Returns the appropriate font provider or null-object if provider could not be created.
    function SetFontParameters(const AParameters: TFontParameters): TFontProvider; inline;

    // Returns the logical dimensions that the given text will occupy when rendered.
    function Extent(const AText: Utf8String; const AModifiers: PTextRenderModifiers = nil): TPoint2f; inline;

    // Calculates a physical shape area that a given text occupies when rendered at zero position.
    function ExtentByShape(const AText: Utf8String;
      const AModifiers: PTextRenderModifiers = nil): TFloatRect; inline;

    // Provides information regarding individual character position and sizes for the given text string when
    // rendered. This can be useful for components such as text edit box, for highlighting and selecting
    // different characters. The actual width and height of the text is, optionally, provided as well.
    // Returns actual number of elements returned. Calling this function with @link(ARects) set to @nil would
    // calculate how many elements need to be allocated.
    function Rects(const AText: Utf8String; const AExtent: PPoint2f; const ARects: PTextEntryRect;
      const AModifiers: PTextRenderModifiers = nil): Integer;

    // Draws 2D text at the given position with specific alignment directly to canvas.
    procedure Draw(const ACanvas: TCanvas; const APosition: TPoint2f; const AText: Utf8String;
      const AColors: TColorPair; const AAlignHoriz: TTextAlignment = TTextAlignment.Middle;
      const AAlignVert: TTextAlignment = TTextAlignment.Middle; const AAlignByShape: Boolean = False;
      const AModifiers: PTextRenderModifiers = nil;
      const AEffect: TBlendingEffect = TCanvas.DefaultBlendingEffect); overload; inline;

    // Draws 2D text at the given position with specific alignment to a canvas buffer.
    procedure Draw(const ACanvasBuffer: TCanvasBuffer; const APosition: TPoint2f; const AText: Utf8String;
      const AColors: TColorPair; const AAlignHoriz: TTextAlignment = TTextAlignment.Middle;
      const AAlignVert: TTextAlignment = TTextAlignment.Middle; const AAlignByShape: Boolean = False;
      const AModifiers: PTextRenderModifiers = nil); overload; inline;

    // Draws a curved 2D text at the given position with specific alignment directly to canvas.
    // Note: if angle is "NaN", then the text is centered vertically according to the curvature; that is,
    // if the radius would converge to infinity, the text would converge to look like if it wasn't curved.
    procedure DrawCurved(const ACanvas: TCanvas; const APosition: TPoint2f; const AText: Utf8String;
      const ARadius, AAngle: Single; const AColors: TColorPair; const AModifiers: PTextRenderModifiers = nil;
      const AEffect: TBlendingEffect = TCanvas.DefaultBlendingEffect); overload; inline;

    // Draws a curved 2D text at the given position with specific alignment to a canvas buffer.
    // Note: if angle is "NaN", then the text is centered vertically according to the curvature; that is,
    // if the radius would converge to infinity, the text would converge to look like if it wasn't curved.
    procedure DrawCurved(const ACanvasBuffer: TCanvasBuffer; const APosition: TPoint2f;
      const AText: Utf8String; const ARadius, AAngle: Single; const AColors: TColorPair;
      const AModifiers: PTextRenderModifiers = nil); overload; inline;

    // Renders a volumetric 3D text at the given position, orientation and alignment. The text is oriented in
    // XZ plane with front side oriented towards positive Y axis.
    procedure Draw(const APosition: TVector3f; const AText: Utf8String; const ADepth: Single;
      const AColors: TColorPair; const AAlignHoriz: TTextAlignment = TTextAlignment.Middle;
      const AAlignVert: TTextAlignment = TTextAlignment.Middle;
      const AAlignDepth: TTextAlignment = TTextAlignment.Start; const AAlignByShape: Boolean = False;
      const AModifiers: PTextRenderModifiers = nil); overload; inline;

    // Renders a curved volumetric 3D text at the given position, orientation and alignment. The text is
    // oriented in XZ plane with front side oriented towards positive Y axis, and the rotation is performed
    // around Y axis. Note: if angle is "NaN", then the text is centered vertically according to the curvature;
    // that is, if the radius would converge to infinity, the text would converge to look like if it wasn't
    // curved.
    procedure DrawCurved(const APosition: TVector3f; const AText: Utf8String; const ARadius, AAngle,
      ADepth: Single; const AColors: TColorPair; const AAlignDepth: TTextAlignment = TTextAlignment.Start;
      const AModifiers: PTextRenderModifiers = nil); overload; inline;

    // Renders a curved volumetric 3D text at the given position, orientation and alignment. The text is
    // oriented in XZ plane with front side oriented towards positive Y axis, and the rotation is performed
    // around Z axis. Note: if angle is "NaN", then the text is centered vertically according to the curvature;
    // that is, if the radius would converge to infinity, the text would converge to look like if it wasn't
    // curved.
    procedure DrawDepthCurved(const APosition: TVector3f; const AText: Utf8String; const ARadius, AAngle,
      ADepth: Single; const AColors: TColorPair;
      const AModifiers: PTextRenderModifiers = nil); overload; inline;

    // Clears GPU buffers for rendering 3D text.
    procedure Clear; inline;

    // Prepares and fills GPU buffers with an existing 3D text rendering data.
    procedure Prepare; inline;

    // Issues draw call with existing GPU buffers attached.
    procedure Render(const AProgram: TProgram; const AChannel: Cardinal = 0); inline;

    // Releases existing resources, character records and buffers.
    procedure Reset; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with this module.
    property Device: TDevice read GetDevice;

    // Currently associated font provider.
    property FontProvider: TFontProvider read GetFontProvider write SetFontProvider;

    // Current 3D transformation matrix.
    property Transform: TMatrix4f read GetTransform write SetTransform;
  end;

  // Pointer to @link(TGrapher).
  PGrapher = ^TGrapher;

  // 3D graph plotting module.
  TGrapher = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetTransform: TMatrix4f; inline;
    procedure SetTransform(const ATransform: TMatrix4f); inline;
    function GetBatchCount: Integer; inline;
  public
    // Creates new instance of 3D plotting grapher.
    class function Create(const ADevice: TDevice): TGrapher; static; inline;

    // Releases the 3D plotting grapher.
    procedure Free; inline;

    // Prepares grapher for rendering.
    procedure BeginScene; inline;

    // Finishes grapher rendering.
    procedure EndScene; inline;

    // Draws points at their given vertices with their respective sizes ("w" component of each vertex),
    // colors, angles and the appropriate shape in 3D space.
    procedure Points(const AVertices: PVector4f; const AColors: PIntColor; const AAngles: PSingle;
      const AElementCount: Integer; const AShape: TPointShape); inline;

    // Draws lines at their given vertices, thicknesses ("w" component of each vertex), colors and indices.
    procedure Lines(const AVertices: PVector4f; const AColors: PIntColor; const AIndices: PLongInt;
      const AVertexCount, AIndexCount: Integer; const ACaps: TLineCaps); inline;

    // Draws a single point with the given parameters.
    procedure Point(const APosition: TVector3f; const AColor: TIntColor; const ASize: Single;
      const AShape: TPointShape; const AAngle: Single = 0.0); inline;

    // Draws a single line with the given parameters.
    procedure Line(const APosition1, APosition2: TVector3f; const AColor1, AColor2: TIntColor;
      const AThickness1, AThickness2: Single; const ACaps: TLineCaps = TLineCaps.Butt); inline;

    // Draws an arrow from origin to destination and the given parameters.
    // @italic(targetSize) is the rendering surface size (required for proper arrow angle calculation).
    procedure Arrow(const ATargetSize: TPoint2f; const AOrigin, ADestination: TVector3f;
      const AColor: TIntColor; const AThickness, ASize: Single;
      const ACaps: TLineCaps = TLineCaps.Butt); inline;

    // Draws lines around a bounding box for the given volume matrix.
    procedure BoundingBox(const AVolume: TMatrix4f; const AColor: TIntColor; const AThickness: Single;
      const ALength: Single = 0.5; const ACaps: TLineCaps = TLineCaps.Butt); inline;

    // Draws a line of dots with the given parameters.
    procedure DottedLine(const APosition1, APosition2: TVector3f; const AColor1, AColor2: TIntColor;
      const AThickness1, AThickness2, ASparsity: Single; const AShape: TPointShape = TPointShape.Square;
      const AAngle: Single = 0.0); inline;

    // Flushes grapher cache and draws any pending primitives on the rendering surface.
    // This can be useful to make sure that nothing remains in graph cache before changing any device states.
    procedure Flush; inline;

    // Resets grapher internal resources and releases any non-critical memory buffers. This can reduce
    // overall memory consumption, when switching from one complex visual scene to another, to allow graph
    // to "adapt" to a new scene from a fresh start.
    procedure Reset; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given grapher.
    property Device: TDevice read GetDevice;

    // Currently set 3D transformation (world / view / projection) matrix.
    property Transform: TMatrix4f read GetTransform write SetTransform;

    // Number of batches that were sent to rendering pipeline. Drawing each individual batch involves some
    // overhead, which is implementation-specific. If this parameter happens to be considerably high at some
    // point, the rendering code should be revised for better grouping of plotting primitives.
    property BatchCount: Integer read GetBatchCount;
  end;

  // Pointer to @link(TGaussianBlur).
  PGaussianBlur = ^TGaussianBlur;

  // Gaussian Blur module.
  TGaussianBlur = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetSamples: Integer; inline;
    procedure SetSamples(const ASamples: Integer); inline;
    function GetSigma: Single; inline;
    procedure SetSigma(const ASigma: Single); inline;
    function GetChroma: Single; inline;
    procedure SetChroma(const AChroma: Single); inline;
    function GetFixedSamples: Integer; inline;
    function GetHardwareFiltering: Boolean; inline;
    procedure SetHardwareFiltering(const AHardwareFiltering: Boolean); inline;
  public
    // Creates a new instance of gaussian blur module.
    class function Create(const ADevice: TDevice; const ASigma: Single = 3.0; const ASamples: Integer = 9;
      const AHardwareFiltering: Boolean = True): TGaussianBlur; static; inline;

    // Releases the gaussian blur module.
    procedure Free; inline;

    // Applies a horizontal blur from source to intermediary texture, then a vertical blur from intermediary
    // to destination texture. Both destination and intermediary textures must have drawable attribute.
    // If source texture has drawable attribute, then it can be passed as destination texture as well.
    procedure Update(const ADestinationTexture, AIntermediaryTexture,
      ASourceTexture: TTexture); overload; inline;

    // Applies a horizontal blur from source to intermediary texture, then a vertical blur from intermediary
    // to destination texture. Both destination and intermediary textures must have drawable attribute.
    // If source texture has drawable attribute, then it can be passed as destination texture as well.
    // If destination position and/or intermediate rectangle are specified, then only a portion of
    // intermediary texture will be copied to destination at the given offset.
    procedure Update(const ADestinationTexture, AIntermediaryTexture, ASourceTexture: TTexture;
      const ADestPos: TPoint2i; const AIntermRect: TIntRect); overload; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given module.
    property Device: TDevice read GetDevice;

    // Number of samples in the kernel.
    property Samples: Integer read GetSamples write SetSamples;

    // Value of sigma (affects blur strength).
    property Sigma: Single read GetSigma write SetSigma;

    // Chroma offset for color adjustment.
    property Chroma: Single read GetChroma write SetChroma;

    // Returns maximum number of samples supported by a fixed-sample blur, which enables higher performance.
    property FixedSamples: Integer read GetFixedSamples;

    // Whether hardware filtering is used to accelerate the blur.
    property HardwareFiltering: Boolean read GetHardwareFiltering write SetHardwareFiltering;
  end;

  // Pointer to @link(TKawaseBlur).
  PKawaseBlur = ^TKawaseBlur;

  // Kawase Blur module.
  TKawaseBlur = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetPasses: Integer; inline;
    procedure SetPasses(const ASamples: Integer); inline;
    function GetOffset: TPoint2f; inline;
    procedure SetOffset(const AOffset: TPoint2f); inline;
  public
    // Creates a new instance of kawase blur module.
    class function Create(const ADevice: TDevice): TGaussianBlur; static; inline;

    // Releases the kawase blur module.
    procedure Free; inline;

    /// Applies an advancing blur kernel from source to intermediary texture, then from intermediary to
    /// destination texture. Both destination and intermediary textures must have drawable attribute.
    /// If source texture has drawable attribute, then it can be passed as destination texture as well.
    procedure Update(const ADestinationTexture, AIntermediaryTexture,
      ASourceTexture: TTexture); inline;

    // Applies blur in a single pass with the given custom offset. This can be used simultaneously when
    // performing downsampling.
    procedure SinglePass(const ADestinationTexture, ASourceTexture: TTexture;
      const AOffset: TPoint2f); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given module.
    property Device: TDevice read GetDevice;

    // Number of blur passes (each pass includes two processing steps).
    property Passes: Integer read GetPasses write SetPasses;

    // Value of sigma (affects blur strength).
    property Offset: TPoint2f read GetOffset write SetOffset;
  end;

  // Pointer to @link(TColorDithering).
  PColorDithering = ^TColorDithering;

  // Color dithering module that can be used when rendering from higher precision color formats (e.g. HDR) to
  // lower precision, effectively reducing banding effect.
  TColorDithering = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetFormat: TColorDitheringFormat; inline;
    procedure SetFormat(const AFormat: TColorDitheringFormat); inline;
  public
    // Creates a new instance of color dithering module.
    class function Create(const ADevice: TDevice): TColorDithering; static; inline;

    // Releases the color dithering module.
    procedure Free; inline;

    // Performs color dithering of the source texture rendering to destination texture.
    procedure Execute(const ADestinationTexture, ASourceTexture: TTexture); overload; inline;

    /// Performs color dithering of the source texture rendering a full-screen quad.
    procedure Execute(const ASourceTexture: TTexture); overload; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given module.
    property Device: TDevice read GetDevice;

    // Target color quantization format for dithering.
    property Format: TColorDitheringFormat read GetFormat write SetFormat;
  end;

  // Pointer to @link(TSelectionHighlight).
  PSelectionHighlight = ^TSelectionHighlight;

  // A module that shows a selection highlight around one or more 2D and/or 3D objects in multiple colors
  // with an optional outline.
  TSelectionHighlight = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetSize: TPoint2i; inline;
    procedure SetSize(const ASize: TPoint2i); inline;
    function GetDepthStencil: TPixelFormat; inline;
    function GetSamples: Integer; inline;
    function GetGrayscale: Boolean; inline;
    function GetParameters: TSelectionHighlightParameters; inline;
    procedure SetParameters(const AParameters: TSelectionHighlightParameters); inline;
    function GetRendering: Boolean; inline;
    function GetTexture(const AType: TSelectionHighlightTextureType): TTexture; inline;
    function GetTextureCoordinates: TQuad; inline;
  public
    // Creates a new instance of selection highlight module.
    class function Create(const ADevice: TDevice; const ASize: TPoint2i;
      const ADepthStencil: TPixelFormat = TPixelFormat.Unknown; const ASamples: Integer = 8;
      const AGrayscale: Boolean = False): TSelectionHighlight; static; inline;

    // Releases the color dithering module.
    procedure Free; inline;

    // Clears the primary rendering textures.
    procedure Clear; inline;

    // Begins rendering the primary textures.
    procedure BeginScene; inline;

    // Ends rendering to the primary textures.
    procedure EndScene; inline;

    // Performs final post-filtering of the textures to produce the highlight selection effect.
    procedure Filter; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given module.
    property Device: TDevice read GetDevice;

    // Size of the rendering surfaces.
    property Size: TPoint2i read GetSize write SetSize;

    // Pixel format used for depth/stencil buffer.
    property DepthStencil: TPixelFormat read GetDepthStencil;

    // Number of samples used for multisampling.
    property Samples: Integer read GetSamples;

    // Whether the technique is performed in a more optimal, grayscale mode, instead of full color.
    property Grayscale: Boolean read GetGrayscale;

    // Parameters that define the behavior and characteristics of the technique.
    property Parameters: TSelectionHighlightParameters read GetParameters write SetParameters;

    // Indicates that the rendering is currently performed to the primary textures.
    property Rendering: Boolean read GetRendering;

    // Texture of the given type associated with the module.
    property Texture[const AType: TSelectionHighlightTextureType]: TTexture read GetTexture;

    // Texture coordinates that can be passed to canvas for rendering the highlight.
    property TextureCoordinates: TQuad read GetTextureCoordinates;
  end;

  // Module that performs both ground fog and distance-based fog simultaneously.
  TSpatialFog = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetParameters: TFogParameters; inline;
    procedure SetParameters(const AParameters: TFogParameters); inline;
    function GetView: TMatrix4f; inline;
    procedure SetView(const AView: TMatrix4f); inline;
    function GetProjection: TMatrix4f; inline;
    procedure SetProjection(const AProjection: TMatrix4f); inline;
    function GetFormula: TFogFormula; inline;
    procedure SetFormula(const AFormula: TFogFormula); inline;
    function GetDistance: TDepthFogDistance; inline;
    procedure SetDistance(const ADistance: TDepthFogDistance); inline;
  public
    // Creates a new instance of spatial fog module.
    class function Create(const ADevice: TDevice): TSpatialFog; static; inline;

    // Releases the spatial fog module.
    procedure Free; inline;

    // Computes fog and applies it to the destination high-dynamic range (HDR) color texture. The module
    // requires an existing linear depths texture (optionally multisampled) to be provided.
    procedure Execute(const ADestination, ALinearDepths: TTexture); inline;

    // Computes fog and applies it to the destination "glassy" composition texture. The module
    // requires an existing linear depths texture (optionally multisampled) to be provided.
    procedure ExecuteGlassy(const ADestination, ALinearDepths: TTexture); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given module.
    property Device: TDevice read GetDevice;

    // Parameters that define spatial fog characteristics.
    property Parameters: TFogParameters read GetParameters write SetParameters;

    // View transformation matrix.
    property View: TMatrix4f read GetView write SetView;

    // Projection transformation matrix.
    property Projection: TMatrix4f read GetProjection write SetProjection;

    // Formula used for the fog.
    property Formula: TFogFormula read GetFormula write SetFormula;

    // Distance calculation formula for the fog.
    property Distance: TDepthFogDistance read GetDistance write SetDistance;
  end;

  // Pointer to @link(TSceneLights).
  PSceneLights = ^TSceneLights;

  // Module for storing and working with lights in a 3D scene.
  TSceneLights = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetCount: Integer; inline;
    function GetItem(const AIndex: Integer): PSceneLight; inline;
    function GetViewSize: TPoint2i; inline;
    procedure SetViewSize(const AViewSize: TPoint2i); inline;
    function GetClusterSize: Integer; inline;
    procedure SetClusterSize(const AClusterSize: Integer); inline;
    function GetDepthSlices: Integer; inline;
    procedure SetDepthSlices(const ADepthSlices: Integer);
    function GetClustersCullingMode: TClustersCullingMode; inline;
    procedure SetClustersCullingMode(const AMode: TClustersCullingMode); inline;
    function GetClusters: TPoint2i; inline;
    function GetIndices: TBuffer; inline;
  public
    // Creates a new instance of 3D scene light module.
    class function Create(const ADevice: TDevice): TSceneLights; static; inline;

    // Releases the container.
    procedure Free; inline;

    // Adds a new light with default parameters.
    function Add: PSceneLight; inline;

    // Erases light with the given index.
    procedure Erase(const AIndex: Integer); inline;

    // Removes all existing lights.
    procedure Clear; inline;

    // Calculates cluster frustums, then performs light culling for each of the clusters.
    // As a result, a buffer of light indices for each of the clusters will be produced.
    procedure Execute(const AView, AProjection: TMatrix4f); inline;

    // Renders light culling debug information. A call to @link(Execute) must be performed before this.
    procedure RenderDebug(const AIntensity: Boolean); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given module.
    property Device: TDevice read GetDevice;

    // Returns total number of light sources.
    property Count: Integer read GetCount;

    // Pointer to an existing 3D scene light parameters.
    property Items[const AIndex: Integer]: PSceneLight read GetItem; default;

    // Size of the viewable area.
    property ViewSize: TPoint2i read GetViewSize write SetViewSize;

    // Size of individual lighting clusters.
    property ClusterSize: Integer read GetClusterSize write SetClusterSize;

    // Number of cluster slices per viewable depth.
    property DepthSlices: Integer read GetDepthSlices write SetDepthSlices;

    // Mode used for packing culled lights in clusters.
    property ClustersCullingMode: TClustersCullingMode read GetClustersCullingMode
      write SetClustersCullingMode;

    // Number of clusters per width and height of viewable area.
    property Clusters: TPoint2i read GetClusters;

    // Light indices that have been calculated for each of the clusters.
    property Indices: TBuffer read GetIndices;
  end;

  // Pointer to @link(TObjectMaterials).
  PObjectMaterials = ^TObjectMaterials;

  // Container for storing 3D object materials.
  TObjectMaterials = record
  private
    FHandle: TUntypedObjectHandle;
    function GetCount: Integer; inline;
    function GetItem(const AIndex: Integer): PObjectMaterial; inline;
  public
    // Creates a new instance of 3D object materials container.
    class function Create: TObjectMaterials; static; inline;

    // Releases the container.
    procedure Free; inline;

    // Adds a new material with default parameters.
    function Add: PObjectMaterial; inline;

    // Erases material with the given index.
    procedure Erase(const AIndex: Integer); inline;

    // Removes all existing materials.
    procedure Clear; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Returns total number of object materials.
    property Count: Integer read GetCount;

    // Pointer to an existing 3D object material parameters.
    property Items[const AIndex: Integer]: PObjectMaterial read GetItem; default;
  end;

  // Pointer to @link(TMeshModel).
  PMeshModel = ^TMeshModel;

  // 3D mesh model that has vertex and optionally index buffers for rendering.
  TMeshModel = record
  private
    FHandle: TUntypedObjectHandle;
    function GetRenderable: Boolean; inline;
    function GetVertexCount: Integer; inline;
    function GetIndexCount: Integer; inline;
    function GetChannel: Cardinal; inline;
    function GetVertexBuffer: TBuffer; inline;
    function GetIndexBuffer: TBuffer; inline;
  public
    // Creates new instance of 3D mesh model.
    class function Create(const ADevice: TDevice; const AVertexCount: Integer;
      const AVertexElementPitch: Cardinal; const AIndexCount: Integer = 0; const AChannel: Cardinal = 0;
      const AInitialVertexData: Pointer = nil;
      const AInitialIndexData: Pointer = nil): TMeshModel; static;

    // Releases the 3D mesh model.
    procedure Free; inline;

    // Releases vertex and/or index buffers, setting number of vertices and indices to zero.
    // This basically turns an existing model into a "dummy" or a placeholder.
    procedure Dismantle; inline;

    // Renders the mesh model with the given program.
    procedure Draw(const AProgram: TProgram;
      const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles;
      const AElementCount: Integer = 0; const AStartIndex: Integer = 0; const ABaseVertex: Integer = 0;
      const APostUnbind: Boolean = True); inline;

    // Renders multiple instances of the mesh model with a given program.
    procedure DrawInstances(const AProgram: TProgram; const AInstanceCount: Integer;
      const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles; const AElementCount: Integer = 0;
      const AStartIndex: Integer = 0; const ABaseVertex: Integer = 0;
      const APostUnbind: Boolean = True); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Tests whether the model can actually be rendered. For that, it must contain at least a non-empty
    // vertex buffer. Non-renderable mesh is basically a "dummy" that serves as a placeholder.
    property Renderable: Boolean read GetRenderable;

    // Total number of vertices stored in the model.
    property VertexCount: Integer read GetVertexCount;

    // Total number of indices stored in the model.
    property IndexCount: Integer read GetIndexCount;

    // Channel associated with vertex buffer format.
    property Channel: Cardinal read GetChannel;

    // Integrated vertex buffer.
    property VertexBuffer: TBuffer read GetVertexBuffer;

    // Integrated index buffer.
    property IndexBuffer: TBuffer read GetIndexBuffer;
  end;

  // Helper module for TMeshModel.
  TMeshModelHelper = record helper for TMeshModel
  public
    // Takes away the internal contents from another 3D mesh model container without doing any copying.
    procedure TakeAway(const AMeshModel: TMeshModel);
  end;

  // Enumerator for portions in 3D mesh meta-tags.
  TMeshMetaTagPortionEnumerator = record
  private
    FHandle: TUntypedObjectHandle;
    FCurrent: Integer;
    function GetCurrent: TMeshMetaTagPortion; inline;
  public
    // Creates new 3D mesh meta-tag portion enumerator.
    class function Create(const AHandle: TUntypedObjectHandle): TMeshMetaTagPortionEnumerator; static; inline;

    // Moves enumerator to the next element.
    function MoveNext: Boolean; inline;

    // Current enumerator's element.
    property Current: TMeshMetaTagPortion read GetCurrent;
  end;

  // Pointer to @link(TMeshMetaTag).
  PMeshMetaTag = ^TMeshMetaTag;

  // Meta-tag, which describes a certain important axis-aligned bounding area inside a mesh.
  TMeshMetaTag = record
  private
    FHandle: TUntypedObjectHandle;
    function GetName: Utf8String;
    function GetTagType: TMeshMetaTagType; inline;
    function GetPortionCount: Integer; inline;
    function GetPortion(const AIndex: Integer): TMeshMetaTagPortion; inline;
  public
    // Adds a new portion to the tag.
    procedure Add(const APortion: TMeshMetaTagPortion); inline;

    // Erases a portion with the given index.
    procedure Erase(const APortionIndex: Integer); inline;

    // Removes all existing portions.
    procedure Clear; inline;

    // Removes all existing portions.
    procedure Copy(const ATag: TMeshMetaTag); inline;

    // Calculates and returns boundaries for all existing tag's portions, if such exist.
    procedure GetBounds(out ABoundsMin, ABoundsMax: TVector3f);

    // Returns enumerator for tag portions.
    function GetEnumerator: TMeshMetaTagPortionEnumerator; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Unique name of te tag (case-sensitive).
    property Name: Utf8String read GetName;

    // Returns type of the tag.
    property TagType: TMeshMetaTagType read GetTagType;

    // Returns number of existing portions associated with the tag.
    property PortionCount: Integer read GetPortionCount;

    // Returns a portion that corresponds to the tag with a given index.
    property Portion[const AIndex: Integer]: TMeshMetaTagPortion read GetPortion; default;
  end;

  // Enumerator for 3D mesh meta-tag container.
  TMeshMetaTagsEnumerator = record
  private
    FHandle: TUntypedObjectHandle;
    FCurrent: Integer;
    function GetCurrent: TMeshMetaTag; inline;
  public
    // Creates new 3D mesh meta-tag container enumerator.
    class function Create(const AHandle: TUntypedObjectHandle): TMeshMetaTagsEnumerator; static; inline;

    // Moves enumerator to the next element.
    function MoveNext: Boolean; inline;

    // Current enumerator's element.
    property Current: TMeshMetaTag read GetCurrent;
  end;

  // Pointer to @link(TMeshMetaTags).
  PMeshMetaTags = ^TMeshMetaTags;

  // List of meta-tags that describe sub-meshes inside a 3D mesh.
  TMeshMetaTags = record
  private
    FHandle: TUntypedObjectHandle;
    function GetCount: Integer; inline;
    function GetTags(const AIndex: Integer): TMeshMetaTag; inline;
  public
    // Creates a new instance of meta-tags that describe important axis-aligned bounding areas inside a 3D mesh.
    class function Create: TMeshMetaTags; static; inline;

    // Releases the given mesh meta tag container instance.
    procedure Free; inline;

    // Returns a particular tag with the given name (case-sensitive) and type.
    function GetTag(const AName: Utf8String; const ATagType: TMeshMetaTagType): TMeshMetaTag; inline;

    // Creates a new tag with the given type.
    function Add(const AName: Utf8String;
      const ATagType: TMeshMetaTagType = TMeshMetaTagType.Indeterminate): TMeshMetaTag; overload; inline;

    // Removes an existing list from container list.
    procedure Erase(const AIndex: Integer); inline;

    // Removes all existing tags from container list.
    procedure Clear; inline;

    // Returns enumerator for container list.
    function GetEnumerator: TMeshMetaTagsEnumerator; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Number of existing tags.
    property Count: Integer read GetCount;

    // Returns a particular tag with the given index.
    property Tags[const AIndex: Integer]: TMeshMetaTag read GetTags;
  end;

  // Helper module for TMeshMetaTag.
  TMeshMetaTagHelper = record helper for TMeshMetaTag
  private
    function GetParent: TMeshMetaTags; inline;
  public
    // Mesh meta tag's parent container.
    property Parent: TMeshMetaTags read GetParent;
  end;

  // Helper module for TMeshMetaTags.
  TMeshMetaTagsHelper = record helper for TMeshMetaTags
  public
    // Copies contents of another list into the current one, overwriting existing contents.
    procedure Copy(const ATags: TMeshMetaTags); inline;

    // Takes away the internal contents from another tags container without doing any copying.
    procedure TakeAway(const ATags: TMeshMetaTags); inline;
  end;

  // Enumerator for portions in 3D scene mesh material.
  TSceneMeshMaterialRangeEnumerator = record
  private
    FHandle: TUntypedObjectHandle;
    FCurrent: Integer;
    function GetCurrent: TSceneMeshMaterialRange; inline;
  public
    // Creates new 3D scene mesh material range  enumerator.
    class function Create(
      const AHandle: TUntypedObjectHandle): TSceneMeshMaterialRangeEnumerator; static; inline;

    // Moves enumerator to the next element.
    function MoveNext: Boolean; inline;

    // Current enumerator's element.
    property Current: TSceneMeshMaterialRange read GetCurrent;
  end;

  // Material that describes how a portion of a scene mesh geometry should look like.
  TSceneMeshMaterial = record
  private
    FHandle: TUntypedObjectHandle;
    function GetName: Utf8String;
    function GetShading: TSceneMeshMaterialShading; inline;
    procedure SetShading(const AShading: TSceneMeshMaterialShading); inline;
    function GetTexture(const AType: TSceneMeshTexture): TTexture; inline;
    procedure SetTexture(const AType: TSceneMeshTexture; const ATexture: TTexture); inline;
    function GetRangeCount: Integer; inline;
    function GetRange(const AIndex: Integer): TSceneMeshMaterialRange; inline;
    procedure SetRange(const AIndex: Integer; const ARange: TSceneMeshMaterialRange); inline;
  public
    // Releases existing textures.
    procedure ReleaseTextures; inline;

    // Adds a given range to the list of existing ranges and returns its index.
    function AddRange(const ARange: TSceneMeshMaterialRange): Integer; inline;

    // Removes all existing ranges.
    procedure ClearRanges; inline;

    // Converts existing "scratch" textures into hardware-based textures.
    procedure Commit(const ADevice: TDevice; const AAttributes: TTextureAttributes =
      TTextureAttribute.MipMapping); inline;

    // Copies contents from another material. In case of textures, creates new instances of textures and
    // copies the contents of textures from the source.
    procedure Copy(const AMaterial: TSceneMeshMaterial); inline;

    // Returns enumerator for material ranges.
    function GetEnumerator: TSceneMeshMaterialRangeEnumerator; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Returns material name.
    property Name: Utf8String read GetName;

    // Returns shading parameters of the material.
    property Shading: TSceneMeshMaterialShading read GetShading write SetShading;

    // Returns an existing texture associated with the given type or @nil if such does not exist, or
    // changes texture of the given type. Note: material will take ownership of the given texture instance
    // and reliquish ownership of instance that was previously assigned (if it is not @nil).
    property Texture[const AType: TSceneMeshTexture]: TTexture read GetTexture write SetTexture;

    // Returns number of existing ranges.
    property RangeCount: Integer read GetRangeCount;

    // Returns or changes a range with the given index.
    property Range[const AIndex: Integer]: TSceneMeshMaterialRange read GetRange write SetRange; default;
  end;

  // Enumerator for 3D scene mesh materials.
  TSceneMeshMaterialsEnumerator = record
  private
    FHandle: TUntypedObjectHandle;
    FCurrent: Integer;
    function GetCurrent: TSceneMeshMaterial; inline;
  public
    // Creates a new 3D scene mesh materials enumerator.
    class function Create(const AHandle: TUntypedObjectHandle): TSceneMeshMaterialsEnumerator; static; inline;

    // Moves enumerator to the next element.
    function MoveNext: Boolean; inline;

    // Current enumerator's element.
    property Current: TSceneMeshMaterial read GetCurrent;
  end;

  // Container for scene mesh materials that describe the appearance of the geometry.
  TSceneMeshMaterials = record
  private
    FHandle: TUntypedObjectHandle;
    function GetName: Utf8String; inline;
    procedure SetName(const AName: Utf8String); inline;
    function GetCount: Integer; inline;
    function GetMaterial(const AIndex: Integer): TSceneMeshMaterial; inline;
    function GetTexturing: Boolean; inline;
  public
    // Creates a new instance of a scene mesh materials.
    class function Create: TSceneMeshMaterials; static; inline;

    // Releases current instance of scene mesh materials.
    procedure Free; inline;

    // Adds a new material to the scene mesh and returns its index.
    function Add(const AName: Utf8String): Integer; inline;

    // Removes a material with the given index.
    procedure Erase(const AIndex: Integer); inline;

    // Removes and releases all existing materials.
    procedure Clear; inline;

    // Removes and releases all existing materials.
    procedure Commit(const ADevice: TDevice; const AAttributes: TTextureAttributes =
      TTextureAttribute.MipMapping); inline;

    // Returns enumerator for contained materials.
    function GetEnumerator: TSceneMeshMaterialsEnumerator; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Returns or changes the name of the material library.
    property Name: Utf8String read GetName write SetName;

    // Returns total number of existing materials.
    property Count: Integer read GetCount;

    // Returns a pointer to an existing material.
    property Material[const AIndex: Integer]: TSceneMeshMaterial read GetMaterial; default;

    /// Reviews existing materials to determine whether any of them use texturing.
    property Texturing: Boolean read GetTexturing;
  end;

  // Helper module for TSceneMeshMaterials.
  TSceneMeshMaterialsHelper = record helper for TSceneMeshMaterials
  public
    // Copies contents from another container.
    procedure Copy(const AMaterials: TSceneMeshMaterials); inline;

    // Takes away the internal contents from another material container without doing any copying.
    procedure TakeAway(const AMaterials: TSceneMeshMaterials); inline;
  end;

  /// A collection of bone joint connections and/or waypoints for the 3D mesh.
  TSceneMeshLatches = record
  private
    FHandle: TUntypedObjectHandle;
    function GetCount: Integer; inline;
    function GetLatch(const AIndex: Integer): TSceneMeshLatch; inline;
    procedure SetLatch(const AIndex: Integer; const AValue: TSceneMeshLatch); inline;
  public
    // Creates a new instance of a scene mesh latches.
    class function Create: TSceneMeshLatches; static; inline;

    // Releases current instance of scene mesh latches.
    procedure Free; inline;

    // Returns an index of a latch with the given name (case-insensitive) index or -1 if it doesn't exist.
    function IndexOf(const AName: Utf8String): Integer; inline;

    // Adds a new latch to the container and returns its index.
    procedure Add(const AName: Utf8String; const AType: TSceneMeshLatchType; const AGroup: Integer;
      const APosition: TVector3f; const AOrientation: TQuaternion); inline;

    // Removes an existing latch with the given index.
    procedure Erase(const AIndex: Integer); inline;

    // Removes and releases all existing latches.
    procedure Clear; inline;

    // Loads a collection of latches from file on disk.
    procedure LoadFromFile(const AFileName: Utf8String); inline;

    // Loads a collection of latches from file in memory.
    procedure LoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal); inline;

    // Loads a collection of latches from file on disk.
    function TryLoadFromFile(const AFileName: Utf8String): Boolean; inline;

    // Loads a collection of latches from file in memory.
    function TryLoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal): Boolean; inline;

    // Saves a collection of latches to file on disk.
    procedure SaveToFile(const AFileName: Utf8String); inline;

    // Saves a collection of latches to file on disk.
    function TrySaveToFile(const AFileName: Utf8String): Boolean; inline;

    // Returns calculated waypoint traveling distance for the given latch group.
    function GetWaypointDistance(const AGroup: Integer = 0): Single; inline;

    // Calculates interpolated position and orientation based on the given distance for the given latch
    // group.
    procedure GetWaypointCouple(out APosition: TVector3f; out AOrientation: TQuaternion;
      const ADistance: Single; const AGroup: Integer = 0); inline;

    // Triggers recalculation of the interpolated waypoint positions.
    // This should be done when one or more latches have been modified.
    procedure InvalidateWaypoints; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Total number of existing latches.
    property Count: Integer read GetCount;

    // Either returns or modifies an existing latch.
    property Latch[const AIndex: Integer]: TSceneMeshLatch read GetLatch write SetLatch; default;
  end;

  // Helper module for TMeshMetaTag.
  TSceneMeshLatchesHelper = record helper for TSceneMeshLatches
  public
    // Copies latches from another collection.
    procedure Copy(const ALatches: TSceneMeshLatches); inline;

    // Takes away the internal contents from another collection without doing any copying.
    procedure TakeAway(const ALatches: TSceneMeshLatches); inline;
  end;

  // Pointer to @link(TMeshBuffer).
  PMeshBuffer = ^TMeshBuffer;

  // Buffer that contains 3D mesh information.
  TMeshBuffer = record
  private
    FHandle: TUntypedObjectHandle;
    function GetVertices: PMeshBufferEntry; inline;
    function GetVertexCount: Integer; inline;
    procedure SetVertexCount(const AVertexCount: Integer); inline;
    function GetIndices: PLongInt; inline;
    function GetIndexCount: Integer; inline;
    procedure SetIndexCount(const AIndexCount: Integer); inline;
    function GetTransform: TMatrix4f; inline;
    procedure SetTransform(const ATransform: TMatrix4f); inline;
  public
    // Creates new instance of 3D mesh buffer.
    class function Create: TMeshBuffer; static; inline;

    // Releases the 3D mesh buffer.
    procedure Free; inline;

    // Creates new instance of 3D mesh model with the given vertex elements and actual data from this buffer.
    function Model(const ADevice: TDevice; const AVertexElements: TVertexElements;
      const AFirstVertex: Integer = 0; const AVertexCount: Integer = 0; const AFirstIndex: Integer = 0;
      const AIndexCount: Integer = 0; const AChannel: Cardinal = 0): TMeshModel; overload;

    // Creates new instance of 3D mesh model with the given vertex elements and actual data from this buffer.
    function Model(const ADevice: TDevice; const AVertexElements: array of TVertexElement;
      const AFirstVertex: Integer = 0; const AVertexCount: Integer = 0; const AFirstIndex: Integer = 0;
      const AIndexCount: Integer = 0; const AChannel: Cardinal = 0): TMeshModel; overload;

    // Creates a 3D superellipse with the given parameters. Longitude's initial and ending angles are
    // specified in [0, 2 * PI] ranges, whereas latitude's initial and ending angles are specified in a
    // different range of [-PI / 2, PI / 2].
    procedure SuperEllipse(const ALongitudeSections, ALatitudeSections: Cardinal; const AOrigin,
      ARadius: TVector3f; const AInitLongitudeAngle, AEndLongitudeAngle, ALongitudeShape, AInitLatitudeAngle,
      AEndLatitudeAngle, ALatitudeShape: Single; const AInitTexCoord, AEndTexCoord: TPoint2f;
      const AColor: TFloatColor; const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D geosphere with the given parameters.
    procedure Geosphere(const ASubdivisions: Integer; const AColor: TFloatColor;
      const AFlatNormals: Boolean = False; const AIndicesClockwise: Boolean = True); inline;

    // Creates a cylinder with the given parameters. Axis vectors define both orientation and size.
    // Initial and ending angles are specified in [0, 2 * PI] range.
    procedure Cylinder(const ARadialSections, AHeightSections: Cardinal; const AOrigin, AHorizAxis,
      AVertAxis, AHeightAxis: TVector3f; const AInitAngle, AEndAngle, AShape: Single; const AInitTexCoord,
      AEndTexCoord: TPoint2f; const ABendAngle, ALateralAngle: Single; const AColor: TFloatColor;
      const AIndicesClockwise: Boolean = True); inline;

    // Creates a simplistic cone with the given parameters in XZ plane with its base at origin.
    procedure Cone(const ASections: Cardinal; const AOrigin: TVector3f; const ARadius, AHeight: Single;
      const AColor: TFloatColor; const AIndicesClockwise: Boolean = True); inline;

    // Creates a disc with the given parameters. Axis vectors define both orientation and size. Initial and
    // ending angles are specified in [0, 2 * PI] range. @italic(AInitRadius) defines initial radius in
    // proportion to overall disc size.
    procedure Disc(const ARadialSections, AInnerSections: Cardinal; const AOrigin, AHorizAxis, AVertAxis,
      ANormal: TVector3f; const AInitAngle, AEndAngle, AShape, AInitRadius: Single; const AInitTexCoord,
      AEndTexCoord: TPoint2f; const ALateralAngle: Single; const AColor: TFloatColor;
      const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D plane with the given parameters. Axis vectors define both orientation and size.
    procedure Plane(const AHorizSections, AVertSections: Cardinal; const AOrigin, AHorizAxis, AVertAxis,
      ANormal: TVector3f; const AInitTexCoord, AEndTexCoord: TPoint2f; const AColor: TFloatColor;
      const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D cube with the given parameters. Axis vectors define both orientation and size.
    procedure Cube(const AHorizSections, AVertSections, ADepthSections: Cardinal; const AOrigin, AHorizAxis,
      AVertAxis, ADepthAxis: TVector3f; const AInitTexCoord, AEndTexCoord: TPoint2f;
      const AColor: TFloatColor; const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D cube with minimal number of vertices and indices. Texture coordinates, normals and
    // tangents are not generated.
    procedure CubeMinimal(const AOrigin, ASize: TVector3f; const AColor: TFloatColor;
      const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D cube with round corners.
    procedure CubeRound(const AOrigin, ASize: TVector3f; const ARoundness: Single; const AColor: TFloatColor;
      const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D torus with the given parameters. Axis vectors define both orientation and size.
    // Initial and ending angles for both inner and outer rings are specified in [0, 2 * PI] range.
    procedure Torus(const AOuterSections, AInnerSections: Cardinal; const AOrigin, AHorizAxis,
      AVertAxis: TVector3f; const AInitOuterAngle, AEndOuterAngle, AOuterShape, AInitInnerAngle,
      AEndInnerAngle, AInnerShape: Single; const AInnerRadius, AInitTexCoord, AEndTexCoord: TPoint2f;
      const ALateralAngle: Single; const AColor: TFloatColor;
      const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D p-q torus knot with the given parameters. Initial and ending angles for both inner and
    // outer rings are specified in [0, 2 * PI] range.
    procedure TorusKnot(const AOuterSections, AInnerSections: Cardinal; const AOrigin: TVector3f;
      const AP, AQ: Integer; const AInitOuterAngle, AEndOuterAngle, AOuterRadius, AInitInnerAngle,
      AEndInnerAngle, AInnerShape: Single; const AInnerRadius, AInitTexCoord, AEndTexCoord: TPoint2f;
      const ALateralAngle: Single; const AColor: TFloatColor;
      const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D supertoroid located in XY plane with the given parameters. Initial and ending angles for
    // both inner and outer rings are specified in [0, 2 * PI] range.
    procedure Supertoroid(const AOuterSections, AInnerSections: Cardinal; const AOrigin: TVector3f;
      const AInitOuterAngle, AEndOuterAngle, AOuterRadius, AOuterShape, AInitInnerAngle, AEndInnerAngle,
      AInnerRadius, AInnerShape: Single; const AInitTexCoord, AEndTexCoord: TPoint2f;
      const AColor: TFloatColor; const AIndicesClockwise: Boolean = True); inline;

    // Creates a 3D volume of a view frustum from the given view/projection matrix.
    procedure FrustumVolume(const AViewProjectionInverse: TMatrix4f;
      const ADepthClipNegative: Boolean = False); inline;

    // Creates an extruded mesh around the given path. The mesh is colored with a gradient of four colors.
    // That is, "colors" parameter is an array of four colors each corresponding to top/left, top/right,
    // bottom/right and bottom/left corners.
    procedure Extrusion(const APath: array of TPathElement; const ADepth: Single; const ATexCoords: TPoint2f;
      const AColors: TFloatColorRect; const AQuality: Boolean = True); overload;

    // Creates an extruded mesh around the given path. The mesh is colored with a gradient of four colors.
    // That is, "colors" parameter is an array of four colors each corresponding to top/left, top/right,
    // bottom/right and bottom/left corners.
    procedure Extrusion(const APath: TPathElementArray; const ADepth: Single; const ATexCoords: TPoint2f;
      const AColors: TFloatColorRect; const AQuality: Boolean = True); overload;

    // Applies current transformation to the vertices.
    procedure TransformVertices(const AFirstVertex: Integer = 0; const AVertexCount: Integer = 0); inline;

    // Recalculates mesh normals for the given range of indices by using normals of triangles.
    // This rebuilds vertex and index lists entirely.
    procedure CalculateFlatNormals(const AEpsilon: Single = VectorEpsilon); inline;

    // Recalculates mesh normals for the given range of indices by calculating face normals first and then
    // averaging the resulting values to face vertices.
    procedure CalculateNormals(const AFirstVertex: Integer = 0; const AVertexCount: Integer = 0;
      const AFirstIndex: Integer = 0; const AIndexCount: Integer = 0); inline;

    // Recalculates mesh normals for the given range of indices. Vertices that have their positions almost
    // the same (depends on @italic(AWeldEpsilon)) will be considered duplicated and thus will receive
    // weighted normals from neighbor faces as if it would be the same shared vertex.
    procedure CalculateNormalsWeld(const AFirstVertex: Integer = 0; const AVertexCount: Integer = 0;
      const AFirstIndex: Integer = 0; const AIndexCount: Integer = 0;
      const AWeldEpsilon: Single = VectorEpsilon); inline;

    // Invert the normals for vertices in the given range. If vertex count is not specified, then the
    // remaining vertices starting at the given vertex and up to the end of the list are processed.
    procedure InvertNormals(const AFirstVertex: Integer = 0; const AVertexCount: Integer = 0); inline;

    // Inverts order of indices (e.g. from clockwise to counter-clockwise and vice-versa).
    procedure InvertIndexOrder(const AFirstIndex: Integer = 0; const AIndexCount: Integer = 0); inline;

    // Calculates minimum and maximum vertex coordinate boundaries in the given range of mesh.
    procedure CalculateBounds(var AVertexMin, AVertexMax: TVector3f; const AFirstVertex: Integer = 0;
      const AVertexCount: Integer = 0); inline;

    // Calculates coordinate boundaries and centralizes the vertices in the given range of mesh.
    procedure Centralize(const AFirstVertex: Integer = 0; const AVertexCount: Integer = 0); inline;

    // Processes given range of indices, marking vertices that are used and then eliminating vertices that
    // were left unused, updating the entire array of indices.
    procedure EliminateUnusedVertices(const AFirstVertex: Integer = 0;
      const AVertexCount: Integer = 0); inline;

    // Joins vertices that have duplicate positions, averaging their normals, tangents and colors.
    // This has complexity of O(n^2), where n is number of vertices to process.
    procedure JoinDuplicateVertices(const AFirstVertex: Integer = 0; const AVertexCount: Integer = 0;
      const ATreshold: Single = VectorEpsilon); inline;

    // Includes vertices and indices from source mesh buffer into the current one.
    procedure Combine(const AMeshBufferSource: TUntypedObjectHandle; const AFirstVertex: Integer = 0;
      const AVertexCount: Integer = 0; const AFirstIndex: Integer = 0;
      const AIndexCount: Integer = 0); inline;

    // Clears all arrays.
    procedure Clear; inline;

    // Transfers mesh vertex elements to an interleaved system buffer with the given format at the specified
    // channel. Returns number of bytes that the resulting data occupies. If @italic(ABuffer) is @nil, this
    // function only calculates how many bytes the data will occupy.
    procedure TransferVertices(const ABuffer: Pointer; const AVertexElements: TVertexElements;
      const AFirstVertex, AVertexCount: Integer; const AChannel: Cardinal = 0;
      const ASemanticIndex: Integer = 0); overload;

    // Transfers mesh vertex elements to an interleaved system buffer with the given format at the specified
    // channel. Returns number of bytes that the resulting data occupies. If @italic(ABuffer) is @nil, this
    // function only calculates how many bytes the data will occupy.
    procedure TransferVertices(const ABuffer: Pointer; const AVertexElements: array of TVertexElement;
      const AFirstVertex, AVertexCount: Integer; const AChannel: Cardinal = 0;
      const ASemanticIndex: Integer = 0); overload;

    // Transfers mesh vertex elements to an interleaved vertex buffer object with the given format at the
    // specified channel and offset.
    procedure TransferVertices(const ABuffer: TBuffer; const AVertexElements: TVertexElements;
      const AFirstVertex, AVertexCount: Integer; const AChannel: Cardinal = 0; const AOffset: Cardinal = 0;
      const ASemanticIndex: Integer = 0); overload;

    // Transfers mesh vertex elements to an interleaved vertex buffer object with the given format at the
    // specified channel and offset.
    procedure TransferVertices(const ABuffer: TBuffer;
      const AVertexElements: array of TVertexElement; const AFirstVertex, AVertexCount: Integer;
      const AChannel: Cardinal = 0; const AOffset: Cardinal = 0;
      const ASemanticIndex: Integer = 0); overload;

    // Transfers mesh indices to the specified index buffer. The buffer data type is determined by its pitch.
    // Returns number of bytes that the resulting data occupies. If @italic(ABuffer) is @nil, this function
    // only calculates how many bytes the data will occupy.
    procedure TransferIndices(const ABuffer: Pointer; const APitch: Cardinal; const AFirstVertex,
      AFirstIndex, AIndexCount: Integer); overload; inline;

    // Transfers mesh index elements to an index buffer object with the given format at the specified offset.
    procedure TransferIndices(const ABuffer: TBuffer; const AFirstVertex, AFirstIndex, AIndexCount: Integer;
      const AOffset: Cardinal = 0); overload; inline;

    // Natively loads Wavefront OBJ mesh file format (or a proprietary mesh binary format) from disk to mesh
    // buffer.
    procedure LoadFromFile(const AFileName: Utf8String; var AOptions: Cardinal;
      const AFeedback: TMeshLoadSaveFeedback = nil; const AFeedbackUser: Pointer = nil); overload;

    // Natively loads Wavefront OBJ mesh file format (or a proprietary mesh binary format) from disk to mesh
    // buffer. Additionally, loads material and meta-tag information that describe sub-mesh objects.
    procedure LoadFromFile(const AFileName: Utf8String; const AMaterials: TSceneMeshMaterials;
      const AMeshMetaTags: TMeshMetaTags; var AOptions: Cardinal;
      const AFeedback: TMeshLoadSaveFeedback = nil; const AFeedbackUser: Pointer = nil); overload;

    // Natively loads Wavefront OBJ mesh file format (or a proprietary mesh binary format) from disk to mesh
    // buffer. In case of error, returns false and an error message string, instead of raising an exception.
    function TryLoadFromFile(const AFileName: Utf8String; out ADebug: Utf8String; var AOptions: Cardinal;
      const AFeedback: TMeshLoadSaveFeedback = nil; const AFeedbackUser: Pointer = nil): Boolean; overload;

    // Natively loads Wavefront OBJ mesh file format (or a proprietary mesh binary format) from disk to mesh
    // buffer. Additionally, loads material and meta-tag information that describe sub-mesh objects. In case
    // of error, returns false and an error message string, instead of raising an exception.
    function TryLoadFromFile(const AFileName: Utf8String; const AMaterials: TSceneMeshMaterials;
      const AMeshMetaTags: TMeshMetaTags; out ADebug: Utf8String; var AOptions: Cardinal;
      const AFeedback: TMeshLoadSaveFeedback = nil; const AFeedbackUser: Pointer = nil): Boolean; overload;

    // Natively saves mesh buffer contents to a Wavefront OBJ mesh file format on disk. Materials and tags
    // are optional and will also be saved, if provided. In case of error, raises an exception.
    procedure SaveToFile(const AFileName: Utf8String; const AMaterials: TSceneMeshMaterials;
      const AMeshMetaTags: TMeshMetaTags; const AOptions: Cardinal = 0;
      const AFeedback: TMeshLoadSaveFeedback = nil; const AFeedbackUser: Pointer = nil); overload;

    // Natively saves mesh buffer contents to a Wavefront OBJ mesh file format on disk. Materials and tags
    // are optional and will also be saved, if provided. In case of error, returns
    // an error message string. This does not raise exception on failure.
    function TrySaveToFile(const AFileName: Utf8String; const AMaterials: TSceneMeshMaterials;
      const AMeshMetaTags: TMeshMetaTags; out ADebug: Utf8String; const AOptions: Cardinal = 0;
      const AFeedback: TMeshLoadSaveFeedback = nil; const AFeedbackUser: Pointer = nil): Boolean; overload;

    // Attempts to load the mesh file from disk using Assimp DLL to mesh buffer and calculates its boundaries.
    procedure LoadFromFileEx(const AFileName: Utf8String; var AMinBounds, AMaxBounds: TVector3f;
      const ANormals: Boolean = True); overload;

    // Attempts to load the mesh file from disk using Assimp DLL to mesh buffer and calculates its boundaries.
    // Additionally, loads meta-tag information describing individual mesh sub-objects.
    procedure LoadFromFileEx(const AMeshMetaTags: TMeshMetaTags; const AFileName: Utf8String; var AMinBounds,
      AMaxBounds: TVector3f; const ANormals: Boolean = True); overload;

    // Attempts to load the mesh file from disk using Assimp DLL to mesh buffer and calculates its boundaries.
    // In case of error, returns an error message string. This does not raise exception on failure.
    function TryLoadFromFileEx(const AFileName: Utf8String; var AMinBounds, AMaxBounds: TVector3f;
      out ADebug: Utf8String; const ANormals: Boolean = True): Boolean; overload;

    // Attempts to load the mesh file from disk using Assimp DLL to mesh buffer and calculates its boundaries.
    // Additionally, loads meta-tag information describing individual mesh sub-objects. In case of error,
    // returns an error message string. This does not raise exception on failure.
    function TryLoadFromFileEx(const AMeshMetaTags: TMeshMetaTags; const AFileName: Utf8String;
      var AMinBounds, AMaxBounds: TVector3f; out ADebug: Utf8String;
      const ANormals: Boolean = True): Boolean; overload;

    // Attempts to save the mesh to file on disk using Assimp DLL from an existing mesh buffer.
    procedure SaveToFileEx(const AFileName: Utf8String; const AOptions: Cardinal = 0); overload;

    // Attempts to save the mesh to file on disk using Assimp DLL from an existing mesh buffer.
    // In case of error, returns an error message string.
    function TrySaveToFileEx(const AFileName: Utf8String; out ADebug: Utf8String;
      const AOptions: Cardinal = 0): Boolean; overload;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Pointer to vertices stored in 3D mesh buffer.
    property Vertices: PMeshBufferEntry read GetVertices;

    // Total number of vertices in 3D mesh buffer.
    property VertexCount: Integer read GetVertexCount write SetVertexCount;

    // Pointer to indices stored in the 3D mesh buffer.
    property Indices: PLongInt read GetIndices;

    // Total number of indices in 3D mesh buffer.
    property IndexCount: Integer read GetIndexCount write SetIndexCount;

    // Currently set transformation matrix.
    property Transform: TMatrix4f read GetTransform write SetTransform;
  end;

  // Helper module for TTextModeller.
  TTextModellerHelper = record helper for TTextModeller
  public
    // Copies the contents of existing 3D buffers into a mesh buffer.
    procedure CopyToMeshBuffer(const AMeshBuffer: TMeshBuffer); inline;
  end;

  // Pointer to @link(TTextureCabinet).
  PTextureContainer = ^TTextureCabinet;

  // A container and management module for drawable textures for rendering and processing 3D scene.
  TTextureCabinet = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetSize: TPoint2i; inline;
    procedure SetSize(const ASize: TPoint2i); inline;
    function GetSamples: Integer; inline;
    function GetDepthStencil: TPixelFormat; inline;
    function GetFidelity: TTextureFidelity; inline;
    function GetAttributes: TTextureCabinetAttributes; inline;
    procedure SetAttributes(const AAttributes: TTextureCabinetAttributes); inline;
    function GetRendering: Boolean; inline;
    function GetToneMappingBloom: TToneMappingBloom; inline;
    procedure SetToneMappingBloom(const AToneMappingBloom: TToneMappingBloom); inline;
    function GetAmbientOcclusion: TAmbientOcclusionParameters; inline;
    procedure SetAmbientOcclusion(const AAmbientOcclusion: TAmbientOcclusionParameters); inline;
    function GetTexture(const AType: TTextureCabinetType): TTexture; inline;
    function GetFinalTexture: TTexture; inline;
  public
    // Creates a new collection of drawable textures for 3D scene rendering.
    class function Create(const ADevice: TDevice; const ASize: TPoint2i;
      const AFidelity: TTextureFidelity = TTextureFidelity.Medium; const ASamples: Integer = 8;
      const ADepthStencil: TPixelFormat = TPixelFormat.Unknown): TTextureCabinet; static; inline;

    // Releases the texture container.
    procedure Free; inline;

    /// Clears the content of textures related to the given pass.
    /// Note: the given color is used only in main/color pass.
    procedure Clear(const APass: TTextureCabinetPass; const ABackground: TFloatColor); overload; inline;

    /// Clears the content of textures related to the given pass.
    procedure Clear(const APass: TTextureCabinetPass); overload; inline;

    // Begins rendering the given pass.
    procedure BeginScene(const APass: TTextureCabinetPass); inline;

    // Ends rendering the pass that was previously started.
    procedure EndScene; inline;

    // Performs processing and filtering of the textures according to the filter type. For occlusion,
    // performs depth linearization and computes ambient occlusion. For bloom, downscales the color texture,
    // applies blur to compute glare, combines the effects and then performs HDR tone-mapping.
    // For order-independent transparency (OIT or "glassy") pass, performs compositioning of the textures.
    // This step must be performed outside of @link(BeginScene) and @link(EndScene) block after finishing
    // each corresponding rendering pass but before starting next one. For performance reasons,
    // the filtering should be performed as late as possible after finishing previous pass (e.g. after
    // doing some CPU calculations) to enable GPU to do work in parallel. Note: OIT rendering pass does not
    // require projection matrix.
    procedure Filter(const AFilter: TTextureCabinetFilterType;
      const AProjection: TMatrix4f); overload; inline;

    // Performs processing and filtering of the textures according to the filter type. For occlusion,
    // performs depth linearization and computes ambient occlusion. For bloom, downscales the color texture,
    // applies blur to compute glare, combines the effects and then performs HDR tone-mapping.
    // For order-independent transparency (OIT or "glassy") pass, performs compositioning of the textures.
    // This step must be performed outside of @link(BeginScene) and @link(EndScene) block after finishing
    // each corresponding rendering pass but before starting next one. For performance reasons,
    // the filtering should be performed as late as possible after finishing previous pass (e.g. after
    // doing some CPU calculations) to enable GPU to do work in parallel. Note: OIT rendering pass does not
    // require projection matrix.
    procedure Filter(const AFilter: TTextureCabinetFilterType); overload; inline;

    // Resolves primary rendering target to color texture.
    procedure Resolve; inline;

    // Renders the texture on currently active target surface.
    procedure Present; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the collection.
    property Device: TDevice read GetDevice;

    // The size of the integrated textures.
    property Size: TPoint2i read GetSize write SetSize;

    // Number of samples used in rendering.
    property Samples: Integer read GetSamples;

    // Format used for depth/stencil buffers.
    property DepthStencil: TPixelFormat read GetDepthStencil;

    // Level of fidelity used for choosing integrated pixel formats.
    property Fidelity: TTextureFidelity read GetFidelity;

    // Rendering attributes that affect how scene is being processed.
    property Attributes: TTextureCabinetAttributes read GetAttributes write SetAttributes;

    // Indicates that the rendering is currently taking place (inside @link(BeginScene) / @link(EndScene)
    // block).
    property Rendering: Boolean read GetRendering;

    // Returns texture for the given type.
    property Texture[const AType: TTextureCabinetType]: TTexture read GetTexture;

    // Returns final color texture that can contains the scene.
    property FinalTexture: TTexture read GetFinalTexture;

    // Tone-mapping and bloom parameters.
    property ToneMappingBloom: TToneMappingBloom read GetToneMappingBloom write SetToneMappingBloom;

    // Ambient occlusion parameters.
    property AmbientOcclusion: TAmbientOcclusionParameters read GetAmbientOcclusion write SetAmbientOcclusion;
  end;

  // 3D mesh voxel representation object.
  TMeshVoxel = record
  private
    FHandle: TUntypedObjectHandle;
  public
    // Creates an empty 3D mesh voxel representation.
    class function Create: TMeshVoxel; static; inline;

    // Releases an existing 3D mesh voxel representation.
    procedure Free; inline;

    // Loads an existing 3D mesh voxel representation from a file on disk.
    // Provides information about loaded voxel, including its dimensions, sub-division levels and whether
    // color information is present. In case of failure, raises an exception.
    procedure LoadFromFile(const AFileName: Utf8String; out ADimensions: TVector3iArray;
      out ALevels: Byte; out AColors: Boolean); overload; inline;

    // Loads an existing 3D mesh voxel representation from a file on disk.
    // In case of failure, raises an exception.
    procedure LoadFromFile(const AFileName: Utf8String); overload; inline;

    // Loads an existing 3D mesh voxel representation from a file on disk.
    // Provides information about loaded voxel, including its dimensions, sub-division levels and whether
    // color information is present. Instead of raising exceptions, this returns a boolean value indicating
    // success or failure of the operation.
    function TryLoadFromFile(const AFileName: Utf8String; out ADimensions: TVector3iArray;
      out ALevels: Byte; out AColors: Boolean): Boolean; overload; inline;

    // Loads an existing 3D mesh voxel representation from a file on disk. Instead of raising exceptions,
    // this returns a boolean value indicating success or failure of the operation.
    function TryLoadFromFile(const AFileName: Utf8String): Boolean; overload; inline;

    // Loads an existing 3D mesh voxel representation directly from data in memory.
    // Provides information about loaded voxel, including its dimensions, sub-division levels and whether
    // color information is present. In case of failure, raises an exception.
    procedure LoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal;
      out ADimensions: TVector3iArray; out ALevels: Byte; out AColors: Boolean); overload; inline;

    // Loads an existing 3D mesh voxel representation directly from data in memory.
    // In case of failure, raises an exception.
    procedure LoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal); overload; inline;

    // Loads an existing 3D mesh voxel representation directly from data in memory.
    // Provides information about loaded voxel, including its dimensions, sub-division levels and whether
    // color information is present. Instead of raising exceptions, this returns a boolean value indicating
    // success or failure of the operation.
    function TryLoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal;
      out ADimensions: TVector3iArray; out ALevels: Byte;
      out AColors: Boolean): Boolean; overload; inline;

    // Loads an existing 3D mesh voxel representation directly from data in memory. Instead of raising
    // exceptions, this returns a boolean value indicating success or failure of the operation.
    function TryLoadFromMemory(const ABuffer: Pointer;
      const ABufferSize: Cardinal): Boolean; overload; inline;

    // Saves 3D mesh voxel representation to a file on disk. In case of failure, raises an exception.
    procedure SaveToFile(const AFileName: Utf8String; const ADimensions: TVector3iArray;
      const ALevels: Byte; const AColors: Boolean); inline;

    // Saves 3D mesh voxel representation to a file on disk. Instead of raising exceptions, this returns
    // a boolean value indicating success or failure of the operation.
    function TrySaveToFile(const AFileName: Utf8String; const ADimensions: TVector3iArray;
      const ALevels: Byte; const AColors: Boolean): Boolean; inline;

    // Calculates existing voxel dimensions, levels and whether colors are present.
    procedure ComputeParameters(out ADimensions: TVector3iArray; out ALevels: Byte;
      out AColors: Boolean); inline;

    // Returns position and size of 3D mesh voxel representation.
    procedure Extents(out APosition, ASize: TVector3f); inline;

    // Visualizes 3D mesh voxel representation by invoking callback function for each cube.
    procedure Visualize(const ALevelMax: Byte; const AVisualizeFunc: TMeshVoxelVisualizeFunc;
      const AUser: Pointer); inline;

    // Performs intersection between voxel representation and ray.
    function Intersect(const ARay: TRay; const AWorld, AView: TMatrix4f; out ADistance: Single;
      const ATestsCount: PLongInt = nil): Boolean; overload; inline;

    // Performs intersection between voxel representation and ray.
    function Intersect(const ARay: TRay; const AWorld, AView: TMatrix4f;
      const ATestsCount: PLongInt = nil): Boolean; overload; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;
  end;

  // Helper module for TMeshVoxel.
  TMeshVoxelHelper = record helper for TMeshVoxel
  public
    // Takes away the internal contents from another 3D mesh voxel representation replacing any existing
    // contents, without doing any copying.
    procedure TakeAway(const AMeshVoxel: TMeshVoxel); inline;
  end;

  // Helper module for TMeshBuffer.
  TMeshBufferHelper = record helper for TMeshBuffer
  public
    // Creates and builds a 3D voxel representation for geometry in the mesh buffer. A special care must be
    // taken to define levels, as using high values may exhaust available RAM resources; typical values
    // should be between 3 and 4.
    function Voxelize(const ALevels: Byte; const AColors: Boolean = False): TMeshVoxel; inline;
  end;

  // A container for a 3D scene mesh model, its optional voxel representation and a bounding volume.
  TSceneMesh = record
  public const
    // A special "undefined" value for an vertex elements index.
    VertexElementIndexUndefined = $FF;
  private
    FHandle: TUntypedObjectHandle;
    function GetName: Utf8String; inline;
    function GetPayload: TObjectPayload; inline;
    function GetModel: TMeshModel; inline;
    function GetVoxel: TMeshVoxel; inline;
    function GetMaterials: TSceneMeshMaterials; inline;
    function GetTags: TMeshMetaTags; inline;
    function GetLatches: TSceneMeshLatches; inline;
    function GetMinBounds: TVector3f; inline;
    function GetMaxBounds: TVector3f; inline;
    function GetScale: Single; inline;
    function GetSize: TVector3f; inline;
    function GetVertexElementsIndex: Byte; inline;
    procedure SetVertexElementsIndex(const AVertexElementsIndex: Byte); inline;
  public
    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Name of the mesh (case-insensitive).
    property Name: Utf8String read GetName;

    // Payload associated with the mesh.
    property Payload: TObjectPayload read GetPayload;

    // Rendereable mesh model.
    property Model: TMeshModel read GetModel;

    // Voxel representation of a 3D mesh.
    property Voxel: TMeshVoxel read GetVoxel;

    // Materials associated with a 3D mesh.
    property Materials: TSceneMeshMaterials read GetMaterials;

    // Meta tags that represent sub-meshes within a 3D mesh.
    property Tags: TMeshMetaTags read GetTags;

    // Bone joint connections and waypoints associated with the 3D mesh.
    property Latches: TSceneMeshLatches read GetLatches;

    // Minimum mesh boundaries.
    property MinBounds: TVector3f read GetMinBounds;

    // Maximum mesh boundaries.
    property MaxBounds: TVector3f read GetMaxBounds;

    // Scale of the mesh.
    property Scale: Single read GetScale;

    // Size of the mesh.
    property Size: TVector3f read GetSize;

    // Changes index of vertex elements that describe the format of mesh vertex buffer.
    // A value of $FF means index is undefined.
    property VertexElementsIndex: Byte read GetVertexElementsIndex write SetVertexElementsIndex;
  end;

  // Enumerator for high-level scene mesh container.
  TSceneMeshesEnumerator = record
  private
    FHandle: TUntypedObjectHandle;
    FCurrent: Integer;
    function GetCurrent: TSceneMesh; inline;
  public
    // Creates new scene meshes enumerator.
    class function Create(const AHandle: TUntypedObjectHandle): TSceneMeshesEnumerator; static; inline;

    // Moves enumerator to the next element.
    function MoveNext: Boolean; inline;

    // Current enumerator's element.
    property Current: TSceneMesh read GetCurrent;
  end;

  // Container for high-level wrappers of rendereable and pickable objects in 3D scene.
  TSceneMeshes = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetCount: Integer; inline;
    function GetMesh(const AIndex: Integer): TSceneMesh; inline;
    function GetMeshByName(const AName: Utf8String): TSceneMesh; inline;
    function GetPayload(const APayload: TObjectPayload): TSceneMesh; inline;
  public
    // Creates a new instance of scene mesh container associated with the given device.
    class function Create(const ADevice: TDevice): TSceneMeshes; static; inline;

    // Releases the container.
    procedure Free; inline;

    // Creates a new 3D scene mesh with the given parameters.
    function Add(const AName: Utf8String; const APayload: TObjectPayload; const AMinBounds,
      AMaxBounds: TVector3f; const AScale: Single = 1.0): TSceneMesh; inline;

    // Creates a new 3D scene mesh, loading its information from the given mesh buffer.
    function AddFromBuffer(const AName: Utf8String; const AMeshBuffer: TMeshBuffer;
      const APayload: TObjectPayload; const AVertexElements: TVertexElements; const ABoundsMin,
      ABoundsMax: TVector3f; const AChannel: Cardinal = 0; const ASemanticIndex: Cardinal = 0;
      const AScale: Single = 1.0): TSceneMesh; overload;

    // Creates a new 3D scene mesh, loading its information from the given mesh buffer.
    function AddFromBuffer(const AName: Utf8String; const AMeshBuffer: TMeshBuffer;
      const APayload: TObjectPayload; const AVertexElements: array of TVertexElement; const ABoundsMin,
      ABoundsMax: TVector3f; const AChannel: Cardinal = 0; const ASemanticIndex: Cardinal = 0;
      const AScale: Single = 1.0): TSceneMesh; overload;

    // Creates a new 3D scene mesh, loading its information from the given mesh buffer.
    function AddFromBuffer(const AName: Utf8String; const AMeshBuffer: TMeshBuffer;
      const APayload: TObjectPayload; const AVertexElements: TVertexElements; const AChannel: Cardinal = 0;
      const ASemanticIndex: Cardinal = 0; const AScale: Single = 1.0): TSceneMesh; overload;

    // Creates a new 3D scene mesh, loading its information from the given mesh buffer.
    function AddFromBuffer(const AName: Utf8String; const AMeshBuffer: TMeshBuffer;
      const APayload: TObjectPayload; const AVertexElements: array of TVertexElement;
      const AChannel: Cardinal = 0; const ASemanticIndex: Cardinal = 0;
      const AScale: Single = 1.0): TSceneMesh; overload;

    // Creates new scene mesh loading its contents from external (Wavefront OBJ or proprietary binary) file.
    // This function also automatically loads accompanying voxel file (same file name, but with ".voxel"
    // extension) if such is present with the same path. In case of failure, returns a mesh with nil handle
    // instead of raising an exception.
    function TryAddFromFile(const AName, AFileName: Utf8String; const APayload: TObjectPayload;
      const AVertexElements: TVertexElements; const AChannel, ASemanticIndex: Cardinal;
      var AOptions: Cardinal; const AScale: Single; out ADebug: Utf8String;
      const AFeedback: TMeshLoadSaveFeedback = nil;
      const AFeedbackUser: Pointer = nil): TSceneMesh; overload;

    // Creates new scene mesh loading its contents from external (Wavefront OBJ or proprietary binary) file. This function also
    // automatically loads accompanying voxel file (same file name, but with ".voxel" extension) if such
    // is present with the same path. In case of failure, returns a mesh with nil handle instead of raising
    // an exception.
    function TryAddFromFile(const AName, AFileName: Utf8String; const APayload: TObjectPayload;
      const AVertexElements: array of TVertexElement; const AChannel, ASemanticIndex: Cardinal;
      var AOptions: Cardinal; const AScale: Single; out ADebug: Utf8String;
      const AFeedback: TMeshLoadSaveFeedback = nil;
      const AFeedbackUser: Pointer = nil): TSceneMesh; overload;

    // Creates new scene mesh loading its contents from external (Wavefront OBJ or proprietary binary) file. This function also
    // automatically loads accompanying voxel file (same file name, but with ".voxel" extension) if such
    // is present with the same path.
    function AddFromFile(const AName, AFileName: Utf8String; const APayload: TObjectPayload;
      const AVertexElements: TVertexElements; const AChannel, ASemanticIndex: Cardinal;
      var AOptions: Cardinal; const AScale: Single = 1.0; const AFeedback: TMeshLoadSaveFeedback = nil;
      const AFeedbackUser: Pointer = nil): TSceneMesh; overload;

    // Creates new scene mesh loading its contents from external (Wavefront OBJ or proprietary binary) file. This function also
    // automatically loads accompanying voxel file (same file name, but with ".voxel" extension) if such
    // is present with the same path.
    function AddFromFile(const AName, AFileName: Utf8String; const APayload: TObjectPayload;
      const AVertexElements: array of TVertexElement; const AChannel, ASemanticIndex: Cardinal;
      var AOptions: Cardinal; const AScale: Single = 1.0; const AFeedback: TMeshLoadSaveFeedback = nil;
      const AFeedbackUser: Pointer = nil): TSceneMesh; overload;

    // Splits the given parent mesh into "dummy" sub-meshes by specific meta-tag type. Each sub-mesh splitted
    // this way will refer to parent and one of its tag accordingly. This enables sharing the same 3D model
    // for rendering, while having customized voxel representation and/or latches for each splitted segment.
    procedure Slice(const AParent: TSceneMesh; const ATagType: TMeshMetaTagType;
      const APrefix: Utf8String); overload; inline;

    // Splits the given parent mesh into "dummy" sub-meshes by specific meta-tag type. Each sub-mesh splitted
    // this way will refer to parent and one of its tag accordingly. This enables sharing the same 3D model
    // for rendering, while having customized voxel representation and/or latches for each splitted segment.
    procedure Slice(const AParent: TSceneMesh;
      const ATagType: TMeshMetaTagType = TMeshMetaTagType.ObjectGroup); overload; inline;

    // Erases the given mesh.
    procedure Erase(const ASceneMesh: TSceneMesh); inline;

    // Removes all existing meshes.
    procedure Clear; inline;

    // Returns mesh with the given name (case-insensitive) or a null object if no mesh with such name exists.
    function TryMesh(const AName: Utf8String): TSceneMesh; inline;

    // Returns a mesh that corresponds to the given payload or a null object if such doesn't exist.
    function TryPayload(const APayload: TObjectPayload): TSceneMesh; inline;

    // Returns enumerator for the container.
    function GetEnumerator: TSceneMeshesEnumerator; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the container.
    property Device: TDevice read GetDevice;

    // Number of existing meshes.
    property Count: Integer read GetCount;

    // Returns mesh with the given index. Note that adding or removing meshes invalidates all previous
    // mesh indices.
    property Meshes[const AIndex: Integer]: TSceneMesh read GetMesh;

    // Returns mesh with the given name (case-insensitive).
    property Mesh[const AName: Utf8String]: TSceneMesh read GetMeshByName; default;

    // Returns a mesh that corresponds to the given payload.
    property Payloads[const APayload: TObjectPayload]: TSceneMesh read GetPayload;
  end;

  // Pointer to @link(TObjectModel).
  PObjectModel = ^TObjectModel;

  // Object that represents a 3D model in the scene.
  TObjectModel = record
  private
    FHandle: TUntypedObjectHandle;
    function GetID: TObjectModelID; inline;
    function GetName: Utf8String; inline;
    procedure SetName(const AName: Utf8String); inline;
    function GetPayload: TObjectPayload; inline;
    function GetDescription: Utf8String; inline;
    procedure SetDescription(const ADescription: Utf8String); inline;
    function GetParent: TObjectModel; inline;
    procedure SetParent(const AParent: TObjectModel); inline;
    function GetVoxel: TMeshVoxel; inline;
    procedure SetVoxel(const AMeshVoxel: TMeshVoxel); inline;
    function GetMesh: TSceneMesh; inline;
    procedure SetMesh(const AMesh: TSceneMesh); inline;
    function GetMeshName: Utf8String; inline;
    procedure SetMeshName(const AName: Utf8String); inline;
    function GetTransform(const ATransform: TModelTransform): TMatrix4f; inline;
    procedure SetTransform(const ATransform: TModelTransform; const AMatrix: TMatrix4f); inline;
    function GetPosition: TVector3f; inline;
    function GetDepthBias: Single; inline;
    procedure SetDepthBias(const ADepthBias: Single); inline;
    function GetAttributes: TModelAttributes; inline;
    procedure SetAttributes(const AAttributes: TModelAttributes); inline;
    function GetOrderIndex: Integer; inline;
    procedure SetOrderIndex(const AOrderIndex: Integer); inline;
    function GetLayers: UInt64; inline;
    procedure SetLayers(const ALayers: UInt64); inline;
    function GetChildCount: Integer; inline;
    function GetChild(const AIndex: Integer): TObjectModel; inline;
    function GetSize: TVector3f; inline;
    procedure SetSize(const ASize: TVector3f); inline;
    function GetAligns: TMeshAligns; inline;
    procedure SetAligns(const AAligns: TMeshAligns); inline;
    function GetMaterial: Integer; inline;
    procedure SetMaterial(const AMaterial: Integer); inline;
    function GetColor: TFloatColor; inline;
    procedure SetColor(const AColor: TFloatColor); inline;
    function GetHighlight: TFloatColor; inline;
    procedure SetHighlight(const AHighlight: TFloatColor); inline;
  public
    // Returns axis-aligned bounding box of the object.
    procedure GetAABB(out ABoxMin, ABoxMax: TVector3f);

    // Connects a local bone joint with the given index to a corresponding joint of the parent.
    procedure ConnectLatches(const ALatchParent, ALatchLocal: Integer); overload; inline;

    // Connects a local bone joint with the given name (case-insensitive) to a corresponding joint of
    // the parent.
    procedure ConnectLatches(const ALatchParentName, ALatchLocalName: Utf8String); overload; inline;

    // Returns indexes of currently connected bone joints.
    procedure GetConnectedLatches(out ALatchParent, ALatchLocal: Integer); inline;

    // Returns calculated waypoint traveling distance for the given latch group.
    function GetWaypointDistance(const AGroup: Integer = 0): Single; inline;

    // Calculates interpolated position and orientation based on the given distance for the given latch group.
    procedure GetLatchWaypointCouple(out APosition: TVector3f; out AOrientation: TQuaternion;
      const ADistance: Single; const AGroup: Integer = 0); overload; inline;

    // Calculates transformation matrix with interpolated position and orientation based on the given
    // distance for the given latch group.
    function GetLatchWaypointCouple(const ADistance: Single;
      const AGroup: Integer = 0): TMatrix4f; overload; inline;

    // Retrieves either local or global transformation matrix for a given latch index.
    function GetLatchTransform(const ALatchIndex: Integer;
      const ALocal: Boolean = False): TMatrix4f; overload; inline;

    // Retrieves either local or global transformation matrix for a latch with the given name.
    function GetLatchTransform(const ALatchName: Utf8String;
      const ALocal: Boolean = False): TMatrix4f; overload; inline;

    // Marks local transform, volume and position as dirty, including children.
    procedure Invalidate; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Object's unique identification number.
    property ID: TObjectModelID read GetID;

    // Object's name (case-insensitive).
    property Name: Utf8String read GetName write SetName;

    // Object model's payload.
    property Payload: TObjectPayload read GetPayload;

    // Object's description.
    property Description: Utf8String read GetDescription write SetDescription;

    // Object model's parent.
    property Parent: TObjectModel read GetParent write SetParent;

    // Object model's voxel representation.
    property Voxel: TMeshVoxel read GetVoxel write SetVoxel;

    // Object's rendereable mesh.
    property Mesh: TSceneMesh read GetMesh write SetMesh;

    // Name of object's rendereable mesh.
    property MeshName: Utf8String read GetMeshName write SetMeshName;

    // Current object model's transforms.
    property Transform[const ATransform: TModelTransform]: TMatrix4f read GetTransform write SetTransform;

    // Current object position in world/view space.
    property Position: TVector3f read GetPosition;

    // Current depth bias, which is added to Z position (in world/view space).
    property DepthBias: Single read GetDepthBias write SetDepthBias;

    // Current object model's attributes.
    property Attributes: TModelAttributes read GetAttributes write SetAttributes;

    // Current object model's priority order index.
    property OrderIndex: Integer read GetOrderIndex write SetOrderIndex;

    // Layers on which the object is visible.
    // Note: If no layer bits are set, then object would appear visible on all layers.
    property Layers: UInt64 read GetLayers write SetLayers;

    // Number of object model's children.
    property ChildCount: Integer read GetChildCount;

    // Returns a particular object model's child with a zero-based index.
    property Child[const AIndex: Integer]: TObjectModel read GetChild; default;

    // Object's size.
    property Size: TVector3f read GetSize write SetSize;

    // Mesh alignments.
    property Aligns: TMeshAligns read GetAligns write SetAligns;

    // Object's material index.
    property Material: Integer read GetMaterial write SetMaterial;

    // Object's color.
    property Color: TFloatColor read GetColor write SetColor;

    // Object's highlight color.
    property Highlight: TFloatColor read GetHighlight write SetHighlight;
  end;

  // Enumerator for high-level scene mesh container.
  TObjectModelViewEnumerator = record
  private
    FHandle: TUntypedObjectHandle;
    FCurrent: Integer;
    function GetCurrent: TObjectModel; inline;
  public
    // Creates new object model's view enumerator.
    class function Create(const AHandle: TUntypedObjectHandle): TObjectModelViewEnumerator; static; inline;

    // Moves enumerator to the next element.
    function MoveNext: Boolean; inline;

    // Current enumerator's element.
    property Current: TObjectModel read GetCurrent;
  end;

  // Pointer to @link(TObjectModels).
  PObjectModels = ^TObjectModels;

  // Pointer to @link(TObjectModelView).
  PObjectModelView = ^TObjectModelView;

  // A container that represents a particular view that watches objects in the world.
  TObjectModelView = record
  private
    FHandle: TUntypedObjectHandle;
    function GetView: TMatrix4f; inline;
    procedure SetView(const AView: TMatrix4f); inline;
    function GetProjection: TMatrix4f; inline;
    function GetViewProjection: TMatrix4f; inline;
    function GetDepthClipNegative: Boolean; inline;
    function GetLayers: UInt64; inline;
    procedure SetLayers(const ALayers: UInt64); inline;
    function GetObjectCount: Integer; inline;
    function GetObject(const AIndex: Integer): TObjectModel; inline;
    function GetObjectsNotCulled: Integer; inline;
    function GetIntersectedRays: Integer; inline;
    function GetIntersectedObjects: Integer; inline;
  public
    // Sets new view's projection matrix.
    procedure SetProjection(const AProjection: TMatrix4f; const ADepthClipNegative: Boolean = False); inline;

    // Marks view that all visible objects must be recalculated.
    procedure UpdateNeeded; inline;

    // Marks view that it needs to be repainted.
    procedure Invalidate; inline;

    // Creates a list of objects for selection and rendering.
    // Returns true if there are any objects visible in the view.
    function Update: Boolean; inline;

    // Sorts the list of objects using a predefined comparison function.
    procedure Sort(const ACompare: TObjectModelViewCompare = TObjectModelViewCompare.Depth); overload; inline;

    // Sorts the list of objects using a custom comparison function.
    procedure Sort(const ACompareFunc: TObjectModelCompareFunc; const AUser: Pointer); overload; inline;

    // Performs object selection either through object's voxel hierarchy if such is present and if not,
    // through ray-volume intersection. All selectable and visible objects are tested and the object with
    // closest intersection is finally chosen. Returns distance from ray's origin to object.
    function Select(const ARay: TRay; out ADistance: Single): TObjectModel; overload; inline;

    // Performs object selection either through object's voxel hierarchy if such is present and if not,
    // through ray-volume intersection. All selectable and visible objects are tested and the object with
    // closest intersection is finally chosen.
    function Select(const ARay: TRay): TObjectModel; overload; inline;

    // Performs object selection either through object's voxel hierarchy if such is present and if not,
    // through ray-volume intersection. All visible objects are tested and the object with closest
    // intersection is finally chosen.
    function SelectAny(const ARay: TRay; out ADistance: Single): TObjectModel; overload; inline;

    // Performs object selection either through object's voxel hierarchy if such is present and if not,
    // through ray-volume intersection. All visible objects are tested and the object with closest
    // intersection is finally chosen.
    function SelectAny(const ARay: TRay): TObjectModel; overload; inline;

    // Returns enumerator for the container.
    function GetEnumerator: TObjectModelViewEnumerator; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // View transformation matrix.
    property View: TMatrix4f read GetView write SetView;

    // Projection transformation matrix.
    property Projection: TMatrix4f read GetProjection;

    // Combined view/projection transformation matrix.
    property ViewProjection: TMatrix4f read GetViewProjection;

    // Indicates whether negative depth ranges (legacy OpenGL style) are used in projection matrix.
    property DepthClipNegative: Boolean read GetDepthClipNegative;

    // Layer bits that define object's visibility.
    property Layers: UInt64 read GetLayers write SetLayers;

    // Number of objects that are within the view.
    property ObjectCount: Integer read GetObjectCount;

    // Provides access to individual objects within the view.
    property Objects[const AIndex: Integer]: TObjectModel read GetObject; default;

    // Number of objects that were not performed cull test, either because they were determined to be
    // fully visible, or because they were marked as hierarchyless.
    property ObjectsNotCulled: Integer read GetObjectsNotCulled;

    // Number of ray vs AABB intersections performed last time \c select() was called.
    property IntersectedRays: Integer read GetIntersectedRays;

    // Number of ray vs object intersections performed last time \c select() was called.
    property IntersectedObjects: Integer read GetIntersectedObjects;
  end;

  // Enumerator for 3D object model container that goes through the collection.
  TObjectModelsEnumerator = record
  private
    FOwner: TUntypedObjectHandle;
    FCurrent: TUntypedObjectHandle;
    FStarted: Boolean;
    function GetCurrent: TObjectModel; inline;
  public
    // Creates an object model's iterator.
    class function Create(
      const AObjectsModelsHandle: TUntypedObjectHandle): TObjectModelsEnumerator; static; inline;

    // Moves enumerator to the next element.
    function MoveNext: Boolean; inline;

    // Current enumerator's element.
    property Current: TObjectModel read GetCurrent;
  end;

  // A collection of objects and their volume representation in 3D world.
  TObjectModels = record
  private
    FHandle: TUntypedObjectHandle;
    function GetMeshes: TSceneMeshes; inline;
    function GetPayload(const APayload: TObjectPayload): TObjectModel; inline;
    function GetObject(const AName: Utf8String): TObjectModel; inline;
    function GetObjectByID(const AID: TObjectModelID): TObjectModel; inline;
    function GetObjectCount: Integer; inline;
  public
    // Creates a new 3D object models collection.
    class function Create(const AMeshes: TSceneMeshes): TObjectModels; static; inline;

    // Releases an existing 3D object models collection.
    procedure Free; inline;

    // Creates a new object with a given name (case-insensitive) and/or payload association.
    function Add(const AName: Utf8String = '';
      const APayload: TObjectPayload = nil; const AAttributes: TModelAttributes =
      [TModelAttribute.Visible, TModelAttribute.Selectable]): TObjectModel; overload; inline;

    // Creates a new object with a given unique identification number, name (case-sensitive) and/or
    // payload association. If the given ID already exists, a new unique one will be assigned to the object.
    // Unique IDs are generated incrementally with roll-over when highest possible ID is reached, so
    // eventually lower order IDs might be reused; the function ensures, however, that a given ID is unique
    // in sense that it is not assigned to another other object.
    function Add(const AID: TObjectModelID; const AName: Utf8String = '';
      const APayload: TObjectPayload = nil; const AAttributes: TModelAttributes =
      [TModelAttribute.Visible, TModelAttribute.Selectable]): TObjectModel; overload; inline;

    // Removes an existing object.
    procedure Erase(const AObjectModel: TObjectModel); inline;

    // Removes all objects.
    procedure Clear;

    // Returns object with a given unique identification number or a null object, if such doesn't exist.
    function TryObject(const AID: TObjectModelID): TObjectModel; overload; inline;

    // Returns object with a given name (case-sensitive) or a null object, if such doesn't exist.
    function TryObject(const AName: Utf8String): TObjectModel; overload; inline;

    // Returns object reference that corresponds to the given payload in 3D object model controller or a null
    // object, if such doesn't exist.
    function TryPayload(const APayload: TObjectPayload): TObjectModel; inline;

    // Tests whether an object with the given ID exists.
    function Exists(const AID: TObjectModelID): Boolean; overload; inline;

    // Tests whether an object with the given name exists.
    function Exists(const AName: Utf8String): Boolean; overload; inline;

    // Tests whether an object with the given payload exists.
    function PayloadExists(const APayload: TObjectPayload): Boolean; overload; inline;

    // Creates a new view associated with the collection.
    function CreateView: TObjectModelView; inline;

    // Removes an existing view.
    procedure EraseView(const AView: TObjectModelView); inline;

    // Removes all existing views.
    procedure ClearViews;

    // Returns enumerator for ordered 3D object model list.
    function GetEnumerator: TObjectModelsEnumerator; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Associated mesh container.
    property Meshes: TSceneMeshes read GetMeshes;

    // Returns object reference that corresponds to the given payload in 3D object model controller.
    property Payload[const APayload: TObjectPayload]: TObjectModel read GetPayload;

    // Returns object with a given name (case-sensitive).
    property Objects[const AName: Utf8String]: TObjectModel read GetObject; default;

    // Returns object with a given unique identification number.
    property ObjectByID[const AID: TObjectModelID]: TObjectModel read GetObjectByID;

    // Returns a total number of existing objects.
    property ObjectCount: Integer read GetObjectCount;
  end;

  // Helper module for TObjectModel.
  TObjectModelHelper = record helper for TObjectModel
  private
    function GetOwner: TObjectModels; inline;
  public
    // Object model's owner container.
    property Owner: TObjectModels read GetOwner;
  end;

  // A source that casts shadows that can be shared among multiple light sources.
  TShadowCaster = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetPosition: TPoint2i; inline;
    function GetSize: TPoint2i; inline;
    function GetViewProjection: TMatrix4f; inline;
    procedure SetViewProjection(const AViewProjection: TMatrix4f); inline;
    function GetRendering: Boolean; inline;
    function GetTexture(const AIndex: Integer): TTexture; inline;
  public
    // Clears the integrated textures.
    procedure Clear; inline;

    // Starts rendering the shadow map.
    procedure BeginScene; inline;

    // Finishes rendering the shadow map.
    procedure EndScene; inline;

    // Performs filtering on the shadow map and accomodates it on the atlas.
    procedure Filter; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the caster.
    property Device: TDevice read GetDevice;

    // Position of the shadow map in parent atlas.
    property Position: TPoint2i read GetPosition;

    // Size of the shadow map.
    property Size: TPoint2i read GetSize;

    // Returns or updates a combined view and projection matrices of the shadow caster.
    property ViewProjection: TMatrix4f read GetViewProjection write SetViewProjection;

    // Indicates whether rendering is taking place.
    property Rendering: Boolean read GetRendering;

    // Shadow map texture (typically depth w/MSAA, primary filtered and secondary filtered).
    property Texture[const AIndex: Integer]: TTexture read GetTexture;
  end;

  // Module that manages shadow maps produced by one or more shadow casters.
  TShadowCastingAtlas = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetTechnique: TTechniqueShadows; inline;
    function GetParameters: TShadowParameters; inline;
    procedure SetParameters(const AParameters: TShadowParameters); inline;
    function GetSize: TPoint2i; inline;
    function GetSamples: Integer; inline;
    function GetPadding: Integer; inline;
    function GetTexture: TTexture; inline;
    function GetCasterCount: Integer; inline;
    function GetCaster(const AIndex: Integer): TShadowCaster; inline;
  public
    // Creates new instance of shadow casting atlas.
    class function Create(const ADevice: TDevice; const ASize: TPoint2i; const ATechnique: TTechniqueShadows;
      const ASamples: Integer = 8; const APadding: Integer = 1): TShadowCastingAtlas; static; inline;

    // Releases an existing shadow casting atlas.
    procedure Free; inline;

    // Creates a new shadow caster with the given texture size. Shared parameter determines whether the
    // caster should share its shadow map textures with others to reduce memory footprint. For "progressive"
    // rendering, where more portions are added to shadow map in next frames, the shadow caster must own its
    // shadow map.
    function Add(const ASize: TPoint2i; const AShared: Boolean = True): TShadowCaster; inline;

    // Removes an existing shadow caster.
    procedure Erase(const ACaster: TShadowCaster); inline;

    // Removes all existing shadow casters.
    procedure Clear; inline;

    // Clears the surface of atlas texture using border color to avoid edge artifacts.
    // This is typically required after shadow parameters have been changed.
    procedure BorderFill; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the atlas.
    property Device: TDevice read GetDevice;

    // Shadow rendering technique.
    property Technique: TTechniqueShadows read GetTechnique;

    // Parameters that define how shadows are rendered.
    property Parameters: TShadowParameters read GetParameters write SetParameters;

    // Size of the shadow-casting atlas.
    property Size: TPoint2i read GetSize;

    // Number of samples used for shadow map multisampling.
    property Samples: Integer read GetSamples;

    // Padding used to accomodate shadow maps.
    property Padding: Integer read GetPadding;

    // Atlas texture containing existing shadow maps.
    property Texture: TTexture read GetTexture;

    // Number of existing shadow casters.
    property CasterCount: Integer read GetCasterCount;

    // Shadow caster for the given index.
    property Casters[const AIndex: Integer]: TShadowCaster read GetCaster; default;
  end;

  // Helper module for TShadowCaster.
  TShadowCasterHelper = record helper for TShadowCaster
  private
    function GetAtlas: TShadowCastingAtlas; inline;
  public
    // Parent atlas container.
    property Atlas: TShadowCastingAtlas read GetAtlas;
  end;

  // Pointer to @link(TScene).
  PScene = ^TScene;

  // 3D rendering scene.
  TScene = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetActiveVertexElements: Integer; inline;
    procedure SetActiveVertexElements(const AActiveVertexElements: Integer); inline;
    function GetWorld: TMatrix4f; inline;
    procedure SetWorld(const AWorld: TMatrix4f); inline;
    function GetView: TMatrix4f; inline;
    procedure SetView(const AView: TMatrix4f); inline;
    function GetProjection: TMatrix4f; inline;
    procedure SetProjection(const AProjection: TMatrix4f); inline;
    function GetInstancesCount: Integer; inline;
    function GetSceneProgram: TProgram; inline;
    function GetRendering: Boolean; inline;
    function GetAttributes: TSceneAttributes; inline;
    procedure SetAttributes(const AAttributes: TSceneAttributes); inline;
    function GetMaterial: TObjectMaterial; inline;
    procedure SetMaterial(const AMaterial: TObjectMaterial); inline;
    function GetToneMappingCoefficients: TVector4f; inline;
    procedure SetToneMappingCoefficients(const ACoefficients: TVector4f); inline;
    function GetParallaxMappingParameters: TParallaxMappingParameters; inline;
    procedure SetParallaxMappingParameters(const AParameters: TParallaxMappingParameters); inline;
    function GetTexture(const AType: TSceneTextureType): TTexture; inline;
    procedure SetTexture(const AType: TSceneTextureType; const ATexture: TTexture); inline;
    function GetSampler(const AType: TSceneSamplerType): TSampler; inline;
    function GetLights: TSceneLights; inline;
    procedure SetLights(const ALights: TSceneLights); inline;
    function GetShadowCastingAtlas: TShadowCastingAtlas; inline;
    procedure SetShadowCastingAtlas(const AShadowCastingAtlas: TShadowCastingAtlas); inline;
    function GetTextureCabinet: TTextureCabinet; inline;
    procedure SetTextureCabinet(const ATextureCabinet: TTextureCabinet); inline;
  public
    // Creates a new instance of depths/normals rendering scene.
    class function CreateDepthNormals(const ADevice: TDevice): TScene; static;

    // Creates a new instance of 3D rendering scene.
    class function CreateModeling(const ADevice: TDevice): TScene; static;

    // Releases the scene module.
    procedure Free; inline;

    // Adds or updates vertex elements declaration for the given index.
    procedure SetVertexElements(const AIndex: Integer; const AVertexElements: TVertexElements); overload;

    // Adds or updates vertex elements declaration for the given index.
    procedure SetVertexElements(const AIndex: Integer;
      const AVertexElements: array of TVertexElement); overload;

    // Adds or updates vertex element declaration to match the one of 2D and 3D text rendering module,
    // associated with the given index.
    procedure SetVertexElementsFromTextModeller(const AIndex: Integer);

    // Returns vertex elements declaration for the given index.
    function GetVertexElements(const AIndex: Integer): TVertexElements;

    // Supplies instance information to the pipeline. The number of instances may not exceed 128.
    procedure Instances(const ATransforms: TMatrix4fArray; const AColors: TFloatColorArray); overload;

    // Supplies instance information to the pipeline. The number of instances may not exceed 128.
    procedure Instances(const ATransforms: array of TMatrix4f;
      const AColors: array of TFloatColor); overload;

    // Supplies instance information to the pipeline. The number of instances may not exceed 128.
    procedure Instances(const ATransforms: PMatrix4f; const AColors: PFloatColor;
      const AInstanceCount: Integer); overload;

    // Activates the appropriate shader program and begins rendering the scene.
    procedure BeginScene; inline;

    // Finishes rendering the scene and deactivates previously activated shader program.
    procedure EndScene; inline;

    // Configures rendering parameters for the scene, retrieves light indices from light container module and
    // updates internal buffers required for rendering the scene. This must be called outside
    // of @link(BeginScene) and @link(EndScene) block, but before starting any rendering. Ambient occlusion
    // texture, shadow casting atlas, view and projection matrices have been assigned prior this call.
    procedure Prepare; inline;

    // Configures rendering parameters for the scene, retrieves light indices from light container module and
    // updates internal buffers required for rendering the scene. This must be called outside
    // of @link(BeginScene) and @link(EndScene) block, but before starting any rendering. Ambient occlusion
    // texture, shadow casting atlas, view and projection matrices have been assigned prior this call.
    function TryPrepare: Boolean; inline;

    // Clears cached buffers and resources in the scene.
    procedure ResetCache; inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given scene.
    property Device: TDevice read GetDevice;

    // Current object World matrix.
    property World: TMatrix4f read GetWorld write SetWorld;

    // Current View matrix.
    property View: TMatrix4f read GetView write SetView;

    // Current Projection matrix.
    property Projection: TMatrix4f read GetProjection write SetProjection;

    // Maximum number of supported instances.
    property InstancesCount: Integer read GetInstancesCount;

    // Currently active shader program.
    property SceneProgram: TProgram read GetSceneProgram;

    // Indicates that the rendering is currently taking place; that is, currently running inside
    // @link(BeginScene) and @link(EndScene) blocks.
    property Rendering: Boolean read GetRendering;

    // Currently active index of vertex elements declaration.
    property ActiveVertexElements: Integer read GetActiveVertexElements write SetActiveVertexElements;

    // Scene's rendering attributes that define what resources are present and how rendering is made.
    property Attributes: TSceneAttributes read GetAttributes write SetAttributes;

    // Scene's currently set material for rendered objects.
    property Material: TObjectMaterial read GetMaterial write SetMaterial;

    // Tone-mapping RGB luma coefficients and maximum allowed level of white.
    property ToneMappingCoefficients: TVector4f read GetToneMappingCoefficients
      write SetToneMappingCoefficients;

    // Parameters that define how Parallax Mapping technique is performed.
    property ParallaxMappingParameters: TParallaxMappingParameters read GetParallaxMappingParameters
      write SetParallaxMappingParameters;

    // Texture of the given type associated with the scene. Note: changing ambient occlusion texture must be
    // done outside of @link(BeginScene) and @link(EndScene) block, but before calling @link(Prepare) function.
    property Texture[const AType: TSceneTextureType]: TTexture read GetTexture write SetTexture;

    // Sampler of the given type associated with the scene.
    property Sampler[const AType: TSceneSamplerType]: TSampler read GetSampler;

    // Lights container module associated with the scene. Note: changing this must be done outside of
    // @link(BeginScene) and @link(EndScene) block, but before calling @link(Prepare) function.
    property Lights: TSceneLights read GetLights write SetLights;

    // Shadow-casting atlas module associated with the scene. Note: changing this must be done outside of
    // @link(BeginScene) and @link(EndScene) block, but before calling @link(Prepare) function.
    property ShadowCastingAtlas: TShadowCastingAtlas read GetShadowCastingAtlas write SetShadowCastingAtlas;

    // Texture cabinet associated with the scene. Note: changing this must be done outside of
    // @link(BeginScene) and @link(EndScene) block, but before calling @link(Prepare) function.
    property TextureCabinet: TTextureCabinet read GetTextureCabinet write SetTextureCabinet;
  end;

  // Helper module for TSceneMesh.
  TSceneMeshHelper = record helper for TSceneMesh
  public
    // Specify the parent mesh and the tag to which this slice would refer to.
    procedure SetSlice(const AParent: TSceneMesh; const ATag: TMeshMetaTag); inline;

    // Returns parent mesh that contains the actual 3D model to be rendered and tag that contains information
    // about what portion of parent mesh should be rendered.
    procedure GetSlice(out AParent: TSceneMesh; out ATag: TMeshMetaTag); overload; inline;

    // Returns parent mesh that contains the actual 3D model to be rendered.
    function GetSlice: TSceneMesh; overload; inline;

    // Issues one or more draw calls for the scene mesh, assigning materials and textures accordingly.
    // If @link(AInstanceCount) is zero, then non-instancing draw calls will be issued.
    // Returns an actual number of draw calls that were issued.
    function AutoDraw(const AScene: TScene; const AMaterial: TObjectMaterial; const AColor: TFloatColor;
      const AOptions: Cardinal = 0; const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles;
      const AInstanceCount: Integer = 0; const AElementCount: Integer = 0; const AFirstIndex: Integer = 0;
      const ABaseVertex: Integer = 0): Integer; overload; inline;

    // Issues one or more draw calls for the scene mesh, assigning materials and textures accordingly.
    // If @link(AInstanceCount) is zero, then non-instancing draw calls will be issued.
    // Returns an actual number of draw calls that were issued.
    function AutoDraw(const AScene: TScene; const AColor: TFloatColor; const AOptions: Cardinal = 0;
      const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles; const AInstanceCount: Integer = 0;
      const AElementCount: Integer = 0; const AFirstIndex: Integer = 0;
      const ABaseVertex: Integer = 0): Integer; overload; inline;

    // Issues one or more draw calls for the scene mesh, assigning materials and textures accordingly.
    // If @link(AInstanceCount) is zero, then non-instancing draw calls will be issued.
    // Returns an actual number of draw calls that were issued.
    function AutoDraw(const AScene: TScene; const AMaterial: TObjectMaterial; const AOptions: Cardinal = 0;
      const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles; const AInstanceCount: Integer = 0;
      const AElementCount: Integer = 0; const AFirstIndex: Integer = 0;
      const ABaseVertex: Integer = 0): Integer; overload; inline;

    // Issues one or more draw calls for the scene mesh, assigning materials and textures accordingly.
    // If @link(AInstanceCount) is zero, then non-instancing draw calls will be issued.
    // Returns an actual number of draw calls that were issued.
    function AutoDraw(const AScene: TScene; const AOptions: Cardinal = 0;
      const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles; const AInstanceCount: Integer = 0;
      const AElementCount: Integer = 0; const AFirstIndex: Integer = 0;
      const ABaseVertex: Integer = 0): Integer; overload; inline;

    // Counts any tentative draw calls that would be issued during an auto-draw operation. This can be used
    // to determine if any portions of the object would be actually rendered.
    function MeasureAutoDraw(const AOptions: Cardinal = 0; const AElementCount: Integer = 0;
      const AFirstIndex: Integer = 0): Integer; inline;

    // Issues one or more draw calls for the scene mesh, assigning materials and textures accordingly.
    // If scene mesh is a slice, then renders the portions of the slice accordingly.
    // If @link(AInstanceCount) is zero, then non-instancing draw calls will be issued.
    // Returns an actual number of draw calls that were issued.
    function AutoDrawSliced(const AScene: TScene; const AMaterial: TObjectMaterial; const AColor: TFloatColor;
      const AOptions: Cardinal = 0; const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles;
      const AInstanceCount: Integer = 0): Integer; overload; inline;

    // Issues one or more draw calls for the scene mesh, assigning materials and textures accordingly.
    // If scene mesh is a slice, then renders the portions of the slice accordingly.
    // If @link(AInstanceCount) is zero, then non-instancing draw calls will be issued.
    // Returns an actual number of draw calls that were issued.
    function AutoDrawSliced(const AScene: TScene; const AMaterial: TObjectMaterial;
      const AOptions: Cardinal = 0; const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles;
      const AInstanceCount: Integer = 0): Integer; overload; inline;

    // Issues one or more draw calls for the scene mesh, assigning materials and textures accordingly.
    // If scene mesh is a slice, then renders the portions of the slice accordingly.
    // If @link(AInstanceCount) is zero, then non-instancing draw calls will be issued.
    // Returns an actual number of draw calls that were issued.
    function AutoDrawSliced(const AScene: TScene; const AColor: TFloatColor; const AOptions: Cardinal = 0;
      const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles;
      const AInstanceCount: Integer = 0): Integer; overload; inline;

    // Issues one or more draw calls for the scene mesh, assigning materials and textures accordingly.
    // If scene mesh is a slice, then renders the portions of the slice accordingly.
    // If @link(AInstanceCount) is zero, then non-instancing draw calls will be issued.
    // Returns an actual number of draw calls that were issued.
    function AutoDrawSliced(const AScene: TScene; const AOptions: Cardinal = 0;
      const ATopology: TPrimitiveTopology = TPrimitiveTopology.Triangles;
      const AInstanceCount: Integer = 0): Integer; overload; inline;

    // Counts any tentative draw calls that would be issued during an auto-draw operation. If scene mesh is
    // a slice, then portions of the slice are processed accordingly. This can be used to determine if any
    // portions of the object would be actually rendered.
    function MeasureAutoDrawSliced(const AOptions: Cardinal = 0): Integer; inline;
  end;

  // Helper module for TObjectModelView.
  TObjectModelViewHelper = record helper for TObjectModelView
  private
    function GetOwner: TObjectModels; inline;
  public
    // Renders objects that are visible in the view according to the provided options.
    procedure AutoDraw(const AScene: TScene; const AMaterials: TObjectMaterials; const AOptions: Cardinal;
      var ADrawCalls: Integer); overload; inline;

    // Renders objects that are visible in the view according to the provided options.
    procedure AutoDraw(const AScene: TScene; const AMaterials: TObjectMaterials;
      const AOptions: Cardinal = 0); overload; inline;

    // Renders objects that are visible in the view according to the provided options.
    procedure AutoDraw(const AScene: TScene; const AOptions: Cardinal;
      var ADrawCalls: Integer); overload; inline;

    // Renders objects that are visible in the view according to the provided options.
    procedure AutoDraw(const AScene: TScene; const AOptions: Cardinal); overload; inline;

    // Performs a simple non-instancing run, counting any tentative draw calls that would be issued.
    // This can be used to determine if any objects would be rendered at all or not to decide whether to
    // perform certain pass or not.
    function MeasureAutoDraw(const AOptions: Cardinal): Integer; inline;

    // Object model view's owner container.
    property Owner: TObjectModels read GetOwner;
  end;

  // 3D ocean semi-infinite wave field rendering module.
  TOceanSimulation = record
  private
    FHandle: TUntypedObjectHandle;
    function GetDevice: TDevice; inline;
    function GetWavesParameters: TOceanWavesParameters; inline;
    procedure SetWavesParameters(const AParameters: TOceanWavesParameters); inline;
    function GetWavesTexture(const AIndex: Integer): TTexture; inline;
    function GetSections: TPoint2i; inline;
    procedure SetSections(const ASections: TPoint2i); inline;
    function GetView: TMatrix4f; inline;
    procedure SetView(const AView: TMatrix4f); inline;
    function GetProjection: TMatrix4f; inline;
    procedure SetProjection(const AProjection: TMatrix4f); inline;
    function GetScale: TVector3f; inline;
    procedure SetScale(const AScale: TVector3f); inline;
    function GetPlane: TVector4f; inline;
    procedure SetPlane(const APlane: TVector4f); inline;
    function GetViewDistance: Single; inline;
    procedure SetViewDistance(const AViewDistance: Single); inline;
    function GetAttributes: TSceneAttributes; inline;
    procedure SetAttributes(const AAttributes: TSceneAttributes); inline;
    function GetMaterial: TOceanMaterial; inline;
    procedure SetMaterial(const AMaterial: TOceanMaterial); inline;
    function GetSamplerShadow: TSampler; inline;
  public
    // Creates a new instance of 3D ocean rendering module.
    class function Create(const ADevice: TDevice): TOceanSimulation; static; inline;

    // Releases the ocean rendering module.
    procedure Free; inline;

    // Executes the simulation of ocean waves and updates internal textures.
    procedure Update(const ALatency: Double); inline;

    /// Renders the simulation to the existing render target.
    procedure Render(const ALinearDepths: TTexture; const ASceneLights: TSceneLights;
      const AAtlas: TShadowCastingAtlas); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Device associated with the given module.
    property Device: TDevice read GetDevice;

    // Ocean waves simulation parameters.
    property WavesParameters: TOceanWavesParameters read GetWavesParameters write SetWavesParameters;

    // An existing ocean waves simulation texture. This is mostly useful for debug purposes.
    property WavesTexture[const AIndex: Integer]: TTexture read GetWavesTexture;

    // Number of sub-divisions in screen-space.
    property Sections: TPoint2i read GetSections write SetSections;

    // View transformation matrix.
    property View: TMatrix4f read GetView write SetView;

    // Projection transformation matrix.
    property Projection: TMatrix4f read GetProjection write SetProjection;

    // Scale of the displacement map.
    property Scale: TVector3f read GetScale write SetScale;

    // Grid's plane equation.
    property Plane: TVector4f read GetPlane write SetPlane;

    // Maximum height-map viewing distance.
    property ViewDistance: Single read GetViewDistance write SetViewDistance;

    // Currently set rendering attributes.
    property Attributes: TSceneAttributes read GetAttributes write SetAttributes;

    // Ocean's material.
    property Material: TOceanMaterial read GetMaterial write SetMaterial;

    // Sampler for reading from shadow map.
    property SamplerShadow: TSampler read GetSamplerShadow;
  end;

  // Pointer to @link(TActorCamera).
  PActorCamera = ^TActorCamera;

  // A camera module that can represent both first-person and third-person views for 3D world exploration
  // and object manipulation.
  TActorCamera = record
  private
    FHandle: TUntypedObjectHandle;
    function GetPosition: TVector3f; inline;
    procedure SetPosition(const APosition: TVector3f); inline;
    function GetRotation: TVector4f; inline;
    procedure SetRotation(const ARotation: TVector4f); inline;
    function GetDistance: Single; inline;
    procedure SetDistance(const ADistance: Single); inline;
    function GetConstraints: TCameraConstraints; inline;
    procedure SetConstraints(const AConstraints: TCameraConstraints); inline;
    function GetView: TMatrix4f; inline;
    function GetQuaternion: TQuaternion; inline;
    procedure SetQuaternion(const AQuaternion: TQuaternion); inline;
    function GetForward: TVector3f; inline;
    function GetRight: TVector3f; inline;
    function GetCeiling: TVector3f; inline;
  public
    // Creates new instance of camera module.
    class function Create(const APosition: TVector3f; const ARotation: TVector3f;
      const ADistance: Single = 100.0): TActorCamera; overload; static; inline;

    // Creates new instance of camera module.
    class function Create(const ADistance: Single = 100.0): TActorCamera; overload; static; inline;

    // Releases the object camera.
    procedure Free; inline;

    // Issue a camera command for rotation/movement. Returns true if camera's position or rotation has
    // changed. For movement, "sense" defines the normal of the movement plane and "position.Z" its altitude.
    // For dragging and rotation, "sense" (if specified) defines sensitivity for X and Y axes respectively
    // (Z is ignored). Projection matrix is only used for movement command.
    function Command(const ACommand: TCameraCommand; const APosition: TVector3f; const ASize: TPoint2f;
      const ASense: TVector3f; const AProjection: TMatrix4f): Boolean; overload; inline;

    // Issue a camera command for rotation/movement. Returns true if camera's position or rotation has
    // changed. For movement, "sense" (if specified) defines the normal of the movement plane and
    // "position.Z" its altitude. For dragging and rotation, "sense" defines sensitivity for X and Y axes
    // respectively (Z is ignored).
    function Command(const ACommand: TCameraCommand; const APosition: TVector3f; const ASize: TPoint2f;
      const ASense: TVector3f): Boolean; overload; inline;

    // Issue a camera command for rotation/movement. Returns true if camera's position or rotation has
    // changed. For movement, "sense" defines the normal of the movement plane and "position.z" its altitude.
    function Command(const ACommand: TCameraCommand; const APosition: TVector3f;
      const ASize: TPoint2f): Boolean; overload; inline;

    // Changes camera's zoom by adjusting its distance and position around the specified point.
    procedure Zoom(const APosition, ASize: TPoint2f; const AProjection: TMatrix4f;
      const ADelta: Single); inline;

    // Adjusts camera position while performing orthographic zoom.
    procedure ZoomOrtho(const APosition, ASize: TPoint2f; const ADistance, AAdjustedDistance: Single;
      const AProjection, AAdjustedProjection: TMatrix4f); inline;

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Position at which the camera is looking at.
    property Position: TVector3f read GetPosition write SetPosition;

    // Rotation that defines overall orientation of the camera.
    property Rotation: TVector4f read GetRotation write SetRotation;

    // The distance between camera's position and the viewer (for third-person view).
    property Distance: Single read GetDistance write SetDistance;

    // Camera position, rotation and distance constraints.
    property Constraints: TCameraConstraints read GetConstraints write SetConstraints;

    // Camera's resulting 3D view matrix.
    property View: TMatrix4f read GetView;

    // Camera's rotation represented as quaternion.
    property Quaternion: TQuaternion read GetQuaternion write SetQuaternion;

    // Returns camera's forward vector (looking direction).
    property Forward: TVector3f read GetForward;

    /// Returns camera's right vector (sideway direction).
    property Right: TVector3f read GetRight;

    /// Returns camera's ceiling vector (up direction).
    property Ceiling: TVector3f read GetCeiling;
  end;

  // Timer that can be used in multimedia interactive applications to ensure fixed processing rate and/or
  // variable processing rate (e.g. as fast as possible) with a fixed-rate counter.
  TTimer = record
  private
    FHandle: TUntypedObjectHandle;
    function GetSpeed: Double; inline;
    procedure SetSpeed(const ASpeed: Double); inline;
    function GetFrameRate: Single; inline;
    function GetLatency: UInt64; inline;
    function GetTimeSlice: Int64; inline;
    function GetSkippedTimeSlices: Int64; inline;
    function GetTokens: Integer; inline;
  public
    // Creates new instance of multimedia timer.
    class function Create: TTimer; overload; static; inline;

    // Releases the multimedia timer.
    procedure Free; inline;

    // Ensures that next processing time slice is reached. Returns whether the method was executed within the
    // correct time slice (in other words, no timer slices have been lost, which could result in stuttering).
    function NextSlice: Boolean; inline;

    // Ensures that next processing time slice is reached, updates latency and calculates average frame rate.
    // Returns whether the method was executed within the correct time slice (in other words, no timer slices
    // have been lost, which could result in stuttering). Updates fixed-rate token counter.
    function UpdateNextSlice: Boolean; inline;

    // Updates latency, calculates average frame rate and updates fixed-rate token counter.
    procedure Update; inline;

    // Resets internal structures of the timer and restarts the timing calculations. This can be useful when
    // a very time-consuming task was executed inside @link(OnTimer) event, that is not part of rendering and
    // was expected to take longer. Normally, it would stall the timer making it think that the processing
    // takes too long or the rendering is too slow; calling this method will tell the timer that it should
    // ignore the situation and prevent the stall.
    procedure Reset; inline;

    // Reduces number of skipped time slices by the given quantity. If the quantity is larger than the actual
    // number of skipped time slices, resets that number to zero.
    procedure TrimSkippedTimeSlices(const ASlicesToTrim: Int64 = High(Int64));

    // Wrapped module's handle.
    property Handle: TUntypedObjectHandle read FHandle;

    // Timer processing speed (in terms of frames per second).
    property Speed: Double read GetSpeed write SetSpeed;

    // Calculated average frame rate in frames per second. This value is updated roughly two times per second
    // and can only be used for informative purposes (e.g. displaying frame rate in the application).
    // For precise real-time indications it is recommended to use @link(Latency).
    property FrameRate: Single read GetFrameRate;

    // Time (in microseconds) calculated between previous and current frames. This can be a direct indication
    // of rendering performance as it tells how much time it took to render (and possibly, process) the frame.
    property Latency: UInt64 read GetLatency;

    // Current time slice.
    property TimeSlice: Int64 read GetTimeSlice;

    // Total number of skipped time slices.
    property SkippedTimeSlices: Int64 read GetSkippedTimeSlices;

    // Extracts existing fixed-rate token counter from the timer.
    property Tokens: Integer read GetTokens;
  end;

  // Basic application event.
  TBasicApplicationEvent = procedure(const AApplication: TApplication) of object;

  // Application creation event.
  TApplicationBooleanEvent = function(const AApplication: TApplication): Boolean of object;

  // Application mouse handling event.
  TApplicationMouseEvent = procedure(const AApplication: TApplication; const AEvent: TMouseEvent;
    const AButton: TMouseButton; const APosition: TPoint2i) of object;

  // Application keyboard handling event.
  TApplicationKeyboardEvent = procedure(const AApplication: TApplication; const AEvent: TKeyEvent;
    const AVirtualKey: Integer; const AKeyCode: Word) of object;

  // Application status change event.
  TApplicationStatusEvent = procedure(const AApplication: TApplication;
    const AEvent: TApplicationEvent) of object;

  // Custom application message handling event.
  TApplicationHookEvent = function(const AApplication: TApplication;
    const AEvent: Pointer): Boolean of object;

  // Application helper module.
  TApplication = class(TBaseClass)
  private
    FOnCreate: TApplicationBooleanEvent;
    FOnDestroy: TBasicApplicationEvent;
    FOnRender: TBasicApplicationEvent;
    FOnMouse: TApplicationMouseEvent;
    FOnKey: TApplicationKeyboardEvent;
    FOnStatus: TApplicationStatusEvent;
    FOnIdle: TApplicationBooleanEvent;
    FOnResize: TBasicApplicationEvent;
    FOnHook: TApplicationHookEvent;

    function GetWindowHandle: TUntypedHandle; inline;
    function GetTitle: Utf8String;
    procedure SetTitle(const ATitle: Utf8String); inline;
    function GetIconTitle: Utf8String;
    procedure SetIconTitle(const AIconTitle: Utf8String); inline;
    function GetExecutablePath: Utf8String;
    function GetWindowRect: TIntRect; inline;
    procedure SetWindowRect(const ARect: TIntRect); inline;
    function GetClientRect: TIntRect; inline;
    function GetMinimalSize: TPoint2i; inline;
    procedure SetMinimalSize(const AMinimalSize: TPoint2i); inline;
    function GetWindowScale: Double; inline;
    function GetWindowState: TApplicationWindowState; inline;
    procedure SetWindowState(const AState: TApplicationWindowState); inline;
    function GetClipboard: Utf8String;
    procedure SetClipboard(const AClipboard: Utf8String); inline;
    function GetCursor: TAppCursor; inline;
    procedure SetCursor(const ACursor: TAppCursor); inline;
    function GetMouseInputCaptured: Boolean; inline;
    procedure InitializeEvents;
  public
    // Creates a new application instance (for Windows platform).
    constructor Create(const AHandleInstance, AHandleIconSmall, AHandleIconBig: TUntypedHandle;
      const AWindowClassName: Utf8String; const ASize: TPoint2i;
      const AState: TApplicationWindowState = TApplicationWindowState.Windowed;
      const APosition: PPoint2i = nil); overload;

    // Creates a new application instance (for Unix-based platforms).
    constructor Create(const ASize: TPoint2i; const AState: TApplicationWindowState =
      TApplicationWindowState.Windowed; const APosition: PPoint2i = nil); overload;

    // Creates a new application instance wrapping an existing handle.
    constructor Create(const AHandle: TUntypedObjectHandle); overload;

    // Releases the application.
    destructor Destroy; override;

    // Sets new application's window client size.
    procedure SetClientSize(const ASize: TPoint2i); inline;

    // Requests the application window to be repainted.
    procedure Invalidate; inline;

    // Executes application's main loop (or raises an exception if there were errors during application
    // create event.
    procedure Execute; inline;

    // Finishes execution of the application.
    procedure Terminate; inline;

    // Converts virtual key code to portable key code. Returns Key::Null if no equivalent is found.
    function TranslateVirtualKey(const AVirtualKey: Integer): TKey; inline;

    // Converts portable key code to virtual key code. Returns 0 if no equivalent is found.
    function ConvertPortableKey(const AKey: TKey): Integer; inline;

    // Tries to capture mouse input for the application window.
    function TryCaptureMouseInput: Boolean; inline;

    // Tries to release mouse input for the application window.
    function TryReleaseMouseInput: Boolean; inline;

    // Captures mouse input for the application window.
    procedure CaptureMouseInput; inline;

    // Releases mouse input for the application window.
    procedure ReleaseMouseInput; inline;

    // Specifies one or more application icons.
    procedure SetIcons(const AIcons: array of TSurface);

    // Changes application icons by loading them from external files.
    // Note: this method will only work while at least one device instance currently exists.
    procedure SetIconsFromFiles(const AIconNames: array of Utf8String);

    /// Shows a file chooser dialog to pick a file for loading or saving. Starting file path, as well as
    /// suggested file name are taken from "filePath". "filters" is a list of one or more filter, specified
    /// by its name and extension wildcard, separated by semicolon. Multiple filters are separated by '|'.
    /// An example filter would be: Wavefront OBj files (*.obj);*.obj|All files (*.*);*.*
    function FileChooserDialog(var AFilePath: Utf8String; const ADialog: TFileChooserDialog;
      const AFilters, ACaption, AAccept, AReject: Utf8String): Boolean;

    // The handle of main application window.
    property WindowHandle: TUntypedHandle read GetWindowHandle;

    // Current application title.
    property Title: Utf8String read GetTitle write SetTitle;

    // Current application's icon title.
    property IconTitle: Utf8String read GetIconTitle write SetIconTitle;

    // Application executable path.
    property ExecutablePath: Utf8String read GetExecutablePath;

    // Current application's window rectangle
    property WindowRect: TIntRect read GetWindowRect write SetWindowRect;

    // Current application's window client rectangle.
    property ClientRect: TIntRect read GetClientRect;

    // Minimal size of application's window (zero means no limit).
    property MinimalSize: TPoint2i read GetMinimalSize write SetMinimalSize;

    // Current application's window scale.
    property WindowScale: Double read GetWindowScale;

    // The current state of application's window.
    property WindowState: TApplicationWindowState read GetWindowState write SetWindowState;

    // Reads or writes text from/to clipboard.
    property Clipboard: Utf8String read GetClipboard write SetClipboard;

    // Change application's cursor.
    property Cursor: TAppCursor read GetCursor write SetCursor;

    // whether the mouse input is currently captured.
    property MouseInputCaptured: Boolean read GetMouseInputCaptured;

    // Event invoked during startup to load application resources. If this function returns  @false,
    // application creation will fail.
    property OnCreate: TApplicationBooleanEvent read FOnCreate write FOnCreate;

    // Event invoked during application shutdown to release the resources.
    property OnDestroy: TBasicApplicationEvent read FOnDestroy write FOnDestroy;

    // Event invoked when application needs to render contents of the window.
    property OnRender: TBasicApplicationEvent read FOnRender write FOnRender;

    // Event invoked when a mouse event has been registered.
    property OnMouse: TApplicationMouseEvent read FOnMouse write FOnMouse;

    // Event invoked when a key event has been registered.
    property OnKey: TApplicationKeyboardEvent read FOnKey write FOnKey;

    // Event invoked when application status changes.
    property OnStatus: TApplicationStatusEvent read FOnStatus write FOnStatus;

    // Event invoked every time the application is idle.
    property OnIdle: TApplicationBooleanEvent read FOnIdle write FOnIdle;

    // Event invoked when application window is resized.
    property OnResize: TBasicApplicationEvent read FOnResize write FOnResize;

    // Event hook invoked to process application events.
    property OnHook: TApplicationHookEvent read FOnHook write FOnHook;
  end;

  // State-sensitive pseudo-random number generator (PRNG).
  TRandomSequence = record
  private
    FState: UInt64;
  public
    // Initializes random number generator state using local time.
    class function Create: TRandomSequence; overload; static; inline;

    // Initializes random number generator state with a given seed to generate a reproducible sequence of
    // random numbers.
    class function Create(const ASeed: UInt64): TRandomSequence; overload; static; inline;

    // Generates next random number in the sequence from the specified context.
    function Raw: Cardinal; inline;

    // Returns 64-bit random number by generating two 32-bit numbers.
    function Raw64: UInt64; inline;

    // Generates a random value in range [0, 1) with 23 bits of precision, using relatively linear
    // distribution.
    function Value: Single; inline;

    // Generates a random value in range [0, 1) with 53 bits of precision, using relatively linear
    // distribution.
    function ValueDouble: Double; inline;

    // Generates random value in range [0, Range) using relatively linear distribution.
    function Ranged(const ARange: Integer): Integer; inline;

    // Generates a random value using Gaussian distribution with mean 0 and standard deviation of 1.
    function Gaussian: Single; inline;

    // Current state of pseudo-random number generator.
    property State: UInt64 read FState write FState;
  end;

implementation

uses
  Classes, Math, PXT.Graphics.Consts;

const
  StringBufferLength = 65536;

{$REGION 'Service Functions'}

{$IF NOT DEFINED(DELPHI_MODERN)}
const
  exAllArithmeticExceptions = [exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision];
{$IFEND}

function InterpretVertexElements(const AVertexElements: TVertexElements): PVertexElement; overload;
begin
  if Length(AVertexElements) > 0 then
    Result := @AVertexElements[0]
  else
    Result := nil;
end;

function InterpretVertexElements(
  const AVertexElements: array of TVertexElement): PVertexElement; overload;
begin
  if Length(AVertexElements) > 0 then
    Result := @AVertexElements[0]
  else
    Result := nil;
end;

{$ENDREGION}
{$REGION 'TRay'}

class function TRay.Create(const APosition, ASurfaceSize: TPoint2f; const AViewInverse,
  AProjection: TMatrix4f): TRay;
begin
  RayCreate(@Result.Origin, @Result.Direction, @APosition, @ASurfaceSize, @AViewInverse, @AProjection);
end;

function TRay.IntersectTriangle(const AVertex1, AVertex2, AVertex3: TVector3f; const ABackFacing: Boolean;
  out AIntersection: TPoint2f; out ADistance: Single): Boolean;
begin
  Result := RayIntersectTriangle(@Origin, @Direction, @AVertex1, @AVertex2, @AVertex3, ABackFacing,
    @AIntersection, @ADistance);
end;

function TRay.IntersectTriangle(const AVertex1, AVertex2, AVertex3: TVector3f;
  const ABackFacing: Boolean): Boolean;
begin
  Result := RayIntersectTriangle(@Origin, @Direction, @AVertex1, @AVertex2, @AVertex3, ABackFacing, nil, nil);
end;

function TRay.IntersectCubeVolume(const AWorld: TMatrix4f; out ADistance: Single): Boolean;
begin
  Result := RayIntersectCubeVolume(@Origin, @Direction, @AWorld, @ADistance);
end;

function TRay.IntersectCubeVolume(const AWorld: TMatrix4f): Boolean;
begin
  Result := RayIntersectCubeVolume(@Origin, @Direction, @AWorld, nil);
end;

function TRay.IntersectPlane(const APlanePoint, APlaneNormal: TVector3f; out AIntersection: TVector3f;
  out ADistance: Single): Boolean;
begin
  Result := RayIntersectPlane(@Origin, @Direction, @APlanePoint, @APlaneNormal, @AIntersection, @ADistance);
end;

function TRay.IntersectPlane(const APlanePoint, APlaneNormal: TVector3f; out AIntersection: TVector3f): Boolean;
begin
  Result := RayIntersectPlane(@Origin, @Direction, @APlanePoint, @APlaneNormal, @AIntersection, nil);
end;

function TRay.IntersectPlane(const APlanePoint, APlaneNormal: TVector3f): Boolean;
begin
  Result := RayIntersectPlane(@Origin, @Direction, @APlanePoint, @APlaneNormal, nil, nil);
end;

{$ENDREGION}
{$REGION 'TVolume'}

class function TVolume.BoundsToMatrixModel(const AMinBounds, AMaxBounds: TVector3f;
  const AMeshAligns: TMeshAligns; const AScale: Single): TMatrix4f;
begin
  MeshBoundsToMatrixModel(@Result, @AMinBounds, @AMaxBounds, @AMeshAligns, AScale);
end;

class function TVolume.BoundsToMatrixVolume(const AMinBounds, AMaxBounds: TVector3f;
  const AMeshAligns: TMeshAligns; const AScale: Single): TMatrix4f;
begin
  MeshBoundsToMatrixVolume(@Result, @AMinBounds, @AMaxBounds, @AMeshAligns, AScale);
end;

class function TVolume.BoundsTagOffset(const AMeshMinBounds, AMeshMaxBounds, ATagMinBounds,
  ATagMaxBounds: TVector3f): TVector3f;
begin
  MeshBoundsTagOffset(@AMeshMinBounds, @AMeshMaxBounds, @ATagMinBounds, @ATagMaxBounds, @Result);
end;

class function TVolume.BoundsToMatrixVolumeTag(const AMeshMinBounds, AMeshMaxBounds, ATagMinBounds,
  ATagMaxBounds: TVector3f; const ASizeBias: Single): TMatrix4f;
begin
  MeshBoundsToMatrixVolumeTag(@AMeshMinBounds, @AMeshMaxBounds, @ATagMinBounds, @ATagMaxBounds, @Result,
    ASizeBias);
end;

class procedure TVolume.NearFarPlanes(const AWorldView: TMatrix4f; out ANearPlane, AFarPlane: Single);
begin
  VolumeCalculateNearFarPlanes(@AWorldView, @ANearPlane, @AFarPlane);
end;

class function TVolume.VisibleFrame(const AWorldViewProjection: TMatrix4f;
  const ASurfaceSize: TPoint2f): TFloatRect;
begin
  VolumeCalculateVisibleFrame(@AWorldViewProjection, @ASurfaceSize, @Result);
end;

{$ENDREGION}
{$REGION 'TBaseClass'}

function TBaseClass.GetInitialized: Boolean;
begin
  Result := FHandle <> nil;
end;

{$ENDREGION}
{$REGION 'TDevice'}

class function TDevice.Create(const ATechnology: TDeviceTechnology;
  const AAttributes: TDeviceAttributes): TDevice;
begin
  SetExceptionMask(exAllArithmeticExceptions);
  Result.FHandle := DeviceCreate(nil, ATechnology, AAttributes);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateGraphicsDevice);
end;

class function TDevice.Create(const AApplication: TApplication; const ATechnology: TDeviceTechnology;
  const AAttributes: TDeviceAttributes): TDevice;
begin
  SetExceptionMask(exAllArithmeticExceptions);
  Result.FHandle := DeviceCreate(AApplication.Handle, ATechnology, AAttributes);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateGraphicsDevice);
end;

class function TDevice.CreateWrapped(const ATechnology: TDeviceTechnology;
  const AAttributes: TDeviceAttributes; const APlatformDevice: Pointer): TDevice;
begin
  SetExceptionMask(exAllArithmeticExceptions);
  Result.FHandle := DeviceCreateWrapped(nil, ATechnology, AAttributes, TUntypedHandle(APlatformDevice));
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateGraphicsWrappedDevice);
end;

class function TDevice.CreateWrapped(const AApplication: TApplication; const ATechnology: TDeviceTechnology;
  const AAttributes: TDeviceAttributes; const APlatformDevice: Pointer): TDevice;
begin
  SetExceptionMask(exAllArithmeticExceptions);
  Result.FHandle := DeviceCreateWrapped(AApplication.Handle, ATechnology, AAttributes,
    TUntypedHandle(APlatformDevice));
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateGraphicsWrappedDevice);
end;

procedure TDevice.Free;
begin
  if FHandle <> nil then
  begin
    DeviceDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TDevice.GetPlatformDevice: Pointer;
begin
  Result := Pointer(DeviceGetPlatformDevice(FHandle));
end;

function TDevice.GetAttributes: TDeviceAttributes;
begin
  Result := DeviceGetAttributes(FHandle);
end;

function TDevice.GetBehavior: Cardinal;
begin
  Result := DeviceGetBehavior(FHandle);
end;

function TDevice.GetLegacyBits: TDeviceLegacyBits;
begin
  Result := DeviceGetLegacyBits(FHandle);
end;

function TDevice.GetTechnology: TDeviceTechnology;
begin
  Result := DeviceGetTechnology(FHandle);
end;

function TDevice.GetDevicePlatform: TDevicePlatform;
begin
  Result := DeviceGetPlatform(FHandle);
end;

function TDevice.GetTechVersion: Cardinal;
begin
  Result := DeviceGetTechVersion(FHandle);
end;

function TDevice.GetTechFeatureVersion: Cardinal;
begin
  Result := DeviceGetTechFeatureVersion(FHandle);
end;

function TDevice.GetRenderingState: TRenderingState;
begin
  DeviceGetRenderingState(FHandle, @Result);
end;

procedure TDevice.SetRenderingState(const ARenderingState: TRenderingState);
begin
  if not DeviceSetRenderingState(FHandle, @ARenderingState) then
    raise EAfterwarpException.Create(SCouldNotUpdateDeviceRenderingState);
end;

function TDevice.GetViewport: TIntRect;
begin
  DeviceGetViewport(FHandle, @Result);
end;

procedure TDevice.SetViewport(const AViewport: TIntRect);
begin
  if not DeviceSetViewport(FHandle, @AViewport) then
    raise EAfterwarpException.Create(SCouldNotUpdateDeviceViewportRectangle);
end;

function TDevice.GetScissor: TIntRect;
begin
  DeviceGetScissor(FHandle, @Result);
end;

procedure TDevice.SetScissor(const AScissor: TIntRect);
begin
  if not DeviceSetScissor(FHandle, @AScissor) then
    raise EAfterwarpException.Create(SCouldNotUpdateDeviceScissorRectangle);
end;

procedure TDevice.ResetCache;
begin
  DeviceResetCache(FHandle);
end;

procedure TDevice.Clear(const ALayers: TClearLayers; const AColor: TFloatColor; const ADepth: Single;
  const AStencil: Cardinal);
begin
  if not DeviceClear(FHandle, Byte(ALayers), @AColor, ADepth, AStencil) then
    raise EAfterwarpException.Create(SFailedClearingRenderingSurface);
end;

function TDevice.VersionCheck(const AMajorVersion, AMinorVersion: Integer): Boolean;
begin
  Result := (Integer(TechFeatureVersion shr 8) > AMajorVersion) or
    ((Integer(TechFeatureVersion shr 8) = AMajorVersion) and
    (Integer((TechFeatureVersion shr 4) and $0F) >= AMinorVersion));
end;

procedure TDevice.MemoryBarrier(const ABarrierBits: TDeviceBarrierBits);
begin
  DeviceMemoryBarrier(FHandle, Byte(ABarrierBits));
end;

{$ENDREGION}
{$REGION 'TSwapChain'}

class function TSwapChain.Create(const ADevice: TDevice; const AWindowHandle: TUntypedHandle;
  const ASize: TPoint2i; const AFormat: TPixelFormat; const ADepthStencil: TPixelFormat;
  const AMultisamples: Integer; const AVSync: Boolean): TSwapChain;
begin
  Result.FHandle := SwapChainCreate(ADevice.Handle, AWindowHandle, @ASize, AFormat, ADepthStencil,
    AMultisamples, AVSync);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateRenderingSwapChain);
end;

procedure TSwapChain.Free;
begin
  if FHandle <> nil then
  begin
    SwapChainDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSwapChain.GetDevice: TDevice;
begin
  Result.FHandle := SwapChainGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TSwapChain.GetWindowHandle: TUntypedHandle;
begin
  Result := SwapChainGetWindowHandle(FHandle);
end;

function TSwapChain.GetWidth: Integer;
begin
  Result := SwapChainGetWidth(FHandle);
end;

function TSwapChain.GetHeight: Integer;
begin
  Result := SwapChainGetHeight(FHandle);
end;

function TSwapChain.GetSize: TPoint2i;
begin
  Result.X := SwapChainGetWidth(FHandle);
  Result.Y := SwapChainGetHeight(FHandle);
end;

function TSwapChain.GetDepthStencil: TPixelFormat;
begin
  Result := SwapChainGetDepthStencil(FHandle);
end;

function TSwapChain.GetFormat: TPixelFormat;
begin
  Result := SwapChainGetFormat(FHandle);
end;

function TSwapChain.GetMultisamples: Integer;
begin
  Result := SwapChainGetMultisamples(FHandle);
end;

function TSwapChain.GetVSync: Boolean;
begin
  Result := SwapChainGetVSync(FHandle);
end;

procedure TSwapChain.Resize(const ASize: TPoint2i);
begin
  if not SwapChainResize(FHandle, @ASize) then
    raise EAfterwarpException.Create(SCouldNotResizeRenderingSwapChain);
end;

procedure TSwapChain.BeginScene;
begin
  if not SwapChainBegin(FHandle) then
    raise EAfterwarpException.Create(SCouldNotStartRenderingToSwapChain);
end;

procedure TSwapChain.EndScene;
begin
  SwapChainEnd(FHandle);
end;

{$ENDREGION}
{$REGION 'TBuffer'}

class function TBuffer.Create(const ADevice: TDevice; const ADataType: TBufferDataType;
  const AAccessType: TBufferAccessType; const ASize: Cardinal; const APitch: Cardinal;
  const AFormat: TPixelFormat; const AInitialData: Pointer): TBuffer;
begin
  Result.FHandle := BufferCreate(ADevice.Handle, ADataType, AAccessType, ASize, APitch, AFormat,
    AInitialData);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateGraphicsBuffer);
end;

procedure TBuffer.Free;
begin
  if FHandle <> nil then
  begin
    BufferDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TBuffer.GetPlatformHandle: Pointer;
begin
  Result := Pointer(BufferGetPlatformHandle(FHandle));
end;

function TBuffer.GetDevice: TDevice;
begin
  Result.FHandle := BufferGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TBuffer.GetDataType: TBufferDataType;
begin
  Result := BufferGetDataType(FHandle);
end;

function TBuffer.GetAccessType: TBufferAccessType;
begin
  Result := BufferGetAccessType(FHandle);
end;

function TBuffer.GetPitch: Cardinal;
begin
  Result := BufferGetPitch(FHandle);
end;

function TBuffer.GetFormat: TPixelFormat;
begin
  Result := BufferGetFormat(FHandle);
end;

function TBuffer.GetSize: Cardinal;
begin
  Result := BufferGetSize(FHandle);
end;

procedure TBuffer.Update(const AData: Pointer; const AOffset, ASize: Cardinal);
begin
  if not BufferUpdate(FHandle, AData, AOffset, ASize) then
    raise EAfterwarpException.Create(SCouldNotUpdateContentsOfGraphicsBuffer);
end;

procedure TBuffer.Retrieve(const AData: Pointer; const AOffset, ASize: Cardinal);
begin
  if not BufferRetrieve(FHandle, AData, AOffset, ASize) then
    raise EAfterwarpException.Create(SCouldNotRetrieveContentsOfGraphicsBuffer);
end;

procedure TBufferHelper.Copy(const ABuffer: TBuffer; const AOffsetDest: Cardinal;
  const AOffsetSource: Cardinal; const ASize: Cardinal);
begin
  if not BufferCopy(FHandle, ABuffer.Handle, AOffsetDest, AOffsetSource, ASize) then
    raise EAfterwarpException.Create(SCouldNotCopyContentsFromAnotherGraphicsBuffer);
end;

{$ENDREGION}
{$REGION 'TProgram'}

function LoadShaderFromFile(const AFileName: string): TBytes;
var
  LStream: TFileStream;
  LByteCount: Integer;
begin
  LStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
  try
    SetLength(Result, LStream.Size);
    LByteCount := LStream.Read(Result[0], LStream.Size);
    SetLength(Result, LByteCount);
  finally
    LStream.Free;
  end;
end;

class function TProgram.Create(const ADevice: TDevice; const AVertexElements: TVertexElements;
  const AProgramElements: TProgramElements; const AProgramVariables: TProgramVariables; const AVertexShader,
  AHullShader, ADomainShader, AGeometryShader, APixelShader: TBytes): TProgram;
var
  LProgramElements: PProgramElement;
  LProgramVariables: PProgramVariable;
  LVertexShader, LHullShader, LDomainShader, LGeometryShader, LPixelShader: PAnsiChar;
begin
  if Length(AProgramElements) > 0 then
    LProgramElements := @AProgramElements[0]
  else
    LProgramElements := nil;

  if Length(AProgramVariables) > 0 then
    LProgramVariables := @AProgramVariables[0]
  else
    LProgramVariables := nil;

  if Length(AVertexShader) > 0 then
    LVertexShader := @AVertexShader[0]
  else
    LVertexShader := nil;

  if Length(AHullShader) > 0 then
    LHullShader := @AHullShader[0]
  else
    LHullShader := nil;

  if Length(ADomainShader) > 0 then
    LDomainShader := @ADomainShader[0]
  else
    LDomainShader := nil;

  if Length(AGeometryShader) > 0 then
    LGeometryShader := @AGeometryShader[0]
  else
    LGeometryShader := nil;

  if Length(APixelShader) > 0 then
    LPixelShader := @APixelShader[0]
  else
    LPixelShader := nil;

  Result.FHandle := ProgramCreate(ADevice.Handle, InterpretVertexElements(AVertexElements),
    Length(AVertexElements), LProgramElements, Length(AProgramElements), LProgramVariables,
    Length(AProgramVariables), LVertexShader, Length(AVertexShader), LHullShader, Length(AHullShader),
    LDomainShader, Length(ADomainShader), LGeometryShader, Length(AGeometryShader), LPixelShader,
    Length(APixelShader));
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateShaderProgram);
end;

class function TProgram.Create(const ADevice: TDevice; const AVertexElements: array of TVertexElement;
  const AProgramElements: array of TProgramElement; const AProgramVariables: array of TProgramVariable;
  const AVertexShader, AHullShader, ADomainShader, AGeometryShader, APixelShader: TBytes): TProgram;
var
  LProgramElements: PProgramElement;
  LProgramVariables: PProgramVariable;
  LVertexShader, LHullShader, LDomainShader, LGeometryShader, LPixelShader: PAnsiChar;
begin
  if Length(AProgramElements) > 0 then
    LProgramElements := @AProgramElements[0]
  else
    LProgramElements := nil;

  if Length(AProgramVariables) > 0 then
    LProgramVariables := @AProgramVariables[0]
  else
    LProgramVariables := nil;

  if Length(AVertexShader) > 0 then
    LVertexShader := @AVertexShader[0]
  else
    LVertexShader := nil;

  if Length(AHullShader) > 0 then
    LHullShader := @AHullShader[0]
  else
    LHullShader := nil;

  if Length(ADomainShader) > 0 then
    LDomainShader := @ADomainShader[0]
  else
    LDomainShader := nil;

  if Length(AGeometryShader) > 0 then
    LGeometryShader := @AGeometryShader[0]
  else
    LGeometryShader := nil;

  if Length(APixelShader) > 0 then
    LPixelShader := @APixelShader[0]
  else
    LPixelShader := nil;

  Result.FHandle := ProgramCreate(ADevice.Handle, InterpretVertexElements(AVertexElements),
    Length(AVertexElements), LProgramElements, Length(AProgramElements), LProgramVariables,
    Length(AProgramVariables), LVertexShader, Length(AVertexShader), LHullShader, Length(AHullShader),
    LDomainShader, Length(ADomainShader), LGeometryShader, Length(AGeometryShader), LPixelShader,
    Length(APixelShader));
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateShaderProgram);
end;

class function TProgram.CreateFromFiles(const ADevice: TDevice; const AVertexElements: TVertexElements;
  const AProgramElements: TProgramElements; const AProgramVariables: TProgramVariables; const AVertexShader,
  AHullShader, ADomainShader, AGeometryShader, APixelShader: string): TProgram;
var
  LVertexShader, LHullShader, LDomainShader, LGeometryShader, LPixelShader: TBytes;
begin
  if Length(AVertexShader) > 0 then
    LVertexShader := LoadShaderFromFile(AVertexShader)
  else
    SetLength(LVertexShader, 0);

  if Length(AHullShader) > 0 then
    LHullShader := LoadShaderFromFile(AHullShader)
  else
    SetLength(LHullShader, 0);

  if Length(ADomainShader) > 0 then
    LDomainShader := LoadShaderFromFile(ADomainShader)
  else
    SetLength(LDomainShader, 0);

  if Length(AGeometryShader) > 0 then
    LGeometryShader := LoadShaderFromFile(AGeometryShader)
  else
    SetLength(LGeometryShader, 0);

  if Length(APixelShader) > 0 then
    LPixelShader := LoadShaderFromFile(APixelShader)
  else
    SetLength(LPixelShader, 0);

  Result := Create(ADevice, AVertexElements, AProgramElements, AProgramVariables, LVertexShader,
    LHullShader, LDomainShader, LGeometryShader, LPixelShader);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateShaderProgram);
end;

class function TProgram.CreateFromFiles(const ADevice: TDevice; const AVertexElements: array of TVertexElement;
  const AProgramElements: array of TProgramElement; const AProgramVariables: array of TProgramVariable;
  const AVertexShader, AHullShader, ADomainShader, AGeometryShader, APixelShader: string): TProgram;
var
  LVertexShader, LHullShader, LDomainShader, LGeometryShader, LPixelShader: TBytes;
begin
  if Length(AVertexShader) > 0 then
    LVertexShader := LoadShaderFromFile(AVertexShader)
  else
    SetLength(LVertexShader, 0);

  if Length(AHullShader) > 0 then
    LHullShader := LoadShaderFromFile(AHullShader)
  else
    SetLength(LHullShader, 0);

  if Length(ADomainShader) > 0 then
    LDomainShader := LoadShaderFromFile(ADomainShader)
  else
    SetLength(LDomainShader, 0);

  if Length(AGeometryShader) > 0 then
    LGeometryShader := LoadShaderFromFile(AGeometryShader)
  else
    SetLength(LGeometryShader, 0);

  if Length(APixelShader) > 0 then
    LPixelShader := LoadShaderFromFile(APixelShader)
  else
    SetLength(LPixelShader, 0);

  Result := Create(ADevice, AVertexElements, AProgramElements, AProgramVariables, LVertexShader,
    LHullShader, LDomainShader, LGeometryShader, LPixelShader);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateShaderProgram);
end;

procedure TProgram.Free;
begin
  if FHandle <> nil then
  begin
    ProgramDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TProgram.GetDevice: TDevice;
begin
  Result.FHandle := ProgramGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

procedure TProgram.SetPatchVertices(const AValue: Integer);
begin
  if not ProgramSetPatchVertices(FHandle, AValue) then
    raise EAfterwarpException.CreateFmt(SCouldNotSetPatchVerticesToShaderProgram, [AValue]);
end;

procedure TProgram.Update(const AVariableIndex: Integer; const AVariableData: Pointer;
  const ASize: Cardinal);
begin
  if not ProgramUpdateByIndex(FHandle, AVariableIndex, AVariableData, ASize) then
    raise EAfterwarpException.CreateFmt(SCouldNotUpdateContentsShaderVariableIndex, [AVariableIndex]);
end;

procedure TProgram.Update(const AVariableName: Utf8String; const AVariableData: Pointer;
  const ASize: Cardinal);
begin
  if not ProgramUpdateByName(FHandle, PAnsiChar(AVariableName), AVariableData, ASize) then
    raise EAfterwarpException.CreateFmt(SCouldNotUpdateContentsShaderVariableName, [AVariableName]);
end;

procedure TProgram.Bind(const ABuffer: TBuffer; const AChannel, AOffset: Cardinal);
begin
  if not ProgramBind(FHandle, ABuffer.Handle, AChannel, AOffset) then
    raise EAfterwarpException.CreateFmt(SCouldNotBindBufferToProgramAtChannel, [AChannel, AOffset]);
end;

procedure TProgram.Unbind(const ABuffer: TBuffer; const AChannel: Cardinal);
begin
  ProgramUnbind(FHandle, ABuffer.Handle, AChannel);
end;

procedure TProgram.ResetBindings;
begin
  ProgramResetBindings(FHandle);
end;

procedure TProgram.ResetCache;
begin
  ProgramResetCache(FHandle);
end;

procedure TProgram.Commit;
begin
  if not ProgramCommit(FHandle) then
    raise EAfterwarpException.Create(SCouldNotCommitChangesToShaderProgram);
end;

procedure TProgram.BeginScene;
begin
  if not ProgramBegin(FHandle) then
    raise EAfterwarpException.Create(SCouldNotBeginRenderingWithShaderProgram);
end;

procedure TProgram.EndScene;
begin
  ProgramEnd(FHandle);
end;

procedure TProgram.Draw(const ATopology: TPrimitiveTopology; const AVertexCount: Cardinal;
  const ABaseVertex: Integer);
begin
  if not ProgramDraw(FHandle, ATopology, AVertexCount, ABaseVertex) then
    raise EAfterwarpException.CreateFmt(SFailedIssuingDrawCallWithTopology, [Ord(ATopology), AVertexCount,
      ABaseVertex]);
end;

procedure TProgram.DrawIndexed(const ATopology: TPrimitiveTopology; const AIndexCount, AStartIndex,
  ABaseVertex: Integer);
begin
  if not ProgramDrawIndexed(FHandle, ATopology, AIndexCount, AStartIndex, ABaseVertex) then
    raise EAfterwarpException.CreateFmt(SFailedIssuingIndexedDrawCallWithTopology, [Ord(ATopology),
      AIndexCount, AStartIndex, ABaseVertex]);
end;

procedure TProgram.DrawInstances(const ATopology: TPrimitiveTopology; const AVertexCount,
  AInstanceCount, ABaseVertex: Integer);
begin
  if not ProgramDrawInstances(FHandle, ATopology, AVertexCount, AInstanceCount, ABaseVertex) then
    raise EAfterwarpException.CreateFmt(SFailedIssuingInstancedDrawCallWithTopology, [Ord(ATopology),
      AInstanceCount, AVertexCount, ABaseVertex]);
end;

procedure TProgram.DrawInstancesIndexed(const ATopology: TPrimitiveTopology; const AIndexCount,
  AInstanceCount, AStartIndex, ABaseVertex: Integer);
begin
  if not ProgramDrawInstancesIndexed(FHandle, ATopology, AIndexCount, AInstanceCount, AStartIndex,
    ABaseVertex) then
    raise EAfterwarpException.CreateFmt(SFailedIssuingInstancedIndexedDrawCallWithTopology, [Ord(ATopology),
      AIndexCount, AInstanceCount, AStartIndex, ABaseVertex]);
end;

{$ENDREGION}
{$REGION 'TComputeProgram'}

class function TComputeProgram.Create(const ADevice: TDevice; const AProgramElements: TProgramElements;
  const AShader: TBytes): TComputeProgram;
var
  LProgramElements: PProgramElement;
  LProgramElementCount: Cardinal;
  LShader: PAnsiChar;
  LShaderLength: Cardinal;
begin
  if Length(AProgramElements) > 0 then
  begin
    LProgramElements := @AProgramElements[0];
    LProgramElementCount := Length(AProgramElements);
  end
  else
  begin
    LProgramElements := nil;
    LProgramElementCount := 0;
  end;

  if Length(AShader) > 0 then
  begin
    LShader := @AShader[0];
    LShaderLength := Length(AShader);
  end
  else
  begin
    LShader := nil;
    LShaderLength := 0;
  end;

  Result.FHandle := ComputeProgramCreate(ADevice.Handle, LProgramElements, LProgramElementCount, LShader,
    LShaderLength);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateComputeShaderProgram);
end;

class function TComputeProgram.Create(const ADevice: TDevice;
  const AProgramElements: array of TProgramElement; const AShader: TBytes): TComputeProgram;
var
  LProgramElements: PProgramElement;
  LProgramElementCount: Cardinal;
  LShader: PAnsiChar;
  LShaderLength: Cardinal;
begin
  if Length(AProgramElements) > 0 then
  begin
    LProgramElements := @AProgramElements[0];
    LProgramElementCount := Length(AProgramElements);
  end
  else
  begin
    LProgramElements := nil;
    LProgramElementCount := 0;
  end;

  if Length(AShader) > 0 then
  begin
    LShader := @AShader[0];
    LShaderLength := Length(AShader);
  end
  else
  begin
    LShader := nil;
    LShaderLength := 0;
  end;

  Result.FHandle := ComputeProgramCreate(ADevice.Handle, LProgramElements, LProgramElementCount, LShader,
    LShaderLength);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateComputeShaderProgram);
end;

class function TComputeProgram.CreateFromFile(const ADevice: TDevice;
  const AProgramElements: TProgramElements; const AShader: string): TComputeProgram;
var
  LShader: TBytes;
begin
  if Length(AShader) > 0 then
    LShader := LoadShaderFromFile(AShader)
  else
    SetLength(LShader, 0);

  Result := Create(ADevice, AProgramElements, LShader);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateComputeShaderProgram);
end;

class function TComputeProgram.CreateFromFile(const ADevice: TDevice;
  const AProgramElements: array of TProgramElement; const AShader: string): TComputeProgram;
var
  LShader: TBytes;
begin
  if Length(AShader) > 0 then
    LShader := LoadShaderFromFile(AShader)
  else
    SetLength(LShader, 0);

  Result := Create(ADevice, AProgramElements, LShader);
end;

procedure TComputeProgram.Free;
begin
  if FHandle <> nil then
  begin
    ComputeProgramDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TComputeProgram.GetDevice: TDevice;
begin
  Result.FHandle := ComputeProgramGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

procedure TComputeProgram.Bind(const ABuffer: TBuffer; const AChannel, AOffset: Cardinal);
begin
  if not ComputeProgramBindBuffer(FHandle, ABuffer.Handle, AChannel, AOffset) then
    raise EAfterwarpException.CreateFmt(SCouldNotBindBufferToComputeProgramAtChannel, [AChannel, AOffset]);
end;

procedure TComputeProgram.Unbind(const ABuffer: TBuffer; const AChannel: Cardinal);
begin
  ComputeProgramUnbindBuffer(FHandle, ABuffer.Handle, AChannel);
end;

procedure TComputeProgram.Bind(const ATexture: PTexture; const ABindFormat: TComputeBindTextureFormat);
begin
  if not ComputeProgramBindTexture(FHandle, ATexture.Handle, @ABindFormat) then
    raise EAfterwarpException.Create(SCouldNotBindTextureToComputeProgram);
end;

procedure TComputeProgram.Unbind(const ATexture: PTexture;
  const ABindFormat: TComputeBindTextureFormat);
begin
  ComputeProgramUnbindTexture(FHandle, ATexture.Handle, @ABindFormat);
end;

procedure TComputeProgram.ResetBindings;
begin
  ComputeProgramResetBindings(FHandle);
end;

procedure TComputeProgram.Commit;
begin
  if not ComputeProgramCommit(FHandle) then
    raise EAfterwarpException.Create(SCouldNotCommitChangesToComputeProgram);
end;

procedure TComputeProgram.BeginScene;
begin
  if not ComputeProgramBegin(FHandle) then
    raise EAfterwarpException.Create(SCouldNotBeginRenderingWithComputeProgram);
end;

procedure TComputeProgram.EndScene;
begin
  ComputeProgramEnd(FHandle);
end;

procedure TComputeProgram.Dispatch(const AGroupsX, AGroupsY, AGroupsZ: Integer);
begin
  if not ComputeProgramDispatch(FHandle, AGroupsX, AGroupsY, AGroupsZ) then
    raise EAfterwarpException.CreateFmt(SCouldNotExecuteComputeProgramWithGroups, [AGroupsX, AGroupsY,
      AGroupsZ]);
end;

{$ENDREGION}
{$REGION 'TSampler'}

class function TSampler.Create(const ADevice: TDevice; const ASamplerState: TSamplerState): TSampler;
begin
  Result.FHandle := SamplerCreate(ADevice.Handle, @ASamplerState);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateTextureSampler);
end;

procedure TSampler.Free;
begin
  if FHandle <> nil then
  begin
    SamplerDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSampler.GetDevice: TDevice;
begin
  Result.FHandle := SamplerGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TSampler.GetState: TSamplerState;
begin
  SamplerGetState(FHandle, @Result);
end;

procedure TSampler.SetState(const AState: TSamplerState);
begin
  if not SamplerSetState(FHandle, @AState) then
    raise EAfterwarpException.Create(SCouldNotUpdateTextureSamplerState);
end;

procedure TSampler.Bind(const AChannel: Cardinal);
begin
  if not SamplerBind(FHandle, AChannel) then
    raise EAfterwarpException.CreateFmt(SCouldNotBindTextureSamplerToChannel, [AChannel]);
end;

procedure TSampler.Unbind(const AChannel: Cardinal);
begin
  SamplerUnbind(FHandle, AChannel);
end;

{$ENDREGION}
{$REGION 'TSurface'}

class function TSurface.Create(const AWidth, AHeight: Integer; const AFormat: TPixelFormat): TSurface;
begin
  Result.FHandle := SurfaceCreate(AWidth, AHeight, AFormat);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateRasterSurface);
end;

class function TSurface.Create(const AFileName: Utf8String;
  const AFormatRequest: TAlphaFormatRequest): TSurface;
begin
  Result.FHandle := SurfaceCreateFromFile(PAnsiChar(AFileName), AFormatRequest);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateRasterSurfaceFromFile, [AFileName]);
end;

class function TSurface.Create(const AFileContent: Pointer; const AContentSize: Cardinal;
  const AExtension: Utf8String; const AFormatRequest: TAlphaFormatRequest): TSurface;
begin
  Result.FHandle := SurfaceCreateFromFileInMemory(AFileContent, AContentSize, PAnsiChar(AExtension),
    AFormatRequest);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateRasterSurfaceFromMemory);
end;

procedure TSurface.Free;
begin
  if FHandle <> nil then
  begin
    SurfaceDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSurface.GetBits: Pointer;
begin
  Result := SurfaceGetBits(FHandle);
end;

function TSurface.GetPitch: Cardinal;
begin
  Result := SurfaceGetPitch(FHandle);
end;

function TSurface.GetBytesPerPixel: Integer;
begin
  Result := SurfaceGetBytesPerPixel(FHandle);
end;

function TSurface.GetWidth: Integer;
begin
  Result := SurfaceGetWidth(FHandle);
end;

function TSurface.GetHeight: Integer;
begin
  Result := SurfaceGetHeight(FHandle);
end;

function TSurface.GetSize: TPoint2i;
begin
  Result := Point2i(GetWidth, GetHeight);
end;

function TSurface.GetBufferSize: Integer;
begin
  Result := SurfaceGetByteSize(FHandle);
end;

function TSurface.GetFormat: TPixelFormat;
begin
  Result := SurfaceGetFormat(FHandle);
end;

function TSurface.GetPremultipliedAlpha: Boolean;
begin
  Result := SurfaceGetPremultipliedAlpha(FHandle);
end;

procedure TSurface.SetPremultipliedAlpha(const APremultipliedAlpha: Boolean);
begin
  SurfaceSetPremultipliedAlpha(FHandle, APremultipliedAlpha);
end;

function TSurface.GetScanline(const AIndex: Integer): Pointer;
begin
  Result := SurfaceGetScanline(FHandle, AIndex);
end;

function TSurface.GetPixelPtr(const AX, AY: Integer): Pointer;
begin
  Result := SurfaceGetPixelPtr(FHandle, AX, AY);
end;

function TSurface.GetEmpty: Boolean;
begin
  Result := SurfaceEmpty(FHandle);
end;

function TSurface.GetPixel(const AX, AY: Integer): TIntColor;
begin
  Result := SurfaceGetPixel(FHandle, AX, AY);
end;

procedure TSurface.SetPixel(const AX, AY: Integer; const AColor: TIntColor);
begin
  SurfaceSetPixel(FHandle, AX, AY, AColor);
end;

function TSurface.BilinearPixel(const AX, AY: Single): TIntColor;
begin
  Result := SurfaceGetPixelBilinear(FHandle, AX, AY);
end;

procedure TSurface.Resize(const AWidth, AHeight: Integer; const AFormat: TPixelFormat);
begin
  if not SurfaceResize(FHandle, AWidth, AHeight, AFormat) then
    raise EAfterwarpException.CreateFmt(SCouldNotResizeRasterSurface, [AWidth, AHeight, Ord(AFormat)]);
end;

procedure TSurface.ConvertFormat(const AFormat: TPixelFormat);
begin
  if not SurfaceConvertFormat(FHandle, AFormat) then
    raise EAfterwarpException.Create(SCouldNotConvertFormatOfRasterSurface);
end;

procedure TSurface.Clear;
begin
  SurfaceClear(FHandle);
end;

procedure TSurface.Clear(const AColor: TIntColor);
begin
  if not SurfaceClearWith(FHandle, AColor) then
    raise EAfterwarpException.Create(SCouldNotClearRasterSurface);
end;

procedure TSurface.ResetAlpha(const AOpaque: Boolean);
begin
  if not SurfaceResetAlpha(FHandle, AOpaque) then
    raise EAfterwarpException.Create(SCouldNotResetAlphaChannelOfRasterSurface);
end;

function TSurface.HasAlphaChannel: Boolean;
begin
  Result := SurfaceHasAlphaChannel(FHandle);
end;

procedure TSurface.PremultiplyAlpha;
begin
  if not SurfacePremultiplyAlpha(FHandle) then
    raise EAfterwarpException.Create(SCouldNotPremultiplyAlphaChannelOfRasterSurface);
end;

procedure TSurface.UnpremultiplyAlpha;
begin
  if not SurfaceUnpremultiplyAlpha(FHandle) then
    raise EAfterwarpException.Create(SCouldNotUnpremultiplyAlphaChannelOfRasterSurface);
end;

procedure TSurface.Mirror;
begin
  if not SurfaceMirror(FHandle) then
    raise EAfterwarpException.Create(SCouldNotMirrorRasterSurface);
end;

procedure TSurface.Flip;
begin
  if not SurfaceFlip(FHandle) then
    raise EAfterwarpException.Create(SCouldNotFlipRasterSurface);
end;

procedure TSurface.Invert;
begin
  if not SurfaceInvert(FHandle) then
    raise EAfterwarpException.Create(SCouldNotInvertRasterSurface);
end;

procedure TSurface.SaveToFile(const AFileName: Utf8String; const AQuality: Integer);
begin
  if not SurfaceSaveToFile(FHandle, PAnsiChar(AFileName), AQuality) then
    raise EAfterwarpException.CreateFmt(SCouldNotSaveRasterSurfaceToFile, [AFileName]);
end;

function TSurface.SaveToFileInMemory(const AFileContent: Pointer; const AFileContentSize: Cardinal;
  const AExtension: Utf8String; const AQuality: Integer): Cardinal;
begin
  Result := SurfaceSaveToFileInMemory(FHandle, AFileContent, AFileContentSize, PAnsiChar(AExtension),
    AQuality);
end;

{$ENDREGION}
{$REGION 'TSurfaceHelper'}

procedure TSurfaceHelper.CopyRect(const ASurface: TSurface; const ASourceRect: TIntRect;
  const ADestPos: TPoint2i);
begin
  if not SurfaceCopyRect(FHandle, ASurface.Handle, @ASourceRect, @ADestPos) then
    raise EAfterwarpException.Create(SCouldNotCopyPortionOfSourceRasterSurface);
end;

procedure TSurfaceHelper.CopyFrom(const ASurface: TSurface);
begin
  if not SurfaceCopyFrom(FHandle, ASurface.Handle) then
    raise EAfterwarpException.Create(SCouldNotMakeCopyOfSourceSurface);
end;

procedure TSurfaceHelper.ShrinkFrom(const ASurface: TSurface);
begin
  if not SurfaceShrinkFrom(FHandle, ASurface.Handle) then
    raise EAfterwarpException.Create(SCouldNotShrinkToHalfRasterSurface);
end;

procedure TSurfaceHelper.StretchRect(const ASurface: TSurface; const ADestRect, ASourceRect: TFloatRect);
begin
  if not SurfaceStretchRect(FHandle, ASurface.Handle, @ADestRect, @ASourceRect) then
    raise EAfterwarpException.Create(SCouldNotCopyPortionOfSourceSurfaceStretching);
end;

procedure TSurfaceHelper.StretchRectBilinear(const ASurface: TSurface; const ADestRect,
  ASourceRect: TFloatRect);
begin
  if not SurfaceStretchRectBilinear(FHandle, ASurface.Handle, @ADestRect, @ASourceRect) then
    raise EAfterwarpException.Create(SCouldNotCopyPortionOfSourceSurfaceBilinear);
end;

procedure TSurfaceHelper.MakeSignedDistanceField(const ASurface: TSurface; const ASpread: VectorFloat;
  const ADestPos: TPoint2i; const ASourceRect: TIntRect);
begin
  if not SurfaceMakeSignedDistanceField(FHandle, ASurface.Handle, ASpread, @ADestPos, @ASourceRect) then
    raise EAfterwarpException.Create(SCouldNotCalculateSignedDistanceFieldOfSurface);
end;

{$ENDREGION}
{$REGION 'TTexture'}

class function TTexture.Create(const ADevice: TDevice; const AParameters: TTextureParameters): TTexture;
begin
  Result.FHandle := TextureCreate(ADevice.Handle, @AParameters);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateTexture);
end;

class function TTexture.Create(const ADevice: TDevice; const AFileName: Utf8String;
  const AFormat: TPixelFormat; const AAttributes: Cardinal): TTexture;
begin
  Result.FHandle := TextureCreateFromFile(ADevice.Handle, PAnsiChar(AFileName), AFormat, AAttributes);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateTextureFromFile, [AFileName]);
end;

class function TTexture.Create(const ADevice: TDevice; const AFileContent: Pointer;
  const AContentSize: Cardinal; const AExtension: Utf8String; const AFormat: TPixelFormat;
  const AAttributes: Cardinal): TTexture;
begin
  Result.FHandle := TextureCreateFromFileInMemory(ADevice.Handle, AFileContent, AContentSize,
    PAnsiChar(AExtension), AFormat, AAttributes);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateTextureFromMemory);
end;

class function TTexture.Create(const ADevice: TDevice; const ASurface: TSurface;
  const AAttributes: Cardinal): TTexture;
begin
  Result.FHandle := TextureCreateFromSurface(ADevice.Handle, ASurface.Handle, AAttributes);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateTextureFromSurface);
end;

class function TTexture.CreateFromFileParallax(const ADevice: TDevice; const AFileName: Utf8String;
  const AFormat: TPixelFormat; const AAttributes: Cardinal): TTexture;
begin
  Result.FHandle := TextureCreateFromFileParallax(ADevice.Handle, PAnsiChar(AFileName), AFormat, AAttributes);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateDisplacementMappingTextureFromFile, [AFileName]);
end;

class function TTexture.CreateFromFilesNormalsAndOcclusion(const ADevice: TDevice; const AFileNameNormalMap,
  AFileNameOcclusionMap: Utf8String; const AAttributes: Cardinal): TTexture;
begin
  Result.FHandle := TextureCreateFromFileNormalsAndOcclusion(ADevice.Handle, PAnsiChar(AFileNameNormalMap),
    PAnsiChar(AFileNameOcclusionMap), AAttributes);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateNormalOcclusion, [AFileNameNormalMap,
      AFileNameOcclusionMap]);
end;

procedure TTexture.Free;
begin
  if FHandle <> nil then
  begin
    TextureDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TTexture.GetPlatformHandle: Pointer;
begin
  Result := Pointer(TextureGetPlatformHandle(FHandle));
end;

function TTexture.GetDevice: TDevice;
begin
  Result.FHandle := TextureGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TTexture.GetParameters: TTextureParameters;
begin
  TextureGetParameters(FHandle, @Result);
end;

procedure TTexture.Update(const AContent: Pointer; const APitch: Cardinal; const ALayer: Integer;
  const ARect: TIntRect; const AMipLevel: Integer);
begin
  if not TextureUpdate(FHandle, AContent, APitch, ALayer, @ARect, AMipLevel) then
    raise EAfterwarpException.Create(SCouldNotUpdatePortionOfTexture);
end;

procedure TTexture.Retrieve(const AContent: Pointer; const APitch: Cardinal; const ALayer: Integer;
  const ARect: TIntRect; const AMipLevel: Integer);
begin
  if not TextureRetrieve(FHandle, AContent, APitch, ALayer, @ARect, AMipLevel) then
    raise EAfterwarpException.Create(SCouldNotRetrieveAPortionOfTexture);
end;

procedure TTexture.Copy(const ASurface: TSurface; const ALayer: Integer; const ADestPos: TPoint2i;
  const ASourceRect: TIntRect; const AMipLevel: Integer);
begin
  if not TextureCopyFromSurface(FHandle, ASurface.Handle, ALayer, @ADestPos, @ASourceRect, AMipLevel) then
    raise EAfterwarpException.Create(SCouldNotCopyAPortionOfRasterSurfaceToTexture);
end;

procedure TTexture.Save(const ASurface: TSurface; const ALayer: Integer; const ADestPos: TPoint2i;
  const ASourceRect: TIntRect; const AMipLevel: Integer);
begin
  if not TextureCopyToSurface(FHandle, ASurface.Handle, ALayer, @ADestPos, @ASourceRect, AMipLevel) then
    raise EAfterwarpException.Create(SCouldNotCopyAPortionOfTextureToRasterSurface);
end;

procedure TTexture.LoadFromFile(const AFileName: Utf8String; const ALayer: Integer; const ADestPos: TPoint2i;
  const ASourceRect: TIntRect; const AMipLevel: Integer);
begin
  if not TextureLoadFromFile(FHandle, PAnsiChar(AFileName), ALayer, @ADestPos, @ASourceRect, AMipLevel) then
    raise EAfterwarpException.CreateFmt(SCouldNotLoadAPortionOfTextureFromFile, [AFileName]);
end;

procedure TTexture.SaveToFile(const AFileName: Utf8String; const AQuality, ALayer: Integer;
  const ASourceRect: TIntRect; const AMipLevel: Integer);
begin
  if not TextureSaveToFile(FHandle, PAnsiChar(AFileName), AQuality, ALayer, @ASourceRect, AMipLevel) then
    raise EAfterwarpException.CreateFmt(SCouldNotSaveAPortionOfTextureToFile, [AFileName]);
end;

procedure TTexture.Clear;
begin
  if not TextureClear(FHandle) then
    raise EAfterwarpException.Create(SFailedClearingTexture);
end;

procedure TTexture.Clear(const AColor: TFloatColor; const ALayer, AMipLevel: Integer);
begin
  if not TextureClearWith(FHandle, @AColor, ALayer, AMipLevel) then
    raise EAfterwarpException.Create(SFailedClearingTextureWithColor);
end;

procedure TTexture.Bind(const AChannel: Cardinal);
begin
  if not TextureBind(FHandle, AChannel) then
    raise EAfterwarpException.CreateFmt(SFailedBindingTextureToChannel, [AChannel]);
end;

procedure TTexture.Unbind(const AChannel: Cardinal);
begin
  if not TextureUnbind(FHandle, AChannel) then
    raise EAfterwarpException.CreateFmt(SFailedUnbindingTextureFromChannel, [AChannel]);
end;

procedure TTexture.Detach;
begin
  TextureDetach(FHandle);
end;

procedure TTexture.BeginScene(const ALayer, AMipLevel: Integer);
begin
  if not TextureBegin(FHandle, ALayer, AMipLevel) then
    raise EAfterwarpException.Create(SCouldNotStartRenderingToTexture);
end;

procedure TTexture.EndScene;
begin
  if not TextureEnd(FHandle) then
    raise EAfterwarpException.Create(SCouldNotFinishRenderingToTexture);
end;

procedure TTexture.GenerateMipMaps;
begin
  if not TextureGenerateMipMaps(FHandle) then
    raise EAfterwarpException.Create(SCouldNotGenerateMipmapsForTexture);
end;

procedure TTexture.ResetCache;
begin
  TextureResetCache(FHandle);
end;

{$ENDREGION}
{$REGION 'TTextureHelper'}

procedure TTextureHelper.Copy(const ATexture: TTexture; const ADestLayer: Integer; const ADestPos: TPoint2i;
  const ASrcLayer: Integer; const ASourceRect: TIntRect; const ADestMipLevel: Integer;
  const ASrcMipLevel: Integer);
begin
  if not TextureCopy(FHandle, ATexture.Handle, ADestLayer, @ADestPos, ASrcLayer, @ASourceRect,
    ADestMipLevel, ASrcMipLevel) then
    raise EAfterwarpException.Create(SCouldNotCopyAPortionOfTextureToAnother);
end;

procedure TTextureHelper.Attach(const ATexture: TTexture; const ALayer, AMipLevel: Integer);
begin
  if not TextureAttach(FHandle, ATexture.Handle, ALayer, AMipLevel) then
    raise EAfterwarpException.Create(SCouldNotCopyAPortionOfTextureToAnother);
end;

{$ENDREGION}
{$REGION 'TImageAtlas'}

class function TImageAtlas.Create(const ADevice: TDevice): TImageAtlas;
begin
  Result.FHandle := ImageAtlasCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateImageAtlas);
end;

procedure TImageAtlas.Free;
begin
  if FHandle <> nil then
  begin
    ImageAtlasDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TImageAtlas.GetDevice: TDevice;
begin
  Result.FHandle := ImageAtlasGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TImageAtlas.GetTextureCount: Integer;
begin
  Result := ImageAtlasTextureCount(FHandle);
end;

function TImageAtlas.GetTexture(const ATextureIndex: Integer): TTexture;
begin
  Result.FHandle := ImageAtlasTexture(FHandle, ATextureIndex);
end;

function TImageAtlas.GetRegionCount: Integer;
begin
  Result := ImageAtlasRegionCount(FHandle);
end;

function TImageAtlas.GetRegion(const ARegionIndex: Integer): TImageRegion;
begin
  if not ImageAtlasRegion(FHandle, ARegionIndex, @Result) then
  begin
    FillChar(Result, SizeOf(TImageRegion), 0);
    Result.Index := High(Word);
  end;
end;

function TImageAtlas.CreateRegion(const ARect: TIntRect; const ATextureIndex: Integer): Integer;
begin
  Result := ImageAtlasCreateRegion(FHandle, @ARect, ATextureIndex);
end;

procedure TImageAtlas.RemoveRegion(const ARegionIndex: Integer);
begin
  ImageAtlasRemoveRegion(FHandle, ARegionIndex);
end;

procedure TImageAtlas.MakeRegions(const APatternSize, AVisibleSize: TPoint2i; const APatternCount: Integer);
begin
  ImageAtlasMakeRegions(FHandle, @APatternSize, @AVisibleSize, APatternCount);
end;

procedure TImageAtlas.ClearTextures;
begin
  ImageAtlasClearTextures(FHandle);
end;

procedure TImageAtlas.RemoveTexture(const ATextureIndex: Integer);
begin
  ImageAtlasRemoveTexture(FHandle, ATextureIndex);
end;

function TImageAtlas.CreateTexture(const ASize: TPoint2i; const AFormat: TPixelFormat;
  const AAttributes: Cardinal): Integer;
begin
  Result := ImageAtlasCreateTexture(FHandle, @ASize, AFormat, AAttributes);
end;

function TImageAtlas.Pack(const ASize: TPoint2i; const APadding: Integer): Integer;
begin
  Result := ImageAtlasPackRegion(FHandle, @ASize, APadding);
end;

function TImageAtlas.Pack(const ASurface: TSurface; const ASourceRect: TIntRect;
  const APadding: Integer): Integer;
begin
  Result := ImageAtlasPackSurface(FHandle, ASurface.Handle, @ASourceRect, APadding);
end;

{$ENDREGION}
{$REGION 'TCanvasBuffer'}

class function TCanvasBuffer.Create: TCanvasBuffer;
begin
  Result.FHandle := CanvasBufferCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateCanvasBuffer);
end;

procedure TCanvasBuffer.Free;
begin
  if FHandle <> nil then
  begin
    CanvasBufferDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TCanvasBuffer.GetVertexCount: Integer;
begin
  Result := CanvasBufferGetVertexCount(FHandle);
end;

procedure TCanvasBuffer.SetVertexCount(const AVertexCount: Integer);
begin
  if not CanvasBufferSetVertexCount(FHandle, AVertexCount) then
    raise EAfterwarpException.CreateFmt(SCouldNotSetVertexCountOfCanvasBuffer, [AVertexCount]);
end;

function TCanvasBuffer.GetIndexCount: Integer;
begin
  Result := CanvasBufferGetIndexCount(FHandle);
end;

procedure TCanvasBuffer.SetIndexCount(const AIndexCount: Integer);
begin
  if not CanvasBufferSetIndexCount(FHandle, AIndexCount) then
    raise EAfterwarpException.CreateFmt(SCouldNotSetIndexCountOfCanvasBuffer, [AIndexCount]);
end;

function TCanvasBuffer.GetVertices: PPoint2f;
begin
  Result := CanvasBufferGetVertices(FHandle);
end;

function TCanvasBuffer.GetColors: PIntColor;
begin
  Result := CanvasBufferGetColors(FHandle);
end;

function TCanvasBuffer.GetIndices: PLongInt;
begin
  Result := CanvasBufferGetIndices(FHandle);
end;

function TCanvasBuffer.GetExtents: TFloatRect;
begin
  CanvasBufferGetExtents(FHandle, @Result);
end;

procedure TCanvasBuffer.Clear;
begin
  CanvasBufferClear(FHandle);
end;

procedure TCanvasBuffer.ClearAndShrink;
begin
  CanvasBufferClearAndShrink(FHandle);
end;

{$ENDREGION}
{$REGION 'TPathBroker'}

class function TPathBroker.Create: TPathBroker;
begin
  Result.FHandle := PathBrokerCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreatePathBroker);
end;

procedure TPathBroker.Free;
begin
  if FHandle <> nil then
  begin
    PathBrokerDestroy(FHandle);
    FHandle := nil;
  end;
end;

procedure TPathBroker.Fill(const ABuffer: TCanvasBuffer; const APath: array of TPathElement;
  const AColors: TColorRect; const AQuality: Boolean);
begin
  if (Length(APath) <= 0) or (not PathBrokerFill(FHandle, ABuffer.Handle, @APath[0], Length(APath),
    @AColors, AQuality)) then
    raise EAfterwarpException.Create(SCouldNotPreRenderFillForGivenPath);
end;

procedure TPathBroker.Fill(const ABuffer: TCanvasBuffer; const APath: TPathElementArray;
  const AColors: TColorRect; const AQuality: Boolean);
begin
  if (Length(APath) <= 0) or (not PathBrokerFill(FHandle, ABuffer.Handle, @APath[0], Length(APath),
    @AColors, AQuality)) then
    raise EAfterwarpException.Create(SCouldNotPreRenderFillForGivenPath);
end;

procedure TPathBroker.Stroke(const ABuffer: TCanvasBuffer; const APath: array of TPathElement;
  const AThickness: Single; const AColor: TIntColor; const AJoints: TPathJoint; const ACaps: TLineCaps;
  const AMiterLimit, ASmoothStep: Single);
begin
  if (Length(APath) <= 0) or (not PathBrokerStroke(FHandle, ABuffer.Handle, @APath[0], Length(APath),
    AThickness, AColor, AJoints, ACaps, AMiterLimit, ASmoothStep)) then
    raise EAfterwarpException.Create(SCouldNotPreRenderStrokeForGivenPath);
end;

procedure TPathBroker.Stroke(const ABuffer: TCanvasBuffer; const APath: TPathElementArray;
  const AThickness: Single; const AColor: TIntColor; const AJoints: TPathJoint; const ACaps: TLineCaps;
  const AMiterLimit, ASmoothStep: Single);
begin
  if (Length(APath) <= 0) or (not PathBrokerStroke(FHandle, ABuffer.Handle, @APath[0], Length(APath),
    AThickness, AColor, AJoints, ACaps, AMiterLimit, ASmoothStep)) then
    raise EAfterwarpException.Create(SCouldNotPreRenderStrokeForGivenPath);
end;

procedure TPathBroker.Reset;
begin
  PathBrokerReset(FHandle);
end;

{$ENDREGION}
{$REGION 'TCanvas'}

class function TCanvas.Create(const ADevice: TDevice): TCanvas;
begin
  Result.FHandle := CanvasCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateGraphicsCanvas);
end;

procedure TCanvas.Free;
begin
  if FHandle <> nil then
  begin
    CanvasDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TCanvas.GetDevice: TDevice;
begin
  Result.FHandle := CanvasGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TCanvas.GetClipRect: TIntRect;
begin
  CanvasGetClipRect(FHandle, @Result);
end;

procedure TCanvas.SetClipRect(const AClipRect: TIntRect);
begin
  if not CanvasSetClipRect(FHandle, @AClipRect) then
    raise EAfterwarpException.Create(SCouldNotUpdateCanvasClippingRectangle);
end;

function TCanvas.GetWorld: TMatrix4f;
begin
  CanvasGetWorld(FHandle, @Result);
end;

procedure TCanvas.SetWorld(const AWorld: TMatrix4f);
begin
  CanvasSetWorld(FHandle, @AWorld);
end;

function TCanvas.GetViewProjection: TMatrix4f;
begin
  CanvasGetViewProjection(FHandle, @Result);
end;

procedure TCanvas.SetViewProjection(const AViewProjection: TMatrix4f);
begin
  CanvasSetViewProjection(FHandle, @AViewProjection);
end;

function TCanvas.GetTransform: TMatrix3x2f;
begin
  CanvasGetTransform(FHandle, @Result);
end;

procedure TCanvas.SetTransform(const ATransform: TMatrix3x2f);
begin
  CanvasSetTransform(FHandle, @ATransform);
end;

function TCanvas.GetBatchCount: Integer;
begin
  Result := CanvasGetBatchCount(FHandle);
end;

function TCanvas.GetAttributes: TCanvasAttributes;
var
  LAttributes: Cardinal;
begin
  LAttributes := CanvasGetAttributes(FHandle);
  Result := PCanvasAttributes(@LAttributes)^;
end;

procedure TCanvas.SetAttributes(const AAttributes: TCanvasAttributes);
var
  LAttributes: Cardinal;
begin
  LAttributes := 0;
  PCanvasAttributes(@LAttributes)^ := AAttributes;
  CanvasSetAttributes(FHandle, LAttributes);
end;

function TCanvas.GetSignedDistanceField: TSignedDistanceField;
begin
  CanvasGetSignedDistanceField(FHandle, @Result);
end;

procedure TCanvas.SetSignedDistanceField(const ASignedDistanceField: TSignedDistanceField);
begin
  CanvasSetSignedDistanceField(FHandle, @ASignedDistanceField);
end;

function TCanvas.GetContextState: TCanvasContextState;
begin
  Result := CanvasGetContextState(FHandle);
end;

procedure TCanvas.SetContextState(const AContextState: TCanvasContextState);
begin
  if not CanvasSetContextState(FHandle, AContextState) then
    raise EAfterwarpException.Create(SCouldNotUpdateCanvasContextState);
end;

function TCanvas.GetSamplerState: TCanvasSamplerState;
begin
  if not CanvasGetSamplerState(FHandle, @Result) then
    raise EAfterwarpException.Create(SCouldNotRetrieveCanvasSamplerState);
end;

procedure TCanvas.SetSamplerState(const ASamplerState: TCanvasSamplerState);
begin
  if not CanvasSetSamplerState(FHandle, @ASamplerState) then
    raise EAfterwarpException.Create(SCouldNotUpdateCanvasSamplerState);
end;

procedure TCanvas.BeginScene;
begin
  if not CanvasBegin(FHandle) then
    raise EAfterwarpException.Create(SCouldNotStartRenderingWithCanvas);
end;

procedure TCanvas.EndScene;
begin
  CanvasEnd(FHandle);
end;

procedure TCanvas.Pixels(const APositions: PPoint2f; const AColors: PIntColor; const AElementCount: Integer;
  const AEffect: TBlendingEffect);
begin
  CanvasPixels(FHandle, APositions, AColors, AElementCount, AEffect);
end;

procedure TCanvas.Lines(const AVertices: PPoint2f; const AColors: PIntColor; const AIndices: PLongInt;
  const AVertexCount, APrimitiveCount: Integer; const AEffect: TBlendingEffect);
begin
  CanvasLines(FHandle, AVertices, AColors, AIndices, AVertexCount, APrimitiveCount, AEffect);
end;

procedure TCanvas.Triangles(const AVertices: PPoint2f; const AColors: PIntColor; const AIndices: PLongInt;
  const AVertexCount, APrimitiveCount: Integer; const AEffect: TBlendingEffect);
begin
  CanvasTriangles(FHandle, AVertices, AColors, AIndices, AVertexCount, APrimitiveCount, AEffect);
end;

procedure TCanvas.Triangles(const ATexture: TTexture; const AVertices, ATexCoords: PPoint2f;
  const AColors: PIntColor; const AIndices: PLongInt; const AVertexCount, APrimitiveCount: Integer;
  const AEffect: TBlendingEffect);
begin
  CanvasTexturedTriangles(FHandle, ATexture.Handle, AVertices, ATexCoords, AColors, AIndices, AVertexCount,
    APrimitiveCount, AEffect);
end;

procedure TCanvas.Pixel(const APosition: TPoint2f; const AColor: TIntColor; const AEffect: TBlendingEffect);
begin
  CanvasPixel(FHandle, @APosition, AColor, AEffect);
end;

procedure TCanvas.Line(const ASrcPos, ADestPos: TPoint2f; const AColors: TColorPair;
  const AEffect: TBlendingEffect);
begin
  CanvasLine(FHandle, @ASrcPos, @ADestPos, @AColors, AEffect);
end;

procedure TCanvas.ThickLine(const ASrcPos, ADestPos: TPoint2f; const AColors: TColorPair;
  const AThickness: Single; const AEffect: TBlendingEffect);
begin
  CanvasThickLine(FHandle, @ASrcPos, @ADestPos, @AColors, AThickness, AEffect);
end;

procedure TCanvas.LineEllipse(const AOrigin, ARadius: TPoint2f; const ASteps: Integer;
  const AColor: TIntColor; const AEffect: TBlendingEffect);
begin
  CanvasLineEllipse(FHandle, @AOrigin, @ARadius, ASteps, AColor, AEffect);
end;

procedure TCanvas.LineCircle(const AOrigin: TPoint2f; const ARadius: VectorFloat; const ASteps: Integer;
  const AColor: TIntColor; const AEffect: TBlendingEffect);
begin
  CanvasLineCircle(FHandle, @AOrigin, ARadius, ASteps, AColor, AEffect);
end;

procedure TCanvas.ThickLineEllipse(const AOrigin, ARadius: TPoint2f; const ASteps: Integer;
  const AColor: TIntColor; const AThickness: Single; const AEffect: TBlendingEffect);
begin
  CanvasThickLineEllipse(FHandle, @AOrigin, @ARadius, ASteps, AColor, AThickness, AEffect);
end;

procedure TCanvas.ThickLineCircle(const AOrigin: TPoint2f; const ARadius: VectorFloat; const ASteps: Integer;
  const AColor: TIntColor; const AThickness: Single; const AEffect: TBlendingEffect);
begin
  CanvasThickLineCircle(FHandle, @AOrigin, ARadius, ASteps, AColor, AThickness, AEffect);
end;

procedure TCanvas.LineTriangle(const AVertex1, AVertex2, AVertex3: TPoint2f; const AColor1, AColor2,
  AColor3: TIntColor; const AEffect: TBlendingEffect);
begin
  CanvasLineTriangle(FHandle, @AVertex1, @AVertex2, @AVertex3, AColor1, AColor2, AColor3, AEffect);
end;

procedure TCanvas.ThickLineTriangle(const AVertex1, AVertex2, AVertex3: TPoint2f; const AColor1, AColor2,
  AColor3: TIntColor; const AThickness: VectorFloat; const AEffect: TBlendingEffect);
begin
  CanvasThickLineTriangle(FHandle, @AVertex1, @AVertex2, @AVertex3, AColor1, AColor2, AColor3, AThickness,
    AEffect);
end;

procedure TCanvas.LineHexagon(const AColor1, AColor2, AColor3, AColor4, AColor5, AColor6: TIntColor;
  const AEffect: TBlendingEffect);
begin
  CanvasLineHexagon(FHandle, AColor1, AColor2, AColor3, AColor4, AColor5, AColor6, AEffect);
end;

procedure TCanvas.LineHexagon(const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasLineHexagonGrad(FHandle, @AColors, AEffect);
end;

procedure TCanvas.ThickLineHexagon(const AColor1, AColor2, AColor3, AColor4, AColor5, AColor6: TIntColor;
  const AThickness: VectorFloat; const AEffect: TBlendingEffect);
begin
  CanvasThickLineHexagon(FHandle, AColor1, AColor2, AColor3, AColor4, AColor5, AColor6, AThickness, AEffect);
end;

procedure TCanvas.ThickLineHexagon(const AColors: TColorRect; const AThickness: VectorFloat;
  const AEffect: TBlendingEffect);
begin
  CanvasThickLineHexagonGrad(FHandle, @AColors, AThickness, AEffect);
end;

procedure TCanvas.LineQuad(const AVertices: TQuad; const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasLineQuad(FHandle, @AVertices, @AColors, AEffect);
end;

procedure TCanvas.ThickLineQuad(const AVertices: TQuad; const AColors: TColorRect; const AThickness: Single;
  const AEffect: TBlendingEffect);
begin
  CanvasThickLineQuad(FHandle, @AVertices, @AColors, AThickness, AEffect);
end;

procedure TCanvas.Triangle(const AVertex1, AVertex2, AVertex3: TPoint2f; const AColor1, AColor2,
  AColor3: TIntColor; const AEffect: TBlendingEffect);
begin
  CanvasTriangle(FHandle, @AVertex1, @AVertex2, @AVertex3, AColor1, AColor2, AColor3, AEffect);
end;

procedure TCanvas.Quad(const AVertices: TQuad; const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasQuad(FHandle, @AVertices, @AColors, AEffect);
end;

procedure TCanvas.FillRect(const ARect: TFloatRect; const AColors: TColorRect;
  const AEffect: TBlendingEffect);
begin
  CanvasFillRect(FHandle, @ARect, @AColors, AEffect);
end;

procedure TCanvas.FrameRect(const ARect: TFloatRect; const AColors: TColorRect; const AThickness: Single;
  const AEffect: TBlendingEffect);
begin
  CanvasFrameRect(FHandle, @ARect, @AColors, AThickness, AEffect);
end;

procedure TCanvas.FillRoundRect(const ARect: TFloatRect; const AColors: TColorRect; const ARadius: Single;
  const ASteps: Cardinal; const AEffect: TBlendingEffect);
begin
  CanvasFillRoundRect(FHandle, @ARect, @AColors, ARadius, ASteps, AEffect);
end;

procedure TCanvas.FrameRoundRect(const ARect: TFloatRect; const AColors: TColorRect; const ARadius,
  AThickness: Single; const ASteps: Cardinal; const AEffect: TBlendingEffect);
begin
  CanvasFrameRoundRect(FHandle, @ARect, @AColors, ARadius, AThickness, ASteps, AEffect);
end;

procedure TCanvas.FillRoundRectTop(const ARect: TFloatRect; const AColors: TColorRect;
  const ARadius: Single; const ASteps: Integer; const AEffect: TBlendingEffect);
begin
  CanvasFillRoundRectTop(FHandle, @ARect, @AColors, ARadius, ASteps, AEffect);
end;

procedure TCanvas.FillRoundRectBottom(const ARect: TFloatRect; const AColors: TColorRect;
  const ARadius: Single; const ASteps: Integer; const AEffect: TBlendingEffect);
begin
  CanvasFillRoundRectBottom(FHandle, @ARect, @AColors, ARadius, ASteps, AEffect);
end;

procedure TCanvas.FillRoundRectTopInverse(const ARect: TFloatRect; const AColors: TColorRect;
  const ARadius: Single; const ASteps: Integer; const AEffect: TBlendingEffect);
begin
  CanvasFillRoundRectTopInverse(FHandle, @ARect, @AColors, ARadius, ASteps, AEffect);
end;

procedure TCanvas.Highlight(const ARect: TFloatRect; const ACornerRadius, ALuma, ADistance: Single;
  const ASteps: Integer);
begin
  CanvasHighlight(FHandle, @ARect, ACornerRadius, ALuma, ADistance, ASteps);
end;

procedure TCanvas.Hexagon(const AColor1, AColor2, AColor3, AColor4, AColor5, AColor6: TIntColor;
  const AEffect: TBlendingEffect);
begin
  CanvasHexagon(FHandle, AColor1, AColor2, AColor3, AColor4, AColor5, AColor6, AEffect);
end;

procedure TCanvas.Hexagon(const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasHexagonGrad(FHandle, @AColors, AEffect);
end;

procedure TCanvas.Arc(const AOrigin, ARadius: TPoint2f; const AInitAngle, AEndAngle: Single;
  const ASteps: Integer; const AColorInside, AColorOutside: TIntColor; const AEffect: TBlendingEffect);
begin
  CanvasArc(FHandle, @AOrigin, @ARadius, AInitAngle, AEndAngle, ASteps, AColorInside, AColorOutside,
    AEffect);
end;

procedure TCanvas.Arc(const AOrigin, ARadius: TPoint2f; const AInitAngle, AEndAngle: Single;
  const ASteps: Integer; const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasArcGrad(FHandle, @AOrigin, @ARadius, AInitAngle, AEndAngle, ASteps, @AColors, AEffect);
end;

procedure TCanvas.Ellipse(const AOrigin, ARadius: TPoint2f; const ASteps: Integer; const AColors: TColorRect;
  const AEffect: TBlendingEffect);
begin
  CanvasEllipse(FHandle, @AOrigin, @ARadius, ASteps, @AColors, AEffect);
end;

procedure TCanvas.Ribbon(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const ASteps: Integer;
  const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasRibbonGrad(FHandle, @AOrigin, @AInsideRadius, @AOutsideRadius, ASteps, @AColors, AEffect);
end;

procedure TCanvas.Ribbon(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const ASteps: Integer;
  const AColor1, AColor2, AColor3: TColorPair; const AEffect: TBlendingEffect);
begin
  CanvasRibbonTri(FHandle, @AOrigin, @AInsideRadius, @AOutsideRadius, ASteps, @AColor1, @AColor2, @AColor3,
    AEffect);
end;

procedure TCanvas.Ribbon(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const ASteps: Integer;
  const AColor: TIntColor; const AEffect: TBlendingEffect);
begin
  CanvasRibbon(FHandle, @AOrigin, @AInsideRadius, @AOutsideRadius, ASteps, AColor, AEffect);
end;

procedure TCanvas.Tape(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const AInitAngle,
  AEndAngle: Single; const ASteps: Integer; const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasTapeGrad(FHandle, @AOrigin, @AInsideRadius, @AOutsideRadius, AInitAngle, AEndAngle, ASteps, @AColors,
    AEffect);
end;

procedure TCanvas.Tape(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f; const AInitAngle,
  AEndAngle: Single; const ASteps: Integer; const AColor1, AColor2, AColor3: TColorPair;
  const AEffect: TBlendingEffect);
begin
  CanvasTapeTri(FHandle, @AOrigin, @AInsideRadius, @AOutsideRadius, AInitAngle, AEndAngle, ASteps, @AColor1,
    @AColor2, @AColor3, AEffect);
end;

procedure TCanvas.Tape(const AOrigin, AInsideRadius, AOutsideRadius: TPoint2f;const AInitAngle,
  AEndAngle: Single; const ASteps: Integer; const AColor: TIntColor; const AEffect: TBlendingEffect);
begin
  CanvasTape(FHandle, @AOrigin, @AInsideRadius, @AOutsideRadius, AInitAngle, AEndAngle, ASteps, AColor,
    AEffect);
end;

procedure TCanvas.RectWithHole(const ARect: TFloatRect; const AHoleOrigin, AHoleRadius: TPoint2f;
  const AColors: TColorPair; const ASteps: Integer; const AEffect: TBlendingEffect);
begin
  CanvasRectWithHole(FHandle, @ARect, @AHoleOrigin, @AHoleRadius, @AColors, ASteps, AEffect);
end;

procedure TCanvas.Draw(const ABuffer: TCanvasBuffer; const AEffect: TBlendingEffect);
begin
  CanvasDrawBuffer(FHandle, ABuffer.Handle, AEffect);
end;

procedure TCanvas.Triangle(const ATexture: TTexture; const AVertex1, AVertex2, AVertex3, ATexCoord1,
  ATexCoord2, ATexCoord3: TPoint2f; const AColor1, AColor2, AColor3: TIntColor;
  const AEffect: TBlendingEffect);
begin
  CanvasTexturedTriangle(FHandle, ATexture.Handle, @AVertex1, @AVertex2, @AVertex3, @ATexCoord1, @ATexCoord2,
    @ATexCoord3, AColor1, AColor2, AColor3, AEffect);
end;

procedure TCanvas.TriangleRegion(const ATexture: TTexture; const AVertex1, AVertex2, AVertex3, ATexCoord1,
  ATexCoord2, ATexCoord3: TPoint2f; const AColor1, AColor2, AColor3: TIntColor;
  const AEffect: TBlendingEffect);
begin
  CanvasTexturedTriangleRegion(FHandle, ATexture.Handle, @AVertex1, @AVertex2, @AVertex3, @ATexCoord1,
    @ATexCoord2, @ATexCoord3, AColor1, AColor2, AColor3, AEffect);
end;

procedure TCanvas.Quad(const ATexture: TTexture; const AVertices, ATexCoords: TQuad;
  const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasTexturedQuad(FHandle, ATexture.Handle, @AVertices, @ATexCoords, @AColors, AEffect);
end;

procedure TCanvas.QuadRegion(const ATexture: TTexture; const AVertices, ATexCoords: TQuad;
  const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasTexturedQuadRegion(FHandle, ATexture.Handle, @AVertices, @ATexCoords, @AColors, AEffect);
end;

procedure TCanvas.TexturedRoundRect(const ATexture: TTexture; const ARect: TFloatRect;
  const ATexCoords: TQuad; const AColors: TColorRect; const ARadius: Single; const ASteps: Integer;
  const AEffect: TBlendingEffect);
begin
  CanvasTexturedRoundRect(FHandle, ATexture.Handle, @ARect, @ATexCoords, @AColors, ARadius, ASteps, AEffect);
end;

procedure TCanvas.TexturedRoundRectRegion(const ATexture: TTexture; const ARect: TFloatRect;
  const ATexCoords: TQuad; const AColors: TColorRect; const ARadius: Single; const ASteps: Integer;
  const AEffect: TBlendingEffect);
begin
  CanvasTexturedRoundRectRegion(FHandle, ATexture.Handle, @ARect, @ATexCoords, @AColors, ARadius, ASteps,
    AEffect);
end;

procedure TCanvas.QuadImage(const AImageAtlas: TImageAtlas; const AVertices: TQuad;
  const ARegionIndex: Integer; const AColors: TColorRect; const ASourceRect: TFloatRect;
  const AModifiers: TImageAttributes; const AEffect: TBlendingEffect);
begin
  CanvasQuadImage(FHandle, AImageAtlas.Handle, @AVertices, ARegionIndex, @AColors, @ASourceRect,
    Byte(AModifiers), AEffect);
end;

procedure TCanvas.QuadImage(const AImageAtlas: TImageAtlas; const AVertices: TQuad;
  const ARegionIndex: Integer; const AColors: TColorRect; const AEffect: TBlendingEffect);
begin
  CanvasQuadImage(FHandle, AImageAtlas.Handle, @AVertices, ARegionIndex, @AColors, nil, 0, AEffect);
end;

procedure TCanvas.ResetSamplerState;
begin
  CanvasResetSamplerState(FHandle);
end;

procedure TCanvas.Flush;
begin
  CanvasFlush(FHandle);
end;

procedure TCanvas.Reset;
begin
  CanvasReset(FHandle);
end;

{$ENDREGION}
{$REGION 'TTextRenderer'}

class function TTextRenderer.Create(const ACanvas: TCanvas; const ATextureSize: TPoint2i;
  const APixelFormat: TPixelFormat; const AMipMapping: Boolean): TTextRenderer;
begin
  Result.FHandle := TextRendererCreate(ACanvas.Handle, @ATextureSize, APixelFormat, AMipMapping);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateTextRenderer);
end;

procedure TTextRenderer.Free;
begin
  if FHandle <> nil then
  begin
    TextRendererDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TTextRenderer.GetFontParameters: TFontParameters;
begin
  if not TextRendererGetFontParameters(FHandle, @Result) then
    raise EAfterwarpException.Create(SCouldNotRetrieveTextRendererFontParameters);
end;

procedure TTextRenderer.SetFontParameters(const AFontParameters: TFontParameters);
begin
  if not TextRendererSetFontParameters(FHandle, @AFontParameters) then
    raise EAfterwarpException.Create(SCouldNotUpdateTextRendererFontParameters);
end;

function TTextRenderer.GetCanvas: TCanvas;
begin
  Result.FHandle := TextRendererGetCanvas(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedCanvas);
end;

function TTextRenderer.Extent(const AText: Utf8String; const AModifiers: PTextRenderModifiers): TPoint2f;
begin
  TextRendererExtent(FHandle, PAnsiChar(AText), @Result, AModifiers);
end;

function TTextRenderer.ExtentByPixels(const AText: Utf8String;
  const AModifiers: PTextRenderModifiers): TFloatRect;
begin
  TextRendererExtentByPixels(FHandle, PAnsiChar(AText), @Result, AModifiers);
end;

function TTextRenderer.Rects(const AText: Utf8String; const AExtent: PPoint2f; const ARects: PTextEntryRect;
  const AModifiers: PTextRenderModifiers): Integer;
begin
  Result := TextRendererRects(FHandle, AExtent, ARects, PAnsiChar(AText), AModifiers);
end;

procedure TTextRenderer.Draw(const APosition: TPoint2f; const AText: Utf8String; const AColors: TColorPair;
  const AAlpha: VectorFloat; const AModifiers: PTextRenderModifiers);
begin
  TextRendererDraw(FHandle, @APosition, PAnsiChar(AText), @AColors, AAlpha, AModifiers);
end;

procedure TTextRenderer.DrawAligned(const APosition: TPoint2f; const AText: Utf8String;
  const AColors: TColorPair; const AHorizAlign, AVertAlign: TTextAlignment; const AAlpha: VectorFloat;
  const AAlignToPixels: Boolean; const AModifiers: PTextRenderModifiers);
begin
  TextRendererDrawAligned(FHandle, @APosition, PAnsiChar(AText), @AColors, AHorizAlign, AVertAlign, AAlpha,
    AAlignToPixels, AModifiers);
end;

procedure TTextRenderer.DrawCentered(const APosition: TPoint2f; const AText: Utf8String;
  const AColors: TColorPair; const AAlpha: VectorFloat; const AAlignToPixels: Boolean;
  const AModifiers: PTextRenderModifiers);
begin
  TextRendererDrawCentered(FHandle, @APosition, PAnsiChar(AText), @AColors, AAlpha, AAlignToPixels,
    AModifiers);
end;

procedure TTextRenderer.DrawAlignedByPixels(const APosition: TPoint2f; const AText: Utf8String;
  const AColors: TColorPair; const AHorizAlign, AVertAlign: TTextAlignment; const AAlpha: VectorFloat;
  const AAlignToPixels: Boolean; const AModifiers: PTextRenderModifiers);
begin
  TextRendererDrawAlignedByPixels(FHandle, @APosition, PAnsiChar(AText), @AColors, AHorizAlign, AVertAlign,
    AAlpha, AAlignToPixels, AModifiers);
end;

procedure TTextRenderer.DrawCenteredByPixels(const APosition: TPoint2f; const AText: Utf8String;
  const AColors: TColorPair; const AAlpha: VectorFloat; const AAlignToPixels: Boolean;
  const AModifiers: PTextRenderModifiers);
begin
  TextRendererDrawCenteredByPixels(FHandle, @APosition, PAnsiChar(AText), @AColors, AAlpha, AAlignToPixels,
    AModifiers);
end;

{$ENDREGION}
{$REGION 'TTextModeller'}

class function TTextModeller.Create(const ADevice: TDevice): TTextModeller;
begin
  Result.FHandle := TextModellerCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateTextModeller);
end;

procedure TTextModeller.Free;
begin
  if FHandle <> nil then
  begin
    TextModellerDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TTextModeller.GetDevice: TDevice;
begin
  Result.FHandle := TextModellerGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TTextModeller.GetFontProvider: TFontProvider;
begin
  Result.FHandle := TUntypedObjectHandle(TextModellerGetFontProvider(FHandle));
end;

procedure TTextModeller.SetFontProvider(const AProvider: TFontProvider);
begin
  TextModellerSetFontProvider(FHandle, NativeInt(AProvider.Handle));
end;

function TTextModeller.GetTransform: TMatrix4f;
begin
  TextModellerGetTransform(FHandle, @Result);
end;

procedure TTextModeller.SetTransform(const ATransform: TMatrix4f);
begin
  TextModellerSetTransform(FHandle, @ATransform);
end;

function TTextModeller.SetFontParameters(const AParameters: TFontParameters): TFontProvider;
begin
  Result.FHandle := TUntypedObjectHandle(TextModellerSetFontParameters(FHandle, @AParameters));
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SFailedSettingTextModellerFontParameters);
end;

function TTextModeller.Extent(const AText: Utf8String; const AModifiers: PTextRenderModifiers): TPoint2f;
begin
  TextModellerExtent(FHandle, PAnsiChar(AText), @Result, AModifiers);
end;

function TTextModeller.ExtentByShape(const AText: Utf8String;
  const AModifiers: PTextRenderModifiers): TFloatRect;
begin
  TextModellerExtentByShape(FHandle, PAnsiChar(AText), @Result, AModifiers);
end;

function TTextModeller.Rects(const AText: Utf8String; const AExtent: PPoint2f; const ARects: PTextEntryRect;
  const AModifiers: PTextRenderModifiers): Integer;
begin
  Result := TextModellerRects(FHandle, AExtent, ARects, PAnsiChar(AText), AModifiers);
end;

procedure TTextModeller.Draw(const ACanvas: TCanvas; const APosition: TPoint2f; const AText: Utf8String;
  const AColors: TColorPair; const AAlignHoriz: TTextAlignment; const AAlignVert: TTextAlignment;
  const AAlignByShape: Boolean; const AModifiers: PTextRenderModifiers;
  const AEffect: TBlendingEffect);
begin
  if not TextModellerDrawToCanvas(FHandle, ACanvas.Handle, @APosition, PAnsiChar(AText), @AColors,
    AAlignHoriz, AAlignVert, AAlignByShape, AModifiers, AEffect) then
    raise EAfterwarpException.Create(SFailedRendering2DVectorBasedTextStringToCanvas);
end;

procedure TTextModeller.Draw(const ACanvasBuffer: TCanvasBuffer; const APosition: TPoint2f;
  const AText: Utf8String; const AColors: TColorPair; const AAlignHoriz: TTextAlignment;
  const AAlignVert: TTextAlignment; const AAlignByShape: Boolean;
  const AModifiers: PTextRenderModifiers);
begin
  if not TextModellerDrawToCanvasBuffer(FHandle, ACanvasBuffer.Handle, @APosition, PAnsiChar(AText),
    @AColors, AAlignHoriz, AAlignVert, AAlignByShape, AModifiers) then
    raise EAfterwarpException.Create(SFailedRendering2DVectorBasedTextStringToCanvasBuffer);
end;

procedure TTextModeller.DrawCurved(const ACanvas: TCanvas; const APosition: TPoint2f;
  const AText: Utf8String; const ARadius, AAngle: Single; const AColors: TColorPair;
  const AModifiers: PTextRenderModifiers; const AEffect: TBlendingEffect);
begin
  if not TextModellerDrawCurvedToCanvas(FHandle, ACanvas.Handle, @APosition, PAnsiChar(AText), ARadius,
    AAngle, @AColors, AModifiers, AEffect) then
    raise EAfterwarpException.Create(SFailedRenderingCurved2DVectorBasedTextStringToCanvas);
end;

procedure TTextModeller.DrawCurved(const ACanvasBuffer: TCanvasBuffer; const APosition: TPoint2f;
  const AText: Utf8String; const ARadius, AAngle: Single; const AColors: TColorPair;
  const AModifiers: PTextRenderModifiers);
begin
  if not TextModellerDrawCurvedToCanvasBuffer(FHandle, ACanvasBuffer.Handle, @APosition,
    PAnsiChar(AText), ARadius, AAngle, @AColors, AModifiers) then
    raise EAfterwarpException.Create(SFailedRenderingCurved2DVectorBasedTextStringToCanvasBuffer);
end;

procedure TTextModeller.Draw(const APosition: TVector3f; const AText: Utf8String; const ADepth: Single;
  const AColors: TColorPair; const AAlignHoriz: TTextAlignment; const AAlignVert: TTextAlignment;
  const AAlignDepth: TTextAlignment; const AAlignByShape: Boolean;
  const AModifiers: PTextRenderModifiers);
begin
  if not TextModellerDraw(FHandle, @APosition, PAnsiChar(AText), ADepth, @AColors, AAlignHoriz, AAlignVert,
    AAlignDepth, AAlignByShape, AModifiers) then
    raise EAfterwarpException.Create(SFailedPreRendering3DText);
end;

procedure TTextModeller.DrawCurved(const APosition: TVector3f; const AText: Utf8String; const ARadius,
  AAngle, ADepth: Single; const AColors: TColorPair; const AAlignDepth: TTextAlignment;
  const AModifiers: PTextRenderModifiers);
begin
  if not TextModellerDrawCurved(FHandle, @APosition, PAnsiChar(AText), ARadius, AAngle, ADepth, @AColors,
    AAlignDepth, AModifiers) then
    raise EAfterwarpException.Create(SFailedPreRenderingCurved3DText);
end;

procedure TTextModeller.DrawDepthCurved(const APosition: TVector3f; const AText: Utf8String; const ARadius,
  AAngle, ADepth: Single; const AColors: TColorPair; const AModifiers: PTextRenderModifiers);
begin
  if not TextModellerDrawDepthCurved(FHandle, @APosition, PAnsiChar(AText), ARadius, AAngle, ADepth,
    @AColors, AModifiers) then
    raise EAfterwarpException.Create(SFailedPreRenderingDepthCurved3DText);
end;

procedure TTextModeller.Clear;
begin
  TextModellerClear(FHandle);
end;

procedure TTextModeller.Prepare;
begin
  if not TextModellerPrepare(FHandle) then
    raise EAfterwarpException.Create(SCouldNotPrepareGPUBuffersWith3DTextData);
end;

procedure TTextModeller.Render(const AProgram: TProgram; const AChannel: Cardinal);
begin
  if not TextModellerRender(FHandle, AProgram.Handle, AChannel) then
    raise EAfterwarpException.Create(SCouldNotRenderGPUBuffersContaining3DTextData);
end;

procedure TTextModeller.Reset;
begin
  TextModellerReset(FHandle);
end;

procedure TTextModellerHelper.CopyToMeshBuffer(const AMeshBuffer: TMeshBuffer);
begin
  if not TextModellerCopyToMeshBuffer(FHandle, AMeshBuffer.Handle) then
    raise EAfterwarpException.Create(SCouldNotCopyGPUBuffers3DTextDataToMeshBuffer);
end;

{$ENDREGION}
{$REGION 'TGrapher'}

class function TGrapher.Create(const ADevice: TDevice): TGrapher;
begin
  Result.FHandle := GrapherCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DGraphPlottingModule);
end;

procedure TGrapher.Free;
begin
  if FHandle <> nil then
  begin
    GrapherDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TGrapher.GetDevice: TDevice;
begin
  Result.FHandle := GrapherGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TGrapher.GetTransform: TMatrix4f;
begin
  GrapherGetTransform(FHandle, @Result);
end;

procedure TGrapher.SetTransform(const ATransform: TMatrix4f);
begin
  GrapherSetTransform(FHandle, @ATransform);
end;

function TGrapher.GetBatchCount: Integer;
begin
  Result := GrapherGetBatchCount(FHandle);
end;

procedure TGrapher.BeginScene;
begin
  if not GrapherBegin(FHandle) then
    raise EAfterwarpException.Create(SCouldNotStartRenderingWith3DGraphPlottingModule);
end;

procedure TGrapher.EndScene;
begin
  GrapherEnd(FHandle);
end;

procedure TGrapher.Points(const AVertices: PVector4f; const AColors: PIntColor; const AAngles: PSingle;
  const AElementCount: Integer; const AShape: TPointShape);
begin
  GrapherPoints(FHandle, AVertices, AColors, AAngles, AElementCount, AShape);
end;

procedure TGrapher.Lines(const AVertices: PVector4f; const AColors: PIntColor; const AIndices: PLongInt;
  const AVertexCount, AIndexCount: Integer; const ACaps: TLineCaps);
begin
  GrapherLines(FHandle, AVertices, AColors, AIndices, AVertexCount, AIndexCount, ACaps);
end;

procedure TGrapher.Point(const APosition: TVector3f; const AColor: TIntColor; const ASize: Single;
  const AShape: TPointShape; const AAngle: Single);
begin
  GrapherPoint(FHandle, @APosition, AColor, ASize, AShape, AAngle);
end;

procedure TGrapher.Line(const APosition1, APosition2: TVector3f; const AColor1, AColor2: TIntColor;
  const AThickness1, AThickness2: Single; const ACaps: TLineCaps);
begin
  GrapherLine(FHandle, @APosition1, @APosition2, AColor1, AColor2, AThickness1, AThickness2, ACaps);
end;

procedure TGrapher.Arrow(const ATargetSize: TPoint2f; const AOrigin, ADestination: TVector3f;
  const AColor: TIntColor; const AThickness, ASize: Single; const ACaps: TLineCaps);
begin
  GrapherArrow(FHandle, @ATargetSize, @AOrigin, @ADestination, AColor, AThickness, ASize, ACaps);
end;

procedure TGrapher.BoundingBox(const AVolume: TMatrix4f; const AColor: TIntColor; const AThickness,
  ALength: Single; const ACaps: TLineCaps);
begin
  GrapherBoundingBox(FHandle, @AVolume, AColor, AThickness, ALength, ACaps);
end;

procedure TGrapher.DottedLine(const APosition1, APosition2: TVector3f; const AColor1, AColor2: TIntColor;
  const AThickness1, AThickness2, ASparsity: Single; const AShape: TPointShape; const AAngle: Single);
begin
  GrapherDottedLine(FHandle, @APosition1, @APosition2, AColor1, AColor2, AThickness1, AThickness2, ASparsity,
    AShape, AAngle);
end;

procedure TGrapher.Flush;
begin
  GrapherFlush(FHandle);
end;

procedure TGrapher.Reset;
begin
  GrapherReset(FHandle);
end;

{$ENDREGION}
{$REGION 'TGaussianBlur'}

class function TGaussianBlur.Create(const ADevice: TDevice; const ASigma: Single; const ASamples: Integer;
  const AHardwareFiltering: Boolean): TGaussianBlur;
begin
  Result.FHandle := GaussianBlurCreate(ADevice.Handle, ASigma, ASamples, AHardwareFiltering);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateGaussianBlurModule);
end;

procedure TGaussianBlur.Free;
begin
  if FHandle <> nil then
  begin
    GaussianBlurDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TGaussianBlur.GetDevice: TDevice;
begin
  Result.FHandle := GaussianBlurGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TGaussianBlur.GetSamples: Integer;
begin
  Result := GaussianBlurGetSamples(FHandle);
end;

procedure TGaussianBlur.SetSamples(const ASamples: Integer);
begin
  GaussianBlurSetSamples(FHandle, ASamples);
end;

function TGaussianBlur.GetSigma: Single;
begin
  Result := GaussianBlurGetSigma(FHandle);
end;

procedure TGaussianBlur.SetSigma(const ASigma: Single);
begin
  GaussianBlurSetSigma(FHandle, ASigma);
end;

function TGaussianBlur.GetChroma: Single;
begin
  Result := GaussianBlurGetChroma(FHandle);
end;

procedure TGaussianBlur.SetChroma(const AChroma: Single);
begin
  GaussianBlurSetChroma(FHandle, AChroma);
end;

procedure TGaussianBlur.Update(const ADestinationTexture, AIntermediaryTexture, ASourceTexture: TTexture);
begin
  if not GaussianBlurUpdate(FHandle, ADestinationTexture.Handle, AIntermediaryTexture.Handle,
    ASourceTexture.Handle) then
    raise EAfterwarpException.Create(SCouldNotExecuteGaussianBlurModule);
end;

procedure TGaussianBlur.Update(const ADestinationTexture, AIntermediaryTexture, ASourceTexture: TTexture;
  const ADestPos: TPoint2i; const AIntermRect: TIntRect);
begin
  if not GaussianBlurUpdateAt(FHandle, ADestinationTexture.Handle, AIntermediaryTexture.Handle,
    ASourceTexture.Handle, @ADestPos, @AIntermRect) then
  raise EAfterwarpException.Create(SCouldNotExecuteGaussianBlurModuleAtGivenRectangle);
end;

function TGaussianBlur.GetFixedSamples: Integer;
begin
  Result := GaussianBlurGetFixedSamples(FHandle);
end;

function TGaussianBlur.GetHardwareFiltering: Boolean;
begin
  Result := GaussianBlurGetHardwareFiltering(FHandle);
end;

procedure TGaussianBlur.SetHardwareFiltering(const AHardwareFiltering: Boolean);
begin
  GaussianBlurSetHardwareFiltering(FHandle, AHardwareFiltering);
end;

{$ENDREGION}
{$REGION 'TKawaseBlur'}

class function TKawaseBlur.Create(const ADevice: TDevice): TGaussianBlur;
begin
  Result.FHandle := KawaseBlurCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateKawaseBlurModule);
end;

procedure TKawaseBlur.Free;
begin
  if FHandle <> nil then
  begin
    KawaseBlurDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TKawaseBlur.GetDevice: TDevice;
begin
  Result.FHandle := KawaseBlurGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TKawaseBlur.GetPasses: Integer;
begin
  Result := KawaseBlurGetPasses(FHandle);
end;

procedure TKawaseBlur.SetPasses(const ASamples: Integer);
begin
  KawaseBlurSetPasses(FHandle, ASamples);
end;

function TKawaseBlur.GetOffset: TPoint2f;
begin
  KawaseBlurGetOffset(FHandle, @Result);
end;

procedure TKawaseBlur.SetOffset(const AOffset: TPoint2f);
begin
  KawaseBlurSetOffset(FHandle, @AOffset);
end;

procedure TKawaseBlur.Update(const ADestinationTexture, AIntermediaryTexture, ASourceTexture: TTexture);
begin
  if not KawaseBlurUpdate(FHandle, ADestinationTexture.Handle, AIntermediaryTexture.Handle,
    ASourceTexture.Handle) then
    raise EAfterwarpException.Create(SCouldNotExecuteKawaseBlurModule);
end;

procedure TKawaseBlur.SinglePass(const ADestinationTexture, ASourceTexture: TTexture;
  const AOffset: TPoint2f);
begin
  if not KawaseBlurSinglePass(FHandle, ADestinationTexture.Handle, ASourceTexture.Handle, @AOffset) then
    raise EAfterwarpException.Create(SCouldNotExecuteSinglePassKawaseBlur);
end;

{$ENDREGION}
{$REGION 'TColorDithering'}

class function TColorDithering.Create(const ADevice: TDevice): TColorDithering;
begin
  Result.FHandle := ColorDitheringCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateColorDitheringModule);
end;

procedure TColorDithering.Free;
begin
  if FHandle <> nil then
  begin
    ColorDitheringDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TColorDithering.GetDevice: TDevice;
begin
  Result.FHandle := ColorDitheringGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TColorDithering.GetFormat: TColorDitheringFormat;
begin
  Result := ColorDitheringGetFormat(FHandle);
end;

procedure TColorDithering.SetFormat(const AFormat: TColorDitheringFormat);
begin
  ColorDitheringSetFormat(FHandle, AFormat);
end;

procedure TColorDithering.Execute(const ADestinationTexture, ASourceTexture: TTexture);
begin
  if not ColorDitheringExecuteTo(FHandle, ADestinationTexture.Handle, ASourceTexture.Handle) then
    raise EAfterwarpException.Create(SCouldNotExecuteColorDitheringModule);
end;

procedure TColorDithering.Execute(const ASourceTexture: TTexture);
begin
  if not ColorDitheringExecute(FHandle, ASourceTexture.Handle) then
    raise EAfterwarpException.Create(SCouldNotExecuteColorDitheringModuleForDestinationTexture);
end;

{$ENDREGION}
{$REGION 'TSelectionHighlight'}

class function TSelectionHighlight.Create(const ADevice: TDevice; const ASize: TPoint2i;
  const ADepthStencil: TPixelFormat; const ASamples: Integer;
  const AGrayscale: Boolean): TSelectionHighlight;
begin
  Result.FHandle := SelectionHighlightCreate(ADevice.Handle, @ASize, ADepthStencil, ASamples, AGrayscale);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateSelectionHighlightModule);
end;

procedure TSelectionHighlight.Free;
begin
  if FHandle <> nil then
  begin
    SelectionHighlightDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSelectionHighlight.GetDevice: TDevice;
begin
  Result.FHandle := SelectionHighlightGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TSelectionHighlight.GetSize: TPoint2i;
begin
  SelectionHighlightGetSize(FHandle, @Result);
end;

procedure TSelectionHighlight.SetSize(const ASize: TPoint2i);
begin
  if not SelectionHighlightSetSize(FHandle, @ASize) then
    raise EAfterwarpException.Create(SCouldNotResizeSelectionHighlightTextures);
end;

function TSelectionHighlight.GetDepthStencil: TPixelFormat;
begin
  Result := SelectionHighlightGetDepthStencil(FHandle);
end;

function TSelectionHighlight.GetSamples: Integer;
begin
  Result := SelectionHighlightGetSamples(FHandle);
end;

function TSelectionHighlight.GetGrayscale: Boolean;
begin
  Result := SelectionHighlightGetGrayscale(FHandle);
end;

function TSelectionHighlight.GetParameters: TSelectionHighlightParameters;
begin
  SelectionHighlightGetParameters(FHandle, @Result);
end;

procedure TSelectionHighlight.SetParameters(const AParameters: TSelectionHighlightParameters);
begin
  if not SelectionHighlightSetParameters(FHandle, @AParameters) then
    raise EAfterwarpException.Create(SCouldNotUpdateSelectionHighlightParameters);
end;

function TSelectionHighlight.GetRendering: Boolean;
begin
  Result := SelectionHighlightRendering(FHandle);
end;

function TSelectionHighlight.GetTexture(const AType: TSelectionHighlightTextureType): TTexture;
begin
  Result.FHandle := SelectionHighlightGetTexture(FHandle, AType);
end;

function TSelectionHighlight.GetTextureCoordinates: TQuad;
begin
  SelectionHighlightGetTextureCoordinates(FHandle, @Result);
end;

procedure TSelectionHighlight.Clear;
begin
  if not SelectionHighlightClear(FHandle) then
    raise EAfterwarpException.Create(SCouldNotClearSelectionHighlightTextures);
end;

procedure TSelectionHighlight.BeginScene;
begin
  if not SelectionHighlightBegin(FHandle) then
    raise EAfterwarpException.Create(SCouldNotStartRenderingWithSelectionHighlight);
end;

procedure TSelectionHighlight.EndScene;
begin
  SelectionHighlightEnd(FHandle);
end;

procedure TSelectionHighlight.Filter;
begin
  if not SelectionHighlightFilter(FHandle) then
    raise EAfterwarpException.Create(SCouldNotPerformFilteringSelectionHighlight);
end;

{$ENDREGION}
{$REGION 'TSpatialFog'}

class function TSpatialFog.Create(const ADevice: TDevice): TSpatialFog;
begin
  Result.FHandle := SpatialFogCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateSpatialFogModule);
end;

procedure TSpatialFog.Free;
begin
  if FHandle <> nil then
  begin
    SpatialFogDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSpatialFog.GetDevice: TDevice;
begin
  Result.FHandle := SpatialFogGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TSpatialFog.GetParameters: TFogParameters;
begin
  SpatialFogGetParameters(FHandle, @Result);
end;

procedure TSpatialFog.SetParameters(const AParameters: TFogParameters);
begin
  if not SpatialFogSetParameters(FHandle, @AParameters) then
    raise EAfterwarpException.Create(SCouldNotUpdateSpatialFogParameters);
end;

function TSpatialFog.GetView: TMatrix4f;
begin
  SpatialFogGetView(FHandle, @Result);
end;

procedure TSpatialFog.SetView(const AView: TMatrix4f);
begin
  if not SpatialFogSetView(FHandle, @AView) then
    raise EAfterwarpException.Create(SCouldNotUpdateSpatialFogViewMatrix);
end;

function TSpatialFog.GetProjection: TMatrix4f;
begin
  SpatialFogGetProjection(FHandle, @Result);
end;

procedure TSpatialFog.SetProjection(const AProjection: TMatrix4f);
begin
  if not SpatialFogSetProjection(FHandle, @AProjection) then
    raise EAfterwarpException.Create(SCouldNotUpdateSpatialFogProjectionMatrix);
end;

function TSpatialFog.GetFormula: TFogFormula;
begin
  Result := SpatialFogGetFormula(FHandle);
end;

procedure TSpatialFog.SetFormula(const AFormula: TFogFormula);
begin
  if not SpatialFogSetFormula(FHandle, AFormula) then
    raise EAfterwarpException.Create(SCouldNotUpdateSpatialFogFormula);
end;

function TSpatialFog.GetDistance: TDepthFogDistance;
begin
  Result := SpatialFogGetDepthDistance(FHandle);
end;

procedure TSpatialFog.SetDistance(const ADistance: TDepthFogDistance);
begin
  if not SpatialFogSetDepthDistance(FHandle, ADistance) then
    raise EAfterwarpException.Create(SCouldNotUpdateSpatialFogDepthDistance);
end;

procedure TSpatialFog.Execute(const ADestination, ALinearDepths: TTexture);
begin
  if not SpatialFogExecute(FHandle, ADestination.Handle, ALinearDepths.Handle) then
    raise EAfterwarpException.Create(SFailedExecutingSpatialFogModule);
end;

procedure TSpatialFog.ExecuteGlassy(const ADestination, ALinearDepths: TTexture);
begin
  if not SpatialFogExecuteGlassy(FHandle, ADestination.Handle, ALinearDepths.Handle) then
    raise EAfterwarpException.Create(SFailedExecutingGlassySpatialFogModule);
end;

{$ENDREGION}
{$REGION 'TSceneLights'}

class function TSceneLights.Create(const ADevice: TDevice): TSceneLights;
begin
  Result.FHandle := SceneLightsCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateLightsManagementModule);
end;

procedure TSceneLights.Free;
begin
  if FHandle <> nil then
  begin
    SceneLightsDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSceneLights.GetDevice: TDevice;
begin
  Result.FHandle := SceneLightsGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TSceneLights.GetCount: Integer;
begin
  Result := SceneLightsGetCount(FHandle);
end;

function TSceneLights.GetItem(const AIndex: Integer): PSceneLight;
begin
  Result := SceneLightsGetElement(FHandle, AIndex);
  if Result = nil then
    raise EAfterwarpException.CreateFmt(SCannotAccess3DLightElement, [AIndex]);
end;

function TSceneLights.GetViewSize: TPoint2i;
begin
  SceneLightsGetViewSize(FHandle, @Result);
end;

procedure TSceneLights.SetViewSize(const AViewSize: TPoint2i);
begin
  SceneLightsSetViewSize(FHandle, @AViewSize);
end;

function TSceneLights.GetClusterSize: Integer;
begin
  Result := SceneLightsGetClusterSize(FHandle);
end;

procedure TSceneLights.SetClusterSize(const AClusterSize: Integer);
begin
  SceneLightsSetClusterSize(FHandle, AClusterSize);
end;

function TSceneLights.GetDepthSlices: Integer;
begin
  Result := SceneLightsGetDepthSlices(FHandle);
end;

procedure TSceneLights.SetDepthSlices(const ADepthSlices: Integer);
begin
  SceneLightsSetDepthSlices(FHandle, ADepthSlices);
end;

function TSceneLights.GetClustersCullingMode: TClustersCullingMode;
begin
  Result := SceneLightsGetCullingMode(FHandle);
end;

procedure TSceneLights.SetClustersCullingMode(const AMode: TClustersCullingMode);
begin
  SceneLightsSetCullingMode(FHandle, AMode);
end;

function TSceneLights.GetClusters: TPoint2i;
begin
  SceneLightsGetClusters(FHandle, @Result);
end;

function TSceneLights.GetIndices: TBuffer;
begin
  Result.FHandle := SceneLightsGetIndices(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveClusterIndicesFromSceneLights);
end;

function TSceneLights.Add: PSceneLight;
begin
  Result := SceneLightsAdd(FHandle);
  if Result = nil then
    raise EAfterwarpException.Create(SCouldNotAddNewLightElement);
end;

procedure TSceneLights.Erase(const AIndex: Integer);
begin
  if not SceneLightsErase(FHandle, AIndex) then
    raise EAfterwarpException.CreateFmt(SFailedErasingGiven3DLight, [AIndex]);
end;

procedure TSceneLights.Clear;
begin
  SceneLightsClear(FHandle);
end;

procedure TSceneLights.Execute(const AView, AProjection: TMatrix4f);
begin
  if not SceneLightsExecute(FHandle, @AView, @AProjection) then
    raise EAfterwarpException.Create(SFailedPerformingClustered3DLightComputation);
end;

procedure TSceneLights.RenderDebug(const AIntensity: Boolean);
begin
  if not SceneLightsRenderDebug(FHandle, AIntensity) then
    raise EAfterwarpException.Create(SFailedRenderingClustered3DLightInformation);
end;

{$ENDREGION}
{$REGION 'TObjectMaterials'}

class function TObjectMaterials.Create: TObjectMaterials;
begin
  Result.FHandle := ObjectMaterialsCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateObjectMaterialsContainer);
end;

procedure TObjectMaterials.Free;
begin
  if FHandle <> nil then
  begin
    ObjectMaterialsDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TObjectMaterials.GetCount: Integer;
begin
  Result := ObjectMaterialsGetCount(FHandle);
end;

function TObjectMaterials.GetItem(const AIndex: Integer): PObjectMaterial;
begin
  Result := ObjectMaterialsGetElement(FHandle, AIndex);
  if Result = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieve3DObjectMaterial, [AIndex]);
end;

function TObjectMaterials.Add: PObjectMaterial;
begin
  Result := ObjectMaterialsAdd(FHandle);
  if Result = nil then
    raise EAfterwarpException.Create(SCouldNotAddNew3DObjectMaterialToContainer);
end;

procedure TObjectMaterials.Erase(const AIndex: Integer);
begin
  if not ObjectMaterialsErase(FHandle, AIndex) then
    raise EAfterwarpException.CreateFmt(SFailedErasingGiven3DObjectMaterial, [AIndex]);
end;

procedure TObjectMaterials.Clear;
begin
  ObjectMaterialsClear(FHandle);
end;

{$ENDREGION}
{$REGION 'TMeshModel'}

class function TMeshModel.Create(const ADevice: TDevice; const AVertexCount: Integer;
  const AVertexElementPitch: Cardinal; const AIndexCount: Integer; const AChannel: Cardinal;
  const AInitialVertexData: Pointer; const AInitialIndexData: Pointer): TMeshModel;
begin
  Result.FHandle := MeshModelCreate(ADevice.Handle, AVertexCount, AVertexElementPitch, AIndexCount,
    AChannel, AInitialVertexData, AInitialIndexData);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DMeshModelContainer);
end;

procedure TMeshModel.Free;
begin
  if FHandle <> nil then
  begin
    MeshModelDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TMeshModel.GetRenderable: Boolean;
begin
  Result := MeshModelRenderable(FHandle);
end;

function TMeshModel.GetVertexCount: Integer;
begin
  Result := MeshModelGetVertexCount(FHandle);
end;

function TMeshModel.GetIndexCount: Integer;
begin
  Result := MeshModelGetIndexCount(FHandle);
end;

function TMeshModel.GetChannel: Cardinal;
begin
  Result := MeshModelGetChannel(FHandle);
end;

function TMeshModel.GetVertexBuffer: TBuffer;
begin
  Result.FHandle := MeshModelGetVertexBuffer(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveVertexBufferFromMeshModel);
end;

function TMeshModel.GetIndexBuffer: TBuffer;
begin
  Result.FHandle := MeshModelGetIndexBuffer(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveIndexBufferFromMeshModel);
end;

procedure TMeshModel.Dismantle;
begin
  MeshModelDismantle(FHandle);
end;

procedure TMeshModel.Draw(const AProgram: TProgram; const ATopology: TPrimitiveTopology;
  const AElementCount, AStartIndex, ABaseVertex: Integer; const APostUnbind: Boolean);
begin
  if not MeshModelDraw(FHandle, AProgram.Handle, ATopology, AElementCount, AStartIndex, ABaseVertex,
    APostUnbind) then
    raise EAfterwarpException.Create(SFailedRendering3DMeshModel);
end;

procedure TMeshModel.DrawInstances(const AProgram: TProgram; const AInstanceCount: Integer;
  const ATopology: TPrimitiveTopology; const AElementCount, AStartIndex, ABaseVertex: Integer;
  const APostUnbind: Boolean);
begin
  if not MeshModelDrawInstances(FHandle, AProgram.Handle, AInstanceCount, ATopology, AElementCount,
    AStartIndex, ABaseVertex, APostUnbind) then
    raise EAfterwarpException.Create(SFailedRenderingMultipleInstances3DMeshModel);
end;

procedure TMeshModelHelper.TakeAway(const AMeshModel: TMeshModel);
begin
  MeshModelTakeAway(FHandle, AMeshModel.Handle);
end;

{$ENDREGION}
{$REGION 'TMeshMetaTagPortionEnumerator'}

class function TMeshMetaTagPortionEnumerator.Create(
  const AHandle: TUntypedObjectHandle): TMeshMetaTagPortionEnumerator;
begin
  Result.FHandle := AHandle;
  Result.FCurrent := -1;
end;

function TMeshMetaTagPortionEnumerator.GetCurrent: TMeshMetaTagPortion;
begin
  if not MeshMetaTagPortionGet(FHandle, @Result, FCurrent) then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveMeshMetaTagPortion, [FCurrent]);
end;

function TMeshMetaTagPortionEnumerator.MoveNext: Boolean;
begin
  Inc(FCurrent);
  Result := FCurrent < MeshMetaTagGetPortionCount(FHandle);
end;

{$ENDREGION}
{$REGION 'TMeshMetaTag'}

function TMeshMetaTag.GetName: Utf8String;
var
  LLength: LongInt;
begin
  MeshMetaTagGetName(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    MeshMetaTagGetName(FHandle, @Result[1], @LLength);
end;

function TMeshMetaTag.GetTagType: TMeshMetaTagType;
begin
  Result := TMeshMetaTagType(MeshMetaTagGetType(FHandle));
end;

function TMeshMetaTag.GetPortionCount: Integer;
begin
  Result := MeshMetaTagGetPortionCount(FHandle);
end;

function TMeshMetaTag.GetPortion(const AIndex: Integer): TMeshMetaTagPortion;
begin
  if not MeshMetaTagPortionGet(FHandle, @Result, AIndex) then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveMeshMetaTagPortion, [AIndex]);
end;

procedure TMeshMetaTag.Add(const APortion: TMeshMetaTagPortion);
begin
  if not MeshMetaTagPortionAdd(FHandle, @APortion) then
    raise EAfterwarpException.Create(SCouldNotAddMeshMetaTagPortion);
end;

procedure TMeshMetaTag.Erase(const APortionIndex: Integer);
begin
  MeshMetaTagPortionErase(FHandle, APortionIndex);
end;

procedure TMeshMetaTag.Clear;
begin
  MeshMetaTagPortionsClear(FHandle);
end;

procedure TMeshMetaTag.Copy(const ATag: TMeshMetaTag);
begin
  if not MeshMetaTagPortionsCopy(FHandle, ATag.Handle) then
    raise EAfterwarpException.Create(SCouldNotCopyMeshMetaTagPortions);
end;

procedure TMeshMetaTag.GetBounds(out ABoundsMin, ABoundsMax: TVector3f);
begin
  MeshMetaTagGetBounds(FHandle, @ABoundsMin, @ABoundsMax);
end;

function TMeshMetaTag.GetEnumerator: TMeshMetaTagPortionEnumerator;
begin
  Result := TMeshMetaTagPortionEnumerator.Create(FHandle);
end;

function TMeshMetaTagHelper.GetParent: TMeshMetaTags;
begin
  Result.FHandle := MeshMetaTagGetParent(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveParentContainerMeshMetaTag);
end;

{$ENDREGION}
{$REGION 'TMeshMetaTagsEnumerator'}

class function TMeshMetaTagsEnumerator.Create(const AHandle: TUntypedObjectHandle): TMeshMetaTagsEnumerator;
begin
  Result.FHandle := AHandle;
  Result.FCurrent := -1;
end;

function TMeshMetaTagsEnumerator.GetCurrent: TMeshMetaTag;
begin
  Result.FHandle := MeshMetaTagsGetByIndex(FHandle, FCurrent);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveMeshMetaTagWithIndex, [FCurrent]);
end;

function TMeshMetaTagsEnumerator.MoveNext: Boolean;
begin
  Inc(FCurrent);
  Result := FCurrent < MeshMetaTagsCount(FHandle);
end;

{$ENDREGION}
{$REGION 'TMeshMetaTags'}

class function TMeshMetaTags.Create: TMeshMetaTags;
begin
  Result.FHandle := MeshMetaTagsCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateMeshMetaTagContainer);
end;

procedure TMeshMetaTags.Free;
begin
  if FHandle <> nil then
  begin
    MeshMetaTagsDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TMeshMetaTags.GetCount: Integer;
begin
  Result := MeshMetaTagsCount(FHandle);
end;

function TMeshMetaTags.GetTags(const AIndex: Integer): TMeshMetaTag;
begin
  Result.FHandle := MeshMetaTagsGetByIndex(FHandle, AIndex);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveMeshMetaTagWithIndex, [AIndex]);
end;

function TMeshMetaTags.GetTag(const AName: Utf8String; const ATagType: TMeshMetaTagType): TMeshMetaTag;
begin
  Result.FHandle := MeshMetaTagsGetByName(FHandle, PAnsiChar(AName), Ord(ATagType));
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveMeshMetaTagWithNameAndType,
      [AName, Ord(ATagType)]);
end;

function TMeshMetaTags.Add(const AName: Utf8String; const ATagType: TMeshMetaTagType): TMeshMetaTag;
begin
  Result.FHandle := MeshMetaTagsAdd(FHandle, PAnsiChar(AName), Ord(ATagType));
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotAddNewMeshMetaTagWithName, [AName]);
end;

procedure TMeshMetaTags.Erase(const AIndex: Integer);
begin
  MeshMetaTagsErase(FHandle, AIndex);
end;

procedure TMeshMetaTags.Clear;
begin
  MeshMetaTagsClear(FHandle);
end;

function TMeshMetaTags.GetEnumerator: TMeshMetaTagsEnumerator;
begin
  Result := TMeshMetaTagsEnumerator.Create(FHandle);
end;

procedure TMeshMetaTagsHelper.Copy(const ATags: TMeshMetaTags);
begin
  if not MeshMetaTagsCopy(FHandle, ATags.Handle) then
    raise EAfterwarpException.Create(SCouldNotCopyMeshMetaTagsFromAnotherList);
end;

procedure TMeshMetaTagsHelper.TakeAway(const ATags: TMeshMetaTags);
begin
  MeshMetaTagsTakeAway(FHandle, ATags.Handle);
end;

{$ENDREGION}
{$REGION 'TSceneMeshMaterialRangeEnumerator'}

class function TSceneMeshMaterialRangeEnumerator.Create(
  const AHandle: TUntypedObjectHandle): TSceneMeshMaterialRangeEnumerator;
begin
  Result.FHandle := AHandle;
  Result.FCurrent := -1;
end;

function TSceneMeshMaterialRangeEnumerator.GetCurrent: TSceneMeshMaterialRange;
begin
  SceneMeshMaterialGetRange(FHandle, FCurrent, @Result);
end;

function TSceneMeshMaterialRangeEnumerator.MoveNext: Boolean;
begin
  Inc(FCurrent);
  Result := FCurrent < SceneMeshMaterialGetRangeCount(FHandle);
end;

{$ENDREGION}
{$REGION 'TSceneMeshMaterial'}

function TSceneMeshMaterial.GetName: Utf8String;
var
  LLength: LongInt;
begin
  SceneMeshMaterialGetName(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    SceneMeshMaterialGetName(FHandle, @Result[1], @LLength);
end;

function TSceneMeshMaterial.GetShading: TSceneMeshMaterialShading;
begin
  SceneMeshMaterialGetShading(FHandle, @Result);
end;

procedure TSceneMeshMaterial.SetShading(const AShading: TSceneMeshMaterialShading);
begin
  SceneMeshMaterialSetShading(FHandle, @AShading);
end;

function TSceneMeshMaterial.GetTexture(const AType: TSceneMeshTexture): TTexture;
begin
  Result.FHandle := SceneMeshMaterialGetTexture(FHandle, Ord(AType));
end;

procedure TSceneMeshMaterial.SetTexture(const AType: TSceneMeshTexture; const ATexture: TTexture);
begin
  if not SceneMeshMaterialSetTexture(FHandle, ATexture.Handle, Ord(AType)) then
    raise EAfterwarpException.CreateFmt(SCouldNotAssignSceneMeshMaterialTexture, [Ord(AType)]);
end;

function TSceneMeshMaterial.GetRangeCount: Integer;
begin
  Result := SceneMeshMaterialGetRangeCount(FHandle);
end;

function TSceneMeshMaterial.GetRange(const AIndex: Integer): TSceneMeshMaterialRange;
begin
  if not SceneMeshMaterialGetRange(FHandle, AIndex, @Result) then
    raise EAfterwarpException.CreateFmt(SCouldRetrieveSceneMeshMaterialRange, [AIndex]);
end;

procedure TSceneMeshMaterial.SetRange(const AIndex: Integer; const ARange: TSceneMeshMaterialRange);
begin
  if not SceneMeshMaterialSetRange(FHandle, AIndex, @ARange) then
    raise EAfterwarpException.CreateFmt(SCouldUpdateSceneMeshMaterialRange, [AIndex]);
end;

procedure TSceneMeshMaterial.ReleaseTextures;
begin
  SceneMeshMaterialReleaseTextures(FHandle);
end;

function TSceneMeshMaterial.AddRange(const ARange: TSceneMeshMaterialRange): Integer;
begin
  Result := SceneMeshMaterialAddRange(FHandle, @ARange);
  if Result = -1 then
    raise EAfterwarpException.Create(SCouldNotAddSceneMeshMaterialRange);
end;

procedure TSceneMeshMaterial.ClearRanges;
begin
  SceneMeshMaterialClearRanges(FHandle);
end;

procedure TSceneMeshMaterial.Commit(const ADevice: TDevice;
  const AAttributes: TTextureAttributes);
begin
  if not SceneMeshMaterialCommit(FHandle, ADevice.Handle, AAttributes) then
    raise EAfterwarpException.Create(SCouldNotCommitSceneMeshMaterial);
end;

procedure TSceneMeshMaterial.Copy(const AMaterial: TSceneMeshMaterial);
begin
  if not SceneMeshMaterialCopy(FHandle, AMaterial.Handle) then
    raise EAfterwarpException.Create(SCouldNotCopySceneMeshMaterial);
end;

function TSceneMeshMaterial.GetEnumerator: TSceneMeshMaterialRangeEnumerator;
begin
  Result := TSceneMeshMaterialRangeEnumerator.Create(FHandle);
end;

{$ENDREGION}
{$REGION 'TSceneMeshMaterialsEnumerator'}

class function TSceneMeshMaterialsEnumerator.Create(
  const AHandle: TUntypedObjectHandle): TSceneMeshMaterialsEnumerator;
begin
  Result.FHandle := AHandle;
  Result.FCurrent := -1;
end;

function TSceneMeshMaterialsEnumerator.GetCurrent: TSceneMeshMaterial;
begin
  Result.FHandle := SceneMeshMaterialsGetMaterial(FHandle, FCurrent);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotGetSceneMeshMaterialForIndex, [FCurrent]);
end;

function TSceneMeshMaterialsEnumerator.MoveNext: Boolean;
begin
  Inc(FCurrent);
  Result := FCurrent < SceneMeshMaterialsGetCount(FHandle);
end;

{$ENDREGION}
{$REGION 'TSceneMeshMaterials'}

class function TSceneMeshMaterials.Create: TSceneMeshMaterials;
begin
  Result.FHandle := SceneMeshMaterialsCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateSceneMeshMaterials);
end;

procedure TSceneMeshMaterials.Free;
begin
  if FHandle <> nil then
  begin
    SceneMeshMaterialsDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSceneMeshMaterials.GetName: Utf8String;
var
  LLength: LongInt;
begin
  SceneMeshMaterialsGetName(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    SceneMeshMaterialsGetName(FHandle, @Result[1], @LLength);
end;

procedure TSceneMeshMaterials.SetName(const AName: Utf8String);
begin
  if not SceneMeshMaterialsSetName(FHandle, PAnsiChar(AName)) then
    raise EAfterwarpException.CreateFmt(SCouldNotChangeSceneMeshMaterialsName, [AName]);
end;

function TSceneMeshMaterials.GetCount: Integer;
begin
  Result := SceneMeshMaterialsGetCount(FHandle);
end;

function TSceneMeshMaterials.GetMaterial(const AIndex: Integer): TSceneMeshMaterial;
begin
  Result.FHandle := SceneMeshMaterialsGetMaterial(FHandle, AIndex);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotGetSceneMeshMaterialForIndex, [AIndex]);
end;

function TSceneMeshMaterials.GetTexturing: Boolean;
begin
  Result := SceneMeshMaterialsTexturing(FHandle);
end;

function TSceneMeshMaterials.Add(const AName: Utf8String): Integer;
begin
  Result := SceneMeshMaterialsAdd(FHandle, PAnsiChar(AName));
  if Result = -1 then
    raise EAfterwarpException.CreateFmt(SCouldNotAddSceneMeshMaterial, [AName]);
end;

procedure TSceneMeshMaterials.Erase(const AIndex: Integer);
begin
  SceneMeshMaterialsErase(FHandle, AIndex);
end;

procedure TSceneMeshMaterials.Clear;
begin
  SceneMeshMaterialsClear(FHandle);
end;

procedure TSceneMeshMaterials.Commit(const ADevice: TDevice; const AAttributes: TTextureAttributes);
begin
  if not SceneMeshMaterialsCommit(FHandle, ADevice.Handle, AAttributes) then
    raise EAfterwarpException.Create(SCouldNotCommitSceneMeshMaterials);
end;

function TSceneMeshMaterials.GetEnumerator: TSceneMeshMaterialsEnumerator;
begin
  Result := TSceneMeshMaterialsEnumerator.Create(FHandle);
end;

procedure TSceneMeshMaterialsHelper.Copy(const AMaterials: TSceneMeshMaterials);
begin
  if not SceneMeshMaterialsCopy(FHandle, AMaterials.Handle) then
    raise EAfterwarpException.Create(SCouldNotCopySceneMeshMaterials);
end;

procedure TSceneMeshMaterialsHelper.TakeAway(const AMaterials: TSceneMeshMaterials);
begin
  SceneMeshMaterialsTakeAway(FHandle, AMaterials.Handle);
end;

{$ENDREGION}
{$REGION 'TSceneMeshLatches'}

class function TSceneMeshLatches.Create: TSceneMeshLatches;
begin
  Result.FHandle := SceneMeshLatchesCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateSceneMeshLatches);
end;

procedure TSceneMeshLatches.Free;
begin
  if FHandle <> nil then
  begin
    SceneMeshLatchesDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSceneMeshLatches.GetCount: Integer;
begin
  Result := SceneMeshLatchesGetCount(FHandle);
end;

function TSceneMeshLatches.GetLatch(const AIndex: Integer): TSceneMeshLatch;
begin
  if not SceneMeshLatchesGetLatch(FHandle, AIndex, @Result) then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveLatchForTheGivenIndex, [AIndex]);
end;

procedure TSceneMeshLatches.SetLatch(const AIndex: Integer; const AValue: TSceneMeshLatch);
begin
  if not SceneMeshLatchesSetLatch(FHandle, AIndex, @AValue) then
    raise EAfterwarpException.CreateFmt(SCouldNotUpdateLatchForTheGivenIndex, [AIndex]);
end;

function TSceneMeshLatches.IndexOf(const AName: Utf8String): Integer;
begin
  Result := SceneMeshLatchesGetLatchIndex(FHandle, PAnsiChar(AName));
end;

procedure TSceneMeshLatches.Add(const AName: Utf8String; const AType: TSceneMeshLatchType;
  const AGroup: Integer; const APosition: TVector3f; const AOrientation: TQuaternion);
begin
  if SceneMeshLatchesAdd(FHandle, PAnsiChar(AName), Ord(AType), AGroup, @APosition, @AOrientation) = -1 then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateNewLatchWithName, [AName]);
end;

procedure TSceneMeshLatches.Erase(const AIndex: Integer);
begin
  SceneMeshLatchesErase(FHandle, AIndex);
end;

procedure TSceneMeshLatches.Clear;
begin
  SceneMeshLatchesClear(FHandle);
end;

procedure TSceneMeshLatches.LoadFromFile(const AFileName: Utf8String);
begin
  if not SceneMeshLatchesLoadFromFile(FHandle, PAnsiChar(AFileName)) then
    raise EAfterwarpException.CreateFmt(SCouldNotLoadLatchesFromFile, [AFileName]);
end;

procedure TSceneMeshLatches.LoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal);
begin
  if not SceneMeshLatchesLoadFromFileInMemory(FHandle, ABuffer, ABufferSize) then
    raise EAfterwarpException.Create(SCouldNotLoadLatchesFromMemory);
end;

function TSceneMeshLatches.TryLoadFromFile(const AFileName: Utf8String): Boolean;
begin
  Result := SceneMeshLatchesLoadFromFile(FHandle, PAnsiChar(AFileName));
end;

function TSceneMeshLatches.TryLoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal): Boolean;
begin
  Result := SceneMeshLatchesLoadFromFileInMemory(FHandle, ABuffer, ABufferSize);
end;

procedure TSceneMeshLatches.SaveToFile(const AFileName: Utf8String);
begin
  if not SceneMeshLatchesSaveToFile(FHandle, PAnsiChar(AFileName)) then
    raise EAfterwarpException.CreateFmt(SCouldNotSaveLatchesToFile, [AFileName]);
end;

function TSceneMeshLatches.TrySaveToFile(const AFileName: Utf8String): Boolean;
begin
  Result := SceneMeshLatchesSaveToFile(FHandle, PAnsiChar(AFileName));
end;

function TSceneMeshLatches.GetWaypointDistance(const AGroup: Integer): Single;
begin
  Result := SceneMeshLatchesGetWaypointDistance(FHandle, AGroup);
end;

procedure TSceneMeshLatches.GetWaypointCouple(out APosition: TVector3f; out AOrientation: TQuaternion;
  const ADistance: Single; const AGroup: Integer);
begin
  SceneMeshLatchesGetWaypointCouple(FHandle, AGroup, ADistance, @APosition, @AOrientation);
end;

procedure TSceneMeshLatches.InvalidateWaypoints;
begin
  SceneMeshLatchesInvalidateWaypoints(FHandle);
end;

procedure TSceneMeshLatchesHelper.Copy(const ALatches: TSceneMeshLatches);
begin
  if not SceneMeshLatchesCopy(FHandle, ALatches.Handle) then
    raise EAfterwarpException.Create(SCouldNotCopyLatchesFromAnotherCollection);
end;

procedure TSceneMeshLatchesHelper.TakeAway(const ALatches: TSceneMeshLatches);
begin
  SceneMeshLatchesTakeAway(FHandle, ALatches.Handle);
end;

{$ENDREGION}
{$REGION 'TMeshBuffer'}

class function TMeshBuffer.Create: TMeshBuffer;
begin
  Result.FHandle := MeshBufferCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DMeshBufferContainer);
end;

procedure TMeshBuffer.Free;
begin
  if FHandle <> nil then
  begin
    MeshBufferDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TMeshBuffer.GetVertices: PMeshBufferEntry;
begin
  Result := MeshBufferGetVertices(FHandle);
end;

function TMeshBuffer.GetVertexCount: Integer;
begin
  Result := MeshBufferGetVertexCount(FHandle);
end;

procedure TMeshBuffer.SetVertexCount(const AVertexCount: Integer);
begin
  if not MeshBufferSetVertexCount(FHandle, AVertexCount) then
    raise EAfterwarpException.CreateFmt(SCouldNotUpdateNumberOfVerticesInMeshBuffer, [AVertexCount]);
end;

function TMeshBuffer.GetIndices: PLongInt;
begin
  Result := MeshBufferGetIndices(FHandle);
end;

function TMeshBuffer.GetIndexCount: Integer;
begin
  Result := MeshBufferGetIndexCount(FHandle);
end;

procedure TMeshBuffer.SetIndexCount(const AIndexCount: Integer);
begin
  if not MeshBufferSetIndexCount(FHandle, AIndexCount) then
    raise EAfterwarpException.CreateFmt(SCouldNotUpdateNumberOfIndicesInMeshBuffer, [AIndexCount]);
end;

function TMeshBuffer.GetTransform: TMatrix4f;
begin
  MeshBufferGetTransform(FHandle, @Result);
end;

procedure TMeshBuffer.SetTransform(const ATransform: TMatrix4f);
begin
  MeshBufferSetTransform(FHandle, @ATransform);
end;

function TMeshBuffer.Model(const ADevice: TDevice; const AVertexElements: TVertexElements;
  const AFirstVertex, AVertexCount, AFirstIndex, AIndexCount: Integer; const AChannel: Cardinal): TMeshModel;
begin
  Result.FHandle := MeshBufferCreateModel(FHandle, ADevice.Handle, InterpretVertexElements(AVertexElements),
    Length(AVertexElements), AFirstVertex, AVertexCount, AFirstIndex, AIndexCount, AChannel);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DMeshModelFromContentsofMeshBuffer);
end;

function TMeshBuffer.Model(const ADevice: TDevice; const AVertexElements: array of TVertexElement;
  const AFirstVertex, AVertexCount, AFirstIndex, AIndexCount: Integer; const AChannel: Cardinal): TMeshModel;
begin
  Result.FHandle := MeshBufferCreateModel(FHandle, ADevice.Handle, InterpretVertexElements(AVertexElements),
    Length(AVertexElements), AFirstVertex, AVertexCount, AFirstIndex, AIndexCount, AChannel);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DMeshModelFromContentsofMeshBuffer);
end;

procedure TMeshBuffer.SuperEllipse(const ALongitudeSections, ALatitudeSections: Cardinal; const AOrigin,
  ARadius: TVector3f; const AInitLongitudeAngle, AEndLongitudeAngle, ALongitudeShape, AInitLatitudeAngle,
  AEndLatitudeAngle, ALatitudeShape: Single; const AInitTexCoord, AEndTexCoord: TPoint2f;
  const AColor: TFloatColor; const AIndicesClockwise: Boolean);
begin
  if not MeshBufferSuperEllipse(FHandle, ALongitudeSections, ALatitudeSections, @AOrigin, @ARadius,
    AInitLongitudeAngle, AEndLongitudeAngle, ALongitudeShape, AInitLatitudeAngle, AEndLatitudeAngle,
    ALatitudeShape, @AInitTexCoord, @AEndTexCoord, @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateSuperEllipseGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Geosphere(const ASubdivisions: Integer; const AColor: TFloatColor;
  const AFlatNormals: Boolean; const AIndicesClockwise: Boolean);
begin
  if not MeshBufferGeosphere(FHandle, ASubdivisions, @AColor, AFlatNormals, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateGeosphereGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Cylinder(const ARadialSections, AHeightSections: Cardinal; const AOrigin, AHorizAxis,
  AVertAxis, AHeightAxis: TVector3f; const AInitAngle, AEndAngle, AShape: Single; const AInitTexCoord,
  AEndTexCoord: TPoint2f; const ABendAngle, ALateralAngle: Single; const AColor: TFloatColor;
  const AIndicesClockwise: Boolean);
begin
  if not MeshBufferCylinder(FHandle, ARadialSections, AHeightSections, @AOrigin, @AHorizAxis, @AVertAxis,
    @AHeightAxis, AInitAngle, AEndAngle, AShape, @AInitTexCoord, @AEndTexCoord, ABendAngle, ALateralAngle,
    @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateCylinderGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Cone(const ASections: Cardinal; const AOrigin: TVector3f; const ARadius,
  AHeight: Single; const AColor: TFloatColor; const AIndicesClockwise: Boolean);
begin
  if not MeshBufferConeSimple(FHandle, ASections, @AOrigin, ARadius, AHeight, @AColor,
    AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateConeGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Disc(const ARadialSections, AInnerSections: Cardinal; const AOrigin, AHorizAxis,
  AVertAxis, ANormal: TVector3f; const AInitAngle, AEndAngle, AShape, AInitRadius: Single;
  const AInitTexCoord, AEndTexCoord: TPoint2f; const ALateralAngle: Single; const AColor: TFloatColor;
  const AIndicesClockwise: Boolean);
begin
  if not MeshBufferDisc(FHandle, ARadialSections, AInnerSections, @AOrigin, @AHorizAxis, @AVertAxis,
    @ANormal, AInitAngle, AEndAngle, AShape, AInitRadius, @AInitTexCoord, @AEndTexCoord, ALateralAngle,
    @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateDiscGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Plane(const AHorizSections, AVertSections: Cardinal; const AOrigin, AHorizAxis,
  AVertAxis, ANormal: TVector3f; const AInitTexCoord, AEndTexCoord: TPoint2f; const AColor: TFloatColor;
  const AIndicesClockwise: Boolean);
begin
  if not MeshBufferPlane(FHandle, AHorizSections, AVertSections, @AOrigin, @AHorizAxis, @AVertAxis, @ANormal,
    @AInitTexCoord, @AEndTexCoord, @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGeneratePlaneGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Cube(const AHorizSections, AVertSections, ADepthSections: Cardinal; const AOrigin,
  AHorizAxis, AVertAxis, ADepthAxis: TVector3f; const AInitTexCoord, AEndTexCoord: TPoint2f;
  const AColor: TFloatColor; const AIndicesClockwise: Boolean);
begin
  if not MeshBufferCube(FHandle, AHorizSections, AVertSections, ADepthSections, @AOrigin, @AHorizAxis,
    @AVertAxis, @ADepthAxis, @AInitTexCoord, @AEndTexCoord, @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateCubeGeometryInMeshBuffer);
end;

procedure TMeshBuffer.CubeMinimal(const AOrigin, ASize: TVector3f; const AColor: TFloatColor;
  const AIndicesClockwise: Boolean);
begin
  if not MeshBufferCubeMinimal(FHandle, @AOrigin, @ASize, @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateMinimalCubeGeometryInMeshBuffer);
end;

procedure TMeshBuffer.CubeRound(const AOrigin, ASize: TVector3f; const ARoundness: Single;
  const AColor: TFloatColor; const AIndicesClockwise: Boolean);
begin
  if not MeshBufferCubeRound(FHandle, @AOrigin, @ASize, ARoundness, @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateCubeRoundGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Torus(const AOuterSections, AInnerSections: Cardinal; const AOrigin, AHorizAxis,
  AVertAxis: TVector3f; const AInitOuterAngle, AEndOuterAngle, AOuterShape, AInitInnerAngle, AEndInnerAngle,
  AInnerShape: Single; const AInnerRadius, AInitTexCoord, AEndTexCoord: TPoint2f;
  const ALateralAngle: Single; const AColor: TFloatColor; const AIndicesClockwise: Boolean);
begin
  if not MeshBufferTorus(FHandle, AOuterSections, AInnerSections, @AOrigin, @AHorizAxis, @AVertAxis,
    AInitOuterAngle, AEndOuterAngle, AOuterShape, AInitInnerAngle, AEndInnerAngle, AInnerShape,
    @AInnerRadius, @AInitTexCoord, @AEndTexCoord, ALateralAngle, @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateTorusGeometryInMeshBuffer);
end;

procedure TMeshBuffer.TorusKnot(const AOuterSections, AInnerSections: Cardinal; const AOrigin: TVector3f;
  const AP, AQ: Integer; const AInitOuterAngle, AEndOuterAngle, AOuterRadius, AInitInnerAngle,
  AEndInnerAngle, AInnerShape: Single; const AInnerRadius, AInitTexCoord, AEndTexCoord: TPoint2f;
  const ALateralAngle: Single; const AColor: TFloatColor; const AIndicesClockwise: Boolean);
begin
  if not MeshBufferTorusKnot(FHandle, AOuterSections, AInnerSections, @AOrigin, AP, AQ, AInitOuterAngle,
    AEndOuterAngle, AOuterRadius, AInitInnerAngle, AEndInnerAngle, AInnerShape, @AInnerRadius,
    @AInitTexCoord, @AEndTexCoord, ALateralAngle, @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateTorusKnotGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Supertoroid(const AOuterSections, AInnerSections: Cardinal; const AOrigin: TVector3f;
  const AInitOuterAngle, AEndOuterAngle, AOuterRadius, AOuterShape, AInitInnerAngle, AEndInnerAngle,
  AInnerRadius, AInnerShape: Single; const AInitTexCoord, AEndTexCoord: TPoint2f; const AColor: TFloatColor;
  const AIndicesClockwise: Boolean);
begin
  if not MeshBufferSupertoroid(FHandle, AOuterSections, AInnerSections, @AOrigin, AInitOuterAngle,
    AEndOuterAngle, AOuterRadius, AOuterShape, AInitInnerAngle, AEndInnerAngle, AInnerRadius, AInnerShape,
    @AInitTexCoord, @AEndTexCoord, @AColor, AIndicesClockwise) then
    raise EAfterwarpException.Create(SCouldNotGenerateSupertoroidGeometryInMeshBuffer);
end;

procedure TMeshBuffer.FrustumVolume(const AViewProjectionInverse: TMatrix4f;
  const ADepthClipNegative: Boolean);
begin
  if not MeshBufferFrustumVolume(FHandle, @AViewProjectionInverse, ADepthClipNegative) then
    raise EAfterwarpException.Create(SCouldNotGenerateFrustumVolumeGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Extrusion(const APath: array of TPathElement; const ADepth: Single;
  const ATexCoords: TPoint2f; const AColors: TFloatColorRect; const AQuality: Boolean);
begin
  if (Length(APath) <= 0) or (not MeshBufferExtrusion(FHandle, @APath[0], Length(APath), ADepth, @ATexCoords,
    @AColors.TopLeft, AQuality)) then
    raise EAfterwarpException.Create(SCouldNotGenerateExtrusionGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Extrusion(const APath: TPathElementArray; const ADepth: Single;
  const ATexCoords: TPoint2f; const AColors: TFloatColorRect; const AQuality: Boolean);
begin
  if (Length(APath) <= 0) or (not MeshBufferExtrusion(FHandle, @APath[0], Length(APath), ADepth, @ATexCoords,
    @AColors.TopLeft, AQuality)) then
    raise EAfterwarpException.Create(SCouldNotGenerateExtrusionGeometryInMeshBuffer);
end;

procedure TMeshBuffer.TransformVertices(const AFirstVertex, AVertexCount: Integer);
begin
  MeshBufferTransformVertices(FHandle, AFirstVertex, AVertexCount);
end;

procedure TMeshBuffer.CalculateFlatNormals(const AEpsilon: Single);
begin
  if not MeshBufferCalculateFlatNormals(FHandle, AEpsilon) then
    raise EAfterwarpException.Create(SFailedCalculatingFlatNormalsForGeometryInMeshBuffer);
end;

procedure TMeshBuffer.CalculateNormals(const AFirstVertex, AVertexCount, AFirstIndex, AIndexCount: Integer);
begin
  MeshBufferCalculateNormals(FHandle, AFirstVertex, AVertexCount, AFirstIndex, AIndexCount);
end;

procedure TMeshBuffer.CalculateNormalsWeld(const AFirstVertex, AVertexCount, AFirstIndex,
  AIndexCount: Integer; const AWeldEpsilon: Single);
begin
  MeshBufferCalculateNormalsWeld(FHandle, AFirstVertex, AVertexCount, AFirstIndex, AIndexCount,
    AWeldEpsilon);
end;

procedure TMeshBuffer.InvertNormals(const AFirstVertex, AVertexCount: Integer);
begin
  MeshBufferInvertNormals(FHandle, AFirstVertex, AVertexCount);
end;

procedure TMeshBuffer.InvertIndexOrder(const AFirstIndex, AIndexCount: Integer);
begin
  MeshBufferInvertIndexOrder(FHandle, AFirstIndex, AIndexCount);
end;

procedure TMeshBuffer.CalculateBounds(var AVertexMin, AVertexMax: TVector3f; const AFirstVertex,
  AVertexCount: Integer);
begin
  MeshBufferCalculateBounds(FHandle, @AVertexMin, @AVertexMax, AFirstVertex, AVertexCount);
end;

procedure TMeshBuffer.Centralize(const AFirstVertex, AVertexCount: Integer);
begin
  MeshBufferCentralize(FHandle, AFirstVertex, AVertexCount);
end;

procedure TMeshBuffer.EliminateUnusedVertices(const AFirstVertex, AVertexCount: Integer);
begin
  MeshBufferEliminateUnusedVertices(FHandle, AFirstVertex, AVertexCount);
end;

procedure TMeshBuffer.JoinDuplicateVertices(const AFirstVertex, AVertexCount: Integer;
  const ATreshold: Single);
begin
  if not MeshBufferJoinDuplicateVertices(FHandle, AFirstVertex, AVertexCount, ATreshold) then
    raise EAfterwarpException.Create(SFailedJoiningDuplicateVerticesForGeometryInMeshBuffer);
end;

procedure TMeshBuffer.Combine(const AMeshBufferSource: TUntypedObjectHandle; const AFirstVertex,
  AVertexCount, AFirstIndex, AIndexCount: Integer);
begin
  if not MeshBufferCombine(FHandle, AMeshBufferSource, AFirstVertex, AVertexCount, AFirstIndex,
    AIndexCount) then
    raise EAfterwarpException.Create(SFailedCombiningGeometryInMeshBufferFromAnotherMeshBuffer);
end;

procedure TMeshBuffer.Clear;
begin
  MeshBufferClear(FHandle);
end;

procedure TMeshBuffer.TransferVertices(const ABuffer: Pointer; const AVertexElements: TVertexElements;
  const AFirstVertex, AVertexCount: Integer; const AChannel: Cardinal; const ASemanticIndex: Integer);
begin
  MeshBufferTransferVertexElements(FHandle, ABuffer, InterpretVertexElements(AVertexElements),
    Length(AVertexElements), AFirstVertex, AVertexCount, AChannel, ASemanticIndex);
end;

procedure TMeshBuffer.TransferVertices(const ABuffer: Pointer; const AVertexElements: array of TVertexElement;
  const AFirstVertex, AVertexCount: Integer; const AChannel: Cardinal; const ASemanticIndex: Integer);
begin
  MeshBufferTransferVertexElements(FHandle, ABuffer, InterpretVertexElements(AVertexElements),
    Length(AVertexElements), AFirstVertex, AVertexCount, AChannel, ASemanticIndex);
end;

procedure TMeshBuffer.TransferVertices(const ABuffer: TBuffer; const AVertexElements: TVertexElements;
  const AFirstVertex, AVertexCount: Integer; const AChannel, AOffset: Cardinal;
  const ASemanticIndex: Integer);
begin
  MeshBufferTransferVertices(FHandle, ABuffer.Handle, InterpretVertexElements(AVertexElements),
    Length(AVertexElements), AFirstVertex, AVertexCount, AChannel, AOffset, ASemanticIndex);
end;

procedure TMeshBuffer.TransferVertices(const ABuffer: TBuffer;
  const AVertexElements: array of TVertexElement; const AFirstVertex, AVertexCount: Integer; const AChannel,
  AOffset: Cardinal; const ASemanticIndex: Integer);
begin
  MeshBufferTransferVertices(FHandle, ABuffer.Handle, InterpretVertexElements(AVertexElements),
    Length(AVertexElements), AFirstVertex, AVertexCount, AChannel, AOffset, ASemanticIndex);
end;

procedure TMeshBuffer.TransferIndices(const ABuffer: Pointer; const APitch: Cardinal; const AFirstVertex,
  AFirstIndex, AIndexCount: Integer);
begin
  MeshBufferTransferIndexElements(FHandle, ABuffer, APitch, AFirstVertex, AFirstIndex, AIndexCount);
end;

procedure TMeshBuffer.TransferIndices(const ABuffer: TBuffer; const AFirstVertex, AFirstIndex,
  AIndexCount: Integer; const AOffset: Cardinal);
begin
  MeshBufferTransferIndices(FHandle, ABuffer.Handle, AFirstVertex, AFirstIndex, AIndexCount, AOffset);
end;

procedure TMeshBuffer.LoadFromFile(const AFileName: Utf8String; var AOptions: Cardinal;
  const AFeedback: TMeshLoadSaveFeedback; const AFeedbackUser: Pointer);
var
  LDebug: Utf8String;
  LLength: LongWord;
  LSucceeded: Boolean;
begin
  LLength := StringBufferLength;
  SetLength(LDebug, StringBufferLength);

  LSucceeded := MeshBufferLoadFromFile(FHandle, PAnsiChar(AFileName), nil, nil, @AOptions, AFeedback,
    AFeedbackUser, @LDebug[1], @LLength);

  SetLength(LDebug, LLength);

  if not LSucceeded then
    raise EAfterwarpException.CreateFmt(SCouldNotLoad3DMeshFromFile, [LDebug]);
end;

procedure TMeshBuffer.LoadFromFile(const AFileName: Utf8String; const AMaterials: TSceneMeshMaterials;
  const AMeshMetaTags: TMeshMetaTags; var AOptions: Cardinal; const AFeedback: TMeshLoadSaveFeedback;
  const AFeedbackUser: Pointer);
var
  LDebug: Utf8String;
  LLength: LongWord;
  LSucceeded: Boolean;
begin
  LLength := StringBufferLength;
  SetLength(LDebug, StringBufferLength);

  LSucceeded := MeshBufferLoadFromFile(FHandle, PAnsiChar(AFileName), AMaterials.Handle,
    AMeshMetaTags.Handle, @AOptions, AFeedback, AFeedbackUser, @LDebug[1], @LLength);

  SetLength(LDebug, LLength);

  if not LSucceeded then
    raise EAfterwarpException.CreateFmt(SCouldNotLoad3DMeshFromFile, [LDebug]);
end;

function TMeshBuffer.TryLoadFromFile(const AFileName: Utf8String; out ADebug: Utf8String;
  var AOptions: Cardinal; const AFeedback: TMeshLoadSaveFeedback; const AFeedbackUser: Pointer): Boolean;
begin
  Result := TryLoadFromFile(AFileName, TSceneMeshMaterials(NullObject), TMeshMetaTags(NullObject), ADebug,
    AOptions, AFeedback, AFeedbackUser);
end;

function TMeshBuffer.TryLoadFromFile(const AFileName: Utf8String; const AMaterials: TSceneMeshMaterials;
  const AMeshMetaTags: TMeshMetaTags; out ADebug: Utf8String; var AOptions: Cardinal;
  const AFeedback: TMeshLoadSaveFeedback; const AFeedbackUser: Pointer): Boolean;
var
  LLength: LongWord;
begin
  LLength := StringBufferLength;
  SetLength(ADebug, StringBufferLength);

  Result := MeshBufferLoadFromFile(FHandle, PAnsiChar(AFileName), AMaterials.Handle, AMeshMetaTags.Handle,
    @AOptions, AFeedback, AFeedbackUser, @ADebug[1], @LLength);

  SetLength(ADebug, LLength);
end;

procedure TMeshBuffer.SaveToFile(const AFileName: Utf8String; const AMaterials: TSceneMeshMaterials;
  const AMeshMetaTags: TMeshMetaTags; const AOptions: Cardinal; const AFeedback: TMeshLoadSaveFeedback;
  const AFeedbackUser: Pointer);
var
  LDebug: Utf8String;
  LLength: LongWord;
  LSucceeded: Boolean;
begin
  LLength := StringBufferLength;
  SetLength(LDebug, StringBufferLength);

  LSucceeded := MeshBufferSaveToFile(FHandle, PAnsiChar(AFileName), AMaterials.Handle, AMeshMetaTags.Handle,
    AOptions, AFeedback, AFeedbackUser, @LDebug[1], @LLength);

  SetLength(LDebug, LLength);

  if not LSucceeded then
    raise EAfterwarpException.CreateFmt(SCouldNotSave3DMeshToFile, [LDebug]);
end;

function TMeshBuffer.TrySaveToFile(const AFileName: Utf8String; const AMaterials: TSceneMeshMaterials;
  const AMeshMetaTags: TMeshMetaTags; out ADebug: Utf8String; const AOptions: Cardinal;
  const AFeedback: TMeshLoadSaveFeedback; const AFeedbackUser: Pointer): Boolean;
var
  LLength: Cardinal;
begin
  LLength := StringBufferLength;
  SetLength(ADebug, StringBufferLength);

  Result := MeshBufferSaveToFile(FHandle, PAnsiChar(AFileName), AMaterials.Handle, AMeshMetaTags.Handle,
    AOptions, AFeedback, AFeedbackUser, @ADebug[1], @LLength);

  SetLength(ADebug, LLength);
end;

procedure TMeshBuffer.LoadFromFileEx(const AFileName: Utf8String; var AMinBounds,
  AMaxBounds: TVector3f; const ANormals: Boolean);
var
  LDebug: Utf8String;
  LLength: LongWord;
  LSucceeded: Boolean;
begin
  LLength := StringBufferLength;
  SetLength(LDebug, StringBufferLength);

  LSucceeded := MeshBufferLoadFromFileEx(FHandle, nil, PAnsiChar(AFileName), @AMinBounds, @AMaxBounds,
    ANormals, @LDebug[1], @LLength);

  SetLength(LDebug, LLength);

  if not LSucceeded then
    raise EAfterwarpException.CreateFmt(SCouldNotLoad3DMeshFromFile, [LDebug]);
end;

procedure TMeshBuffer.LoadFromFileEx(const AMeshMetaTags: TMeshMetaTags; const AFileName: Utf8String;
  var AMinBounds, AMaxBounds: TVector3f; const ANormals: Boolean);
var
  LDebug: Utf8String;
  LLength: LongWord;
  LSucceeded: Boolean;
begin
  LLength := StringBufferLength;
  SetLength(LDebug, StringBufferLength);

  LSucceeded := MeshBufferLoadFromFileEx(FHandle, AMeshMetaTags.Handle, PAnsiChar(AFileName), @AMinBounds,
    @AMaxBounds, ANormals, @LDebug[1], @LLength);

  SetLength(LDebug, LLength);

  if not LSucceeded then
    raise EAfterwarpException.CreateFmt(SCouldNotLoad3DMeshFromFile, [LDebug]);
end;

function TMeshBuffer.TryLoadFromFileEx(const AFileName: Utf8String; var AMinBounds,
  AMaxBounds: TVector3f; out ADebug: Utf8String; const ANormals: Boolean): Boolean;
begin
  Result := TryLoadFromFileEx(TMeshMetaTags(NullObject), AFileName, AMinBounds, AMaxBounds, ADebug);
end;

function TMeshBuffer.TryLoadFromFileEx(const AMeshMetaTags: TMeshMetaTags; const AFileName: Utf8String;
  var AMinBounds, AMaxBounds: TVector3f; out ADebug: Utf8String; const ANormals: Boolean): Boolean;
var
  LLength: Cardinal;
begin
  LLength := StringBufferLength;
  SetLength(ADebug, StringBufferLength);

  Result := MeshBufferLoadFromFileEx(FHandle, AMeshMetaTags.Handle, PAnsiChar(AFileName), @AMinBounds,
    @AMaxBounds, ANormals, @ADebug[1], @LLength);

  SetLength(ADebug, LLength);
end;

procedure TMeshBuffer.SaveToFileEx(const AFileName: Utf8String; const AOptions: Cardinal);
var
  LDebug: Utf8String;
  LLength: LongWord;
  LSucceeded: Boolean;
begin
  LLength := StringBufferLength;
  SetLength(LDebug, StringBufferLength);

  LSucceeded := MeshBufferSaveToFileEx(FHandle, PAnsiChar(AFileName), @AOptions, @LDebug[1], @LLength);
  SetLength(LDebug, LLength);

  if not LSucceeded then
    raise EAfterwarpException.CreateFmt(SCouldNotSave3DMeshToFile, [LDebug]);
end;

function TMeshBuffer.TrySaveToFileEx(const AFileName: Utf8String; out ADebug: Utf8String;
  const AOptions: Cardinal): Boolean;
var
  LLength: LongWord;
begin
  LLength := StringBufferLength;
  SetLength(ADebug, StringBufferLength);

  Result := MeshBufferSaveToFileEx(FHandle, PAnsiChar(AFileName), @AOptions, @ADebug[1], @LLength);
  SetLength(ADebug, LLength);
end;

function TMeshBufferHelper.Voxelize(const ALevels: Byte; const AColors: Boolean): TMeshVoxel;
begin
  Result.FHandle := MeshBufferVoxelize(FHandle, ALevels, AColors);
  if Result.Handle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotVoxelizeMeshBuffer, [ALevels, Ord(AColors)]);
end;

{$ENDREGION}
{$REGION 'TTextureCabinet'}

class function TTextureCabinet.Create(const ADevice: TDevice; const ASize: TPoint2i;
  const AFidelity: TTextureFidelity; const ASamples: Integer;
  const ADepthStencil: TPixelFormat): TTextureCabinet;
begin
  Result.FHandle := TextureCabinetCreate(ADevice.Handle, @ASize, AFidelity, ASamples, ADepthStencil);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateTextureContainerManagementModule);
end;

procedure TTextureCabinet.Free;
begin
  if FHandle <> nil then
  begin
    TextureCabinetDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TTextureCabinet.GetDevice: TDevice;
begin
  Result.FHandle := TextureCabinetGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TTextureCabinet.GetSize: TPoint2i;
begin
  TextureCabinetGetSize(FHandle, @Result);
end;

procedure TTextureCabinet.SetSize(const ASize: TPoint2i);
begin
  if not TextureCabinetSetSize(FHandle, @ASize) then
    raise EAfterwarpException.CreateFmt(SFailedChangingTextureCabinetToSize, [ASize.X, ASize.Y]);
end;

function TTextureCabinet.GetSamples: Integer;
begin
  Result := TextureCabinetGetSamples(FHandle);
end;

function TTextureCabinet.GetDepthStencil: TPixelFormat;
begin
  Result := TextureCabinetGetDepthStencil(FHandle);
end;

function TTextureCabinet.GetFidelity: TTextureFidelity;
begin
  Result := TextureCabinetGetFidelity(FHandle);
end;

function TTextureCabinet.GetAttributes: TTextureCabinetAttributes;
begin
  Result := TextureCabinetGetAttributes(FHandle);
end;

procedure TTextureCabinet.SetAttributes(const AAttributes: TTextureCabinetAttributes);
begin
  if not TextureCabinetSetAttributes(FHandle, AAttributes) then
    raise EAfterwarpException.Create(SFailedUpdatingTextureCabinetRenderingAttributes);
end;

function TTextureCabinet.GetRendering: Boolean;
begin
  Result := TextureCabinetRendering(FHandle);
end;

function TTextureCabinet.GetToneMappingBloom: TToneMappingBloom;
begin
  TextureCabinetGetToneMappingBloom(FHandle, @Result);
end;

procedure TTextureCabinet.SetToneMappingBloom(const AToneMappingBloom: TToneMappingBloom);
begin
  TextureCabinetSetToneMappingBloom(FHandle, @AToneMappingBloom);
end;

function TTextureCabinet.GetAmbientOcclusion: TAmbientOcclusionParameters;
begin
  TextureCabinetGetAmbientOcclusionParameters(FHandle, @Result);
end;

procedure TTextureCabinet.SetAmbientOcclusion(const AAmbientOcclusion: TAmbientOcclusionParameters);
begin
  TextureCabinetSetAmbientOcclusionParameters(FHandle, @AAmbientOcclusion);
end;

function TTextureCabinet.GetFinalTexture: TTexture;
begin
  Result.FHandle := TextureCabinetGetFinalTexture(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveFinalColorTextureFromTextureCabinet);
end;

procedure TTextureCabinet.Clear(const APass: TTextureCabinetPass; const ABackground: TFloatColor);
begin
  if not TextureCabinetClear(FHandle, APass, @ABackground) then
    raise EAfterwarpException.CreateFmt(SCouldClearTextureCabinetForPassColor, [Ord(APass)]);
end;

procedure TTextureCabinet.Clear(const APass: TTextureCabinetPass);
begin
  if not TextureCabinetClear(FHandle, APass, nil) then
    raise EAfterwarpException.CreateFmt(SCouldClearTextureCabinetForPass, [Ord(APass)]);
end;

procedure TTextureCabinet.BeginScene(const APass: TTextureCabinetPass);
begin
  if not TextureCabinetBegin(FHandle, APass) then
    raise EAfterwarpException.CreateFmt(SCouldNotBeginRenderingTextureCabinetGivenPass, [Ord(APass)]);
end;

procedure TTextureCabinet.EndScene;
begin
  TextureCabinetEnd(FHandle);
end;

procedure TTextureCabinet.Filter(const AFilter: TTextureCabinetFilterType; const AProjection: TMatrix4f);
begin
  if not TextureCabinetFilter(FHandle, AFilter, @AProjection) then
    raise EAfterwarpException.CreateFmt(SCouldNotPerformPostProcessingTextureCabinetFilter, [Ord(AFilter)]);
end;

procedure TTextureCabinet.Filter(const AFilter: TTextureCabinetFilterType);
begin
  if not TextureCabinetFilter(FHandle, AFilter, nil) then
    raise EAfterwarpException.CreateFmt(SCouldNotPerformPostProcessingTextureCabinetFilter, [Ord(AFilter)]);
end;

procedure TTextureCabinet.Resolve;
begin
  if not TextureCabinetResolve(FHandle) then
    raise EAfterwarpException.Create(SCouldNotRenderMultisampleHDRTextureToFinalColorTexture);
end;

procedure TTextureCabinet.Present;
begin
  if not TextureCabinetPresent(FHandle) then
    raise EAfterwarpException.Create(SCouldNotRenderFinalColorTextureOnActiveRenderingSurface);
end;

function TTextureCabinet.GetTexture(const AType: TTextureCabinetType): TTexture;
begin
  Result.FHandle := TextureCabinetGetTexture(FHandle, AType);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveTextureFromTextureCabinet, [Ord(AType)]);
end;

{$ENDREGION}
{$REGION 'TMeshVoxel'}

class function TMeshVoxel.Create: TMeshVoxel;
begin
  Result.FHandle := MeshVoxelCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateEmptyMeshVoxel);
end;

procedure TMeshVoxel.Free;
begin
  if FHandle <> nil then
  begin
    MeshVoxelDestroy(FHandle);
    FHandle := nil;
  end;
end;

procedure TMeshVoxel.LoadFromFile(const AFileName: Utf8String; out ADimensions: TVector3iArray;
  out ALevels: Byte; out AColors: Boolean);
var
  LColors: TLibraryBool;
begin
  if not MeshVoxelLoadFromFile(FHandle, PAnsiChar(AFileName), @ADimensions[0], @ALevels, @LColors) then
    raise EAfterwarpException.CreateFmt(SCouldNotLoadMeshVoxelFromFile, [AFileName]);

  AColors := LColors;
end;

procedure TMeshVoxel.LoadFromFile(const AFileName: Utf8String);
begin
  if not MeshVoxelLoadFromFile(FHandle, PAnsiChar(AFileName), nil, nil, nil) then
    raise EAfterwarpException.CreateFmt(SCouldNotLoadMeshVoxelFromFile, [AFileName]);
end;

function TMeshVoxel.TryLoadFromFile(const AFileName: Utf8String; out ADimensions: TVector3iArray;
  out ALevels: Byte; out AColors: Boolean): Boolean;
var
  LColors: TLibraryBool;
begin
  Result := MeshVoxelLoadFromFile(FHandle, PAnsiChar(AFileName), @ADimensions[0], @ALevels, @LColors);
  AColors := LColors;
end;

function TMeshVoxel.TryLoadFromFile(const AFileName: Utf8String): Boolean;
begin
  Result := MeshVoxelLoadFromFile(FHandle, PAnsiChar(AFileName), nil, nil, nil);
end;

procedure TMeshVoxel.LoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal;
  out ADimensions: TVector3iArray; out ALevels: Byte; out AColors: Boolean);
var
  LColors: TLibraryBool;
begin
  if not MeshVoxelLoadFromFileInMemory(FHandle, ABuffer, ABufferSize, @ADimensions[0], @ALevels,
    @LColors) then
    raise EAfterwarpException.Create(SCouldNotLoadMeshVoxelFromMemory);

  AColors := LColors;
end;

procedure TMeshVoxel.LoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal);
begin
  if not MeshVoxelLoadFromFileInMemory(FHandle, ABuffer, ABufferSize, nil, nil, nil) then
    raise EAfterwarpException.Create(SCouldNotLoadMeshVoxelFromMemory);
end;

function TMeshVoxel.TryLoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal;
  out ADimensions: TVector3iArray; out ALevels: Byte; out AColors: Boolean): Boolean;
var
  LColors: TLibraryBool;
begin
  Result := MeshVoxelLoadFromFileInMemory(FHandle, ABuffer, ABufferSize, @ADimensions[0], @ALevels,
    @LColors);
  AColors := LColors;
end;

function TMeshVoxel.TryLoadFromMemory(const ABuffer: Pointer; const ABufferSize: Cardinal): Boolean;
begin
  Result := MeshVoxelLoadFromFileInMemory(FHandle, ABuffer, ABufferSize, nil, nil, nil);
end;

procedure TMeshVoxel.SaveToFile(const AFileName: Utf8String; const ADimensions: TVector3iArray;
  const ALevels: Byte; const AColors: Boolean);
begin
  if not MeshVoxelSaveToFile(FHandle, PAnsiChar(AFileName), @ADimensions[0], ALevels, AColors) then
    raise EAfterwarpException.CreateFmt(SCouldNotSaveMeshVoxelToFile, [AFileName]);
end;

function TMeshVoxel.TrySaveToFile(const AFileName: Utf8String; const ADimensions: TVector3iArray;
  const ALevels: Byte; const AColors: Boolean): Boolean;
begin
  Result := MeshVoxelSaveToFile(FHandle, PAnsiChar(AFileName), @ADimensions[0], ALevels, AColors);
end;

procedure TMeshVoxel.ComputeParameters(out ADimensions: TVector3iArray; out ALevels: Byte;
  out AColors: Boolean);
var
  LColors: TLibraryBool;
begin
  MeshVoxelComputeParameters(FHandle, @ADimensions[0], @ALevels, @LColors);
  AColors := LColors;
end;

procedure TMeshVoxel.Extents(out APosition, ASize: TVector3f);
begin
  MeshVoxelExtents(FHandle, @APosition, @ASize);
end;

procedure TMeshVoxel.Visualize(const ALevelMax: Byte; const AVisualizeFunc: TMeshVoxelVisualizeFunc;
  const AUser: Pointer);
begin
  MeshVoxelVisualize(FHandle, ALevelMax, AVisualizeFunc, AUser);
end;

function TMeshVoxel.Intersect(const ARay: TRay; const AWorld, AView: TMatrix4f;
  out ADistance: Single; const ATestsCount: PLongInt): Boolean;
begin
  Result := MeshVoxelIntersect(FHandle, @ARay.Origin, @ARay.Direction, @AWorld, @AView, @ADistance,
    ATestsCount);
end;

function TMeshVoxel.Intersect(const ARay: TRay; const AWorld, AView: TMatrix4f;
  const ATestsCount: PLongInt): Boolean;
begin
  Result := MeshVoxelIntersect(FHandle, @ARay.Origin, @ARay.Direction, @AWorld, @AView, nil, ATestsCount);
end;

procedure TMeshVoxelHelper.TakeAway(const AMeshVoxel: TMeshVoxel);
begin
  MeshVoxelTakeAway(FHandle, AMeshVoxel.Handle);
end;

{$ENDREGION}
{$REGION 'TSceneMesh'}

function TSceneMesh.GetName: Utf8String;
var
  LLength: Integer;
begin
  SceneMeshGetName(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    SceneMeshGetName(FHandle, @Result[1], @LLength);
end;

function TSceneMesh.GetPayload: TObjectPayload;
begin
  Result := SceneMeshGetPayload(FHandle);
end;

function TSceneMesh.GetModel: TMeshModel;
begin
  Result.FHandle := SceneMeshGetModel(FHandle);
end;

function TSceneMesh.GetVoxel: TMeshVoxel;
begin
  Result.FHandle := SceneMeshGetVoxel(FHandle);
end;

function TSceneMesh.GetMaterials: TSceneMeshMaterials;
begin
  Result.FHandle := SceneMeshGetMaterials(FHandle);
end;

function TSceneMesh.GetTags: TMeshMetaTags;
begin
  Result.FHandle := SceneMeshGetTags(FHandle);
end;

function TSceneMesh.GetLatches: TSceneMeshLatches;
begin
  Result.FHandle := SceneMeshGetLatches(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveSceneMeshLatches);
end;

function TSceneMesh.GetMinBounds: TVector3f;
begin
  SceneMeshGetBounds(FHandle, @Result, nil);
end;

function TSceneMesh.GetMaxBounds: TVector3f;
begin
  SceneMeshGetBounds(FHandle, nil, @Result);
end;

function TSceneMesh.GetScale: Single;
begin
  Result := SceneMeshGetScale(FHandle);
end;

function TSceneMesh.GetSize: TVector3f;
begin
  SceneMeshGetSize(FHandle, @Result);
end;

function TSceneMesh.GetVertexElementsIndex: Byte;
begin
  Result := SceneMeshGetVertexElementsIndex(FHandle);
end;

procedure TSceneMesh.SetVertexElementsIndex(const AVertexElementsIndex: Byte);
begin
  SceneMeshSetVertexElementsIndex(FHandle, AVertexElementsIndex);
end;

procedure TSceneMeshHelper.SetSlice(const AParent: TSceneMesh; const ATag: TMeshMetaTag);
begin
  SceneMeshSetSlice(FHandle, AParent.Handle, ATag.Handle);
end;

procedure TSceneMeshHelper.GetSlice(out AParent: TSceneMesh; out ATag: TMeshMetaTag);
begin
  SceneMeshGetSlice(FHandle, @AParent.FHandle, @ATag.FHandle);
end;

function TSceneMeshHelper.GetSlice: TSceneMesh;
begin
  SceneMeshGetSlice(FHandle, @Result.FHandle, nil);
end;

function TSceneMeshHelper.AutoDraw(const AScene: TScene; const AMaterial: TObjectMaterial;
  const AColor: TFloatColor; const AOptions: Cardinal; const ATopology: TPrimitiveTopology;
  const AInstanceCount, AElementCount, AFirstIndex, ABaseVertex: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDraw(FHandle, AScene.Handle, @AMaterial, @AColor, ATopology, AInstanceCount,
    AElementCount, AFirstIndex, ABaseVertex, AOptions, PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawSceneMesh);
end;

function TSceneMeshHelper.AutoDraw(const AScene: TScene; const AMaterial: TObjectMaterial;
  const AOptions: Cardinal; const ATopology: TPrimitiveTopology; const AInstanceCount, AElementCount,
  AFirstIndex, ABaseVertex: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDraw(FHandle, AScene.Handle, @AMaterial, nil, ATopology, AInstanceCount, AElementCount,
    AFirstIndex, ABaseVertex, AOptions, PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawSceneMesh);
end;

function TSceneMeshHelper.AutoDraw(const AScene: TScene; const AColor: TFloatColor; const AOptions: Cardinal;
  const ATopology: TPrimitiveTopology; const AInstanceCount, AElementCount, AFirstIndex,
  ABaseVertex: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDraw(FHandle, AScene.Handle, nil, @AColor, ATopology, AInstanceCount, AElementCount,
    AFirstIndex, ABaseVertex, AOptions, PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawSceneMesh);
end;

function TSceneMeshHelper.AutoDraw(const AScene: TScene; const AOptions: Cardinal;
  const ATopology: TPrimitiveTopology; const AInstanceCount, AElementCount, AFirstIndex,
  ABaseVertex: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDraw(FHandle, AScene.Handle, nil, nil, ATopology, AInstanceCount, AElementCount,
    AFirstIndex, ABaseVertex, AOptions, PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawSceneMesh);
end;

function TSceneMeshHelper.MeasureAutoDraw(const AOptions: Cardinal; const AElementCount,
  AFirstIndex: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDraw(FHandle, nil, nil, nil, TPrimitiveTopology.Unknown, 0, AElementCount, AFirstIndex,
    0, AOptions, PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotMeasureAutoDrawSceneMesh);
end;

function TSceneMeshHelper.AutoDrawSliced(const AScene: TScene; const AMaterial: TObjectMaterial;
  const AColor: TFloatColor; const AOptions: Cardinal; const ATopology: TPrimitiveTopology;
  const AInstanceCount: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDrawSliced(FHandle, AScene.Handle, @AMaterial, @AColor, ATopology, AInstanceCount,
    AOptions, PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawSlicedSceneMesh);
end;

function TSceneMeshHelper.AutoDrawSliced(const AScene: TScene; const AMaterial: TObjectMaterial;
  const AOptions: Cardinal; const ATopology: TPrimitiveTopology; const AInstanceCount: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDrawSliced(FHandle, AScene.Handle, @AMaterial, nil, ATopology, AInstanceCount,
    AOptions, PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawSlicedSceneMesh);
end;

function TSceneMeshHelper.AutoDrawSliced(const AScene: TScene; const AColor: TFloatColor;
  const AOptions: Cardinal; const ATopology: TPrimitiveTopology; const AInstanceCount: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDrawSliced(FHandle, AScene.Handle, nil, @AColor, ATopology, AInstanceCount, AOptions,
    PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawSlicedSceneMesh);
end;

function TSceneMeshHelper.AutoDrawSliced(const AScene: TScene; const AOptions: Cardinal;
  const ATopology: TPrimitiveTopology; const AInstanceCount: Integer): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDrawSliced(FHandle, AScene.Handle, nil, nil, ATopology, AInstanceCount, AOptions,
    PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawSlicedSceneMesh);
end;

function TSceneMeshHelper.MeasureAutoDrawSliced(const AOptions: Cardinal): Integer;
begin
  Result := 0;

  if not SceneMeshAutoDrawSliced(FHandle, nil, nil, nil, TPrimitiveTopology.Unknown, 0, AOptions,
    PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotMeasureAutoDrawSlicedSceneMesh);
end;

{$ENDREGION}
{$REGION 'TSceneMeshesEnumerator'}

class function TSceneMeshesEnumerator.Create(const AHandle: TUntypedObjectHandle): TSceneMeshesEnumerator;
begin
  Result.FHandle := AHandle;
  Result.FCurrent := -1;
end;

function TSceneMeshesEnumerator.GetCurrent: TSceneMesh;
begin
  Result.FHandle := SceneMeshesGetMeshByIndex(FHandle, FCurrent);
end;

function TSceneMeshesEnumerator.MoveNext: Boolean;
begin
  Inc(FCurrent);
  Result := FCurrent < SceneMeshesGetCount(FHandle);
end;

{$ENDREGION}
{$REGION 'TSceneMeshes'}

class function TSceneMeshes.Create(const ADevice: TDevice): TSceneMeshes;
begin
  Result.FHandle := SceneMeshesCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateCollectionOf3DSceneMeshes);
end;

procedure TSceneMeshes.Free;
begin
  if FHandle <> nil then
  begin
    SceneMeshesDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TSceneMeshes.GetDevice: TDevice;
begin
  Result.FHandle := SceneMeshesGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TSceneMeshes.GetCount: Integer;
begin
  Result := SceneMeshesGetCount(FHandle);
end;

function TSceneMeshes.GetMesh(const AIndex: Integer): TSceneMesh;
begin
  Result.FHandle := SceneMeshesGetMeshByIndex(FHandle, AIndex);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieve3DSceneMeshAtIndex, [AIndex]);
end;

function TSceneMeshes.GetMeshByName(const AName: Utf8String): TSceneMesh;
begin
  Result.FHandle := SceneMeshesGetMeshByName(FHandle, PAnsiChar(AName));
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieve3DSceneMeshWithName, [AName]);
end;

function TSceneMeshes.GetPayload(const APayload: TObjectPayload): TSceneMesh;
begin
  Result.FHandle := SceneMeshesPayload(FHandle, APayload);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieve3DSceneMeshWithPayload);
end;

function TSceneMeshes.Add(const AName: Utf8String; const APayload: TObjectPayload; const AMinBounds,
  AMaxBounds: TVector3f; const AScale: Single): TSceneMesh;
begin
  Result.FHandle := SceneMeshesAdd(FHandle, PAnsiChar(AName), APayload, @AMinBounds, @AMaxBounds, AScale);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateNew3DSceneMesh, [AName]);
end;

function TSceneMeshes.AddFromBuffer(const AName: Utf8String; const AMeshBuffer: TMeshBuffer;
  const APayload: TObjectPayload; const AVertexElements: TVertexElements; const ABoundsMin,
  ABoundsMax: TVector3f; const AChannel, ASemanticIndex: Cardinal; const AScale: Single): TSceneMesh;
begin
  Result.FHandle := SceneMeshesAddFromBuffer(FHandle, PAnsiChar(AName), AMeshBuffer.Handle, APayload,
    InterpretVertexElements(AVertexElements), Length(AVertexElements), @ABoundsMin, @ABoundsMax, AChannel,
    ASemanticIndex, AScale);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateNew3DSceneMeshFromBuffer, [AName]);
end;

function TSceneMeshes.AddFromBuffer(const AName: Utf8String; const AMeshBuffer: TMeshBuffer;
  const APayload: TObjectPayload; const AVertexElements: array of TVertexElement; const ABoundsMin,
  ABoundsMax: TVector3f; const AChannel, ASemanticIndex: Cardinal; const AScale: Single): TSceneMesh;
begin
  Result.FHandle := SceneMeshesAddFromBuffer(FHandle, PAnsiChar(AName), AMeshBuffer.Handle, APayload,
    InterpretVertexElements(AVertexElements), Length(AVertexElements), @ABoundsMin, @ABoundsMax, AChannel,
    ASemanticIndex, AScale);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateNew3DSceneMeshFromBuffer, [AName]);
end;

function TSceneMeshes.AddFromBuffer(const AName: Utf8String; const AMeshBuffer: TMeshBuffer;
  const APayload: TObjectPayload; const AVertexElements: TVertexElements; const AChannel,
  ASemanticIndex: Cardinal; const AScale: Single): TSceneMesh;
begin
  Result.FHandle := SceneMeshesAddFromBuffer(FHandle, PAnsiChar(AName), AMeshBuffer.Handle, APayload,
    InterpretVertexElements(AVertexElements), Length(AVertexElements), nil, nil, AChannel, ASemanticIndex,
    AScale);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateNew3DSceneMeshFromBuffer, [AName]);
end;

function TSceneMeshes.AddFromBuffer(const AName: Utf8String; const AMeshBuffer: TMeshBuffer;
  const APayload: TObjectPayload; const AVertexElements: array of TVertexElement; const AChannel,
  ASemanticIndex: Cardinal; const AScale: Single): TSceneMesh;
begin
  Result.FHandle := SceneMeshesAddFromBuffer(FHandle, PAnsiChar(AName), AMeshBuffer.Handle, APayload,
    InterpretVertexElements(AVertexElements), Length(AVertexElements), nil, nil, AChannel, ASemanticIndex,
    AScale);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotCreateNew3DSceneMeshFromBuffer, [AName]);
end;

function TSceneMeshes.TryAddFromFile(const AName, AFileName: Utf8String; const APayload: TObjectPayload;
  const AVertexElements: TVertexElements; const AChannel, ASemanticIndex: Cardinal; var AOptions: Cardinal;
  const AScale: Single; out ADebug: Utf8String; const AFeedback: TMeshLoadSaveFeedback;
  const AFeedbackUser: Pointer): TSceneMesh;
var
  LLength: Cardinal;
begin
  LLength := StringBufferLength;
  SetLength(ADebug, StringBufferLength);

  Result.FHandle := SceneMeshesAddFromFile(FHandle, PAnsiChar(AName), PAnsiChar(AFileName), APayload,
    InterpretVertexElements(AVertexElements), Length(AVertexElements), AChannel, ASemanticIndex, @AOptions,
    AScale, AFeedback, AFeedbackUser, @ADebug[1], @LLength);

  SetLength(ADebug, LLength);
end;

function TSceneMeshes.TryAddFromFile(const AName, AFileName: Utf8String; const APayload: TObjectPayload;
  const AVertexElements: array of TVertexElement; const AChannel, ASemanticIndex: Cardinal;
  var AOptions: Cardinal; const AScale: Single; out ADebug: Utf8String;
  const AFeedback: TMeshLoadSaveFeedback; const AFeedbackUser: Pointer): TSceneMesh;
var
  LLength: Cardinal;
begin
  LLength := StringBufferLength;
  SetLength(ADebug, StringBufferLength);

  Result.FHandle := SceneMeshesAddFromFile(FHandle, PAnsiChar(AName), PAnsiChar(AFileName), APayload,
    InterpretVertexElements(AVertexElements), Length(AVertexElements), AChannel, ASemanticIndex, @AOptions,
    AScale, AFeedback, AFeedbackUser, @ADebug[1], @LLength);

  SetLength(ADebug, LLength);
end;

function TSceneMeshes.AddFromFile(const AName, AFileName: Utf8String; const APayload: TObjectPayload;
  const AVertexElements: TVertexElements; const AChannel, ASemanticIndex: Cardinal; var AOptions: Cardinal;
  const AScale: Single; const AFeedback: TMeshLoadSaveFeedback; const AFeedbackUser: Pointer): TSceneMesh;
var
  LDebug: Utf8String;
  LLength: Cardinal;
begin
  LLength := StringBufferLength;
  SetLength(LDebug, StringBufferLength);

  Result.FHandle := SceneMeshesAddFromFile(FHandle, PAnsiChar(AName), PAnsiChar(AFileName), APayload,
    InterpretVertexElements(AVertexElements), Length(AVertexElements), AChannel, ASemanticIndex, @AOptions,
    AScale, AFeedback, AFeedbackUser, @LDebug[1], @LLength);

  SetLength(LDebug, LLength);

  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotAdd3DSceneMeshFromFile, [AName, AFileName, LDebug]);
end;

function TSceneMeshes.AddFromFile(const AName, AFileName: Utf8String; const APayload: TObjectPayload;
  const AVertexElements: array of TVertexElement; const AChannel, ASemanticIndex: Cardinal;
  var AOptions: Cardinal; const AScale: Single; const AFeedback: TMeshLoadSaveFeedback;
  const AFeedbackUser: Pointer): TSceneMesh;
var
  LDebug: Utf8String;
  LLength: Cardinal;
begin
  LLength := StringBufferLength;
  SetLength(LDebug, StringBufferLength);

  Result.FHandle := SceneMeshesAddFromFile(FHandle, PAnsiChar(AName), PAnsiChar(AFileName), APayload,
    InterpretVertexElements(AVertexElements), Length(AVertexElements), AChannel, ASemanticIndex, @AOptions,
    AScale, AFeedback, AFeedbackUser, @LDebug[1], @LLength);

  SetLength(LDebug, LLength);

  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotAdd3DSceneMeshFromFile, [AName, AFileName, LDebug]);
end;

procedure TSceneMeshes.Slice(const AParent: TSceneMesh; const ATagType: TMeshMetaTagType;
  const APrefix: Utf8String);
var
  LParentName: string;
begin
  if not SceneMeshesSlice(FHandle, AParent.Handle, Ord(ATagType), PAnsiChar(APrefix)) then
  begin
    if AParent.Handle <> nil then
      LParentName := string(AParent.Name)
    else
      LParentName := 'nil';

    raise EAfterwarpException.CreateFmt(SCouldNotSpliceSceneMesh, [LParentName]);
  end;
end;

procedure TSceneMeshes.Slice(const AParent: TSceneMesh; const ATagType: TMeshMetaTagType);
var
  LParentName: string;
begin
  if not SceneMeshesSlice(FHandle, AParent.Handle, Ord(ATagType), nil) then
  begin
    if AParent.Handle <> nil then
      LParentName := string(AParent.Name)
    else
      LParentName := 'nil';

    raise EAfterwarpException.CreateFmt(SCouldNotSpliceSceneMesh, [LParentName]);
  end;
end;

procedure TSceneMeshes.Erase(const ASceneMesh: TSceneMesh);
begin
  SceneMeshesErase(FHandle, ASceneMesh.Handle);
end;

procedure TSceneMeshes.Clear;
begin
  SceneMeshesClear(FHandle);
end;

function TSceneMeshes.TryMesh(const AName: Utf8String): TSceneMesh;
begin
  Result.FHandle := SceneMeshesGetMeshByName(FHandle, PAnsiChar(AName));
end;

function TSceneMeshes.TryPayload(const APayload: TObjectPayload): TSceneMesh;
begin
  Result.FHandle := SceneMeshesPayload(FHandle, APayload);
end;

function TSceneMeshes.GetEnumerator: TSceneMeshesEnumerator;
begin
  Result := TSceneMeshesEnumerator.Create(FHandle);
end;

{$ENDREGION}
{$REGION 'TObjectModel'}

function TObjectModel.GetID: TObjectModelID;
begin
  Result := ObjectModelGetID(FHandle);
end;

function TObjectModel.GetName: Utf8String;
var
  LLength: LongInt;
begin
  ObjectModelGetName(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    ObjectModelGetName(FHandle, @Result[1], @LLength);
end;

procedure TObjectModel.SetName(const AName: Utf8String);
begin
  if not ObjectModelSetName(FHandle, PAnsiChar(AName)) then
    raise EAfterwarpException.CreateFmt(SCouldNotChange3DObjectModelName, [AName]);
end;

function TObjectModel.GetPayload: TObjectPayload;
begin
  Result := ObjectModelGetPayload(FHandle);
end;

function TObjectModel.GetDescription: Utf8String;
var
  LLength: LongInt;
begin
  ObjectModelGetDescription(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    ObjectModelGetDescription(FHandle, @Result[1], @LLength);
end;

procedure TObjectModel.SetDescription(const ADescription: Utf8String);
begin
  if not ObjectModelSetDescription(FHandle, PAnsiChar(ADescription)) then
    raise EAfterwarpException.CreateFmt(SCouldNotChange3DObjectModelDescription, [ADescription]);
end;

function TObjectModel.GetParent: TObjectModel;
begin
  Result.FHandle := ObjectModelGetParent(FHandle);
end;

procedure TObjectModel.SetParent(const AParent: TObjectModel);
begin
  if not ObjectModelSetParent(FHandle, AParent.FHandle) then
    raise EAfterwarpException.Create(SCouldNotChange3DObjectModelParent);
end;

function TObjectModel.GetVoxel: TMeshVoxel;
begin
  Result.FHandle := ObjectModelGetVoxel(FHandle);
end;

procedure TObjectModel.SetVoxel(const AMeshVoxel: TMeshVoxel);
begin
  ObjectModelSetVoxel(FHandle, AMeshVoxel.FHandle);
end;

function TObjectModel.GetMesh: TSceneMesh;
begin
  Result.FHandle := ObjectModelGetMesh(FHandle);
end;

procedure TObjectModel.SetMesh(const AMesh: TSceneMesh);
begin
  if not ObjectModelSetMesh(FHandle, AMesh.Handle) then
    raise EAfterwarpException.Create(SCouldNotChangeSceneMeshIn3DObjectModel);
end;

function TObjectModel.GetMeshName: Utf8String;
var
  LLength: Integer;
begin
  ObjectModelGetMeshName(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    ObjectModelGetMeshName(FHandle, @Result[1], @LLength);
end;

procedure TObjectModel.SetMeshName(const AName: Utf8String);
begin
  if not ObjectModelSetMeshName(FHandle, PAnsiChar(AName)) then
    raise EAfterwarpException.CreateFmt(SCouldNotChange3DObjectModelMeshName, [AName]);
end;

function TObjectModel.GetTransform(const ATransform: TModelTransform): TMatrix4f;
begin
  ObjectModelGetTransform(FHandle, ATransform, @Result);
end;

procedure TObjectModel.SetTransform(const ATransform: TModelTransform; const AMatrix: TMatrix4f);
begin
  ObjectModelSetTransform(FHandle, ATransform, @AMatrix);
end;

function TObjectModel.GetPosition: TVector3f;
begin
  ObjectModelGetPosition(FHandle, @Result);
end;

function TObjectModel.GetDepthBias: Single;
begin
  Result := ObjectModelGetDepthBias(FHandle);
end;

procedure TObjectModel.SetDepthBias(const ADepthBias: Single);
begin
  ObjectModelSetDepthBias(FHandle, ADepthBias);
end;

function TObjectModel.GetAttributes: TModelAttributes;
begin
  Result := TModelAttributes(Byte(ObjectModelGetAttributes(FHandle)));
end;

procedure TObjectModel.SetAttributes(const AAttributes: TModelAttributes);
begin
  ObjectModelSetAttributes(FHandle, Byte(AAttributes));
end;

function TObjectModel.GetOrderIndex: Integer;
begin
  Result := ObjectModelGetOrderIndex(FHandle);
end;

procedure TObjectModel.SetOrderIndex(const AOrderIndex: Integer);
begin
  ObjectModelSetOrderIndex(FHandle, AOrderIndex);
end;

function TObjectModel.GetLayers: UInt64;
begin
  Result := ObjectModelGetLayers(FHandle);
end;

procedure TObjectModel.SetLayers(const ALayers: UInt64);
begin
  ObjectModelSetLayers(FHandle, ALayers);
end;

function TObjectModel.GetChildCount: Integer;
begin
  Result := ObjectModelGetChildCount(FHandle);
end;

function TObjectModel.GetChild(const AIndex: Integer): TObjectModel;
begin
  Result.FHandle := ObjectModelGetChild(FHandle, AIndex);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieve3DObjectModelChild, [AIndex]);
end;

function TObjectModel.GetSize: TVector3f;
begin
  ObjectModelGetSize(FHandle, @Result);
end;

procedure TObjectModel.SetSize(const ASize: TVector3f);
begin
  ObjectModelSetSize(FHandle, @ASize);
end;

function TObjectModel.GetAligns: TMeshAligns;
begin
  ObjectModelGetAlignments(FHandle, @Result);
end;

procedure TObjectModel.SetAligns(const AAligns: TMeshAligns);
begin
  ObjectModelSetAlignments(FHandle, @AAligns);
end;

function TObjectModel.GetMaterial: Integer;
begin
  Result := ObjectModelGetMaterial(FHandle);
end;

procedure TObjectModel.SetMaterial(const AMaterial: Integer);
begin
  ObjectModelSetMaterial(FHandle, AMaterial);
end;

function TObjectModel.GetColor: TFloatColor;
begin
  ObjectModelGetColor(FHandle, @Result);
end;

procedure TObjectModel.SetColor(const AColor: TFloatColor);
begin
  ObjectModelSetColor(FHandle, @AColor);
end;

function TObjectModel.GetHighlight: TFloatColor;
begin
  ObjectModelGetHighlight(FHandle, @Result);
end;

procedure TObjectModel.SetHighlight(const AHighlight: TFloatColor);
begin
  ObjectModelSetHighlight(FHandle, @AHighlight);
end;

procedure TObjectModel.GetAABB(out ABoxMin, ABoxMax: TVector3f);
begin
  ObjectModelGetAABB(FHandle, @ABoxMin, @ABoxMax);
end;

procedure TObjectModel.ConnectLatches(const ALatchParent, ALatchLocal: Integer);
begin
  ObjectModelConnectLatches(FHandle, ALatchParent, ALatchLocal);
end;

procedure TObjectModel.ConnectLatches(const ALatchParentName, ALatchLocalName: Utf8String);
begin
  ObjectModelConnectLatchesByName(FHandle, PAnsiChar(ALatchParentName), PAnsiChar(ALatchLocalName));
end;

procedure TObjectModel.GetConnectedLatches(out ALatchParent, ALatchLocal: Integer);
begin
  ObjectModelGetConnectedLatches(FHandle, PLongInt(@ALatchParent), PLongInt(@ALatchLocal));
end;

function TObjectModel.GetWaypointDistance(const AGroup: Integer): Single;
begin
  Result := ObjectModelGetWaypointDistance(FHandle, AGroup);
end;

procedure TObjectModel.GetLatchWaypointCouple(out APosition: TVector3f; out AOrientation: TQuaternion;
  const ADistance: Single; const AGroup: Integer);
begin
  ObjectModelGetLatchWaypointCouple(FHandle, AGroup, ADistance, @APosition, @AOrientation);
end;

function TObjectModel.GetLatchWaypointCouple(const ADistance: Single; const AGroup: Integer): TMatrix4f;
begin
  ObjectModelGetLatchWaypointCoupleMatrix(FHandle, AGroup, ADistance, @Result);
end;

function TObjectModel.GetLatchTransform(const ALatchIndex: Integer; const ALocal: Boolean): TMatrix4f;
begin
  ObjectModelGetLatchTransform(FHandle, @Result, ALatchIndex, ALocal);
end;

function TObjectModel.GetLatchTransform(const ALatchName: Utf8String; const ALocal: Boolean): TMatrix4f;
begin
  ObjectModelGetLatchTransformByName(FHandle, @Result, PAnsiChar(ALatchName), ALocal);
end;

procedure TObjectModel.Invalidate;
begin
  ObjectModelInvalidate(FHandle);
end;

function TObjectModelHelper.GetOwner: TObjectModels;
begin
  Result.FHandle := ObjectModelGetOwner(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveParentContainerFor3DObjectModel);
end;

{$ENDREGION}
{$REGION 'TObjectModelViewEnumerator'}

class function TObjectModelViewEnumerator.Create(
  const AHandle: TUntypedObjectHandle): TObjectModelViewEnumerator;
begin
  Result.FHandle := AHandle;
  Result.FCurrent := -1;
end;

function TObjectModelViewEnumerator.GetCurrent: TObjectModel;
begin
  Result.FHandle := ObjectModelViewGetObject(FHandle, FCurrent);
end;

function TObjectModelViewEnumerator.MoveNext: Boolean;
begin
  Inc(FCurrent);
  Result := FCurrent < ObjectModelViewGetObjectCount(FHandle);
end;

{$ENDREGION}
{$REGION 'TObjectModelView'}

function TObjectModelView.GetView: TMatrix4f;
begin
  ObjectModelViewGetView(FHandle, @Result);
end;

procedure TObjectModelView.SetView(const AView: TMatrix4f);
begin
  ObjectModelViewSetView(FHandle, @AView);
end;

function TObjectModelView.GetProjection: TMatrix4f;
begin
  ObjectModelViewGetProjection(FHandle, @Result, nil);
end;

function TObjectModelView.GetViewProjection: TMatrix4f;
begin
  ObjectModelViewGetViewProjection(FHandle, @Result);
end;

function TObjectModelView.GetDepthClipNegative: Boolean;
begin
  ObjectModelViewGetProjection(FHandle, nil, @Result);
end;

function TObjectModelView.GetLayers: UInt64;
begin
  Result := ObjectModelViewGetLayers(FHandle);
end;

procedure TObjectModelView.SetLayers(const ALayers: UInt64);
begin
  ObjectModelViewSetLayers(FHandle, ALayers);
end;

function TObjectModelView.GetObjectCount: Integer;
begin
  Result := ObjectModelViewGetObjectCount(FHandle);
end;

function TObjectModelView.GetObject(const AIndex: Integer): TObjectModel;
begin
  Result.FHandle := ObjectModelViewGetObject(FHandle, AIndex);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveObjectFromObjectModelViewContainer, [AIndex]);
end;

function TObjectModelView.GetObjectsNotCulled: Integer;
begin
  Result := ObjectModelViewGetObjectsNotCulled(FHandle);
end;

function TObjectModelView.GetIntersectedRays: Integer;
begin
  Result := ObjectModelViewGetIntersectedRays(FHandle);
end;

function TObjectModelView.GetIntersectedObjects: Integer;
begin
  Result := ObjectModelViewGetIntersectedObjects(FHandle);
end;

procedure TObjectModelView.SetProjection(const AProjection: TMatrix4f; const ADepthClipNegative: Boolean);
begin
  ObjectModelViewSetProjection(FHandle, @AProjection, ADepthClipNegative);
end;

procedure TObjectModelView.UpdateNeeded;
begin
  ObjectModelViewUpdateNeeded(FHandle);
end;

procedure TObjectModelView.Invalidate;
begin
  ObjectModelViewInvalidate(FHandle);
end;

function TObjectModelView.Update: Boolean;
begin
  Result := ObjectModelViewUpdate(FHandle);
end;

procedure TObjectModelView.Sort(const ACompare: TObjectModelViewCompare);
begin
  ObjectModelViewSort(FHandle, ACompare);
end;

procedure TObjectModelView.Sort(const ACompareFunc: TObjectModelCompareFunc; const AUser: Pointer);
begin
  ObjectModelViewSortWith(FHandle, ACompareFunc, AUser);
end;

function TObjectModelView.Select(const ARay: TRay; out ADistance: Single): TObjectModel;
begin
  Result.FHandle := ObjectModelViewSelect(FHandle, @ARay.Origin, @ARay.Direction, @ADistance);
end;

function TObjectModelView.Select(const ARay: TRay): TObjectModel;
begin
  Result.FHandle := ObjectModelViewSelect(FHandle, @ARay.Origin, @ARay.Direction, nil);
end;

function TObjectModelView.SelectAny(const ARay: TRay; out ADistance: Single): TObjectModel;
begin
  Result.FHandle := ObjectModelViewSelectAny(FHandle, @ARay.Origin, @ARay.Direction, @ADistance);
end;

function TObjectModelView.SelectAny(const ARay: TRay): TObjectModel;
begin
  Result.FHandle := ObjectModelViewSelectAny(FHandle, @ARay.Origin, @ARay.Direction, nil);
end;

function TObjectModelView.GetEnumerator: TObjectModelViewEnumerator;
begin
  Result := TObjectModelViewEnumerator.Create(FHandle);
end;

function TObjectModelViewHelper.GetOwner: TObjectModels;
begin
  Result.FHandle := ObjectModelViewGetOwner(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveObjectModelViewParentContainer);
end;

procedure TObjectModelViewHelper.AutoDraw(const AScene: TScene; const AMaterials: TObjectMaterials;
  const AOptions: Cardinal; var ADrawCalls: Integer);
begin
  if not ObjectModelViewAutoDraw(FHandle, AScene.Handle, AMaterials.Handle, AOptions,
    PLongInt(@ADrawCalls)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawObjectsInModelView);
end;

procedure TObjectModelViewHelper.AutoDraw(const AScene: TScene; const AMaterials: TObjectMaterials;
  const AOptions: Cardinal);
begin
  if not ObjectModelViewAutoDraw(FHandle, AScene.Handle, AMaterials.Handle, AOptions, nil) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawObjectsInModelView);
end;

procedure TObjectModelViewHelper.AutoDraw(const AScene: TScene; const AOptions: Cardinal;
  var ADrawCalls: Integer);
begin
  if not ObjectModelViewAutoDraw(FHandle, AScene.Handle, nil, AOptions, PLongInt(@ADrawCalls)) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawObjectsInModelView);
end;

procedure TObjectModelViewHelper.AutoDraw(const AScene: TScene; const AOptions: Cardinal);
begin
  if not ObjectModelViewAutoDraw(FHandle, AScene.Handle, nil, AOptions, nil) then
    raise EAfterwarpException.Create(SCouldNotAutoDrawObjectsInModelView);
end;

function TObjectModelViewHelper.MeasureAutoDraw(const AOptions: Cardinal): Integer;
begin
  Result := 0;
  if not ObjectModelViewAutoDraw(FHandle, nil, nil, AOptions, PLongInt(@Result)) then
    raise EAfterwarpException.Create(SCouldNotMeasureAutoDrawObjectsInModelView);
end;

{$ENDREGION}
{$REGION 'TObjectModelsEnumerator'}

class function TObjectModelsEnumerator.Create(
  const AObjectsModelsHandle: TUntypedObjectHandle): TObjectModelsEnumerator;
begin
  Result.FOwner := AObjectsModelsHandle;
  Result.FCurrent := nil;
  Result.FStarted := False;
end;

function TObjectModelsEnumerator.GetCurrent: TObjectModel;
begin
  Result.FHandle := FCurrent;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveNextObjectModelFromCollection);
end;

function TObjectModelsEnumerator.MoveNext: Boolean;
begin
  if not FStarted then
  begin
    FCurrent := ObjectModelsGetFirst(FOwner);
    FStarted := True;
  end
  else if FCurrent <> nil then
    FCurrent := ObjectModelGetNext(FCurrent);

  Result := FCurrent <> nil;
end;

{$ENDREGION}
{$REGION 'TObjectModels'}

class function TObjectModels.Create(const AMeshes: TSceneMeshes): TObjectModels;
begin
  Result.FHandle := ObjectModelsCreate(AMeshes.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateContainerFor3DObjects);
end;

procedure TObjectModels.Free;
begin
  if FHandle <> nil then
  begin
    ObjectModelsDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TObjectModels.GetMeshes: TSceneMeshes;
begin
  Result.FHandle := ObjectModelsGetMeshes(FHandle);
end;

function TObjectModels.GetPayload(const APayload: TObjectPayload): TObjectModel;
begin
  Result.FHandle := ObjectModelsPayload(FHandle, APayload);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SObjectWithGivenPayloadCouldNotBeRetrieved);
end;

function TObjectModels.GetObject(const AName: Utf8String): TObjectModel;
begin
  Result.FHandle := ObjectModelsGetObjectByName(FHandle, PAnsiChar(AName));
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SObjectWithGivenNameCouldNotBeRetrieved, [AName]);
end;

function TObjectModels.GetObjectByID(const AID: TObjectModelID): TObjectModel;
begin
  Result.FHandle := ObjectModelsGetObjectByID(FHandle, AID);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SObjectWithGivenIDCouldNotBeRetrieved, [AID]);
end;

function TObjectModels.GetObjectCount: Integer;
begin
  Result := ObjectModelsGetObjectCount(FHandle);
end;

function TObjectModels.Add(const AName: Utf8String; const APayload: TObjectPayload;
  const AAttributes: TModelAttributes): TObjectModel;
begin
  Result.FHandle := ObjectModelsAdd(FHandle, PAnsiChar(AName), APayload, Byte(AAttributes));
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotAddNew3DObjectModelToContainer, [AName]);
end;

function TObjectModels.Add(const AID: TObjectModelID; const AName: Utf8String;
  const APayload: TObjectPayload; const AAttributes: TModelAttributes): TObjectModel;
begin
  Result.FHandle := ObjectModelsAddWithID(FHandle, AID, PAnsiChar(AName), APayload, Byte(AAttributes));
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotAddNew3DObjectModelWithIDToContainer, [AID, AName]);
end;

procedure TObjectModels.Erase(const AObjectModel: TObjectModel);
begin
  ObjectModelsErase(FHandle, AObjectModel.Handle);
end;

procedure TObjectModels.Clear;
begin
  ObjectModelsClear(FHandle);
end;

function TObjectModels.TryObject(const AID: TObjectModelID): TObjectModel;
begin
  Result.FHandle := ObjectModelsGetObjectByID(FHandle, AID);
end;

function TObjectModels.TryObject(const AName: Utf8String): TObjectModel;
begin
  Result.FHandle := ObjectModelsGetObjectByName(FHandle, PAnsiChar(AName));
end;

function TObjectModels.TryPayload(const APayload: TObjectPayload): TObjectModel;
begin
  Result.FHandle := ObjectModelsPayload(FHandle, APayload);
end;

function TObjectModels.Exists(const AID: TObjectModelID): Boolean;
begin
  Result := TryObject(AID).Handle <> nil;
end;

function TObjectModels.Exists(const AName: Utf8String): Boolean;
begin
  Result := TryObject(AName).Handle <> nil;
end;

function TObjectModels.PayloadExists(const APayload: TObjectPayload): Boolean;
begin
  Result := TryPayload(APayload).Handle <> nil;
end;

function TObjectModels.CreateView: TObjectModelView;
begin
  Result.FHandle := ObjectModelsCreateView(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateNewViewForObjectModelContainer);
end;

procedure TObjectModels.EraseView(const AView: TObjectModelView);
begin
  ObjectModelsEraseView(FHandle, AView.Handle);
end;

procedure TObjectModels.ClearViews;
begin
  ObjectModelsClearViews(FHandle);
end;

function TObjectModels.GetEnumerator: TObjectModelsEnumerator;
begin
  Result := TObjectModelsEnumerator.Create(FHandle);
end;

{$ENDREGION}
{$REGION 'TShadowCaster'}

function TShadowCaster.GetDevice: TDevice;
begin
  Result.FHandle := ShadowCasterGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TShadowCaster.GetPosition: TPoint2i;
begin
  ShadowCasterGetPosition(FHandle, @Result);
end;

function TShadowCaster.GetSize: TPoint2i;
begin
  ShadowCasterGetSize(FHandle, @Result);
end;

function TShadowCaster.GetViewProjection: TMatrix4f;
begin
  ShadowCasterGetViewProjection(FHandle, @Result);
end;

procedure TShadowCaster.SetViewProjection(const AViewProjection: TMatrix4f);
begin
  ShadowCasterSetViewProjection(FHandle, @AViewProjection);
end;

function TShadowCaster.GetRendering: Boolean;
begin
  Result := ShadowCasterRendering(FHandle);
end;

function TShadowCaster.GetTexture(const AIndex: Integer): TTexture;
begin
  Result.FHandle := ShadowCasterGetTexture(FHandle, AIndex);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveShadowCasterTexture, [AIndex]);
end;

procedure TShadowCaster.Clear;
begin
  if not ShadowCasterClear(FHandle) then
    raise EAfterwarpException.Create(SCouldNotClearShadowCasterTextures);
end;

procedure TShadowCaster.BeginScene;
begin
  if not ShadowCasterBegin(FHandle) then
    raise EAfterwarpException.Create(SCouldNotStartRenderingToShadowCasterTexture);
end;

procedure TShadowCaster.EndScene;
begin
  ShadowCasterEnd(FHandle);
end;

procedure TShadowCaster.Filter;
begin
  if not ShadowCasterFilter(FHandle) then
    raise EAfterwarpException.Create(SCouldNotFilterShadowCasterTextureIntoShadowMap);
end;

function TShadowCasterHelper.GetAtlas: TShadowCastingAtlas;
begin
  Result.FHandle := ShadowCasterGetAtlas(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveTextureAtlasAssociatedWithShadowCaster);
end;

{$ENDREGION}
{$REGION 'TShadowCastingAtlas'}

class function TShadowCastingAtlas.Create(const ADevice: TDevice; const ASize: TPoint2i;
  const ATechnique: TTechniqueShadows; const ASamples: Integer;
  const APadding: Integer): TShadowCastingAtlas;
begin
  Result.FHandle := ShadowCastingAtlasCreate(ADevice.Handle, @ASize, ATechnique, ASamples, APadding);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateShadowCastingAtlasContainer);
end;

procedure TShadowCastingAtlas.Free;
begin
  if FHandle <> nil then
  begin
    ShadowCastingAtlasDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TShadowCastingAtlas.GetDevice: TDevice;
begin
  Result.FHandle := ShadowCastingAtlasGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TShadowCastingAtlas.GetTechnique: TTechniqueShadows;
begin
  Result := ShadowCastingAtlasGetTechnique(FHandle);
end;

function TShadowCastingAtlas.GetParameters: TShadowParameters;
begin
  ShadowCastingAtlasGetParameters(FHandle, @Result);
end;

procedure TShadowCastingAtlas.SetParameters(const AParameters: TShadowParameters);
begin
  if not ShadowCastingAtlasSetParameters(FHandle, @AParameters) then
    raise EAfterwarpException.Create(SCouldNotUpdateShadowMappingParametersInAtlas);
end;

function TShadowCastingAtlas.GetSize: TPoint2i;
begin
  ShadowCastingAtlasGetSize(FHandle, @Result);
end;

function TShadowCastingAtlas.GetSamples: Integer;
begin
  Result := ShadowCastingAtlasGetSamples(FHandle);
end;

function TShadowCastingAtlas.GetPadding: Integer;
begin
  Result := ShadowCastingAtlasGetPadding(FHandle);
end;

function TShadowCastingAtlas.GetTexture: TTexture;
begin
  Result.FHandle := ShadowCastingAtlasGetTexture(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveTextureFromShadowCastingAtlas);
end;

function TShadowCastingAtlas.GetCasterCount: Integer;
begin
  Result := ShadowCastingAtlasGetCount(FHandle);
end;

function TShadowCastingAtlas.GetCaster(const AIndex: Integer): TShadowCaster;
begin
  Result.FHandle := ShadowCastingAtlasGetCaster(FHandle, AIndex);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveShadowMappingCasterFromAtlas, [AIndex]);
end;

function TShadowCastingAtlas.Add(const ASize: TPoint2i; const AShared: Boolean): TShadowCaster;
begin
  Result.FHandle := ShadowCastingAtlasAdd(FHandle, @ASize, AShared);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SFailedAddingNewShadowCasterShadowMappingAtlas);
end;

procedure TShadowCastingAtlas.Erase(const ACaster: TShadowCaster);
begin
  ShadowCastingAtlasErase(FHandle, ACaster.Handle);
end;

procedure TShadowCastingAtlas.Clear;
begin
  ShadowCastingAtlasClear(FHandle);
end;

procedure TShadowCastingAtlas.BorderFill;
begin
  if not ShadowCastingAtlasBorderFill(FHandle) then
    raise EAfterwarpException.Create(SCouldNotClearShadowMappingAtlasTextureWithBorderColor);
end;

{$ENDREGION}
{$REGION 'TScene'}

class function TScene.CreateDepthNormals(const ADevice: TDevice): TScene;
begin
  Result.FHandle := SceneCreateDepthsNormals(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DDepthNormalsRenderingScene);
end;

class function TScene.CreateModeling(const ADevice: TDevice): TScene;
begin
  Result.FHandle := SceneCreateModeling(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DModelingRenderingScene);
end;

procedure TScene.Free;
begin
  if FHandle <> nil then
  begin
    SceneDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TScene.GetDevice: TDevice;
begin
  Result.FHandle := SceneGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TScene.GetActiveVertexElements: Integer;
begin
  Result := SceneGetActiveVertexElements(FHandle);
end;

procedure TScene.SetActiveVertexElements(const AActiveVertexElements: Integer);
begin
  if not SceneSetActiveVertexElements(FHandle, AActiveVertexElements) then
    raise EAfterwarpException.CreateFmt(SCouldNotSetActiveVertexElementDeclarationOf3DRenderingScene,
      [AActiveVertexElements]);
end;

function TScene.GetWorld: TMatrix4f;
begin
  SceneGetWorld(FHandle, @Result);
end;

procedure TScene.SetWorld(const AWorld: TMatrix4f);
begin
  SceneSetWorld(FHandle, @AWorld);
end;

function TScene.GetView: TMatrix4f;
begin
  SceneGetView(FHandle, @Result);
end;

procedure TScene.SetView(const AView: TMatrix4f);
begin
  SceneSetView(FHandle, @AView);
end;

function TScene.GetProjection: TMatrix4f;
begin
  SceneGetProjection(FHandle, @Result);
end;

procedure TScene.SetProjection(const AProjection: TMatrix4f);
begin
  SceneSetProjection(FHandle, @AProjection);
end;

function TScene.GetInstancesCount: Integer;
begin
  Result := SceneGetInstancesCount(FHandle);
end;

function TScene.GetSceneProgram: TProgram;
begin
  Result.FHandle := SceneGetProgram(FHandle);
end;

function TScene.GetRendering: Boolean;
begin
  Result := SceneRendering(FHandle);
end;

function TScene.GetAttributes: TSceneAttributes;
begin
  Result := SceneGetAttributes(FHandle);
end;

procedure TScene.SetAttributes(const AAttributes: TSceneAttributes);
begin
  if not SceneSetAttributes(FHandle, AAttributes) then
    raise EAfterwarpException.Create(SCouldNotChangeRenderingAttributesOf3DRenderingScene);
end;

function TScene.GetMaterial: TObjectMaterial;
begin
  SceneGetMaterial(FHandle, @Result);
end;

procedure TScene.SetMaterial(const AMaterial: TObjectMaterial);
begin
  if not SceneSetMaterial(FHandle, @AMaterial) then
    raise EAfterwarpException.Create(SCouldNotChangeObjectMaterialPropertiesIn3DRenderingScene);
end;

function TScene.GetToneMappingCoefficients: TVector4f;
begin
  SceneGetToneMappingCoefficients(FHandle, @Result);
end;

procedure TScene.SetToneMappingCoefficients(const ACoefficients: TVector4f);
begin
  if not SceneSetToneMappingCoefficients(FHandle, @ACoefficients) then
    raise EAfterwarpException.Create(SCouldNotChangeToneMappingCoefficientsIn3DRenderingScene);
end;

function TScene.GetParallaxMappingParameters: TParallaxMappingParameters;
begin
  SceneGetParallaxMappingParameters(FHandle, @Result);
end;

procedure TScene.SetParallaxMappingParameters(const AParameters: TParallaxMappingParameters);
begin
  if not SceneSetParallaxMappingParameters(FHandle, @AParameters) then
    raise EAfterwarpException.Create(SCouldNotChangeParallaxMappingParametersIn3DRenderingScene);
end;

function TScene.GetTexture(const AType: TSceneTextureType): TTexture;
begin
  Result.FHandle := SceneGetTexture(FHandle, AType);
end;

procedure TScene.SetTexture(const AType: TSceneTextureType; const ATexture: TTexture);
begin
  if not SceneSetTexture(FHandle, ATexture.Handle, AType) then
    raise EAfterwarpException.CreateFmt(SCouldNotChangeTextureTypeIn3DRenderingScene, [Ord(AType)]);
end;

function TScene.GetSampler(const AType: TSceneSamplerType): TSampler;
begin
  Result.FHandle := SceneGetSampler(FHandle, AType);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveSamplerAssociatedWith3DRenderingScene, [Ord(AType)]);
end;

function TScene.GetLights: TSceneLights;
begin
  Result.FHandle := SceneGetLights(FHandle);
end;

procedure TScene.SetLights(const ALights: TSceneLights);
begin
  SceneSetLights(FHandle, ALights.Handle);
end;

function TScene.GetShadowCastingAtlas: TShadowCastingAtlas;
begin
  Result.FHandle := SceneGetShadowCastingAtlas(FHandle);
end;

procedure TScene.SetShadowCastingAtlas(const AShadowCastingAtlas: TShadowCastingAtlas);
begin
  SceneSetShadowCastingAtlas(FHandle, AShadowCastingAtlas.Handle);
end;

function TScene.GetTextureCabinet: TTextureCabinet;
begin
  Result.FHandle := SceneGetTextureCabinet(FHandle);
end;

procedure TScene.SetTextureCabinet(const ATextureCabinet: TTextureCabinet);
begin
  SceneSetTextureCabinet(FHandle, ATextureCabinet.Handle);
end;

procedure TScene.SetVertexElements(const AIndex: Integer; const AVertexElements: TVertexElements);
begin
  if not SceneSetVertexElements(FHandle, AIndex, InterpretVertexElements(AVertexElements),
    Length(AVertexElements)) then
    raise EAfterwarpException.CreateFmt(SFailedToModifyVertexElementDeclarationOf3DRenderingScene, [AIndex]);
end;

procedure TScene.SetVertexElements(const AIndex: Integer; const AVertexElements: array of TVertexElement);
begin
  if not SceneSetVertexElements(FHandle, AIndex, InterpretVertexElements(AVertexElements),
    Length(AVertexElements)) then
    raise EAfterwarpException.CreateFmt(SFailedToModifyVertexElementDeclarationOf3DRenderingScene, [AIndex]);
end;

procedure TScene.SetVertexElementsFromTextModeller(const AIndex: Integer);
begin
  if not SceneSetVertexElementsFromTextModeller(FHandle, AIndex) then
    raise EAfterwarpException.CreateFmt(
      SFailedToModifyVertexElementDeclarationOf3DRenderingSceneFromTextModeller, [AIndex]);
end;

function TScene.GetVertexElements(const AIndex: Integer): TVertexElements;
var
  LElements: PVertexElement;
  I: Integer;
begin
  SetLength(Result, SceneGetVertexElementCount(FHandle, AIndex));
  if Length(Result) > 0 then
  begin
    LElements := SceneGetVertexElements(FHandle, AIndex);

    for I := 0 to Length(Result) - 1 do
    begin
      Result[I] := LElements^;
      Inc(LElements);
    end;
  end;
end;

procedure TScene.Instances(const ATransforms: TMatrix4fArray; const AColors: TFloatColorArray);
var
  LColors: PFloatColor;
begin
  if (Length(ATransforms) <= 0) or ((Length(ATransforms) <> Length(AColors)) and (Length(AColors) > 0)) then
    raise EAfterwarpException.CreateFmt(SInvalidNumberOfTransformsAndColorsTo3DRenderingScene,
      [Length(ATransforms), Length(AColors)]);

  if Length(AColors) > 0 then
    LColors := @AColors[0]
  else
    LColors := nil;

  if not SceneInstances(FHandle, @ATransforms[0], LColors, Length(ATransforms)) then
    raise EAfterwarpException.Create(SCouldNotSupplyInstanceInformationTo3DRenderingScene);
end;

procedure TScene.Instances(const ATransforms: array of TMatrix4f; const AColors: array of TFloatColor);
var
  LColors: PFloatColor;
begin
  if (Length(ATransforms) <= 0) or ((Length(ATransforms) <> Length(AColors)) and (Length(AColors) > 0)) then
    raise EAfterwarpException.CreateFmt(SInvalidNumberOfTransformsAndColorsTo3DRenderingScene,
      [Length(ATransforms), Length(AColors)]);

  if Length(AColors) > 0 then
    LColors := @AColors[0]
  else
    LColors := nil;

  if not SceneInstances(FHandle, @ATransforms[0], LColors, Length(ATransforms)) then
    raise EAfterwarpException.Create(SCouldNotSupplyInstanceInformationTo3DRenderingScene);
end;

procedure TScene.Instances(const ATransforms: PMatrix4f; const AColors: PFloatColor;
  const AInstanceCount: Integer);
begin
  if not SceneInstances(FHandle, ATransforms, AColors, AInstanceCount) then
    raise EAfterwarpException.Create(SCouldNotSupplyInstanceInformationTo3DRenderingScene);
end;

procedure TScene.BeginScene;
begin
  if not SceneBegin(FHandle) then
    raise EAfterwarpException.Create(SCouldNotStartRendering3DScene);
end;

procedure TScene.EndScene;
begin
  SceneEnd(FHandle);
end;

procedure TScene.Prepare;
begin
  if not ScenePrepare(FHandle) then
    raise EAfterwarpException.Create(SCouldNotPrepare3DSceneForRendering);
end;

function TScene.TryPrepare: Boolean;
begin
  Result := ScenePrepare(FHandle);
end;

procedure TScene.ResetCache;
begin
  SceneResetCache(FHandle);
end;

{$ENDREGION}
{$REGION 'TOceanSimulation'}

class function TOceanSimulation.Create(const ADevice: TDevice): TOceanSimulation;
begin
  Result.FHandle := OceanSimulationCreate(ADevice.Handle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DOceanRenderingModule);
end;

procedure TOceanSimulation.Free;
begin
  if FHandle <> nil then
  begin
    OceanSimulationDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TOceanSimulation.GetDevice: TDevice;
begin
  Result.FHandle := OceanSimulationGetDevice(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveAssociatedDevice);
end;

function TOceanSimulation.GetWavesParameters: TOceanWavesParameters;
begin
  OceanSimulationGetWavesParameters(FHandle, @Result);
end;

procedure TOceanSimulation.SetWavesParameters(const AParameters: TOceanWavesParameters);
begin
  if not OceanSimulationSetWavesParameters(FHandle, @AParameters) then
    raise EAfterwarpException.Create(SCouldNotChangeWaveSimulationParametersIn3DOceanRenderingModule);
end;

function TOceanSimulation.GetWavesTexture(const AIndex: Integer): TTexture;
begin
  Result.FHandle := OceanSimulationGetWavesTexture(FHandle, AIndex);
  if Result.FHandle = nil then
    raise EAfterwarpException.CreateFmt(SCouldNotRetrieveWavesTextureFrom3DOceanRenderingModule, [AIndex]);
end;

function TOceanSimulation.GetSections: TPoint2i;
begin
  OceanSimulationGetSections(FHandle, @Result);
end;

procedure TOceanSimulation.SetSections(const ASections: TPoint2i);
begin
  if not OceanSimulationSetSections(FHandle, @ASections) then
    raise EAfterwarpException.CreateFmt(SFailedChangingNumberOfSectionsIn3DOceanRenderingModule,
      [ASections.X, ASections.Y]);
end;

function TOceanSimulation.GetView: TMatrix4f;
begin
  OceanSimulationGetView(FHandle, @Result);
end;

procedure TOceanSimulation.SetView(const AView: TMatrix4f);
begin
  OceanSimulationSetView(FHandle, @AView);
end;

function TOceanSimulation.GetProjection: TMatrix4f;
begin
  OceanSimulationGetProjection(FHandle, @Result);
end;

procedure TOceanSimulation.SetProjection(const AProjection: TMatrix4f);
begin
  OceanSimulationSetProjection(FHandle, @AProjection);
end;

function TOceanSimulation.GetScale: TVector3f;
begin
  OceanSimulationGetScale(FHandle, @Result);
end;

procedure TOceanSimulation.SetScale(const AScale: TVector3f);
begin
  if not OceanSimulationSetScale(FHandle, @AScale) then
    raise EAfterwarpException.Create(SCouldNotChangeScaleOfDisplacementMapIn3DOceanRenderingModule);
end;

function TOceanSimulation.GetPlane: TVector4f;
begin
  OceanSimulationGetPlane(FHandle, @Result);
end;

procedure TOceanSimulation.SetPlane(const APlane: TVector4f);
begin
  if not OceanSimulationSetPlane(FHandle, @APlane) then
    raise EAfterwarpException.Create(SCouldNotChangeGridPlaneEquationIn3DOceanRenderingModule);
end;

function TOceanSimulation.GetViewDistance: Single;
begin
  Result := OceanSimulationGetViewDistance(FHandle);
end;

procedure TOceanSimulation.SetViewDistance(const AViewDistance: Single);
begin
  if not OceanSimulationSetViewDistance(FHandle, AViewDistance) then
    raise EAfterwarpException.Create(SCouldNotChangeMaximumHeightViewDistanceIn3DOceanRenderingModule);
end;

function TOceanSimulation.GetAttributes: TSceneAttributes;
begin
  Result := OceanSimulationGetAttributes(FHandle);
end;

procedure TOceanSimulation.SetAttributes(const AAttributes: TSceneAttributes);
begin
  OceanSimulationSetAttributes(FHandle, AAttributes);
end;

function TOceanSimulation.GetMaterial: TOceanMaterial;
begin
  OceanSimulationGetMaterial(FHandle, @Result);
end;

procedure TOceanSimulation.SetMaterial(const AMaterial: TOceanMaterial);
begin
  if not OceanSimulationSetMaterial(FHandle, @AMaterial) then
    raise EAfterwarpException.Create(SCouldNotChangeObjectMaterialIn3DRenderingModule);
end;

function TOceanSimulation.GetSamplerShadow: TSampler;
begin
  Result.FHandle := OceanSimulationGetSamplerShadow(FHandle);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotRetrieveShadowMappingSamplerFrom3DRenderingModule);
end;

procedure TOceanSimulation.Update(const ALatency: Double);
begin
  if not OceanSimulationUpdate(FHandle, ALatency) then
    raise EAfterwarpException.Create(SCouldNotPerformUpdateIn3DRenderingModule);
end;

procedure TOceanSimulation.Render(const ALinearDepths: TTexture; const ASceneLights: TSceneLights;
  const AAtlas: TShadowCastingAtlas);
begin
  if not OceanSimulationRender(FHandle, ALinearDepths.Handle, ASceneLights.Handle, AAtlas.Handle) then
    raise EAfterwarpException.Create(SCouldNotPerformRenderingOf3DRenderingModule);
end;

{$ENDREGION}
{$REGION 'TTimer'}

class function TTimer.Create: TTimer;
begin
  Result.FHandle := TimerCreate;
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateTimerModule);
end;

procedure TTimer.Free;
begin
  if FHandle <> nil then
  begin
    TimerDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TTimer.GetSpeed: Double;
begin
  Result := TimerGetSpeed(FHandle);
end;

procedure TTimer.SetSpeed(const ASpeed: Double);
begin
  TimerSetSpeed(FHandle, ASpeed);
end;

function TTimer.GetFrameRate: Single;
begin
  Result := TimerGetFrameRate(FHandle);
end;

function TTimer.GetLatency: UInt64;
begin
  Result := TimerGetLatency(FHandle);
end;

function TTimer.GetTimeSlice: Int64;
begin
  Result := TimerGetTimeSlice(FHandle);
end;

function TTimer.GetSkippedTimeSlices: Int64;
begin
  Result := TimerGetSkippedTimeSlices(FHandle);
end;

function TTimer.GetTokens: Integer;
begin
  Result := TimerExtractTokens(FHandle);
end;

function TTimer.NextSlice: Boolean;
begin
  Result := TimerNextSlice(FHandle);
end;

function TTimer.UpdateNextSlice: Boolean;
begin
  Result := TimerUpdateNextSlice(FHandle);
end;

procedure TTimer.Update;
begin
  TimerUpdate(FHandle);
end;

procedure TTimer.Reset;
begin
  TimerReset(FHandle);
end;

procedure TTimer.TrimSkippedTimeSlices(const ASlicesToTrim: Int64);
begin
  TimerTrimSkippedTimeSlices(FHandle, ASlicesToTrim);
end;

{$ENDREGION}
{$REGION 'TActorCamera'}

class function TActorCamera.Create(const APosition: TVector3f;
  const ARotation: TVector3f; const ADistance: Single): TActorCamera;
begin
  Result.FHandle := ActorCameraCreate(@APosition, @ARotation, ADistance);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DCameraModule);
end;

class function TActorCamera.Create(const ADistance: Single): TActorCamera;
begin
  Result := Create(TVector3f.Zero, Vector3f(Pi * 0.25, 0.0, 0.0), ADistance);
  if Result.FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreate3DCameraModule);
end;

procedure TActorCamera.Free;
begin
  if FHandle <> nil then
  begin
    ActorCameraDestroy(FHandle);
    FHandle := nil;
  end;
end;

function TActorCamera.GetPosition: TVector3f;
begin
  ActorCameraGetPosition(FHandle, @Result);
end;

procedure TActorCamera.SetPosition(const APosition: TVector3f);
begin
  ActorCameraSetPosition(FHandle, @APosition);
end;

function TActorCamera.GetRotation: TVector4f;
begin
  ActorCameraGetRotation(FHandle, @Result);
end;

procedure TActorCamera.SetRotation(const ARotation: TVector4f);
begin
  ActorCameraSetRotation(FHandle, @ARotation);
end;

function TActorCamera.GetDistance: Single;
begin
  Result := ActorCameraGetDistance(FHandle);
end;

procedure TActorCamera.SetDistance(const ADistance: Single);
begin
  ActorCameraSetDistance(FHandle, ADistance);
end;

function TActorCamera.GetConstraints: TCameraConstraints;
begin
  ActorCameraGetConstraints(FHandle, @Result);
end;

procedure TActorCamera.SetConstraints(const AConstraints: TCameraConstraints);
begin
  ActorCameraSetConstraints(FHandle, @AConstraints);
end;

function TActorCamera.GetView: TMatrix4f;
begin
  ActorCameraGetView(FHandle, @Result);
end;

function TActorCamera.GetQuaternion: TQuaternion;
begin
  ActorCameraGetQuaternion(FHandle, @Result);
end;

procedure TActorCamera.SetQuaternion(const AQuaternion: TQuaternion);
begin
  ActorCameraSetQuaternion(FHandle, @AQuaternion);
end;

function TActorCamera.GetForward: TVector3f;
begin
  ActorCameraGetForward(FHandle, @Result);
end;

function TActorCamera.GetRight: TVector3f;
begin
  ActorCameraGetRight(FHandle, @Result);
end;

function TActorCamera.GetCeiling: TVector3f;
begin
  ActorCameraGetCeiling(FHandle, @Result);
end;

function TActorCamera.Command(const ACommand: TCameraCommand; const APosition: TVector3f;
  const ASize: TPoint2f; const ASense: TVector3f; const AProjection: TMatrix4f): Boolean;
begin
  Result := ActorCameraCommand(FHandle, ACommand, @APosition, @ASize, @ASense, @AProjection);
end;

function TActorCamera.Command(const ACommand: TCameraCommand; const APosition: TVector3f;
  const ASize: TPoint2f; const ASense: TVector3f): Boolean;
begin
  Result := ActorCameraCommand(FHandle, ACommand, @APosition, @ASize, @ASense, nil);
end;

function TActorCamera.Command(const ACommand: TCameraCommand; const APosition: TVector3f;
  const ASize: TPoint2f): Boolean;
begin
  Result := ActorCameraCommand(FHandle, ACommand, @APosition, @ASize, nil, nil);
end;

procedure TActorCamera.Zoom(const APosition, ASize: TPoint2f; const AProjection: TMatrix4f;
  const ADelta: Single);
begin
  ActorCameraZoom(FHandle, @APosition, @ASize, @AProjection, ADelta);
end;

procedure TActorCamera.ZoomOrtho(const APosition, ASize: TPoint2f; const ADistance,
  AAdjustedDistance: Single; const AProjection, AAdjustedProjection: TMatrix4f);
begin
  ActorCameraZoomOrtho(FHandle, @APosition, @ASize, ADistance, AAdjustedDistance, @AProjection,
    @AAdjustedProjection);
end;

{$ENDREGION}
{$REGION 'TApplication'}

function FuncAppCreate(AApplicationHandle: Pointer; AUser: Pointer): TLibraryBool; cdecl;
begin
  if Assigned(TApplication(AUser).FOnCreate) then
    Result := TApplication(AUser).FOnCreate(TApplication(AUser))
  else
    Result := True;
end;

procedure FuncAppDestroy(AApplicationHandle: Pointer; AUser: Pointer); cdecl;
begin
  if Assigned(TApplication(AUser).FOnDestroy) then
    TApplication(AUser).FOnDestroy(TApplication(AUser));
end;

procedure FuncAppRender(AApplicationHandle: Pointer; AUser: Pointer); cdecl;
begin
  if Assigned(TApplication(AUser).FOnRender) then
    TApplication(AUser).FOnRender(TApplication(AUser));
end;

procedure FuncAppMouse(AApplicationHandle: Pointer; AEvent: TMouseEvent; AButton: TMouseButton;
  APosition: PPoint2i; AUser: Pointer); cdecl;
begin
  if Assigned(TApplication(AUser).FOnMouse) then
    TApplication(AUser).FOnMouse(TApplication(AUser), AEvent, AButton, APosition^);
end;

procedure FuncAppKey(AApplicationHandle: Pointer; AEvent: TKeyEvent; AVirtualKey: Integer;
  AKeyCode: Word; AUser: Pointer); cdecl;
begin
  if Assigned(TApplication(AUser).FOnKey) then
    TApplication(AUser).FOnKey(TApplication(AUser), AEvent, AVirtualKey, AKeyCode);
end;

procedure FuncAppStatus(AApplicationHandle: Pointer; AEvent: TApplicationEvent; AUser: Pointer); cdecl;
begin
  if Assigned(TApplication(AUser).FOnStatus) then
    TApplication(AUser).FOnStatus(TApplication(AUser), AEvent);
end;

function FuncAppIdle(AApplicationHandle: Pointer; AUser: Pointer): TLibraryBool; cdecl;
begin
  if Assigned(TApplication(AUser).FOnIdle) then
    Result := TApplication(AUser).FOnIdle(TApplication(AUser))
  else
    Result := True;
end;

procedure FuncAppResize(AApplicationHandle: Pointer; AUser: Pointer); cdecl;
begin
  if Assigned(TApplication(AUser).FOnResize) then
    TApplication(AUser).FOnResize(TApplication(AUser));
end;

function FuncAppHook(AApplicationHandle: Pointer; AEvent: Pointer;
  AUser: Pointer): TLibraryBool; cdecl;
begin
  if Assigned(TApplication(AUser).FOnHook) then
    Result := TApplication(AUser).FOnHook(TApplication(AUser), AEvent)
  else
    Result := False;
end;

// Application members.

constructor TApplication.Create(const AHandleInstance, AHandleIconSmall, AHandleIconBig: TUntypedHandle;
  const AWindowClassName: Utf8String; const ASize: TPoint2i; const AState: TApplicationWindowState;
  const APosition: PPoint2i);
var
  LConfiguration: TApplicationConfiguration;
begin
  FillChar(LConfiguration, SizeOf(TApplicationConfiguration), 0);

  LConfiguration.Size := ASize;
  LConfiguration.State := AState;
  LConfiguration.Startup.Win.HandleInstance := AHandleInstance;
  LConfiguration.Startup.Win.HandleIconSmall := AHandleIconSmall;
  LConfiguration.Startup.Win.HandleIconBig := AHandleIconBig;

  if APosition <> nil then
    LConfiguration.Position := APosition^
  else
    LConfiguration.Position := Point2i(High(Integer), High(Integer));

  if Length(AWindowClassName) > 0 then
    Move(AWindowClassName[1], LConfiguration.Startup.Win.WindowClassName, Min(Length(AWindowClassName), 32));

  FHandle := ApplicationCreate(@LConfiguration);
  if FHandle = nil then
    raise EAfterwarpException.Create(SCouldNotCreateApplicationModule);

  InitializeEvents;
end;

constructor TApplication.Create(const ASize: TPoint2i; const AState: TApplicationWindowState;
  const APosition: PPoint2i);
var
  LConfiguration: TApplicationConfiguration;
begin
  LConfiguration.Size := ASize;
  LConfiguration.State := AState;
  LConfiguration.Startup.Nx.RenderingType := 0;
  LConfiguration.Startup.Nx.ParameterCount := 0;
  LConfiguration.Startup.Nx.ParameterStrings := nil;

  if APosition <> nil then
    LConfiguration.Position := APosition^
  else
    LConfiguration.Position := Point2i(High(Integer), High(Integer));

  FHandle := ApplicationCreate(@LConfiguration);
  InitializeEvents;
end;

constructor TApplication.Create(const AHandle: TUntypedObjectHandle);
begin
  FHandle := AHandle;
end;

procedure TApplication.InitializeEvents;
var
  LEvents: TApplicationEvents;
begin
  LEvents.EventCreate := FuncAppCreate;
  LEvents.EventDestroy := FuncAppDestroy;
  LEvents.EventRender := FuncAppRender;
  LEvents.EventMouse := FuncAppMouse;
  LEvents.EventKey := FuncAppKey;
  LEvents.EventStatus := FuncAppStatus;
  LEvents.EventIdle := FuncAppIdle;
  LEvents.EventResize := FuncAppResize;
  LEvents.EventHook := FuncAppHook;

  ApplicationSetEvents(FHandle, @LEvents, Self);
end;

destructor TApplication.Destroy;
begin
  if FHandle <> nil then
  begin
    ApplicationDestroy(FHandle);
    FHandle := nil;
  end;
  inherited;
end;

function TApplication.GetWindowHandle: TUntypedHandle;
begin
  Result := ApplicationGetWindowHandle(FHandle);
end;

function TApplication.GetTitle: Utf8String;
var
  LLength: LongInt;
begin
  ApplicationGetTitle(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    ApplicationGetTitle(FHandle, @Result[1], @LLength);
end;

procedure TApplication.SetTitle(const ATitle: Utf8String);
begin
  if not ApplicationSetTitle(FHandle, PAnsiChar(ATitle)) then
    raise EAfterwarpException.CreateFmt(SCouldNotChangeApplicationTitle, [ATitle]);
end;

function TApplication.GetIconTitle: Utf8String;
var
  LLength: LongInt;
begin
  ApplicationGetIconTitle(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    ApplicationGetIconTitle(FHandle, @Result[1], @LLength);
end;

procedure TApplication.SetIconTitle(const AIconTitle: Utf8String);
begin
  if not ApplicationSetIconTitle(FHandle, PAnsiChar(AIconTitle)) then
    raise EAfterwarpException.CreateFmt(SCouldNotChangeApplicationIconTitle, [AIconTitle]);
end;

function TApplication.GetExecutablePath: Utf8String;
var
  LLength: LongInt;
begin
  ApplicationGetExecutablePath(FHandle, nil, @LLength);
  SetLength(Result, LLength);
  if LLength > 0 then
    ApplicationGetExecutablePath(FHandle, @Result[1], @LLength);
end;

function TApplication.GetWindowRect: TIntRect;
begin
  ApplicationGetWindowRect(FHandle, @Result);
end;

procedure TApplication.SetWindowRect(const ARect: TIntRect);
begin
  ApplicationSetWindowRect(FHandle, @ARect);
end;

function TApplication.GetClientRect: TIntRect;
begin
  ApplicationGetClientRect(FHandle, @Result);
end;

function TApplication.GetMinimalSize: TPoint2i;
begin
  ApplicationGetMinimalSize(FHandle, @Result);
end;

procedure TApplication.SetMinimalSize(const AMinimalSize: TPoint2i);
begin
  ApplicationSetMinimalSize(FHandle, @AMinimalSize);
end;

procedure TApplication.SetClientSize(const ASize: TPoint2i);
begin
  ApplicationSetClientSize(FHandle, @ASize);
end;

function TApplication.GetWindowScale: Double;
begin
  Result := ApplicationGetWindowScale(FHandle);
end;

function TApplication.GetWindowState: TApplicationWindowState;
begin
  Result := ApplicationGetWindowState(FHandle);
end;

procedure TApplication.SetWindowState(const AState: TApplicationWindowState);
begin
  if not ApplicationSetWindowState(FHandle, AState) then
    raise EAfterwarpException.CreateFmt(SCouldNotChangeApplicationWindowState, [Ord(AState)]);
end;

function TApplication.GetClipboard: Utf8String;
var
  LLength: Integer;
begin
  SetLength(Result, StringBufferLength);
  LLength := Length(Result);

  if (ApplicationReadTextFromClipboard(FHandle, @Result[1], @LLength)) then
    SetLength(Result, LLength)
  else
    SetLength(Result, 0);
end;

procedure TApplication.SetClipboard(const AClipboard: Utf8String);
begin
  if not ApplicationWriteTextToClipboard(FHandle, PAnsiChar(AClipboard)) then
    raise EAfterwarpException.Create(SCouldNotSetApplicationClipboard);
end;

function TApplication.GetCursor: TAppCursor;
begin
  Result := ApplicationGetCursor(FHandle);
end;

procedure TApplication.SetCursor(const ACursor: TAppCursor);
begin
  if not ApplicationSetCursor(FHandle, ACursor) then
    raise EAfterwarpException.Create(SCouldNotChangeApplicationCursor);
end;

function TApplication.GetMouseInputCaptured: Boolean;
begin
  Result := ApplicationMouseInputCaptured(FHandle);
end;

procedure TApplication.Invalidate;
begin
  ApplicationInvalidate(FHandle);
end;

procedure TApplication.Execute;
begin
  if not ApplicationExecute(FHandle) then
    raise EAfterwarpException.Create(SApplicationFailedToExecute);
end;

procedure TApplication.Terminate;
begin
  ApplicationTerminate(FHandle);
end;

function TApplication.TranslateVirtualKey(const AVirtualKey: Integer): TKey;
begin
  Result := ApplicationTranslateVirtualKey(FHandle, AVirtualKey);
end;

function TApplication.ConvertPortableKey(const AKey: TKey): Integer;
begin
  Result := ApplicationConvertPortableKey(FHandle, AKey);
end;

function TApplication.TryCaptureMouseInput: Boolean;
begin
  Result := ApplicationCaptureMouseInput(FHandle);
end;

function TApplication.TryReleaseMouseInput: Boolean;
begin
  Result := ApplicationReleaseMouseInput(FHandle);
end;

procedure TApplication.CaptureMouseInput;
begin
  if not ApplicationCaptureMouseInput(FHandle) then
    raise EAfterwarpException.Create(SFailedCapturingApplicationMouseInput);
end;

procedure TApplication.ReleaseMouseInput;
begin
  if not ApplicationReleaseMouseInput(FHandle) then
    raise EAfterwarpException.Create(SFailedReleasingApplicationMouseInput);
end;

procedure TApplication.SetIcons(const AIcons: array of TSurface);
begin
  if Length(AIcons) <= 0 then
    raise EAfterwarpException.Create(SInvalidNumberOfIconsForApplication);

  if not ApplicationSetIcons(FHandle, @AIcons[0].FHandle, Length(AIcons)) then
    raise EAfterwarpException.Create(SFailedChangingApplicationIconsFromSurfaces);
end;

procedure TApplication.SetIconsFromFiles(const AIconNames: array of Utf8String);
const
  FileNameLength = 254;
type
  TFileName = array[0..FileNameLength - 1] of AnsiChar;
var
  LFileNames: array of TFileName;
  LFileNamePtrs: array of PAnsiChar;
  I: Integer;
begin
  if Length(AIconNames) < 1 then
    raise EAfterwarpException.Create(SInvalidApplicationIconFiles);

  SetLength(LFileNames, Length(AIconNames));
  SetLength(LFileNamePtrs, Length(AIconNames));

  for I := 0 to Length(LFileNamePtrs) - 1 do
  begin
    if Length(AIconNames[I]) < 1 then
      raise EAfterwarpException.Create(SInvalidApplicationIconFiles);

    FillChar(LFileNames[I][0], SizeOf(TFileName), 0);
    LFileNamePtrs[I] := @LFileNames[I][0];

    StrPLCopy(LFileNamePtrs[I], AIconNames[I], FileNameLength);
  end;

  if not ApplicationSetIconsFromFiles(FHandle, @LFileNamePtrs[0], Length(LFileNamePtrs)) then
    raise EAfterwarpException.Create(SFailedChangingApplicationIconsFromFiles);
end;

function TApplication.FileChooserDialog(var AFilePath: Utf8String; const ADialog: TFileChooserDialog;
  const AFilters, ACaption, AAccept, AReject: Utf8String): Boolean;
var
  LFilePathLength: LongInt;
  I: Integer;
begin
  LFilePathLength := Length(AFilePath);
  SetLength(AFilePath, StringBufferLength);

  for I := LFilePathLength to Length(AFilePath) - 1 do
    AFilePath[I] := #0;

  LFilePathLength := StringBufferLength;

  Result := ApplicationFileChooserDialog(FHandle, @AFilePath[1], @LFilePathLength, ADialog,
    PAnsiChar(AFilters), PAnsiChar(ACaption), PAnsiChar(AAccept), PAnsiChar(AReject));

  SetLength(AFilePath, LFilePathLength);
end;

{$ENDREGION}
{$REGION 'TRandomSequence'}

class function TRandomSequence.Create: TRandomSequence;
begin
  RandomSequenceInit(@Result.FState);
end;

class function TRandomSequence.Create(const ASeed: UInt64): TRandomSequence;
begin
  RandomSequenceInitBySeed(@Result.FState, ASeed);
end;

function TRandomSequence.Raw: Cardinal;
begin
  Result := RandomSequenceGenerate(@FState);
end;

function TRandomSequence.Raw64: UInt64;
begin
  Result := RandomSequenceGenerate64(@FState);
end;

function TRandomSequence.Value: Single;
begin
  Result := RandomSequenceGenerateFloat(@FState);
end;

function TRandomSequence.ValueDouble: Double;
begin
  Result := RandomSequenceGenerateDouble(@FState);
end;

function TRandomSequence.Ranged(const ARange: Integer): Integer;
begin
  Result := RandomSequenceGenerateRanged(@FState, ARange);
end;

function TRandomSequence.Gaussian: Single;
begin
  Result := RandomSequenceGenerateGaussian(@FState);
end;

{$ENDREGION}

end.