diff --git a/Sources/UntoldEngine/Renderer/RenderExtensionModelSurface.swift b/Sources/UntoldEngine/Renderer/RenderExtensionModelSurface.swift index c64f85c79..97ab709be 100644 --- a/Sources/UntoldEngine/Renderer/RenderExtensionModelSurface.swift +++ b/Sources/UntoldEngine/Renderer/RenderExtensionModelSurface.swift @@ -494,59 +494,7 @@ private func drawModelSurfaceMeshes( index: Int(modelPassFragmentUniformIndex.rawValue) ) - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = getEntityComponent(entityId: entityId, componentType: SkeletonComponent.self) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes( - &hasArmature, - length: MemoryLayout.stride, - index: Int(modelPassHasArmature.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassVerticesIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassNormalIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassUVIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassTangentIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassJointWeightsIndex.rawValue) - ) - - if let jointTransformBuffer { - renderEncoder.setVertexBuffer( - jointTransformBuffer, - offset: 0, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes( - &identityMatrix, - length: MemoryLayout.stride, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) for submesh in mesh.submeshes { renderEncoder.drawIndexedPrimitivesTracked( diff --git a/Sources/UntoldEngine/Renderer/RenderPasses.swift b/Sources/UntoldEngine/Renderer/RenderPasses.swift index 3d8c91fda..a8b9f2652 100644 --- a/Sources/UntoldEngine/Renderer/RenderPasses.swift +++ b/Sources/UntoldEngine/Renderer/RenderPasses.swift @@ -1177,28 +1177,7 @@ public enum RenderPasses { &modelUniforms, length: MemoryLayout.stride, index: Int(shadowPassModelUniform.rawValue) ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(shadowPassModelPositionIndex.rawValue)].buffer, - offset: 0, index: Int(shadowPassModelPositionIndex.rawValue) - ) - - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(shadowPassHasArmature.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, index: Int(shadowPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, index: Int(shadowPassJointWeightsIndex.rawValue) - ) - if let jtb = jointTransformBuffer { - renderEncoder.setVertexBuffer(jtb, offset: 0, index: Int(shadowPassJointTransformIndex.rawValue)) - } else { - var identity = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identity, length: MemoryLayout.stride, index: Int(shadowPassJointTransformIndex.rawValue)) - } + renderEncoder.bindShadowVertexStreams(mesh: mesh, entityId: entityId) for subMesh in mesh.submeshes { renderEncoder.drawIndexedPrimitivesTracked( @@ -1391,31 +1370,7 @@ public enum RenderPasses { modelUniforms.projectionMatrix = renderInfo.perspectiveSpace renderEncoder.setVertexBytes(&modelUniforms, length: MemoryLayout.stride, index: Int(shadowPassModelUniform.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(shadowPassModelPositionIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassModelPositionIndex.rawValue) - ) - - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(shadowPassHasArmature.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassJointWeightsIndex.rawValue) - ) - if let jtb = jointTransformBuffer { - renderEncoder.setVertexBuffer(jtb, offset: 0, index: Int(shadowPassJointTransformIndex.rawValue)) - } else { - var identity = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identity, length: MemoryLayout.stride, index: Int(shadowPassJointTransformIndex.rawValue)) - } + renderEncoder.bindShadowVertexStreams(mesh: mesh, entityId: entityId) for subMesh in mesh.submeshes { renderEncoder.drawIndexedPrimitivesTracked( @@ -1545,31 +1500,7 @@ public enum RenderPasses { modelUniforms.projectionMatrix = renderInfo.perspectiveSpace renderEncoder.setVertexBytes(&modelUniforms, length: MemoryLayout.stride, index: Int(shadowPassModelUniform.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(shadowPassModelPositionIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassModelPositionIndex.rawValue) - ) - - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(shadowPassHasArmature.rawValue)) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, - index: Int(shadowPassJointWeightsIndex.rawValue) - ) - if let jtb = jointTransformBuffer { - renderEncoder.setVertexBuffer(jtb, offset: 0, index: Int(shadowPassJointTransformIndex.rawValue)) - } else { - var identity = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identity, length: MemoryLayout.stride, index: Int(shadowPassJointTransformIndex.rawValue)) - } + renderEncoder.bindShadowVertexStreams(mesh: mesh, entityId: entityId) for subMesh in mesh.submeshes { renderEncoder.drawIndexedPrimitivesTracked( @@ -1770,48 +1701,7 @@ public enum RenderPasses { &modelUniforms, length: MemoryLayout.stride, index: Int(modelPassUniformIndex.rawValue) ) - // Only enable armature path when a valid joint transform buffer exists. - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(modelPassHasArmature.rawValue)) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassVerticesIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassNormalIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, offset: 0, - index: Int(modelPassUVIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassTangentIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassJointIdIndex.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, index: Int(modelPassJointWeightsIndex.rawValue) - ) - - if let jointTransformBuffer { - renderEncoder.setVertexBuffer(jointTransformBuffer, offset: 0, index: Int(modelPassJointTransformIndex.rawValue)) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identityMatrix, length: MemoryLayout.stride, index: Int(modelPassJointTransformIndex.rawValue)) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) renderEncoder.setFragmentBytes( &modelUniforms, length: MemoryLayout.stride, index: Int(modelPassFragmentUniformIndex.rawValue) @@ -2238,23 +2128,7 @@ public enum RenderPasses { renderEncoder.setVertexBytes(&modelUniforms, length: MemoryLayout.stride, index: Int(modelPassUniformIndex.rawValue)) - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: Int(modelPassHasArmature.rawValue)) - - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, offset: 0, index: Int(modelPassVerticesIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, offset: 0, index: Int(modelPassNormalIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, offset: 0, index: Int(modelPassUVIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, offset: 0, index: Int(modelPassTangentIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointIdIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointWeightsIndex.rawValue)) - - if let jtb = jointTransformBuffer { - renderEncoder.setVertexBuffer(jtb, offset: 0, index: Int(modelPassJointTransformIndex.rawValue)) - } else { - var identity = matrix_identity_float4x4 - renderEncoder.setVertexBytes(&identity, length: MemoryLayout.stride, index: Int(modelPassJointTransformIndex.rawValue)) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) renderEncoder.setFragmentBytes(&modelUniforms, length: MemoryLayout.stride, index: Int(modelPassFragmentUniformIndex.rawValue)) @@ -3644,59 +3518,7 @@ public enum RenderPasses { index: Int(modelPassUniformIndex.rawValue) ) - // Only enable armature path when a valid joint transform buffer exists. - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes( - &hasArmature, - length: MemoryLayout.stride, - index: Int(modelPassHasArmature.rawValue) - ) - - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassVerticesIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassNormalIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassUVIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassTangentIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassJointIdIndex.rawValue) - ) - renderEncoder.setVertexBuffer( - mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, - offset: 0, - index: Int(modelPassJointWeightsIndex.rawValue) - ) - if let jointTransformBuffer { - renderEncoder.setVertexBuffer( - jointTransformBuffer, - offset: 0, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes( - &identityMatrix, - length: MemoryLayout.stride, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) renderEncoder.setFragmentBytes( &modelUniforms, @@ -3916,30 +3738,7 @@ public enum RenderPasses { index: Int(modelPassUniformIndex.rawValue) ) - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes( - &hasArmature, - length: MemoryLayout.stride, - index: Int(modelPassHasArmature.rawValue) - ) - - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, offset: 0, index: Int(modelPassVerticesIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, offset: 0, index: Int(modelPassNormalIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, offset: 0, index: Int(modelPassUVIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, offset: 0, index: Int(modelPassTangentIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointIdIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointWeightsIndex.rawValue)) - if let jointTransformBuffer { - renderEncoder.setVertexBuffer(jointTransformBuffer, offset: 0, index: Int(modelPassJointTransformIndex.rawValue)) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes( - &identityMatrix, - length: MemoryLayout.stride, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) for subMesh in mesh.submeshes { renderEncoder.drawIndexedPrimitives( @@ -4155,30 +3954,7 @@ public enum RenderPasses { index: Int(modelPassUniformIndex.rawValue) ) - let jointTransformBuffer = mesh.skin?.jointTransformsBuffer - var hasArmature = scene.get(component: SkeletonComponent.self, for: entityId) != nil && jointTransformBuffer != nil - renderEncoder.setVertexBytes( - &hasArmature, - length: MemoryLayout.stride, - index: Int(modelPassHasArmature.rawValue) - ) - - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, offset: 0, index: Int(modelPassVerticesIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassNormalIndex.rawValue)].buffer, offset: 0, index: Int(modelPassNormalIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassUVIndex.rawValue)].buffer, offset: 0, index: Int(modelPassUVIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassTangentIndex.rawValue)].buffer, offset: 0, index: Int(modelPassTangentIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointIdIndex.rawValue)) - renderEncoder.setVertexBuffer(mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, offset: 0, index: Int(modelPassJointWeightsIndex.rawValue)) - if let jointTransformBuffer { - renderEncoder.setVertexBuffer(jointTransformBuffer, offset: 0, index: Int(modelPassJointTransformIndex.rawValue)) - } else { - var identityMatrix = matrix_identity_float4x4 - renderEncoder.setVertexBytes( - &identityMatrix, - length: MemoryLayout.stride, - index: Int(modelPassJointTransformIndex.rawValue) - ) - } + renderEncoder.bindModelVertexStreams(mesh: mesh, entityId: entityId) if let edgeIndexBuffer = mesh.featureEdgeIndexBuffer, mesh.featureEdgeIndexCount > 0 { renderEncoder.setTriangleFillMode(.fill) diff --git a/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift b/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift new file mode 100644 index 000000000..b3c50d187 --- /dev/null +++ b/Sources/UntoldEngine/Renderer/RenderVertexStreamBinding.swift @@ -0,0 +1,91 @@ +// +// RenderVertexStreamBinding.swift +// UntoldEngine +// +// Copyright (C) Untold Engine Studios +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import CShaderTypes +import Metal +import MetalKit +import simd + +/// Mesh.metalKitMesh.vertexBuffers is laid out in model-descriptor order +/// (ModelPassBufferIndices 0-5) regardless of which pass consumes it; the +/// shadow pass sources from that same array but binds to its own slots. +extension MTLRenderCommandEncoder { + func bindModelVertexStreams(mesh: Mesh, entityId: EntityID) { + let slots: [ModelPassBufferIndices] = [ + modelPassVerticesIndex, + modelPassNormalIndex, + modelPassUVIndex, + modelPassTangentIndex, + modelPassJointIdIndex, + modelPassJointWeightsIndex, + ] + for slot in slots { + setVertexBuffer( + mesh.metalKitMesh.vertexBuffers[Int(slot.rawValue)].buffer, + offset: 0, + index: Int(slot.rawValue) + ) + } + bindJointStreams( + mesh: mesh, + entityId: entityId, + hasArmatureIndex: Int(modelPassHasArmature.rawValue), + jointTransformIndex: Int(modelPassJointTransformIndex.rawValue) + ) + } + + func bindShadowVertexStreams(mesh: Mesh, entityId: EntityID) { + setVertexBuffer( + mesh.metalKitMesh.vertexBuffers[Int(modelPassVerticesIndex.rawValue)].buffer, + offset: 0, + index: Int(shadowPassModelPositionIndex.rawValue) + ) + setVertexBuffer( + mesh.metalKitMesh.vertexBuffers[Int(modelPassJointIdIndex.rawValue)].buffer, + offset: 0, + index: Int(shadowPassJointIdIndex.rawValue) + ) + setVertexBuffer( + mesh.metalKitMesh.vertexBuffers[Int(modelPassJointWeightsIndex.rawValue)].buffer, + offset: 0, + index: Int(shadowPassJointWeightsIndex.rawValue) + ) + bindJointStreams( + mesh: mesh, + entityId: entityId, + hasArmatureIndex: Int(shadowPassHasArmature.rawValue), + jointTransformIndex: Int(shadowPassJointTransformIndex.rawValue) + ) + } + + private func bindJointStreams( + mesh: Mesh, + entityId: EntityID, + hasArmatureIndex: Int, + jointTransformIndex: Int + ) { + // Only enable armature path when a valid joint transform buffer exists. + let jointTransformBuffer = mesh.skin?.jointTransformsBuffer + var hasArmature = getEntityComponent(entityId: entityId, componentType: SkeletonComponent.self) != nil + && jointTransformBuffer != nil + setVertexBytes(&hasArmature, length: MemoryLayout.stride, index: hasArmatureIndex) + + if let jointTransformBuffer { + setVertexBuffer(jointTransformBuffer, offset: 0, index: jointTransformIndex) + } else { + var identityMatrix = matrix_identity_float4x4 + setVertexBytes( + &identityMatrix, + length: MemoryLayout.stride, + index: jointTransformIndex + ) + } + } +} diff --git a/Tests/UntoldEngineTests/NativeFormatTests.swift b/Tests/UntoldEngineTests/NativeFormatTests.swift index 822167909..b33e70738 100644 --- a/Tests/UntoldEngineTests/NativeFormatTests.swift +++ b/Tests/UntoldEngineTests/NativeFormatTests.swift @@ -264,6 +264,22 @@ final class NativeFormatTests: XCTestCase { } } + func testUnknownCoreChunkTypeIsIgnored() throws { + // A core-range chunk type this runtime does not know (e.g. one added by a + // newer format revision) must not prevent the rest of the asset from loading. + let futureChunkType = UntoldChunkType(rawValue: 22) + let fixture = makeTinyFixture(pluginChunks: [ + (futureChunkType, Data([0x01, 0x02, 0x03, 0x04]), 0), + ]) + + let decoded = try UntoldReader().readAsset(from: fixture.fileData) + + XCTAssertTrue(decoded.chunks.contains(where: { $0.chunkType == futureChunkType })) + XCTAssertTrue(decoded.pluginChunks.isEmpty) + XCTAssertEqual(decoded.meshes.count, 1) + XCTAssertEqual(decoded.entities.count, 1) + } + func testColorManagementRoundtripsThroughRuntimeLoader() throws { // Reuses the tiny fixture's existing texture (index 0, "albedo.ktx2") // as the LUT reference, to exercise the same index -> URL resolution