shaderpad 1.0.0-beta.41 → 1.0.0-beta.42

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.
Files changed (52) hide show
  1. package/README.md +116 -56
  2. package/dist/chunk-5CBGNOA3.mjs +10 -0
  3. package/dist/chunk-5CBGNOA3.mjs.map +1 -0
  4. package/dist/chunk-JRSBIGBN.mjs +7 -0
  5. package/dist/chunk-JRSBIGBN.mjs.map +1 -0
  6. package/dist/index.d.mts +2 -1
  7. package/dist/index.d.ts +2 -1
  8. package/dist/index.js +4 -4
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +1 -1
  11. package/dist/plugins/face.d.mts +1 -0
  12. package/dist/plugins/face.d.ts +1 -0
  13. package/dist/plugins/face.js +53 -76
  14. package/dist/plugins/face.js.map +1 -1
  15. package/dist/plugins/face.mjs +51 -79
  16. package/dist/plugins/face.mjs.map +1 -1
  17. package/dist/plugins/hands.d.mts +1 -0
  18. package/dist/plugins/hands.d.ts +1 -0
  19. package/dist/plugins/hands.js +20 -16
  20. package/dist/plugins/hands.js.map +1 -1
  21. package/dist/plugins/hands.mjs +15 -16
  22. package/dist/plugins/hands.mjs.map +1 -1
  23. package/dist/plugins/helpers.js +1 -1
  24. package/dist/plugins/helpers.js.map +1 -1
  25. package/dist/plugins/helpers.mjs +1 -1
  26. package/dist/plugins/helpers.mjs.map +1 -1
  27. package/dist/plugins/mediapipe-common.d.mts +18 -0
  28. package/dist/plugins/mediapipe-common.d.ts +18 -0
  29. package/dist/plugins/mediapipe-common.js +7 -0
  30. package/dist/plugins/mediapipe-common.js.map +1 -0
  31. package/dist/plugins/mediapipe-common.mjs +2 -0
  32. package/dist/plugins/mediapipe-common.mjs.map +1 -0
  33. package/dist/plugins/pose.d.mts +1 -0
  34. package/dist/plugins/pose.d.ts +1 -0
  35. package/dist/plugins/pose.js +54 -49
  36. package/dist/plugins/pose.js.map +1 -1
  37. package/dist/plugins/pose.mjs +46 -46
  38. package/dist/plugins/pose.mjs.map +1 -1
  39. package/dist/plugins/save.d.mts +1 -1
  40. package/dist/plugins/save.d.ts +1 -1
  41. package/dist/plugins/save.js +1 -1
  42. package/dist/plugins/save.js.map +1 -1
  43. package/dist/plugins/save.mjs.map +1 -1
  44. package/dist/plugins/segmenter.d.mts +1 -0
  45. package/dist/plugins/segmenter.d.ts +1 -0
  46. package/dist/plugins/segmenter.js +16 -11
  47. package/dist/plugins/segmenter.js.map +1 -1
  48. package/dist/plugins/segmenter.mjs +10 -10
  49. package/dist/plugins/segmenter.mjs.map +1 -1
  50. package/package.json +1 -1
  51. package/dist/chunk-A3XQBYSC.mjs +0 -10
  52. package/dist/chunk-A3XQBYSC.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, FaceLandmarkerResult, 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 STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst LANDMARKS_TEXTURE_WIDTH = 512;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst OUTER_MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_MOUTH: OUTER_MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\nconst REGION_NAMES = [\n\t'BACKGROUND',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'OUTER_MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst nFaceRegions = REGION_NAMES.length - 1;\nconst RED_CHANNEL_VALUES = Object.fromEntries(REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\n\nfunction fanTriangulate(indices: readonly number[]): number[] {\n\tconst tris: number[] = [];\n\tfor (let i = 1; i < indices.length - 1; ++i) {\n\t\ttris.push(indices[0], indices[i], indices[i + 1]);\n\t}\n\treturn tris;\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 { injectGLSL, gl, emitHook } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tlet runningMode: 'IMAGE' | 'VIDEO' = 'VIDEO';\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tlet landmarksTextureHeight = 0;\n\t\tlet landmarksDataArray: Float32Array | null = null;\n\n\t\tconst mediaPipeCanvas = new OffscreenCanvas(1, 1);\n\t\tconst maskCanvas = new OffscreenCanvas(1, 1);\n\n\t\t// WebGL resources for triangle rendering (no antialiasing).\n\t\tlet maskGl: WebGL2RenderingContext | null = null;\n\t\tlet maskProgram: WebGLProgram | null = null;\n\t\tlet positionBuffer: WebGLBuffer | null = null;\n\t\tlet colorLocation: WebGLUniformLocation | null = null;\n\n\t\tconst regionTriangles: Record<string, number[]> = {\n\t\t\tLEFT_EYEBROW: fanTriangulate(LEFT_EYEBROW_INDICES),\n\t\t\tRIGHT_EYEBROW: fanTriangulate(RIGHT_EYEBROW_INDICES),\n\t\t\tLEFT_EYE: fanTriangulate(LEFT_EYE_INDICES),\n\t\t\tRIGHT_EYE: fanTriangulate(RIGHT_EYE_INDICES),\n\t\t\tOUTER_MOUTH: fanTriangulate(OUTER_MOUTH_INDICES),\n\t\t\tINNER_MOUTH: fanTriangulate(INNER_MOUTH_INDICES),\n\t\t\t// Populated after FaceLandmarker loads.\n\t\t\tTESSELATION: [],\n\t\t\tOVAL: [],\n\t\t};\n\n\t\tfunction initMaskRenderer() {\n\t\t\tmaskGl = maskCanvas.getContext('webgl2', { antialias: false, preserveDrawingBuffer: true });\n\t\t\tif (!maskGl) throw new Error('Failed to get WebGL2 context for mask');\n\n\t\t\tconst vertexShader = maskGl.createShader(maskGl.VERTEX_SHADER)!;\n\t\t\tmaskGl.shaderSource(\n\t\t\t\tvertexShader,\n\t\t\t\t`#version 300 es\nin vec2 a_pos;\nvoid main() {\n\tgl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);\n}`\n\t\t\t);\n\t\t\tmaskGl.compileShader(vertexShader);\n\n\t\t\tconst fragmentShader = maskGl.createShader(maskGl.FRAGMENT_SHADER)!;\n\t\t\tmaskGl.shaderSource(\n\t\t\t\tfragmentShader,\n\t\t\t\t`#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`\n\t\t\t);\n\t\t\tmaskGl.compileShader(fragmentShader);\n\n\t\t\tmaskProgram = maskGl.createProgram()!;\n\t\t\tmaskGl.attachShader(maskProgram, vertexShader);\n\t\t\tmaskGl.attachShader(maskProgram, fragmentShader);\n\t\t\tmaskGl.linkProgram(maskProgram);\n\t\t\tmaskGl.deleteShader(vertexShader);\n\t\t\tmaskGl.deleteShader(fragmentShader);\n\n\t\t\tpositionBuffer = maskGl.createBuffer();\n\t\t\tmaskGl.bindBuffer(maskGl.ARRAY_BUFFER, positionBuffer);\n\t\t\tconst positionLocation = maskGl.getAttribLocation(maskProgram, 'a_pos');\n\t\t\tmaskGl.enableVertexAttribArray(positionLocation);\n\t\t\tmaskGl.vertexAttribPointer(positionLocation, 2, maskGl.FLOAT, false, 0, 0);\n\n\t\t\tcolorLocation = maskGl.getUniformLocation(maskProgram, 'u_color');\n\t\t\tmaskGl.useProgram(maskProgram);\n\n\t\t\t// Enable blending to handle overlapping faces (set once, never disabled).\n\t\t\tmaskGl.enable(maskGl.BLEND);\n\t\t\tmaskGl.blendEquation(maskGl.MAX);\n\t\t}\n\n\t\tfunction drawTriangles(triangleIndices: number[], faceIdx: number, r: number, g: number, b: number) {\n\t\t\tif (!maskGl || !landmarksDataArray || triangleIndices.length === 0) return;\n\n\t\t\tconst vertices = new Float32Array(triangleIndices.length * 2);\n\t\t\tfor (let i = 0; i < triangleIndices.length; ++i) {\n\t\t\t\tconst landmarkIdx = (faceIdx * LANDMARK_COUNT + triangleIndices[i]) * 4;\n\t\t\t\tvertices[i * 2] = landmarksDataArray[landmarkIdx];\n\t\t\t\tvertices[i * 2 + 1] = landmarksDataArray[landmarkIdx + 1];\n\t\t\t}\n\n\t\t\tmaskGl.bufferData(maskGl.ARRAY_BUFFER, vertices, maskGl.DYNAMIC_DRAW);\n\t\t\tmaskGl.uniform4f(colorLocation, r, g, b, 1.0);\n\t\t\tmaskGl.drawArrays(maskGl.TRIANGLES, 0, triangleIndices.length);\n\t\t}\n\n\t\tasync function initializeMediaPipe() {\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\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\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediaPipeCanvas,\n\t\t\t\t\trunningMode,\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\n\t\t\t\tconst tesselationConnections = FaceLandmarker.FACE_LANDMARKS_TESSELATION;\n\t\t\t\tregionTriangles.TESSELATION = [];\n\t\t\t\tfor (let i = 0; i < tesselationConnections.length - 2; i += 3) {\n\t\t\t\t\tregionTriangles.TESSELATION.push(\n\t\t\t\t\t\ttesselationConnections[i].start,\n\t\t\t\t\t\ttesselationConnections[i + 1].start,\n\t\t\t\t\t\ttesselationConnections[i + 2].start\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst ovalIndices = FaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\t\t\tregionTriangles.OVAL = fanTriangulate(ovalIndices);\n\n\t\t\t\tinitMaskRenderer();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to initialize MediaPipe:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\tconst mediaPipeInitPromise = initializeMediaPipe();\n\n\t\tfunction calculateBoundingBoxCenter(\n\t\t\tdata: Float32Array,\n\t\t\tfaceIdx: number,\n\t\t\tindices: readonly number[] | number[]\n\t\t): [number, number, 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\t\t\t\tavgZ = 0,\n\t\t\t\tavgVisibility = 0;\n\n\t\t\tfor (const idx of indices) {\n\t\t\t\tconst i = (faceIdx * LANDMARK_COUNT + idx) * 4;\n\t\t\t\tconst x = data[i],\n\t\t\t\t\ty = data[i + 1];\n\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\tavgZ += data[i + 2];\n\t\t\t\tavgVisibility += data[i + 3];\n\t\t\t}\n\t\t\treturn [(minX + maxX) / 2, (minY + maxY) / 2, avgZ / indices.length, avgVisibility / indices.length];\n\t\t}\n\n\t\tfunction updateLandmarksTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst nFaces = faces.length;\n\t\t\tconst totalLandmarks = nFaces * LANDMARK_COUNT;\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst landmarks = faces[faceIdx];\n\t\t\t\tfor (let landmarkIdx = 0; landmarkIdx < STANDARD_LANDMARK_COUNT; ++landmarkIdx) {\n\t\t\t\t\tconst landmark = landmarks[landmarkIdx];\n\t\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + landmarkIdx) * 4;\n\t\t\t\t\tlandmarksDataArray[dataIdx] = landmark.x;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 1] = 1 - landmark.y;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 2] = landmark.z ?? 0;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t\t\t}\n\n\t\t\t\tconst faceCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, ALL_STANDARD_INDICES);\n\t\t\t\tlandmarksDataArray.set(faceCenter, (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4);\n\n\t\t\t\tconst mouthCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, INNER_MOUTH_INDICES);\n\t\t\t\tlandmarksDataArray.set(mouthCenter, (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4);\n\t\t\t}\n\n\t\t\tconst rowsToUpdate = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures({\n\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\tisPartial: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tfunction updateMaskTexture(nFaces: number) {\n\t\t\tif (!maskGl || !landmarksDataArray) return;\n\n\t\t\t// Resize and clear.\n\t\t\tmaskCanvas.width = mediaPipeCanvas.width;\n\t\t\tmaskCanvas.height = mediaPipeCanvas.height;\n\t\t\tmaskGl.viewport(0, 0, maskCanvas.width, maskCanvas.height);\n\t\t\tmaskGl.clearColor(0, 0, 0, 0);\n\t\t\tmaskGl.clear(maskGl.COLOR_BUFFER_BIT);\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst b = (faceIdx + 1) / maxFaces;\n\n\t\t\t\t// G channel: face mesh (0.5) and oval (1.0)\n\t\t\t\tdrawTriangles(regionTriangles.TESSELATION, faceIdx, 0, 0.5, b);\n\t\t\t\tdrawTriangles(regionTriangles.OVAL, faceIdx, 0, 1.0, b);\n\n\t\t\t\t// R channel: feature regions\n\t\t\t\tdrawTriangles(regionTriangles.LEFT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.LEFT_EYEBROW, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.RIGHT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYEBROW, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.OUTER_MOUTH, faceIdx, RED_CHANNEL_VALUES.OUTER_MOUTH, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.INNER_MOUTH, faceIdx, RED_CHANNEL_VALUES.INNER_MOUTH, 0, b);\n\t\t\t}\n\n\t\t\tshaderPad.updateTextures({ u_faceMask: maskCanvas });\n\t\t}\n\n\t\tfunction processFaces(result: FaceLandmarkerResult) {\n\t\t\tif (!result.faceLandmarks || !landmarksDataArray) return;\n\n\t\t\tconst nFaces = result.faceLandmarks.length;\n\t\t\tupdateLandmarksTexture(result.faceLandmarks);\n\t\t\tupdateMaskTexture(nFaces);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\t\t\temitHook('face:result', result);\n\t\t}\n\n\t\t// `detectFaces` may be called multiple times before MediaPipe is\n\t\t// initialized. This ensures we only process the last call.\n\t\tlet nDetectionCalls = 0;\n\t\tasync function detectFaces(source: TextureSource) {\n\t\t\tconst callOrder = ++nDetectionCalls;\n\t\t\tawait mediaPipeInitPromise;\n\t\t\tif (callOrder !== nDetectionCalls || !faceLandmarker) return;\n\n\t\t\tconst previousSource = textureSources.get(textureName);\n\t\t\tif (previousSource !== source) lastVideoTime = -1;\n\t\t\ttextureSources.set(textureName, source);\n\n\t\t\ttry {\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (runningMode !== requiredMode) {\n\t\t\t\t\trunningMode = requiredMode;\n\t\t\t\t\tawait faceLandmarker.setOptions({ runningMode });\n\t\t\t\t}\n\n\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\tprocessFaces(faceLandmarker.detectForVideo(source, performance.now()));\n\t\t\t\t\t}\n\t\t\t\t} else if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement) {\n\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\tprocessFaces(faceLandmarker.detect(source));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Detection error:', error);\n\t\t\t}\n\t\t}\n\n\t\tshaderPad.on('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', maskCanvas, {\n\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t});\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\n\t\t\tconst totalLandmarks = maxFaces * LANDMARK_COUNT;\n\t\t\tlandmarksTextureHeight = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tlandmarksDataArray = new Float32Array(LANDMARKS_TEXTURE_WIDTH * landmarksTextureHeight * 4);\n\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{ data: landmarksDataArray, width: LANDMARKS_TEXTURE_WIDTH, height: landmarksTextureHeight },\n\t\t\t\t{ internalFormat: gl.RGBA32F, type: gl.FLOAT, minFilter: gl.NEAREST, magFilter: gl.NEAREST }\n\t\t\t);\n\t\t\tawait mediaPipeInitPromise;\n\t\t\temitHook('face:ready');\n\t\t});\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName) detectFaces(source);\n\t\t});\n\n\t\tshaderPad.on('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tconst source = updates[textureName];\n\t\t\tif (source) detectFaces(source);\n\t\t});\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tfaceLandmarker?.close();\n\t\t\tfaceLandmarker = null;\n\t\t\tif (maskGl && maskProgram) {\n\t\t\t\tmaskGl.deleteProgram(maskProgram);\n\t\t\t\tmaskGl.deleteBuffer(positionBuffer);\n\t\t\t}\n\t\t\tmaskGl = null;\n\t\t\tmaskProgram = null;\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tlandmarksDataArray = null;\n\t\t});\n\n\t\tconst checkAt = (\n\t\t\tregionMin: keyof typeof RED_CHANNEL_VALUES,\n\t\t\tregionMax: keyof typeof RED_CHANNEL_VALUES = regionMin\n\t\t) =>\n\t\t\t`vec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn (mask.r > ${(RED_CHANNEL_VALUES[regionMin] - HALF_GAP).toFixed(4)} && mask.r < ${(\n\t\t\t\tRED_CHANNEL_VALUES[regionMax] + HALF_GAP\n\t\t\t).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform sampler2D u_faceLandmarksTex;\nuniform sampler2D u_faceMask;\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\nvec4 faceLandmark(int faceIndex, int landmarkIndex) {\n\tint i = faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);\n}\n\nvec2 leftEyebrowAt(vec2 pos) {\n\t${checkAt('LEFT_EYEBROW')}\n}\n\nvec2 rightEyebrowAt(vec2 pos) {\n\t${checkAt('RIGHT_EYEBROW')}\n}\n\nvec2 leftEyeAt(vec2 pos) {\n\t${checkAt('LEFT_EYE')}\n}\n\nvec2 rightEyeAt(vec2 pos) {\n\t${checkAt('RIGHT_EYE')}\n}\n\nvec2 lipsAt(vec2 pos) {\n\t${checkAt('OUTER_MOUTH')}\n}\n\nvec2 outerMouthAt(vec2 pos) {\n\t${checkAt('OUTER_MOUTH', 'INNER_MOUTH')}\n}\n\nvec2 innerMouthAt(vec2 pos) {\n\t${checkAt('INNER_MOUTH')}\n}\n\nvec2 faceOvalAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.75 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\n// Includes face mesh and oval.\nvec2 faceAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.25 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\nvec2 eyeAt(vec2 pos) {\n\tvec2 left = leftEyeAt(pos);\n\treturn left.x > 0.0 ? left : rightEyeAt(pos);\n}\n\nvec2 eyebrowAt(vec2 pos) {\n\tvec2 left = leftEyebrowAt(pos);\n\treturn left.x > 0.0 ? left : rightEyebrowAt(pos);\n}\n\nfloat inEyebrow(vec2 pos) { return eyebrowAt(pos).x; }\nfloat inEye(vec2 pos) { return eyeAt(pos).x; }\nfloat inOuterMouth(vec2 pos) { return outerMouthAt(pos).x; }\nfloat inInnerMouth(vec2 pos) { return innerMouthAt(pos).x; }\nfloat inLips(vec2 pos) { return lipsAt(pos).x; }\nfloat inFace(vec2 pos) { return faceAt(pos).x; }`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["face_exports","__export","face_default","__toCommonJS","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LANDMARKS_TEXTURE_WIDTH","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","OUTER_MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","REGION_NAMES","nFaceRegions","RED_CHANNEL_VALUES","name","HALF_GAP","fanTriangulate","indices","tris","face","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","gl","emitHook","faceLandmarker","vision","lastVideoTime","runningMode","textureSources","maxFaces","landmarksTextureHeight","landmarksDataArray","mediaPipeCanvas","maskCanvas","maskGl","maskProgram","positionBuffer","colorLocation","regionTriangles","initMaskRenderer","vertexShader","fragmentShader","positionLocation","drawTriangles","triangleIndices","faceIdx","r","g","b","vertices","landmarkIdx","initializeMediaPipe","FilesetResolver","FaceLandmarker","tesselationConnections","ovalIndices","start","error","mediaPipeInitPromise","calculateBoundingBoxCenter","data","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","x","y","updateLandmarksTexture","faces","nFaces","totalLandmarks","landmarks","landmark","dataIdx","faceCenter","mouthCenter","rowsToUpdate","updateMaskTexture","processFaces","result","nDetectionCalls","detectFaces","source","callOrder","requiredMode","updates","checkAt","regionMin","regionMax"]}
1
+ {"version":3,"sources":["../../src/plugins/face.ts","../../src/plugins/mediapipe-common.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport {\n\tcalculateBoundingBoxCenter,\n\tgenerateGLSLFn,\n\tdummyTexture,\n\tgetSharedFileset,\n\thashOptions,\n\tisMediaPipeSource,\n\tMediaPipeSource,\n} from './mediapipe-common';\nimport type { FaceLandmarker, FaceLandmarkerResult, 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\thistory?: number;\n}\n\nconst MASK_VERTEX_SHADER = `#version 300 es\nin vec2 a_pos;\nvoid main() { gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0); }`;\nconst MASK_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`;\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst LANDMARKS_TEXTURE_WIDTH = 512;\nconst N_LANDMARK_METADATA_SLOTS = 1;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst OUTER_MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_MOUTH: OUTER_MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\nconst REGION_NAMES = [\n\t'BACKGROUND',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'OUTER_MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst nFaceRegions = REGION_NAMES.length - 1;\nconst RED_CHANNEL_VALUES = Object.fromEntries(REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\n\nconst DEFAULT_FACE_OPTIONS: Required<Omit<FacePluginOptions, 'history'>> = {\n\tmodelPath:\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task',\n\tmaxFaces: 1,\n\tminFaceDetectionConfidence: 0.5,\n\tminFacePresenceConfidence: 0.5,\n\tminTrackingConfidence: 0.5,\n\toutputFaceBlendshapes: false,\n\toutputFacialTransformationMatrixes: false,\n};\n\nfunction fanTriangulate(indices: readonly number[]): number[] {\n\tconst tris: number[] = [];\n\tfor (let i = 1; i < indices.length - 1; ++i) {\n\t\ttris.push(indices[0], indices[i], indices[i + 1]);\n\t}\n\treturn tris;\n}\n\ntype FaceRegion = { triangles: number[]; vertices: Float32Array };\nlet faceRegions: Record<string, FaceRegion> | null = null;\nfunction initFaceRegions(LandmarkerClass: typeof FaceLandmarker): void {\n\tif (!faceRegions) {\n\t\tconst tesselationConnections = LandmarkerClass.FACE_LANDMARKS_TESSELATION;\n\t\tconst tesselation: number[] = [];\n\t\tfor (let i = 0; i < tesselationConnections.length - 2; i += 3) {\n\t\t\ttesselation.push(\n\t\t\t\ttesselationConnections[i].start,\n\t\t\t\ttesselationConnections[i + 1].start,\n\t\t\t\ttesselationConnections[i + 2].start\n\t\t\t);\n\t\t}\n\t\tconst ovalIndices = LandmarkerClass.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\tfaceRegions = Object.fromEntries(\n\t\t\tObject.entries({\n\t\t\t\tLEFT_EYEBROW: fanTriangulate(LEFT_EYEBROW_INDICES),\n\t\t\t\tRIGHT_EYEBROW: fanTriangulate(RIGHT_EYEBROW_INDICES),\n\t\t\t\tLEFT_EYE: fanTriangulate(LEFT_EYE_INDICES),\n\t\t\t\tRIGHT_EYE: fanTriangulate(RIGHT_EYE_INDICES),\n\t\t\t\tOUTER_MOUTH: fanTriangulate(OUTER_MOUTH_INDICES),\n\t\t\t\tINNER_MOUTH: fanTriangulate(INNER_MOUTH_INDICES),\n\t\t\t\tTESSELATION: tesselation,\n\t\t\t\tOVAL: fanTriangulate(ovalIndices),\n\t\t\t}).map(([key, triangles]) => [key, { triangles, vertices: new Float32Array(triangles.length * 2) }])\n\t\t);\n\t}\n}\n\ninterface Detector {\n\tlandmarker: FaceLandmarker;\n\tcanvas: OffscreenCanvas;\n\tsubscribers: Map<() => void, boolean>;\n\tmaxFaces: number;\n\tstate: {\n\t\trunningMode: 'IMAGE' | 'VIDEO';\n\t\tsource: MediaPipeSource | null;\n\t\tvideoTime: number;\n\t\tresultTimestamp: number;\n\t\tresult: FaceLandmarkerResult | null;\n\t\tpending: Promise<void>;\n\t\tnFaces: number;\n\t};\n\tlandmarks: {\n\t\tdata: Float32Array;\n\t\ttextureHeight: number;\n\t};\n\tmask: {\n\t\tcanvas: OffscreenCanvas;\n\t\tgl: WebGL2RenderingContext;\n\t\tprogram: WebGLProgram;\n\t\tpositionBuffer: WebGLBuffer;\n\t\tcolorLocation: WebGLUniformLocation;\n\t};\n}\nconst sharedDetectors = new Map<string, Detector>();\n\nfunction initMaskRenderer(detector: Detector) {\n\tconst gl = detector.mask.canvas.getContext('webgl2', { antialias: false, preserveDrawingBuffer: true })!;\n\n\tconst vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n\tgl.shaderSource(vertexShader, MASK_VERTEX_SHADER);\n\tgl.compileShader(vertexShader);\n\n\tconst fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n\tgl.shaderSource(fragmentShader, MASK_FRAGMENT_SHADER);\n\tgl.compileShader(fragmentShader);\n\n\tconst program = gl.createProgram()!;\n\tgl.attachShader(program, vertexShader);\n\tgl.attachShader(program, fragmentShader);\n\tgl.linkProgram(program);\n\tgl.deleteShader(vertexShader);\n\tgl.deleteShader(fragmentShader);\n\n\tconst positionBuffer = gl.createBuffer()!;\n\tgl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n\tconst positionLocation = gl.getAttribLocation(program, 'a_pos');\n\tgl.enableVertexAttribArray(positionLocation);\n\tgl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);\n\n\tconst colorLocation = gl.getUniformLocation(program, 'u_color')!;\n\tgl.useProgram(program);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.MAX);\n\n\tdetector.mask = { ...detector.mask, gl, program, positionBuffer, colorLocation };\n}\n\nfunction drawTriangles(detector: Detector, faceRegion: FaceRegion, faceIdx: number, r: number, g: number, b: number) {\n\tconst { triangles, vertices } = faceRegion;\n\tconst {\n\t\tmask: { gl, colorLocation },\n\t\tlandmarks,\n\t} = detector;\n\tconst { data: landmarksDataArray } = landmarks;\n\n\tfor (let i = 0; i < triangles.length; ++i) {\n\t\tconst landmarkIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + triangles[i]) * 4;\n\t\tvertices[i * 2] = landmarksDataArray[landmarkIdx];\n\t\tvertices[i * 2 + 1] = landmarksDataArray[landmarkIdx + 1];\n\t}\n\n\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW);\n\tgl.uniform4f(colorLocation, r, g, b, 1.0);\n\tgl.drawArrays(gl.TRIANGLES, 0, triangles.length);\n}\n\nfunction updateLandmarksData(detector: Detector, faces: NormalizedLandmark[][]) {\n\tconst data = detector.landmarks.data;\n\tconst nFaces = faces.length;\n\tdata[0] = nFaces;\n\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst landmarks = faces[faceIdx];\n\t\tfor (let landmarkIdx = 0; landmarkIdx < STANDARD_LANDMARK_COUNT; ++landmarkIdx) {\n\t\t\tconst landmark = landmarks[landmarkIdx];\n\t\t\tconst dataIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + landmarkIdx) * 4;\n\t\t\tdata[dataIdx] = landmark.x;\n\t\t\tdata[dataIdx + 1] = 1 - landmark.y;\n\t\t\tdata[dataIdx + 2] = landmark.z ?? 0;\n\t\t\tdata[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t}\n\n\t\tconst faceCenter = calculateBoundingBoxCenter(\n\t\t\tdata,\n\t\t\tfaceIdx,\n\t\t\tALL_STANDARD_INDICES,\n\t\t\tLANDMARK_COUNT,\n\t\t\tN_LANDMARK_METADATA_SLOTS\n\t\t);\n\t\tdata.set(faceCenter, (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4);\n\n\t\tconst mouthCenter = calculateBoundingBoxCenter(data, faceIdx, INNER_MOUTH_INDICES, LANDMARK_COUNT, 1);\n\t\tdata.set(\n\t\t\tmouthCenter,\n\t\t\t(N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4\n\t\t);\n\t}\n\n\tdetector.state.nFaces = nFaces;\n}\n\nfunction updateMaskCanvas(detector: Detector) {\n\tif (!faceRegions) return;\n\tconst {\n\t\tmask,\n\t\tcanvas,\n\t\tmaxFaces,\n\t\tstate: { nFaces },\n\t} = detector;\n\tconst { gl: maskGl, canvas: maskCanvas } = mask;\n\n\tmaskCanvas.width = canvas.width;\n\tmaskCanvas.height = canvas.height;\n\tmaskGl.viewport(0, 0, maskCanvas.width, maskCanvas.height);\n\tmaskGl.clearColor(0, 0, 0, 0);\n\tmaskGl.clear(maskGl.COLOR_BUFFER_BIT);\n\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst b = (faceIdx + 1) / maxFaces;\n\n\t\t// G channel: face mesh (0.5) and oval (1.0)\n\t\tdrawTriangles(detector, faceRegions.TESSELATION, faceIdx, 0, 0.5, b);\n\t\tdrawTriangles(detector, faceRegions.OVAL, faceIdx, 0, 1.0, b);\n\n\t\t// R channel: feature regions\n\t\tdrawTriangles(detector, faceRegions.LEFT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.LEFT_EYEBROW, 0, b);\n\t\tdrawTriangles(detector, faceRegions.RIGHT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYEBROW, 0, b);\n\t\tdrawTriangles(detector, faceRegions.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, 0, b);\n\t\tdrawTriangles(detector, faceRegions.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, 0, b);\n\t\tdrawTriangles(detector, faceRegions.OUTER_MOUTH, faceIdx, RED_CHANNEL_VALUES.OUTER_MOUTH, 0, b);\n\t\tdrawTriangles(detector, faceRegions.INNER_MOUTH, faceIdx, RED_CHANNEL_VALUES.INNER_MOUTH, 0, b);\n\t}\n}\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options: { history, ...mediapipeOptions } = {} } = config;\n\tconst options = { ...DEFAULT_FACE_OPTIONS, ...mediapipeOptions };\n\tconst optionsKey = hashOptions({ ...options, textureName });\n\n\tconst nLandmarksMax = options.maxFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\tconst textureHeight = Math.ceil(nLandmarksMax / LANDMARKS_TEXTURE_WIDTH);\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl, emitHook } = context;\n\n\t\tconst existingDetector = sharedDetectors.get(optionsKey);\n\t\tconst landmarksData =\n\t\t\texistingDetector?.landmarks.data ?? new Float32Array(LANDMARKS_TEXTURE_WIDTH * textureHeight * 4);\n\t\tconst maskCanvas = existingDetector?.mask.canvas ?? new OffscreenCanvas(1, 1);\n\t\tlet detector: Detector | null = null;\n\t\tlet skipHistoryWrite = false;\n\n\t\tfunction onResult() {\n\t\t\tif (!detector) return;\n\t\t\tconst nFaces = detector.state.nFaces;\n\t\t\tconst nSlots = nFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\t\t\tconst rowsToUpdate = Math.ceil(nSlots / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures(\n\t\t\t\t{\n\t\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\t\tdata: detector.landmarks.data,\n\t\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\t\tisPartial: true,\n\t\t\t\t\t},\n\t\t\t\t\tu_faceMask: detector.mask.canvas,\n\t\t\t\t},\n\t\t\t\t{ skipHistoryWrite }\n\t\t\t);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\t\t\temitHook('face:result', detector.state.result);\n\t\t}\n\n\t\tasync function initializeDetector() {\n\t\t\tif (sharedDetectors.has(optionsKey)) {\n\t\t\t\tdetector = sharedDetectors.get(optionsKey)!;\n\t\t\t} else {\n\t\t\t\tconst [mediaPipe, { FaceLandmarker }] = await Promise.all([\n\t\t\t\t\tgetSharedFileset(),\n\t\t\t\t\timport('@mediapipe/tasks-vision'),\n\t\t\t\t]);\n\n\t\t\t\tconst mediapipeCanvas = new OffscreenCanvas(1, 1);\n\t\t\t\tconst faceLandmarker = await FaceLandmarker.createFromOptions(mediaPipe, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options.modelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediapipeCanvas,\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options.maxFaces,\n\t\t\t\t\tminFaceDetectionConfidence: options.minFaceDetectionConfidence,\n\t\t\t\t\tminFacePresenceConfidence: options.minFacePresenceConfidence,\n\t\t\t\t\tminTrackingConfidence: options.minTrackingConfidence,\n\t\t\t\t\toutputFaceBlendshapes: options.outputFaceBlendshapes,\n\t\t\t\t\toutputFacialTransformationMatrixes: options.outputFacialTransformationMatrixes,\n\t\t\t\t});\n\n\t\t\t\tdetector = {\n\t\t\t\t\tlandmarker: faceLandmarker,\n\t\t\t\t\tcanvas: mediapipeCanvas,\n\t\t\t\t\tsubscribers: new Map(),\n\t\t\t\t\tmaxFaces: options.maxFaces,\n\t\t\t\t\tstate: {\n\t\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\tvideoTime: -1,\n\t\t\t\t\t\tresultTimestamp: 0,\n\t\t\t\t\t\tresult: null,\n\t\t\t\t\t\tpending: Promise.resolve(),\n\t\t\t\t\t\tnFaces: 0,\n\t\t\t\t\t},\n\t\t\t\t\tlandmarks: {\n\t\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\t\ttextureHeight,\n\t\t\t\t\t},\n\t\t\t\t\tmask: {\n\t\t\t\t\t\tcanvas: maskCanvas,\n\t\t\t\t\t} as Detector['mask'],\n\t\t\t\t};\n\n\t\t\t\tinitFaceRegions(FaceLandmarker);\n\t\t\t\tinitMaskRenderer(detector);\n\t\t\t\tsharedDetectors.set(optionsKey, detector);\n\t\t\t}\n\n\t\t\tdetector.subscribers.set(onResult, false);\n\t\t}\n\t\tconst initPromise = initializeDetector();\n\n\t\tlet nDetectionCalls = 0;\n\t\tasync function detectFaces(source: MediaPipeSource) {\n\t\t\tconst now = performance.now();\n\t\t\tconst callOrder = ++nDetectionCalls;\n\t\t\tawait initPromise;\n\t\t\tif (!detector) return;\n\n\t\t\tdetector.state.pending = detector.state.pending.then(async () => {\n\t\t\t\tif (callOrder !== nDetectionCalls || !detector) return;\n\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (detector.state.runningMode !== requiredMode) {\n\t\t\t\t\tdetector.state.runningMode = requiredMode;\n\t\t\t\t\tawait detector.landmarker.setOptions({ runningMode: requiredMode });\n\t\t\t\t}\n\n\t\t\t\tlet shouldDetect = false;\n\n\t\t\t\tif (source !== detector.state.source) {\n\t\t\t\t\tdetector.state.source = source;\n\t\t\t\t\tdetector.state.videoTime = -1;\n\t\t\t\t\tshouldDetect = true;\n\t\t\t\t} else if (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.currentTime !== detector.state.videoTime) {\n\t\t\t\t\t\tdetector.state.videoTime = source.currentTime;\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t} else if (!(source instanceof HTMLImageElement)) {\n\t\t\t\t\tif (now - detector.state.resultTimestamp > 2) {\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldDetect) {\n\t\t\t\t\tlet result: FaceLandmarkerResult | undefined;\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\t\tresult = detector.landmarker.detectForVideo(source, now);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\t\tresult = detector.landmarker.detect(source);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tdetector.state.resultTimestamp = now;\n\t\t\t\t\t\tdetector.state.result = result;\n\t\t\t\t\t\tupdateLandmarksData(detector, result.faceLandmarks);\n\t\t\t\t\t\tupdateMaskCanvas(detector);\n\t\t\t\t\t\tfor (const cb of detector.subscribers.keys()) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (detector.state.result && !detector.subscribers.get(onResult)) {\n\t\t\t\t\tonResult();\n\t\t\t\t\tdetector.subscribers.set(onResult, true);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait detector.state.pending;\n\t\t}\n\n\t\tshaderPad.on('init', () => {\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', options.maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{ data: landmarksData, width: LANDMARKS_TEXTURE_WIDTH, height: textureHeight },\n\t\t\t\t{ internalFormat: gl.RGBA32F, type: gl.FLOAT, minFilter: gl.NEAREST, magFilter: gl.NEAREST, history }\n\t\t\t);\n\t\t\tshaderPad.initializeTexture('u_faceMask', maskCanvas, {\n\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t\thistory,\n\t\t\t});\n\t\t\tinitPromise.then(() => emitHook('face:ready'));\n\t\t});\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName && isMediaPipeSource(source)) detectFaces(source);\n\t\t});\n\n\t\tshaderPad.on(\n\t\t\t'updateTextures',\n\t\t\t(updates: Record<string, TextureSource>, options?: { skipHistoryWrite?: boolean }) => {\n\t\t\t\tconst source = updates[textureName];\n\t\t\t\tif (isMediaPipeSource(source)) {\n\t\t\t\t\tskipHistoryWrite = options?.skipHistoryWrite ?? false;\n\t\t\t\t\tdetectFaces(source);\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tif (detector) {\n\t\t\t\tdetector.subscribers.delete(onResult);\n\t\t\t\tif (detector.subscribers.size === 0) {\n\t\t\t\t\tdetector.landmarker.close();\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.program);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.positionBuffer);\n\t\t\t\t\tsharedDetectors.delete(optionsKey);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdetector = null;\n\t\t});\n\n\t\tconst { fn, historyParams } = generateGLSLFn(history);\n\t\tconst sampleMask = history ? `_sampleFaceMask(pos, framesAgo)` : `texture(u_faceMask, pos)`;\n\n\t\tconst checkAt = (\n\t\t\tregionMin: keyof typeof RED_CHANNEL_VALUES,\n\t\t\tregionMax: keyof typeof RED_CHANNEL_VALUES = regionMin\n\t\t) =>\n\t\t\t`vec4 mask = ${sampleMask};\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn (mask.r > ${(RED_CHANNEL_VALUES[regionMin] - HALF_GAP).toFixed(4)} && mask.r < ${(\n\t\t\t\tRED_CHANNEL_VALUES[regionMax] + HALF_GAP\n\t\t\t).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;\n\n\t\tconst checkMaskG = (threshold: number) =>\n\t\t\t`vec4 mask = ${sampleMask};\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > ${threshold.toFixed(2)} ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;\n\n\t\tconst combineLeftRight = (leftFn: string, rightFn: string) =>\n\t\t\t`vec2 left = ${leftFn}(pos${historyParams});\n\treturn left.x > 0.0 ? left : ${rightFn}(pos${historyParams});`;\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform highp sampler2D${history ? 'Array' : ''} u_faceLandmarksTex;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceLandmarksTexFrameOffset;`\n\t\t\t\t: ''\n\t\t}\nuniform sampler2D${history ? 'Array' : ''} u_faceMask;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceMaskFrameOffset;`\n\t\t\t\t: ''\n\t\t}\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\n${fn(\n\t'int',\n\t'nFacesAt',\n\t'',\n\thistory\n\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history}) % ${history};\n\treturn int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`\n\t\t: `\n\treturn int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`\n)}\n${fn(\n\t'vec4',\n\t'faceLandmark',\n\t'int faceIndex, int landmarkIndex',\n\t`int i = ${N_LANDMARK_METADATA_SLOTS} + faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};${\n\t\thistory\n\t\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history}) % ${history};\n\treturn texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`\n\t\t\t: `\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`\n\t}`\n)}\n${\n\thistory\n\t\t? `\nvec4 _sampleFaceMask(vec2 pos, int framesAgo) {\n\tint layer = (u_faceMaskFrameOffset - framesAgo + ${history}) % ${history};\n\treturn texture(u_faceMask, vec3(pos, float(layer)));\n}\n`\n\t\t: ''\n}\n${fn('vec2', 'leftEyebrowAt', 'vec2 pos', checkAt('LEFT_EYEBROW'))}\n${fn('vec2', 'rightEyebrowAt', 'vec2 pos', checkAt('RIGHT_EYEBROW'))}\n${fn('vec2', 'leftEyeAt', 'vec2 pos', checkAt('LEFT_EYE'))}\n${fn('vec2', 'rightEyeAt', 'vec2 pos', checkAt('RIGHT_EYE'))}\n${fn('vec2', 'lipsAt', 'vec2 pos', checkAt('OUTER_MOUTH'))}\n${fn('vec2', 'outerMouthAt', 'vec2 pos', checkAt('OUTER_MOUTH', 'INNER_MOUTH'))}\n${fn('vec2', 'innerMouthAt', 'vec2 pos', checkAt('INNER_MOUTH'))}\n${fn('vec2', 'faceOvalAt', 'vec2 pos', checkMaskG(0.75))}\n${fn('vec2', 'faceAt', 'vec2 pos', checkMaskG(0.25))}\n${fn('vec2', 'eyeAt', 'vec2 pos', combineLeftRight('leftEyeAt', 'rightEyeAt'))}\n${fn('vec2', 'eyebrowAt', 'vec2 pos', combineLeftRight('leftEyebrowAt', 'rightEyebrowAt'))}\n${fn('float', 'inEyebrow', 'vec2 pos', `return eyebrowAt(pos${historyParams}).x;`)}\n${fn('float', 'inEye', 'vec2 pos', `return eyeAt(pos${historyParams}).x;`)}\n${fn('float', 'inOuterMouth', 'vec2 pos', `return outerMouthAt(pos${historyParams}).x;`)}\n${fn('float', 'inInnerMouth', 'vec2 pos', `return innerMouthAt(pos${historyParams}).x;`)}\n${fn('float', 'inLips', 'vec2 pos', `return lipsAt(pos${historyParams}).x;`)}\n${fn('float', 'inFace', 'vec2 pos', `return faceAt(pos${historyParams}).x;`)}`);\n\t};\n}\n\nexport default face;\n","import { TextureSource } from '..';\n\nexport const dummyTexture = { data: new Uint8Array(4), width: 1, height: 1 };\n\nexport type MediaPipeSource = HTMLVideoElement | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas;\n\nexport function isMediaPipeSource(source: TextureSource): source is MediaPipeSource {\n\treturn (\n\t\tsource instanceof HTMLVideoElement ||\n\t\tsource instanceof HTMLImageElement ||\n\t\tsource instanceof HTMLCanvasElement ||\n\t\tsource instanceof OffscreenCanvas\n\t);\n}\n\nexport function hashOptions(options: object): string {\n\treturn JSON.stringify(options, Object.keys(options).sort());\n}\n\nexport function calculateBoundingBoxCenter(\n\tdata: Float32Array,\n\tentityIdx: number,\n\tlandmarkIndices: readonly number[] | number[],\n\tlandmarkCount: number,\n\toffset: number = 0\n): [number, number, number, number] {\n\tlet minX = Infinity,\n\t\tmaxX = -Infinity,\n\t\tminY = Infinity,\n\t\tmaxY = -Infinity,\n\t\tavgZ = 0,\n\t\tavgVisibility = 0;\n\n\tfor (const idx of landmarkIndices) {\n\t\tconst dataIdx = (offset + entityIdx * landmarkCount + idx) * 4;\n\t\tconst x = data[dataIdx];\n\t\tconst y = data[dataIdx + 1];\n\t\tminX = Math.min(minX, x);\n\t\tmaxX = Math.max(maxX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxY = Math.max(maxY, y);\n\t\tavgZ += data[dataIdx + 2];\n\t\tavgVisibility += data[dataIdx + 3];\n\t}\n\n\treturn [\n\t\t(minX + maxX) / 2,\n\t\t(minY + maxY) / 2,\n\t\tavgZ / landmarkIndices.length,\n\t\tavgVisibility / landmarkIndices.length,\n\t];\n}\n\nlet filesetPromise: Promise<any> | null = null;\nexport function getSharedFileset(): Promise<any> {\n\tif (!filesetPromise) {\n\t\tfilesetPromise = import('@mediapipe/tasks-vision').then(({ FilesetResolver }) =>\n\t\t\tFilesetResolver.forVisionTasks(\n\t\t\t\t`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${__MEDIAPIPE_TASKS_VISION_VERSION__}/wasm`\n\t\t\t)\n\t\t);\n\t}\n\treturn filesetPromise;\n}\n\nexport function generateGLSLFn(history: number | undefined) {\n\tconst historyParams = history ? ', framesAgo' : '';\n\tconst fn = history\n\t\t? (returnType: string, name: string, args: string, body: string) => {\n\t\t\t\tconst argsOnly = args.replace(/\\w+ /g, '');\n\t\t\t\tconst historyArgs = args ? `${args}, int framesAgo` : 'int framesAgo';\n\t\t\t\tconst callArgs = argsOnly ? `${argsOnly}, 0` : '0';\n\t\t\t\treturn `${returnType} ${name}(${historyArgs}) {\\n${body}\\n}\n${returnType} ${name}(${args}) { return ${name}(${callArgs}); }`;\n\t\t }\n\t\t: (returnType: string, name: string, args: string, body: string) =>\n\t\t\t\t`${returnType} ${name}(${args}) {\\n${body}\\n}`;\n\treturn { historyParams, fn };\n}\n"],"mappings":"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","names":["face_exports","__export","face_default","__toCommonJS","dummyTexture","isMediaPipeSource","source","hashOptions","options","calculateBoundingBoxCenter","data","entityIdx","landmarkIndices","landmarkCount","offset","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","filesetPromise","getSharedFileset","FilesetResolver","generateGLSLFn","history","returnType","name","args","body","argsOnly","historyArgs","callArgs","MASK_VERTEX_SHADER","MASK_FRAGMENT_SHADER","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LANDMARKS_TEXTURE_WIDTH","N_LANDMARK_METADATA_SLOTS","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","OUTER_MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","REGION_NAMES","nFaceRegions","RED_CHANNEL_VALUES","name","HALF_GAP","DEFAULT_FACE_OPTIONS","fanTriangulate","indices","tris","faceRegions","initFaceRegions","LandmarkerClass","tesselationConnections","tesselation","ovalIndices","start","key","triangles","sharedDetectors","initMaskRenderer","detector","gl","vertexShader","fragmentShader","program","positionBuffer","positionLocation","colorLocation","drawTriangles","faceRegion","faceIdx","r","g","b","vertices","landmarks","landmarksDataArray","landmarkIdx","updateLandmarksData","faces","data","nFaces","landmark","dataIdx","faceCenter","calculateBoundingBoxCenter","mouthCenter","updateMaskCanvas","mask","canvas","maxFaces","maskGl","maskCanvas","face","config","textureName","history","mediapipeOptions","options","optionsKey","hashOptions","nLandmarksMax","textureHeight","shaderPad","context","injectGLSL","emitHook","existingDetector","landmarksData","skipHistoryWrite","onResult","nSlots","rowsToUpdate","initializeDetector","mediaPipe","FaceLandmarker","getSharedFileset","mediapipeCanvas","initPromise","nDetectionCalls","detectFaces","source","now","callOrder","requiredMode","shouldDetect","result","cb","isMediaPipeSource","updates","fn","historyParams","generateGLSLFn","sampleMask","checkAt","regionMin","regionMax","checkMaskG","threshold","combineLeftRight","leftFn","rightFn","face_default"]}
@@ -1,87 +1,59 @@
1
- var C=478,Ee=2,A=C+Ee,I=512,j=[336,296,334,293,300,276,283,282,295,285],q=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],Z=[70,63,105,66,107,55,65,52,53,46],J=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],Q=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],Y=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],le=Array.from({length:C},(m,s)=>s),O={LEFT_EYEBROW:j,LEFT_EYE:q,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:Z,RIGHT_EYE:J,RIGHT_EYE_CENTER:468,NOSE_TIP:4,OUTER_MOUTH:Q,INNER_MOUTH:Y,FACE_CENTER:C,MOUTH_CENTER:C+1},ee=["BACKGROUND","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","OUTER_MOUTH","INNER_MOUTH"],te=ee.length-1,R=Object.fromEntries(ee.map((m,s)=>[m,s/te])),X=.5/te;function v(m){let s=[];for(let c=1;c<m.length-1;++c)s.push(m[0],m[c],m[c+1]);return s}function ue(m){let{textureName:s,options:c}=m,ne="https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task";return function(E,ae){let{injectGLSL:oe,gl:h,emitHook:w}=ae,p=null,k=null,S=-1,g="VIDEO",b=new Map,y=c?.maxFaces??1,H=0,r=null,D=new OffscreenCanvas(1,1),F=new OffscreenCanvas(1,1),t=null,f=null,U=null,G=null,l={LEFT_EYEBROW:v(j),RIGHT_EYEBROW:v(Z),LEFT_EYE:v(q),RIGHT_EYE:v(J),OUTER_MOUTH:v(Q),INNER_MOUTH:v(Y),TESSELATION:[],OVAL:[]};function re(){if(t=F.getContext("webgl2",{antialias:!1,preserveDrawingBuffer:!0}),!t)throw new Error("Failed to get WebGL2 context for mask");let e=t.createShader(t.VERTEX_SHADER);t.shaderSource(e,`#version 300 es
1
+ import{b as y,c as j,d as H,e as X,f as q}from"../chunk-JRSBIGBN.mjs";var oe=`#version 300 es
2
2
  in vec2 a_pos;
3
- void main() {
4
- gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);
5
- }`),t.compileShader(e);let n=t.createShader(t.FRAGMENT_SHADER);t.shaderSource(n,`#version 300 es
3
+ void main() { gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0); }`,ie=`#version 300 es
6
4
  precision mediump float;
7
5
  uniform vec4 u_color;
8
6
  out vec4 outColor;
9
- void main() { outColor = u_color; }`),t.compileShader(n),f=t.createProgram(),t.attachShader(f,e),t.attachShader(f,n),t.linkProgram(f),t.deleteShader(e),t.deleteShader(n),U=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,U);let a=t.getAttribLocation(f,"a_pos");t.enableVertexAttribArray(a),t.vertexAttribPointer(a,2,t.FLOAT,!1,0,0),G=t.getUniformLocation(f,"u_color"),t.useProgram(f),t.enable(t.BLEND),t.blendEquation(t.MAX)}function d(e,n,a,i,o){if(!t||!r||e.length===0)return;let _=new Float32Array(e.length*2);for(let u=0;u<e.length;++u){let N=(n*A+e[u])*4;_[u*2]=r[N],_[u*2+1]=r[N+1]}t.bufferData(t.ARRAY_BUFFER,_,t.DYNAMIC_DRAW),t.uniform4f(G,a,i,o,1),t.drawArrays(t.TRIANGLES,0,e.length)}async function ie(){try{let{FilesetResolver:e,FaceLandmarker:n}=await import("@mediapipe/tasks-vision");k=await e.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),p=await n.createFromOptions(k,{baseOptions:{modelAssetPath:c?.modelPath||ne,delegate:"GPU"},canvas:D,runningMode:g,numFaces:c?.maxFaces??1,minFaceDetectionConfidence:c?.minFaceDetectionConfidence??.5,minFacePresenceConfidence:c?.minFacePresenceConfidence??.5,minTrackingConfidence:c?.minTrackingConfidence??.5,outputFaceBlendshapes:c?.outputFaceBlendshapes??!1,outputFacialTransformationMatrixes:c?.outputFacialTransformationMatrixes??!1});let a=n.FACE_LANDMARKS_TESSELATION;l.TESSELATION=[];for(let o=0;o<a.length-2;o+=3)l.TESSELATION.push(a[o].start,a[o+1].start,a[o+2].start);let i=n.FACE_LANDMARKS_FACE_OVAL.map(({start:o})=>o);l.OVAL=v(i),re()}catch(e){throw console.error("[Face Plugin] Failed to initialize MediaPipe:",e),e}}let B=ie();function P(e,n,a){let i=1/0,o=-1/0,_=1/0,u=-1/0,N=0,x=0;for(let M of a){let T=(n*A+M)*4,K=e[T],z=e[T+1];i=Math.min(i,K),o=Math.max(o,K),_=Math.min(_,z),u=Math.max(u,z),N+=e[T+2],x+=e[T+3]}return[(i+o)/2,(_+u)/2,N/a.length,x/a.length]}function ce(e){if(!r)return;let n=e.length,a=n*A;for(let o=0;o<n;++o){let _=e[o];for(let x=0;x<C;++x){let M=_[x],T=(o*A+x)*4;r[T]=M.x,r[T+1]=1-M.y,r[T+2]=M.z??0,r[T+3]=M.visibility??1}let u=P(r,o,le);r.set(u,(o*A+O.FACE_CENTER)*4);let N=P(r,o,Y);r.set(N,(o*A+O.MOUTH_CENTER)*4)}let i=Math.ceil(a/I);E.updateTextures({u_faceLandmarksTex:{data:r,width:I,height:i,isPartial:!0}})}function se(e){if(!(!t||!r)){F.width=D.width,F.height=D.height,t.viewport(0,0,F.width,F.height),t.clearColor(0,0,0,0),t.clear(t.COLOR_BUFFER_BIT);for(let n=0;n<e;++n){let a=(n+1)/y;d(l.TESSELATION,n,0,.5,a),d(l.OVAL,n,0,1,a),d(l.LEFT_EYEBROW,n,R.LEFT_EYEBROW,0,a),d(l.RIGHT_EYEBROW,n,R.RIGHT_EYEBROW,0,a),d(l.LEFT_EYE,n,R.LEFT_EYE,0,a),d(l.RIGHT_EYE,n,R.RIGHT_EYE,0,a),d(l.OUTER_MOUTH,n,R.OUTER_MOUTH,0,a),d(l.INNER_MOUTH,n,R.INNER_MOUTH,0,a)}E.updateTextures({u_faceMask:F})}}function W(e){if(!e.faceLandmarks||!r)return;let n=e.faceLandmarks.length;ce(e.faceLandmarks),se(n),E.updateUniforms({u_nFaces:n}),w("face:result",e)}let V=0;async function $(e){let n=++V;if(await B,n!==V||!p)return;b.get(s)!==e&&(S=-1),b.set(s,e);try{let i=e instanceof HTMLVideoElement?"VIDEO":"IMAGE";if(g!==i&&(g=i,await p.setOptions({runningMode:g})),e instanceof HTMLVideoElement){if(e.videoWidth===0||e.videoHeight===0||e.readyState<2)return;e.currentTime!==S&&(S=e.currentTime,W(p.detectForVideo(e,performance.now())))}else if(e instanceof HTMLImageElement||e instanceof HTMLCanvasElement){if(e.width===0||e.height===0)return;W(p.detect(e))}}catch(i){console.error("[Face Plugin] Detection error:",i)}}E.on("init",async()=>{E.initializeTexture("u_faceMask",F,{minFilter:h.NEAREST,magFilter:h.NEAREST}),E.initializeUniform("u_maxFaces","int",y),E.initializeUniform("u_nFaces","int",0);let e=y*A;H=Math.ceil(e/I),r=new Float32Array(I*H*4),E.initializeTexture("u_faceLandmarksTex",{data:r,width:I,height:H},{internalFormat:h.RGBA32F,type:h.FLOAT,minFilter:h.NEAREST,magFilter:h.NEAREST}),await B,w("face:ready")}),E.on("initializeTexture",(e,n)=>{e===s&&$(n)}),E.on("updateTextures",e=>{let n=e[s];n&&$(n)}),E.on("destroy",()=>{p?.close(),p=null,t&&f&&(t.deleteProgram(f),t.deleteBuffer(U)),t=null,f=null,k=null,b.clear(),r=null});let L=(e,n=e)=>`vec4 mask = texture(u_faceMask, pos);
7
+ void main() { outColor = u_color; }`,b=478,ce=2,T=b+ce,v=512,L=1,Q=[336,296,334,293,300,276,283,282,295,285],Z=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],ee=[70,63,105,66,107,55,65,52,53,46],te=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],ae=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],$=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],le=Array.from({length:b},(r,e)=>e),N={LEFT_EYEBROW:Q,LEFT_EYE:Z,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:ee,RIGHT_EYE:te,RIGHT_EYE_CENTER:468,NOSE_TIP:4,OUTER_MOUTH:ae,INNER_MOUTH:$,FACE_CENTER:b,MOUTH_CENTER:b+1},ne=["BACKGROUND","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","OUTER_MOUTH","INNER_MOUTH"],re=ne.length-1,k=Object.fromEntries(ne.map((r,e)=>[r,e/re])),J=.5/re,me={modelPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task",maxFaces:1,minFaceDetectionConfidence:.5,minFacePresenceConfidence:.5,minTrackingConfidence:.5,outputFaceBlendshapes:!1,outputFacialTransformationMatrixes:!1};function M(r){let e=[];for(let t=1;t<r.length-1;++t)e.push(r[0],r[t],r[t+1]);return e}var f=null;function Ee(r){if(!f){let e=r.FACE_LANDMARKS_TESSELATION,t=[];for(let n=0;n<e.length-2;n+=3)t.push(e[n].start,e[n+1].start,e[n+2].start);let E=r.FACE_LANDMARKS_FACE_OVAL.map(({start:n})=>n);f=Object.fromEntries(Object.entries({LEFT_EYEBROW:M(Q),RIGHT_EYEBROW:M(ee),LEFT_EYE:M(Z),RIGHT_EYE:M(te),OUTER_MOUTH:M(ae),INNER_MOUTH:M($),TESSELATION:t,OVAL:M(E)}).map(([n,c])=>[n,{triangles:c,vertices:new Float32Array(c.length*2)}]))}}var I=new Map;function ue(r){let e=r.mask.canvas.getContext("webgl2",{antialias:!1,preserveDrawingBuffer:!0}),t=e.createShader(e.VERTEX_SHADER);e.shaderSource(t,oe),e.compileShader(t);let E=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(E,ie),e.compileShader(E);let n=e.createProgram();e.attachShader(n,t),e.attachShader(n,E),e.linkProgram(n),e.deleteShader(t),e.deleteShader(E);let c=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,c);let u=e.getAttribLocation(n,"a_pos");e.enableVertexAttribArray(u),e.vertexAttribPointer(u,2,e.FLOAT,!1,0,0);let i=e.getUniformLocation(n,"u_color");e.useProgram(n),e.enable(e.BLEND),e.blendEquation(e.MAX),r.mask={...r.mask,gl:e,program:n,positionBuffer:c,colorLocation:i}}function g(r,e,t,E,n,c){let{triangles:u,vertices:i}=e,{mask:{gl:s,colorLocation:p},landmarks:A}=r,{data:d}=A;for(let F=0;F<u.length;++F){let x=(L+t*T+u[F])*4;i[F*2]=d[x],i[F*2+1]=d[x+1]}s.bufferData(s.ARRAY_BUFFER,i,s.DYNAMIC_DRAW),s.uniform4f(p,E,n,c,1),s.drawArrays(s.TRIANGLES,0,u.length)}function fe(r,e){let t=r.landmarks.data,E=e.length;t[0]=E;for(let n=0;n<E;++n){let c=e[n];for(let s=0;s<b;++s){let p=c[s],A=(L+n*T+s)*4;t[A]=p.x,t[A+1]=1-p.y,t[A+2]=p.z??0,t[A+3]=p.visibility??1}let u=H(t,n,le,T,L);t.set(u,(L+n*T+N.FACE_CENTER)*4);let i=H(t,n,$,T,1);t.set(i,(L+n*T+N.MOUTH_CENTER)*4)}r.state.nFaces=E}function de(r){if(!f)return;let{mask:e,canvas:t,maxFaces:E,state:{nFaces:n}}=r,{gl:c,canvas:u}=e;u.width=t.width,u.height=t.height,c.viewport(0,0,u.width,u.height),c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT);for(let i=0;i<n;++i){let s=(i+1)/E;g(r,f.TESSELATION,i,0,.5,s),g(r,f.OVAL,i,0,1,s),g(r,f.LEFT_EYEBROW,i,k.LEFT_EYEBROW,0,s),g(r,f.RIGHT_EYEBROW,i,k.RIGHT_EYEBROW,0,s),g(r,f.LEFT_EYE,i,k.LEFT_EYE,0,s),g(r,f.RIGHT_EYE,i,k.RIGHT_EYE,0,s),g(r,f.OUTER_MOUTH,i,k.OUTER_MOUTH,0,s),g(r,f.INNER_MOUTH,i,k.INNER_MOUTH,0,s)}}function _e(r){let{textureName:e,options:{history:t,...E}={}}=r,n={...me,...E},c=j({...n,textureName:e}),u=n.maxFaces*T+L,i=Math.ceil(u/v);return function(s,p){let{injectGLSL:A,gl:d,emitHook:F}=p,x=I.get(c),U=x?.landmarks.data??new Float32Array(v*i*4),Y=x?.mask.canvas??new OffscreenCanvas(1,1),a=null,B=!1;function h(){if(!a)return;let o=a.state.nFaces,m=o*T+L,_=Math.ceil(m/v);s.updateTextures({u_faceLandmarksTex:{data:a.landmarks.data,width:v,height:_,isPartial:!0},u_faceMask:a.mask.canvas},{skipHistoryWrite:B}),s.updateUniforms({u_nFaces:o}),F("face:result",a.state.result)}async function se(){if(I.has(c))a=I.get(c);else{let[o,{FaceLandmarker:m}]=await Promise.all([X(),import("@mediapipe/tasks-vision")]),_=new OffscreenCanvas(1,1);a={landmarker:await m.createFromOptions(o,{baseOptions:{modelAssetPath:n.modelPath,delegate:"GPU"},canvas:_,runningMode:"VIDEO",numFaces:n.maxFaces,minFaceDetectionConfidence:n.minFaceDetectionConfidence,minFacePresenceConfidence:n.minFacePresenceConfidence,minTrackingConfidence:n.minTrackingConfidence,outputFaceBlendshapes:n.outputFaceBlendshapes,outputFacialTransformationMatrixes:n.outputFacialTransformationMatrixes}),canvas:_,subscribers:new Map,maxFaces:n.maxFaces,state:{runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nFaces:0},landmarks:{data:U,textureHeight:i},mask:{canvas:Y}},Ee(m),ue(a),I.set(c,a)}a.subscribers.set(h,!1)}let G=se(),w=0;async function P(o){let m=performance.now(),_=++w;await G,a&&(a.state.pending=a.state.pending.then(async()=>{if(_!==w||!a)return;let S=o instanceof HTMLVideoElement?"VIDEO":"IMAGE";a.state.runningMode!==S&&(a.state.runningMode=S,await a.landmarker.setOptions({runningMode:S}));let D=!1;if(o!==a.state.source?(a.state.source=o,a.state.videoTime=-1,D=!0):o instanceof HTMLVideoElement?o.currentTime!==a.state.videoTime&&(a.state.videoTime=o.currentTime,D=!0):o instanceof HTMLImageElement||m-a.state.resultTimestamp>2&&(D=!0),D){let C;if(o instanceof HTMLVideoElement){if(o.videoWidth===0||o.videoHeight===0||o.readyState<2)return;C=a.landmarker.detectForVideo(o,m)}else{if(o.width===0||o.height===0)return;C=a.landmarker.detect(o)}if(C){a.state.resultTimestamp=m,a.state.result=C,fe(a,C.faceLandmarks),de(a);for(let z of a.subscribers.keys())z(),a.subscribers.set(z,!0)}}else a.state.result&&!a.subscribers.get(h)&&(h(),a.subscribers.set(h,!0))}),await a.state.pending)}s.on("init",()=>{s.initializeUniform("u_maxFaces","int",n.maxFaces),s.initializeUniform("u_nFaces","int",0),s.initializeTexture("u_faceLandmarksTex",{data:U,width:v,height:i},{internalFormat:d.RGBA32F,type:d.FLOAT,minFilter:d.NEAREST,magFilter:d.NEAREST,history:t}),s.initializeTexture("u_faceMask",Y,{minFilter:d.NEAREST,magFilter:d.NEAREST,history:t}),G.then(()=>F("face:ready"))}),s.on("initializeTexture",(o,m)=>{o===e&&y(m)&&P(m)}),s.on("updateTextures",(o,m)=>{let _=o[e];y(_)&&(B=m?.skipHistoryWrite??!1,P(_))}),s.on("destroy",()=>{a&&(a.subscribers.delete(h),a.subscribers.size===0&&(a.landmarker.close(),a.mask.gl.deleteProgram(a.mask.program),a.mask.gl.deleteBuffer(a.mask.positionBuffer),I.delete(c))),a=null});let{fn:l,historyParams:R}=q(t),W=t?"_sampleFaceMask(pos, framesAgo)":"texture(u_faceMask, pos)",O=(o,m=o)=>`vec4 mask = ${W};
10
8
  float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
11
- return (mask.r > ${(R[e]-X).toFixed(4)} && mask.r < ${(R[n]+X).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;oe(`
9
+ return (mask.r > ${(k[o]-J).toFixed(4)} && mask.r < ${(k[m]+J).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`,K=o=>`vec4 mask = ${W};
10
+ float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
11
+ return mask.g > ${o.toFixed(2)} ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`,V=(o,m)=>`vec2 left = ${o}(pos${R});
12
+ return left.x > 0.0 ? left : ${m}(pos${R});`;A(`
12
13
  uniform int u_maxFaces;
13
14
  uniform int u_nFaces;
14
- uniform sampler2D u_faceLandmarksTex;
15
- uniform sampler2D u_faceMask;
16
-
17
- #define FACE_LANDMARK_L_EYE_CENTER ${O.LEFT_EYE_CENTER}
18
- #define FACE_LANDMARK_R_EYE_CENTER ${O.RIGHT_EYE_CENTER}
19
- #define FACE_LANDMARK_NOSE_TIP ${O.NOSE_TIP}
20
- #define FACE_LANDMARK_FACE_CENTER ${O.FACE_CENTER}
21
- #define FACE_LANDMARK_MOUTH_CENTER ${O.MOUTH_CENTER}
22
-
23
- vec4 faceLandmark(int faceIndex, int landmarkIndex) {
24
- int i = faceIndex * ${A} + landmarkIndex;
25
- int x = i % ${I};
26
- int y = i / ${I};
27
- return texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);
28
- }
29
-
30
- vec2 leftEyebrowAt(vec2 pos) {
31
- ${L("LEFT_EYEBROW")}
32
- }
33
-
34
- vec2 rightEyebrowAt(vec2 pos) {
35
- ${L("RIGHT_EYEBROW")}
36
- }
37
-
38
- vec2 leftEyeAt(vec2 pos) {
39
- ${L("LEFT_EYE")}
40
- }
41
-
42
- vec2 rightEyeAt(vec2 pos) {
43
- ${L("RIGHT_EYE")}
44
- }
45
-
46
- vec2 lipsAt(vec2 pos) {
47
- ${L("OUTER_MOUTH")}
48
- }
49
-
50
- vec2 outerMouthAt(vec2 pos) {
51
- ${L("OUTER_MOUTH","INNER_MOUTH")}
52
- }
53
-
54
- vec2 innerMouthAt(vec2 pos) {
55
- ${L("INNER_MOUTH")}
56
- }
57
-
58
- vec2 faceOvalAt(vec2 pos) {
59
- vec4 mask = texture(u_faceMask, pos);
60
- float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
61
- return mask.g > 0.75 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);
62
- }
63
-
64
- // Includes face mesh and oval.
65
- vec2 faceAt(vec2 pos) {
66
- vec4 mask = texture(u_faceMask, pos);
67
- float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
68
- return mask.g > 0.25 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);
69
- }
70
-
71
- vec2 eyeAt(vec2 pos) {
72
- vec2 left = leftEyeAt(pos);
73
- return left.x > 0.0 ? left : rightEyeAt(pos);
74
- }
75
-
76
- vec2 eyebrowAt(vec2 pos) {
77
- vec2 left = leftEyebrowAt(pos);
78
- return left.x > 0.0 ? left : rightEyebrowAt(pos);
79
- }
80
-
81
- float inEyebrow(vec2 pos) { return eyebrowAt(pos).x; }
82
- float inEye(vec2 pos) { return eyeAt(pos).x; }
83
- float inOuterMouth(vec2 pos) { return outerMouthAt(pos).x; }
84
- float inInnerMouth(vec2 pos) { return innerMouthAt(pos).x; }
85
- float inLips(vec2 pos) { return lipsAt(pos).x; }
86
- float inFace(vec2 pos) { return faceAt(pos).x; }`)}}var fe=ue;export{fe as default};
15
+ uniform highp sampler2D${t?"Array":""} u_faceLandmarksTex;${t?`
16
+ uniform int u_faceLandmarksTexFrameOffset;`:""}
17
+ uniform sampler2D${t?"Array":""} u_faceMask;${t?`
18
+ uniform int u_faceMaskFrameOffset;`:""}
19
+
20
+ #define FACE_LANDMARK_L_EYE_CENTER ${N.LEFT_EYE_CENTER}
21
+ #define FACE_LANDMARK_R_EYE_CENTER ${N.RIGHT_EYE_CENTER}
22
+ #define FACE_LANDMARK_NOSE_TIP ${N.NOSE_TIP}
23
+ #define FACE_LANDMARK_FACE_CENTER ${N.FACE_CENTER}
24
+ #define FACE_LANDMARK_MOUTH_CENTER ${N.MOUTH_CENTER}
25
+
26
+ ${l("int","nFacesAt","",t?`
27
+ int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${t}) % ${t};
28
+ return int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`:`
29
+ return int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`)}
30
+ ${l("vec4","faceLandmark","int faceIndex, int landmarkIndex",`int i = ${L} + faceIndex * ${T} + landmarkIndex;
31
+ int x = i % ${v};
32
+ int y = i / ${v};${t?`
33
+ int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${t}) % ${t};
34
+ return texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`:`
35
+ return texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`}`)}
36
+ ${t?`
37
+ vec4 _sampleFaceMask(vec2 pos, int framesAgo) {
38
+ int layer = (u_faceMaskFrameOffset - framesAgo + ${t}) % ${t};
39
+ return texture(u_faceMask, vec3(pos, float(layer)));
40
+ }
41
+ `:""}
42
+ ${l("vec2","leftEyebrowAt","vec2 pos",O("LEFT_EYEBROW"))}
43
+ ${l("vec2","rightEyebrowAt","vec2 pos",O("RIGHT_EYEBROW"))}
44
+ ${l("vec2","leftEyeAt","vec2 pos",O("LEFT_EYE"))}
45
+ ${l("vec2","rightEyeAt","vec2 pos",O("RIGHT_EYE"))}
46
+ ${l("vec2","lipsAt","vec2 pos",O("OUTER_MOUTH"))}
47
+ ${l("vec2","outerMouthAt","vec2 pos",O("OUTER_MOUTH","INNER_MOUTH"))}
48
+ ${l("vec2","innerMouthAt","vec2 pos",O("INNER_MOUTH"))}
49
+ ${l("vec2","faceOvalAt","vec2 pos",K(.75))}
50
+ ${l("vec2","faceAt","vec2 pos",K(.25))}
51
+ ${l("vec2","eyeAt","vec2 pos",V("leftEyeAt","rightEyeAt"))}
52
+ ${l("vec2","eyebrowAt","vec2 pos",V("leftEyebrowAt","rightEyebrowAt"))}
53
+ ${l("float","inEyebrow","vec2 pos",`return eyebrowAt(pos${R}).x;`)}
54
+ ${l("float","inEye","vec2 pos",`return eyeAt(pos${R}).x;`)}
55
+ ${l("float","inOuterMouth","vec2 pos",`return outerMouthAt(pos${R}).x;`)}
56
+ ${l("float","inInnerMouth","vec2 pos",`return innerMouthAt(pos${R}).x;`)}
57
+ ${l("float","inLips","vec2 pos",`return lipsAt(pos${R}).x;`)}
58
+ ${l("float","inFace","vec2 pos",`return faceAt(pos${R}).x;`)}`)}}var Fe=_e;export{Fe as default};
87
59
  //# sourceMappingURL=face.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { FaceLandmarker, FaceLandmarkerResult, 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 STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst LANDMARKS_TEXTURE_WIDTH = 512;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst OUTER_MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_MOUTH: OUTER_MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\nconst REGION_NAMES = [\n\t'BACKGROUND',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'OUTER_MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst nFaceRegions = REGION_NAMES.length - 1;\nconst RED_CHANNEL_VALUES = Object.fromEntries(REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\n\nfunction fanTriangulate(indices: readonly number[]): number[] {\n\tconst tris: number[] = [];\n\tfor (let i = 1; i < indices.length - 1; ++i) {\n\t\ttris.push(indices[0], indices[i], indices[i + 1]);\n\t}\n\treturn tris;\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 { injectGLSL, gl, emitHook } = context;\n\n\t\tlet faceLandmarker: FaceLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tlet runningMode: 'IMAGE' | 'VIDEO' = 'VIDEO';\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxFaces = options?.maxFaces ?? 1;\n\n\t\tlet landmarksTextureHeight = 0;\n\t\tlet landmarksDataArray: Float32Array | null = null;\n\n\t\tconst mediaPipeCanvas = new OffscreenCanvas(1, 1);\n\t\tconst maskCanvas = new OffscreenCanvas(1, 1);\n\n\t\t// WebGL resources for triangle rendering (no antialiasing).\n\t\tlet maskGl: WebGL2RenderingContext | null = null;\n\t\tlet maskProgram: WebGLProgram | null = null;\n\t\tlet positionBuffer: WebGLBuffer | null = null;\n\t\tlet colorLocation: WebGLUniformLocation | null = null;\n\n\t\tconst regionTriangles: Record<string, number[]> = {\n\t\t\tLEFT_EYEBROW: fanTriangulate(LEFT_EYEBROW_INDICES),\n\t\t\tRIGHT_EYEBROW: fanTriangulate(RIGHT_EYEBROW_INDICES),\n\t\t\tLEFT_EYE: fanTriangulate(LEFT_EYE_INDICES),\n\t\t\tRIGHT_EYE: fanTriangulate(RIGHT_EYE_INDICES),\n\t\t\tOUTER_MOUTH: fanTriangulate(OUTER_MOUTH_INDICES),\n\t\t\tINNER_MOUTH: fanTriangulate(INNER_MOUTH_INDICES),\n\t\t\t// Populated after FaceLandmarker loads.\n\t\t\tTESSELATION: [],\n\t\t\tOVAL: [],\n\t\t};\n\n\t\tfunction initMaskRenderer() {\n\t\t\tmaskGl = maskCanvas.getContext('webgl2', { antialias: false, preserveDrawingBuffer: true });\n\t\t\tif (!maskGl) throw new Error('Failed to get WebGL2 context for mask');\n\n\t\t\tconst vertexShader = maskGl.createShader(maskGl.VERTEX_SHADER)!;\n\t\t\tmaskGl.shaderSource(\n\t\t\t\tvertexShader,\n\t\t\t\t`#version 300 es\nin vec2 a_pos;\nvoid main() {\n\tgl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);\n}`\n\t\t\t);\n\t\t\tmaskGl.compileShader(vertexShader);\n\n\t\t\tconst fragmentShader = maskGl.createShader(maskGl.FRAGMENT_SHADER)!;\n\t\t\tmaskGl.shaderSource(\n\t\t\t\tfragmentShader,\n\t\t\t\t`#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`\n\t\t\t);\n\t\t\tmaskGl.compileShader(fragmentShader);\n\n\t\t\tmaskProgram = maskGl.createProgram()!;\n\t\t\tmaskGl.attachShader(maskProgram, vertexShader);\n\t\t\tmaskGl.attachShader(maskProgram, fragmentShader);\n\t\t\tmaskGl.linkProgram(maskProgram);\n\t\t\tmaskGl.deleteShader(vertexShader);\n\t\t\tmaskGl.deleteShader(fragmentShader);\n\n\t\t\tpositionBuffer = maskGl.createBuffer();\n\t\t\tmaskGl.bindBuffer(maskGl.ARRAY_BUFFER, positionBuffer);\n\t\t\tconst positionLocation = maskGl.getAttribLocation(maskProgram, 'a_pos');\n\t\t\tmaskGl.enableVertexAttribArray(positionLocation);\n\t\t\tmaskGl.vertexAttribPointer(positionLocation, 2, maskGl.FLOAT, false, 0, 0);\n\n\t\t\tcolorLocation = maskGl.getUniformLocation(maskProgram, 'u_color');\n\t\t\tmaskGl.useProgram(maskProgram);\n\n\t\t\t// Enable blending to handle overlapping faces (set once, never disabled).\n\t\t\tmaskGl.enable(maskGl.BLEND);\n\t\t\tmaskGl.blendEquation(maskGl.MAX);\n\t\t}\n\n\t\tfunction drawTriangles(triangleIndices: number[], faceIdx: number, r: number, g: number, b: number) {\n\t\t\tif (!maskGl || !landmarksDataArray || triangleIndices.length === 0) return;\n\n\t\t\tconst vertices = new Float32Array(triangleIndices.length * 2);\n\t\t\tfor (let i = 0; i < triangleIndices.length; ++i) {\n\t\t\t\tconst landmarkIdx = (faceIdx * LANDMARK_COUNT + triangleIndices[i]) * 4;\n\t\t\t\tvertices[i * 2] = landmarksDataArray[landmarkIdx];\n\t\t\t\tvertices[i * 2 + 1] = landmarksDataArray[landmarkIdx + 1];\n\t\t\t}\n\n\t\t\tmaskGl.bufferData(maskGl.ARRAY_BUFFER, vertices, maskGl.DYNAMIC_DRAW);\n\t\t\tmaskGl.uniform4f(colorLocation, r, g, b, 1.0);\n\t\t\tmaskGl.drawArrays(maskGl.TRIANGLES, 0, triangleIndices.length);\n\t\t}\n\n\t\tasync function initializeMediaPipe() {\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\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\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediaPipeCanvas,\n\t\t\t\t\trunningMode,\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\n\t\t\t\tconst tesselationConnections = FaceLandmarker.FACE_LANDMARKS_TESSELATION;\n\t\t\t\tregionTriangles.TESSELATION = [];\n\t\t\t\tfor (let i = 0; i < tesselationConnections.length - 2; i += 3) {\n\t\t\t\t\tregionTriangles.TESSELATION.push(\n\t\t\t\t\t\ttesselationConnections[i].start,\n\t\t\t\t\t\ttesselationConnections[i + 1].start,\n\t\t\t\t\t\ttesselationConnections[i + 2].start\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst ovalIndices = FaceLandmarker.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\t\t\tregionTriangles.OVAL = fanTriangulate(ovalIndices);\n\n\t\t\t\tinitMaskRenderer();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Failed to initialize MediaPipe:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\tconst mediaPipeInitPromise = initializeMediaPipe();\n\n\t\tfunction calculateBoundingBoxCenter(\n\t\t\tdata: Float32Array,\n\t\t\tfaceIdx: number,\n\t\t\tindices: readonly number[] | number[]\n\t\t): [number, number, 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\t\t\t\tavgZ = 0,\n\t\t\t\tavgVisibility = 0;\n\n\t\t\tfor (const idx of indices) {\n\t\t\t\tconst i = (faceIdx * LANDMARK_COUNT + idx) * 4;\n\t\t\t\tconst x = data[i],\n\t\t\t\t\ty = data[i + 1];\n\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\tavgZ += data[i + 2];\n\t\t\t\tavgVisibility += data[i + 3];\n\t\t\t}\n\t\t\treturn [(minX + maxX) / 2, (minY + maxY) / 2, avgZ / indices.length, avgVisibility / indices.length];\n\t\t}\n\n\t\tfunction updateLandmarksTexture(faces: NormalizedLandmark[][]) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst nFaces = faces.length;\n\t\t\tconst totalLandmarks = nFaces * LANDMARK_COUNT;\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst landmarks = faces[faceIdx];\n\t\t\t\tfor (let landmarkIdx = 0; landmarkIdx < STANDARD_LANDMARK_COUNT; ++landmarkIdx) {\n\t\t\t\t\tconst landmark = landmarks[landmarkIdx];\n\t\t\t\t\tconst dataIdx = (faceIdx * LANDMARK_COUNT + landmarkIdx) * 4;\n\t\t\t\t\tlandmarksDataArray[dataIdx] = landmark.x;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 1] = 1 - landmark.y;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 2] = landmark.z ?? 0;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t\t\t}\n\n\t\t\t\tconst faceCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, ALL_STANDARD_INDICES);\n\t\t\t\tlandmarksDataArray.set(faceCenter, (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4);\n\n\t\t\t\tconst mouthCenter = calculateBoundingBoxCenter(landmarksDataArray, faceIdx, INNER_MOUTH_INDICES);\n\t\t\t\tlandmarksDataArray.set(mouthCenter, (faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4);\n\t\t\t}\n\n\t\t\tconst rowsToUpdate = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures({\n\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\tisPartial: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tfunction updateMaskTexture(nFaces: number) {\n\t\t\tif (!maskGl || !landmarksDataArray) return;\n\n\t\t\t// Resize and clear.\n\t\t\tmaskCanvas.width = mediaPipeCanvas.width;\n\t\t\tmaskCanvas.height = mediaPipeCanvas.height;\n\t\t\tmaskGl.viewport(0, 0, maskCanvas.width, maskCanvas.height);\n\t\t\tmaskGl.clearColor(0, 0, 0, 0);\n\t\t\tmaskGl.clear(maskGl.COLOR_BUFFER_BIT);\n\n\t\t\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\t\t\tconst b = (faceIdx + 1) / maxFaces;\n\n\t\t\t\t// G channel: face mesh (0.5) and oval (1.0)\n\t\t\t\tdrawTriangles(regionTriangles.TESSELATION, faceIdx, 0, 0.5, b);\n\t\t\t\tdrawTriangles(regionTriangles.OVAL, faceIdx, 0, 1.0, b);\n\n\t\t\t\t// R channel: feature regions\n\t\t\t\tdrawTriangles(regionTriangles.LEFT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.LEFT_EYEBROW, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.RIGHT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYEBROW, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.OUTER_MOUTH, faceIdx, RED_CHANNEL_VALUES.OUTER_MOUTH, 0, b);\n\t\t\t\tdrawTriangles(regionTriangles.INNER_MOUTH, faceIdx, RED_CHANNEL_VALUES.INNER_MOUTH, 0, b);\n\t\t\t}\n\n\t\t\tshaderPad.updateTextures({ u_faceMask: maskCanvas });\n\t\t}\n\n\t\tfunction processFaces(result: FaceLandmarkerResult) {\n\t\t\tif (!result.faceLandmarks || !landmarksDataArray) return;\n\n\t\t\tconst nFaces = result.faceLandmarks.length;\n\t\t\tupdateLandmarksTexture(result.faceLandmarks);\n\t\t\tupdateMaskTexture(nFaces);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\t\t\temitHook('face:result', result);\n\t\t}\n\n\t\t// `detectFaces` may be called multiple times before MediaPipe is\n\t\t// initialized. This ensures we only process the last call.\n\t\tlet nDetectionCalls = 0;\n\t\tasync function detectFaces(source: TextureSource) {\n\t\t\tconst callOrder = ++nDetectionCalls;\n\t\t\tawait mediaPipeInitPromise;\n\t\t\tif (callOrder !== nDetectionCalls || !faceLandmarker) return;\n\n\t\t\tconst previousSource = textureSources.get(textureName);\n\t\t\tif (previousSource !== source) lastVideoTime = -1;\n\t\t\ttextureSources.set(textureName, source);\n\n\t\t\ttry {\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (runningMode !== requiredMode) {\n\t\t\t\t\trunningMode = requiredMode;\n\t\t\t\t\tawait faceLandmarker.setOptions({ runningMode });\n\t\t\t\t}\n\n\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\tprocessFaces(faceLandmarker.detectForVideo(source, performance.now()));\n\t\t\t\t\t}\n\t\t\t\t} else if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement) {\n\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\tprocessFaces(faceLandmarker.detect(source));\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Face Plugin] Detection error:', error);\n\t\t\t}\n\t\t}\n\n\t\tshaderPad.on('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_faceMask', maskCanvas, {\n\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t});\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\n\t\t\tconst totalLandmarks = maxFaces * LANDMARK_COUNT;\n\t\t\tlandmarksTextureHeight = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tlandmarksDataArray = new Float32Array(LANDMARKS_TEXTURE_WIDTH * landmarksTextureHeight * 4);\n\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{ data: landmarksDataArray, width: LANDMARKS_TEXTURE_WIDTH, height: landmarksTextureHeight },\n\t\t\t\t{ internalFormat: gl.RGBA32F, type: gl.FLOAT, minFilter: gl.NEAREST, magFilter: gl.NEAREST }\n\t\t\t);\n\t\t\tawait mediaPipeInitPromise;\n\t\t\temitHook('face:ready');\n\t\t});\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName) detectFaces(source);\n\t\t});\n\n\t\tshaderPad.on('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tconst source = updates[textureName];\n\t\t\tif (source) detectFaces(source);\n\t\t});\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tfaceLandmarker?.close();\n\t\t\tfaceLandmarker = null;\n\t\t\tif (maskGl && maskProgram) {\n\t\t\t\tmaskGl.deleteProgram(maskProgram);\n\t\t\t\tmaskGl.deleteBuffer(positionBuffer);\n\t\t\t}\n\t\t\tmaskGl = null;\n\t\t\tmaskProgram = null;\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tlandmarksDataArray = null;\n\t\t});\n\n\t\tconst checkAt = (\n\t\t\tregionMin: keyof typeof RED_CHANNEL_VALUES,\n\t\t\tregionMax: keyof typeof RED_CHANNEL_VALUES = regionMin\n\t\t) =>\n\t\t\t`vec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn (mask.r > ${(RED_CHANNEL_VALUES[regionMin] - HALF_GAP).toFixed(4)} && mask.r < ${(\n\t\t\t\tRED_CHANNEL_VALUES[regionMax] + HALF_GAP\n\t\t\t).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform sampler2D u_faceLandmarksTex;\nuniform sampler2D u_faceMask;\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\nvec4 faceLandmark(int faceIndex, int landmarkIndex) {\n\tint i = faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);\n}\n\nvec2 leftEyebrowAt(vec2 pos) {\n\t${checkAt('LEFT_EYEBROW')}\n}\n\nvec2 rightEyebrowAt(vec2 pos) {\n\t${checkAt('RIGHT_EYEBROW')}\n}\n\nvec2 leftEyeAt(vec2 pos) {\n\t${checkAt('LEFT_EYE')}\n}\n\nvec2 rightEyeAt(vec2 pos) {\n\t${checkAt('RIGHT_EYE')}\n}\n\nvec2 lipsAt(vec2 pos) {\n\t${checkAt('OUTER_MOUTH')}\n}\n\nvec2 outerMouthAt(vec2 pos) {\n\t${checkAt('OUTER_MOUTH', 'INNER_MOUTH')}\n}\n\nvec2 innerMouthAt(vec2 pos) {\n\t${checkAt('INNER_MOUTH')}\n}\n\nvec2 faceOvalAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.75 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\n// Includes face mesh and oval.\nvec2 faceAt(vec2 pos) {\n\tvec4 mask = texture(u_faceMask, pos);\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > 0.25 ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);\n}\n\nvec2 eyeAt(vec2 pos) {\n\tvec2 left = leftEyeAt(pos);\n\treturn left.x > 0.0 ? left : rightEyeAt(pos);\n}\n\nvec2 eyebrowAt(vec2 pos) {\n\tvec2 left = leftEyebrowAt(pos);\n\treturn left.x > 0.0 ? left : rightEyebrowAt(pos);\n}\n\nfloat inEyebrow(vec2 pos) { return eyebrowAt(pos).x; }\nfloat inEye(vec2 pos) { return eyeAt(pos).x; }\nfloat inOuterMouth(vec2 pos) { return outerMouthAt(pos).x; }\nfloat inInnerMouth(vec2 pos) { return innerMouthAt(pos).x; }\nfloat inLips(vec2 pos) { return lipsAt(pos).x; }\nfloat inFace(vec2 pos) { return faceAt(pos).x; }`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LANDMARKS_TEXTURE_WIDTH","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","OUTER_MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","REGION_NAMES","nFaceRegions","RED_CHANNEL_VALUES","name","HALF_GAP","fanTriangulate","indices","tris","face","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","gl","emitHook","faceLandmarker","vision","lastVideoTime","runningMode","textureSources","maxFaces","landmarksTextureHeight","landmarksDataArray","mediaPipeCanvas","maskCanvas","maskGl","maskProgram","positionBuffer","colorLocation","regionTriangles","initMaskRenderer","vertexShader","fragmentShader","positionLocation","drawTriangles","triangleIndices","faceIdx","r","g","b","vertices","landmarkIdx","initializeMediaPipe","FilesetResolver","FaceLandmarker","tesselationConnections","ovalIndices","start","error","mediaPipeInitPromise","calculateBoundingBoxCenter","data","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","x","y","updateLandmarksTexture","faces","nFaces","totalLandmarks","landmarks","landmark","dataIdx","faceCenter","mouthCenter","rowsToUpdate","updateMaskTexture","processFaces","result","nDetectionCalls","detectFaces","source","callOrder","requiredMode","updates","checkAt","regionMin","regionMax","face_default"]}
1
+ {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport {\n\tcalculateBoundingBoxCenter,\n\tgenerateGLSLFn,\n\tdummyTexture,\n\tgetSharedFileset,\n\thashOptions,\n\tisMediaPipeSource,\n\tMediaPipeSource,\n} from './mediapipe-common';\nimport type { FaceLandmarker, FaceLandmarkerResult, 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\thistory?: number;\n}\n\nconst MASK_VERTEX_SHADER = `#version 300 es\nin vec2 a_pos;\nvoid main() { gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0); }`;\nconst MASK_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`;\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst LANDMARKS_TEXTURE_WIDTH = 512;\nconst N_LANDMARK_METADATA_SLOTS = 1;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst OUTER_MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tOUTER_MOUTH: OUTER_MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\nconst REGION_NAMES = [\n\t'BACKGROUND',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'OUTER_MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst nFaceRegions = REGION_NAMES.length - 1;\nconst RED_CHANNEL_VALUES = Object.fromEntries(REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\n\nconst DEFAULT_FACE_OPTIONS: Required<Omit<FacePluginOptions, 'history'>> = {\n\tmodelPath:\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task',\n\tmaxFaces: 1,\n\tminFaceDetectionConfidence: 0.5,\n\tminFacePresenceConfidence: 0.5,\n\tminTrackingConfidence: 0.5,\n\toutputFaceBlendshapes: false,\n\toutputFacialTransformationMatrixes: false,\n};\n\nfunction fanTriangulate(indices: readonly number[]): number[] {\n\tconst tris: number[] = [];\n\tfor (let i = 1; i < indices.length - 1; ++i) {\n\t\ttris.push(indices[0], indices[i], indices[i + 1]);\n\t}\n\treturn tris;\n}\n\ntype FaceRegion = { triangles: number[]; vertices: Float32Array };\nlet faceRegions: Record<string, FaceRegion> | null = null;\nfunction initFaceRegions(LandmarkerClass: typeof FaceLandmarker): void {\n\tif (!faceRegions) {\n\t\tconst tesselationConnections = LandmarkerClass.FACE_LANDMARKS_TESSELATION;\n\t\tconst tesselation: number[] = [];\n\t\tfor (let i = 0; i < tesselationConnections.length - 2; i += 3) {\n\t\t\ttesselation.push(\n\t\t\t\ttesselationConnections[i].start,\n\t\t\t\ttesselationConnections[i + 1].start,\n\t\t\t\ttesselationConnections[i + 2].start\n\t\t\t);\n\t\t}\n\t\tconst ovalIndices = LandmarkerClass.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\tfaceRegions = Object.fromEntries(\n\t\t\tObject.entries({\n\t\t\t\tLEFT_EYEBROW: fanTriangulate(LEFT_EYEBROW_INDICES),\n\t\t\t\tRIGHT_EYEBROW: fanTriangulate(RIGHT_EYEBROW_INDICES),\n\t\t\t\tLEFT_EYE: fanTriangulate(LEFT_EYE_INDICES),\n\t\t\t\tRIGHT_EYE: fanTriangulate(RIGHT_EYE_INDICES),\n\t\t\t\tOUTER_MOUTH: fanTriangulate(OUTER_MOUTH_INDICES),\n\t\t\t\tINNER_MOUTH: fanTriangulate(INNER_MOUTH_INDICES),\n\t\t\t\tTESSELATION: tesselation,\n\t\t\t\tOVAL: fanTriangulate(ovalIndices),\n\t\t\t}).map(([key, triangles]) => [key, { triangles, vertices: new Float32Array(triangles.length * 2) }])\n\t\t);\n\t}\n}\n\ninterface Detector {\n\tlandmarker: FaceLandmarker;\n\tcanvas: OffscreenCanvas;\n\tsubscribers: Map<() => void, boolean>;\n\tmaxFaces: number;\n\tstate: {\n\t\trunningMode: 'IMAGE' | 'VIDEO';\n\t\tsource: MediaPipeSource | null;\n\t\tvideoTime: number;\n\t\tresultTimestamp: number;\n\t\tresult: FaceLandmarkerResult | null;\n\t\tpending: Promise<void>;\n\t\tnFaces: number;\n\t};\n\tlandmarks: {\n\t\tdata: Float32Array;\n\t\ttextureHeight: number;\n\t};\n\tmask: {\n\t\tcanvas: OffscreenCanvas;\n\t\tgl: WebGL2RenderingContext;\n\t\tprogram: WebGLProgram;\n\t\tpositionBuffer: WebGLBuffer;\n\t\tcolorLocation: WebGLUniformLocation;\n\t};\n}\nconst sharedDetectors = new Map<string, Detector>();\n\nfunction initMaskRenderer(detector: Detector) {\n\tconst gl = detector.mask.canvas.getContext('webgl2', { antialias: false, preserveDrawingBuffer: true })!;\n\n\tconst vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n\tgl.shaderSource(vertexShader, MASK_VERTEX_SHADER);\n\tgl.compileShader(vertexShader);\n\n\tconst fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n\tgl.shaderSource(fragmentShader, MASK_FRAGMENT_SHADER);\n\tgl.compileShader(fragmentShader);\n\n\tconst program = gl.createProgram()!;\n\tgl.attachShader(program, vertexShader);\n\tgl.attachShader(program, fragmentShader);\n\tgl.linkProgram(program);\n\tgl.deleteShader(vertexShader);\n\tgl.deleteShader(fragmentShader);\n\n\tconst positionBuffer = gl.createBuffer()!;\n\tgl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n\tconst positionLocation = gl.getAttribLocation(program, 'a_pos');\n\tgl.enableVertexAttribArray(positionLocation);\n\tgl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);\n\n\tconst colorLocation = gl.getUniformLocation(program, 'u_color')!;\n\tgl.useProgram(program);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.MAX);\n\n\tdetector.mask = { ...detector.mask, gl, program, positionBuffer, colorLocation };\n}\n\nfunction drawTriangles(detector: Detector, faceRegion: FaceRegion, faceIdx: number, r: number, g: number, b: number) {\n\tconst { triangles, vertices } = faceRegion;\n\tconst {\n\t\tmask: { gl, colorLocation },\n\t\tlandmarks,\n\t} = detector;\n\tconst { data: landmarksDataArray } = landmarks;\n\n\tfor (let i = 0; i < triangles.length; ++i) {\n\t\tconst landmarkIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + triangles[i]) * 4;\n\t\tvertices[i * 2] = landmarksDataArray[landmarkIdx];\n\t\tvertices[i * 2 + 1] = landmarksDataArray[landmarkIdx + 1];\n\t}\n\n\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW);\n\tgl.uniform4f(colorLocation, r, g, b, 1.0);\n\tgl.drawArrays(gl.TRIANGLES, 0, triangles.length);\n}\n\nfunction updateLandmarksData(detector: Detector, faces: NormalizedLandmark[][]) {\n\tconst data = detector.landmarks.data;\n\tconst nFaces = faces.length;\n\tdata[0] = nFaces;\n\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst landmarks = faces[faceIdx];\n\t\tfor (let landmarkIdx = 0; landmarkIdx < STANDARD_LANDMARK_COUNT; ++landmarkIdx) {\n\t\t\tconst landmark = landmarks[landmarkIdx];\n\t\t\tconst dataIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + landmarkIdx) * 4;\n\t\t\tdata[dataIdx] = landmark.x;\n\t\t\tdata[dataIdx + 1] = 1 - landmark.y;\n\t\t\tdata[dataIdx + 2] = landmark.z ?? 0;\n\t\t\tdata[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t}\n\n\t\tconst faceCenter = calculateBoundingBoxCenter(\n\t\t\tdata,\n\t\t\tfaceIdx,\n\t\t\tALL_STANDARD_INDICES,\n\t\t\tLANDMARK_COUNT,\n\t\t\tN_LANDMARK_METADATA_SLOTS\n\t\t);\n\t\tdata.set(faceCenter, (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4);\n\n\t\tconst mouthCenter = calculateBoundingBoxCenter(data, faceIdx, INNER_MOUTH_INDICES, LANDMARK_COUNT, 1);\n\t\tdata.set(\n\t\t\tmouthCenter,\n\t\t\t(N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4\n\t\t);\n\t}\n\n\tdetector.state.nFaces = nFaces;\n}\n\nfunction updateMaskCanvas(detector: Detector) {\n\tif (!faceRegions) return;\n\tconst {\n\t\tmask,\n\t\tcanvas,\n\t\tmaxFaces,\n\t\tstate: { nFaces },\n\t} = detector;\n\tconst { gl: maskGl, canvas: maskCanvas } = mask;\n\n\tmaskCanvas.width = canvas.width;\n\tmaskCanvas.height = canvas.height;\n\tmaskGl.viewport(0, 0, maskCanvas.width, maskCanvas.height);\n\tmaskGl.clearColor(0, 0, 0, 0);\n\tmaskGl.clear(maskGl.COLOR_BUFFER_BIT);\n\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst b = (faceIdx + 1) / maxFaces;\n\n\t\t// G channel: face mesh (0.5) and oval (1.0)\n\t\tdrawTriangles(detector, faceRegions.TESSELATION, faceIdx, 0, 0.5, b);\n\t\tdrawTriangles(detector, faceRegions.OVAL, faceIdx, 0, 1.0, b);\n\n\t\t// R channel: feature regions\n\t\tdrawTriangles(detector, faceRegions.LEFT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.LEFT_EYEBROW, 0, b);\n\t\tdrawTriangles(detector, faceRegions.RIGHT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYEBROW, 0, b);\n\t\tdrawTriangles(detector, faceRegions.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, 0, b);\n\t\tdrawTriangles(detector, faceRegions.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, 0, b);\n\t\tdrawTriangles(detector, faceRegions.OUTER_MOUTH, faceIdx, RED_CHANNEL_VALUES.OUTER_MOUTH, 0, b);\n\t\tdrawTriangles(detector, faceRegions.INNER_MOUTH, faceIdx, RED_CHANNEL_VALUES.INNER_MOUTH, 0, b);\n\t}\n}\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options: { history, ...mediapipeOptions } = {} } = config;\n\tconst options = { ...DEFAULT_FACE_OPTIONS, ...mediapipeOptions };\n\tconst optionsKey = hashOptions({ ...options, textureName });\n\n\tconst nLandmarksMax = options.maxFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\tconst textureHeight = Math.ceil(nLandmarksMax / LANDMARKS_TEXTURE_WIDTH);\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl, emitHook } = context;\n\n\t\tconst existingDetector = sharedDetectors.get(optionsKey);\n\t\tconst landmarksData =\n\t\t\texistingDetector?.landmarks.data ?? new Float32Array(LANDMARKS_TEXTURE_WIDTH * textureHeight * 4);\n\t\tconst maskCanvas = existingDetector?.mask.canvas ?? new OffscreenCanvas(1, 1);\n\t\tlet detector: Detector | null = null;\n\t\tlet skipHistoryWrite = false;\n\n\t\tfunction onResult() {\n\t\t\tif (!detector) return;\n\t\t\tconst nFaces = detector.state.nFaces;\n\t\t\tconst nSlots = nFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\t\t\tconst rowsToUpdate = Math.ceil(nSlots / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures(\n\t\t\t\t{\n\t\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\t\tdata: detector.landmarks.data,\n\t\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\t\tisPartial: true,\n\t\t\t\t\t},\n\t\t\t\t\tu_faceMask: detector.mask.canvas,\n\t\t\t\t},\n\t\t\t\t{ skipHistoryWrite }\n\t\t\t);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\t\t\temitHook('face:result', detector.state.result);\n\t\t}\n\n\t\tasync function initializeDetector() {\n\t\t\tif (sharedDetectors.has(optionsKey)) {\n\t\t\t\tdetector = sharedDetectors.get(optionsKey)!;\n\t\t\t} else {\n\t\t\t\tconst [mediaPipe, { FaceLandmarker }] = await Promise.all([\n\t\t\t\t\tgetSharedFileset(),\n\t\t\t\t\timport('@mediapipe/tasks-vision'),\n\t\t\t\t]);\n\n\t\t\t\tconst mediapipeCanvas = new OffscreenCanvas(1, 1);\n\t\t\t\tconst faceLandmarker = await FaceLandmarker.createFromOptions(mediaPipe, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options.modelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediapipeCanvas,\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options.maxFaces,\n\t\t\t\t\tminFaceDetectionConfidence: options.minFaceDetectionConfidence,\n\t\t\t\t\tminFacePresenceConfidence: options.minFacePresenceConfidence,\n\t\t\t\t\tminTrackingConfidence: options.minTrackingConfidence,\n\t\t\t\t\toutputFaceBlendshapes: options.outputFaceBlendshapes,\n\t\t\t\t\toutputFacialTransformationMatrixes: options.outputFacialTransformationMatrixes,\n\t\t\t\t});\n\n\t\t\t\tdetector = {\n\t\t\t\t\tlandmarker: faceLandmarker,\n\t\t\t\t\tcanvas: mediapipeCanvas,\n\t\t\t\t\tsubscribers: new Map(),\n\t\t\t\t\tmaxFaces: options.maxFaces,\n\t\t\t\t\tstate: {\n\t\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\tvideoTime: -1,\n\t\t\t\t\t\tresultTimestamp: 0,\n\t\t\t\t\t\tresult: null,\n\t\t\t\t\t\tpending: Promise.resolve(),\n\t\t\t\t\t\tnFaces: 0,\n\t\t\t\t\t},\n\t\t\t\t\tlandmarks: {\n\t\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\t\ttextureHeight,\n\t\t\t\t\t},\n\t\t\t\t\tmask: {\n\t\t\t\t\t\tcanvas: maskCanvas,\n\t\t\t\t\t} as Detector['mask'],\n\t\t\t\t};\n\n\t\t\t\tinitFaceRegions(FaceLandmarker);\n\t\t\t\tinitMaskRenderer(detector);\n\t\t\t\tsharedDetectors.set(optionsKey, detector);\n\t\t\t}\n\n\t\t\tdetector.subscribers.set(onResult, false);\n\t\t}\n\t\tconst initPromise = initializeDetector();\n\n\t\tlet nDetectionCalls = 0;\n\t\tasync function detectFaces(source: MediaPipeSource) {\n\t\t\tconst now = performance.now();\n\t\t\tconst callOrder = ++nDetectionCalls;\n\t\t\tawait initPromise;\n\t\t\tif (!detector) return;\n\n\t\t\tdetector.state.pending = detector.state.pending.then(async () => {\n\t\t\t\tif (callOrder !== nDetectionCalls || !detector) return;\n\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (detector.state.runningMode !== requiredMode) {\n\t\t\t\t\tdetector.state.runningMode = requiredMode;\n\t\t\t\t\tawait detector.landmarker.setOptions({ runningMode: requiredMode });\n\t\t\t\t}\n\n\t\t\t\tlet shouldDetect = false;\n\n\t\t\t\tif (source !== detector.state.source) {\n\t\t\t\t\tdetector.state.source = source;\n\t\t\t\t\tdetector.state.videoTime = -1;\n\t\t\t\t\tshouldDetect = true;\n\t\t\t\t} else if (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.currentTime !== detector.state.videoTime) {\n\t\t\t\t\t\tdetector.state.videoTime = source.currentTime;\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t} else if (!(source instanceof HTMLImageElement)) {\n\t\t\t\t\tif (now - detector.state.resultTimestamp > 2) {\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldDetect) {\n\t\t\t\t\tlet result: FaceLandmarkerResult | undefined;\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\t\tresult = detector.landmarker.detectForVideo(source, now);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\t\tresult = detector.landmarker.detect(source);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tdetector.state.resultTimestamp = now;\n\t\t\t\t\t\tdetector.state.result = result;\n\t\t\t\t\t\tupdateLandmarksData(detector, result.faceLandmarks);\n\t\t\t\t\t\tupdateMaskCanvas(detector);\n\t\t\t\t\t\tfor (const cb of detector.subscribers.keys()) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (detector.state.result && !detector.subscribers.get(onResult)) {\n\t\t\t\t\tonResult();\n\t\t\t\t\tdetector.subscribers.set(onResult, true);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait detector.state.pending;\n\t\t}\n\n\t\tshaderPad.on('init', () => {\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', options.maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{ data: landmarksData, width: LANDMARKS_TEXTURE_WIDTH, height: textureHeight },\n\t\t\t\t{ internalFormat: gl.RGBA32F, type: gl.FLOAT, minFilter: gl.NEAREST, magFilter: gl.NEAREST, history }\n\t\t\t);\n\t\t\tshaderPad.initializeTexture('u_faceMask', maskCanvas, {\n\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t\thistory,\n\t\t\t});\n\t\t\tinitPromise.then(() => emitHook('face:ready'));\n\t\t});\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName && isMediaPipeSource(source)) detectFaces(source);\n\t\t});\n\n\t\tshaderPad.on(\n\t\t\t'updateTextures',\n\t\t\t(updates: Record<string, TextureSource>, options?: { skipHistoryWrite?: boolean }) => {\n\t\t\t\tconst source = updates[textureName];\n\t\t\t\tif (isMediaPipeSource(source)) {\n\t\t\t\t\tskipHistoryWrite = options?.skipHistoryWrite ?? false;\n\t\t\t\t\tdetectFaces(source);\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tif (detector) {\n\t\t\t\tdetector.subscribers.delete(onResult);\n\t\t\t\tif (detector.subscribers.size === 0) {\n\t\t\t\t\tdetector.landmarker.close();\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.program);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.positionBuffer);\n\t\t\t\t\tsharedDetectors.delete(optionsKey);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdetector = null;\n\t\t});\n\n\t\tconst { fn, historyParams } = generateGLSLFn(history);\n\t\tconst sampleMask = history ? `_sampleFaceMask(pos, framesAgo)` : `texture(u_faceMask, pos)`;\n\n\t\tconst checkAt = (\n\t\t\tregionMin: keyof typeof RED_CHANNEL_VALUES,\n\t\t\tregionMax: keyof typeof RED_CHANNEL_VALUES = regionMin\n\t\t) =>\n\t\t\t`vec4 mask = ${sampleMask};\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn (mask.r > ${(RED_CHANNEL_VALUES[regionMin] - HALF_GAP).toFixed(4)} && mask.r < ${(\n\t\t\t\tRED_CHANNEL_VALUES[regionMax] + HALF_GAP\n\t\t\t).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;\n\n\t\tconst checkMaskG = (threshold: number) =>\n\t\t\t`vec4 mask = ${sampleMask};\n\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > ${threshold.toFixed(2)} ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`;\n\n\t\tconst combineLeftRight = (leftFn: string, rightFn: string) =>\n\t\t\t`vec2 left = ${leftFn}(pos${historyParams});\n\treturn left.x > 0.0 ? left : ${rightFn}(pos${historyParams});`;\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform highp sampler2D${history ? 'Array' : ''} u_faceLandmarksTex;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceLandmarksTexFrameOffset;`\n\t\t\t\t: ''\n\t\t}\nuniform sampler2D${history ? 'Array' : ''} u_faceMask;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceMaskFrameOffset;`\n\t\t\t\t: ''\n\t\t}\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\n${fn(\n\t'int',\n\t'nFacesAt',\n\t'',\n\thistory\n\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history}) % ${history};\n\treturn int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`\n\t\t: `\n\treturn int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`\n)}\n${fn(\n\t'vec4',\n\t'faceLandmark',\n\t'int faceIndex, int landmarkIndex',\n\t`int i = ${N_LANDMARK_METADATA_SLOTS} + faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};${\n\t\thistory\n\t\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history}) % ${history};\n\treturn texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`\n\t\t\t: `\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`\n\t}`\n)}\n${\n\thistory\n\t\t? `\nvec4 _sampleFaceMask(vec2 pos, int framesAgo) {\n\tint layer = (u_faceMaskFrameOffset - framesAgo + ${history}) % ${history};\n\treturn texture(u_faceMask, vec3(pos, float(layer)));\n}\n`\n\t\t: ''\n}\n${fn('vec2', 'leftEyebrowAt', 'vec2 pos', checkAt('LEFT_EYEBROW'))}\n${fn('vec2', 'rightEyebrowAt', 'vec2 pos', checkAt('RIGHT_EYEBROW'))}\n${fn('vec2', 'leftEyeAt', 'vec2 pos', checkAt('LEFT_EYE'))}\n${fn('vec2', 'rightEyeAt', 'vec2 pos', checkAt('RIGHT_EYE'))}\n${fn('vec2', 'lipsAt', 'vec2 pos', checkAt('OUTER_MOUTH'))}\n${fn('vec2', 'outerMouthAt', 'vec2 pos', checkAt('OUTER_MOUTH', 'INNER_MOUTH'))}\n${fn('vec2', 'innerMouthAt', 'vec2 pos', checkAt('INNER_MOUTH'))}\n${fn('vec2', 'faceOvalAt', 'vec2 pos', checkMaskG(0.75))}\n${fn('vec2', 'faceAt', 'vec2 pos', checkMaskG(0.25))}\n${fn('vec2', 'eyeAt', 'vec2 pos', combineLeftRight('leftEyeAt', 'rightEyeAt'))}\n${fn('vec2', 'eyebrowAt', 'vec2 pos', combineLeftRight('leftEyebrowAt', 'rightEyebrowAt'))}\n${fn('float', 'inEyebrow', 'vec2 pos', `return eyebrowAt(pos${historyParams}).x;`)}\n${fn('float', 'inEye', 'vec2 pos', `return eyeAt(pos${historyParams}).x;`)}\n${fn('float', 'inOuterMouth', 'vec2 pos', `return outerMouthAt(pos${historyParams}).x;`)}\n${fn('float', 'inInnerMouth', 'vec2 pos', `return innerMouthAt(pos${historyParams}).x;`)}\n${fn('float', 'inLips', 'vec2 pos', `return lipsAt(pos${historyParams}).x;`)}\n${fn('float', 'inFace', 'vec2 pos', `return faceAt(pos${historyParams}).x;`)}`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["MASK_VERTEX_SHADER","MASK_FRAGMENT_SHADER","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LANDMARKS_TEXTURE_WIDTH","N_LANDMARK_METADATA_SLOTS","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","OUTER_MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","REGION_NAMES","nFaceRegions","RED_CHANNEL_VALUES","name","HALF_GAP","DEFAULT_FACE_OPTIONS","fanTriangulate","indices","tris","faceRegions","initFaceRegions","LandmarkerClass","tesselationConnections","tesselation","ovalIndices","start","key","triangles","sharedDetectors","initMaskRenderer","detector","gl","vertexShader","fragmentShader","program","positionBuffer","positionLocation","colorLocation","drawTriangles","faceRegion","faceIdx","r","g","b","vertices","landmarks","landmarksDataArray","landmarkIdx","updateLandmarksData","faces","data","nFaces","landmark","dataIdx","faceCenter","calculateBoundingBoxCenter","mouthCenter","updateMaskCanvas","mask","canvas","maxFaces","maskGl","maskCanvas","face","config","textureName","history","mediapipeOptions","options","optionsKey","hashOptions","nLandmarksMax","textureHeight","shaderPad","context","injectGLSL","emitHook","existingDetector","landmarksData","skipHistoryWrite","onResult","nSlots","rowsToUpdate","initializeDetector","mediaPipe","FaceLandmarker","getSharedFileset","mediapipeCanvas","initPromise","nDetectionCalls","detectFaces","source","now","callOrder","requiredMode","shouldDetect","result","cb","isMediaPipeSource","updates","fn","historyParams","generateGLSLFn","sampleMask","checkAt","regionMin","regionMax","checkMaskG","threshold","combineLeftRight","leftFn","rightFn","face_default"]}
@@ -6,6 +6,7 @@ interface HandsPluginOptions {
6
6
  minHandDetectionConfidence?: number;
7
7
  minHandPresenceConfidence?: number;
8
8
  minTrackingConfidence?: number;
9
+ history?: number;
9
10
  }
10
11
  declare function hands(config: {
11
12
  textureName: string;
@@ -6,6 +6,7 @@ interface HandsPluginOptions {
6
6
  minHandDetectionConfidence?: number;
7
7
  minHandPresenceConfidence?: number;
8
8
  minTrackingConfidence?: number;
9
+ history?: number;
9
10
  }
10
11
  declare function hands(config: {
11
12
  textureName: string;
@@ -1,20 +1,24 @@
1
- "use strict";var Y=Object.create;var C=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var W=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty;var q=(e,t)=>{for(var i in t)C(e,i,{get:t[i],enumerable:!0})},z=(e,t,i,L)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of j(t))!Z.call(e,r)&&r!==i&&C(e,r,{get:()=>t[r],enumerable:!(L=$(t,r))||L.enumerable});return e};var J=(e,t,i)=>(i=e!=null?Y(W(e)):{},z(t||!e||!e.__esModule?C(i,"default",{value:e,enumerable:!0}):i,e)),Q=e=>z(C({},"__esModule",{value:!0}),e);var rn={};q(rn,{default:()=>an});module.exports=Q(rn);var w=21,nn=1,g=w+nn,en=[0,0,5,9,13,17];function tn(e){let{textureName:t,options:i}=e,L="https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task";return function(r,U){let{injectGLSL:V,gl:y,emitHook:R}=U,m=null,A=null,_=-1,M="VIDEO",N=new Map,E=i?.maxHands??2,c=512,S=0,o=null;async function G(){try{let{FilesetResolver:n,HandLandmarker:a}=await import("@mediapipe/tasks-vision");A=await n.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),m=await a.createFromOptions(A,{baseOptions:{modelAssetPath:i?.modelPath||L,delegate:"GPU"},canvas:new OffscreenCanvas(1,1),runningMode:M,numHands:i?.maxHands??2,minHandDetectionConfidence:i?.minHandDetectionConfidence??.5,minHandPresenceConfidence:i?.minHandPresenceConfidence??.5,minTrackingConfidence:i?.minTrackingConfidence??.5})}catch(n){throw console.error("[Hands Plugin] Failed to initialize MediaPipe:",n),n}}let v=G();function K(n,a,l){let s=1/0,H=-1/0,d=1/0,T=-1/0,p=0,u=0;for(let B of l){let I=(a*g+B)*4,F=n[I],P=n[I+1];s=Math.min(s,F),H=Math.max(H,F),d=Math.min(d,P),T=Math.max(T,P),p+=n[I+2],u+=n[I+3]}let f=(s+H)/2,x=(d+T)/2,k=p/l.length,h=u/l.length;return[f,x,k,h]}function X(n,a){if(!o)return;let l=n.length,s=l*g;for(let d=0;d<l;++d){let T=n[d],p=a[d]?.[0]?.categoryName==="Right";for(let x=0;x<w;++x){let k=T[x],h=(d*g+x)*4;o[h]=k.x,o[h+1]=1-k.y,o[h+2]=k.z??0,o[h+3]=p?1:0}let u=K(o,d,en),f=(d*g+w)*4;o[f]=u[0],o[f+1]=u[1],o[f+2]=u[2],o[f+3]=p?1:0}let H=Math.ceil(s/c);r.updateTextures({u_handLandmarksTex:{data:o,width:c,height:H,isPartial:!0}})}function D(n){if(!n.landmarks||!o)return;let a=n.landmarks.length;X(n.landmarks,n.handedness),r.updateUniforms({u_nHands:a}),R("hands:result",n)}r.on("init",async()=>{r.initializeUniform("u_maxHands","int",E),r.initializeUniform("u_nHands","int",0);let n=E*g;S=Math.ceil(n/c);let a=c*S*4;o=new Float32Array(a),r.initializeTexture("u_handLandmarksTex",{data:o,width:c,height:S},{internalFormat:y.RGBA32F,type:y.FLOAT,minFilter:y.NEAREST,magFilter:y.NEAREST}),await v,R("hands:ready")}),r.on("initializeTexture",(n,a)=>{n===t&&b(a)}),r.on("updateTextures",n=>{let a=n[t];a&&b(a)});let O=0;async function b(n){let a=++O;if(await v,a!==O||!m)return;N.get(t)!==n&&(_=-1),N.set(t,n);try{let s=n instanceof HTMLVideoElement?"VIDEO":"IMAGE";if(M!==s&&(M=s,await m.setOptions({runningMode:M})),n instanceof HTMLVideoElement){if(n.videoWidth===0||n.videoHeight===0||n.readyState<2)return;n.currentTime!==_&&(_=n.currentTime,D(m.detectForVideo(n,performance.now())))}else if(n instanceof HTMLImageElement||n instanceof HTMLCanvasElement){if(n.width===0||n.height===0)return;D(m.detect(n))}}catch(s){console.error("[Hands Plugin] Detection error:",s)}}r.on("destroy",()=>{m&&(m.close(),m=null),A=null,N.clear(),o=null}),V(`
1
+ "use strict";var B=Object.create;var S=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var X=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var Y=(e,o)=>{for(var a in o)S(e,a,{get:o[a],enumerable:!0})},R=(e,o,a,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of j(o))!q.call(e,t)&&t!==a&&S(e,t,{get:()=>o[t],enumerable:!(s=W(o,t))||s.enumerable});return e};var w=(e,o,a)=>(a=e!=null?B(X(e)):{},R(o||!e||!e.__esModule?S(a,"default",{value:e,enumerable:!0}):a,e)),J=e=>R(S({},"__esModule",{value:!0}),e);var ie={};Y(ie,{default:()=>ae});module.exports=J(ie);var se={data:new Uint8Array(4),width:1,height:1};function $(e){return e instanceof HTMLVideoElement||e instanceof HTMLImageElement||e instanceof HTMLCanvasElement||e instanceof OffscreenCanvas}function V(e){return JSON.stringify(e,Object.keys(e).sort())}function U(e,o,a,s,t=0){let i=1/0,u=-1/0,m=1/0,d=-1/0,f=0,g=0;for(let h of a){let c=(t+o*s+h)*4,D=e[c],L=e[c+1];i=Math.min(i,D),u=Math.max(u,D),m=Math.min(m,L),d=Math.max(d,L),f+=e[c+2],g+=e[c+3]}return[(i+u)/2,(m+d)/2,f/a.length,g/a.length]}var O=null;function z(){return O||(O=import("@mediapipe/tasks-vision").then(({FilesetResolver:e})=>e.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.22-rc.20250304/wasm"))),O}function K(e){return{historyParams:e?", framesAgo":"",fn:e?(s,t,i,u)=>{let m=i.replace(/\w+ /g,""),d=i?`${i}, int framesAgo`:"int framesAgo",f=m?`${m}, 0`:"0";return`${s} ${t}(${d}) {
2
+ ${u}
3
+ }
4
+ ${s} ${t}(${i}) { return ${t}(${f}); }`}:(s,t,i,u)=>`${s} ${t}(${i}) {
5
+ ${u}
6
+ }`}}var C=21,Z=1,H=C+Z,Q=[0,0,5,9,13,17],p=512,k=1,ee={modelPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",maxHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5},b=new Map;function ne(e,o,a){let s=e.landmarks.data,t=o.length;s[0]=t;for(let i=0;i<t;++i){let u=o[i],m=a[i]?.[0]?.categoryName==="Right";for(let g=0;g<C;++g){let h=u[g],c=(k+i*H+g)*4;s[c]=h.x,s[c+1]=1-h.y,s[c+2]=h.z??0,s[c+3]=m?1:0}let d=U(s,i,Q,H,k),f=(k+i*H+C)*4;s[f]=d[0],s[f+1]=d[1],s[f+2]=d[2],s[f+3]=m?1:0}e.state.nHands=t}function te(e){let{textureName:o,options:{history:a,...s}={}}=e,t={...ee,...s},i=V({...t,textureName:o}),u=t.maxHands*H+k,m=Math.ceil(u/p);return function(d,f){let{injectGLSL:g,gl:h,emitHook:c}=f,L=b.get(i)?.landmarks.data??new Float32Array(p*m*4),n=null,I=!1;function y(){if(!n)return;let{nHands:r}=n.state,l=r*H+k,x=Math.ceil(l/p);d.updateTextures({u_handLandmarksTex:{data:n.landmarks.data,width:p,height:x,isPartial:!0}},{skipHistoryWrite:I}),d.updateUniforms({u_nHands:r}),c("hands:result",n.state.result)}async function G(){if(b.has(i))n=b.get(i);else{let[r,{HandLandmarker:l}]=await Promise.all([z(),import("@mediapipe/tasks-vision")]),x=new OffscreenCanvas(1,1);n={landmarker:await l.createFromOptions(r,{baseOptions:{modelAssetPath:t.modelPath,delegate:"GPU"},canvas:x,runningMode:"VIDEO",numHands:t.maxHands,minHandDetectionConfidence:t.minHandDetectionConfidence,minHandPresenceConfidence:t.minHandPresenceConfidence,minTrackingConfidence:t.minTrackingConfidence}),canvas:x,subscribers:new Map,maxHands:t.maxHands,state:{runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nHands:0},landmarks:{data:L,textureHeight:m}},b.set(i,n)}n.subscribers.set(y,!1)}let v=G();d.on("init",()=>{d.initializeUniform("u_maxHands","int",t.maxHands),d.initializeUniform("u_nHands","int",0),d.initializeTexture("u_handLandmarksTex",{data:L,width:p,height:m},{internalFormat:h.RGBA32F,type:h.FLOAT,minFilter:h.NEAREST,magFilter:h.NEAREST,history:a}),v.then(()=>c("hands:ready"))}),d.on("initializeTexture",(r,l)=>{r===o&&$(l)&&E(l)}),d.on("updateTextures",(r,l)=>{let x=r[o];$(x)&&(I=l?.skipHistoryWrite??!1,E(x))});let P=0;async function E(r){let l=performance.now(),x=++P;await v,n&&(n.state.pending=n.state.pending.then(async()=>{if(x!==P||!n)return;let A=r instanceof HTMLVideoElement?"VIDEO":"IMAGE";n.state.runningMode!==A&&(n.state.runningMode=A,await n.landmarker.setOptions({runningMode:A}));let _=!1;if(r!==n.state.source?(n.state.source=r,n.state.videoTime=-1,_=!0):r instanceof HTMLVideoElement?r.currentTime!==n.state.videoTime&&(n.state.videoTime=r.currentTime,_=!0):r instanceof HTMLImageElement||l-n.state.resultTimestamp>2&&(_=!0),_){let T;if(r instanceof HTMLVideoElement){if(r.videoWidth===0||r.videoHeight===0||r.readyState<2)return;T=n.landmarker.detectForVideo(r,l)}else{if(r.width===0||r.height===0)return;T=n.landmarker.detect(r)}if(T){n.state.resultTimestamp=l,n.state.result=T,ne(n,T.landmarks,T.handedness);for(let F of n.subscribers.keys())F(),n.subscribers.set(F,!0)}}else n.state.result&&!n.subscribers.get(y)&&(y(),n.subscribers.set(y,!0))}),await n.state.pending)}d.on("destroy",()=>{n&&(n.subscribers.delete(y),n.subscribers.size===0&&(n.landmarker.close(),b.delete(i))),n=null});let{fn:M,historyParams:N}=K(a);g(`
2
7
  uniform int u_maxHands;
3
8
  uniform int u_nHands;
4
- uniform sampler2D u_handLandmarksTex;
5
-
6
- vec4 handLandmark(int handIndex, int landmarkIndex) {
7
- int i = handIndex * ${g} + landmarkIndex;
8
- int x = i % ${c};
9
- int y = i / ${c};
10
- return texelFetch(u_handLandmarksTex, ivec2(x, y), 0);
11
- }
12
-
13
- float isRightHand(int handIndex) {
14
- return handLandmark(handIndex, 0).w;
15
- }
9
+ uniform highp sampler2D${a?"Array":""} u_handLandmarksTex;${a?`
10
+ uniform int u_handLandmarksTexFrameOffset;`:""}
16
11
 
17
- float isLeftHand(int handIndex) {
18
- return 1.0 - handLandmark(handIndex, 0).w;
19
- }`)}}var an=tn;
12
+ ${M("int","nHandsAt","",a?`
13
+ int layer = (u_handLandmarksTexFrameOffset - framesAgo + ${a}) % ${a};
14
+ return int(texelFetch(u_handLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`:`
15
+ return int(texelFetch(u_handLandmarksTex, ivec2(0, 0), 0).r + 0.5);`)}
16
+ ${M("vec4","handLandmark","int handIndex, int landmarkIndex",`int i = ${k} + handIndex * ${H} + landmarkIndex;
17
+ int x = i % ${p};
18
+ int y = i / ${p};${a?`
19
+ int layer = (u_handLandmarksTexFrameOffset - framesAgo + ${a}) % ${a};
20
+ return texelFetch(u_handLandmarksTex, ivec3(x, y, layer), 0);`:`
21
+ return texelFetch(u_handLandmarksTex, ivec2(x, y), 0);`}`)}
22
+ ${M("float","isRightHand","int handIndex",`return handLandmark(handIndex, 0${N}).w;`)}
23
+ ${M("float","isLeftHand","int handIndex",`return 1.0 - handLandmark(handIndex, 0${N}).w;`)}`)}}var ae=te;
20
24
  //# sourceMappingURL=hands.js.map