mirror of
https://github.com/ilyakooo0/metal-metaballs.git
synced 2024-10-26 10:22:52 +03:00
Merged feature-graph into dev
This commit is contained in:
commit
db76528513
@ -12,6 +12,7 @@
|
||||
2426868D1C63B72A00D62456 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2426868B1C63B72A00D62456 /* Main.storyboard */; };
|
||||
2426868F1C63B72A00D62456 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2426868E1C63B72A00D62456 /* Assets.xcassets */; };
|
||||
242686921C63B72A00D62456 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 242686901C63B72A00D62456 /* LaunchScreen.storyboard */; };
|
||||
249CF84C1C6D113C008B0C78 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249CF84B1C6D113C008B0C78 /* Matrix.swift */; };
|
||||
24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */; };
|
||||
24E1E7E21C67D37F00ECF1C4 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */; };
|
||||
24E1E7E41C67D39300ECF1C4 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E31C67D39300ECF1C4 /* MetalKit.framework */; };
|
||||
@ -57,6 +58,7 @@
|
||||
2426868E1C63B72A00D62456 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
242686911C63B72A00D62456 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
242686931C63B72A00D62456 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
249CF84B1C6D113C008B0C78 /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = "<group>"; };
|
||||
24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKit.swift; sourceTree = "<group>"; };
|
||||
24E1E7E11C67D37F00ECF1C4 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
|
||||
24E1E7E31C67D39300ECF1C4 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
|
||||
@ -135,6 +137,7 @@
|
||||
24ED21CC1C6C2397009C40FC /* MetaballDataSource.swift */,
|
||||
24E1E7EA1C67D70300ECF1C4 /* MetalMetaballRenderer.swift */,
|
||||
24E1E7EE1C67D74300ECF1C4 /* Metal.swift */,
|
||||
249CF84B1C6D113C008B0C78 /* Matrix.swift */,
|
||||
24E1E7EF1C67D74300ECF1C4 /* shaders.metal */,
|
||||
2426868B1C63B72A00D62456 /* Main.storyboard */,
|
||||
2426868E1C63B72A00D62456 /* Assets.xcassets */,
|
||||
@ -295,6 +298,7 @@
|
||||
24FFA8711C6BA038005ACD60 /* PowerOperator.swift in Sources */,
|
||||
24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */,
|
||||
24FFA8851C6BA038005ACD60 /* Zip.swift in Sources */,
|
||||
249CF84C1C6D113C008B0C78 /* Matrix.swift in Sources */,
|
||||
24FFA8791C6BA038005ACD60 /* Cycle.swift in Sources */,
|
||||
24E1E7F11C67D74300ECF1C4 /* shaders.metal in Sources */,
|
||||
24FFA86E1C6BA038005ACD60 /* Dictionary.swift in Sources */,
|
||||
|
72
PixelImage/Matrix.swift
Normal file
72
PixelImage/Matrix.swift
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class Graph<T> {
|
||||
var adjacencyMatrix: Matrix
|
||||
var vertices: [T]
|
||||
|
||||
var size: Int {
|
||||
get {
|
||||
return vertices.count
|
||||
}
|
||||
}
|
||||
|
||||
init(vertices: [T]) {
|
||||
let size = vertices.count
|
||||
|
||||
self.vertices = vertices
|
||||
self.adjacencyMatrix = Matrix(size: size)
|
||||
}
|
||||
|
||||
func addEdge(i: Int, _ j: Int) {
|
||||
adjacencyMatrix.set(i, j)
|
||||
adjacencyMatrix.set(j, i)
|
||||
}
|
||||
|
||||
func removeEdge(i: Int, _ j: Int) {
|
||||
adjacencyMatrix.reset(i, j)
|
||||
adjacencyMatrix.reset(j, i)
|
||||
}
|
||||
}
|
||||
|
||||
struct Matrix: CustomStringConvertible {
|
||||
var buffer: [Float]
|
||||
|
||||
let size: Int
|
||||
|
||||
init(size: Int) {
|
||||
buffer = [Float](count: size * size, repeatedValue: 0.0)
|
||||
self.size = size
|
||||
}
|
||||
|
||||
func get(i: Int, j: Int) -> Float {
|
||||
return buffer[index(i, j)]
|
||||
}
|
||||
|
||||
mutating func set(i: Int, _ j: Int, value: Float = 1.0) {
|
||||
buffer[index(i, j)] = value
|
||||
}
|
||||
|
||||
mutating func reset(i: Int, _ j: Int) {
|
||||
buffer[index(i, j)] = 0.0
|
||||
}
|
||||
|
||||
private func index(i: Int, _ j: Int) -> Int {
|
||||
return j * size + i
|
||||
}
|
||||
|
||||
var description: String {
|
||||
get {
|
||||
var result = ""
|
||||
for (index, value) in buffer.enumerate() {
|
||||
result = result + "\(value)"
|
||||
if index % size == size - 1 {
|
||||
result = result + "\n"
|
||||
} else {
|
||||
result = result + " "
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
@ -2,5 +2,13 @@
|
||||
import Metal
|
||||
|
||||
protocol MetaballDataSource {
|
||||
var metaballs: [Metaball] { get }
|
||||
var metaballGraph: Graph<Metaball>! { get }
|
||||
}
|
||||
|
||||
extension MetaballDataSource {
|
||||
var metaballs: [Metaball]! {
|
||||
get {
|
||||
return metaballGraph.vertices
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
import Metal
|
||||
import UIKit
|
||||
|
||||
var i = 0
|
||||
|
||||
class MetalMetaballRenderer {
|
||||
|
||||
typealias TargetView = UIImageView
|
||||
@ -54,8 +52,6 @@ class MetalMetaballRenderer {
|
||||
let timeout = dispatch_time(DISPATCH_TIME_NOW, 1000000000)
|
||||
dispatch_semaphore_wait(semaphore, timeout)
|
||||
|
||||
print("Update \(i++)!")
|
||||
|
||||
state = .Ending
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.rawValue), 0)) { () -> Void in
|
||||
@ -108,6 +104,8 @@ class MetalMetaballRenderer {
|
||||
commandEncoder.setTexture(computeContext.texture, atIndex: 0)
|
||||
let metaballInfoBuffer = metaballBuffer()
|
||||
commandEncoder.setBuffer(metaballInfoBuffer, offset: 0, atIndex: 0)
|
||||
let edgesBuffer = metaballEdgesBuffer()
|
||||
commandEncoder.setBuffer(edgesBuffer, offset: 0, atIndex: 1)
|
||||
commandEncoder.dispatchThreadgroups(threadGroups,
|
||||
threadsPerThreadgroup: threadGroupCounts)
|
||||
commandEncoder.endEncoding()
|
||||
@ -163,6 +161,14 @@ class MetalMetaballRenderer {
|
||||
return image
|
||||
}
|
||||
|
||||
func metaballEdgesBuffer() -> MTLBuffer {
|
||||
let floats = dataSource.metaballGraph.adjacencyMatrix.buffer
|
||||
|
||||
let buffer = context.device.newBufferWithBytes(floats, length: floats.count * sizeof(Float), options: .StorageModeShared)
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
func metaballBuffer() -> MTLBuffer {
|
||||
// Create Float array for buffer
|
||||
// Exclude metaballs far from the view's bounds
|
||||
|
@ -9,9 +9,7 @@ class ViewController: UIViewController, MetaballDataSource {
|
||||
let width = 350
|
||||
let height = 600
|
||||
|
||||
var metaballs = [CGPoint(x: 70, y: 70), CGPoint(x: 270, y: 470), CGPoint(x: 270, y: 70), CGPoint(x: 70, y: 470)].map {
|
||||
Metaball(position: $0)
|
||||
}
|
||||
var metaballGraph: Graph<Metaball>!
|
||||
var previousLocation: CGPoint!
|
||||
|
||||
var selectedMetaball: Metaball?
|
||||
@ -22,6 +20,17 @@ class ViewController: UIViewController, MetaballDataSource {
|
||||
let recognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
|
||||
view.addGestureRecognizer(recognizer)
|
||||
|
||||
let metaballs = [CGPoint(x: 70, y: 70), CGPoint(x: 270, y: 70), CGPoint(x: 270, y: 470), CGPoint(x: 70, y: 470)].map {
|
||||
Metaball(position: $0)
|
||||
}
|
||||
metaballGraph = Graph(vertices: metaballs)
|
||||
metaballGraph.addEdge(0, 1)
|
||||
metaballGraph.addEdge(0, 3)
|
||||
metaballGraph.addEdge(1, 2)
|
||||
metaballGraph.addEdge(2, 3)
|
||||
metaballGraph.removeEdge(2, 3)
|
||||
print(metaballGraph.adjacencyMatrix)
|
||||
|
||||
let border = 20
|
||||
let metaballViewFrame = CGRect(x: border/2, y: border/2, width: width, height: height)
|
||||
renderer = MetalMetaballRenderer(dataSource: self, frame: metaballViewFrame)
|
||||
|
@ -5,40 +5,46 @@ using namespace metal;
|
||||
|
||||
kernel void
|
||||
drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]],
|
||||
constant float *metaballBuffer [[buffer(0)]],
|
||||
constant float *edgesBuffer[[buffer(1)]],
|
||||
constant float *metaballBuffer[[buffer(0)]],
|
||||
uint2 gid[[thread_position_in_grid]]) {
|
||||
|
||||
char numberOfMetaballs = metaballBuffer[0];
|
||||
char metaballArraySize = numberOfMetaballs * 2 + 1;
|
||||
uint numberOfMetaballs = metaballBuffer[0];
|
||||
uint metaballArraySize = numberOfMetaballs * 2 + 1;
|
||||
|
||||
float sum = 0;
|
||||
|
||||
for (char i = 1; i < metaballArraySize; i += 2) {
|
||||
for (char j = i + 2; j < metaballArraySize; j += 2) {
|
||||
float2 metaball1 = float2(metaballBuffer[i],
|
||||
metaballBuffer[i + 1]);
|
||||
uint i, j, x, y;
|
||||
|
||||
float2 metaball2 = float2(metaballBuffer[j],
|
||||
metaballBuffer[j + 1]);
|
||||
for (i = 1, x = 0; i < metaballArraySize; i += 2, x += 1) {
|
||||
for (j = i + 2, y = x + 1; j < metaballArraySize; j += 2, y += 1) {
|
||||
float2 metaball1 = float2(metaballBuffer[i], metaballBuffer[i + 1]);
|
||||
|
||||
float2 metaball2 = float2(metaballBuffer[j], metaballBuffer[j + 1]);
|
||||
|
||||
float2 metaball1Vector
|
||||
= float2(metaball1.x - gid.x, metaball1.y - gid.y);
|
||||
float2 metaball2Vector
|
||||
= float2(metaball2.x - gid.x, metaball2.y - gid.y);
|
||||
|
||||
float value1 = 2048 / (dot(metaball1Vector, metaball1Vector) + 1);
|
||||
float value2 = 2048 / (dot(metaball2Vector, metaball2Vector) + 1);
|
||||
|
||||
float v = value1 + value2;
|
||||
|
||||
float weightedValue = 0.5 * v;
|
||||
|
||||
char edgeIndex = y * numberOfMetaballs + x;
|
||||
float edgeWeight = edgesBuffer[edgeIndex];
|
||||
float2 direction1 = normalize(metaball1Vector);
|
||||
float2 direction2 = normalize(metaball2Vector);
|
||||
|
||||
float cosine = dot(direction1, direction2);
|
||||
|
||||
float value1 = 2048 / (dot(metaball1Vector, metaball1Vector) + 1);
|
||||
float value2 = 2048 / (dot(metaball2Vector, metaball2Vector) + 1);
|
||||
|
||||
float v = value1 + value2;
|
||||
float link = pow(((1 - cosine) * 0.5), 100);
|
||||
|
||||
float weightedLink = 0.6 * link;
|
||||
float weightedValue = 0.5 * v;
|
||||
float weightedLink = 0.6 * link * edgeWeight;
|
||||
|
||||
|
||||
sum += mix(0.0, 1.0, step(0.5, weightedValue + weightedLink));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user