Render sampled grid with colors and stuff!
This commit is contained in:
parent
7694a641a0
commit
720e3b1b64
3 changed files with 55 additions and 82 deletions
|
@ -19,6 +19,7 @@ struct RasterizerData {
|
||||||
float4 position [[position]];
|
float4 position [[position]];
|
||||||
float4 color;
|
float4 color;
|
||||||
float2 textureCoordinate;
|
float2 textureCoordinate;
|
||||||
|
int instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
vertex RasterizerData
|
vertex RasterizerData
|
||||||
|
@ -34,12 +35,17 @@ gridVertexShader(constant Vertex *vertexes [[buffer(0)]],
|
||||||
|
|
||||||
RasterizerData out;
|
RasterizerData out;
|
||||||
out.position = renderParameters.projection * rect.transform * float4(v.position.xy, 0, 1);
|
out.position = renderParameters.projection * rect.transform * float4(v.position.xy, 0, 1);
|
||||||
|
out.color = rect.color;
|
||||||
out.textureCoordinate = v.textureCoordinate;
|
out.textureCoordinate = v.textureCoordinate;
|
||||||
|
out.instance = instid;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment float4
|
fragment float4
|
||||||
gridFragmentShader(RasterizerData in [[stage_in]])
|
gridFragmentShader(RasterizerData in [[stage_in]],
|
||||||
|
constant float *samples [[buffer(0)]])
|
||||||
{
|
{
|
||||||
return in.color;
|
int instance = in.instance;
|
||||||
|
float sample = samples[instance];
|
||||||
|
return sample > 1.0 ? in.color : float4(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,13 @@ class MarchingSquares {
|
||||||
private var field: Field
|
private var field: Field
|
||||||
private var sampleGridSize = Size(16)
|
private var sampleGridSize = Size(16)
|
||||||
|
|
||||||
|
private var semaphore: DispatchSemaphore
|
||||||
|
|
||||||
/// Samples of the field's current state.
|
/// Samples of the field's current state.
|
||||||
private(set) var samples: MTLTexture?
|
private(set) var samplesBuffer: MTLBuffer?
|
||||||
/// Indexes of geometry to render.
|
/// Indexes of geometry to render.
|
||||||
private(set) var indexes: MTLTexture?
|
private(set) var indexes: MTLTexture?
|
||||||
|
|
||||||
private var lastGridCount: Int = 0
|
|
||||||
private(set) var gridGeometry: MTLBuffer?
|
private(set) var gridGeometry: MTLBuffer?
|
||||||
|
|
||||||
private var xSamples: Int {
|
private var xSamples: Int {
|
||||||
|
@ -29,12 +30,15 @@ class MarchingSquares {
|
||||||
return Int(field.size.y / sampleGridSize.y)
|
return Int(field.size.y / sampleGridSize.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var lastSamplesCount = 0
|
||||||
|
|
||||||
var samplesCount: Int {
|
var samplesCount: Int {
|
||||||
return xSamples * ySamples
|
return xSamples * ySamples
|
||||||
}
|
}
|
||||||
|
|
||||||
init(field: Field) {
|
init(field: Field) {
|
||||||
self.field = field
|
self.field = field
|
||||||
|
semaphore = DispatchSemaphore(value: 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupMetal(withDevice device: MTLDevice) {
|
func setupMetal(withDevice device: MTLDevice) {
|
||||||
|
@ -54,15 +58,16 @@ class MarchingSquares {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fieldDidResize() {
|
func fieldDidResize() {
|
||||||
guard let gridGeometry = gridGeometry else {
|
guard let device = gridGeometry?.device else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
createGridGeometryBuffer(withDevice: gridGeometry.device)
|
populateGrid(withDevice: device)
|
||||||
populateGrid(withDevice: gridGeometry.device)
|
populateSamples(withDevice: device)
|
||||||
|
lastSamplesCount = samplesCount
|
||||||
}
|
}
|
||||||
|
|
||||||
func populateGrid(withDevice device: MTLDevice) {
|
func populateGrid(withDevice device: MTLDevice) {
|
||||||
guard lastGridCount != samplesCount else {
|
guard lastSamplesCount != samplesCount else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +82,8 @@ class MarchingSquares {
|
||||||
for y in 0..<ySamples {
|
for y in 0..<ySamples {
|
||||||
for x in 0..<xSamples {
|
for x in 0..<xSamples {
|
||||||
let transform = Matrix4x4.translation(dx: Float(x) * gridSizeX, dy: Float(y) * gridSizeY, dz: 0.0) * Matrix4x4.scale(x: gridSizeX, y: gridSizeY, z: 1)
|
let transform = Matrix4x4.translation(dx: Float(x) * gridSizeX, dy: Float(y) * gridSizeY, dz: 0.0) * Matrix4x4.scale(x: gridSizeX, y: gridSizeY, z: 1)
|
||||||
let rect = Rect(transform: transform, color: Float4(1.0))
|
let color = Float4(r: 0, g: 1, b: 0, a: 1)
|
||||||
|
let rect = Rect(transform: transform, color: color)
|
||||||
grid.append(rect)
|
grid.append(rect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,72 +94,29 @@ class MarchingSquares {
|
||||||
} else {
|
} else {
|
||||||
fatalError("Couldn't create buffer for grid rects")
|
fatalError("Couldn't create buffer for grid rects")
|
||||||
}
|
}
|
||||||
|
|
||||||
lastGridCount = samplesCount
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createGridGeometryBuffer(withDevice device: MTLDevice) {
|
func populateSamples(withDevice device: MTLDevice) {
|
||||||
// Allocate a buffer with enough space for the rect vertex data, and all the rect instances we need to render.
|
print("Populating samples buffer with \(samplesCount) values")
|
||||||
// [rect] [rect] ...
|
|
||||||
|
|
||||||
}
|
var samples = [Float]()
|
||||||
|
samples.reserveCapacity(samplesCount)
|
||||||
|
|
||||||
func sampleField() {
|
for ys in 0..<ySamples {
|
||||||
guard let samples = samples else { return }
|
let y = Float(ys * Int(sampleGridSize.y))
|
||||||
|
for xs in 0..<xSamples {
|
||||||
let bytesPerRow = samples.width * MemoryLayout<Float>.stride
|
let x = Float(xs * Int(sampleGridSize.x))
|
||||||
|
let sample = field.sample(at: Float2(x: x, y: y))
|
||||||
for xSample in 0..<samples.width {
|
samples.append(sample)
|
||||||
let x = Float(xSample * Int(sampleGridSize.x))
|
|
||||||
for ySample in 0..<samples.height {
|
|
||||||
let y = Float(ySample * Int(sampleGridSize.y))
|
|
||||||
let sample = [field.sample(at: Float2(x: x, y: y))]
|
|
||||||
|
|
||||||
let origin = MTLOrigin(x: xSample, y: ySample, z: 0)
|
|
||||||
let size = MTLSize(width: 1, height: 1, depth: 1)
|
|
||||||
let region = MTLRegion(origin: origin, size: size)
|
|
||||||
samples.replace(region: region, mipmapLevel: 0, withBytes: sample, bytesPerRow: bytesPerRow)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func populateIndexes() {
|
let samplesLength = MemoryLayout<Float>.stride * samplesCount
|
||||||
guard let indexes = indexes else { return }
|
if let buffer = device.makeBuffer(length: MemoryLayout<Float>.stride * samples.count, options: .storageModeShared) {
|
||||||
|
memcpy(buffer.contents(), samples, samplesLength)
|
||||||
let bytesPerRow = indexes.width * MemoryLayout<UInt8>.stride
|
samplesBuffer = buffer
|
||||||
|
} else {
|
||||||
for x in 0..<indexes.width {
|
fatalError("Couldn't create buffer for samples")
|
||||||
for y in 0..<indexes.height {
|
|
||||||
guard let samples = getSampleBlock(x: x, y: y) else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = (samples[0] > 1.0 ? 0b1000 : 0) +
|
|
||||||
(samples[1] > 1.0 ? 0b0100 : 0) +
|
|
||||||
(samples[2] > 1.0 ? 0b0001 : 0) +
|
|
||||||
(samples[3] > 1.0 ? 0b0010 : 0)
|
|
||||||
|
|
||||||
let origin = MTLOrigin(x: x, y: y, z: 0)
|
|
||||||
let size = MTLSize(width: 1, height: 1, depth: 1)
|
|
||||||
let region = MTLRegion(origin: origin, size: size)
|
|
||||||
let indexArr = [index]
|
|
||||||
indexes.replace(region: region, mipmapLevel: 0, withBytes: indexArr, bytesPerRow: bytesPerRow)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getSampleBlock(x: Int, y: Int) -> [Float]? {
|
|
||||||
guard let samples = samples else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var block: [Float] = [0, 0, 0, 0]
|
|
||||||
let bytesPerRow = samples.width * MemoryLayout<Float>.stride
|
|
||||||
let origin = MTLOrigin(x: x, y: y, z: 0)
|
|
||||||
let size = MTLSize(width: 2, height: 2, depth: 1)
|
|
||||||
let region = MTLRegion(origin: origin, size: size)
|
|
||||||
samples.getBytes(&block, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
|
|
||||||
|
|
||||||
return block
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ public class Renderer: NSObject, MTKViewDelegate {
|
||||||
delegate.field.setupMetal(withDevice: device)
|
delegate.field.setupMetal(withDevice: device)
|
||||||
delegate.marchingSquares.setupMetal(withDevice: device)
|
delegate.marchingSquares.setupMetal(withDevice: device)
|
||||||
delegate.marchingSquares.populateGrid(withDevice: device)
|
delegate.marchingSquares.populateGrid(withDevice: device)
|
||||||
|
delegate.marchingSquares.populateSamples(withDevice: device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +208,9 @@ public class Renderer: NSObject, MTKViewDelegate {
|
||||||
buffer.label = "Metaballs Command Buffer"
|
buffer.label = "Metaballs Command Buffer"
|
||||||
|
|
||||||
field.update()
|
field.update()
|
||||||
|
if let ms = delegate?.marchingSquares {
|
||||||
|
ms.populateSamples(withDevice: device)
|
||||||
|
}
|
||||||
|
|
||||||
if self.pixelGeometry == nil {
|
if self.pixelGeometry == nil {
|
||||||
self.pixelGeometry = self.pixelGeometry(forViewSize: view.drawableSize)
|
self.pixelGeometry = self.pixelGeometry(forViewSize: view.drawableSize)
|
||||||
|
@ -215,24 +219,24 @@ public class Renderer: NSObject, MTKViewDelegate {
|
||||||
|
|
||||||
if let renderPass = view.currentRenderPassDescriptor {
|
if let renderPass = view.currentRenderPassDescriptor {
|
||||||
// Render the per-pixel metaballs
|
// Render the per-pixel metaballs
|
||||||
if let pipeline = pixelPipeline,
|
// if let pipeline = pixelPipeline,
|
||||||
let encoder = buffer.makeRenderCommandEncoder(descriptor: renderPass) {
|
// let encoder = buffer.makeRenderCommandEncoder(descriptor: renderPass) {
|
||||||
encoder.label = "Pixel Render"
|
// encoder.label = "Pixel Render"
|
||||||
encoder.setRenderPipelineState(pipeline)
|
// encoder.setRenderPipelineState(pipeline)
|
||||||
encoder.setVertexBytes(pixelGeometry, length: pixelGeometry.count * MemoryLayout<Vertex>.stride, index: 0)
|
// encoder.setVertexBytes(pixelGeometry, length: pixelGeometry.count * MemoryLayout<Vertex>.stride, index: 0)
|
||||||
encoder.setVertexBuffer(parametersBuffer, offset: 0, index: 1)
|
// encoder.setVertexBuffer(parametersBuffer, offset: 0, index: 1)
|
||||||
encoder.setFragmentBuffer(field.parametersBuffer, offset: 0, index: 0)
|
// encoder.setFragmentBuffer(field.parametersBuffer, offset: 0, index: 0)
|
||||||
encoder.setFragmentBuffer(field.ballBuffer, offset: 0, index: 1)
|
// encoder.setFragmentBuffer(field.ballBuffer, offset: 0, index: 1)
|
||||||
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6)
|
// encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6)
|
||||||
encoder.endEncoding()
|
// encoder.endEncoding()
|
||||||
didEncode = true
|
// didEncode = true
|
||||||
}
|
// }
|
||||||
|
|
||||||
if let marchingSquares = delegate?.marchingSquares {
|
if let marchingSquares = delegate?.marchingSquares {
|
||||||
// Render the marching squares version over top of the pixel version.
|
// Render the marching squares version over top of the pixel version.
|
||||||
// We need our own render pass descriptor that specifies that we load the results of the previous pass to make this render pass appear on top of the other.
|
// We need our own render pass descriptor that specifies that we load the results of the previous pass to make this render pass appear on top of the other.
|
||||||
let pass = renderPass.copy() as! MTLRenderPassDescriptor
|
let pass = renderPass.copy() as! MTLRenderPassDescriptor
|
||||||
pass.colorAttachments[0].loadAction = .load
|
// pass.colorAttachments[0].loadAction = .load
|
||||||
if let pipeline = marchingSquaresPipeline,
|
if let pipeline = marchingSquaresPipeline,
|
||||||
let encoder = buffer.makeRenderCommandEncoder(descriptor: pass) {
|
let encoder = buffer.makeRenderCommandEncoder(descriptor: pass) {
|
||||||
encoder.label = "Marching Squares Grid Render"
|
encoder.label = "Marching Squares Grid Render"
|
||||||
|
@ -240,7 +244,7 @@ public class Renderer: NSObject, MTKViewDelegate {
|
||||||
encoder.setVertexBytes(Rect.geometry, length: MemoryLayout<Vertex>.stride * Rect.geometry.count, index: 0)
|
encoder.setVertexBytes(Rect.geometry, length: MemoryLayout<Vertex>.stride * Rect.geometry.count, index: 0)
|
||||||
encoder.setVertexBuffer(marchingSquares.gridGeometry, offset: 0, index: 1)
|
encoder.setVertexBuffer(marchingSquares.gridGeometry, offset: 0, index: 1)
|
||||||
encoder.setVertexBuffer(parametersBuffer, offset: 0, index: 2)
|
encoder.setVertexBuffer(parametersBuffer, offset: 0, index: 2)
|
||||||
encoder.setTriangleFillMode(.lines)
|
encoder.setFragmentBuffer(marchingSquares.samplesBuffer, offset: 0, index: 0)
|
||||||
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: Rect.geometry.count, instanceCount: marchingSquares.samplesCount)
|
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: Rect.geometry.count, instanceCount: marchingSquares.samplesCount)
|
||||||
encoder.endEncoding()
|
encoder.endEncoding()
|
||||||
didEncode = true
|
didEncode = true
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue