[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 {
 | 
			
		||||
    var dx: Float
 | 
			
		||||
    var dy: Float
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,63 @@ public enum MetaballsError: Error {
 | 
			
		|||
    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 {
 | 
			
		||||
    let radius: Float
 | 
			
		||||
    var position = Point()
 | 
			
		||||
| 
						 | 
				
			
			@ -36,17 +93,16 @@ extension Ball: CustomStringConvertible {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
public class Field {
 | 
			
		||||
    public var size: CGSize {
 | 
			
		||||
        didSet {
 | 
			
		||||
            if size != oldValue {
 | 
			
		||||
                NSLog("Updating size of field: old:\(oldValue), new:\(size)")
 | 
			
		||||
                let numberOfBallsBeforeFilter = balls.count
 | 
			
		||||
 | 
			
		||||
                // Remove balls that fall outside the new bounds.
 | 
			
		||||
//                balls = balls.filter { bounds.contains($0.bounds) }
 | 
			
		||||
    public var size: Size {
 | 
			
		||||
        get {
 | 
			
		||||
            return parameters.size
 | 
			
		||||
        }
 | 
			
		||||
        set {
 | 
			
		||||
            if parameters.size != newValue {
 | 
			
		||||
                NSLog("Updating size of field: old:\(parameters.size), new:\(newValue)")
 | 
			
		||||
 | 
			
		||||
                // 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 {
 | 
			
		||||
                    let r = $0.radius * scale
 | 
			
		||||
                    let p = randomPoint(forBallWithRadius: r)
 | 
			
		||||
| 
						 | 
				
			
			@ -56,21 +112,22 @@ public class Field {
 | 
			
		|||
 | 
			
		||||
                // Update Metal state as needed.
 | 
			
		||||
                populateParametersBuffer()
 | 
			
		||||
                if numberOfBallsBeforeFilter != balls.count {
 | 
			
		||||
                    ballBuffer = nil
 | 
			
		||||
                    populateBallBuffer()
 | 
			
		||||
                }
 | 
			
		||||
                populateBallBuffer()
 | 
			
		||||
 | 
			
		||||
                parameters.size = newValue
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private(set) var balls = [Ball]()
 | 
			
		||||
 | 
			
		||||
    private var parameters = Parameters()
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,23 +194,14 @@ public class Field {
 | 
			
		|||
    private func populateParametersBuffer() {
 | 
			
		||||
        if parametersBuffer == nil {
 | 
			
		||||
            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: [])
 | 
			
		||||
            NSLog("Making parameters buffer, length:\(length)")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let parameters = parametersBuffer {
 | 
			
		||||
            var ptr = parameters.contents()
 | 
			
		||||
            var width = UInt32(size.width)
 | 
			
		||||
            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)")
 | 
			
		||||
        if let parametersBuffer = parametersBuffer {
 | 
			
		||||
            parameters.numberOfBalls = Parameters.Short(balls.count)
 | 
			
		||||
            self.parameters.write(to: parametersBuffer)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue