@doki-land/live2d 0.0.16 → 0.0.18
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 +50 -17
- package/dist/index.d.ts +10 -4
- package/dist/index.js +33 -33
- 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
|
@@ -25,19 +25,28 @@ pnpm add @doki-land/live2d
|
|
|
25
25
|
|
|
26
26
|
The implementation packages are installed transitively. Most applications should not depend on them directly.
|
|
27
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
|
+
|
|
28
37
|
## 🚀 Quick Start
|
|
29
38
|
|
|
30
39
|
```ts
|
|
31
|
-
import {
|
|
40
|
+
import {createLive2D} from "@doki-land/live2d";
|
|
32
41
|
|
|
33
42
|
const canvas = document.querySelector<HTMLCanvasElement>("#live2d");
|
|
34
43
|
|
|
35
44
|
if (!canvas) {
|
|
36
|
-
|
|
45
|
+
throw new Error("Missing Live2D canvas");
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
const runtime = createLive2D({
|
|
40
|
-
|
|
49
|
+
prefer: ["webgpu", "webgl2", "canvas2d"],
|
|
41
50
|
});
|
|
42
51
|
|
|
43
52
|
runtime.mount(canvas);
|
|
@@ -46,9 +55,9 @@ await runtime.loadModel("/models/character.model3.json");
|
|
|
46
55
|
let previous = performance.now();
|
|
47
56
|
|
|
48
57
|
function frame(now: number) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
runtime.update((now - previous) / 1000);
|
|
59
|
+
previous = now;
|
|
60
|
+
requestAnimationFrame(frame);
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
requestAnimationFrame(frame);
|
|
@@ -60,7 +69,7 @@ The runtime does not require ownership of `requestAnimationFrame`. Drive it from
|
|
|
60
69
|
|
|
61
70
|
```ts
|
|
62
71
|
engine.onUpdate((deltaTime) => {
|
|
63
|
-
|
|
72
|
+
runtime.update(deltaTime);
|
|
64
73
|
});
|
|
65
74
|
```
|
|
66
75
|
|
|
@@ -73,11 +82,11 @@ animation and physics once those systems are enabled.
|
|
|
73
82
|
await runtime.loadModel("/models/actor.model3.json");
|
|
74
83
|
|
|
75
84
|
await runtime.loadModel(
|
|
76
|
-
|
|
85
|
+
"https://cdn.example.com/models/actor.model3.json",
|
|
77
86
|
);
|
|
78
87
|
|
|
79
88
|
await runtime.loadModel(
|
|
80
|
-
|
|
89
|
+
"npm:live2d-widget-model-hijiki@1.0.5/assets/hijiki.model.json",
|
|
81
90
|
);
|
|
82
91
|
```
|
|
83
92
|
|
|
@@ -97,8 +106,8 @@ Remote servers must allow cross-origin access to settings, model binaries, and t
|
|
|
97
106
|
runtime.setParameter("PARAM_ANGLE_X", 15);
|
|
98
107
|
|
|
99
108
|
const angleX = runtime
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
.listParameters()
|
|
110
|
+
.find((parameter) => parameter.id === "PARAM_ANGLE_X");
|
|
102
111
|
|
|
103
112
|
console.log(angleX);
|
|
104
113
|
```
|
|
@@ -113,7 +122,7 @@ Parameter availability and ranges belong to the loaded model. Do not assume ever
|
|
|
113
122
|
const area = runtime.hitTest(modelX, modelY);
|
|
114
123
|
|
|
115
124
|
if (area) {
|
|
116
|
-
|
|
125
|
+
console.log("Hit", area);
|
|
117
126
|
}
|
|
118
127
|
```
|
|
119
128
|
|
|
@@ -143,8 +152,8 @@ Advanced applications can provide a renderer or model backends:
|
|
|
143
152
|
|
|
144
153
|
```ts
|
|
145
154
|
const runtime = createLive2D({
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
renderer: customRenderer,
|
|
156
|
+
backends: [customBackend],
|
|
148
157
|
});
|
|
149
158
|
```
|
|
150
159
|
|
|
@@ -183,9 +192,33 @@ applicable.
|
|
|
183
192
|
|
|
184
193
|
## 🤝 Contributing
|
|
185
194
|
|
|
186
|
-
|
|
187
|
-
|
|
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.
|
|
188
202
|
|
|
189
203
|
## 📄 License
|
|
190
204
|
|
|
191
|
-
|
|
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,43 +20,13 @@ 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
|
|
31
|
-
function focusParameterUpdates(parameters, dragX, dragY) {
|
|
32
|
-
const byId = new Map(parameters.map((p) => [p.id, p]));
|
|
33
|
-
const x = clampUnit(dragX);
|
|
34
|
-
const y = clampUnit(dragY);
|
|
35
|
-
const out = [];
|
|
36
|
-
const set = (id, normalized) => {
|
|
37
|
-
const binding = byId.get(id);
|
|
38
|
-
if (!binding) return;
|
|
39
|
-
out.push({ id, value: valueFromNormalized(binding, normalized) });
|
|
40
|
-
};
|
|
41
|
-
set("PARAM_ANGLE_X", x);
|
|
42
|
-
set("PARAM_ANGLE_Y", y);
|
|
43
|
-
set("PARAM_ANGLE_Z", clampUnit(x * y * -1));
|
|
44
|
-
set("PARAM_BODY_ANGLE_X", x);
|
|
45
|
-
set("PARAM_BODY_ANGLE_Y", y);
|
|
46
|
-
set("PARAM_EYE_BALL_X", x);
|
|
47
|
-
set("PARAM_EYE_BALL_Y", y);
|
|
48
|
-
return out;
|
|
49
|
-
}
|
|
50
|
-
function clampUnit(n) {
|
|
51
|
-
if (n > 1) return 1;
|
|
52
|
-
if (n < -1) return -1;
|
|
53
|
-
return n;
|
|
54
|
-
}
|
|
55
|
-
function valueFromNormalized(binding, normalized) {
|
|
56
|
-
const n = clampUnit(normalized);
|
|
57
|
-
return n >= 0 ? binding.defaultValue + (binding.max - binding.defaultValue) * n : binding.defaultValue + (binding.defaultValue - binding.min) * n;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
30
|
// src/motion/evaluate-curve.ts
|
|
61
31
|
function evaluateMotion3(clip, timeSeconds) {
|
|
62
32
|
const t = clamp(timeSeconds, 0, clip.duration);
|
|
@@ -762,6 +732,36 @@ var ActorModelSlot = class {
|
|
|
762
732
|
}
|
|
763
733
|
};
|
|
764
734
|
|
|
735
|
+
// src/stage/assets/focus.ts
|
|
736
|
+
function focusParameterUpdates(parameters, dragX, dragY) {
|
|
737
|
+
const byId = new Map(parameters.map((p) => [p.id, p]));
|
|
738
|
+
const x = clampUnit(dragX);
|
|
739
|
+
const y = clampUnit(dragY);
|
|
740
|
+
const out = [];
|
|
741
|
+
const set = (id, normalized) => {
|
|
742
|
+
const binding = byId.get(id);
|
|
743
|
+
if (!binding) return;
|
|
744
|
+
out.push({ id, value: valueFromNormalized(binding, normalized) });
|
|
745
|
+
};
|
|
746
|
+
set("PARAM_ANGLE_X", x);
|
|
747
|
+
set("PARAM_ANGLE_Y", y);
|
|
748
|
+
set("PARAM_ANGLE_Z", clampUnit(x * y * -1));
|
|
749
|
+
set("PARAM_BODY_ANGLE_X", x);
|
|
750
|
+
set("PARAM_BODY_ANGLE_Y", y);
|
|
751
|
+
set("PARAM_EYE_BALL_X", x);
|
|
752
|
+
set("PARAM_EYE_BALL_Y", y);
|
|
753
|
+
return out;
|
|
754
|
+
}
|
|
755
|
+
function clampUnit(n) {
|
|
756
|
+
if (n > 1) return 1;
|
|
757
|
+
if (n < -1) return -1;
|
|
758
|
+
return n;
|
|
759
|
+
}
|
|
760
|
+
function valueFromNormalized(binding, normalized) {
|
|
761
|
+
const n = clampUnit(normalized);
|
|
762
|
+
return n >= 0 ? binding.defaultValue + (binding.max - binding.defaultValue) * n : binding.defaultValue + (binding.defaultValue - binding.min) * n;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
765
|
// src/stage/transform.ts
|
|
766
766
|
import { DEFAULT_ACTOR_TRANSFORM } from "@doki-land/live2d-core";
|
|
767
767
|
function resolveActorTransform(patch) {
|
|
@@ -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.18",
|
|
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.18",
|
|
63
|
+
"@doki-land/live2d-loader": "0.0.18",
|
|
64
|
+
"@doki-land/live2d-renderer": "0.0.18"
|
|
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,7 @@ 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";
|
|
71
76
|
export {
|
|
72
77
|
blendMotionLayers,
|
|
73
78
|
evaluateCurve,
|
|
@@ -77,6 +82,7 @@ export {
|
|
|
77
82
|
MotionPlayer,
|
|
78
83
|
parseMotion3,
|
|
79
84
|
} from "./motion/index.js";
|
|
85
|
+
export { focusParameterUpdates } from "./stage/assets/focus.js";
|
|
80
86
|
export {
|
|
81
87
|
type CreateLive2dStageFullOptions,
|
|
82
88
|
createLive2dStage,
|
package/src/stage/actor.ts
CHANGED
|
@@ -8,9 +8,9 @@ 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 "../focus.js";
|
|
12
11
|
import type { PlayMotionOptions } from "../motion/index.js";
|
|
13
12
|
import { ActorModelSlot } from "./actor-model-slot.js";
|
|
13
|
+
import { focusParameterUpdates } from "./assets/focus.js";
|
|
14
14
|
import type { ModelAssetRegistry } from "./model-asset-registry.js";
|
|
15
15
|
import {
|
|
16
16
|
resolveActorTransform,
|
|
@@ -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,8 +23,8 @@ import {
|
|
|
23
23
|
compileSharedModelCompile,
|
|
24
24
|
selectModelBackend,
|
|
25
25
|
} from "@doki-land/live2d-renderer";
|
|
26
|
-
import { loadTextureData, releaseTextureData } from "../load-textures.js";
|
|
27
26
|
import type { Motion3Clip } from "../motion/index.js";
|
|
27
|
+
import { loadTextureData, releaseTextureData } from "./assets/load-textures.js";
|
|
28
28
|
import { resolveModelAssetKey } from "./model-asset-key.js";
|
|
29
29
|
|
|
30
30
|
function lerp(a: number, b: number, t: number): number {
|
|
File without changes
|