shaderpad 1.0.0-beta.20 → 1.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,4 +14,4 @@ declare function face(config: {
14
14
  options?: FacePluginOptions;
15
15
  }): (shaderPad: ShaderPad, context: PluginContext) => void;
16
16
 
17
- export { type FacePluginOptions, face };
17
+ export { type FacePluginOptions, face as default };
@@ -14,4 +14,4 @@ declare function face(config: {
14
14
  options?: FacePluginOptions;
15
15
  }): (shaderPad: ShaderPad, context: PluginContext) => void;
16
16
 
17
- export { type FacePluginOptions, face };
17
+ export { type FacePluginOptions, face as default };
@@ -1,4 +1,4 @@
1
- "use strict";var z=Object.create;var T=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var D=Object.getPrototypeOf,U=Object.prototype.hasOwnProperty;var B=(c,s)=>{for(var r in s)T(c,r,{get:s[r],enumerable:!0})},R=(c,s,r,h)=>{if(s&&typeof s=="object"||typeof s=="function")for(let i of H(s))!U.call(c,i)&&i!==r&&T(c,i,{get:()=>s[i],enumerable:!(h=A(s,i))||h.enumerable});return c};var I=(c,s,r)=>(r=c!=null?z(D(c)):{},R(s||!c||!c.__esModule?T(r,"default",{value:c,enumerable:!0}):r,c)),G=c=>R(T({},"__esModule",{value:!0}),c);var V={};B(V,{face:()=>$});module.exports=G(V);var f={LEFT_EYEBROW:[336,296,334,293,300,276,283,282,295,285],LEFT_EYE:[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],LEFT_EYE_CENTER:473,RIGHT_EYEBROW:[70,63,105,66,107,55,65,52,53,46],RIGHT_EYE:[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],RIGHT_EYE_CENTER:468,NOSE_TIP:4,OUTER_LIP:[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],INNER_LIP:[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95]};function $(c){let{textureName:s,options:r}=c,h="https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task";return function(i,N){let{uniforms:F,injectGLSL:O}=N,l=null,b=null,x=-1,y=new Map,m=r?.maxFaces??1,v=512,w=512,u=document.createElement("canvas");u.width=v,u.height=w;let p=u.getContext("2d");p.globalCompositeOperation="lighten";async function S(){try{let{FilesetResolver:t,FaceLandmarker:a}=await import("@mediapipe/tasks-vision");b=await t.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),l=await a.createFromOptions(b,{baseOptions:{modelAssetPath:r?.modelPath||h},runningMode:"VIDEO",numFaces:r?.maxFaces??1,minFaceDetectionConfidence:r?.minFaceDetectionConfidence??.5,minFacePresenceConfidence:r?.minFacePresenceConfidence??.5,minTrackingConfidence:r?.minTrackingConfidence??.5,outputFaceBlendshapes:r?.outputFaceBlendshapes??!1,outputFacialTransformationMatrixes:r?.outputFacialTransformationMatrixes??!1})}catch(t){throw console.error("Failed to initialize Face Landmarker:",t),t}}function Y(t){let a=1/0,n=-1/0,o=1/0,e=-1/0;for(let d of t)a=Math.min(a,d.x),n=Math.max(n,d.x),o=Math.min(o,d.y),e=Math.max(e,d.y);let _=(a+n)/2,g=(o+e)/2;return[_,g]}function E(t,a){p.fillStyle=`rgb(${Math.round(a.r*255)}, ${Math.round(a.g*255)}, ${Math.round(a.b*255)})`;let n=t[0];p.beginPath(),p.moveTo(n.x*u.width,n.y*u.height);for(let o=1;o<t.length;o++){let e=t[o];p.lineTo(e.x*u.width,e.y*u.height)}p.closePath(),p.fill()}async function P(t){if(!l){console.warn("[Face Plugin] Cannot update mask: faceLandmarker missing");return}try{let{FaceLandmarker:a}=await import("@mediapipe/tasks-vision");p.clearRect(0,0,u.width,u.height),t.forEach((n,o)=>{E(f.OUTER_LIP.map(e=>n[e]),{r:.25,g:0,b:0}),E(f.INNER_LIP.map(e=>n[e]),{r:.75,g:0,b:0}),E(a.FACE_LANDMARKS_TESSELATION.map(({start:e})=>n[e]),{r:0,g:.25,b:0}),E(a.FACE_LANDMARKS_FACE_OVAL.map(({start:e})=>n[e]),{r:0,g:1,b:0}),E(f.LEFT_EYEBROW.map(e=>n[e]),{r:0,g:0,b:.15}),E(f.RIGHT_EYEBROW.map(e=>n[e]),{r:0,g:0,b:.35}),E(f.LEFT_EYE.map(e=>n[e]),{r:0,g:0,b:.65}),E(f.RIGHT_EYE.map(e=>n[e]),{r:0,g:0,b:.85})}),i.updateTextures({u_faceMask:u})}catch(a){console.error("[Face Plugin] Failed to generate mask texture:",a)}}function L(t){if(!t.faceLandmarks)return;let a=[],n=[],o=[],e=[];for(let g of t.faceLandmarks){let d=Y(g),k=g[f.LEFT_EYE_CENTER],C=g[f.RIGHT_EYE_CENTER],M=g[f.NOSE_TIP];a.push([d[0],1-d[1]]),n.push([k.x,1-k.y]),o.push([C.x,1-C.y]),e.push([M.x,1-M.y])}P(t.faceLandmarks).catch(g=>{console.warn("Mask texture update error:",g)});let _={u_nFaces:t.faceLandmarks.length};t.faceLandmarks.length&&(F.has("u_faceCenter")&&(_.u_faceCenter=a),F.has("u_leftEye")&&(_.u_leftEye=n),F.has("u_rightEye")&&(_.u_rightEye=o),F.has("u_noseTip")&&(_.u_noseTip=e)),i.updateUniforms(_)}i.registerHook("init",async()=>{i.initializeTexture("u_faceMask",u),i.initializeUniform("u_maxFaces","int",m),i.initializeUniform("u_nFaces","int",0);let t=Array.from({length:m},()=>[.5,.5]);i.initializeUniform("u_faceCenter","float",t,{arrayLength:m}),i.initializeUniform("u_leftEye","float",t,{arrayLength:m}),i.initializeUniform("u_rightEye","float",t,{arrayLength:m}),i.initializeUniform("u_noseTip","float",t,{arrayLength:m}),await S()}),i.registerHook("updateTextures",t=>{Object.entries(t).forEach(([a,n])=>{if(a===s&&(y.set(a,n),!!l))try{if(n instanceof HTMLVideoElement){if(n.currentTime!==x){x=n.currentTime;let o=performance.now(),e=l.detectForVideo(n,o);L(e)}}else if(n instanceof HTMLImageElement){let o=l.detect(n);L(o)}}catch(o){console.warn("Face detection error:",o)}})}),i.registerHook("destroy",()=>{l&&(l.close(),l=null),b=null,y.clear(),u.remove()}),O(`
1
+ "use strict";var z=Object.create;var T=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var D=Object.getPrototypeOf,U=Object.prototype.hasOwnProperty;var B=(c,s)=>{for(var r in s)T(c,r,{get:s[r],enumerable:!0})},R=(c,s,r,h)=>{if(s&&typeof s=="object"||typeof s=="function")for(let i of H(s))!U.call(c,i)&&i!==r&&T(c,i,{get:()=>s[i],enumerable:!(h=A(s,i))||h.enumerable});return c};var I=(c,s,r)=>(r=c!=null?z(D(c)):{},R(s||!c||!c.__esModule?T(r,"default",{value:c,enumerable:!0}):r,c)),G=c=>R(T({},"__esModule",{value:!0}),c);var W={};B(W,{default:()=>V});module.exports=G(W);var f={LEFT_EYEBROW:[336,296,334,293,300,276,283,282,295,285],LEFT_EYE:[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],LEFT_EYE_CENTER:473,RIGHT_EYEBROW:[70,63,105,66,107,55,65,52,53,46],RIGHT_EYE:[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],RIGHT_EYE_CENTER:468,NOSE_TIP:4,OUTER_LIP:[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],INNER_LIP:[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95]};function $(c){let{textureName:s,options:r}=c,h="https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task";return function(i,N){let{uniforms:F,injectGLSL:O}=N,l=null,b=null,x=-1,y=new Map,m=r?.maxFaces??1,v=512,w=512,u=document.createElement("canvas");u.width=v,u.height=w;let p=u.getContext("2d");p.globalCompositeOperation="lighten";async function S(){try{let{FilesetResolver:t,FaceLandmarker:a}=await import("@mediapipe/tasks-vision");b=await t.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),l=await a.createFromOptions(b,{baseOptions:{modelAssetPath:r?.modelPath||h},runningMode:"VIDEO",numFaces:r?.maxFaces??1,minFaceDetectionConfidence:r?.minFaceDetectionConfidence??.5,minFacePresenceConfidence:r?.minFacePresenceConfidence??.5,minTrackingConfidence:r?.minTrackingConfidence??.5,outputFaceBlendshapes:r?.outputFaceBlendshapes??!1,outputFacialTransformationMatrixes:r?.outputFacialTransformationMatrixes??!1})}catch(t){throw console.error("Failed to initialize Face Landmarker:",t),t}}function Y(t){let a=1/0,n=-1/0,o=1/0,e=-1/0;for(let d of t)a=Math.min(a,d.x),n=Math.max(n,d.x),o=Math.min(o,d.y),e=Math.max(e,d.y);let _=(a+n)/2,g=(o+e)/2;return[_,g]}function E(t,a){p.fillStyle=`rgb(${Math.round(a.r*255)}, ${Math.round(a.g*255)}, ${Math.round(a.b*255)})`;let n=t[0];p.beginPath(),p.moveTo(n.x*u.width,n.y*u.height);for(let o=1;o<t.length;o++){let e=t[o];p.lineTo(e.x*u.width,e.y*u.height)}p.closePath(),p.fill()}async function P(t){if(!l){console.warn("[Face Plugin] Cannot update mask: faceLandmarker missing");return}try{let{FaceLandmarker:a}=await import("@mediapipe/tasks-vision");p.clearRect(0,0,u.width,u.height),t.forEach((n,o)=>{E(f.OUTER_LIP.map(e=>n[e]),{r:.25,g:0,b:0}),E(f.INNER_LIP.map(e=>n[e]),{r:.75,g:0,b:0}),E(a.FACE_LANDMARKS_TESSELATION.map(({start:e})=>n[e]),{r:0,g:.25,b:0}),E(a.FACE_LANDMARKS_FACE_OVAL.map(({start:e})=>n[e]),{r:0,g:1,b:0}),E(f.LEFT_EYEBROW.map(e=>n[e]),{r:0,g:0,b:.15}),E(f.RIGHT_EYEBROW.map(e=>n[e]),{r:0,g:0,b:.35}),E(f.LEFT_EYE.map(e=>n[e]),{r:0,g:0,b:.65}),E(f.RIGHT_EYE.map(e=>n[e]),{r:0,g:0,b:.85})}),i.updateTextures({u_faceMask:u})}catch(a){console.error("[Face Plugin] Failed to generate mask texture:",a)}}function L(t){if(!t.faceLandmarks)return;let a=[],n=[],o=[],e=[];for(let g of t.faceLandmarks){let d=Y(g),k=g[f.LEFT_EYE_CENTER],C=g[f.RIGHT_EYE_CENTER],M=g[f.NOSE_TIP];a.push([d[0],1-d[1]]),n.push([k.x,1-k.y]),o.push([C.x,1-C.y]),e.push([M.x,1-M.y])}P(t.faceLandmarks).catch(g=>{console.warn("Mask texture update error:",g)});let _={u_nFaces:t.faceLandmarks.length};t.faceLandmarks.length&&(F.has("u_faceCenter")&&(_.u_faceCenter=a),F.has("u_leftEye")&&(_.u_leftEye=n),F.has("u_rightEye")&&(_.u_rightEye=o),F.has("u_noseTip")&&(_.u_noseTip=e)),i.updateUniforms(_)}i.registerHook("init",async()=>{i.initializeTexture("u_faceMask",u),i.initializeUniform("u_maxFaces","int",m),i.initializeUniform("u_nFaces","int",0);let t=Array.from({length:m},()=>[.5,.5]);i.initializeUniform("u_faceCenter","float",t,{arrayLength:m}),i.initializeUniform("u_leftEye","float",t,{arrayLength:m}),i.initializeUniform("u_rightEye","float",t,{arrayLength:m}),i.initializeUniform("u_noseTip","float",t,{arrayLength:m}),await S()}),i.registerHook("updateTextures",t=>{Object.entries(t).forEach(([a,n])=>{if(a===s&&(y.set(a,n),!!l))try{if(n instanceof HTMLVideoElement){if(n.currentTime!==x){x=n.currentTime;let o=performance.now(),e=l.detectForVideo(n,o);L(e)}}else if(n instanceof HTMLImageElement){let o=l.detect(n);L(o)}}catch(o){console.warn("Face detection error:",o)}})}),i.registerHook("destroy",()=>{l&&(l.close(),l=null),b=null,y.clear(),u.remove()}),O(`
2
2
  uniform int u_maxFaces;
3
3
  uniform int u_nFaces;
4
4
  uniform vec2 u_faceCenter[${m}];
@@ -8,5 +8,5 @@ uniform vec2 u_noseTip[${m}];
8
8
  uniform sampler2D u_faceMask;
9
9
  float getFace(vec2 pos) { return texture(u_faceMask, pos).g; }
10
10
  float getEye(vec2 pos) { return texture(u_faceMask, pos).b; }
11
- float getMouth(vec2 pos) { return texture(u_faceMask, pos).r; }`)}}0&&(module.exports={face});
11
+ float getMouth(vec2 pos) { return texture(u_faceMask, pos).r; }`)}}var V=$;
12
12
  //# sourceMappingURL=face.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n}\n\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: [336, 296, 334, 293, 300, 276, 283, 282, 295, 285],\n\tLEFT_EYE: [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382],\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: [70, 63, 105, 66, 107, 55, 65, 52, 53, 46],\n\tRIGHT_EYE: [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7],\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_LIP: [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146],\n\tINNER_LIP: [78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95],\n};\n\nexport function face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { uniforms, injectGLSL } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tconst maskWidth = 512;\n\t\tconst maskHeight = 512;\n\t\tconst faceMaskCanvas = document.createElement('canvas');\n\t\tfaceMaskCanvas.width = maskWidth;\n\t\tfaceMaskCanvas.height = maskHeight;\n\t\tconst faceMaskCtx = faceMaskCanvas.getContext('2d')!;\n\t\tfaceMaskCtx.globalCompositeOperation = 'lighten'; // Keep the highest value of each channel.\n\n\t\tasync function initializeFaceLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\tfaceLandmarker = await FaceLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options?.maxFaces ?? 1,\n\t\t\t\t\tminFaceDetectionConfidence: options?.minFaceDetectionConfidence ?? 0.5,\n\t\t\t\t\tminFacePresenceConfidence: options?.minFacePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputFaceBlendshapes: options?.outputFaceBlendshapes ?? false,\n\t\t\t\t\toutputFacialTransformationMatrixes: options?.outputFacialTransformationMatrixes ?? false,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to initialize Face Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateFaceCenter(landmarks: NormalizedLandmark[]): [number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity;\n\n\t\t\t// Calculate bounding box center.\n\t\t\tfor (const landmark of landmarks) {\n\t\t\t\tminX = Math.min(minX, landmark.x);\n\t\t\t\tmaxX = Math.max(maxX, landmark.x);\n\t\t\t\tminY = Math.min(minY, landmark.y);\n\t\t\t\tmaxY = Math.max(maxY, landmark.y);\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\treturn [centerX, centerY];\n\t\t}\n\n\t\tfunction fillRegion(landmarks: NormalizedLandmark[], color: { r: number; g: number; b: number }) {\n\t\t\tfaceMaskCtx.fillStyle = `rgb(${Math.round(color.r * 255)}, ${Math.round(color.g * 255)}, ${Math.round(\n\t\t\t\tcolor.b * 255\n\t\t\t)})`;\n\t\t\tconst origin = landmarks[0];\n\t\t\tfaceMaskCtx.beginPath();\n\t\t\tfaceMaskCtx.moveTo(origin.x * faceMaskCanvas.width, origin.y * faceMaskCanvas.height);\n\t\t\tfor (let i = 1; i < landmarks.length; i++) {\n\t\t\t\tconst destination = landmarks[i];\n\t\t\t\tfaceMaskCtx.lineTo(destination.x * faceMaskCanvas.width, destination.y * faceMaskCanvas.height);\n\t\t\t}\n\t\t\tfaceMaskCtx.closePath();\n\t\t\tfaceMaskCtx.fill();\n\t\t}\n\n\t\tasync function updateMaskTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!faceLandmarker) {\n\t\t\t\tconsole.warn('[Face Plugin] Cannot update mask: faceLandmarker missing');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tfaceMaskCtx.clearRect(0, 0, faceMaskCanvas.width, faceMaskCanvas.height);\n\n\t\t\t\tfaces.forEach((landmarks, i) => {\n\t\t\t\t\t// Build combined mask with RGBA channels\n\t\t\t\t\t// R: Mouth | G: Face | B: Eyes\n\t\t\t\t\t// Mouth (red channel).\n\t\t\t\t\t// Lips.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.OUTER_LIP.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0.25, g: 0, b: 0 }\n\t\t\t\t\t);\n\t\t\t\t\t// Inner mouth.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.INNER_LIP.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0.75, g: 0, b: 0 }\n\t\t\t\t\t);\n\n\t\t\t\t\t// Face (green channel).\n\t\t\t\t\t// Entire face.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tFaceLandmarker.FACE_LANDMARKS_TESSELATION.map(({ start }) => landmarks[start]),\n\t\t\t\t\t\t{ r: 0, g: 0.25, b: 0 }\n\t\t\t\t\t);\n\t\t\t\t\t// Face contour (excludes nose in profile view).\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tFaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => landmarks[start]),\n\t\t\t\t\t\t{ r: 0, g: 1, b: 0 }\n\t\t\t\t\t);\n\n\t\t\t\t\t// Eyes (blue channel).\n\t\t\t\t\t// Eyebrows.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.LEFT_EYEBROW.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tr: 0,\n\t\t\t\t\t\t\tg: 0,\n\t\t\t\t\t\t\tb: 0.15,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.RIGHT_EYEBROW.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tr: 0,\n\t\t\t\t\t\t\tg: 0,\n\t\t\t\t\t\t\tb: 0.35,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t\t// Eyes.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.LEFT_EYE.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0, g: 0, b: 0.65 }\n\t\t\t\t\t);\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.RIGHT_EYE.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0, g: 0, b: 0.85 }\n\t\t\t\t\t);\n\t\t\t\t});\n\n\t\t\t\tshaderPad.updateTextures({ u_faceMask: faceMaskCanvas });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to generate mask texture:', error);\n\t\t\t}\n\t\t}\n\n\t\tfunction processFaceResults(result: any) {\n\t\t\tif (!result.faceLandmarks) return;\n\n\t\t\tconst faceCenters: [number, number][] = [];\n\t\t\tconst leftEyes: [number, number][] = [];\n\t\t\tconst rightEyes: [number, number][] = [];\n\t\t\tconst noseTips: [number, number][] = [];\n\t\t\tfor (const landmarks of result.faceLandmarks) {\n\t\t\t\tconst faceCenter = calculateFaceCenter(landmarks);\n\t\t\t\tconst leftEye = landmarks[LANDMARK_INDICES.LEFT_EYE_CENTER];\n\t\t\t\tconst rightEye = landmarks[LANDMARK_INDICES.RIGHT_EYE_CENTER];\n\t\t\t\tconst noseTip = landmarks[LANDMARK_INDICES.NOSE_TIP];\n\n\t\t\t\t// Invert Y-axis to match WebGL coordinate system.\n\t\t\t\tfaceCenters.push([faceCenter[0], 1.0 - faceCenter[1]]);\n\t\t\t\tleftEyes.push([leftEye.x, 1.0 - leftEye.y]);\n\t\t\t\trightEyes.push([rightEye.x, 1.0 - rightEye.y]);\n\t\t\t\tnoseTips.push([noseTip.x, 1.0 - noseTip.y]);\n\t\t\t}\n\n\t\t\tupdateMaskTexture(result.faceLandmarks).catch(error => {\n\t\t\t\tconsole.warn('Mask texture update error:', error);\n\t\t\t});\n\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nFaces: result.faceLandmarks.length };\n\t\t\tif (result.faceLandmarks.length) {\n\t\t\t\tif (uniforms.has('u_faceCenter')) updates.u_faceCenter = faceCenters;\n\t\t\t\tif (uniforms.has('u_leftEye')) updates.u_leftEye = leftEyes;\n\t\t\t\tif (uniforms.has('u_rightEye')) updates.u_rightEye = rightEyes;\n\t\t\t\tif (uniforms.has('u_noseTip')) updates.u_noseTip = noseTips;\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', faceMaskCanvas);\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tconst defaultFaceData: [number, number][] = Array.from({ length: maxFaces }, () => [0.5, 0.5]);\n\t\t\tshaderPad.initializeUniform('u_faceCenter', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_leftEye', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_rightEye', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_noseTip', 'float', defaultFaceData, { arrayLength: maxFaces });\n\n\t\t\tawait initializeFaceLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!faceLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = faceLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = faceLandmarker.detect(source);\n\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Face detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (faceLandmarker) {\n\t\t\t\tfaceLandmarker.close();\n\t\t\t\tfaceLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tfaceMaskCanvas.remove();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform vec2 u_faceCenter[${maxFaces}];\nuniform vec2 u_leftEye[${maxFaces}];\nuniform vec2 u_rightEye[${maxFaces}];\nuniform vec2 u_noseTip[${maxFaces}];\nuniform sampler2D u_faceMask;\nfloat getFace(vec2 pos) { return texture(u_faceMask, pos).g; }\nfloat getEye(vec2 pos) { return texture(u_faceMask, pos).b; }\nfloat getMouth(vec2 pos) { return texture(u_faceMask, pos).r; }`);\n\t};\n}\n"],"mappings":"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","names":["face_exports","__export","face","__toCommonJS","LANDMARK_INDICES","config","textureName","options","defaultModelPath","shaderPad","context","uniforms","injectGLSL","faceLandmarker","vision","lastVideoTime","textureSources","maxFaces","maskWidth","maskHeight","faceMaskCanvas","faceMaskCtx","initializeFaceLandmarker","FilesetResolver","FaceLandmarker","error","calculateFaceCenter","landmarks","minX","maxX","minY","maxY","landmark","centerX","centerY","fillRegion","color","origin","i","destination","updateMaskTexture","faces","idx","start","processFaceResults","result","faceCenters","leftEyes","rightEyes","noseTips","faceCenter","leftEye","rightEye","noseTip","updates","defaultFaceData","name","source","timestamp"]}
