(RMB/Save link as
then change file extension from .png
to .zip
)
Last active
September 28, 2023 18:51
-
-
Save andrew-raphael-lukasik/cbf9d0097c3b4da67b5e0ecb3715e219 to your computer and use it in GitHub Desktop.
multi-threaded cube marching where geometry is sourced from a mesh asset template (artist-friendly)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment