Skip to content

EditableMesh

Not yet Class Physics NotCreatable

Object which allows for the runtime creation and manipulation of meshes.

Not implemented yet

Runtime mesh editing is not implemented.

0 Nova members · 102 modern Roblox members not implemented yet

Description

EditableMesh changes the applied visual mesh when linked to a MeshPart, allowing for querying and modification of the mesh both in Studio and in-game.

Enabling for published games

For security purposes, using EditableMesh fails by default in published games. To enable it, open the Creator Dashboard, go to the game's Configure ⟩ Settings, and toggle on Enable Mesh / Image APIs. Alternatively, in Studio, open Experience Settings and toggle on Allow Mesh / Image APIs under Security. Remember to review the Terms of Use before enabling the toggle.

Permissions

To prevent misuse, AssetService.CreateEditableMeshAsync() only allows you to load and edit mesh assets if any of the following is true:

  • Owned by or explicitly shared with the game owner.
  • Owned by or explicitly shared with the logged in Studio user.
  • Owned by or explicitly shared with the logged in player if the EditableMesh is on the client side.
  • Owned by a group where the game owner, Studio user, or player has a role with permission to edit the group's assets. See Roles and permissions for more information.

See Grant permissions to learn how to share assets with users or groups.

The APIs throw an error if they are used to load an asset that does not meet the criteria above.

Memory limits

Editable assets are currently expensive for memory usage. To minimize its impact on client performance, EditableMesh has strict client-side memory budgets, although the server, Studio, and plugins operate with unlimited memory. Using FixedSize may help you stay within the memory budget and, in some scenarios, linking one EditableMesh to multiple MeshParts (multi-referencing) can help with memory optimization.

Creation and display

An EditableMesh can be created from an existing Content of a MeshPart or a mesh ID using AssetService.CreateEditableMeshAsync(), or a blank EditableMesh can be created with AssetService.CreateEditableMesh(). It can then be displayed, modified, and its collision model updated. Not all of the steps are necessary; for example, you might want to create an EditableMesh just to raycast without ever displaying it.

local AssetService = game:GetService("AssetService")

-- Create empty EditableMesh
local editableMesh = AssetService:CreateEditableMesh()

-- Create EditableMesh from asset ID
local editableMeshFromAsset = nil
local success, errorMessage = pcall(function()
    editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID))
end)

-- Create EditableMesh from another EditableMesh
local editableMeshFromAnother = nil
local success, errorMessage = pcall(function()
    editableMeshFromAnother = AssetService:CreateEditableMeshAsync(Content.fromObject(OTHER_EDITABLE_MESH))
end)

-- Create EditableMesh from MeshPart
local editableMeshFromMeshPart = nil
local success, errorMessage = pcall(function()
    editableMeshFromMeshPart = AssetService:CreateEditableMeshAsync(MESH_PART.MeshContent)
end)

An EditableMesh is displayed when it's linked to a new MeshPart, through AssetService.CreateMeshPartAsync(). You can create more MeshPart instances that reference the same EditableMesh Content, or link to an existing MeshPart through MeshPart.ApplyMesh().

local AssetService = game:GetService("AssetService")
local Workspace = game:GetService("Workspace")

-- Create EditableMesh from asset ID
local editableMeshFromAsset = nil
local success, errorMessage = pcall(function()
    editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID))
end)

-- Create new MeshPart linked to the EditableMesh
local newMeshPart = nil
local success, errorMessage = pcall(function()
    newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMeshFromAsset))
end)

-- Alternatively, link the new MeshPart created above to an existing MeshPart
local existingMeshPart = Workspace:FindFirstChild("EXISTING_MESH_PART")
existingMeshPart:ApplyMesh(newMeshPart)

To recalculate collision and fluid geometry after editing, you can again call AssetService.CreateMeshPartAsync() and MeshPart.ApplyMesh() to update an existing MeshPart. It's generally recommended to do this at the end of a conceptual edit, not after individual calls to methods that manipulate geometry. Visual changes to the mesh will always be immediately reflected by the engine, without the need to call AssetService.CreateMeshPartAsync().

