# How to implement Marching Cubes as IJobParallelFor

I don’t know how to get the Job System implemented in my code. Would be great if someone could give me a rough idea on how to do it.

Here’s my code rn:

``````for( int x=0 ; x<width-1 ; x++ )
for( int y=0 ; y<height-1 ; y++ )
for( int z=0 ; z<depth-1 ; z++ )
{
// get the values in the 8 neighbours which make up a cube
for( int i=0 ; i<8 ; i++ )
{
int ix = x + VertexOffset[i,0];
int iy = y + VertexOffset[i,1];
int iz = z + VertexOffset[i,2];
Cube[i] = voxels[ ix + iy*width + iz*width * height ];
}

// perform algorithm
March( x , y , z , Cube , verts , indices );
}
``````

And the Vertex Offset array:

``````protected static readonly int[,] VertexOffset = new int[,]
{
{0,0,0} , {1,0,0} , {1,1,0} , {0,1,0} ,
{0,0,1} , {1,0,1} , {1,1,1} , {0,1,1}
};
``````

Can i get this to work using `IJobParallelFor` and when yes, then how would i get the data needed for the `March` function?

Here is an example of how to read the voxel field in a `IJobParallelFor` and construct basic mesh data with 4 different `IJob` executed in parallel (thanks to scheduling setup and RO dependencies).

To make this code work, import this mesh into your project: 193032-marching-squares-prototypefbx.zip (6.4 KB) ( `RMB`/`Save link as...`)

And use it to fill the `Template` field as follows:

profiler timeline for 32/32/32 grid; 32k voxels @ i3-4170 cpu:

``````// LetsCubeMarch.cs

// web* src = https://gist.github.com/andrew-raphael-lukasik/cbf9d0097c3b4da67b5e0ecb3715e219
using UnityEngine;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine.Rendering;

using BurstCompile = Unity.Burst.BurstCompileAttribute;