1
+ {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n}\n\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: [336, 296, 334, 293, 300, 276, 283, 282, 295, 285],\n\tLEFT_EYE: [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382],\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: [70, 63, 105, 66, 107, 55, 65, 52, 53, 46],\n\tRIGHT_EYE: [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7],\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_LIP: [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146],\n\tINNER_LIP: [78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95],\n};\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { uniforms, injectGLSL } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tconst maskWidth = 512;\n\t\tconst maskHeight = 512;\n\t\tconst faceMaskCanvas = document.createElement('canvas');\n\t\tfaceMaskCanvas.width = maskWidth;\n\t\tfaceMaskCanvas.height = maskHeight;\n\t\tconst faceMaskCtx = faceMaskCanvas.getContext('2d')!;\n\t\tfaceMaskCtx.globalCompositeOperation = 'lighten'; // Keep the highest value of each channel.\n\n\t\tasync function initializeFaceLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\tfaceLandmarker = await FaceLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options?.maxFaces ?? 1,\n\t\t\t\t\tminFaceDetectionConfidence: options?.minFaceDetectionConfidence ?? 0.5,\n\t\t\t\t\tminFacePresenceConfidence: options?.minFacePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputFaceBlendshapes: options?.outputFaceBlendshapes ?? false,\n\t\t\t\t\toutputFacialTransformationMatrixes: options?.outputFacialTransformationMatrixes ?? false,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to initialize Face Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateFaceCenter(landmarks: NormalizedLandmark[]): [number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity;\n\n\t\t\t// Calculate bounding box center.\n\t\t\tfor (const landmark of landmarks) {\n\t\t\t\tminX = Math.min(minX, landmark.x);\n\t\t\t\tmaxX = Math.max(maxX, landmark.x);\n\t\t\t\tminY = Math.min(minY, landmark.y);\n\t\t\t\tmaxY = Math.max(maxY, landmark.y);\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\treturn [centerX, centerY];\n\t\t}\n\n\t\tfunction fillRegion(landmarks: NormalizedLandmark[], color: { r: number; g: number; b: number }) {\n\t\t\tfaceMaskCtx.fillStyle = `rgb(${Math.round(color.r * 255)}, ${Math.round(color.g * 255)}, ${Math.round(\n\t\t\t\tcolor.b * 255\n\t\t\t)})`;\n\t\t\tconst origin = landmarks[0];\n\t\t\tfaceMaskCtx.beginPath();\n\t\t\tfaceMaskCtx.moveTo(origin.x * faceMaskCanvas.width, origin.y * faceMaskCanvas.height);\n\t\t\tfor (let i = 1; i < landmarks.length; i++) {\n\t\t\t\tconst destination = landmarks[i];\n\t\t\t\tfaceMaskCtx.lineTo(destination.x * faceMaskCanvas.width, destination.y * faceMaskCanvas.height);\n\t\t\t}\n\t\t\tfaceMaskCtx.closePath();\n\t\t\tfaceMaskCtx.fill();\n\t\t}\n\n\t\tasync function updateMaskTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!faceLandmarker) {\n\t\t\t\tconsole.warn('[Face Plugin] Cannot update mask: faceLandmarker missing');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tfaceMaskCtx.clearRect(0, 0, faceMaskCanvas.width, faceMaskCanvas.height);\n\n\t\t\t\tfaces.forEach((landmarks, i) => {\n\t\t\t\t\t// Build combined mask with RGBA channels\n\t\t\t\t\t// R: Mouth | G: Face | B: Eyes\n\t\t\t\t\t// Mouth (red channel).\n\t\t\t\t\t// Lips.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.OUTER_LIP.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0.25, g: 0, b: 0 }\n\t\t\t\t\t);\n\t\t\t\t\t// Inner mouth.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.INNER_LIP.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0.75, g: 0, b: 0 }\n\t\t\t\t\t);\n\n\t\t\t\t\t// Face (green channel).\n\t\t\t\t\t// Entire face.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tFaceLandmarker.FACE_LANDMARKS_TESSELATION.map(({ start }) => landmarks[start]),\n\t\t\t\t\t\t{ r: 0, g: 0.25, b: 0 }\n\t\t\t\t\t);\n\t\t\t\t\t// Face contour (excludes nose in profile view).\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tFaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => landmarks[start]),\n\t\t\t\t\t\t{ r: 0, g: 1, b: 0 }\n\t\t\t\t\t);\n\n\t\t\t\t\t// Eyes (blue channel).\n\t\t\t\t\t// Eyebrows.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.LEFT_EYEBROW.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tr: 0,\n\t\t\t\t\t\t\tg: 0,\n\t\t\t\t\t\t\tb: 0.15,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.RIGHT_EYEBROW.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tr: 0,\n\t\t\t\t\t\t\tg: 0,\n\t\t\t\t\t\t\tb: 0.35,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t\t// Eyes.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.LEFT_EYE.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0, g: 0, b: 0.65 }\n\t\t\t\t\t);\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.RIGHT_EYE.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0, g: 0, b: 0.85 }\n\t\t\t\t\t);\n\t\t\t\t});\n\n\t\t\t\tshaderPad.updateTextures({ u_faceMask: faceMaskCanvas });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to generate mask texture:', error);\n\t\t\t}\n\t\t}\n\n\t\tfunction processFaceResults(result: any) {\n\t\t\tif (!result.faceLandmarks) return;\n\n\t\t\tconst faceCenters: [number, number][] = [];\n\t\t\tconst leftEyes: [number, number][] = [];\n\t\t\tconst rightEyes: [number, number][] = [];\n\t\t\tconst noseTips: [number, number][] = [];\n\t\t\tfor (const landmarks of result.faceLandmarks) {\n\t\t\t\tconst faceCenter = calculateFaceCenter(landmarks);\n\t\t\t\tconst leftEye = landmarks[LANDMARK_INDICES.LEFT_EYE_CENTER];\n\t\t\t\tconst rightEye = landmarks[LANDMARK_INDICES.RIGHT_EYE_CENTER];\n\t\t\t\tconst noseTip = landmarks[LANDMARK_INDICES.NOSE_TIP];\n\n\t\t\t\t// Invert Y-axis to match WebGL coordinate system.\n\t\t\t\tfaceCenters.push([faceCenter[0], 1.0 - faceCenter[1]]);\n\t\t\t\tleftEyes.push([leftEye.x, 1.0 - leftEye.y]);\n\t\t\t\trightEyes.push([rightEye.x, 1.0 - rightEye.y]);\n\t\t\t\tnoseTips.push([noseTip.x, 1.0 - noseTip.y]);\n\t\t\t}\n\n\t\t\tupdateMaskTexture(result.faceLandmarks).catch(error => {\n\t\t\t\tconsole.warn('Mask texture update error:', error);\n\t\t\t});\n\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nFaces: result.faceLandmarks.length };\n\t\t\tif (result.faceLandmarks.length) {\n\t\t\t\tif (uniforms.has('u_faceCenter')) updates.u_faceCenter = faceCenters;\n\t\t\t\tif (uniforms.has('u_leftEye')) updates.u_leftEye = leftEyes;\n\t\t\t\tif (uniforms.has('u_rightEye')) updates.u_rightEye = rightEyes;\n\t\t\t\tif (uniforms.has('u_noseTip')) updates.u_noseTip = noseTips;\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', faceMaskCanvas);\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tconst defaultFaceData: [number, number][] = Array.from({ length: maxFaces }, () => [0.5, 0.5]);\n\t\t\tshaderPad.initializeUniform('u_faceCenter', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_leftEye', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_rightEye', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_noseTip', 'float', defaultFaceData, { arrayLength: maxFaces });\n\n\t\t\tawait initializeFaceLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!faceLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = faceLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = faceLandmarker.detect(source);\n\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Face detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (faceLandmarker) {\n\t\t\t\tfaceLandmarker.close();\n\t\t\t\tfaceLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tfaceMaskCanvas.remove();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform vec2 u_faceCenter[${maxFaces}];\nuniform vec2 u_leftEye[${maxFaces}];\nuniform vec2 u_rightEye[${maxFaces}];\nuniform vec2 u_noseTip[${maxFaces}];\nuniform sampler2D u_faceMask;\nfloat getFace(vec2 pos) { return texture(u_faceMask, pos).g; }\nfloat getEye(vec2 pos) { return texture(u_faceMask, pos).b; }\nfloat getMouth(vec2 pos) { return texture(u_faceMask, pos).r; }`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["face_exports","__export","face_default","__toCommonJS","LANDMARK_INDICES","face","config","textureName","options","defaultModelPath","shaderPad","context","uniforms","injectGLSL","faceLandmarker","vision","lastVideoTime","textureSources","maxFaces","maskWidth","maskHeight","faceMaskCanvas","faceMaskCtx","initializeFaceLandmarker","FilesetResolver","FaceLandmarker","error","calculateFaceCenter","landmarks","minX","maxX","minY","maxY","landmark","centerX","centerY","fillRegion","color","origin","i","destination","updateMaskTexture","faces","idx","start","processFaceResults","result","faceCenters","leftEyes","rightEyes","noseTips","faceCenter","leftEye","rightEye","noseTip","updates","defaultFaceData","name","source","timestamp"]}
@@ -8,5 +8,5 @@ uniform vec2 u_noseTip[${c}];
8
8
  uniform sampler2D u_faceMask;
