🆙 Updated Mirroring in the windows, now showing face when infront

This commit is contained in:
duckietm
2026-02-17 15:20:07 +01:00
parent ac0226e09a
commit 764dd680c8
3 changed files with 126 additions and 12 deletions
@@ -8,6 +8,8 @@ interface IWindowReflectionAvatarState
texture: Texture;
location: IVector3D;
verticalOffset: number;
direction: number;
oppositeTexture: Texture;
}
export class RoomWindowReflectionState
@@ -15,7 +17,7 @@ export class RoomWindowReflectionState
private static _avatars: Map<number, IWindowReflectionAvatarState> = new Map();
private static _updateId: number = 0;
public static setAvatar(id: number, texture: Texture, location: IVector3D, verticalOffset: number = 0): void
public static setAvatar(id: number, texture: Texture, location: IVector3D, verticalOffset: number = 0, direction: number = 0, oppositeTexture: Texture = null): void
{
if(!texture || !location) return;
@@ -27,7 +29,9 @@ export class RoomWindowReflectionState
id,
texture,
location: storedLocation,
verticalOffset
verticalOffset,
direction,
oppositeTexture: (oppositeTexture || texture)
});
// Always bump updateId so reflected walk cycles stay frame-synced
@@ -1,7 +1,7 @@
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 { AdvancedMap, GetRenderer } from '@nitrots/utils';
import { Container, RenderTexture, Sprite, Texture } from 'pixi.js';
import { RoomObjectSpriteVisualization } from '../RoomObjectSpriteVisualization';
import { RoomWindowReflectionState } from '../RoomWindowReflectionState';
import { AvatarVisualizationData } from './AvatarVisualizationData';
@@ -77,6 +77,9 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
private _needsUpdate: boolean;
private _geometryUpdateCounter: number;
private _reflectionVerticalOffset: number;
private _reflectionOppositeTexture: Texture;
private _reflectionOppositeDirection: number;
private _reflectionOppositeBaseTexture: Texture;
private _additions: Map<number, IAvatarAddition>;
@@ -129,6 +132,9 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
this._needsUpdate = false;
this._geometryUpdateCounter = -1;
this._reflectionVerticalOffset = 0;
this._reflectionOppositeTexture = null;
this._reflectionOppositeDirection = -1;
this._reflectionOppositeBaseTexture = null;
this._additions = new Map();
}
@@ -154,6 +160,12 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
if(this._avatarImage) this._avatarImage.dispose();
if(this._reflectionOppositeTexture)
{
this._reflectionOppositeTexture.destroy(true);
this._reflectionOppositeTexture = null;
}
if(this.object) RoomWindowReflectionState.removeAvatar(this.object.id);
this._shadow = null;
@@ -1010,6 +1022,29 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
this.clearAvatar();
}
private cloneTexture(texture: Texture): Texture
{
if(!texture) return null;
const width = Math.max(1, Math.ceil(texture.width));
const height = Math.max(1, Math.ceil(texture.height));
const target = RenderTexture.create({ width, height });
const sprite = new Sprite(texture);
const container = new Container();
container.addChild(sprite);
GetRenderer().render({
target,
container,
clear: true
});
container.destroy({ children: true });
return target;
}
private updateWindowReflectionSource(): void
{
if(!this.object) return;
@@ -1018,7 +1053,45 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
if(sprite?.texture)
{
RoomWindowReflectionState.setAvatar(this.object.id, sprite.texture, this.object.getLocation(), this._reflectionVerticalOffset);
const currentDirection = this._avatarImage?.getDirection();
let oppositeTexture = sprite.texture;
if((currentDirection !== undefined) && this._avatarImage)
{
const oppositeDirection = ((currentDirection + 4) % 8);
if(oppositeDirection !== currentDirection)
{
const highlightEnabled = ((this.object.model.getValue<number>(RoomObjectVariable.FIGURE_HIGHLIGHT_ENABLE) === 1) && (this.object.model.getValue<number>(RoomObjectVariable.FIGURE_HIGHLIGHT) === 1));
this._avatarImage.setDirection(AvatarSetType.FULL, oppositeDirection);
const renderedOpposite = (this._avatarImage.processAsTexture(AvatarSetType.FULL, highlightEnabled) || sprite.texture);
if((this._reflectionOppositeDirection !== currentDirection) || (this._reflectionOppositeBaseTexture !== sprite.texture) || !this._reflectionOppositeTexture)
{
if(this._reflectionOppositeTexture)
{
this._reflectionOppositeTexture.destroy(true);
this._reflectionOppositeTexture = null;
}
this._reflectionOppositeTexture = this.cloneTexture(renderedOpposite);
this._reflectionOppositeDirection = currentDirection;
this._reflectionOppositeBaseTexture = sprite.texture;
}
oppositeTexture = (this._reflectionOppositeTexture || renderedOpposite);
// Restore the live avatar direction and refresh the current texture so
// movement updates do not keep showing the opposite-facing texture.
this._avatarImage.setDirection(AvatarSetType.FULL, currentDirection);
sprite.texture = (this._avatarImage.processAsTexture(AvatarSetType.FULL, highlightEnabled) || sprite.texture);
}
}
RoomWindowReflectionState.setAvatar(this.object.id, sprite.texture, this.object.getLocation(), this._reflectionVerticalOffset, this.object.getDirection().x, oppositeTexture);
return;
}
@@ -1044,6 +1117,15 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
this._cachedAvatars.reset();
this._cachedAvatarEffects.reset();
if(this._reflectionOppositeTexture)
{
this._reflectionOppositeTexture.destroy(true);
this._reflectionOppositeTexture = null;
}
this._reflectionOppositeDirection = -1;
this._reflectionOppositeBaseTexture = null;
this._avatarImage = null;
if(this.object) RoomWindowReflectionState.removeAvatar(this.object.id);
@@ -91,8 +91,8 @@ export class RoomPlane implements IRoomPlane
private _windowMasks: { leftSideLoc: number; rightSideLoc: number }[] = [];
private _lastWindowReflectionUpdateId: number = -1;
private _windowReflectionFirstSeenAt: Map<number, number> = new Map();
private _windowReflectionLastVisible: Map<number, { texture: Texture; location: IVector3D; verticalOffset: number }> = new Map();
private _windowReflectionFadeOut: Map<number, { texture: Texture; location: IVector3D; verticalOffset: number; startedAt: number }> = new Map();
private _windowReflectionLastVisible: Map<number, { texture: Texture; oppositeTexture: Texture; location: IVector3D; verticalOffset: number; direction: number }> = new Map();
private _windowReflectionFadeOut: Map<number, { texture: Texture; oppositeTexture: Texture; location: IVector3D; verticalOffset: number; direction: number; startedAt: number }> = new Map();
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)
{
@@ -884,7 +884,7 @@ export class RoomPlane implements IRoomPlane
const container = new Container();
const visibleAvatarIds = new Set<number>();
const addReflectionSprite = (texture: Texture, location: IVector3D, alpha: number, verticalOffset: number = 0): boolean => {
const addReflectionSprite = (texture: Texture, oppositeTexture: Texture, location: IVector3D, alpha: number, verticalOffset: number = 0, direction: number = 0, avatarId: number = -1): boolean => {
if(!texture?.source || texture.source.destroyed || !texture.source.style || !location || alpha < 0) return false;
const relative = Vector3d.dif(location, this._location);
@@ -908,9 +908,33 @@ export class RoomPlane implements IRoomPlane
const x = (canvasWidth - ((canvasWidth * leftSideLoc) / this._leftSide.length));
const y = (canvasHeight - ((canvasHeight * rightSideLoc) / this._rightSide.length)) + verticalOffset;
const sprite = new Sprite(texture);
const toPlaneX = (this._location.x - location.x);
const toPlaneY = (this._location.y - location.y);
const toPlaneLength = Math.hypot(toPlaneX, toPlaneY);
const facingRadians = ((((direction - 90) % 360) + 360) % 360) * (Math.PI / 180);
const facingX = Math.cos(facingRadians);
const facingY = Math.sin(facingRadians);
const facingWindow = (toPlaneLength > 0.001)
? (((facingX * toPlaneX) + (facingY * toPlaneY)) / toPlaneLength) > 0.5
: false;
const deltaLeft = Math.abs(closestMask.mask.leftSideLoc - leftSideLoc);
const deltaRight = Math.abs(closestMask.mask.rightSideLoc - rightSideLoc);
const isInFrontOfWindow = ((closestMask.score <= 2) && ((deltaLeft <= 0.9) || (deltaRight <= 0.9)));
const shouldMirror = isInFrontOfWindow;
const normal2DLength = Math.hypot(this._normal.x, this._normal.y);
const normalX = (normal2DLength > 0.0001) ? (this._normal.x / normal2DLength) : 0;
const normalY = (normal2DLength > 0.0001) ? (this._normal.y / normal2DLength) : 0;
const normalFacingDot = Math.abs((facingX * normalX) + (facingY * normalY));
const useOppositeTexture = (shouldMirror && (normalFacingDot > 0.7));
const sprite = new Sprite((useOppositeTexture ? (oppositeTexture || texture) : texture));
sprite.anchor.set(0.5, 1);
sprite.position.set(Math.trunc(x), Math.trunc(y));
sprite.scale.set(1, 1);
sprite.tint = 0xCFE3FF;
sprite.alpha = alpha;
@@ -935,7 +959,7 @@ export class RoomPlane implements IRoomPlane
const progress = (elapsed / fadeDurationMs);
const alpha = (0.4 * progress);
if(!addReflectionSprite(avatar.texture, avatar.location, alpha, avatar.verticalOffset || 0)) continue;
if(!addReflectionSprite(avatar.texture, avatar.oppositeTexture, avatar.location, alpha, avatar.verticalOffset || 0, avatar.direction || 0, avatar.id)) continue;
if(!this._windowReflectionFirstSeenAt.has(avatar.id)) this._windowReflectionFirstSeenAt.set(avatar.id, firstSeenAt);
@@ -947,8 +971,10 @@ export class RoomPlane implements IRoomPlane
this._windowReflectionLastVisible.set(avatar.id, {
texture: avatar.texture,
oppositeTexture: avatar.oppositeTexture,
location: storedLocation,
verticalOffset: avatar.verticalOffset || 0
verticalOffset: avatar.verticalOffset || 0,
direction: avatar.direction || 0
});
}
@@ -966,8 +992,10 @@ export class RoomPlane implements IRoomPlane
this._windowReflectionFadeOut.set(id, {
texture: lastVisible.texture,
oppositeTexture: lastVisible.oppositeTexture,
location: lastVisible.location,
verticalOffset: lastVisible.verticalOffset,
direction: lastVisible.direction,
startedAt: now
});
@@ -988,7 +1016,7 @@ export class RoomPlane implements IRoomPlane
const alpha = (0.4 * (1 - (elapsed / fadeDurationMs)));
if(!addReflectionSprite(fadeOut.texture, fadeOut.location, alpha, fadeOut.verticalOffset)) this._windowReflectionFadeOut.delete(id);
if(!addReflectionSprite(fadeOut.texture, fadeOut.oppositeTexture, fadeOut.location, alpha, fadeOut.verticalOffset, fadeOut.direction, id)) this._windowReflectionFadeOut.delete(id);
}
if(!container.children.length)