Fixed-size meshes

When creating an EditableMesh from an existing mesh asset (via AssetService.CreateEditableMeshAsync()), the resulting editable mesh is fixed-size by default. Fixed-size meshes are more efficient in terms of memory but you cannot change the number of vertices, faces, or attributes. Only the values of vertex attributes and positions can be edited.

local AssetService = game:GetService("AssetService")

-- Create EditableMesh without fixed-size default
local editableMeshFromAsset = nil
local success, errorMessage = pcall(function()
    editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID), {FixedSize = false})
end)

Stable IDs

Many EditableMesh methods take vertex, normal, UV, color and face IDs. These are represented as integers in Luau but they require some special handling. The main difference is that IDs are stable and they remain the same even if other parts of the mesh change. For example, if an EditableMesh has five vertices {1, 2, 3, 4, 5} and you remove vertex 4, the new vertices will be {1, 2, 3, 5}.

Note that the IDs are not guaranteed to be in order and there may be holes in the numbering, so when iterating through vertices or faces, you should iterate through the table returned by GetVertices() or GetFaces().

For debugging purposes, it can be very helpful to get a more readable string for a stable ID with IdDebugString().

Split vertex attributes

A vertex is a corner of a face, and topologically connects faces together. Each vertex has a single position, while its normal, UV coordinate, and color (with transparency) are stored per face corner. A vertex shared by multiple faces can use the same values on every face, or different values on each.

Sometimes it's useful for all faces that touch a vertex to use the same attribute values, but sometimes you'll want different faces to use different attribute values on the same vertex. For example, on a smooth sphere, each vertex will only have a single normal. In contrast, at the corner of a cube, the vertex will have 3 different normals (one for each adjacent face). You can also have seams in the UV coordinates or sharp changes in the vertex colors.

When creating faces with AddFace() or AddTriangle(), each corner of the new face is automatically assigned a normal, UV, and color attribute. If a vertex already belongs to an existing face, the new face reuses that vertex's existing attributes (producing smooth shading and continuous UVs). If the vertex is new, fresh attribute IDs are created with default values (auto-computed normal, UV of (0, 0), white color). To create a seam, you should create new attributes and set them on the face. For example, this code will create a sharp cube:

local AssetService = game:GetService("AssetService")

-- Given 4 vertex IDs, adds a new normal and a quad face, making a sharp quad
local function addSharpQuad(editableMesh, vid0, vid1, vid2, vid3)
    local nid = editableMesh:AddNormal() -- This creates a normal ID which is automatically computed

    local fid = editableMesh:AddFace(vid0, vid1, vid2, vid3)
    editableMesh:SetFaceNormals(fid, {nid, nid, nid, nid})
end

-- Makes a cube with creased edges between the 6 sides
local function makeSharpCube()
    local editableMesh = AssetService:CreateEditableMesh()

    local v1 = editableMesh:AddVertex(Vector3.new(0, 0, 0))
    local v2 = editableMesh:AddVertex(Vector3.new(1, 0, 0))
    local v3 = editableMesh:AddVertex(Vector3.new(0, 1, 0))
    local v4 = editableMesh:AddVertex(Vector3.new(1, 1, 0))
    local v5 = editableMesh:AddVertex(Vector3.new(0, 0, 1))
    local v6 = editableMesh:AddVertex(Vector3.new(1, 0, 1))
    local v7 = editableMesh:AddVertex(Vector3.new(0, 1, 1))
    local v8 = editableMesh:AddVertex(Vector3.new(1, 1, 1))

    addSharpQuad(editableMesh, v5, v6, v8, v7) -- Front
    addSharpQuad(editableMesh, v1, v3, v4, v2) -- Back
    addSharpQuad(editableMesh, v1, v5, v7, v3) -- Left
    addSharpQuad(editableMesh, v2, v4, v8, v6) -- Right
    addSharpQuad(editableMesh, v1, v2, v6, v5) -- Bottom
    addSharpQuad(editableMesh, v3, v7, v8, v4) -- Top

    editableMesh:RemoveUnused()
    return editableMesh
