Files
Foundry-VTT-Docker/resources/app/client/pixi/groups/environment.js
2025-01-04 00:34:03 +01:00

329 lines
11 KiB
JavaScript

/**
* A container group which contains the primary canvas group and the effects canvas group.
*
* @category - Canvas
*/
class EnvironmentCanvasGroup extends CanvasGroupMixin(PIXI.Container) {
constructor(...args) {
super(...args);
this.eventMode = "static";
/**
* The global light source attached to the environment
* @type {GlobalLightSource}
*/
Object.defineProperty(this, "globalLightSource", {
value: new CONFIG.Canvas.globalLightSourceClass({object: this, sourceId: "globalLight"}),
configurable: false,
enumerable: true,
writable: false
});
}
/** @override */
static groupName = "environment";
/** @override */
static tearDownChildren = false;
/**
* The scene darkness level.
* @type {number}
*/
#darknessLevel;
/**
* Colors exposed by the manager.
* @enum {Color}
*/
colors = {
darkness: undefined,
halfdark: undefined,
background: undefined,
dim: undefined,
bright: undefined,
ambientBrightest: undefined,
ambientDaylight: undefined,
ambientDarkness: undefined,
sceneBackground: undefined,
fogExplored: undefined,
fogUnexplored: undefined
};
/**
* Weights used by the manager to compute colors.
* @enum {number}
*/
weights = {
dark: undefined,
halfdark: undefined,
dim: undefined,
bright: undefined
};
/**
* Fallback colors.
* @enum {Color}
*/
static #fallbackColors = {
darknessColor: 0x242448,
daylightColor: 0xEEEEEE,
brightestColor: 0xFFFFFF,
backgroundColor: 0x999999,
fogUnexplored: 0x000000,
fogExplored: 0x000000
};
/**
* Contains a list of subscribed function for darkness handler.
* @type {PIXI.EventBoundary}
*/
#eventBoundary;
/* -------------------------------------------- */
/* Properties */
/* -------------------------------------------- */
/**
* Get the darkness level of this scene.
* @returns {number}
*/
get darknessLevel() {
return this.#darknessLevel;
}
/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */
/** @override */
async _draw(options) {
await super._draw(options);
this.#eventBoundary = new PIXI.EventBoundary(this);
this.initialize();
}
/* -------------------------------------------- */
/* Ambience Methods */
/* -------------------------------------------- */
/**
* Initialize the scene environment options.
* @param {object} [config={}]
* @param {ColorSource} [config.backgroundColor] The background canvas color
* @param {ColorSource} [config.brightestColor] The brightest ambient color
* @param {ColorSource} [config.darknessColor] The color of darkness
* @param {ColorSource} [config.daylightColor] The ambient daylight color
* @param {ColorSource} [config.fogExploredColor] The color applied to explored areas
* @param {ColorSource} [config.fogUnexploredColor] The color applied to unexplored areas
* @param {SceneEnvironmentData} [config.environment] The scene environment data
* @fires PIXI.FederatedEvent type: "darknessChange" - event: {environmentData: {darknessLevel, priorDarknessLevel}}
*/
initialize({backgroundColor, brightestColor, darknessColor, daylightColor, fogExploredColor,
fogUnexploredColor, darknessLevel, environment={}}={}) {
const scene = canvas.scene;
// Update base ambient colors, and darkness level
const fbc = EnvironmentCanvasGroup.#fallbackColors;
this.colors.ambientDarkness = Color.from(darknessColor ?? CONFIG.Canvas.darknessColor ?? fbc.darknessColor);
this.colors.ambientDaylight = Color.from(daylightColor
?? (scene.tokenVision ? (CONFIG.Canvas.daylightColor ?? fbc.daylightColor) : 0xFFFFFF));
this.colors.ambientBrightest = Color.from(brightestColor ?? CONFIG.Canvas.brightestColor ?? fbc.brightestColor);
/**
* @deprecated since v12
*/
if ( darknessLevel !== undefined ) {
const msg = "config.darknessLevel parameter into EnvironmentCanvasGroup#initialize is deprecated. " +
"You should pass the darkness level into config.environment.darknessLevel";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
environment.darknessLevel = darknessLevel;
}
// Darkness Level Control
const priorDarknessLevel = this.#darknessLevel ?? 0;
const dl = environment.darknessLevel ?? scene.environment.darknessLevel;
const darknessChanged = (dl !== this.#darknessLevel);
this.#darknessLevel = scene.environment.darknessLevel = dl;
// Update weights
Object.assign(this.weights, CONFIG.Canvas.lightLevels ?? {
dark: 0,
halfdark: 0.5,
dim: 0.25,
bright: 1
});
// Compute colors
this.#configureColors({fogExploredColor, fogUnexploredColor, backgroundColor});
// Configure the scene environment
this.#configureEnvironment(environment);
// Update primary cached container and renderer clear color with scene background color
canvas.app.renderer.background.color = this.colors.rendererBackground;
canvas.primary._backgroundColor = this.colors.sceneBackground.rgb;
// Dispatching the darkness change event
if ( darknessChanged ) {
const event = new PIXI.FederatedEvent(this.#eventBoundary);
event.type = "darknessChange";
event.environmentData = {
darknessLevel: this.#darknessLevel,
priorDarknessLevel
};
this.dispatchEvent(event);
}
// Push a perception update to refresh lighting and sources with the new computed color values
canvas.perception.update({
refreshPrimary: true,
refreshLighting: true,
refreshVision: true
});
}
/* -------------------------------------------- */
/**
* Configure all colors pertaining to a scene.
* @param {object} [options={}] Preview options.
* @param {ColorSource} [options.fogExploredColor] A preview fog explored color.
* @param {ColorSource} [options.fogUnexploredColor] A preview fog unexplored color.
* @param {ColorSource} [options.backgroundColor] The background canvas color.
*/
#configureColors({fogExploredColor, fogUnexploredColor, backgroundColor}={}) {
const scene = canvas.scene;
const fbc = EnvironmentCanvasGroup.#fallbackColors;
// Compute the middle ambient color
this.colors.background = this.colors.ambientDarkness.mix(this.colors.ambientDaylight, 1.0 - this.darknessLevel);
// Compute dark ambient colors
this.colors.darkness = this.colors.ambientDarkness.mix(this.colors.background, this.weights.dark);
this.colors.halfdark = this.colors.darkness.mix(this.colors.background, this.weights.halfdark);
// Compute light ambient colors
this.colors.bright =
this.colors.background.mix(this.colors.ambientBrightest, this.weights.bright);
this.colors.dim = this.colors.background.mix(this.colors.bright, this.weights.dim);
// Compute fog colors
const cfg = CONFIG.Canvas;
const uc = Color.from(fogUnexploredColor ?? scene.fog.colors.unexplored ?? cfg.unexploredColor ?? fbc.fogUnexplored);
this.colors.fogUnexplored = this.colors.background.multiply(uc);
const ec = Color.from(fogExploredColor ?? scene.fog.colors.explored ?? cfg.exploredColor ?? fbc.fogExplored);
this.colors.fogExplored = this.colors.background.multiply(ec);
// Compute scene background color
const sceneBG = Color.from(backgroundColor ?? scene?.backgroundColor ?? fbc.backgroundColor);
this.colors.sceneBackground = sceneBG;
this.colors.rendererBackground = sceneBG.multiply(this.colors.background);
}
/* -------------------------------------------- */
/**
* Configure the ambience filter for scene ambient lighting.
* @param {SceneEnvironmentData} [environment] The scene environment data object.
*/
#configureEnvironment(environment={}) {
const currentEnvironment = canvas.scene.toObject().environment;
/**
* @type {SceneEnvironmentData}
*/
const data = foundry.utils.mergeObject(environment, currentEnvironment, {
inplace: false,
insertKeys: true,
insertValues: true,
overwrite: false
});
// First configure the ambience filter
this.#configureAmbienceFilter(data);
// Then configure the global light
this.#configureGlobalLight(data);
}
/* -------------------------------------------- */
/**
* Configure the ambience filter.
* @param {SceneEnvironmentData} environment
* @param {boolean} environment.cycle The cycle option.
* @param {EnvironmentData} environment.base The base environement data.
* @param {EnvironmentData} environment.dark The dark environment data.
*/
#configureAmbienceFilter({cycle, base, dark}) {
const ambienceFilter = canvas.primary._ambienceFilter;
if ( !ambienceFilter ) return;
const u = ambienceFilter.uniforms;
// Assigning base ambience parameters
const bh = Color.fromHSL([base.hue, 1, 0.5]).linear;
Color.applyRGB(bh, u.baseTint);
u.baseLuminosity = base.luminosity;
u.baseShadows = base.shadows;
u.baseIntensity = base.intensity;
u.baseSaturation = base.saturation;
const baseAmbienceHasEffect = (base.luminosity !== 0) || (base.shadows > 0)
|| (base.intensity > 0) || (base.saturation !== 0);
// Assigning dark ambience parameters
const dh = Color.fromHSL([dark.hue, 1, 0.5]).linear;
Color.applyRGB(dh, u.darkTint);
u.darkLuminosity = dark.luminosity;
u.darkShadows = dark.shadows;
u.darkIntensity = dark.intensity;
u.darkSaturation = dark.saturation;
const darkAmbienceHasEffect = ((dark.luminosity !== 0) || (dark.shadows > 0)
|| (dark.intensity > 0) || (dark.saturation !== 0)) && cycle;
// Assigning the cycle option
u.cycle = cycle;
// Darkness level texture
u.darknessLevelTexture = canvas.effects.illumination.renderTexture;
// Enable ambience filter if it is impacting visuals
ambienceFilter.enabled = baseAmbienceHasEffect || darkAmbienceHasEffect;
}
/* -------------------------------------------- */
/**
* Configure the global light.
* @param {SceneEnvironmentData} environment
* @param {GlobalLightData} environment.globalLight
*/
#configureGlobalLight({globalLight}) {
const maxR = canvas.dimensions.maxR * 1.2;
const globalLightData = foundry.utils.mergeObject({
z: -Infinity,
elevation: Infinity,
dim: globalLight.bright ? 0 : maxR,
bright: globalLight.bright ? maxR : 0,
disabled: !globalLight.enabled
}, globalLight, {overwrite: false});
this.globalLightSource.initialize(globalLightData);
this.globalLightSource.add();
}
/* -------------------------------------------- */
/* Deprecations and Compatibility */
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
get darknessPenalty() {
const msg = "EnvironmentCanvasGroup#darknessPenalty is deprecated without replacement. " +
"The darkness penalty is no longer applied on light and vision sources.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14});
return 0;
}
}