[MetaballsKit] Add parameters field to get ready for colors
This commit is contained in:
parent
cceb00dfe0
commit
48365abee3
2 changed files with 120 additions and 28 deletions
|
@ -32,6 +32,50 @@ extension Point: CustomStringConvertible {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct Size {
|
||||||
|
var width: UInt16
|
||||||
|
var height: UInt16
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
self.init(width: 0, height: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(width: UInt16, height: UInt16) {
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(size: CGSize) {
|
||||||
|
self.init(width: UInt16(size.width), height: UInt16(size.height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Size: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
return "(\(width), \(height))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Size: Equatable {
|
||||||
|
/// Returns a Boolean value indicating whether two values are equal.
|
||||||
|
///
|
||||||
|
/// Equality is the inverse of inequality. For any values `a` and `b`,
|
||||||
|
/// `a == b` implies that `a != b` is `false`.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - lhs: A value to compare.
|
||||||
|
/// - rhs: Another value to compare.
|
||||||
|
public static func ==(lhs: Size, rhs: Size) -> Bool {
|
||||||
|
return lhs.width == rhs.width && lhs.height == rhs.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CGSize {
|
||||||
|
init(size: Size) {
|
||||||
|
self.init(width: CGFloat(size.width), height: CGFloat(size.height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct Vector {
|
public struct Vector {
|
||||||
var dx: Float
|
var dx: Float
|
||||||
var dy: Float
|
var dy: Float
|
||||||
|
|
|
@ -13,6 +13,63 @@ public enum MetaballsError: Error {
|
||||||
case metalError(String)
|
case metalError(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct Parameters {
|
||||||
|
/// Metal's short type. 2 bytes, 2 byte aligned
|
||||||
|
typealias Short = UInt16
|
||||||
|
/// Metal's short2 type. 2 bytes per int, 4 bytes, 4 byte aligned
|
||||||
|
typealias Short2 = (UInt16, UInt16)
|
||||||
|
/// Metal's float4 type. 4 bytes per float, 16 bytes total, 16 byte aligned.
|
||||||
|
typealias Color4 = (r: Float, g: Float, b: Float, a: Float)
|
||||||
|
|
||||||
|
// Simulation parameters
|
||||||
|
var size: Size
|
||||||
|
var numberOfBalls: Short
|
||||||
|
// 4 bytes unused
|
||||||
|
|
||||||
|
// Color parameters
|
||||||
|
var topLeft: Color4
|
||||||
|
var topRight: Color4
|
||||||
|
var bottomLeft: Color4
|
||||||
|
var bottomRight: Color4
|
||||||
|
|
||||||
|
public static var size: Int {
|
||||||
|
var size = 0
|
||||||
|
size += MemoryLayout<Size>.stride
|
||||||
|
size += MemoryLayout<Short>.stride
|
||||||
|
size += 2+8
|
||||||
|
size += 4 * MemoryLayout<Color4>.stride
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
size = Size(width: 0, height: 0)
|
||||||
|
numberOfBalls = 0
|
||||||
|
topLeft = (0, 0, 0, 0)
|
||||||
|
topRight = (0, 0, 0, 0)
|
||||||
|
bottomLeft = (0, 0, 0, 0)
|
||||||
|
bottomRight = (0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func write(to buffer: MTLBuffer, offset: Int = 0) {
|
||||||
|
let start = buffer.contents().advanced(by: offset)
|
||||||
|
var ptr = start
|
||||||
|
|
||||||
|
let simBegin = ptr
|
||||||
|
ptr = ptr.writeAndAdvance(value: &size)
|
||||||
|
ptr = ptr.writeAndAdvance(value: &numberOfBalls)
|
||||||
|
ptr = ptr.advanced(by: 2+8) // Skip 10 bytes to maintain alignment.
|
||||||
|
let simLength = simBegin.distance(to: ptr)
|
||||||
|
buffer.addDebugMarker("Simulation Parameters", range: NSRange(location: start.distance(to: simBegin), length: simLength))
|
||||||
|
|
||||||
|
ptr = ptr.writeAndAdvance(value: &topLeft)
|
||||||
|
ptr = ptr.writeAndAdvance(value: &topRight)
|
||||||
|
ptr = ptr.writeAndAdvance(value: &bottomLeft)
|
||||||
|
ptr = ptr.writeAndAdvance(value: &bottomRight)
|
||||||
|
|
||||||
|
NSLog("Populated parameters: size:\(size), n:\(numberOfBalls)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct Ball {
|
public struct Ball {
|
||||||
let radius: Float
|
let radius: Float
|
||||||
var position = Point()
|
var position = Point()
|
||||||
|
@ -36,17 +93,16 @@ extension Ball: CustomStringConvertible {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Field {
|
public class Field {
|
||||||
public var size: CGSize {
|
public var size: Size {
|
||||||
didSet {
|
get {
|
||||||
if size != oldValue {
|
return parameters.size
|
||||||
NSLog("Updating size of field: old:\(oldValue), new:\(size)")
|
}
|
||||||
let numberOfBallsBeforeFilter = balls.count
|
set {
|
||||||
|
if parameters.size != newValue {
|
||||||
// Remove balls that fall outside the new bounds.
|
NSLog("Updating size of field: old:\(parameters.size), new:\(newValue)")
|
||||||
// balls = balls.filter { bounds.contains($0.bounds) }
|
|
||||||
|
|
||||||
// Scale balls to new position and size.
|
// Scale balls to new position and size.
|
||||||
let scale = Float(size.width / oldValue.width)
|
let scale = parameters.size.width != 0 ? Float(newValue.width / parameters.size.width) : 1
|
||||||
balls = balls.map {
|
balls = balls.map {
|
||||||
let r = $0.radius * scale
|
let r = $0.radius * scale
|
||||||
let p = randomPoint(forBallWithRadius: r)
|
let p = randomPoint(forBallWithRadius: r)
|
||||||
|
@ -56,21 +112,22 @@ public class Field {
|
||||||
|
|
||||||
// Update Metal state as needed.
|
// Update Metal state as needed.
|
||||||
populateParametersBuffer()
|
populateParametersBuffer()
|
||||||
if numberOfBallsBeforeFilter != balls.count {
|
populateBallBuffer()
|
||||||
ballBuffer = nil
|
|
||||||
populateBallBuffer()
|
parameters.size = newValue
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private(set) var balls = [Ball]()
|
private(set) var balls = [Ball]()
|
||||||
|
|
||||||
|
private var parameters = Parameters()
|
||||||
|
|
||||||
internal var bounds: CGRect {
|
internal var bounds: CGRect {
|
||||||
return CGRect(origin: CGPoint(), size: size)
|
return CGRect(origin: CGPoint(), size: CGSize(size: size))
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(size s: CGSize) {
|
public init(size s: Size) {
|
||||||
size = s
|
size = s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,23 +194,14 @@ public class Field {
|
||||||
private func populateParametersBuffer() {
|
private func populateParametersBuffer() {
|
||||||
if parametersBuffer == nil {
|
if parametersBuffer == nil {
|
||||||
guard let device = self.device else { return }
|
guard let device = self.device else { return }
|
||||||
let length = 16 // A Parameters struct in shader-land is an int3, which takes 16 bytes.
|
let length = Parameters.size
|
||||||
parametersBuffer = device.makeBuffer(length: length, options: [])
|
parametersBuffer = device.makeBuffer(length: length, options: [])
|
||||||
NSLog("Making parameters buffer, length:\(length)")
|
NSLog("Making parameters buffer, length:\(length)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let parameters = parametersBuffer {
|
if let parametersBuffer = parametersBuffer {
|
||||||
var ptr = parameters.contents()
|
parameters.numberOfBalls = Parameters.Short(balls.count)
|
||||||
var width = UInt32(size.width)
|
self.parameters.write(to: parametersBuffer)
|
||||||
parameters.addDebugMarker("Width", range: NSRange(location: parameters.contents().distance(to: ptr), length: 4))
|
|
||||||
ptr = write(value: &width, to: ptr)
|
|
||||||
var height = UInt32(size.height)
|
|
||||||
parameters.addDebugMarker("Height", range: NSRange(location: parameters.contents().distance(to: ptr), length: 4))
|
|
||||||
ptr = write(value: &height, to: ptr)
|
|
||||||
var numberOfBalls = UInt32(self.balls.count)
|
|
||||||
parameters.addDebugMarker("Number Of Balls", range: NSRange(location: parameters.contents().distance(to: ptr), length: 4))
|
|
||||||
ptr = write(value: &numberOfBalls, to: ptr)
|
|
||||||
NSLog("Populated parameters: w:\(width), h:\(height), n:\(numberOfBalls)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue