mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 05:10:57 +00:00
prep for chunk meshing
This commit is contained in:
parent
cb0e7bb232
commit
da26773221
@ -36,6 +36,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
|
|||||||
# Renderer classes
|
# Renderer classes
|
||||||
Renderer/Material.swift
|
Renderer/Material.swift
|
||||||
Renderer/Environment.swift
|
Renderer/Environment.swift
|
||||||
|
Renderer/Mesh.swift
|
||||||
Renderer/Renderer.swift
|
Renderer/Renderer.swift
|
||||||
|
|
||||||
# Input wrappers
|
# Input wrappers
|
||||||
@ -44,6 +45,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
|
|||||||
Input/Mouse.swift
|
Input/Mouse.swift
|
||||||
|
|
||||||
# Core utility classes
|
# Core utility classes
|
||||||
|
CubeMeshBuilder.swift
|
||||||
Color.swift
|
Color.swift
|
||||||
Camera.swift
|
Camera.swift
|
||||||
FPSCalculator.swift
|
FPSCalculator.swift
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
public struct Chunk {
|
public struct Chunk: Hashable {
|
||||||
public static let shift = 4 // 16
|
public static let shift = 4 // 16
|
||||||
public static let size: Int = 1 << shift
|
public static let size: Int = 1 << shift
|
||||||
public static let mask: Int = size - 1
|
public static let mask: Int = size - 1
|
||||||
@ -63,6 +63,15 @@ public struct Chunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func forEach(_ body: @escaping (Block, SIMD3<Int>) throws -> Void) rethrows {
|
||||||
|
for i in 0..<Self.blockCount {
|
||||||
|
try body(blocks[i], self.origin &+ SIMD3(
|
||||||
|
x: i & Self.mask,
|
||||||
|
y: (i &>> Self.shift) & Self.mask,
|
||||||
|
z: (i &>> (Self.shift + Self.shift)) & Self.mask))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func map<T>(block transform: (Block, SIMD3<Int>) throws -> T) rethrows -> [T] {
|
public func map<T>(block transform: (Block, SIMD3<Int>) throws -> T) rethrows -> [T] {
|
||||||
assert(self.blocks.count == Self.blockCount)
|
assert(self.blocks.count == Self.blockCount)
|
||||||
|
|
||||||
@ -112,12 +121,12 @@ public struct Chunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum BlockType: Equatable {
|
public enum BlockType: Hashable {
|
||||||
case air
|
case air
|
||||||
case solid(_ color: Color<Float16>)
|
case solid(_ color: Color<Float16>)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Block {
|
public struct Block: Hashable {
|
||||||
public var type: BlockType
|
public var type: BlockType
|
||||||
|
|
||||||
public init(_ type: BlockType) {
|
public init(_ type: BlockType) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Color<T: SIMDScalar>: Equatable {
|
public struct Color<T: SIMDScalar>: Hashable {
|
||||||
private var _values: SIMD4<T>
|
private var _values: SIMD4<T>
|
||||||
|
|
||||||
internal var values: SIMD4<T> { self._values }
|
internal var values: SIMD4<T> { self._values }
|
||||||
|
40
Sources/Voxelotl/CubeMeshBuilder.swift
Normal file
40
Sources/Voxelotl/CubeMeshBuilder.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
public struct CubeMeshBuilder {
|
||||||
|
public static func build(bound: AABB) -> Mesh<VertexPositionNormalTexcoord, UInt16> {
|
||||||
|
let cubeVertices: [VertexPositionNormalTexcoord] = [
|
||||||
|
.init(position: .init( bound.left, bound.bottom, bound.near), normal: .back, texCoord: .init(0, 0)),
|
||||||
|
.init(position: .init(bound.right, bound.bottom, bound.near), normal: .back, texCoord: .init(1, 0)),
|
||||||
|
.init(position: .init( bound.left, bound.top, bound.near), normal: .back, texCoord: .init(0, 1)),
|
||||||
|
.init(position: .init(bound.right, bound.top, bound.near), normal: .back, texCoord: .init(1, 1)),
|
||||||
|
.init(position: .init(bound.right, bound.bottom, bound.near), normal: .right, texCoord: .init(0, 0)),
|
||||||
|
.init(position: .init(bound.right, bound.bottom, bound.far), normal: .right, texCoord: .init(1, 0)),
|
||||||
|
.init(position: .init(bound.right, bound.top, bound.near), normal: .right, texCoord: .init(0, 1)),
|
||||||
|
.init(position: .init(bound.right, bound.top, bound.far), normal: .right, texCoord: .init(1, 1)),
|
||||||
|
.init(position: .init(bound.right, bound.bottom, bound.far), normal: .forward, texCoord: .init(0, 0)),
|
||||||
|
.init(position: .init( bound.left, bound.bottom, bound.far), normal: .forward, texCoord: .init(1, 0)),
|
||||||
|
.init(position: .init(bound.right, bound.top, bound.far), normal: .forward, texCoord: .init(0, 1)),
|
||||||
|
.init(position: .init( bound.left, bound.top, bound.far), normal: .forward, texCoord: .init(1, 1)),
|
||||||
|
.init(position: .init( bound.left, bound.bottom, bound.far), normal: .left, texCoord: .init(0, 0)),
|
||||||
|
.init(position: .init( bound.left, bound.bottom, bound.near), normal: .left, texCoord: .init(1, 0)),
|
||||||
|
.init(position: .init( bound.left, bound.top, bound.far), normal: .left, texCoord: .init(0, 1)),
|
||||||
|
.init(position: .init( bound.left, bound.top, bound.near), normal: .left, texCoord: .init(1, 1)),
|
||||||
|
.init(position: .init( bound.left, bound.bottom, bound.far), normal: .down, texCoord: .init(0, 0)),
|
||||||
|
.init(position: .init(bound.right, bound.bottom, bound.far), normal: .down, texCoord: .init(1, 0)),
|
||||||
|
.init(position: .init( bound.left, bound.bottom, bound.near), normal: .down, texCoord: .init(0, 1)),
|
||||||
|
.init(position: .init(bound.right, bound.bottom, bound.near), normal: .down, texCoord: .init(1, 1)),
|
||||||
|
.init(position: .init( bound.left, bound.top, bound.near), normal: .up, texCoord: .init(0, 0)),
|
||||||
|
.init(position: .init(bound.right, bound.top, bound.near), normal: .up, texCoord: .init(1, 0)),
|
||||||
|
.init(position: .init( bound.left, bound.top, bound.far), normal: .up, texCoord: .init(0, 1)),
|
||||||
|
.init(position: .init(bound.right, bound.top, bound.far), normal: .up, texCoord: .init(1, 1))
|
||||||
|
]
|
||||||
|
return .init(vertices: cubeVertices, indices: cubeIndices)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate let cubeIndices: [UInt16] = [
|
||||||
|
0, 1, 2, 2, 1, 3,
|
||||||
|
4, 5, 6, 6, 5, 7,
|
||||||
|
8, 9, 10, 10, 9, 11,
|
||||||
|
12, 13, 14, 14, 13, 15,
|
||||||
|
16, 17, 18, 18, 17, 19,
|
||||||
|
20, 21, 22, 22, 21, 23
|
||||||
|
]
|
@ -25,11 +25,15 @@ class Game: GameDelegate {
|
|||||||
var player = Player()
|
var player = Player()
|
||||||
var projection: matrix_float4x4 = .identity
|
var projection: matrix_float4x4 = .identity
|
||||||
var world = World()
|
var world = World()
|
||||||
|
var cubeMesh: RendererMesh?
|
||||||
|
var renderChunks = [SIMD3<Int>: Mesh<VertexPositionNormalTexcoord, UInt16>]()
|
||||||
|
|
||||||
func create(_ renderer: Renderer) {
|
func create(_ renderer: Renderer) {
|
||||||
self.resetPlayer()
|
self.resetPlayer()
|
||||||
self.generateWorld()
|
self.generateWorld()
|
||||||
|
|
||||||
|
self.cubeMesh = renderer.createMesh(CubeMeshBuilder.build(bound: .fromUnitCube(position: .zero, scale: .one)))
|
||||||
|
|
||||||
renderer.clearColor = Color<Double>.black.mix(.white, 0.1).linear
|
renderer.clearColor = Color<Double>.black.mix(.white, 0.1).linear
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +108,8 @@ class Game: GameDelegate {
|
|||||||
.init(angle: totalTime * 0.7, axis: .init(0, 0, 1)),
|
.init(angle: totalTime * 0.7, axis: .init(0, 0, 1)),
|
||||||
color: .init(r: 0.5, g: 0.5, b: 1).linear))
|
color: .init(r: 0.5, g: 0.5, b: 1).linear))
|
||||||
}
|
}
|
||||||
if !instances.isEmpty {
|
if self.cubeMesh != nil && !instances.isEmpty {
|
||||||
renderer.batch(instances: instances, material: material, environment: env, camera: self.camera)
|
renderer.batch(instances: instances, mesh: self.cubeMesh!, material: material, environment: env, camera: self.camera)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,45 +1,45 @@
|
|||||||
import simd
|
import simd
|
||||||
|
|
||||||
struct AABB {
|
public struct AABB {
|
||||||
private var _bounds: simd_float2x3
|
private var _bounds: simd_float2x3
|
||||||
|
|
||||||
var lower: SIMD3<Float> {
|
public var lower: SIMD3<Float> {
|
||||||
get { _bounds[0] }
|
get { _bounds[0] }
|
||||||
set(row) { self._bounds[0] = row }
|
set(row) { self._bounds[0] = row }
|
||||||
}
|
}
|
||||||
var upper: SIMD3<Float> {
|
public var upper: SIMD3<Float> {
|
||||||
get { _bounds[1] }
|
get { _bounds[1] }
|
||||||
set(row) { self._bounds[1] = row }
|
set(row) { self._bounds[1] = row }
|
||||||
}
|
}
|
||||||
var center: SIMD3<Float> {
|
public var center: SIMD3<Float> {
|
||||||
get { (self._bounds[0] + self._bounds[1]) / 2 }
|
get { (self._bounds[0] + self._bounds[1]) / 2 }
|
||||||
}
|
}
|
||||||
var size: SIMD3<Float> {
|
public var size: SIMD3<Float> {
|
||||||
get { self._bounds[1] - self._bounds[0] }
|
get { self._bounds[1] - self._bounds[0] }
|
||||||
}
|
}
|
||||||
|
|
||||||
var left: Float { self._bounds[0].x }
|
public var left: Float { self._bounds[0].x }
|
||||||
var bottom: Float { self._bounds[0].y }
|
public var bottom: Float { self._bounds[0].y }
|
||||||
var far: Float { self._bounds[0].z }
|
public var far: Float { self._bounds[0].z }
|
||||||
var right: Float { self._bounds[1].x }
|
public var right: Float { self._bounds[1].x }
|
||||||
var top: Float { self._bounds[1].y }
|
public var top: Float { self._bounds[1].y }
|
||||||
var near: Float { self._bounds[1].z }
|
public var near: Float { self._bounds[1].z }
|
||||||
|
|
||||||
private init(bounds: simd_float2x3) {
|
private init(bounds: simd_float2x3) {
|
||||||
self._bounds = bounds
|
self._bounds = bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from: SIMD3<Float>, to: SIMD3<Float>) {
|
public init(from: SIMD3<Float>, to: SIMD3<Float>) {
|
||||||
self.init(bounds: .init(from, to))
|
self.init(bounds: .init(from, to))
|
||||||
}
|
}
|
||||||
|
|
||||||
static func fromUnitCube(position: SIMD3<Float> = .zero, scale: SIMD3<Float> = .one) -> Self {
|
public static func fromUnitCube(position: SIMD3<Float> = .zero, scale: SIMD3<Float> = .one) -> Self {
|
||||||
self.init(
|
self.init(
|
||||||
from: position - scale,
|
from: position - scale,
|
||||||
to: position + scale)
|
to: position + scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
func touching(_ other: Self) -> Bool{
|
public func touching(_ other: Self) -> Bool{
|
||||||
let distLower = other._bounds[0] - self._bounds[1] // x: left, y: bottom, z: far
|
let distLower = other._bounds[0] - self._bounds[1] // x: left, y: bottom, z: far
|
||||||
let distUpper = self._bounds[0] - other._bounds[1] // x: right, y: top, z: near
|
let distUpper = self._bounds[0] - other._bounds[1] // x: right, y: top, z: near
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ struct AABB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AABB {
|
public extension AABB {
|
||||||
static func + (lhs: Self, rhs: SIMD3<Float>) -> Self {
|
public static func + (lhs: Self, rhs: SIMD3<Float>) -> Self {
|
||||||
.init(bounds: lhs._bounds + .init(rhs, rhs))
|
.init(bounds: lhs._bounds + .init(rhs, rhs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,13 @@ public extension SIMD3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension SIMD3 where Scalar: FloatingPoint {
|
public extension SIMD3 where Scalar: Numeric {
|
||||||
@inline(__always) static var X: Self { Self(1, 0, 0) }
|
@inline(__always) static var X: Self { Self(1, 0, 0) }
|
||||||
@inline(__always) static var Y: Self { Self(0, 1, 0) }
|
@inline(__always) static var Y: Self { Self(0, 1, 0) }
|
||||||
@inline(__always) static var Z: Self { Self(0, 0, 1) }
|
@inline(__always) static var Z: Self { Self(0, 0, 1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension SIMD3 where Scalar: FloatingPoint {
|
||||||
@inline(__always) static var up: Self { Y }
|
@inline(__always) static var up: Self { Y }
|
||||||
@inline(__always) static var down: Self { -Y }
|
@inline(__always) static var down: Self { -Y }
|
||||||
@inline(__always) static var left: Self { -X }
|
@inline(__always) static var left: Self { -X }
|
||||||
@ -35,6 +37,15 @@ public extension SIMD3 where Scalar: FloatingPoint {
|
|||||||
@inline(__always) static var back: Self { Z }
|
@inline(__always) static var back: Self { Z }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension SIMD3 where Scalar: SignedInteger & FixedWidthInteger {
|
||||||
|
@inline(__always) static var up: Self { Y }
|
||||||
|
@inline(__always) static var down: Self { 0 &- Y }
|
||||||
|
@inline(__always) static var left: Self { 0 &- X }
|
||||||
|
@inline(__always) static var right: Self { X }
|
||||||
|
@inline(__always) static var forward: Self { 0 &- Z }
|
||||||
|
@inline(__always) static var back: Self { Z }
|
||||||
|
}
|
||||||
|
|
||||||
public extension SIMD3 where Scalar: Numeric & AdditiveArithmetic {
|
public extension SIMD3 where Scalar: Numeric & AdditiveArithmetic {
|
||||||
@inline(__always) func dot(_ b: Self) -> Scalar { self.x * b.x + self.y * b.y + self.z * b.z }
|
@inline(__always) func dot(_ b: Self) -> Scalar { self.x * b.x + self.y * b.y + self.z * b.z }
|
||||||
}
|
}
|
||||||
|
@ -90,12 +90,12 @@ public enum RaycastSide {
|
|||||||
public extension SIMD3 where Scalar == Int {
|
public extension SIMD3 where Scalar == Int {
|
||||||
func offset(by side: RaycastSide) -> Self {
|
func offset(by side: RaycastSide) -> Self {
|
||||||
let ofs: Self = switch side {
|
let ofs: Self = switch side {
|
||||||
case .right: .init( 1, 0, 0)
|
case .right: .right
|
||||||
case .left: .init(-1, 0, 0)
|
case .left: .left
|
||||||
case .up: .init( 0, 1, 0)
|
case .up: .up
|
||||||
case .down: .init( 0, -1, 0)
|
case .down: .down
|
||||||
case .back: .init( 0, 0, 1)
|
case .back: .back
|
||||||
case .front: .init( 0, 0, -1)
|
case .front: .forward
|
||||||
}
|
}
|
||||||
return self &+ ofs
|
return self &+ ofs
|
||||||
}
|
}
|
||||||
|
16
Sources/Voxelotl/Renderer/Mesh.swift
Normal file
16
Sources/Voxelotl/Renderer/Mesh.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
public struct Mesh<VertexType: Vertex, IndexType: UnsignedInteger> {
|
||||||
|
public let vertices: [VertexType]
|
||||||
|
public let indices: [IndexType]
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Mesh {
|
||||||
|
static var empty: Self { .init(vertices: .init(), indices: .init()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol Vertex: Equatable {}
|
||||||
|
|
||||||
|
public struct VertexPositionNormalTexcoord: Vertex {
|
||||||
|
var position: SIMD3<Float>
|
||||||
|
var normal: SIMD3<Float>
|
||||||
|
var texCoord: SIMD2<Float>
|
||||||
|
}
|
@ -4,42 +4,6 @@ import QuartzCore.CAMetalLayer
|
|||||||
import simd
|
import simd
|
||||||
import ShaderTypes
|
import ShaderTypes
|
||||||
|
|
||||||
fileprivate let cubeVertices: [ShaderVertex] = [
|
|
||||||
.init(position: .init(-1, -1, 1), normal: .back, texCoord: .init(0, 0)),
|
|
||||||
.init(position: .init( 1, -1, 1), normal: .back, texCoord: .init(1, 0)),
|
|
||||||
.init(position: .init(-1, 1, 1), normal: .back, texCoord: .init(0, 1)),
|
|
||||||
.init(position: .init( 1, 1, 1), normal: .back, texCoord: .init(1, 1)),
|
|
||||||
.init(position: .init( 1, -1, 1), normal: .right, texCoord: .init(0, 0)),
|
|
||||||
.init(position: .init( 1, -1, -1), normal: .right, texCoord: .init(1, 0)),
|
|
||||||
.init(position: .init( 1, 1, 1), normal: .right, texCoord: .init(0, 1)),
|
|
||||||
.init(position: .init( 1, 1, -1), normal: .right, texCoord: .init(1, 1)),
|
|
||||||
.init(position: .init( 1, -1, -1), normal: .forward, texCoord: .init(0, 0)),
|
|
||||||
.init(position: .init(-1, -1, -1), normal: .forward, texCoord: .init(1, 0)),
|
|
||||||
.init(position: .init( 1, 1, -1), normal: .forward, texCoord: .init(0, 1)),
|
|
||||||
.init(position: .init(-1, 1, -1), normal: .forward, texCoord: .init(1, 1)),
|
|
||||||
.init(position: .init(-1, -1, -1), normal: .left, texCoord: .init(0, 0)),
|
|
||||||
.init(position: .init(-1, -1, 1), normal: .left, texCoord: .init(1, 0)),
|
|
||||||
.init(position: .init(-1, 1, -1), normal: .left, texCoord: .init(0, 1)),
|
|
||||||
.init(position: .init(-1, 1, 1), normal: .left, texCoord: .init(1, 1)),
|
|
||||||
.init(position: .init(-1, -1, -1), normal: .down, texCoord: .init(0, 0)),
|
|
||||||
.init(position: .init( 1, -1, -1), normal: .down, texCoord: .init(1, 0)),
|
|
||||||
.init(position: .init(-1, -1, 1), normal: .down, texCoord: .init(0, 1)),
|
|
||||||
.init(position: .init( 1, -1, 1), normal: .down, texCoord: .init(1, 1)),
|
|
||||||
.init(position: .init(-1, 1, 1), normal: .up, texCoord: .init(0, 0)),
|
|
||||||
.init(position: .init( 1, 1, 1), normal: .up, texCoord: .init(1, 0)),
|
|
||||||
.init(position: .init(-1, 1, -1), normal: .up, texCoord: .init(0, 1)),
|
|
||||||
.init(position: .init( 1, 1, -1), normal: .up, texCoord: .init(1, 1)),
|
|
||||||
]
|
|
||||||
|
|
||||||
fileprivate let cubeIndices: [UInt16] = [
|
|
||||||
0, 1, 2, 2, 1, 3,
|
|
||||||
4, 5, 6, 6, 5, 7,
|
|
||||||
8, 9, 10, 10, 9, 11,
|
|
||||||
12, 13, 14, 14, 13, 15,
|
|
||||||
16, 17, 18, 18, 17, 19,
|
|
||||||
20, 21, 22, 22, 21, 23
|
|
||||||
]
|
|
||||||
|
|
||||||
fileprivate let numFramesInFlight: Int = 3
|
fileprivate let numFramesInFlight: Int = 3
|
||||||
fileprivate let colorFormat: MTLPixelFormat = .bgra8Unorm_srgb
|
fileprivate let colorFormat: MTLPixelFormat = .bgra8Unorm_srgb
|
||||||
fileprivate let depthFormat: MTLPixelFormat = .depth32Float
|
fileprivate let depthFormat: MTLPixelFormat = .depth32Float
|
||||||
@ -61,7 +25,6 @@ public class Renderer {
|
|||||||
|
|
||||||
private var _encoder: MTLRenderCommandEncoder! = nil
|
private var _encoder: MTLRenderCommandEncoder! = nil
|
||||||
|
|
||||||
private var vtxBuffer: MTLBuffer, idxBuffer: MTLBuffer
|
|
||||||
private var defaultTexture: MTLTexture
|
private var defaultTexture: MTLTexture
|
||||||
private var cubeTexture: MTLTexture? = nil
|
private var cubeTexture: MTLTexture? = nil
|
||||||
|
|
||||||
@ -150,24 +113,6 @@ public class Renderer {
|
|||||||
throw RendererError.initFailure("Failed to create pipeline state: \(error.localizedDescription)")
|
throw RendererError.initFailure("Failed to create pipeline state: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create cube mesh buffers
|
|
||||||
guard let vtxBuffer = device.makeBuffer(
|
|
||||||
bytes: cubeVertices,
|
|
||||||
length: cubeVertices.count * MemoryLayout<ShaderVertex>.stride,
|
|
||||||
options: .storageModeManaged)
|
|
||||||
else {
|
|
||||||
throw RendererError.initFailure("Failed to create vertex buffer")
|
|
||||||
}
|
|
||||||
self.vtxBuffer = vtxBuffer
|
|
||||||
guard let idxBuffer = device.makeBuffer(
|
|
||||||
bytes: cubeIndices,
|
|
||||||
length: cubeIndices.count * MemoryLayout<UInt16>.stride,
|
|
||||||
options: .storageModeManaged)
|
|
||||||
else {
|
|
||||||
throw RendererError.initFailure("Failed to create index buffer")
|
|
||||||
}
|
|
||||||
self.idxBuffer = idxBuffer
|
|
||||||
|
|
||||||
// Create a default texture
|
// Create a default texture
|
||||||
do {
|
do {
|
||||||
self.defaultTexture = try Self.loadTexture(device, queue, image2D: Image2D(Data([
|
self.defaultTexture = try Self.loadTexture(device, queue, image2D: Image2D(Data([
|
||||||
@ -192,6 +137,29 @@ public class Renderer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createMesh(_ mesh: Mesh<VertexPositionNormalTexcoord, UInt16>) -> RendererMesh? {
|
||||||
|
let vertices = mesh.vertices.map {
|
||||||
|
ShaderVertex(position: $0.position, normal: $0.normal, texCoord: $0.texCoord)
|
||||||
|
}
|
||||||
|
guard let vtxBuffer = self.device.makeBuffer(
|
||||||
|
bytes: vertices,
|
||||||
|
length: vertices.count * MemoryLayout<ShaderVertex>.stride,
|
||||||
|
options: .storageModeManaged)
|
||||||
|
else {
|
||||||
|
printErr("Failed to create vertex buffer")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let idxBuffer = device.makeBuffer(
|
||||||
|
bytes: mesh.indices,
|
||||||
|
length: mesh.indices.count * MemoryLayout<UInt16>.stride,
|
||||||
|
options: .storageModeManaged)
|
||||||
|
else {
|
||||||
|
printErr("Failed to create index buffer")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return .init(_vertBuf: vtxBuffer, _idxBuf: idxBuffer, numIndices: mesh.indices.count)
|
||||||
|
}
|
||||||
|
|
||||||
static func loadTexture(_ device: MTLDevice, _ queue: MTLCommandQueue, resourcePath path: String) throws -> MTLTexture {
|
static func loadTexture(_ device: MTLDevice, _ queue: MTLCommandQueue, resourcePath path: String) throws -> MTLTexture {
|
||||||
do {
|
do {
|
||||||
return try loadTexture(device, queue, url: Bundle.main.getResource(path))
|
return try loadTexture(device, queue, url: Bundle.main.getResource(path))
|
||||||
@ -329,7 +297,6 @@ public class Renderer {
|
|||||||
encoder.setRenderPipelineState(pso)
|
encoder.setRenderPipelineState(pso)
|
||||||
encoder.setDepthStencilState(depthStencilState)
|
encoder.setDepthStencilState(depthStencilState)
|
||||||
encoder.setFragmentTexture(cubeTexture ?? defaultTexture, index: 0)
|
encoder.setFragmentTexture(cubeTexture ?? defaultTexture, index: 0)
|
||||||
encoder.setVertexBuffer(vtxBuffer, offset: 0, index: VertexShaderInputIdx.vertices.rawValue)
|
|
||||||
|
|
||||||
self._encoder = encoder
|
self._encoder = encoder
|
||||||
frameFunc(self)
|
frameFunc(self)
|
||||||
@ -346,7 +313,7 @@ public class Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func batch(instances: [Instance], material: Material, environment: Environment, camera: Camera) {
|
func batch(instances: [Instance], mesh: RendererMesh, material: Material, environment: Environment, camera: Camera) {
|
||||||
assert(self._encoder != nil, "batch can't be called outside of a frame being rendered")
|
assert(self._encoder != nil, "batch can't be called outside of a frame being rendered")
|
||||||
|
|
||||||
var vertUniforms = VertexShaderUniforms(projView: camera.viewProjection)
|
var vertUniforms = VertexShaderUniforms(projView: camera.viewProjection)
|
||||||
@ -391,6 +358,7 @@ public class Renderer {
|
|||||||
|
|
||||||
self._encoder.setCullMode(.init(environment.cullFace))
|
self._encoder.setCullMode(.init(environment.cullFace))
|
||||||
|
|
||||||
|
self._encoder.setVertexBuffer(mesh._vertBuf, offset: 0, index: VertexShaderInputIdx.vertices.rawValue)
|
||||||
self._encoder.setVertexBuffer(instanceBuffer,
|
self._encoder.setVertexBuffer(instanceBuffer,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
index: VertexShaderInputIdx.instance.rawValue)
|
index: VertexShaderInputIdx.instance.rawValue)
|
||||||
@ -404,14 +372,20 @@ public class Renderer {
|
|||||||
|
|
||||||
self._encoder.drawIndexedPrimitives(
|
self._encoder.drawIndexedPrimitives(
|
||||||
type: .triangle,
|
type: .triangle,
|
||||||
indexCount: cubeIndices.count,
|
indexCount: mesh.numIndices,
|
||||||
indexType: .uint16,
|
indexType: .uint16,
|
||||||
indexBuffer: idxBuffer,
|
indexBuffer: mesh._idxBuf,
|
||||||
indexBufferOffset: 0,
|
indexBufferOffset: 0,
|
||||||
instanceCount: numInstances)
|
instanceCount: numInstances)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct RendererMesh {
|
||||||
|
fileprivate let _vertBuf: MTLBuffer
|
||||||
|
fileprivate let _idxBuf: MTLBuffer
|
||||||
|
public let numIndices: Int
|
||||||
|
}
|
||||||
|
|
||||||
extension MTLClearColor {
|
extension MTLClearColor {
|
||||||
init(_ color: Color<Double>) {
|
init(_ color: Color<Double>) {
|
||||||
self.init(red: color.r, green: color.g, blue: color.b, alpha: color.a)
|
self.init(red: color.r, green: color.g, blue: color.b, alpha: color.a)
|
||||||
|
@ -19,6 +19,16 @@ public class World {
|
|||||||
self._chunks[position &>> Chunk.shift]?.setBlock(at: position, type: type)
|
self._chunks[position &>> Chunk.shift]?.setBlock(at: position, type: type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getChunk(id chunkID: SIMD3<Int>) -> Chunk? {
|
||||||
|
self._chunks[chunkID]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func forEachChunk(_ body: @escaping (_ id: SIMD3<Int>, _ chunk: Chunk) throws -> Void) rethrows {
|
||||||
|
for i in self._chunks {
|
||||||
|
try body(i.key, i.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func generate(width: Int, height: Int, depth: Int, seed: UInt64) {
|
func generate(width: Int, height: Int, depth: Int, seed: UInt64) {
|
||||||
self._generator.reset(seed: seed)
|
self._generator.reset(seed: seed)
|
||||||
let orig = SIMD3(width, height, depth) / 2
|
let orig = SIMD3(width, height, depth) / 2
|
||||||
|
Loading…
Reference in New Issue
Block a user