public float DistanceSquared(ref Vector3Int otherVector) { var dX = otherVector.X - X; var dY = otherVector.X - X; var dZ = otherVector.X - X; return dX * dX + dY * dY + dZ * dZ; }
public FluidContainer(MappingFunction mappingFunction, Block[] blocks, FluidCell solidCellReference) { m_mappingFunction = mappingFunction; m_blocks = blocks; m_solidCellReference = solidCellReference; Update = true; Alive = true; m_min = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue); m_max = new Vector3Int(-int.MaxValue, -int.MaxValue, -int.MaxValue); Cells = new List<FluidCell>(); CellDictionary = new Dictionary<long, FluidCell>(); Updated = new List<FluidCell>(); }
public CloudTarget(Vector3Int position, int sizeX, int sizeY, int sizeZ, int renderScale) { // calculate the real world position. Position = position; // calculate bounding-box. BoundingBox = new BoundingBox(new Vector3(Position.X, Position.Y, Position.Z), new Vector3(Position.X + sizeX, Position.Y + sizeY, Position.Z + sizeZ)); RenderingBoundingBox = new BoundingBox(new Vector3(Position.X, Position.Y, Position.Z), new Vector3(Position.X + sizeX * renderScale, Position.Y + sizeY * renderScale, Position.Z + sizeZ * renderScale)); }
public abstract void RemoveBlock(Vector3Int position);
//TODO : break this out into sub functions public void Step() { foreach (var cell in Cells) { if (cell.Awake && cell.Type == FluidCell.CellType.Water) //In theory this check is redundant... { cell.Propagate(); } } var hasChanged = false; var potentialPruningNeeded = false; foreach (var cell in Updated) { if (cell.Type == FluidCell.CellType.Solid) { hasChanged = true; potentialPruningNeeded = true; } if (Math.Abs(cell.Level - cell.LevelNextStep) >= FluidCell.MinFlow / 2.0f) { hasChanged = true; cell.Awake = true; } cell.Level = cell.LevelNextStep; if (cell.Type == FluidCell.CellType.Water && cell.Level < FluidCell.MinLevel) { potentialPruningNeeded = true; cell.Type = FluidCell.CellType.Air; } if (!(cell.Type == FluidCell.CellType.Air && cell.Level > FluidCell.MinLevel)) { continue; } cell.Type = FluidCell.CellType.Water; RecalculateBounds(cell); BuildNeighborhood(cell); Cells.Add(cell); } Updated.Clear(); if (potentialPruningNeeded) { m_min = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue); m_max = new Vector3Int(-int.MaxValue, -int.MaxValue, -int.MaxValue); var removeList = new List<FluidCell>(); foreach (var cell in Cells) { if (cell.Type == FluidCell.CellType.Solid || cell.Type == FluidCell.CellType.Air) { removeList.Add(cell); } else { RecalculateBounds(cell); } } foreach (var cell in removeList) { Cells.Remove(cell); if ((cell.Up != null && cell.Up.Type == FluidCell.CellType.Water) || (cell.North != null && cell.North.Type == FluidCell.CellType.Water) || (cell.East != null && cell.East.Type == FluidCell.CellType.Water) || (cell.South != null && cell.South.Type == FluidCell.CellType.Water) || (cell.West != null && cell.West.Type == FluidCell.CellType.Water) || (cell.Down != null && cell.Down.Type == FluidCell.CellType.Water)) { continue; } var cellHash = m_mappingFunction(cell.X, cell.Y, cell.Z); CellDictionary.Remove(cellHash); } } Alive = Cells.Count != 0; Update = hasChanged; }
public VisibilityTraceNode(Vector3Int position, int index) { Position = position; Index = index; }
private int IterateOnFace(FaceDirection direction, ref Vector3Int index) { int i1; switch(direction) { case FaceDirection.XIncreasing: case FaceDirection.XDecreasing: i1 = index.Y; index.Y = (i1 + 1) % SizeInBlocks; if(index.Y < i1) { index.Z = (index.Z + 1) % SizeInBlocks; } break; case FaceDirection.YIncreasing: case FaceDirection.YDecreasing: i1 = index.X; index.X = (i1 + 1) % SizeInBlocks; if(index.X < i1) { index.Z = (index.Z + 1) % SizeInBlocks; } break; case FaceDirection.ZIncreasing: case FaceDirection.ZDecreasing: i1 = index.Y; index.Y = (i1 + 1) % SizeInBlocks; if(index.Y < i1) { index.X = (index.X + 1) % SizeInBlocks; } break; } return m_mappingFunction(index.X, index.Y, index.Z); }
private void RecacheChunks() { foreach (var chunk in m_chunkStorage.Values.Where(chunk => !IsInCacheRange(chunk))) { m_chunkStorage.Remove(chunk.ChunkCachePosition.X, chunk.ChunkCachePosition.Y, chunk.ChunkCachePosition.Z); chunk.PrepForRemoval(); } m_processingQueue.Clear(); var toAdd = new List<Chunk>(); var chunkPosition = new Vector3Int((int)(m_cacheCenterPosition.X / Chunk.SizeInBlocks), (int)(m_cacheCenterPosition.Y / Chunk.SizeInBlocks), ((int)m_cacheCenterPosition.Z / Chunk.SizeInBlocks)); for (var z = -CacheRange; z <= CacheRange; z++) { for (var y = -CacheRange; y <= CacheRange; y++) { for (var x = -CacheRange; x <= CacheRange; x++) { Chunk chunk; if (!m_chunkStorage.ContainsKey(chunkPosition.X + x, chunkPosition.Y + y, chunkPosition.Z + z)) { chunk = new Chunk( new Vector3Int(chunkPosition.X + x, chunkPosition.Y + y, chunkPosition.Z + z), Blocks, BlockIndexByWorldPosition, GetNeighborChunk); m_chunkStorage[ chunk.ChunkCachePosition.X, chunk.ChunkCachePosition.Y, chunk.ChunkCachePosition.Z] = chunk; } else { chunk = m_chunkStorage[chunkPosition.X + x, chunkPosition.Y + y, chunkPosition.Z + z]; } if ((IsInViewRange(chunk) && chunk.ChunkState == ChunkState.Ready) || (!IsInViewRange(chunk) && chunk.ChunkState == ChunkState.AwaitingLighting)) { continue; } toAdd.Add(chunk); } } } var center = new Vector3(m_cacheCenterPosition.X - Chunk.SizeInBlocks / 2, m_cacheCenterPosition.Y - Chunk.SizeInBlocks / 2, m_cacheCenterPosition.Z - Chunk.SizeInBlocks / 2); toAdd.Sort((chunk1, chunk2) => { var ret = (int) (chunk1.Position.DistanceSquared(ref center) - chunk2.Position.DistanceSquared(ref center)); if (ret == 0) { return (chunk2.ChunkState - chunk1.ChunkState); } return ret; }); foreach (var chunk in toAdd) { m_processingQueue.Enqueue(chunk); } }
private static void CopyCunkToPage(Page page, Vector3Int position, Block[] blocks, MappingFunction mappingFunction) { var originX = MathUtilities.Modulo(position.X, Page.PageSizeInBlocks); var originY = MathUtilities.Modulo(position.Y, Page.PageSizeInBlocks); var originZ = MathUtilities.Modulo(position.Z, Page.PageSizeInBlocks); for (var x = originX; x < originX + Chunk.SizeInBlocks; x++) { for (var y = originY; y < originY + Chunk.SizeInBlocks; y++) { for (var z = originZ; z < originZ + Chunk.SizeInBlocks; z++) { page.Data[Page.BlockIndexFromRelativePosition(x, y, z)] = blocks[mappingFunction(position.X + x, position.Y + y, position.Z + z)]; } } } }
public bool Equals(Vector3Int other) { return (((X == other.X) && (Y == other.Y)) && (Z == other.Z)); }
public float Distance(ref Vector3Int otherVector) { return (float)Math.Sqrt(DistanceSquared(ref otherVector)); }
private void EvaluateLinearTree(Block[] blocks, out float compressionRatio, out ScanDirection optimalDirection) { var count = m_chunkSize * m_chunkSize * m_chunkSize; compressionRatio = 0; optimalDirection = ScanDirection.Xyz; foreach (ScanDirection scanDirection in Enum.GetValues(typeof(ScanDirection))) { var nodesRemoved = count; Block current = Block.Empty; Vector3Int workCoords = new Vector3Int(); for (var i = 0; i < count; ++i) { var block = blocks[GetNextIndex(scanDirection, m_chunkSize, ref workCoords)]; if (block == current) { continue; } current = block; --nodesRemoved; } var currentRatio = nodesRemoved / (float)count; if (currentRatio <= compressionRatio) { continue; } compressionRatio = currentRatio; optimalDirection = scanDirection; //this means we are mostly air and can just exit out now! if (currentRatio > 0.97f) { break; } } }
private SortedList<Interval, Block> ConvertArrayToIntervalTreeLinear(Block[] blocks, ScanDirection optimalDirection, out float compressionRatio) { var count = m_chunkSize * m_chunkSize * m_chunkSize; var nodesRemoved = count; var intervals = new SortedList<Interval, Block>(); var min = 0; var max = 0; var current = Block.Empty; var started = false; var workCoords = new Vector3Int(); for (var i = 0; i < count; ++i) { var block = blocks[GetNextIndex(optimalDirection, m_chunkSize, ref workCoords)]; if (!(started && block == current)) { if (started) { intervals.Add(new Interval((ushort)min, (ushort)max), current); } current = block; min = i; started = true; --nodesRemoved; } max = i; } intervals.Add(new Interval((ushort)min, (ushort)max), current); compressionRatio = nodesRemoved / (float)count; return intervals; }
private static int GetNextIndex(ScanDirection direction, int chunkSize, ref Vector3Int workCoords) { var x = workCoords.X; var y = workCoords.Y; var z = workCoords.Z; switch (direction) { case ScanDirection.Xyz: workCoords.X = (x + 1)%chunkSize; if (workCoords.X < x) { workCoords.Y = (y + 1)%chunkSize; if (workCoords.Y < y) { workCoords.Z = (z + 1)%chunkSize; } } break; case ScanDirection.Xzy: workCoords.X = (x + 1)%chunkSize; if (workCoords.X < x) { workCoords.Z = (z + 1)%chunkSize; if (workCoords.Z < z) { workCoords.Y = (y + 1)%chunkSize; } } break; case ScanDirection.Yxz: workCoords.Y = (y + 1)%chunkSize; if (workCoords.Y < y) { workCoords.X = (x + 1)%chunkSize; if (workCoords.X < x) { workCoords.Z = (z + 1)%chunkSize; } } break; case ScanDirection.Yzx: workCoords.Y = (y + 1)%chunkSize; if (workCoords.Y < y) { workCoords.Z = (z + 1) % chunkSize; if (workCoords.Z < z) { workCoords.X = (x + 1) % chunkSize; } } break; case ScanDirection.Zxy: workCoords.Z = (z + 1) % chunkSize; if (workCoords.Z < z) { workCoords.X = (x + 1) % chunkSize; if (workCoords.X < x) { workCoords.Y = (y + 1) % chunkSize; } } break; case ScanDirection.Zyx: workCoords.Z = (z + 1) % chunkSize; if (workCoords.Z < z) { workCoords.Y = (y + 1) % chunkSize; if (workCoords.Y < y) { workCoords.X = (x + 1) % chunkSize; } } break; default: throw new ArgumentOutOfRangeException("direction"); } return workCoords.X*chunkSize*chunkSize + workCoords.Z*chunkSize + workCoords.Y; }
public WorldEdit(Vector3Int position, WorldEditType editType) { Position = position; EditType = editType; }
public Chunk(Vector3Int chunkCachePosition, Block[] blocks, MappingFunction mappingFunction, GetNeighborChunk getNeighborChunk) { ChunkState = ChunkState.AwaitingGenerate; // set initial state to awaiting generation. ChunkCachePosition = chunkCachePosition; // set the relative position. m_blocks = blocks; m_mappingFunction = mappingFunction; m_getNeighborChunk = getNeighborChunk; // calculate the real world position. Position = new Vector3Int(ChunkCachePosition.X * SizeInBlocks, ChunkCachePosition.Y * SizeInBlocks, ChunkCachePosition.Z * SizeInBlocks); // calculate bounding-box. BoundingBox = new BoundingBox(new Vector3(Position.X, Position.Y, Position.Z), new Vector3(Position.X + SizeInBlocks, Position.Y + SizeInBlocks, Position.Z + SizeInBlocks)); #if DEBUG MainEngine.GetEngineInstance().DebugOnlyDebugManager.RegisterInGameDebuggable(this); #endif }
public void SaveChunk(Vector3Int position, Block[] blocks, MappingFunction mappingFunction, Action<bool> callback) { var pagePositionX = position.X >> 6; var pagePositionY = position.Y >> 6; var pagePositionZ = position.Z >> 6; var pageId = CreatePageId(pagePositionX, pagePositionY, pagePositionZ); if (m_pagesPendingWrite.Contains(pageId)) { if (callback != null) { callback(false); } } else { Page page; if (m_pageCache.ContainsPage(pageId)) { page = m_pageCache.GetPage(pageId); CopyCunkToPage(page, position, blocks, mappingFunction); } else if (m_directory.Pages.Contains(pageId)) { page = DecompressPage(pageId); CopyCunkToPage(page, position, blocks, mappingFunction); m_pageCache.InsertPage(page); } else { page = new Page(position.X, position.Y, position.Z, pageId); for (var x = 0; x < Page.PageSizeInBlocks; x++) { for (var y = 0; y < Page.PageSizeInBlocks; y++) { for (var z = 0; z < Page.PageSizeInBlocks; z++) { page.Data[Page.BlockIndexFromRelativePosition(x, y, z)] = blocks[mappingFunction(position.X + x, position.Y + y, position.Z + z)]; } } } m_pageCache.InsertPage(page); m_directory.Pages.Add(pageId); // m_jsonWriter.Write("SaveDirectory", m_directory); } m_pagesPendingWrite.Add(pageId); Task.Run(() => { CompressPage(page); if (callback != null) { callback(true); } m_pagesPendingWrite.Remove(page.PageId); }); } }
public void RecalculateVisivility() { m_visibilityTraceQueue = new Queue<VisibilityTraceNode>(); //We need to iterate over all blocks on each face of the chunk foreach (FaceDirection direction in Enum.GetValues(typeof(FaceDirection))) { var workVector = new Vector3Int(); for (var index = 0; index < SizeInBlocks * SizeInBlocks; ++index) { int faceIndex = IterateOnFace(direction, ref workVector); m_visibilityTraceQueue.Enqueue(new VisibilityTraceNode(workVector, faceIndex)); } while(m_visibilityTraceQueue.Count > 0) { var node = m_visibilityTraceQueue.Dequeue(); } } }
public void LoadOrCreateChunk(Vector3Int position, Block[] blocks, MappingFunction mappingFunction) { var pagePositionX = position.X / Page.PageSizeInBlocks; var pagePositionY = position.Y / Page.PageSizeInBlocks; var pagePositionZ = position.Z / Page.PageSizeInBlocks; var pageId = CreatePageId(pagePositionX, pagePositionY, pagePositionZ); if (m_pageCache.ContainsPage(pageId)) { var page = m_pageCache.GetPage(pageId); CopyPageToChunk(page, position, blocks, mappingFunction); } else if (m_directory.Pages.Contains(pageId)) { var page = DecompressPage(pageId); CopyPageToChunk(page, position, blocks, mappingFunction); m_pageCache.InsertPage(page); } else if (!m_pagesPendingWrite.Contains(pageId)) { m_pagesPendingWrite.Add(pageId); var page = new Page(position.X, position.Y, position.Z, pageId); var worldPosX = pagePositionX * Page.PageSizeInBlocks; var worldPosY = pagePositionY * Page.PageSizeInBlocks; var worldPosZ = pagePositionZ * Page.PageSizeInBlocks; TerrainGenerator.Instance.GenerateDataForChunk(worldPosX, worldPosY, worldPosZ, Page.PageSizeInBlocks, page.Data, (x, y, z) => Page.BlockIndexFromRelativePosition( x - worldPosX, y - worldPosY, z - worldPosZ)); //BlockDataUtilities.SetupScanDirectedHilbertCurve(Page.PageSizeInBlocks); // ChunkCompressor.GetHilbertCurve(32); var timer = Stopwatch.StartNew(); float compressionRatio; //ChunkCompressor.ScanDirection scanDir; var tree = ChunkCompressor.GetCompressor(Page.PageSizeInBlocks).ConvertArrayToIntervalTree(page.Data, out compressionRatio); Console.WriteLine("tree creation time: " + timer.ElapsedMilliseconds); // Console.WriteLine("h: " + scanDir); // Console.WriteLine("tree compresion ratio: " + compressionRatio); // timer.Restart(); // var tree2 = ChunkCompressor.ConvertArrayToIntervalTreeLinear(page.Data, Page.PageSizeInBlocks, // out compressionRatio, out scanDir); //Console.WriteLine("linear tree creation time: " + timer.ElapsedMilliseconds); // Console.WriteLine("l: " + scanDir); // Console.WriteLine("l: " + compressionRatio); // tree. timer.Restart(); var res = tree[new Interval(17490)]; Console.WriteLine("tree query time: " + timer.ElapsedMilliseconds); using (var fileStream = new FileStream(Path.Combine(m_dataDirectory, page.PageId + "_tree.page"), FileMode.Create)) { using (var compressor = new GZipStream(fileStream, CompressionLevel.Optimal)) { //Serializer.Serialize(compressor, tree); } } Console.WriteLine("tree compressionTime time: " + timer.ElapsedMilliseconds); timer.Restart(); CompressPage(page); Console.WriteLine("normal compressionTime time: " + timer.ElapsedMilliseconds); m_pageCache.InsertPage(page); m_directory.Pages.Add(pageId); CopyPageToChunk(page, position, blocks, mappingFunction); //m_jsonWriter.Write("SaveDirectory", m_directory); /* Task.Run(() => { CompressPage(page); m_pagesPendingWrite.Remove(page.PageId); });*/ } }
public Chunk GetChunkByWorldPosition(Vector3Int position) { return GetChunkByWorldPosition(position.X, position.Y, position.Z); }