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

private func mdl_asset_info(_ asset: MDLAsset) -> [String: Any] {
    var info: [String: Any] = [
        "count": asset.count,
        "frame_interval": asset.frameInterval,
        "start_time": asset.startTime,
        "end_time": asset.endTime,
        "bounding_box": mdl_bounding_box(asset.boundingBox),
        "up_axis": [asset.upAxis.x, asset.upAxis.y, asset.upAxis.z],
    ]
    if let url = asset.url {
        info["url"] = url.path
    }
    return info
}

@_cdecl("mdl_asset_new_empty")
public func mdl_asset_new_empty(
    _ outAsset: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outAsset else {
            throw ModelIOBridgeError.invalidArgument("missing output asset pointer")
        }
        outAsset.pointee = mdl_retain(MDLAsset(bufferAllocator: nil))
    }
}

@_cdecl("mdl_asset_new_with_url")
public func mdl_asset_new_with_url(
    _ path: UnsafePointer<CChar>?,
    _ outAsset: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let path, let outAsset else {
            throw ModelIOBridgeError.invalidArgument("missing asset path or output pointer")
        }
        let url = URL(fileURLWithPath: String(cString: path))
        outAsset.pointee = mdl_retain(MDLAsset(url: url))
    }
}

@_cdecl("mdl_asset_can_import_file_extension")
public func mdl_asset_can_import_file_extension(_ pathExtension: UnsafePointer<CChar>?) -> Int32 {
    guard let pathExtension else { return 0 }
    return MDLAsset.canImportFileExtension(String(cString: pathExtension)) ? 1 : 0
}

@_cdecl("mdl_asset_can_export_file_extension")
public func mdl_asset_can_export_file_extension(_ pathExtension: UnsafePointer<CChar>?) -> Int32 {
    guard let pathExtension else { return 0 }
    return MDLAsset.canExportFileExtension(String(cString: pathExtension)) ? 1 : 0
}

@_cdecl("mdl_asset_info_json")
public func mdl_asset_info_json(_ handle: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<CChar>? {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return nil }
    return mdl_string(mdl_json_string(from: mdl_asset_info(asset)) ?? "{}")
}

@_cdecl("mdl_asset_count")
public func mdl_asset_count(_ handle: UnsafeMutableRawPointer?) -> UInt64 {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return 0 }
    return UInt64(asset.count)
}

@_cdecl("mdl_asset_bounding_box")
public func mdl_asset_bounding_box(
    _ handle: UnsafeMutableRawPointer?,
    _ outMinX: UnsafeMutablePointer<Float>?,
    _ outMinY: UnsafeMutablePointer<Float>?,
    _ outMinZ: UnsafeMutablePointer<Float>?,
    _ outMaxX: UnsafeMutablePointer<Float>?,
    _ outMaxY: UnsafeMutablePointer<Float>?,
    _ outMaxZ: UnsafeMutablePointer<Float>?
) {
    let zero = MDLAxisAlignedBoundingBox(maxBounds: SIMD3<Float>(repeating: 0), minBounds: SIMD3<Float>(repeating: 0))
    let boundingBox = (mdl_borrow_object(handle) as? MDLAsset)?.boundingBox ?? zero
    outMinX?.pointee = boundingBox.minBounds.x
    outMinY?.pointee = boundingBox.minBounds.y
    outMinZ?.pointee = boundingBox.minBounds.z
    outMaxX?.pointee = boundingBox.maxBounds.x
    outMaxY?.pointee = boundingBox.maxBounds.y
    outMaxZ?.pointee = boundingBox.maxBounds.z
}

@_cdecl("mdl_asset_bounding_box_at_time")
public func mdl_asset_bounding_box_at_time(
    _ handle: UnsafeMutableRawPointer?,
    _ time: Double,
    _ outMinX: UnsafeMutablePointer<Float>?,
    _ outMinY: UnsafeMutablePointer<Float>?,
    _ outMinZ: UnsafeMutablePointer<Float>?,
    _ outMaxX: UnsafeMutablePointer<Float>?,
    _ outMaxY: UnsafeMutablePointer<Float>?,
    _ outMaxZ: UnsafeMutablePointer<Float>?
) {
    let zero = MDLAxisAlignedBoundingBox(maxBounds: SIMD3<Float>(repeating: 0), minBounds: SIMD3<Float>(repeating: 0))
    let boundingBox = (mdl_borrow_object(handle) as? MDLAsset)?.boundingBox(atTime: time) ?? zero
    outMinX?.pointee = boundingBox.minBounds.x
    outMinY?.pointee = boundingBox.minBounds.y
    outMinZ?.pointee = boundingBox.minBounds.z
    outMaxX?.pointee = boundingBox.maxBounds.x
    outMaxY?.pointee = boundingBox.maxBounds.y
    outMaxZ?.pointee = boundingBox.maxBounds.z
}

@_cdecl("mdl_asset_url_string")
public func mdl_asset_url_string(_ handle: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<CChar>? {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset,
          let url = asset.url
    else {
        return nil
    }
    return mdl_string(url.path)
}

@_cdecl("mdl_asset_object_at_index")
public func mdl_asset_object_at_index(
    _ handle: UnsafeMutableRawPointer?,
    _ index: UInt64
) -> UnsafeMutableRawPointer? {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset,
          index < UInt64(asset.count)
    else {
        return nil
    }
    return mdl_retain(asset.object(at: Int(index)))
}

@_cdecl("mdl_asset_mesh_at_index")
public func mdl_asset_mesh_at_index(
    _ handle: UnsafeMutableRawPointer?,
    _ index: UInt64
) -> UnsafeMutableRawPointer? {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset,
          index < UInt64(asset.count),
          let mesh = asset.object(at: Int(index)) as? MDLMesh
    else {
        return nil
    }
    return mdl_retain(mesh)
}

@_cdecl("mdl_asset_object_at_path")
public func mdl_asset_object_at_path(
    _ handle: UnsafeMutableRawPointer?,
    _ path: UnsafePointer<CChar>?
) -> UnsafeMutableRawPointer? {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset,
          let path
    else {
        return nil
    }
    return mdl_retain(asset.object(atPath: String(cString: path)))
}

@_cdecl("mdl_asset_add_object")
public func mdl_asset_add_object(_ handle: UnsafeMutableRawPointer?, _ objectHandle: UnsafeMutableRawPointer?) {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset,
          let object = mdl_borrow_object(objectHandle) as? MDLObject
    else {
        return
    }
    asset.add(object)
}

@_cdecl("mdl_asset_remove_object")
public func mdl_asset_remove_object(_ handle: UnsafeMutableRawPointer?, _ objectHandle: UnsafeMutableRawPointer?) {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset,
          let object = mdl_borrow_object(objectHandle) as? MDLObject
    else {
        return
    }
    asset.remove(object)
}

@_cdecl("mdl_asset_load_textures")
public func mdl_asset_load_textures(_ handle: UnsafeMutableRawPointer?) {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return }
    asset.loadTextures()
}

@_cdecl("mdl_asset_export_to_url")
public func mdl_asset_export_to_url(
    _ handle: UnsafeMutableRawPointer?,
    _ path: UnsafePointer<CChar>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let asset = mdl_borrow_object(handle) as? MDLAsset,
              let path
        else {
            throw ModelIOBridgeError.invalidArgument("missing asset or export path")
        }
        let url = URL(fileURLWithPath: String(cString: path))
        try asset.export(to: url)
    }
}

@_cdecl("mdl_asset_frame_interval")
public func mdl_asset_frame_interval(_ handle: UnsafeMutableRawPointer?) -> Double {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return 0 }
    return asset.frameInterval
}

@_cdecl("mdl_asset_set_frame_interval")
public func mdl_asset_set_frame_interval(_ handle: UnsafeMutableRawPointer?, _ value: Double) {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return }
    asset.frameInterval = value
}

@_cdecl("mdl_asset_start_time")
public func mdl_asset_start_time(_ handle: UnsafeMutableRawPointer?) -> Double {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return 0 }
    return asset.startTime
}

@_cdecl("mdl_asset_set_start_time")
public func mdl_asset_set_start_time(_ handle: UnsafeMutableRawPointer?, _ value: Double) {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return }
    asset.startTime = value
}

@_cdecl("mdl_asset_end_time")
public func mdl_asset_end_time(_ handle: UnsafeMutableRawPointer?) -> Double {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return 0 }
    return asset.endTime
}

@_cdecl("mdl_asset_set_end_time")
public func mdl_asset_set_end_time(_ handle: UnsafeMutableRawPointer?, _ value: Double) {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return }
    asset.endTime = value
}

@_cdecl("mdl_asset_up_axis")
public func mdl_asset_up_axis(
    _ handle: UnsafeMutableRawPointer?,
    _ outX: UnsafeMutablePointer<Float>?,
    _ outY: UnsafeMutablePointer<Float>?,
    _ outZ: UnsafeMutablePointer<Float>?
) {
    let upAxis = (mdl_borrow_object(handle) as? MDLAsset)?.upAxis ?? SIMD3<Float>(0, 1, 0)
    outX?.pointee = upAxis.x
    outY?.pointee = upAxis.y
    outZ?.pointee = upAxis.z
}

@_cdecl("mdl_asset_set_up_axis")
public func mdl_asset_set_up_axis(_ handle: UnsafeMutableRawPointer?, _ x: Float, _ y: Float, _ z: Float) {
    guard let asset = mdl_borrow_object(handle) as? MDLAsset else { return }
    asset.upAxis = SIMD3<Float>(x, y, z)
}