public ChunkCache(Game game) : base(game) { m_logger = MainEngine.GetEngineInstance().GetLogger("ChunkCache"); Debug.Assert(game != null); var graphicsDevice = game.GraphicsDevice; Debug.Assert(ViewRange < CacheRange); Debug.Assert(graphicsDevice != null); Blocks = new Block[CacheSizeInBlocks * CacheSizeInBlocks * CacheSizeInBlocks]; m_lightingEngine = new CellularLighting<Block>(Blocks, BlockIndexByWorldPosition, Chunk.SizeInBlocks, GetChunkByWorldPosition); m_vertexBuilder = new VertexBuilder<Block>(Blocks, BlockIndexByWorldPosition, graphicsDevice); m_chunkStorage = new SparseArray3D<Chunk>(CacheRange * 2 + 1, CacheRange * 2 + 1); m_cacheCenterPosition = new Vector4(); m_eventSinkImpl = new EventSinkImpl (); m_eventSinkImpl.AddHandler<Vector3Args>(EventConstants.PlayerPositionUpdated, OnUpdateCachePosition); m_startUpState = StartUpState.NotStarted; m_processingQueue = new Queue<Chunk>(); m_EditQueue = new ConcurrentQueue<WorldEdit>(); #if DEBUG StateStatistics = new Dictionary<ChunkState, int> // init. the debug stastics. { {ChunkState.AwaitingGenerate, 0}, {ChunkState.Generating, 0}, {ChunkState.AwaitingLighting, 0}, {ChunkState.Lighting, 0}, {ChunkState.AwaitingBuild, 0}, {ChunkState.Building, 0}, {ChunkState.Ready, 0}, {ChunkState.AwaitingRemoval, 0}, }; #endif }
public Page(int x, int y, int z, string pageId, Block[] data) { X = x; Y = y; Z = z; PageId = pageId; Data = data; }
public SortedList<Interval, Block> ConvertArrayToIntervalTree(Block[] blocks, out float compressionRatio, CompressionFlag flag = CompressionFlag.None) { var scanDirection = ScanDirection.Xyz; CompressionMode compressionMode; if (flag == CompressionFlag.None) { float hilbertCompressionRatio; float linearCompressionRatio; EvaluateHilbertTree(blocks, out hilbertCompressionRatio); EvaluateLinearTree(blocks, out linearCompressionRatio, out scanDirection); compressionMode = hilbertCompressionRatio > linearCompressionRatio ? CompressionMode.Hilbert : CompressionMode.Linear; } else { switch (flag) { case CompressionFlag.LinearXyz: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Xyz; break; case CompressionFlag.LinearXzy: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Xzy; break; case CompressionFlag.LinearYxz: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Yxz; break; case CompressionFlag.LinearYzx: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Yzx; break; case CompressionFlag.LinearZxy: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Zxy; break; case CompressionFlag.LinearZyx: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Zyx; break; case CompressionFlag.Hilbert: compressionMode = CompressionMode.Hilbert; break; default: throw new ArgumentOutOfRangeException("flag"); } } if (compressionMode == CompressionMode.Hilbert) { //Console.Out.WriteLine(CompressionMode.Hilbert.ToString()); return ConvertArrayToIntervalTreeHilbert(blocks, out compressionRatio); } else { //Console.Out.WriteLine(scanDirection.ToString()); return ConvertArrayToIntervalTreeLinear(blocks, scanDirection, out compressionRatio); } }
//TODO : make a physics thread and put this on it public FluidSimulation(MappingFunction mappingFunction, Block[] blocks) { m_mappingFunction = mappingFunction; m_blocks = blocks; m_containers = new List<FluidContainer>(); m_solidCell = new FluidCell(FluidCell.CellType.Solid); m_addQueue = new Queue<List<FluidCell>>(); m_cellAccumulator = new List<FluidCell>(); }
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 Player(Block[] blocks, MappingFunctionVector3 mappingFunction) { FlyingEnabled = true; m_blocks = blocks; m_mappingFunction = mappingFunction; Equipable = new Shovel(); m_eventSinkImpl = new EventSinkImpl(); m_eventSinkImpl.AddHandler<Vector2Args>(EventConstants.MousePositionUpdated, OnMouseUpdated); m_eventSinkImpl.AddHandler<KeyArgs>(EventConstants.KeyDown, OnKeyDown); m_eventSinkImpl.AddHandler<KeyArgs>(EventConstants.KeyUp, OnKeyUp); m_eventSinkImpl.AddHandler<MouseButtonArgs>(EventConstants.LeftMouseDown, OnLeftMouseDown); m_eventSinkImpl.AddHandler<MouseButtonArgs>(EventConstants.LeftMouseUp, OnLeftMouseUp); m_eventSinkImpl.AddHandler<MouseButtonArgs>(EventConstants.RightMouseDown, OnRightMouseDown); m_eventSinkImpl.AddHandler<MouseButtonArgs>(EventConstants.RightMouseUp, OnRightMouseUp); EventsFired = new[] { EventConstants.PlayerPositionUpdated, EventConstants.ViewUpdated }; m_eventSourceImpl = new EventSource(EventsFired, true); }
public void AddBlockAt(int x, int y, int z, ref Block block) { m_chunkCache.AddBlock(x, y, z, ref block); }
public void GenerateDataForChunk(int chunkX, int chunkY, int chunkZ, int chunkSize, Block[] blocks, MappingFunction mappingFunction) { for (var x = 0; x < chunkSize; ++x) { var cX = chunkX + x; for (var z = 0; z < chunkSize; ++z) { var cZ = chunkZ + z; var groundLevel = GetHeight(cX, cZ); var detailNoise = m_detail2.FractalBrownianMotion(cX, cZ, 64, 0, 8); var overhangStart = m_sealevel + MathHelper.Clamp(detailNoise * 4, -1, 1) * 2; var biome = m_biomeGenerator.GetRegionGenerator(cX, cZ); var province = m_provinceGenerator.GetRegionGenerator(cX, cZ); var data = m_populator.CalculateNearestSamplePosition(cX, cZ); /*var color = (ushort)data.Id;*/ var riverData = m_riverGenerator.GetRiverData(cX, cZ); if (riverData != null) { /* if (groundLevel < riverData.Position.Y) { Console.Out.WriteLine(x + ", " + z + ": " + (riverData.Position.Y - groundLevel)); }*/ groundLevel = riverData.Position.Y; } for (var y = chunkSize - 1; y >= 0 ; --y) { var cY = chunkY + y; Block block; if (cY > groundLevel + 10) { block = new Block(Block.None) {LightSun = CellularLighting<Block>.MaxSun}; } else if (cY > groundLevel) { block = new Block(Block.None) { LightSun = CellularLighting<Block>.MinLight }; } else if (cY >= groundLevel - m_biomeThickness) { if (cY > overhangStart) { var density = (MathHelper.Clamp(m_volume.FractalBrownianMotion(cX, cY, cZ, 64, 0, 4)*3, -1, 1) + 1)*0.5f; block = density > 0.125 ? biome.Apply((int) groundLevel, cX, cY, cZ) : Block.Empty; } else { block = biome.Apply((int) groundLevel, cX, cY, cZ); } } else { block = province.Apply((int)groundLevel - m_biomeThickness, cX, cY, cZ); } // var tint = (int) ((detailNoise * 5 + 10)); // block.Color = (ushort)((tint << 8) | (tint << 4) | tint); if (riverData != null && cY <= groundLevel && cY >= groundLevel - 1) { block = new Block(BlockDictionary.Instance.GetBlockIdForName("Water")); } blocks[mappingFunction(cX, cY, cZ)] = block; } for (var y = 0; y < chunkSize && chunkY + y < m_sealevel; ++y) { var cY = chunkY + y; var blockIndex = mappingFunction(cX, cY, cZ); if (!blocks[blockIndex].Exists) blocks[blockIndex] = new Block(BlockDictionary.Instance.GetBlockIdForName("Water")); } //TODO : redesign populator to be compatible with page loading /* if (Math.Abs(data.Delta.X - cX) < 0.01 && Math.Abs(data.Delta.Y - cZ) < 0.01) { m_populator.PopulateTree(cX, cZ, (int)groundLevel, blocks, mappingFunction); }*/ } } //TODO : query if chunk has all neighbors loaded so we can run our erosion step //TODO : after eroding generate structures }
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 Page DecompressPage(string pageId) { var buffer = CheckOutBuffer(); Page page; using (var fileStream = new FileStream(Path.Combine(m_dataDirectory, pageId + ".page"), FileMode.Open)) { using(var decompressor = new GZipStream(fileStream, CompressionMode.Decompress)) { decompressor.Read(buffer, 0, BufferSize); var pageX = GetInt(buffer, 0); var pageY = GetInt(buffer, 4); var pageZ = GetInt(buffer, 8); page = new Page(pageX, pageY, pageZ, pageId); for (var curveIndex = 0; curveIndex < BlockCount; ++curveIndex) { var offset = HeaderSize + curveIndex * 2; var block = new Block(GetShort(buffer, offset)); offset = HeaderSize + BlockCount * 2 + curveIndex; block.LightSun = buffer[offset]; offset = HeaderSize + BlockCount * 3 + curveIndex; block.LightRed = buffer[offset]; offset = HeaderSize + BlockCount * 4 + curveIndex; block.LightGreen = buffer[offset]; offset = HeaderSize + BlockCount * 5 + curveIndex; block.LightBlue = buffer[offset]; offset = HeaderSize + BlockCount * 6 + curveIndex * 2; block.Color = GetShort(buffer, offset); page.Data[m_hilbertCurve[curveIndex]] = block; } } } CheckInBuffer(buffer); return page; }
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 void EvaluateHilbertTree(Block[] blocks, out float compressionRatio) { var nodesRemoved = m_hilbertCurve.Length; var current = Block.Empty; foreach (var index in m_hilbertCurve) { var block = blocks[index]; if (block == current) { continue; } current = block; --nodesRemoved; } compressionRatio = nodesRemoved / (float)m_hilbertCurve.Length; }
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 SortedList<Interval, Block> ConvertArrayToIntervalTreeHilbert(Block[] blocks, out float compressionRatio) { var nodesRemoved = m_hilbertCurve.Length; var min = 0; var max = 0; var current = Block.Empty; var started = false; var intervals = new SortedList<Interval, Block>(); for (var i = 0; i < m_hilbertCurve.Length; ++i) { var block = blocks[m_hilbertCurve[i]]; 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)m_hilbertCurve.Length; return intervals; }
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); }); } }
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(Block other) { return m_type == other.m_type && m_color == other.m_color && m_lightSun == other.m_lightSun && m_lightRed == other.m_lightRed && m_lightGreen == other.m_lightGreen && m_lightBlue == other.m_lightBlue; }
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 void AddBlock(int x, int y, int z, ref Block block) { var chunk = GetChunkByWorldPosition(x, y, z); // get the chunk that block is hosted in. if (chunk == null) { return; } var flattenIndex = BlockIndexByWorldPosition(x, y, z); Blocks[flattenIndex] = block; chunk.AddBlock(x, y, z); m_lightingEngine.AddBlock(x, y, z, block); }