layershift 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -60
- package/dist/components/layershift.js +537 -7
- package/dist/npm/layershift.es.js +2605 -366
- package/dist/types/components/layershift/index.d.ts +8 -3
- package/dist/types/components/layershift/index.d.ts.map +1 -1
- package/dist/types/components/layershift/portal-element.d.ts +88 -0
- package/dist/types/components/layershift/portal-element.d.ts.map +1 -0
- package/dist/types/components/layershift/types.d.ts +130 -0
- package/dist/types/components/layershift/types.d.ts.map +1 -1
- package/dist/types/portal-renderer.d.ts +185 -0
- package/dist/types/portal-renderer.d.ts.map +1 -0
- package/dist/types/shape-generator.d.ts +83 -0
- package/dist/types/shape-generator.d.ts.map +1 -0
- package/package.json +3 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class st {
|
|
2
2
|
worker;
|
|
3
3
|
currentBuffer;
|
|
4
4
|
pendingTimeSec = null;
|
|
@@ -14,28 +14,28 @@ class _ {
|
|
|
14
14
|
* After this call, the original depthData.frames arrays are neutered
|
|
15
15
|
* and should not be accessed.
|
|
16
16
|
*/
|
|
17
|
-
static async create(t, e,
|
|
18
|
-
const
|
|
17
|
+
static async create(t, e, i) {
|
|
18
|
+
const r = new Worker(
|
|
19
19
|
new URL(
|
|
20
20
|
/* @vite-ignore */
|
|
21
21
|
"/assets/depth-worker-CMcEa805.js",
|
|
22
22
|
import.meta.url
|
|
23
23
|
),
|
|
24
24
|
{ type: "module" }
|
|
25
|
-
),
|
|
26
|
-
const
|
|
27
|
-
return
|
|
25
|
+
), o = e * i, s = new st(r, o), a = t.frames.map((h) => {
|
|
26
|
+
const l = new Uint8Array(h.length);
|
|
27
|
+
return l.set(h), l.buffer;
|
|
28
28
|
});
|
|
29
|
-
return await new Promise((
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
},
|
|
34
|
-
clearTimeout(
|
|
35
|
-
},
|
|
29
|
+
return await new Promise((h, l) => {
|
|
30
|
+
const u = setTimeout(() => l(new Error("Worker init timeout")), 1e4);
|
|
31
|
+
r.onmessage = (f) => {
|
|
32
|
+
f.data.type === "ready" && (clearTimeout(u), h());
|
|
33
|
+
}, r.onerror = (f) => {
|
|
34
|
+
clearTimeout(u), l(f);
|
|
35
|
+
}, r.postMessage(
|
|
36
36
|
{
|
|
37
37
|
type: "init",
|
|
38
|
-
frames:
|
|
38
|
+
frames: a,
|
|
39
39
|
meta: {
|
|
40
40
|
frameCount: t.meta.frameCount,
|
|
41
41
|
fps: t.meta.fps,
|
|
@@ -43,17 +43,17 @@ class _ {
|
|
|
43
43
|
height: t.meta.height
|
|
44
44
|
},
|
|
45
45
|
targetWidth: e,
|
|
46
|
-
targetHeight:
|
|
46
|
+
targetHeight: i
|
|
47
47
|
},
|
|
48
|
-
|
|
48
|
+
a
|
|
49
49
|
// Transfer list
|
|
50
50
|
);
|
|
51
|
-
}),
|
|
52
|
-
if (
|
|
53
|
-
const
|
|
54
|
-
|
|
51
|
+
}), r.onmessage = (h) => {
|
|
52
|
+
if (h.data.type === "result" && (s.currentBuffer = h.data.data, s.workerBusy = !1, s.pendingTimeSec !== null)) {
|
|
53
|
+
const l = s.pendingTimeSec;
|
|
54
|
+
s.pendingTimeSec = null, s.requestSample(l);
|
|
55
55
|
}
|
|
56
|
-
},
|
|
56
|
+
}, s;
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Get the current depth frame — always synchronous, zero main-thread work.
|
|
@@ -82,11 +82,11 @@ class _ {
|
|
|
82
82
|
this.disposed = !0, this.worker.terminate();
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
class
|
|
86
|
-
constructor(t, e,
|
|
87
|
-
this.depthData = t, this.targetWidth = e, this.targetHeight =
|
|
88
|
-
const
|
|
89
|
-
this.interpolatedDepth = new Float32Array(
|
|
85
|
+
class bt {
|
|
86
|
+
constructor(t, e, i) {
|
|
87
|
+
this.depthData = t, this.targetWidth = e, this.targetHeight = i;
|
|
88
|
+
const r = t.meta.width * t.meta.height, o = e * i;
|
|
89
|
+
this.interpolatedDepth = new Float32Array(r), this.bilateralOutput = new Float32Array(r), this.resizedDepth = new Float32Array(o), this.uint8Output = new Uint8Array(o);
|
|
90
90
|
}
|
|
91
91
|
interpolatedDepth;
|
|
92
92
|
resizedDepth;
|
|
@@ -100,21 +100,21 @@ class H {
|
|
|
100
100
|
lastNextFrameIndex = -1;
|
|
101
101
|
lastLerpFactor = -1;
|
|
102
102
|
sample(t) {
|
|
103
|
-
const e =
|
|
104
|
-
if (!
|
|
103
|
+
const e = Z(t * this.depthData.meta.fps, 0, this.depthData.meta.frameCount - 1), i = Math.floor(e), r = Math.min(i + 1, this.depthData.meta.frameCount - 1), o = e - i, s = i !== this.lastFrameIndex || r !== this.lastNextFrameIndex, a = Math.abs(o - this.lastLerpFactor) > 1e-3;
|
|
104
|
+
if (!s && !a)
|
|
105
105
|
return this.uint8Output;
|
|
106
|
-
this.lastFrameIndex =
|
|
107
|
-
const
|
|
108
|
-
for (let
|
|
109
|
-
this.interpolatedDepth[
|
|
110
|
-
|
|
106
|
+
this.lastFrameIndex = i, this.lastNextFrameIndex = r, this.lastLerpFactor = o;
|
|
107
|
+
const h = 1 - o, l = this.depthData.frames[i], u = this.depthData.frames[r];
|
|
108
|
+
for (let d = 0; d < this.interpolatedDepth.length; d += 1)
|
|
109
|
+
this.interpolatedDepth[d] = (l[d] * h + u[d] * o) / 255;
|
|
110
|
+
Ot(
|
|
111
111
|
this.interpolatedDepth,
|
|
112
112
|
this.depthData.meta.width,
|
|
113
113
|
this.depthData.meta.height,
|
|
114
114
|
this.bilateralOutput
|
|
115
115
|
);
|
|
116
|
-
const
|
|
117
|
-
|
|
116
|
+
const f = this.targetWidth !== this.depthData.meta.width || this.targetHeight !== this.depthData.meta.height;
|
|
117
|
+
f && Vt(
|
|
118
118
|
this.bilateralOutput,
|
|
119
119
|
this.depthData.meta.width,
|
|
120
120
|
this.depthData.meta.height,
|
|
@@ -122,25 +122,25 @@ class H {
|
|
|
122
122
|
this.targetHeight,
|
|
123
123
|
this.resizedDepth
|
|
124
124
|
);
|
|
125
|
-
const
|
|
126
|
-
for (let
|
|
127
|
-
this.uint8Output[
|
|
125
|
+
const c = f ? this.resizedDepth : this.bilateralOutput;
|
|
126
|
+
for (let d = 0; d < this.uint8Output.length; d += 1)
|
|
127
|
+
this.uint8Output[d] = c[d] * 255 + 0.5 | 0;
|
|
128
128
|
return this.uint8Output;
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
-
async function
|
|
132
|
-
const [
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
async function At(n, t, e) {
|
|
132
|
+
const [i, r] = await Promise.all([
|
|
133
|
+
Ct(t),
|
|
134
|
+
Mt(n)
|
|
135
135
|
]);
|
|
136
|
-
return
|
|
136
|
+
return _t(r, i);
|
|
137
137
|
}
|
|
138
|
-
async function
|
|
139
|
-
const t = await fetch(
|
|
138
|
+
async function Ct(n) {
|
|
139
|
+
const t = await fetch(n);
|
|
140
140
|
if (!t.ok)
|
|
141
141
|
throw new Error(`Failed to fetch depth metadata (${t.status} ${t.statusText}).`);
|
|
142
142
|
const e = await t.json();
|
|
143
|
-
return
|
|
143
|
+
return It(e), {
|
|
144
144
|
frameCount: e.frameCount,
|
|
145
145
|
fps: e.fps,
|
|
146
146
|
width: e.width,
|
|
@@ -148,87 +148,87 @@ async function k(o) {
|
|
|
148
148
|
sourceFps: e.sourceFps
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
|
-
async function
|
|
152
|
-
const e = await fetch(
|
|
151
|
+
async function Mt(n, t) {
|
|
152
|
+
const e = await fetch(n);
|
|
153
153
|
if (!e.ok)
|
|
154
154
|
throw new Error(`Failed to fetch depth data (${e.status} ${e.statusText}).`);
|
|
155
155
|
e.headers.get("content-length");
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
156
|
+
const i = e.body;
|
|
157
|
+
if (!i)
|
|
158
158
|
return new Uint8Array(await e.arrayBuffer());
|
|
159
|
-
const
|
|
160
|
-
let
|
|
161
|
-
const
|
|
159
|
+
const r = [];
|
|
160
|
+
let o = 0;
|
|
161
|
+
const s = i.getReader();
|
|
162
162
|
for (; ; ) {
|
|
163
|
-
const { done:
|
|
164
|
-
if (
|
|
163
|
+
const { done: l, value: u } = await s.read();
|
|
164
|
+
if (l)
|
|
165
165
|
break;
|
|
166
|
-
|
|
166
|
+
u && (r.push(u), o += u.byteLength);
|
|
167
167
|
}
|
|
168
|
-
const
|
|
169
|
-
let
|
|
170
|
-
for (const
|
|
171
|
-
|
|
172
|
-
return
|
|
168
|
+
const a = new Uint8Array(o);
|
|
169
|
+
let h = 0;
|
|
170
|
+
for (const l of r)
|
|
171
|
+
a.set(l, h), h += l.byteLength;
|
|
172
|
+
return a;
|
|
173
173
|
}
|
|
174
|
-
function
|
|
175
|
-
if (
|
|
174
|
+
function _t(n, t) {
|
|
175
|
+
if (n.byteLength < 4)
|
|
176
176
|
throw new Error("Depth data binary is missing the frame-count header.");
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
177
|
+
const i = new DataView(n.buffer, n.byteOffset, n.byteLength).getUint32(0, !0), r = t.width * t.height, o = 4 + i * r;
|
|
178
|
+
if (n.byteLength !== o)
|
|
179
179
|
throw new Error(
|
|
180
|
-
`Depth data byte length mismatch. Expected ${
|
|
180
|
+
`Depth data byte length mismatch. Expected ${o} bytes, received ${n.byteLength}.`
|
|
181
181
|
);
|
|
182
|
-
if (
|
|
182
|
+
if (i !== t.frameCount)
|
|
183
183
|
throw new Error(
|
|
184
|
-
`Depth frame count mismatch between metadata (${t.frameCount}) and binary header (${
|
|
184
|
+
`Depth frame count mismatch between metadata (${t.frameCount}) and binary header (${i}).`
|
|
185
185
|
);
|
|
186
|
-
const
|
|
187
|
-
for (let
|
|
188
|
-
const
|
|
189
|
-
|
|
186
|
+
const s = n.subarray(4), a = new Array(i);
|
|
187
|
+
for (let h = 0; h < i; h += 1) {
|
|
188
|
+
const l = h * r;
|
|
189
|
+
a[h] = s.subarray(l, l + r);
|
|
190
190
|
}
|
|
191
|
-
return { meta: t, frames:
|
|
191
|
+
return { meta: t, frames: a };
|
|
192
192
|
}
|
|
193
|
-
function
|
|
194
|
-
if (!
|
|
193
|
+
function It(n) {
|
|
194
|
+
if (!n || typeof n.frameCount != "number" || typeof n.fps != "number" || typeof n.width != "number" || typeof n.height != "number" || typeof n.sourceFps != "number")
|
|
195
195
|
throw new Error("Depth metadata is malformed.");
|
|
196
|
-
if (!Number.isFinite(
|
|
196
|
+
if (!Number.isFinite(n.frameCount) || !Number.isFinite(n.fps) || !Number.isFinite(n.width) || !Number.isFinite(n.height) || !Number.isFinite(n.sourceFps) || n.frameCount <= 0 || n.fps <= 0 || n.width <= 0 || n.height <= 0 || n.sourceFps <= 0)
|
|
197
197
|
throw new Error("Depth metadata contains invalid numeric values.");
|
|
198
198
|
}
|
|
199
|
-
function
|
|
200
|
-
const
|
|
201
|
-
for (let
|
|
202
|
-
const
|
|
203
|
-
for (let
|
|
204
|
-
const m = (
|
|
205
|
-
|
|
199
|
+
function Vt(n, t, e, i, r, o) {
|
|
200
|
+
const s = t / i, a = e / r;
|
|
201
|
+
for (let h = 0; h < r; h += 1) {
|
|
202
|
+
const l = (h + 0.5) * a - 0.5, u = Z(Math.floor(l), 0, e - 1), f = Z(u + 1, 0, e - 1), c = l - u;
|
|
203
|
+
for (let d = 0; d < i; d += 1) {
|
|
204
|
+
const m = (d + 0.5) * s - 0.5, g = Z(Math.floor(m), 0, t - 1), U = Z(g + 1, 0, t - 1), b = m - g, E = n[u * t + g], p = n[u * t + U], v = n[f * t + g], T = n[f * t + U], x = E + (p - E) * b, y = v + (T - v) * b;
|
|
205
|
+
o[h * i + d] = x + (y - x) * c;
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
-
function
|
|
210
|
-
for (let
|
|
211
|
-
for (let
|
|
212
|
-
const
|
|
213
|
-
let
|
|
214
|
-
for (let
|
|
215
|
-
const
|
|
216
|
-
if (!(
|
|
209
|
+
function Ot(n, t, e, i) {
|
|
210
|
+
for (let s = 0; s < e; s += 1)
|
|
211
|
+
for (let a = 0; a < t; a += 1) {
|
|
212
|
+
const h = s * t + a, l = n[h];
|
|
213
|
+
let u = 1, f = l;
|
|
214
|
+
for (let c = -2; c <= 2; c += 1) {
|
|
215
|
+
const d = s + c;
|
|
216
|
+
if (!(d < 0 || d >= e))
|
|
217
217
|
for (let m = -2; m <= 2; m += 1) {
|
|
218
|
-
if (m === 0 &&
|
|
219
|
-
const
|
|
220
|
-
if (
|
|
221
|
-
const
|
|
222
|
-
|
|
218
|
+
if (m === 0 && c === 0) continue;
|
|
219
|
+
const g = a + m;
|
|
220
|
+
if (g < 0 || g >= t) continue;
|
|
221
|
+
const U = n[d * t + g], b = m * m + c * c, E = U - l, p = Math.exp(-b / 2.25 - E * E / 0.01);
|
|
222
|
+
u += p, f += U * p;
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
|
-
|
|
225
|
+
i[h] = f / u;
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
-
function
|
|
229
|
-
return Math.min(e, Math.max(t,
|
|
228
|
+
function Z(n, t, e) {
|
|
229
|
+
return Math.min(e, Math.max(t, n));
|
|
230
230
|
}
|
|
231
|
-
const
|
|
231
|
+
const kt = {
|
|
232
232
|
parallaxStrength: 0.05,
|
|
233
233
|
contrastLow: 0.05,
|
|
234
234
|
contrastHigh: 0.95,
|
|
@@ -238,123 +238,123 @@ const q = {
|
|
|
238
238
|
pomSteps: 16,
|
|
239
239
|
overscanPadding: 0.08
|
|
240
240
|
};
|
|
241
|
-
function
|
|
242
|
-
const
|
|
243
|
-
if (
|
|
244
|
-
return
|
|
245
|
-
const
|
|
246
|
-
let
|
|
247
|
-
const
|
|
248
|
-
for (const
|
|
249
|
-
const x =
|
|
250
|
-
for (let
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
if (
|
|
255
|
-
return
|
|
256
|
-
const
|
|
257
|
-
for (let
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
for (let
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
let
|
|
265
|
-
for (let
|
|
266
|
-
|
|
267
|
-
let
|
|
268
|
-
for (let
|
|
269
|
-
const x =
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
const
|
|
241
|
+
function Ht(n, t, e) {
|
|
242
|
+
const i = new Float32Array(256);
|
|
243
|
+
if (n.length === 0 || t <= 0 || e <= 0)
|
|
244
|
+
return pt(i);
|
|
245
|
+
const r = Xt(n.length), o = t * e;
|
|
246
|
+
let s = 0;
|
|
247
|
+
const a = new Uint32Array(256);
|
|
248
|
+
for (const T of r) {
|
|
249
|
+
const x = n[T], y = Math.min(x.length, o);
|
|
250
|
+
for (let S = 0; S < y; S += 1)
|
|
251
|
+
a[x[S]] += 1;
|
|
252
|
+
s += y;
|
|
253
|
+
}
|
|
254
|
+
if (s === 0)
|
|
255
|
+
return pt(i);
|
|
256
|
+
const h = 1 / s;
|
|
257
|
+
for (let T = 0; T < 256; T += 1)
|
|
258
|
+
i[T] = a[T] * h;
|
|
259
|
+
const l = new Float32Array(256);
|
|
260
|
+
l[0] = i[0];
|
|
261
|
+
for (let T = 1; T < 256; T += 1)
|
|
262
|
+
l[T] = l[T - 1] + i[T];
|
|
263
|
+
const u = Y(l, 0.05), f = Y(l, 0.25), c = Y(l, 0.5), d = Y(l, 0.75), m = Y(l, 0.95);
|
|
264
|
+
let g = 0;
|
|
265
|
+
for (let T = 0; T < 256; T += 1)
|
|
266
|
+
g += T / 255 * i[T];
|
|
267
|
+
let U = 0;
|
|
268
|
+
for (let T = 0; T < 256; T += 1) {
|
|
269
|
+
const x = T / 255 - g;
|
|
270
|
+
U += i[T] * x * x;
|
|
271
|
+
}
|
|
272
|
+
const b = Math.sqrt(U), E = m - u, p = d - f, v = Nt(i);
|
|
273
273
|
return {
|
|
274
|
-
mean:
|
|
275
|
-
stdDev:
|
|
276
|
-
p5:
|
|
277
|
-
p25:
|
|
278
|
-
median:
|
|
279
|
-
p75:
|
|
274
|
+
mean: g,
|
|
275
|
+
stdDev: b,
|
|
276
|
+
p5: u,
|
|
277
|
+
p25: f,
|
|
278
|
+
median: c,
|
|
279
|
+
p75: d,
|
|
280
280
|
p95: m,
|
|
281
|
-
effectiveRange:
|
|
282
|
-
iqr:
|
|
283
|
-
bimodality:
|
|
284
|
-
histogram:
|
|
281
|
+
effectiveRange: E,
|
|
282
|
+
iqr: p,
|
|
283
|
+
bimodality: v,
|
|
284
|
+
histogram: i
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
|
-
function
|
|
288
|
-
if (
|
|
289
|
-
return { ...
|
|
290
|
-
const t =
|
|
287
|
+
function Bt(n) {
|
|
288
|
+
if (n.effectiveRange < 0.05 || n.stdDev < 0.02)
|
|
289
|
+
return { ...kt };
|
|
290
|
+
const t = n.effectiveRange - 0.5, e = n.bimodality - 0.4, i = O(
|
|
291
291
|
0.05 - t * 0.03 + e * 0.01,
|
|
292
292
|
0.035,
|
|
293
293
|
0.065
|
|
294
|
-
),
|
|
294
|
+
), r = O(n.p5 - 0.03, 0, 0.25), o = O(n.p95 + 0.03, 0.75, 1), s = O((i - 0.03) / 0.05, 0, 1), a = O(0.6 - s * 0.25, 0.35, 0.6), h = O(0.6 - t * 0.2, 0.5, 0.7), l = O(0.4 + t * 0.2, 0.25, 0.5), u = 16, f = O(i + 0.03, 0.06, 0.1);
|
|
295
295
|
return {
|
|
296
|
-
parallaxStrength:
|
|
297
|
-
contrastLow:
|
|
298
|
-
contrastHigh:
|
|
299
|
-
verticalReduction:
|
|
300
|
-
dofStart:
|
|
301
|
-
dofStrength:
|
|
302
|
-
pomSteps:
|
|
303
|
-
overscanPadding:
|
|
296
|
+
parallaxStrength: i,
|
|
297
|
+
contrastLow: r,
|
|
298
|
+
contrastHigh: o,
|
|
299
|
+
verticalReduction: a,
|
|
300
|
+
dofStart: h,
|
|
301
|
+
dofStrength: l,
|
|
302
|
+
pomSteps: u,
|
|
303
|
+
overscanPadding: f
|
|
304
304
|
};
|
|
305
305
|
}
|
|
306
|
-
function
|
|
307
|
-
if (
|
|
308
|
-
if (
|
|
309
|
-
const t =
|
|
306
|
+
function Xt(n) {
|
|
307
|
+
if (n <= 0) return [];
|
|
308
|
+
if (n === 1) return [0];
|
|
309
|
+
const t = n - 1, e = [
|
|
310
310
|
0,
|
|
311
|
-
Math.floor(
|
|
312
|
-
Math.floor(
|
|
313
|
-
Math.floor(3 *
|
|
311
|
+
Math.floor(n / 4),
|
|
312
|
+
Math.floor(n / 2),
|
|
313
|
+
Math.floor(3 * n / 4),
|
|
314
314
|
t
|
|
315
|
-
],
|
|
316
|
-
for (const
|
|
317
|
-
|
|
318
|
-
return
|
|
315
|
+
], i = /* @__PURE__ */ new Set(), r = [];
|
|
316
|
+
for (const o of e)
|
|
317
|
+
i.has(o) || (i.add(o), r.push(o));
|
|
318
|
+
return r;
|
|
319
319
|
}
|
|
320
|
-
function
|
|
320
|
+
function Y(n, t) {
|
|
321
321
|
for (let e = 0; e < 256; e += 1)
|
|
322
|
-
if (
|
|
322
|
+
if (n[e] >= t)
|
|
323
323
|
return e / 255;
|
|
324
324
|
return 1;
|
|
325
325
|
}
|
|
326
|
-
function
|
|
326
|
+
function Nt(n) {
|
|
327
327
|
const t = new Float32Array(256);
|
|
328
|
-
for (let
|
|
329
|
-
let
|
|
330
|
-
for (let
|
|
331
|
-
|
|
332
|
-
t[
|
|
328
|
+
for (let c = 0; c < 256; c += 1) {
|
|
329
|
+
let d = 0, m = 0;
|
|
330
|
+
for (let g = c - 2; g <= c + 2; g += 1)
|
|
331
|
+
g >= 0 && g < 256 && (d += n[g], m += 1);
|
|
332
|
+
t[c] = d / m;
|
|
333
333
|
}
|
|
334
334
|
let e = 0;
|
|
335
|
-
for (let
|
|
336
|
-
e += t[
|
|
335
|
+
for (let c = 0; c < 256; c += 1)
|
|
336
|
+
e += t[c];
|
|
337
337
|
e /= 256;
|
|
338
|
-
const
|
|
339
|
-
for (let
|
|
340
|
-
t[
|
|
341
|
-
if (t[0] > t[1] && t[0] >=
|
|
342
|
-
const
|
|
343
|
-
let
|
|
344
|
-
for (let
|
|
345
|
-
if (Math.abs(
|
|
346
|
-
|
|
338
|
+
const i = e * 2, r = 25, o = [];
|
|
339
|
+
for (let c = 1; c < 255; c += 1)
|
|
340
|
+
t[c] > t[c - 1] && t[c] > t[c + 1] && t[c] >= i && o.push({ bin: c, height: t[c] });
|
|
341
|
+
if (t[0] > t[1] && t[0] >= i && o.push({ bin: 0, height: t[0] }), t[255] > t[254] && t[255] >= i && o.push({ bin: 255, height: t[255] }), o.sort((c, d) => d.height - c.height), o.length < 2) return 0;
|
|
342
|
+
const s = o[0];
|
|
343
|
+
let a = null;
|
|
344
|
+
for (let c = 1; c < o.length; c += 1)
|
|
345
|
+
if (Math.abs(o[c].bin - s.bin) >= r) {
|
|
346
|
+
a = o[c];
|
|
347
347
|
break;
|
|
348
348
|
}
|
|
349
|
-
if (!
|
|
350
|
-
const
|
|
351
|
-
let
|
|
352
|
-
for (let
|
|
353
|
-
t[
|
|
354
|
-
const
|
|
355
|
-
return
|
|
356
|
-
}
|
|
357
|
-
function
|
|
349
|
+
if (!a) return 0;
|
|
350
|
+
const h = Math.min(s.bin, a.bin), l = Math.max(s.bin, a.bin);
|
|
351
|
+
let u = 1 / 0;
|
|
352
|
+
for (let c = h; c <= l; c += 1)
|
|
353
|
+
t[c] < u && (u = t[c]);
|
|
354
|
+
const f = Math.min(s.height, a.height);
|
|
355
|
+
return f <= 0 ? 0 : O(1 - u / f, 0, 1);
|
|
356
|
+
}
|
|
357
|
+
function pt(n) {
|
|
358
358
|
return {
|
|
359
359
|
mean: 0,
|
|
360
360
|
stdDev: 0,
|
|
@@ -366,13 +366,13 @@ function O(o) {
|
|
|
366
366
|
effectiveRange: 0,
|
|
367
367
|
iqr: 0,
|
|
368
368
|
bimodality: 0,
|
|
369
|
-
histogram:
|
|
369
|
+
histogram: n
|
|
370
370
|
};
|
|
371
371
|
}
|
|
372
|
-
function
|
|
373
|
-
return Math.min(e, Math.max(t,
|
|
372
|
+
function O(n, t, e) {
|
|
373
|
+
return Math.min(e, Math.max(t, n));
|
|
374
374
|
}
|
|
375
|
-
const
|
|
375
|
+
const zt = (
|
|
376
376
|
/* glsl */
|
|
377
377
|
`#version 300 es
|
|
378
378
|
in vec2 aPosition;
|
|
@@ -396,7 +396,7 @@ const Z = (
|
|
|
396
396
|
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
397
397
|
}
|
|
398
398
|
`
|
|
399
|
-
),
|
|
399
|
+
), Wt = (
|
|
400
400
|
/* glsl */
|
|
401
401
|
`#version 300 es
|
|
402
402
|
precision highp float;
|
|
@@ -567,52 +567,52 @@ const Z = (
|
|
|
567
567
|
fragColor = color;
|
|
568
568
|
}
|
|
569
569
|
`
|
|
570
|
-
),
|
|
570
|
+
), q = {
|
|
571
571
|
contrastLow: 0.05,
|
|
572
572
|
contrastHigh: 0.95,
|
|
573
573
|
verticalReduction: 0.5,
|
|
574
574
|
dofStart: 0.6,
|
|
575
575
|
dofStrength: 0.4
|
|
576
576
|
};
|
|
577
|
-
function
|
|
578
|
-
const
|
|
579
|
-
if (!
|
|
580
|
-
if (
|
|
581
|
-
const
|
|
582
|
-
throw
|
|
583
|
-
${
|
|
577
|
+
function gt(n, t, e) {
|
|
578
|
+
const i = n.createShader(t);
|
|
579
|
+
if (!i) throw new Error("Failed to create shader.");
|
|
580
|
+
if (n.shaderSource(i, e), n.compileShader(i), !n.getShaderParameter(i, n.COMPILE_STATUS)) {
|
|
581
|
+
const r = n.getShaderInfoLog(i) ?? "";
|
|
582
|
+
throw n.deleteShader(i), new Error(`Shader compilation failed:
|
|
583
|
+
${r}`);
|
|
584
584
|
}
|
|
585
|
-
return
|
|
585
|
+
return i;
|
|
586
586
|
}
|
|
587
|
-
function
|
|
588
|
-
const
|
|
589
|
-
if (!
|
|
590
|
-
if (
|
|
591
|
-
const
|
|
592
|
-
throw
|
|
593
|
-
${
|
|
587
|
+
function Gt(n, t, e) {
|
|
588
|
+
const i = n.createProgram();
|
|
589
|
+
if (!i) throw new Error("Failed to create program.");
|
|
590
|
+
if (n.attachShader(i, t), n.attachShader(i, e), n.linkProgram(i), !n.getProgramParameter(i, n.LINK_STATUS)) {
|
|
591
|
+
const r = n.getProgramInfoLog(i) ?? "";
|
|
592
|
+
throw n.deleteProgram(i), new Error(`Program linking failed:
|
|
593
|
+
${r}`);
|
|
594
594
|
}
|
|
595
|
-
return
|
|
595
|
+
return n.detachShader(i, t), n.detachShader(i, e), n.deleteShader(t), n.deleteShader(e), i;
|
|
596
596
|
}
|
|
597
|
-
function
|
|
597
|
+
function jt(n, t) {
|
|
598
598
|
return {
|
|
599
|
-
uImage:
|
|
600
|
-
uDepth:
|
|
601
|
-
uOffset:
|
|
602
|
-
uStrength:
|
|
603
|
-
uPomEnabled:
|
|
604
|
-
uPomSteps:
|
|
605
|
-
uContrastLow:
|
|
606
|
-
uContrastHigh:
|
|
607
|
-
uVerticalReduction:
|
|
608
|
-
uDofStart:
|
|
609
|
-
uDofStrength:
|
|
610
|
-
uImageTexelSize:
|
|
611
|
-
uUvOffset:
|
|
612
|
-
uUvScale:
|
|
599
|
+
uImage: n.getUniformLocation(t, "uImage"),
|
|
600
|
+
uDepth: n.getUniformLocation(t, "uDepth"),
|
|
601
|
+
uOffset: n.getUniformLocation(t, "uOffset"),
|
|
602
|
+
uStrength: n.getUniformLocation(t, "uStrength"),
|
|
603
|
+
uPomEnabled: n.getUniformLocation(t, "uPomEnabled"),
|
|
604
|
+
uPomSteps: n.getUniformLocation(t, "uPomSteps"),
|
|
605
|
+
uContrastLow: n.getUniformLocation(t, "uContrastLow"),
|
|
606
|
+
uContrastHigh: n.getUniformLocation(t, "uContrastHigh"),
|
|
607
|
+
uVerticalReduction: n.getUniformLocation(t, "uVerticalReduction"),
|
|
608
|
+
uDofStart: n.getUniformLocation(t, "uDofStart"),
|
|
609
|
+
uDofStrength: n.getUniformLocation(t, "uDofStrength"),
|
|
610
|
+
uImageTexelSize: n.getUniformLocation(t, "uImageTexelSize"),
|
|
611
|
+
uUvOffset: n.getUniformLocation(t, "uUvOffset"),
|
|
612
|
+
uUvScale: n.getUniformLocation(t, "uUvScale")
|
|
613
613
|
};
|
|
614
614
|
}
|
|
615
|
-
class
|
|
615
|
+
class $ {
|
|
616
616
|
/** Debounce delay for resize events to avoid layout thrashing. */
|
|
617
617
|
static RESIZE_DEBOUNCE_MS = 100;
|
|
618
618
|
/** Compile-time upper bound for the POM for-loop in GLSL. */
|
|
@@ -667,20 +667,20 @@ class R {
|
|
|
667
667
|
pomEnabled: e.pomEnabled,
|
|
668
668
|
pomSteps: e.pomSteps,
|
|
669
669
|
overscanPadding: e.overscanPadding,
|
|
670
|
-
contrastLow: e.contrastLow ??
|
|
671
|
-
contrastHigh: e.contrastHigh ??
|
|
672
|
-
verticalReduction: e.verticalReduction ??
|
|
673
|
-
dofStart: e.dofStart ??
|
|
674
|
-
dofStrength: e.dofStrength ??
|
|
670
|
+
contrastLow: e.contrastLow ?? q.contrastLow,
|
|
671
|
+
contrastHigh: e.contrastHigh ?? q.contrastHigh,
|
|
672
|
+
verticalReduction: e.verticalReduction ?? q.verticalReduction,
|
|
673
|
+
dofStart: e.dofStart ?? q.dofStart,
|
|
674
|
+
dofStrength: e.dofStrength ?? q.dofStrength
|
|
675
675
|
}, this.canvas = document.createElement("canvas");
|
|
676
|
-
const
|
|
676
|
+
const i = this.canvas.getContext("webgl2", {
|
|
677
677
|
antialias: !1,
|
|
678
678
|
alpha: !1,
|
|
679
679
|
desynchronized: !0,
|
|
680
680
|
powerPreference: "high-performance"
|
|
681
681
|
});
|
|
682
|
-
if (!
|
|
683
|
-
this.gl =
|
|
682
|
+
if (!i) throw new Error("WebGL 2 is not supported.");
|
|
683
|
+
this.gl = i, "drawingBufferColorSpace" in i && (i.drawingBufferColorSpace = "srgb"), i.clearColor(0, 0, 0, 1), i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL, !0), this.container.appendChild(this.canvas), this.initGPUResources(), this.setupResizeHandling(), this.canvas.addEventListener("webglcontextlost", this.handleContextLost), this.canvas.addEventListener("webglcontextrestored", this.handleContextRestored);
|
|
684
684
|
}
|
|
685
685
|
/**
|
|
686
686
|
* Set up the scene: create video texture, depth texture, and set
|
|
@@ -693,9 +693,9 @@ class R {
|
|
|
693
693
|
* @param depthWidth - Width of the precomputed depth map (e.g. 512).
|
|
694
694
|
* @param depthHeight - Height of the precomputed depth map (e.g. 512).
|
|
695
695
|
*/
|
|
696
|
-
initialize(t, e,
|
|
697
|
-
const
|
|
698
|
-
|
|
696
|
+
initialize(t, e, i) {
|
|
697
|
+
const r = this.gl;
|
|
698
|
+
r && (this.disposeTextures(), this.videoAspect = t.videoWidth / t.videoHeight, this.depthWidth = e, this.depthHeight = i, this.videoTexture = r.createTexture(), r.activeTexture(r.TEXTURE0), r.bindTexture(r.TEXTURE_2D, this.videoTexture), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_MIN_FILTER, r.LINEAR), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_MAG_FILTER, r.LINEAR), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_WRAP_S, r.CLAMP_TO_EDGE), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_WRAP_T, r.CLAMP_TO_EDGE), this.depthTexture = r.createTexture(), r.activeTexture(r.TEXTURE1), r.bindTexture(r.TEXTURE_2D, this.depthTexture), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_MIN_FILTER, r.LINEAR), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_MAG_FILTER, r.LINEAR), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_WRAP_S, r.CLAMP_TO_EDGE), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_WRAP_T, r.CLAMP_TO_EDGE), r.texStorage2D(r.TEXTURE_2D, 1, r.R8, e, i), this.program && this.uniforms && (r.useProgram(this.program), r.uniform1i(this.uniforms.uImage, 0), r.uniform1i(this.uniforms.uDepth, 1), r.uniform1f(this.uniforms.uStrength, this.config.parallaxStrength), r.uniform1i(this.uniforms.uPomEnabled, this.config.pomEnabled ? 1 : 0), r.uniform1i(this.uniforms.uPomSteps, this.config.pomSteps), r.uniform1f(this.uniforms.uContrastLow, this.config.contrastLow), r.uniform1f(this.uniforms.uContrastHigh, this.config.contrastHigh), r.uniform1f(this.uniforms.uVerticalReduction, this.config.verticalReduction), r.uniform1f(this.uniforms.uDofStart, this.config.dofStart), r.uniform1f(this.uniforms.uDofStrength, this.config.dofStrength), r.uniform2f(this.uniforms.uImageTexelSize, 1 / t.videoWidth, 1 / t.videoHeight)), this.recalculateViewportLayout());
|
|
699
699
|
}
|
|
700
700
|
/**
|
|
701
701
|
* Begin the render loop.
|
|
@@ -707,8 +707,8 @@ class R {
|
|
|
707
707
|
* When RVFC is not available, falls back to a single RAF loop that
|
|
708
708
|
* does everything (the pre-RVFC behavior).
|
|
709
709
|
*/
|
|
710
|
-
start(t, e,
|
|
711
|
-
this.stop(), this.playbackVideo = t, this.readDepth = e, this.readInput =
|
|
710
|
+
start(t, e, i, r) {
|
|
711
|
+
this.stop(), this.playbackVideo = t, this.readDepth = e, this.readInput = i, this.onVideoFrame = r ?? null, this.rvfcSupported = $.isRVFCSupported(), this.rvfcSupported && (this.rvfcHandle = t.requestVideoFrameCallback(this.videoFrameLoop)), this.animationFrameHandle = window.requestAnimationFrame(this.renderLoop);
|
|
712
712
|
}
|
|
713
713
|
/** Stop both render loops and release callbacks. */
|
|
714
714
|
stop() {
|
|
@@ -725,13 +725,13 @@ class R {
|
|
|
725
725
|
initGPUResources() {
|
|
726
726
|
const t = this.gl;
|
|
727
727
|
if (!t) return;
|
|
728
|
-
const e =
|
|
728
|
+
const e = Wt.replace(
|
|
729
729
|
"#version 300 es",
|
|
730
730
|
`#version 300 es
|
|
731
|
-
#define MAX_POM_STEPS ${
|
|
732
|
-
),
|
|
733
|
-
this.program =
|
|
734
|
-
const
|
|
731
|
+
#define MAX_POM_STEPS ${$.MAX_POM_STEPS}`
|
|
732
|
+
), i = gt(t, t.VERTEX_SHADER, zt), r = gt(t, t.FRAGMENT_SHADER, e);
|
|
733
|
+
this.program = Gt(t, i, r), this.uniforms = jt(t, this.program);
|
|
734
|
+
const o = new Float32Array([
|
|
735
735
|
-1,
|
|
736
736
|
-1,
|
|
737
737
|
// bottom-left
|
|
@@ -746,10 +746,10 @@ class R {
|
|
|
746
746
|
// top-right
|
|
747
747
|
]);
|
|
748
748
|
this.vao = t.createVertexArray(), t.bindVertexArray(this.vao);
|
|
749
|
-
const
|
|
750
|
-
t.bindBuffer(t.ARRAY_BUFFER,
|
|
751
|
-
const
|
|
752
|
-
t.enableVertexAttribArray(
|
|
749
|
+
const s = t.createBuffer();
|
|
750
|
+
t.bindBuffer(t.ARRAY_BUFFER, s), t.bufferData(t.ARRAY_BUFFER, o, t.STATIC_DRAW);
|
|
751
|
+
const a = t.getAttribLocation(this.program, "aPosition");
|
|
752
|
+
t.enableVertexAttribArray(a), t.vertexAttribPointer(a, 2, t.FLOAT, !1, 0, 0), t.bindVertexArray(null), t.disable(t.DEPTH_TEST);
|
|
753
753
|
}
|
|
754
754
|
// -----------------------------------------------------------------------
|
|
755
755
|
// RVFC feature detection
|
|
@@ -768,11 +768,11 @@ class R {
|
|
|
768
768
|
* when the video frame actually changes (~24-30fps, not 60-120fps).
|
|
769
769
|
*/
|
|
770
770
|
videoFrameLoop = (t, e) => {
|
|
771
|
-
const
|
|
772
|
-
if (!
|
|
773
|
-
this.rvfcHandle =
|
|
774
|
-
const
|
|
775
|
-
this.updateDepthTexture(
|
|
771
|
+
const i = this.playbackVideo;
|
|
772
|
+
if (!i) return;
|
|
773
|
+
this.rvfcHandle = i.requestVideoFrameCallback(this.videoFrameLoop);
|
|
774
|
+
const r = e.mediaTime ?? i.currentTime;
|
|
775
|
+
this.updateDepthTexture(r), this.onVideoFrame && this.onVideoFrame(r, e.presentedFrames ?? 0);
|
|
776
776
|
};
|
|
777
777
|
// -----------------------------------------------------------------------
|
|
778
778
|
// Render loop (RAF) — input + render at display refresh rate
|
|
@@ -793,8 +793,8 @@ class R {
|
|
|
793
793
|
const t = this.gl, e = this.playbackVideo;
|
|
794
794
|
if (!(!t || !this.program || !this.uniforms || !this.vao) && !(!e || e.readyState < HTMLMediaElement.HAVE_CURRENT_DATA)) {
|
|
795
795
|
if (t.useProgram(this.program), t.activeTexture(t.TEXTURE0), t.bindTexture(t.TEXTURE_2D, this.videoTexture), t.texImage2D(t.TEXTURE_2D, 0, t.RGBA, t.RGBA, t.UNSIGNED_BYTE, e), this.rvfcSupported || this.updateDepthTexture(e.currentTime), this.readInput) {
|
|
796
|
-
const
|
|
797
|
-
t.uniform2f(this.uniforms.uOffset, -
|
|
796
|
+
const i = this.readInput();
|
|
797
|
+
t.uniform2f(this.uniforms.uOffset, -i.x, i.y);
|
|
798
798
|
}
|
|
799
799
|
t.bindVertexArray(this.vao), t.drawArrays(t.TRIANGLE_STRIP, 0, 4);
|
|
800
800
|
}
|
|
@@ -803,7 +803,7 @@ class R {
|
|
|
803
803
|
updateDepthTexture(t) {
|
|
804
804
|
const e = this.gl;
|
|
805
805
|
if (!e || !this.readDepth || !this.depthTexture) return;
|
|
806
|
-
const
|
|
806
|
+
const i = this.readDepth(t);
|
|
807
807
|
e.activeTexture(e.TEXTURE1), e.bindTexture(e.TEXTURE_2D, this.depthTexture), e.texSubImage2D(
|
|
808
808
|
e.TEXTURE_2D,
|
|
809
809
|
0,
|
|
@@ -813,7 +813,7 @@ class R {
|
|
|
813
813
|
this.depthHeight,
|
|
814
814
|
e.RED,
|
|
815
815
|
e.UNSIGNED_BYTE,
|
|
816
|
-
|
|
816
|
+
i
|
|
817
817
|
);
|
|
818
818
|
}
|
|
819
819
|
// -----------------------------------------------------------------------
|
|
@@ -832,7 +832,7 @@ class R {
|
|
|
832
832
|
scheduleResizeRecalculate = () => {
|
|
833
833
|
this.resizeTimer !== null && window.clearTimeout(this.resizeTimer), this.resizeTimer = window.setTimeout(() => {
|
|
834
834
|
this.resizeTimer = null, this.recalculateViewportLayout();
|
|
835
|
-
},
|
|
835
|
+
}, $.RESIZE_DEBOUNCE_MS);
|
|
836
836
|
};
|
|
837
837
|
/**
|
|
838
838
|
* Recalculate the WebGL canvas size and UV transform to match the
|
|
@@ -844,13 +844,13 @@ class R {
|
|
|
844
844
|
recalculateViewportLayout() {
|
|
845
845
|
const t = this.gl;
|
|
846
846
|
if (!t) return;
|
|
847
|
-
const { width: e, height:
|
|
848
|
-
(this.canvas.width !==
|
|
849
|
-
const
|
|
850
|
-
let
|
|
851
|
-
|
|
852
|
-
const
|
|
853
|
-
|
|
847
|
+
const { width: e, height: i } = this.getViewportSize(), r = Math.min(window.devicePixelRatio, 2), o = Math.round(e * r), s = Math.round(i * r);
|
|
848
|
+
(this.canvas.width !== o || this.canvas.height !== s) && (this.canvas.width = o, this.canvas.height = s, t.viewport(0, 0, o, s));
|
|
849
|
+
const a = e / i, h = this.config.parallaxStrength + this.config.overscanPadding;
|
|
850
|
+
let l = 1, u = 1;
|
|
851
|
+
a > this.videoAspect ? u = this.videoAspect / a : l = a / this.videoAspect;
|
|
852
|
+
const f = 1 + h * 2;
|
|
853
|
+
l /= f, u /= f, this.uvOffset = [(1 - l) / 2, (1 - u) / 2], this.uvScale = [l, u], this.program && this.uniforms && (t.useProgram(this.program), t.uniform2f(this.uniforms.uUvOffset, this.uvOffset[0], this.uvOffset[1]), t.uniform2f(this.uniforms.uUvScale, this.uvScale[0], this.uvScale[1]));
|
|
854
854
|
}
|
|
855
855
|
/** Read the container's pixel dimensions, with a minimum of 1x1. */
|
|
856
856
|
getViewportSize() {
|
|
@@ -881,7 +881,7 @@ class R {
|
|
|
881
881
|
t && (this.program && (t.deleteProgram(this.program), this.program = null), this.vao && (t.deleteVertexArray(this.vao), this.vao = null), this.uniforms = null);
|
|
882
882
|
}
|
|
883
883
|
}
|
|
884
|
-
const
|
|
884
|
+
const B = {
|
|
885
885
|
parallaxX: 0.4,
|
|
886
886
|
parallaxY: 1,
|
|
887
887
|
parallaxMax: 30,
|
|
@@ -890,9 +890,9 @@ const E = {
|
|
|
890
890
|
loop: !0,
|
|
891
891
|
muted: !0
|
|
892
892
|
};
|
|
893
|
-
class
|
|
894
|
-
constructor(t, e = 0.08,
|
|
895
|
-
this.host = t, this.lerpFactor = e, this.motionLerpFactor =
|
|
893
|
+
let Yt = class yt {
|
|
894
|
+
constructor(t, e = 0.08, i = 0.06) {
|
|
895
|
+
this.host = t, this.lerpFactor = e, this.motionLerpFactor = i, this.host.addEventListener("mousemove", this.handleMouseMove), this.host.addEventListener("mouseleave", this.resetPointerTarget), this.host.addEventListener("touchstart", this.handleTouchStart, { passive: !0 }), this.host.addEventListener("touchmove", this.handleTouchMove, { passive: !0 }), this.host.addEventListener("touchend", this.handleTouchEnd, { passive: !0 }), this.host.addEventListener("touchcancel", this.handleTouchEnd, { passive: !0 });
|
|
896
896
|
}
|
|
897
897
|
pointerTarget = { x: 0, y: 0 };
|
|
898
898
|
motionTarget = { x: 0, y: 0 };
|
|
@@ -909,14 +909,14 @@ class V {
|
|
|
909
909
|
static TOUCH_DRAG_RANGE = 100;
|
|
910
910
|
update() {
|
|
911
911
|
const t = this.touchActive ? this.pointerTarget : this.usingMotionInput ? this.motionTarget : this.pointerTarget, e = this.usingMotionInput && !this.touchActive ? this.motionLerpFactor : this.lerpFactor;
|
|
912
|
-
return this.smoothedOutput.x =
|
|
912
|
+
return this.smoothedOutput.x = it(this.smoothedOutput.x, t.x, e), this.smoothedOutput.y = it(this.smoothedOutput.y, t.y, e), this.smoothedOutput;
|
|
913
913
|
}
|
|
914
914
|
dispose() {
|
|
915
915
|
this.host.removeEventListener("mousemove", this.handleMouseMove), this.host.removeEventListener("mouseleave", this.resetPointerTarget), this.host.removeEventListener("touchstart", this.handleTouchStart), this.host.removeEventListener("touchmove", this.handleTouchMove), this.host.removeEventListener("touchend", this.handleTouchEnd), this.host.removeEventListener("touchcancel", this.handleTouchEnd), this.motionListenerAttached && (window.removeEventListener("deviceorientation", this.handleDeviceOrientation), this.motionListenerAttached = !1);
|
|
916
916
|
}
|
|
917
917
|
handleMouseMove = (t) => {
|
|
918
|
-
const e = this.host.getBoundingClientRect(),
|
|
919
|
-
this.pointerTarget.x =
|
|
918
|
+
const e = this.host.getBoundingClientRect(), i = (t.clientX - e.left) / e.width * 2 - 1, r = (t.clientY - e.top) / e.height * 2 - 1;
|
|
919
|
+
this.pointerTarget.x = z(i, -1, 1), this.pointerTarget.y = z(r, -1, 1);
|
|
920
920
|
};
|
|
921
921
|
resetPointerTarget = () => {
|
|
922
922
|
this.pointerTarget.x = 0, this.pointerTarget.y = 0;
|
|
@@ -928,8 +928,8 @@ class V {
|
|
|
928
928
|
handleTouchMove = (t) => {
|
|
929
929
|
const e = t.touches[0];
|
|
930
930
|
if (!e) return;
|
|
931
|
-
const
|
|
932
|
-
this.pointerTarget.x =
|
|
931
|
+
const i = e.clientX - this.touchAnchorX, r = e.clientY - this.touchAnchorY, o = yt.TOUCH_DRAG_RANGE;
|
|
932
|
+
this.pointerTarget.x = z(i / o, -1, 1), this.pointerTarget.y = z(r / o, -1, 1);
|
|
933
933
|
};
|
|
934
934
|
handleTouchEnd = () => {
|
|
935
935
|
this.touchActive = !1, this.pointerTarget.x = 0, this.pointerTarget.y = 0;
|
|
@@ -946,11 +946,11 @@ class V {
|
|
|
946
946
|
this.motionListenerAttached || (window.addEventListener("deviceorientation", this.handleDeviceOrientation), this.motionListenerAttached = !0), this.usingMotionInput = !0;
|
|
947
947
|
}
|
|
948
948
|
handleDeviceOrientation = (t) => {
|
|
949
|
-
const e =
|
|
950
|
-
this.motionTarget.x =
|
|
949
|
+
const e = z((t.gamma ?? 0) / 45, -1, 1), i = z((t.beta ?? 0) / 45, -1, 1);
|
|
950
|
+
this.motionTarget.x = it(this.motionTarget.x, e, this.motionLerpFactor), this.motionTarget.y = it(this.motionTarget.y, i, this.motionLerpFactor);
|
|
951
951
|
};
|
|
952
|
-
}
|
|
953
|
-
class
|
|
952
|
+
};
|
|
953
|
+
class ht extends HTMLElement {
|
|
954
954
|
static TAG_NAME = "layershift-parallax";
|
|
955
955
|
static get observedAttributes() {
|
|
956
956
|
return [
|
|
@@ -981,36 +981,36 @@ class C extends HTMLElement {
|
|
|
981
981
|
}
|
|
982
982
|
// --- Attribute helpers ---
|
|
983
983
|
getAttrFloat(t, e) {
|
|
984
|
-
const
|
|
985
|
-
if (
|
|
986
|
-
const
|
|
987
|
-
return Number.isFinite(
|
|
984
|
+
const i = this.getAttribute(t);
|
|
985
|
+
if (i === null) return e;
|
|
986
|
+
const r = parseFloat(i);
|
|
987
|
+
return Number.isFinite(r) ? r : e;
|
|
988
988
|
}
|
|
989
989
|
getAttrBool(t, e) {
|
|
990
990
|
if (!this.hasAttribute(t)) return e;
|
|
991
|
-
const
|
|
992
|
-
return !(
|
|
991
|
+
const i = this.getAttribute(t);
|
|
992
|
+
return !(i === "false" || i === "0");
|
|
993
993
|
}
|
|
994
994
|
get parallaxX() {
|
|
995
|
-
return this.getAttrFloat("parallax-x",
|
|
995
|
+
return this.getAttrFloat("parallax-x", B.parallaxX);
|
|
996
996
|
}
|
|
997
997
|
get parallaxY() {
|
|
998
|
-
return this.getAttrFloat("parallax-y",
|
|
998
|
+
return this.getAttrFloat("parallax-y", B.parallaxY);
|
|
999
999
|
}
|
|
1000
1000
|
get parallaxMax() {
|
|
1001
|
-
return this.getAttrFloat("parallax-max",
|
|
1001
|
+
return this.getAttrFloat("parallax-max", B.parallaxMax);
|
|
1002
1002
|
}
|
|
1003
1003
|
get overscan() {
|
|
1004
|
-
return this.getAttrFloat("overscan",
|
|
1004
|
+
return this.getAttrFloat("overscan", B.overscan);
|
|
1005
1005
|
}
|
|
1006
1006
|
get shouldAutoplay() {
|
|
1007
|
-
return this.getAttrBool("autoplay",
|
|
1007
|
+
return this.getAttrBool("autoplay", B.autoplay);
|
|
1008
1008
|
}
|
|
1009
1009
|
get shouldLoop() {
|
|
1010
|
-
return this.getAttrBool("loop",
|
|
1010
|
+
return this.getAttrBool("loop", B.loop);
|
|
1011
1011
|
}
|
|
1012
1012
|
get shouldMute() {
|
|
1013
|
-
return this.getAttrBool("muted",
|
|
1013
|
+
return this.getAttrBool("muted", B.muted);
|
|
1014
1014
|
}
|
|
1015
1015
|
// --- Event dispatching ---
|
|
1016
1016
|
/**
|
|
@@ -1053,7 +1053,7 @@ class C extends HTMLElement {
|
|
|
1053
1053
|
disconnectedCallback() {
|
|
1054
1054
|
this.dispose();
|
|
1055
1055
|
}
|
|
1056
|
-
attributeChangedCallback(t, e,
|
|
1056
|
+
attributeChangedCallback(t, e, i) {
|
|
1057
1057
|
["src", "depth-src", "depth-meta"].includes(t) && (this.initialized ? (this.dispose(), this.setupShadowDOM(), this.init()) : this.isConnected && this.getAttribute("src") && this.getAttribute("depth-src") && this.getAttribute("depth-meta") && this.init());
|
|
1058
1058
|
}
|
|
1059
1059
|
// --- Shadow DOM setup ---
|
|
@@ -1084,114 +1084,114 @@ class C extends HTMLElement {
|
|
|
1084
1084
|
}
|
|
1085
1085
|
// --- Initialization ---
|
|
1086
1086
|
async init() {
|
|
1087
|
-
const t = this.getAttribute("src"), e = this.getAttribute("depth-src"),
|
|
1088
|
-
if (!t || !e || !
|
|
1089
|
-
const
|
|
1090
|
-
console.warn(`<layershift-parallax>: ${
|
|
1087
|
+
const t = this.getAttribute("src"), e = this.getAttribute("depth-src"), i = this.getAttribute("depth-meta");
|
|
1088
|
+
if (!t || !e || !i) {
|
|
1089
|
+
const r = "src, depth-src, and depth-meta attributes are required.";
|
|
1090
|
+
console.warn(`<layershift-parallax>: ${r}`), this.emit("layershift-parallax:error", { message: r });
|
|
1091
1091
|
return;
|
|
1092
1092
|
}
|
|
1093
1093
|
if (this.container) {
|
|
1094
1094
|
this.abortController = new AbortController();
|
|
1095
1095
|
try {
|
|
1096
|
-
const [
|
|
1096
|
+
const [r, o] = await Promise.all([
|
|
1097
1097
|
this.createVideoElement(t),
|
|
1098
|
-
|
|
1098
|
+
At(e, i)
|
|
1099
1099
|
]);
|
|
1100
1100
|
if (this.abortController.signal.aborted) {
|
|
1101
|
-
|
|
1101
|
+
r.remove();
|
|
1102
1102
|
return;
|
|
1103
1103
|
}
|
|
1104
|
-
this.video =
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
),
|
|
1110
|
-
let
|
|
1104
|
+
this.video = r, this.loopCount = 0, this.attachVideoEventListeners(r);
|
|
1105
|
+
const s = Ht(
|
|
1106
|
+
o.frames,
|
|
1107
|
+
o.meta.width,
|
|
1108
|
+
o.meta.height
|
|
1109
|
+
), a = Bt(s), h = this.hasAttribute("parallax-max") ? this.parallaxMax / Math.max(r.videoWidth, 1) : a.parallaxStrength, l = this.hasAttribute("overscan") ? this.overscan : a.overscanPadding;
|
|
1110
|
+
let u;
|
|
1111
1111
|
try {
|
|
1112
|
-
const
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1112
|
+
const d = await st.create(
|
|
1113
|
+
o,
|
|
1114
|
+
o.meta.width,
|
|
1115
|
+
o.meta.height
|
|
1116
1116
|
);
|
|
1117
|
-
this.depthWorker =
|
|
1117
|
+
this.depthWorker = d, u = (m) => d.sample(m);
|
|
1118
1118
|
} catch {
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1119
|
+
const d = new bt(
|
|
1120
|
+
o,
|
|
1121
|
+
o.meta.width,
|
|
1122
|
+
o.meta.height
|
|
1123
1123
|
);
|
|
1124
|
-
|
|
1124
|
+
u = (m) => d.sample(m);
|
|
1125
1125
|
}
|
|
1126
1126
|
if (this.abortController.signal.aborted) {
|
|
1127
|
-
|
|
1127
|
+
r.remove(), this.depthWorker?.dispose(), this.depthWorker = null;
|
|
1128
1128
|
return;
|
|
1129
1129
|
}
|
|
1130
|
-
this.renderer = new
|
|
1131
|
-
parallaxStrength:
|
|
1130
|
+
this.renderer = new $(this.container, {
|
|
1131
|
+
parallaxStrength: h,
|
|
1132
1132
|
pomEnabled: !0,
|
|
1133
|
-
pomSteps:
|
|
1134
|
-
overscanPadding:
|
|
1135
|
-
contrastLow:
|
|
1136
|
-
contrastHigh:
|
|
1137
|
-
verticalReduction:
|
|
1138
|
-
dofStart:
|
|
1139
|
-
dofStrength:
|
|
1140
|
-
}), this.renderer.initialize(
|
|
1141
|
-
const
|
|
1133
|
+
pomSteps: a.pomSteps,
|
|
1134
|
+
overscanPadding: l,
|
|
1135
|
+
contrastLow: a.contrastLow,
|
|
1136
|
+
contrastHigh: a.contrastHigh,
|
|
1137
|
+
verticalReduction: a.verticalReduction,
|
|
1138
|
+
dofStart: a.dofStart,
|
|
1139
|
+
dofStrength: a.dofStrength
|
|
1140
|
+
}), this.renderer.initialize(r, o.meta.width, o.meta.height), this.inputHandler = new Yt(this);
|
|
1141
|
+
const f = this.parallaxX, c = this.parallaxY;
|
|
1142
1142
|
if (this.renderer.start(
|
|
1143
|
-
|
|
1144
|
-
|
|
1143
|
+
r,
|
|
1144
|
+
u,
|
|
1145
1145
|
() => {
|
|
1146
|
-
const
|
|
1146
|
+
const d = this.inputHandler.update();
|
|
1147
1147
|
return {
|
|
1148
|
-
x:
|
|
1149
|
-
y:
|
|
1148
|
+
x: d.x * f,
|
|
1149
|
+
y: d.y * c
|
|
1150
1150
|
};
|
|
1151
1151
|
},
|
|
1152
1152
|
// RVFC callback: dispatch 'frame' event on each new video frame
|
|
1153
|
-
(
|
|
1153
|
+
(d, m) => {
|
|
1154
1154
|
this.emit("layershift-parallax:frame", {
|
|
1155
|
-
currentTime:
|
|
1155
|
+
currentTime: d,
|
|
1156
1156
|
frameNumber: m
|
|
1157
1157
|
});
|
|
1158
1158
|
}
|
|
1159
1159
|
), this.shouldAutoplay) {
|
|
1160
|
-
|
|
1160
|
+
r.currentTime = 0;
|
|
1161
1161
|
try {
|
|
1162
|
-
await
|
|
1162
|
+
await r.play();
|
|
1163
1163
|
} catch {
|
|
1164
1164
|
}
|
|
1165
1165
|
}
|
|
1166
1166
|
this.initialized = !0, this.emit("layershift-parallax:ready", {
|
|
1167
|
-
videoWidth:
|
|
1168
|
-
videoHeight:
|
|
1169
|
-
duration:
|
|
1170
|
-
depthProfile:
|
|
1171
|
-
derivedParams:
|
|
1167
|
+
videoWidth: r.videoWidth,
|
|
1168
|
+
videoHeight: r.videoHeight,
|
|
1169
|
+
duration: r.duration,
|
|
1170
|
+
depthProfile: s,
|
|
1171
|
+
derivedParams: a
|
|
1172
1172
|
});
|
|
1173
|
-
} catch (
|
|
1174
|
-
const
|
|
1175
|
-
console.error("<layershift-parallax>: Failed to initialize.",
|
|
1173
|
+
} catch (r) {
|
|
1174
|
+
const o = r instanceof Error ? r.message : "Failed to initialize.";
|
|
1175
|
+
console.error("<layershift-parallax>: Failed to initialize.", r), this.emit("layershift-parallax:error", { message: o });
|
|
1176
1176
|
}
|
|
1177
1177
|
}
|
|
1178
1178
|
}
|
|
1179
1179
|
// --- Video element ---
|
|
1180
1180
|
async createVideoElement(t) {
|
|
1181
1181
|
const e = document.createElement("video");
|
|
1182
|
-
return e.crossOrigin = "anonymous", e.setAttribute("crossorigin", "anonymous"), e.playsInline = !0, e.setAttribute("playsinline", ""), e.setAttribute("webkit-playsinline", "true"), e.muted = this.shouldMute, e.defaultMuted = this.shouldMute, this.shouldMute && e.setAttribute("muted", ""), e.loop = this.shouldLoop, e.preload = "auto", e.style.display = "none", e.src = t, this.shadow.appendChild(e), await new Promise((
|
|
1182
|
+
return e.crossOrigin = "anonymous", e.setAttribute("crossorigin", "anonymous"), e.playsInline = !0, e.setAttribute("playsinline", ""), e.setAttribute("webkit-playsinline", "true"), e.muted = this.shouldMute, e.defaultMuted = this.shouldMute, this.shouldMute && e.setAttribute("muted", ""), e.loop = this.shouldLoop, e.preload = "auto", e.style.display = "none", e.src = t, this.shadow.appendChild(e), await new Promise((i, r) => {
|
|
1183
1183
|
if (e.readyState >= HTMLMediaElement.HAVE_METADATA) {
|
|
1184
|
-
|
|
1184
|
+
i();
|
|
1185
1185
|
return;
|
|
1186
1186
|
}
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
},
|
|
1190
|
-
|
|
1191
|
-
},
|
|
1192
|
-
e.removeEventListener("loadedmetadata",
|
|
1187
|
+
const o = () => {
|
|
1188
|
+
a(), i();
|
|
1189
|
+
}, s = () => {
|
|
1190
|
+
a(), r(new Error("Failed to load video metadata."));
|
|
1191
|
+
}, a = () => {
|
|
1192
|
+
e.removeEventListener("loadedmetadata", o), e.removeEventListener("error", s);
|
|
1193
1193
|
};
|
|
1194
|
-
e.addEventListener("loadedmetadata",
|
|
1194
|
+
e.addEventListener("loadedmetadata", o), e.addEventListener("error", s), e.load();
|
|
1195
1195
|
}), e;
|
|
1196
1196
|
}
|
|
1197
1197
|
// --- Cleanup ---
|
|
@@ -1199,13 +1199,2252 @@ class C extends HTMLElement {
|
|
|
1199
1199
|
this.abortController?.abort(), this.abortController = null, this.renderer?.dispose(), this.renderer = null, this.inputHandler?.dispose(), this.inputHandler = null, this.depthWorker?.dispose(), this.depthWorker = null, this.video && (this.video.pause(), this.video.removeAttribute("src"), this.video.load(), this.video.remove(), this.video = null), this.initialized = !1, this.loopCount = 0, this.container = null;
|
|
1200
1200
|
}
|
|
1201
1201
|
}
|
|
1202
|
-
function
|
|
1203
|
-
return Math.min(e, Math.max(t,
|
|
1202
|
+
function z(n, t, e) {
|
|
1203
|
+
return Math.min(e, Math.max(t, n));
|
|
1204
1204
|
}
|
|
1205
|
-
function
|
|
1206
|
-
return
|
|
1205
|
+
function it(n, t, e) {
|
|
1206
|
+
return n + (t - n) * e;
|
|
1207
|
+
}
|
|
1208
|
+
const qt = (
|
|
1209
|
+
/* glsl */
|
|
1210
|
+
`#version 300 es
|
|
1211
|
+
in vec2 aPosition;
|
|
1212
|
+
uniform vec2 uMeshScale;
|
|
1213
|
+
void main() {
|
|
1214
|
+
gl_Position = vec4(aPosition * uMeshScale, 0.0, 1.0);
|
|
1215
|
+
}
|
|
1216
|
+
`
|
|
1217
|
+
), Zt = (
|
|
1218
|
+
/* glsl */
|
|
1219
|
+
`#version 300 es
|
|
1220
|
+
precision lowp float;
|
|
1221
|
+
out vec4 fragColor;
|
|
1222
|
+
void main() { fragColor = vec4(0.0); }
|
|
1223
|
+
`
|
|
1224
|
+
), $t = (
|
|
1225
|
+
/* glsl */
|
|
1226
|
+
`#version 300 es
|
|
1227
|
+
in vec2 aPosition;
|
|
1228
|
+
uniform vec2 uMeshScale;
|
|
1229
|
+
void main() {
|
|
1230
|
+
gl_Position = vec4(aPosition * uMeshScale, 0.0, 1.0);
|
|
1231
|
+
}
|
|
1232
|
+
`
|
|
1233
|
+
), Jt = (
|
|
1234
|
+
/* glsl */
|
|
1235
|
+
`#version 300 es
|
|
1236
|
+
precision lowp float;
|
|
1237
|
+
out vec4 fragColor;
|
|
1238
|
+
void main() { fragColor = vec4(1.0); }
|
|
1239
|
+
`
|
|
1240
|
+
), Kt = (
|
|
1241
|
+
/* glsl */
|
|
1242
|
+
`#version 300 es
|
|
1243
|
+
in vec2 aPosition;
|
|
1244
|
+
out vec2 vUv;
|
|
1245
|
+
void main() {
|
|
1246
|
+
vUv = aPosition * 0.5 + 0.5;
|
|
1247
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
1248
|
+
}
|
|
1249
|
+
`
|
|
1250
|
+
), Qt = (
|
|
1251
|
+
/* glsl */
|
|
1252
|
+
`#version 300 es
|
|
1253
|
+
precision highp float;
|
|
1254
|
+
uniform sampler2D uMask;
|
|
1255
|
+
uniform vec2 uTexelSize;
|
|
1256
|
+
in vec2 vUv;
|
|
1257
|
+
out vec2 fragSeed;
|
|
1258
|
+
|
|
1259
|
+
void main() {
|
|
1260
|
+
float center = texture(uMask, vUv).r;
|
|
1261
|
+
float left = texture(uMask, vUv + vec2(-uTexelSize.x, 0.0)).r;
|
|
1262
|
+
float right = texture(uMask, vUv + vec2( uTexelSize.x, 0.0)).r;
|
|
1263
|
+
float up = texture(uMask, vUv + vec2(0.0, uTexelSize.y)).r;
|
|
1264
|
+
float down = texture(uMask, vUv + vec2(0.0, -uTexelSize.y)).r;
|
|
1265
|
+
|
|
1266
|
+
bool isEdge = (step(0.5, center) != step(0.5, left)) ||
|
|
1267
|
+
(step(0.5, center) != step(0.5, right)) ||
|
|
1268
|
+
(step(0.5, center) != step(0.5, up)) ||
|
|
1269
|
+
(step(0.5, center) != step(0.5, down));
|
|
1270
|
+
|
|
1271
|
+
if (isEdge) {
|
|
1272
|
+
fragSeed = vUv;
|
|
1273
|
+
} else {
|
|
1274
|
+
fragSeed = vec2(-1.0);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
`
|
|
1278
|
+
), te = (
|
|
1279
|
+
/* glsl */
|
|
1280
|
+
`#version 300 es
|
|
1281
|
+
in vec2 aPosition;
|
|
1282
|
+
out vec2 vUv;
|
|
1283
|
+
void main() {
|
|
1284
|
+
vUv = aPosition * 0.5 + 0.5;
|
|
1285
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
1286
|
+
}
|
|
1287
|
+
`
|
|
1288
|
+
), ee = (
|
|
1289
|
+
/* glsl */
|
|
1290
|
+
`#version 300 es
|
|
1291
|
+
precision highp float;
|
|
1292
|
+
uniform sampler2D uSeedTex;
|
|
1293
|
+
uniform float uStepSize;
|
|
1294
|
+
in vec2 vUv;
|
|
1295
|
+
out vec2 fragSeed;
|
|
1296
|
+
|
|
1297
|
+
void main() {
|
|
1298
|
+
vec2 bestSeed = texture(uSeedTex, vUv).rg;
|
|
1299
|
+
float bestDist = (bestSeed.x < 0.0) ? 1.0e10 : distance(vUv, bestSeed);
|
|
1300
|
+
|
|
1301
|
+
for (int dy = -1; dy <= 1; dy++) {
|
|
1302
|
+
for (int dx = -1; dx <= 1; dx++) {
|
|
1303
|
+
if (dx == 0 && dy == 0) continue;
|
|
1304
|
+
vec2 offset = vec2(float(dx), float(dy)) * uStepSize;
|
|
1305
|
+
vec2 sampleUv = vUv + offset;
|
|
1306
|
+
if (sampleUv.x < 0.0 || sampleUv.x > 1.0 || sampleUv.y < 0.0 || sampleUv.y > 1.0) continue;
|
|
1307
|
+
vec2 neighborSeed = texture(uSeedTex, sampleUv).rg;
|
|
1308
|
+
if (neighborSeed.x < 0.0) continue;
|
|
1309
|
+
float d = distance(vUv, neighborSeed);
|
|
1310
|
+
if (d < bestDist) {
|
|
1311
|
+
bestDist = d;
|
|
1312
|
+
bestSeed = neighborSeed;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
fragSeed = bestSeed;
|
|
1318
|
+
}
|
|
1319
|
+
`
|
|
1320
|
+
), ie = (
|
|
1321
|
+
/* glsl */
|
|
1322
|
+
`#version 300 es
|
|
1323
|
+
in vec2 aPosition;
|
|
1324
|
+
out vec2 vUv;
|
|
1325
|
+
void main() {
|
|
1326
|
+
vUv = aPosition * 0.5 + 0.5;
|
|
1327
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
1328
|
+
}
|
|
1329
|
+
`
|
|
1330
|
+
), re = (
|
|
1331
|
+
/* glsl */
|
|
1332
|
+
`#version 300 es
|
|
1333
|
+
precision highp float;
|
|
1334
|
+
uniform sampler2D uSeedTex;
|
|
1335
|
+
uniform sampler2D uMask;
|
|
1336
|
+
uniform float uBevelWidth;
|
|
1337
|
+
in vec2 vUv;
|
|
1338
|
+
out vec4 fragDist;
|
|
1339
|
+
|
|
1340
|
+
void main() {
|
|
1341
|
+
float mask = texture(uMask, vUv).r;
|
|
1342
|
+
if (mask < 0.5) {
|
|
1343
|
+
fragDist = vec4(0.0);
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
vec2 seed = texture(uSeedTex, vUv).rg;
|
|
1348
|
+
if (seed.x < 0.0) {
|
|
1349
|
+
fragDist = vec4(1.0);
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
float d = distance(vUv, seed);
|
|
1354
|
+
float normalized = clamp(d / max(uBevelWidth, 0.001), 0.0, 1.0);
|
|
1355
|
+
fragDist = vec4(normalized, 0.0, 0.0, 1.0);
|
|
1356
|
+
}
|
|
1357
|
+
`
|
|
1358
|
+
), oe = (
|
|
1359
|
+
/* glsl */
|
|
1360
|
+
`#version 300 es
|
|
1361
|
+
in vec2 aPosition;
|
|
1362
|
+
uniform vec2 uUvOffset;
|
|
1363
|
+
uniform vec2 uUvScale;
|
|
1364
|
+
out vec2 vUv;
|
|
1365
|
+
out vec2 vScreenUv;
|
|
1366
|
+
void main() {
|
|
1367
|
+
vec2 baseUv = aPosition * 0.5 + 0.5;
|
|
1368
|
+
vUv = baseUv * uUvScale + uUvOffset;
|
|
1369
|
+
vScreenUv = baseUv;
|
|
1370
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
1371
|
+
}
|
|
1372
|
+
`
|
|
1373
|
+
), ne = (
|
|
1374
|
+
/* glsl */
|
|
1375
|
+
`#version 300 es
|
|
1376
|
+
precision highp float;
|
|
1377
|
+
|
|
1378
|
+
#define MAX_POM_STEPS 32
|
|
1379
|
+
|
|
1380
|
+
uniform sampler2D uImage;
|
|
1381
|
+
uniform sampler2D uDepth;
|
|
1382
|
+
uniform vec2 uOffset;
|
|
1383
|
+
uniform float uStrength;
|
|
1384
|
+
uniform int uPomSteps;
|
|
1385
|
+
|
|
1386
|
+
// Lens transform: remap depth curve for exaggerated/compressed depth feel
|
|
1387
|
+
uniform float uDepthPower; // >1 = telephoto, <1 = wide-angle
|
|
1388
|
+
uniform float uDepthScale; // multiplier on depth range
|
|
1389
|
+
uniform float uDepthBias; // shift depth origin
|
|
1390
|
+
|
|
1391
|
+
// Depth-adaptive contrast
|
|
1392
|
+
uniform float uContrastLow;
|
|
1393
|
+
uniform float uContrastHigh;
|
|
1394
|
+
uniform float uVerticalReduction;
|
|
1395
|
+
|
|
1396
|
+
// DOF
|
|
1397
|
+
uniform float uDofStart;
|
|
1398
|
+
uniform float uDofStrength;
|
|
1399
|
+
uniform vec2 uImageTexelSize;
|
|
1400
|
+
|
|
1401
|
+
// Interior mood
|
|
1402
|
+
uniform float uFogDensity; // volumetric fog bias (0 = none, 0.3 = subtle)
|
|
1403
|
+
uniform vec3 uFogColor; // fog tint color
|
|
1404
|
+
uniform float uColorShift; // warm/cool grading shift
|
|
1405
|
+
uniform float uBrightnessBias; // overall brightness adjustment
|
|
1406
|
+
|
|
1407
|
+
in vec2 vUv;
|
|
1408
|
+
in vec2 vScreenUv;
|
|
1409
|
+
|
|
1410
|
+
layout(location = 0) out vec4 fragColor;
|
|
1411
|
+
layout(location = 1) out vec4 fragDepth;
|
|
1412
|
+
|
|
1413
|
+
// Apply lens transform to raw depth
|
|
1414
|
+
float lensDepth(float raw) {
|
|
1415
|
+
float d = smoothstep(uContrastLow, uContrastHigh, raw);
|
|
1416
|
+
d = pow(d, uDepthPower) * uDepthScale + uDepthBias;
|
|
1417
|
+
return clamp(d, 0.0, 1.0);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
float edgeFade(vec2 uv) {
|
|
1421
|
+
float margin = uStrength * 1.5;
|
|
1422
|
+
float fadeX = smoothstep(0.0, margin, uv.x) * smoothstep(0.0, margin, 1.0 - uv.x);
|
|
1423
|
+
float fadeY = smoothstep(0.0, margin, uv.y) * smoothstep(0.0, margin, 1.0 - uv.y);
|
|
1424
|
+
return fadeX * fadeY;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// POM ray-march with lens-transformed depth
|
|
1428
|
+
vec2 pomDisplace(vec2 uv, out float hitDepth) {
|
|
1429
|
+
float layerD = 1.0 / float(uPomSteps);
|
|
1430
|
+
vec2 scaledOffset = uOffset;
|
|
1431
|
+
scaledOffset.y *= uVerticalReduction;
|
|
1432
|
+
vec2 deltaUV = scaledOffset * uStrength / float(uPomSteps);
|
|
1433
|
+
float currentLayerDepth = 0.0;
|
|
1434
|
+
vec2 currentUV = uv;
|
|
1435
|
+
float fade = edgeFade(uv);
|
|
1436
|
+
|
|
1437
|
+
for (int i = 0; i < MAX_POM_STEPS; i++) {
|
|
1438
|
+
if (i >= uPomSteps) break;
|
|
1439
|
+
float raw = texture(uDepth, currentUV).r;
|
|
1440
|
+
float depthAtUV = 1.0 - lensDepth(raw);
|
|
1441
|
+
if (currentLayerDepth > depthAtUV) {
|
|
1442
|
+
vec2 prevUV = currentUV - deltaUV;
|
|
1443
|
+
float prevLayerD = currentLayerDepth - layerD;
|
|
1444
|
+
float prevRaw = texture(uDepth, prevUV).r;
|
|
1445
|
+
float prevDepthAtUV = 1.0 - lensDepth(prevRaw);
|
|
1446
|
+
float afterD = depthAtUV - currentLayerDepth;
|
|
1447
|
+
float beforeD = prevDepthAtUV - prevLayerD;
|
|
1448
|
+
float t = afterD / (afterD - beforeD);
|
|
1449
|
+
vec2 hitUV = mix(currentUV, prevUV, t);
|
|
1450
|
+
hitDepth = mix(depthAtUV, prevDepthAtUV, t);
|
|
1451
|
+
return mix(uv, hitUV, fade);
|
|
1452
|
+
}
|
|
1453
|
+
currentUV += deltaUV;
|
|
1454
|
+
currentLayerDepth += layerD;
|
|
1455
|
+
}
|
|
1456
|
+
hitDepth = 1.0 - lensDepth(texture(uDepth, currentUV).r);
|
|
1457
|
+
return mix(uv, currentUV, fade);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
void main() {
|
|
1461
|
+
float hitDepth;
|
|
1462
|
+
vec2 displaced = pomDisplace(vUv, hitDepth);
|
|
1463
|
+
displaced = clamp(displaced, vec2(0.0), vec2(1.0));
|
|
1464
|
+
|
|
1465
|
+
vec4 color = texture(uImage, displaced);
|
|
1466
|
+
|
|
1467
|
+
// DOF: blur far objects
|
|
1468
|
+
float rawDepthAtHit = texture(uDepth, displaced).r;
|
|
1469
|
+
float lensD = lensDepth(rawDepthAtHit);
|
|
1470
|
+
float dof = smoothstep(uDofStart, 1.0, lensD) * uDofStrength;
|
|
1471
|
+
if (dof > 0.01) {
|
|
1472
|
+
vec2 ts = uImageTexelSize;
|
|
1473
|
+
vec4 blurred = (
|
|
1474
|
+
texture(uImage, displaced + vec2( ts.x, 0.0)) +
|
|
1475
|
+
texture(uImage, displaced + vec2(-ts.x, 0.0)) +
|
|
1476
|
+
texture(uImage, displaced + vec2( 0.0, ts.y)) +
|
|
1477
|
+
texture(uImage, displaced + vec2( 0.0, -ts.y)) +
|
|
1478
|
+
texture(uImage, displaced + vec2( ts.x, ts.y)) +
|
|
1479
|
+
texture(uImage, displaced + vec2(-ts.x, -ts.y)) +
|
|
1480
|
+
texture(uImage, displaced + vec2( ts.x, -ts.y)) +
|
|
1481
|
+
texture(uImage, displaced + vec2(-ts.x, ts.y))
|
|
1482
|
+
) * 0.125;
|
|
1483
|
+
color = mix(color, blurred, dof);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// Volumetric fog bias: far objects fade into fog color
|
|
1487
|
+
float fogFactor = smoothstep(0.3, 1.0, lensD) * uFogDensity;
|
|
1488
|
+
color.rgb = mix(color.rgb, uFogColor, fogFactor);
|
|
1489
|
+
|
|
1490
|
+
// Color grading shift: warm near, cool far (or vice versa)
|
|
1491
|
+
float gradeAmount = (lensD - 0.5) * uColorShift;
|
|
1492
|
+
color.r += gradeAmount * 0.08;
|
|
1493
|
+
color.b -= gradeAmount * 0.08;
|
|
1494
|
+
|
|
1495
|
+
// Brightness bias
|
|
1496
|
+
color.rgb *= (1.0 + uBrightnessBias);
|
|
1497
|
+
|
|
1498
|
+
// Subtle vignette inside portal
|
|
1499
|
+
float dist = length(vScreenUv - 0.5) * 1.4;
|
|
1500
|
+
color.rgb *= 1.0 - pow(dist, 3.0) * 0.3;
|
|
1501
|
+
|
|
1502
|
+
fragColor = color;
|
|
1503
|
+
// Write lens-transformed depth to second attachment for boundary effects
|
|
1504
|
+
fragDepth = vec4(lensD, 0.0, 0.0, 1.0);
|
|
1505
|
+
}
|
|
1506
|
+
`
|
|
1507
|
+
), se = (
|
|
1508
|
+
/* glsl */
|
|
1509
|
+
`#version 300 es
|
|
1510
|
+
in vec2 aPosition;
|
|
1511
|
+
out vec2 vUv;
|
|
1512
|
+
void main() {
|
|
1513
|
+
vUv = aPosition * 0.5 + 0.5;
|
|
1514
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
1515
|
+
}
|
|
1516
|
+
`
|
|
1517
|
+
), ae = (
|
|
1518
|
+
/* glsl */
|
|
1519
|
+
`#version 300 es
|
|
1520
|
+
precision highp float;
|
|
1521
|
+
uniform sampler2D uInteriorColor;
|
|
1522
|
+
uniform sampler2D uDistField;
|
|
1523
|
+
uniform float uEdgeOcclusionWidth; // how far edge darkening extends
|
|
1524
|
+
uniform float uEdgeOcclusionStrength; // how strong (0=none, 1=full black)
|
|
1525
|
+
|
|
1526
|
+
in vec2 vUv;
|
|
1527
|
+
out vec4 fragColor;
|
|
1528
|
+
|
|
1529
|
+
// sRGB ↔ linear conversions for correct lighting math
|
|
1530
|
+
vec3 toLinear(vec3 s) {
|
|
1531
|
+
return mix(s / 12.92, pow((s + 0.055) / 1.055, vec3(2.4)), step(0.04045, s));
|
|
1532
|
+
}
|
|
1533
|
+
vec3 toSRGB(vec3 l) {
|
|
1534
|
+
return mix(l * 12.92, 1.055 * pow(l, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, l));
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
void main() {
|
|
1538
|
+
vec4 color = texture(uInteriorColor, vUv);
|
|
1539
|
+
float dist = texture(uDistField, vUv).r; // 0=edge, 1=deep interior
|
|
1540
|
+
|
|
1541
|
+
// Emissive passthrough: preserve original video luminance.
|
|
1542
|
+
// Only apply a subtle edge occlusion ramp to sell chamfer→interior depth.
|
|
1543
|
+
vec3 linear = toLinear(color.rgb);
|
|
1544
|
+
float occ = smoothstep(0.0, uEdgeOcclusionWidth, dist);
|
|
1545
|
+
linear *= mix(1.0 - uEdgeOcclusionStrength, 1.0, occ);
|
|
1546
|
+
|
|
1547
|
+
fragColor = vec4(toSRGB(linear), color.a);
|
|
1548
|
+
}
|
|
1549
|
+
`
|
|
1550
|
+
), he = (
|
|
1551
|
+
/* glsl */
|
|
1552
|
+
`#version 300 es
|
|
1553
|
+
in vec2 aPosition;
|
|
1554
|
+
in vec2 aNormal;
|
|
1555
|
+
uniform float uRimWidth;
|
|
1556
|
+
uniform vec2 uMeshScale;
|
|
1557
|
+
out vec2 vNormal;
|
|
1558
|
+
out vec2 vEdgeUv; // screen-space UV for sampling FBO textures
|
|
1559
|
+
out float vEdgeDist; // 0 at edge, 1 at outer extent
|
|
1560
|
+
|
|
1561
|
+
void main() {
|
|
1562
|
+
vec2 scaledPos = aPosition * uMeshScale;
|
|
1563
|
+
vec2 scaledNormal = normalize(aNormal * uMeshScale);
|
|
1564
|
+
vec2 pos = scaledPos + scaledNormal * uRimWidth;
|
|
1565
|
+
|
|
1566
|
+
// Pass screen-space UV of this fragment for FBO sampling
|
|
1567
|
+
vEdgeUv = pos * 0.5 + 0.5;
|
|
1568
|
+
vNormal = scaledNormal;
|
|
1569
|
+
|
|
1570
|
+
// Distance from the actual edge (0) to the outer rim extent (1)
|
|
1571
|
+
vEdgeDist = length(pos - scaledPos) / max(uRimWidth, 0.001);
|
|
1572
|
+
|
|
1573
|
+
gl_Position = vec4(pos, 0.0, 1.0);
|
|
1574
|
+
}
|
|
1575
|
+
`
|
|
1576
|
+
), le = (
|
|
1577
|
+
/* glsl */
|
|
1578
|
+
`#version 300 es
|
|
1579
|
+
precision highp float;
|
|
1580
|
+
|
|
1581
|
+
uniform sampler2D uInteriorColor;
|
|
1582
|
+
uniform sampler2D uInteriorDepth;
|
|
1583
|
+
uniform sampler2D uDistField;
|
|
1584
|
+
uniform float uRimIntensity;
|
|
1585
|
+
uniform vec3 uRimColor;
|
|
1586
|
+
uniform float uRefractionStrength;
|
|
1587
|
+
uniform float uChromaticStrength;
|
|
1588
|
+
uniform float uOcclusionIntensity;
|
|
1589
|
+
uniform vec2 uTexelSize; // 1.0 / viewport resolution
|
|
1590
|
+
|
|
1591
|
+
// Volumetric edge wall
|
|
1592
|
+
uniform float uEdgeThickness;
|
|
1593
|
+
uniform float uEdgeSpecular;
|
|
1594
|
+
uniform vec3 uEdgeColor;
|
|
1595
|
+
uniform vec2 uLightDir;
|
|
1596
|
+
uniform float uBevelIntensity;
|
|
1597
|
+
|
|
1598
|
+
in vec2 vNormal;
|
|
1599
|
+
in vec2 vEdgeUv;
|
|
1600
|
+
in float vEdgeDist;
|
|
1601
|
+
out vec4 fragColor;
|
|
1602
|
+
|
|
1603
|
+
void main() {
|
|
1604
|
+
// Clamp UV to valid range for texture sampling
|
|
1605
|
+
vec2 sampleUv = clamp(vEdgeUv, vec2(0.001), vec2(0.999));
|
|
1606
|
+
|
|
1607
|
+
// Sample interior depth at this boundary location
|
|
1608
|
+
float interiorDepth = texture(uInteriorDepth, sampleUv).r;
|
|
1609
|
+
|
|
1610
|
+
// === DEPTH-REACTIVE RIM (structural seam) ===
|
|
1611
|
+
float depthReactivity = 1.0 - interiorDepth; // 1=near, 0=far
|
|
1612
|
+
float rimProfile = 1.0 - smoothstep(0.0, 1.0, vEdgeDist);
|
|
1613
|
+
rimProfile = pow(rimProfile, 1.5); // sharper falloff = more structural
|
|
1614
|
+
|
|
1615
|
+
float depthPressure = mix(0.2, 1.0, depthReactivity * depthReactivity);
|
|
1616
|
+
float rim = rimProfile * depthPressure * uRimIntensity;
|
|
1617
|
+
|
|
1618
|
+
vec3 rimCol = uRimColor;
|
|
1619
|
+
rimCol.r += depthReactivity * 0.15;
|
|
1620
|
+
rimCol.g += depthReactivity * 0.05;
|
|
1621
|
+
|
|
1622
|
+
// === REFRACTION DISTORTION ===
|
|
1623
|
+
vec2 ts = uTexelSize * 3.0;
|
|
1624
|
+
float dLeft = texture(uInteriorDepth, sampleUv + vec2(-ts.x, 0.0)).r;
|
|
1625
|
+
float dRight = texture(uInteriorDepth, sampleUv + vec2( ts.x, 0.0)).r;
|
|
1626
|
+
float dUp = texture(uInteriorDepth, sampleUv + vec2(0.0, ts.y)).r;
|
|
1627
|
+
float dDown = texture(uInteriorDepth, sampleUv + vec2(0.0, -ts.y)).r;
|
|
1628
|
+
vec2 depthGradient = vec2(dRight - dLeft, dUp - dDown);
|
|
1629
|
+
vec2 refractUv = sampleUv + depthGradient * uRefractionStrength * rimProfile;
|
|
1630
|
+
refractUv = clamp(refractUv, vec2(0.001), vec2(0.999));
|
|
1631
|
+
|
|
1632
|
+
vec4 refractedColor = texture(uInteriorColor, refractUv);
|
|
1633
|
+
|
|
1634
|
+
// === CHROMATIC FRINGE ===
|
|
1635
|
+
float chromaticAmount = uChromaticStrength * depthReactivity * rimProfile;
|
|
1636
|
+
vec2 chromaticDir = vNormal * chromaticAmount;
|
|
1637
|
+
float cr = texture(uInteriorColor, refractUv + chromaticDir).r;
|
|
1638
|
+
float cg = refractedColor.g;
|
|
1639
|
+
float cb = texture(uInteriorColor, refractUv - chromaticDir).b;
|
|
1640
|
+
vec3 chromaticColor = vec3(cr, cg, cb);
|
|
1641
|
+
|
|
1642
|
+
// === OCCLUSION CONTACT SHADOW ===
|
|
1643
|
+
float occlusionAmount = smoothstep(0.4, 0.0, interiorDepth) * uOcclusionIntensity * rimProfile;
|
|
1644
|
+
|
|
1645
|
+
// === VOLUMETRIC EDGE WALL ===
|
|
1646
|
+
// Sample distance field to get the inner-side distance at this boundary location
|
|
1647
|
+
float edgeDist = texture(uDistField, sampleUv).r;
|
|
1648
|
+
float wallZone = smoothstep(uEdgeThickness, 0.0, edgeDist) * rimProfile;
|
|
1649
|
+
|
|
1650
|
+
// Wall lighting from distance field gradient
|
|
1651
|
+
vec2 dtx = vec2(1.0) / vec2(textureSize(uDistField, 0));
|
|
1652
|
+
float wdL = texture(uDistField, sampleUv + vec2(-dtx.x, 0.0)).r;
|
|
1653
|
+
float wdR = texture(uDistField, sampleUv + vec2( dtx.x, 0.0)).r;
|
|
1654
|
+
float wdU = texture(uDistField, sampleUv + vec2(0.0, dtx.y)).r;
|
|
1655
|
+
float wdD = texture(uDistField, sampleUv + vec2(0.0, -dtx.y)).r;
|
|
1656
|
+
vec2 wallNormal = vec2(wdR - wdL, wdU - wdD);
|
|
1657
|
+
float wnLen = length(wallNormal);
|
|
1658
|
+
if (wnLen > 0.001) wallNormal /= wnLen;
|
|
1659
|
+
|
|
1660
|
+
float wallSpec = pow(max(dot(wallNormal, uLightDir), 0.0), 16.0) * uEdgeSpecular;
|
|
1661
|
+
vec3 wallColor = mix(refractedColor.rgb * 0.4, uEdgeColor, 0.3);
|
|
1662
|
+
wallColor += vec3(wallSpec);
|
|
1663
|
+
|
|
1664
|
+
// === COMPOSITE ===
|
|
1665
|
+
vec3 color = mix(refractedColor.rgb, chromaticColor, min(chromaticAmount * 10.0, 1.0));
|
|
1666
|
+
color *= (1.0 - occlusionAmount * 0.4);
|
|
1667
|
+
|
|
1668
|
+
// Blend in volumetric wall
|
|
1669
|
+
color = mix(color, wallColor, wallZone * uBevelIntensity);
|
|
1670
|
+
|
|
1671
|
+
// Add rim energy on top
|
|
1672
|
+
color += rimCol * rim;
|
|
1673
|
+
|
|
1674
|
+
// Alpha: rim edge fades out
|
|
1675
|
+
float alpha = rimProfile * max(rim, occlusionAmount + chromaticAmount * 5.0 + wallZone * 0.5);
|
|
1676
|
+
alpha = clamp(alpha, 0.0, 1.0);
|
|
1677
|
+
|
|
1678
|
+
fragColor = vec4(color * alpha, alpha);
|
|
1679
|
+
}
|
|
1680
|
+
`
|
|
1681
|
+
), ce = (
|
|
1682
|
+
/* glsl */
|
|
1683
|
+
`#version 300 es
|
|
1684
|
+
in vec2 aPosition;
|
|
1685
|
+
in vec3 aNormal3;
|
|
1686
|
+
in float aLerpT; // 0 = inner (at silhouette), 1 = outer edge
|
|
1687
|
+
uniform vec2 uMeshScale;
|
|
1688
|
+
out vec3 vNormal;
|
|
1689
|
+
out vec2 vScreenUv;
|
|
1690
|
+
out float vLerpT;
|
|
1691
|
+
|
|
1692
|
+
void main() {
|
|
1693
|
+
vec2 sp = aPosition * uMeshScale;
|
|
1694
|
+
vNormal = aNormal3;
|
|
1695
|
+
vScreenUv = sp * 0.5 + 0.5;
|
|
1696
|
+
vLerpT = aLerpT;
|
|
1697
|
+
gl_Position = vec4(sp, 0.0, 1.0);
|
|
1698
|
+
}
|
|
1699
|
+
`
|
|
1700
|
+
), ue = (
|
|
1701
|
+
/* glsl */
|
|
1702
|
+
`#version 300 es
|
|
1703
|
+
precision highp float;
|
|
1704
|
+
uniform vec3 uLightDir3;
|
|
1705
|
+
uniform vec3 uChamferColor;
|
|
1706
|
+
uniform float uChamferAmbient;
|
|
1707
|
+
uniform float uChamferSpecular;
|
|
1708
|
+
uniform float uChamferShininess;
|
|
1709
|
+
uniform sampler2D uInteriorColor;
|
|
1710
|
+
uniform vec2 uTexelSize; // 1 / viewport resolution
|
|
1711
|
+
|
|
1712
|
+
in vec3 vNormal;
|
|
1713
|
+
in vec2 vScreenUv;
|
|
1714
|
+
in float vLerpT;
|
|
1715
|
+
out vec4 fragColor;
|
|
1716
|
+
|
|
1717
|
+
vec3 toLinear(vec3 s) {
|
|
1718
|
+
return mix(s / 12.92, pow((s + 0.055) / 1.055, vec3(2.4)), step(0.04045, s));
|
|
1719
|
+
}
|
|
1720
|
+
vec3 toSRGB(vec3 l) {
|
|
1721
|
+
return mix(l * 12.92, 1.055 * pow(l, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, l));
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
// Approximate gaussian blur via 13-tap poisson disc, radius scaled by vLerpT.
|
|
1725
|
+
vec3 blurSample(vec2 center, float radius) {
|
|
1726
|
+
// Poisson disc offsets (normalized to unit circle)
|
|
1727
|
+
const vec2 offsets[12] = vec2[12](
|
|
1728
|
+
vec2(-0.326, -0.406), vec2(-0.840, -0.074), vec2(-0.696, 0.457),
|
|
1729
|
+
vec2(-0.203, 0.621), vec2( 0.962, -0.195), vec2( 0.473, -0.480),
|
|
1730
|
+
vec2( 0.519, 0.767), vec2( 0.185, -0.893), vec2( 0.507, 0.064),
|
|
1731
|
+
vec2(-0.321, -0.860), vec2(-0.791, 0.557), vec2( 0.330, 0.418)
|
|
1732
|
+
);
|
|
1733
|
+
vec3 sum = texture(uInteriorColor, center).rgb;
|
|
1734
|
+
for (int i = 0; i < 12; i++) {
|
|
1735
|
+
vec2 uv = center + offsets[i] * radius;
|
|
1736
|
+
uv = clamp(uv, vec2(0.001), vec2(0.999));
|
|
1737
|
+
sum += texture(uInteriorColor, uv).rgb;
|
|
1738
|
+
}
|
|
1739
|
+
return sum / 13.0;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
void main() {
|
|
1743
|
+
vec3 N = normalize(vNormal);
|
|
1744
|
+
vec3 L = normalize(uLightDir3);
|
|
1745
|
+
vec3 V = vec3(0.0, 0.0, -1.0); // orthographic view direction
|
|
1746
|
+
|
|
1747
|
+
// Blinn-Phong lighting in linear space
|
|
1748
|
+
float diff = max(dot(N, L), 0.0);
|
|
1749
|
+
vec3 H = normalize(L + V);
|
|
1750
|
+
float spec = pow(max(dot(N, H), 0.0), uChamferShininess) * uChamferSpecular;
|
|
1751
|
+
|
|
1752
|
+
// Sample interior video with progressive blur (sharper at inner edge)
|
|
1753
|
+
vec2 uv = clamp(vScreenUv, vec2(0.001), vec2(0.999));
|
|
1754
|
+
float blurRadius = vLerpT * 12.0 * length(uTexelSize);
|
|
1755
|
+
vec3 videoSample = blurRadius > 0.0001
|
|
1756
|
+
? blurSample(uv, blurRadius)
|
|
1757
|
+
: texture(uInteriorColor, uv).rgb;
|
|
1758
|
+
|
|
1759
|
+
// Base color: video tinted through chamfer color (like frosted glass)
|
|
1760
|
+
vec3 video = toLinear(videoSample);
|
|
1761
|
+
vec3 tint = toLinear(uChamferColor);
|
|
1762
|
+
// Blend: mostly video near inner edge, more tinted at outer edge
|
|
1763
|
+
vec3 base = mix(video, video * tint * 3.0, vLerpT * 0.5);
|
|
1764
|
+
|
|
1765
|
+
// Apply Blinn-Phong
|
|
1766
|
+
vec3 lit = base * (uChamferAmbient + (1.0 - uChamferAmbient) * diff) + vec3(spec);
|
|
1767
|
+
fragColor = vec4(toSRGB(lit), 1.0);
|
|
1768
|
+
}
|
|
1769
|
+
`
|
|
1770
|
+
);
|
|
1771
|
+
function L(n, t, e) {
|
|
1772
|
+
const i = n.createShader(t);
|
|
1773
|
+
if (!i) throw new Error("Failed to create shader.");
|
|
1774
|
+
if (n.shaderSource(i, e), n.compileShader(i), !n.getShaderParameter(i, n.COMPILE_STATUS)) {
|
|
1775
|
+
const r = n.getShaderInfoLog(i) ?? "";
|
|
1776
|
+
throw n.deleteShader(i), new Error(`Shader compilation failed:
|
|
1777
|
+
${r}`);
|
|
1778
|
+
}
|
|
1779
|
+
return i;
|
|
1780
|
+
}
|
|
1781
|
+
function V(n, t, e) {
|
|
1782
|
+
const i = n.createProgram();
|
|
1783
|
+
if (!i) throw new Error("Failed to create program.");
|
|
1784
|
+
if (n.attachShader(i, t), n.attachShader(i, e), n.linkProgram(i), !n.getProgramParameter(i, n.LINK_STATUS)) {
|
|
1785
|
+
const r = n.getProgramInfoLog(i) ?? "";
|
|
1786
|
+
throw n.deleteProgram(i), new Error(`Program linking failed:
|
|
1787
|
+
${r}`);
|
|
1788
|
+
}
|
|
1789
|
+
return n.detachShader(i, t), n.detachShader(i, e), n.deleteShader(t), n.deleteShader(e), i;
|
|
1790
|
+
}
|
|
1791
|
+
function fe(n) {
|
|
1792
|
+
const t = [];
|
|
1793
|
+
let e = 0;
|
|
1794
|
+
for (let i = 0; i < n.length - 2; i += 2) {
|
|
1795
|
+
const r = n[i], o = n[i + 1], s = n[i + 2], a = n[i + 3], h = s - r, l = a - o, u = Math.sqrt(h * h + l * l);
|
|
1796
|
+
if (u < 1e-6) continue;
|
|
1797
|
+
const f = -l / u, c = h / u;
|
|
1798
|
+
t.push(
|
|
1799
|
+
r,
|
|
1800
|
+
o,
|
|
1801
|
+
f,
|
|
1802
|
+
c,
|
|
1803
|
+
r,
|
|
1804
|
+
o,
|
|
1805
|
+
-f,
|
|
1806
|
+
-c,
|
|
1807
|
+
s,
|
|
1808
|
+
a,
|
|
1809
|
+
f,
|
|
1810
|
+
c,
|
|
1811
|
+
s,
|
|
1812
|
+
a,
|
|
1813
|
+
f,
|
|
1814
|
+
c,
|
|
1815
|
+
r,
|
|
1816
|
+
o,
|
|
1817
|
+
-f,
|
|
1818
|
+
-c,
|
|
1819
|
+
s,
|
|
1820
|
+
a,
|
|
1821
|
+
-f,
|
|
1822
|
+
-c
|
|
1823
|
+
), e += 6;
|
|
1824
|
+
}
|
|
1825
|
+
return {
|
|
1826
|
+
vertices: new Float32Array(t),
|
|
1827
|
+
count: e
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
function de(n, t, e, i, r) {
|
|
1831
|
+
if (i <= 0)
|
|
1832
|
+
return { vertices: new Float32Array(0), count: 0 };
|
|
1833
|
+
const o = r * Math.PI / 180, s = -Math.cos(o), a = Math.sin(o), h = [];
|
|
1834
|
+
let l = 0;
|
|
1835
|
+
for (let u = 0; u < t.length; u++) {
|
|
1836
|
+
const f = t[u], m = ((u + 1 < t.length ? t[u + 1] : n.length) - f) / 2;
|
|
1837
|
+
if (m < 3) continue;
|
|
1838
|
+
const g = m - 1;
|
|
1839
|
+
let U = 0;
|
|
1840
|
+
for (let x = 0; x < g; x++) {
|
|
1841
|
+
const y = f + x * 2, S = n[y], F = n[y + 1], P = n[y + 2], _ = n[y + 3];
|
|
1842
|
+
U += S * _ - P * F;
|
|
1843
|
+
}
|
|
1844
|
+
const b = U >= 0 ? 1 : -1, E = [], p = [];
|
|
1845
|
+
for (let x = 0; x < g; x++) {
|
|
1846
|
+
const y = f + x * 2, S = n[y + 2] - n[y], F = n[y + 3] - n[y + 1], P = Math.sqrt(S * S + F * F);
|
|
1847
|
+
P < 1e-8 ? (E.push(x > 0 ? E[x - 1] : 0), p.push(x > 0 ? p[x - 1] : 0)) : (E.push(-F / P * b), p.push(S / P * b));
|
|
1848
|
+
}
|
|
1849
|
+
const v = [], T = [];
|
|
1850
|
+
for (let x = 0; x < g; x++) {
|
|
1851
|
+
const y = (x - 1 + g) % g;
|
|
1852
|
+
let S = E[y] + E[x], F = p[y] + p[x];
|
|
1853
|
+
const P = Math.sqrt(S * S + F * F);
|
|
1854
|
+
P > 1e-8 ? (S /= P, F /= P) : (S = E[x], F = p[x]), v.push(S), T.push(F);
|
|
1855
|
+
}
|
|
1856
|
+
for (let x = 0; x < g; x++) {
|
|
1857
|
+
const y = x, S = (x + 1) % g, F = f + x * 2, P = f + (x + 1) % g * 2, _ = n[F], k = n[F + 1], D = n[P], R = n[P + 1], C = v[y] * a, M = T[y] * a, H = s, I = v[S] * a, N = T[S] * a, j = s, et = _ + v[y] * i, mt = k + T[y] * i, wt = D + v[S] * i, Lt = R + T[S] * i;
|
|
1858
|
+
h.push(_, k, C, M, H, 0), h.push(et, mt, C, M, H, 1), h.push(D, R, I, N, j, 0), h.push(D, R, I, N, j, 0), h.push(et, mt, C, M, H, 1), h.push(wt, Lt, I, N, j, 1), l += 6;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
return {
|
|
1862
|
+
vertices: new Float32Array(h),
|
|
1863
|
+
count: l
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
class ft {
|
|
1867
|
+
static RESIZE_DEBOUNCE_MS = 100;
|
|
1868
|
+
canvas;
|
|
1869
|
+
gl = null;
|
|
1870
|
+
container;
|
|
1871
|
+
// Shader programs
|
|
1872
|
+
stencilProgram = null;
|
|
1873
|
+
maskProgram = null;
|
|
1874
|
+
jfaSeedProgram = null;
|
|
1875
|
+
jfaFloodProgram = null;
|
|
1876
|
+
jfaDistProgram = null;
|
|
1877
|
+
interiorProgram = null;
|
|
1878
|
+
compositeProgram = null;
|
|
1879
|
+
boundaryProgram = null;
|
|
1880
|
+
chamferProgram = null;
|
|
1881
|
+
// Uniform locations (stored as Record for flexibility)
|
|
1882
|
+
stencilUniforms = {};
|
|
1883
|
+
maskUniforms = {};
|
|
1884
|
+
jfaSeedUniforms = {};
|
|
1885
|
+
jfaFloodUniforms = {};
|
|
1886
|
+
jfaDistUniforms = {};
|
|
1887
|
+
interiorUniforms = {};
|
|
1888
|
+
compositeUniforms = {};
|
|
1889
|
+
boundaryUniforms = {};
|
|
1890
|
+
chamferUniforms = {};
|
|
1891
|
+
// Geometry
|
|
1892
|
+
quadVao = null;
|
|
1893
|
+
stencilVao = null;
|
|
1894
|
+
stencilIndexCount = 0;
|
|
1895
|
+
maskVao = null;
|
|
1896
|
+
boundaryVao = null;
|
|
1897
|
+
boundaryVertexCount = 0;
|
|
1898
|
+
chamferVao = null;
|
|
1899
|
+
chamferVertexCount = 0;
|
|
1900
|
+
// Source textures
|
|
1901
|
+
videoTexture = null;
|
|
1902
|
+
depthTexture = null;
|
|
1903
|
+
// Interior FBO (units 2, 3)
|
|
1904
|
+
interiorFbo = null;
|
|
1905
|
+
interiorColorTex = null;
|
|
1906
|
+
interiorDepthTex = null;
|
|
1907
|
+
fboWidth = 0;
|
|
1908
|
+
fboHeight = 0;
|
|
1909
|
+
// JFA distance field system (unit 4 for final distance)
|
|
1910
|
+
maskFbo = null;
|
|
1911
|
+
maskTex = null;
|
|
1912
|
+
jfaPingFbo = null;
|
|
1913
|
+
jfaPingTex = null;
|
|
1914
|
+
jfaPongFbo = null;
|
|
1915
|
+
jfaPongTex = null;
|
|
1916
|
+
distFbo = null;
|
|
1917
|
+
distTex = null;
|
|
1918
|
+
jfaWidth = 0;
|
|
1919
|
+
jfaHeight = 0;
|
|
1920
|
+
distFieldDirty = !0;
|
|
1921
|
+
// Dimensions
|
|
1922
|
+
depthWidth = 0;
|
|
1923
|
+
depthHeight = 0;
|
|
1924
|
+
videoAspect = 16 / 9;
|
|
1925
|
+
meshAspect = 1;
|
|
1926
|
+
meshScaleX = 0.65;
|
|
1927
|
+
meshScaleY = 0.65;
|
|
1928
|
+
// Callbacks
|
|
1929
|
+
readDepth = null;
|
|
1930
|
+
readInput = null;
|
|
1931
|
+
playbackVideo = null;
|
|
1932
|
+
onVideoFrame = null;
|
|
1933
|
+
// Animation
|
|
1934
|
+
animationFrameHandle = 0;
|
|
1935
|
+
rvfcHandle = 0;
|
|
1936
|
+
rvfcSupported = !1;
|
|
1937
|
+
resizeObserver = null;
|
|
1938
|
+
resizeTimer = null;
|
|
1939
|
+
// UV transform
|
|
1940
|
+
uvOffset = [0, 0];
|
|
1941
|
+
uvScale = [1, 1];
|
|
1942
|
+
// Precomputed light direction (2D for bevel, 3D for chamfer)
|
|
1943
|
+
lightDirX = -0.707;
|
|
1944
|
+
lightDirY = 0.707;
|
|
1945
|
+
lightDir3 = [-0.5, 0.7, -0.3];
|
|
1946
|
+
config;
|
|
1947
|
+
constructor(t, e) {
|
|
1948
|
+
this.container = t, this.config = { ...e };
|
|
1949
|
+
const i = this.config.bevelLightAngle * Math.PI / 180;
|
|
1950
|
+
this.lightDirX = Math.cos(i), this.lightDirY = Math.sin(i);
|
|
1951
|
+
const r = this.config.lightDirection, o = Math.sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]);
|
|
1952
|
+
o > 1e-6 && (this.lightDir3 = [r[0] / o, r[1] / o, r[2] / o]), this.canvas = document.createElement("canvas");
|
|
1953
|
+
const s = this.canvas.getContext("webgl2", {
|
|
1954
|
+
antialias: !0,
|
|
1955
|
+
alpha: !0,
|
|
1956
|
+
premultipliedAlpha: !0,
|
|
1957
|
+
stencil: !0,
|
|
1958
|
+
desynchronized: !0,
|
|
1959
|
+
powerPreference: "high-performance"
|
|
1960
|
+
});
|
|
1961
|
+
if (!s) throw new Error("WebGL 2 is not supported.");
|
|
1962
|
+
this.gl = s, "drawingBufferColorSpace" in s && (s.drawingBufferColorSpace = "srgb"), s.clearColor(0, 0, 0, 0), s.pixelStorei(s.UNPACK_FLIP_Y_WEBGL, !0), this.container.appendChild(this.canvas), this.initGPUResources(), this.setupResizeHandling(), this.canvas.addEventListener("webglcontextlost", this.handleContextLost), this.canvas.addEventListener("webglcontextrestored", this.handleContextRestored);
|
|
1963
|
+
}
|
|
1964
|
+
initialize(t, e, i, r) {
|
|
1965
|
+
const o = this.gl;
|
|
1966
|
+
o && (this.disposeTextures(), this.disposeFBO(), this.disposeJFA(), this.disposeStencilGeometry(), this.disposeBoundaryGeometry(), this.disposeChamferGeometry(), this.videoAspect = t.videoWidth / t.videoHeight, this.meshAspect = r.aspect, this.depthWidth = e, this.depthHeight = i, this.videoTexture = o.createTexture(), o.activeTexture(o.TEXTURE0), o.bindTexture(o.TEXTURE_2D, this.videoTexture), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MIN_FILTER, o.LINEAR), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MAG_FILTER, o.LINEAR), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_WRAP_S, o.CLAMP_TO_EDGE), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_WRAP_T, o.CLAMP_TO_EDGE), this.depthTexture = o.createTexture(), o.activeTexture(o.TEXTURE1), o.bindTexture(o.TEXTURE_2D, this.depthTexture), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MIN_FILTER, o.LINEAR), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MAG_FILTER, o.LINEAR), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_WRAP_S, o.CLAMP_TO_EDGE), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_WRAP_T, o.CLAMP_TO_EDGE), o.texStorage2D(o.TEXTURE_2D, 1, o.R8, e, i), this.uploadStencilMesh(r), this.uploadMaskMesh(r), this.uploadBoundaryMesh(r), this.uploadChamferMesh(r), this.interiorProgram && (o.useProgram(this.interiorProgram), o.uniform1i(this.interiorUniforms.uImage, 0), o.uniform1i(this.interiorUniforms.uDepth, 1), o.uniform1f(this.interiorUniforms.uStrength, this.config.parallaxStrength), o.uniform1i(this.interiorUniforms.uPomSteps, this.config.pomSteps), o.uniform1f(this.interiorUniforms.uDepthPower, this.config.depthPower), o.uniform1f(this.interiorUniforms.uDepthScale, this.config.depthScale), o.uniform1f(this.interiorUniforms.uDepthBias, this.config.depthBias), o.uniform1f(this.interiorUniforms.uContrastLow, this.config.contrastLow), o.uniform1f(this.interiorUniforms.uContrastHigh, this.config.contrastHigh), o.uniform1f(this.interiorUniforms.uVerticalReduction, this.config.verticalReduction), o.uniform1f(this.interiorUniforms.uDofStart, this.config.dofStart), o.uniform1f(this.interiorUniforms.uDofStrength, this.config.dofStrength), o.uniform2f(this.interiorUniforms.uImageTexelSize, 1 / t.videoWidth, 1 / t.videoHeight), o.uniform1f(this.interiorUniforms.uFogDensity, this.config.fogDensity), o.uniform3f(this.interiorUniforms.uFogColor, ...this.config.fogColor), o.uniform1f(this.interiorUniforms.uColorShift, this.config.colorShift), o.uniform1f(this.interiorUniforms.uBrightnessBias, this.config.brightnessBias)), this.compositeProgram && (o.useProgram(this.compositeProgram), o.uniform1i(this.compositeUniforms.uInteriorColor, 2), o.uniform1i(this.compositeUniforms.uDistField, 4), o.uniform1f(this.compositeUniforms.uEdgeOcclusionWidth, this.config.edgeOcclusionWidth), o.uniform1f(this.compositeUniforms.uEdgeOcclusionStrength, this.config.edgeOcclusionStrength)), this.chamferProgram && (o.useProgram(this.chamferProgram), o.uniform3f(this.chamferUniforms.uLightDir3, ...this.lightDir3), o.uniform3f(this.chamferUniforms.uChamferColor, ...this.config.chamferColor), o.uniform1f(this.chamferUniforms.uChamferAmbient, this.config.chamferAmbient), o.uniform1f(this.chamferUniforms.uChamferSpecular, this.config.chamferSpecular), o.uniform1f(this.chamferUniforms.uChamferShininess, this.config.chamferShininess), o.uniform1i(this.chamferUniforms.uInteriorColor, 2)), this.boundaryProgram && (o.useProgram(this.boundaryProgram), o.uniform1i(this.boundaryUniforms.uInteriorColor, 2), o.uniform1i(this.boundaryUniforms.uInteriorDepth, 3), o.uniform1i(this.boundaryUniforms.uDistField, 4), o.uniform1f(this.boundaryUniforms.uRimIntensity, this.config.rimLightIntensity), o.uniform3f(this.boundaryUniforms.uRimColor, ...this.config.rimLightColor), o.uniform1f(this.boundaryUniforms.uRefractionStrength, this.config.refractionStrength), o.uniform1f(this.boundaryUniforms.uChromaticStrength, this.config.chromaticStrength), o.uniform1f(this.boundaryUniforms.uOcclusionIntensity, this.config.occlusionIntensity), o.uniform1f(this.boundaryUniforms.uEdgeThickness, this.config.edgeThickness), o.uniform1f(this.boundaryUniforms.uEdgeSpecular, this.config.edgeSpecular), o.uniform3f(this.boundaryUniforms.uEdgeColor, ...this.config.edgeColor), o.uniform2f(this.boundaryUniforms.uLightDir, this.lightDirX, this.lightDirY), o.uniform1f(this.boundaryUniforms.uBevelIntensity, this.config.bevelIntensity)), this.recalculateViewportLayout());
|
|
1967
|
+
}
|
|
1968
|
+
// -----------------------------------------------------------------------
|
|
1969
|
+
// Geometry upload
|
|
1970
|
+
// -----------------------------------------------------------------------
|
|
1971
|
+
uploadStencilMesh(t) {
|
|
1972
|
+
const e = this.gl;
|
|
1973
|
+
if (!e || !this.stencilProgram) return;
|
|
1974
|
+
this.stencilVao = e.createVertexArray(), e.bindVertexArray(this.stencilVao);
|
|
1975
|
+
const i = e.createBuffer();
|
|
1976
|
+
e.bindBuffer(e.ARRAY_BUFFER, i), e.bufferData(e.ARRAY_BUFFER, t.vertices, e.STATIC_DRAW);
|
|
1977
|
+
const r = e.getAttribLocation(this.stencilProgram, "aPosition");
|
|
1978
|
+
e.enableVertexAttribArray(r), e.vertexAttribPointer(r, 2, e.FLOAT, !1, 0, 0);
|
|
1979
|
+
const o = e.createBuffer();
|
|
1980
|
+
e.bindBuffer(e.ELEMENT_ARRAY_BUFFER, o), e.bufferData(e.ELEMENT_ARRAY_BUFFER, t.indices, e.STATIC_DRAW), this.stencilIndexCount = t.indices.length, e.bindVertexArray(null);
|
|
1981
|
+
}
|
|
1982
|
+
uploadMaskMesh(t) {
|
|
1983
|
+
const e = this.gl;
|
|
1984
|
+
if (!e || !this.maskProgram) return;
|
|
1985
|
+
this.maskVao = e.createVertexArray(), e.bindVertexArray(this.maskVao);
|
|
1986
|
+
const i = e.createBuffer();
|
|
1987
|
+
e.bindBuffer(e.ARRAY_BUFFER, i), e.bufferData(e.ARRAY_BUFFER, t.vertices, e.STATIC_DRAW);
|
|
1988
|
+
const r = e.getAttribLocation(this.maskProgram, "aPosition");
|
|
1989
|
+
e.enableVertexAttribArray(r), e.vertexAttribPointer(r, 2, e.FLOAT, !1, 0, 0);
|
|
1990
|
+
const o = e.createBuffer();
|
|
1991
|
+
e.bindBuffer(e.ELEMENT_ARRAY_BUFFER, o), e.bufferData(e.ELEMENT_ARRAY_BUFFER, t.indices, e.STATIC_DRAW), e.bindVertexArray(null);
|
|
1992
|
+
}
|
|
1993
|
+
uploadBoundaryMesh(t) {
|
|
1994
|
+
const e = this.gl;
|
|
1995
|
+
if (!e || !this.boundaryProgram) return;
|
|
1996
|
+
const i = fe(t.edgeVertices);
|
|
1997
|
+
if (i.count === 0) return;
|
|
1998
|
+
this.boundaryVao = e.createVertexArray(), e.bindVertexArray(this.boundaryVao);
|
|
1999
|
+
const r = e.createBuffer();
|
|
2000
|
+
e.bindBuffer(e.ARRAY_BUFFER, r), e.bufferData(e.ARRAY_BUFFER, i.vertices, e.STATIC_DRAW);
|
|
2001
|
+
const o = 16, s = e.getAttribLocation(this.boundaryProgram, "aPosition");
|
|
2002
|
+
e.enableVertexAttribArray(s), e.vertexAttribPointer(s, 2, e.FLOAT, !1, o, 0);
|
|
2003
|
+
const a = e.getAttribLocation(this.boundaryProgram, "aNormal");
|
|
2004
|
+
a >= 0 && (e.enableVertexAttribArray(a), e.vertexAttribPointer(a, 2, e.FLOAT, !1, o, 8)), this.boundaryVertexCount = i.count, e.bindVertexArray(null);
|
|
2005
|
+
}
|
|
2006
|
+
uploadChamferMesh(t) {
|
|
2007
|
+
const e = this.gl;
|
|
2008
|
+
if (!e || !this.chamferProgram || this.config.chamferWidth <= 0) return;
|
|
2009
|
+
const i = de(
|
|
2010
|
+
t.edgeVertices,
|
|
2011
|
+
t.contourOffsets,
|
|
2012
|
+
t.contourIsHole,
|
|
2013
|
+
this.config.chamferWidth,
|
|
2014
|
+
this.config.chamferAngle
|
|
2015
|
+
);
|
|
2016
|
+
if (i.count === 0) return;
|
|
2017
|
+
this.chamferVao = e.createVertexArray(), e.bindVertexArray(this.chamferVao);
|
|
2018
|
+
const r = e.createBuffer();
|
|
2019
|
+
e.bindBuffer(e.ARRAY_BUFFER, r), e.bufferData(e.ARRAY_BUFFER, i.vertices, e.STATIC_DRAW);
|
|
2020
|
+
const o = 24, s = e.getAttribLocation(this.chamferProgram, "aPosition");
|
|
2021
|
+
e.enableVertexAttribArray(s), e.vertexAttribPointer(s, 2, e.FLOAT, !1, o, 0);
|
|
2022
|
+
const a = e.getAttribLocation(this.chamferProgram, "aNormal3");
|
|
2023
|
+
a >= 0 && (e.enableVertexAttribArray(a), e.vertexAttribPointer(a, 3, e.FLOAT, !1, o, 8));
|
|
2024
|
+
const h = e.getAttribLocation(this.chamferProgram, "aLerpT");
|
|
2025
|
+
h >= 0 && (e.enableVertexAttribArray(h), e.vertexAttribPointer(h, 1, e.FLOAT, !1, o, 20)), this.chamferVertexCount = i.count, e.bindVertexArray(null);
|
|
2026
|
+
}
|
|
2027
|
+
disposeChamferGeometry() {
|
|
2028
|
+
const t = this.gl;
|
|
2029
|
+
t && (this.chamferVao && (t.deleteVertexArray(this.chamferVao), this.chamferVao = null), this.chamferVertexCount = 0);
|
|
2030
|
+
}
|
|
2031
|
+
// -----------------------------------------------------------------------
|
|
2032
|
+
// FBO management
|
|
2033
|
+
// -----------------------------------------------------------------------
|
|
2034
|
+
createFBO(t, e) {
|
|
2035
|
+
const i = this.gl;
|
|
2036
|
+
if (!i) return;
|
|
2037
|
+
this.disposeFBO(), this.fboWidth = t, this.fboHeight = e, this.interiorFbo = i.createFramebuffer(), i.bindFramebuffer(i.FRAMEBUFFER, this.interiorFbo), this.interiorColorTex = i.createTexture(), i.activeTexture(i.TEXTURE2), i.bindTexture(i.TEXTURE_2D, this.interiorColorTex), i.texStorage2D(i.TEXTURE_2D, 1, i.RGBA8, t, e), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MIN_FILTER, i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MAG_FILTER, i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_S, i.CLAMP_TO_EDGE), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_T, i.CLAMP_TO_EDGE), i.framebufferTexture2D(i.FRAMEBUFFER, i.COLOR_ATTACHMENT0, i.TEXTURE_2D, this.interiorColorTex, 0), this.interiorDepthTex = i.createTexture(), i.activeTexture(i.TEXTURE3), i.bindTexture(i.TEXTURE_2D, this.interiorDepthTex), i.texStorage2D(i.TEXTURE_2D, 1, i.RGBA8, t, e), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MIN_FILTER, i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MAG_FILTER, i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_S, i.CLAMP_TO_EDGE), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_T, i.CLAMP_TO_EDGE), i.framebufferTexture2D(i.FRAMEBUFFER, i.COLOR_ATTACHMENT1, i.TEXTURE_2D, this.interiorDepthTex, 0), i.drawBuffers([i.COLOR_ATTACHMENT0, i.COLOR_ATTACHMENT1]);
|
|
2038
|
+
const r = i.checkFramebufferStatus(i.FRAMEBUFFER);
|
|
2039
|
+
r !== i.FRAMEBUFFER_COMPLETE && console.error("Interior FBO incomplete:", r), i.bindFramebuffer(i.FRAMEBUFFER, null);
|
|
2040
|
+
}
|
|
2041
|
+
// -----------------------------------------------------------------------
|
|
2042
|
+
// JFA Distance Field
|
|
2043
|
+
// -----------------------------------------------------------------------
|
|
2044
|
+
createJFAResources(t, e) {
|
|
2045
|
+
const i = this.gl;
|
|
2046
|
+
if (!i) return;
|
|
2047
|
+
this.disposeJFA();
|
|
2048
|
+
const r = Math.max(1, Math.round(t / 2)), o = Math.max(1, Math.round(e / 2));
|
|
2049
|
+
this.jfaWidth = r, this.jfaHeight = o;
|
|
2050
|
+
const s = (a, h, l, u) => {
|
|
2051
|
+
const f = i.createFramebuffer();
|
|
2052
|
+
return i.bindFramebuffer(i.FRAMEBUFFER, f), i.bindTexture(i.TEXTURE_2D, a), i.texStorage2D(i.TEXTURE_2D, 1, h, l, u), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MIN_FILTER, i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MAG_FILTER, i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_S, i.CLAMP_TO_EDGE), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_T, i.CLAMP_TO_EDGE), i.framebufferTexture2D(i.FRAMEBUFFER, i.COLOR_ATTACHMENT0, i.TEXTURE_2D, a, 0), i.bindFramebuffer(i.FRAMEBUFFER, null), f;
|
|
2053
|
+
};
|
|
2054
|
+
this.maskTex = i.createTexture(), this.maskFbo = s(this.maskTex, i.R8, r, o), this.jfaPingTex = i.createTexture(), this.jfaPingFbo = s(this.jfaPingTex, i.RG16F, r, o), this.jfaPongTex = i.createTexture(), this.jfaPongFbo = s(this.jfaPongTex, i.RG16F, r, o), this.distTex = i.createTexture(), this.distFbo = s(this.distTex, i.RGBA8, r, o), this.distFieldDirty = !0;
|
|
2055
|
+
}
|
|
2056
|
+
computeDistanceField() {
|
|
2057
|
+
const t = this.gl;
|
|
2058
|
+
if (!t || !this.maskFbo || !this.maskVao || !this.quadVao || !this.jfaPingFbo || !this.jfaPongFbo || !this.distFbo) return;
|
|
2059
|
+
const e = this.jfaWidth, i = this.jfaHeight;
|
|
2060
|
+
if (e === 0 || i === 0) return;
|
|
2061
|
+
t.viewport(0, 0, e, i), t.disable(t.STENCIL_TEST), t.disable(t.BLEND), t.bindFramebuffer(t.FRAMEBUFFER, this.maskFbo), t.clearColor(0, 0, 0, 1), t.clear(t.COLOR_BUFFER_BIT), t.useProgram(this.maskProgram), t.uniform2f(this.maskUniforms.uMeshScale, this.meshScaleX, this.meshScaleY), t.bindVertexArray(this.maskVao), t.drawElements(t.TRIANGLES, this.stencilIndexCount, t.UNSIGNED_SHORT, 0), t.bindFramebuffer(t.FRAMEBUFFER, this.jfaPingFbo), t.clearColor(-1, -1, 0, 0), t.clear(t.COLOR_BUFFER_BIT), t.useProgram(this.jfaSeedProgram), t.activeTexture(t.TEXTURE5), t.bindTexture(t.TEXTURE_2D, this.maskTex), t.uniform1i(this.jfaSeedUniforms.uMask, 5), t.uniform2f(this.jfaSeedUniforms.uTexelSize, 1 / e, 1 / i), t.bindVertexArray(this.quadVao), t.drawArrays(t.TRIANGLE_STRIP, 0, 4);
|
|
2062
|
+
const r = Math.max(e, i), o = [];
|
|
2063
|
+
let s = Math.ceil(r / 2);
|
|
2064
|
+
for (; s >= 1; )
|
|
2065
|
+
o.push(s), s = Math.floor(s / 2);
|
|
2066
|
+
t.useProgram(this.jfaFloodProgram);
|
|
2067
|
+
let a = this.jfaPingTex, h = this.jfaPongFbo, l = this.jfaPongTex;
|
|
2068
|
+
for (let f = 0; f < o.length; f++) {
|
|
2069
|
+
const c = o[f] / Math.max(e, i);
|
|
2070
|
+
t.bindFramebuffer(t.FRAMEBUFFER, h), t.activeTexture(t.TEXTURE5), t.bindTexture(t.TEXTURE_2D, a), t.uniform1i(this.jfaFloodUniforms.uSeedTex, 5), t.uniform1f(this.jfaFloodUniforms.uStepSize, c), t.bindVertexArray(this.quadVao), t.drawArrays(t.TRIANGLE_STRIP, 0, 4);
|
|
2071
|
+
const d = a, m = h;
|
|
2072
|
+
a = l, h = m === this.jfaPongFbo ? this.jfaPingFbo : this.jfaPongFbo, l = d;
|
|
2073
|
+
}
|
|
2074
|
+
t.bindFramebuffer(t.FRAMEBUFFER, this.distFbo), t.clearColor(0, 0, 0, 1), t.clear(t.COLOR_BUFFER_BIT), t.useProgram(this.jfaDistProgram), t.activeTexture(t.TEXTURE5), t.bindTexture(t.TEXTURE_2D, a), t.uniform1i(this.jfaDistUniforms.uSeedTex, 5), t.activeTexture(t.TEXTURE6), t.bindTexture(t.TEXTURE_2D, this.maskTex), t.uniform1i(this.jfaDistUniforms.uMask, 6);
|
|
2075
|
+
const u = Math.max(this.config.bevelWidth, this.config.edgeOcclusionWidth);
|
|
2076
|
+
t.uniform1f(this.jfaDistUniforms.uBevelWidth, u), t.bindVertexArray(this.quadVao), t.drawArrays(t.TRIANGLE_STRIP, 0, 4), t.activeTexture(t.TEXTURE4), t.bindTexture(t.TEXTURE_2D, this.distTex), t.bindFramebuffer(t.FRAMEBUFFER, null), this.distFieldDirty = !1;
|
|
2077
|
+
}
|
|
2078
|
+
// -----------------------------------------------------------------------
|
|
2079
|
+
// Render loop control
|
|
2080
|
+
// -----------------------------------------------------------------------
|
|
2081
|
+
start(t, e, i, r) {
|
|
2082
|
+
this.stop(), this.playbackVideo = t, this.readDepth = e, this.readInput = i, this.onVideoFrame = r ?? null, this.rvfcSupported = "requestVideoFrameCallback" in HTMLVideoElement.prototype, this.rvfcSupported && (this.rvfcHandle = t.requestVideoFrameCallback(this.videoFrameLoop)), this.animationFrameHandle = window.requestAnimationFrame(this.renderLoop);
|
|
2083
|
+
}
|
|
2084
|
+
stop() {
|
|
2085
|
+
this.animationFrameHandle && (window.cancelAnimationFrame(this.animationFrameHandle), this.animationFrameHandle = 0), this.rvfcHandle && this.playbackVideo && (this.playbackVideo.cancelVideoFrameCallback(this.rvfcHandle), this.rvfcHandle = 0), this.playbackVideo = null, this.readDepth = null, this.readInput = null, this.onVideoFrame = null, this.rvfcSupported = !1;
|
|
2086
|
+
}
|
|
2087
|
+
dispose() {
|
|
2088
|
+
this.stop(), this.disposeTextures(), this.disposeFBO(), this.disposeJFA(), this.disposeStencilGeometry(), this.disposeBoundaryGeometry(), this.disposeChamferGeometry(), this.disposeGPUResources(), this.canvas.removeEventListener("webglcontextlost", this.handleContextLost), this.canvas.removeEventListener("webglcontextrestored", this.handleContextRestored), this.resizeObserver && (this.resizeObserver.disconnect(), this.resizeObserver = null), window.removeEventListener("resize", this.scheduleResizeRecalculate), this.resizeTimer !== null && (window.clearTimeout(this.resizeTimer), this.resizeTimer = null);
|
|
2089
|
+
}
|
|
2090
|
+
// -----------------------------------------------------------------------
|
|
2091
|
+
// GPU resource initialization
|
|
2092
|
+
// -----------------------------------------------------------------------
|
|
2093
|
+
initGPUResources() {
|
|
2094
|
+
const t = this.gl;
|
|
2095
|
+
if (!t) return;
|
|
2096
|
+
this.stencilProgram = V(
|
|
2097
|
+
t,
|
|
2098
|
+
L(t, t.VERTEX_SHADER, qt),
|
|
2099
|
+
L(t, t.FRAGMENT_SHADER, Zt)
|
|
2100
|
+
), this.stencilUniforms = {
|
|
2101
|
+
uMeshScale: t.getUniformLocation(this.stencilProgram, "uMeshScale")
|
|
2102
|
+
}, this.maskProgram = V(
|
|
2103
|
+
t,
|
|
2104
|
+
L(t, t.VERTEX_SHADER, $t),
|
|
2105
|
+
L(t, t.FRAGMENT_SHADER, Jt)
|
|
2106
|
+
), this.maskUniforms = {
|
|
2107
|
+
uMeshScale: t.getUniformLocation(this.maskProgram, "uMeshScale")
|
|
2108
|
+
}, this.jfaSeedProgram = V(
|
|
2109
|
+
t,
|
|
2110
|
+
L(t, t.VERTEX_SHADER, Kt),
|
|
2111
|
+
L(t, t.FRAGMENT_SHADER, Qt)
|
|
2112
|
+
), this.jfaSeedUniforms = this.getUniforms(this.jfaSeedProgram, ["uMask", "uTexelSize"]), this.jfaFloodProgram = V(
|
|
2113
|
+
t,
|
|
2114
|
+
L(t, t.VERTEX_SHADER, te),
|
|
2115
|
+
L(t, t.FRAGMENT_SHADER, ee)
|
|
2116
|
+
), this.jfaFloodUniforms = this.getUniforms(this.jfaFloodProgram, ["uSeedTex", "uStepSize"]), this.jfaDistProgram = V(
|
|
2117
|
+
t,
|
|
2118
|
+
L(t, t.VERTEX_SHADER, ie),
|
|
2119
|
+
L(t, t.FRAGMENT_SHADER, re)
|
|
2120
|
+
), this.jfaDistUniforms = this.getUniforms(this.jfaDistProgram, ["uSeedTex", "uMask", "uBevelWidth"]), this.interiorProgram = V(
|
|
2121
|
+
t,
|
|
2122
|
+
L(t, t.VERTEX_SHADER, oe),
|
|
2123
|
+
L(t, t.FRAGMENT_SHADER, ne)
|
|
2124
|
+
), this.interiorUniforms = this.getUniforms(this.interiorProgram, [
|
|
2125
|
+
"uImage",
|
|
2126
|
+
"uDepth",
|
|
2127
|
+
"uOffset",
|
|
2128
|
+
"uStrength",
|
|
2129
|
+
"uPomSteps",
|
|
2130
|
+
"uDepthPower",
|
|
2131
|
+
"uDepthScale",
|
|
2132
|
+
"uDepthBias",
|
|
2133
|
+
"uContrastLow",
|
|
2134
|
+
"uContrastHigh",
|
|
2135
|
+
"uVerticalReduction",
|
|
2136
|
+
"uDofStart",
|
|
2137
|
+
"uDofStrength",
|
|
2138
|
+
"uImageTexelSize",
|
|
2139
|
+
"uFogDensity",
|
|
2140
|
+
"uFogColor",
|
|
2141
|
+
"uColorShift",
|
|
2142
|
+
"uBrightnessBias",
|
|
2143
|
+
"uUvOffset",
|
|
2144
|
+
"uUvScale"
|
|
2145
|
+
]), this.compositeProgram = V(
|
|
2146
|
+
t,
|
|
2147
|
+
L(t, t.VERTEX_SHADER, se),
|
|
2148
|
+
L(t, t.FRAGMENT_SHADER, ae)
|
|
2149
|
+
), this.compositeUniforms = this.getUniforms(this.compositeProgram, [
|
|
2150
|
+
"uInteriorColor",
|
|
2151
|
+
"uDistField",
|
|
2152
|
+
"uEdgeOcclusionWidth",
|
|
2153
|
+
"uEdgeOcclusionStrength"
|
|
2154
|
+
]), this.boundaryProgram = V(
|
|
2155
|
+
t,
|
|
2156
|
+
L(t, t.VERTEX_SHADER, he),
|
|
2157
|
+
L(t, t.FRAGMENT_SHADER, le)
|
|
2158
|
+
), this.boundaryUniforms = this.getUniforms(this.boundaryProgram, [
|
|
2159
|
+
"uInteriorColor",
|
|
2160
|
+
"uInteriorDepth",
|
|
2161
|
+
"uDistField",
|
|
2162
|
+
"uRimIntensity",
|
|
2163
|
+
"uRimColor",
|
|
2164
|
+
"uRimWidth",
|
|
2165
|
+
"uMeshScale",
|
|
2166
|
+
"uRefractionStrength",
|
|
2167
|
+
"uChromaticStrength",
|
|
2168
|
+
"uOcclusionIntensity",
|
|
2169
|
+
"uTexelSize",
|
|
2170
|
+
"uEdgeThickness",
|
|
2171
|
+
"uEdgeSpecular",
|
|
2172
|
+
"uEdgeColor",
|
|
2173
|
+
"uLightDir",
|
|
2174
|
+
"uBevelIntensity"
|
|
2175
|
+
]), this.chamferProgram = V(
|
|
2176
|
+
t,
|
|
2177
|
+
L(t, t.VERTEX_SHADER, ce),
|
|
2178
|
+
L(t, t.FRAGMENT_SHADER, ue)
|
|
2179
|
+
), this.chamferUniforms = this.getUniforms(this.chamferProgram, [
|
|
2180
|
+
"uMeshScale",
|
|
2181
|
+
"uLightDir3",
|
|
2182
|
+
"uChamferColor",
|
|
2183
|
+
"uChamferAmbient",
|
|
2184
|
+
"uChamferSpecular",
|
|
2185
|
+
"uChamferShininess",
|
|
2186
|
+
"uInteriorColor",
|
|
2187
|
+
"uTexelSize"
|
|
2188
|
+
]);
|
|
2189
|
+
const e = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
|
|
2190
|
+
this.quadVao = t.createVertexArray(), t.bindVertexArray(this.quadVao);
|
|
2191
|
+
const i = t.createBuffer();
|
|
2192
|
+
t.bindBuffer(t.ARRAY_BUFFER, i), t.bufferData(t.ARRAY_BUFFER, e, t.STATIC_DRAW);
|
|
2193
|
+
const r = t.getAttribLocation(this.interiorProgram, "aPosition");
|
|
2194
|
+
t.enableVertexAttribArray(r), t.vertexAttribPointer(r, 2, t.FLOAT, !1, 0, 0), t.bindVertexArray(null), t.disable(t.DEPTH_TEST);
|
|
2195
|
+
}
|
|
2196
|
+
getUniforms(t, e) {
|
|
2197
|
+
const i = this.gl, r = {};
|
|
2198
|
+
for (const o of e)
|
|
2199
|
+
r[o] = i.getUniformLocation(t, o);
|
|
2200
|
+
return r;
|
|
2201
|
+
}
|
|
2202
|
+
// -----------------------------------------------------------------------
|
|
2203
|
+
// RVFC loop
|
|
2204
|
+
// -----------------------------------------------------------------------
|
|
2205
|
+
videoFrameLoop = (t, e) => {
|
|
2206
|
+
const i = this.playbackVideo;
|
|
2207
|
+
if (!i) return;
|
|
2208
|
+
this.rvfcHandle = i.requestVideoFrameCallback(this.videoFrameLoop);
|
|
2209
|
+
const r = e.mediaTime ?? i.currentTime;
|
|
2210
|
+
this.updateDepthTexture(r), this.onVideoFrame && this.onVideoFrame(r, e.presentedFrames ?? 0);
|
|
2211
|
+
};
|
|
2212
|
+
// -----------------------------------------------------------------------
|
|
2213
|
+
// Main render loop
|
|
2214
|
+
// -----------------------------------------------------------------------
|
|
2215
|
+
renderLoop = () => {
|
|
2216
|
+
this.animationFrameHandle = window.requestAnimationFrame(this.renderLoop);
|
|
2217
|
+
const t = this.gl, e = this.playbackVideo;
|
|
2218
|
+
if (!t || !this.interiorProgram || !this.quadVao || !e || e.readyState < HTMLMediaElement.HAVE_CURRENT_DATA || !this.interiorFbo || !this.interiorColorTex || !this.interiorDepthTex) return;
|
|
2219
|
+
this.distFieldDirty && this.maskVao && this.distFbo && (this.computeDistanceField(), t.viewport(0, 0, this.canvas.width, this.canvas.height)), t.activeTexture(t.TEXTURE0), t.bindTexture(t.TEXTURE_2D, this.videoTexture), t.texImage2D(t.TEXTURE_2D, 0, t.RGBA, t.RGBA, t.UNSIGNED_BYTE, e), this.rvfcSupported || this.updateDepthTexture(e.currentTime);
|
|
2220
|
+
let i = 0, r = 0;
|
|
2221
|
+
if (this.readInput) {
|
|
2222
|
+
const o = this.readInput();
|
|
2223
|
+
i = -o.x, r = o.y;
|
|
2224
|
+
}
|
|
2225
|
+
t.bindFramebuffer(t.FRAMEBUFFER, this.interiorFbo), t.viewport(0, 0, this.fboWidth, this.fboHeight), t.clearColor(0, 0, 0, 1), t.clear(t.COLOR_BUFFER_BIT), t.useProgram(this.interiorProgram), t.uniform2f(this.interiorUniforms.uOffset, i, r), t.activeTexture(t.TEXTURE0), t.bindTexture(t.TEXTURE_2D, this.videoTexture), t.activeTexture(t.TEXTURE1), t.bindTexture(t.TEXTURE_2D, this.depthTexture), t.bindVertexArray(this.quadVao), t.drawArrays(t.TRIANGLE_STRIP, 0, 4), t.bindFramebuffer(t.FRAMEBUFFER, null), t.clearColor(0, 0, 0, 0), t.viewport(0, 0, this.canvas.width, this.canvas.height), t.clear(t.COLOR_BUFFER_BIT | t.STENCIL_BUFFER_BIT), this.stencilVao && this.stencilProgram && this.stencilIndexCount > 0 && (t.enable(t.STENCIL_TEST), t.stencilFunc(t.ALWAYS, 1, 255), t.stencilOp(t.KEEP, t.KEEP, t.REPLACE), t.stencilMask(255), t.colorMask(!1, !1, !1, !1), t.useProgram(this.stencilProgram), t.bindVertexArray(this.stencilVao), t.drawElements(t.TRIANGLES, this.stencilIndexCount, t.UNSIGNED_SHORT, 0), t.colorMask(!0, !0, !0, !0)), t.stencilFunc(t.EQUAL, 1, 255), t.stencilMask(0), t.activeTexture(t.TEXTURE2), t.bindTexture(t.TEXTURE_2D, this.interiorColorTex), t.activeTexture(t.TEXTURE3), t.bindTexture(t.TEXTURE_2D, this.interiorDepthTex), t.activeTexture(t.TEXTURE4), t.bindTexture(t.TEXTURE_2D, this.distTex), t.useProgram(this.compositeProgram), t.bindVertexArray(this.quadVao), t.drawArrays(t.TRIANGLE_STRIP, 0, 4), t.disable(t.STENCIL_TEST), this.chamferVao && this.chamferProgram && this.chamferVertexCount > 0 && (t.useProgram(this.chamferProgram), t.uniform2f(this.chamferUniforms.uMeshScale, this.meshScaleX, this.meshScaleY), t.uniform2f(this.chamferUniforms.uTexelSize, 1 / this.canvas.width, 1 / this.canvas.height), t.bindVertexArray(this.chamferVao), t.drawArrays(t.TRIANGLES, 0, this.chamferVertexCount)), this.boundaryVao && this.boundaryProgram && this.boundaryVertexCount > 0 && this.config.rimLightIntensity > 0 && (t.enable(t.BLEND), t.blendFunc(t.SRC_ALPHA, t.ONE_MINUS_SRC_ALPHA), t.useProgram(this.boundaryProgram), t.bindVertexArray(this.boundaryVao), t.drawArrays(t.TRIANGLES, 0, this.boundaryVertexCount), t.disable(t.BLEND));
|
|
2226
|
+
};
|
|
2227
|
+
updateDepthTexture(t) {
|
|
2228
|
+
const e = this.gl;
|
|
2229
|
+
if (!e || !this.readDepth || !this.depthTexture) return;
|
|
2230
|
+
const i = this.readDepth(t);
|
|
2231
|
+
e.activeTexture(e.TEXTURE1), e.bindTexture(e.TEXTURE_2D, this.depthTexture), e.texSubImage2D(
|
|
2232
|
+
e.TEXTURE_2D,
|
|
2233
|
+
0,
|
|
2234
|
+
0,
|
|
2235
|
+
0,
|
|
2236
|
+
this.depthWidth,
|
|
2237
|
+
this.depthHeight,
|
|
2238
|
+
e.RED,
|
|
2239
|
+
e.UNSIGNED_BYTE,
|
|
2240
|
+
i
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
// -----------------------------------------------------------------------
|
|
2244
|
+
// Resize handling
|
|
2245
|
+
// -----------------------------------------------------------------------
|
|
2246
|
+
setupResizeHandling() {
|
|
2247
|
+
typeof ResizeObserver < "u" && (this.resizeObserver = new ResizeObserver(() => {
|
|
2248
|
+
this.scheduleResizeRecalculate();
|
|
2249
|
+
}), this.resizeObserver.observe(this.container)), window.addEventListener("resize", this.scheduleResizeRecalculate), this.recalculateViewportLayout();
|
|
2250
|
+
}
|
|
2251
|
+
scheduleResizeRecalculate = () => {
|
|
2252
|
+
this.resizeTimer !== null && window.clearTimeout(this.resizeTimer), this.resizeTimer = window.setTimeout(() => {
|
|
2253
|
+
this.resizeTimer = null, this.recalculateViewportLayout();
|
|
2254
|
+
}, ft.RESIZE_DEBOUNCE_MS);
|
|
2255
|
+
};
|
|
2256
|
+
recalculateViewportLayout() {
|
|
2257
|
+
const t = this.gl;
|
|
2258
|
+
if (!t) return;
|
|
2259
|
+
const { width: e, height: i } = this.getViewportSize(), r = Math.min(window.devicePixelRatio, 2), o = Math.round(e * r), s = Math.round(i * r);
|
|
2260
|
+
(this.canvas.width !== o || this.canvas.height !== s) && (this.canvas.width = o, this.canvas.height = s, t.viewport(0, 0, o, s)), (this.fboWidth !== o || this.fboHeight !== s) && this.createFBO(o, s);
|
|
2261
|
+
const a = Math.max(1, Math.round(o / 2)), h = Math.max(1, Math.round(s / 2));
|
|
2262
|
+
(this.jfaWidth !== a || this.jfaHeight !== h) && this.createJFAResources(o, s);
|
|
2263
|
+
const l = e / i, u = this.config.parallaxStrength + this.config.overscanPadding;
|
|
2264
|
+
let f = 1, c = 1;
|
|
2265
|
+
l > this.videoAspect ? c = this.videoAspect / l : f = l / this.videoAspect;
|
|
2266
|
+
const d = 1 + u * 2;
|
|
2267
|
+
f /= d, c /= d, this.uvOffset = [(1 - f) / 2, (1 - c) / 2], this.uvScale = [f, c], this.interiorProgram && (t.useProgram(this.interiorProgram), t.uniform2f(this.interiorUniforms.uUvOffset, this.uvOffset[0], this.uvOffset[1]), t.uniform2f(this.interiorUniforms.uUvScale, this.uvScale[0], this.uvScale[1]));
|
|
2268
|
+
const m = 0.65;
|
|
2269
|
+
this.meshScaleX = m, this.meshScaleY = m, l > this.meshAspect ? this.meshScaleX = m * (this.meshAspect / l) : this.meshScaleY = m * (l / this.meshAspect), this.stencilProgram && (t.useProgram(this.stencilProgram), t.uniform2f(this.stencilUniforms.uMeshScale, this.meshScaleX, this.meshScaleY)), this.boundaryProgram && (t.useProgram(this.boundaryProgram), t.uniform2f(this.boundaryUniforms.uMeshScale, this.meshScaleX, this.meshScaleY), t.uniform1f(this.boundaryUniforms.uRimWidth, this.config.rimLightWidth), t.uniform2f(this.boundaryUniforms.uTexelSize, 1 / o, 1 / s)), this.chamferProgram && (t.useProgram(this.chamferProgram), t.uniform2f(this.chamferUniforms.uMeshScale, this.meshScaleX, this.meshScaleY)), this.distFieldDirty = !0;
|
|
2270
|
+
}
|
|
2271
|
+
getViewportSize() {
|
|
2272
|
+
const t = Math.max(1, Math.round(this.container.clientWidth || window.innerWidth)), e = Math.max(1, Math.round(this.container.clientHeight || window.innerHeight));
|
|
2273
|
+
return { width: t, height: e };
|
|
2274
|
+
}
|
|
2275
|
+
// -----------------------------------------------------------------------
|
|
2276
|
+
// Context loss
|
|
2277
|
+
// -----------------------------------------------------------------------
|
|
2278
|
+
handleContextLost = (t) => {
|
|
2279
|
+
t.preventDefault(), this.animationFrameHandle && (window.cancelAnimationFrame(this.animationFrameHandle), this.animationFrameHandle = 0);
|
|
2280
|
+
};
|
|
2281
|
+
handleContextRestored = () => {
|
|
2282
|
+
const t = this.canvas.getContext("webgl2", {
|
|
2283
|
+
alpha: !0,
|
|
2284
|
+
premultipliedAlpha: !0,
|
|
2285
|
+
stencil: !0
|
|
2286
|
+
});
|
|
2287
|
+
t && (this.gl = t, t.clearColor(0, 0, 0, 0), t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL, !0), this.initGPUResources(), this.playbackVideo && (this.animationFrameHandle = window.requestAnimationFrame(this.renderLoop)));
|
|
2288
|
+
};
|
|
2289
|
+
// -----------------------------------------------------------------------
|
|
2290
|
+
// Cleanup
|
|
2291
|
+
// -----------------------------------------------------------------------
|
|
2292
|
+
disposeTextures() {
|
|
2293
|
+
const t = this.gl;
|
|
2294
|
+
t && (this.videoTexture && (t.deleteTexture(this.videoTexture), this.videoTexture = null), this.depthTexture && (t.deleteTexture(this.depthTexture), this.depthTexture = null));
|
|
2295
|
+
}
|
|
2296
|
+
disposeFBO() {
|
|
2297
|
+
const t = this.gl;
|
|
2298
|
+
t && (this.interiorColorTex && (t.deleteTexture(this.interiorColorTex), this.interiorColorTex = null), this.interiorDepthTex && (t.deleteTexture(this.interiorDepthTex), this.interiorDepthTex = null), this.interiorFbo && (t.deleteFramebuffer(this.interiorFbo), this.interiorFbo = null), this.fboWidth = 0, this.fboHeight = 0);
|
|
2299
|
+
}
|
|
2300
|
+
disposeJFA() {
|
|
2301
|
+
const t = this.gl;
|
|
2302
|
+
t && (this.maskTex && (t.deleteTexture(this.maskTex), this.maskTex = null), this.maskFbo && (t.deleteFramebuffer(this.maskFbo), this.maskFbo = null), this.jfaPingTex && (t.deleteTexture(this.jfaPingTex), this.jfaPingTex = null), this.jfaPingFbo && (t.deleteFramebuffer(this.jfaPingFbo), this.jfaPingFbo = null), this.jfaPongTex && (t.deleteTexture(this.jfaPongTex), this.jfaPongTex = null), this.jfaPongFbo && (t.deleteFramebuffer(this.jfaPongFbo), this.jfaPongFbo = null), this.distTex && (t.deleteTexture(this.distTex), this.distTex = null), this.distFbo && (t.deleteFramebuffer(this.distFbo), this.distFbo = null), this.jfaWidth = 0, this.jfaHeight = 0, this.distFieldDirty = !0);
|
|
2303
|
+
}
|
|
2304
|
+
disposeStencilGeometry() {
|
|
2305
|
+
const t = this.gl;
|
|
2306
|
+
t && (this.stencilVao && (t.deleteVertexArray(this.stencilVao), this.stencilVao = null), this.maskVao && (t.deleteVertexArray(this.maskVao), this.maskVao = null), this.stencilIndexCount = 0);
|
|
2307
|
+
}
|
|
2308
|
+
disposeBoundaryGeometry() {
|
|
2309
|
+
const t = this.gl;
|
|
2310
|
+
t && (this.boundaryVao && (t.deleteVertexArray(this.boundaryVao), this.boundaryVao = null), this.boundaryVertexCount = 0);
|
|
2311
|
+
}
|
|
2312
|
+
disposeGPUResources() {
|
|
2313
|
+
const t = this.gl;
|
|
2314
|
+
t && (this.stencilProgram && (t.deleteProgram(this.stencilProgram), this.stencilProgram = null), this.maskProgram && (t.deleteProgram(this.maskProgram), this.maskProgram = null), this.jfaSeedProgram && (t.deleteProgram(this.jfaSeedProgram), this.jfaSeedProgram = null), this.jfaFloodProgram && (t.deleteProgram(this.jfaFloodProgram), this.jfaFloodProgram = null), this.jfaDistProgram && (t.deleteProgram(this.jfaDistProgram), this.jfaDistProgram = null), this.interiorProgram && (t.deleteProgram(this.interiorProgram), this.interiorProgram = null), this.compositeProgram && (t.deleteProgram(this.compositeProgram), this.compositeProgram = null), this.boundaryProgram && (t.deleteProgram(this.boundaryProgram), this.boundaryProgram = null), this.chamferProgram && (t.deleteProgram(this.chamferProgram), this.chamferProgram = null), this.quadVao && (t.deleteVertexArray(this.quadVao), this.quadVao = null), this.stencilUniforms = {}, this.maskUniforms = {}, this.jfaSeedUniforms = {}, this.jfaFloodUniforms = {}, this.jfaDistUniforms = {}, this.interiorUniforms = {}, this.compositeUniforms = {}, this.boundaryUniforms = {}, this.chamferUniforms = {});
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
async function me(n) {
|
|
2318
|
+
const t = await fetch(n);
|
|
2319
|
+
if (!t.ok)
|
|
2320
|
+
throw new Error(`Failed to fetch SVG: ${t.status} ${t.statusText}`);
|
|
2321
|
+
const e = await t.text();
|
|
2322
|
+
return pe(e);
|
|
2323
|
+
}
|
|
2324
|
+
function pe(n) {
|
|
2325
|
+
const i = new DOMParser().parseFromString(n, "image/svg+xml").querySelector("svg");
|
|
2326
|
+
if (!i)
|
|
2327
|
+
throw new Error("No <svg> element found in document.");
|
|
2328
|
+
const r = ge(i);
|
|
2329
|
+
if (r.length === 0)
|
|
2330
|
+
throw new Error("No path data found in SVG.");
|
|
2331
|
+
let o = 1 / 0, s = 1 / 0, a = -1 / 0, h = -1 / 0;
|
|
2332
|
+
for (const D of r)
|
|
2333
|
+
for (let R = 0; R < D.length; R += 2)
|
|
2334
|
+
o = Math.min(o, D[R]), a = Math.max(a, D[R]), s = Math.min(s, D[R + 1]), h = Math.max(h, D[R + 1]);
|
|
2335
|
+
const l = a - o, u = h - s, f = (o + a) / 2, c = (s + h) / 2, d = 2 / Math.max(l, u), m = l / u, g = r.map((D) => {
|
|
2336
|
+
const R = [];
|
|
2337
|
+
for (let C = 0; C < D.length; C += 2)
|
|
2338
|
+
R.push((D[C] - f) * d), R.push(-((D[C + 1] - c) * d));
|
|
2339
|
+
return R;
|
|
2340
|
+
}), U = Se(g), b = [], E = [];
|
|
2341
|
+
for (const D of U) {
|
|
2342
|
+
const { flatCoords: R, holeIndices: C } = ye(D), M = Re(R, C), H = b.length / 2;
|
|
2343
|
+
for (const I of M)
|
|
2344
|
+
E.push(I + H);
|
|
2345
|
+
for (const I of R)
|
|
2346
|
+
b.push(I);
|
|
2347
|
+
}
|
|
2348
|
+
const p = b, v = E, T = [], x = [], y = [], S = St(g);
|
|
2349
|
+
for (let D = 0; D < g.length; D++) {
|
|
2350
|
+
const R = g[D];
|
|
2351
|
+
x.push(T.length), y.push(S[D]);
|
|
2352
|
+
for (let C = 0; C < R.length; C++)
|
|
2353
|
+
T.push(R[C]);
|
|
2354
|
+
R.length >= 2 && T.push(R[0], R[1]);
|
|
2355
|
+
}
|
|
2356
|
+
let F = 1 / 0, P = 1 / 0, _ = -1 / 0, k = -1 / 0;
|
|
2357
|
+
for (let D = 0; D < p.length; D += 2)
|
|
2358
|
+
F = Math.min(F, p[D]), _ = Math.max(_, p[D]), P = Math.min(P, p[D + 1]), k = Math.max(k, p[D + 1]);
|
|
2359
|
+
return {
|
|
2360
|
+
vertices: new Float32Array(p),
|
|
2361
|
+
indices: new Uint16Array(v),
|
|
2362
|
+
edgeVertices: new Float32Array(T),
|
|
2363
|
+
contourOffsets: x,
|
|
2364
|
+
contourIsHole: y,
|
|
2365
|
+
bounds: { minX: F, maxX: _, minY: P, maxY: k },
|
|
2366
|
+
aspect: m
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
function ge(n) {
|
|
2370
|
+
const t = [];
|
|
2371
|
+
return n.querySelectorAll("path").forEach((h) => {
|
|
2372
|
+
const l = h.getAttribute("d");
|
|
2373
|
+
if (!l) return;
|
|
2374
|
+
const u = Te(l);
|
|
2375
|
+
t.push(...u);
|
|
2376
|
+
}), n.querySelectorAll("polygon").forEach((h) => {
|
|
2377
|
+
const l = h.getAttribute("points");
|
|
2378
|
+
if (!l) return;
|
|
2379
|
+
const u = vt(l);
|
|
2380
|
+
u.length >= 6 && t.push(u);
|
|
2381
|
+
}), n.querySelectorAll("polyline").forEach((h) => {
|
|
2382
|
+
const l = h.getAttribute("points");
|
|
2383
|
+
if (!l) return;
|
|
2384
|
+
const u = vt(l);
|
|
2385
|
+
u.length >= 6 && t.push(u);
|
|
2386
|
+
}), n.querySelectorAll("rect").forEach((h) => {
|
|
2387
|
+
const l = parseFloat(h.getAttribute("x") || "0"), u = parseFloat(h.getAttribute("y") || "0"), f = parseFloat(h.getAttribute("width") || "0"), c = parseFloat(h.getAttribute("height") || "0");
|
|
2388
|
+
f > 0 && c > 0 && t.push([l, u, l + f, u, l + f, u + c, l, u + c]);
|
|
2389
|
+
}), n.querySelectorAll("circle").forEach((h) => {
|
|
2390
|
+
const l = parseFloat(h.getAttribute("cx") || "0"), u = parseFloat(h.getAttribute("cy") || "0"), f = parseFloat(h.getAttribute("r") || "0");
|
|
2391
|
+
f > 0 && t.push(ve(l, u, f));
|
|
2392
|
+
}), n.querySelectorAll("ellipse").forEach((h) => {
|
|
2393
|
+
const l = parseFloat(h.getAttribute("cx") || "0"), u = parseFloat(h.getAttribute("cy") || "0"), f = parseFloat(h.getAttribute("rx") || "0"), c = parseFloat(h.getAttribute("ry") || "0");
|
|
2394
|
+
f > 0 && c > 0 && t.push(xe(l, u, f, c));
|
|
2395
|
+
}), t;
|
|
2396
|
+
}
|
|
2397
|
+
function vt(n) {
|
|
2398
|
+
const t = [], e = n.trim().split(/[\s,]+/);
|
|
2399
|
+
for (let i = 0; i < e.length - 1; i += 2) {
|
|
2400
|
+
const r = parseFloat(e[i]), o = parseFloat(e[i + 1]);
|
|
2401
|
+
Number.isFinite(r) && Number.isFinite(o) && t.push(r, o);
|
|
2402
|
+
}
|
|
2403
|
+
return t;
|
|
2404
|
+
}
|
|
2405
|
+
function ve(n, t, e, i = 64) {
|
|
2406
|
+
const r = [];
|
|
2407
|
+
for (let o = 0; o < i; o++) {
|
|
2408
|
+
const s = 2 * Math.PI * o / i;
|
|
2409
|
+
r.push(n + e * Math.cos(s), t + e * Math.sin(s));
|
|
2410
|
+
}
|
|
2411
|
+
return r;
|
|
2412
|
+
}
|
|
2413
|
+
function xe(n, t, e, i, r = 64) {
|
|
2414
|
+
const o = [];
|
|
2415
|
+
for (let s = 0; s < r; s++) {
|
|
2416
|
+
const a = 2 * Math.PI * s / r;
|
|
2417
|
+
o.push(n + e * Math.cos(a), t + i * Math.sin(a));
|
|
2418
|
+
}
|
|
2419
|
+
return o;
|
|
2420
|
+
}
|
|
2421
|
+
function Te(n) {
|
|
2422
|
+
const t = [];
|
|
2423
|
+
let e = [], i = 0, r = 0, o = 0, s = 0, a = 0, h = 0, l = "";
|
|
2424
|
+
const u = Ee(n);
|
|
2425
|
+
let f = 0;
|
|
2426
|
+
function c() {
|
|
2427
|
+
return f >= u.length ? 0 : parseFloat(u[f++]);
|
|
2428
|
+
}
|
|
2429
|
+
for (; f < u.length; ) {
|
|
2430
|
+
const d = u[f];
|
|
2431
|
+
let m;
|
|
2432
|
+
/^[a-zA-Z]$/.test(d) ? (m = d, f++) : m = l === "M" ? "L" : l === "m" ? "l" : l;
|
|
2433
|
+
const g = m === m.toLowerCase();
|
|
2434
|
+
switch (m.toUpperCase()) {
|
|
2435
|
+
case "M": {
|
|
2436
|
+
e.length > 0 && t.push(e), e = [];
|
|
2437
|
+
const b = c() + (g ? i : 0), E = c() + (g ? r : 0);
|
|
2438
|
+
i = b, r = E, o = b, s = E, e.push(i, r), a = i, h = r;
|
|
2439
|
+
break;
|
|
2440
|
+
}
|
|
2441
|
+
case "L": {
|
|
2442
|
+
i = c() + (g ? i : 0), r = c() + (g ? r : 0), e.push(i, r), a = i, h = r;
|
|
2443
|
+
break;
|
|
2444
|
+
}
|
|
2445
|
+
case "H": {
|
|
2446
|
+
i = c() + (g ? i : 0), e.push(i, r), a = i, h = r;
|
|
2447
|
+
break;
|
|
2448
|
+
}
|
|
2449
|
+
case "V": {
|
|
2450
|
+
r = c() + (g ? r : 0), e.push(i, r), a = i, h = r;
|
|
2451
|
+
break;
|
|
2452
|
+
}
|
|
2453
|
+
case "C": {
|
|
2454
|
+
const b = c() + (g ? i : 0), E = c() + (g ? r : 0), p = c() + (g ? i : 0), v = c() + (g ? r : 0), T = c() + (g ? i : 0), x = c() + (g ? r : 0);
|
|
2455
|
+
J(e, i, r, b, E, p, v, T, x), i = T, r = x, a = p, h = v;
|
|
2456
|
+
break;
|
|
2457
|
+
}
|
|
2458
|
+
case "S": {
|
|
2459
|
+
const b = 2 * i - a, E = 2 * r - h, p = c() + (g ? i : 0), v = c() + (g ? r : 0), T = c() + (g ? i : 0), x = c() + (g ? r : 0);
|
|
2460
|
+
J(e, i, r, b, E, p, v, T, x), i = T, r = x, a = p, h = v;
|
|
2461
|
+
break;
|
|
2462
|
+
}
|
|
2463
|
+
case "Q": {
|
|
2464
|
+
const b = c() + (g ? i : 0), E = c() + (g ? r : 0), p = c() + (g ? i : 0), v = c() + (g ? r : 0);
|
|
2465
|
+
xt(e, i, r, b, E, p, v), i = p, r = v, a = b, h = E;
|
|
2466
|
+
break;
|
|
2467
|
+
}
|
|
2468
|
+
case "T": {
|
|
2469
|
+
const b = 2 * i - a, E = 2 * r - h, p = c() + (g ? i : 0), v = c() + (g ? r : 0);
|
|
2470
|
+
xt(e, i, r, b, E, p, v), i = p, r = v, a = b, h = E;
|
|
2471
|
+
break;
|
|
2472
|
+
}
|
|
2473
|
+
case "A": {
|
|
2474
|
+
const b = c(), E = c(), p = c(), v = c(), T = c(), x = c() + (g ? i : 0), y = c() + (g ? r : 0);
|
|
2475
|
+
Ae(e, i, r, b, E, p, !!v, !!T, x, y), i = x, r = y, a = i, h = r;
|
|
2476
|
+
break;
|
|
2477
|
+
}
|
|
2478
|
+
case "Z": {
|
|
2479
|
+
i = o, r = s, e.length > 0 && t.push(e), e = [], a = i, h = r;
|
|
2480
|
+
break;
|
|
2481
|
+
}
|
|
2482
|
+
default:
|
|
2483
|
+
f++;
|
|
2484
|
+
break;
|
|
2485
|
+
}
|
|
2486
|
+
l = m;
|
|
2487
|
+
}
|
|
2488
|
+
return e.length >= 6 && t.push(e), t;
|
|
2489
|
+
}
|
|
2490
|
+
function Ee(n) {
|
|
2491
|
+
const t = [], e = /([a-zA-Z])|([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)/g;
|
|
2492
|
+
let i;
|
|
2493
|
+
for (; (i = e.exec(n)) !== null; )
|
|
2494
|
+
t.push(i[0]);
|
|
2495
|
+
return t;
|
|
2496
|
+
}
|
|
2497
|
+
const be = 0.5;
|
|
2498
|
+
function J(n, t, e, i, r, o, s, a, h, l = 0) {
|
|
2499
|
+
if (l > 12) {
|
|
2500
|
+
n.push(a, h);
|
|
2501
|
+
return;
|
|
2502
|
+
}
|
|
2503
|
+
const u = a - t, f = h - e, c = Math.sqrt(u * u + f * f);
|
|
2504
|
+
if (c < 1e-6) {
|
|
2505
|
+
n.push(a, h);
|
|
2506
|
+
return;
|
|
2507
|
+
}
|
|
2508
|
+
const d = Math.abs((i - a) * f - (r - h) * u) / c, m = Math.abs((o - a) * f - (s - h) * u) / c;
|
|
2509
|
+
if (d + m < be) {
|
|
2510
|
+
n.push(a, h);
|
|
2511
|
+
return;
|
|
2512
|
+
}
|
|
2513
|
+
const g = (t + i) / 2, U = (e + r) / 2, b = (i + o) / 2, E = (r + s) / 2, p = (o + a) / 2, v = (s + h) / 2, T = (g + b) / 2, x = (U + E) / 2, y = (b + p) / 2, S = (E + v) / 2, F = (T + y) / 2, P = (x + S) / 2;
|
|
2514
|
+
J(n, t, e, g, U, T, x, F, P, l + 1), J(n, F, P, y, S, p, v, a, h, l + 1);
|
|
2515
|
+
}
|
|
2516
|
+
function xt(n, t, e, i, r, o, s) {
|
|
2517
|
+
const a = t + 0.6666666666666666 * (i - t), h = e + 2 / 3 * (r - e), l = o + 2 / 3 * (i - o), u = s + 2 / 3 * (r - s);
|
|
2518
|
+
J(n, t, e, a, h, l, u, o, s);
|
|
2519
|
+
}
|
|
2520
|
+
function Ae(n, t, e, i, r, o, s, a, h, l) {
|
|
2521
|
+
if (i === 0 || r === 0) {
|
|
2522
|
+
n.push(h, l);
|
|
2523
|
+
return;
|
|
2524
|
+
}
|
|
2525
|
+
let u = Math.abs(i), f = Math.abs(r);
|
|
2526
|
+
const c = o * Math.PI / 180, d = Math.cos(c), m = Math.sin(c), g = (t - h) / 2, U = (e - l) / 2, b = d * g + m * U, E = -m * g + d * U;
|
|
2527
|
+
let p = b * b / (u * u) + E * E / (f * f);
|
|
2528
|
+
if (p > 1) {
|
|
2529
|
+
const M = Math.sqrt(p);
|
|
2530
|
+
u *= M, f *= M, p = 1;
|
|
2531
|
+
}
|
|
2532
|
+
const v = u * u, T = f * f, x = b * b, y = E * E;
|
|
2533
|
+
let S = Math.max(0, (v * T - v * y - T * x) / (v * y + T * x));
|
|
2534
|
+
S = Math.sqrt(S), s === a && (S = -S);
|
|
2535
|
+
const F = S * (u * E) / f, P = S * -(f * b) / u, _ = d * F - m * P + (t + h) / 2, k = m * F + d * P + (e + l) / 2, D = Tt(1, 0, (b - F) / u, (E - P) / f);
|
|
2536
|
+
let R = Tt(
|
|
2537
|
+
(b - F) / u,
|
|
2538
|
+
(E - P) / f,
|
|
2539
|
+
(-b - F) / u,
|
|
2540
|
+
(-E - P) / f
|
|
2541
|
+
);
|
|
2542
|
+
!a && R > 0 && (R -= 2 * Math.PI), a && R < 0 && (R += 2 * Math.PI);
|
|
2543
|
+
const C = Math.max(4, Math.ceil(Math.abs(R) / (Math.PI / 16)));
|
|
2544
|
+
for (let M = 1; M <= C; M++) {
|
|
2545
|
+
const H = D + M / C * R, I = Math.cos(H), N = Math.sin(H), j = d * u * I - m * f * N + _, et = m * u * I + d * f * N + k;
|
|
2546
|
+
n.push(j, et);
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
function Tt(n, t, e, i) {
|
|
2550
|
+
const r = n * i - t * e < 0 ? -1 : 1, o = n * e + t * i, s = Math.sqrt(n * n + t * t), a = Math.sqrt(e * e + i * i), h = o / (s * a);
|
|
2551
|
+
return r * Math.acos(Math.max(-1, Math.min(1, h)));
|
|
2552
|
+
}
|
|
2553
|
+
function ye(n) {
|
|
2554
|
+
const t = [], e = [];
|
|
2555
|
+
for (let i = 0; i < n.length; i++) {
|
|
2556
|
+
i > 0 && e.push(t.length / 2);
|
|
2557
|
+
for (const r of n[i])
|
|
2558
|
+
t.push(r);
|
|
2559
|
+
}
|
|
2560
|
+
return { flatCoords: t, holeIndices: e };
|
|
2561
|
+
}
|
|
2562
|
+
function St(n) {
|
|
2563
|
+
const t = n.length, e = n.map((r) => Math.abs(Rt(r))), i = new Array(t).fill(!1);
|
|
2564
|
+
for (let r = 0; r < t; r++) {
|
|
2565
|
+
let o = 0;
|
|
2566
|
+
const s = n[r][0], a = n[r][1];
|
|
2567
|
+
for (let h = 0; h < t; h++)
|
|
2568
|
+
r !== h && e[h] > e[r] && Dt(s, a, n[h]) && o++;
|
|
2569
|
+
i[r] = o % 2 === 1;
|
|
2570
|
+
}
|
|
2571
|
+
return i;
|
|
2572
|
+
}
|
|
2573
|
+
function Se(n) {
|
|
2574
|
+
if (n.length <= 1)
|
|
2575
|
+
return [n];
|
|
2576
|
+
const t = St(n), e = n.map((s, a) => {
|
|
2577
|
+
const h = Rt(s);
|
|
2578
|
+
return { index: a, contour: s, area: h, isOuter: !t[a] };
|
|
2579
|
+
}), i = e.filter((s) => s.isOuter), r = e.filter((s) => !s.isOuter);
|
|
2580
|
+
if (i.length === 0)
|
|
2581
|
+
return n.map((s) => [s]);
|
|
2582
|
+
const o = i.map((s) => ({
|
|
2583
|
+
outer: s.contour,
|
|
2584
|
+
holes: []
|
|
2585
|
+
}));
|
|
2586
|
+
for (const s of r) {
|
|
2587
|
+
const a = s.contour[0], h = s.contour[1];
|
|
2588
|
+
let l = -1, u = 1 / 0;
|
|
2589
|
+
for (let f = 0; f < i.length; f++)
|
|
2590
|
+
if (Dt(a, h, i[f].contour)) {
|
|
2591
|
+
const c = Math.abs(i[f].area);
|
|
2592
|
+
c < u && (u = c, l = f);
|
|
2593
|
+
}
|
|
2594
|
+
l >= 0 ? o[l].holes.push(s.contour) : o.push({ outer: s.contour, holes: [] });
|
|
2595
|
+
}
|
|
2596
|
+
return o.map((s) => [s.outer, ...s.holes]);
|
|
2597
|
+
}
|
|
2598
|
+
function Rt(n) {
|
|
2599
|
+
let t = 0;
|
|
2600
|
+
const e = n.length;
|
|
2601
|
+
for (let i = 0; i < e; i += 2) {
|
|
2602
|
+
const r = n[i], o = n[i + 1], s = n[(i + 2) % e], a = n[(i + 3) % e];
|
|
2603
|
+
t += r * a - s * o;
|
|
2604
|
+
}
|
|
2605
|
+
return t / 2;
|
|
2606
|
+
}
|
|
2607
|
+
function Dt(n, t, e) {
|
|
2608
|
+
let i = !1;
|
|
2609
|
+
const r = e.length;
|
|
2610
|
+
for (let o = 0, s = r - 2; o < r; s = o, o += 2) {
|
|
2611
|
+
const a = e[o], h = e[o + 1], l = e[s], u = e[s + 1];
|
|
2612
|
+
h > t != u > t && n < (l - a) * (t - h) / (u - h) + a && (i = !i);
|
|
2613
|
+
}
|
|
2614
|
+
return i;
|
|
2615
|
+
}
|
|
2616
|
+
function Re(n, t, e = 2) {
|
|
2617
|
+
const i = t && t.length > 0, r = i ? t[0] * e : n.length;
|
|
2618
|
+
let o = Ft(n, 0, r, e, !0);
|
|
2619
|
+
const s = [];
|
|
2620
|
+
if (!o || o.next === o.prev) return s;
|
|
2621
|
+
i && (o = we(n, t, o, e));
|
|
2622
|
+
let a = 1 / 0, h = 1 / 0, l = -1 / 0, u = -1 / 0, f = 0;
|
|
2623
|
+
if (n.length > 80 * e) {
|
|
2624
|
+
for (let c = 0; c < r; c += e) {
|
|
2625
|
+
const d = n[c], m = n[c + 1];
|
|
2626
|
+
d < a && (a = d), m < h && (h = m), d > l && (l = d), m > u && (u = m);
|
|
2627
|
+
}
|
|
2628
|
+
f = Math.max(l - a, u - h), f = f !== 0 ? 32767 / f : 0;
|
|
2629
|
+
}
|
|
2630
|
+
return K(o, s, e, a, h, f, 0), s;
|
|
2631
|
+
}
|
|
2632
|
+
function Ft(n, t, e, i, r) {
|
|
2633
|
+
let o = null;
|
|
2634
|
+
if (r === Be(n, t, e, i) > 0)
|
|
2635
|
+
for (let s = t; s < e; s += i)
|
|
2636
|
+
o = Et(s, n[s], n[s + 1], o);
|
|
2637
|
+
else
|
|
2638
|
+
for (let s = e - i; s >= t; s -= i)
|
|
2639
|
+
o = Et(s, n[s], n[s + 1], o);
|
|
2640
|
+
return o && at(o, o.next) && (tt(o), o = o.next), o ? (o.next.prev = o, o.prev.next = o, o.next) : null;
|
|
2641
|
+
}
|
|
2642
|
+
function X(n, t) {
|
|
2643
|
+
t || (t = n);
|
|
2644
|
+
let e = n, i;
|
|
2645
|
+
do
|
|
2646
|
+
if (i = !1, !e.steiner && (at(e, e.next) || w(e.prev, e, e.next) === 0)) {
|
|
2647
|
+
if (tt(e), e = t = e.prev, e === e.next) break;
|
|
2648
|
+
i = !0;
|
|
2649
|
+
} else
|
|
2650
|
+
e = e.next;
|
|
2651
|
+
while (i || e !== t);
|
|
2652
|
+
return t;
|
|
2653
|
+
}
|
|
2654
|
+
function K(n, t, e, i, r, o, s) {
|
|
2655
|
+
if (!n) return;
|
|
2656
|
+
!s && o && _e(n, i, r, o);
|
|
2657
|
+
let a = n, h, l;
|
|
2658
|
+
for (; n.prev !== n.next; ) {
|
|
2659
|
+
if (h = n.prev, l = n.next, o ? Fe(n, i, r, o) : De(n)) {
|
|
2660
|
+
t.push(h.i / e, n.i / e, l.i / e), tt(n), n = l.next, a = l.next;
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
if (n = l, n === a) {
|
|
2664
|
+
s ? s === 1 ? (n = Ue(X(n), t, e), K(n, t, e, i, r, o, 2)) : s === 2 && Pe(n, t, e, i, r, o) : K(X(n), t, e, i, r, o, 1);
|
|
2665
|
+
break;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
function De(n) {
|
|
2670
|
+
const t = n.prev, e = n, i = n.next;
|
|
2671
|
+
if (w(t, e, i) >= 0) return !1;
|
|
2672
|
+
const r = t.x, o = e.x, s = i.x, a = t.y, h = e.y, l = i.y, u = r < o ? r < s ? r : s : o < s ? o : s, f = a < h ? a < l ? a : l : h < l ? h : l, c = r > o ? r > s ? r : s : o > s ? o : s, d = a > h ? a > l ? a : l : h > l ? h : l;
|
|
2673
|
+
let m = i.next;
|
|
2674
|
+
for (; m !== t; ) {
|
|
2675
|
+
if (m.x >= u && m.x <= c && m.y >= f && m.y <= d && G(r, a, o, h, s, l, m.x, m.y) && w(m.prev, m, m.next) >= 0)
|
|
2676
|
+
return !1;
|
|
2677
|
+
m = m.next;
|
|
2678
|
+
}
|
|
2679
|
+
return !0;
|
|
2680
|
+
}
|
|
2681
|
+
function Fe(n, t, e, i) {
|
|
2682
|
+
const r = n.prev, o = n, s = n.next;
|
|
2683
|
+
if (w(r, o, s) >= 0) return !1;
|
|
2684
|
+
const a = r.x, h = o.x, l = s.x, u = r.y, f = o.y, c = s.y, d = a < h ? a < l ? a : l : h < l ? h : l, m = u < f ? u < c ? u : c : f < c ? f : c, g = a > h ? a > l ? a : l : h > l ? h : l, U = u > f ? u > c ? u : c : f > c ? f : c, b = ct(d, m, t, e, i), E = ct(g, U, t, e, i);
|
|
2685
|
+
let p = n.prevZ, v = n.nextZ;
|
|
2686
|
+
for (; p && p.z >= b && v && v.z <= E; ) {
|
|
2687
|
+
if (p.x >= d && p.x <= g && p.y >= m && p.y <= U && p !== r && p !== s && G(a, u, h, f, l, c, p.x, p.y) && w(p.prev, p, p.next) >= 0 || (p = p.prevZ, v.x >= d && v.x <= g && v.y >= m && v.y <= U && v !== r && v !== s && G(a, u, h, f, l, c, v.x, v.y) && w(v.prev, v, v.next) >= 0)) return !1;
|
|
2688
|
+
v = v.nextZ;
|
|
2689
|
+
}
|
|
2690
|
+
for (; p && p.z >= b; ) {
|
|
2691
|
+
if (p.x >= d && p.x <= g && p.y >= m && p.y <= U && p !== r && p !== s && G(a, u, h, f, l, c, p.x, p.y) && w(p.prev, p, p.next) >= 0) return !1;
|
|
2692
|
+
p = p.prevZ;
|
|
2693
|
+
}
|
|
2694
|
+
for (; v && v.z <= E; ) {
|
|
2695
|
+
if (v.x >= d && v.x <= g && v.y >= m && v.y <= U && v !== r && v !== s && G(a, u, h, f, l, c, v.x, v.y) && w(v.prev, v, v.next) >= 0) return !1;
|
|
2696
|
+
v = v.nextZ;
|
|
2697
|
+
}
|
|
2698
|
+
return !0;
|
|
2699
|
+
}
|
|
2700
|
+
function Ue(n, t, e) {
|
|
2701
|
+
let i = n;
|
|
2702
|
+
do {
|
|
2703
|
+
const r = i.prev, o = i.next.next;
|
|
2704
|
+
!at(r, o) && Ut(r, i, i.next, o) && Q(r, o) && Q(o, r) && (t.push(r.i / e, i.i / e, o.i / e), tt(i), tt(i.next), i = n = o), i = i.next;
|
|
2705
|
+
} while (i !== n);
|
|
2706
|
+
return X(i);
|
|
2707
|
+
}
|
|
2708
|
+
function Pe(n, t, e, i, r, o) {
|
|
2709
|
+
let s = n;
|
|
2710
|
+
do {
|
|
2711
|
+
let a = s.next.next;
|
|
2712
|
+
for (; a !== s.prev; ) {
|
|
2713
|
+
if (s.i !== a.i && Oe(s, a)) {
|
|
2714
|
+
let h = Pt(s, a);
|
|
2715
|
+
s = X(s, s.next), h = X(h, h.next), K(s, t, e, i, r, o, 0), K(h, t, e, i, r, o, 0);
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
a = a.next;
|
|
2719
|
+
}
|
|
2720
|
+
s = s.next;
|
|
2721
|
+
} while (s !== n);
|
|
2722
|
+
}
|
|
2723
|
+
function we(n, t, e, i) {
|
|
2724
|
+
const r = [];
|
|
2725
|
+
for (let o = 0; o < t.length; o++) {
|
|
2726
|
+
const s = t[o] * i, a = o < t.length - 1 ? t[o + 1] * i : n.length, h = Ft(n, s, a, i, !1);
|
|
2727
|
+
h && (h === h.next && (h.steiner = !0), r.push(Ve(h)));
|
|
2728
|
+
}
|
|
2729
|
+
r.sort((o, s) => o.x - s.x);
|
|
2730
|
+
for (const o of r)
|
|
2731
|
+
e = Le(o, e);
|
|
2732
|
+
return e;
|
|
2733
|
+
}
|
|
2734
|
+
function Le(n, t) {
|
|
2735
|
+
const e = Ce(n, t);
|
|
2736
|
+
if (!e) return t;
|
|
2737
|
+
const i = Pt(e, n);
|
|
2738
|
+
return X(i, i.next), X(e, e.next);
|
|
2739
|
+
}
|
|
2740
|
+
function Ce(n, t) {
|
|
2741
|
+
let e = t;
|
|
2742
|
+
const i = n.x, r = n.y;
|
|
2743
|
+
let o = -1 / 0, s = null;
|
|
2744
|
+
do {
|
|
2745
|
+
if (r <= e.y && r >= e.next.y && e.next.y !== e.y) {
|
|
2746
|
+
const f = e.x + (r - e.y) / (e.next.y - e.y) * (e.next.x - e.x);
|
|
2747
|
+
if (f <= i && f > o && (o = f, s = e.x < e.next.x ? e : e.next, f === i))
|
|
2748
|
+
return s;
|
|
2749
|
+
}
|
|
2750
|
+
e = e.next;
|
|
2751
|
+
} while (e !== t);
|
|
2752
|
+
if (!s) return null;
|
|
2753
|
+
const a = s, h = s.x, l = s.y;
|
|
2754
|
+
let u = 1 / 0;
|
|
2755
|
+
e = s;
|
|
2756
|
+
do {
|
|
2757
|
+
if (i >= e.x && e.x >= h && i !== e.x && G(r < l ? i : o, r, h, l, r < l ? o : i, r, e.x, e.y)) {
|
|
2758
|
+
const f = Math.abs(r - e.y) / (i - e.x);
|
|
2759
|
+
Q(e, n) && (f < u || f === u && (e.x > s.x || Me(s, e))) && (s = e, u = f);
|
|
2760
|
+
}
|
|
2761
|
+
e = e.next;
|
|
2762
|
+
} while (e !== a);
|
|
2763
|
+
return s;
|
|
2764
|
+
}
|
|
2765
|
+
function Me(n, t) {
|
|
2766
|
+
return w(n.prev, n, t.prev) < 0 && w(t.next, n, n.next) < 0;
|
|
2767
|
+
}
|
|
2768
|
+
function _e(n, t, e, i) {
|
|
2769
|
+
let r = n;
|
|
2770
|
+
do
|
|
2771
|
+
r.z === 0 && (r.z = ct(r.x, r.y, t, e, i)), r.prevZ = r.prev, r.nextZ = r.next, r = r.next;
|
|
2772
|
+
while (r !== n);
|
|
2773
|
+
r.prevZ.nextZ = null, r.prevZ = null, Ie(r);
|
|
2774
|
+
}
|
|
2775
|
+
function Ie(n) {
|
|
2776
|
+
let t = 1, e;
|
|
2777
|
+
do {
|
|
2778
|
+
let i = n;
|
|
2779
|
+
n = null;
|
|
2780
|
+
let r = null;
|
|
2781
|
+
for (e = 0; i; ) {
|
|
2782
|
+
e++;
|
|
2783
|
+
let o = i, s = 0;
|
|
2784
|
+
for (let h = 0; h < t && (s++, o = o.nextZ, !!o); h++)
|
|
2785
|
+
;
|
|
2786
|
+
let a = t;
|
|
2787
|
+
for (; s > 0 || a > 0 && o; ) {
|
|
2788
|
+
let h;
|
|
2789
|
+
s !== 0 && (a === 0 || !o || i.z <= o.z) ? (h = i, i = i.nextZ, s--) : (h = o, o = o.nextZ, a--), r ? r.nextZ = h : n = h, h.prevZ = r, r = h;
|
|
2790
|
+
}
|
|
2791
|
+
i = o;
|
|
2792
|
+
}
|
|
2793
|
+
r.nextZ = null, t *= 2;
|
|
2794
|
+
} while (e > 1);
|
|
2795
|
+
return n;
|
|
2796
|
+
}
|
|
2797
|
+
function ct(n, t, e, i, r) {
|
|
2798
|
+
let o = (n - e) * r | 0, s = (t - i) * r | 0;
|
|
2799
|
+
return o = (o | o << 8) & 16711935, o = (o | o << 4) & 252645135, o = (o | o << 2) & 858993459, o = (o | o << 1) & 1431655765, s = (s | s << 8) & 16711935, s = (s | s << 4) & 252645135, s = (s | s << 2) & 858993459, s = (s | s << 1) & 1431655765, o | s << 1;
|
|
2800
|
+
}
|
|
2801
|
+
function Ve(n) {
|
|
2802
|
+
let t = n, e = n;
|
|
2803
|
+
do
|
|
2804
|
+
(t.x < e.x || t.x === e.x && t.y < e.y) && (e = t), t = t.next;
|
|
2805
|
+
while (t !== n);
|
|
2806
|
+
return e;
|
|
2807
|
+
}
|
|
2808
|
+
function G(n, t, e, i, r, o, s, a) {
|
|
2809
|
+
return (r - s) * (t - a) - (n - s) * (o - a) >= 0 && (n - s) * (i - a) - (e - s) * (t - a) >= 0 && (e - s) * (o - a) - (r - s) * (i - a) >= 0;
|
|
2810
|
+
}
|
|
2811
|
+
function Oe(n, t) {
|
|
2812
|
+
return n.next.i !== t.i && n.prev.i !== t.i && !ke(n, t) && (Q(n, t) && Q(t, n) && He(n, t) && (w(n.prev, n, t.prev) !== 0 || w(n, t.prev, t) !== 0) || at(n, t) && w(n.prev, n, n.next) > 0 && w(t.prev, t, t.next) > 0);
|
|
2813
|
+
}
|
|
2814
|
+
function w(n, t, e) {
|
|
2815
|
+
return (t.y - n.y) * (e.x - t.x) - (t.x - n.x) * (e.y - t.y);
|
|
2816
|
+
}
|
|
2817
|
+
function at(n, t) {
|
|
2818
|
+
return n.x === t.x && n.y === t.y;
|
|
2819
|
+
}
|
|
2820
|
+
function Ut(n, t, e, i) {
|
|
2821
|
+
const r = ot(w(n, t, e)), o = ot(w(n, t, i)), s = ot(w(e, i, n)), a = ot(w(e, i, t));
|
|
2822
|
+
return !!(r !== o && s !== a || r === 0 && rt(n, e, t) || o === 0 && rt(n, i, t) || s === 0 && rt(e, n, i) || a === 0 && rt(e, t, i));
|
|
2823
|
+
}
|
|
2824
|
+
function rt(n, t, e) {
|
|
2825
|
+
return t.x <= Math.max(n.x, e.x) && t.x >= Math.min(n.x, e.x) && t.y <= Math.max(n.y, e.y) && t.y >= Math.min(n.y, e.y);
|
|
2826
|
+
}
|
|
2827
|
+
function ot(n) {
|
|
2828
|
+
return n > 0 ? 1 : n < 0 ? -1 : 0;
|
|
2829
|
+
}
|
|
2830
|
+
function ke(n, t) {
|
|
2831
|
+
let e = n;
|
|
2832
|
+
do {
|
|
2833
|
+
if (e.i !== n.i && e.next.i !== n.i && e.i !== t.i && e.next.i !== t.i && Ut(e, e.next, n, t)) return !0;
|
|
2834
|
+
e = e.next;
|
|
2835
|
+
} while (e !== n);
|
|
2836
|
+
return !1;
|
|
2837
|
+
}
|
|
2838
|
+
function Q(n, t) {
|
|
2839
|
+
return w(n.prev, n, n.next) < 0 ? w(n, t, n.next) >= 0 && w(n, n.prev, t) >= 0 : w(n, t, n.prev) < 0 || w(n, n.next, t) < 0;
|
|
2840
|
+
}
|
|
2841
|
+
function He(n, t) {
|
|
2842
|
+
let e = n, i = !1;
|
|
2843
|
+
const r = (n.x + t.x) / 2, o = (n.y + t.y) / 2;
|
|
2844
|
+
do
|
|
2845
|
+
e.y > o != e.next.y > o && e.next.y !== e.y && r < (e.next.x - e.x) * (o - e.y) / (e.next.y - e.y) + e.x && (i = !i), e = e.next;
|
|
2846
|
+
while (e !== n);
|
|
2847
|
+
return i;
|
|
2848
|
+
}
|
|
2849
|
+
function Pt(n, t) {
|
|
2850
|
+
const e = ut(n.i, n.x, n.y), i = ut(t.i, t.x, t.y), r = n.next, o = t.prev;
|
|
2851
|
+
return n.next = t, t.prev = n, e.next = r, r.prev = e, i.next = e, e.prev = i, o.next = i, i.prev = o, i;
|
|
2852
|
+
}
|
|
2853
|
+
function Et(n, t, e, i) {
|
|
2854
|
+
const r = ut(n, t, e);
|
|
2855
|
+
return i ? (r.next = i.next, r.prev = i, i.next.prev = r, i.next = r) : (r.prev = r, r.next = r), r;
|
|
2856
|
+
}
|
|
2857
|
+
function tt(n) {
|
|
2858
|
+
n.next.prev = n.prev, n.prev.next = n.next, n.prevZ && (n.prevZ.nextZ = n.nextZ), n.nextZ && (n.nextZ.prevZ = n.prevZ);
|
|
2859
|
+
}
|
|
2860
|
+
function ut(n, t, e) {
|
|
2861
|
+
return {
|
|
2862
|
+
i: n,
|
|
2863
|
+
x: t,
|
|
2864
|
+
y: e,
|
|
2865
|
+
prev: null,
|
|
2866
|
+
next: null,
|
|
2867
|
+
z: 0,
|
|
2868
|
+
prevZ: null,
|
|
2869
|
+
nextZ: null,
|
|
2870
|
+
steiner: !1
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
function Be(n, t, e, i) {
|
|
2874
|
+
let r = 0;
|
|
2875
|
+
for (let o = t, s = e - i; o < e; o += i)
|
|
2876
|
+
r += (n[s] - n[o]) * (n[o + 1] + n[s + 1]), s = o;
|
|
2877
|
+
return r;
|
|
2878
|
+
}
|
|
2879
|
+
const A = {
|
|
2880
|
+
parallaxX: 0.4,
|
|
2881
|
+
parallaxY: 0.8,
|
|
2882
|
+
parallaxMax: 30,
|
|
2883
|
+
overscan: 0.06,
|
|
2884
|
+
pomSteps: 16,
|
|
2885
|
+
// Boundary effects
|
|
2886
|
+
rimIntensity: 0.6,
|
|
2887
|
+
rimColor: "#ffffff",
|
|
2888
|
+
rimWidth: 0.025,
|
|
2889
|
+
refractionStrength: 0.015,
|
|
2890
|
+
chromaticStrength: 8e-3,
|
|
2891
|
+
occlusionIntensity: 0.4,
|
|
2892
|
+
// Lens transform
|
|
2893
|
+
depthPower: 0.7,
|
|
2894
|
+
// < 1 = wide-angle, exaggerated foreground
|
|
2895
|
+
depthScale: 1.2,
|
|
2896
|
+
// expand depth range beyond 1.0
|
|
2897
|
+
depthBias: -0.05,
|
|
2898
|
+
// slight bias toward near
|
|
2899
|
+
// Interior mood
|
|
2900
|
+
fogDensity: 0.15,
|
|
2901
|
+
fogColor: "#1a1a2e",
|
|
2902
|
+
colorShift: 0.6,
|
|
2903
|
+
brightnessBias: 0.05,
|
|
2904
|
+
// Depth-adaptive
|
|
2905
|
+
contrastLow: 0.02,
|
|
2906
|
+
contrastHigh: 0.98,
|
|
2907
|
+
verticalReduction: 0.5,
|
|
2908
|
+
dofStart: 0.5,
|
|
2909
|
+
dofStrength: 0.5,
|
|
2910
|
+
// Bevel / dimensional typography
|
|
2911
|
+
bevelIntensity: 0.5,
|
|
2912
|
+
bevelWidth: 0.04,
|
|
2913
|
+
bevelDarkening: 0.2,
|
|
2914
|
+
bevelDesaturation: 0.12,
|
|
2915
|
+
bevelLightAngle: 135,
|
|
2916
|
+
// Volumetric edge wall
|
|
2917
|
+
edgeThickness: 0.01,
|
|
2918
|
+
edgeSpecular: 0.35,
|
|
2919
|
+
edgeColor: "#a0a0a0",
|
|
2920
|
+
// Chamfer geometry
|
|
2921
|
+
chamferWidth: 0.025,
|
|
2922
|
+
chamferAngle: 45,
|
|
2923
|
+
chamferColor: "#262630",
|
|
2924
|
+
chamferAmbient: 0.12,
|
|
2925
|
+
chamferSpecular: 0.3,
|
|
2926
|
+
chamferShininess: 24,
|
|
2927
|
+
// Edge occlusion (emissive interior)
|
|
2928
|
+
edgeOcclusionWidth: 0.03,
|
|
2929
|
+
edgeOcclusionStrength: 0.2,
|
|
2930
|
+
lightDirection: "-0.5,0.7,-0.3",
|
|
2931
|
+
autoplay: !0,
|
|
2932
|
+
loop: !0,
|
|
2933
|
+
muted: !0
|
|
2934
|
+
};
|
|
2935
|
+
class dt {
|
|
2936
|
+
constructor(t, e = 0.08, i = 0.06) {
|
|
2937
|
+
this.host = t, this.lerpFactor = e, this.motionLerpFactor = i, this.host.addEventListener("mousemove", this.handleMouseMove), this.host.addEventListener("mouseleave", this.resetPointerTarget), this.host.addEventListener("touchstart", this.handleTouchStart, { passive: !0 }), this.host.addEventListener("touchmove", this.handleTouchMove, { passive: !0 }), this.host.addEventListener("touchend", this.handleTouchEnd, { passive: !0 }), this.host.addEventListener("touchcancel", this.handleTouchEnd, { passive: !0 });
|
|
2938
|
+
}
|
|
2939
|
+
pointerTarget = { x: 0, y: 0 };
|
|
2940
|
+
motionTarget = { x: 0, y: 0 };
|
|
2941
|
+
smoothedOutput = { x: 0, y: 0 };
|
|
2942
|
+
usingMotionInput = !1;
|
|
2943
|
+
motionListenerAttached = !1;
|
|
2944
|
+
motionRequested = !1;
|
|
2945
|
+
touchActive = !1;
|
|
2946
|
+
touchAnchorX = 0;
|
|
2947
|
+
touchAnchorY = 0;
|
|
2948
|
+
lerpFactor;
|
|
2949
|
+
motionLerpFactor;
|
|
2950
|
+
static TOUCH_DRAG_RANGE = 100;
|
|
2951
|
+
update() {
|
|
2952
|
+
const t = this.touchActive ? this.pointerTarget : this.usingMotionInput ? this.motionTarget : this.pointerTarget, e = this.usingMotionInput && !this.touchActive ? this.motionLerpFactor : this.lerpFactor;
|
|
2953
|
+
return this.smoothedOutput.x = nt(this.smoothedOutput.x, t.x, e), this.smoothedOutput.y = nt(this.smoothedOutput.y, t.y, e), this.smoothedOutput;
|
|
2954
|
+
}
|
|
2955
|
+
dispose() {
|
|
2956
|
+
this.host.removeEventListener("mousemove", this.handleMouseMove), this.host.removeEventListener("mouseleave", this.resetPointerTarget), this.host.removeEventListener("touchstart", this.handleTouchStart), this.host.removeEventListener("touchmove", this.handleTouchMove), this.host.removeEventListener("touchend", this.handleTouchEnd), this.host.removeEventListener("touchcancel", this.handleTouchEnd), this.motionListenerAttached && (window.removeEventListener("deviceorientation", this.handleDeviceOrientation), this.motionListenerAttached = !1);
|
|
2957
|
+
}
|
|
2958
|
+
handleMouseMove = (t) => {
|
|
2959
|
+
const e = this.host.getBoundingClientRect(), i = (t.clientX - e.left) / e.width * 2 - 1, r = (t.clientY - e.top) / e.height * 2 - 1;
|
|
2960
|
+
this.pointerTarget.x = W(i, -1, 1), this.pointerTarget.y = W(r, -1, 1);
|
|
2961
|
+
};
|
|
2962
|
+
resetPointerTarget = () => {
|
|
2963
|
+
this.pointerTarget.x = 0, this.pointerTarget.y = 0;
|
|
2964
|
+
};
|
|
2965
|
+
handleTouchStart = (t) => {
|
|
2966
|
+
const e = t.touches[0];
|
|
2967
|
+
e && (this.touchActive = !0, this.touchAnchorX = e.clientX, this.touchAnchorY = e.clientY, this.pointerTarget.x = 0, this.pointerTarget.y = 0, this.motionRequested || (this.motionRequested = !0, this.requestMotionPermission()));
|
|
2968
|
+
};
|
|
2969
|
+
handleTouchMove = (t) => {
|
|
2970
|
+
const e = t.touches[0];
|
|
2971
|
+
if (!e) return;
|
|
2972
|
+
const i = e.clientX - this.touchAnchorX, r = e.clientY - this.touchAnchorY, o = dt.TOUCH_DRAG_RANGE;
|
|
2973
|
+
this.pointerTarget.x = W(i / o, -1, 1), this.pointerTarget.y = W(r / o, -1, 1);
|
|
2974
|
+
};
|
|
2975
|
+
handleTouchEnd = () => {
|
|
2976
|
+
this.touchActive = !1, this.pointerTarget.x = 0, this.pointerTarget.y = 0;
|
|
2977
|
+
};
|
|
2978
|
+
async requestMotionPermission() {
|
|
2979
|
+
if (typeof DeviceOrientationEvent > "u") return;
|
|
2980
|
+
const t = DeviceOrientationEvent;
|
|
2981
|
+
if (typeof t.requestPermission == "function")
|
|
2982
|
+
try {
|
|
2983
|
+
if (await t.requestPermission() !== "granted") return;
|
|
2984
|
+
} catch {
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
this.motionListenerAttached || (window.addEventListener("deviceorientation", this.handleDeviceOrientation), this.motionListenerAttached = !0), this.usingMotionInput = !0;
|
|
2988
|
+
}
|
|
2989
|
+
handleDeviceOrientation = (t) => {
|
|
2990
|
+
const e = W((t.gamma ?? 0) / 45, -1, 1), i = W((t.beta ?? 0) / 45, -1, 1);
|
|
2991
|
+
this.motionTarget.x = nt(this.motionTarget.x, e, this.motionLerpFactor), this.motionTarget.y = nt(this.motionTarget.y, i, this.motionLerpFactor);
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
class lt extends HTMLElement {
|
|
2995
|
+
static TAG_NAME = "layershift-portal";
|
|
2996
|
+
static get observedAttributes() {
|
|
2997
|
+
return [
|
|
2998
|
+
"src",
|
|
2999
|
+
"depth-src",
|
|
3000
|
+
"depth-meta",
|
|
3001
|
+
"logo-src",
|
|
3002
|
+
"parallax-x",
|
|
3003
|
+
"parallax-y",
|
|
3004
|
+
"parallax-max",
|
|
3005
|
+
"overscan",
|
|
3006
|
+
"pom-steps",
|
|
3007
|
+
"rim-intensity",
|
|
3008
|
+
"rim-color",
|
|
3009
|
+
"rim-width",
|
|
3010
|
+
"refraction-strength",
|
|
3011
|
+
"chromatic-strength",
|
|
3012
|
+
"occlusion-intensity",
|
|
3013
|
+
"depth-power",
|
|
3014
|
+
"depth-scale",
|
|
3015
|
+
"depth-bias",
|
|
3016
|
+
"fog-density",
|
|
3017
|
+
"fog-color",
|
|
3018
|
+
"color-shift",
|
|
3019
|
+
"brightness-bias",
|
|
3020
|
+
"contrast-low",
|
|
3021
|
+
"contrast-high",
|
|
3022
|
+
"vertical-reduction",
|
|
3023
|
+
"dof-start",
|
|
3024
|
+
"dof-strength",
|
|
3025
|
+
"bevel-intensity",
|
|
3026
|
+
"bevel-width",
|
|
3027
|
+
"bevel-darkening",
|
|
3028
|
+
"bevel-desaturation",
|
|
3029
|
+
"bevel-light-angle",
|
|
3030
|
+
"edge-thickness",
|
|
3031
|
+
"edge-specular",
|
|
3032
|
+
"edge-color",
|
|
3033
|
+
"chamfer-width",
|
|
3034
|
+
"chamfer-angle",
|
|
3035
|
+
"chamfer-color",
|
|
3036
|
+
"chamfer-ambient",
|
|
3037
|
+
"chamfer-specular",
|
|
3038
|
+
"chamfer-shininess",
|
|
3039
|
+
"edge-occlusion-width",
|
|
3040
|
+
"edge-occlusion-strength",
|
|
3041
|
+
"light-direction",
|
|
3042
|
+
"autoplay",
|
|
3043
|
+
"loop",
|
|
3044
|
+
"muted"
|
|
3045
|
+
];
|
|
3046
|
+
}
|
|
3047
|
+
shadow;
|
|
3048
|
+
container = null;
|
|
3049
|
+
renderer = null;
|
|
3050
|
+
inputHandler = null;
|
|
3051
|
+
depthWorker = null;
|
|
3052
|
+
video = null;
|
|
3053
|
+
mesh = null;
|
|
3054
|
+
initialized = !1;
|
|
3055
|
+
abortController = null;
|
|
3056
|
+
loopCount = 0;
|
|
3057
|
+
constructor() {
|
|
3058
|
+
super(), this.shadow = this.attachShadow({ mode: "open" });
|
|
3059
|
+
}
|
|
3060
|
+
// --- Attribute helpers ---
|
|
3061
|
+
getAttrFloat(t, e) {
|
|
3062
|
+
const i = this.getAttribute(t);
|
|
3063
|
+
if (i === null) return e;
|
|
3064
|
+
const r = parseFloat(i);
|
|
3065
|
+
return Number.isFinite(r) ? r : e;
|
|
3066
|
+
}
|
|
3067
|
+
getAttrBool(t, e) {
|
|
3068
|
+
if (!this.hasAttribute(t)) return e;
|
|
3069
|
+
const i = this.getAttribute(t);
|
|
3070
|
+
return !(i === "false" || i === "0");
|
|
3071
|
+
}
|
|
3072
|
+
getAttrColor(t, e) {
|
|
3073
|
+
const i = this.getAttribute(t) ?? e;
|
|
3074
|
+
return Xe(i);
|
|
3075
|
+
}
|
|
3076
|
+
getAttrVec3(t, e) {
|
|
3077
|
+
const r = (this.getAttribute(t) ?? e).split(",").map((s) => parseFloat(s.trim()));
|
|
3078
|
+
if (r.length >= 3 && r.every(Number.isFinite))
|
|
3079
|
+
return [r[0], r[1], r[2]];
|
|
3080
|
+
const o = e.split(",").map((s) => parseFloat(s.trim()));
|
|
3081
|
+
return [o[0], o[1], o[2]];
|
|
3082
|
+
}
|
|
3083
|
+
get parallaxX() {
|
|
3084
|
+
return this.getAttrFloat("parallax-x", A.parallaxX);
|
|
3085
|
+
}
|
|
3086
|
+
get parallaxY() {
|
|
3087
|
+
return this.getAttrFloat("parallax-y", A.parallaxY);
|
|
3088
|
+
}
|
|
3089
|
+
get parallaxMax() {
|
|
3090
|
+
return this.getAttrFloat("parallax-max", A.parallaxMax);
|
|
3091
|
+
}
|
|
3092
|
+
get overscan() {
|
|
3093
|
+
return this.getAttrFloat("overscan", A.overscan);
|
|
3094
|
+
}
|
|
3095
|
+
get pomSteps() {
|
|
3096
|
+
return this.getAttrFloat("pom-steps", A.pomSteps);
|
|
3097
|
+
}
|
|
3098
|
+
// Boundary
|
|
3099
|
+
get rimIntensity() {
|
|
3100
|
+
return this.getAttrFloat("rim-intensity", A.rimIntensity);
|
|
3101
|
+
}
|
|
3102
|
+
get rimWidth() {
|
|
3103
|
+
return this.getAttrFloat("rim-width", A.rimWidth);
|
|
3104
|
+
}
|
|
3105
|
+
get rimColor() {
|
|
3106
|
+
return this.getAttrColor("rim-color", A.rimColor);
|
|
3107
|
+
}
|
|
3108
|
+
get refractionStrength() {
|
|
3109
|
+
return this.getAttrFloat("refraction-strength", A.refractionStrength);
|
|
3110
|
+
}
|
|
3111
|
+
get chromaticStrength() {
|
|
3112
|
+
return this.getAttrFloat("chromatic-strength", A.chromaticStrength);
|
|
3113
|
+
}
|
|
3114
|
+
get occlusionIntensity() {
|
|
3115
|
+
return this.getAttrFloat("occlusion-intensity", A.occlusionIntensity);
|
|
3116
|
+
}
|
|
3117
|
+
// Lens transform
|
|
3118
|
+
get depthPower() {
|
|
3119
|
+
return this.getAttrFloat("depth-power", A.depthPower);
|
|
3120
|
+
}
|
|
3121
|
+
get depthScale() {
|
|
3122
|
+
return this.getAttrFloat("depth-scale", A.depthScale);
|
|
3123
|
+
}
|
|
3124
|
+
get depthBias() {
|
|
3125
|
+
return this.getAttrFloat("depth-bias", A.depthBias);
|
|
3126
|
+
}
|
|
3127
|
+
// Interior mood
|
|
3128
|
+
get fogDensity() {
|
|
3129
|
+
return this.getAttrFloat("fog-density", A.fogDensity);
|
|
3130
|
+
}
|
|
3131
|
+
get fogColor() {
|
|
3132
|
+
return this.getAttrColor("fog-color", A.fogColor);
|
|
3133
|
+
}
|
|
3134
|
+
get colorShift() {
|
|
3135
|
+
return this.getAttrFloat("color-shift", A.colorShift);
|
|
3136
|
+
}
|
|
3137
|
+
get brightnessBias() {
|
|
3138
|
+
return this.getAttrFloat("brightness-bias", A.brightnessBias);
|
|
3139
|
+
}
|
|
3140
|
+
// Depth-adaptive
|
|
3141
|
+
get contrastLow() {
|
|
3142
|
+
return this.getAttrFloat("contrast-low", A.contrastLow);
|
|
3143
|
+
}
|
|
3144
|
+
get contrastHigh() {
|
|
3145
|
+
return this.getAttrFloat("contrast-high", A.contrastHigh);
|
|
3146
|
+
}
|
|
3147
|
+
get verticalReduction() {
|
|
3148
|
+
return this.getAttrFloat("vertical-reduction", A.verticalReduction);
|
|
3149
|
+
}
|
|
3150
|
+
get dofStart() {
|
|
3151
|
+
return this.getAttrFloat("dof-start", A.dofStart);
|
|
3152
|
+
}
|
|
3153
|
+
get dofStrength() {
|
|
3154
|
+
return this.getAttrFloat("dof-strength", A.dofStrength);
|
|
3155
|
+
}
|
|
3156
|
+
// Bevel / dimensional typography
|
|
3157
|
+
get bevelIntensity() {
|
|
3158
|
+
return this.getAttrFloat("bevel-intensity", A.bevelIntensity);
|
|
3159
|
+
}
|
|
3160
|
+
get bevelWidth() {
|
|
3161
|
+
return this.getAttrFloat("bevel-width", A.bevelWidth);
|
|
3162
|
+
}
|
|
3163
|
+
get bevelDarkening() {
|
|
3164
|
+
return this.getAttrFloat("bevel-darkening", A.bevelDarkening);
|
|
3165
|
+
}
|
|
3166
|
+
get bevelDesaturation() {
|
|
3167
|
+
return this.getAttrFloat("bevel-desaturation", A.bevelDesaturation);
|
|
3168
|
+
}
|
|
3169
|
+
get bevelLightAngle() {
|
|
3170
|
+
return this.getAttrFloat("bevel-light-angle", A.bevelLightAngle);
|
|
3171
|
+
}
|
|
3172
|
+
// Volumetric edge wall
|
|
3173
|
+
get edgeThickness() {
|
|
3174
|
+
return this.getAttrFloat("edge-thickness", A.edgeThickness);
|
|
3175
|
+
}
|
|
3176
|
+
get edgeSpecular() {
|
|
3177
|
+
return this.getAttrFloat("edge-specular", A.edgeSpecular);
|
|
3178
|
+
}
|
|
3179
|
+
get edgeColor() {
|
|
3180
|
+
return this.getAttrColor("edge-color", A.edgeColor);
|
|
3181
|
+
}
|
|
3182
|
+
// Chamfer geometry
|
|
3183
|
+
get chamferWidth() {
|
|
3184
|
+
return this.getAttrFloat("chamfer-width", A.chamferWidth);
|
|
3185
|
+
}
|
|
3186
|
+
get chamferAngle() {
|
|
3187
|
+
return this.getAttrFloat("chamfer-angle", A.chamferAngle);
|
|
3188
|
+
}
|
|
3189
|
+
get chamferColor() {
|
|
3190
|
+
return this.getAttrColor("chamfer-color", A.chamferColor);
|
|
3191
|
+
}
|
|
3192
|
+
get chamferAmbient() {
|
|
3193
|
+
return this.getAttrFloat("chamfer-ambient", A.chamferAmbient);
|
|
3194
|
+
}
|
|
3195
|
+
get chamferSpecular() {
|
|
3196
|
+
return this.getAttrFloat("chamfer-specular", A.chamferSpecular);
|
|
3197
|
+
}
|
|
3198
|
+
get chamferShininess() {
|
|
3199
|
+
return this.getAttrFloat("chamfer-shininess", A.chamferShininess);
|
|
3200
|
+
}
|
|
3201
|
+
// Edge occlusion
|
|
3202
|
+
get edgeOcclusionWidth() {
|
|
3203
|
+
return this.getAttrFloat("edge-occlusion-width", A.edgeOcclusionWidth);
|
|
3204
|
+
}
|
|
3205
|
+
get edgeOcclusionStrength() {
|
|
3206
|
+
return this.getAttrFloat("edge-occlusion-strength", A.edgeOcclusionStrength);
|
|
3207
|
+
}
|
|
3208
|
+
get lightDirection3() {
|
|
3209
|
+
return this.getAttrVec3("light-direction", A.lightDirection);
|
|
3210
|
+
}
|
|
3211
|
+
get shouldAutoplay() {
|
|
3212
|
+
return this.getAttrBool("autoplay", A.autoplay);
|
|
3213
|
+
}
|
|
3214
|
+
get shouldLoop() {
|
|
3215
|
+
return this.getAttrBool("loop", A.loop);
|
|
3216
|
+
}
|
|
3217
|
+
get shouldMute() {
|
|
3218
|
+
return this.getAttrBool("muted", A.muted);
|
|
3219
|
+
}
|
|
3220
|
+
// --- Event dispatching ---
|
|
3221
|
+
emit(t, e) {
|
|
3222
|
+
this.dispatchEvent(
|
|
3223
|
+
new CustomEvent(t, {
|
|
3224
|
+
detail: e,
|
|
3225
|
+
bubbles: !0,
|
|
3226
|
+
composed: !0
|
|
3227
|
+
})
|
|
3228
|
+
);
|
|
3229
|
+
}
|
|
3230
|
+
attachVideoEventListeners(t) {
|
|
3231
|
+
t.addEventListener("play", () => {
|
|
3232
|
+
this.emit("layershift-portal:play", {
|
|
3233
|
+
currentTime: t.currentTime
|
|
3234
|
+
});
|
|
3235
|
+
}), t.addEventListener("pause", () => {
|
|
3236
|
+
this.emit("layershift-portal:pause", {
|
|
3237
|
+
currentTime: t.currentTime
|
|
3238
|
+
});
|
|
3239
|
+
}), t.addEventListener("ended", () => {
|
|
3240
|
+
t.loop && (this.loopCount += 1, this.emit("layershift-portal:loop", {
|
|
3241
|
+
loopCount: this.loopCount
|
|
3242
|
+
}));
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
// --- Lifecycle ---
|
|
3246
|
+
connectedCallback() {
|
|
3247
|
+
this.setupShadowDOM(), this.init();
|
|
3248
|
+
}
|
|
3249
|
+
disconnectedCallback() {
|
|
3250
|
+
this.dispose();
|
|
3251
|
+
}
|
|
3252
|
+
attributeChangedCallback(t, e, i) {
|
|
3253
|
+
["src", "depth-src", "depth-meta", "logo-src"].includes(t) && (this.initialized ? (this.dispose(), this.setupShadowDOM(), this.init()) : this.isConnected && this.getAttribute("src") && this.getAttribute("depth-src") && this.getAttribute("depth-meta") && this.getAttribute("logo-src") && this.init());
|
|
3254
|
+
}
|
|
3255
|
+
// --- Shadow DOM ---
|
|
3256
|
+
setupShadowDOM() {
|
|
3257
|
+
this.shadow.innerHTML = "";
|
|
3258
|
+
const t = document.createElement("style");
|
|
3259
|
+
t.textContent = `
|
|
3260
|
+
:host {
|
|
3261
|
+
display: block;
|
|
3262
|
+
width: 100%;
|
|
3263
|
+
height: 100%;
|
|
3264
|
+
position: relative;
|
|
3265
|
+
overflow: hidden;
|
|
3266
|
+
background: transparent;
|
|
3267
|
+
}
|
|
3268
|
+
.container {
|
|
3269
|
+
width: 100%;
|
|
3270
|
+
height: 100%;
|
|
3271
|
+
position: absolute;
|
|
3272
|
+
inset: 0;
|
|
3273
|
+
}
|
|
3274
|
+
canvas {
|
|
3275
|
+
display: block;
|
|
3276
|
+
width: 100%;
|
|
3277
|
+
height: 100%;
|
|
3278
|
+
}
|
|
3279
|
+
`, this.shadow.appendChild(t), this.container = document.createElement("div"), this.container.className = "container", this.shadow.appendChild(this.container);
|
|
3280
|
+
}
|
|
3281
|
+
// --- Initialization ---
|
|
3282
|
+
async init() {
|
|
3283
|
+
const t = this.getAttribute("src"), e = this.getAttribute("depth-src"), i = this.getAttribute("depth-meta"), r = this.getAttribute("logo-src");
|
|
3284
|
+
if (!t || !e || !i || !r) {
|
|
3285
|
+
const o = "src, depth-src, depth-meta, and logo-src attributes are required.";
|
|
3286
|
+
console.warn(`<layershift-portal>: ${o}`), this.emit("layershift-portal:error", { message: o });
|
|
3287
|
+
return;
|
|
3288
|
+
}
|
|
3289
|
+
if (this.container) {
|
|
3290
|
+
this.abortController = new AbortController();
|
|
3291
|
+
try {
|
|
3292
|
+
const [o, s, a] = await Promise.all([
|
|
3293
|
+
this.createVideoElement(t),
|
|
3294
|
+
At(e, i),
|
|
3295
|
+
me(r)
|
|
3296
|
+
]);
|
|
3297
|
+
if (this.abortController.signal.aborted) {
|
|
3298
|
+
o.remove();
|
|
3299
|
+
return;
|
|
3300
|
+
}
|
|
3301
|
+
this.video = o, this.mesh = a, this.loopCount = 0, this.attachVideoEventListeners(o);
|
|
3302
|
+
const h = this.parallaxMax / Math.max(o.videoWidth, 1);
|
|
3303
|
+
let l;
|
|
3304
|
+
try {
|
|
3305
|
+
const d = await st.create(
|
|
3306
|
+
s,
|
|
3307
|
+
s.meta.width,
|
|
3308
|
+
s.meta.height
|
|
3309
|
+
);
|
|
3310
|
+
this.depthWorker = d, l = (m) => d.sample(m);
|
|
3311
|
+
} catch {
|
|
3312
|
+
const d = new bt(
|
|
3313
|
+
s,
|
|
3314
|
+
s.meta.width,
|
|
3315
|
+
s.meta.height
|
|
3316
|
+
);
|
|
3317
|
+
l = (m) => d.sample(m);
|
|
3318
|
+
}
|
|
3319
|
+
if (this.abortController.signal.aborted) {
|
|
3320
|
+
o.remove(), this.depthWorker?.dispose(), this.depthWorker = null;
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
3323
|
+
const u = {
|
|
3324
|
+
parallaxStrength: h,
|
|
3325
|
+
overscanPadding: this.overscan,
|
|
3326
|
+
pomSteps: this.pomSteps,
|
|
3327
|
+
// Boundary
|
|
3328
|
+
rimLightIntensity: this.rimIntensity,
|
|
3329
|
+
rimLightColor: this.rimColor,
|
|
3330
|
+
rimLightWidth: this.rimWidth,
|
|
3331
|
+
refractionStrength: this.refractionStrength,
|
|
3332
|
+
chromaticStrength: this.chromaticStrength,
|
|
3333
|
+
occlusionIntensity: this.occlusionIntensity,
|
|
3334
|
+
// Lens transform
|
|
3335
|
+
depthPower: this.depthPower,
|
|
3336
|
+
depthScale: this.depthScale,
|
|
3337
|
+
depthBias: this.depthBias,
|
|
3338
|
+
// Interior mood
|
|
3339
|
+
fogDensity: this.fogDensity,
|
|
3340
|
+
fogColor: this.fogColor,
|
|
3341
|
+
colorShift: this.colorShift,
|
|
3342
|
+
brightnessBias: this.brightnessBias,
|
|
3343
|
+
// Depth-adaptive
|
|
3344
|
+
contrastLow: this.contrastLow,
|
|
3345
|
+
contrastHigh: this.contrastHigh,
|
|
3346
|
+
verticalReduction: this.verticalReduction,
|
|
3347
|
+
dofStart: this.dofStart,
|
|
3348
|
+
dofStrength: this.dofStrength,
|
|
3349
|
+
// Bevel / dimensional typography
|
|
3350
|
+
bevelIntensity: this.bevelIntensity,
|
|
3351
|
+
bevelWidth: this.bevelWidth,
|
|
3352
|
+
bevelDarkening: this.bevelDarkening,
|
|
3353
|
+
bevelDesaturation: this.bevelDesaturation,
|
|
3354
|
+
bevelLightAngle: this.bevelLightAngle,
|
|
3355
|
+
// Volumetric edge wall
|
|
3356
|
+
edgeThickness: this.edgeThickness,
|
|
3357
|
+
edgeSpecular: this.edgeSpecular,
|
|
3358
|
+
edgeColor: this.edgeColor,
|
|
3359
|
+
// Chamfer geometry
|
|
3360
|
+
chamferWidth: this.chamferWidth,
|
|
3361
|
+
chamferAngle: this.chamferAngle,
|
|
3362
|
+
chamferColor: this.chamferColor,
|
|
3363
|
+
chamferAmbient: this.chamferAmbient,
|
|
3364
|
+
chamferSpecular: this.chamferSpecular,
|
|
3365
|
+
chamferShininess: this.chamferShininess,
|
|
3366
|
+
// Edge occlusion
|
|
3367
|
+
edgeOcclusionWidth: this.edgeOcclusionWidth,
|
|
3368
|
+
edgeOcclusionStrength: this.edgeOcclusionStrength,
|
|
3369
|
+
lightDirection: this.lightDirection3
|
|
3370
|
+
};
|
|
3371
|
+
this.renderer = new ft(this.container, u), this.renderer.initialize(o, s.meta.width, s.meta.height, a), this.inputHandler = new dt(this);
|
|
3372
|
+
const f = this.parallaxX, c = this.parallaxY;
|
|
3373
|
+
if (this.renderer.start(
|
|
3374
|
+
o,
|
|
3375
|
+
l,
|
|
3376
|
+
() => {
|
|
3377
|
+
const d = this.inputHandler.update();
|
|
3378
|
+
return { x: d.x * f, y: d.y * c };
|
|
3379
|
+
},
|
|
3380
|
+
(d, m) => {
|
|
3381
|
+
this.emit("layershift-portal:frame", {
|
|
3382
|
+
currentTime: d,
|
|
3383
|
+
frameNumber: m
|
|
3384
|
+
});
|
|
3385
|
+
}
|
|
3386
|
+
), this.shouldAutoplay) {
|
|
3387
|
+
o.currentTime = 0;
|
|
3388
|
+
try {
|
|
3389
|
+
await o.play();
|
|
3390
|
+
} catch {
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
this.initialized = !0, this.emit("layershift-portal:ready", {
|
|
3394
|
+
videoWidth: o.videoWidth,
|
|
3395
|
+
videoHeight: o.videoHeight,
|
|
3396
|
+
duration: o.duration
|
|
3397
|
+
});
|
|
3398
|
+
} catch (o) {
|
|
3399
|
+
const s = o instanceof Error ? o.message : "Failed to initialize.";
|
|
3400
|
+
console.error("<layershift-portal>: Failed to initialize.", o), this.emit("layershift-portal:error", { message: s });
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
// --- Video element ---
|
|
3405
|
+
async createVideoElement(t) {
|
|
3406
|
+
const e = document.createElement("video");
|
|
3407
|
+
return e.crossOrigin = "anonymous", e.setAttribute("crossorigin", "anonymous"), e.playsInline = !0, e.setAttribute("playsinline", ""), e.setAttribute("webkit-playsinline", "true"), e.muted = this.shouldMute, e.defaultMuted = this.shouldMute, this.shouldMute && e.setAttribute("muted", ""), e.loop = this.shouldLoop, e.preload = "auto", e.style.display = "none", e.src = t, this.shadow.appendChild(e), await new Promise((i, r) => {
|
|
3408
|
+
if (e.readyState >= HTMLMediaElement.HAVE_METADATA) {
|
|
3409
|
+
i();
|
|
3410
|
+
return;
|
|
3411
|
+
}
|
|
3412
|
+
const o = () => {
|
|
3413
|
+
a(), i();
|
|
3414
|
+
}, s = () => {
|
|
3415
|
+
a(), r(new Error("Failed to load video metadata."));
|
|
3416
|
+
}, a = () => {
|
|
3417
|
+
e.removeEventListener("loadedmetadata", o), e.removeEventListener("error", s);
|
|
3418
|
+
};
|
|
3419
|
+
e.addEventListener("loadedmetadata", o), e.addEventListener("error", s), e.load();
|
|
3420
|
+
}), e;
|
|
3421
|
+
}
|
|
3422
|
+
// --- Cleanup ---
|
|
3423
|
+
dispose() {
|
|
3424
|
+
this.abortController?.abort(), this.abortController = null, this.renderer?.dispose(), this.renderer = null, this.inputHandler?.dispose(), this.inputHandler = null, this.depthWorker?.dispose(), this.depthWorker = null, this.video && (this.video.pause(), this.video.removeAttribute("src"), this.video.load(), this.video.remove(), this.video = null), this.mesh = null, this.initialized = !1, this.loopCount = 0, this.container = null;
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
function W(n, t, e) {
|
|
3428
|
+
return Math.min(e, Math.max(t, n));
|
|
3429
|
+
}
|
|
3430
|
+
function nt(n, t, e) {
|
|
3431
|
+
return n + (t - n) * e;
|
|
3432
|
+
}
|
|
3433
|
+
function Xe(n) {
|
|
3434
|
+
const t = n.replace("#", "");
|
|
3435
|
+
if (t.length === 3) {
|
|
3436
|
+
const e = parseInt(t[0] + t[0], 16) / 255, i = parseInt(t[1] + t[1], 16) / 255, r = parseInt(t[2] + t[2], 16) / 255;
|
|
3437
|
+
return [e, i, r];
|
|
3438
|
+
}
|
|
3439
|
+
if (t.length === 6) {
|
|
3440
|
+
const e = parseInt(t.substring(0, 2), 16) / 255, i = parseInt(t.substring(2, 4), 16) / 255, r = parseInt(t.substring(4, 6), 16) / 255;
|
|
3441
|
+
return [e, i, r];
|
|
3442
|
+
}
|
|
3443
|
+
return [0, 0, 0];
|
|
1207
3444
|
}
|
|
1208
|
-
customElements.get(
|
|
3445
|
+
customElements.get(ht.TAG_NAME) || customElements.define(ht.TAG_NAME, ht);
|
|
3446
|
+
customElements.get(lt.TAG_NAME) || customElements.define(lt.TAG_NAME, lt);
|
|
1209
3447
|
export {
|
|
1210
|
-
|
|
3448
|
+
ht as LayershiftElement,
|
|
3449
|
+
lt as LayershiftPortalElement
|
|
1211
3450
|
};
|