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 Darwin
import Foundation
import ModelIO
import simd

private func mdl_mesh_buffer_info(_ buffer: any MDLMeshBuffer) -> [String: Any] {
    [
        "length": buffer.length,
        "buffer_type": buffer.type.rawValue,
    ]
}

private func mdl_vertex_attribute_data_info(_ attributeData: MDLVertexAttributeData) -> [String: Any] {
    [
        "stride": attributeData.stride,
        "format": attributeData.format.rawValue,
        "buffer_size": attributeData.bufferSize,
    ]
}

@_cdecl("mdl_mesh_new_box")
public func mdl_mesh_new_box(
    _ extentX: Float,
    _ extentY: Float,
    _ extentZ: Float,
    _ segmentX: UInt32,
    _ segmentY: UInt32,
    _ segmentZ: UInt32,
    _ inwardNormals: Int32,
    _ geometryTypeRaw: Int32,
    _ outMesh: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outMesh else {
            throw ModelIOBridgeError.invalidArgument("missing output mesh pointer")
        }
        let mesh = MDLMesh(
            boxWithExtent: SIMD3<Float>(extentX, extentY, extentZ),
            segments: SIMD3<UInt32>(segmentX, segmentY, segmentZ),
            inwardNormals: inwardNormals != 0,
            geometryType: try mdl_geometry_type(geometryTypeRaw),
            allocator: nil
        )
        outMesh.pointee = mdl_retain(mesh)
    }
}

@_cdecl("mdl_mesh_new_ellipsoid")
public func mdl_mesh_new_ellipsoid(
    _ extentX: Float,
    _ extentY: Float,
    _ extentZ: Float,
    _ segmentX: UInt32,
    _ segmentY: UInt32,
    _ inwardNormals: Int32,
    _ hemisphere: Int32,
    _ geometryTypeRaw: Int32,
    _ outMesh: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outMesh else {
            throw ModelIOBridgeError.invalidArgument("missing output mesh pointer")
        }
        let extent = SIMD3<Float>(extentX, extentY, extentZ)
        let segments = SIMD2<UInt32>(segmentX, segmentY)
        let geometryType = try mdl_geometry_type(geometryTypeRaw)
        let mesh: MDLMesh
        if hemisphere != 0 {
            mesh = MDLMesh(
                hemisphereWithExtent: extent,
                segments: segments,
                inwardNormals: inwardNormals != 0,
                cap: true,
                geometryType: geometryType,
                allocator: nil
            )
        } else {
            mesh = MDLMesh(
                sphereWithExtent: extent,
                segments: segments,
                inwardNormals: inwardNormals != 0,
                geometryType: geometryType,
                allocator: nil
            )
        }
        outMesh.pointee = mdl_retain(mesh)
    }
}

@_cdecl("mdl_mesh_new_cylinder")
public func mdl_mesh_new_cylinder(
    _ extentX: Float,
    _ extentY: Float,
    _ extentZ: Float,
    _ segmentX: UInt32,
    _ segmentY: UInt32,
    _ inwardNormals: Int32,
    _ topCap: Int32,
    _ bottomCap: Int32,
    _ geometryTypeRaw: Int32,
    _ outMesh: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outMesh else {
            throw ModelIOBridgeError.invalidArgument("missing output mesh pointer")
        }
        let mesh = MDLMesh(
            cylinderWithExtent: SIMD3<Float>(extentX, extentY, extentZ),
            segments: SIMD2<UInt32>(segmentX, segmentY),
            inwardNormals: inwardNormals != 0,
            topCap: topCap != 0,
            bottomCap: bottomCap != 0,
            geometryType: try mdl_geometry_type(geometryTypeRaw),
            allocator: nil
        )
        outMesh.pointee = mdl_retain(mesh)
    }
}

@_cdecl("mdl_mesh_new_plane")
public func mdl_mesh_new_plane(
    _ extentX: Float,
    _ extentY: Float,
    _ extentZ: Float,
    _ segmentX: UInt32,
    _ segmentY: UInt32,
    _ geometryTypeRaw: Int32,
    _ outMesh: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outMesh else {
            throw ModelIOBridgeError.invalidArgument("missing output mesh pointer")
        }
        let mesh = MDLMesh(
            planeWithExtent: SIMD3<Float>(extentX, extentY, extentZ),
            segments: SIMD2<UInt32>(segmentX, segmentY),
            geometryType: try mdl_geometry_type(geometryTypeRaw),
            allocator: nil
        )
        outMesh.pointee = mdl_retain(mesh)
    }
}

@_cdecl("mdl_mesh_new_icosahedron")
public func mdl_mesh_new_icosahedron(
    _ extentX: Float,
    _ extentY: Float,
    _ extentZ: Float,
    _ inwardNormals: Int32,
    _ geometryTypeRaw: Int32,
    _ outMesh: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
    _ outError: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    mdl_run(outError) {
        guard let outMesh else {
            throw ModelIOBridgeError.invalidArgument("missing output mesh pointer")
        }
        let mesh = MDLMesh(
            icosahedronWithExtent: SIMD3<Float>(extentX, extentY, extentZ),
            inwardNormals: inwardNormals != 0,
            geometryType: try mdl_geometry_type(geometryTypeRaw),
            allocator: nil
        )
        outMesh.pointee = mdl_retain(mesh)
    }
}

