diff --git a/packages/avatar/src/AvatarImage.ts b/packages/avatar/src/AvatarImage.ts index b98e2e3..4cc52cf 100644 --- a/packages/avatar/src/AvatarImage.ts +++ b/packages/avatar/src/AvatarImage.ts @@ -285,6 +285,8 @@ export class AvatarImage implements IAvatarImage, IAvatarEffectListener const container = this.buildAvatarContainer(avatarCanvas, setType); + if(!container) return null; + GetRenderer().render({ target: this._activeTexture, container: container, diff --git a/packages/camera/src/RoomCameraWidgetManager.ts b/packages/camera/src/RoomCameraWidgetManager.ts index 1d28a4c..85b7067 100644 --- a/packages/camera/src/RoomCameraWidgetManager.ts +++ b/packages/camera/src/RoomCameraWidgetManager.ts @@ -117,7 +117,12 @@ export class RoomCameraWidgetManager implements IRoomCameraWidgetManager 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 diff --git a/packages/communication/src/CommunicationManager.ts b/packages/communication/src/CommunicationManager.ts index 0fa6a90..3718463 100644 --- a/packages/communication/src/CommunicationManager.ts +++ b/packages/communication/src/CommunicationManager.ts @@ -62,7 +62,8 @@ export class CommunicationManager implements ICommunicationManager t.shadowColor = 'blue'; t.fillRect(-20, 10, 234, 5); const i = e.toDataURL(); - document.body.appendChild(e); + e.width = 0; + e.height = 0; let r = 0; if (i.length === 0) return 'nothing!'; for (let n = 0; n < i.length; n++) { diff --git a/packages/room/src/object/visualization/furniture/FurnitureBadgeDisplayVisualization.ts b/packages/room/src/object/visualization/furniture/FurnitureBadgeDisplayVisualization.ts index 8e39508..7e344e8 100644 --- a/packages/room/src/object/visualization/furniture/FurnitureBadgeDisplayVisualization.ts +++ b/packages/room/src/object/visualization/furniture/FurnitureBadgeDisplayVisualization.ts @@ -283,6 +283,8 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali tempCtx.putImageData(patchData, 0, 0); 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 const frameCanvas = document.createElement('canvas'); @@ -299,6 +301,9 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali frameDelays.push(frame.delay || 10); } + accCanvas.width = 0; + accCanvas.height = 0; + // Create AnimatedSprite with frame textures if(this._frameTextures.length > 1) { @@ -342,13 +347,11 @@ export class FurnitureBadgeDisplayVisualization extends FurnitureAnimatedVisuali tex.source.update(); img.onload = null; img.onerror = null; - img.src = ''; }; img.onerror = () => { img.onload = null; img.onerror = null; - img.src = ''; }; img.src = badgeUrl; } diff --git a/packages/room/src/object/visualization/furniture/FurnitureDynamicThumbnailVisualization.ts b/packages/room/src/object/visualization/furniture/FurnitureDynamicThumbnailVisualization.ts index 810ca9d..4c0697a 100644 --- a/packages/room/src/object/visualization/furniture/FurnitureDynamicThumbnailVisualization.ts +++ b/packages/room/src/object/visualization/furniture/FurnitureDynamicThumbnailVisualization.ts @@ -35,14 +35,12 @@ export class FurnitureDynamicThumbnailVisualization extends IsometricImageFurniV } image.onload = null; image.onerror = null; - image.src = ''; }; image.onerror = () => { this.setThumbnailImages(null); image.onload = null; image.onerror = null; - image.src = ''; }; } else { this.setThumbnailImages(null); diff --git a/packages/room/src/object/visualization/room/RoomPlane.ts b/packages/room/src/object/visualization/room/RoomPlane.ts index a1391bd..3556447 100644 --- a/packages/room/src/object/visualization/room/RoomPlane.ts +++ b/packages/room/src/object/visualization/room/RoomPlane.ts @@ -867,203 +867,218 @@ export class RoomPlane implements IRoomPlane } 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(); + + 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(); - const fadeDurationMs = 150; - const avatars = RoomWindowReflectionState.getAvatars(); - const canvasWidth = this._landscapeRenderWidth; - const canvasHeight = this._landscapeRenderHeight; + if(planeDistance > 0.8) return false; - 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 visibleAvatarIds = new Set(); - - 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) + const closestMask = this._windowMasks.reduce((best, mask) => { - 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) - { - firstSeenAt = now; - } + return best; + }, null as { mask: { leftSideLoc: number; rightSideLoc: number }; score: number } | null); - const elapsed = Math.min(fadeDurationMs, Math.max(0, (now - firstSeenAt))); - const progress = (elapsed / fadeDurationMs); - const alpha = (0.4 * progress); + if(!closestMask || (closestMask.score > 3)) return false; - 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); - this._windowReflectionFadeOut.delete(avatar.id); + 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 storedLocation = new Vector3d(); - storedLocation.assign(avatar.location); + const deltaLeft = Math.abs(closestMask.mask.leftSideLoc - leftSideLoc); + const deltaRight = Math.abs(closestMask.mask.rightSideLoc - rightSideLoc); - this._windowReflectionLastVisible.set(avatar.id, { - texture: avatar.texture, - oppositeTexture: avatar.oppositeTexture, - location: storedLocation, - verticalOffset: avatar.verticalOffset || 0, - direction: avatar.direction || 0 - }); + 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); } - for(const [id, lastVisible] of this._windowReflectionLastVisible) + if(oppositeWeight < 1) { - if(visibleAvatarIds.has(id) || this._windowReflectionFadeOut.has(id)) continue; - - if(!lastVisible.texture?.source || lastVisible.texture.source.destroyed || !lastVisible.texture.source.style) - { - this._windowReflectionLastVisible.delete(id); - this._windowReflectionFirstSeenAt.delete(id); - - 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); + const sprite = new Sprite(texture); + sprite.anchor.set(0.5, 1); + sprite.position.set(Math.trunc(x), Math.trunc(y)); + sprite.tint = 0xCFE3FF; + sprite.alpha = alpha * (1 - oppositeWeight); + container.addChild(sprite); } - for(const [id, fadeOut] of this._windowReflectionFadeOut) + if(oppositeWeight > 0 && oppositeTexture) { - 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); + const sprite = new Sprite(oppositeTexture); + sprite.anchor.set(0.5, 1); + sprite.position.set(Math.trunc(x), Math.trunc(y)); + sprite.tint = 0xCFE3FF; + sprite.alpha = alpha * oppositeWeight; + container.addChild(sprite); } - if(!container.children.length) - { - container.destroy({ children: true }); + return true; + }; - if(!avatars.length) - { - this._windowReflectionFirstSeenAt.clear(); - this._windowReflectionLastVisible.clear(); - } + for(const avatar of avatars) + { + if(!avatar?.texture?.source || avatar.texture.source.destroyed || !avatar.texture.source.style || !avatar.location) + continue; - return; - } + let firstSeenAt = this._windowReflectionFirstSeenAt.get(avatar.id); - if(this._maskFilter) container.filters = [this._maskFilter]; + if(firstSeenAt === undefined) firstSeenAt = now; - GetRenderer().render({ - target: this._planeTexture, - container, - transform: this.getMatrixForDimensions(canvasWidth, canvasHeight), - clear: false + const elapsed = Math.min(fadeDurationMs, Math.max(0, (now - firstSeenAt))); + const alpha = (0.4 * (elapsed / fadeDurationMs)); + + 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); + + 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 { this._cornerA.assign(geometry.getScreenPosition(this._location)); diff --git a/packages/session/src/badge/BadgeImageManager.ts b/packages/session/src/badge/BadgeImageManager.ts index 6a51f7e..9bb5647 100644 --- a/packages/session/src/badge/BadgeImageManager.ts +++ b/packages/session/src/badge/BadgeImageManager.ts @@ -248,6 +248,7 @@ export class BadgeImageManager if(!renderedLayers) return false; const texture = TextureUtils.generateTexture(container); + container.destroy({ children: true }); GetAssetManager().setTexture(groupBadge.code, texture); GetEventDispatcher().dispatchEvent(new BadgeImageReadyEvent(groupBadge.code, texture));