ArrayMesh
Mesh built from vertex/attribute arrays with separate surfaces.
ArrayMesh stores geometry; MeshInstance3D places it in the world. It is a resource rather than an Entity. Every AddSurfaceFromArrays appends a surface; assign its material separately with SurfaceSetMaterial. Surface indices start at zero and shift after SurfaceRemove. Positions and optional indices are sufficient for a first mesh.
using var mesh = new ArrayMesh();
using var arrays = new Reckfell.Collections.Array();
arrays.Resize((int)Mesh.ArrayType.Max);
arrays[(int)Mesh.ArrayType.Vertex] = new Vector3[] {
new(0, 0, 0), new(1, 0, 0), new(1, 1, 0), new(0, 1, 0)
};
arrays[(int)Mesh.ArrayType.Index] = new int[] { 0, 1, 2, 0, 2, 3 };
mesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);
int vertexCount = mesh.SurfaceGetArrayLen(0); // 4
int indexCount = mesh.SurfaceGetArrayIndexLen(0); // 6The example checks data without creating a visible object. To display it, assign the resource to MeshInstance3D.Mesh and add the instance to the tree; keep the mesh alive while used rather than ending its using scope early. Prepare material, normals and UVs for the intended result. Triangle front faces use clockwise winding.
The arrays container must have Mesh.ArrayType.Max slots; unused slots remain Nil. Vertex accepts Vector3[] or Vector2[]; Index accepts int[] with indices inside the vertex count. Supply Normal/UV/Color for every vertex and four floats per vertex for Tangent. Bones/Weights need matching influence counts and a valid format. Index count is not triangle count: for Triangles it is divisible by three. Validate lengths, values and primitive before adding. SurfaceGetArrays returns a snapshot: editing returned arrays does not edit a surface.
Define blend-shape names before adding surfaces; each surface needs one array per shape in the same order. Vertex/Normal/Tangent channels must match the base format. Default mode is Relative. ClearSurfaces retains blend definitions and mode; ClearBlendShapes can then remove names. Supply LODs as a Dictionary of positive numeric thresholds and shorter int[] index lists addressing the same vertices. These are prepared levels, not automatic simplification. There is no public C# SurfaceGetLods: do not copy that C++ name into a managed example.
SurfaceUpdateVertexRegion/AttributeRegion/SkinRegion operate on packed buffer bytes rather than Vector3 indices. Format, offsets and stride depend on channels/compression; use the corresponding RenderingServer MeshSurfaceGetFormat methods. Such writes cannot change lengths or topology. Expand CustomAabb for deformation culling; setting CustomAabb does not change geometric GetAabb. A Changed notification does not establish CPU-cache, bounds or collision recomputation. The headless backend does not perform these region writes at all.
RegenNormalMaps rebuilds surfaces through SurfaceTool and generates tangents when normals and UVs exist; it neither creates normal-map images nor generates normals. It is not a lossless preservation path for names, blend shapes, LODs or custom channels. LightmapUnwrap creates UV2 and can change vertex splitting; it does not bake lighting. It requires an unwrap callback in the build, Triangles with normals, positive texelSize and no blend shapes.
ClearSurfaces does not emit Changed; SurfaceRemove, surface append, name changes and CustomAabb changes do. Call EmitChanged explicitly after clearing if your consumer relies on that signal. ShadowMesh is shared additional geometry for shadows/depth prepass with identical positions, including LODs, rather than a replacement visible mesh. Initialize the main mesh by adding a surface or calling GetRid before assigning ShadowMesh.
20 Windows headless checks covered metadata, CPU snapshots, names, clearing, bounds, shared references and a simple blend/LOD package. GPU updates, visible deformation, LOD selection, light baking/UV2 quality and performance were not tested.
Declarations checked against C# SDK 1.0.0. Expand a name to see declarations and documentation.
Constructors1
ArrayMeshConstructor
public ArrayMesh();Creates an owned empty Mesh without surfaces or blend shapes. Mode is Relative; release after all consumers finish using it.
Properties5
BlendShapeModeProperty
public Mesh.BlendShapeMode BlendShapeMode { get; set; }Blend-data interpretation mode: Relative by default, or Normalized blending. Does not automatically convert existing arrays.
CustomAabbProperty
public Aabb CustomAabb { get; set; }Custom culling bounds, useful for shader deformation. Zero Aabb disables the override. Does not alter vertices or GetAabb; setter emits Changed.
ShadowMeshProperty
public ArrayMesh ShadowMesh { get; set; }Shared auxiliary mesh for shadows/depth prepass. Positions must match the main mesh including LODs; seam vertices can be fused and unnecessary attributes omitted. Does not replace visible material/geometry.
_BlendShapeNamesProperty
public string[] _BlendShapeNames { get; set; }Hidden SDK serialization property for shape names. Use AddBlendShape/SetBlendShapeName/ClearBlendShapes in ordinary code; not an animation-weight array.
_SurfacesProperty
public Reckfell.Collections.Array _Surfaces { get; set; }Hidden SDK serialization property for packed surfaces. An Array of dictionaries containing format, vertex_data, counts and optional blocks, not AddSurfaceFromArrays channel arrays. Do not hand-edit binary dictionaries. The getter returns a caller-owned container.
Methods29
AddBlendShapevoid
public void AddBlendShape(StringName name);Adds a name before surface creation. Duplicates receive suffixes " 2", " 3", etc. Changing the count after surfaces exist is rejected.
| Name | Type | Default |
|---|---|---|
name | StringName | — |
nameShape name; conflicts automatically receive an available numeric suffix.
AddSurfaceFromArraysvoid
public void AddSurfaceFromArrays(Mesh.PrimitiveType primitive, Reckfell.Collections.Array arrays, Reckfell.Collections.Array<Reckfell.Collections.Array> blendShapes = null, Reckfell.Collections.Dictionary lods = null, Mesh.ArrayFormat flags = (Mesh.ArrayFormat)0);Appends rather than replaces a surface. Requires Max slots, consistent channels and one array per declared blend shape. Data/flags determine format; emits Changed on success. Returns void: inspect surface count and diagnostics.
| Name | Type | Default |
|---|---|---|
primitive | Mesh.PrimitiveType | — |
arrays | Reckfell.Collections.Array | — |
blendShapes | Reckfell.Collections.Array<Reckfell.Collections.Array> | null |
lods | Reckfell.Collections.Dictionary | null |
flags | Mesh.ArrayFormat | (Mesh.ArrayFormat)0 |
primitivePrimitive assembly mode; Triangles needs a corner/index count divisible by three.
arraysExactly Mesh.ArrayType.Max slots containing packed arrays of matching types/lengths; unused slots Nil.
blendShapesOne Max-slot array per declared shape in the same order; matching Vertex/Normal/Tangent channels. null only with no declared shapes.
lodsOptional Dictionary: positive threshold → shorter int[] addressing base vertices. Validate indices yourself; not a new vertex grid.
flagsMesh.ArrayFormat flags for packing/update and custom-channel formats. Default 0; base channel bits are derived from arrays.
ClearBlendShapesvoid
public void ClearBlendShapes();Removes blend definitions only when no surfaces exist. Call ClearSurfaces first. Does not itself emit Changed.
ClearSurfacesvoid
public void ClearSurfaces();Removes all surfaces and resets geometric Aabb. Retains blend names/mode, CustomAabb and ShadowMesh. Does not emit Changed and is not a full resource reset.
GetBlendShapeCountint
public int GetBlendShapeCount();Returns the declared shape count rather than surface count or active MeshInstance3D weight count.
intDeclared shape count, including definitions retained after ClearSurfaces.
GetBlendShapeModeMesh.BlendShapeMode
public Mesh.BlendShapeMode GetBlendShapeMode();Reads the current mode, also exposed by BlendShapeMode. Initially Relative.
Mesh.BlendShapeModeCurrent Mesh.BlendShapeMode; Relative for a new mesh.
GetBlendShapeNameStringName
public StringName GetBlendShapeName(int index);Reads a shape name in declaration order. An empty invalid-index fallback does not establish that a shape exists.
| Name | Type | Default |
|---|---|---|
index | int | — |
indexShape index from 0 to GetBlendShapeCount()-1 rather than a surface index.
StringNameCurrent shape StringName; empty fallback for an invalid index.
GetCustomAabbAabb
public Aabb GetCustomAabb();Reads the CustomAabb culling override rather than merged geometric surface bounds.
AabbStored custom override; zero Aabb by default.
GetShadowMeshArrayMesh
public ArrayMesh GetShadowMesh();Returns the shared ShadowMesh reference or null. Does not copy the resource; do not release this alias ahead of its owner.
ArrayMeshShared ArrayMesh reference or null, not a copy or separately owned object to Dispose early.
LightmapUnwrapError
public Error LightmapUnwrap(Transform3D transform, float texelSize);Builds lightmap UV2 without baking lighting. Requires an unwrap callback, Triangles with normals, texelSize > 0 and no blend shapes. Uses transform scale and can rebuild vertices/indices. Work on a copy and inspect the result separately.
| Name | Type | Default |
|---|---|---|
transform | Transform3D | — |
texelSize | float | — |
transformPlacement scale for UV2 density. The implementation extracts basis-column lengths; translation/rotation are not baked into vertices.
texelSizePositive finite texel size in scaled-geometry units; smaller values increase required resolution.
ErrorError.Ok on completed unwrap; Unconfigured without a callback, Unavailable for blend shapes/unsupported geometry, ParameterRangeError for texelSize ≤ 0. Also inspect other errors, new UV2 and diagnostics.
RegenNormalMapsvoid
public void RegenNormalMaps();Rebuilds surfaces via SurfaceTool.GenerateTangents. Requires normals and UVs; does not generate normals or normal-map images. Does not guarantee names, blend shapes, LODs or custom-channel preservation.
SetBlendShapeModevoid
public void SetBlendShapeMode(Mesh.BlendShapeMode mode);Sets the mode also exposed by BlendShapeMode. Does not convert shape data or emit Changed.
| Name | Type | Default |
|---|---|---|
mode | Mesh.BlendShapeMode | — |
modeRelative or Normalized to match shape data. A blending mode rather than a conversion command.
SetBlendShapeNamevoid
public void SetBlendShapeName(int index, StringName name);Renames a shape without changing its index/data. A conflict with another name receives the next available numeric suffix; does not emit Changed.
| Name | Type | Default |
|---|---|---|
index | int | — |
name | StringName | — |
indexShape index from 0 to GetBlendShapeCount()-1 rather than a surface index.
nameShape name; conflicts automatically receive an available numeric suffix.
SetCustomAabbvoid
public void SetCustomAabb(Aabb aabb);Sets CustomAabb for culling and emits Changed. Does not alter geometric GetAabb or collision.
| Name | Type | Default |
|---|---|---|
aabb | Aabb | — |
aabbFinite bounds enclosing deformation; zero Aabb disables the override. Not new geometry.
SetShadowMeshvoid
public void SetShadowMesh(ArrayMesh mesh);Assigns a shared auxiliary mesh or null. Self-reference is rejected. Initialize the main mesh RID first; this method does not itself call GetRid or emit Changed.
| Name | Type | Default |
|---|---|---|
mesh | ArrayMesh | — |
meshShared ArrayMesh with matching positions, or null to disable. Not this; the main RID must already be initialized.
SurfaceFindByNameint
public int SurfaceFindByName(string name);Returns the first exact name match or -1. Names need not be unique; empty names participate in lookup too.
| Name | Type | Default |
|---|---|---|
name | string | — |
nameExact surface name string; uniqueness is not required.
intFirst matching index or -1 if no name matches.
SurfaceGetArrayIndexLenint
public int SurfaceGetArrayIndexLen(int surfIdx);Surface Index element count; 0 for nonindexed geometry. Not triangle count.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
intIndex count; 0 without Index, -1 on invalid surface index.
SurfaceGetArrayLenint
public int SurfaceGetArrayLen(int surfIdx);Surface vertex count rather than index, byte or triangle count.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
intVertex count; -1 on invalid surface index.
SurfaceGetFormatMesh.ArrayFormat
public Mesh.ArrayFormat SurfaceGetFormat(int surfIdx);Bit mask of the stored format: channels, packing and flags. Use it to calculate layout rather than assuming every channel is a plain float array.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
Mesh.ArrayFormatStored Mesh.ArrayFormat mask; 0 on diagnosed invalid surface index.
SurfaceGetNamestring
public string SurfaceGetName(int surfIdx);Returns a surface name, which can be empty or duplicated. Not a persistent unique ID.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
stringPossibly empty name; an empty fallback is also used for an invalid surface index.
SurfaceGetPrimitiveTypeMesh.PrimitiveType
public Mesh.PrimitiveType SurfaceGetPrimitiveType(int surfIdx);Returns the surface primitive assembly mode. The invalid-index Lines fallback does not establish a valid surface.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
Mesh.PrimitiveTypeStored primitive; Lines on diagnosed invalid surface index.
SurfaceRemovevoid
public void SurfaceRemove(int surfIdx);Removes one surface, shifts subsequent indices, clears Mesh cache, recomputes geometric Aabb and emits Changed. Update externally retained surface IDs yourself.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
SurfaceSetNamevoid
public void SurfaceSetName(int surfIdx, string name);Sets a surface name without uniqueness validation; emits Changed even when writing the same name again.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
name | string | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
nameExact surface name string; uniqueness is not required.
SurfaceUpdateAttributeRegion2 overloads
public void SurfaceUpdateAttributeRegion(int surfIdx, int offset, byte[] data);Writes a nonempty byte block into the existing buffer for attributes (color, UV, custom). offset is in bytes. Requires exact layout and bounds; does not change topology/count or recompute Aabb/collision. Array/span overloads share semantics. Emits Changed; dummy backend does not perform the write.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
offset | int | — |
data | byte[] | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
offsetByte offset inside the corresponding packed buffer; offset plus data length must fit its allocated range.
dataNonempty bytes matching the current SurfaceGetFormat layout. Not a serialized Vector3[] or Variant values; the call does not grow the buffer.
public void SurfaceUpdateAttributeRegion(int surfIdx, int offset, ReadOnlySpan<byte> data);Writes a nonempty byte block into the existing buffer for attributes (color, UV, custom). offset is in bytes. Requires exact layout and bounds; does not change topology/count or recompute Aabb/collision. Array/span overloads share semantics. Emits Changed; dummy backend does not perform the write.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
offset | int | — |
data | ReadOnlySpan<byte> | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
offsetByte offset inside the corresponding packed buffer; offset plus data length must fit its allocated range.
dataNonempty bytes matching the current SurfaceGetFormat layout. Not a serialized Vector3[] or Variant values; the call does not grow the buffer.
SurfaceUpdateSkinRegion2 overloads
public void SurfaceUpdateSkinRegion(int surfIdx, int offset, byte[] data);Writes a nonempty byte block into the existing buffer for bones and weights. offset is in bytes. Requires exact layout and bounds; does not change topology/count or recompute Aabb/collision. Array/span overloads share semantics. Emits Changed; dummy backend does not perform the write.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
offset | int | — |
data | byte[] | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
offsetByte offset inside the corresponding packed buffer; offset plus data length must fit its allocated range.
dataNonempty bytes matching the current SurfaceGetFormat layout. Not a serialized Vector3[] or Variant values; the call does not grow the buffer.
public void SurfaceUpdateSkinRegion(int surfIdx, int offset, ReadOnlySpan<byte> data);Writes a nonempty byte block into the existing buffer for bones and weights. offset is in bytes. Requires exact layout and bounds; does not change topology/count or recompute Aabb/collision. Array/span overloads share semantics. Emits Changed; dummy backend does not perform the write.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
offset | int | — |
data | ReadOnlySpan<byte> | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
offsetByte offset inside the corresponding packed buffer; offset plus data length must fit its allocated range.
dataNonempty bytes matching the current SurfaceGetFormat layout. Not a serialized Vector3[] or Variant values; the call does not grow the buffer.
SurfaceUpdateVertexRegion2 overloads
public void SurfaceUpdateVertexRegion(int surfIdx, int offset, byte[] data);Writes a nonempty byte block into the existing buffer for vertices and packed normals/tangents. offset is in bytes. Requires exact layout and bounds; does not change topology/count or recompute Aabb/collision. Array/span overloads share semantics. Emits Changed; dummy backend does not perform the write.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
offset | int | — |
data | byte[] | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
offsetByte offset inside the corresponding packed buffer; offset plus data length must fit its allocated range.
dataNonempty bytes matching the current SurfaceGetFormat layout. Not a serialized Vector3[] or Variant values; the call does not grow the buffer.
public void SurfaceUpdateVertexRegion(int surfIdx, int offset, ReadOnlySpan<byte> data);Writes a nonempty byte block into the existing buffer for vertices and packed normals/tangents. offset is in bytes. Requires exact layout and bounds; does not change topology/count or recompute Aabb/collision. Array/span overloads share semantics. Emits Changed; dummy backend does not perform the write.
| Name | Type | Default |
|---|---|---|
surfIdx | int | — |
offset | int | — |
data | ReadOnlySpan<byte> | — |
surfIdxCurrent surface index from 0 to GetSurfaceCount()-1; removal can shift indices.
offsetByte offset inside the corresponding packed buffer; offset plus data length must fit its allocated range.
dataNonempty bytes matching the current SurfaceGetFormat layout. Not a serialized Vector3[] or Variant values; the call does not grow the buffer.