diff --git a/Sources/Euclid+RealityKit.swift b/Sources/Euclid+RealityKit.swift index 40f27652..3071ca84 100644 --- a/Sources/Euclid+RealityKit.swift +++ b/Sources/Euclid+RealityKit.swift @@ -83,13 +83,21 @@ private func defaultMaterialLookup(_ material: Polygon.Material?) -> RealityKit. @available(macOS 12.0, iOS 15.0, *) public extension MeshDescriptor { + private typealias SIMDVertexData = ( + positions: [simd_float3], + normals: [simd_float3], + texcoords: [simd_float2]?, + indices: [UInt32], + materialIndices: [UInt32] + ) + /// Creates a mesh descriptor from a ``Mesh`` using triangles. /// - Parameters: /// - mesh: The mesh to convert into a RealityKit mesh descriptor. init(triangles mesh: Mesh) { self.init() var counts: [UInt8]? - let data = mesh.getVertexData(maxSides: 3, counts: &counts) + let data: SIMDVertexData = mesh.getVertexData(maxSides: 3, counts: &counts) self.positions = .init(data.positions) self.normals = .init(data.normals) self.textureCoordinates = data.texcoords.map(MeshBuffers.TextureCoordinates.init) @@ -105,7 +113,7 @@ public extension MeshDescriptor { init(polygons mesh: Mesh) { self.init() var counts: [UInt8]? = [] - let data = mesh.getVertexData(maxSides: 255, counts: &counts) + let data: SIMDVertexData = mesh.getVertexData(maxSides: 255, counts: &counts) self.positions = .init(data.positions) self.normals = .init(data.normals) self.textureCoordinates = data.texcoords.map(MeshBuffers.TextureCoordinates.init) @@ -121,7 +129,7 @@ public extension MeshDescriptor { init(quads mesh: Mesh) { self.init() var counts: [UInt8]? = [] - let data = mesh.getVertexData(maxSides: 4, counts: &counts) + let data: SIMDVertexData = mesh.getVertexData(maxSides: 4, counts: &counts) self.positions = .init(data.positions) self.normals = .init(data.normals) self.textureCoordinates = data.texcoords.map(MeshBuffers.TextureCoordinates.init) @@ -214,56 +222,6 @@ private extension Mesh { let materialLookup = materialLookup ?? defaultMaterialLookup return materials.map { materialLookup($0) ?? SimpleMaterial() } } - - func getVertexData(maxSides: UInt8, counts: inout [UInt8]?) -> ( - positions: [SIMD3], - normals: [SIMD3], - texcoords: [SIMD2]?, - indices: [UInt32], - materialIndices: [UInt32] - ) { - var positions: [SIMD3] = [] - var normals: [SIMD3] = [] // looks bad with default normals - var texcoords: [SIMD2]? = hasTexcoords ? [] : nil - var indices = [UInt32]() - var materialIndices = [UInt32]() - var indicesByVertex = [Vertex: UInt32]() - let polygonsByMaterial = self.polygonsByMaterial - let perFaceMaterials = materials.count > 1 - for (materialIndex, material) in materials.enumerated() { - let polygons = polygonsByMaterial[material] ?? [] - for polygon in polygons.tessellate(maxSides: Int(maxSides)) { - counts?.append(UInt8(polygon.vertices.count)) - for var vertex in polygon.vertices { - vertex.color = .white // Note: vertex colors are not supported - if let index = indicesByVertex[vertex] { - indices.append(index) - continue - } - let index = UInt32(indicesByVertex.count) - indicesByVertex[vertex] = index - indices.append(index) - positions.append(.init(vertex.position)) - normals.append(.init(vertex.normal)) - if texcoords != nil { - var texcoord = vertex.texcoord - texcoord.y = 1 - texcoord.y - texcoords?.append(.init(texcoord)) - } - } - if perFaceMaterials { - materialIndices.append(UInt32(materialIndex)) - } - } - } - return ( - positions: positions, - normals: normals, - texcoords: texcoords, - indices: indices, - materialIndices: materialIndices - ) - } } #endif diff --git a/Sources/Euclid+SceneKit.swift b/Sources/Euclid+SceneKit.swift index 3a8b6ed7..0f73bc57 100644 --- a/Sources/Euclid+SceneKit.swift +++ b/Sources/Euclid+SceneKit.swift @@ -146,6 +146,15 @@ extension SCNGeometrySource { } public extension SCNGeometry { + private typealias SCNVertexData = ( + positions: [SCNVector3], + normals: [SCNVector3], + texcoords: [CGPoint]?, + colors: [SCNVector4]?, + indices: [UInt32], + materialIndices: [UInt32] + ) + /// A closure that maps a Euclid material to a SceneKit material. /// - Parameter m: A Euclid material to convert, or `nil` for the default material. /// - Returns: An `SCNMaterial` used by SceneKit. diff --git a/Sources/Mesh.swift b/Sources/Mesh.swift index 3432cd7c..8f951ca8 100644 --- a/Sources/Mesh.swift +++ b/Sources/Mesh.swift @@ -396,6 +396,63 @@ extension Mesh { func getBSP(_ isCancelled: CancellationHandler) -> BSP { storage.getBSP(isCancelled) } + + func getVertexData< + Position: XYZRepresentable, + Normal: XYZRepresentable, + Texcoord: XYZRepresentable + >(maxSides: UInt8, counts: inout [UInt8]?) -> ( + positions: [Position], + normals: [Normal], + texcoords: [Texcoord], + indices: [UInt32], + materialIndices: [UInt32] + ) { + var positions: [Position] = [] + var normals: [Normal] = [] + var texcoords: [Texcoord] = [] + var indices = [UInt32]() + var materialIndices = [UInt32]() + let hasTexcoords = self.hasTexcoords + var indicesByVertex = [Vertex: UInt32]() + let polygonsByMaterial = self.polygonsByMaterial + let perFaceMaterials = materials.count > 1 + for (materialIndex, material) in materials.enumerated() { + let polygons = polygonsByMaterial[material] ?? [] + for polygon in polygons { + for polygon in polygon.tessellate(maxSides: Int(maxSides)) { + counts?.append(UInt8(polygon.vertices.count)) + for vertex in polygon.vertices { + if let index = indicesByVertex[vertex] { + indices.append(index) + continue + } + let index = UInt32(indicesByVertex.count) + indicesByVertex[vertex] = index + indices.append(index) + positions.append(.init(vertex.position)) + normals.append(.init(vertex.normal)) + if hasTexcoords { + var texcoord = vertex.texcoord + texcoord.y = 1 - texcoord.y + texcoords.append(.init(texcoord)) + } + // Note: vertex colors are not supported + } + if perFaceMaterials { + materialIndices.append(UInt32(materialIndex)) + } + } + } + } + return ( + positions: positions, + normals: normals, + texcoords: texcoords, + indices: indices, + materialIndices: materialIndices + ) + } } private extension Mesh { diff --git a/Sources/Polygon.swift b/Sources/Polygon.swift index 0c54a0f3..ca00df4f 100644 --- a/Sources/Polygon.swift +++ b/Sources/Polygon.swift @@ -1164,7 +1164,7 @@ struct CodableMaterial: Codable { self.value = color } else if let data = try container.decodeIfPresent(Data.self, forKey: .nscoded) { guard let value = try NSKeyedUnarchiver.unarchivedObject( - ofClasses: Polygon.codableClasses, from: data + ofClasses: NSSet(array: Polygon.codableClasses) as! Set, from: data ) as? Polygon.Material else { throw DecodingError.dataCorruptedError( forKey: .nscoded,