vireglass 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -1
- package/dist/{chunk-3PGYF2NK.js → chunk-CTTZNZFV.js} +3 -3
- package/dist/{chunk-3PGYF2NK.js.map → chunk-CTTZNZFV.js.map} +1 -1
- package/dist/{chunk-FL5UZPY5.js → chunk-H5QTBF67.js} +4 -4
- package/dist/{chunk-FL5UZPY5.js.map → chunk-H5QTBF67.js.map} +1 -1
- package/dist/{chunk-TTOKBARR.js → chunk-MMPK6DCK.js} +10 -3
- package/dist/chunk-MMPK6DCK.js.map +1 -0
- package/dist/{chunk-BZC2WHCK.js → chunk-PM2SWCSF.js} +4 -4
- package/dist/{chunk-BZC2WHCK.js.map → chunk-PM2SWCSF.js.map} +1 -1
- package/dist/{chunk-P2ZOODXD.js → chunk-SLRPHDV7.js} +3 -3
- package/dist/{chunk-P2ZOODXD.js.map → chunk-SLRPHDV7.js.map} +1 -1
- package/dist/dom.cjs +48 -20
- package/dist/dom.cjs.map +1 -1
- package/dist/dom.d.cts +15 -3
- package/dist/dom.d.ts +15 -3
- package/dist/dom.js +44 -23
- package/dist/dom.js.map +1 -1
- package/dist/index.cjs +1 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -5
- package/dist/law.cjs +8 -1
- package/dist/law.cjs.map +1 -1
- package/dist/law.d.cts +7 -0
- package/dist/law.d.ts +7 -0
- package/dist/law.js +1 -1
- package/dist/reference.js +2 -2
- package/dist/web/index.cjs +262 -153
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +88 -2
- package/dist/web/index.d.ts +88 -2
- package/dist/web/index.js +257 -156
- package/dist/web/index.js.map +1 -1
- package/docs/law.md +2 -2
- package/package.json +13 -9
- package/src/dom/body.ts +24 -9
- package/src/dom/index.ts +44 -10
- package/src/dom/probe.ts +27 -3
- package/src/law.ts +7 -0
- package/src/targets/glsl.ts +4 -0
- package/src/web/gl.ts +57 -4
- package/src/web/gpu-timer.ts +111 -0
- package/src/web/index.ts +1 -0
- package/src/web/renderer.ts +305 -218
- package/dist/chunk-TTOKBARR.js.map +0 -1
package/dist/web/index.cjs
CHANGED
|
@@ -41,8 +41,7 @@ var BODY = {
|
|
|
41
41
|
darkSideFrom: 0.42,
|
|
42
42
|
darkSideTo: 0.58,
|
|
43
43
|
/** Ceiling on any single demand, so one requirement cannot make the glass opaque on its own. */
|
|
44
|
-
maxDemand: 0.92
|
|
45
|
-
};
|
|
44
|
+
maxDemand: 0.92};
|
|
46
45
|
var DISPERSION = {
|
|
47
46
|
/** Red bends less than the base index, blue more — and not symmetrically. */
|
|
48
47
|
redShift: -0.4,
|
|
@@ -1547,6 +1546,7 @@ function createTexture(gl, options) {
|
|
|
1547
1546
|
const internalFormat = options.internalFormat ?? gl.RGBA8;
|
|
1548
1547
|
const format = options.format ?? gl.RGBA;
|
|
1549
1548
|
const type = options.type ?? gl.UNSIGNED_BYTE;
|
|
1549
|
+
const wrap = options.wrap ?? gl.CLAMP_TO_EDGE;
|
|
1550
1550
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
1551
1551
|
gl.texImage2D(
|
|
1552
1552
|
gl.TEXTURE_2D,
|
|
@@ -1561,11 +1561,45 @@ function createTexture(gl, options) {
|
|
|
1561
1561
|
);
|
|
1562
1562
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
1563
1563
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
1564
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S,
|
|
1565
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T,
|
|
1564
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
|
|
1565
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
|
|
1566
1566
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1567
1567
|
return texture;
|
|
1568
1568
|
}
|
|
1569
|
+
function locationCache(gl, program) {
|
|
1570
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1571
|
+
return (name) => {
|
|
1572
|
+
let loc = cache.get(name);
|
|
1573
|
+
if (loc === void 0) {
|
|
1574
|
+
loc = gl.getUniformLocation(program, name);
|
|
1575
|
+
cache.set(name, loc);
|
|
1576
|
+
}
|
|
1577
|
+
return loc;
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
function setUniform(gl, loc, value) {
|
|
1581
|
+
if (!loc) return;
|
|
1582
|
+
if (typeof value === "number") {
|
|
1583
|
+
gl.uniform1f(loc, value);
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
switch (value.length) {
|
|
1587
|
+
case 1:
|
|
1588
|
+
gl.uniform1f(loc, value[0]);
|
|
1589
|
+
break;
|
|
1590
|
+
case 2:
|
|
1591
|
+
gl.uniform2f(loc, value[0], value[1]);
|
|
1592
|
+
break;
|
|
1593
|
+
case 3:
|
|
1594
|
+
gl.uniform3f(loc, value[0], value[1], value[2]);
|
|
1595
|
+
break;
|
|
1596
|
+
case 4:
|
|
1597
|
+
gl.uniform4f(loc, value[0], value[1], value[2], value[3]);
|
|
1598
|
+
break;
|
|
1599
|
+
default:
|
|
1600
|
+
throw new Error(`vireglass/web: unsupported uniform size (${value.length})`);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1569
1603
|
function createFramebuffer(gl, texture) {
|
|
1570
1604
|
const fbo = gl.createFramebuffer();
|
|
1571
1605
|
if (!fbo) throw new Error("vireglass/web: gl.createFramebuffer returned null");
|
|
@@ -1585,6 +1619,62 @@ function bindTextureAt(gl, unit, texture, program, uniformName) {
|
|
|
1585
1619
|
gl.uniform1i(loc, unit);
|
|
1586
1620
|
}
|
|
1587
1621
|
|
|
1622
|
+
// src/web/gpu-timer.ts
|
|
1623
|
+
var GPU_TIMER_RING_SIZE = 3;
|
|
1624
|
+
function createGpuTimer(gl) {
|
|
1625
|
+
const ext = gl.getExtension("EXT_disjoint_timer_query_webgl2");
|
|
1626
|
+
const slots = ext ? Array.from({ length: GPU_TIMER_RING_SIZE }, () => ({
|
|
1627
|
+
query: gl.createQuery(),
|
|
1628
|
+
pending: false
|
|
1629
|
+
})) : [];
|
|
1630
|
+
let cursor = 0;
|
|
1631
|
+
let active = null;
|
|
1632
|
+
let lastMs = null;
|
|
1633
|
+
function poll() {
|
|
1634
|
+
if (!ext) return;
|
|
1635
|
+
for (const slot of slots) {
|
|
1636
|
+
if (!slot.pending) continue;
|
|
1637
|
+
if (!gl.getQueryParameter(slot.query, gl.QUERY_RESULT_AVAILABLE)) continue;
|
|
1638
|
+
slot.pending = false;
|
|
1639
|
+
if (gl.getParameter(ext.GPU_DISJOINT_EXT)) continue;
|
|
1640
|
+
const ns = gl.getQueryParameter(slot.query, gl.QUERY_RESULT);
|
|
1641
|
+
lastMs = ns / 1e6;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function begin() {
|
|
1645
|
+
active = null;
|
|
1646
|
+
if (!ext || slots.length === 0) return;
|
|
1647
|
+
poll();
|
|
1648
|
+
const slot = slots[cursor];
|
|
1649
|
+
if (slot.pending) return;
|
|
1650
|
+
gl.beginQuery(ext.TIME_ELAPSED_EXT, slot.query);
|
|
1651
|
+
active = slot;
|
|
1652
|
+
}
|
|
1653
|
+
function end() {
|
|
1654
|
+
if (!ext || !active) return;
|
|
1655
|
+
gl.endQuery(ext.TIME_ELAPSED_EXT);
|
|
1656
|
+
active.pending = true;
|
|
1657
|
+
cursor = (cursor + 1) % slots.length;
|
|
1658
|
+
active = null;
|
|
1659
|
+
}
|
|
1660
|
+
function time(fn) {
|
|
1661
|
+
begin();
|
|
1662
|
+
try {
|
|
1663
|
+
return fn();
|
|
1664
|
+
} finally {
|
|
1665
|
+
end();
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
function getLastMs() {
|
|
1669
|
+
poll();
|
|
1670
|
+
return lastMs;
|
|
1671
|
+
}
|
|
1672
|
+
function destroy() {
|
|
1673
|
+
for (const slot of slots) gl.deleteQuery(slot.query);
|
|
1674
|
+
}
|
|
1675
|
+
return { time, getLastMs, destroy };
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1588
1678
|
// src/web/probe.ts
|
|
1589
1679
|
var PROBE_GRID_WIDTH = 48;
|
|
1590
1680
|
var PROBE_GRID_HEIGHT = 96;
|
|
@@ -1805,40 +1895,6 @@ void main() {
|
|
|
1805
1895
|
fragColor = texture(u_content, uv);
|
|
1806
1896
|
}
|
|
1807
1897
|
`;
|
|
1808
|
-
function locationCache(gl, program) {
|
|
1809
|
-
const cache = /* @__PURE__ */ new Map();
|
|
1810
|
-
return (name) => {
|
|
1811
|
-
let loc = cache.get(name);
|
|
1812
|
-
if (loc === void 0) {
|
|
1813
|
-
loc = gl.getUniformLocation(program, name);
|
|
1814
|
-
cache.set(name, loc);
|
|
1815
|
-
}
|
|
1816
|
-
return loc;
|
|
1817
|
-
};
|
|
1818
|
-
}
|
|
1819
|
-
function setUniform(gl, loc, value) {
|
|
1820
|
-
if (!loc) return;
|
|
1821
|
-
if (typeof value === "number") {
|
|
1822
|
-
gl.uniform1f(loc, value);
|
|
1823
|
-
return;
|
|
1824
|
-
}
|
|
1825
|
-
switch (value.length) {
|
|
1826
|
-
case 1:
|
|
1827
|
-
gl.uniform1f(loc, value[0]);
|
|
1828
|
-
break;
|
|
1829
|
-
case 2:
|
|
1830
|
-
gl.uniform2f(loc, value[0], value[1]);
|
|
1831
|
-
break;
|
|
1832
|
-
case 3:
|
|
1833
|
-
gl.uniform3f(loc, value[0], value[1], value[2]);
|
|
1834
|
-
break;
|
|
1835
|
-
case 4:
|
|
1836
|
-
gl.uniform4f(loc, value[0], value[1], value[2], value[3]);
|
|
1837
|
-
break;
|
|
1838
|
-
default:
|
|
1839
|
-
throw new Error(`vireglass/web: unsupported uniform size (${value.length})`);
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
1898
|
function applyChannel(gl, get, names, sizes, values) {
|
|
1843
1899
|
let cursor = 0;
|
|
1844
1900
|
for (let i = 0; i < names.length; i += 1) {
|
|
@@ -1901,6 +1957,12 @@ function createVireGlassRenderer(canvas) {
|
|
|
1901
1957
|
width: Math.max(width, 1),
|
|
1902
1958
|
height: Math.max(height, 1)
|
|
1903
1959
|
});
|
|
1960
|
+
let backdropFbo = null;
|
|
1961
|
+
function ensureBackdropFbo() {
|
|
1962
|
+
if (!backdropFbo) backdropFbo = createFramebuffer(gl, contentTexture);
|
|
1963
|
+
return backdropFbo;
|
|
1964
|
+
}
|
|
1965
|
+
const gpuTimer = createGpuTimer(gl);
|
|
1904
1966
|
const iconTexture = createTexture(gl, { width: 1, height: 1 });
|
|
1905
1967
|
const colorTexture = createTexture(gl, { width: 1, height: 1 });
|
|
1906
1968
|
gl.bindTexture(gl.TEXTURE_2D, iconTexture);
|
|
@@ -1929,128 +1991,164 @@ function createVireGlassRenderer(canvas) {
|
|
|
1929
1991
|
if (!sceneCtx) throw new Error("vireglass/web: scene 2D context unavailable");
|
|
1930
1992
|
gl.deleteTexture(contentTexture);
|
|
1931
1993
|
contentTexture = createTexture(gl, { width, height });
|
|
1994
|
+
if (backdropFbo) {
|
|
1995
|
+
gl.deleteFramebuffer(backdropFbo);
|
|
1996
|
+
backdropFbo = null;
|
|
1997
|
+
}
|
|
1932
1998
|
}
|
|
1933
1999
|
function render(options) {
|
|
1934
2000
|
if (!sceneCtx) throw new Error("vireglass/web: renderer not initialized (resize was never called)");
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
if (options.colorLayer) {
|
|
1941
|
-
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
|
|
1942
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.colorLayer);
|
|
2001
|
+
const ctx = sceneCtx;
|
|
2002
|
+
if (options.backdrop && options.scene) {
|
|
2003
|
+
throw new Error(
|
|
2004
|
+
"vireglass/web: render() needs exactly one of options.scene or options.backdrop, not both"
|
|
2005
|
+
);
|
|
1943
2006
|
}
|
|
1944
|
-
if (options.
|
|
1945
|
-
|
|
1946
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.iconMask);
|
|
1947
|
-
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
2007
|
+
if (!options.backdrop && !options.scene) {
|
|
2008
|
+
throw new Error("vireglass/web: render() needs either options.scene or options.backdrop");
|
|
1948
2009
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
const stats = fresh ? settleStats(index, fresh) : null;
|
|
1970
|
-
probes.push(stats);
|
|
1971
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
1972
|
-
gl.viewport(0, 0, width, height);
|
|
1973
|
-
gl.enable(gl.BLEND);
|
|
1974
|
-
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
1975
|
-
const reachOf = (m) => m ? Math.hypot(m.offsetX, m.offsetY) + Math.max(m.width, m.height) / 2 + m.smoothing : 0;
|
|
1976
|
-
const morphReach = Math.max(reachOf(piece.morph), reachOf(piece.morph2));
|
|
1977
|
-
const touchReach = piece.touch ? Math.hypot(piece.touch.pullX, piece.touch.pullY) + piece.touch.waveAmp * 2 + Math.max(piece.geometry.width, piece.geometry.height) * 0.05 * piece.touch.press : 0;
|
|
1978
|
-
const padPx = (lensPadDp(piece.geometry, piece.optics) + surfacePadDp(piece.geometry, 0) + morphReach + touchReach) * options.density;
|
|
1979
|
-
const left = Math.max(0, Math.floor(piece.centerX - halfWidth - padPx));
|
|
1980
|
-
const right = Math.min(width, Math.ceil(piece.centerX + halfWidth + padPx));
|
|
1981
|
-
const top = Math.max(0, Math.floor(height - (piece.centerY + halfHeight + padPx)));
|
|
1982
|
-
const bottom = Math.min(height, Math.ceil(height - (piece.centerY - halfHeight - padPx)));
|
|
1983
|
-
gl.enable(gl.SCISSOR_TEST);
|
|
1984
|
-
gl.scissor(left, top, Math.max(0, right - left), Math.max(0, bottom - top));
|
|
1985
|
-
gl.useProgram(lensProgram);
|
|
1986
|
-
bindTextureAt(gl, 0, contentTexture, lensProgram, "content");
|
|
1987
|
-
setUniform(gl, lensLoc("u_contentSize"), [width, height]);
|
|
1988
|
-
setUniform(gl, lensLoc("u_resolution"), [width, height]);
|
|
1989
|
-
setUniform(gl, lensLoc("u_center"), [piece.centerX, piece.centerY]);
|
|
1990
|
-
setUniform(gl, lensLoc("u_reach"), Math.max(width, height));
|
|
1991
|
-
setUniform(gl, lensLoc("u_contentMin"), [1, 1]);
|
|
1992
|
-
setUniform(gl, lensLoc("u_contentMax"), [width - 1, height - 1]);
|
|
1993
|
-
if (stats) {
|
|
1994
|
-
setUniform(gl, lensLoc("u_probeLuma"), stats.luma);
|
|
1995
|
-
setUniform(gl, lensLoc("u_probeBusy"), stats.busy);
|
|
1996
|
-
setUniform(gl, lensLoc("u_probeRange"), [stats.lo, stats.hi]);
|
|
1997
|
-
setUniform(gl, lensLoc("u_probeSlope"), [stats.slopeX, stats.slopeY]);
|
|
1998
|
-
setUniform(gl, lensLoc("u_probe"), [stats.r, stats.g, stats.b]);
|
|
2010
|
+
return gpuTimer.time(() => {
|
|
2011
|
+
if (options.backdrop) {
|
|
2012
|
+
const fbo = ensureBackdropFbo();
|
|
2013
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
|
2014
|
+
gl.viewport(0, 0, width, height);
|
|
2015
|
+
options.backdrop(gl, { framebuffer: fbo, width, height });
|
|
2016
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
2017
|
+
gl.viewport(0, 0, width, height);
|
|
2018
|
+
gl.disable(gl.BLEND);
|
|
2019
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
2020
|
+
gl.disable(gl.DEPTH_TEST);
|
|
2021
|
+
gl.disable(gl.CULL_FACE);
|
|
2022
|
+
gl.disable(gl.STENCIL_TEST);
|
|
2023
|
+
gl.colorMask(true, true, true, true);
|
|
2024
|
+
gl.depthMask(true);
|
|
2025
|
+
gl.bindVertexArray(null);
|
|
2026
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
2027
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
2028
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
2029
|
+
gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL);
|
|
1999
2030
|
} else {
|
|
2000
|
-
|
|
2031
|
+
ctx.clearRect(0, 0, width, height);
|
|
2032
|
+
options.scene?.(ctx, width, height, options.offsetX ?? 0, options.offsetY ?? 0, options.density);
|
|
2033
|
+
gl.bindTexture(gl.TEXTURE_2D, contentTexture);
|
|
2034
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, sceneCanvas);
|
|
2035
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
2001
2036
|
}
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
gl.
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
morph2: piece.morph2,
|
|
2019
|
-
bodyInLens: true,
|
|
2020
|
-
touch: piece.touch,
|
|
2021
|
-
progress: piece.progress,
|
|
2022
|
-
// The shadow is denser over text, weaker over a flat backdrop (M 11:47). The rule lives
|
|
2023
|
-
// in the core: while it lived here, Android didn't have it at all.
|
|
2024
|
-
shadow: stats ? shadowOpacity(stats.busy) : 1,
|
|
2025
|
-
// The probe has already computed the ambient color — the shadow gets it for free. Taken
|
|
2026
|
-
// as-is: `settleStats` has already smoothed it, and rounding would bring back exactly the
|
|
2027
|
-
// steps the smoothing was written to remove.
|
|
2028
|
-
ambient: stats ? [stats.r, stats.g, stats.b] : void 0,
|
|
2029
|
-
appear: piece.appear,
|
|
2030
|
-
lift: piece.lift
|
|
2031
|
-
});
|
|
2032
|
-
applyObject(gl, surfaceLoc, {
|
|
2033
|
-
...toDeviceSurfaceUniforms(rawSurface, options.density),
|
|
2034
|
-
u_center: [piece.centerX, piece.centerY]
|
|
2035
|
-
});
|
|
2036
|
-
setUniform(gl, surfaceLoc(DYNAMIC_UNIFORMS[0]), piece.press ?? 0);
|
|
2037
|
-
setUniform(gl, surfaceLoc(DYNAMIC_UNIFORMS[1]), piece.active ?? 0);
|
|
2038
|
-
const hasIcon = Boolean(piece.icon && options.iconMask);
|
|
2039
|
-
setUniform(gl, surfaceLoc(ICON_UNIFORMS[0]), hasIcon ? 1 : 0);
|
|
2040
|
-
setUniform(gl, surfaceLoc(ICON_UNIFORMS[1]), 1);
|
|
2041
|
-
setUniform(gl, surfaceLoc(ICON_UNIFORMS[2]), piece.inkIdle ?? [1, 1, 1, 1]);
|
|
2042
|
-
setUniform(gl, surfaceLoc(ICON_UNIFORMS[3]), piece.inkActive ?? [1, 1, 1, 1]);
|
|
2043
|
-
bindTextureAt(gl, 1, iconTexture, surfaceProgram, "u_icon");
|
|
2044
|
-
setUniform(gl, surfaceLoc("u_iconSize"), hasIcon ? [width, height] : [1, 1]);
|
|
2045
|
-
const hasColor = Boolean(piece.overlay && options.colorLayer);
|
|
2046
|
-
setUniform(gl, surfaceLoc(OVERLAY_UNIFORMS[0]), hasColor ? 1 : 0);
|
|
2047
|
-
bindTextureAt(gl, 2, colorTexture, surfaceProgram, "u_overlay");
|
|
2048
|
-
setUniform(gl, surfaceLoc("u_overlaySize"), hasColor ? [width, height] : [1, 1]);
|
|
2049
|
-
setUniform(gl, surfaceLoc("u_resolution"), [width, height]);
|
|
2037
|
+
if (options.colorLayer) {
|
|
2038
|
+
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
|
|
2039
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.colorLayer);
|
|
2040
|
+
}
|
|
2041
|
+
if (options.iconMask) {
|
|
2042
|
+
gl.bindTexture(gl.TEXTURE_2D, iconTexture);
|
|
2043
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.iconMask);
|
|
2044
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
2045
|
+
}
|
|
2046
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
2047
|
+
gl.viewport(0, 0, width, height);
|
|
2048
|
+
gl.disable(gl.BLEND);
|
|
2049
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
2050
|
+
gl.useProgram(blitProgram);
|
|
2051
|
+
bindTextureAt(gl, 0, contentTexture, blitProgram, "u_content");
|
|
2052
|
+
setUniform(gl, blitLoc("u_resolution"), [width, height]);
|
|
2050
2053
|
drawFullscreenTriangle(gl);
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
+
const probes = [];
|
|
2055
|
+
for (let index = 0; index < options.pieces.length; index += 1) {
|
|
2056
|
+
const piece = options.pieces[index];
|
|
2057
|
+
const halfWidth = piece.geometry.width * options.density / 2;
|
|
2058
|
+
const halfHeight = piece.geometry.height * options.density / 2;
|
|
2059
|
+
const rect = {
|
|
2060
|
+
centerX: piece.centerX,
|
|
2061
|
+
centerY: piece.centerY,
|
|
2062
|
+
halfWidth,
|
|
2063
|
+
halfHeight
|
|
2064
|
+
};
|
|
2065
|
+
const fresh = index === 0 ? probe.sample(contentTexture, width, height, rect) : probe.statsFor(width, height, rect);
|
|
2066
|
+
const stats = fresh ? settleStats(index, fresh) : null;
|
|
2067
|
+
probes.push(stats);
|
|
2068
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
2069
|
+
gl.viewport(0, 0, width, height);
|
|
2070
|
+
gl.enable(gl.BLEND);
|
|
2071
|
+
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
2072
|
+
const reachOf = (m) => m ? Math.hypot(m.offsetX, m.offsetY) + Math.max(m.width, m.height) / 2 + m.smoothing : 0;
|
|
2073
|
+
const morphReach = Math.max(reachOf(piece.morph), reachOf(piece.morph2));
|
|
2074
|
+
const touchReach = piece.touch ? Math.hypot(piece.touch.pullX, piece.touch.pullY) + piece.touch.waveAmp * 2 + Math.max(piece.geometry.width, piece.geometry.height) * 0.05 * piece.touch.press : 0;
|
|
2075
|
+
const padPx = (lensPadDp(piece.geometry, piece.optics) + surfacePadDp(piece.geometry, 0) + morphReach + touchReach) * options.density;
|
|
2076
|
+
const left = Math.max(0, Math.floor(piece.centerX - halfWidth - padPx));
|
|
2077
|
+
const right = Math.min(width, Math.ceil(piece.centerX + halfWidth + padPx));
|
|
2078
|
+
const top = Math.max(0, Math.floor(height - (piece.centerY + halfHeight + padPx)));
|
|
2079
|
+
const bottom = Math.min(height, Math.ceil(height - (piece.centerY - halfHeight - padPx)));
|
|
2080
|
+
gl.enable(gl.SCISSOR_TEST);
|
|
2081
|
+
gl.scissor(left, top, Math.max(0, right - left), Math.max(0, bottom - top));
|
|
2082
|
+
gl.useProgram(lensProgram);
|
|
2083
|
+
bindTextureAt(gl, 0, contentTexture, lensProgram, "content");
|
|
2084
|
+
setUniform(gl, lensLoc("u_contentSize"), [width, height]);
|
|
2085
|
+
setUniform(gl, lensLoc("u_resolution"), [width, height]);
|
|
2086
|
+
setUniform(gl, lensLoc("u_center"), [piece.centerX, piece.centerY]);
|
|
2087
|
+
setUniform(gl, lensLoc("u_reach"), Math.max(width, height));
|
|
2088
|
+
setUniform(gl, lensLoc("u_contentMin"), [1, 1]);
|
|
2089
|
+
setUniform(gl, lensLoc("u_contentMax"), [width - 1, height - 1]);
|
|
2090
|
+
if (stats) {
|
|
2091
|
+
setUniform(gl, lensLoc("u_probeLuma"), stats.luma);
|
|
2092
|
+
setUniform(gl, lensLoc("u_probeBusy"), stats.busy);
|
|
2093
|
+
setUniform(gl, lensLoc("u_probeRange"), [stats.lo, stats.hi]);
|
|
2094
|
+
setUniform(gl, lensLoc("u_probeSlope"), [stats.slopeX, stats.slopeY]);
|
|
2095
|
+
setUniform(gl, lensLoc("u_probe"), [stats.r, stats.g, stats.b]);
|
|
2096
|
+
} else {
|
|
2097
|
+
setUniform(gl, lensLoc("u_probeLuma"), -1);
|
|
2098
|
+
}
|
|
2099
|
+
const lens = toLensProps(piece.optics, piece.geometry, options.density, {
|
|
2100
|
+
debug: options.debug,
|
|
2101
|
+
morph: piece.morph,
|
|
2102
|
+
morph2: piece.morph2,
|
|
2103
|
+
touch: piece.touch,
|
|
2104
|
+
progress: piece.progress,
|
|
2105
|
+
light: piece.light,
|
|
2106
|
+
appear: piece.appear,
|
|
2107
|
+
accent: piece.accent
|
|
2108
|
+
});
|
|
2109
|
+
applyChannel(gl, lensLoc, lens.uniformNames, lens.uniformSizes, lens.uniformValues);
|
|
2110
|
+
drawFullscreenTriangle(gl);
|
|
2111
|
+
gl.useProgram(surfaceProgram);
|
|
2112
|
+
const rawSurface = toSurfaceUniforms(piece.optics, piece.geometry, {
|
|
2113
|
+
debug: options.debug,
|
|
2114
|
+
morph: piece.morph,
|
|
2115
|
+
morph2: piece.morph2,
|
|
2116
|
+
bodyInLens: true,
|
|
2117
|
+
touch: piece.touch,
|
|
2118
|
+
progress: piece.progress,
|
|
2119
|
+
// The shadow is denser over text, weaker over a flat backdrop (M 11:47). The rule lives
|
|
2120
|
+
// in the core: while it lived here, Android didn't have it at all.
|
|
2121
|
+
shadow: stats ? shadowOpacity(stats.busy) : 1,
|
|
2122
|
+
// The probe has already computed the ambient color — the shadow gets it for free. Taken
|
|
2123
|
+
// as-is: `settleStats` has already smoothed it, and rounding would bring back exactly the
|
|
2124
|
+
// steps the smoothing was written to remove.
|
|
2125
|
+
ambient: stats ? [stats.r, stats.g, stats.b] : void 0,
|
|
2126
|
+
appear: piece.appear,
|
|
2127
|
+
lift: piece.lift
|
|
2128
|
+
});
|
|
2129
|
+
applyObject(gl, surfaceLoc, {
|
|
2130
|
+
...toDeviceSurfaceUniforms(rawSurface, options.density),
|
|
2131
|
+
u_center: [piece.centerX, piece.centerY]
|
|
2132
|
+
});
|
|
2133
|
+
setUniform(gl, surfaceLoc(DYNAMIC_UNIFORMS[0]), piece.press ?? 0);
|
|
2134
|
+
setUniform(gl, surfaceLoc(DYNAMIC_UNIFORMS[1]), piece.active ?? 0);
|
|
2135
|
+
const hasIcon = Boolean(piece.icon && options.iconMask);
|
|
2136
|
+
setUniform(gl, surfaceLoc(ICON_UNIFORMS[0]), hasIcon ? 1 : 0);
|
|
2137
|
+
setUniform(gl, surfaceLoc(ICON_UNIFORMS[1]), 1);
|
|
2138
|
+
setUniform(gl, surfaceLoc(ICON_UNIFORMS[2]), piece.inkIdle ?? [1, 1, 1, 1]);
|
|
2139
|
+
setUniform(gl, surfaceLoc(ICON_UNIFORMS[3]), piece.inkActive ?? [1, 1, 1, 1]);
|
|
2140
|
+
bindTextureAt(gl, 1, iconTexture, surfaceProgram, "u_icon");
|
|
2141
|
+
setUniform(gl, surfaceLoc("u_iconSize"), hasIcon ? [width, height] : [1, 1]);
|
|
2142
|
+
const hasColor = Boolean(piece.overlay && options.colorLayer);
|
|
2143
|
+
setUniform(gl, surfaceLoc(OVERLAY_UNIFORMS[0]), hasColor ? 1 : 0);
|
|
2144
|
+
bindTextureAt(gl, 2, colorTexture, surfaceProgram, "u_overlay");
|
|
2145
|
+
setUniform(gl, surfaceLoc("u_overlaySize"), hasColor ? [width, height] : [1, 1]);
|
|
2146
|
+
setUniform(gl, surfaceLoc("u_resolution"), [width, height]);
|
|
2147
|
+
drawFullscreenTriangle(gl);
|
|
2148
|
+
}
|
|
2149
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
2150
|
+
return { probes };
|
|
2151
|
+
});
|
|
2054
2152
|
}
|
|
2055
2153
|
function destroy() {
|
|
2056
2154
|
probe.destroy();
|
|
@@ -2060,13 +2158,24 @@ function createVireGlassRenderer(canvas) {
|
|
|
2060
2158
|
gl.deleteTexture(contentTexture);
|
|
2061
2159
|
gl.deleteTexture(iconTexture);
|
|
2062
2160
|
gl.deleteTexture(colorTexture);
|
|
2161
|
+
if (backdropFbo) gl.deleteFramebuffer(backdropFbo);
|
|
2162
|
+
gpuTimer.destroy();
|
|
2063
2163
|
}
|
|
2064
|
-
return { resize, render, destroy };
|
|
2164
|
+
return { resize, render, getLastGpuMs: gpuTimer.getLastMs, destroy };
|
|
2065
2165
|
}
|
|
2066
2166
|
|
|
2167
|
+
exports.FULLSCREEN_TRIANGLE_VERTEX_SOURCE = FULLSCREEN_TRIANGLE_VERTEX_SOURCE;
|
|
2067
2168
|
exports.PROBE_GRID_HEIGHT = PROBE_GRID_HEIGHT;
|
|
2068
2169
|
exports.PROBE_GRID_WIDTH = PROBE_GRID_WIDTH;
|
|
2170
|
+
exports.bindTextureAt = bindTextureAt;
|
|
2171
|
+
exports.compileShader = compileShader;
|
|
2172
|
+
exports.createFramebuffer = createFramebuffer;
|
|
2069
2173
|
exports.createProbe = createProbe;
|
|
2174
|
+
exports.createProgram = createProgram;
|
|
2175
|
+
exports.createTexture = createTexture;
|
|
2070
2176
|
exports.createVireGlassRenderer = createVireGlassRenderer;
|
|
2177
|
+
exports.drawFullscreenTriangle = drawFullscreenTriangle;
|
|
2178
|
+
exports.locationCache = locationCache;
|
|
2179
|
+
exports.setUniform = setUniform;
|
|
2071
2180
|
//# sourceMappingURL=index.cjs.map
|
|
2072
2181
|
//# sourceMappingURL=index.cjs.map
|