@doki-land/live2d-renderer 0.0.17 → 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/dist/index.d.ts +133 -133
- package/dist/index.js +91 -91
- package/package.json +2 -2
- package/src/index.ts +37 -37
package/dist/index.d.ts
CHANGED
|
@@ -138,123 +138,6 @@ declare class WebGpuRendererImpl implements WebGpuRenderer {
|
|
|
138
138
|
declare function createWebGpuRenderer(options?: WebGpuRendererOptions): WebGpuRenderer;
|
|
139
139
|
declare function isWebGpuAvailable(): boolean;
|
|
140
140
|
|
|
141
|
-
/**
|
|
142
|
-
* Map Live2D blend modes to WebGL / Canvas compositing.
|
|
143
|
-
*/
|
|
144
|
-
|
|
145
|
-
/** Apply premultiplied-friendly Live2D blend factors on a WebGL2 context. */
|
|
146
|
-
declare function applyWebGl2BlendMode(gl: WebGL2RenderingContext, mode: BlendMode): void;
|
|
147
|
-
/** Canvas2D globalCompositeOperation for Live2D blend modes. */
|
|
148
|
-
declare function canvasCompositeForBlendMode(mode: BlendMode): GlobalCompositeOperation;
|
|
149
|
-
/** WebGPU blend state for Live2D modes (straight alpha draw path). */
|
|
150
|
-
declare function webGpuBlendState(mode: BlendMode): GPUBlendState;
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Clipping-mask context grouping + mask atlas layout.
|
|
154
|
-
*
|
|
155
|
-
* Two packing modes:
|
|
156
|
-
* - `uv-grid`: √N UV cells, alpha channel only (Canvas2D / simple path)
|
|
157
|
-
* - `rgba`: Cubism-style channel × UV packing (≤4 → full UV on R/G/B/A)
|
|
158
|
-
*/
|
|
159
|
-
interface ClippingDrawableRef {
|
|
160
|
-
readonly index: number;
|
|
161
|
-
readonly maskIndices: readonly number[];
|
|
162
|
-
readonly invertedMask: boolean;
|
|
163
|
-
}
|
|
164
|
-
interface ClippingContext {
|
|
165
|
-
/** Stable key: invert flag + sorted mask drawable indices. */
|
|
166
|
-
readonly key: string;
|
|
167
|
-
readonly maskIndices: readonly number[];
|
|
168
|
-
readonly clippedIndices: readonly number[];
|
|
169
|
-
readonly invertedMask: boolean;
|
|
170
|
-
}
|
|
171
|
-
/** UV-space rectangle inside the shared mask atlas (0..1). */
|
|
172
|
-
interface MaskLayoutRect {
|
|
173
|
-
readonly x: number;
|
|
174
|
-
readonly y: number;
|
|
175
|
-
readonly width: number;
|
|
176
|
-
readonly height: number;
|
|
177
|
-
}
|
|
178
|
-
/** R/G/B/A write/sample selector (Cubism channelFlag). */
|
|
179
|
-
type MaskChannelFlag = readonly [number, number, number, number];
|
|
180
|
-
declare const MASK_CHANNEL_FLAGS: readonly MaskChannelFlag[];
|
|
181
|
-
type MaskAtlasMode = "uv-grid" | "rgba";
|
|
182
|
-
interface MaskAtlasOptions {
|
|
183
|
-
/** Packing strategy. Default `rgba` (Cubism density). */
|
|
184
|
-
readonly mode?: MaskAtlasMode;
|
|
185
|
-
/** Cell inset for `uv-grid` only (reduces neighbour bleed). */
|
|
186
|
-
readonly inset?: number;
|
|
187
|
-
/** Render-texture count for `rgba` (default 1 → max 36). */
|
|
188
|
-
readonly renderTextureCount?: number;
|
|
189
|
-
}
|
|
190
|
-
interface LaidOutClippingContext extends ClippingContext {
|
|
191
|
-
readonly layout: MaskLayoutRect;
|
|
192
|
-
/** 0=R … 3=A. `uv-grid` always uses A (3). */
|
|
193
|
-
readonly channelIndex: number;
|
|
194
|
-
readonly channelFlag: MaskChannelFlag;
|
|
195
|
-
/** Cubism multi-RT buffer index; currently always 0. */
|
|
196
|
-
readonly bufferIndex: number;
|
|
197
|
-
/**
|
|
198
|
-
* Expanded AABB of clipped drawables in vertex/NDC space.
|
|
199
|
-
* Mask write/sample map this rect into `layout` (Cubism bounds-fit).
|
|
200
|
-
* Default before `fitClippingContexts`: full NDC (-1..1)².
|
|
201
|
-
*/
|
|
202
|
-
readonly modelBounds: MaskLayoutRect;
|
|
203
|
-
}
|
|
204
|
-
/** Full clip-space quad used when no valid clipped bounds exist. */
|
|
205
|
-
declare const FULL_NDC_BOUNDS: MaskLayoutRect;
|
|
206
|
-
interface ClippingPartition {
|
|
207
|
-
readonly contexts: readonly LaidOutClippingContext[];
|
|
208
|
-
/** Drawables that consume a mask (drawn after mask pass). */
|
|
209
|
-
readonly clipped: ReadonlySet<number>;
|
|
210
|
-
/** Drawables used as masks (mask buffer only, not color target). */
|
|
211
|
-
readonly maskOnly: ReadonlySet<number>;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Group drawables that consume clipping masks into shared contexts.
|
|
215
|
-
* Drawables with empty maskIndices are omitted.
|
|
216
|
-
*/
|
|
217
|
-
declare function buildClippingContexts(drawables: readonly ClippingDrawableRef[]): ClippingContext[];
|
|
218
|
-
/**
|
|
219
|
-
* Pack clipping contexts into a square-ish UV grid on one atlas (alpha only).
|
|
220
|
-
*/
|
|
221
|
-
declare function layoutMaskAtlasUvGrid(contexts: readonly ClippingContext[], options?: {
|
|
222
|
-
inset?: number;
|
|
223
|
-
}): LaidOutClippingContext[];
|
|
224
|
-
/**
|
|
225
|
-
* Cubism `setupLayoutBounds`: pack across R/G/B/A then UV cells.
|
|
226
|
-
* Single RT → max 36 (4×9); multi-RT → 32 per sheet.
|
|
227
|
-
*/
|
|
228
|
-
declare function layoutMaskAtlasRgba(contexts: readonly ClippingContext[], options?: {
|
|
229
|
-
renderTextureCount?: number;
|
|
230
|
-
}): LaidOutClippingContext[];
|
|
231
|
-
/** Pack clipping contexts (default: Cubism RGBA density). */
|
|
232
|
-
declare function layoutMaskAtlas(contexts: readonly ClippingContext[], options?: MaskAtlasOptions): LaidOutClippingContext[];
|
|
233
|
-
/** Partition drawables and assign atlas layouts. */
|
|
234
|
-
declare function partitionForClipping(drawables: readonly ClippingDrawableRef[], options?: MaskAtlasOptions): ClippingPartition;
|
|
235
|
-
/** Axis-aligned bounds of interleaved xy vertex positions. */
|
|
236
|
-
declare function calcVertexBounds(positions: Float32Array): MaskLayoutRect | null;
|
|
237
|
-
/** Expand bounds by a relative margin on each axis (Cubism uses 0.05). */
|
|
238
|
-
declare function expandBounds(bounds: MaskLayoutRect, margin?: number): MaskLayoutRect;
|
|
239
|
-
interface MaskBoundsMeshRef {
|
|
240
|
-
readonly index: number;
|
|
241
|
-
readonly vertexPositions: Float32Array;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Union AABB of clipped drawables, expanded by margin (Cubism
|
|
245
|
-
* `calcClippedDrawableTotalBounds` + 0.05 expand).
|
|
246
|
-
*/
|
|
247
|
-
declare function calcClippedDrawableBounds(clippedIndices: readonly number[], byIndex: ReadonlyMap<number, MaskBoundsMeshRef>, margin?: number): MaskLayoutRect;
|
|
248
|
-
/**
|
|
249
|
-
* Attach per-context `modelBounds` so mask write/sample fit the clipped
|
|
250
|
-
* drawable AABB into the atlas cell (higher mask texel density).
|
|
251
|
-
*/
|
|
252
|
-
declare function fitClippingContexts(contexts: readonly LaidOutClippingContext[], byIndex: ReadonlyMap<number, MaskBoundsMeshRef>, margin?: number): LaidOutClippingContext[];
|
|
253
|
-
/** Flatten layout to `[x, y, w, h]` for GPU uniforms. */
|
|
254
|
-
declare function maskLayoutVec4(layout: MaskLayoutRect): Float32Array;
|
|
255
|
-
/** Flatten channel flag to `[r, g, b, a]` for GPU uniforms. */
|
|
256
|
-
declare function maskChannelVec4(flag: MaskChannelFlag): Float32Array;
|
|
257
|
-
|
|
258
141
|
declare const CPU_PROGRAM_KIND: "cpu-program";
|
|
259
142
|
interface CpuProgramFile {
|
|
260
143
|
readonly kind: typeof CPU_PROGRAM_KIND;
|
|
@@ -299,22 +182,6 @@ declare function evaluateFrame(instance: ModelInstance): FrameSnapshot;
|
|
|
299
182
|
/** Stable fingerprint for golden tests (positions + topology). */
|
|
300
183
|
declare function fingerprintSnapshot(snapshot: FrameSnapshot): string;
|
|
301
184
|
|
|
302
|
-
interface CreateRendererOptions {
|
|
303
|
-
/**
|
|
304
|
-
* Backend try order at initialize time.
|
|
305
|
-
* Default: `["webgpu", "webgl2", "canvas2d"]`.
|
|
306
|
-
*/
|
|
307
|
-
prefer?: RendererKind[];
|
|
308
|
-
webgpu?: WebGpuRendererOptions;
|
|
309
|
-
webgl2?: WebGl2RendererOptions;
|
|
310
|
-
canvas2d?: Canvas2DRendererOptions;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Create a renderer provider. Actual backend is chosen on `initialize`
|
|
314
|
-
* (WebGPU → WebGL2 → Canvas2D by default).
|
|
315
|
-
*/
|
|
316
|
-
declare function createRenderer(options?: CreateRendererOptions): Renderer;
|
|
317
|
-
|
|
318
185
|
/**
|
|
319
186
|
* moc binary format peek. Settings JSON detection lives in live2d-core
|
|
320
187
|
* (`detectModelSettingsFormat`) so loader and renderer share one helper.
|
|
@@ -522,6 +389,123 @@ declare function applyMoc3Glues(positionsByMesh: Map<number, Float32Array>, glue
|
|
|
522
389
|
/** Mean Euclidean distance between glued vertex pairs (for tests). */
|
|
523
390
|
declare function meanGlueSeamDistance(positionsByMesh: ReadonlyMap<number, Float32Array>, glue: Moc3GlueDef): number;
|
|
524
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Map Live2D blend modes to WebGL / Canvas compositing.
|
|
394
|
+
*/
|
|
395
|
+
|
|
396
|
+
/** Apply premultiplied-friendly Live2D blend factors on a WebGL2 context. */
|
|
397
|
+
declare function applyWebGl2BlendMode(gl: WebGL2RenderingContext, mode: BlendMode): void;
|
|
398
|
+
/** Canvas2D globalCompositeOperation for Live2D blend modes. */
|
|
399
|
+
declare function canvasCompositeForBlendMode(mode: BlendMode): GlobalCompositeOperation;
|
|
400
|
+
/** WebGPU blend state for Live2D modes (straight alpha draw path). */
|
|
401
|
+
declare function webGpuBlendState(mode: BlendMode): GPUBlendState;
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Clipping-mask context grouping + mask atlas layout.
|
|
405
|
+
*
|
|
406
|
+
* Two packing modes:
|
|
407
|
+
* - `uv-grid`: √N UV cells, alpha channel only (Canvas2D / simple path)
|
|
408
|
+
* - `rgba`: Cubism-style channel × UV packing (≤4 → full UV on R/G/B/A)
|
|
409
|
+
*/
|
|
410
|
+
interface ClippingDrawableRef {
|
|
411
|
+
readonly index: number;
|
|
412
|
+
readonly maskIndices: readonly number[];
|
|
413
|
+
readonly invertedMask: boolean;
|
|
414
|
+
}
|
|
415
|
+
interface ClippingContext {
|
|
416
|
+
/** Stable key: invert flag + sorted mask drawable indices. */
|
|
417
|
+
readonly key: string;
|
|
418
|
+
readonly maskIndices: readonly number[];
|
|
419
|
+
readonly clippedIndices: readonly number[];
|
|
420
|
+
readonly invertedMask: boolean;
|
|
421
|
+
}
|
|
422
|
+
/** UV-space rectangle inside the shared mask atlas (0..1). */
|
|
423
|
+
interface MaskLayoutRect {
|
|
424
|
+
readonly x: number;
|
|
425
|
+
readonly y: number;
|
|
426
|
+
readonly width: number;
|
|
427
|
+
readonly height: number;
|
|
428
|
+
}
|
|
429
|
+
/** R/G/B/A write/sample selector (Cubism channelFlag). */
|
|
430
|
+
type MaskChannelFlag = readonly [number, number, number, number];
|
|
431
|
+
declare const MASK_CHANNEL_FLAGS: readonly MaskChannelFlag[];
|
|
432
|
+
type MaskAtlasMode = "uv-grid" | "rgba";
|
|
433
|
+
interface MaskAtlasOptions {
|
|
434
|
+
/** Packing strategy. Default `rgba` (Cubism density). */
|
|
435
|
+
readonly mode?: MaskAtlasMode;
|
|
436
|
+
/** Cell inset for `uv-grid` only (reduces neighbour bleed). */
|
|
437
|
+
readonly inset?: number;
|
|
438
|
+
/** Render-texture count for `rgba` (default 1 → max 36). */
|
|
439
|
+
readonly renderTextureCount?: number;
|
|
440
|
+
}
|
|
441
|
+
interface LaidOutClippingContext extends ClippingContext {
|
|
442
|
+
readonly layout: MaskLayoutRect;
|
|
443
|
+
/** 0=R … 3=A. `uv-grid` always uses A (3). */
|
|
444
|
+
readonly channelIndex: number;
|
|
445
|
+
readonly channelFlag: MaskChannelFlag;
|
|
446
|
+
/** Cubism multi-RT buffer index; currently always 0. */
|
|
447
|
+
readonly bufferIndex: number;
|
|
448
|
+
/**
|
|
449
|
+
* Expanded AABB of clipped drawables in vertex/NDC space.
|
|
450
|
+
* Mask write/sample map this rect into `layout` (Cubism bounds-fit).
|
|
451
|
+
* Default before `fitClippingContexts`: full NDC (-1..1)².
|
|
452
|
+
*/
|
|
453
|
+
readonly modelBounds: MaskLayoutRect;
|
|
454
|
+
}
|
|
455
|
+
/** Full clip-space quad used when no valid clipped bounds exist. */
|
|
456
|
+
declare const FULL_NDC_BOUNDS: MaskLayoutRect;
|
|
457
|
+
interface ClippingPartition {
|
|
458
|
+
readonly contexts: readonly LaidOutClippingContext[];
|
|
459
|
+
/** Drawables that consume a mask (drawn after mask pass). */
|
|
460
|
+
readonly clipped: ReadonlySet<number>;
|
|
461
|
+
/** Drawables used as masks (mask buffer only, not color target). */
|
|
462
|
+
readonly maskOnly: ReadonlySet<number>;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Group drawables that consume clipping masks into shared contexts.
|
|
466
|
+
* Drawables with empty maskIndices are omitted.
|
|
467
|
+
*/
|
|
468
|
+
declare function buildClippingContexts(drawables: readonly ClippingDrawableRef[]): ClippingContext[];
|
|
469
|
+
/**
|
|
470
|
+
* Pack clipping contexts into a square-ish UV grid on one atlas (alpha only).
|
|
471
|
+
*/
|
|
472
|
+
declare function layoutMaskAtlasUvGrid(contexts: readonly ClippingContext[], options?: {
|
|
473
|
+
inset?: number;
|
|
474
|
+
}): LaidOutClippingContext[];
|
|
475
|
+
/**
|
|
476
|
+
* Cubism `setupLayoutBounds`: pack across R/G/B/A then UV cells.
|
|
477
|
+
* Single RT → max 36 (4×9); multi-RT → 32 per sheet.
|
|
478
|
+
*/
|
|
479
|
+
declare function layoutMaskAtlasRgba(contexts: readonly ClippingContext[], options?: {
|
|
480
|
+
renderTextureCount?: number;
|
|
481
|
+
}): LaidOutClippingContext[];
|
|
482
|
+
/** Pack clipping contexts (default: Cubism RGBA density). */
|
|
483
|
+
declare function layoutMaskAtlas(contexts: readonly ClippingContext[], options?: MaskAtlasOptions): LaidOutClippingContext[];
|
|
484
|
+
/** Partition drawables and assign atlas layouts. */
|
|
485
|
+
declare function partitionForClipping(drawables: readonly ClippingDrawableRef[], options?: MaskAtlasOptions): ClippingPartition;
|
|
486
|
+
/** Axis-aligned bounds of interleaved xy vertex positions. */
|
|
487
|
+
declare function calcVertexBounds(positions: Float32Array): MaskLayoutRect | null;
|
|
488
|
+
/** Expand bounds by a relative margin on each axis (Cubism uses 0.05). */
|
|
489
|
+
declare function expandBounds(bounds: MaskLayoutRect, margin?: number): MaskLayoutRect;
|
|
490
|
+
interface MaskBoundsMeshRef {
|
|
491
|
+
readonly index: number;
|
|
492
|
+
readonly vertexPositions: Float32Array;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Union AABB of clipped drawables, expanded by margin (Cubism
|
|
496
|
+
* `calcClippedDrawableTotalBounds` + 0.05 expand).
|
|
497
|
+
*/
|
|
498
|
+
declare function calcClippedDrawableBounds(clippedIndices: readonly number[], byIndex: ReadonlyMap<number, MaskBoundsMeshRef>, margin?: number): MaskLayoutRect;
|
|
499
|
+
/**
|
|
500
|
+
* Attach per-context `modelBounds` so mask write/sample fit the clipped
|
|
501
|
+
* drawable AABB into the atlas cell (higher mask texel density).
|
|
502
|
+
*/
|
|
503
|
+
declare function fitClippingContexts(contexts: readonly LaidOutClippingContext[], byIndex: ReadonlyMap<number, MaskBoundsMeshRef>, margin?: number): LaidOutClippingContext[];
|
|
504
|
+
/** Flatten layout to `[x, y, w, h]` for GPU uniforms. */
|
|
505
|
+
declare function maskLayoutVec4(layout: MaskLayoutRect): Float32Array;
|
|
506
|
+
/** Flatten channel flag to `[r, g, b, a]` for GPU uniforms. */
|
|
507
|
+
declare function maskChannelVec4(flag: MaskChannelFlag): Float32Array;
|
|
508
|
+
|
|
525
509
|
/** Shared untextured preview colors (Canvas2D / WebGL2 / WebGPU parity). */
|
|
526
510
|
declare const PREVIEW_FILL: {
|
|
527
511
|
readonly r: number;
|
|
@@ -538,6 +522,22 @@ declare const PREVIEW_STROKE: {
|
|
|
538
522
|
/** Expand triangle indices into a line-list (each edge twice-wound). */
|
|
539
523
|
declare function triangleEdgesToLineList(indices: Uint16Array): Uint16Array;
|
|
540
524
|
|
|
525
|
+
interface CreateRendererOptions {
|
|
526
|
+
/**
|
|
527
|
+
* Backend try order at initialize time.
|
|
528
|
+
* Default: `["webgpu", "webgl2", "canvas2d"]`.
|
|
529
|
+
*/
|
|
530
|
+
prefer?: RendererKind[];
|
|
531
|
+
webgpu?: WebGpuRendererOptions;
|
|
532
|
+
webgl2?: WebGl2RendererOptions;
|
|
533
|
+
canvas2d?: Canvas2DRendererOptions;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Create a renderer provider. Actual backend is chosen on `initialize`
|
|
537
|
+
* (WebGPU → WebGL2 → Canvas2D by default).
|
|
538
|
+
*/
|
|
539
|
+
declare function createRenderer(options?: CreateRendererOptions): Renderer;
|
|
540
|
+
|
|
541
541
|
/** One-time decode of moc bytes for stage-level {@link ModelAsset} sharing. */
|
|
542
542
|
declare function compileSharedModelCompile(settings: ModelSettings, mocBytes: ArrayBuffer): SharedModelCompile;
|
|
543
543
|
|
package/dist/index.js
CHANGED
|
@@ -2620,97 +2620,6 @@ function fingerprintSnapshot(snapshot) {
|
|
|
2620
2620
|
return parts.join("|");
|
|
2621
2621
|
}
|
|
2622
2622
|
|
|
2623
|
-
// src/runtime/create-renderer.ts
|
|
2624
|
-
var DEFAULT_PREFER = ["webgpu", "webgl2", "canvas2d"];
|
|
2625
|
-
var INIT_TIMEOUT_MS = {
|
|
2626
|
-
webgpu: 2500,
|
|
2627
|
-
webgl2: 1500,
|
|
2628
|
-
canvas2d: 1e3
|
|
2629
|
-
};
|
|
2630
|
-
function instantiate(kind, options) {
|
|
2631
|
-
if (kind === "webgpu") return createWebGpuRenderer(options.webgpu);
|
|
2632
|
-
if (kind === "webgl2") return createWebGl2Renderer(options.webgl2);
|
|
2633
|
-
return createCanvas2DRenderer(options.canvas2d);
|
|
2634
|
-
}
|
|
2635
|
-
async function initializeWithTimeout(kind, candidate, canvas) {
|
|
2636
|
-
const ms = INIT_TIMEOUT_MS[kind];
|
|
2637
|
-
let timer;
|
|
2638
|
-
try {
|
|
2639
|
-
await Promise.race([
|
|
2640
|
-
candidate.initialize(canvas),
|
|
2641
|
-
new Promise((_, reject) => {
|
|
2642
|
-
timer = setTimeout(() => {
|
|
2643
|
-
reject(
|
|
2644
|
-
new Error(
|
|
2645
|
-
`@doki-land/live2d-renderer: ${kind} initialize timed out after ${ms}ms`
|
|
2646
|
-
)
|
|
2647
|
-
);
|
|
2648
|
-
}, ms);
|
|
2649
|
-
})
|
|
2650
|
-
]);
|
|
2651
|
-
} finally {
|
|
2652
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
2653
|
-
}
|
|
2654
|
-
}
|
|
2655
|
-
var FallbackRenderer = class {
|
|
2656
|
-
#inner = null;
|
|
2657
|
-
#kind = "webgpu";
|
|
2658
|
-
#prefer;
|
|
2659
|
-
#options;
|
|
2660
|
-
constructor(prefer, options) {
|
|
2661
|
-
this.#prefer = prefer;
|
|
2662
|
-
this.#options = options;
|
|
2663
|
-
}
|
|
2664
|
-
get kind() {
|
|
2665
|
-
return this.#inner?.kind ?? this.#kind;
|
|
2666
|
-
}
|
|
2667
|
-
async initialize(canvas) {
|
|
2668
|
-
const errors = [];
|
|
2669
|
-
for (const kind of this.#prefer) {
|
|
2670
|
-
const candidate = instantiate(kind, this.#options);
|
|
2671
|
-
try {
|
|
2672
|
-
await initializeWithTimeout(kind, candidate, canvas);
|
|
2673
|
-
this.#inner = candidate;
|
|
2674
|
-
this.#kind = candidate.kind;
|
|
2675
|
-
return;
|
|
2676
|
-
} catch (err) {
|
|
2677
|
-
candidate.destroy();
|
|
2678
|
-
errors.push(
|
|
2679
|
-
`${kind}: ${err instanceof Error ? err.message : String(err)}`
|
|
2680
|
-
);
|
|
2681
|
-
}
|
|
2682
|
-
}
|
|
2683
|
-
throw new Error(
|
|
2684
|
-
`@doki-land/live2d-renderer: no renderer initialized (${errors.join("; ")})`
|
|
2685
|
-
);
|
|
2686
|
-
}
|
|
2687
|
-
createModelDrawPass() {
|
|
2688
|
-
if (!this.#inner) {
|
|
2689
|
-
throw new Error(
|
|
2690
|
-
"@doki-land/live2d-renderer: renderer not initialized"
|
|
2691
|
-
);
|
|
2692
|
-
}
|
|
2693
|
-
return this.#inner.createModelDrawPass();
|
|
2694
|
-
}
|
|
2695
|
-
beginFrame() {
|
|
2696
|
-
this.#inner?.beginFrame();
|
|
2697
|
-
}
|
|
2698
|
-
endFrame() {
|
|
2699
|
-
this.#inner?.endFrame();
|
|
2700
|
-
}
|
|
2701
|
-
resize(width, height) {
|
|
2702
|
-
this.#inner?.resize(width, height);
|
|
2703
|
-
}
|
|
2704
|
-
destroy() {
|
|
2705
|
-
this.#inner?.destroy();
|
|
2706
|
-
this.#inner = null;
|
|
2707
|
-
}
|
|
2708
|
-
};
|
|
2709
|
-
function createRenderer(options = {}) {
|
|
2710
|
-
const prefer = options.prefer?.length ? options.prefer : DEFAULT_PREFER;
|
|
2711
|
-
return new FallbackRenderer(prefer, options);
|
|
2712
|
-
}
|
|
2713
|
-
|
|
2714
2623
|
// src/format/detect-format.ts
|
|
2715
2624
|
import { detectModelSettingsFormat } from "@doki-land/live2d-core";
|
|
2716
2625
|
function detectMocBinaryFormat(bytes) {
|
|
@@ -5512,6 +5421,97 @@ function createMoc3Backend() {
|
|
|
5512
5421
|
return new Moc3Backend();
|
|
5513
5422
|
}
|
|
5514
5423
|
|
|
5424
|
+
// src/runtime/create-renderer.ts
|
|
5425
|
+
var DEFAULT_PREFER = ["webgpu", "webgl2", "canvas2d"];
|
|
5426
|
+
var INIT_TIMEOUT_MS = {
|
|
5427
|
+
webgpu: 2500,
|
|
5428
|
+
webgl2: 1500,
|
|
5429
|
+
canvas2d: 1e3
|
|
5430
|
+
};
|
|
5431
|
+
function instantiate(kind, options) {
|
|
5432
|
+
if (kind === "webgpu") return createWebGpuRenderer(options.webgpu);
|
|
5433
|
+
if (kind === "webgl2") return createWebGl2Renderer(options.webgl2);
|
|
5434
|
+
return createCanvas2DRenderer(options.canvas2d);
|
|
5435
|
+
}
|
|
5436
|
+
async function initializeWithTimeout(kind, candidate, canvas) {
|
|
5437
|
+
const ms = INIT_TIMEOUT_MS[kind];
|
|
5438
|
+
let timer;
|
|
5439
|
+
try {
|
|
5440
|
+
await Promise.race([
|
|
5441
|
+
candidate.initialize(canvas),
|
|
5442
|
+
new Promise((_, reject) => {
|
|
5443
|
+
timer = setTimeout(() => {
|
|
5444
|
+
reject(
|
|
5445
|
+
new Error(
|
|
5446
|
+
`@doki-land/live2d-renderer: ${kind} initialize timed out after ${ms}ms`
|
|
5447
|
+
)
|
|
5448
|
+
);
|
|
5449
|
+
}, ms);
|
|
5450
|
+
})
|
|
5451
|
+
]);
|
|
5452
|
+
} finally {
|
|
5453
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
var FallbackRenderer = class {
|
|
5457
|
+
#inner = null;
|
|
5458
|
+
#kind = "webgpu";
|
|
5459
|
+
#prefer;
|
|
5460
|
+
#options;
|
|
5461
|
+
constructor(prefer, options) {
|
|
5462
|
+
this.#prefer = prefer;
|
|
5463
|
+
this.#options = options;
|
|
5464
|
+
}
|
|
5465
|
+
get kind() {
|
|
5466
|
+
return this.#inner?.kind ?? this.#kind;
|
|
5467
|
+
}
|
|
5468
|
+
async initialize(canvas) {
|
|
5469
|
+
const errors = [];
|
|
5470
|
+
for (const kind of this.#prefer) {
|
|
5471
|
+
const candidate = instantiate(kind, this.#options);
|
|
5472
|
+
try {
|
|
5473
|
+
await initializeWithTimeout(kind, candidate, canvas);
|
|
5474
|
+
this.#inner = candidate;
|
|
5475
|
+
this.#kind = candidate.kind;
|
|
5476
|
+
return;
|
|
5477
|
+
} catch (err) {
|
|
5478
|
+
candidate.destroy();
|
|
5479
|
+
errors.push(
|
|
5480
|
+
`${kind}: ${err instanceof Error ? err.message : String(err)}`
|
|
5481
|
+
);
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
throw new Error(
|
|
5485
|
+
`@doki-land/live2d-renderer: no renderer initialized (${errors.join("; ")})`
|
|
5486
|
+
);
|
|
5487
|
+
}
|
|
5488
|
+
createModelDrawPass() {
|
|
5489
|
+
if (!this.#inner) {
|
|
5490
|
+
throw new Error(
|
|
5491
|
+
"@doki-land/live2d-renderer: renderer not initialized"
|
|
5492
|
+
);
|
|
5493
|
+
}
|
|
5494
|
+
return this.#inner.createModelDrawPass();
|
|
5495
|
+
}
|
|
5496
|
+
beginFrame() {
|
|
5497
|
+
this.#inner?.beginFrame();
|
|
5498
|
+
}
|
|
5499
|
+
endFrame() {
|
|
5500
|
+
this.#inner?.endFrame();
|
|
5501
|
+
}
|
|
5502
|
+
resize(width, height) {
|
|
5503
|
+
this.#inner?.resize(width, height);
|
|
5504
|
+
}
|
|
5505
|
+
destroy() {
|
|
5506
|
+
this.#inner?.destroy();
|
|
5507
|
+
this.#inner = null;
|
|
5508
|
+
}
|
|
5509
|
+
};
|
|
5510
|
+
function createRenderer(options = {}) {
|
|
5511
|
+
const prefer = options.prefer?.length ? options.prefer : DEFAULT_PREFER;
|
|
5512
|
+
return new FallbackRenderer(prefer, options);
|
|
5513
|
+
}
|
|
5514
|
+
|
|
5515
5515
|
// src/runtime/model-runtime.ts
|
|
5516
5516
|
function selectModelBackend(backends, json) {
|
|
5517
5517
|
const hit = backends.find((b) => b.canHandle(json));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doki-land/live2d-renderer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "Live2D rendering — moc2/moc3 decode + WebGPU, WebGL2, Canvas2D backends for live2d.ts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"test": "vitest run"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@doki-land/live2d-core": "0.0.
|
|
48
|
+
"@doki-land/live2d-core": "0.0.18"
|
|
49
49
|
},
|
|
50
50
|
"sideEffects": false
|
|
51
51
|
}
|
package/src/index.ts
CHANGED
|
@@ -29,34 +29,6 @@ export {
|
|
|
29
29
|
WebGpuRendererImpl,
|
|
30
30
|
type WebGpuRendererOptions,
|
|
31
31
|
} from "./backends/webgpu.js";
|
|
32
|
-
export {
|
|
33
|
-
applyWebGl2BlendMode,
|
|
34
|
-
canvasCompositeForBlendMode,
|
|
35
|
-
webGpuBlendState,
|
|
36
|
-
} from "./render/blend.js";
|
|
37
|
-
export {
|
|
38
|
-
buildClippingContexts,
|
|
39
|
-
type ClippingContext,
|
|
40
|
-
type ClippingDrawableRef,
|
|
41
|
-
type ClippingPartition,
|
|
42
|
-
calcClippedDrawableBounds,
|
|
43
|
-
calcVertexBounds,
|
|
44
|
-
expandBounds,
|
|
45
|
-
FULL_NDC_BOUNDS,
|
|
46
|
-
fitClippingContexts,
|
|
47
|
-
type LaidOutClippingContext,
|
|
48
|
-
layoutMaskAtlas,
|
|
49
|
-
layoutMaskAtlasRgba,
|
|
50
|
-
layoutMaskAtlasUvGrid,
|
|
51
|
-
MASK_CHANNEL_FLAGS,
|
|
52
|
-
type MaskAtlasMode,
|
|
53
|
-
type MaskAtlasOptions,
|
|
54
|
-
type MaskChannelFlag,
|
|
55
|
-
type MaskLayoutRect,
|
|
56
|
-
maskChannelVec4,
|
|
57
|
-
maskLayoutVec4,
|
|
58
|
-
partitionForClipping,
|
|
59
|
-
} from "./render/clipping.js";
|
|
60
32
|
export {
|
|
61
33
|
CPU_PROGRAM_KIND,
|
|
62
34
|
type CpuProgramFile,
|
|
@@ -71,10 +43,6 @@ export {
|
|
|
71
43
|
fingerprintSnapshot,
|
|
72
44
|
setParameterValue,
|
|
73
45
|
} from "./cpu/evaluate.js";
|
|
74
|
-
export {
|
|
75
|
-
type CreateRendererOptions,
|
|
76
|
-
createRenderer,
|
|
77
|
-
} from "./runtime/create-renderer.js";
|
|
78
46
|
export {
|
|
79
47
|
detectMocBinaryFormat,
|
|
80
48
|
detectModelSettingsFormat,
|
|
@@ -99,6 +67,43 @@ export {
|
|
|
99
67
|
type Moc3GluePair,
|
|
100
68
|
meanGlueSeamDistance,
|
|
101
69
|
} from "./moc/moc3-glue.js";
|
|
70
|
+
export {
|
|
71
|
+
applyWebGl2BlendMode,
|
|
72
|
+
canvasCompositeForBlendMode,
|
|
73
|
+
webGpuBlendState,
|
|
74
|
+
} from "./render/blend.js";
|
|
75
|
+
export {
|
|
76
|
+
buildClippingContexts,
|
|
77
|
+
type ClippingContext,
|
|
78
|
+
type ClippingDrawableRef,
|
|
79
|
+
type ClippingPartition,
|
|
80
|
+
calcClippedDrawableBounds,
|
|
81
|
+
calcVertexBounds,
|
|
82
|
+
expandBounds,
|
|
83
|
+
FULL_NDC_BOUNDS,
|
|
84
|
+
fitClippingContexts,
|
|
85
|
+
type LaidOutClippingContext,
|
|
86
|
+
layoutMaskAtlas,
|
|
87
|
+
layoutMaskAtlasRgba,
|
|
88
|
+
layoutMaskAtlasUvGrid,
|
|
89
|
+
MASK_CHANNEL_FLAGS,
|
|
90
|
+
type MaskAtlasMode,
|
|
91
|
+
type MaskAtlasOptions,
|
|
92
|
+
type MaskChannelFlag,
|
|
93
|
+
type MaskLayoutRect,
|
|
94
|
+
maskChannelVec4,
|
|
95
|
+
maskLayoutVec4,
|
|
96
|
+
partitionForClipping,
|
|
97
|
+
} from "./render/clipping.js";
|
|
98
|
+
export {
|
|
99
|
+
PREVIEW_FILL,
|
|
100
|
+
PREVIEW_STROKE,
|
|
101
|
+
triangleEdgesToLineList,
|
|
102
|
+
} from "./render/preview-style.js";
|
|
103
|
+
export {
|
|
104
|
+
type CreateRendererOptions,
|
|
105
|
+
createRenderer,
|
|
106
|
+
} from "./runtime/create-renderer.js";
|
|
102
107
|
export {
|
|
103
108
|
type ModelBackend,
|
|
104
109
|
type ModelBackendOptions,
|
|
@@ -106,11 +111,6 @@ export {
|
|
|
106
111
|
type SharedModelCompile,
|
|
107
112
|
selectModelBackend,
|
|
108
113
|
} from "./runtime/model-runtime.js";
|
|
109
|
-
export {
|
|
110
|
-
PREVIEW_FILL,
|
|
111
|
-
PREVIEW_STROKE,
|
|
112
|
-
triangleEdgesToLineList,
|
|
113
|
-
} from "./render/preview-style.js";
|
|
114
114
|
export { compileSharedModelCompile } from "./runtime/shared-compile.js";
|
|
115
115
|
export {
|
|
116
116
|
BlendMode,
|