9
9
  float getFace(vec2 pos) { return texture(u_faceMask, pos).g; }
10
10
  float getEye(vec2 pos) { return texture(u_faceMask, pos).b; }
11
- float getMouth(vec2 pos) { return texture(u_faceMask, pos).r; }`)}}export{S as face};
11
+ float getMouth(vec2 pos) { return texture(u_faceMask, pos).r; }`)}}var Y=S;export{Y as default};
12
12
  //# sourceMappingURL=face.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n}\n\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: [336, 296, 334, 293, 300, 276, 283, 282, 295, 285],\n\tLEFT_EYE: [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382],\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: [70, 63, 105, 66, 107, 55, 65, 52, 53, 46],\n\tRIGHT_EYE: [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7],\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_LIP: [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146],\n\tINNER_LIP: [78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95],\n};\n\nexport function face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { uniforms, injectGLSL } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tconst maskWidth = 512;\n\t\tconst maskHeight = 512;\n\t\tconst faceMaskCanvas = document.createElement('canvas');\n\t\tfaceMaskCanvas.width = maskWidth;\n\t\tfaceMaskCanvas.height = maskHeight;\n\t\tconst faceMaskCtx = faceMaskCanvas.getContext('2d')!;\n\t\tfaceMaskCtx.globalCompositeOperation = 'lighten'; // Keep the highest value of each channel.\n\n\t\tasync function initializeFaceLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\tfaceLandmarker = await FaceLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options?.maxFaces ?? 1,\n\t\t\t\t\tminFaceDetectionConfidence: options?.minFaceDetectionConfidence ?? 0.5,\n\t\t\t\t\tminFacePresenceConfidence: options?.minFacePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputFaceBlendshapes: options?.outputFaceBlendshapes ?? false,\n\t\t\t\t\toutputFacialTransformationMatrixes: options?.outputFacialTransformationMatrixes ?? false,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to initialize Face Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateFaceCenter(landmarks: NormalizedLandmark[]): [number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity;\n\n\t\t\t// Calculate bounding box center.\n\t\t\tfor (const landmark of landmarks) {\n\t\t\t\tminX = Math.min(minX, landmark.x);\n\t\t\t\tmaxX = Math.max(maxX, landmark.x);\n\t\t\t\tminY = Math.min(minY, landmark.y);\n\t\t\t\tmaxY = Math.max(maxY, landmark.y);\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\treturn [centerX, centerY];\n\t\t}\n\n\t\tfunction fillRegion(landmarks: NormalizedLandmark[], color: { r: number; g: number; b: number }) {\n\t\t\tfaceMaskCtx.fillStyle = `rgb(${Math.round(color.r * 255)}, ${Math.round(color.g * 255)}, ${Math.round(\n\t\t\t\tcolor.b * 255\n\t\t\t)})`;\n\t\t\tconst origin = landmarks[0];\n\t\t\tfaceMaskCtx.beginPath();\n\t\t\tfaceMaskCtx.moveTo(origin.x * faceMaskCanvas.width, origin.y * faceMaskCanvas.height);\n\t\t\tfor (let i = 1; i < landmarks.length; i++) {\n\t\t\t\tconst destination = landmarks[i];\n\t\t\t\tfaceMaskCtx.lineTo(destination.x * faceMaskCanvas.width, destination.y * faceMaskCanvas.height);\n\t\t\t}\n\t\t\tfaceMaskCtx.closePath();\n\t\t\tfaceMaskCtx.fill();\n\t\t}\n\n\t\tasync function updateMaskTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!faceLandmarker) {\n\t\t\t\tconsole.warn('[Face Plugin] Cannot update mask: faceLandmarker missing');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tfaceMaskCtx.clearRect(0, 0, faceMaskCanvas.width, faceMaskCanvas.height);\n\n\t\t\t\tfaces.forEach((landmarks, i) => {\n\t\t\t\t\t// Build combined mask with RGBA channels\n\t\t\t\t\t// R: Mouth | G: Face | B: Eyes\n\t\t\t\t\t// Mouth (red channel).\n\t\t\t\t\t// Lips.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.OUTER_LIP.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0.25, g: 0, b: 0 }\n\t\t\t\t\t);\n\t\t\t\t\t// Inner mouth.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.INNER_LIP.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0.75, g: 0, b: 0 }\n\t\t\t\t\t);\n\n\t\t\t\t\t// Face (green channel).\n\t\t\t\t\t// Entire face.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tFaceLandmarker.FACE_LANDMARKS_TESSELATION.map(({ start }) => landmarks[start]),\n\t\t\t\t\t\t{ r: 0, g: 0.25, b: 0 }\n\t\t\t\t\t);\n\t\t\t\t\t// Face contour (excludes nose in profile view).\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tFaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => landmarks[start]),\n\t\t\t\t\t\t{ r: 0, g: 1, b: 0 }\n\t\t\t\t\t);\n\n\t\t\t\t\t// Eyes (blue channel).\n\t\t\t\t\t// Eyebrows.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.LEFT_EYEBROW.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tr: 0,\n\t\t\t\t\t\t\tg: 0,\n\t\t\t\t\t\t\tb: 0.15,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.RIGHT_EYEBROW.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tr: 0,\n\t\t\t\t\t\t\tg: 0,\n\t\t\t\t\t\t\tb: 0.35,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t\t// Eyes.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.LEFT_EYE.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0, g: 0, b: 0.65 }\n\t\t\t\t\t);\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.RIGHT_EYE.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0, g: 0, b: 0.85 }\n\t\t\t\t\t);\n\t\t\t\t});\n\n\t\t\t\tshaderPad.updateTextures({ u_faceMask: faceMaskCanvas });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to generate mask texture:', error);\n\t\t\t}\n\t\t}\n\n\t\tfunction processFaceResults(result: any) {\n\t\t\tif (!result.faceLandmarks) return;\n\n\t\t\tconst faceCenters: [number, number][] = [];\n\t\t\tconst leftEyes: [number, number][] = [];\n\t\t\tconst rightEyes: [number, number][] = [];\n\t\t\tconst noseTips: [number, number][] = [];\n\t\t\tfor (const landmarks of result.faceLandmarks) {\n\t\t\t\tconst faceCenter = calculateFaceCenter(landmarks);\n\t\t\t\tconst leftEye = landmarks[LANDMARK_INDICES.LEFT_EYE_CENTER];\n\t\t\t\tconst rightEye = landmarks[LANDMARK_INDICES.RIGHT_EYE_CENTER];\n\t\t\t\tconst noseTip = landmarks[LANDMARK_INDICES.NOSE_TIP];\n\n\t\t\t\t// Invert Y-axis to match WebGL coordinate system.\n\t\t\t\tfaceCenters.push([faceCenter[0], 1.0 - faceCenter[1]]);\n\t\t\t\tleftEyes.push([leftEye.x, 1.0 - leftEye.y]);\n\t\t\t\trightEyes.push([rightEye.x, 1.0 - rightEye.y]);\n\t\t\t\tnoseTips.push([noseTip.x, 1.0 - noseTip.y]);\n\t\t\t}\n\n\t\t\tupdateMaskTexture(result.faceLandmarks).catch(error => {\n\t\t\t\tconsole.warn('Mask texture update error:', error);\n\t\t\t});\n\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nFaces: result.faceLandmarks.length };\n\t\t\tif (result.faceLandmarks.length) {\n\t\t\t\tif (uniforms.has('u_faceCenter')) updates.u_faceCenter = faceCenters;\n\t\t\t\tif (uniforms.has('u_leftEye')) updates.u_leftEye = leftEyes;\n\t\t\t\tif (uniforms.has('u_rightEye')) updates.u_rightEye = rightEyes;\n\t\t\t\tif (uniforms.has('u_noseTip')) updates.u_noseTip = noseTips;\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', faceMaskCanvas);\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tconst defaultFaceData: [number, number][] = Array.from({ length: maxFaces }, () => [0.5, 0.5]);\n\t\t\tshaderPad.initializeUniform('u_faceCenter', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_leftEye', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_rightEye', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_noseTip', 'float', defaultFaceData, { arrayLength: maxFaces });\n\n\t\t\tawait initializeFaceLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!faceLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = faceLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = faceLandmarker.detect(source);\n\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Face detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (faceLandmarker) {\n\t\t\t\tfaceLandmarker.close();\n\t\t\t\tfaceLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tfaceMaskCanvas.remove();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform vec2 u_faceCenter[${maxFaces}];\nuniform vec2 u_leftEye[${maxFaces}];\nuniform vec2 u_rightEye[${maxFaces}];\nuniform vec2 u_noseTip[${maxFaces}];\nuniform sampler2D u_faceMask;\nfloat getFace(vec2 pos) { return texture(u_faceMask, pos).g; }\nfloat getEye(vec2 pos) { return texture(u_faceMask, pos).b; }\nfloat getMouth(vec2 pos) { return texture(u_faceMask, pos).r; }`);\n\t};\n}\n"],"mappings":"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","names":["LANDMARK_INDICES","face","config","textureName","options","defaultModelPath","shaderPad","context","uniforms","injectGLSL","faceLandmarker","vision","lastVideoTime","textureSources","maxFaces","maskWidth","maskHeight","faceMaskCanvas","faceMaskCtx","initializeFaceLandmarker","FilesetResolver","FaceLandmarker","error","calculateFaceCenter","landmarks","minX","maxX","minY","maxY","landmark","centerX","centerY","fillRegion","color","origin","destination","updateMaskTexture","faces","idx","start","processFaceResults","result","faceCenters","leftEyes","rightEyes","noseTips","faceCenter","leftEye","rightEye","noseTip","updates","defaultFaceData","name","source","timestamp"]}
