@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 CHANGED
@@ -1,3 +1,182 @@
1
1
  # @doki-land/live2d
2
2
 
3
- live2d.ts package 0.0.14.
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 { LoadProgress, InternalModel, ModelSource, AssetResolver, Live2dActor, CreateActorOptions, ActorTransform, PlayMotionActorOptions, ActorHit, CreateLive2dStageOptions, Live2dStage, PointerTrackingPolicy, StagePointerEvent, Live2DSession } from '@doki-land/live2d-core';
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
- interface ActorModelSlotOptions {
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(): ReturnType<Renderer["createModelDrawPass"]> | null;
183
- ensureDrawPass(): ReturnType<Renderer["createModelDrawPass"]>;
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
- backends: readonly ModelBackend[];
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);