@_cdecl("mdl_mesh_vertex_count")
public func mdl_mesh_vertex_count(_ handle: UnsafeMutableRawPointer?) -> UInt64 {
    guard let mesh = mdl_borrow_object(handle) as? MDLMesh else { return 0 }
    return UInt64(mesh.vertexCount)
}

@_cdecl("mdl_mesh_vertex_buffer_count")
public func mdl_mesh_vertex_buffer_count(_ handle: UnsafeMutableRawPointer?) -> UInt64 {
    guard let mesh = mdl_borrow_object(handle) as? MDLMesh else { return 0 }
    return UInt64(mesh.vertexBuffers.count)
}

@_cdecl("mdl_mesh_vertex_buffer_at")
public func mdl_mesh_vertex_buffer_at(
    _ handle: UnsafeMutableRawPointer?,
    _ index: UInt64
) -> UnsafeMutableRawPointer? {
    guard let mesh = mdl_borrow_object(handle) as? MDLMesh,
          index < UInt64(mesh.vertexBuffers.count)
    else {
        return nil
    }
    return mdl_retain(mesh.vertexBuffers[Int(index)] as AnyObject)
}

@_cdecl("mdl_mesh_submesh_count")
public func mdl_mesh_submesh_count(_ handle: UnsafeMutableRawPointer?) -> UInt64 {
    guard let mesh = mdl_borrow_object(handle) as? MDLMesh else { return 0 }
    return UInt64(mesh.submeshes?.count ?? 0)
}

@_cdecl("mdl_mesh_submesh_at")
public func mdl_mesh_submesh_at(
    _ handle: UnsafeMutableRawPointer?,
    _ index: UInt64
) -> UnsafeMutableRawPointer? {
    guard let mesh = mdl_borrow_object(handle) as? MDLMesh,
          let submeshes = mesh.submeshes,
          index < UInt64(submeshes.count),
          let submesh = submeshes[Int(index)] as? MDLSubmesh
    else {
        return nil
    }
    return mdl_retain(submesh)
}

@_cdecl("mdl_mesh_bounding_box")
public func mdl_mesh_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? MDLMesh)?.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_mesh_vertex_descriptor")
public func mdl_mesh_vertex_descriptor(_ handle: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
    guard let mesh = mdl_borrow_object(handle) as? MDLMesh else { return nil }
    return mdl_retain(mesh.vertexDescriptor)
}

@_cdecl("mdl_mesh_vertex_attribute_data")
public func mdl_mesh_vertex_attribute_data(
    _ handle: UnsafeMutableRawPointer?,
    _ attributeName: UnsafePointer<CChar>?
) -> UnsafeMutableRawPointer? {
    guard let mesh = mdl_borrow_object(handle) as? MDLMesh,
          let attributeName
    else {
        return nil
    }
    guard let attributeData = mesh.vertexAttributeData(forAttributeNamed: String(cString: attributeName)) else {
        return nil
    }
    return mdl_retain(attributeData)
}

@_cdecl("mdl_mesh_buffer_info_json")
public func mdl_mesh_buffer_info_json(_ handle: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<CChar>? {
    guard let buffer = mdl_borrow_object(handle) as? any MDLMeshBuffer else { return nil }
    return mdl_string(mdl_json_string(from: mdl_mesh_buffer_info(buffer)) ?? "{}")
}

@_cdecl("mdl_mesh_buffer_copy_bytes")
public func mdl_mesh_buffer_copy_bytes(
    _ handle: UnsafeMutableRawPointer?,
    _ outBytes: UnsafeMutablePointer<UInt8>?,
    _ capacity: UInt64
) -> UInt64 {
    guard let buffer = mdl_borrow_object(handle) as? any MDLMeshBuffer,
          let outBytes
    else {
        return 0
    }
    let map = buffer.map()
    let byteCount = min(Int(capacity), buffer.length)
    memcpy(outBytes, map.bytes, byteCount)
    return UInt64(byteCount)
}

@_cdecl("mdl_vertex_attribute_data_info_json")
public func mdl_vertex_attribute_data_info_json(_ handle: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<CChar>? {
    guard let attributeData = mdl_borrow_object(handle) as? MDLVertexAttributeData else { return nil }
    return mdl_string(mdl_json_string(from: mdl_vertex_attribute_data_info(attributeData)) ?? "{}")
}

@_cdecl("mdl_vertex_attribute_data_copy_bytes")
public func mdl_vertex_attribute_data_copy_bytes(
    _ handle: UnsafeMutableRawPointer?,
    _ outBytes: UnsafeMutablePointer<UInt8>?,
    _ capacity: UInt64
) -> UInt64 {
    guard let attributeData = mdl_borrow_object(handle) as? MDLVertexAttributeData,
          let outBytes
    else {
        return 0
    }
    let byteCount = min(Int(capacity), attributeData.bufferSize)
    memcpy(outBytes, attributeData.dataStart, byteCount)
    return UInt64(byteCount)
}