1
+ {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n}\n\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: [336, 296, 334, 293, 300, 276, 283, 282, 295, 285],\n\tLEFT_EYE: [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382],\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: [70, 63, 105, 66, 107, 55, 65, 52, 53, 46],\n\tRIGHT_EYE: [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7],\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_LIP: [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146],\n\tINNER_LIP: [78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95],\n};\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { uniforms, injectGLSL } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tconst maskWidth = 512;\n\t\tconst maskHeight = 512;\n\t\tconst faceMaskCanvas = document.createElement('canvas');\n\t\tfaceMaskCanvas.width = maskWidth;\n\t\tfaceMaskCanvas.height = maskHeight;\n\t\tconst faceMaskCtx = faceMaskCanvas.getContext('2d')!;\n\t\tfaceMaskCtx.globalCompositeOperation = 'lighten'; // Keep the highest value of each channel.\n\n\t\tasync function initializeFaceLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\tfaceLandmarker = await FaceLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options?.maxFaces ?? 1,\n\t\t\t\t\tminFaceDetectionConfidence: options?.minFaceDetectionConfidence ?? 0.5,\n\t\t\t\t\tminFacePresenceConfidence: options?.minFacePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputFaceBlendshapes: options?.outputFaceBlendshapes ?? false,\n\t\t\t\t\toutputFacialTransformationMatrixes: options?.outputFacialTransformationMatrixes ?? false,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to initialize Face Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateFaceCenter(landmarks: NormalizedLandmark[]): [number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity;\n\n\t\t\t// Calculate bounding box center.\n\t\t\tfor (const landmark of landmarks) {\n\t\t\t\tminX = Math.min(minX, landmark.x);\n\t\t\t\tmaxX = Math.max(maxX, landmark.x);\n\t\t\t\tminY = Math.min(minY, landmark.y);\n\t\t\t\tmaxY = Math.max(maxY, landmark.y);\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\treturn [centerX, centerY];\n\t\t}\n\n\t\tfunction fillRegion(landmarks: NormalizedLandmark[], color: { r: number; g: number; b: number }) {\n\t\t\tfaceMaskCtx.fillStyle = `rgb(${Math.round(color.r * 255)}, ${Math.round(color.g * 255)}, ${Math.round(\n\t\t\t\tcolor.b * 255\n\t\t\t)})`;\n\t\t\tconst origin = landmarks[0];\n\t\t\tfaceMaskCtx.beginPath();\n\t\t\tfaceMaskCtx.moveTo(origin.x * faceMaskCanvas.width, origin.y * faceMaskCanvas.height);\n\t\t\tfor (let i = 1; i < landmarks.length; i++) {\n\t\t\t\tconst destination = landmarks[i];\n\t\t\t\tfaceMaskCtx.lineTo(destination.x * faceMaskCanvas.width, destination.y * faceMaskCanvas.height);\n\t\t\t}\n\t\t\tfaceMaskCtx.closePath();\n\t\t\tfaceMaskCtx.fill();\n\t\t}\n\n\t\tasync function updateMaskTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!faceLandmarker) {\n\t\t\t\tconsole.warn('[Face Plugin] Cannot update mask: faceLandmarker missing');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { FaceLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tfaceMaskCtx.clearRect(0, 0, faceMaskCanvas.width, faceMaskCanvas.height);\n\n\t\t\t\tfaces.forEach((landmarks, i) => {\n\t\t\t\t\t// Build combined mask with RGBA channels\n\t\t\t\t\t// R: Mouth | G: Face | B: Eyes\n\t\t\t\t\t// Mouth (red channel).\n\t\t\t\t\t// Lips.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.OUTER_LIP.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0.25, g: 0, b: 0 }\n\t\t\t\t\t);\n\t\t\t\t\t// Inner mouth.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.INNER_LIP.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0.75, g: 0, b: 0 }\n\t\t\t\t\t);\n\n\t\t\t\t\t// Face (green channel).\n\t\t\t\t\t// Entire face.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tFaceLandmarker.FACE_LANDMARKS_TESSELATION.map(({ start }) => landmarks[start]),\n\t\t\t\t\t\t{ r: 0, g: 0.25, b: 0 }\n\t\t\t\t\t);\n\t\t\t\t\t// Face contour (excludes nose in profile view).\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tFaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => landmarks[start]),\n\t\t\t\t\t\t{ r: 0, g: 1, b: 0 }\n\t\t\t\t\t);\n\n\t\t\t\t\t// Eyes (blue channel).\n\t\t\t\t\t// Eyebrows.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.LEFT_EYEBROW.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tr: 0,\n\t\t\t\t\t\t\tg: 0,\n\t\t\t\t\t\t\tb: 0.15,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.RIGHT_EYEBROW.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tr: 0,\n\t\t\t\t\t\t\tg: 0,\n\t\t\t\t\t\t\tb: 0.35,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t\t// Eyes.\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.LEFT_EYE.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0, g: 0, b: 0.65 }\n\t\t\t\t\t);\n\t\t\t\t\tfillRegion(\n\t\t\t\t\t\tLANDMARK_INDICES.RIGHT_EYE.map((idx: number) => landmarks[idx]),\n\t\t\t\t\t\t{ r: 0, g: 0, b: 0.85 }\n\t\t\t\t\t);\n\t\t\t\t});\n\n\t\t\t\tshaderPad.updateTextures({ u_faceMask: faceMaskCanvas });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to generate mask texture:', error);\n\t\t\t}\n\t\t}\n\n\t\tfunction processFaceResults(result: any) {\n\t\t\tif (!result.faceLandmarks) return;\n\n\t\t\tconst faceCenters: [number, number][] = [];\n\t\t\tconst leftEyes: [number, number][] = [];\n\t\t\tconst rightEyes: [number, number][] = [];\n\t\t\tconst noseTips: [number, number][] = [];\n\t\t\tfor (const landmarks of result.faceLandmarks) {\n\t\t\t\tconst faceCenter = calculateFaceCenter(landmarks);\n\t\t\t\tconst leftEye = landmarks[LANDMARK_INDICES.LEFT_EYE_CENTER];\n\t\t\t\tconst rightEye = landmarks[LANDMARK_INDICES.RIGHT_EYE_CENTER];\n\t\t\t\tconst noseTip = landmarks[LANDMARK_INDICES.NOSE_TIP];\n\n\t\t\t\t// Invert Y-axis to match WebGL coordinate system.\n\t\t\t\tfaceCenters.push([faceCenter[0], 1.0 - faceCenter[1]]);\n\t\t\t\tleftEyes.push([leftEye.x, 1.0 - leftEye.y]);\n\t\t\t\trightEyes.push([rightEye.x, 1.0 - rightEye.y]);\n\t\t\t\tnoseTips.push([noseTip.x, 1.0 - noseTip.y]);\n\t\t\t}\n\n\t\t\tupdateMaskTexture(result.faceLandmarks).catch(error => {\n\t\t\t\tconsole.warn('Mask texture update error:', error);\n\t\t\t});\n\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nFaces: result.faceLandmarks.length };\n\t\t\tif (result.faceLandmarks.length) {\n\t\t\t\tif (uniforms.has('u_faceCenter')) updates.u_faceCenter = faceCenters;\n\t\t\t\tif (uniforms.has('u_leftEye')) updates.u_leftEye = leftEyes;\n\t\t\t\tif (uniforms.has('u_rightEye')) updates.u_rightEye = rightEyes;\n\t\t\t\tif (uniforms.has('u_noseTip')) updates.u_noseTip = noseTips;\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', faceMaskCanvas);\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tconst defaultFaceData: [number, number][] = Array.from({ length: maxFaces }, () => [0.5, 0.5]);\n\t\t\tshaderPad.initializeUniform('u_faceCenter', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_leftEye', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_rightEye', 'float', defaultFaceData, { arrayLength: maxFaces });\n\t\t\tshaderPad.initializeUniform('u_noseTip', 'float', defaultFaceData, { arrayLength: maxFaces });\n\n\t\t\tawait initializeFaceLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!faceLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = faceLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = faceLandmarker.detect(source);\n\t\t\t\t\t\tprocessFaceResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Face detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (faceLandmarker) {\n\t\t\t\tfaceLandmarker.close();\n\t\t\t\tfaceLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tfaceMaskCanvas.remove();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform vec2 u_faceCenter[${maxFaces}];\nuniform vec2 u_leftEye[${maxFaces}];\nuniform vec2 u_rightEye[${maxFaces}];\nuniform vec2 u_noseTip[${maxFaces}];\nuniform sampler2D u_faceMask;\nfloat getFace(vec2 pos) { return texture(u_faceMask, pos).g; }\nfloat getEye(vec2 pos) { return texture(u_faceMask, pos).b; }\nfloat getMouth(vec2 pos) { return texture(u_faceMask, pos).r; }`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["LANDMARK_INDICES","face","config","textureName","options","defaultModelPath","shaderPad","context","uniforms","injectGLSL","faceLandmarker","vision","lastVideoTime","textureSources","maxFaces","maskWidth","maskHeight","faceMaskCanvas","faceMaskCtx","initializeFaceLandmarker","FilesetResolver","FaceLandmarker","error","calculateFaceCenter","landmarks","minX","maxX","minY","maxY","landmark","centerX","centerY","fillRegion","color","origin","destination","updateMaskTexture","faces","idx","start","processFaceResults","result","faceCenters","leftEyes","rightEyes","noseTips","faceCenter","leftEye","rightEye","noseTip","updates","defaultFaceData","name","source","timestamp","face_default"]}
@@ -12,4 +12,4 @@ declare function hands(config: {
12
12
  options?: HandsPluginOptions;
13
13
  }): (shaderPad: ShaderPad, context: PluginContext) => void;
14
14
 
15
- export { type HandsPluginOptions, hands };
15
+ export { type HandsPluginOptions, hands as default };
@@ -12,4 +12,4 @@ declare function hands(config: {
12
12
  options?: HandsPluginOptions;
13
13
  }): (shaderPad: ShaderPad, context: PluginContext) => void;
14
14
 
