...continued from previous reply.
Texture coordinates reader
struct TextureCoordinatesReader {
private let uvPositions: [SIMD2<Float>]
private let faceIndices: [UInt16]
init(meshResource:MeshResource) {
var positions: [SIMD3<Float>] = []
var uvPositions: [SIMD2<Float>] = []
var faceIndices: [UInt16] = []
var indexOffset: Int = 0
for instance in meshResource.contents.instances {
guard let model = meshResource.contents.models[instance.model] else {
fatalError("Failed to get mesh model when generating static mesh data")
}
for part in model.parts {
let partPositions = part.positions.map { point in
let v0 = SIMD4<Float>(point.x, point.y, point.z, 1.0)
let v1 = instance.transform * v0
return SIMD3<Float>(v1.x, v1.y, v1.z)
}
part.textureCoordinates?.forEach { coord in
let uv0 = SIMD4<Float>(coord.x, coord.y, 0.0, 1.0)
let uv1 = instance.transform * uv0
uvPositions.append(SIMD2<Float>(uv1.x, uv1.y))
}
positions.append(contentsOf: partPositions)
guard let triangleIndices = part.triangleIndices else {
fatalError("Failed to get triangle indices off of mesh model part when generating static mesh data")
}
faceIndices.append(contentsOf: triangleIndices.elements.map { index in
UInt16(Int(index) + indexOffset)
})
indexOffset += partPositions.count
}
}
self.uvPositions = uvPositions
self.faceIndices = faceIndices
}
func coordinates(for faceIndex:Int, at uv: SIMD2<Float>) -> SIMD2<Float> {
// Get indices for each triangle vertex for the face
let vertexTexCoords: [SIMD2<Float>] = [
Int(faceIndices[faceIndex * 3 + 0]),
Int(faceIndices[faceIndex * 3 + 1]),
Int(faceIndices[faceIndex * 3 + 2])
].map { i in
uvPositions[i]
}
let u = uv.x
let v = uv.y
let w = 1 - u - v
let textureCoordinates = w * vertexTexCoords[0] + u * vertexTexCoords[1] + v * vertexTexCoords[2]
return textureCoordinates
}
}
Extension to read the texture color given a texture coordinate
extension TextureResource {
func readColor(at coordinates:SIMD2<Float>) throws -> Color? {
guard let mtlTexture = MTLCreateSystemDefaultDevice()?.makeTexture(descriptor: {
let descriptor = MTLTextureDescriptor()
descriptor.width = Int(width)
descriptor.height = Int(height)
descriptor.pixelFormat = .rgba8Unorm
descriptor.usage = [.shaderRead, .shaderWrite]
return descriptor
}()) else {return nil}
try copy(to: mtlTexture)
var bytes: [UInt8] = .init(repeating: 0, count: 4)
bytes.withUnsafeMutableBytes { ptr in
mtlTexture.getBytes(
ptr.baseAddress!,
bytesPerRow: mtlTexture.bufferBytesPerRow,
from: .init(origin: MTLOrigin(
x: Int(coordinates.x * Float(width)),
y: Int((1 - coordinates.y) * Float(height)),
z: 0
),size: MTLSize(width: 1, height: 1, depth: 1)),
mipmapLevel: 0
)
}
let r = Double(bytes[0]) / 255.0
let g = Double(bytes[1]) / 255.0
let b = Double(bytes[2]) / 255.0
let a = Double(bytes[3]) / 255.0
return Color(red: r, green: g, blue: b, opacity: a)
}
}