Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DEVLOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Devlog

#### 2026-01-23

<p align="center">
<img src="./Progress/2026-01-23.jpg" width="100%"/>
</p>

#### 2025-04-06

<p align="center">
Expand Down
12 changes: 12 additions & 0 deletions Engine.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@
56F0FB46284986570004D212 /* PNTextureAtlas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F0FB45284986570004D212 /* PNTextureAtlas.swift */; };
56F59269273725FB008652C9 /* PNDirectionalJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F59268273725FB008652C9 /* PNDirectionalJob.swift */; };
56F5926D2737276D008652C9 /* Directional.metal in Sources */ = {isa = PBXBuildFile; fileRef = 56F5926C2737276D008652C9 /* Directional.metal */; };
56F61F412F240E7A00920DDB /* BoundingBox.metal in Sources */ = {isa = PBXBuildFile; fileRef = 56F61F402F240E3600920DDB /* BoundingBox.metal */; };
56F7570B2F24302900F5B906 /* PNBoundingBoxCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F7570A2F24302500F5B906 /* PNBoundingBoxCreator.swift */; };
56F7570D2F24343200F5B906 /* PNBoundingBoxJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F7570C2F24343100F5B906 /* PNBoundingBoxJob.swift */; };
56FA634228AD161900D857A6 /* PNNode+Alias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FA634128AD161900D857A6 /* PNNode+Alias.swift */; };
56FA634528AD1E8200D857A6 /* PNWeakRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FA634428AD1E8200D857A6 /* PNWeakRef.swift */; };
56FA6EE128C6425200803314 /* Math+Global.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FA6EE028C6425200803314 /* Math+Global.swift */; };
Expand Down Expand Up @@ -713,6 +716,9 @@
56F5926727372526008652C9 /* DirectionalLight.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DirectionalLight.h; sourceTree = "<group>"; };
56F59268273725FB008652C9 /* PNDirectionalJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNDirectionalJob.swift; sourceTree = "<group>"; };
56F5926C2737276D008652C9 /* Directional.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Directional.metal; sourceTree = "<group>"; };
56F61F402F240E3600920DDB /* BoundingBox.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = BoundingBox.metal; sourceTree = "<group>"; };
56F7570A2F24302500F5B906 /* PNBoundingBoxCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNBoundingBoxCreator.swift; sourceTree = "<group>"; };
56F7570C2F24343100F5B906 /* PNBoundingBoxJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNBoundingBoxJob.swift; sourceTree = "<group>"; };
56FA634128AD161900D857A6 /* PNNode+Alias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PNNode+Alias.swift"; sourceTree = "<group>"; };
56FA634428AD1E8200D857A6 /* PNWeakRef.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNWeakRef.swift; sourceTree = "<group>"; };
56FA6EE028C6425200803314 /* Math+Global.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Math+Global.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -982,6 +988,7 @@
561666C62719E2CC000063B1 /* Mesh */ = {
isa = PBXGroup;
children = (
56F7570A2F24302500F5B906 /* PNBoundingBoxCreator.swift */,
568807442747EEB300C95F55 /* Skeleton */,
5617F2EC2942B1B70068177C /* Submesh */,
561666C72719E2CC000063B1 /* PNMesh.swift */,
Expand Down Expand Up @@ -1009,6 +1016,7 @@
561666D52719E2CC000063B1 /* Shaders */ = {
isa = PBXGroup;
children = (
56F61F402F240E3600920DDB /* BoundingBox.metal */,
05A51DA12DA288BF00D7FA95 /* Postprocess */,
563C9C22273882EB0014CFF3 /* Shadows */,
5670EC012735A3BB00375194 /* Bloom */,
Expand Down Expand Up @@ -1647,6 +1655,7 @@
5670EC1E2736C1A100375194 /* Stages */ = {
isa = PBXGroup;
children = (
56F7570C2F24343100F5B906 /* PNBoundingBoxJob.swift */,
56AD3B7B2E8833A700C7FC69 /* PNDirectionalShadowStage.swift */,
56AD3B7C2E8833A700C7FC69 /* PNOmniShadowStage.swift */,
56AD3B7D2E8833A700C7FC69 /* PNSpotShadowStage.swift */,
Expand Down Expand Up @@ -2349,6 +2358,7 @@
5669778929434DF300B37284 /* Documentation.docc in Sources */,
561667412719E2CD000063B1 /* PNMaterial.swift in Sources */,
56C3174528CFEB0E00C04F3C /* NSRegularExpression+Extension.swift in Sources */,
56F7570D2F24343200F5B906 /* PNBoundingBoxJob.swift in Sources */,
56C47BA527516BD20075C61B /* PNRenderMask.swift in Sources */,
566795C4274EA1FB005993D7 /* PNIMeshNode.swift in Sources */,
563BFC81272859630003EDB7 /* MDLObjectContainerComponent+Extension.swift in Sources */,
Expand Down Expand Up @@ -2439,6 +2449,7 @@
56D7ED5C274C611C00A6C25D /* PNBoundingBox+Alias.swift in Sources */,
56D7ED43274C381400A6C25D /* PNBoundingBoxInteractor.swift in Sources */,
563BFC91272898F20003EDB7 /* MDLAnimatedVector3Array+Extension.swift in Sources */,
56F7570B2F24302900F5B906 /* PNBoundingBoxCreator.swift in Sources */,
56DF90DE29A644BA002A49E7 /* simd_quatf+Alias.swift in Sources */,
5670EBDC273561FD00375194 /* Operator+Global.swift in Sources */,
566CEB272843944000DA6227 /* PNIBufferStoreFactory.swift in Sources */,
Expand Down Expand Up @@ -2585,6 +2596,7 @@
566020E227343280000F4CA7 /* SSAO.metal in Sources */,
561667232719E2CD000063B1 /* MTLPixelFormat+Extension.swift in Sources */,
562C73A92849510900E51606 /* PNParticleGenerator.swift in Sources */,
56F61F412F240E7A00920DDB /* BoundingBox.metal in Sources */,
560A0BBB2754223B005AE29D /* PNAnimatedCameraNode.swift in Sources */,
56B6AFB428A269BC00266421 /* PNSceneValidator.swift in Sources */,
56E011852840E07A007859F2 /* PNBoundEstimator.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Engine/Core/Buffers/PNBufferStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public protocol PNBufferStore {
var directionalLights: PNAnyDynamicBuffer<DirectionalLight> { get }
var spotLights: PNAnyDynamicBuffer<SpotLight> { get }
var cameras: PNAnyDynamicBuffer<CameraUniforms> { get }
var boundingBoxes: PNAnyDynamicBuffer<VertexP> { get }
var modelCoordinateSystems: PNAnyDynamicBuffer<ModelUniforms> { get }
var previousModelCoordinateSystems: PNAnyDynamicBuffer<ModelUniforms> { get }
var matrixPalettes: PNAnyDynamicBuffer<PNBLTransform> { get }
Expand Down
5 changes: 4 additions & 1 deletion Engine/Core/Buffers/PNIBufferStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import PNShared
import simd

public final class PNIBufferStore: PNBufferStore {
public var boundingBoxes: PNAnyDynamicBuffer<VertexP>
public var omniLights: PNAnyDynamicBuffer<OmniLight>
public var ambientLights: PNAnyDynamicBuffer<AmbientLight>
public var directionalLights: PNAnyDynamicBuffer<DirectionalLight>
Expand All @@ -25,7 +26,8 @@ public final class PNIBufferStore: PNBufferStore {
let previousMatrixPalettes = PNIDynamicBuffer<PNBLTransform>(device: device),
let ambientLights = PNIDynamicBuffer<AmbientLight>(device: device),
let directionalLights = PNIDynamicBuffer<DirectionalLight>(device: device),
let spotLights = PNIDynamicBuffer<SpotLight>(device: device) else {
let spotLights = PNIDynamicBuffer<SpotLight>(device: device),
let boundingBoxes = PNIDynamicBuffer<VertexP>(device: device) else {
return nil
}
self.omniLights = PNAnyDynamicBuffer(omniLights)
Expand All @@ -37,5 +39,6 @@ public final class PNIBufferStore: PNBufferStore {
self.previousMatrixPalettes = PNAnyDynamicBuffer(previousMatrixPalettes)
self.directionalLights = PNAnyDynamicBuffer(directionalLights)
self.spotLights = PNAnyDynamicBuffer(spotLights)
self.boundingBoxes = PNAnyDynamicBuffer(boundingBoxes)
}
}
6 changes: 6 additions & 0 deletions Engine/Core/Configuration/PNDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ public struct PNDefaults {
/// Configuration for rendering settings.
public var rendering = PNRendering()
/// Private initializer to prevent external instantiation.
public var debug = PNDebug()
fileprivate init() {
// Empty
}
/// Debug configuration.
public struct PNDebug {
/// Render bounding boxes around meshes
public var boundingBoxes = false
}
/// Rendering configuration.
public struct PNRendering {
/// Shadow size configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

import PNShared

/// A multi-threaded rendering process coordinator.
public class PNIThreadedWorkloadManager: PNWorkloadManager {
private var renderingCoordinator: PNRenderingCoordinator
Expand Down Expand Up @@ -34,6 +36,10 @@ public class PNIThreadedWorkloadManager: PNWorkloadManager {
nodeUpdate.update(rootNode: sceneGraph.rootNode)
let scene = transcriber.transcribe(scene: sceneGraph)
let inactive = frameSupplies.pullInactive
if PNDefaults.shared.debug.boundingBoxes {
let geometry = PNBoundingBoxCreator.vertices(boundingBoxes: scene.boundingBoxes)
inactive.bufferStore.boundingBoxes.upload(data: geometry)
}
inactive.bufferStore.matrixPalettes.upload(data: scene.palettes)
inactive.bufferStore.ambientLights.upload(data: scene.ambientLights)
inactive.bufferStore.omniLights.upload(data: scene.omniLights)
Expand All @@ -56,4 +62,5 @@ public class PNIThreadedWorkloadManager: PNWorkloadManager {
dispatchGroup.wait()
frameSupplies.swap()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public class PNIWorkloadManager: PNWorkloadManager {
taskQueue.execute()
nodeUpdate.update(rootNode: sceneGraph.rootNode)
let scene = transcriber.transcribe(scene: sceneGraph)
if PNDefaults.shared.debug.boundingBoxes {
let geometry = PNBoundingBoxCreator.vertices(boundingBoxes: scene.boundingBoxes)
bufferStore.boundingBoxes.upload(data: geometry)
}
bufferStore.matrixPalettes.upload(data: scene.palettes)
bufferStore.ambientLights.upload(data: scene.ambientLights)
bufferStore.omniLights.upload(data: scene.omniLights)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ extension MTLDepthStencilDescriptor {
descriptor.frontFaceStencil = .translucent
return descriptor
}
static var boundingBox: MTLDepthStencilDescriptor {
let descriptor = MTLDepthStencilDescriptor()
descriptor.label = "BoundingBox Depth Stencil"
descriptor.depthCompareFunction = .lessEqual
descriptor.isDepthWriteEnabled = true
descriptor.frontFaceStencil = .boundingBox
return descriptor
}
static var particle: MTLDepthStencilDescriptor {
let descriptor = MTLDepthStencilDescriptor()
descriptor.label = "Particle Depth Stencil"
Expand Down
6 changes: 6 additions & 0 deletions Engine/Core/Extensions/Metal/MTLDevice+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ extension MTLDevice {
func makeDSSTranslucent() -> MTLDepthStencilState {
failOrMakeDepthStencilState(descriptor: .translucent)
}
func makeDSSBoundingBox() -> MTLDepthStencilState {
failOrMakeDepthStencilState(descriptor: .boundingBox)
}
func makeDSSParticle() -> MTLDepthStencilState {
failOrMakeDepthStencilState(descriptor: .particle)
}
Expand All @@ -75,6 +78,9 @@ extension MTLDevice {
func makeRPSTranslucent(library: MTLLibrary) -> MTLRenderPipelineState {
failOrMakeRenderPipelineState(descriptor: .translucent(library: library))
}
func makeRPSBoundingBox(library: MTLLibrary) -> MTLRenderPipelineState {
failOrMakeRenderPipelineState(descriptor: .boundingBox(library: library))
}
func makeRPSTranslucentAnimated(library: MTLLibrary) -> MTLRenderPipelineState {
failOrMakeRenderPipelineState(descriptor: .translucentAnimated(library: library))
}
Expand Down
6 changes: 6 additions & 0 deletions Engine/Core/Extensions/Metal/MTLPixelFormat+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@ extension MTLPixelFormat {
static var translucentC: MTLPixelFormat {
.rgba16Float
}
static var boundingBoxC: MTLPixelFormat {
.rgba16Float
}
static var translucentDS: MTLPixelFormat {
.depth32Float_stencil8
}
static var boundingBoxDS: MTLPixelFormat {
.depth32Float_stencil8
}
static var particleC: MTLPixelFormat {
.rgba16Float
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ extension MTLRenderPipelineDescriptor {
descriptor.vertexDescriptor = .vertex
return descriptor
}
static func boundingBox(library: MTLLibrary) -> MTLRenderPipelineDescriptor {
let descriptor = MTLRenderPipelineDescriptor()
descriptor.label = "BoundingBox"
descriptor.vertexFunction = library.failOrMakeFunction(name: "vertexBoundingBox")
descriptor.fragmentFunction = library.failOrMakeFunction(name: "fragmentBoundingBox")
descriptor.colorAttachments[0].pixelFormat = .boundingBoxC
descriptor.depthAttachmentPixelFormat = .boundingBoxDS
descriptor.stencilAttachmentPixelFormat = .boundingBoxDS
descriptor.vertexDescriptor = .vertexP
return descriptor
}
static func particle(library: MTLLibrary) -> MTLRenderPipelineDescriptor {
let descriptor = MTLRenderPipelineDescriptor()
descriptor.label = "Particle"
Expand Down
10 changes: 10 additions & 0 deletions Engine/Core/Extensions/Metal/MTLStencilDescriptor+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,14 @@ extension MTLStencilDescriptor {
stencil.depthStencilPassOperation = .replace
return stencil
}
static var boundingBox: MTLStencilDescriptor {
let stencil = MTLStencilDescriptor()
stencil.stencilCompareFunction = .greaterEqual
stencil.readMask = 0b00000000
stencil.writeMask = 0xFF
stencil.stencilFailureOperation = .keep
stencil.depthFailureOperation = .keep
stencil.depthStencilPassOperation = .replace
return stencil
}
}
34 changes: 34 additions & 0 deletions Engine/Core/Rendering/Stages/PNBoundingBoxJob.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Copyright © 2025 Mateusz Stompór. All rights reserved.
//

import Metal

struct PNBoundingBoxJob: PNRenderJob {
private let pipelineState: MTLRenderPipelineState
private let depthStencilState: MTLDepthStencilState
init(pipelineState: MTLRenderPipelineState,
depthStencilState: MTLDepthStencilState) {
self.pipelineState = pipelineState
self.depthStencilState = depthStencilState
}
func draw(encoder: MTLRenderCommandEncoder, supply: PNFrameSupply) {
let dataStore = supply.bufferStore
encoder.setCullMode(.none)
encoder.setFrontFacing(.counterClockwise)
encoder.setDepthStencilState(depthStencilState)
encoder.setVertexBuffer(dataStore.boundingBoxes.buffer, index: 0)
encoder.setStencilReferenceValue(1)
encoder.setVertexBuffer(dataStore.cameras.buffer, index: 1)
encoder.setVertexBuffer(dataStore.modelCoordinateSystems.buffer, index: 2)
encoder.setRenderPipelineState(pipelineState)
encoder.drawPrimitives(type: .line, vertexStart: 0, vertexCount: dataStore.boundingBoxes.count)
}
static func make(device: MTLDevice) -> PNBoundingBoxJob? {
let library = device.makePorcelainLibrary()
let pipelineState = device.makeRPSBoundingBox(library: library)
let depthStencilState = device.makeDSSBoundingBox()
return PNBoundingBoxJob(pipelineState: pipelineState,
depthStencilState: depthStencilState)
}
}
6 changes: 5 additions & 1 deletion Engine/Core/Rendering/Stages/PNCombineStage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ struct PNCombineStage: PNStage {
private var spotJob: PNRenderJob
private var particleJob: PNRenderJob
private var directionalJob: PNRenderJob
private var translucentJob: PNTranslucentJob
private var translucentJob: PNRenderJob
private var renderPassDescriptor: MTLRenderPassDescriptor
private var ssaoTexture: PNTextureProvider
private var boundingBoxJob: PNRenderJob
init?(device: MTLDevice,
renderingSize: CGSize,
gBufferOutput: PNGPUSupply,
Expand All @@ -42,6 +43,7 @@ struct PNCombineStage: PNStage {
let translucentJob = PNTranslucentJob.make(device: device),
let fogJob = PNFogJob.make(device: device,
prTexture: gBufferOutput.color[2]),
let boundingBoxJob = PNBoundingBoxJob.make(device: device),
let stencil = gBufferOutput.stencil[0].texture,
let depth = gBufferOutput.depth[0].texture else {
return nil
Expand All @@ -60,6 +62,7 @@ struct PNCombineStage: PNStage {
self.spotJob = spotJob
self.fogJob = fogJob
self.translucentJob = translucentJob
self.boundingBoxJob = boundingBoxJob
self.particleJob = particleJob
self.directionalJob = directionalJob
self.io = PNGPUIO(input: PNGPUSupply(color: gBufferOutput.color + [ssaoTexture],
Expand All @@ -78,6 +81,7 @@ struct PNCombineStage: PNStage {
translucentJob.draw(encoder: encoder, supply: supply)
particleJob.draw(encoder: encoder, supply: supply)
fogJob.draw(encoder: encoder, supply: supply)
boundingBoxJob.draw(encoder: encoder, supply: supply)
encoder.endEncoding()
}
}
62 changes: 62 additions & 0 deletions Engine/Core/Scene/Mesh/PNBoundingBoxCreator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// Copyright © 2025 Mateusz Stompór. All rights reserved.
//

import PNShared

enum PNBoundingBoxCreator {
static func vertices(boundingBoxes: [PNBoundingBox?]) -> [VertexP] {
boundingBoxes.compactMap { $0 } .map { vertices(bb: $0) }.reduce(+) ?? []
}
static private func vertices(bb: PNBoundingBox) -> [VertexP] {
[
VertexP(position: bb.cornersLower.columns.0.xyz),
VertexP(position: bb.cornersLower.columns.1.xyz),

VertexP(position: bb.cornersLower.columns.1.xyz),
VertexP(position: bb.cornersLower.columns.3.xyz),

VertexP(position: bb.cornersLower.columns.1.xyz),
VertexP(position: bb.cornersLower.columns.2.xyz),

VertexP(position: bb.cornersLower.columns.2.xyz),
VertexP(position: bb.cornersLower.columns.3.xyz),

VertexP(position: bb.cornersLower.columns.2.xyz),
VertexP(position: bb.cornersLower.columns.0.xyz),

VertexP(position: bb.cornersLower.columns.3.xyz),
VertexP(position: bb.cornersLower.columns.0.xyz),

VertexP(position: bb.cornersUpper.columns.0.xyz),
VertexP(position: bb.cornersLower.columns.0.xyz),

VertexP(position: bb.cornersUpper.columns.1.xyz),
VertexP(position: bb.cornersLower.columns.1.xyz),

VertexP(position: bb.cornersUpper.columns.2.xyz),
VertexP(position: bb.cornersLower.columns.2.xyz),

VertexP(position: bb.cornersUpper.columns.3.xyz),
VertexP(position: bb.cornersLower.columns.3.xyz),

VertexP(position: bb.cornersUpper.columns.0.xyz),
VertexP(position: bb.cornersUpper.columns.1.xyz),

VertexP(position: bb.cornersUpper.columns.1.xyz),
VertexP(position: bb.cornersUpper.columns.3.xyz),

VertexP(position: bb.cornersUpper.columns.1.xyz),
VertexP(position: bb.cornersUpper.columns.2.xyz),

VertexP(position: bb.cornersUpper.columns.2.xyz),
VertexP(position: bb.cornersUpper.columns.3.xyz),

VertexP(position: bb.cornersUpper.columns.2.xyz),
VertexP(position: bb.cornersUpper.columns.0.xyz),

VertexP(position: bb.cornersUpper.columns.3.xyz),
VertexP(position: bb.cornersUpper.columns.0.xyz)
]
}
}
Loading
Loading