Skip to content

Commit

Permalink
Let pipeline disallow virtual lights, more robust reload
Browse files Browse the repository at this point in the history
- More robust LightLevel and EntityLightTracker reload
- More robust CanvasState requireReload
  • Loading branch information
spiralhalo committed Jan 7, 2024
1 parent f5f78da commit 1cc497f
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 28 deletions.
14 changes: 9 additions & 5 deletions src/main/java/grondag/canvas/apiimpl/CanvasState.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

package grondag.canvas.apiimpl;

import java.util.Objects;

import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.language.I18n;

Expand Down Expand Up @@ -69,16 +71,18 @@ public static void recompile() {
private static int loopCounter = 0;

private static boolean recompilePipeline() {
final boolean prevColorLightsState = Pipeline.coloredLightsEnabled();
final boolean prevAdvancedCullingState = Pipeline.advancedTerrainCulling();
final var prev_coloredLightsConfig = Pipeline.config().coloredLights;
final boolean coloredLights_wasDisabled = !Pipeline.coloredLightsEnabled();
final boolean advancedCulling_wasDisabled = !Pipeline.advancedTerrainCulling();

PipelineLoader.reload(Minecraft.getInstance().getResourceManager());
PipelineManager.reload();

final boolean coloredLightsChanged = Pipeline.coloredLightsEnabled() && !prevColorLightsState;
final boolean requireCullingRebuild = Pipeline.advancedTerrainCulling() && !prevAdvancedCullingState;
final boolean coloredLightsConfigChanged = !Objects.equals(Pipeline.config().coloredLights, prev_coloredLightsConfig);
final boolean coloredLights_requiresRebuild = Pipeline.coloredLightsEnabled() && (coloredLightsConfigChanged || coloredLights_wasDisabled);
final boolean culling_requiresRebuild = Pipeline.advancedTerrainCulling() && advancedCulling_wasDisabled;

return coloredLightsChanged || requireCullingRebuild;
return coloredLights_requiresRebuild || culling_requiresRebuild;
}

private static void recompile(boolean wasReloaded) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/grondag/canvas/config/ConfigData.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class ConfigData {
boolean semiFlatLighting = true;
@Comment("Enable colored block lights on pipelines that support it. Replaces vanilla lighting but only visually.")
boolean coloredLights = false;
@Comment("Enable entity as dynamic light sources. Requires colored lights.")
@Comment("Enable entity as dynamic light sources. Requires colored lights and supporting pipeline.")
boolean entityLightSource = false;

// TWEAKS
Expand Down
18 changes: 10 additions & 8 deletions src/main/java/grondag/canvas/light/color/EntityLightTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,26 +77,28 @@ public static void levelRemovesEntity(int id) {
}
}

void reload() {
void reload(boolean clearLights) {
requiresInitialization = true;
// might be called twice due to multiple hook (setLevel and allChanged). idempotent.
removeAll();
removeAll(clearLights);
}

void close(boolean lightLevelIsClosing) {
if (!lightLevelIsClosing) {
removeAll();
void close(boolean clear) {
if (clear) {
removeAll(true);
}

if (INSTANCE == this) {
INSTANCE = null;
}
}

private void removeAll() {
private void removeAll(boolean clearLights) {
// see notes on reload()
for (var entity:entities.values()) {
entity.removeLight();
if (clearLights) {
for (var entity : entities.values()) {
entity.removeLight();
}
}

entities.clear();
Expand Down
72 changes: 59 additions & 13 deletions src/main/java/grondag/canvas/light/color/LightLevel.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

import grondag.canvas.CanvasMod;
import grondag.canvas.config.Configurator;
import grondag.canvas.pipeline.Pipeline;

class LightLevel implements LightLevelAccess {
private BlockAndTintGetter baseLevel = null;
Expand All @@ -42,6 +43,13 @@ class LightLevel implements LightLevelAccess {
private final LongArrayFIFOQueue virtualQueue = new LongArrayFIFOQueue();
private final ObjectOpenHashSet<LightRegion> virtualCheckQueue = new ObjectOpenHashSet<>();

private static final Operator DO_NOTHING = (pos, light) -> { };
private static final Getter GET_ZERO = pos -> (short) 0;

private Operator placer = DO_NOTHING;
private Operator remover = DO_NOTHING;
private Getter getter = GET_ZERO;

LightLevel() {
reload();

Expand All @@ -50,27 +58,46 @@ class LightLevel implements LightLevelAccess {
}

public void reload() {
if (Configurator.entityLightSource) {
assert Pipeline.config().coloredLights != null;

final boolean virtualAllowed = Pipeline.config().coloredLights.allowVirtual;

if (Configurator.entityLightSource && virtualAllowed) {
if (entityLightTracker == null) {
entityLightTracker = new EntityLightTracker(this);
} else {
entityLightTracker.reload();
// no need to clear as we are nuking everything
entityLightTracker.reload(false);
}
} else {
if (entityLightTracker != null) {
// no need to clear as we are nuking everything
entityLightTracker.close(false);
}

entityLightTracker = null;
}

// NB: implementations are expected to repopulate every time chunk is reloaded
virtualLights.clear();

if (virtualAllowed) {
placer = PLACE_VIRTUAL;
remover = REMOVE_VIRTUAL;
getter = GET_VIRTUAL;
} else {
placer = DO_NOTHING;
remover = DO_NOTHING;
getter = GET_ZERO;
}
}

void updateOnStartFrame(ClientLevel level) {
if (baseLevel != level) {
baseLevel = level;

if (entityLightTracker != null) {
entityLightTracker.reload();
entityLightTracker.reload(true);
}
}

Expand All @@ -89,26 +116,34 @@ public BlockAndTintGetter level() {
@Override
public short getRegistered(BlockPos pos) {
var registered = baseLevel == null ? 0 : LightRegistry.get(baseLevel.getBlockState(pos));
return combineWithBlockLight(registered, getVirtualLight(pos));
return combineWithBlockLight(registered, getter.apply(pos));
}

@Override
public short getRegistered(BlockPos pos, @Nullable BlockState state) {
// MAINTENANCE NOTICE: this function is a special casing of getRegistered(BlockPos)
var registered = state != null ? LightRegistry.get(state) : (baseLevel == null ? 0 : LightRegistry.get(baseLevel.getBlockState(pos)));
return combineWithBlockLight(registered, getVirtualLight(pos));
return combineWithBlockLight(registered, getter.apply(pos));
}

@Override
public void placeVirtualLight(BlockPos blockPos, short light) {
placer.apply(blockPos, light);
}

@Override
public void removeVirtualLight(BlockPos blockPos, short light) {
remover.apply(blockPos, light);
}

private final Operator PLACE_VIRTUAL = (blockPos, light) -> {
final long pos = blockPos.asLong();
final var list = virtualLights.computeIfAbsent(pos, l -> new ShortArrayList());
list.add(encodeVirtualLight(light));
virtualQueue.enqueue(pos);
}
};

@Override
public void removeVirtualLight(BlockPos blockPos, short light) {
private final Operator REMOVE_VIRTUAL = (blockPos, light) -> {
final long pos = blockPos.asLong();
final var list = virtualLights.get(pos);
final int i = list == null ? -1 : list.indexOf(encodeVirtualLight(light));
Expand All @@ -117,16 +152,17 @@ public void removeVirtualLight(BlockPos blockPos, short light) {
list.removeShort(i);
virtualQueue.enqueue(pos);
}
}
};

void close() {
if (entityLightTracker != null) {
entityLightTracker.close(true);
// no need to clear as we are nuking everything
entityLightTracker.close(false);
entityLightTracker = null;
}
}

private short getVirtualLight(BlockPos blockPos) {
private final Getter GET_VIRTUAL = blockPos -> {
ShortArrayList lights = virtualLights.get(blockPos.asLong());

if (lights != null) {
Expand All @@ -139,8 +175,8 @@ private short getVirtualLight(BlockPos blockPos) {
return combined;
}

return 0;
}
return (short) 0;
};

private void updateInner() {
while (!virtualQueue.isEmpty()) {
Expand Down Expand Up @@ -172,4 +208,14 @@ public static short encodeVirtualLight(short light) {
private static short combineWithBlockLight(short block, short virtual) {
return LightOp.makeEmitter(LightOp.max(block, virtual));
}

@FunctionalInterface
private interface Operator {
void apply(BlockPos pos, short light);
}

@FunctionalInterface
private interface Getter {
short apply(BlockPos pos);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@

public class ColoredLightsConfig extends AbstractConfig {
public final boolean enabled;
public final boolean allowVirtual;
public final boolean useOcclusionData;

protected ColoredLightsConfig(ConfigContext ctx, JsonObject config) {
super(ctx);
enabled = ctx.dynamic.getBoolean(config, "enabled", true);
allowVirtual = ctx.dynamic.getBoolean(config, "allowVirtual", true);
useOcclusionData = ctx.dynamic.getBoolean(config, "useOcclusionData", false);
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/assets/canvas/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"config.canvas.value.colored_lights": "Colored Lights",
"config.canvas.help.colored_lights": "Enable colored block lights on pipelines that support it. Replaces vanilla lighting but only visually.",
"config.canvas.value.entity_light_source": "Entity Light Source",
"config.canvas.help.entity_light_source": "Enable entity as dynamic light sources. Requires colored lights.",
"config.canvas.help.entity_light_source": "Enable entity as dynamic light sources. Requires colored lights and supporting pipeline.",
"config.canvas.enum.ao_mode.normal": "Vanilla",
"config.canvas.enum.ao_mode.subtle_always": "Subtle",
"config.canvas.enum.ao_mode.subtle_block_light": "Subtle Torchlit",
Expand Down

0 comments on commit 1cc497f

Please sign in to comment.