end

Winding

Mesh faces have a front side and a back side. When drawing meshes, only the front of the faces are drawn by default, although you can change this by setting the mesh' DoubleSided property to true.

The order of the vertices around the face determines whether you are looking at the front or the back. The front of the face is visible when the vertices go counterclockwise around it.

Order of the vertices around the face

FACS poses

Animatable heads use the Facial Action Coding System (FACS). See the FACS poses reference for helpful information when using GetFacsPoses() and similar methods.

Each FACS pose is specified by an FacsActionUnit value. For the FACS pose, virtual bones can each have a CFrame that transforms the bones' initial CFrame in the bind pose of the mesh into the CFrame for that FACS action unit's pose. All bone CFrames are in the mesh's local space.

These FACS poses are blended together during animation. Sometimes, the blending of the base poses produces poor results. In those cases, you can override the blending of specific combinations of base poses with a corrective pose that is more pleasing. A corrective pose is specified by 2 or 3 FacsActionUnit values. Like a base FACS pose, for a corrective pose, virtual bones can each have a CFrame that transforms the bones' initial CFrame in the bind pose of the mesh into the CFrame for that FACS corrective.

Please note that corrective poses that operate on both left and right action units are not currently expressible. For example, using LeftCheekPuff and RightEyeClosed together in a corrective pose is not currently possible.

Batching

The batch methods let you create, read, update, and remove many mesh elements in a single call instead of invoking the singular methods (AddVertex(), SetPosition(), GetColor(), and so on) once per element. Batching your mesh operations is typically much more performant than handling them one by one.

Rather than one method per attribute, each batch method shares a small set of general entry points and uses an MeshAttribute value (or the type already encoded in each mesh ID) to determine which attribute you mean. For example, BatchSetValues() sets positions when called with vertex IDs, but sets normals when called with normal IDs. You cannot mix-and-match ID types within a single call.

Batch setters take parallel arrays: element i of every argument array describes the same logical operation. In Class.EditableMesh:BatchSetValues()|BatchSetValues(ids, values), values[i] is written to ids[i]. All argument arrays must be the same length or the call errors. Batch getters take an array of IDs and return results in the same order.

If a mesh ID is invalid, batching still applies updates to all elements before the invalid ID, then stops.

Limitations

EditableMesh currently has a limit of 60,000 vertices and 20,000 triangles. Faces can be triangles or quads, and each quad counts as 2 triangles toward the triangle limit. Attempting to add too many vertices or triangles will cause an error.

Inheritance

Object → EditableMesh

Properties

Member Signature Summary
EditableMeshDataHolder EditableMeshDataHolder: NetAssetRef —
FixedSize FixedSize: bool Returns true if a mesh is fixed-size.
IsReplicatedCopy IsReplicatedCopy: bool —
MeshData MeshData: SharedString —

Methods

