mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 21:21:34 +00:00
refactor chunk rendering and fix chunk rendering precision loss at large positions
This commit is contained in:
@ -46,6 +46,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
|
|||||||
Renderer/Environment.swift
|
Renderer/Environment.swift
|
||||||
Renderer/Mesh.swift
|
Renderer/Mesh.swift
|
||||||
Renderer/ModelBatch.swift
|
Renderer/ModelBatch.swift
|
||||||
|
Renderer/ChunkRenderer.swift
|
||||||
Renderer/Renderer.swift
|
Renderer/Renderer.swift
|
||||||
|
|
||||||
# Input wrappers
|
# Input wrappers
|
||||||
|
@ -129,6 +129,20 @@ public final class Camera {
|
|||||||
self._dirty = [ .projection, .view, .viewProj ]
|
self._dirty = [ .projection, .view, .viewProj ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: maybe make this a struct instead?
|
||||||
|
convenience init(_ copy: Camera) {
|
||||||
|
self.init(fov: copy._fieldOfView, size: copy.size, range: copy._zNearFar)
|
||||||
|
self._position = copy._position
|
||||||
|
self._rotation = copy._rotation
|
||||||
|
self._aspectRatio = copy._aspectRatio
|
||||||
|
self._viewport = copy._viewport
|
||||||
|
self._dirty = copy._dirty
|
||||||
|
self._projection = copy._projection
|
||||||
|
self._view = copy._view
|
||||||
|
self._viewProjection = copy._viewProjection
|
||||||
|
self._invViewProjection = copy._invViewProjection
|
||||||
|
}
|
||||||
|
|
||||||
public func screenRay(_ screen: SIMD2<Float>) -> SIMD3<Float> {
|
public func screenRay(_ screen: SIMD2<Float>) -> SIMD3<Float> {
|
||||||
#if true
|
#if true
|
||||||
simd_normalize(self.unproject(screen: SIMD3(screen, 1)) - self.unproject(screen: SIMD3(screen, 0)))
|
simd_normalize(self.unproject(screen: SIMD3(screen, 1)) - self.unproject(screen: SIMD3(screen, 0)))
|
||||||
|
@ -4,10 +4,12 @@ public struct ChunkMeshGeneration {
|
|||||||
private let queue: OperationQueue
|
private let queue: OperationQueue
|
||||||
private let localReadyMeshes = ConcurrentDictionary<ChunkID, RendererMesh?>()
|
private let localReadyMeshes = ConcurrentDictionary<ChunkID, RendererMesh?>()
|
||||||
|
|
||||||
weak var game: Game?
|
private weak var _world: World?
|
||||||
weak var renderer: Renderer?
|
private weak var _renderer: Renderer?
|
||||||
|
|
||||||
init(queue: DispatchQueue) {
|
init(world: World, renderer: Renderer, queue: DispatchQueue) {
|
||||||
|
self._world = world
|
||||||
|
self._renderer = renderer
|
||||||
self.queue = OperationQueue()
|
self.queue = OperationQueue()
|
||||||
self.queue.underlyingQueue = queue
|
self.queue.underlyingQueue = queue
|
||||||
self.queue.maxConcurrentOperationCount = 8
|
self.queue.maxConcurrentOperationCount = 8
|
||||||
@ -20,28 +22,19 @@ public struct ChunkMeshGeneration {
|
|||||||
|
|
||||||
func queueGenerateJob(id chunkID: ChunkID, chunk: Chunk) {
|
func queueGenerateJob(id chunkID: ChunkID, chunk: Chunk) {
|
||||||
self.queue.addOperation {
|
self.queue.addOperation {
|
||||||
guard let game = self.game else {
|
let mesh = ChunkMeshBuilder.build(world: self._world!, chunk: chunk)
|
||||||
return
|
self.localReadyMeshes[chunkID] = self._renderer!.createMesh(mesh)
|
||||||
}
|
|
||||||
|
|
||||||
guard let renderer = self.renderer else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let mesh = ChunkMeshBuilder.build(world: game.world, chunk: chunk)
|
|
||||||
self.localReadyMeshes[chunkID] = renderer.createMesh(mesh)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func acceptReadyMeshes() {
|
public mutating func acceptReadyMeshes(_ chunkRenderer: inout ChunkRenderer) {
|
||||||
guard let game = self.game else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
queue.waitUntilAllOperationsAreFinished()
|
queue.waitUntilAllOperationsAreFinished()
|
||||||
|
|
||||||
for (chunkID, mesh) in self.localReadyMeshes.take() {
|
for (chunkID, mesh) in self.localReadyMeshes.take() {
|
||||||
game.renderChunks.updateValue(mesh, forKey: chunkID)
|
if let mesh = mesh {
|
||||||
|
chunkRenderer.addChunk(id: chunkID, mesh: mesh)
|
||||||
|
} else {
|
||||||
|
chunkRenderer.removeChunk(id: chunkID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,18 @@ class Game: GameDelegate {
|
|||||||
var projection: matrix_float4x4 = .identity
|
var projection: matrix_float4x4 = .identity
|
||||||
var world = World(generator: StandardWorldGenerator())
|
var world = World(generator: StandardWorldGenerator())
|
||||||
var cubeMesh: RendererMesh?
|
var cubeMesh: RendererMesh?
|
||||||
var renderChunks = [ChunkID: RendererMesh?]()
|
|
||||||
var chunkMeshGeneration: ChunkMeshGeneration!
|
var chunkMeshGeneration: ChunkMeshGeneration!
|
||||||
|
var chunkRenderer: ChunkRenderer!
|
||||||
var modelBatch: ModelBatch!
|
var modelBatch: ModelBatch!
|
||||||
|
|
||||||
func create(_ renderer: Renderer) {
|
func create(_ renderer: Renderer) {
|
||||||
|
self.chunkRenderer = ChunkRenderer(renderer: renderer)
|
||||||
|
self.chunkRenderer.material = .init(
|
||||||
|
ambient: Color(rgba8888: 0x4F4F4F00).linear,
|
||||||
|
diffuse: Color(rgba8888: 0xDFDFDF00).linear,
|
||||||
|
specular: Color(rgba8888: 0x2F2F2F00).linear,
|
||||||
|
gloss: 75)
|
||||||
|
|
||||||
self.resetPlayer()
|
self.resetPlayer()
|
||||||
self.generateWorld()
|
self.generateWorld()
|
||||||
self.world.waitForActiveOperations()
|
self.world.waitForActiveOperations()
|
||||||
@ -19,9 +26,9 @@ class Game: GameDelegate {
|
|||||||
self.cubeMesh = renderer.createMesh(CubeMeshBuilder.build(bound: .fromUnitCube(position: .zero, scale: .one)))
|
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
|
||||||
self.chunkMeshGeneration = .init(queue: .global(qos: .userInitiated))
|
self.chunkMeshGeneration = .init(
|
||||||
self.chunkMeshGeneration.game = self
|
world: world, renderer: renderer,
|
||||||
self.chunkMeshGeneration.renderer = renderer
|
queue: .global(qos: .userInitiated))
|
||||||
self.modelBatch = renderer.createModelBatch()
|
self.modelBatch = renderer.createModelBatch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +40,7 @@ class Game: GameDelegate {
|
|||||||
|
|
||||||
private func generateWorld() {
|
private func generateWorld() {
|
||||||
self.world.removeAllChunks()
|
self.world.removeAllChunks()
|
||||||
self.renderChunks.removeAll()
|
self.chunkRenderer.removeAll()
|
||||||
let seed = UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32
|
let seed = UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32
|
||||||
printErr(seed)
|
printErr(seed)
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@ -86,12 +93,6 @@ class Game: GameDelegate {
|
|||||||
self.world.update()
|
self.world.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static let material = Material(
|
|
||||||
ambient: Color(rgba8888: 0x4F4F4F00).linear,
|
|
||||||
diffuse: Color(rgba8888: 0xDFDFDF00).linear,
|
|
||||||
specular: Color(rgba8888: 0x2F2F2F00).linear,
|
|
||||||
gloss: 75)
|
|
||||||
|
|
||||||
func draw(_ renderer: Renderer, _ time: GameTime) {
|
func draw(_ renderer: Renderer, _ time: GameTime) {
|
||||||
let totalTime = Float(time.total.asFloat)
|
let totalTime = Float(time.total.asFloat)
|
||||||
|
|
||||||
@ -103,24 +104,21 @@ class Game: GameDelegate {
|
|||||||
self.world.handleRenderDamagedChunks { id, chunk in
|
self.world.handleRenderDamagedChunks { id, chunk in
|
||||||
self.chunkMeshGeneration.generate(id: id, chunk: chunk)
|
self.chunkMeshGeneration.generate(id: id, chunk: chunk)
|
||||||
}
|
}
|
||||||
self.chunkMeshGeneration.acceptReadyMeshes()
|
self.chunkMeshGeneration.acceptReadyMeshes(&self.chunkRenderer)
|
||||||
|
|
||||||
|
self.chunkRenderer.draw(environment: env, camera: self.camera)
|
||||||
|
|
||||||
self.modelBatch.begin(camera: camera, environment: env)
|
self.modelBatch.begin(camera: camera, environment: env)
|
||||||
|
|
||||||
for (id, chunk) in self.renderChunks {
|
|
||||||
if chunk == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
let drawPos = id.getFloatPosition()
|
|
||||||
self.modelBatch.draw(.init(mesh: chunk!, material: Self.material), position: drawPos)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let position = player.rayhitPos {
|
if let position = player.rayhitPos {
|
||||||
let rotation: simd_quatf =
|
let rotation: simd_quatf =
|
||||||
.init(angle: totalTime * 3.0, axis: .Y) *
|
.init(angle: totalTime * 3.0, axis: .Y) *
|
||||||
.init(angle: totalTime * 1.5, axis: .X) *
|
.init(angle: totalTime * 1.5, axis: .X) *
|
||||||
.init(angle: totalTime * 0.7, axis: .Z)
|
.init(angle: totalTime * 0.7, axis: .Z)
|
||||||
self.modelBatch.draw(.init(mesh: self.cubeMesh!, material: Self.material),
|
self.modelBatch.draw(.init(mesh: self.cubeMesh!, material: .init(
|
||||||
|
ambient: .black.mix(.green, 0.65).linear,
|
||||||
|
diffuse: .white.mix(.black, 0.20).linear,
|
||||||
|
specular: .magenta.linear,
|
||||||
|
gloss: 250)),
|
||||||
position: position, scale: 0.0725 * 0.5, rotation: rotation,
|
position: position, scale: 0.0725 * 0.5, rotation: rotation,
|
||||||
color: .init(r: 0.5, g: 0.5, b: 1))
|
color: .init(r: 0.5, g: 0.5, b: 1))
|
||||||
}
|
}
|
||||||
|
@ -76,8 +76,9 @@ struct Player {
|
|||||||
|
|
||||||
func checkCollisionRaycast(_ world: World, _ position: SIMD3<Float>, top: Bool) -> Optional<RaycastHit> {
|
func checkCollisionRaycast(_ world: World, _ position: SIMD3<Float>, top: Bool) -> Optional<RaycastHit> {
|
||||||
let dir: SIMD3<Float> = !top ? .down : .up
|
let dir: SIMD3<Float> = !top ? .down : .up
|
||||||
var org = !top ? self._position + .up * Self.height : self._position
|
let hHeight = Self.height * 0.5
|
||||||
let max: Float = Self.height + Self.epsilon * 4
|
var org = self._position + .up * hHeight
|
||||||
|
let max: Float = hHeight + Self.epsilon * 4
|
||||||
|
|
||||||
org.x -= Self.radius
|
org.x -= Self.radius
|
||||||
org.z -= Self.radius
|
org.z -= Self.radius
|
||||||
@ -92,6 +93,7 @@ struct Player {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testPos: SIMD3<Float>
|
||||||
#if false
|
#if false
|
||||||
self._position.y = newPosition.y
|
self._position.y = newPosition.y
|
||||||
if self._velocity.y <= 0, let hit = checkCollisionRaycast(world, self._position, top: false)
|
if self._velocity.y <= 0, let hit = checkCollisionRaycast(world, self._position, top: false)
|
||||||
@ -109,7 +111,7 @@ struct Player {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
self._position.y = newPosition.y
|
self._position.y = newPosition.y
|
||||||
var testPos = self._position
|
testPos = self._position
|
||||||
if self._velocity.y > 0 { testPos.y -= Self.epsilon }
|
if self._velocity.y > 0 { testPos.y -= Self.epsilon }
|
||||||
if let aabb = checkCollision(world, testPos) {
|
if let aabb = checkCollision(world, testPos) {
|
||||||
if self._velocity.y <= 0 {
|
if self._velocity.y <= 0 {
|
||||||
@ -246,6 +248,10 @@ struct Player {
|
|||||||
jumpInput = .held
|
jumpInput = .held
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if Keyboard.pressed(.leftBracket, repeat: true) {
|
||||||
|
self._position *= 2
|
||||||
|
}
|
||||||
|
|
||||||
// Read mouse input
|
// Read mouse input
|
||||||
if Mouse.pressed(.left) { destroy = true }
|
if Mouse.pressed(.left) { destroy = true }
|
||||||
if Mouse.pressed(.right) { place = true }
|
if Mouse.pressed(.right) { place = true }
|
||||||
@ -298,15 +304,19 @@ struct Player {
|
|||||||
// Flying and unflying
|
// Flying and unflying
|
||||||
self._velocity.y += Float(flying).clamp(-1, 1) * Self.flySpeedCoeff * deltaTime
|
self._velocity.y += Float(flying).clamp(-1, 1) * Self.flySpeedCoeff * deltaTime
|
||||||
// Apply physics
|
// Apply physics
|
||||||
if self._onGround {
|
let iterations = 1
|
||||||
self.moveGround(deltaTime, world, moveDir: movement)
|
let iterDT = deltaTime / Float(iterations)
|
||||||
} else {
|
for _ in 0..<iterations {
|
||||||
self.moveAir(deltaTime, world, moveDir: movement)
|
if self._onGround {
|
||||||
}
|
self.moveGround(iterDT, world, moveDir: movement)
|
||||||
// Limit maximum velocity
|
} else {
|
||||||
let velocityLen = simd_length(self._velocity)
|
self.moveAir(iterDT, world, moveDir: movement)
|
||||||
if velocityLen > Self.maxVelocity {
|
}
|
||||||
self._velocity = self._velocity / velocityLen * Self.maxVelocity
|
// Limit maximum velocity
|
||||||
|
let velocityLen = simd_length(self._velocity)
|
||||||
|
if velocityLen > Self.maxVelocity {
|
||||||
|
self._velocity = self._velocity / velocityLen * Self.maxVelocity
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jumping
|
// Jumping
|
||||||
|
42
Sources/Voxelotl/Renderer/ChunkRenderer.swift
Normal file
42
Sources/Voxelotl/Renderer/ChunkRenderer.swift
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import simd
|
||||||
|
|
||||||
|
public struct ChunkRenderer {
|
||||||
|
private weak var _renderer: Renderer?
|
||||||
|
private var _renderChunks = [ChunkID: RendererMesh]()
|
||||||
|
|
||||||
|
public var material: Material
|
||||||
|
|
||||||
|
public init(renderer: Renderer) {
|
||||||
|
self._renderer = renderer
|
||||||
|
self.material = .init(ambient: .black, diffuse: .white, specular: .white, gloss: 20.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func draw(environment: Environment, camera globalCamera: Camera) {
|
||||||
|
let fChunkSz = Float(Chunk.size), divisor = 1 / fChunkSz
|
||||||
|
let origin = SIMD3<Int>(floor(globalCamera.position * divisor), rounding: .down)
|
||||||
|
|
||||||
|
let localCamera = Camera(globalCamera)
|
||||||
|
localCamera.position = globalCamera.position - SIMD3<Float>(origin) * fChunkSz
|
||||||
|
|
||||||
|
self._renderer!.setupBatch(environment: environment, camera: localCamera)
|
||||||
|
for (chunkID, mesh) in self._renderChunks {
|
||||||
|
let drawPos = SIMD3<Float>(SIMD3<Int>(chunkID) &- origin) * fChunkSz
|
||||||
|
self._renderer!.submit(
|
||||||
|
mesh: mesh,
|
||||||
|
instance: .init(world: .translate(drawPos)),
|
||||||
|
material: self.material)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func addChunk(id chunkID: ChunkID, mesh: RendererMesh) {
|
||||||
|
self._renderChunks.updateValue(mesh, forKey: chunkID)
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func removeChunk(id chunkID: ChunkID) {
|
||||||
|
self._renderChunks.removeValue(forKey: chunkID)
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func removeAll() {
|
||||||
|
self._renderChunks.removeAll()
|
||||||
|
}
|
||||||
|
}
|
@ -18,12 +18,12 @@ public struct ModelBatch {
|
|||||||
self._cam = camera
|
self._cam = camera
|
||||||
self._env = environment
|
self._env = environment
|
||||||
self._prev = nil
|
self._prev = nil
|
||||||
self._renderer.setupBatch(material: Game.material, environment: environment, camera: camera)
|
self._renderer.setupBatch(environment: environment, camera: camera)
|
||||||
}
|
}
|
||||||
|
|
||||||
private mutating func flush() {
|
private mutating func flush() {
|
||||||
assert(self._instances.count > 0)
|
assert(self._instances.count > 0)
|
||||||
self._renderer.submitBatch(mesh: self._prev.mesh, instances: self._instances)
|
self._renderer.submitBatch(mesh: self._prev.mesh, instances: self._instances, material: self._prev.material)
|
||||||
self._instances.removeAll(keepingCapacity: true)
|
self._instances.removeAll(keepingCapacity: true)
|
||||||
self._prev = nil
|
self._prev = nil
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ public class Renderer {
|
|||||||
|
|
||||||
private var depthTextures: [MTLTexture]
|
private var depthTextures: [MTLTexture]
|
||||||
//private var _instances: [MTLBuffer?]
|
//private var _instances: [MTLBuffer?]
|
||||||
|
private var _cameraPos: SIMD3<Float> = .zero, _directionalDir: SIMD3<Float> = .zero
|
||||||
|
|
||||||
private var _encoder: MTLRenderCommandEncoder! = nil
|
private var _encoder: MTLRenderCommandEncoder! = nil
|
||||||
|
|
||||||
@ -381,17 +382,13 @@ public class Renderer {
|
|||||||
return ModelBatch(self)
|
return ModelBatch(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func setupBatch(material: Material, environment: Environment, camera: Camera) {
|
internal func setupBatch(environment: Environment, camera: Camera) {
|
||||||
assert(self._encoder != nil, "startBatch can't be called outside of a frame being rendered")
|
assert(self._encoder != nil, "startBatch can't be called outside of a frame being rendered")
|
||||||
|
|
||||||
var vertUniforms = VertexShaderUniforms(projView: camera.viewProjection)
|
var vertUniforms = VertexShaderUniforms(projView: camera.viewProjection)
|
||||||
var fragUniforms = FragmentShaderUniforms(
|
|
||||||
cameraPosition: camera.position,
|
self._cameraPos = camera.position
|
||||||
directionalLight: normalize(environment.lightDirection),
|
self._directionalDir = simd_normalize(environment.lightDirection)
|
||||||
ambientColor: SIMD4(material.ambient),
|
|
||||||
diffuseColor: SIMD4(material.diffuse),
|
|
||||||
specularColor: SIMD4(material.specular),
|
|
||||||
specularIntensity: material.gloss)
|
|
||||||
|
|
||||||
self._encoder.setCullMode(.init(environment.cullFace))
|
self._encoder.setCullMode(.init(environment.cullFace))
|
||||||
|
|
||||||
@ -399,12 +396,40 @@ public class Renderer {
|
|||||||
self._encoder.setVertexBytes(&vertUniforms,
|
self._encoder.setVertexBytes(&vertUniforms,
|
||||||
length: MemoryLayout<VertexShaderUniforms>.stride,
|
length: MemoryLayout<VertexShaderUniforms>.stride,
|
||||||
index: VertexShaderInputIdx.uniforms.rawValue)
|
index: VertexShaderInputIdx.uniforms.rawValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func submit(mesh: RendererMesh, instance: ModelBatch.Instance, material: Material) {
|
||||||
|
assert(self._encoder != nil, "submit can't be called outside of a frame being rendered")
|
||||||
|
var instanceData = VertexShaderInstance(
|
||||||
|
model: instance.world,
|
||||||
|
normalModel: instance.world.inverse.transpose,
|
||||||
|
color: SIMD4(instance.color))
|
||||||
|
var fragUniforms = FragmentShaderUniforms(
|
||||||
|
cameraPosition: self._cameraPos,
|
||||||
|
directionalLight: self._directionalDir,
|
||||||
|
ambientColor: SIMD4(material.ambient),
|
||||||
|
diffuseColor: SIMD4(material.diffuse),
|
||||||
|
specularColor: SIMD4(material.specular),
|
||||||
|
specularIntensity: material.gloss)
|
||||||
|
|
||||||
|
self._encoder.setVertexBuffer(mesh._vertBuf, offset: 0, index: VertexShaderInputIdx.vertices.rawValue)
|
||||||
|
// Ideal as long as our uniforms total 4 KB or less
|
||||||
|
self._encoder.setVertexBytes(&instanceData,
|
||||||
|
length: MemoryLayout<VertexShaderInstance>.stride,
|
||||||
|
index: VertexShaderInputIdx.instance.rawValue)
|
||||||
self._encoder.setFragmentBytes(&fragUniforms,
|
self._encoder.setFragmentBytes(&fragUniforms,
|
||||||
length: MemoryLayout<FragmentShaderUniforms>.stride,
|
length: MemoryLayout<FragmentShaderUniforms>.stride,
|
||||||
index: FragmentShaderInputIdx.uniforms.rawValue)
|
index: FragmentShaderInputIdx.uniforms.rawValue)
|
||||||
|
|
||||||
|
self._encoder.drawIndexedPrimitives(
|
||||||
|
type: .triangle,
|
||||||
|
indexCount: mesh.numIndices,
|
||||||
|
indexType: .uint16,
|
||||||
|
indexBuffer: mesh._idxBuf,
|
||||||
|
indexBufferOffset: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func submitBatch(mesh: RendererMesh, instances: [ModelBatch.Instance]) {
|
internal func submitBatch(mesh: RendererMesh, instances: [ModelBatch.Instance], material: Material) {
|
||||||
assert(self._encoder != nil, "submitBatch can't be called outside of a frame being rendered")
|
assert(self._encoder != nil, "submitBatch can't be called outside of a frame being rendered")
|
||||||
let numInstances = instances.count
|
let numInstances = instances.count
|
||||||
assert(numInstances > 0, "submitBatch called with zero instances")
|
assert(numInstances > 0, "submitBatch called with zero instances")
|
||||||
@ -451,12 +476,22 @@ public class Renderer {
|
|||||||
normalModel: instance.world.inverse.transpose,
|
normalModel: instance.world.inverse.transpose,
|
||||||
color: SIMD4(instance.color))
|
color: SIMD4(instance.color))
|
||||||
}
|
}
|
||||||
|
var fragUniforms = FragmentShaderUniforms(
|
||||||
|
cameraPosition: self._cameraPos,
|
||||||
|
directionalLight: self._directionalDir,
|
||||||
|
ambientColor: SIMD4(material.ambient),
|
||||||
|
diffuseColor: SIMD4(material.diffuse),
|
||||||
|
specularColor: SIMD4(material.specular),
|
||||||
|
specularIntensity: material.gloss)
|
||||||
|
|
||||||
self._encoder.setVertexBuffer(mesh._vertBuf, offset: 0, index: VertexShaderInputIdx.vertices.rawValue)
|
self._encoder.setVertexBuffer(mesh._vertBuf, offset: 0, index: VertexShaderInputIdx.vertices.rawValue)
|
||||||
// Ideal as long as our uniforms total 4 KB or less
|
// Ideal as long as our uniforms total 4 KB or less
|
||||||
self._encoder.setVertexBytes(instanceData,
|
self._encoder.setVertexBytes(instanceData,
|
||||||
length: numInstances * MemoryLayout<VertexShaderInstance>.stride,
|
length: numInstances * MemoryLayout<VertexShaderInstance>.stride,
|
||||||
index: VertexShaderInputIdx.instance.rawValue)
|
index: VertexShaderInputIdx.instance.rawValue)
|
||||||
|
self._encoder.setFragmentBytes(&fragUniforms,
|
||||||
|
length: MemoryLayout<FragmentShaderUniforms>.stride,
|
||||||
|
index: FragmentShaderInputIdx.uniforms.rawValue)
|
||||||
|
|
||||||
self._encoder.drawIndexedPrimitives(
|
self._encoder.drawIndexedPrimitives(
|
||||||
type: .triangle,
|
type: .triangle,
|
||||||
|
Reference in New Issue
Block a user