diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..b9b3b0de --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.3.11 diff --git a/CLAUDE.md b/CLAUDE.md index b873e862..661f6ae3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,6 +44,7 @@ mdl . # Markdown files ### Rendering Pipeline The engine uses a **deferred rendering pipeline** implemented in Metal. Shaders live in `Engine/Shaders/` and cover: + - PBR shading with normal mapping and translucency - Shadow mapping for point, spot, and directional lights (PCF soft shadows) - Post-processing: bloom, motion blur, film grain, vignette, distance fog @@ -55,10 +56,10 @@ Scenes are built from nodes in `Engine/Core/Organization/`. The scene graph supp ### Pipeline from Asset to Frame -1. **Import** (`Engine/Core/Import/`) — USDZ models and height-map meshes are loaded via Model I/O. -2. **Translation** (`Engine/Core/Translation/`) — Scene descriptions are validated and converted to render-ready representations with render masks. -3. **Rendering** (`Engine/Core/Rendering/`) — The `Transcriber` drives the deferred pipeline each frame, consuming scene descriptions. -4. **Buffers** (`Engine/Core/Buffers/`) — `DynamicBuffer` and `FlatTree` manage GPU-visible memory; `DataBuffer` wraps raw Metal buffers. +- **Import** (`Engine/Core/Import/`) — USDZ models and height-map meshes are loaded via Model I/O. +- **Translation** (`Engine/Core/Translation/`) — Scene descriptions are validated and converted to render-ready representations with render masks. +- **Rendering** (`Engine/Core/Rendering/`) — The `Transcriber` drives the deferred pipeline each frame, consuming scene descriptions. +- **Buffers** (`Engine/Core/Buffers/`) — `DynamicBuffer` and `FlatTree` manage GPU-visible memory; `DataBuffer` wraps raw Metal buffers. ### Engine Entry Point diff --git a/Engine.xcodeproj/project.pbxproj b/Engine.xcodeproj/project.pbxproj index d4979393..3fbf6e47 100644 --- a/Engine.xcodeproj/project.pbxproj +++ b/Engine.xcodeproj/project.pbxproj @@ -261,7 +261,6 @@ 569D923D273DD594004FDFAC /* PNBound.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569D923C273DD594004FDFAC /* PNBound.swift */; }; 569D9242273E7EED004FDFAC /* Color.metal in Sources */ = {isa = PBXBuildFile; fileRef = 569D9241273E7EED004FDFAC /* Color.metal */; }; 569D9244273E81DD004FDFAC /* VertexP+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569D9243273E81DD004FDFAC /* VertexP+Extension.swift */; }; - 569D9246273EA5F6004FDFAC /* PNBoundingBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569D9245273EA5F6004FDFAC /* PNBoundingBox.swift */; }; 569F10FE2847A6D4005046CC /* simd_float3+Alias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569F10FD2847A6D4005046CC /* simd_float3+Alias.swift */; }; 569F11002847A722005046CC /* simd_float4+Alias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569F10FF2847A722005046CC /* simd_float4+Alias.swift */; }; 56A0CD2B28576D2600EBB890 /* PNDynamicTexture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A0CD2A28576D2600EBB890 /* PNDynamicTexture.swift */; }; @@ -280,7 +279,7 @@ 56ADD2F4273707C500107E24 /* PNAmbientJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ADD2F3273707C500107E24 /* PNAmbientJob.swift */; }; 56ADD2F6273707E200107E24 /* Ambient.metal in Sources */ = {isa = PBXBuildFile; fileRef = 56ADD2F5273707E200107E24 /* Ambient.metal */; }; 56B32FE728496D3200190D6A /* PNParticleSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B32FE628496D3200190D6A /* PNParticleSystem.swift */; }; - 56B65DEC273ED4CE00DB927E /* PNIBoundingBoxInteractor+Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B65DEB273ED4CE00DB927E /* PNIBoundingBoxInteractor+Tests.swift */; }; + 56B65DEC273ED4CE00DB927E /* PNIBoundInteractor+Extended+Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B65DEB273ED4CE00DB927E /* PNIBoundInteractor+Extended+Tests.swift */; }; 56B65DEF273EE06300DB927E /* PNICullingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B65DEE273EE06300DB927E /* PNICullingController.swift */; }; 56B6AFAC28A259C800266421 /* PNPaletteGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B6AFAB28A259C800266421 /* PNPaletteGenerator.swift */; }; 56B6AFAE28A259D300266421 /* PNIPaletteGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B6AFAD28A259D300266421 /* PNIPaletteGenerator.swift */; }; @@ -303,14 +302,11 @@ 56D12AD027231DAD00550530 /* RawRepresentable+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D12ACF27231DAD00550530 /* RawRepresentable+Extension.swift */; }; 56D7ED3F274C36F200A6C25D /* PNBoundInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED3E274C36F200A6C25D /* PNBoundInteractor.swift */; }; 56D7ED41274C372800A6C25D /* PNIBoundInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED40274C372800A6C25D /* PNIBoundInteractor.swift */; }; - 56D7ED43274C381400A6C25D /* PNBoundingBoxInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED42274C381400A6C25D /* PNBoundingBoxInteractor.swift */; }; - 56D7ED45274C3AD400A6C25D /* PNIBoundingBoxInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED44274C3AD400A6C25D /* PNIBoundingBoxInteractor.swift */; }; 56D7ED4D274C52E000A6C25D /* PNAssetLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED4C274C52E000A6C25D /* PNAssetLoader.swift */; }; 56D7ED4F274C52EA00A6C25D /* PNIAssetLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED4E274C52EA00A6C25D /* PNIAssetLoader.swift */; }; 56D7ED52274C535F00A6C25D /* PNSceneLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED51274C535F00A6C25D /* PNSceneLoader.swift */; }; 56D7ED54274C536700A6C25D /* PNISceneLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED53274C536700A6C25D /* PNISceneLoader.swift */; }; 56D7ED5A274C605C00A6C25D /* PNCullingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED59274C605C00A6C25D /* PNCullingController.swift */; }; - 56D7ED5C274C611C00A6C25D /* PNBoundingBox+Alias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED5B274C611C00A6C25D /* PNBoundingBox+Alias.swift */; }; 56D7ED5E274C617200A6C25D /* ModelUniforms+Alias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D7ED5D274C617200A6C25D /* ModelUniforms+Alias.swift */; }; 56D8C4122739D404005E58E3 /* SpotLight+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D8C4112739D404005E58E3 /* SpotLight+Extension.swift */; }; 56DF90DC29A6447C002A49E7 /* simd_float3x3+Alias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56DF90DB29A6447C002A49E7 /* simd_float3x3+Alias.swift */; }; @@ -636,7 +632,6 @@ 569D9240273E7EE6004FDFAC /* Color.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Color.h; sourceTree = ""; }; 569D9241273E7EED004FDFAC /* Color.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Color.metal; sourceTree = ""; }; 569D9243273E81DD004FDFAC /* VertexP+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VertexP+Extension.swift"; sourceTree = ""; }; - 569D9245273EA5F6004FDFAC /* PNBoundingBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNBoundingBox.swift; sourceTree = ""; }; 569F10FD2847A6D4005046CC /* simd_float3+Alias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "simd_float3+Alias.swift"; sourceTree = ""; }; 569F10FF2847A722005046CC /* simd_float4+Alias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "simd_float4+Alias.swift"; sourceTree = ""; }; 56A0CD2A28576D2600EBB890 /* PNDynamicTexture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNDynamicTexture.swift; sourceTree = ""; }; @@ -656,7 +651,7 @@ 56ADD2F3273707C500107E24 /* PNAmbientJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNAmbientJob.swift; sourceTree = ""; }; 56ADD2F5273707E200107E24 /* Ambient.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Ambient.metal; sourceTree = ""; }; 56B32FE628496D3200190D6A /* PNParticleSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNParticleSystem.swift; sourceTree = ""; }; - 56B65DEB273ED4CE00DB927E /* PNIBoundingBoxInteractor+Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PNIBoundingBoxInteractor+Tests.swift"; sourceTree = ""; }; + 56B65DEB273ED4CE00DB927E /* PNIBoundInteractor+Extended+Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PNIBoundInteractor+Extended+Tests.swift"; sourceTree = ""; }; 56B65DEE273EE06300DB927E /* PNICullingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNICullingController.swift; sourceTree = ""; }; 56B6AFAB28A259C800266421 /* PNPaletteGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNPaletteGenerator.swift; sourceTree = ""; }; 56B6AFAD28A259D300266421 /* PNIPaletteGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNIPaletteGenerator.swift; sourceTree = ""; }; @@ -681,14 +676,11 @@ 56D12ACF27231DAD00550530 /* RawRepresentable+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RawRepresentable+Extension.swift"; sourceTree = ""; }; 56D7ED3E274C36F200A6C25D /* PNBoundInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNBoundInteractor.swift; sourceTree = ""; }; 56D7ED40274C372800A6C25D /* PNIBoundInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNIBoundInteractor.swift; sourceTree = ""; }; - 56D7ED42274C381400A6C25D /* PNBoundingBoxInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNBoundingBoxInteractor.swift; sourceTree = ""; }; - 56D7ED44274C3AD400A6C25D /* PNIBoundingBoxInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNIBoundingBoxInteractor.swift; sourceTree = ""; }; 56D7ED4C274C52E000A6C25D /* PNAssetLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNAssetLoader.swift; sourceTree = ""; }; 56D7ED4E274C52EA00A6C25D /* PNIAssetLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNIAssetLoader.swift; sourceTree = ""; }; 56D7ED51274C535F00A6C25D /* PNSceneLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNSceneLoader.swift; sourceTree = ""; }; 56D7ED53274C536700A6C25D /* PNISceneLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNISceneLoader.swift; sourceTree = ""; }; 56D7ED59274C605C00A6C25D /* PNCullingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNCullingController.swift; sourceTree = ""; }; - 56D7ED5B274C611C00A6C25D /* PNBoundingBox+Alias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PNBoundingBox+Alias.swift"; sourceTree = ""; }; 56D7ED5D274C617200A6C25D /* ModelUniforms+Alias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ModelUniforms+Alias.swift"; sourceTree = ""; }; 56D8C4112739D404005E58E3 /* SpotLight+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SpotLight+Extension.swift"; sourceTree = ""; }; 56DF90DB29A6447C002A49E7 /* simd_float3x3+Alias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "simd_float3x3+Alias.swift"; sourceTree = ""; }; @@ -916,7 +908,6 @@ isa = PBXGroup; children = ( 56E011892840FE90007859F2 /* Bound */, - 56D7ED49274C4BEB00A6C25D /* BoundingBox */, ); path = Bounds; sourceTree = ""; @@ -1556,7 +1547,6 @@ 561666C42719E2CC000063B1 /* PNFlatTree+Alias.swift */, 56924AE3271A181600FF3E06 /* PNIAnimatedValue+Alias.swift */, 56270168272B556500071620 /* PNAnimatedTransform+Alias.swift */, - 56D7ED5B274C611C00A6C25D /* PNBoundingBox+Alias.swift */, 56D7ED5D274C617200A6C25D /* ModelUniforms+Alias.swift */, 56FA634128AD161900D857A6 /* PNNode+Alias.swift */, ); @@ -2069,9 +2059,6 @@ 56D7ED46274C3B2400A6C25D /* BoundingBox */ = { isa = PBXGroup; children = ( - 569D9245273EA5F6004FDFAC /* PNBoundingBox.swift */, - 56D7ED44274C3AD400A6C25D /* PNIBoundingBoxInteractor.swift */, - 56D7ED42274C381400A6C25D /* PNBoundingBoxInteractor.swift */, ); path = BoundingBox; sourceTree = ""; @@ -2088,14 +2075,6 @@ path = Bound; sourceTree = ""; }; - 56D7ED49274C4BEB00A6C25D /* BoundingBox */ = { - isa = PBXGroup; - children = ( - 56B65DEB273ED4CE00DB927E /* PNIBoundingBoxInteractor+Tests.swift */, - ); - path = BoundingBox; - sourceTree = ""; - }; 56D7ED4B274C52C600A6C25D /* AssetLoader */ = { isa = PBXGroup; children = ( @@ -2196,6 +2175,7 @@ 56FA6EE228C66C4C00803314 /* PNIBoundInteractor */ = { isa = PBXGroup; children = ( + 56B65DEB273ED4CE00DB927E /* PNIBoundInteractor+Extended+Tests.swift */, 565D37EA28AABD5A000ECCC7 /* PNIBoundInteractor+Tests.swift */, 56FA6EE328C66C7000803314 /* PNIBoundInteractorIntersectionPoint+Tests.swift */, 56FA6EE528C66C8F00803314 /* PNIBoundInteractorIntersection+Tests.swift */, @@ -2379,7 +2359,6 @@ 56E8208F2727FD13002D58C3 /* Date+Extensioin.swift in Sources */, 566795EF274FF240005993D7 /* PNIRenderingCoordinatorFactory.swift in Sources */, 566795F9274FFE72005993D7 /* PNIWorkloadManagerFactory.swift in Sources */, - 56D7ED45274C3AD400A6C25D /* PNIBoundingBoxInteractor.swift in Sources */, 5686BD9A273C1CD300329FA0 /* PNIStaticBuffer.swift in Sources */, 5620C8272754F7DF0098D6FA /* PNIOmniLightNode.swift in Sources */, 5635177128AD5AE200807BDA /* PNScenePiece+Extension.swift in Sources */, @@ -2446,8 +2425,6 @@ 561667272719E2CD000063B1 /* MTLRenderPassDescriptor+Extension.swift in Sources */, 561667382719E2CD000063B1 /* PNMesh.swift in Sources */, 56D7ED52274C535F00A6C25D /* PNSceneLoader.swift in Sources */, - 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 */, @@ -2531,7 +2508,6 @@ 569D9242273E7EED004FDFAC /* Color.metal in Sources */, 56D7ED5A274C605C00A6C25D /* PNCullingController.swift in Sources */, 560B5DC52757CEBB00C98F1A /* PNModelReference.swift in Sources */, - 569D9246273EA5F6004FDFAC /* PNBoundingBox.swift in Sources */, 56924AE8271A187400FF3E06 /* PNAnimatedFloat3+Extension.swift in Sources */, 566795B8274DC51E005993D7 /* PNMeshNode.swift in Sources */, 561666FA2719E2CD000063B1 /* simd_float4+Extension.swift in Sources */, @@ -2650,7 +2626,7 @@ 56165BF627444A680025D58B /* PNFlatTree+Tests.swift in Sources */, 56416D7427400F66003C99C2 /* PNIDynamicBuffer+Tests.swift in Sources */, 561340B328C26A4B00D36F89 /* Data+Extension+Tests.swift in Sources */, - 56B65DEC273ED4CE00DB927E /* PNIBoundingBoxInteractor+Tests.swift in Sources */, + 56B65DEC273ED4CE00DB927E /* PNIBoundInteractor+Extended+Tests.swift in Sources */, 56FA6EE428C66C7000803314 /* PNIBoundInteractorIntersectionPoint+Tests.swift in Sources */, 5677AD3128C151CA00BF822C /* XCTest+Extension.swift in Sources */, 560253D72726CCCA00B3A13A /* PNIChronometer+Tests.swift in Sources */, diff --git a/Engine/Core/Aliases/Porcelain/PNBoundingBox+Alias.swift b/Engine/Core/Aliases/Porcelain/PNBoundingBox+Alias.swift deleted file mode 100644 index a622a3a9..00000000 --- a/Engine/Core/Aliases/Porcelain/PNBoundingBox+Alias.swift +++ /dev/null @@ -1,6 +0,0 @@ -// -// Copyright © 2021 Mateusz Stompór. All rights reserved. -// - -/// Bounding box in world coordinate system. -public typealias PNWBoundingBox = PNBoundingBox diff --git a/Engine/Core/Engine/PNIEngine.swift b/Engine/Core/Engine/PNIEngine.swift index 8e160f71..dcbc60b5 100644 --- a/Engine/Core/Engine/PNIEngine.swift +++ b/Engine/Core/Engine/PNIEngine.swift @@ -68,10 +68,8 @@ public final class PNIEngine: PNEngine { assertionFailure("Could not retrieve device from the view") return nil } - let interactor = PNIBoundingBoxInteractor.default - let cullingController = PNICullingController(interactor: interactor) - let maskGenerator = PNIRenderMaskGenerator(cullingController: cullingController, - interactor: interactor) + let cullingController = PNICullingController(interactor: PNIBoundInteractor()) + let maskGenerator = PNIRenderMaskGenerator(cullingController: cullingController) return PNIEngine(view: view, renderingSize: renderingSize, scene: scene, diff --git a/Engine/Core/Engine/Workload/Factories/PNIThreadedWorkloadManagerFactory.swift b/Engine/Core/Engine/Workload/Factories/PNIThreadedWorkloadManagerFactory.swift index 3c2a366b..314b0ad4 100644 --- a/Engine/Core/Engine/Workload/Factories/PNIThreadedWorkloadManagerFactory.swift +++ b/Engine/Core/Engine/Workload/Factories/PNIThreadedWorkloadManagerFactory.swift @@ -12,7 +12,9 @@ public struct PNIThreadedWorkloadManagerFactory: PNWorkloadManagerFactory { let bufferStoreC = bufferStoreFactory.new() else { return nil } - return PNIThreadedWorkloadManager(bufferStores: (bufferStoreA, bufferStoreB, bufferStoreC), + return PNIThreadedWorkloadManager(bufferStores: [bufferStoreA, + bufferStoreB, + bufferStoreC], renderingCoordinator: renderingCoordinator, renderMaskGenerator: renderMaskGenerator, transcriber: PNITranscriber.default) diff --git a/Engine/Core/Engine/Workload/WorkloadManager/PNIThreadedWorkloadManager.swift b/Engine/Core/Engine/Workload/WorkloadManager/PNIThreadedWorkloadManager.swift index 1e6ab82c..c8f90792 100644 --- a/Engine/Core/Engine/Workload/WorkloadManager/PNIThreadedWorkloadManager.swift +++ b/Engine/Core/Engine/Workload/WorkloadManager/PNIThreadedWorkloadManager.swift @@ -16,17 +16,18 @@ public class PNIThreadedWorkloadManager: PNWorkloadManager { private var writeIndex = 0 private let semaphore = DispatchSemaphore(value: 3) private var previousFrameScene: PNSceneDescription? - public init(bufferStores: (PNBufferStore, PNBufferStore, PNBufferStore), + public init(bufferStores: [PNBufferStore], renderingCoordinator: PNRenderingCoordinator, renderMaskGenerator: PNRenderMaskGenerator, transcriber: PNTranscriber) { + precondition(bufferStores.count == 3, "Buffer stores must contain exactly 3 items") self.renderingCoordinator = renderingCoordinator self.transcriber = transcriber self.renderMaskGenerator = renderMaskGenerator - supplies = [ - PNFrameSupply(scene: PNSceneDescription(), bufferStore: bufferStores.0, mask: .empty), - PNFrameSupply(scene: PNSceneDescription(), bufferStore: bufferStores.1, mask: .empty), - PNFrameSupply(scene: PNSceneDescription(), bufferStore: bufferStores.2, mask: .empty) + self.supplies = [ + PNFrameSupply(scene: PNSceneDescription(), bufferStore: bufferStores[0], mask: .empty), + PNFrameSupply(scene: PNSceneDescription(), bufferStore: bufferStores[1], mask: .empty), + PNFrameSupply(scene: PNSceneDescription(), bufferStore: bufferStores[2], mask: .empty) ] } public func draw(sceneGraph: PNScene, taskQueue: PNRepeatableTaskQueue) { @@ -40,7 +41,7 @@ public class PNIThreadedWorkloadManager: PNWorkloadManager { nodeUpdate.update(rootNode: sceneGraph.rootNode) let scene = transcriber.transcribe(scene: sceneGraph) if PNDefaults.shared.debug.boundingBoxes { - let geometry = PNBoundingBoxCreator.vertices(boundingBoxes: scene.boundingBoxes) + let geometry = PNBoundingBoxCreator.vertices(bounds: scene.bounds) slot.bufferStore.boundingBoxes.upload(data: geometry) } slot.bufferStore.matrixPalettes.upload(data: scene.palettes) diff --git a/Engine/Core/Engine/Workload/WorkloadManager/PNIWorkloadManager.swift b/Engine/Core/Engine/Workload/WorkloadManager/PNIWorkloadManager.swift index 050bef89..5ff9e8ce 100644 --- a/Engine/Core/Engine/Workload/WorkloadManager/PNIWorkloadManager.swift +++ b/Engine/Core/Engine/Workload/WorkloadManager/PNIWorkloadManager.swift @@ -26,7 +26,7 @@ public class PNIWorkloadManager: PNWorkloadManager { nodeUpdate.update(rootNode: sceneGraph.rootNode) let scene = transcriber.transcribe(scene: sceneGraph) if PNDefaults.shared.debug.boundingBoxes { - let geometry = PNBoundingBoxCreator.vertices(boundingBoxes: scene.boundingBoxes) + let geometry = PNBoundingBoxCreator.vertices(bounds: scene.bounds) bufferStore.boundingBoxes.upload(data: geometry) } bufferStore.matrixPalettes.upload(data: scene.palettes) diff --git a/Engine/Core/Extensions/Porcelain/PNMesh+Extension.swift b/Engine/Core/Extensions/Porcelain/PNMesh+Extension.swift index e61a4465..53ef4b9b 100644 --- a/Engine/Core/Extensions/Porcelain/PNMesh+Extension.swift +++ b/Engine/Core/Extensions/Porcelain/PNMesh+Extension.swift @@ -23,9 +23,8 @@ extension PNMesh { indexType: .uint16, primitiveType: .triangle) let pieceDescription = PNPieceDescription(drawDescription: drawDescription) - let boundingBox = PNIBoundingBoxInteractor.default.from(bound: PNBound(min: [-0.5, -0.5, -0.5], - max: [0.5, 0.5, 0.5])) - return PNMesh(boundingBox: boundingBox, + let bound = PNBound(min: [-0.5, -0.5, -0.5], max: [0.5, 0.5, 0.5]) + return PNMesh(bound: bound, vertexBuffer: verticesBuffer, pieceDescriptions: [pieceDescription]) } @@ -83,9 +82,7 @@ extension PNMesh { primitiveType: .triangle) let pieceDescription = PNPieceDescription(drawDescription: drawDescription, material: material) - let interactor = PNIBoundingBoxInteractor.default - return PNMesh(boundingBox: interactor.from(bound: PNBound(min: [-1, -1, 0], - max: [1, 1, 0])), + return PNMesh(bound: PNBound(min: [-1, -1, 0], max: [1, 1, 0]), vertexBuffer: verticesBuffer, pieceDescriptions: [pieceDescription]) } diff --git a/Engine/Core/Import/Terrain/PNITerrainLoader.swift b/Engine/Core/Import/Terrain/PNITerrainLoader.swift index ef8874a8..b6f74347 100644 --- a/Engine/Core/Import/Terrain/PNITerrainLoader.swift +++ b/Engine/Core/Import/Terrain/PNITerrainLoader.swift @@ -107,7 +107,7 @@ public struct PNITerrainLoader: PNTerrainLoader { pieces.append(PNPieceDescription(drawDescription: submesh, material: material)) } let bound = PNIBoundEstimator().bound(vertexBuffer: verts) - return PNMesh(boundingBox: PNIBoundingBoxInteractor.default.from(bound: bound), + return PNMesh(bound: bound, vertexBuffer: PNDataBuffer(buffer: buffer, length: verts.count, label: "Vertices"), pieceDescriptions: pieces) } diff --git a/Engine/Core/Import/Translator/PNISceneTranslator.swift b/Engine/Core/Import/Translator/PNISceneTranslator.swift index f93803b5..005df438 100644 --- a/Engine/Core/Import/Translator/PNISceneTranslator.swift +++ b/Engine/Core/Import/Translator/PNISceneTranslator.swift @@ -8,7 +8,7 @@ import PNShared public final class PNISceneTranslator: PNSceneTranslator { private let device: MTLDevice - private let interactor = PNIBoundingBoxInteractor.default + private let interactor = PNIBoundInteractor() private var materialCache = [String: PNMaterial]() public init(device: MTLDevice) { self.device = device @@ -138,7 +138,7 @@ public final class PNISceneTranslator: PNSceneTranslator { } } - return PNMesh(boundingBox: interactor.from(bound: mesh.boundingBox.pnBound), + return PNMesh(bound: mesh.boundingBox.pnBound, vertexBuffer: dataBuffer, pieceDescriptions: pieceDescriptions) } diff --git a/Engine/Core/Organization/Node/Kinds/AmbientLight/PNIAmbientLightNode.swift b/Engine/Core/Organization/Node/Kinds/AmbientLight/PNIAmbientLightNode.swift index e4e809bf..6feb0b81 100644 --- a/Engine/Core/Organization/Node/Kinds/AmbientLight/PNIAmbientLightNode.swift +++ b/Engine/Core/Organization/Node/Kinds/AmbientLight/PNIAmbientLightNode.swift @@ -11,10 +11,10 @@ public final class PNIAmbientLightNode: PNAmbientLightNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(light: PNAmbientLight, transform: PNLTransform, name: String = "") { @@ -24,10 +24,10 @@ public final class PNIAmbientLightNode: PNAmbientLightNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = light.boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = light.bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .ambientLight, diff --git a/Engine/Core/Organization/Node/Kinds/Camera/PNIAnimatedCameraNode.swift b/Engine/Core/Organization/Node/Kinds/Camera/PNIAnimatedCameraNode.swift index 92652e36..47bac46a 100644 --- a/Engine/Core/Organization/Node/Kinds/Camera/PNIAnimatedCameraNode.swift +++ b/Engine/Core/Organization/Node/Kinds/Camera/PNIAnimatedCameraNode.swift @@ -13,10 +13,10 @@ public final class PNIAnimatedCameraNode: PNAnimatedCameraNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(camera: PNCamera, animator: PNAnimator, animation: PNAnimatedCoordinateSpace, @@ -29,10 +29,10 @@ public final class PNIAnimatedCameraNode: PNAnimatedCameraNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = camera.boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = camera.bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { scene.entities.add(parentIdx: parentIdx, data: PNEntity(type: .camera, diff --git a/Engine/Core/Organization/Node/Kinds/Camera/PNICameraNode.swift b/Engine/Core/Organization/Node/Kinds/Camera/PNICameraNode.swift index 15df713e..623e6931 100644 --- a/Engine/Core/Organization/Node/Kinds/Camera/PNICameraNode.swift +++ b/Engine/Core/Organization/Node/Kinds/Camera/PNICameraNode.swift @@ -11,10 +11,10 @@ public final class PNICameraNode: PNCameraNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(camera: PNCamera, transform: PNLTransform, @@ -25,10 +25,10 @@ public final class PNICameraNode: PNCameraNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.intrinsicBoundingBox = camera.boundingBox - self.childrenMergedBoundingBox = nil + self.localBound = nil + self.worldBound = nil + self.intrinsicBound = camera.bound + self.childrenMergedBound = nil } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { diff --git a/Engine/Core/Organization/Node/Kinds/Mesh/PNIAnimatedMeshNode.swift b/Engine/Core/Organization/Node/Kinds/Mesh/PNIAnimatedMeshNode.swift index df8f2e38..e8691138 100644 --- a/Engine/Core/Organization/Node/Kinds/Mesh/PNIAnimatedMeshNode.swift +++ b/Engine/Core/Organization/Node/Kinds/Mesh/PNIAnimatedMeshNode.swift @@ -13,10 +13,10 @@ public final class PNIAnimatedMeshNode: PNAnimatedMeshNode { public var worldTransform: PNM2WTransform public var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(mesh: PNMesh, animator: PNAnimator, animation: PNAnimatedCoordinateSpace, @@ -29,10 +29,10 @@ public final class PNIAnimatedMeshNode: PNAnimatedMeshNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = mesh.boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = mesh.bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .mesh, diff --git a/Engine/Core/Organization/Node/Kinds/Mesh/PNIMeshNode.swift b/Engine/Core/Organization/Node/Kinds/Mesh/PNIMeshNode.swift index f5f86538..304e4392 100644 --- a/Engine/Core/Organization/Node/Kinds/Mesh/PNIMeshNode.swift +++ b/Engine/Core/Organization/Node/Kinds/Mesh/PNIMeshNode.swift @@ -12,10 +12,10 @@ public final class PNIMeshNode: PNMeshNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(mesh: PNMesh, transform: PNLTransform, name: String = "") { @@ -25,10 +25,10 @@ public final class PNIMeshNode: PNMeshNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = mesh.boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = mesh.bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .mesh, diff --git a/Engine/Core/Organization/Node/Kinds/OmniLight/PNIOmniLightNode.swift b/Engine/Core/Organization/Node/Kinds/OmniLight/PNIOmniLightNode.swift index ee0e19e2..5e8239b7 100644 --- a/Engine/Core/Organization/Node/Kinds/OmniLight/PNIOmniLightNode.swift +++ b/Engine/Core/Organization/Node/Kinds/OmniLight/PNIOmniLightNode.swift @@ -12,10 +12,10 @@ public final class PNIOmniLightNode: PNOmniLightNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(light: PNOmniLight, transform: PNLTransform, name: String = "") { @@ -25,10 +25,10 @@ public final class PNIOmniLightNode: PNOmniLightNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = light.boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = light.bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .omniLight, diff --git a/Engine/Core/Organization/Node/Kinds/Particle/PNIParticleNode.swift b/Engine/Core/Organization/Node/Kinds/Particle/PNIParticleNode.swift index 2a1a7e07..eee2cb72 100644 --- a/Engine/Core/Organization/Node/Kinds/Particle/PNIParticleNode.swift +++ b/Engine/Core/Organization/Node/Kinds/Particle/PNIParticleNode.swift @@ -12,11 +12,11 @@ public final class PNIParticleNode: PNParticleNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public var intrinsicBoundingBox: PNBoundingBox? { - PNIBoundingBoxInteractor.default.from(bound: provider.positioningRules.bound) + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public var intrinsicBound: PNBound? { + provider.positioningRules.bound } public init(provider: PNRenderableParticlesProvider, transform: PNLTransform, @@ -27,9 +27,9 @@ public final class PNIParticleNode: PNParticleNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .particle, diff --git a/Engine/Core/Organization/Node/Kinds/RiggedMesh/PNIAnimatedRiggedMeshNode.swift b/Engine/Core/Organization/Node/Kinds/RiggedMesh/PNIAnimatedRiggedMeshNode.swift index a2c203e0..b6cc1636 100644 --- a/Engine/Core/Organization/Node/Kinds/RiggedMesh/PNIAnimatedRiggedMeshNode.swift +++ b/Engine/Core/Organization/Node/Kinds/RiggedMesh/PNIAnimatedRiggedMeshNode.swift @@ -15,10 +15,10 @@ public final class PNIAnimatedRiggedMeshNode: PNAnimatedRiggedMeshNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public var intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public var intrinsicBound: PNBound? public init(mesh: PNMesh, skeleton: PNSkeleton, animator: PNAnimator, @@ -33,10 +33,10 @@ public final class PNIAnimatedRiggedMeshNode: PNAnimatedRiggedMeshNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = mesh.boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = mesh.bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .animatedMesh, diff --git a/Engine/Core/Organization/Node/Kinds/RiggedMesh/PNIRiggedMeshNode.swift b/Engine/Core/Organization/Node/Kinds/RiggedMesh/PNIRiggedMeshNode.swift index 21364acf..84dfe6b6 100644 --- a/Engine/Core/Organization/Node/Kinds/RiggedMesh/PNIRiggedMeshNode.swift +++ b/Engine/Core/Organization/Node/Kinds/RiggedMesh/PNIRiggedMeshNode.swift @@ -13,10 +13,10 @@ public final class PNIRiggedMeshNode: PNRiggedMeshNode { public var worldTransform: PNM2WTransform public var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(mesh: PNMesh, skeleton: PNSkeleton, transform: PNLTransform, @@ -28,10 +28,10 @@ public final class PNIRiggedMeshNode: PNRiggedMeshNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = mesh.boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = mesh.bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .animatedMesh, diff --git a/Engine/Core/Organization/Node/Kinds/SceneNode/PNIAnimatedNode.swift b/Engine/Core/Organization/Node/Kinds/SceneNode/PNIAnimatedNode.swift index cdfd6479..1570f2e8 100644 --- a/Engine/Core/Organization/Node/Kinds/SceneNode/PNIAnimatedNode.swift +++ b/Engine/Core/Organization/Node/Kinds/SceneNode/PNIAnimatedNode.swift @@ -13,10 +13,10 @@ public final class PNIAnimatedNode: PNAnimatedNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(animator: PNAnimator, animation: PNAnimatedCoordinateSpace, name: String = "") { @@ -27,10 +27,10 @@ public final class PNIAnimatedNode: PNAnimatedNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = nil + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = nil } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .group, diff --git a/Engine/Core/Organization/Node/Kinds/SceneNode/PNISceneNode.swift b/Engine/Core/Organization/Node/Kinds/SceneNode/PNISceneNode.swift index 38c728c6..cf212ffe 100644 --- a/Engine/Core/Organization/Node/Kinds/SceneNode/PNISceneNode.swift +++ b/Engine/Core/Organization/Node/Kinds/SceneNode/PNISceneNode.swift @@ -12,22 +12,22 @@ public final class PNISceneNode: PNSceneNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(transform: PNLTransform = .identity, - boundingBox: PNBoundingBox? = nil, + bound: PNBound? = nil, name: String = "") { self.name = name self.transform = transform self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .group, diff --git a/Engine/Core/Organization/Node/Kinds/SceneNode/PNSceneNode.swift b/Engine/Core/Organization/Node/Kinds/SceneNode/PNSceneNode.swift index 8ad8af06..990083be 100644 --- a/Engine/Core/Organization/Node/Kinds/SceneNode/PNSceneNode.swift +++ b/Engine/Core/Organization/Node/Kinds/SceneNode/PNSceneNode.swift @@ -12,10 +12,10 @@ public protocol PNSceneNode: AnyObject { var worldTransform: PNM2WTransform { get set } var enclosingNode: PNScenePiece? { get set } var modelUniforms: PNWModelUniforms { get set } - var localBoundingBox: PNBoundingBox? { get set } - var worldBoundingBox: PNBoundingBox? { get set } - var childrenMergedBoundingBox: PNBoundingBox? { get set } - var intrinsicBoundingBox: PNBoundingBox? { get } + var localBound: PNBound? { get set } + var worldBound: PNBound? { get set } + var childrenMergedBound: PNBound? { get set } + var intrinsicBound: PNBound? { get } func update() func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex } diff --git a/Engine/Core/Organization/Node/Kinds/SpotLight/PNISpotLightNode.swift b/Engine/Core/Organization/Node/Kinds/SpotLight/PNISpotLightNode.swift index e9476d35..92a75b7d 100644 --- a/Engine/Core/Organization/Node/Kinds/SpotLight/PNISpotLightNode.swift +++ b/Engine/Core/Organization/Node/Kinds/SpotLight/PNISpotLightNode.swift @@ -11,10 +11,10 @@ public final class PNISpotLightNode: PNSpotLightNode { public var worldTransform: PNM2WTransform public weak var enclosingNode: PNScenePiece? public var modelUniforms: PNWModelUniforms - public var localBoundingBox: PNBoundingBox? - public var worldBoundingBox: PNBoundingBox? - public var childrenMergedBoundingBox: PNBoundingBox? - public let intrinsicBoundingBox: PNBoundingBox? + public var localBound: PNBound? + public var worldBound: PNBound? + public var childrenMergedBound: PNBound? + public let intrinsicBound: PNBound? public init(light: PNSpotLight, transform: PNLTransform, name: String = "") { @@ -24,10 +24,10 @@ public final class PNISpotLightNode: PNSpotLightNode { self.worldTransform = .identity self.enclosingNode = nil self.modelUniforms = .identity - self.localBoundingBox = nil - self.worldBoundingBox = nil - self.childrenMergedBoundingBox = nil - self.intrinsicBoundingBox = light.boundingBox + self.localBound = nil + self.worldBound = nil + self.childrenMergedBound = nil + self.intrinsicBound = light.bound } public func write(scene: PNSceneDescription, parentIdx: PNParentIndex) -> PNNewlyWrittenIndex { let entity = PNEntity(type: .spotLight, referenceIdx: scene.spotLights.count) diff --git a/Engine/Core/Organization/PNNodeUpdater.swift b/Engine/Core/Organization/PNNodeUpdater.swift index 3a4ba873..89a3425c 100644 --- a/Engine/Core/Organization/PNNodeUpdater.swift +++ b/Engine/Core/Organization/PNNodeUpdater.swift @@ -5,7 +5,7 @@ import simd class PNNodeUpdater { - private let interactor = PNIBoundingBoxInteractor.default + private let interactor = PNIBoundInteractor() init() { // Empty } @@ -22,44 +22,26 @@ class PNNodeUpdater { modelMatrixInverse: concatenatedTransform.inverse) if node.children.isEmpty { - if let bb = node.data.intrinsicBoundingBox { - let aabb = interactor.aabb(interactor.multiply(node.data.transform, bb)) - node.data.localBoundingBox = aabb - } else { - node.data.localBoundingBox = nil - } - node.data.childrenMergedBoundingBox = nil + node.data.localBound = node.data.intrinsicBound.map { interactor.multiply(node.data.transform, $0) } + node.data.childrenMergedBound = nil } else { for child in node.children { update(from: child, worldTransform: concatenatedTransform) } - let localBoundingBoxes = node.children.compactMap { $0.data.localBoundingBox } - let mergedLocalBoundingBoxes = localBoundingBoxes.reduce(interactor.merge(_:_:)) - node.data.childrenMergedBoundingBox = mergedLocalBoundingBoxes + let localBounds = node.children.compactMap { $0.data.localBound } + let mergedLocalBounds = localBounds.reduce { interactor.merge($0, rhs: $1) } + node.data.childrenMergedBound = mergedLocalBounds - if let bb = node.data.intrinsicBoundingBox { - if let childrenBB = mergedLocalBoundingBoxes { - let merged = interactor.merge(bb, childrenBB) - let aabb = interactor.aabb(interactor.multiply(node.data.transform, merged)) - node.data.localBoundingBox = aabb + if let bb = node.data.intrinsicBound { + if let childrenBound = mergedLocalBounds { + node.data.localBound = interactor.multiply(node.data.transform, interactor.merge(bb, rhs: childrenBound)) } else { - let aabb = interactor.aabb(interactor.multiply(node.data.transform, bb)) - node.data.localBoundingBox = aabb + node.data.localBound = interactor.multiply(node.data.transform, bb) } } else { - if let childrenBB = mergedLocalBoundingBoxes { - let aabb = interactor.aabb(interactor.multiply(node.data.transform, childrenBB)) - node.data.localBoundingBox = aabb - } else { - node.data.localBoundingBox = nil - } + node.data.localBound = mergedLocalBounds.map { interactor.multiply(node.data.transform, $0) } } } - if let localBoundingBox = node.data.localBoundingBox { - let aabb = interactor.aabb(interactor.multiply(worldTransform, localBoundingBox)) - node.data.worldBoundingBox = aabb - } else { - node.data.worldBoundingBox = nil - } + node.data.worldBound = node.data.localBound.map { interactor.multiply(worldTransform, $0) } } } diff --git a/Engine/Core/Scene/Bounds/Bound/PNBound.swift b/Engine/Core/Scene/Bounds/Bound/PNBound.swift index 9185f9fd..5df27926 100644 --- a/Engine/Core/Scene/Bounds/Bound/PNBound.swift +++ b/Engine/Core/Scene/Bounds/Bound/PNBound.swift @@ -5,7 +5,6 @@ import simd /// Represents the opposite, extreme points of an abstract object in 3D space. -/// A shortened version of ``PNBoundingBox`` for simpler cases. public struct PNBound: CustomDebugStringConvertible { /// The minimum (lowest) corner point of the bound in 3D space. public let min: simd_float3 diff --git a/Engine/Core/Scene/Bounds/Bound/PNBoundInteractor.swift b/Engine/Core/Scene/Bounds/Bound/PNBoundInteractor.swift index 09338a78..b00c1b91 100644 --- a/Engine/Core/Scene/Bounds/Bound/PNBoundInteractor.swift +++ b/Engine/Core/Scene/Bounds/Bound/PNBoundInteractor.swift @@ -2,6 +2,8 @@ // Copyright © 2021 Mateusz Stompór. All rights reserved. // +import simd + /// Interface encapsulating operations that can be performed on bounds. public protocol PNBoundInteractor { func overlap(_ lhs: PNBound, _ rhs: PNBound) -> Bool @@ -14,4 +16,8 @@ public protocol PNBoundInteractor { func depth(_ bound: PNBound) -> Float func volume(_ bound: PNBound) -> Float func center(_ bound: PNBound) -> PNPoint3D + func multiply(_ lhs: simd_float4x4, _ rhs: PNBound) -> PNBound + func from(inverseProjection: simd_float4x4) -> PNBound + func from(_ corners: [simd_float3]) -> PNBound + func corners(_ bound: PNBound) -> [simd_float3] } diff --git a/Engine/Core/Scene/Bounds/Bound/PNIBoundInteractor.swift b/Engine/Core/Scene/Bounds/Bound/PNIBoundInteractor.swift index 41e26476..9791977f 100644 --- a/Engine/Core/Scene/Bounds/Bound/PNIBoundInteractor.swift +++ b/Engine/Core/Scene/Bounds/Bound/PNIBoundInteractor.swift @@ -2,6 +2,8 @@ // Copyright © 2021 Mateusz Stompór. All rights reserved. // +import simd + public struct PNIBoundInteractor: PNBoundInteractor { public init() { // Default @@ -62,4 +64,37 @@ public struct PNIBoundInteractor: PNBoundInteractor { avg(bound.min.y, bound.max.y), avg(bound.min.z, bound.max.z)] } + public func corners(_ bound: PNBound) -> [simd_float3] { + [simd_float3(bound.min.x, bound.min.y, bound.min.z), + simd_float3(bound.max.x, bound.min.y, bound.min.z), + simd_float3(bound.min.x, bound.min.y, bound.max.z), + simd_float3(bound.max.x, bound.min.y, bound.max.z), + simd_float3(bound.min.x, bound.max.y, bound.min.z), + simd_float3(bound.max.x, bound.max.y, bound.min.z), + simd_float3(bound.min.x, bound.max.y, bound.max.z), + simd_float3(bound.max.x, bound.max.y, bound.max.z)] + } + public func from(_ corners: [simd_float3]) -> PNBound { + var minV = simd_float3(repeating: Float.infinity) + var maxV = simd_float3(repeating: -Float.infinity) + for c in corners { + minV = simd_min(minV, c) + maxV = simd_max(maxV, c) + } + return PNBound(min: minV, max: maxV) + } + public func multiply(_ lhs: simd_float4x4, _ rhs: PNBound) -> PNBound { + from(corners(rhs).map { (lhs * simd_float4($0, 1)).xyz }) + } + public func from(inverseProjection: simd_float4x4) -> PNBound { + let ndcCorners: [simd_float4] = [ + [-1, -1, 0, 1], [ 1, -1, 0, 1], [-1, -1, 1, 1], [ 1, -1, 1, 1], + [-1, 1, 0, 1], [ 1, 1, 0, 1], [-1, 1, 1, 1], [ 1, 1, 1, 1] + ] + return from(ndcCorners.map { v -> simd_float3 in + var p = inverseProjection * v + p /= p.w + return p.xyz + }) + } } diff --git a/Engine/Core/Scene/Bounds/BoundingBox/PNBoundingBox.swift b/Engine/Core/Scene/Bounds/BoundingBox/PNBoundingBox.swift deleted file mode 100644 index 295acd2d..00000000 --- a/Engine/Core/Scene/Bounds/BoundingBox/PNBoundingBox.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright © 2021 Mateusz Stompór. All rights reserved. -// - -import simd - -/// Specifies all extreme points of a given abstract bounding volume. In contrast to ``PNBound``, definitions are explicit. -public struct PNBoundingBox: Equatable { - /// The four lower extreme corners of the bounding box, each stored as a column in the matrix. - public let cornersLower: simd_float4x4 - /// The four upper extreme corners of the bounding box, each stored as a column in the matrix. - public let cornersUpper: simd_float4x4 -} diff --git a/Engine/Core/Scene/Bounds/BoundingBox/PNBoundingBoxInteractor.swift b/Engine/Core/Scene/Bounds/BoundingBox/PNBoundingBoxInteractor.swift deleted file mode 100644 index f59e078a..00000000 --- a/Engine/Core/Scene/Bounds/BoundingBox/PNBoundingBoxInteractor.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright © 2021 Mateusz Stompór. All rights reserved. -// - -import simd - -/// Interface encapsulating operations that can be performed on bounding boxes. -public protocol PNBoundingBoxInteractor { - func aabb(_ boundingBox: PNBoundingBox) -> PNBoundingBox - func bound(_ boundingBox: PNBoundingBox) -> PNBound - func safeBound(_ boundingBox: PNBoundingBox?) -> PNBound? - func merge(_ lhs: PNBoundingBox, _ rhs: PNBoundingBox) -> PNBoundingBox - func overlap(_ lhs: PNBoundingBox, _ rhs: PNBoundingBox) -> Bool - func from(bound: PNBound) -> PNBoundingBox - func from(inverseProjection: simd_float4x4) -> PNBoundingBox - func from(_ corners: [simd_float3]) -> PNBoundingBox - func isEqual(_ lhs: PNBoundingBox, _ rhs: PNBoundingBox) -> Bool - func multiply(_ lhs: simd_float4x4, _ rhs: PNBoundingBox) -> PNBoundingBox - func corners(_ bound: PNBoundingBox) -> [simd_float3] -} diff --git a/Engine/Core/Scene/Bounds/BoundingBox/PNIBoundingBoxInteractor.swift b/Engine/Core/Scene/Bounds/BoundingBox/PNIBoundingBoxInteractor.swift deleted file mode 100644 index 85cb0a3c..00000000 --- a/Engine/Core/Scene/Bounds/BoundingBox/PNIBoundingBoxInteractor.swift +++ /dev/null @@ -1,143 +0,0 @@ -// -// Copyright © 2021 Mateusz Stompór. All rights reserved. -// - -import simd - -public struct PNIBoundingBoxInteractor: PNBoundingBoxInteractor { - private let boundInteractor: PNBoundInteractor - init(boundInteractor: PNBoundInteractor) { - self.boundInteractor = boundInteractor - } - public func corners(_ bound: PNBoundingBox) -> [simd_float3] { - [bound.cornersLower.columns.0.xyz, - bound.cornersLower.columns.1.xyz, - bound.cornersLower.columns.2.xyz, - bound.cornersLower.columns.3.xyz, - bound.cornersUpper.columns.0.xyz, - bound.cornersUpper.columns.1.xyz, - bound.cornersUpper.columns.2.xyz, - bound.cornersUpper.columns.3.xyz] - } - public func isEqual(_ lhs: PNBoundingBox, _ rhs: PNBoundingBox) -> Bool { - boundInteractor.isEqual(bound(lhs), bound(rhs)) - } - public func multiply(_ lhs: simd_float4x4, _ rhs: PNBoundingBox) -> PNBoundingBox { - PNBoundingBox(cornersLower: lhs * rhs.cornersLower, - cornersUpper: lhs * rhs.cornersUpper) - } - public func bound(_ boundingBox: PNBoundingBox) -> PNBound { - let minX = min(boundingBox.cornersLower.columns.0.x, - boundingBox.cornersLower.columns.1.x, - boundingBox.cornersLower.columns.2.x, - boundingBox.cornersLower.columns.3.x, - boundingBox.cornersUpper.columns.0.x, - boundingBox.cornersUpper.columns.1.x, - boundingBox.cornersUpper.columns.2.x, - boundingBox.cornersUpper.columns.3.x) - let maxX = max(boundingBox.cornersLower.columns.0.x, - boundingBox.cornersLower.columns.1.x, - boundingBox.cornersLower.columns.2.x, - boundingBox.cornersLower.columns.3.x, - boundingBox.cornersUpper.columns.0.x, - boundingBox.cornersUpper.columns.1.x, - boundingBox.cornersUpper.columns.2.x, - boundingBox.cornersUpper.columns.3.x) - let minY = min(boundingBox.cornersLower.columns.0.y, - boundingBox.cornersLower.columns.1.y, - boundingBox.cornersLower.columns.2.y, - boundingBox.cornersLower.columns.3.y, - boundingBox.cornersUpper.columns.0.y, - boundingBox.cornersUpper.columns.1.y, - boundingBox.cornersUpper.columns.2.y, - boundingBox.cornersUpper.columns.3.y) - let maxY = max(boundingBox.cornersLower.columns.0.y, - boundingBox.cornersLower.columns.1.y, - boundingBox.cornersLower.columns.2.y, - boundingBox.cornersLower.columns.3.y, - boundingBox.cornersUpper.columns.0.y, - boundingBox.cornersUpper.columns.1.y, - boundingBox.cornersUpper.columns.2.y, - boundingBox.cornersUpper.columns.3.y) - let minZ = min(boundingBox.cornersLower.columns.0.z, - boundingBox.cornersLower.columns.1.z, - boundingBox.cornersLower.columns.2.z, - boundingBox.cornersLower.columns.3.z, - boundingBox.cornersUpper.columns.0.z, - boundingBox.cornersUpper.columns.1.z, - boundingBox.cornersUpper.columns.2.z, - boundingBox.cornersUpper.columns.3.z) - let maxZ = max(boundingBox.cornersLower.columns.0.z, - boundingBox.cornersLower.columns.1.z, - boundingBox.cornersLower.columns.2.z, - boundingBox.cornersLower.columns.3.z, - boundingBox.cornersUpper.columns.0.z, - boundingBox.cornersUpper.columns.1.z, - boundingBox.cornersUpper.columns.2.z, - boundingBox.cornersUpper.columns.3.z) - return PNBound(min: simd_float3(minX, minY, minZ), - max: simd_float3(maxX, maxY, maxZ)) - } - public func safeBound(_ boundingBox: PNBoundingBox?) -> PNBound? { - guard let boundingBox = boundingBox else { - return nil - } - return bound(boundingBox) - } - public func aabb(_ boundingBox: PNBoundingBox) -> PNBoundingBox { - from(bound: bound(boundingBox)) - } - public func merge(_ lhs: PNBoundingBox, _ rhs: PNBoundingBox) -> PNBoundingBox { - from(bound: boundInteractor.merge(bound(lhs), rhs: bound(rhs))) - } - public func overlap(_ lhs: PNBoundingBox, _ rhs: PNBoundingBox) -> Bool { - boundInteractor.overlap(bound(lhs), bound(rhs)) - } - public func from(bound: PNBound) -> PNBoundingBox { - PNBoundingBox(cornersLower: simd_float4x4(simd_float4(bound.min.x, bound.min.y, bound.min.z, 1), - simd_float4(bound.max.x, bound.min.y, bound.min.z, 1), - simd_float4(bound.min.x, bound.min.y, bound.max.z, 1), - simd_float4(bound.max.x, bound.min.y, bound.max.z, 1) - ), - cornersUpper: simd_float4x4(simd_float4(bound.min.x, bound.max.y, bound.min.z, 1), - simd_float4(bound.max.x, bound.max.y, bound.min.z, 1), - simd_float4(bound.min.x, bound.max.y, bound.max.z, 1), - simd_float4(bound.max.x, bound.max.y, bound.max.z, 1) - )) - } - public func from(_ corners: [simd_float3]) -> PNBoundingBox { - assert(corners.count == 8, "Each point of the bounding box must be defined") - return PNBoundingBox(cornersLower: simd_float4x4(simd_float4(corners[0].x, corners[0].y, corners[0].z, 1), - simd_float4(corners[1].x, corners[1].y, corners[1].z, 1), - simd_float4(corners[2].x, corners[2].y, corners[2].z, 1), - simd_float4(corners[3].x, corners[3].y, corners[3].z, 1)), - cornersUpper: simd_float4x4(simd_float4(corners[4].x, corners[4].y, corners[4].z, 1), - simd_float4(corners[5].x, corners[5].y, corners[5].z, 1), - simd_float4(corners[6].x, corners[6].y, corners[6].z, 1), - simd_float4(corners[7].x, corners[7].y, corners[7].z, 1))) - } - public func from(inverseProjection: simd_float4x4) -> PNBoundingBox { - var lower = inverseProjection * simd_float4x4(simd_float4(-1, -1, 0, 1), - simd_float4(1, -1, 0, 1), - simd_float4(-1, -1, 1, 1), - simd_float4(1, -1, 1, 1)) - var upper = inverseProjection * simd_float4x4(simd_float4(-1, 1, 0, 1), - simd_float4(1, 1, 0, 1), - simd_float4(-1, 1, 1, 1), - simd_float4(1, 1, 1, 1)) - // Lower - lower.columns.0 /= lower.columns.0.w - lower.columns.1 /= lower.columns.1.w - lower.columns.2 /= lower.columns.2.w - lower.columns.3 /= lower.columns.3.w - // Upper - upper.columns.0 /= upper.columns.0.w - upper.columns.1 /= upper.columns.1.w - upper.columns.2 /= upper.columns.2.w - upper.columns.3 /= upper.columns.3.w - return aabb(PNBoundingBox(cornersLower: lower, cornersUpper: upper)) - } - public static var `default`: PNBoundingBoxInteractor { - PNIBoundingBoxInteractor(boundInteractor: PNIBoundInteractor()) - } -} diff --git a/Engine/Core/Scene/Camera/PNCamera.swift b/Engine/Core/Scene/Camera/PNCamera.swift index db10b644..bcc799ae 100644 --- a/Engine/Core/Scene/Camera/PNCamera.swift +++ b/Engine/Core/Scene/Camera/PNCamera.swift @@ -9,5 +9,5 @@ import simd public protocol PNCamera { var projectionMatrix: matrix_float4x4 { get } var projectionMatrixInverse: matrix_float4x4 { get } - var boundingBox: PNBoundingBox { get } + var bound: PNBound { get } } diff --git a/Engine/Core/Scene/Camera/PNOrthographicCamera.swift b/Engine/Core/Scene/Camera/PNOrthographicCamera.swift index e0f2d58f..5f89cad0 100644 --- a/Engine/Core/Scene/Camera/PNOrthographicCamera.swift +++ b/Engine/Core/Scene/Camera/PNOrthographicCamera.swift @@ -9,10 +9,10 @@ import simd public struct PNOrthographicCamera: PNCamera { public let projectionMatrix: matrix_float4x4 public let projectionMatrixInverse: matrix_float4x4 - public let boundingBox: PNBoundingBox + public let bound: PNBound public init(bound: PNBound) { projectionMatrix = matrix_float4x4.orthographicProjection(bound: bound) projectionMatrixInverse = projectionMatrix.inverse - boundingBox = PNIBoundingBoxInteractor.default.from(inverseProjection: projectionMatrixInverse) + self.bound = PNIBoundInteractor().from(inverseProjection: projectionMatrixInverse) } } diff --git a/Engine/Core/Scene/Camera/PNPerspectiveCamera.swift b/Engine/Core/Scene/Camera/PNPerspectiveCamera.swift index 082c7917..8f82fb83 100644 --- a/Engine/Core/Scene/Camera/PNPerspectiveCamera.swift +++ b/Engine/Core/Scene/Camera/PNPerspectiveCamera.swift @@ -9,7 +9,7 @@ import simd public struct PNPerspectiveCamera: PNCamera { public let projectionMatrix: matrix_float4x4 public let projectionMatrixInverse: matrix_float4x4 - public let boundingBox: PNBoundingBox + public let bound: PNBound public init(nearPlane: Float, farPlane: Float, fovRadians: Float, @@ -19,6 +19,6 @@ public struct PNPerspectiveCamera: PNCamera { nearZ: nearPlane, farZ: farPlane) projectionMatrixInverse = projectionMatrix.inverse - boundingBox = PNIBoundingBoxInteractor.default.from(inverseProjection: projectionMatrixInverse) + bound = PNIBoundInteractor().from(inverseProjection: projectionMatrixInverse) } } diff --git a/Engine/Core/Scene/Light/PNAmbientLight.swift b/Engine/Core/Scene/Light/PNAmbientLight.swift index 490a0b7f..db494c59 100644 --- a/Engine/Core/Scene/Light/PNAmbientLight.swift +++ b/Engine/Core/Scene/Light/PNAmbientLight.swift @@ -10,5 +10,5 @@ public protocol PNAmbientLight { var diameter: Float { get } var color: PNColorRGB { get } var intensity: Float { get } - var boundingBox: PNBoundingBox { get } + var bound: PNBound { get } } diff --git a/Engine/Core/Scene/Light/PNIAmbientLight.swift b/Engine/Core/Scene/Light/PNIAmbientLight.swift index e7c1c703..be38f6d5 100644 --- a/Engine/Core/Scene/Light/PNIAmbientLight.swift +++ b/Engine/Core/Scene/Light/PNIAmbientLight.swift @@ -8,7 +8,7 @@ public struct PNIAmbientLight: PNAmbientLight { public let diameter: Float public let color: PNColorRGB public let intensity: Float - public let boundingBox: PNBoundingBox + public let bound: PNBound public init(diameter: Float, color: PNColorRGB, intensity: Float) { @@ -16,12 +16,8 @@ public struct PNIAmbientLight: PNAmbientLight { self.diameter = diameter self.color = color self.intensity = intensity - self.boundingBox = PNIAmbientLight.boundingBox(diameter: diameter) - } - private static func boundingBox(diameter: Float) -> PNBoundingBox { let radius = diameter / 2 - let bound = PNBound(min: [-radius, -radius, -radius], - max: [radius, radius, radius]) - return PNIBoundingBoxInteractor.default.from(bound: bound) + self.bound = PNBound(min: [-radius, -radius, -radius], + max: [radius, radius, radius]) } } diff --git a/Engine/Core/Scene/Light/PNIOmniLight.swift b/Engine/Core/Scene/Light/PNIOmniLight.swift index 8c96051a..28b308fe 100644 --- a/Engine/Core/Scene/Light/PNIOmniLight.swift +++ b/Engine/Core/Scene/Light/PNIOmniLight.swift @@ -13,7 +13,7 @@ public struct PNIOmniLight: PNOmniLight { public let castsShadows: Bool public let projectionMatrix: simd_float4x4 public let projectionMatrixInverse: simd_float4x4 - public let boundingBox: PNBoundingBox + public let bound: PNBound public init(color: PNColorRGB, intensity: Float, influenceRadius: Float, @@ -29,24 +29,23 @@ public struct PNIOmniLight: PNOmniLight { nearZ: nearPlance, farZ: influenceRadius) self.projectionMatrixInverse = projectionMatrix.inverse - self.boundingBox = PNIOmniLight.boundingBox(projectionMatrixInverse: projectionMatrixInverse) + self.bound = PNIOmniLight.computeBound(projectionMatrixInverse: projectionMatrixInverse) self.nearPlane = nearPlance self.farPlane = influenceRadius } - private static func boundingBox(projectionMatrixInverse: simd_float4x4) -> PNBoundingBox { - // TODO: Ensure that calling aabb(interactor.multiply(axis, projection)) is not needed - let interactor = PNIBoundingBoxInteractor.default - let projectionBoundingBox = interactor.from(inverseProjection: projectionMatrixInverse) - guard let boundingBox = [ - interactor.multiply(PNSurroundings.positiveX, projectionBoundingBox), - interactor.multiply(PNSurroundings.negativeX, projectionBoundingBox), - interactor.multiply(PNSurroundings.positiveY, projectionBoundingBox), - interactor.multiply(PNSurroundings.negativeY, projectionBoundingBox), - interactor.multiply(PNSurroundings.positiveZ, projectionBoundingBox), - interactor.multiply(PNSurroundings.negativeZ, projectionBoundingBox) - ].reduce(interactor.merge) else { + private static func computeBound(projectionMatrixInverse: simd_float4x4) -> PNBound { + let interactor = PNIBoundInteractor() + let projectionBound = interactor.from(inverseProjection: projectionMatrixInverse) + guard let merged = [ + interactor.multiply(PNSurroundings.positiveX, projectionBound), + interactor.multiply(PNSurroundings.negativeX, projectionBound), + interactor.multiply(PNSurroundings.positiveY, projectionBound), + interactor.multiply(PNSurroundings.negativeY, projectionBound), + interactor.multiply(PNSurroundings.positiveZ, projectionBound), + interactor.multiply(PNSurroundings.negativeZ, projectionBound) + ].reduce({ interactor.merge($0, rhs: $1) }) else { fatalError("Reduce returned nil even if it never should in this circumstances") } - return boundingBox + return merged } } diff --git a/Engine/Core/Scene/Light/PNISpotLight.swift b/Engine/Core/Scene/Light/PNISpotLight.swift index 7b8fdee2..c9ce1e38 100644 --- a/Engine/Core/Scene/Light/PNISpotLight.swift +++ b/Engine/Core/Scene/Light/PNISpotLight.swift @@ -14,7 +14,7 @@ public struct PNISpotLight: PNSpotLight { public let castsShadows: Bool public let projectionMatrix: simd_float4x4 public let projectionMatrixInverse: simd_float4x4 - public let boundingBox: PNBoundingBox + public let bound: PNBound public init(color: PNColorRGB, intensity: Float, influenceRadius: Float, @@ -33,7 +33,7 @@ public struct PNISpotLight: PNSpotLight { self.projectionMatrix = PNISpotLight.projectionMatrix(coneAngle: coneAngle, influenceRadius: influenceRadius) self.projectionMatrixInverse = projectionMatrix.inverse - self.boundingBox = PNIBoundingBoxInteractor.default.from(inverseProjection: projectionMatrixInverse) + self.bound = PNIBoundInteractor().from(inverseProjection: projectionMatrixInverse) } static func projectionMatrix(coneAngle: PNRadians, influenceRadius: Float) -> simd_float4x4 { simd_float4x4.perspectiveProjectionRightHand(fovyRadians: coneAngle, diff --git a/Engine/Core/Scene/Light/PNOmniLight.swift b/Engine/Core/Scene/Light/PNOmniLight.swift index 30578dc8..df697539 100644 --- a/Engine/Core/Scene/Light/PNOmniLight.swift +++ b/Engine/Core/Scene/Light/PNOmniLight.swift @@ -22,7 +22,7 @@ public protocol PNOmniLight { /// The inverse of the projection matrix. var projectionMatrixInverse: simd_float4x4 { get } /// The bounding box of the light in world space. - var boundingBox: PNBoundingBox { get } + var bound: PNBound { get } /// The far plane distance for view/projection calculations. var farPlane: Float { get } /// The near plane distance for view/projection calculations. diff --git a/Engine/Core/Scene/Light/PNSpotLight.swift b/Engine/Core/Scene/Light/PNSpotLight.swift index ceecf339..9b01d5e8 100644 --- a/Engine/Core/Scene/Light/PNSpotLight.swift +++ b/Engine/Core/Scene/Light/PNSpotLight.swift @@ -19,5 +19,5 @@ public protocol PNSpotLight { var castsShadows: Bool { get } var projectionMatrix: simd_float4x4 { get } var projectionMatrixInverse: simd_float4x4 { get } - var boundingBox: PNBoundingBox { get } + var bound: PNBound { get } } diff --git a/Engine/Core/Scene/Mesh/PNBoundingBoxCreator.swift b/Engine/Core/Scene/Mesh/PNBoundingBoxCreator.swift index fac427b7..223dc1c6 100644 --- a/Engine/Core/Scene/Mesh/PNBoundingBoxCreator.swift +++ b/Engine/Core/Scene/Mesh/PNBoundingBoxCreator.swift @@ -5,58 +5,29 @@ import PNShared enum PNBoundingBoxCreator { - static func vertices(boundingBoxes: [PNBoundingBox?]) -> [VertexP] { - boundingBoxes.compactMap { $0 } .map { vertices(bb: $0) }.reduce(+) ?? [] + private static let interactor = PNIBoundInteractor() + static func vertices(bounds: [PNBound?]) -> [VertexP] { + bounds.compactMap { $0 }.map { vertices(bound: $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) + static private func vertices(bound: PNBound) -> [VertexP] { + let c = interactor.corners(bound) + return [ + VertexP(position: c[0]), VertexP(position: c[1]), + VertexP(position: c[1]), VertexP(position: c[3]), + VertexP(position: c[1]), VertexP(position: c[2]), + VertexP(position: c[2]), VertexP(position: c[3]), + VertexP(position: c[2]), VertexP(position: c[0]), + VertexP(position: c[3]), VertexP(position: c[0]), + VertexP(position: c[4]), VertexP(position: c[0]), + VertexP(position: c[5]), VertexP(position: c[1]), + VertexP(position: c[6]), VertexP(position: c[2]), + VertexP(position: c[7]), VertexP(position: c[3]), + VertexP(position: c[4]), VertexP(position: c[5]), + VertexP(position: c[5]), VertexP(position: c[7]), + VertexP(position: c[5]), VertexP(position: c[6]), + VertexP(position: c[6]), VertexP(position: c[7]), + VertexP(position: c[6]), VertexP(position: c[4]), + VertexP(position: c[7]), VertexP(position: c[4]) ] } } diff --git a/Engine/Core/Scene/Mesh/PNMesh.swift b/Engine/Core/Scene/Mesh/PNMesh.swift index 70769b64..33cf5e67 100644 --- a/Engine/Core/Scene/Mesh/PNMesh.swift +++ b/Engine/Core/Scene/Mesh/PNMesh.swift @@ -4,17 +4,17 @@ /// Mesh is a 3D object representation consisting of a collection of vertices and polygons. public final class PNMesh { - /// The spatial bounding box that encapsulates the mesh. - public let boundingBox: PNBoundingBox + /// The spatial bounds that encapsulate the mesh. + public let bound: PNBound /// Buffer containing the vertex data used to render the mesh. public let vertexBuffer: PNDataBuffer /// Descriptions of the individual segments of the mesh. public var pieceDescriptions: [PNPieceDescription] - /// Initializes a mesh with a bounding box, vertex buffer, piece descriptions, and culling strategy. - public init(boundingBox: PNBoundingBox, + /// Initializes a mesh with bounds, vertex buffer, and piece descriptions. + public init(bound: PNBound, vertexBuffer: PNDataBuffer, pieceDescriptions: [PNPieceDescription]) { - self.boundingBox = boundingBox + self.bound = bound self.vertexBuffer = vertexBuffer self.pieceDescriptions = pieceDescriptions } diff --git a/Engine/Core/Translation/Culling/PNCullingController.swift b/Engine/Core/Translation/Culling/PNCullingController.swift index c6cc03a3..533d0d45 100644 --- a/Engine/Core/Translation/Culling/PNCullingController.swift +++ b/Engine/Core/Translation/Culling/PNCullingController.swift @@ -3,6 +3,5 @@ // protocol PNCullingController { - func cullingMask(scene: PNSceneDescription, - boundingBox: PNWBoundingBox) -> [Bool] + func cullingMask(scene: PNSceneDescription, bound: PNBound) -> [Bool] } diff --git a/Engine/Core/Translation/Culling/PNICullingController.swift b/Engine/Core/Translation/Culling/PNICullingController.swift index 7614f630..c7973d40 100644 --- a/Engine/Core/Translation/Culling/PNICullingController.swift +++ b/Engine/Core/Translation/Culling/PNICullingController.swift @@ -5,15 +5,14 @@ import PNShared struct PNICullingController: PNCullingController { - private let interactor: PNBoundingBoxInteractor - init(interactor: PNBoundingBoxInteractor) { + private let interactor: PNBoundInteractor + init(interactor: PNBoundInteractor) { self.interactor = interactor } - func cullingMask(scene: PNSceneDescription, - boundingBox: PNWBoundingBox) -> [Bool] { - scene.boundingBoxes.indices.map { - if let box = scene.boundingBoxes[$0] { - return interactor.overlap(box, boundingBox) + func cullingMask(scene: PNSceneDescription, bound: PNBound) -> [Bool] { + scene.bounds.indices.map { + if let box = scene.bounds[$0] { + return interactor.overlap(box, bound) } else { return true } diff --git a/Engine/Core/Translation/PNSceneDescription.swift b/Engine/Core/Translation/PNSceneDescription.swift index 606c1da0..fe5e1470 100644 --- a/Engine/Core/Translation/PNSceneDescription.swift +++ b/Engine/Core/Translation/PNSceneDescription.swift @@ -11,7 +11,7 @@ public final class PNSceneDescription { // MARK: - Capacity A public var entities = PNEntityTree() public var uniforms = [PNWModelUniforms]() - public var boundingBoxes = [PNWBoundingBox?]() + public var bounds = [PNBound?]() // MARK: - Capacity B public var models = [PNModelReference]() // MARK: - Capacity C diff --git a/Engine/Core/Translation/PNSceneValidator.swift b/Engine/Core/Translation/PNSceneValidator.swift index 91ec1c29..ec7982b3 100644 --- a/Engine/Core/Translation/PNSceneValidator.swift +++ b/Engine/Core/Translation/PNSceneValidator.swift @@ -5,7 +5,7 @@ func validate(scene: PNSceneDescription) -> Bool { let entityReferences = cmp(scene.entities.count, scene.uniforms.count, - scene.boundingBoxes.count) + scene.bounds.count) let paletteReferences = cmp(scene.animatedModels.count, scene.paletteOffset.count) let cameraReferences = cmp(scene.cameras.count, diff --git a/Engine/Core/Translation/RenderMask/PNIRenderMaskGenerator.swift b/Engine/Core/Translation/RenderMask/PNIRenderMaskGenerator.swift index 839fa64a..f258ef06 100644 --- a/Engine/Core/Translation/RenderMask/PNIRenderMaskGenerator.swift +++ b/Engine/Core/Translation/RenderMask/PNIRenderMaskGenerator.swift @@ -6,11 +6,8 @@ import simd public struct PNIRenderMaskGenerator: PNRenderMaskGenerator { private let cullingController: PNCullingController - private let interactor: PNBoundingBoxInteractor - init(cullingController: PNCullingController, - interactor: PNBoundingBoxInteractor) { + init(cullingController: PNCullingController) { self.cullingController = cullingController - self.interactor = interactor } public func generate(scene: PNSceneDescription) -> PNRenderMask { PNRenderMask(cameras: generateCameraRenderMask(scene: scene), @@ -33,9 +30,9 @@ public struct PNIRenderMaskGenerator: PNRenderMaskGenerator { } } private func mask(scene: PNSceneDescription, for index: Int) -> [Bool] { - guard let bb = scene.boundingBoxes[index] else { - return Array(repeating: false, count: scene.boundingBoxes.count) + guard let bound = scene.bounds[index] else { + return Array(repeating: false, count: scene.bounds.count) } - return cullingController.cullingMask(scene: scene, boundingBox: bb) + return cullingController.cullingMask(scene: scene, bound: bound) } } diff --git a/Engine/Core/Translation/Transcriber/PNITranscriber.swift b/Engine/Core/Translation/Transcriber/PNITranscriber.swift index 26cd26cf..0e386d4c 100644 --- a/Engine/Core/Translation/Transcriber/PNITranscriber.swift +++ b/Engine/Core/Translation/Transcriber/PNITranscriber.swift @@ -3,13 +3,14 @@ // import PNShared +import simd struct PNITranscriber: PNTranscriber { - private let interactor: PNBoundingBoxInteractor + private let interactor: PNBoundInteractor private let paletteGenerator: PNPaletteGenerator - init(boundingBoxInteractor: PNBoundingBoxInteractor, + init(interactor: PNBoundInteractor, paletteGenerator: PNPaletteGenerator) { - self.interactor = boundingBoxInteractor + self.interactor = interactor self.paletteGenerator = paletteGenerator } func transcribe(scene: PNScene) -> PNSceneDescription { @@ -24,14 +25,13 @@ struct PNITranscriber: PNTranscriber { return sceneDescription } private func write(lights: [PNDirectionalLight], scene: PNSceneDescription) { - // Getting bounding box of root node - guard let sceneBB = scene.boundingBoxes[0] else { + guard let sceneBound = scene.bounds[0] else { return } for light in lights { let orientation = simd_float4x4.from(directionVector: light.direction) let orientationInverse = orientation.inverse - let bound = interactor.bound(interactor.aabb(interactor.multiply(orientationInverse, sceneBB))) + let bound = interactor.multiply(orientationInverse, sceneBound) let projectionMatrix = simd_float4x4.orthographicProjection(bound: bound) scene.directionalLights.append(DirectionalLight(color: light.color, intensity: light.intensity, @@ -46,14 +46,13 @@ struct PNITranscriber: PNTranscriber { node.data.update() let index = node.data.write(scene: scene, parentIdx: parentIndex) scene.uniforms.append(node.data.modelUniforms) - scene.boundingBoxes.append(node.data.worldBoundingBox) + scene.bounds.append(node.data.worldBound) node.children.forEach { write(node: $0, scene: scene, parentIndex: index) } } static var `default`: PNITranscriber { - let boundingBoxInteractor = PNIBoundingBoxInteractor.default - return PNITranscriber(boundingBoxInteractor: boundingBoxInteractor, - paletteGenerator: PNIPaletteGenerator()) + PNITranscriber(interactor: PNIBoundInteractor(), + paletteGenerator: PNIPaletteGenerator()) } } diff --git a/Engine/Core/UI/PNIScreenInteractor.swift b/Engine/Core/UI/PNIScreenInteractor.swift index 9937871c..3086064c 100644 --- a/Engine/Core/UI/PNIScreenInteractor.swift +++ b/Engine/Core/UI/PNIScreenInteractor.swift @@ -7,7 +7,6 @@ import ZPack class PNIScreenInteractor: PNScreenInteractor { private let boundInteractor = PNIBoundInteractor() - private let boundingBoxInteractor = PNIBoundingBoxInteractor.default private let nodeInteractor = PNINodeInteractor() init() { // Default @@ -26,10 +25,10 @@ class PNIScreenInteractor: PNScreenInteractor { let rayEye = PNRay(origin: origin, direction: direction.xyz.normalized) let rayWorld = cameraNode.worldTransform * rayEye return nodeInteractor.deepSearch(from: scene.rootNode) { node in - guard let value = node.data.worldBoundingBox else { + guard let value = node.data.worldBound else { return false } - return boundInteractor.intersect(boundingBoxInteractor.bound(value), ray: rayWorld) + return boundInteractor.intersect(value, ray: rayWorld) } } } diff --git a/EngineTests/Engine/Rendering/Transcriber/PNITranscriber+Tests.swift b/EngineTests/Engine/Rendering/Transcriber/PNITranscriber+Tests.swift index 4d83f928..a6a45190 100644 --- a/EngineTests/Engine/Rendering/Transcriber/PNITranscriber+Tests.swift +++ b/EngineTests/Engine/Rendering/Transcriber/PNITranscriber+Tests.swift @@ -12,7 +12,7 @@ class PNITranscriberTests: XCTestCase { let scene = transcriber.transcribe(scene: PNScene.default) XCTAssertEqual(scene.entities.count, 1) XCTAssertEqual(scene.uniforms.count, 1) - XCTAssertEqual(scene.boundingBoxes.count, 1) + XCTAssertEqual(scene.bounds.count, 1) XCTAssertTrue(scene.models.isEmpty) XCTAssertTrue(scene.animatedModels.isEmpty) XCTAssertTrue(scene.paletteOffset.isEmpty) diff --git a/EngineTests/Engine/Scene/Bounds/Bound/PNIBoundInteractor/PNIBoundInteractor+Extended+Tests.swift b/EngineTests/Engine/Scene/Bounds/Bound/PNIBoundInteractor/PNIBoundInteractor+Extended+Tests.swift new file mode 100644 index 00000000..918642c8 --- /dev/null +++ b/EngineTests/Engine/Scene/Bounds/Bound/PNIBoundInteractor/PNIBoundInteractor+Extended+Tests.swift @@ -0,0 +1,132 @@ +// +// Copyright © 2021 Mateusz Stompór. All rights reserved. +// + +@testable import Engine +import simd +import XCTest + +class PNIBoundInteractorExtendedTests: XCTestCase { + let interactor = PNIBoundInteractor() + let bounds = PNBound(min: [-1, -2, -3], max: [4, 5, 6]) + func testTransformations() throws { + let result = interactor.multiply(simd_float4x4.scale([2, 3, 4]), bounds) + let corners = interactor.corners(result) + XCTAssertEqual(corners[0], [-2, -6, -12]) + XCTAssertEqual(corners[1], [8, -6, -12]) + XCTAssertEqual(corners[2], [-2, -6, 24]) + XCTAssertEqual(corners[3], [8, -6, 24]) + XCTAssertEqual(corners[4], [-2, 15, -12]) + XCTAssertEqual(corners[5], [8, 15, -12]) + XCTAssertEqual(corners[6], [-2, 15, 24]) + XCTAssertEqual(corners[7], [8, 15, 24]) + } + func testCorners() throws { + let corners = interactor.corners(bounds) + XCTAssertEqual(corners[0], [-1, -2, -3]) + XCTAssertEqual(corners[1], [4, -2, -3]) + XCTAssertEqual(corners[2], [-1, -2, 6]) + XCTAssertEqual(corners[3], [4, -2, 6]) + XCTAssertEqual(corners[4], [-1, 5, -3]) + XCTAssertEqual(corners[5], [4, 5, -3]) + XCTAssertEqual(corners[6], [-1, 5, 6]) + XCTAssertEqual(corners[7], [4, 5, 6]) + } + func testFromCorners() throws { + let corners: [simd_float3] = [[0, 0, 0], [2, 0, 0], [0, 1, 3], [2, 1, 3], + [0, 3, 0], [2, 3, 0], [0, 4, 3], [2, 4, 3]] + let result = interactor.from(corners) + XCTAssertEqual(result.min, [0, 0, 0]) + XCTAssertEqual(result.max, [2, 4, 3]) + } + func testMergeTheSameBox() throws { + let zero = PNBound(min: .zero, max: .zero) + let merged = interactor.merge(zero, rhs: zero) + XCTAssertEqual(merged.min, .zero) + XCTAssertEqual(merged.max, .zero) + } + func testMergeDisjointBoxes() throws { + let boundA = PNBound(min: [0, 0, 0], max: [2, 2, 2]) + let boundB = PNBound(min: [-4, -4, -4], max: [-2, -2, -2]) + let merged = interactor.merge(boundA, rhs: boundB) + XCTAssertEqual(merged.min, [-4, -4, -4]) + XCTAssertEqual(merged.max, [2, 2, 2]) + } + func testMultiplication() throws { + let b = PNBound(min: .zero, max: [2, 2, 2]) + let translated = interactor.multiply(simd_float4x4.translation(vector: [1, 2, 3]), b) + XCTAssertEqual(translated.min, [1, 2, 3]) + XCTAssertEqual(translated.max, [3, 4, 5]) + } + func testOverlapping() throws { + let boxA = PNBound(min: [0, 0, 0], max: [2, 2, 2]) + let boxB = PNBound(min: [-2, -2, -2], max: [0.1, 0.1, 0.1]) + XCTAssertTrue(interactor.overlap(boxA, boxB)) + } + func testNotOverlapping() throws { + let boxA = PNBound(min: [0, 0, 0], max: [2, 2, 2]) + let boxB = PNBound(min: [-2, -2, -2], max: [-0.1, -0.1, -0.1]) + XCTAssertFalse(interactor.overlap(boxA, boxB)) + } + func testIsEqualSameValues() throws { + let a = PNBound(min: .zero, max: .zero) + let b = PNBound(min: .zero, max: .zero) + XCTAssertTrue(interactor.isEqual(a, b)) + } + func testNotEqual() throws { + let a = PNBound(min: .zero, max: .zero) + let b = PNBound(min: [1, 1, 1], max: [3, 3, 3]) + XCTAssertFalse(interactor.isEqual(a, b)) + } + func testCornersCount() throws { + XCTAssertEqual(interactor.corners(bounds).count, 8) + } + func testCornersRoundTrip() throws { + let result = interactor.from(interactor.corners(bounds)) + XCTAssertEqual(result.min, bounds.min) + XCTAssertEqual(result.max, bounds.max) + } + func testFromCornersUnordered() throws { + let points: [simd_float3] = [[3, 0, -1], [-2, 5, 4], [1, -3, 0], [0, 2, -5]] + let result = interactor.from(points) + XCTAssertEqual(result.min, [-2, -3, -5]) + XCTAssertEqual(result.max, [3, 5, 4]) + } + func testMultiplyIdentity() throws { + let result = interactor.multiply(.identity, bounds) + XCTAssertEqual(result.min, bounds.min) + XCTAssertEqual(result.max, bounds.max) + } + func testMultiplyRotation() throws { + // 90° rotation around Y: (x,y,z) → (z, y, -x) + // Bound min=[0,0,0] max=[2,1,3] becomes min=[0,0,-2] max=[3,1,0] + let b = PNBound(min: [0, 0, 0], max: [2, 1, 3]) + let rotation = simd_float4x4(simd_quatf(angle: .pi / 2, axis: [0, 1, 0])) + let result = interactor.multiply(rotation, b) + XCTAssertEqual(result.min.x, 0, accuracy: 1e-5) + XCTAssertEqual(result.min.y, 0, accuracy: 1e-5) + XCTAssertEqual(result.min.z, -2, accuracy: 1e-5) + XCTAssertEqual(result.max.x, 3, accuracy: 1e-5) + XCTAssertEqual(result.max.y, 1, accuracy: 1e-5) + XCTAssertEqual(result.max.z, 0, accuracy: 1e-5) + } + func testFromInverseProjectionOrthographic() throws { + let original = PNBound(min: [-5, -3, 0], max: [5, 3, 10]) + let proj = simd_float4x4.orthographicProjection(bound: original) + let result = interactor.from(inverseProjection: proj.inverse) + XCTAssertEqual(result.min.x, original.min.x, accuracy: 1e-4) + XCTAssertEqual(result.min.y, original.min.y, accuracy: 1e-4) + XCTAssertEqual(result.max.x, original.max.x, accuracy: 1e-4) + XCTAssertEqual(result.max.y, original.max.y, accuracy: 1e-4) + } + func testFromInverseProjectionPerspectiveIsNonDegenerate() throws { + let proj = simd_float4x4.perspectiveProjectionRightHand(fovyRadians: Float(60).radians, + aspect: 16.0 / 9.0, + nearZ: 0.1, + farZ: 100) + let result = interactor.from(inverseProjection: proj.inverse) + XCTAssertLessThan(result.min.x, result.max.x) + XCTAssertLessThan(result.min.y, result.max.y) + XCTAssertLessThan(result.min.z, result.max.z) + } +} diff --git a/EngineTests/Engine/Scene/Bounds/BoundingBox/PNIBoundingBoxInteractor+Tests.swift b/EngineTests/Engine/Scene/Bounds/BoundingBox/PNIBoundingBoxInteractor+Tests.swift deleted file mode 100644 index ccf2f425..00000000 --- a/EngineTests/Engine/Scene/Bounds/BoundingBox/PNIBoundingBoxInteractor+Tests.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright © 2021 Mateusz Stompór. All rights reserved. -// - -@testable import Engine -import simd -import XCTest - -class PNIBoundingBoxInteractorTests: XCTestCase { - let interactor: PNBoundingBoxInteractor = PNIBoundingBoxInteractor.default - let boundInteractor: PNBoundInteractor = PNIBoundInteractor() - let bounds = PNBound(min: [-1, -2, -3], max: [4, 5, 6]) - func testTransformations() throws { - let boundingBox = interactor.from(bound: bounds) - let result = interactor.multiply(simd_float4x4.scale([2, 3, 4]), boundingBox) - let corners = interactor.corners(result) - XCTAssertEqual(corners[0], [-2, -6, -12]) - XCTAssertEqual(corners[1], [8, -6, -12]) - XCTAssertEqual(corners[2], [-2, -6, 24]) - XCTAssertEqual(corners[3], [8, -6, 24]) - XCTAssertEqual(corners[4], [-2, 15, -12]) - XCTAssertEqual(corners[5], [8, 15, -12]) - XCTAssertEqual(corners[6], [-2, 15, 24]) - XCTAssertEqual(corners[7], [8, 15, 24]) - } - func testCreationFromBounds() throws { - let boundingBox = interactor.from(bound: bounds) - let corners = interactor.corners(boundingBox) - XCTAssertEqual(corners[0], [-1, -2, -3]) - XCTAssertEqual(corners[1], [4, -2, -3]) - XCTAssertEqual(corners[2], [-1, -2, 6]) - XCTAssertEqual(corners[3], [4, -2, 6]) - XCTAssertEqual(corners[4], [-1, 5, -3]) - XCTAssertEqual(corners[5], [4, 5, -3]) - XCTAssertEqual(corners[6], [-1, 5, 6]) - XCTAssertEqual(corners[7], [4, 5, 6]) - } - func testBound() throws { - let boundingBox = interactor.from(bound: bounds) - let retrievedBound = interactor.bound(boundingBox) - XCTAssertEqual(bounds.min, retrievedBound.min) - XCTAssertEqual(bounds.max, retrievedBound.max) - } - func testAABB() throws { - let boundingBox = interactor.from(bound: bounds) - let aaBoundingBox = interactor.aabb(boundingBox) - XCTAssertEqual(interactor.corners(boundingBox), - interactor.corners(aaBoundingBox)) - } - func testAABBFromOOBB() throws { - let boundingBox = interactor.from([[0, 0, 0], [2, 0, 0], [0, 1, 3], [2, 1, 3], - [0, 3, 0], [2, 3, 0], [0, 4, 3], [2, 4, 3]]) - let aabb = interactor.aabb(boundingBox) - let corners = interactor.corners(aabb) - XCTAssertEqual(corners[0], [0, 0, 0]) - XCTAssertEqual(corners[1], [2, 0, 0]) - XCTAssertEqual(corners[2], [0, 0, 3]) - XCTAssertEqual(corners[3], [2, 0, 3]) - XCTAssertEqual(corners[4], [0, 4, 0]) - XCTAssertEqual(corners[5], [2, 4, 0]) - XCTAssertEqual(corners[6], [0, 4, 3]) - XCTAssertEqual(corners[7], [2, 4, 3]) - } - func testMergeTheSameBox() throws { - let minimalBound = PNBound(min: [0, 0, 0], max: [0, 0, 0]) - let boundingBox = interactor.from(bound: minimalBound) - let box = interactor.merge(boundingBox, boundingBox) - let corners = interactor.corners(box) - for i in 8.exclusiveON { - XCTAssertEqual(corners[i], [0, 0, 0]) - } - } - func testMergeDisjointBoxes() throws { - let boundA = PNBound(min: [0, 0, 0], max: [2, 2, 2]) - let boundB = PNBound(min: [-4, -4, -4], max: [-2, -2, -2]) - let merged = interactor.merge(interactor.from(bound: boundA), - interactor.from(bound: boundB)) - XCTAssertTrue(boundInteractor.isEqual(interactor.bound(merged), - PNBound(min: [-4, -4, -4], max: [2, 2, 2]))) - } - func testMultiplication() throws { - let bounds = PNBound(min: .zero, max: [2, 2, 2]) - let boundingBox = interactor.from(bound: bounds) - let translated = interactor.multiply(simd_float4x4.translation(vector: [1, 2, 3]), boundingBox) - let result = interactor.bound(translated) - XCTAssertEqual(result.min, [1, 2, 3]) - XCTAssertEqual(result.max, [3, 4, 5]) - } - func testOverlaping() throws { - let boxA = interactor.from(bound: PNBound(min: [0, 0, 0], - max: [2, 2, 2])) - let boxB = interactor.from(bound: PNBound(min: [-2, -2, -2], - max: [0.1, 0.1, 0.1])) - XCTAssertTrue(interactor.overlap(boxA, boxB)) - } - func testNotOverlaping() throws { - let boxA = interactor.from(bound: PNBound(min: [0, 0, 0], - max: [2, 2, 2])) - let boxB = interactor.from(bound: PNBound(min: [-2, -2, -2], - max: [-0.1, -0.1, -0.1])) - XCTAssertFalse(interactor.overlap(boxA, boxB)) - } - func testIsEqualSameObject() throws { - let box = interactor.from(bound: PNBound(min: [0, 0, 0], max: [0, 0, 0])) - XCTAssertTrue(interactor.isEqual(box, box)) - } - func testIsEqualDifferentObjects() throws { - let objA = interactor.from(bound: PNBound(min: [0, 0, 0], max: [0, 0, 0])) - let objB = interactor.from(bound: PNBound(min: [0, 0, 0], max: [0, 0, 0])) - XCTAssertTrue(interactor.isEqual(objA, objB)) - } - func testNotEqual() throws { - let boxA = - interactor.from(bound: PNBound(min: [0, 0, 0], max: [0, 0, 0])) - let boxB = interactor.from(bound: PNBound(min: [1, 1, 1], max: [3, 3, 3])) - XCTAssertFalse(interactor.isEqual(boxA, boxB)) - } -} diff --git a/EngineTests/Engine/Scene/Organization/Node/Kinds/PNISceneNode+Tests.swift b/EngineTests/Engine/Scene/Organization/Node/Kinds/PNISceneNode+Tests.swift index 070fabbd..f534c940 100644 --- a/EngineTests/Engine/Scene/Organization/Node/Kinds/PNISceneNode+Tests.swift +++ b/EngineTests/Engine/Scene/Organization/Node/Kinds/PNISceneNode+Tests.swift @@ -7,18 +7,17 @@ import simd import XCTest class PNISceneNodeTests: XCTestCase { - private let interactor = PNIBoundingBoxInteractor.default private let nodeUpdate = PNNodeUpdater() func testSingleNode() throws { let node = PNScenePiece.make(data: PNISceneNode(transform: .translation(vector: [2, 0, 0]))) nodeUpdate.update(rootNode: node) XCTAssertEqual(node.data.transform, .translation(vector: [2, 0, 0])) - XCTAssertNil(node.data.intrinsicBoundingBox) - XCTAssertNil(node.data.localBoundingBox) - XCTAssertNil(node.data.worldBoundingBox) + XCTAssertNil(node.data.intrinsicBound) + XCTAssertNil(node.data.localBound) + XCTAssertNil(node.data.worldBound) XCTAssertEqual(node.data.modelUniforms.modelMatrix.translation, [2, 0, 0]) - XCTAssertNil(node.data.childrenMergedBoundingBox) + XCTAssertNil(node.data.childrenMergedBound) XCTAssertIdentical(node, node.data.enclosingNode) } func testNestedNodes() throws { @@ -73,169 +72,149 @@ class PNISceneNodeTests: XCTestCase { assertion() } func testBoundingBox() throws { - let bb = interactor.from(bound: PNBound(min: [-1, -1, -1], max: [1, 1, 1])) + let bb = PNBound(min: [-1, -1, -1], max: [1, 1, 1]) let node = PNScenePiece.make(data: PNISceneNode(transform: .translation(vector: [1, 2, 3]), - boundingBox: bb)) + bound: bb)) nodeUpdate.update(rootNode: node) XCTAssertEqual(node.data.worldTransform.translation, [1, 2, 3]) XCTAssertEqual(node.data.worldTransform.translation, node.data.transform.translation) - XCTAssertEqual(interactor.safeBound(node.data.worldBoundingBox)?.min, - interactor.safeBound(node.data.localBoundingBox)?.min) - XCTAssertEqual(interactor.safeBound(node.data.worldBoundingBox)?.max, - interactor.safeBound(node.data.localBoundingBox)?.max) - let wbb = node.data.worldBoundingBox - XCTAssertEqual(interactor.safeBound(wbb)?.min, [0, 1, 2]) - XCTAssertEqual(interactor.safeBound(wbb)?.max, [2, 3, 4]) + XCTAssertEqual(node.data.worldBound?.min, node.data.localBound?.min) + XCTAssertEqual(node.data.worldBound?.max, node.data.localBound?.max) + XCTAssertEqual(node.data.worldBound?.min, [0, 1, 2]) + XCTAssertEqual(node.data.worldBound?.max, [2, 3, 4]) } func testBoundingBoxNestedWithTranslations() throws { - let bb = interactor.from(bound: PNBound(min: [-1, -1, -1], max: [1, 1, 1])) + let bb = PNBound(min: [-1, -1, -1], max: [1, 1, 1]) let node = PNScenePiece.make(data: PNISceneNode(transform: .translation(vector: [1, 2, 3]), - boundingBox: bb)) - let parentBb = interactor.from(bound: PNBound(min: [2, 2, 2], max: [4, 4, 4])) + bound: bb)) + let parentBb = PNBound(min: [2, 2, 2], max: [4, 4, 4]) let parentNode = PNScenePiece.make(data: PNISceneNode(transform: .translation(vector: [4, 5, 6]), - boundingBox: parentBb)) + bound: parentBb)) parentNode.add(child: node) nodeUpdate.update(rootNode: parentNode) XCTAssertEqual(parentNode.data.worldTransform.translation, [4, 5, 6]) XCTAssertEqual(node.data.worldTransform.translation, [5, 7, 9]) - if let bbParent = parentNode.data.worldBoundingBox, - let bbChild = node.data.worldBoundingBox, - let bbChildLocal = node.data.localBoundingBox, - let childrenMerged = parentNode.data.childrenMergedBoundingBox { - XCTAssertEqual(interactor.bound(bbChild).min, [4, 6, 8]) - XCTAssertEqual(interactor.bound(bbChild).max, [6, 8, 10]) - XCTAssertEqual(interactor.bound(bbParent).min, [4, 6, 8]) - XCTAssertEqual(interactor.bound(bbParent).max, [8, 9, 10]) - XCTAssertEqual(interactor.bound(childrenMerged).min, [0, 1, 2]) - XCTAssertEqual(interactor.bound(childrenMerged).max, [2, 3, 4]) - XCTAssertEqual(interactor.bound(childrenMerged).min, - interactor.bound(bbChildLocal).min) - XCTAssertEqual(interactor.bound(childrenMerged).max, - interactor.bound(bbChildLocal).max) + if let bbChild = node.data.worldBound, + let bbParent = parentNode.data.worldBound, + let bbChildLocal = node.data.localBound, + let childrenMerged = parentNode.data.childrenMergedBound { + XCTAssertEqual(bbChild.min, [4, 6, 8]) + XCTAssertEqual(bbChild.max, [6, 8, 10]) + XCTAssertEqual(bbParent.min, [4, 6, 8]) + XCTAssertEqual(bbParent.max, [8, 9, 10]) + XCTAssertEqual(childrenMerged.min, [0, 1, 2]) + XCTAssertEqual(childrenMerged.max, [2, 3, 4]) + XCTAssertEqual(childrenMerged.min, bbChildLocal.min) + XCTAssertEqual(childrenMerged.max, bbChildLocal.max) } else { XCTFail("Unexpected nil") } } func testBoundingBoxNestedNoTranslation() throws { - let firstBb = interactor.from(bound: PNBound(min: [-1, -1, -1], max: [5, 5, 5])) + let firstBb = PNBound(min: [-1, -1, -1], max: [5, 5, 5]) let firstNode = PNScenePiece.make(data: PNISceneNode(transform: .identity, - boundingBox: firstBb)) - let secondBb = interactor.from(bound: PNBound(min: [-4, -4, -4], max: [2, 2, 2])) + bound: firstBb)) + let secondBb = PNBound(min: [-4, -4, -4], max: [2, 2, 2]) let secondNode = PNScenePiece.make(data: PNISceneNode(transform: .identity, - boundingBox: secondBb)) - let parentBb = interactor.from(bound: PNBound(min: [-10, -10, -10], max: [4, 4, 4])) + bound: secondBb)) + let parentBb = PNBound(min: [-10, -10, -10], max: [4, 4, 4]) let parentNode = PNScenePiece.make(data: PNISceneNode(transform: .identity, - boundingBox: parentBb)) + bound: parentBb)) parentNode.add(child: firstNode) parentNode.add(child: secondNode) nodeUpdate.update(rootNode: parentNode) - let firstNodeWBB = interactor.safeBound(firstNode.data.worldBoundingBox) - let secondNodeWBB = interactor.safeBound(secondNode.data.worldBoundingBox) - let mergedChildrenWBB = interactor.safeBound(parentNode.data.childrenMergedBoundingBox) - let parentNodeWBB = interactor.safeBound(parentNode.data.worldBoundingBox) - XCTAssertEqual(firstNodeWBB?.min, [-1, -1, -1]) - XCTAssertEqual(firstNodeWBB?.max, [5, 5, 5]) - XCTAssertEqual(secondNodeWBB?.min, [-4, -4, -4]) - XCTAssertEqual(secondNodeWBB?.max, [2, 2, 2]) - XCTAssertEqual(mergedChildrenWBB?.min, [-4, -4, -4]) - XCTAssertEqual(mergedChildrenWBB?.max, [5, 5, 5]) - XCTAssertEqual(parentNodeWBB?.min, [-10, -10, -10]) - XCTAssertEqual(parentNodeWBB?.max, [5, 5, 5]) + XCTAssertEqual(firstNode.data.worldBound?.min, [-1, -1, -1]) + XCTAssertEqual(firstNode.data.worldBound?.max, [5, 5, 5]) + XCTAssertEqual(secondNode.data.worldBound?.min, [-4, -4, -4]) + XCTAssertEqual(secondNode.data.worldBound?.max, [2, 2, 2]) + XCTAssertEqual(parentNode.data.childrenMergedBound?.min, [-4, -4, -4]) + XCTAssertEqual(parentNode.data.childrenMergedBound?.max, [5, 5, 5]) + XCTAssertEqual(parentNode.data.worldBound?.min, [-10, -10, -10]) + XCTAssertEqual(parentNode.data.worldBound?.max, [5, 5, 5]) } func testBoundingBoxReloading() throws { - let bb = interactor.from(bound: PNBound(min: [-1, -1, -1], max: [1, 1, 1])) - let node = PNScenePiece.make(data: PNISceneNode(transform: .identity, boundingBox: bb)) + let bb = PNBound(min: [-1, -1, -1], max: [1, 1, 1]) + let node = PNScenePiece.make(data: PNISceneNode(transform: .identity, bound: bb)) node.data.transform = .scale(factor: 2) nodeUpdate.update(rootNode: node) XCTAssertEqual(node.data.transform, .scale(factor: 2)) XCTAssertEqual(node.data.worldTransform, .scale(factor: 2)) - XCTAssertEqual(interactor.safeBound(node.data.intrinsicBoundingBox)?.min, [-1, -1, -1]) - XCTAssertEqual(interactor.safeBound(node.data.intrinsicBoundingBox)?.max, [1, 1, 1]) - XCTAssertNil(node.data.childrenMergedBoundingBox) - XCTAssertEqual(interactor.safeBound(node.data.localBoundingBox)?.min, [-2, -2, -2]) - XCTAssertEqual(interactor.safeBound(node.data.localBoundingBox)?.max, [2, 2, 2]) - XCTAssertEqual(interactor.safeBound(node.data.worldBoundingBox)?.max, [2, 2, 2]) + XCTAssertEqual(node.data.intrinsicBound?.min, [-1, -1, -1]) + XCTAssertEqual(node.data.intrinsicBound?.max, [1, 1, 1]) + XCTAssertNil(node.data.childrenMergedBound) + XCTAssertEqual(node.data.localBound?.min, [-2, -2, -2]) + XCTAssertEqual(node.data.localBound?.max, [2, 2, 2]) + XCTAssertEqual(node.data.worldBound?.max, [2, 2, 2]) } func testInitialNodeState() throws { let node = PNScenePiece.make(data: PNISceneNode()) nodeUpdate.update(rootNode: node) - XCTAssertNil(node.data.intrinsicBoundingBox) - XCTAssertNil(node.data.worldBoundingBox) - XCTAssertNil(node.data.localBoundingBox) - XCTAssertNil(node.data.childrenMergedBoundingBox) + XCTAssertNil(node.data.intrinsicBound) + XCTAssertNil(node.data.worldBound) + XCTAssertNil(node.data.localBound) + XCTAssertNil(node.data.childrenMergedBound) XCTAssertEqual(node.data.worldTransform, .identity) XCTAssertEqual(node.data.transform, .identity) } func testMinimalBoard() throws { let boardBound = PNBound(min: [-30, -2, -30], max: [30, -1, 30]) - let boardBB = interactor.from(bound: boardBound) - let boardNode = PNScenePiece.make(data: PNISceneNode(boundingBox: boardBB)) + let boardNode = PNScenePiece.make(data: PNISceneNode(bound: boardBound)) let transformNode = PNScenePiece.make(data: PNISceneNode(transform: PNTransform.scale(factor: 0.5))) transformNode.add(child: boardNode) nodeUpdate.update(rootNode: transformNode) - XCTAssertNotNil(boardNode.data.intrinsicBoundingBox) - XCTAssertEqual(interactor.safeBound(boardNode.data.localBoundingBox)?.min, boardBound.min) - XCTAssertEqual(interactor.safeBound(boardNode.data.localBoundingBox)?.max, boardBound.max) - XCTAssertEqual(interactor.safeBound(boardNode.data.worldBoundingBox)?.min, [-15, -1, -15]) - XCTAssertEqual(interactor.safeBound(boardNode.data.worldBoundingBox)?.max, [15, -0.5, 15]) - XCTAssertNil(boardNode.data.childrenMergedBoundingBox) - XCTAssertNil(transformNode.data.intrinsicBoundingBox) - XCTAssertEqual(interactor.safeBound(transformNode.data.worldBoundingBox)?.min, [-15, -1, -15]) - XCTAssertEqual(interactor.safeBound(transformNode.data.worldBoundingBox)?.max, [15, -0.5, 15]) - XCTAssertEqual(interactor.safeBound(transformNode.data.localBoundingBox)?.min, [-15, -1, -15]) - XCTAssertEqual(interactor.safeBound(transformNode.data.localBoundingBox)?.max, [15, -0.5, 15]) - XCTAssertEqual(interactor.safeBound(transformNode.data.worldBoundingBox)?.min, - interactor.safeBound(boardNode.data.worldBoundingBox)?.min) - XCTAssertEqual(interactor.safeBound(transformNode.data.worldBoundingBox)?.max, - interactor.safeBound(boardNode.data.worldBoundingBox)?.max) - XCTAssertEqual(interactor.safeBound(transformNode.data.childrenMergedBoundingBox)?.min, - [-30, -2, -30]) - XCTAssertEqual(interactor.safeBound(transformNode.data.childrenMergedBoundingBox)?.max, - [30, -1, 30]) + XCTAssertNotNil(boardNode.data.intrinsicBound) + XCTAssertEqual(boardNode.data.localBound?.min, boardBound.min) + XCTAssertEqual(boardNode.data.localBound?.max, boardBound.max) + XCTAssertEqual(boardNode.data.worldBound?.min, [-15, -1, -15]) + XCTAssertEqual(boardNode.data.worldBound?.max, [15, -0.5, 15]) + XCTAssertNil(boardNode.data.childrenMergedBound) + XCTAssertNil(transformNode.data.intrinsicBound) + XCTAssertEqual(transformNode.data.worldBound?.min, [-15, -1, -15]) + XCTAssertEqual(transformNode.data.worldBound?.max, [15, -0.5, 15]) + XCTAssertEqual(transformNode.data.localBound?.min, [-15, -1, -15]) + XCTAssertEqual(transformNode.data.localBound?.max, [15, -0.5, 15]) + XCTAssertEqual(transformNode.data.worldBound?.min, boardNode.data.worldBound?.min) + XCTAssertEqual(transformNode.data.worldBound?.max, boardNode.data.worldBound?.max) + XCTAssertEqual(transformNode.data.childrenMergedBound?.min, [-30, -2, -30]) + XCTAssertEqual(transformNode.data.childrenMergedBound?.max, [30, -1, 30]) } func testBoundingBoxNestedBoard() throws { - let boardBB = interactor.from(bound: PNBound(min: [-30, -2, -30], max: [30, -1, 30])) - let boardNode = PNScenePiece.make(data: PNISceneNode(transform: .identity, boundingBox: boardBB)) + let boardBound = PNBound(min: [-30, -2, -30], max: [30, -1, 30]) + let boardNode = PNScenePiece.make(data: PNISceneNode(transform: .identity, bound: boardBound)) let transformNode = PNScenePiece.make(data: PNISceneNode(transform: PNTransform.scale(factor: 0.5))) transformNode.add(child: boardNode) nodeUpdate.update(rootNode: transformNode) - let mainBB = transformNode.data.worldBoundingBox - let boardWorldBB = boardNode.data.worldBoundingBox - XCTAssertEqual(interactor.safeBound(mainBB)?.min, interactor.safeBound(boardWorldBB)?.min) - XCTAssertEqual(interactor.safeBound(mainBB)?.max, interactor.safeBound(boardWorldBB)?.max) + XCTAssertEqual(transformNode.data.worldBound?.min, boardNode.data.worldBound?.min) + XCTAssertEqual(transformNode.data.worldBound?.max, boardNode.data.worldBound?.max) let passthroughNode = PNScenePiece.make(data: PNISceneNode(transform: .translation(vector: [1, 0, 0]))) passthroughNode.add(child: transformNode) } func testBoundingBoxNestedNoTranslationAfterBoundingBoxUpdate() throws { - let firstBb = interactor.from(bound: PNBound(min: [-1, -1, -1], max: [5, 5, 5])) + let firstBb = PNBound(min: [-1, -1, -1], max: [5, 5, 5]) let firstNode = PNScenePiece.make(data: PNISceneNode(transform: .identity, - boundingBox: firstBb)) - let secondBb = interactor.from(bound: PNBound(min: [-4, -4, -4], max: [2, 2, 2])) + bound: firstBb)) + let secondBb = PNBound(min: [-4, -4, -4], max: [2, 2, 2]) let secondNode = PNScenePiece.make(data: PNISceneNode(transform: .identity, - boundingBox: secondBb)) - let parentBb = interactor.from(bound: PNBound(min: [-10, -10, -10], max: [4, 4, 4])) + bound: secondBb)) + let parentBb = PNBound(min: [-10, -10, -10], max: [4, 4, 4]) let parentNode = PNScenePiece.make(data: PNISceneNode(transform: .identity, - boundingBox: parentBb)) + bound: parentBb)) parentNode.add(child: firstNode) parentNode.add(child: secondNode) secondNode.data.transform = .translation(vector: [20, 20, 20]) nodeUpdate.update(rootNode: parentNode) - let firstNodeWBB = interactor.safeBound(firstNode.data.worldBoundingBox) - let secondNodeWBB = interactor.safeBound(secondNode.data.worldBoundingBox) - let mergedChildrenWBB = interactor.safeBound(parentNode.data.childrenMergedBoundingBox) - let parentNodeWBB = interactor.safeBound(parentNode.data.worldBoundingBox) - XCTAssertEqual(firstNodeWBB?.min, [-1, -1, -1]) - XCTAssertEqual(firstNodeWBB?.max, [5, 5, 5]) - XCTAssertEqual(secondNodeWBB?.min, [16, 16, 16]) - XCTAssertEqual(secondNodeWBB?.max, [22, 22, 22]) - XCTAssertEqual(mergedChildrenWBB?.min, [-1, -1, -1]) - XCTAssertEqual(mergedChildrenWBB?.max, [22, 22, 22]) - XCTAssertEqual(parentNodeWBB?.min, [-10, -10, -10]) - XCTAssertEqual(parentNodeWBB?.max, [22, 22, 22]) + XCTAssertEqual(firstNode.data.worldBound?.min, [-1, -1, -1]) + XCTAssertEqual(firstNode.data.worldBound?.max, [5, 5, 5]) + XCTAssertEqual(secondNode.data.worldBound?.min, [16, 16, 16]) + XCTAssertEqual(secondNode.data.worldBound?.max, [22, 22, 22]) + XCTAssertEqual(parentNode.data.childrenMergedBound?.min, [-1, -1, -1]) + XCTAssertEqual(parentNode.data.childrenMergedBound?.max, [22, 22, 22]) + XCTAssertEqual(parentNode.data.worldBound?.min, [-10, -10, -10]) + XCTAssertEqual(parentNode.data.worldBound?.max, [22, 22, 22]) } }