{"record":{"id":"80d8c88eb205d683","repo":"moeru-ai/airi","slug":"mmd-gaze-no-eye-bone-matched-eye-tr","errorCode":null,"errorMessage":"[mmd] gaze: no eye bone matched (左目/右目/両目); eye tracking disabled. Bones containing 目/eye:","messagePattern":"\\[mmd\\] gaze: no eye bone matched \\(左目/右目/両目\\); eye tracking disabled\\. Bones containing 目/eye:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"packages/stage-ui-mmd/src/composables/mmd/gaze.ts","lineNumber":81,"sourceCode":" */\nexport function createGazeController(mesh: SkinnedMesh): GazeController {\n  const eyeBones: GazeBone[] = []\n  const left = findBone(mesh, LEFT_EYE_NAMES)\n  const right = findBone(mesh, RIGHT_EYE_NAMES)\n  if (left)\n    eyeBones.push({ bone: left, rest: left.quaternion.clone() })\n  if (right)\n    eyeBones.push({ bone: right, rest: right.quaternion.clone() })\n\n  // Fallback: a single both-eyes bone the eyeballs are skinned to directly.\n  if (eyeBones.length === 0) {\n    const both = findBone(mesh, BOTH_EYES_NAMES)\n    if (both)\n      eyeBones.push({ bone: both, rest: both.quaternion.clone() })\n  }\n\n  if (eyeBones.length === 0) {\n    console.warn(\n      '[mmd] gaze: no eye bone matched (左目/右目/両目); eye tracking disabled. '\n      + 'Bones containing 目/eye:',\n      mesh.skeleton.bones.filter(b => /目|eye/i.test(b.name)).map(b => b.name),\n    )\n  }\n\n  const headBoneRaw = findBone(mesh, HEAD_NAMES)\n  const headBone: GazeBone | undefined = headBoneRaw\n    ? { bone: headBoneRaw, rest: headBoneRaw.quaternion.clone() }\n    : undefined\n\n  // Smoothed current aim, lerped toward the target each frame.\n  const current: GazeOffset = { x: 0, y: 0 }\n  const scratchEuler = new Euler()\n  const scratchQuat = new Quaternion()\n\n  // Idle saccade state.\n  let saccade: GazeOffset = { x: 0, y: 0 }","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui-mmd/src/composables/mmd/gaze.ts#L63-L99","documentation":"createGazeController() in packages/stage-ui-mmd/src/composables/mmd/gaze.ts builds bone-based eye tracking by looking up standard MMD eye bones: 左目/eye_L/EyeLeft/LeftEye and 右目/eye_R/EyeRight/RightEye, with 両目/'Eyes' as a single-bone fallback. If none match, it logs every bone whose name contains 目 or eye (to help identify renames) and simply runs with zero eye bones: head-only gaze and idle saccades continue, eye rotation is disabled.","triggerScenarios":"Loading a PMX whose eye bones use non-standard names (e.g. 'eyeLeft001', 'EYE.L'), whose eyeballs are skinned directly to the head bone, or whose eyes are driven only by morphs instead of bones.","commonSituations":"Western-authored or blender-exported MMD-style models that translate standard Japanese bone names; heavily modified models; models rigged for morph-only blinking where eye bones were deleted.","solutions":["Read the warn's printed bone list; if an eye bone exists under a different name, rename it to 左目/右目 in a PMX editor (PMXE/Blender+mmd_tools).","Or extend LEFT_EYE_NAMES/RIGHT_EYE_NAMES/BOTH_EYE_NAMES in gaze.ts to cover your model's naming.","If eyes are morph-driven, accept the degradation (head tracking still works) — no code change needed."],"exampleFix":"// in packages/stage-ui-mmd/src/composables/mmd/gaze.ts\n// before\nconst LEFT_EYE_NAMES = ['左目', 'eye_L', 'EyeLeft', 'LeftEye']\n\n// after (cover your model's naming)\nconst LEFT_EYE_NAMES = ['左目', 'eye_L', 'EyeLeft', 'LeftEye', 'eyeInner_L', 'EYE.L']","handlingStrategy":"validation","validationCode":"const hasEyeBones = mesh.skeleton.bones.some(b => ['左目', 'eye_L', 'EyeLeft', 'LeftEye'].includes(b.name))\n|| mesh.skeleton.bones.some(b => ['右目', 'eye_R', 'EyeRight', 'RightEye'].includes(b.name))\n|| mesh.skeleton.bones.some(b => ['両目', 'Eyes'].includes(b.name))\nif (!hasEyeBones) console.info('gaze: head-only tracking for this model')","typeGuard":"function modelSupportsEyeGaze(mesh: SkinnedMesh): boolean {\n  const names = new Set(mesh.skeleton.bones.map(b => b.name))\n  return ['左目', 'eye_L', 'EyeLeft', 'LeftEye', '右目', 'eye_R', 'EyeRight', 'RightEye', '両目', 'Eyes'].some(n => names.has(n))\n}","tryCatchPattern":null,"preventionTips":["Validate PMX assets in onboarding: warn users when standard eye bones are absent.","Keep the bone-name candidate lists in gaze.ts updated as you encounter new export conventions.","Treat eye tracking as optional; design UX so head-only gaze is acceptable."],"tags":["mmd","gaze","skeleton","bone-names","pmx"],"backgroundTag":"missing-skeleton-bone","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}