modelio-rs 0.3.0

Safe Rust bindings for Apple's ModelIO framework — assets, meshes, materials, lights, cameras, voxels, textures, and animation on macOS
Documentation
import Foundation
import ModelIO
import simd

private func mdl_animated_value_info(_ value: MDLAnimatedValue) -> [String: Any] {
    var info: [String: Any] = [
        "is_animated": value.isAnimated(),
        "precision": value.precision.rawValue,
        "time_sample_count": value.timeSampleCount,
        "minimum_time": value.minimumTime,
        "maximum_time": value.maximumTime,
        "interpolation": value.interpolation.rawValue,
        "key_times": value.keyTimes,
    ]
    switch value {
    case let scalarArray as MDLAnimatedScalarArray:
        info["element_count"] = scalarArray.elementCount
    case let vector3Array as MDLAnimatedVector3Array:
        info["element_count"] = vector3Array.elementCount
    case let quaternionArray as MDLAnimatedQuaternionArray:
        info["element_count"] = quaternionArray.elementCount
    default:
        break
    }
    return info
}

@_cdecl("mdl_animated_value_info_json")
public func mdl_animated_value_info_json(_ handle: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<CChar>? {
    guard let value = mdl_borrow_object(handle) as? MDLAnimatedValue else { return nil }
    return mdl_string(mdl_json_string(from: mdl_animated_value_info(value)) ?? "{}")
}

@_cdecl("mdl_animated_value_clear")
public func mdl_animated_value_clear(_ handle: UnsafeMutableRawPointer?) {
    guard let value = mdl_borrow_object(handle) as? MDLAnimatedValue else { return }
    value.clear()
}

@_cdecl("mdl_animated_value_set_interpolation")
public func mdl_animated_value_set_interpolation(_ handle: UnsafeMutableRawPointer?, _ rawValue: UInt32) {
    guard let value = mdl_borrow_object(handle) as? MDLAnimatedValue,
          let interpolation = try? mdl_interpolation(rawValue)
    else {
        return
    }
    value.interpolation = interpolation
}

@_cdecl("mdl_animated_scalar_new")
public func mdl_animated_scalar_new(
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated scalar pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedScalar())
    }
}

@_cdecl("mdl_animated_scalar_set_float")
public func mdl_animated_scalar_set_float(_ handle: UnsafeMutableRawPointer?, _ value: Float, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedScalar else { return }
    animated.setFloat(value, atTime: time)
}

@_cdecl("mdl_animated_scalar_float_value")
public func mdl_animated_scalar_float_value(_ handle: UnsafeMutableRawPointer?, _ time: Double) -> Float {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedScalar else { return 0 }
    return animated.floatValue(atTime: time)
}

@_cdecl("mdl_animated_vector2_new")
public func mdl_animated_vector2_new(
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated vector2 pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedVector2())
    }
}

@_cdecl("mdl_animated_vector2_set_float2")
public func mdl_animated_vector2_set_float2(_ handle: UnsafeMutableRawPointer?, _ x: Float, _ y: Float, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedVector2 else { return }
    animated.setFloat2(SIMD2<Float>(x, y), atTime: time)
}

@_cdecl("mdl_animated_vector2_copy_float2_value")
public func mdl_animated_vector2_copy_float2_value(
    _ handle: UnsafeMutableRawPointer?,
    _ time: Double,
    _ outX: UnsafeMutablePointer<Float>?,
    _ outY: UnsafeMutablePointer<Float>?
) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedVector2 else { return }
    let value = animated.float2Value(atTime: time)
    outX?.pointee = value.x
    outY?.pointee = value.y
}

@_cdecl("mdl_animated_vector3_new")
public func mdl_animated_vector3_new(
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated vector3 pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedVector3())
    }
}

@_cdecl("mdl_animated_vector3_set_float3")
public func mdl_animated_vector3_set_float3(_ handle: UnsafeMutableRawPointer?, _ x: Float, _ y: Float, _ z: Float, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedVector3 else { return }
    animated.setFloat3(SIMD3<Float>(x, y, z), atTime: time)
}

@_cdecl("mdl_animated_vector3_copy_float3_value")
public func mdl_animated_vector3_copy_float3_value(
    _ handle: UnsafeMutableRawPointer?,
    _ time: Double,
    _ outX: UnsafeMutablePointer<Float>?,
    _ outY: UnsafeMutablePointer<Float>?,
    _ outZ: UnsafeMutablePointer<Float>?
) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedVector3 else { return }
    let value = animated.float3Value(atTime: time)
    outX?.pointee = value.x
    outY?.pointee = value.y
    outZ?.pointee = value.z
}

