@laplace.live/pixijs-live2d 0.0.1
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/LICENSE +22 -0
- package/README.md +68 -0
- package/dist/extra.d.ts +16 -0
- package/dist/extra.js +75 -0
- package/dist/extra.js.map +1 -0
- package/dist/index.d.ts +7023 -0
- package/dist/index.js +16076 -0
- package/dist/index.js.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/package.json +66 -0
- package/vendor/cubism/LICENSE.md +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GuangChen2333
|
|
4
|
+
Copyright (c) 2026 LAPLACE Live!
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# @laplace.live/pixijs-live2d
|
|
2
|
+
|
|
3
|
+
PixiJS v8 Live2D engine for Cubism 3/4/5 models — motions, expressions, physics, pose, lip
|
|
4
|
+
sync with motion sounds, parallel motions, and hit-area picking. The 2D-object engine of
|
|
5
|
+
[LAPLACE Persona](https://github.com/laplace-live/persona); its WebGPU sibling for three.js is
|
|
6
|
+
[`@laplace.live/three-live2d`](https://www.npmjs.com/package/@laplace.live/three-live2d).
|
|
7
|
+
|
|
8
|
+
Forked from
|
|
9
|
+
[untitled-pixi-live2d-engine](https://github.com/Untitled-Story/untitled-pixi-live2d-engine)
|
|
10
|
+
`v1.3.5` (itself a PixiJS v8 continuation of the pixi-live2d-display line). Diverges
|
|
11
|
+
deliberately: Cubism 2 support removed, the CubismWebFramework mirrored pristine at tag
|
|
12
|
+
`5-r.5`, and moc3 v6 (Cubism 5.3 offscreens + new blend modes) rendering verified.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm i @laplace.live/pixijs-live2d pixi.js
|
|
18
|
+
npm i @pixi/sound # optional — motion sounds / audio lip sync
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`pixi.js` ≥ 8.13 is a peer dependency. `@pixi/sound` is an optional peer, dynamic-imported the
|
|
22
|
+
first time audio is used.
|
|
23
|
+
|
|
24
|
+
## Cubism Core
|
|
25
|
+
|
|
26
|
+
Live2D Cubism Core (`live2dcubismcore.js`) is Live2D's proprietary runtime and is **not
|
|
27
|
+
bundled**. Download it with the
|
|
28
|
+
[Cubism SDK for Web](https://www.live2d.com/en/sdk/download/web/), serve it yourself, and load
|
|
29
|
+
it **before this package is imported** — the entry throws immediately when
|
|
30
|
+
`window.Live2DCubismCore` is missing. A `<script>` tag ahead of your bundle works; so does
|
|
31
|
+
evaluating Core first and pulling the engine in with a dynamic `import()`. Adding the SDK's
|
|
32
|
+
`live2dcubismcore.d.ts` to your project completes the typings.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { Live2DModel } from '@laplace.live/pixijs-live2d'
|
|
38
|
+
import { Application, Ticker } from 'pixi.js'
|
|
39
|
+
|
|
40
|
+
const app = new Application()
|
|
41
|
+
await app.init({ backgroundAlpha: 0 })
|
|
42
|
+
|
|
43
|
+
Live2DModel.registerTicker(Ticker) // drive motion updates from pixi's shared ticker
|
|
44
|
+
|
|
45
|
+
const model = await Live2DModel.from('https://example.com/hiyori/hiyori.model3.json')
|
|
46
|
+
app.stage.addChild(model)
|
|
47
|
+
model.scale.set(0.25)
|
|
48
|
+
|
|
49
|
+
model.on('hit', (hitAreas) => {
|
|
50
|
+
if (hitAreas.includes('Body')) model.motion('TapBody')
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`@laplace.live/pixijs-live2d/extra` ships `HitAreaFrames`, a debug overlay that draws the
|
|
55
|
+
model's hit areas.
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
The engine (`src/`) is [MIT](./LICENSE), carrying the upstream fork's copyright alongside ours.
|
|
60
|
+
It compiles in a pristine mirror of
|
|
61
|
+
[Live2D/CubismWebFramework](https://github.com/Live2D/CubismWebFramework), covered by the
|
|
62
|
+
[Live2D Open Software License](https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html)
|
|
63
|
+
— its license text ships in this package at `vendor/cubism/LICENSE.md`. Live2D Cubism Core is
|
|
64
|
+
not included; it is distributed by Live2D under the
|
|
65
|
+
[Live2D Proprietary Software License](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html),
|
|
66
|
+
and businesses above the revenue threshold also need a
|
|
67
|
+
[Cubism SDK Release License](https://www.live2d.com/en/sdk/license/). Review Live2D's terms
|
|
68
|
+
before shipping a product.
|
package/dist/extra.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Graphics, Renderer, Text } from "pixi.js";
|
|
2
|
+
//#region src/tools/HitAreaFrames.d.ts
|
|
3
|
+
declare class HitAreaFrames extends Graphics {
|
|
4
|
+
initialized: boolean;
|
|
5
|
+
texts: Text[];
|
|
6
|
+
strokeWidth: number;
|
|
7
|
+
normalColor: number;
|
|
8
|
+
activeColor: number;
|
|
9
|
+
constructor();
|
|
10
|
+
private readonly handleInit;
|
|
11
|
+
private readonly handlePointerMove;
|
|
12
|
+
protected _render(renderer: Renderer): void;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { HitAreaFrames };
|
|
16
|
+
//# sourceMappingURL=extra.d.ts.map
|
package/dist/extra.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Graphics, Rectangle, Text, TextStyle } from "pixi.js";
|
|
2
|
+
//#region src/tools/HitAreaFrames.ts
|
|
3
|
+
const tempBounds = new Rectangle();
|
|
4
|
+
var HitAreaFrames = class extends Graphics {
|
|
5
|
+
initialized = false;
|
|
6
|
+
texts = [];
|
|
7
|
+
strokeWidth = 4;
|
|
8
|
+
normalColor = 14883354;
|
|
9
|
+
activeColor = 2017330;
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
this.eventMode = "static";
|
|
13
|
+
this.on("added", this.handleInit).on("globalpointermove", this.handlePointerMove);
|
|
14
|
+
}
|
|
15
|
+
handleInit = () => {
|
|
16
|
+
const internalModel = this.parent.internalModel;
|
|
17
|
+
const textStyle = new TextStyle({
|
|
18
|
+
fontSize: 24,
|
|
19
|
+
fill: "#ffffff",
|
|
20
|
+
stroke: {
|
|
21
|
+
color: "#000000",
|
|
22
|
+
width: 4
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
this.texts = Object.keys(internalModel.hitAreas).map((hitAreaName) => {
|
|
26
|
+
const text = new Text({
|
|
27
|
+
text: hitAreaName,
|
|
28
|
+
style: textStyle
|
|
29
|
+
});
|
|
30
|
+
text.visible = false;
|
|
31
|
+
this.addChild(text);
|
|
32
|
+
return text;
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
handlePointerMove = (e) => {
|
|
36
|
+
const hitAreaNames = this.parent.hitTest(e.global.x, e.global.y);
|
|
37
|
+
this.texts.forEach((text) => {
|
|
38
|
+
text.visible = hitAreaNames.includes(text.text);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
_render(renderer) {
|
|
42
|
+
const internalModel = this.parent.internalModel;
|
|
43
|
+
const matrix = this.worldTransform;
|
|
44
|
+
const scale = 1 / Math.sqrt(matrix.a ** 2 + matrix.b ** 2);
|
|
45
|
+
this.texts.forEach((text) => {
|
|
46
|
+
const hitArea = internalModel.hitAreas[text.text];
|
|
47
|
+
if (!hitArea) return;
|
|
48
|
+
let drawIndex = hitArea.index;
|
|
49
|
+
if (drawIndex < 0) {
|
|
50
|
+
drawIndex = internalModel.getDrawableIndex(hitArea.id);
|
|
51
|
+
if (drawIndex < 0) return;
|
|
52
|
+
hitArea.index = drawIndex;
|
|
53
|
+
}
|
|
54
|
+
const bounds = internalModel.getDrawableBounds(drawIndex, tempBounds);
|
|
55
|
+
const transform = internalModel.localTransform;
|
|
56
|
+
bounds.x = bounds.x * transform.a + transform.tx;
|
|
57
|
+
bounds.y = bounds.y * transform.d + transform.ty;
|
|
58
|
+
bounds.width = bounds.width * transform.a;
|
|
59
|
+
bounds.height = bounds.height * transform.d;
|
|
60
|
+
this.setStrokeStyle({
|
|
61
|
+
width: this.strokeWidth * scale,
|
|
62
|
+
color: text.visible ? this.activeColor : this.normalColor
|
|
63
|
+
}).rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
64
|
+
text.x = bounds.x + this.strokeWidth * scale;
|
|
65
|
+
text.y = bounds.y + this.strokeWidth * scale;
|
|
66
|
+
text.scale.set(scale);
|
|
67
|
+
});
|
|
68
|
+
Graphics.prototype._render.call(this, renderer);
|
|
69
|
+
this.clear();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
export { HitAreaFrames };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=extra.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extra.js","names":[],"sources":["../src/tools/HitAreaFrames.ts"],"sourcesContent":["// noinspection JSUnusedGlobalSymbols\n\nimport type { Renderer } from 'pixi.js'\nimport { type FederatedPointerEvent, Graphics, Rectangle, Text, TextStyle } from 'pixi.js'\n\nimport type { Live2DModel } from '@/Live2DModel'\n\nconst tempBounds = new Rectangle()\n\nexport class HitAreaFrames extends Graphics {\n initialized = false\n\n texts: Text[] = []\n\n strokeWidth = 4\n normalColor = 0xe31a1a\n activeColor = 0x1ec832\n\n constructor() {\n super()\n\n this.eventMode = 'static'\n\n this.on('added', this.handleInit).on('globalpointermove', this.handlePointerMove)\n }\n\n private readonly handleInit = () => {\n const internalModel = (this.parent as Live2DModel).internalModel\n\n const textStyle = new TextStyle({\n fontSize: 24,\n fill: '#ffffff',\n stroke: { color: '#000000', width: 4 },\n })\n\n this.texts = Object.keys(internalModel.hitAreas).map(hitAreaName => {\n const text = new Text({\n text: hitAreaName,\n style: textStyle,\n })\n\n text.visible = false\n\n this.addChild(text)\n\n return text\n })\n }\n\n private readonly handlePointerMove = (e: FederatedPointerEvent) => {\n const hitAreaNames = (this.parent as Live2DModel).hitTest(e.global.x, e.global.y)\n\n this.texts.forEach(text => {\n text.visible = hitAreaNames.includes(text.text)\n })\n }\n\n protected _render(renderer: Renderer): void {\n const internalModel = (this.parent as Live2DModel).internalModel\n\n // extract scale from the transform matrix, and invert it to ease following calculation\n // https://math.stackexchange.com/a/13165\n const matrix = this.worldTransform\n const scale = 1 / Math.sqrt(matrix.a ** 2 + matrix.b ** 2)\n\n this.texts.forEach(text => {\n const hitArea = internalModel.hitAreas[text.text]\n\n if (!hitArea) {\n return\n }\n\n let drawIndex = hitArea.index\n\n if (drawIndex < 0) {\n drawIndex = internalModel.getDrawableIndex(hitArea.id)\n\n if (drawIndex < 0) {\n return\n }\n\n hitArea.index = drawIndex\n }\n\n const bounds = internalModel.getDrawableBounds(drawIndex, tempBounds)\n const transform = internalModel.localTransform\n\n bounds.x = bounds.x * transform.a + transform.tx\n bounds.y = bounds.y * transform.d + transform.ty\n bounds.width = bounds.width * transform.a\n bounds.height = bounds.height * transform.d\n\n this.setStrokeStyle({\n width: this.strokeWidth * scale,\n color: text.visible ? this.activeColor : this.normalColor,\n }).rect(bounds.x, bounds.y, bounds.width, bounds.height)\n\n text.x = bounds.x + this.strokeWidth * scale\n text.y = bounds.y + this.strokeWidth * scale\n text.scale.set(scale)\n })\n\n const render = (\n Graphics.prototype as unknown as {\n _render: (renderer: Renderer) => void\n }\n )._render\n render.call(this, renderer)\n\n this.clear()\n }\n}\n"],"mappings":";;AAOA,MAAM,aAAa,IAAI,UAAU;AAEjC,IAAa,gBAAb,cAAmC,SAAS;CAC1C,cAAc;CAEd,QAAgB,CAAC;CAEjB,cAAc;CACd,cAAc;CACd,cAAc;CAEd,cAAc;EACZ,MAAM;EAEN,KAAK,YAAY;EAEjB,KAAK,GAAG,SAAS,KAAK,UAAU,CAAC,CAAC,GAAG,qBAAqB,KAAK,iBAAiB;CAClF;CAEA,mBAAoC;EAClC,MAAM,gBAAiB,KAAK,OAAuB;EAEnD,MAAM,YAAY,IAAI,UAAU;GAC9B,UAAU;GACV,MAAM;GACN,QAAQ;IAAE,OAAO;IAAW,OAAO;GAAE;EACvC,CAAC;EAED,KAAK,QAAQ,OAAO,KAAK,cAAc,QAAQ,CAAC,CAAC,KAAI,gBAAe;GAClE,MAAM,OAAO,IAAI,KAAK;IACpB,MAAM;IACN,OAAO;GACT,CAAC;GAED,KAAK,UAAU;GAEf,KAAK,SAAS,IAAI;GAElB,OAAO;EACT,CAAC;CACH;CAEA,qBAAsC,MAA6B;EACjE,MAAM,eAAgB,KAAK,OAAuB,QAAQ,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC;EAEhF,KAAK,MAAM,SAAQ,SAAQ;GACzB,KAAK,UAAU,aAAa,SAAS,KAAK,IAAI;EAChD,CAAC;CACH;CAEA,QAAkB,UAA0B;EAC1C,MAAM,gBAAiB,KAAK,OAAuB;EAInD,MAAM,SAAS,KAAK;EACpB,MAAM,QAAQ,IAAI,KAAK,KAAK,OAAO,KAAK,IAAI,OAAO,KAAK,CAAC;EAEzD,KAAK,MAAM,SAAQ,SAAQ;GACzB,MAAM,UAAU,cAAc,SAAS,KAAK;GAE5C,IAAI,CAAC,SACH;GAGF,IAAI,YAAY,QAAQ;GAExB,IAAI,YAAY,GAAG;IACjB,YAAY,cAAc,iBAAiB,QAAQ,EAAE;IAErD,IAAI,YAAY,GACd;IAGF,QAAQ,QAAQ;GAClB;GAEA,MAAM,SAAS,cAAc,kBAAkB,WAAW,UAAU;GACpE,MAAM,YAAY,cAAc;GAEhC,OAAO,IAAI,OAAO,IAAI,UAAU,IAAI,UAAU;GAC9C,OAAO,IAAI,OAAO,IAAI,UAAU,IAAI,UAAU;GAC9C,OAAO,QAAQ,OAAO,QAAQ,UAAU;GACxC,OAAO,SAAS,OAAO,SAAS,UAAU;GAE1C,KAAK,eAAe;IAClB,OAAO,KAAK,cAAc;IAC1B,OAAO,KAAK,UAAU,KAAK,cAAc,KAAK;GAChD,CAAC,CAAC,CAAC,KAAK,OAAO,GAAG,OAAO,GAAG,OAAO,OAAO,OAAO,MAAM;GAEvD,KAAK,IAAI,OAAO,IAAI,KAAK,cAAc;GACvC,KAAK,IAAI,OAAO,IAAI,KAAK,cAAc;GACvC,KAAK,MAAM,IAAI,KAAK;EACtB,CAAC;EAOD,SAJW,UAGT,QACK,KAAK,MAAM,QAAQ;EAE1B,KAAK,MAAM;CACb;AACF"}
|