private static void BuildVoxelGeometry( RawPrimitive Into, int X, int Y, int Z, VoxelChunk Chunk, //BoxPrimitive BedrockModel, Cache Cache, DesignationSet Designations, WorldManager World) { var v = VoxelHandle.UnsafeCreateLocalHandle(Chunk, new LocalVoxelCoordinate(X, Y, Z)); if (!v.IsValid || !v.IsVisible) { return; // How did this even get called then?? } BuildDesignationGeometry(Into, Chunk, Cache, Designations, World, v); if ((v.IsExplored && v.IsEmpty)) { return; } if (!v.IsExplored && v.Sunlight) { return; } if (Library.GetVoxelPrimitive(v.Type).HasValue(out BoxPrimitive primitive)) { BuildVoxelGeometryFromPrimitive(Into, Chunk, Cache, v, primitive); } }
private static void BuildVoxelGeometry( RawPrimitive Into, int X, int Y, int Z, VoxelChunk Chunk, BoxPrimitive BedrockModel, Cache Cache, DesignationSet Designations, WorldManager World) { var v = VoxelHandle.UnsafeCreateLocalHandle(Chunk, new LocalVoxelCoordinate(X, Y, Z)); if (!v.IsValid || !v.IsVisible) { return; // How did this even get called then?? } BuildDesignationGeometry(Into, Chunk, Cache, Designations, World, v); if ((v.IsExplored && v.IsEmpty)) { return; } var primitive = Library.GetVoxelPrimitive(v.Type); if (v.IsExplored && primitive == null) { return; } if (!v.IsExplored && v.Sunlight) { return; } if (primitive == null) { primitive = BedrockModel; } var tint = v.Type.Tint; var uvs = primitive.UVs; if (v.Type.HasTransitionTextures && v.IsExplored) { uvs = ComputeTransitionTexture(new VoxelHandle(v.Chunk.Manager, v.Coordinate)); } BuildVoxelTopFaceGeometry(Into, Chunk, Cache, primitive, v, uvs, 0); for (int i = 1; i < 6; i++) { BuildVoxelFaceGeometry(Into, Chunk, Cache, primitive, v, tint, uvs, Matrix.Identity, i, true); } }
private static void UpdateCornerRamps(ChunkManager Chunks, VoxelChunk Chunk, int LocalY) { for (int x = 0; x < VoxelConstants.ChunkSizeX; x++) { for (int z = 0; z < VoxelConstants.ChunkSizeZ; z++) { UpdateVoxelRamps(Chunks, VoxelHandle.UnsafeCreateLocalHandle(Chunk, new LocalVoxelCoordinate(x, LocalY, z))); } } }
public VoxelChunk ToChunk(ChunkManager Manager) { var c = new VoxelChunk(Manager, ID); for (var i = 0; i < VoxelConstants.ChunkVoxelCount; ++i) { c.Data.Types[i] = Types[i]; } // Remap the saved voxel ids to the ids of the currently loaded voxels. Remap(c.Data.Types.Length, VoxelTypeMap, Library.GetVoxelTypeMap(), (index) => Types[index], (index, value) => c.Data.Types[index] = (byte)value); for (var i = 0; i < VoxelConstants.ChunkVoxelCount; ++i) { if (c.Data.Types[i] > 0) { c.Data.VoxelsPresentInSlice[(i >> VoxelConstants.ZDivShift) >> VoxelConstants.XDivShift] += 1; } } if (Liquid != null) { Liquid.CopyTo(c.Data._Water, 0); for (int y = 0; y < VoxelConstants.ChunkSizeY; y++) { for (int x = 0; x < VoxelConstants.ChunkSizeX; x++) { for (int z = 0; z < VoxelConstants.ChunkSizeZ; z++) { c.Data.LiquidPresent[y] += VoxelHandle.UnsafeCreateLocalHandle(c, new LocalVoxelCoordinate(x, y, z)).LiquidLevel; } } } } if (RampsSunlightExplored != null) { RampsSunlightExplored.CopyTo(c.Data.RampsSunlightExploredPlayerBuilt, 0); } if (GrassType != null) { GrassType.CopyTo(c.Data.Grass, 0); } // Remap grass. Remap(c.Data.Grass.Length, GrassTypeMap, Library.GetGrassTypeMap(), (index) => c.Data.Grass[index] >> VoxelConstants.GrassTypeShift, (index, value) => c.Data.Grass[index] = (byte)((c.Data.Grass[index] & VoxelConstants.GrassDecayMask) | (value << VoxelConstants.GrassTypeShift))); return(c); }
private void DiscreteUpdate(ChunkManager ChunkManager, VoxelChunk chunk) { for (var y = 0; y < VoxelConstants.ChunkSizeY; ++y) { // Apply 'liquid present' tracking in voxel data to skip entire slices. if (chunk.Data.LiquidPresent[y] == 0) { continue; } var layerOrder = SlicePermutations[MathFunctions.RandInt(0, SlicePermutations.Length)]; for (var i = 0; i < layerOrder.Length; ++i) { var x = layerOrder[i] % VoxelConstants.ChunkSizeX; var z = (layerOrder[i] >> VoxelConstants.XDivShift) % VoxelConstants.ChunkSizeZ; var currentVoxel = VoxelHandle.UnsafeCreateLocalHandle(chunk, new LocalVoxelCoordinate(x, y, z)); if (currentVoxel.TypeID != 0) { continue; } if (currentVoxel.LiquidType == LiquidType.None || currentVoxel.LiquidLevel < 1) { continue; } // Evaporate. if (currentVoxel.LiquidLevel <= EvaporationLevel && MathFunctions.RandEvent(0.01f)) { if (currentVoxel.LiquidType == LiquidType.Lava) { currentVoxel.Type = Library.GetVoxelType("Stone"); } NeedsMinimapUpdate = true; currentVoxel.QuickSetLiquid(LiquidType.None, 0); continue; } var voxBelow = ChunkManager.CreateVoxelHandle(new GlobalVoxelCoordinate(currentVoxel.Coordinate.X, currentVoxel.Coordinate.Y - 1, currentVoxel.Coordinate.Z)); if (voxBelow.IsValid && voxBelow.IsEmpty) { // Fall into the voxel below. // Special case: No liquid below, just drop down. if (voxBelow.LiquidType == LiquidType.None) { NeedsMinimapUpdate = true; CreateSplash(currentVoxel.Coordinate.ToVector3(), currentVoxel.LiquidType); voxBelow.QuickSetLiquid(currentVoxel.LiquidType, currentVoxel.LiquidLevel); currentVoxel.QuickSetLiquid(LiquidType.None, 0); continue; } var belowType = voxBelow.LiquidType; var aboveType = currentVoxel.LiquidType; var spaceLeftBelow = maxWaterLevel - voxBelow.LiquidLevel; if (spaceLeftBelow >= currentVoxel.LiquidLevel) { NeedsMinimapUpdate = true; CreateSplash(currentVoxel.Coordinate.ToVector3(), aboveType); voxBelow.LiquidLevel += currentVoxel.LiquidLevel; currentVoxel.QuickSetLiquid(LiquidType.None, 0); HandleLiquidInteraction(voxBelow, aboveType, belowType); continue; } if (spaceLeftBelow > 0) { NeedsMinimapUpdate = true; CreateSplash(currentVoxel.Coordinate.ToVector3(), aboveType); currentVoxel.LiquidLevel = (byte)(currentVoxel.LiquidLevel - maxWaterLevel + voxBelow.LiquidLevel); voxBelow.LiquidLevel = maxWaterLevel; HandleLiquidInteraction(voxBelow, aboveType, belowType); continue; } } else if (voxBelow.IsValid && currentVoxel.LiquidType == LiquidType.Lava && !voxBelow.IsEmpty && voxBelow.GrassType > 0) { voxBelow.GrassType = 0; } if (currentVoxel.LiquidLevel <= 1) { continue; } // Nothing left to do but spread. RollArray(NeighborPermutations[MathFunctions.RandInt(0, NeighborPermutations.Length)], NeighborScratch, MathFunctions.RandInt(0, 4)); for (var n = 0; n < NeighborScratch.Length; ++n) { var neighborOffset = VoxelHelpers.ManhattanNeighbors2D[NeighborScratch[n]]; var neighborVoxel = new VoxelHandle(Chunks, currentVoxel.Coordinate + neighborOffset); if (neighborVoxel.IsValid && neighborVoxel.IsEmpty) { if (neighborVoxel.LiquidLevel < currentVoxel.LiquidLevel) { NeedsMinimapUpdate = true; var amountToMove = (int)(currentVoxel.LiquidLevel * GetSpreadRate(currentVoxel.LiquidType)); if (neighborVoxel.LiquidLevel + amountToMove > maxWaterLevel) { amountToMove = maxWaterLevel - neighborVoxel.LiquidLevel; } if (amountToMove > 2) { CreateSplash(neighborVoxel.Coordinate.ToVector3(), currentVoxel.LiquidType); } var newWater = currentVoxel.LiquidLevel - amountToMove; var sourceType = currentVoxel.LiquidType; var destType = neighborVoxel.LiquidType; currentVoxel.QuickSetLiquid(newWater == 0 ? LiquidType.None : sourceType, (byte)newWater); neighborVoxel.QuickSetLiquid(destType == LiquidType.None ? sourceType : destType, (byte)(neighborVoxel.LiquidLevel + amountToMove)); HandleLiquidInteraction(neighborVoxel, sourceType, destType); break; } } } } } }
private static void UpdateChunk(VoxelChunk chunk) { var addGrassToThese = new List <Tuple <VoxelHandle, byte> >(); for (var y = 0; y < VoxelConstants.ChunkSizeY; ++y) { // Skip empty slices. if (chunk.Data.VoxelsPresentInSlice[y] == 0) { continue; } for (var x = 0; x < VoxelConstants.ChunkSizeX; ++x) { for (var z = 0; z < VoxelConstants.ChunkSizeZ; ++z) { var voxel = VoxelHandle.UnsafeCreateLocalHandle(chunk, new LocalVoxelCoordinate(x, y, z)); // Allow grass to decay if (voxel.GrassType != 0) { var grass = Library.GetGrassType(voxel.GrassType); if (grass.NeedsSunlight && !voxel.Sunlight) { voxel.GrassType = 0; } else if (grass.Decay) { if (voxel.GrassDecay == 0) { var newDecal = Library.GetGrassType(grass.BecomeWhenDecays); if (newDecal != null) { voxel.GrassType = newDecal.ID; } else { voxel.GrassType = 0; } } else { voxel.GrassDecay -= 1; } } } //#if false else if (voxel.Type.GrassSpreadsHere) { // Spread grass onto this tile - but only from the same biome. // Don't spread if there's an entity here. var entityPresent = chunk.Manager.World.EnumerateIntersectingObjects( new BoundingBox(voxel.WorldPosition + new Vector3(0.1f, 1.1f, 0.1f), voxel.WorldPosition + new Vector3(0.9f, 1.9f, 0.9f)), CollisionType.Static).Any(); if (entityPresent) { continue; } if (chunk.Manager.World.Overworld.Map.GetBiomeAt(voxel.Coordinate.ToVector3(), chunk.Manager.World.Overworld.InstanceSettings.Origin).HasValue(out var biome)) { var grassyNeighbors = VoxelHelpers.EnumerateManhattanNeighbors2D(voxel.Coordinate) .Select(c => new VoxelHandle(voxel.Chunk.Manager, c)) .Where(v => v.IsValid && v.GrassType != 0) .Where(v => Library.GetGrassType(v.GrassType).Spreads) .Where(v => { if (chunk.Manager.World.Overworld.Map.GetBiomeAt(v.Coordinate.ToVector3(), chunk.Manager.World.Overworld.InstanceSettings.Origin).HasValue(out var otherBiome)) { return(biome == otherBiome); } return(false); }) .ToList(); if (grassyNeighbors.Count > 0) { if (MathFunctions.RandEvent(0.1f)) { addGrassToThese.Add(Tuple.Create(voxel, grassyNeighbors[MathFunctions.RandInt(0, grassyNeighbors.Count)].GrassType)); } } } } //#endif } } } foreach (var v in addGrassToThese) { var l = v.Item1; var grassType = Library.GetGrassType(v.Item2); if (grassType.NeedsSunlight && !l.Sunlight) { continue; } l.GrassType = v.Item2; } }
// This will loop through the whole world and draw out all liquid primatives that are handed to the function. public static void InitializePrimativesFromChunk(VoxelChunk chunk, List <LiquidPrimitive> primitivesToInit) { LiquidPrimitive[] lps = new LiquidPrimitive[(int)LiquidType.Count]; if (!AddCaches(primitivesToInit, ref lps)) { return; } LiquidType curLiqType = LiquidType.None; LiquidPrimitive curPrimitive = null; ExtendedVertex[] curVertices = null; ushort[] curIndexes = null; int[] maxVertices = new int[lps.Length]; int[] maxIndexes = new int[lps.Length]; int maxVertex = 0; int maxIndex = 0; int totalFaces = 6; bool fogOfWar = GameSettings.Current.FogofWar; for (int globalY = chunk.Origin.Y; globalY < Math.Min(chunk.Manager.World.Renderer.PersistentSettings.MaxViewingLevel + 1, chunk.Origin.Y + VoxelConstants.ChunkSizeY); globalY++) { var y = globalY - chunk.Origin.Y; if (chunk.Data.LiquidPresent[y] == 0) { continue; } for (int x = 0; x < VoxelConstants.ChunkSizeX; x++) { for (int z = 0; z < VoxelConstants.ChunkSizeZ; z++) { var voxel = VoxelHandle.UnsafeCreateLocalHandle(chunk, new LocalVoxelCoordinate(x, y, z)); if (fogOfWar && !voxel.IsExplored) { continue; } if (voxel.LiquidLevel > 0) { var liqType = voxel.LiquidType; // We need to see if we changed types and should change the data we are writing to. if (liqType != curLiqType) { LiquidPrimitive newPrimitive = lps[(int)liqType]; // We weren't passed a LiquidPrimitive object to work with for this type so we'll skip it. if (newPrimitive == null) { continue; } maxVertices[(int)curLiqType] = maxVertex; maxIndexes[(int)curLiqType] = maxIndex; curVertices = newPrimitive.Vertices; curIndexes = newPrimitive.Indexes; curLiqType = liqType; curPrimitive = newPrimitive; maxVertex = maxVertices[(int)liqType]; maxIndex = maxIndexes[(int)liqType]; } int facesToDraw = 0; for (int i = 0; i < totalFaces; i++) { BoxFace face = (BoxFace)i; // We won't draw the bottom face. This might be needed down the line if we add transparent tiles like glass. if (face == BoxFace.Bottom) { continue; } var delta = faceDeltas[(int)face]; // Pull the current neighbor DestinationVoxel based on the face it would be touching. var vox = VoxelHelpers.GetNeighbor(voxel, delta); if (vox.IsValid) { if (face == BoxFace.Top) { if (!(vox.LiquidLevel == 0 || y == (int)chunk.Manager.World.Renderer.PersistentSettings.MaxViewingLevel)) { cache.drawFace[(int)face] = false; continue; } } else { if (vox.LiquidLevel != 0 || !vox.IsEmpty) { cache.drawFace[(int)face] = false; continue; } } } else { cache.drawFace[(int)face] = false; continue; } cache.drawFace[(int)face] = true; facesToDraw++; } // There's no faces to draw on this voxel. Let's go to the next one. if (facesToDraw == 0) { continue; } // Now we check to see if we need to resize the current Vertex array. int vertexSizeIncrease = facesToDraw * 4; int indexSizeIncrease = facesToDraw * 6; lock (curPrimitive.VertexLock) { // Check vertex array size if (curVertices == null) { curVertices = new ExtendedVertex[256]; curPrimitive.Vertices = curVertices; } else if (curVertices.Length <= maxVertex + vertexSizeIncrease) { ExtendedVertex[] newVerts = new ExtendedVertex[MathFunctions.NearestPowerOf2(maxVertex + vertexSizeIncrease)]; curVertices.CopyTo(newVerts, 0); curVertices = newVerts; curPrimitive.Vertices = curVertices; } // Check index array size if (curIndexes == null) { curIndexes = new ushort[256]; curPrimitive.Indexes = curIndexes; } else if (curIndexes.Length <= maxIndex + indexSizeIncrease) { ushort[] newIdxs = new ushort[MathFunctions.NearestPowerOf2(maxIndex + indexSizeIncrease)]; curIndexes.CopyTo(newIdxs, 0); curIndexes = newIdxs; curPrimitive.Indexes = curIndexes; } } // Now we have a list of all the faces that will need to be drawn. Let's draw them. CreateWaterFaces(voxel, chunk, x, y, z, curVertices, curIndexes, maxVertex, maxIndex); // Finally increase the size so we can move on. maxVertex += vertexSizeIncrease; maxIndex += indexSizeIncrease; } } } } // The last thing we need to do is make sure we set the current primative's maxVertices to the right value. maxVertices[(int)curLiqType] = maxVertex; maxIndexes[(int)curLiqType] = maxIndex; // Now actually force the VertexBuffer to be recreated in each primative we worked with. for (int i = 0; i < lps.Length; i++) { LiquidPrimitive updatedPrimative = lps[i]; if (updatedPrimative == null) { continue; } maxVertex = maxVertices[i]; maxIndex = maxIndexes[i]; if (maxVertex > 0) { try { lock (updatedPrimative.VertexLock) { updatedPrimative.VertexCount = maxVertex; updatedPrimative.IndexCount = maxIndex; updatedPrimative.VertexBuffer = null; updatedPrimative.IndexBuffer = null; } } catch (global::System.Threading.AbandonedMutexException e) { Console.Error.WriteLine(e.Message); } } else { try { lock (updatedPrimative.VertexLock) { updatedPrimative.VertexBuffer = null; updatedPrimative.Vertices = null; updatedPrimative.IndexBuffer = null; updatedPrimative.Indexes = null; updatedPrimative.VertexCount = 0; updatedPrimative.IndexCount = 0; } } catch (global::System.Threading.AbandonedMutexException e) { Console.Error.WriteLine(e.Message); } } updatedPrimative.IsBuilding = false; } cache.inUse = false; cache = null; }
public void RebuildMoteLayer(int LocalY) { #if DEBUG if (LocalY < 0 || LocalY >= VoxelConstants.ChunkSizeY) { throw new InvalidOperationException(); } #endif var moteList = new List <NewInstanceData>(); // Enumerate voxels. for (var x = 0; x < VoxelConstants.ChunkSizeX; ++x) { for (var z = 0; z < VoxelConstants.ChunkSizeZ; ++z) { var v = VoxelHandle.UnsafeCreateLocalHandle(this, new LocalVoxelCoordinate(x, LocalY, z)); if (!v.IsValid) { continue; } // Don't generate in empty voxels. if (v.IsEmpty) { continue; } if (!v.IsExplored) { continue; } // Don't generate motes if above is not empty var voxelAbove = VoxelHelpers.GetVoxelAbove(v); if (voxelAbove.IsValid && (!voxelAbove.IsEmpty || voxelAbove.LiquidLevel != 0)) { continue; } // Find biome type. var biomeData = Manager.World.Overworld.Map.GetBiomeAt(v.WorldPosition, Manager.World.Overworld.InstanceSettings.Origin); // Don't generate if not on grass type. if (v.GrassType == 0 || Library.GetGrassType(v.GrassType).Name != biomeData.GrassDecal) { continue; } // Biomes can contain multiple types of mote. foreach (var moteDetail in biomeData.Motes) { // Lower mote if voxel is ramped. float vOffset = 0.0f; if (v.RampType != RampType.None) { vOffset = -0.5f; } var vPos = v.WorldPosition * moteDetail.RegionScale; float value = MoteNoise.Noise(vPos.X, vPos.Y, vPos.Z); if (!(Math.Abs(value) > moteDetail.SpawnThreshold)) { continue; } float s = MoteScaleNoise.Noise(vPos.X, vPos.Y, vPos.Z) * moteDetail.MoteScale; var smallNoise = ClampVector(VertexNoise.GetRandomNoiseVector(vPos * 20.0f) * 20.0f, 0.4f); smallNoise.Y = 0.0f; var mote = GenerateGrassMote( Manager.World, v.WorldPosition + new Vector3(0.5f, 1.0f + s * 0.5f + vOffset, 0.5f) + smallNoise, new Color(v.Sunlight ? 255 : 0, 128, 0), s, moteDetail); moteList.Add(mote); } } } MoteRecords[LocalY] = moteList; }