├── doc
├── halo.png
├── step.png
├── Helvetica.png
├── drop_shadow.png
├── plot_halo.png
├── plot_step.png
├── slope_step.png
├── smooth_step.png
├── trapezoid.png
├── twin_peaks.png
├── pass_through.png
├── StarWarsARBanner.png
├── plot_slope_step.png
├── plot_smooth_step.png
├── plot_trapezoid.png
├── plot_twin_peaks.png
├── StarWarsMacBanner.png
├── plot_pass_through.png
└── generate_plots.py
├── SWOpeningRoll
├── ios
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── MetalView.swift
│ └── RenderCoordinator.swift
├── macos
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── SWOpeningRollmacos.entitlements
│ ├── MetalView.swift
│ └── RenderCoordinator.swift
├── SWOpeningRoll.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── SWOpeningRollios.xcscheme
│ │ └── SWOpeningRollmacos.xcscheme
├── shared
│ ├── ContentView.swift
│ ├── SWOpeningRollApp.swift
│ ├── AnimationSequencer.swift
│ ├── AnimationConstants.swift
│ ├── SDTextPlane.swift
│ └── WorldManager.swift
└── rendering
│ ├── shaders
│ ├── render_vertex_ins.h
│ ├── render_uniforms.h
│ └── default_renderer.metal
│ ├── FloatUtil.swift
│ ├── Camera.swift
│ ├── VertexDescriptorGenerator.swift
│ ├── RenderVertexIns.swift
│ ├── PerSceneRenderHelper.swift
│ └── RenderUniforms.swift
├── SWOpeningRollAR
├── SWOpeningRollAR
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Textures.xcassets
│ │ ├── Contents.json
│ │ └── texture01.textureset
│ │ │ ├── Universal.mipmapset
│ │ │ ├── texture512.png
│ │ │ └── Contents.json
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── SWOpeningRollARApp.swift
│ ├── ContentView.swift
│ ├── TouchMTKView.swift
│ ├── ShaderCameraImage.metal
│ ├── MetalView.swift
│ ├── ARCoordinator.swift
│ └── PhotoImageRenderer.swift
├── SWOpeningRollAR.xcodeproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── rendering
│ ├── Camera.swift
│ ├── shaders
│ │ ├── render_vertex_ins.h
│ │ ├── render_uniforms.h
│ │ └── default_renderer.metal
│ ├── FloatUtil.swift
│ ├── VertexDescriptorGenerator.swift
│ ├── RenderVertexIns.swift
│ ├── PerSceneRenderHelper.swift
│ └── RenderUniforms.swift
└── demo
│ ├── AnimationSequencer.swift
│ ├── AnimationConstants.swift
│ └── SDTextPlane.swift
├── SDFont
├── SDFont.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── SDFont.xcscheme
│ │ └── sdfontgen.xcscheme
├── SDFont
│ ├── SDFontUtilities.swift
│ ├── SDFont.docc
│ │ └── SDFont.md
│ ├── SDFont.h
│ ├── MetalZeroInitializer.swift
│ ├── MetalComputeBase.swift
│ ├── SDFontGlyphBounds.swift
│ ├── SDFontSignedDistanceGenerator.swift
│ ├── SDFontDownSampler.swift
│ ├── SDFontBinaryPixmapGenerator.swift
│ ├── SDFont.metal
│ ├── SDFontMetalSourceCode.swift
│ ├── SDFontIOHandler.swift
│ ├── SDFontRuntimeHelper.swift
│ └── SDFontGlyphPackingFinder.swift
├── build_all.sh
└── sdfontgen
│ └── main.swift
└── .gitignore
/doc/halo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/halo.png
--------------------------------------------------------------------------------
/doc/step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/step.png
--------------------------------------------------------------------------------
/doc/Helvetica.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/Helvetica.png
--------------------------------------------------------------------------------
/doc/drop_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/drop_shadow.png
--------------------------------------------------------------------------------
/doc/plot_halo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/plot_halo.png
--------------------------------------------------------------------------------
/doc/plot_step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/plot_step.png
--------------------------------------------------------------------------------
/doc/slope_step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/slope_step.png
--------------------------------------------------------------------------------
/doc/smooth_step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/smooth_step.png
--------------------------------------------------------------------------------
/doc/trapezoid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/trapezoid.png
--------------------------------------------------------------------------------
/doc/twin_peaks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/twin_peaks.png
--------------------------------------------------------------------------------
/doc/pass_through.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/pass_through.png
--------------------------------------------------------------------------------
/doc/StarWarsARBanner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/StarWarsARBanner.png
--------------------------------------------------------------------------------
/doc/plot_slope_step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/plot_slope_step.png
--------------------------------------------------------------------------------
/doc/plot_smooth_step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/plot_smooth_step.png
--------------------------------------------------------------------------------
/doc/plot_trapezoid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/plot_trapezoid.png
--------------------------------------------------------------------------------
/doc/plot_twin_peaks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/plot_twin_peaks.png
--------------------------------------------------------------------------------
/doc/StarWarsMacBanner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/StarWarsMacBanner.png
--------------------------------------------------------------------------------
/doc/plot_pass_through.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/doc/plot_pass_through.png
--------------------------------------------------------------------------------
/SWOpeningRoll/ios/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SWOpeningRoll/macos/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/Textures.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SWOpeningRoll/ios/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SWOpeningRoll/macos/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SDFont/SDFont.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SWOpeningRoll/SWOpeningRoll.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SWOpeningRoll/ios/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SWOpeningRoll/macos/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/Textures.xcassets/texture01.textureset/Universal.mipmapset/texture512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShoYamanishi/SDFontApple/HEAD/SWOpeningRollAR/SWOpeningRollAR/Textures.xcassets/texture01.textureset/Universal.mipmapset/texture512.png
--------------------------------------------------------------------------------
/SWOpeningRoll/shared/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 |
5 | @EnvironmentObject var worldManager : WorldManager
6 |
7 | var body: some View {
8 | VStack {
9 | MetalView()
10 | }//.padding()
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SWOpeningRoll/ios/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/Textures.xcassets/texture01.textureset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "textures" : [
7 | {
8 | "filename" : "Universal.mipmapset",
9 | "idiom" : "universal"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/Textures.xcassets/texture01.textureset/Universal.mipmapset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "levels" : [
7 | {
8 | "filename" : "texture512.png",
9 | "mipmap-level" : "base"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontUtilities.swift:
--------------------------------------------------------------------------------
1 | func roundUp16( _ v : Int ) -> Int {
2 | return (v + 15) / 16 * 16
3 | }
4 |
5 | func roundUp16( _ v : Int32 ) -> Int32 {
6 | return (v + 15) / 16 * 16
7 | }
8 |
9 | func clamp( low : T, val : T, high : T ) -> T {
10 | return max( low, min( high, val ) )
11 | }
12 |
--------------------------------------------------------------------------------
/SDFont/SDFont.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFont.docc/SDFont.md:
--------------------------------------------------------------------------------
1 | # ``SDFont``
2 |
3 | Summary
4 |
5 | ## Overview
6 |
7 | Text
8 |
9 | ## Topics
10 |
11 | ### Group
12 |
13 | - ``Symbol``
--------------------------------------------------------------------------------
/SWOpeningRoll/SWOpeningRoll.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SWOpeningRoll/macos/SWOpeningRollmacos.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFont.h:
--------------------------------------------------------------------------------
1 |
2 | #import
3 |
4 | //! Project version number for SDFont.
5 | FOUNDATION_EXPORT double SDFontVersionNumber;
6 |
7 | //! Project version string for SDFont.
8 | FOUNDATION_EXPORT const unsigned char SDFontVersionString[];
9 |
10 | // In this header, you should import all the public headers of your framework using statements like #import
11 |
12 |
13 |
--------------------------------------------------------------------------------
/SWOpeningRoll/shared/SWOpeningRollApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct SWOpeningRollApp: App {
5 |
6 | let worldManager : WorldManager
7 |
8 | init() {
9 | worldManager = WorldManager( device: MTLCreateSystemDefaultDevice()! )
10 | }
11 |
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView().environmentObject( worldManager )
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/SWOpeningRollARApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct SWOpeningRollARApp: App {
5 |
6 | let worldManager : WorldManager!
7 |
8 | init() {
9 | worldManager = WorldManager( device: MTLCreateSystemDefaultDevice()! )
10 | }
11 |
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView().environmentObject( worldManager )
15 | }
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/Camera.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | import simd
4 |
5 | class Camera {
6 |
7 | var projectionMatrix: float4x4
8 | var viewMatrixLHS: float4x4
9 |
10 | init () {
11 | self.projectionMatrix = float4x4.identity()
12 | self.viewMatrixLHS = float4x4.identity()
13 | }
14 |
15 | init ( projectionMatrix: float4x4, viewMatrixLHS: float4x4 ) {
16 | self.projectionMatrix = projectionMatrix
17 | self.viewMatrixLHS = viewMatrixLHS
18 | }
19 | }
20 |
21 |
22 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 |
5 | @EnvironmentObject var worldManager: WorldManager
6 |
7 | @Environment(\.verticalSizeClass) var verticalSizeClass
8 |
9 | var body: some View {
10 |
11 | if verticalSizeClass == .compact {
12 | HStack {
13 | MetalView().padding()
14 | }
15 |
16 | } else {
17 | VStack(alignment: .center) {
18 | MetalView().padding()
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/shaders/render_vertex_ins.h:
--------------------------------------------------------------------------------
1 | #ifndef __RENDER_VERTEX_INS_H__
2 | #define __RENDER_VERTEX_INS_H__
3 |
4 | struct VertexInPositionUV {
5 |
6 | // MemoryLayout.stride 32
7 | // MemoryLayout.size 24
8 | // MemoryLayout.alignment 16
9 |
10 | float4 position [[ attribute( 0 ) ]]; // size 16, alignment 16
11 |
12 | float2 uv [[ attribute( 1 ) ]]; // size 8, alignment 8
13 |
14 | //float _padding_01_; // size 4, alignment 4
15 | //float _padding_02_; // size 4, alignment 4
16 | };
17 |
18 | #endif /*__RENDER_VERTEX_INS_H__*/
19 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/shaders/render_vertex_ins.h:
--------------------------------------------------------------------------------
1 | #ifndef __RENDER_VERTEX_INS_H__
2 | #define __RENDER_VERTEX_INS_H__
3 |
4 | struct VertexInPositionUV {
5 |
6 | // MemoryLayout.stride 32
7 | // MemoryLayout.size 24
8 | // MemoryLayout.alignment 16
9 |
10 | float4 position [[ attribute( 0 ) ]]; // size 16, alignment 16
11 |
12 | float2 uv [[ attribute( 1 ) ]]; // size 8, alignment 8
13 |
14 | //float _padding_01_; // size 4, alignment 4
15 | //float _padding_02_; // size 4, alignment 4
16 | };
17 |
18 | #endif /*__RENDER_VERTEX_INS_H__*/
19 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/FloatUtil.swift:
--------------------------------------------------------------------------------
1 | import simd
2 |
3 | extension float4x4 {
4 |
5 |
6 | static func identity() -> float4x4 {
7 | matrix_identity_float4x4
8 | }
9 |
10 | init( fov: Float, aspect: Float, near: Float, far: Float,lhs: Bool = true ) {
11 |
12 | let y = 1 / tan(fov * 0.5)
13 | let x = y / aspect
14 | let z = lhs ? far / (far - near) : far / (near - far)
15 | let X = SIMD4( x, 0.0, 0.0, 0.0)
16 | let Y = SIMD4( 0.0, y, 0.0, 0.0)
17 | let Z = lhs ? SIMD4( 0.0, 0.0, z, 1.0 ) : SIMD4( 0.0, 0.0, z, -1.0 )
18 | let W = lhs ? SIMD4( 0.0, 0.0, -1.0*near*z, 0.0 ) : SIMD4( 0.0, 0.0, z * near, 0.0 )
19 |
20 | self.init()
21 | columns = ( X, Y, Z, W )
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/FloatUtil.swift:
--------------------------------------------------------------------------------
1 | import simd
2 |
3 | extension float4x4 {
4 |
5 |
6 | static func identity() -> float4x4 {
7 | matrix_identity_float4x4
8 | }
9 |
10 | init( fov: Float, aspect: Float, near: Float, far: Float,lhs: Bool = true ) {
11 |
12 | let y = 1 / tan(fov * 0.5)
13 | let x = y / aspect
14 | let z = lhs ? far / (far - near) : far / (near - far)
15 | let X = SIMD4( x, 0.0, 0.0, 0.0)
16 | let Y = SIMD4( 0.0, y, 0.0, 0.0)
17 | let Z = lhs ? SIMD4( 0.0, 0.0, z, 1.0 ) : SIMD4( 0.0, 0.0, z, -1.0 )
18 | let W = lhs ? SIMD4( 0.0, 0.0, -1.0*near*z, 0.0 ) : SIMD4( 0.0, 0.0, z * near, 0.0 )
19 |
20 | self.init()
21 | columns = ( X, Y, Z, W )
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/Camera.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | import simd
4 |
5 | class Camera {
6 |
7 | var fov : Float = 0.3 * Float.pi
8 | var aspect : Float = 1.0
9 | var near : Float = 0.01 // Do not make this too small, otherwise the depth buffer gets messed up
10 | var far : Float = 1000.0 // Do not make this too large, otherwise the depth buffer gets messed up
11 |
12 | func updateCameraDimension( dimension: SIMD2 ) {
13 | aspect = dimension.x / dimension.y
14 | }
15 |
16 | var projectionMatrix: float4x4 {
17 | return float4x4( fov: fov, aspect: aspect, near: near, far: far )
18 | }
19 |
20 | var viewMatrixLHS: float4x4 {
21 |
22 | var toLHS = float4x4.identity()
23 | toLHS.columns.2.z = -1.0
24 |
25 | return toLHS
26 | }
27 | }
28 |
29 |
30 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/VertexDescriptorGenerator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | extension MTLVertexDescriptor {
5 |
6 | static let float4Stride = MemoryLayout>.stride
7 | static let float3Stride = MemoryLayout>.stride
8 | static let float2Stride = MemoryLayout>.stride
9 |
10 |
11 | static func positionUV() -> MTLVertexDescriptor {
12 |
13 | let d = MTLVertexDescriptor()
14 |
15 | d.attributes[0].format = .float4
16 | d.attributes[0].offset = 0
17 | d.attributes[0].bufferIndex = 0
18 |
19 | d.attributes[1].format = .float2
20 | d.attributes[1].offset = float4Stride
21 | d.attributes[1].bufferIndex = 0
22 |
23 | d.layouts[0].stride = float4Stride + float4Stride
24 | d.layouts[0].stepFunction = .perVertex
25 |
26 | return d
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/VertexDescriptorGenerator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | extension MTLVertexDescriptor {
5 |
6 | static let float4Stride = MemoryLayout>.stride
7 | static let float3Stride = MemoryLayout>.stride
8 | static let float2Stride = MemoryLayout>.stride
9 |
10 |
11 | static func positionUV() -> MTLVertexDescriptor {
12 |
13 | let d = MTLVertexDescriptor()
14 |
15 | d.attributes[0].format = .float4
16 | d.attributes[0].offset = 0
17 | d.attributes[0].bufferIndex = 0
18 |
19 | d.attributes[1].format = .float2
20 | d.attributes[1].offset = float4Stride
21 | d.attributes[1].bufferIndex = 0
22 |
23 | d.layouts[0].stride = float4Stride + float4Stride
24 | d.layouts[0].stepFunction = .perVertex
25 |
26 | return d
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/SWOpeningRoll/macos/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/TouchMTKView.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | protocol TouchMTKViewDelegate {
5 | func touchesBegan( location: CGPoint, size: CGRect )
6 | func touchesMoved( location: CGPoint, size: CGRect )
7 | func touchesEnded( location: CGPoint, size: CGRect )
8 | }
9 |
10 |
11 | // Decorator to MTKView in order to forward touches to the delegate
12 | class TouchMTKView: MTKView {
13 |
14 | var touchDelegate : TouchMTKViewDelegate?
15 |
16 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
17 | if touches.count > 0 {
18 | let loc = touches.first!.location( in: self )
19 | touchDelegate?.touchesBegan( location: loc, size: self.frame )
20 | }
21 | }
22 |
23 | override func touchesMoved(_ touches: Set, with event: UIEvent?) {
24 | if touches.count > 0 {
25 | let loc = touches.first!.location( in: self )
26 | touchDelegate?.touchesMoved( location: loc, size: self.frame )
27 | }
28 | }
29 |
30 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
31 | if touches.count > 0 {
32 | let loc = touches.first!.location( in: self )
33 | touchDelegate?.touchesEnded( location: loc, size: self.frame )
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SDFont/SDFont/MetalZeroInitializer.swift:
--------------------------------------------------------------------------------
1 | import MetalKit
2 |
3 | class ZeroInitializer : MetalComputeBase {
4 |
5 | struct Config {
6 | var lengthInInt32: Int32
7 | }
8 |
9 | override init( device : MTLDevice ) {
10 | super.init( device : device )
11 | createPipelineState( functionName : "initialize_with_zero")
12 | }
13 |
14 | func perform( buffer: MTLBuffer, lengthInInt32 : Int ) {
15 |
16 | var config = Config( lengthInInt32 : Int32(lengthInInt32) )
17 |
18 | guard let commandBuffer = queue!.makeCommandBuffer() else {
19 | print ("ERROR: Cannot make command buffer for ZeroInitializer.")
20 | return
21 | }
22 |
23 | let encoder = commandBuffer.makeComputeCommandEncoder()
24 |
25 | encoder!.setComputePipelineState( pipelineState! )
26 |
27 | encoder!.setBytes( &config, length: MemoryLayout.stride, index: 0 )
28 | encoder!.setBuffer( buffer, offset: 0, index: 1 )
29 |
30 | encoder!.dispatchThreadgroups(
31 | MTLSizeMake( 1, 1, 1 ),
32 | threadsPerThreadgroup: MTLSizeMake( Self.ThreadsPerGroup, 1, 1 )
33 | )
34 | encoder!.endEncoding()
35 | commandBuffer.commit()
36 | commandBuffer.waitUntilCompleted()
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/RenderVertexIns.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | struct VertexInIndex {
5 |
6 | // MemoryLayout.stride 4
7 | // MemoryLayout.size 4
8 | // MemoryLayout.alignment 4
9 | var index: Int32
10 |
11 | static func generateMTLBuffer( device: MTLDevice, indices : [Int32] ) -> MTLBuffer? {
12 | return device.makeBuffer(bytes: indices, length: MemoryLayout.stride * indices.count, options: .storageModeShared )
13 | }
14 | }
15 |
16 | struct VertexInPositionUV {
17 |
18 | // MemoryLayout.stride 32
19 | // MemoryLayout.size 24
20 | // MemoryLayout.alignment 16
21 | var position : SIMD4
22 | var uv : SIMD2
23 |
24 | static func generateMTLBuffer( device: MTLDevice, positions : [SIMD4], uvs: [SIMD2] ) -> MTLBuffer? {
25 | var V : [Self] = []
26 | for i in 0 ..< positions.count {
27 | V.append( Self( position: positions[i], uv: uvs[i] ) )
28 | }
29 | return Self.generateMTLBuffer( device: device, vertices : V )
30 | }
31 |
32 | static func generateMTLBuffer( device: MTLDevice, vertices : [Self] ) -> MTLBuffer? {
33 | return device.makeBuffer(bytes: vertices, length: MemoryLayout.stride * vertices.count, options: .storageModeShared )
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/RenderVertexIns.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | struct VertexInIndex {
5 |
6 | // MemoryLayout.stride 4
7 | // MemoryLayout.size 4
8 | // MemoryLayout.alignment 4
9 | var index: Int32
10 |
11 | static func generateMTLBuffer( device: MTLDevice, indices : [Int32] ) -> MTLBuffer? {
12 | return device.makeBuffer(bytes: indices, length: MemoryLayout.stride * indices.count, options: .storageModeShared )
13 | }
14 | }
15 |
16 | struct VertexInPositionUV {
17 |
18 | // MemoryLayout.stride 32
19 | // MemoryLayout.size 24
20 | // MemoryLayout.alignment 16
21 | var position : SIMD4
22 | var uv : SIMD2
23 |
24 | static func generateMTLBuffer( device: MTLDevice, positions : [SIMD4], uvs: [SIMD2] ) -> MTLBuffer? {
25 | var V : [Self] = []
26 | for i in 0 ..< positions.count {
27 | V.append( Self( position: positions[i], uv: uvs[i] ) )
28 | }
29 | return Self.generateMTLBuffer( device: device, vertices : V )
30 | }
31 |
32 | static func generateMTLBuffer( device: MTLDevice, vertices : [Self] ) -> MTLBuffer? {
33 | return device.makeBuffer(bytes: vertices, length: MemoryLayout.stride * vertices.count, options: .storageModeShared )
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/shaders/render_uniforms.h:
--------------------------------------------------------------------------------
1 | #ifndef __RENDER_UNIFORMS_H__
2 | #define __RENDER_UNIFORMS_H__
3 |
4 | using namespace metal;
5 |
6 | struct UniformPerInstance {
7 |
8 | // MemoryLayout.stride 64
9 | // MemoryLayout.size 64
10 | // MemoryLayout.alignment 16
11 |
12 | float4x4 model_matrix; // size 64, alignment 16
13 | };
14 |
15 | struct UniformPerScene {
16 |
17 | // MemoryLayout.stride 160
18 | // MemoryLayout.size 148
19 | // MemoryLayout.alignment 16
20 |
21 | float4x4 view_matrix; // size 64, alignment 16
22 | float4x4 projection_matrix; // size 64, alignment 16
23 | float3 camera_position; // size 16, alignment 16
24 | int32_t num_lights; // size 4, alignment 4
25 | int32_t _padding_01_; // size 4, alignment 4
26 | int32_t _padding_02_; // size 4, alignment 4
27 | int32_t _padding_03_; // size 4, alignment 4
28 | };
29 |
30 | typedef enum {
31 |
32 | SDFONT_PASS_THROUGH = 0,
33 | SDFONT_STEP = 1,
34 | SDFONT_SMOOTH_STEP = 2,
35 | SDFONT_SLOPE_STEP = 3,
36 | SDFONT_TRAPEZOID = 4,
37 | SDFONT_TWIN_PEAKS = 5,
38 | SDFONT_HALO = 6
39 |
40 | } SDFontFunctionType;
41 |
42 |
43 | struct UniformSDFont {
44 | float4 foreground_color;
45 | int func_type;
46 | float width;
47 | float point1;
48 | float point2;
49 | };
50 |
51 |
52 | #endif /* __RENDER_UNIFORMS_H__ */
53 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/shaders/render_uniforms.h:
--------------------------------------------------------------------------------
1 | #ifndef __RENDER_UNIFORMS_H__
2 | #define __RENDER_UNIFORMS_H__
3 |
4 | using namespace metal;
5 |
6 | struct UniformPerInstance {
7 |
8 | // MemoryLayout.stride 64
9 | // MemoryLayout.size 64
10 | // MemoryLayout.alignment 16
11 |
12 | float4x4 model_matrix; // size 64, alignment 16
13 | };
14 |
15 | struct UniformPerScene {
16 |
17 | // MemoryLayout.stride 160
18 | // MemoryLayout.size 148
19 | // MemoryLayout.alignment 16
20 |
21 | float4x4 view_matrix; // size 64, alignment 16
22 | float4x4 projection_matrix; // size 64, alignment 16
23 | float3 camera_position; // size 16, alignment 16
24 | int32_t num_lights; // size 4, alignment 4
25 | int32_t _padding_01_; // size 4, alignment 4
26 | int32_t _padding_02_; // size 4, alignment 4
27 | int32_t _padding_03_; // size 4, alignment 4
28 | };
29 |
30 | typedef enum {
31 |
32 | SDFONT_PASS_THROUGH = 0,
33 | SDFONT_STEP = 1,
34 | SDFONT_SMOOTH_STEP = 2,
35 | SDFONT_SLOPE_STEP = 3,
36 | SDFONT_TRAPEZOID = 4,
37 | SDFONT_TWIN_PEAKS = 5,
38 | SDFONT_HALO = 6
39 |
40 | } SDFontFunctionType;
41 |
42 |
43 | struct UniformSDFont {
44 | float4 foreground_color;
45 | int func_type;
46 | float width;
47 | float point1;
48 | float point2;
49 | };
50 |
51 |
52 | #endif /* __RENDER_UNIFORMS_H__ */
53 |
--------------------------------------------------------------------------------
/SDFont/build_all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | xcodebuild archive -scheme SDFont -configuration Release -destination 'generic/platform=iOS' -archivePath './build/SDFont.framework-iphoneos.xcarchive' SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
3 | xcodebuild archive -scheme SDFont -configuration Release -destination 'generic/platform=iOS Simulator' -archivePath './build/SDFont.framework-iphonesimulator.xcarchive' SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
4 | xcodebuild archive -scheme SDFont -configuration Release -sdk macosx -archivePath './build/SDFont.framework-macos.xcarchive' SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
5 | xcodebuild -create-xcframework -framework './build/SDFont.framework-iphoneos.xcarchive/Products/Library/Frameworks/SDFont.framework' -framework './build/SDFont.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SDFont.framework' -framework './build/SDFont.framework-macos.xcarchive/Products/Library/Frameworks/SDFont.framework' -output './build/SDFont.xcframework'
6 | xcodebuild docbuild -scheme SDFont -derivedDataPath ./build
7 | cp -r ./build/Build/Products/Release/SDFont.doccarchive .
8 | xcodebuild archive -scheme sdfontgen -configuration Release -sdk macosx -archivePath './build/sdfontgen' SKIP_INSTALL=NO
9 | cp build/sdfontgen.xcarchive/Products/usr/local/bin/sdfontgen ./build/
10 |
11 | while true; do
12 | read -p "Do you wish to install SDFont.framework--macosx to this Mac under /Library/Frameworks?" yn
13 | case $yn in
14 | [Yy]* ) sudo cp -r build/SDFont.xcframework/macos-arm64_x86_64/SDFont.framework /Library/Frameworks; break;;
15 | [Nn]* ) exit;;
16 | * ) echo "Please answer yes or no.";;
17 | esac
18 | done
19 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/ShaderCameraImage.metal:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace metal;
3 |
4 | typedef struct {
5 | float2 position [[ attribute(0) ]];
6 | float2 texCoord [[ attribute(1) ]];
7 | } ImageVertex;
8 |
9 |
10 | typedef struct {
11 | float4 position [[position]];
12 | float2 texCoord;
13 | } ImageColorInOut;
14 |
15 |
16 | // Captured image vertex function
17 | vertex ImageColorInOut capturedImageVertexTransform(ImageVertex in [[stage_in]]) {
18 | ImageColorInOut out;
19 |
20 | // Pass through the image vertex's position
21 | out.position = float4(in.position, 0.0, 1.0);
22 |
23 | // Pass through the texture coordinate
24 | out.texCoord = in.texCoord;
25 |
26 | return out;
27 | }
28 |
29 | // Captured image fragment function
30 | fragment float4 capturedImageFragmentShader(ImageColorInOut in [[stage_in]],
31 | texture2d capturedImageTextureY [[ texture(1) ]],
32 | texture2d capturedImageTextureCbCr [[ texture(2) ]]) {
33 |
34 | constexpr sampler colorSampler(mip_filter::linear,
35 | mag_filter::linear,
36 | min_filter::linear);
37 |
38 | const float4x4 ycbcrToRGBTransform = float4x4(
39 | float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f),
40 | float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f),
41 | float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f),
42 | float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f)
43 | );
44 |
45 | // Sample Y and CbCr textures to get the YCbCr color at the given texture coordinate
46 | float4 ycbcr = float4(capturedImageTextureY.sample(colorSampler, in.texCoord).r,
47 | capturedImageTextureCbCr.sample(colorSampler, in.texCoord).rg, 1.0);
48 |
49 | // Return converted RGB color
50 | return ycbcrToRGBTransform * ycbcr;
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/SWOpeningRoll/macos/MetalView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import MetalKit
3 |
4 | struct MetalView: NSViewRepresentable {
5 |
6 | @EnvironmentObject var worldManager: WorldManager
7 |
8 | func makeCoordinator() -> Coordinator {
9 |
10 | Coordinator(self)
11 | }
12 |
13 | func makeNSView( context: NSViewRepresentableContext ) -> MTKView {
14 |
15 | let mtkView = MTKView()
16 |
17 | mtkView.delegate = context.coordinator
18 | mtkView.preferredFramesPerSecond = 60
19 | mtkView.device = worldManager.device
20 | mtkView.framebufferOnly = false
21 | mtkView.clearColor = MTLClearColor( red: 0, green: 0, blue: 0, alpha: 0 )
22 | mtkView.drawableSize = mtkView.frame.size
23 | //mtkView.enableSetNeedsDisplay = true
24 | mtkView.depthStencilPixelFormat = .depth32Float
25 |
26 | context.coordinator.renderCoordinator.createPipelineStates( colorPixelFormat: mtkView.colorPixelFormat )
27 | context.coordinator.mtkView( mtkView, drawableSizeWillChange: mtkView.bounds.size )
28 |
29 | return mtkView
30 | }
31 |
32 | func updateNSView(_ nsView: MTKView, context: NSViewRepresentableContext ) {
33 |
34 | }
35 |
36 | class Coordinator : NSObject, MTKViewDelegate {
37 |
38 | let renderCoordinator : RenderCoordinator
39 | let parent : MetalView
40 |
41 | init( _ parent: MetalView ) {
42 |
43 | self.renderCoordinator = RenderCoordinator( worldManager : parent.worldManager, device: parent.worldManager.device )
44 | self.parent = parent
45 |
46 | super.init()
47 | }
48 |
49 | func mtkView( _ view : MTKView, drawableSizeWillChange size: CGSize ) {
50 |
51 | renderCoordinator.mtkView( view, drawableSizeWillChange: size )
52 | }
53 |
54 | func draw( in view: MTKView ) {
55 |
56 | renderCoordinator.draw( in: view )
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/SWOpeningRoll/ios/MetalView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import MetalKit
3 |
4 | struct MetalView: UIViewRepresentable {
5 |
6 | @EnvironmentObject var worldManager: WorldManager
7 |
8 | func makeCoordinator() -> Coordinator {
9 |
10 | return Coordinator( self )
11 | }
12 |
13 | func makeUIView( context: UIViewRepresentableContext ) -> MTKView {
14 |
15 | let mtkView = MTKView()
16 |
17 | mtkView.delegate = context.coordinator
18 | mtkView.preferredFramesPerSecond = 60
19 | mtkView.device = MTLCreateSystemDefaultDevice()
20 | mtkView.framebufferOnly = false
21 | mtkView.clearColor = MTLClearColor( red: 0, green: 0, blue: 0, alpha: 0 )
22 | mtkView.drawableSize = mtkView.frame.size
23 | mtkView.enableSetNeedsDisplay = false
24 | mtkView.depthStencilPixelFormat = .depth32Float
25 |
26 | context.coordinator.renderCoordinator.createPipelineStates( colorPixelFormat: mtkView.colorPixelFormat )
27 | context.coordinator.mtkView( mtkView, drawableSizeWillChange: mtkView.bounds.size )
28 |
29 | return mtkView
30 | }
31 |
32 | func updateUIView( _ uiView: MTKView, context: UIViewRepresentableContext ) {
33 |
34 | }
35 |
36 | class Coordinator : NSObject, MTKViewDelegate {
37 |
38 | let renderCoordinator : RenderCoordinator
39 | let parent : MetalView
40 |
41 | init( _ parent: MetalView ) {
42 |
43 | self.renderCoordinator = RenderCoordinator( worldManager : parent.worldManager, device: parent.worldManager.device )
44 | self.parent = parent
45 |
46 | super.init()
47 | }
48 |
49 | func mtkView( _ view: MTKView, drawableSizeWillChange size: CGSize ) {
50 |
51 | renderCoordinator.mtkView( view, drawableSizeWillChange: size )
52 | }
53 |
54 | func draw( in view: MTKView ) {
55 | renderCoordinator.draw( in: view )
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/SDFont/SDFont/MetalComputeBase.swift:
--------------------------------------------------------------------------------
1 | import MetalKit
2 |
3 | class MetalComputeBase {
4 |
5 | #if os(iOS)
6 | static let ThreadsPerGroup = 512
7 | #else
8 | static let ThreadsPerGroup = 1024
9 | #endif
10 |
11 | let device : MTLDevice
12 | var queue : MTLCommandQueue?
13 | var pipelineState : MTLComputePipelineState?
14 |
15 | init( device : MTLDevice ) {
16 |
17 | self.device = device
18 | self.queue = nil
19 | self.pipelineState = nil
20 |
21 | guard let queue = device.makeCommandQueue()
22 | else {
23 | print ("ERROR: Cannot make a Metal command queue.")
24 | return
25 | }
26 | self.queue = queue
27 | }
28 |
29 | func createPipelineState( functionName : String) {
30 |
31 | do {
32 | // var options = MTLCompileOptions()
33 | // options.fastMathEnabled = false
34 | let library = try device.makeLibrary(source: SDFontMetalSourceCode, options: nil )
35 |
36 | let desc = MTLComputePipelineDescriptor()
37 | desc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true
38 | desc.computeFunction = library.makeFunction( name: functionName )
39 | do {
40 | pipelineState = try device.makeComputePipelineState( descriptor: desc, options: [], reflection : nil )
41 | }
42 | catch {
43 | print ( "ERROR: Cannot make pipeline state for \(functionName)" )
44 | return
45 | }
46 | } catch {
47 | fatalError("ERROR: Cannot compile metal code from the baked string.")
48 | }
49 | }
50 |
51 | static func getThreadConfiguration( numThreads: Int ) -> ( numGroupsPerGrid: Int, numThreadsPerGroup: Int ) {
52 |
53 | let TPG = Self.ThreadsPerGroup
54 |
55 | let numThreadsAligned32 = Int( ( ( numThreads + 31 ) / 32 ) * 32 )
56 | let numThreadsPerGroup = Int( ( numThreadsAligned32 < TPG ) ? numThreadsAligned32 : TPG )
57 | let numGroupsPerGrid = Int( ( numThreadsAligned32 + TPG - 1 ) / TPG )
58 |
59 | return ( numGroupsPerGrid: numGroupsPerGrid, numThreadsPerGroup: numThreadsPerGroup )
60 | }
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/SWOpeningRoll/shared/AnimationSequencer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | class AnimationSequencer {
4 |
5 | var timeStart : TimeInterval
6 |
7 | let temporalPoints : [TimeInterval]
8 | let spacialPoints : [SIMD3]
9 | let colors : [SIMD4]
10 |
11 | var timeNow : TimeInterval
12 | var spacialPointNow : SIMD3
13 | var colorNow : SIMD4
14 | var animationActive : Bool
15 |
16 | init(
17 | temporalPoints : [TimeInterval],
18 | spacialPoints : [SIMD3],
19 | colors : [SIMD4]
20 | ) {
21 | self.timeStart = Date().timeIntervalSince1970
22 | self.temporalPoints = temporalPoints
23 | self.spacialPoints = spacialPoints
24 | self.colors = colors
25 | self.timeNow = timeStart
26 | self.spacialPointNow = spacialPoints[0]
27 | self.colorNow = colors[0]
28 | self.animationActive = false
29 | }
30 |
31 | func startAnimation() {
32 | timeStart = Date().timeIntervalSince1970
33 | timeNow = timeStart
34 | spacialPointNow = spacialPoints[0]
35 | colorNow = colors[0]
36 | animationActive = true
37 | }
38 |
39 | func step() {
40 |
41 | let timeNow = Date().timeIntervalSince1970
42 | let timeSinceStart = timeNow - timeStart
43 |
44 | for i in 1 ..< temporalPoints.count {
45 |
46 | if temporalPoints[i] > timeSinceStart {
47 |
48 | // interpolate between temporalPoints[i-1] and temporalPoints[i]
49 |
50 | let alpha = Float( ( timeSinceStart - temporalPoints[ i - 1 ] )
51 | / ( temporalPoints[ i ] - temporalPoints[ i - 1 ] ) )
52 |
53 | spacialPointNow = spacialPoints[ i - 1 ] * ( 1.0 - alpha ) + spacialPoints[i] * alpha
54 | colorNow = colors[ i - 1 ] * ( 1.0 - alpha ) + colors[i] * alpha
55 | return
56 | }
57 | }
58 | animationActive = false
59 | spacialPointNow = spacialPoints[ temporalPoints.count - 1 ]
60 | colorNow = colors [ temporalPoints.count - 1 ]
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/demo/AnimationSequencer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | class AnimationSequencer {
4 |
5 | var timeStart : TimeInterval
6 |
7 | let temporalPoints : [TimeInterval]
8 | let spacialPoints : [SIMD3]
9 | let colors : [SIMD4]
10 |
11 | var timeNow : TimeInterval
12 | var spacialPointNow : SIMD3
13 | var colorNow : SIMD4
14 | var animationActive : Bool
15 |
16 | init(
17 | temporalPoints : [TimeInterval],
18 | spacialPoints : [SIMD3],
19 | colors : [SIMD4]
20 | ) {
21 | self.timeStart = Date().timeIntervalSince1970
22 | self.temporalPoints = temporalPoints
23 | self.spacialPoints = spacialPoints
24 | self.colors = colors
25 | self.timeNow = timeStart
26 | self.spacialPointNow = spacialPoints[0]
27 | self.colorNow = colors[0]
28 | self.animationActive = false
29 | }
30 |
31 | func startAnimation() {
32 | timeStart = Date().timeIntervalSince1970
33 | timeNow = timeStart
34 | spacialPointNow = spacialPoints[0]
35 | colorNow = colors[0]
36 | animationActive = true
37 | }
38 |
39 | func step() {
40 |
41 | let timeNow = Date().timeIntervalSince1970
42 | let timeSinceStart = timeNow - timeStart
43 |
44 | for i in 1 ..< temporalPoints.count {
45 |
46 | if temporalPoints[i] > timeSinceStart {
47 |
48 | // interpolate between temporalPoints[i-1] and temporalPoints[i]
49 |
50 | let alpha = Float( ( timeSinceStart - temporalPoints[ i - 1 ] )
51 | / ( temporalPoints[ i ] - temporalPoints[ i - 1 ] ) )
52 |
53 | spacialPointNow = spacialPoints[ i - 1 ] * ( 1.0 - alpha ) + spacialPoints[i] * alpha
54 | colorNow = colors[ i - 1 ] * ( 1.0 - alpha ) + colors[i] * alpha
55 | return
56 | }
57 | }
58 | animationActive = false
59 | spacialPointNow = spacialPoints[ temporalPoints.count - 1 ]
60 | colorNow = colors [ temporalPoints.count - 1 ]
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/MetalView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import MetalKit
3 |
4 |
5 | // This is based on https://developer.apple.com/forums/thread/119112?answerId=654964022#654964022
6 | struct MetalView: UIViewRepresentable {
7 |
8 | @EnvironmentObject var worldManager: WorldManager
9 |
10 | class Coordinator : NSObject, MTKViewDelegate {
11 |
12 | let arCoordinator : ARCoordinator
13 | let parent : MetalView
14 |
15 | init( _ parent: MetalView ) {
16 |
17 | self.arCoordinator = ARCoordinator( worldManager : parent.worldManager, device: parent.worldManager.device )
18 | self.parent = parent
19 |
20 | super.init()
21 |
22 | parent.worldManager.arCoordinator = arCoordinator
23 | }
24 |
25 | func mtkView( _ view: MTKView, drawableSizeWillChange size: CGSize ) {
26 |
27 | arCoordinator.mtkView( view, drawableSizeWillChange: size )
28 | }
29 |
30 | func draw( in view: MTKView ) {
31 | arCoordinator.draw( in: view )
32 | }
33 | }
34 |
35 | func makeCoordinator() -> Coordinator {
36 |
37 | return Coordinator( self )
38 | }
39 |
40 | func makeUIView( context: UIViewRepresentableContext ) -> MTKView {
41 |
42 | let mtkView = TouchMTKView()
43 |
44 | mtkView.delegate = context.coordinator
45 | mtkView.touchDelegate = worldManager
46 | mtkView.preferredFramesPerSecond = 60
47 | mtkView.device = context.coordinator.arCoordinator.device
48 | mtkView.framebufferOnly = true
49 | mtkView.clearColor = MTLClearColor( red: 0, green: 0, blue: 0, alpha: 0 )
50 | mtkView.drawableSize = mtkView.frame.size
51 | mtkView.enableSetNeedsDisplay = false
52 | mtkView.depthStencilPixelFormat = .depth32Float
53 |
54 | context.coordinator.arCoordinator.createPipelineStates( mtkView: mtkView )
55 | context.coordinator.mtkView( mtkView, drawableSizeWillChange: mtkView.frame.size )
56 |
57 | return mtkView
58 | }
59 |
60 | func updateUIView( _ uiView: MTKView, context: UIViewRepresentableContext ) {
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontGlyphBounds.swift:
--------------------------------------------------------------------------------
1 | import CoreGraphics
2 |
3 | struct SignedDistanceFontGlyphBounds {
4 |
5 | let outer : CGRect // with spread. Integer-aligned
6 | let inner : CGRect // glyph bounds from CoreText
7 | let spreadThickness : CGFloat // spread in pixels
8 | let textureSideLen : CGFloat // length of the dies of the texture in which this glyph resides.
9 |
10 | func normalizedInnerBound( flipY: Bool ) -> CGRect {
11 |
12 | let adjustedOriginY = flipY ? (textureSideLen - (inner.origin.y + inner.size.height)) : inner.origin.y
13 |
14 | return CGRect(
15 | x : inner.origin.x / textureSideLen,
16 | y : adjustedOriginY / textureSideLen,
17 | width : inner.size.width / textureSideLen,
18 | height : inner.size.height / textureSideLen
19 | )
20 | }
21 |
22 | init( outerOrigin : CGPoint, outerSize : CGSize, spreadThickness : CGFloat, innerSize : CGSize, textureSideLen : CGFloat ) {
23 |
24 | self.outer = CGRect(
25 |
26 | x : outerOrigin.x,
27 | y : outerOrigin.y,
28 | width : outerSize.width,
29 | height : outerSize.height
30 | )
31 |
32 | self.inner = CGRect(
33 |
34 | x : outerOrigin.x + spreadThickness,
35 | y : outerOrigin.y + spreadThickness,
36 | width : innerSize.width,
37 | height : innerSize.height
38 | )
39 |
40 | self.spreadThickness = spreadThickness
41 | self.textureSideLen = textureSideLen
42 | }
43 |
44 | func localizeAndUpSample( upSamplingFactor : CGFloat ) -> Self {
45 |
46 | let converted = SignedDistanceFontGlyphBounds(
47 |
48 | outerOrigin : CGPoint(x: 0, y: 0 ),
49 |
50 | outerSize : CGSize( width: self.outer.size.width * upSamplingFactor,
51 | height: self.outer.size.height * upSamplingFactor ),
52 |
53 | spreadThickness : self.spreadThickness * upSamplingFactor,
54 |
55 | innerSize : CGSize( width: self.inner.size.width * upSamplingFactor,
56 | height: self.inner.size.height * upSamplingFactor ),
57 |
58 | textureSideLen : self.textureSideLen * upSamplingFactor
59 | )
60 |
61 | return converted
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SWOpeningRoll/macos/RenderCoordinator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | class RenderCoordinator: NSObject, MTKViewDelegate {
5 |
6 | var device: MTLDevice!
7 | var commandQueue: MTLCommandQueue!
8 | var colorPixelFormat: MTLPixelFormat!
9 | var worldManager: WorldManager?
10 |
11 | var previousFrameSize: CGSize // to detect screen dimension change. SwiftUI does not always call mtkView().
12 |
13 | init( worldManager: WorldManager, device : MTLDevice ) {
14 |
15 | self.worldManager = worldManager
16 | self.device = worldManager.device
17 | guard
18 | let commandQueue = device.makeCommandQueue()
19 | else {
20 | fatalError("GPU not available")
21 | }
22 | self.commandQueue = commandQueue
23 | self.previousFrameSize = CGSize( width: 0.0, height: 0.0 )
24 |
25 | super.init()
26 | }
27 |
28 | /// 2nd part of init. It must wait until device and the pixel format become available in MTKView
29 | func createPipelineStates( colorPixelFormat: MTLPixelFormat ) {
30 | worldManager?.createPipelineStates( colorPixelFormat: colorPixelFormat )
31 | }
32 |
33 | // MARK: - MTKViewDelegate
34 |
35 | func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize ) {
36 |
37 | self.colorPixelFormat = view.colorPixelFormat
38 | worldManager?.updateScreenSizes( view )
39 | }
40 |
41 | func draw( in view: MTKView ) {
42 |
43 | if previousFrameSize != view.frame.size {
44 | previousFrameSize = view.frame.size
45 | self.mtkView( view, drawableSizeWillChange: view.frame.size )
46 | }
47 |
48 | worldManager?.updateWorld()
49 |
50 | guard
51 | let descriptor = view.currentRenderPassDescriptor,
52 | let commandBuffer = commandQueue.makeCommandBuffer(),
53 | let encoder = commandBuffer.makeRenderCommandEncoder( descriptor: descriptor )
54 | else {
55 | return
56 | }
57 |
58 | worldManager?.encode( encoder: encoder )
59 |
60 | encoder.endEncoding()
61 |
62 | guard let drawable = view.currentDrawable
63 | else {
64 | return
65 | }
66 | commandBuffer.present( drawable )
67 | commandBuffer.commit()
68 | commandBuffer.waitUntilCompleted()
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/SWOpeningRoll/ios/RenderCoordinator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 |
5 | class RenderCoordinator: NSObject, MTKViewDelegate {
6 |
7 | var device: MTLDevice!
8 | var commandQueue: MTLCommandQueue!
9 | var colorPixelFormat: MTLPixelFormat!
10 | var worldManager: WorldManager?
11 |
12 | var previousFrameSize: CGSize // to detect screen dimension change. SwiftUI does not always call mtkView().
13 |
14 | init( worldManager: WorldManager, device : MTLDevice ) {
15 |
16 | self.worldManager = worldManager
17 | self.device = worldManager.device
18 | guard
19 | let commandQueue = device.makeCommandQueue()
20 | else {
21 | fatalError("GPU not available")
22 | }
23 | self.commandQueue = commandQueue
24 |
25 | self.previousFrameSize = CGSize( width: 0.0, height: 0.0 )
26 |
27 | super.init()
28 |
29 | }
30 |
31 | /// 2nd part of init. It must wait until device and the pixel format become available in MTKView
32 | func createPipelineStates( colorPixelFormat: MTLPixelFormat ) {
33 | worldManager?.createPipelineStates( colorPixelFormat: colorPixelFormat )
34 | }
35 |
36 | // MARK: - MTKViewDelegate
37 |
38 | func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize ) {
39 |
40 | self.colorPixelFormat = view.colorPixelFormat
41 | worldManager?.updateScreenSizes( view )
42 | }
43 |
44 | func draw( in view: MTKView ) {
45 |
46 | if previousFrameSize != view.frame.size {
47 | previousFrameSize = view.frame.size
48 | self.mtkView( view, drawableSizeWillChange: view.frame.size )
49 | }
50 |
51 | worldManager?.updateWorld()
52 |
53 | guard
54 | let descriptor = view.currentRenderPassDescriptor,
55 | let commandBuffer = commandQueue.makeCommandBuffer(),
56 | let encoder = commandBuffer.makeRenderCommandEncoder( descriptor: descriptor )
57 | else {
58 | return
59 | }
60 |
61 | worldManager?.encode( encoder: encoder )
62 |
63 | encoder.endEncoding()
64 |
65 | guard let drawable = view.currentDrawable
66 | else {
67 | return
68 | }
69 | commandBuffer.present( drawable )
70 | commandBuffer.commit()
71 | commandBuffer.waitUntilCompleted()
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontSignedDistanceGenerator.swift:
--------------------------------------------------------------------------------
1 | import MetalKit
2 |
3 | class SDFontSignedDistanceGenerator : MetalComputeBase {
4 |
5 | struct Config {
6 |
7 | var drawAreaSideLen : Int32
8 | var spreadThickness : Float
9 | var width : Int32
10 | var height : Int32
11 |
12 | init( drawAreaSideLen : Int32, spreadThickness : Float ) {
13 | self.drawAreaSideLen = drawAreaSideLen
14 | self.spreadThickness = spreadThickness
15 | self.width = 0
16 | self.height = 0
17 | }
18 | }
19 |
20 | var config : Config
21 | var signedDistanceBuffer : MTLBuffer!
22 |
23 | init( device : MTLDevice, drawAreaSideLen : Int , spreadThickness : Float ) {
24 |
25 | self.signedDistanceBuffer = device.makeBuffer(
26 | length: drawAreaSideLen * drawAreaSideLen * MemoryLayout.stride,
27 | options: .storageModeShared
28 | )
29 |
30 | config = Config( drawAreaSideLen : Int32(drawAreaSideLen), spreadThickness : spreadThickness )
31 |
32 | super.init( device : device )
33 |
34 | createPipelineState( functionName : "generate_signed_distance" )
35 | }
36 |
37 | func generate( pixmapBuffer: MTLBuffer, glyphBounds : SignedDistanceFontGlyphBounds ) {
38 |
39 | config.width = Int32( glyphBounds.outer.size.width )
40 | config.height = Int32( glyphBounds.outer.size.height )
41 |
42 | guard let commandBuffer = queue!.makeCommandBuffer() else {
43 | print ("ERROR: Cannot make command buffer for SigneDistanceGenerator.")
44 | return
45 | }
46 |
47 | let encoder = commandBuffer.makeComputeCommandEncoder()
48 |
49 | encoder!.setComputePipelineState( pipelineState! )
50 |
51 | encoder!.setBytes( &config, length: MemoryLayout.stride, index: 0 )
52 | encoder!.setBuffer( pixmapBuffer, offset: 0, index: 1 )
53 | encoder!.setBuffer( signedDistanceBuffer, offset: 0, index: 2 )
54 |
55 | let threadConfig = MetalComputeBase.getThreadConfiguration( numThreads: Int( config.width * config.height ) )
56 | encoder!.dispatchThreadgroups(
57 | MTLSizeMake( threadConfig.numGroupsPerGrid, 1, 1 ),
58 | threadsPerThreadgroup: MTLSizeMake( threadConfig.numThreadsPerGroup, 1, 1 )
59 | )
60 | encoder!.endEncoding()
61 | commandBuffer.commit()
62 | commandBuffer.waitUntilCompleted()
63 | }
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/SDFont/SDFont.xcodeproj/xcshareddata/xcschemes/SDFont.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
34 |
40 |
41 |
42 |
43 |
44 |
54 |
55 |
61 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/SDFont/SDFont.xcodeproj/xcshareddata/xcschemes/sdfontgen.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
44 |
46 |
52 |
53 |
54 |
55 |
61 |
63 |
69 |
70 |
71 |
72 |
74 |
75 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/SWOpeningRoll/SWOpeningRoll.xcodeproj/xcshareddata/xcschemes/SWOpeningRollios.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/SWOpeningRoll/SWOpeningRoll.xcodeproj/xcshareddata/xcschemes/SWOpeningRollmacos.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontDownSampler.swift:
--------------------------------------------------------------------------------
1 | import MetalKit
2 |
3 | class Downsampler : MetalComputeBase {
4 |
5 | struct Config {
6 |
7 | // per-glyph local upsampled region
8 | var sideLenSrc : Int32
9 | var widthSrc : Int32
10 | var heightSrc : Int32
11 |
12 | // output drawing area
13 | var sideLenDst : Int32
14 | var widthDst : Int32
15 | var heightDst : Int32
16 | var originXDst : Int32
17 | var originYDst : Int32
18 |
19 | var upSamplingFactor : Int32
20 | var spreadThickness : Float
21 | var flipY : Int32
22 |
23 | init( rowLenSrc : Int32, rowLenDst : Int32, upSamplingFactor: Int32, spreadThickness : Float , flipY: Bool ) {
24 |
25 | self.sideLenSrc = rowLenSrc
26 | self.sideLenDst = rowLenDst
27 | self.upSamplingFactor = upSamplingFactor
28 | self.spreadThickness = spreadThickness
29 |
30 | self.widthSrc = 0
31 | self.heightSrc = 0
32 | self.widthDst = 0
33 | self.heightDst = 0
34 | self.originXDst = 0
35 | self.originYDst = 0
36 | self.flipY = flipY ? 1 : 0
37 | }
38 | }
39 |
40 | var bufferSrc : MTLBuffer
41 | var bufferDst : MTLBuffer
42 | var config : Config
43 |
44 | init(
45 | device : MTLDevice,
46 | bufferSrc : MTLBuffer,
47 | bufferSrcSideLen : Int,
48 | bufferDst : MTLBuffer,
49 | bufferDstSideLen : Int,
50 | upSamplingFactor : Int,
51 | spreadThickness : Float,
52 | flipY : Bool
53 | ) {
54 | self.bufferSrc = bufferSrc
55 | self.bufferDst = bufferDst
56 |
57 | config = Config(
58 | rowLenSrc : Int32(bufferSrcSideLen),
59 | rowLenDst : Int32(bufferDstSideLen),
60 | upSamplingFactor : Int32(upSamplingFactor),
61 | spreadThickness : spreadThickness,
62 | flipY : flipY
63 | )
64 |
65 | super.init( device : device )
66 |
67 | createPipelineState( functionName : "downsample")
68 | }
69 |
70 | func perform(
71 | originalBounds : SignedDistanceFontGlyphBounds,
72 | upSampledLocalizedBounds : SignedDistanceFontGlyphBounds
73 | ) {
74 |
75 | config.widthSrc = Int32( upSampledLocalizedBounds.outer.size.width )
76 | config.heightSrc = Int32( upSampledLocalizedBounds.outer.size.height )
77 | config.widthDst = Int32( originalBounds.outer.size.width )
78 | config.heightDst = Int32( originalBounds.outer.size.height )
79 | config.originXDst = Int32( originalBounds.outer.origin.x )
80 | config.originYDst = Int32( originalBounds.outer.origin.y )
81 |
82 | guard let commandBuffer = queue!.makeCommandBuffer() else {
83 | print ("ERROR: Cannot make command buffer for Downsampler.")
84 | return
85 | }
86 |
87 | let encoder = commandBuffer.makeComputeCommandEncoder()
88 |
89 | encoder!.setComputePipelineState( pipelineState! )
90 |
91 | encoder!.setBytes( &config, length: MemoryLayout.stride, index: 0 )
92 | encoder!.setBuffer( bufferSrc, offset: 0, index: 1 )
93 | encoder!.setBuffer( bufferDst, offset: 0, index: 2 )
94 |
95 | let threadConfig = MetalComputeBase.getThreadConfiguration( numThreads: Int( config.widthDst * config.heightDst ) )
96 | encoder!.dispatchThreadgroups(
97 | MTLSizeMake( threadConfig.numGroupsPerGrid, 1, 1 ),
98 | threadsPerThreadgroup: MTLSizeMake( threadConfig.numThreadsPerGroup, 1, 1 )
99 | )
100 | encoder!.endEncoding()
101 | commandBuffer.commit()
102 | commandBuffer.waitUntilCompleted()
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontBinaryPixmapGenerator.swift:
--------------------------------------------------------------------------------
1 | import MetalKit
2 |
3 | class SDFontBinaryPixmapGenerator {
4 |
5 | static let EPSILON = 1.0e-8
6 | let fontName : CFString
7 | let fontSize : CGFloat
8 | var cgFont : CGFont?
9 | let drawAreaSideLen : Int
10 | let flipY : Bool
11 |
12 | var pixelBuffer : MTLBuffer!
13 | var context : CGContext!
14 |
15 | init( device: MTLDevice, fontName: CFString, fontSize : CGFloat, drawAreaSideLen : Int, flipY : Bool ) {
16 |
17 | self.fontName = fontName
18 | self.fontSize = fontSize
19 | self.cgFont = CGFont( fontName )
20 |
21 | self.drawAreaSideLen = drawAreaSideLen
22 | self.flipY = flipY
23 |
24 | guard let pixelBuffer = device.makeBuffer( length: MemoryLayout.stride * drawAreaSideLen * drawAreaSideLen, options: .storageModeShared )
25 | else {
26 | print ( "ERROR: MTLBuffer cannot be made for the Pixmap of size \(drawAreaSideLen)" )
27 | return
28 | }
29 | self.pixelBuffer = pixelBuffer
30 |
31 | let colorSpace : CGColorSpace = CGColorSpaceCreateDeviceGray();
32 | let bitmapInfo = CGBitmapInfo( rawValue: CGImageAlphaInfo.none.rawValue
33 | | CGImageByteOrderInfo.orderDefault.rawValue
34 | | CGImagePixelFormatInfo.packed.rawValue
35 | )
36 |
37 | guard let context = CGContext(
38 | data: pixelBuffer.contents(),
39 | width: drawAreaSideLen,
40 | height: drawAreaSideLen,
41 | bitsPerComponent: 8,
42 | bytesPerRow: drawAreaSideLen,
43 | space: colorSpace,
44 | bitmapInfo: bitmapInfo.rawValue
45 | ) else {
46 | print ( "ERROR: CGContext cannot be made for the Pixmap of size \(drawAreaSideLen)" )
47 | return
48 | }
49 | context.setAllowsAntialiasing(false)
50 | self.context = context
51 |
52 | if flipY {
53 | self.context.translateBy( x: 0, y: CGFloat(drawAreaSideLen) )
54 | self.context.scaleBy( x: 1, y: -1 )
55 | }
56 | }
57 |
58 | func generatePixmap( forGlyph : CGGlyph, fontSize : CGFloat, glyphBounds : SignedDistanceFontGlyphBounds ) {
59 |
60 | var g = forGlyph
61 |
62 | context.setFillColor(red: 0, green: 0, blue: 0, alpha: 1)
63 | context.fill( CGRect( x: 0, y: 0, width: CGFloat( drawAreaSideLen ), height: CGFloat( drawAreaSideLen ) ) )
64 | context.setFillColor( red: 1, green: 1, blue: 1, alpha: 1 )
65 |
66 | var bboxes = UnsafeMutablePointer.allocate( capacity: 1 )
67 | var advances = UnsafeMutablePointer.allocate( capacity: 1 )
68 |
69 | if ( !cgFont!.getGlyphBBoxes( glyphs: [g], count: 1, bboxes: bboxes ) ) {
70 | print ( "WARNING: Cant't retrieve BBox for glyph [\(g)].")
71 | return
72 | }
73 | if ( !cgFont!.getGlyphAdvances(glyphs: [g], count: 1, advances: advances ) ) {
74 | print ( "WARNING: Cant't retrieve advances for glyph [\(g)].")
75 | return
76 | }
77 |
78 | let fontScaleFactor = fontSize / CGFloat( cgFont!.unitsPerEm )
79 |
80 | let rect = CGRect(
81 | origin: CGPoint( x: bboxes[0].origin.x * fontScaleFactor, y: bboxes[0].origin.y * fontScaleFactor ),
82 | size: CGSize( width: bboxes[0].width * fontScaleFactor, height: bboxes[0].height * fontScaleFactor )
83 | )
84 |
85 | let normalizedAdvance = CGFloat( advances[0] ) / CGFloat( cgFont!.unitsPerEm )
86 |
87 | if abs( glyphBounds.inner.size.width - rect.size.width ) >= Self.EPSILON
88 | || abs( glyphBounds.inner.size.height - rect.size.height ) >= Self.EPSILON {
89 |
90 | print ( "WARNING: Boundary requested: \(glyphBounds.inner.size) is different from the boundary from CGFont: \(rect.size) for glyph [\(g)]")
91 | }
92 |
93 | var translation = CGAffineTransform( translationX: glyphBounds.inner.origin.x - rect.origin.x,
94 | y: glyphBounds.inner.origin.y - rect.origin.y )
95 | context.setShouldSmoothFonts( false )
96 | context.setShouldSubpixelPositionFonts( false )
97 | context.setShouldSubpixelQuantizeFonts( false )
98 | context.textMatrix = translation
99 | context.setFont( cgFont! )
100 | context.setFontSize( fontSize )
101 | context.showGlyphs( [g], at: [CGPoint( x: 0, y: 0 )] )
102 | }
103 | }
104 |
105 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/ARCoordinator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 | import ARKit
4 |
5 | class ARCoordinator: NSObject, MTKViewDelegate, ARSessionDelegate {
6 |
7 | var arSession: ARSession!
8 |
9 | var device: MTLDevice!
10 | var renderSemaphore: DispatchSemaphore
11 | var commandQueue: MTLCommandQueue!
12 |
13 | var previousFrameSize: CGSize // to detect screen dimension change. SwiftUI does not always call mtkView().
14 | var viewportSize: CGSize // current viewport size
15 |
16 | let worldManager: WorldManager
17 | let photoImageRenderer: PhotoImageRenderer
18 |
19 | init( worldManager: WorldManager, device : MTLDevice ) {
20 |
21 | self.device = worldManager.device
22 | self.renderSemaphore = DispatchSemaphore( value: 1 )
23 |
24 | guard let commandQueue = device.makeCommandQueue()
25 | else {
26 | fatalError("GPU not available")
27 | }
28 | self.commandQueue = commandQueue
29 | self.worldManager = worldManager
30 | self.photoImageRenderer = PhotoImageRenderer( device: device )
31 | self.previousFrameSize = CGSize( width: 0.0, height: 0.0 )
32 | self.viewportSize = CGSize()
33 |
34 | super.init()
35 |
36 | arSession = ARSession()
37 | arSession.delegate = self
38 | }
39 |
40 | /// 2nd part of init. It must wait until device and the pixel format become available in MTKView
41 | func createPipelineStates( mtkView: MTKView ) {
42 | worldManager.createPipelineStates( mtkView: mtkView )
43 | }
44 |
45 | // MARK: - MTKViewDelegate
46 |
47 | func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize ) {
48 |
49 | let sizeInPixels = CGSize( width: size.width * view.contentScaleFactor, height: size.height * view.contentScaleFactor )
50 |
51 | worldManager.updateScreenSizes( size: sizeInPixels )
52 |
53 | photoImageRenderer.arrangeMetalForCameraImage( view: view )
54 |
55 | let arConfiguration = ARWorldTrackingConfiguration()
56 |
57 | arSession.run( arConfiguration )
58 | }
59 |
60 | func draw( in view: MTKView ) {
61 |
62 | // update the world
63 |
64 | guard let currentFrame = arSession.currentFrame else {
65 | return
66 | }
67 |
68 | guard let drawable = view.currentDrawable
69 | else {
70 | return
71 | }
72 |
73 | if previousFrameSize != view.frame.size {
74 |
75 | viewportSize = view.frame.size
76 | previousFrameSize = view.frame.size
77 | self.mtkView( view, drawableSizeWillChange: view.frame.size )
78 | photoImageRenderer.updateImagePlane(frame: currentFrame, viewportSize : viewportSize )
79 | }
80 |
81 | photoImageRenderer.updateCapturedImageTextures( frame: currentFrame )
82 |
83 | // MARK: - Device Orientation
84 | let V = arSession.currentFrame!.camera.viewMatrix( for: .portrait )
85 | let P = arSession.currentFrame!.camera.projectionMatrix( for: .portrait, viewportSize: viewportSize, zNear: 0.001, zFar: 10000.0 )
86 | // Correction matrix from UIDeviceOrientation.landscapeRight to UIDeviceOrientation.portrait,
87 | // as camera.transform is based on .landscapeRight.
88 | var C = float4x4.identity()
89 | C.columns.0 = SIMD4( 0.0, 1.0, 0.0, 0.0)
90 | C.columns.1 = SIMD4( -1.0, 0.0, 0.0, 0.0)
91 | let M = C * arSession.currentFrame!.camera.transform
92 |
93 | worldManager.updateViewAndProjectionMatrixForCamera( viewMatrix: V, projectionMatrix: P, transform: M )
94 | worldManager.updateWorld()
95 |
96 | // render the world
97 |
98 | guard
99 | let commandBuffer = commandQueue.makeCommandBuffer()
100 | else {
101 | return
102 | }
103 |
104 | commandBuffer.addCompletedHandler { _ in self.renderSemaphore.signal() }
105 | renderSemaphore.wait()
106 |
107 | photoImageRenderer.draw( in : view, commandBuffer: commandBuffer )
108 |
109 | worldManager.draw( in : view, commandBuffer: commandBuffer )
110 |
111 | commandBuffer.present( drawable )
112 | commandBuffer.commit()
113 | }
114 |
115 | // MARK: - ARSessionDelegate
116 |
117 | func session(_ session: ARSession, didUpdate frame: ARFrame){
118 |
119 | }
120 |
121 | func session(_ session: ARSession, didAdd anchors: [ARAnchor]){
122 |
123 | }
124 |
125 | func session(_ session: ARSession, didUpdate anchors: [ARAnchor]){
126 |
127 | }
128 |
129 | func session(_ session: ARSession, didRemove anchors: [ARAnchor]){
130 |
131 | }
132 | }
133 |
134 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/demo/AnimationConstants.swift:
--------------------------------------------------------------------------------
1 | import MetalKit
2 |
3 | class AnimationConstants {
4 |
5 | static let Helvetica = "Helvetica"
6 | static let HelveticaBold = "Helvetica-Bold"
7 | static let SDTextureSize = 1024
8 |
9 | // MARK: Text 1
10 |
11 | static let Text1 = "A long time ago, in a gallaxy far\nfar away...."
12 | static let FontName1 = Helvetica
13 | static let ColorOn1 = SIMD4(0.2, 0.6, 1.0, 1.0)
14 | static let ColorOff1 = SIMD4(0.2, 0.6, 1.0, 0.0)
15 | static let Dimension1 = CGSize( width: 1.2, height: 0.24 )
16 | static let Scale1 = 250.0
17 | static let Translation1 = SIMD3( x: 0.065, y: -0.1, z: -0.15 )
18 | static let Translation1Demo = SIMD3( x: 0.065, y: 0.03, z: -0.15 )
19 |
20 | static let TemporalPoints1 : [TimeInterval] = [ 0.0, 1.0, 2.0, 4.0, 5.0 ]
21 | static let SpacialPoints1 : [SIMD3] = [ Translation1, Translation1, Translation1, Translation1, Translation1 ]
22 | static let Colors1 : [SIMD4] = [ ColorOff1, ColorOff1, ColorOn1, ColorOn1, ColorOff1 ]
23 |
24 | static var SDFontUniform1 = UniformSDFont(
25 | foregroundColor : ColorOn1,
26 | funcType : UniformSDFont.FunctionType.SDFONT_SMOOTH_STEP,
27 | point1 : 0.2,
28 | point2 : 0.9,
29 | width : 0.0
30 | )
31 |
32 | // MARK: Text 2
33 |
34 | static let Text2 = "STAR\nWARS"
35 | static let FontName2 = HelveticaBold
36 | static let ColorOn2 = SIMD4( 1.0, 0.8, 0.0, 1.0 )
37 | static let ColorOff2 = SIMD4( 1.0, 0.8, 0.0, 0.0 )
38 | static let Dimension2 = CGSize( width: 2.0, height: 1.2 )
39 | static let Scale2 = 25.0
40 | static let Translation2_1 = SIMD3( x: 0.0, y: 0.0, z: 0.0 )
41 | static let Translation2_2 = SIMD3( x: 0.0, y: 0.0, z: -10.0 )
42 | static let Translation2_3 = SIMD3( x: 0.0, y: 0.0, z: -15.0 )
43 | static let Translation2Demo = SIMD3( x: 0.0, y: 0.0, z: -1.5 )
44 |
45 | static let TemporalPoints2 : [TimeInterval] = [ 0.0, 5.99, 6.0, 14.0, 17.0, ]
46 | static let SpacialPoints2 : [SIMD3] = [ Translation2_1, Translation2_1, Translation2_1, Translation2_2, Translation2_3 ]
47 | static let Colors2 : [SIMD4] = [ ColorOff2, ColorOff2, ColorOn2, ColorOn2, ColorOff2 ]
48 |
49 | static var SDFontUniform2 = UniformSDFont(
50 | foregroundColor : ColorOn2,
51 | funcType : UniformSDFont.FunctionType.SDFONT_TRAPEZOID,
52 | point1 : 0.55,
53 | point2 : 0.65,
54 | width : 0.1
55 | )
56 |
57 | // MARK: Text 3
58 |
59 | static let Text3 =
60 | """
61 | EPISODE IV
62 |
63 | It is a period of civil war.
64 | Rebel spaceships, striking
65 | from a hidden base, have won
66 | their first victory against
67 | the evil Galactic Empire.
68 |
69 | During the battle, Rebel
70 | spies managed to steal secret
71 | plans to the Empire's
72 | ultimate weapon, the DEATH
73 | STAR, an armored space
74 | station with enough power
75 | to destroy an entire planet.
76 |
77 | Pursued by the Empire's
78 | sinister agents, Princess
79 | Leia races home aboard her
80 | starship, custodian of the
81 | stolen plans that can save her
82 | people and restore
83 | freedom to the galaxy....
84 | """
85 | static let FontName3 = Helvetica
86 | static let ColorOn3 = SIMD4( 1.0, 0.8, 0.0, 1.0 )
87 | static let ColorOff3 = SIMD4( 1.0, 0.8, 0.0, 0.0 )
88 | static let Dimension3 = CGSize( width: 0.6, height: 1.0 )
89 | static let Scale3 = 500.0
90 | static let Translation3_1 = SIMD3( x: 0.0, y: -0.06, z: 0.6 )
91 | static let Translation3_2 = SIMD3( x: 0.0, y: -0.06, z: -0.5 + 0.6 )
92 | static let Translation3_3 = SIMD3( x: 0.0, y: -0.06, z: -0.5 - 0.25 + 0.6 )
93 | static let Translation3Demo = SIMD3( x: 0.0, y: -0.06, z: 0.1 )
94 |
95 | static let TemporalPoints3 : [TimeInterval] = [ 0.0, 7.99, 8.0, 18.0, 23.0, ]
96 | static let SpacialPoints3 : [SIMD3] = [ Translation3_1, Translation3_1, Translation3_1, Translation3_2, Translation3_3 ]
97 | static let Colors3 : [SIMD4] = [ ColorOff3, ColorOff3, ColorOn3, ColorOn3, ColorOff3 ]
98 |
99 | static var SDFontUniform3 = UniformSDFont(
100 | foregroundColor : ColorOn3,
101 | funcType : UniformSDFont.FunctionType.SDFONT_STEP,
102 | point1 : 0.49,
103 | point2 : 0.51,
104 | width : 0.1
105 | )
106 | }
107 |
--------------------------------------------------------------------------------
/doc/generate_plots.py:
--------------------------------------------------------------------------------
1 | import math
2 | import numpy as np
3 | import matplotlib.pyplot as plt
4 |
5 |
6 | def through(x):
7 | return x
8 |
9 |
10 | def smooth_step(e0, e1, x):
11 | x = min( 1.0, max( 0.0, (x - e0) / (e1 - e0) ) )
12 | return x * x * x * (x * (x * 6 - 15) + 10);
13 |
14 | def step(c, x):
15 | if x < c:
16 | return 0.0
17 | else:
18 | return 1.0
19 |
20 | def slope_step( c, w, x ):
21 | if x < c - w * 0.5:
22 | return 0.0
23 |
24 | elif x < c + w * 0.5:
25 | return (x - ( c - w * 0.5 ) ) / w
26 |
27 | else:
28 | return 1.0
29 |
30 | def trapezoid( e0, e1, w, x ):
31 | if x < e0 - w:
32 | return 0.0
33 |
34 | elif x < e0:
35 | return ( x - (e0 - w))/w
36 |
37 | elif x < e1:
38 | return 1.0
39 |
40 | elif x < e1 + w:
41 | return 1.0 - (x - e1) /w
42 |
43 | else:
44 | return 0.0
45 |
46 |
47 | def twin_peaks( e0, e1, w, x ):
48 | hw = 0.5 * w
49 | if x < e0 - hw:
50 | return 0.0;
51 |
52 | elif x < e0:
53 | return (x - (e0 - hw))/ hw
54 |
55 | elif x < e0 + hw:
56 | return 1.0 - (x - e0) / hw
57 |
58 | if x < e1 - hw:
59 | return 0.0;
60 |
61 | elif x < e1:
62 | return (x - (e1 - hw))/ hw
63 |
64 | elif x < e1 + hw:
65 | return 1.0 - (x - e1) / hw
66 |
67 | else:
68 | return 0.0;
69 |
70 | def halo( e0, e1, w, x ):
71 | if x > e1:
72 | return 0.0
73 | else:
74 | return slope_step( e0, w, x )
75 |
76 |
77 | xs = np.arange( start=0.0, stop=1.001, step=0.001 )
78 |
79 |
80 | ys = []
81 | for x in xs:
82 | ys.append( x )
83 | ys = np.array(ys)
84 | plt.plot( xs, ys )
85 | plt.xlabel( 'x' )
86 | plt.ylabel( 'pass_thgourh(x)' )
87 | plt.title( 'pass through' )
88 | plt.savefig( 'plot_pass_through.png' )
89 | plt.clf()
90 |
91 | ys = []
92 | for x in xs:
93 | ys.append( step(0.5, x) )
94 | ys = np.array(ys)
95 | plt.plot( xs, ys )
96 | plt.text( 0.52, 0.2, 'edge' )
97 | plt.xlabel( 'x' )
98 | plt.ylabel( 'step(x)' )
99 | plt.title( 'step' )
100 | plt.savefig( 'plot_step.png' )
101 | plt.clf()
102 |
103 | ys = []
104 | for x in xs:
105 | ys.append( smooth_step(0.2, 0.8, x) )
106 | ys = np.array(ys)
107 | plt.plot( xs, ys )
108 | plt.plot( [0.2, 0.2], [0.0, 1.0], '--' )
109 | plt.plot( [0.8, 0.8], [0.0, 1.0], '--' )
110 | plt.text( 0.22, 0.2, 'edge0' )
111 | plt.text( 0.82, 0.2, 'edge1' )
112 | plt.xlabel( 'x' )
113 | plt.ylabel( 'smooth_step(x)' )
114 | plt.title( 'smooth step' )
115 | plt.savefig( 'plot_smooth_step.png' )
116 | plt.clf()
117 |
118 | ys = []
119 | for x in xs:
120 | ys.append( slope_step(0.5, 0.2, x) )
121 | ys = np.array(ys)
122 | plt.plot( xs, ys )
123 | plt.plot( [0.5, 0.5], [0.0, 1.0], '--' )
124 | plt.plot( [0.4, 0.6], [0.0, 0.0], '--' )
125 | plt.text( 0.51, 0.2, 'edge' )
126 | plt.text( 0.6, 0.01, 'width' )
127 | plt.xlabel( 'x' )
128 | plt.ylabel( 'slope_step(x)' )
129 | plt.title( 'slope step' )
130 | plt.savefig( 'plot_slope_step.png' )
131 | plt.clf()
132 |
133 |
134 | ys = []
135 | for x in xs:
136 | ys.append( trapezoid( 0.3, 0.7, 0.2, x ) )
137 | ys = np.array(ys)
138 | plt.plot( xs, ys )
139 | plt.plot( [0.3, 0.3], [0.0, 1.0], '--' )
140 | plt.plot( [0.7, 0.7], [0.0, 1.0], '--' )
141 | plt.plot( [0.1, 0.3], [0.0, 0.0], '--' )
142 | plt.plot( [0.7, 0.9], [0.0, 0.0], '--' )
143 | plt.text( 0.31, 0.2, 'edge0' )
144 | plt.text( 0.71, 0.2, 'edge1' )
145 | plt.text( 0.15, 0.01, 'width' )
146 | plt.text( 0.75, 0.01, 'width' )
147 | plt.xlabel( 'x' )
148 | plt.ylabel( 'trapezoid(x)' )
149 | plt.title( 'trapezoid' )
150 | plt.savefig( 'plot_trapezoid.png' )
151 | plt.clf()
152 |
153 |
154 | ys = []
155 | for x in xs:
156 | ys.append( twin_peaks( 0.3, 0.7, 0.2, x ) )
157 | ys = np.array(ys)
158 | plt.plot( xs, ys )
159 | plt.plot( [0.3, 0.3], [0.0, 1.0], '--' )
160 | plt.plot( [0.7, 0.7], [0.0, 1.0], '--' )
161 | plt.plot( [0.2, 0.4], [0.0, 0.0], '--' )
162 | plt.plot( [0.6, 0.8], [0.0, 0.0], '--' )
163 | plt.text( 0.31, 0.2, 'edge0' )
164 | plt.text( 0.71, 0.2, 'edge1' )
165 | plt.text( 0.25, 0.01, 'width' )
166 | plt.text( 0.65, 0.01, 'width' )
167 | plt.xlabel( 'x' )
168 | plt.ylabel( 'twin_peaks(x)' )
169 | plt.title( 'twin peaks' )
170 | plt.savefig( 'plot_twin_peaks.png' )
171 | plt.clf()
172 |
173 |
174 | ys = []
175 | for x in xs:
176 | ys.append( halo( 0.5, 0.65, 0.6, x ) )
177 | ys = np.array(ys)
178 | plt.plot( xs, ys )
179 | plt.plot( [0.5, 0.5 ], [0.0, 1.0], '--' )
180 | plt.plot( [0.65, 0.65], [(1.0/0.6)*0.65 - 0.2/0.6, 1.0], '--' )
181 | plt.plot( [0.65, 0.8 ], [(1.0/0.6)*0.65 - 0.2/0.6, 1.0], '--' )
182 | plt.plot( [0.2, 0.8], [0.01, 0.01], '--' )
183 |
184 | plt.text( 0.51, 0.2, 'edge0' )
185 | plt.text( 0.66, 0.2, 'cutoff' )
186 | plt.text( 0.25, 0.01, 'width' )
187 | plt.xlabel( 'x' )
188 | plt.ylabel( 'halo(x)' )
189 | plt.title( 'halo' )
190 | plt.savefig( 'plot_halo.png' )
191 | plt.clf()
192 |
--------------------------------------------------------------------------------
/SWOpeningRoll/shared/AnimationConstants.swift:
--------------------------------------------------------------------------------
1 | import MetalKit
2 |
3 | class AnimationConstants {
4 |
5 | static let Helvetica = "Helvetica"
6 | static let HelveticaBold = "Helvetica-Bold"
7 | static let SDTextureSize = 1024
8 |
9 | // MARK: Text 1
10 |
11 | static let Text1 = "A long time ago, in a gallaxy far\nfar away...."
12 | static let FontName1 = Helvetica
13 | static let ColorOn1 = SIMD4(0.2, 0.6, 1.0, 1.0)
14 | static let ColorOff1 = SIMD4(0.2, 0.6, 1.0, 0.0)
15 | static let Dimension1 = CGSize( width: 0.3, height: 0.06 )
16 | static let Scale1 = 1000.0
17 | static let Translation1 = SIMD3( x: 0.065, y: -0.02, z: -0.15 )
18 | static let Translation1Demo = SIMD3( x: 0.065, y: 0.03, z: -0.15 )
19 |
20 | static let TemporalPoints1 : [TimeInterval] = [ 0.0, 1.0, 2.0, 4.0, 5.0 ]
21 | static let SpacialPoints1 : [SIMD3] = [ Translation1, Translation1, Translation1, Translation1, Translation1 ]
22 | static let Colors1 : [SIMD4] = [ ColorOff1, ColorOff1, ColorOn1, ColorOn1, ColorOff1 ]
23 |
24 | static var SDFontUniform1 = UniformSDFont(
25 | foregroundColor : ColorOn1,
26 | funcType : UniformSDFont.FunctionType.SDFONT_SMOOTH_STEP,
27 | point1 : 0.2,
28 | point2 : 0.9,
29 | width : 0.0
30 | )
31 |
32 | // MARK: Text 2
33 |
34 | static let Text2 = "STAR\nWARS"
35 | static let FontName2 = HelveticaBold
36 | static let ColorOn2 = SIMD4( 1.0, 0.8, 0.0, 1.0 )
37 | static let ColorOff2 = SIMD4( 1.0, 0.8, 0.0, 0.0 )
38 | static let Dimension2 = CGSize( width: 1.0, height: 0.6 )
39 | static let Scale2 = 50.0
40 | static let Translation2_1 = SIMD3( x: 0.0, y: 0.0, z: 0.0 )
41 | static let Translation2_2 = SIMD3( x: 0.0, y: 0.0, z: -10.0 )
42 | static let Translation2_3 = SIMD3( x: 0.0, y: 0.0, z: -15.0 )
43 | static let Translation2Demo = SIMD3( x: 0.0, y: 0.0, z: -1.5 )
44 |
45 | static let TemporalPoints2 : [TimeInterval] = [ 0.0, 5.99, 6.0, 14.0, 17.0, ]
46 | static let SpacialPoints2 : [SIMD3] = [ Translation2_1, Translation2_1, Translation2_1, Translation2_2, Translation2_3 ]
47 | static let Colors2 : [SIMD4] = [ ColorOff2, ColorOff2, ColorOn2, ColorOn2, ColorOff2 ]
48 |
49 | static var SDFontUniform2 = UniformSDFont(
50 | foregroundColor : ColorOn2,
51 | funcType : UniformSDFont.FunctionType.SDFONT_TRAPEZOID,
52 | point1 : 0.55,
53 | point2 : 0.65,
54 | width : 0.1
55 | )
56 |
57 | // MARK: Text 3
58 |
59 | static let Text3 =
60 | """
61 | EPISODE IV
62 |
63 | It is a period of civil war.
64 | Rebel spaceships, striking
65 | from a hidden base, have won
66 | their first victory against
67 | the evil Galactic Empire.
68 |
69 | During the battle, Rebel
70 | spies managed to steal secret
71 | plans to the Empire's
72 | ultimate weapon, the DEATH
73 | STAR, an armored space
74 | station with enough power
75 | to destroy an entire planet.
76 |
77 | Pursued by the Empire's
78 | sinister agents, Princess
79 | Leia races home aboard her
80 | starship, custodian of the
81 | stolen plans that can save her
82 | people and restore
83 | freedom to the galaxy....
84 | """
85 | static let FontName3 = Helvetica
86 | static let ColorOn3 = SIMD4( 1.0, 0.8, 0.0, 1.0 )
87 | static let ColorOff3 = SIMD4( 1.0, 0.8, 0.0, 0.0 )
88 | static let Dimension3 = CGSize( width: 0.3, height: 0.5 )
89 | static let Scale3 = 1000.0
90 | static let Translation3_1 = SIMD3( x: 0.0, y: -0.03, z: 0.3 )
91 | static let Translation3_2 = SIMD3( x: 0.0, y: -0.03, z: -0.5 + 0.3 )
92 | static let Translation3_3 = SIMD3( x: 0.0, y: -0.03, z: -0.5 - 0.25 + 0.3 )
93 | static let Translation3Demo = SIMD3( x: 0.0, y: -0.03, z: 0.1 )
94 |
95 | static let TemporalPoints3 : [TimeInterval] = [ 0.0, 7.99, 8.0, 28.0, 38.0, ]
96 | static let SpacialPoints3 : [SIMD3] = [ Translation3_1, Translation3_1, Translation3_1, Translation3_2, Translation3_3 ]
97 | static let Colors3 : [SIMD4] = [ ColorOff3, ColorOff3, ColorOn3, ColorOn3, ColorOff3 ]
98 |
99 | static var SDFontUniform3 = UniformSDFont(
100 | foregroundColor : ColorOn3,
101 | funcType : UniformSDFont.FunctionType.SDFONT_STEP,
102 | point1 : 0.49,
103 | point2 : 0.51,
104 | width : 0.1
105 | )
106 | }
107 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/shaders/default_renderer.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include "render_uniforms.h"
3 | #include "render_vertex_ins.h"
4 | using namespace metal;
5 |
6 | constant int VertexBufferIndexUniformPerScene = 1;
7 | constant int VertexBufferIndexUniformPerInstance = 2;
8 | constant int FragmentBufferIndexUniformSDFont = 4;
9 | constant int TextureIndexSDFont = 3;
10 |
11 | struct VertexOut {
12 |
13 | float4 position [[ position ]];
14 | float2 uv;
15 |
16 | };
17 |
18 | vertex VertexOut vertex_position_uv(
19 |
20 | const VertexInPositionUV vertex_in [[ stage_in ]],
21 | constant UniformPerScene& per_scene [[ buffer( VertexBufferIndexUniformPerScene ) ]],
22 | constant UniformPerInstance* per_instance [[ buffer( VertexBufferIndexUniformPerInstance ) ]],
23 | const ushort instance_id [[ instance_id ]]
24 | ) {
25 | const float4 position = per_scene.projection_matrix
26 | * per_scene.view_matrix
27 | * per_instance[instance_id].model_matrix
28 | * vertex_in.position;
29 |
30 | const float2 uv = vertex_in.uv;
31 |
32 | VertexOut out {
33 |
34 | .position = position,
35 | .uv = uv
36 | };
37 |
38 | return out;
39 | }
40 |
41 | static inline float sdfont_step( const float v, const float pos )
42 | {
43 | if ( v < pos ) {
44 | return 0.0;
45 | }
46 | else {
47 | return 1.0;
48 | }
49 | }
50 |
51 | static inline float sdfont_slope_step( const float v, const float width, const float pos )
52 | {
53 | if ( v < pos - width * 0.5 ) {
54 | return 0.0;
55 | }
56 | else if (v < pos + width * 0.5 ) {
57 | return (v - ( pos - width * 0.5 ) ) / width;
58 | }
59 | else {
60 | return 1.0;
61 | }
62 | }
63 |
64 | static inline float sdfont_trapezoid( const float v, const float width, const float pos1, const float pos2 )
65 | {
66 | if ( v < pos1 - width ) {
67 | return 0.0;
68 | }
69 | else if (v < pos1 ) {
70 | return (v - (pos1 - width))/width;
71 | }
72 | else if (v < pos2 ) {
73 | return 1.0;
74 | }
75 | else if (v < pos2 + width ) {
76 | return 1.0 - (v - pos2) /width;
77 | }
78 | else {
79 | return 0.0;
80 | }
81 | }
82 |
83 | static inline float sdfont_twin_peaks( const float v, const float width, const float pos1, const float pos2 )
84 | {
85 | const float hw = 0.5 * width;
86 |
87 | if ( v < pos1 - hw ) {
88 | return 0.0;
89 | }
90 | else if (v < pos1 ) {
91 | return (v - (pos1 - hw)) / hw;
92 | }
93 | else if (v < pos1 + hw ) {
94 | return 1.0 - (v - pos1) / hw;
95 | }
96 | if ( v < pos2 - 0.5 * hw ) {
97 | return 0.0;
98 | }
99 |
100 | else if (v < pos2 ) {
101 | return (v - (pos2 - hw)) / hw;
102 | }
103 | else if (v < pos2 + hw ) {
104 | return 1.0 - (v - pos2) /hw;
105 | }
106 | else {
107 | return 0.0;
108 | }
109 | }
110 |
111 | static inline float sdfont_halo( const float v, const float width, const float pos1, const float pos2 )
112 | {
113 | if (v > pos2) {
114 | return 0.0; // cutoff at pos2
115 | }
116 | else {
117 | return sdfont_slope_step( v, width, pos1 );
118 | }
119 | }
120 |
121 | fragment float4 fragment_sdfont(
122 | VertexOut in [[ stage_in ]],
123 | constant UniformSDFont& sd_font [[ buffer( FragmentBufferIndexUniformSDFont ) ]],
124 | sampler texture_sampler [[ sampler( 0 ) ]],
125 | texture2d texture_sdfont [[ texture( TextureIndexSDFont ) ]]
126 | ) {
127 | float4 base_color = sd_font.foreground_color;
128 |
129 | float sampleDistance = texture_sdfont.sample(texture_sampler, in.uv).r;
130 |
131 | const auto func_type = (SDFontFunctionType)sd_font.func_type;
132 |
133 | float alpha = 0.0;
134 |
135 | switch (func_type) {
136 |
137 | case SDFONT_PASS_THROUGH:
138 | alpha = sampleDistance;
139 | break;
140 |
141 | case SDFONT_STEP:
142 | alpha = sdfont_step( sampleDistance, sd_font.point1 );
143 | break;
144 |
145 | case SDFONT_SMOOTH_STEP:
146 | alpha = smoothstep( sd_font.point1, sd_font.point2, sampleDistance );
147 | break;
148 |
149 | case SDFONT_SLOPE_STEP:
150 | alpha = sdfont_slope_step( sampleDistance, sd_font.width, sd_font.point1 );
151 | break;
152 |
153 | case SDFONT_TRAPEZOID:
154 | alpha = sdfont_trapezoid( sampleDistance, sd_font.width, sd_font.point1, sd_font.point2 );
155 | break;
156 |
157 | case SDFONT_TWIN_PEAKS:
158 | alpha = sdfont_twin_peaks( sampleDistance, sd_font.width, sd_font.point1, sd_font.point2 );
159 | break;
160 |
161 | case SDFONT_HALO:
162 | alpha = sdfont_halo( sampleDistance, sd_font.width, sd_font.point1, sd_font.point2 );
163 | break;
164 |
165 |
166 | }
167 | return float4(base_color.r, base_color.g, base_color.b, alpha * base_color.a );
168 | }
169 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/shaders/default_renderer.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include "render_uniforms.h"
3 | #include "render_vertex_ins.h"
4 | using namespace metal;
5 |
6 | constant int VertexBufferIndexUniformPerScene = 1;
7 | constant int VertexBufferIndexUniformPerInstance = 2;
8 | constant int FragmentBufferIndexUniformSDFont = 4;
9 | constant int TextureIndexSDFont = 3;
10 |
11 | struct VertexOut {
12 |
13 | float4 position [[ position ]];
14 | float2 uv;
15 |
16 | };
17 |
18 | vertex VertexOut vertex_position_uv(
19 |
20 | const VertexInPositionUV vertex_in [[ stage_in ]],
21 | constant UniformPerScene& per_scene [[ buffer( VertexBufferIndexUniformPerScene ) ]],
22 | constant UniformPerInstance* per_instance [[ buffer( VertexBufferIndexUniformPerInstance ) ]],
23 | const ushort instance_id [[ instance_id ]]
24 | ) {
25 | const float4 position = per_scene.projection_matrix
26 | * per_scene.view_matrix
27 | * per_instance[instance_id].model_matrix
28 | * vertex_in.position;
29 |
30 | const float2 uv = vertex_in.uv;
31 |
32 | VertexOut out {
33 |
34 | .position = position,
35 | .uv = uv
36 | };
37 |
38 | return out;
39 | }
40 |
41 | static inline float sdfont_step( const float v, const float pos )
42 | {
43 | if ( v < pos ) {
44 | return 0.0;
45 | }
46 | else {
47 | return 1.0;
48 | }
49 | }
50 |
51 | static inline float sdfont_slope_step( const float v, const float width, const float pos )
52 | {
53 | if ( v < pos - width * 0.5 ) {
54 | return 0.0;
55 | }
56 | else if (v < pos + width * 0.5 ) {
57 | return (v - ( pos - width * 0.5 ) ) / width;
58 | }
59 | else {
60 | return 1.0;
61 | }
62 | }
63 |
64 | static inline float sdfont_trapezoid( const float v, const float width, const float pos1, const float pos2 )
65 | {
66 | if ( v < pos1 - width ) {
67 | return 0.0;
68 | }
69 | else if (v < pos1 ) {
70 | return (v - (pos1 - width))/width;
71 | }
72 | else if (v < pos2 ) {
73 | return 1.0;
74 | }
75 | else if (v < pos2 + width ) {
76 | return 1.0 - (v - pos2) /width;
77 | }
78 | else {
79 | return 0.0;
80 | }
81 | }
82 |
83 | static inline float sdfont_twin_peaks( const float v, const float width, const float pos1, const float pos2 )
84 | {
85 | const float hw = 0.5 * width;
86 |
87 | if ( v < pos1 - hw ) {
88 | return 0.0;
89 | }
90 | else if (v < pos1 ) {
91 | return (v - (pos1 - hw)) / hw;
92 | }
93 | else if (v < pos1 + hw ) {
94 | return 1.0 - (v - pos1) / hw;
95 | }
96 | if ( v < pos2 - 0.5 * hw ) {
97 | return 0.0;
98 | }
99 |
100 | else if (v < pos2 ) {
101 | return (v - (pos2 - hw)) / hw;
102 | }
103 | else if (v < pos2 + hw ) {
104 | return 1.0 - (v - pos2) /hw;
105 | }
106 | else {
107 | return 0.0;
108 | }
109 | }
110 |
111 | static inline float sdfont_halo( const float v, const float width, const float pos1, const float pos2 )
112 | {
113 | if (v > pos2) {
114 | return 0.0; // cutoff at pos2
115 | }
116 | else {
117 | return sdfont_slope_step( v, width, pos1 );
118 | }
119 | }
120 |
121 | fragment float4 fragment_sdfont(
122 | VertexOut in [[ stage_in ]],
123 | constant UniformSDFont& sd_font [[ buffer( FragmentBufferIndexUniformSDFont ) ]],
124 | sampler texture_sampler [[ sampler( 0 ) ]],
125 | texture2d texture_sdfont [[ texture( TextureIndexSDFont ) ]]
126 | ) {
127 | float4 base_color = sd_font.foreground_color;
128 |
129 | float sampleDistance = texture_sdfont.sample(texture_sampler, in.uv).r;
130 |
131 | const auto func_type = (SDFontFunctionType)sd_font.func_type;
132 |
133 | float alpha = 0.0;
134 |
135 | switch (func_type) {
136 |
137 | case SDFONT_PASS_THROUGH:
138 | alpha = sampleDistance;
139 | break;
140 |
141 | case SDFONT_STEP:
142 | alpha = sdfont_step( sampleDistance, sd_font.point1 );
143 | break;
144 |
145 | case SDFONT_SMOOTH_STEP:
146 | alpha = smoothstep( sd_font.point1, sd_font.point2, sampleDistance );
147 | break;
148 |
149 | case SDFONT_SLOPE_STEP:
150 | alpha = sdfont_slope_step( sampleDistance, sd_font.width, sd_font.point1 );
151 | break;
152 |
153 | case SDFONT_TRAPEZOID:
154 | alpha = sdfont_trapezoid( sampleDistance, sd_font.width, sd_font.point1, sd_font.point2 );
155 | break;
156 |
157 | case SDFONT_TWIN_PEAKS:
158 | alpha = sdfont_twin_peaks( sampleDistance, sd_font.width, sd_font.point1, sd_font.point2 );
159 | break;
160 |
161 | case SDFONT_HALO:
162 | alpha = sdfont_halo( sampleDistance, sd_font.width, sd_font.point1, sd_font.point2 );
163 | break;
164 |
165 |
166 | }
167 | return float4(base_color.r, base_color.g, base_color.b, alpha * base_color.a );
168 | }
169 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/PerSceneRenderHelper.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | class PerSceneRenderHelper {
5 |
6 | static let FragmentFunctionNameSDFont = "fragment_sdfont"
7 | static let VertexFunctionNamePositionUV = "vertex_position_uv"
8 |
9 | static let SamplerStateMaxAnisotropy = 4
10 |
11 | static let VertexBufferIndexVertexIn = 0
12 | static let VertexBufferIndexUniformPerScene = 1
13 | static let VertexBufferIndexUniformPerInstance = 2
14 |
15 | static let FragmentBufferIndexUniformPerScene = 1
16 | static let FragmentBufferIndexUniformSDFont = 4
17 |
18 | static let TextureIndexSDFont = 3
19 |
20 | let device : MTLDevice
21 | var pipelineState : MTLRenderPipelineState?
22 | var depthStencilState : MTLDepthStencilState?
23 | let bufferUniformPerScene : MTLBuffer?
24 | var samplerState : MTLSamplerState?
25 |
26 | init(
27 | device : MTLDevice,
28 | bufferUniformPerScene : MTLBuffer
29 | ) {
30 | self.device = device
31 | self.bufferUniformPerScene = bufferUniformPerScene
32 | self.samplerState = Self.buildSamplerStateSDFont( device: device )
33 |
34 | let desc = MTLDepthStencilDescriptor()
35 | desc.depthCompareFunction = .always
36 | desc.isDepthWriteEnabled = false
37 | depthStencilState = device.makeDepthStencilState( descriptor: desc )
38 | }
39 |
40 |
41 | func createPipelineState( colorPixelFormat : MTLPixelFormat ) {
42 |
43 | let library = device.makeDefaultLibrary()
44 |
45 | let descriptor = MTLRenderPipelineDescriptor()
46 |
47 | descriptor.vertexFunction = library!.makeFunction( name: Self.VertexFunctionNamePositionUV )
48 | descriptor.fragmentFunction = library!.makeFunction( name: Self.FragmentFunctionNameSDFont )
49 |
50 | descriptor.vertexDescriptor = MTLVertexDescriptor.positionUV()
51 | descriptor.colorAttachments[0].pixelFormat = colorPixelFormat
52 |
53 | descriptor.colorAttachments[0].isBlendingEnabled = true
54 | descriptor.colorAttachments[0].rgbBlendOperation = .add
55 | descriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
56 | descriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
57 | descriptor.colorAttachments[0].alphaBlendOperation = .add;
58 | descriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha;
59 | descriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha;
60 |
61 | descriptor.depthAttachmentPixelFormat = .depth32Float
62 |
63 | do {
64 | pipelineState = try device.makeRenderPipelineState( descriptor: descriptor )
65 | } catch let error {
66 | fatalError( error.localizedDescription )
67 | }
68 | }
69 |
70 | func renderModelSDFont(
71 | encoder : MTLRenderCommandEncoder,
72 | bufferVertexIn : MTLBuffer,
73 | bufferUniformPerInstance : MTLBuffer,
74 | numInstances : Int,
75 | bufferIndices : MTLBuffer,
76 | numIndices : Int,
77 | bufferUniformSDFont : MTLBuffer,
78 | textureSDFont : MTLTexture,
79 | wireFrame : Bool = false
80 | ) {
81 | encoder.setRenderPipelineState( pipelineState! )
82 | encoder.setDepthStencilState( depthStencilState )
83 |
84 | encoder.setVertexBuffer( bufferVertexIn, offset: 0, index: Self.VertexBufferIndexVertexIn )
85 | encoder.setVertexBuffer( bufferUniformPerScene, offset: 0, index: Self.VertexBufferIndexUniformPerScene )
86 | encoder.setVertexBuffer( bufferUniformPerInstance, offset: 0, index: Self.VertexBufferIndexUniformPerInstance )
87 |
88 | encoder.setFragmentBuffer( bufferUniformSDFont, offset: 0, index: Self.FragmentBufferIndexUniformSDFont )
89 |
90 | encoder.setFragmentTexture( textureSDFont, index: Self.TextureIndexSDFont )
91 |
92 | encoder.setFragmentSamplerState( samplerState, index: 0 )
93 |
94 | if wireFrame {
95 | encoder.setTriangleFillMode( .lines )
96 | }
97 |
98 | encoder.drawIndexedPrimitives(
99 | type: .triangle,
100 | indexCount: numIndices,
101 | indexType: .uint32,
102 | indexBuffer: bufferIndices,
103 | indexBufferOffset: 0,
104 | instanceCount: numInstances,
105 | baseVertex: 0,
106 | baseInstance: 0
107 | )
108 |
109 | if wireFrame {
110 | encoder.setTriangleFillMode( .fill )
111 | }
112 | }
113 |
114 | static func buildSamplerStateSDFont( device: MTLDevice ) -> MTLSamplerState? {
115 |
116 | let descriptor = MTLSamplerDescriptor()
117 | descriptor.mipFilter = .linear
118 | descriptor.minFilter = .nearest;
119 | descriptor.magFilter = .linear;
120 | descriptor.sAddressMode = .clampToZero;
121 | descriptor.tAddressMode = .clampToZero;
122 | descriptor.maxAnisotropy = Self.SamplerStateMaxAnisotropy
123 |
124 | let samplerState = device.makeSamplerState( descriptor: descriptor )
125 |
126 | return samplerState
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/PerSceneRenderHelper.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | class PerSceneRenderHelper {
5 |
6 | static let FragmentFunctionNameSDFont = "fragment_sdfont"
7 | static let VertexFunctionNamePositionUV = "vertex_position_uv"
8 |
9 | static let SamplerStateMaxAnisotropy = 4
10 |
11 | static let VertexBufferIndexVertexIn = 0
12 | static let VertexBufferIndexUniformPerScene = 1
13 | static let VertexBufferIndexUniformPerInstance = 2
14 |
15 | static let FragmentBufferIndexUniformPerScene = 1
16 | static let FragmentBufferIndexUniformSDFont = 4
17 |
18 | static let TextureIndexSDFont = 3
19 |
20 | let device : MTLDevice
21 | var pipelineState : MTLRenderPipelineState?
22 | var depthStencilState : MTLDepthStencilState?
23 | let bufferUniformPerScene : MTLBuffer?
24 | var samplerState : MTLSamplerState?
25 |
26 | init(
27 | device : MTLDevice,
28 | bufferUniformPerScene : MTLBuffer
29 | ) {
30 | self.device = device
31 | self.bufferUniformPerScene = bufferUniformPerScene
32 | self.samplerState = Self.buildSamplerStateSDFont( device: device )
33 |
34 | let desc = MTLDepthStencilDescriptor()
35 | desc.depthCompareFunction = .always
36 | desc.isDepthWriteEnabled = false
37 | depthStencilState = device.makeDepthStencilState( descriptor: desc )
38 | }
39 |
40 |
41 | func createPipelineState( colorPixelFormat : MTLPixelFormat ) {
42 |
43 | let library = device.makeDefaultLibrary()
44 |
45 | let descriptor = MTLRenderPipelineDescriptor()
46 |
47 | descriptor.vertexFunction = library!.makeFunction( name: Self.VertexFunctionNamePositionUV )
48 | descriptor.fragmentFunction = library!.makeFunction( name: Self.FragmentFunctionNameSDFont )
49 |
50 | descriptor.vertexDescriptor = MTLVertexDescriptor.positionUV()
51 | descriptor.colorAttachments[0].pixelFormat = colorPixelFormat
52 |
53 | descriptor.colorAttachments[0].isBlendingEnabled = true
54 | descriptor.colorAttachments[0].rgbBlendOperation = .add
55 | descriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
56 | descriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
57 | descriptor.colorAttachments[0].alphaBlendOperation = .add;
58 | descriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha;
59 | descriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha;
60 |
61 | descriptor.depthAttachmentPixelFormat = .depth32Float
62 |
63 | do {
64 | pipelineState = try device.makeRenderPipelineState( descriptor: descriptor )
65 | } catch let error {
66 | fatalError( error.localizedDescription )
67 | }
68 | }
69 |
70 | func renderModelSDFont(
71 | encoder : MTLRenderCommandEncoder,
72 | bufferVertexIn : MTLBuffer,
73 | bufferUniformPerInstance : MTLBuffer,
74 | numInstances : Int,
75 | bufferIndices : MTLBuffer,
76 | numIndices : Int,
77 | bufferUniformSDFont : MTLBuffer,
78 | textureSDFont : MTLTexture,
79 | wireFrame : Bool = false
80 | ) {
81 | encoder.setRenderPipelineState( pipelineState! )
82 | encoder.setDepthStencilState( depthStencilState )
83 |
84 | encoder.setVertexBuffer( bufferVertexIn, offset: 0, index: Self.VertexBufferIndexVertexIn )
85 | encoder.setVertexBuffer( bufferUniformPerScene, offset: 0, index: Self.VertexBufferIndexUniformPerScene )
86 | encoder.setVertexBuffer( bufferUniformPerInstance, offset: 0, index: Self.VertexBufferIndexUniformPerInstance )
87 |
88 | encoder.setFragmentBuffer( bufferUniformSDFont, offset: 0, index: Self.FragmentBufferIndexUniformSDFont )
89 |
90 | encoder.setFragmentTexture( textureSDFont, index: Self.TextureIndexSDFont )
91 |
92 | encoder.setFragmentSamplerState( samplerState, index: 0 )
93 |
94 | if wireFrame {
95 | encoder.setTriangleFillMode( .lines )
96 | }
97 |
98 | encoder.drawIndexedPrimitives(
99 | type: .triangle,
100 | indexCount: numIndices,
101 | indexType: .uint32,
102 | indexBuffer: bufferIndices,
103 | indexBufferOffset: 0,
104 | instanceCount: numInstances,
105 | baseVertex: 0,
106 | baseInstance: 0
107 | )
108 |
109 | if wireFrame {
110 | encoder.setTriangleFillMode( .fill )
111 | }
112 | }
113 |
114 | static func buildSamplerStateSDFont( device: MTLDevice ) -> MTLSamplerState? {
115 |
116 | let descriptor = MTLSamplerDescriptor()
117 | descriptor.mipFilter = .linear
118 | descriptor.minFilter = .nearest;
119 | descriptor.magFilter = .linear;
120 | descriptor.sAddressMode = .clampToZero;
121 | descriptor.tAddressMode = .clampToZero;
122 | descriptor.maxAnisotropy = Self.SamplerStateMaxAnisotropy
123 |
124 | let samplerState = device.makeSamplerState( descriptor: descriptor )
125 |
126 | return samplerState
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/SWOpeningRoll/rendering/RenderUniforms.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | struct UniformPerInstance {
5 |
6 | // MemoryLayout.stride 64
7 | // MemoryLayout.size 64
8 | // MemoryLayout.alignment 16
9 |
10 | var modelMatrix : float4x4
11 |
12 | public init ( modelMatrix: matrix_float4x4 ) {
13 | self.modelMatrix = modelMatrix
14 | }
15 |
16 | public init () {
17 | modelMatrix = matrix_identity_float4x4
18 | }
19 |
20 | static func generateMTLBuffer( device: MTLDevice, instances : [Self] ) -> MTLBuffer? {
21 | return device.makeBuffer(bytes: instances, length: MemoryLayout.stride * instances.count, options: .storageModeShared )
22 | }
23 |
24 | static func updateMTLBuffer( buffer : MTLBuffer, instances : [Self] ) {
25 |
26 | let rawPointer = buffer.contents()
27 | let typedPointer = rawPointer.bindMemory( to: Self.self, capacity: MemoryLayout.stride )
28 | let bufferedPointer = UnsafeMutableBufferPointer( start: typedPointer, count: instances.count )
29 | for (index, instance) in instances.enumerated() {
30 | bufferedPointer[index] = instance
31 | }
32 | // NOTE: The following does not work with optimization as of XCode 14.2
33 | // buffer.contents().copyMemory( from: instances, byteCount: MemoryLayout.stride * instances.count )
34 | }
35 | }
36 |
37 | struct UniformPerScene {
38 |
39 | // MemoryLayout.stride 160
40 | // MemoryLayout.size 148
41 | // MemoryLayout.alignment 16
42 |
43 | var viewMatrix : float4x4
44 | var projectionMatrix : float4x4
45 | var cameraPosition : SIMD3
46 | var numLights : Int32
47 |
48 | public init( viewMatrix: float4x4, projectionMatrix: float4x4, cameraPosition : SIMD3, numLights: Int ) {
49 | self.viewMatrix = viewMatrix
50 | self.projectionMatrix = projectionMatrix
51 | self.cameraPosition = cameraPosition
52 | self.numLights = Int32(numLights)
53 | }
54 |
55 | public init() {
56 | viewMatrix = matrix_identity_float4x4
57 | projectionMatrix = matrix_identity_float4x4
58 | cameraPosition = SIMD3( 0.0, 0.0, 0.0 )
59 | self.numLights = 0
60 | }
61 |
62 | mutating func update( camera: Camera ) {
63 | viewMatrix = camera.viewMatrixLHS
64 | projectionMatrix = camera.projectionMatrix
65 | }
66 |
67 | static func generateMTLBuffer( device: MTLDevice, uniformPerScene : inout Self ) -> MTLBuffer? {
68 | return device.makeBuffer(bytes: &uniformPerScene, length: MemoryLayout.stride, options: .storageModeShared )
69 | }
70 |
71 | static func updateMTLBuffer( buffer : MTLBuffer, uniformPerScene : inout Self ) {
72 | let rawPointer = buffer.contents()
73 | let typedPointer = rawPointer.bindMemory( to: Self.self, capacity: MemoryLayout.stride )
74 | let bufferedPointer = UnsafeMutableBufferPointer( start: typedPointer, count: 1 )
75 | bufferedPointer[0] = uniformPerScene
76 | // NOTE: The following does not work with optimization as of XCode 14.2
77 | // buffer.contents().copyMemory( from: &uniformPerScene, byteCount: MemoryLayout.stride )
78 | }
79 | }
80 |
81 |
82 | struct UniformSDFont {
83 |
84 | enum FunctionType : Int32 {
85 | case SDFONT_PASS_THROUGH = 0
86 | case SDFONT_STEP = 1
87 | case SDFONT_SMOOTH_STEP = 2
88 | case SDFONT_SLOPE_STEP = 3
89 | case SDFONT_TRAPEZOID = 4
90 | case SDFONT_TWIN_PEAKS = 5
91 | case SDFONT_HALO = 6
92 | }
93 |
94 | var foregroundColor : SIMD4
95 | var funcType : Int32
96 | var width : Float
97 | var point1 : Float
98 | var point2 : Float
99 |
100 | init(
101 | foregroundColor : SIMD4,
102 | funcType : FunctionType,
103 | point1 : Float,
104 | point2 : Float,
105 | width : Float
106 | ) {
107 | self.foregroundColor = foregroundColor
108 | self.funcType = funcType.rawValue
109 | self.width = width
110 | self.point1 = point1
111 | self.point2 = point2
112 | }
113 |
114 | init() {
115 | self.foregroundColor = SIMD4(1.0, 1.0, 1.0, 1.0)
116 | self.funcType = FunctionType.SDFONT_PASS_THROUGH.rawValue
117 | self.width = 0
118 | self.point1 = 0
119 | self.point2 = 0
120 | }
121 |
122 | static func generateMTLBuffer( device: MTLDevice, v : inout Self ) -> MTLBuffer? {
123 | return device.makeBuffer(bytes: &v, length: MemoryLayout.stride, options: .storageModeShared )
124 | }
125 |
126 | static func updateMTLBuffer( buffer : MTLBuffer, v : inout Self ) {
127 | let rawPointer = buffer.contents()
128 | let typedPointer = rawPointer.bindMemory( to: Self.self, capacity: MemoryLayout.stride )
129 | let bufferedPointer = UnsafeMutableBufferPointer( start: typedPointer, count: 1 )
130 | bufferedPointer[0] = v
131 | // NOTE: The following does not work with optimization as of XCode 14.2
132 | // buffer.contents().copyMemory( from: &v, byteCount: MemoryLayout.stride )
133 | }
134 | }
135 |
136 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/rendering/RenderUniforms.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | struct UniformPerInstance {
5 |
6 | // MemoryLayout.stride 64
7 | // MemoryLayout.size 64
8 | // MemoryLayout.alignment 16
9 |
10 | var modelMatrix : float4x4
11 |
12 | public init ( modelMatrix: matrix_float4x4 ) {
13 | self.modelMatrix = modelMatrix
14 | }
15 |
16 | public init () {
17 | modelMatrix = matrix_identity_float4x4
18 | }
19 |
20 | static func generateMTLBuffer( device: MTLDevice, instances : [Self] ) -> MTLBuffer? {
21 | return device.makeBuffer(bytes: instances, length: MemoryLayout.stride * instances.count, options: .storageModeShared )
22 | }
23 |
24 | static func updateMTLBuffer( buffer : MTLBuffer, instances : [Self] ) {
25 |
26 | let rawPointer = buffer.contents()
27 | let typedPointer = rawPointer.bindMemory( to: Self.self, capacity: MemoryLayout.stride * instances.count )
28 | let bufferedPointer = UnsafeMutableBufferPointer( start: typedPointer, count: instances.count )
29 | for (index, instance) in instances.enumerated() {
30 | bufferedPointer[index] = instance
31 | }
32 | // NOTE: The following does not work with optimization as of XCode 14.2
33 | //buffer.contents().copyMemory( from: instances, byteCount: MemoryLayout.stride * instances.count )
34 | }
35 | }
36 |
37 | struct UniformPerScene {
38 |
39 | // MemoryLayout.stride 160
40 | // MemoryLayout.size 148
41 | // MemoryLayout.alignment 16
42 |
43 | var viewMatrix : float4x4
44 | var projectionMatrix : float4x4
45 | var cameraPosition : SIMD3
46 | var numLights : Int32
47 |
48 | public init( viewMatrix: float4x4, projectionMatrix: float4x4, cameraPosition : SIMD3, numLights: Int ) {
49 | self.viewMatrix = viewMatrix
50 | self.projectionMatrix = projectionMatrix
51 | self.cameraPosition = cameraPosition
52 | self.numLights = Int32(numLights)
53 | }
54 |
55 | public init() {
56 | viewMatrix = matrix_identity_float4x4
57 | projectionMatrix = matrix_identity_float4x4
58 | cameraPosition = SIMD3( 0.0, 0.0, 0.0 )
59 | self.numLights = 0
60 | }
61 |
62 | mutating func update( camera: Camera ) {
63 | viewMatrix = camera.viewMatrixLHS
64 | projectionMatrix = camera.projectionMatrix
65 | }
66 |
67 | static func generateMTLBuffer( device: MTLDevice, uniformPerScene : inout Self ) -> MTLBuffer? {
68 | return device.makeBuffer(bytes: &uniformPerScene, length: MemoryLayout.stride, options: .storageModeShared )
69 | }
70 |
71 | static func updateMTLBuffer( buffer : MTLBuffer, uniformPerScene : inout Self ) {
72 | let rawPointer = buffer.contents()
73 | let typedPointer = rawPointer.bindMemory( to: Self.self, capacity: MemoryLayout.stride )
74 | let bufferedPointer = UnsafeMutableBufferPointer( start: typedPointer, count: 1 )
75 | bufferedPointer[0] = uniformPerScene
76 | // NOTE: The following does not work with optimization as of XCode 14.2
77 | // buffer.contents().copyMemory( from: &uniformPerScene, byteCount: MemoryLayout.stride )
78 | }
79 | }
80 |
81 |
82 | struct UniformSDFont {
83 |
84 | enum FunctionType : Int32 {
85 | case SDFONT_PASS_THROUGH = 0
86 | case SDFONT_STEP = 1
87 | case SDFONT_SMOOTH_STEP = 2
88 | case SDFONT_SLOPE_STEP = 3
89 | case SDFONT_TRAPEZOID = 4
90 | case SDFONT_TWIN_PEAKS = 5
91 | case SDFONT_HALO = 6
92 | }
93 |
94 | var foregroundColor : SIMD4
95 | var funcType : Int32
96 | var width : Float
97 | var point1 : Float
98 | var point2 : Float
99 |
100 | init(
101 | foregroundColor : SIMD4,
102 | funcType : FunctionType,
103 | point1 : Float,
104 | point2 : Float,
105 | width : Float
106 | ) {
107 | self.foregroundColor = foregroundColor
108 | self.funcType = funcType.rawValue
109 | self.width = width
110 | self.point1 = point1
111 | self.point2 = point2
112 | }
113 |
114 | init() {
115 | self.foregroundColor = SIMD4(1.0, 1.0, 1.0, 1.0)
116 | self.funcType = FunctionType.SDFONT_PASS_THROUGH.rawValue
117 | self.width = 0
118 | self.point1 = 0
119 | self.point2 = 0
120 | }
121 |
122 | static func generateMTLBuffer( device: MTLDevice, v : inout Self ) -> MTLBuffer? {
123 | return device.makeBuffer(bytes: &v, length: MemoryLayout.stride, options: .storageModeShared )
124 | }
125 |
126 | static func updateMTLBuffer( buffer : MTLBuffer, v : inout Self ) {
127 | let rawPointer = buffer.contents()
128 | let typedPointer = rawPointer.bindMemory( to: Self.self, capacity: MemoryLayout.stride )
129 | let bufferedPointer = UnsafeMutableBufferPointer( start: typedPointer, count: 1 )
130 | bufferedPointer[0] = v
131 | // NOTE: The following does not work with optimization as of XCode 14.2
132 | // buffer.contents().copyMemory( from: &v, byteCount: MemoryLayout.stride )
133 | }
134 | }
135 |
136 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFont.metal:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace metal;
3 |
4 | typedef struct _ConfigGen {
5 | int draw_area_side_len; // number of pixels per row.
6 | float spread_thickness; // it detemines the rectangular area for searching signed distance.
7 | int width; // width of the glyph including the spread.
8 | int height; // height of the glyph including the spread.
9 | } ConfigGen;
10 |
11 |
12 | // Brute-force signed-distance finder. Each pixel gets one thread to calculate
13 | // the signed distance (distance from the closest pixel of the different value.)
14 | // Interpretation of the output float value:
15 | // * 0.5 <= v : the pixel is inside the glyph and v is the distance to the closest pixel outside the glyph
16 | // * -0.5 >= v : the pixel is outside the glyph and v is the distance to the closest pixel inside the glyph
17 | kernel void generate_signed_distance (
18 | device const ConfigGen& config [[ buffer(0)]],
19 | device const uint8_t* pixmap_buffer [[ buffer(1)]],
20 | device float* sd_buffer [[ buffer(2)]],
21 | const uint tid [[ thread_position_in_grid ]]
22 | ) {
23 | if ( (int)tid < config.width * config.height ) {
24 |
25 | const int base_i = tid % config.width;
26 | const int base_j = tid / config.width;
27 |
28 | const int v = pixmap_buffer[ base_j * config.draw_area_side_len + base_i ];
29 | const int spread = (int)config.spread_thickness;
30 |
31 | float sq_dist_min = (float)config.draw_area_side_len * (float)config.draw_area_side_len * 4.0; // arbitrary large value
32 |
33 | for ( int j = -1 * spread; j < spread; j++ ) {
34 |
35 | if ( base_j + j >= 0 && base_j + j < config.height ) {
36 |
37 | for ( int i = -1 * spread; i < spread; i++ ) {
38 |
39 | if ( base_i + i >= 0 && base_i + i < config.width ) {
40 |
41 | if ( (i != 0) || (j != 0) ) {
42 |
43 | const int v_other = pixmap_buffer[ ( base_j + j ) * config.draw_area_side_len + base_i + i ];
44 |
45 | if ( ( v >= 128 && v_other < 128 ) || ( v < 128 && v_other >= 128 ) ) {
46 |
47 | const float sq_dist = (float)( i * i + j * j );
48 |
49 | if ( sq_dist_min > sq_dist ) {
50 | sq_dist_min = sq_dist;
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
59 | const float normalized_min_dist = ( /*precise::*/sqrt(sq_dist_min) - 1.0 ) / config.spread_thickness;
60 | if ( v >= 128 ) {
61 | // inside glyph
62 | sd_buffer[ base_j * config.draw_area_side_len + base_i ] = 0.5 + normalized_min_dist / 2.0;
63 | }
64 | else {
65 | // outside glyph
66 | sd_buffer[ base_j * config.draw_area_side_len + base_i ] = 0.5 - normalized_min_dist / 2.0;
67 | }
68 | }
69 | }
70 |
71 | typedef struct _ConfigDownsample {
72 |
73 | // glyph-local upsampled space
74 | int side_len_src;
75 | int width_src;
76 | int height_src;
77 |
78 | // output texture space
79 | int side_len_dst;
80 | int width_dst;
81 | int height_dst;
82 | int origin_x_dst;
83 | int origin_y_dst;
84 |
85 | int upsampling_factor;
86 | float spread_thickness;
87 |
88 | int flip_y;
89 |
90 | } ConfigDownsample;
91 |
92 |
93 | // Each output value in uint8 is calculated from the square region of the upsampled signed distance values,
94 | // whose side length is config.upsampling_factor.
95 | // The output value is the average of the square resion, re-scaled to the down-sampled space.
96 | kernel void downsample (
97 | device const ConfigDownsample& config [[ buffer(0)]],
98 | device const float* sd_buffer_src [[ buffer(1)]],
99 | device uint8_t* sd_buffer_dst [[ buffer(2)]],
100 | const uint tid [[ thread_position_in_grid ]]
101 | ) {
102 | if ( (int)tid < config.width_dst * config.height_dst ) {
103 |
104 | const int i_dst = tid % config.width_dst;
105 | const int j_dst = tid / config.width_dst;
106 |
107 | const int base_i_src = i_dst * config.upsampling_factor;
108 | const int base_j_src = j_dst * config.upsampling_factor;
109 |
110 | const float sd_in_upsampled = sd_buffer_src[ base_j_src * config.side_len_src + base_i_src ];
111 | const long quantized_sd = (long)( sd_in_upsampled * 256.0 );
112 | const long clamped_sd = max( (long)0, ( min( (long)255, quantized_sd ) ) );
113 |
114 | if ( config.flip_y ) {
115 |
116 | sd_buffer_dst[ ( ( config.side_len_dst - 1 ) - ( config.origin_y_dst + j_dst ) ) * config.side_len_dst + config.origin_x_dst + i_dst ] = (uint8_t)clamped_sd;
117 | }
118 | else {
119 | sd_buffer_dst[ (config.origin_y_dst + j_dst) * config.side_len_dst + config.origin_x_dst + i_dst ] = (uint8_t)clamped_sd;
120 | }
121 | }
122 | }
123 |
124 |
125 | typedef struct _ConfigZeroInitialize {
126 | int length_in_int32;
127 | } ConfigZeroInitialize;
128 |
129 | kernel void initialize_with_zero (
130 | device const ConfigZeroInitialize& config [[ buffer(0)]],
131 | device int* buf [[ buffer(1)]],
132 | const uint tid [[ thread_position_in_grid ]],
133 | const uint threads_per_threadgroup [[ threads_per_threadgroup ]]
134 | ) {
135 | for( int i = tid; i < config.length_in_int32 ; i += threads_per_threadgroup ) {
136 | buf[i] = 0;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontMetalSourceCode.swift:
--------------------------------------------------------------------------------
1 | let SDFontMetalSourceCode = """
2 | #include
3 | using namespace metal;
4 |
5 | typedef struct _ConfigGen {
6 | int draw_area_side_len; // number of pixels per row.
7 | float spread_thickness; // it detemines the rectangular area for searching signed distance.
8 | int width; // width of the glyph including the spread.
9 | int height; // height of the glyph including the spread.
10 | } ConfigGen;
11 |
12 |
13 | // Brute-force signed-distance finder. Each pixel gets one thread to calculate
14 | // the signed distance (distance from the closest pixel of the different value.)
15 | // Interpretation of the output float value:
16 | // * 0.5 <= v : the pixel is inside the glyph and v is the distance to the closest pixel outside the glyph
17 | // * -0.5 >= v : the pixel is outside the glyph and v is the distance to the closest pixel inside the glyph
18 | kernel void generate_signed_distance (
19 | device const ConfigGen& config [[ buffer(0)]],
20 | device const uint8_t* pixmap_buffer [[ buffer(1)]],
21 | device float* sd_buffer [[ buffer(2)]],
22 | const uint tid [[ thread_position_in_grid ]]
23 | ) {
24 | if ( (int)tid < config.width * config.height ) {
25 |
26 | const int base_i = tid % config.width;
27 | const int base_j = tid / config.width;
28 |
29 | const int v = pixmap_buffer[ base_j * config.draw_area_side_len + base_i ];
30 | const int spread = (int)config.spread_thickness;
31 |
32 | float sq_dist_min = (float)config.draw_area_side_len * (float)config.draw_area_side_len * 4.0; // arbitrary large value
33 |
34 | for ( int j = -1 * spread; j < spread; j++ ) {
35 |
36 | if ( base_j + j >= 0 && base_j + j < config.height ) {
37 |
38 | for ( int i = -1 * spread; i < spread; i++ ) {
39 |
40 | if ( base_i + i >= 0 && base_i + i < config.width ) {
41 |
42 | if ( (i != 0) || (j != 0) ) {
43 |
44 | const int v_other = pixmap_buffer[ ( base_j + j ) * config.draw_area_side_len + base_i + i ];
45 |
46 | if ( ( v >= 128 && v_other < 128 ) || ( v < 128 && v_other >= 128 ) ) {
47 |
48 | const float sq_dist = (float)( i * i + j * j );
49 |
50 | if ( sq_dist_min > sq_dist ) {
51 | sq_dist_min = sq_dist;
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
60 | const float normalized_min_dist = ( /*precise::*/sqrt(sq_dist_min) - 1.0 ) / config.spread_thickness;
61 | if ( v >= 128 ) {
62 | // inside glyph
63 | sd_buffer[ base_j * config.draw_area_side_len + base_i ] = 0.5 + normalized_min_dist / 2.0;
64 | }
65 | else {
66 | // outside glyph
67 | sd_buffer[ base_j * config.draw_area_side_len + base_i ] = 0.5 - normalized_min_dist / 2.0;
68 | }
69 | }
70 | }
71 |
72 | typedef struct _ConfigDownsample {
73 |
74 | // glyph-local upsampled space
75 | int side_len_src;
76 | int width_src;
77 | int height_src;
78 |
79 | // output texture space
80 | int side_len_dst;
81 | int width_dst;
82 | int height_dst;
83 | int origin_x_dst;
84 | int origin_y_dst;
85 |
86 | int upsampling_factor;
87 | float spread_thickness;
88 |
89 | int flip_y;
90 |
91 | } ConfigDownsample;
92 |
93 |
94 | // Each output value in uint8 is calculated from the square region of the upsampled signed distance values,
95 | // whose side length is config.upsampling_factor.
96 | // The output value is the average of the square resion, re-scaled to the down-sampled space.
97 | kernel void downsample (
98 | device const ConfigDownsample& config [[ buffer(0)]],
99 | device const float* sd_buffer_src [[ buffer(1)]],
100 | device uint8_t* sd_buffer_dst [[ buffer(2)]],
101 | const uint tid [[ thread_position_in_grid ]]
102 | ) {
103 | if ( (int)tid < config.width_dst * config.height_dst ) {
104 |
105 | const int i_dst = tid % config.width_dst;
106 | const int j_dst = tid / config.width_dst;
107 |
108 | const int base_i_src = i_dst * config.upsampling_factor;
109 | const int base_j_src = j_dst * config.upsampling_factor;
110 |
111 | const float sd_in_upsampled = sd_buffer_src[ base_j_src * config.side_len_src + base_i_src ];
112 | const long quantized_sd = (long)( sd_in_upsampled * 256.0 );
113 | const long clamped_sd = max( (long)0, ( min( (long)255, quantized_sd ) ) );
114 |
115 | if ( config.flip_y ) {
116 |
117 | sd_buffer_dst[ ( ( config.side_len_dst - 1 ) - ( config.origin_y_dst + j_dst ) ) * config.side_len_dst + config.origin_x_dst + i_dst ] = (uint8_t)clamped_sd;
118 | }
119 | else {
120 | sd_buffer_dst[ (config.origin_y_dst + j_dst) * config.side_len_dst + config.origin_x_dst + i_dst ] = (uint8_t)clamped_sd;
121 | }
122 | }
123 | }
124 |
125 |
126 | typedef struct _ConfigZeroInitialize {
127 | int length_in_int32;
128 | } ConfigZeroInitialize;
129 |
130 | kernel void initialize_with_zero (
131 | device const ConfigZeroInitialize& config [[ buffer(0)]],
132 | device int* buf [[ buffer(1)]],
133 | const uint tid [[ thread_position_in_grid ]],
134 | const uint threads_per_threadgroup [[ threads_per_threadgroup ]]
135 | ) {
136 | for( int i = tid; i < config.length_in_int32 ; i += threads_per_threadgroup ) {
137 | buf[i] = 0;
138 | }
139 | }
140 | """
141 |
--------------------------------------------------------------------------------
/SDFont/sdfontgen/main.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 | import UniformTypeIdentifiers
4 | //import SDFont
5 |
6 | var helpFound : Bool = false
7 | var verboseFound : Bool = false
8 | var showFontListFound : Bool = false
9 | var fontName : String = ""
10 | var fontNameExpected : Bool = false
11 | var glyphNumCutoff : Int = 0
12 | var glyphNumCutoffExpected : Bool = false
13 | var spread : Double = 0
14 | var spreadExpected : Bool = false
15 | var upSample : Int = 1
16 | var upSampleExpected : Bool = false
17 | var outputPath : String = ""
18 | var outputPathExpected : Bool = false
19 | var textureSize : Int = 512
20 | var textureSizeExpected : Bool = false
21 |
22 | for arg in CommandLine.arguments {
23 |
24 | switch arg {
25 |
26 | case "-h", "--help":
27 | helpFound = true
28 |
29 | case "-verbose":
30 | verboseFound = true
31 |
32 | case "-showfontlist":
33 | showFontListFound = true
34 |
35 | case "-fontname":
36 | fontNameExpected = true
37 |
38 | case "-glyphnumcutoff":
39 | glyphNumCutoffExpected = true
40 |
41 | case "-spread":
42 | spreadExpected = true
43 |
44 | case "-upsample":
45 | upSampleExpected = true
46 |
47 | case "-outputpath":
48 | outputPathExpected = true
49 |
50 | case "-texturesize":
51 | textureSizeExpected = true
52 |
53 | default:
54 |
55 | if fontNameExpected {
56 | fontName = arg
57 | }
58 | else if glyphNumCutoffExpected {
59 | if let intArg = Int(arg) {
60 | glyphNumCutoff = max(0, intArg)
61 | }
62 | }
63 | else if spreadExpected {
64 | if let doubleArg = Double(arg) {
65 | spread = min( 2.0, max(0.0, doubleArg) )
66 | }
67 | }
68 | else if upSampleExpected {
69 | if let intArg = Int(arg) {
70 | upSample = max(0, intArg)
71 | }
72 | }
73 | else if outputPathExpected {
74 | outputPath = arg
75 | }
76 | else if textureSizeExpected {
77 | if let intArg = Int(arg) {
78 | textureSize = max(0, intArg)
79 | }
80 | }
81 |
82 | fontNameExpected = false
83 | glyphNumCutoffExpected = false
84 | spreadExpected = false
85 | upSampleExpected = false
86 | outputPathExpected = false
87 | textureSizeExpected = false
88 | }
89 | }
90 |
91 | if showFontListFound {
92 | print ("Available fonts:")
93 | print ("(Please consult https://developer.apple.com/fonts/ for the official information.)")
94 | for font in NSFontManager.shared.availableFonts {
95 | print (" [\(font)]")
96 | }
97 | exit(0)
98 | }
99 |
100 | if helpFound || fontName == "" || spread == 0.0 || outputPath == "" {
101 | print ("")
102 | print ("sdfontgen : command-line signed distance font generator for macos.")
103 | print ("")
104 | print ("options")
105 | print ("")
106 | print (" -h/--help: show this message" )
107 | print ("")
108 | print (" -verbose: show INFO and WARNING messages" )
109 | print ("")
110 | print (" -showfontlist: show the list of the fonts in the system" )
111 | print ("")
112 | print (" -fontname : name of the font, preferrably in Postscript name," )
113 | print (" e.g. -fontname Helvetica" )
114 | print ("")
115 | print (" -glyphnumcutoff : maximum index of the glyphs to process." )
116 | print ("")
117 | print (" -spread : specifies the margin around each glyph in the fraction" )
118 | print (" of the average glyph width and height. Usually within the range of [0.1,0.2].")
119 | print ("")
120 | print (" -upsample : specifies the fontsize in the integer multiple to the original")
121 | print (" font size, in order to sample the glyph bitmaps. The original fontsize")
122 | print (" is determined as the best size to pack the glyphs to the output texture of")
123 | print (" the specified size. For example if the side length of the output texture")
124 | print (" is 2048, and the font is Helvecita with about 2000 glyphs, then the best")
125 | print (" font size will be 43.0. If the upsample factor is 4, then the font size 172.0")
126 | print (" is used to sample the glyph bitmap and to generate signed distance.")
127 | print (" Usually within the range of [2,4].")
128 | print ("")
129 | print (" -texturesize : the length of the sides in pixels of the output texture.")
130 | print (" Usually within the range of [512,4096].")
131 | print ("")
132 | print (" -outputpath : the path in which the output PNG and JSON files are stored.")
133 | print (" If the outputpath is \"/path/to/output\", and fontname is \"Helvetica\",")
134 | print (" then the output files will be /path/to/output/Helvetica.png and /path/to/output/Helvetica.json.")
135 | print ("")
136 | print ("NOTES on -glyphnumcutoff:")
137 | print (" This is useful for example, if you want a small texture size for some games," )
138 | print (" and you know you use only the first 256 glyphs. However, you should be careful" )
139 | print (" as the CoreText's typesetter may select a ligature glyph such as 'fi' and 'ff'" )
140 | print (" whose indices are above 255, even if you use only English alphabets." )
141 | print ("")
142 | exit(0)
143 | }
144 |
145 | if verboseFound {
146 | print ("sdfontgen : generating signed distance fonts with the following parameters.")
147 | print (" font name: [\(fontName)]")
148 | print (" glyph number cut-off: [\(glyphNumCutoff)]")
149 | print (" spread: [\(spread)]")
150 | print (" up-sample factor: [\(upSample)]")
151 | print (" texture size: [\(textureSize)]")
152 | print (" output path: [\(outputPath)]")
153 | }
154 |
155 | let sdGenerator = SDFontGenerator(
156 | device : MTLCreateSystemDefaultDevice()!,
157 | fontName : fontName,
158 | outputTextureSideLen : textureSize,
159 | spreadFactor : spread,
160 | upSamplingFactor : upSample,
161 | glyphNumCutoff : glyphNumCutoff,
162 | verbose : verboseFound,
163 | usePosixPath : true
164 | )
165 |
166 | let rtn1 = sdGenerator.writeToPNGFile(fileName: fontName, path: outputPath )
167 | if !rtn1 {
168 | print ("ERROR: cannot write PNG file [\(outputPath)/\(fontName).png]")
169 | }
170 |
171 | let rtn2 = sdGenerator.writeMetricsToJSONFile(fileName: fontName, path: outputPath )
172 | if !rtn2 {
173 | print ("ERROR: cannot write JSON file [\(outputPath)/\(fontName).json]")
174 | }
175 | print ("sdfontgen: finished processing.")
176 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #########################
2 | # .gitignore file for Xcode4 and Xcode5 Source projects
3 | #
4 | # Apple bugs, waiting for Apple to fix/respond:
5 | #
6 | # 15564624 - what does the xccheckout file in Xcode5 do? Where's the documentation?
7 | #
8 | # Version 2.6
9 | # For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects
10 | #
11 | # 2015 updates:
12 | # - Fixed typo in "xccheckout" line - thanks to @lyck for pointing it out!
13 | # - Fixed the .idea optional ignore. Thanks to @hashier for pointing this out
14 | # - Finally added "xccheckout" to the ignore. Apple still refuses to answer support requests about this, but in practice it seems you should ignore it.
15 | # - minor tweaks from Jona and Coeur (slightly more precise xc* filtering/names)
16 | # 2014 updates:
17 | # - appended non-standard items DISABLED by default (uncomment if you use those tools)
18 | # - removed the edit that an SO.com moderator made without bothering to ask me
19 | # - researched CocoaPods .lock more carefully, thanks to Gokhan Celiker
20 | # 2013 updates:
21 | # - fixed the broken "save personal Schemes"
22 | # - added line-by-line explanations for EVERYTHING (some were missing)
23 | #
24 | # NB: if you are storing "built" products, this WILL NOT WORK,
25 | # and you should use a different .gitignore (or none at all)
26 | # This file is for SOURCE projects, where there are many extra
27 | # files that we want to exclude
28 | #
29 | #########################
30 |
31 | #####
32 | # OS X temporary files that should never be committed
33 | #
34 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html
35 |
36 | .DS_Store
37 |
38 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html
39 |
40 | .Trashes
41 |
42 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html
43 |
44 | *.swp
45 |
46 | #
47 | # *.lock - this is used and abused by many editors for many different things.
48 | # For the main ones I use (e.g. Eclipse), it should be excluded
49 | # from source-control, but YMMV.
50 | # (lock files are usually local-only file-synchronization on the local FS that should NOT go in git)
51 | # c.f. the "OPTIONAL" section at bottom though, for tool-specific variations!
52 | #
53 | # In particular, if you're using CocoaPods, you'll want to comment-out this line:
54 | *.lock
55 |
56 |
57 | #
58 | # profile - REMOVED temporarily (on double-checking, I can't find it in OS X docs?)
59 | #profile
60 |
61 |
62 | ####
63 | # Xcode temporary files that should never be committed
64 | #
65 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this...
66 |
67 | *~.nib
68 |
69 |
70 | ####
71 | # Xcode build files -
72 | #
73 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData"
74 |
75 | DerivedData/
76 |
77 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build"
78 |
79 | build/
80 |
81 |
82 | #####
83 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups)
84 | #
85 | # This is complicated:
86 | #
87 | # SOMETIMES you need to put this file in version control.
88 | # Apple designed it poorly - if you use "custom executables", they are
89 | # saved in this file.
90 | # 99% of projects do NOT use those, so they do NOT want to version control this file.
91 | # ..but if you're in the 1%, comment out the line "*.pbxuser"
92 |
93 | # .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html
94 |
95 | *.pbxuser
96 |
97 | # .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html
98 |
99 | *.mode1v3
100 |
101 | # .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html
102 |
103 | *.mode2v3
104 |
105 | # .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file
106 |
107 | *.perspectivev3
108 |
109 | # NB: also, whitelist the default ones, some projects need to use these
110 | !default.pbxuser
111 | !default.mode1v3
112 | !default.mode2v3
113 | !default.perspectivev3
114 |
115 |
116 | ####
117 | # Xcode 4 - semi-personal settings
118 | #
119 | # Apple Shared data that Apple put in the wrong folder
120 | # c.f. http://stackoverflow.com/a/19260712/153422
121 | # FROM ANSWER: Apple says "don't ignore it"
122 | # FROM COMMENTS: Apple is wrong; Apple code is too buggy to trust; there are no known negative side-effects to ignoring Apple's unofficial advice and instead doing the thing that actively fixes bugs in Xcode
123 | # Up to you, but ... current advice: ignore it.
124 | *.xccheckout
125 |
126 | #
127 | #
128 | # OPTION 1: ---------------------------------
129 | # throw away ALL personal settings (including custom schemes!
130 | # - unless they are "shared")
131 | # As per build/ and DerivedData/, this ought to have a trailing slash
132 | #
133 | # NB: this is exclusive with OPTION 2 below
134 | xcuserdata/
135 |
136 | # OPTION 2: ---------------------------------
137 | # get rid of ALL personal settings, but KEEP SOME OF THEM
138 | # - NB: you must manually uncomment the bits you want to keep
139 | #
140 | # NB: this *requires* git v1.8.2 or above; you may need to upgrade to latest OS X,
141 | # or manually install git over the top of the OS X version
142 | # NB: this is exclusive with OPTION 1 above
143 | #
144 | #xcuserdata/**/*
145 |
146 | # (requires option 2 above): Personal Schemes
147 | #
148 | #!xcuserdata/**/xcschemes/*
149 |
150 | ####
151 | # XCode 4 workspaces - more detailed
152 | #
153 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :)
154 | #
155 | # Workspace layout is quite spammy. For reference:
156 | #
157 | # /(root)/
158 | # /(project-name).xcodeproj/
159 | # project.pbxproj
160 | # /project.xcworkspace/
161 | # contents.xcworkspacedata
162 | # /xcuserdata/
163 | # /(your name)/xcuserdatad/
164 | # UserInterfaceState.xcuserstate
165 | # /xcshareddata/
166 | # /xcschemes/
167 | # (shared scheme name).xcscheme
168 | # /xcuserdata/
169 | # /(your name)/xcuserdatad/
170 | # (private scheme).xcscheme
171 | # xcschememanagement.plist
172 | #
173 | #
174 |
175 | ####
176 | # Xcode 4 - Deprecated classes
177 | #
178 | # Allegedly, if you manually "deprecate" your classes, they get moved here.
179 | #
180 | # We're using source-control, so this is a "feature" that we do not want!
181 |
182 | *.moved-aside
183 |
184 | ####
185 | # OPTIONAL: Some well-known tools that people use side-by-side with Xcode / iOS development
186 | #
187 | # NB: I'd rather not include these here, but gitignore's design is weak and doesn't allow
188 | # modular gitignore: you have to put EVERYTHING in one file.
189 | #
190 | # COCOAPODS:
191 | #
192 | # c.f. http://guides.cocoapods.org/using/using-cocoapods.html#what-is-a-podfilelock
193 | # c.f. http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
194 | #
195 | #!Podfile.lock
196 | #
197 | # RUBY:
198 | #
199 | # c.f. http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
200 | #
201 | #!Gemfile.lock
202 | #
203 | # IDEA:
204 | #
205 | # c.f. https://www.jetbrains.com/objc/help/managing-projects-under-version-control.html?search=workspace.xml
206 | #
207 | #.idea/workspace.xml
208 | #
209 | # TEXTMATE:
210 | #
211 | # -- UNVERIFIED: c.f. http://stackoverflow.com/a/50283/153422
212 | #
213 | #tm_build_errors
214 |
215 | ####
216 | # UNKNOWN: recommended by others, but I can't discover what these files are
217 | #
218 |
219 | bin/
220 | objs/
221 | *.*~
222 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontIOHandler.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 | import UniformTypeIdentifiers
4 |
5 | class SDFontIOHandler {
6 |
7 | struct Metrics : Decodable {
8 | let x : Double
9 | let y : Double
10 | let width : Double
11 | let height : Double
12 | }
13 |
14 | static func getFileURL( fileName : String, ext : String, path : String?, usePosixPath : Bool ) -> URL {
15 | #if os(iOS)
16 | var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
17 | if let path = path {
18 | documentsURL = documentsURL.appendingPathComponent( path )
19 | }
20 | return documentsURL.appendingPathComponent("\(fileName).\(ext)")
21 | #else
22 | var documentsURL : URL
23 |
24 | if usePosixPath {
25 | documentsURL = URL( fileURLWithPath: (path != nil) ? path! : "./" )
26 | }
27 | else {
28 | documentsURL = URL( fileURLWithPath: FileManager.default.currentDirectoryPath )
29 | if let path = path {
30 | documentsURL = documentsURL.appendingPathComponent( path )
31 | }
32 | }
33 | return documentsURL.appendingPathComponent("\(fileName).\(ext)")
34 | #endif
35 | }
36 |
37 | static func loadMetricsFromJSONFile( fileName : String, path : String?, usePosixPath : Bool, verbose : Bool ) -> [CGRect] {
38 |
39 | var outArray : [CGRect] = []
40 |
41 | let fileURL = Self.getFileURL( fileName : fileName, ext : "json", path : path, usePosixPath : usePosixPath )
42 |
43 | if verbose {
44 | print ("INFO: JSON file path to load [\(fileURL)]: ")
45 | }
46 | do {
47 | let rawData = try Data( contentsOf: fileURL )
48 | let decoder = JSONDecoder()
49 | let jsonData: [Metrics] = try! decoder.decode( [Metrics].self, from: rawData )
50 | for elem in jsonData {
51 | outArray.append( CGRect( x: CGFloat(elem.x), y: CGFloat(elem.y), width: CGFloat(elem.width), height: CGFloat(elem.height) ) )
52 | }
53 | } catch {
54 | print ( "ERROR: Cannot read and parse json file \(fileName)." )
55 | }
56 | return outArray
57 | }
58 |
59 |
60 | static func writeMetricsToJSONFile( fileName : String, path : String?, usePosixPath : Bool, bounds : [CGRect], verbose : Bool ) -> Bool {
61 | var array : [Any] = []
62 | for bound in bounds {
63 | array.append( ["x" : bound.origin.x, "y" : bound.origin.y, "width" : bound.size.width, "height" : bound.size.height ])
64 | }
65 |
66 | do {
67 | let jsonData = try JSONSerialization.data( withJSONObject: array )
68 | let jsonString = String( data: jsonData, encoding: String.Encoding.utf8 )
69 | let fileURL = Self.getFileURL( fileName : fileName, ext : "json", path : path, usePosixPath : usePosixPath )
70 | if verbose {
71 | print ("INFO: JSON file path to write [\(fileURL)]: ")
72 | }
73 | try jsonString!.write( to: fileURL, atomically: true, encoding: String.Encoding.utf8 )
74 |
75 | } catch {
76 | return false
77 | }
78 | return true
79 | }
80 |
81 | static func writeMetricsToTSVFile( fileName : String, path : String?, usePosixPath : Bool, bounds : [CGRect], verbose : Bool ) -> Bool {
82 |
83 | var outStr = ""
84 | for bound in bounds {
85 | let str = String( format : "%f\t%f\t%f\t%f\n", bound.origin.x, bound.origin.y, bound.size.width, bound.size.height )
86 | outStr += str
87 | }
88 |
89 | do {
90 | let fileURL = Self.getFileURL( fileName : fileName, ext : "txt", path : path, usePosixPath : usePosixPath )
91 | if verbose {
92 | print ("INFO: TSV file path to write [\(fileURL)]: ")
93 | }
94 | try outStr.write( to: fileURL, atomically: true, encoding: String.Encoding.utf8 )
95 |
96 | } catch {
97 | return false
98 | }
99 | return true
100 | }
101 |
102 | #if os(iOS)
103 | static func generateUIImage( buf : MTLBuffer, sidesLenPixels : Int ) -> UIImage? {
104 |
105 | let cgImage = generateCGImage(buf : buf, sidesLenPixels : sidesLenPixels )
106 | let uiImage = UIImage( cgImage: cgImage! )
107 | return uiImage
108 | }
109 | #endif
110 |
111 | static func writeToPNGFile( fileName : String, path : String?, usePosixPath : Bool, buf : MTLBuffer, sidesLenPixels : Int, verbose : Bool ) -> Bool {
112 |
113 | let cgImage = generateCGImage(buf : buf, sidesLenPixels : sidesLenPixels )
114 |
115 | let fileURL = Self.getFileURL( fileName : fileName, ext : "png", path : path, usePosixPath : usePosixPath )
116 |
117 | if verbose {
118 | print ("INFO: PNG file path to write [\(fileURL)]: ")
119 | }
120 |
121 | let dest = CGImageDestinationCreateWithURL( fileURL as CFURL, UTType.png.identifier as CFString, 1, nil )
122 |
123 | if let dest = dest {
124 | CGImageDestinationAddImage( dest, cgImage!, nil );
125 | return CGImageDestinationFinalize( dest )
126 | }
127 | return false
128 | }
129 |
130 | static func loadTextureFromPNGFile( device : MTLDevice, fileName : String, path : String?, usePosixPath : Bool, verbose : Bool ) -> MTLTexture? {
131 |
132 | let loader = MTKTextureLoader( device : device )
133 |
134 | let fileURL = Self.getFileURL( fileName : fileName, ext : "png", path : path, usePosixPath : usePosixPath )
135 |
136 | if verbose {
137 | print ("INFO: PNG file path to load [\(fileURL)]: ")
138 | }
139 | do {
140 | let texture = try loader.newTexture(
141 | URL : fileURL,
142 | options : [ .origin: MTKTextureLoader.Origin.bottomLeft,
143 | .SRGB: false,
144 | .generateMipmaps: NSNumber(booleanLiteral: true)
145 | ] )
146 | return texture
147 | }
148 | catch {
149 | print ("ERROR: Cannot load PNG file \(fileName) for MTLTexture.")
150 | return nil
151 | }
152 | }
153 |
154 | static func generateMTLTexture( device : MTLDevice, buf : MTLBuffer, sidesLenPixels : Int ) -> MTLTexture? {
155 |
156 | let cgImage = generateCGImage(buf : buf, sidesLenPixels : sidesLenPixels )
157 | let loader = MTKTextureLoader( device: device )
158 | do {
159 | let texture = try loader.newTexture(
160 | cgImage: cgImage!,
161 | options:[ .origin: MTKTextureLoader.Origin.bottomLeft,
162 | .SRGB: false,
163 | .generateMipmaps: NSNumber(booleanLiteral: true)
164 | ]
165 | )
166 | return texture
167 | }
168 | catch {
169 | print ("ERROR: Cannot generate MTLTexture.")
170 | return nil
171 | }
172 | }
173 |
174 | static func generateCGImage(buf : MTLBuffer, sidesLenPixels : Int ) -> CGImage? {
175 |
176 | let colorSpace : CGColorSpace = CGColorSpaceCreateDeviceGray();
177 | let bitmapInfo = CGBitmapInfo( rawValue: CGImageAlphaInfo.none.rawValue | CGImageByteOrderInfo.orderDefault.rawValue | CGImagePixelFormatInfo.packed.rawValue )
178 |
179 | let context = CGContext(
180 | data: buf.contents(),
181 | width: sidesLenPixels,
182 | height: sidesLenPixels,
183 | bitsPerComponent: 8,
184 | bytesPerRow: sidesLenPixels,
185 | space: colorSpace,
186 | bitmapInfo: bitmapInfo.rawValue
187 | )
188 | context!.setAllowsAntialiasing(false)
189 | return context!.makeImage()
190 | }
191 | }
192 |
193 |
--------------------------------------------------------------------------------
/SWOpeningRoll/shared/SDTextPlane.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 | import SDFont
4 |
5 | class SDTextPlane {
6 |
7 | static let TextureMargin : CGFloat = 0.001 // margin from the real glyph box to include the spread part
8 |
9 | let device : MTLDevice
10 | var dimension : CGSize
11 | var sdHelper : SDFontRuntimeHelper
12 | var scaleForTypeSetting : CGFloat
13 |
14 | var vertexBuffer : MTLBuffer?
15 | var numVertices : Int
16 | var indexBuffer : MTLBuffer?
17 | var numIndices : Int
18 |
19 | var localTransform : float4x4
20 | var globalTransform : float4x4
21 | var uniformPerInstance : UniformPerInstance
22 | var perInstanceBuffer : MTLBuffer?
23 |
24 | var uniformSDFont : UniformSDFont
25 | var sdFontUniformBuffer : MTLBuffer?
26 |
27 | var sdFontTexture : MTLTexture?
28 |
29 | var animationSequencer : AnimationSequencer?
30 |
31 | init(
32 | device : MTLDevice,
33 | dimension : CGSize,
34 | sdHelper : SDFontRuntimeHelper,
35 | scaleForTypeSetting : CGFloat,
36 | uniformSDFont : UniformSDFont
37 | ) {
38 | self.device = device
39 | self.dimension = dimension
40 | self.sdHelper = sdHelper
41 | self.scaleForTypeSetting = scaleForTypeSetting
42 |
43 | self.numVertices = 0
44 | self.numIndices = 0
45 | self.localTransform = float4x4.identity()
46 | self.globalTransform = float4x4.identity()
47 | self.uniformPerInstance = UniformPerInstance( modelMatrix : self.globalTransform * self.localTransform )
48 | self.uniformSDFont = uniformSDFont
49 |
50 | self.perInstanceBuffer = UniformPerInstance.generateMTLBuffer( device : device, instances : [uniformPerInstance] )
51 | self.sdFontUniformBuffer = UniformSDFont.generateMTLBuffer( device : device, v : &self.uniformSDFont )
52 |
53 | self.sdFontTexture = sdHelper.texture()
54 | }
55 |
56 | func setLocalTransform( translation : SIMD3 ) {
57 |
58 | localTransform.columns.3.x = translation.x
59 | localTransform.columns.3.y = translation.y
60 | localTransform.columns.3.z = translation.z
61 | self.uniformPerInstance = UniformPerInstance( modelMatrix : self.globalTransform * self.localTransform )
62 | UniformPerInstance.updateMTLBuffer( buffer : perInstanceBuffer!, instances : [uniformPerInstance] )
63 | }
64 |
65 | func setGlobalTransform( transform : float4x4 ) {
66 |
67 | self.globalTransform = transform
68 | self.uniformPerInstance = UniformPerInstance( modelMatrix : self.globalTransform * self.localTransform )
69 | UniformPerInstance.updateMTLBuffer( buffer : perInstanceBuffer!, instances : [uniformPerInstance] )
70 | }
71 |
72 | func rotate90AroundX() {
73 |
74 | localTransform.columns.0 = SIMD4( 1.0, 0.0, 0.0, 0.0 )
75 | localTransform.columns.1 = SIMD4( 0.0, 0.0, -1.0, 0.0 )
76 | localTransform.columns.2 = SIMD4( 0.0, 1.0, 0.0, 0.0 )
77 | self.uniformPerInstance = UniformPerInstance( modelMatrix : self.globalTransform * self.localTransform )
78 | UniformPerInstance.updateMTLBuffer( buffer : perInstanceBuffer!, instances : [uniformPerInstance] )
79 | }
80 |
81 | func setAnimation(
82 | temporalPoints : [TimeInterval],
83 | spacialPoints : [SIMD3],
84 | colors : [SIMD4]
85 | ) {
86 | animationSequencer = AnimationSequencer(
87 | temporalPoints : temporalPoints,
88 | spacialPoints : spacialPoints,
89 | colors : colors
90 | )
91 | }
92 |
93 | func startAnimation() {
94 | animationSequencer!.startAnimation()
95 | }
96 |
97 | func animationActive()-> Bool {
98 | animationSequencer!.animationActive
99 | }
100 |
101 | func step() {
102 |
103 | animationSequencer!.step()
104 |
105 | let s = animationSequencer!.spacialPointNow
106 | setLocalTransform( translation : s )
107 | uniformSDFont.foregroundColor = animationSequencer!.colorNow
108 | UniformSDFont.updateMTLBuffer( buffer : sdFontUniformBuffer!, v : &uniformSDFont )
109 | }
110 |
111 | func setText( textPlain: String, lineAlignment: CTTextAlignment ) {
112 | let rectForTypeSetting = CGRect(
113 | x : -0.5 * dimension.width * scaleForTypeSetting,
114 | y : -0.5 * dimension.height * scaleForTypeSetting,
115 | width : dimension.width * scaleForTypeSetting,
116 | height : dimension.height * scaleForTypeSetting
117 | )
118 | let scaledBounds = sdHelper.typeset( frameRect: rectForTypeSetting, textPlain: textPlain, lineAlignment: lineAlignment )
119 |
120 | generateVerticesAndIndices( bounds : scaledBounds )
121 | }
122 |
123 | func generateVerticesAndIndices( bounds : [SDFontRuntimeHelper.GlyphBound] )
124 | {
125 | var indexBegin : Int32 = 0
126 |
127 | var positions : [ SIMD4 ] = []
128 | var uvs : [ SIMD2 ] = []
129 | var indices : [ Int32 ] = []
130 |
131 | for bound in bounds {
132 | var factor : CGFloat
133 | if bound.textureBound.size.width > bound.textureBound.size.height {
134 | factor = bound.frameBound.size.width / bound.textureBound.size.width
135 | }
136 | else {
137 | factor = bound.frameBound.size.height / bound.textureBound.size.height
138 | }
139 | let frameMargin = factor * Self.TextureMargin
140 |
141 | let xLeft = Float( ( bound.frameBound.origin.x - frameMargin ) / scaleForTypeSetting )
142 | let xRight = Float( ( bound.frameBound.origin.x + bound.frameBound.size.width + frameMargin ) / scaleForTypeSetting )
143 | let yLower = Float( ( bound.frameBound.origin.y - frameMargin ) / scaleForTypeSetting )
144 | let yUpper = Float( ( bound.frameBound.origin.y + bound.frameBound.size.height + frameMargin ) / scaleForTypeSetting )
145 |
146 | positions.append( SIMD4( xLeft, yLower, 0.0, 1.0 ) )
147 | positions.append( SIMD4( xRight, yLower, 0.0, 1.0 ) )
148 | positions.append( SIMD4( xRight, yUpper, 0.0, 1.0 ) )
149 | positions.append( SIMD4( xLeft, yUpper, 0.0, 1.0 ) )
150 |
151 | let uLeft = Float( bound.textureBound.origin.x - Self.TextureMargin )
152 | let uRight = Float( bound.textureBound.origin.x + bound.textureBound.size.width + Self.TextureMargin )
153 | let vLower = Float( bound.textureBound.origin.y - Self.TextureMargin )
154 | let vUpper = Float( bound.textureBound.origin.y + bound.textureBound.size.height + Self.TextureMargin )
155 |
156 | uvs.append( SIMD2( uLeft, vLower ) )
157 | uvs.append( SIMD2( uRight, vLower ) )
158 | uvs.append( SIMD2( uRight, vUpper ) )
159 | uvs.append( SIMD2( uLeft, vUpper ) )
160 |
161 | indices.append( indexBegin )
162 | indices.append( indexBegin + 1 )
163 | indices.append( indexBegin + 2 )
164 | indices.append( indexBegin )
165 | indices.append( indexBegin + 2 )
166 | indices.append( indexBegin + 3 )
167 |
168 | indexBegin += 4
169 | }
170 |
171 | vertexBuffer = VertexInPositionUV.generateMTLBuffer(
172 | device : device,
173 | positions : positions,
174 | uvs : uvs
175 | )
176 |
177 | numVertices = positions.count
178 | numIndices = indices.count
179 | indexBuffer = VertexInIndex.generateMTLBuffer( device: device, indices : indices )
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/demo/SDTextPlane.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 | import SDFont
4 |
5 | class SDTextPlane {
6 |
7 | static let TextureMargin : CGFloat = 0.001 // margin from the real glyph box to include the spread part
8 |
9 | let device : MTLDevice
10 | var dimension : CGSize
11 | var sdHelper : SDFontRuntimeHelper
12 | var scaleForTypeSetting : CGFloat
13 |
14 | var vertexBuffer : MTLBuffer?
15 | var numVertices : Int
16 | var indexBuffer : MTLBuffer?
17 | var numIndices : Int
18 |
19 | var localTransform : float4x4
20 | var globalTransform : float4x4
21 | var uniformPerInstance : UniformPerInstance
22 | var perInstanceBuffer : MTLBuffer?
23 |
24 | var uniformSDFont : UniformSDFont
25 | var sdFontUniformBuffer : MTLBuffer?
26 |
27 | var sdFontTexture : MTLTexture?
28 |
29 | var animationSequencer : AnimationSequencer?
30 |
31 | init(
32 | device : MTLDevice,
33 | dimension : CGSize,
34 | sdHelper : SDFontRuntimeHelper,
35 | scaleForTypeSetting : CGFloat,
36 | uniformSDFont : UniformSDFont
37 | ) {
38 | self.device = device
39 | self.dimension = dimension
40 | self.sdHelper = sdHelper
41 | self.scaleForTypeSetting = scaleForTypeSetting
42 |
43 | self.numVertices = 0
44 | self.numIndices = 0
45 | self.localTransform = float4x4.identity()
46 | self.globalTransform = float4x4.identity()
47 | self.uniformPerInstance = UniformPerInstance( modelMatrix : self.globalTransform * self.localTransform )
48 | self.uniformSDFont = uniformSDFont
49 |
50 | self.perInstanceBuffer = UniformPerInstance.generateMTLBuffer( device : device, instances : [uniformPerInstance] )
51 | self.sdFontUniformBuffer = UniformSDFont.generateMTLBuffer( device : device, v : &self.uniformSDFont )
52 |
53 | self.sdFontTexture = sdHelper.texture()
54 | }
55 |
56 | func setLocalTransform( translation : SIMD3 ) {
57 |
58 | localTransform.columns.3.x = translation.x
59 | localTransform.columns.3.y = translation.y
60 | localTransform.columns.3.z = translation.z
61 | self.uniformPerInstance = UniformPerInstance( modelMatrix : self.globalTransform * self.localTransform )
62 | UniformPerInstance.updateMTLBuffer( buffer : perInstanceBuffer!, instances : [uniformPerInstance] )
63 | }
64 |
65 | func setGlobalTransform( transform : float4x4 ) {
66 |
67 | self.globalTransform = transform
68 | self.uniformPerInstance = UniformPerInstance( modelMatrix : self.globalTransform * self.localTransform )
69 | UniformPerInstance.updateMTLBuffer( buffer : perInstanceBuffer!, instances : [uniformPerInstance] )
70 | }
71 |
72 | func rotate90AroundX() {
73 |
74 | localTransform.columns.0 = SIMD4( 1.0, 0.0, 0.0, 0.0 )
75 | localTransform.columns.1 = SIMD4( 0.0, 0.0, -1.0, 0.0 )
76 | localTransform.columns.2 = SIMD4( 0.0, 1.0, 0.0, 0.0 )
77 | self.uniformPerInstance = UniformPerInstance( modelMatrix : self.globalTransform * self.localTransform )
78 | UniformPerInstance.updateMTLBuffer( buffer : perInstanceBuffer!, instances : [uniformPerInstance] )
79 | }
80 |
81 | func setAnimation(
82 | temporalPoints : [TimeInterval],
83 | spacialPoints : [SIMD3],
84 | colors : [SIMD4]
85 | ) {
86 | animationSequencer = AnimationSequencer(
87 | temporalPoints : temporalPoints,
88 | spacialPoints : spacialPoints,
89 | colors : colors
90 | )
91 | }
92 |
93 | func startAnimation() {
94 | animationSequencer!.startAnimation()
95 | }
96 |
97 | func animationActive()-> Bool {
98 | animationSequencer!.animationActive
99 | }
100 |
101 | func step() {
102 |
103 | animationSequencer!.step()
104 |
105 | let s = animationSequencer!.spacialPointNow
106 | setLocalTransform( translation : s )
107 | uniformSDFont.foregroundColor = animationSequencer!.colorNow
108 | UniformSDFont.updateMTLBuffer( buffer : sdFontUniformBuffer!, v : &uniformSDFont )
109 | }
110 |
111 | func setText( textPlain: String, lineAlignment: CTTextAlignment ) {
112 | let rectForTypeSetting = CGRect(
113 | x : -0.5 * dimension.width * scaleForTypeSetting,
114 | y : -0.5 * dimension.height * scaleForTypeSetting,
115 | width : dimension.width * scaleForTypeSetting,
116 | height : dimension.height * scaleForTypeSetting
117 | )
118 | let scaledBounds = sdHelper.typeset( frameRect: rectForTypeSetting, textPlain: textPlain, lineAlignment: lineAlignment )
119 |
120 | generateVerticesAndIndices( bounds : scaledBounds )
121 | }
122 |
123 | func generateVerticesAndIndices( bounds : [SDFontRuntimeHelper.GlyphBound] )
124 | {
125 | var indexBegin : Int32 = 0
126 |
127 | var positions : [ SIMD4 ] = []
128 | var uvs : [ SIMD2 ] = []
129 | var indices : [ Int32 ] = []
130 |
131 | for bound in bounds {
132 | var factor : CGFloat
133 | if bound.textureBound.size.width > bound.textureBound.size.height {
134 | factor = bound.frameBound.size.width / bound.textureBound.size.width
135 | }
136 | else {
137 | factor = bound.frameBound.size.height / bound.textureBound.size.height
138 | }
139 | let frameMargin = factor * Self.TextureMargin
140 |
141 | let xLeft = Float( ( bound.frameBound.origin.x - frameMargin ) / scaleForTypeSetting )
142 | let xRight = Float( ( bound.frameBound.origin.x + bound.frameBound.size.width + frameMargin ) / scaleForTypeSetting )
143 | let yLower = Float( ( bound.frameBound.origin.y - frameMargin ) / scaleForTypeSetting )
144 | let yUpper = Float( ( bound.frameBound.origin.y + bound.frameBound.size.height + frameMargin ) / scaleForTypeSetting )
145 |
146 | positions.append( SIMD4( xLeft, yLower, 0.0, 1.0 ) )
147 | positions.append( SIMD4( xRight, yLower, 0.0, 1.0 ) )
148 | positions.append( SIMD4( xRight, yUpper, 0.0, 1.0 ) )
149 | positions.append( SIMD4( xLeft, yUpper, 0.0, 1.0 ) )
150 |
151 | let uLeft = Float( bound.textureBound.origin.x - Self.TextureMargin )
152 | let uRight = Float( bound.textureBound.origin.x + bound.textureBound.size.width + Self.TextureMargin )
153 | let vLower = Float( bound.textureBound.origin.y - Self.TextureMargin )
154 | let vUpper = Float( bound.textureBound.origin.y + bound.textureBound.size.height + Self.TextureMargin )
155 |
156 | uvs.append( SIMD2( uLeft, vLower ) )
157 | uvs.append( SIMD2( uRight, vLower ) )
158 | uvs.append( SIMD2( uRight, vUpper ) )
159 | uvs.append( SIMD2( uLeft, vUpper ) )
160 |
161 | indices.append( indexBegin )
162 | indices.append( indexBegin + 1 )
163 | indices.append( indexBegin + 2 )
164 | indices.append( indexBegin )
165 | indices.append( indexBegin + 2 )
166 | indices.append( indexBegin + 3 )
167 |
168 | indexBegin += 4
169 | }
170 |
171 | vertexBuffer = VertexInPositionUV.generateMTLBuffer(
172 | device : device,
173 | positions : positions,
174 | uvs : uvs
175 | )
176 |
177 | numVertices = positions.count
178 | numIndices = indices.count
179 | indexBuffer = VertexInIndex.generateMTLBuffer( device: device, indices : indices )
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/SWOpeningRollAR/SWOpeningRollAR/PhotoImageRenderer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 | import ARKit
4 |
5 | // This is based on Renderer.swift generated for iOS/Augmented Reality App template.
6 | class PhotoImageRenderer {
7 |
8 | let imagePlaneVertexData: [Float] = [
9 | -1.0, -1.0, 0.0, 1.0,
10 | 1.0, -1.0, 1.0, 1.0,
11 | -1.0, 1.0, 0.0, 0.0,
12 | 1.0, 1.0, 1.0, 0.0,
13 | ]
14 |
15 | var device: MTLDevice!
16 | var imagePlaneVertexBuffer: MTLBuffer!
17 | var capturedImagePipelineState: MTLRenderPipelineState!
18 | var capturedImageDepthState: MTLDepthStencilState!
19 | var capturedImageTextureY: CVMetalTexture?
20 | var capturedImageTextureCbCr: CVMetalTexture?
21 | var capturedImageTextureCache: CVMetalTextureCache!
22 |
23 | init( device : MTLDevice ) {
24 | self.device = device
25 | }
26 |
27 | func arrangeMetalForCameraImage( view: MTKView ) {
28 |
29 | // Create a vertex buffer with our image plane vertex data.
30 | let imagePlaneVertexDataCount = imagePlaneVertexData.count * MemoryLayout.size
31 | imagePlaneVertexBuffer = device.makeBuffer(bytes: imagePlaneVertexData, length: imagePlaneVertexDataCount, options: [])
32 | imagePlaneVertexBuffer.label = "ImagePlaneVertexBuffer"
33 |
34 | // Load all the shader files with a metal file extension in the project
35 | let defaultLibrary = device.makeDefaultLibrary()!
36 |
37 | let capturedImageVertexFunction = defaultLibrary.makeFunction(name: "capturedImageVertexTransform")!
38 | let capturedImageFragmentFunction = defaultLibrary.makeFunction(name: "capturedImageFragmentShader")!
39 |
40 | // Create a vertex descriptor for our image plane vertex buffer
41 | let imagePlaneVertexDescriptor = MTLVertexDescriptor()
42 |
43 | // Positions.
44 | imagePlaneVertexDescriptor.attributes[0].format = .float2
45 | imagePlaneVertexDescriptor.attributes[0].offset = 0
46 | imagePlaneVertexDescriptor.attributes[0].bufferIndex = 0
47 |
48 | // Texture coordinates.
49 | imagePlaneVertexDescriptor.attributes[1].format = .float2
50 | imagePlaneVertexDescriptor.attributes[1].offset = 8
51 | imagePlaneVertexDescriptor.attributes[1].bufferIndex = 0
52 |
53 | // Buffer Layout
54 | imagePlaneVertexDescriptor.layouts[0].stride = 16
55 | imagePlaneVertexDescriptor.layouts[0].stepRate = 1
56 | imagePlaneVertexDescriptor.layouts[0].stepFunction = .perVertex
57 |
58 | // Create a pipeline state for rendering the captured image
59 | let capturedImagePipelineStateDescriptor = MTLRenderPipelineDescriptor()
60 | capturedImagePipelineStateDescriptor.label = "MyCapturedImagePipeline"
61 | capturedImagePipelineStateDescriptor.vertexFunction = capturedImageVertexFunction
62 | capturedImagePipelineStateDescriptor.fragmentFunction = capturedImageFragmentFunction
63 | capturedImagePipelineStateDescriptor.vertexDescriptor = imagePlaneVertexDescriptor
64 | capturedImagePipelineStateDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat
65 | capturedImagePipelineStateDescriptor.depthAttachmentPixelFormat = view.depthStencilPixelFormat
66 | capturedImagePipelineStateDescriptor.stencilAttachmentPixelFormat = .invalid
67 |
68 | do {
69 | try capturedImagePipelineState = device.makeRenderPipelineState(descriptor: capturedImagePipelineStateDescriptor)
70 | } catch let error {
71 | print("Failed to create captured image pipeline state, error \(error)")
72 | }
73 |
74 | let capturedImageDepthStateDescriptor = MTLDepthStencilDescriptor()
75 | capturedImageDepthStateDescriptor.depthCompareFunction = .always
76 | capturedImageDepthStateDescriptor.isDepthWriteEnabled = false
77 | capturedImageDepthState = device.makeDepthStencilState(descriptor: capturedImageDepthStateDescriptor)
78 |
79 | // Create captured image texture cache
80 | var textureCache: CVMetalTextureCache?
81 | CVMetalTextureCacheCreate(nil, nil, device, nil, &textureCache)
82 | capturedImageTextureCache = textureCache
83 | }
84 |
85 | func updateCapturedImageTextures(frame: ARFrame) {
86 | // Create two textures (Y and CbCr) from the provided frame's captured image
87 | let pixelBuffer = frame.capturedImage
88 |
89 | if (CVPixelBufferGetPlaneCount(pixelBuffer) < 2) {
90 | return
91 | }
92 |
93 | capturedImageTextureY = createTexture(fromPixelBuffer: pixelBuffer, pixelFormat:.r8Unorm, planeIndex:0)
94 | capturedImageTextureCbCr = createTexture(fromPixelBuffer: pixelBuffer, pixelFormat:.rg8Unorm, planeIndex:1)
95 | }
96 |
97 | func updateImagePlane(frame: ARFrame, viewportSize : CGSize ) {
98 | // Update the texture coordinates of our image plane to aspect fill the viewport
99 | // MARK: - Device Orientation
100 | let displayToCameraTransform = frame.displayTransform(for: .portrait, viewportSize: viewportSize).inverted()
101 |
102 | let vertexData = imagePlaneVertexBuffer.contents().assumingMemoryBound(to: Float.self)
103 | for index in 0...3 {
104 | let textureCoordIndex = 4 * index + 2
105 | let textureCoord = CGPoint(x: CGFloat(imagePlaneVertexData[textureCoordIndex]), y: CGFloat(imagePlaneVertexData[textureCoordIndex + 1]))
106 | let transformedCoord = textureCoord.applying(displayToCameraTransform)
107 | vertexData[textureCoordIndex] = Float(transformedCoord.x)
108 | vertexData[textureCoordIndex + 1] = Float(transformedCoord.y)
109 | }
110 | }
111 |
112 | func createTexture(fromPixelBuffer pixelBuffer: CVPixelBuffer, pixelFormat: MTLPixelFormat, planeIndex: Int) -> CVMetalTexture? {
113 | let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex)
114 | let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex)
115 |
116 | var texture: CVMetalTexture? = nil
117 | let status = CVMetalTextureCacheCreateTextureFromImage(nil, capturedImageTextureCache, pixelBuffer, nil, pixelFormat, width, height, planeIndex, &texture)
118 |
119 | if status != kCVReturnSuccess {
120 | texture = nil
121 | }
122 |
123 | return texture
124 | }
125 |
126 | func draw( in view: MTKView, commandBuffer: MTLCommandBuffer ) {
127 |
128 | let descriptor = view.currentRenderPassDescriptor
129 |
130 | guard
131 | let encoder = commandBuffer.makeRenderCommandEncoder( descriptor: descriptor! )
132 | else {
133 | return
134 | }
135 |
136 | guard let textureY = capturedImageTextureY, let textureCbCr = capturedImageTextureCbCr else {
137 | return
138 | }
139 |
140 | // Push a debug group allowing us to identify render commands in the GPU Frame Capture tool
141 | encoder.pushDebugGroup("DrawCapturedImage")
142 |
143 | // Set render command encoder state
144 | encoder.setCullMode(.none)
145 | encoder.setRenderPipelineState(capturedImagePipelineState)
146 | encoder.setDepthStencilState(capturedImageDepthState)
147 |
148 | // Set mesh's vertex buffers
149 | encoder.setVertexBuffer(imagePlaneVertexBuffer, offset: 0, index: 0 )
150 |
151 | // Set any textures read/sampled from our render pipeline
152 | encoder.setFragmentTexture(CVMetalTextureGetTexture(textureY), index: 1 )
153 | encoder.setFragmentTexture(CVMetalTextureGetTexture(textureCbCr), index: 2 )
154 |
155 | // Draw each submesh of our mesh
156 | encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
157 |
158 | encoder.popDebugGroup()
159 |
160 | encoder.endEncoding()
161 | }
162 | }
163 |
164 |
--------------------------------------------------------------------------------
/SWOpeningRoll/shared/WorldManager.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 | import SwiftUI
4 | import SDFont
5 |
6 |
7 | class WorldManager : ObservableObject {
8 |
9 | var device: MTLDevice!
10 | var colorPixelFormat: MTLPixelFormat
11 |
12 | var camera: Camera
13 |
14 | var scene: UniformPerScene
15 | var sceneBuffer: MTLBuffer?
16 |
17 | var sdHelperHelveticaBold: SDFontRuntimeHelper?
18 | var sdHelperHelvetica: SDFontRuntimeHelper?
19 |
20 | var textPlaneHelper: PerSceneRenderHelper?
21 |
22 | var textPlane1: SDTextPlane?
23 | var textPlane2: SDTextPlane?
24 | var textPlane3: SDTextPlane?
25 |
26 | public init( device : MTLDevice ) {
27 |
28 | self.device = device
29 | self.colorPixelFormat = .invalid
30 | self.camera = Camera()
31 | self.scene = UniformPerScene()
32 | self.sceneBuffer = UniformPerScene.generateMTLBuffer( device: device, uniformPerScene: &scene )
33 |
34 | // Set this to false after running the App for the first time.
35 | let generatingSDFonts : Bool = true
36 |
37 | if generatingSDFonts {
38 | generateSDFonts( fontName : AnimationConstants.Helvetica, textureSize: AnimationConstants.SDTextureSize )
39 | generateSDFonts( fontName : AnimationConstants.HelveticaBold, textureSize: AnimationConstants.SDTextureSize )
40 | }
41 |
42 | sdHelperHelveticaBold = SDFontRuntimeHelper(
43 | device : device,
44 | fontName : AnimationConstants.HelveticaBold,
45 | fontSize : 12,
46 | fileName : AnimationConstants.HelveticaBold,
47 | path : nil,
48 | usePosixPath : false,
49 | verbose : true
50 | )
51 |
52 | sdHelperHelvetica = SDFontRuntimeHelper(
53 | device : device,
54 | fontName : AnimationConstants.Helvetica,
55 | fontSize : 12,
56 | fileName : AnimationConstants.Helvetica,
57 | path : nil,
58 | usePosixPath : false,
59 | verbose : true
60 | )
61 |
62 | self.textPlaneHelper = PerSceneRenderHelper(
63 | device : device,
64 | bufferUniformPerScene : self.sceneBuffer!
65 | )
66 |
67 | print ("Available fonts in the system: ")
68 | for fontName in SDFontRuntimeHelper.listAvailableFonts() {
69 | print (" [\(fontName)]")
70 | }
71 |
72 | // MARK: - Text 1 "Long time ago..."
73 |
74 | self.textPlane1 = SDTextPlane(
75 | device : device,
76 | dimension : AnimationConstants.Dimension1,
77 | sdHelper : sdHelperHelvetica!,
78 | scaleForTypeSetting : AnimationConstants.Scale1,
79 | uniformSDFont : AnimationConstants.SDFontUniform1
80 | )
81 | self.textPlane1!.setText( textPlain: AnimationConstants.Text1, lineAlignment: .left )
82 |
83 | self.textPlane1!.setAnimation(
84 | temporalPoints : AnimationConstants.TemporalPoints1,
85 | spacialPoints : AnimationConstants.SpacialPoints1,
86 | colors : AnimationConstants.Colors1
87 | )
88 |
89 | // MARK: - Text 2 "STAR WARS"
90 |
91 | self.textPlane2 = SDTextPlane(
92 | device : device,
93 | dimension : AnimationConstants.Dimension2,
94 | sdHelper : sdHelperHelveticaBold!,
95 | scaleForTypeSetting : AnimationConstants.Scale2,
96 | uniformSDFont : AnimationConstants.SDFontUniform2
97 | )
98 | self.textPlane2!.setText( textPlain: AnimationConstants.Text2, lineAlignment: .center )
99 |
100 | self.textPlane2!.setAnimation(
101 | temporalPoints : AnimationConstants.TemporalPoints2,
102 | spacialPoints : AnimationConstants.SpacialPoints2,
103 | colors : AnimationConstants.Colors2
104 | )
105 |
106 | // MARK: - Text 3 Main Text
107 |
108 | self.textPlane3 = SDTextPlane(
109 | device : device,
110 | dimension : AnimationConstants.Dimension3,
111 | sdHelper : sdHelperHelvetica!,
112 | scaleForTypeSetting : AnimationConstants.Scale3,
113 | uniformSDFont : AnimationConstants.SDFontUniform3
114 | )
115 | self.textPlane3!.setText( textPlain: AnimationConstants.Text3, lineAlignment: .center )
116 | self.textPlane3!.rotate90AroundX()
117 |
118 | self.textPlane3!.setAnimation(
119 | temporalPoints : AnimationConstants.TemporalPoints3,
120 | spacialPoints : AnimationConstants.SpacialPoints3,
121 | colors : AnimationConstants.Colors3
122 | )
123 |
124 | textPlane1!.startAnimation()
125 | textPlane2!.startAnimation()
126 | textPlane3!.startAnimation()
127 | }
128 |
129 | func generateSDFonts( fontName : String, textureSize: Int) {
130 |
131 | let sdGeneratorHelvetica = SDFontGenerator(
132 | device : device,
133 | fontName : fontName,
134 | outputTextureSideLen : textureSize,
135 | spreadFactor : 0.2,
136 | upSamplingFactor : 4,
137 | glyphNumCutoff : 512,
138 | verbose : true,
139 | usePosixPath : false
140 | )
141 |
142 | if !sdGeneratorHelvetica.writeToPNGFile(fileName: fontName, path: nil ) {
143 | print ("ERROR: cannot write PNG file for \(fontName).")
144 | }
145 |
146 | if !sdGeneratorHelvetica.writeMetricsToJSONFile(fileName: fontName, path: nil ) {
147 | print ("ERROR: cannot write PNG file for \(fontName).")
148 | }
149 | }
150 |
151 | func createPipelineStates( colorPixelFormat: MTLPixelFormat ) {
152 |
153 | self.colorPixelFormat = colorPixelFormat
154 | self.textPlaneHelper?.createPipelineState( colorPixelFormat: colorPixelFormat )
155 | }
156 |
157 | func updateScreenSizes(_ view: MTKView ){
158 | camera.updateCameraDimension( dimension: SIMD2(x: Float(view.frame.size.width), y: Float(view.frame.size.height) ) )
159 | scene.update( camera: camera )
160 | UniformPerScene.updateMTLBuffer(buffer: sceneBuffer!, uniformPerScene: &scene )
161 | }
162 |
163 | func updateWorld() {
164 |
165 | scene.update( camera: camera )
166 | UniformPerScene.updateMTLBuffer(buffer: sceneBuffer!, uniformPerScene: &scene )
167 | textPlane1!.step()
168 | textPlane2!.step()
169 | textPlane3!.step()
170 |
171 | if !( textPlane1!.animationActive())
172 | && !( textPlane2!.animationActive() )
173 | && !( textPlane3!.animationActive() ) {
174 |
175 | textPlane1!.startAnimation()
176 | textPlane2!.startAnimation()
177 | textPlane3!.startAnimation()
178 | }
179 | }
180 |
181 | func encode( encoder : MTLRenderCommandEncoder ) {
182 |
183 | textPlaneHelper?.renderModelSDFont(
184 | encoder : encoder,
185 | bufferVertexIn : textPlane1!.vertexBuffer!,
186 | bufferUniformPerInstance : textPlane1!.perInstanceBuffer!,
187 | numInstances : 1,
188 | bufferIndices : textPlane1!.indexBuffer!,
189 | numIndices : textPlane1!.numIndices,
190 | bufferUniformSDFont : textPlane1!.sdFontUniformBuffer!,
191 | textureSDFont : textPlane1!.sdFontTexture!,
192 | wireFrame : false
193 | )
194 |
195 | textPlaneHelper?.renderModelSDFont(
196 | encoder : encoder,
197 | bufferVertexIn : textPlane2!.vertexBuffer!,
198 | bufferUniformPerInstance : textPlane2!.perInstanceBuffer!,
199 | numInstances : 1,
200 | bufferIndices : textPlane2!.indexBuffer!,
201 | numIndices : textPlane2!.numIndices,
202 | bufferUniformSDFont : textPlane2!.sdFontUniformBuffer!,
203 | textureSDFont : textPlane2!.sdFontTexture!,
204 | wireFrame : false
205 | )
206 |
207 | textPlaneHelper?.renderModelSDFont(
208 | encoder : encoder,
209 | bufferVertexIn : textPlane3!.vertexBuffer!,
210 | bufferUniformPerInstance : textPlane3!.perInstanceBuffer!,
211 | numInstances : 1,
212 | bufferIndices : textPlane3!.indexBuffer!,
213 | numIndices : textPlane3!.numIndices,
214 | bufferUniformSDFont : textPlane3!.sdFontUniformBuffer!,
215 | textureSDFont : textPlane3!.sdFontTexture!,
216 | wireFrame : false
217 | )
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontRuntimeHelper.swift:
--------------------------------------------------------------------------------
1 | import MetalKit
2 |
3 |
4 | /// SDFontRuntimerHelper performs the type-setting for the signed distance fonts.
5 | /// At the initialization, it takes a signed distance font in MTLTexture and its accompanying list of bounding boxes.
6 | /// It then takes the text and the drawing area as the inputs, performs typesetting with CoreText, and generates
7 | /// the necessary data in a list of pairs of bounding boxes in order to render the text with Metal.
8 | /// The first part of the pair specifies the rectagular region in the drawing area, and the second specifies the
9 | /// bounding box in the signed distance font texture.
10 | /// Please note that the list of the bounding boxes does not have a 1-to-1 mapping to the character sequence of the input text, as
11 | /// the glyphs can contain ligatures, such as "ff" and "fi".
12 | public class SDFontRuntimeHelper {
13 |
14 |
15 | /// The output from typeset() in the list of pairs of rectangular bounds.
16 | public struct GlyphBound {
17 |
18 | /// The rectangular bounding box in the specified drawing area.
19 | public let frameBound : CGRect
20 |
21 | /// The rectangular bounding box of the glyph in the texture coodinate space.
22 | public let textureBound : CGRect
23 | }
24 | let verbose : Bool
25 | let device : MTLDevice
26 | var fontName : String
27 | var font : CTFont?
28 | var signedDistanceBuffer : MTLTexture?
29 | var textureBounds : [ CGRect ]?
30 |
31 | var contextBuffer : [ UInt8 ]
32 | let context : CGContext
33 |
34 | /// Use this initializer, if the font is generated at runtime with SDFontGenerator.
35 | /// - Parameters:
36 | /// - generator: SDFont generator
37 | /// - fontSize: The size of the font used for type setting.
38 | public init( generator: SDFontGenerator, fontSize : Int, verbose : Bool ) {
39 | self.verbose = verbose
40 | self.device = generator.device
41 | self.fontName = generator.fontName
42 | self.font = CTFontCreateWithName( fontName as CFString, CGFloat(fontSize), nil )
43 | self.signedDistanceBuffer = generator.generateMTLTexture()
44 | self.textureBounds = generator.textureBounds
45 | self.contextBuffer = [ 0 ]
46 |
47 | // the following context is used as a dummy parameter to CTRunGetImageBounds().
48 | let colorSpace : CGColorSpace = CGColorSpaceCreateDeviceGray();
49 | let bitmapInfo = CGBitmapInfo( rawValue: CGImageAlphaInfo.none.rawValue | CGImageByteOrderInfo.orderDefault.rawValue | CGImagePixelFormatInfo.packed.rawValue )
50 | self.context = CGContext(
51 | data: &contextBuffer,
52 | width: 1,
53 | height: 1,
54 | bitsPerComponent: 8,
55 | bytesPerRow: 1,
56 | space: colorSpace,
57 | bitmapInfo: bitmapInfo.rawValue
58 | )!
59 | }
60 |
61 | /// - Parameters:
62 | /// - device: The metal device
63 | /// - fontName: The name of the font used. This must be the same one used for the generation of the signed distance font.
64 | /// - fontSize: The size of the font used for type setting.
65 | /// - fileName: The file name without extension for the PNG and JSON files to be loaded.
66 | /// - path: The subdirectory of the path in which the JSON file is stored.
67 | /// Use this if the signed distance fonts were generated off-line and stored in the PNG/JSON file pair.
68 | public init( device: MTLDevice, fontName: String, fontSize: Int, fileName : String, path : String?, usePosixPath : Bool, verbose : Bool ) {
69 | self.verbose = verbose
70 | self.device = device
71 | self.fontName = fontName
72 | self.font = CTFontCreateWithName( fontName as CFString, CGFloat(fontSize), nil )
73 | self.signedDistanceBuffer = SDFontIOHandler.loadTextureFromPNGFile(
74 | device : device,
75 | fileName : fileName,
76 | path : path,
77 | usePosixPath : usePosixPath,
78 | verbose : verbose
79 | )
80 | self.textureBounds = SDFontIOHandler.loadMetricsFromJSONFile(
81 | fileName : fileName,
82 | path : path,
83 | usePosixPath : usePosixPath,
84 | verbose : verbose
85 | )
86 |
87 | self.contextBuffer = [ 0 ]
88 |
89 | // the following context is used as a dummy parameter to CTRunGetImageBounds().
90 | let colorSpace : CGColorSpace = CGColorSpaceCreateDeviceGray();
91 | let bitmapInfo = CGBitmapInfo( rawValue: CGImageAlphaInfo.none.rawValue | CGImageByteOrderInfo.orderDefault.rawValue | CGImagePixelFormatInfo.packed.rawValue )
92 | self.context = CGContext(
93 | data: &contextBuffer,
94 | width: 1,
95 | height: 1,
96 | bitsPerComponent: 8,
97 | bytesPerRow: 1,
98 | space: colorSpace,
99 | bitmapInfo: bitmapInfo.rawValue
100 | )!
101 | }
102 |
103 | /// - Returns: The metal texture of type MTLPixelFormatR8Unorm that contains the signed distance values.
104 | public func texture() -> MTLTexture {
105 | return signedDistanceBuffer!
106 | }
107 |
108 | /// Performs type setting and generates the data in a list of bounding boxes for the given text to render it with a Metal shader.
109 | /// - Parameters:
110 | /// - frameRect: The drawing area to which the text is rendered.
111 | /// - textPlain: The plain text to be type-set and drawn to the drawing area.
112 | /// - lineAlignment: .left, .center, or .right
113 | /// - Returns: List of pairs of bounding boxes. The first bounding box of each pair specifies the rectangular resion in the drawing area to which the glyph is drawn. The second bounding box specifies the region in the texture.
114 | public func typeset( frameRect: CGRect, textPlain: String, lineAlignment : CTTextAlignment ) -> [GlyphBound] {
115 |
116 | var alignment : CTTextAlignment = lineAlignment
117 | var settings : CTParagraphStyleSetting?
118 | withUnsafePointer( to: &alignment) { (ptr: UnsafePointer) in
119 | settings = CTParagraphStyleSetting(
120 | spec : .alignment,
121 | valueSize : MemoryLayout.size,
122 | value : ptr
123 | )
124 | }
125 | let paragraphStyle = CTParagraphStyleCreate([ settings! ], 1)
126 |
127 | let attr = [ kCTFontAttributeName : font!,
128 | kCTParagraphStyleAttributeName : paragraphStyle
129 | ] as CFDictionary
130 | let textAttr = CFAttributedStringCreate( nil, textPlain as CFString, attr )
131 | let textRange = CFRange( location: 0, length: CFAttributedStringGetLength( textAttr ) );
132 | let framePath = CGPath(rect: frameRect, transform: nil )
133 | let frameSetter = CTFramesetterCreateWithAttributedString( textAttr! )
134 | let ctFrame = CTFramesetterCreateFrame( frameSetter, textRange, framePath, nil )
135 |
136 | let frameBoundingRect = framePath.boundingBoxOfPath;
137 | let ctLines = CTFrameGetLines( ctFrame ) as [AnyObject] as! [CTLine]
138 | var ctLineOrigins = [CGPoint]( repeating: CGPoint( x:0, y:0 ), count: ctLines.count )
139 | CTFrameGetLineOrigins( ctFrame, CFRangeMake(0,0), &ctLineOrigins )
140 |
141 | var outArray : [GlyphBound] = []
142 |
143 | for i in 0 ..< ctLines.count {
144 |
145 | let ctLine = ctLines[i]
146 | let ctLineOrigin = ctLineOrigins[i]
147 | let ctRuns = CTLineGetGlyphRuns( ctLine ) as [AnyObject] as! [CTRun]
148 |
149 | for ctRun in ctRuns {
150 |
151 | let glyphCount = CTRunGetGlyphCount( ctRun )
152 |
153 | var glyphs = [CGGlyph]( repeating: CGGlyph(), count: glyphCount )
154 | CTRunGetGlyphs( ctRun, CFRangeMake(0,0), &glyphs )
155 |
156 | for j in 0 ..< glyphCount {
157 |
158 | let glyph = glyphs[j]
159 | let glyphBound = CTRunGetImageBounds( ctRun, context, CFRangeMake(j, 1));
160 |
161 | let frameBound = CGRect(
162 | x : frameBoundingRect.origin.x + ctLineOrigin.x + glyphBound.origin.x,
163 | y : frameBoundingRect.origin.y + ctLineOrigin.y + glyphBound.origin.y,
164 | width : glyphBound.size.width,
165 | height : glyphBound.size.height
166 | )
167 | let textureBound = textureBounds![ Int(glyph) ]
168 | outArray.append( GlyphBound( frameBound: frameBound, textureBound: textureBound ) )
169 | }
170 | }
171 | }
172 |
173 | return outArray
174 | }
175 |
176 | /// - Returns: The list of font names available in the system.
177 | ///
178 | /// Please consult https://developer.apple.com/fonts/ for the official information.
179 | public static func listAvailableFonts() -> [String] {
180 | #if os(iOS)
181 | var fontList : [String] = []
182 | for family in UIFont.familyNames {
183 | for fontName in UIFont.fontNames(forFamilyName: family) {
184 | fontList.append( fontName )
185 | }
186 | }
187 | return fontList
188 | #else
189 | return NSFontManager.shared.availableFonts
190 | #endif
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/SDFont/SDFont/SDFontGlyphPackingFinder.swift:
--------------------------------------------------------------------------------
1 | import CoreGraphics
2 |
3 | class SDFontGlyphPackingFinder {
4 |
5 | static let ALPHA : CGFloat = 0.1
6 | static let NUM_TRIALS_IMPROVEMENT : Int = 5
7 | static let MAX_NUM_TRIALS_IMPROVEMENT : Int = 1000
8 | static let FONT_SIZE_FOR_PACKING : CGFloat = 64.0
9 | static let FONT_SIZE_MINIMUM_ALLOWED : CGFloat = 5.0
10 |
11 | let fontName : CFString
12 | let spreadFactor : CGFloat
13 | let drawAreaSideLen : Int
14 | let glyphNumCutoff : Int
15 | let verbose : Bool
16 | var numGlyphs : Int
17 | var referenceCGFont : CGFont?
18 | var spreadThickness : CGFloat
19 | var referenceFontSize : CGFloat
20 | var glyphBoundsArray : [ SignedDistanceFontGlyphBounds ]
21 | var maxOuterGlyphBoundsSideLen : CGFloat
22 | var meanInnerGlyphBoundsSideLen : CGFloat
23 | var occupancyRate : CGFloat
24 |
25 | init( fontName: CFString, spreadFactor: CGFloat, drawAreaSideLen : Int, glyphNumCutoff : Int, verbose : Bool ) {
26 |
27 | self.fontName = fontName
28 | self.spreadFactor = spreadFactor
29 | self.drawAreaSideLen = drawAreaSideLen
30 | self.glyphNumCutoff = glyphNumCutoff
31 | self.verbose = verbose
32 | self.numGlyphs = 0
33 | self.spreadThickness = 0.0
34 | self.referenceFontSize = 0.0
35 | self.glyphBoundsArray = []
36 | self.maxOuterGlyphBoundsSideLen = 0.0
37 | self.meanInnerGlyphBoundsSideLen = 0.0
38 | self.occupancyRate = 0.0
39 |
40 | getFontAndCheckFontName( fontName )
41 |
42 | self.meanInnerGlyphBoundsSideLen = findMeanInnerSideLen( fontSize : Self.FONT_SIZE_FOR_PACKING )
43 | self.spreadThickness = spreadFactor * meanInnerGlyphBoundsSideLen
44 | self.maxOuterGlyphBoundsSideLen = findMaxOuterSideLen( fontSize : Self.FONT_SIZE_FOR_PACKING )
45 |
46 | let bestArea = findBestPackedArea( fontSize : Self.FONT_SIZE_FOR_PACKING )
47 | let suggestedFontSize = floor( Self.FONT_SIZE_FOR_PACKING * CGFloat( drawAreaSideLen ) / max( bestArea.width, bestArea.height ) )
48 | self.referenceFontSize = findBestFontSize( initialSize: suggestedFontSize )
49 | generateBoundingBoxes( fontSize : self.referenceFontSize )
50 |
51 | var areaSum : CGFloat = 0.0
52 |
53 | for bound in glyphBoundsArray {
54 |
55 | areaSum += ( bound.outer.size.width * bound.outer.size.height )
56 | }
57 | self.occupancyRate = areaSum / ( CGFloat(self.drawAreaSideLen) * CGFloat(self.drawAreaSideLen) )
58 | }
59 |
60 | func findBestFontSize( initialSize : CGFloat ) -> CGFloat {
61 |
62 | var fontSize = initialSize
63 | let allowedAreaSize = CGFloat(self.drawAreaSideLen)
64 |
65 | while fontSize >= Self.FONT_SIZE_MINIMUM_ALLOWED {
66 |
67 | self.meanInnerGlyphBoundsSideLen = findMeanInnerSideLen( fontSize : fontSize )
68 | self.spreadThickness = spreadFactor * self.meanInnerGlyphBoundsSideLen
69 | self.maxOuterGlyphBoundsSideLen = findMaxOuterSideLen( fontSize : fontSize )
70 |
71 | let area = findPackedArea( widthLimit: allowedAreaSize, generateBoundingBoxes : false, fontSize : fontSize )
72 |
73 | if area.width <= allowedAreaSize && area.height <= allowedAreaSize {
74 | return fontSize
75 | }
76 | fontSize -= 1.0
77 | }
78 |
79 | print ( "WARNING: Glyphs can not be fully packed to the texture of size [\(self.drawAreaSideLen)] for the minimum font size [\(Self.FONT_SIZE_FOR_PACKING)]." )
80 | return fontSize
81 | }
82 |
83 | func getFontAndCheckFontName( _ fontName : CFString ) {
84 |
85 | self.referenceCGFont = CGFont( fontName )
86 | self.referenceFontSize = Self.FONT_SIZE_FOR_PACKING
87 |
88 | let glyphCount = self.referenceCGFont!.numberOfGlyphs
89 |
90 | self.numGlyphs = min( self.glyphNumCutoff, glyphCount )
91 |
92 | if verbose {
93 | print ("INFO: Number of glyphs in font \(fontName): [\(glyphCount)].")
94 | print ("INFO: Number of glyphs after cut-off: [\(numGlyphs)].")
95 | }
96 | }
97 |
98 | func findBestPackedArea( fontSize : CGFloat ) -> CGSize {
99 |
100 | let sqNumGlyphs = sqrt( Double(numGlyphs) )
101 | let initialAreaWidthLimit = ( self.meanInnerGlyphBoundsSideLen + spreadThickness * 2.0 ) * sqNumGlyphs
102 | var area = findPackedArea( widthLimit: initialAreaWidthLimit, generateBoundingBoxes : false, fontSize : fontSize )
103 | var bestArea = area
104 | var numTimesNotImproved = 0
105 |
106 | for _ in 0 ..< Self.MAX_NUM_TRIALS_IMPROVEMENT {
107 |
108 | if abs(bestArea.width - bestArea.height) <= abs(area.width - area.height) {
109 |
110 | numTimesNotImproved += 1
111 |
112 | if numTimesNotImproved >= Self.NUM_TRIALS_IMPROVEMENT {
113 | return bestArea
114 | }
115 | }
116 | else {
117 | bestArea = area
118 | }
119 | let areaWidthLimit = area.width + (area.height - area.width) * Self.ALPHA
120 | area = findPackedArea( widthLimit: areaWidthLimit, generateBoundingBoxes : false, fontSize : fontSize )
121 | }
122 |
123 | print ( "WARNING: Feasible packing area for font [\(fontName)] can not be found. Using [\(bestArea)].")
124 | return bestArea
125 | }
126 |
127 | func findMeanInnerSideLen( fontSize : CGFloat ) -> CGFloat {
128 |
129 | var total : CGFloat = 0.0
130 |
131 | for i in 0 ..< self.numGlyphs {
132 |
133 | var g = CGGlyph(i)
134 | var bboxes = UnsafeMutablePointer.allocate( capacity: 1 )
135 |
136 | if ( !referenceCGFont!.getGlyphBBoxes( glyphs: [g], count: 1, bboxes: bboxes ) ) {
137 | print ( "WARNING: Cant't retrieve BBox for glyph [\(g)].")
138 | return 0.0
139 | }
140 |
141 | let fontScaleFactor = fontSize / CGFloat( referenceCGFont!.unitsPerEm )
142 |
143 | let rect = CGRect(
144 | origin: CGPoint( x: bboxes[0].origin.x * fontScaleFactor, y: bboxes[0].origin.y * fontScaleFactor ),
145 | size: CGSize( width: bboxes[0].width * fontScaleFactor, height: bboxes[0].height * fontScaleFactor )
146 | )
147 |
148 | total += ( rect.width + rect.height )
149 | }
150 |
151 | return total / CGFloat( 2 * self.numGlyphs )
152 | }
153 |
154 | func findMaxOuterSideLen( fontSize : CGFloat ) -> CGFloat {
155 |
156 | var maxSideLen : CGFloat = 0.0
157 |
158 | for i in 0 ..< numGlyphs {
159 | var g = CGGlyph(i)
160 |
161 | var bboxes = UnsafeMutablePointer.allocate( capacity: 1 )
162 |
163 | if ( !referenceCGFont!.getGlyphBBoxes( glyphs: [g], count: 1, bboxes: bboxes ) ) {
164 | print ( "WARNING: Cant't retrieve BBox for glyph [\(g)].")
165 | return 0.0
166 | }
167 |
168 | let fontScaleFactor = fontSize / CGFloat( referenceCGFont!.unitsPerEm )
169 |
170 | let rect = CGRect(
171 | origin: CGPoint( x: bboxes[0].origin.x * fontScaleFactor, y: bboxes[0].origin.y * fontScaleFactor ),
172 | size: CGSize( width: bboxes[0].width * fontScaleFactor, height: bboxes[0].height * fontScaleFactor )
173 | )
174 |
175 | maxSideLen = max( maxSideLen, rect.width, rect.height )
176 | }
177 |
178 | return maxSideLen + (self.spreadThickness * 2.0)
179 | }
180 |
181 |
182 | func generateBoundingBoxes( fontSize : CGFloat ) {
183 |
184 | let _ = findPackedArea( widthLimit: CGFloat(self.drawAreaSideLen), generateBoundingBoxes : true, fontSize : fontSize )
185 | }
186 |
187 | // pack the glyph left-to-right and then bottom-to-top in the area with the specified width.
188 | func findPackedArea( widthLimit: CGFloat, generateBoundingBoxes : Bool, fontSize : CGFloat ) -> CGSize {
189 |
190 | var xAdvance : CGFloat = 0.0
191 | var yAdvance : CGFloat = 0.0
192 | var maxHeightCurrentRow : CGFloat = 0.0
193 | var maxWidth : CGFloat = 0.0
194 |
195 | for i in 0 ..< self.numGlyphs {
196 |
197 | var g = CGGlyph(i)
198 |
199 | var bboxes = UnsafeMutablePointer.allocate( capacity: 1 )
200 |
201 | if ( !referenceCGFont!.getGlyphBBoxes( glyphs: [g], count: 1, bboxes: bboxes ) ) {
202 | print ( "WARNING: Cant't retrieve BBox for glyph [\(g)].")
203 | return CGSize( width: 0, height: 0 )
204 | }
205 |
206 | let fontScaleFactor = fontSize / CGFloat( referenceCGFont!.unitsPerEm )
207 |
208 | let rect = CGRect(
209 | origin: CGPoint( x: bboxes[0].origin.x * fontScaleFactor, y: bboxes[0].origin.y * fontScaleFactor ),
210 | size: CGSize( width: bboxes[0].width * fontScaleFactor, height: bboxes[0].height * fontScaleFactor )
211 | )
212 |
213 | // outer sizes rounded up to the integer
214 | let outerWidth = ceil( rect.width + ( spreadThickness * 2.0 ) )
215 | let outerHeight = ceil( rect.height + ( spreadThickness * 2.0 ) )
216 |
217 | if xAdvance + outerWidth > widthLimit {
218 |
219 | maxWidth = max( maxWidth, xAdvance )
220 | xAdvance = 0.0
221 | yAdvance += maxHeightCurrentRow
222 | maxHeightCurrentRow = outerHeight
223 | }
224 | else {
225 | maxHeightCurrentRow = max( maxHeightCurrentRow, outerHeight )
226 | }
227 |
228 | if generateBoundingBoxes {
229 | let bounds = SignedDistanceFontGlyphBounds(
230 | outerOrigin : CGPoint( x: xAdvance, y: yAdvance ),
231 | outerSize : CGSize( width: outerWidth, height: outerHeight ),
232 | spreadThickness : spreadThickness,
233 | innerSize : rect.size,
234 | textureSideLen : CGFloat(self.drawAreaSideLen)
235 | )
236 | glyphBoundsArray.append( bounds )
237 | }
238 |
239 | xAdvance += outerWidth
240 |
241 | }
242 | if xAdvance > 0.0 {
243 |
244 | maxWidth = max( maxWidth, xAdvance )
245 | xAdvance = 0.0
246 | yAdvance += maxHeightCurrentRow
247 | maxHeightCurrentRow = 0.0
248 | }
249 |
250 | return CGSize( width: maxWidth, height: yAdvance + maxHeightCurrentRow )
251 | }
252 | }
253 |
--------------------------------------------------------------------------------