diff --git a/launcher/AV-Live-Body/Package.swift b/launcher/AV-Live-Body/Package.swift index 85caf3d..80bc220 100644 --- a/launcher/AV-Live-Body/Package.swift +++ b/launcher/AV-Live-Body/Package.swift @@ -1,15 +1,18 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 import PackageDescription let package = Package( name: "AVLiveBody", - platforms: [.macOS(.v14)], + platforms: [.macOS(.v15)], targets: [ .executableTarget( name: "AVLiveBody", path: "Sources/AVLiveBody", resources: [ .copy("Resources/smplx_faces.bin"), + ], + swiftSettings: [ + .swiftLanguageMode(.v5), ] ) ] diff --git a/launcher/AV-Live-Body/Sources/AVLiveBody/MeshRenderer.swift b/launcher/AV-Live-Body/Sources/AVLiveBody/MeshRenderer.swift index 3841178..5525685 100644 --- a/launcher/AV-Live-Body/Sources/AVLiveBody/MeshRenderer.swift +++ b/launcher/AV-Live-Body/Sources/AVLiveBody/MeshRenderer.swift @@ -47,28 +47,45 @@ final class MeshRenderer: ObservableObject { let receivedPids = Set(persons.map { $0.pid }) for (pid, _) in personEntities where !receivedPids.contains(pid) { personEntities.removeValue(forKey: pid) + lowLevelMeshes.removeValue(forKey: pid) } for p in persons { - let entity = personEntities[p.pid] ?? makeEntity(pid: p.pid) + let entity: ModelEntity + if let existing = personEntities[p.pid] { + entity = existing + } else { + entity = makeEntity(pid: p.pid) + entity.components.set(PidComponent(pid: p.pid)) + } updateMeshVertices(entity, vertices: p.vertices) entity.transform.translation = p.translation personEntities[p.pid] = entity } } + private var lowLevelMeshes: [Int: LowLevelMesh] = [:] + private func makeEntity(pid: Int) -> ModelEntity { let material = SimpleMaterial(color: colorForPid(pid), isMetallic: false) let entity = ModelEntity() let initial = Array(repeating: SIMD3(0, 0, 0), count: 10475) - entity.model = ModelComponent( - mesh: makeMesh(vertices: initial), - materials: [material] - ) + if let mesh = createLowLevelMesh(vertices: initial) { + lowLevelMeshes[pid] = mesh + if let resource = try? MeshResource(from: mesh) { + entity.model = ModelComponent(mesh: resource, + materials: [material]) + } + } else { + entity.model = ModelComponent( + mesh: fallbackMesh(vertices: initial), + materials: [material] + ) + } return entity } - private func makeMesh(vertices: [SIMD3]) -> MeshResource { + private func fallbackMesh(vertices: [SIMD3]) -> MeshResource { var desc = MeshDescriptor(name: "smplx") desc.positions = MeshBuffer(vertices) desc.primitives = .triangles(faces) @@ -78,9 +95,59 @@ final class MeshRenderer: ObservableObject { return MeshResource.generateBox(size: 0.1) } + /// Update les positions des vertices in-place via LowLevelMesh + /// (macOS 14+) ; fallback rebuild du MeshResource si indisponible. private func updateMeshVertices(_ entity: ModelEntity, vertices: [SIMD3]) { - entity.model?.mesh = makeMesh(vertices: vertices) + let pid = entity.components[PidComponent.self]?.pid ?? -1 + if let mesh = lowLevelMeshes[pid] { + mesh.withUnsafeMutableBytes(bufferIndex: 0) { rawPtr in + let dst = rawPtr.bindMemory(to: SIMD3.self) + let n = min(dst.count, vertices.count) + for i in 0..]) -> LowLevelMesh? { + let vertexAttr = LowLevelMesh.Attribute( + semantic: .position, format: .float3, offset: 0) + let vertexLayout = LowLevelMesh.Layout( + bufferIndex: 0, + bufferStride: MemoryLayout>.stride) + let desc = LowLevelMesh.Descriptor( + vertexCapacity: vertices.count, + vertexAttributes: [vertexAttr], + vertexLayouts: [vertexLayout], + indexCapacity: faces.count, + indexType: .uint32 + ) + guard let mesh = try? LowLevelMesh(descriptor: desc) else { + return nil + } + mesh.withUnsafeMutableBytes(bufferIndex: 0) { ptr in + let dst = ptr.bindMemory(to: SIMD3.self) + for (i, v) in vertices.enumerated() where i < dst.count { + dst[i] = v + } + } + mesh.withUnsafeMutableIndices { ptr in + let dst = ptr.bindMemory(to: UInt32.self) + for (i, f) in faces.enumerated() where i < dst.count { + dst[i] = f + } + } + let bounds = BoundingBox(min: SIMD3(-2, -2, -2), + max: SIMD3(2, 2, 2)) + mesh.parts.replaceAll([ + .init(indexCount: faces.count, + topology: .triangle, + materialIndex: 0, + bounds: bounds) + ]) + return mesh } private func colorForPid(_ pid: Int) -> NSColor { @@ -99,3 +166,9 @@ struct SMPLXPersonData { let translation: SIMD3 let vertices: [SIMD3] } + +/// Component permettant de retrouver le pid d'une ModelEntity sans +/// passer par un dictionnaire externe. +struct PidComponent: Component { + let pid: Int +}