15
- export { type HandsPluginOptions, hands };
15
+ export { type HandsPluginOptions, hands as default };
@@ -1,8 +1,8 @@
1
- "use strict";var _=Object.create;var u=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var w=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var v=(n,r)=>{for(var e in r)u(n,e,{get:r[e],enumerable:!0})},x=(n,r,e,c)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of N(r))!P.call(n,i)&&i!==e&&u(n,i,{get:()=>r[i],enumerable:!(c=b(r,i))||c.enumerable});return n};var z=(n,r,e)=>(e=n!=null?_(w(n)):{},x(r||!n||!n.__esModule?u(e,"default",{value:n,enumerable:!0}):e,n)),M=n=>x(u({},"__esModule",{value:!0}),n);var D={};v(D,{hands:()=>A});module.exports=M(D);var f=22,S=[0,0,5,9,13,17];function A(n){let{textureName:r,options:e}=n,c="https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task";return function(i,L){let{injectGLSL:y}=L,m=null,p=null,H=-1,k=new Map,l=e?.maxHands??2;async function C(){try{let{FilesetResolver:t,HandLandmarker:d}=await import("@mediapipe/tasks-vision");p=await t.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),m=await d.createFromOptions(p,{baseOptions:{modelAssetPath:e?.modelPath||c},runningMode:"VIDEO",numHands:e?.maxHands??2,minHandDetectionConfidence:e?.minHandDetectionConfidence??.5,minHandPresenceConfidence:e?.minHandPresenceConfidence??.5,minTrackingConfidence:e?.minTrackingConfidence??.5})}catch(t){throw console.error("Failed to initialize Hand Landmarker:",t),t}}function T(t){let d=S.map(a=>t[a]).filter(a=>a&&typeof a.x=="number"&&typeof a.y=="number");if(d.length===0)return[0,0];let o=0,s=0;for(let a of d)o+=a.x,s+=a.y;return[o/d.length,s/d.length]}function h(t){if(!t.landmarks)return;let d=t.landmarks.length,o={u_nHands:d};d&&(o.u_handLandmarks=t.landmarks.flatMap(s=>{let a=T(s);return a[1]=1-a[1],s.map(g=>[g.x,1-g.y]).concat([a])})),i.updateUniforms(o)}i.registerHook("init",async()=>{i.initializeUniform("u_maxHands","int",l),i.initializeUniform("u_nHands","int",0);let t=Array.from({length:l*f},()=>[.5,.5]);i.initializeUniform("u_handLandmarks","float",t,{arrayLength:l*f}),await C()}),i.registerHook("updateTextures",t=>{Object.entries(t).forEach(([d,o])=>{if(d===r&&(k.set(d,o),!!m))try{if(o instanceof HTMLVideoElement){if(o.currentTime!==H){H=o.currentTime;let s=performance.now(),a=m.detectForVideo(o,s);h(a)}}else if(o instanceof HTMLImageElement){let s=m.detect(o);h(s)}}catch(s){console.warn("Hand detection error:",s)}})}),i.registerHook("destroy",()=>{m&&(m.close(),m=null),p=null,k.clear()}),y(`
1
+ "use strict";var _=Object.create;var u=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var w=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var v=(n,r)=>{for(var e in r)u(n,e,{get:r[e],enumerable:!0})},x=(n,r,e,c)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of N(r))!P.call(n,i)&&i!==e&&u(n,i,{get:()=>r[i],enumerable:!(c=b(r,i))||c.enumerable});return n};var z=(n,r,e)=>(e=n!=null?_(w(n)):{},x(r||!n||!n.__esModule?u(e,"default",{value:n,enumerable:!0}):e,n)),M=n=>x(u({},"__esModule",{value:!0}),n);var O={};v(O,{default:()=>D});module.exports=M(O);var f=22,S=[0,0,5,9,13,17];function A(n){let{textureName:r,options:e}=n,c="https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task";return function(i,L){let{injectGLSL:y}=L,m=null,p=null,H=-1,k=new Map,l=e?.maxHands??2;async function C(){try{let{FilesetResolver:t,HandLandmarker:d}=await import("@mediapipe/tasks-vision");p=await t.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),m=await d.createFromOptions(p,{baseOptions:{modelAssetPath:e?.modelPath||c},runningMode:"VIDEO",numHands:e?.maxHands??2,minHandDetectionConfidence:e?.minHandDetectionConfidence??.5,minHandPresenceConfidence:e?.minHandPresenceConfidence??.5,minTrackingConfidence:e?.minTrackingConfidence??.5})}catch(t){throw console.error("Failed to initialize Hand Landmarker:",t),t}}function T(t){let d=S.map(a=>t[a]).filter(a=>a&&typeof a.x=="number"&&typeof a.y=="number");if(d.length===0)return[0,0];let o=0,s=0;for(let a of d)o+=a.x,s+=a.y;return[o/d.length,s/d.length]}function h(t){if(!t.landmarks)return;let d=t.landmarks.length,o={u_nHands:d};d&&(o.u_handLandmarks=t.landmarks.flatMap(s=>{let a=T(s);return a[1]=1-a[1],s.map(g=>[g.x,1-g.y]).concat([a])})),i.updateUniforms(o)}i.registerHook("init",async()=>{i.initializeUniform("u_maxHands","int",l),i.initializeUniform("u_nHands","int",0);let t=Array.from({length:l*f},()=>[.5,.5]);i.initializeUniform("u_handLandmarks","float",t,{arrayLength:l*f}),await C()}),i.registerHook("updateTextures",t=>{Object.entries(t).forEach(([d,o])=>{if(d===r&&(k.set(d,o),!!m))try{if(o instanceof HTMLVideoElement){if(o.currentTime!==H){H=o.currentTime;let s=performance.now(),a=m.detectForVideo(o,s);h(a)}}else if(o instanceof HTMLImageElement){let s=m.detect(o);h(s)}}catch(s){console.warn("Hand detection error:",s)}})}),i.registerHook("destroy",()=>{m&&(m.close(),m=null),p=null,k.clear()}),y(`
2
2
  uniform int u_maxHands;
3
3
  uniform int u_nHands;
4
4
  uniform vec2 u_handLandmarks[${l*f}];
5
5
  vec2 handLandmark(int handIndex, int landmarkIndex) {
6
6
  return u_handLandmarks[handIndex * ${f} + landmarkIndex];
7
- }`)}}0&&(module.exports={hands});
7
+ }`)}}var D=A;
8
8
  //# sourceMappingURL=hands.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/hands.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { HandLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface HandsPluginOptions {\n\tmodelPath?: string;\n\tmaxHands?: number;\n\tminHandDetectionConfidence?: number;\n\tminHandPresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n}\n\nconst LANDMARK_COUNT = 21 + 1; // See https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models, plus the hand center.\nconst HAND_CENTER_LANDMARKS = [0, 0, 5, 9, 13, 17]; // Wrist + MCP joints of all fingers, weighted toward the wrist.\n\nexport function hands(config: { textureName: string; options?: HandsPluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL } = context;\n\n\t\tlet handLandmarker: HandLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxHands = options?.maxHands ?? 2;\n\n\t\tasync function initializeHandLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, HandLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\thandLandmarker = await HandLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumHands: options?.maxHands ?? 2,\n\t\t\t\t\tminHandDetectionConfidence: options?.minHandDetectionConfidence ?? 0.5,\n\t\t\t\t\tminHandPresenceConfidence: options?.minHandPresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to initialize Hand Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateHandCenter(landmarks: NormalizedLandmark[]): [number, number] {\n\t\t\tconst visibleLandmarks = HAND_CENTER_LANDMARKS.map(index => landmarks[index]).filter(\n\t\t\t\tlandmark => landmark && typeof landmark.x === 'number' && typeof landmark.y === 'number'\n\t\t\t);\n\t\t\tif (visibleLandmarks.length === 0) return [0, 0];\n\n\t\t\tlet sumX = 0,\n\t\t\t\tsumY = 0;\n\t\t\tfor (const landmark of visibleLandmarks) {\n\t\t\t\tsumX += landmark.x;\n\t\t\t\tsumY += landmark.y;\n\t\t\t}\n\t\t\treturn [sumX / visibleLandmarks.length, sumY / visibleLandmarks.length];\n\t\t}\n\n\t\tfunction processHandResults(result: any) {\n\t\t\tif (!result.landmarks) return;\n\n\t\t\tconst nHands = result.landmarks.length;\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nHands: nHands };\n\t\t\tif (nHands) {\n\t\t\t\tupdates.u_handLandmarks = result.landmarks.flatMap((landmarks: NormalizedLandmark[]) => {\n\t\t\t\t\tconst handCenter = calculateHandCenter(landmarks);\n\t\t\t\t\thandCenter[1] = 1.0 - handCenter[1];\n\t\t\t\t\treturn landmarks.map(landmark => [landmark.x, 1.0 - landmark.y]).concat([handCenter]);\n\t\t\t\t});\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeUniform('u_maxHands', 'int', maxHands);\n\t\t\tshaderPad.initializeUniform('u_nHands', 'int', 0);\n\t\t\tconst defaultHandLandmarks: [number, number][] = Array.from({ length: maxHands * LANDMARK_COUNT }, () => [\n\t\t\t\t0.5, 0.5,\n\t\t\t]);\n\t\t\tshaderPad.initializeUniform('u_handLandmarks', 'float', defaultHandLandmarks, {\n\t\t\t\tarrayLength: maxHands * LANDMARK_COUNT,\n\t\t\t});\n\t\t\tawait initializeHandLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!handLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = handLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = handLandmarker.detect(source);\n\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Hand detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (handLandmarker) {\n\t\t\t\thandLandmarker.close();\n\t\t\t\thandLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxHands;\nuniform int u_nHands;\nuniform vec2 u_handLandmarks[${maxHands * LANDMARK_COUNT}];\nvec2 handLandmark(int handIndex, int landmarkIndex) {\n\treturn u_handLandmarks[handIndex * ${LANDMARK_COUNT} + landmarkIndex];\n}`);\n\t};\n}\n"],"mappings":"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","names":["hands_exports","__export","hands","__toCommonJS","LANDMARK_COUNT","HAND_CENTER_LANDMARKS","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","handLandmarker","vision","lastVideoTime","textureSources","maxHands","initializeHandLandmarker","FilesetResolver","HandLandmarker","error","calculateHandCenter","landmarks","visibleLandmarks","index","landmark","sumX","sumY","processHandResults","result","nHands","updates","handCenter","defaultHandLandmarks","name","source","timestamp"]}
1
+ {"version":3,"sources":["../../src/plugins/hands.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { HandLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface HandsPluginOptions {\n\tmodelPath?: string;\n\tmaxHands?: number;\n\tminHandDetectionConfidence?: number;\n\tminHandPresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n}\n\nconst LANDMARK_COUNT = 21 + 1; // See https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models, plus the hand center.\nconst HAND_CENTER_LANDMARKS = [0, 0, 5, 9, 13, 17]; // Wrist + MCP joints of all fingers, weighted toward the wrist.\n\nfunction hands(config: { textureName: string; options?: HandsPluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL } = context;\n\n\t\tlet handLandmarker: HandLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxHands = options?.maxHands ?? 2;\n\n\t\tasync function initializeHandLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, HandLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\thandLandmarker = await HandLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumHands: options?.maxHands ?? 2,\n\t\t\t\t\tminHandDetectionConfidence: options?.minHandDetectionConfidence ?? 0.5,\n\t\t\t\t\tminHandPresenceConfidence: options?.minHandPresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to initialize Hand Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateHandCenter(landmarks: NormalizedLandmark[]): [number, number] {\n\t\t\tconst visibleLandmarks = HAND_CENTER_LANDMARKS.map(index => landmarks[index]).filter(\n\t\t\t\tlandmark => landmark && typeof landmark.x === 'number' && typeof landmark.y === 'number'\n\t\t\t);\n\t\t\tif (visibleLandmarks.length === 0) return [0, 0];\n\n\t\t\tlet sumX = 0,\n\t\t\t\tsumY = 0;\n\t\t\tfor (const landmark of visibleLandmarks) {\n\t\t\t\tsumX += landmark.x;\n\t\t\t\tsumY += landmark.y;\n\t\t\t}\n\t\t\treturn [sumX / visibleLandmarks.length, sumY / visibleLandmarks.length];\n\t\t}\n\n\t\tfunction processHandResults(result: any) {\n\t\t\tif (!result.landmarks) return;\n\n\t\t\tconst nHands = result.landmarks.length;\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nHands: nHands };\n\t\t\tif (nHands) {\n\t\t\t\tupdates.u_handLandmarks = result.landmarks.flatMap((landmarks: NormalizedLandmark[]) => {\n\t\t\t\t\tconst handCenter = calculateHandCenter(landmarks);\n\t\t\t\t\thandCenter[1] = 1.0 - handCenter[1];\n\t\t\t\t\treturn landmarks.map(landmark => [landmark.x, 1.0 - landmark.y]).concat([handCenter]);\n\t\t\t\t});\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeUniform('u_maxHands', 'int', maxHands);\n\t\t\tshaderPad.initializeUniform('u_nHands', 'int', 0);\n\t\t\tconst defaultHandLandmarks: [number, number][] = Array.from({ length: maxHands * LANDMARK_COUNT }, () => [\n\t\t\t\t0.5, 0.5,\n\t\t\t]);\n\t\t\tshaderPad.initializeUniform('u_handLandmarks', 'float', defaultHandLandmarks, {\n\t\t\t\tarrayLength: maxHands * LANDMARK_COUNT,\n\t\t\t});\n\t\t\tawait initializeHandLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!handLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = handLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = handLandmarker.detect(source);\n\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Hand detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (handLandmarker) {\n\t\t\t\thandLandmarker.close();\n\t\t\t\thandLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxHands;\nuniform int u_nHands;\nuniform vec2 u_handLandmarks[${maxHands * LANDMARK_COUNT}];\nvec2 handLandmark(int handIndex, int landmarkIndex) {\n\treturn u_handLandmarks[handIndex * ${LANDMARK_COUNT} + landmarkIndex];\n}`);\n\t};\n}\n\nexport default hands;\n"],"mappings":"0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GAWA,IAAMI,EAAiB,GACjBC,EAAwB,CAAC,EAAG,EAAG,EAAG,EAAG,GAAI,EAAE,EAEjD,SAASC,EAAMC,EAA+D,CAC7E,GAAM,CAAE,YAAAC,EAAa,QAAAC,CAAQ,EAAIF,EAC3BG,EACL,iHAED,OAAO,SAAUC,EAAsBC,EAAwB,CAC9D,GAAM,CAAE,WAAAC,CAAW,EAAID,EAEnBE,EAAwC,KACxCC,EAAc,KACdC,EAAgB,GACdC,EAAiB,IAAI,IACrBC,EAAWT,GAAS,UAAY,EAEtC,eAAeU,GAA2B,CACzC,GAAI,CACH,GAAM,CAAE,gBAAAC,EAAiB,eAAAC,CAAe,EAAI,KAAM,QAAO,yBAAyB,EAClFN,EAAS,MAAMK,EAAgB,eAC9B,kEACD,EAEAN,EAAiB,MAAMO,EAAe,kBAAkBN,EAAQ,CAC/D,YAAa,CACZ,eAAgBN,GAAS,WAAaC,CACvC,EACA,YAAa,QACb,SAAUD,GAAS,UAAY,EAC/B,2BAA4BA,GAAS,4BAA8B,GACnE,0BAA2BA,GAAS,2BAA6B,GACjE,sBAAuBA,GAAS,uBAAyB,EAC1D,CAAC,CACF,OAASa,EAAO,CACf,cAAQ,MAAM,wCAAyCA,CAAK,EACtDA,CACP,CACD,CAEA,SAASC,EAAoBC,EAAmD,CAC/E,IAAMC,EAAmBpB,EAAsB,IAAIqB,GAASF,EAAUE,CAAK,CAAC,EAAE,OAC7EC,GAAYA,GAAY,OAAOA,EAAS,GAAM,UAAY,OAAOA,EAAS,GAAM,QACjF,EACA,GAAIF,EAAiB,SAAW,EAAG,MAAO,CAAC,EAAG,CAAC,EAE/C,IAAIG,EAAO,EACVC,EAAO,EACR,QAAWF,KAAYF,EACtBG,GAAQD,EAAS,EACjBE,GAAQF,EAAS,EAElB,MAAO,CAACC,EAAOH,EAAiB,OAAQI,EAAOJ,EAAiB,MAAM,CACvE,CAEA,SAASK,EAAmBC,EAAa,CACxC,GAAI,CAACA,EAAO,UAAW,OAEvB,IAAMC,EAASD,EAAO,UAAU,OAC1BE,EAA0D,CAAE,SAAUD,CAAO,EAC/EA,IACHC,EAAQ,gBAAkBF,EAAO,UAAU,QAASP,GAAoC,CACvF,IAAMU,EAAaX,EAAoBC,CAAS,EAChD,OAAAU,EAAW,CAAC,EAAI,EAAMA,EAAW,CAAC,EAC3BV,EAAU,IAAIG,GAAY,CAACA,EAAS,EAAG,EAAMA,EAAS,CAAC,CAAC,EAAE,OAAO,CAACO,CAAU,CAAC,CACrF,CAAC,GAEFvB,EAAU,eAAesB,CAAO,CACjC,CAEAtB,EAAU,aAAa,OAAQ,SAAY,CAC1CA,EAAU,kBAAkB,aAAc,MAAOO,CAAQ,EACzDP,EAAU,kBAAkB,WAAY,MAAO,CAAC,EAChD,IAAMwB,EAA2C,MAAM,KAAK,CAAE,OAAQjB,EAAWd,CAAe,EAAG,IAAM,CACxG,GAAK,EACN,CAAC,EACDO,EAAU,kBAAkB,kBAAmB,QAASwB,EAAsB,CAC7E,YAAajB,EAAWd,CACzB,CAAC,EACD,MAAMe,EAAyB,CAChC,CAAC,EAEDR,EAAU,aAAa,iBAAmBsB,GAA2C,CACpF,OAAO,QAAQA,CAAO,EAAE,QAAQ,CAAC,CAACG,EAAMC,CAAM,IAAM,CACnD,GAAID,IAAS5B,IACbS,EAAe,IAAImB,EAAMC,CAAM,EAC3B,EAACvB,GACL,GAAI,CACH,GAAIuB,aAAkB,kBACrB,GAAIA,EAAO,cAAgBrB,EAAe,CACzCA,EAAgBqB,EAAO,YACvB,IAAMC,EAAY,YAAY,IAAI,EAC5BP,EAASjB,EAAe,eAAeuB,EAAQC,CAAS,EAC9DR,EAAmBC,CAAM,CAC1B,UACUM,aAAkB,iBAAkB,CAC9C,IAAMN,EAASjB,EAAe,OAAOuB,CAAM,EAC3CP,EAAmBC,CAAM,CAC1B,CACD,OAAST,EAAO,CACf,QAAQ,KAAK,wBAAyBA,CAAK,CAC5C,CACD,CAAC,CACF,CAAC,EAEDX,EAAU,aAAa,UAAW,IAAM,CACnCG,IACHA,EAAe,MAAM,EACrBA,EAAiB,MAElBC,EAAS,KACTE,EAAe,MAAM,CACtB,CAAC,EAEDJ,EAAW;AAAA;AAAA;AAAA,+BAGkBK,EAAWd,CAAc;AAAA;AAAA,sCAElBA,CAAc;AAAA,EAClD,CACD,CACD,CAEA,IAAOF,EAAQI","names":["hands_exports","__export","hands_default","__toCommonJS","LANDMARK_COUNT","HAND_CENTER_LANDMARKS","hands","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","handLandmarker","vision","lastVideoTime","textureSources","maxHands","initializeHandLandmarker","FilesetResolver","HandLandmarker","error","calculateHandCenter","landmarks","visibleLandmarks","index","landmark","sumX","sumY","processHandResults","result","nHands","updates","handCenter","defaultHandLandmarks","name","source","timestamp"]}
@@ -4,5 +4,5 @@ uniform int u_nHands;
4
4
  uniform vec2 u_handLandmarks[${s*m}];
