- Home /
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?
I don't know how to get the Job System implemented in my code
Here is a starting point:
public struct ThyJob : IJobParallelFor
{
[ReadOnly] NativeArray<?> Voxels;
[WriteOnly] NativeArray<?> Cube;
void IJobParallelFor.Execute ( int i )
{
foreach neighbour
Cube[i] = Voxels[ neighbour ];// idk what this is, but fine
}
}
No idea what that Cube
array is. The basic setup for marching cubes algo is:
Iterate neighbors to create a bitmask representations.
Create a mesh, cell by cell, using bitmasks as lookup hashes/keys to find matching geometry
That's it
// get the values in the 8 neighbours which make up a cube
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?
Answer by andrew-lukasik · Feb 23 at 09:45 PM
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: marching-squares-prototype.fbx ( 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;
NativeList<VoxelsToBitmasksJob.Entry> _relevantVoxelData;
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;
var voxelsToBitmasksJob = new VoxelsToBitmasksJob{
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 };
Results.AddNoResize( cellCenter );
}
}
}
#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;
[ReadOnly] public NativeArray<byte> Voxels;
[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 );
int bitmask = 0;
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 )
bitmask |= 1<<direction;
}
if( bitmask!=0 )// ignore cells neighbouring empty space only
{
Results.AddNoResize( new Entry{
Bitmask = bitmask ,
Coords = cellCoords
} );
}
}
}
public struct Entry
{
public int Bitmask;
public int3 Coords;
}
}
[BurstCompile] public struct IndicesJob : IJob
{
[WriteOnly] public NativeList<int> Indices;
[ReadOnly] public NativeList<VoxelsToBitmasksJob.Entry> Entries;
[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() )
{
int bitmask = next.Bitmask;
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 NativeList<VoxelsToBitmasksJob.Entry> Entries;
[ReadOnly] public NativeArray<Vector3> TemplateVertices0, TemplateVertices1, TemplateVertices2, TemplateVertices3, TemplateVertices4, TemplateVertices5;
void IJob.Execute ()
{
foreach( var next in Entries.AsArray() )
{
int3 cellCoords = next.Coords;
int bitmask = next.Bitmask;
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 NativeList<VoxelsToBitmasksJob.Entry> Entries;
[ReadOnly] public NativeArray<Vector3> TemplateNormals0, TemplateNormals1, TemplateNormals2, TemplateNormals3, TemplateNormals4, TemplateNormals5;
void IJob.Execute ()
{
foreach( var next in Entries.AsArray() )
{
int3 cellCoords = next.Coords;
int bitmask = next.Bitmask;
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 NativeList<VoxelsToBitmasksJob.Entry> Entries;
[ReadOnly] public NativeArray<Vector2> TemplateUVs0, TemplateUVs1, TemplateUVs2, TemplateUVs3, TemplateUVs4, TemplateUVs5;
void IJob.Execute ()
{
foreach( var next in Entries.AsArray() )
{
int bitmask = next.Bitmask;
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;
}
}
}
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
Do i just flatten/unflatten it?
Yes
i want to maker a "real" marching cubes algorithm
This one is quite real to me. There are many different variants (distance fields instead of voxels or different mesh sets) but the core idea remains the same. I give you the basic implementation for you to take it in whatever direction you need.
Your answer
![](https://koobas.hobune.stream/wayback/20220613053146im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Get GameObject dependencies only once for JobComponentSystem 1 Answer
Can the burst compiler be used for any code? 0 Answers
Does DOTS have a animation component? 1 Answer
Someone Please provide reference materials for starting with Unity Dots 1 Answer
ECS System OnUpdate not being called on Unity 2020.1b7 + UniRx 1 Answer