370 lines
9.9 KiB
JavaScript
370 lines
9.9 KiB
JavaScript
/**
|
|
* The base sampler shader exposes a simple sprite shader and all the framework to handle:
|
|
* - Batched shaders and plugin subscription
|
|
* - Configure method (for special processing done once or punctually)
|
|
* - Update method (pre-binding, normally done each frame)
|
|
* All other sampler shaders (batched or not) should extend BaseSamplerShader
|
|
*/
|
|
class BaseSamplerShader extends AbstractBaseShader {
|
|
|
|
/**
|
|
* The named batch sampler plugin that is used by this shader, or null if no batching is used.
|
|
* @type {string|null}
|
|
*/
|
|
static classPluginName = "batch";
|
|
|
|
/**
|
|
* Is this shader pausable or not?
|
|
* @type {boolean}
|
|
*/
|
|
static pausable = true;
|
|
|
|
/**
|
|
* The plugin name associated for this instance, if any.
|
|
* Returns "batch" if the shader is disabled.
|
|
* @type {string|null}
|
|
*/
|
|
get pluginName() {
|
|
return this.#pluginName;
|
|
}
|
|
|
|
#pluginName = this.constructor.classPluginName;
|
|
|
|
/**
|
|
* Activate or deactivate this sampler. If set to false, the batch rendering is redirected to "batch".
|
|
* Otherwise, the batch rendering is directed toward the instance pluginName (might be null)
|
|
* @type {boolean}
|
|
*/
|
|
get enabled() {
|
|
return this.#enabled;
|
|
}
|
|
|
|
set enabled(enabled) {
|
|
this.#pluginName = enabled ? this.constructor.classPluginName : "batch";
|
|
this.#enabled = enabled;
|
|
}
|
|
|
|
#enabled = true;
|
|
|
|
/**
|
|
* Pause or Unpause this sampler. If set to true, the shader is disabled. Otherwise, it is enabled.
|
|
* Contrary to enabled, a shader might decide to refuse a pause, to continue to render animations per example.
|
|
* @see {enabled}
|
|
* @type {boolean}
|
|
*/
|
|
get paused() {
|
|
return !this.#enabled;
|
|
}
|
|
|
|
set paused(paused) {
|
|
if ( !this.constructor.pausable ) return;
|
|
this.enabled = !paused;
|
|
}
|
|
|
|
/**
|
|
* Contrast adjustment
|
|
* @type {string}
|
|
*/
|
|
static CONTRAST = `
|
|
// Computing contrasted color
|
|
if ( contrast != 0.0 ) {
|
|
changedColor = (changedColor - 0.5) * (contrast + 1.0) + 0.5;
|
|
}`;
|
|
|
|
/**
|
|
* Saturation adjustment
|
|
* @type {string}
|
|
*/
|
|
static SATURATION = `
|
|
// Computing saturated color
|
|
if ( saturation != 0.0 ) {
|
|
vec3 grey = vec3(perceivedBrightness(changedColor));
|
|
changedColor = mix(grey, changedColor, 1.0 + saturation);
|
|
}`;
|
|
|
|
/**
|
|
* Exposure adjustment.
|
|
* @type {string}
|
|
*/
|
|
static EXPOSURE = `
|
|
if ( exposure != 0.0 ) {
|
|
changedColor *= (1.0 + exposure);
|
|
}`;
|
|
|
|
/**
|
|
* The adjustments made into fragment shaders.
|
|
* @type {string}
|
|
*/
|
|
static get ADJUSTMENTS() {
|
|
return `vec3 changedColor = baseColor.rgb;
|
|
${this.CONTRAST}
|
|
${this.SATURATION}
|
|
${this.EXPOSURE}
|
|
baseColor.rgb = changedColor;`;
|
|
}
|
|
|
|
/** @override */
|
|
static vertexShader = `
|
|
precision ${PIXI.settings.PRECISION_VERTEX} float;
|
|
attribute vec2 aVertexPosition;
|
|
attribute vec2 aTextureCoord;
|
|
uniform mat3 projectionMatrix;
|
|
varying vec2 vUvs;
|
|
|
|
void main() {
|
|
gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
|
vUvs = aTextureCoord;
|
|
}
|
|
`;
|
|
|
|
/** @override */
|
|
static fragmentShader = `
|
|
precision ${PIXI.settings.PRECISION_FRAGMENT} float;
|
|
uniform sampler2D sampler;
|
|
uniform vec4 tintAlpha;
|
|
varying vec2 vUvs;
|
|
|
|
void main() {
|
|
gl_FragColor = texture2D(sampler, vUvs) * tintAlpha;
|
|
}
|
|
`;
|
|
|
|
/**
|
|
* The batch vertex shader source.
|
|
* @type {string}
|
|
*/
|
|
static batchVertexShader = `
|
|
#version 300 es
|
|
precision ${PIXI.settings.PRECISION_VERTEX} float;
|
|
in vec2 aVertexPosition;
|
|
in vec2 aTextureCoord;
|
|
in vec4 aColor;
|
|
in float aTextureId;
|
|
uniform mat3 projectionMatrix;
|
|
uniform mat3 translationMatrix;
|
|
uniform vec4 tint;
|
|
out vec2 vTextureCoord;
|
|
flat out vec4 vColor;
|
|
flat out float vTextureId;
|
|
|
|
void main(void){
|
|
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
|
vTextureCoord = aTextureCoord;
|
|
vTextureId = aTextureId;
|
|
vColor = aColor * tint;
|
|
}
|
|
`;
|
|
|
|
/**
|
|
* The batch fragment shader source.
|
|
* @type {string}
|
|
*/
|
|
static batchFragmentShader = `
|
|
#version 300 es
|
|
precision ${PIXI.settings.PRECISION_FRAGMENT} float;
|
|
in vec2 vTextureCoord;
|
|
flat in vec4 vColor;
|
|
flat in float vTextureId;
|
|
uniform sampler2D uSamplers[%count%];
|
|
out vec4 fragColor;
|
|
|
|
#define texture2D texture
|
|
|
|
void main(void){
|
|
vec4 color;
|
|
%forloop%
|
|
fragColor = color * vColor;
|
|
}
|
|
`;
|
|
|
|
/** @inheritdoc */
|
|
static defaultUniforms = {
|
|
sampler: 0,
|
|
tintAlpha: [1, 1, 1, 1]
|
|
};
|
|
|
|
/**
|
|
* Batch geometry associated with this sampler.
|
|
* @type {typeof PIXI.BatchGeometry|{id: string, size: number, normalized: boolean, type: PIXI.TYPES}[]}
|
|
*/
|
|
static batchGeometry = PIXI.BatchGeometry;
|
|
|
|
/**
|
|
* The size of a vertice with all its packed attributes.
|
|
* @type {number}
|
|
*/
|
|
static batchVertexSize = 6;
|
|
|
|
/**
|
|
* Pack interleaved geometry custom function.
|
|
* @type {Function|undefined}
|
|
* @protected
|
|
*/
|
|
static _packInterleavedGeometry;
|
|
|
|
/**
|
|
* A prerender function happening just before the batch renderer is flushed.
|
|
* @type {(batchRenderer: BatchRenderer) => void | undefined}
|
|
* @protected
|
|
*/
|
|
static _preRenderBatch;
|
|
|
|
/**
|
|
* A function that returns default uniforms associated with the batched version of this sampler.
|
|
* @type {object}
|
|
*/
|
|
static batchDefaultUniforms = {};
|
|
|
|
/**
|
|
* The number of reserved texture units for this shader that cannot be used by the batch renderer.
|
|
* @type {number}
|
|
*/
|
|
static reservedTextureUnits = 0;
|
|
|
|
/**
|
|
* Initialize the batch geometry with custom properties.
|
|
*/
|
|
static initializeBatchGeometry() {}
|
|
|
|
/**
|
|
* The batch renderer to use.
|
|
* @type {typeof BatchRenderer}
|
|
*/
|
|
static batchRendererClass = BatchRenderer;
|
|
|
|
/**
|
|
* The batch generator to use.
|
|
* @type {typeof BatchShaderGenerator}
|
|
*/
|
|
static batchShaderGeneratorClass = BatchShaderGenerator;
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/**
|
|
* Create a batch plugin for this sampler class.
|
|
* @returns {typeof BatchPlugin} The batch plugin class linked to this sampler class.
|
|
*/
|
|
static createPlugin() {
|
|
const shaderClass = this;
|
|
const geometryClass = Array.isArray(shaderClass.batchGeometry)
|
|
? class BatchGeometry extends PIXI.Geometry {
|
|
constructor(_static=false) {
|
|
super();
|
|
this._buffer = new PIXI.Buffer(null, _static, false);
|
|
this._indexBuffer = new PIXI.Buffer(null, _static, true);
|
|
for ( const {id, size, normalized, type} of shaderClass.batchGeometry ) {
|
|
this.addAttribute(id, this._buffer, size, normalized, type);
|
|
}
|
|
this.addIndex(this._indexBuffer);
|
|
}
|
|
} : shaderClass.batchGeometry;
|
|
return class BatchPlugin extends shaderClass.batchRendererClass {
|
|
|
|
/** @override */
|
|
static get shaderGeneratorClass() {
|
|
return shaderClass.batchShaderGeneratorClass;
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static get defaultVertexSrc() {
|
|
return shaderClass.batchVertexShader;
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static get defaultFragmentTemplate() {
|
|
return shaderClass.batchFragmentShader;
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
static get defaultUniforms() {
|
|
return shaderClass.batchDefaultUniforms;
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/**
|
|
* The batch plugin constructor.
|
|
* @param {PIXI.Renderer} renderer The renderer
|
|
*/
|
|
constructor(renderer) {
|
|
super(renderer);
|
|
this.geometryClass = geometryClass;
|
|
this.vertexSize = shaderClass.batchVertexSize;
|
|
this.reservedTextureUnits = shaderClass.reservedTextureUnits;
|
|
this._packInterleavedGeometry = shaderClass._packInterleavedGeometry;
|
|
this._preRenderBatch = shaderClass._preRenderBatch;
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
setShaderGenerator(options) {
|
|
if ( !canvas.performance ) return;
|
|
super.setShaderGenerator(options);
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
contextChange() {
|
|
this.shaderGenerator = null;
|
|
super.contextChange();
|
|
}
|
|
};
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/**
|
|
* Register the plugin for this sampler.
|
|
* @param {object} [options] The options
|
|
* @param {object} [options.force=false] Override the plugin of the same name that is already registered?
|
|
*/
|
|
static registerPlugin({force=false}={}) {
|
|
const pluginName = this.classPluginName;
|
|
|
|
// Checking the pluginName
|
|
if ( !(pluginName && (typeof pluginName === "string") && (pluginName.length > 0)) ) {
|
|
const msg = `Impossible to create a PIXI plugin for ${this.name}. `
|
|
+ `The plugin name is invalid: [pluginName=${pluginName}]. `
|
|
+ "The plugin name must be a string with at least 1 character.";
|
|
throw new Error(msg);
|
|
}
|
|
|
|
// Checking for existing plugins
|
|
if ( !force && BatchRenderer.hasPlugin(pluginName) ) {
|
|
const msg = `Impossible to create a PIXI plugin for ${this.name}. `
|
|
+ `The plugin name is already associated to a plugin in PIXI.Renderer: [pluginName=${pluginName}].`;
|
|
throw new Error(msg);
|
|
}
|
|
|
|
// Initialize custom properties for the batch geometry
|
|
this.initializeBatchGeometry();
|
|
|
|
// Create our custom batch renderer for this geometry
|
|
const plugin = this.createPlugin();
|
|
|
|
// Register this plugin with its batch renderer
|
|
PIXI.extensions.add({
|
|
name: pluginName,
|
|
type: PIXI.ExtensionType.RendererPlugin,
|
|
ref: plugin
|
|
});
|
|
}
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
/** @override */
|
|
_preRender(mesh, renderer) {
|
|
const uniforms = this.uniforms;
|
|
uniforms.sampler = mesh.texture;
|
|
uniforms.tintAlpha = mesh._cachedTint;
|
|
}
|
|
}
|