modelio-rs 0.3.1

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

@_silgen_name("mdlx_light_probe_irradiance_data_source_coefficients")
private func mdlx_light_probe_irradiance_data_source_coefficients(
    _ context: UnsafeMutableRawPointer?,
    _ x: Float,
    _ y: Float,
    _ z: Float,
    _ outValues: UnsafeMutablePointer<Float>?,
    _ capacity: UInt64
) -> UInt64

@_silgen_name("mdlx_light_probe_irradiance_data_source_release")
private func mdlx_light_probe_irradiance_data_source_release(_ context: UnsafeMutableRawPointer?)

private func mdl_probe_placement(_ rawValue: Int32) throws -> MDLProbePlacement {
    guard let placement = MDLProbePlacement(rawValue: Int(rawValue)) else {
        throw ModelIOBridgeError.invalidArgument("invalid MDLProbePlacement: \(rawValue)")
    }
    return placement
}

private final class RustLightProbeIrradianceDataSource: NSObject, MDLLightProbeIrradianceDataSource {
    var boundingBox: MDLAxisAlignedBoundingBox
    var sphericalHarmonicsLevel: UInt
    private let callbackContext: UnsafeMutableRawPointer?

    init(
        boundingBox: MDLAxisAlignedBoundingBox,
        sphericalHarmonicsLevel: UInt,
        callbackContext: UnsafeMutableRawPointer?
    ) {
        self.boundingBox = boundingBox
        self.sphericalHarmonicsLevel = sphericalHarmonicsLevel
        self.callbackContext = callbackContext
        super.init()
    }

    deinit {
        mdlx_light_probe_irradiance_data_source_release(callbackContext)
    }

    func sphericalHarmonicsCoefficients(atPosition position: SIMD3<Float>) -> Data {
        let valueCount = Int(
            mdlx_light_probe_irradiance_data_source_coefficients(
                callbackContext,
                position.x,
                position.y,
                position.z,
                nil,
                0
            )
        )
        guard valueCount > 0 else { return Data() }
        var values = [Float](repeating: 0, count: valueCount)
        let written = values.withUnsafeMutableBufferPointer { buffer in
            mdlx_light_probe_irradiance_data_source_coefficients(
                callbackContext,
                position.x,
                position.y,
                position.z,
                buffer.baseAddress,
                UInt64(buffer.count)
            )
        }
        let clampedCount = min(valueCount, Int(written))
        return values.withUnsafeBufferPointer { buffer in
            Data(bytes: buffer.baseAddress!, count: clampedCount * MemoryLayout<Float>.stride)
        }
    }
}

@_cdecl("mdl_light_probe_irradiance_data_source_new")
public func mdl_light_probe_irradiance_data_source_new(
    _ minX: Float,
    _ minY: Float,
    _ minZ: Float,
    _ maxX: Float,
    _ maxY: Float,
    _ maxZ: Float,
    _ sphericalHarmonicsLevel: UInt64,
    _ callbackContext: UnsafeMutableRawPointer?,
    _ outDataSource: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outDataSource else {
            throw ModelIOBridgeError.invalidArgument("missing output data source pointer")
        }
        let dataSource = RustLightProbeIrradianceDataSource(
            boundingBox: MDLAxisAlignedBoundingBox(
                maxBounds: SIMD3<Float>(maxX, maxY, maxZ),
                minBounds: SIMD3<Float>(minX, minY, minZ)
            ),
            sphericalHarmonicsLevel: UInt(sphericalHarmonicsLevel),
            callbackContext: callbackContext
        )
        outDataSource.pointee = mdl_retain(dataSource)
    }
}

@_cdecl("mdl_light_probe_irradiance_data_source_bounding_box")
public func mdl_light_probe_irradiance_data_source_bounding_box(
    _ handle: UnsafeMutableRawPointer?,
    _ outMinX: UnsafeMutablePointer<Float>?,
    _ outMinY: UnsafeMutablePointer<Float>?,
    _ outMinZ: UnsafeMutablePointer<Float>?,
    _ outMaxX: UnsafeMutablePointer<Float>?,
    _ outMaxY: UnsafeMutablePointer<Float>?,
    _ outMaxZ: UnsafeMutablePointer<Float>?
) {
    guard let dataSource = mdl_borrow_object(handle) as? RustLightProbeIrradianceDataSource else { return }
    outMinX?.pointee = dataSource.boundingBox.minBounds.x
    outMinY?.pointee = dataSource.boundingBox.minBounds.y
    outMinZ?.pointee = dataSource.boundingBox.minBounds.z
    outMaxX?.pointee = dataSource.boundingBox.maxBounds.x
    outMaxY?.pointee = dataSource.boundingBox.maxBounds.y
    outMaxZ?.pointee = dataSource.boundingBox.maxBounds.z
}

@_cdecl("mdl_light_probe_irradiance_data_source_set_bounding_box")
public func mdl_light_probe_irradiance_data_source_set_bounding_box(
    _ handle: UnsafeMutableRawPointer?,
    _ minX: Float,
    _ minY: Float,
    _ minZ: Float,
    _ maxX: Float,
    _ maxY: Float,
    _ maxZ: Float
) {
    guard let dataSource = mdl_borrow_object(handle) as? RustLightProbeIrradianceDataSource else { return }
    dataSource.boundingBox = MDLAxisAlignedBoundingBox(
        maxBounds: SIMD3<Float>(maxX, maxY, maxZ),
        minBounds: SIMD3<Float>(minX, minY, minZ)
    )
}

