Oops fix my algorithm and update the tests

This commit is contained in:
Eryn Wells 2018-11-10 17:05:54 -05:00
parent 359c7ef987
commit 5233a94fec
2 changed files with 62 additions and 63 deletions

View file

@ -17,10 +17,12 @@ enum KernelError: Error {
protocol TerrainGenerator { protocol TerrainGenerator {
var name: String { get } var name: String { get }
var needsGPU: Bool { get }
var outTexture: MTLTexture { get } var outTexture: MTLTexture { get }
func updateUniforms() func updateUniforms()
func encode(in encoder: MTLComputeCommandEncoder) func encode(in encoder: MTLComputeCommandEncoder)
func render()
} }
class Kernel { class Kernel {
@ -79,6 +81,8 @@ class Kernel {
class ZeroAlgorithm: Kernel, TerrainGenerator { class ZeroAlgorithm: Kernel, TerrainGenerator {
let name = "Zero" let name = "Zero"
let needsGPU: Bool = true
init?(device: MTLDevice, library: MTLLibrary) { init?(device: MTLDevice, library: MTLLibrary) {
do { do {
try super.init(device: device, library: library, functionName: "zeroKernel") try super.init(device: device, library: library, functionName: "zeroKernel")
@ -91,12 +95,16 @@ class ZeroAlgorithm: Kernel, TerrainGenerator {
// MARK: Algorithm // MARK: Algorithm
func updateUniforms() { } func updateUniforms() { }
func render() { }
} }
/// Randomly generate heights that are independent of all others. /// Randomly generate heights that are independent of all others.
class RandomAlgorithm: Kernel, TerrainGenerator { class RandomAlgorithm: Kernel, TerrainGenerator {
let name = "Random" let name = "Random"
let needsGPU: Bool = true
private var uniforms: UnsafeMutablePointer<RandomAlgorithmUniforms> private var uniforms: UnsafeMutablePointer<RandomAlgorithmUniforms>
init?(device: MTLDevice, library: MTLLibrary) { init?(device: MTLDevice, library: MTLLibrary) {
@ -121,6 +129,8 @@ class RandomAlgorithm: Kernel, TerrainGenerator {
func updateUniforms() { func updateUniforms() {
RandomAlgorithmUniforms_refreshRandoms(uniforms) RandomAlgorithmUniforms_refreshRandoms(uniforms)
} }
func render() { }
} }
/// Implementation of the Diamond-Squares algorithm. /// Implementation of the Diamond-Squares algorithm.
@ -162,19 +172,19 @@ public class DiamondSquareGenerator: TerrainGenerator {
} }
var north: Point { var north: Point {
return Point(x: origin.x + (size.w / 2 + 1), y: origin.y) return Point(x: origin.x + size.w / 2, y: origin.y)
} }
var west: Point { var west: Point {
return Point(x: origin.x, y: origin.y + (size.h / 2 + 1)) return Point(x: origin.x, y: origin.y + size.h / 2)
} }
var south: Point { var south: Point {
return Point(x: origin.x + (size.w / 2 + 1), y: origin.y + size.h) return Point(x: origin.x + size.w / 2, y: origin.y + size.h - 1)
} }
var east: Point { var east: Point {
return Point(x: origin.x + size.w, y: origin.y + (size.h / 2 + 1)) return Point(x: origin.x + size.w - 1, y: origin.y + size.h / 2)
} }
var northwest: Point { var northwest: Point {
@ -182,32 +192,32 @@ public class DiamondSquareGenerator: TerrainGenerator {
} }
var southwest: Point { var southwest: Point {
return Point(x: origin.x, y: origin.y + size.h) return Point(x: origin.x, y: origin.y + size.h - 1)
} }
var northeast: Point { var northeast: Point {
return Point(x: origin.x + size.w, y: origin.y) return Point(x: origin.x + size.w - 1, y: origin.y)
} }
var southeast: Point { var southeast: Point {
return Point(x: origin.x + size.w, y: origin.y + size.h) return Point(x: origin.x + size.w - 1, y: origin.y + size.h - 1)
} }
var midpoint: Point { var midpoint: Point {
return Point(x: origin.x + (size.w / 2 + 1), y: origin.y + (size.h / 2 + 1)) return Point(x: origin.x + (size.w / 2), y: origin.y + (size.h / 2))
} }
var subdivisions: [Box] { var subdivisions: [Box] {
guard size.w > 2 && size.h > 2 else { guard size.w > 2 && size.h > 2 else {
return [] return []
} }
let midp = midpoint let halfSize = size.half
let newSize = Size(w: midp.x - origin.x, h: midp.y - origin.y) let newSize = Size(w: halfSize.w + 1, h: halfSize.h + 1)
return [ return [
Box(origin: origin, size: newSize), Box(origin: origin, size: newSize),
Box(origin: Point(x: origin.x + newSize.w, y: origin.y), size: newSize), Box(origin: Point(x: origin.x + halfSize.w, y: origin.y), size: newSize),
Box(origin: Point(x: origin.x, y: origin.y + newSize.h), size: newSize), Box(origin: Point(x: origin.x, y: origin.y + halfSize.h), size: newSize),
Box(origin: Point(x: origin.x + newSize.w, y: origin.y + newSize.h), size: newSize) Box(origin: Point(x: origin.x + halfSize.w, y: origin.y + halfSize.h), size: newSize)
] ]
} }
@ -293,6 +303,7 @@ public class DiamondSquareGenerator: TerrainGenerator {
} }
let name = "Diamond-Square" let name = "Diamond-Square"
let needsGPU: Bool = false
class var textureSize: MTLSize { class var textureSize: MTLSize {
// Needs to 2n + 1 on each side. // Needs to 2n + 1 on each side.
@ -307,7 +318,6 @@ public class DiamondSquareGenerator: TerrainGenerator {
let size = DiamondSquareGenerator.textureSize let size = DiamondSquareGenerator.textureSize
let desc = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r32Float, width: size.width, height: size.height, mipmapped: false) let desc = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r32Float, width: size.width, height: size.height, mipmapped: false)
desc.usage = [.shaderRead, .shaderWrite] desc.usage = [.shaderRead, .shaderWrite]
desc.resourceOptions = .storageModeShared
guard let tex = device.makeTexture(descriptor: desc) else { guard let tex = device.makeTexture(descriptor: desc) else {
print("Couldn't create texture for Diamond-Squares algorithm.") print("Couldn't create texture for Diamond-Squares algorithm.")
return nil return nil
@ -320,7 +330,7 @@ public class DiamondSquareGenerator: TerrainGenerator {
func render() { func render() {
let heightMap = algorithm.render() let heightMap = algorithm.render()
let region = MTLRegion(origin: MTLOrigin(), size: DiamondSquareGenerator.textureSize) let region = MTLRegion(origin: MTLOrigin(), size: DiamondSquareGenerator.textureSize)
texture.replace(region: region, mipmapLevel: 0, withBytes: heightMap, bytesPerRow: MemoryLayout<Float>.stride * size.width) texture.replace(region: region, mipmapLevel: 0, withBytes: heightMap, bytesPerRow: MemoryLayout<Float>.stride * DiamondSquareGenerator.textureSize.width)
} }
// MARK: Algorithm // MARK: Algorithm

View file

@ -65,50 +65,51 @@ class DiamondSquareAlgorithmTests: XCTestCase {
} }
class DiamondSquareBoxTests: XCTestCase { class DiamondSquareBoxTests: XCTestCase {
fileprivate let box = Box(origin: Point(x: 3, y: 4), size: Size(w: 5, h: 5)) fileprivate let box = Box(origin: Point(x: 0, y: 0), size: Size(w: 5, h: 5))
func testMidpoint() { func testMidpoint() {
let midpoint = box.midpoint let midpoint = box.midpoint
XCTAssertEqual(midpoint.x, 6) XCTAssertEqual(midpoint.x, 2)
XCTAssertEqual(midpoint.y, 7) XCTAssertEqual(midpoint.y, 2)
} }
func testSubdivision() { func testSubdivision() {
let subdivs = box.subdivisions let subdivs = box.subdivisions
XCTAssertEqual(subdivs.count, 4) XCTAssertEqual(subdivs.count, 4)
XCTAssertEqual(subdivs[0], Box(origin: Point(x: 3, y: 4), size: Size(w: 3, h: 3))) XCTAssertEqual(subdivs[0], Box(origin: Point(x: 0, y: 0), size: Size(w: 3, h: 3)))
XCTAssertEqual(subdivs[1], Box(origin: Point(x: 6, y: 4), size: Size(w: 3, h: 3))) XCTAssertEqual(subdivs[1], Box(origin: Point(x: 2, y: 0), size: Size(w: 3, h: 3)))
XCTAssertEqual(subdivs[2], Box(origin: Point(x: 3, y: 7), size: Size(w: 3, h: 3))) XCTAssertEqual(subdivs[2], Box(origin: Point(x: 0, y: 2), size: Size(w: 3, h: 3)))
XCTAssertEqual(subdivs[3], Box(origin: Point(x: 6, y: 7), size: Size(w: 3, h: 3))) XCTAssertEqual(subdivs[3], Box(origin: Point(x: 2, y: 2), size: Size(w: 3, h: 3)))
} }
func testBFS() { func testBFS() {
var expectedBoxes: [Box] = [ var expectedBoxes: [Box] = [
box, box,
Box(origin: Point(x: 3, y: 4), size: Size(w: 3, h: 3)),
Box(origin: Point(x: 6, y: 4), size: Size(w: 3, h: 3)),
Box(origin: Point(x: 3, y: 7), size: Size(w: 3, h: 3)),
Box(origin: Point(x: 6, y: 7), size: Size(w: 3, h: 3)),
Box(origin: Point(x: 3, y: 4), size: Size(w: 2, h: 2)), Box(origin: Point(x: 0, y: 0), size: Size(w: 3, h: 3)),
Box(origin: Point(x: 5, y: 4), size: Size(w: 2, h: 2)), Box(origin: Point(x: 2, y: 0), size: Size(w: 3, h: 3)),
Box(origin: Point(x: 3, y: 6), size: Size(w: 2, h: 2)), Box(origin: Point(x: 0, y: 2), size: Size(w: 3, h: 3)),
Box(origin: Point(x: 5, y: 6), size: Size(w: 2, h: 2)), Box(origin: Point(x: 2, y: 2), size: Size(w: 3, h: 3)),
Box(origin: Point(x: 6, y: 4), size: Size(w: 2, h: 2)), Box(origin: Point(x: 0, y: 0), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 8, y: 4), size: Size(w: 2, h: 2)), Box(origin: Point(x: 1, y: 0), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 6, y: 6), size: Size(w: 2, h: 2)), Box(origin: Point(x: 0, y: 1), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 8, y: 6), size: Size(w: 2, h: 2)), Box(origin: Point(x: 1, y: 1), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 3, y: 7), size: Size(w: 2, h: 2)), Box(origin: Point(x: 2, y: 0), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 5, y: 7), size: Size(w: 2, h: 2)), Box(origin: Point(x: 3, y: 0), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 3, y: 9), size: Size(w: 2, h: 2)), Box(origin: Point(x: 2, y: 1), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 5, y: 9), size: Size(w: 2, h: 2)), Box(origin: Point(x: 3, y: 1), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 6, y: 7), size: Size(w: 2, h: 2)), Box(origin: Point(x: 0, y: 2), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 8, y: 7), size: Size(w: 2, h: 2)), Box(origin: Point(x: 1, y: 2), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 6, y: 9), size: Size(w: 2, h: 2)), Box(origin: Point(x: 0, y: 3), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 8, y: 9), size: Size(w: 2, h: 2)), Box(origin: Point(x: 1, y: 3), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 2, y: 2), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 3, y: 2), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 2, y: 3), size: Size(w: 2, h: 2)),
Box(origin: Point(x: 3, y: 3), size: Size(w: 2, h: 2)),
].reversed() ].reversed()
box.breadthFirstSearch { (box: Box) -> (Void) in box.breadthFirstSearch { (box: Box) -> (Void) in
@ -122,52 +123,40 @@ class DiamondSquareBoxTests: XCTestCase {
// MARK: Sides // MARK: Sides
func testNorth() { func testNorth() {
let pt = box.north XCTAssertEqual(box.north, Point(x: 2, y: 0))
XCTAssertEqual(pt.x, 6)
XCTAssertEqual(pt.y, 4)
} }
func testWest() { func testWest() {
let pt = box.west XCTAssertEqual(box.west, Point(x: 0, y: 2))
XCTAssertEqual(pt.x, 3)
XCTAssertEqual(pt.y, 7)
} }
func testSouth() { func testSouth() {
let pt = box.south XCTAssertEqual(box.south, Point(x: 2, y: 4))
XCTAssertEqual(pt.x, 6)
XCTAssertEqual(pt.y, 9)
} }
func testEast() { func testEast() {
let pt = box.east XCTAssertEqual(box.east, Point(x: 4, y: 2))
XCTAssertEqual(pt.x, 8)
XCTAssertEqual(pt.y, 7)
} }
// MARK: Corners // MARK: Corners
func testNorthwest() { func testNorthwest() {
let pt = box.northwest let pt = box.northwest
XCTAssertEqual(pt.x, 3) XCTAssertEqual(pt, Point())
XCTAssertEqual(pt.y, 4)
} }
func testNortheast() { func testNortheast() {
let pt = box.northeast let pt = box.northeast
XCTAssertEqual(pt.x, 8) XCTAssertEqual(pt, Point(x: 4, y: 0))
XCTAssertEqual(pt.y, 4)
} }
func testSouthwest() { func testSouthwest() {
let pt = box.southwest let pt = box.southwest
XCTAssertEqual(pt.x, 3) XCTAssertEqual(pt, Point(x: 0, y: 4))
XCTAssertEqual(pt.y, 9)
} }
func testSoutheast() { func testSoutheast() {
let pt = box.southeast let pt = box.southeast
XCTAssertEqual(pt.x, 8) XCTAssertEqual(pt, Point(x: 4, y: 4))
XCTAssertEqual(pt.y, 9)
} }
} }