[RequireComponent( typeof(MeshFilter) , typeof(MeshRenderer) )]
public class LetsCubeMarch : MonoBehaviour
{
[SerializeField] int3 _numCells = new int3( 32 , 32 , 32 );
[SerializeField] float3 _noiseRepetition = new float3( 10 , 10 , 10 );
[SerializeField] float3 _noiseOffset = new float3( 0.5f , 1.4f , -0.35f );
[SerializeField][Range(0,1)] float _fill = 0.45f;
[SerializeField] Mesh[] _template = new Mesh[6];
public JobHandle Dependency = default(JobHandle);
NativeArray<byte> _voxels;
NativeArray<int> _templateIndices0, _templateIndices1, _templateIndices2, _templateIndices3, _templateIndices4, _templateIndices5;
NativeArray<Vector3> _templateVertices0, _templateVertices1, _templateVertices2, _templateVertices3, _templateVertices4, _templateVertices5;
NativeArray<Vector3> _templateNormals0, _templateNormals1, _templateNormals2, _templateNormals3, _templateNormals4, _templateNormals5;
NativeArray<Vector2> _templateUVs0, _templateUVs1, _templateUVs2, _templateUVs3, _templateUVs4, _templateUVs5;
NativeList<int> _indices;
NativeList<Vector3> _vertices, _normals;
NativeList<Vector2> _uv;
Mesh _mesh = null;
bool _voxelsChanged, _jobScheduled;

void Awake ()
{
_mesh = new Mesh();
for( int i=0 ; i<_template.Length ; i++ )
Debug.Log(\$"template {i} // topology:{_template[i].GetTopology(0)}, tri:{_template[i].triangles.Length}, vert:{_template[i].vertices.Length}");
_mesh.MarkDynamic();
GetComponent<MeshFilter>().sharedMesh = _mesh;

var generateVoxelsJob = new GenerateVoxelsJob( numCells:_numCells , threshold:1.0f-_fill*2f , noiseRepetition:_noiseRepetition , noiseOffset:_noiseOffset , Allocator.TempJob );
generateVoxelsJob.Schedule( _numCells.x*_numCells.y*_numCells.z , 128 ).Complete();
_voxels = generateVoxelsJob.Results;
_voxelsChanged = true;

_indices = new NativeList<int>( Allocator.Persistent );
_vertices = new NativeList<Vector3>( Allocator.Persistent );
_normals = new NativeList<Vector3>( Allocator.Persistent );
_uv = new NativeList<Vector2>( Allocator.Persistent );
_relevantVoxelData = new NativeList<VoxelsToBitmasksJob.Entry>( Allocator.Persistent );

_templateVertices0 = new NativeArray<Vector3>( _template[0].vertices , Allocator.Persistent );// x-
_templateVertices1 = new NativeArray<Vector3>( _template[1].vertices , Allocator.Persistent );// x+
_templateVertices2 = new NativeArray<Vector3>( _template[2].vertices , Allocator.Persistent );// y-
_templateVertices3 = new NativeArray<Vector3>( _template[3].vertices , Allocator.Persistent );// y+
_templateVertices4 = new NativeArray<Vector3>( _template[4].vertices , Allocator.Persistent );// z-
_templateVertices5 = new NativeArray<Vector3>( _template[5].vertices , Allocator.Persistent );// z+

_templateNormals0 = new NativeArray<Vector3>( _template[0].normals , Allocator.Persistent );// x-
_templateNormals1 = new NativeArray<Vector3>( _template[1].normals , Allocator.Persistent );// x+
_templateNormals2 = new NativeArray<Vector3>( _template[2].normals , Allocator.Persistent );// y-
_templateNormals3 = new NativeArray<Vector3>( _template[3].normals , Allocator.Persistent );// y+
_templateNormals4 = new NativeArray<Vector3>( _template[4].normals , Allocator.Persistent );// z-
_templateNormals5 = new NativeArray<Vector3>( _template[5].normals , Allocator.Persistent );// z+

_templateUVs0 = new NativeArray<Vector2>( _template[0].uv , Allocator.Persistent );// x-
_templateUVs1 = new NativeArray<Vector2>( _template[1].uv , Allocator.Persistent );// x+
_templateUVs2 = new NativeArray<Vector2>( _template[2].uv , Allocator.Persistent );// y-
_templateUVs3 = new NativeArray<Vector2>( _template[3].uv , Allocator.Persistent );// y+
_templateUVs4 = new NativeArray<Vector2>( _template[4].uv , Allocator.Persistent );// z-
_templateUVs5 = new NativeArray<Vector2>( _template[5].uv , Allocator.Persistent );// z+

_templateIndices0 = new NativeArray<int>( _template[0].triangles , Allocator.Persistent );// x-
_templateIndices1 = new NativeArray<int>( _template[1].triangles , Allocator.Persistent );// x+
_templateIndices2 = new NativeArray<int>( _template[2].triangles , Allocator.Persistent );// y-
_templateIndices3 = new NativeArray<int>( _template[3].triangles , Allocator.Persistent );// y+
_templateIndices4 = new NativeArray<int>( _template[4].triangles , Allocator.Persistent );// z-
_templateIndices5 = new NativeArray<int>( _template[5].triangles , Allocator.Persistent );// z+
}

void OnDestroy ()
{
Dependency.Complete();

if( _voxels.IsCreated ) _voxels.Dispose();
if( _indices.IsCreated ) _indices.Dispose();
if( _vertices.IsCreated ) _vertices.Dispose();
if( _normals.IsCreated ) _normals.Dispose();
if( _uv.IsCreated ) _uv.Dispose();
if( _relevantVoxelData.IsCreated ) _relevantVoxelData.Dispose();

if( _templateVertices0.IsCreated ) _templateVertices0.Dispose();
if( _templateVertices1.IsCreated ) _templateVertices1.Dispose();
if( _templateVertices2.IsCreated ) _templateVertices2.Dispose();
if( _templateVertices3.IsCreated ) _templateVertices3.Dispose();
if( _templateVertices4.IsCreated ) _templateVertices4.Dispose();
if( _templateVertices5.IsCreated ) _templateVertices5.Dispose();

if( _templateNormals0.IsCreated ) _templateNormals0.Dispose();
if( _templateNormals1.IsCreated ) _templateNormals1.Dispose();
if( _templateNormals2.IsCreated ) _templateNormals2.Dispose();
if( _templateNormals3.IsCreated ) _templateNormals3.Dispose();
if( _templateNormals4.IsCreated ) _templateNormals4.Dispose();
if( _templateNormals5.IsCreated ) _templateNormals5.Dispose();

if( _templateUVs0.IsCreated ) _templateUVs0.Dispose();
if( _templateUVs1.IsCreated ) _templateUVs1.Dispose();
if( _templateUVs2.IsCreated ) _templateUVs2.Dispose();
if( _templateUVs3.IsCreated ) _templateUVs3.Dispose();
if( _templateUVs4.IsCreated ) _templateUVs4.Dispose();
if( _templateUVs5.IsCreated ) _templateUVs5.Dispose();

if( _templateIndices0.IsCreated ) _templateIndices0.Dispose();
if( _templateIndices1.IsCreated ) _templateIndices1.Dispose();
if( _templateIndices2.IsCreated ) _templateIndices2.Dispose();
if( _templateIndices3.IsCreated ) _templateIndices3.Dispose();
if( _templateIndices4.IsCreated ) _templateIndices4.Dispose();
if( _templateIndices5.IsCreated ) _templateIndices5.Dispose();

if( _templateUVs0.IsCreated ) _templateUVs0.Dispose();
if( _templateUVs1.IsCreated ) _templateUVs1.Dispose();
if( _templateUVs2.IsCreated ) _templateUVs2.Dispose();
if( _templateUVs3.IsCreated ) _templateUVs3.Dispose();
if( _templateUVs4.IsCreated ) _templateUVs4.Dispose();
if( _templateUVs5.IsCreated ) _templateUVs5.Dispose();

Destroy( _mesh );
}

void Update ()
{
Dependency.Complete();
if( _jobScheduled )
{
Debug.Log(\$"new mesh data // indices:{_indices.Length}, vertices:{_vertices.Length}, normals:{_normals.Length}, uv:{_uv.Length} ");

_mesh.Clear();
_mesh.SetVertices( _vertices.AsArray() );
_mesh.SetNormals( _normals.AsArray() );
_mesh.SetUVs( 0 , _uv.AsArray() );
_mesh.indexFormat = _indices.Length>ushort.MaxValue ? IndexFormat.UInt32 : IndexFormat.UInt16;
_mesh.SetIndices( _indices.AsArray() , MeshTopology.Triangles , 0 );

_jobScheduled = false;
}

if( _voxelsChanged )
{
_vertices.Clear();
_indices.Clear();
_normals.Clear();
_uv.Clear();
_relevantVoxelData.Clear();
int maxCellCount = _numCells.x * _numCells.y * _numCells.z;
int maxVerticesInSingleTemplateMesh = 4;// estimate the max number of vertices (you may want to change this when mesh changes)
int maxVertices = maxCellCount * 6*maxVerticesInSingleTemplateMesh;
_relevantVoxelData.Capacity = maxCellCount;

NumCells			= _numCells ,
Voxels				= _voxels ,
Results				= _relevantVoxelData.AsParallelWriter() ,
};
var vertJob = new VertJob{
Entries				= _relevantVoxelData ,
Vertices			= _vertices ,
TemplateVertices0	= _templateVertices0 ,
TemplateVertices1	= _templateVertices1 ,
TemplateVertices2	= _templateVertices2 ,
TemplateVertices3	= _templateVertices3 ,
TemplateVertices4	= _templateVertices4 ,
TemplateVertices5	= _templateVertices5 ,
};
var normJob = new NormalsJob{
Entries				= _relevantVoxelData ,
Normals				= _normals ,
TemplateNormals0	= _templateNormals0 ,
TemplateNormals1	= _templateNormals1 ,
TemplateNormals2	= _templateNormals2 ,
TemplateNormals3	= _templateNormals3 ,
TemplateNormals4	= _templateNormals4 ,
TemplateNormals5	= _templateNormals5 ,
};
var uvJob = new UVJob{
Entries				= _relevantVoxelData ,
UV					= _uv ,
TemplateUVs0		= _templateUVs0 ,
TemplateUVs1		= _templateUVs1 ,
TemplateUVs2		= _templateUVs2 ,
TemplateUVs3		= _templateUVs3 ,
TemplateUVs4		= _templateUVs4 ,
TemplateUVs5		= _templateUVs5 ,
};
var indicesJob = new IndicesJob{
Entries				= _relevantVoxelData ,
Indices				= _indices ,
TemplateIndices0	= _templateIndices0 ,
TemplateIndices1	= _templateIndices1 ,
TemplateIndices2	= _templateIndices2 ,
TemplateIndices3	= _templateIndices3 ,
TemplateIndices4	= _templateIndices4 ,
TemplateIndices5	= _templateIndices5 ,
TemplateVertices0Length	= _templateVertices0.Length ,
TemplateVertices1Length	= _templateVertices1.Length ,
TemplateVertices2Length	= _templateVertices2.Length ,
TemplateVertices3Length	= _templateVertices3.Length ,
TemplateVertices4Length	= _templateVertices4.Length ,
TemplateVertices5Length	= _templateVertices5.Length ,
};

Dependency = voxelsToBitmasksJob.Schedule( _voxels.Length , _numCells.x*_numCells.y , Dependency );
var parallelJobs = new NativeArray<JobHandle>( 4 , Allocator.Temp );
parallelJobs[0] = vertJob.Schedule( Dependency );
parallelJobs[1] = normJob.Schedule( Dependency );
parallelJobs[2] = uvJob.Schedule( Dependency );
parallelJobs[3] = indicesJob.Schedule( Dependency );
Dependency = JobHandle.CombineDependencies( parallelJobs );

_voxelsChanged = false;
_jobScheduled = true;
}
}