@_cdecl("mdl_animated_vector4_new")
public func mdl_animated_vector4_new(
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated vector4 pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedVector4())
    }
}

@_cdecl("mdl_animated_vector4_set_float4")
public func mdl_animated_vector4_set_float4(_ handle: UnsafeMutableRawPointer?, _ x: Float, _ y: Float, _ z: Float, _ w: Float, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedVector4 else { return }
    animated.setFloat4(SIMD4<Float>(x, y, z, w), atTime: time)
}

@_cdecl("mdl_animated_vector4_copy_float4_value")
public func mdl_animated_vector4_copy_float4_value(
    _ handle: UnsafeMutableRawPointer?,
    _ time: Double,
    _ outX: UnsafeMutablePointer<Float>?,
    _ outY: UnsafeMutablePointer<Float>?,
    _ outZ: UnsafeMutablePointer<Float>?,
    _ outW: UnsafeMutablePointer<Float>?
) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedVector4 else { return }
    let value = animated.float4Value(atTime: time)
    outX?.pointee = value.x
    outY?.pointee = value.y
    outZ?.pointee = value.z
    outW?.pointee = value.w
}

@_cdecl("mdl_animated_quaternion_new")
public func mdl_animated_quaternion_new(
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated quaternion pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedQuaternion())
    }
}

@_cdecl("mdl_animated_quaternion_set_float")
public func mdl_animated_quaternion_set_float(_ handle: UnsafeMutableRawPointer?, _ x: Float, _ y: Float, _ z: Float, _ w: Float, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedQuaternion else { return }
    animated.setFloatQuaternion(simd_quatf(ix: x, iy: y, iz: z, r: w), atTime: time)
}

@_cdecl("mdl_animated_quaternion_copy_float_value")
public func mdl_animated_quaternion_copy_float_value(_ handle: UnsafeMutableRawPointer?, _ time: Double, _ outValues: UnsafeMutablePointer<Float>?) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedQuaternion,
          let outValues
    else {
        return
    }
    _ = mdl_copy_floats(mdl_quaternion_to_array(animated.floatQuaternionValue(atTime: time)), to: outValues, capacity: 4)
}

@_cdecl("mdl_animated_matrix4x4_new")
public func mdl_animated_matrix4x4_new(
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated matrix pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedMatrix4x4())
    }
}

@_cdecl("mdl_animated_matrix4x4_set_float")
public func mdl_animated_matrix4x4_set_float(_ handle: UnsafeMutableRawPointer?, _ values: UnsafePointer<Float>?, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedMatrix4x4 else { return }
    animated.setFloat4x4(mdl_matrix_from_array(values), atTime: time)
}

@_cdecl("mdl_animated_matrix4x4_copy_float_value")
public func mdl_animated_matrix4x4_copy_float_value(_ handle: UnsafeMutableRawPointer?, _ time: Double, _ outValues: UnsafeMutablePointer<Float>?) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedMatrix4x4,
          let outValues
    else {
        return
    }
    _ = mdl_copy_floats(mdl_matrix_to_array(animated.float4x4Value(atTime: time)), to: outValues, capacity: 16)
}

@_cdecl("mdl_animated_scalar_array_new")
public func mdl_animated_scalar_array_new(
    _ elementCount: UInt64,
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated scalar array pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedScalarArray(elementCount: Int(elementCount)))
    }
}

@_cdecl("mdl_animated_scalar_array_set_float")
public func mdl_animated_scalar_array_set_float(_ handle: UnsafeMutableRawPointer?, _ values: UnsafePointer<Float>?, _ count: UInt64, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedScalarArray else { return }
    guard let values, count > 0 else {
        animated.set(floatArray: [], atTime: time)
        return
    }
    animated.set(floatArray: Array(UnsafeBufferPointer(start: values, count: Int(count))), atTime: time)
}

@_cdecl("mdl_animated_scalar_array_copy_float_at_time")
public func mdl_animated_scalar_array_copy_float_at_time(_ handle: UnsafeMutableRawPointer?, _ time: Double, _ outValues: UnsafeMutablePointer<Float>?, _ capacity: UInt64) -> UInt64 {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedScalarArray else { return 0 }
    let values = animated.floatArray(atTime: time)
    let written = mdl_copy_floats(values, to: outValues, capacity: capacity)
    return min(UInt64(values.count), written)
}

