Compare commits

...

10 Commits

Author SHA1 Message Date
Divinezx 6faf8980f7
Merge branch 'billsonnn:v2' into v2 2024-04-01 15:05:35 +01:00
billsonnn 8d7fc1e9b4 Updates 2024-03-30 22:53:11 -04:00
billsonnn 9a403d72db Start PaletteMapFilter 2024-03-30 22:42:10 -04:00
billsonnn 9cbdd937d2 Add wired filter 2024-03-30 21:28:36 -04:00
billsonnn 76af91a69b Various avatar updates 2024-03-30 19:58:15 -04:00
billsonnn 2a7e7e8864 Update mannequins 2024-03-30 19:58:03 -04:00
billsonnn ebf481419f Updates 2024-03-30 17:42:42 -04:00
billsonnn 49fe53e48d Add TexturePool 2024-03-30 17:41:12 -04:00
billsonnn 036a03f7d0 Updates 2024-03-30 17:39:35 -04:00
billsonnn 0a15fcd926 Update hit detection 2024-03-27 21:52:03 -04:00
37 changed files with 928 additions and 1006 deletions

View File

@ -1,6 +1,4 @@
import { IDisposable } from '../../common';
export interface IAvatarEffectListener extends IDisposable
export interface IAvatarEffectListener
{
resetEffect(effect: number): void;
}

View File

@ -1,23 +1,20 @@
import { Sprite, Texture } from 'pixi.js';
import { IGraphicAsset } from '../../asset';
import { IDisposable } from '../../common';
import { Container, Texture } from 'pixi.js';
import { IAvatarFigureContainer } from './IAvatarFigureContainer';
import { IAnimationLayerData, IAvatarDataContainer, ISpriteDataContainer } from './animation';
import { IAnimationLayerData, ISpriteDataContainer } from './animation';
import { IPartColor } from './structure';
export interface IAvatarImage extends IDisposable
export interface IAvatarImage
{
getServerRenderData(): any;
dispose(): void;
setDirection(_arg_1: string, _arg_2: number): void;
setDirectionAngle(_arg_1: string, _arg_2: number): void;
updateAnimationByFrames(_arg_1?: number): void;
getScale(): string;
getSprites(): ISpriteDataContainer[];
getLayerData(_arg_1: ISpriteDataContainer): IAnimationLayerData;
getImage(setType: string, hightlight: boolean, scale?: number, cache?: boolean): Texture;
getImageAsSprite(setType: string, scale?: number): Sprite;
getCroppedImageUrl(setType: string, scale?: number): Promise<string>;
getAsset(_arg_1: string): IGraphicAsset;
processAsTexture(setType: string, hightlight: boolean, texture?: Texture): Texture;
processAsImageUrl(setType: string): string;
processAsContainer(setType: string): Container;
getDirection(): number;
getFigure(): IAvatarFigureContainer;
getPartColor(_arg_1: string): IPartColor;
@ -26,10 +23,7 @@ export interface IAvatarImage extends IDisposable
initActionAppends(): void;
endActionAppends(): void;
appendAction(_arg_1: string, ..._args: any[]): boolean;
avatarSpriteData: IAvatarDataContainer;
isPlaceholder(): boolean;
forceActionUpdate(): void;
animationHasResetOnToggle: boolean;
resetAnimationFrameCounter(): void;
mainAction: string;
}

View File

@ -4,7 +4,7 @@ import { IRoomCameraWidgetSelectedEffect } from './IRoomCameraWidgetSelectedEffe
export interface IRoomCameraWidgetManager
{
init(): void;
init(): Promise<void>;
applyEffects(texture: Texture, selectedEffects: IRoomCameraWidgetSelectedEffect[], isZoomed: boolean): Promise<HTMLImageElement>;
effects: Map<string, IRoomCameraWidgetEffect>;
isLoaded: boolean;

View File

@ -12,8 +12,6 @@ export interface IRoomEngine
{
init(): Promise<void>;
setActiveRoomId(roomId: number): void;
disableUpdate(flag: boolean): void;
runUpdate(): void;
createRoomInstance(roomId: number, roomMap: IRoomMapData): void;
getRoomInstanceDisplay(roomId: number, id: number, width: number, height: number, scale: number): Container;
setRoomInstanceRenderingCanvasScale(roomId: number, canvasId: number, scale: number, point?: Point, offsetPoint?: Point, override?: boolean, asDelta?: boolean): void;

View File

@ -2,6 +2,8 @@ import { BLEND_MODES, Filter, Texture } from 'pixi.js';
export interface IRoomObjectSprite
{
dispose(): void;
increaseUpdateCounter(): void;
id: number;
name: string;
type: string;

View File

@ -40,6 +40,8 @@ export class AssetManager implements IAssetManager
return existing;
}
NitroLogger.warn(`AssetManager: Asset not found: ${name}`);
return null;
}
@ -76,13 +78,9 @@ export class AssetManager implements IAssetManager
{
if(!urls || !urls.length) return Promise.resolve(true);
const promises: Promise<boolean>[] = [];
for(const url of urls) promises.push(this.downloadAsset(url));
try
{
await Promise.all(promises);
await Promise.all(urls.map(url => this.downloadAsset(url)));
return true;
}
@ -112,22 +110,12 @@ export class AssetManager implements IAssetManager
const response = await fetch(url);
if(response.status !== 200) return false;
if(response.status !== 200 || !response.headers.has('Content-Type') || response.headers.get('Content-Type') !== 'application/octet-stream') return false;
let contentType = 'application/octet-stream';
const buffer = await response.arrayBuffer();
const nitroBundle = await NitroBundle.from(buffer);
if(response.headers.has('Content-Type')) contentType = response.headers.get('Content-Type');
switch(contentType)
{
case 'application/octet-stream': {
const buffer = await response.arrayBuffer();
const nitroBundle = await NitroBundle.from(buffer);
await this.processAsset(nitroBundle.texture, nitroBundle.jsonFile as IAssetData);
break;
}
}
await this.processAsset(nitroBundle.texture, nitroBundle.jsonFile as IAssetData);
return true;
}

View File

@ -3,8 +3,6 @@ import { AvatarRenderLibraryEvent, GetEventDispatcher, NitroEventType } from '@n
export class AvatarAssetDownloadLibrary implements IAvatarAssetDownloadLibrary
{
public static DOWNLOAD_COMPLETE: string = 'AADL_DOWNLOAD_COMPLETE';
private static NOT_LOADED: number = 0;
private static LOADING: number = 1;
private static LOADED: number = 2;

File diff suppressed because it is too large Load Diff

View File

@ -16,10 +16,10 @@ export class ActionDefinition implements IActionDefinition
private _startFromFrameZero: boolean;
private _prevents: string[];
private _preventHeadTurn: boolean;
private _types: Map<number, ActionType>;
private _params: Map<string, string>;
private _defaultParameterValue: string;
private _canvasOffsets: Map<string, Map<number, number[]>>;
private _types: Map<number, ActionType> = new Map();
private _params: Map<string, string> = new Map();
private _defaultParameterValue: string = '';
private _canvasOffsets: Map<string, Map<number, number[]>> = new Map();
constructor(data: any)
{
@ -36,10 +36,6 @@ export class ActionDefinition implements IActionDefinition
this._startFromFrameZero = data.startFromFrameZero || false;
this._prevents = data.prevents || [];
this._preventHeadTurn = data.preventHeadTurn || false;
this._types = new Map();
this._params = new Map();
this._defaultParameterValue = '';
this._canvasOffsets = null;
if(data.params && (data.params.length > 0))
{

View File

@ -1,4 +1,4 @@
import { AvatarDirectionAngle, AvatarFigurePartType, AvatarScaleType, GeometryType, IActiveActionData, IAvatarImage, RoomObjectSpriteData } from '@nitrots/api';
import { AvatarDirectionAngle, AvatarFigurePartType, AvatarScaleType, GeometryType, IActiveActionData, IAvatarImage } from '@nitrots/api';
import { GetTickerTime } from '@nitrots/utils';
import { Container, Matrix, Point, Rectangle, Sprite, Texture } from 'pixi.js';
import { AvatarImageBodyPartContainer } from '../AvatarImageBodyPartContainer';
@ -26,7 +26,6 @@ export class AvatarImageCache
private _geometryType: string;
private _unionImages: ImageData[];
private _matrix: Matrix;
private _serverRenderData: RoomObjectSpriteData[];
constructor(k: AvatarStructure, _arg_2: IAvatarImage, _arg_3: AssetAliasCollection, _arg_4: string)
{
@ -39,7 +38,6 @@ export class AvatarImageCache
this._disposed = false;
this._unionImages = [];
this._matrix = new Matrix();
this._serverRenderData = [];
}
public dispose(): void
@ -152,129 +150,91 @@ export class AvatarImageCache
this._canvas = null;
}
public getImageContainer(k: string, frameNumber: number, _arg_3: boolean = false): AvatarImageBodyPartContainer
public getImageContainer(key: string, frameNumber: number, forceRefresh: boolean = false): AvatarImageBodyPartContainer
{
let _local_4 = this.getBodyPartCache(k);
const bodyPartCache = this.getBodyPartCache(key) || new AvatarImageBodyPartCache();
if(!_local_4)
this._cache.set(key, bodyPartCache);
let direction = bodyPartCache.getDirection();
let action = bodyPartCache.getAction();
let adjustedFrameCount = frameNumber;
if(action.definition.startFromFrameZero) adjustedFrameCount -= action.startFrame;
let adjustedAction = action;
let removeData: string[] = [];
let items: Map<string, string> = new Map();
const positionOffset = new Point();
if(action.definition.isAnimation)
{
_local_4 = new AvatarImageBodyPartCache();
let adjustedDirection = direction;
this._cache.set(k, _local_4);
}
const animation = this._structure.getAnimation(((action.definition.state + '.') + action.actionParameter));
const animationFrameOffset = (frameNumber - action.startFrame);
let _local_5 = _local_4.getDirection();
let _local_7 = _local_4.getAction();
let frameCount = frameNumber;
if(_local_7.definition.startFromFrameZero) frameCount -= _local_7.startFrame;
let _local_8 = _local_7;
let _local_9: string[] = [];
let _local_10: Map<string, string> = new Map();
const _local_11 = new Point();
if(!((!(_local_7)) || (!(_local_7.definition))))
{
if(_local_7.definition.isAnimation)
if(animation)
{
let _local_15 = _local_5;
const layerData = animation.getLayerData(animationFrameOffset, key, action.overridingAction);
const _local_16 = this._structure.getAnimation(((_local_7.definition.state + '.') + _local_7.actionParameter));
const _local_17 = (frameNumber - _local_7.startFrame);
if(_local_16)
if(layerData)
{
const _local_18 = _local_16.getLayerData(_local_17, k, _local_7.overridingAction);
adjustedDirection = (direction + layerData.dd + 8) % 8;
if(_local_18)
positionOffset.x = this._scale === AvatarScaleType.LARGE ? layerData.dx : layerData.dx / 2;
positionOffset.y = this._scale === AvatarScaleType.LARGE ? layerData.dy : layerData.dy / 2;
adjustedFrameCount = layerData.animationFrame;
if(layerData.action) action = layerData.action;
if(layerData.type === AvatarAnimationLayerData.BODYPART)
{
_local_15 = (_local_5 + _local_18.dd);
if(layerData.action) adjustedAction = layerData.action;
if(_local_18.dd < 0)
{
if(_local_15 < 0)
{
_local_15 = (8 + _local_15);
}
else if(_local_15 > 7) _local_15 = (8 - _local_15);
}
else
{
if(_local_15 < 0)
{
_local_15 = (_local_15 + 8);
}
else if(_local_15 > 7) _local_15 = (_local_15 - 8);
}
if(this._scale === AvatarScaleType.LARGE)
{
_local_11.x = _local_18.dx;
_local_11.y = _local_18.dy;
}
else
{
_local_11.x = (_local_18.dx / 2);
_local_11.y = (_local_18.dy / 2);
}
frameCount = _local_18.animationFrame;
if(_local_18.action)
{
_local_7 = _local_18.action;
}
if(_local_18.type === AvatarAnimationLayerData.BODYPART)
{
if(_local_18.action != null)
{
_local_8 = _local_18.action;
}
_local_5 = _local_15;
}
else if(_local_18.type === AvatarAnimationLayerData.FX) _local_5 = _local_15;
_local_10 = _local_18.items;
direction = adjustedDirection;
}
else if(layerData.type === AvatarAnimationLayerData.FX) direction = adjustedDirection;
_local_9 = _local_16.removeData;
items = layerData.items;
}
removeData = animation.removeData;
}
}
let _local_12 = _local_4.getActionCache(_local_8);
let actionCache = bodyPartCache.getActionCache(adjustedAction);
if(!_local_12 || _arg_3)
if(!actionCache || forceRefresh)
{
_local_12 = new AvatarImageActionCache();
_local_4.updateActionCache(_local_8, _local_12);
actionCache = new AvatarImageActionCache();
bodyPartCache.updateActionCache(adjustedAction, actionCache);
}
let _local_13 = _local_12.getDirectionCache(_local_5);
let directionCache = actionCache.getDirectionCache(direction);
if(!_local_13 || _arg_3)
if(!directionCache || forceRefresh)
{
const _local_19 = this._structure.getParts(k, this._avatar.getFigure(), _local_8, this._geometryType, _local_5, _local_9, this._avatar, _local_10);
const partList = this._structure.getParts(key, this._avatar.getFigure(), adjustedAction, this._geometryType, direction, removeData, this._avatar, items);
_local_13 = new AvatarImageDirectionCache(_local_19);
directionCache = new AvatarImageDirectionCache(partList);
_local_12.updateDirectionCache(_local_5, _local_13);
actionCache.updateDirectionCache(direction, directionCache);
}
let _local_14 = _local_13.getImageContainer(frameCount);
let imageContainer = directionCache.getImageContainer(adjustedFrameCount);
if(!_local_14 || _arg_3)
if(!imageContainer || forceRefresh)
{
const _local_20 = _local_13.getPartList();
const partList = directionCache.getPartList();
_local_14 = this.renderBodyPart(_local_5, _local_20, frameCount, _local_7, _arg_3);
imageContainer = this.renderBodyPart(direction, partList, adjustedFrameCount, action);
if(_local_14 && !_arg_3)
if(imageContainer && !forceRefresh)
{
if(_local_14.isCacheable) _local_13.updateImageContainer(_local_14, frameCount);
if(imageContainer.isCacheable) directionCache.updateImageContainer(imageContainer, adjustedFrameCount);
}
else
{
@ -282,21 +242,14 @@ export class AvatarImageCache
}
}
const offset = this._structure.getFrameBodyPartOffset(_local_8, _local_5, frameCount, k);
const offset = this._structure.getFrameBodyPartOffset(adjustedAction, direction, adjustedFrameCount, key);
_local_11.x += offset.x;
_local_11.y += offset.y;
positionOffset.x += offset.x;
positionOffset.y += offset.y;
_local_14.offset = _local_11;
imageContainer.offset = positionOffset;
return _local_14;
}
public getServerRenderData(): any[]
{
this._serverRenderData = [];
return this._serverRenderData;
return imageContainer;
}
public getBodyPartCache(k: string): AvatarImageBodyPartCache
@ -313,7 +266,7 @@ export class AvatarImageCache
return existing;
}
private renderBodyPart(direction: number, containers: AvatarImagePartContainer[], frameCount: number, _arg_4: IActiveActionData, renderServerData: boolean = false): AvatarImageBodyPartContainer
private renderBodyPart(direction: number, containers: AvatarImagePartContainer[], frameCount: number, action: IActiveActionData): AvatarImageBodyPartContainer
{
if(!containers || !containers.length) return null;
@ -325,7 +278,7 @@ export class AvatarImageCache
}
const isFlipped = AvatarDirectionAngle.DIRECTION_IS_FLIPPED[direction] || false;
let assetPartDefinition = _arg_4.definition.assetPartDefinition;
let assetPartDefinition = action.definition.assetPartDefinition;
let isCacheable = true;
let containerIndex = (containers.length - 1);
@ -397,33 +350,6 @@ export class AvatarImageCache
if(flipH) offset.x = (offset.x + ((this._scale === AvatarScaleType.LARGE) ? 65 : 31));
if(renderServerData)
{
const spriteData = new RoomObjectSpriteData();
spriteData.name = this._assets.getAssetName(assetName);
spriteData.x = (-(offset.x) - 33);
spriteData.y = -(offset.y);
spriteData.z = (this._serverRenderData.length * -0.0001);
spriteData.width = asset.rectangle.width;
spriteData.height = asset.rectangle.height;
spriteData.flipH = flipH;
if(assetPartDefinition === 'lay') spriteData.x = (spriteData.x + 53);
if(isFlipped)
{
spriteData.flipH = (!(spriteData.flipH));
if(spriteData.flipH) spriteData.x = (-(spriteData.x) - texture.width);
else spriteData.x = (spriteData.x + 65);
}
if(container.isColorable) spriteData.color = `${color}`;
this._serverRenderData.push(spriteData);
}
this._unionImages.push(new ImageData(texture, asset.rectangle, offset, flipH, color));
}
}
@ -485,9 +411,6 @@ export class AvatarImageCache
{
if(!data) continue;
const texture = data.texture;
const color = data.colorTransform;
const flipH = (!(isFlipped && data.flipH) && (isFlipped || data.flipH));
const regPoint = point.clone();
regPoint.x -= data.regPoint.x;
@ -495,7 +418,7 @@ export class AvatarImageCache
if(isFlipped) regPoint.x = (container.width - (regPoint.x + data.rect.width));
if(flipH)
if(isFlipped != data.flipH)
{
this._matrix.a = -1;
this._matrix.tx = ((data.rect.x + data.rect.width) + regPoint.x);
@ -508,9 +431,9 @@ export class AvatarImageCache
this._matrix.ty = (regPoint.y - data.rect.y);
}
const sprite = new Sprite(texture);
const sprite = new Sprite(data.texture);
sprite.tint = color;
sprite.tint = data.colorTransform;
sprite.setFromMatrix(this._matrix);
container.addChild(sprite);

View File

@ -21,11 +21,6 @@ export class FigurePart implements IFigurePart
this._breed = -1;
}
public dispose(): void
{
}
public get id(): number
{
return this._id;

View File

@ -46,13 +46,6 @@ export class FigurePartSet implements IFigurePartSet
public dispose(): void
{
for(const part of this._parts)
{
const figurePart = part as FigurePart;
figurePart.dispose();
}
this._parts = null;
this._hiddenLayers = null;
}

View File

@ -1,4 +1,5 @@
import { IRoomCameraWidgetEffect, IRoomCameraWidgetManager, IRoomCameraWidgetSelectedEffect } from '@nitrots/api';
import { GetAssetManager } from '@nitrots/assets';
import { GetConfiguration } from '@nitrots/configuration';
import { GetEventDispatcher, RoomCameraWidgetManagerEvent } from '@nitrots/events';
import { TextureUtils } from '@nitrots/utils';
@ -16,7 +17,7 @@ export class RoomCameraWidgetManager implements IRoomCameraWidgetManager
this._isLoaded = false;
}
public init(): void
public async init(): Promise<void>
{
if(this._isLoaded) return;
@ -37,7 +38,11 @@ export class RoomCameraWidgetManager implements IRoomCameraWidgetManager
}
else
{
cameraEffect.texture = Texture.from(imagesUrl + effect.name + '.png');
const url = `${ imagesUrl }${ effect.name }.png`;
await GetAssetManager().downloadAsset(url);
cameraEffect.texture = GetAssetManager().getTexture(url);
cameraEffect.blendMode = effect.blendMode;
}

