private static bool AddCaches(List <LiquidPrimitive> primitivesToInit, ref LiquidPrimitive[] lps) { // We are going to first set up the internal array. foreach (LiquidPrimitive lp in primitivesToInit) { if (lp != null) { lps[(int)lp.LiqType] = lp; } } // We are going to lock around the IsBuilding check/set to avoid the situation where two threads could both pass through // if they both checked IsBuilding at the same time before either of them set IsBuilding. lock (caches) { // We check all parts of the array before setting any to avoid somehow setting a few then leaving before we can unset them. for (int i = 0; i < lps.Length; i++) { if (lps[i] != null && lps[i].IsBuilding) { return(false); } } // Now we know we are safe so we can set IsBuilding. for (int i = 0; i < lps.Length; i++) { if (lps[i] != null) { lps[i].IsBuilding = true; } } // Now we have to get a valid cache object. bool cacheSet = false; for (int i = 0; i < caches.Count; i++) { if (!caches[i].inUse) { cache = caches[i]; cache.inUse = true; cacheSet = true; } } if (!cacheSet) { cache = new LiquidRebuildCache(); cache.inUse = true; caches.Add(cache); } } return(true); }
// 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; }
// 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]; // We are going to first set up the internal array. foreach (LiquidPrimitive lp in primitivesToInit) { if (lp != null) { lps[(int)lp.LiqType] = lp; } } // We are going to lock around the IsBuilding check/set to avoid the situation where two threads could both pass through // if they both checked IsBuilding at the same time before either of them set IsBuilding. lock (caches) { // We check all parts of the array before setting any to avoid somehow setting a few then leaving before we can unset them. for (int i = 0; i < lps.Length; i++) { if (lps[i] != null && lps[i].IsBuilding) { return; } } // Now we know we are safe so we can set IsBuilding. for (int i = 0; i < lps.Length; i++) { if (lps[i] != null) { lps[i].IsBuilding = true; } } // Now we have to get a valid cache object. bool cacheSet = false; for (int i = 0; i < caches.Count; i++) { if (!caches[i].inUse) { cache = caches[i]; cache.inUse = true; cacheSet = true; } } if (!cacheSet) { cache = new LiquidRebuildCache(); cache.inUse = true; caches.Add(cache); } } LiquidType curLiqType = LiquidType.None; LiquidPrimitive curPrimative = null; ExtendedVertex[] curVertices = null; int[] maxVertices = new int[lps.Length]; int maxY = (int)Math.Min(chunk.Manager.ChunkData.MaxViewingLevel + 1, chunk.SizeY); Voxel myVoxel = chunk.MakeVoxel(0, 0, 0); Voxel vox = chunk.MakeVoxel(0, 0, 0); int maxVertex = 0; bool fogOfWar = GameSettings.Default.FogofWar; for (int x = 0; x < chunk.SizeX; x++) { for (int y = 0; y < maxY; y++) { for (int z = 0; z < chunk.SizeZ; z++) { int index = chunk.Data.IndexAt(x, y, z); if (fogOfWar && !chunk.Data.IsExplored[index]) { continue; } if (chunk.Data.Water[index].WaterLevel > 0) { LiquidType liqType = chunk.Data.Water[index].Type; // 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; curVertices = newPrimitive.Vertices; curLiqType = liqType; curPrimative = newPrimitive; maxVertex = maxVertices[(int)liqType]; } myVoxel.GridPosition = new Vector3(x, y, z); int facesToDraw = 0; for (int i = 0; i < 6; 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; } Vector3 delta = faceDeltas[(int)face]; // Pull the current neighbor Voxel based on the face it would be touching. bool success = myVoxel.GetNeighborBySuccessor(delta, ref vox, false); if (success) { if (face == BoxFace.Top) { if (!(vox.WaterLevel == 0 || y == (int)chunk.Manager.ChunkData.MaxViewingLevel)) { cache.drawFace[(int)face] = false; continue; } } else { if (vox.WaterLevel != 0 || !vox.IsEmpty) { 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 * 6; if (curVertices == null) { curVertices = new ExtendedVertex[256]; curPrimative.Vertices = curVertices; } else if (curVertices.Length <= maxVertex + vertexSizeIncrease) { ExtendedVertex[] newVerts = new ExtendedVertex[curVertices.Length * 2]; curVertices.CopyTo(newVerts, 0); curVertices = newVerts; curPrimative.Vertices = curVertices; } // Now we have a list of all the faces that will need to be drawn. Let's draw them. CreateWaterFaces(myVoxel, chunk, x, y, z, curVertices, maxVertex); // Finally increase the size so we can move on. maxVertex += vertexSizeIncrease; } } } } // 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; // 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]; if (maxVertex > 0) { try { lock (updatedPrimative.VertexLock) { updatedPrimative.MaxVertex = maxVertex; updatedPrimative.VertexBuffer = null; } } catch (System.Threading.AbandonedMutexException e) { Console.Error.WriteLine(e.Message); } } else { try { lock (updatedPrimative.VertexLock) { updatedPrimative.VertexBuffer = null; updatedPrimative.Vertices = null; updatedPrimative.Indexes = null; updatedPrimative.MaxVertex = 0; updatedPrimative.MaxIndex = 0; } } catch (System.Threading.AbandonedMutexException e) { Console.Error.WriteLine(e.Message); } } updatedPrimative.IsBuilding = false; } cache.inUse = false; cache = null; }