From a45f92d337184e8e2ef3de2c2e5936bad69c29a9 Mon Sep 17 00:00:00 2001 From: Westermann Date: Mon, 23 Dec 2019 10:25:50 +0100 Subject: [PATCH] fix compile errors for latest Elements --- .../Metal/MetalExample/ExampleHelpers.pas | 68 ++ .../Metal/MetalExample/ExampleMetalView.pas | 106 +- .../MainWindowController.Metal.pas | 10 +- .../MetalExample/MainWindowController.pas | 5 +- .../MetalExample/MainWindowController.xib | 16 +- .../Metal/MetalExample/MetalExample.elements | 20 +- .../OS X/Metal/MetalExample/MetalExample2.pas | 3 +- .../OS X/Metal/MetalExample/MetalExample4.pas | 45 +- .../OS X/Metal/MetalExample/MetalExample5.pas | 305 +++++ .../Metal/MetalExample/MetalExampleTypes.pas | 35 +- .../MetalExample/MetalHelper/MetalGlobals.pas | 96 ++ .../MetalExample/MetalHelper/MetalMatrix3.pas | 581 +++++++++ .../MetalExample/MetalHelper/MetalMatrix4.pas | 1085 +++++++++++++++++ .../MetalExample/MetalHelper/MetalVec3.pas | 963 +++++++++++++++ .../MetalExample/MetalHelper/MetalVec4.pas | 1027 ++++++++++++++++ .../MetalHelper/MetalVertexArrays.pas | 48 + .../{ => MetalHelper}/MetalVertexBuffer.pas | 20 +- .../MetalExample/MetalHelper/Metalvec2.pas | 992 +++++++++++++++ .../OS X/Metal/MetalExample/MetalRenderer.pas | 14 +- .../Metal/MetalExample/MetalShaderLoader.pas | 1 + .../MetalExample/Shader/AAPLShaderTypes.h | 73 +- .../MetalExample/Shader/AAPLShaders1.metal | 15 +- .../MetalExample/Shader/AAPLShaders2.metal | 13 +- .../MetalExample/Shader/AAPLShaders4.metal | 35 +- .../MetalExample/Shader/AAPLShaders5.metal | 110 ++ .../OS X/Metal/MetalExample/glAssets.pas | 8 +- 26 files changed, 5604 insertions(+), 90 deletions(-) create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/ExampleHelpers.pas create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/MetalExample5.pas create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalGlobals.pas create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalMatrix3.pas create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalMatrix4.pas create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVec3.pas create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVec4.pas create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/MetalVertexArrays.pas rename Oxygene/Toffee/OS X/Metal/MetalExample/{ => MetalHelper}/MetalVertexBuffer.pas (64%) create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/MetalHelper/Metalvec2.pas create mode 100644 Oxygene/Toffee/OS X/Metal/MetalExample/Shader/AAPLShaders5.metal 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;