🆙 Some minor mem tweaks

This commit is contained in:
duckietm
2026-03-31 16:03:21 +02:00
parent 44cb722f54
commit fd74fd323b
7 changed files with 198 additions and 173 deletions
+2
View File
@@ -285,6 +285,8 @@ export class AvatarImage implements IAvatarImage, IAvatarEffectListener
const container = this.buildAvatarContainer(avatarCanvas, setType); const container = this.buildAvatarContainer(avatarCanvas, setType);
if(!container) return null;
GetRenderer().render({ GetRenderer().render({
target: this._activeTexture, target: this._activeTexture,
container: container, container: container,
@@ -117,7 +117,12 @@ export class RoomCameraWidgetManager implements IRoomCameraWidgetManager
TextureUtils.writeToTexture(container, renderTexture); TextureUtils.writeToTexture(container, renderTexture);
return await TextureUtils.generateImage(renderTexture); const image = await TextureUtils.generateImage(renderTexture);
container.destroy({ children: true });
renderTexture.destroy(true);
return image;
} }
public get effects(): Map<string, IRoomCameraWidgetEffect> public get effects(): Map<string, IRoomCameraWidgetEffect>
@@ -62,7 +62,8 @@ export class CommunicationManager implements ICommunicationManager
t.shadowColor = 'blue'; t.shadowColor = 'blue';
t.fillRect(-20, 10, 234, 5); t.fillRect(-20, 10, 234, 5);
const i = e.toDataURL(); const i = e.toDataURL();
document.body.appendChild(e); e.width = 0;
e.height = 0;
let r = 0; let r = 0;
if (i.length === 0) return 'nothing!'; if (i.length === 0) return 'nothing!';
for (let n = 0; n < i.length; n++) { for (let n = 0; n < i.length; n++) {
@@ -283,6 +283,8 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
tempCtx.putImageData(patchData, 0, 0); tempCtx.putImageData(patchData, 0, 0);
accCtx.drawImage(tempCanvas, frame.dims.left, frame.dims.top); accCtx.drawImage(tempCanvas, frame.dims.left, frame.dims.top);
tempCanvas.width = 0;
tempCanvas.height = 0;
// Create a new canvas for this frame and create a texture from it // Create a new canvas for this frame and create a texture from it
const frameCanvas = document.createElement('canvas'); const frameCanvas = document.createElement('canvas');
@@ -299,6 +301,9 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
frameDelays.push(frame.delay || 10); frameDelays.push(frame.delay || 10);
} }
accCanvas.width = 0;
accCanvas.height = 0;
// Create AnimatedSprite with frame textures // Create AnimatedSprite with frame textures
if(this._frameTextures.length > 1) if(this._frameTextures.length > 1)
{ {
@@ -342,13 +347,11 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali
tex.source.update(); tex.source.update();
img.onload = null; img.onload = null;
img.onerror = null; img.onerror = null;
img.src = '';
}; };
img.onerror = () => img.onerror = () =>
{ {
img.onload = null; img.onload = null;
img.onerror = null; img.onerror = null;
img.src = '';
}; };
img.src = badgeUrl; img.src = badgeUrl;
} }
@@ -35,14 +35,12 @@ export class FurnitureDynamicThumbnailVisualization extends IsometricImageFurniV
} }
image.onload = null; image.onload = null;
image.onerror = null; image.onerror = null;
image.src = '';
}; };
image.onerror = () => { image.onerror = () => {
this.setThumbnailImages(null); this.setThumbnailImages(null);
image.onload = null; image.onload = null;
image.onerror = null; image.onerror = null;
image.src = '';
}; };
} else { } else {
this.setThumbnailImages(null); this.setThumbnailImages(null);
@@ -867,203 +867,218 @@ export class RoomPlane implements IRoomPlane
} }
private renderWindowReflections(): void private renderWindowReflections(): void
{
if(!this._planeTexture || !this._leftSide || !this._rightSide || !this._normal) return;
if(this._leftSide.length <= 0 || this._rightSide.length <= 0) return;
const now = Date.now();
const fadeDurationMs = 150;
const avatars = RoomWindowReflectionState.getAvatars();
const canvasWidth = this._landscapeRenderWidth;
const canvasHeight = this._landscapeRenderHeight;
if(canvasWidth <= 0 || canvasHeight <= 0) return;
const container = new Container();
const visibleAvatarIds = new Set<number>();
const addReflectionSprite = (
texture: Texture,
oppositeTexture: Texture,
location: IVector3D,
alpha: number,
verticalOffset: number = 0,
direction: number = 0,
avatarId: number = -1
): boolean =>
{ {
if(!this._planeTexture || !this._leftSide || !this._rightSide || !this._normal) return; if(!texture?.source || texture.source.destroyed || !texture.source.style || !location || alpha < 0)
return false;
if(this._leftSide.length <= 0 || this._rightSide.length <= 0) return; const relative = Vector3d.dif(location, this._location);
const planeDistance = Math.abs(Vector3d.scalarProjection(relative, this._normal));
const now = Date.now(); if(planeDistance > 0.8) return false;
const fadeDurationMs = 150;
const avatars = RoomWindowReflectionState.getAvatars();
const canvasWidth = this._landscapeRenderWidth;
const canvasHeight = this._landscapeRenderHeight;
if(canvasWidth <= 0 || canvasHeight <= 0) return; const leftSideLoc = Vector3d.scalarProjection(relative, this._leftSide);
const rightSideLoc = Vector3d.scalarProjection(relative, this._rightSide);
const container = new Container(); const closestMask = this._windowMasks.reduce((best, mask) =>
const visibleAvatarIds = new Set<number>();
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);
const planeDistance = Math.abs(Vector3d.scalarProjection(relative, this._normal));
if(planeDistance > 0.8) return false;
const leftSideLoc = Vector3d.scalarProjection(relative, this._leftSide);
const rightSideLoc = Vector3d.scalarProjection(relative, this._rightSide);
const closestMask = this._windowMasks.reduce((best, mask) => {
const score = Math.abs(mask.leftSideLoc - leftSideLoc) + Math.abs(mask.rightSideLoc - rightSideLoc);
if(!best || (score < best.score)) return { mask, score };
return best;
}, null as { mask: { leftSideLoc: number; rightSideLoc: number }; score: number } | null);
if(!closestMask || (closestMask.score > 3)) return false;
const x = (canvasWidth - ((canvasWidth * leftSideLoc) / this._leftSide.length));
const y = (canvasHeight - ((canvasHeight * rightSideLoc) / this._rightSide.length)) + verticalOffset;
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 transitionLow = 0.6;
const transitionHigh = 0.8;
let oppositeWeight = 0;
if(shouldMirror && oppositeTexture)
{
if(normalFacingDot >= transitionHigh) oppositeWeight = 1;
else if(normalFacingDot > transitionLow) oppositeWeight = (normalFacingDot - transitionLow) / (transitionHigh - transitionLow);
}
if(oppositeWeight < 1)
{
const sprite = new Sprite(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 * (1 - oppositeWeight);
container.addChild(sprite);
}
if(oppositeWeight > 0 && oppositeTexture)
{
const sprite = new Sprite(oppositeTexture);
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 * oppositeWeight;
container.addChild(sprite);
}
return true;
};
for(const avatar of avatars)
{ {
if(!avatar?.texture?.source || avatar.texture.source.destroyed || !avatar.texture.source.style || !avatar.location) continue; const score = Math.abs(mask.leftSideLoc - leftSideLoc) + Math.abs(mask.rightSideLoc - rightSideLoc);
let firstSeenAt = this._windowReflectionFirstSeenAt.get(avatar.id); if(!best || (score < best.score)) return { mask, score };
if(firstSeenAt === undefined) return best;
{ }, null as { mask: { leftSideLoc: number; rightSideLoc: number }; score: number } | null);
firstSeenAt = now;
}
const elapsed = Math.min(fadeDurationMs, Math.max(0, (now - firstSeenAt))); if(!closestMask || (closestMask.score > 3)) return false;
const progress = (elapsed / fadeDurationMs);
const alpha = (0.4 * progress);
if(!addReflectionSprite(avatar.texture, avatar.oppositeTexture, avatar.location, alpha, avatar.verticalOffset || 0, avatar.direction || 0, avatar.id)) continue; const x = (canvasWidth - ((canvasWidth * leftSideLoc) / this._leftSide.length));
const y = (canvasHeight - ((canvasHeight * rightSideLoc) / this._rightSide.length)) + verticalOffset;
if(!this._windowReflectionFirstSeenAt.has(avatar.id)) this._windowReflectionFirstSeenAt.set(avatar.id, firstSeenAt); const toPlaneX = (this._location.x - location.x);
const toPlaneY = (this._location.y - location.y);
const toPlaneLength = Math.hypot(toPlaneX, toPlaneY);
visibleAvatarIds.add(avatar.id); const facingRadians = ((((direction - 90) % 360) + 360) % 360) * (Math.PI / 180);
this._windowReflectionFadeOut.delete(avatar.id); const facingX = Math.cos(facingRadians);
const facingY = Math.sin(facingRadians);
const facingWindow = (toPlaneLength > 0.001)
? (((facingX * toPlaneX) + (facingY * toPlaneY)) / toPlaneLength) > 0.5
: false;
const storedLocation = new Vector3d(); const deltaLeft = Math.abs(closestMask.mask.leftSideLoc - leftSideLoc);
storedLocation.assign(avatar.location); const deltaRight = Math.abs(closestMask.mask.rightSideLoc - rightSideLoc);
this._windowReflectionLastVisible.set(avatar.id, { const isInFrontOfWindow = ((closestMask.score <= 2) && ((deltaLeft <= 0.9) || (deltaRight <= 0.9)));
texture: avatar.texture, const shouldMirror = isInFrontOfWindow;
oppositeTexture: avatar.oppositeTexture,
location: storedLocation, const normal2DLength = Math.hypot(this._normal.x, this._normal.y);
verticalOffset: avatar.verticalOffset || 0, const normalX = (normal2DLength > 0.0001) ? (this._normal.x / normal2DLength) : 0;
direction: avatar.direction || 0 const normalY = (normal2DLength > 0.0001) ? (this._normal.y / normal2DLength) : 0;
}); const normalFacingDot = Math.abs((facingX * normalX) + (facingY * normalY));
const transitionLow = 0.6;
const transitionHigh = 0.8;
let oppositeWeight = 0;
if(shouldMirror && oppositeTexture)
{
if(normalFacingDot >= transitionHigh) oppositeWeight = 1;
else if(normalFacingDot > transitionLow)
oppositeWeight = (normalFacingDot - transitionLow) / (transitionHigh - transitionLow);
} }
for(const [id, lastVisible] of this._windowReflectionLastVisible) if(oppositeWeight < 1)
{ {
if(visibleAvatarIds.has(id) || this._windowReflectionFadeOut.has(id)) continue; const sprite = new Sprite(texture);
sprite.anchor.set(0.5, 1);
if(!lastVisible.texture?.source || lastVisible.texture.source.destroyed || !lastVisible.texture.source.style) sprite.position.set(Math.trunc(x), Math.trunc(y));
{ sprite.tint = 0xCFE3FF;
this._windowReflectionLastVisible.delete(id); sprite.alpha = alpha * (1 - oppositeWeight);
this._windowReflectionFirstSeenAt.delete(id); container.addChild(sprite);
continue;
}
this._windowReflectionFadeOut.set(id, {
texture: lastVisible.texture,
oppositeTexture: lastVisible.oppositeTexture,
location: lastVisible.location,
verticalOffset: lastVisible.verticalOffset,
direction: lastVisible.direction,
startedAt: now
});
this._windowReflectionLastVisible.delete(id);
this._windowReflectionFirstSeenAt.delete(id);
} }
for(const [id, fadeOut] of this._windowReflectionFadeOut) if(oppositeWeight > 0 && oppositeTexture)
{ {
const elapsed = (now - fadeOut.startedAt); const sprite = new Sprite(oppositeTexture);
sprite.anchor.set(0.5, 1);
if(elapsed >= fadeDurationMs) sprite.position.set(Math.trunc(x), Math.trunc(y));
{ sprite.tint = 0xCFE3FF;
this._windowReflectionFadeOut.delete(id); sprite.alpha = alpha * oppositeWeight;
container.addChild(sprite);
continue;
}
const alpha = (0.4 * (1 - (elapsed / fadeDurationMs)));
if(!addReflectionSprite(fadeOut.texture, fadeOut.oppositeTexture, fadeOut.location, alpha, fadeOut.verticalOffset, fadeOut.direction, id)) this._windowReflectionFadeOut.delete(id);
} }
if(!container.children.length) return true;
{ };
container.destroy({ children: true });
if(!avatars.length) for(const avatar of avatars)
{ {
this._windowReflectionFirstSeenAt.clear(); if(!avatar?.texture?.source || avatar.texture.source.destroyed || !avatar.texture.source.style || !avatar.location)
this._windowReflectionLastVisible.clear(); continue;
}
return; let firstSeenAt = this._windowReflectionFirstSeenAt.get(avatar.id);
}
if(this._maskFilter) container.filters = [this._maskFilter]; if(firstSeenAt === undefined) firstSeenAt = now;
GetRenderer().render({ const elapsed = Math.min(fadeDurationMs, Math.max(0, (now - firstSeenAt)));
target: this._planeTexture, const alpha = (0.4 * (elapsed / fadeDurationMs));
container,
transform: this.getMatrixForDimensions(canvasWidth, canvasHeight), if(!addReflectionSprite(
clear: false 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);
visibleAvatarIds.add(avatar.id);
this._windowReflectionFadeOut.delete(avatar.id);
const storedLocation = new Vector3d();
storedLocation.assign(avatar.location);
this._windowReflectionLastVisible.set(avatar.id, {
texture: avatar.texture,
oppositeTexture: avatar.oppositeTexture,
location: storedLocation,
verticalOffset: avatar.verticalOffset || 0,
direction: avatar.direction || 0
});
}
// move to fade-out (NO destruction)
for(const [id, lastVisible] of this._windowReflectionLastVisible)
{
if(visibleAvatarIds.has(id) || this._windowReflectionFadeOut.has(id)) continue;
this._windowReflectionFadeOut.set(id, {
...lastVisible,
startedAt: now
}); });
container.destroy({ children: true }); this._windowReflectionLastVisible.delete(id);
this._windowReflectionFirstSeenAt.delete(id);
} }
// fade-out rendering (NO destruction)
for(const [id, fadeOut] of this._windowReflectionFadeOut)
{
const elapsed = (now - fadeOut.startedAt);
if(elapsed >= fadeDurationMs)
{
this._windowReflectionFadeOut.delete(id);
continue;
}
const alpha = (0.4 * (1 - (elapsed / fadeDurationMs)));
if(!addReflectionSprite(
fadeOut.texture,
fadeOut.oppositeTexture,
fadeOut.location,
alpha,
fadeOut.verticalOffset,
fadeOut.direction,
id))
{
this._windowReflectionFadeOut.delete(id);
}
}
if(!container.children.length)
{
container.destroy({ children: true });
if(!avatars.length)
{
this._windowReflectionFirstSeenAt.clear();
this._windowReflectionLastVisible.clear();
}
return;
}
if(this._maskFilter) container.filters = [this._maskFilter];
GetRenderer().render({
target: this._planeTexture,
container,
transform: this.getMatrixForDimensions(canvasWidth, canvasHeight),
clear: false
});
container.destroy({ children: true });
}
private updateCorners(geometry: IRoomGeometry): void private updateCorners(geometry: IRoomGeometry): void
{ {
this._cornerA.assign(geometry.getScreenPosition(this._location)); this._cornerA.assign(geometry.getScreenPosition(this._location));
@@ -248,6 +248,7 @@ export class BadgeImageManager
if(!renderedLayers) return false; if(!renderedLayers) return false;
const texture = TextureUtils.generateTexture(container); const texture = TextureUtils.generateTexture(container);
container.destroy({ children: true });
GetAssetManager().setTexture(groupBadge.code, texture); GetAssetManager().setTexture(groupBadge.code, texture);
GetEventDispatcher().dispatchEvent(new BadgeImageReadyEvent(groupBadge.code, texture)); GetEventDispatcher().dispatchEvent(new BadgeImageReadyEvent(groupBadge.code, texture));