5
5
  vec2 handLandmark(int handIndex, int landmarkIndex) {
6
6
  return u_handLandmarks[handIndex * ${m} + landmarkIndex];
7
- }`)}}export{T as hands};
7
+ }`)}}var _=T;export{_ as default};
8
8
  //# sourceMappingURL=hands.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/hands.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { HandLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface HandsPluginOptions {\n\tmodelPath?: string;\n\tmaxHands?: number;\n\tminHandDetectionConfidence?: number;\n\tminHandPresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n}\n\nconst LANDMARK_COUNT = 21 + 1; // See https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models, plus the hand center.\nconst HAND_CENTER_LANDMARKS = [0, 0, 5, 9, 13, 17]; // Wrist + MCP joints of all fingers, weighted toward the wrist.\n\nexport function hands(config: { textureName: string; options?: HandsPluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL } = context;\n\n\t\tlet handLandmarker: HandLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxHands = options?.maxHands ?? 2;\n\n\t\tasync function initializeHandLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, HandLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\thandLandmarker = await HandLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumHands: options?.maxHands ?? 2,\n\t\t\t\t\tminHandDetectionConfidence: options?.minHandDetectionConfidence ?? 0.5,\n\t\t\t\t\tminHandPresenceConfidence: options?.minHandPresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to initialize Hand Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateHandCenter(landmarks: NormalizedLandmark[]): [number, number] {\n\t\t\tconst visibleLandmarks = HAND_CENTER_LANDMARKS.map(index => landmarks[index]).filter(\n\t\t\t\tlandmark => landmark && typeof landmark.x === 'number' && typeof landmark.y === 'number'\n\t\t\t);\n\t\t\tif (visibleLandmarks.length === 0) return [0, 0];\n\n\t\t\tlet sumX = 0,\n\t\t\t\tsumY = 0;\n\t\t\tfor (const landmark of visibleLandmarks) {\n\t\t\t\tsumX += landmark.x;\n\t\t\t\tsumY += landmark.y;\n\t\t\t}\n\t\t\treturn [sumX / visibleLandmarks.length, sumY / visibleLandmarks.length];\n\t\t}\n\n\t\tfunction processHandResults(result: any) {\n\t\t\tif (!result.landmarks) return;\n\n\t\t\tconst nHands = result.landmarks.length;\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nHands: nHands };\n\t\t\tif (nHands) {\n\t\t\t\tupdates.u_handLandmarks = result.landmarks.flatMap((landmarks: NormalizedLandmark[]) => {\n\t\t\t\t\tconst handCenter = calculateHandCenter(landmarks);\n\t\t\t\t\thandCenter[1] = 1.0 - handCenter[1];\n\t\t\t\t\treturn landmarks.map(landmark => [landmark.x, 1.0 - landmark.y]).concat([handCenter]);\n\t\t\t\t});\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeUniform('u_maxHands', 'int', maxHands);\n\t\t\tshaderPad.initializeUniform('u_nHands', 'int', 0);\n\t\t\tconst defaultHandLandmarks: [number, number][] = Array.from({ length: maxHands * LANDMARK_COUNT }, () => [\n\t\t\t\t0.5, 0.5,\n\t\t\t]);\n\t\t\tshaderPad.initializeUniform('u_handLandmarks', 'float', defaultHandLandmarks, {\n\t\t\t\tarrayLength: maxHands * LANDMARK_COUNT,\n\t\t\t});\n\t\t\tawait initializeHandLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!handLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = handLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = handLandmarker.detect(source);\n\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Hand detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (handLandmarker) {\n\t\t\t\thandLandmarker.close();\n\t\t\t\thandLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxHands;\nuniform int u_nHands;\nuniform vec2 u_handLandmarks[${maxHands * LANDMARK_COUNT}];\nvec2 handLandmark(int handIndex, int landmarkIndex) {\n\treturn u_handLandmarks[handIndex * ${LANDMARK_COUNT} + landmarkIndex];\n}`);\n\t};\n}\n"],"mappings":"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","names":["LANDMARK_COUNT","HAND_CENTER_LANDMARKS","hands","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","handLandmarker","vision","lastVideoTime","textureSources","maxHands","initializeHandLandmarker","FilesetResolver","HandLandmarker","error","calculateHandCenter","landmarks","visibleLandmarks","index","landmark","sumX","sumY","processHandResults","result","nHands","updates","handCenter","defaultHandLandmarks","name","source","timestamp"]}
1
+ {"version":3,"sources":["../../src/plugins/hands.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { HandLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface HandsPluginOptions {\n\tmodelPath?: string;\n\tmaxHands?: number;\n\tminHandDetectionConfidence?: number;\n\tminHandPresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n}\n\nconst LANDMARK_COUNT = 21 + 1; // See https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models, plus the hand center.\nconst HAND_CENTER_LANDMARKS = [0, 0, 5, 9, 13, 17]; // Wrist + MCP joints of all fingers, weighted toward the wrist.\n\nfunction hands(config: { textureName: string; options?: HandsPluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL } = context;\n\n\t\tlet handLandmarker: HandLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxHands = options?.maxHands ?? 2;\n\n\t\tasync function initializeHandLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, HandLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\thandLandmarker = await HandLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumHands: options?.maxHands ?? 2,\n\t\t\t\t\tminHandDetectionConfidence: options?.minHandDetectionConfidence ?? 0.5,\n\t\t\t\t\tminHandPresenceConfidence: options?.minHandPresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to initialize Hand Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateHandCenter(landmarks: NormalizedLandmark[]): [number, number] {\n\t\t\tconst visibleLandmarks = HAND_CENTER_LANDMARKS.map(index => landmarks[index]).filter(\n\t\t\t\tlandmark => landmark && typeof landmark.x === 'number' && typeof landmark.y === 'number'\n\t\t\t);\n\t\t\tif (visibleLandmarks.length === 0) return [0, 0];\n\n\t\t\tlet sumX = 0,\n\t\t\t\tsumY = 0;\n\t\t\tfor (const landmark of visibleLandmarks) {\n\t\t\t\tsumX += landmark.x;\n\t\t\t\tsumY += landmark.y;\n\t\t\t}\n\t\t\treturn [sumX / visibleLandmarks.length, sumY / visibleLandmarks.length];\n\t\t}\n\n\t\tfunction processHandResults(result: any) {\n\t\t\tif (!result.landmarks) return;\n\n\t\t\tconst nHands = result.landmarks.length;\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nHands: nHands };\n\t\t\tif (nHands) {\n\t\t\t\tupdates.u_handLandmarks = result.landmarks.flatMap((landmarks: NormalizedLandmark[]) => {\n\t\t\t\t\tconst handCenter = calculateHandCenter(landmarks);\n\t\t\t\t\thandCenter[1] = 1.0 - handCenter[1];\n\t\t\t\t\treturn landmarks.map(landmark => [landmark.x, 1.0 - landmark.y]).concat([handCenter]);\n\t\t\t\t});\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeUniform('u_maxHands', 'int', maxHands);\n\t\t\tshaderPad.initializeUniform('u_nHands', 'int', 0);\n\t\t\tconst defaultHandLandmarks: [number, number][] = Array.from({ length: maxHands * LANDMARK_COUNT }, () => [\n\t\t\t\t0.5, 0.5,\n\t\t\t]);\n\t\t\tshaderPad.initializeUniform('u_handLandmarks', 'float', defaultHandLandmarks, {\n\t\t\t\tarrayLength: maxHands * LANDMARK_COUNT,\n\t\t\t});\n\t\t\tawait initializeHandLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!handLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = handLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = handLandmarker.detect(source);\n\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Hand detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (handLandmarker) {\n\t\t\t\thandLandmarker.close();\n\t\t\t\thandLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxHands;\nuniform int u_nHands;\nuniform vec2 u_handLandmarks[${maxHands * LANDMARK_COUNT}];\nvec2 handLandmark(int handIndex, int landmarkIndex) {\n\treturn u_handLandmarks[handIndex * ${LANDMARK_COUNT} + landmarkIndex];\n}`);\n\t};\n}\n\nexport default hands;\n"],"mappings":"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","names":["LANDMARK_COUNT","HAND_CENTER_LANDMARKS","hands","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","handLandmarker","vision","lastVideoTime","textureSources","maxHands","initializeHandLandmarker","FilesetResolver","HandLandmarker","error","calculateHandCenter","landmarks","visibleLandmarks","index","landmark","sumX","sumY","processHandResults","result","nHands","updates","handCenter","defaultHandLandmarks","name","source","timestamp","hands_default"]}
@@ -2,4 +2,4 @@ import ShaderPad, { PluginContext } from '../index.mjs';
2
2
 
3
3
  declare function helpers(): (_shader: ShaderPad, context: PluginContext) => void;
4
4
 
5
- export { helpers };
5
+ export { helpers as default };
@@ -2,4 +2,4 @@ import ShaderPad, { PluginContext } from '../index.js';
2
2
 
3
3
  declare function helpers(): (_shader: ShaderPad, context: PluginContext) => void;
4
4
 
5
- export { helpers };
5
+ export { helpers as default };
@@ -1,7 +1,7 @@
1
- "use strict";var o=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var p=(e,t)=>{for(var i in t)o(e,i,{get:t[i],enumerable:!0})},m=(e,t,i,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of a(t))!s.call(e,r)&&r!==i&&o(e,r,{get:()=>t[r],enumerable:!(n=h(t,r))||n.enumerable});return e};var l=e=>m(o({},"__esModule",{value:!0}),e);var d={};p(d,{helpers:()=>x});module.exports=l(d);var f=`float historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {
1
+ "use strict";var o=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var p=(e,t)=>{for(var i in t)o(e,i,{get:t[i],enumerable:!0})},m=(e,t,i,f)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of h(t))!s.call(e,r)&&r!==i&&o(e,r,{get:()=>t[r],enumerable:!(f=a(t,r))||f.enumerable});return e};var l=e=>m(o({},"__esModule",{value:!0}),e);var g={};p(g,{default:()=>x});module.exports=l(g);var n=`float historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {
2
2
  int historyDepth = textureSize(tex, 0).z;
3
3
  int z = (historyDepth + frameOffset - framesAgo) % historyDepth;
4
4
  return float(z);
5
5
  }