View File

@ -3,7 +3,7 @@ import { GetCommunication, RenderRoomMessageComposer, RenderRoomThumbnailMessage
import { GetConfiguration } from '@nitrots/configuration';
import { BadgeImageReadyEvent, GetEventDispatcher, NitroToolbarAnimateIconEvent, RoomBackgroundColorEvent, RoomDragEvent, RoomEngineEvent, RoomEngineObjectEvent, RoomObjectEvent, RoomObjectFurnitureActionEvent, RoomObjectMouseEvent, RoomSessionEvent, RoomToObjectOwnAvatarMoveEvent } from '@nitrots/events';
import { GetRoomSessionManager, GetSessionDataManager } from '@nitrots/session';
import { FurniId, GetTicker, GetTickerTime, NitroLogger, NumberBank, TextureUtils, Vector3d } from '@nitrots/utils';
import { FurniId, GetTickerTime, NitroLogger, NumberBank, TextureUtils, Vector3d } from '@nitrots/utils';
import { Container, Matrix, Point, Rectangle, RenderTexture, Sprite, Texture, Ticker } from 'pixi.js';
import { GetRoomContentLoader } from './GetRoomContentLoader';
import { GetRoomManager } from './GetRoomManager';
@ -93,13 +93,6 @@ export class RoomEngine implements IRoomEngine, IRoomCreator, IRoomEngineService
this._roomManager.addUpdateCategory(RoomObjectCategory.UNIT);
this._roomManager.addUpdateCategory(RoomObjectCategory.CURSOR);
this._roomManager.addUpdateCategory(RoomObjectCategory.ROOM);
GetTicker().add(this.update, this);
document.addEventListener('visibilitychange', event =>
{
if(!document.hidden) this.update(GetTicker()); // true
});
}
private onRoomSessionEvent(event: RoomSessionEvent): void
@ -633,24 +626,6 @@ export class RoomEngine implements IRoomEngine, IRoomCreator, IRoomEngineService
return this.isRoomIdPlayingGame(this._activeRoomId);
}
public disableUpdate(flag: boolean): void
{
if(flag)
{
GetTicker().remove(this.update, this);
}
else
{
GetTicker().remove(this.update, this);
GetTicker().add(this.update, this);
}
}
public runUpdate(): void
{
this.update(GetTicker());
}
public update(ticker: Ticker): void
{
if(!this._roomManager) return;

View File

@ -724,16 +724,6 @@ export class RoomPreviewer
}
}
public set disableUpdate(flag: boolean)
{
this._disableUpdate = flag;
}
public set disableRoomEngineUpdate(flag: boolean)
{
if(this.isRoomEngineReady) this._roomEngine.disableUpdate(flag);
}
private onRoomInitializedonRoomInitialized(event: RoomEngineEvent): void
{
if(!event) return;
@ -771,11 +761,6 @@ export class RoomPreviewer
}
}
public updateRoomEngine(): void
{
if(this.isRoomEngineReady) this._roomEngine.runUpdate();
}
public getRenderingCanvas(): IRoomRenderingCanvas
{
const renderingCanvas = this._roomEngine.getRoomInstanceRenderingCanvas(this._previewRoomId, RoomPreviewer.PREVIEW_CANVAS_ID);

View File

@ -36,22 +36,16 @@ export class RoomObjectSprite implements IRoomObjectSprite
public dispose(): void
{
if(this._spriteType !== RoomObjectSpriteType.DEFAULT)
{
if(this._texture)
{
//@ts-ignore
if(this._texture?.source?.hitMap) this._texture.source.hitMap = null;
this._texture.destroy(true);
}
}
this._texture = null;
this._width = 0;
this._height = 0;
}
public increaseUpdateCounter(): void
{
this._updateCounter++;
}
public get id(): number
{
return this._id;

View File

@ -1,4 +1,5 @@
import { AlphaTolerance, AvatarAction, AvatarGuideStatus, AvatarSetType, IAdvancedMap, IAvatarEffectListener, IAvatarImage, IAvatarImageListener, IGraphicAsset, IObjectVisualizationData, IRoomGeometry, IRoomObject, IRoomObjectModel, RoomObjectSpriteType, RoomObjectVariable } from '@nitrots/api';
import { GetAssetManager } from '@nitrots/assets';
import { AdvancedMap } from '@nitrots/utils';
import { Texture } from 'pixi.js';
import { RoomObjectSpriteVisualization } from '../RoomObjectSpriteVisualization';
@ -276,8 +277,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
if(sprite)
{
const highlightEnabled = ((this.object.model.getValue<number>(RoomObjectVariable.FIGURE_HIGHLIGHT_ENABLE) === 1) && (this.object.model.getValue<number>(RoomObjectVariable.FIGURE_HIGHLIGHT) === 1));
const avatarImage = this._avatarImage.getImage(AvatarSetType.FULL, highlightEnabled);
const avatarImage = this._avatarImage.processAsTexture(AvatarSetType.FULL, highlightEnabled);
if(avatarImage)
{
@ -411,7 +411,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
const assetName = ((((((this._avatarImage.getScale() + '_') + spriteData.member) + '_') + dd) + '_') + frameNumber);
const asset = this._avatarImage.getAsset(assetName);
const asset = GetAssetManager().getAsset(assetName);
if(!asset) continue;
@ -995,6 +995,14 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
private clearAvatar(): void
{
const sprite = this.getSprite(AvatarVisualization.AVATAR_LAYER_ID);
if(sprite)
{
sprite.texture = Texture.EMPTY;
sprite.alpha = 255;
}
for(const avatar of this._cachedAvatars.getValues()) avatar && avatar.dispose();
for(const avatar of this._cachedAvatarEffects.getValues()) avatar && avatar.dispose();
@ -1003,14 +1011,6 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
this._cachedAvatarEffects.reset();
this._avatarImage = null;
const sprite = this.getSprite(AvatarVisualization.AVATAR_LAYER_ID);
if(sprite)
{
sprite.texture = Texture.EMPTY;
sprite.alpha = 255;
}
}
private getAddition(id: number): IAvatarAddition
@ -1071,7 +1071,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
{
sprite.libraryAssetName = 'sh_std_sd_1_0_0';
this._shadow = this._avatarImage.getAsset(sprite.libraryAssetName);
this._shadow = GetAssetManager().getAsset(sprite.libraryAssetName);
offsetX = -8;
offsetY = ((this._canStandUp) ? 6 : -3);
@ -1080,7 +1080,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
{
sprite.libraryAssetName = 'h_std_sd_1_0_0';
this._shadow = this._avatarImage.getAsset(sprite.libraryAssetName);
this._shadow = GetAssetManager().getAsset(sprite.libraryAssetName);
offsetX = -17;
offsetY = ((this._canStandUp) ? 10 : -7);

View File

@ -7,7 +7,8 @@ export class ExpressionAddition implements IExpressionAddition
constructor(
private _id: number,
private _type: number,
private _visualization: AvatarVisualization) {}
private _visualization: AvatarVisualization)
{}
public dispose(): void
{

View File

@ -21,7 +21,8 @@ export class FloatingIdleZAddition implements IAvatarAddition
constructor(
private _id: number,
private _visualization: AvatarVisualization) {}
private _visualization: AvatarVisualization)
{}
public dispose(): void
{

View File

@ -1,5 +1,5 @@
import { AlphaTolerance, IRoomObjectSprite } from '@nitrots/api';
import { TextureUtils } from '@nitrots/utils';
import { GetTexturePool } from '@nitrots/utils';
import { Texture } from 'pixi.js';
import { IAvatarAddition } from './IAvatarAddition';
@ -13,19 +13,24 @@ export class GameClickTargetAddition implements IAvatarAddition
private _asset: Texture = null;
constructor(
private _id: number) {}
private _id: number)
{}
public dispose(): void
{
this._asset = null;
if(this._asset)
{
GetTexturePool().putTexture(this._asset);
this._asset = null;
}
}
// TODO: needs testing
public update(sprite: IRoomObjectSprite, scale: number): void
{
if(!sprite) return;
if(!this._asset) this._asset = TextureUtils.createRenderTexture(GameClickTargetAddition.WIDTH, GameClickTargetAddition.HEIGHT);
if(!this._asset) this._asset = GetTexturePool().getTexture(GameClickTargetAddition.WIDTH, GameClickTargetAddition.HEIGHT);
sprite.visible = true;
sprite.texture = this._asset;

View File

@ -12,7 +12,8 @@ export class GuideStatusBubbleAddition implements IAvatarAddition
constructor(
private _id: number,
private _visualization: AvatarVisualization,
private _status: number) {}
private _status: number)
{}
public dispose(): void
{

View File

@ -10,7 +10,8 @@ export class MutedBubbleAddition implements IAvatarAddition
constructor(
private _id: number,
private _visualization: AvatarVisualization) {}
private _visualization: AvatarVisualization)
{}
public dispose(): void
{

View File

@ -15,7 +15,8 @@ export class NumberBubbleAddition implements IAvatarAddition
constructor(
private _id: number,
private _number: number,
private _visualization: AvatarVisualization) {}
private _visualization: AvatarVisualization)
{}
public dispose(): void
{

View File

@ -11,7 +11,8 @@ export class TypingBubbleAddition implements IAvatarAddition
constructor(
private _id: number,
private _visualization: AvatarVisualization) {}
private _visualization: AvatarVisualization)
{}
public dispose(): void
{

View File

@ -1,35 +1,20 @@
import { AvatarSetType, IAvatarImageListener, IObjectVisualizationData, RoomObjectVariable } from '@nitrots/api';
import { AvatarSetType, IAvatarImage, IAvatarImageListener, IGraphicAsset, IObjectVisualizationData, RoomObjectVariable } from '@nitrots/api';
import { Texture } from 'pixi.js';
import { FurnitureMannequinVisualizationData } from './FurnitureMannequinVisualizationData';
import { FurnitureVisualization } from './FurnitureVisualization';
export class FurnitureMannequinVisualization extends FurnitureVisualization implements IAvatarImageListener
{
private static AVATAR_IMAGE_SPRITE_TAG: string = 'avatar_image';
private _mannequinScale: number;
private _figure: string;
private _gender: string;
private _dynamicAssetName: string;
private _needsUpdate: boolean;
private _placeHolderFigure: string;
private _disposed: boolean;
constructor()
{
super();
this._mannequinScale = -1;
this._figure = null;
this._gender = null;
this._dynamicAssetName = null;
this._needsUpdate = false;
this._placeHolderFigure = 'hd-99999-99998';
this._disposed = false;
}
private _mannequinScale: number = -1;
private _figure: string = null;
private _gender: string = null;
private _avatarImage: IAvatarImage = null;
private _avatarWidth: number = 90;
private _avatarHeight: number = 130;
private _needsUpdate: boolean = false;
private _placeHolderFigure: string = 'hd-99999-99998';
private _disposed: boolean = false;
public initialize(data: IObjectVisualizationData): boolean
{
@ -44,11 +29,11 @@ export class FurnitureMannequinVisualization extends FurnitureVisualization impl
this._disposed = true;
if(this._dynamicAssetName && this.asset)
if(this._avatarImage)
{
this.asset.disposeAsset(this._dynamicAssetName);
this._avatarImage.dispose();
this._dynamicAssetName = null;
this._avatarImage = null;
}
super.dispose();
@ -81,7 +66,7 @@ export class FurnitureMannequinVisualization extends FurnitureVisualization impl
if(figure)
{
this._figure = (figure + '.' + this._placeHolderFigure);
this._figure = `${ figure }.${ this._placeHolderFigure }`;
this._gender = (this.object.model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_GENDER) || null);
this.updateAvatar();
@ -95,60 +80,30 @@ export class FurnitureMannequinVisualization extends FurnitureVisualization impl
return updateModel;
}
private updateAvatar(forceUpdate: boolean = false): void
private updateAvatar(): void
{
if(!this.avatarExists() || forceUpdate)
if(this._avatarImage)
{
const avatarImage = this.data.createAvatarImage(this._figure, this._mannequinScale, this._gender, this);
this._avatarImage.dispose();
if(avatarImage)
{
avatarImage.setDirection(AvatarSetType.FULL, this.direction);
if(this._dynamicAssetName) this.asset.disposeAsset(this._dynamicAssetName);
this.asset.addAsset(this.getAvatarAssetName(), avatarImage.getImage(AvatarSetType.FULL, false, 1, false), true);
this._dynamicAssetName = this.getAvatarAssetName();
this._needsUpdate = true;
avatarImage.dispose();
}
this._avatarImage = null;
}
}
private avatarExists(): boolean
{
return (this._figure && (this.getAsset(this.getAvatarAssetName()) !== null));
}
private getAvatarAssetName(): string
{
return (((((('mannequin_' + this._figure) + '_') + this._mannequinScale) + '_') + this.direction) + '_') + this.object.id;
this._avatarImage = this.data.createAvatarImage(this._figure, this._mannequinScale, this._gender, this);
}
public resetFigure(figure: string): void
{
if(figure === this._figure) this.updateAvatar(true);
}
this.updateAvatar();
protected getSpriteAssetName(scale: number, layerId: number): string
{
const tag = this.getLayerTag(scale, this.direction, layerId);
if(this._figure && (tag === FurnitureMannequinVisualization.AVATAR_IMAGE_SPRITE_TAG) && this.avatarExists())
{
return this.getAvatarAssetName();
}
return super.getSpriteAssetName(scale, layerId);
this._needsUpdate = true;
}
protected getLayerXOffset(scale: number, direction: number, layerId: number): number
{
const tag = this.getLayerTag(scale, direction, layerId);
if((tag === FurnitureMannequinVisualization.AVATAR_IMAGE_SPRITE_TAG) && this.avatarExists()) return (-(this.getSprite(layerId).width) / 2);
if((tag === FurnitureMannequinVisualization.AVATAR_IMAGE_SPRITE_TAG) && this._avatarImage) return (-(this._avatarWidth) / 3);
return super.getLayerXOffset(scale, direction, layerId);
}
@ -157,11 +112,25 @@ export class FurnitureMannequinVisualization extends FurnitureVisualization impl
{
const tag = this.getLayerTag(scale, direction, layerId);
if((tag === FurnitureMannequinVisualization.AVATAR_IMAGE_SPRITE_TAG) && this.avatarExists()) return -(this.getSprite(layerId).height);
if((tag === FurnitureMannequinVisualization.AVATAR_IMAGE_SPRITE_TAG) && this._avatarImage) return (-(this._avatarHeight) / 3);
return super.getLayerYOffset(scale, direction, layerId);
}
public getTexture(scale: number, layerId: number, asset: IGraphicAsset): Texture
{
const tag = this.getLayerTag(scale, this.direction, layerId);
if((tag === FurnitureMannequinVisualization.AVATAR_IMAGE_SPRITE_TAG) && this._avatarImage)
{
this._avatarImage.setDirection(AvatarSetType.FULL, this.direction);
return this._avatarImage.processAsTexture(AvatarSetType.FULL, false);
}
return super.getTexture(scale, layerId, asset);
}
public get disposed(): boolean
{
return this._disposed;

View File

@ -1,5 +1,5 @@
import { AlphaTolerance, IGraphicAsset, IObjectVisualizationData, IRoomGeometry, IRoomObjectSprite, RoomObjectVariable, RoomObjectVisualizationType } from '@nitrots/api';
import { BLEND_MODES } from 'pixi.js';
import { BLEND_MODES, Texture } from 'pixi.js';
import { RoomObjectSpriteVisualization } from '../RoomObjectSpriteVisualization';
import { ColorData, LayerData } from '../data';
import { FurnitureVisualizationData } from './FurnitureVisualizationData';
@ -279,11 +279,11 @@ export class FurnitureVisualization extends RoomObjectSpriteVisualization
{
const assetData = this.getAsset(assetName, layerId);
if(assetData && assetData.texture)
if(assetData)
{
sprite.visible = true;
sprite.type = this._type;
sprite.texture = assetData.texture;
sprite.texture = this.getTexture(scale, layerId, assetData);
sprite.flipH = assetData.flipH;
sprite.flipV = assetData.flipV;
sprite.direction = this._direction;
@ -577,6 +577,11 @@ export class FurnitureVisualization extends RoomObjectSpriteVisualization
return this.asset.getAsset(name);
}
public getTexture(scale: number, layerId: number, asset: IGraphicAsset): Texture
{
return asset?.texture ?? null;
}
protected get direction(): number
{
return this._direction;

View File

@ -1,6 +1,6 @@
import { IAssetPlaneVisualizationLayer, IAssetRoomVisualizationData, IRoomGeometry, IRoomPlane, IVector3D } from '@nitrots/api';
import { GetAssetManager } from '@nitrots/assets';
import { GetRenderer, PlaneMaskFilter, TextureUtils, Vector3d } from '@nitrots/utils';
import { GetRenderer, GetTexturePool, PlaneMaskFilter, Vector3d } from '@nitrots/utils';
import { Container, Filter, Matrix, Point, Sprite, Texture, TilingSprite } from 'pixi.js';
import { RoomGeometry } from '../../../utils';
import { RoomPlaneBitmapMask } from './RoomPlaneBitmapMask';
@ -112,9 +112,7 @@ export class RoomPlane implements IRoomPlane
if(this._planeTexture)
{
//@ts-ignore
if(this._planeTexture.source?.hitMap) this._planeTexture.source.hitMap = null;
this._planeTexture.destroy(true);
GetTexturePool().putTexture(this._planeTexture);
this._planeTexture = null;
}
@ -336,13 +334,13 @@ export class RoomPlane implements IRoomPlane
{
if(this._planeTexture.width !== this._width || this._planeTexture.height !== this._height)
{
this._planeTexture.destroy(true);
GetTexturePool().putTexture(this._planeTexture);
this._planeTexture = null;
}
}
if(!this._planeTexture) this._planeTexture = TextureUtils.createRenderTexture(this._width, this._height);
if(!this._planeTexture) this._planeTexture = GetTexturePool().getTexture(this._width, this._height);
this._planeTexture.source.label = `room_plane_${ this._uniqueId.toString() }`;

View File

@ -124,7 +124,7 @@ export class RoomRenderer implements IRoomRenderer, IRoomSpriteCanvasContainer
private createSpriteCanvas(id: number, width: number, height: number, scale: number): IRoomRenderingCanvas
{
return new RoomSpriteCanvas(this, id, width, height, scale);
return new RoomSpriteCanvas(id, this, width, height, scale);
}
public removeCanvas(id: number): void

View File

@ -2,48 +2,45 @@ import { IRoomCanvasMouseListener, IRoomGeometry, IRoomObject, IRoomObjectSprite
import { GetConfiguration } from '@nitrots/configuration';
import { RoomSpriteMouseEvent } from '@nitrots/events';
import { GetTicker, TextureUtils, Vector3d } from '@nitrots/utils';
import { Container, Graphics, Matrix, Point, Rectangle, Texture } from 'pixi.js';
import { Container, Matrix, Point, Rectangle, Sprite, Texture } from 'pixi.js';
import { RoomEnterEffect, RoomGeometry, RoomRotatingEffect, RoomShakingEffect } from '../utils';
import { RoomObjectCache, RoomObjectCacheItem } from './cache';
import { ExtendedSprite, ObjectMouseData, SortableSprite } from './utils';
export class RoomSpriteCanvas implements IRoomRenderingCanvas
{
private _id: number;
private _container: IRoomSpriteCanvasContainer;
private _geometry: RoomGeometry;
private _animationFPS: number;
private _renderTimestamp: number;
private _totalTimeRunning: number;
private _lastFrame: number;
private _renderTimestamp: number = 0;
private _totalTimeRunning: number = 0;
private _lastFrame: number = 0;
private _master: Container;
private _display: Container;
private _mask: Graphics;
private _master: Container = null;
private _display: Container = null;
private _mask: Sprite = null;
private _sortableSprites: SortableSprite[];
private _spriteCount: number;
private _activeSpriteCount: number;
private _spritePool: ExtendedSprite[];
private _skipObjectUpdate: boolean;
private _runningSlow: boolean;
private _sortableSprites: SortableSprite[] = [];
private _spriteCount: number = 0;
private _activeSpriteCount: number = 0;
private _spritePool: ExtendedSprite[] = [];
private _skipObjectUpdate: boolean = false;
private _runningSlow: boolean = false;
private _width: number;
private _height: number;
private _renderedWidth: number;
private _renderedHeight: number;
private _screenOffsetX: number;
private _screenOffsetY: number;
private _mouseLocation: Point;
private _mouseOldX: number;
private _mouseOldY: number;
private _mouseCheckCount: number;
private _mouseSpriteWasHit: boolean;
private _mouseActiveObjects: Map<string, ObjectMouseData>;
private _eventCache: Map<string, IRoomSpriteMouseEvent>;
private _eventId: number;
private _scale: number;
private _width: number = 0;
private _height: number = 0;
private _renderedWidth: number = 0;
private _renderedHeight: number = 0;
private _screenOffsetX: number = 0;
private _screenOffsetY: number = 0;
private _mouseLocation: Point = new Point();
private _mouseOldX: number = 0;
private _mouseOldY: number = 0;
private _mouseCheckCount: number = 0;
private _mouseSpriteWasHit: boolean = false;
private _mouseActiveObjects: Map<string, ObjectMouseData> = new Map();
private _eventCache: Map<string, IRoomSpriteMouseEvent> = new Map();
private _eventId: number = 0;
private _scale: number = 1;
private _SafeStr_4507: boolean = false;
private _rotation: number = 0;
@ -53,62 +50,26 @@ export class RoomSpriteCanvas implements IRoomRenderingCanvas
private _effectLocation: Vector3d;
private _SafeStr_795: number = 0;
private _noSpriteVisibilityChecking: boolean;
private _usesExclusionRectangles: boolean;
private _usesMask: boolean;
private _canvasUpdated: boolean;
private _noSpriteVisibilityChecking: boolean = false;
private _usesExclusionRectangles: boolean = false;
private _usesMask: boolean = true;
private _canvasUpdated: boolean = false;
private _objectCache: RoomObjectCache;
private _mouseListener: IRoomCanvasMouseListener;
private _mouseListener: IRoomCanvasMouseListener = null;
constructor(container: IRoomSpriteCanvasContainer, id: number, width: number, height: number, scale: number)
constructor(
private _id: number,
private _container: IRoomSpriteCanvasContainer,
width: number,
height: number,
scale: number)
{
this._id = id;
this._container = container;
this._geometry = new RoomGeometry(scale, new Vector3d(-135, 30, 0), new Vector3d(11, 11, 5), new Vector3d(-135, 0.5, 0));
this._animationFPS = GetConfiguration().getValue<number>('system.fps.animation', 24);
this._renderTimestamp = 0;
this._totalTimeRunning = 0;
this._lastFrame = 0;
this._master = null;
this._display = null;
this._mask = null;
this._sortableSprites = [];
this._spriteCount = 0;
this._activeSpriteCount = 0;
this._spritePool = [];
this._skipObjectUpdate = false;
this._runningSlow = false;
this._width = 0;
this._height = 0;
this._renderedWidth = 0;
this._renderedHeight = 0;
this._screenOffsetX = 0;
this._screenOffsetY = 0;
this._mouseLocation = new Point;
this._mouseOldX = 0;
this._mouseOldY = 0;
this._mouseCheckCount = 0;
this._mouseSpriteWasHit = false;
this._mouseActiveObjects = new Map();
this._eventCache = new Map();
this._eventId = 0;
this._scale = 1;
this._noSpriteVisibilityChecking = false;
this._usesExclusionRectangles = false;
this._usesMask = true;
this._canvasUpdated = false;
this._objectCache = new RoomObjectCache(this._container.roomObjectVariableAccurateZ);
this._mouseListener = null;
this.setupCanvas();
this.initialize(width, height);
}
@ -123,8 +84,13 @@ export class RoomSpriteCanvas implements IRoomRenderingCanvas
{
const display = new Container();
display.isRenderGroup = true;
display.cullableChildren = false;
display.interactive = false;
display.interactiveChildren = false;
this._master.addChild(display);
this._display = display;
@ -206,9 +172,11 @@ export class RoomSpriteCanvas implements IRoomRenderingCanvas
{
if(!this._mask)
{
this._mask = new Graphics()
.rect(0, 0, width, height)
.fill(0xFF0000);
this._mask = new Sprite(Texture.WHITE);
this._mask.tint = 0xFF0000;
this._mask.width = width;
this._mask.height = height;
if(this._master)
{
@ -219,27 +187,13 @@ export class RoomSpriteCanvas implements IRoomRenderingCanvas
}
else
{
this._mask
.clear()
.rect(0, 0, width, height)
.fill(0xFF0000);
this._mask.width = width;
this._mask.height = height;
}
}
if(this._master)
{
/* if(this._master.hitArea)
{
const hitArea = (this._master.hitArea as Rectangle);
hitArea.width = width;
hitArea.height = height;
}
else
{
this._master.hitArea = new Rectangle(0, 0, width, height);
} */
if(this._master.filterArea)
{
const filterArea = this._master.filterArea;

View File

@ -1,6 +1,8 @@
import { AlphaTolerance } from '@nitrots/api';
import { GetRenderer } from '@nitrots/utils';
import { Point, Sprite, Texture, TextureSource } from 'pixi.js';
import { GlRenderTarget, Point, Sprite, Texture, TextureSource, WebGLRenderer } from 'pixi.js';
const BYTES_PER_PIXEL = 4;
export class ExtendedSprite extends Sprite
{
@ -77,14 +79,31 @@ export class ExtendedSprite extends Sprite
{
if(!textureSource) return false;
const texture = new Texture(textureSource);
const canvas = GetRenderer().texture.generateCanvas(texture);
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const width = Math.max(Math.round(textureSource.width * textureSource.resolution), 1);
const height = Math.max(Math.round(textureSource.height * textureSource.resolution), 1);
const pixels = new Uint8Array(BYTES_PER_PIXEL * width * height);
const renderer = GetRenderer() as WebGLRenderer;
const renderTarget = renderer.renderTarget.getRenderTarget(textureSource);
const glRenterTarget = renderer.renderTarget.getGpuRenderTarget(renderTarget) as GlRenderTarget;
const gl = renderer.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, glRenterTarget.resolveTargetFramebuffer);
gl.readPixels(
0,
0,
width,
height,
gl.RGBA,
gl.UNSIGNED_BYTE,
pixels
);
//@ts-ignore
textureSource.hitMap = imageData.data;
texture.destroy();
textureSource.hitMap = pixels;
return true;
}

View File

@ -0,0 +1,5 @@
import { TexturePool } from './TexturePool';
const texturePool = new TexturePool();
export const GetTexturePool = () => texturePool;

View File

@ -0,0 +1,105 @@
import { NitroLogger, TextureUtils } from '@nitrots/utils';
import { Texture } from 'pixi.js';
export class TexturePool
{
private static MAX_IDLE: number = 3600;
private _textures: { [index: string]: { [index: string]: Texture[] } } = {};
private _totalTextures: number = 0;
private _runCount: number = 0;
public getTotalTextures(): number
{
let total = 0;
for(const width in this._textures)
{
for(const height in this._textures[width])
{
total += this._textures[width][height].length;
}
}
this._totalTextures = total;
return this._totalTextures;
}
public getTexture(width: number, height: number): Texture
{
if(!this._textures[width]) this._textures[width] = {};
if(!this._textures[width][height]) this._textures[width][height] = [];
if(this._textures[width][height].length)
{
const texture = this._textures[width][height].shift();
if(texture)
{
this._totalTextures--;
return texture;
}
}
return TextureUtils.createRenderTexture(width, height);
}
public putTexture(texture: Texture)
{
if(!texture) return;
if(!this._textures[texture.width]) this._textures[texture.width] = {};
if(!this._textures[texture.width][texture.height]) this._textures[texture.width][texture.height] = [];
//@ts-ignore
delete texture.source.hitMap;
this._textures[texture.width][texture.height].push(texture);
this._totalTextures++;
}
public run(): void
{
this._runCount++;
if(!this._totalTextures) return;
for(const width in this._textures)
{
for(const height in this._textures[width])
{
const textures = this._textures[width][height];
for(let i = textures.length - 1; i >= 0; i--)
{
const texture = textures[i];
const source = texture.source;
if((source._touched > -1) && (this._runCount - source._touched) > TexturePool.MAX_IDLE)
{
//@ts-ignore
delete texture.source.hitMap;
if(!source.destroyed) texture.destroy(true);
this._textures[texture.width][texture.height].splice(i, 1);
this._totalTextures--;
NitroLogger.log(`[TexturePool] Texture disposed: ${texture.width}x${texture.height}`);
}
}
}
}
}
public get textures(): { [index: string]: { [index: string]: Texture[] } }
{
return this._textures;
}
}

View File

@ -0,0 +1,147 @@
import { BufferImageSource, Filter, FilterSystem, GlProgram, RenderSurface, Texture } from 'pixi.js';
import { TextureUtils } from '../TextureUtils';
export interface PaletteMapFilterOptions
{
palette: number[];
channel: number;
}
export class PaletteMapFilter extends Filter
{
public static readonly CHANNEL_RED = 0;
public static readonly CHANNEL_GREEN = 1;
public static readonly CHANNEL_BLUE = 2;
public static readonly CHANNEL_ALPHA = 3;
public static readonly DEFAULT_OPTIONS: PaletteMapFilterOptions = {
palette: [],
channel: PaletteMapFilter.CHANNEL_RED,
};
public uniforms: {
uPalette: Float32Array,
uChannel: Float32Array
};
constructor(options: PaletteMapFilterOptions)
{
options = { ...PaletteMapFilter.DEFAULT_OPTIONS, ...options };
const glProgram = GlProgram.from({
vertex: `in vec2 aPosition;
out vec2 vTextureCoord;
uniform vec4 uInputSize;
uniform vec4 uOutputFrame;
uniform vec4 uOutputTexture;
vec4 filterVertexPosition( void )
{
vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
return vec4(position, 0.0, 1.0);
}
vec2 filterTextureCoord( void )
{
return aPosition * (uOutputFrame.zw * uInputSize.zw);
}
void main(void)
{
gl_Position = filterVertexPosition();
vTextureCoord = filterTextureCoord();
}`,
fragment: `
in vec2 vTextureCoord;
out vec4 finalColor;
uniform sampler2D uTexture;
uniform sampler2D uLutTexture;
uniform int channel;
void main(void) {
vec4 currentColor = texture(uTexture, vTextureCoord);
vec4 adjusted = currentColor;
if(currentColor.a > 0.0)
{
if(channel == 0)
{
adjusted = texture2D(uLutTexture, vec2((currentColor.r * 255.0 + 0.5) / 256.0, 0.5));
} else if(channel == 1) {
adjusted = texture2D(uLutTexture, vec2((currentColor.g * 255.0 + 0.5) / 256.0, 0.5));
} else if(channel == 2) {
adjusted = texture2D(uLutTexture, vec2((currentColor.b * 255.0 + 0.5) / 256.0, 0.5));
} else if(channel == 3) {
adjusted = texture2D(uLutTexture, vec2((currentColor.a * 255.0 + 0.5) / 256.0, 0.5));
}
}
finalColor = vec4(adjusted.r, adjusted.g, adjusted.b, currentColor.a);
}
`,
name: 'palette-map-filter',
});
const lookUpTable = PaletteMapFilter.getLookUpTable(options.palette);
const lutTexture = new Texture({
source: new BufferImageSource({
resource: Uint8Array.from(lookUpTable),
width: lookUpTable.length / 4,
height: 1
})
});
(async () =>
{
console.log(await TextureUtils.generateImageUrl(lutTexture));
})();
super({
gpuProgram: null,
glProgram,
resources: {
paletteMapUniforms: {
uChannel: { value: options.channel, type: 'int' }
},
uLutTexture: lutTexture.source
},
});
this.uniforms = this.resources.paletteMapUniforms.uniforms;
Object.assign(this, options);
}
public apply(
filterManager: FilterSystem,
input: Texture,
output: RenderSurface,
clearMode: boolean,
): void
{
filterManager.applyFilter(this, input, output, clearMode);
}
private static getLookUpTable(data: number[]): number[]
{
const lookUpTable = [];
for(let i = 0; i < data.length; i++)
{
lookUpTable[(i * 4) + PaletteMapFilter.CHANNEL_RED] = ((data[i] >> 16) & 0xFF);
lookUpTable[(i * 4) + PaletteMapFilter.CHANNEL_GREEN] = ((data[i] >> 8) & 0xFF);
lookUpTable[(i * 4) + PaletteMapFilter.CHANNEL_BLUE] = (data[i] & 0xFF);
lookUpTable[(i * 4) + PaletteMapFilter.CHANNEL_ALPHA] = ((data[i] >> 24) & 0xFF);
}
return lookUpTable;
}
}

View File

@ -0,0 +1,143 @@
import { Color, ColorSource, Filter, FilterSystem, GlProgram, RenderSurface, Texture } from 'pixi.js';
export interface WiredFilterOptions
{
lineColor: ColorSource;
color: ColorSource;
}
export class WiredFilter extends Filter
{
public static readonly DEFAULT_OPTIONS: WiredFilterOptions = {
lineColor: 0x000000,
color: 0x000000,
};
public uniforms: {
uLineColor: Float32Array,
uColor: Float32Array
};
private _lineColor!: Color;
private _color!: Color;
constructor(options: WiredFilterOptions)
{
options = { ...WiredFilter.DEFAULT_OPTIONS, ...options };
const glProgram = GlProgram.from({
vertex: `in vec2 aPosition;
out vec2 vTextureCoord;
uniform vec4 uInputSize;
uniform vec4 uOutputFrame;
uniform vec4 uOutputTexture;
vec4 filterVertexPosition( void )
{
vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
return vec4(position, 0.0, 1.0);
}
vec2 filterTextureCoord( void )
{
return aPosition * (uOutputFrame.zw * uInputSize.zw);
}
void main(void)
{
gl_Position = filterVertexPosition();
vTextureCoord = filterTextureCoord();
}`,
fragment: `
in vec2 vTextureCoord;
out vec4 finalColor;
uniform sampler2D uTexture;
uniform vec3 uLineColor;
uniform vec3 uColor;
void main(void) {
vec4 currentColor = texture(uTexture, vTextureCoord);
vec3 colorLine = uLineColor * currentColor.a;
vec3 colorOverlay = uColor * currentColor.a;
if(currentColor.r == 0.0 && currentColor.g == 0.0 && currentColor.b == 0.0 && currentColor.a > 0.0) {
finalColor = vec4(colorLine.r, colorLine.g, colorLine.b, currentColor.a);
} else if(currentColor.a > 0.0) {
finalColor = vec4(colorOverlay.r, colorOverlay.g, colorOverlay.b, currentColor.a);
}
}
`,
name: 'wired-filter',
});
super({
gpuProgram: null,
glProgram,
resources: {
planeMaskUniforms: {
uLineColor: { value: new Float32Array(3), type: 'vec3<f32>' },
uColor: { value: new Float32Array(3), type: 'vec3<f32>' }
},
},
});
this.uniforms = this.resources.planeMaskUniforms.uniforms;
this._lineColor = new Color();
this.lineColor = options.lineColor ?? 0x000000;
this._color = new Color();
this.color = options.color ?? 0x000000;
Object.assign(this, options);
}
public apply(
filterManager: FilterSystem,
input: Texture,
output: RenderSurface,
clearMode: boolean,
): void
{
filterManager.applyFilter(this, input, output, clearMode);
}
public get lineColor(): ColorSource
{
return this._lineColor.value as ColorSource;
}
public set lineColor(value: ColorSource)
{
this._lineColor.setValue(value);
const [r, g, b] = this._lineColor.toArray();
this.uniforms.uLineColor[0] = r;
this.uniforms.uLineColor[1] = g;
this.uniforms.uLineColor[2] = b;
}
public get color(): ColorSource
{
return this._color.value as ColorSource;
}
public set color(value: ColorSource)
{
this._color.setValue(value);
const [r, g, b] = this._color.toArray();
this.uniforms.uColor[0] = r;
this.uniforms.uColor[1] = g;
this.uniforms.uColor[2] = b;
}
}

View File

@ -1 +1,3 @@
export * from './PaletteMapFilter';
export * from './PlaneMaskFilter';
export * from './WiredFilter';

View File

@ -7,6 +7,7 @@ export * from './FurniId';
export * from './GetPixi';
export * from './GetRenderer';
export * from './GetStage';
export * from './GetTexturePool';
export * from './GetTicker';
export * from './GetTickerFPS';
export * from './GetTickerTime';
@ -23,6 +24,7 @@ export * from './Node3D';
export * from './NumberBank';
export * from './PointMath';
export * from './RoomId';
export * from './TexturePool';
export * from './TextureUtils';
export * from './Vector3d';
export * from './filters';

View File

@ -1,6 +1,6 @@
import { GetRoomEngine, RoomEngine } from '@nitrots/room';
import { GetRenderer } from '@nitrots/utils';
import { TextureSource } from 'pixi.js';
import { GetRenderer, GetTexturePool } from '@nitrots/utils';
import { Texture, TextureGCSystem, TextureSource } from 'pixi.js';
export { };
declare global
@ -9,13 +9,17 @@ declare global
{
NitroDevTools?:
{
getRoomEngine(): RoomEngine;
showTextureCache(): TextureSource<any>[];
roomEngine(): RoomEngine;
textureCache(): TextureSource<any>[];
texturePool(): { [index: string]: { [index: string]: Texture[] } };
textureGC(): TextureGCSystem;
};
}
}
window.NitroDevTools = {
getRoomEngine: () => GetRoomEngine(),
showTextureCache: () => GetRenderer().texture.managedTextures,
roomEngine: () => GetRoomEngine(),
textureCache: () => GetRenderer().texture.managedTextures,
texturePool: () => GetTexturePool().textures,
textureGC: () => GetRenderer().textureGC
};