#if UNITY_EDITOR
void OnValidate ()
{
if( Application.isPlaying && _voxels.IsCreated )
{
if( _jobScheduled )
{
Dependency.Complete();
_jobScheduled = false;
}
_voxels.Dispose();
var generateVoxelsJob = new GenerateVoxelsJob( numCells:_numCells , threshold:1.0f-_fill*2f , noiseRepetition:_noiseRepetition , noiseOffset:_noiseOffset , Allocator.TempJob );
generateVoxelsJob.Schedule( _numCells.x*_numCells.y*_numCells.z , 128 ).Complete();
_voxels = generateVoxelsJob.Results;
_voxelsChanged = true;
}
}
void OnDrawGizmos ()
{
float3 cellSize = Vector3.one;
Gizmos.matrix = transform.localToWorldMatrix;
if( !Application.isPlaying )
{
int len = _numCells.x*_numCells.y*_numCells.z;
var positionsNative = new NativeList<float3>( len , Allocator.TempJob );
var job = new OnDrawGizmosJob{
NumCells			= _numCells ,
Threshold			= 1.0f-_fill*2f ,
NoiseRepetition		= _noiseRepetition ,
NoiseOffset			= _noiseOffset ,
Results				= positionsNative.AsParallelWriter() ,
};
job.Schedule( len , _numCells.x*_numCells.y ).Complete();
float3[] positions = positionsNative.ToArray();
positionsNative.Dispose();

Gizmos.color = Color.black;
foreach( float3 point in positions )
Gizmos.DrawCube( point , cellSize );
}
Gizmos.color = Color.yellow;
Gizmos.DrawWireCube( (float3)_numCells * 0.5f , (float3)_numCells * cellSize );
}

