Begin working on improved lighting to better match 1.14's "new" light… (#611)

* Begin working on improved lighting to better match 1.14's "new" lighting engine
I guess the "new" lighting engine is somewhat old now lol
Also implement the remove first stuff to fixlight

* And then make it work
This commit is contained in:
dordsor21 2020-09-13 15:36:36 +01:00 committed by GitHub
parent d00899e177
commit 4243e8e86b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 574 additions and 95 deletions

View File

@ -381,8 +381,12 @@ public class FaweAPI {
} }
} }
if (mode != RelightMode.NONE) { if (mode != RelightMode.NONE) {
relighter.fixSkyLighting(); if (Settings.IMP.LIGHTING.REMOVE_FIRST) {
relighter.fixBlockLighting(); relighter.removeAndRelight(true);
} else {
relighter.fixSkyLighting();
relighter.fixBlockLighting();
}
} else { } else {
relighter.removeLighting(); relighter.removeLighting();
} }

View File

@ -9,6 +9,13 @@ import com.boydti.fawe.object.collection.BlockVectorSet;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.math.MutableBlockVector3;
import com.sk89q.worldedit.registry.state.DirectionalProperty;
import com.sk89q.worldedit.registry.state.EnumProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -25,22 +32,29 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
public class NMSRelighter implements Relighter { public class NMSRelighter implements Relighter {
private final IQueueExtent<IQueueChunk> queue; private static final int DISPATCH_SIZE = 64;
private static final DirectionalProperty stairDirection;
private static final EnumProperty stairHalf;
private static final EnumProperty stairShape;
private static final EnumProperty slabHalf;
static {
stairDirection = (DirectionalProperty) (Property<?>) BlockTypes.SANDSTONE_STAIRS.getProperty("facing");
stairHalf = (EnumProperty) (Property<?>) BlockTypes.SANDSTONE_STAIRS.getProperty("half");
stairShape = (EnumProperty) (Property<?>) BlockTypes.SANDSTONE_STAIRS.getProperty("shape");
slabHalf = (EnumProperty) (Property<?>) BlockTypes.SANDSTONE_SLAB.getProperty("type");
}
public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0);
private final IQueueExtent<IQueueChunk> queue;
private final Map<Long, RelightSkyEntry> skyToRelight; private final Map<Long, RelightSkyEntry> skyToRelight;
private final Object present = new Object(); private final Object present = new Object();
private final Map<Long, Integer> chunksToSend; private final Map<Long, Integer> chunksToSend;
private final ConcurrentLinkedQueue<RelightSkyEntry> extentdSkyToRelight = new ConcurrentLinkedQueue<>(); private final ConcurrentLinkedQueue<RelightSkyEntry> extentdSkyToRelight = new ConcurrentLinkedQueue<>();
private final Map<Long, long[][][] /* z y x */> lightQueue;
private final Map<Long, long[][][] /* z y x */ > lightQueue;
private final AtomicBoolean lightLock = new AtomicBoolean(false); private final AtomicBoolean lightLock = new AtomicBoolean(false);
private final ConcurrentHashMap<Long, long[][][]> concurrentLightQueue; private final ConcurrentHashMap<Long, long[][][]> concurrentLightQueue;
private final int maxY; private final int maxY;
public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0);
private static final int DISPATCH_SIZE = 64;
private boolean removeFirst; private boolean removeFirst;
public NMSRelighter(IQueueExtent<IQueueChunk> queue) { public NMSRelighter(IQueueExtent<IQueueChunk> queue) {
@ -52,13 +66,11 @@ public class NMSRelighter implements Relighter {
this.maxY = queue.getMaxY(); this.maxY = queue.getMaxY();
} }
@Override @Override public boolean isEmpty() {
public boolean isEmpty() {
return skyToRelight.isEmpty() && lightQueue.isEmpty() && extentdSkyToRelight.isEmpty() && concurrentLightQueue.isEmpty(); return skyToRelight.isEmpty() && lightQueue.isEmpty() && extentdSkyToRelight.isEmpty() && concurrentLightQueue.isEmpty();
} }
@Override @Override public synchronized void removeAndRelight(boolean sky) {
public synchronized void removeAndRelight(boolean sky) {
removeFirst = true; removeFirst = true;
fixLightingSafe(sky); fixLightingSafe(sky);
removeFirst = false; removeFirst = false;
@ -67,9 +79,9 @@ public class NMSRelighter implements Relighter {
/** /**
* Utility method to reduce duplicated code to ensure values are written to long[][][] without NPEs * Utility method to reduce duplicated code to ensure values are written to long[][][] without NPEs
* *
* @param x x coordinate * @param x x coordinate
* @param y y coordinate * @param y y coordinate
* @param z z coordinate * @param z z coordinate
* @param map long[][][] to add values to * @param map long[][][] to add values to
*/ */
private void set(int x, int y, int z, long[][][] map) { private void set(int x, int y, int z, long[][][] map) {
@ -147,7 +159,7 @@ public class NMSRelighter implements Relighter {
long pair = entry.getKey(); long pair = entry.getKey();
Integer existing = chunksToSend.get(pair); Integer existing = chunksToSend.get(pair);
chunksToSend.put(pair, chunk.bitmask | (existing != null ? existing : 0)); chunksToSend.put(pair, chunk.bitmask | (existing != null ? existing : 0));
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z); ChunkHolder<?> iChunk = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x, chunk.z);
if (!iChunk.isInit()) { if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z); iChunk.init(queue, chunk.x, chunk.z);
} }
@ -168,6 +180,9 @@ public class NMSRelighter implements Relighter {
Map<MutableBlockVector3, Object> visited = new HashMap<>(32); Map<MutableBlockVector3, Object> visited = new HashMap<>(32);
Map<MutableBlockVector3, Object> removalVisited = new HashMap<>(32); Map<MutableBlockVector3, Object> removalVisited = new HashMap<>(32);
// Make sure BlockTypes is initialised so we can check block characteristics later if needed
BlockTypes.STONE.getMaterial();
Iterator<Map.Entry<Long, long[][][]>> iter = map.entrySet().iterator(); Iterator<Map.Entry<Long, long[][][]>> iter = map.entrySet().iterator();
while (iter.hasNext() && size-- > 0) { while (iter.hasNext() && size-- > 0) {
Map.Entry<Long, long[][][]> entry = iter.next(); Map.Entry<Long, long[][][]> entry = iter.next();
@ -177,16 +192,20 @@ public class NMSRelighter implements Relighter {
int chunkZ = MathMan.unpairIntY(index); int chunkZ = MathMan.unpairIntY(index);
int bx = chunkX << 4; int bx = chunkX << 4;
int bz = chunkZ << 4; int bz = chunkZ << 4;
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunkX, chunkZ); ChunkHolder<?> iChunk = (ChunkHolder<?>) queue.getOrCreateChunk(chunkX, chunkZ);
if (!iChunk.isInit()) { if (!iChunk.isInit()) {
iChunk.init(queue, chunkX, chunkZ); iChunk.init(queue, chunkX, chunkZ);
} }
for (int lz = 0; lz < blocks.length; lz++) { for (int lz = 0; lz < blocks.length; lz++) {
long[][] m1 = blocks[lz]; long[][] m1 = blocks[lz];
if (m1 == null) continue; if (m1 == null) {
continue;
}
for (int lx = 0; lx < m1.length; lx++) { for (int lx = 0; lx < m1.length; lx++) {
long[] m2 = m1[lx]; long[] m2 = m1[lx];
if (m2 == null) continue; if (m2 == null) {
continue;
}
for (int i = 0; i < m2.length; i++) { for (int i = 0; i < m2.length; i++) {
int yStart = i << 6; int yStart = i << 6;
long value = m2[i]; long value = m2[i];
@ -203,7 +222,7 @@ public class NMSRelighter implements Relighter {
MutableBlockVector3 node = new MutableBlockVector3(x, y, z); MutableBlockVector3 node = new MutableBlockVector3(x, y, z);
if (newLevel < oldLevel) { if (newLevel < oldLevel) {
removalVisited.put(node, present); removalVisited.put(node, present);
lightRemovalQueue.add(new Object[]{node, oldLevel}); lightRemovalQueue.add(new Object[] {node, oldLevel});
} else { } else {
visited.put(node, present); visited.put(node, present);
lightPropagationQueue.add(node); lightPropagationQueue.add(node);
@ -223,43 +242,458 @@ public class NMSRelighter implements Relighter {
MutableBlockVector3 node = (MutableBlockVector3) val[0]; MutableBlockVector3 node = (MutableBlockVector3) val[0];
int lightLevel = (int) val[1]; int lightLevel = (int) val[1];
this.computeRemoveBlockLight(node.getX() - 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); this.computeRemoveBlockLight(node.getX() - 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue,
this.computeRemoveBlockLight(node.getX() + 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); removalVisited, visited);
this.computeRemoveBlockLight(node.getX() + 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue,
removalVisited, visited);
if (node.getY() > 0) { if (node.getY() > 0) {
this.computeRemoveBlockLight(node.getX(), node.getY() - 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); this.computeRemoveBlockLight(node.getX(), node.getY() - 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue,
removalVisited, visited);
} }
if (node.getY() < 255) { if (node.getY() < 255) {
this.computeRemoveBlockLight(node.getX(), node.getY() + 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); this.computeRemoveBlockLight(node.getX(), node.getY() + 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue,
removalVisited, visited);
} }
this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() - 1, lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() - 1, lightLevel, lightRemovalQueue, lightPropagationQueue,
this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() + 1, lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); removalVisited, visited);
this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() + 1, lightLevel, lightRemovalQueue, lightPropagationQueue,
removalVisited, visited);
} }
while (!lightPropagationQueue.isEmpty()) { while (!lightPropagationQueue.isEmpty()) {
MutableBlockVector3 node = lightPropagationQueue.poll(); MutableBlockVector3 node = lightPropagationQueue.poll();
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(node.getX() >> 4, node.getZ() >> 4); ChunkHolder<?> iChunk = (ChunkHolder<?>) queue.getOrCreateChunk(node.getX() >> 4, node.getZ() >> 4);
if (!iChunk.isInit()) { if (!iChunk.isInit()) {
iChunk.init(queue, node.getX() >> 4, node.getZ() >> 4); iChunk.init(queue, node.getX() >> 4, node.getZ() >> 4);
} }
int lightLevel = iChunk.getEmmittedLight(node.getX() & 15, node.getY(), node.getZ() & 15); int lightLevel = iChunk.getEmmittedLight(node.getX() & 15, node.getY(), node.getZ() & 15);
if (lightLevel > 1) { BlockState state = this.queue.getBlock(node.getX(), node.getY(), node.getZ());
this.computeSpreadBlockLight(node.getX() - 1, node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited); String id = state.getBlockType().getId().toLowerCase();
this.computeSpreadBlockLight(node.getX() + 1, node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited); if (lightLevel <= 1) {
if (node.getY() > 0) { continue;
this.computeSpreadBlockLight(node.getX(), node.getY() - 1, node.getZ(), lightLevel, lightPropagationQueue, visited); }
} if (id.contains("slab")) {
if (node.getY() < 255) { boolean top = state.getState(slabHalf).equalsIgnoreCase("top");
this.computeSpreadBlockLight(node.getX(), node.getY() + 1, node.getZ(), lightLevel, lightPropagationQueue, visited); computeSlab(node.getX(), node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited, top);
} } else if (id.contains("stair")) {
this.computeSpreadBlockLight(node.getX(), node.getY(), node.getZ() - 1, lightLevel, lightPropagationQueue, visited); boolean top = state.getState(stairHalf).equalsIgnoreCase("top");
this.computeSpreadBlockLight(node.getX(), node.getY(), node.getZ() + 1, lightLevel, lightPropagationQueue, visited); Direction direction = getStairDir(state);
String shape = getStairShape(state);
computeStair(node.getX(), node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited, top, direction, shape);
} else {
computeNormal(node.getX(), node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited);
} }
} }
} }
private void computeRemoveBlockLight(int x, int y, int z, int currentLight, Queue<Object[]> queue, Queue<MutableBlockVector3> spreadQueue, Map<MutableBlockVector3, Object> visited, private void computeStair(int x,
Map<MutableBlockVector3, Object> spreadVisited) { int y,
ChunkHolder iChunk = (ChunkHolder) this.queue.getOrCreateChunk(x >> 4, z >> 4); int z,
int currentLight,
Queue<MutableBlockVector3> queue,
Map<MutableBlockVector3, Object> visited,
boolean top,
Direction direction,
String shape) {
east:
{
// Block East
if (direction != Direction.WEST && !((direction == Direction.NORTH && !shape.equals("inner_left")) || (direction == Direction.SOUTH
&& !shape.equals("inner_right")) || (direction == Direction.EAST && shape.contains("outer")))) {
break east;
}
BlockState state = this.queue.getBlock(x + 1, y, z);
if (!(checkStairEast(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom"))) {
break east;
}
if (!state.getBlockType().getId().toLowerCase().contains("stair")) {
this.computeSpreadBlockLight(x + 1, y, z, currentLight, queue, visited);
break east;
}
Direction otherDir = getStairDir(state);
String otherShape = getStairShape(state);
boolean b1 =
(otherDir == Direction.NORTH && !otherShape.equals("outer_right")) || (otherDir == Direction.EAST && otherShape.equals("inner_left"));
boolean b2 =
(otherDir == Direction.SOUTH && !otherShape.equals("outer_left")) || (otherDir == Direction.EAST && otherShape.equals("inner_right"));
switch (direction) {
case EAST:
if (shape.equals("outer_right") && b1) {
break east;
} else if (shape.equals("outer_left") && b2) {
break east;
}
break;
case WEST:
if (shape.equals("straight") || shape.contains("outer")) {
break;
} else if (shape.equals("inner_left") && b1) {
break east;
} else if (shape.equals("inner_right") && b2) {
break east;
}
break;
case SOUTH:
if (shape.equals("inner_left") || b1 || (otherDir == Direction.SOUTH && otherShape.equals("inner_right"))) {
break east;
}
break;
case NORTH:
if (shape.equals("inner_right") || b2 || (otherDir == Direction.NORTH && otherShape.equals("inner_left"))) {
break east;
}
break;
}
this.computeSpreadBlockLight(x + 1, y, z, currentLight, queue, visited);
}
west:
{
// Block West
if (direction != Direction.EAST && !((direction == Direction.SOUTH && !shape.equals("inner_left")) || (direction == Direction.NORTH
&& !shape.equals("inner_right")) || (direction == Direction.WEST && shape.contains("outer")))) {
break west;
}
BlockState state = this.queue.getBlock(x - 1, y, z);
if (!(checkStairWest(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom"))) {
break west;
}
if (!state.getBlockType().getId().toLowerCase().contains("stair")) {
this.computeSpreadBlockLight(x - 1, y, z, currentLight, queue, visited);
break west;
}
Direction otherDir = getStairDir(state);
String otherShape = getStairShape(state);
boolean b1 =
(otherDir == Direction.SOUTH && !otherShape.equals("outer_right")) || (otherDir == Direction.WEST && otherShape.equals("inner_left"));
boolean b2 =
(otherDir == Direction.NORTH && !otherShape.equals("outer_left")) || (otherDir == Direction.WEST && otherShape.equals("inner_right"));
switch (direction) {
case WEST:
if (shape.equals("outer_right") && b1) {
break west;
} else if (shape.equals("outer_left") && b2) {
break west;
}
break;
case EAST:
if (shape.equals("straight") || shape.contains("outer")) {
break;
} else if (shape.equals("inner_left") && b1) {
break west;
} else if (shape.equals("inner_right") && b2) {
break west;
}
break;
case NORTH:
if (shape.equals("inner_left") || b1 || (otherDir == Direction.NORTH && otherShape.equals("inner_right"))) {
break west;
}
break;
case SOUTH:
if (shape.equals("inner_right") || b2 || (otherDir == Direction.SOUTH && otherShape.equals("inner_left"))) {
break west;
}
break;
}
this.computeSpreadBlockLight(x - 1, y, z, currentLight, queue, visited);
}
south:
{
// Block South
if (direction != Direction.NORTH && !((direction == Direction.WEST && !shape.equals("inner_left")) || (direction == Direction.EAST
&& !shape.equals("inner_right")) || (direction == Direction.SOUTH && shape.contains("outer")))) {
break south;
}
BlockState state = this.queue.getBlock(x, y, z + 1);
if (!(checkStairSouth(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom"))) {
break south;
}
if (!state.getBlockType().getId().toLowerCase().contains("stair")) {
this.computeSpreadBlockLight(x, y, z + 1, currentLight, queue, visited);
break south;
}
Direction otherDir = getStairDir(state);
String otherShape = getStairShape(state);
boolean b1 =
(otherDir == Direction.EAST && !otherShape.equals("outer_right")) || (otherDir == Direction.SOUTH && otherShape.equals("inner_left"));
boolean b2 =
(otherDir == Direction.WEST && !otherShape.equals("outer_left")) || (otherDir == Direction.SOUTH && otherShape.equals("inner_right"));
switch (direction) {
case SOUTH:
if (shape.equals("outer_right") && b1) {
break south;
} else if (shape.equals("outer_left") && b2) {
break south;
}
break;
case NORTH:
if (shape.equals("straight") || shape.contains("outer")) {
break;
} else if (shape.equals("inner_left") && b1) {
break south;
} else if (shape.equals("inner_right") && b2) {
break south;
}
break;
case WEST:
if (shape.equals("inner_left") || b1 || (otherDir == Direction.WEST && otherShape.equals("inner_right"))) {
break south;
}
break;
case EAST:
if (shape.equals("inner_right") || b2 || (otherDir == Direction.EAST && otherShape.equals("inner_left"))) {
break south;
}
break;
}
this.computeSpreadBlockLight(x, y, z + 1, currentLight, queue, visited);
}
north:
{
// Block North
if (direction != Direction.SOUTH && !((direction == Direction.EAST && !shape.equals("inner_left")) || (direction == Direction.WEST
&& !shape.equals("inner_right")) || (direction == Direction.NORTH && shape.contains("outer")))) {
break north;
}
BlockState state = this.queue.getBlock(x, y, z - 1);
if (!(checkStairNorth(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom"))) {
break north;
}
if (!state.getBlockType().getId().toLowerCase().contains("stair")) {
this.computeSpreadBlockLight(x, y, z - 1, currentLight, queue, visited);
break north;
}
Direction otherDir = getStairDir(state);
String otherShape = getStairShape(state);
boolean b1 =
(otherDir == Direction.WEST && !otherShape.equals("outer_right")) || (otherDir == Direction.NORTH && otherShape.equals("inner_left"));
boolean b2 =
(otherDir == Direction.EAST && !otherShape.equals("outer_left")) || (otherDir == Direction.NORTH && otherShape.equals("inner_right"));
switch (direction) {
case NORTH:
if (shape.equals("outer_right") && b1) {
break north;
} else if (shape.equals("outer_left") && b2) {
break north;
}
break;
case SOUTH:
if (shape.equals("straight") || shape.contains("outer")) {
break;
} else if (shape.equals("inner_left") && b1) {
break north;
} else if (shape.equals("inner_right") && b2) {
break north;
}
break;
case EAST:
if (shape.equals("inner_left") || b1 || (otherDir == Direction.EAST && otherShape.equals("inner_right"))) {
break north;
}
break;
case WEST:
if (shape.equals("inner_right") || b2 || (otherDir == Direction.WEST && otherShape.equals("inner_left"))) {
break north;
}
break;
}
this.computeSpreadBlockLight(x, y, z - 1, currentLight, queue, visited);
}
computeUpDown(x, y, z, currentLight, queue, visited, top);
}
private void computeSlab(int x,
int y,
int z,
int currentLight,
Queue<MutableBlockVector3> queue,
Map<MutableBlockVector3, Object> visited,
boolean top) {
{
// Block East
BlockState state = this.queue.getBlock(x + 1, y, z);
if (checkStairEast(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom")) {
this.computeSpreadBlockLight(x + 1, y, z, currentLight, queue, visited);
}
}
{
// Block West
BlockState state = this.queue.getBlock(x - 1, y, z);
if (checkStairWest(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom")) {
this.computeSpreadBlockLight(x - 1, y, z, currentLight, queue, visited);
}
}
{
// Block South
BlockState state = this.queue.getBlock(x, y, z + 1);
if (checkStairSouth(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom")) {
this.computeSpreadBlockLight(x, y, z + 1, currentLight, queue, visited);
}
}
{
// Block North
BlockState state = this.queue.getBlock(x, y, z - 1);
if (checkStairNorth(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom")) {
this.computeSpreadBlockLight(x, y, z - 1, currentLight, queue, visited);
}
}
computeUpDown(x, y, z, currentLight, queue, visited, top);
}
private void computeUpDown(int x,
int y,
int z,
int currentLight,
Queue<MutableBlockVector3> queue,
Map<MutableBlockVector3, Object> visited,
boolean top) {
BlockState state = this.queue.getBlock(x, y - 1, z);
if (y > 0 && top && isSlabOrTrueValue(state, "bottom") && isStairOrTrueTop(state, false)) {
this.computeSpreadBlockLight(x, y - 1, z, currentLight, queue, visited);
}
state = this.queue.getBlock(x, y + 1, z);
if (y < 255 && !top && isSlabOrTrueValue(state, "top") && isStairOrTrueTop(state, true)) {
this.computeSpreadBlockLight(x, y + 1, z, currentLight, queue, visited);
}
}
private void computeNormal(int x, int y, int z, int currentLight, Queue<MutableBlockVector3> queue, Map<MutableBlockVector3, Object> visited) {
{
// Block East
BlockState state = this.queue.getBlock(x + 1, y, z);
if (checkStairEast(state) && (isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "bottom"))) {
this.computeSpreadBlockLight(x + 1, y, z, currentLight, queue, visited);
}
}
{
// Block West
BlockState state = this.queue.getBlock(x - 1, y, z);
if (checkStairWest(state) && (isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "bottom"))) {
this.computeSpreadBlockLight(x - 1, y, z, currentLight, queue, visited);
}
}
{
// Block South
BlockState state = this.queue.getBlock(x, y, z + 1);
if (checkStairSouth(state) && (isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "bottom"))) {
this.computeSpreadBlockLight(x, y, z + 1, currentLight, queue, visited);
}
}
{
// Block North
BlockState state = this.queue.getBlock(x, y, z - 1);
if (checkStairNorth(state) && (isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "bottom"))) {
this.computeSpreadBlockLight(x, y, z - 1, currentLight, queue, visited);
}
}
BlockState state = this.queue.getBlock(x, y - 1, z);
if (y > 0 && isSlabOrTrueValue(state, "bottom") && isStairOrTrueTop(state, false)) {
this.computeSpreadBlockLight(x, y - 1, z, currentLight, queue, visited);
}
state = this.queue.getBlock(x, y + 1, z);
if (y < 255 && isSlabOrTrueValue(state, "top") && isStairOrTrueTop(state, false)) {
this.computeSpreadBlockLight(x, y + 1, z, currentLight, queue, visited);
}
}
private boolean checkStairNorth(BlockState state) {
if (!state.getBlockType().getId().toLowerCase().contains("stair")) {
return true;
}
Direction direction = getStairDir(state);
String shape = getStairShape(state);
if (shape.contains("outer") || direction == Direction.NORTH) {
return true;
}
if (direction == Direction.SOUTH) {
return false;
}
if (direction == Direction.WEST) {
return !shape.equals("inner_left");
}
return direction != Direction.EAST || !shape.equals("inner_right");
}
private boolean checkStairSouth(BlockState state) {
if (!state.getBlockType().getId().toLowerCase().contains("stair")) {
return true;
}
Direction direction = getStairDir(state);
String shape = getStairShape(state);
if (shape.contains("outer") || direction == Direction.SOUTH) {
return true;
}
if (direction == Direction.NORTH) {
return false;
}
if (direction == Direction.EAST) {
return !shape.equals("inner_left");
}
return direction != Direction.WEST || !shape.equals("inner_right");
}
private boolean checkStairEast(BlockState state) {
if (!state.getBlockType().getId().toLowerCase().contains("stair")) {
return true;
}
Direction direction = getStairDir(state);
String shape = getStairShape(state);
if (shape.contains("outer") || direction == Direction.EAST) {
return true;
}
if (direction == Direction.WEST) {
return false;
}
if (direction == Direction.NORTH) {
return !shape.equals("inner_left");
}
return direction != Direction.SOUTH || !shape.equals("inner_right");
}
private boolean checkStairWest(BlockState state) {
if (!state.getBlockType().getId().toLowerCase().contains("stair")) {
return true;
}
Direction direction = getStairDir(state);
String shape = getStairShape(state);
if (shape.contains("outer") || direction == Direction.WEST) {
return true;
}
if (direction == Direction.EAST) {
return false;
}
if (direction == Direction.SOUTH) {
return !shape.equals("inner_left");
}
return direction != Direction.NORTH || !shape.equals("inner_right");
}
private Direction getStairDir(BlockState state) {
return state.getState(stairDirection);
}
private String getStairShape(BlockState state) {
return state.getState(stairShape).toLowerCase();
}
private boolean isStairOrTrueTop(BlockState state, boolean top) {
return !state.getBlockType().getId().contains("stair") || state.getState(stairHalf).equals("top") == top;
}
private boolean isSlabOrTrueValue(BlockState state, String value) {
return !state.getBlockType().getId().contains("slab") || state.getState(slabHalf).equals(value);
}
private void computeRemoveBlockLight(int x,
int y,
int z,
int currentLight,
Queue<Object[]> queue,
Queue<MutableBlockVector3> spreadQueue,
Map<MutableBlockVector3, Object> visited,
Map<MutableBlockVector3, Object> spreadVisited) {
ChunkHolder<?> iChunk = (ChunkHolder<?>) this.queue.getOrCreateChunk(x >> 4, z >> 4);
if (!iChunk.isInit()) { if (!iChunk.isInit()) {
iChunk.init(this.queue, x >> 4, z >> 4); iChunk.init(this.queue, x >> 4, z >> 4);
} }
@ -270,7 +704,7 @@ public class NMSRelighter implements Relighter {
if (!visited.containsKey(mutableBlockPos)) { if (!visited.containsKey(mutableBlockPos)) {
MutableBlockVector3 index = new MutableBlockVector3(x, y, z); MutableBlockVector3 index = new MutableBlockVector3(x, y, z);
visited.put(index, present); visited.put(index, present);
queue.add(new Object[]{index, current}); queue.add(new Object[] {index, current});
} }
} }
} else if (current >= currentLight) { } else if (current >= currentLight) {
@ -283,16 +717,23 @@ public class NMSRelighter implements Relighter {
} }
} }
private void computeSpreadBlockLight(int x, int y, int z, int currentLight, Queue<MutableBlockVector3> queue, Map<MutableBlockVector3, Object> visited) { private void computeSpreadBlockLight(int x,
currentLight = currentLight - Math.max(1, this.queue.getOpacity(x, y, z)); int y,
int z,
int currentLight,
Queue<MutableBlockVector3> queue,
Map<MutableBlockVector3, Object> visited) {
BlockMaterial material = this.queue.getBlock(x, y, z).getMaterial();
boolean solidNeedsLight = (!material.isSolid() || !material.isFullCube()) && material.getLightOpacity() > 0 && material.getLightValue() == 0;
currentLight = !solidNeedsLight ? currentLight - Math.max(1, material.getLightOpacity()) : currentLight - 1;
if (currentLight > 0) { if (currentLight > 0) {
ChunkHolder iChunk = (ChunkHolder) this.queue.getOrCreateChunk(x >> 4, z >> 4); ChunkHolder<?> iChunk = (ChunkHolder<?>) this.queue.getOrCreateChunk(x >> 4, z >> 4);
if (!iChunk.isInit()) { if (!iChunk.isInit()) {
iChunk.init(this.queue, x >> 4, z >> 4); iChunk.init(this.queue, x >> 4, z >> 4);
} }
int current = iChunk.getEmmittedLight(x & 15, y, z & 15); int current = iChunk.getEmmittedLight(x & 15, y, z & 15);
if (current < currentLight) { if (currentLight > current) {
iChunk.setBlockLight(x, y, z, currentLight); iChunk.setBlockLight(x & 15, y, z & 15, currentLight);
mutableBlockPos.setComponents(x, y, z); mutableBlockPos.setComponents(x, y, z);
if (!visited.containsKey(mutableBlockPos)) { if (!visited.containsKey(mutableBlockPos)) {
visited.put(new MutableBlockVector3(x, y, z), present); visited.put(new MutableBlockVector3(x, y, z), present);
@ -305,7 +746,9 @@ public class NMSRelighter implements Relighter {
} }
public void fixLightingSafe(boolean sky) { public void fixLightingSafe(boolean sky) {
if (isEmpty()) return; if (isEmpty()) {
return;
}
try { try {
if (sky) { if (sky) {
fixSkyLighting(); fixSkyLighting();
@ -328,7 +771,8 @@ public class NMSRelighter implements Relighter {
public void fixBlockLighting() { public void fixBlockLighting() {
synchronized (lightQueue) { synchronized (lightQueue) {
while (!lightLock.compareAndSet(false, true)); while (!lightLock.compareAndSet(false, true))
;
try { try {
updateBlockLight(this.lightQueue); updateBlockLight(this.lightQueue);
} finally { } finally {
@ -345,7 +789,7 @@ public class NMSRelighter implements Relighter {
int bitMask = entry.getValue(); int bitMask = entry.getValue();
int x = MathMan.unpairIntX(pair); int x = MathMan.unpairIntX(pair);
int z = MathMan.unpairIntY(pair); int z = MathMan.unpairIntY(pair);
ChunkHolder chunk = (ChunkHolder) queue.getOrCreateChunk(x, z); ChunkHolder<?> chunk = (ChunkHolder<?>) queue.getOrCreateChunk(x, z);
chunk.setBitMask(bitMask); chunk.setBitMask(bitMask);
iter.remove(); iter.remove();
} }
@ -439,7 +883,7 @@ public class NMSRelighter implements Relighter {
} }
int bx = chunk.x << 4; int bx = chunk.x << 4;
int bz = chunk.z << 4; int bz = chunk.z << 4;
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z); ChunkHolder<?> iChunk = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x, chunk.z);
if (!iChunk.isInit()) { if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z); iChunk.init(queue, chunk.x, chunk.z);
} }
@ -453,9 +897,10 @@ public class NMSRelighter implements Relighter {
int x = j & 15; int x = j & 15;
int z = j >> 4; int z = j >> 4;
byte value = mask[j]; byte value = mask[j];
byte pair = MathMan.pair16(iChunk.getOpacity(x, y, z), iChunk.getBrightness(x, y, z)); BlockMaterial material = iChunk.getBlock(x, y, z).getBlockType().getMaterial();
int opacity = MathMan.unpair16x(pair); int opacity = material.getLightOpacity();
int brightness = MathMan.unpair16y(pair); int brightness = iChunk.getBrightness(x, y, z);
boolean solidNeedsLight = (!material.isSolid() || !material.isFullCube()) && material.getLightOpacity() > 0;
if (brightness > 1) { if (brightness > 1) {
addLightUpdate(bx + x, y, bz + z); addLightUpdate(bx + x, y, bz + z);
} }
@ -482,7 +927,11 @@ public class NMSRelighter implements Relighter {
case 14: case 14:
if (opacity >= value) { if (opacity >= value) {
mask[j] = 0; mask[j] = 0;
iChunk.setSkyLight(x, y, z, 0); if (solidNeedsLight) {
iChunk.setSkyLight(x, y, z, value);
} else {
iChunk.setSkyLight(x, y, z, 0);
}
continue; continue;
} }
if (opacity <= 1) { if (opacity <= 1) {
@ -496,7 +945,11 @@ public class NMSRelighter implements Relighter {
value -= opacity; value -= opacity;
mask[j] = value; mask[j] = value;
} }
iChunk.setSkyLight(x, y, z, value); if (solidNeedsLight) {
iChunk.setSkyLight(x, y, z, value + opacity);
} else {
iChunk.setSkyLight(x, y, z, value);
}
continue; continue;
} }
chunk.smooth = true; chunk.smooth = true;
@ -519,15 +972,15 @@ public class NMSRelighter implements Relighter {
public void smoothSkyLight(RelightSkyEntry chunk, int y, boolean direction) { public void smoothSkyLight(RelightSkyEntry chunk, int y, boolean direction) {
byte[] mask = chunk.mask; byte[] mask = chunk.mask;
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z); ChunkHolder<?> iChunk = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x, chunk.z);
ChunkHolder iChunkx; ChunkHolder<?> iChunkx;
ChunkHolder iChunkz; ChunkHolder<?> iChunkz;
if (!iChunk.isInit()) { if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z); iChunk.init(queue, chunk.x, chunk.z);
} }
if (direction) { if (direction) {
iChunkx = (ChunkHolder) queue.getOrCreateChunk(chunk.x - 1, chunk.z); iChunkx = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x - 1, chunk.z);
iChunkz = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z - 1); iChunkz = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x, chunk.z - 1);
if (!iChunkx.isInit()) { if (!iChunkx.isInit()) {
iChunkx.init(queue, chunk.x - 1, chunk.z); iChunkx.init(queue, chunk.x - 1, chunk.z);
} }
@ -542,26 +995,38 @@ public class NMSRelighter implements Relighter {
} }
byte value = mask[j]; byte value = mask[j];
if (x != 0 && z != 0) { if (x != 0 && z != 0) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) ; if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) {
else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) ; } else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) {
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); }
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else if (x == 0 && z == 0) { } else if (x == 0 && z == 0) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) ; if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) {
else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) ; } else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) {
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); }
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else if (x == 0) { } else if (x == 0) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) ; if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) {
else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) ; } else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) {
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); }
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else { } else {
if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) ; if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) {
else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) ; } else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) {
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); }
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} }
} }
} else { } else {
iChunkx = (ChunkHolder) queue.getOrCreateChunk(chunk.x + 1, chunk.z); iChunkx = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x + 1, chunk.z);
iChunkz = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z + 1); iChunkz = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x, chunk.z + 1);
if (!iChunkx.isInit()) { if (!iChunkx.isInit()) {
iChunkx.init(queue, chunk.x - 1, chunk.z); iChunkx.init(queue, chunk.x - 1, chunk.z);
} }
@ -575,22 +1040,34 @@ public class NMSRelighter implements Relighter {
continue; continue;
} }
byte value = mask[j]; byte value = mask[j];
if ( x != 15 && z != 15) { if (x != 15 && z != 15) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) ; if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) {
else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) ; } else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) {
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); }
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else if (x == 15 && z == 15) { } else if (x == 15 && z == 15) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) ; if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) {
else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) ; } else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) {
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); }
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else if (x == 15) { } else if (x == 15) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) ; if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) {
else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) ; } else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) {
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); }
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else { } else {
if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) ; if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) {
else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) ; } else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) {
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); }
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} }
} }
} }
@ -619,13 +1096,11 @@ public class NMSRelighter implements Relighter {
} }
} }
@Override @Override public String toString() {
public String toString() {
return x + "," + z; return x + "," + z;
} }
@Override @Override public int compareTo(RelightSkyEntry o) {
public int compareTo(RelightSkyEntry o) {
if (o.x < x) { if (o.x < x) {
return 1; return 1;
} }

View File

@ -480,7 +480,7 @@ public class Settings extends Config {
}) })
public int MODE = 1; public int MODE = 1;
@Comment({"If existing lighting should be removed before relighting"}) @Comment({"If existing lighting should be removed before relighting"})
public boolean REMOVE_FIRST = false; public boolean REMOVE_FIRST = true;
} }
public void reload(File file) { public void reload(File file) {