public Change ScheduleSdf <TSdf>(float ox, float oy, float oz, TSdf sdf, int material, bool replace) where TSdf : struct, ISdf { var changed = new NativeArray <bool>(1, Allocator.TempJob); var outVoxels = new NativeArray3D <Voxel, TIndexer>(indexerFactory(voxels.Length(0), voxels.Length(1), voxels.Length(2)), voxels.Length(0), voxels.Length(1), voxels.Length(2), Allocator.Persistent, NativeArrayOptions.UninitializedMemory); var sdfJob = new ChunkSdfJob <TSdf, TIndexer> { origin = new float3(ox, oy, oz), sdf = sdf, material = material, replace = replace, snapshot = voxels, changed = changed, outVoxels = outVoxels }; return(new Change(sdfJob.Schedule(), () => { voxels.Dispose(); voxels = outVoxels; if (sdfJob.changed[0]) { NeedsRebuild = true; } changed.Dispose(); //Update the padding of all -X/-Y/-Z adjacent chunks //TODO Only propagate those sides that have changed PropagatePadding(); } )); }
public VoxelChunk(VoxelWorld <TIndexer> world, ChunkPos pos, int chunkSize, IndexerFactory <TIndexer> indexerFactory) { this.world = world; this.chunkSize = chunkSize; this.chunkSizeSq = chunkSize * chunkSize; this.Pos = pos; this.indexerFactory = indexerFactory; voxels = new NativeArray3D <Voxel, TIndexer>(indexerFactory(chunkSize + 1, chunkSize + 1, chunkSize + 1), chunkSize + 1, chunkSize + 1, chunkSize + 1, Allocator.Persistent); //voxels = new Voxel[chunkSize * chunkSize * chunkSize]; //voxels = new NativeArray<Voxel>(chunkSize * chunkSize * chunkSize, Allocator.Persistent); //TODO Dispose }
public Change ScheduleGrid <TGridIndexer>(int tx, int ty, int tz, int gx, int gy, int gz, NativeArray3D <Voxel, TGridIndexer> grid, bool propagatePadding, bool includePadding, bool writeUnsetVoxels) where TGridIndexer : struct, IIndexer { var gridJob = new ChunkGridJob <TGridIndexer, TIndexer> { source = grid, chunkSize = chunkSize + (includePadding ? 1 : 0), writeUnsetVoxels = writeUnsetVoxels, tx = tx, ty = ty, tz = tz, gx = gx, gy = gy, gz = gz, target = voxels }; return(new Change(gridJob.Schedule(), () => { NeedsRebuild = true; if (propagatePadding) { //Update the padding of all -X/-Y/-Z adjacent chunks //TODO Only propagate those sides that have changed PropagatePadding(); } } )); }
public void ApplyGrid <TGridIndexer>(int x, int y, int z, NativeArray3D <Voxel, TGridIndexer> grid, bool propagatePadding, bool includePadding, VoxelEditConsumer <TIndexer> edits, bool writeToChunks = true, bool writeUnsetVoxels = false) where TGridIndexer : struct, IIndexer { int minX = Mathf.FloorToInt((float)x / ChunkSize); int minY = Mathf.FloorToInt((float)y / ChunkSize); int minZ = Mathf.FloorToInt((float)z / ChunkSize); int maxX = Mathf.FloorToInt((float)(x + grid.Length(0)) / ChunkSize); int maxY = Mathf.FloorToInt((float)(y + grid.Length(1)) / ChunkSize); int maxZ = Mathf.FloorToInt((float)(z + grid.Length(2)) / ChunkSize); var handles = new List <VoxelChunk <TIndexer> .FinalizeChange>(); var watch = System.Diagnostics.Stopwatch.StartNew(); if (edits != null) { var snapshots = new List <VoxelChunk <TIndexer> .Snapshot>(); //Include padding int minX2 = Mathf.FloorToInt((float)(x - 1) / ChunkSize); int minY2 = Mathf.FloorToInt((float)(y - 1) / ChunkSize); int minZ2 = Mathf.FloorToInt((float)(z - 1) / ChunkSize); int maxX2 = Mathf.FloorToInt((float)(x + grid.Length(0) + 1) / ChunkSize); int maxY2 = Mathf.FloorToInt((float)(y + grid.Length(1) + 1) / ChunkSize); int maxZ2 = Mathf.FloorToInt((float)(z + grid.Length(2) + 1) / ChunkSize); var snapshotChunks = new List <VoxelChunk <TIndexer> >(); for (int cx = minX2; cx <= maxX2; cx++) { for (int cy = minY2; cy <= maxY2; cy++) { for (int cz = minZ2; cz <= maxZ2; cz++) { ChunkPos chunkPos = ChunkPos.FromChunk(cx, cy, cz); chunks.TryGetValue(chunkPos, out VoxelChunk <TIndexer> chunk); if (chunk != null) { snapshots.Add(chunk.ScheduleSnapshot()); } else { snapshotChunks.Add(new VoxelChunk <TIndexer>(this, chunkPos, ChunkSize, IndexerFactory)); } } } } //Finalize clone jobs foreach (VoxelChunk <TIndexer> .Snapshot snapshot in snapshots) { snapshot.handle.Complete(); snapshotChunks.Add(snapshot.chunk); } //Produce edit edits(new VoxelEdit <TIndexer>(this, snapshotChunks)); } if (writeToChunks) { var changes = new List <VoxelChunk <TIndexer> .Change>(); //Schedule all jobs for (int cx = minX; cx <= maxX; cx++) { for (int cy = minY; cy <= maxY; cy++) { for (int cz = minZ; cz <= maxZ; cz++) { ChunkPos chunkPos = ChunkPos.FromChunk(cx, cy, cz); chunks.TryGetValue(chunkPos, out VoxelChunk <TIndexer> chunk); if (chunk == null) { chunks[chunkPos] = chunk = new VoxelChunk <TIndexer>(this, chunkPos, ChunkSize, IndexerFactory); } var gx = (cx - minX) * ChunkSize; var gy = (cy - minY) * ChunkSize; var gz = (cz - minZ) * ChunkSize; changes.Add(chunk.ScheduleGrid(0, 0, 0, gx, gy, gz, grid, propagatePadding, includePadding, writeUnsetVoxels)); } } } //Wait and finalize jobs foreach (var change in changes) { change.handle.Complete(); } //Finalize foreach (var change in changes) { change.finalize(); } } }
public static bool FillCell(NativeArray3D <Voxel, TIndexer> voxels, int x, int y, int z, int cellIndex, NativeArray <int> materials, NativeArray <float> intersections, NativeArray <float3> normals) { var v000 = voxels[x, y, z]; var v100 = voxels[x + 1, y, z]; var v101 = voxels[x + 1, y, z + 1]; var v001 = voxels[x, y, z + 1]; var v010 = voxels[x, y + 1, z]; var v110 = voxels[x + 1, y + 1, z]; var v111 = voxels[x + 1, y + 1, z + 1]; var v011 = voxels[x, y + 1, z + 1]; var d000 = (float4x3)v000.Data; var d100 = (float4x3)v100.Data; var d101 = (float4x3)v101.Data; var d001 = (float4x3)v001.Data; var d010 = (float4x3)v010.Data; var d110 = (float4x3)v110.Data; var d111 = (float4x3)v111.Data; var d011 = (float4x3)v011.Data; int solids = 0; if ((materials[cellIndex * 8 + 0] = v000.Material) != 0) { solids++; } if ((materials[cellIndex * 8 + 1] = v100.Material) != 0) { solids++; } if ((materials[cellIndex * 8 + 2] = v101.Material) != 0) { solids++; } if ((materials[cellIndex * 8 + 3] = v001.Material) != 0) { solids++; } if ((materials[cellIndex * 8 + 4] = v010.Material) != 0) { solids++; } if ((materials[cellIndex * 8 + 5] = v110.Material) != 0) { solids++; } if ((materials[cellIndex * 8 + 6] = v111.Material) != 0) { solids++; } if ((materials[cellIndex * 8 + 7] = v011.Material) != 0) { solids++; } if (solids == 0 || solids == 8) { //No surface in cell return(false); } intersections[cellIndex * 12 + 0] = d000[0].w; intersections[cellIndex * 12 + 1] = d100[2].w; intersections[cellIndex * 12 + 2] = d001[0].w; intersections[cellIndex * 12 + 3] = d000[2].w; intersections[cellIndex * 12 + 4] = d010[0].w; intersections[cellIndex * 12 + 5] = d110[2].w; intersections[cellIndex * 12 + 6] = d011[0].w; intersections[cellIndex * 12 + 7] = d010[2].w; intersections[cellIndex * 12 + 8] = d000[1].w; intersections[cellIndex * 12 + 9] = d100[1].w; intersections[cellIndex * 12 + 10] = d101[1].w; intersections[cellIndex * 12 + 11] = d001[1].w; normals[cellIndex * 12 + 0] = d000[0].xyz; normals[cellIndex * 12 + 1] = d100[2].xyz; normals[cellIndex * 12 + 2] = d001[0].xyz; normals[cellIndex * 12 + 3] = d000[2].xyz; normals[cellIndex * 12 + 4] = d010[0].xyz; normals[cellIndex * 12 + 5] = d110[2].xyz; normals[cellIndex * 12 + 6] = d011[0].xyz; normals[cellIndex * 12 + 7] = d010[2].xyz; normals[cellIndex * 12 + 8] = d000[1].xyz; normals[cellIndex * 12 + 9] = d100[1].xyz; normals[cellIndex * 12 + 10] = d101[1].xyz; normals[cellIndex * 12 + 11] = d001[1].xyz; return(true); }
public void CopyFrom(NativeArray3D <T, TIndexer> array) { data.CopyFrom(array.data); }
private static void ApplySdfIntersection(NativeArray3D <Voxel, TIndexer> snapshot, float3 origin, int x, int y, int z, int chunkSize, int edge, int xo, int yo, int zo, TSdf sdf, NativeArray3D <float, LinearIndexer> evaluatedSdf, int material, bool replace, NativeArray3D <Voxel, TIndexer> outVoxels) { if (x < 0 || y < 0 || z < 0 || x >= chunkSize || y >= chunkSize || z >= chunkSize) { return; } //Remove intersections and normals from edges that do not have //a material change /*if (voxels[x, y, z].material == voxels[x + xo, y + yo, z + zo].material) * { * intersections[x][y][z][edge] = 0; * normals[x][y][z][edge] = Vector3.zero; * return; * }*/ bool isIgnoredReplacement = replace && (outVoxels[x, y, z].Material == 0 || outVoxels[x + xo, y + yo, z + zo].Material == 0); float d1 = evaluatedSdf[x, y, z]; float d2 = evaluatedSdf[x + xo, y + yo, z + zo]; float normalFacing = material == 0 ? -1.0f : 1.0f; const float epsilon = 0.001F; if (!isIgnoredReplacement) { if ((d1 < 0) != (d2 < 0)) { float3 edgeStart = new float3(x, y, z) - origin; float3 newIntersectionPoint = FindIntersection(edgeStart, d1, new float3(x + xo, y + yo, z + zo) - origin, d2, sdf, 0.001f, 4); float newIntersection = math.length(newIntersectionPoint - edgeStart); if (snapshot[x, y, z].Material == snapshot[x + xo, y + yo, z + zo].Material) { //Currently no existing intersection, can just set outVoxels[x, y, z] = outVoxels[x, y, z].ModifyEdge(true, edge, newIntersection, normalFacing * SdfDerivative.FirstOrderCentralFiniteDifferenceNormalized(newIntersectionPoint, epsilon, sdf)); } else { //An intersection already exists, need to check where the already existing intersection is and compare to the new one float intersection = outVoxels[x, y, z].Data[edge].w; bool overwrite = false; bool s1 = snapshot[x, y, z].Material != 0; bool s2 = snapshot[x + xo, y + yo, z + zo].Material != 0; if (material != 0) { if (d1 < 0 && s1 && newIntersection > intersection) { overwrite = true; } else if (d2 < 0 && s2 && newIntersection < intersection) { overwrite = true; } } else { if (d1 < 0 && s2 && newIntersection > intersection) { overwrite = true; } else if (d2 < 0 && s1 && newIntersection < intersection) { overwrite = true; } } if (overwrite) { outVoxels[x, y, z] = outVoxels[x, y, z].ModifyEdge(true, edge, newIntersection, normalFacing * SdfDerivative.FirstOrderCentralFiniteDifferenceNormalized(newIntersectionPoint, epsilon, sdf)); } } } else if (d1 < 0 && d2 < 0) { outVoxels[x, y, z] = outVoxels[x, y, z].ModifyEdge(true, edge, 0, 0); } } else if ((d1 < 0) != (d2 < 0)) { float3 intersection = FindIntersection(new float3(x, y, z) - origin, d1, new float3(x + xo, y + yo, z + zo) - origin, d2, sdf, 0.001F, 4); float intersectionDist = math.length(intersection - new float3(x, y, z) + origin); //TODO Use inVoxels for comparison? if (outVoxels[x, y, z].Material == outVoxels[x + xo, y + yo, z + zo].Material) { outVoxels[x, y, z] = outVoxels[x, y, z].ModifyEdge(true, edge, math.length(intersection - new float3(x, y, z) + origin), normalFacing * SdfDerivative.FirstOrderCentralFiniteDifferenceNormalized(intersection, epsilon, sdf)); } } }
public static bool ApplySdf(NativeArray3D <Voxel, TIndexer> snapshot, float3 origin, TSdf sdf, int material, bool replace, NativeArray3D <Voxel, TIndexer> outVoxels, Allocator allocator) { int chunkSize = snapshot.Length(0) - 1; float3 minBound = math.floor(origin + sdf.Min()); float3 maxBound = math.ceil(origin + sdf.Max()); snapshot.CopyTo(outVoxels); var evaluatedSdf = new NativeArray3D <float, LinearIndexer>(new LinearIndexer(chunkSize + 1, chunkSize + 1, chunkSize + 1), chunkSize + 1, chunkSize + 1, chunkSize + 1, allocator); bool changed = false; //Apply materials for (int z = (int)minBound.z; z <= maxBound.z + 1; z++) { for (int y = (int)minBound.y; y <= maxBound.y + 1; y++) { for (int x = (int)minBound.x; x <= maxBound.x + 1; x++) { if (x >= 0 && x < chunkSize + 1 && y >= 0 && y < chunkSize + 1 && z >= 0 && z < chunkSize + 1) { bool isInside = (evaluatedSdf[x, y, z] = sdf.Eval(new float3(x, y, z) - origin)) < 0; if (isInside) { if (replace && outVoxels[x, y, z].Material != 0) { //TODO Does this need intersection value checks? outVoxels[x, y, z] = outVoxels[x, y, z].ModifyMaterial(true, material); changed = true; } else if (!replace && outVoxels[x, y, z].Material != material) { outVoxels[x, y, z] = outVoxels[x, y, z].ModifyMaterial(true, material); changed = true; } } } } } } //Apply intersections and normals in a second pass, such that they aren't unnecessarily //asigned to edges with no material change for (int z = (int)minBound.z; z <= maxBound.z; z++) { for (int y = (int)minBound.y; y <= maxBound.y; y++) { for (int x = (int)minBound.x; x <= maxBound.x; x++) { if (x >= 0 && x < chunkSize && y >= 0 && y < chunkSize && z >= 0 && z < chunkSize) { ApplySdfIntersection(snapshot, origin, x, y, z, chunkSize, 0, 1, 0, 0, sdf, evaluatedSdf, material, replace, outVoxels); ApplySdfIntersection(snapshot, origin, x, y, z, chunkSize, 1, 0, 1, 0, sdf, evaluatedSdf, material, replace, outVoxels); ApplySdfIntersection(snapshot, origin, x, y, z, chunkSize, 2, 0, 0, 1, sdf, evaluatedSdf, material, replace, outVoxels); } } } } evaluatedSdf.Dispose(); return(changed); }