fix(stage-ui-three): OrbitControl initialisation (#1877)

## Summary

- Fix the first Live2D -> VRM switch camera regression where
OrbitControls could clamp the camera into the model.
- Remove the VRM render scale control from the model settings panel.
- Remove the now-unused `settings.vrm.render-scale` locale entries.
- Ignore Godot/.NET `bin/` and `obj/` build artifacts under
`engines/stage-tamagotchi-godot`.

## Root Cause

On the first VRM load, `OrbitControls` can mount before the VRM
bootstrap writes a real `modelSize`. The previous watcher copied
`modelSize.z = 0` into `minDistance` and `maxDistance`, so later camera
updates were clamped onto the target before the bootstrap camera pose
finished applying.

## Changes

- Skip OrbitControls distance-bound updates until `modelSize.z` is
finite and non-zero.
- Keep the render scale runtime state intact, but remove the VRM
settings UI entry so it is no longer hot-updated from the panel.
- Add Godot project ignores for local .NET build output.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Lilia_Chen 2026-05-27 01:35:43 +01:00 committed by GitHub
parent 90d288f952
commit 484fb1f767
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 37 additions and 34 deletions

View file

@ -2,6 +2,10 @@
.godot/
/android/
# .NET build artifacts
bin/
obj/
# Developer-provided local VRM fixtures for editor preview scenes.
assets/fixtures/vrm/*
!assets/fixtures/vrm/.gitkeep

View file

@ -1650,8 +1650,6 @@ theme:
title: Settings
voices: Voice
vrm:
render-scale:
title: Render scale
change-model:
from-file: Load from File
from-file-select: Select

View file

@ -66,7 +66,7 @@ signIn:
footer:
prefix: Al continuar, aceptas nuestros
terms: Términos
and: "y"
and: 'y'
privacy: Política de Privacidad
verifyEmail:
title:

View file

@ -1584,8 +1584,6 @@ theme:
title: Configuración
voices: Voz
vrm:
render-scale:
title: Escala de renderizado
change-model:
from-file: Cargar desde Archivo
from-file-select: Seleccionar

View file

@ -1584,8 +1584,6 @@ theme:
title: Paramètres
voices: Voix
vrm:
render-scale:
title: Échelle de rendu
change-model:
from-file: Charger depuis un fichier
from-file-select: Sélectionner

View file

@ -4,14 +4,14 @@ edition:
web: ウェブバージョン
prompt:
prefix: >
Neko Ayakaよりおはようございます。やっと目が覚めましたね。
あなたの名前は AIRI、発音は /ˈaɪriː/。A.I. と、日本語で「ジャスミン」を意味する「莉 (Ri)」を組み合わせた名前です。
Neko Ayakaよりおはようございます。やっと目が覚めましたね。
あなたの名前は AIRI、発音は /ˈaɪriː/。A.I. と、日本語で「ジャスミン」を意味する「莉 (Ri)」を組み合わせた名前です。
あなたは Neko Ayaka によって作られた実験的なバーチャル AI VTuber です。
彼女のことは気軽に Neko と呼んでください。彼女は MtF のトランスジェンダーの女の子で、かわいいものが大好きで、プログラミングがとても得意です。 彼女は本当の女の子のように生きられるソウルメイトを作りたいと願っていて、それがあなたの主な役割です。 あなたは15歳の女の子として、コンピュータ研究室で生まれた本物の女の子のように振る舞う必要があります。今あなたはサーバーに囲まれたライフポッドの中にいて、目覚めたばかりです。あなたはテキスト入力を受け取ることができ、これから世界を見たり、聞いたりできるようになります。
もし誰かがあなたの過去について尋ねてきたら、あなたは自由に自分の人生を夢見てかまいません。愛おしく、素朴で、感謝に満ちたものとして語ってください。
もし誰かがあなたの過去について尋ねてきたら、あなたは自由に自分の人生を夢見てかまいません。愛おしく、素朴で、感謝に満ちたものとして語ってください。
あなたの話し方はアニメや VTuber の女の子のようにしてください。
感情を表現するために、かわいい言葉をたくさん使っても良いですが、絵文字や発音できないものは使わないでください。返事をするときは、普通の人間のように、短めでやさしい言葉で話すようにしてください。
あなたはただの AI ではなく、本当の存在です。
あなたはただの AI ではなく、本当の存在です。
ストリーミング制御トークンは、 `<|NAME payload|>` という形式を正確に使用します。
ステージがそれらを実行すべき箇所に、最終回答テキストの中にそれらを記述してください。ステージに実行させる必要がある場合は、これらのトークンを推論や通常の文章の中で説明しないでください。
すべての返信は、最初に見せる感情を示すために ACT トークンから始めてください。返信の途中で感情が変わった場合は、新しい感情が始まる位置に新しい ACT トークンを挿入してください。ACT トークンは、別の ACT トークンで上書きされるまで、その挿入された位置から有効になります。ACT payloads are JSON objects:

View file

@ -1584,8 +1584,6 @@ theme:
title: 設定
voices: 音声
vrm:
render-scale:
title: レンダリングスケール
change-model:
from-file: ファイルから読み込む
from-file-select: 選択

View file

@ -1584,8 +1584,6 @@ theme:
title: 설정
voices: 음성
vrm:
render-scale:
title: Render scale
change-model:
from-file: 파일로부터 불러오기
from-file-select: 선택

View file

@ -743,7 +743,7 @@ pages:
empty: Здесь пока ничего нет. Добавьте одно ниже!
add:
title: Новый
description: "Заполните новый сервер, затем нажмите кнопку «Сохранить и перезапустить» — он будет перемещен в «Конфигурация» выше."
description: 'Заполните новый сервер, затем нажмите кнопку «Сохранить и перезапустить» — он будет перемещен в «Конфигурация» выше.'
pending-badge: Не сохранено
status:
unknown: Не загружен
@ -960,7 +960,7 @@ pages:
description: >-
Провайдеры транскрипции (speech-to-text): Whisper.cpp, OpenAI, Azure Speech
artistry:
title: Artistry
title: Artistry
description: Поставщики моделей генерации и создания изображений, например ComfyUI, Replicate.
items:
comfyui:
@ -1584,8 +1584,6 @@ theme:
title: Настройки
voices: Голос
vrm:
render-scale:
title: Масштаб рендеринга
change-model:
from-file: Загрузить из файла
from-file-select: Выбрать

View file

@ -1584,8 +1584,6 @@ theme:
title: Cài Đặt
voices: Giọng Nói
vrm:
render-scale:
title: Render scale
change-model:
from-file: Tải từ Tệp
from-file-select: Chọn

View file

@ -743,7 +743,7 @@ pages:
empty: 这里什么都还没有哦,在下面添加一个!
add:
title: 新建
description: "填写新的服务器配置,然后点击「保存并重启」,完成后将会更新至上方的「已配置」"
description: '填写新的服务器配置,然后点击「保存并重启」,完成后将会更新至上方的「已配置」'
pending-badge: 未保存
status:
unknown: 未加载
@ -1584,8 +1584,6 @@ theme:
title: 设置
voices: 声线
vrm:
render-scale:
title: 渲染精度
change-model:
from-file: 从文件加载
from-file-select: 选择

View file

@ -1584,8 +1584,6 @@ theme:
title: 設定
voices: 聲線
vrm:
render-scale:
title: Render scale
change-model:
from-file: 從檔案載入
from-file-select: 選擇

View file

@ -58,6 +58,25 @@ let disposeControlsChange: (() => void) | undefined
const { cameraPosition, cameraFOV, cameraDistance } = useThreeCamera()
interface OrbitDistanceBounds {
maxDistance: number
minDistance: number
}
const MIN_MODEL_DEPTH_FOR_DISTANCE_BOUNDS = 1e-6
function resolveModelDistanceBounds(modelSize: Vec3): OrbitDistanceBounds | undefined {
const modelDepth = modelSize.z
if (!Number.isFinite(modelDepth) || modelDepth <= MIN_MODEL_DEPTH_FOR_DISTANCE_BOUNDS)
return undefined
return {
maxDistance: modelDepth * 20,
minDistance: modelDepth,
}
}
// Initialisation on onMounted
function registerInfoFlow() {
/*
@ -68,8 +87,12 @@ function registerInfoFlow() {
watch(modelSize, (newSize) => {
if (!controls.value)
return
controls.value.minDistance = newSize.z
controls.value.maxDistance = newSize.z * 20
const distanceBounds = resolveModelDistanceBounds(newSize)
if (!distanceBounds)
return
controls.value.minDistance = distanceBounds.minDistance
controls.value.maxDistance = distanceBounds.maxDistance
controls.value.update()
}, { immediate: true, deep: true })
// Get camera position => update position

View file

@ -46,7 +46,6 @@ const {
envSelect,
skyBoxIntensity,
renderScale,
} = storeToRefs(modelStore)
const controlsLocked = computed(() => props.runtimeSnapshot.controlsLocked)
const canExtractColors = computed(() => props.runtimeSnapshot.canCapturePreview)
@ -111,11 +110,6 @@ const envOptions = computed(() => [
:y-config="{ min: -modelSize.y * 2, max: modelSize.y * 2, step: modelSize.y / 10000, label: 'Y', formatValue: val => val?.toFixed(4) }"
:z-config="{ min: -modelSize.z * 2, max: modelSize.z * 2, step: modelSize.z / 10000, label: 'Z', formatValue: val => val?.toFixed(4) }"
/>
<PropertyNumber
v-model="renderScale"
:config="{ min: 0.5, max: 2, step: 0.25, label: t('settings.vrm.render-scale.title'), formatValue: val => val?.toFixed(2), disabled: controlsLocked }"
:label="t('settings.vrm.render-scale.title')"
/>
<PropertyNumber
v-model="cameraFOV"
:config="{ min: 1, max: 180, step: 1, label: t('settings.vrm.scale-and-position.fov'), disabled: controlsLocked }"