fix(stage-ui): auto eye blink wasn't functioning (#352)

This commit is contained in:
Neko
2025-08-06 18:21:12 +08:00
committed by GitHub
parent 281d436e59
commit b5a5ec6a82
@@ -1,5 +1,6 @@
<script setup lang="ts">
import type { Application } from '@pixi/app'
import type { Cubism4InternalModel, InternalModel } from 'pixi-live2d-display/cubism4'
import localforage from 'localforage'
@@ -16,6 +17,13 @@ import { computed, onMounted, onUnmounted, ref, shallowRef, toRef, watch } from
import { useLive2DIdleEyeFocus } from '../../../composables/live2d'
import { useLive2d, useSettings } from '../../../stores'
type CubismModel = Cubism4InternalModel['coreModel']
type CubismEyeBlink = Cubism4InternalModel['eyeBlink']
type PixiLive2DInternalModel = InternalModel & {
eyeBlink?: CubismEyeBlink
coreModel: CubismModel
}
const props = withDefaults(defineProps<{
app?: Application
mouthOpenSize?: number
@@ -61,10 +69,11 @@ const offset = computed(() => parsePropsOffset())
const pixiApp = toRef(() => props.app)
const paused = toRef(() => props.paused)
const focusAt = toRef(() => props.focusAt)
const model = ref<Live2DModel>()
const model = ref<Live2DModel<PixiLive2DInternalModel>>()
const initialModelWidth = ref<number>(0)
const initialModelHeight = ref<number>(0)
const mouthOpenSize = computed(() => Math.max(0, Math.min(100, props.mouthOpenSize)))
const lastUpdateTime = ref(0)
const dark = useDark()
const breakpoints = useBreakpoints(breakpointsTailwind)
@@ -126,7 +135,7 @@ async function loadModel() {
model.value = undefined
}
const modelInstance = new Live2DModel()
const modelInstance = new Live2DModel<PixiLive2DInternalModel>()
if (loadSource.value === 'file') {
await Live2DFactory.setupLive2DModel(modelInstance, [modelFile.value], { autoInteract: false })
@@ -149,7 +158,7 @@ async function loadModel() {
})
const internalModel = model.value.internalModel
const coreModel = internalModel.coreModel as any
const coreModel = internalModel.coreModel
const motionManager = internalModel.motionManager
coreModel.setParameterValueById('ParamMouthOpenY', mouthOpenSize.value)
@@ -179,15 +188,51 @@ async function loadModel() {
}
// This is hacky too
const hookedUpdate = motionManager.update
motionManager.update = function (model, now) {
hookedUpdate?.call(this, model, now)
// Only update eye focus when the model is idle
if (motionManager.state.currentGroup === motionManager.groups.idle) {
idleEyeFocus.update(internalModel, now)
const hookedUpdate = motionManager.update as (model: CubismModel, now: number) => boolean
motionManager.update = function (model: CubismModel, now: number) {
// Let's initialize the last update time first
if (lastUpdateTime.value === 0) {
lastUpdateTime.value = now
}
return true
hookedUpdate?.call(this, model, now)
// Possibility 1: Only update eye focus when the model is idle
// Possibility 2: For models having no motion groups, currentGroup will be undefined while groups can be { idle: ... }
if (!motionManager.state.currentGroup || motionManager.state.currentGroup === motionManager.groups.idle) {
idleEyeFocus.update(internalModel, now)
// If the model has eye blink parameters
if (internalModel.eyeBlink != null) {
// For the part of the auto eye blink implementation in pixi-live2d-display
//
// this.emit("beforeMotionUpdate");
// const motionUpdated = this.motionManager.update(this.coreModel, now);
// this.emit("afterMotionUpdate");
// model.saveParameters();
// this.motionManager.expressionManager?.update(model, now);
// if (!motionUpdated) {
// this.eyeBlink?.updateParameters(model, dt);
// }
//
// https://github.com/guansss/pixi-live2d-display/blob/31317b37d5e22955a44d5b11f37f421e94a11269/src/cubism4/Cubism4InternalModel.ts#L202-L214
//
// If the this.motionManager.update returns true, as motion updated flag on,
// the eye blink parameters will not be updated, in another hand, the auto eye blink is disabled
//
// Since we are hooking the motionManager.update method currently,
// and previously a always `true` was returned, eye blink parameters were never updated.
//
// Thous we are here to manually update the eye blink parameters within this hooked method
internalModel.eyeBlink.updateParameters(model, (now - lastUpdateTime.value) / 1000)
}
// still, mark the motion as updated
return true
}
lastUpdateTime.value = now
return false
}
motionManager.on('motionStart', (group, index) => {