diff --git a/Sources/GateEngine/Physics/SkinCollider.swift b/Sources/GateEngine/Physics/SkinCollider.swift index ee276f4c..160454a4 100644 --- a/Sources/GateEngine/Physics/SkinCollider.swift +++ b/Sources/GateEngine/Physics/SkinCollider.swift @@ -18,50 +18,85 @@ public final class SkinCollider: Collider3D { public let geometry: RawGeometry public let skin: Skin - public private(set) var transformedTriangles: [CollisionTriangle] = [] + public private(set) var transformedTriangles: [CollisionTriangle] + private var attributesPerTriangle: [UInt32]! = nil + @MainActor + @inline(__always) var rigComponent: Rig3DComponent? { - return entity[Rig3DComponent.self] + return entity.component(ofType: Rig3DComponent.self) } var currentAnimation: ObjectIdentifier? = nil var computedAtAnimationProgress: Float = -1 var forceRecompute: Bool = true - @MainActor var needsRecompute: Bool { + @MainActor + var needsRecompute: Bool { if forceRecompute { return true } - if let activeAnimation = rigComponent?.activeAnimation { - if currentAnimation != ObjectIdentifier(activeAnimation) { - return true + if let rigComponent { + if let activeAnimation = rigComponent.activeAnimation { + if currentAnimation != ObjectIdentifier(activeAnimation) { + return true + } } + return rigComponent.animationProgress != computedAtAnimationProgress } - return rigComponent?.animationProgress != computedAtAnimationProgress + return false } - @MainActor func recomputeIfNeeded() { - guard let rigComponent = rigComponent else {return} + @MainActor + func populateAttributes() { + let indicies = geometry.indices.map({Int($0)}) + let uvs = geometry.uvSets + + let positionCount: Int = indicies.count + + var transformedUVs: [[Size2]] = Array(repeating: Array(repeating: .zero, count: positionCount), count: geometry.uvSets.count) + for vertexIndex in indicies.indices { + let index = indicies[vertexIndex] + + let index2_1 = index * 2 + let index2_2 = index2_1 + 1 + + for setIndex in 0 ..< geometry.uvSets.count { + transformedUVs[setIndex][vertexIndex] = Size2(uvs[setIndex][index2_1], uvs[setIndex][index2_2]) + } + } + + for triangleIndex in transformedTriangles.indices { + var attributes: UInt32 = 0 + for uvSetIndex in 0 ..< transformedUVs.count { + let uvSet = transformedUVs[uvSetIndex] + let uv = uvSet[triangleIndex * 3] + attributes |= CollisionTriangle.attributeParser(uv.x, uv.y, UInt32(uvSetIndex)) + } + transformedTriangles[triangleIndex]._attributes = attributes + } + } + + @MainActor + func recomputeIfNeeded() { guard needsRecompute else {return} - forceRecompute = false + guard let rigComponent = rigComponent else {return} + self.forceRecompute = false if let activeAnimation = rigComponent.activeAnimation { - currentAnimation = ObjectIdentifier(activeAnimation) + self.currentAnimation = ObjectIdentifier(activeAnimation) } - computedAtAnimationProgress = rigComponent.animationProgress + self.computedAtAnimationProgress = rigComponent.animationProgress - let matrix = transform.matrix() + let matrix = self.transform.matrix() - let indicies = geometry.indices.map({Int($0)}) - let positions = geometry.positions - let uvs = geometry.uvSet1 + let indicies = self.geometry.indices.map({Int($0)}) + let positions = self.geometry.positions - let boneMatricies = rigComponent.skeleton.getPose().shaderMatrixArray(orderedFromSkinJoints: skin.joints).map({$0.transposed()}) - let boneIndicies = skin.jointIndices.map({Int($0)}) - let boneWeights = skin.jointWeights + let boneMatricies = rigComponent.skeleton.getPose().shaderMatrixArray(orderedFromSkinJoints: self.skin.joints).map({$0.transposed()}) + let boneIndicies = self.skin.jointIndices.map({Int($0)}) + let boneWeights = self.skin.jointWeights - let positionCount: Int = indicies.count - - var transformedPositions: [Position3] = Array(repeating: .zero, count: positionCount) - var transformedUVs: [Size2] = Array(repeating: .zero, count: positionCount) + var triangleIndex = 0 + var triangleVertex = 0 for vertexIndex in indicies.indices { let index = indicies[vertexIndex] @@ -69,54 +104,47 @@ public final class SkinCollider: Collider3D { let index3_2 = index3_1 + 1 let index3_3 = index3_2 + 1 - let index2_1 = index * 2 - let index2_2 = index2_1 + 1 - let vertex = Position3(positions[index3_1], positions[index3_2], positions[index3_3]) - let position: Position3 = { - let index4_1: Int = index * 4 - let index4_2: Int = index4_1 + 1 - let index4_3: Int = index4_2 + 1 - let index4_4: Int = index4_3 + 1 - - let boneIndex1: Int = boneIndicies[index4_1] - let boneIndex2: Int = boneIndicies[index4_2] - let boneIndex3: Int = boneIndicies[index4_3] - let boneIndex4: Int = boneIndicies[index4_4] - - let boneMaterix1: Matrix4x4 = boneMatricies[boneIndex1] - let boneMaterix2: Matrix4x4 = boneMatricies[boneIndex2] - let boneMaterix3: Matrix4x4 = boneMatricies[boneIndex3] - let boneMaterix4: Matrix4x4 = boneMatricies[boneIndex4] - - let boneWeight1: Float = boneWeights[index4_1] - let boneWeight2: Float = boneWeights[index4_2] - let boneWeight3: Float = boneWeights[index4_3] - let boneWeight4: Float = boneWeights[index4_4] - - let w1: Position3 = boneMaterix1 * vertex * boneWeight1 - let w2: Position3 = boneMaterix2 * vertex * boneWeight2 - let w3: Position3 = boneMaterix3 * vertex * boneWeight3 - let w4: Position3 = boneMaterix4 * vertex * boneWeight4 + let index4_1: Int = index * 4 + let index4_2: Int = index4_1 + 1 + let index4_3: Int = index4_2 + 1 + let index4_4: Int = index4_3 + 1 + + let boneIndex1: Int = boneIndicies[index4_1] + let boneIndex2: Int = boneIndicies[index4_2] + let boneIndex3: Int = boneIndicies[index4_3] + let boneIndex4: Int = boneIndicies[index4_4] + + let boneMaterix1: Matrix4x4 = boneMatricies[boneIndex1] + let boneMaterix2: Matrix4x4 = boneMatricies[boneIndex2] + let boneMaterix3: Matrix4x4 = boneMatricies[boneIndex3] + let boneMaterix4: Matrix4x4 = boneMatricies[boneIndex4] + + let boneWeight1: Float = boneWeights[index4_1] + let boneWeight2: Float = boneWeights[index4_2] + let boneWeight3: Float = boneWeights[index4_3] + let boneWeight4: Float = boneWeights[index4_4] + + let w1: Position3 = boneMaterix1 * vertex * boneWeight1 + let w2: Position3 = boneMaterix2 * vertex * boneWeight2 + let w3: Position3 = boneMaterix3 * vertex * boneWeight3 + let w4: Position3 = boneMaterix4 * vertex * boneWeight4 + + let position: Position3 = w1 + w2 + w3 + w4 + + // Update the vertex of the triangle + self.transformedTriangles[triangleIndex].positions[triangleVertex] = position * matrix + + triangleVertex += 1 + if triangleVertex == 3 { + // Recomupte the triangle once after all 3 verts have been updated + self.transformedTriangles[triangleIndex].recomputeAll() - return w1 + w2 + w3 + w4 - }() - - transformedPositions[vertexIndex] = position * matrix - transformedUVs[vertexIndex] = Size2(uvs[index2_1], uvs[index2_2]) + triangleIndex += 1 + triangleVertex = 0 + } } - - transformedTriangles = stride(from: 0, to: transformedPositions.count, by: 3).map({ - let uv = transformedUVs[$0] - let attributes = CollisionTriangle.attributeParser(uv.x, uv.y, 0) - return CollisionTriangle( - transformedPositions[$0 + 0], - transformedPositions[$0 + 1], - transformedPositions[$0 + 2], - attributes: attributes - ) - }) } public var center: Position3 { @@ -213,7 +241,7 @@ public final class SkinCollider: Collider3D { } } if let closestTriangle, let closest { - return SurfaceImpact3D(normal: closestTriangle.normal, position: closest) + return SurfaceImpact3D(normal: closestTriangle.normal, position: closest, triangle: closestTriangle) } return nil } @@ -236,13 +264,18 @@ public final class SkinCollider: Collider3D { public var boundingBox: AxisAlignedBoundingBox3D = AxisAlignedBoundingBox3D() + @MainActor public init(entity: Entity, geometry: RawGeometry, skin: Skin, boundingBoxSize: Size3) { self.entity = entity self.geometry = geometry self.skin = skin - self.transformedTriangles = [] self.boundingBox.size = boundingBoxSize self.boundingBox.offset.y = boundingBoxSize.y * 0.5 + + self.forceRecompute = true + + self.transformedTriangles = Array(repeating: CollisionTriangle(.zero, .zero, .zero, attributes: 0), count: Int(geometry.indices.count / 3)) + self.populateAttributes() } }