public void RecomputeNeighbors() { foreach (VoxelChunk chunk in ChunkMap.Select(chunks => chunks.Value)) { chunk.Neighbors.Clear(); chunk.EuclidianNeighbors.Clear(); } foreach (KeyValuePair <Point3, VoxelChunk> chunks in ChunkMap) { VoxelChunk chunk = chunks.Value; Point3 successor = new Point3(0, 0, 0); for (successor.X = -1; successor.X < 2; successor.X++) { for (successor.Z = -1; successor.Z < 2; successor.Z++) { for (successor.Y = -1; successor.Y < 2; successor.Y++) { Point3 sideChunkID = chunk.ID + successor; VoxelChunk sideChunk; ChunkMap.TryGetValue(sideChunkID, out sideChunk); if (successor.Y == 0 && sideChunk != null) { if (!sideChunk.Neighbors.ContainsKey(chunk.ID) && chunk != sideChunk) { chunk.Neighbors[chunk.ID] = chunk; } chunk.Neighbors[sideChunkID] = sideChunk; } chunk.EuclidianNeighbors[VoxelChunk.SuccessorToEuclidianLookupKey(successor)] = sideChunk; } } } } }
public bool GetNeighborBySuccessor(Vector3 succ, ref Voxel neighbor, bool requireQuickCompare = true) { Debug.Assert(neighbor != null, "Null reference passed"); Debug.Assert(_chunk != null, "Voxel has no valid chunk reference"); Vector3 newPos = gridpos + succ; Point3 chunkSuccessor = Point3.Zero; bool useSuccessor = false; if (newPos.X >= _chunk.SizeX) { chunkSuccessor.X = 1; newPos.X = 0; useSuccessor = true; } else if (newPos.X < 0) { chunkSuccessor.X = -1; newPos.X = _chunk.SizeX - 1; useSuccessor = true; } if (newPos.Y >= _chunk.SizeY) { chunkSuccessor.Y = 1; newPos.Y = 0; useSuccessor = true; } else if (newPos.Y < 0) { chunkSuccessor.Y = -1; newPos.Y = _chunk.SizeY - 1; useSuccessor = true; } if (newPos.Z >= _chunk.SizeZ) { chunkSuccessor.Z = 1; newPos.Z = 0; useSuccessor = true; } else if (newPos.Z < 0) { chunkSuccessor.Z = -1; newPos.Z = _chunk.SizeZ - 1; useSuccessor = true; } VoxelChunk useChunk; if (useSuccessor) { useChunk = _chunk.EuclidianNeighbors[VoxelChunk.SuccessorToEuclidianLookupKey(chunkSuccessor)]; if (useChunk == null) { return(false); } } else { useChunk = _chunk; } neighbor.ChangeVoxel(useChunk, newPos, requireQuickCompare); return(true); }
private static void CreateWaterFaces(Voxel voxel, VoxelChunk chunk, int x, int y, int z, ExtendedVertex[] vertices, int startVertex) { // Reset the appropriate parts of the cache. cache.Reset(); // These are reused for every face. Vector3 origin = chunk.Origin + new Vector3(x, y, z); int index = chunk.Data.IndexAt(x, y, z); float centerWaterlevel = chunk.Data.Water[chunk.Data.IndexAt(x, y, z)].WaterLevel; for (int faces = 0; faces < cache.drawFace.Length; faces++) { if (!cache.drawFace[faces]) { continue; } BoxFace face = (BoxFace)faces; // Let's get the vertex/index positions for the current face. int idx = 0; int vertexCount = 0; int vertOffset = 0; int numVerts = 0; primitive.GetFace(face, primitive.UVs, out idx, out vertexCount, out vertOffset, out numVerts); for (int i = 0; i < vertexCount; i++) { // Used twice so we'll store it for later use. int primitiveIndex = primitive.Indexes[i + idx]; VoxelVertex currentVertex = primitive.Deltas[primitiveIndex]; // These two will be filled out before being used. float foaminess; Vector3 pos; // We are going to have to reuse some vertices when drawing a single so we'll store the position/foaminess // for quick lookup when we find one of those reused ones. // When drawing multiple faces the Vertex overlap gets bigger, which is a bonus. if (!cache.vertexCalculated[(int)currentVertex]) { float count = 1.0f; float emptyNeighbors = 0.0f; float averageWaterLevel = centerWaterlevel; List <Vector3> vertexSucc = VoxelChunk.VertexSuccessors[currentVertex]; // Run through the successors and count up the water in each voxel. for (int v = 0; v < vertexSucc.Count; v++) { Vector3 succ = vertexSucc[v]; // We are going to use a lookup key so calculate it now. int key = VoxelChunk.SuccessorToEuclidianLookupKey(succ); // If we haven't gotten this Voxel yet then retrieve it. // This allows us to only get a particular voxel once a function call instead of once per vertexCount/per face. if (!cache.retrievedNeighbors[key]) { Voxel neighbor = cache.neighbors[key]; cache.validNeighbors[key] = voxel.GetNeighborBySuccessor(succ, ref neighbor, false); cache.retrievedNeighbors[key] = true; } // Only continue if it's a valid (non-null) voxel. if (!cache.validNeighbors[key]) { continue; } // Now actually do the math. Voxel vox = cache.neighbors[key]; averageWaterLevel += vox.WaterLevel; count++; if (vox.WaterLevel < 1) { emptyNeighbors++; } } averageWaterLevel = averageWaterLevel / count; float averageWaterHeight = averageWaterLevel / WaterManager.maxWaterLevel; foaminess = emptyNeighbors / count; if (foaminess <= 0.5f) { foaminess = 0.0f; } pos = primitive.Vertices[primitiveIndex].Position; pos.Y *= averageWaterHeight; pos += origin; // Store the vertex information for future use when we need it again on this or another face. cache.vertexCalculated[(int)currentVertex] = true; cache.vertexFoaminess[(int)currentVertex] = foaminess; cache.vertexPositions[(int)currentVertex] = pos; } else { // We've already calculated this one. Time for a cheap grab from the lookup. foaminess = cache.vertexFoaminess[(int)currentVertex]; pos = cache.vertexPositions[(int)currentVertex]; } switch (face) { case BoxFace.Back: case BoxFace.Front: vertices[i + startVertex].Set(pos, new Color(foaminess, 0.0f, 1.0f, 1.0f), Color.White, new Vector2(pos.X, pos.Y), new Vector4(0, 0, 1, 1)); break; case BoxFace.Right: case BoxFace.Left: vertices[i + startVertex].Set(pos, new Color(foaminess, 0.0f, 1.0f, 1.0f), Color.White, new Vector2(pos.Z, pos.Y), new Vector4(0, 0, 1, 1)); break; case BoxFace.Top: vertices[i + startVertex].Set(pos, new Color(foaminess, 0.0f, 1.0f, 1.0f), Color.White, new Vector2(pos.X, pos.Z), new Vector4(0, 0, 1, 1)); break; } } startVertex += 6; } }