Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ZoneView to keep lighting consistent #3952

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* This software Copyright by the RPTools.net development team, and
* licensed under the Affero GPL Version 3 or, at your option, any later
* version.
*
* MapTool Source Code is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public
* License * along with this source Code. If not, please visit
* <http://www.gnu.org/licenses/> and specifically the Affero license
* text at <http://www.gnu.org/licenses/agpl.html>.
*/
package net.rptools.maptool.client.ui.zone;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
import net.rptools.maptool.model.GUID;
import net.rptools.maptool.model.Light;
import net.rptools.maptool.model.LightSource;

/**
* Manages the light sources and illuminations of a zone, for a given set of illumniator parameters.
*
* <p>This needs to be kept in sync with the associated {@code Zone} in order for the results to
* make sense
*
* <p>No caching is done here, this is purely a data structure.
*/
public class IlluminationModel {
/**
* Associates a LitArea stored in an Illuminator with the Light that it was created for.
*
* <p>This is used to represent normal lights, personal lights, and day light. In the case of
* daylight, lightInfo will be null since it doesn't originate from an actual light, and should
* never be rendered.
*
* @param litArea
* @param lightInfo
*/
public record ContributedLight(Illuminator.LitArea litArea, LightInfo lightInfo) {}

/**
* Combines a LightSource and a Light for easy referencing in ContributedLight.
*
* @param lightSource
* @param light
*/
public record LightInfo(LightSource lightSource, Light light) {}

/**
* The data structure for calculating lit areas according to lumens. Lit areas can be added and
* removed from this structure.
*/
private final Illuminator illuminator = new Illuminator();

/**
* The list of all non-personal lights contributing to the the illuminator.
*
* <p>This is used to associate the LitAreas added to the Illuminator with the original lighting
* parameters that created it. We can use this information to generate DrawableLights for
* rendering.
*/
private final Map<GUID, List<ContributedLight>> contributedLightsByToken = new HashMap<>();

public void removeToken(GUID tokenId) {
final var contributions =
Objects.requireNonNullElse(
contributedLightsByToken.remove(tokenId), Collections.<ContributedLight>emptyList());
// Remove each contribution from the illuminator as well.
for (final var contributedLight : contributions) {
illuminator.remove(contributedLight.litArea());
}
}

public boolean hasToken(GUID tokenId) {
return contributedLightsByToken.containsKey(tokenId);
}

public void addToken(GUID tokenId, List<ContributedLight> contributions) {
for (final var contribution : contributions) {
illuminator.add(contribution.litArea());
contributedLightsByToken.computeIfAbsent(tokenId, id -> new ArrayList<>()).add(contribution);
}
}

public Stream<ContributedLight> getContributions() {
return contributedLightsByToken.values().stream().flatMap(Collection::stream);
}

public Illumination getIllumination() {
return illuminator.getIllumination();
}
}
41 changes: 8 additions & 33 deletions src/main/java/net/rptools/maptool/client/ui/zone/ZoneRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -618,8 +618,7 @@ public void centerOn(CellPoint point) {
/**
* Remove the token from: {@link #tokenLocationCache}, {@link #flipImageMap}, {@link
* #flipIsoImageMap}, {@link #labelRenderingCache}. Set the {@link #visibleScreenArea}, {@link
* #tokenStackMap}, {@link #drawableLights}, {@link #drawableAuras} to null. Flush the token from
* the zoneView.
* #tokenStackMap} to null. Flush the token from {@link #zoneView}.
*
* @param token the token to flush
*/
Expand All @@ -639,9 +638,6 @@ public void flush(Token token) {
// This could also be smarter
tokenStackMap = null;

drawableLights = null;
drawableAuras = null;

zoneView.flush(token);
}

Expand All @@ -668,20 +664,13 @@ public void flush() {
flushDrawableRenderer();
flipImageMap.clear();
flipIsoImageMap.clear();
drawableLights = null;
drawableAuras = null;
zoneView.flushFog();

isLoaded = false;
}

/**
* Set the {@link #drawableLights} and {@link #drawableAuras} to null, flush the zoneView, and
* repaint.
*/
/** Flush the {@link #zoneView} and repaint. */
public void flushLight() {
drawableLights = null;
drawableAuras = null;
zoneView.flush();
repaintDebouncer.dispatch();
}
Expand Down Expand Up @@ -1045,12 +1034,10 @@ public Rectangle zoneExtents(PlayerView view) {
}

/**
* This method clears {@link #drawableLights}, {@link #drawableAuras}, {@link #visibleScreenArea},
* and {@link #lastView}. It also flushes the {@link #zoneView}.
* This method clears {@link #visibleScreenArea} and {@link #lastView}. It also flushes the {@link
* #zoneView}.
*/
public void invalidateCurrentViewCache() {
drawableLights = null;
drawableAuras = null;
visibleScreenArea = null;
lastView = null;
}
Expand Down Expand Up @@ -1423,12 +1410,8 @@ private enum LightOverlayClipStyle {
private void renderLights(Graphics2D g, PlayerView view) {
// Collect and organize lights
timer.start("renderLights:getLights");
if (drawableLights == null) {
timer.start("renderLights:populateCache");
drawableLights = new ArrayList<>(zoneView.getDrawableLights(view));
timer.stop("renderLights:populateCache");
}
timer.start("renderLights:filterLights");
final var drawableLights = zoneView.getDrawableLights(view);
timer.stop("renderLights:getLights");

if (AppState.isShowLights()) {
// Lighting enabled.
Expand Down Expand Up @@ -1482,24 +1465,16 @@ private void renderLights(Graphics2D g, PlayerView view) {
}
}

/** Caches the lights to be drawn as returned ZoneView. */
private List<DrawableLight> drawableLights;
/** Holds the auras from lightSourceMap after they have been combined. */
private List<DrawableLight> drawableAuras;

/**
* Get the list of auras from lightSourceMap, combine them, store them in drawableAuras, and draw
* them.
* Render the auras.
*
* @param g the Graphics2D object.
* @param view the player view.
*/
private void renderAuras(Graphics2D g, PlayerView view) {
// Setup
timer.start("renderAuras:getAuras");
if (drawableAuras == null) {
drawableAuras = new ArrayList<>(zoneView.getDrawableAuras());
}
final var drawableAuras = zoneView.getDrawableAuras();
timer.stop("renderAuras:getAuras");

timer.start("renderAuras:renderAuraOverlay");
Expand Down