@_cdecl("mdl_animated_vector3_array_new")
public func mdl_animated_vector3_array_new(
    _ elementCount: UInt64,
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated vector3 array pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedVector3Array(elementCount: Int(elementCount)))
    }
}

@_cdecl("mdl_animated_vector3_array_set_float")
public func mdl_animated_vector3_array_set_float(_ handle: UnsafeMutableRawPointer?, _ values: UnsafePointer<Float>?, _ count: UInt64, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedVector3Array else { return }
    guard let values, count > 0 else {
        animated.set(float3Array: [], atTime: time)
        return
    }
    let raw = UnsafeBufferPointer(start: values, count: Int(count) * 3)
    var vectors: [SIMD3<Float>] = []
    vectors.reserveCapacity(Int(count))
    for index in 0..<Int(count) {
        vectors.append(SIMD3<Float>(raw[index * 3], raw[index * 3 + 1], raw[index * 3 + 2]))
    }
    animated.set(float3Array: vectors, atTime: time)
}

@_cdecl("mdl_animated_vector3_array_copy_float_at_time")
public func mdl_animated_vector3_array_copy_float_at_time(_ handle: UnsafeMutableRawPointer?, _ time: Double, _ outValues: UnsafeMutablePointer<Float>?, _ capacityElements: UInt64) -> UInt64 {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedVector3Array,
          let outValues
    else {
        return 0
    }
    let values = animated.float3Array(atTime: time)
    let copyCount = min(Int(capacityElements), values.count)
    guard copyCount > 0 else { return 0 }
    var flattened: [Float] = []
    flattened.reserveCapacity(copyCount * 3)
    for value in values.prefix(copyCount) {
        flattened.append(contentsOf: [value.x, value.y, value.z])
    }
    flattened.withUnsafeBufferPointer { buffer in
        outValues.initialize(from: buffer.baseAddress!, count: flattened.count)
    }
    return UInt64(copyCount)
}

@_cdecl("mdl_animated_quaternion_array_new")
public func mdl_animated_quaternion_array_new(
    _ elementCount: UInt64,
    _ outValue: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outValue else {
            throw ModelIOBridgeError.invalidArgument("missing output animated quaternion array pointer")
        }
        outValue.pointee = mdl_retain(MDLAnimatedQuaternionArray(elementCount: Int(elementCount)))
    }
}

@_cdecl("mdl_animated_quaternion_array_set_float")
public func mdl_animated_quaternion_array_set_float(_ handle: UnsafeMutableRawPointer?, _ values: UnsafePointer<Float>?, _ count: UInt64, _ time: Double) {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedQuaternionArray else { return }
    guard let values, count > 0 else {
        animated.set(floatQuaternionArray: [], atTime: time)
        return
    }
    let raw = UnsafeBufferPointer(start: values, count: Int(count) * 4)
    var quaternions: [simd_quatf] = []
    quaternions.reserveCapacity(Int(count))
    for index in 0..<Int(count) {
        quaternions.append(simd_quatf(ix: raw[index * 4], iy: raw[index * 4 + 1], iz: raw[index * 4 + 2], r: raw[index * 4 + 3]))
    }
    animated.set(floatQuaternionArray: quaternions, atTime: time)
}

@_cdecl("mdl_animated_quaternion_array_copy_float_at_time")
public func mdl_animated_quaternion_array_copy_float_at_time(_ handle: UnsafeMutableRawPointer?, _ time: Double, _ outValues: UnsafeMutablePointer<Float>?, _ capacityElements: UInt64) -> UInt64 {
    guard let animated = mdl_borrow_object(handle) as? MDLAnimatedQuaternionArray,
          let outValues
    else {
        return 0
    }
    let values = animated.floatQuaternionArray(atTime: time)
    let copyCount = min(Int(capacityElements), values.count)
    guard copyCount > 0 else { return 0 }
    var flattened: [Float] = []
    flattened.reserveCapacity(copyCount * 4)
    for value in values.prefix(copyCount) {
        flattened.append(contentsOf: mdl_quaternion_to_array(value))
    }
    flattened.withUnsafeBufferPointer { buffer in
        outValues.initialize(from: buffer.baseAddress!, count: flattened.count)
    }
    return UInt64(copyCount)
}