422 lines
13 KiB
JavaScript
422 lines
13 KiB
JavaScript
/**
|
|
* The batch data that is needed by {@link DepthSamplerShader} to render an element with batching.
|
|
* @typedef {object} DepthBatchData
|
|
* @property {PIXI.Texture} _texture The texture
|
|
* @property {Float32Array} vertexData The vertices
|
|
* @property {Uint16Array|Uint32Array|number[]} indices The indices
|
|
* @property {Float32Array} uvs The texture UVs
|
|
* @property {number} elevation The elevation
|
|
* @property {number} textureAlphaThreshold The texture alpha threshold
|
|
* @property {number} fadeOcclusion The amount of FADE occlusion
|
|
* @property {number} radialOcclusion The amount of RADIAL occlusion
|
|
* @property {number} visionOcclusion The amount of VISION occlusion
|
|
*/
|
|
|
|
/**
|
|
* The depth sampler shader.
|
|
*/
|
|
class DepthSamplerShader extends BaseSamplerShader {
|
|
|
|
/* -------------------------------------------- */
|
|
/* Batched version Rendering */
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static classPluginName = "batchDepth";
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static batchGeometry = [
|
|
{id: "aVertexPosition", size: 2, normalized: false, type: PIXI.TYPES.FLOAT},
|
|
{id: "aTextureCoord", size: 2, normalized: false, type: PIXI.TYPES.FLOAT},
|
|
{id: "aTextureId", size: 1, normalized: false, type: PIXI.TYPES.UNSIGNED_BYTE},
|
|
{id: "aTextureAlphaThreshold", size: 1, normalized: true, type: PIXI.TYPES.UNSIGNED_BYTE},
|
|
{id: "aDepthElevation", size: 1, normalized: true, type: PIXI.TYPES.UNSIGNED_BYTE},
|
|
{id: "aRestrictionState", size: 1, normalized: false, type: PIXI.TYPES.UNSIGNED_BYTE},
|
|
{id: "aOcclusionData", size: 4, normalized: true, type: PIXI.TYPES.UNSIGNED_BYTE}
|
|
];
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static batchVertexSize = 6;
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static reservedTextureUnits = 1; // We need a texture unit for the occlusion texture
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static defaultUniforms = {
|
|
screenDimensions: [1, 1],
|
|
sampler: null,
|
|
occlusionTexture: null,
|
|
textureAlphaThreshold: 0,
|
|
depthElevation: 0,
|
|
occlusionElevation: 0,
|
|
fadeOcclusion: 0,
|
|
radialOcclusion: 0,
|
|
visionOcclusion: 0,
|
|
restrictsLight: false,
|
|
restrictsWeather: false
|
|
};
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static batchDefaultUniforms(maxTex) {
|
|
return {
|
|
screenDimensions: [1, 1],
|
|
occlusionTexture: maxTex
|
|
};
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static _preRenderBatch(batchRenderer) {
|
|
const uniforms = batchRenderer._shader.uniforms;
|
|
uniforms.screenDimensions = canvas.screenDimensions;
|
|
batchRenderer.renderer.texture.bind(canvas.masks.occlusion.renderTexture, uniforms.occlusionTexture);
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static _packInterleavedGeometry(element, attributeBuffer, indexBuffer, aIndex, iIndex) {
|
|
const {float32View, uint8View} = attributeBuffer;
|
|
|
|
// Write indices into buffer
|
|
const packedVertices = aIndex / this.vertexSize;
|
|
const indices = element.indices;
|
|
for ( let i = 0; i < indices.length; i++ ) {
|
|
indexBuffer[iIndex++] = packedVertices + indices[i];
|
|
}
|
|
|
|
// Prepare attributes
|
|
const vertexData = element.vertexData;
|
|
const uvs = element.uvs;
|
|
const textureId = element._texture.baseTexture._batchLocation;
|
|
const restrictionState = element.restrictionState;
|
|
const textureAlphaThreshold = (element.textureAlphaThreshold * 255) | 0;
|
|
const depthElevation = (canvas.masks.depth.mapElevation(element.elevation) * 255) | 0;
|
|
const occlusionElevation = (canvas.masks.occlusion.mapElevation(element.elevation) * 255) | 0;
|
|
const fadeOcclusion = (element.fadeOcclusion * 255) | 0;
|
|
const radialOcclusion = (element.radialOcclusion * 255) | 0;
|
|
const visionOcclusion = (element.visionOcclusion * 255) | 0;
|
|
|
|
// Write attributes into buffer
|
|
const vertexSize = this.vertexSize;
|
|
for ( let i = 0, j = 0; i < vertexData.length; i += 2, j += vertexSize ) {
|
|
let k = aIndex + j;
|
|
float32View[k++] = vertexData[i];
|
|
float32View[k++] = vertexData[i + 1];
|
|
float32View[k++] = uvs[i];
|
|
float32View[k++] = uvs[i + 1];
|
|
k <<= 2;
|
|
uint8View[k++] = textureId;
|
|
uint8View[k++] = textureAlphaThreshold;
|
|
uint8View[k++] = depthElevation;
|
|
uint8View[k++] = restrictionState;
|
|
uint8View[k++] = occlusionElevation;
|
|
uint8View[k++] = fadeOcclusion;
|
|
uint8View[k++] = radialOcclusion;
|
|
uint8View[k++] = visionOcclusion;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static get batchVertexShader() {
|
|
return `
|
|
#version 300 es
|
|
|
|
${this.GLSL1_COMPATIBILITY_VERTEX}
|
|
|
|
precision ${PIXI.settings.PRECISION_VERTEX} float;
|
|
|
|
in vec2 aVertexPosition;
|
|
in vec2 aTextureCoord;
|
|
|
|
uniform vec2 screenDimensions;
|
|
|
|
${this._batchVertexShader}
|
|
|
|
in float aTextureId;
|
|
in float aTextureAlphaThreshold;
|
|
in float aDepthElevation;
|
|
in vec4 aOcclusionData;
|
|
in float aRestrictionState;
|
|
|
|
uniform mat3 projectionMatrix;
|
|
uniform mat3 translationMatrix;
|
|
|
|
out vec2 vTextureCoord;
|
|
out vec2 vOcclusionCoord;
|
|
flat out float vTextureId;
|
|
flat out float vTextureAlphaThreshold;
|
|
flat out float vDepthElevation;
|
|
flat out float vOcclusionElevation;
|
|
flat out float vFadeOcclusion;
|
|
flat out float vRadialOcclusion;
|
|
flat out float vVisionOcclusion;
|
|
flat out uint vRestrictionState;
|
|
|
|
void main() {
|
|
vec2 vertexPosition;
|
|
vec2 textureCoord;
|
|
_main(vertexPosition, textureCoord);
|
|
vec3 tPos = translationMatrix * vec3(vertexPosition, 1.0);
|
|
gl_Position = vec4((projectionMatrix * tPos).xy, 0.0, 1.0);
|
|
vTextureCoord = textureCoord;
|
|
vOcclusionCoord = tPos.xy / screenDimensions;
|
|
vTextureId = aTextureId;
|
|
vTextureAlphaThreshold = aTextureAlphaThreshold;
|
|
vDepthElevation = aDepthElevation;
|
|
vOcclusionElevation = aOcclusionData.x;
|
|
vFadeOcclusion = aOcclusionData.y;
|
|
vRadialOcclusion = aOcclusionData.z;
|
|
vVisionOcclusion = aOcclusionData.w;
|
|
vRestrictionState = uint(aRestrictionState);
|
|
}
|
|
`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The batch vertex shader source. Subclasses can override it.
|
|
* @type {string}
|
|
* @protected
|
|
*/
|
|
static _batchVertexShader = `
|
|
void _main(out vec2 vertexPosition, out vec2 textureCoord) {
|
|
vertexPosition = aVertexPosition;
|
|
textureCoord = aTextureCoord;
|
|
}
|
|
`;
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static get batchFragmentShader() {
|
|
return `
|
|
#version 300 es
|
|
|
|
${this.GLSL1_COMPATIBILITY_FRAGMENT}
|
|
|
|
precision ${PIXI.settings.PRECISION_FRAGMENT} float;
|
|
|
|
in vec2 vTextureCoord;
|
|
flat in float vTextureId;
|
|
|
|
uniform sampler2D uSamplers[%count%];
|
|
|
|
${DepthSamplerShader.#OPTIONS_CONSTANTS}
|
|
${this._batchFragmentShader}
|
|
|
|
in vec2 vOcclusionCoord;
|
|
flat in float vTextureAlphaThreshold;
|
|
flat in float vDepthElevation;
|
|
flat in float vOcclusionElevation;
|
|
flat in float vFadeOcclusion;
|
|
flat in float vRadialOcclusion;
|
|
flat in float vVisionOcclusion;
|
|
flat in uint vRestrictionState;
|
|
|
|
uniform sampler2D occlusionTexture;
|
|
|
|
out vec3 fragColor;
|
|
|
|
void main() {
|
|
float textureAlpha = _main();
|
|
float textureAlphaThreshold = vTextureAlphaThreshold;
|
|
float depthElevation = vDepthElevation;
|
|
float occlusionElevation = vOcclusionElevation;
|
|
float fadeOcclusion = vFadeOcclusion;
|
|
float radialOcclusion = vRadialOcclusion;
|
|
float visionOcclusion = vVisionOcclusion;
|
|
bool restrictsLight = ((vRestrictionState & RESTRICTS_LIGHT) == RESTRICTS_LIGHT);
|
|
bool restrictsWeather = ((vRestrictionState & RESTRICTS_WEATHER) == RESTRICTS_WEATHER);
|
|
${DepthSamplerShader.#FRAGMENT_MAIN}
|
|
}
|
|
`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The batch fragment shader source. Subclasses can override it.
|
|
* @type {string}
|
|
* @protected
|
|
*/
|
|
static _batchFragmentShader = `
|
|
float _main() {
|
|
vec4 color;
|
|
%forloop%
|
|
return color.a;
|
|
}
|
|
`;
|
|
|
|
/* -------------------------------------------- */
|
|
/* Non-Batched version Rendering */
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static get vertexShader() {
|
|
return `
|
|
#version 300 es
|
|
|
|
${this.GLSL1_COMPATIBILITY_VERTEX}
|
|
|
|
precision ${PIXI.settings.PRECISION_VERTEX} float;
|
|
|
|
in vec2 aVertexPosition;
|
|
in vec2 aTextureCoord;
|
|
|
|
uniform vec2 screenDimensions;
|
|
|
|
${this._vertexShader}
|
|
|
|
uniform mat3 projectionMatrix;
|
|
|
|
out vec2 vUvs;
|
|
out vec2 vOcclusionCoord;
|
|
|
|
void main() {
|
|
vec2 vertexPosition;
|
|
vec2 textureCoord;
|
|
_main(vertexPosition, textureCoord);
|
|
gl_Position = vec4((projectionMatrix * vec3(vertexPosition, 1.0)).xy, 0.0, 1.0);
|
|
vUvs = textureCoord;
|
|
vOcclusionCoord = vertexPosition / screenDimensions;
|
|
}
|
|
`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The vertex shader source. Subclasses can override it.
|
|
* @type {string}
|
|
* @protected
|
|
*/
|
|
static _vertexShader = `
|
|
void _main(out vec2 vertexPosition, out vec2 textureCoord) {
|
|
vertexPosition = aVertexPosition;
|
|
textureCoord = aTextureCoord;
|
|
}
|
|
`;
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static get fragmentShader() {
|
|
return `
|
|
#version 300 es
|
|
|
|
${this.GLSL1_COMPATIBILITY_FRAGMENT}
|
|
|
|
precision ${PIXI.settings.PRECISION_FRAGMENT} float;
|
|
|
|
in vec2 vUvs;
|
|
|
|
uniform sampler2D sampler;
|
|
|
|
${DepthSamplerShader.#OPTIONS_CONSTANTS}
|
|
${this._fragmentShader}
|
|
|
|
in vec2 vOcclusionCoord;
|
|
|
|
uniform sampler2D occlusionTexture;
|
|
uniform float textureAlphaThreshold;
|
|
uniform float depthElevation;
|
|
uniform float occlusionElevation;
|
|
uniform float fadeOcclusion;
|
|
uniform float radialOcclusion;
|
|
uniform float visionOcclusion;
|
|
uniform bool restrictsLight;
|
|
uniform bool restrictsWeather;
|
|
|
|
out vec3 fragColor;
|
|
|
|
void main() {
|
|
float textureAlpha = _main();
|
|
${DepthSamplerShader.#FRAGMENT_MAIN}
|
|
}
|
|
`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The fragment shader source. Subclasses can override it.
|
|
* @type {string}
|
|
* @protected
|
|
*/
|
|
static _fragmentShader = `
|
|
float _main() {
|
|
return texture(sampler, vUvs).a;
|
|
}
|
|
`;
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
_preRender(mesh, renderer) {
|
|
super._preRender(mesh, renderer);
|
|
const uniforms = this.uniforms;
|
|
uniforms.screenDimensions = canvas.screenDimensions;
|
|
uniforms.textureAlphaThreshold = mesh.textureAlphaThreshold;
|
|
const occlusionMask = canvas.masks.occlusion;
|
|
uniforms.occlusionTexture = occlusionMask.renderTexture;
|
|
uniforms.occlusionElevation = occlusionMask.mapElevation(mesh.elevation);
|
|
uniforms.depthElevation = canvas.masks.depth.mapElevation(mesh.elevation);
|
|
const occlusionState = mesh._occlusionState;
|
|
uniforms.fadeOcclusion = occlusionState.fade;
|
|
uniforms.radialOcclusion = occlusionState.radial;
|
|
uniforms.visionOcclusion = occlusionState.vision;
|
|
uniforms.restrictsLight = mesh.restrictsLight;
|
|
uniforms.restrictsWeather = mesh.restrictsWeather;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The restriction options bit mask constants.
|
|
* @type {string}
|
|
*/
|
|
static #OPTIONS_CONSTANTS = foundry.utils.BitMask.generateShaderBitMaskConstants([
|
|
"RESTRICTS_LIGHT",
|
|
"RESTRICTS_WEATHER"
|
|
]);
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The fragment source.
|
|
* @type {string}
|
|
*/
|
|
static #FRAGMENT_MAIN = `
|
|
float inverseDepthElevation = 1.0 - depthElevation;
|
|
fragColor = vec3(inverseDepthElevation, depthElevation, inverseDepthElevation);
|
|
fragColor *= step(textureAlphaThreshold, textureAlpha);
|
|
vec3 weight = 1.0 - step(occlusionElevation, texture(occlusionTexture, vOcclusionCoord).rgb);
|
|
float occlusion = step(0.5, max(max(weight.r * fadeOcclusion, weight.g * radialOcclusion), weight.b * visionOcclusion));
|
|
fragColor.r *= occlusion;
|
|
fragColor.g *= 1.0 - occlusion;
|
|
fragColor.b *= occlusion;
|
|
if ( !restrictsLight ) {
|
|
fragColor.r = 0.0;
|
|
fragColor.g = 0.0;
|
|
}
|
|
if ( !restrictsWeather ) {
|
|
fragColor.b = 0.0;
|
|
}
|
|
`;
|
|
}
|