git.iko.soy/metal-metaballs
on master help sign in
Merged feature-graph into dev
db76528vinivendra
PixelImage.xcodeproj/project.pbxproj+4
@@ -12,6 +12,7 @@
12 2426868D1C63B72A00D62456 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2426868B1C63B72A00D62456 /* Main.storyboard */; };
12 2426868D1C63B72A00D62456 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2426868B1C63B72A00D62456 /* Main.storyboard */; };
13 2426868F1C63B72A00D62456 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2426868E1C63B72A00D62456 /* Assets.xcassets */; };
13 2426868F1C63B72A00D62456 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2426868E1C63B72A00D62456 /* Assets.xcassets */; };
14 242686921C63B72A00D62456 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 242686901C63B72A00D62456 /* LaunchScreen.storyboard */; };
14 242686921C63B72A00D62456 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 242686901C63B72A00D62456 /* LaunchScreen.storyboard */; };
15+ 249CF84C1C6D113C008B0C78 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249CF84B1C6D113C008B0C78 /* Matrix.swift */; };
15 24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */; };
16 24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */; };
16 24E1E7E21C67D37F00ECF1C4 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */; };
17 24E1E7E21C67D37F00ECF1C4 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */; };
17 24E1E7E41C67D39300ECF1C4 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E31C67D39300ECF1C4 /* MetalKit.framework */; };
18 24E1E7E41C67D39300ECF1C4 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E31C67D39300ECF1C4 /* MetalKit.framework */; };
@@ -57,6 +58,7 @@
57 2426868E1C63B72A00D62456 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
58 2426868E1C63B72A00D62456 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
58 242686911C63B72A00D62456 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
59 242686911C63B72A00D62456 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
59 242686931C63B72A00D62456 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
60 242686931C63B72A00D62456 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
61+ 249CF84B1C6D113C008B0C78 /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = "<group>"; };
60 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKit.swift; sourceTree = "<group>"; };
62 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKit.swift; sourceTree = "<group>"; };
61 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
63 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
62 24E1E7E31C67D39300ECF1C4 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
64 24E1E7E31C67D39300ECF1C4 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
@@ -135,6 +137,7 @@
135 24ED21CC1C6C2397009C40FC /* MetaballDataSource.swift */,
137 24ED21CC1C6C2397009C40FC /* MetaballDataSource.swift */,
136 24E1E7EA1C67D70300ECF1C4 /* MetalMetaballRenderer.swift */,
138 24E1E7EA1C67D70300ECF1C4 /* MetalMetaballRenderer.swift */,
137 24E1E7EE1C67D74300ECF1C4 /* Metal.swift */,
139 24E1E7EE1C67D74300ECF1C4 /* Metal.swift */,
140+ 249CF84B1C6D113C008B0C78 /* Matrix.swift */,
138 24E1E7EF1C67D74300ECF1C4 /* shaders.metal */,
141 24E1E7EF1C67D74300ECF1C4 /* shaders.metal */,
139 2426868B1C63B72A00D62456 /* Main.storyboard */,
142 2426868B1C63B72A00D62456 /* Main.storyboard */,
140 2426868E1C63B72A00D62456 /* Assets.xcassets */,
143 2426868E1C63B72A00D62456 /* Assets.xcassets */,
@@ -295,6 +298,7 @@
295 24FFA8711C6BA038005ACD60 /* PowerOperator.swift in Sources */,
298 24FFA8711C6BA038005ACD60 /* PowerOperator.swift in Sources */,
296 24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */,
299 24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */,
297 24FFA8851C6BA038005ACD60 /* Zip.swift in Sources */,
300 24FFA8851C6BA038005ACD60 /* Zip.swift in Sources */,
301+ 249CF84C1C6D113C008B0C78 /* Matrix.swift in Sources */,
298 24FFA8791C6BA038005ACD60 /* Cycle.swift in Sources */,
302 24FFA8791C6BA038005ACD60 /* Cycle.swift in Sources */,
299 24E1E7F11C67D74300ECF1C4 /* shaders.metal in Sources */,
303 24E1E7F11C67D74300ECF1C4 /* shaders.metal in Sources */,
300 24FFA86E1C6BA038005ACD60 /* Dictionary.swift in Sources */,
304 24FFA86E1C6BA038005ACD60 /* Dictionary.swift in Sources */,
PixelImage/Matrix.swiftnew file+72
@@ -0,0 +1,72 @@
1+
2+import UIKit
3+
4+class Graph<T> {
5+ var adjacencyMatrix: Matrix
6+ var vertices: [T]
7+
8+ var size: Int {
9+ get {
10+ return vertices.count
11+ }
12+ }
13+
14+ init(vertices: [T]) {
15+ let size = vertices.count
16+
17+ self.vertices = vertices
18+ self.adjacencyMatrix = Matrix(size: size)
19+ }
20+
21+ func addEdge(i: Int, _ j: Int) {
22+ adjacencyMatrix.set(i, j)
23+ adjacencyMatrix.set(j, i)
24+ }
25+
26+ func removeEdge(i: Int, _ j: Int) {
27+ adjacencyMatrix.reset(i, j)
28+ adjacencyMatrix.reset(j, i)
29+ }
30+}
31+
32+struct Matrix: CustomStringConvertible {
33+ var buffer: [Float]
34+
35+ let size: Int
36+
37+ init(size: Int) {
38+ buffer = [Float](count: size * size, repeatedValue: 0.0)
39+ self.size = size
40+ }
41+
42+ func get(i: Int, j: Int) -> Float {
43+ return buffer[index(i, j)]
44+ }
45+
46+ mutating func set(i: Int, _ j: Int, value: Float = 1.0) {
47+ buffer[index(i, j)] = value
48+ }
49+
50+ mutating func reset(i: Int, _ j: Int) {
51+ buffer[index(i, j)] = 0.0
52+ }
53+
54+ private func index(i: Int, _ j: Int) -> Int {
55+ return j * size + i
56+ }
57+
58+ var description: String {
59+ get {
60+ var result = ""
61+ for (index, value) in buffer.enumerate() {
62+ result = result + "\(value)"
63+ if index % size == size - 1 {
64+ result = result + "\n"
65+ } else {
66+ result = result + " "
67+ }
68+ }
69+ return result
70+ }
71+ }
72+}
PixelImage/MetaballDataSource.swift+9−1
@@ -2,5 +2,13 @@
2import Metal
2import Metal
3
3
4protocol MetaballDataSource {
4protocol MetaballDataSource {
5− var metaballs: [Metaball] { get }
5+ var metaballGraph: Graph<Metaball>! { get }
6}
6}
7+
8+extension MetaballDataSource {
9+ var metaballs: [Metaball]! {
10+ get {
11+ return metaballGraph.vertices
12+ }
13+ }
14+}
PixelImage/MetalMetaballRenderer.swift+10−4
@@ -1,8 +1,6 @@
1import Metal
1import Metal
2import UIKit
2import UIKit
3
3
4−var i = 0
5−
6class MetalMetaballRenderer {
4class MetalMetaballRenderer {
7
5
8 typealias TargetView = UIImageView
6 typealias TargetView = UIImageView
@@ -54,8 +52,6 @@ class MetalMetaballRenderer {
54 let timeout = dispatch_time(DISPATCH_TIME_NOW, 1000000000)
52 let timeout = dispatch_time(DISPATCH_TIME_NOW, 1000000000)
55 dispatch_semaphore_wait(semaphore, timeout)
53 dispatch_semaphore_wait(semaphore, timeout)
56
54
57− print("Update \(i++)!")
58−
59 state = .Ending
55 state = .Ending
60
56
61 dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.rawValue), 0)) { () -> Void in
57 dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.rawValue), 0)) { () -> Void in
@@ -108,6 +104,8 @@ class MetalMetaballRenderer {
108 commandEncoder.setTexture(computeContext.texture, atIndex: 0)
104 commandEncoder.setTexture(computeContext.texture, atIndex: 0)
109 let metaballInfoBuffer = metaballBuffer()
105 let metaballInfoBuffer = metaballBuffer()
110 commandEncoder.setBuffer(metaballInfoBuffer, offset: 0, atIndex: 0)
106 commandEncoder.setBuffer(metaballInfoBuffer, offset: 0, atIndex: 0)
107+ let edgesBuffer = metaballEdgesBuffer()
108+ commandEncoder.setBuffer(edgesBuffer, offset: 0, atIndex: 1)
111 commandEncoder.dispatchThreadgroups(threadGroups,
109 commandEncoder.dispatchThreadgroups(threadGroups,
112 threadsPerThreadgroup: threadGroupCounts)
110 threadsPerThreadgroup: threadGroupCounts)
113 commandEncoder.endEncoding()
111 commandEncoder.endEncoding()
@@ -163,6 +161,14 @@ class MetalMetaballRenderer {
163 return image
161 return image
164 }
162 }
165
163
164+ func metaballEdgesBuffer() -> MTLBuffer {
165+ let floats = dataSource.metaballGraph.adjacencyMatrix.buffer
166+
167+ let buffer = context.device.newBufferWithBytes(floats, length: floats.count * sizeof(Float), options: .StorageModeShared)
168+
169+ return buffer
170+ }
171+
166 func metaballBuffer() -> MTLBuffer {
172 func metaballBuffer() -> MTLBuffer {
167 // Create Float array for buffer
173 // Create Float array for buffer
168 // Exclude metaballs far from the view's bounds
174 // Exclude metaballs far from the view's bounds
PixelImage/ViewController.swift+12−3
@@ -9,9 +9,7 @@ class ViewController: UIViewController, MetaballDataSource {
9 let width = 350
9 let width = 350
10 let height = 600
10 let height = 600
11
11
12− var metaballs = [CGPoint(x: 70, y: 70), CGPoint(x: 270, y: 470), CGPoint(x: 270, y: 70), CGPoint(x: 70, y: 470)].map {
12+ var metaballGraph: Graph<Metaball>!
13− Metaball(position: $0)
14− }
15 var previousLocation: CGPoint!
13 var previousLocation: CGPoint!
16
14
17 var selectedMetaball: Metaball?
15 var selectedMetaball: Metaball?
@@ -22,6 +20,17 @@ class ViewController: UIViewController, MetaballDataSource {
22 let recognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
20 let recognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
23 view.addGestureRecognizer(recognizer)
21 view.addGestureRecognizer(recognizer)
24
22
23+ let metaballs = [CGPoint(x: 70, y: 70), CGPoint(x: 270, y: 70), CGPoint(x: 270, y: 470), CGPoint(x: 70, y: 470)].map {
24+ Metaball(position: $0)
25+ }
26+ metaballGraph = Graph(vertices: metaballs)
27+ metaballGraph.addEdge(0, 1)
28+ metaballGraph.addEdge(0, 3)
29+ metaballGraph.addEdge(1, 2)
30+ metaballGraph.addEdge(2, 3)
31+ metaballGraph.removeEdge(2, 3)
32+ print(metaballGraph.adjacencyMatrix)
33+
25 let border = 20
34 let border = 20
26 let metaballViewFrame = CGRect(x: border/2, y: border/2, width: width, height: height)
35 let metaballViewFrame = CGRect(x: border/2, y: border/2, width: width, height: height)
27 renderer = MetalMetaballRenderer(dataSource: self, frame: metaballViewFrame)
36 renderer = MetalMetaballRenderer(dataSource: self, frame: metaballViewFrame)
PixelImage/shaders.metal+21−15
@@ -5,40 +5,46 @@ using namespace metal;
5
5
6kernel void
6kernel void
7 drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]],
7 drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]],
8+ constant float *edgesBuffer[[buffer(1)]],
8− constant float *metaballBuffer [[buffer(0)]],
9+ constant float *metaballBuffer[[buffer(0)]],
9 uint2 gid[[thread_position_in_grid]]) {
10 uint2 gid[[thread_position_in_grid]]) {
10
11
11− char numberOfMetaballs = metaballBuffer[0];
12+ uint numberOfMetaballs = metaballBuffer[0];
12− char metaballArraySize = numberOfMetaballs * 2 + 1;
13+ uint metaballArraySize = numberOfMetaballs * 2 + 1;
13
14
14 float sum = 0;
15 float sum = 0;
15
16
16− for (char i = 1; i < metaballArraySize; i += 2) {
17+ uint i, j, x, y;
17− for (char j = i + 2; j < metaballArraySize; j += 2) {
18− float2 metaball1 = float2(metaballBuffer[i],
19− metaballBuffer[i + 1]);
20
18
19+ for (i = 1, x = 0; i < metaballArraySize; i += 2, x += 1) {
20+ for (j = i + 2, y = x + 1; j < metaballArraySize; j += 2, y += 1) {
21+ float2 metaball1 = float2(metaballBuffer[i], metaballBuffer[i + 1]);
22+
21− float2 metaball2 = float2(metaballBuffer[j],
23+ float2 metaball2 = float2(metaballBuffer[j], metaballBuffer[j + 1]);
22− metaballBuffer[j + 1]);
23
24
24 float2 metaball1Vector
25 float2 metaball1Vector
25 = float2(metaball1.x - gid.x, metaball1.y - gid.y);
26 = float2(metaball1.x - gid.x, metaball1.y - gid.y);
26 float2 metaball2Vector
27 float2 metaball2Vector
27 = float2(metaball2.x - gid.x, metaball2.y - gid.y);
28 = float2(metaball2.x - gid.x, metaball2.y - gid.y);
28
29
30+ float value1 = 2048 / (dot(metaball1Vector, metaball1Vector) + 1);
31+ float value2 = 2048 / (dot(metaball2Vector, metaball2Vector) + 1);
32+
33+ float v = value1 + value2;
34+
35+ float weightedValue = 0.5 * v;
36+
37+ char edgeIndex = y * numberOfMetaballs + x;
38+ float edgeWeight = edgesBuffer[edgeIndex];
29 float2 direction1 = normalize(metaball1Vector);
39 float2 direction1 = normalize(metaball1Vector);
30 float2 direction2 = normalize(metaball2Vector);
40 float2 direction2 = normalize(metaball2Vector);
31
41
32 float cosine = dot(direction1, direction2);
42 float cosine = dot(direction1, direction2);
33
43
34− float value1 = 2048 / (dot(metaball1Vector, metaball1Vector) + 1);
35− float value2 = 2048 / (dot(metaball2Vector, metaball2Vector) + 1);
36−
37− float v = value1 + value2;
38 float link = pow(((1 - cosine) * 0.5), 100);
44 float link = pow(((1 - cosine) * 0.5), 100);
39
45
40− float weightedLink = 0.6 * link;
46+ float weightedLink = 0.6 * link * edgeWeight;
41− float weightedValue = 0.5 * v;
47+
42
48
43 sum += mix(0.0, 1.0, step(0.5, weightedValue + weightedLink));
49 sum += mix(0.0, 1.0, step(0.5, weightedValue + weightedLink));
44 }
50 }