[BurstCompile] struct OnDrawGizmosJob : IJobParallelFor
{
public int3 NumCells;
public float Threshold;
public float3 NoiseRepetition;
public float3 NoiseOffset;
[WriteOnly] public NativeList<float3>.ParallelWriter Results;

void IJobParallelFor.Execute ( int i )
{
int3 coords = default(Utilities).IndexToCoords( i:i , numCells:NumCells );
byte voxel = default(Utilities).CoordsToVoxel( coords:coords , numCells:NumCells , threshold:Threshold , noiseRepetition:NoiseRepetition , noiseOffset:NoiseOffset );
if( voxel!=0 )
{
float3 cellCenter = (float3)coords + new float3{ x=0.5f , y=0.5f , z=0.5f };
}
}
}
#endif

[BurstCompile] public struct GenerateVoxelsJob : IJobParallelFor
{
public int3 NumCells;
public float Threshold;
public float3 NoiseRepetition;
public float3 NoiseOffset;
[WriteOnly] public NativeArray<byte> Results;
public GenerateVoxelsJob ( int3 numCells , float threshold , float3 noiseRepetition , float3 noiseOffset , Allocator allocator )
{
this.NumCells = numCells;
this.Threshold = threshold;
this.NoiseRepetition = noiseRepetition;
this.NoiseOffset = noiseOffset;
this.Results = new NativeArray<byte>( numCells.x*numCells.y*numCells.z , allocator );
}
void IJobParallelFor.Execute ( int index )
{
int3 coords = default(Utilities).IndexToCoords( i:index , numCells:NumCells );
Results[index] = default(Utilities).CoordsToVoxel( coords:coords , numCells:NumCells , threshold:Threshold , noiseRepetition:NoiseRepetition , noiseOffset:NoiseOffset );
}
}

