@doki-land/live2d-renderer 0.0.14 → 0.0.16
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/README.md +158 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +24 -5
- package/package.json +12 -10
- package/src/index.ts +2 -0
- package/src/moc/moc2.ts +5 -2
- package/src/moc/moc3.ts +6 -3
- package/src/model-runtime.ts +13 -0
- package/src/shared-compile.ts +28 -0
package/README.md
CHANGED
|
@@ -1,3 +1,160 @@
|
|
|
1
1
|
# @doki-land/live2d-renderer
|
|
2
2
|
|
|
3
|
-
live2d.ts
|
|
3
|
+
Model execution and browser-native rendering backends for `live2d.ts`.
|
|
4
|
+
|
|
5
|
+
This package owns both MOC2/MOC3 runtime behavior and the WebGPU, WebGL2, and Canvas2D graphics implementations.
|
|
6
|
+
Applications should normally use these capabilities through `@doki-land/live2d`.
|
|
7
|
+
|
|
8
|
+
## ✨ Features
|
|
9
|
+
|
|
10
|
+
- Pure TypeScript MOC2 and MOC3 parsing paths.
|
|
11
|
+
- CPU model programs and deterministic frame evaluation.
|
|
12
|
+
- WebGPU rendering.
|
|
13
|
+
- WebGL2 rendering.
|
|
14
|
+
- Canvas2D compatibility rendering.
|
|
15
|
+
- Texture, blend mode, clipping, and mask-atlas support.
|
|
16
|
+
- Parameter bindings for inspectors and interactive hosts.
|
|
17
|
+
- Renderer initialization fallback.
|
|
18
|
+
- Structural and model-fixture tests.
|
|
19
|
+
|
|
20
|
+
## 🧭 Package Role
|
|
21
|
+
|
|
22
|
+
The source tree separates three concerns:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
src/moc/ model formats, deformation, and ModelBackend implementations
|
|
26
|
+
src/cpu/ model program parsing and CPU frame evaluation
|
|
27
|
+
src/backends/ WebGPU, WebGL2, and Canvas2D graphics backends
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
MOC2 and MOC3 are model formats, not graphics backends. WebGPU, WebGL2, and Canvas2D are graphics backends, not model
|
|
31
|
+
formats.
|
|
32
|
+
|
|
33
|
+
## 📦 Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm add @doki-land/live2d-renderer
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Install this package directly when building renderer tools, conformance tests, custom facades, or specialized host
|
|
40
|
+
pipelines.
|
|
41
|
+
|
|
42
|
+
## 🚀 Creating a Renderer
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { createRenderer } from "@doki-land/live2d-renderer";
|
|
46
|
+
|
|
47
|
+
const renderer = createRenderer({
|
|
48
|
+
prefer: ["webgpu", "webgl2", "canvas2d"],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
await renderer.initialize(canvas);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Fallback occurs during initialization. This matters because browser feature detection alone cannot prove that an
|
|
55
|
+
adapter, device, context, or required capability can be created.
|
|
56
|
+
|
|
57
|
+
## 🖼️ Backend Selection
|
|
58
|
+
|
|
59
|
+
### WebGPU
|
|
60
|
+
|
|
61
|
+
Preferred for modern browsers and explicit GPU resource management. A valid `navigator.gpu` object is not sufficient;
|
|
62
|
+
adapter and device creation can still fail.
|
|
63
|
+
|
|
64
|
+
### WebGL2
|
|
65
|
+
|
|
66
|
+
Primary GPU fallback with broad browser support. Shader compilation, framebuffer allocation, texture limits, and context
|
|
67
|
+
loss remain device-dependent.
|
|
68
|
+
|
|
69
|
+
### Canvas2D
|
|
70
|
+
|
|
71
|
+
Compatibility and diagnostic path using textured triangle approximation. It is useful for fallback rendering and
|
|
72
|
+
CPU-output inspection but is not expected to match GPU throughput for complex models.
|
|
73
|
+
|
|
74
|
+
## 🧠 Model Runtime
|
|
75
|
+
|
|
76
|
+
A `ModelBackend` is responsible for:
|
|
77
|
+
|
|
78
|
+
- determining whether it can handle normalized model settings;
|
|
79
|
+
- creating model state from settings and binary data;
|
|
80
|
+
- updating model time and parameters;
|
|
81
|
+
- producing drawable meshes or CPU frame snapshots;
|
|
82
|
+
- exposing parameter bindings;
|
|
83
|
+
- releasing model-owned state.
|
|
84
|
+
|
|
85
|
+
Model runtimes must not create their own application frame loop or DOM controls.
|
|
86
|
+
|
|
87
|
+
## 🎭 Rendering Semantics
|
|
88
|
+
|
|
89
|
+
The renderer preserves model-defined behavior including:
|
|
90
|
+
|
|
91
|
+
- drawable render order;
|
|
92
|
+
- opacity and visibility;
|
|
93
|
+
- normal, additive, and multiplicative blending where implemented;
|
|
94
|
+
- mask relationships;
|
|
95
|
+
- inverted masks;
|
|
96
|
+
- texture coordinates;
|
|
97
|
+
- model-space orientation.
|
|
98
|
+
|
|
99
|
+
Transparent drawables cannot be freely reordered across characters or masks merely to reduce state changes. Visual
|
|
100
|
+
correctness takes precedence over speculative batching.
|
|
101
|
+
|
|
102
|
+
## 🧭 Coordinate Contract
|
|
103
|
+
|
|
104
|
+
Model geometry uses a consistent model-space convention before reaching a graphics backend. Each backend is responsible
|
|
105
|
+
for exactly one conversion to its target coordinate system.
|
|
106
|
+
|
|
107
|
+
Do not fix orientation problems with CSS canvas transforms. CSS transforms also affect pointer mapping and can hide a
|
|
108
|
+
double conversion instead of correcting the model-to-renderer contract.
|
|
109
|
+
|
|
110
|
+
Orientation changes require a neutral fixture or pixel evidence across the affected backends.
|
|
111
|
+
|
|
112
|
+
## ⚡ Performance Engineering
|
|
113
|
+
|
|
114
|
+
Performance work should target measured costs:
|
|
115
|
+
|
|
116
|
+
- model-program and instance lifetime;
|
|
117
|
+
- per-frame allocations;
|
|
118
|
+
- parameter dirty tracking;
|
|
119
|
+
- affected deformer and drawable evaluation;
|
|
120
|
+
- dynamic vertex upload ranges;
|
|
121
|
+
- mask-pass reuse;
|
|
122
|
+
- texture and pipeline state changes;
|
|
123
|
+
- overdraw and canvas resolution;
|
|
124
|
+
- multi-character scaling.
|
|
125
|
+
|
|
126
|
+
WebAssembly, TypeScript, WebGPU, and WebGL2 are implementation tools, not benchmark results. Comparisons must use
|
|
127
|
+
equivalent visual output and report CPU, GPU, allocation, upload, and memory metrics.
|
|
128
|
+
|
|
129
|
+
## 🧪 Testing
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pnpm --filter @doki-land/live2d-renderer typecheck
|
|
133
|
+
pnpm --filter @doki-land/live2d-renderer test
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Renderer changes should add the smallest appropriate evidence:
|
|
137
|
+
|
|
138
|
+
- CPU golden snapshots for evaluation changes;
|
|
139
|
+
- structural assertions for format decoding;
|
|
140
|
+
- orientation tests for coordinate changes;
|
|
141
|
+
- mask and blend fixtures for compositing changes;
|
|
142
|
+
- pixel comparisons for graphics parity;
|
|
143
|
+
- browser checks for device-specific behavior.
|
|
144
|
+
|
|
145
|
+
Proprietary models must not be committed without redistribution permission.
|
|
146
|
+
|
|
147
|
+
## 🔌 Extending the Package
|
|
148
|
+
|
|
149
|
+
New model-format support belongs under `src/moc/`. New browser graphics implementations belong under `src/backends/`. Do
|
|
150
|
+
not create a separate published package for every MOC revision or GPU backend unless an independently useful public
|
|
151
|
+
contract justifies it.
|
|
152
|
+
|
|
153
|
+
## 🤝 Contributing
|
|
154
|
+
|
|
155
|
+
Keep hot paths allocation-aware, preserve deterministic CPU behavior, and document the visual invariant behind renderer
|
|
156
|
+
changes. Generated bundles must be rebuilt from source.
|
|
157
|
+
|
|
158
|
+
## 📄 License
|
|
159
|
+
|
|
160
|
+
See the repository license. Model assets used for local testing may have separate terms.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as _doki_land_live2d_core from '@doki-land/live2d-core';
|
|
1
2
|
import { ParameterProgram, ModelProgram, ModelInstance, FrameSnapshot, ModelFormat, ModelSettings, AssetResolver, InternalModel } from '@doki-land/live2d-core';
|
|
2
3
|
export { detectModelSettingsFormat } from '@doki-land/live2d-core';
|
|
3
4
|
|
|
@@ -376,6 +377,8 @@ interface ModelBackendOptions {
|
|
|
376
377
|
resolver?: AssetResolver;
|
|
377
378
|
/** Preloaded moc bytes (tests). */
|
|
378
379
|
mocBytes?: ArrayBuffer;
|
|
380
|
+
/** Reuse a prior compile from {@link SharedModelCompile}. */
|
|
381
|
+
sharedCompile?: SharedModelCompile;
|
|
379
382
|
}
|
|
380
383
|
/** Live parameter binding for inspectors / playground. */
|
|
381
384
|
interface ParameterBinding {
|
|
@@ -385,6 +388,16 @@ interface ParameterBinding {
|
|
|
385
388
|
readonly defaultValue: number;
|
|
386
389
|
readonly value: number;
|
|
387
390
|
}
|
|
391
|
+
/** Shared read-only decode payload (renderer-internal; set by stage asset cache). */
|
|
392
|
+
interface SharedModelCompile {
|
|
393
|
+
readonly mocBytes: ArrayBuffer;
|
|
394
|
+
/** moc3 official `.moc3` document. */
|
|
395
|
+
readonly moc3Doc?: unknown;
|
|
396
|
+
/** moc3 CPU `.program.json` program. */
|
|
397
|
+
readonly cpuProgram?: _doki_land_live2d_core.ModelProgram;
|
|
398
|
+
/** Parsed moc2 model graph. */
|
|
399
|
+
readonly moc2Model?: unknown;
|
|
400
|
+
}
|
|
388
401
|
/** moc2 / moc3 model runtime. */
|
|
389
402
|
interface ModelBackend {
|
|
390
403
|
readonly format: ModelFormat;
|
|
@@ -525,6 +538,9 @@ declare const PREVIEW_STROKE: {
|
|
|
525
538
|
/** Expand triangle indices into a line-list (each edge twice-wound). */
|
|
526
539
|
declare function triangleEdgesToLineList(indices: Uint16Array): Uint16Array;
|
|
527
540
|
|
|
541
|
+
/** One-time decode of moc bytes for stage-level {@link ModelAsset} sharing. */
|
|
542
|
+
declare function compileSharedModelCompile(settings: ModelSettings, mocBytes: ArrayBuffer): SharedModelCompile;
|
|
543
|
+
|
|
528
544
|
/**
|
|
529
545
|
* `@doki-land/live2d-renderer`
|
|
530
546
|
*
|
|
@@ -537,4 +553,4 @@ declare function triangleEdgesToLineList(indices: Uint16Array): Uint16Array;
|
|
|
537
553
|
|
|
538
554
|
declare const LIVE2D_RENDERER_VERSION: "0.0.0";
|
|
539
555
|
|
|
540
|
-
export { BlendMode, CPU_PROGRAM_KIND, Canvas2DRendererImpl, type Canvas2DRendererOptions, type ClippingContext, type ClippingDrawableRef, type ClippingPartition, type CpuProgramFile, type CreateRendererOptions, type DecodedMoc2, type DecodedMoc3, type DrawableMesh, FULL_NDC_BOUNDS, LIVE2D_RENDERER_VERSION, type LaidOutClippingContext, MASK_CHANNEL_FLAGS, type MaskAtlasMode, type MaskAtlasOptions, type MaskChannelFlag, type MaskLayoutRect, Moc2Backend, Moc3Backend, Moc3DrawableFlag, type Moc3GlueDef, type Moc3GluePair, type ModelBackend, type ModelBackendOptions, type ModelDrawPass, PREVIEW_FILL, PREVIEW_STROKE, type ParameterBinding, type Renderer, type RendererKind, type TextureData, type WebGl2Renderer, WebGl2RendererImpl, type WebGl2RendererOptions, type WebGpuRenderer, WebGpuRendererImpl, type WebGpuRendererOptions, applyMoc3Glues, applyWebGl2BlendMode, buildClippingContexts, calcClippedDrawableBounds, calcVertexBounds, canvasCompositeForBlendMode, createCanvas2DRenderer, createMoc2Backend, createMoc3Backend, createModelInstance, createQuadProgram, createRenderer, createWebGl2Renderer, createWebGpuRenderer, decodeMoc2, decodeMoc2ColorComposition, decodeMoc3, decodeMoc3DrawableFlags, detectMocBinaryFormat, evaluateFrame, expandBounds, fingerprintSnapshot, fitClippingContexts, isCanvas2DAvailable, isCpuProgramBytes, isWebGl2Available, isWebGpuAvailable, layoutMaskAtlas, layoutMaskAtlasRgba, layoutMaskAtlasUvGrid, loadMoc3Glues, maskChannelVec4, maskLayoutVec4, meanGlueSeamDistance, parseCpuProgram, partitionForClipping, selectModelBackend, serializeCpuProgram, setParameterValue, triangleEdgesToLineList, webGpuBlendState };
|
|
556
|
+
export { BlendMode, CPU_PROGRAM_KIND, Canvas2DRendererImpl, type Canvas2DRendererOptions, type ClippingContext, type ClippingDrawableRef, type ClippingPartition, type CpuProgramFile, type CreateRendererOptions, type DecodedMoc2, type DecodedMoc3, type DrawableMesh, FULL_NDC_BOUNDS, LIVE2D_RENDERER_VERSION, type LaidOutClippingContext, MASK_CHANNEL_FLAGS, type MaskAtlasMode, type MaskAtlasOptions, type MaskChannelFlag, type MaskLayoutRect, Moc2Backend, Moc3Backend, Moc3DrawableFlag, type Moc3GlueDef, type Moc3GluePair, type ModelBackend, type ModelBackendOptions, type ModelDrawPass, PREVIEW_FILL, PREVIEW_STROKE, type ParameterBinding, type Renderer, type RendererKind, type SharedModelCompile, type TextureData, type WebGl2Renderer, WebGl2RendererImpl, type WebGl2RendererOptions, type WebGpuRenderer, WebGpuRendererImpl, type WebGpuRendererOptions, applyMoc3Glues, applyWebGl2BlendMode, buildClippingContexts, calcClippedDrawableBounds, calcVertexBounds, canvasCompositeForBlendMode, compileSharedModelCompile, createCanvas2DRenderer, createMoc2Backend, createMoc3Backend, createModelInstance, createQuadProgram, createRenderer, createWebGl2Renderer, createWebGpuRenderer, decodeMoc2, decodeMoc2ColorComposition, decodeMoc3, decodeMoc3DrawableFlags, detectMocBinaryFormat, evaluateFrame, expandBounds, fingerprintSnapshot, fitClippingContexts, isCanvas2DAvailable, isCpuProgramBytes, isWebGl2Available, isWebGpuAvailable, layoutMaskAtlas, layoutMaskAtlasRgba, layoutMaskAtlasUvGrid, loadMoc3Glues, maskChannelVec4, maskLayoutVec4, meanGlueSeamDistance, parseCpuProgram, partitionForClipping, selectModelBackend, serializeCpuProgram, setParameterValue, triangleEdgesToLineList, webGpuBlendState };
|
package/dist/index.js
CHANGED
|
@@ -5236,7 +5236,8 @@ var Moc2Backend = class {
|
|
|
5236
5236
|
return detectModelSettingsFormat2(json) === "moc2";
|
|
5237
5237
|
}
|
|
5238
5238
|
async createModel(settings, options) {
|
|
5239
|
-
|
|
5239
|
+
const shared = options?.sharedCompile;
|
|
5240
|
+
let bytes = options?.mocBytes ?? shared?.mocBytes;
|
|
5240
5241
|
if (!bytes) {
|
|
5241
5242
|
if (!options?.resolver) {
|
|
5242
5243
|
throw new Error(
|
|
@@ -5245,7 +5246,7 @@ var Moc2Backend = class {
|
|
|
5245
5246
|
}
|
|
5246
5247
|
bytes = await options.resolver.fetchBytes(settings.moc);
|
|
5247
5248
|
}
|
|
5248
|
-
const moc = new Moc2Parser(bytes).parseModel();
|
|
5249
|
+
const moc = shared?.moc2Model ?? new Moc2Parser(bytes).parseModel();
|
|
5249
5250
|
const program = moc2ModelToProgram(moc);
|
|
5250
5251
|
const instance = createModelInstance(program);
|
|
5251
5252
|
const model = {
|
|
@@ -5400,7 +5401,8 @@ var Moc3Backend = class {
|
|
|
5400
5401
|
return detectModelSettingsFormat3(json) === "moc3";
|
|
5401
5402
|
}
|
|
5402
5403
|
async createModel(settings, options) {
|
|
5403
|
-
|
|
5404
|
+
const shared = options?.sharedCompile;
|
|
5405
|
+
let bytes = options?.mocBytes ?? shared?.mocBytes;
|
|
5404
5406
|
if (!bytes) {
|
|
5405
5407
|
if (!options?.resolver) {
|
|
5406
5408
|
throw new Error(
|
|
@@ -5413,9 +5415,9 @@ var Moc3Backend = class {
|
|
|
5413
5415
|
let doc = null;
|
|
5414
5416
|
let program;
|
|
5415
5417
|
if (isCpu) {
|
|
5416
|
-
program = parseCpuProgram(bytes);
|
|
5418
|
+
program = shared?.cpuProgram ?? parseCpuProgram(bytes);
|
|
5417
5419
|
} else {
|
|
5418
|
-
doc = parseMoc3Document(bytes);
|
|
5420
|
+
doc = shared?.moc3Doc ?? parseMoc3Document(bytes);
|
|
5419
5421
|
program = moc3DocumentToProgram(doc);
|
|
5420
5422
|
}
|
|
5421
5423
|
const instance = createModelInstance(program);
|
|
@@ -5521,6 +5523,22 @@ function selectModelBackend(backends, json) {
|
|
|
5521
5523
|
return hit;
|
|
5522
5524
|
}
|
|
5523
5525
|
|
|
5526
|
+
// src/shared-compile.ts
|
|
5527
|
+
function compileSharedModelCompile(settings, mocBytes) {
|
|
5528
|
+
if (settings.format === "moc2") {
|
|
5529
|
+
return {
|
|
5530
|
+
mocBytes,
|
|
5531
|
+
moc2Model: new Moc2Parser(mocBytes).parseModel()
|
|
5532
|
+
};
|
|
5533
|
+
}
|
|
5534
|
+
const isCpu = settings.moc.toLowerCase().endsWith(".program.json") || isCpuProgramBytes(mocBytes);
|
|
5535
|
+
if (isCpu) {
|
|
5536
|
+
const cpuProgram = parseCpuProgram(mocBytes);
|
|
5537
|
+
return { mocBytes, cpuProgram };
|
|
5538
|
+
}
|
|
5539
|
+
return { mocBytes, moc3Doc: parseMoc3Document(mocBytes) };
|
|
5540
|
+
}
|
|
5541
|
+
|
|
5524
5542
|
// src/index.ts
|
|
5525
5543
|
var LIVE2D_RENDERER_VERSION = "0.0.0";
|
|
5526
5544
|
export {
|
|
@@ -5543,6 +5561,7 @@ export {
|
|
|
5543
5561
|
calcClippedDrawableBounds,
|
|
5544
5562
|
calcVertexBounds,
|
|
5545
5563
|
canvasCompositeForBlendMode,
|
|
5564
|
+
compileSharedModelCompile,
|
|
5546
5565
|
createCanvas2DRenderer,
|
|
5547
5566
|
createMoc2Backend,
|
|
5548
5567
|
createMoc3Backend,
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doki-land/live2d-renderer",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Live2D
|
|
3
|
+
"version": "0.0.16",
|
|
4
|
+
"description": "Live2D rendering — moc2/moc3 decode + WebGPU, WebGL2, Canvas2D backends for live2d.ts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"
|
|
7
|
+
"homepage": "https://github.com/doki-land/live2d.ts",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/doki-land/live2d.ts.git",
|
|
11
|
+
"directory": "projects/live2d-renderer"
|
|
12
|
+
},
|
|
8
13
|
"keywords": [
|
|
9
14
|
"live2d",
|
|
10
15
|
"webgpu",
|
|
@@ -20,7 +25,8 @@
|
|
|
20
25
|
},
|
|
21
26
|
"files": [
|
|
22
27
|
"dist",
|
|
23
|
-
"src"
|
|
28
|
+
"src",
|
|
29
|
+
"README.md"
|
|
24
30
|
],
|
|
25
31
|
"publishConfig": {
|
|
26
32
|
"access": "public",
|
|
@@ -39,11 +45,7 @@
|
|
|
39
45
|
"test": "vitest run"
|
|
40
46
|
},
|
|
41
47
|
"dependencies": {
|
|
42
|
-
"@doki-land/live2d-core": "0.0.
|
|
48
|
+
"@doki-land/live2d-core": "0.0.16"
|
|
43
49
|
},
|
|
44
|
-
"sideEffects": false
|
|
45
|
-
"repository": {
|
|
46
|
-
"type": "git",
|
|
47
|
-
"url": "git+https://github.com/doki-land/live2d.ts.git"
|
|
48
|
-
}
|
|
50
|
+
"sideEffects": false
|
|
49
51
|
}
|
package/src/index.ts
CHANGED
|
@@ -100,6 +100,7 @@ export {
|
|
|
100
100
|
type ModelBackend,
|
|
101
101
|
type ModelBackendOptions,
|
|
102
102
|
type ParameterBinding,
|
|
103
|
+
type SharedModelCompile,
|
|
103
104
|
selectModelBackend,
|
|
104
105
|
} from "./model-runtime.js";
|
|
105
106
|
export {
|
|
@@ -107,6 +108,7 @@ export {
|
|
|
107
108
|
PREVIEW_STROKE,
|
|
108
109
|
triangleEdgesToLineList,
|
|
109
110
|
} from "./preview-style.js";
|
|
111
|
+
export { compileSharedModelCompile } from "./shared-compile.js";
|
|
110
112
|
export {
|
|
111
113
|
BlendMode,
|
|
112
114
|
type DrawableMesh,
|
package/src/moc/moc2.ts
CHANGED
|
@@ -92,7 +92,8 @@ export class Moc2Backend implements ModelBackend {
|
|
|
92
92
|
settings: ModelSettings,
|
|
93
93
|
options?: ModelBackendOptions,
|
|
94
94
|
): Promise<InternalModel> {
|
|
95
|
-
|
|
95
|
+
const shared = options?.sharedCompile;
|
|
96
|
+
let bytes = options?.mocBytes ?? shared?.mocBytes;
|
|
96
97
|
if (!bytes) {
|
|
97
98
|
if (!options?.resolver) {
|
|
98
99
|
throw new Error(
|
|
@@ -102,7 +103,9 @@ export class Moc2Backend implements ModelBackend {
|
|
|
102
103
|
bytes = await options.resolver.fetchBytes(settings.moc);
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
const moc =
|
|
106
|
+
const moc =
|
|
107
|
+
(shared?.moc2Model as Moc2ModelImpl | undefined) ??
|
|
108
|
+
new Moc2Parser(bytes).parseModel();
|
|
106
109
|
const program = moc2ModelToProgram(moc);
|
|
107
110
|
const instance = createModelInstance(program);
|
|
108
111
|
const model: InternalModel = {
|
package/src/moc/moc3.ts
CHANGED
|
@@ -94,7 +94,8 @@ export class Moc3Backend implements ModelBackend {
|
|
|
94
94
|
settings: ModelSettings,
|
|
95
95
|
options?: ModelBackendOptions,
|
|
96
96
|
): Promise<InternalModel> {
|
|
97
|
-
|
|
97
|
+
const shared = options?.sharedCompile;
|
|
98
|
+
let bytes = options?.mocBytes ?? shared?.mocBytes;
|
|
98
99
|
if (!bytes) {
|
|
99
100
|
if (!options?.resolver) {
|
|
100
101
|
throw new Error(
|
|
@@ -111,9 +112,11 @@ export class Moc3Backend implements ModelBackend {
|
|
|
111
112
|
let doc: Moc3Document | null = null;
|
|
112
113
|
let program: ModelProgram;
|
|
113
114
|
if (isCpu) {
|
|
114
|
-
program = parseCpuProgram(bytes);
|
|
115
|
+
program = shared?.cpuProgram ?? parseCpuProgram(bytes);
|
|
115
116
|
} else {
|
|
116
|
-
doc =
|
|
117
|
+
doc =
|
|
118
|
+
(shared?.moc3Doc as Moc3Document | undefined) ??
|
|
119
|
+
parseMoc3Document(bytes);
|
|
117
120
|
program = moc3DocumentToProgram(doc);
|
|
118
121
|
}
|
|
119
122
|
|
package/src/model-runtime.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface ModelBackendOptions {
|
|
|
18
18
|
resolver?: AssetResolver;
|
|
19
19
|
/** Preloaded moc bytes (tests). */
|
|
20
20
|
mocBytes?: ArrayBuffer;
|
|
21
|
+
/** Reuse a prior compile from {@link SharedModelCompile}. */
|
|
22
|
+
sharedCompile?: SharedModelCompile;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/** Live parameter binding for inspectors / playground. */
|
|
@@ -29,6 +31,17 @@ export interface ParameterBinding {
|
|
|
29
31
|
readonly value: number;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
/** Shared read-only decode payload (renderer-internal; set by stage asset cache). */
|
|
35
|
+
export interface SharedModelCompile {
|
|
36
|
+
readonly mocBytes: ArrayBuffer;
|
|
37
|
+
/** moc3 official `.moc3` document. */
|
|
38
|
+
readonly moc3Doc?: unknown;
|
|
39
|
+
/** moc3 CPU `.program.json` program. */
|
|
40
|
+
readonly cpuProgram?: import("@doki-land/live2d-core").ModelProgram;
|
|
41
|
+
/** Parsed moc2 model graph. */
|
|
42
|
+
readonly moc2Model?: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
/** moc2 / moc3 model runtime. */
|
|
33
46
|
export interface ModelBackend {
|
|
34
47
|
readonly format: ModelFormat;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ModelProgram, ModelSettings } from "@doki-land/live2d-core";
|
|
2
|
+
import { isCpuProgramBytes, parseCpuProgram } from "./cpu/cpu-program.js";
|
|
3
|
+
import { Moc2Parser } from "./moc/moc2-objects.js";
|
|
4
|
+
import { parseMoc3Document } from "./moc/moc3-reader.js";
|
|
5
|
+
import type { SharedModelCompile } from "./model-runtime.js";
|
|
6
|
+
|
|
7
|
+
/** One-time decode of moc bytes for stage-level {@link ModelAsset} sharing. */
|
|
8
|
+
export function compileSharedModelCompile(
|
|
9
|
+
settings: ModelSettings,
|
|
10
|
+
mocBytes: ArrayBuffer,
|
|
11
|
+
): SharedModelCompile {
|
|
12
|
+
if (settings.format === "moc2") {
|
|
13
|
+
return {
|
|
14
|
+
mocBytes,
|
|
15
|
+
moc2Model: new Moc2Parser(mocBytes).parseModel(),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const isCpu =
|
|
20
|
+
settings.moc.toLowerCase().endsWith(".program.json") ||
|
|
21
|
+
isCpuProgramBytes(mocBytes);
|
|
22
|
+
if (isCpu) {
|
|
23
|
+
const cpuProgram: ModelProgram = parseCpuProgram(mocBytes);
|
|
24
|
+
return { mocBytes, cpuProgram };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { mocBytes, moc3Doc: parseMoc3Document(mocBytes) };
|
|
28
|
+
}
|