6
- `;function x(){return function(e,t){t.injectGLSL(f)}}0&&(module.exports={helpers});
6
+ `;function d(){return function(e,t){t.injectGLSL(n)}}var x=d;
7
7
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/helpers.ts","../../src/plugins/helpers.glsl"],"sourcesContent":["import ShaderPad, { PluginContext } from '../index';\nimport helpersGLSL from './helpers.glsl';\n\nexport function helpers() {\n\treturn function (_shader: ShaderPad, context: PluginContext) {\n\t\tcontext.injectGLSL(helpersGLSL);\n\t};\n}\n","float historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {\n\tint historyDepth = textureSize(tex, 0).z;\n\tint z = (historyDepth + frameOffset - framesAgo) % historyDepth;\n\treturn float(z);\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GCAA,IAAAI,EAAA;AAAA;AAAA;AAAA;AAAA;EDGO,SAASC,GAAU,CACzB,OAAO,SAAUC,EAAoBC,EAAwB,CAC5DA,EAAQ,WAAWC,CAAW,CAC/B,CACD","names":["helpers_exports","__export","helpers","__toCommonJS","helpers_default","helpers","_shader","context","helpers_default"]}
1
+ {"version":3,"sources":["../../src/plugins/helpers.ts","../../src/plugins/helpers.glsl"],"sourcesContent":["import ShaderPad, { PluginContext } from '../index';\nimport helpersGLSL from './helpers.glsl';\n\nfunction helpers() {\n\treturn function (_shader: ShaderPad, context: PluginContext) {\n\t\tcontext.injectGLSL(helpersGLSL);\n\t};\n}\n\nexport default helpers;\n","float historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {\n\tint historyDepth = textureSize(tex, 0).z;\n\tint z = (historyDepth + frameOffset - framesAgo) % historyDepth;\n\treturn float(z);\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GCAA,IAAAI,EAAA;AAAA;AAAA;AAAA;AAAA;EDGA,SAASC,GAAU,CAClB,OAAO,SAAUC,EAAoBC,EAAwB,CAC5DA,EAAQ,WAAWC,CAAW,CAC/B,CACD,CAEA,IAAOA,EAAQH","names":["helpers_exports","__export","helpers_default","__toCommonJS","helpers_default","helpers","_shader","context","helpers_default"]}
@@ -3,5 +3,5 @@ var t=`float historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo)
3
3
  int z = (historyDepth + frameOffset - framesAgo) % historyDepth;
4
4
  return float(z);
5
5
  }
6
- `;function f(){return function(i,e){e.injectGLSL(t)}}export{f as helpers};
6
+ `;function i(){return function(o,e){e.injectGLSL(t)}}var a=i;export{a as default};
7
7
  //# sourceMappingURL=helpers.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/helpers.glsl","../../src/plugins/helpers.ts"],"sourcesContent":["float historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {\n\tint historyDepth = textureSize(tex, 0).z;\n\tint z = (historyDepth + frameOffset - framesAgo) % historyDepth;\n\treturn float(z);\n}\n","import ShaderPad, { PluginContext } from '../index';\nimport helpersGLSL from './helpers.glsl';\n\nexport function helpers() {\n\treturn function (_shader: ShaderPad, context: PluginContext) {\n\t\tcontext.injectGLSL(helpersGLSL);\n\t};\n}\n"],"mappings":"AAAA,IAAAA,EAAA;AAAA;AAAA;AAAA;AAAA;ECGO,SAASC,GAAU,CACzB,OAAO,SAAUC,EAAoBC,EAAwB,CAC5DA,EAAQ,WAAWC,CAAW,CAC/B,CACD","names":["helpers_default","helpers","_shader","context","helpers_default"]}
1
+ {"version":3,"sources":["../../src/plugins/helpers.glsl","../../src/plugins/helpers.ts"],"sourcesContent":["float historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {\n\tint historyDepth = textureSize(tex, 0).z;\n\tint z = (historyDepth + frameOffset - framesAgo) % historyDepth;\n\treturn float(z);\n}\n","import ShaderPad, { PluginContext } from '../index';\nimport helpersGLSL from './helpers.glsl';\n\nfunction helpers() {\n\treturn function (_shader: ShaderPad, context: PluginContext) {\n\t\tcontext.injectGLSL(helpersGLSL);\n\t};\n}\n\nexport default helpers;\n"],"mappings":"AAAA,IAAAA,EAAA;AAAA;AAAA;AAAA;AAAA;ECGA,SAASC,GAAU,CAClB,OAAO,SAAUC,EAAoBC,EAAwB,CAC5DA,EAAQ,WAAWC,CAAW,CAC/B,CACD,CAEA,IAAOA,EAAQH","names":["helpers_default","helpers","_shader","context","helpers_default"]}
@@ -13,4 +13,4 @@ declare function pose(config: {
13
13
  options?: PosePluginOptions;
14
14
  }): (shaderPad: ShaderPad, context: PluginContext) => void;
15
15
 
16
- export { type PosePluginOptions, pose };
16
+ export { type PosePluginOptions, pose as default };
@@ -13,4 +13,4 @@ declare function pose(config: {
13
13
  options?: PosePluginOptions;
14
14
  }): (shaderPad: ShaderPad, context: PluginContext) => void;
15
15
 
16
- export { type PosePluginOptions, pose };
16
+ export { type PosePluginOptions, pose as default };
@@ -1,4 +1,4 @@
1
- "use strict";var I=Object.create;var h=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var N=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var H=(o,r)=>{for(var i in r)h(o,i,{get:r[i],enumerable:!0})},_=(o,r,i,d)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of D(r))!z.call(o,a)&&a!==i&&h(o,a,{get:()=>r[a],enumerable:!(d=O(r,a))||d.enumerable});return o};var U=(o,r,i)=>(i=o!=null?I(N(o)):{},_(r||!o||!o.__esModule?h(i,"default",{value:o,enumerable:!0}):i,o)),F=o=>_(h({},"__esModule",{value:!0}),o);var V={};H(V,{pose:()=>R});module.exports=F(V);var P=33;function R(o){let{textureName:r,options:i}=o,d="https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task";return function(a,y){let{injectGLSL:T}=y,u=null,k=null,C=-1,w=new Map,p=i?.maxPoses??1,M=512,b=512,t=document.createElement("canvas");t.width=M,t.height=b;let c=t.getContext("2d");c.globalCompositeOperation="lighten";let x=[];async function v(){try{let{FilesetResolver:s,PoseLandmarker:m}=await import("@mediapipe/tasks-vision");k=await s.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),x.push(...m.POSE_CONNECTIONS),u=await m.createFromOptions(k,{baseOptions:{modelAssetPath:i?.modelPath||d},runningMode:"VIDEO",numPoses:i?.maxPoses??1,minPoseDetectionConfidence:i?.minPoseDetectionConfidence??.5,minPosePresenceConfidence:i?.minPosePresenceConfidence??.5,minTrackingConfidence:i?.minTrackingConfidence??.5,outputSegmentationMasks:i?.outputSegmentationMasks??!1})}catch(s){throw console.error("[Pose Plugin] Failed to initialize Pose Landmarker:",s),s}}async function S(s,m){if(!u){console.warn("[Pose Plugin] Cannot update mask: poseLandmarker missing");return}try{if(c.clearRect(0,0,t.width,t.height),m&&m.length>0&&m.forEach(e=>{if(e.width!==t.width||e.height!==t.height){let n=document.createElement("canvas");n.width=e.width,n.height=e.height,n.getContext("2d").putImageData(e,0,0),c.drawImage(n,0,0,t.width,t.height)}else c.putImageData(e,0,0)}),x.length){let e=Math.max(2,t.width/256);c.lineWidth=e,c.lineCap="round",c.strokeStyle="#00f",s.forEach(n=>{x.forEach(({start:l,end:E})=>{let f=n[l],g=n[E];!f||!g||(f.visibility??1)<.3||(g.visibility??1)<.3||(c.beginPath(),c.moveTo(f.x*t.width,f.y*t.height),c.lineTo(g.x*t.width,g.y*t.height),c.stroke())})})}a.updateTextures({u_poseMask:t})}catch(e){console.error("[Pose Plugin] Failed to generate mask texture:",e)}}function L(s){if(!s.landmarks)return;S(s.landmarks,s.segmentationMasks).catch(n=>{console.warn("[Pose Plugin] Mask texture update error:",n)});let m=s.landmarks.length,e={u_nPoses:m};m&&(e.u_poseLandmarks=s.landmarks.flatMap(n=>n.map(l=>[l.x,1-l.y]))),a.updateUniforms(e)}a.registerHook("init",async()=>{a.initializeTexture("u_poseMask",t),a.initializeUniform("u_maxPoses","int",p),a.initializeUniform("u_nPoses","int",0);let s=Array.from({length:p*P},()=>[.5,.5]);a.initializeUniform("u_poseLandmarks","float",s,{arrayLength:p*P}),await v()}),a.registerHook("updateTextures",s=>{Object.entries(s).forEach(([m,e])=>{if(m===r&&(w.set(m,e),!!u))try{if(e instanceof HTMLVideoElement){if(e.currentTime!==C){C=e.currentTime;let n=performance.now(),l=u.detectForVideo(e,n);L(l)}}else if(e instanceof HTMLImageElement){let n=u.detect(e);L(n)}}catch(n){console.error("[Pose Plugin] Pose detection error:",n)}})}),a.registerHook("destroy",()=>{u&&(u.close(),u=null),k=null,w.clear(),t.remove()}),T(`
1
+ "use strict";var I=Object.create;var h=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var N=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var H=(o,r)=>{for(var i in r)h(o,i,{get:r[i],enumerable:!0})},_=(o,r,i,d)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of D(r))!z.call(o,a)&&a!==i&&h(o,a,{get:()=>r[a],enumerable:!(d=O(r,a))||d.enumerable});return o};var U=(o,r,i)=>(i=o!=null?I(N(o)):{},_(r||!o||!o.__esModule?h(i,"default",{value:o,enumerable:!0}):i,o)),F=o=>_(h({},"__esModule",{value:!0}),o);var A={};H(A,{default:()=>V});module.exports=F(A);var P=33;function R(o){let{textureName:r,options:i}=o,d="https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task";return function(a,y){let{injectGLSL:T}=y,u=null,k=null,C=-1,w=new Map,p=i?.maxPoses??1,M=512,b=512,t=document.createElement("canvas");t.width=M,t.height=b;let c=t.getContext("2d");c.globalCompositeOperation="lighten";let x=[];async function v(){try{let{FilesetResolver:s,PoseLandmarker:m}=await import("@mediapipe/tasks-vision");k=await s.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),x.push(...m.POSE_CONNECTIONS),u=await m.createFromOptions(k,{baseOptions:{modelAssetPath:i?.modelPath||d},runningMode:"VIDEO",numPoses:i?.maxPoses??1,minPoseDetectionConfidence:i?.minPoseDetectionConfidence??.5,minPosePresenceConfidence:i?.minPosePresenceConfidence??.5,minTrackingConfidence:i?.minTrackingConfidence??.5,outputSegmentationMasks:i?.outputSegmentationMasks??!1})}catch(s){throw console.error("[Pose Plugin] Failed to initialize Pose Landmarker:",s),s}}async function S(s,m){if(!u){console.warn("[Pose Plugin] Cannot update mask: poseLandmarker missing");return}try{if(c.clearRect(0,0,t.width,t.height),m&&m.length>0&&m.forEach(e=>{if(e.width!==t.width||e.height!==t.height){let n=document.createElement("canvas");n.width=e.width,n.height=e.height,n.getContext("2d").putImageData(e,0,0),c.drawImage(n,0,0,t.width,t.height)}else c.putImageData(e,0,0)}),x.length){let e=Math.max(2,t.width/256);c.lineWidth=e,c.lineCap="round",c.strokeStyle="#00f",s.forEach(n=>{x.forEach(({start:l,end:E})=>{let f=n[l],g=n[E];!f||!g||(f.visibility??1)<.3||(g.visibility??1)<.3||(c.beginPath(),c.moveTo(f.x*t.width,f.y*t.height),c.lineTo(g.x*t.width,g.y*t.height),c.stroke())})})}a.updateTextures({u_poseMask:t})}catch(e){console.error("[Pose Plugin] Failed to generate mask texture:",e)}}function L(s){if(!s.landmarks)return;S(s.landmarks,s.segmentationMasks).catch(n=>{console.warn("[Pose Plugin] Mask texture update error:",n)});let m=s.landmarks.length,e={u_nPoses:m};m&&(e.u_poseLandmarks=s.landmarks.flatMap(n=>n.map(l=>[l.x,1-l.y]))),a.updateUniforms(e)}a.registerHook("init",async()=>{a.initializeTexture("u_poseMask",t),a.initializeUniform("u_maxPoses","int",p),a.initializeUniform("u_nPoses","int",0);let s=Array.from({length:p*P},()=>[.5,.5]);a.initializeUniform("u_poseLandmarks","float",s,{arrayLength:p*P}),await v()}),a.registerHook("updateTextures",s=>{Object.entries(s).forEach(([m,e])=>{if(m===r&&(w.set(m,e),!!u))try{if(e instanceof HTMLVideoElement){if(e.currentTime!==C){C=e.currentTime;let n=performance.now(),l=u.detectForVideo(e,n);L(l)}}else if(e instanceof HTMLImageElement){let n=u.detect(e);L(n)}}catch(n){console.error("[Pose Plugin] Pose detection error:",n)}})}),a.registerHook("destroy",()=>{u&&(u.close(),u=null),k=null,w.clear(),t.remove()}),T(`
2
2
  uniform int u_maxPoses;
3
3
  uniform int u_nPoses;
4
4
  uniform vec2 u_poseLandmarks[${p*P}];
@@ -7,5 +7,5 @@ vec2 poseLandmark(int poseIndex, int landmarkIndex) {
7
7
  return u_poseLandmarks[poseIndex * ${P} + landmarkIndex];
8
8
  }
9
9
  float getBody(vec2 pos) { return texture(u_poseMask, pos).g; }
10
- float getSkeleton(vec2 pos) { return texture(u_poseMask, pos).b; }`)}}0&&(module.exports={pose});
10
+ float getSkeleton(vec2 pos) { return texture(u_poseMask, pos).b; }`)}}var V=R;
11
11
  //# sourceMappingURL=pose.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/pose.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { PoseLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface PosePluginOptions {\n\tmodelPath?: string;\n\tmaxPoses?: number;\n\tminPoseDetectionConfidence?: number;\n\tminPosePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputSegmentationMasks?: boolean;\n}\n\nconst LANDMARK_COUNT = 33; // See https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker#pose_landmarker_model.\n\nexport function pose(config: { textureName: string; options?: PosePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL } = context;\n\n\t\tlet poseLandmarker: PoseLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxPoses = options?.maxPoses ?? 1;\n\t\tconst maskWidth = 512;\n\t\tconst maskHeight = 512;\n\t\tconst poseMaskCanvas = document.createElement('canvas');\n\t\tposeMaskCanvas.width = maskWidth;\n\t\tposeMaskCanvas.height = maskHeight;\n\t\tconst poseMaskCtx = poseMaskCanvas.getContext('2d')!;\n\t\tposeMaskCtx.globalCompositeOperation = 'lighten'; // Keep the highest value of each channel.\n\t\tconst poseConnections: { start: number; end: number }[] = [];\n\n\t\tasync function initializePoseLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, PoseLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\t\t\t\tposeConnections.push(...PoseLandmarker.POSE_CONNECTIONS);\n\t\t\t\tposeLandmarker = await PoseLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumPoses: options?.maxPoses ?? 1,\n\t\t\t\t\tminPoseDetectionConfidence: options?.minPoseDetectionConfidence ?? 0.5,\n\t\t\t\t\tminPosePresenceConfidence: options?.minPosePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputSegmentationMasks: options?.outputSegmentationMasks ?? false,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Pose Plugin] Failed to initialize Pose Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tasync function updateMaskTexture(poses: NormalizedLandmark[][], segmentationMasks?: ImageData[]) {\n\t\t\tif (!poseLandmarker) {\n\t\t\t\tconsole.warn('[Pose Plugin] Cannot update mask: poseLandmarker missing');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tposeMaskCtx.clearRect(0, 0, poseMaskCanvas.width, poseMaskCanvas.height);\n\n\t\t\t\t// Draw the segmentation masks.\n\t\t\t\tif (segmentationMasks && segmentationMasks.length > 0) {\n\t\t\t\t\t// Combine all segmentation masks (for multiple poses).\n\t\t\t\t\tsegmentationMasks.forEach(mask => {\n\t\t\t\t\t\t// Resize mask to canvas size if needed.\n\t\t\t\t\t\tif (mask.width !== poseMaskCanvas.width || mask.height !== poseMaskCanvas.height) {\n\t\t\t\t\t\t\tconst tempCanvas = document.createElement('canvas');\n\t\t\t\t\t\t\ttempCanvas.width = mask.width;\n\t\t\t\t\t\t\ttempCanvas.height = mask.height;\n\t\t\t\t\t\t\tconst tempCtx = tempCanvas.getContext('2d')!;\n\t\t\t\t\t\t\ttempCtx.putImageData(mask, 0, 0);\n\t\t\t\t\t\t\tposeMaskCtx.drawImage(tempCanvas, 0, 0, poseMaskCanvas.width, poseMaskCanvas.height);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tposeMaskCtx.putImageData(mask, 0, 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Draw the skeleton.\n\t\t\t\tif (poseConnections.length) {\n\t\t\t\t\tconst lineWidth = Math.max(2, poseMaskCanvas.width / 256);\n\t\t\t\t\tposeMaskCtx.lineWidth = lineWidth;\n\t\t\t\t\tposeMaskCtx.lineCap = 'round';\n\t\t\t\t\tposeMaskCtx.strokeStyle = '#00f';\n\n\t\t\t\t\tposes.forEach(landmarks => {\n\t\t\t\t\t\tposeConnections.forEach(({ start, end }) => {\n\t\t\t\t\t\t\tconst a = landmarks[start];\n\t\t\t\t\t\t\tconst b = landmarks[end];\n\t\t\t\t\t\t\tif (!a || !b) return;\n\t\t\t\t\t\t\tif ((a.visibility ?? 1) < 0.3 || (b.visibility ?? 1) < 0.3) return;\n\t\t\t\t\t\t\tposeMaskCtx.beginPath();\n\t\t\t\t\t\t\tposeMaskCtx.moveTo(a.x * poseMaskCanvas.width, a.y * poseMaskCanvas.height);\n\t\t\t\t\t\t\tposeMaskCtx.lineTo(b.x * poseMaskCanvas.width, b.y * poseMaskCanvas.height);\n\t\t\t\t\t\t\tposeMaskCtx.stroke();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tshaderPad.updateTextures({ u_poseMask: poseMaskCanvas });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Pose Plugin] Failed to generate mask texture:', error);\n\t\t\t}\n\t\t}\n\n\t\tfunction processPoseResults(result: any) {\n\t\t\tif (!result.landmarks) return;\n\n\t\t\tupdateMaskTexture(result.landmarks, result.segmentationMasks).catch(error => {\n\t\t\t\tconsole.warn('[Pose Plugin] Mask texture update error:', error);\n\t\t\t});\n\n\t\t\tconst nPoses = result.landmarks.length;\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nPoses: nPoses };\n\t\t\tif (nPoses) {\n\t\t\t\tupdates.u_poseLandmarks = result.landmarks.flatMap((landmarks: NormalizedLandmark[]) =>\n\t\t\t\t\tlandmarks.map(landmark => [landmark.x, 1.0 - landmark.y])\n\t\t\t\t);\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_poseMask', poseMaskCanvas);\n\t\t\tshaderPad.initializeUniform('u_maxPoses', 'int', maxPoses);\n\t\t\tshaderPad.initializeUniform('u_nPoses', 'int', 0);\n\t\t\tconst defaultPoseData: [number, number][] = Array.from({ length: maxPoses * LANDMARK_COUNT }, () => [\n\t\t\t\t0.5, 0.5,\n\t\t\t]);\n\t\t\tshaderPad.initializeUniform('u_poseLandmarks', 'float', defaultPoseData, {\n\t\t\t\tarrayLength: maxPoses * LANDMARK_COUNT,\n\t\t\t});\n\n\t\t\tawait initializePoseLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!poseLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = poseLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessPoseResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = poseLandmarker.detect(source);\n\t\t\t\t\t\tprocessPoseResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error('[Pose Plugin] Pose detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (poseLandmarker) {\n\t\t\t\tposeLandmarker.close();\n\t\t\t\tposeLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tposeMaskCanvas.remove();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxPoses;\nuniform int u_nPoses;\nuniform vec2 u_poseLandmarks[${maxPoses * LANDMARK_COUNT}];\nuniform sampler2D u_poseMask;\nvec2 poseLandmark(int poseIndex, int landmarkIndex) {\n\treturn u_poseLandmarks[poseIndex * ${LANDMARK_COUNT} + landmarkIndex];\n}\nfloat getBody(vec2 pos) { return texture(u_poseMask, pos).g; }\nfloat getSkeleton(vec2 pos) { return texture(u_poseMask, pos).b; }`);\n\t};\n}\n"],"mappings":"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","names":["pose_exports","__export","pose","__toCommonJS","LANDMARK_COUNT","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","poseLandmarker","vision","lastVideoTime","textureSources","maxPoses","maskWidth","maskHeight","poseMaskCanvas","poseMaskCtx","poseConnections","initializePoseLandmarker","FilesetResolver","PoseLandmarker","error","updateMaskTexture","poses","segmentationMasks","mask","tempCanvas","lineWidth","landmarks","start","end","a","b","processPoseResults","result","nPoses","updates","landmark","defaultPoseData","name","source","timestamp"]}