[BurstCompile] public struct VoxelsToBitmasksJob : IJobParallelFor
{
public int3 NumCells;
[WriteOnly] public NativeList<Entry>.ParallelWriter Results;
void IJobParallelFor.Execute ( int index )
{
if( Voxels[index]==0 )// iterate empty cells only
{
int3 cellCoords = default(Utilities).IndexToCoords( i:index , numCells:NumCells );
for( byte direction=0 ; direction<6 ; direction++ )
{
int3 neighbourCoords = cellCoords + default(Utilities).Offset(direction);
if( !math.any( neighbourCoords<0 | neighbourCoords>=NumCells ) )// index bounds test
if( Voxels[default(Utilities).CoordsToIndex(neighbourCoords,NumCells)]!=0 )
}
if( bitmask!=0 )// ignore cells neighbouring empty space only
{
Coords		= cellCoords
} );
}
}
}
public struct Entry
{
public int3 Coords;
}
}

[BurstCompile] public struct IndicesJob : IJob
{
[WriteOnly] public NativeList<int> Indices;
[ReadOnly] public NativeArray<int> TemplateIndices0, TemplateIndices1, TemplateIndices2, TemplateIndices3, TemplateIndices4, TemplateIndices5;
public int TemplateVertices0Length, TemplateVertices1Length, TemplateVertices2Length, TemplateVertices3Length, TemplateVertices4Length, TemplateVertices5Length;
void IJob.Execute ()
{
int baseIndex = 0;
foreach( var next in Entries.AsArray() )
{
for( byte direction=0 ; direction<6 ; direction++ )
if( (bitmask&1<<direction)==1<<direction )// the same as Voxels[neighbourIndex]!=0, but read from bitmask, so it's faster and you can test many directions at once
switch( direction )
{
case 0: foreach( int index in TemplateIndices0 ) Indices.Add( baseIndex + index ); baseIndex += TemplateVertices0Length; break;// x-
case 1: foreach( int index in TemplateIndices1 ) Indices.Add( baseIndex + index ); baseIndex += TemplateVertices1Length; break;// x+
case 2: foreach( int index in TemplateIndices2 ) Indices.Add( baseIndex + index ); baseIndex += TemplateVertices2Length; break;// y-
case 3: foreach( int index in TemplateIndices3 ) Indices.Add( baseIndex + index ); baseIndex += TemplateVertices3Length; break;// y+
case 4: foreach( int index in TemplateIndices4 ) Indices.Add( baseIndex + index ); baseIndex += TemplateVertices4Length; break;// z-
case 5: foreach( int index in TemplateIndices5 ) Indices.Add( baseIndex + index ); baseIndex += TemplateVertices5Length; break;// z+
}
}
}
}

[BurstCompile] public struct VertJob : IJob
{
[WriteOnly] public NativeList<Vector3> Vertices;
[ReadOnly] public NativeArray<Vector3> TemplateVertices0, TemplateVertices1, TemplateVertices2, TemplateVertices3, TemplateVertices4, TemplateVertices5;
void IJob.Execute ()
{
foreach( var next in Entries.AsArray() )
{
int3 cellCoords = next.Coords;
float3 cellCenter = (float3) cellCoords + new float3{ x=0.5f , y=0.5f , z=0.5f };
for( byte direction=0 ; direction<6 ; direction++ )
if( (bitmask&1<<direction)==1<<direction )// the same as Voxels[neighbourIndex]!=0, but read from bitmask, so it's faster and you can test many directions at once
switch( direction )
{
case 0: foreach( Vector3 vert in TemplateVertices0 ) Vertices.Add( cellCenter + (float3)vert ); break;// x-
case 1: foreach( Vector3 vert in TemplateVertices1 ) Vertices.Add( cellCenter + (float3)vert ); break;// x+
case 2: foreach( Vector3 vert in TemplateVertices2 ) Vertices.Add( cellCenter + (float3)vert ); break;// y-
case 3: foreach( Vector3 vert in TemplateVertices3 ) Vertices.Add( cellCenter + (float3)vert ); break;// y+
case 4: foreach( Vector3 vert in TemplateVertices4 ) Vertices.Add( cellCenter + (float3)vert ); break;// z-
case 5: foreach( Vector3 vert in TemplateVertices5 ) Vertices.Add( cellCenter + (float3)vert ); break;// z+
}
}
}
}

