@doki-land/live2d 0.0.15 → 0.0.17
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 +75 -33
- package/dist/index.d.ts +10 -4
- package/dist/index.js +4 -4
- package/package.json +4 -4
- package/src/{create-live2d.ts → facade/create-live2d.ts} +7 -7
- package/src/index.ts +8 -2
- package/src/stage/actor.ts +1 -1
- package/src/{focus.ts → stage/assets/focus.ts} +5 -6
- package/src/stage/model-asset-registry.ts +1 -1
- /package/src/{load-textures.ts → stage/assets/load-textures.ts} +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The public runtime facade for browser-native Live2D rendering.
|
|
4
4
|
|
|
5
|
-
Use this package in browser games, game-engine integrations, interactive content, model tools, and custom webpage
|
|
5
|
+
Use this package in browser games, game-engine integrations, interactive content, model tools, and custom webpage
|
|
6
|
+
experiences. It composes the default loader, MOC runtimes, and rendering backends behind one small API.
|
|
6
7
|
|
|
7
8
|
## ✨ Features
|
|
8
9
|
|
|
@@ -24,19 +25,28 @@ pnpm add @doki-land/live2d
|
|
|
24
25
|
|
|
25
26
|
The implementation packages are installed transitively. Most applications should not depend on them directly.
|
|
26
27
|
|
|
28
|
+
## 🧭 Source Layout
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
src/facade/ createLive2D() — default stage + single actor
|
|
32
|
+
src/motion/ motion3 parse, curves, MotionPlayer
|
|
33
|
+
src/stage/ Live2dStage, actors, asset registry, transforms
|
|
34
|
+
src/reexports/ optional subpath exports for core / loader / renderer
|
|
35
|
+
```
|
|
36
|
+
|
|
27
37
|
## 🚀 Quick Start
|
|
28
38
|
|
|
29
39
|
```ts
|
|
30
|
-
import {
|
|
40
|
+
import {createLive2D} from "@doki-land/live2d";
|
|
31
41
|
|
|
32
42
|
const canvas = document.querySelector<HTMLCanvasElement>("#live2d");
|
|
33
43
|
|
|
34
44
|
if (!canvas) {
|
|
35
|
-
|
|
45
|
+
throw new Error("Missing Live2D canvas");
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
const runtime = createLive2D({
|
|
39
|
-
|
|
49
|
+
prefer: ["webgpu", "webgl2", "canvas2d"],
|
|
40
50
|
});
|
|
41
51
|
|
|
42
52
|
runtime.mount(canvas);
|
|
@@ -45,9 +55,9 @@ await runtime.loadModel("/models/character.model3.json");
|
|
|
45
55
|
let previous = performance.now();
|
|
46
56
|
|
|
47
57
|
function frame(now: number) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
runtime.update((now - previous) / 1000);
|
|
59
|
+
previous = now;
|
|
60
|
+
requestAnimationFrame(frame);
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
requestAnimationFrame(frame);
|
|
@@ -59,11 +69,12 @@ The runtime does not require ownership of `requestAnimationFrame`. Drive it from
|
|
|
59
69
|
|
|
60
70
|
```ts
|
|
61
71
|
engine.onUpdate((deltaTime) => {
|
|
62
|
-
|
|
72
|
+
runtime.update(deltaTime);
|
|
63
73
|
});
|
|
64
74
|
```
|
|
65
75
|
|
|
66
|
-
The delta is expressed in seconds. Keep the value bounded after tab suspension or long pauses to avoid unstable
|
|
76
|
+
The delta is expressed in seconds. Keep the value bounded after tab suspension or long pauses to avoid unstable
|
|
77
|
+
animation and physics once those systems are enabled.
|
|
67
78
|
|
|
68
79
|
## 📥 Loading Models
|
|
69
80
|
|
|
@@ -71,19 +82,19 @@ The delta is expressed in seconds. Keep the value bounded after tab suspension o
|
|
|
71
82
|
await runtime.loadModel("/models/actor.model3.json");
|
|
72
83
|
|
|
73
84
|
await runtime.loadModel(
|
|
74
|
-
|
|
85
|
+
"https://cdn.example.com/models/actor.model3.json",
|
|
75
86
|
);
|
|
76
87
|
|
|
77
88
|
await runtime.loadModel(
|
|
78
|
-
|
|
89
|
+
"npm:live2d-widget-model-hijiki@1.0.5/assets/hijiki.model.json",
|
|
79
90
|
);
|
|
80
91
|
```
|
|
81
92
|
|
|
82
93
|
Listen for progress when presenting a loading interface:
|
|
83
94
|
|
|
84
95
|
```ts
|
|
85
|
-
runtime.events.on("progress", ({
|
|
86
|
-
|
|
96
|
+
runtime.events.on("progress", ({stage, progress, detail}) => {
|
|
97
|
+
console.log(stage, Math.round(progress * 100), detail);
|
|
87
98
|
});
|
|
88
99
|
```
|
|
89
100
|
|
|
@@ -95,8 +106,8 @@ Remote servers must allow cross-origin access to settings, model binaries, and t
|
|
|
95
106
|
runtime.setParameter("PARAM_ANGLE_X", 15);
|
|
96
107
|
|
|
97
108
|
const angleX = runtime
|
|
98
|
-
|
|
99
|
-
|
|
109
|
+
.listParameters()
|
|
110
|
+
.find((parameter) => parameter.id === "PARAM_ANGLE_X");
|
|
100
111
|
|
|
101
112
|
console.log(angleX);
|
|
102
113
|
```
|
|
@@ -111,27 +122,29 @@ Parameter availability and ranges belong to the loaded model. Do not assume ever
|
|
|
111
122
|
const area = runtime.hitTest(modelX, modelY);
|
|
112
123
|
|
|
113
124
|
if (area) {
|
|
114
|
-
|
|
125
|
+
console.log("Hit", area);
|
|
115
126
|
}
|
|
116
127
|
```
|
|
117
128
|
|
|
118
|
-
Current fallback hit testing can identify visible drawables. Semantic names such as `Head` or `Body` require
|
|
129
|
+
Current fallback hit testing can identify visible drawables. Semantic names such as `Head` or `Body` require
|
|
130
|
+
corresponding model metadata and runtime support.
|
|
119
131
|
|
|
120
132
|
## 📊 Frame Profiling
|
|
121
133
|
|
|
122
134
|
```ts
|
|
123
135
|
runtime.events.on("profile", (profile) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
console.log({
|
|
137
|
+
fps: profile.fpsSmooth,
|
|
138
|
+
frameMs: profile.frameMs,
|
|
139
|
+
evaluateMs: profile.evaluateMs,
|
|
140
|
+
drawMs: profile.drawMs,
|
|
141
|
+
drawables: profile.drawableCount,
|
|
142
|
+
});
|
|
131
143
|
});
|
|
132
144
|
```
|
|
133
145
|
|
|
134
|
-
These timings are runtime-side measurements. Use browser GPU profiling when diagnosing shader, mask, upload, or device
|
|
146
|
+
These timings are runtime-side measurements. Use browser GPU profiling when diagnosing shader, mask, upload, or device
|
|
147
|
+
scheduling costs.
|
|
135
148
|
|
|
136
149
|
## 🧩 Custom Pipeline
|
|
137
150
|
|
|
@@ -139,12 +152,13 @@ Advanced applications can provide a renderer or model backends:
|
|
|
139
152
|
|
|
140
153
|
```ts
|
|
141
154
|
const runtime = createLive2D({
|
|
142
|
-
|
|
143
|
-
|
|
155
|
+
renderer: customRenderer,
|
|
156
|
+
backends: [customBackend],
|
|
144
157
|
});
|
|
145
158
|
```
|
|
146
159
|
|
|
147
|
-
Custom implementations must preserve the contracts exported by the renderer package. Avoid moving format or renderer
|
|
160
|
+
Custom implementations must preserve the contracts exported by the renderer package. Avoid moving format or renderer
|
|
161
|
+
logic into application adapters.
|
|
148
162
|
|
|
149
163
|
## 🧹 Lifecycle
|
|
150
164
|
|
|
@@ -152,7 +166,8 @@ Custom implementations must preserve the contracts exported by the renderer pack
|
|
|
152
166
|
runtime.destroy();
|
|
153
167
|
```
|
|
154
168
|
|
|
155
|
-
Destroy the runtime when its canvas or host scene is permanently removed. This releases model state, textures, draw
|
|
169
|
+
Destroy the runtime when its canvas or host scene is permanently removed. This releases model state, textures, draw
|
|
170
|
+
passes, event listeners owned by the runtime, and graphics resources owned by the selected renderer.
|
|
156
171
|
|
|
157
172
|
## ⚡ Performance Notes
|
|
158
173
|
|
|
@@ -160,7 +175,8 @@ Destroy the runtime when its canvas or host scene is permanently removed. This r
|
|
|
160
175
|
- Keep canvas backing dimensions intentional; CSS size alone does not limit GPU pixel work.
|
|
161
176
|
- Prefer an engine-owned loop when integrating with a game.
|
|
162
177
|
- 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
|
|
178
|
+
- Measure the complete frame path before attributing a bottleneck to TypeScript, WebAssembly, or a specific graphics
|
|
179
|
+
API.
|
|
164
180
|
|
|
165
181
|
## 🧪 Development
|
|
166
182
|
|
|
@@ -171,12 +187,38 @@ pnpm typecheck
|
|
|
171
187
|
pnpm --filter @doki-land/live2d build
|
|
172
188
|
```
|
|
173
189
|
|
|
174
|
-
Changes to the facade should include tests for state transitions, cancellation, events, and resource cleanup where
|
|
190
|
+
Changes to the facade should include tests for state transitions, cancellation, events, and resource cleanup where
|
|
191
|
+
applicable.
|
|
175
192
|
|
|
176
193
|
## 🤝 Contributing
|
|
177
194
|
|
|
178
|
-
|
|
195
|
+
### Package ownership
|
|
196
|
+
|
|
197
|
+
- Keep the facade small and host-independent.
|
|
198
|
+
- Put model-format behavior in `@doki-land/live2d-renderer`.
|
|
199
|
+
- Put source resolution in `@doki-land/live2d-loader`.
|
|
200
|
+
- Put webpage chrome in `@doki-land/live2d-widget`.
|
|
201
|
+
- Include tests for state transitions, cancellation, events, and resource cleanup where applicable.
|
|
179
202
|
|
|
180
203
|
## 📄 License
|
|
181
204
|
|
|
182
|
-
|
|
205
|
+
### Implementation independence
|
|
206
|
+
|
|
207
|
+
- This package composes an independently developed clean-room runtime.
|
|
208
|
+
- It does not load, link against, wrap, translate, port, or derive its implementation from an official Cubism SDK or
|
|
209
|
+
Core binary.
|
|
210
|
+
- Compatible model support is an interoperability goal, not evidence of a shared implementation, endorsement,
|
|
211
|
+
affiliation, sponsorship, or employment relationship.
|
|
212
|
+
- The project and its contributors are independent and do not act on behalf of the official Cubism SDK vendor.
|
|
213
|
+
|
|
214
|
+
### Contribution boundary
|
|
215
|
+
|
|
216
|
+
- Do not submit official SDK or shader source, disassembly-derived code, mechanically translated implementation code, or
|
|
217
|
+
changes that require an official runtime.
|
|
218
|
+
- Support compatibility-sensitive work with public format facts, neutral fixtures, reproducible independent
|
|
219
|
+
observations, or independently authored technical rationale.
|
|
220
|
+
|
|
221
|
+
### Terms
|
|
222
|
+
|
|
223
|
+
- See the repository license for source-code terms.
|
|
224
|
+
- Model and artwork licenses are separate from the runtime license.
|
package/dist/index.d.ts
CHANGED
|
@@ -367,11 +367,11 @@ interface Live2DRuntime extends Live2DSession {
|
|
|
367
367
|
declare function createLive2D(options?: CreateLive2DOptions): Live2DRuntime;
|
|
368
368
|
|
|
369
369
|
/**
|
|
370
|
-
* Map canvas focus (-1..1, Y-up) onto
|
|
370
|
+
* Map canvas focus (-1..1, Y-up) onto commonly used model parameter IDs.
|
|
371
371
|
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
372
|
+
* ANGLE_X/Y use the full declared range, ANGLE_Z combines both axes, and body
|
|
373
|
+
* and eye parameters receive the corresponding normalized axis. Parameters not
|
|
374
|
+
* declared by a model are skipped.
|
|
375
375
|
*/
|
|
376
376
|
declare function focusParameterUpdates(parameters: readonly ParameterBinding[], dragX: number, dragY: number): Array<{
|
|
377
377
|
id: string;
|
|
@@ -381,6 +381,12 @@ declare function focusParameterUpdates(parameters: readonly ParameterBinding[],
|
|
|
381
381
|
/**
|
|
382
382
|
* `@doki-land/live2d` — public facade.
|
|
383
383
|
*
|
|
384
|
+
* Layout:
|
|
385
|
+
* - `facade/` — `createLive2D()` default stage + actor entry
|
|
386
|
+
* - `motion/` — motion3 parse + playback
|
|
387
|
+
* - `stage/` — multi-actor stage, assets, transforms
|
|
388
|
+
* - `reexports/` — optional subpath `@doki-land/live2d/{core,loader,renderer}`
|
|
389
|
+
*
|
|
384
390
|
* ```ts
|
|
385
391
|
* import { createLive2D } from "@doki-land/live2d";
|
|
386
392
|
* ```
|
package/dist/index.js
CHANGED
|
@@ -20,14 +20,14 @@ import {
|
|
|
20
20
|
serializeCpuProgram
|
|
21
21
|
} from "@doki-land/live2d-renderer";
|
|
22
22
|
|
|
23
|
-
// src/create-live2d.ts
|
|
23
|
+
// src/facade/create-live2d.ts
|
|
24
24
|
import {
|
|
25
25
|
createMoc2Backend as createMoc2Backend2,
|
|
26
26
|
createMoc3Backend as createMoc3Backend2,
|
|
27
27
|
createRenderer as createRenderer2
|
|
28
28
|
} from "@doki-land/live2d-renderer";
|
|
29
29
|
|
|
30
|
-
// src/focus.ts
|
|
30
|
+
// src/stage/assets/focus.ts
|
|
31
31
|
function focusParameterUpdates(parameters, dragX, dragY) {
|
|
32
32
|
const byId = new Map(parameters.map((p) => [p.id, p]));
|
|
33
33
|
const x = clampUnit(dragX);
|
|
@@ -1195,7 +1195,7 @@ import {
|
|
|
1195
1195
|
selectModelBackend
|
|
1196
1196
|
} from "@doki-land/live2d-renderer";
|
|
1197
1197
|
|
|
1198
|
-
// src/load-textures.ts
|
|
1198
|
+
// src/stage/assets/load-textures.ts
|
|
1199
1199
|
function guessMime(path) {
|
|
1200
1200
|
const lower = path.toLowerCase();
|
|
1201
1201
|
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
@@ -1816,7 +1816,7 @@ function createLive2dStage(options) {
|
|
|
1816
1816
|
return new Live2dStageImpl(options);
|
|
1817
1817
|
}
|
|
1818
1818
|
|
|
1819
|
-
// src/create-live2d.ts
|
|
1819
|
+
// src/facade/create-live2d.ts
|
|
1820
1820
|
function createLive2D(options = {}) {
|
|
1821
1821
|
const backends = options.backends ?? [
|
|
1822
1822
|
createMoc2Backend2(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doki-land/live2d",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "Live2D in the browser — load moc2/moc3 models, Stage + multi-actor, motion; WebGPU/WebGL2/Canvas2D. Main entry for live2d.ts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"test": "vitest run --passWithNoTests"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@doki-land/live2d-core": "0.0.
|
|
63
|
-
"@doki-land/live2d-loader": "0.0.
|
|
64
|
-
"@doki-land/live2d-renderer": "0.0.
|
|
62
|
+
"@doki-land/live2d-core": "0.0.17",
|
|
63
|
+
"@doki-land/live2d-loader": "0.0.17",
|
|
64
|
+
"@doki-land/live2d-renderer": "0.0.17"
|
|
65
65
|
},
|
|
66
66
|
"sideEffects": false
|
|
67
67
|
}
|
|
@@ -3,22 +3,22 @@ import {
|
|
|
3
3
|
createMoc3Backend,
|
|
4
4
|
createRenderer,
|
|
5
5
|
} from "@doki-land/live2d-renderer";
|
|
6
|
-
import { allocateActorId } from "
|
|
6
|
+
import { allocateActorId } from "../stage/actor.js";
|
|
7
7
|
import {
|
|
8
8
|
type CreateLive2DOptions,
|
|
9
9
|
createSingleActorFacade,
|
|
10
10
|
type Live2DRuntime,
|
|
11
|
-
} from "
|
|
12
|
-
import { createLive2dStage } from "
|
|
11
|
+
} from "../stage/single-facade.js";
|
|
12
|
+
import { createLive2dStage } from "../stage/stage.js";
|
|
13
13
|
|
|
14
14
|
export type {
|
|
15
15
|
CreateLive2DOptions,
|
|
16
16
|
Live2DRuntime,
|
|
17
|
-
} from "
|
|
17
|
+
} from "../stage/single-facade.js";
|
|
18
18
|
export {
|
|
19
19
|
MotionPriority,
|
|
20
20
|
type PlayMotionOptions,
|
|
21
|
-
} from "
|
|
21
|
+
} from "../stage/single-facade.js";
|
|
22
22
|
|
|
23
23
|
/** Wire moc backends and a renderer into one single-actor session. */
|
|
24
24
|
export function createLive2D(options: CreateLive2DOptions = {}): Live2DRuntime {
|
|
@@ -31,9 +31,9 @@ export function createLive2D(options: CreateLive2DOptions = {}): Live2DRuntime {
|
|
|
31
31
|
renderer:
|
|
32
32
|
options.renderer ?? createRenderer({ prefer: options.prefer }),
|
|
33
33
|
updateMode: options.updateMode ?? "manual",
|
|
34
|
-
}) as import("
|
|
34
|
+
}) as import("../stage/stage.js").Live2dStageImpl;
|
|
35
35
|
const actor = stage.createActor({
|
|
36
36
|
id: allocateActorId("default"),
|
|
37
|
-
}) as import("
|
|
37
|
+
}) as import("../stage/actor.js").Live2dActorImpl;
|
|
38
38
|
return createSingleActorFacade(stage, actor, backends);
|
|
39
39
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@doki-land/live2d` — public facade.
|
|
3
3
|
*
|
|
4
|
+
* Layout:
|
|
5
|
+
* - `facade/` — `createLive2D()` default stage + actor entry
|
|
6
|
+
* - `motion/` — motion3 parse + playback
|
|
7
|
+
* - `stage/` — multi-actor stage, assets, transforms
|
|
8
|
+
* - `reexports/` — optional subpath `@doki-land/live2d/{core,loader,renderer}`
|
|
9
|
+
*
|
|
4
10
|
* ```ts
|
|
5
11
|
* import { createLive2D } from "@doki-land/live2d";
|
|
6
12
|
* ```
|
|
@@ -66,8 +72,8 @@ export {
|
|
|
66
72
|
type Live2DRuntime,
|
|
67
73
|
MotionPriority,
|
|
68
74
|
type PlayMotionOptions,
|
|
69
|
-
} from "./create-live2d.js";
|
|
70
|
-
export { focusParameterUpdates } from "./focus.js";
|
|
75
|
+
} from "./facade/create-live2d.js";
|
|
76
|
+
export { focusParameterUpdates } from "./stage/assets/focus.js";
|
|
71
77
|
export {
|
|
72
78
|
blendMotionLayers,
|
|
73
79
|
evaluateCurve,
|
package/src/stage/actor.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
PlayMotionActorOptions,
|
|
9
9
|
} from "@doki-land/live2d-core";
|
|
10
10
|
import type { DrawableMesh, Renderer } from "@doki-land/live2d-renderer";
|
|
11
|
-
import { focusParameterUpdates } from "
|
|
11
|
+
import { focusParameterUpdates } from "./assets/focus.js";
|
|
12
12
|
import type { PlayMotionOptions } from "../motion/index.js";
|
|
13
13
|
import { ActorModelSlot } from "./actor-model-slot.js";
|
|
14
14
|
import type { ModelAssetRegistry } from "./model-asset-registry.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ParameterBinding } from "@doki-land/live2d-renderer";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Map canvas focus (-1..1, Y-up) onto
|
|
4
|
+
* Map canvas focus (-1..1, Y-up) onto commonly used model parameter IDs.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* ANGLE_X/Y use the full declared range, ANGLE_Z combines both axes, and body
|
|
7
|
+
* and eye parameters receive the corresponding normalized axis. Parameters not
|
|
8
|
+
* declared by a model are skipped.
|
|
9
9
|
*/
|
|
10
10
|
export function focusParameterUpdates(
|
|
11
11
|
parameters: readonly ParameterBinding[],
|
|
@@ -26,8 +26,7 @@ export function focusParameterUpdates(
|
|
|
26
26
|
set("PARAM_ANGLE_X", x);
|
|
27
27
|
set("PARAM_ANGLE_Y", y);
|
|
28
28
|
set("PARAM_ANGLE_Z", clampUnit(x * y * -1));
|
|
29
|
-
//
|
|
30
|
-
// so full-range normalized drag still matches that relative weight.
|
|
29
|
+
// Each binding maps normalized input through its own declared range.
|
|
31
30
|
set("PARAM_BODY_ANGLE_X", x);
|
|
32
31
|
set("PARAM_BODY_ANGLE_Y", y);
|
|
33
32
|
set("PARAM_EYE_BALL_X", x);
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
compileSharedModelCompile,
|
|
24
24
|
selectModelBackend,
|
|
25
25
|
} from "@doki-land/live2d-renderer";
|
|
26
|
-
import { loadTextureData, releaseTextureData } from "
|
|
26
|
+
import { loadTextureData, releaseTextureData } from "./assets/load-textures.js";
|
|
27
27
|
import type { Motion3Clip } from "../motion/index.js";
|
|
28
28
|
import { resolveModelAssetKey } from "./model-asset-key.js";
|
|
29
29
|
|
|
File without changes
|