396 lines
12 KiB
JavaScript
396 lines
12 KiB
JavaScript
/**
|
|
* The batch data that is needed by {@link OccludableSamplerShader} to render an element with batching.
|
|
* @typedef {object} OccludableBatchData
|
|
* @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} worldAlpha The world alpha
|
|
* @property {number} _tintRGB The tint
|
|
* @property {number} blendMode The blend mode
|
|
* @property {number} elevation The elevation
|
|
* @property {number} unoccludedAlpha The unoccluded alpha
|
|
* @property {number} occludedAlpha The unoccluded alpha
|
|
* @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 occlusion sampler shader.
|
|
*/
|
|
class OccludableSamplerShader extends BaseSamplerShader {
|
|
|
|
/**
|
|
* The fragment shader code that applies occlusion.
|
|
* @type {string}
|
|
*/
|
|
static #OCCLUSION = `
|
|
vec3 occluded = 1.0 - step(occlusionElevation, texture(occlusionTexture, vScreenCoord).rgb);
|
|
float occlusion = max(occluded.r * fadeOcclusion, max(occluded.g * radialOcclusion, occluded.b * visionOcclusion));
|
|
fragColor *= mix(unoccludedAlpha, occludedAlpha, occlusion);
|
|
`;
|
|
|
|
/* -------------------------------------------- */
|
|
/* Batched version Rendering */
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static classPluginName = "batchOcclusion";
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static batchGeometry = [
|
|
{id: "aVertexPosition", size: 2, normalized: false, type: PIXI.TYPES.FLOAT},
|
|
{id: "aTextureCoord", size: 2, normalized: false, type: PIXI.TYPES.FLOAT},
|
|
{id: "aColor", size: 4, normalized: true, type: PIXI.TYPES.UNSIGNED_BYTE},
|
|
{id: "aTextureId", size: 1, normalized: false, type: PIXI.TYPES.UNSIGNED_SHORT},
|
|
{id: "aOcclusionAlphas", size: 2, normalized: true, type: PIXI.TYPES.UNSIGNED_BYTE},
|
|
{id: "aOcclusionData", size: 4, normalized: true, type: PIXI.TYPES.UNSIGNED_BYTE}
|
|
];
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static batchVertexSize = 7;
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static reservedTextureUnits = 1; // We need a texture unit for the occlusion texture
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static defaultUniforms = {
|
|
screenDimensions: [1, 1],
|
|
sampler: null,
|
|
tintAlpha: [1, 1, 1, 1],
|
|
occlusionTexture: null,
|
|
unoccludedAlpha: 1,
|
|
occludedAlpha: 0,
|
|
occlusionElevation: 0,
|
|
fadeOcclusion: 0,
|
|
radialOcclusion: 0,
|
|
visionOcclusion: 0
|
|
};
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static batchDefaultUniforms(maxTex) {
|
|
return {
|
|
screenDimensions: [1, 1],
|
|
occlusionTexture: maxTex
|
|
};
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static _preRenderBatch(batchRenderer) {
|
|
const occlusionMask = canvas.masks.occlusion;
|
|
const uniforms = batchRenderer._shader.uniforms;
|
|
uniforms.screenDimensions = canvas.screenDimensions;
|
|
batchRenderer.renderer.texture.bind(occlusionMask.renderTexture, uniforms.occlusionTexture);
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static _packInterleavedGeometry(element, attributeBuffer, indexBuffer, aIndex, iIndex) {
|
|
const {float32View, uint8View, uint16View, uint32View} = 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 baseTexture = element._texture.baseTexture;
|
|
const alpha = Math.min(element.worldAlpha, 1.0);
|
|
const argb = PIXI.Color.shared.setValue(element._tintRGB).toPremultiplied(alpha, baseTexture.alphaMode > 0);
|
|
const textureId = baseTexture._batchLocation;
|
|
const unoccludedAlpha = (element.unoccludedAlpha * 255) | 0;
|
|
const occludedAlpha = (element.occludedAlpha * 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];
|
|
uint32View[k++] = argb;
|
|
k <<= 1;
|
|
uint16View[k++] = textureId;
|
|
k <<= 1;
|
|
uint8View[k++] = unoccludedAlpha;
|
|
uint8View[k++] = occludedAlpha;
|
|
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;
|
|
in vec4 aColor;
|
|
|
|
uniform mat3 translationMatrix;
|
|
uniform vec4 tint;
|
|
uniform vec2 screenDimensions;
|
|
|
|
${this._batchVertexShader}
|
|
|
|
in float aTextureId;
|
|
in vec2 aOcclusionAlphas;
|
|
in vec4 aOcclusionData;
|
|
|
|
uniform mat3 projectionMatrix;
|
|
|
|
out vec2 vTextureCoord;
|
|
out vec2 vScreenCoord;
|
|
flat out vec4 vColor;
|
|
flat out float vTextureId;
|
|
flat out float vUnoccludedAlpha;
|
|
flat out float vOccludedAlpha;
|
|
flat out float vOcclusionElevation;
|
|
flat out float vFadeOcclusion;
|
|
flat out float vRadialOcclusion;
|
|
flat out float vVisionOcclusion;
|
|
|
|
void main() {
|
|
vec2 vertexPosition;
|
|
vec2 textureCoord;
|
|
vec4 color;
|
|
_main(vertexPosition, textureCoord, color);
|
|
gl_Position = vec4((projectionMatrix * vec3(vertexPosition, 1.0)).xy, 0.0, 1.0);
|
|
vTextureCoord = textureCoord;
|
|
vScreenCoord = vertexPosition / screenDimensions;
|
|
vColor = color;
|
|
vTextureId = aTextureId;
|
|
vUnoccludedAlpha = aOcclusionAlphas.x;
|
|
vOccludedAlpha = aOcclusionAlphas.y;
|
|
vOcclusionElevation = aOcclusionData.x;
|
|
vFadeOcclusion = aOcclusionData.y;
|
|
vRadialOcclusion = aOcclusionData.z;
|
|
vVisionOcclusion = aOcclusionData.w;
|
|
}
|
|
`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The batch vertex shader source. Subclasses can override it.
|
|
* @type {string}
|
|
* @protected
|
|
*/
|
|
static _batchVertexShader = `
|
|
void _main(out vec2 vertexPosition, out vec2 textureCoord, out vec4 color) {
|
|
vertexPosition = (translationMatrix * vec3(aVertexPosition, 1.0)).xy;
|
|
textureCoord = aTextureCoord;
|
|
color = aColor * tint;
|
|
}
|
|
`;
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static get batchFragmentShader() {
|
|
return `
|
|
#version 300 es
|
|
|
|
${this.GLSL1_COMPATIBILITY_FRAGMENT}
|
|
|
|
precision ${PIXI.settings.PRECISION_FRAGMENT} float;
|
|
|
|
in vec2 vTextureCoord;
|
|
in vec2 vScreenCoord;
|
|
flat in vec4 vColor;
|
|
flat in float vTextureId;
|
|
|
|
uniform sampler2D uSamplers[%count%];
|
|
|
|
${this._batchFragmentShader}
|
|
|
|
flat in float vUnoccludedAlpha;
|
|
flat in float vOccludedAlpha;
|
|
flat in float vOcclusionElevation;
|
|
flat in float vFadeOcclusion;
|
|
flat in float vRadialOcclusion;
|
|
flat in float vVisionOcclusion;
|
|
|
|
uniform sampler2D occlusionTexture;
|
|
|
|
out vec4 fragColor;
|
|
|
|
void main() {
|
|
fragColor = _main();
|
|
float unoccludedAlpha = vUnoccludedAlpha;
|
|
float occludedAlpha = vOccludedAlpha;
|
|
float occlusionElevation = vOcclusionElevation;
|
|
float fadeOcclusion = vFadeOcclusion;
|
|
float radialOcclusion = vRadialOcclusion;
|
|
float visionOcclusion = vVisionOcclusion;
|
|
${OccludableSamplerShader.#OCCLUSION}
|
|
}
|
|
`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The batch fragment shader source. Subclasses can override it.
|
|
* @type {string}
|
|
* @protected
|
|
*/
|
|
static _batchFragmentShader = `
|
|
vec4 _main() {
|
|
vec4 color;
|
|
%forloop%
|
|
return color * vColor;
|
|
}
|
|
`;
|
|
|
|
/* -------------------------------------------- */
|
|
/* 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 vScreenCoord;
|
|
|
|
void main() {
|
|
vec2 vertexPosition;
|
|
vec2 textureCoord;
|
|
_main(vertexPosition, textureCoord);
|
|
gl_Position = vec4((projectionMatrix * vec3(vertexPosition, 1.0)).xy, 0.0, 1.0);
|
|
vUvs = textureCoord;
|
|
vScreenCoord = 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;
|
|
in vec2 vScreenCoord;
|
|
|
|
uniform sampler2D sampler;
|
|
uniform vec4 tintAlpha;
|
|
|
|
${this._fragmentShader}
|
|
|
|
uniform sampler2D occlusionTexture;
|
|
uniform float unoccludedAlpha;
|
|
uniform float occludedAlpha;
|
|
uniform float occlusionElevation;
|
|
uniform float fadeOcclusion;
|
|
uniform float radialOcclusion;
|
|
uniform float visionOcclusion;
|
|
|
|
out vec4 fragColor;
|
|
|
|
void main() {
|
|
fragColor = _main();
|
|
${OccludableSamplerShader.#OCCLUSION}
|
|
}
|
|
`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* The fragment shader source. Subclasses can override it.
|
|
* @type {string}
|
|
* @protected
|
|
*/
|
|
static _fragmentShader = `
|
|
vec4 _main() {
|
|
return texture(sampler, vUvs) * tintAlpha;
|
|
}
|
|
`;
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
_preRender(mesh, renderer) {
|
|
super._preRender(mesh, renderer);
|
|
const uniforms = this.uniforms;
|
|
uniforms.screenDimensions = canvas.screenDimensions;
|
|
const occlusionMask = canvas.masks.occlusion;
|
|
uniforms.occlusionTexture = occlusionMask.renderTexture;
|
|
uniforms.occlusionElevation = occlusionMask.mapElevation(mesh.elevation);
|
|
uniforms.unoccludedAlpha = mesh.unoccludedAlpha;
|
|
uniforms.occludedAlpha = mesh.occludedAlpha;
|
|
const occlusionState = mesh._occlusionState;
|
|
uniforms.fadeOcclusion = occlusionState.fade;
|
|
uniforms.radialOcclusion = occlusionState.radial;
|
|
uniforms.visionOcclusion = occlusionState.vision;
|
|
}
|
|
}
|