[BurstCompile] public struct NormalsJob : IJob
{
[WriteOnly] public NativeList<Vector3> Normals;
[ReadOnly] public NativeArray<Vector3> TemplateNormals0, TemplateNormals1, TemplateNormals2, TemplateNormals3, TemplateNormals4, TemplateNormals5;
void IJob.Execute ()
{
foreach( var next in Entries.AsArray() )
{
int3 cellCoords = next.Coords;
for( byte direction=0 ; direction<6 ; direction++ )
if( (bitmask&1<<direction)==1<<direction )// the same as Voxels[neighbourIndex]!=0, but read from bitmask, so it's faster and you can test many directions at once
switch( direction )
{
case 0: Normals.AddRange( TemplateNormals0 ); break;// x-
case 1: Normals.AddRange( TemplateNormals1 ); break;// x+
case 2: Normals.AddRange( TemplateNormals2 ); break;// y-
case 3: Normals.AddRange( TemplateNormals3 ); break;// y+
case 4: Normals.AddRange( TemplateNormals4 ); break;// z-
case 5: Normals.AddRange( TemplateNormals5 ); break;// z+
}
}
}
}

[BurstCompile] public struct UVJob : IJob
{
[WriteOnly] public NativeList<Vector2> UV;
[ReadOnly] public NativeArray<Vector2> TemplateUVs0, TemplateUVs1, TemplateUVs2, TemplateUVs3, TemplateUVs4, TemplateUVs5;
void IJob.Execute ()
{
foreach( var next in Entries.AsArray() )
{
for( byte direction=0 ; direction<6 ; direction++ )
if( (bitmask&1<<direction)==1<<direction )// the same as Voxels[neighbourIndex]!=0, but read from bitmask, so it's faster and you can test many directions at once
switch( direction )
{
case 0: UV.AddRange( TemplateUVs0 ); break;// x-
case 1: UV.AddRange( TemplateUVs1 ); break;// x+
case 2: UV.AddRange( TemplateUVs2 ); break;// y-
case 3: UV.AddRange( TemplateUVs3 ); break;// y+
case 4: UV.AddRange( TemplateUVs4 ); break;// z-
case 5: UV.AddRange( TemplateUVs5 ); break;// z+
}
}
}
}

public struct Utilities
{
public int3 Offset ( int direction )
{
switch( direction )
{
case 0:		return new int3{ x=-1 };// x-
case 1:		return new int3{ x=+1 };// x+
case 2:		return new int3{ y=-1 };// y-
case 3:		return new int3{ y=+1 };// y+
case 4:		return new int3{ z=-1 };// z-
case 5:		return new int3{ z=+1 };// z+
default:	throw new System.ArgumentOutOfRangeException();
}
}
public int CoordsToIndex ( int x , int y , int z , int3 numCells ) => z*numCells.x*numCells.y + y*numCells.x + x;
public int CoordsToIndex ( int3 coords , int3 numCells ) => this.CoordsToIndex( x:coords.x , y:coords.y , z:coords.z , numCells:numCells );
public int3 IndexToCoords ( int i , int3 numCells )
{
int numSlices = numCells.x * numCells.y;
int z = i / numSlices;
int ilayer = i % numSlices;
int y = ilayer / numCells.x;
int x = ilayer % numCells.x;
return new int3{ x=x , y=y , z=z };
}
public void IndexToCoords ( int i , int3 numCells , out int x , out int y , out int z )
{
int3 coords = this.IndexToCoords( i:i , numCells:numCells );
x = coords.x;
y = coords.y;
z = coords.z;
}
public byte CoordsToVoxel ( int3 coords , int3 numCells , float threshold , float3 noiseRepetition , float3 noiseOffset )
{
float3 noisePos = (float3)coords/(float3)numCells + noiseOffset;
return noise.pnoise(noisePos,noiseRepetition)>threshold ? (byte)1 : (byte)0;
}
}

}
``````

I figure i’ll have to make the March function into an IJobParallelFor but how would i start them parallel and how would i give them the data they need?

Ok first of all, thanks for this amazing help. It’s nice to see such nice people online.
Second of all, i think i’ve nearly understood your code now, but if i want to maker a “real” marching cubes algorithm, then i’d have to get Native Arrays with multiple dimension for for example the Triangle Connection Table. So i searched online a bit but i can’t seem to find a way to do x dimensional Natives Arrays. Any tipps? Do i just flatten/unflatten it? Thanks in advance