Masks now work

This commit is contained in:
billsonnn 2024-03-24 22:57:06 -04:00
parent 842a996b93
commit 2f01259b46
4 changed files with 353 additions and 270 deletions

View File

@ -1,6 +1,6 @@
import { IAssetPlaneVisualizationLayer, IAssetRoomVisualizationData, IRoomGeometry, IRoomPlane, IVector3D } from '@nitrots/api'; import { IAssetPlaneVisualizationLayer, IAssetRoomVisualizationData, IRoomGeometry, IRoomPlane, IVector3D } from '@nitrots/api';
import { GetAssetManager } from '@nitrots/assets'; import { GetAssetManager } from '@nitrots/assets';
import { TextureUtils, Vector3d } from '@nitrots/utils'; import { GetRenderer, PlaneMaskFilter, TextureUtils, Vector3d } from '@nitrots/utils';
import { Container, Matrix, Point, Sprite, Texture, TilingSprite } from 'pixi.js'; import { Container, Matrix, Point, Sprite, Texture, TilingSprite } from 'pixi.js';
import { RoomGeometry } from '../../../utils'; import { RoomGeometry } from '../../../utils';
import { RoomPlaneBitmapMask } from './RoomPlaneBitmapMask'; import { RoomPlaneBitmapMask } from './RoomPlaneBitmapMask';
@ -48,6 +48,7 @@ export class RoomPlane implements IRoomPlane
private _height: number = 0; private _height: number = 0;
private _hasTexture: boolean = true; private _hasTexture: boolean = true;
private _canBeVisible: boolean = true; private _canBeVisible: boolean = true;
private _geometryUpdateId: number = -1;
private _useMask: boolean; private _useMask: boolean;
private _bitmapMasks: RoomPlaneBitmapMask[] = []; private _bitmapMasks: RoomPlaneBitmapMask[] = [];
@ -56,6 +57,7 @@ export class RoomPlane implements IRoomPlane
private _bitmapMasksOld: RoomPlaneBitmapMask[] = []; private _bitmapMasksOld: RoomPlaneBitmapMask[] = [];
private _rectangleMasksOld: RoomPlaneRectangleMask[] = []; private _rectangleMasksOld: RoomPlaneRectangleMask[] = [];
private _planeSprite: TilingSprite = null;
private _planeTexture: Texture = null; private _planeTexture: Texture = null;
constructor(origin: IVector3D, location: IVector3D, leftSide: IVector3D, rightSide: IVector3D, type: number, usesMask: boolean, secondaryNormals: IVector3D[], randomSeed: number, textureOffsetX: number = 0, textureOffsetY: number = 0, textureMaxX: number = 0, textureMaxY: number = 0) constructor(origin: IVector3D, location: IVector3D, leftSide: IVector3D, rightSide: IVector3D, type: number, usesMask: boolean, secondaryNormals: IVector3D[], randomSeed: number, textureOffsetX: number = 0, textureOffsetY: number = 0, textureMaxX: number = 0, textureMaxY: number = 0)
@ -111,6 +113,17 @@ export class RoomPlane implements IRoomPlane
{ {
if(!geometry || this._disposed) return false; if(!geometry || this._disposed) return false;
let geometryChanged = false;
if(this._geometryUpdateId != geometry.updateId) geometryChanged = true;
if(!geometryChanged || !this._canBeVisible)
{
if(!this.visible) return false;
}
if(geometryChanged)
{
let cosAngle = 0; let cosAngle = 0;
cosAngle = Vector3d.cosAngle(geometry.directionAxis, this.normal); cosAngle = Vector3d.cosAngle(geometry.directionAxis, this.normal);
@ -163,7 +176,11 @@ export class RoomPlane implements IRoomPlane
this._relativeDepth = relativeDepth; this._relativeDepth = relativeDepth;
this._isVisible = true; this._isVisible = true;
this._geometryUpdateId = geometry.updateId;
}
if(geometryChanged)
{
Randomizer.setSeed(this._randomSeed); Randomizer.setSeed(this._randomSeed);
let width = (this._leftSide.length * RoomPlane.PLANE_GEOMETRY.scale); let width = (this._leftSide.length * RoomPlane.PLANE_GEOMETRY.scale);
@ -200,8 +217,6 @@ export class RoomPlane implements IRoomPlane
const planeData = getTextureAndColorForPlane(this._id, this._type); const planeData = getTextureAndColorForPlane(this._id, this._type);
const texture = this._hasTexture ? planeData.texture ?? Texture.WHITE : Texture.WHITE; const texture = this._hasTexture ? planeData.texture ?? Texture.WHITE : Texture.WHITE;
let planeSprite: TilingSprite = null;
switch(this._type) switch(this._type)
{ {
case RoomPlane.TYPE_FLOOR: { case RoomPlane.TYPE_FLOOR: {
@ -230,7 +245,7 @@ export class RoomPlane implements IRoomPlane
while(y < 0) y += texture.height; while(y < 0) y += texture.height;
} }
planeSprite = new TilingSprite({ this._planeSprite = new TilingSprite({
texture, texture,
width, width,
height, height,
@ -254,7 +269,7 @@ export class RoomPlane implements IRoomPlane
height = Math.round(Math.abs((_local_8.y - _local_9.y))); height = Math.round(Math.abs((_local_8.y - _local_9.y)));
} }
planeSprite = new TilingSprite({ this._planeSprite = new TilingSprite({
texture, texture,
width, width,
height, height,
@ -284,7 +299,7 @@ export class RoomPlane implements IRoomPlane
const renderOffsetX = Math.trunc(this._textureOffsetX * Math.abs((_local_13.x - _local_15.x))); const renderOffsetX = Math.trunc(this._textureOffsetX * Math.abs((_local_13.x - _local_15.x)));
const renderOffsetY = Math.trunc(this._textureOffsetY * Math.abs((_local_13.y - _local_14.y))); const renderOffsetY = Math.trunc(this._textureOffsetY * Math.abs((_local_13.y - _local_14.y)));
planeSprite = new TilingSprite({ this._planeSprite = new TilingSprite({
texture, texture,
width: width, width: width,
height: height, height: height,
@ -298,7 +313,7 @@ export class RoomPlane implements IRoomPlane
break; break;
} }
default: { default: {
planeSprite = new TilingSprite({ this._planeSprite = new TilingSprite({
texture: Texture.WHITE, texture: Texture.WHITE,
width: width, width: width,
height: height, height: height,
@ -307,9 +322,39 @@ export class RoomPlane implements IRoomPlane
} }
} }
this.updateMask(planeSprite, geometry); this._planeSprite.allowChildren = true;
}
this._planeTexture = TextureUtils.createAndWriteRenderTexture(this._width, this._height, planeSprite, this.getMatrixForDimensions(width, height)) as Texture; const maskChanged = this._maskChanged;
if(maskChanged)
{
this._planeSprite.removeChildren();
this.updateMask(this._planeSprite, geometry);
}
if(this._planeTexture)
{
if(this._planeTexture.width !== this._width || this._planeTexture.height !== this._height)
{
this._planeTexture.destroy(true);
this._planeTexture = null;
}
}
if(!this._planeTexture) this._planeTexture = TextureUtils.createRenderTexture(this._width, this._height);
if(geometryChanged || maskChanged)
{
GetRenderer().render({
target: this._planeTexture,
container: this._planeSprite,
clear: true,
transform: this.getMatrixForDimensions(this._planeSprite.width, this._planeSprite.height)
});
}
return true; return true;
} }
@ -483,22 +528,12 @@ export class RoomPlane implements IRoomPlane
if(maskChanged) this._maskChanged = false; if(maskChanged) this._maskChanged = false;
} }
private updateMask(sprite: Container, geometry: IRoomGeometry): void private updateMask(container: Container, geometry: IRoomGeometry): void
{ {
if(!sprite || !geometry) return; if(!container || !geometry || !this._useMask || (!this._bitmapMasks.length && !this._rectangleMasks.length) || !this._maskManager) return;
if(!this._useMask || ((!this._bitmapMasks.length && !this._rectangleMasks.length) && !this._maskChanged) || !this._maskManager) return;
this.updateMaskChangeStatus(); this.updateMaskChangeStatus();
/* if(!this._maskBitmapData || (this._maskBitmapData.width !== width) || (this._maskBitmapData.height !== height))
{
this._maskBitmapData = this._textureCache.createAndFillRenderTexture(width, height, 'mask');
this._maskChanged = true;
} */
if(this._maskChanged)
{
this._bitmapMasksOld = []; this._bitmapMasksOld = [];
this._rectangleMasksOld = []; this._rectangleMasksOld = [];
@ -516,10 +551,10 @@ export class RoomPlane implements IRoomPlane
if(mask) if(mask)
{ {
type = mask.type; type = mask.type;
posX = (sprite.width - ((sprite.width * mask.leftSideLoc) / this._leftSide.length)); posX = (container.width - ((container.width * mask.leftSideLoc) / this._leftSide.length));
posY = (sprite.height - ((sprite.height * mask.rightSideLoc) / this._rightSide.length)); posY = (container.height - ((container.height * mask.rightSideLoc) / this._rightSide.length));
this._maskManager.updateMask(sprite, type, geometry.scale, normal, posX, posY); this._maskManager.addMaskToContainer(container, type, geometry.scale, normal, posX, posY);
this._bitmapMasksOld.push(new RoomPlaneBitmapMask(type, mask.leftSideLoc, mask.rightSideLoc)); this._bitmapMasksOld.push(new RoomPlaneBitmapMask(type, mask.leftSideLoc, mask.rightSideLoc));
} }
@ -534,11 +569,11 @@ export class RoomPlane implements IRoomPlane
if(rectMask) if(rectMask)
{ {
posX = (sprite.width - ((sprite.width * rectMask.leftSideLoc) / this._leftSide.length)); posX = (container.width - ((container.width * rectMask.leftSideLoc) / this._leftSide.length));
posY = (sprite.height - ((sprite.height * rectMask.rightSideLoc) / this._rightSide.length)); posY = (container.height - ((container.height * rectMask.rightSideLoc) / this._rightSide.length));
const wd = ((sprite.width * rectMask.leftSideLength) / this._leftSide.length); const wd = ((container.width * rectMask.leftSideLength) / this._leftSide.length);
const ht = ((sprite.height * rectMask.rightSideLength) / this._rightSide.length); const ht = ((container.height * rectMask.rightSideLength) / this._rightSide.length);
const maskSprite = new Sprite(Texture.WHITE); const maskSprite = new Sprite(Texture.WHITE);
@ -547,7 +582,7 @@ export class RoomPlane implements IRoomPlane
maskSprite.height = ht; maskSprite.height = ht;
maskSprite.position.set((posX - wd), (posY - ht)); maskSprite.position.set((posX - wd), (posY - ht));
//this._textureCache.writeToRenderTexture(sprite, this._maskBitmapData, false); container.addChild(maskSprite);
this._rectangleMasksOld.push(new RoomPlaneRectangleMask(rectMask.leftSideLength, rectMask.rightSideLoc, rectMask.leftSideLength, rectMask.rightSideLength)); this._rectangleMasksOld.push(new RoomPlaneRectangleMask(rectMask.leftSideLength, rectMask.rightSideLoc, rectMask.leftSideLength, rectMask.rightSideLength));
} }
@ -556,9 +591,10 @@ export class RoomPlane implements IRoomPlane
} }
this._maskChanged = false; this._maskChanged = false;
}
//this.combineTextureMask(canvas, this._maskPixels); container.filterArea = container.getBounds().rectangle;
container.filters = [ new PlaneMaskFilter({}) ];
} }
public get canBeVisible(): boolean public get canBeVisible(): boolean

View File

@ -1,6 +1,6 @@
import { IAssetPlaneMaskData, IAssetPlaneTextureBitmap, IGraphicAssetCollection, IVector3D } from '@nitrots/api'; import { IAssetPlaneMaskData, IAssetPlaneTextureBitmap, IGraphicAssetCollection, IVector3D } from '@nitrots/api';
import { CutMaskFilter } from '@nitrots/utils'; import { GetRenderer } from '@nitrots/utils';
import { Container, Matrix, Point } from 'pixi.js'; import { Container, Matrix, Point, Sprite, Texture } from 'pixi.js';
import { PlaneMask } from './PlaneMask'; import { PlaneMask } from './PlaneMask';
import { PlaneMaskVisualization } from './PlaneMaskVisualization'; import { PlaneMaskVisualization } from './PlaneMaskVisualization';
@ -141,7 +141,7 @@ export class PlaneMaskManager
return graphicName; return graphicName;
} }
public updateMask(sprite: Container, type: string, scale: number, normal: IVector3D, posX: number, posY: number): boolean public addMaskToContainer(container: Container, type: string, scale: number, normal: IVector3D, posX: number, posY: number): boolean
{ {
const mask = this._masks.get(type); const mask = this._masks.get(type);
@ -157,14 +157,6 @@ export class PlaneMaskManager
const point = new Point((posX + asset.offsetX), (posY + asset.offsetY)); const point = new Point((posX + asset.offsetX), (posY + asset.offsetY));
const filter = new CutMaskFilter({
maskTexture: texture
});
sprite.filters = [filter];
return true;
const matrix = new Matrix(); const matrix = new Matrix();
let xScale = 1; let xScale = 1;
@ -193,7 +185,65 @@ export class PlaneMaskManager
matrix.scale(xScale, ySkew); matrix.scale(xScale, ySkew);
matrix.translate(tx, ty); matrix.translate(tx, ty);
//TextureUtils.writeToTexture(new Sprite(texture), canvas, false, matrix); const sprite = new Sprite(texture);
sprite.setFromMatrix(matrix);
container.addChild(sprite);
return true;
}
public writeMaskToTexture(targetTexture: Texture, type: string, scale: number, normal: IVector3D, posX: number, posY: number): boolean
{
const mask = this._masks.get(type);
if(!mask) return true;
const asset = mask.getGraphicAsset(scale, normal);
if(!asset) return true;
const texture = asset.texture;
if(!texture) return true;
const point = new Point((posX + asset.offsetX), (posY + asset.offsetY));
const matrix = new Matrix();
let xScale = 1;
let ySkew = 1;
let xSkew = 0;
let yScale = 0;
let tx = (point.x + xSkew);
let ty = (point.y + yScale);
if(asset.flipH)
{
xScale = -1;
xSkew = texture.width;
tx = ((point.x + xSkew) - texture.width);
}
if(asset.flipV)
{
ySkew = -1;
yScale = texture.height;
ty = ((point.y + yScale) - texture.height);
}
matrix.scale(xScale, ySkew);
matrix.translate(tx, ty);
GetRenderer().render({
target: targetTexture,
container: new Sprite(texture),
clear: false,
transform: matrix
});
return true; return true;
} }

View File

@ -1,27 +1,20 @@
import { Filter, FilterSystem, GlProgram, RenderSurface, Texture } from 'pixi.js'; import { Filter, FilterSystem, GlProgram, RenderSurface, Texture } from 'pixi.js';
export interface CutTransparentAreaFilterOptions export interface PlaneMaskFilterOptions
{ {
maskTexture: Texture;
} }
export class CutMaskFilter extends Filter export class PlaneMaskFilter extends Filter
{ {
public static readonly DEFAULT_OPTIONS: CutTransparentAreaFilterOptions = { public static readonly DEFAULT_OPTIONS: PlaneMaskFilterOptions = {
maskTexture: Texture.WHITE,
}; };
public uniforms: { public uniforms: {
uColor: Float32Array;
uAlpha: number;
uDimensions: Float32Array;
}; };
constructor(options: CutTransparentAreaFilterOptions) constructor(options: PlaneMaskFilterOptions)
{ {
options = { ...CutMaskFilter.DEFAULT_OPTIONS, ...options }; options = { ...PlaneMaskFilter.DEFAULT_OPTIONS, ...options };
if(!options.maskTexture) throw Error('No texture provided for mask filter');
const glProgram = GlProgram.from({ const glProgram = GlProgram.from({
vertex: `in vec2 aPosition; vertex: `in vec2 aPosition;
@ -51,33 +44,36 @@ export class CutMaskFilter extends Filter
gl_Position = filterVertexPosition(); gl_Position = filterVertexPosition();
vTextureCoord = filterTextureCoord(); vTextureCoord = filterTextureCoord();
}`, }`,
fragment: `varying vec2 vTextureCoord; fragment: `
in vec2 vTextureCoord;
out vec4 finalColor;
uniform sampler2D uSampler; // The sprite's texture uniform sampler2D uTexture;
uniform sampler2D maskTexture; // The mask texture
void main(void) { void main(void) {
vec4 spriteColor = texture2D(uSampler, vTextureCoord); vec4 c = texture(uTexture, vTextureCoord);
vec4 maskColor = texture2D(maskTexture, vTextureCoord);
// Use mask alpha to determine the transparency if(c.r == 0.0 && c.g == 0.0 && c.b == 0.0) {
float alpha = maskColor.a; finalColor = vec4(0.0, 0.0, 0.0, 0.0);
} else {
// Apply the mask based on its alpha value finalColor = c;
// You can modify this logic to suit your masking criteria }
gl_FragColor = vec4(spriteColor.rgb, spriteColor.a * (1.0 - alpha)); }
}`, `,
name: 'cut-mask-filter', name: 'plane-mask-filter',
}); });
super({ super({
gpuProgram: null, gpuProgram: null,
glProgram, glProgram,
resources: { resources: {
maskTexture: options.maskTexture planeMaskUniforms: {
},
}, },
}); });
this.uniforms = this.resources.planeMaskUniforms.uniforms;
Object.assign(this, options); Object.assign(this, options);
} }
@ -88,6 +84,7 @@ export class CutMaskFilter extends Filter
clearMode: boolean, clearMode: boolean,
): void ): void
{ {
filterManager.applyFilter(this, input, output, clearMode); filterManager.applyFilter(this, input, output, clearMode);
} }
} }

View File

@ -1 +1 @@
export * from './CutMaskFilter'; export * from './PlaneMaskFilter';