|
@@ -29,18 +29,18 @@
|
|
|
|
|
|
import Foundation
|
|
import Foundation
|
|
import MetalKit
|
|
import MetalKit
|
|
-import SpineShadersStructs
|
|
|
|
import Spine
|
|
import Spine
|
|
import SpineCppLite
|
|
import SpineCppLite
|
|
|
|
+import SpineShadersStructs
|
|
|
|
|
|
protocol SpineRendererDelegate: AnyObject {
|
|
protocol SpineRendererDelegate: AnyObject {
|
|
func spineRendererWillUpdate(_ spineRenderer: SpineRenderer)
|
|
func spineRendererWillUpdate(_ spineRenderer: SpineRenderer)
|
|
func spineRenderer(_ spineRenderer: SpineRenderer, needsUpdate delta: TimeInterval)
|
|
func spineRenderer(_ spineRenderer: SpineRenderer, needsUpdate delta: TimeInterval)
|
|
func spineRendererDidUpdate(_ spineRenderer: SpineRenderer)
|
|
func spineRendererDidUpdate(_ spineRenderer: SpineRenderer)
|
|
-
|
|
|
|
|
|
+
|
|
func spineRendererWillDraw(_ spineRenderer: SpineRenderer)
|
|
func spineRendererWillDraw(_ spineRenderer: SpineRenderer)
|
|
func spineRendererDidDraw(_ spineRenderer: SpineRenderer)
|
|
func spineRendererDidDraw(_ spineRenderer: SpineRenderer)
|
|
-
|
|
|
|
|
|
+
|
|
func spineRendererDidUpdate(_ spineRenderer: SpineRenderer, scaleX: CGFloat, scaleY: CGFloat, offsetX: CGFloat, offsetY: CGFloat, size: CGSize)
|
|
func spineRendererDidUpdate(_ spineRenderer: SpineRenderer, scaleX: CGFloat, scaleY: CGFloat, offsetX: CGFloat, offsetY: CGFloat, size: CGSize)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -50,11 +50,11 @@ protocol SpineRendererDataSource: AnyObject {
|
|
}
|
|
}
|
|
|
|
|
|
internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
-
|
|
|
|
|
|
+
|
|
private let device: MTLDevice
|
|
private let device: MTLDevice
|
|
private let textures: [MTLTexture]
|
|
private let textures: [MTLTexture]
|
|
private let commandQueue: MTLCommandQueue
|
|
private let commandQueue: MTLCommandQueue
|
|
-
|
|
|
|
|
|
+
|
|
private var sizeInPoints: CGSize = .zero
|
|
private var sizeInPoints: CGSize = .zero
|
|
private var viewPortSize = vector_uint2(0, 0)
|
|
private var viewPortSize = vector_uint2(0, 0)
|
|
private var transform = SpineTransform(
|
|
private var transform = SpineTransform(
|
|
@@ -65,17 +65,17 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
internal var lastDraw: CFTimeInterval = 0
|
|
internal var lastDraw: CFTimeInterval = 0
|
|
internal var waitUntilCompleted = false
|
|
internal var waitUntilCompleted = false
|
|
private var pipelineStatesByBlendMode = [Int: MTLRenderPipelineState]()
|
|
private var pipelineStatesByBlendMode = [Int: MTLRenderPipelineState]()
|
|
-
|
|
|
|
|
|
+
|
|
private static let numberOfBuffers = 3
|
|
private static let numberOfBuffers = 3
|
|
- private static let defaultBufferSize = 32 * 1024 // 32KB
|
|
|
|
-
|
|
|
|
|
|
+ private static let defaultBufferSize = 32 * 1024 // 32KB
|
|
|
|
+
|
|
private var buffers = [MTLBuffer]()
|
|
private var buffers = [MTLBuffer]()
|
|
private let bufferingSemaphore = DispatchSemaphore(value: SpineRenderer.numberOfBuffers)
|
|
private let bufferingSemaphore = DispatchSemaphore(value: SpineRenderer.numberOfBuffers)
|
|
private var currentBufferIndex: Int = 0
|
|
private var currentBufferIndex: Int = 0
|
|
-
|
|
|
|
|
|
+
|
|
weak var dataSource: SpineRendererDataSource?
|
|
weak var dataSource: SpineRendererDataSource?
|
|
weak var delegate: SpineRendererDelegate?
|
|
weak var delegate: SpineRendererDelegate?
|
|
-
|
|
|
|
|
|
+
|
|
internal init(
|
|
internal init(
|
|
device: MTLDevice,
|
|
device: MTLDevice,
|
|
commandQueue: MTLCommandQueue,
|
|
commandQueue: MTLCommandQueue,
|
|
@@ -85,18 +85,19 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
) throws {
|
|
) throws {
|
|
self.device = device
|
|
self.device = device
|
|
self.commandQueue = commandQueue
|
|
self.commandQueue = commandQueue
|
|
-
|
|
|
|
|
|
+
|
|
let bundle: Bundle
|
|
let bundle: Bundle
|
|
- #if SWIFT_PACKAGE // SPM
|
|
|
|
- bundle = .module
|
|
|
|
- #else // CocoaPods
|
|
|
|
- let bundleURL = Bundle(for: SpineRenderer.self).url(forResource: "SpineBundle", withExtension: "bundle")
|
|
|
|
- bundle = Bundle(url: bundleURL!)!
|
|
|
|
|
|
+ #if SWIFT_PACKAGE // SPM
|
|
|
|
+ bundle = .module
|
|
|
|
+ #else // CocoaPods
|
|
|
|
+ let bundleURL = Bundle(for: SpineRenderer.self).url(forResource: "SpineBundle", withExtension: "bundle")
|
|
|
|
+ bundle = Bundle(url: bundleURL!)!
|
|
#endif
|
|
#endif
|
|
-
|
|
|
|
|
|
+
|
|
let defaultLibrary = try device.makeDefaultLibrary(bundle: bundle)
|
|
let defaultLibrary = try device.makeDefaultLibrary(bundle: bundle)
|
|
let textureLoader = MTKTextureLoader(device: device)
|
|
let textureLoader = MTKTextureLoader(device: device)
|
|
- textures = try atlasPages
|
|
|
|
|
|
+ textures =
|
|
|
|
+ try atlasPages
|
|
.compactMap { $0.cgImage }
|
|
.compactMap { $0.cgImage }
|
|
.map {
|
|
.map {
|
|
try textureLoader.newTexture(
|
|
try textureLoader.newTexture(
|
|
@@ -107,12 +108,12 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
]
|
|
]
|
|
)
|
|
)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
let blendModes = [
|
|
let blendModes = [
|
|
SPINE_BLEND_MODE_NORMAL,
|
|
SPINE_BLEND_MODE_NORMAL,
|
|
SPINE_BLEND_MODE_ADDITIVE,
|
|
SPINE_BLEND_MODE_ADDITIVE,
|
|
SPINE_BLEND_MODE_MULTIPLY,
|
|
SPINE_BLEND_MODE_MULTIPLY,
|
|
- SPINE_BLEND_MODE_SCREEN
|
|
|
|
|
|
+ SPINE_BLEND_MODE_SCREEN,
|
|
]
|
|
]
|
|
for blendMode in blendModes {
|
|
for blendMode in blendModes {
|
|
let descriptor = MTLRenderPipelineDescriptor()
|
|
let descriptor = MTLRenderPipelineDescriptor()
|
|
@@ -125,15 +126,15 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
)
|
|
)
|
|
pipelineStatesByBlendMode[Int(blendMode.rawValue)] = try device.makeRenderPipelineState(descriptor: descriptor)
|
|
pipelineStatesByBlendMode[Int(blendMode.rawValue)] = try device.makeRenderPipelineState(descriptor: descriptor)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
super.init()
|
|
super.init()
|
|
-
|
|
|
|
|
|
+
|
|
increaseBuffersSize(to: SpineRenderer.defaultBufferSize)
|
|
increaseBuffersSize(to: SpineRenderer.defaultBufferSize)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
|
|
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
|
|
guard let spineView = view as? SpineUIView else { return }
|
|
guard let spineView = view as? SpineUIView else { return }
|
|
-
|
|
|
|
|
|
+
|
|
sizeInPoints = CGSize(width: size.width / UIScreen.main.scale, height: size.height / UIScreen.main.scale)
|
|
sizeInPoints = CGSize(width: size.width / UIScreen.main.scale, height: size.height / UIScreen.main.scale)
|
|
viewPortSize = vector_uint2(UInt32(size.width), UInt32(size.height))
|
|
viewPortSize = vector_uint2(UInt32(size.width), UInt32(size.height))
|
|
setTransform(
|
|
setTransform(
|
|
@@ -142,35 +143,36 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
alignment: spineView.alignment
|
|
alignment: spineView.alignment
|
|
)
|
|
)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func draw(in view: MTKView) {
|
|
func draw(in view: MTKView) {
|
|
guard dataSource?.isPlaying(self) ?? false else {
|
|
guard dataSource?.isPlaying(self) ?? false else {
|
|
lastDraw = CACurrentMediaTime()
|
|
lastDraw = CACurrentMediaTime()
|
|
return
|
|
return
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
callNeedsUpdate()
|
|
callNeedsUpdate()
|
|
-
|
|
|
|
|
|
+
|
|
// Tripple Buffering
|
|
// Tripple Buffering
|
|
// Source: https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/TripleBuffering.html#//apple_ref/doc/uid/TP40016642-CH5-SW1
|
|
// Source: https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/TripleBuffering.html#//apple_ref/doc/uid/TP40016642-CH5-SW1
|
|
bufferingSemaphore.wait()
|
|
bufferingSemaphore.wait()
|
|
currentBufferIndex = (currentBufferIndex + 1) % SpineRenderer.numberOfBuffers
|
|
currentBufferIndex = (currentBufferIndex + 1) % SpineRenderer.numberOfBuffers
|
|
-
|
|
|
|
|
|
+
|
|
guard let renderCommands = dataSource?.renderCommands(self),
|
|
guard let renderCommands = dataSource?.renderCommands(self),
|
|
- let commandBuffer = commandQueue.makeCommandBuffer(),
|
|
|
|
- let renderPassDescriptor = view.currentRenderPassDescriptor,
|
|
|
|
- let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
|
|
|
|
- // this can happen if,
|
|
|
|
- // - CAMetalLayer is configured with drawable timeout, and CAMetalLayer is run out of Drawable
|
|
|
|
- // - CAMetalLayer is added to the window with frame size of zero or incorrect layout constraint -> currentRenderPassDescriptor is null
|
|
|
|
|
|
+ let commandBuffer = commandQueue.makeCommandBuffer(),
|
|
|
|
+ let renderPassDescriptor = view.currentRenderPassDescriptor,
|
|
|
|
+ let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
|
|
|
|
+ else {
|
|
|
|
+ // this can happen if,
|
|
|
|
+ // - CAMetalLayer is configured with drawable timeout, and CAMetalLayer is run out of Drawable
|
|
|
|
+ // - CAMetalLayer is added to the window with frame size of zero or incorrect layout constraint -> currentRenderPassDescriptor is null
|
|
bufferingSemaphore.signal()
|
|
bufferingSemaphore.signal()
|
|
return
|
|
return
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
delegate?.spineRendererWillDraw(self)
|
|
delegate?.spineRendererWillDraw(self)
|
|
draw(renderCommands: renderCommands, renderEncoder: renderEncoder, in: view)
|
|
draw(renderCommands: renderCommands, renderEncoder: renderEncoder, in: view)
|
|
delegate?.spineRendererDidDraw(self)
|
|
delegate?.spineRendererDidDraw(self)
|
|
-
|
|
|
|
|
|
+
|
|
renderEncoder.endEncoding()
|
|
renderEncoder.endEncoding()
|
|
view.currentDrawable.flatMap {
|
|
view.currentDrawable.flatMap {
|
|
commandBuffer.present($0)
|
|
commandBuffer.present($0)
|
|
@@ -183,14 +185,14 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
commandBuffer.waitUntilCompleted()
|
|
commandBuffer.waitUntilCompleted()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private func setTransform(bounds: CGRect, mode: Spine.ContentMode, alignment: Spine.Alignment) {
|
|
private func setTransform(bounds: CGRect, mode: Spine.ContentMode, alignment: Spine.Alignment) {
|
|
let x = -bounds.minX - bounds.width / 2.0
|
|
let x = -bounds.minX - bounds.width / 2.0
|
|
let y = -bounds.minY - bounds.height / 2.0
|
|
let y = -bounds.minY - bounds.height / 2.0
|
|
-
|
|
|
|
|
|
+
|
|
var scaleX: CGFloat = 1.0
|
|
var scaleX: CGFloat = 1.0
|
|
var scaleY: CGFloat = 1.0
|
|
var scaleY: CGFloat = 1.0
|
|
-
|
|
|
|
|
|
+
|
|
switch mode {
|
|
switch mode {
|
|
case .fit:
|
|
case .fit:
|
|
scaleX = min(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
|
|
scaleX = min(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
|
|
@@ -199,16 +201,16 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
scaleX = max(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
|
|
scaleX = max(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
|
|
scaleY = scaleX
|
|
scaleY = scaleX
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
let offsetX = abs(sizeInPoints.width - bounds.width * scaleX) / 2 * alignment.x
|
|
let offsetX = abs(sizeInPoints.width - bounds.width * scaleX) / 2 * alignment.x
|
|
let offsetY = abs(sizeInPoints.height - bounds.height * scaleY) / 2 * alignment.y
|
|
let offsetY = abs(sizeInPoints.height - bounds.height * scaleY) / 2 * alignment.y
|
|
-
|
|
|
|
|
|
+
|
|
transform = SpineTransform(
|
|
transform = SpineTransform(
|
|
translation: vector_float2(Float(x), Float(y)),
|
|
translation: vector_float2(Float(x), Float(y)),
|
|
scale: vector_float2(Float(scaleX * UIScreen.main.scale), Float(scaleY * UIScreen.main.scale)),
|
|
scale: vector_float2(Float(scaleX * UIScreen.main.scale), Float(scaleY * UIScreen.main.scale)),
|
|
offset: vector_float2(Float(offsetX * UIScreen.main.scale), Float(offsetY * UIScreen.main.scale))
|
|
offset: vector_float2(Float(offsetX * UIScreen.main.scale), Float(offsetY * UIScreen.main.scale))
|
|
)
|
|
)
|
|
-
|
|
|
|
|
|
+
|
|
delegate?.spineRendererDidUpdate(
|
|
delegate?.spineRendererDidUpdate(
|
|
self,
|
|
self,
|
|
scaleX: scaleX,
|
|
scaleX: scaleX,
|
|
@@ -218,37 +220,37 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
size: sizeInPoints
|
|
size: sizeInPoints
|
|
)
|
|
)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private func callNeedsUpdate() {
|
|
private func callNeedsUpdate() {
|
|
if lastDraw == 0 {
|
|
if lastDraw == 0 {
|
|
lastDraw = CACurrentMediaTime()
|
|
lastDraw = CACurrentMediaTime()
|
|
}
|
|
}
|
|
let delta = CACurrentMediaTime() - lastDraw
|
|
let delta = CACurrentMediaTime() - lastDraw
|
|
- delegate?.spineRendererWillUpdate(self)
|
|
|
|
|
|
+ delegate?.spineRendererWillUpdate(self)
|
|
delegate?.spineRenderer(self, needsUpdate: delta)
|
|
delegate?.spineRenderer(self, needsUpdate: delta)
|
|
lastDraw = CACurrentMediaTime()
|
|
lastDraw = CACurrentMediaTime()
|
|
delegate?.spineRendererDidUpdate(self)
|
|
delegate?.spineRendererDidUpdate(self)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private func draw(renderCommands: [RenderCommand], renderEncoder: MTLRenderCommandEncoder, in view: MTKView) {
|
|
private func draw(renderCommands: [RenderCommand], renderEncoder: MTLRenderCommandEncoder, in view: MTKView) {
|
|
let allVertices = renderCommands.map { renderCommand in
|
|
let allVertices = renderCommands.map { renderCommand in
|
|
Array(renderCommand.getVertices())
|
|
Array(renderCommand.getVertices())
|
|
}
|
|
}
|
|
let vertices = allVertices.flatMap { $0 }
|
|
let vertices = allVertices.flatMap { $0 }
|
|
let verticesSize = MemoryLayout<SpineVertex>.stride * vertices.count
|
|
let verticesSize = MemoryLayout<SpineVertex>.stride * vertices.count
|
|
-
|
|
|
|
|
|
+
|
|
guard verticesSize > 0 else {
|
|
guard verticesSize > 0 else {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
var vertexBuffer = buffers[currentBufferIndex]
|
|
var vertexBuffer = buffers[currentBufferIndex]
|
|
var vertexBufferSize = vertexBuffer.length
|
|
var vertexBufferSize = vertexBuffer.length
|
|
-
|
|
|
|
|
|
+
|
|
if vertexBufferSize < verticesSize {
|
|
if vertexBufferSize < verticesSize {
|
|
increaseBuffersSize(to: verticesSize)
|
|
increaseBuffersSize(to: verticesSize)
|
|
vertexBuffer = buffers[currentBufferIndex]
|
|
vertexBuffer = buffers[currentBufferIndex]
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
renderEncoder.setViewport(
|
|
renderEncoder.setViewport(
|
|
MTLViewport(
|
|
MTLViewport(
|
|
originX: 0.0,
|
|
originX: 0.0,
|
|
@@ -259,9 +261,9 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
zfar: 1.0
|
|
zfar: 1.0
|
|
)
|
|
)
|
|
)
|
|
)
|
|
-
|
|
|
|
|
|
+
|
|
memcpy(vertexBuffer.contents(), vertices, verticesSize)
|
|
memcpy(vertexBuffer.contents(), vertices, verticesSize)
|
|
-
|
|
|
|
|
|
+
|
|
renderEncoder.setVertexBuffer(
|
|
renderEncoder.setVertexBuffer(
|
|
vertexBuffer,
|
|
vertexBuffer,
|
|
offset: 0,
|
|
offset: 0,
|
|
@@ -277,7 +279,7 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
length: MemoryLayout.size(ofValue: viewPortSize),
|
|
length: MemoryLayout.size(ofValue: viewPortSize),
|
|
index: Int(SpineVertexInputIndexViewportSize.rawValue)
|
|
index: Int(SpineVertexInputIndexViewportSize.rawValue)
|
|
)
|
|
)
|
|
-
|
|
|
|
|
|
+
|
|
// Buffer Bindings
|
|
// Buffer Bindings
|
|
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/BufferBindings.html#//apple_ref/doc/uid/TP40016642-CH28-SW3
|
|
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/BufferBindings.html#//apple_ref/doc/uid/TP40016642-CH28-SW3
|
|
var vertexStart = 0
|
|
var vertexStart = 0
|
|
@@ -286,9 +288,9 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
renderEncoder.setRenderPipelineState(pipelineState)
|
|
renderEncoder.setRenderPipelineState(pipelineState)
|
|
-
|
|
|
|
|
|
+
|
|
let vertices = allVertices[index]
|
|
let vertices = allVertices[index]
|
|
-
|
|
|
|
|
|
+
|
|
let textureIndex = Int(renderCommand.atlasPage)
|
|
let textureIndex = Int(renderCommand.atlasPage)
|
|
if textures.indices.contains(textureIndex) {
|
|
if textures.indices.contains(textureIndex) {
|
|
renderEncoder.setFragmentTexture(
|
|
renderEncoder.setFragmentTexture(
|
|
@@ -296,7 +298,7 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
index: Int(SpineTextureIndexBaseColor.rawValue)
|
|
index: Int(SpineTextureIndexBaseColor.rawValue)
|
|
)
|
|
)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
renderEncoder.drawPrimitives(
|
|
renderEncoder.drawPrimitives(
|
|
type: .triangle,
|
|
type: .triangle,
|
|
vertexStart: vertexStart,
|
|
vertexStart: vertexStart,
|
|
@@ -305,89 +307,89 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
|
vertexStart += vertices.count
|
|
vertexStart += vertices.count
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private func getPipelineState(blendMode: BlendMode) -> MTLRenderPipelineState? {
|
|
private func getPipelineState(blendMode: BlendMode) -> MTLRenderPipelineState? {
|
|
pipelineStatesByBlendMode[Int(blendMode.rawValue)]
|
|
pipelineStatesByBlendMode[Int(blendMode.rawValue)]
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private func increaseBuffersSize(to size: Int) {
|
|
private func increaseBuffersSize(to size: Int) {
|
|
- buffers = (0 ..< SpineRenderer.numberOfBuffers).map { _ in
|
|
|
|
|
|
+ buffers = (0..<SpineRenderer.numberOfBuffers).map { _ in
|
|
device.makeBuffer(length: size, options: .storageModeShared)!
|
|
device.makeBuffer(length: size, options: .storageModeShared)!
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-fileprivate extension BlendMode {
|
|
|
|
- func sourceRGBBlendFactor(premultipliedAlpha: Bool) -> MTLBlendFactor {
|
|
|
|
- switch self {
|
|
|
|
- case SPINE_BLEND_MODE_NORMAL:
|
|
|
|
- return premultipliedAlpha ? .one : .sourceAlpha
|
|
|
|
- case SPINE_BLEND_MODE_ADDITIVE:
|
|
|
|
- // additvie only needs sourceAlpha multiply if it is not pma
|
|
|
|
- return premultipliedAlpha ? .one : .sourceAlpha
|
|
|
|
- case SPINE_BLEND_MODE_MULTIPLY:
|
|
|
|
- return .destinationColor
|
|
|
|
- case SPINE_BLEND_MODE_SCREEN:
|
|
|
|
- return .one
|
|
|
|
- default:
|
|
|
|
- return .one // Should never be called
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var sourceAlphaBlendFactor: MTLBlendFactor {
|
|
|
|
- // pma and non-pma has no-relation ship with alpha blending
|
|
|
|
- switch self {
|
|
|
|
- case SPINE_BLEND_MODE_NORMAL:
|
|
|
|
- return .one
|
|
|
|
- case SPINE_BLEND_MODE_ADDITIVE:
|
|
|
|
- return .one
|
|
|
|
- case SPINE_BLEND_MODE_MULTIPLY:
|
|
|
|
- return .oneMinusSourceAlpha
|
|
|
|
- case SPINE_BLEND_MODE_SCREEN:
|
|
|
|
- return .oneMinusSourceColor
|
|
|
|
- default:
|
|
|
|
- return .one // Should never be called
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var destinationRGBBlendFactor: MTLBlendFactor {
|
|
|
|
- switch self {
|
|
|
|
- case SPINE_BLEND_MODE_NORMAL:
|
|
|
|
- return .oneMinusSourceAlpha
|
|
|
|
- case SPINE_BLEND_MODE_ADDITIVE:
|
|
|
|
- return .one
|
|
|
|
- case SPINE_BLEND_MODE_MULTIPLY:
|
|
|
|
- return .oneMinusSourceAlpha
|
|
|
|
- case SPINE_BLEND_MODE_SCREEN:
|
|
|
|
- return .oneMinusSourceColor
|
|
|
|
- default:
|
|
|
|
- return .one // Should never be called
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var destinationAlphaBlendFactor: MTLBlendFactor {
|
|
|
|
- switch self {
|
|
|
|
- case SPINE_BLEND_MODE_NORMAL:
|
|
|
|
- return .oneMinusSourceAlpha
|
|
|
|
- case SPINE_BLEND_MODE_ADDITIVE:
|
|
|
|
- return .one
|
|
|
|
- case SPINE_BLEND_MODE_MULTIPLY:
|
|
|
|
- return .oneMinusSourceAlpha
|
|
|
|
- case SPINE_BLEND_MODE_SCREEN:
|
|
|
|
- return .oneMinusSourceColor
|
|
|
|
- default:
|
|
|
|
- return .one // Should never be called
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+extension BlendMode {
|
|
|
|
+ fileprivate func sourceRGBBlendFactor(premultipliedAlpha: Bool) -> MTLBlendFactor {
|
|
|
|
+ switch self {
|
|
|
|
+ case SPINE_BLEND_MODE_NORMAL:
|
|
|
|
+ return premultipliedAlpha ? .one : .sourceAlpha
|
|
|
|
+ case SPINE_BLEND_MODE_ADDITIVE:
|
|
|
|
+ // additvie only needs sourceAlpha multiply if it is not pma
|
|
|
|
+ return premultipliedAlpha ? .one : .sourceAlpha
|
|
|
|
+ case SPINE_BLEND_MODE_MULTIPLY:
|
|
|
|
+ return .destinationColor
|
|
|
|
+ case SPINE_BLEND_MODE_SCREEN:
|
|
|
|
+ return .one
|
|
|
|
+ default:
|
|
|
|
+ return .one // Should never be called
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fileprivate var sourceAlphaBlendFactor: MTLBlendFactor {
|
|
|
|
+ // pma and non-pma has no-relation ship with alpha blending
|
|
|
|
+ switch self {
|
|
|
|
+ case SPINE_BLEND_MODE_NORMAL:
|
|
|
|
+ return .one
|
|
|
|
+ case SPINE_BLEND_MODE_ADDITIVE:
|
|
|
|
+ return .one
|
|
|
|
+ case SPINE_BLEND_MODE_MULTIPLY:
|
|
|
|
+ return .oneMinusSourceAlpha
|
|
|
|
+ case SPINE_BLEND_MODE_SCREEN:
|
|
|
|
+ return .oneMinusSourceColor
|
|
|
|
+ default:
|
|
|
|
+ return .one // Should never be called
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fileprivate var destinationRGBBlendFactor: MTLBlendFactor {
|
|
|
|
+ switch self {
|
|
|
|
+ case SPINE_BLEND_MODE_NORMAL:
|
|
|
|
+ return .oneMinusSourceAlpha
|
|
|
|
+ case SPINE_BLEND_MODE_ADDITIVE:
|
|
|
|
+ return .one
|
|
|
|
+ case SPINE_BLEND_MODE_MULTIPLY:
|
|
|
|
+ return .oneMinusSourceAlpha
|
|
|
|
+ case SPINE_BLEND_MODE_SCREEN:
|
|
|
|
+ return .oneMinusSourceColor
|
|
|
|
+ default:
|
|
|
|
+ return .one // Should never be called
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fileprivate var destinationAlphaBlendFactor: MTLBlendFactor {
|
|
|
|
+ switch self {
|
|
|
|
+ case SPINE_BLEND_MODE_NORMAL:
|
|
|
|
+ return .oneMinusSourceAlpha
|
|
|
|
+ case SPINE_BLEND_MODE_ADDITIVE:
|
|
|
|
+ return .one
|
|
|
|
+ case SPINE_BLEND_MODE_MULTIPLY:
|
|
|
|
+ return .oneMinusSourceAlpha
|
|
|
|
+ case SPINE_BLEND_MODE_SCREEN:
|
|
|
|
+ return .oneMinusSourceColor
|
|
|
|
+ default:
|
|
|
|
+ return .one // Should never be called
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-fileprivate extension MTLRenderPipelineColorAttachmentDescriptor {
|
|
|
|
-
|
|
|
|
- func apply(blendMode: BlendMode, with premultipliedAlpha: Bool) {
|
|
|
|
- isBlendingEnabled = true
|
|
|
|
- sourceRGBBlendFactor = blendMode.sourceRGBBlendFactor(premultipliedAlpha: premultipliedAlpha)
|
|
|
|
- sourceAlphaBlendFactor = blendMode.sourceAlphaBlendFactor
|
|
|
|
- destinationRGBBlendFactor = blendMode.destinationRGBBlendFactor
|
|
|
|
- destinationAlphaBlendFactor = blendMode.destinationAlphaBlendFactor
|
|
|
|
- }
|
|
|
|
|
|
+extension MTLRenderPipelineColorAttachmentDescriptor {
|
|
|
|
+
|
|
|
|
+ fileprivate func apply(blendMode: BlendMode, with premultipliedAlpha: Bool) {
|
|
|
|
+ isBlendingEnabled = true
|
|
|
|
+ sourceRGBBlendFactor = blendMode.sourceRGBBlendFactor(premultipliedAlpha: premultipliedAlpha)
|
|
|
|
+ sourceAlphaBlendFactor = blendMode.sourceAlphaBlendFactor
|
|
|
|
+ destinationRGBBlendFactor = blendMode.destinationRGBBlendFactor
|
|
|
|
+ destinationAlphaBlendFactor = blendMode.destinationAlphaBlendFactor
|
|
|
|
+ }
|
|
}
|
|
}
|