modelstage 0.0.0-stage → 0.5.28
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.
- package/LICENSE +21 -0
- package/README.md +274 -2
- package/THIRD_PARTY_NOTICES.md +90 -0
- package/bin/modelstage.mjs +137 -0
- package/config/diag-upload.example.json +7 -0
- package/config/models/live2d.assetbundls.json +1 -0
- package/config/models/live2d.azurlane.json +1 -0
- package/config/models/live2d.azurlane_bundle.json +1 -0
- package/config/models/live2d.azurlane_spine.json +1 -0
- package/config/models/live2d.bd2.json +1 -0
- package/config/models/live2d.bluearchive.json +1 -0
- package/config/models/live2d.live2d.json +1 -0
- package/config/models/live2d.nikke.json +1 -0
- package/config/models/live2d.nikke_2.json +1 -0
- package/config/models/live2d.npc.json +1 -0
- package/config/models/spine.assetbundls.json +1 -0
- package/config/models/spine.azurlane.json +1 -0
- package/config/models/spine.azurlane_bundle.json +1 -0
- package/config/models/spine.azurlane_spine.json +1 -0
- package/config/models/spine.bd2.json +1 -0
- package/config/models/spine.bluearchive.json +1 -0
- package/config/models/spine.live2d.json +1 -0
- package/config/models/spine.nikke.json +1 -0
- package/config/models/spine.nikke_2.json +1 -0
- package/config/models/spine.npc.json +1 -0
- package/config/names/NIKKE.json +1058 -0
- package/config/names/README.md +47 -0
- package/config/names//345/244/251/345/221/275/344/271/213/345/255/220.json +42897 -0
- package/config/names//346/243/225/350/211/262/345/260/230/345/237/2032.json +4016 -0
- package/config/names//347/242/247/350/223/235/350/210/252/347/272/277.json +38342 -0
- package/config/plugins.json +13 -0
- package/config/resources.example.json +40 -0
- package/package.json +63 -4
- package/plugins/unity-live2d-bundle/README.md +98 -0
- package/plugins/unity-live2d-bundle/index.js +891 -0
- package/plugins/unity-live2d-bundle/lib/anim-to-motion.js +402 -0
- package/plugins/unity-live2d-bundle/lib/bundle-probe.js +244 -0
- package/plugins/unity-live2d-bundle/lib/bytes.js +189 -0
- package/plugins/unity-live2d-bundle/lib/common-strings.js +46 -0
- package/plugins/unity-live2d-bundle/lib/dxt.js +197 -0
- package/plugins/unity-live2d-bundle/lib/errors.js +71 -0
- package/plugins/unity-live2d-bundle/lib/etc2.js +371 -0
- package/plugins/unity-live2d-bundle/lib/extract.js +242 -0
- package/plugins/unity-live2d-bundle/lib/lzma-d-min.LICENSE.txt +21 -0
- package/plugins/unity-live2d-bundle/lib/lzma-d-min.js +34 -0
- package/plugins/unity-live2d-bundle/lib/model3.js +137 -0
- package/plugins/unity-live2d-bundle/lib/png.js +424 -0
- package/plugins/unity-live2d-bundle/lib/serialized.js +233 -0
- package/plugins/unity-live2d-bundle/lib/texdecode.js +190 -0
- package/plugins/unity-live2d-bundle/lib/typetree.js +215 -0
- package/plugins/unity-live2d-bundle/lib/unityfs.js +396 -0
- package/plugins/unity-live2d-bundle/plugin.json +81 -0
- package/plugins/unity-live2d-bundle/profiles/unityfs-live2d.json +26 -0
- package/plugins/unity-spine-bundle/index.js +535 -0
- package/plugins/unity-spine-bundle/lib/bytes.js +189 -0
- package/plugins/unity-spine-bundle/lib/common-strings.js +46 -0
- package/plugins/unity-spine-bundle/lib/dxt.js +197 -0
- package/plugins/unity-spine-bundle/lib/errors.js +79 -0
- package/plugins/unity-spine-bundle/lib/etc2.js +371 -0
- package/plugins/unity-spine-bundle/lib/extract.js +242 -0
- package/plugins/unity-spine-bundle/lib/lzma-d-min.LICENSE.txt +21 -0
- package/plugins/unity-spine-bundle/lib/lzma-d-min.js +34 -0
- package/plugins/unity-spine-bundle/lib/png.js +424 -0
- package/plugins/unity-spine-bundle/lib/serialized.js +233 -0
- package/plugins/unity-spine-bundle/lib/texdecode.js +190 -0
- package/plugins/unity-spine-bundle/lib/typetree.js +215 -0
- package/plugins/unity-spine-bundle/lib/unityfs.js +396 -0
- package/plugins/unity-spine-bundle/plugin.json +75 -0
- package/src/server/README.md +69 -0
- package/src/server/assets/browse.mjs +56 -0
- package/src/server/assets/bundle-probe.mjs +94 -0
- package/src/server/assets/probe.mjs +184 -0
- package/src/server/core/config.mjs +173 -0
- package/src/server/core/env-num.mjs +60 -0
- package/src/server/core/env.mjs +49 -0
- package/src/server/core/fs-portable.mjs +37 -0
- package/src/server/core/log.mjs +107 -0
- package/src/server/core/paths.mjs +44 -0
- package/src/server/data/catalog.mjs +1091 -0
- package/src/server/data/entry-states.mjs +499 -0
- package/src/server/data/live2d.mjs +272 -0
- package/src/server/data/manifest-cache.mjs +58 -0
- package/src/server/data/manifest-stamp.mjs +135 -0
- package/src/server/data/model-meta.mjs +114 -0
- package/src/server/data/names.mjs +106 -0
- package/src/server/data/shape-probe-worker.mjs +33 -0
- package/src/server/data/shape-probe.mjs +626 -0
- package/src/server/data/spine.mjs +336 -0
- package/src/server/data/tree.mjs +126 -0
- package/src/server/http/atomic-json.mjs +145 -0
- package/src/server/http/background-solids.mjs +270 -0
- package/src/server/http/backgrounds.mjs +388 -0
- package/src/server/http/diag-upload.mjs +264 -0
- package/src/server/http/diag.mjs +173 -0
- package/src/server/http/ready.mjs +167 -0
- package/src/server/http/static.mjs +312 -0
- package/src/server/http/write.mjs +654 -0
- package/src/server/index.mjs +114 -0
- package/src/server/plugins/builtin-providers.json +30 -0
- package/src/server/plugins/registry.mjs +650 -0
- package/src/server/plugins/slot-catalog.mjs +286 -0
- package/src/server/routes/background-solids.mjs +56 -0
- package/src/server/routes/backgrounds.mjs +77 -0
- package/src/server/routes/config.mjs +38 -0
- package/src/server/routes/data.mjs +313 -0
- package/src/server/routes/diag.mjs +37 -0
- package/src/server/routes/entries.mjs +170 -0
- package/src/server/routes/ready.mjs +122 -0
- package/src/server/serve.mjs +396 -0
- package/src/server/unpack/capacity.mjs +217 -0
- package/src/server/unpack/mem-probe.mjs +27 -0
- package/src/server/unpack/plugin-runner.mjs +529 -0
- package/src/server/unpack/plugin-store.mjs +736 -0
- package/src/server/unpack/probe-store.mjs +73 -0
- package/src/server/unpack/routes.mjs +359 -0
- package/src/server/unpack/runtime/exec.mjs +558 -0
- package/src/server/unpack/runtime/index.mjs +141 -0
- package/src/server/unpack/runtime/node-vm.mjs +111 -0
- package/src/server/unpack/unpack-worker.mjs +105 -0
- package/src/server/unpack/worker-client.mjs +184 -0
- package/src/server/unpack/write-result.mjs +84 -0
- package/src/shared/bundle-shape.mjs +83 -0
- package/src/shared/entry-reasons.mjs +119 -0
- package/src/shared/names.mjs +45 -0
- package/src/shared/type-labels.mjs +31 -0
- package/src/web/README.md +193 -0
- package/src/web/app/anim-list.js +222 -0
- package/src/web/app/background-solids.js +150 -0
- package/src/web/app/backgrounds.js +152 -0
- package/src/web/app/camera-log.js +28 -0
- package/src/web/app/context.js +72 -0
- package/src/web/app/dir-picker.js +242 -0
- package/src/web/app/entry-report.js +71 -0
- package/src/web/app/memory-bars.js +91 -0
- package/src/web/app/memory-budget.js +228 -0
- package/src/web/app/memory-guard.js +204 -0
- package/src/web/app/model-actions.js +682 -0
- package/src/web/app/renderer-setting.js +70 -0
- package/src/web/app/root-manager.js +254 -0
- package/src/web/app/status.js +138 -0
- package/src/web/app/topbar-actions.js +357 -0
- package/src/web/app/transport-actions.js +297 -0
- package/src/web/app/ui-state.js +559 -0
- package/src/web/app/url-params.js +171 -0
- package/src/web/app/view-settings.js +186 -0
- package/src/web/components.css +524 -0
- package/src/web/debug/diag-hud.js +95 -0
- package/src/web/debug/drag.js +369 -0
- package/src/web/debug/log.js +320 -0
- package/src/web/debug/model-cmp.js +292 -0
- package/src/web/debug/panel.css +474 -0
- package/src/web/debug/panel.js +712 -0
- package/src/web/debug/race-probe.js +82 -0
- package/src/web/debug/upload.js +130 -0
- package/src/web/engine/adapters/atlas.js +47 -0
- package/src/web/engine/adapters/index.js +22 -0
- package/src/web/engine/adapters/renderer.js +223 -0
- package/src/web/engine/adapters/skeleton.js +105 -0
- package/src/web/engine/adapters/texture.js +64 -0
- package/src/web/engine/animation.js +181 -0
- package/src/web/engine/appearance.js +136 -0
- package/src/web/engine/camera-adapter.js +212 -0
- package/src/web/engine/effect-chain.js +138 -0
- package/src/web/engine/engine-state.js +120 -0
- package/src/web/engine/filters.js +224 -0
- package/src/web/engine/fit.js +197 -0
- package/src/web/engine/frame-gate.js +33 -0
- package/src/web/engine/gl-driver.js +56 -0
- package/src/web/engine/gl.js +77 -0
- package/src/web/engine/index.js +20 -0
- package/src/web/engine/loader.js +55 -0
- package/src/web/engine/model-info.js +26 -0
- package/src/web/engine/pipeline.js +200 -0
- package/src/web/engine/player.js +427 -0
- package/src/web/engine/pma/blend.js +76 -0
- package/src/web/engine/pma/detect.js +118 -0
- package/src/web/engine/pma/index.js +8 -0
- package/src/web/engine/pma/resolve.js +112 -0
- package/src/web/engine/pma/upload.js +127 -0
- package/src/web/engine/pma.js +5 -0
- package/src/web/engine/render-frame.js +93 -0
- package/src/web/engine/runtime-capabilities.js +209 -0
- package/src/web/engine/runtime.js +37 -0
- package/src/web/engine/skins.js +165 -0
- package/src/web/engine/textures.js +174 -0
- package/src/web/index.html +117 -0
- package/src/web/infra/api.js +531 -0
- package/src/web/infra/diag.js +509 -0
- package/src/web/infra/document.js +269 -0
- package/src/web/infra/dom.js +31 -0
- package/src/web/infra/events.mjs +27 -0
- package/src/web/infra/log.js +153 -0
- package/src/web/infra/mem-stats.js +112 -0
- package/src/web/infra/plugin-host-registration.mjs +285 -0
- package/src/web/infra/plugin-host-report.mjs +32 -0
- package/src/web/infra/plugin-host-unload.mjs +79 -0
- package/src/web/infra/plugin-host-unpack.mjs +899 -0
- package/src/web/infra/plugin-host-warmup.mjs +180 -0
- package/src/web/infra/plugin-host.js +628 -0
- package/src/web/infra/ready.js +461 -0
- package/src/web/infra/schedule.js +51 -0
- package/src/web/infra/shortcuts.js +129 -0
- package/src/web/infra/slot-registry.js +402 -0
- package/src/web/infra/vfs.js +247 -0
- package/src/web/live2d/bridge.js +400 -0
- package/src/web/live2d/model.js +321 -0
- package/src/web/live2d/overlay.js +142 -0
- package/src/web/live2d/pixi-app.js +111 -0
- package/src/web/live2d/playback-probe.js +92 -0
- package/src/web/live2d/probe-geometry.js +329 -0
- package/src/web/live2d/probe.js +119 -0
- package/src/web/live2d/release.js +66 -0
- package/src/web/live2d/runtime-adapter.js +289 -0
- package/src/web/live2d/stage-ctx.js +152 -0
- package/src/web/live2d/stage.js +204 -0
- package/src/web/live2d/vfs-settings.js +174 -0
- package/src/web/live2d/view.js +89 -0
- package/src/web/main.js +629 -0
- package/src/web/panel/background-list.js +373 -0
- package/src/web/panel/bg-source.js +291 -0
- package/src/web/panel/color-picker.css +199 -0
- package/src/web/panel/color-picker.js +278 -0
- package/src/web/panel/dialog.css +116 -0
- package/src/web/panel/dialog.js +173 -0
- package/src/web/panel/dom.js +35 -0
- package/src/web/panel/icons.js +58 -0
- package/src/web/panel/memory-bar.js +195 -0
- package/src/web/panel/panel-settings.js +1263 -0
- package/src/web/panel/panel.css +1304 -0
- package/src/web/panel/render-choice.js +104 -0
- package/src/web/panel/schema.js +784 -0
- package/src/web/panel/settings-io.js +291 -0
- package/src/web/panel/shell-controls.js +274 -0
- package/src/web/panel/slider.js +210 -0
- package/src/web/panel/toolbar-schema.js +111 -0
- package/src/web/panel/toolbar.js +195 -0
- package/src/web/panel/transport-picker.js +375 -0
- package/src/web/panel/transport.js +321 -0
- package/src/web/panel/view.js +553 -0
- package/src/web/plugin/unpack-worker.js +185 -0
- package/src/web/render/camera/constants.js +60 -0
- package/src/web/render/camera/index.js +123 -0
- package/src/web/render/camera/spine.js +124 -0
- package/src/web/render/capture.js +30 -0
- package/src/web/render/interaction.js +527 -0
- package/src/web/render/live2d.js +627 -0
- package/src/web/render/playback.js +47 -0
- package/src/web/render/reset-view.js +29 -0
- package/src/web/render/spine.js +386 -0
- package/src/web/render/surface-switch.js +176 -0
- package/src/web/render/surface.js +571 -0
- package/src/web/render/surfaces.js +70 -0
- package/src/web/roster/filter.js +31 -0
- package/src/web/roster/list.js +276 -0
- package/src/web/roster/persist.js +92 -0
- package/src/web/roster/reveal.js +77 -0
- package/src/web/roster/rows.js +248 -0
- package/src/web/roster/snapshot.js +39 -0
- package/src/web/roster/type-filter.js +97 -0
- package/src/web/roster/view.js +761 -0
- package/src/web/spine/canvas-api-groups.js +267 -0
- package/src/web/spine/canvas-api.js +97 -0
- package/src/web/spine/canvas-document.js +80 -0
- package/src/web/spine/canvas-engine.js +193 -0
- package/src/web/spine/canvas-entry.js +28 -0
- package/src/web/spine/canvas-pixels.js +36 -0
- package/src/web/spine/canvas-runtime.js +127 -0
- package/src/web/spine/canvas-view.js +108 -0
- package/src/web/spine/canvas.js +7 -0
- package/src/web/spine/runtime-hint.js +73 -0
- package/src/web/spine/runtimes.js +212 -0
- package/src/web/style.css +493 -0
- package/tool/lib/bundle-probe-node.mjs +121 -0
- package/tool/lib/paths.mjs +20 -0
- package/tool/lib/runtime-guides.mjs +65 -0
- package/tool/lib/runtime-package.mjs +403 -0
- package/tool/lib/spine-meta.mjs +227 -0
- package/tool/lib/spine-motions.mjs +104 -0
- package/tool/parse-jsonc.mjs +86 -0
- package/tool/resolve-live2d-runtimes.mjs +276 -0
- package/tool/spine-motion-rules.json +101 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// live2d/vfs-settings.js —— 让 Live2D 的内部文件(moc3 / 贴图 / physics / 动作)也走 VirtualFS
|
|
2
|
+
//
|
|
3
|
+
// 背景:pixi-live2d-display 加载模型时,model3.json 里的相对引用最终都经
|
|
4
|
+
// `ModelSettings.resolveURL(ref)` 变成实际地址 —— 这是那套库里**唯一**的"相对引用 → 地址"入口
|
|
5
|
+
// (moc3 走 `_live2d_load(settings.resolveURL(moc))`、贴图与 physics 在 setup 阶段、
|
|
6
|
+
// 动作/表情/音声经 FileLoader 的 `settings.resolveURL(url)`)。
|
|
7
|
+
// 库自己"把文件夹拖进页面"的功能就是靠覆盖这个方法把引用映射到 blob URL 的。
|
|
8
|
+
//
|
|
9
|
+
// 所以这里不改库、也不 patch fetch(契约禁止):构造 ModelSettings 实例、覆盖 resolveURL,
|
|
10
|
+
// 让它先去问 VirtualFS,命中就用虚拟地址(blob),未命中才回落到网络地址。
|
|
11
|
+
//
|
|
12
|
+
// 为什么不会踩到"blob 不能当基址":
|
|
13
|
+
// settings.url 只用于给库做占位/校验,真实解析全由我们的 resolveURL 完成;
|
|
14
|
+
// 返回的 blob URL 直接进 XHR/Image,不再经过库里的 url.resolve。
|
|
15
|
+
//
|
|
16
|
+
// ⚠️ 本文件**只对容器模型**生效(entryUrl 是 vfs://):VFS 只登记容器产物,
|
|
17
|
+
// 散目录模型的每个内部文件来查都必然 miss(白跑查找 + 每文件一条 warn),
|
|
18
|
+
// 所以 model.js 的 buildVfsSettingsOrNull 对散目录直接返回 null、走原生 URL 路径。
|
|
19
|
+
// ⇒ 能走到下面 resolveURL 的都是"本该命中 VFS"的模型;这里 miss 意味着
|
|
20
|
+
// **容器产物真缺该文件**,那条 warn 值得看(不是噪音)。
|
|
21
|
+
import { isDirectUrl } from './stage-ctx.js';
|
|
22
|
+
// 版本差异(settings 类名)只在 runtime-adapter 里收敛:本文件不再出现具体类名
|
|
23
|
+
import { getSettingsCtor } from './runtime-adapter.js';
|
|
24
|
+
|
|
25
|
+
/** 单模型解析统计(验收/排查:命中了哪些文件、哪些回落网络) */
|
|
26
|
+
function createStats() {
|
|
27
|
+
return { calls: 0, hits: [], missing: [], source: null };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 引用归一:去掉 './' 前缀(model3.json 里两种写法都常见) */
|
|
31
|
+
function normalizeRef(ref) {
|
|
32
|
+
return String(ref == null ? '' : ref).replace(/^\.\//, '');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 目录地址(保留末尾斜杠);不可解析返回 null */
|
|
36
|
+
function dirOf(url) {
|
|
37
|
+
const text = String(url == null ? '' : url);
|
|
38
|
+
if (!text) return null;
|
|
39
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(text)) return text.replace(/[^/]*$/, '');
|
|
40
|
+
try {
|
|
41
|
+
const base = typeof location !== 'undefined' ? location.href : 'http://localhost/';
|
|
42
|
+
return new URL(text, base).href.replace(/[^/]*$/, '');
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** 入口若是 vfs:// 就返回其目录(作为虚拟前缀),否则 null */
|
|
49
|
+
function vfsPrefixOf(entryUrl) {
|
|
50
|
+
return /^vfs:\/\//i.test(String(entryUrl || '')) ? dirOf(entryUrl) : null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 判定 Cubism 2(.moc)/ 4(.moc3):按 moc 扩展名,其次看 Version */
|
|
54
|
+
function isCubism2(json) {
|
|
55
|
+
const moc = String((json && json.FileReferences && json.FileReferences.Moc) || '');
|
|
56
|
+
if (/\.moc$/i.test(moc)) return true;
|
|
57
|
+
const ver = Number(json && json.Version);
|
|
58
|
+
return Number.isFinite(ver) && ver < 3;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 取实例构造器(类名兼容在 adapter 里,这里只判 Cubism 世代) */
|
|
62
|
+
function pickSettingsCtor(cubism2) {
|
|
63
|
+
try {
|
|
64
|
+
return getSettingsCtor(cubism2 ? 'cubism2' : 'cubism4');
|
|
65
|
+
} catch (e) {
|
|
66
|
+
// 把 adapter 的明确原因(version-unsupported + 库实际导出的类名)带出去,
|
|
67
|
+
// 而不是笼统的 null —— 排障时要看的就是"库到底导出了哪些 Settings 类"
|
|
68
|
+
return { error: (e && e.message) || 'version-unsupported' };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 入参校验:可用返回 null,否则返回原因(供调用方 warn) */
|
|
73
|
+
function inputReason(json, vfs) {
|
|
74
|
+
if (!json || !json.FileReferences) return 'no-json';
|
|
75
|
+
if (!vfs || typeof vfs.resolve !== 'function') return 'no-vfs';
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 实例化 settings。
|
|
81
|
+
* 库升级可能改名/改语义:没有 resolveURL 就当作不支持(调用方回落旧路径,绝不白屏)。
|
|
82
|
+
* @returns {{settings: object|null, reason: string|null}}
|
|
83
|
+
*/
|
|
84
|
+
function instantiateSettings(json, entryUrl) {
|
|
85
|
+
const picked = pickSettingsCtor(isCubism2(json));
|
|
86
|
+
// 取不到构造器时把 adapter 给的**具体原因**(试过哪些类名、库实际导出了什么)带出去
|
|
87
|
+
if (!picked || typeof picked !== 'function') {
|
|
88
|
+
return { settings: null, reason: (picked && picked.error) || 'version-unsupported: 没有可用的 settings 构造器' };
|
|
89
|
+
}
|
|
90
|
+
const Ctor = picked;
|
|
91
|
+
try {
|
|
92
|
+
const settings = new Ctor({ ...json, url: vfsPrefixOf(entryUrl) || dirOf(entryUrl) || '' });
|
|
93
|
+
if (!settings || typeof settings.resolveURL !== 'function') return { settings: null, reason: 'no-resolve-url' };
|
|
94
|
+
return { settings, reason: null };
|
|
95
|
+
} catch (e) {
|
|
96
|
+
return { settings: null, reason: `ctor-throw:${(e && e.message) || e}` };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 候选虚拟地址(**按引用形状选**,不做暴力试探)。
|
|
102
|
+
* 为什么不三个候选都试:VFS 的 stats.misses 按「每次未命中的 resolve 调用」累计,
|
|
103
|
+
* 盲目试探会让 misses 虚高(曾出现"明明全部命中却报 6 次 miss"的假象)。
|
|
104
|
+
* · 绝对路径引用(/dir/x):直接按该路径问 VFS —— 对应"插件按服务端路径注册"的做法
|
|
105
|
+
* · 相对引用(motions/x):以 vfs 前缀(入口是 vfs:// 时)或入口网络目录为基准
|
|
106
|
+
*/
|
|
107
|
+
function candidatesFor(key, vfsPrefix, entryDir) {
|
|
108
|
+
if (key.startsWith('/')) return [key];
|
|
109
|
+
const out = [];
|
|
110
|
+
if (vfsPrefix) out.push(vfsPrefix + key);
|
|
111
|
+
if (entryDir) out.push(entryDir + key);
|
|
112
|
+
// 去重:容器模型里 vfsPrefix 与 entryDir **常常是同一个目录**(都从 entryUrl 推出来),
|
|
113
|
+
// 不去重就会把同一个地址试两遍 —— 多一次 VFS 未命中计数,而且诊断信息里
|
|
114
|
+
// "尝试过 2 个候选"会看起来像试了两个不同地址,把排查方向带偏(实测踩到过)。
|
|
115
|
+
const list = out.length ? out : [key];
|
|
116
|
+
return list.length > 1 ? [...new Set(list)] : list;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** 单个候选去问 VFS(异常一律当未命中) */
|
|
120
|
+
function tryResolve(vfs, cand) {
|
|
121
|
+
try {
|
|
122
|
+
return vfs.resolve(cand);
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** 把 resolveURL 换成"先问 VFS、未命中回落网络"(统计供验收/排查) */
|
|
129
|
+
function installResolver(settings, ctx) {
|
|
130
|
+
const { vfs, vfsPrefix, entryDir, stats, note } = ctx;
|
|
131
|
+
settings.resolveURL = function resolveUrlViaVfs(ref) {
|
|
132
|
+
const key = normalizeRef(ref);
|
|
133
|
+
stats.calls += 1;
|
|
134
|
+
if (isDirectUrl(key)) return key; // data:/blob:/http(s) 已是最终地址
|
|
135
|
+
const cands = candidatesFor(key, vfsPrefix, entryDir);
|
|
136
|
+
for (const cand of cands) {
|
|
137
|
+
const hit = tryResolve(vfs, cand);
|
|
138
|
+
if (hit) { stats.hits.push(key); return hit; }
|
|
139
|
+
}
|
|
140
|
+
stats.missing.push(key);
|
|
141
|
+
// 未命中时**把病因一起打出来**:只给 key 的话,看不出是"prefix 对不上"还是"这包真缺这个文件"。
|
|
142
|
+
// 实测教训:排查"容器产物明明已注册却回落网络"时,只能临时 hook vfs 才拿得到 prefix / 已注册数,
|
|
143
|
+
// 而这两项恰好是判断的分水岭(0 个文件 = prefix 不匹配,N>0 个文件 = 只缺这一个)。
|
|
144
|
+
// 级别保持 warn、信息只增不减 —— 这条 warn 本身就是有价值的信号,不许为"看着干净"而降级。
|
|
145
|
+
const registered = typeof vfs.prefixFileCount === 'function' ? vfs.prefixFileCount(vfsPrefix || '') : -1;
|
|
146
|
+
const prefixText = vfsPrefix || '(无前缀)';
|
|
147
|
+
const why = registered === 0
|
|
148
|
+
? '⚠️ 该前缀下没有任何已注册文件 —— 基本可判定是 prefix 对不上(例如缓存产物里存的是旧 prefix),不是偶发 miss'
|
|
149
|
+
: (registered < 0
|
|
150
|
+
? '该前缀的已注册文件数查不到(vfs 没提供 prefixFileCount)'
|
|
151
|
+
: `该前缀已注册 ${registered} 个文件`);
|
|
152
|
+
note(`Live2D 资源未在 VFS 中命中,回落网络:${key}(prefix=${prefixText};${why};尝试过 ${cands.length} 个候选:${cands.join(' , ')})`, 'warn');
|
|
153
|
+
return entryDir ? entryDir + key.replace(/^\/+/, '') : key;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 构造"内部文件走 VirtualFS"的 settings 实例。
|
|
159
|
+
* @param {{json:object, entryUrl:string, vfs:object, log?:Function}} options
|
|
160
|
+
* @returns {{settings:object|null, stats:object, reason?:string}}
|
|
161
|
+
*/
|
|
162
|
+
export function createVfsSettings({ json, entryUrl, vfs, log } = {}) {
|
|
163
|
+
const stats = createStats();
|
|
164
|
+
const note = typeof log === 'function' ? log : () => {};
|
|
165
|
+
const bad = inputReason(json, vfs);
|
|
166
|
+
if (bad) return { settings: null, stats, reason: bad };
|
|
167
|
+
const { settings, reason } = instantiateSettings(json, entryUrl);
|
|
168
|
+
if (!settings) return { settings: null, stats, reason };
|
|
169
|
+
stats.source = isCubism2(json) ? 'cubism2' : 'cubism4';
|
|
170
|
+
installResolver(settings, {
|
|
171
|
+
vfs, vfsPrefix: vfsPrefixOf(entryUrl), entryDir: dirOf(entryUrl), stats, note,
|
|
172
|
+
});
|
|
173
|
+
return { settings, stats };
|
|
174
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// live2d/view.js —— 视图数学(适配 / 缩放 / 平移 / 尺寸同步)
|
|
2
|
+
//
|
|
3
|
+
// 这里是"模型在画布上落在哪、多大"的唯一算法来源:computeFit 量包围盒,applyZoom 叠加
|
|
4
|
+
// 缩放与平移。相机状态(zoomRatio / panX / panY)在 ctx.st 上,由 bridge 写入。
|
|
5
|
+
//
|
|
6
|
+
// 两条尺寸入口都必须同时做两件事,缺一就会出现"画布变了但可见区域没变":
|
|
7
|
+
// ① PIXI 渲染器 resize(pixiApp.fitAppToOverlay / 直接 resize)
|
|
8
|
+
// ② 覆盖层容器与画布尺寸由 overlay.setBox 统一写入(本模块两条尺寸入口都会调它)
|
|
9
|
+
// —— 旋转后"模型挤在左侧窄带"就是只做了 ① 没做 ②。
|
|
10
|
+
|
|
11
|
+
/** 取包围盒字段,缺失时回落(b 可能为 null) */
|
|
12
|
+
function boundOr(b, key, fallback) {
|
|
13
|
+
return (b && b[key]) || fallback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 创建视图模块。
|
|
18
|
+
* @param {object} ctx 运行时上下文
|
|
19
|
+
* @returns {{computeFit:Function, applyZoom:Function, syncRect:Function, syncSize:Function}}
|
|
20
|
+
*/
|
|
21
|
+
export function createView(ctx) {
|
|
22
|
+
const { st, surfaceGeometry, surfaceSize } = ctx;
|
|
23
|
+
|
|
24
|
+
function computeFit() {
|
|
25
|
+
const model = st.model;
|
|
26
|
+
if (!model) return null;
|
|
27
|
+
model.scale.set(1);
|
|
28
|
+
model.position.set(0, 0);
|
|
29
|
+
const b = model.getBounds();
|
|
30
|
+
return {
|
|
31
|
+
x: boundOr(b, 'x', 0),
|
|
32
|
+
y: boundOr(b, 'y', 0),
|
|
33
|
+
w: boundOr(b, 'width', model.width || 1),
|
|
34
|
+
h: boundOr(b, 'height', model.height || 1),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function applyZoom() {
|
|
39
|
+
const model = st.model;
|
|
40
|
+
if (!model || !st.app) return;
|
|
41
|
+
const res = st.app.renderer.resolution || 1;
|
|
42
|
+
// 尺寸优先取 Surface 句柄(渲染器不自己算画布尺寸)
|
|
43
|
+
const size = surfaceSize();
|
|
44
|
+
const W = size ? size.width : st.app.renderer.width / res;
|
|
45
|
+
const H = size ? size.height : st.app.renderer.height / res;
|
|
46
|
+
const fit = computeFit();
|
|
47
|
+
if (!fit) return;
|
|
48
|
+
const s = Math.min(W / fit.w, H / fit.h) * 0.98 * st.zoomRatio;
|
|
49
|
+
model.scale.set(s);
|
|
50
|
+
model.x = W / 2 - (fit.x + fit.w / 2) * s + st.panX;
|
|
51
|
+
model.y = H / 2 - (fit.y + fit.h / 2) * s + st.panY;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 覆盖层与画布几何对齐(尺寸变化、旋转时都会走到)。
|
|
56
|
+
*
|
|
57
|
+
* 关键:**覆盖层容器的 CSS 盒必须一起同步**。容器是 absolute + overflow:hidden,
|
|
58
|
+
* 尺寸由内联 px 决定;只 resize PIXI 而不改容器,画布会被旧宽度裁掉
|
|
59
|
+
* (真机现象:模型挤在左侧窄带、右侧露出棋盘格,重载模型才恢复)。
|
|
60
|
+
* 尺寸写入收口到 overlay.setBox(本模块不写内联尺寸,只负责调用)。
|
|
61
|
+
*/
|
|
62
|
+
function syncRect() {
|
|
63
|
+
// 几何由 StageSurface 负责:有句柄时直接同步尺寸,不再读 DOM
|
|
64
|
+
if (!ctx.handleRef) return;
|
|
65
|
+
ctx.api.overlay.setBox();
|
|
66
|
+
ctx.api.pixiApp.fitAppToOverlay();
|
|
67
|
+
applyZoom();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 尺寸同步:Surface 量好尺寸后调用(渲染器不自己读 DOM)。
|
|
72
|
+
* 顺序:句柄 → 渲染器 resize → 覆盖层容器盒 → 视图重算。
|
|
73
|
+
* 覆盖层盒放在 resize 之后、applyZoom 之前:先把"画布可见区域"改对,再算模型落点。
|
|
74
|
+
* @param {object} handle StageSurface 句柄
|
|
75
|
+
*/
|
|
76
|
+
function syncSize(handle) {
|
|
77
|
+
ctx.handleRef = handle || ctx.handleRef;
|
|
78
|
+
try {
|
|
79
|
+
const gSync = surfaceGeometry();
|
|
80
|
+
if (st.app && st.app.renderer && gSync) {
|
|
81
|
+
st.app.renderer.resize(gSync.cssW, gSync.cssH);
|
|
82
|
+
}
|
|
83
|
+
ctx.api.overlay.setBox(gSync);
|
|
84
|
+
applyZoom();
|
|
85
|
+
} catch { /* 忽略 */ }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { computeFit, applyZoom, syncRect, syncSize };
|
|
89
|
+
}
|