Initial
This commit is contained in:
213
resources/app/client-esm/canvas/regions/mesh.mjs
Normal file
213
resources/app/client-esm/canvas/regions/mesh.mjs
Normal file
@@ -0,0 +1,213 @@
|
||||
|
||||
/**
|
||||
* A mesh of a {@link Region}.
|
||||
* @extends {PIXI.Container}
|
||||
*/
|
||||
export default class RegionMesh extends PIXI.Container {
|
||||
|
||||
/**
|
||||
* Create a RegionMesh.
|
||||
* @param {Region} region The Region to create the RegionMesh from.
|
||||
* @param {AbstractBaseShader} [shaderClass] The shader class to use.
|
||||
*/
|
||||
constructor(region, shaderClass=RegionShader) {
|
||||
super();
|
||||
this.#region = region;
|
||||
this.region.geometry.refCount++;
|
||||
if ( !AbstractBaseShader.isPrototypeOf(shaderClass) ) {
|
||||
throw new Error("RegionMesh shader class must inherit from AbstractBaseShader.");
|
||||
}
|
||||
this.#shader = shaderClass.create();
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* Shared point instance.
|
||||
* @type {PIXI.Point}
|
||||
*/
|
||||
static #SHARED_POINT = new PIXI.Point();
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* The Region of this RegionMesh.
|
||||
* @type {RegionMesh}
|
||||
*/
|
||||
get region() {
|
||||
return this.#region;
|
||||
}
|
||||
|
||||
#region;
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* The shader bound to this RegionMesh.
|
||||
* @type {AbstractBaseShader}
|
||||
*/
|
||||
get shader() {
|
||||
return this.#shader;
|
||||
}
|
||||
|
||||
#shader;
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* The blend mode assigned to this RegionMesh.
|
||||
* @type {PIXI.BLEND_MODES}
|
||||
*/
|
||||
get blendMode() {
|
||||
return this.#state.blendMode;
|
||||
}
|
||||
|
||||
set blendMode(value) {
|
||||
if ( this.#state.blendMode === value ) return;
|
||||
this.#state.blendMode = value;
|
||||
this._tintAlphaDirty = true;
|
||||
}
|
||||
|
||||
#state = PIXI.State.for2d();
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* The tint applied to the mesh. This is a hex value.
|
||||
*
|
||||
* A value of 0xFFFFFF will remove any tint effect.
|
||||
* @type {number}
|
||||
* @defaultValue 0xFFFFFF
|
||||
*/
|
||||
get tint() {
|
||||
return this._tintColor.value;
|
||||
}
|
||||
|
||||
set tint(tint) {
|
||||
const currentTint = this._tintColor.value;
|
||||
this._tintColor.setValue(tint);
|
||||
if ( currentTint === this._tintColor.value ) return;
|
||||
this._tintAlphaDirty = true;
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect.
|
||||
* @type {PIXI.Color}
|
||||
* @protected
|
||||
*/
|
||||
_tintColor = new PIXI.Color(0xFFFFFF);
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* Cached tint value for the shader uniforms.
|
||||
* @type {[red: number, green: number, blue: number, alpha: number]}
|
||||
* @protected
|
||||
* @internal
|
||||
*/
|
||||
_cachedTint = [1, 1, 1, 1];
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* Used to track a tint or alpha change to execute a recomputation of _cachedTint.
|
||||
* @type {boolean}
|
||||
* @protected
|
||||
*/
|
||||
_tintAlphaDirty = true;
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* Initialize shader based on the shader class type.
|
||||
* @param {type AbstractBaseShader} shaderClass The shader class, which must inherit from {@link AbstractBaseShader}.
|
||||
*/
|
||||
setShaderClass(shaderClass) {
|
||||
if ( !AbstractBaseShader.isPrototypeOf(shaderClass) ) {
|
||||
throw new Error("RegionMesh shader class must inherit from AbstractBaseShader.");
|
||||
}
|
||||
if ( this.#shader.constructor === shaderClass ) return;
|
||||
|
||||
// Create shader program
|
||||
this.#shader = shaderClass.create();
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
updateTransform() {
|
||||
super.updateTransform();
|
||||
|
||||
// We set tintAlphaDirty to true if the worldAlpha has changed
|
||||
// It is needed to recompute the _cachedTint vec4 which is a combination of tint and alpha
|
||||
if ( this.#worldAlpha !== this.worldAlpha ) {
|
||||
this.#worldAlpha = this.worldAlpha;
|
||||
this._tintAlphaDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
#worldAlpha;
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
_render(renderer) {
|
||||
if ( this._tintAlphaDirty ) {
|
||||
const premultiply = PIXI.utils.premultiplyBlendMode[1][this.blendMode] === this.blendMode;
|
||||
PIXI.Color.shared.setValue(this._tintColor)
|
||||
.premultiply(this.worldAlpha, premultiply)
|
||||
.toArray(this._cachedTint);
|
||||
this._tintAlphaDirty = false;
|
||||
}
|
||||
this.#shader._preRender(this, renderer);
|
||||
this.#shader.uniforms.translationMatrix = this.transform.worldTransform.toArray(true);
|
||||
|
||||
// Flush batch renderer
|
||||
renderer.batch.flush();
|
||||
|
||||
// Set state
|
||||
renderer.state.set(this.#state);
|
||||
|
||||
// Bind shader and geometry
|
||||
renderer.shader.bind(this.#shader);
|
||||
const geometry = this.region.geometry;
|
||||
geometry._updateBuffers();
|
||||
renderer.geometry.bind(geometry, this.#shader);
|
||||
|
||||
// Draw the geometry
|
||||
renderer.geometry.draw(PIXI.DRAW_MODES.TRIANGLES);
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
_calculateBounds() {
|
||||
const {left, top, right, bottom} = this.region.bounds;
|
||||
this._bounds.addFrame(this.transform, left, top, right, bottom);
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tests if a point is inside this RegionMesh.
|
||||
* @param {PIXI.IPointData} point
|
||||
* @returns {boolean}
|
||||
*/
|
||||
containsPoint(point) {
|
||||
return this.region.polygonTree.testPoint(this.worldTransform.applyInverse(point, RegionMesh.#SHARED_POINT));
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
destroy(options) {
|
||||
super.destroy(options);
|
||||
const geometry = this.region.geometry;
|
||||
geometry.refCount--;
|
||||
if ( geometry.refCount === 0 ) geometry.dispose();
|
||||
this.#shader = null;
|
||||
this.#state = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user