git.iko.soy/metal-metaballs
on master help sign in
Merged feature-graph into dev
commitdb7652851303838390b02628cc1c2fc34bc555d1 authorvinivendra <vinivendra@gmail.com> dateThu 11 Feb 2016, 23:32 (11 years ago) parent51490667fdd9e1
6 files changed+128−23
PixelImage.xcodeproj/project.pbxproj+4 PixelImage/Matrix.swift+72 PixelImage/MetaballDataSource.swift+9−1 PixelImage/MetalMetaballRenderer.swift+10−4 PixelImage/ViewController.swift+12−3 PixelImage/shaders.metal+21−15
PixelImage.xcodeproj/project.pbxproj+4
@@ -12,6 +12,7 @@
1212 2426868D1C63B72A00D62456 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2426868B1C63B72A00D62456 /* Main.storyboard */; };
1313 2426868F1C63B72A00D62456 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2426868E1C63B72A00D62456 /* Assets.xcassets */; };
1414 242686921C63B72A00D62456 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 242686901C63B72A00D62456 /* LaunchScreen.storyboard */; };
15+ 249CF84C1C6D113C008B0C78 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249CF84B1C6D113C008B0C78 /* Matrix.swift */; };
1516 24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */; };
1617 24E1E7E21C67D37F00ECF1C4 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */; };
1718 24E1E7E41C67D39300ECF1C4 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E31C67D39300ECF1C4 /* MetalKit.framework */; };
@@ -57,6 +58,7 @@
5758 2426868E1C63B72A00D62456 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
5859 242686911C63B72A00D62456 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
5960 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>"; };
6062 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKit.swift; sourceTree = "<group>"; };
6163 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
6264 24E1E7E31C67D39300ECF1C4 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
@@ -135,6 +137,7 @@
135137 24ED21CC1C6C2397009C40FC /* MetaballDataSource.swift */,
136138 24E1E7EA1C67D70300ECF1C4 /* MetalMetaballRenderer.swift */,
137139 24E1E7EE1C67D74300ECF1C4 /* Metal.swift */,
140+ 249CF84B1C6D113C008B0C78 /* Matrix.swift */,
138141 24E1E7EF1C67D74300ECF1C4 /* shaders.metal */,
139142 2426868B1C63B72A00D62456 /* Main.storyboard */,
140143 2426868E1C63B72A00D62456 /* Assets.xcassets */,
@@ -295,6 +298,7 @@
295298 24FFA8711C6BA038005ACD60 /* PowerOperator.swift in Sources */,
296299 24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */,
297300 24FFA8851C6BA038005ACD60 /* Zip.swift in Sources */,
301+ 249CF84C1C6D113C008B0C78 /* Matrix.swift in Sources */,
298302 24FFA8791C6BA038005ACD60 /* Cycle.swift in Sources */,
299303 24E1E7F11C67D74300ECF1C4 /* shaders.metal in Sources */,
300304 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 @@
22import Metal
33
44protocol MetaballDataSource {
5− var metaballs: [Metaball] { get }
5+ var metaballGraph: Graph<Metaball>! { get }
66}
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 @@
11import Metal
22import UIKit
33
4−var i = 0
5−
64class MetalMetaballRenderer {
75
86 typealias TargetView = UIImageView
@@ -54,8 +52,6 @@ class MetalMetaballRenderer {
5452 let timeout = dispatch_time(DISPATCH_TIME_NOW, 1000000000)
5553 dispatch_semaphore_wait(semaphore, timeout)
5654
57− print("Update \(i++)!")
58−
5955 state = .Ending
6056
6157 dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.rawValue), 0)) { () -> Void in
@@ -108,6 +104,8 @@ class MetalMetaballRenderer {
108104 commandEncoder.setTexture(computeContext.texture, atIndex: 0)
109105 let metaballInfoBuffer = metaballBuffer()
110106 commandEncoder.setBuffer(metaballInfoBuffer, offset: 0, atIndex: 0)
107+ let edgesBuffer = metaballEdgesBuffer()
108+ commandEncoder.setBuffer(edgesBuffer, offset: 0, atIndex: 1)
111109 commandEncoder.dispatchThreadgroups(threadGroups,
112110 threadsPerThreadgroup: threadGroupCounts)
113111 commandEncoder.endEncoding()
@@ -163,6 +161,14 @@ class MetalMetaballRenderer {
163161 return image
164162 }
165163
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+
166172 func metaballBuffer() -> MTLBuffer {
167173 // Create Float array for buffer
168174 // Exclude metaballs far from the view's bounds
PixelImage/ViewController.swift+12−3
@@ -9,9 +9,7 @@ class ViewController: UIViewController, MetaballDataSource {
99 let width = 350
1010 let height = 600
1111
12− var metaballs = [CGPoint(x: 70, y: 70), CGPoint(x: 270, y: 470), CGPoint(x: 270, y: 70), CGPoint(x: 70, y: 470)].map {
13− Metaball(position: $0)
14− }
12+ var metaballGraph: Graph<Metaball>!
1513 var previousLocation: CGPoint!
1614
1715 var selectedMetaball: Metaball?
@@ -22,6 +20,17 @@ class ViewController: UIViewController, MetaballDataSource {
2220 let recognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
2321 view.addGestureRecognizer(recognizer)
2422
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+
2534 let border = 20
2635 let metaballViewFrame = CGRect(x: border/2, y: border/2, width: width, height: height)
2736 renderer = MetalMetaballRenderer(dataSource: self, frame: metaballViewFrame)
PixelImage/shaders.metal+21−15
@@ -5,40 +5,46 @@ using namespace metal;
55
66kernel void
77 drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]],
8− constant float *metaballBuffer [[buffer(0)]],
8+ constant float *edgesBuffer[[buffer(1)]],
9+ constant float *metaballBuffer[[buffer(0)]],
910 uint2 gid[[thread_position_in_grid]]) {
1011
11− char numberOfMetaballs = metaballBuffer[0];
12− char metaballArraySize = numberOfMetaballs * 2 + 1;
12+ uint numberOfMetaballs = metaballBuffer[0];
13+ uint metaballArraySize = numberOfMetaballs * 2 + 1;
1314
1415 float sum = 0;
1516
16− for (char i = 1; i < metaballArraySize; i += 2) {
17− for (char j = i + 2; j < metaballArraySize; j += 2) {
18− float2 metaball1 = float2(metaballBuffer[i],
19− metaballBuffer[i + 1]);
17+ uint i, j, x, y;
2018
21− float2 metaball2 = float2(metaballBuffer[j],
22− metaballBuffer[j + 1]);
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+
23+ float2 metaball2 = float2(metaballBuffer[j], metaballBuffer[j + 1]);
2324
2425 float2 metaball1Vector
2526 = float2(metaball1.x - gid.x, metaball1.y - gid.y);
2627 float2 metaball2Vector
2728 = float2(metaball2.x - gid.x, metaball2.y - gid.y);
2829
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];
2939 float2 direction1 = normalize(metaball1Vector);
3040 float2 direction2 = normalize(metaball2Vector);
3141
3242 float cosine = dot(direction1, direction2);
3343
34− float value1 = 2048 / (dot(metaball1Vector, metaball1Vector) + 1);
35− float value2 = 2048 / (dot(metaball2Vector, metaball2Vector) + 1);
36−
37− float v = value1 + value2;
3844 float link = pow(((1 - cosine) * 0.5), 100);
3945
40− float weightedLink = 0.6 * link;
41− float weightedValue = 0.5 * v;
46+ float weightedLink = 0.6 * link * edgeWeight;
47+
4248
4349 sum += mix(0.0, 1.0, step(0.5, weightedValue + weightedLink));
4450 }