nitro-renderer/src/room/renderer/utils/ExtendedSprite.ts

207 lines
5.0 KiB
TypeScript

import { BLEND_MODES } from '@pixi/constants';
import { BaseTexture, Resource, Texture } from '@pixi/core';
import { Point } from '@pixi/math';
import { Sprite } from '@pixi/sprite';
import { AlphaTolerance } from '../../../api';
import { TextureUtils } from '../../../pixi-proxy';
export class ExtendedSprite extends Sprite
{
private _offsetX: number;
private _offsetY: number;
private _tag: string;
private _alphaTolerance: number;
private _varyingDepth: boolean;
private _clickHandling: boolean;
private _pairedSpriteId: number;
private _pairedSpriteUpdateCounter: number;
constructor(texture: Texture<Resource> = null)
{
super(texture);
this._offsetX = 0;
this._offsetY = 0;
this._tag = '';
this._alphaTolerance = 128;
this._varyingDepth = false;
this._clickHandling = false;
this._pairedSpriteId = -1;
this._pairedSpriteUpdateCounter = -1;
}
public needsUpdate(pairedSpriteId: number, pairedSpriteUpdateCounter: number): boolean
{
if((this._pairedSpriteId === pairedSpriteId) && (this._pairedSpriteUpdateCounter === pairedSpriteUpdateCounter)) return false;
this._pairedSpriteId = pairedSpriteId;
this._pairedSpriteUpdateCounter = pairedSpriteUpdateCounter;
return true;
}
public calculateVertices(): void
{
if(!this.texture.orig) return;
super.calculateVertices();
}
public setTexture(texture: Texture<Resource>): void
{
if(!texture) texture = Texture.EMPTY;
if(texture === this.texture) return;
if(texture === Texture.EMPTY)
{
this._pairedSpriteId = -1;
this._pairedSpriteUpdateCounter = -1;
}
this.texture = texture;
}
public containsPoint(point: Point): boolean
{
return ExtendedSprite.containsPoint(this, point);
}
public static containsPoint(sprite: ExtendedSprite, point: Point): boolean
{
if(!sprite || !point || (sprite.alphaTolerance > 255)) return false;
if(!(sprite instanceof Sprite)) return false;
if((sprite.texture === Texture.EMPTY) || (sprite.blendMode !== BLEND_MODES.NORMAL)) return;
const texture = sprite.texture;
const baseTexture = texture.baseTexture;
if(!texture || !baseTexture || !baseTexture.valid) return false;
const x = (point.x * sprite.scale.x);
const y = (point.y * sprite.scale.y);
if(!sprite.getLocalBounds().contains(x, y)) return false;
//@ts-ignore
if(!baseTexture.hitMap)
{
if(!ExtendedSprite.generateHitMap(baseTexture)) return false;
}
//@ts-ignore
const hitMap = (baseTexture.hitMap as Uint32Array);
let dx = (x + texture.frame.x);
let dy = (y + texture.frame.y);
if(texture.trim)
{
dx -= texture.trim.x;
dy -= texture.trim.y;
}
dx = (Math.round(dx) * baseTexture.resolution);
dy = (Math.round(dy) * baseTexture.resolution);
const ind = (dx + dy * baseTexture.realWidth);
const ind1 = ind % 32;
const ind2 = ind / 32 | 0;
return (hitMap[ind2] & (1 << ind1)) !== 0;
}
private static generateHitMap(baseTexture: BaseTexture): boolean
{
if(!baseTexture) return false;
const texture = new Texture(baseTexture);
const sprite = new Sprite(texture);
const pixels = TextureUtils.getPixels(sprite);
const width = baseTexture.width;
const height = baseTexture.height;
const hitmap = new Uint32Array(Math.ceil(width * height / 32));
const threshold = AlphaTolerance.MATCH_OPAQUE_PIXELS;
for(let i = 0; i < width * height; i++)
{
const ind1 = i % 32;
const ind2 = i / 32 | 0;
if(pixels[i * 4 + 3] >= threshold) hitmap[ind2] = hitmap[ind2] | (1 << ind1);
}
//@ts-ignore
baseTexture.hitMap = hitmap;
sprite.destroy();
texture.destroy();
return true;
}
public get offsetX(): number
{
return this._offsetX;
}
public set offsetX(offset: number)
{
this._offsetX = offset;
}
public get offsetY(): number
{
return this._offsetY;
}
public set offsetY(offset: number)
{
this._offsetY = offset;
}
public get tag(): string
{
return this._tag;
}
public set tag(tag: string)
{
this._tag = tag;
}
public get alphaTolerance(): number
{
return this._alphaTolerance;
}
public set alphaTolerance(tolerance: number)
{
this._alphaTolerance = tolerance;
}
public get varyingDepth(): boolean
{
return this._varyingDepth;
}
public set varyingDepth(flag: boolean)
{
this._varyingDepth = flag;
}
public get clickHandling(): boolean
{
return this._clickHandling;
}
public set clickHandling(flag: boolean)
{
this._clickHandling = flag;
}
}