@doki-land/live2d-renderer 0.0.0 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +540 -0
- package/dist/index.js +5582 -0
- package/package.json +45 -3
- package/src/backends/canvas2d.ts +447 -0
- package/src/backends/webgl2.ts +685 -0
- package/src/backends/webgpu.ts +1336 -0
- package/src/blend.ts +93 -0
- package/src/clipping.ts +423 -0
- package/src/coords.ts +24 -0
- package/src/cpu/cpu-program.ts +176 -0
- package/src/cpu/evaluate.ts +101 -0
- package/src/create-renderer.ts +143 -0
- package/src/detect-format.ts +19 -0
- package/src/index.ts +121 -0
- package/src/moc/decode.ts +73 -0
- package/src/moc/drawable-flags.ts +42 -0
- package/src/moc/moc2-deform.ts +394 -0
- package/src/moc/moc2-keyforms.ts +178 -0
- package/src/moc/moc2-objects.ts +464 -0
- package/src/moc/moc2-reader.ts +224 -0
- package/src/moc/moc2-to-program.ts +213 -0
- package/src/moc/moc2.ts +184 -0
- package/src/moc/moc3-deform.ts +394 -0
- package/src/moc/moc3-glue.ts +151 -0
- package/src/moc/moc3-keyforms.ts +230 -0
- package/src/moc/moc3-layout.ts +534 -0
- package/src/moc/moc3-parts.ts +45 -0
- package/src/moc/moc3-reader.ts +215 -0
- package/src/moc/moc3-to-program.ts +305 -0
- package/src/moc/moc3.ts +222 -0
- package/src/model-runtime.ts +73 -0
- package/src/preview-style.ts +33 -0
- package/src/types.ts +71 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* moc3 keyform binding bands → multilinear blend weights.
|
|
3
|
+
*
|
|
4
|
+
* A drawable/deformer references a binding band; the band lists parameter
|
|
5
|
+
* bindings; each binding has an ordered key table. Keyforms are laid out as
|
|
6
|
+
* the cartesian product of those bindings (same structure as moc2 pivots).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const EPS = 0.001;
|
|
10
|
+
|
|
11
|
+
export interface Moc3KeyTables {
|
|
12
|
+
readonly bindingIndex: Int32Array;
|
|
13
|
+
readonly bandBegin: Int32Array;
|
|
14
|
+
readonly bandCount: Int32Array;
|
|
15
|
+
readonly keysBegin: Int32Array;
|
|
16
|
+
readonly keysCount: Int32Array;
|
|
17
|
+
readonly keys: Float32Array;
|
|
18
|
+
/** parameterIndex → bindings owned by that parameter */
|
|
19
|
+
readonly paramBindingBegin: Int32Array;
|
|
20
|
+
readonly paramBindingCount: Int32Array;
|
|
21
|
+
readonly paramCount: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface KeyformBlend {
|
|
25
|
+
/** Relative keyform indices within the object's keyform range. */
|
|
26
|
+
readonly indices: number[];
|
|
27
|
+
readonly weights: number[];
|
|
28
|
+
readonly lerpCount: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface PivotSample {
|
|
32
|
+
index: number;
|
|
33
|
+
weight: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function sampleKeys(
|
|
37
|
+
keys: Float32Array,
|
|
38
|
+
begin: number,
|
|
39
|
+
count: number,
|
|
40
|
+
value: number,
|
|
41
|
+
): PivotSample {
|
|
42
|
+
if (count < 1) return { index: 0, weight: 0 };
|
|
43
|
+
if (count === 1) return { index: 0, weight: 0 };
|
|
44
|
+
|
|
45
|
+
const first = keys[begin] ?? 0;
|
|
46
|
+
if (value < first + EPS) return { index: 0, weight: 0 };
|
|
47
|
+
|
|
48
|
+
for (let i = 1; i < count; i++) {
|
|
49
|
+
const lo = keys[begin + i - 1] ?? 0;
|
|
50
|
+
const hi = keys[begin + i] ?? 0;
|
|
51
|
+
if (value < hi + EPS) {
|
|
52
|
+
if (value > hi - EPS) return { index: i, weight: 0 };
|
|
53
|
+
const span = hi - lo;
|
|
54
|
+
return {
|
|
55
|
+
index: i - 1,
|
|
56
|
+
weight: span === 0 ? 0 : (value - lo) / span,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { index: count - 1, weight: 0 };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function bindingToParam(tables: Moc3KeyTables, bindingIndex: number): number {
|
|
64
|
+
for (let p = 0; p < tables.paramCount; p++) {
|
|
65
|
+
const begin = tables.paramBindingBegin[p] ?? -1;
|
|
66
|
+
const count = tables.paramBindingCount[p] ?? 0;
|
|
67
|
+
if (count <= 0 || begin < 0) continue;
|
|
68
|
+
if (bindingIndex >= begin && bindingIndex < begin + count) return p;
|
|
69
|
+
}
|
|
70
|
+
return -1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Resolve multilinear keyform blend for a binding-band index. */
|
|
74
|
+
export function resolveMoc3KeyformBlend(
|
|
75
|
+
tables: Moc3KeyTables,
|
|
76
|
+
bandIndex: number,
|
|
77
|
+
getParamByIndex: (paramIndex: number) => number,
|
|
78
|
+
): KeyformBlend {
|
|
79
|
+
if (bandIndex < 0 || bandIndex >= tables.bandCount.length) {
|
|
80
|
+
return { indices: [0], weights: [], lerpCount: 0 };
|
|
81
|
+
}
|
|
82
|
+
const bindCount = tables.bandCount[bandIndex] ?? 0;
|
|
83
|
+
const bindBegin = tables.bandBegin[bandIndex] ?? 0;
|
|
84
|
+
if (bindCount <= 0) {
|
|
85
|
+
return { indices: [0], weights: [], lerpCount: 0 };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const samples: PivotSample[] = [];
|
|
89
|
+
const keyCounts: number[] = [];
|
|
90
|
+
for (let i = 0; i < bindCount; i++) {
|
|
91
|
+
const binding = tables.bindingIndex[bindBegin + i] ?? -1;
|
|
92
|
+
if (binding < 0) {
|
|
93
|
+
samples.push({ index: 0, weight: 0 });
|
|
94
|
+
keyCounts.push(1);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const paramIndex = bindingToParam(tables, binding);
|
|
98
|
+
const kBegin = tables.keysBegin[binding] ?? 0;
|
|
99
|
+
const kCount = Math.max(1, tables.keysCount[binding] ?? 1);
|
|
100
|
+
const value = paramIndex >= 0 ? getParamByIndex(paramIndex) : 0;
|
|
101
|
+
samples.push(sampleKeys(tables.keys, kBegin, kCount, value));
|
|
102
|
+
keyCounts.push(kCount);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let lerpCount = 0;
|
|
106
|
+
for (const s of samples) if (s.weight > 0) lerpCount++;
|
|
107
|
+
|
|
108
|
+
const n = 1 << lerpCount;
|
|
109
|
+
const indices = new Array<number>(n).fill(0);
|
|
110
|
+
const weights: number[] = [];
|
|
111
|
+
|
|
112
|
+
let stride = 1;
|
|
113
|
+
let lerpDim = 0;
|
|
114
|
+
for (let p = 0; p < samples.length; p++) {
|
|
115
|
+
const s = samples[p]!;
|
|
116
|
+
const pivotCount = keyCounts[p]!;
|
|
117
|
+
if (s.weight === 0) {
|
|
118
|
+
const add = s.index * stride;
|
|
119
|
+
for (let i = 0; i < n; i++) indices[i]! += add;
|
|
120
|
+
} else {
|
|
121
|
+
const a = s.index * stride;
|
|
122
|
+
const b = (s.index + 1) * stride;
|
|
123
|
+
const dimMask = 1 << lerpDim;
|
|
124
|
+
for (let i = 0; i < n; i++) {
|
|
125
|
+
indices[i]! += (i & dimMask) === 0 ? a : b;
|
|
126
|
+
}
|
|
127
|
+
weights[lerpDim] = s.weight;
|
|
128
|
+
lerpDim++;
|
|
129
|
+
}
|
|
130
|
+
stride *= pivotCount;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { indices, weights, lerpCount };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Blend parallel float keyform slices (each keyform starts at begins[kf]). */
|
|
137
|
+
export function blendKeyformFloats(
|
|
138
|
+
values: Float32Array,
|
|
139
|
+
begins: Int32Array,
|
|
140
|
+
keyformBase: number,
|
|
141
|
+
keyformCount: number,
|
|
142
|
+
floatCount: number,
|
|
143
|
+
blend: KeyformBlend,
|
|
144
|
+
): Float32Array {
|
|
145
|
+
const out = new Float32Array(floatCount);
|
|
146
|
+
if (keyformCount <= 0 || floatCount <= 0) return out;
|
|
147
|
+
|
|
148
|
+
const pick = (rel: number): number => {
|
|
149
|
+
const clamped = Math.min(keyformCount - 1, Math.max(0, rel));
|
|
150
|
+
return begins[keyformBase + clamped] ?? 0;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (blend.lerpCount <= 0) {
|
|
154
|
+
const srcOff = pick(blend.indices[0] ?? 0);
|
|
155
|
+
for (let i = 0; i < floatCount; i++) {
|
|
156
|
+
out[i] = values[srcOff + i] ?? 0;
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (blend.lerpCount === 1) {
|
|
162
|
+
const aOff = pick(blend.indices[0] ?? 0);
|
|
163
|
+
const bOff = pick(blend.indices[1] ?? 0);
|
|
164
|
+
const t = blend.weights[0]!;
|
|
165
|
+
const u = 1 - t;
|
|
166
|
+
for (let i = 0; i < floatCount; i++) {
|
|
167
|
+
out[i] = (values[aOff + i] ?? 0) * u + (values[bOff + i] ?? 0) * t;
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const corners = 1 << blend.lerpCount;
|
|
173
|
+
const cornerW = new Float32Array(corners);
|
|
174
|
+
for (let c = 0; c < corners; c++) {
|
|
175
|
+
let w = 1;
|
|
176
|
+
for (let d = 0; d < blend.lerpCount; d++) {
|
|
177
|
+
const t = blend.weights[d]!;
|
|
178
|
+
w *= (c & (1 << d)) === 0 ? 1 - t : t;
|
|
179
|
+
}
|
|
180
|
+
cornerW[c] = w;
|
|
181
|
+
}
|
|
182
|
+
for (let i = 0; i < floatCount; i++) {
|
|
183
|
+
let sum = 0;
|
|
184
|
+
for (let c = 0; c < corners; c++) {
|
|
185
|
+
const srcOff = pick(blend.indices[c] ?? 0);
|
|
186
|
+
sum += cornerW[c]! * (values[srcOff + i] ?? 0);
|
|
187
|
+
}
|
|
188
|
+
out[i] = sum;
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Blend a scalar-per-keyform table (opacity / draw order / rotation fields). */
|
|
194
|
+
export function blendKeyformScalar(
|
|
195
|
+
table: ArrayLike<number>,
|
|
196
|
+
keyformBase: number,
|
|
197
|
+
keyformCount: number,
|
|
198
|
+
blend: KeyformBlend,
|
|
199
|
+
fallback: number,
|
|
200
|
+
): number {
|
|
201
|
+
if (keyformCount <= 0) return fallback;
|
|
202
|
+
|
|
203
|
+
const pick = (rel: number): number => {
|
|
204
|
+
const clamped = Math.min(keyformCount - 1, Math.max(0, rel));
|
|
205
|
+
return table[keyformBase + clamped] ?? fallback;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
if (blend.lerpCount <= 0) {
|
|
209
|
+
return pick(blend.indices[0] ?? 0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (blend.lerpCount === 1) {
|
|
213
|
+
const a = pick(blend.indices[0] ?? 0);
|
|
214
|
+
const b = pick(blend.indices[1] ?? 0);
|
|
215
|
+
const t = blend.weights[0]!;
|
|
216
|
+
return a * (1 - t) + b * t;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const corners = 1 << blend.lerpCount;
|
|
220
|
+
let sum = 0;
|
|
221
|
+
for (let c = 0; c < corners; c++) {
|
|
222
|
+
let w = 1;
|
|
223
|
+
for (let d = 0; d < blend.lerpCount; d++) {
|
|
224
|
+
const t = blend.weights[d]!;
|
|
225
|
+
w *= (c & (1 << d)) === 0 ? 1 - t : t;
|
|
226
|
+
}
|
|
227
|
+
sum += w * pick(blend.indices[c] ?? 0);
|
|
228
|
+
}
|
|
229
|
+
return sum;
|
|
230
|
+
}
|