diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/ExampleHelpers.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/ExampleHelpers.pas
new file mode 100644
index 0000000..e8127ba
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/ExampleHelpers.pas
@@ -0,0 +1,68 @@
+namespace MetalExample;
+
+interface
+uses
+ Metal,
+ MetalKit;
+
+
+type
+ MetalHelper = class
+ public
+ class method createBox(_device: MTLDevice) : VertexBuffer;
+ end;
+
+implementation
+
+class method MetalHelper.createBox(_device: MTLDevice): VertexBuffer;
+
+const
+
+ BOX_VERTICES: array of Single = [
+ // Positions // Normals
+ // unten
+ -0.5, -0.5, -0.5, 0.0, 0.0, -1.0, // 0
+ 0.5, -0.5, -0.5, 0.0, 0.0, -1.0, // 1
+ 0.5, 0.5, -0.5, 0.0, 0.0, -1.0, //2
+ -0.5, 0.5, -0.5, 0.0, 0.0, -1.0, //3
+ // Oben
+ -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, //4
+ 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, //5
+ 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, // 6
+ -0.5, 0.5, 0.5, 0.0, 0.0, 1.0, //7
+//Links
+ -0.5, 0.5, 0.5, -1.0, 0.0, 0.0, //8
+ -0.5, 0.5, -0.5, -1.0, 0.0, 0.0, //9
+ -0.5, -0.5, -0.5, -1.0, 0.0, 0.0, //10
+ -0.5, -0.5, 0.5, -1.0, 0.0, 0.0, //11
+//Rechts
+ 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, //12
+ 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, //13
+ 0.5, -0.5, -0.5, 1.0, 0.0, 0.0, //14
+ 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, //15
+// hinten
+ -0.5, -0.5, -0.5, 0.0, -1.0, 0.0, //16
+ 0.5, -0.5, -0.5, 0.0, -1.0, 0.0, //17
+ 0.5, -0.5, 0.5, 0.0, -1.0, 0.0, //18
+ -0.5, -0.5, 0.5, 0.0, -1.0, 0.0, //19
+
+ -0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
+ 0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
+ 0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
+ -0.5, 0.5, 0.5, 0.0, 1.0, 0.0];
+
+
+ { The indices define 2 triangles per cube face, 6 faces total }
+ INDICES: array of UInt16 = [
+ 2, 1, 0, 0, 3, 2, // Unten
+ 4, 5, 6, 6, 7, 4, // Oben
+ 8, 9, 10, 10, 11, 8, //Links
+ 14, 13, 12, 12, 15, 14,// Rechts
+ 16, 17, 18, 18, 19, 16 , //Hinten
+ 22, 21, 20, 20, 23, 22]; // Vorne
+begin
+ var FBox := new VertexArray(BOX_VERTICES, 6, INDICES);
+ result := VertexBuffer.newBuffer(_device) SoureArray(FBox);
+end;
+
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/ExampleMetalView.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/ExampleMetalView.pas
index 65019d4..96c7dbf 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/ExampleMetalView.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/ExampleMetalView.pas
@@ -6,10 +6,38 @@ interface
MetalKit;
type
+ mouseDownstate = enum(none, cmd, shift, alt, ctrl);
[IBObject]
Metal_View = public class(MTKView)
+private
+
+ trackingArea: NSTrackingArea;
+ fMousePos : NSPoint;
+ {$HIDE H6}
+ fMouseDownPos : NSPoint;
+ mState : mouseDownstate;
+ {$SHOW H6}
+ {$HIDE H7}
+ fMousein : Boolean;
+ fFilllayer : Boolean := true;
+ {$SHOW H7}
+ method setTrackingAreas;
+
+private
+ fMouseDelegate : nullable MetalMouseDelegate;
public
method awakeFromNib; override;
+ method setFrameSize(newSize: NSSize); override;
+
+ method mouseEntered(&event: not nullable NSEvent); override;
+ method mouseExited(&event: not nullable NSEvent); override;
+ method mouseDown(&event: not nullable NSEvent); override;
+ method mouseUp(&event: not nullable NSEvent); override;
+ method mouseMoved(&event: not nullable NSEvent); override;
+ method mouseDragged(&event: not nullable NSEvent); override;
+
+ property MouseDelegate : nullable MetalMouseDelegate read fMouseDelegate write fMouseDelegate;
+
end;
implementation
@@ -19,8 +47,84 @@ implementation
inherited;
device := MTLCreateSystemDefaultDevice();
if device = nil then
- NSLog("Could not create a default MetalDevice!!")
+ NSLog("Could not create a default MetalDevice!!");
+ setTrackingAreas;
+end;
+
+method Metal_View.mouseEntered(&event: not nullable NSEvent);
+begin
+ fMousein := true;
+ NSLog("mouseEntered");
+ if fMouseDelegate:DontShowCursor then
+ begin
+ NSCursor.hide;
+ fMouseDelegate:showCrosshair := true;
+ end;
+end;
+
+method Metal_View.mouseExited(&event: not nullable NSEvent);
+begin
+ fMousein := false;
+ NSLog("mouseExited");
+ // if fMouseDelegate:DontShowCursor then
+ NSCursor.unhide;
+ fMouseDelegate:showCrosshair := false;
+end;
+
+method Metal_View.mouseDown(&event: not nullable NSEvent);
+begin
+ NSLog("mouseDown");
+end;
+
+method Metal_View.mouseUp(&event: not nullable NSEvent);
+begin
+ NSLog("mouseUp");
end;
+method Metal_View.mouseMoved(&event: not nullable NSEvent);
+begin
+ fMousePos :=convertPointToBacking(
+ convertPoint(&event.locationInWindow) fromView(nil));
+ fMouseDelegate:MouseMove(fMousePos.x, fMousePos.y);
+ // NSLog("mouseMoved");
+end;
+
+method Metal_View.mouseDragged(&event: not nullable NSEvent);
+begin
+ fMousePos :=convertPointToBacking(
+ convertPoint(&event.locationInWindow) fromView(nil));
+ fMouseDelegate:MouseMove(fMousePos.x, fMousePos.y);
+ // NSLog("mouseDragged");
+end;
+
+
+
+method Metal_View.setTrackingAreas;
+begin
+ // Remove existing tracking area if necessary.
+ if trackingArea <> nil then
+ removeTrackingArea(trackingArea);
+
+ // Create new tracking area.
+ var options: NSTrackingAreaOptions := [
+ NSTrackingAreaOptions.MouseEnteredAndExited,
+ NSTrackingAreaOptions.MouseMoved,
+ NSTrackingAreaOptions.ActiveInActiveApp,
+ NSTrackingAreaOptions.NSTrackingInVisibleRect
+ ,NSTrackingAreaOptions.EnabledDuringMouseDrag
+
+ // NSTrackingAreaOptions.ActiveInActiveApp,
+ ];
+ trackingArea := new NSTrackingArea() withRect(frame) options(options) owner(self) userInfo(nil);
+ addTrackingArea(trackingArea);
+end;
+
+method Metal_View.setFrameSize(newSize: NSSize);
+begin
+ inherited setFrameSize(newSize);
+ setTrackingAreas;
+end;
+
+
end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.Metal.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.Metal.pas
index d451ee1..3743b85 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.Metal.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.Metal.pas
@@ -12,7 +12,7 @@
method switchApp(const AppId : Integer);
//method MainWindowController.switchApp(const AppId: Integer);
begin
- var App : MTKViewDelegate := nil;
+ var App : MetalBaseDelegate := nil;
case AppId of
0 : begin
@@ -29,10 +29,14 @@
App := new MetalExample3 InitWithMetalKitView(ViewGL);
TimeLabel.label := '"Basic Texturing" running';
end;
-
3 : begin
App := new MetalExample4 InitWithMetalKitView(ViewGL);
TimeLabel.label := '"Basic Texturing" with Blend running';
+ end;
+
+ 4 : begin
+ App := new MetalExample5 InitWithMetalKitView(ViewGL);
+ TimeLabel.label := '"Draw a Cube" running';
end
else
begin
@@ -51,7 +55,7 @@
renderer := App;
renderer.mtkView(ViewGL) drawableSizeWillChange(ViewGL.drawableSize);
ViewGL.delegate := renderer;
-
+ ViewGL.MouseDelegate := App;
end;
end;
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.pas
index dd3d187..4731c9e 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.pas
@@ -19,11 +19,12 @@ interface
var ViewGL: Metal_View;
[IBOutlet]
var TimeLabel : NSToolbarItem;
+
[IBAction]
method pressAppButton(sender: id);
// Overrides from NSWindowController
- method init: instancetype; override;
+ method init: InstanceType; override;
method windowDidLoad; override;
end;
@@ -43,6 +44,8 @@ implementation
begin
switchApp(-1);
ViewGL.preferredFramesPerSecond := 60;
+ ViewGL.depthStencilPixelFormat := MTLPixelFormat.Depth32Float;
+ //ViewGL.depthStencilTexture
end;
end;
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.xib b/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.xib
index a8d84af..9f0a63e 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.xib
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MainWindowController.xib
@@ -25,7 +25,7 @@
-
+
@@ -42,21 +42,26 @@
-
+
-
+
-
+
+
+
+
+
+
@@ -77,11 +82,12 @@
+
-
+
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample.elements b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample.elements
index 0017c13..68ba712 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample.elements
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample.elements
@@ -10,14 +10,16 @@
False
False
False
+ true
Release
macOS
True
True
.\Resources\Info.plist
- Resources\Entitlements.entitlements
.\Resources\App.icns
RemObjects.Elements.RTL
+ Oxygene
+ .\Resources\Entitlements.entitlements
false
@@ -29,6 +31,7 @@
False
True
False
+ true
true
@@ -38,6 +41,7 @@
False
False
True
+ false
@@ -49,6 +53,7 @@
+
@@ -57,6 +62,7 @@
+
@@ -78,11 +84,21 @@
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample2.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample2.pas
index ce20e49..f1da6cc 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample2.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample2.pas
@@ -58,7 +58,8 @@ implementation
renderEncoder.setRenderPipelineState(_pipelineState);
-
+ renderEncoder.setTriangleFillMode(MTLTriangleFillMode.Fill);
+ renderEncoder.setCullMode(MTLCullMode.None);
// We call -[MTLRenderCommandEncoder setVertexBuffer:offset:atIndex:] to send data in our
// preloaded MTLBuffer from our ObjC code here to our Metal 'vertexShader' function
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample4.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample4.pas
index 4aeb60f..b6abf6b 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample4.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample4.pas
@@ -18,8 +18,11 @@ MetalExample4 = class(MetalBaseDelegate)
_viewportSize : array [0..1] of UInt32;//Integer;
_texture : array of MTLTexture;
_Vertextes : array of AAPLVertex3;
- akttex : Integer := 0;
- aktloop : Integer;
+ _BlendFac : Single := 0.0;
+ _BlendStep : Single := 0.0095;
+
+ method switchBlend();
+
method mtkView(view: not nullable MTKView) drawableSizeWillChange(size: CGSize); override;
method drawInMTKView(view: not nullable MTKView); override;
@@ -91,8 +94,8 @@ constructor MetalExample4 initWithMetalKitView(const mtkView: not nullable MTKVi
begin
var Loader : MTKTextureLoader := new MTKTextureLoader withDevice(_device);
var Lerror : Error;
- var texturl0 := Asset.getUrlfor("Tex1.JPG");
- var texturl1 := Asset.getUrlfor("coral.JPG");
+ var texturl1 := Asset.getUrlfor("Tex1.JPG");
+ var texturl0 := Asset.getUrlfor("coral.JPG");
var lDict := NSDictionary.dictionaryWithObjects(
@@ -134,13 +137,15 @@ constructor MetalExample4 initWithMetalKitView(const mtkView: not nullable MTKVi
result := new AAPLVertex3[6];
const hsize = 600.0;
const vsize = 400.0;
+ const dsize = 0.0;
//Positions
result[0].position := [hsize, -vsize];
- result[1].position := [-hsize, -vsize];
- result[2].position := [-hsize, vsize];
+ result[1].position := [-hsize, -vsize+dsize];
+ result[2].position := [-hsize, vsize-dsize];
+
result[3].position := [hsize, -vsize];
- result[4].position := [-hsize, vsize];
+ result[4].position := [-hsize, vsize-dsize];
result[5].position := [hsize, vsize];
// Texture
@@ -193,16 +198,11 @@ constructor MetalExample4 initWithMetalKitView(const mtkView: not nullable MTKVi
// for its index
renderEncoder.setVertexBytes(@_viewportSize[0]) length(sizeOf(Int32)*2) atIndex(AAPLVertexInputIndexViewportSize );
- renderEncoder.setFragmentTexture(_texture[0]) atIndex(0);
-
-
- // Draw the 3 vertices of our triangle
- renderEncoder.drawPrimitives(MTLPrimitiveType.MTLPrimitiveTypeTriangle) vertexStart(0) vertexCount(6);
-
renderEncoder.setFragmentTexture(_texture[1]) atIndex(0);
+ renderEncoder.setFragmentTexture(_texture[0]) atIndex(1);
+ renderEncoder.setFragmentBytes(@_BlendFac) length(sizeOf(Single)) atIndex(0 );
-
- // Draw the 3 vertices of our triangle
+ // Draw the 3 vertices of our triangle
renderEncoder.drawPrimitives(MTLPrimitiveType.MTLPrimitiveTypeTriangle) vertexStart(0) vertexCount(6);
@@ -211,12 +211,15 @@ constructor MetalExample4 initWithMetalKitView(const mtkView: not nullable MTKVi
end;
commandBuffer.commit();
- inc(aktloop);
- if aktloop > 100 then
- begin
- akttex := if akttex = 0 then 1 else 0;
- aktloop := 0;
- end;
+ switchBlend();
+
+end;
+
+method MetalExample4.switchBlend;
+begin
+ _BlendFac := _BlendFac+ _BlendStep;;
+ if (_BlendFac > 1.0) or (_BlendFac < 0) then
+ _BlendStep := -_BlendStep;
end;
end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample5.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample5.pas
new file mode 100644
index 0000000..8ae464f
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample5.pas
@@ -0,0 +1,305 @@
+namespace MetalExample;
+
+interface
+uses
+ Metal,
+ MetalKit;
+
+type
+ // "3d Box"
+
+ Uniforms = record
+ {$HIDE H7}
+ modelMatrix : TMatrix4;
+ projectionMatrix : TMatrix4;
+ {$SHOW H7}
+ normalMatrix : TMatrix3;
+ end;
+
+
+ MetalExample5 = class(MetalBaseDelegate)
+ private
+ method DrawCursor(renderEncoder: IMTLRenderCommandEncoder);
+ method CreateCursorPipeline(const mtkView: not nullable MTKView);
+ method gBufferRenderPassDescriptor(const mtkView: not nullable MTKView): MTLRenderPassDescriptor;
+ method zBufferTexture(const x,y : Integer): MTLTexture;
+ // Box
+ const
+ cShaderName = 'AAPLShaders5.metallib';
+ cVertexFuncName = 'basic_vertex';
+ cFragmentFuncName = 'basic_fragment';
+ var
+ _pipelineState :MTLRenderPipelineState ;
+ _depthStencilState : MTLDepthStencilState;
+ _viewportSize : array [0..1] of UInt32;//Integer;
+ _vertexBuffer : VertexBuffer;//s;
+ // _numVertices : NSUInteger ;
+ _uniform : Uniforms;
+ FmodelRotation : Single := 0;
+
+ // Cursor
+
+ var
+ _CursorPipelineState :MTLRenderPipelineState ;
+ mx, my : Single;
+ // Interface
+ method drawInMTKView(view: not nullable MTKView); override;
+ method mtkView(view: not nullable MTKView) drawableSizeWillChange(size: CGSize); override;
+
+ private
+ //FRemObjectsVAO: array of VertexArray;
+ method UpdateViewAndProjection(const width: Single; const Height: Single);
+
+ method createBox(_device: MTLDevice);
+ method MouseMove(const amx, amy : Single); override;
+ method DontShowCursor : Boolean; override;
+ begin
+ exit true;
+ end;
+ public
+ constructor initWithMetalKitView(const mtkView : not nullable MTKView);// : MTKViewDelegate;
+ end;
+implementation
+
+method MetalExample5.drawInMTKView(view: not nullable MTKView);
+begin
+ // exit;
+ var commandBuffer := _commandQueue.commandBuffer();
+ commandBuffer.label := 'MyCommand';
+ // var renderPassDescriptor: MTLRenderPassDescriptor := view.currentRenderPassDescriptor;
+
+ var renderPassDescriptor: MTLRenderPassDescriptor := gBufferRenderPassDescriptor(view);//view.currentRenderPassDescriptor;
+
+ if renderPassDescriptor ≠ nil then
+ begin
+ UpdateViewAndProjection(view.drawableSize.width, view.drawableSize.height);
+
+
+ var renderEncoder: IMTLRenderCommandEncoder := commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor);
+ //var renderEncoder: IMTLRenderCommandEncoder := commandBuffer.renderCommandEncoderWithDescriptor(view.currentRenderPassDescriptor);
+ renderEncoder.label := 'MyRenderEncoder';
+
+ renderEncoder.setRenderPipelineState(_pipelineState);
+
+ renderEncoder.setDepthStencilState(_depthStencilState);
+ renderEncoder.setTriangleFillMode(MTLTriangleFillMode.Fill);
+
+
+ // renderEncoder.setDepthStoreAction(MTLStoreAction.MTLStoreActionStore);
+
+ renderEncoder.setFrontFacingWinding(MTLWinding.CounterClockwise);
+ renderEncoder.setCullMode(MTLCullMode.None);
+
+
+ renderEncoder.setVertexBuffer(_vertexBuffer.verticies ) offset(0) atIndex(AAPLVertexInputIndexVertices);
+
+ renderEncoder.setVertexBytes(@_uniform) length(sizeOf(_uniform)) atIndex(AAPLVertexInputIndexViewportSize );
+ // renderEncoder.setFragmentBytes(@_uniform) length(sizeOf(_uniform)) atIndex(0 );
+ // Draw the vertices of our Box
+ renderEncoder.drawPrimitives(MTLPrimitiveType.MTLPrimitiveTypeTriangle) vertexStart(0) vertexCount(_vertexBuffer.Count);
+
+ // Try to show the Cursor
+ if showCrosshair then
+ DrawCursor(renderEncoder);
+ renderEncoder.endEncoding();
+
+
+
+ commandBuffer.presentDrawable(view.currentDrawable);
+ end;
+ commandBuffer.commit();
+
+ {Change the Rotation in every step}
+ if FmodelRotation >= 360 then
+ FmodelRotation := 1.0 else
+ FmodelRotation := FmodelRotation + 1.0;
+
+end;
+
+method MetalExample5.DrawCursor(renderEncoder: IMTLRenderCommandEncoder);
+var a : Array of AAPLVertex1;
+begin
+
+ var G : Color := Color.createGreen;
+ var R : Color := Color.createRed;
+ a := new AAPLVertex1[4];
+ a[0].color := G;
+ a[1].color := R;
+
+ a[2].color := G;
+ a[3].color := R;
+ var xpos : Single := _viewportSize[0] / 2;
+ var ypos : Single := _viewportSize[1] / 2;
+
+
+ a[0].position := [-xpos, my];
+ a[1].position := [xpos, my];
+ a[2].position := [mx, -ypos];
+ a[3].position := [mx, ypos];
+ renderEncoder.setRenderPipelineState(_CursorPipelineState);
+ renderEncoder.setVertexBytes(@a[0]) length(sizeOf(AAPLVertex1) * a.length) atIndex(0 );
+ renderEncoder.setVertexBytes(@_viewportSize[0]) length(sizeOf(Int32)*2) atIndex(AAPLVertexInputIndexViewportSize );
+ renderEncoder.drawPrimitives(MTLPrimitiveType.Line) vertexStart(0) vertexCount(4);
+
+
+end;
+
+
+method MetalExample5.mtkView(view: not nullable MTKView) drawableSizeWillChange(size: CGSize);
+begin
+ _viewportSize[0] := Convert.ToInt32(size.width);
+ _viewportSize[1] := Convert.ToInt32(size.height);
+ UpdateViewAndProjection(size.width, size.height);
+end;
+
+constructor MetalExample5 initWithMetalKitView(const mtkView: not nullable MTKView);
+begin
+ inherited;
+ var ShaderLoader := new shaderLoader(_device) Shadername(cShaderName) Vertexname(cVertexFuncName) Fragmentname(cFragmentFuncName);
+ if ShaderLoader = nil then exit nil
+
+ else
+ begin
+ var lError : Error;
+ UpdateViewAndProjection(mtkView.drawableSize.width, mtkView.drawableSize.height);
+ // Configure a pipeline descriptor that is used to create a pipeline state
+ var pipelineStateDescriptor : MTLRenderPipelineDescriptor := new MTLRenderPipelineDescriptor();
+ pipelineStateDescriptor.label := "Simple Pipeline";
+ pipelineStateDescriptor.vertexFunction := ShaderLoader.VertexFunc;
+ pipelineStateDescriptor.fragmentFunction := ShaderLoader.FragmentFunc;
+ pipelineStateDescriptor.colorAttachments[0].pixelFormat := mtkView.colorPixelFormat;
+ pipelineStateDescriptor.depthAttachmentPixelFormat := MTLPixelFormat.Depth32Float;
+ // pipelineStateDescriptor.d
+
+ var depthStencilDescriptor : MTLDepthStencilDescriptor := new MTLDepthStencilDescriptor;
+
+
+ depthStencilDescriptor.depthCompareFunction := MTLCompareFunction.LessEqual;
+ depthStencilDescriptor.depthWriteEnabled := true;
+ _depthStencilState := _device.newDepthStencilStateWithDescriptor(depthStencilDescriptor);
+ if (_depthStencilState = nil) then
+ begin
+
+ NSLog("Failed to created _depthStencilState ");
+ exit nil;
+ end;
+
+
+ _pipelineState := _device.newRenderPipelineStateWithDescriptor(pipelineStateDescriptor) error(var lError);
+
+ if (_pipelineState = nil) then
+ begin
+ // Pipeline State creation could fail if we haven't properly set up our pipeline descriptor.
+ // If the Metal API validation is enabled, we can find out more information about what
+ // went wrong. (Metal API validation is enabled by default when a debug build is run
+ // from Xcode)
+ NSLog("Failed to created pipeline state, error %@", lError);
+ exit nil;
+ end;
+ CreateCursorPipeline(mtkView);
+
+ createBox(_device);
+
+ end;
+end;
+
+method MetalExample5.CreateCursorPipeline(const mtkView: not nullable MTKView);
+
+const
+ cShaderNameCur = 'AAPLShaders1.metallib';
+ cVertexFuncNameCur = 'vertexShader';
+ cFragmentFuncNameCur = 'fragmentColorShader';
+begin
+ var ShaderLoader := new shaderLoader(_device) Shadername(cShaderNameCur) Vertexname(cVertexFuncNameCur) Fragmentname(cFragmentFuncNameCur);
+ if ShaderLoader = nil then exit
+
+ else
+ begin
+ var lError : Error;
+ var pipelineStateDescriptor : MTLRenderPipelineDescriptor := new MTLRenderPipelineDescriptor();
+ pipelineStateDescriptor.label := "Cursor Pipeline";
+ pipelineStateDescriptor.vertexFunction := ShaderLoader.VertexFunc;
+ pipelineStateDescriptor.fragmentFunction := ShaderLoader.FragmentFunc;
+ pipelineStateDescriptor.colorAttachments[0].pixelFormat := mtkView.colorPixelFormat;
+
+ _CursorPipelineState := _device.newRenderPipelineStateWithDescriptor(pipelineStateDescriptor) error(var lError);
+
+ if (_CursorPipelineState = nil) then
+ NSLog("Failed to created pipeline state, error %@", lError);
+
+
+ end;
+end;
+
+
+method MetalExample5.createBox(_device: MTLDevice);
+begin
+ _vertexBuffer := MetalHelper.createBox(_device);
+ // _numVertices := _vertexBuffer.Count;//INDICES.length;
+end;
+
+method MetalExample5.UpdateViewAndProjection(const width, Height : Single);
+var Projection, View : TMatrix4;
+begin
+ var rot : Single := 310;
+ const V : Single = 1.5;
+
+
+ var lAspect : Single := (Height / width);
+{ORTHOGNAL VIEW}
+
+ Projection.InitOrthoOffCenterLH(-V, V*lAspect, V, -V*lAspect, V, -V);
+ rot := FmodelRotation;
+ //View.InitRotationYawPitchRoll(MetalMath.Radians(-rot), MetalMath.Radians(rot), MetalMath.Radians(-rot));
+ View.InitRotationYawPitchRoll(MetalMath.Radians(-rot), MetalMath.Radians(-rot), MetalMath.Radians(-rot));
+ _uniform.modelMatrix := View;
+ _uniform.projectionMatrix := Projection;
+ _uniform.normalMatrix.Init;
+ //_uniform.normalMatrix := _uniform.normalMatrix.Inverse.Transpose;
+
+ { Pass matrices to shader }
+end;
+
+//MARK:- Textures
+method MetalExample5.zBufferTexture(const x,y : integer): MTLTexture;
+begin
+ var zbufferTextureDescriptor := MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(MTLPixelFormat.Depth32Float) width(x) height(y) mipmapped(false);
+//(pixelFormat: MTLPixelFormat.depth32Float, width: x, height: y, mipmapped: false)
+ zbufferTextureDescriptor.usage := [MTLTextureUsage.RenderTarget, MTLTextureUsage.ShaderRead];
+// [.renderTarget, .shaderRead]
+ zbufferTextureDescriptor.storageMode := MTLStorageMode.Private;
+ exit _device.newTextureWithDescriptor(zbufferTextureDescriptor);
+//return HgRenderer.device.makeTexture(descriptor: zbufferTextureDescriptor)!
+//}()
+end;
+
+
+method MetalExample5.gBufferRenderPassDescriptor(const mtkView: not nullable MTKView): MTLRenderPassDescriptor;
+begin
+ var desc := mtkView.currentRenderPassDescriptor;
+ // new MTLRenderPassDescriptor();
+ var color := desc.colorAttachments[0];
+ color:clearColor := MTLClearColorMake(0.0,0.0,0.0,1);
+
+ color:loadAction := MTLLoadAction.Clear;
+ color:storeAction := MTLStoreAction.Store;
+ var depth := desc.depthAttachment;
+ if depth <> nil then
+ begin
+ depth.loadAction := MTLLoadAction.Clear;
+ depth.storeAction := MTLStoreAction.Store;
+ // depth.texture := zBufferTexture(_viewportSize[0], _viewportSize[1]);
+ depth.clearDepth := 1.0;
+ end;
+
+ exit desc;
+end;
+
+method MetalExample5.MouseMove(const amx: Single; const amy: Single);
+begin
+ self.mx := -(_viewportSize[0]*0.5) + amx;
+ self.my := -(_viewportSize[1]*0.5) +amy;
+end;
+
+
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExampleTypes.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExampleTypes.pas
index cd0ce11..ddd0b14 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExampleTypes.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalExampleTypes.pas
@@ -12,13 +12,16 @@ interface
vector_float2 = Array[0..1] of Single;
vector_float4 = Array[0..3] of Single;
+ //vector_float2 = TVector2;
+ //vector_float4 = TVector4;
+
type
// This structure defines the layout of each vertex in the array of vertices set as an input to our
// Metal vertex shader. Since this header is shared between our .metal shader and C code,
// we can be sure that the layout of the vertex array in our C code matches the layout that
// our .metal vertex shader expects
-// At moment we need a dummy record inside because the shader is using vectortypes with a alignment of 16
+
// Used in Example1
AAPLVertex1 = record
// Positions in pixel space
@@ -26,8 +29,6 @@ AAPLVertex1 = record
{$HIDE H7}
position : vector_float2;
- // Is needed for 16 byte alignement used in Metal
- dummy : vector_float2;
// Floating-point RGBA colors
color : Color;//vector_float4;
@@ -53,6 +54,17 @@ type Color = record
end;
+Vertex3d = record
+ position : array[0..2] of Single;
+ normal : array[0..2] of Single;
+ color : Color;
+ tex : array[0..1] of Single;
+ method toBuffer : array of Single;
+ class method fromBuffer(const buffer : Array of Single) : Vertex3d;
+end;
+
+
+
implementation
class method Color.create(const r: single; const g: single; const b: single; const a: single): Color;
begin
@@ -86,4 +98,21 @@ implementation
result.alpha := 1;
end;
+method Vertex3d.toBuffer: array of Single;
+begin
+ result := [position[0], position[1], position[2], normal[0], normal[1], normal[2], tex[0], tex[1]];
+end;
+
+class method Vertex3d.fromBuffer(const buffer: array of Single) : Vertex3d;
+begin
+ result := new Vertex3d();
+ result.position[0] := buffer[0];
+ result.position[1] := buffer[1];
+ result.position[2] := buffer[2];
+ result.normal[0] := buffer[3];
+ result.normal[1] := buffer[4];
+ result.normal[2] := buffer[5];
+ result.color := Color.create(1,0,0,1);
+end;
+
end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalGlobals.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalGlobals.pas
new file mode 100644
index 0000000..7bd6a0a
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalGlobals.pas
@@ -0,0 +1,96 @@
+namespace MetalExample;
+
+uses
+ rtl,
+ RemObjects.Elements.RTL;
+
+ const
+ { Default tolerance for comparing small floating-point values. }
+ SINGLE_TOLERANCE = 0.000001;
+ EPSILON: Single = 1E-40;
+
+
+type MetalMath = class
+public
+ class method Sqrt(const A: Single): Single;
+ begin
+ Result := Math.Sqrt(A);
+ end;
+
+ class method InverseSqrt(const A: Single): Single;
+ begin
+ Result := 1 / Sqrt(A)
+ end;
+
+ class method ArcTan2(const Y, X: Single): Single;
+ begin
+ Result := Math.Atan2(Y, X);
+ end;
+
+ class method Abs(const A: Single): Single;
+ begin
+ Result := Math.Abs(A);
+ end;
+
+ class method Mix(const A, B, T: Single): Single;
+ begin
+ Result := A + (T * (B - A)); // Faster
+ end;
+
+ class method Mix(const A, B: TVector2; const T: Single): TVector2;
+ begin
+ Result.Init(Mix(A.X, B.X, T), Mix(A.Y, B.Y, T));
+ end;
+
+ class method Mix(const A, B: TVector3; const T: Single): TVector3;
+ begin
+ Result.Init(Mix(A.X, B.X, T), Mix(A.Y, B.Y, T), Mix(A.Z, B.Z, T));
+ end;
+
+ class method Mix(const A, B, T: TVector3): TVector3;
+ begin
+ Result.Init(Mix(A.X, B.X, T.X), Mix(A.Y, B.Y, T.Y), Mix(A.Z, B.Z, T.Z));
+ end;
+
+ class method Mix(const A, B: TVector4; const T: Single): TVector4;
+ begin
+ Result.Init(Mix(A.X, B.X, T), Mix(A.Y, B.Y, T), Mix(A.Z, B.Z, T), Mix(A.W, B.W, T));
+ end;
+
+ class method Mix(const A, B, T: TVector4): TVector4;
+ begin
+ Result.Init(Mix(A.X, B.X, T.X), Mix(A.Y, B.Y, T.Y), Mix(A.Z, B.Z, T.Z), Mix(A.W, B.W, T.W));
+ end;
+
+
+ class method SinCos(const ARadians: Single; out ASin, ACos: Single);
+ begin
+ ASin := Math.Sin(ARadians);
+ ACos := Math.Cos(ARadians);
+
+ end;
+
+ class method SinCos(const ARadians: TVector4; out ASin, ACos: TVector4);
+ begin
+ SinCos(ARadians.X, out ASin.X, out ACos.X);
+ SinCos(ARadians.Y, out ASin.Y, out ACos.Y);
+ SinCos(ARadians.Z, out ASin.Z, out ACos.Z);
+ SinCos(ARadians.W, out ASin.W, out ACos.W);
+ end;
+
+ class method Radians(const ADegrees: Single): Single;
+ begin
+ Result := ADegrees * (Consts.PI / 180);
+ end;
+
+ class method EnsureRange(const A, AMin, AMax: Single): Single;
+ begin
+ Result := A;
+
+ if Result < AMin then
+ Result := AMin;
+ if Result > AMax then
+ Result := AMax;
+ end;
+end;
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalMatrix3.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalMatrix3.pas
new file mode 100644
index 0000000..170dc59
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalMatrix3.pas
@@ -0,0 +1,581 @@
+
+namespace MetalExample;
+
+interface
+
+type
+ { A 3x3 matrix in row-major order (M[Row, Column]).
+ You can access the elements directly using M[0,0]..M[2,2] or m11..m33.
+ You can also access the matrix using its three rows R[0]..R[2] (which map
+ directly to the elements M[]).
+
+}
+ TMatrix3 = record
+
+ private
+
+ method GetComponent(const ARow, AColumn: Integer): Single; inline;
+ method SetComponent(const ARow, AColumn: Integer; const Value: Single); inline;
+ method GetRow(const AIndex: Integer): TVector3;
+ method SetRow(const AIndex: Integer; const Value: TVector3);
+
+ method GetDeterminant: Single;
+
+ public
+ { Initializes the matrix to an identity matrix (filled with 0 and value 1
+ for the diagonal) }
+ {$HIDE W8}
+ method Init;
+ {$SHOW W8}
+ { Fills the matrix with zeros and sets the diagonal.
+
+ Parameters:
+ ADiagonal: the value to use for the diagonal. Use 1 to set the matrix
+ to an identity matrix. }
+ method Init(const ADiagonal: Single);
+
+
+ { Initializes the matrix using three row vectors.
+
+ Parameters:
+ ARow0: the first row of the matrix.
+ ARow1: the second row of the matrix.
+ ARow2: the third row of the matrix. }
+ method Init(const ARow0, ARow1, ARow2: TVector3);
+
+{ Initializes the matrix using a Tmatrix4.
+
+ Parameters:
+ ARow0: the Matrix.
+ ARow1: the second row of the matrix.
+ ARow2: the third row of the matrix. }
+ method Init(const AMatrix : TMatrix4);
+
+ { Initializes the matrix with explicit values.
+
+ Parameters:
+ A11-A33: the values of the matrix elements, in row-major order. }
+ method Init(const A11, A12, A13, A21, A22, A23, A31, A32, A33: Single);
+
+
+ { Creates a scaling matrix that scales uniformly.
+
+ Parameters:
+ AScale: the uniform scale factor }
+ method InitScaling(const AScale: Single);
+
+ { Creates a scaling matrix.
+
+ Parameters:
+ AScaleX: the value to scale by on the X axis
+ AScaleY: the value to scale by on the Y axis }
+ method InitScaling(const AScaleX, AScaleY: Single);
+
+ { Creates a scaling matrix.
+
+ Parameters:
+ AScale: the scale factors }
+ method InitScaling(const AScale: TVector2);
+
+ { Creates a translation matrix.
+
+ Parameters:
+ ADeltaX: translation in the X direction
+ ADeltaY: translation in the Y direction }
+ method InitTranslation(const ADeltaX, ADeltaY: Single);
+
+ { Creates a translation matrix.
+
+ Parameters:
+ ADelta: translation vector }
+ method InitTranslation(const ADelta: TVector2);
+
+ { Creates a rotation the matrix using a rotation angle in radians.
+
+ Parameters:
+ AAngle: the rotation angle in radians }
+ method InitRotation(const AAngle: Single);
+
+
+ { Checks two matrices for equality.
+
+ Returns:
+ True if the two matrices match each other exactly. }
+ class operator Equal(const A, B: TMatrix3): Boolean; inline;
+
+ { Checks two matrices for inequality.
+
+ Returns:
+ True if the two matrices are not equal. }
+ class operator NotEqual(const A, B: TMatrix3): Boolean; inline;
+
+ { Negates a matrix.
+
+ Returns:
+ The negative value of the matrix (with all elements negated). }
+ class operator Minus(const A: TMatrix3): TMatrix3; inline;
+
+ { Adds a scalar value to each element of a matrix. }
+ class operator Add(const A: TMatrix3; const B: Single): TMatrix3; inline;
+
+ { Adds a scalar value to each element of a matrix. }
+ class operator Add(const A: Single; const B: TMatrix3): TMatrix3; inline;
+
+ { Adds two matrices component-wise. }
+ class operator Add(const A, B: TMatrix3): TMatrix3; inline;
+
+ { Subtracts a scalar value from each element of a matrix. }
+ class operator Subtract(const A: TMatrix3; const B: Single): TMatrix3; inline;
+
+ { Subtracts a matrix from a scalar value. }
+ class operator Subtract(const A: Single; const B: TMatrix3): TMatrix3; inline;
+
+ { Subtracts two matrices component-wise. }
+ class operator Subtract(const A, B: TMatrix3): TMatrix3; inline;
+
+ { Multiplies a matrix with a scalar value. }
+ class operator Multiply(const A: TMatrix3; const B: Single): TMatrix3; inline;
+
+ { Multiplies a matrix with a scalar value. }
+ class operator Multiply(const A: Single; const B: TMatrix3): TMatrix3; inline;
+
+ { Performs a matrix * row vector linear algebraic multiplication. }
+ class operator Multiply(const A: TMatrix3; const B: TVector3): TVector3; inline;
+
+ { Performs a column vector * matrix linear algebraic multiplication. }
+ class operator Multiply(const A: TVector3; const B: TMatrix3): TVector3; inline;
+
+ { Multiplies two matrices using linear algebraic multiplication. }
+ class operator Multiply(const A, B: TMatrix3): TMatrix3; inline;
+
+ { Divides a matrix by a scalar value. }
+ class operator Divide(const A: TMatrix3; const B: Single): TMatrix3; inline;
+
+ { Divides a scalar value by a matrix. }
+ class operator Divide(const A: Single; const B: TMatrix3): TMatrix3; inline;
+
+ { Divides a matrix by a vector. This is equivalent to multiplying the
+ inverse of the matrix with a row vector using linear algebraic
+ multiplication. }
+ class operator Divide(const A: TMatrix3; const B: TVector3): TVector3; inline;
+
+ { Divides a vector by a matrix. This is equivalent to multiplying a column
+ vector with the inverse of the matrix using linear algebraic
+ multiplication. }
+ class operator Divide(const A: TVector3; const B: TMatrix3): TVector3; inline;
+
+ { Divides two matrices. This is equivalent to multiplying the first matrix
+ with the inverse of the second matrix using linear algebraic
+ multiplication. }
+ class operator Divide(const A, B: TMatrix3): TMatrix3; inline;
+
+ { Multiplies this matrix with another matrix component-wise.
+
+ Parameters:
+ AOther: the other matrix.
+
+ Returns:
+ This matrix multiplied by AOther component-wise.
+ That is, Result.M[I,J] := M[I,J] * AOther.M[I,J].
+
+ @bold(Note): For linear algebraic matrix multiplication, use the multiply
+ (*) operator instead. }
+ method CompMult(const AOther: TMatrix3): TMatrix3; inline;
+
+ { Creates a transposed version of this matrix.
+
+ Returns:
+ The transposed version of this matrix.
+
+ @bold(Note): Does not change this matrix. To update this itself, use
+ SetTransposed. }
+ method Transpose: TMatrix3; inline;
+
+ { Transposes this matrix.
+
+ @bold(Note): If you do not want to change this matrix, but get a
+ transposed version instead, then use Transpose. }
+ method SetTransposed;
+
+ { Calculates the inverse of this matrix.
+
+ Returns:
+ The inverse of this matrix.
+
+ @bold(Note): Does not change this matrix. To update this itself, use
+ SetInversed.
+
+ @bold(Note): The values in the returned matrix are undefined if this
+ matrix is singular or poorly conditioned (nearly singular). }
+ method Inverse: TMatrix3; inline;
+
+ { Inverts this matrix.
+
+ @bold(Note): If you do not want to change this matrix, but get an
+ inversed version instead, then use Inverse.
+
+ @bold(Note): The values in the inversed matrix are undefined if this
+ matrix is singular or poorly conditioned (nearly singular). }
+ method SetInversed;
+
+ { Returns the rows of the matrix. This is identical to accessing the
+ V-field.
+
+ Parameters:
+ AIndex: index of the row to return (0-2). Range is checked with
+ an assertion. }
+ property Rows[const AIndex: Integer]: TVector3 read GetRow write SetRow;
+ property M[const ARow, AColumn: Integer]: Single read GetComponent write SetComponent; default;
+
+
+ { The determinant of this matrix. }
+ property Determinant: Single read GetDeterminant;
+ method getPglMatrix3f : ^Single;
+ public
+
+ { Row vectors}
+ V: array [0..2] of TVector3;
+
+
+ property m11 : Single read GetComponent(0,0);
+ property m12 : Single read GetComponent(0,1);
+ property m13 : Single read GetComponent(0,2);
+
+ property m21 : Single read GetComponent(1,0);
+ property m22 : Single read GetComponent(1,1);
+ property m23 : Single read GetComponent(1,2);
+
+ property m31 : Single read GetComponent(2,0);
+ property m32 : Single read GetComponent(2,1);
+ property m33 : Single read GetComponent(2,2);
+
+ end;
+
+implementation
+
+{ TMatrix3 }
+
+class operator TMatrix3.Add(const A: TMatrix3; const B: Single): TMatrix3;
+begin
+ Result.V[0] := A.V[0] + B;
+ Result.V[1] := A.V[1] + B;
+ Result.V[2] := A.V[2] + B;
+end;
+
+class operator TMatrix3.Add(const A: Single; const B: TMatrix3): TMatrix3;
+begin
+ Result.V[0] := A + B.V[0];
+ Result.V[1] := A + B.V[1];
+ Result.V[2] := A + B.V[2];
+end;
+
+class operator TMatrix3.Add(const A, B: TMatrix3): TMatrix3;
+begin
+ Result.V[0] := A.V[0] + B.V[0];
+ Result.V[1] := A.V[1] + B.V[1];
+ Result.V[2] := A.V[2] + B.V[2];
+end;
+
+method TMatrix3.CompMult(const AOther: TMatrix3): TMatrix3;
+var
+ I: Integer;
+begin
+ for I := 0 to 2 do
+ Result.V[I] := V[I] * AOther.V[I];
+end;
+
+class operator TMatrix3.Divide(const A: Single; const B: TMatrix3): TMatrix3;
+begin
+ Result.V[0] := A / B.V[0];
+ Result.V[1] := A / B.V[1];
+ Result.V[2] := A / B.V[2];
+end;
+
+class operator TMatrix3.Divide(const A: TMatrix3; const B: Single): TMatrix3;
+var
+ InvB: Single;
+begin
+ InvB := 1 / B;
+ Result.V[0] := A.V[0] * InvB;
+ Result.V[1] := A.V[1] * InvB;
+ Result.V[2] := A.V[2] * InvB;
+end;
+
+class operator TMatrix3.Multiply(const A: Single; const B: TMatrix3): TMatrix3;
+begin
+ Result.V[0] := A * B.V[0];
+ Result.V[1] := A * B.V[1];
+ Result.V[2] := A * B.V[2];
+end;
+
+class operator TMatrix3.Multiply(const A: TMatrix3; const B: Single): TMatrix3;
+begin
+ Result.V[0] := A.V[0] * B;
+ Result.V[1] := A.V[1] * B;
+ Result.V[2] := A.V[2] * B;
+end;
+
+class operator TMatrix3.Multiply(const A: TMatrix3; const B: TVector3): TVector3;
+begin
+ Result.X := (B.X * A.M[0,0]) + (B.Y * A.M[0,1]) + (B.Z * A.M[0,2]);
+ Result.Y := (B.X * A.M[1,0]) + (B.Y * A.M[1,1]) + (B.Z * A.M[1,2]);
+ Result.Z := (B.X * A.M[2,0]) + (B.Y * A.M[2,1]) + (B.Z * A.M[2,2]);
+end;
+
+class operator TMatrix3.Multiply(const A: TVector3; const B: TMatrix3): TVector3;
+begin
+ Result.X := (B.M[0,0] * A.X) + (B.M[1,0] * A.Y) + (B.M[2,0] * A.Z);
+ Result.Y := (B.M[0,1] * A.X) + (B.M[1,1] * A.Y) + (B.M[2,1] * A.Z);
+ Result.Z := (B.M[0,2] * A.X) + (B.M[1,2] * A.Y) + (B.M[2,2] * A.Z);
+end;
+
+class operator TMatrix3.Multiply(const A, B: TMatrix3): TMatrix3;
+var
+ A00, A01, A02, A10, A11, A12, A20, A21, A22: Single;
+ B00, B01, B02, B10, B11, B12, B20, B21, B22: Single;
+begin
+ A00 := A.M[0,0];
+ A01 := A.M[0,1];
+ A02 := A.M[0,2];
+ A10 := A.M[1,0];
+ A11 := A.M[1,1];
+ A12 := A.M[1,2];
+ A20 := A.M[2,0];
+ A21 := A.M[2,1];
+ A22 := A.M[2,2];
+
+ B00 := B.M[0,0];
+ B01 := B.M[0,1];
+ B02 := B.M[0,2];
+ B10 := B.M[1,0];
+ B11 := B.M[1,1];
+ B12 := B.M[1,2];
+ B20 := B.M[2,0];
+ B21 := B.M[2,1];
+ B22 := B.M[2,2];
+
+
+ Result.M[0,0] := (A00 * B00) + (A01 * B10) + (A02 * B20);
+ Result.M[0,1] := (A00 * B01) + (A01 * B11) + (A02 * B21);
+ Result.M[0,2] := (A00 * B02) + (A01 * B12) + (A02 * B22);
+ Result.M[1,0] := (A10 * B00) + (A11 * B10) + (A12 * B20);
+ Result.M[1,1] := (A10 * B01) + (A11 * B11) + (A12 * B21);
+ Result.M[1,2] := (A10 * B02) + (A11 * B12) + (A12 * B22);
+ Result.M[2,0] := (A20 * B00) + (A21 * B10) + (A22 * B20);
+ Result.M[2,1] := (A20 * B01) + (A21 * B11) + (A22 * B21);
+ Result.M[2,2] := (A20 * B02) + (A21 * B12) + (A22 * B22);
+
+end;
+
+class operator TMatrix3.Minus(const A: TMatrix3): TMatrix3;
+begin
+ Result.V[0] := -A.V[0];
+ Result.V[1] := -A.V[1];
+ Result.V[2] := -A.V[2];
+end;
+
+method TMatrix3.SetTransposed;
+begin
+ Self := Transpose;
+end;
+
+class operator TMatrix3.Subtract(const A: TMatrix3; const B: Single): TMatrix3;
+begin
+ Result.V[0] := A.V[0] - B;
+ Result.V[1] := A.V[1] - B;
+ Result.V[2] := A.V[2] - B;
+end;
+
+class operator TMatrix3.Subtract(const A, B: TMatrix3): TMatrix3;
+begin
+ Result.V[0] := A.V[0] - B.V[0];
+ Result.V[1] := A.V[1] - B.V[1];
+ Result.V[2] := A.V[2] - B.V[2];
+end;
+
+class operator TMatrix3.Subtract(const A: Single; const B: TMatrix3): TMatrix3;
+begin
+ Result.V[0] := A - B.V[0];
+ Result.V[1] := A - B.V[1];
+ Result.V[2] := A - B.V[2];
+end;
+
+method TMatrix3.Transpose: TMatrix3;
+begin
+ Result.M[0,0] := M[0,0];
+ Result.M[0,1] := M[1,0];
+ Result.M[0,2] := M[2,0];
+
+ Result.M[1,0] := M[0,1];
+ Result.M[1,1] := M[1,1];
+ Result.M[1,2] := M[2,1];
+
+ Result.M[2,0] := M[0,2];
+ Result.M[2,1] := M[1,2];
+ Result.M[2,2] := M[2,2];
+end;
+
+class operator TMatrix3.Divide(const A, B: TMatrix3): TMatrix3;
+begin
+ Result := A * B.Inverse;
+end;
+
+class operator TMatrix3.Divide(const A: TVector3; const B: TMatrix3): TVector3;
+begin
+ Result := A * B.Inverse;
+end;
+
+class operator TMatrix3.Divide(const A: TMatrix3; const B: TVector3): TVector3;
+begin
+ Result := A.Inverse * B;
+end;
+
+method TMatrix3.GetDeterminant: Single;
+begin
+ Result :=
+ + (M[0,0] * ((M[1,1] * M[2,2]) - (M[2,1] * M[1,2])))
+ - (M[0,1] * ((M[1,0] * M[2,2]) - (M[2,0] * M[1,2])))
+ + (M[0,2] * ((M[1,0] * M[2,1]) - (M[2,0] * M[1,1])));
+end;
+
+method TMatrix3.Inverse: TMatrix3;
+var
+ OneOverDeterminant: Single;
+begin
+ OneOverDeterminant := 1 / Determinant;
+ Result.M[0,0] := + ((M[1,1] * M[2,2]) - (M[2,1] * M[1,2])) * OneOverDeterminant;
+ Result.M[1,0] := - ((M[1,0] * M[2,2]) - (M[2,0] * M[1,2])) * OneOverDeterminant;
+ Result.M[2,0] := + ((M[1,0] * M[2,1]) - (M[2,0] * M[1,1])) * OneOverDeterminant;
+ Result.M[0,1] := - ((M[0,1] * M[2,2]) - (M[2,1] * M[0,2])) * OneOverDeterminant;
+ Result.M[1,1] := + ((M[0,0] * M[2,2]) - (M[2,0] * M[0,2])) * OneOverDeterminant;
+ Result.M[2,1] := - ((M[0,0] * M[2,1]) - (M[2,0] * M[0,1])) * OneOverDeterminant;
+ Result.M[0,2] := + ((M[0,1] * M[1,2]) - (M[1,1] * M[0,2])) * OneOverDeterminant;
+ Result.M[1,2] := - ((M[0,0] * M[1,2]) - (M[1,0] * M[0,2])) * OneOverDeterminant;
+ Result.M[2,2] := + ((M[0,0] * M[1,1]) - (M[1,0] * M[0,1])) * OneOverDeterminant;
+end;
+
+method TMatrix3.SetInversed;
+begin
+ Self := Inverse;
+end;
+
+class operator TMatrix3.Equal(const A, B: TMatrix3): Boolean;
+begin
+ Result := (A.V[0] = B.V[0]) and (A.V[1] = B.V[1]) and (A.V[2] = B.V[2]);
+end;
+
+method TMatrix3.Init(const ADiagonal: Single);
+begin
+ V[0].Init(ADiagonal, 0, 0);
+ V[1].Init(0, ADiagonal, 0);
+ V[2].Init(0, 0, ADiagonal);
+end;
+
+method TMatrix3.Init;
+begin
+ V[0].Init(1, 0, 0);
+ V[1].Init(0, 1, 0);
+ V[2].Init(0, 0, 1);
+end;
+
+method TMatrix3.Init(const AMatrix : TMatrix4);
+begin
+ V[0].Init(AMatrix.V[0].X, AMatrix.V[0].Y, AMatrix.V[0].Z);
+ V[1].Init(AMatrix.V[1].X, AMatrix.V[1].Y, AMatrix.V[1].Z);
+ V[2].Init(AMatrix.V[2].X, AMatrix.V[2].Y, AMatrix.V[2].Z);
+end;
+
+method TMatrix3.Init(const A11, A12, A13, A21, A22, A23, A31, A32,
+ A33: Single);
+begin
+ V[0].Init(A11, A12, A13);
+ V[1].Init(A21, A22, A23);
+ V[2].Init(A31, A32, A33);
+end;
+
+method TMatrix3.InitScaling(const AScale: Single);
+begin
+ V[0].Init(AScale, 0, 0);
+ V[1].Init(0, AScale, 0);
+ V[2].Init(0, 0, 1);
+end;
+
+method TMatrix3.InitScaling(const AScaleX, AScaleY: Single);
+begin
+ V[0].Init(AScaleX, 0, 0);
+ V[1].Init(0, AScaleY, 0);
+ V[2].Init(0, 0, 1);
+end;
+
+method TMatrix3.InitScaling(const AScale: TVector2);
+begin
+ V[0].Init(AScale.X, 0, 0);
+ V[1].Init(0, AScale.Y, 0);
+ V[2].Init(0, 0, 1);
+end;
+
+method TMatrix3.InitTranslation(const ADeltaX, ADeltaY: Single);
+begin
+ V[0].Init(1, 0, 0);
+ V[1].Init(0, 1, 0);
+ V[2].Init(ADeltaX, ADeltaY, 1);
+end;
+
+method TMatrix3.InitTranslation(const ADelta: TVector2);
+begin
+ V[0].Init(1, 0, 0);
+ V[1].Init(0, 1, 0);
+ V[2].Init(ADelta.X, ADelta.Y, 1);
+end;
+
+class operator TMatrix3.NotEqual(const A, B: TMatrix3): Boolean;
+begin
+ Result := (A.V[0] <> B.V[0]) or (A.V[1] <> B.V[1]) or (A.V[2] <> B.V[2]);
+end;
+
+method TMatrix3.GetComponent(const ARow, AColumn: Integer): Single;
+begin
+ Result := V[ARow][AColumn];
+end;
+
+method TMatrix3.GetRow(const AIndex: Integer): TVector3;
+begin
+ Result := V[AIndex];
+end;
+
+method TMatrix3.Init(const ARow0, ARow1, ARow2: TVector3);
+begin
+ V[0] := ARow0;
+ V[1] := ARow1;
+ V[2] := ARow2;
+end;
+
+method TMatrix3.InitRotation(const AAngle: Single);
+var
+ S, C: Single;
+begin
+ MetalMath.SinCos(AAngle, out S, out C);
+ V[0].Init(C, S, 0);
+ V[1].Init(-S, C, 0);
+ V[2].Init(0, 0, 1);
+end;
+
+method TMatrix3.SetComponent(const ARow, AColumn: Integer; const Value: Single);
+begin
+ V[ARow][ AColumn] := Value;
+end;
+
+method TMatrix3.SetRow(const AIndex: Integer; const Value: TVector3);
+begin
+ V[AIndex] := Value;
+end;
+
+
+
+
+method TMatrix3.getPglMatrix3f: ^Single;
+begin
+ exit @V[0].X;
+end;
+
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalMatrix4.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalMatrix4.pas
new file mode 100644
index 0000000..140c372
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalMatrix4.pas
@@ -0,0 +1,1085 @@
+namespace MetalExample;
+{$DEFINE USEINLINE}
+interface
+
+type
+ { A 4x4 matrix in row-major order (M[Row, Column]).
+ You can access the elements directly using M[0,0]..M[3,3] or m11..m44.
+
+ }
+
+
+ TMatrix4 = record
+ private
+
+ method GetComponent(const ARow, AColumn: Integer): Single; inline;
+ method SetComponent(const ARow, AColumn: Integer; const Value: Single); inline;
+ method GetRow(const AIndex: Integer): TVector4;
+ method SetRow(const AIndex: Integer; const Value: TVector4);
+
+ method GetDeterminant: Single;
+
+ public
+ { Initializes the matrix to an identity matrix (filled with 0 and value 1
+ for the diagonal) }
+ {$HIDE W8}
+ method Init;
+ {$SHOW W8}
+
+ { Fills the matrix with zeros and sets the diagonal.
+
+ Parameters:
+ ADiagonal: the value to use for the diagonal. Use 1 to set the matrix
+ to an identity matrix. }
+ method Init(const ADiagonal: Single);
+
+
+ { Initializes the matrix using four row vectors.
+
+ Parameters:
+ ARow0: the first row of the matrix.
+ ARow1: the second row of the matrix.
+ ARow2: the third row of the matrix.
+ ARow3: the fourth row of the matrix. }
+ method Init(const ARow0, ARow1, ARow2, ARow3: TVector4);
+
+
+ { Initializes the matrix with explicit values.
+
+ Parameters:
+ A11-A44: the values of the matrix elements, in row-major order. }
+ method Init(const A11, A12, A13, A14, A21, A22, A23, A24, A31, A32, A33,
+ A34, A41, A42, A43, A44: Single);
+
+
+ { Initializes the matrix with a 3x3 matrix. The 3x3 matrix is copied to the
+ top-left corner of the 4x4 matrix, and the remaining elements are set
+ according to an identity matrix.
+
+ Parameters:
+ AMatrix: the source 3x3 matrix. }
+ method Init(const AMatrix: TMatrix3);
+
+ { Creates a scaling matrix that scales uniformly.
+
+ Parameters:
+ AScale: the uniform scale factor }
+ method InitScaling(const AScale: Single);
+
+ { Creates a scaling matrix.
+
+ Parameters:
+ AScaleX: the value to scale by on the X axis
+ AScaleY: the value to scale by on the Y axis
+ AScaleZ: the value to scale by on the Z axis }
+ method InitScaling(const AScaleX, AScaleY, AScaleZ: Single);
+
+ { Creates a scaling matrix.
+
+ Parameters:
+ AScale: the scale factors }
+ method InitScaling(const AScale: TVector3);
+
+ { Creates a translation matrix.
+
+ Parameters:
+ ADeltaX: translation in the X direction
+ ADeltaY: translation in the Y direction
+ ADeltaZ: translation in the Z direction }
+ method InitTranslation(const ADeltaX, ADeltaY, ADeltaZ: Single);
+
+ { Creates a translation matrix.
+
+ Parameters:
+ ADelta: translation vector }
+ method InitTranslation(const ADelta: TVector3);
+
+ { Creates a matrix for rotating points around the X axis.
+
+ Parameters:
+ AAngle: the rotation angle around the X axis, in radians }
+ method InitRotationX(const AAngle: Single);
+
+ { Creates a matrix for rotating points around the Y axis.
+
+ Parameters:
+ AAngle: the rotation angle around the Y axis, in radians }
+ method InitRotationY(const AAngle: Single);
+
+ { Creates a matrix for rotating points around the Z axis.
+
+ Parameters:
+ AAngle: the rotation angle around the Z axis, in radians }
+ method InitRotationZ(const AAngle: Single);
+
+ { Creates a matrix for rotating points around a certain axis.
+
+ Parameters:
+ AAxis: the direction of the axis to rotate around.
+ AAngle: the rotation angle around AAxis, in radians }
+ method InitRotation(const AAxis: TVector3; const AAngle: Single);
+
+ { Creates a rotation matrix from a yaw, pitch and roll angle.
+
+ Parameters:
+ AYaw: the rotation angle around the Y axis, in radians
+ APitch: the rotation angle around the X axis, in radians
+ ARoll: the rotation angle around the Z axis, in radians }
+ method InitRotationYawPitchRoll(const AYaw, APitch, ARoll: Single);
+
+ { Creates a rotation matrix from a heading, pitch and bank angle.
+
+ Parameters:
+ AHeading: the heading angle, in radians
+ APitch: the pitch angle, in radians
+ ABank: the bank angle, in radians }
+ method InitRotationHeadingPitchBank(const AHeading, APitch, ABank: Single);
+
+ { Creates a left-handed view matrix looking at a certain target.
+
+ Parameters:
+ ACameraPosition: position of the camera (or eye).
+ ACameraTarget: the target towards which the camera is pointing.
+ ACameraUp: the direction that is "up" from the camera's point of view }
+ method InitLookAtLH(const ACameraPosition, ACameraTarget, ACameraUp: TVector3);
+
+ { Creates a right-handed view matrix looking at a certain target.
+
+ Parameters:
+ ACameraPosition: position of the camera (or eye).
+ ACameraTarget: the target towards which the camera is pointing.
+ ACameraUp: the direction that is "up" from the camera's point of view }
+ method InitLookAtRH(const ACameraPosition, ACameraTarget, ACameraUp: TVector3);
+
+ { Creates a left-handed view matrix looking into a certain direction.
+
+ Parameters:
+ ACameraPosition: position of the camera (or eye).
+ ACameraDirection: the direction the camera is pointing in.
+ ACameraUp: the direction that is "up" from the camera's point of view }
+ method InitLookAtDirLH(const ACameraPosition, ACameraDirection, ACameraUp: TVector3);
+
+ { Creates a right-handed view matrix looking into a certain direction.
+
+ Parameters:
+ ACameraPosition: position of the camera (or eye).
+ ACameraDirection: the direction the camera is pointing in.
+ ACameraUp: the direction that is "up" from the camera's point of view }
+ method InitLookAtDirRH(const ACameraPosition, ACameraDirection, ACameraUp: TVector3);
+
+ { Creates a left-handed orthographic projection matrix from the given view
+ volume dimensions.
+
+ Parameters:
+ AWidth: the width of the view volume.
+ AHeight: the height of the view volume.
+ AZNearPlane: the minimum Z-value of the view volume.
+ AZFarPlane: the maximum Z-value of the view volume. }
+ method InitOrthoLH(const AWidth, AHeight, AZNearPlane, AZFarPlane: Single);
+
+ { Creates a right-handed orthographic projection matrix from the given view
+ volume dimensions.
+
+ Parameters:
+ AWidth: the width of the view volume.
+ AHeight: the height of the view volume.
+ AZNearPlane: the minimum Z-value of the view volume.
+ AZFarPlane: the maximum Z-value of the view volume. }
+ method InitOrthoRH(const AWidth, AHeight, AZNearPlane, AZFarPlane: Single);
+
+ { Creates a customized left-handed orthographic projection matrix.
+
+ Parameters:
+ ALeft: the minimum X-value of the view volume.
+ ATop: the maximum Y-value of the view volume.
+ ARight: the maximum X-value of the view volume.
+ ABottom: the minimum Y-value of the view volume.
+ AZNearPlane: the minimum Z-value of the view volume.
+ AZFarPlane: the maximum Z-value of the view volume. }
+ method InitOrthoOffCenterLH(const ALeft, ATop, ARight, ABottom,
+ AZNearPlane, AZFarPlane: Single);
+
+ { Creates a customized right-handed orthographic projection matrix.
+
+ Parameters:
+ ALeft: the minimum X-value of the view volume.
+ ATop: the maximum Y-value of the view volume.
+ ARight: the maximum X-value of the view volume.
+ ABottom: the minimum Y-value of the view volume.
+ AZNearPlane: the minimum Z-value of the view volume.
+ AZFarPlane: the maximum Z-value of the view volume. }
+ method InitOrthoOffCenterRH(const ALeft, ATop, ARight, ABottom,
+ AZNearPlane, AZFarPlane: Single);
+
+ { Creates a left-handed perspective projection matrix based on a field of
+ view, aspect ratio, and near and far view plane distances.
+
+ Parameters:
+ AFieldOfView: the field of view in radians.
+ AAspectRatio: the aspect ratio, defined as view space width divided by
+ height.
+ ANearPlaneDistance:the distance to the near view plane.
+ AFarPlaneDistance:the distance to the far view plane.
+ AHorizontalFOV: (optional) boolean indicating the direction of the
+ field of view. If False (default), AFieldOfView is in the Y direction,
+ otherwise in the X direction }
+ method InitPerspectiveFovLH(const AFieldOfView, AAspectRatio,
+ ANearPlaneDistance, AFarPlaneDistance: Single;
+ const AHorizontalFOV: Boolean := False);
+
+ { Creates a right-handed perspective projection matrix based on a field of
+ view, aspect ratio, and near and far view plane distances.
+
+ Parameters:
+ AFieldOfView: the field of view in radians.
+ AAspectRatio: the aspect ratio, defined as view space width divided by
+ height.
+ ANearPlaneDistance:the distance to the near view plane.
+ AFarPlaneDistance:the distance to the far view plane.
+ AHorizontalFOV: (optional) boolean indicating the direction of the
+ field of view. If False (default), AFieldOfView is in the Y direction,
+ otherwise in the X direction }
+ method InitPerspectiveFovRH(const AFieldOfView, AAspectRatio,
+ ANearPlaneDistance, AFarPlaneDistance: Single;
+ const AHorizontalFOV: Boolean := False);
+
+
+
+ { Checks two matrices for equality.
+
+ Returns:
+ True if the two matrices match each other exactly. }
+ class operator Equal(const A, B: TMatrix4): Boolean; inline;
+
+ { Checks two matrices for inequality.
+
+ Returns:
+ True if the two matrices are not equal. }
+ class operator NotEqual(const A, B: TMatrix4): Boolean; inline;
+
+ { Negates a matrix.
+
+ Returns:
+ The negative value of the matrix (with all elements negated). }
+ class operator Minus(const A: TMatrix4): TMatrix4; inline;
+
+ { Adds a scalar value to each element of a matrix. }
+ class operator Add(const A: TMatrix4; const B: Single): TMatrix4; inline;
+
+ { Adds a scalar value to each element of a matrix. }
+ class operator Add(const A: Single; const B: TMatrix4): TMatrix4; inline;
+
+ { Adds two matrices component-wise. }
+ class operator Add(const A, B: TMatrix4): TMatrix4; inline;
+
+ { Subtracts a scalar value from each element of a matrix. }
+ class operator Subtract(const A: TMatrix4; const B: Single): TMatrix4; inline;
+
+ { Subtracts a matrix from a scalar value. }
+ class operator Subtract(const A: Single; const B: TMatrix4): TMatrix4; inline;
+
+ { Subtracts two matrices component-wise. }
+ class operator Subtract(const A, B: TMatrix4): TMatrix4; inline;
+
+ { Multiplies a matrix with a scalar value. }
+ class operator Multiply(const A: TMatrix4; const B: Single): TMatrix4; inline;
+
+ { Multiplies a matrix with a scalar value. }
+ class operator Multiply(const A: Single; const B: TMatrix4): TMatrix4; inline;
+
+ { Performs a matrix * row vector linear algebraic multiplication. }
+ class operator Multiply(const A: TMatrix4; const B: TVector4): TVector4; inline;
+ { Performs a column vector * matrix linear algebraic multiplication. }
+ class operator Multiply(const A: TVector4; const B: TMatrix4): TVector4; inline;
+
+ { Multiplies two matrices using linear algebraic multiplication. }
+ class operator Multiply(const A, B: TMatrix4): TMatrix4; inline;
+
+ { Divides a matrix by a scalar value. }
+ class operator Divide(const A: TMatrix4; const B: Single): TMatrix4; inline;
+
+ { Divides a scalar value by a matrix. }
+ class operator Divide(const A: Single; const B: TMatrix4): TMatrix4; inline;
+
+ { Divides a matrix by a vector. This is equivalent to multiplying the
+ inverse of the matrix with a row vector using linear algebraic
+ multiplication. }
+ class operator Divide(const A: TMatrix4; const B: TVector4): TVector4; inline;
+
+ { Divides a vector by a matrix. This is equivalent to multiplying a column
+ vector with the inverse of the matrix using linear algebraic
+ multiplication. }
+ class operator Divide(const A: TVector4; const B: TMatrix4): TVector4; inline;
+
+ { Divides two matrices. This is equivalent to multiplying the first matrix
+ with the inverse of the second matrix using linear algebraic
+ multiplication. }
+ class operator Divide(const A, B: TMatrix4): TMatrix4; inline;
+
+ { Multiplies this matrix with another matrix component-wise.
+
+ Parameters:
+ AOther: the other matrix.
+
+ Returns:
+ This matrix multiplied by AOther component-wise.
+ That is, Result.M[I,J] := M[I,J] * AOther.M[I,J].
+
+ @bold(Note): For linear algebraic matrix multiplication, use the multiply
+ (*) operator instead. }
+ method CompMult(const AOther: TMatrix4): TMatrix4; inline;
+
+ { Creates a transposed version of this matrix.
+
+ Returns:
+ The transposed version of this matrix.
+
+ @bold(Note): Does not change this matrix. To update this itself, use
+ SetTransposed. }
+ method Transpose: TMatrix4; inline;
+
+ { Transposes this matrix.
+
+ @bold(Note): If you do not want to change this matrix, but get a
+ transposed version instead, then use Transpose. }
+ method SetTransposed;
+
+ { Calculates the inverse of this matrix.
+
+ Returns:
+ The inverse of this matrix.
+
+ @bold(Note): Does not change this matrix. To update this itself, use
+ SetInversed.
+
+ @bold(Note): The values in the returned matrix are undefined if this
+ matrix is singular or poorly conditioned (nearly singular). }
+ method Inverse: TMatrix4; inline;
+
+ { Inverts this matrix.
+
+ @bold(Note): If you do not want to change this matrix, but get an
+ inversed version instead, then use Inverse.
+
+ @bold(Note): The values in the inversed matrix are undefined if this
+ matrix is singular or poorly conditioned (nearly singular). }
+ method SetInversed;
+ { Returns the rows of the matrix. This is identical to accessing the
+ R-field.
+
+ Parameters:
+ AIndex: index of the row to return (0-3). Range is checked with
+ an assertion. }
+ property Rows[const AIndex: Integer]: TVector4 read GetRow write SetRow;
+
+
+
+ { The determinant of this matrix. }
+ property Determinant: Single read GetDeterminant;
+
+ method getPglMatrix4f : ^Single;
+ public
+ {The Rows of the Matrix}
+ V: array [0..3] of TVector4;
+ { Returns the elements of the matrix (in row-major order).
+ This is identical to accessing the M-field, but this property can be used
+ as a default array property.
+
+ Parameters:
+ ARow: the row index (0-3). Range is checked with an assertion.
+ AColumn: the column index (0-3). Range is checked with requires. }
+ property M[const ARow, AColumn: Integer]: Single read GetComponent write SetComponent; default;
+
+
+
+ property m11 : Single read GetComponent(0,0);
+ property m12 : Single read GetComponent(0,1);
+ property m13 : Single read GetComponent(0,2);
+ property m14 : Single read GetComponent(0,3);
+
+ property m21 : Single read GetComponent(1,0);
+ property m22 : Single read GetComponent(1,1);
+ property m23 : Single read GetComponent(1,2);
+ property m24 : Single read GetComponent(1,3);
+
+ property m31 : Single read GetComponent(2,0);
+ property m32 : Single read GetComponent(2,1);
+ property m33 : Single read GetComponent(2,2);
+ property m34 : Single read GetComponent(2,3);
+
+ property m41 : Single read GetComponent(3,0);
+ property m42 : Single read GetComponent(3,1);
+ property m43 : Single read GetComponent(3,2);
+ property m44 : Single read GetComponent(3,3);
+
+ //m11,
+ // m12, m13, m14: Single;
+ // m21, m22, m23, m24: Single;
+ // m31, m32, m33, m34: Single;
+ // m41, m42, m43, m44: Single;
+ end;
+
+implementation
+
+{ TMatrix 4 }
+
+class operator TMatrix4.Add(const A: TMatrix4; const B: Single): TMatrix4;
+begin
+ Result.V[0] := A.V[0] + B;
+ Result.V[1] := A.V[1] + B;
+ Result.V[2] := A.V[2] + B;
+ Result.V[3] := A.V[3] + B;
+end;
+
+class operator TMatrix4.Add(const A: Single; const B: TMatrix4): TMatrix4;
+begin
+ Result.V[0] := A + B.V[0];
+ Result.V[1] := A + B.V[1];
+ Result.V[2] := A + B.V[2];
+ Result.V[3] := A + B.V[3];
+end;
+
+class operator TMatrix4.Add(const A, B: TMatrix4): TMatrix4;
+begin
+ Result.V[0] := A.V[0] + B.V[0];
+ Result.V[1] := A.V[1] + B.V[1];
+ Result.V[2] := A.V[2] + B.V[2];
+ Result.V[3] := A.V[3] + B.V[3];
+end;
+
+method TMatrix4.CompMult(const AOther: TMatrix4): TMatrix4;
+var
+I: Integer;
+begin
+ for I := 0 to 3 do
+ Result.V[I] := V[I] * AOther.V[I];
+end;
+
+class operator TMatrix4.Divide(const A: Single; const B: TMatrix4): TMatrix4;
+begin
+ Result.V[0] := A / B.V[0];
+ Result.V[1] := A / B.V[1];
+ Result.V[2] := A / B.V[2];
+ Result.V[3] := A / B.V[3];
+end;
+
+class operator TMatrix4.Divide(const A: TMatrix4; const B: Single): TMatrix4;
+var
+InvB: Single;
+begin
+ InvB := 1 / B;
+ Result.V[0] := A.V[0] * InvB;
+ Result.V[1] := A.V[1] * InvB;
+ Result.V[2] := A.V[2] * InvB;
+ Result.V[3] := A.V[3] * InvB;
+end;
+
+method TMatrix4.Inverse: TMatrix4;
+var
+C00, C02, C03, C04, C06, C07, C08, C10, C11: Single;
+C12, C14, C15, C16, C18, C19, C20, C22, C23: Single;
+F0, F1, F2, F3, F4, F5, V0, V1, V2, V3, I0, I1, I2, I3, SA, SB, Row, Dot: TVector4;
+Inv: TMatrix4;
+OneOverDeterminant: Single;
+begin
+ C00 := (M[2,2] * M[3,3]) - (M[3,2] * M[2,3]);
+ C02 := (M[1,2] * M[3,3]) - (M[3,2] * M[1,3]);
+ C03 := (M[1,2] * M[2,3]) - (M[2,2] * M[1,3]);
+
+ C04 := (M[2,1] * M[3,3]) - (M[3,1] * M[2,3]);
+ C06 := (M[1,1] * M[3,3]) - (M[3,1] * M[1,3]);
+ C07 := (M[1,1] * M[2,3]) - (M[2,1] * M[1,3]);
+
+ C08 := (M[2,1] * M[3,2]) - (M[3,1] * M[2,2]);
+ C10 := (M[1,1] * M[3,2]) - (M[3,1] * M[1,2]);
+ C11 := (M[1,1] * M[2,2]) - (M[2,1] * M[1,2]);
+
+ C12 := (M[2,0] * M[3,3]) - (M[3,0] * M[2,3]);
+ C14 := (M[1,0] * M[3,3]) - (M[3,0] * M[1,3]);
+ C15 := (M[1,0] * M[2,3]) - (M[2,0] * M[1,3]);
+
+ C16 := (M[2,0] * M[3,2]) - (M[3,0] * M[2,2]);
+ C18 := (M[1,0] * M[3,2]) - (M[3,0] * M[1,2]);
+ C19 := (M[1,0] * M[2,2]) - (M[2,0] * M[1,2]);
+
+ C20 := (M[2,0] * M[3,1]) - (M[3,0] * M[2,1]);
+ C22 := (M[1,0] * M[3,1]) - (M[3,0] * M[1,1]);
+ C23 := (M[1,0] * M[2,1]) - (M[2,0] * M[1,1]);
+
+ F0.Init(C00, C00, C02, C03);
+ F1.Init(C04, C04, C06, C07);
+ F2.Init(C08, C08, C10, C11);
+ F3.Init(C12, C12, C14, C15);
+ F4.Init(C16, C16, C18, C19);
+ F5.Init(C20, C20, C22, C23);
+
+ V0.Init(M[1,0], M[0,0], M[0,0], M[0,0]);
+ V1.Init(M[1,1], M[0,1], M[0,1], M[0,1]);
+ V2.Init(M[1,2], M[0,2], M[0,2], M[0,2]);
+ V3.Init(M[1,3], M[0,3], M[0,3], M[0,3]);
+
+ I0 := (V1 * F0) - (V2 * F1) + (V3 * F2);
+ I1 := (V0 * F0) - (V2 * F3) + (V3 * F4);
+ I2 := (V0 * F1) - (V1 * F3) + (V3 * F5);
+ I3 := (V0 * F2) - (V1 * F4) + (V2 * F5);
+
+ SA.Init(+1, -1, +1, -1);
+ SB.Init(-1, +1, -1, +1);
+
+ Inv.Init(I0 * SA, I1 * SB, I2 * SA, I3 * SB);
+
+ Row.Init(Inv[0,0], Inv[1,0], Inv[2,0], Inv[3,0]);
+ Dot := V[0] * Row;
+
+ OneOverDeterminant := 1 / ((Dot.X + Dot.Y) + (Dot.Z + Dot.W));
+ Result := Inv * OneOverDeterminant;
+end;
+
+class operator TMatrix4.Multiply(const A: Single; const B: TMatrix4): TMatrix4;
+begin
+ Result.V[0] := A * B.V[0];
+ Result.V[1] := A * B.V[1];
+ Result.V[2] := A * B.V[2];
+ Result.V[3] := A * B.V[3];
+end;
+
+class operator TMatrix4.Multiply(const A: TMatrix4; const B: Single): TMatrix4;
+begin
+ Result.V[0] := A.V[0] * B;
+ Result.V[1] := A.V[1] * B;
+ Result.V[2] := A.V[2] * B;
+ Result.V[3] := A.V[3] * B;
+end;
+
+class operator TMatrix4.Multiply(const A: TVector4; const B: TMatrix4): TVector4;
+begin
+ Result.X := (B.M[0,0] * A.X) + (B.M[1,0] * A.Y) + (B.M[2,0] * A.Z) + (B.M[3,0] * A.W);
+ Result.Y := (B.M[0,1] * A.X) + (B.M[1,1] * A.Y) + (B.M[2,1] * A.Z) + (B.M[3,1] * A.W);
+ Result.Z := (B.M[0,2] * A.X) + (B.M[1,2] * A.Y) + (B.M[2,2] * A.Z) + (B.M[3,2] * A.W);
+ Result.W := (B.M[0,3] * A.X) + (B.M[1,3] * A.Y) + (B.M[2,3] * A.Z) + (B.M[3,3] * A.W);
+end;
+
+class operator TMatrix4.Multiply(const A: TMatrix4; const B: TVector4): TVector4;
+begin
+ Result.X := (B.X * A.M[0,0]) + (B.Y * A.M[0,1]) + (B.Z * A.M[0,2]) + (B.W * A.M[0,3]);
+ Result.Y := (B.X * A.M[1,0]) + (B.Y * A.M[1,1]) + (B.Z * A.M[1,2]) + (B.W * A.M[1,3]);
+ Result.Z := (B.X * A.M[2,0]) + (B.Y * A.M[2,1]) + (B.Z * A.M[2,2]) + (B.W * A.M[2,3]);
+ Result.W := (B.X * A.M[3,0]) + (B.Y * A.M[3,1]) + (B.Z * A.M[3,2]) + (B.W * A.M[3,3]);
+end;
+
+class operator TMatrix4.Multiply(const A, B: TMatrix4): TMatrix4;
+begin
+ Result.M[0,0] := (A.M[0,0] * B.M[0,0]) + (A.M[0,1] * B.M[1,0]) + (A.M[0,2] * B.M[2,0]) + (A.M[0,3] * B.M[3,0]);
+ Result.M[0,1] := (A.M[0,0] * B.M[0,1]) + (A.M[0,1] * B.M[1,1]) + (A.M[0,2] * B.M[2,1]) + (A.M[0,3] * B.M[3,1]);
+ Result.M[0,2] := (A.M[0,0] * B.M[0,2]) + (A.M[0,1] * B.M[1,2]) + (A.M[0,2] * B.M[2,2]) + (A.M[0,3] * B.M[3,2]);
+ Result.M[0,3] := (A.M[0,0] * B.M[0,3]) + (A.M[0,1] * B.M[1,3]) + (A.M[0,2] * B.M[2,3]) + (A.M[0,3] * B.M[3,3]);
+
+ Result.M[1,0] := (A.M[1,0] * B.M[0,0]) + (A.M[1,1] * B.M[1,0]) + (A.M[1,2] * B.M[2,0]) + (A.M[1,3] * B.M[3,0]);
+ Result.M[1,1] := (A.M[1,0] * B.M[0,1]) + (A.M[1,1] * B.M[1,1]) + (A.M[1,2] * B.M[2,1]) + (A.M[1,3] * B.M[3,1]);
+ Result.M[1,2] := (A.M[1,0] * B.M[0,2]) + (A.M[1,1] * B.M[1,2]) + (A.M[1,2] * B.M[2,2]) + (A.M[1,3] * B.M[3,2]);
+ Result.M[1,3] := (A.M[1,0] * B.M[0,3]) + (A.M[1,1] * B.M[1,3]) + (A.M[1,2] * B.M[2,3]) + (A.M[1,3] * B.M[3,3]);
+
+ Result.M[2,0] := (A.M[2,0] * B.M[0,0]) + (A.M[2,1] * B.M[1,0]) + (A.M[2,2] * B.M[2,0]) + (A.M[2,3] * B.M[3,0]);
+ Result.M[2,1] := (A.M[2,0] * B.M[0,1]) + (A.M[2,1] * B.M[1,1]) + (A.M[2,2] * B.M[2,1]) + (A.M[2,3] * B.M[3,1]);
+ Result.M[2,2] := (A.M[2,0] * B.M[0,2]) + (A.M[2,1] * B.M[1,2]) + (A.M[2,2] * B.M[2,2]) + (A.M[2,3] * B.M[3,2]);
+ Result.M[2,3] := (A.M[2,0] * B.M[0,3]) + (A.M[2,1] * B.M[1,3]) + (A.M[2,2] * B.M[2,3]) + (A.M[2,3] * B.M[3,3]);
+
+ Result.M[3,0] := (A.M[3,0] * B.M[0,0]) + (A.M[3,1] * B.M[1,0]) + (A.M[3,2] * B.M[2,0]) + (A.M[3,3] * B.M[3,0]);
+ Result.M[3,1] := (A.M[3,0] * B.M[0,1]) + (A.M[3,1] * B.M[1,1]) + (A.M[3,2] * B.M[2,1]) + (A.M[3,3] * B.M[3,1]);
+ Result.M[3,2] := (A.M[3,0] * B.M[0,2]) + (A.M[3,1] * B.M[1,2]) + (A.M[3,2] * B.M[2,2]) + (A.M[3,3] * B.M[3,2]);
+ Result.M[3,3] := (A.M[3,0] * B.M[0,3]) + (A.M[3,1] * B.M[1,3]) + (A.M[3,2] * B.M[2,3]) + (A.M[3,3] * B.M[3,3]);
+end;
+
+class operator TMatrix4.Minus(const A: TMatrix4): TMatrix4;
+begin
+ Result.V[0] := -A.V[0];
+ Result.V[1] := -A.V[1];
+ Result.V[2] := -A.V[2];
+ Result.V[3] := -A.V[3];
+end;
+
+method TMatrix4.SetInversed;
+begin
+ Self := Inverse;
+end;
+
+method TMatrix4.SetTransposed;
+begin
+ Self := Transpose;
+end;
+
+class operator TMatrix4.Subtract(const A: TMatrix4; const B: Single): TMatrix4;
+begin
+ Result.V[0] := A.V[0] - B;
+ Result.V[1] := A.V[1] - B;
+ Result.V[2] := A.V[2] - B;
+ Result.V[3] := A.V[3] - B;
+end;
+
+class operator TMatrix4.Subtract(const A, B: TMatrix4): TMatrix4;
+begin
+ Result.V[0] := A.V[0] - B.V[0];
+ Result.V[1] := A.V[1] - B.V[1];
+ Result.V[2] := A.V[2] - B.V[2];
+ Result.V[3] := A.V[3] - B.V[3];
+end;
+
+class operator TMatrix4.Subtract(const A: Single; const B: TMatrix4): TMatrix4;
+begin
+ Result.V[0] := A - B.V[0];
+ Result.V[1] := A - B.V[1];
+ Result.V[2] := A - B.V[2];
+ Result.V[3] := A - B.V[3];
+end;
+
+method TMatrix4.Transpose: TMatrix4;
+begin
+ Result.M[0,0] := M[0,0];
+ Result.M[0,1] := M[1,0];
+ Result.M[0,2] := M[2,0];
+ Result.M[0,3] := M[3,0];
+
+ Result.M[1,0] := M[0,1];
+ Result.M[1,1] := M[1,1];
+ Result.M[1,2] := M[2,1];
+ Result.M[1,3] := M[3,1];
+
+ Result.M[2,0] := M[0,2];
+ Result.M[2,1] := M[1,2];
+ Result.M[2,2] := M[2,2];
+ Result.M[2,3] := M[3,2];
+
+ Result.M[3,0] := M[0,3];
+ Result.M[3,1] := M[1,3];
+ Result.M[3,2] := M[2,3];
+ Result.M[3,3] := M[3,3];
+end;
+
+{ TMatrix4 }
+
+class operator TMatrix4.Divide(const A, B: TMatrix4): TMatrix4;
+begin
+ Result := A * B.Inverse;
+end;
+
+class operator TMatrix4.Divide(const A: TVector4; const B: TMatrix4): TVector4;
+begin
+ Result := A * B.Inverse;
+end;
+
+class operator TMatrix4.Divide(const A: TMatrix4; const B: TVector4): TVector4;
+begin
+ Result := A.Inverse * B;
+end;
+
+method TMatrix4.GetDeterminant: Single;
+var
+F00, F01, F02, F03, F04, F05: Single;
+C: TVector4;
+begin
+ F00 := (M[2,2] * M[3,3]) - (M[3,2] * M[2,3]);
+ F01 := (M[2,1] * M[3,3]) - (M[3,1] * M[2,3]);
+ F02 := (M[2,1] * M[3,2]) - (M[3,1] * M[2,2]);
+ F03 := (M[2,0] * M[3,3]) - (M[3,0] * M[2,3]);
+ F04 := (M[2,0] * M[3,2]) - (M[3,0] * M[2,2]);
+ F05 := (M[2,0] * M[3,1]) - (M[3,0] * M[2,1]);
+
+ C.X := + ((M[1,1] * F00) - (M[1,2] * F01) + (M[1,3] * F02));
+ C.Y := - ((M[1,0] * F00) - (M[1,2] * F03) + (M[1,3] * F04));
+ C.Z := + ((M[1,0] * F01) - (M[1,1] * F03) + (M[1,3] * F05));
+ C.W := - ((M[1,0] * F02) - (M[1,1] * F04) + (M[1,2] * F05));
+
+ Result := (M[0,0] * C.X) + (M[0,1] * C.Y) + (M[0,2] * C.Z) + (M[0,3] * C.W);
+end;
+
+class operator TMatrix4.Equal(const A, B: TMatrix4): Boolean;
+begin
+ Result := (A.V[0] = B.V[0]) and (A.V[1] = B.V[1]) and (A.V[2] = B.V[2]) and (A.V[3] = B.V[3]);
+end;
+
+method TMatrix4.Init(const ADiagonal: Single);
+begin
+ V[0].Init(ADiagonal, 0, 0, 0);
+ V[1].Init(0, ADiagonal, 0, 0);
+ V[2].Init(0, 0, ADiagonal, 0);
+ V[3].Init(0, 0, 0, ADiagonal);
+end;
+
+method TMatrix4.Init;
+begin
+ V[0].Init(1, 0, 0, 0);
+ V[1].Init(0, 1, 0, 0);
+ V[2].Init(0, 0, 1, 0);
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.Init(const A11, A12, A13, A14, A21, A22, A23, A24, A31, A32,
+A33, A34, A41, A42, A43, A44: Single);
+begin
+ V[0].Init(A11, A12, A13, A14);
+ V[1].Init(A21, A22, A23, A24);
+ V[2].Init(A31, A32, A33, A34);
+ V[3].Init(A41, A42, A43, A44);
+end;
+
+
+method TMatrix4.Init(const AMatrix: TMatrix3);
+begin
+ V[0].Init(AMatrix.V[0], 0);
+ V[1].Init(AMatrix.V[1], 0);
+ V[2].Init(AMatrix.V[2], 0);
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitLookAtLH(const ACameraPosition, ACameraTarget,
+ACameraUp: TVector3);
+var
+XAxis, YAxis, ZAxis: TVector3;
+begin
+ ZAxis := (ACameraTarget - ACameraPosition).Normalize;
+ XAxis := ACameraUp.Cross(ZAxis).Normalize;
+ YAxis := ZAxis.Cross(XAxis);
+
+ V[0].Init(XAxis.X, YAxis.X, ZAxis.X, 0);
+ V[1].Init(XAxis.Y, YAxis.Y, ZAxis.Y, 0);
+ V[2].Init(XAxis.Z, YAxis.Z, ZAxis.Z, 0);
+ V[3].Init(-XAxis.Dot(ACameraPosition),
+ -YAxis.Dot(ACameraPosition),
+ -ZAxis.Dot(ACameraPosition), 1);
+end;
+
+method TMatrix4.InitLookAtRH(const ACameraPosition, ACameraTarget,
+ACameraUp: TVector3);
+var
+XAxis, YAxis, ZAxis: TVector3;
+begin
+ ZAxis := (ACameraPosition - ACameraTarget).Normalize;
+ XAxis := ACameraUp.Cross(ZAxis).Normalize;
+ YAxis := ZAxis.Cross(XAxis);
+
+ V[0].Init(XAxis.X, YAxis.X, ZAxis.X, 0);
+ V[1].Init(XAxis.Y, YAxis.Y, ZAxis.Y, 0);
+ V[2].Init(XAxis.Z, YAxis.Z, ZAxis.Z, 0);
+ V[3].Init(-XAxis.Dot(ACameraPosition),
+ -YAxis.Dot(ACameraPosition),
+ -ZAxis.Dot(ACameraPosition), 1);
+end;
+
+method TMatrix4.InitLookAtDirLH(const ACameraPosition, ACameraDirection,
+ACameraUp: TVector3);
+var
+XAxis, YAxis, ZAxis: TVector3;
+begin
+ ZAxis := -ACameraDirection.Normalize;
+ XAxis := ACameraUp.Cross(ZAxis).Normalize;
+ YAxis := ZAxis.Cross(XAxis);
+
+ V[0].Init(XAxis.X, YAxis.X, ZAxis.X, 0);
+ V[1].Init(XAxis.Y, YAxis.Y, ZAxis.Y, 0);
+ V[2].Init(XAxis.Z, YAxis.Z, ZAxis.Z, 0);
+ V[3].Init(-XAxis.Dot(ACameraPosition),
+ -YAxis.Dot(ACameraPosition),
+ -ZAxis.Dot(ACameraPosition), 1);
+end;
+
+method TMatrix4.InitLookAtDirRH(const ACameraPosition, ACameraDirection,
+ACameraUp: TVector3);
+var
+XAxis, YAxis, ZAxis: TVector3;
+begin
+ ZAxis := ACameraDirection.Normalize;
+ XAxis := ACameraUp.Cross(ZAxis).Normalize;
+ YAxis := ZAxis.Cross(XAxis);
+
+ V[0].Init(XAxis.X, YAxis.X, ZAxis.X, 0);
+ V[1].Init(XAxis.Y, YAxis.Y, ZAxis.Y, 0);
+ V[2].Init(XAxis.Z, YAxis.Z, ZAxis.Z, 0);
+ V[3].Init(-XAxis.Dot(ACameraPosition),
+ -YAxis.Dot(ACameraPosition),
+ -ZAxis.Dot(ACameraPosition), 1);
+end;
+
+method TMatrix4.InitScaling(const AScale: Single);
+begin
+ V[0].Init(AScale, 0, 0, 0);
+ V[1].Init(0, AScale, 0, 0);
+ V[2].Init(0, 0, AScale, 0);
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitScaling(const AScaleX, AScaleY, AScaleZ: Single);
+begin
+ V[0].Init(AScaleX, 0, 0, 0);
+ V[1].Init(0, AScaleY, 0, 0);
+ V[2].Init(0, 0, AScaleZ, 0);
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitScaling(const AScale: TVector3);
+begin
+ V[0].Init(AScale.X, 0, 0, 0);
+ V[1].Init(0, AScale.Y, 0, 0);
+ V[2].Init(0, 0, AScale.Z, 0);
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitTranslation(const ADeltaX, ADeltaY, ADeltaZ: Single);
+begin
+ V[0].Init(1, 0, 0, 0);
+ V[1].Init(0, 1, 0, 0);
+ V[2].Init(0, 0, 1, 0);
+ V[3].Init(ADeltaX, ADeltaY, ADeltaZ, 1);
+end;
+
+method TMatrix4.InitTranslation(const ADelta: TVector3);
+begin
+ V[0].Init(1, 0, 0, 0);
+ V[1].Init(0, 1, 0, 0);
+ V[2].Init(0, 0, 1, 0);
+ V[3].Init(ADelta.X, ADelta.Y, ADelta.Z, 1);
+end;
+
+class operator TMatrix4.NotEqual(const A, B: TMatrix4): Boolean;
+begin
+ Result := (A.V[0] <> B.V[0]) or (A.V[1] <> B.V[1]) or (A.V[2] <> B.V[2]) or (A.V[3] <> B.V[3]);
+end;
+
+method TMatrix4.InitRotationX(const AAngle: Single);
+var
+S, C: Single;
+begin
+ MetalMath.SinCos(AAngle, out S, out C);
+ V[0].Init(1, 0, 0, 0);
+ V[1].Init(0, C, S, 0);
+ V[2].Init(0, -S, C, 0);
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitRotationY(const AAngle: Single);
+var
+S, C: Single;
+begin
+ MetalMath.SinCos(AAngle, out S, out C);
+ V[0].Init(C, 0, -S, 0);
+ V[1].Init(0, 1, 0, 0);
+ V[2].Init(S, 0, C, 0);
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitRotationZ(const AAngle: Single);
+var
+S, C: Single;
+begin
+ MetalMath.SinCos(AAngle, out S,out C);
+ V[0].Init(C, S, 0, 0);
+ V[1].Init(-S, C, 0, 0);
+ V[2].Init(0, 0, 1, 0);
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitRotation(const AAxis: TVector3; const AAngle: Single);
+var
+S, C, C1: Single;
+N: TVector3;
+begin
+ MetalMath.SinCos(AAngle, out S, out C);
+ C1 := 1 - C;
+ N := AAxis.Normalize;
+
+ V[0].Init((C1 * N.X * N.X) + C,
+ (C1 * N.X * N.Y) + (N.Z * S),
+ (C1 * N.Z * N.X) - (N.Y * S), 0);
+
+
+ V[1].Init((C1 * N.X * N.Y) - (N.Z * S),
+ (C1 * N.Y * N.Y) + C,
+ (C1 * N.Y * N.Z) + (N.X * S), 0);
+
+ V[2].Init((C1 * N.Z * N.X) + (N.Y * S),
+ (C1 * N.Y * N.Z) - (N.X * S),
+ (C1 * N.Z * N.Z) + C, 0);
+
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitRotationYawPitchRoll(const AYaw, APitch, ARoll: Single);
+var
+A, S, C: TVector4;
+begin
+ A.Init(APitch, AYaw, ARoll, 0);
+ MetalMath.SinCos(A, out S, out C);
+
+ V[0].Init((C.Y * C.Z) + (S.X * S.Y * S.Z),
+ (C.Y * S.X * S.Z) - (C.Z * S.Y),
+ -C.X * S.Z, 0);
+
+ V[1].Init(C.X * S.Y,
+ C.X * C.Y,
+ S.X, 0);
+
+ V[2].Init((C.Y * S.Z) - (C.Z * S.X * S.Y),
+ (-C.Z * C.Y * S.X) - (S.Z * S.Y),
+ C.X * C.Z, 0);
+
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.InitRotationHeadingPitchBank(const AHeading, APitch, ABank: Single);
+var
+A, S, C: TVector4;
+begin
+ A.Init(AHeading, APitch, ABank, 0);
+ MetalMath.SinCos(A, out S, out C);
+
+ V[0].Init((C.X * C.Z) + (S.X * S.Y * S.Z),
+ (-C.X * S.Z) + (S.X * S.Y * C.Z),
+ S.X * C.Y, 0);
+
+ V[1].Init(S.Z * C.Y,
+ C.Y * C.Z,
+ -S.Y, 0);
+
+ V[2].Init((-S.X * C.Z) + (C.X * S.Y * S.Z),
+ (S.Z * S.X) + (C.X * S.Y * C.Z),
+ C.X * C.Y, 0);
+
+ V[3].Init(0, 0, 0, 1);
+end;
+
+method TMatrix4.GetComponent(const ARow, AColumn: Integer): Single;
+
+require
+ (ARow >= 0) and (ARow < 4);
+ (AColumn >= 0) and (AColumn < 4);
+
+ begin
+ Result := V[ARow][AColumn];
+ end;
+
+ method TMatrix4.GetRow(const AIndex: Integer): TVector4;
+
+ require
+ (AIndex >= 0) and (AIndex < 4);
+
+ begin
+ Result := V[AIndex];
+ end;
+
+ method TMatrix4.Init(const ARow0, ARow1, ARow2, ARow3: TVector4);
+ begin
+ V[0] := ARow0;
+ V[1] := ARow1;
+ V[2] := ARow2;
+ V[3] := ARow3;
+ end;
+
+ method TMatrix4.InitOrthoLH(const AWidth, AHeight, AZNearPlane,
+ AZFarPlane: Single);
+ begin
+ V[0].Init(2 / AWidth, 0, 0, 0);
+ V[1].Init(0, 2 / AHeight, 0, 0);
+ V[2].Init(0, 0, 1 / (AZFarPlane - AZNearPlane), 0);
+ V[3].Init(0, AZNearPlane / (AZNearPlane - AZFarPlane), 0, 1);
+ end;
+
+ method TMatrix4.InitOrthoRH(const AWidth, AHeight, AZNearPlane,
+ AZFarPlane: Single);
+ begin
+ V[0].Init(2 / AWidth, 0, 0, 0);
+ V[1].Init(0, 2 / AHeight, 0, 0);
+ V[2].Init(0, 0, 1 / (AZNearPlane - AZFarPlane), 0);
+ V[3].Init(0, AZNearPlane / (AZNearPlane - AZFarPlane), 0, 1);
+ end;
+
+ method TMatrix4.InitOrthoOffCenterLH(const ALeft, ATop, ARight, ABottom,
+ AZNearPlane, AZFarPlane: Single);
+ begin
+ V[0].Init(2 / (ARight - ALeft), 0, 0, 0);
+ V[1].Init(0, 2 / (ATop - ABottom), 0, 0);
+ V[2].Init(0, 0, 1 / (AZFarPlane - AZNearPlane), 0);
+ V[3].Init((ALeft + ARight) / (ALeft - ARight),
+ (ATop + ABottom) / (ABottom - ATop),
+ AZNearPlane / (AZNearPlane - AZFarPlane), 1);
+ end;
+
+ method TMatrix4.InitOrthoOffCenterRH(const ALeft, ATop, ARight, ABottom,
+ AZNearPlane, AZFarPlane: Single);
+ begin
+ V[0].Init(2 / (ARight - ALeft), 0, 0, 0);
+ V[1].Init(0, 2 / (ATop - ABottom), 0, 0);
+ V[2].Init(0, 0, 1 / (AZNearPlane - AZFarPlane), 0);
+ V[3].Init((ALeft + ARight) / (ALeft - ARight),
+ (ATop + ABottom) / (ABottom - ATop),
+ AZNearPlane / (AZNearPlane - AZFarPlane), 1);
+ end;
+
+ method TMatrix4.InitPerspectiveFovLH(const AFieldOfView, AAspectRatio,
+ ANearPlaneDistance, AFarPlaneDistance: Single; const AHorizontalFOV: Boolean);
+ var
+ XScale, YScale: Single;
+ begin
+ if (AHorizontalFOV) then
+ begin
+ XScale := 1 / Math.Tan(0.5 * AFieldOfView);
+ YScale := XScale / AAspectRatio;
+ end
+ else
+ begin
+ YScale := 1 / Math.Tan(0.5 * AFieldOfView);
+ XScale := YScale / AAspectRatio;
+ end;
+
+ V[0].Init(XScale, 0, 0, 0);
+ V[1].Init(0, YScale, 0, 0);
+ V[2].Init(0, 0, AFarPlaneDistance / (AFarPlaneDistance - ANearPlaneDistance), 1);
+ V[3].Init(0, 0, (-ANearPlaneDistance * AFarPlaneDistance) / (AFarPlaneDistance - ANearPlaneDistance), 0);
+ end;
+
+ method TMatrix4.InitPerspectiveFovRH(const AFieldOfView, AAspectRatio,
+ ANearPlaneDistance, AFarPlaneDistance: Single; const AHorizontalFOV: Boolean);
+ var
+ XScale, YScale: Single;
+ begin
+ if (AHorizontalFOV) then
+ begin
+ XScale := 1 / Math.Tan(0.5 * AFieldOfView);
+ YScale := XScale / AAspectRatio;
+ end
+ else
+ begin
+ YScale := 1 / Math.Tan(0.5 * AFieldOfView);
+ XScale := YScale / AAspectRatio;
+ end;
+
+ V[0].Init(XScale, 0, 0, 0);
+ V[1].Init(0, YScale, 0, 0);
+ V[2].Init(0, 0, AFarPlaneDistance / (ANearPlaneDistance - AFarPlaneDistance), -1);
+ V[3].Init(0, 0, (ANearPlaneDistance * AFarPlaneDistance) / (ANearPlaneDistance - AFarPlaneDistance), 0);
+ end;
+
+ method TMatrix4.SetComponent(const ARow, AColumn: Integer; const Value: Single);
+
+ require
+ (ARow >= 0) and (ARow < 4);
+ (AColumn >= 0) and (AColumn < 4);
+
+ begin
+ V[ARow][AColumn] := Value;
+ end;
+
+
+ method TMatrix4.SetRow(const AIndex: Integer; const Value: TVector4);
+
+ require
+ (AIndex >= 0) and (AIndex < 4);
+
+ begin
+ V[AIndex] := Value;
+ end;
+
+
+ method TMatrix4.getPglMatrix4f: ^Single;
+ begin
+ exit @V[0].X;
+ end;
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVec3.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVec3.pas
new file mode 100644
index 0000000..6b63bbf
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVec3.pas
@@ -0,0 +1,963 @@
+
+
+namespace MetalExample;
+
+interface
+
+
+type
+ { A 3-dimensional vector. Can be used for a variety of purposes:
+ * To represent points or vectors in 3D space, using the X, Y and Z fields.
+ * To represent colors (using the R, G and B fields, which are just aliases
+ for X, Y and Z)
+ * To represent texture coordinates (S, T and P, again just aliases for X, Y
+ and Z).
+ * To group 3 arbitrary values together in an array (C[0]..C[2], also just
+ aliases for X, Y and Z)
+
+ @bold(Note): when possible, use TVector4 instead of TVector3, since TVector4
+ has better hardware support. Operations on TVector4 types are usually faster
+ than operations on TVector3 types.
+
+ }
+ TVector3 = record
+
+ private
+ method GetComponent(const AIndex: Integer): Single;
+ method SetComponent(const AIndex: Integer; const Value: Single);
+ method GetLength: Single;
+ method SetLength(const AValue: Single);
+ method GetLengthSquared: Single;
+ method SetLengthSquared(const AValue: Single);
+
+ public
+ constructor (const A1, A2, A3: Single);
+ class method Vector3(const A1, A2, A3: Single): TVector3;
+
+ { Sets the three elements (X, Y and Z) to 0. }
+ {$HIDE W8}
+ method Init;
+ {$SHOW W8}
+
+ { Sets the three elements (X, Y and Z) to A.
+
+ Parameters:
+ A: the value to set the three elements to. }
+ method Init(const A: Single);
+
+ { Sets the three elements (X, Y and Z) to A1, A2 and A3 respectively.
+
+ Parameters:
+ A1: the value to set the first element to.
+ A2: the value to set the second element to.
+ A3: the value to set the third element to. }
+ method Init(const A1, A2, A3: Single);
+
+ { Sets the first two elements from a 2D vector, and the third element from
+ a scalar.
+
+ Parameters:
+ A1: the vector to use for the first two elements.
+ A2: the value to set the third element to. }
+ method Init(const A1: TVector2; const A2: Single);
+
+ { Sets the first element from a scaler, and the last two elements from a
+ 2D vector.
+
+ Parameters:
+ A1: the value to set the first element to.
+ A2: the vector to use for the last two elements. }
+ method Init(const A1: Single; const A2: TVector2);
+
+
+ { Checks two vectors for equality.
+
+ Returns:
+ True if the two vectors match each other exactly. }
+ class operator Equal(const A, B: TVector3): Boolean; inline;
+
+ { Checks two vectors for inequality.
+
+ Returns:
+ True if the two vectors are not equal. }
+ class operator NotEqual(const A, B: TVector3): Boolean; inline;
+
+ { Negates a vector.
+
+ Returns:
+ The negative value of a vector (eg. (-A.X, -A.Y, -A.Z)) }
+ class operator Minus(const A: TVector3): TVector3; inline;
+
+ { Adds a scalar value to a vector.
+
+ Returns:
+ (A.X + B, A.Y + B, A.Z + B) }
+ class operator Add(const A: TVector3; const B: Single): TVector3; inline;
+
+ { Adds a vector to a scalar value.
+
+ Returns:
+ (A + B.X, A + B.Y, A + B.Z) }
+ class operator Add(const A: Single; const B: TVector3): TVector3; inline;
+
+ { Adds two vectors.
+
+ Returns:
+ (A.X + B.X, A.Y + B.Y, A.Z + B.Z) }
+ class operator Add(const A, B: TVector3): TVector3; inline;
+
+ { Subtracts a scalar value from a vector.
+
+ Returns:
+ (A.X - B, A.Y - B, A.Z - B) }
+ class operator Subtract(const A: TVector3; const B: Single): TVector3;inline;
+
+ { Subtracts a vector from a scalar value.
+
+ Returns:
+ (A - B.X, A - B.Y, A - B.Z) }
+ class operator Subtract(const A: Single; const B: TVector3): TVector3; inline;
+
+ { Subtracts two vectors.
+
+ Returns:
+ (A.X - B.X, A.Y - B.Y, A.Z - B.Z) }
+ class operator Subtract(const A, B: TVector3): TVector3; inline;
+
+ { Multiplies a vector with a scalar value.
+
+ Returns:
+ (A.X * B, A.Y * B, A.Z * B) }
+ class operator Multiply(const A: TVector3; const B: Single): TVector3; inline;
+
+ { Multiplies a scalar value with a vector.
+
+ Returns:
+ (A * B.X, A * B.Y, A * B.Z) }
+ class operator Multiply(const A: Single; const B: TVector3): TVector3; inline;
+
+ { Multiplies two vectors component-wise.
+ To calculate a dot or cross product instead, use the Dot or Cross function.
+
+ Returns:
+ (A.X * B.X, A.Y * B.Y, A.Z * B.Z) }
+ class operator Multiply(const A, B: TVector3): TVector3; inline;
+
+ { Divides a vector by a scalar value.
+
+ Returns:
+ (A.X / B, A.Y / B, A.Z / B) }
+ class operator Divide(const A: TVector3; const B: Single): TVector3; inline;
+
+ { Divides a scalar value by a vector.
+
+ Returns:
+ (A / B.X, A / B.Y, A / B.Z) }
+ class operator Divide(const A: Single; const B: TVector3): TVector3; inline;
+
+ { Divides two vectors component-wise.
+
+ Returns:
+ (A.X / B.X, A.Y / B.Y, A.Z / B.Z) }
+ class operator Divide(const A, B: TVector3): TVector3; inline;
+
+ { Whether this vector equals another vector, within a certain tolerance.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if both vectors are equal (within the given tolerance). }
+ method Equals(const AOther: TVector3; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Calculates the distance between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The distance between this vector and AOther.
+
+ @bold(Note): If you only want to compare distances, you should use
+ DistanceSquared instead, which is faster. }
+ method Distance(const AOther: TVector3): Single;
+
+ { Calculates the squared distance between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The squared distance between this vector and AOther. }
+ method DistanceSquared(const AOther: TVector3): Single;
+
+ { Calculates the dot product between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The dot product between this vector and AOther. }
+ method Dot(const AOther: TVector3): Single;
+
+ { Calculates the cross product between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The cross product between this vector and AOther. }
+ method Cross(const AOther: TVector3): TVector3;
+
+ { Offsets this vector in a certain direction.
+
+ Parameters:
+ ADeltaX: the delta X direction.
+ ADeltaY: the delta Y direction.
+ ADeltaZ: the delta Z direction. }
+ method Offset(const ADeltaX, ADeltaY, ADeltaZ: Single);
+
+ { Offsets this vector in a certain direction.
+
+ Parameters:
+ ADelta: the delta.
+
+ @bold(Note): this is equivalent to adding two vectors together. }
+ method Offset(const ADelta: TVector3);
+
+
+ { Calculates a normalized version of this vector.
+
+ Returns:
+ The normalized version of of this vector. That is, a vector in the same
+ direction as A, but with a length of 1.
+
+ @bold(Note): for a faster, less accurate version, use NormalizeFast.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetNormalized. }
+ method Normalize: TVector3;
+
+ { Normalizes this vector. This is, keep the current direction, but set the
+ length to 1.
+
+ @bold(Note): The SIMD optimized versions of this method use an
+ approximation, resulting in a very small error.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ normalized version instead, then use Normalize. }
+ method SetNormalized;
+
+ { Calculates a normalized version of this vector.
+
+ Returns:
+ The normalized version of of this vector. That is, a vector in the same
+ direction as A, but with a length of 1.
+
+ @bold(Note): this is an SIMD optimized version that uses an approximation,
+ resulting in a small error. For an accurate version, use Normalize.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetNormalizedFast. }
+ method NormalizeFast: TVector3;
+
+ { Normalizes this vector. This is, keep the current direction, but set the
+ length to 1.
+
+ @bold(Note): this is an SIMD optimized version that uses an approximation,
+ resulting in a small error. For an accurate version, use SetNormalized.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ normalized version instead, then use NormalizeFast. }
+ method SetNormalizedFast;
+
+ { Calculates a vector pointing in the same direction as this vector.
+
+ Parameters:
+ I: the incident vector.
+ NRef: the reference vector.
+
+ Returns:
+ A vector that points away from the surface as defined by its normal. If
+ NRef.Dot(I) < 0 then it returns this vector, otherwise it returns the
+ negative of this vector. }
+ method FaceForward(const I, NRef: TVector3): TVector3;
+
+ { Calculates the reflection direction for this (incident) vector.
+
+ Parameters:
+ N: the normal vector. Should be normalized in order to achieve the desired
+ result.
+
+ Returns:
+ The reflection direction calculated as Self - 2 * N.Dot(Self) * N. }
+ method Reflect(const N: TVector3): TVector3;
+
+ { Calculates the refraction direction for this (incident) vector.
+
+ Parameters:
+ N: the normal vector. Should be normalized in order to achieve the
+ desired result.
+ Eta: the ratio of indices of refraction.
+
+ Returns:
+ The refraction vector.
+
+ @bold(Note): This vector should be normalized in order to achieve the
+ desired result.}
+ method Refract(const N: TVector3; const Eta: Single): TVector3;
+
+ { Creates a vector with the same direction as this vector, but with the
+ length limited, based on the desired maximum length.
+
+ Parameters:
+ AMaxLength: The desired maximum length of the vector.
+
+ Returns:
+ A length-limited version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLimit. }
+ method Limit(const AMaxLength: Single): TVector3;
+
+ { Limits the length of this vector, based on the desired maximum length.
+
+ Parameters:
+ AMaxLength: The desired maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-limited version instead, then use Limit. }
+ method SetLimit(const AMaxLength: Single);
+
+ { Creates a vector with the same direction as this vector, but with the
+ length limited, based on the desired squared maximum length.
+
+ Parameters:
+ AMaxLengthSquared: The desired squared maximum length of the vector.
+
+ Returns:
+ A length-limited version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLimitSquared. }
+ method LimitSquared(const AMaxLengthSquared: Single): TVector3;
+
+ { Limits the length of this vector, based on the desired squared maximum
+ length.
+
+ Parameters:
+ AMaxLengthSquared: The desired squared maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-limited version instead, then use LimitSquared. }
+ method SetLimitSquared(const AMaxLengthSquared: Single);
+
+ { Creates a vector with the same direction as this vector, but with the
+ length clamped between a minimim and maximum length.
+
+ Parameters:
+ AMinLength: The minimum length of the vector.
+ AMaxLength: The maximum length of the vector.
+
+ Returns:
+ A length-clamped version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetClamped. }
+ method Clamp(const AMinLength, AMaxLength: Single): TVector3;
+
+ { Clamps the length of this vector between a minimim and maximum length.
+
+ Parameters:
+ AMinLength: The minimum length of this vector.
+ AMaxLength: The maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-clamped version instead, then use Clamp. }
+ method SetClamped(const AMinLength, AMaxLength: Single);
+
+ { Linearly interpolates between this vector and a target vector.
+
+ Parameters:
+ ATarget: the target vector.
+ AAlpha: the interpolation coefficient (between 0.0 and 1.0).
+
+ Returns:
+ The interpolation result vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLerp. }
+ method Lerp(const ATarget: TVector3; const AAlpha: Single): TVector3;
+
+ { Linearly interpolates between this vector and a target vector and stores
+ the result in this vector.
+
+ Parameters:
+ ATarget: the target vector.
+ AAlpha: the interpolation coefficient (between 0.0 and 1.0).
+
+ @bold(Note): If you do not want to change this vector, but get an
+ interpolated version instead, then use Lerp. }
+ method SetLerp(const ATarget: TVector3; const AAlpha: Single);
+
+ { Whether the vector is normalized (within a small margin of error).
+
+ Returns:
+ True if the length of the vector is (very close to) 1.0 }
+ method IsNormalized: Boolean;
+
+ { Whether the vector is normalized within a given margin of error.
+
+ Parameters:
+ AErrorMargin: the allowed margin of error.
+
+ Returns:
+ True if the squared length of the vector is 1.0 within the margin of
+ error. }
+ method IsNormalized(const AErrorMargin: Single): Boolean;
+
+ { Whether this is a zero vector.
+
+ Returns:
+ True if X, Y and Z are exactly 0.0 }
+ method IsZero: Boolean;
+
+ { Whether this is a zero vector within a given margin of error.
+
+ Parameters:
+ AErrorMargin: the allowed margin of error.
+
+ Returns:
+ True if the squared length is smaller then the margin of error. }
+ method IsZero(const AErrorMargin: Single): Boolean;
+
+ { Whether this vector has a similar direction compared to another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ True if the normalized dot product is greater than 0. }
+ method HasSameDirection(const AOther: TVector3): Boolean;
+
+ { Whether this vector has an opposite direction compared to another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ True if the normalized dot product is less than 0. }
+ method HasOppositeDirection(const AOther: TVector3): Boolean;
+
+ { Whether this vector runs parallel to another vector (either in the same
+ or the opposite direction).
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector runs parallel to AOther (within the given tolerance) }
+ method IsParallel(const AOther: TVector3; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is collinear with another vector. Two vectors are
+ collinear if they run parallel to each other and point in the same
+ direction.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is collinear to AOther (within the given tolerance) }
+ method IsCollinear(const AOther: TVector3; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is opposite collinear with another vector. Two vectors
+ are opposite collinear if they run parallel to each other and point in
+ opposite directions.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is opposite collinear to AOther (within the given
+ tolerance) }
+ method IsCollinearOpposite(const AOther: TVector3; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is perpendicular to another vector.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is perpendicular to AOther. That is, if the dot
+ product is 0 (within the given tolerance) }
+ method IsPerpendicular(const AOther: TVector3; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Returns the components of the vector.
+ This is identical to accessing the C-field, but this property can be used
+ as a default array property.
+
+ Parameters:
+ AIndex: index of the component to return (0-2). Range is checked
+ with an assertion. }
+ property Components[const AIndex: Integer]: Single read GetComponent write SetComponent; default;
+
+ { The euclidean length of this vector.
+
+ @bold(Note): If you only want to compare lengths of vectors, you should
+ use LengthSquared instead, which is faster.
+
+ @bold(Note): You can also set the length of the vector. In that case, it
+ will keep the current direction. }
+ property Length: Single read GetLength write SetLength;
+
+ { The squared length of the vector.
+
+ @bold(Note): This property is faster than Length because it avoids
+ calculating a square root. It is useful for comparing lengths instead of
+ calculating actual lengths.
+
+ @bold(Note): You can also set the squared length of the vector. In that
+ case, it will keep the current direction. }
+ property LengthSquared: Single read GetLengthSquared write SetLengthSquared;
+ public
+ X, Y, Z: Single;
+
+ property R : Single read X write X;
+ property G : Single read Y write Y;
+ property B : Single read Z write Z;
+
+ property S : Single read X write X;
+ property T : Single read Y write Y;
+ property P : Single read Z write Z;
+
+// case Byte of
+// { X, Y and Z components of the vector. Aliases for C[0], C[1] and C[2]. }
+// 0: (X, Y, Z: Single);
+//
+// { Red, Green and Blue components of the vector. Aliases for C[0], C[1]
+// and C[2]. }
+// 1: (R, G, B: Single);
+//
+// { S, T and P components of the vector. Aliases for C[0], C[1] and C[2]. }
+// 2: (S, T, P: Single);
+//
+// { The three components of the vector. }
+// 3: (C: array [0..2] of Single);
+ end;
+
+implementation
+
+{ TVector3 }
+
+class operator TVector3.Add(const A: TVector3; const B: Single): TVector3;
+begin
+ Result.X := A.X + B;
+ Result.Y := A.Y + B;
+ Result.Z := A.Z + B;
+end;
+
+class operator TVector3.Add(const A: Single; const B: TVector3): TVector3;
+begin
+ Result.X := A + B.X;
+ Result.Y := A + B.Y;
+ Result.Z := A + B.Z;
+end;
+
+class operator TVector3.Add(const A, B: TVector3): TVector3;
+begin
+ Result.X := A.X + B.X;
+ Result.Y := A.Y + B.Y;
+ Result.Z := A.Z + B.Z;
+end;
+
+method TVector3.Distance(const AOther: TVector3): Single;
+begin
+ Result := (Self - AOther).Length;
+end;
+
+method TVector3.DistanceSquared(const AOther: TVector3): Single;
+begin
+ Result := (Self - AOther).LengthSquared;
+end;
+
+class operator TVector3.Divide(const A: TVector3; const B: Single): TVector3;
+var
+ InvB: Single;
+begin
+ InvB := 1 / B;
+ Result.X := A.X * InvB;
+ Result.Y := A.Y * InvB;
+ Result.Z := A.Z * InvB;
+end;
+
+class operator TVector3.Divide(const A: Single; const B: TVector3): TVector3;
+begin
+ Result.X := A / B.X;
+ Result.Y := A / B.Y;
+ Result.Z := A / B.Z;
+end;
+
+class operator TVector3.Divide(const A, B: TVector3): TVector3;
+begin
+ Result.X := A.X / B.X;
+ Result.Y := A.Y / B.Y;
+ Result.Z := A.Z / B.Z;
+end;
+
+method TVector3.Cross(const AOther: TVector3): TVector3;
+begin
+ Result.X := (Y * AOther.Z) - (AOther.Y * Z);
+ Result.Y := (Z * AOther.X) - (AOther.Z * X);
+ Result.Z := (X * AOther.Y) - (AOther.X * Y);
+end;
+
+method TVector3.Dot(const AOther: TVector3): Single;
+begin
+ Result := (X * AOther.X) + (Y * AOther.Y) + (Z * AOther.Z);
+end;
+
+method TVector3.FaceForward(const I, NRef: TVector3): TVector3;
+begin
+ if (NRef.Dot(I) < 0) then
+ Result := Self
+ else
+ Result := -Self;
+end;
+
+method TVector3.GetLength: Single;
+begin
+ Result := sqrt((X * X) + (Y * Y) + (Z * Z));
+end;
+
+method TVector3.GetLengthSquared: Single;
+begin
+ Result := (X * X) + (Y * Y) + (Z * Z);
+end;
+
+class operator TVector3.Multiply(const A: TVector3; const B: Single): TVector3;
+begin
+ Result.X := A.X * B;
+ Result.Y := A.Y * B;
+ Result.Z := A.Z * B;
+end;
+
+class operator TVector3.Multiply(const A: Single; const B: TVector3): TVector3;
+begin
+ Result.X := A * B.X;
+ Result.Y := A * B.Y;
+ Result.Z := A * B.Z;
+end;
+
+class operator TVector3.Multiply(const A, B: TVector3): TVector3;
+begin
+ Result.X := A.X * B.X;
+ Result.Y := A.Y * B.Y;
+ Result.Z := A.Z * B.Z;
+end;
+
+class operator TVector3.Minus(const A: TVector3): TVector3;
+begin
+ Result.X := -A.X;
+ Result.Y := -A.Y;
+ Result.Z := -A.Z;
+end;
+
+method TVector3.NormalizeFast: TVector3;
+begin
+ Result := Self * MetalMath.InverseSqrt(Self.LengthSquared);
+end;
+
+method TVector3.Reflect(const N: TVector3): TVector3;
+begin
+ Result := Self - ((2 * N.Dot(Self)) * N);
+end;
+
+method TVector3.Refract(const N: TVector3; const Eta: Single): TVector3;
+var
+ D, K: Single;
+begin
+ D := N.Dot(Self);
+ K := 1 - Eta * Eta * (1 - D * D);
+ if (K < 0) then
+ Result.Init
+ else
+ Result := (Eta * Self) - ((Eta * D + sqrt(K)) * N);
+end;
+
+method TVector3.SetNormalizedFast;
+begin
+ Self := Self * MetalMath.InverseSqrt(Self.LengthSquared);
+end;
+
+class operator TVector3.Subtract(const A: TVector3; const B: Single): TVector3;
+begin
+ Result.X := A.X - B;
+ Result.Y := A.Y - B;
+ Result.Z := A.Z - B;
+end;
+
+class operator TVector3.Subtract(const A: Single; const B: TVector3): TVector3;
+begin
+ Result.X := A - B.X;
+ Result.Y := A - B.Y;
+ Result.Z := A - B.Z;
+end;
+
+class operator TVector3.Subtract(const A, B: TVector3): TVector3;
+begin
+ Result.X := A.X - B.X;
+ Result.Y := A.Y - B.Y;
+ Result.Z := A.Z - B.Z;
+end;
+
+{ TVector3 }
+
+method TVector3.Clamp(const AMinLength, AMaxLength: Single): TVector3;
+var
+ LenSq, EdgeSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq = 0) then
+ Exit(Self);
+
+ EdgeSq := AMaxLength * AMaxLength;
+ if (LenSq > EdgeSq) then
+ Exit(Self * sqrt(EdgeSq / LenSq));
+
+ EdgeSq := AMinLength * AMinLength;
+ if (LenSq < EdgeSq) then
+ Exit(Self * sqrt(EdgeSq / LenSq));
+
+ Result := Self;
+end;
+
+class operator TVector3.Equal(const A, B: TVector3): Boolean;
+begin
+ Result := (A.X = B.X) and (A.Y = B.Y) and (A.Z = B.Z);
+end;
+
+method TVector3.Equals(const AOther: TVector3; const ATolerance: Single): Boolean;
+begin
+ Result := (Abs(X - AOther.X) <= ATolerance)
+ and (Abs(Y - AOther.Y) <= ATolerance)
+ and (Abs(Z - AOther.Z) <= ATolerance);
+end;
+
+method TVector3.GetComponent(const AIndex: Integer): Single;
+begin
+
+ case AIndex of
+ 0 : result := X;
+ 1 : result := Y;
+ 2 : result := Z;
+ end;
+ //Result := C[AIndex];
+end;
+
+method TVector3.HasSameDirection(const AOther: TVector3): Boolean;
+begin
+ Result := (Dot(AOther) > 0);
+end;
+
+method TVector3.HasOppositeDirection(const AOther: TVector3): Boolean;
+begin
+ Result := (Dot(AOther) < 0);
+end;
+
+
+
+method TVector3.Init(const A: Single);
+begin
+ X := A;
+ Y := A;
+ Z := A;
+end;
+
+method TVector3.Init;
+begin
+ X := 0;
+ Y := 0;
+ Z := 0;
+end;
+
+method TVector3.Init(const A1, A2, A3: Single);
+begin
+ X := A1;
+ Y := A2;
+ Z := A3;
+end;
+
+method TVector3.Init(const A1: Single; const A2: TVector2);
+begin
+ X := A1;
+ Y := A2.X;
+ Z := A2.Y;
+end;
+
+method TVector3.Init(const A1: TVector2; const A2: Single);
+begin
+ X := A1.X;
+ Y := A1.Y;
+ Z := A2;
+end;
+
+method TVector3.IsCollinear(const AOther: TVector3; const ATolerance: Single): Boolean;
+begin
+ Result := IsParallel(AOther, ATolerance) and (Dot(AOther) > 0);
+end;
+
+method TVector3.IsCollinearOpposite(const AOther: TVector3; const ATolerance: Single): Boolean;
+begin
+ Result := IsParallel(AOther, ATolerance) and (Dot(AOther) < 0);
+end;
+
+method TVector3.IsNormalized: Boolean;
+begin
+ Result := IsNormalized(0.000000001);
+end;
+
+method TVector3.IsNormalized(const AErrorMargin: Single): Boolean;
+begin
+ Result := (Abs(LengthSquared - 1.0) < AErrorMargin);
+end;
+
+method TVector3.IsParallel(const AOther: TVector3; const ATolerance: Single): Boolean;
+
+begin
+ Result := ((Vector3(Y * AOther.Z - Z * AOther.Y,
+ Z * AOther.X - X * AOther.Z,
+ X * AOther.Y - Y * AOther.X).LengthSquared) <= ATolerance);
+end;
+
+method TVector3.IsPerpendicular(const AOther: TVector3; const ATolerance: Single): Boolean;
+begin
+ Result := (Abs(Dot(AOther)) <= ATolerance);
+end;
+
+method TVector3.IsZero: Boolean;
+begin
+ Result := (X = 0) and (Y = 0) and (Z = 0);
+end;
+
+method TVector3.IsZero(const AErrorMargin: Single): Boolean;
+begin
+ Result := (LengthSquared < AErrorMargin);
+end;
+
+method TVector3.Lerp(const ATarget: TVector3; const AAlpha: Single): TVector3;
+begin
+ Result := MetalMath.Mix(Self, ATarget, AAlpha);
+end;
+
+method TVector3.Limit(const AMaxLength: Single): TVector3;
+begin
+ Result := LimitSquared(AMaxLength * AMaxLength);
+end;
+
+method TVector3.LimitSquared(const AMaxLengthSquared: Single): TVector3;
+var
+ LenSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq > AMaxLengthSquared) then
+ Result := Self * sqrt(AMaxLengthSquared / LenSq)
+ else
+ Result := Self;
+end;
+
+method TVector3.Normalize: TVector3;
+begin
+ Result := Self / Length;
+end;
+
+class operator TVector3.NotEqual(const A, B: TVector3): Boolean;
+begin
+ Result := (A.X <> B.X) or (A.Y <> B.Y) or (A.Z <> B.Z);
+end;
+
+method TVector3.Offset(const ADeltaX, ADeltaY, ADeltaZ: Single);
+begin
+ X := X + ADeltaX;
+ Y := Y + ADeltaY;
+ Z := Z + ADeltaZ;
+end;
+
+method TVector3.Offset(const ADelta: TVector3);
+begin
+ Self := Self + ADelta;
+end;
+
+method TVector3.SetClamped(const AMinLength, AMaxLength: Single);
+begin
+ Self := Clamp(AMinLength, AMaxLength);
+end;
+
+method TVector3.SetComponent(const AIndex: Integer; const Value: Single);
+begin
+
+ case AIndex of
+ 0 : X := Value;
+ 1 : Y := Value;
+ 2 : Z := Value;
+ end;
+ //C[AIndex] := Value;
+end;
+
+method TVector3.SetLength(const AValue: Single);
+begin
+ setLengthSquared(AValue * AValue);
+end;
+
+method TVector3.SetLengthSquared(const AValue: Single);
+var
+ LenSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq <> 0) and (LenSq <> AValue) then
+ Self := Self * sqrt(AValue / LenSq);
+end;
+
+method TVector3.SetLerp(const ATarget: TVector3; const AAlpha: Single);
+begin
+ Self := MetalMath.Mix(Self, ATarget, AAlpha);
+end;
+
+method TVector3.SetLimit(const AMaxLength: Single);
+begin
+ Self := LimitSquared(AMaxLength * AMaxLength);
+end;
+
+method TVector3.SetLimitSquared(const AMaxLengthSquared: Single);
+begin
+ Self := LimitSquared(AMaxLengthSquared);
+end;
+
+method TVector3.SetNormalized;
+begin
+ Self := Self / Length;
+end;
+
+class method TVector3.Vector3(const A1: Single; const A2: Single; const A3: Single): TVector3;
+begin
+ result.Init(A1, A2, A3);
+end;
+
+constructor TVector3(const A1: Single; const A2: Single; const A3: Single);
+begin
+ X := A1;
+ Y := A2;
+ Z := A3;
+end;
+
+
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVec4.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVec4.pas
new file mode 100644
index 0000000..01b6296
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVec4.pas
@@ -0,0 +1,1027 @@
+namespace MetalExample;
+
+interface
+
+
+type
+ { A 4-dimensional vector. Can be used for a variety of purposes:
+ * To represent points or vectors in 4D space, using the X, Y, Z and W
+ fields.
+ * To represent colors with alpha channel information (using the R, G, B and
+ A fields, which are just aliases for X, Y, Z and W)
+ * To represent texture coordinates (S, T, P and Q, again just aliases for X,
+ Y, Z and W).
+ * To group 4 arbitrary values together in an array (C[0]..C[3], also just
+ aliases for X, Y, Z and W)
+
+ }
+ TVector4 = record
+
+ private
+ method GetComponent(const AIndex: Integer): Single;
+ method SetComponent(const AIndex: Integer; const Value: Single);
+ method GetLength: Single;
+ method SetLength(const AValue: Single);
+ method GetLengthSquared: Single;
+ method SetLengthSquared(const AValue: Single);
+
+ public
+ { Sets the four elements (X, Y, Z and W) to 0. }
+ {$HIDE W8}
+ method Init;
+ {$SHOW W8}
+
+ { Sets the four elements (X, Y, Z and W) to A.
+
+ Parameters:
+ A: the value to set the three elements to. }
+ method Init(const Value: Single);
+
+ { Sets the four elements (X, Y, Z and W) to A1, A2, A3 and A4 respectively.
+
+ Parameters:
+ A1: the value to set the first element to.
+ A2: the value to set the second element to.
+ A3: the value to set the third element to.
+ A4: the value to set the fourth element to. }
+ method Init(const A1, A2, A3, A4: Single);
+
+ { Sets the first two elements from a 2D vector, and the last two elements
+ from two scalars.
+
+ Parameters:
+ A1: the vector to use for the first two elements.
+ A2: the value to set the third element to.
+ A3: the value to set the fourth element to. }
+ method Init(const A1: TVector2; const A2, A3: Single);
+
+ { Sets the first and last elements from two scalars, and the middle two
+ elements from a 2D vector.
+
+ Parameters:
+ A1: the value to set the first element to.
+ A2: the vector to use for the second and third elements.
+ A3: the value to set the fourth element to. }
+ method Init(const A1: Single; const A2: TVector2; const A3: Single);
+
+ { Sets the first two elements from two scalars and the last two elements
+ from a 2D vector.
+
+ Parameters:
+ A1: the value to set the first element to.
+ A2: the value to set the second element to.
+ A3: the vector to use for the last two elements. }
+ method Init(const A1, A2: Single; const A3: TVector2);
+
+ { Sets the first two elements and last two elements from two 2D vectors.
+
+ Parameters:
+ A1: the vector to use for the first two elements.
+ A2: the vector to use for the last two elements. }
+ method Init(const A1, A2: TVector2);
+
+ { Sets the first three elements from a 3D vector, and the fourth element
+ from a scalar.
+
+ Parameters:
+ A1: the vector to use for the first three elements.
+ A2: the value to set the fourth element to. }
+ method Init(const A1: TVector3; const A2: Single);
+
+ { Sets the first element from a scaler, and the last three elements from a
+ 3D vector.
+
+ Parameters:
+ A1: the value to set the first element to.
+ A2: the vector to use for the last three elements. }
+ method Init(const A1: Single; const A2: TVector3);
+
+
+
+ { Checks two vectors for equality.
+
+ Returns:
+ True if the two vectors match each other exactly. }
+ class operator Equal(const A, B: TVector4): Boolean; inline;
+
+ { Checks two vectors for inequality.
+
+ Returns:
+ True if the two vectors are not equal. }
+ class operator NotEqual(const A, B: TVector4): Boolean; inline;
+
+ { Negates a vector.
+
+ Returns:
+ The negative value of a vector (eg. (-A.X, -A.Y, -A.Z, -A.W)) }
+ class operator Minus(const A: TVector4): TVector4; inline;
+
+ { Adds a scalar value to a vector.
+
+ Returns:
+ (A.X + B, A.Y + B, A.Z + B, A.W + B) }
+ class operator Add(const A: TVector4; const B: Single): TVector4; inline;
+
+ { Adds a vector to a scalar value.
+
+ Returns:
+ (A + B.X, A + B.Y, A + B.Z, A + B.W) }
+ class operator Add(const A: Single; const B: TVector4): TVector4;inline;
+
+ { Adds two vectors.
+
+ Returns:
+ (A.X + B.X, A.Y + B.Y, A.Z + B.Z, A.W + B.W) }
+ class operator Add(const A, B: TVector4): TVector4; inline;
+
+ { Subtracts a scalar value from a vector.
+
+ Returns:
+ (A.X - B, A.Y - B, A.Z - B, A.W - B) }
+ class operator Subtract(const A: TVector4; const B: Single): TVector4; inline;
+
+ { Subtracts a vector from a scalar value.
+
+ Returns:
+ (A - B.X, A - B.Y, A - B.Z, A - B.W) }
+ class operator Subtract(const A: Single; const B: TVector4): TVector4; inline;
+
+ { Subtracts two vectors.
+
+ Returns:
+ (A.X - B.X, A.Y - B.Y, A.Z - B.Z, A.W - B.W) }
+ class operator Subtract(const A, B: TVector4): TVector4; inline;
+
+ { Multiplies a vector with a scalar value.
+
+ Returns:
+ (A.X * B, A.Y * B, A.Z * B, A.W * B) }
+ class operator Multiply(const A: TVector4; const B: Single): TVector4; inline;
+
+ { Multiplies a scalar value with a vector.
+
+ Returns:
+ (A * B.X, A * B.Y, A * B.Z, A * B.W) }
+ class operator Multiply(const A: Single; const B: TVector4): TVector4; inline;
+
+ { Multiplies two vectors component-wise.
+ To calculate a dot product instead, use the Dot function.
+
+ Returns:
+ (A.X * B.X, A.Y * B.Y, A.Z * B.Z, A.W * B.W) }
+ class operator Multiply(const A, B: TVector4): TVector4; inline;
+
+ { Divides a vector by a scalar value.
+
+ Returns:
+ (A.X / B, A.Y / B, A.Z / B, A.W / B) }
+ class operator Divide(const A: TVector4; const B: Single): TVector4; inline;
+
+ { Divides a scalar value by a vector.
+
+ Returns:
+ (A / B.X, A / B.Y, A / B.Z, A / B.W) }
+ class operator Divide(const A: Single; const B: TVector4): TVector4; inline;
+
+ { Divides two vectors component-wise.
+
+ Returns:
+ (A.X / B.X, A.Y / B.Y, A.Z / B.Z, A.W / B.W) }
+ class operator Divide(const A, B: TVector4): TVector4; inline;
+
+ { Whether this vector equals another vector, within a certain tolerance.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if both vectors are equal (within the given tolerance). }
+ method Equals(const AOther: TVector4; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Calculates the distance between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The distance between this vector and AOther.
+
+ @bold(Note): If you only want to compare distances, you should use
+ DistanceSquared instead, which is faster. }
+ method Distance(const AOther: TVector4): Single;
+
+ { Calculates the squared distance between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The squared distance between this vector and AOther. }
+ method DistanceSquared(const AOther: TVector4): Single;
+
+ { Calculates the dot product between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The dot product between this vector and AOther. }
+ method Dot(const AOther: TVector4): Single;
+
+ { Offsets this vector in a certain direction.
+
+ Parameters:
+ ADeltaX: the delta X direction.
+ ADeltaY: the delta Y direction.
+ ADeltaZ: the delta Z direction.
+ ADeltaW: the delta W direction. }
+ method Offset(const ADeltaX, ADeltaY, ADeltaZ, ADeltaW: Single);
+
+ { Offsets this vector in a certain direction.
+
+ Parameters:
+ ADelta: the delta.
+
+ @bold(Note): this is equivalent to adding two vectors together. }
+ method Offset(const ADelta: TVector4);
+
+
+ { Calculates a normalized version of this vector.
+
+ Returns:
+ The normalized version of of this vector. That is, a vector in the same
+ direction as A, but with a length of 1.
+
+ @bold(Note): for a faster, less accurate version, use NormalizeFast.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetNormalized. }
+ method Normalize: TVector4;
+
+ { Normalizes this vector. This is, keep the current direction, but set the
+ length to 1.
+
+ @bold(Note): The SIMD optimized versions of this method use an
+ approximation, resulting in a very small error.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ normalized version instead, then use Normalize. }
+ method SetNormalized;
+
+ { Calculates a normalized version of this vector.
+
+ Returns:
+ The normalized version of of this vector. That is, a vector in the same
+ direction as A, but with a length of 1.
+
+ @bold(Note): this is an SIMD optimized version that uses an approximation,
+ resulting in a small error. For an accurate version, use Normalize.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetNormalizedFast. }
+ method NormalizeFast: TVector4;
+
+ { Normalizes this vector. This is, keep the current direction, but set the
+ length to 1.
+
+ @bold(Note): this is an SIMD optimized version that uses an approximation,
+ resulting in a small error. For an accurate version, use SetNormalized.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ normalized version instead, then use NormalizeFast. }
+ method SetNormalizedFast;
+
+ { Calculates a vector pointing in the same direction as this vector.
+
+ Parameters:
+ I: the incident vector.
+ NRef: the reference vector.
+
+ Returns:
+ A vector that points away from the surface as defined by its normal. If
+ NRef.Dot(I) < 0 then it returns this vector, otherwise it returns the
+ negative of this vector. }
+ method FaceForward(const I, NRef: TVector4): TVector4;
+
+ { Calculates the reflection direction for this (incident) vector.
+
+ Parameters:
+ N: the normal vector. Should be normalized in order to achieve the desired
+ result.
+
+ Returns:
+ The reflection direction calculated as Self - 2 * N.Dot(Self) * N. }
+ method Reflect(const N: TVector4): TVector4;
+
+ { Calculates the refraction direction for this (incident) vector.
+
+ Parameters:
+ N: the normal vector. Should be normalized in order to achieve the
+ desired result.
+ Eta: the ratio of indices of refraction.
+
+ Returns:
+ The refraction vector.
+
+ @bold(Note): This vector should be normalized in order to achieve the
+ desired result.}
+ method Refract(const N: TVector4; const Eta: Single): TVector4;
+
+ { Creates a vector with the same direction as this vector, but with the
+ length limited, based on the desired maximum length.
+
+ Parameters:
+ AMaxLength: The desired maximum length of the vector.
+
+ Returns:
+ A length-limited version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLimit. }
+ method Limit(const AMaxLength: Single): TVector4;
+
+ { Limits the length of this vector, based on the desired maximum length.
+
+ Parameters:
+ AMaxLength: The desired maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-limited version instead, then use Limit. }
+ method SetLimit(const AMaxLength: Single);
+
+ { Creates a vector with the same direction as this vector, but with the
+ length limited, based on the desired squared maximum length.
+
+ Parameters:
+ AMaxLengthSquared: The desired squared maximum length of the vector.
+
+ Returns:
+ A length-limited version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLimitSquared. }
+ method LimitSquared(const AMaxLengthSquared: Single): TVector4;
+
+ { Limits the length of this vector, based on the desired squared maximum
+ length.
+
+ Parameters:
+ AMaxLengthSquared: The desired squared maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-limited version instead, then use LimitSquared. }
+ method SetLimitSquared(const AMaxLengthSquared: Single);
+
+ { Creates a vector with the same direction as this vector, but with the
+ length clamped between a minimim and maximum length.
+
+ Parameters:
+ AMinLength: The minimum length of the vector.
+ AMaxLength: The maximum length of the vector.
+
+ Returns:
+ A length-clamped version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetClamped. }
+ method Clamp(const AMinLength, AMaxLength: Single): TVector4;
+
+ { Clamps the length of this vector between a minimim and maximum length.
+
+ Parameters:
+ AMinLength: The minimum length of this vector.
+ AMaxLength: The maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-clamped version instead, then use Clamp. }
+ method SetClamped(const AMinLength, AMaxLength: Single);
+
+ { Linearly interpolates between this vector and a target vector.
+
+ Parameters:
+ ATarget: the target vector.
+ AAlpha: the interpolation coefficient (between 0.0 and 1.0).
+
+ Returns:
+ The interpolation result vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLerp. }
+ method Lerp(const ATarget: TVector4; const AAlpha: Single): TVector4;
+
+ { Linearly interpolates between this vector and a target vector and stores
+ the result in this vector.
+
+ Parameters:
+ ATarget: the target vector.
+ AAlpha: the interpolation coefficient (between 0.0 and 1.0).
+
+ @bold(Note): If you do not want to change this vector, but get an
+ interpolated version instead, then use Lerp. }
+ method SetLerp(const ATarget: TVector4; const AAlpha: Single);
+
+ { Whether the vector is normalized (within a small margin of error).
+
+ Returns:
+ True if the length of the vector is (very close to) 1.0 }
+ method IsNormalized: Boolean;
+
+ { Whether the vector is normalized within a given margin of error.
+
+ Parameters:
+ AErrorMargin: the allowed margin of error.
+
+ Returns:
+ True if the squared length of the vector is 1.0 within the margin of
+ error. }
+ method IsNormalized(const AErrorMargin: Single): Boolean;
+
+ { Whether this is a zero vector.
+
+ Returns:
+ True if X, Y, Z and W are exactly 0.0 }
+ method IsZero: Boolean;
+
+ { Whether this is a zero vector within a given margin of error.
+
+ Parameters:
+ AErrorMargin: the allowed margin of error.
+
+ Returns:
+ True if the squared length is smaller then the margin of error. }
+ method IsZero(const AErrorMargin: Single): Boolean;
+
+ { Whether this vector has a similar direction compared to another vector.
+ The test is performed in 3 dimensions (that is, the W-component is ignored).
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ True if the normalized dot product (ignoring the W-component) is greater
+ than 0. }
+ method HasSameDirection(const AOther: TVector4): Boolean;
+
+ { Whether this vector has an opposite direction compared to another vector.
+ The test is performed in 3 dimensions (that is, the W-component is ignored).
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ True if the normalized dot product (ignoring the W-component) is less
+ than 0. }
+ method HasOppositeDirection(const AOther: TVector4): Boolean;
+
+ { Whether this vector runs parallel to another vector (either in the same
+ or the opposite direction). The test is performed in 3 dimensions (that
+ is, the W-component is ignored).
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector runs parallel to AOther (within the given tolerance
+ and ignoring the W-component) }
+ method IsParallel(const AOther: TVector4; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is collinear with another vector. Two vectors are
+ collinear if they run parallel to each other and point in the same
+ direction. The test is performed in 3 dimensions (that is, the W-component
+ is ignored).
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is collinear to AOther (within the given tolerance
+ and ignoring the W-component) }
+ method IsCollinear(const AOther: TVector4; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is opposite collinear with another vector. Two vectors
+ are opposite collinear if they run parallel to each other and point in
+ opposite directions. The test is performed in 3 dimensions (that is, the
+ W-component is ignored).
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is opposite collinear to AOther (within the given
+ tolerance and ignoring the W-component) }
+ method IsCollinearOpposite(const AOther: TVector4; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is perpendicular to another vector. The test is
+ performed in 3 dimensions (that is, the W-component is ignored).
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is perpendicular to AOther. That is, if the dot
+ product is 0 (within the given tolerance and ignoring the W-component) }
+ method IsPerpendicular(const AOther: TVector4; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Returns the components of the vector.
+ This is identical to accessing the C-field, but this property can be used
+ as a default array property.
+
+ Parameters:
+ AIndex: index of the component to return (0-3). Range is checked
+ with an assertion. }
+ property Components[const AIndex: Integer]: Single read GetComponent write SetComponent; default;
+
+ { The euclidean length of this vector.
+
+ @bold(Note): If you only want to compare lengths of vectors, you should
+ use LengthSquared instead, which is faster.
+
+ @bold(Note): You can also set the length of the vector. In that case, it
+ will keep the current direction. }
+ property Length: Single read GetLength write SetLength;
+
+ { The squared length of the vector.
+
+ @bold(Note): This property is faster than Length because it avoids
+ calculating a square root. It is useful for comparing lengths instead of
+ calculating actual lengths.
+
+ @bold(Note): You can also set the squared length of the vector. In that
+ case, it will keep the current direction. }
+ property LengthSquared: Single read GetLengthSquared write SetLengthSquared;
+ public
+ X, Y, Z, W: Single;
+
+ property R : Single read X write X;
+ property G : Single read Y write Y;
+ property B : Single read Z write Z;
+ property A : Single read W write W;
+
+ property S : Single read X write X;
+ property T : Single read Y write Y;
+ property P : Single read Z write Z;
+ property Q : Single read W write W;
+
+
+
+// case Byte of
+// { X, Y, Z and W components of the vector. Aliases for C[0], C[1], C[2]
+// and C[3]. }
+// 0: (X, Y, Z, W: Single);
+//
+// { Red, Green, Blue and Alpha components of the vector. Aliases for C[0],
+// C[1], C[2] and C[3]. }
+// 1: (R, G, B, A: Single);
+//
+// { S, T, P and Q components of the vector. Aliases for C[0], C[1], C[2] and
+// C[3]. }
+// 2: (S, T, P, Q: Single);
+//
+// { The four components of the vector. }
+// 3: (C: array [0..3] of Single);
+ end;
+
+implementation
+
+{ TVector4 }
+
+class operator TVector4.Add(const A: TVector4; const B: Single): TVector4;
+begin
+ Result.X := A.X + B;
+ Result.Y := A.Y + B;
+ Result.Z := A.Z + B;
+ Result.W := A.W + B;
+end;
+
+class operator TVector4.Add(const A: Single; const B: TVector4): TVector4;
+begin
+ Result.X := A + B.X;
+ Result.Y := A + B.Y;
+ Result.Z := A + B.Z;
+ Result.W := A + B.W;
+end;
+
+class operator TVector4.Add(const A, B: TVector4): TVector4;
+begin
+ Result.X := A.X + B.X;
+ Result.Y := A.Y + B.Y;
+ Result.Z := A.Z + B.Z;
+ Result.W := A.W + B.W;
+end;
+
+method TVector4.Distance(const AOther: TVector4): Single;
+begin
+ Result := (Self - AOther).Length;
+end;
+
+method TVector4.DistanceSquared(const AOther: TVector4): Single;
+begin
+ Result := (Self - AOther).LengthSquared;
+end;
+
+class operator TVector4.Divide(const A: TVector4; const B: Single): TVector4;
+begin
+ Result.X := A.X / B;
+ Result.Y := A.Y / B;
+ Result.Z := A.Z / B;
+ Result.W := A.W / B;
+end;
+
+class operator TVector4.Divide(const A: Single; const B: TVector4): TVector4;
+begin
+ Result.X := A / B.X;
+ Result.Y := A / B.Y;
+ Result.Z := A / B.Z;
+ Result.W := A / B.W;
+end;
+
+class operator TVector4.Divide(const A, B: TVector4): TVector4;
+begin
+ Result.X := A.X / B.X;
+ Result.Y := A.Y / B.Y;
+ Result.Z := A.Z / B.Z;
+ Result.W := A.W / B.W;
+end;
+
+method TVector4.Dot(const AOther: TVector4): Single;
+begin
+ Result := (X * AOther.X) + (Y * AOther.Y) + (Z * AOther.Z) + (W * AOther.W);
+end;
+
+method TVector4.FaceForward(const I, NRef: TVector4): TVector4;
+begin
+ if (NRef.Dot(I) < 0) then
+ Result := Self
+ else
+ Result := -Self;
+end;
+
+method TVector4.GetLength: Single;
+begin
+ Result := sqrt((X * X) + (Y * Y) + (Z * Z) + (W * W));
+end;
+
+method TVector4.GetLengthSquared: Single;
+begin
+ Result := (X * X) + (Y * Y) + (Z * Z) + (W * W);
+end;
+
+class operator TVector4.Multiply(const A: TVector4; const B: Single): TVector4;
+begin
+ Result.X := A.X * B;
+ Result.Y := A.Y * B;
+ Result.Z := A.Z * B;
+ Result.W := A.W * B;
+end;
+
+class operator TVector4.Multiply(const A: Single; const B: TVector4): TVector4;
+begin
+ Result.X := A * B.X;
+ Result.Y := A * B.Y;
+ Result.Z := A * B.Z;
+ Result.W := A * B.W;
+end;
+
+class operator TVector4.Multiply(const A, B: TVector4): TVector4;
+begin
+ Result.X := A.X * B.X;
+ Result.Y := A.Y * B.Y;
+ Result.Z := A.Z * B.Z;
+ Result.W := A.W * B.W;
+end;
+
+class operator TVector4.Minus(const A: TVector4): TVector4;
+begin
+ Result.X := -A.X;
+ Result.Y := -A.Y;
+ Result.Z := -A.Z;
+ Result.W := -A.W;
+end;
+
+method TVector4.NormalizeFast: TVector4;
+begin
+ Result := Self * MetalMath.InverseSqrt(Self.LengthSquared);
+end;
+
+method TVector4.Reflect(const N: TVector4): TVector4;
+begin
+ Result := Self - ((2 * N.Dot(Self)) * N);
+end;
+
+method TVector4.Refract(const N: TVector4; const Eta: Single): TVector4;
+var
+ D, K: Single;
+begin
+ D := N.Dot(Self);
+ K := 1 - Eta * Eta * (1 - D * D);
+ if (K < 0) then
+ Result.Init
+ else
+ Result := (Eta * Self) - ((Eta * D + sqrt(K)) * N);
+end;
+
+method TVector4.SetNormalizedFast;
+begin
+ Self := Self * MetalMath.InverseSqrt(Self.LengthSquared);
+end;
+
+class operator TVector4.Subtract(const A: TVector4; const B: Single): TVector4;
+begin
+ Result.X := A.X - B;
+ Result.Y := A.Y - B;
+ Result.Z := A.Z - B;
+ Result.W := A.W - B;
+end;
+
+class operator TVector4.Subtract(const A: Single; const B: TVector4): TVector4;
+begin
+ Result.X := A - B.X;
+ Result.Y := A - B.Y;
+ Result.Z := A - B.Z;
+ Result.W := A - B.W;
+end;
+
+class operator TVector4.Subtract(const A, B: TVector4): TVector4;
+begin
+ Result.X := A.X - B.X;
+ Result.Y := A.Y - B.Y;
+ Result.Z := A.Z - B.Z;
+ Result.W := A.W - B.W;
+end;
+
+{ TVector4 }
+
+method TVector4.Clamp(const AMinLength, AMaxLength: Single): TVector4;
+var
+ LenSq, EdgeSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq = 0) then
+ Exit(Self);
+
+ EdgeSq := AMaxLength * AMaxLength;
+ if (LenSq > EdgeSq) then
+ Exit(Self * sqrt(EdgeSq / LenSq));
+
+ EdgeSq := AMinLength * AMinLength;
+ if (LenSq < EdgeSq) then
+ Exit(Self * sqrt(EdgeSq / LenSq));
+
+ Result := Self;
+end;
+
+class operator TVector4.Equal(const A, B: TVector4): Boolean;
+begin
+ Result := (A.X = B.X) and (A.Y = B.Y) and (A.Z = B.Z) and (A.W = B.W);
+end;
+
+method TVector4.Equals(const AOther: TVector4; const ATolerance: Single): Boolean;
+begin
+ Result := (Abs(X - AOther.X) <= ATolerance)
+ and (Abs(Y - AOther.Y) <= ATolerance)
+ and (Abs(Z - AOther.Z) <= ATolerance)
+ and (Abs(W - AOther.W) <= ATolerance);
+end;
+
+method TVector4.GetComponent(const AIndex: Integer): Single;
+begin
+
+ case AIndex of
+ 0 : result := X;
+ 1 : result := Y;
+ 2 : result := Z;
+ 3 : result := W;
+ end;
+ // Result := C[AIndex];
+end;
+
+method TVector4.HasSameDirection(const AOther: TVector4): Boolean;
+begin
+ Result := (((X * AOther.X) + (Y * AOther.Y) + (Z * AOther.Z)) > 0);
+end;
+
+method TVector4.HasOppositeDirection(const AOther: TVector4): Boolean;
+begin
+ Result := (((X * AOther.X) + (Y * AOther.Y) + (Z * AOther.Z)) < 0);
+end;
+
+
+method TVector4.Init(const A1, A2, A3, A4: Single);
+begin
+ X := A1;
+ Y := A2;
+ Z := A3;
+ W := A4;
+end;
+
+method TVector4.Init(const Value: Single);
+begin
+ X := Value;
+ Y := Value;
+ Z := Value;
+ W := Value;
+end;
+
+method TVector4.Init;
+begin
+ X := 0;
+ Y := 0;
+ Z := 0;
+ W := 0;
+end;
+
+method TVector4.Init(const A1: TVector2; const A2, A3: Single);
+begin
+ X := A1.X;
+ Y := A1.Y;
+ Z := A2;
+ W := A3;
+end;
+
+method TVector4.Init(const A1, A2: TVector2);
+begin
+ X := A1.X;
+ Y := A1.Y;
+ Z := A2.X;
+ W := A2.Y;
+end;
+
+method TVector4.Init(const A1, A2: Single; const A3: TVector2);
+begin
+ X := A1;
+ Y := A2;
+ Z := A3.X;
+ W := A3.Y;
+end;
+
+method TVector4.Init(const A1: Single; const A2: TVector2; const A3: Single);
+begin
+ X := A1;
+ Y := A2.X;
+ Z := A2.Y;
+ W := A3;
+end;
+
+method TVector4.Init(const A1: TVector3; const A2: Single);
+begin
+ X := A1.X;
+ Y := A1.Y;
+ Z := A1.Z;
+ W := A2;
+end;
+
+method TVector4.Init(const A1: Single; const A2: TVector3);
+begin
+ X := A1;
+ Y := A2.X;
+ Z := A2.Y;
+ W := A2.Z;
+end;
+
+method TVector4.IsCollinear(const AOther: TVector4; const ATolerance: Single): Boolean;
+begin
+ Result := IsParallel(AOther, ATolerance) and (HasSameDirection(AOther));
+end;
+
+method TVector4.IsCollinearOpposite(const AOther: TVector4; const ATolerance: Single): Boolean;
+begin
+ Result := IsParallel(AOther, ATolerance) and (HasOppositeDirection(AOther));
+end;
+
+method TVector4.IsNormalized: Boolean;
+begin
+ Result := IsNormalized(0.000000001);
+end;
+
+method TVector4.IsNormalized(const AErrorMargin: Single): Boolean;
+begin
+ Result := (Abs(LengthSquared - 1.0) < AErrorMargin);
+end;
+
+method TVector4.IsParallel(const AOther: TVector4; const ATolerance: Single): Boolean;
+begin
+ Result := (TVector3.Vector3(Y * AOther.Z - Z * AOther.Y,
+ Z * AOther.X - X * AOther.Z,
+ X * AOther.Y - Y * AOther.X).LengthSquared <= ATolerance);
+end;
+
+method TVector4.IsPerpendicular(const AOther: TVector4; const ATolerance: Single): Boolean;
+begin
+ Result := (Abs((X * AOther.X) + (Y * AOther.Y) + (Z * AOther.Z)) <= ATolerance);
+end;
+
+method TVector4.IsZero: Boolean;
+begin
+ Result := (X = 0) and (Y = 0) and (Z = 0) and (W = 0);
+end;
+
+method TVector4.IsZero(const AErrorMargin: Single): Boolean;
+begin
+ Result := (LengthSquared < AErrorMargin);
+end;
+
+method TVector4.Lerp(const ATarget: TVector4; const AAlpha: Single): TVector4;
+begin
+ Result := MetalMath.Mix(Self, ATarget, AAlpha);
+end;
+
+method TVector4.Limit(const AMaxLength: Single): TVector4;
+begin
+ Result := LimitSquared(AMaxLength * AMaxLength);
+end;
+
+method TVector4.LimitSquared(const AMaxLengthSquared: Single): TVector4;
+var
+ LenSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq > AMaxLengthSquared) then
+ Result := Self * sqrt(AMaxLengthSquared / LenSq)
+ else
+ Result := Self;
+end;
+
+method TVector4.Normalize: TVector4;
+begin
+ Result := Self / Length;
+end;
+
+class operator TVector4.NotEqual(const A, B: TVector4): Boolean;
+begin
+ Result := (A.X <> B.X) or (A.Y <> B.Y) or (A.Z <> B.Z) or (A.W <> B.W);
+end;
+
+method TVector4.Offset(const ADeltaX, ADeltaY, ADeltaZ, ADeltaW: Single);
+begin
+ X := X + ADeltaX;
+ Y := Y + ADeltaY;
+ Z := Z + ADeltaZ;
+ W := W + ADeltaW;
+end;
+
+method TVector4.Offset(const ADelta: TVector4);
+begin
+ Self := Self + ADelta;
+end;
+
+method TVector4.SetClamped(const AMinLength, AMaxLength: Single);
+begin
+ Self := Clamp(AMinLength, AMaxLength);
+end;
+
+method TVector4.SetComponent(const AIndex: Integer; const Value: Single);
+begin
+
+ case AIndex of
+ 0 : X := Value;
+ 1 : Y := Value;
+ 2 : Z := Value;
+ 3 : W := Value;
+ end;
+// C[AIndex] := Value;
+end;
+
+method TVector4.SetLength(const AValue: Single);
+begin
+ setLengthSquared(AValue * AValue);
+end;
+
+method TVector4.SetLengthSquared(const AValue: Single);
+var
+ LenSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq <> 0) and (LenSq <> AValue) then
+ Self := Self * sqrt(AValue / LenSq);
+end;
+
+method TVector4.SetLerp(const ATarget: TVector4; const AAlpha: Single);
+begin
+ Self := MetalMath.Mix(Self, ATarget, AAlpha);
+end;
+
+method TVector4.SetLimit(const AMaxLength: Single);
+begin
+ Self := LimitSquared(AMaxLength * AMaxLength);
+end;
+
+method TVector4.SetLimitSquared(const AMaxLengthSquared: Single);
+begin
+ Self := LimitSquared(AMaxLengthSquared);
+end;
+
+method TVector4.SetNormalized;
+begin
+ Self := Self / Length;
+end;
+
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVertexArrays.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVertexArrays.pas
new file mode 100644
index 0000000..d1a970c
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVertexArrays.pas
@@ -0,0 +1,48 @@
+namespace MetalExample;
+{$GLOBALS ON}
+interface
+uses
+ Foundation;
+
+type
+ VertexArray = class
+ private
+ data : List;
+ indicies: array of UInt16;
+ public
+ constructor (const adata : array of Single; const valueCount : Integer; const aIndicies : array of UInt16);
+ method getArray : array of Vertex3d;
+ property Count : Integer read indicies.length;
+ end;
+
+
+implementation
+
+constructor VertexArray(const adata: array of Single; const valueCount: integer; const aIndicies : array of Uint16);
+begin
+ inherited constructor ();
+ data := new List;
+ var temp := new Single[valueCount];
+ var i : Integer := 0;
+ while i <= (adata.length-valueCount) do
+ begin
+ for j : Integer := 0 to valueCount-1 do
+ temp[j] := adata[i+j];
+ var v : Vertex3d := Vertex3d.fromBuffer(temp);
+ v.color := Color.createGreen;
+ data.Add(v);
+ inc(i, valueCount);
+ end;
+ indicies := aIndicies;
+ NSLog("Count of Verticies3d, %d", data.Count);
+end;
+
+method VertexArray.getArray: array of Vertex3d;
+begin
+ result := new Vertex3d[indicies.length];
+ for i : Integer := 0 to indicies.length - 1 do
+ result[i] := data[indicies[i]];
+
+end;
+
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalVertexBuffer.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVertexBuffer.pas
similarity index 64%
rename from Oxygene/Toffee/OS X/Metal/MetalExample/MetalVertexBuffer.pas
rename to Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVertexBuffer.pas
index 5647e12..a8513c9 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalVertexBuffer.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVertexBuffer.pas
@@ -8,12 +8,16 @@ interface
type
VertexBuffer = class
private
+ method getCount: Integer;
_buffer : MTLBuffer;
+ _count : Integer;
public
class method newBuffer(_device : MTLDevice; const _src : ^Void; const _bufflen : Integer) : VertexBuffer;
class method newBuffer(_device : MTLDevice) SourceData(_src :^Void) withLength(_bufflen : Integer) : VertexBuffer;
+ class method newBuffer(_device : MTLDevice) SoureArray(_src : VertexArray) : VertexBuffer;
property verticies : MTLBuffer read _buffer;
+ property Count : Integer read getCount;
end;
@@ -41,7 +45,21 @@ implementation
memcpy(result._buffer.contents, _src, _bufflen);
end;
+class method VertexBuffer.newBuffer(_device: MTLDevice) SoureArray(_src: VertexArray): VertexBuffer;
+//var buff : Array of Vertex3d;
+begin
+ result := new VertexBuffer();
+ var vsize := sizeOf(Vertex3d);
+ var buff := _src.getArray;
+ var _bufflen := vsize * buff.length;
+ result._count := buff.length;
+ result._buffer := _device.newBufferWithLength(_bufflen) options(MTLResourceOptions.StorageModeShared);
+ memcpy(result._buffer.contents, @buff[0], _bufflen);
+end;
-
+method VertexBuffer.getCount: Integer;
+begin
+ exit _count;
+end;
end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/Metalvec2.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/Metalvec2.pas
new file mode 100644
index 0000000..b64c38c
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/Metalvec2.pas
@@ -0,0 +1,992 @@
+namespace MetalExample;
+
+interface
+
+type
+ { A 2-dimensional vector. Can be used to represent points or vectors in
+ 2D space, using the X and Y fields. You can also use it to represent
+ texture coordinates, by using S and T (these are just aliases for X and Y).
+ It can also be used to group 2 arbitrary values together in an array (using
+ C[0] and C[1], which are also just aliases for X and Y).
+
+ }
+
+ TVector2 = record
+ private
+ method GetComponent(const AIndex: Integer): Single;
+ method SetComponent(const AIndex: Integer; const Value: Single);
+ method GetLength: Single;
+ method SetLength(const AValue: Single);
+ method GetLengthSquared: Single;
+ method SetLengthSquared(const AValue: Single);
+ method GetAngle: Single;
+ method SetAngle(const AValue: Single);
+
+ public
+ { Sets the two elements (X and Y) to 0. }
+ {$HIDE W8}
+ method Init;
+ {$SHOW W8}
+
+ { Sets the two elements (X and Y) to A.
+
+ Parameters:
+ A: the value to set the two elements to. }
+ method Init(const A: Single);
+
+ { Sets the two elements (X and Y) to A1 and A2 respectively.
+
+ Parameters:
+ A1: the value to set the first element to.
+ A2: the value to set the second element to. }
+ method Init(const A1, A2: Single);
+
+
+ { Checks two vectors for equality.
+
+ Returns:
+ True if the two vectors match each other exactly. }
+ class operator &Equal(const A, B: TVector2): Boolean; //inline;
+
+ { Checks two vectors for inequality.
+
+ Returns:
+ True if the two vectors are not equal. }
+ class operator NotEqual(const A, B: TVector2): Boolean; inline;
+
+ { Negates a vector.
+
+ Returns:
+ The negative value of a vector (eg. (-A.X, -A.Y)) }
+ class operator Minus(const A: TVector2): TVector2; {$IF FM_INLINE }inline;{$ENDIF}
+
+ { Adds a scalar value to a vector.
+
+ Returns:
+ (A.X + B, A.Y + B) }
+ class operator Add(const A: TVector2; const B: Single): TVector2; inline;
+
+ { Adds a vector to a scalar value.
+
+ Returns:
+ (A + B.X, A + B.Y) }
+ class operator Add(const A: Single; const B: TVector2): TVector2; inline;
+
+ { Adds two vectors.
+
+ Returns:
+ (A.X + B.X, A.Y + B.Y) }
+ class operator Add(const A, B: TVector2): TVector2; {$IF FM_INLINE }inline;{$ENDIF}
+
+ { Subtracts a scalar value from a vector.
+
+ Returns:
+ (A.X - B, A.Y - B) }
+ class operator Subtract(const A: TVector2; const B: Single): TVector2; inline;
+
+ { Subtracts a vector from a scalar value.
+
+ Returns:
+ (A - B.X, A - B.Y) }
+ class operator Subtract(const A: Single; const B: TVector2): TVector2; inline;
+
+ { Subtracts two vectors.
+
+ Returns:
+ (A.X - B.X, A.Y - B.Y) }
+ class operator Subtract(const A, B: TVector2): TVector2; inline;
+
+ { Multiplies a vector with a scalar value.
+
+ Returns:
+ (A.X * B, A.Y * B) }
+ class operator Multiply(const A: TVector2; const B: Single): TVector2; inline;
+
+ { Multiplies a scalar value with a vector.
+
+ Returns:
+ (A * B.X, A * B.Y) }
+ class operator Multiply(const A: Single; const B: TVector2): TVector2; inline;
+
+ { Multiplies two vectors component-wise.
+ To calculate a dot or cross product instead, use the Dot or Cross function.
+
+ Returns:
+ (A.X * B.X, A.Y * B.Y) }
+ class operator Multiply(const A, B: TVector2): TVector2; inline;
+
+ { Divides a vector by a scalar value.
+
+ Returns:
+ (A.X / B, A.Y / B) }
+ class operator Divide(const A: TVector2; const B: Single): TVector2; inline;
+
+ { Divides a scalar value by a vector.
+
+ Returns:
+ (A / B.X, A / B.Y) }
+ class operator Divide(const A: Single; const B: TVector2): TVector2;inline;
+
+ { Divides two vectors component-wise.
+
+ Returns:
+ (A.X / B.X, A.Y / B.Y) }
+ class operator Divide(const A, B: TVector2): TVector2; inline;
+
+ { Whether this vector equals another vector, within a certain tolerance.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if both vectors are equal (within the given tolerance). }
+ method Equals(const AOther: TVector2; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Calculates the distance between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The distance between this vector and AOther.
+
+ @bold(Note): If you only want to compare distances, you should use
+ DistanceSquared instead, which is faster. }
+ method Distance(const AOther: TVector2): Single;
+
+ { Calculates the squared distance between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The squared distance between this vector and AOther. }
+ method DistanceSquared(const AOther: TVector2): Single;
+
+ { Calculates the dot product between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The dot product between this vector and AOther. }
+ method Dot(const AOther: TVector2): Single; inline;
+
+ { Calculates the cross product between this vector and another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ The cross product between this vector and AOther. }
+ method Cross(const AOther: TVector2): Single; inline;
+
+ { Offsets this vector in a certain direction.
+
+ Parameters:
+ ADeltaX: the delta X direction.
+ ADeltaY: the delta Y direction. }
+ method Offset(const ADeltaX, ADeltaY: Single);
+
+ { Offsets this vector in a certain direction.
+
+ Parameters:
+ ADelta: the delta.
+
+ @bold(Note): this is equivalent to adding two vectors together. }
+ method Offset(const ADelta: TVector2);
+
+ { Calculates a normalized version of this vector.
+
+ Returns:
+ The normalized version of of this vector. That is, a vector in the same
+ direction as A, but with a length of 1.
+
+ @bold(Note): for a faster, less accurate version, use NormalizeFast.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetNormalized. }
+ method Normalize: TVector2;
+
+ { Normalizes this vector. This is, keep the current direction, but set the
+ length to 1.
+
+ @bold(Note): The SIMD optimized versions of this method use an
+ approximation, resulting in a very small error.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ normalized version instead, then use Normalize. }
+ method SetNormalized;
+
+ { Calculates a normalized version of this vector.
+
+ Returns:
+ The normalized version of of this vector. That is, a vector in the same
+ direction as A, but with a length of 1.
+
+ @bold(Note): this is an SIMD optimized version that uses an approximation,
+ resulting in a small error. For an accurate version, use Normalize.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetNormalizedFast. }
+ method NormalizeFast: TVector2; inline;
+
+ { Normalizes this vector. This is, keep the current direction, but set the
+ length to 1.
+
+ @bold(Note): this is an SIMD optimized version that uses an approximation,
+ resulting in a small error. For an accurate version, use SetNormalized.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ normalized version instead, then use NormalizeFast. }
+ method SetNormalizedFast; inline;
+
+ { Calculates a vector pointing in the same direction as this vector.
+
+ Parameters:
+ I: the incident vector.
+ NRef: the reference vector.
+
+ Returns:
+ A vector that points away from the surface as defined by its normal. If
+ NRef.Dot(I) < 0 then it returns this vector, otherwise it returns the
+ negative of this vector. }
+ method FaceForward(const I, NRef: TVector2): TVector2;
+
+ { Calculates the reflection direction for this (incident) vector.
+
+ Parameters:
+ N: the normal vector. Should be normalized in order to achieve the desired
+ result.
+
+ Returns:
+ The reflection direction calculated as Self - 2 * N.Dot(Self) * N. }
+ method Reflect(const N: TVector2): TVector2;
+
+ { Calculates the refraction direction for this (incident) vector.
+
+ Parameters:
+ N: the normal vector. Should be normalized in order to achieve the
+ desired result.
+ Eta: the ratio of indices of refraction.
+
+ Returns:
+ The refraction vector.
+
+ @bold(Note): This vector should be normalized in order to achieve the
+ desired result.}
+ method Refract(const N: TVector2; const Eta: Single): TVector2;
+
+ { Creates a vector with the same direction as this vector, but with the
+ length limited, based on the desired maximum length.
+
+ Parameters:
+ AMaxLength: The desired maximum length of the vector.
+
+ Returns:
+ A length-limited version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLimit. }
+ method Limit(const AMaxLength: Single): TVector2;
+
+ { Limits the length of this vector, based on the desired maximum length.
+
+ Parameters:
+ AMaxLength: The desired maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-limited version instead, then use Limit. }
+ method SetLimit(const AMaxLength: Single);
+
+ { Creates a vector with the same direction as this vector, but with the
+ length limited, based on the desired squared maximum length.
+
+ Parameters:
+ AMaxLengthSquared: The desired squared maximum length of the vector.
+
+ Returns:
+ A length-limited version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLimitSquared. }
+ method LimitSquared(const AMaxLengthSquared: Single): TVector2;
+
+ { Limits the length of this vector, based on the desired squared maximum
+ length.
+
+ Parameters:
+ AMaxLengthSquared: The desired squared maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-limited version instead, then use LimitSquared. }
+ method SetLimitSquared(const AMaxLengthSquared: Single);
+
+ { Creates a vector with the same direction as this vector, but with the
+ length clamped between a minimim and maximum length.
+
+ Parameters:
+ AMinLength: The minimum length of the vector.
+ AMaxLength: The maximum length of the vector.
+
+ Returns:
+ A length-clamped version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetClamped. }
+ method Clamp(const AMinLength, AMaxLength: Single): TVector2;
+
+ { Clamps the length of this vector between a minimim and maximum length.
+
+ Parameters:
+ AMinLength: The minimum length of this vector.
+ AMaxLength: The maximum length of this vector.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ length-clamped version instead, then use Clamp. }
+ method SetClamped(const AMinLength, AMaxLength: Single);
+
+ { Creates a vector by rotating this vector counter-clockwise.
+
+ AParameters:
+ ARadians: the rotation angle in radians, counter-clockwise assuming the
+ Y-axis points up.
+
+ Returns:
+ A rotated version version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetRotated. }
+ method Rotate(const ARadians: Single): TVector2;
+
+ { Rotates this vector counter-clockwise.
+
+ AParameters:
+ ARadians: the rotation angle in radians, counter-clockwise assuming the
+ Y-axis points up.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ rotated version instead, then use Rotate. }
+ method SetRotated(const ARadians: Single);
+
+ { Creates a vector by rotating this vector 90 degrees counter-clockwise.
+
+ Returns:
+ A rotated version version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetRotated90CCW. }
+ method Rotate90CCW: TVector2;
+
+ { Rotates this vector 90 degrees counter-clockwise.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ rotated version instead, then use Rotate90CCW. }
+ method SetRotated90CCW;
+
+ { Creates a vector by rotating this vector 90 degrees clockwise.
+
+ Returns:
+ A rotated version version of this vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetRotated90CW. }
+ method Rotate90CW: TVector2;
+
+ { Rotates this vector 90 degrees clockwise.
+
+ @bold(Note): If you do not want to change this vector, but get a
+ rotated version instead, then use Rotate90CW. }
+ method SetRotated90CW;
+
+ { Calculates the angle in radians to rotate this vector/point to a target
+ vector. Angles are towards the positive Y-axis (counter-clockwise).
+
+ Parameters:
+ ATarget: the target vector.
+
+ Returns:
+ The angle in radians to the target vector, in the range -Pi to Pi. }
+ method AngleTo(const ATarget: TVector2): Single;
+
+ { Linearly interpolates between this vector and a target vector.
+
+ Parameters:
+ ATarget: the target vector.
+ AAlpha: the interpolation coefficient (between 0.0 and 1.0).
+
+ Returns:
+ The interpolation result vector.
+
+ @bold(Note): Does not change this vector. To update this vector itself,
+ use SetLerp. }
+ method Lerp(const ATarget: TVector2; const AAlpha: Single): TVector2;
+
+ { Linearly interpolates between this vector and a target vector and stores
+ the result in this vector.
+
+ Parameters:
+ ATarget: the target vector.
+ AAlpha: the interpolation coefficient (between 0.0 and 1.0).
+
+ @bold(Note): If you do not want to change this vector, but get an
+ interpolated version instead, then use Lerp. }
+ method SetLerp(const ATarget: TVector2; const AAlpha: Single);
+
+ { Whether the vector is normalized (within a small margin of error).
+
+ Returns:
+ True if the length of the vector is (very close to) 1.0 }
+ method IsNormalized: Boolean;
+
+ { Whether the vector is normalized within a given margin of error.
+
+ Parameters:
+ AErrorMargin: the allowed margin of error.
+
+ Returns:
+ True if the squared length of the vector is 1.0 within the margin of
+ error. }
+ method IsNormalized(const AErrorMargin: Single): Boolean;
+
+ { Whether this is a zero vector.
+
+ Returns:
+ True if X and Y are exactly 0.0 }
+ method IsZero: Boolean;
+
+ { Whether this is a zero vector within a given margin of error.
+
+ Parameters:
+ AErrorMargin: the allowed margin of error.
+
+ Returns:
+ True if the squared length is smaller then the margin of error. }
+ method IsZero(const AErrorMargin: Single): Boolean;
+
+ { Whether this vector has a similar direction compared to another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ True if the normalized dot product is greater than 0. }
+ method HasSameDirection(const AOther: TVector2): Boolean;
+
+ { Whether this vector has an opposite direction compared to another vector.
+
+ Parameters:
+ AOther: the other vector.
+
+ Returns:
+ True if the normalized dot product is less than 0. }
+ method HasOppositeDirection(const AOther: TVector2): Boolean;
+
+ { Whether this vector runs parallel to another vector (either in the same
+ or the opposite direction).
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector runs parallel to AOther (within the given tolerance)
+
+ @bold(Note): every vector is considered to run parallel to a zero vector. }
+ method IsParallel(const AOther: TVector2; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is collinear with another vector. Two vectors are
+ collinear if they run parallel to each other and point in the same
+ direction.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is collinear to AOther (within the given tolerance) }
+ method IsCollinear(const AOther: TVector2; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is opposite collinear with another vector. Two vectors
+ are opposite collinear if they run parallel to each other and point in
+ opposite directions.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is opposite collinear to AOther (within the given
+ tolerance) }
+ method IsCollinearOpposite(const AOther: TVector2; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Whether this vector is perpendicular to another vector.
+
+ Parameters:
+ AOther: the other vector.
+ ATolerance: (optional) tolerance. If not specified, a small tolerance
+ is used.
+
+ Returns:
+ True if this vector is perpendicular to AOther. That is, if the dot
+ product is 0 (within the given tolerance) }
+ method IsPerpendicular(const AOther: TVector2; const ATolerance: Single := SINGLE_TOLERANCE): Boolean;
+
+ { Returns the components of the vector.
+ This is identical to accessing the C-field, but this property can be used
+ as a default array property.
+
+
+ { The euclidean length of this vector.
+
+ @bold(Note): If you only want to compare lengths of vectors, you should
+ use LengthSquared instead, which is faster.
+
+ @bold(Note): You can also set the length of the vector. In that case, it
+ will keep the current direction. }
+ property Length: Single read GetLength write SetLength;
+
+ { The squared length of the vector.
+
+ @bold(Note): This property is faster than Length because it avoids
+ calculating a square root. It is useful for comparing lengths instead of
+ calculating actual lengths.
+
+ @bold(Note): You can also set the squared length of the vector. In that
+ case, it will keep the current direction. }
+ property LengthSquared: Single read GetLengthSquared write SetLengthSquared;
+
+ { The angle in radians of this vector/point relative to the X-axis. Angles
+ are towards the positive Y-axis (counter-clockwise).
+
+ When getting the angle, the result will be between -Pi and Pi. }
+ property Angle: Single read GetAngle write SetAngle;
+ public
+ X,Y : Single;
+ property R : Single read X write X;
+ property G : Single read X write X;
+ property S : Single read X write X;
+ property T : Single read X write X;
+ { Parameters:
+ AIndex: index of the component to return (0 or 1). Range is checked
+ with an assertion. }
+ property C[const AIndex: Integer]: Single read GetComponent write SetComponent; default;
+
+
+
+// Union :
+// { X and Y components of the vector. Aliases for C[0] and C[1]. }
+// 0: (X, Y: Single);
+//
+// { Red and Green components of the vector. Aliases for C[0] and C[1]. }
+// 1: (R, G: Single);
+//
+// { S and T components of the vector. Aliases for C[0] and C[1]. }
+// 2: (S, T: Single);
+//
+// { The two components of the vector. }
+// 3: (C: array [0..1] of Single);
+ end;
+
+implementation
+
+{ TVector2 }
+
+class operator TVector2.Add(const A: TVector2; const B: Single): TVector2;
+begin
+ Result.X := A.X + B;
+ Result.Y := A.Y + B;
+end;
+
+class operator TVector2.Add(const A: Single; const B: TVector2): TVector2;
+begin
+ Result.X := A + B.X;
+ Result.Y := A + B.Y;
+end;
+
+class operator TVector2.Add(const A, B: TVector2): TVector2;
+begin
+ Result.X := A.X + B.X;
+ Result.Y := A.Y + B.Y;
+end;
+
+method TVector2.Distance(const AOther: TVector2): Single;
+begin
+ Result := (Self - AOther).Length;
+end;
+
+method TVector2.DistanceSquared(const AOther: TVector2): Single;
+begin
+ Result := (Self - AOther).LengthSquared;
+end;
+
+class operator TVector2.Divide(const A: TVector2; const B: Single): TVector2;
+var
+ InvB: Single;
+begin
+ InvB := 1 / B;
+ Result.X := A.X * InvB;
+ Result.Y := A.Y * InvB;
+end;
+
+class operator TVector2.Divide(const A: Single; const B: TVector2): TVector2;
+begin
+ Result.X := A / B.X;
+ Result.Y := A / B.Y;
+end;
+
+class operator TVector2.Divide(const A, B: TVector2): TVector2;
+begin
+ Result.X := A.X / B.X;
+ Result.Y := A.Y / B.Y;
+end;
+
+method TVector2.Dot(const AOther: TVector2): Single;
+begin
+ Result := (X * AOther.X) + (Y * AOther.Y);
+end;
+
+method TVector2.FaceForward(const I, NRef: TVector2): TVector2;
+begin
+ if (NRef.Dot(I) < 0) then
+ Result := Self
+ else
+ Result := -Self;
+end;
+
+method TVector2.GetLength: Single;
+begin
+ Result := sqrt((X * X) + (Y * Y));
+end;
+
+method TVector2.GetLengthSquared: Single;
+begin
+ Result := (X * X) + (Y * Y);
+end;
+
+class operator TVector2.Multiply(const A: TVector2; const B: Single): TVector2;
+begin
+ Result.X := A.X * B;
+ Result.Y := A.Y * B;
+end;
+
+class operator TVector2.Multiply(const A: Single; const B: TVector2): TVector2;
+begin
+ Result.X := A * B.X;
+ Result.Y := A * B.Y;
+end;
+
+class operator TVector2.Multiply(const A, B: TVector2): TVector2;
+begin
+ Result.X := A.X * B.X;
+ Result.Y := A.Y * B.Y;
+end;
+
+method TVector2.NormalizeFast: TVector2;
+begin
+ Result := Self * MetalMath.InverseSqrt(Self.LengthSquared);
+end;
+
+method TVector2.Reflect(const N: TVector2): TVector2;
+begin
+ Result := Self - ((2 * N.Dot(Self)) * N);
+end;
+
+method TVector2.Refract(const N: TVector2; const Eta: Single): TVector2;
+var
+ D, K: Single;
+begin
+ D := N.Dot(Self);
+ K := 1 - Eta * Eta * (1 - D * D);
+ if (K < 0) then
+ Result.Init
+ else
+ Result := (Eta * Self) - ((Eta * D + sqrt(K)) * N);
+end;
+
+method TVector2.SetNormalizedFast;
+begin
+ Self := Self * MetalMath.InverseSqrt(Self.LengthSquared);
+end;
+
+class operator TVector2.Subtract(const A: TVector2; const B: Single): TVector2;
+begin
+ Result.X := A.X - B;
+ Result.Y := A.Y - B;
+end;
+
+class operator TVector2.Subtract(const A: Single; const B: TVector2): TVector2;
+begin
+ Result.X := A - B.X;
+ Result.Y := A - B.Y;
+end;
+
+class operator TVector2.Subtract(const A, B: TVector2): TVector2;
+begin
+ Result.X := A.X - B.X;
+ Result.Y := A.Y - B.Y;
+end;
+
+{ TVector2 }
+
+method TVector2.AngleTo(const ATarget: TVector2): Single;
+begin
+ Result := MetalMath.ArcTan2(Cross(ATarget), Dot(ATarget));
+end;
+
+method TVector2.Clamp(const AMinLength, AMaxLength: Single): TVector2;
+var
+ LenSq, EdgeSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq = 0) then
+ Exit(Self);
+
+ EdgeSq := AMaxLength * AMaxLength;
+ if (LenSq > EdgeSq) then
+ Exit(Self * sqrt(EdgeSq / LenSq));
+
+ EdgeSq := AMinLength * AMinLength;
+ if (LenSq < EdgeSq) then
+ Exit(Self * sqrt(EdgeSq / LenSq));
+
+ Result := Self;
+end;
+
+method TVector2.Cross(const AOther: TVector2): Single;
+begin
+ Result := (X * AOther.Y) - (Y * AOther.X);
+end;
+
+class operator TVector2.Equal(const A, B: TVector2): Boolean;
+begin
+ Result := (A.X = B.X) and (A.Y = B.Y);
+end;
+
+method TVector2.Equals(const AOther: TVector2; const ATolerance: Single): Boolean;
+begin
+ Result := (Abs(X - AOther.X) <= ATolerance)
+ and (Abs(Y - AOther.Y) <= ATolerance);
+end;
+
+method TVector2.GetAngle: Single;
+begin
+ Result := MetalMath.ArcTan2(Y, X)
+end;
+
+method TVector2.GetComponent(const AIndex: Integer): Single;
+begin
+ if AIndex = 0 then exit X else exit Y;
+end;
+
+method TVector2.HasSameDirection(const AOther: TVector2): Boolean;
+begin
+ Result := (Dot(AOther) > 0);
+end;
+
+method TVector2.HasOppositeDirection(const AOther: TVector2): Boolean;
+begin
+ Result := (Dot(AOther) < 0);
+end;
+
+
+method TVector2.Init;
+begin
+ X := 0;
+ Y := 0;
+end;
+
+method TVector2.Init(const A: Single);
+begin
+ X := A;
+ Y := A;
+end;
+
+method TVector2.Init(const A1, A2: Single);
+begin
+ X := A1;
+ Y := A2;
+end;
+
+
+method TVector2.IsCollinear(const AOther: TVector2; const ATolerance: Single): Boolean;
+begin
+ Result := IsParallel(AOther, ATolerance) and (Dot(AOther) > 0);
+end;
+
+method TVector2.IsCollinearOpposite(const AOther: TVector2; const ATolerance: Single): Boolean;
+begin
+ Result := IsParallel(AOther, ATolerance) and (Dot(AOther) < 0);
+end;
+
+method TVector2.IsNormalized: Boolean;
+begin
+ Result := IsNormalized(0.000000001);
+end;
+
+method TVector2.IsNormalized(const AErrorMargin: Single): Boolean;
+begin
+ Result := (Abs(LengthSquared - 1.0) < AErrorMargin);
+end;
+
+method TVector2.IsParallel(const AOther: TVector2; const ATolerance: Single): Boolean;
+begin
+ Result := (Abs(X * AOther.Y - Y * AOther.X) <= ATolerance);
+end;
+
+method TVector2.IsPerpendicular(const AOther: TVector2; const ATolerance: Single): Boolean;
+begin
+ Result := (Abs(Dot(AOther)) <= ATolerance);
+end;
+
+method TVector2.IsZero: Boolean;
+begin
+ Result := (X = 0) and (Y = 0);
+end;
+
+method TVector2.IsZero(const AErrorMargin: Single): Boolean;
+begin
+ Result := (LengthSquared < AErrorMargin);
+end;
+
+method TVector2.Lerp(const ATarget: TVector2; const AAlpha: Single): TVector2;
+begin
+ Result := MetalMath.Mix(Self, ATarget, AAlpha);
+end;
+
+method TVector2.Limit(const AMaxLength: Single): TVector2;
+begin
+ Result := LimitSquared(AMaxLength * AMaxLength);
+end;
+
+method TVector2.LimitSquared(const AMaxLengthSquared: Single): TVector2;
+var
+ LenSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq > AMaxLengthSquared) then
+ Result := Self * sqrt(AMaxLengthSquared / LenSq)
+ else
+ Result := Self;
+end;
+
+class operator TVector2.Minus(const A: TVector2): TVector2;
+begin
+ Result.X := -A.X;
+ Result.Y := -A.Y;
+end;
+
+method TVector2.Normalize: TVector2;
+begin
+ Result := Self / Length;
+end;
+
+class operator TVector2.NotEqual(const A, B: TVector2): Boolean;
+begin
+ Result := (A.X <> B.X) or (A.Y <> B.Y);
+end;
+
+method TVector2.Offset(const ADeltaX, ADeltaY: Single);
+begin
+ X := X + ADeltaX;
+ Y := Y + ADeltaY;
+end;
+
+method TVector2.Offset(const ADelta: TVector2);
+begin
+ Self := Self + ADelta;
+end;
+
+method TVector2.Rotate(const ARadians: Single): TVector2;
+var
+ lS, lC: Single;
+begin
+ MetalMath.SinCos(ARadians, out lS, out lC);
+ Result.X := (X * lC) - (Y * lS);
+ Result.Y := (X * lS) + (Y * lC);
+end;
+
+method TVector2.Rotate90CCW: TVector2;
+begin
+ Result.X := -Y;
+ Result.Y := X;
+end;
+
+method TVector2.Rotate90CW: TVector2;
+begin
+ Result.X := Y;
+ Result.Y := -X;
+end;
+
+method TVector2.SetLerp(const ATarget: TVector2; const AAlpha: Single);
+begin
+ Self := MetalMath.Mix(Self, ATarget, AAlpha);
+end;
+
+method TVector2.SetNormalized;
+begin
+ Self := Self / Length;
+end;
+
+method TVector2.SetRotated90CCW;
+begin
+ Self := Rotate90CCW;
+end;
+
+method TVector2.SetRotated90CW;
+begin
+ Self := Rotate90CW;
+end;
+
+method TVector2.SetAngle(const AValue: Single);
+begin
+ X := Length;
+ Y := 0;
+ SetRotated(AValue);
+end;
+
+method TVector2.SetClamped(const AMinLength, AMaxLength: Single);
+begin
+ Self := Clamp(AMinLength, AMaxLength);
+end;
+
+method TVector2.SetComponent(const AIndex: Integer; const Value: Single);
+begin
+ if AIndex = 0 then X := Value else Y := Value;
+end;
+
+method TVector2.SetLength(const AValue: Single);
+begin
+ setLengthSquared(AValue * AValue);
+end;
+
+method TVector2.SetLengthSquared(const AValue: Single);
+var
+ LenSq: Single;
+begin
+ LenSq := GetLengthSquared;
+ if (LenSq <> 0) and (LenSq <> AValue) then
+ Self := Self * sqrt(AValue / LenSq);
+end;
+
+method TVector2.SetLimit(const AMaxLength: Single);
+begin
+ Self := LimitSquared(AMaxLength * AMaxLength);
+end;
+
+method TVector2.SetLimitSquared(const AMaxLengthSquared: Single);
+begin
+ Self := LimitSquared(AMaxLengthSquared);
+end;
+
+method TVector2.SetRotated(const ARadians: Single);
+begin
+ Self := Rotate(ARadians);
+end;
+
+end.
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalRenderer.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalRenderer.pas
index 7215407..9212950 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalRenderer.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalRenderer.pas
@@ -8,7 +8,13 @@ interface
// Base class for the examples MTKViewDelegates
type
- MetalBaseDelegate = class(MTKViewDelegate)
+ MetalMouseDelegate = public interface
+ method MouseMove(const mx, my : Single);
+ method DontShowCursor : Boolean;
+ property showCrosshair : Boolean;
+ end;
+
+ MetalBaseDelegate = class(MTKViewDelegate, MetalMouseDelegate)
protected //MTKViewDelegate
_device : MTLDevice;// id;
_commandQueue : MTLCommandQueue; // id;
@@ -23,6 +29,12 @@ MetalBaseDelegate = class(MTKViewDelegate)
method drawInMTKView(view: not nullable MTKView); virtual; empty;
method mtkView(view: not nullable MTKView) drawableSizeWillChange(size: CGSize); virtual; empty;
+ method MouseMove(const mx, my : Single); virtual; empty;
+ method DontShowCursor : Boolean; virtual;
+ begin
+ exit false;
+ end;
+ property showCrosshair : Boolean;
public
constructor initWithMetalKitView(const mtkView : not nullable MTKView);// : MTKViewDelegate;
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalShaderLoader.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalShaderLoader.pas
index 4a7781a..af0b22b 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/MetalShaderLoader.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/MetalShaderLoader.pas
@@ -33,6 +33,7 @@ constructor shaderLoader(const device: MTLDevice) Shadername(const shadername: S
// var defaultLibrary : MTLLibrary := _device.newDefaultLibrary;
if defaultLibrary = nil then
begin
+ NSLog("Shaderlib, error %@", SourceShader);
NSLog("Failed to load the Shaderlib, error %@", lError);
exit nil;
end
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaderTypes.h b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaderTypes.h
index 5b83edc..f275d3f 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaderTypes.h
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaderTypes.h
@@ -26,29 +26,66 @@ typedef struct
{
// Positions in pixel space
// (e.g. a value of 100 indicates 100 pixels from the center)
- vector_float2 position;
+ packed_float2 position;
// Floating-point RGBA colors
- vector_float4 color;
+ packed_float4 color;
} AAPLVertex;
typedef enum AAPLTextureIndex
- {
- AAPLTextureIndexBaseColor = 0,
- } AAPLTextureIndex;
-
- // This structure defines the layout of each vertex in the array of vertices set as an input to our
- // Metal vertex shader. Since this header is shared between our .metal shader and C code,
- // we can be sure that the layout of the vertex array in the code matches the layout that
- // our vertex shader expects
- typedef struct
- {
- // Positions in pixel space (i.e. a value of 100 indicates 100 pixels from the origin/center)
- vector_float2 position;
-
- // 2D texture coordinate
- vector_float2 textureCoordinate;
- } AAPLVertexTex;
+ {
+ AAPLTextureIndexBaseColor = 0,
+ AAPLTextureIndexBaseColor2 = 1,
+ } AAPLTextureIndex;
+
+ // This structure defines the layout of each vertex in the array of vertices set as an input to our
+ // Metal vertex shader. Since this header is shared between our .metal shader and C code,
+ // we can be sure that the layout of the vertex array in the code matches the layout that
+ // our vertex shader expects
+ typedef struct
+ {
+ // Positions in pixel space (i.e. a value of 100 indicates 100 pixels from the origin/center)
+ vector_float2 position;
+
+ // 2D texture coordinate
+ vector_float2 textureCoordinate;
+ } AAPLVertexTex;
+
+ typedef struct
+ // Vertex3d = record
+ {
+ packed_float3 position;// : array[3] of Single;
+ packed_float3 normal;// : array[3] of Single;
+ packed_float2 tex;// : array[2] of Single;
+ } vertex3d;
+
+
+
+
+
+//#ifndef METALFUNCS
+// GLSL mod func for metal
+ template ::type>::value>::type>
+ METAL_FUNC T mod(T x, T y) {
+ return x - y * floor(x/y);
+ }
+
+ METAL_FUNC float4 unpremultiply(float4 s) {
+ return float4(s.rgb/max(s.a,0.00001), s.a);
+ }
+
+ METAL_FUNC float4 premultiply(float4 s) {
+ return float4(s.rgb * s.a, s.a);
+ }
+
+
+//source over blend
+ METAL_FUNC float4 normalBlend(float4 Cb, float4 Cs) {
+ float4 dst = premultiply(Cb);
+ float4 src = premultiply(Cs);
+ return unpremultiply(src + dst * (1.0 - src.a));
+ }
+ // #endif
#endif /* AAPLShaderTypes_h */
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders1.metal b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders1.metal
index 62b3f54..bebfabf 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders1.metal
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders1.metal
@@ -1,4 +1,4 @@
-/*
+/*
See LICENSE folder for this sample’s licensing information.
Abstract:
@@ -27,18 +27,18 @@ typedef struct
// Vertex function
vertex RasterizerData
vertexShader(uint vertexID [[vertex_id]],
- constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]],
- constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]])
+ constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]],
+ constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]])
{
RasterizerData out;
-
+
// Initialize our output clip space position
out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0);
// Index into our array of positions to get the current vertex
// Our positions are specified in pixel dimensions (i.e. a value of 100 is 100 pixels from
// the origin)
- float2 pixelSpacePosition = vertices[vertexID].position.xy;
+ float2 pixelSpacePosition = vertices[vertexID].position; //.xy;
// Dereference viewportSizePointer and cast to float so we can do floating-point division
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
@@ -51,7 +51,7 @@ vertexShader(uint vertexID [[vertex_id]],
// Calculate and write x and y values to our clip-space position. In order to convert from
// positions in pixel space to positions in clip-space, we divide the pixel coordinates by
// half the size of the viewport.
- out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0);
+ out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0);
// Pass our input color straight to our output color. This value will be interpolated
// with the other color values of the vertices that make up the triangle to produce
@@ -66,5 +66,4 @@ fragment float4 fragmentColorShader(RasterizerData in [[stage_in]])
{
// We return the color we just set which will be written to our color attachment.
return in.color;
-}
-
+}
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders2.metal b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders2.metal
index 400e76f..f256a75 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders2.metal
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders2.metal
@@ -1,4 +1,4 @@
-/*
+/*
See LICENSE folder for this sample’s licensing information.
Abstract:
@@ -13,7 +13,7 @@ using namespace metal;
// Include header shared between this Metal shader code and C code executing Metal API commands
#import "AAPLShaderTypes.h"
-
+
// Vertex shader outputs and fragment shader inputs
typedef struct
{
@@ -31,8 +31,8 @@ typedef struct
// Vertex function
vertex RasterizerData
vertexShader2(uint vertexID [[ vertex_id ]],
- device AAPLVertex *vertices [[ buffer(AAPLVertexInputIndexVertices) ]],
- constant vector_uint2 *viewportSizePointer [[ buffer(AAPLVertexInputIndexViewportSize) ]])
+ device AAPLVertex *vertices [[ buffer(AAPLVertexInputIndexVertices) ]],
+ constant vector_uint2 *viewportSizePointer [[ buffer(AAPLVertexInputIndexViewportSize) ]])
{
RasterizerData out;
@@ -42,7 +42,7 @@ vertexShader2(uint vertexID [[ vertex_id ]],
// Index into our array of positions to get the current vertex
// Our positions are specified in pixel dimensions (i.e. a value of 100 is 100 pixels from
// the origin)
- float2 pixelSpacePosition = vertices[vertexID].position.xy;
+ float2 pixelSpacePosition = vertices[vertexID].position;//.xy;
// Dereference viewportSizePointer and cast to float so we can do floating-point division
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
@@ -70,5 +70,4 @@ fragment float4 fragmentColorShader2(RasterizerData in [[stage_in]])
{
// We return the color we just set which will be written to our color attachment.
return in.color;
-}
-
+}
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders4.metal b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders4.metal
index 8de2344..6f5289f 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders4.metal
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders4.metal
@@ -1,4 +1,4 @@
-/*
+/*
See LICENSE folder for this sample’s licensing information.
Abstract:
@@ -13,14 +13,13 @@ using namespace metal;
// Include header shared between this Metal shader code and C code executing Metal API commands
#import "AAPLShaderTypes.h"
-
// Vertex shader outputs and per-fragment inputs. Includes clip-space position and vertex outputs
// interpolated by rasterizer and fed to each fragment generated by clip-space primitives.
typedef struct
{
// The [[position]] attribute qualifier of this member indicates this value is the clip space
// position of the vertex wen this structure is returned from the vertex shader
- float4 clipSpacePosition [[position]];
+ float4 clipSpacePosition[[position]];
// Since this member does not have a special attribute qualifier, the rasterizer will
// interpolate its value with values of other vertices making up the triangle and
@@ -31,9 +30,9 @@ typedef struct
// Vertex Function
vertex RasterizerData
-vertexShaderTex2(uint vertexID [[ vertex_id ]],
- constant AAPLVertexTex *vertexArray [[ buffer(AAPLVertexInputIndexVertices) ]],
- constant vector_uint2 *viewportSizePointer [[ buffer(AAPLVertexInputIndexViewportSize) ]])
+vertexShaderTex2(uint vertexID[[vertex_id]],
+ constant AAPLVertexTex *vertexArray[[buffer(AAPLVertexInputIndexVertices)]],
+ constant vector_uint2 *viewportSizePointer[[buffer(AAPLVertexInputIndexViewportSize)]])
{
@@ -68,22 +67,24 @@ vertexShaderTex2(uint vertexID [[ vertex_id ]],
// interpolated with the other textureCoordinate values in the vertices that make up the
// triangle.
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
-
+
return out;
}
// Fragment function
fragment float4
-samplingShader2(RasterizerData in [[stage_in]],
- texture2d colorTexture [[ texture(AAPLTextureIndexBaseColor) ]])
+samplingShader2(RasterizerData in[[stage_in]],
+ texture2d colorTexture[[texture(AAPLTextureIndexBaseColor)]],
+ texture2d colorTexture2[[texture(AAPLTextureIndexBaseColor2)]],
+ constant float &intensity [[buffer(0)]]
+ )
{
- constexpr sampler textureSampler (mag_filter::linear,
- min_filter::linear);
+ constexpr sampler textureSampler(mag_filter::linear,
+ min_filter::linear);
// Sample the texture to obtain a color
- const half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate);
- colorSample.a = 0.7;
- // We return the color of the texture
- return float4(colorSample);
-}
-
+ float4 colorSample = (float4) colorTexture.sample(textureSampler, in.textureCoordinate);
+ float4 colorSample2 = (float4) colorTexture2.sample(textureSampler, in.textureCoordinate);
+ float4 blendedColor = normalBlend(colorSample, colorSample2);
+ return mix(colorSample, blendedColor, intensity);
+}
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders5.metal b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders5.metal
new file mode 100644
index 0000000..98b4fda
--- /dev/null
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders5.metal
@@ -0,0 +1,110 @@
+//
+// Shaders.metal
+// HelloMetal
+//
+// Created by Andriy K. on 11/12/16.
+// Copyright © 2016 razeware. All rights reserved.
+//
+
+#include
+using namespace metal;
+
+struct Light
+{
+ float3 direction;
+ float3 ambientColor;
+ float3 diffuseColor;
+ float3 specularColor;
+};
+
+constant Light light = {
+ //.direction = { 0.13, 0.72, 0.68 },
+ .direction = { 0.2, 0.2, 1.680 },
+ //.ambientColor = { 0.05, 0.05, 0.05 },
+ .ambientColor = { 0.05, 0.05, 0.05 },
+ .diffuseColor = { 0.9, 0.9, 0.9 },
+ .specularColor = { 1, 1, 1 }
+};
+
+struct Material
+{
+ float3 ambientColor;
+ float3 diffuseColor;
+ float3 specularColor;
+ float specularPower;
+};
+
+constant Material material = {
+ .ambientColor = { 0.1, 0.1, 0 },
+ .diffuseColor = { 0.9, 0.1, 0 },
+ .specularColor = { 1, 1, 1 },
+ .specularPower = 100
+};
+
+
+
+// 1
+struct VertexIn{
+ packed_float3 position;
+ packed_float3 norm;
+ packed_float4 color;
+ packed_float2 texCoord;
+};
+
+struct VertexOut{
+ float4 position [[position]];
+ float4 color;
+ float3 normal;
+ float3 eye;
+ // float2 texCoord;
+};
+
+struct Uniforms{
+ float4x4 modelMatrix;
+ float4x4 projectionMatrix;
+ float3x3 normalMatrix;
+};
+
+vertex VertexOut basic_vertex(
+ const device VertexIn* vertex_array [[ buffer(0) ]],
+ const device Uniforms& uniforms [[ buffer(1) ]],
+ unsigned int vid [[ vertex_id ]]) {
+
+ // float4x4 mv_Matrix = uniforms.modelMatrix;
+ // float4x4 proj_Matrix = uniforms.projectionMatrix;
+
+ float4x4 ProjModell = uniforms.projectionMatrix * uniforms.modelMatrix;
+
+ VertexIn VertexIn = vertex_array[vid];
+
+ VertexOut VertexOut;
+ VertexOut.position = ProjModell * float4(VertexIn.position,1);
+ VertexOut.color = VertexIn.color;
+ VertexOut.eye = -(uniforms.modelMatrix * float4(VertexIn.position,1)).xyz;
+ VertexOut.normal = (uniforms.projectionMatrix * float4(VertexIn.norm,1)).xyz;
+
+ return VertexOut;
+}
+
+// 3
+fragment float4 basic_fragment(VertexOut vert [[stage_in]]
+//constant Uniforms &uniforms [[buffer(0)]]
+) {
+ float3 ambientTerm = light.ambientColor * material.ambientColor;
+
+ float3 normal = normalize(vert.normal);
+ float diffuseIntensity = saturate(dot(normal, normalize(light.direction)));
+ // float3 diffuseTerm = light.diffuseColor * material.diffuseColor * diffuseIntensity;
+ float3 diffuseTerm = light.diffuseColor * vert.color.xyz * diffuseIntensity;
+
+ float3 specularTerm(0);
+ if (diffuseIntensity > 0)
+ {
+ float3 eyeDirection = normalize(vert.eye);
+ float3 halfway = normalize(light.direction + eyeDirection);
+ float specularFactor = pow(saturate(dot(normal, halfway)), material.specularPower);
+ specularTerm = light.specularColor * material.specularColor * specularFactor;
+ }
+
+ return float4(ambientTerm + diffuseTerm + specularTerm, 1);
+}
\ No newline at end of file
diff --git a/Oxygene/Toffee/OS X/Metal/MetalExample/glAssets.pas b/Oxygene/Toffee/OS X/Metal/MetalExample/glAssets.pas
index de48684..50fd66e 100644
--- a/Oxygene/Toffee/OS X/Metal/MetalExample/glAssets.pas
+++ b/Oxygene/Toffee/OS X/Metal/MetalExample/glAssets.pas
@@ -27,7 +27,13 @@ implementation
class method Asset.loadFile(const aFilename: String): String;
begin
- var lname := getFullname(aFilename);
+ {$IF TEST}
+ var lname : string = "";
+ {$ELSE}
+ var
+ lname := getFullname(aFilename);
+ {$ENDIF}
+
if lname.FileExists then
exit File.ReadText(lname) else exit nil;
end;