2023-06-05 02:47:43 +01:00
|
|
|
import Pokemon from "../pokemon";
|
2023-10-07 21:08:33 +01:00
|
|
|
import Trainer from "../trainer";
|
2023-06-02 23:33:51 +01:00
|
|
|
|
2023-06-05 02:47:43 +01:00
|
|
|
const spriteFragShader = `
|
2023-06-02 23:33:51 +01:00
|
|
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
|
|
|
precision highp float;
|
|
|
|
#else
|
|
|
|
precision mediump float;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uniform sampler2D uMainSampler[%count%];
|
|
|
|
|
|
|
|
varying vec2 outTexCoord;
|
|
|
|
varying float outTexId;
|
2023-06-05 02:47:43 +01:00
|
|
|
varying vec2 outPosition;
|
2023-06-02 23:33:51 +01:00
|
|
|
varying float outTintEffect;
|
|
|
|
varying vec4 outTint;
|
|
|
|
|
2023-06-06 04:01:00 +01:00
|
|
|
uniform int hasShadow;
|
|
|
|
uniform int yCenter;
|
2023-06-05 02:47:43 +01:00
|
|
|
uniform float vCutoff;
|
|
|
|
uniform vec2 relPosition;
|
|
|
|
uniform vec2 size;
|
|
|
|
uniform float yOffset;
|
2023-06-02 23:33:51 +01:00
|
|
|
uniform vec4 tone;
|
|
|
|
|
|
|
|
const vec3 lumaF = vec3(.299, .587, .114);
|
|
|
|
|
2023-06-05 02:47:43 +01:00
|
|
|
void main()
|
2023-06-02 23:33:51 +01:00
|
|
|
{
|
|
|
|
vec4 texture;
|
|
|
|
|
|
|
|
%forloop%
|
|
|
|
|
|
|
|
vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a);
|
|
|
|
|
|
|
|
// Multiply texture tint
|
|
|
|
vec4 color = texture * texel;
|
|
|
|
|
|
|
|
if (outTintEffect == 1.0)
|
|
|
|
{
|
|
|
|
// Solid color + texture alpha
|
|
|
|
color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a);
|
|
|
|
}
|
|
|
|
else if (outTintEffect == 2.0)
|
|
|
|
{
|
|
|
|
// Solid color, no texture
|
|
|
|
color = texel;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Apply gray */
|
|
|
|
float luma = dot(color.rgb, lumaF);
|
|
|
|
color.rgb = mix(color.rgb, vec3(luma), tone.w);
|
|
|
|
|
|
|
|
/* Apply tone */
|
|
|
|
color.rgb += tone.rgb * (color.a / 255.0);
|
|
|
|
|
2023-06-06 04:01:00 +01:00
|
|
|
if (hasShadow == 1) {
|
2023-06-05 02:47:43 +01:00
|
|
|
float width = size.x - (yOffset / 2.0);
|
|
|
|
|
|
|
|
float spriteX = ((floor(outPosition.x / 6.0) - relPosition.x) / width) + 0.5;
|
|
|
|
float spriteY = ((floor(outPosition.y / 6.0) - relPosition.y) / size.y);
|
|
|
|
|
2023-06-06 04:01:00 +01:00
|
|
|
if (yCenter == 1) {
|
2023-06-05 02:47:43 +01:00
|
|
|
spriteY += 0.5;
|
|
|
|
} else {
|
|
|
|
spriteY += 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool yOverflow = outTexCoord.y >= vCutoff;
|
|
|
|
|
|
|
|
if ((spriteY >= 0.9 && (color.a == 0.0 || yOverflow))) {
|
|
|
|
float shadowSpriteY = (spriteY - 0.9) * (1.0 / 0.15);
|
|
|
|
if (distance(vec2(spriteX, shadowSpriteY), vec2(0.5, 0.5)) < 0.5) {
|
|
|
|
color = vec4(vec3(0.0, 0.0, 0.0), 0.5);
|
|
|
|
} else if (yOverflow) {
|
|
|
|
discard;
|
|
|
|
}
|
|
|
|
} else if (yOverflow) {
|
|
|
|
discard;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-02 23:33:51 +01:00
|
|
|
gl_FragColor = color;
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
2023-06-05 02:47:43 +01:00
|
|
|
const spriteVertShader = `
|
|
|
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
|
|
|
precision highp float;
|
|
|
|
#else
|
|
|
|
precision mediump float;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uniform mat4 uProjectionMatrix;
|
|
|
|
|
|
|
|
attribute vec2 inPosition;
|
|
|
|
attribute vec2 inTexCoord;
|
|
|
|
attribute float inTexId;
|
|
|
|
attribute float inTintEffect;
|
|
|
|
attribute vec4 inTint;
|
|
|
|
|
|
|
|
varying vec2 outTexCoord;
|
|
|
|
varying float outTexId;
|
|
|
|
varying vec2 outPosition;
|
|
|
|
varying float outTintEffect;
|
|
|
|
varying vec4 outTint;
|
|
|
|
|
|
|
|
void main()
|
2023-06-02 23:33:51 +01:00
|
|
|
{
|
2023-06-05 02:47:43 +01:00
|
|
|
gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0);
|
|
|
|
|
|
|
|
outTexCoord = inTexCoord;
|
|
|
|
outTexId = inTexId;
|
|
|
|
outPosition = inPosition;
|
|
|
|
outTint = inTint;
|
|
|
|
outTintEffect = inTintEffect;
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
export default class SpritePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline {
|
2023-06-02 23:33:51 +01:00
|
|
|
private _tone: number[];
|
|
|
|
|
|
|
|
constructor(game: Phaser.Game) {
|
|
|
|
super({
|
|
|
|
game: game,
|
|
|
|
name: 'sprite',
|
2023-06-05 02:47:43 +01:00
|
|
|
fragShader: spriteFragShader,
|
|
|
|
vertShader: spriteVertShader
|
2023-06-02 23:33:51 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
this._tone = [ 0, 0, 0, 0 ];
|
|
|
|
}
|
|
|
|
|
|
|
|
onPreRender(): void {
|
2023-06-06 04:01:00 +01:00
|
|
|
this.set1i('hasShadow', 0);
|
|
|
|
this.set1i('yCenter', 0);
|
2023-06-05 02:47:43 +01:00
|
|
|
this.set2f('relPosition', 0, 0);
|
|
|
|
this.set2f('size', 0, 0);
|
|
|
|
this.set1f('yOffset', 0);
|
2023-06-02 23:33:51 +01:00
|
|
|
this.set4f('tone', this._tone[0], this._tone[1], this._tone[2], this._tone[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
onBind(gameObject: Phaser.GameObjects.GameObject): void {
|
|
|
|
super.onBind();
|
|
|
|
|
2023-06-05 02:47:43 +01:00
|
|
|
const sprite = (gameObject as Phaser.GameObjects.Sprite);
|
2023-06-02 23:33:51 +01:00
|
|
|
|
2023-06-05 02:47:43 +01:00
|
|
|
const data = sprite.pipelineData;
|
|
|
|
const tone = data['tone'] as number[];
|
|
|
|
const hasShadow = data['hasShadow'] as boolean;
|
|
|
|
|
2023-10-07 21:08:33 +01:00
|
|
|
const position = sprite.parentContainer instanceof Pokemon || sprite.parentContainer instanceof Trainer
|
2023-06-05 02:47:43 +01:00
|
|
|
? [ sprite.parentContainer.x, sprite.parentContainer.y ]
|
|
|
|
: [ sprite.x, sprite.y ];
|
|
|
|
position[0] += -(sprite.width - sprite.frame.width) / 2 + sprite.frame.x;
|
2023-06-06 04:01:00 +01:00
|
|
|
this.set1i('hasShadow', hasShadow ? 1 : 0);
|
|
|
|
this.set1i('yCenter', sprite.originY === 0.5 ? 1 : 0);
|
2023-06-05 02:47:43 +01:00
|
|
|
this.set2f('relPosition', position[0], position[1]);
|
|
|
|
this.set2f('size', sprite.frame.width, sprite.height);
|
|
|
|
this.set1f('yOffset', sprite.height - sprite.frame.height);
|
2023-06-02 23:33:51 +01:00
|
|
|
this.set4f('tone', tone[0], tone[1], tone[2], tone[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
onBatch(gameObject: Phaser.GameObjects.GameObject): void {
|
2023-06-05 02:47:43 +01:00
|
|
|
if (gameObject)
|
2023-06-02 23:33:51 +01:00
|
|
|
this.flush();
|
2023-06-05 02:47:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
batchQuad(gameObject: Phaser.GameObjects.GameObject, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number,
|
|
|
|
u0: number, v0: number, u1: number, v1: number, tintTL: number, tintTR: number, tintBL: number, tintBR: number, tintEffect: number | boolean,
|
|
|
|
texture?: WebGLTexture, unit?: number): boolean {
|
|
|
|
const sprite = gameObject as Phaser.GameObjects.Sprite;
|
|
|
|
|
|
|
|
this.set1f('vCutoff', v1);
|
|
|
|
|
|
|
|
const hasShadow = sprite.pipelineData['hasShadow'] as boolean;
|
|
|
|
if (hasShadow) {
|
2023-10-07 21:08:33 +01:00
|
|
|
const baseY = (sprite.parentContainer instanceof Pokemon || sprite.parentContainer instanceof Trainer
|
2023-06-05 02:47:43 +01:00
|
|
|
? sprite.parentContainer.y
|
|
|
|
: sprite.y + sprite.height / 2) * 6;
|
2023-06-05 16:39:49 +01:00
|
|
|
const bottomPadding = Math.ceil(sprite.height * 0.05) * 6;
|
2023-06-05 02:47:43 +01:00
|
|
|
const yDelta = (baseY - y1) / 6;
|
|
|
|
y2 = y1 = baseY + bottomPadding;
|
|
|
|
const pixelHeight = (v1 - v0) / sprite.frame.height;
|
|
|
|
v1 += (yDelta + bottomPadding / 6) * pixelHeight;
|
2023-06-02 23:33:51 +01:00
|
|
|
}
|
2023-06-05 02:47:43 +01:00
|
|
|
|
|
|
|
return super.batchQuad(gameObject, x0, y0, x1, y1, x2, y2, x3, y3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, tintEffect, texture, unit);
|
2023-06-02 23:33:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
get tone(): number[] {
|
|
|
|
return this._tone;
|
|
|
|
}
|
|
|
|
|
|
|
|
set tone(value: number[]) {
|
|
|
|
this._tone = value;
|
|
|
|
}
|
|
|
|
}
|