A container for vertex data that you can use to create and update meshes using your own format.
Use `LowLevelMesh` when you want to bring your own vertex format to RealityKit
or update your data frequently.
To update your data in `LowLevelMesh`, you can either use Swift for CPU processing,
or Metal Compute Shaders for GPU processing.
- Note: Use ``MeshDescriptor`` for a simpler alternative to `LowLevelMesh`.
For information on loading a model from a USD or Reality file,
see <doc:loading-entities-from-a-file>.
Express your vertex by creating a ``LowLevelMesh/Descriptor-swift.struct`` that describes
your layout, along with the required index and vertex capacities.
This descriptor is similar to
<doc://com.apple.documentation/documentation/metal/mtlvertexdescriptor>,
with additional semantics that make the data available in your shaders.
To use `LowLevelMesh`, first define your own vertex structure, either in a Metal header
or using a Swift structure:
```swift
struct MyVertex {
var position: SIMD3<Float> = .zero
var color: UInt32 = .zero
}
```
Next, describe your structure to `LowLevelMesh` by creating a list of vertex attributes and a vertex layout.
This description informs `LowLevelMesh` how to represent the vertex data in memory:
```swift
extension MyVertex {
static var vertexAttributes: [LowLevelMesh.Attribute] = [
.init(semantic: .position, format: .float3, offset: MemoryLayout<Self>.offset(of: \.position)!),
.init(semantic: .color, format: .uchar4Normalized_bgra, offset: MemoryLayout<Self>.offset(of: \.color)!)
]
static var vertexLayouts: [LowLevelMesh.Layout] = [
.init(bufferIndex: 0, bufferStride: MemoryLayout<Self>.stride)
]
static var descriptor: LowLevelMesh.Descriptor {
var desc = LowLevelMesh.Descriptor()
desc.vertexAttributes = MyVertex.vertexAttributes
desc.vertexLayouts = MyVertex.vertexLayouts
desc.indexType = .uint32
return desc
}
}
```
Create a ``LowLevelMesh/Descriptor-swift.struct`` and `LowLevelMesh`,
and assign your mesh data and parts:
```swift
func triangleMesh() throws -> LowLevelMesh {
var desc = MyVertex.descriptor
desc.vertexCapacity = 3
desc.indexCapacity = 3
let mesh = try LowLevelMesh(descriptor: desc)
mesh.withUnsafeMutableBytes(bufferIndex: 0) { rawBytes in
let vertices = rawBytes.bindMemory(to: MyVertex.self)
vertices[0] = MyVertex(position: [-1, -1, 0], color: 0xFF00FF00)
vertices[1] = MyVertex(position: [ 1, -1, 0], color: 0xFFFF0000)
vertices[2] = MyVertex(position: [ 0, 1, 0], color: 0xFF0000FF)
}
mesh.withUnsafeMutableIndices { rawIndices in
let indices = rawIndices.bindMemory(to: UInt32.self)
indices[0] = 0
indices[1] = 1
indices[2] = 2
}
let meshBounds = BoundingBox(min: [-1, -1, 0], max: [1, 1, 0])
mesh.parts.replaceAll([
LowLevelMesh.Part(
indexCount: 3,
topology: .triangle,
bounds: meshBounds
)
])
return mesh
}
```
To finish, create a ``MeshResource`` from the `LowLevelMesh`, and add it to a ``ModelComponent``.
You can then add this model to any ``Entity`` in your scene:
```swift
func triangleEntity() throws -> Entity {
let lowLevelMesh = try triangleMesh()
let resource = try MeshResource(from: lowLevelMesh)
let modelComponent = ModelComponent(mesh: resource, materials: [UnlitMaterial()])
let entity = Entity()
entity.name = "Triangle"
entity.components.set(modelComponent)
entity.scale *= 0.1
return entity
}
```
The low-level mesh creates a triangular shape in your RealityKit scene:

