You've already forked Nitro_Render_V3
mirror of
https://github.com/duckietm/Nitro_Render_V3.git
synced 2026-06-20 15:36:18 +00:00
🆙 Update Furniture Badge
- Better performance - PixiJS handles animation - GPU acceleration - Textures are uploaded to GPU instead of CPU canvas operations
This commit is contained in:
+85
-47
@@ -1,7 +1,7 @@
|
|||||||
import { IGraphicAsset, IRoomObjectSprite, RoomObjectVariable } from '@nitrots/api';
|
import { IGraphicAsset, IRoomObjectSprite, RoomObjectVariable } from '@nitrots/api';
|
||||||
import { GetConfiguration } from '@nitrots/configuration';
|
import { GetConfiguration } from '@nitrots/configuration';
|
||||||
import { GetSessionDataManager } from '@nitrots/session';
|
import { GetSessionDataManager } from '@nitrots/session';
|
||||||
import { Texture } from 'pixi.js';
|
import { AnimatedSprite, Texture } from 'pixi.js';
|
||||||
import { parseGIF, decompressFrames } from 'gifuct-js';
|
import { parseGIF, decompressFrames } from 'gifuct-js';
|
||||||
import { FurnitureAnimatedVisualization } from './FurnitureAnimatedVisualization';
|
import { FurnitureAnimatedVisualization } from './FurnitureAnimatedVisualization';
|
||||||
|
|
||||||
@@ -14,10 +14,36 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
|
|||||||
private _badgeAssetNameNormalScale = '';
|
private _badgeAssetNameNormalScale = '';
|
||||||
private _badgeAssetNameSmallScale = '';
|
private _badgeAssetNameSmallScale = '';
|
||||||
private _badgeVisibleInState = -1;
|
private _badgeVisibleInState = -1;
|
||||||
private _gifFrames: ImageData[] = null;
|
private _frameTextures: Texture[] = null;
|
||||||
private _frameDelays: number[] = null;
|
private _animatedSprite: AnimatedSprite = null;
|
||||||
private _currentFrame = 0;
|
private _lastFrameIndex = -1;
|
||||||
private _lastFrameTime = 0;
|
|
||||||
|
public dispose(): void
|
||||||
|
{
|
||||||
|
this.disposeAnimatedSprite();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private disposeAnimatedSprite(): void
|
||||||
|
{
|
||||||
|
if(this._animatedSprite)
|
||||||
|
{
|
||||||
|
this._animatedSprite.stop();
|
||||||
|
this._animatedSprite.destroy();
|
||||||
|
this._animatedSprite = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._frameTextures)
|
||||||
|
{
|
||||||
|
for(const texture of this._frameTextures)
|
||||||
|
{
|
||||||
|
texture.destroy(true);
|
||||||
|
}
|
||||||
|
this._frameTextures = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lastFrameIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
public getTexture(scale: number, layerId: number, asset: IGraphicAsset): Texture
|
public getTexture(scale: number, layerId: number, asset: IGraphicAsset): Texture
|
||||||
{
|
{
|
||||||
@@ -28,38 +54,38 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
|
|||||||
{
|
{
|
||||||
super.update(geometry, time, update, skipUpdate);
|
super.update(geometry, time, update, skipUpdate);
|
||||||
|
|
||||||
// Update animated GIF
|
// GIF using AnimatedSprite's
|
||||||
if(this._gifFrames && this._gifFrames.length > 1)
|
if(this._animatedSprite && this._frameTextures && this._frameTextures.length > 1)
|
||||||
{
|
{
|
||||||
|
const currentFrameIndex = this._animatedSprite.currentFrame;
|
||||||
|
|
||||||
|
|
||||||
|
if(currentFrameIndex !== this._lastFrameIndex)
|
||||||
|
{
|
||||||
|
this._lastFrameIndex = currentFrameIndex;
|
||||||
|
|
||||||
const sessionDataManager = GetSessionDataManager();
|
const sessionDataManager = GetSessionDataManager();
|
||||||
let tex = sessionDataManager.getBadgeImage(this._badgeId);
|
let tex = sessionDataManager.getBadgeImage(this._badgeId);
|
||||||
if(!tex) tex = sessionDataManager.getGroupBadgeImage(this._badgeId);
|
if(!tex) tex = sessionDataManager.getGroupBadgeImage(this._badgeId);
|
||||||
|
|
||||||
if(!tex)
|
if(!tex) return;
|
||||||
{
|
|
||||||
console.warn('⚠️ No texture found for badge:', this._badgeId);
|
const currentFrameTexture = this._frameTextures[currentFrameIndex];
|
||||||
return;
|
if(!currentFrameTexture) return;
|
||||||
}
|
|
||||||
|
|
||||||
const badgeCanvas = (tex.source as any).resource as HTMLCanvasElement;
|
const badgeCanvas = (tex.source as any).resource as HTMLCanvasElement;
|
||||||
|
|
||||||
const now = performance.now();
|
|
||||||
const elapsed = now - this._lastFrameTime;
|
|
||||||
|
|
||||||
const frameDelay = (this._frameDelays[this._currentFrame] || 10) * 10;
|
|
||||||
|
|
||||||
if(elapsed >= frameDelay)
|
|
||||||
{
|
|
||||||
this._lastFrameTime = now;
|
|
||||||
const oldFrame = this._currentFrame;
|
|
||||||
this._currentFrame = (this._currentFrame + 1) % this._gifFrames.length;
|
|
||||||
|
|
||||||
const ctx = badgeCanvas.getContext('2d', { willReadFrequently: true });
|
const ctx = badgeCanvas.getContext('2d', { willReadFrequently: true });
|
||||||
const frame = this._gifFrames[this._currentFrame];
|
|
||||||
|
|
||||||
if(frame)
|
const frameCanvas = document.createElement('canvas');
|
||||||
|
frameCanvas.width = currentFrameTexture.width;
|
||||||
|
frameCanvas.height = currentFrameTexture.height;
|
||||||
|
const frameCtx = frameCanvas.getContext('2d');
|
||||||
|
|
||||||
|
const frameSource = (currentFrameTexture.source as any).resource;
|
||||||
|
if(frameSource instanceof HTMLCanvasElement || frameSource instanceof HTMLImageElement)
|
||||||
{
|
{
|
||||||
ctx.putImageData(frame, 0, 0);
|
ctx.clearRect(0, 0, badgeCanvas.width, badgeCanvas.height);
|
||||||
|
ctx.drawImage(frameSource, 0, 0);
|
||||||
tex.source.update();
|
tex.source.update();
|
||||||
|
|
||||||
const assetName = this._badgeAssetNameNormalScale;
|
const assetName = this._badgeAssetNameNormalScale;
|
||||||
@@ -89,8 +115,7 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
|
|||||||
this._badgeAssetNameNormalScale = '';
|
this._badgeAssetNameNormalScale = '';
|
||||||
this._badgeAssetNameSmallScale = '';
|
this._badgeAssetNameSmallScale = '';
|
||||||
this._badgeVisibleInState = -1;
|
this._badgeVisibleInState = -1;
|
||||||
this._gifFrames = null;
|
this.disposeAnimatedSprite();
|
||||||
this._frameDelays = null;
|
|
||||||
|
|
||||||
return needsUpdate;
|
return needsUpdate;
|
||||||
}
|
}
|
||||||
@@ -104,11 +129,7 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
|
|||||||
const visibleInState = this.object.model.getValue<number>(RoomObjectVariable.FURNITURE_BADGE_VISIBLE_IN_STATE);
|
const visibleInState = this.object.model.getValue<number>(RoomObjectVariable.FURNITURE_BADGE_VISIBLE_IN_STATE);
|
||||||
this._badgeVisibleInState = isNaN(visibleInState) ? -1 : visibleInState;
|
this._badgeVisibleInState = isNaN(visibleInState) ? -1 : visibleInState;
|
||||||
|
|
||||||
this._gifFrames = null;
|
this.disposeAnimatedSprite();
|
||||||
this._frameDelays = null;
|
|
||||||
this._currentFrame = 0;
|
|
||||||
this._lastFrameTime = 0;
|
|
||||||
|
|
||||||
this.addBadgeToAssetCollection(badgeId);
|
this.addBadgeToAssetCollection(badgeId);
|
||||||
|
|
||||||
const layerId = FurnitureBadgeDisplayVisualization.BADGE_LAYER_ID;
|
const layerId = FurnitureBadgeDisplayVisualization.BADGE_LAYER_ID;
|
||||||
@@ -212,7 +233,7 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
|
|||||||
const imageData = ctx.getImageData(0, 0, 1, 1);
|
const imageData = ctx.getImageData(0, 0, 1, 1);
|
||||||
const isEmpty = imageData.data[3] === 0;
|
const isEmpty = imageData.data[3] === 0;
|
||||||
|
|
||||||
if (isEmpty || !this._gifFrames)
|
if(isEmpty || !this._frameTextures)
|
||||||
{
|
{
|
||||||
const badgeUrl = GetConfiguration().getValue<string>('badge.asset.url', '').replace('%badgename%', badgeId);
|
const badgeUrl = GetConfiguration().getValue<string>('badge.asset.url', '').replace('%badgename%', badgeId);
|
||||||
|
|
||||||
@@ -225,8 +246,8 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
|
|||||||
|
|
||||||
if(frames && frames.length > 0)
|
if(frames && frames.length > 0)
|
||||||
{
|
{
|
||||||
this._gifFrames = [];
|
this._frameTextures = [];
|
||||||
this._frameDelays = [];
|
const frameDelays: number[] = [];
|
||||||
|
|
||||||
const accCanvas = document.createElement('canvas');
|
const accCanvas = document.createElement('canvas');
|
||||||
accCanvas.width = gif.lsd.width;
|
accCanvas.width = gif.lsd.width;
|
||||||
@@ -260,24 +281,41 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
|
|||||||
|
|
||||||
accCtx.drawImage(tempCanvas, frame.dims.left, frame.dims.top);
|
accCtx.drawImage(tempCanvas, frame.dims.left, frame.dims.top);
|
||||||
|
|
||||||
const fullFrame = accCtx.getImageData(0, 0, gif.lsd.width, gif.lsd.height);
|
const frameCanvas = document.createElement('canvas');
|
||||||
this._gifFrames.push(fullFrame);
|
frameCanvas.width = gif.lsd.width;
|
||||||
this._frameDelays.push(frame.delay || 10);
|
frameCanvas.height = gif.lsd.height;
|
||||||
|
const frameCtx = frameCanvas.getContext('2d');
|
||||||
|
frameCtx.drawImage(accCanvas, 0, 0);
|
||||||
|
|
||||||
|
const frameTexture = Texture.from(frameCanvas);
|
||||||
|
this._frameTextures.push(frameTexture);
|
||||||
|
|
||||||
|
frameDelays.push(frame.delay || 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._currentFrame = 0;
|
if(this._frameTextures.length > 1)
|
||||||
this._lastFrameTime = performance.now();
|
|
||||||
|
|
||||||
const avgDelay = this._frameDelays.reduce((a, b) => a + b, 0) / this._frameDelays.length;
|
|
||||||
if(avgDelay > 50)
|
|
||||||
{
|
{
|
||||||
this._frameDelays = this._frameDelays.map(() => 10);
|
this._animatedSprite = new AnimatedSprite(this._frameTextures);
|
||||||
|
|
||||||
|
const avgDelay = frameDelays.reduce((a, b) => a + b, 0) / frameDelays.length;
|
||||||
|
const delayMs = (avgDelay > 50 ? 10 : avgDelay) * 10;
|
||||||
|
const framesPerTick = 16.67 / delayMs;
|
||||||
|
this._animatedSprite.animationSpeed = framesPerTick;
|
||||||
|
|
||||||
|
this._animatedSprite.loop = true;
|
||||||
|
this._animatedSprite.play();
|
||||||
|
this._lastFrameIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.putImageData(this._gifFrames[0], 0, 0);
|
const firstFrameSource = (this._frameTextures[0].source as any).resource;
|
||||||
|
if(firstFrameSource instanceof HTMLCanvasElement)
|
||||||
|
{
|
||||||
|
ctx.clearRect(0, 0, badgeCanvas.width, badgeCanvas.height);
|
||||||
|
ctx.drawImage(firstFrameSource, 0, 0);
|
||||||
tex.source.update();
|
tex.source.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch(err)
|
catch(err)
|
||||||
{
|
{
|
||||||
console.error('Failed to parse GIF, using static image:', err);
|
console.error('Failed to parse GIF, using static image:', err);
|
||||||
|
|||||||
Reference in New Issue
Block a user