1
+ {"version":3,"sources":["../../src/plugins/pose.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { PoseLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface PosePluginOptions {\n\tmodelPath?: string;\n\tmaxPoses?: number;\n\tminPoseDetectionConfidence?: number;\n\tminPosePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputSegmentationMasks?: boolean;\n}\n\nconst LANDMARK_COUNT = 33; // See https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker#pose_landmarker_model.\n\nfunction pose(config: { textureName: string; options?: PosePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL } = context;\n\n\t\tlet poseLandmarker: PoseLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxPoses = options?.maxPoses ?? 1;\n\t\tconst maskWidth = 512;\n\t\tconst maskHeight = 512;\n\t\tconst poseMaskCanvas = document.createElement('canvas');\n\t\tposeMaskCanvas.width = maskWidth;\n\t\tposeMaskCanvas.height = maskHeight;\n\t\tconst poseMaskCtx = poseMaskCanvas.getContext('2d')!;\n\t\tposeMaskCtx.globalCompositeOperation = 'lighten'; // Keep the highest value of each channel.\n\t\tconst poseConnections: { start: number; end: number }[] = [];\n\n\t\tasync function initializePoseLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, PoseLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\t\t\t\tposeConnections.push(...PoseLandmarker.POSE_CONNECTIONS);\n\t\t\t\tposeLandmarker = await PoseLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumPoses: options?.maxPoses ?? 1,\n\t\t\t\t\tminPoseDetectionConfidence: options?.minPoseDetectionConfidence ?? 0.5,\n\t\t\t\t\tminPosePresenceConfidence: options?.minPosePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputSegmentationMasks: options?.outputSegmentationMasks ?? false,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Pose Plugin] Failed to initialize Pose Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tasync function updateMaskTexture(poses: NormalizedLandmark[][], segmentationMasks?: ImageData[]) {\n\t\t\tif (!poseLandmarker) {\n\t\t\t\tconsole.warn('[Pose Plugin] Cannot update mask: poseLandmarker missing');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tposeMaskCtx.clearRect(0, 0, poseMaskCanvas.width, poseMaskCanvas.height);\n\n\t\t\t\t// Draw the segmentation masks.\n\t\t\t\tif (segmentationMasks && segmentationMasks.length > 0) {\n\t\t\t\t\t// Combine all segmentation masks (for multiple poses).\n\t\t\t\t\tsegmentationMasks.forEach(mask => {\n\t\t\t\t\t\t// Resize mask to canvas size if needed.\n\t\t\t\t\t\tif (mask.width !== poseMaskCanvas.width || mask.height !== poseMaskCanvas.height) {\n\t\t\t\t\t\t\tconst tempCanvas = document.createElement('canvas');\n\t\t\t\t\t\t\ttempCanvas.width = mask.width;\n\t\t\t\t\t\t\ttempCanvas.height = mask.height;\n\t\t\t\t\t\t\tconst tempCtx = tempCanvas.getContext('2d')!;\n\t\t\t\t\t\t\ttempCtx.putImageData(mask, 0, 0);\n\t\t\t\t\t\t\tposeMaskCtx.drawImage(tempCanvas, 0, 0, poseMaskCanvas.width, poseMaskCanvas.height);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tposeMaskCtx.putImageData(mask, 0, 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Draw the skeleton.\n\t\t\t\tif (poseConnections.length) {\n\t\t\t\t\tconst lineWidth = Math.max(2, poseMaskCanvas.width / 256);\n\t\t\t\t\tposeMaskCtx.lineWidth = lineWidth;\n\t\t\t\t\tposeMaskCtx.lineCap = 'round';\n\t\t\t\t\tposeMaskCtx.strokeStyle = '#00f';\n\n\t\t\t\t\tposes.forEach(landmarks => {\n\t\t\t\t\t\tposeConnections.forEach(({ start, end }) => {\n\t\t\t\t\t\t\tconst a = landmarks[start];\n\t\t\t\t\t\t\tconst b = landmarks[end];\n\t\t\t\t\t\t\tif (!a || !b) return;\n\t\t\t\t\t\t\tif ((a.visibility ?? 1) < 0.3 || (b.visibility ?? 1) < 0.3) return;\n\t\t\t\t\t\t\tposeMaskCtx.beginPath();\n\t\t\t\t\t\t\tposeMaskCtx.moveTo(a.x * poseMaskCanvas.width, a.y * poseMaskCanvas.height);\n\t\t\t\t\t\t\tposeMaskCtx.lineTo(b.x * poseMaskCanvas.width, b.y * poseMaskCanvas.height);\n\t\t\t\t\t\t\tposeMaskCtx.stroke();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tshaderPad.updateTextures({ u_poseMask: poseMaskCanvas });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Pose Plugin] Failed to generate mask texture:', error);\n\t\t\t}\n\t\t}\n\n\t\tfunction processPoseResults(result: any) {\n\t\t\tif (!result.landmarks) return;\n\n\t\t\tupdateMaskTexture(result.landmarks, result.segmentationMasks).catch(error => {\n\t\t\t\tconsole.warn('[Pose Plugin] Mask texture update error:', error);\n\t\t\t});\n\n\t\t\tconst nPoses = result.landmarks.length;\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nPoses: nPoses };\n\t\t\tif (nPoses) {\n\t\t\t\tupdates.u_poseLandmarks = result.landmarks.flatMap((landmarks: NormalizedLandmark[]) =>\n\t\t\t\t\tlandmarks.map(landmark => [landmark.x, 1.0 - landmark.y])\n\t\t\t\t);\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_poseMask', poseMaskCanvas);\n\t\t\tshaderPad.initializeUniform('u_maxPoses', 'int', maxPoses);\n\t\t\tshaderPad.initializeUniform('u_nPoses', 'int', 0);\n\t\t\tconst defaultPoseData: [number, number][] = Array.from({ length: maxPoses * LANDMARK_COUNT }, () => [\n\t\t\t\t0.5, 0.5,\n\t\t\t]);\n\t\t\tshaderPad.initializeUniform('u_poseLandmarks', 'float', defaultPoseData, {\n\t\t\t\tarrayLength: maxPoses * LANDMARK_COUNT,\n\t\t\t});\n\n\t\t\tawait initializePoseLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!poseLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = poseLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessPoseResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = poseLandmarker.detect(source);\n\t\t\t\t\t\tprocessPoseResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error('[Pose Plugin] Pose detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (poseLandmarker) {\n\t\t\t\tposeLandmarker.close();\n\t\t\t\tposeLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tposeMaskCanvas.remove();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxPoses;\nuniform int u_nPoses;\nuniform vec2 u_poseLandmarks[${maxPoses * LANDMARK_COUNT}];\nuniform sampler2D u_poseMask;\nvec2 poseLandmark(int poseIndex, int landmarkIndex) {\n\treturn u_poseLandmarks[poseIndex * ${LANDMARK_COUNT} + landmarkIndex];\n}\nfloat getBody(vec2 pos) { return texture(u_poseMask, pos).g; }\nfloat getSkeleton(vec2 pos) { return texture(u_poseMask, pos).b; }`);\n\t};\n}\n\nexport default pose;\n"],"mappings":"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","names":["pose_exports","__export","pose_default","__toCommonJS","LANDMARK_COUNT","pose","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","poseLandmarker","vision","lastVideoTime","textureSources","maxPoses","maskWidth","maskHeight","poseMaskCanvas","poseMaskCtx","poseConnections","initializePoseLandmarker","FilesetResolver","PoseLandmarker","error","updateMaskTexture","poses","segmentationMasks","mask","tempCanvas","lineWidth","landmarks","start","end","a","b","processPoseResults","result","nPoses","updates","landmark","defaultPoseData","name","source","timestamp"]}
@@ -7,5 +7,5 @@ vec2 poseLandmark(int poseIndex, int landmarkIndex) {
7
7
  return u_poseLandmarks[poseIndex * ${p} + landmarkIndex];
8
8
  }
9
9
  float getBody(vec2 pos) { return texture(u_poseMask, pos).g; }
10
- float getSkeleton(vec2 pos) { return texture(u_poseMask, pos).b; }`)}}export{S as pose};
10
+ float getSkeleton(vec2 pos) { return texture(u_poseMask, pos).b; }`)}}var E=S;export{E as default};
11
11
  //# sourceMappingURL=pose.mjs.map