diff --git a/src/features/avatar/__tests__/live-expression-preview-test.tsx b/src/features/avatar/__tests__/live-expression-preview-test.tsx index 9a5d738..7e05570 100644 --- a/src/features/avatar/__tests__/live-expression-preview-test.tsx +++ b/src/features/avatar/__tests__/live-expression-preview-test.tsx @@ -33,9 +33,23 @@ it('keeps the headset visible and transformed in a live animation preview', () = ) const headset = view.container.querySelector('.avatar-headset') + const head = view.container.querySelector( + 'path[mask="url(#live-preview-animated-headset-headset-occlusion-head-mask)"]' + ) expect(headset).not.toBeNull() + expect(head?.getAttribute('mask')).toBe( + 'url(#live-preview-animated-headset-headset-occlusion-head-mask)' + ) expect(headset?.getAttribute('d')).toBe(geometry.frontPaths.at(-1)) expect(headset?.getAttribute('transform')).toBe(geometry.pathTransforms?.front.at(-1)) + expect(headset?.getAttribute('mask')).toBe( + 'url(#live-preview-animated-headset-headset-occlusion-mask)' + ) + expect( + view.container + .querySelector('#live-preview-animated-headset-headset-occlusion-left-half rect') + ?.getAttribute('width') + ).toBe('573.13') }) it('keeps the stroked Frame 172 skin unfilled in a live animation preview', () => { @@ -72,4 +86,7 @@ it('keeps the stroked Frame 172 skin unfilled in a live animation preview', () = expect(Array.from(eyes).every(eye => eye.style.fill === 'none')).toBe(true) expect(headset.style.fill).toBe('none') expect(headset?.getAttribute('transform')).toBe(geometry.pathTransforms?.front.at(-1)) + expect(headset?.getAttribute('mask')).toBe( + 'url(#live-preview-animated-stroke-headset-headset-occlusion-mask)' + ) }) diff --git a/src/features/avatar/components/ExpressionWorkspace.tsx b/src/features/avatar/components/ExpressionWorkspace.tsx index d81e6c4..75690e1 100644 --- a/src/features/avatar/components/ExpressionWorkspace.tsx +++ b/src/features/avatar/components/ExpressionWorkspace.tsx @@ -40,7 +40,13 @@ import { LivePixelAvatarCanvas, StaticPixelAvatarCanvas, } from '@/features/rendering/components/PixelAvatarCanvas' -import { SvgTransformPath } from '@/features/rendering/components/AvatarCanvas' +import { + headsetHeadOcclusionMaskValue, + headsetOcclusionMaskValue, + LiveHeadsetOcclusionMask, + StaticHeadsetOcclusionMask, + SvgTransformPath, +} from '@/features/rendering/components/AvatarCanvas' import type { RenderedColors, RenderedScene } from '@/features/rendering/renderedScene' export function SurfaceThumbnail({ surface }: { surface: SurfaceConfig }) { const geometry = getPreviewGeometry(defaultExpression, surface, emptyBodyNodes) @@ -125,12 +131,21 @@ export function ExpressionPreview({ ) } const clipId = `preview-${id}` + const headsetOcclusionId = `${clipId}-headset-occlusion` return (