Example #1
0
        /// <summary>
        /// When finished, alert the neighbors too
        /// </summary>
        /// <param name="job"></param>
        public override void onJobComplete(IAdjustmentJob job)
        {
            /// since neighbors may need this chunk to load, check if this one loading makes them ready to mesh:
            /// TODO: check if ForEachDirtiedNeighbor is the right set of neighbors, we may be able to use less
            if (job.adjustment.type == FocusAdjustmentType.InFocus &&
                lens.tryToGetAperture(resolution + 1, out IChunkResolutionAperture nextApetureInLine)
                )
            {
                MarchingTetsMeshGenerator.ForEachDirtiedNeighbor(job.adjustment.chunkID, lens.level, chunk => {
                    if (chunk.currentResolution == Chunk.Resolution.Loaded)
                    {
#if DEBUG
                        lens.level.getChunk(chunk.id).recordEvent($"Attempting to get apeture job for {(Chunk.Resolution.Meshed, job.adjustment.type)} from neighbor");
#endif
                        if (nextApetureInLine.tryToGetAdjustmentJobHandle(
                                new Adjustment(
                                    chunk.id,
                                    job.adjustment.type,
                                    job.adjustment.resolution + 1,
                                    job.adjustment.focusID
                                    ),
                                out ApetureJobHandle jobHandle
                                ))
                        {
                            jobHandle.schedule();
#if DEBUG
                            lens.incrementRunningJobCount(job.adjustment.resolution + 1);
#endif
                        }
                    }
                });
Example #2
0
        protected override bool isValidAndReady(Adjustment adjustment, Chunk chunk)
        {
            switch (adjustment.type)
            {
            /// for dirty chunks we can just go for it
            case FocusAdjustmentType.Dirty:
                return(true);

            case FocusAdjustmentType.InFocus:
                /// we can only mesh a newly in focus chunk if it's at the loaded resolution.
                if (chunk.currentResolution == Chunk.Resolution.Loaded)
                {
                    bool necessaryNeighborsAreLoaded = true;
                    bool necessaryNeighborsAreEmpty  = chunk.isEmpty;
                    bool blockingNeighborsAreSolid   = chunk.isSolid;
                    MarchingTetsMeshGenerator.ForEachRequiredNeighbor(adjustment.chunkID, lens.level, (neighborID, neighborChunk) => {
                        bool neighborIsWithinLevelBounds = neighborID.isWithin(Coordinate.Zero, lens.level.chunkBounds);
                        // check if they're loaded. out of bounds chunks will never be loaded and can be skipped
                        if (neighborIsWithinLevelBounds && neighborChunk.currentResolution < Chunk.Resolution.Loaded)
                        {
                            necessaryNeighborsAreLoaded = false;
                            return(false);
                        }

                        // out of bounds chunks will never be loaded and will always be empty, we only need to check in bounds chunks for empty
                        if (neighborIsWithinLevelBounds && !neighborChunk.isEmpty)
                        {
                            necessaryNeighborsAreEmpty = false;
                        }

                        // only in bounds chunks can be solid, so if it's out of bounds or not solid, we mark it so.
                        if (!neighborIsWithinLevelBounds || !neighborChunk.isSolid)
                        {
                            blockingNeighborsAreSolid = false;
                        }

                        return(true);
                    });

                    if (!necessaryNeighborsAreLoaded)
                    {
#if DEBUG
                        chunk.recordEvent($"Chunk is not ready for MeshGenerationAperture job, Necessary neighbors not loaded yet.");
#endif
                        return(false);
                    }

                    /// we don't need to load the mesh if it and it's neighbors are all solid or empty
                    if (blockingNeighborsAreSolid || necessaryNeighborsAreEmpty)
                    {
                        /// set mesh as empty
                        if (chunk.adjustmentLockType == (Chunk.Resolution.Meshed, FocusAdjustmentType.InFocus))
                        {
#if DEBUG
                            chunk.recordEvent($"Chunk can skip MeshGenerationAperture queue, {(blockingNeighborsAreSolid ? "solid chunk is hidden" : "required neighbors are all empty")}. Setting empty mesh");
#endif
                            chunk.setMesh(default);
                            return(false);
                        }
Example #3
0
 /// <summary>
 /// Capture notifications about dirtied chunks
 /// </summary>
 /// <param name="event"></param>
 public void notifyOf(IEvent @event)
 {
     if (@event is Level.ChunkDirtiedEvent cde)
     {
         foreach (ChunkResolutionAperture aperture in apeturesByPriority)
         {
             if (aperture.resolution == Chunk.Resolution.Meshed && aperture.isWithinManagedBounds(cde.chunkID))
             {
                 // throw in jobs to updat the neighbors
                 // TODO: are these the right neighbors?
                 MarchingTetsMeshGenerator.ForEachDirtiedNeighbor(
                     cde.chunkID,
                     level,
                     neighboringChunk => aperture.updateDirtyChunk(neighboringChunk.id, focus)
                     );
                 // then throw in the current chunk, so it's at position 0
                 aperture.updateDirtyChunk(cde.chunkID, focus);
             }
         }
     }
 }
Example #4
0
        void setUpTestChunk()
        {
            // Set up one chunk to load and mesh
            Level storage = new Level((1, 1, 1), null);

            Chunk.ID chunkID = new Chunk.ID(0, 0, 0);
            World.setActiveLevel(storage);
            levelManager.initializeFor(World.Current.activeLevel);
            World.EventSystem.subscribe(
                levelManager,
                WorldEventSystem.Channels.ChunkActivationUpdates
                );

            // run the load job syncly
            BiomeMap.GenerateChunkDataFromSourceJob terrainGenJob = BiomeMap.GetTerrainGenerationJob(chunkID, storage);
            terrainGenJob.Execute();

            // get the data from the load job
            Chunk newlyLoadedChunk = new Chunk();

            if (terrainGenJob.solidVoxelCount[0] > 0)
            {
                newlyLoadedChunk.setVoxels(
                    terrainGenJob.outVoxels,
                    terrainGenJob.solidVoxelCount[0]
                    );
            }
            terrainGenJob.outVoxels.Dispose();

            // add the loaded chunk to storage
            storage.chunks.Add(chunkID, newlyLoadedChunk);
            newlyLoadedChunk.isLoaded = true;

            // get the mesh gen job and run it syncly
            MarchingTetsMeshGenerator.MarchingTetsMeshGenJob meshGenJob = MarchingTetsMeshGenerator.GetJob(MarchingTetsMeshGenerator.GetVoxelsToMarchOver(chunkID, storage));
            meshGenJob.Execute();

            // set up the mesh data
            bool          meshIsEmpty   = meshGenJob.outVerticies.Length <= 0;
            VoxelMeshData chunkMeshData = new VoxelMeshData(
                chunkID,
                meshIsEmpty,
                meshGenJob.outVerticies,
                meshGenJob.outTriangles,
                meshGenJob.outColors
                );

            // dispose of the allocated resources
            meshGenJob.outVerticies.Dispose();
            meshGenJob.outTriangles.Dispose();
            meshGenJob.outColors.Dispose();

            // update the chunk data to say if it's meshed
            if (storage.chunks.TryGetValue(chunkID, out Chunk updatedChunk))
            {
                updatedChunk.meshIsGenerated = true;
                updatedChunk.meshIsEmpty     = meshIsEmpty;
            }

            /// notify the chunk manager
            World.EventSystem.notifyChannelOf(
                new MeshGenerationAperture.ChunkMeshLoadingFinishedEvent(chunkMeshData),
                WorldEventSystem.Channels.ChunkActivationUpdates
                );

            // set the chunk active
            ActiveChunkObjectAperture.ActivateChunkObjectJob activateChunkJob = new ActiveChunkObjectAperture.ActivateChunkObjectJob(chunkID);
            activateChunkJob.Execute();
        }