@doki-land/live2d 0.0.14 โ 0.0.15
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 +180 -1
- package/dist/index.d.ts +55 -7
- package/dist/index.js +370 -203
- package/package.json +9 -7
- package/src/index.ts +3 -0
- package/src/stage/actor-model-slot.ts +63 -167
- package/src/stage/actor.ts +12 -7
- package/src/stage/model-asset-key.ts +19 -0
- package/src/stage/model-asset-registry.ts +337 -0
- package/src/stage/stage.ts +9 -1
package/README.md
CHANGED
|
@@ -1,3 +1,182 @@
|
|
|
1
1
|
# @doki-land/live2d
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The public runtime facade for browser-native Live2D rendering.
|
|
4
|
+
|
|
5
|
+
Use this package in browser games, game-engine integrations, interactive content, model tools, and custom webpage experiences. It composes the default loader, MOC runtimes, and rendering backends behind one small API.
|
|
6
|
+
|
|
7
|
+
## โจ Features
|
|
8
|
+
|
|
9
|
+
- One dependency for the standard runtime pipeline.
|
|
10
|
+
- Pure TypeScript model execution.
|
|
11
|
+
- WebGPU, WebGL2, and Canvas2D backend selection.
|
|
12
|
+
- Explicit game-loop integration through `update(deltaTime)`.
|
|
13
|
+
- Local URL, remote URL, and `npm:` model sources.
|
|
14
|
+
- Parameter inspection and mutation.
|
|
15
|
+
- Frame capture for diagnostics.
|
|
16
|
+
- Hit testing in normalized model coordinates.
|
|
17
|
+
- Progress, ready, error, phase, and frame-profile events.
|
|
18
|
+
|
|
19
|
+
## ๐ฆ Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @doki-land/live2d
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The implementation packages are installed transitively. Most applications should not depend on them directly.
|
|
26
|
+
|
|
27
|
+
## ๐ Quick Start
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { createLive2D } from "@doki-land/live2d";
|
|
31
|
+
|
|
32
|
+
const canvas = document.querySelector<HTMLCanvasElement>("#live2d");
|
|
33
|
+
|
|
34
|
+
if (!canvas) {
|
|
35
|
+
throw new Error("Missing Live2D canvas");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const runtime = createLive2D({
|
|
39
|
+
prefer: ["webgpu", "webgl2", "canvas2d"],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
runtime.mount(canvas);
|
|
43
|
+
await runtime.loadModel("/models/character.model3.json");
|
|
44
|
+
|
|
45
|
+
let previous = performance.now();
|
|
46
|
+
|
|
47
|
+
function frame(now: number) {
|
|
48
|
+
runtime.update((now - previous) / 1000);
|
|
49
|
+
previous = now;
|
|
50
|
+
requestAnimationFrame(frame);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
requestAnimationFrame(frame);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## ๐ฎ Game Loop
|
|
57
|
+
|
|
58
|
+
The runtime does not require ownership of `requestAnimationFrame`. Drive it from an existing engine scheduler:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
engine.onUpdate((deltaTime) => {
|
|
62
|
+
runtime.update(deltaTime);
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The delta is expressed in seconds. Keep the value bounded after tab suspension or long pauses to avoid unstable animation and physics once those systems are enabled.
|
|
67
|
+
|
|
68
|
+
## ๐ฅ Loading Models
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
await runtime.loadModel("/models/actor.model3.json");
|
|
72
|
+
|
|
73
|
+
await runtime.loadModel(
|
|
74
|
+
"https://cdn.example.com/models/actor.model3.json",
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
await runtime.loadModel(
|
|
78
|
+
"npm:live2d-widget-model-hijiki@1.0.5/assets/hijiki.model.json",
|
|
79
|
+
);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Listen for progress when presenting a loading interface:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
runtime.events.on("progress", ({ stage, progress, detail }) => {
|
|
86
|
+
console.log(stage, Math.round(progress * 100), detail);
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Remote servers must allow cross-origin access to settings, model binaries, and textures.
|
|
91
|
+
|
|
92
|
+
## ๐๏ธ Parameters
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
runtime.setParameter("PARAM_ANGLE_X", 15);
|
|
96
|
+
|
|
97
|
+
const angleX = runtime
|
|
98
|
+
.listParameters()
|
|
99
|
+
.find((parameter) => parameter.id === "PARAM_ANGLE_X");
|
|
100
|
+
|
|
101
|
+
console.log(angleX);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Parameter availability and ranges belong to the loaded model. Do not assume every model implements the same IDs.
|
|
105
|
+
|
|
106
|
+
## ๐ฑ๏ธ Hit Testing
|
|
107
|
+
|
|
108
|
+
`hitTest(x, y)` expects normalized model coordinates where both axes are approximately in the `-1..1` range:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const area = runtime.hitTest(modelX, modelY);
|
|
112
|
+
|
|
113
|
+
if (area) {
|
|
114
|
+
console.log("Hit", area);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Current fallback hit testing can identify visible drawables. Semantic names such as `Head` or `Body` require corresponding model metadata and runtime support.
|
|
119
|
+
|
|
120
|
+
## ๐ Frame Profiling
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
runtime.events.on("profile", (profile) => {
|
|
124
|
+
console.log({
|
|
125
|
+
fps: profile.fpsSmooth,
|
|
126
|
+
frameMs: profile.frameMs,
|
|
127
|
+
evaluateMs: profile.evaluateMs,
|
|
128
|
+
drawMs: profile.drawMs,
|
|
129
|
+
drawables: profile.drawableCount,
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
These timings are runtime-side measurements. Use browser GPU profiling when diagnosing shader, mask, upload, or device scheduling costs.
|
|
135
|
+
|
|
136
|
+
## ๐งฉ Custom Pipeline
|
|
137
|
+
|
|
138
|
+
Advanced applications can provide a renderer or model backends:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const runtime = createLive2D({
|
|
142
|
+
renderer: customRenderer,
|
|
143
|
+
backends: [customBackend],
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Custom implementations must preserve the contracts exported by the renderer package. Avoid moving format or renderer logic into application adapters.
|
|
148
|
+
|
|
149
|
+
## ๐งน Lifecycle
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
runtime.destroy();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Destroy the runtime when its canvas or host scene is permanently removed. This releases model state, textures, draw passes, event listeners owned by the runtime, and graphics resources owned by the selected renderer.
|
|
156
|
+
|
|
157
|
+
## โก Performance Notes
|
|
158
|
+
|
|
159
|
+
- Reuse one runtime for repeated updates instead of recreating it per frame.
|
|
160
|
+
- Keep canvas backing dimensions intentional; CSS size alone does not limit GPU pixel work.
|
|
161
|
+
- Prefer an engine-owned loop when integrating with a game.
|
|
162
|
+
- Avoid repeatedly enumerating parameters in a hot loop.
|
|
163
|
+
- Measure the complete frame path before attributing a bottleneck to TypeScript, WebAssembly, or a specific graphics API.
|
|
164
|
+
|
|
165
|
+
## ๐งช Development
|
|
166
|
+
|
|
167
|
+
From the workspace root:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
pnpm typecheck
|
|
171
|
+
pnpm --filter @doki-land/live2d build
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Changes to the facade should include tests for state transitions, cancellation, events, and resource cleanup where applicable.
|
|
175
|
+
|
|
176
|
+
## ๐ค Contributing
|
|
177
|
+
|
|
178
|
+
Keep the facade small. Model-format behavior belongs in `@doki-land/live2d-renderer`, source resolution belongs in `@doki-land/live2d-loader`, and webpage chrome belongs in `@doki-land/live2d-widget`.
|
|
179
|
+
|
|
180
|
+
## ๐ License
|
|
181
|
+
|
|
182
|
+
See the repository license. Model and artwork licenses are separate from the runtime license.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as _doki_land_live2d_core from '@doki-land/live2d-core';
|
|
2
|
-
import {
|
|
3
|
-
export { ActorHit, ActorTransform, AssetResolver, CreateActorOptions, CreateLive2dStageOptions, DEFAULT_ACTOR_TRANSFORM, EventEmitter, FrameProfile, FrameSnapshot, InternalModel, Live2DSession, Live2dActor, Live2dStage, LoadProgress, LoadProgressStage, ModelFormat, ModelInstance, ModelProgram, ModelSettings, ModelSource, PlayMotionActorOptions, PointerTrackingMode, PointerTrackingPolicy, SessionPhase, SessionState, StagePointerEvent, StageUpdateMode } from '@doki-land/live2d-core';
|
|
2
|
+
import { Live2dStageAssets, ModelSource, AssetResolver, ModelAsset, LoadProgress, ModelSettings, InternalModel, Live2dActor, CreateActorOptions, ActorTransform, PlayMotionActorOptions, ActorHit, CreateLive2dStageOptions, Live2dStage, PointerTrackingPolicy, StagePointerEvent, Live2DSession } from '@doki-land/live2d-core';
|
|
3
|
+
export { ActorHit, ActorInstance, ActorTransform, AssetResolver, CreateActorOptions, CreateLive2dStageOptions, DEFAULT_ACTOR_TRANSFORM, EventEmitter, FrameProfile, FrameSnapshot, InternalModel, Live2DSession, Live2dActor, Live2dStage, Live2dStageAssets, LoadProgress, LoadProgressStage, ModelAsset, ModelFormat, ModelInstance, ModelProgram, ModelSettings, ModelSource, PlayMotionActorOptions, PointerTrackingMode, PointerTrackingPolicy, SessionPhase, SessionState, StagePointerEvent, StageUpdateMode } from '@doki-land/live2d-core';
|
|
4
4
|
export { DEFAULT_NPM_CDN, resolveModelSourceUrl, resolveNpmSpecifier } from '@doki-land/live2d-loader';
|
|
5
5
|
import * as _doki_land_live2d_renderer from '@doki-land/live2d-renderer';
|
|
6
|
-
import { ModelBackend, Renderer, ParameterBinding, DrawableMesh, RendererKind } from '@doki-land/live2d-renderer';
|
|
6
|
+
import { ModelBackend, compileSharedModelCompile, TextureData, Renderer, ParameterBinding, DrawableMesh, RendererKind } from '@doki-land/live2d-renderer';
|
|
7
7
|
export { ModelBackend, ParameterBinding, Renderer, RendererKind, createCanvas2DRenderer, createMoc2Backend, createMoc3Backend, createQuadProgram, createRenderer, createWebGl2Renderer, createWebGpuRenderer, decodeMoc3, evaluateFrame, fingerprintSnapshot, parseCpuProgram, serializeCpuProgram } from '@doki-land/live2d-renderer';
|
|
8
8
|
|
|
9
9
|
/** Cubism motion3 segment kinds (spec). */
|
|
@@ -159,8 +159,53 @@ declare function blendMotionLayers(layers: ReadonlyArray<{
|
|
|
159
159
|
*/
|
|
160
160
|
declare function parseMotion3(json: unknown): Motion3Clip;
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
/** @internal */
|
|
163
|
+
declare class ModelAssetHandle implements ModelAsset {
|
|
164
|
+
#private;
|
|
165
|
+
constructor(entry: SharedModelAssetEntry);
|
|
166
|
+
get key(): string;
|
|
167
|
+
get settings(): ModelSettings;
|
|
168
|
+
/** @internal */
|
|
169
|
+
get entry(): SharedModelAssetEntry;
|
|
170
|
+
}
|
|
171
|
+
/** @internal */
|
|
172
|
+
interface SharedModelAssetEntry {
|
|
173
|
+
readonly key: string;
|
|
174
|
+
readonly settings: ModelSettings;
|
|
175
|
+
readonly backend: ModelBackend;
|
|
176
|
+
readonly resolver: AssetResolver;
|
|
177
|
+
readonly sharedCompile: ReturnType<typeof compileSharedModelCompile>;
|
|
178
|
+
readonly textures: TextureData[];
|
|
179
|
+
readonly motionCache: Map<string, Motion3Clip>;
|
|
180
|
+
refCount: number;
|
|
181
|
+
}
|
|
182
|
+
/** Lease held by one actor slot while a model is attached. */
|
|
183
|
+
interface ModelAssetLease {
|
|
184
|
+
readonly asset: ModelAssetHandle;
|
|
185
|
+
readonly motionCache: Map<string, Motion3Clip>;
|
|
186
|
+
readonly resolver: AssetResolver;
|
|
187
|
+
readonly textures: readonly TextureData[];
|
|
188
|
+
createInstance(renderer: Renderer): Promise<{
|
|
189
|
+
model: InternalModel;
|
|
190
|
+
backend: ModelBackend;
|
|
191
|
+
}>;
|
|
192
|
+
release(): void;
|
|
193
|
+
}
|
|
194
|
+
interface ModelAssetRegistryOptions {
|
|
163
195
|
backends: readonly ModelBackend[];
|
|
196
|
+
}
|
|
197
|
+
declare class ModelAssetRegistry implements Live2dStageAssets {
|
|
198
|
+
#private;
|
|
199
|
+
constructor(options: ModelAssetRegistryOptions);
|
|
200
|
+
load(source: ModelSource, resolver?: AssetResolver): Promise<ModelAsset>;
|
|
201
|
+
acquire(source: ModelSource, resolver: AssetResolver | undefined, onProgress?: (payload: LoadProgress) => void): Promise<ModelAssetLease>;
|
|
202
|
+
acquireExisting(asset: ModelAsset): ModelAssetLease;
|
|
203
|
+
destroy(): void;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
type ModelDrawPass = ReturnType<Renderer["createModelDrawPass"]>;
|
|
207
|
+
interface ActorModelSlotOptions {
|
|
208
|
+
assets: ModelAssetRegistry;
|
|
164
209
|
renderer: Renderer;
|
|
165
210
|
onProgress?: (payload: LoadProgress) => void;
|
|
166
211
|
onMotionStart?: (payload: {
|
|
@@ -179,9 +224,10 @@ declare class ActorModelSlot {
|
|
|
179
224
|
#private;
|
|
180
225
|
constructor(options: ActorModelSlotOptions);
|
|
181
226
|
get model(): InternalModel | null;
|
|
182
|
-
get drawPass():
|
|
183
|
-
ensureDrawPass():
|
|
227
|
+
get drawPass(): ModelDrawPass | null;
|
|
228
|
+
ensureDrawPass(): ModelDrawPass;
|
|
184
229
|
load(source: ModelSource, resolver?: AssetResolver): Promise<InternalModel>;
|
|
230
|
+
loadAsset(asset: ModelAsset): Promise<InternalModel>;
|
|
185
231
|
setParameter(id: string, value: number): void;
|
|
186
232
|
listParameters(): readonly ParameterBinding[];
|
|
187
233
|
listMotionGroups(): Record<string, readonly _doki_land_live2d_core.MotionDefinition[]>;
|
|
@@ -205,7 +251,7 @@ declare class ActorModelSlot {
|
|
|
205
251
|
interface Live2dActorImplOptions {
|
|
206
252
|
id: string;
|
|
207
253
|
creationIndex: number;
|
|
208
|
-
|
|
254
|
+
assets: ModelAssetRegistry;
|
|
209
255
|
renderer: Renderer;
|
|
210
256
|
}
|
|
211
257
|
declare class Live2dActorImpl implements Live2dActor {
|
|
@@ -225,6 +271,7 @@ declare class Live2dActorImpl implements Live2dActor {
|
|
|
225
271
|
getTransform(): ActorTransform;
|
|
226
272
|
setTransform(patch: Partial<ActorTransform>): void;
|
|
227
273
|
load(source: Parameters<Live2dActor["load"]>[0], resolver?: Parameters<Live2dActor["load"]>[1]): Promise<InternalModel>;
|
|
274
|
+
loadAsset(asset: ModelAsset): Promise<InternalModel>;
|
|
228
275
|
setParameter(id: string, value: number): void;
|
|
229
276
|
listParameters(): readonly _doki_land_live2d_renderer.ParameterBinding[];
|
|
230
277
|
listMotionGroups(): Record<string, readonly _doki_land_live2d_core.MotionDefinition[]>;
|
|
@@ -258,6 +305,7 @@ type PointerListener = (event: StagePointerEvent) => void;
|
|
|
258
305
|
declare class Live2dStageImpl implements Live2dStage {
|
|
259
306
|
#private;
|
|
260
307
|
constructor(options?: CreateLive2dStageFullOptions);
|
|
308
|
+
get assets(): ModelAssetRegistry;
|
|
261
309
|
get actors(): readonly Live2dActor[];
|
|
262
310
|
get pointerTracking(): PointerTrackingPolicy;
|
|
263
311
|
set pointerTracking(policy: PointerTrackingPolicy);
|