@doki-land/live2d-renderer 0.0.18 → 0.0.20
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 +63 -7
- package/dist/index.js +985 -339
- package/package.json +2 -2
- package/src/backends/canvas2d.ts +6 -7
- package/src/backends/webgl2.ts +54 -33
- package/src/backends/webgpu-mesh-cache.ts +206 -0
- package/src/backends/webgpu.ts +158 -186
- package/src/cpu/evaluate.ts +63 -4
- package/src/index.ts +9 -0
- package/src/moc/moc2-to-program.ts +89 -3
- package/src/moc/moc2.ts +151 -75
- package/src/moc/moc3-to-program.ts +193 -3
- package/src/moc/moc3.ts +160 -81
- package/src/render/clipping-plan.ts +140 -0
- package/src/render/clipping.ts +22 -6
- package/src/render/mesh-interleave.ts +28 -0
- package/src/runtime/model-runtime.ts +6 -0
- package/src/types.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -322,12 +322,112 @@ function fitClippingContexts(contexts, byIndex, margin = 0.05) {
|
|
|
322
322
|
)
|
|
323
323
|
}));
|
|
324
324
|
}
|
|
325
|
-
function maskLayoutVec4(layout) {
|
|
326
|
-
|
|
325
|
+
function maskLayoutVec4(layout, into) {
|
|
326
|
+
const out = into && into.length >= 4 ? into : new Float32Array(4);
|
|
327
|
+
out[0] = layout.x;
|
|
328
|
+
out[1] = layout.y;
|
|
329
|
+
out[2] = layout.width;
|
|
330
|
+
out[3] = layout.height;
|
|
331
|
+
return out;
|
|
332
|
+
}
|
|
333
|
+
function maskChannelVec4(flag, into) {
|
|
334
|
+
const out = into && into.length >= 4 ? into : new Float32Array(4);
|
|
335
|
+
out[0] = flag[0];
|
|
336
|
+
out[1] = flag[1];
|
|
337
|
+
out[2] = flag[2];
|
|
338
|
+
out[3] = flag[3];
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/render/clipping-plan.ts
|
|
343
|
+
function clippingTopologyKey(drawables) {
|
|
344
|
+
let key = `n:${drawables.length}`;
|
|
345
|
+
for (let i = 0; i < drawables.length; i++) {
|
|
346
|
+
const d = drawables[i];
|
|
347
|
+
key += `|${d.index}:${d.invertedMask ? 1 : 0}:`;
|
|
348
|
+
const masks = d.maskIndices;
|
|
349
|
+
for (let j = 0; j < masks.length; j++) {
|
|
350
|
+
key += `${masks[j]},`;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return key;
|
|
327
354
|
}
|
|
328
|
-
function
|
|
329
|
-
return
|
|
355
|
+
function atlasOptionsKey(options) {
|
|
356
|
+
return `${options.mode ?? "rgba"}:${options.inset ?? ""}:${options.renderTextureCount ?? ""}`;
|
|
330
357
|
}
|
|
358
|
+
function fitClippingContextsInPlace(contexts, byIndex, margin = 0.05) {
|
|
359
|
+
for (let i = 0; i < contexts.length; i++) {
|
|
360
|
+
const ctx = contexts[i];
|
|
361
|
+
ctx.modelBounds = calcClippedDrawableBounds(
|
|
362
|
+
ctx.clippedIndices,
|
|
363
|
+
byIndex,
|
|
364
|
+
margin
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
var ResidentClippingPlan = class {
|
|
369
|
+
#cacheKey = "";
|
|
370
|
+
#contexts = [];
|
|
371
|
+
#clipped = /* @__PURE__ */ new Set();
|
|
372
|
+
#maskOnly = /* @__PURE__ */ new Set();
|
|
373
|
+
#byIndex = /* @__PURE__ */ new Map();
|
|
374
|
+
/**
|
|
375
|
+
* Returns a partition whose `contexts` array identity is stable across
|
|
376
|
+
* frames with the same topology. `modelBounds` are refreshed in place.
|
|
377
|
+
*/
|
|
378
|
+
resolve(drawables, options = {}) {
|
|
379
|
+
const nextKey = `${clippingTopologyKey(drawables)}|${atlasOptionsKey(options)}`;
|
|
380
|
+
if (nextKey !== this.#cacheKey) {
|
|
381
|
+
const partitioned = partitionForClipping(drawables, options);
|
|
382
|
+
this.#contexts = partitioned.contexts.map((ctx) => ({
|
|
383
|
+
key: ctx.key,
|
|
384
|
+
maskIndices: ctx.maskIndices,
|
|
385
|
+
clippedIndices: ctx.clippedIndices,
|
|
386
|
+
invertedMask: ctx.invertedMask,
|
|
387
|
+
layout: ctx.layout,
|
|
388
|
+
channelIndex: ctx.channelIndex,
|
|
389
|
+
channelFlag: ctx.channelFlag,
|
|
390
|
+
bufferIndex: ctx.bufferIndex,
|
|
391
|
+
modelBounds: { ...ctx.modelBounds }
|
|
392
|
+
}));
|
|
393
|
+
this.#clipped = new Set(partitioned.clipped);
|
|
394
|
+
this.#maskOnly = new Set(partitioned.maskOnly);
|
|
395
|
+
this.#cacheKey = nextKey;
|
|
396
|
+
}
|
|
397
|
+
this.#byIndex.clear();
|
|
398
|
+
for (let i = 0; i < drawables.length; i++) {
|
|
399
|
+
const d = drawables[i];
|
|
400
|
+
if ("vertexPositions" in d) {
|
|
401
|
+
this.#byIndex.set(d.index, d);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
fitClippingContextsInPlace(this.#contexts, this.#byIndex);
|
|
405
|
+
return {
|
|
406
|
+
contexts: this.#contexts,
|
|
407
|
+
clipped: this.#clipped,
|
|
408
|
+
maskOnly: this.#maskOnly
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
/** Convenience for full drawable meshes (GPU backends). */
|
|
412
|
+
resolveMeshes(drawables, options = {}) {
|
|
413
|
+
return this.resolve(drawables, options);
|
|
414
|
+
}
|
|
415
|
+
/** True when the last {@link resolve} reused the cached atlas layout. */
|
|
416
|
+
get hasPlan() {
|
|
417
|
+
return this.#cacheKey.length > 0;
|
|
418
|
+
}
|
|
419
|
+
/** Exposed for tests — context array identity while topology holds. */
|
|
420
|
+
get residentContexts() {
|
|
421
|
+
return this.#contexts;
|
|
422
|
+
}
|
|
423
|
+
clear() {
|
|
424
|
+
this.#cacheKey = "";
|
|
425
|
+
this.#contexts = [];
|
|
426
|
+
this.#clipped = /* @__PURE__ */ new Set();
|
|
427
|
+
this.#maskOnly = /* @__PURE__ */ new Set();
|
|
428
|
+
this.#byIndex.clear();
|
|
429
|
+
}
|
|
430
|
+
};
|
|
331
431
|
|
|
332
432
|
// src/render/coords.ts
|
|
333
433
|
function modelYUpToCanvasPixelY(modelY, canvasHeight) {
|
|
@@ -443,6 +543,7 @@ var Canvas2DModelDrawPass = class {
|
|
|
443
543
|
#ctx;
|
|
444
544
|
#options;
|
|
445
545
|
#textures = [];
|
|
546
|
+
#clipPlan = new ResidentClippingPlan();
|
|
446
547
|
constructor(ctx, options) {
|
|
447
548
|
this.#ctx = ctx;
|
|
448
549
|
this.#options = options;
|
|
@@ -459,10 +560,10 @@ var Canvas2DModelDrawPass = class {
|
|
|
459
560
|
const h = ctx.canvas.height;
|
|
460
561
|
const byIndex = /* @__PURE__ */ new Map();
|
|
461
562
|
for (const d of drawables) byIndex.set(d.index, d);
|
|
462
|
-
const partitioned =
|
|
563
|
+
const partitioned = this.#clipPlan.resolveMeshes(drawables, {
|
|
463
564
|
mode: "uv-grid"
|
|
464
565
|
});
|
|
465
|
-
const contexts =
|
|
566
|
+
const contexts = partitioned.contexts;
|
|
466
567
|
const { maskOnly } = partitioned;
|
|
467
568
|
if (contexts.length === 0) {
|
|
468
569
|
for (const d of drawables) {
|
|
@@ -627,6 +728,7 @@ var Canvas2DModelDrawPass = class {
|
|
|
627
728
|
}
|
|
628
729
|
destroy() {
|
|
629
730
|
this.#textures = [];
|
|
731
|
+
this.#clipPlan.clear();
|
|
630
732
|
}
|
|
631
733
|
};
|
|
632
734
|
var Canvas2DRendererImpl = class {
|
|
@@ -683,6 +785,25 @@ function isCanvas2DAvailable() {
|
|
|
683
785
|
return !!canvas.getContext("2d");
|
|
684
786
|
}
|
|
685
787
|
|
|
788
|
+
// src/render/mesh-interleave.ts
|
|
789
|
+
function interleavePosUv(positions, uvs, into) {
|
|
790
|
+
const n = Math.floor(positions.length / 2);
|
|
791
|
+
const need = n * 4;
|
|
792
|
+
const out = into && into.length >= need ? into : new Float32Array(need);
|
|
793
|
+
for (let i = 0; i < n; i++) {
|
|
794
|
+
const o = i * 4;
|
|
795
|
+
const p = i * 2;
|
|
796
|
+
out[o] = positions[p];
|
|
797
|
+
out[o + 1] = positions[p + 1];
|
|
798
|
+
out[o + 2] = uvs[p] ?? 0;
|
|
799
|
+
out[o + 3] = uvs[p + 1] ?? 0;
|
|
800
|
+
}
|
|
801
|
+
return out;
|
|
802
|
+
}
|
|
803
|
+
function interleaveByteLength(positions) {
|
|
804
|
+
return Math.floor(positions.length / 2) * 16;
|
|
805
|
+
}
|
|
806
|
+
|
|
686
807
|
// src/backends/webgl2.ts
|
|
687
808
|
var VS = `#version 300 es
|
|
688
809
|
layout(location = 0) in vec2 a_pos;
|
|
@@ -791,19 +912,6 @@ function linkProgram(gl, vsSource, fsSource) {
|
|
|
791
912
|
}
|
|
792
913
|
return program;
|
|
793
914
|
}
|
|
794
|
-
function interleavePosUv(positions, uvs) {
|
|
795
|
-
const n = Math.floor(positions.length / 2);
|
|
796
|
-
const out = new Float32Array(n * 4);
|
|
797
|
-
for (let i = 0; i < n; i++) {
|
|
798
|
-
const o = i * 4;
|
|
799
|
-
const p = i * 2;
|
|
800
|
-
out[o] = positions[p];
|
|
801
|
-
out[o + 1] = positions[p + 1];
|
|
802
|
-
out[o + 2] = uvs[p] ?? 0;
|
|
803
|
-
out[o + 3] = uvs[p + 1] ?? 0;
|
|
804
|
-
}
|
|
805
|
-
return out;
|
|
806
|
-
}
|
|
807
915
|
function requireUniform(gl, program, name) {
|
|
808
916
|
const loc = gl.getUniformLocation(program, name);
|
|
809
917
|
if (!loc) {
|
|
@@ -841,6 +949,13 @@ var WebGl2ModelDrawPass = class {
|
|
|
841
949
|
#maskTex = null;
|
|
842
950
|
#maskW = 0;
|
|
843
951
|
#maskH = 0;
|
|
952
|
+
#clipPlan = new ResidentClippingPlan();
|
|
953
|
+
#interleaved = new Float32Array(64);
|
|
954
|
+
#indicesRef = null;
|
|
955
|
+
#layoutScratch = new Float32Array(4);
|
|
956
|
+
#boundsScratch = new Float32Array(4);
|
|
957
|
+
#channelScratch = new Float32Array(4);
|
|
958
|
+
#defaultChannel = new Float32Array([0, 0, 0, 1]);
|
|
844
959
|
constructor(gl) {
|
|
845
960
|
this.#gl = gl;
|
|
846
961
|
this.#program = linkProgram(gl, VS, FS);
|
|
@@ -1012,13 +1127,25 @@ var WebGl2ModelDrawPass = class {
|
|
|
1012
1127
|
}
|
|
1013
1128
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1014
1129
|
}
|
|
1015
|
-
#uploadMesh(d) {
|
|
1130
|
+
#uploadMesh(d, uploadIndices = true) {
|
|
1016
1131
|
const gl = this.#gl;
|
|
1017
|
-
|
|
1132
|
+
this.#interleaved = interleavePosUv(
|
|
1133
|
+
d.vertexPositions,
|
|
1134
|
+
d.uvs,
|
|
1135
|
+
this.#interleaved
|
|
1136
|
+
);
|
|
1137
|
+
const vBytes = interleaveByteLength(d.vertexPositions);
|
|
1018
1138
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.#vbo);
|
|
1019
|
-
gl.bufferData(
|
|
1139
|
+
gl.bufferData(
|
|
1140
|
+
gl.ARRAY_BUFFER,
|
|
1141
|
+
this.#interleaved.subarray(0, vBytes / 4),
|
|
1142
|
+
gl.DYNAMIC_DRAW
|
|
1143
|
+
);
|
|
1020
1144
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.#ibo);
|
|
1021
|
-
|
|
1145
|
+
if (uploadIndices && this.#indicesRef !== d.indices) {
|
|
1146
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, d.indices, gl.STATIC_DRAW);
|
|
1147
|
+
this.#indicesRef = d.indices;
|
|
1148
|
+
}
|
|
1022
1149
|
}
|
|
1023
1150
|
#drawMeshColor(d, useMask, invertMask, layout, modelBounds, channelFlag) {
|
|
1024
1151
|
const gl = this.#gl;
|
|
@@ -1041,19 +1168,23 @@ var WebGl2ModelDrawPass = class {
|
|
|
1041
1168
|
);
|
|
1042
1169
|
gl.uniform1f(this.#useMaskLoc, useMask ? 1 : 0);
|
|
1043
1170
|
gl.uniform1f(this.#invertMaskLoc, invertMask ? 1 : 0);
|
|
1044
|
-
|
|
1045
|
-
|
|
1171
|
+
gl.uniform4fv(
|
|
1172
|
+
this.#maskLayoutLoc,
|
|
1173
|
+
maskLayoutVec4(
|
|
1174
|
+
layout ?? { x: 0, y: 0, width: 1, height: 1 },
|
|
1175
|
+
this.#layoutScratch
|
|
1176
|
+
)
|
|
1046
1177
|
);
|
|
1047
|
-
gl.uniform4fv(this.#maskLayoutLoc, layoutVec);
|
|
1048
1178
|
gl.uniform4fv(
|
|
1049
1179
|
this.#maskBoundsLoc,
|
|
1050
1180
|
maskLayoutVec4(
|
|
1051
|
-
modelBounds ?? { x: -1, y: -1, width: 2, height: 2 }
|
|
1181
|
+
modelBounds ?? { x: -1, y: -1, width: 2, height: 2 },
|
|
1182
|
+
this.#boundsScratch
|
|
1052
1183
|
)
|
|
1053
1184
|
);
|
|
1054
1185
|
gl.uniform4fv(
|
|
1055
1186
|
this.#channelFlagLoc,
|
|
1056
|
-
channelFlag ??
|
|
1187
|
+
channelFlag ?? this.#defaultChannel
|
|
1057
1188
|
);
|
|
1058
1189
|
if (useMask && this.#maskTex) {
|
|
1059
1190
|
gl.activeTexture(gl.TEXTURE1);
|
|
@@ -1072,6 +1203,7 @@ var WebGl2ModelDrawPass = class {
|
|
|
1072
1203
|
PREVIEW_STROKE.a * d.opacity
|
|
1073
1204
|
);
|
|
1074
1205
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, lines, gl.DYNAMIC_DRAW);
|
|
1206
|
+
this.#indicesRef = null;
|
|
1075
1207
|
gl.drawElements(gl.LINES, lines.length, gl.UNSIGNED_SHORT, 0);
|
|
1076
1208
|
}
|
|
1077
1209
|
}
|
|
@@ -1085,8 +1217,14 @@ var WebGl2ModelDrawPass = class {
|
|
|
1085
1217
|
gl.uniform1i(this.#maskTexLoc, 0);
|
|
1086
1218
|
gl.uniform1f(this.#maskUseTexLoc, gpuTex ? 1 : 0);
|
|
1087
1219
|
gl.uniform1f(this.#maskOpacityLoc, Math.max(d.opacity, 1));
|
|
1088
|
-
gl.uniform4fv(
|
|
1089
|
-
|
|
1220
|
+
gl.uniform4fv(
|
|
1221
|
+
this.#maskWriteLayoutLoc,
|
|
1222
|
+
maskLayoutVec4(layout, this.#layoutScratch)
|
|
1223
|
+
);
|
|
1224
|
+
gl.uniform4fv(
|
|
1225
|
+
this.#maskWriteBoundsLoc,
|
|
1226
|
+
maskLayoutVec4(modelBounds, this.#boundsScratch)
|
|
1227
|
+
);
|
|
1090
1228
|
gl.uniform4fv(this.#maskWriteChannelLoc, channelFlag);
|
|
1091
1229
|
gl.drawElements(gl.TRIANGLES, d.indices.length, gl.UNSIGNED_SHORT, 0);
|
|
1092
1230
|
}
|
|
@@ -1097,8 +1235,8 @@ var WebGl2ModelDrawPass = class {
|
|
|
1097
1235
|
const height = canvas instanceof HTMLCanvasElement ? canvas.height : canvas.height;
|
|
1098
1236
|
const byIndex = /* @__PURE__ */ new Map();
|
|
1099
1237
|
for (const d of drawables) byIndex.set(d.index, d);
|
|
1100
|
-
const partitioned =
|
|
1101
|
-
const contexts =
|
|
1238
|
+
const partitioned = this.#clipPlan.resolveMeshes(drawables);
|
|
1239
|
+
const contexts = partitioned.contexts;
|
|
1102
1240
|
const { maskOnly } = partitioned;
|
|
1103
1241
|
gl.bindVertexArray(this.#vao);
|
|
1104
1242
|
if (contexts.length > 0) {
|
|
@@ -1113,7 +1251,10 @@ var WebGl2ModelDrawPass = class {
|
|
|
1113
1251
|
gl.enable(gl.BLEND);
|
|
1114
1252
|
gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
|
|
1115
1253
|
for (const ctx of contexts) {
|
|
1116
|
-
const flag = maskChannelVec4(
|
|
1254
|
+
const flag = maskChannelVec4(
|
|
1255
|
+
ctx.channelFlag,
|
|
1256
|
+
this.#channelScratch
|
|
1257
|
+
);
|
|
1117
1258
|
for (const mi of ctx.maskIndices) {
|
|
1118
1259
|
const maskMesh = byIndex.get(mi);
|
|
1119
1260
|
if (maskMesh) {
|
|
@@ -1152,7 +1293,7 @@ var WebGl2ModelDrawPass = class {
|
|
|
1152
1293
|
clip.invertedMask,
|
|
1153
1294
|
clip.layout,
|
|
1154
1295
|
clip.modelBounds,
|
|
1155
|
-
maskChannelVec4(clip.channelFlag)
|
|
1296
|
+
maskChannelVec4(clip.channelFlag, this.#channelScratch)
|
|
1156
1297
|
);
|
|
1157
1298
|
} else {
|
|
1158
1299
|
this.#drawMeshColor(d, false, false, null, null, null);
|
|
@@ -1167,6 +1308,8 @@ var WebGl2ModelDrawPass = class {
|
|
|
1167
1308
|
destroy() {
|
|
1168
1309
|
const gl = this.#gl;
|
|
1169
1310
|
this.#clearGpuTextures();
|
|
1311
|
+
this.#clipPlan.clear();
|
|
1312
|
+
this.#indicesRef = null;
|
|
1170
1313
|
if (this.#maskTex) gl.deleteTexture(this.#maskTex);
|
|
1171
1314
|
if (this.#maskFbo) gl.deleteFramebuffer(this.#maskFbo);
|
|
1172
1315
|
gl.deleteBuffer(this.#vbo);
|
|
@@ -1240,6 +1383,155 @@ function isWebGl2Available() {
|
|
|
1240
1383
|
return !!gl;
|
|
1241
1384
|
}
|
|
1242
1385
|
|
|
1386
|
+
// src/backends/webgpu-mesh-cache.ts
|
|
1387
|
+
function indexBufferByteSize(byteLength) {
|
|
1388
|
+
return byteLength + 3 & ~3;
|
|
1389
|
+
}
|
|
1390
|
+
function createOrGrowBuffer(device, prev, prevBytes, needBytes, usage) {
|
|
1391
|
+
const bytes = Math.max(4, needBytes);
|
|
1392
|
+
if (prev && prevBytes >= bytes) return { buffer: prev, bytes: prevBytes };
|
|
1393
|
+
prev?.destroy();
|
|
1394
|
+
return {
|
|
1395
|
+
buffer: device.createBuffer({
|
|
1396
|
+
size: bytes,
|
|
1397
|
+
usage
|
|
1398
|
+
}),
|
|
1399
|
+
bytes
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
var WebGpuMeshCache = class {
|
|
1403
|
+
#slots = /* @__PURE__ */ new Map();
|
|
1404
|
+
#maskGeneration = 0;
|
|
1405
|
+
bumpMaskGeneration() {
|
|
1406
|
+
this.#maskGeneration += 1;
|
|
1407
|
+
}
|
|
1408
|
+
get maskGeneration() {
|
|
1409
|
+
return this.#maskGeneration;
|
|
1410
|
+
}
|
|
1411
|
+
clear() {
|
|
1412
|
+
for (const slot of this.#slots.values()) {
|
|
1413
|
+
slot.vbo.destroy();
|
|
1414
|
+
slot.ibo.destroy();
|
|
1415
|
+
slot.ubo.destroy();
|
|
1416
|
+
}
|
|
1417
|
+
this.#slots.clear();
|
|
1418
|
+
}
|
|
1419
|
+
/** Drop bind groups that reference model textures (after setTextures). */
|
|
1420
|
+
invalidateTextureBindGroups() {
|
|
1421
|
+
for (const slot of this.#slots.values()) {
|
|
1422
|
+
slot.bgMask = null;
|
|
1423
|
+
slot.bgTextured = null;
|
|
1424
|
+
slot.bgClippedTextured = null;
|
|
1425
|
+
slot.bgSolid = null;
|
|
1426
|
+
slot.bgClippedSolid = null;
|
|
1427
|
+
slot.texIndex = -1;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
ensureSlot(device, drawableIndex) {
|
|
1431
|
+
let slot = this.#slots.get(drawableIndex);
|
|
1432
|
+
if (slot) return slot;
|
|
1433
|
+
const vbo = device.createBuffer({
|
|
1434
|
+
size: 64,
|
|
1435
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
1436
|
+
});
|
|
1437
|
+
const ibo = device.createBuffer({
|
|
1438
|
+
size: 4,
|
|
1439
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST
|
|
1440
|
+
});
|
|
1441
|
+
const ubo = device.createBuffer({
|
|
1442
|
+
size: 80,
|
|
1443
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
1444
|
+
});
|
|
1445
|
+
slot = {
|
|
1446
|
+
index: drawableIndex,
|
|
1447
|
+
vbo,
|
|
1448
|
+
vboBytes: 64,
|
|
1449
|
+
interleaved: new Float32Array(16),
|
|
1450
|
+
ibo,
|
|
1451
|
+
iboBytes: 4,
|
|
1452
|
+
indicesRef: null,
|
|
1453
|
+
uvsRef: null,
|
|
1454
|
+
ubo,
|
|
1455
|
+
uboFloats: new Float32Array(20),
|
|
1456
|
+
bgMask: null,
|
|
1457
|
+
bgTextured: null,
|
|
1458
|
+
bgClippedTextured: null,
|
|
1459
|
+
bgSolid: null,
|
|
1460
|
+
bgClippedSolid: null,
|
|
1461
|
+
texIndex: -1,
|
|
1462
|
+
useWhite: false,
|
|
1463
|
+
maskGeneration: -1
|
|
1464
|
+
};
|
|
1465
|
+
this.#slots.set(drawableIndex, slot);
|
|
1466
|
+
return slot;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Upload interleaved pos/uv when positions change every frame; refresh
|
|
1470
|
+
* index buffer only when the `indices` TypedArray identity changes.
|
|
1471
|
+
*/
|
|
1472
|
+
uploadMesh(device, slot, positions, uvs, indices) {
|
|
1473
|
+
slot.interleaved = interleavePosUv(positions, uvs, slot.interleaved);
|
|
1474
|
+
const vBytes = interleaveByteLength(positions);
|
|
1475
|
+
const grownV = createOrGrowBuffer(
|
|
1476
|
+
device,
|
|
1477
|
+
slot.vbo,
|
|
1478
|
+
slot.vboBytes,
|
|
1479
|
+
vBytes,
|
|
1480
|
+
GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
1481
|
+
);
|
|
1482
|
+
slot.vbo = grownV.buffer;
|
|
1483
|
+
slot.vboBytes = grownV.bytes;
|
|
1484
|
+
device.queue.writeBuffer(
|
|
1485
|
+
slot.vbo,
|
|
1486
|
+
0,
|
|
1487
|
+
slot.interleaved.buffer,
|
|
1488
|
+
slot.interleaved.byteOffset,
|
|
1489
|
+
vBytes
|
|
1490
|
+
);
|
|
1491
|
+
const iBytes = indexBufferByteSize(indices.byteLength);
|
|
1492
|
+
const grownI = createOrGrowBuffer(
|
|
1493
|
+
device,
|
|
1494
|
+
slot.ibo,
|
|
1495
|
+
slot.iboBytes,
|
|
1496
|
+
iBytes,
|
|
1497
|
+
GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST
|
|
1498
|
+
);
|
|
1499
|
+
if (grownI.buffer !== slot.ibo) {
|
|
1500
|
+
slot.indicesRef = null;
|
|
1501
|
+
}
|
|
1502
|
+
slot.ibo = grownI.buffer;
|
|
1503
|
+
slot.iboBytes = grownI.bytes;
|
|
1504
|
+
if (slot.indicesRef !== indices || slot.uvsRef !== uvs) {
|
|
1505
|
+
if (iBytes === indices.byteLength) {
|
|
1506
|
+
device.queue.writeBuffer(
|
|
1507
|
+
slot.ibo,
|
|
1508
|
+
0,
|
|
1509
|
+
indices.buffer,
|
|
1510
|
+
indices.byteOffset,
|
|
1511
|
+
indices.byteLength
|
|
1512
|
+
);
|
|
1513
|
+
} else {
|
|
1514
|
+
const padded = new Uint16Array(iBytes / 2);
|
|
1515
|
+
padded.set(indices);
|
|
1516
|
+
device.queue.writeBuffer(slot.ibo, 0, padded);
|
|
1517
|
+
}
|
|
1518
|
+
slot.indicesRef = indices;
|
|
1519
|
+
slot.uvsRef = uvs;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
writeUbo(device, slot, floats) {
|
|
1523
|
+
const n = floats.length;
|
|
1524
|
+
for (let i = 0; i < n; i++) slot.uboFloats[i] = floats[i];
|
|
1525
|
+
device.queue.writeBuffer(
|
|
1526
|
+
slot.ubo,
|
|
1527
|
+
0,
|
|
1528
|
+
slot.uboFloats.buffer,
|
|
1529
|
+
slot.uboFloats.byteOffset,
|
|
1530
|
+
Math.max(16, n * 4 + 15 & ~15)
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
|
|
1243
1535
|
// src/backends/webgpu.ts
|
|
1244
1536
|
var SOLID_WGSL = (
|
|
1245
1537
|
/* wgsl */
|
|
@@ -1432,33 +1724,6 @@ var BLEND_MODES = [
|
|
|
1432
1724
|
BlendMode.Additive,
|
|
1433
1725
|
BlendMode.Multiplicative
|
|
1434
1726
|
];
|
|
1435
|
-
function indexBufferSize(byteLength) {
|
|
1436
|
-
return byteLength + 3 & ~3;
|
|
1437
|
-
}
|
|
1438
|
-
function writeIndexBuffer(device, indices) {
|
|
1439
|
-
const size = indexBufferSize(indices.byteLength);
|
|
1440
|
-
const ibo = device.createBuffer({
|
|
1441
|
-
size: Math.max(4, size),
|
|
1442
|
-
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
1443
|
-
mappedAtCreation: true
|
|
1444
|
-
});
|
|
1445
|
-
new Uint16Array(ibo.getMappedRange()).set(indices);
|
|
1446
|
-
ibo.unmap();
|
|
1447
|
-
return ibo;
|
|
1448
|
-
}
|
|
1449
|
-
function interleavePosUv2(positions, uvs) {
|
|
1450
|
-
const n = Math.floor(positions.length / 2);
|
|
1451
|
-
const out = new Float32Array(n * 4);
|
|
1452
|
-
for (let i = 0; i < n; i++) {
|
|
1453
|
-
const o = i * 4;
|
|
1454
|
-
const p = i * 2;
|
|
1455
|
-
out[o] = positions[p];
|
|
1456
|
-
out[o + 1] = positions[p + 1];
|
|
1457
|
-
out[o + 2] = uvs[p] ?? 0;
|
|
1458
|
-
out[o + 3] = uvs[p + 1] ?? 0;
|
|
1459
|
-
}
|
|
1460
|
-
return out;
|
|
1461
|
-
}
|
|
1462
1727
|
function createWhiteTexture(device) {
|
|
1463
1728
|
const tex = device.createTexture({
|
|
1464
1729
|
size: [1, 1],
|
|
@@ -1514,8 +1779,17 @@ var WebGpuRendererImpl = class {
|
|
|
1514
1779
|
#maskW = 0;
|
|
1515
1780
|
#maskH = 0;
|
|
1516
1781
|
#whiteTexture = null;
|
|
1517
|
-
#
|
|
1782
|
+
#whiteView = null;
|
|
1783
|
+
#maskView = null;
|
|
1518
1784
|
#gpuTextures = [];
|
|
1785
|
+
#gpuTextureViews = [];
|
|
1786
|
+
#meshCache = new WebGpuMeshCache();
|
|
1787
|
+
#clipPlan = new ResidentClippingPlan();
|
|
1788
|
+
#layoutScratch = new Float32Array(4);
|
|
1789
|
+
#channelScratch = new Float32Array(4);
|
|
1790
|
+
#boundsScratch = new Float32Array(4);
|
|
1791
|
+
/** Preview-only line index buffers destroyed at endFrame. */
|
|
1792
|
+
#transient = [];
|
|
1519
1793
|
#options;
|
|
1520
1794
|
constructor(options = {}) {
|
|
1521
1795
|
this.#options = options;
|
|
@@ -1885,6 +2159,7 @@ var WebGpuRendererImpl = class {
|
|
|
1885
2159
|
addressModeV: "clamp-to-edge"
|
|
1886
2160
|
});
|
|
1887
2161
|
this.#whiteTexture = createWhiteTexture(device);
|
|
2162
|
+
this.#whiteView = this.#whiteTexture.createView();
|
|
1888
2163
|
this.#ensureMsaa();
|
|
1889
2164
|
}
|
|
1890
2165
|
#ensureMsaa() {
|
|
@@ -1923,11 +2198,15 @@ var WebGpuRendererImpl = class {
|
|
|
1923
2198
|
});
|
|
1924
2199
|
this.#maskW = w;
|
|
1925
2200
|
this.#maskH = h;
|
|
2201
|
+
this.#maskView = this.#maskTexture.createView();
|
|
2202
|
+
this.#meshCache.bumpMaskGeneration();
|
|
1926
2203
|
return this.#maskTexture;
|
|
1927
2204
|
}
|
|
1928
2205
|
#clearGpuTextures() {
|
|
1929
2206
|
for (const t of this.#gpuTextures) t?.destroy();
|
|
1930
2207
|
this.#gpuTextures = [];
|
|
2208
|
+
this.#gpuTextureViews = [];
|
|
2209
|
+
this.#meshCache.invalidateTextureBindGroups();
|
|
1931
2210
|
}
|
|
1932
2211
|
setTextures(textures) {
|
|
1933
2212
|
const device = this.#device;
|
|
@@ -1936,6 +2215,7 @@ var WebGpuRendererImpl = class {
|
|
|
1936
2215
|
let maxIndex = -1;
|
|
1937
2216
|
for (const t of textures) maxIndex = Math.max(maxIndex, t.index);
|
|
1938
2217
|
this.#gpuTextures = new Array(Math.max(0, maxIndex + 1)).fill(null);
|
|
2218
|
+
this.#gpuTextureViews = new Array(Math.max(0, maxIndex + 1)).fill(null);
|
|
1939
2219
|
for (const t of textures) {
|
|
1940
2220
|
const gpuTex = device.createTexture({
|
|
1941
2221
|
size: [t.width, t.height],
|
|
@@ -1948,6 +2228,7 @@ var WebGpuRendererImpl = class {
|
|
|
1948
2228
|
[t.width, t.height]
|
|
1949
2229
|
);
|
|
1950
2230
|
this.#gpuTextures[t.index] = gpuTex;
|
|
2231
|
+
this.#gpuTextureViews[t.index] = gpuTex.createView();
|
|
1951
2232
|
}
|
|
1952
2233
|
}
|
|
1953
2234
|
createModelDrawPass() {
|
|
@@ -2018,8 +2299,8 @@ var WebGpuRendererImpl = class {
|
|
|
2018
2299
|
}
|
|
2019
2300
|
const byIndex = /* @__PURE__ */ new Map();
|
|
2020
2301
|
for (const d of drawables) byIndex.set(d.index, d);
|
|
2021
|
-
const partitioned =
|
|
2022
|
-
const contexts =
|
|
2302
|
+
const partitioned = this.#clipPlan.resolveMeshes(drawables);
|
|
2303
|
+
const contexts = partitioned.contexts;
|
|
2023
2304
|
const { maskOnly } = partitioned;
|
|
2024
2305
|
let maskTex = null;
|
|
2025
2306
|
if (contexts.length > 0) {
|
|
@@ -2090,27 +2371,23 @@ var WebGpuRendererImpl = class {
|
|
|
2090
2371
|
}
|
|
2091
2372
|
#drawMaskMesh(d, pass, layout, sampler, whiteTex, atlasLayout, modelBounds, channelFlag) {
|
|
2092
2373
|
const device = this.#device;
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
const
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
const layoutVec = maskLayoutVec4(atlasLayout);
|
|
2109
|
-
const ch = maskChannelVec4(channelFlag);
|
|
2110
|
-
const boundsVec = maskLayoutVec4(modelBounds);
|
|
2111
|
-
new Float32Array(ubo.getMappedRange()).set([
|
|
2374
|
+
const whiteView = this.#whiteView;
|
|
2375
|
+
if (!device || !whiteView || d.opacity <= 0) return;
|
|
2376
|
+
const slot = this.#meshCache.ensureSlot(device, d.index);
|
|
2377
|
+
this.#meshCache.uploadMesh(
|
|
2378
|
+
device,
|
|
2379
|
+
slot,
|
|
2380
|
+
d.vertexPositions,
|
|
2381
|
+
d.uvs,
|
|
2382
|
+
d.indices
|
|
2383
|
+
);
|
|
2384
|
+
const layoutVec = maskLayoutVec4(atlasLayout, this.#layoutScratch);
|
|
2385
|
+
const ch = maskChannelVec4(channelFlag, this.#channelScratch);
|
|
2386
|
+
const boundsVec = maskLayoutVec4(modelBounds, this.#boundsScratch);
|
|
2387
|
+
const useWhite = !this.#gpuTextures[d.textureIndex];
|
|
2388
|
+
this.#meshCache.writeUbo(device, slot, [
|
|
2112
2389
|
Math.max(d.opacity, 1),
|
|
2113
|
-
|
|
2390
|
+
useWhite ? 0 : 1,
|
|
2114
2391
|
0,
|
|
2115
2392
|
0,
|
|
2116
2393
|
layoutVec[0],
|
|
@@ -2126,23 +2403,24 @@ var WebGpuRendererImpl = class {
|
|
|
2126
2403
|
boundsVec[2],
|
|
2127
2404
|
boundsVec[3]
|
|
2128
2405
|
]);
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
0,
|
|
2133
|
-
device.createBindGroup({
|
|
2406
|
+
const texView = this.#gpuTextureViews[d.textureIndex] ?? whiteView;
|
|
2407
|
+
if (!slot.bgMask || slot.texIndex !== d.textureIndex || slot.useWhite !== useWhite) {
|
|
2408
|
+
slot.bgMask = device.createBindGroup({
|
|
2134
2409
|
layout,
|
|
2135
2410
|
entries: [
|
|
2136
|
-
{ binding: 0, resource: { buffer: ubo } },
|
|
2411
|
+
{ binding: 0, resource: { buffer: slot.ubo } },
|
|
2137
2412
|
{ binding: 1, resource: sampler },
|
|
2138
|
-
{ binding: 2, resource:
|
|
2413
|
+
{ binding: 2, resource: texView }
|
|
2139
2414
|
]
|
|
2140
|
-
})
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2415
|
+
});
|
|
2416
|
+
slot.texIndex = d.textureIndex;
|
|
2417
|
+
slot.useWhite = useWhite;
|
|
2418
|
+
}
|
|
2419
|
+
pass.setBindGroup(0, slot.bgMask);
|
|
2420
|
+
pass.setVertexBuffer(0, slot.vbo);
|
|
2421
|
+
pass.setIndexBuffer(slot.ibo, "uint16");
|
|
2144
2422
|
pass.drawIndexed(d.indices.length);
|
|
2145
|
-
|
|
2423
|
+
void whiteTex;
|
|
2146
2424
|
}
|
|
2147
2425
|
#drawColorMesh(d, pass, useMask, invertMask, maskTex, atlasLayout, channelFlag, modelBounds) {
|
|
2148
2426
|
const device = this.#device;
|
|
@@ -2151,6 +2429,7 @@ var WebGpuRendererImpl = class {
|
|
|
2151
2429
|
const texturedLayout = this.#texturedBindGroupLayout;
|
|
2152
2430
|
const clippedSolidLayout = this.#clippedSolidBindGroupLayout;
|
|
2153
2431
|
const clippedTexturedLayout = this.#clippedTexturedBindGroupLayout;
|
|
2432
|
+
const maskView = this.#maskView;
|
|
2154
2433
|
if (!device || !sampler || !solidLayout || !texturedLayout || !clippedSolidLayout || !clippedTexturedLayout) {
|
|
2155
2434
|
return;
|
|
2156
2435
|
}
|
|
@@ -2158,30 +2437,30 @@ var WebGpuRendererImpl = class {
|
|
|
2158
2437
|
const set = this.#pipelines[d.blendMode] ?? this.#pipelines[BlendMode.Normal];
|
|
2159
2438
|
if (!set) return;
|
|
2160
2439
|
const layoutVec = maskLayoutVec4(
|
|
2161
|
-
atlasLayout ?? { x: 0, y: 0, width: 1, height: 1 }
|
|
2440
|
+
atlasLayout ?? { x: 0, y: 0, width: 1, height: 1 },
|
|
2441
|
+
this.#layoutScratch
|
|
2442
|
+
);
|
|
2443
|
+
const ch = maskChannelVec4(
|
|
2444
|
+
channelFlag ?? [0, 0, 0, 1],
|
|
2445
|
+
this.#channelScratch
|
|
2162
2446
|
);
|
|
2163
|
-
const ch = maskChannelVec4(channelFlag ?? [0, 0, 0, 1]);
|
|
2164
2447
|
const boundsVec = maskLayoutVec4(
|
|
2165
|
-
modelBounds ?? { x: -1, y: -1, width: 2, height: 2 }
|
|
2448
|
+
modelBounds ?? { x: -1, y: -1, width: 2, height: 2 },
|
|
2449
|
+
this.#boundsScratch
|
|
2166
2450
|
);
|
|
2451
|
+
const slot = this.#meshCache.ensureSlot(device, d.index);
|
|
2167
2452
|
const gpuTex = this.#gpuTextures[d.textureIndex] ?? null;
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
size: uboSize,
|
|
2180
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
2181
|
-
mappedAtCreation: true
|
|
2182
|
-
});
|
|
2183
|
-
if (useMask && maskTex) {
|
|
2184
|
-
new Float32Array(ubo.getMappedRange()).set([
|
|
2453
|
+
const texView = this.#gpuTextureViews[d.textureIndex] ?? null;
|
|
2454
|
+
if (gpuTex && texView) {
|
|
2455
|
+
this.#meshCache.uploadMesh(
|
|
2456
|
+
device,
|
|
2457
|
+
slot,
|
|
2458
|
+
d.vertexPositions,
|
|
2459
|
+
d.uvs,
|
|
2460
|
+
d.indices
|
|
2461
|
+
);
|
|
2462
|
+
if (useMask && maskTex && maskView) {
|
|
2463
|
+
this.#meshCache.writeUbo(device, slot, [
|
|
2185
2464
|
d.opacity,
|
|
2186
2465
|
invertMask ? 1 : 0,
|
|
2187
2466
|
0,
|
|
@@ -2199,64 +2478,51 @@ var WebGpuRendererImpl = class {
|
|
|
2199
2478
|
boundsVec[2],
|
|
2200
2479
|
boundsVec[3]
|
|
2201
2480
|
]);
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
d.opacity,
|
|
2205
|
-
0,
|
|
2206
|
-
0,
|
|
2207
|
-
0
|
|
2208
|
-
]);
|
|
2209
|
-
}
|
|
2210
|
-
ubo.unmap();
|
|
2211
|
-
const ibo = writeIndexBuffer(device, d.indices);
|
|
2212
|
-
if (useMask && maskTex) {
|
|
2213
|
-
pass.setPipeline(set.clippedTextured);
|
|
2214
|
-
pass.setBindGroup(
|
|
2215
|
-
0,
|
|
2216
|
-
device.createBindGroup({
|
|
2481
|
+
if (!slot.bgClippedTextured || slot.texIndex !== d.textureIndex || slot.maskGeneration !== this.#meshCache.maskGeneration) {
|
|
2482
|
+
slot.bgClippedTextured = device.createBindGroup({
|
|
2217
2483
|
layout: clippedTexturedLayout,
|
|
2218
2484
|
entries: [
|
|
2219
|
-
{ binding: 0, resource: { buffer: ubo } },
|
|
2485
|
+
{ binding: 0, resource: { buffer: slot.ubo } },
|
|
2220
2486
|
{ binding: 1, resource: sampler },
|
|
2221
|
-
{ binding: 2, resource:
|
|
2222
|
-
{ binding: 3, resource:
|
|
2487
|
+
{ binding: 2, resource: texView },
|
|
2488
|
+
{ binding: 3, resource: maskView }
|
|
2223
2489
|
]
|
|
2224
|
-
})
|
|
2225
|
-
|
|
2490
|
+
});
|
|
2491
|
+
slot.texIndex = d.textureIndex;
|
|
2492
|
+
slot.maskGeneration = this.#meshCache.maskGeneration;
|
|
2493
|
+
}
|
|
2494
|
+
pass.setPipeline(set.clippedTextured);
|
|
2495
|
+
pass.setBindGroup(0, slot.bgClippedTextured);
|
|
2226
2496
|
} else {
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
device.createBindGroup({
|
|
2497
|
+
this.#meshCache.writeUbo(device, slot, [d.opacity, 0, 0, 0]);
|
|
2498
|
+
if (!slot.bgTextured || slot.texIndex !== d.textureIndex) {
|
|
2499
|
+
slot.bgTextured = device.createBindGroup({
|
|
2231
2500
|
layout: texturedLayout,
|
|
2232
2501
|
entries: [
|
|
2233
|
-
{ binding: 0, resource: { buffer: ubo } },
|
|
2502
|
+
{ binding: 0, resource: { buffer: slot.ubo } },
|
|
2234
2503
|
{ binding: 1, resource: sampler },
|
|
2235
|
-
{ binding: 2, resource:
|
|
2504
|
+
{ binding: 2, resource: texView }
|
|
2236
2505
|
]
|
|
2237
|
-
})
|
|
2238
|
-
|
|
2506
|
+
});
|
|
2507
|
+
slot.texIndex = d.textureIndex;
|
|
2508
|
+
}
|
|
2509
|
+
pass.setPipeline(set.textured);
|
|
2510
|
+
pass.setBindGroup(0, slot.bgTextured);
|
|
2239
2511
|
}
|
|
2240
|
-
pass.setVertexBuffer(0,
|
|
2241
|
-
pass.setIndexBuffer(ibo, "uint16");
|
|
2512
|
+
pass.setVertexBuffer(0, slot.vbo);
|
|
2513
|
+
pass.setIndexBuffer(slot.ibo, "uint16");
|
|
2242
2514
|
pass.drawIndexed(d.indices.length);
|
|
2243
|
-
this.#transient.push(vbo2, ubo, ibo);
|
|
2244
2515
|
return;
|
|
2245
2516
|
}
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
if (useMask && maskTex) {
|
|
2254
|
-
|
|
2255
|
-
size: 80,
|
|
2256
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
2257
|
-
mappedAtCreation: true
|
|
2258
|
-
});
|
|
2259
|
-
new Float32Array(ubo.getMappedRange()).set([
|
|
2517
|
+
this.#meshCache.uploadMesh(
|
|
2518
|
+
device,
|
|
2519
|
+
slot,
|
|
2520
|
+
d.vertexPositions,
|
|
2521
|
+
d.uvs,
|
|
2522
|
+
d.indices
|
|
2523
|
+
);
|
|
2524
|
+
if (useMask && maskTex && maskView) {
|
|
2525
|
+
this.#meshCache.writeUbo(device, slot, [
|
|
2260
2526
|
PREVIEW_FILL.r,
|
|
2261
2527
|
PREVIEW_FILL.g,
|
|
2262
2528
|
PREVIEW_FILL.b,
|
|
@@ -2278,54 +2544,61 @@ var WebGpuRendererImpl = class {
|
|
|
2278
2544
|
boundsVec[2],
|
|
2279
2545
|
boundsVec[3]
|
|
2280
2546
|
]);
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
pass.setPipeline(set.clippedFill);
|
|
2284
|
-
pass.setBindGroup(
|
|
2285
|
-
0,
|
|
2286
|
-
device.createBindGroup({
|
|
2547
|
+
if (!slot.bgClippedSolid || slot.maskGeneration !== this.#meshCache.maskGeneration) {
|
|
2548
|
+
slot.bgClippedSolid = device.createBindGroup({
|
|
2287
2549
|
layout: clippedSolidLayout,
|
|
2288
2550
|
entries: [
|
|
2289
|
-
{ binding: 0, resource: { buffer: ubo } },
|
|
2551
|
+
{ binding: 0, resource: { buffer: slot.ubo } },
|
|
2290
2552
|
{ binding: 1, resource: sampler },
|
|
2291
|
-
{ binding: 2, resource:
|
|
2553
|
+
{ binding: 2, resource: maskView }
|
|
2292
2554
|
]
|
|
2293
|
-
})
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
pass.
|
|
2555
|
+
});
|
|
2556
|
+
slot.maskGeneration = this.#meshCache.maskGeneration;
|
|
2557
|
+
}
|
|
2558
|
+
pass.setPipeline(set.clippedFill);
|
|
2559
|
+
pass.setBindGroup(0, slot.bgClippedSolid);
|
|
2560
|
+
pass.setVertexBuffer(0, slot.vbo);
|
|
2561
|
+
pass.setIndexBuffer(slot.ibo, "uint16");
|
|
2297
2562
|
pass.drawIndexed(d.indices.length);
|
|
2298
|
-
this.#transient.push(vbo, ubo, ibo);
|
|
2299
2563
|
return;
|
|
2300
2564
|
}
|
|
2301
|
-
|
|
2302
|
-
device,
|
|
2565
|
+
this.#meshCache.writeUbo(device, slot, [
|
|
2303
2566
|
PREVIEW_FILL.r,
|
|
2304
2567
|
PREVIEW_FILL.g,
|
|
2305
2568
|
PREVIEW_FILL.b,
|
|
2306
2569
|
PREVIEW_FILL.a * d.opacity
|
|
2307
|
-
);
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
pass.setBindGroup(
|
|
2311
|
-
0,
|
|
2312
|
-
device.createBindGroup({
|
|
2570
|
+
]);
|
|
2571
|
+
if (!slot.bgSolid) {
|
|
2572
|
+
slot.bgSolid = device.createBindGroup({
|
|
2313
2573
|
layout: solidLayout,
|
|
2314
|
-
entries: [{ binding: 0, resource: { buffer:
|
|
2315
|
-
})
|
|
2316
|
-
|
|
2317
|
-
pass.
|
|
2318
|
-
pass.
|
|
2574
|
+
entries: [{ binding: 0, resource: { buffer: slot.ubo } }]
|
|
2575
|
+
});
|
|
2576
|
+
}
|
|
2577
|
+
pass.setPipeline(set.fill);
|
|
2578
|
+
pass.setBindGroup(0, slot.bgSolid);
|
|
2579
|
+
pass.setVertexBuffer(0, slot.vbo);
|
|
2580
|
+
pass.setIndexBuffer(slot.ibo, "uint16");
|
|
2319
2581
|
pass.drawIndexed(d.indices.length);
|
|
2320
2582
|
const lines = triangleEdgesToLineList(d.indices);
|
|
2321
|
-
const lineUbo =
|
|
2322
|
-
|
|
2583
|
+
const lineUbo = device.createBuffer({
|
|
2584
|
+
size: 16,
|
|
2585
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
2586
|
+
mappedAtCreation: true
|
|
2587
|
+
});
|
|
2588
|
+
new Float32Array(lineUbo.getMappedRange()).set([
|
|
2323
2589
|
PREVIEW_STROKE.r,
|
|
2324
2590
|
PREVIEW_STROKE.g,
|
|
2325
2591
|
PREVIEW_STROKE.b,
|
|
2326
2592
|
PREVIEW_STROKE.a * d.opacity
|
|
2327
|
-
);
|
|
2328
|
-
|
|
2593
|
+
]);
|
|
2594
|
+
lineUbo.unmap();
|
|
2595
|
+
const lineIbo = device.createBuffer({
|
|
2596
|
+
size: Math.max(4, lines.byteLength + 3 & ~3),
|
|
2597
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
2598
|
+
mappedAtCreation: true
|
|
2599
|
+
});
|
|
2600
|
+
new Uint16Array(lineIbo.getMappedRange()).set(lines);
|
|
2601
|
+
lineIbo.unmap();
|
|
2329
2602
|
pass.setPipeline(set.line);
|
|
2330
2603
|
pass.setBindGroup(
|
|
2331
2604
|
0,
|
|
@@ -2334,20 +2607,10 @@ var WebGpuRendererImpl = class {
|
|
|
2334
2607
|
entries: [{ binding: 0, resource: { buffer: lineUbo } }]
|
|
2335
2608
|
})
|
|
2336
2609
|
);
|
|
2337
|
-
pass.setVertexBuffer(0, vbo);
|
|
2610
|
+
pass.setVertexBuffer(0, slot.vbo);
|
|
2338
2611
|
pass.setIndexBuffer(lineIbo, "uint16");
|
|
2339
2612
|
pass.drawIndexed(lines.length);
|
|
2340
|
-
this.#transient.push(
|
|
2341
|
-
}
|
|
2342
|
-
#makeColorUbo(device, r, g, b, a) {
|
|
2343
|
-
const ubo = device.createBuffer({
|
|
2344
|
-
size: 16,
|
|
2345
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
2346
|
-
mappedAtCreation: true
|
|
2347
|
-
});
|
|
2348
|
-
new Float32Array(ubo.getMappedRange()).set([r, g, b, a]);
|
|
2349
|
-
ubo.unmap();
|
|
2350
|
-
return ubo;
|
|
2613
|
+
this.#transient.push(lineUbo, lineIbo);
|
|
2351
2614
|
}
|
|
2352
2615
|
endFrame() {
|
|
2353
2616
|
const device = this.#device;
|
|
@@ -2376,13 +2639,17 @@ var WebGpuRendererImpl = class {
|
|
|
2376
2639
|
destroy() {
|
|
2377
2640
|
for (const b of this.#transient) b.destroy();
|
|
2378
2641
|
this.#transient = [];
|
|
2642
|
+
this.#meshCache.clear();
|
|
2643
|
+
this.#clipPlan.clear();
|
|
2379
2644
|
this.#clearGpuTextures();
|
|
2380
2645
|
this.#msaaTexture?.destroy();
|
|
2381
2646
|
this.#msaaTexture = null;
|
|
2382
2647
|
this.#maskTexture?.destroy();
|
|
2383
2648
|
this.#maskTexture = null;
|
|
2649
|
+
this.#maskView = null;
|
|
2384
2650
|
this.#whiteTexture?.destroy();
|
|
2385
2651
|
this.#whiteTexture = null;
|
|
2652
|
+
this.#whiteView = null;
|
|
2386
2653
|
this.#device?.destroy();
|
|
2387
2654
|
this.#device = null;
|
|
2388
2655
|
this.#context = null;
|
|
@@ -2551,10 +2818,21 @@ function createModelInstance(program) {
|
|
|
2551
2818
|
timeSeconds: 0
|
|
2552
2819
|
};
|
|
2553
2820
|
}
|
|
2821
|
+
var paramIndexCache = /* @__PURE__ */ new WeakMap();
|
|
2822
|
+
function parameterIndex(instance, parameterId) {
|
|
2823
|
+
let map = paramIndexCache.get(instance.program);
|
|
2824
|
+
if (!map) {
|
|
2825
|
+
map = /* @__PURE__ */ new Map();
|
|
2826
|
+
for (let i = 0; i < instance.program.parameters.length; i++) {
|
|
2827
|
+
map.set(instance.program.parameters[i].id, i);
|
|
2828
|
+
}
|
|
2829
|
+
paramIndexCache.set(instance.program, map);
|
|
2830
|
+
}
|
|
2831
|
+
const index = map.get(parameterId);
|
|
2832
|
+
return index ?? -1;
|
|
2833
|
+
}
|
|
2554
2834
|
function setParameterValue(instance, parameterId, value) {
|
|
2555
|
-
const index = instance
|
|
2556
|
-
(p2) => p2.id === parameterId
|
|
2557
|
-
);
|
|
2835
|
+
const index = parameterIndex(instance, parameterId);
|
|
2558
2836
|
if (index < 0) {
|
|
2559
2837
|
throw new Error(
|
|
2560
2838
|
`@doki-land/live2d-renderer: unknown parameter "${parameterId}"`
|
|
@@ -2604,6 +2882,32 @@ function evaluateFrame(instance) {
|
|
|
2604
2882
|
drawables
|
|
2605
2883
|
};
|
|
2606
2884
|
}
|
|
2885
|
+
function evaluateFrameInto(instance, meshes, poseOpacity) {
|
|
2886
|
+
const byIndex = /* @__PURE__ */ new Map();
|
|
2887
|
+
for (const m of meshes) byIndex.set(m.index, m);
|
|
2888
|
+
for (const d of instance.program.drawables) {
|
|
2889
|
+
const sink = byIndex.get(d.index);
|
|
2890
|
+
if (!sink) continue;
|
|
2891
|
+
let positions = sink.vertexPositions;
|
|
2892
|
+
if (positions.length !== d.positions.length) {
|
|
2893
|
+
positions = new Float32Array(d.positions.length);
|
|
2894
|
+
sink.vertexPositions = positions;
|
|
2895
|
+
}
|
|
2896
|
+
positions.set(d.positions);
|
|
2897
|
+
if (d.deformParamIndex >= 0 && d.deformDeltas) {
|
|
2898
|
+
const w = paramWeight(instance, d.deformParamIndex);
|
|
2899
|
+
for (let i = 0; i < positions.length; i++) {
|
|
2900
|
+
positions[i] = positions[i] + w * d.deformDeltas[i];
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
sink.opacity = d.opacity;
|
|
2904
|
+
sink.renderOrder = d.renderOrder;
|
|
2905
|
+
sink.visible = d.visible;
|
|
2906
|
+
if (d.index >= 0 && d.index < poseOpacity.length) {
|
|
2907
|
+
poseOpacity[d.index] = d.opacity;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2607
2911
|
function fingerprintSnapshot(snapshot) {
|
|
2608
2912
|
const parts = [`t=${snapshot.timeSeconds.toFixed(4)}`];
|
|
2609
2913
|
for (const d of snapshot.drawables) {
|
|
@@ -3611,16 +3915,81 @@ function moc2ParamGetterFromValues(params, values) {
|
|
|
3611
3915
|
}
|
|
3612
3916
|
return (id) => byId.get(id) ?? 0;
|
|
3613
3917
|
}
|
|
3614
|
-
function
|
|
3918
|
+
function normalizePositionsInto(positions, canvasWidth, canvasHeight, out) {
|
|
3615
3919
|
const w = canvasWidth > 0 ? canvasWidth : 1;
|
|
3616
3920
|
const h = canvasHeight > 0 ? canvasHeight : 1;
|
|
3617
|
-
const out = new Float32Array(positions.length);
|
|
3618
3921
|
for (let i = 0; i + 1 < positions.length; i += 2) {
|
|
3619
3922
|
out[i] = positions[i] / w * 2 - 1;
|
|
3620
3923
|
out[i + 1] = 1 - positions[i + 1] / h * 2;
|
|
3621
3924
|
}
|
|
3925
|
+
}
|
|
3926
|
+
function normalizePositions(positions, canvasWidth, canvasHeight) {
|
|
3927
|
+
const out = new Float32Array(positions.length);
|
|
3928
|
+
normalizePositionsInto(positions, canvasWidth, canvasHeight, out);
|
|
3622
3929
|
return out;
|
|
3623
3930
|
}
|
|
3931
|
+
function evaluateMoc2PoseInto(model, getParam, byId) {
|
|
3932
|
+
const ops = bakeDeformerOps(model, getParam);
|
|
3933
|
+
const w = model.canvasWidth;
|
|
3934
|
+
const h = model.canvasHeight;
|
|
3935
|
+
for (const part of model.parts) {
|
|
3936
|
+
for (const mesh of part.drawData) {
|
|
3937
|
+
const sink = byId.get(mesh.id);
|
|
3938
|
+
if (!sink) continue;
|
|
3939
|
+
const floatCount = mesh.numPoints * 2;
|
|
3940
|
+
const local = interpolateKeyforms(
|
|
3941
|
+
mesh.keyforms,
|
|
3942
|
+
mesh.pivotManager,
|
|
3943
|
+
getParam,
|
|
3944
|
+
floatCount
|
|
3945
|
+
);
|
|
3946
|
+
const world = transformDrawablePositions(mesh, local, ops);
|
|
3947
|
+
let pos = sink.vertexPositions;
|
|
3948
|
+
if (pos.length !== world.length) {
|
|
3949
|
+
pos = new Float32Array(world.length);
|
|
3950
|
+
sink.vertexPositions = pos;
|
|
3951
|
+
}
|
|
3952
|
+
normalizePositionsInto(world, w, h, pos);
|
|
3953
|
+
sink.opacity = interpolateScalarTable(
|
|
3954
|
+
mesh.opacities,
|
|
3955
|
+
mesh.pivotManager,
|
|
3956
|
+
getParam,
|
|
3957
|
+
1
|
|
3958
|
+
);
|
|
3959
|
+
sink.renderOrder = Math.round(
|
|
3960
|
+
interpolateScalarTable(
|
|
3961
|
+
mesh.drawOrders,
|
|
3962
|
+
mesh.pivotManager,
|
|
3963
|
+
getParam,
|
|
3964
|
+
mesh.averageDrawOrder
|
|
3965
|
+
)
|
|
3966
|
+
);
|
|
3967
|
+
sink.visible = part.visible;
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
function moc2DrawableIdsInProgramOrder(model, getParam) {
|
|
3972
|
+
const paramDefs = model.paramDefSet?.params ?? [];
|
|
3973
|
+
const gp = getParam ?? buildParamGetter(paramDefs);
|
|
3974
|
+
const drafts = [];
|
|
3975
|
+
for (const part of model.parts) {
|
|
3976
|
+
for (const mesh of part.drawData) {
|
|
3977
|
+
drafts.push({
|
|
3978
|
+
id: mesh.id,
|
|
3979
|
+
renderOrder: Math.round(
|
|
3980
|
+
interpolateScalarTable(
|
|
3981
|
+
mesh.drawOrders,
|
|
3982
|
+
mesh.pivotManager,
|
|
3983
|
+
gp,
|
|
3984
|
+
mesh.averageDrawOrder
|
|
3985
|
+
)
|
|
3986
|
+
)
|
|
3987
|
+
});
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
drafts.sort((a, b) => a.renderOrder - b.renderOrder);
|
|
3991
|
+
return drafts.map((d) => d.id);
|
|
3992
|
+
}
|
|
3624
3993
|
function lowerDrawable(mesh, getParam, ops, canvasWidth, canvasHeight, partVisible) {
|
|
3625
3994
|
const floatCount = mesh.numPoints * 2;
|
|
3626
3995
|
const local = interpolateKeyforms(
|
|
@@ -4851,15 +5220,24 @@ function meanGlueSeamDistance(positionsByMesh, glue) {
|
|
|
4851
5220
|
}
|
|
4852
5221
|
|
|
4853
5222
|
// src/moc/moc3-to-program.ts
|
|
4854
|
-
function
|
|
5223
|
+
function normalizePositionsInto2(positions, canvasWidth, canvasHeight, pixelsPerUnit, out) {
|
|
4855
5224
|
const ppu = pixelsPerUnit > 0 ? pixelsPerUnit : 1;
|
|
4856
5225
|
const hw = canvasWidth > 0 ? canvasWidth / ppu * 0.5 : 0.5;
|
|
4857
5226
|
const hh = canvasHeight > 0 ? canvasHeight / ppu * 0.5 : 0.5;
|
|
4858
|
-
const out = new Float32Array(positions.length);
|
|
4859
5227
|
for (let i = 0; i + 1 < positions.length; i += 2) {
|
|
4860
5228
|
out[i] = positions[i] / hw;
|
|
4861
5229
|
out[i + 1] = -positions[i + 1] / hh;
|
|
4862
5230
|
}
|
|
5231
|
+
}
|
|
5232
|
+
function normalizePositions2(positions, canvasWidth, canvasHeight, pixelsPerUnit) {
|
|
5233
|
+
const out = new Float32Array(positions.length);
|
|
5234
|
+
normalizePositionsInto2(
|
|
5235
|
+
positions,
|
|
5236
|
+
canvasWidth,
|
|
5237
|
+
canvasHeight,
|
|
5238
|
+
pixelsPerUnit,
|
|
5239
|
+
out
|
|
5240
|
+
);
|
|
4863
5241
|
return out;
|
|
4864
5242
|
}
|
|
4865
5243
|
function moc3DocumentToProgram(doc, options = {}) {
|
|
@@ -5045,6 +5423,155 @@ function moc3DocumentToProgram(doc, options = {}) {
|
|
|
5045
5423
|
drawables
|
|
5046
5424
|
};
|
|
5047
5425
|
}
|
|
5426
|
+
function moc3ArtMeshIndicesInProgramOrder(doc, getParamByIndex) {
|
|
5427
|
+
const meshCount = doc.counts[CountIdx.ART_MESHES] ?? 0;
|
|
5428
|
+
const defaultValues = moc3SectionF32(doc, "parameter.default_values");
|
|
5429
|
+
const gp = getParamByIndex ?? ((index) => defaultValues[index] ?? 0);
|
|
5430
|
+
const keyTables = loadMoc3KeyTables(doc);
|
|
5431
|
+
const enables = moc3SectionI32(doc, "art_mesh.enables");
|
|
5432
|
+
const vertexCounts = moc3SectionI32(doc, "art_mesh.vertex_counts");
|
|
5433
|
+
const indexCounts = moc3SectionI32(doc, "art_mesh.position_index_counts");
|
|
5434
|
+
const keyformBegins = moc3SectionI32(doc, "art_mesh.keyform_begin_indices");
|
|
5435
|
+
const keyformCounts = moc3SectionI32(doc, "art_mesh.keyform_counts");
|
|
5436
|
+
const bandIndices = moc3SectionI32(
|
|
5437
|
+
doc,
|
|
5438
|
+
"art_mesh.keyform_binding_band_indices"
|
|
5439
|
+
);
|
|
5440
|
+
const keyformDrawOrders = moc3SectionF32(
|
|
5441
|
+
doc,
|
|
5442
|
+
"art_mesh_keyform.draw_orders"
|
|
5443
|
+
);
|
|
5444
|
+
const drafts = [];
|
|
5445
|
+
for (let i = 0; i < meshCount; i++) {
|
|
5446
|
+
if ((enables[i] ?? 1) === 0) continue;
|
|
5447
|
+
const vertexCount = vertexCounts[i] ?? 0;
|
|
5448
|
+
const indexCount = indexCounts[i] ?? 0;
|
|
5449
|
+
if (vertexCount <= 0 || indexCount < 3) continue;
|
|
5450
|
+
const kfBegin = keyformBegins[i] ?? 0;
|
|
5451
|
+
const kfCount = keyformCounts[i] ?? 0;
|
|
5452
|
+
if (kfCount <= 0) continue;
|
|
5453
|
+
const band = bandIndices[i] ?? -1;
|
|
5454
|
+
const blend = resolveMoc3KeyformBlend(keyTables, band, gp);
|
|
5455
|
+
drafts.push({
|
|
5456
|
+
artMeshIndex: i,
|
|
5457
|
+
renderOrder: Math.round(
|
|
5458
|
+
blendKeyformScalar(
|
|
5459
|
+
keyformDrawOrders,
|
|
5460
|
+
kfBegin,
|
|
5461
|
+
kfCount,
|
|
5462
|
+
blend,
|
|
5463
|
+
i
|
|
5464
|
+
)
|
|
5465
|
+
)
|
|
5466
|
+
});
|
|
5467
|
+
}
|
|
5468
|
+
drafts.sort((a, b) => a.renderOrder - b.renderOrder);
|
|
5469
|
+
return drafts.map((d) => d.artMeshIndex);
|
|
5470
|
+
}
|
|
5471
|
+
function evaluateMoc3PoseInto(doc, getParamByIndex, byArtMesh, poseOpacity) {
|
|
5472
|
+
const meshCount = doc.counts[CountIdx.ART_MESHES] ?? 0;
|
|
5473
|
+
const keyTables = loadMoc3KeyTables(doc);
|
|
5474
|
+
const deformers = bakeMoc3Deformers(doc, keyTables, getParamByIndex);
|
|
5475
|
+
const glues = loadMoc3Glues(doc);
|
|
5476
|
+
const glueIntensities = moc3SectionF32(doc, "glue_keyform.intensities");
|
|
5477
|
+
const visibles = moc3SectionI32(doc, "art_mesh.visibles");
|
|
5478
|
+
const enables = moc3SectionI32(doc, "art_mesh.enables");
|
|
5479
|
+
const vertexCounts = moc3SectionI32(doc, "art_mesh.vertex_counts");
|
|
5480
|
+
const indexCounts = moc3SectionI32(doc, "art_mesh.position_index_counts");
|
|
5481
|
+
const keyformBegins = moc3SectionI32(doc, "art_mesh.keyform_begin_indices");
|
|
5482
|
+
const keyformCounts = moc3SectionI32(doc, "art_mesh.keyform_counts");
|
|
5483
|
+
const bandIndices = moc3SectionI32(
|
|
5484
|
+
doc,
|
|
5485
|
+
"art_mesh.keyform_binding_band_indices"
|
|
5486
|
+
);
|
|
5487
|
+
const parentDeformers = moc3SectionI32(
|
|
5488
|
+
doc,
|
|
5489
|
+
"art_mesh.parent_deformer_indices"
|
|
5490
|
+
);
|
|
5491
|
+
const keyformOpacities = moc3SectionF32(doc, "art_mesh_keyform.opacities");
|
|
5492
|
+
const keyformDrawOrders = moc3SectionF32(
|
|
5493
|
+
doc,
|
|
5494
|
+
"art_mesh_keyform.draw_orders"
|
|
5495
|
+
);
|
|
5496
|
+
const keyformPosBegins = moc3SectionI32(
|
|
5497
|
+
doc,
|
|
5498
|
+
"art_mesh_keyform.keyform_position_begin_indices"
|
|
5499
|
+
);
|
|
5500
|
+
const keyformPositions = moc3SectionF32(doc, "keyform_position.xys");
|
|
5501
|
+
const cw = doc.canvas.canvasWidth;
|
|
5502
|
+
const ch = doc.canvas.canvasHeight;
|
|
5503
|
+
const ppu = doc.canvas.pixelsPerUnit;
|
|
5504
|
+
const worldByMesh = /* @__PURE__ */ new Map();
|
|
5505
|
+
const opacityByMesh = /* @__PURE__ */ new Map();
|
|
5506
|
+
const orderByMesh = /* @__PURE__ */ new Map();
|
|
5507
|
+
const visibleByMesh = /* @__PURE__ */ new Map();
|
|
5508
|
+
for (let i = 0; i < meshCount; i++) {
|
|
5509
|
+
if (!byArtMesh.has(i)) continue;
|
|
5510
|
+
if ((enables[i] ?? 1) === 0) continue;
|
|
5511
|
+
const vertexCount = vertexCounts[i] ?? 0;
|
|
5512
|
+
const indexCount = indexCounts[i] ?? 0;
|
|
5513
|
+
if (vertexCount <= 0 || indexCount < 3) continue;
|
|
5514
|
+
const kfBegin = keyformBegins[i] ?? 0;
|
|
5515
|
+
const kfCount = keyformCounts[i] ?? 0;
|
|
5516
|
+
if (kfCount <= 0) continue;
|
|
5517
|
+
const band = bandIndices[i] ?? -1;
|
|
5518
|
+
const blend = resolveMoc3KeyformBlend(keyTables, band, getParamByIndex);
|
|
5519
|
+
const local = blendKeyformFloats(
|
|
5520
|
+
keyformPositions,
|
|
5521
|
+
keyformPosBegins,
|
|
5522
|
+
kfBegin,
|
|
5523
|
+
kfCount,
|
|
5524
|
+
vertexCount * 2,
|
|
5525
|
+
blend
|
|
5526
|
+
);
|
|
5527
|
+
const parentIndex = parentDeformers[i] ?? -1;
|
|
5528
|
+
const parent = parentIndex >= 0 ? deformers[parentIndex] ?? null : null;
|
|
5529
|
+
const world = parent ? applyParentToPoints(local, parent) : local;
|
|
5530
|
+
worldByMesh.set(i, world);
|
|
5531
|
+
opacityByMesh.set(
|
|
5532
|
+
i,
|
|
5533
|
+
blendKeyformScalar(keyformOpacities, kfBegin, kfCount, blend, 1)
|
|
5534
|
+
);
|
|
5535
|
+
orderByMesh.set(
|
|
5536
|
+
i,
|
|
5537
|
+
Math.round(
|
|
5538
|
+
blendKeyformScalar(
|
|
5539
|
+
keyformDrawOrders,
|
|
5540
|
+
kfBegin,
|
|
5541
|
+
kfCount,
|
|
5542
|
+
blend,
|
|
5543
|
+
i
|
|
5544
|
+
)
|
|
5545
|
+
)
|
|
5546
|
+
);
|
|
5547
|
+
visibleByMesh.set(i, (visibles[i] ?? 1) !== 0);
|
|
5548
|
+
}
|
|
5549
|
+
applyMoc3Glues(
|
|
5550
|
+
worldByMesh,
|
|
5551
|
+
glues,
|
|
5552
|
+
keyTables,
|
|
5553
|
+
getParamByIndex,
|
|
5554
|
+
glueIntensities
|
|
5555
|
+
);
|
|
5556
|
+
for (const [artMeshIndex, world] of worldByMesh) {
|
|
5557
|
+
const sink = byArtMesh.get(artMeshIndex);
|
|
5558
|
+
if (!sink) continue;
|
|
5559
|
+
let pos = sink.vertexPositions;
|
|
5560
|
+
if (pos.length !== world.length) {
|
|
5561
|
+
pos = new Float32Array(world.length);
|
|
5562
|
+
sink.vertexPositions = pos;
|
|
5563
|
+
}
|
|
5564
|
+
normalizePositionsInto2(world, cw, ch, ppu, pos);
|
|
5565
|
+
const opacity = opacityByMesh.get(artMeshIndex) ?? 1;
|
|
5566
|
+
sink.renderOrder = orderByMesh.get(artMeshIndex) ?? sink.renderOrder;
|
|
5567
|
+
sink.visible = visibleByMesh.get(artMeshIndex) ?? true;
|
|
5568
|
+
const slot = sink.index;
|
|
5569
|
+
if (slot >= 0 && slot < poseOpacity.length) {
|
|
5570
|
+
poseOpacity[slot] = opacity;
|
|
5571
|
+
}
|
|
5572
|
+
sink.opacity = opacity;
|
|
5573
|
+
}
|
|
5574
|
+
}
|
|
5048
5575
|
|
|
5049
5576
|
// src/moc/decode.ts
|
|
5050
5577
|
function readMagic2(bytes) {
|
|
@@ -5095,49 +5622,90 @@ async function decodeMoc3(bytes) {
|
|
|
5095
5622
|
|
|
5096
5623
|
// src/moc/moc2.ts
|
|
5097
5624
|
import { detectModelSettingsFormat as detectModelSettingsFormat2 } from "@doki-land/live2d-core";
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5625
|
+
var stateByModel = /* @__PURE__ */ new WeakMap();
|
|
5626
|
+
function allocateMeshesFromProgram(program, drawableIds) {
|
|
5627
|
+
const meshes = [];
|
|
5628
|
+
const byId = /* @__PURE__ */ new Map();
|
|
5629
|
+
for (let i = 0; i < program.drawables.length; i++) {
|
|
5630
|
+
const d = program.drawables[i];
|
|
5631
|
+
const mesh = {
|
|
5632
|
+
index: d.index,
|
|
5633
|
+
textureIndex: d.textureIndex,
|
|
5634
|
+
vertexPositions: new Float32Array(d.positions),
|
|
5635
|
+
uvs: d.uvs,
|
|
5636
|
+
indices: d.indices,
|
|
5637
|
+
opacity: d.opacity,
|
|
5638
|
+
blendMode: d.blendMode,
|
|
5639
|
+
invertedMask: d.invertedMask,
|
|
5640
|
+
renderOrder: d.renderOrder,
|
|
5641
|
+
dynamicFlag: true,
|
|
5642
|
+
maskIndices: [...d.maskIndices],
|
|
5643
|
+
visible: d.visible
|
|
5644
|
+
};
|
|
5645
|
+
meshes.push(mesh);
|
|
5646
|
+
const id = drawableIds[i];
|
|
5647
|
+
if (id) byId.set(id, mesh);
|
|
5648
|
+
}
|
|
5649
|
+
return { meshes, byId };
|
|
5650
|
+
}
|
|
5651
|
+
function buildBindings(instance) {
|
|
5652
|
+
const bindings = [];
|
|
5653
|
+
const paramIndexById = /* @__PURE__ */ new Map();
|
|
5654
|
+
for (let i = 0; i < instance.program.parameters.length; i++) {
|
|
5655
|
+
const p = instance.program.parameters[i];
|
|
5656
|
+
const binding = {
|
|
5657
|
+
id: p.id,
|
|
5658
|
+
min: p.min,
|
|
5659
|
+
max: p.max,
|
|
5660
|
+
defaultValue: p.defaultValue,
|
|
5661
|
+
value: instance.parameterValues[i] ?? p.defaultValue
|
|
5662
|
+
};
|
|
5663
|
+
bindings.push(binding);
|
|
5664
|
+
paramIndexById.set(p.id, i);
|
|
5665
|
+
}
|
|
5666
|
+
return { bindings, paramIndexById };
|
|
5113
5667
|
}
|
|
5114
|
-
function
|
|
5115
|
-
let
|
|
5116
|
-
|
|
5117
|
-
|
|
5668
|
+
function syncBindingValues(state) {
|
|
5669
|
+
for (let i = 0; i < state.bindings.length; i++) {
|
|
5670
|
+
const b = state.bindings[i];
|
|
5671
|
+
b.value = state.instance.parameterValues[i] ?? b.defaultValue;
|
|
5118
5672
|
}
|
|
5119
|
-
return s;
|
|
5120
5673
|
}
|
|
5121
|
-
|
|
5674
|
+
function refreshDrawView(state) {
|
|
5675
|
+
const view = state.drawView;
|
|
5676
|
+
view.length = 0;
|
|
5677
|
+
for (const m of state.meshes) view.push(m);
|
|
5678
|
+
view.sort((a, b) => a.renderOrder - b.renderOrder || a.index - b.index);
|
|
5679
|
+
}
|
|
5122
5680
|
function bakePose(state) {
|
|
5123
|
-
|
|
5124
|
-
const
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5681
|
+
if (!state.poseDirty) return;
|
|
5682
|
+
const getParam = moc2ParamGetterFromValues(
|
|
5683
|
+
state.instance.program.parameters,
|
|
5684
|
+
state.instance.parameterValues
|
|
5685
|
+
);
|
|
5686
|
+
evaluateMoc2PoseInto(state.moc, getParam, state.byId);
|
|
5687
|
+
refreshDrawView(state);
|
|
5688
|
+
syncBindingValues(state);
|
|
5689
|
+
state.poseDirty = false;
|
|
5690
|
+
}
|
|
5691
|
+
function frameFromMeshes(meshes, timeSeconds) {
|
|
5692
|
+
const drawables = [];
|
|
5693
|
+
for (const m of meshes) {
|
|
5694
|
+
drawables.push({
|
|
5695
|
+
index: m.index,
|
|
5696
|
+
textureIndex: m.textureIndex,
|
|
5697
|
+
positions: m.vertexPositions,
|
|
5698
|
+
uvs: m.uvs,
|
|
5699
|
+
indices: m.indices,
|
|
5700
|
+
opacity: m.opacity,
|
|
5701
|
+
blendMode: m.blendMode,
|
|
5702
|
+
renderOrder: m.renderOrder,
|
|
5703
|
+
visible: m.visible,
|
|
5704
|
+
invertedMask: m.invertedMask,
|
|
5705
|
+
maskIndices: m.maskIndices
|
|
5706
|
+
});
|
|
5128
5707
|
}
|
|
5129
|
-
|
|
5130
|
-
getParam: moc2ParamGetterFromValues(
|
|
5131
|
-
state.moc.paramDefSet.params,
|
|
5132
|
-
values
|
|
5133
|
-
)
|
|
5134
|
-
});
|
|
5135
|
-
const next = createModelInstance(program);
|
|
5136
|
-
next.parameterValues.set(values);
|
|
5137
|
-
next.timeSeconds = timeSeconds;
|
|
5138
|
-
state.instance = next;
|
|
5139
|
-
state.bakedFingerprint = fp;
|
|
5140
|
-
state.lastFrame = evaluateFrame(next);
|
|
5708
|
+
return { timeSeconds, drawables };
|
|
5141
5709
|
}
|
|
5142
5710
|
var Moc2Backend = class {
|
|
5143
5711
|
format = "moc2";
|
|
@@ -5158,17 +5726,30 @@ var Moc2Backend = class {
|
|
|
5158
5726
|
const moc = shared?.moc2Model ?? new Moc2Parser(bytes).parseModel();
|
|
5159
5727
|
const program = moc2ModelToProgram(moc);
|
|
5160
5728
|
const instance = createModelInstance(program);
|
|
5729
|
+
const drawableIds = moc2DrawableIdsInProgramOrder(moc);
|
|
5730
|
+
const { meshes, byId } = allocateMeshesFromProgram(
|
|
5731
|
+
program,
|
|
5732
|
+
drawableIds
|
|
5733
|
+
);
|
|
5734
|
+
const { bindings, paramIndexById } = buildBindings(instance);
|
|
5735
|
+
const drawView = [];
|
|
5161
5736
|
const model = {
|
|
5162
5737
|
id: settings.name ?? settings.url,
|
|
5163
5738
|
settings,
|
|
5164
5739
|
format: "moc2"
|
|
5165
5740
|
};
|
|
5166
|
-
|
|
5741
|
+
const state = {
|
|
5167
5742
|
moc,
|
|
5168
5743
|
instance,
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5744
|
+
meshes,
|
|
5745
|
+
drawView,
|
|
5746
|
+
byId,
|
|
5747
|
+
poseDirty: true,
|
|
5748
|
+
bindings,
|
|
5749
|
+
paramIndexById
|
|
5750
|
+
};
|
|
5751
|
+
bakePose(state);
|
|
5752
|
+
stateByModel.set(model, state);
|
|
5172
5753
|
return model;
|
|
5173
5754
|
}
|
|
5174
5755
|
updateModel(model, deltaTimeSeconds) {
|
|
@@ -5176,46 +5757,43 @@ var Moc2Backend = class {
|
|
|
5176
5757
|
if (!state) return;
|
|
5177
5758
|
state.instance.timeSeconds += deltaTimeSeconds;
|
|
5178
5759
|
bakePose(state);
|
|
5179
|
-
if (state.lastFrame) {
|
|
5180
|
-
state.lastFrame = {
|
|
5181
|
-
...state.lastFrame,
|
|
5182
|
-
timeSeconds: state.instance.timeSeconds
|
|
5183
|
-
};
|
|
5184
|
-
}
|
|
5185
5760
|
}
|
|
5186
5761
|
getDrawables(model) {
|
|
5187
5762
|
const state = stateByModel.get(model);
|
|
5188
5763
|
if (!state) return [];
|
|
5189
5764
|
bakePose(state);
|
|
5190
|
-
|
|
5191
|
-
state.lastFrame = frame;
|
|
5192
|
-
return frame.drawables.map(toDrawableMesh);
|
|
5765
|
+
return state.drawView;
|
|
5193
5766
|
}
|
|
5194
5767
|
captureFrame(model) {
|
|
5195
5768
|
const state = stateByModel.get(model);
|
|
5196
5769
|
if (!state) return null;
|
|
5197
5770
|
bakePose(state);
|
|
5198
|
-
|
|
5199
|
-
state.lastFrame = frame;
|
|
5200
|
-
return frame;
|
|
5771
|
+
return frameFromMeshes(state.drawView, state.instance.timeSeconds);
|
|
5201
5772
|
}
|
|
5202
5773
|
setParameter(model, id, value) {
|
|
5203
5774
|
const state = stateByModel.get(model);
|
|
5204
5775
|
if (!state) return;
|
|
5205
5776
|
setParameterValue(state.instance, id, value);
|
|
5206
|
-
|
|
5207
|
-
|
|
5777
|
+
const index = state.paramIndexById.get(id);
|
|
5778
|
+
if (index !== void 0) {
|
|
5779
|
+
const binding = state.bindings[index];
|
|
5780
|
+
if (binding) {
|
|
5781
|
+
binding.value = state.instance.parameterValues[index] ?? binding.defaultValue;
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
state.poseDirty = true;
|
|
5785
|
+
}
|
|
5786
|
+
resolveParameter(model, id) {
|
|
5787
|
+
const state = stateByModel.get(model);
|
|
5788
|
+
if (!state) return void 0;
|
|
5789
|
+
return state.paramIndexById.get(id);
|
|
5208
5790
|
}
|
|
5209
5791
|
listParameters(model) {
|
|
5210
5792
|
const state = stateByModel.get(model);
|
|
5211
5793
|
if (!state) return [];
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
max: p.max,
|
|
5216
|
-
defaultValue: p.defaultValue,
|
|
5217
|
-
value: state.instance.parameterValues[i] ?? p.defaultValue
|
|
5218
|
-
}));
|
|
5794
|
+
bakePose(state);
|
|
5795
|
+
syncBindingValues(state);
|
|
5796
|
+
return state.bindings;
|
|
5219
5797
|
}
|
|
5220
5798
|
hitTest(_model, _x, _y) {
|
|
5221
5799
|
return null;
|
|
@@ -5223,6 +5801,10 @@ var Moc2Backend = class {
|
|
|
5223
5801
|
destroyModel(model) {
|
|
5224
5802
|
stateByModel.delete(model);
|
|
5225
5803
|
}
|
|
5804
|
+
/** Test/harness: stable program + instance refs. */
|
|
5805
|
+
getResidentInstance(model) {
|
|
5806
|
+
return stateByModel.get(model)?.instance ?? null;
|
|
5807
|
+
}
|
|
5226
5808
|
};
|
|
5227
5809
|
function createMoc2Backend() {
|
|
5228
5810
|
return new Moc2Backend();
|
|
@@ -5261,11 +5843,12 @@ function cascadedPartOpacity(tables, artMeshIndex, overrides) {
|
|
|
5261
5843
|
}
|
|
5262
5844
|
|
|
5263
5845
|
// src/moc/moc3.ts
|
|
5264
|
-
|
|
5265
|
-
|
|
5846
|
+
var stateByModel2 = /* @__PURE__ */ new WeakMap();
|
|
5847
|
+
function allocateMeshesFromProgram2(program) {
|
|
5848
|
+
return program.drawables.map((d) => ({
|
|
5266
5849
|
index: d.index,
|
|
5267
5850
|
textureIndex: d.textureIndex,
|
|
5268
|
-
vertexPositions: d.positions,
|
|
5851
|
+
vertexPositions: new Float32Array(d.positions),
|
|
5269
5852
|
uvs: d.uvs,
|
|
5270
5853
|
indices: d.indices,
|
|
5271
5854
|
opacity: d.opacity,
|
|
@@ -5275,34 +5858,82 @@ function toDrawableMesh2(d) {
|
|
|
5275
5858
|
dynamicFlag: true,
|
|
5276
5859
|
maskIndices: [...d.maskIndices],
|
|
5277
5860
|
visible: d.visible
|
|
5278
|
-
};
|
|
5861
|
+
}));
|
|
5862
|
+
}
|
|
5863
|
+
function buildBindings2(instance) {
|
|
5864
|
+
const bindings = [];
|
|
5865
|
+
const paramIndexById = /* @__PURE__ */ new Map();
|
|
5866
|
+
for (let i = 0; i < instance.program.parameters.length; i++) {
|
|
5867
|
+
const p = instance.program.parameters[i];
|
|
5868
|
+
bindings.push({
|
|
5869
|
+
id: p.id,
|
|
5870
|
+
min: p.min,
|
|
5871
|
+
max: p.max,
|
|
5872
|
+
defaultValue: p.defaultValue,
|
|
5873
|
+
value: instance.parameterValues[i] ?? p.defaultValue
|
|
5874
|
+
});
|
|
5875
|
+
paramIndexById.set(p.id, i);
|
|
5876
|
+
}
|
|
5877
|
+
return { bindings, paramIndexById };
|
|
5279
5878
|
}
|
|
5280
|
-
function
|
|
5281
|
-
let
|
|
5282
|
-
|
|
5283
|
-
|
|
5879
|
+
function syncBindingValues2(state) {
|
|
5880
|
+
for (let i = 0; i < state.bindings.length; i++) {
|
|
5881
|
+
const b = state.bindings[i];
|
|
5882
|
+
b.value = state.instance.parameterValues[i] ?? b.defaultValue;
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
function refreshDrawView2(state) {
|
|
5886
|
+
const view = state.drawView;
|
|
5887
|
+
view.length = 0;
|
|
5888
|
+
for (const m of state.meshes) view.push(m);
|
|
5889
|
+
view.sort((a, b) => a.renderOrder - b.renderOrder || a.index - b.index);
|
|
5890
|
+
}
|
|
5891
|
+
function applyPartOpacity(state) {
|
|
5892
|
+
const tables = state.doc ? readMoc3PartTables(state.doc) : null;
|
|
5893
|
+
for (const mesh of state.meshes) {
|
|
5894
|
+
const base = state.poseOpacity[mesh.index] ?? mesh.opacity;
|
|
5895
|
+
if (!tables || state.partOpacity.size === 0) {
|
|
5896
|
+
mesh.opacity = base;
|
|
5897
|
+
continue;
|
|
5898
|
+
}
|
|
5899
|
+
mesh.opacity = base * cascadedPartOpacity(tables, mesh.index, state.partOpacity);
|
|
5284
5900
|
}
|
|
5285
|
-
return s;
|
|
5286
5901
|
}
|
|
5287
|
-
var stateByModel2 = /* @__PURE__ */ new WeakMap();
|
|
5288
5902
|
function bakePose2(state) {
|
|
5289
|
-
if (!state.
|
|
5290
|
-
|
|
5291
|
-
|
|
5903
|
+
if (!state.poseDirty) return;
|
|
5904
|
+
if (state.doc) {
|
|
5905
|
+
const values = state.instance.parameterValues;
|
|
5906
|
+
evaluateMoc3PoseInto(
|
|
5907
|
+
state.doc,
|
|
5908
|
+
(i) => values[i] ?? 0,
|
|
5909
|
+
state.byArtMesh,
|
|
5910
|
+
state.poseOpacity
|
|
5911
|
+
);
|
|
5912
|
+
} else {
|
|
5913
|
+
evaluateFrameInto(state.instance, state.meshes, state.poseOpacity);
|
|
5292
5914
|
}
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5915
|
+
refreshDrawView2(state);
|
|
5916
|
+
syncBindingValues2(state);
|
|
5917
|
+
state.poseDirty = false;
|
|
5918
|
+
}
|
|
5919
|
+
function frameFromMeshes2(meshes, timeSeconds) {
|
|
5920
|
+
const drawables = [];
|
|
5921
|
+
for (const m of meshes) {
|
|
5922
|
+
drawables.push({
|
|
5923
|
+
index: m.index,
|
|
5924
|
+
textureIndex: m.textureIndex,
|
|
5925
|
+
positions: m.vertexPositions,
|
|
5926
|
+
uvs: m.uvs,
|
|
5927
|
+
indices: m.indices,
|
|
5928
|
+
opacity: m.opacity,
|
|
5929
|
+
blendMode: m.blendMode,
|
|
5930
|
+
renderOrder: m.renderOrder,
|
|
5931
|
+
visible: m.visible,
|
|
5932
|
+
invertedMask: m.invertedMask,
|
|
5933
|
+
maskIndices: m.maskIndices
|
|
5934
|
+
});
|
|
5935
|
+
}
|
|
5936
|
+
return { timeSeconds, drawables };
|
|
5306
5937
|
}
|
|
5307
5938
|
var Moc3Backend = class {
|
|
5308
5939
|
format = "moc3";
|
|
@@ -5330,18 +5961,39 @@ var Moc3Backend = class {
|
|
|
5330
5961
|
program = moc3DocumentToProgram(doc);
|
|
5331
5962
|
}
|
|
5332
5963
|
const instance = createModelInstance(program);
|
|
5964
|
+
const meshes = allocateMeshesFromProgram2(program);
|
|
5965
|
+
const byArtMesh = /* @__PURE__ */ new Map();
|
|
5966
|
+
if (doc) {
|
|
5967
|
+
const artOrder = moc3ArtMeshIndicesInProgramOrder(doc);
|
|
5968
|
+
for (let i = 0; i < artOrder.length; i++) {
|
|
5969
|
+
const art = artOrder[i];
|
|
5970
|
+
const mesh = meshes[i];
|
|
5971
|
+
if (mesh) byArtMesh.set(art, mesh);
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
const poseOpacity = new Float32Array(meshes.length);
|
|
5975
|
+
for (const m of meshes) poseOpacity[m.index] = m.opacity;
|
|
5976
|
+
const { bindings, paramIndexById } = buildBindings2(instance);
|
|
5333
5977
|
const model = {
|
|
5334
5978
|
id: settings.name ?? settings.url,
|
|
5335
5979
|
settings,
|
|
5336
5980
|
format: "moc3"
|
|
5337
5981
|
};
|
|
5338
|
-
|
|
5982
|
+
const state = {
|
|
5339
5983
|
doc,
|
|
5340
5984
|
instance,
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5985
|
+
meshes,
|
|
5986
|
+
drawView: [],
|
|
5987
|
+
byArtMesh,
|
|
5988
|
+
poseOpacity,
|
|
5989
|
+
poseDirty: true,
|
|
5990
|
+
partOpacity: /* @__PURE__ */ new Map(),
|
|
5991
|
+
bindings,
|
|
5992
|
+
paramIndexById
|
|
5993
|
+
};
|
|
5994
|
+
bakePose2(state);
|
|
5995
|
+
applyPartOpacity(state);
|
|
5996
|
+
stateByModel2.set(model, state);
|
|
5345
5997
|
return model;
|
|
5346
5998
|
}
|
|
5347
5999
|
updateModel(model, deltaTimeSeconds) {
|
|
@@ -5349,49 +6001,34 @@ var Moc3Backend = class {
|
|
|
5349
6001
|
if (!state) return;
|
|
5350
6002
|
state.instance.timeSeconds += deltaTimeSeconds;
|
|
5351
6003
|
bakePose2(state);
|
|
5352
|
-
|
|
5353
|
-
state.lastFrame = {
|
|
5354
|
-
...state.lastFrame,
|
|
5355
|
-
timeSeconds: state.instance.timeSeconds
|
|
5356
|
-
};
|
|
5357
|
-
}
|
|
6004
|
+
applyPartOpacity(state);
|
|
5358
6005
|
}
|
|
5359
6006
|
getDrawables(model) {
|
|
5360
6007
|
const state = stateByModel2.get(model);
|
|
5361
6008
|
if (!state) return [];
|
|
5362
6009
|
bakePose2(state);
|
|
5363
|
-
|
|
5364
|
-
state.
|
|
5365
|
-
const tables = state.doc ? readMoc3PartTables(state.doc) : null;
|
|
5366
|
-
return frame.drawables.map((d) => {
|
|
5367
|
-
const mesh = toDrawableMesh2(d);
|
|
5368
|
-
if (!tables || state.partOpacity.size === 0) return mesh;
|
|
5369
|
-
const mul = cascadedPartOpacity(tables, d.index, state.partOpacity);
|
|
5370
|
-
return { ...mesh, opacity: mesh.opacity * mul };
|
|
5371
|
-
});
|
|
6010
|
+
applyPartOpacity(state);
|
|
6011
|
+
return state.drawView;
|
|
5372
6012
|
}
|
|
5373
6013
|
captureFrame(model) {
|
|
5374
6014
|
const state = stateByModel2.get(model);
|
|
5375
6015
|
if (!state) return null;
|
|
5376
6016
|
bakePose2(state);
|
|
5377
|
-
|
|
5378
|
-
state.
|
|
5379
|
-
const tables = state.doc ? readMoc3PartTables(state.doc) : null;
|
|
5380
|
-
if (!tables || state.partOpacity.size === 0) return frame;
|
|
5381
|
-
return {
|
|
5382
|
-
...frame,
|
|
5383
|
-
drawables: frame.drawables.map((d) => ({
|
|
5384
|
-
...d,
|
|
5385
|
-
opacity: d.opacity * cascadedPartOpacity(tables, d.index, state.partOpacity)
|
|
5386
|
-
}))
|
|
5387
|
-
};
|
|
6017
|
+
applyPartOpacity(state);
|
|
6018
|
+
return frameFromMeshes2(state.drawView, state.instance.timeSeconds);
|
|
5388
6019
|
}
|
|
5389
6020
|
setParameter(model, id, value) {
|
|
5390
6021
|
const state = stateByModel2.get(model);
|
|
5391
6022
|
if (!state) return;
|
|
5392
6023
|
setParameterValue(state.instance, id, value);
|
|
5393
|
-
|
|
5394
|
-
|
|
6024
|
+
const index = state.paramIndexById.get(id);
|
|
6025
|
+
if (index !== void 0) {
|
|
6026
|
+
const binding = state.bindings[index];
|
|
6027
|
+
if (binding) {
|
|
6028
|
+
binding.value = state.instance.parameterValues[index] ?? binding.defaultValue;
|
|
6029
|
+
}
|
|
6030
|
+
}
|
|
6031
|
+
state.poseDirty = true;
|
|
5395
6032
|
}
|
|
5396
6033
|
setPartOpacity(model, id, value) {
|
|
5397
6034
|
const state = stateByModel2.get(model);
|
|
@@ -5399,16 +6036,17 @@ var Moc3Backend = class {
|
|
|
5399
6036
|
const v = Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 1;
|
|
5400
6037
|
state.partOpacity.set(id, v);
|
|
5401
6038
|
}
|
|
6039
|
+
resolveParameter(model, id) {
|
|
6040
|
+
const state = stateByModel2.get(model);
|
|
6041
|
+
if (!state) return void 0;
|
|
6042
|
+
return state.paramIndexById.get(id);
|
|
6043
|
+
}
|
|
5402
6044
|
listParameters(model) {
|
|
5403
6045
|
const state = stateByModel2.get(model);
|
|
5404
6046
|
if (!state) return [];
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
max: p.max,
|
|
5409
|
-
defaultValue: p.defaultValue,
|
|
5410
|
-
value: state.instance.parameterValues[i] ?? p.defaultValue
|
|
5411
|
-
}));
|
|
6047
|
+
bakePose2(state);
|
|
6048
|
+
syncBindingValues2(state);
|
|
6049
|
+
return state.bindings;
|
|
5412
6050
|
}
|
|
5413
6051
|
hitTest(_model, _x, _y) {
|
|
5414
6052
|
return null;
|
|
@@ -5416,6 +6054,9 @@ var Moc3Backend = class {
|
|
|
5416
6054
|
destroyModel(model) {
|
|
5417
6055
|
stateByModel2.delete(model);
|
|
5418
6056
|
}
|
|
6057
|
+
getResidentInstance(model) {
|
|
6058
|
+
return stateByModel2.get(model)?.instance ?? null;
|
|
6059
|
+
}
|
|
5419
6060
|
};
|
|
5420
6061
|
function createMoc3Backend() {
|
|
5421
6062
|
return new Moc3Backend();
|
|
@@ -5553,6 +6194,7 @@ export {
|
|
|
5553
6194
|
Moc3DrawableFlag,
|
|
5554
6195
|
PREVIEW_FILL,
|
|
5555
6196
|
PREVIEW_STROKE,
|
|
6197
|
+
ResidentClippingPlan,
|
|
5556
6198
|
WebGl2RendererImpl,
|
|
5557
6199
|
WebGpuRendererImpl,
|
|
5558
6200
|
applyMoc3Glues,
|
|
@@ -5561,6 +6203,7 @@ export {
|
|
|
5561
6203
|
calcClippedDrawableBounds,
|
|
5562
6204
|
calcVertexBounds,
|
|
5563
6205
|
canvasCompositeForBlendMode,
|
|
6206
|
+
clippingTopologyKey,
|
|
5564
6207
|
compileSharedModelCompile,
|
|
5565
6208
|
createCanvas2DRenderer,
|
|
5566
6209
|
createMoc2Backend,
|
|
@@ -5580,6 +6223,9 @@ export {
|
|
|
5580
6223
|
expandBounds,
|
|
5581
6224
|
fingerprintSnapshot,
|
|
5582
6225
|
fitClippingContexts,
|
|
6226
|
+
fitClippingContextsInPlace,
|
|
6227
|
+
interleaveByteLength,
|
|
6228
|
+
interleavePosUv,
|
|
5583
6229
|
isCanvas2DAvailable,
|
|
5584
6230
|
isCpuProgramBytes,
|
|
5585
6231
|
isWebGl2Available,
|