Member Signature Summary
AddBone EditableMesh:AddBone(boneProperties: Dictionary): int64 Adds a new bone and returns a bone ID.
AddColor EditableMesh:AddColor(color: Color3, alpha: float): int64 Adds a new color to the geometry and returns a color ID.
AddFace EditableMesh:AddFace(vertexIds: Array): int64 Adds a new triangle or quad face to the mesh and returns a face ID.
AddNormal EditableMesh:AddNormal(normal: Vector3?): int64 Adds a new normal to the geometry and returns a normal ID.
AddTriangle EditableMesh:AddTriangle(vertexId0: int64, vertexId1: int64, vertexId2: int64): int64 Adds a new triangle to the mesh and returns a face ID.
AddUV EditableMesh:AddUV(uv: Vector2): int64 Adds a new UV to the geometry and returns a UV ID.
AddVertex EditableMesh:AddVertex(p: Vector3): int64 Adds a new vertex to the geometry and returns a vertex ID.
BatchAdd EditableMesh:BatchAdd(attr: MeshAttribute, data: Array): Array Creates new mesh elements of the given attribute type and returns their IDs.
BatchGetFaceAttributes EditableMesh:BatchGetFaceAttributes(attr: MeshAttribute, faceIds: Array): Array Returns the per-corner attribute IDs for each face in a batch.
BatchGetValues EditableMesh:BatchGetValues(ids: Array): Tuple Returns attribute values for each ID in a batch.
BatchGetVertexAttributes EditableMesh:BatchGetVertexAttributes(attr: MeshAttribute, vertexIds: Array): Array Returns the attribute IDs associated with each vertex in a batch.
BatchGetVertexFaceAttributes EditableMesh:BatchGetVertexFaceAttributes(attr: MeshAttribute, vertexIds: Array, faceIds: Array): Array Returns the attribute ID at each specified corner for a batch of vertex–face pairs.
BatchRemove EditableMesh:BatchRemove(ids: Array): null Removes a batch of faces.
BatchSetFaceAttributes EditableMesh:BatchSetFaceAttributes(faceIds: Array, attrIdArrays: Array): null Sets the per-corner attribute IDs for each face in a batch.
BatchSetValues EditableMesh:BatchSetValues(ids: Array, values: Array): null Writes attribute values to a batch of mesh element IDs.
BatchSetVertexFaceAttributes EditableMesh:BatchSetVertexFaceAttributes(vertexIds: Array, faceIds: Array, attrIds: Array): null Sets the attribute at a specific corner for each vertex–face pair in a batch.
Clear EditableMesh:Clear(): null Clears all of an EditableMesh's geometry.
Destroy EditableMesh:Destroy(): null Destroys the mesh.
FindClosestPointOnSurface EditableMesh:FindClosestPointOnSurface(point: Vector3): Tuple Finds the closest point on the mesh's surface.
FindClosestVertex EditableMesh:FindClosestVertex(toThisPoint: Vector3): int64 Finds the closest vertex to a specific point in space.
FindVerticesWithinSphere EditableMesh:FindVerticesWithinSphere(center: Vector3, radius: float): Array Finds all vertices within a specific sphere.
GetAdjacentFaces EditableMesh:GetAdjacentFaces(faceId: int64): Array Returns a list of faces adjacent to a given face.
GetAdjacentVertices EditableMesh:GetAdjacentVertices(vertexId: int64): Array Returns a list of vertices adjacent to a given vertex.
GetBoneByName EditableMesh:GetBoneByName(boneName: string): int64 Finds the bone ID of the bone with the given name.
GetBoneCFrame EditableMesh:GetBoneCFrame(boneId: int64): CFrame Returns the initial CFrame of the bone in the bind pose of the mesh.
GetBoneIsVirtual EditableMesh:GetBoneIsVirtual(boneId: int64): bool Returns true if the bone is virtual.
GetBoneName EditableMesh:GetBoneName(boneId: int64): string Returns the bone name.
GetBoneParent EditableMesh:GetBoneParent(boneId: int64): int64 Returns the parent bone ID, if any.
GetBones EditableMesh:GetBones(): Array Returns all bones of the mesh.
GetCenter EditableMesh:GetCenter(): Vector3 Returns the center of the mesh's axis-aligned bounding box.
GetColor EditableMesh:GetColor(colorId: int64): Color3? Returns the color for the given color ID.
GetColorAlpha EditableMesh:GetColorAlpha(colorId: int64): float? Returns the color alpha (transparency) at the given color ID.
GetColors EditableMesh:GetColors(): Array Returns all colors of the mesh.
GetFaceColors EditableMesh:GetFaceColors(faceId: int64): Array Returns the face's color IDs for the vertices on the face.
GetFaceNormals EditableMesh:GetFaceNormals(faceId: int64): Array Returns the face's normal IDs for the vertices on the face.
GetFaces EditableMesh:GetFaces(): Array Returns all faces of the mesh.
GetFacesWithAttribute EditableMesh:GetFacesWithAttribute(id: int64): Array Returns a list of faces that use a given attribute ID.
GetFacesWithColor EditableMesh:GetFacesWithColor(colorId: int64): Array Returns an array of face IDs that use the given color ID.
GetFacesWithNormal EditableMesh:GetFacesWithNormal(normalId: int64): Array Returns an array of face IDs that use the given normal ID.
GetFacesWithUV EditableMesh:GetFacesWithUV(uvId: int64): Array Returns an array of face IDs that use the given UV ID.
GetFaceUVs EditableMesh:GetFaceUVs(faceId: int64): Array Returns the face's UV IDs for the vertices on the face.
GetFaceVertices EditableMesh:GetFaceVertices(faceId: int64): Array Returns the face's vertex IDs.
GetFacsCorrectivePose EditableMesh:GetFacsCorrectivePose(actions: Array): Tuple Returns bone IDs and bone CFrames for all bones in a specific FACS corrective pose.
GetFacsCorrectivePoses EditableMesh:GetFacsCorrectivePoses(): Array Returns all FACS corrective poses that are in use.
GetFacsPose EditableMesh:GetFacsPose(action: FacsActionUnit): Tuple Returns bone IDs and bone CFrames for all bones in a specific FACS action unit.
GetFacsPoses EditableMesh:GetFacsPoses(): Array Returns all FACS action units that have poses defined.
GetNormal EditableMesh:GetNormal(normalId: int64): Vector3? Returns the normal vector for the given normal ID.
GetNormals EditableMesh:GetNormals(): Array Returns all normals of the mesh.
GetPosition EditableMesh:GetPosition(vertexId: int64): Vector3 Gets the position of a vertex.
GetSize EditableMesh:GetSize(): Vector3 Returns the size of the mesh's axis-aligned bounding box.
GetUV EditableMesh:GetUV(uvId: int64): Vector2? Returns UV coordinates at the given UV ID.
GetUVs EditableMesh:GetUVs(): Array Returns all UVs of the mesh.
GetVertexBones EditableMesh:GetVertexBones(vertexId: int64): Array Returns all bone IDs that are associated with the vertex for skinning.
GetVertexBoneWeights EditableMesh:GetVertexBoneWeights(vertexId: int64): Array Returns skinning blend weights for each bone that is associated with the vertex.
GetVertexColors EditableMesh:GetVertexColors(vertexId: int64): Array Returns the color IDs of the faces attached to the given vertex.
GetVertexFaceColor EditableMesh:GetVertexFaceColor(vertexId: int64, faceId: int64): int64 Returns the color ID of a vertex/face pair.
GetVertexFaceNormal EditableMesh:GetVertexFaceNormal(vertexId: int64, faceId: int64): int64 Returns the normal ID of a vertex/face pair.
GetVertexFaces EditableMesh:GetVertexFaces(vertexId: int64): Array Returns the face IDs of the faces attached to the given vertex.
GetVertexFaceUV EditableMesh:GetVertexFaceUV(vertexId: int64, faceId: int64): int64 Returns the UV ID of a vertex/face pair.
GetVertexNormals EditableMesh:GetVertexNormals(vertexId: int64): Array Returns the normal IDs of the faces attached to the given vertex.
GetVertexUVs EditableMesh:GetVertexUVs(vertexId: int64): Array Returns the UV IDs of the faces attached to the given vertex.
GetVertices EditableMesh:GetVertices(): Array Returns all vertices as a list of vertex IDs.
GetVerticesWithAttribute EditableMesh:GetVerticesWithAttribute(id: int64): Array Returns a list of vertices that use a given attribute ID.
GetVerticesWithColor EditableMesh:GetVerticesWithColor(colorId: int64): Array Returns an array of vertex IDs that use the given color ID.
GetVerticesWithNormal EditableMesh:GetVerticesWithNormal(normalId: int64): Array Returns an array of vertex IDs that use the given normal ID.
GetVerticesWithUV EditableMesh:GetVerticesWithUV(uvId: int64): Array Returns an array of vertex IDs that use the given UV ID.
IdDebugString EditableMesh:IdDebugString(id: int64): string Returns a string describing an ID, useful for debugging purposes.
MergeVertices EditableMesh:MergeVertices(mergeTolerance: float): Map Merges vertices that touch together.
RaycastLocal EditableMesh:RaycastLocal(origin: Vector3, direction: Vector3): Tuple Casts a ray and returns the intersection point, face ID, and barycentric coordinates.
RemoveBone EditableMesh:RemoveBone(boneId: int64): null Removes a bone using its bone ID.
RemoveFace EditableMesh:RemoveFace(faceId: int64): null Removes a face using its face ID.
RemoveUnused EditableMesh:RemoveUnused(): Array Removes all unused vertices, normals, UVs, and colors, and returns the removed IDs.
ResetNormal EditableMesh:ResetNormal(normalId: int64): null Reset this normal ID to be automatically calculated.
SetBoneCFrame EditableMesh:SetBoneCFrame(boneId: int64, cframe: CFrame): null Set the initial CFrame for a bone in the mesh's bind pose.
SetBoneIsVirtual EditableMesh:SetBoneIsVirtual(boneId: int64, virtual: bool): null Set whether a bone is virtual.
SetBoneName EditableMesh:SetBoneName(boneId: int64, name: string): null Sets the name for a bone.
SetBoneParent EditableMesh:SetBoneParent(boneId: int64, parentBoneId: int64): null Set a parent for a bone.
SetColor EditableMesh:SetColor(colorId: int64, color: Color3): null Sets the color for a color ID.
SetColorAlpha EditableMesh:SetColorAlpha(colorId: int64, alpha: float): null Sets the color alpha (transparency) for a color ID.
SetFaceColors EditableMesh:SetFaceColors(faceId: int64, ids: Array): null Sets the face's vertex colors to new color IDs.
SetFaceNormals EditableMesh:SetFaceNormals(faceId: int64, ids: Array): null Sets the face's vertex normals to new normal IDs.
SetFaceUVs EditableMesh:SetFaceUVs(faceId: int64, ids: Array): null Sets the face's vertex UVs to new UV IDs.
SetFaceVertices EditableMesh:SetFaceVertices(faceId: int64, ids: Array): null Sets the face's vertices to new vertex IDs.
SetFacsBonePose EditableMesh:SetFacsBonePose(action: FacsActionUnit, boneId: int64, cframe: CFrame): null Set CFrame for an individual bone in a specific FACS action unit.
SetFacsCorrectivePose EditableMesh:SetFacsCorrectivePose(actions: Array, boneIds: Array, cframes: Array): null Set pose for all bones in a specific FACS corrective pose.
SetFacsPose EditableMesh:SetFacsPose(action: FacsActionUnit, boneIds: Array, cframes: Array): null Set pose for all bones in a specific FACS action unit.
SetNormal EditableMesh:SetNormal(normalId: int64, normal: Vector3): null Set the normal for a normal ID.
SetPosition EditableMesh:SetPosition(vertexId: int64, p: Vector3): null Sets a vertex position in the mesh's local object space.
SetUV EditableMesh:SetUV(uvId: int64, uv: Vector2): null Sets UV coordinates for a UV ID.
SetVertexBones EditableMesh:SetVertexBones(vertexId: int64, boneIDs: Array): null Assign a list of bones with the vertex for skinning.
SetVertexBoneWeights EditableMesh:SetVertexBoneWeights(vertexId: int64, boneWeights: Array): null Sets skinning blend weights for each bone associated with the vertex.
SetVertexFaceColor EditableMesh:SetVertexFaceColor(vertexId: int64, faceId: int64, colorId: int64): null Sets the color ID of a vertex/face pair.
SetVertexFaceNormal EditableMesh:SetVertexFaceNormal(vertexId: int64, faceId: int64, normalId: int64): null Sets the normal ID of a vertex/face pair.
SetVertexFaceUV EditableMesh:SetVertexFaceUV(vertexId: int64, faceId: int64, uvId: int64): null Sets the UV ID of a vertex/face pair.
Triangulate EditableMesh:Triangulate(): null Splits all faces on the mesh to be triangles.

Roblox reference: EditableMesh

Generated from Nova's API dump and Roblox creator-docs. Nova-only members come from the engine's reflection metadata.