ldr 0.2.0 → 0.3.0
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 +5 -3
- package/dist/cli.js +784 -188
- package/dist/index.d.ts +3 -3
- package/dist/index.js +554 -32
- package/dist/node/build-pack.d.ts +6 -0
- package/dist/node/compile.d.ts +29 -2
- package/dist/node/index.js +969 -71
- package/dist/node/library.d.ts +9 -2
- package/dist/node/resolver.d.ts +1 -0
- package/dist/render.d.ts +26 -0
- package/dist/scene.d.ts +18 -0
- package/dist/spec.d.ts +83 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,15 +6,139 @@ function parseColor(tok) {
|
|
|
6
6
|
return +tok;
|
|
7
7
|
}
|
|
8
8
|
var MOVED_RE = /^~?\s*moved\s+to\s+(.+)$/i;
|
|
9
|
+
function texNames(rest) {
|
|
10
|
+
const out = [];
|
|
11
|
+
let i = 0;
|
|
12
|
+
while (i < rest.length) {
|
|
13
|
+
while (rest[i] === " " || rest[i] === "\t")
|
|
14
|
+
i++;
|
|
15
|
+
if (i >= rest.length)
|
|
16
|
+
break;
|
|
17
|
+
if (rest[i] === '"') {
|
|
18
|
+
let v = "";
|
|
19
|
+
i++;
|
|
20
|
+
while (i < rest.length && rest[i] !== '"') {
|
|
21
|
+
if (rest[i] === "\\" && i + 1 < rest.length)
|
|
22
|
+
i++;
|
|
23
|
+
v += rest[i++];
|
|
24
|
+
}
|
|
25
|
+
i++;
|
|
26
|
+
out.push(v);
|
|
27
|
+
} else {
|
|
28
|
+
let j = i;
|
|
29
|
+
while (j < rest.length && rest[j] !== " " && rest[j] !== "\t")
|
|
30
|
+
j++;
|
|
31
|
+
out.push(rest.slice(i, j));
|
|
32
|
+
i = j;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
function parseTexMap(tail, next = false) {
|
|
38
|
+
const m = /^\s*(\S+)\s+(.*)$/.exec(tail);
|
|
39
|
+
if (!m)
|
|
40
|
+
return null;
|
|
41
|
+
const method = m[1].toUpperCase();
|
|
42
|
+
const n = method === "PLANAR" ? 9 : method === "CYLINDRICAL" ? 10 : method === "SPHERICAL" ? 11 : 0;
|
|
43
|
+
if (!n)
|
|
44
|
+
return null;
|
|
45
|
+
const parts = m[2].trim().split(/\s+/);
|
|
46
|
+
if (parts.length < n + 1)
|
|
47
|
+
return null;
|
|
48
|
+
const v = parts.slice(0, n).map(Number);
|
|
49
|
+
if (!v.every(Number.isFinite))
|
|
50
|
+
return null;
|
|
51
|
+
let rest = m[2].trim();
|
|
52
|
+
for (let k = 0;k < n; k++)
|
|
53
|
+
rest = rest.replace(/^\S+\s*/, "");
|
|
54
|
+
const names = texNames(rest);
|
|
55
|
+
if (!names.length)
|
|
56
|
+
return null;
|
|
57
|
+
const t = { method, p1: v.slice(0, 3), p2: v.slice(3, 6), p3: v.slice(6, 9), texture: names[0] };
|
|
58
|
+
if (n >= 10)
|
|
59
|
+
t.a = v[9];
|
|
60
|
+
if (n === 11)
|
|
61
|
+
t.b = v[10];
|
|
62
|
+
const g = names.findIndex((x, i) => i > 0 && x.toUpperCase() === "GLOSSMAP");
|
|
63
|
+
if (g > 0 && names[g + 1])
|
|
64
|
+
t.glossmap = names[g + 1];
|
|
65
|
+
if (next)
|
|
66
|
+
t.next = true;
|
|
67
|
+
return t;
|
|
68
|
+
}
|
|
69
|
+
var b64 = (s) => {
|
|
70
|
+
const clean = s.replace(/[^A-Za-z0-9+/]/g, "");
|
|
71
|
+
const pad = clean + "=".repeat((4 - clean.length % 4) % 4);
|
|
72
|
+
const bin = atob(pad);
|
|
73
|
+
const out = new Uint8Array(bin.length);
|
|
74
|
+
for (let i = 0;i < bin.length; i++)
|
|
75
|
+
out[i] = bin.charCodeAt(i);
|
|
76
|
+
return out;
|
|
77
|
+
};
|
|
78
|
+
function geomLine(tk) {
|
|
79
|
+
const t = +tk[0];
|
|
80
|
+
if (!(t >= 1 && t <= 5) || tk.length < 2)
|
|
81
|
+
return null;
|
|
82
|
+
const color = parseColor(tk[1]);
|
|
83
|
+
const nums = tk.slice(2).map(Number);
|
|
84
|
+
if (t === 1)
|
|
85
|
+
return { t: 1, color, m: nums.slice(0, 12), file: norm(tk.slice(14).join(" ")) };
|
|
86
|
+
if (t === 2)
|
|
87
|
+
return { t: 2, color, p: nums.slice(0, 6) };
|
|
88
|
+
if (t === 3)
|
|
89
|
+
return { t: 3, color, p: nums.slice(0, 9) };
|
|
90
|
+
if (t === 4)
|
|
91
|
+
return { t: 4, color, p: nums.slice(0, 12) };
|
|
92
|
+
return { t: 5, color, p: nums.slice(0, 12) };
|
|
93
|
+
}
|
|
9
94
|
function parse(src, name = "main.ldr") {
|
|
10
95
|
const files = new Map;
|
|
11
96
|
let cur = null;
|
|
12
97
|
let main = "";
|
|
98
|
+
let data;
|
|
99
|
+
let dataCur = null;
|
|
100
|
+
let texStack = [];
|
|
101
|
+
let texNext = null;
|
|
102
|
+
const closeData = () => {
|
|
103
|
+
if (!dataCur)
|
|
104
|
+
return;
|
|
105
|
+
const bytes = b64(dataCur.chunks.join(""));
|
|
106
|
+
const png = bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71;
|
|
107
|
+
(data ??= new Map).set(norm(dataCur.name), { name: dataCur.name, bytes, ...png ? { mime: "image/png" } : {} });
|
|
108
|
+
dataCur = null;
|
|
109
|
+
};
|
|
13
110
|
const start = (n) => {
|
|
111
|
+
closeData();
|
|
14
112
|
cur = { name: norm(n), cmds: [] };
|
|
15
113
|
files.set(cur.name, cur);
|
|
16
114
|
if (!main)
|
|
17
115
|
main = cur.name;
|
|
116
|
+
texStack = [];
|
|
117
|
+
texNext = null;
|
|
118
|
+
};
|
|
119
|
+
let skip = false, sawBlock = false;
|
|
120
|
+
const firstBlock = () => {
|
|
121
|
+
if (sawBlock)
|
|
122
|
+
return;
|
|
123
|
+
sawBlock = true;
|
|
124
|
+
files.clear();
|
|
125
|
+
cur = null;
|
|
126
|
+
main = "";
|
|
127
|
+
};
|
|
128
|
+
const annotate = (c, fromColon) => {
|
|
129
|
+
if (texNext) {
|
|
130
|
+
c.tex = texNext;
|
|
131
|
+
texNext = null;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const top = texStack[texStack.length - 1];
|
|
135
|
+
if (!top)
|
|
136
|
+
return;
|
|
137
|
+
if (top.fallback) {
|
|
138
|
+
if (!fromColon)
|
|
139
|
+
c.fallback = true;
|
|
140
|
+
} else
|
|
141
|
+
c.tex = top.map;
|
|
18
142
|
};
|
|
19
143
|
for (const raw of src.split(/\r?\n/)) {
|
|
20
144
|
const line = raw.trim();
|
|
@@ -25,19 +149,40 @@ function parse(src, name = "main.ldr") {
|
|
|
25
149
|
if (t === 0) {
|
|
26
150
|
const text = line.slice(1).trim();
|
|
27
151
|
if (tk[1] === "FILE") {
|
|
152
|
+
firstBlock();
|
|
153
|
+
skip = false;
|
|
28
154
|
start(text.slice(5));
|
|
29
155
|
continue;
|
|
30
156
|
}
|
|
31
157
|
if (tk[1] === "NOFILE") {
|
|
158
|
+
closeData();
|
|
32
159
|
cur = null;
|
|
160
|
+
skip = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (tk[1] === "!DATA") {
|
|
164
|
+
firstBlock();
|
|
165
|
+
closeData();
|
|
166
|
+
cur = null;
|
|
167
|
+
skip = false;
|
|
168
|
+
dataCur = { name: text.slice(5).trim(), chunks: [] };
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (skip)
|
|
172
|
+
continue;
|
|
173
|
+
if (dataCur) {
|
|
174
|
+
if (tk[1] === "!:")
|
|
175
|
+
dataCur.chunks.push(tk.slice(2).join(""));
|
|
33
176
|
continue;
|
|
34
177
|
}
|
|
35
178
|
if (!cur)
|
|
36
179
|
start(name);
|
|
37
180
|
const c = { t: 0, text };
|
|
38
|
-
if (tk[1] === "STEP")
|
|
181
|
+
if (tk[1] === "STEP") {
|
|
39
182
|
c.step = true;
|
|
40
|
-
|
|
183
|
+
texStack = [];
|
|
184
|
+
texNext = null;
|
|
185
|
+
} else if (tk[1] === "BFC")
|
|
41
186
|
c.bfc = tk.slice(2).join(" ");
|
|
42
187
|
else if (tk[1] === "ROTSTEP") {
|
|
43
188
|
c.meta = "ROTSTEP";
|
|
@@ -53,6 +198,37 @@ function parse(src, name = "main.ldr") {
|
|
|
53
198
|
c.bufexchg = { op: op === "RETRIEVE" ? "RETRIEVE" : "STORE", buf: tk[3] ?? "A" };
|
|
54
199
|
} else if (tk[1]?.startsWith("!"))
|
|
55
200
|
c.meta = tk[1];
|
|
201
|
+
if (tk[1] === "!TEXMAP") {
|
|
202
|
+
const op = (tk[2] ?? "").toUpperCase();
|
|
203
|
+
texNext = null;
|
|
204
|
+
if (op === "START" || op === "NEXT") {
|
|
205
|
+
const map = parseTexMap(text.replace(/^!TEXMAP\s+\S+/i, ""), op === "NEXT");
|
|
206
|
+
c.texmap = { op, ...map ? { map } : {} };
|
|
207
|
+
if (map) {
|
|
208
|
+
(cur.texmaps ??= []).push(map);
|
|
209
|
+
if (op === "START")
|
|
210
|
+
texStack.push({ map, fallback: false });
|
|
211
|
+
else
|
|
212
|
+
texNext = map;
|
|
213
|
+
}
|
|
214
|
+
} else if (op === "FALLBACK") {
|
|
215
|
+
c.texmap = { op };
|
|
216
|
+
const top = texStack[texStack.length - 1];
|
|
217
|
+
if (top)
|
|
218
|
+
top.fallback = true;
|
|
219
|
+
} else if (op === "END") {
|
|
220
|
+
c.texmap = { op };
|
|
221
|
+
texStack.pop();
|
|
222
|
+
}
|
|
223
|
+
} else if (tk[1] === "!:") {
|
|
224
|
+
texNext = null;
|
|
225
|
+
const g = geomLine(tk.slice(2));
|
|
226
|
+
if (g) {
|
|
227
|
+
annotate(g, true);
|
|
228
|
+
c.geom = g;
|
|
229
|
+
}
|
|
230
|
+
} else
|
|
231
|
+
texNext = null;
|
|
56
232
|
const moved = text.match(MOVED_RE);
|
|
57
233
|
if (moved && !cur.movedTo) {
|
|
58
234
|
let target = moved[1].trim().replace(/\\/g, "/");
|
|
@@ -63,22 +239,109 @@ function parse(src, name = "main.ldr") {
|
|
|
63
239
|
cur.cmds.push(c);
|
|
64
240
|
continue;
|
|
65
241
|
}
|
|
242
|
+
if (dataCur || skip)
|
|
243
|
+
continue;
|
|
66
244
|
if (!cur)
|
|
67
245
|
start(name);
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
246
|
+
const g = geomLine(tk);
|
|
247
|
+
if (!g)
|
|
248
|
+
continue;
|
|
249
|
+
annotate(g, false);
|
|
250
|
+
cur.cmds.push(g);
|
|
251
|
+
}
|
|
252
|
+
closeData();
|
|
253
|
+
return data ? { main, files, data } : { main, files };
|
|
254
|
+
}
|
|
255
|
+
function resolveTexture(doc, name) {
|
|
256
|
+
if (!doc.data)
|
|
257
|
+
return null;
|
|
258
|
+
const n = norm(name);
|
|
259
|
+
return doc.data.get(n) ?? doc.data.get(n.replace(/^textures\//, "")) ?? doc.data.get("textures/" + n) ?? null;
|
|
260
|
+
}
|
|
261
|
+
var sub3 = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
262
|
+
var dot3 = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
263
|
+
var cross3 = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
264
|
+
var unit3 = (a) => {
|
|
265
|
+
const l = Math.hypot(a[0], a[1], a[2]) || 1;
|
|
266
|
+
return [a[0] / l, a[1] / l, a[2] / l];
|
|
267
|
+
};
|
|
268
|
+
var DEG = Math.PI / 180;
|
|
269
|
+
function texmapUV(map, p) {
|
|
270
|
+
const w = texmapTriangleUV(map, [p, p, p]);
|
|
271
|
+
const u = w[0], v = w[1];
|
|
272
|
+
return { u, v, inside: u >= -0.000001 && u <= 1 + 0.000001 && v >= -0.000001 && v <= 1 + 0.000001 };
|
|
273
|
+
}
|
|
274
|
+
var radial = (p, c, n) => {
|
|
275
|
+
const d = sub3(p, c);
|
|
276
|
+
const k = dot3(d, n);
|
|
277
|
+
const r = [d[0] - n[0] * k, d[1] - n[1] * k, d[2] - n[2] * k];
|
|
278
|
+
return Math.hypot(r[0], r[1], r[2]) < 0.00001 ? null : unit3(r);
|
|
279
|
+
};
|
|
280
|
+
function texmapTriangleUV(map, pts) {
|
|
281
|
+
const { p1, p2, p3 } = map;
|
|
282
|
+
const out = new Array(pts.length * 2).fill(0);
|
|
283
|
+
if (map.method === "PLANAR") {
|
|
284
|
+
const e1 = sub3(p2, p1), e2 = sub3(p3, p1);
|
|
285
|
+
const l1 = dot3(e1, e1) || 1, l2 = dot3(e2, e2) || 1;
|
|
286
|
+
pts.forEach((p, i) => {
|
|
287
|
+
const d = sub3(p, p1);
|
|
288
|
+
out[i * 2] = dot3(d, e1) / l1;
|
|
289
|
+
out[i * 2 + 1] = dot3(d, e2) / l2;
|
|
290
|
+
});
|
|
291
|
+
return out;
|
|
80
292
|
}
|
|
81
|
-
|
|
293
|
+
const cyl = map.method === "CYLINDRICAL";
|
|
294
|
+
const n = cyl ? unit3(sub3(p2, p1)) : unit3(cross3(sub3(p1, p2), sub3(p3, p2)).map((x) => -x));
|
|
295
|
+
const dir = cyl ? radial(p3, p1, n) : unit3(sub3(p2, p1));
|
|
296
|
+
const A = (map.a ?? 360) * DEG;
|
|
297
|
+
let base = null, baseAng = 0;
|
|
298
|
+
const sAng = (q) => {
|
|
299
|
+
if (!base) {
|
|
300
|
+
base = q;
|
|
301
|
+
baseAng = Math.atan2(dot3(cross3(dir, q), n), dot3(q, dir));
|
|
302
|
+
return baseAng;
|
|
303
|
+
}
|
|
304
|
+
return Math.atan2(dot3(cross3(base, q), n), dot3(q, base)) + baseAng;
|
|
305
|
+
};
|
|
306
|
+
const miss = [];
|
|
307
|
+
let uSum = 0;
|
|
308
|
+
const h = cyl ? Math.hypot(...sub3(p2, p1)) || 1 : 0, n2 = cyl ? null : unit3(cross3(n, sub3(p2, p1))), B = (map.b ?? 180) * DEG;
|
|
309
|
+
pts.forEach((p, i) => {
|
|
310
|
+
if (cyl) {
|
|
311
|
+
const q = dir ? radial(p, p1, n) : null;
|
|
312
|
+
if (q) {
|
|
313
|
+
const a = sAng(q);
|
|
314
|
+
out[i * 2] = 0.5 + a / A;
|
|
315
|
+
uSum += out[i * 2];
|
|
316
|
+
} else
|
|
317
|
+
miss.push(i);
|
|
318
|
+
out[i * 2 + 1] = dot3(n, sub3(p2, p)) / h;
|
|
319
|
+
} else {
|
|
320
|
+
const pd = unit3(sub3(p, p1));
|
|
321
|
+
const c = dot3(pd, n);
|
|
322
|
+
if (Math.abs(c) > 1 - 0.000000001) {
|
|
323
|
+
miss.push(i);
|
|
324
|
+
out[i * 2 + 1] = 0.5 + (c > 0 ? -Math.PI / 2 : Math.PI / 2) / B;
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const a = sAng(radial(p, p1, n));
|
|
328
|
+
uSum += a;
|
|
329
|
+
out[i * 2] = 0.5 + a / A;
|
|
330
|
+
const th = -a, ct = Math.cos(th), st = Math.sin(th), cr = cross3(n, pd), k = dot3(n, pd) * (1 - ct);
|
|
331
|
+
const r = unit3([pd[0] * ct + cr[0] * st + n[0] * k, pd[1] * ct + cr[1] * st + n[1] * k, pd[2] * ct + cr[2] * st + n[2] * k]);
|
|
332
|
+
out[i * 2 + 1] = 0.5 + Math.atan2(dot3(cross3(dir, r), n2), dot3(r, dir)) / B;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
if (miss.length && miss.length < pts.length) {
|
|
336
|
+
const avg = cyl ? uSum / (pts.length - miss.length) : 0.5 + uSum / (pts.length - miss.length) / A;
|
|
337
|
+
for (const i of miss)
|
|
338
|
+
out[i * 2] = avg;
|
|
339
|
+
}
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
function transformTexMap(map, m) {
|
|
343
|
+
const x = (q) => [m[3] * q[0] + m[4] * q[1] + m[5] * q[2] + m[0], m[6] * q[0] + m[7] * q[1] + m[8] * q[2] + m[1], m[9] * q[0] + m[10] * q[1] + m[11] * q[2] + m[2]];
|
|
344
|
+
return { ...map, p1: x(map.p1), p2: x(map.p2), p3: x(map.p3) };
|
|
82
345
|
}
|
|
83
346
|
function mul(a, b) {
|
|
84
347
|
const [ax, ay, az, a0, a1, a2, a3, a4, a5, a6, a7, a8] = a, [bx, by, bz, b0, b1, b2, b3, b4, b5, b6, b7, b8] = b;
|
|
@@ -241,7 +504,7 @@ function decodePack(buf) {
|
|
|
241
504
|
const hl = new DataView(buf).getUint32(0, true);
|
|
242
505
|
const header = JSON.parse(new TextDecoder().decode(new Uint8Array(buf, 4, hl)));
|
|
243
506
|
const base = 4 + hl;
|
|
244
|
-
const arr = (r) => r && (r[2] === "f32" ? new Float32Array(buf, base + r[0], r[1]) : r[2] === "u16" ? new Uint16Array(buf, base + r[0], r[1]) : new Uint32Array(buf, base + r[0], r[1]));
|
|
507
|
+
const arr = (r) => r && (r[2] === "f32" ? new Float32Array(buf, base + r[0], r[1]) : r[2] === "u16" ? new Uint16Array(buf, base + r[0], r[1]) : r[2] === "u8" ? new Uint8Array(buf, base + r[0], r[1]) : new Uint32Array(buf, base + r[0], r[1]));
|
|
245
508
|
return { header, arr, bytes: buf.byteLength };
|
|
246
509
|
}
|
|
247
510
|
function geom(b, arr) {
|
|
@@ -253,6 +516,8 @@ function geom(b, arr) {
|
|
|
253
516
|
g.setAttribute("ldrColor", new THREE.BufferAttribute(arr(b.col), 4));
|
|
254
517
|
if (b.idx)
|
|
255
518
|
g.setIndex(new THREE.BufferAttribute(arr(b.idx), 1));
|
|
519
|
+
if (b.uv)
|
|
520
|
+
g.setAttribute("ldrUV", new THREE.BufferAttribute(arr(b.uv), 2));
|
|
256
521
|
return g;
|
|
257
522
|
}
|
|
258
523
|
function makeMaterial(side) {
|
|
@@ -268,6 +533,41 @@ function makeMaterial(side) {
|
|
|
268
533
|
m.customProgramCacheKey = () => "ldrColor";
|
|
269
534
|
return m;
|
|
270
535
|
}
|
|
536
|
+
function makeTexturedMaterial(side, map, wrapU = false) {
|
|
537
|
+
const m = makeMaterial(side);
|
|
538
|
+
const base = m.onBeforeCompile;
|
|
539
|
+
m.onBeforeCompile = (sh, r) => {
|
|
540
|
+
base.call(m, sh, r);
|
|
541
|
+
sh.uniforms.ldrMap = { value: map };
|
|
542
|
+
sh.uniforms.ldrWrapU = { value: wrapU ? 1 : 0 };
|
|
543
|
+
sh.vertexShader = `attribute vec2 ldrUV; varying vec2 vLdrUV;
|
|
544
|
+
` + sh.vertexShader.replace("#include <uv_vertex>", `#include <uv_vertex>
|
|
545
|
+
vLdrUV = ldrUV;`);
|
|
546
|
+
sh.fragmentShader = `uniform sampler2D ldrMap; uniform int ldrWrapU; varying vec2 vLdrUV;
|
|
547
|
+
` + sh.fragmentShader.replace("#include <color_fragment>", `#include <color_fragment>
|
|
548
|
+
{ vec2 uv = vec2(ldrWrapU == 1 ? fract(vLdrUV.x) : vLdrUV.x, vLdrUV.y); vec4 t = texture2D(ldrMap, uv);
|
|
549
|
+
float inside = step(0.0, uv.x) * step(uv.x, 1.0) * step(0.0, uv.y) * step(uv.y, 1.0);
|
|
550
|
+
diffuseColor.rgb = mix(diffuseColor.rgb, t.rgb, t.a * inside); }`);
|
|
551
|
+
};
|
|
552
|
+
m.customProgramCacheKey = () => "ldrColorTex";
|
|
553
|
+
m.userData.ldrMap = map;
|
|
554
|
+
return m;
|
|
555
|
+
}
|
|
556
|
+
function packTexture(pack, i) {
|
|
557
|
+
const t = new THREE.Texture;
|
|
558
|
+
t.flipY = false;
|
|
559
|
+
t.colorSpace = THREE.SRGBColorSpace;
|
|
560
|
+
t.wrapS = t.wrapT = THREE.ClampToEdgeWrapping;
|
|
561
|
+
const e = pack.header.textures?.[i];
|
|
562
|
+
const bytes = e?.data ? pack.arr(e.data) : null;
|
|
563
|
+
if (!bytes || typeof createImageBitmap === "undefined")
|
|
564
|
+
return { texture: t, ready: Promise.resolve() };
|
|
565
|
+
const ready = createImageBitmap(new Blob([bytes], { type: e.mime || "image/png" }), { premultiplyAlpha: "none", colorSpaceConversion: "none" }).then((img) => {
|
|
566
|
+
t.image = img;
|
|
567
|
+
t.needsUpdate = true;
|
|
568
|
+
}).catch(() => {});
|
|
569
|
+
return { texture: t, ready };
|
|
570
|
+
}
|
|
271
571
|
var lineMat = () => new THREE.ShaderMaterial({
|
|
272
572
|
vertexShader: `attribute vec4 im0; attribute vec4 im1; attribute vec4 im2; attribute vec4 im3;
|
|
273
573
|
void main(){ gl_Position = projectionMatrix * modelViewMatrix * mat4(im0,im1,im2,im3) * vec4(position,1.0); }`,
|
|
@@ -296,6 +596,109 @@ var weldLines = (pos) => {
|
|
|
296
596
|
function createInstancedEdges(pos, im, material = sharedLineMat()) {
|
|
297
597
|
return sharedLines(pos, im, material);
|
|
298
598
|
}
|
|
599
|
+
function conditionalLines(pack, entry, which = "all") {
|
|
600
|
+
if (which === "all") {
|
|
601
|
+
const a = conditionalLines(pack, entry, "body"), b = conditionalLines(pack, entry, "under");
|
|
602
|
+
if (!a || !b)
|
|
603
|
+
return a ?? b;
|
|
604
|
+
const o = new Float32Array(a.length + b.length);
|
|
605
|
+
o.set(a);
|
|
606
|
+
o.set(b, a.length);
|
|
607
|
+
return o;
|
|
608
|
+
}
|
|
609
|
+
const c = which === "under" ? entry?.condlu : entry?.condl;
|
|
610
|
+
if (!c)
|
|
611
|
+
return null;
|
|
612
|
+
const { arr } = pack;
|
|
613
|
+
if (c.raw)
|
|
614
|
+
return arr(c.raw);
|
|
615
|
+
const pools = [entry.body ? arr(entry.body.pos) : [], entry.under ? arr(entry.under.pos) : [], entry.edges ? arr(entry.edges) : [], c.x ? arr(c.x) : []];
|
|
616
|
+
const idx = arr(c.idx);
|
|
617
|
+
const out = new Float32Array(idx.length * 3);
|
|
618
|
+
const starts = [];
|
|
619
|
+
let base = 0;
|
|
620
|
+
for (const p of pools) {
|
|
621
|
+
starts.push(base);
|
|
622
|
+
base += p.length / 3;
|
|
623
|
+
}
|
|
624
|
+
let prev = 0;
|
|
625
|
+
for (let i = 0;i < idx.length; i++) {
|
|
626
|
+
const z = idx[i];
|
|
627
|
+
const j = prev + (z & 1 ? -(z + 1) / 2 : z / 2);
|
|
628
|
+
prev = j;
|
|
629
|
+
let k = 3;
|
|
630
|
+
while (k > 0 && j < starts[k])
|
|
631
|
+
k--;
|
|
632
|
+
const p = pools[k], o = (j - starts[k]) * 3;
|
|
633
|
+
out[i * 3] = p[o];
|
|
634
|
+
out[i * 3 + 1] = p[o + 1];
|
|
635
|
+
out[i * 3 + 2] = p[o + 2];
|
|
636
|
+
}
|
|
637
|
+
return out;
|
|
638
|
+
}
|
|
639
|
+
var condLineMat = () => new THREE.ShaderMaterial({
|
|
640
|
+
uniforms: { ldrViewProj: { value: new THREE.Matrix4 } },
|
|
641
|
+
vertexShader: `uniform mat4 ldrViewProj; attribute mat4 instanceMatrix; attribute vec3 ldrP1; attribute vec3 ldrC0; attribute vec3 ldrC1; attribute float ldrSide;
|
|
642
|
+
void main(){
|
|
643
|
+
mat4 mvp = ldrViewProj * instanceMatrix;
|
|
644
|
+
vec4 a = mvp * vec4(position, 1.0), b = mvp * vec4(ldrP1, 1.0), c0 = mvp * vec4(ldrC0, 1.0), c1 = mvp * vec4(ldrC1, 1.0);
|
|
645
|
+
vec2 A = a.xy / a.w, B = b.xy / b.w, C0 = c0.xy / c0.w, C1 = c1.xy / c1.w, d = B - A;
|
|
646
|
+
float s0 = d.x * (C0.y - A.y) - d.y * (C0.x - A.x), s1 = d.x * (C1.y - A.y) - d.y * (C1.x - A.x);
|
|
647
|
+
gl_Position = s0 * s1 >= 0.0 ? (ldrSide > 0.5 ? b : a) : vec4(2.0, 2.0, 2.0, 1.0);
|
|
648
|
+
}`,
|
|
649
|
+
fragmentShader: `void main(){ gl_FragColor = vec4(0.13,0.13,0.13,1.0); }`
|
|
650
|
+
});
|
|
651
|
+
function conditionalLineVisible(cond, i, mvp) {
|
|
652
|
+
const e = mvp.elements;
|
|
653
|
+
const pr = (o) => {
|
|
654
|
+
const x = cond[o], y = cond[o + 1], z = cond[o + 2];
|
|
655
|
+
const w = e[3] * x + e[7] * y + e[11] * z + e[15];
|
|
656
|
+
return [(e[0] * x + e[4] * y + e[8] * z + e[12]) / w, (e[1] * x + e[5] * y + e[9] * z + e[13]) / w];
|
|
657
|
+
};
|
|
658
|
+
const o = i * 12;
|
|
659
|
+
const A = pr(o), B = pr(o + 3), C0 = pr(o + 6), C1 = pr(o + 9);
|
|
660
|
+
const dx = B[0] - A[0], dy = B[1] - A[1];
|
|
661
|
+
const s0 = dx * (C0[1] - A[1]) - dy * (C0[0] - A[0]), s1 = dx * (C1[1] - A[1]) - dy * (C1[0] - A[0]);
|
|
662
|
+
return s0 * s1 >= 0;
|
|
663
|
+
}
|
|
664
|
+
function createInstancedConditionalEdges(cond, im, material = condLineMat()) {
|
|
665
|
+
return condEdges(condBuffers(cond), im, material);
|
|
666
|
+
}
|
|
667
|
+
function condBuffers(cond) {
|
|
668
|
+
const n = Math.floor(cond.length / 12);
|
|
669
|
+
const buf = new Float32Array(n * 24), side = new Float32Array(n * 2);
|
|
670
|
+
for (let l = 0;l < n; l++) {
|
|
671
|
+
for (let k = 0;k < 12; k++) {
|
|
672
|
+
const v = cond[l * 12 + k];
|
|
673
|
+
buf[l * 24 + k] = v;
|
|
674
|
+
buf[l * 24 + 12 + k] = v;
|
|
675
|
+
}
|
|
676
|
+
side[l * 2 + 1] = 1;
|
|
677
|
+
}
|
|
678
|
+
return { ib: new THREE.InterleavedBuffer(buf, 12), side: new THREE.BufferAttribute(side, 1) };
|
|
679
|
+
}
|
|
680
|
+
function condEdges({ ib, side }, im, material) {
|
|
681
|
+
const g = new THREE.InstancedBufferGeometry;
|
|
682
|
+
g.setAttribute("position", new THREE.InterleavedBufferAttribute(ib, 3, 0));
|
|
683
|
+
g.setAttribute("ldrP1", new THREE.InterleavedBufferAttribute(ib, 3, 3));
|
|
684
|
+
g.setAttribute("ldrC0", new THREE.InterleavedBufferAttribute(ib, 3, 6));
|
|
685
|
+
g.setAttribute("ldrC1", new THREE.InterleavedBufferAttribute(ib, 3, 9));
|
|
686
|
+
g.setAttribute("ldrSide", side);
|
|
687
|
+
g.setAttribute("instanceMatrix", im.instanceMatrix);
|
|
688
|
+
g.instanceCount = im.count;
|
|
689
|
+
const l = new THREE.LineSegments(g, material);
|
|
690
|
+
l.frustumCulled = false;
|
|
691
|
+
l.name = "ldrCondLines";
|
|
692
|
+
l.onBeforeRender = (_r, _s, cam, _g, mat) => {
|
|
693
|
+
const u = mat.uniforms?.ldrViewProj;
|
|
694
|
+
if (!u)
|
|
695
|
+
return;
|
|
696
|
+
l.modelViewMatrix.multiplyMatrices(cam.matrixWorldInverse, l.matrixWorld);
|
|
697
|
+
u.value.multiplyMatrices(cam.projectionMatrix, l.modelViewMatrix);
|
|
698
|
+
mat.uniformsNeedUpdate = true;
|
|
699
|
+
};
|
|
700
|
+
return l;
|
|
701
|
+
}
|
|
299
702
|
function sharedLines(pos, im, material = sharedLineMat()) {
|
|
300
703
|
const { P, I } = weldLines(pos);
|
|
301
704
|
const g = new THREE.InstancedBufferGeometry;
|
|
@@ -426,29 +829,96 @@ function buildLdrScene(insts, pack, P, color, mark) {
|
|
|
426
829
|
const cull = computeCulling(insts, H);
|
|
427
830
|
mark("culling");
|
|
428
831
|
const edgeObjs = [];
|
|
832
|
+
const condObjs = [];
|
|
833
|
+
const wantCond = P.cond !== false && P.edges === "idx";
|
|
834
|
+
let condMat = null;
|
|
835
|
+
const cmat = () => condMat ??= condLineMat();
|
|
836
|
+
const condCache = new Map;
|
|
837
|
+
const addCond = (entry, im, which = "body") => {
|
|
838
|
+
if (!wantCond)
|
|
839
|
+
return null;
|
|
840
|
+
const ck = which === "under" ? entry.condlu : entry.condl;
|
|
841
|
+
if (!ck)
|
|
842
|
+
return null;
|
|
843
|
+
let cb = condCache.get(ck);
|
|
844
|
+
if (cb === undefined) {
|
|
845
|
+
const c = conditionalLines(pack, entry, which);
|
|
846
|
+
cb = c && c.length ? condBuffers(c) : null;
|
|
847
|
+
condCache.set(ck, cb);
|
|
848
|
+
}
|
|
849
|
+
if (!cb)
|
|
850
|
+
return null;
|
|
851
|
+
const l = condEdges(cb, im, cmat());
|
|
852
|
+
edgeObjs.push(l);
|
|
853
|
+
condObjs.push(l);
|
|
854
|
+
root.add(l);
|
|
855
|
+
return l;
|
|
856
|
+
};
|
|
429
857
|
const stats = { studsTotal: cull.studs.length, studsHidden: cull.studs.filter((s) => s.hidden).length, undersTotal: 0, undersHidden: 0 };
|
|
430
858
|
const toggles = [];
|
|
431
859
|
const studMeshes = [];
|
|
860
|
+
const wantTex = P.tex !== false && !!H.textures;
|
|
861
|
+
const texCache = new Map;
|
|
862
|
+
const texMats = new Map;
|
|
863
|
+
const pending = [];
|
|
864
|
+
const texMat = (t, base, wrapU) => {
|
|
865
|
+
const role = base === matF ? "F" : base === matB ? "B" : "D";
|
|
866
|
+
const k = t + ":" + role + ":" + (wrapU ? 1 : 0);
|
|
867
|
+
let m = texMats.get(k);
|
|
868
|
+
if (!m) {
|
|
869
|
+
let tx = texCache.get(t);
|
|
870
|
+
if (!tx) {
|
|
871
|
+
const r = packTexture(pack, t);
|
|
872
|
+
tx = r.texture;
|
|
873
|
+
pending.push(r.ready);
|
|
874
|
+
texCache.set(t, tx);
|
|
875
|
+
}
|
|
876
|
+
texMats.set(k, m = makeTexturedMaterial(base.side, tx, wrapU));
|
|
877
|
+
}
|
|
878
|
+
return m;
|
|
879
|
+
};
|
|
880
|
+
let texBatches = 0;
|
|
432
881
|
for (const [pk, ids] of byPart) {
|
|
433
882
|
const part = pk.replace(/#m$/, "");
|
|
434
883
|
const p = H.parts[part];
|
|
435
884
|
const mat = P.uncertDouble !== false && p.uncert ? (nUncert++, matD) : pk.endsWith("#m") ? matB : matF;
|
|
436
|
-
const
|
|
885
|
+
const textured = wantTex && !!p.tex;
|
|
886
|
+
const body = geom(textured ? p.tbody : p.body, arr);
|
|
437
887
|
let bodyIm = null;
|
|
438
|
-
|
|
439
|
-
const im = bodyIm = new THREE.InstancedMesh(body, mat, ids.length);
|
|
888
|
+
const place = (im) => {
|
|
440
889
|
ids.forEach((id, k) => {
|
|
441
890
|
im.setMatrixAt(k, toM4(insts[id].m));
|
|
442
891
|
im.setColorAt(k, color(insts[id].color));
|
|
443
892
|
});
|
|
444
893
|
im.computeBoundingSphere();
|
|
445
894
|
root.add(im);
|
|
895
|
+
};
|
|
896
|
+
if (body) {
|
|
897
|
+
const im = bodyIm = new THREE.InstancedMesh(body, mat, ids.length);
|
|
898
|
+
place(im);
|
|
446
899
|
}
|
|
900
|
+
if (textured)
|
|
901
|
+
for (const tb of p.tex) {
|
|
902
|
+
const g = geom(tb, arr);
|
|
903
|
+
const im = new THREE.InstancedMesh(g, texMat(tb.t, mat, !!tb.wrapU), ids.length);
|
|
904
|
+
texBatches++;
|
|
905
|
+
if (bodyIm) {
|
|
906
|
+
im.instanceMatrix = bodyIm.instanceMatrix;
|
|
907
|
+
im.instanceColor = bodyIm.instanceColor;
|
|
908
|
+
im.boundingSphere = bodyIm.boundingSphere;
|
|
909
|
+
root.add(im);
|
|
910
|
+
} else {
|
|
911
|
+
place(im);
|
|
912
|
+
bodyIm = im;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
447
915
|
if (P.edges === "idx" && p.edges && bodyIm) {
|
|
448
916
|
const l = sharedLines(arr(p.edges), bodyIm);
|
|
449
917
|
edgeObjs.push(l);
|
|
450
918
|
root.add(l);
|
|
451
919
|
}
|
|
920
|
+
if (bodyIm)
|
|
921
|
+
addCond(p, bodyIm);
|
|
452
922
|
const under = geom(p.under, arr);
|
|
453
923
|
if (under) {
|
|
454
924
|
const ord = [...ids.filter((i) => !cull.underHidden[i]), ...ids.filter((i) => cull.underHidden[i])];
|
|
@@ -464,6 +934,15 @@ function buildLdrScene(insts, pack, P, color, mark) {
|
|
|
464
934
|
im.count = P.cullUnder ? vis : ids.length;
|
|
465
935
|
root.add(im);
|
|
466
936
|
toggles.push({ mesh: im, all: ids.length, visible: vis, flag: "under" });
|
|
937
|
+
{
|
|
938
|
+
const cl = addCond(p, im, "under");
|
|
939
|
+
if (cl) {
|
|
940
|
+
toggles.push({ mesh: { set count(n) {
|
|
941
|
+
cl.geometry.instanceCount = n;
|
|
942
|
+
} }, all: ids.length, visible: vis, flag: "under" });
|
|
943
|
+
cl.geometry.instanceCount = im.count;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
467
946
|
}
|
|
468
947
|
if (P.edges === true && p.edges) {
|
|
469
948
|
const mats = new Float32Array(ids.length * 16);
|
|
@@ -508,6 +987,15 @@ function buildLdrScene(insts, pack, P, color, mark) {
|
|
|
508
987
|
} }, all: ord.length, visible: vis, flag: "studs" });
|
|
509
988
|
l.geometry.instanceCount = im.count;
|
|
510
989
|
}
|
|
990
|
+
{
|
|
991
|
+
const cl = P.condStuds === false ? null : addCond(k, im);
|
|
992
|
+
if (cl) {
|
|
993
|
+
toggles.push({ mesh: { set count(n) {
|
|
994
|
+
cl.geometry.instanceCount = n;
|
|
995
|
+
} }, all: ord.length, visible: vis, flag: "studs" });
|
|
996
|
+
cl.geometry.instanceCount = im.count;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
511
999
|
if (P.edges === true && k.edges) {
|
|
512
1000
|
const mats = new Float32Array(ord.length * 16);
|
|
513
1001
|
ord.forEach((s, i) => toM4(s.w).toArray(mats, i * 16));
|
|
@@ -521,7 +1009,19 @@ function buildLdrScene(insts, pack, P, color, mark) {
|
|
|
521
1009
|
}
|
|
522
1010
|
stats.uncertParts = Object.values(H.parts).filter((x) => x.uncert).length;
|
|
523
1011
|
stats.uncertBatches = nUncert;
|
|
524
|
-
|
|
1012
|
+
let condOn = true, edgesOn = true;
|
|
1013
|
+
const setEdges = (v) => {
|
|
1014
|
+
edgesOn = v;
|
|
1015
|
+
edgeObjs.forEach((o) => o.visible = v && (condOn || !condObjs.includes(o)));
|
|
1016
|
+
};
|
|
1017
|
+
const setCond = (v) => {
|
|
1018
|
+
condOn = v;
|
|
1019
|
+
condObjs.forEach((o) => o.visible = v && edgesOn);
|
|
1020
|
+
};
|
|
1021
|
+
stats.condBatches = condObjs.length;
|
|
1022
|
+
stats.texBatches = texBatches;
|
|
1023
|
+
stats.textures = texCache.size;
|
|
1024
|
+
const texturesReady = Promise.all(pending).then(() => {});
|
|
525
1025
|
const setCull = (studs, under) => {
|
|
526
1026
|
for (const t of toggles)
|
|
527
1027
|
t.mesh.count = (t.flag === "studs" ? studs : under) ? t.visible : t.all;
|
|
@@ -537,6 +1037,11 @@ function buildLdrScene(insts, pack, P, color, mark) {
|
|
|
537
1037
|
matF.side = front ? THREE.FrontSide : THREE.DoubleSide;
|
|
538
1038
|
matB.side = front ? THREE.BackSide : THREE.DoubleSide;
|
|
539
1039
|
matF.needsUpdate = matB.needsUpdate = true;
|
|
1040
|
+
for (const [k, m] of texMats) {
|
|
1041
|
+
const role = k.split(":")[1];
|
|
1042
|
+
m.side = role === "D" || !front ? THREE.DoubleSide : role === "B" ? THREE.BackSide : THREE.FrontSide;
|
|
1043
|
+
m.needsUpdate = true;
|
|
1044
|
+
}
|
|
540
1045
|
};
|
|
541
1046
|
const swapStuds = (alt) => {
|
|
542
1047
|
const saved = studMeshes.map((s) => s.im.geometry);
|
|
@@ -547,7 +1052,7 @@ function buildLdrScene(insts, pack, P, color, mark) {
|
|
|
547
1052
|
}
|
|
548
1053
|
return () => studMeshes.forEach((s, i) => s.im.geometry = saved[i]);
|
|
549
1054
|
};
|
|
550
|
-
return { root, stats, setCull, setSide, setEdges, swapStuds, box };
|
|
1055
|
+
return { root, stats, setCull, setSide, setEdges, setCond, swapStuds, box, texturesReady };
|
|
551
1056
|
}
|
|
552
1057
|
// src/scene.ts
|
|
553
1058
|
import * as THREE2 from "three";
|
|
@@ -584,13 +1089,14 @@ function toPlastic(old) {
|
|
|
584
1089
|
if (!m) {
|
|
585
1090
|
const p = new THREE2.MeshPhysicalMaterial({ vertexColors: true, roughness: 0.3, clearcoat: 0.3, clearcoatRoughness: 0.2, specularIntensity: 0.6, side: old.side });
|
|
586
1091
|
p.onBeforeCompile = old.onBeforeCompile;
|
|
587
|
-
p.customProgramCacheKey = () => "
|
|
1092
|
+
p.customProgramCacheKey = () => "Phys" + old.customProgramCacheKey() + old.side;
|
|
1093
|
+
p.userData = old.userData;
|
|
588
1094
|
plasticCache.set(old, m = p);
|
|
589
1095
|
}
|
|
590
1096
|
return m;
|
|
591
1097
|
}
|
|
592
1098
|
function createLdrObject(instances, pack, options = {}) {
|
|
593
|
-
const o = { edges: true, cullStuds: true, cullUndersides: true, side: "front", material: "plastic", ...options };
|
|
1099
|
+
const o = { edges: true, conditionalLines: true, cullStuds: true, cullUndersides: true, side: "front", material: "plastic", ...options };
|
|
594
1100
|
const color = colorFn(o.colors, pack, new THREE2.Color(o.fallbackColor ?? 10528169));
|
|
595
1101
|
const all = instances.map((i) => ({ part: i.part, color: String(i.color), m: i.m, steps: i.steps }));
|
|
596
1102
|
const key = (i) => i.steps.map((x) => String(x).padStart(6, "0")).join(".");
|
|
@@ -606,7 +1112,7 @@ function createLdrObject(instances, pack, options = {}) {
|
|
|
606
1112
|
object.scale.setScalar(o.scale);
|
|
607
1113
|
const pairs = new Map;
|
|
608
1114
|
let cur = null;
|
|
609
|
-
let { edges, cullStuds: cs, cullUndersides: cu, side } = o;
|
|
1115
|
+
let { edges, conditionalLines: cond, cullStuds: cs, cullUndersides: cu, side } = o;
|
|
610
1116
|
const stats = { instances: all.length, drawn: 0, missing, studsTotal: 0, studsHidden: 0, undersTotal: 0, undersHidden: 0, buildMs: 0 };
|
|
611
1117
|
const disposeCur = () => {
|
|
612
1118
|
if (!cur)
|
|
@@ -620,17 +1126,17 @@ function createLdrObject(instances, pack, options = {}) {
|
|
|
620
1126
|
if (x.dispose && x.isInstancedMesh)
|
|
621
1127
|
x.dispose();
|
|
622
1128
|
});
|
|
623
|
-
for (const m of mats)
|
|
1129
|
+
for (const m of [...mats, ...pairs.values()]) {
|
|
1130
|
+
m.userData.ldrMap?.dispose();
|
|
624
1131
|
m.dispose();
|
|
625
|
-
|
|
626
|
-
old.dispose();
|
|
1132
|
+
}
|
|
627
1133
|
cur = null;
|
|
628
1134
|
};
|
|
629
1135
|
const build = (step) => {
|
|
630
1136
|
const t0 = performance.now();
|
|
631
1137
|
disposeCur();
|
|
632
1138
|
const insts = step == null ? all : all.filter((_, k) => stepOf[k] <= step);
|
|
633
|
-
cur = buildLdrScene(insts, pack, { pack: "", cullStuds: cs, cullUnder: cu, edges: edges ? "idx" : false, side }, color, () => {});
|
|
1139
|
+
cur = buildLdrScene(insts, pack, { pack: "", cullStuds: cs, cullUnder: cu, edges: edges ? "idx" : false, side, cond, condStuds: o.conditionalStudLines !== false, tex: o.textures !== false }, color, () => {});
|
|
634
1140
|
pairs.clear();
|
|
635
1141
|
if (o.material === "plastic")
|
|
636
1142
|
cur.root.traverse((x) => {
|
|
@@ -642,11 +1148,15 @@ function createLdrObject(instances, pack, options = {}) {
|
|
|
642
1148
|
});
|
|
643
1149
|
if (!edges)
|
|
644
1150
|
cur.setEdges(false);
|
|
1151
|
+
if (!cond)
|
|
1152
|
+
cur.setCond(false);
|
|
645
1153
|
object.add(cur.root);
|
|
646
|
-
Object.assign(stats, { drawn: insts.filter((i) => pack.header.parts[i.part]).length, studsTotal: cur.stats.studsTotal, studsHidden: cur.stats.studsHidden, undersTotal: cur.stats.undersTotal, undersHidden: cur.stats.undersHidden, buildMs: performance.now() - t0 });
|
|
1154
|
+
Object.assign(stats, { drawn: insts.filter((i) => pack.header.parts[i.part]).length, studsTotal: cur.stats.studsTotal, studsHidden: cur.stats.studsHidden, undersTotal: cur.stats.undersTotal, undersHidden: cur.stats.undersHidden, condBatches: cur.stats.condBatches ?? 0, texBatches: cur.stats.texBatches ?? 0, textures: cur.stats.textures ?? 0, buildMs: performance.now() - t0 });
|
|
1155
|
+
api.texturesReady = cur.texturesReady;
|
|
647
1156
|
};
|
|
1157
|
+
const api = { texturesReady: Promise.resolve() };
|
|
648
1158
|
build(o.step ?? null);
|
|
649
|
-
return {
|
|
1159
|
+
return Object.assign(api, {
|
|
650
1160
|
object,
|
|
651
1161
|
stats,
|
|
652
1162
|
stepCount: uniq.length,
|
|
@@ -655,6 +1165,10 @@ function createLdrObject(instances, pack, options = {}) {
|
|
|
655
1165
|
edges = v;
|
|
656
1166
|
cur.setEdges(v);
|
|
657
1167
|
},
|
|
1168
|
+
setConditionalLines: (v) => {
|
|
1169
|
+
cond = v;
|
|
1170
|
+
cur.setCond(v);
|
|
1171
|
+
},
|
|
658
1172
|
setCulling: (a, b) => {
|
|
659
1173
|
cs = a;
|
|
660
1174
|
cu = b;
|
|
@@ -681,7 +1195,7 @@ function createLdrObject(instances, pack, options = {}) {
|
|
|
681
1195
|
return target;
|
|
682
1196
|
},
|
|
683
1197
|
dispose: disposeCur
|
|
684
|
-
};
|
|
1198
|
+
});
|
|
685
1199
|
}
|
|
686
1200
|
// src/pack-io.ts
|
|
687
1201
|
import { decompress } from "fzstd";
|
|
@@ -698,6 +1212,9 @@ export {
|
|
|
698
1212
|
I as IDENTITY,
|
|
699
1213
|
buildLdrScene,
|
|
700
1214
|
computeCulling,
|
|
1215
|
+
conditionalLineVisible,
|
|
1216
|
+
conditionalLines,
|
|
1217
|
+
createInstancedConditionalEdges,
|
|
701
1218
|
createInstancedEdges,
|
|
702
1219
|
createLdrObject,
|
|
703
1220
|
decodePack,
|
|
@@ -712,6 +1229,11 @@ export {
|
|
|
712
1229
|
parseColor,
|
|
713
1230
|
parseLDConfig,
|
|
714
1231
|
parseModel,
|
|
1232
|
+
parseTexMap,
|
|
1233
|
+
resolveTexture,
|
|
715
1234
|
sequenceSteps,
|
|
716
|
-
smoothNormals
|
|
1235
|
+
smoothNormals,
|
|
1236
|
+
texmapTriangleUV,
|
|
1237
|
+
texmapUV,
|
|
1238
|
+
transformTexMap
|
|
717
1239
|
};
|