<!--
{
  "availability" : [
    "iOS: 18.0.0 -",
    "iPadOS: 18.0.0 -",
    "macCatalyst: 18.0.0 -",
    "macOS: 15.0.0 -",
    "tvOS: 26.0.0 -",
    "visionOS: 2.0.0 -"
  ],
  "documentType" : "symbol",
  "framework" : "RealityKit",
  "identifier" : "/documentation/RealityKit/LowLevelMesh",
  "metadataVersion" : "0.1.0",
  "role" : "Class",
  "symbol" : {
    "kind" : "Class",
    "modules" : [
      "RealityKit"
    ],
    "preciseIdentifier" : "s:17RealityFoundation12LowLevelMeshC"
  },
  "title" : "LowLevelMesh"
}
-->

# LowLevelMesh

A container for vertex data that you can use to create and update meshes using your own format.

```
@MainActor class LowLevelMesh
```

## Overview

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 ``doc://com.apple.RealityKit/documentation/RealityKit/MeshDescriptor`` for a simpler alternative to `LowLevelMesh`.
> For information on loading a model from a USD or Reality file,
> see <doc://com.apple.RealityKit/documentation/RealityKit/loading-entities-from-a-file>.

Express your vertex by creating a [`LowLevelMesh.Descriptor`](/documentation/RealityKit/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`](/documentation/RealityKit/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`](/documentation/RealityKit/MeshResource) from the `LowLevelMesh`, and add it to a [`ModelComponent`](/documentation/RealityKit/ModelComponent).
You can then add this model to any [`Entity`](/documentation/RealityKit/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:

![A screenshot of an isosceles triangle, floating in a kitchen scene. The triangle appears light gray in color.](images/com.apple.RealityKit/lowlevelmesh-triangle-unlit.jpg)

The [`MeshResource`](/documentation/RealityKit/MeshResource) retains a reference to the `LowLevelMesh`,
reflecting any changes when the renderer updates.

## Topics

### Creating a low-level mesh

[`init(descriptor:)`](/documentation/RealityKit/LowLevelMesh/init(descriptor:))

Constructs a low-level mesh from a descriptor.

### Describing a low-level mesh

[`descriptor`](/documentation/RealityKit/LowLevelMesh/descriptor-swift.property)

The definition of the structure of this low-level mesh.

[`parts`](/documentation/RealityKit/LowLevelMesh/parts)

A mutable collection of parts.

[`indexCapacity`](/documentation/RealityKit/LowLevelMesh/indexCapacity)

The capacity of the index buffer, measured in indices.

[`vertexCapacity`](/documentation/RealityKit/LowLevelMesh/vertexCapacity)

The capacity of the vertex buffer, measured in vertices.

### Accessing mesh data on the CPU with Swift

[`withUnsafeBytes(bufferIndex:_:)`](/documentation/RealityKit/LowLevelMesh/withUnsafeBytes(bufferIndex:_:))

Reads a Metal vertex buffer synchronously on the CPU.

[`withUnsafeMutableBytes(bufferIndex:_:)`](/documentation/RealityKit/LowLevelMesh/withUnsafeMutableBytes(bufferIndex:_:))

Updates a Metal vertex buffer synchronously on the CPU.

[`withUnsafeIndices(_:)`](/documentation/RealityKit/LowLevelMesh/withUnsafeIndices(_:))

Reads the index buffer synchronously on the CPU.

[`withUnsafeMutableIndices(_:)`](/documentation/RealityKit/LowLevelMesh/withUnsafeMutableIndices(_:))

Updates the index buffer synchronously on the CPU.

[`replaceUnsafeMutableBytes(bufferIndex:_:)`](/documentation/RealityKit/LowLevelMesh/replaceUnsafeMutableBytes(bufferIndex:_:))

Replaces a Metal vertex buffer synchronously on the CPU.

[`replaceUnsafeMutableIndices(_:)`](/documentation/RealityKit/LowLevelMesh/replaceUnsafeMutableIndices(_:))

Replaces the index buffer synchronously on the CPU.

### Accessing mesh data on the GPU with Metal

[`read(bufferIndex:using:)`](/documentation/RealityKit/LowLevelMesh/read(bufferIndex:using:))

Retrieves a Metal vertex buffer at the specified index, for GPU reading.

[`readIndices(using:)`](/documentation/RealityKit/LowLevelMesh/readIndices(using:))

Retrieves the Metal index buffer for GPU reading.

[`replace(bufferIndex:using:)`](/documentation/RealityKit/LowLevelMesh/replace(bufferIndex:using:))

Retrieves a Metal vertex buffer you can use to replace the contents of the specified buffer
on the GPU using Metal.

[`replaceIndices(using:)`](/documentation/RealityKit/LowLevelMesh/replaceIndices(using:))

Retrieves a Metal index buffer that you can use to replace the indices of this low-level mesh.



---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)