The ``MeshResource`` retains a reference to the `LowLevelMesh`,
reflecting any changes when the renderer updates.
Constructs a low-level mesh from a descriptor.
- Parameter descriptor: An object that defines the structure of the low-level mesh.
- Throws: If the descriptor is invalid or if you do not successfully allocate its memory.
get · iOS 18.0+
descriptor
TypeScript
get descriptor(): Promise<LowLevelMesh_Descriptor>
Swift declaration
@MainActor final let descriptor: LowLevelMesh.Descriptor
The definition of the structure of this low-level mesh.
@MainActor func readIndices(using commandBuffer: any MTLCommandBuffer) -> any MTLBuffer
Retrieves the Metal index buffer for GPU reading.
- Parameter commandBuffer: The <doc://com.apple.documentation/documentation/metal/mtlcommandbuffer>
you intend to use for reading.
RealityKit waits for the command buffer to complete before discarding the buffer.
get · iOS 18.0+
indexCapacity
TypeScript
get indexCapacity(): Promise<number>
Swift declaration
@MainActor var indexCapacity: Int { get }
The capacity of the index buffer, measured in indices.
This value is equivalent to ``LowLevelMesh/Descriptor-swift.struct/indexCapacity``.
@MainActor func replaceIndices(using commandBuffer: any MTLCommandBuffer) -> any MTLBuffer
Retrieves a Metal index buffer that you can use to replace the indices of this low-level mesh.
The buffer's contents are in an uninitialized state.
- Parameter commandBuffer: The <doc://com.apple.documentation/documentation/metal/mtlcommandbuffer>
you intend to use for reading.
RealityKit waits for the command buffer to complete before discarding the buffer.
get · iOS 18.0+
vertexCapacity
TypeScript
get vertexCapacity(): Promise<number>
Swift declaration
@MainActor var vertexCapacity: Int { get }
The capacity of the vertex buffer, measured in vertices.
This value is equivalent to ``LowLevelMesh/Descriptor-swift.struct/vertexCapacity``.
Replaces a Metal vertex buffer synchronously on the CPU.
The buffer's contents are unspecified, and it is
the caller's responsibility to populate the buffer with valid data.
The buffer is only valid for the lifetime of the callback.
Replaces the index buffer synchronously on the CPU.
The buffer's contents are unspecified, and it is
the caller's responsibility to populate the buffer with valid data.
The buffer is only valid for the lifetime of the callback.
@MainActor func read(bufferIndex index: Int, using commandBuffer: any MTLCommandBuffer) -> any MTLBuffer
Retrieves a Metal vertex buffer at the specified index, for GPU reading.
- Parameters:
- index: The index of the buffer to read.
Use a value that is less than ``LowLevelMesh/Descriptor/vertexBufferCount``.
- commandBuffer: The <doc://com.apple.documentation/documentation/metal/mtlcommandbuffer> you intend
to use for reading.
RealityKit waits for the command buffer to complete before discarding the buffer.
get · iOS 18.0+
parts
TypeScript
get parts(): Promise<LowLevelMesh_PartsCollection>
Swift declaration
@MainActor var parts: LowLevelMesh.PartsCollection { get set }
A mutable collection of parts.
The parts of a ``LowLevelMesh`` object specify how to interpret the index buffer.
You can also use `parts` to customize the material index and primitive type.
@MainActor var parts: LowLevelMesh.PartsCollection { get set }
A mutable collection of parts.
The parts of a ``LowLevelMesh`` object specify how to interpret the index buffer.
You can also use `parts` to customize the material index and primitive type.
@MainActor func replace(bufferIndex index: Int, using commandBuffer: any MTLCommandBuffer) -> any MTLBuffer
Retrieves a Metal vertex buffer you can use to replace the contents of the specified buffer
on the GPU using Metal.
The buffer's contents are in an uninitialized state.
- Parameters:
- index: The index of the buffer to modify.
Use a value that is less than ``LowLevelMesh/Descriptor/vertexBufferCount``.
- commandBuffer: The <doc://com.apple.documentation/documentation/metal/mtlcommandbuffer>
you intend to use for buffer modifications.
RealityKit waits for the command buffer to complete before utilizing the buffer for rendering.