layershift 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/layershift.js +537 -7
- package/dist/npm/layershift.es.js +2625 -368
- 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/layershift-element.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/input-handler.d.ts +6 -0
- package/dist/types/input-handler.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 V {
|
|
|
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 V {
|
|
|
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 V {
|
|
|
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 I {
|
|
|
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 I {
|
|
|
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 z(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 W = {
|
|
|
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
|
|
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
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
const b = Math.sqrt(
|
|
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:
|
|
274
|
+
mean: g,
|
|
275
275
|
stdDev: b,
|
|
276
|
-
p5:
|
|
277
|
-
p25:
|
|
278
|
-
median:
|
|
279
|
-
p75:
|
|
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) {
|
|
|
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 K = (
|
|
|
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 K = (
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 };
|
|
@@ -900,24 +900,42 @@ class tt {
|
|
|
900
900
|
usingMotionInput = !1;
|
|
901
901
|
motionListenerAttached = !1;
|
|
902
902
|
motionRequested = !1;
|
|
903
|
+
touchActive = !1;
|
|
904
|
+
touchAnchorX = 0;
|
|
905
|
+
touchAnchorY = 0;
|
|
903
906
|
lerpFactor;
|
|
904
907
|
motionLerpFactor;
|
|
908
|
+
/** Pixels of finger drag to reach full parallax offset (-1 or 1). */
|
|
909
|
+
static TOUCH_DRAG_RANGE = 100;
|
|
905
910
|
update() {
|
|
906
|
-
const t = this.usingMotionInput ? this.motionTarget : this.pointerTarget, e = this.usingMotionInput ? this.motionLerpFactor : this.lerpFactor;
|
|
907
|
-
return this.smoothedOutput.x =
|
|
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 = it(this.smoothedOutput.x, t.x, e), this.smoothedOutput.y = it(this.smoothedOutput.y, t.y, e), this.smoothedOutput;
|
|
908
913
|
}
|
|
909
914
|
dispose() {
|
|
910
|
-
this.host.removeEventListener("mousemove", this.handleMouseMove), this.host.removeEventListener("mouseleave", this.resetPointerTarget), this.host.removeEventListener("touchstart", this.
|
|
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);
|
|
911
916
|
}
|
|
912
917
|
handleMouseMove = (t) => {
|
|
913
|
-
const e = this.host.getBoundingClientRect(),
|
|
914
|
-
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);
|
|
915
920
|
};
|
|
916
921
|
resetPointerTarget = () => {
|
|
917
922
|
this.pointerTarget.x = 0, this.pointerTarget.y = 0;
|
|
918
923
|
};
|
|
919
|
-
|
|
920
|
-
|
|
924
|
+
handleTouchStart = (t) => {
|
|
925
|
+
const e = t.touches[0];
|
|
926
|
+
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()));
|
|
927
|
+
};
|
|
928
|
+
handleTouchMove = (t) => {
|
|
929
|
+
const e = t.touches[0];
|
|
930
|
+
if (!e) return;
|
|
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
|
+
};
|
|
934
|
+
handleTouchEnd = () => {
|
|
935
|
+
this.touchActive = !1, this.pointerTarget.x = 0, this.pointerTarget.y = 0;
|
|
936
|
+
};
|
|
937
|
+
async requestMotionPermission() {
|
|
938
|
+
if (typeof DeviceOrientationEvent > "u") return;
|
|
921
939
|
const t = DeviceOrientationEvent;
|
|
922
940
|
if (typeof t.requestPermission == "function")
|
|
923
941
|
try {
|
|
@@ -926,13 +944,13 @@ class tt {
|
|
|
926
944
|
return;
|
|
927
945
|
}
|
|
928
946
|
this.motionListenerAttached || (window.addEventListener("deviceorientation", this.handleDeviceOrientation), this.motionListenerAttached = !0), this.usingMotionInput = !0;
|
|
929
|
-
}
|
|
947
|
+
}
|
|
930
948
|
handleDeviceOrientation = (t) => {
|
|
931
|
-
const e =
|
|
932
|
-
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);
|
|
933
951
|
};
|
|
934
|
-
}
|
|
935
|
-
class
|
|
952
|
+
};
|
|
953
|
+
class ht extends HTMLElement {
|
|
936
954
|
static TAG_NAME = "layershift-parallax";
|
|
937
955
|
static get observedAttributes() {
|
|
938
956
|
return [
|
|
@@ -963,36 +981,36 @@ class C extends HTMLElement {
|
|
|
963
981
|
}
|
|
964
982
|
// --- Attribute helpers ---
|
|
965
983
|
getAttrFloat(t, e) {
|
|
966
|
-
const
|
|
967
|
-
if (
|
|
968
|
-
const
|
|
969
|
-
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;
|
|
970
988
|
}
|
|
971
989
|
getAttrBool(t, e) {
|
|
972
990
|
if (!this.hasAttribute(t)) return e;
|
|
973
|
-
const
|
|
974
|
-
return !(
|
|
991
|
+
const i = this.getAttribute(t);
|
|
992
|
+
return !(i === "false" || i === "0");
|
|
975
993
|
}
|
|
976
994
|
get parallaxX() {
|
|
977
|
-
return this.getAttrFloat("parallax-x",
|
|
995
|
+
return this.getAttrFloat("parallax-x", B.parallaxX);
|
|
978
996
|
}
|
|
979
997
|
get parallaxY() {
|
|
980
|
-
return this.getAttrFloat("parallax-y",
|
|
998
|
+
return this.getAttrFloat("parallax-y", B.parallaxY);
|
|
981
999
|
}
|
|
982
1000
|
get parallaxMax() {
|
|
983
|
-
return this.getAttrFloat("parallax-max",
|
|
1001
|
+
return this.getAttrFloat("parallax-max", B.parallaxMax);
|
|
984
1002
|
}
|
|
985
1003
|
get overscan() {
|
|
986
|
-
return this.getAttrFloat("overscan",
|
|
1004
|
+
return this.getAttrFloat("overscan", B.overscan);
|
|
987
1005
|
}
|
|
988
1006
|
get shouldAutoplay() {
|
|
989
|
-
return this.getAttrBool("autoplay",
|
|
1007
|
+
return this.getAttrBool("autoplay", B.autoplay);
|
|
990
1008
|
}
|
|
991
1009
|
get shouldLoop() {
|
|
992
|
-
return this.getAttrBool("loop",
|
|
1010
|
+
return this.getAttrBool("loop", B.loop);
|
|
993
1011
|
}
|
|
994
1012
|
get shouldMute() {
|
|
995
|
-
return this.getAttrBool("muted",
|
|
1013
|
+
return this.getAttrBool("muted", B.muted);
|
|
996
1014
|
}
|
|
997
1015
|
// --- Event dispatching ---
|
|
998
1016
|
/**
|
|
@@ -1035,7 +1053,7 @@ class C extends HTMLElement {
|
|
|
1035
1053
|
disconnectedCallback() {
|
|
1036
1054
|
this.dispose();
|
|
1037
1055
|
}
|
|
1038
|
-
attributeChangedCallback(t, e,
|
|
1056
|
+
attributeChangedCallback(t, e, i) {
|
|
1039
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());
|
|
1040
1058
|
}
|
|
1041
1059
|
// --- Shadow DOM setup ---
|
|
@@ -1066,114 +1084,114 @@ class C extends HTMLElement {
|
|
|
1066
1084
|
}
|
|
1067
1085
|
// --- Initialization ---
|
|
1068
1086
|
async init() {
|
|
1069
|
-
const t = this.getAttribute("src"), e = this.getAttribute("depth-src"),
|
|
1070
|
-
if (!t || !e || !
|
|
1071
|
-
const
|
|
1072
|
-
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 });
|
|
1073
1091
|
return;
|
|
1074
1092
|
}
|
|
1075
1093
|
if (this.container) {
|
|
1076
1094
|
this.abortController = new AbortController();
|
|
1077
1095
|
try {
|
|
1078
|
-
const [
|
|
1096
|
+
const [r, o] = await Promise.all([
|
|
1079
1097
|
this.createVideoElement(t),
|
|
1080
|
-
|
|
1098
|
+
At(e, i)
|
|
1081
1099
|
]);
|
|
1082
1100
|
if (this.abortController.signal.aborted) {
|
|
1083
|
-
|
|
1101
|
+
r.remove();
|
|
1084
1102
|
return;
|
|
1085
1103
|
}
|
|
1086
|
-
this.video =
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
),
|
|
1092
|
-
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;
|
|
1093
1111
|
try {
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1112
|
+
const d = await st.create(
|
|
1113
|
+
o,
|
|
1114
|
+
o.meta.width,
|
|
1115
|
+
o.meta.height
|
|
1098
1116
|
);
|
|
1099
|
-
this.depthWorker =
|
|
1117
|
+
this.depthWorker = d, u = (m) => d.sample(m);
|
|
1100
1118
|
} catch {
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1119
|
+
const d = new bt(
|
|
1120
|
+
o,
|
|
1121
|
+
o.meta.width,
|
|
1122
|
+
o.meta.height
|
|
1105
1123
|
);
|
|
1106
|
-
|
|
1124
|
+
u = (m) => d.sample(m);
|
|
1107
1125
|
}
|
|
1108
1126
|
if (this.abortController.signal.aborted) {
|
|
1109
|
-
|
|
1127
|
+
r.remove(), this.depthWorker?.dispose(), this.depthWorker = null;
|
|
1110
1128
|
return;
|
|
1111
1129
|
}
|
|
1112
|
-
this.renderer = new
|
|
1113
|
-
parallaxStrength:
|
|
1130
|
+
this.renderer = new $(this.container, {
|
|
1131
|
+
parallaxStrength: h,
|
|
1114
1132
|
pomEnabled: !0,
|
|
1115
|
-
pomSteps:
|
|
1116
|
-
overscanPadding:
|
|
1117
|
-
contrastLow:
|
|
1118
|
-
contrastHigh:
|
|
1119
|
-
verticalReduction:
|
|
1120
|
-
dofStart:
|
|
1121
|
-
dofStrength:
|
|
1122
|
-
}), this.renderer.initialize(
|
|
1123
|
-
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;
|
|
1124
1142
|
if (this.renderer.start(
|
|
1125
|
-
|
|
1126
|
-
|
|
1143
|
+
r,
|
|
1144
|
+
u,
|
|
1127
1145
|
() => {
|
|
1128
|
-
const
|
|
1146
|
+
const d = this.inputHandler.update();
|
|
1129
1147
|
return {
|
|
1130
|
-
x:
|
|
1131
|
-
y:
|
|
1148
|
+
x: d.x * f,
|
|
1149
|
+
y: d.y * c
|
|
1132
1150
|
};
|
|
1133
1151
|
},
|
|
1134
1152
|
// RVFC callback: dispatch 'frame' event on each new video frame
|
|
1135
|
-
(
|
|
1153
|
+
(d, m) => {
|
|
1136
1154
|
this.emit("layershift-parallax:frame", {
|
|
1137
|
-
currentTime:
|
|
1155
|
+
currentTime: d,
|
|
1138
1156
|
frameNumber: m
|
|
1139
1157
|
});
|
|
1140
1158
|
}
|
|
1141
1159
|
), this.shouldAutoplay) {
|
|
1142
|
-
|
|
1160
|
+
r.currentTime = 0;
|
|
1143
1161
|
try {
|
|
1144
|
-
await
|
|
1162
|
+
await r.play();
|
|
1145
1163
|
} catch {
|
|
1146
1164
|
}
|
|
1147
1165
|
}
|
|
1148
1166
|
this.initialized = !0, this.emit("layershift-parallax:ready", {
|
|
1149
|
-
videoWidth:
|
|
1150
|
-
videoHeight:
|
|
1151
|
-
duration:
|
|
1152
|
-
depthProfile:
|
|
1153
|
-
derivedParams:
|
|
1167
|
+
videoWidth: r.videoWidth,
|
|
1168
|
+
videoHeight: r.videoHeight,
|
|
1169
|
+
duration: r.duration,
|
|
1170
|
+
depthProfile: s,
|
|
1171
|
+
derivedParams: a
|
|
1154
1172
|
});
|
|
1155
|
-
} catch (
|
|
1156
|
-
const
|
|
1157
|
-
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 });
|
|
1158
1176
|
}
|
|
1159
1177
|
}
|
|
1160
1178
|
}
|
|
1161
1179
|
// --- Video element ---
|
|
1162
1180
|
async createVideoElement(t) {
|
|
1163
1181
|
const e = document.createElement("video");
|
|
1164
|
-
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) => {
|
|
1165
1183
|
if (e.readyState >= HTMLMediaElement.HAVE_METADATA) {
|
|
1166
|
-
|
|
1184
|
+
i();
|
|
1167
1185
|
return;
|
|
1168
1186
|
}
|
|
1169
|
-
const
|
|
1170
|
-
|
|
1171
|
-
},
|
|
1172
|
-
|
|
1173
|
-
},
|
|
1174
|
-
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);
|
|
1175
1193
|
};
|
|
1176
|
-
e.addEventListener("loadedmetadata",
|
|
1194
|
+
e.addEventListener("loadedmetadata", o), e.addEventListener("error", s), e.load();
|
|
1177
1195
|
}), e;
|
|
1178
1196
|
}
|
|
1179
1197
|
// --- Cleanup ---
|
|
@@ -1181,13 +1199,2252 @@ class C extends HTMLElement {
|
|
|
1181
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;
|
|
1182
1200
|
}
|
|
1183
1201
|
}
|
|
1184
|
-
function
|
|
1185
|
-
return Math.min(e, Math.max(t,
|
|
1202
|
+
function z(n, t, e) {
|
|
1203
|
+
return Math.min(e, Math.max(t, n));
|
|
1204
|
+
}
|
|
1205
|
+
function it(n, t, e) {
|
|
1206
|
+
return n + (t - n) * e;
|
|
1186
1207
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
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];
|
|
1189
3444
|
}
|
|
1190
|
-
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);
|
|
1191
3447
|
export {
|
|
1192
|
-
|
|
3448
|
+
ht as LayershiftElement,
|
|
3449
|
+
lt as LayershiftPortalElement
|
|
1193
3450
|
};
|