/*
* Copyright 2016 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.world.propagation.light;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.math.Side;
import org.terasology.math.geom.Vector3i;
import org.terasology.world.chunks.Chunk;
import org.terasology.world.chunks.LitChunk;
import org.terasology.world.chunks.internal.GeneratingChunkProvider;
import org.terasology.world.propagation.BatchPropagator;
import org.terasology.world.propagation.LocalChunkView;
import org.terasology.world.propagation.PropagationRules;
import org.terasology.world.propagation.PropagatorWorldView;
import org.terasology.world.propagation.StandardBatchPropagator;
import org.terasology.world.propagation.SunlightRegenBatchPropagator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class LightMerger<T> {
private static final int CENTER_INDEX = 13;
private static final Logger logger = LoggerFactory.getLogger(LightMerger.class);
private ExecutorService executorService = Executors.newSingleThreadExecutor();
private Future<T> resultFuture;
private GeneratingChunkProvider chunkProvider;
private LightPropagationRules lightRules = new LightPropagationRules();
private SunlightRegenPropagationRules sunlightRegenRules = new SunlightRegenPropagationRules();
private boolean running = true;
public LightMerger(GeneratingChunkProvider chunkProvider) {
this.chunkProvider = chunkProvider;
}
public void beginMerge(final Chunk chunk, final T data) {
resultFuture = executorService.submit(() -> {
merge(chunk);
return data;
});
}
public T completeMerge() {
if (resultFuture != null) {
try {
T result = resultFuture.get();
resultFuture = null;
return result;
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException("Error completing lighting merge", e);
}
}
return null;
}
private void merge(Chunk chunk) {
Chunk[] localChunks = assembleLocalChunks(chunk);
localChunks[CENTER_INDEX] = chunk;
List<BatchPropagator> propagators = Lists.newArrayList();
propagators.add(new StandardBatchPropagator(new LightPropagationRules(), new LocalChunkView(localChunks, lightRules)));
PropagatorWorldView regenWorldView = new LocalChunkView(localChunks, sunlightRegenRules);
PropagationRules sunlightRules = new SunlightPropagationRules(regenWorldView);
PropagatorWorldView sunlightWorldView = new LocalChunkView(localChunks, sunlightRules);
BatchPropagator sunlightPropagator = new StandardBatchPropagator(sunlightRules, sunlightWorldView);
propagators.add(new SunlightRegenBatchPropagator(sunlightRegenRules, regenWorldView, sunlightPropagator, sunlightWorldView));
propagators.add(sunlightPropagator);
for (BatchPropagator propagator : propagators) {
// Propagate Inwards
for (Side side : Side.values()) {
Vector3i adjChunkPos = side.getAdjacentPos(chunk.getPosition());
LitChunk adjChunk = chunkProvider.getChunkUnready(adjChunkPos);
if (adjChunk != null) {
propagator.propagateBetween(adjChunk, chunk, side.reverse(), false);
}
}
// Propagate Outwards
for (Side side : Side.values()) {
Vector3i adjChunkPos = side.getAdjacentPos(chunk.getPosition());
LitChunk adjChunk = chunkProvider.getChunk(adjChunkPos);
if (adjChunk != null) {
propagator.propagateBetween(chunk, adjChunk, side, true);
}
}
}
for (BatchPropagator propagator : propagators) {
propagator.process();
}
chunk.deflateSunlight();
}
private Chunk[] assembleLocalChunks(Chunk chunk) {
Chunk[] localChunks = new Chunk[27];
int index = 0;
for (int z = -1; z < 2; ++z) {
for (int y = -1; y < 2; ++y) {
for (int x = -1; x < 2; ++x) {
Chunk localChunk = chunkProvider.getChunk(chunk.getPosition().x + x, chunk.getPosition().y + y, chunk.getPosition().z + z);
if (localChunk != null) {
localChunks[index] = localChunk;
}
index++;
}
}
}
return localChunks;
}
public void shutdown() {
running = false;
executorService.shutdown();
try {
executorService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.error("Failed to shutdown light merge thread in a timely manner");
}
}
public void restart() {
if (!running) {
executorService = Executors.newSingleThreadExecutor();
running = true;
}
}
}