public static void LoadFromJsonData(VoxelWorld world, VoxelJsonData data, bool clearTemp = true) { if (clearTemp) { ClearTemp(); } Dictionary <int, string> palette = new Dictionary <int, string>(); for (int i = 0; i < data.palette.Length; i++) { palette.Add(data.palette[i].index, data.palette[i].id); } for (int i = 0; i < data.chunks.Length; i++) { int3 chunkPosition = new int3(data.chunks[i].position.x, data.chunks[i].position.y, data.chunks[i].position.z); NativeList <int2> blocks = new NativeList <int2>(Allocator.Temp); for (int j = 0; j < data.chunks[i].blocks.Length; j++) { blocks.Add(new int2(data.chunks[i].blocks[j].x, data.chunks[i].blocks[j].y)); } using (BinaryWriter w = new BinaryWriter(File.Open(SaveFile(chunkPosition, true), FileMode.OpenOrCreate))) { WriteChunkInfo(w, chunkPosition, palette, blocks); } } world.RefreshWorld(); }
private static void DumpLoadedChunksToTemp(VoxelWorld world) { ICollection <Chunk> loadedChunks = world.Chunks; foreach (Chunk chunk in loadedChunks) { if (chunk.changed) { SaveChunk(chunk, true); } } }
/// <summary> /// Sets multiple blocks in the world. /// </summary> /// <param name="from">From world position.</param> /// <param name="to">To world position.</param> /// <param name="block">The block you want to set.</param> /// <param name="world">The world you want to get the block from. If null, the current active world will be used.</param> public static void SetBlocks(int3 from, int3 to, Block block, VoxelWorld world = null) { if (world == null) { world = VoxelWorld.Main; if (world == null) { Debug.LogError("There's no world to set blocks in."); return; } } world.SetBlocks(from, to, block); }
public static void SaveAllChunksToLocation(VoxelWorld world, string location) { List <Chunk> chunks = LoadAllChunks(world, true); string originalPath = SaveLocation; SaveLocation = location; for (int i = 0; i < chunks.Count; i++) { SaveChunk(chunks[i], false); chunks[i].Dispose(); } SaveLocation = originalPath; }
/// <summary> /// Set a block in the world but it does NOT updates the terrain. /// </summary> /// <param name="position">The world position.</param> /// <param name="block">The block you want to set.</param> /// <param name="world">The world you want to get the block from. If null, the current active world will be used.</param> public static void SetBlockRaw(int3 position, Block block, VoxelWorld world = null) { if (world == null) { world = VoxelWorld.Main; if (world == null) { Debug.LogError("There's no world to set a block in."); return; } } world.SetBlockRaw(position, block); }
/// <summary> /// Gets a block from the world. /// </summary> /// <param name="position">The world position.</param> /// <param name="world">The world you want to get the block from. If null, the current active world will be used.</param> /// <returns>The block at the given position.</returns> public static Block GetBlock(int3 position, VoxelWorld world = null) { if (world == null) { world = VoxelWorld.Main; if (world == null) { Debug.LogError("There's no world to get a block from."); return(BlockProvider.GetBlock(BlockProvider.AIR_TYPE)); } } return(world.GetBlock(position)); }
private static List <Chunk> LoadAllChunks(VoxelWorld world, bool ignoreEmptyChunks) { DumpLoadedChunksToTemp(world); List <Chunk> chunks = new List <Chunk>(); string[] tempChunks = Directory.GetFiles(TempSaveLocation, "*.bin"); if (tempChunks != null && tempChunks.Length > 0) { for (int i = 0; i < tempChunks.Length; i++) { Chunk chunk = new Chunk(world, int3.zero, new ChunkBlocks(Chunk.CHUNK_SIZE)); DeserializeChunk(chunk, tempChunks[i]); if (ignoreEmptyChunks) { NativeArray <int> blocks = chunk.GetAllBlocks(Allocator.Temp); bool empty = true; for (int b = 0; b < blocks.Length; b++) { if (blocks[b] != BlockProvider.AIR_TYPE_ID) { empty = false; break; } } blocks.Dispose(); if (empty) { // Need to dispose the chunk here to avoid memory leak. chunk.Dispose(); continue; } } chunks.Add(chunk); } } return(chunks); }
private void OnEnable() { if (Main != null) { if (!dontDestroyOnLoad && Main != this) { Debug.LogError("Multiple Voxel Worlds! There can only be one.", gameObject); } else { Destroy(gameObject); } return; } if (Main == null) { Main = this; } }
public static VoxelJsonData GetJsonData(VoxelWorld world, bool ignoreEmptyChunks = false) { VoxelJsonData data = new VoxelJsonData(BlockProvider.GetBlockPalette()); List <Chunk> chunks = LoadAllChunks(world, ignoreEmptyChunks); VoxelJsonChunkData[] chunkData = new VoxelJsonChunkData[chunks.Count]; for (int i = 0; i < chunks.Count; i++) { chunkData[i] = new VoxelJsonChunkData(chunks[i]); } data.chunks = chunkData; for (int i = 0; i < chunks.Count; i++) { chunks[i].Dispose(true); } return(data); }
public Chunk(VoxelWorld world, int3 position) { this.world = world; this.position = position; }
public static void LoadAllJson(VoxelWorld world, string json, bool clearTemp = true) { LoadFromJsonData(world, JsonUtility.FromJson <VoxelJsonData>(json), clearTemp); }
//public static void SaveAllJson(VoxelWorld world, string saveLocation = null) //{ // VoxelJsonData data = GetJsonData(world); // string json = JsonUtility.ToJson(data, false); // Debug.Log(json); //} public static string ToJson(VoxelWorld world, bool ignoreEmptyChunks = false, bool prettyPrint = false) { VoxelJsonData data = GetJsonData(world, ignoreEmptyChunks); return(ToJson(data, prettyPrint)); }
private static void ResetStatics() { Main = null; }
/// <summary> /// Set a block in the world but it does NOT updates the terrain. /// </summary> /// <param name="x">The X position.</param> /// <param name="y">The Y position.</param> /// <param name="z">The Z position.</param> /// <param name="block">The block you want to set.</param> /// <param name="world">The world you want to get the block from. If null, the current active world will be used.</param> public static void SetBlockRaw(int x, int y, int z, Block block, VoxelWorld world = null) { SetBlockRaw(new int3(x, y, z), block, world); }
/// <summary> /// Set a block in the world and updates the terrain. /// </summary> /// <param name="x">The X position.</param> /// <param name="y">The Y position.</param> /// <param name="z">The Z position.</param> /// <param name="block">The block you want to set.</param> /// <param name="urgent">If true, the chunk will update as soon as possible.</param> /// <param name="world">The world you want to get the block from. If null, the current active world will be used.</param> public static void SetBlock(int x, int y, int z, Block block, bool urgent = true, VoxelWorld world = null) { SetBlock(new int3(x, y, z), block, urgent, world); }
/// <summary> /// Casts a ray into the world. /// </summary> /// <param name="ray">The ray to cast from.</param> /// <param name="hit">The output hit.</param> /// <param name="range">How far the ray reaches.</param> /// <param name="stopAtZero">Stop if the ray goes below Y position 0.</param> /// <param name="world">The world to cast the ray in.</param> /// <returns>True if it hit something.</returns> public static bool Raycast(Ray ray, out VoxelRaycastHit hit, float range, bool stopAtZero, VoxelWorld world = null) { if (world == null) { world = VoxelWorld.Main; if (world == null) { throw new System.NullReferenceException("There's no world to raycast in."); } } // Position as we work through the raycast, starts at origin and gets updated as it reaches each block boundary on the route Vector3 pos = ray.origin; //Normalized direction of the ray Vector3 dir = ray.direction.normalized; //Transform the ray to match the rotation and position of the world: pos -= world.transform.position; pos -= new Vector3(0.5f, 0.5f, 0.5f); pos = Quaternion.Inverse(world.gameObject.transform.rotation) * pos; dir = Quaternion.Inverse(world.transform.rotation) * dir; // BlockPos to check if the block should be returned int3 bPos = new int3(Mathf.RoundToInt(pos.x), Mathf.RoundToInt(pos.y), Mathf.RoundToInt(pos.z)); //Block pos that gets set to one block behind the hit block, useful for placing blocks at the hit location int3 adjacentBPos = bPos; // Positive copy of the direction Vector3 dirP = new Vector3(Math.Abs(dir.x), Math.Abs(dir.y), Math.Abs(dir.z)); // The sign of the direction int3 dirS = new int3(dir.x > 0 ? 1 : -1, dir.y > 0 ? 1 : -1, dir.z > 0 ? 1 : -1); // Boundary will be set each step as the nearest block boundary to each direction Vector3 boundary; // dist will be set to the distance in each direction to hit a boundary Vector3 dist; //The block at bPos Block hitBlock = world.GetBlock(bPos); bool hitSomething = false; while (hitBlock.id == 0 && math.distance(ray.origin, pos) < range) { // Get the nearest upcoming boundary for each direction boundary.x = MakeBoundary(dirS.x, pos.x); boundary.y = MakeBoundary(dirS.y, pos.y); boundary.z = MakeBoundary(dirS.z, pos.z); //Find the distance to each boundary and make the number positive dist = boundary - pos; dist = new Vector3(Math.Abs(dist.x), Math.Abs(dist.y), Math.Abs(dist.z)); // Divide the distance by the strength of the corresponding direction, the // lowest number will be the boundary we will hit first. This is like distance // over speed = time where dirP is the speed and the it's time to reach the boundary dist.x /= dirP.x; dist.y /= dirP.y; dist.z /= dirP.z; // Use the shortest distance as the distance to travel this step times each direction // to give us the position where the ray intersects the closest boundary if (dist.x < dist.y && dist.x < dist.z) { pos += dist.x * dir; } else if (dist.y < dist.z) { pos += dist.y * dir; } else { pos += dist.z * dir; } // Set the block pos but use ResolveBlockPos because one of the components of pos will be exactly on a block boundary // and will need to use the corresponding direction sign to decide which side of the boundary to fall on adjacentBPos = bPos; bPos = new int3(ResolveBlockPos(pos.x, dirS.x), ResolveBlockPos(pos.y, dirS.y), ResolveBlockPos(pos.z, dirS.z)); hitBlock = world.GetBlock(bPos); if (hitBlock.id != 0) { hitSomething = true; } if (bPos.y <= 0 && stopAtZero) { break; } // The while loop then evaluates if hitblock is a viable block to stop on and // if not does it all again starting from the new position } if (!hitSomething && stopAtZero) { bPos.y = 0; adjacentBPos.y = 0; } hit = new VoxelRaycastHit() { block = hitBlock, blockPosition = bPos, adjacentPosition = adjacentBPos, direction = dir, scenePosition = pos }; return(hitSomething); }
/// <summary> /// Gets a block from the world. /// </summary> /// <param name="x">The X position.</param> /// <param name="y">The Y position.</param> /// <param name="z">The Z position.</param> /// <param name="world">The world you want to get the block from. If null, the current active world will be used.</param> /// <returns>The block at the given position.</returns> public static Block GetBlock(int x, int y, int z, VoxelWorld world = null) { return(GetBlock(new int3(x, y, z), world)); }
public Chunk(VoxelWorld world, int3 position, ChunkBlocks blocks) : this(world, position) { this.blocks = blocks; }
/// <summary> /// Sets multiple blocks in the world. /// </summary> /// <param name="fromX">From X position.</param> /// <param name="fromY">From Y position.</param> /// <param name="fromZ">From Z position.</param> /// <param name="toX">To X position.</param> /// <param name="toY">To Y position.</param> /// <param name="toZ">To Z position.</param> /// <param name="block">The block you want to set.</param> /// <param name="world">The world you want to get the block from. If null, the current active world will be used.</param> public static void SetBlocks(int fromX, int fromY, int fromZ, int toX, int toY, int toZ, Block block, VoxelWorld world = null) { SetBlocks(new int3(fromX, fromY, fromZ), new int3(toX, toY, toZ), block, world); }