@_cdecl("mdl_light_probe_irradiance_data_source_spherical_harmonics_level")
public func mdl_light_probe_irradiance_data_source_spherical_harmonics_level(_ handle: UnsafeMutableRawPointer?) -> UInt64 {
    guard let dataSource = mdl_borrow_object(handle) as? RustLightProbeIrradianceDataSource else {
        return 0
    }
    return UInt64(dataSource.sphericalHarmonicsLevel)
}

@_cdecl("mdl_light_probe_irradiance_data_source_set_spherical_harmonics_level")
public func mdl_light_probe_irradiance_data_source_set_spherical_harmonics_level(
    _ handle: UnsafeMutableRawPointer?,
    _ sphericalHarmonicsLevel: UInt64
) {
    guard let dataSource = mdl_borrow_object(handle) as? RustLightProbeIrradianceDataSource else {
        return
    }
    dataSource.sphericalHarmonicsLevel = UInt(sphericalHarmonicsLevel)
}

@_cdecl("mdl_asset_place_light_probes")
public func mdl_asset_place_light_probes(
    _ density: Float,
    _ heuristicRaw: Int32,
    _ dataSourceHandle: UnsafeMutableRawPointer?
) -> UnsafeMutableRawPointer? {
    guard let dataSource = mdl_borrow_object(dataSourceHandle) as? RustLightProbeIrradianceDataSource,
          let heuristic = try? mdl_probe_placement(heuristicRaw)
    else {
        return nil
    }
    let probes = MDLAsset.placeLightProbes(withDensity: density, heuristic: heuristic, using: dataSource)
    return mdl_retain(probes as NSArray)
}

@_cdecl("mdl_light_probe_new")
public func mdl_light_probe_new(
    _ reflectiveTextureHandle: UnsafeMutableRawPointer?,
    _ irradianceTextureHandle: UnsafeMutableRawPointer?,
    _ outProbe: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outProbe else {
            throw ModelIOBridgeError.invalidArgument("missing output light probe pointer")
        }
        let reflectiveTexture = mdl_borrow_object(reflectiveTextureHandle) as? MDLTexture
        let irradianceTexture = mdl_borrow_object(irradianceTextureHandle) as? MDLTexture
        outProbe.pointee = mdl_retain(
            MDLLightProbe(reflectiveTexture: reflectiveTexture, irradianceTexture: irradianceTexture)
        )
    }
}

@_cdecl("mdl_light_probe_generate_spherical_harmonics_from_irradiance")
public func mdl_light_probe_generate_spherical_harmonics_from_irradiance(
    _ handle: UnsafeMutableRawPointer?,
    _ sphericalHarmonicsLevel: UInt64
) {
    guard let probe = mdl_borrow_object(handle) as? MDLLightProbe else { return }
    probe.generateSphericalHarmonics(fromIrradiance: Int(sphericalHarmonicsLevel))
}

@_cdecl("mdl_light_probe_reflective_texture")
public func mdl_light_probe_reflective_texture(_ handle: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
    guard let probe = mdl_borrow_object(handle) as? MDLLightProbe,
          let texture = probe.reflectiveTexture
    else {
        return nil
    }
    return mdl_retain(texture)
}

@_cdecl("mdl_light_probe_irradiance_texture")
public func mdl_light_probe_irradiance_texture(_ handle: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
    guard let probe = mdl_borrow_object(handle) as? MDLLightProbe,
          let texture = probe.irradianceTexture
    else {
        return nil
    }
    return mdl_retain(texture)
}

@_cdecl("mdl_light_probe_spherical_harmonics_level")
public func mdl_light_probe_spherical_harmonics_level(_ handle: UnsafeMutableRawPointer?) -> UInt64 {
    guard let probe = mdl_borrow_object(handle) as? MDLLightProbe else { return 0 }
    return UInt64(probe.sphericalHarmonicsLevel)
}

@_cdecl("mdl_light_probe_spherical_harmonics_coefficient_count")
public func mdl_light_probe_spherical_harmonics_coefficient_count(_ handle: UnsafeMutableRawPointer?) -> UInt64 {
    guard let probe = mdl_borrow_object(handle) as? MDLLightProbe,
          let coefficients = probe.sphericalHarmonicsCoefficients
    else {
        return 0
    }
    return UInt64(coefficients.count / MemoryLayout<Float>.stride)
}

@_cdecl("mdl_light_probe_copy_spherical_harmonics_coefficients")
public func mdl_light_probe_copy_spherical_harmonics_coefficients(
    _ handle: UnsafeMutableRawPointer?,
    _ outValues: UnsafeMutablePointer<Float>?,
    _ capacity: UInt64
) -> UInt64 {
    guard let probe = mdl_borrow_object(handle) as? MDLLightProbe,
          let coefficients = probe.sphericalHarmonicsCoefficients,
          let outValues
    else {
        return 0
    }
    let valueCount = min(Int(capacity), coefficients.count / MemoryLayout<Float>.stride)
    guard valueCount > 0 else { return 0 }
    coefficients.withUnsafeBytes { rawBuffer in
        guard let baseAddress = rawBuffer.baseAddress?.assumingMemoryBound(to: Float.self) else {
            return
        }
        outValues.initialize(from: baseAddress, count: valueCount)
    }
    return UInt64(valueCount)
}