diff --git a/Sources/Voxelotl/Application.swift b/Sources/Voxelotl/Application.swift index c02618f..1288ed9 100644 --- a/Sources/Voxelotl/Application.swift +++ b/Sources/Voxelotl/Application.swift @@ -4,7 +4,7 @@ import QuartzCore.CAMetalLayer public class Application { private let cfg: ApplicationConfiguration - private let del: GameDelegate + private var del: GameDelegate! private var window: OpaquePointer? = nil private var view: SDL_MetalView? = nil @@ -71,7 +71,8 @@ public class Application { } private func deinitialize() { - renderer = nil + self.del = nil + self.renderer = nil SDL_Metal_DestroyView(view) SDL_DestroyWindow(window) SDL_Quit() diff --git a/Sources/Voxelotl/CMakeLists.txt b/Sources/Voxelotl/CMakeLists.txt index 1489701..daeafa8 100644 --- a/Sources/Voxelotl/CMakeLists.txt +++ b/Sources/Voxelotl/CMakeLists.txt @@ -45,6 +45,7 @@ add_executable(Voxelotl MACOSX_BUNDLE Renderer/Material.swift Renderer/Environment.swift Renderer/Mesh.swift + Renderer/ModelBatch.swift Renderer/Renderer.swift # Input wrappers diff --git a/Sources/Voxelotl/Game.swift b/Sources/Voxelotl/Game.swift index 0cec992..7c599d6 100644 --- a/Sources/Voxelotl/Game.swift +++ b/Sources/Voxelotl/Game.swift @@ -1,24 +1,5 @@ import simd -struct Instance { - let position: SIMD3 - let scale: SIMD3 - let rotation: simd_quatf - let color: Color - - init( - position: SIMD3 = .zero, - scale: SIMD3 = .one, - rotation: simd_quatf = .identity, - color: Color = .white - ) { - self.position = position - self.scale = scale - self.rotation = rotation - self.color = color - } -} - class Game: GameDelegate { private var fpsCalculator = FPSCalculator() var camera = Camera(fov: 60, size: .one, range: 0.06...900) @@ -28,6 +9,7 @@ class Game: GameDelegate { var cubeMesh: RendererMesh? var renderChunks = [SIMD3: RendererMesh]() var chunkMeshGeneration: ChunkMeshGeneration! + var modelBatch: ModelBatch! func create(_ renderer: Renderer) { self.resetPlayer() @@ -39,6 +21,7 @@ class Game: GameDelegate { self.chunkMeshGeneration = .init(queue: .global(qos: .userInitiated)) self.chunkMeshGeneration.game = self self.chunkMeshGeneration.renderer = renderer + self.modelBatch = renderer.createModelBatch() } private func resetPlayer() { @@ -118,32 +101,24 @@ class Game: GameDelegate { } self.chunkMeshGeneration.acceptReadyMeshes() + self.modelBatch.begin(camera: camera, environment: env) + for (id, chunk) in self.renderChunks { let drawPos = SIMD3(id &<< Chunk.shift) - renderer.draw( - model: .translate(drawPos), - color: .white, - mesh: chunk, - material: material, - environment: env, - camera: self.camera) + self.modelBatch.draw(.init(mesh: chunk, material: material), position: drawPos) } - var instances = [Instance]() if let position = player.rayhitPos { - instances.append( - Instance( - position: position, - scale: .init(repeating: 0.0725 * 0.5), - rotation: - .init(angle: totalTime * 3.0, axis: .init(0, 1, 0)) * - .init(angle: totalTime * 1.5, axis: .init(1, 0, 0)) * - .init(angle: totalTime * 0.7, axis: .init(0, 0, 1)), - color: .init(r: 0.5, g: 0.5, b: 1).linear)) - } - if self.cubeMesh != nil && !instances.isEmpty { - renderer.batch(instances: instances, mesh: self.cubeMesh!, material: material, environment: env, camera: self.camera) + let rotation: simd_quatf = + .init(angle: totalTime * 3.0, axis: .Y) * + .init(angle: totalTime * 1.5, axis: .X) * + .init(angle: totalTime * 0.7, axis: .Z) + self.modelBatch.draw(.init(mesh: self.cubeMesh!, material: material), + position: position, scale: 0.0725 * 0.5, rotation: rotation, + color: .init(r: 0.5, g: 0.5, b: 1)) } + + self.modelBatch.end() } func resize(_ size: Size) { diff --git a/Sources/Voxelotl/Renderer/ModelBatch.swift b/Sources/Voxelotl/Renderer/ModelBatch.swift new file mode 100644 index 0000000..d12df11 --- /dev/null +++ b/Sources/Voxelotl/Renderer/ModelBatch.swift @@ -0,0 +1,73 @@ +import simd + +public struct ModelBatch { + private let _renderer: Renderer + private var _active = false + private var _cam: Camera! + private var _env: Environment! + + internal init(_ renderer: Renderer) { + self._renderer = renderer + } + + //TODO: Sort, Blend + mutating func begin(camera: Camera, environment: Environment) { + self._active = true + self._cam = camera + self._env = environment + } + + mutating func end() { + self._active = false + } + + mutating func draw(_ model: ModelInstance, position: SIMD3, color: Color = .white + ) { + self.draw(model, world: .translate(position), color: color) + } + + mutating func draw(_ model: ModelInstance, + position: SIMD3, scale: Float, rotation: simd_quatf, + color: Color = .white + ) { + self.draw(model, position: position, scale: .init(repeating: scale), rotation: rotation, color: color) + } + + mutating func draw(_ model: ModelInstance, + position: SIMD3, scale: SIMD3, rotation: simd_quatf, + color: Color = .white + ) { + let world = + .translate(position) * + simd_float4x4(rotation) * + .scale(scale) + self.draw(model, world: world, color: color) + } + + mutating func draw(_ model: ModelInstance, world: simd_float4x4, color: Color = .white) { + assert(self._active) + self._renderer.draw( + model: world, + color: color.linear, + mesh: model.mesh, + material: model.material, + environment: self._env, + camera: self._cam) + } + + internal struct Instance { + let model: simd_float4x4 + let color: Color + + init(model: simd_float4x4, color: Color = .white) { + self.model = model + self.color = color + } + } +} + +//TODO: delet +public struct ModelInstance { + let mesh: RendererMesh + let material: Material +} diff --git a/Sources/Voxelotl/Renderer/Renderer.swift b/Sources/Voxelotl/Renderer/Renderer.swift index b3da10c..c88db76 100644 --- a/Sources/Voxelotl/Renderer/Renderer.swift +++ b/Sources/Voxelotl/Renderer/Renderer.swift @@ -415,7 +415,11 @@ public class Renderer { indexBufferOffset: 0) } - func batch(instances: [Instance], mesh: RendererMesh, material: Material, environment: Environment, camera: Camera) { + func createModelBatch() -> ModelBatch { + return ModelBatch(self) + } + + func batch(instances: [ModelBatch.Instance], mesh: RendererMesh, material: Material, environment: Environment, camera: Camera) { assert(self._encoder != nil, "batch can't be called outside of a frame being rendered") var vertUniforms = VertexShaderUniforms(projView: camera.viewProjection) @@ -447,12 +451,9 @@ public class Renderer { instanceBuffer.contents().withMemoryRebound(to: VertexShaderInstance.self, capacity: numInstances) { data in for i in 0.. +# define CONSTANT_PTR(TYPE) uint64_t #endif #include