public void TryRemoveComponents(WorldApi worldApi, List <FluidComponent> components, bool rebuildEnabled) { UnityEngine.Profiling.Profiler.BeginSample("TryRemoveComponents"); for (int i = 0; i < components.Count; i++) { FluidComponent component = components[i]; component.UpdateLifetime(RealtimeSinceStartup); // cleanup components marked for rebuild if (rebuildEnabled && component.ToRebuild) { component.Unsettle(component.Count * component.Viscosity); component.Cleanup(worldApi); } // remove small and old components if (component.ToRemove) { component.Cleanup(worldApi); component.UpdateJob.ToRemove = true; components.RemoveAtViaEndSwap(i--); } } UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary> /// Initializes array of blocks and the neighbour references in blocks and chunks. /// </summary> public void Initialize() { _worldApi = GetComponent <WorldApi>(); // initialize arrays _blocks = new Block[_worldApi.SizeX * _worldApi.SizeY * _worldApi.SizeZ]; _worldApi.Initialize(_blocks); for (int blockX = 0; blockX < _worldApi.SizeX; blockX++) { for (int blockY = 0; blockY < _worldApi.SizeY; blockY++) { for (int blockZ = 0; blockZ < _worldApi.SizeZ; blockZ++) { int blockId = blockX * _worldApi.SizeZ * _worldApi.SizeY + blockY * _worldApi.SizeZ + blockZ; VectorI3 coords = new VectorI3(blockX, blockY, blockZ); Block block = new GameObject($"Block ({blockX}, {blockY}, {blockZ})").AddComponent <Block>(); _blocks[blockId] = block; block.Initialize(blockId, transform, _worldApi, coords); } } } InitializeBlockReferences(); for (int blockId = 0; blockId < _blocks.Length; blockId++) { _blocks[blockId].InitializeChunkReferences(); } }
/// <summary> /// Traverses the 2 top rows of component and validates them. /// Removes/shortens segments containing invalid voxels and adds eligible voxels to outlets. /// </summary> private void UpdateSegments(WorldApi worldApi, Dictionary <Vector2, List <FluidSegment> > allSegments, HashSet <VectorI3> outlets, ref int componentCount, ref float componentWaterLevel) { UnityEngine.Profiling.Profiler.BeginSample("UpdateSegments"); foreach (var segments in allSegments) { Vector2 row = segments.Key; // skip deep y segments if (row.y < componentWaterLevel - 2 * WorldGridInfo.kVoxelSize) { continue; } bool allValid = ValidateSegmentRow(worldApi, in row, segments.Value, outlets, ref componentCount); // grow water level to the highest valid segment row if there are no outlets if (componentWaterLevel < row.y && allValid && outlets?.Count == 0) { componentWaterLevel = row.y; } if (segments.Value.Count == 0) { _helperDictKeysList.Add(row); } } allSegments.RemoveRange(in _helperDictKeysList); _helperDictKeysList.Clear(); UnityEngine.Profiling.Profiler.EndSample(); }
public void CreateData(FluidComponentManager manager, WorldApi worldApi) { _helperIndicesList = new NativeList <VectorI3>(Unity.Collections.Allocator.Persistent); _worldApiPointer = GCHandle.Alloc(worldApi, GCHandleType.Pinned); _managerPointer = GCHandle.Alloc(manager, GCHandleType.Pinned); }
public void Execute() { WorldApi worldApi = (WorldApi)_worldApiPointer.Target; FluidComponentManager manager = (FluidComponentManager)_managerPointer.Target; TryCreateComponents(worldApi, manager); TryRemoveComponents(worldApi, manager.Components, manager.RebuildEnabled); }
public FluidComponentManager(WorldApi worldApi) { _worldApi = worldApi; MaintenanceJob.CreateData(this, _worldApi); Components = new List <FluidComponent>(); VoxelsToProcess = new HashSet <VectorI3>(); _componentsWithJobsRunning = new List <FluidComponent>(); }
public void Initialize(int id, Transform parent, WorldApi worldApi, Block block) { Id = id; transform.parent = parent; Block = block; worldApi.GetChunkWorldPos(block.Id, Id, out Vector3 worldPos); WorldPos = worldPos; RenderData = new ChunkRenderData(this); }
public void CreateData(WorldApi worldApi, FluidComponentManager manager, FluidComponent component) { IsRunning = true; ToRemove = false; _helperIndicesList = new NativeList <VectorI3>(Unity.Collections.Allocator.Persistent); _helperDictKeysList = new NativeList <Vector2>(Unity.Collections.Allocator.Persistent); _componentPointer = GCHandle.Alloc(component, GCHandleType.Pinned); _worldApiPointer = GCHandle.Alloc(worldApi, GCHandleType.Pinned); _managerPointer = GCHandle.Alloc(manager, GCHandleType.Pinned); }
/// <summary> /// Initialize array of voxels and chunks. /// </summary> public void Initialize(int id, Transform parent, WorldApi worldApi, VectorI3 coords) { Id = id; transform.parent = parent; Coords = coords; worldApi.GetBlockWorldPos(id, out Vector3 worldPos); WorldPos = worldPos; Chunks = new Chunk[WorldGridInfo.kTotalChunksInBlock]; _voxels = new NativeArray <Voxel>(WorldGridInfo.kTotalVoxelsInBlock, Unity.Collections.Allocator.Persistent); SimData = new FluidBlockSimData(this); SimData.ReadVoxels = _voxels; for (int chunkX = 0; chunkX < WorldGridInfo.kChunksPerBlock; chunkX++) { for (int chunkY = 0; chunkY < WorldGridInfo.kChunksPerBlock; chunkY++) { for (int chunkZ = 0; chunkZ < WorldGridInfo.kChunksPerBlock; chunkZ++) { int chunkId = chunkX * WorldGridInfo.kChunksPerBlock * WorldGridInfo.kChunksPerBlock + chunkY * WorldGridInfo.kChunksPerBlock + chunkZ; Chunk chunk = new GameObject($"Chunk ({chunkX}, {chunkY}, {chunkZ})").AddComponent <Chunk>(); Chunks[chunkId] = chunk; chunk.Initialize(chunkId, transform, worldApi, this); for (int voxelX = 0; voxelX < WorldGridInfo.kVoxelsPerChunk; voxelX++) { for (int voxelY = 0; voxelY < WorldGridInfo.kVoxelsPerChunk; voxelY++) { for (int voxelZ = 0; voxelZ < WorldGridInfo.kVoxelsPerChunk; voxelZ++) { int voxelId = voxelX * WorldGridInfo.kVoxelsPerChunk * WorldGridInfo.kVoxelsPerChunk + voxelY * WorldGridInfo.kVoxelsPerChunk + voxelZ; _voxels.GetWritable(chunkId, voxelId).Valid = true; } } } } } } }
public void TryCreateComponents(WorldApi worldApi, FluidComponentManager manager) { UnityEngine.Profiling.Profiler.BeginSample("TryCreateComponents"); // first try to add voxel to a nearby existing component because creating a new one is expensive int processed = 0; foreach (VectorI3 indices in manager.VoxelsToProcess) { if (TryAddToExistingComponent(worldApi, manager, in indices)) { _helperIndicesList.Add(indices); } if (processed++ > FluidComponentManager.kMaxVoxelsProcessedPerIteration) { break; } } manager.VoxelsToProcess.RemoveRange(_helperIndicesList); _helperIndicesList.Clear(); // no nearby existing component found if (manager.VoxelsToProcess.Count > FluidComponentManager.kMinComponentSize) { // try to create a single new component foreach (VectorI3 indices in manager.VoxelsToProcess) { if (TryCreateNewComponent(worldApi, manager, in indices)) { manager.VoxelsToProcess.Remove(indices); } break; } } UnityEngine.Profiling.Profiler.EndSample(); }
// TODO speedup /// <summary> /// Validates the existing segments and updates / equalizes the outlets of this component. /// </summary> public void Execute() { UnityEngine.Profiling.Profiler.BeginSample("Update"); FluidComponent component = (FluidComponent)_componentPointer.Target; WorldApi worldApi = (WorldApi)_worldApiPointer.Target; FluidComponentManager manager = (FluidComponentManager)_managerPointer.Target; int diff = component.Count; if (component.Viscosity > FluidComponentManager.kMaxViscosityNotEqualize && component.Outlets == null) { component.Outlets = new HashSet <VectorI3>(); } UpdateSegments(worldApi, component.AllSegments, component.Outlets, ref component.Count, ref component.WaterLevel); diff -= component.Count; if (diff != 0) { component.Unsettle(diff * component.Viscosity); } else { component.DecreaseSettle(); } if (component.Viscosity > FluidComponentManager.kMaxViscosityNotEqualize && component.Outlets.Count > 0) { UpdateOutlets(worldApi, component.Outlets, component.Rebuilding, component.Lifetime, ref component.WaterLevel); EqualizeOutlets(true, worldApi, component.Outlets, component.Viscosity); EqualizeOutlets(false, worldApi, component.Outlets, component.Viscosity); } UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary> /// Try to create a new component out of given voxel indices. /// </summary> private bool TryCreateNewComponent(WorldApi worldApi, FluidComponentManager manager, in VectorI3 indices)
/// <summary> /// Traverses each segment in the row voxel by voxel and adds outlets from valid ones or removes the invalid ones. /// </summary> private bool ValidateSegmentRow(WorldApi worldApi, in Vector2 row, List <FluidSegment> segments, HashSet <VectorI3> outlets, ref int componentCount)