From 8844748edf6c9207aa65e5804514c4bac367b15d Mon Sep 17 00:00:00 2001 From: foxnne Date: Sun, 18 Oct 2026 14:52:09 -0500 Subject: [PATCH 1/7] core: glass that merges with its place's edge, and stays inside it `LiquidField.edge`: a place's outline (rect and corner radii). Its outside is one more shape the field's shapes run into, so a shape near the outline joins the edge by a neck, metaball-like, and the outline cuts the glass's coverage, so none of it crosses. The rim and bend come from the joined shape, so there is no rim along the cut. The edge takes the program's last shape slot, flagged 2, so the uniforms keep their layout. `glassBubbles` takes it as `.edge`. Co-Authored-By: Claude Opus 5.5 --- core/dialogs.zig | 32 +- core/gfx/LiquidField.zig | 85 ++- .../compiled/dxil/liquid_glass.fragment.dxil | Bin 9072 -> 9864 bytes .../compiled/glsl/liquid_glass.fragment.glsl | 667 +++++++++++------- .../compiled/msl/liquid_glass.fragment.msl | 655 ++++++++++------- .../compiled/spv/liquid_glass.fragment.spv | Bin 15488 -> 18368 bytes core/gfx/shaders/liquid_glass.fragment.hlsl | 26 +- tests/integration.zig | 29 + 8 files changed, 954 insertions(+), 540 deletions(-) diff --git a/core/dialogs.zig b/core/dialogs.zig index 52cdfca54..242e72d1f 100644 --- a/core/dialogs.zig +++ b/core/dialogs.zig @@ -246,6 +246,11 @@ pub const Bubble = struct { /// `radius`: glass cut to fit a corner of something else, as a place's corner is to its card's /// curve. Each is held to half the shorter side. radii: ?[4]f32 = null, + /// How much of a lens it is, 0…1: its edge's bend, clearing, light and dark line + /// (`LiquidField.Shape.lens`). Less, it lies flatter in what it is over. + lens: f32 = 1, + /// How frosted it is, 0 (what is behind, sharp) to 1 (`LiquidField.Shape.blur`). + blur: f32 = 1, fn cornerRadius(self: Bubble) f32 { const half = @min(self.rect.w, self.rect.h) / 2; @@ -278,6 +283,15 @@ pub const BubblesOptions = struct { /// A signature of what lies under the bubbles, as `frostPaneKept`'s: the frost is read again /// only when it changes. Null reads it every frame. witness: ?u64 = null, + /// The place the bubbles belong to (`LiquidField.edge`): a bubble near its outline joins it by a + /// neck, and none crosses it. Physical. No shadow rings with it (`shadow`). Without the glass + /// program, the bubbles are only cut to its rect. + edge: ?widgets.LiquidField.Edge = null, + /// The glass's colour and how much of it, over the dialog style's (`LiquidField.surface`): for + /// glass that must read as part of a surface, as a place's corner at rest does its card's fill. + /// Without the glass program, the fill it falls back to. + tint: ?dvui.Color = null, + mix: f32 = 1, }; /// Controls of glass over content, one look wherever they float: the demo player's transport, a @@ -289,34 +303,44 @@ pub const BubblesOptions = struct { pub fn glassBubbles(id: dvui.Id, bubbles: []const Bubble, scale: f32, opts: BubblesOptions) void { const bs = surfaceShadow(); // The ring after the glass, so the glass never blurs it in. - defer if (opts.shadow > 0) for (bubbles) |b| { + // None for bubbles merged with an edge: a ring round each bubble's own outline would lie across + // its neck, splitting it from the edge it is one glass with. + defer if (opts.shadow > 0 and opts.edge == null) for (bubbles) |b| { if (b.rect.w < 1 or b.rect.h < 1) continue; glassShadow(b.rect, b.corners(scale), scale, bs, opts.shadow); }; if (widgets.LiquidField.ready()) { if (widgets.liquidFrost()) |frost| { - var field: widgets.LiquidField = .{ .merge_px = opts.merge * scale, .scale = scale }; + var field: widgets.LiquidField = .{ .merge_px = opts.merge * scale, .scale = scale, .edge = opts.edge }; for (bubbles) |b| { if (b.rect.w < 1 or b.rect.h < 1) continue; // A capsule's edge bends round as a drop's does; a rounded box's keeps to its sides. const capsule = b.radius == null and b.radii == null; const radii: [4]f32 = if (capsule) @splat(@min(b.rect.w, b.rect.h)) else b.cornerRadii(); - field.add(.{ .rect = b.rect, .radii = radii, .round = capsule, .light = b.light }); + field.add(.{ .rect = b.rect, .radii = radii, .round = capsule, .light = b.light, .lens = b.lens, .blur = b.blur }); } var pane = frost; pane.form = 1; + if (opts.tint) |t| { + pane.tint = t; + pane.mix = opts.mix; + field.surface = true; + } pane.witness = opts.witness; widgets.BlurBackdrop.fieldPane(id, field, scale, pane); return; } } + const prev_clip = dvui.clipGet(); + defer dvui.clipSet(prev_clip); + if (opts.edge) |e| dvui.clipSet(prev_clip.intersect(e.rect)); for (bubbles, 0..) |b, i| { if (b.rect.w < 1 or b.rect.h < 1) continue; const round = b.corners(scale); const physical = round.scale(scale, dvui.CornerRect.Physical); const piece = id.update(std.mem.asBytes(&i)); const frosted = if (opts.witness) |w| frostPaneKept(piece, b.rect, round, scale, w) else frostPane(piece, b.rect, round, scale); - if (!frosted) b.rect.fill(physical, .{ .color = .{ .color = dialogFill() }, .fade = 1 }); + if (!frosted) b.rect.fill(physical, .{ .color = .{ .color = if (opts.tint) |t| t.opacity(opts.mix) else dialogFill() }, .fade = 1 }); if (b.light >= bubble_press) { b.rect.fill(physical, .{ .color = .{ .color = rowPress() }, .fade = 1 }); } else if (b.light > 0) { diff --git a/core/gfx/LiquidField.zig b/core/gfx/LiquidField.zig index b216613a5..b246a66a1 100644 --- a/core/gfx/LiquidField.zig +++ b/core/gfx/LiquidField.zig @@ -63,6 +63,12 @@ pub const Shape = struct { shapes: [max_shapes]Shape = undefined, len: usize = 0, +/// The place the glass belongs to: a view's card, rounded or square. Its outside is one more +/// shape the others run into, so a shape near its outline joins the edge by a neck, metaball-like, +/// and its outline cuts the glass, so none of it crosses. Never drawn on its own: only the shapes, +/// and their necks where they come near the edge. It takes the program's last shape slot, so a +/// field with it holds one shape fewer. +edge: ?Edge = null, /// Physical pixels: how far apart two shapes still bridge. 0 never joins. merge_px: f32 = 0, scale: f32 = 1, @@ -77,11 +83,25 @@ refraction: f32 = 1, /// It carries text — a dialog, a menu — and takes the window's colour as the opacity has it /// (`glass_look.forText`). text: bool = false, +/// Its tint and mix are its own, kept over the look the app publishes (`publishLook`): glass that +/// must read as part of a surface, as a place's corner at rest does its card's fill. +surface: bool = false, /// It is a lens over a picture of the caller's own (`drawPicture`): its middle the picture as it /// is, only the band along its edge glass (`glass_look.forLens`). lens: bool = false, +/// A place's outline (`edge`), physical: its rect and its corner radii, top-left, top-right, +/// bottom-right, bottom-left. +pub const Edge = struct { + rect: dvui.Rect.Physical, + radii: [4]f32 = @splat(0), +}; + +/// Pixels past its place's outline the outside of the place begins: `EDGE_GAP` in the program. +const edge_gap: f32 = 1; + pub fn add(self: *LiquidField, shape: Shape) void { + if (self.len >= max_shapes - 1 and self.edge != null) return; if (self.len >= max_shapes) return; self.shapes[self.len] = shape; self.len += 1; @@ -91,7 +111,15 @@ pub fn add(self: *LiquidField, shape: Shape) void { pub fn bounds(self: *const LiquidField) dvui.Rect.Physical { var r: dvui.Rect.Physical = .{}; for (self.shapes[0..self.len], 0..) |s, i| r = if (i == 0) s.rect else r.unionWith(s.rect); - return r.outsetAll(self.merge_px * 0.25 + 1); + return self.withReach(r); +} + +/// `r`, some shapes' rect, with room for what joins them: the bridges between them, and with an +/// edge, the neck to it (as far as a shape can be from the outline and still join it, half the +/// merge width), kept inside the place. +fn withReach(self: *const LiquidField, r: dvui.Rect.Physical) dvui.Rect.Physical { + const e = self.edge orelse return r.outsetAll(self.merge_px * 0.25 + 1); + return r.outsetAll(self.merge_px * 0.5 + 1).intersect(e.rect.outsetAll(1)); } // ── Groups ────────────────────────────────────────────────────────────────────────────────────── @@ -223,6 +251,16 @@ pub fn pack(self: *const LiquidField, covered: dvui.Rect.Physical, has_sharp: bo .{ sh.blur, sh.lens, sh.light, if (sh.round) 1 else 0 }, }; } + if (self.edge) |e| { + // In the last slot, flagged 2: the program reads it there, whatever the groups. Its glass is + // the first shape's, where they meet. + const first: Shape = if (self.len > 0) self.shapes[0] else .{ .rect = e.rect }; + u.shapes[max_shapes - 1] = .{ + .{ e.rect.x + e.rect.w / 2, e.rect.y + e.rect.h / 2, e.rect.w / 2, e.rect.h / 2 }, + e.radii, + .{ first.blur, first.lens, 0, 2 }, + }; + } return u; } @@ -304,9 +342,12 @@ fn applyLook(self: *const LiquidField, u: *Uniforms, l: glass_look.InApp) void { u.backdrop[3] = l.bevel_cap * self.scale; u.rim[1] = l.clarity * bend_scale; u.rim[3] = l.rim; - u.face[0] = std.math.clamp(l.mix, 0, 1); - u.face[1] = std.math.clamp(l.lift, 0, 1); + if (!self.surface) { + u.face[0] = std.math.clamp(l.mix, 0, 1); + u.face[1] = std.math.clamp(l.lift, 0, 1); + } for (u.shapes[0..self.len]) |*sh| sh[2][0] *= l.frost; + if (self.edge != null) u.shapes[max_shapes - 1][2][0] *= l.frost; } /// Whether `draw` would draw now: programs here, switched on, compiled. @@ -403,7 +444,8 @@ fn quads(self: *const LiquidField, groups: Clusters, glass: bool, frost: dvui.Te const n = groups.size[g]; var r = self.shapes[groups.order[first]].rect; for (groups.order[first..][0..n]) |i| r = r.unionWith(self.shapes[i].rect); - r = r.outsetAll(self.merge_px * 0.25 + 1); + r = self.withReach(r); + if (r.empty()) continue; const col: dvui.Color.PMA = .{ .r = if (glass) 255 else 0, .g = first, .b = n, .a = 255 }; const base: dvui.Vertex.Index = @intCast(4 * g); for ([_]dvui.Point.Physical{ r.topLeft(), r.topRight(), r.bottomRight(), r.bottomLeft() }) |p| { @@ -474,6 +516,37 @@ pub fn sample(self: *const LiquidField, p: dvui.Point.Physical) Sample { o2 = o; } } + // The place (`edge`): its outside one more shape, its outline cutting the coverage. + var in_place: f32 = -big; + if (self.edge) |e| { + const c: [2]f32 = .{ e.rect.x + e.rect.w / 2, e.rect.y + e.rect.h / 2 }; + const half: [2]f32 = .{ e.rect.w / 2, e.rect.h / 2 }; + const lim = @min(half[0], half[1]); + const radii: [4]f32 = .{ @min(e.radii[0], lim), @min(e.radii[1], lim), @min(e.radii[2], lim), @min(e.radii[3], lim) }; + var g: [2]f32 = undefined; + in_place = roundBox(.{ p.x - c[0], p.y - c[1] }, half, radii, &g); + const d = edge_gap - in_place; + const mat: [3]f32 = .{ m1[0], m1[1], 0 }; + if (d < d1) { + d2 = d1; + m2 = m1; + d1 = d; + m1 = mat; + } else if (d < d2) { + d2 = d; + m2 = mat; + } + const o: [2]f32 = .{ -g[0], -g[1] }; + if (d < f1) { + f2 = f1; + o2 = o1; + f1 = d; + o1 = o; + } else if (d < f2) { + f2 = d; + o2 = o; + } + } const k = @max(self.merge_px, 0.0001); var wm: f32 = 0; var wf: f32 = 0; @@ -481,8 +554,8 @@ pub fn sample(self: *const LiquidField, p: dvui.Point.Physical) Sample { const f = smin(f1, f2, k, &wf); const soft = @max(0, -f); return .{ - .d = d, - .coverage = std.math.clamp(0.5 - d, 0, 1), + .d = @max(d, in_place), + .coverage = std.math.clamp(0.5 - @max(d, in_place), 0, 1), .out = .{ .x = std.math.lerp(o1[0], o2[0], wf), .y = std.math.lerp(o1[1], o2[1], wf) }, .steep = @exp(-soft / liquid_glass.depthPx(self.look(), rim)), .blur = std.math.lerp(m1[0], m2[0], wm), diff --git a/core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil b/core/gfx/shaders/compiled/dxil/liquid_glass.fragment.dxil index 24e69dce5d757cbc2759632ffedf993c985066f3..45e7841b789e0696277b0ae917b0d021dc77b570 100644 GIT binary patch delta 5292 zcmY*ddsq|Kwx5I~Oh|x)=Ri;rULsITAOfPM31HL$ijUHuR0o29l>&WyR&9=XAx#xX zcnY3sL$Ov{twd-qP_;=2LIng1^29F z7NIKLKSh+3{K0*X3!8dET|12W$3#$9|rnUn_4ql2oXS|mG zzYl7JnGb~Vq41ZQ$IzPBC01lhyP`$Cw6vTxq0L!5HXleA3$9n+GKuSe>b!` z9ciYq^L-UQmy$tbQ(0RM$cEG@5Ca-cHv z^lPtFFMC#V%r`}}F^wGnK`6Kk)fs|pYY5zo2G$7JPfFI`V^JZ<$+_*2%g>~+R3Zd< z5?W{!0tBrCJf}zXFytMYK}zx3%RlG6qth!zSC>}%0X2)gp= z9;AXUW5UMo*+NLc3|ANs!M+=-<|$$V3syt`V*7(nMgURvAhG0BH-dBfV``z&cH+zX zh(S{RZ~<^e?E3T)kf~s*4vPs)Sg~MRXr&(+5FD&vz5pCxGh|Vb6jq>YVd*cA$KGAs zWrkPKXLSC*->2#k+=)k*50VzQEp~CzU~m}-3MP>u1={)zo}t1=wk9pDKtdo`VR+#3 zHrwUA-sKQGR|7F&(w)c-!O{*zJA8pGw$?R`&HM#wu9IhwjkhMFcIK8+evJxol1g`RknD*z8t z4p)u{scsO#pv@xhW>(RaPLP0ID4Ozx^Xw;1G(gTlew+k|Mo?Z0HP&W$vD`LH*YD;3$VT zXb67x({L2O_J&uSbj%odb}y#yTj*n%FJ{v_6iw@)9E|WkFuC71jb;`rn1K|6wE{sv z5(&I~SJ21kq*)G2HdLBoaW=*G4$F|_`whY81sJV6< z{PtSP*0dTIZCC?rm^iyGHlrU}_Y-Er7qgy!TI@E^T(?v4A-+RVN^TBj!?|+NgfeQ; z1_Kw^P_rv##PyTtq7m1xwUlv8#Km`NxB77XyBQ@MEE|TOlClGO>}2j$dac&Sm-PJF zo9!tjwO4$W2S4KYn^ba}3sPnTFJB*TdHMPzr70??S$FEnCwh;fZ#G=Ha(seM^B5VC9u-s17bJ>9@&5ot=_mY zgTrh%0VFu(c5YmGS36FKxvMp&#M~VoFU#c!fn1}DKY&LU|fJ=nzUf!x3XB{7`z>4;yIu8OdKc5MpdaP8Pu67sIi#omeVDG zRdgp?-Krj%($8S8Gv@R#&(qdMeot>1o)?B#{HprKl;G5p?zNyg2iK}fx>&DH1S3`K ziDYYeaD|n2)#l}~4%EcN0jM~&WxkrQQG++ zNc4XWpJDu4NzW&D$&bo!!^PU%j5r0ue(eXZ8_P~O?o-bpzYt^PEb{ZDq+o<kP9+pMeFkc^Oy;W+k?Tm;18B zKj_iCe+Oqv%7a68Ft28nvmqs#kb8+Z_0MAFAt01_SkPG9`fg66G!#|>F_6=DR**4H z)TdKG0+ro*tfWgYOU&yM;6BRZPWs4xE0_6p+$_rmo>AV5kui|rKj5nFQ(JWG{qwi-MwM*SkQml3i(aa7yWo>+Zb!?K^4^K2~v3(Rry zaqaes@Tu5b)CQV2`?YCkGVN14yROg4UtPODWD<&vlb?|JHaHf7fDOL(@a7$!Lq z`s7~UUpWsl&EoaUK!(DKyxvn_(%Tx)XQy;c`p=j=JDv@f4PGX+<{DB0DfoF&M&z7d z)kNG>>`wYr>|Gkwhyhk%%L)q8_%%oXPJ-0dbcAnPL;LEn!nk#9x8hBZ8vEDH=lk`c zRE0fbMdM-lZ{;`0sO7bE&yUyu~H0r%dNznl( z>zIXP(M`Heh!2)J&+686D6NeleZ65IDQ9q!v@s{paRMMP{|H9n*|E;=qj!+L-ZCaH zbA>>Hr7`(rll-{;NvLWFLt^QBv5$X@Zz0RkWPTv!={j(25|}Ay^_zwqeY)Chs4sOI zAiBO#K|`*~?j5iCnb3=Qv7;ppcOCH=?%f{du#fxQNSyq-?ONbg-K$GHTL%j9 z!6FT5PB)=Z)5|1LpxYY!z%^qv>OD!4D9hHiF*%3oPUoT$3 z^SLaSq#baYZ={hd92RxL#tZ;{56R>Q`eB_Pt$>}u(aQa-w{2?_C29AQR%+zib4W4v z?&$iwaT7ZZOODZ@->7-9c0%Rz(^V)SNNt*j$JGzvg4kS(Ek84#n>O99sHB}A{Mod5 zq5Z~f%$F5mRAV8YgWay-Ro#1W-^8AvQM>85ark~zvj15D8GhCSKA*OmJKBli>3X!kVW%{G%~H=BzH34=QzWycu-wQBNdd&XZLyz!xz>I&xD@ zTa6J^Tb{$0FyHwgv+gq;Ztsp&J+{!?C)G0*VNzQnvN4rnq1Dq;<%;`96zVizfvf>} zBHtqn%3Iy847l9AIyXf4Lb)2GH~R#WiivNn{);drwxzq7&ugO=TS%ptn|&C~8NbiC zt^8PR)zbZ|HMY$8f4YL#hj?k#uF}6L3tb=0tpM9`$4>* zZn}H{mfn9~xzLz|O^k!#KMgN8tC!&ZQRGc{hQtgPU?92edh@H(f&1{!#=_yJoC57o zp&}CQrVIXp?-$~upb=N_)THKG);DYt*_7S|Eco zF9Kb4Q@BbUDwjy3jwq}meP2NX(+zY3oLhT5+s9=}9GBxc*Hm2k5npNt8|IzQSH0G} z!#^FyGscp5KFFL7)nK^8HRr82P%G{C+qUU|*QkbgN|I(VihzwZW0of6kZFJIFbYRBG+@x8TgFAbHF&+STd9f3kK{Ox@`|MXkS zHNpMCEC<%qjlM#BpKI?K^o@Uda!$xxjHM~1>y2;h@jCl_%ZFe#lDn2=kybS-bGXgk zgw?rZo=;s308grIoO)|{wM$e&=)5R`IIGLisEGBORKBF*M+{iS-)z>IH@A=t=)r1VFp+46uulZ;zMnP= zI2cy#d_M9IOAW9ypF9IT&}a|&E1VKrcLO!@)qTCsWbRX9L4c`O@qtE7{vGtedjej$ zb1M``Ny71byeyox8lqLicCqLtWa?SmEBao=R+*Yemdva&i!)e($4K*yDTacENw>L) z~iv`L?AVjDrq?jLqOT)LS=Nem4mBKI^x~*XKKTWz6>F7i(Pw;%GOIiTI@0K$ zuBkOlhk)^A-%AJ1^nZhIbN;TzmUH>!q#O`SzC2U;Dw1whqg^cYs}4k(ZvEKMuu`M; z#z~4$@5PGU5emZP>}dYWb$48*-?GDlzi0mi{nt@^qP0;5;*0a1aI~?bv$r&FU{HBp zRN{JQC6-Lb=5Mt+2<>Af7x25W`u!z$a-H=jNB*q{+huZ4oW#GZlik8P1xL6mr4Tz3 zEH*OZTCJx46%-iE7xIK<6(CG0AWZxuT!~W* z_>V-BF?>fx1$Y_aWsPHksTxNndIu}%N&@&S;y(Sc0^sNx)P~u!xE<)oCDXjtoXcMUBmd7$r(D zYN;D2OhL!mAhwm(c}!R&EGljtYOO|^p=jxRfYc|reBXCMZhW5RkKA+4@4V}I-}k&n zpEW57&d3v|msHIU?L>OyFV6ZHB93hQ3nTyf0ppWS+tC~Uy5E2B=#4DG)eiw}gIz;&Y!=ypM%et~Ee*HM{ z*9qxzUfH~iyya3l1A=707zqx7TyqFmMg?;Stc%kvk62^~T7v7?MSOsZ+zUgHKfH@d z0$(xM(sIv&hXSLraTx&{gq=sEqJT`(E?TAv!E0ahC7knD4ZGzqC*;*`z681RD!;}` z$KbWdYmyuWJ0j=?+U2p6rfVDtRg1J~t#r}Gn~UE7Fmp_<$%^zU`SfiB2+T~hI;}iU%K>eog&IIMM=!ZsfHO?uEcGo#hTW%!KR8UcDyku35C3PO&sWqTUZ|c^sBp#2 z=j+TpsWa9xF*M|dcm?Vd!`0kDV{ETVZ^eKxD%v*XHwF=#2_jBr6L~dR+~sd$cXuJ^ z`99ezZRw6RM8jQdOky?{Ps)$IOSuup?tpIydl62`NyzDz=5Kgiy~NU0CZ-Pg zWdKE?AA1uAk*pt4J=<78vm8=0LBq-9P*#(*j`_DM(_2XoQTRfE>90Xad{amT7k2AD zJbwM-&|Wv3^gU+2MkexO`Wu}i>YhG*qyRN=s%HJ=>IzeT&^wXitl%EK#^scZr`)&0 zXQH>m-&UFe=Od~zfuh+=OmN6G;7q=A4*9M23iL{^?@h|)asu+DKy^`nPf{qY1)jRW zTbIiTWnF-$viJeXG$}uT&jNq(+#$bpV4rMiS#mZPId;Oup|F2>^3P@m%=_6qkpcdO zstdWa2#`?tAuOdP1fCJ<{II8rsVcMV7!uM(7vdCz@}@#6!xClI9I$=@5$6^yU?M7- zn=He*Ce|Y8v;`N3JA$!b!H(59gapWB4D}pYF?8GoHejk;paLiJ0^l%1O$L;jQmE4f zHe$-LA?`ewkc_IllK)ne#DdUpeOm9t|E*ge0UU}06@mJP{vezJPoa4eEqxZ*Kp_!d z=22~Z*oVej3b=#X_@5eTFZXmQA)^UkPFCP{CNa6 zaWv5#AvaDKmQll1LDHFa#8~G&#sV2v5h-|g9!qeSVnrHuu{~nPR^%>NR)?naWXn?R zAM```AJS(_7%7YB?ldg^NSPrmA$;=$KdqQIdZO6o70*eE)j1o2Pl}WV${j?ooifcW zH`W#Fl;wF|&XHlAgj8Z!61(I_v%$wuhkMP+l zJ5IOtRU?9u2oW%5F0nTUaxwLm>;?K;o|r9+esd-cgtRPO$Rw@K@VKy_cmj=KWADIm zz5bsd|14!%Dt$kGitDGMjV2z?#y1nLa|VZZzT&!l!?TYQSUoT7lHE>34%^)l+skM*}UkUJ|0WZ0Y64<`=7&cvcvJs1dObJ<1^hZ z1n0F0VfsH33ZkolyYOBhZEw{2Or zdmnOQ(5%+03}2EfQRIfren93yjM9tv9lXLxSJgXaQ)iwU+E1P6Kkcl&HaTz#b)g}e z+*O9|0_Sz(?e68m0~ZnC(Tc7I9Dx15LG!llu=F2HF}7{l>vY4~KF$iy^;i ztFTwC#=HR42v3(i{?ml5qZ>r0P%+Vo6TG-ZiF3E4<~$H%rIT03YU%1cI{5>|#Rz2} zK5bBlE1@=gyKov!9Cqp0(luozruy(`l|1ETgna-#iPDY)@kNxeF6B+cga_F0O9J+* z5e72Vo+$KW4CN=a!M+gn+~q7A{u=7IgFhJz)OZxQO6kXsqK?b`=>qwRlend!wzDPc z!AZjvL$t~je4emMEn zl)WQs5S}V@g}6f~=pYx%a0=ANxsM)7zrR3A?ytRiFYD(a!}o3<|8u#DpnvmQ$OsyMwk#7j;y!|zj-SRF5wJwQ;RLWav81&45`IUzH&>wRoK^r zD^~l-o90&4B1S_GU5#O&ZmmfxG$k~H^Mbl*8XI2qxKd|p5To}gWx{ylohCyVW>0w_ z{F2TaE=8~E2ci@2%NQH6FGphWGu+aqTV>jX_->kL5dXLg%ZBb!z^H>Z!4yf#Ui z=3>h2a?OaXw8!G=Mb18|KV`w5s;`@pQkfyR**wJ7R}rnRi_$1n>{6{W&tfY*22nsY zenL)4=PEd3k;IZyxq^3Vr1d5AtPcz5PiB`W<+aE($uOn>j-LJ zscmL1oJCeISU2XWE-~fNJL!$9VIR%o@og zQD=wzE@+m&iDWioU!wg~i{$-)JcVGoeFnI-8`L*=mOcwasH+``nPwa)Q%7;y;31w_ z6f$%ZEpUn!@sqpaW`b3=vwps-z}-SSs-&tOv51>F49IjA+&ACieh`S1Y?;sONccDy zRDpYA*ms(HCd?sEf7jMGVF|R$txQXw7AvUnF+)s}7$XP@o#ZYtdOPzt#L0F=&>V-{8|*LX&v2PcN*t;Q#2lKji9@!$6CiW;3Z@~ z&}-Re&wX0>k2N{1mFW9syj2`$7M^Ew-rCbxip1;S#g!Mh+iAzjpN7Fp}ba tN$Wqkzk2=sz>jI}XRq41ChPv1_51Im&tx}z|1X3;!F$)J!CQUq_x~ovaen{+ diff --git a/core/gfx/shaders/compiled/glsl/liquid_glass.fragment.glsl b/core/gfx/shaders/compiled/glsl/liquid_glass.fragment.glsl index 3c3f45f42..151d0b6d5 100644 --- a/core/gfx/shaders/compiled/glsl/liquid_glass.fragment.glsl +++ b/core/gfx/shaders/compiled/glsl/liquid_glass.fragment.glsl @@ -12,183 +12,181 @@ layout(location = 0) out highp vec4 out_var_SV_Target0; void main() { - highp vec4 _678; + highp vec4 _803; do { - int _90 = int((vColor.y * 255.0) + 0.5); - int _95 = int((vColor.z * 255.0) + 0.5); - bool _111 = uData[5].y > 0.0; - highp float _113; - highp float _116; - highp vec2 _118; + int _94 = int((vColor.y * 255.0) + 0.5); + int _99 = int((vColor.z * 255.0) + 0.5); + bool _115 = uData[5].y > 0.0; + highp float _117; highp vec2 _120; highp vec4 _122; - highp vec4 _124; + highp vec2 _124; highp float _126; highp float _128; - highp float _130; + highp vec4 _130; highp float _132; - _113 = 1000000000.0; - _116 = 1000000000.0; - _118 = vec2(0.0); + highp float _134; + highp float _136; + _117 = 1000000000.0; _120 = vec2(0.0); _122 = vec4(0.0); - _124 = vec4(0.0); + _124 = vec2(0.0); _126 = 1000000000.0; _128 = 1000000000.0; - _130 = 1000000000.0; + _130 = vec4(0.0); _132 = 1000000000.0; - highp float _117; - highp vec2 _121; - highp vec4 _125; + _134 = 1000000000.0; + _136 = 1000000000.0; + highp vec2 _125; highp float _129; - highp float _133; - highp float _114; - highp vec2 _119; + highp vec4 _131; + highp float _135; + highp float _137; + highp float _118; + highp vec2 _121; highp vec4 _123; highp float _127; - highp float _131; - int _134 = _90; + highp float _133; + int _138 = _94; for (;;) { - bool _143; - if (_134 < (_90 + _95)) + bool _147; + if (_138 < (_94 + _99)) { - _143 = _134 < 16; + _147 = _138 < 16; } else { - _143 = false; + _147 = false; } - if (_143) + if (_147) { - int _145 = 3 * _134; - int _146 = 7 + _145; - int _152 = _145 + 9; - highp vec2 _156 = vTextureCoord - uData[_146 * 1 + 0].xy; - highp float _160 = min(uData[_146 * 1 + 0].z, uData[_146 * 1 + 0].w); - highp vec4 _162 = min(uData[(_145 + 8) * 1 + 0], vec4(_160)); - highp float _163 = _156.x; - highp float _184; - if (_163 > 0.0) + int _149 = 3 * _138; + int _150 = 7 + _149; + int _156 = _149 + 9; + highp vec2 _160 = vTextureCoord - uData[_150 * 1 + 0].xy; + highp float _164 = min(uData[_150 * 1 + 0].z, uData[_150 * 1 + 0].w); + highp vec4 _166 = min(uData[(_149 + 8) * 1 + 0], vec4(_164)); + highp float _167 = _160.x; + highp float _188; + if (_167 > 0.0) { - highp float _175; - if (_156.y > 0.0) + highp float _179; + if (_160.y > 0.0) { - _175 = _162.z; + _179 = _166.z; } else { - _175 = _162.y; + _179 = _166.y; } - _184 = _175; + _188 = _179; } else { - highp float _183; - if (_156.y > 0.0) + highp float _187; + if (_160.y > 0.0) { - _183 = _162.w; + _187 = _166.w; } else { - _183 = _162.x; + _187 = _166.x; } - _184 = _183; + _188 = _187; } - highp vec2 _188 = (abs(_156) - uData[_146 * 1 + 0].zw) + vec2(_184); - highp float _190 = (_163 < 0.0) ? (-1.0) : 1.0; - highp float _193 = (_156.y < 0.0) ? (-1.0) : 1.0; - highp float _195 = _188.x; - bool _201; - if (_195 > 0.0) + highp vec2 _192 = (abs(_160) - uData[_150 * 1 + 0].zw) + vec2(_188); + highp float _194 = (_167 < 0.0) ? (-1.0) : 1.0; + highp float _197 = (_160.y < 0.0) ? (-1.0) : 1.0; + highp float _199 = _192.x; + bool _205; + if (_199 > 0.0) { - _201 = _188.y > 0.0; + _205 = _192.y > 0.0; } else { - _201 = false; + _205 = false; } - highp vec2 _215; - if (_201) + highp vec2 _219; + if (_205) { - _215 = normalize(_188) * vec2(_190, _193); + _219 = normalize(_192) * vec2(_194, _197); } else { - highp vec2 _214; - if (_195 > _188.y) + highp vec2 _218; + if (_199 > _192.y) { - _214 = vec2(_190, 0.0); + _218 = vec2(_194, 0.0); } else { - _214 = vec2(0.0, _193); + _218 = vec2(0.0, _197); } - _215 = _214; + _219 = _218; } - highp float _222 = (min(max(_195, _188.y), 0.0) + length(max(_188, vec2(0.0)))) - _184; - bool _223 = _222 < _132; - if (_223) + highp float _226 = (min(max(_199, _192.y), 0.0) + length(max(_192, vec2(0.0)))) - _188; + bool _227 = _226 < _134; + if (_227) { - _114 = _116; - _123 = _124; - _131 = _132; + _118 = _136; + _123 = _130; + _133 = _134; } else { - bool _227 = _222 < _130; - bvec4 _229 = bvec4(_227); - _114 = _227 ? _160 : _113; - _123 = vec4(_229.x ? uData[_152 * 1 + 0].x : _122.x, _229.y ? uData[_152 * 1 + 0].y : _122.y, _229.z ? uData[_152 * 1 + 0].z : _122.z, _229.w ? uData[_152 * 1 + 0].w : _122.w); - _131 = _227 ? _222 : _130; + bool _231 = _226 < _132; + bvec4 _233 = bvec4(_231); + _118 = _231 ? _164 : _117; + _123 = vec4(_233.x ? uData[_156 * 1 + 0].x : _122.x, _233.y ? uData[_156 * 1 + 0].y : _122.y, _233.z ? uData[_156 * 1 + 0].z : _122.z, _233.w ? uData[_156 * 1 + 0].w : _122.w); + _133 = _231 ? _226 : _132; } - _117 = _223 ? _160 : _116; - bvec4 _232 = bvec4(_223); - _125 = vec4(_232.x ? uData[_152 * 1 + 0].x : _124.x, _232.y ? uData[_152 * 1 + 0].y : _124.y, _232.z ? uData[_152 * 1 + 0].z : _124.z, _232.w ? uData[_152 * 1 + 0].w : _124.w); - _133 = _223 ? _222 : _132; - highp vec2 _277; - highp float _278; - if (uData[_152 * 1 + 0].w < 0.5) + bvec4 _236 = bvec4(_227); + _131 = vec4(_236.x ? uData[_156 * 1 + 0].x : _130.x, _236.y ? uData[_156 * 1 + 0].y : _130.y, _236.z ? uData[_156 * 1 + 0].z : _130.z, _236.w ? uData[_156 * 1 + 0].w : _130.w); + _135 = _227 ? _226 : _134; + _137 = _227 ? _164 : _136; + highp vec2 _281; + highp float _282; + if (uData[_156 * 1 + 0].w < 0.5) { - highp float _243 = (vTextureCoord.x - uData[_146 * 1 + 0].x) + uData[_146 * 1 + 0].z; - highp float _245 = (uData[_146 * 1 + 0].x + uData[_146 * 1 + 0].z) - vTextureCoord.x; - highp float _250 = (vTextureCoord.y - uData[_146 * 1 + 0].y) + uData[_146 * 1 + 0].w; - highp float _252 = (uData[_146 * 1 + 0].y + uData[_146 * 1 + 0].w) - vTextureCoord.y; - highp float _256 = min(min(_243, _245), min(_250, _252)); - highp vec4 _261 = exp((vec4(_256) - vec4(_243, _245, _250, _252)) / vec4(uData[1].y)); - highp float _262 = _261.x; - highp float _263 = _261.y; - highp float _265 = _261.z; - highp float _267 = _261.w; - highp float _268 = ((_262 + _263) + _265) + _267; - _277 = vec2(_263 - _262, _267 - _265) / vec2(_268); - _278 = (uData[1].y * log(_268)) - _256; + highp float _247 = (vTextureCoord.x - uData[_150 * 1 + 0].x) + uData[_150 * 1 + 0].z; + highp float _249 = (uData[_150 * 1 + 0].x + uData[_150 * 1 + 0].z) - vTextureCoord.x; + highp float _254 = (vTextureCoord.y - uData[_150 * 1 + 0].y) + uData[_150 * 1 + 0].w; + highp float _256 = (uData[_150 * 1 + 0].y + uData[_150 * 1 + 0].w) - vTextureCoord.y; + highp float _260 = min(min(_247, _249), min(_254, _256)); + highp vec4 _265 = exp((vec4(_260) - vec4(_247, _249, _254, _256)) / vec4(uData[1].y)); + highp float _266 = _265.x; + highp float _267 = _265.y; + highp float _269 = _265.z; + highp float _271 = _265.w; + highp float _272 = ((_266 + _267) + _269) + _271; + _281 = vec2(_267 - _266, _271 - _269) / vec2(_272); + _282 = (uData[1].y * log(_272)) - _260; } else { - _277 = _215; - _278 = _222; + _281 = _219; + _282 = _226; } - bool _279 = _278 < _128; - if (_279) + bool _283 = _282 < _128; + if (_283) { - _119 = _120; + _121 = _124; _127 = _128; } else { - bool _283 = _278 < _126; - bvec2 _284 = bvec2(_283); - _119 = vec2(_284.x ? _277.x : _118.x, _284.y ? _277.y : _118.y); - _127 = _283 ? _278 : _126; + bool _287 = _282 < _126; + bvec2 _288 = bvec2(_287); + _121 = vec2(_288.x ? _281.x : _120.x, _288.y ? _281.y : _120.y); + _127 = _287 ? _282 : _126; } - bvec2 _287 = bvec2(_279); - _121 = vec2(_287.x ? _277.x : _120.x, _287.y ? _277.y : _120.y); - _129 = _279 ? _278 : _128; - _113 = _114; - _116 = _117; - _118 = _119; + bvec2 _291 = bvec2(_283); + _125 = vec2(_291.x ? _281.x : _124.x, _291.y ? _281.y : _124.y); + _129 = _283 ? _282 : _128; + _117 = _118; _120 = _121; _122 = _123; _124 = _125; @@ -196,7 +194,9 @@ void main() _128 = _129; _130 = _131; _132 = _133; - _134++; + _134 = _135; + _136 = _137; + _138++; continue; } else @@ -204,274 +204,409 @@ void main() break; } } - highp float _289 = max(uData[1].x, 9.9999997473787516355514526367188e-05); - highp float _294 = max(_289 - abs(_132 - _130), 0.0) / _289; - highp float _295 = _294 * _294; - highp float _296 = _295 * 0.5; - highp float _301; - if (_130 < _132) + highp float _400; + highp vec2 _401; + highp vec2 _402; + highp vec4 _403; + highp vec4 _404; + highp float _405; + highp float _406; + highp float _407; + highp float _408; + highp float _409; + if (uData[54].w > 1.5) + { + highp vec2 _303 = vTextureCoord - uData[52].xy; + highp vec4 _309 = min(uData[53], vec4(min(uData[52].z, uData[52].w))); + highp float _310 = _303.x; + highp float _331; + if (_310 > 0.0) + { + highp float _322; + if (_303.y > 0.0) + { + _322 = _309.z; + } + else + { + _322 = _309.y; + } + _331 = _322; + } + else + { + highp float _330; + if (_303.y > 0.0) + { + _330 = _309.w; + } + else + { + _330 = _309.x; + } + _331 = _330; + } + highp vec2 _335 = (abs(_303) - uData[52].zw) + vec2(_331); + highp float _337 = (_310 < 0.0) ? (-1.0) : 1.0; + highp float _340 = (_303.y < 0.0) ? (-1.0) : 1.0; + highp float _342 = _335.x; + bool _348; + if (_342 > 0.0) + { + _348 = _335.y > 0.0; + } + else + { + _348 = false; + } + highp vec2 _362; + if (_348) + { + _362 = normalize(_335) * vec2(_337, _340); + } + else + { + highp vec2 _361; + if (_342 > _335.y) + { + _361 = vec2(_337, 0.0); + } + else + { + _361 = vec2(0.0, _340); + } + _362 = _361; + } + highp float _369 = (min(max(_342, _335.y), 0.0) + length(max(_335, vec2(0.0)))) - _331; + highp float _370 = 1.0 - _369; + bool _371 = _370 < _134; + highp float _380; + highp vec4 _381; + highp float _382; + if (_371) + { + _380 = _136; + _381 = _130; + _382 = _134; + } + else + { + bool _375 = _370 < _132; + bvec4 _377 = bvec4(_375); + _380 = _375 ? _136 : _117; + _381 = vec4(_377.x ? uData[54].x : _122.x, _377.y ? uData[54].y : _122.y, _377.z ? uData[54].z : _122.z, _377.w ? uData[54].w : _122.w); + _382 = _375 ? _370 : _132; + } + bvec4 _383 = bvec4(_371); + highp vec2 _386 = -_362; + bool _387 = _370 < _128; + highp vec2 _395; + highp float _396; + if (_387) + { + _395 = _124; + _396 = _128; + } + else + { + bool _391 = _370 < _126; + bvec2 _392 = bvec2(_391); + _395 = vec2(_392.x ? _386.x : _120.x, _392.y ? _386.y : _120.y); + _396 = _391 ? _370 : _126; + } + bvec2 _397 = bvec2(_387); + _400 = _380; + _401 = _395; + _402 = vec2(_397.x ? _386.x : _124.x, _397.y ? _386.y : _124.y); + _403 = _381; + _404 = vec4(_383.x ? uData[54].x : _130.x, _383.y ? uData[54].y : _130.y, _383.z ? uData[54].z : _130.z, _383.w ? uData[54].w : _130.w); + _405 = _369; + _406 = _396; + _407 = _387 ? _370 : _128; + _408 = _382; + _409 = _371 ? _370 : _134; + } + else { - _301 = 1.0 - _296; + _400 = _117; + _401 = _120; + _402 = _124; + _403 = _122; + _404 = _130; + _405 = -1000000000.0; + _406 = _126; + _407 = _128; + _408 = _132; + _409 = _134; + } + highp float _411 = max(uData[1].x, 9.9999997473787516355514526367188e-05); + highp float _416 = max(_411 - abs(_409 - _408), 0.0) / _411; + highp float _417 = _416 * _416; + highp float _418 = _417 * 0.5; + highp float _423; + if (_408 < _409) + { + _423 = 1.0 - _418; } else { - _301 = _296; + _423 = _418; } - highp float _305 = min(_132, _130) - ((_295 * _289) * 0.25); - highp float _310 = max(_289 - abs(_128 - _126), 0.0) / _289; - highp float _311 = _310 * _310; - highp float _312 = _311 * 0.5; - highp float _317; - if (_126 < _128) + highp float _427 = min(_409, _408) - ((_417 * _411) * 0.25); + highp float _432 = max(_411 - abs(_407 - _406), 0.0) / _411; + highp float _433 = _432 * _432; + highp float _434 = _433 * 0.5; + highp float _439; + if (_406 < _407) { - _317 = 1.0 - _312; + _439 = 1.0 - _434; } else { - _317 = _312; + _439 = _434; } - highp float _323 = clamp(0.5 - _305, 0.0, 1.0); - if (_323 <= 0.0) + highp float _446 = clamp(0.5 - max(_427, _405), 0.0, 1.0); + if (_446 <= 0.0) { discard; } if (vColor.x < 0.5) { - _678 = vec4(0.0, 0.0, 0.0, _323); + _803 = vec4(0.0, 0.0, 0.0, _446); break; } - highp vec4 _334 = mix(_124, _122, vec4(_301)); - highp vec2 _336 = mix(_120, _118, vec2(_317)); - highp float _338 = max(0.0, -(min(_128, _126) - ((_311 * _289) * 0.25))); - highp float _339 = -_338; - highp float _342 = exp(_339 / uData[1].z); - highp float _348 = _334.y; - highp float _382; - highp float _383; - highp vec2 _384; - if (_111) - { - highp float _356 = max(min(uData[5].y * mix(_116, _113, _301), uData[6].w), 1.0); - highp float _359 = clamp(1.0 - (_338 / _356), 0.0, 1.0); - highp float _364 = (((uData[5].z * _356) * _348) * _359) * _359; - _382 = _359; - _383 = _364; - _384 = clamp(((vTextureCoord - (_336 * _364)) - uData[0].xy) * uData[0].zw, vec2(0.0), vec2(1.0)); + highp vec4 _457 = mix(_404, _403, vec4(_423)); + highp vec2 _459 = mix(_402, _401, vec2(_439)); + highp float _461 = max(0.0, -(min(_407, _406) - ((_433 * _411) * 0.25))); + highp float _462 = -_461; + highp float _465 = exp(_462 / uData[1].z); + highp float _471 = _457.y; + highp float _505; + highp float _506; + highp vec2 _507; + if (_115) + { + highp float _479 = max(min(uData[5].y * mix(_136, _400, _423), uData[6].w), 1.0); + highp float _482 = clamp(1.0 - (_461 / _479), 0.0, 1.0); + highp float _487 = (((uData[5].z * _479) * _471) * _482) * _482; + _505 = _482; + _506 = _487; + _507 = clamp(((vTextureCoord - (_459 * _487)) - uData[0].xy) * uData[0].zw, vec2(0.0), vec2(1.0)); } else { - _382 = 0.0; - _383 = 0.0; - _384 = clamp(((vTextureCoord + (_336 * ((uData[2].x * _348) * _342))) - uData[0].xy) * uData[0].zw, vec2(0.0), vec2(1.0)); + _505 = 0.0; + _506 = 0.0; + _507 = clamp(((vTextureCoord + (_459 * ((uData[2].x * _471) * _465))) - uData[0].xy) * uData[0].zw, vec2(0.0), vec2(1.0)); } - highp vec4 _388 = textureLod(uSampler, _384, 0.0); - bool _390 = uData[4].w > 0.5; - highp vec4 _398; - if (_390) + highp vec4 _511 = textureLod(uSampler, _507, 0.0); + bool _513 = uData[4].w > 0.5; + highp vec4 _521; + if (_513) { - _398 = textureLod(uTex1, _384, 0.0); + _521 = textureLod(uTex1, _507, 0.0); } else { - _398 = _388; + _521 = _511; } - bool _403; - if (_111) + bool _526; + if (_115) { - _403 = uData[6].y > 0.0; + _526 = uData[6].y > 0.0; } else { - _403 = false; + _526 = false; } - bool _407; - if (_403) + bool _530; + if (_526) { - _407 = _383 > 0.5; + _530 = _506 > 0.5; } else { - _407 = false; - } - highp vec4 _468; - highp vec4 _469; - if (_407) - { - highp vec2 _415 = (_336 * ((_383 * 0.039999999105930328369140625) * uData[6].y)) * uData[0].zw; - highp vec2 _417 = clamp(_384 + _415, vec2(0.0), vec2(1.0)); - highp vec2 _419 = clamp(_384 - _415, vec2(0.0), vec2(1.0)); - highp vec4 _423 = textureLod(uSampler, _417, 0.0); - highp float _428 = _388.w; - highp float _429 = (_423.x / max(_423.w, 9.9999997473787516355514526367188e-05)) * _428; - highp vec4 _430 = _388; - _430.x = _429; - highp vec4 _434 = textureLod(uSampler, _419, 0.0); - highp float _439 = (_434.z / max(_434.w, 9.9999997473787516355514526367188e-05)) * _428; - _430.z = _439; - highp vec4 _467; - if (_390) + _530 = false; + } + highp vec4 _591; + highp vec4 _592; + if (_530) + { + highp vec2 _538 = (_459 * ((_506 * 0.039999999105930328369140625) * uData[6].y)) * uData[0].zw; + highp vec2 _540 = clamp(_507 + _538, vec2(0.0), vec2(1.0)); + highp vec2 _542 = clamp(_507 - _538, vec2(0.0), vec2(1.0)); + highp vec4 _546 = textureLod(uSampler, _540, 0.0); + highp float _551 = _511.w; + highp float _552 = (_546.x / max(_546.w, 9.9999997473787516355514526367188e-05)) * _551; + highp vec4 _553 = _511; + _553.x = _552; + highp vec4 _557 = textureLod(uSampler, _542, 0.0); + highp float _562 = (_557.z / max(_557.w, 9.9999997473787516355514526367188e-05)) * _551; + _553.z = _562; + highp vec4 _590; + if (_513) { - highp vec4 _447 = textureLod(uTex1, _417, 0.0); - highp vec4 _454 = _398; - _454.x = (_447.x / max(_447.w, 9.9999997473787516355514526367188e-05)) * _398.w; - highp vec4 _458 = textureLod(uTex1, _419, 0.0); - _454.z = (_458.z / max(_458.w, 9.9999997473787516355514526367188e-05)) * _398.w; - _467 = _454; + highp vec4 _570 = textureLod(uTex1, _540, 0.0); + highp vec4 _577 = _521; + _577.x = (_570.x / max(_570.w, 9.9999997473787516355514526367188e-05)) * _521.w; + highp vec4 _581 = textureLod(uTex1, _542, 0.0); + _577.z = (_581.z / max(_581.w, 9.9999997473787516355514526367188e-05)) * _521.w; + _590 = _577; } else { - highp vec4 _465 = _398; - _465.x = _429; - _465.z = _439; - _467 = _465; + highp vec4 _588 = _521; + _588.x = _552; + _588.z = _562; + _590 = _588; } - _468 = _467; - _469 = _430; + _591 = _590; + _592 = _553; } else { - _468 = _398; - _469 = _388; + _591 = _521; + _592 = _511; } - highp float _477; + highp float _600; if (uData[6].x > 0.5) { - _477 = 1.0; + _600 = 1.0; } else { - _477 = _469.w; + _600 = _592.w; } - highp vec4 _486 = vec4(_469.xyz / vec3(max(_469.w, 9.9999997473787516355514526367188e-05)), 1.0); - highp vec4 _495 = vec4(_468.xyz / vec3(max(_468.w, 9.9999997473787516355514526367188e-05)), 1.0); - bool _497 = uData[4].z > 0.5; - highp float _502; - if (_497) + highp vec4 _609 = vec4(_592.xyz / vec3(max(_592.w, 9.9999997473787516355514526367188e-05)), 1.0); + highp vec4 _618 = vec4(_591.xyz / vec3(max(_591.w, 9.9999997473787516355514526367188e-05)), 1.0); + bool _620 = uData[4].z > 0.5; + highp float _625; + if (_620) { - _502 = uData[4].x; + _625 = uData[4].x; } else { - _502 = 0.0; + _625 = 0.0; } - highp float _511; - if (_111) + highp float _634; + if (_115) { - _511 = _502 * (1.0 - ((_382 * _382) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _502)))); + _634 = _625 * (1.0 - ((_505 * _505) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _625)))); } else { - _511 = _502; + _634 = _625; } - highp float _514 = 1.0 - _511; - highp float _522; - if (_111) + highp float _637 = 1.0 - _634; + highp float _645; + if (_115) { - _522 = _382; + _645 = _505; } else { - _522 = _342 * _342; + _645 = _465 * _465; } - highp float _525 = clamp(((uData[2].y * _348) * _522) * _514, 0.0, 1.0); - highp vec4 _531 = ((_495 * _525) + ((mix(_495, _486, vec4(clamp(_334.x, 0.0, 1.0))) * _514) * (1.0 - _525))) + (uData[3] * _511); - highp float _536 = dot(_336, vec2(-0.707106769084930419921875)); - highp float _537 = max(_536, 0.0); - highp float _539 = max(-_536, 0.0); - highp float _542 = abs(_536); - bool _547; - if (_111) + highp float _648 = clamp(((uData[2].y * _471) * _645) * _637, 0.0, 1.0); + highp vec4 _654 = ((_618 * _648) + ((mix(_618, _609, vec4(clamp(_457.x, 0.0, 1.0))) * _637) * (1.0 - _648))) + (uData[3] * _634); + highp float _659 = dot(_459, vec2(-0.707106769084930419921875)); + highp float _660 = max(_659, 0.0); + highp float _662 = max(-_659, 0.0); + highp float _665 = abs(_659); + bool _670; + if (_115) { - _547 = uData[6].z > 0.0; + _670 = uData[6].z > 0.0; } else { - _547 = false; + _670 = false; } - highp float _567; - if (_547) + highp float _690; + if (_670) { - _567 = (uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _542))) * exp((-max(0.0, -_305)) / (uData[2].z * 2.5)); + _690 = (uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _665))) * exp((-max(0.0, -_427)) / (uData[2].z * 2.5)); } else { - _567 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, uData[2].z * 0.625, -_305)); + _690 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, uData[2].z * 0.625, -_427)); } - highp float _575; - if (_111) + highp float _700; + if (_115) { - _575 = max((uData[5].w * _382) * _382, _567); + _700 = max((uData[5].w * _505) * _505, _690) * min(_471, 1.0); } else { - _575 = 0.0; + _700 = 0.0; } - highp float _578 = _477 * (1.0 - _575); - highp vec4 _584 = vec4((_531 + (_486 * (1.0 - _531.w))).xyz * _578, _578 + _575); - highp float _603; - if (_111) + highp float _703 = _600 * (1.0 - _700); + highp vec4 _709 = vec4((_654 + (_609 * (1.0 - _654.w))).xyz * _703, _703 + _700); + highp float _728; + if (_115) { - highp float _594; - if (_547) + highp float _719; + if (_670) { - _594 = uData[6].z * smoothstep(0.75, 1.0, _542); + _719 = uData[6].z * smoothstep(0.75, 1.0, _665); } else { - _594 = 0.25; + _719 = 0.25; } - _603 = _594; + _728 = _719; } else { - _603 = (0.449999988079071044921875 * ((_537 * sqrt(_537)) + ((0.75 * _539) * sqrt(_539)))) + 0.0599999986588954925537109375; + _728 = (0.449999988079071044921875 * ((_660 * sqrt(_660)) + ((0.75 * _662) * sqrt(_662)))) + 0.0599999986588954925537109375; } - highp float _630; - if (_547) + highp float _755; + if (_670) { - _630 = exp((-max(0.0, -_305)) / (uData[2].z * 1.25)); + _755 = exp((-max(0.0, -_427)) / (uData[2].z * 1.25)); } else { - highp float _629; - if (_111) + highp float _754; + if (_115) { - highp float _617 = -_305; - _629 = smoothstep(uData[2].z * 0.3125, uData[2].z * 0.625, _617) * (1.0 - smoothstep(uData[2].z * 1.5, uData[2].z * 2.0, _617)); + highp float _742 = -_427; + _754 = smoothstep(uData[2].z * 0.3125, uData[2].z * 0.625, _742) * (1.0 - smoothstep(uData[2].z * 1.5, uData[2].z * 2.0, _742)); } else { - _629 = exp((-max(0.0, -_305)) / uData[2].z); + _754 = exp((-max(0.0, -_427)) / uData[2].z); } - _630 = _629; + _755 = _754; } - highp float _637; - if (_111) + highp float _762; + if (_115) { - _637 = 0.0; + _762 = 0.0; } else { - _637 = (0.039999999105930328369140625 * exp(_339 / uData[1].w)) * _537; + _762 = (0.039999999105930328369140625 * exp(_462 / uData[1].w)) * _660; } - highp float _643; - if (_497) + highp float _768; + if (_620) { - _643 = uData[4].y; + _768 = uData[4].y; } else { - _643 = 0.0; + _768 = 0.0; } - highp vec3 _653 = _584.xyz + vec3(clamp((_643 + _334.z) + ((_348 * uData[2].w) * ((_630 * _603) + _637)), 0.0, 1.0)); - highp vec4 _655 = clamp(vec4(_653.x, _653.y, _653.z, _584.w), vec4(0.0), vec4(1.0)); - highp float _662 = max(_655.w, max(_655.x, max(_655.y, _655.z))); - highp vec4 _663 = _655; - _663.w = _662; - highp vec3 _674 = _663.xyz + vec3(((fract(52.98291778564453125 * fract(dot(vTextureCoord, vec2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * uData[5].x) * _662); - _678 = clamp(vec4(_674.x, _674.y, _674.z, _663.w), vec4(0.0), vec4(1.0)) * _323; + highp vec3 _778 = _709.xyz + vec3(clamp((_768 + _457.z) + ((_471 * uData[2].w) * ((_755 * _728) + _762)), 0.0, 1.0)); + highp vec4 _780 = clamp(vec4(_778.x, _778.y, _778.z, _709.w), vec4(0.0), vec4(1.0)); + highp float _787 = max(_780.w, max(_780.x, max(_780.y, _780.z))); + highp vec4 _788 = _780; + _788.w = _787; + highp vec3 _799 = _788.xyz + vec3(((fract(52.98291778564453125 * fract(dot(vTextureCoord, vec2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * uData[5].x) * _787); + _803 = clamp(vec4(_799.x, _799.y, _799.z, _788.w), vec4(0.0), vec4(1.0)) * _446; break; } while(false); - out_var_SV_Target0 = _678; + out_var_SV_Target0 = _803; } diff --git a/core/gfx/shaders/compiled/msl/liquid_glass.fragment.msl b/core/gfx/shaders/compiled/msl/liquid_glass.fragment.msl index bce3c888d..abb47814a 100644 --- a/core/gfx/shaders/compiled/msl/liquid_glass.fragment.msl +++ b/core/gfx/shaders/compiled/msl/liquid_glass.fragment.msl @@ -22,179 +22,177 @@ struct main0_in fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], texture2d Frost [[texture(0)]], texture2d Sharp [[texture(1)]], sampler FrostSampler [[sampler(0)]], sampler SharpSampler [[sampler(1)]]) { main0_out out = {}; - float4 _678; + float4 _803; do { - int _90 = int((in.in_var_COLOR.y * 255.0) + 0.5); - int _95 = int((in.in_var_COLOR.z * 255.0) + 0.5); - bool _111 = Uniforms.uData[5].y > 0.0; - float _113; - float _116; - float2 _118; + int _94 = int((in.in_var_COLOR.y * 255.0) + 0.5); + int _99 = int((in.in_var_COLOR.z * 255.0) + 0.5); + bool _115 = Uniforms.uData[5].y > 0.0; + float _117; float2 _120; float4 _122; - float4 _124; + float2 _124; float _126; float _128; - float _130; + float4 _130; float _132; - _113 = 1000000000.0; - _116 = 1000000000.0; - _118 = float2(0.0); + float _134; + float _136; + _117 = 1000000000.0; _120 = float2(0.0); _122 = float4(0.0); - _124 = float4(0.0); + _124 = float2(0.0); _126 = 1000000000.0; _128 = 1000000000.0; - _130 = 1000000000.0; + _130 = float4(0.0); _132 = 1000000000.0; - float _117; - float2 _121; - float4 _125; + _134 = 1000000000.0; + _136 = 1000000000.0; + float2 _125; float _129; - float _133; - float _114; - float2 _119; + float4 _131; + float _135; + float _137; + float _118; + float2 _121; float4 _123; float _127; - float _131; - int _134 = _90; + float _133; + int _138 = _94; for (;;) { - bool _143; - if (_134 < (_90 + _95)) + bool _147; + if (_138 < (_94 + _99)) { - _143 = _134 < 16; + _147 = _138 < 16; } else { - _143 = false; + _147 = false; } - if (_143) + if (_147) { - int _145 = 3 * _134; - int _146 = 7 + _145; - int _152 = _145 + 9; - float2 _156 = in.in_var_TEXCOORD0 - Uniforms.uData[_146].xy; - float _160 = precise::min(Uniforms.uData[_146].z, Uniforms.uData[_146].w); - float4 _162 = precise::min(Uniforms.uData[_145 + 8], float4(_160)); - float _163 = _156.x; - float _184; - if (_163 > 0.0) + int _149 = 3 * _138; + int _150 = 7 + _149; + int _156 = _149 + 9; + float2 _160 = in.in_var_TEXCOORD0 - Uniforms.uData[_150].xy; + float _164 = precise::min(Uniforms.uData[_150].z, Uniforms.uData[_150].w); + float4 _166 = precise::min(Uniforms.uData[_149 + 8], float4(_164)); + float _167 = _160.x; + float _188; + if (_167 > 0.0) { - float _175; - if (_156.y > 0.0) + float _179; + if (_160.y > 0.0) { - _175 = _162.z; + _179 = _166.z; } else { - _175 = _162.y; + _179 = _166.y; } - _184 = _175; + _188 = _179; } else { - float _183; - if (_156.y > 0.0) + float _187; + if (_160.y > 0.0) { - _183 = _162.w; + _187 = _166.w; } else { - _183 = _162.x; + _187 = _166.x; } - _184 = _183; + _188 = _187; } - float2 _188 = (abs(_156) - Uniforms.uData[_146].zw) + float2(_184); - float _190 = (_163 < 0.0) ? (-1.0) : 1.0; - float _193 = (_156.y < 0.0) ? (-1.0) : 1.0; - float _195 = _188.x; - bool _201; - if (_195 > 0.0) + float2 _192 = (abs(_160) - Uniforms.uData[_150].zw) + float2(_188); + float _194 = (_167 < 0.0) ? (-1.0) : 1.0; + float _197 = (_160.y < 0.0) ? (-1.0) : 1.0; + float _199 = _192.x; + bool _205; + if (_199 > 0.0) { - _201 = _188.y > 0.0; + _205 = _192.y > 0.0; } else { - _201 = false; + _205 = false; } - float2 _215; - if (_201) + float2 _219; + if (_205) { - _215 = fast::normalize(_188) * float2(_190, _193); + _219 = fast::normalize(_192) * float2(_194, _197); } else { - float2 _214; - if (_195 > _188.y) + float2 _218; + if (_199 > _192.y) { - _214 = float2(_190, 0.0); + _218 = float2(_194, 0.0); } else { - _214 = float2(0.0, _193); + _218 = float2(0.0, _197); } - _215 = _214; + _219 = _218; } - float _222 = (precise::min(precise::max(_195, _188.y), 0.0) + length(precise::max(_188, float2(0.0)))) - _184; - bool _223 = _222 < _132; - if (_223) + float _226 = (precise::min(precise::max(_199, _192.y), 0.0) + length(precise::max(_192, float2(0.0)))) - _188; + bool _227 = _226 < _134; + if (_227) { - _114 = _116; - _123 = _124; - _131 = _132; + _118 = _136; + _123 = _130; + _133 = _134; } else { - bool _227 = _222 < _130; - _114 = _227 ? _160 : _113; - _123 = select(_122, Uniforms.uData[_152], bool4(_227)); - _131 = _227 ? _222 : _130; + bool _231 = _226 < _132; + _118 = _231 ? _164 : _117; + _123 = select(_122, Uniforms.uData[_156], bool4(_231)); + _133 = _231 ? _226 : _132; } - _117 = _223 ? _160 : _116; - _125 = select(_124, Uniforms.uData[_152], bool4(_223)); - _133 = _223 ? _222 : _132; - float2 _277; - float _278; - if (Uniforms.uData[_152].w < 0.5) + _131 = select(_130, Uniforms.uData[_156], bool4(_227)); + _135 = _227 ? _226 : _134; + _137 = _227 ? _164 : _136; + float2 _281; + float _282; + if (Uniforms.uData[_156].w < 0.5) { - float _243 = (in.in_var_TEXCOORD0.x - Uniforms.uData[_146].x) + Uniforms.uData[_146].z; - float _245 = (Uniforms.uData[_146].x + Uniforms.uData[_146].z) - in.in_var_TEXCOORD0.x; - float _250 = (in.in_var_TEXCOORD0.y - Uniforms.uData[_146].y) + Uniforms.uData[_146].w; - float _252 = (Uniforms.uData[_146].y + Uniforms.uData[_146].w) - in.in_var_TEXCOORD0.y; - float _256 = precise::min(precise::min(_243, _245), precise::min(_250, _252)); - float4 _261 = exp((float4(_256) - float4(_243, _245, _250, _252)) / float4(Uniforms.uData[1].y)); - float _262 = _261.x; - float _263 = _261.y; - float _265 = _261.z; - float _267 = _261.w; - float _268 = ((_262 + _263) + _265) + _267; - _277 = float2(_263 - _262, _267 - _265) / float2(_268); - _278 = (Uniforms.uData[1].y * log(_268)) - _256; + float _247 = (in.in_var_TEXCOORD0.x - Uniforms.uData[_150].x) + Uniforms.uData[_150].z; + float _249 = (Uniforms.uData[_150].x + Uniforms.uData[_150].z) - in.in_var_TEXCOORD0.x; + float _254 = (in.in_var_TEXCOORD0.y - Uniforms.uData[_150].y) + Uniforms.uData[_150].w; + float _256 = (Uniforms.uData[_150].y + Uniforms.uData[_150].w) - in.in_var_TEXCOORD0.y; + float _260 = precise::min(precise::min(_247, _249), precise::min(_254, _256)); + float4 _265 = exp((float4(_260) - float4(_247, _249, _254, _256)) / float4(Uniforms.uData[1].y)); + float _266 = _265.x; + float _267 = _265.y; + float _269 = _265.z; + float _271 = _265.w; + float _272 = ((_266 + _267) + _269) + _271; + _281 = float2(_267 - _266, _271 - _269) / float2(_272); + _282 = (Uniforms.uData[1].y * log(_272)) - _260; } else { - _277 = _215; - _278 = _222; + _281 = _219; + _282 = _226; } - bool _279 = _278 < _128; - if (_279) + bool _283 = _282 < _128; + if (_283) { - _119 = _120; + _121 = _124; _127 = _128; } else { - bool _283 = _278 < _126; - _119 = select(_118, _277, bool2(_283)); - _127 = _283 ? _278 : _126; + bool _287 = _282 < _126; + _121 = select(_120, _281, bool2(_287)); + _127 = _287 ? _282 : _126; } - _121 = select(_120, _277, bool2(_279)); - _129 = _279 ? _278 : _128; - _113 = _114; - _116 = _117; - _118 = _119; + _125 = select(_124, _281, bool2(_283)); + _129 = _283 ? _282 : _128; + _117 = _118; _120 = _121; _122 = _123; _124 = _125; @@ -202,7 +200,9 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo _128 = _129; _130 = _131; _132 = _133; - _134++; + _134 = _135; + _136 = _137; + _138++; continue; } else @@ -210,275 +210,406 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo break; } } - float _289 = precise::max(Uniforms.uData[1].x, 9.9999997473787516355514526367188e-05); - float _294 = precise::max(_289 - abs(_132 - _130), 0.0) / _289; - float _295 = _294 * _294; - float _296 = _295 * 0.5; - float _301; - if (_130 < _132) + float _400; + float2 _401; + float2 _402; + float4 _403; + float4 _404; + float _405; + float _406; + float _407; + float _408; + float _409; + if (Uniforms.uData[54].w > 1.5) + { + float2 _303 = in.in_var_TEXCOORD0 - Uniforms.uData[52].xy; + float4 _309 = precise::min(Uniforms.uData[53], float4(precise::min(Uniforms.uData[52].z, Uniforms.uData[52].w))); + float _310 = _303.x; + float _331; + if (_310 > 0.0) + { + float _322; + if (_303.y > 0.0) + { + _322 = _309.z; + } + else + { + _322 = _309.y; + } + _331 = _322; + } + else + { + float _330; + if (_303.y > 0.0) + { + _330 = _309.w; + } + else + { + _330 = _309.x; + } + _331 = _330; + } + float2 _335 = (abs(_303) - Uniforms.uData[52].zw) + float2(_331); + float _337 = (_310 < 0.0) ? (-1.0) : 1.0; + float _340 = (_303.y < 0.0) ? (-1.0) : 1.0; + float _342 = _335.x; + bool _348; + if (_342 > 0.0) + { + _348 = _335.y > 0.0; + } + else + { + _348 = false; + } + float2 _362; + if (_348) + { + _362 = fast::normalize(_335) * float2(_337, _340); + } + else + { + float2 _361; + if (_342 > _335.y) + { + _361 = float2(_337, 0.0); + } + else + { + _361 = float2(0.0, _340); + } + _362 = _361; + } + float _369 = (precise::min(precise::max(_342, _335.y), 0.0) + length(precise::max(_335, float2(0.0)))) - _331; + float _370 = 1.0 - _369; + bool _371 = _370 < _134; + float _380; + float4 _381; + float _382; + if (_371) + { + _380 = _136; + _381 = _130; + _382 = _134; + } + else + { + bool _375 = _370 < _132; + _380 = _375 ? _136 : _117; + _381 = select(_122, Uniforms.uData[54], bool4(_375)); + _382 = _375 ? _370 : _132; + } + float2 _386 = -_362; + bool _387 = _370 < _128; + float2 _395; + float _396; + if (_387) + { + _395 = _124; + _396 = _128; + } + else + { + bool _391 = _370 < _126; + _395 = select(_120, _386, bool2(_391)); + _396 = _391 ? _370 : _126; + } + _400 = _380; + _401 = _395; + _402 = select(_124, _386, bool2(_387)); + _403 = _381; + _404 = select(_130, Uniforms.uData[54], bool4(_371)); + _405 = _369; + _406 = _396; + _407 = _387 ? _370 : _128; + _408 = _382; + _409 = _371 ? _370 : _134; + } + else { - _301 = 1.0 - _296; + _400 = _117; + _401 = _120; + _402 = _124; + _403 = _122; + _404 = _130; + _405 = -1000000000.0; + _406 = _126; + _407 = _128; + _408 = _132; + _409 = _134; + } + float _411 = precise::max(Uniforms.uData[1].x, 9.9999997473787516355514526367188e-05); + float _416 = precise::max(_411 - abs(_409 - _408), 0.0) / _411; + float _417 = _416 * _416; + float _418 = _417 * 0.5; + float _423; + if (_408 < _409) + { + _423 = 1.0 - _418; } else { - _301 = _296; + _423 = _418; } - float _305 = precise::min(_132, _130) - ((_295 * _289) * 0.25); - float _310 = precise::max(_289 - abs(_128 - _126), 0.0) / _289; - float _311 = _310 * _310; - float _312 = _311 * 0.5; - float _317; - if (_126 < _128) + float _427 = precise::min(_409, _408) - ((_417 * _411) * 0.25); + float _432 = precise::max(_411 - abs(_407 - _406), 0.0) / _411; + float _433 = _432 * _432; + float _434 = _433 * 0.5; + float _439; + if (_406 < _407) { - _317 = 1.0 - _312; + _439 = 1.0 - _434; } else { - _317 = _312; + _439 = _434; } - float _323 = fast::clamp(0.5 - _305, 0.0, 1.0); - if (_323 <= 0.0) + float _446 = fast::clamp(0.5 - precise::max(_427, _405), 0.0, 1.0); + if (_446 <= 0.0) { discard_fragment(); } if (in.in_var_COLOR.x < 0.5) { - _678 = float4(0.0, 0.0, 0.0, _323); + _803 = float4(0.0, 0.0, 0.0, _446); break; } - float4 _334 = mix(_124, _122, float4(_301)); - float2 _336 = mix(_120, _118, float2(_317)); - float _338 = precise::max(0.0, -(precise::min(_128, _126) - ((_311 * _289) * 0.25))); - float _339 = -_338; - float _342 = exp(_339 / Uniforms.uData[1].z); - float _348 = _334.y; - float _382; - float _383; - float2 _384; - if (_111) - { - float _356 = precise::max(precise::min(Uniforms.uData[5].y * mix(_116, _113, _301), Uniforms.uData[6].w), 1.0); - float _359 = fast::clamp(1.0 - (_338 / _356), 0.0, 1.0); - float _364 = (((Uniforms.uData[5].z * _356) * _348) * _359) * _359; - _382 = _359; - _383 = _364; - _384 = fast::clamp(((in.in_var_TEXCOORD0 - (_336 * _364)) - Uniforms.uData[0].xy) * Uniforms.uData[0].zw, float2(0.0), float2(1.0)); + float4 _457 = mix(_404, _403, float4(_423)); + float2 _459 = mix(_402, _401, float2(_439)); + float _461 = precise::max(0.0, -(precise::min(_407, _406) - ((_433 * _411) * 0.25))); + float _462 = -_461; + float _465 = exp(_462 / Uniforms.uData[1].z); + float _471 = _457.y; + float _505; + float _506; + float2 _507; + if (_115) + { + float _479 = precise::max(precise::min(Uniforms.uData[5].y * mix(_136, _400, _423), Uniforms.uData[6].w), 1.0); + float _482 = fast::clamp(1.0 - (_461 / _479), 0.0, 1.0); + float _487 = (((Uniforms.uData[5].z * _479) * _471) * _482) * _482; + _505 = _482; + _506 = _487; + _507 = fast::clamp(((in.in_var_TEXCOORD0 - (_459 * _487)) - Uniforms.uData[0].xy) * Uniforms.uData[0].zw, float2(0.0), float2(1.0)); } else { - _382 = 0.0; - _383 = 0.0; - _384 = fast::clamp(((in.in_var_TEXCOORD0 + (_336 * ((Uniforms.uData[2].x * _348) * _342))) - Uniforms.uData[0].xy) * Uniforms.uData[0].zw, float2(0.0), float2(1.0)); + _505 = 0.0; + _506 = 0.0; + _507 = fast::clamp(((in.in_var_TEXCOORD0 + (_459 * ((Uniforms.uData[2].x * _471) * _465))) - Uniforms.uData[0].xy) * Uniforms.uData[0].zw, float2(0.0), float2(1.0)); } - float4 _388 = Frost.sample(FrostSampler, _384, level(0.0)); - bool _390 = Uniforms.uData[4].w > 0.5; - float4 _398; - if (_390) + float4 _511 = Frost.sample(FrostSampler, _507, level(0.0)); + bool _513 = Uniforms.uData[4].w > 0.5; + float4 _521; + if (_513) { - _398 = Sharp.sample(SharpSampler, _384, level(0.0)); + _521 = Sharp.sample(SharpSampler, _507, level(0.0)); } else { - _398 = _388; + _521 = _511; } - bool _403; - if (_111) + bool _526; + if (_115) { - _403 = Uniforms.uData[6].y > 0.0; + _526 = Uniforms.uData[6].y > 0.0; } else { - _403 = false; + _526 = false; } - bool _407; - if (_403) + bool _530; + if (_526) { - _407 = _383 > 0.5; + _530 = _506 > 0.5; } else { - _407 = false; - } - float4 _468; - float4 _469; - if (_407) - { - float2 _415 = (_336 * ((_383 * 0.039999999105930328369140625) * Uniforms.uData[6].y)) * Uniforms.uData[0].zw; - float2 _417 = fast::clamp(_384 + _415, float2(0.0), float2(1.0)); - float2 _419 = fast::clamp(_384 - _415, float2(0.0), float2(1.0)); - float4 _423 = Frost.sample(FrostSampler, _417, level(0.0)); - float _428 = _388.w; - float _429 = (_423.x / precise::max(_423.w, 9.9999997473787516355514526367188e-05)) * _428; - float4 _430 = _388; - _430.x = _429; - float4 _434 = Frost.sample(FrostSampler, _419, level(0.0)); - float _439 = (_434.z / precise::max(_434.w, 9.9999997473787516355514526367188e-05)) * _428; - _430.z = _439; - float4 _467; - if (_390) + _530 = false; + } + float4 _591; + float4 _592; + if (_530) + { + float2 _538 = (_459 * ((_506 * 0.039999999105930328369140625) * Uniforms.uData[6].y)) * Uniforms.uData[0].zw; + float2 _540 = fast::clamp(_507 + _538, float2(0.0), float2(1.0)); + float2 _542 = fast::clamp(_507 - _538, float2(0.0), float2(1.0)); + float4 _546 = Frost.sample(FrostSampler, _540, level(0.0)); + float _551 = _511.w; + float _552 = (_546.x / precise::max(_546.w, 9.9999997473787516355514526367188e-05)) * _551; + float4 _553 = _511; + _553.x = _552; + float4 _557 = Frost.sample(FrostSampler, _542, level(0.0)); + float _562 = (_557.z / precise::max(_557.w, 9.9999997473787516355514526367188e-05)) * _551; + _553.z = _562; + float4 _590; + if (_513) { - float4 _447 = Sharp.sample(SharpSampler, _417, level(0.0)); - float4 _454 = _398; - _454.x = (_447.x / precise::max(_447.w, 9.9999997473787516355514526367188e-05)) * _398.w; - float4 _458 = Sharp.sample(SharpSampler, _419, level(0.0)); - _454.z = (_458.z / precise::max(_458.w, 9.9999997473787516355514526367188e-05)) * _398.w; - _467 = _454; + float4 _570 = Sharp.sample(SharpSampler, _540, level(0.0)); + float4 _577 = _521; + _577.x = (_570.x / precise::max(_570.w, 9.9999997473787516355514526367188e-05)) * _521.w; + float4 _581 = Sharp.sample(SharpSampler, _542, level(0.0)); + _577.z = (_581.z / precise::max(_581.w, 9.9999997473787516355514526367188e-05)) * _521.w; + _590 = _577; } else { - float4 _465 = _398; - _465.x = _429; - _465.z = _439; - _467 = _465; + float4 _588 = _521; + _588.x = _552; + _588.z = _562; + _590 = _588; } - _468 = _467; - _469 = _430; + _591 = _590; + _592 = _553; } else { - _468 = _398; - _469 = _388; + _591 = _521; + _592 = _511; } - float _477; + float _600; if (Uniforms.uData[6].x > 0.5) { - _477 = 1.0; + _600 = 1.0; } else { - _477 = _469.w; + _600 = _592.w; } - float4 _486 = float4(_469.xyz / float3(precise::max(_469.w, 9.9999997473787516355514526367188e-05)), 1.0); - float4 _495 = float4(_468.xyz / float3(precise::max(_468.w, 9.9999997473787516355514526367188e-05)), 1.0); - bool _497 = Uniforms.uData[4].z > 0.5; - float _502; - if (_497) + float4 _609 = float4(_592.xyz / float3(precise::max(_592.w, 9.9999997473787516355514526367188e-05)), 1.0); + float4 _618 = float4(_591.xyz / float3(precise::max(_591.w, 9.9999997473787516355514526367188e-05)), 1.0); + bool _620 = Uniforms.uData[4].z > 0.5; + float _625; + if (_620) { - _502 = Uniforms.uData[4].x; + _625 = Uniforms.uData[4].x; } else { - _502 = 0.0; + _625 = 0.0; } - float _511; - if (_111) + float _634; + if (_115) { - _511 = _502 * (1.0 - ((_382 * _382) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _502)))); + _634 = _625 * (1.0 - ((_505 * _505) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _625)))); } else { - _511 = _502; + _634 = _625; } - float _514 = 1.0 - _511; - float _522; - if (_111) + float _637 = 1.0 - _634; + float _645; + if (_115) { - _522 = _382; + _645 = _505; } else { - _522 = _342 * _342; + _645 = _465 * _465; } - float _525 = fast::clamp(((Uniforms.uData[2].y * _348) * _522) * _514, 0.0, 1.0); - float4 _531 = ((_495 * _525) + ((mix(_495, _486, float4(fast::clamp(_334.x, 0.0, 1.0))) * _514) * (1.0 - _525))) + (Uniforms.uData[3] * _511); - float _536 = dot(_336, float2(-0.707106769084930419921875)); - float _537 = precise::max(_536, 0.0); - float _539 = precise::max(-_536, 0.0); - float _542 = abs(_536); - bool _547; - if (_111) + float _648 = fast::clamp(((Uniforms.uData[2].y * _471) * _645) * _637, 0.0, 1.0); + float4 _654 = ((_618 * _648) + ((mix(_618, _609, float4(fast::clamp(_457.x, 0.0, 1.0))) * _637) * (1.0 - _648))) + (Uniforms.uData[3] * _634); + float _659 = dot(_459, float2(-0.707106769084930419921875)); + float _660 = precise::max(_659, 0.0); + float _662 = precise::max(-_659, 0.0); + float _665 = abs(_659); + bool _670; + if (_115) { - _547 = Uniforms.uData[6].z > 0.0; + _670 = Uniforms.uData[6].z > 0.0; } else { - _547 = false; + _670 = false; } - float _567; - if (_547) + float _690; + if (_670) { - _567 = (Uniforms.uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _542))) * exp((-precise::max(0.0, -_305)) / (Uniforms.uData[2].z * 2.5)); + _690 = (Uniforms.uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _665))) * exp((-precise::max(0.0, -_427)) / (Uniforms.uData[2].z * 2.5)); } else { - _567 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, Uniforms.uData[2].z * 0.625, -_305)); + _690 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, Uniforms.uData[2].z * 0.625, -_427)); } - float _575; - if (_111) + float _700; + if (_115) { - _575 = precise::max((Uniforms.uData[5].w * _382) * _382, _567); + _700 = precise::max((Uniforms.uData[5].w * _505) * _505, _690) * precise::min(_471, 1.0); } else { - _575 = 0.0; + _700 = 0.0; } - float _578 = _477 * (1.0 - _575); - float4 _584 = float4((_531 + (_486 * (1.0 - _531.w))).xyz * _578, _578 + _575); - float _603; - if (_111) + float _703 = _600 * (1.0 - _700); + float4 _709 = float4((_654 + (_609 * (1.0 - _654.w))).xyz * _703, _703 + _700); + float _728; + if (_115) { - float _594; - if (_547) + float _719; + if (_670) { - _594 = Uniforms.uData[6].z * smoothstep(0.75, 1.0, _542); + _719 = Uniforms.uData[6].z * smoothstep(0.75, 1.0, _665); } else { - _594 = 0.25; + _719 = 0.25; } - _603 = _594; + _728 = _719; } else { - _603 = (0.449999988079071044921875 * ((_537 * sqrt(_537)) + ((0.75 * _539) * sqrt(_539)))) + 0.0599999986588954925537109375; + _728 = (0.449999988079071044921875 * ((_660 * sqrt(_660)) + ((0.75 * _662) * sqrt(_662)))) + 0.0599999986588954925537109375; } - float _630; - if (_547) + float _755; + if (_670) { - _630 = exp((-precise::max(0.0, -_305)) / (Uniforms.uData[2].z * 1.25)); + _755 = exp((-precise::max(0.0, -_427)) / (Uniforms.uData[2].z * 1.25)); } else { - float _629; - if (_111) + float _754; + if (_115) { - float _617 = -_305; - _629 = smoothstep(Uniforms.uData[2].z * 0.3125, Uniforms.uData[2].z * 0.625, _617) * (1.0 - smoothstep(Uniforms.uData[2].z * 1.5, Uniforms.uData[2].z * 2.0, _617)); + float _742 = -_427; + _754 = smoothstep(Uniforms.uData[2].z * 0.3125, Uniforms.uData[2].z * 0.625, _742) * (1.0 - smoothstep(Uniforms.uData[2].z * 1.5, Uniforms.uData[2].z * 2.0, _742)); } else { - _629 = exp((-precise::max(0.0, -_305)) / Uniforms.uData[2].z); + _754 = exp((-precise::max(0.0, -_427)) / Uniforms.uData[2].z); } - _630 = _629; + _755 = _754; } - float _637; - if (_111) + float _762; + if (_115) { - _637 = 0.0; + _762 = 0.0; } else { - _637 = (0.039999999105930328369140625 * exp(_339 / Uniforms.uData[1].w)) * _537; + _762 = (0.039999999105930328369140625 * exp(_462 / Uniforms.uData[1].w)) * _660; } - float _643; - if (_497) + float _768; + if (_620) { - _643 = Uniforms.uData[4].y; + _768 = Uniforms.uData[4].y; } else { - _643 = 0.0; + _768 = 0.0; } - float3 _653 = _584.xyz + float3(fast::clamp((_643 + _334.z) + ((_348 * Uniforms.uData[2].w) * ((_630 * _603) + _637)), 0.0, 1.0)); - float4 _655 = fast::clamp(float4(_653.x, _653.y, _653.z, _584.w), float4(0.0), float4(1.0)); - float _662 = precise::max(_655.w, precise::max(_655.x, precise::max(_655.y, _655.z))); - float4 _663 = _655; - _663.w = _662; - float3 _674 = _663.xyz + float3(((fract(52.98291778564453125 * fract(dot(in.in_var_TEXCOORD0, float2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * Uniforms.uData[5].x) * _662); - _678 = fast::clamp(float4(_674.x, _674.y, _674.z, _663.w), float4(0.0), float4(1.0)) * _323; + float3 _778 = _709.xyz + float3(fast::clamp((_768 + _457.z) + ((_471 * Uniforms.uData[2].w) * ((_755 * _728) + _762)), 0.0, 1.0)); + float4 _780 = fast::clamp(float4(_778.x, _778.y, _778.z, _709.w), float4(0.0), float4(1.0)); + float _787 = precise::max(_780.w, precise::max(_780.x, precise::max(_780.y, _780.z))); + float4 _788 = _780; + _788.w = _787; + float3 _799 = _788.xyz + float3(((fract(52.98291778564453125 * fract(dot(in.in_var_TEXCOORD0, float2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * Uniforms.uData[5].x) * _787); + _803 = fast::clamp(float4(_799.x, _799.y, _799.z, _788.w), float4(0.0), float4(1.0)) * _446; break; } while(false); - out.out_var_SV_Target0 = _678; + out.out_var_SV_Target0 = _803; return out; } diff --git a/core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv b/core/gfx/shaders/compiled/spv/liquid_glass.fragment.spv index 4a36459a94f4db2d1388a82807b16918a1127b58..08756e77d205d542575a49d25cac554141bf5984 100644 GIT binary patch literal 18368 zcmZ{q1-Mnk*M|>nAc~6EUce3v3=9;+76kR1HSrjiXs(2m&ck&WAPm*G8^ z*tX}0k;Aqh4&RPw+O9Tc^w@C~-<}m}yRpMY?y~dnF|d{Yl+2ag;8NbUF%|N`I}97M zOKmrG_7}NS0-f*9v{ZU7N5o8h_PbyOThQ))vHC{OY=sfZ}dyT zoO#B^E&8RJ)h+ze6+fXR-ZJppZTE2t-y8n8`^Wdm-YpAn4@$~f%OOAT>%l9BUmm{y zu1E9g@xsjq+J|N_(kyr`c6&sCYbAz6)C ztk3Gq#?)publ!K?r)~{qb=K;+U$e%>Tpr#UJ3tP3YxB->MDHEth~7KNA>WVLJIFQU zt;g7{YTkN$9>ZIY&$l-A&gWlEZLnu0`~a|LN!~ivff|ho_l94$;+tB|*}!JL zZ}fvIeqwdbTyH%ndnfM~jy2n_!OZ5GehmS4gRH^+ZBX;pyEK)!2V#eN@+;hi%zdzR z4>yAJ{e_B$GCTeN`@C)pY46;RSgySZWN*B4jbR>}Ldp-pMt-we=G}WhE!(`7d;gBY z&$_mNlqdFISb+Ox_uuHz$jeyj}ZpXsT@0P5)6IfmBb}sDv&WYVfu)5fdD(w6&irr|i zy4dYf*!dk5yIsNR)~okr3|L>!)E&q?OJgDBe%JiQ{*Pn!960~P)94h^@9vp;)5e&?y%8;VZ9eV`^t-M&zCuD2hQ_4c1kH=)oSQ0QFmKq%`S zROpO(FqD{wOr|@u&>c3J?(jl)M4|Ic9|?J`t>Gxh8h+-i9bIFls;9>=+f(NW^j+U$ zA$>i&*VlV-9N3uZd`6FFcKl9Ebtgc|j3tiWiD3PDK<;5|oZTn)`6Q@2WDeTvZ(QXk zL*{H;@xgVDr+~Xc#!lX+g6D?R<@}uno(Ad+sn7X49jq?CXMpuv4~p-Z;P|SK?^$4V z@jV-?@A^=D&jH6*eSFUas~cFK(euFOA~{+?2T`Nva3Zu3q)qJ42gklOxDAwN=z?0O zJhBUGnRXrPXXYZX>kNeS(Pj`j&*{aG`jYfhcL@|fd!TLBxfDDRa^(5F46NMe*E4fD zv%|ercLj7MniX>$h8vdYOwOex(4hpmbz=9>mXx^6YF}gdD|1upMKhC8=D)z zsjalpHuubpU}G6ud;PVwCgWL0;@t$E2$lKY46i)--vV}+zq(tY+aP0!6YF-cxw(I? zr=K?3#^w&NHjdIp+vI;I*jUEaUjH(G>xk`LVC%Jh{fyI*?=R2f-H>&)g(gBdtM`D_ zoevFaG@svl!N!vO{?&gHq^-K!kac6e56U^YoONb^zrU8R!#p39S|0#Ugv=rS4}z83 z+xR~ORv!Q4^e{4Gt53{Fz>^?H&fBA4<=L;tR5IKzb&o^J+zWAHJprBsnO|Z(307__ zdmcaM+<{MlyF-^l+8fhn=4ob!>uU21^ep7M;_S(>@W{?FGH_D#uBH$uY&DwYSm90ZDaFVt#=R1DK^gb!@OUI zjAd-?^)K_cj@Z5dwodz@pK(&#o8U>1wdMYMtL6tWTcbA7ydfgK$T<2< zf|?-v?|FR}QnxasUEVj}0~_;Q$Y({{_aWsz7dgWp)bc}_?e~Yw4*RX{Bj{tuUW)C7 zHuB07=M%8;4~FtAd?vUNU zhePJ-`sS$4*n2|0=Bo_-Bh-UV`RzF~H_H|oBHzJuHgabkTB-X8K?d_$aey3 z{t;3hzn{RTLynx8pTXLx%h~z`to(vP_A6K$W%2zDtX%)(@jKW&riYTpA7JIl<4>@8 zn15pZ1=dbo^7tF9{K7)^4_F&z@imZg{qw%j3Ym47qj9v!v)CHUr+QC`O>}Jv-Q<34 z3!O2v&3f&?>Ps>|&t-e$>dh(k9l-8mNr%_=Q=rpF8}9*QObO;w-5cuD%c;QX9Fa|3 z%M_=V9bvRp7rSY|`nE3YI>BqJE_Ty`we##I-gNNVs>?of1{>dXBAXtpjq!3%&j8j| zU1T$YjValOxmc?USX*_u4`%}NseadJo8Qe{!S+&p)}9%x+&%F=@%i#u@|h}m<+<}` z!Pa$s-rj(;&D}FAa$~CVxmTBGc{b$gCm{1#o*nG)Synd(G$-WxV%OJ3UU}lo1vbup z_}ZHdn4930OWrf~rW;tf`R)Oq`=>j68KYds^-|}a`0N9ji?Pfhb?%RRLaj5Ny5!OW zY&}wP*%w>o=3>m`asasOi@EHM%$l9^JLf3yk&t(WHshGJIhr}Yd*&wgH1OVUYeV_n zGY@igIji%6m3db4o-`l4@_CWDH}iuX?v1(yAZ4y8&YBB?v*udZXU&C>tBc*jh24O{ zZV}|_@*ArsSYOHEX>Pynk`(`QRd@4Wd&^GHV4K`m#db|w0^4XBt21WY%D%4 zu~vXlo>+ZUGK{5eMQA0+SmMO$3pQ_i;yKb!8*O9LueM?OmyNbL|0{!yWo+&BH=Z>a z&pHyXKX~%|S3#yc`L7Cgn7_K!pw%H`i4$uLu=%@xuBV?i+Qw$h+QxXLjkd{uEwHhS zt-bzb{?-xOwZYbF{rVXv@BRb8e8{@F#+=o45UTTh=Kfw6Y%Iy|2+#CDu(s-QzYPNC zoaCNZ51I0H@R<)vt?MI8Z{t50nR0s@{~_?ovnD9>1&MSyft|8oVP({{p!qPTV{uOs2dJ#2N_G8{%#Mp zzo}I}ZM2Qe4z=DrFsIl!+Yisl2(Yn?t-bzb{?-xO9l_RVKlC$BYTF5%+H(KxjEoQY zG}ow2bR%n>zsa<-4t3@cqm8`s#MvEeoP+Vr^RWlKa(|DEhrIuXFz;E* z)rIu&{@)9k`gx%8ciP^yZPqCBFrVBj`+%$Um|fo-)fszV@PW+M;X2Xzn`J+Ed!Wv> z)Ft2j!S?lp^*I+ zTemjy$`j`>uyM-wx5MF;OXlVJM}U=^NBRDCBzzg8T*vkD92^DqUCUgIWe(~6(clTS z&Uos49(|WQ25dc2ayb^P++51{x8vZ;zL?AW$gJ5p>m3jFT=~A{zL$BP0B?Qj%(>K^ z2=998%pp3@$Vu>?J9VCyQg<@Ed#uiVkIwUR3j92^&gZMtoeJ;1=x2{g-D&U>YMphL zy3^%ro#(UEodNH8HKu1xUGAeZ!TwHneq|9?6PL7v=z2}VS*@=yF&d#M^^`0l~^-n#Qf%#PLpy^BUyc~sjmi1f#uY75I zlEano#!(m9Rbcnq^N>DY4dzqzS$%A;0lS9!0_4dhUQ%UiSG;c;l!`+`GVhsy?|7KAXw;Zm_j$(~V6DGrma4)eH853FUdZ5_dSN3Pxb!>oec0U)IRT2 zkAvM$NguyYo&fWye7sjrfVA`b&fY!=c8{_*Pl5SVdt<)pWBW8%eeSVmz}{o6q1-3W zBGXP?o}uT!e5(4@$Nzb-{`NioFCf!jUHo4J^QrvR$Nwd;J#pl$ybQ0%v!dP_JS*{e z1zGy&9=@tE!#y0!{2Ki0kozJ|+&92{sy_Puay*QCl;`11@EGLUWv#csyCN%pcfXB{ zPxVacqfL7MPObCZQakfbU*3hc_uAxHd9SunmptDG^Qr1rKY9HhARCR|JxlH%B2#Yc z$UXuqw_lNc3|8(Mk$nPIuIyNzCC}WaV9%X8b5-^k*uP__%il3R2MYKm|Kt6l!yL;?jXTQDyk3jE8ecvKeUiRTT zc;%k!)cifzn$_iZ?hjyVR+k!n1g8eqfo-YzCm4OSiT%&u*iT;bFX;5qCN=*G=2O+I zz6m^e&DNg#|2MFE=UMlx*pKLb2fK%p>+JU*$nCE>*VNB@vpu>$!5x_8r(pgIeoE#I z*{e>>f5U5|d}#gd{|{KXajnx_vM&lo!n@!3ZQe>FD0gvdd4|uix;9W-$UPP(w|3yE zuuE?3;iraAZXMvYQJ&nU04pzZ^9;GB@m;6nmD@w_>?y(en}aoK>%B7-vm^J;)bPrT zCr<4h!QLUbw$wfiGUfKjb>rvky4{d>0(WON7wwJdy3;Z{TvwavAZ4yAPJcUt-GBS* z8nJQC8q>XKm>sU6&5V#T*AVB~>H>C+1+h)NGa*a8vFnOVTXlKfW(NDb zsmrr63;2IME3=~0N1HsKvw_`Hb$Nbf2P3MUAN5UOpP$@gbNq+ToQ042_~gvZg{*uY zn&8Xlp&Pt5%5&zrgO!^PpMO0MJrJmOq;GTAvhurT9%RRbe@+#d4zSDif(s8^mG z7p-Mwor@t$o$AXv7l${FHaSmAfR&du^@3M!ZG5V8yrh7UJ7p>Or6JF;ICU=rF7K4y z@a3JdEW9?#Q}=RU<^Q$6%Oh9sNZl*cva;?z$WpiZvhEe(jiXIstOWKxD)+Z9yteAH zzx}|NGz~ls4L0k9WmTuG_)-fB>x9^Vopfm5DkbCA?)IRy_ z3pO9)`(C8aeqjC7Wgqqjk3eUvjk4Gs z1-3`(d=Io|M}zfO7rSG?JE60;u{##5t-AE~IIwb`@vM71yz5rFdW{p1xqs>s??kY^ z>Q2VaJM<*5@3o79^LK%hky*R%aq6u-f0H={URi8UEo{|kn_N$W_Z%>_^!jud<%=Q9 z-&@XrcRl~MY;4cRnaqxykF(&Fc^<@B<7}|+?@TTC$2l;{&BZekKWEQKH{|DnyEA)k zv^S>fp2zHPU2P^p=R>Y5&bk+Xvu@7Jg~+VK=U!R@vbOBgMPT)_Sb()%)9p$@Y<@XDL-_2&1D+u^eh>dhs4bVseT z@7{6NmRNV9(^nhoGFS65zmiv;I`0DOXYTe-+q?(d4fa`<9OfiUZ|{L|ePbktd%;5? zb>2C?yG#NbOPzOw_rZNNuWx#GKUiBwWDnFb*N*JLTBd2fZytitUwie&cfTHHc4WUE zfqxXTFJf&TD{OogABUcRv=L|BC&80Hi%%glH=o0$Ap7C7n0`DB)=xY4z_pBP{E}Cm zJ$VMKpKJSX4zaZsea%15*Rx>P_itC;r&;$ou)O-no(G$wcWL}z0LNc>Y+nRxuReSB z64lX4 zy#;R_xx3WI_HD5G+*9v>m7NZy5ATBY_Zd)c-sa-Eu5WJdF*|aW-iQC7wh^Z%AA(PV z%q6mqYFT-geT=NU%RYfO2hWl*^wB0UJ_Q?h-XiX2wX7Gid_R1SEOGSF#yELyzW{qq zwM*SOy2Sbci_Em)cPr{~{+({uIZx~`wI@;%u6 z?6E%PRPK-a6x$!b??Rpx{fv_ue*~-d?75#mF*~xKKP!i^JQopSjXc|K#>N*z;Az1{*Wa%pYL$)5hHXWOkU_&fveGzaeuG=T7~9hK=(_r8 zqixA6PacGC=w}@B(Khi~q4VF75Vhpi+AlFkUF_O`edkh@9<+t=J4sz+?ZC>dFSWLZ zH>SFr*$!Z9SO`*YUY^nXE}jCJHlch+O^M9AUvd~Dx%=HS6?$#-AIM+rd@r85_EG1% zU^~da@55I4XwwlU`Di~UvSp#vKMgW%__WA6)iOjavT19XvEn;jEi=!2uXjeKjdzAV zl}-Kkhv^HQYiJw4848^_X`8?8%m`MWJo7Hz1)2YT)2CX$`Ty~n37PBrcQEzYzpmir zkg3meJ9A;HKKIHjV0HeUPOY=T`yS*-%-N7>t1hzH!OG1ydp-wPc_@26Co*f39Ohl_ z`CQ1g(La0M1lC^N|Jw6z=#1qV)INKj-%j0;X>$)oK3n;BZfBn@&rJ{XkD&M8$+h>n QTai=Z8B_NcEA)o`4>Dg(HUIzs literal 15488 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 F{U5)H+_V4y diff --git a/core/gfx/shaders/liquid_glass.fragment.hlsl b/core/gfx/shaders/liquid_glass.fragment.hlsl index 31f1d22f2..d2246fc02 100644 --- a/core/gfx/shaders/liquid_glass.fragment.hlsl +++ b/core/gfx/shaders/liquid_glass.fragment.hlsl @@ -17,6 +17,9 @@ #define G 7 #define MAX_SHAPES 16 +// How far past its place's outline the outside of the place begins, in pixels: far enough that no +// sliver of it shows inside, near enough that a shape at the outline still runs into it. +#define EDGE_GAP 1.0 // No SV_POSITION: D3D12 links a pixel shader's inputs to the vertex shader's outputs by register, // and the default vertex shader's third output (the instance) sits where a pixel position read @@ -118,11 +121,28 @@ float4 main(PSInput input) : SV_Target0 } if (f < f1) { f2 = f1; o2 = o1; f1 = f; o1 = o; } else if (f < f2) { f2 = f; o2 = o; } } + // The place the glass belongs to (`LiquidField.edge`), in the last slot, flagged 2. Its outside + // is one more shape: a shape near the outline runs into it, a neck that joins it to the edge. + // Its outline cuts the glass's coverage, so none of it crosses; the rim and the bend come from + // the joined shape, so there is no rim along the cut, as if the glass carried on past it. + float inPlace = -1e9; + float4 e2 = uData[G + 3 * (MAX_SHAPES - 1) + 2]; + if (e2.w > 1.5) { + float4 e0 = uData[G + 3 * (MAX_SHAPES - 1)]; + float4 e1 = uData[G + 3 * (MAX_SHAPES - 1) + 1]; + float2 ge; + inPlace = sdRoundBox(p - e0.xy, e0.zw, min(e1, (float4)min(e0.z, e0.w)), ge); + float d = EDGE_GAP - inPlace; + float hs = h1; + if (d < d1) { d2 = d1; m2 = m1; h2 = h1; d1 = d; m1 = e2; h1 = hs; } else if (d < d2) { d2 = d; m2 = e2; h2 = hs; } + float2 o = -ge; + if (d < f1) { f2 = f1; o2 = o1; f1 = d; o1 = o; } else if (d < f2) { f2 = d; o2 = o; } + } float k = max(g1.x, 0.0001); float wm; float wf; float D = smin(d1, d2, k, wm); float F = smin(f1, f2, k, wf); - float cov = clamp(0.5 - D, 0.0, 1.0); + float cov = clamp(0.5 - max(D, inPlace), 0.0, 1.0); if (cov <= 0.0) discard; if (input.color.r < 0.5) return float4(0.0, 0.0, 0.0, cov); @@ -220,7 +240,9 @@ float4 main(PSInput input) : SV_Target0 bool diagonal = inward && g6.z > 0.0; float edge_dark = diagonal ? g6.z * (1.0 - smoothstep(0.2, 0.7, af)) * exp(-max(0.0, -D) / (2.0 * px)) : 0.85 * (1.0 - smoothstep(0.0, 0.5 * px, -D)); - float shade = inward ? max(g5.w * t * t, edge_dark) : 0.0; + // As much as the edge has formed (`lens`), as its bend, clearing and light are: glass that is + // barely a lens yet — a drop sunk into its place's edge — draws no dark line round itself. + float shade = inward ? max(g5.w * t * t, edge_dark) * min(lens, 1.0) : 0.0; float4 o = float4(c.rgb * (under * (1.0 - shade)), under * (1.0 - shade) + shade); // The lift and the rim's light (`drawLift`), brightest facing the top left. float spec = inward ? (diagonal ? g6.z * smoothstep(0.75, 1.0, af) : 0.25) diff --git a/tests/integration.zig b/tests/integration.zig index 5e3ec7e5b..4a3454f4d 100644 --- a/tests/integration.zig +++ b/tests/integration.zig @@ -4880,6 +4880,35 @@ test "liquid field: a rounded box's outline is its rect with its own corner per try std.testing.expect(f.hit(.{ .x = 50, .y = 30 }) and !f.hit(.{ .x = 2, .y = 2 })); } +test "liquid field: a shape near its place's edge joins it by a neck, and none of it crosses the edge" { + const LF = fizzy.core.LiquidField; + // A place 200 x 100 at the origin, a 24 radius at its top right; a drop 6px in from its top. + var f: LF = .{ .merge_px = 16, .edge = .{ .rect = .{ .x = 0, .y = 0, .w = 200, .h = 100 }, .radii = .{ 0, 24, 0, 0 } } }; + f.add(LF.Shape.circle(.{ .x = 100, .y = 26 }, 20)); + // Between the drop and the edge, a neck: glass where the drop alone has none. + try std.testing.expect(f.sample(.{ .x = 100, .y = 3 }).d < 0); + var alone: LF = .{ .merge_px = 16 }; + alone.add(LF.Shape.circle(.{ .x = 100, .y = 26 }, 20)); + try std.testing.expect(alone.sample(.{ .x = 100, .y = 3 }).d > 0); + // The neck is only near the drop: along the edge away from it, no glass, not even a sliver. + try std.testing.expectEqual(@as(f32, 0), f.sample(.{ .x = 20, .y = 0.5 }).coverage); + try std.testing.expectEqual(@as(f32, 0), f.sample(.{ .x = 20, .y = 3 }).coverage); + // A drop pushed past the edge is cut at it: none of it outside the place. + var past: LF = .{ .merge_px = 16, .edge = .{ .rect = .{ .x = 0, .y = 0, .w = 200, .h = 100 } } }; + past.add(LF.Shape.circle(.{ .x = 100, .y = 0 }, 20)); + try std.testing.expectEqual(@as(f32, 0), past.sample(.{ .x = 100, .y = -5 }).coverage); + try std.testing.expect(past.sample(.{ .x = 100, .y = 5 }).d < 0); + // Round the curved corner too: a point past the curve, inside the rect, is outside the place. + var corner: LF = .{ .merge_px = 16, .edge = .{ .rect = .{ .x = 0, .y = 0, .w = 200, .h = 100 }, .radii = .{ 0, 24, 0, 0 } } }; + corner.add(LF.Shape.circle(.{ .x = 200, .y = 0 }, 20)); + try std.testing.expectEqual(@as(f32, 0), corner.sample(.{ .x = 199, .y = 1 }).coverage); + try std.testing.expect(corner.sample(.{ .x = 190, .y = 10 }).d < 0); + // A field with an edge holds one shape fewer: the last slot is the edge's. + var full: LF = .{ .edge = .{ .rect = .{ .x = 0, .y = 0, .w = 200, .h = 100 } } }; + for (0..LF.max_shapes) |_| full.add(LF.Shape.circle(.{ .x = 50, .y = 50 }, 5)); + try std.testing.expectEqual(LF.max_shapes - 1, full.len); +} + test "liquid field: shapes bridge within the merge width, swell no more than a quarter of it, and part beyond" { const LF = fizzy.core.LiquidField; var f: LF = .{ .merge_px = 16 }; From 07d45fb2cb9c7813d63b39f393cd0d14dd196e61 Mon Sep 17 00:00:00 2001 From: foxnne Date: Sun, 18 Oct 2026 17:13:21 -0500 Subject: [PATCH 2/7] core: glass that is the window's own material --- backend/src/glass/LiquidField.zig | 9 +++++-- .../glass/shaders/liquid_glass.fragment.hlsl | 16 +++++++++--- core/dialogs.zig | 26 +++++++++++++++++++ src/editor/Editor.zig | 2 ++ 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/backend/src/glass/LiquidField.zig b/backend/src/glass/LiquidField.zig index f781f9e45..18aa3b507 100644 --- a/backend/src/glass/LiquidField.zig +++ b/backend/src/glass/LiquidField.zig @@ -83,6 +83,11 @@ refraction: f32 = 1, /// It carries text — a dialog, a menu — and takes the window's colour as the opacity has it /// (`glass_look.forText`). text: bool = false, +/// The window's own material (`tint`, the window's base colour, translucent): its colour in place +/// of what is behind it, as see-through as that colour is, so the desktop shows through it as it +/// does through the window round a card. Glass that reads as the window's edge reaching into a +/// card. Its tint is kept over the published look, as `surface`'s is. +window: bool = false, /// Drawn as over an opaque window wherever it is (`publishOpaqueWindow`): glass that must be glass /// even where the window is see-through behind it, as a place's corner button over the explorer, /// which is the window's own material. Off, it is as see-through there as the window is. @@ -241,7 +246,7 @@ pub fn pack(self: *const LiquidField, covered: dvui.Rect.Physical, has_sharp: bo .depths = .{ self.merge_px, liquid_glass.softness * rim, liquid_glass.depthPx(self.look(), rim), liquid_glass.falloff * glow }, .rim = .{ liquid_glass.refraction * rim * self.refraction, liquid_glass.clarity * @min(1, self.refraction), rim_line_width * s, @min(1, self.refraction) }, .tint = tint, - .face = .{ std.math.clamp(self.mix, 0, 1), std.math.clamp(self.lift, 0, 1), if (self.tint != null) 1 else 0, if (has_sharp) 1 else 0 }, + .face = .{ if (self.window) 2 else std.math.clamp(self.mix, 0, 1), std.math.clamp(self.lift, 0, 1), if (self.tint != null) 1 else 0, if (has_sharp) 1 else 0 }, .dither = .{ 1.0 / 255.0, 0, 0, 0 }, .shapes = undefined, }; @@ -346,7 +351,7 @@ fn applyLook(self: *const LiquidField, u: *Uniforms, l: glass_look.InApp) void { u.backdrop[3] = l.bevel_cap * self.scale; u.rim[1] = l.clarity * bend_scale; u.rim[3] = l.rim; - if (!self.surface) { + if (!self.surface and !self.window) { u.face[0] = std.math.clamp(l.mix, 0, 1); u.face[1] = std.math.clamp(l.lift, 0, 1); } diff --git a/backend/src/glass/shaders/liquid_glass.fragment.hlsl b/backend/src/glass/shaders/liquid_glass.fragment.hlsl index d2246fc02..94140fc4a 100644 --- a/backend/src/glass/shaders/liquid_glass.fragment.hlsl +++ b/backend/src/glass/shaders/liquid_glass.fragment.hlsl @@ -200,18 +200,22 @@ float4 main(PSInput input) : SV_Target0 // over the translucent picture as it was, the rim — where the clear glass lies over the // frost — came out more opaque than the face, and the material lit the face and not the rim. // Opaque where the window is (`LiquidField.publishOpaqueWindow`): its alpha is only its shape. - float under = uData[6].x > 0.5 ? 1.0 : frost.a; + // Glass that is the window's own material (`LiquidField.window`, its mix 2): the window's colour, + // the tint, in place of what is behind it, as see-through as that colour is, so the desktop's + // material shows through it as it does through the window round a card. + bool window = g4.x > 1.5; + float under = window ? g3.a : (uData[6].x > 0.5 ? 1.0 : frost.a); frost = float4(frost.rgb / max(frost.a, 1e-4), 1.0); sharp = float4(sharp.rgb / max(sharp.a, 1e-4), 1.0); // The frost at its share of a frost / tint mix, the blur coming in from sharp. The window's // colour comes in whatever the blur: the opacity is the colour, under clear glass too. - float mixv = g4.z > 0.5 ? g4.x : 0.0; + float mixv = window ? 1.0 : (g4.z > 0.5 ? g4.x : 0.0); // Apple's lens: the window's colour fades out across the band, as an OS window's does // (`glass_look.window`, its feather: strong most of the way, gone at the edge), so the edge stays // the lens bending what is behind it — unless the colour is near whole, when it covers the edge // too (`glass_look.shine_start`, 0.8 to 1). - if (inward) mixv *= 1.0 - t * t * (1.0 - smoothstep(0.8, 1.0, mixv)); + if (inward && !window) mixv *= 1.0 - t * t * (1.0 - smoothstep(0.8, 1.0, mixv)); float4 c = lerp(sharp, frost, b) * (1.0 - mixv); // The rim's clearer glass over it (`drawClear`): for Apple's lens, the band, so the rim stays // clear glass while the middle frosts. @@ -221,7 +225,11 @@ float4 main(PSInput input) : SV_Target0 c += g3 * mixv; // A translucent tint lies over the frost, not over what is behind the window: the glass is // opaque here, and `under` alone says how much of the desktop's material shows through it. - c += frost * (1.0 - c.a); + if (window) { + c = float4(g3.rgb / max(g3.a, 1e-4), 1.0); + } else { + c += frost * (1.0 - c.a); + } // The way the edge faces, against a light from the top left. float facing = dot(outv, float2(-0.70710678, -0.70710678)); float toward = max(facing, 0.0); diff --git a/core/dialogs.zig b/core/dialogs.zig index 44227fc6c..b6ac8c1c0 100644 --- a/core/dialogs.zig +++ b/core/dialogs.zig @@ -117,6 +117,23 @@ pub fn dialogFrost() ?widgets.FloatingWindowWidget.Frost { }; } +const window_base_key = "fizzy_window_base"; + +/// Host only, each frame: the colour the main window is drawn on — its base at its opacity over the +/// OS's material, clear on a window of OS glass — for glass that is the window's own material +/// (`BubblesOptions.window`). +pub fn publishWindowBase(c: dvui.Color) void { + const cw = dvui.currentWindow(); + dvui.dataSet(null, cw.data().id, window_base_key, c); +} + +/// The colour the main window is drawn on (`publishWindowBase`), or the chrome's before the host +/// has said. +pub fn windowBase() dvui.Color { + const cw = dvui.currentWindow(); + return dvui.dataGet(null, cw.data().id, window_base_key, dvui.Color) orelse style().chromeColor(); +} + /// Where the host records the dialog refraction setting — its own key beside the style, so a /// plugin built before it existed reads the style it knows and simply never asks for this. const refraction_key = "fizzy_dialog_refraction"; @@ -295,6 +312,10 @@ pub const BubblesOptions = struct { /// Glass even where the window behind it is see-through (`LiquidField.solid`): a control that /// must look the same over a bare stretch of the window's material as over a card. solid: bool = false, + /// The window's own material (`LiquidField.window`): the window's base colour (`windowBase`) in + /// place of what is behind the glass, as see-through as the window is, so over a card the glass + /// reads as the window's edge reaching into it. Over `tint`. + window: bool = false, }; /// Controls of glass over content, one look wherever they float: the demo player's transport, a @@ -329,6 +350,11 @@ pub fn glassBubbles(id: dvui.Id, bubbles: []const Bubble, scale: f32, opts: Bubb pane.mix = opts.mix; field.surface = true; } + if (opts.window) { + pane.tint = windowBase(); + pane.mix = 1; + field.window = true; + } pane.witness = opts.witness; widgets.BlurBackdrop.fieldPane(id, field, scale, pane); return; diff --git a/src/editor/Editor.zig b/src/editor/Editor.zig index 6d8526519..23357b283 100644 --- a/src/editor/Editor.zig +++ b/src/editor/Editor.zig @@ -4478,6 +4478,8 @@ pub fn tick(editor: *Editor) !dvui.App.Result { // (`core.glass_look.window`), and the frame draws none of its own. const glass_window = fizzy.backend.windowGlass(dvui.currentWindow(), windowGlassLook(editor.window_opacity_anim.value, dvui.windowRect().size())); const window_color = if (glass_window) dvui.Color{ .r = 0, .g = 0, .b = 0, .a = 0 } else windowBase(editor.window_opacity_anim.value); + // For glass that is the window's own material (`core.dialogs.windowBase`). + fizzy.core.dialogs.publishWindowBase(window_color); // Linux: the window is transparent and undecorated (`linux_titlebar`), so its shape is // this fill's — rounded while windowed, as the desktop rounds its own, with a hairline From b6e5a3180bd0e741ed72e2e47ee98c23876d041c Mon Sep 17 00:00:00 2001 From: foxnne Date: Sun, 18 Oct 2026 17:20:36 -0500 Subject: [PATCH 3/7] core: the glass program compiled for the window's own material --- .../compiled/dxil/liquid_glass.fragment.dxil | Bin 9864 -> 10068 bytes .../compiled/glsl/liquid_glass.fragment.glsl | 161 ++++++++++-------- .../compiled/msl/liquid_glass.fragment.msl | 161 ++++++++++-------- .../compiled/spv/liquid_glass.fragment.spv | Bin 18368 -> 18972 bytes 4 files changed, 188 insertions(+), 134 deletions(-) diff --git a/backend/src/glass/shaders/compiled/dxil/liquid_glass.fragment.dxil b/backend/src/glass/shaders/compiled/dxil/liquid_glass.fragment.dxil index 45e7841b789e0696277b0ae917b0d021dc77b570..7b37e24b9601dc695c084563d39ef53f63086bd4 100644 GIT binary patch delta 3141 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 delta 3011 zcmY*bdsGu=9-T=h!-OO-kw-&H=tm!0R@Ie%zVpNU}!~)g`Y}Z;{v3Uti z5lq6v23u`VIohg~G}RWjb_q}e0xcA@wDtisYh9%_vi2;n*zQCoo;CmE%kR$j-EV&P z{_e@pdXtiyu9By{{?nX`8x+l-|M=Cx>^~kI1wjA+Ofgn2mzER=09lUZ_dyX80RWt+ zQz-zDu3D1`5D%fm??(XuAWv)iQOE>H^Vq=emq-MKNJfWm|tAxse@R)lL~ z0-jbQ5{^c|lwpD92Ut!Kmm0({j-9=+sBc{P;hqQQkH%In))=t#5J-MpfDXbC-B^Vh z3z=QG5pQr+>H%t^O@Oq)cn5Yy#?>H0c!${T;%1^>;>=`d4|Zx%d|wT+iG8bour71} zYU+LZ!2VO=i12Ep<>S^}B? zO>F-ND}9t|GIdw9)-*Nvx>QveaeEs_`@{E;zD0GWwQs;zJd%E#=c}|8_cot6g##mefv%1T=vz3&z5|W(wZFp+OCqlf8`0XV`(|dXfLg%ZBo&4i)ot$#?2TGzTbYm z@S8ieirhPj6cO@)Wwdp;p}Vj7TC0+7irKk;@xFY`zL#IkF`NpDIN1}qRm<7Zm^aAV z5^=j8Sddov%lysB|H*wmB{w-mr7igGCQ2++J_Dxq^>YRTu3?e=waxxFIup6C=L&9m`KIbaV6)Hs*>U_-m3q>0b z@|Z8hwsURoahVxqZ$y=cA*nt6a4BkYr%psjmH{Yatgh(FC|zHiBt$fbM-49Sazl9* zGyR-^ACe`L#vBIAGZE@!HE}V-AgfEeA7%g2HoM{iDF8F~L9!V+p zha&{qhmJ&SEU};r5Fl`0@E7Kh)F?OrFyQ?<>kM*<1eBZsGrEEVOdB5~A0-xeeLKQA z4}-@)4SIF>=&xQhc+@;z9Rsx$8^k1~S}_^oJ<0+P#pBeeYR(J9{q!#@=I>nbu`%+q zjui#b3hf`p6{qQX@o@o@Jif*HCp4x`!}tsjCUr7Pyl|PAB;z$@{Wc!7^eywGoz1mc zetVWIDkD17Zhn?bl(9HTI+~`uYm}CF6mjX&XSqhK+CbWar({s6xL>lYq%BB;!+VG& zT4E2zUyo?Enta3yRvbu{p+kE@ytKyp5C}Fi`M;6NG!BZ+hmwSDz$t=>B z$5$98Rc&2G#{@xe91I41oFWbn98)LfhfFks{u1ls9J&vu`{r z4cyyB1l^drNDLF6;ut46k}h|ZOj^jCa0po=>NIYfyD~{~Cy}IE@5;elQG5nE)jOwOzve+S|1@Q~n#1u;=TJ^Un5t~u zgd>m8q(i{hGN38WB7(wx(v!Fj3!sKkMFH)Ry$M75xz%6Ks!tZk164|!{eYZlVSaeD!J4_iV_ZP`K4=vQ>Pr**+&YFT8b+weM( z9KQ+pydP!K%77Lvz-P!oX?UntveH7EREYzv=-zvkkgs@vTeXEL*l1maPzB*>2u8ATA0(|)kx*oFpU!zO~IS1%2>HTd0Bb!jND zkp2g5SKEy%@oCI!+)|kZRM@y$rKQewH`;Ed?S9(9@t(^RXTaZUeV!|%%kv2_F@3J_ zMUHaPj9(Sv=RYM@Z@G>wwV1=eat$8#Wc;^^2(@T7dUPQ-hE{@Y`7sk7u!CJ z$7qu_9U(6mHbTaFP1Qr~Ez4$dWG<*mS$|tfy6MQ&trkD<@o?!y$__FOel8eYHF=Ya zi6t@HJN*Dv{qri_EfSC@wx<`9JEGK{r3lH6DdH5A0n6>23-Dp6~A){ zyh8SZojA=c*i8nn9}p(~S8FKrBMBW#PWcgub7sEA4#AB8ETio?}}O6i13m>evnCUDMiELr`xbxb0CqeCZ?1x9@pNT-EG8QDgN7!d3Z s^NXR(zV>R4WHo>$K-8UIW@YvbjbE>P25-xJg8Sj#>lBFi=i>YP525y5=Kufz diff --git a/backend/src/glass/shaders/compiled/glsl/liquid_glass.fragment.glsl b/backend/src/glass/shaders/compiled/glsl/liquid_glass.fragment.glsl index 151d0b6d5..181cb9c5d 100644 --- a/backend/src/glass/shaders/compiled/glsl/liquid_glass.fragment.glsl +++ b/backend/src/glass/shaders/compiled/glsl/liquid_glass.fragment.glsl @@ -12,7 +12,7 @@ layout(location = 0) out highp vec4 out_var_SV_Target0; void main() { - highp vec4 _803; + highp vec4 _829; do { int _94 = int((vColor.y * 255.0) + 0.5); @@ -372,7 +372,7 @@ void main() } if (vColor.x < 0.5) { - _803 = vec4(0.0, 0.0, 0.0, _446); + _829 = vec4(0.0, 0.0, 0.0, _446); break; } highp vec4 _457 = mix(_404, _403, vec4(_423)); @@ -468,145 +468,172 @@ void main() _591 = _521; _592 = _511; } - highp float _600; - if (uData[6].x > 0.5) + bool _594 = uData[4].x > 1.5; + highp float _607; + if (_594) { - _600 = 1.0; + _607 = uData[3].w; } else { - _600 = _592.w; + highp float _606; + if (uData[6].x > 0.5) + { + _606 = 1.0; + } + else + { + _606 = _592.w; + } + _607 = _606; } - highp vec4 _609 = vec4(_592.xyz / vec3(max(_592.w, 9.9999997473787516355514526367188e-05)), 1.0); - highp vec4 _618 = vec4(_591.xyz / vec3(max(_591.w, 9.9999997473787516355514526367188e-05)), 1.0); - bool _620 = uData[4].z > 0.5; - highp float _625; - if (_620) + highp vec4 _616 = vec4(_592.xyz / vec3(max(_592.w, 9.9999997473787516355514526367188e-05)), 1.0); + highp vec4 _625 = vec4(_591.xyz / vec3(max(_591.w, 9.9999997473787516355514526367188e-05)), 1.0); + highp float _632; + if (_594) { - _625 = uData[4].x; + _632 = 1.0; } else { - _625 = 0.0; + _632 = (uData[4].z > 0.5) ? uData[4].x : 0.0; } - highp float _634; + bool _636; if (_115) { - _634 = _625 * (1.0 - ((_505 * _505) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _625)))); + _636 = !_594; } else { - _634 = _625; + _636 = false; } - highp float _637 = 1.0 - _634; highp float _645; + if (_636) + { + _645 = _632 * (1.0 - ((_505 * _505) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _632)))); + } + else + { + _645 = _632; + } + highp float _648 = 1.0 - _645; + highp float _656; if (_115) { - _645 = _505; + _656 = _505; + } + else + { + _656 = _465 * _465; + } + highp float _659 = clamp(((uData[2].y * _471) * _656) * _648, 0.0, 1.0); + highp vec4 _665 = ((_625 * _659) + ((mix(_625, _616, vec4(clamp(_457.x, 0.0, 1.0))) * _648) * (1.0 - _659))) + (uData[3] * _645); + highp vec4 _682; + if (_594) + { + _682 = vec4(uData[3].xyz / vec3(max(uData[3].w, 9.9999997473787516355514526367188e-05)), 1.0); } else { - _645 = _465 * _465; + _682 = _665 + (_616 * (1.0 - _665.w)); } - highp float _648 = clamp(((uData[2].y * _471) * _645) * _637, 0.0, 1.0); - highp vec4 _654 = ((_618 * _648) + ((mix(_618, _609, vec4(clamp(_457.x, 0.0, 1.0))) * _637) * (1.0 - _648))) + (uData[3] * _634); - highp float _659 = dot(_459, vec2(-0.707106769084930419921875)); - highp float _660 = max(_659, 0.0); - highp float _662 = max(-_659, 0.0); - highp float _665 = abs(_659); - bool _670; + highp float _683 = dot(_459, vec2(-0.707106769084930419921875)); + highp float _684 = max(_683, 0.0); + highp float _686 = max(-_683, 0.0); + highp float _689 = abs(_683); + bool _694; if (_115) { - _670 = uData[6].z > 0.0; + _694 = uData[6].z > 0.0; } else { - _670 = false; + _694 = false; } - highp float _690; - if (_670) + highp float _714; + if (_694) { - _690 = (uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _665))) * exp((-max(0.0, -_427)) / (uData[2].z * 2.5)); + _714 = (uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _689))) * exp((-max(0.0, -_427)) / (uData[2].z * 2.5)); } else { - _690 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, uData[2].z * 0.625, -_427)); + _714 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, uData[2].z * 0.625, -_427)); } - highp float _700; + highp float _724; if (_115) { - _700 = max((uData[5].w * _505) * _505, _690) * min(_471, 1.0); + _724 = max((uData[5].w * _505) * _505, _714) * min(_471, 1.0); } else { - _700 = 0.0; + _724 = 0.0; } - highp float _703 = _600 * (1.0 - _700); - highp vec4 _709 = vec4((_654 + (_609 * (1.0 - _654.w))).xyz * _703, _703 + _700); - highp float _728; + highp float _727 = _607 * (1.0 - _724); + highp vec4 _733 = vec4(_682.xyz * _727, _727 + _724); + highp float _752; if (_115) { - highp float _719; - if (_670) + highp float _743; + if (_694) { - _719 = uData[6].z * smoothstep(0.75, 1.0, _665); + _743 = uData[6].z * smoothstep(0.75, 1.0, _689); } else { - _719 = 0.25; + _743 = 0.25; } - _728 = _719; + _752 = _743; } else { - _728 = (0.449999988079071044921875 * ((_660 * sqrt(_660)) + ((0.75 * _662) * sqrt(_662)))) + 0.0599999986588954925537109375; + _752 = (0.449999988079071044921875 * ((_684 * sqrt(_684)) + ((0.75 * _686) * sqrt(_686)))) + 0.0599999986588954925537109375; } - highp float _755; - if (_670) + highp float _779; + if (_694) { - _755 = exp((-max(0.0, -_427)) / (uData[2].z * 1.25)); + _779 = exp((-max(0.0, -_427)) / (uData[2].z * 1.25)); } else { - highp float _754; + highp float _778; if (_115) { - highp float _742 = -_427; - _754 = smoothstep(uData[2].z * 0.3125, uData[2].z * 0.625, _742) * (1.0 - smoothstep(uData[2].z * 1.5, uData[2].z * 2.0, _742)); + highp float _766 = -_427; + _778 = smoothstep(uData[2].z * 0.3125, uData[2].z * 0.625, _766) * (1.0 - smoothstep(uData[2].z * 1.5, uData[2].z * 2.0, _766)); } else { - _754 = exp((-max(0.0, -_427)) / uData[2].z); + _778 = exp((-max(0.0, -_427)) / uData[2].z); } - _755 = _754; + _779 = _778; } - highp float _762; + highp float _786; if (_115) { - _762 = 0.0; + _786 = 0.0; } else { - _762 = (0.039999999105930328369140625 * exp(_462 / uData[1].w)) * _660; + _786 = (0.039999999105930328369140625 * exp(_462 / uData[1].w)) * _684; } - highp float _768; - if (_620) + highp float _794; + if (uData[4].z > 0.5) { - _768 = uData[4].y; + _794 = uData[4].y; } else { - _768 = 0.0; + _794 = 0.0; } - highp vec3 _778 = _709.xyz + vec3(clamp((_768 + _457.z) + ((_471 * uData[2].w) * ((_755 * _728) + _762)), 0.0, 1.0)); - highp vec4 _780 = clamp(vec4(_778.x, _778.y, _778.z, _709.w), vec4(0.0), vec4(1.0)); - highp float _787 = max(_780.w, max(_780.x, max(_780.y, _780.z))); - highp vec4 _788 = _780; - _788.w = _787; - highp vec3 _799 = _788.xyz + vec3(((fract(52.98291778564453125 * fract(dot(vTextureCoord, vec2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * uData[5].x) * _787); - _803 = clamp(vec4(_799.x, _799.y, _799.z, _788.w), vec4(0.0), vec4(1.0)) * _446; + highp vec3 _804 = _733.xyz + vec3(clamp((_794 + _457.z) + ((_471 * uData[2].w) * ((_779 * _752) + _786)), 0.0, 1.0)); + highp vec4 _806 = clamp(vec4(_804.x, _804.y, _804.z, _733.w), vec4(0.0), vec4(1.0)); + highp float _813 = max(_806.w, max(_806.x, max(_806.y, _806.z))); + highp vec4 _814 = _806; + _814.w = _813; + highp vec3 _825 = _814.xyz + vec3(((fract(52.98291778564453125 * fract(dot(vTextureCoord, vec2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * uData[5].x) * _813); + _829 = clamp(vec4(_825.x, _825.y, _825.z, _814.w), vec4(0.0), vec4(1.0)) * _446; break; } while(false); - out_var_SV_Target0 = _803; + out_var_SV_Target0 = _829; } diff --git a/backend/src/glass/shaders/compiled/msl/liquid_glass.fragment.msl b/backend/src/glass/shaders/compiled/msl/liquid_glass.fragment.msl index abb47814a..716e0adbf 100644 --- a/backend/src/glass/shaders/compiled/msl/liquid_glass.fragment.msl +++ b/backend/src/glass/shaders/compiled/msl/liquid_glass.fragment.msl @@ -22,7 +22,7 @@ struct main0_in fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], texture2d Frost [[texture(0)]], texture2d Sharp [[texture(1)]], sampler FrostSampler [[sampler(0)]], sampler SharpSampler [[sampler(1)]]) { main0_out out = {}; - float4 _803; + float4 _829; do { int _94 = int((in.in_var_COLOR.y * 255.0) + 0.5); @@ -374,7 +374,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo } if (in.in_var_COLOR.x < 0.5) { - _803 = float4(0.0, 0.0, 0.0, _446); + _829 = float4(0.0, 0.0, 0.0, _446); break; } float4 _457 = mix(_404, _403, float4(_423)); @@ -470,146 +470,173 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo _591 = _521; _592 = _511; } - float _600; - if (Uniforms.uData[6].x > 0.5) + bool _594 = Uniforms.uData[4].x > 1.5; + float _607; + if (_594) { - _600 = 1.0; + _607 = Uniforms.uData[3].w; } else { - _600 = _592.w; + float _606; + if (Uniforms.uData[6].x > 0.5) + { + _606 = 1.0; + } + else + { + _606 = _592.w; + } + _607 = _606; } - float4 _609 = float4(_592.xyz / float3(precise::max(_592.w, 9.9999997473787516355514526367188e-05)), 1.0); - float4 _618 = float4(_591.xyz / float3(precise::max(_591.w, 9.9999997473787516355514526367188e-05)), 1.0); - bool _620 = Uniforms.uData[4].z > 0.5; - float _625; - if (_620) + float4 _616 = float4(_592.xyz / float3(precise::max(_592.w, 9.9999997473787516355514526367188e-05)), 1.0); + float4 _625 = float4(_591.xyz / float3(precise::max(_591.w, 9.9999997473787516355514526367188e-05)), 1.0); + float _632; + if (_594) { - _625 = Uniforms.uData[4].x; + _632 = 1.0; } else { - _625 = 0.0; + _632 = (Uniforms.uData[4].z > 0.5) ? Uniforms.uData[4].x : 0.0; } - float _634; + bool _636; if (_115) { - _634 = _625 * (1.0 - ((_505 * _505) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _625)))); + _636 = !_594; } else { - _634 = _625; + _636 = false; } - float _637 = 1.0 - _634; float _645; + if (_636) + { + _645 = _632 * (1.0 - ((_505 * _505) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _632)))); + } + else + { + _645 = _632; + } + float _648 = 1.0 - _645; + float _656; if (_115) { - _645 = _505; + _656 = _505; + } + else + { + _656 = _465 * _465; + } + float _659 = fast::clamp(((Uniforms.uData[2].y * _471) * _656) * _648, 0.0, 1.0); + float4 _665 = ((_625 * _659) + ((mix(_625, _616, float4(fast::clamp(_457.x, 0.0, 1.0))) * _648) * (1.0 - _659))) + (Uniforms.uData[3] * _645); + float4 _682; + if (_594) + { + _682 = float4(Uniforms.uData[3].xyz / float3(precise::max(Uniforms.uData[3].w, 9.9999997473787516355514526367188e-05)), 1.0); } else { - _645 = _465 * _465; + _682 = _665 + (_616 * (1.0 - _665.w)); } - float _648 = fast::clamp(((Uniforms.uData[2].y * _471) * _645) * _637, 0.0, 1.0); - float4 _654 = ((_618 * _648) + ((mix(_618, _609, float4(fast::clamp(_457.x, 0.0, 1.0))) * _637) * (1.0 - _648))) + (Uniforms.uData[3] * _634); - float _659 = dot(_459, float2(-0.707106769084930419921875)); - float _660 = precise::max(_659, 0.0); - float _662 = precise::max(-_659, 0.0); - float _665 = abs(_659); - bool _670; + float _683 = dot(_459, float2(-0.707106769084930419921875)); + float _684 = precise::max(_683, 0.0); + float _686 = precise::max(-_683, 0.0); + float _689 = abs(_683); + bool _694; if (_115) { - _670 = Uniforms.uData[6].z > 0.0; + _694 = Uniforms.uData[6].z > 0.0; } else { - _670 = false; + _694 = false; } - float _690; - if (_670) + float _714; + if (_694) { - _690 = (Uniforms.uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _665))) * exp((-precise::max(0.0, -_427)) / (Uniforms.uData[2].z * 2.5)); + _714 = (Uniforms.uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _689))) * exp((-precise::max(0.0, -_427)) / (Uniforms.uData[2].z * 2.5)); } else { - _690 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, Uniforms.uData[2].z * 0.625, -_427)); + _714 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, Uniforms.uData[2].z * 0.625, -_427)); } - float _700; + float _724; if (_115) { - _700 = precise::max((Uniforms.uData[5].w * _505) * _505, _690) * precise::min(_471, 1.0); + _724 = precise::max((Uniforms.uData[5].w * _505) * _505, _714) * precise::min(_471, 1.0); } else { - _700 = 0.0; + _724 = 0.0; } - float _703 = _600 * (1.0 - _700); - float4 _709 = float4((_654 + (_609 * (1.0 - _654.w))).xyz * _703, _703 + _700); - float _728; + float _727 = _607 * (1.0 - _724); + float4 _733 = float4(_682.xyz * _727, _727 + _724); + float _752; if (_115) { - float _719; - if (_670) + float _743; + if (_694) { - _719 = Uniforms.uData[6].z * smoothstep(0.75, 1.0, _665); + _743 = Uniforms.uData[6].z * smoothstep(0.75, 1.0, _689); } else { - _719 = 0.25; + _743 = 0.25; } - _728 = _719; + _752 = _743; } else { - _728 = (0.449999988079071044921875 * ((_660 * sqrt(_660)) + ((0.75 * _662) * sqrt(_662)))) + 0.0599999986588954925537109375; + _752 = (0.449999988079071044921875 * ((_684 * sqrt(_684)) + ((0.75 * _686) * sqrt(_686)))) + 0.0599999986588954925537109375; } - float _755; - if (_670) + float _779; + if (_694) { - _755 = exp((-precise::max(0.0, -_427)) / (Uniforms.uData[2].z * 1.25)); + _779 = exp((-precise::max(0.0, -_427)) / (Uniforms.uData[2].z * 1.25)); } else { - float _754; + float _778; if (_115) { - float _742 = -_427; - _754 = smoothstep(Uniforms.uData[2].z * 0.3125, Uniforms.uData[2].z * 0.625, _742) * (1.0 - smoothstep(Uniforms.uData[2].z * 1.5, Uniforms.uData[2].z * 2.0, _742)); + float _766 = -_427; + _778 = smoothstep(Uniforms.uData[2].z * 0.3125, Uniforms.uData[2].z * 0.625, _766) * (1.0 - smoothstep(Uniforms.uData[2].z * 1.5, Uniforms.uData[2].z * 2.0, _766)); } else { - _754 = exp((-precise::max(0.0, -_427)) / Uniforms.uData[2].z); + _778 = exp((-precise::max(0.0, -_427)) / Uniforms.uData[2].z); } - _755 = _754; + _779 = _778; } - float _762; + float _786; if (_115) { - _762 = 0.0; + _786 = 0.0; } else { - _762 = (0.039999999105930328369140625 * exp(_462 / Uniforms.uData[1].w)) * _660; + _786 = (0.039999999105930328369140625 * exp(_462 / Uniforms.uData[1].w)) * _684; } - float _768; - if (_620) + float _794; + if (Uniforms.uData[4].z > 0.5) { - _768 = Uniforms.uData[4].y; + _794 = Uniforms.uData[4].y; } else { - _768 = 0.0; + _794 = 0.0; } - float3 _778 = _709.xyz + float3(fast::clamp((_768 + _457.z) + ((_471 * Uniforms.uData[2].w) * ((_755 * _728) + _762)), 0.0, 1.0)); - float4 _780 = fast::clamp(float4(_778.x, _778.y, _778.z, _709.w), float4(0.0), float4(1.0)); - float _787 = precise::max(_780.w, precise::max(_780.x, precise::max(_780.y, _780.z))); - float4 _788 = _780; - _788.w = _787; - float3 _799 = _788.xyz + float3(((fract(52.98291778564453125 * fract(dot(in.in_var_TEXCOORD0, float2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * Uniforms.uData[5].x) * _787); - _803 = fast::clamp(float4(_799.x, _799.y, _799.z, _788.w), float4(0.0), float4(1.0)) * _446; + float3 _804 = _733.xyz + float3(fast::clamp((_794 + _457.z) + ((_471 * Uniforms.uData[2].w) * ((_779 * _752) + _786)), 0.0, 1.0)); + float4 _806 = fast::clamp(float4(_804.x, _804.y, _804.z, _733.w), float4(0.0), float4(1.0)); + float _813 = precise::max(_806.w, precise::max(_806.x, precise::max(_806.y, _806.z))); + float4 _814 = _806; + _814.w = _813; + float3 _825 = _814.xyz + float3(((fract(52.98291778564453125 * fract(dot(in.in_var_TEXCOORD0, float2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * Uniforms.uData[5].x) * _813); + _829 = fast::clamp(float4(_825.x, _825.y, _825.z, _814.w), float4(0.0), float4(1.0)) * _446; break; } while(false); - out.out_var_SV_Target0 = _803; + out.out_var_SV_Target0 = _829; return out; } diff --git a/backend/src/glass/shaders/compiled/spv/liquid_glass.fragment.spv b/backend/src/glass/shaders/compiled/spv/liquid_glass.fragment.spv index 08756e77d205d542575a49d25cac554141bf5984..03cf214283617cdf09df3d3b1e0a8fcfb3d96910 100644 GIT binary patch delta 5123 zcmYkAd6-va6~`|#ENTo%zm#HxX+~4hL{XL*Xy~ygAM~FX)HHW(2SXz>)3h6zTPda& z&B{u0L#d3iGF#LZ%@zx@w8gS4%{HUl#hdY&#Cd`I_eGR+9T(&7iN1S z+;NEZEay#NdB1RPZaDM9Ev%RHmWI@Kgd%1!z7@p;255K5DSc&i^uoS!73^}vZl&}S z&{MkhcIj_J*B(jftKn86+EeCPA|DSZu!2@KF~=^l%BfczZl(O z^hwT3z!!IwW2>0TbK$d7L6W1`1JR+E(bZ?=>*4H@e(BJqV14A`cNy3&XYq1>RJ?g;wsYg>(C9-mS%1QdwjK}m)C=h-Pl#FTe4%nr}qX_=eLoXoojt_AvaZ< z7kA3fz@$Z+z@CDVm%|hCS#-I)#h+{WYST9>zY*O#!7zU8K93^5H{8_bf!mDkd=n9J z&#f=Z7mVV|W%(kyob3^3lAEeEoRTlW+=Mv2jf|&b9Rr{#F^<9@EZIWD1Ge0d<})t%le7)ncD|(J=v(QgWV_{bM?LfHkr2c^_$>LWJ+Ie zMb93%DDCmR4Xi!+Zuc77)a5w(1J8~67P^kNBWd)v!6r4K_9XZY*rK%CJv-v+?LvIM z3wDmz5Sv2oK(C7~n|9m@;RJTVNet2{-S{5ZZs?c9cY)`_X=_hz5qaFc54K3RLw^f% z>wduK*Pm1JLx>+CRxi%p-Cb=M?(F|#n7a|*$xQzfba}Zne+##{le<|X?X*o%2XwR-7{^5-DiWJKz`4}2`5%^UCU zWA}rN)aH$M-+$5Q#%A@u1ncV;?pF=xI>Y^Xj4N5lb8poHC?+sK2Pd%izhU%C>wk;> zAYvQD`uwite|Eo8EiE^e2M=HIPvoMmi1=* z6~zD@vO#|Xn^;?t{vEuCq}t;57+8J~zFoKr$dUR7*oC@u{auuO-|CCtmpAR782*L0 za&d0(`i zLH~E-BTiTT1HKWlm~hWFT>BaNFWlH7#tzQ^&`YcEcsv{8E!(u?F*OJ zy$ddRjM2wDdCt1gJ(c<;&qOc$X#j_ECR@&cu#6Fxz3YLfdSRMYXrct%~pSu)Ck07vs#6RZd0M?g4a5d%%9# z(rG2c^AT4*jnPSTQkPGn?ZLYfcrWFLn+_-M)oaHYpWXawu zg<$`0ft=%i((eE#pFin$1pB!PZ8MM;Ar>u8t`~#NWy>wlIDPbO^h0YxLn@vLHqb;? zu5%Lhg6|@m#bp&c!5O11ezU5zukH-ZhVgHTS&ZpfA6VYCC&Np?X31xHY&OGP# z(2nrimp?=Xu=ckkKazHa^X-42n!f*xS-YUg=)Jtyw*EWlrH#if)zGrua#u7P;@=Kh zW-|^UseLy%efT%d&1pEOaqeXeXRgHV-f;FOKZy5$)5q6=n6l6*#+SE@PNH*cd$w#A zrSCyB!(Wc)g0-j6{9?Qp++o;yzIq142vOki1R)zXYF}H_62M6 z2xhhO(N9A6OU?yw`f3Ze5G-%OBS$scxd=r=B-=R%=js$cE6;KB7!1MdV|+GvFadrH%&ZJksEU8Xg);DJubSK6;5LplYQnq>$zS^E8auE4H=WbKX delta 4656 zcmZ9PXOvde6^8H3&?JK;BTfWi#L6!hJx(YF6kXtruxY=o7+8!!-xw@69Q;+$QEUSC znUBA=O_9xzxkY=0=P~M|F25mp%)5Dev2M`|(Sg!*()kMt(`gIv@{A@Wph_^g7*b4v zn}Tk9Z>w#M?sCgoGh1gJy%n78_9EKHR(Mbq{X2B+k>e@LtXspGS$j&|1}yIrZrhqO zKis&g-0f;o-(HFs#W)qk1O{lgowTrhYu>n?atGMSNVD`E(NntiX6ZYjYmcP#o#D1e zw5QBn!16xfcCERel)hU{>YLJcM=^l`+ATdvrnTC}ca?j{{{T^&CZMKmr9P2hl7l+hN_GW-3 zX2lO}&0ce4`3D$FTnTXxJVJ*enfi}lr#90ml1^337+c zgtU9_Fdgi^o=yDzcak8Ed-UmO1{tv_jDDuwk!AKlqYtC8&r;LHW5c^V$9v1!0>AV*)`Z%vX z7bCL~=M`t(OTd{o=j77Xs%v+Zc9YGx4D4oH%9tbeC$Q1lvW|ZSU&$h89WO`En%jl; z`2Gc~J^B6$c10)S$3luA= zMs7gA(Rat{RkB$(f%n4E_aqbF3@7jH5bhRqdAV%Gt?1bb?G_W;ZMDs%_d2t=*2IZJ zrreIfKppJLN-gTfpXu`H@*iO1EZo)8H=pS{z`n3&v}RB2F7E_6l@Td@4tNVhoA-#n zB<=zmsm<5kH}>vYH#W<24_IHHaQD`nt%tj>k1J{9#}CH+C?+sK2PbfI9$@sz<~)f0 z5aKe3^?A7Ac>-*pe(r&jnb--M zx_tKFNw9HFK8}3xwHae=ptG#6t*1~dpa=1i%*0QF)wPFv25gaDka-`U1t(rUzR!X6 z*Pd;A9&BE1WgK5XS%ah+EN)B0RdCYYa%Ur1iPyo#8>`*IEyjs0z~bIu^qF6j zeLu3^gm|lV5@%K32G2q)CfqwU*Sx;og==14|3SA1&yzWf(I+|H1Dkg_Zn%E({hC}2 zE}wx9;F8E7eN2>V@I$bNRlg+v2y6k`?A~te-UhRh{{>rcnd|3c2q((2>k#Kpz}mCy zpMvG=Aj|$4*m##+dvjt7$a(o6*aBT;V#=zj<{r6MasEH}b;JW>po!Au=V0v~K=NyVRv_>>%1`onIwHP$%|H{RxcOkat&)RHnm6Zbum$R4 zao;fdSlk}qZ;|hix!!*&xt+cT`=^9U>BI(FvCd6hK829KFwQ&{q;K-IVSAWlQ2oWV z`{_com2vDq@z=Gstik{kKSkQY4Ft>EVY(fJZc=SIxP!qq;qP4S7Un_i<-+rOG6W8v z5&l<6K2Hmv`yIHyHGRGAQA5#Wbe~)-Q2lZ6i`rvoYxa60%TBaRq-8Y8*F@6TFgShq z*Uv3jb5Q-qHZQqh-Jn{p=*(vn^ From 870dcd09d778498ce965974d08137911ad8906a7 Mon Sep 17 00:00:00 2001 From: foxnne Date: Sun, 18 Oct 2026 20:07:00 -0500 Subject: [PATCH 4/7] core: orb geometry The shapes a control of glass is made of (circles, capsules, panels as rounded boxes), the cut of one by its place's outline as a polygon, and shape interpolation: the geometry the orb system builds on, std-only and tested. Co-Authored-By: Claude Opus 5.5 --- build/app.zig | 3 + core/core.zig | 3 + core/orb_geometry.zig | 201 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 core/orb_geometry.zig diff --git a/build/app.zig b/build/app.zig index 34aa2bf80..be423d126 100644 --- a/build/app.zig +++ b/build/app.zig @@ -859,6 +859,9 @@ pub fn construct(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. // What the app's windows are made of, and the fallback to what the backend can show. // std-only but for `publish`/`get`, which reach dvui and are not under test here. .{ "fizzy-material-tests", "core/material.zig" }, + // An orb's geometry: shapes, how one becomes another, the polygons the no-glass path fills + // (plans/LIQUID_EDGE_PLAN.md, "The orb system"). std-only. + .{ "fizzy-orb-geometry-tests", "core/orb_geometry.zig" }, // The in-app menu bar's order: fizzy's menus, then plugins', then Help. std-only (see // menu_order.zig); `Menu.draw` walks it. .{ "fizzy-menu-order-tests", "src/editor/menu_order.zig" }, diff --git a/core/core.zig b/core/core.zig index f067ce0a7..e098995bf 100644 --- a/core/core.zig +++ b/core/core.zig @@ -79,6 +79,9 @@ pub const native_glass = @import("native_glass.zig"); /// What the app's windows are made of, as the backend can show it: the one material every glass /// in the app and every OS window follows. pub const material = @import("material.zig"); +/// An orb's geometry: the shapes a control of glass is made of, cut by its place and carried from +/// one state to the next (`plans/LIQUID_EDGE_PLAN.md`, "The orb system"). +pub const orb_geometry = @import("orb_geometry.zig"); /// Generic momentum/fling helper (pan, scrub, cover-flow). pub const Fling = @import("Fling.zig"); diff --git a/core/orb_geometry.zig b/core/orb_geometry.zig new file mode 100644 index 000000000..63594eaec --- /dev/null +++ b/core/orb_geometry.zig @@ -0,0 +1,201 @@ +//! An orb's geometry (`plans/LIQUID_EDGE_PLAN.md`, "The orb system"): the shapes a control of glass +//! is made of, how one becomes another, and the polygons the no-glass path fills. +//! +//! - A **shape** is a rounded box: a circle, a capsule or a panel, by its centre, half-size and +//! four corner radii (top-left, top-right, bottom-right, bottom-left; y down), as the glass +//! program and `LiquidField.Shape` take it. +//! - A **place** is the outline that holds a control's shapes: a card's rect and corners. Glass is +//! cut by it (`LiquidField.edge`); here `cut` does the same to a shape's polygon. +//! - A **transition** is shapes interpolated (`lerp`): position, size and corners eased by one +//! curve, so a state becomes another by geometry alone and the material never changes. +//! +//! std-only, so it tests without a window (`build/app.zig`, `fizzy-orb-geometry-tests`). Physical +//! pixels throughout, as the glass works in. +const std = @import("std"); + +pub const Pt = [2]f32; + +/// A rounded box: `center`, `half` its half-size, `radii` each corner's, held to half the shorter +/// side when drawn (`radius`). +pub const Shape = struct { + center: Pt, + half: Pt, + radii: [4]f32 = @splat(0), + + pub fn circle(center: Pt, r: f32) Shape { + return .{ .center = center, .half = .{ r, r }, .radii = @splat(r) }; + } + + /// A box `rect` (x, y, w, h) with its corners' `radii` (top-left, top-right, bottom-right, + /// bottom-left); all of them at half the shorter side is a capsule. + pub fn box(x: f32, y: f32, w: f32, h: f32, radii: [4]f32) Shape { + return .{ .center = .{ x + w / 2, y + h / 2 }, .half = .{ w / 2, h / 2 }, .radii = radii }; + } + + /// Corner `i` (top-left, top-right, bottom-right, bottom-left) as drawn: at most half the + /// shorter side. + pub fn radius(self: Shape, i: usize) f32 { + return @max(0, @min(self.radii[i], @min(self.half[0], self.half[1]))); + } + + /// Signed distance from `p` to the outline: negative inside. + pub fn distance(self: Shape, p: Pt) f32 { + const d: Pt = .{ p[0] - self.center[0], p[1] - self.center[1] }; + const i: usize = if (d[0] > 0) (if (d[1] > 0) 2 else 1) else (if (d[1] > 0) 3 else 0); + const r = self.radius(i); + const q: Pt = .{ @abs(d[0]) - self.half[0] + r, @abs(d[1]) - self.half[1] + r }; + const outside = @sqrt(@max(q[0], 0) * @max(q[0], 0) + @max(q[1], 0) * @max(q[1], 0)); + return @min(@max(q[0], q[1]), 0) + outside - r; + } + + /// The outline as a convex polygon, clockwise on screen (y down), `per_corner` points round + /// each corner (a square corner is one point), into `out`; the points written. + pub fn outline(self: Shape, per_corner: usize, out: []Pt) []Pt { + const n = @max(per_corner, 2); + var len: usize = 0; + // Corner centres and the angle each corner's arc starts at, going clockwise from the top left. + const sx = [4]f32{ -1, 1, 1, -1 }; + const sy = [4]f32{ -1, -1, 1, 1 }; + const start = [4]f32{ std.math.pi, 1.5 * std.math.pi, 0, 0.5 * std.math.pi }; + for (0..4) |c| { + const r = self.radius(c); + const cx = self.center[0] + sx[c] * (self.half[0] - r); + const cy = self.center[1] + sy[c] * (self.half[1] - r); + if (r <= 0) { + if (len < out.len) out[len] = .{ cx, cy }; + len += 1; + continue; + } + for (0..n) |k| { + const a = start[c] + 0.5 * std.math.pi * @as(f32, @floatFromInt(k)) / @as(f32, @floatFromInt(n - 1)); + if (len < out.len) out[len] = .{ cx + r * @cos(a), cy + r * @sin(a) }; + len += 1; + } + } + return out[0..@min(len, out.len)]; + } +}; + +/// `a` to `b`, `t` of the way (0…1): centre, size and corners together. A state into another by +/// geometry alone: the corner's quarter sliding in and growing into its button, a button into a +/// panel. +pub fn lerp(a: Shape, b: Shape, t: f32) Shape { + const k = std.math.clamp(t, 0, 1); + var s: Shape = .{ + .center = .{ std.math.lerp(a.center[0], b.center[0], k), std.math.lerp(a.center[1], b.center[1], k) }, + .half = .{ std.math.lerp(a.half[0], b.half[0], k), std.math.lerp(a.half[1], b.half[1], k) }, + }; + for (&s.radii, 0..) |*r, i| r.* = std.math.lerp(a.radius(i), b.radius(i), k); + return s; +} + +/// `subject` (any polygon) cut by the convex polygon `clip`, both clockwise on screen: the part of +/// `subject` inside `clip` (Sutherland–Hodgman), into `out` using `scratch`; the points written. +/// Both buffers want room for `subject.len + clip.len` points. +pub fn cut(subject: []const Pt, clip: []const Pt, out: []Pt, scratch: []Pt) []Pt { + var src: []Pt = scratch[0..@min(subject.len, scratch.len)]; + @memcpy(src, subject[0..src.len]); + var dst = out; + var len: usize = src.len; + for (clip, 0..) |a, i| { + const b = clip[(i + 1) % clip.len]; + var n: usize = 0; + for (0..len) |j| { + const p = src[j]; + const q = src[(j + 1) % len]; + const pin = side(a, b, p) >= 0; + const qin = side(a, b, q) >= 0; + if (pin and n < dst.len) { + dst[n] = p; + n += 1; + } + if (pin != qin and n < dst.len) { + dst[n] = cross(a, b, p, q); + n += 1; + } + } + len = n; + if (len == 0) return out[0..0]; + // Swap: what was just written is the next pass's input. + const written = dst[0..len]; + dst = if (dst.ptr == out.ptr) scratch else out; + src = written; + } + // Within `out` whatever `scratch` held: a larger scratch could have held more than fits. + const n = @min(len, out.len); + if (src.ptr != out.ptr) @memcpy(out[0..n], src[0..n]); + return out[0..n]; +} + +/// Which side of the edge `a`→`b` `p` is on: positive inside a clockwise (y-down) polygon. +fn side(a: Pt, b: Pt, p: Pt) f32 { + return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); +} + +/// Where the segment `p`→`q` crosses the line through `a`, `b`. +fn cross(a: Pt, b: Pt, p: Pt, q: Pt) Pt { + const sp = side(a, b, p); + const sq = side(a, b, q); + const t = sp / (sp - sq); + return .{ p[0] + (q[0] - p[0]) * t, p[1] + (q[1] - p[1]) * t }; +} + +/// A polygon's area (positive for clockwise on screen). +pub fn area(poly: []const Pt) f32 { + var a: f32 = 0; + for (poly, 0..) |p, i| { + const q = poly[(i + 1) % poly.len]; + a += p[0] * q[1] - q[0] * p[1]; + } + return a / 2; +} + +test "a circle's outline is round, and its area a circle's" { + var buf: [256]Pt = undefined; + const c = Shape.circle(.{ 50, 50 }, 20); + const poly = c.outline(32, &buf); + for (poly) |p| try std.testing.expectApproxEqAbs(@as(f32, 0), c.distance(p), 0.01); + try std.testing.expectApproxEqRel(std.math.pi * 20 * 20, area(poly), 0.01); +} + +test "a square corner is one point, and a box's area is its rect's" { + var buf: [64]Pt = undefined; + const b = Shape.box(0, 0, 100, 60, @splat(0)); + try std.testing.expectEqual(@as(usize, 4), b.outline(16, &buf).len); + try std.testing.expectApproxEqAbs(@as(f32, 6000), area(b.outline(16, &buf)), 0.01); +} + +test "a circle on a square place's corner, cut by the place, is a quarter" { + var a: [256]Pt = undefined; + var b: [256]Pt = undefined; + var out: [512]Pt = undefined; + var scratch: [512]Pt = undefined; + const place = Shape.box(0, 0, 200, 100, @splat(0)); + const orb = Shape.circle(.{ 200, 0 }, 20); + const q = cut(orb.outline(48, &a), place.outline(8, &b), &out, &scratch); + try std.testing.expectApproxEqRel(@as(f32, std.math.pi * 20 * 20) / 4, area(q), 0.02); + for (q) |p| try std.testing.expect(place.distance(p) <= 0.01); +} + +test "cut by a rounded place, the quarter follows its curve: smaller, and none of it past the curve" { + var a: [256]Pt = undefined; + var b: [256]Pt = undefined; + var out: [512]Pt = undefined; + var scratch: [512]Pt = undefined; + const place = Shape.box(0, 0, 200, 100, .{ 0, 24, 0, 0 }); + const orb = Shape.circle(.{ 200, 0 }, 24 * (std.math.sqrt2 - 1) + 12); + const q = cut(orb.outline(48, &a), place.outline(24, &b), &out, &scratch); + try std.testing.expect(area(q) > 0); + try std.testing.expect(area(q) < std.math.pi * orb.half[0] * orb.half[0] / 4); + for (q) |p| try std.testing.expect(place.distance(p) <= 0.05); +} + +test "lerp carries centre, size and corners together, and stops at its ends" { + const a = Shape.circle(.{ 200, 0 }, 20); + const b = Shape.box(150, 20, 36, 36, @splat(18)); + const mid = lerp(a, b, 0.5); + try std.testing.expectApproxEqAbs(@as(f32, 184), mid.center[0], 0.01); + try std.testing.expectApproxEqAbs(@as(f32, 19), mid.half[0], 0.01); + try std.testing.expectEqual(a, lerp(a, b, -1)); + try std.testing.expectEqual(b.center, lerp(a, b, 2).center); +} From ae9b61b261fa1a0918e118cf6ebfdd2161947770 Mon Sep 17 00:00:00 2001 From: foxnne Date: Sun, 18 Oct 2026 17:16:58 -0500 Subject: [PATCH 5/7] app: the history grip is a vial in the card's corner, its liquid alive The history's grip moves from the middle of the card's bottom edge, where a split's bar runs, to a vial in its bottom-right corner: the corner orb, with a history icon. Its liquid (`app/Liquid.zig`) sloshes as the pane moves, splashes as steps come and eases up, asking for frames only while it moves. The bar grows out of it as one liquid: its bubbles pulled out small along arcs, joined and then pinching off, the liquid pouring from the vial into the line. A pane's activity feed stacks above the vial. --- app/HistoryTransport.zig | 89 +++++++---- app/Liquid.zig | 292 ++++++++++++++++++++++++++++++++++++ app/dome.zig | 310 ++++++++++++++++++++++++++++++++++++++- build/app.zig | 2 + src/editor/Editor.zig | 17 ++- 5 files changed, 677 insertions(+), 33 deletions(-) create mode 100644 app/Liquid.zig diff --git a/app/HistoryTransport.zig b/app/HistoryTransport.zig index a2a034fd6..6ad3a1521 100644 --- a/app/HistoryTransport.zig +++ b/app/HistoryTransport.zig @@ -51,6 +51,7 @@ const sdk = @import("fizzy_sdk"); const icons = @import("icons"); const rules = @import("history_transport.zig"); const dome = @import("dome.zig"); +const Liquid = @import("Liquid.zig"); const dome_rules = @import("dome_rules.zig"); shown: rules.Shown = .{}, @@ -72,6 +73,10 @@ presence: dome_rules.Presence = .{}, /// The liquid's swing, and the step the document stood at when it was last drawn: a scrub knocks it. slosh: dome_rules.Slosh = .{}, slosh_at: ?usize = null, +/// The liquid in a corner vial (`DrawOptions.corner`): sloshing as the vial moves, splashing and +/// its level easing up as steps come. It asks for frames only while it moves. +liquid: Liquid = .{}, + /// The bar has its collapse tab (it grew from a grip): its place reaches up over it. tab: bool = false, /// The pointer was on the bar last frame: leaving it, the bar goes soon. @@ -169,6 +174,9 @@ pub const DrawOptions = struct { /// A tape playing into the document (fizzy: in a tape's tab), how far it has played, 0 to 1: /// the grip fills as it plays rather than by `steps`. Null: none. progress: ?f32 = null, + /// The card the document is on, its bottom-right corner (`dome.Corner`): the grip is a vial + /// there rather than a dome on the middle of `edge`, where a split's bar runs. + corner: ?dome.Corner = null, }; const HistoryFrames = @import("HistoryFrames.zig"); @@ -191,8 +199,10 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica self.shown.fade = if (opts.history) dome.growNs() else rules.fade_ns; var reach = area; if (opts.edge) |y| reach.h = @max(reach.h, y - reach.y); - const on_grip = opts.history and reach.contains(mouse) and !core.dialogs.modalOverCurrent() and - if (gripRect(area, dome.radius, opts.edge)) |g| dvui.windowRectScale().rectToPhysical(g.outsetAll(dome.reach)).contains(mouse) else false; + const on_grip = opts.history and !core.dialogs.modalOverCurrent() and if (opts.corner) |c| + c.box(dvui.windowRectScale().s).contains(mouse) + else + reach.contains(mouse) and if (gripRect(area, dome.radius, opts.edge)) |g| dvui.windowRectScale().rectToPhysical(g.outsetAll(dome.reach)).contains(mouse) else false; self.tab = opts.history; const on_bar = if (self.place(area, now)) |b| b.contains(mouse) else false; if (!on_bar) self.held_off = false; @@ -223,22 +233,34 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica if (self.grip_steps) |was| if (opts.steps > was) { self.stepped_at = now; self.slosh.knock(now, 0.35); + // Into a vial, the new step splashes the surface, as a drop landing would. + self.liquid.splashIn(); }; self.grip_steps = opts.steps; + self.liquid.setLevel(fill); + if (opts.corner) |c| { + const s = dvui.windowRectScale().s; + self.liquid.step(now, c.point(), c.radiusPx(s), c.liquidDepth(s, self.liquid.shownLevel())); + } const glow: f32 = if (self.stepped_at) |t| (if (now - t < step_glow_ns) 1 - @as(f32, @floatCast(@as(f64, @floatFromInt(now - t)) / @as(f64, @floatFromInt(step_glow_ns)))) else 0) else 0; if (glow == 0) self.stepped_at = null; const in_use = on_grip or self.stepped_at != null or !self.slosh.settled(now); const alpha = self.presence.ease(in_use, now); // While the bar grows out of it, the grip is the bar's glass: not drawn here. - if (p <= 0) if (gripRect(area, dome.radius, opts.edge)) |g| if (dome.draw(g, id_extra, .{ + const grip_look: dome.Look = .{ .alpha = alpha, .lit = on_grip, .held = self.dwell.held(now), .fill = fill, .glow = glow, .tilt = self.slosh.tilt(now), - }, "history.grip:{s}", .{doc.owner.documentPath(doc)})) self.stir(now); - if (self.dwell.since != null or self.stepped_at != null or !self.slosh.settled(now) or self.presence.moving(in_use)) dvui.refresh(null, @src(), null); + .liquid = if (opts.corner != null) &self.liquid else null, + }; + if (p <= 0) if (opts.corner) |c| { + if (dome.drawCorner(c, id_extra, grip_look, "history.grip:{s}", .{doc.owner.documentPath(doc)})) self.stir(now); + } else if (gripRect(area, dome.radius, opts.edge)) |g| if (dome.draw(g, id_extra, grip_look, "history.grip:{s}", .{doc.owner.documentPath(doc)})) self.stir(now); + if (self.dwell.since != null or self.stepped_at != null or !self.slosh.settled(now) or self.presence.moving(in_use) or + (opts.corner != null and !self.liquid.settled())) dvui.refresh(null, @src(), null); } if (p <= 0) return pressed; const arena = dvui.currentWindow().arena(); @@ -381,10 +403,12 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica // the grip in the same glass and shrinking as they leave it, the glass between them reaching // further while they are near: they run together and part as drops do. What is on them shows // once they are most of the way out. Without a grip, it fades. - const from: ?dvui.Rect.Physical = if (opts.history and p < 1) if (gripRect(area, dome.radius, opts.edge)) |g| wrs.rectToPhysical(g) else null else null; - const out = core.motion.enterFull(p); - const bud: ?dvui.Rect.Physical = if (from) |f| dome.bud(f) else null; - const prev_alpha = dvui.alpha(if (from != null) dome.glassAlpha(self.presence.value, p) else p); + const from: ?dvui.Rect.Physical = if (!opts.history or p >= 1) null else if (opts.corner) |c| c.box(s) else if (gripRect(area, dome.radius, opts.edge)) |g| wrs.rectToPhysical(g) else null; + const out = dome.smooth(p); + // Out of a corner vial, which is whole at rest, the glass is whole throughout: eased toward a + // dome's quiet presence, it was glass at partial alpha — a hole — and the vial at full alpha + // the frame it went back in, a flash. + const prev_alpha = dvui.alpha(if (opts.corner != null and from != null) 1 else if (from != null) dome.glassAlpha(self.presence.value, p) else p); defer dvui.alphaSet(prev_alpha); const pt = dvui.currentWindow().mouse_pt; var bubbles: [5]core.dialogs.Bubble = undefined; @@ -393,9 +417,11 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica // strip grows, so the pictures sit inside them. const line_radius = std.math.lerp(phys[1].h / 2, core.corners.scaled(core.corners.card) * s, @min(1, grown * 1.5)); for (phys, bubbles[0..4], 0..) |r, *b, i| b.* = .{ - .rect = if (bud) |f| dome.lerpRect(f, r, dome.emerge(p, bud_delays[i])) else r, + .rect = if (from) |f| dome.pull(f, r, p, bud_delays[i], bud_arcs[i]) else r, .radius = if (i == 1 and grown > 0) line_radius else null, - .light = if (r.contains(pt) and switch (i) { + // Lit under the pointer only once the bar is out: moving, a lit bubble was a lighter drop + // among the rest, its light running across the necks as they joined. + .light = if (p >= 1 and r.contains(pt) and switch (i) { 0 => trimming or at > 0, 2 => !trimming and at < n, 3 => true, @@ -404,10 +430,12 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica }; // The tab, a small drop on the bar's top that its glass runs into. var count: usize = if (export_g > 0) 4 else 3; + var tab_index: ?usize = null; if (self.tab) { + tab_index = count; bubbles[count] = .{ - .rect = if (bud) |f| dome.lerpRect(f, tab_p, dome.emerge(p, bud_delays[4])) else tab_p, - .light = if (tab_p.contains(pt)) core.dialogs.bubble_hover else 0, + .rect = if (from) |f| dome.pull(f, tab_p, p, bud_delays[4], bud_arcs[4]) else tab_p, + .light = if (p >= 1 and tab_p.contains(pt)) core.dialogs.bubble_hover else 0, }; count += 1; } @@ -415,17 +443,19 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica var shapes: [6]core.dialogs.Bubble = undefined; @memcpy(shapes[0..count], bubbles[0..count]); if (from) |f| { - shapes[count] = dome.shrinking(f, p); + // A corner vial shrinks into its corner; a dome into its edge. + shapes[count] = if (opts.corner) |c| .{ .rect = core.widgets.orb.rectOf(c.shape(s, 1 - dome.smooth(p))) } else dome.shrinking(f, p); count += 1; } { - // Not past the pane's edge, which the grip stands on. - const prev_clip = if (from) |f| dvui.clip(dome.aboveEdge(f)) else dvui.clipGet(); + // Not past the pane's edge, which the grip stands on: the card, for a corner vial. + const prev_clip = if (opts.corner) |c| dvui.clip(c.card) else if (from) |f| dvui.clip(dome.aboveEdge(f)) else dvui.clipGet(); defer dvui.clipSet(prev_clip); // No shadow ring: each bubble's own ring lay dark across the necks where they run together // (the tab into the bar). The one soft shadow of the merged shape is the orb's // (`plans/LIQUID_EDGE_PLAN.md`); the rim and lens are what set the glass off meanwhile. - core.dialogs.glassBubbles(fid, shapes[0..count], s, .{ .merge = dome.merge(p, 14), .shadow = 0 }); + // Out of a corner vial, cut by the card's outline, curve and all, as the vial is at rest. + core.dialogs.glassBubbles(fid, shapes[0..count], s, .{ .merge = dome.merge(p, 14), .shadow = 0, .edge = if (opts.corner) |c| c.place() else null }); } // What is on the bubbles comes once they are most of the way out; the liquid comes with them. const shape_alpha = dvui.alpha(1); @@ -435,15 +465,20 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica } const ink = dvui.themeGet().color(.window, .text); + // Each icon on its bubble where the bubble is now, so it rides out of the grip with it and + // grows with it, rather than waiting at the place the bubble is going to. + const back_now = wrs.rectFromPhysical(bubbles[0].rect); + const fwd_now = wrs.rectFromPhysical(bubbles[2].rect); + const share_now = wrs.rectFromPhysical(bubbles[3].rect); if (trimming) { - button(bounds, back, "history_cancel", icons.tvg.lucide.x, ink.opacity(0.85)); - } else button(bounds, back, "history_back", icons.tvg.lucide.undo, ink.opacity(if (at > 0) 0.85 else 0.3)); - button(bounds, fwd, "history_forward", icons.tvg.lucide.redo, ink.opacity(if (!trimming and at < n) 0.85 else 0.3)); - if (self.tab) dome.chevron(bounds, tab, ink.opacity(0.85)); + button(bounds, back_now, "history_cancel", icons.tvg.lucide.x, ink.opacity(0.85)); + } else button(bounds, back_now, "history_back", icons.tvg.lucide.undo, ink.opacity(if (at > 0) 0.85 else 0.3)); + button(bounds, fwd_now, "history_forward", icons.tvg.lucide.redo, ink.opacity(if (!trimming and at < n) 0.85 else 0.3)); + if (tab_index) |ti| dome.chevron(bounds, wrs.rectFromPhysical(bubbles[ti].rect), ink.opacity(0.85)); if (export_g > 0.3) { if (trimming) { - button(bounds, share, "history_export_done", icons.tvg.lucide.check, yellow); - } else button(bounds, share, "history_export", icons.tvg.lucide.@"share-2", ink.opacity(0.85 * export_g)); + button(bounds, share_now, "history_export_done", icons.tvg.lucide.check, yellow); + } else button(bounds, share_now, "history_export", icons.tvg.lucide.@"share-2", ink.opacity(0.85 * export_g)); } // What the document is at, over the line: the step's title, and how far along. @@ -474,9 +509,11 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica return pressed; } -/// When each bubble buds out of the grip, as a share of the growing: the line first, undo and -/// redo after it, then export, then the tab. -const bud_delays = [5]f32{ 0.12, 0, 0.12, 0.22, 0.3 }; +/// When each bubble buds out of the grip, as a share of the growing — the line first, then the +/// nearest the grip outward: export, redo, undo, the tab — each on its own moment so none travels +/// stacked on another; and how high each arcs, so their paths fan out. +const bud_delays = [5]f32{ 0.27, 0, 0.18, 0.09, 0.34 }; +const bud_arcs = [5]f32{ 1.25, 0.6, 1.0, 0.8, 1.6 }; /// macOS's trim yellow. const yellow: dvui.Color = .{ .r = 0xF5, .g = 0xC0, .b = 0x2E }; diff --git a/app/Liquid.zig b/app/Liquid.zig new file mode 100644 index 000000000..ab9f59143 --- /dev/null +++ b/app/Liquid.zig @@ -0,0 +1,292 @@ +//! The liquid in a history vial (`dome.paintCorner`): a surface that sloshes as the vial moves and +//! takes in drops that fall into it, as a liquid does. +//! +//! The surface is a row of columns tied to their neighbours by springs (the usual small model of +//! water waves): pulled toward their neighbours' heights, toward rest, and losing energy as they +//! go, with what the row holds kept the same, so a wave moves liquid about rather than making it. +//! The vial moving tips it: its acceleration across the screen, from where the caller says it is +//! each frame, pushes one side down and the other up, as a cup's liquid does when the cup is +//! moved. A new step drops a drop in from the top (`drip`): it falls, lands with a splash at the +//! column it hits, and only then is the level up by it. +//! +//! **It lets the app sleep.** `settled` says when nothing moves — the surface flat, no drop in the +//! air, the level where it is going — and the caller asks for frames only while it is not. A long +//! gap between frames (the app slept, and the vial moved in the frame that woke it) is a new start, +//! not an acceleration. +//! +//! Units are physical pixels and seconds; std-only, so it tests without a window +//! (`build/app.zig`). +const std = @import("std"); + +const Liquid = @This(); + +pub const columns = 16; +/// The most drops in the air at once; more wait their turn. +pub const max_drops = 4; + +pub const Drop = struct { + /// Across the vessel, 0 its left to 1 its right. + x: f32, + /// Down from the vessel's top, pixels. + y: f32, + vy: f32, +}; + +/// Each column's height over the level, pixels (up positive), and its speed. +h: [columns]f32 = @splat(0), +v: [columns]f32 = @splat(0), +drops: [max_drops]Drop = undefined, +drop_n: usize = 0, +/// Drops asked for and not yet falling (`drip` with the air full). +waiting: usize = 0, +/// The level, 0 to 1, as it is and where it is going (`setLevel`); a drop in the air or waiting +/// holds back its share until it lands. +level: f32 = 0, +target: f32 = 0, +/// Each drop's share of the level, held back until it lands. +per_drop: f32 = 0, +last_ns: i128 = 0, +last_pos: [2]f32 = .{ 0, 0 }, +last_vel: [2]f32 = .{ 0, 0 }, +/// Which drop comes next, for where it falls. +count: u32 = 0, + +/// How strongly a column is pulled toward its neighbours (wave speed), toward rest, and how fast +/// it loses energy, per second. +const tension: f32 = 900; +const stiffness: f32 = 30; +const damping: f32 = 2.6; +/// Pixels of tip per pixel-per-second² of acceleration across, at the vessel's edges. +const tip_gain: f32 = 0.00045; +/// Gravity on a drop, pixels per second². +const gravity: f32 = 2600; +/// How hard a landing drop pushes the column it hits down, pixels per second. +const splash: f32 = 90; +/// How fast the level comes to where it is going, per second. +const level_rate: f32 = 6; +/// A frame gap past this is the app waking, not the vial moving. +const max_gap_ns: i128 = 100 * std.time.ns_per_ms; +/// Steps the simulation takes per second at most. +const step_s: f32 = 1.0 / 240.0; + +/// Where the level is going, 0 to 1: the history's fill. A rise asked for by `drip` waits for its +/// drop to land. +pub fn setLevel(self: *Liquid, fill: f32) void { + self.target = std.math.clamp(fill, 0, 1); + if (self.last_ns == 0) self.level = self.target; +} + +/// A new step: a drop falls in from the top, and the level is up by `share` (0 to 1) once it lands. +pub fn drip(self: *Liquid, share: f32) void { + self.per_drop = share; + if (self.drop_n < max_drops) self.launch() else self.waiting += 1; +} + +fn launch(self: *Liquid) void { + // Somewhere across the middle of the vessel, a little differently each time. + const spread = [_]f32{ 0.5, 0.38, 0.62, 0.45, 0.55, 0.32 }; + self.drops[self.drop_n] = .{ .x = spread[self.count % spread.len], .y = 0, .vy = 0 }; + self.drop_n += 1; + self.count +%= 1; +} + +/// A new step without a drop: the surface splashes where one would have landed, somewhere across +/// the middle, a little differently each time, and the level comes up by itself (`setLevel`). +pub fn splashIn(self: *Liquid) void { + const spread = [_]f32{ 0.5, 0.38, 0.62, 0.45, 0.55, 0.32 }; + const c: usize = @intFromFloat(@round(spread[self.count % spread.len] * (columns - 1))); + self.count +%= 1; + self.v[c] -= splash; + if (c > 0) self.v[c - 1] -= splash * 0.4; + if (c < columns - 1) self.v[c + 1] -= splash * 0.4; +} + +/// The level the surface is drawn at: where it is, short of what the drops still in the air (or +/// waiting) will add. +pub fn shownLevel(self: *const Liquid) f32 { + const pending = @as(f32, @floatFromInt(self.drop_n + self.waiting)) * self.per_drop; + return @max(0, @min(self.level, self.target - pending)); +} + +/// Advance to `now_ns`, the vial at `pos` (pixels, any fixed point of it on screen), its vessel +/// `width` across and `depth` from its top to the liquid's level: tip the surface by how the +/// vial accelerated, run the waves, and let the drops fall. +pub fn step(self: *Liquid, now_ns: i128, pos: [2]f32, width: f32, depth: f32) void { + defer { + self.last_ns = now_ns; + self.last_pos = pos; + } + if (self.last_ns == 0 or now_ns - self.last_ns > max_gap_ns or now_ns <= self.last_ns) { + self.last_vel = .{ 0, 0 }; + return; + } + const dt: f32 = @floatCast(@as(f64, @floatFromInt(now_ns - self.last_ns)) / std.time.ns_per_s); + const vel: [2]f32 = .{ (pos[0] - self.last_pos[0]) / dt, (pos[1] - self.last_pos[1]) / dt }; + const acc_x = (vel[0] - self.last_vel[0]) / dt; + self.last_vel = vel; + // The vial pushed one way, the liquid lags: higher on the side it was pushed from. + const tip = std.math.clamp(acc_x * tip_gain, -400, 400); + for (&self.v, 0..) |*v, i| v.* += tip * across(i); + + var left = dt; + while (left > 0) { + const h = @min(left, step_s); + left -= h; + self.wave(h); + self.fall(h, width, depth); + } + // The level eases to where it is going. + self.level += (self.shownTarget() - self.level) * @min(1, level_rate * dt); +} + +/// What the level eases to: the target, short of the drops still to land. +fn shownTarget(self: *const Liquid) f32 { + const pending = @as(f32, @floatFromInt(self.drop_n + self.waiting)) * self.per_drop; + return @max(0, self.target - pending); +} + +/// Column `i` across the vessel, -1 at its left to 1 at its right. +fn across(i: usize) f32 { + return @as(f32, @floatFromInt(i)) / @as(f32, columns - 1) * 2 - 1; +} + +fn wave(self: *Liquid, dt: f32) void { + var force: [columns]f32 = undefined; + for (0..columns) |i| { + const l = self.h[if (i == 0) 0 else i - 1]; + const r = self.h[if (i == columns - 1) columns - 1 else i + 1]; + force[i] = tension * (l + r - 2 * self.h[i]) - stiffness * self.h[i]; + } + const keep = @exp(-damping * dt); + var mean: f32 = 0; + for (0..columns) |i| { + self.v[i] = (self.v[i] + force[i] * dt) * keep; + self.h[i] += self.v[i] * dt; + mean += self.h[i]; + } + // What the row holds stays the same: a wave moves liquid, it makes none. + mean /= columns; + for (&self.h) |*h| h.* -= mean; +} + +fn fall(self: *Liquid, dt: f32, width: f32, depth: f32) void { + _ = width; + var i: usize = 0; + while (i < self.drop_n) { + const d = &self.drops[i]; + d.vy += gravity * dt; + d.y += d.vy * dt; + const c: usize = @intFromFloat(std.math.clamp(@round(d.x * (columns - 1)), 0, columns - 1)); + // Near the surface, the surface reaches up for it: the two join, not a ball hitting a floor. + const gap = depth - self.h[c] - d.y; + if (gap < 6) self.v[c] += (6 - gap) * 40 * dt; + if (gap <= 0) { + // Landed: a splash where it hit, and its share in the level. + self.v[c] -= splash; + if (c > 0) self.v[c - 1] -= splash * 0.4; + if (c < columns - 1) self.v[c + 1] -= splash * 0.4; + self.drops[i] = self.drops[self.drop_n - 1]; + self.drop_n -= 1; + if (self.waiting > 0) { + self.waiting -= 1; + self.launch(); + } + continue; + } + i += 1; + } +} + +/// Nothing moves: the surface flat and still, no drop in the air or waiting, the level where it is +/// going. The caller asks for no frames while this holds. +pub fn settled(self: *const Liquid) bool { + if (self.drop_n > 0 or self.waiting > 0) return false; + if (@abs(self.level - self.target) > 0.002) return false; + for (self.h, self.v) |h, v| if (@abs(h) > 0.05 or @abs(v) > 0.5) return false; + return true; +} + +/// A knock, -1 to 1: the surface tipped as by a sideways push (an undo, a redo). +pub fn knock(self: *Liquid, amount: f32) void { + for (&self.v, 0..) |*v, i| v.* += amount * 60 * across(i); +} + +test "a still vial settles and stays settled" { + var l: Liquid = .{}; + l.setLevel(0.5); + var t: i128 = 1; + for (0..200) |_| { + l.step(t, .{ 100, 100 }, 40, 30); + t += 16 * std.time.ns_per_ms; + } + try std.testing.expect(l.settled()); +} + +test "moving the vial tips the surface, and it settles again" { + var l: Liquid = .{}; + l.setLevel(0.5); + var t: i128 = 1; + var x: f32 = 100; + for (0..10) |_| { + l.step(t, .{ x, 100 }, 40, 30); + t += 16 * std.time.ns_per_ms; + x += 20; + } + try std.testing.expect(!l.settled()); + for (0..600) |_| { + l.step(t, .{ x, 100 }, 40, 30); + t += 16 * std.time.ns_per_ms; + } + try std.testing.expect(l.settled()); +} + +test "a drop holds back its share until it lands, and the volume stays the same" { + var l: Liquid = .{}; + l.setLevel(0.2); + var t: i128 = 1; + l.step(t, .{ 0, 0 }, 40, 30); + l.setLevel(0.3); + l.drip(0.1); + try std.testing.expect(l.shownLevel() <= 0.2 + 1e-4); + var landed = false; + for (0..400) |_| { + t += 16 * std.time.ns_per_ms; + l.step(t, .{ 0, 0 }, 40, 30); + if (l.drop_n == 0) landed = true; + var sum: f32 = 0; + for (l.h) |h| sum += h; + try std.testing.expectApproxEqAbs(@as(f32, 0), sum, 0.01); + } + try std.testing.expect(landed); + try std.testing.expect(l.settled()); + try std.testing.expectApproxEqAbs(@as(f32, 0.3), l.level, 0.01); +} + +test "a splash ripples the surface, keeps its volume, and settles" { + var l: Liquid = .{}; + l.setLevel(0.4); + var t: i128 = 1; + l.step(t, .{ 0, 0 }, 40, 30); + l.splashIn(); + t += 16 * std.time.ns_per_ms; + l.step(t, .{ 0, 0 }, 40, 30); + try std.testing.expect(!l.settled()); + for (0..600) |_| { + t += 16 * std.time.ns_per_ms; + l.step(t, .{ 0, 0 }, 40, 30); + var sum: f32 = 0; + for (l.h) |h| sum += h; + try std.testing.expectApproxEqAbs(@as(f32, 0), sum, 0.01); + } + try std.testing.expect(l.settled()); +} + +test "a gap between frames is a new start, not a jolt" { + var l: Liquid = .{}; + l.setLevel(0.5); + var t: i128 = 1; + l.step(t, .{ 0, 0 }, 40, 30); + t += 5 * std.time.ns_per_s; + l.step(t, .{ 500, 0 }, 40, 30); + try std.testing.expect(l.settled()); +} diff --git a/app/dome.zig b/app/dome.zig index 0255416d5..8723d7aab 100644 --- a/app/dome.zig +++ b/app/dome.zig @@ -27,6 +27,7 @@ const dvui = @import("dvui"); const core = @import("core"); const icons = @import("icons"); const rules = @import("dome_rules.zig"); +const Liquid = @import("Liquid.zig"); /// The dome's radius: a semicircle twice as wide as it is tall. pub const radius: f32 = 13; @@ -43,7 +44,17 @@ pub fn rect(cx: f32, edge_y: f32, r: f32) dvui.Rect { /// How long a bar takes to grow out of its dome, and to go back in: a surface's opening time. pub fn growNs() i128 { - return @as(i128, core.motion.duration(core.motion.open_us)) * std.time.ns_per_us; + return @as(i128, core.motion.duration(grow_us)) * std.time.ns_per_us; +} +/// Microseconds, as written, a bar takes to grow out of its grip: slower than a surface opening, +/// so it reads as one liquid swelling and parting into drops, not controls flung out. +const grow_us: i32 = 620_000; + +/// The one curve the growing runs on, both ways: eased in and out, with no swing past and no +/// drawing back, so nothing about it jumps. +pub fn smooth(t: f32) f32 { + const k = std.math.clamp(t, 0, 1); + return if (k < 0.5) 4 * k * k * k else 1 - std.math.pow(f32, -2 * k + 2, 3) / 2; } /// Where the bar's bubbles bud from in the dome at `from` (physical, the semicircle's box): a @@ -58,6 +69,41 @@ pub fn emerge(p: f32, delay: f32) f32 { return core.motion.enterFull(std.math.clamp((p - delay) / (1 - delay), 0, 1)); } +/// Where a bubble of the bar is, `p` of the way out (0 in its grip `from`, 1 at its place `to`), +/// budding at `delay` (a share of the growing): a drop pulled out of the grip rather than a button +/// sliding. Small while it travels, inflating as it nears its place; along an arc, up and then +/// across, rather than straight, `arc` times the usual height (so bubbles leaving together fan out +/// rather than travel stacked); on one smooth curve (`smooth`) both ways, so going back is coming +/// played backwards — in the reverse order, as the later a bubble buds, the sooner it is in. +pub fn pull(from: dvui.Rect.Physical, to: dvui.Rect.Physical, p: f32, delay: f32, arc: f32) dvui.Rect.Physical { + const k = std.math.clamp((p - delay) / (1 - delay), 0, 1); + const t = smooth(k); + if (t <= 0) return bud(from); + const a = bud(from); + const ac: [2]f32 = .{ a.x + a.w / 2, a.y + a.h / 2 }; + const bc: [2]f32 = .{ to.x + to.w / 2, to.y + to.h / 2 }; + // Up off the straight line, most of the way out at the middle of the way. + const dist = @sqrt((bc[0] - ac[0]) * (bc[0] - ac[0]) + (bc[1] - ac[1]) * (bc[1] - ac[1])); + const lift = dist * pull_arc * arc * @sin(std.math.pi * std.math.clamp(t, 0, 1)); + const c: [2]f32 = .{ std.math.lerp(ac[0], bc[0], t), std.math.lerp(ac[1], bc[1], t) - lift }; + // A drop while it travels, as big as the grip's bud at most; its place's size once near it. + const drop = @min(a.h, @min(to.w, to.h) * pull_drop); + const grow = std.math.clamp((t - pull_inflate) / (1 - pull_inflate), 0, 1); + const g = grow * grow * (3 - 2 * grow); + const w = std.math.lerp(drop, to.w, g); + const h = std.math.lerp(drop, to.h, g); + // Past its place in a swing, past its size a little too. + const swell = if (t > 1) t else 1; + return .{ .x = c[0] - w * swell / 2, .y = c[1] - h * swell / 2, .w = w * swell, .h = h * swell }; +} +/// How far up off the straight way a pulled bubble arcs, as a share of the way; how big it is +/// while it travels, as a share of its place's size; and how far along it starts to inflate. +const pull_arc: f32 = 0.14; +// Not much smaller than its place: the glass's rim is one for the whole field, fitted to its +// smallest shape, and a tiny drop is all rim — a darker drop among the rest. +const pull_drop: f32 = 0.7; +const pull_inflate: f32 = 0.55; + /// The dome at `from` itself in the bar's glass while the bar is `p` of the way out, shrinking into /// its edge as the bubbles leave it. Clip the glass to the edge (`aboveEdge`). pub fn shrinking(from: dvui.Rect.Physical, p: f32) core.dialogs.Bubble { @@ -65,10 +111,11 @@ pub fn shrinking(from: dvui.Rect.Physical, p: f32) core.dialogs.Bubble { return .{ .rect = .{ .x = from.x + from.w / 2 - d / 2, .y = from.y + from.h - d / 2, .w = d, .h = d } }; } -/// How near the bar's bubbles reach for each other `p` of the way out: further while they are -/// near the dome, so they run together, and `settled` once out. +/// How near the bar's bubbles reach for each other `p` of the way out: far through the first +/// half, so they and the grip swell as one blob, then falling to `settled` over the second, so the +/// necks between them thin and let go as each nears its place, and they part as drops do. pub fn merge(p: f32, settled: f32) f32 { - return std.math.lerp(40, settled, core.motion.enterFull(p)); + return std.math.lerp(28, settled, smooth((p - 0.3) / 0.6)); } /// The clip that keeps the glass growing out of the dome at `from` from reaching past the edge it @@ -127,6 +174,9 @@ pub const Look = struct { glow: f32 = 0, /// How tipped its liquid is, -1 to 1 (`rules.Slosh`). tilt: f32 = 0, + /// A corner vial's liquid, its surface and drops (`paintCorner`): drawn from it rather than + /// from `fill` and `tilt`. + liquid: ?*const Liquid = null, }; /// The pool in the dome at `from` (physical, the semicircle's box) the liquid flows out of into @@ -218,10 +268,260 @@ pub fn pourTube(done: dvui.Rect.Physical, tilt: f32, from_pool: ?dvui.Rect.Physi const prev = dvui.alpha(1); dvui.alphaSet(alpha); defer dvui.alphaSet(prev); - const tube = if (from_pool) |f| lerpRect(f, done, out) else done; + const pool_r = from_pool orelse return fillTube(done, tilt, color); + // Poured: a stream from the pool to the knob, the end of the line's liquid nearest the pool, + // then the liquid spreading from there back to the line's start. + const k = std.math.clamp(out, 0, 1); + // The stream's head runs from the pool to the line over the first part; its tail leaves the + // pool once the line is filling, and runs into the line after the head. + const head = smooth(k / pour_stream); + const tail = smooth((k - pour_stream) / (pour_fill - pour_stream)); + const th = done.h; + const lip: [2]f32 = .{ pool_r.x + pool_r.w * 0.3, pool_r.y + @min(pool_r.h, th) / 2 }; + const end: [2]f32 = .{ done.x + done.w - th / 2, done.y + th / 2 }; + if (tail < 1 and head > 0) pourStream(lip, end, tail, head, th, color); + // The line fills from the knob back to its start once the stream reaches it. + const fill = smooth((k - pour_stream * 0.8) / (1 - pour_stream * 0.8)); + const w = @max(th, std.math.lerp(th, done.w, fill)); + if (fill > 0) fillTube(.{ .x = done.x + done.w - w, .y = done.y, .w = w, .h = th }, tilt * fill, color); +} + +/// How far into the pour (0 to 1) the stream reaches the line, and lets go of the pool. +const pour_stream: f32 = 0.4; +const pour_fill: f32 = 0.75; + +/// The line's liquid in `tube` (physical): a capsule, its head tipped by `tilt` (-1 to 1). +fn fillTube(tube: dvui.Rect.Physical, tilt: f32, color: dvui.Color) void { var outline: [4 * (rules.corner_points + 1)]rules.Pt = undefined; var kept: [outline.len + 1]rules.Pt = undefined; const vessel = rules.roundedOutline(tube.x, tube.y, tube.w, tube.h, @min(tube.w, tube.h) / 2, &outline); const a = tilt * 0.6; pour(rules.cut(vessel, .{ tube.x + tube.w, tube.y + tube.h / 2 }, .{ @cos(a), @sin(a) }, &kept), color); } + +// ── The grip as a corner vial ──────────────────────────────────────────────────────────────── +// +// The history's grip in the bottom-right corner of the card its document is on, the way a place's +// arrange button is in its top-right (`layout/Region.zig`, `cornerPiece`): an orb on the card's +// corner point, cut by the card's outline with an edge of its own (`LiquidField.Edge.cut`), one +// radius on every card and reaching out by part of what a rounder corner's curve cuts off. The +// corner is the one place on a card no split's bar is (they run along the middle of its edges), +// and the quarter is room for the liquid to show. A history icon sits in it, over the liquid. + +/// The orb's radius in points, and how much of what the card's curve cuts off it reaches out by: +/// the arrange corner's (`Region.corner_grown_radius`, `corner_curve_reach`). +pub const corner_radius: f32 = 30; +pub const corner_curve_reach: f32 = 0.5; +/// The history icon's size in points, and how far in from the quarter's middle it sits. +const corner_icon: f32 = 13; + +/// A card's bottom-right corner, physical: its rect and its corners (top-left, top-right, +/// bottom-right, bottom-left). +pub const Corner = struct { + card: dvui.Rect.Physical, + radii: [4]f32 = @splat(0), + + fn curveIn(self: Corner) f32 { + return self.radii[2] * (std.math.sqrt2 - 1); + } + + /// The card's corner point. + pub fn point(self: Corner) [2]f32 { + return .{ self.card.x + self.card.w, self.card.y + self.card.h }; + } + + /// The orb's radius, physical. + pub fn radiusPx(self: Corner, s: f32) f32 { + return corner_radius * s + self.curveIn() * corner_curve_reach; + } + + /// The square from the corner the quarter lies in: where the pointer is on it, and what the + /// bar grows out of. + pub fn box(self: Corner, s: f32) dvui.Rect.Physical { + const r = self.radiusPx(s); + const p = self.point(); + return .{ .x = p[0] - r, .y = p[1] - r, .w = r, .h = r }; + } + + /// The orb, `t` of its size (1 whole, 0 nothing), on the corner point. + pub fn shape(self: Corner, s: f32, t: f32) core.widgets.orb.Shape { + return .circle(self.point(), self.radiusPx(s) * @max(0, t)); + } + + /// The card's outline, which cuts it. + pub fn place(self: Corner) core.widgets.LiquidField.Edge { + return .{ .rect = self.card, .radii = self.radii, .cut = true }; + } + + /// How far down from the vessel's top the liquid's level is at `level` (0 to 1), physical: + /// where a drop falling from the top meets it (`Liquid.step`). + pub fn liquidDepth(self: Corner, s: f32, level: f32) f32 { + const deep = self.radiusPx(s) - 2 * liquid_inset * s; + return deep * (1 - rules.fill_top * level); + } + + /// Where the icon is centred: on the diagonal, in the middle of what of the quarter shows. + pub fn iconCenter(self: Corner, s: f32) [2]f32 { + const axis = self.radii[2] * (1 - std.math.sqrt1_2); + const in = axis + @max(0, self.radiusPx(s) - self.curveIn()) / 2 * std.math.sqrt1_2; + const p = self.point(); + return .{ p[0] - in, p[1] - in }; + } +}; + +/// The vial at `c`, as `look` says, in a floating widget of its own that takes its presses, named +/// for a tape or a test to aim at. Returns whether it was pressed. +pub fn drawCorner(c: Corner, id_extra: usize, look: Look, comptime anchor_fmt: []const u8, anchor_args: anytype) bool { + const wrs = dvui.windowRectScale(); + const s = wrs.s; + const pr = c.box(s); + var float: dvui.FloatingWidget = undefined; + float.init(@src(), .{}, .{ .rect = wrs.rectFromPhysical(pr), .id_extra = id_extra }); + defer float.deinit(); + core.anchor.markRect(float.data().id, pr, true, anchor_fmt, anchor_args); + var pressed = false; + for (dvui.events()) |*e| { + if (!dvui.eventMatch(e, .{ .id = float.data().id, .r = float.data().borderRectScale().r })) continue; + if (e.evt == .mouse and e.evt.mouse.action == .press and e.evt.mouse.button.pointer()) { + e.handle(@src(), float.data()); + pressed = true; + } + } + paintCorner(float.data().id, c, 1, look); + // The history icon over the liquid: grey at rest, the text colour while it is in use. + const theme = dvui.themeGet(); + const grey = theme.color(.window, .fill).lerp(if (theme.dark) dvui.Color.white else dvui.Color.black, 0.3).opacity(1); + const lit: f32 = if (look.lit) 1 else std.math.clamp(@max(look.glow, look.held), 0, 1); + const ic = c.iconCenter(s); + const d = corner_icon * s; + const bounds = float.data().borderRectScale().r; + core.icon.icon(@src(), "history_grip", icons.tvg.lucide.history, .{ + .stroke_color = .white, + .fill_color = .white, + }, .{ + .rect = .{ .x = (ic[0] - d / 2 - bounds.x) / s, .y = (ic[1] - d / 2 - bounds.y) / s, .w = corner_icon, .h = corner_icon }, + .color_text = .{ .color = grey.lerp(theme.color(.content, .text), lit) }, + }); + return pressed; +} + +/// The vial's pixels at `c`, `t` of its size (1 whole; less while the bar grows out of it): its +/// glass, an orb cut by the card, and the liquid in it up to its level, tipped by its slosh. +pub fn paintCorner(id: dvui.Id, c: Corner, t: f32, look: Look) void { + const s = dvui.windowRectScale().s; + const prev = dvui.clip(c.card); + defer dvui.clipSet(prev); + const shape = c.shape(s, t); + if (shape.half[0] < 1) return; + core.widgets.orb.draw(id, shape, s, .{ + .place = c.place(), + // Lit under the pointer, lighter still as the wait before the bar runs. + .light = if (look.lit) std.math.lerp(core.dialogs.bubble_hover, core.dialogs.bubble_press, look.held) else 0, + }); + if (look.fill <= 0) return; + // The vessel: the glass a little in from its rim, cut by the card a little in from its edge. + const inset = liquid_inset * s; + const geometry = core.orb_geometry; + var a: [160]geometry.Pt = undefined; + var b: [160]geometry.Pt = undefined; + var out: [320]geometry.Pt = undefined; + var scratch: [320]geometry.Pt = undefined; + const inner: geometry.Shape = .circle(shape.center, @max(0, shape.half[0] - inset)); + const card = c.card.insetAll(inset); + const card_radii: [4]f32 = .{ @max(0, c.radii[0] - inset), @max(0, c.radii[1] - inset), @max(0, c.radii[2] - inset), @max(0, c.radii[3] - inset) }; + const vessel = geometry.cut(inner.outline(24, &a), geometry.Shape.box(card.x, card.y, card.w, card.h, card_radii).outline(16, &b), &out, &scratch); + if (vessel.len < 3) return; + const p = c.point(); + if (look.liquid) |liq| return pourLiquid(vessel, liq, p[1] - (shape.half[0] - inset), p[1] - inset, s, liquidColor(@max(look.glow, 0.6 * look.held))); + // Never full: at most `fill_top` of the way up what shows, so there is always room to swing. + const top = p[1] - (shape.half[0] - inset); + const level = std.math.lerp(p[1] - inset, top, rules.fill_top * look.fill); + const tip = look.tilt * 0.45; + var kept: [321]rules.Pt = undefined; + pour(rules.cut(vessel, .{ p[0] - shape.half[0] / 2, level }, .{ @sin(tip), -@cos(tip) }, &kept), liquidColor(@max(look.glow, 0.6 * look.held))); +} + +/// How far in from the glass and the card's edge the liquid keeps, points. +const liquid_inset: f32 = 2.5; +/// A drop's radius, points. +const drop_r: f32 = 2.4; + +/// The liquid `liq` in `vessel` (physical, convex), its top `top` and its bottom `bottom` (y): the +/// surface drawn column to column in strips, each cut by the vessel, with a lighter line along +/// its top, and the drops in the air, cut by the vessel too, so one coming in emerges from the +/// glass rather than over it. +fn pourLiquid(vessel: []const rules.Pt, liq: *const Liquid, top: f32, bottom: f32, s: f32, color: dvui.Color) void { + const geometry = core.orb_geometry; + var minx: f32 = std.math.floatMax(f32); + var maxx: f32 = -std.math.floatMax(f32); + for (vessel) |q| { + minx = @min(minx, q[0]); + maxx = @max(maxx, q[0]); + } + const level_y = std.math.lerp(bottom, top, rules.fill_top * liq.shownLevel()); + const dx = (maxx - minx) / @as(f32, Liquid.columns - 1); + const shine = color.lerp(.white, 0.35); + var out: [256]geometry.Pt = undefined; + var scratch: [256]geometry.Pt = undefined; + for (0..Liquid.columns - 1) |i| { + const x0 = minx + dx * @as(f32, @floatFromInt(i)); + // A hair past the next column, so the strips' smoothed edges do not show as seams. + const x1 = x0 + dx + 0.75 * s; + const y0 = level_y - liq.h[i]; + const y1 = level_y - liq.h[i + 1]; + const strip = [_]geometry.Pt{ .{ x0, y0 }, .{ x1, y1 }, .{ x1, bottom + 2 * s }, .{ x0, bottom + 2 * s } }; + pour(geometry.cut(&strip, vessel, &out, &scratch), color); + const line = [_]geometry.Pt{ .{ x0, y0 - 0.5 * s }, .{ x1, y1 - 0.5 * s }, .{ x1, y1 + 0.9 * s }, .{ x0, y0 + 0.9 * s } }; + pour(geometry.cut(&line, vessel, &out, &scratch), shine); + } + var circle: [48]geometry.Pt = undefined; + for (liq.drops[0..liq.drop_n]) |d| { + const c: geometry.Shape = .circle(.{ minx + d.x * (maxx - minx), top + d.y }, drop_r * s); + pour(geometry.cut(c.outline(12, &circle), vessel, &out, &scratch), color); + } +} + +/// The stream poured from `lip` (the pool's surface) to `end` (the line's knob), physical, from +/// `tail` to `head` of the way along (0 to 1): up off the pool and over in an arc, thin at the lip +/// and the line's width where it lands, as a stream running out of a vessel thins at its lip. +fn pourStream(lip: [2]f32, end: [2]f32, tail: f32, head: f32, th: f32, color: dvui.Color) void { + // A quadratic arc, its control point up off the middle by a part of the way across. + const span = @abs(end[0] - lip[0]) + @abs(end[1] - lip[1]); + const ctrl: [2]f32 = .{ (lip[0] + end[0]) / 2, @min(lip[1], end[1]) - span * 0.18 }; + const at = struct { + fn f(a: [2]f32, b: [2]f32, c: [2]f32, u: f32) [2]f32 { + const v = 1 - u; + return .{ v * v * a[0] + 2 * v * u * b[0] + u * u * c[0], v * v * a[1] + 2 * v * u * b[1] + u * u * c[1] }; + } + }.f; + const n = 18; + var prev = at(lip, ctrl, end, tail); + var prev_w = std.math.lerp(th * 0.45, th, tail) / 2; + for (1..n + 1) |i| { + const u = std.math.lerp(tail, head, @as(f32, @floatFromInt(i)) / n); + const p = at(lip, ctrl, end, u); + const w = std.math.lerp(th * 0.45, th, u) / 2; + const dx = p[0] - prev[0]; + const dy = p[1] - prev[1]; + const len = @max(0.001, @sqrt(dx * dx + dy * dy)); + const nx = -dy / len; + const ny = dx / len; + // A hair longer than the step, so the segments' smoothed edges do not show as seams. + const ex = dx / len * 0.4; + const ey = dy / len * 0.4; + const quad = [_]rules.Pt{ + .{ prev[0] + nx * prev_w - ex, prev[1] + ny * prev_w - ey }, + .{ p[0] + nx * w + ex, p[1] + ny * w + ey }, + .{ p[0] - nx * w + ex, p[1] - ny * w + ey }, + .{ prev[0] - nx * prev_w - ex, prev[1] - ny * prev_w - ey }, + }; + pour(&quad, color); + prev = p; + prev_w = w; + } + // A round head where it is running. + const h = at(lip, ctrl, end, head); + const hw = std.math.lerp(th * 0.45, th, head) / 2; + const r: dvui.Rect.Physical = .{ .x = h[0] - hw, .y = h[1] - hw, .w = 2 * hw, .h = 2 * hw }; + r.fill(.all(hw), .{ .color = .{ .color = color }, .fade = 1 }); +} diff --git a/build/app.zig b/build/app.zig index 209378c35..248a7143c 100644 --- a/build/app.zig +++ b/build/app.zig @@ -862,6 +862,8 @@ pub fn construct(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. // An orb's geometry: shapes, how one becomes another, the polygons the no-glass path fills // (plans/LIQUID_EDGE_PLAN.md, "The orb system"). std-only. .{ "fizzy-orb-geometry-tests", "core/orb_geometry.zig" }, + // The liquid in a history vial: its waves, its drops, and when it is still. std-only. + .{ "fizzy-liquid-tests", "app/Liquid.zig" }, // The in-app menu bar's order: fizzy's menus, then plugins', then Help. std-only (see // menu_order.zig); `Menu.draw` walks it. .{ "fizzy-menu-order-tests", "src/editor/menu_order.zig" }, diff --git a/src/editor/Editor.zig b/src/editor/Editor.zig index f96db7f6d..794d79a53 100644 --- a/src/editor/Editor.zig +++ b/src/editor/Editor.zig @@ -3067,12 +3067,19 @@ fn drawDocSurface(ctx: ?*anyopaque) anyerror!dvui.App.Result { // around it. // The card the document is drawn on, the first box round it reaching lower (its padding): its // bottom edge is where the history's grip stands. + // Its rect and corners too: the history's grip is a vial in its bottom-right corner. + var card_corner: ?@import("app").dome.Corner = null; const card_edge = edge: { var wd = dvui.parentGet().data(); const own = wd.borderRectScale().r; for (0..8) |_| { - const r = wd.borderRectScale().r; - if (r.y + r.h > own.y + own.h + 0.5) break :edge r.y + r.h; + const rs = wd.borderRectScale(); + const r = rs.r; + if (r.y + r.h > own.y + own.h + 0.5) { + const cr = wd.options.cornersGet().scale(rs.s, dvui.CornerRect.Physical); + card_corner = .{ .card = r, .radii = .{ cr.tl.radius(), cr.tr.radius(), cr.br.radius(), cr.bl.radius() } }; + break :edge r.y + r.h; + } if (wd.parent.data() == wd) break; wd = wd.parent.data(); } @@ -3126,6 +3133,11 @@ fn drawDocSurface(ctx: ?*anyopaque) anyerror!dvui.App.Result { if (claim_pill == null) if (ds.transport.place(area, now)) |bar| { area.h = @min(area.h, bar.y - area.y + 6 * dvui.windowRectScale().s); }; + // And above the history's vial in the card's corner, which the feed's newest would sit on. + if (claim_pill == null and ds.has_history) if (card_corner) |c| { + const s = dvui.windowRectScale().s; + area.h = @min(area.h, c.point()[1] - c.radiusPx(s) - area.y + 6 * s); + }; if (ds.feed.draw(area, @truncate(ds.doc_id), now)) |step| editor.redoFromFeed(doc, step); } @@ -3167,6 +3179,7 @@ fn drawDocSurface(ctx: ?*anyopaque) anyerror!dvui.App.Result { .history = ds.has_history, .steps = if (recorded) |tr| tr.steps.items.len else 0, .edge = card_edge, + .corner = card_corner, .frames = if (recorded != null) &ds.frames else null, .export_from = if (track) |tr| tr.base_top else null, .window_summary = summary, From 8dcf15bae5f136972d45787794771818205991ee Mon Sep 17 00:00:00 2001 From: foxnne Date: Sun, 18 Oct 2026 17:16:58 -0500 Subject: [PATCH 6/7] core: glass cut by its place with an edge of its own, and a rim lit from the top right `LiquidField.Edge.cut` cuts glass by its place's outline with a bevel and lens band of its own along the cut, rather than joining the place's outside: a piece of glass laid in a corner, bending the border and ring it lies along. `light_across` turns a drop's rim light a quarter, for glass whose edges that show face across the usual light (a place's top-right or bottom-right corner). Both reach `core.dialogs.glassBubbles` as options. --- backend/src/glass/LiquidField.zig | 35 +- .../compiled/dxil/liquid_glass.fragment.dxil | Bin 10068 -> 10184 bytes .../compiled/glsl/liquid_glass.fragment.glsl | 793 +++++++++--------- .../compiled/msl/liquid_glass.fragment.msl | 774 +++++++++-------- .../compiled/spv/liquid_glass.fragment.spv | Bin 18972 -> 19544 bytes .../glass/shaders/liquid_glass.fragment.hlsl | 28 +- core/dialogs.zig | 5 +- 7 files changed, 879 insertions(+), 756 deletions(-) diff --git a/backend/src/glass/LiquidField.zig b/backend/src/glass/LiquidField.zig index 18aa3b507..145c4fd41 100644 --- a/backend/src/glass/LiquidField.zig +++ b/backend/src/glass/LiquidField.zig @@ -98,12 +98,23 @@ surface: bool = false, /// It is a lens over a picture of the caller's own (`drawPicture`): its middle the picture as it /// is, only the band along its edge glass (`glass_look.forLens`). lens: bool = false, +/// A drop's rim lit from the top right rather than the top left: lit along the other diagonal, +/// top right and bottom left, and shaded across top left and bottom right. The glass program's +/// only; the fallback without it (`liquid_glass`) keeps its light at the top left. For glass whose edges +/// that show face that way, as a piece in a place's top-right corner does — its arc down and to +/// the left, its cut up and to the right — where the usual light would leave both in shade. +light_across: bool = false, /// A place's outline (`edge`), physical: its rect and its corner radii, top-left, top-right, /// bottom-right, bottom-left. pub const Edge = struct { rect: dvui.Rect.Physical, radii: [4]f32 = @splat(0), + /// Cut by the outline with an edge of its own there, rather than joined to the outside: a + /// rim and a bend along the cut, as a piece of glass laid in the place, its edge against the + /// place's, bending what lies along it (a focus ring, the place's border). Joined, the glass + /// carries on past the outline as far as its light and bend go, and the cut shows neither. + cut: bool = false, }; /// Pixels past its place's outline the outside of the place begins: `EDGE_GAP` in the program. @@ -267,7 +278,8 @@ pub fn pack(self: *const LiquidField, covered: dvui.Rect.Physical, has_sharp: bo u.shapes[max_shapes - 1] = .{ .{ e.rect.x + e.rect.w / 2, e.rect.y + e.rect.h / 2, e.rect.w / 2, e.rect.h / 2 }, e.radii, - .{ first.blur, first.lens, 0, 2 }, + // 3: cut, with an edge of its own (`Edge.cut`); 2: joined to the outside. + .{ first.blur, first.lens, 0, if (e.cut) 3 else 2 }, }; } return u; @@ -380,7 +392,9 @@ pub fn draw(self: *const LiquidField, frost: dvui.Texture, covered: dvui.Rect.Ph const groups = self.clusters(); var u = self.pack(covered, sharp != null, groups.order[0..self.len]); // The window behind the glass, published for this frame. - u.backdrop[0] = if (opaqueWindow() or self.solid) 1 else 0; + // And the light turned (`light_across`): +2 on the same lane, so it holds on every look, + // published or not. + u.backdrop[0] = (if (opaqueWindow() or self.solid) @as(f32, 1) else 0) + (if (self.light_across) @as(f32, 2) else 0); // And the glass the sliders make of it this frame (`publishLook`). if (publishedLook()) |l| self.applyLook(&u, if (self.lens) glass_look.forLens(l) else if (self.text) glass_look.forText(l) else glass_look.forDrops(l)); const textures = [_]?*anyopaque{programs.handle(sharp)}; @@ -527,6 +541,8 @@ pub fn sample(self: *const LiquidField, p: dvui.Point.Physical) Sample { } // The place (`edge`): its outside one more shape, its outline cutting the coverage. var in_place: f32 = -big; + var place_out: [2]f32 = .{ 0, 0 }; + const cut = if (self.edge) |e| e.cut else false; if (self.edge) |e| { const c: [2]f32 = .{ e.rect.x + e.rect.w / 2, e.rect.y + e.rect.h / 2 }; const half: [2]f32 = .{ e.rect.w / 2, e.rect.h / 2 }; @@ -534,6 +550,9 @@ pub fn sample(self: *const LiquidField, p: dvui.Point.Physical) Sample { const radii: [4]f32 = .{ @min(e.radii[0], lim), @min(e.radii[1], lim), @min(e.radii[2], lim), @min(e.radii[3], lim) }; var g: [2]f32 = undefined; in_place = roundBox(.{ p.x - c[0], p.y - c[1] }, half, radii, &g); + place_out = g; + } + if (self.edge != null and !cut) { const d = edge_gap - in_place; const mat: [3]f32 = .{ m1[0], m1[1], 0 }; if (d < d1) { @@ -545,7 +564,7 @@ pub fn sample(self: *const LiquidField, p: dvui.Point.Physical) Sample { d2 = d; m2 = mat; } - const o: [2]f32 = .{ -g[0], -g[1] }; + const o: [2]f32 = .{ -place_out[0], -place_out[1] }; if (d < f1) { f2 = f1; o2 = o1; @@ -560,12 +579,18 @@ pub fn sample(self: *const LiquidField, p: dvui.Point.Physical) Sample { var wm: f32 = 0; var wf: f32 = 0; const d = smin(d1, d2, k, &wm); - const f = smin(f1, f2, k, &wf); + var f = smin(f1, f2, k, &wf); + var out: [2]f32 = .{ std.math.lerp(o1[0], o2[0], wf), std.math.lerp(o1[1], o2[1], wf) }; + // Cut: where the place's outline is nearer than the shapes' own, the glass's edge is the cut. + if (cut and in_place > f) { + f = in_place; + out = place_out; + } const soft = @max(0, -f); return .{ .d = @max(d, in_place), .coverage = std.math.clamp(0.5 - @max(d, in_place), 0, 1), - .out = .{ .x = std.math.lerp(o1[0], o2[0], wf), .y = std.math.lerp(o1[1], o2[1], wf) }, + .out = .{ .x = out[0], .y = out[1] }, .steep = @exp(-soft / liquid_glass.depthPx(self.look(), rim)), .blur = std.math.lerp(m1[0], m2[0], wm), .lens = std.math.lerp(m1[1], m2[1], wm), diff --git a/backend/src/glass/shaders/compiled/dxil/liquid_glass.fragment.dxil b/backend/src/glass/shaders/compiled/dxil/liquid_glass.fragment.dxil index 7b37e24b9601dc695c084563d39ef53f63086bd4..081c06c551bbb995922f762b45f22458e45c3650 100644 GIT binary patch delta 5176 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 Nf!wx6JNf?y`X5NtVYUDO diff --git a/backend/src/glass/shaders/compiled/glsl/liquid_glass.fragment.glsl b/backend/src/glass/shaders/compiled/glsl/liquid_glass.fragment.glsl index 181cb9c5d..a0b099fe2 100644 --- a/backend/src/glass/shaders/compiled/glsl/liquid_glass.fragment.glsl +++ b/backend/src/glass/shaders/compiled/glsl/liquid_glass.fragment.glsl @@ -12,182 +12,181 @@ layout(location = 0) out highp vec4 out_var_SV_Target0; void main() { - highp vec4 _829; + highp vec4 _853; do { - int _94 = int((vColor.y * 255.0) + 0.5); - int _99 = int((vColor.z * 255.0) + 0.5); - bool _115 = uData[5].y > 0.0; - highp float _117; - highp vec2 _120; - highp vec4 _122; - highp vec2 _124; - highp float _126; + int _96 = int((vColor.y * 255.0) + 0.5); + int _101 = int((vColor.z * 255.0) + 0.5); + bool _117 = uData[5].y > 0.0; + highp float _119; + highp vec2 _122; + highp vec4 _124; + highp vec2 _126; highp float _128; - highp vec4 _130; - highp float _132; + highp float _130; + highp vec4 _132; highp float _134; highp float _136; - _117 = 1000000000.0; - _120 = vec2(0.0); - _122 = vec4(0.0); - _124 = vec2(0.0); - _126 = 1000000000.0; + highp float _138; + _119 = 1000000000.0; + _122 = vec2(0.0); + _124 = vec4(0.0); + _126 = vec2(0.0); _128 = 1000000000.0; - _130 = vec4(0.0); - _132 = 1000000000.0; + _130 = 1000000000.0; + _132 = vec4(0.0); _134 = 1000000000.0; _136 = 1000000000.0; - highp vec2 _125; + _138 = 1000000000.0; + highp vec2 _127; + highp float _131; + highp vec4 _133; + highp float _137; + highp float _139; + highp float _120; + highp vec2 _123; + highp vec4 _125; highp float _129; - highp vec4 _131; highp float _135; - highp float _137; - highp float _118; - highp vec2 _121; - highp vec4 _123; - highp float _127; - highp float _133; - int _138 = _94; + int _140 = _96; for (;;) { - bool _147; - if (_138 < (_94 + _99)) + bool _149; + if (_140 < (_96 + _101)) { - _147 = _138 < 16; + _149 = _140 < 16; } else { - _147 = false; + _149 = false; } - if (_147) + if (_149) { - int _149 = 3 * _138; - int _150 = 7 + _149; - int _156 = _149 + 9; - highp vec2 _160 = vTextureCoord - uData[_150 * 1 + 0].xy; - highp float _164 = min(uData[_150 * 1 + 0].z, uData[_150 * 1 + 0].w); - highp vec4 _166 = min(uData[(_149 + 8) * 1 + 0], vec4(_164)); - highp float _167 = _160.x; - highp float _188; - if (_167 > 0.0) + int _151 = 3 * _140; + int _152 = 7 + _151; + int _158 = _151 + 9; + highp vec2 _162 = vTextureCoord - uData[_152 * 1 + 0].xy; + highp float _166 = min(uData[_152 * 1 + 0].z, uData[_152 * 1 + 0].w); + highp vec4 _168 = min(uData[(_151 + 8) * 1 + 0], vec4(_166)); + highp float _169 = _162.x; + highp float _190; + if (_169 > 0.0) { - highp float _179; - if (_160.y > 0.0) + highp float _181; + if (_162.y > 0.0) { - _179 = _166.z; + _181 = _168.z; } else { - _179 = _166.y; + _181 = _168.y; } - _188 = _179; + _190 = _181; } else { - highp float _187; - if (_160.y > 0.0) + highp float _189; + if (_162.y > 0.0) { - _187 = _166.w; + _189 = _168.w; } else { - _187 = _166.x; + _189 = _168.x; } - _188 = _187; + _190 = _189; } - highp vec2 _192 = (abs(_160) - uData[_150 * 1 + 0].zw) + vec2(_188); - highp float _194 = (_167 < 0.0) ? (-1.0) : 1.0; - highp float _197 = (_160.y < 0.0) ? (-1.0) : 1.0; - highp float _199 = _192.x; - bool _205; - if (_199 > 0.0) + highp vec2 _194 = (abs(_162) - uData[_152 * 1 + 0].zw) + vec2(_190); + highp float _196 = (_169 < 0.0) ? (-1.0) : 1.0; + highp float _199 = (_162.y < 0.0) ? (-1.0) : 1.0; + highp float _201 = _194.x; + bool _207; + if (_201 > 0.0) { - _205 = _192.y > 0.0; + _207 = _194.y > 0.0; } else { - _205 = false; + _207 = false; } - highp vec2 _219; - if (_205) + highp vec2 _221; + if (_207) { - _219 = normalize(_192) * vec2(_194, _197); + _221 = normalize(_194) * vec2(_196, _199); } else { - highp vec2 _218; - if (_199 > _192.y) + highp vec2 _220; + if (_201 > _194.y) { - _218 = vec2(_194, 0.0); + _220 = vec2(_196, 0.0); } else { - _218 = vec2(0.0, _197); + _220 = vec2(0.0, _199); } - _219 = _218; + _221 = _220; } - highp float _226 = (min(max(_199, _192.y), 0.0) + length(max(_192, vec2(0.0)))) - _188; - bool _227 = _226 < _134; - if (_227) + highp float _228 = (min(max(_201, _194.y), 0.0) + length(max(_194, vec2(0.0)))) - _190; + bool _229 = _228 < _136; + if (_229) { - _118 = _136; - _123 = _130; - _133 = _134; + _120 = _138; + _125 = _132; + _135 = _136; } else { - bool _231 = _226 < _132; - bvec4 _233 = bvec4(_231); - _118 = _231 ? _164 : _117; - _123 = vec4(_233.x ? uData[_156 * 1 + 0].x : _122.x, _233.y ? uData[_156 * 1 + 0].y : _122.y, _233.z ? uData[_156 * 1 + 0].z : _122.z, _233.w ? uData[_156 * 1 + 0].w : _122.w); - _133 = _231 ? _226 : _132; + bool _233 = _228 < _134; + bvec4 _235 = bvec4(_233); + _120 = _233 ? _166 : _119; + _125 = vec4(_235.x ? uData[_158 * 1 + 0].x : _124.x, _235.y ? uData[_158 * 1 + 0].y : _124.y, _235.z ? uData[_158 * 1 + 0].z : _124.z, _235.w ? uData[_158 * 1 + 0].w : _124.w); + _135 = _233 ? _228 : _134; } - bvec4 _236 = bvec4(_227); - _131 = vec4(_236.x ? uData[_156 * 1 + 0].x : _130.x, _236.y ? uData[_156 * 1 + 0].y : _130.y, _236.z ? uData[_156 * 1 + 0].z : _130.z, _236.w ? uData[_156 * 1 + 0].w : _130.w); - _135 = _227 ? _226 : _134; - _137 = _227 ? _164 : _136; - highp vec2 _281; - highp float _282; - if (uData[_156 * 1 + 0].w < 0.5) + bvec4 _238 = bvec4(_229); + _133 = vec4(_238.x ? uData[_158 * 1 + 0].x : _132.x, _238.y ? uData[_158 * 1 + 0].y : _132.y, _238.z ? uData[_158 * 1 + 0].z : _132.z, _238.w ? uData[_158 * 1 + 0].w : _132.w); + _137 = _229 ? _228 : _136; + _139 = _229 ? _166 : _138; + highp vec2 _283; + highp float _284; + if (uData[_158 * 1 + 0].w < 0.5) { - highp float _247 = (vTextureCoord.x - uData[_150 * 1 + 0].x) + uData[_150 * 1 + 0].z; - highp float _249 = (uData[_150 * 1 + 0].x + uData[_150 * 1 + 0].z) - vTextureCoord.x; - highp float _254 = (vTextureCoord.y - uData[_150 * 1 + 0].y) + uData[_150 * 1 + 0].w; - highp float _256 = (uData[_150 * 1 + 0].y + uData[_150 * 1 + 0].w) - vTextureCoord.y; - highp float _260 = min(min(_247, _249), min(_254, _256)); - highp vec4 _265 = exp((vec4(_260) - vec4(_247, _249, _254, _256)) / vec4(uData[1].y)); - highp float _266 = _265.x; - highp float _267 = _265.y; - highp float _269 = _265.z; - highp float _271 = _265.w; - highp float _272 = ((_266 + _267) + _269) + _271; - _281 = vec2(_267 - _266, _271 - _269) / vec2(_272); - _282 = (uData[1].y * log(_272)) - _260; + highp float _249 = (vTextureCoord.x - uData[_152 * 1 + 0].x) + uData[_152 * 1 + 0].z; + highp float _251 = (uData[_152 * 1 + 0].x + uData[_152 * 1 + 0].z) - vTextureCoord.x; + highp float _256 = (vTextureCoord.y - uData[_152 * 1 + 0].y) + uData[_152 * 1 + 0].w; + highp float _258 = (uData[_152 * 1 + 0].y + uData[_152 * 1 + 0].w) - vTextureCoord.y; + highp float _262 = min(min(_249, _251), min(_256, _258)); + highp vec4 _267 = exp((vec4(_262) - vec4(_249, _251, _256, _258)) / vec4(uData[1].y)); + highp float _268 = _267.x; + highp float _269 = _267.y; + highp float _271 = _267.z; + highp float _273 = _267.w; + highp float _274 = ((_268 + _269) + _271) + _273; + _283 = vec2(_269 - _268, _273 - _271) / vec2(_274); + _284 = (uData[1].y * log(_274)) - _262; } else { - _281 = _219; - _282 = _226; + _283 = _221; + _284 = _228; } - bool _283 = _282 < _128; - if (_283) + bool _285 = _284 < _130; + if (_285) { - _121 = _124; - _127 = _128; + _123 = _126; + _129 = _130; } else { - bool _287 = _282 < _126; - bvec2 _288 = bvec2(_287); - _121 = vec2(_288.x ? _281.x : _120.x, _288.y ? _281.y : _120.y); - _127 = _287 ? _282 : _126; + bool _289 = _284 < _128; + bvec2 _290 = bvec2(_289); + _123 = vec2(_290.x ? _283.x : _122.x, _290.y ? _283.y : _122.y); + _129 = _289 ? _284 : _128; } - bvec2 _291 = bvec2(_283); - _125 = vec2(_291.x ? _281.x : _124.x, _291.y ? _281.y : _124.y); - _129 = _283 ? _282 : _128; - _117 = _118; - _120 = _121; + bvec2 _293 = bvec2(_285); + _127 = vec2(_293.x ? _283.x : _126.x, _293.y ? _283.y : _126.y); + _131 = _285 ? _284 : _130; + _119 = _120; _122 = _123; _124 = _125; _126 = _127; @@ -196,7 +195,8 @@ void main() _132 = _133; _134 = _135; _136 = _137; - _138++; + _138 = _139; + _140++; continue; } else @@ -204,436 +204,477 @@ void main() break; } } - highp float _400; - highp vec2 _401; - highp vec2 _402; - highp vec4 _403; - highp vec4 _404; - highp float _405; - highp float _406; - highp float _407; - highp float _408; - highp float _409; - if (uData[54].w > 1.5) - { - highp vec2 _303 = vTextureCoord - uData[52].xy; - highp vec4 _309 = min(uData[53], vec4(min(uData[52].z, uData[52].w))); - highp float _310 = _303.x; - highp float _331; - if (_310 > 0.0) + bool _297 = uData[54].w > 2.5; + bool _298 = uData[54].w > 1.5; + highp float _373; + highp vec2 _374; + if (_298) + { + highp vec2 _306 = vTextureCoord - uData[52].xy; + highp vec4 _312 = min(uData[53], vec4(min(uData[52].z, uData[52].w))); + highp float _313 = _306.x; + highp float _334; + if (_313 > 0.0) { - highp float _322; - if (_303.y > 0.0) + highp float _325; + if (_306.y > 0.0) { - _322 = _309.z; + _325 = _312.z; } else { - _322 = _309.y; + _325 = _312.y; } - _331 = _322; + _334 = _325; } else { - highp float _330; - if (_303.y > 0.0) + highp float _333; + if (_306.y > 0.0) { - _330 = _309.w; + _333 = _312.w; } else { - _330 = _309.x; + _333 = _312.x; } - _331 = _330; + _334 = _333; } - highp vec2 _335 = (abs(_303) - uData[52].zw) + vec2(_331); - highp float _337 = (_310 < 0.0) ? (-1.0) : 1.0; - highp float _340 = (_303.y < 0.0) ? (-1.0) : 1.0; - highp float _342 = _335.x; - bool _348; - if (_342 > 0.0) + highp vec2 _338 = (abs(_306) - uData[52].zw) + vec2(_334); + highp float _340 = (_313 < 0.0) ? (-1.0) : 1.0; + highp float _343 = (_306.y < 0.0) ? (-1.0) : 1.0; + highp float _345 = _338.x; + bool _351; + if (_345 > 0.0) { - _348 = _335.y > 0.0; + _351 = _338.y > 0.0; } else { - _348 = false; + _351 = false; } - highp vec2 _362; - if (_348) + highp vec2 _365; + if (_351) { - _362 = normalize(_335) * vec2(_337, _340); + _365 = normalize(_338) * vec2(_340, _343); } else { - highp vec2 _361; - if (_342 > _335.y) + highp vec2 _364; + if (_345 > _338.y) { - _361 = vec2(_337, 0.0); + _364 = vec2(_340, 0.0); } else { - _361 = vec2(0.0, _340); + _364 = vec2(0.0, _343); } - _362 = _361; + _365 = _364; } - highp float _369 = (min(max(_342, _335.y), 0.0) + length(max(_335, vec2(0.0)))) - _331; - highp float _370 = 1.0 - _369; - bool _371 = _370 < _134; - highp float _380; - highp vec4 _381; - highp float _382; - if (_371) + _373 = (min(max(_345, _338.y), 0.0) + length(max(_338, vec2(0.0)))) - _334; + _374 = _365; + } + else + { + _373 = -1000000000.0; + _374 = vec2(0.0); + } + bool _378; + if (_298) + { + _378 = !_297; + } + else + { + _378 = false; + } + highp float _411; + highp vec2 _412; + highp vec2 _413; + highp vec4 _414; + highp vec4 _415; + highp float _416; + highp float _417; + highp float _418; + highp float _419; + if (_378) + { + highp float _381 = 1.0 - _373; + bool _382 = _381 < _136; + highp float _391; + highp vec4 _392; + highp float _393; + if (_382) { - _380 = _136; - _381 = _130; - _382 = _134; + _391 = _138; + _392 = _132; + _393 = _136; } else { - bool _375 = _370 < _132; - bvec4 _377 = bvec4(_375); - _380 = _375 ? _136 : _117; - _381 = vec4(_377.x ? uData[54].x : _122.x, _377.y ? uData[54].y : _122.y, _377.z ? uData[54].z : _122.z, _377.w ? uData[54].w : _122.w); - _382 = _375 ? _370 : _132; + bool _386 = _381 < _134; + bvec4 _388 = bvec4(_386); + _391 = _386 ? _138 : _119; + _392 = vec4(_388.x ? uData[54].x : _124.x, _388.y ? uData[54].y : _124.y, _388.z ? uData[54].z : _124.z, _388.w ? uData[54].w : _124.w); + _393 = _386 ? _381 : _134; } - bvec4 _383 = bvec4(_371); - highp vec2 _386 = -_362; - bool _387 = _370 < _128; - highp vec2 _395; - highp float _396; - if (_387) + bvec4 _394 = bvec4(_382); + highp vec2 _397 = -_374; + bool _398 = _381 < _130; + highp vec2 _406; + highp float _407; + if (_398) { - _395 = _124; - _396 = _128; + _406 = _126; + _407 = _130; } else { - bool _391 = _370 < _126; - bvec2 _392 = bvec2(_391); - _395 = vec2(_392.x ? _386.x : _120.x, _392.y ? _386.y : _120.y); - _396 = _391 ? _370 : _126; + bool _402 = _381 < _128; + bvec2 _403 = bvec2(_402); + _406 = vec2(_403.x ? _397.x : _122.x, _403.y ? _397.y : _122.y); + _407 = _402 ? _381 : _128; } - bvec2 _397 = bvec2(_387); - _400 = _380; - _401 = _395; - _402 = vec2(_397.x ? _386.x : _124.x, _397.y ? _386.y : _124.y); - _403 = _381; - _404 = vec4(_383.x ? uData[54].x : _130.x, _383.y ? uData[54].y : _130.y, _383.z ? uData[54].z : _130.z, _383.w ? uData[54].w : _130.w); - _405 = _369; - _406 = _396; - _407 = _387 ? _370 : _128; - _408 = _382; - _409 = _371 ? _370 : _134; + bvec2 _408 = bvec2(_398); + _411 = _391; + _412 = _406; + _413 = vec2(_408.x ? _397.x : _126.x, _408.y ? _397.y : _126.y); + _414 = _392; + _415 = vec4(_394.x ? uData[54].x : _132.x, _394.y ? uData[54].y : _132.y, _394.z ? uData[54].z : _132.z, _394.w ? uData[54].w : _132.w); + _416 = _407; + _417 = _398 ? _381 : _130; + _418 = _393; + _419 = _382 ? _381 : _136; } else { - _400 = _117; - _401 = _120; - _402 = _124; - _403 = _122; - _404 = _130; - _405 = -1000000000.0; - _406 = _126; - _407 = _128; - _408 = _132; - _409 = _134; - } - highp float _411 = max(uData[1].x, 9.9999997473787516355514526367188e-05); - highp float _416 = max(_411 - abs(_409 - _408), 0.0) / _411; - highp float _417 = _416 * _416; - highp float _418 = _417 * 0.5; - highp float _423; - if (_408 < _409) - { - _423 = 1.0 - _418; + _411 = _119; + _412 = _122; + _413 = _126; + _414 = _124; + _415 = _132; + _416 = _128; + _417 = _130; + _418 = _134; + _419 = _136; + } + highp float _421 = max(uData[1].x, 9.9999997473787516355514526367188e-05); + highp float _426 = max(_421 - abs(_419 - _418), 0.0) / _421; + highp float _427 = _426 * _426; + highp float _428 = _427 * 0.5; + highp float _433; + if (_418 < _419) + { + _433 = 1.0 - _428; } else { - _423 = _418; + _433 = _428; } - highp float _427 = min(_409, _408) - ((_417 * _411) * 0.25); - highp float _432 = max(_411 - abs(_407 - _406), 0.0) / _411; - highp float _433 = _432 * _432; - highp float _434 = _433 * 0.5; - highp float _439; - if (_406 < _407) + highp float _437 = min(_419, _418) - ((_427 * _421) * 0.25); + highp float _442 = max(_421 - abs(_417 - _416), 0.0) / _421; + highp float _443 = _442 * _442; + highp float _444 = _443 * 0.5; + highp float _449; + if (_416 < _417) { - _439 = 1.0 - _434; + _449 = 1.0 - _444; } else { - _439 = _434; + _449 = _444; } - highp float _446 = clamp(0.5 - max(_427, _405), 0.0, 1.0); - if (_446 <= 0.0) + highp float _453 = min(_417, _416) - ((_443 * _421) * 0.25); + highp float _457; + if (_297) + { + _457 = max(_437, _373); + } + else + { + _457 = _437; + } + highp float _460 = clamp(0.5 - max(_457, _373), 0.0, 1.0); + if (_460 <= 0.0) { discard; } if (vColor.x < 0.5) { - _829 = vec4(0.0, 0.0, 0.0, _446); + _853 = vec4(0.0, 0.0, 0.0, _460); break; } - highp vec4 _457 = mix(_404, _403, vec4(_423)); - highp vec2 _459 = mix(_402, _401, vec2(_439)); - highp float _461 = max(0.0, -(min(_407, _406) - ((_433 * _411) * 0.25))); - highp float _462 = -_461; - highp float _465 = exp(_462 / uData[1].z); - highp float _471 = _457.y; - highp float _505; - highp float _506; - highp vec2 _507; - if (_115) - { - highp float _479 = max(min(uData[5].y * mix(_136, _400, _423), uData[6].w), 1.0); - highp float _482 = clamp(1.0 - (_461 / _479), 0.0, 1.0); - highp float _487 = (((uData[5].z * _479) * _471) * _482) * _482; - _505 = _482; - _506 = _487; - _507 = clamp(((vTextureCoord - (_459 * _487)) - uData[0].xy) * uData[0].zw, vec2(0.0), vec2(1.0)); + highp vec4 _471 = mix(_415, _414, vec4(_433)); + highp vec2 _473 = mix(_413, _412, vec2(_449)); + bool _477; + if (_297) + { + _477 = _373 > _453; + } + else + { + _477 = false; + } + bvec2 _478 = bvec2(_477); + highp vec2 _479 = vec2(_478.x ? _374.x : _473.x, _478.y ? _374.y : _473.y); + highp float _482 = max(0.0, -(_477 ? _373 : _453)); + highp float _483 = -_482; + highp float _486 = exp(_483 / uData[1].z); + highp float _492 = _471.y; + highp float _526; + highp float _527; + highp vec2 _528; + if (_117) + { + highp float _500 = max(min(uData[5].y * mix(_138, _411, _433), uData[6].w), 1.0); + highp float _503 = clamp(1.0 - (_482 / _500), 0.0, 1.0); + highp float _508 = (((uData[5].z * _500) * _492) * _503) * _503; + _526 = _503; + _527 = _508; + _528 = clamp(((vTextureCoord - (_479 * _508)) - uData[0].xy) * uData[0].zw, vec2(0.0), vec2(1.0)); } else { - _505 = 0.0; - _506 = 0.0; - _507 = clamp(((vTextureCoord + (_459 * ((uData[2].x * _471) * _465))) - uData[0].xy) * uData[0].zw, vec2(0.0), vec2(1.0)); + _526 = 0.0; + _527 = 0.0; + _528 = clamp(((vTextureCoord + (_479 * ((uData[2].x * _492) * _486))) - uData[0].xy) * uData[0].zw, vec2(0.0), vec2(1.0)); } - highp vec4 _511 = textureLod(uSampler, _507, 0.0); - bool _513 = uData[4].w > 0.5; - highp vec4 _521; - if (_513) + highp vec4 _532 = textureLod(uSampler, _528, 0.0); + bool _534 = uData[4].w > 0.5; + highp vec4 _542; + if (_534) { - _521 = textureLod(uTex1, _507, 0.0); + _542 = textureLod(uTex1, _528, 0.0); } else { - _521 = _511; + _542 = _532; } - bool _526; - if (_115) + bool _547; + if (_117) { - _526 = uData[6].y > 0.0; + _547 = uData[6].y > 0.0; } else { - _526 = false; + _547 = false; } - bool _530; - if (_526) + bool _551; + if (_547) { - _530 = _506 > 0.5; + _551 = _527 > 0.5; } else { - _530 = false; - } - highp vec4 _591; - highp vec4 _592; - if (_530) - { - highp vec2 _538 = (_459 * ((_506 * 0.039999999105930328369140625) * uData[6].y)) * uData[0].zw; - highp vec2 _540 = clamp(_507 + _538, vec2(0.0), vec2(1.0)); - highp vec2 _542 = clamp(_507 - _538, vec2(0.0), vec2(1.0)); - highp vec4 _546 = textureLod(uSampler, _540, 0.0); - highp float _551 = _511.w; - highp float _552 = (_546.x / max(_546.w, 9.9999997473787516355514526367188e-05)) * _551; - highp vec4 _553 = _511; - _553.x = _552; - highp vec4 _557 = textureLod(uSampler, _542, 0.0); - highp float _562 = (_557.z / max(_557.w, 9.9999997473787516355514526367188e-05)) * _551; - _553.z = _562; - highp vec4 _590; - if (_513) + _551 = false; + } + highp vec4 _612; + highp vec4 _613; + if (_551) + { + highp vec2 _559 = (_479 * ((_527 * 0.039999999105930328369140625) * uData[6].y)) * uData[0].zw; + highp vec2 _561 = clamp(_528 + _559, vec2(0.0), vec2(1.0)); + highp vec2 _563 = clamp(_528 - _559, vec2(0.0), vec2(1.0)); + highp vec4 _567 = textureLod(uSampler, _561, 0.0); + highp float _572 = _532.w; + highp float _573 = (_567.x / max(_567.w, 9.9999997473787516355514526367188e-05)) * _572; + highp vec4 _574 = _532; + _574.x = _573; + highp vec4 _578 = textureLod(uSampler, _563, 0.0); + highp float _583 = (_578.z / max(_578.w, 9.9999997473787516355514526367188e-05)) * _572; + _574.z = _583; + highp vec4 _611; + if (_534) { - highp vec4 _570 = textureLod(uTex1, _540, 0.0); - highp vec4 _577 = _521; - _577.x = (_570.x / max(_570.w, 9.9999997473787516355514526367188e-05)) * _521.w; - highp vec4 _581 = textureLod(uTex1, _542, 0.0); - _577.z = (_581.z / max(_581.w, 9.9999997473787516355514526367188e-05)) * _521.w; - _590 = _577; + highp vec4 _591 = textureLod(uTex1, _561, 0.0); + highp vec4 _598 = _542; + _598.x = (_591.x / max(_591.w, 9.9999997473787516355514526367188e-05)) * _542.w; + highp vec4 _602 = textureLod(uTex1, _563, 0.0); + _598.z = (_602.z / max(_602.w, 9.9999997473787516355514526367188e-05)) * _542.w; + _611 = _598; } else { - highp vec4 _588 = _521; - _588.x = _552; - _588.z = _562; - _590 = _588; + highp vec4 _609 = _542; + _609.x = _573; + _609.z = _583; + _611 = _609; } - _591 = _590; - _592 = _553; + _612 = _611; + _613 = _574; } else { - _591 = _521; - _592 = _511; + _612 = _542; + _613 = _532; } - bool _594 = uData[4].x > 1.5; - highp float _607; - if (_594) + bool _615 = uData[4].x > 1.5; + highp float _629; + if (_615) { - _607 = uData[3].w; + _629 = uData[3].w; } else { - highp float _606; - if (uData[6].x > 0.5) + highp float _628; + if ((uData[6].x - 2.0 * trunc(uData[6].x / 2.0)) > 0.5) { - _606 = 1.0; + _628 = 1.0; } else { - _606 = _592.w; + _628 = _613.w; } - _607 = _606; + _629 = _628; } - highp vec4 _616 = vec4(_592.xyz / vec3(max(_592.w, 9.9999997473787516355514526367188e-05)), 1.0); - highp vec4 _625 = vec4(_591.xyz / vec3(max(_591.w, 9.9999997473787516355514526367188e-05)), 1.0); - highp float _632; - if (_594) + highp vec4 _638 = vec4(_613.xyz / vec3(max(_613.w, 9.9999997473787516355514526367188e-05)), 1.0); + highp vec4 _647 = vec4(_612.xyz / vec3(max(_612.w, 9.9999997473787516355514526367188e-05)), 1.0); + highp float _654; + if (_615) { - _632 = 1.0; + _654 = 1.0; } else { - _632 = (uData[4].z > 0.5) ? uData[4].x : 0.0; + _654 = (uData[4].z > 0.5) ? uData[4].x : 0.0; } - bool _636; - if (_115) + bool _658; + if (_117) { - _636 = !_594; + _658 = !_615; } else { - _636 = false; + _658 = false; } - highp float _645; - if (_636) + highp float _667; + if (_658) { - _645 = _632 * (1.0 - ((_505 * _505) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _632)))); + _667 = _654 * (1.0 - ((_526 * _526) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _654)))); } else { - _645 = _632; + _667 = _654; } - highp float _648 = 1.0 - _645; - highp float _656; - if (_115) + highp float _670 = 1.0 - _667; + highp float _678; + if (_117) { - _656 = _505; + _678 = _526; } else { - _656 = _465 * _465; + _678 = _486 * _486; } - highp float _659 = clamp(((uData[2].y * _471) * _656) * _648, 0.0, 1.0); - highp vec4 _665 = ((_625 * _659) + ((mix(_625, _616, vec4(clamp(_457.x, 0.0, 1.0))) * _648) * (1.0 - _659))) + (uData[3] * _645); - highp vec4 _682; - if (_594) + highp float _681 = clamp(((uData[2].y * _492) * _678) * _670, 0.0, 1.0); + highp vec4 _687 = ((_647 * _681) + ((mix(_647, _638, vec4(clamp(_471.x, 0.0, 1.0))) * _670) * (1.0 - _681))) + (uData[3] * _667); + highp vec4 _704; + if (_615) { - _682 = vec4(uData[3].xyz / vec3(max(uData[3].w, 9.9999997473787516355514526367188e-05)), 1.0); + _704 = vec4(uData[3].xyz / vec3(max(uData[3].w, 9.9999997473787516355514526367188e-05)), 1.0); } else { - _682 = _665 + (_616 * (1.0 - _665.w)); + _704 = _687 + (_638 * (1.0 - _687.w)); } - highp float _683 = dot(_459, vec2(-0.707106769084930419921875)); - highp float _684 = max(_683, 0.0); - highp float _686 = max(-_683, 0.0); - highp float _689 = abs(_683); - bool _694; - if (_115) + bvec2 _707 = bvec2(uData[6].x > 1.5); + highp float _710 = dot(_479, vec2(_707.x ? vec2(0.707106769084930419921875, -0.707106769084930419921875).x : vec2(-0.707106769084930419921875).x, _707.y ? vec2(0.707106769084930419921875, -0.707106769084930419921875).y : vec2(-0.707106769084930419921875).y)); + highp float _711 = max(_710, 0.0); + highp float _713 = max(-_710, 0.0); + highp float _716 = abs(_710); + bool _720; + if (_117) { - _694 = uData[6].z > 0.0; + _720 = uData[6].z > 0.0; } else { - _694 = false; + _720 = false; } - highp float _714; - if (_694) + highp float _739; + if (_720) { - _714 = (uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _689))) * exp((-max(0.0, -_427)) / (uData[2].z * 2.5)); + _739 = (uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _716))) * exp((-max(0.0, -_457)) / (uData[2].z * 2.5)); } else { - _714 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, uData[2].z * 0.625, -_427)); + _739 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, uData[2].z * 0.625, -_457)); } - highp float _724; - if (_115) + highp float _749; + if (_117) { - _724 = max((uData[5].w * _505) * _505, _714) * min(_471, 1.0); + _749 = max((uData[5].w * _526) * _526, _739) * min(_492, 1.0); } else { - _724 = 0.0; + _749 = 0.0; } - highp float _727 = _607 * (1.0 - _724); - highp vec4 _733 = vec4(_682.xyz * _727, _727 + _724); - highp float _752; - if (_115) + highp float _752 = _629 * (1.0 - _749); + highp vec4 _758 = vec4(_704.xyz * _752, _752 + _749); + highp float _776; + if (_117) { - highp float _743; - if (_694) + highp float _767; + if (_720) { - _743 = uData[6].z * smoothstep(0.75, 1.0, _689); + _767 = uData[6].z * smoothstep(0.75, 1.0, _716); } else { - _743 = 0.25; + _767 = 0.25; } - _752 = _743; + _776 = _767; } else { - _752 = (0.449999988079071044921875 * ((_684 * sqrt(_684)) + ((0.75 * _686) * sqrt(_686)))) + 0.0599999986588954925537109375; + _776 = (0.449999988079071044921875 * ((_711 * sqrt(_711)) + ((0.75 * _713) * sqrt(_713)))) + 0.0599999986588954925537109375; } - highp float _779; - if (_694) + highp float _803; + if (_720) { - _779 = exp((-max(0.0, -_427)) / (uData[2].z * 1.25)); + _803 = exp((-max(0.0, -_457)) / (uData[2].z * 1.25)); } else { - highp float _778; - if (_115) + highp float _802; + if (_117) { - highp float _766 = -_427; - _778 = smoothstep(uData[2].z * 0.3125, uData[2].z * 0.625, _766) * (1.0 - smoothstep(uData[2].z * 1.5, uData[2].z * 2.0, _766)); + highp float _790 = -_457; + _802 = smoothstep(uData[2].z * 0.3125, uData[2].z * 0.625, _790) * (1.0 - smoothstep(uData[2].z * 1.5, uData[2].z * 2.0, _790)); } else { - _778 = exp((-max(0.0, -_427)) / uData[2].z); + _802 = exp((-max(0.0, -_457)) / uData[2].z); } - _779 = _778; + _803 = _802; } - highp float _786; - if (_115) + highp float _810; + if (_117) { - _786 = 0.0; + _810 = 0.0; } else { - _786 = (0.039999999105930328369140625 * exp(_462 / uData[1].w)) * _684; + _810 = (0.039999999105930328369140625 * exp(_483 / uData[1].w)) * _711; } - highp float _794; + highp float _818; if (uData[4].z > 0.5) { - _794 = uData[4].y; + _818 = uData[4].y; } else { - _794 = 0.0; + _818 = 0.0; } - highp vec3 _804 = _733.xyz + vec3(clamp((_794 + _457.z) + ((_471 * uData[2].w) * ((_779 * _752) + _786)), 0.0, 1.0)); - highp vec4 _806 = clamp(vec4(_804.x, _804.y, _804.z, _733.w), vec4(0.0), vec4(1.0)); - highp float _813 = max(_806.w, max(_806.x, max(_806.y, _806.z))); - highp vec4 _814 = _806; - _814.w = _813; - highp vec3 _825 = _814.xyz + vec3(((fract(52.98291778564453125 * fract(dot(vTextureCoord, vec2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * uData[5].x) * _813); - _829 = clamp(vec4(_825.x, _825.y, _825.z, _814.w), vec4(0.0), vec4(1.0)) * _446; + highp vec3 _828 = _758.xyz + vec3(clamp((_818 + _471.z) + ((_492 * uData[2].w) * ((_803 * _776) + _810)), 0.0, 1.0)); + highp vec4 _830 = clamp(vec4(_828.x, _828.y, _828.z, _758.w), vec4(0.0), vec4(1.0)); + highp float _837 = max(_830.w, max(_830.x, max(_830.y, _830.z))); + highp vec4 _838 = _830; + _838.w = _837; + highp vec3 _849 = _838.xyz + vec3(((fract(52.98291778564453125 * fract(dot(vTextureCoord, vec2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * uData[5].x) * _837); + _853 = clamp(vec4(_849.x, _849.y, _849.z, _838.w), vec4(0.0), vec4(1.0)) * _460; break; } while(false); - out_var_SV_Target0 = _829; + out_var_SV_Target0 = _853; } diff --git a/backend/src/glass/shaders/compiled/msl/liquid_glass.fragment.msl b/backend/src/glass/shaders/compiled/msl/liquid_glass.fragment.msl index 716e0adbf..d3d722a4e 100644 --- a/backend/src/glass/shaders/compiled/msl/liquid_glass.fragment.msl +++ b/backend/src/glass/shaders/compiled/msl/liquid_glass.fragment.msl @@ -22,178 +22,177 @@ struct main0_in fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], texture2d Frost [[texture(0)]], texture2d Sharp [[texture(1)]], sampler FrostSampler [[sampler(0)]], sampler SharpSampler [[sampler(1)]]) { main0_out out = {}; - float4 _829; + float4 _853; do { - int _94 = int((in.in_var_COLOR.y * 255.0) + 0.5); - int _99 = int((in.in_var_COLOR.z * 255.0) + 0.5); - bool _115 = Uniforms.uData[5].y > 0.0; - float _117; - float2 _120; - float4 _122; - float2 _124; - float _126; + int _96 = int((in.in_var_COLOR.y * 255.0) + 0.5); + int _101 = int((in.in_var_COLOR.z * 255.0) + 0.5); + bool _117 = Uniforms.uData[5].y > 0.0; + float _119; + float2 _122; + float4 _124; + float2 _126; float _128; - float4 _130; - float _132; + float _130; + float4 _132; float _134; float _136; - _117 = 1000000000.0; - _120 = float2(0.0); - _122 = float4(0.0); - _124 = float2(0.0); - _126 = 1000000000.0; + float _138; + _119 = 1000000000.0; + _122 = float2(0.0); + _124 = float4(0.0); + _126 = float2(0.0); _128 = 1000000000.0; - _130 = float4(0.0); - _132 = 1000000000.0; + _130 = 1000000000.0; + _132 = float4(0.0); _134 = 1000000000.0; _136 = 1000000000.0; - float2 _125; + _138 = 1000000000.0; + float2 _127; + float _131; + float4 _133; + float _137; + float _139; + float _120; + float2 _123; + float4 _125; float _129; - float4 _131; float _135; - float _137; - float _118; - float2 _121; - float4 _123; - float _127; - float _133; - int _138 = _94; + int _140 = _96; for (;;) { - bool _147; - if (_138 < (_94 + _99)) + bool _149; + if (_140 < (_96 + _101)) { - _147 = _138 < 16; + _149 = _140 < 16; } else { - _147 = false; + _149 = false; } - if (_147) + if (_149) { - int _149 = 3 * _138; - int _150 = 7 + _149; - int _156 = _149 + 9; - float2 _160 = in.in_var_TEXCOORD0 - Uniforms.uData[_150].xy; - float _164 = precise::min(Uniforms.uData[_150].z, Uniforms.uData[_150].w); - float4 _166 = precise::min(Uniforms.uData[_149 + 8], float4(_164)); - float _167 = _160.x; - float _188; - if (_167 > 0.0) + int _151 = 3 * _140; + int _152 = 7 + _151; + int _158 = _151 + 9; + float2 _162 = in.in_var_TEXCOORD0 - Uniforms.uData[_152].xy; + float _166 = precise::min(Uniforms.uData[_152].z, Uniforms.uData[_152].w); + float4 _168 = precise::min(Uniforms.uData[_151 + 8], float4(_166)); + float _169 = _162.x; + float _190; + if (_169 > 0.0) { - float _179; - if (_160.y > 0.0) + float _181; + if (_162.y > 0.0) { - _179 = _166.z; + _181 = _168.z; } else { - _179 = _166.y; + _181 = _168.y; } - _188 = _179; + _190 = _181; } else { - float _187; - if (_160.y > 0.0) + float _189; + if (_162.y > 0.0) { - _187 = _166.w; + _189 = _168.w; } else { - _187 = _166.x; + _189 = _168.x; } - _188 = _187; + _190 = _189; } - float2 _192 = (abs(_160) - Uniforms.uData[_150].zw) + float2(_188); - float _194 = (_167 < 0.0) ? (-1.0) : 1.0; - float _197 = (_160.y < 0.0) ? (-1.0) : 1.0; - float _199 = _192.x; - bool _205; - if (_199 > 0.0) + float2 _194 = (abs(_162) - Uniforms.uData[_152].zw) + float2(_190); + float _196 = (_169 < 0.0) ? (-1.0) : 1.0; + float _199 = (_162.y < 0.0) ? (-1.0) : 1.0; + float _201 = _194.x; + bool _207; + if (_201 > 0.0) { - _205 = _192.y > 0.0; + _207 = _194.y > 0.0; } else { - _205 = false; + _207 = false; } - float2 _219; - if (_205) + float2 _221; + if (_207) { - _219 = fast::normalize(_192) * float2(_194, _197); + _221 = fast::normalize(_194) * float2(_196, _199); } else { - float2 _218; - if (_199 > _192.y) + float2 _220; + if (_201 > _194.y) { - _218 = float2(_194, 0.0); + _220 = float2(_196, 0.0); } else { - _218 = float2(0.0, _197); + _220 = float2(0.0, _199); } - _219 = _218; + _221 = _220; } - float _226 = (precise::min(precise::max(_199, _192.y), 0.0) + length(precise::max(_192, float2(0.0)))) - _188; - bool _227 = _226 < _134; - if (_227) + float _228 = (precise::min(precise::max(_201, _194.y), 0.0) + length(precise::max(_194, float2(0.0)))) - _190; + bool _229 = _228 < _136; + if (_229) { - _118 = _136; - _123 = _130; - _133 = _134; + _120 = _138; + _125 = _132; + _135 = _136; } else { - bool _231 = _226 < _132; - _118 = _231 ? _164 : _117; - _123 = select(_122, Uniforms.uData[_156], bool4(_231)); - _133 = _231 ? _226 : _132; + bool _233 = _228 < _134; + _120 = _233 ? _166 : _119; + _125 = select(_124, Uniforms.uData[_158], bool4(_233)); + _135 = _233 ? _228 : _134; } - _131 = select(_130, Uniforms.uData[_156], bool4(_227)); - _135 = _227 ? _226 : _134; - _137 = _227 ? _164 : _136; - float2 _281; - float _282; - if (Uniforms.uData[_156].w < 0.5) + _133 = select(_132, Uniforms.uData[_158], bool4(_229)); + _137 = _229 ? _228 : _136; + _139 = _229 ? _166 : _138; + float2 _283; + float _284; + if (Uniforms.uData[_158].w < 0.5) { - float _247 = (in.in_var_TEXCOORD0.x - Uniforms.uData[_150].x) + Uniforms.uData[_150].z; - float _249 = (Uniforms.uData[_150].x + Uniforms.uData[_150].z) - in.in_var_TEXCOORD0.x; - float _254 = (in.in_var_TEXCOORD0.y - Uniforms.uData[_150].y) + Uniforms.uData[_150].w; - float _256 = (Uniforms.uData[_150].y + Uniforms.uData[_150].w) - in.in_var_TEXCOORD0.y; - float _260 = precise::min(precise::min(_247, _249), precise::min(_254, _256)); - float4 _265 = exp((float4(_260) - float4(_247, _249, _254, _256)) / float4(Uniforms.uData[1].y)); - float _266 = _265.x; - float _267 = _265.y; - float _269 = _265.z; - float _271 = _265.w; - float _272 = ((_266 + _267) + _269) + _271; - _281 = float2(_267 - _266, _271 - _269) / float2(_272); - _282 = (Uniforms.uData[1].y * log(_272)) - _260; + float _249 = (in.in_var_TEXCOORD0.x - Uniforms.uData[_152].x) + Uniforms.uData[_152].z; + float _251 = (Uniforms.uData[_152].x + Uniforms.uData[_152].z) - in.in_var_TEXCOORD0.x; + float _256 = (in.in_var_TEXCOORD0.y - Uniforms.uData[_152].y) + Uniforms.uData[_152].w; + float _258 = (Uniforms.uData[_152].y + Uniforms.uData[_152].w) - in.in_var_TEXCOORD0.y; + float _262 = precise::min(precise::min(_249, _251), precise::min(_256, _258)); + float4 _267 = exp((float4(_262) - float4(_249, _251, _256, _258)) / float4(Uniforms.uData[1].y)); + float _268 = _267.x; + float _269 = _267.y; + float _271 = _267.z; + float _273 = _267.w; + float _274 = ((_268 + _269) + _271) + _273; + _283 = float2(_269 - _268, _273 - _271) / float2(_274); + _284 = (Uniforms.uData[1].y * log(_274)) - _262; } else { - _281 = _219; - _282 = _226; + _283 = _221; + _284 = _228; } - bool _283 = _282 < _128; - if (_283) + bool _285 = _284 < _130; + if (_285) { - _121 = _124; - _127 = _128; + _123 = _126; + _129 = _130; } else { - bool _287 = _282 < _126; - _121 = select(_120, _281, bool2(_287)); - _127 = _287 ? _282 : _126; + bool _289 = _284 < _128; + _123 = select(_122, _283, bool2(_289)); + _129 = _289 ? _284 : _128; } - _125 = select(_124, _281, bool2(_283)); - _129 = _283 ? _282 : _128; - _117 = _118; - _120 = _121; + _127 = select(_126, _283, bool2(_285)); + _131 = _285 ? _284 : _130; + _119 = _120; _122 = _123; _124 = _125; _126 = _127; @@ -202,7 +201,8 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo _132 = _133; _134 = _135; _136 = _137; - _138++; + _138 = _139; + _140++; continue; } else @@ -210,433 +210,471 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo break; } } - float _400; - float2 _401; - float2 _402; - float4 _403; - float4 _404; - float _405; - float _406; - float _407; - float _408; - float _409; - if (Uniforms.uData[54].w > 1.5) - { - float2 _303 = in.in_var_TEXCOORD0 - Uniforms.uData[52].xy; - float4 _309 = precise::min(Uniforms.uData[53], float4(precise::min(Uniforms.uData[52].z, Uniforms.uData[52].w))); - float _310 = _303.x; - float _331; - if (_310 > 0.0) + bool _297 = Uniforms.uData[54].w > 2.5; + bool _298 = Uniforms.uData[54].w > 1.5; + float _373; + float2 _374; + if (_298) + { + float2 _306 = in.in_var_TEXCOORD0 - Uniforms.uData[52].xy; + float4 _312 = precise::min(Uniforms.uData[53], float4(precise::min(Uniforms.uData[52].z, Uniforms.uData[52].w))); + float _313 = _306.x; + float _334; + if (_313 > 0.0) { - float _322; - if (_303.y > 0.0) + float _325; + if (_306.y > 0.0) { - _322 = _309.z; + _325 = _312.z; } else { - _322 = _309.y; + _325 = _312.y; } - _331 = _322; + _334 = _325; } else { - float _330; - if (_303.y > 0.0) + float _333; + if (_306.y > 0.0) { - _330 = _309.w; + _333 = _312.w; } else { - _330 = _309.x; + _333 = _312.x; } - _331 = _330; + _334 = _333; } - float2 _335 = (abs(_303) - Uniforms.uData[52].zw) + float2(_331); - float _337 = (_310 < 0.0) ? (-1.0) : 1.0; - float _340 = (_303.y < 0.0) ? (-1.0) : 1.0; - float _342 = _335.x; - bool _348; - if (_342 > 0.0) + float2 _338 = (abs(_306) - Uniforms.uData[52].zw) + float2(_334); + float _340 = (_313 < 0.0) ? (-1.0) : 1.0; + float _343 = (_306.y < 0.0) ? (-1.0) : 1.0; + float _345 = _338.x; + bool _351; + if (_345 > 0.0) { - _348 = _335.y > 0.0; + _351 = _338.y > 0.0; } else { - _348 = false; + _351 = false; } - float2 _362; - if (_348) + float2 _365; + if (_351) { - _362 = fast::normalize(_335) * float2(_337, _340); + _365 = fast::normalize(_338) * float2(_340, _343); } else { - float2 _361; - if (_342 > _335.y) + float2 _364; + if (_345 > _338.y) { - _361 = float2(_337, 0.0); + _364 = float2(_340, 0.0); } else { - _361 = float2(0.0, _340); + _364 = float2(0.0, _343); } - _362 = _361; + _365 = _364; } - float _369 = (precise::min(precise::max(_342, _335.y), 0.0) + length(precise::max(_335, float2(0.0)))) - _331; - float _370 = 1.0 - _369; - bool _371 = _370 < _134; - float _380; - float4 _381; - float _382; - if (_371) + _373 = (precise::min(precise::max(_345, _338.y), 0.0) + length(precise::max(_338, float2(0.0)))) - _334; + _374 = _365; + } + else + { + _373 = -1000000000.0; + _374 = float2(0.0); + } + bool _378; + if (_298) + { + _378 = !_297; + } + else + { + _378 = false; + } + float _411; + float2 _412; + float2 _413; + float4 _414; + float4 _415; + float _416; + float _417; + float _418; + float _419; + if (_378) + { + float _381 = 1.0 - _373; + bool _382 = _381 < _136; + float _391; + float4 _392; + float _393; + if (_382) { - _380 = _136; - _381 = _130; - _382 = _134; + _391 = _138; + _392 = _132; + _393 = _136; } else { - bool _375 = _370 < _132; - _380 = _375 ? _136 : _117; - _381 = select(_122, Uniforms.uData[54], bool4(_375)); - _382 = _375 ? _370 : _132; + bool _386 = _381 < _134; + _391 = _386 ? _138 : _119; + _392 = select(_124, Uniforms.uData[54], bool4(_386)); + _393 = _386 ? _381 : _134; } - float2 _386 = -_362; - bool _387 = _370 < _128; - float2 _395; - float _396; - if (_387) + float2 _397 = -_374; + bool _398 = _381 < _130; + float2 _406; + float _407; + if (_398) { - _395 = _124; - _396 = _128; + _406 = _126; + _407 = _130; } else { - bool _391 = _370 < _126; - _395 = select(_120, _386, bool2(_391)); - _396 = _391 ? _370 : _126; + bool _402 = _381 < _128; + _406 = select(_122, _397, bool2(_402)); + _407 = _402 ? _381 : _128; } - _400 = _380; - _401 = _395; - _402 = select(_124, _386, bool2(_387)); - _403 = _381; - _404 = select(_130, Uniforms.uData[54], bool4(_371)); - _405 = _369; - _406 = _396; - _407 = _387 ? _370 : _128; - _408 = _382; - _409 = _371 ? _370 : _134; + _411 = _391; + _412 = _406; + _413 = select(_126, _397, bool2(_398)); + _414 = _392; + _415 = select(_132, Uniforms.uData[54], bool4(_382)); + _416 = _407; + _417 = _398 ? _381 : _130; + _418 = _393; + _419 = _382 ? _381 : _136; } else { - _400 = _117; - _401 = _120; - _402 = _124; - _403 = _122; - _404 = _130; - _405 = -1000000000.0; - _406 = _126; - _407 = _128; - _408 = _132; - _409 = _134; - } - float _411 = precise::max(Uniforms.uData[1].x, 9.9999997473787516355514526367188e-05); - float _416 = precise::max(_411 - abs(_409 - _408), 0.0) / _411; - float _417 = _416 * _416; - float _418 = _417 * 0.5; - float _423; - if (_408 < _409) - { - _423 = 1.0 - _418; + _411 = _119; + _412 = _122; + _413 = _126; + _414 = _124; + _415 = _132; + _416 = _128; + _417 = _130; + _418 = _134; + _419 = _136; + } + float _421 = precise::max(Uniforms.uData[1].x, 9.9999997473787516355514526367188e-05); + float _426 = precise::max(_421 - abs(_419 - _418), 0.0) / _421; + float _427 = _426 * _426; + float _428 = _427 * 0.5; + float _433; + if (_418 < _419) + { + _433 = 1.0 - _428; } else { - _423 = _418; + _433 = _428; } - float _427 = precise::min(_409, _408) - ((_417 * _411) * 0.25); - float _432 = precise::max(_411 - abs(_407 - _406), 0.0) / _411; - float _433 = _432 * _432; - float _434 = _433 * 0.5; - float _439; - if (_406 < _407) + float _437 = precise::min(_419, _418) - ((_427 * _421) * 0.25); + float _442 = precise::max(_421 - abs(_417 - _416), 0.0) / _421; + float _443 = _442 * _442; + float _444 = _443 * 0.5; + float _449; + if (_416 < _417) { - _439 = 1.0 - _434; + _449 = 1.0 - _444; } else { - _439 = _434; + _449 = _444; } - float _446 = fast::clamp(0.5 - precise::max(_427, _405), 0.0, 1.0); - if (_446 <= 0.0) + float _453 = precise::min(_417, _416) - ((_443 * _421) * 0.25); + float _457; + if (_297) + { + _457 = precise::max(_437, _373); + } + else + { + _457 = _437; + } + float _460 = fast::clamp(0.5 - precise::max(_457, _373), 0.0, 1.0); + if (_460 <= 0.0) { discard_fragment(); } if (in.in_var_COLOR.x < 0.5) { - _829 = float4(0.0, 0.0, 0.0, _446); + _853 = float4(0.0, 0.0, 0.0, _460); break; } - float4 _457 = mix(_404, _403, float4(_423)); - float2 _459 = mix(_402, _401, float2(_439)); - float _461 = precise::max(0.0, -(precise::min(_407, _406) - ((_433 * _411) * 0.25))); - float _462 = -_461; - float _465 = exp(_462 / Uniforms.uData[1].z); - float _471 = _457.y; - float _505; - float _506; - float2 _507; - if (_115) - { - float _479 = precise::max(precise::min(Uniforms.uData[5].y * mix(_136, _400, _423), Uniforms.uData[6].w), 1.0); - float _482 = fast::clamp(1.0 - (_461 / _479), 0.0, 1.0); - float _487 = (((Uniforms.uData[5].z * _479) * _471) * _482) * _482; - _505 = _482; - _506 = _487; - _507 = fast::clamp(((in.in_var_TEXCOORD0 - (_459 * _487)) - Uniforms.uData[0].xy) * Uniforms.uData[0].zw, float2(0.0), float2(1.0)); + float4 _471 = mix(_415, _414, float4(_433)); + bool _477; + if (_297) + { + _477 = _373 > _453; + } + else + { + _477 = false; + } + float2 _479 = select(mix(_413, _412, float2(_449)), _374, bool2(_477)); + float _482 = precise::max(0.0, -(_477 ? _373 : _453)); + float _483 = -_482; + float _486 = exp(_483 / Uniforms.uData[1].z); + float _492 = _471.y; + float _526; + float _527; + float2 _528; + if (_117) + { + float _500 = precise::max(precise::min(Uniforms.uData[5].y * mix(_138, _411, _433), Uniforms.uData[6].w), 1.0); + float _503 = fast::clamp(1.0 - (_482 / _500), 0.0, 1.0); + float _508 = (((Uniforms.uData[5].z * _500) * _492) * _503) * _503; + _526 = _503; + _527 = _508; + _528 = fast::clamp(((in.in_var_TEXCOORD0 - (_479 * _508)) - Uniforms.uData[0].xy) * Uniforms.uData[0].zw, float2(0.0), float2(1.0)); } else { - _505 = 0.0; - _506 = 0.0; - _507 = fast::clamp(((in.in_var_TEXCOORD0 + (_459 * ((Uniforms.uData[2].x * _471) * _465))) - Uniforms.uData[0].xy) * Uniforms.uData[0].zw, float2(0.0), float2(1.0)); + _526 = 0.0; + _527 = 0.0; + _528 = fast::clamp(((in.in_var_TEXCOORD0 + (_479 * ((Uniforms.uData[2].x * _492) * _486))) - Uniforms.uData[0].xy) * Uniforms.uData[0].zw, float2(0.0), float2(1.0)); } - float4 _511 = Frost.sample(FrostSampler, _507, level(0.0)); - bool _513 = Uniforms.uData[4].w > 0.5; - float4 _521; - if (_513) + float4 _532 = Frost.sample(FrostSampler, _528, level(0.0)); + bool _534 = Uniforms.uData[4].w > 0.5; + float4 _542; + if (_534) { - _521 = Sharp.sample(SharpSampler, _507, level(0.0)); + _542 = Sharp.sample(SharpSampler, _528, level(0.0)); } else { - _521 = _511; + _542 = _532; } - bool _526; - if (_115) + bool _547; + if (_117) { - _526 = Uniforms.uData[6].y > 0.0; + _547 = Uniforms.uData[6].y > 0.0; } else { - _526 = false; + _547 = false; } - bool _530; - if (_526) + bool _551; + if (_547) { - _530 = _506 > 0.5; + _551 = _527 > 0.5; } else { - _530 = false; - } - float4 _591; - float4 _592; - if (_530) - { - float2 _538 = (_459 * ((_506 * 0.039999999105930328369140625) * Uniforms.uData[6].y)) * Uniforms.uData[0].zw; - float2 _540 = fast::clamp(_507 + _538, float2(0.0), float2(1.0)); - float2 _542 = fast::clamp(_507 - _538, float2(0.0), float2(1.0)); - float4 _546 = Frost.sample(FrostSampler, _540, level(0.0)); - float _551 = _511.w; - float _552 = (_546.x / precise::max(_546.w, 9.9999997473787516355514526367188e-05)) * _551; - float4 _553 = _511; - _553.x = _552; - float4 _557 = Frost.sample(FrostSampler, _542, level(0.0)); - float _562 = (_557.z / precise::max(_557.w, 9.9999997473787516355514526367188e-05)) * _551; - _553.z = _562; - float4 _590; - if (_513) + _551 = false; + } + float4 _612; + float4 _613; + if (_551) + { + float2 _559 = (_479 * ((_527 * 0.039999999105930328369140625) * Uniforms.uData[6].y)) * Uniforms.uData[0].zw; + float2 _561 = fast::clamp(_528 + _559, float2(0.0), float2(1.0)); + float2 _563 = fast::clamp(_528 - _559, float2(0.0), float2(1.0)); + float4 _567 = Frost.sample(FrostSampler, _561, level(0.0)); + float _572 = _532.w; + float _573 = (_567.x / precise::max(_567.w, 9.9999997473787516355514526367188e-05)) * _572; + float4 _574 = _532; + _574.x = _573; + float4 _578 = Frost.sample(FrostSampler, _563, level(0.0)); + float _583 = (_578.z / precise::max(_578.w, 9.9999997473787516355514526367188e-05)) * _572; + _574.z = _583; + float4 _611; + if (_534) { - float4 _570 = Sharp.sample(SharpSampler, _540, level(0.0)); - float4 _577 = _521; - _577.x = (_570.x / precise::max(_570.w, 9.9999997473787516355514526367188e-05)) * _521.w; - float4 _581 = Sharp.sample(SharpSampler, _542, level(0.0)); - _577.z = (_581.z / precise::max(_581.w, 9.9999997473787516355514526367188e-05)) * _521.w; - _590 = _577; + float4 _591 = Sharp.sample(SharpSampler, _561, level(0.0)); + float4 _598 = _542; + _598.x = (_591.x / precise::max(_591.w, 9.9999997473787516355514526367188e-05)) * _542.w; + float4 _602 = Sharp.sample(SharpSampler, _563, level(0.0)); + _598.z = (_602.z / precise::max(_602.w, 9.9999997473787516355514526367188e-05)) * _542.w; + _611 = _598; } else { - float4 _588 = _521; - _588.x = _552; - _588.z = _562; - _590 = _588; + float4 _609 = _542; + _609.x = _573; + _609.z = _583; + _611 = _609; } - _591 = _590; - _592 = _553; + _612 = _611; + _613 = _574; } else { - _591 = _521; - _592 = _511; + _612 = _542; + _613 = _532; } - bool _594 = Uniforms.uData[4].x > 1.5; - float _607; - if (_594) + bool _615 = Uniforms.uData[4].x > 1.5; + float _629; + if (_615) { - _607 = Uniforms.uData[3].w; + _629 = Uniforms.uData[3].w; } else { - float _606; - if (Uniforms.uData[6].x > 0.5) + float _628; + if (fmod(Uniforms.uData[6].x, 2.0) > 0.5) { - _606 = 1.0; + _628 = 1.0; } else { - _606 = _592.w; + _628 = _613.w; } - _607 = _606; + _629 = _628; } - float4 _616 = float4(_592.xyz / float3(precise::max(_592.w, 9.9999997473787516355514526367188e-05)), 1.0); - float4 _625 = float4(_591.xyz / float3(precise::max(_591.w, 9.9999997473787516355514526367188e-05)), 1.0); - float _632; - if (_594) + float4 _638 = float4(_613.xyz / float3(precise::max(_613.w, 9.9999997473787516355514526367188e-05)), 1.0); + float4 _647 = float4(_612.xyz / float3(precise::max(_612.w, 9.9999997473787516355514526367188e-05)), 1.0); + float _654; + if (_615) { - _632 = 1.0; + _654 = 1.0; } else { - _632 = (Uniforms.uData[4].z > 0.5) ? Uniforms.uData[4].x : 0.0; + _654 = (Uniforms.uData[4].z > 0.5) ? Uniforms.uData[4].x : 0.0; } - bool _636; - if (_115) + bool _658; + if (_117) { - _636 = !_594; + _658 = !_615; } else { - _636 = false; + _658 = false; } - float _645; - if (_636) + float _667; + if (_658) { - _645 = _632 * (1.0 - ((_505 * _505) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _632)))); + _667 = _654 * (1.0 - ((_526 * _526) * (1.0 - smoothstep(0.800000011920928955078125, 1.0, _654)))); } else { - _645 = _632; + _667 = _654; } - float _648 = 1.0 - _645; - float _656; - if (_115) + float _670 = 1.0 - _667; + float _678; + if (_117) { - _656 = _505; + _678 = _526; } else { - _656 = _465 * _465; + _678 = _486 * _486; } - float _659 = fast::clamp(((Uniforms.uData[2].y * _471) * _656) * _648, 0.0, 1.0); - float4 _665 = ((_625 * _659) + ((mix(_625, _616, float4(fast::clamp(_457.x, 0.0, 1.0))) * _648) * (1.0 - _659))) + (Uniforms.uData[3] * _645); - float4 _682; - if (_594) + float _681 = fast::clamp(((Uniforms.uData[2].y * _492) * _678) * _670, 0.0, 1.0); + float4 _687 = ((_647 * _681) + ((mix(_647, _638, float4(fast::clamp(_471.x, 0.0, 1.0))) * _670) * (1.0 - _681))) + (Uniforms.uData[3] * _667); + float4 _704; + if (_615) { - _682 = float4(Uniforms.uData[3].xyz / float3(precise::max(Uniforms.uData[3].w, 9.9999997473787516355514526367188e-05)), 1.0); + _704 = float4(Uniforms.uData[3].xyz / float3(precise::max(Uniforms.uData[3].w, 9.9999997473787516355514526367188e-05)), 1.0); } else { - _682 = _665 + (_616 * (1.0 - _665.w)); + _704 = _687 + (_638 * (1.0 - _687.w)); } - float _683 = dot(_459, float2(-0.707106769084930419921875)); - float _684 = precise::max(_683, 0.0); - float _686 = precise::max(-_683, 0.0); - float _689 = abs(_683); - bool _694; - if (_115) + float _710 = dot(_479, select(float2(-0.707106769084930419921875), float2(0.707106769084930419921875, -0.707106769084930419921875), bool2(Uniforms.uData[6].x > 1.5))); + float _711 = precise::max(_710, 0.0); + float _713 = precise::max(-_710, 0.0); + float _716 = abs(_710); + bool _720; + if (_117) { - _694 = Uniforms.uData[6].z > 0.0; + _720 = Uniforms.uData[6].z > 0.0; } else { - _694 = false; + _720 = false; } - float _714; - if (_694) + float _739; + if (_720) { - _714 = (Uniforms.uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _689))) * exp((-precise::max(0.0, -_427)) / (Uniforms.uData[2].z * 2.5)); + _739 = (Uniforms.uData[6].z * (1.0 - smoothstep(0.20000000298023223876953125, 0.699999988079071044921875, _716))) * exp((-precise::max(0.0, -_457)) / (Uniforms.uData[2].z * 2.5)); } else { - _714 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, Uniforms.uData[2].z * 0.625, -_427)); + _739 = 0.85000002384185791015625 * (1.0 - smoothstep(0.0, Uniforms.uData[2].z * 0.625, -_457)); } - float _724; - if (_115) + float _749; + if (_117) { - _724 = precise::max((Uniforms.uData[5].w * _505) * _505, _714) * precise::min(_471, 1.0); + _749 = precise::max((Uniforms.uData[5].w * _526) * _526, _739) * precise::min(_492, 1.0); } else { - _724 = 0.0; + _749 = 0.0; } - float _727 = _607 * (1.0 - _724); - float4 _733 = float4(_682.xyz * _727, _727 + _724); - float _752; - if (_115) + float _752 = _629 * (1.0 - _749); + float4 _758 = float4(_704.xyz * _752, _752 + _749); + float _776; + if (_117) { - float _743; - if (_694) + float _767; + if (_720) { - _743 = Uniforms.uData[6].z * smoothstep(0.75, 1.0, _689); + _767 = Uniforms.uData[6].z * smoothstep(0.75, 1.0, _716); } else { - _743 = 0.25; + _767 = 0.25; } - _752 = _743; + _776 = _767; } else { - _752 = (0.449999988079071044921875 * ((_684 * sqrt(_684)) + ((0.75 * _686) * sqrt(_686)))) + 0.0599999986588954925537109375; + _776 = (0.449999988079071044921875 * ((_711 * sqrt(_711)) + ((0.75 * _713) * sqrt(_713)))) + 0.0599999986588954925537109375; } - float _779; - if (_694) + float _803; + if (_720) { - _779 = exp((-precise::max(0.0, -_427)) / (Uniforms.uData[2].z * 1.25)); + _803 = exp((-precise::max(0.0, -_457)) / (Uniforms.uData[2].z * 1.25)); } else { - float _778; - if (_115) + float _802; + if (_117) { - float _766 = -_427; - _778 = smoothstep(Uniforms.uData[2].z * 0.3125, Uniforms.uData[2].z * 0.625, _766) * (1.0 - smoothstep(Uniforms.uData[2].z * 1.5, Uniforms.uData[2].z * 2.0, _766)); + float _790 = -_457; + _802 = smoothstep(Uniforms.uData[2].z * 0.3125, Uniforms.uData[2].z * 0.625, _790) * (1.0 - smoothstep(Uniforms.uData[2].z * 1.5, Uniforms.uData[2].z * 2.0, _790)); } else { - _778 = exp((-precise::max(0.0, -_427)) / Uniforms.uData[2].z); + _802 = exp((-precise::max(0.0, -_457)) / Uniforms.uData[2].z); } - _779 = _778; + _803 = _802; } - float _786; - if (_115) + float _810; + if (_117) { - _786 = 0.0; + _810 = 0.0; } else { - _786 = (0.039999999105930328369140625 * exp(_462 / Uniforms.uData[1].w)) * _684; + _810 = (0.039999999105930328369140625 * exp(_483 / Uniforms.uData[1].w)) * _711; } - float _794; + float _818; if (Uniforms.uData[4].z > 0.5) { - _794 = Uniforms.uData[4].y; + _818 = Uniforms.uData[4].y; } else { - _794 = 0.0; + _818 = 0.0; } - float3 _804 = _733.xyz + float3(fast::clamp((_794 + _457.z) + ((_471 * Uniforms.uData[2].w) * ((_779 * _752) + _786)), 0.0, 1.0)); - float4 _806 = fast::clamp(float4(_804.x, _804.y, _804.z, _733.w), float4(0.0), float4(1.0)); - float _813 = precise::max(_806.w, precise::max(_806.x, precise::max(_806.y, _806.z))); - float4 _814 = _806; - _814.w = _813; - float3 _825 = _814.xyz + float3(((fract(52.98291778564453125 * fract(dot(in.in_var_TEXCOORD0, float2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * Uniforms.uData[5].x) * _813); - _829 = fast::clamp(float4(_825.x, _825.y, _825.z, _814.w), float4(0.0), float4(1.0)) * _446; + float3 _828 = _758.xyz + float3(fast::clamp((_818 + _471.z) + ((_492 * Uniforms.uData[2].w) * ((_803 * _776) + _810)), 0.0, 1.0)); + float4 _830 = fast::clamp(float4(_828.x, _828.y, _828.z, _758.w), float4(0.0), float4(1.0)); + float _837 = precise::max(_830.w, precise::max(_830.x, precise::max(_830.y, _830.z))); + float4 _838 = _830; + _838.w = _837; + float3 _849 = _838.xyz + float3(((fract(52.98291778564453125 * fract(dot(in.in_var_TEXCOORD0, float2(0.067110560834407806396484375, 0.005837149918079376220703125)))) - 0.5) * Uniforms.uData[5].x) * _837); + _853 = fast::clamp(float4(_849.x, _849.y, _849.z, _838.w), float4(0.0), float4(1.0)) * _460; break; } while(false); - out.out_var_SV_Target0 = _829; + out.out_var_SV_Target0 = _853; return out; } diff --git a/backend/src/glass/shaders/compiled/spv/liquid_glass.fragment.spv b/backend/src/glass/shaders/compiled/spv/liquid_glass.fragment.spv index 03cf214283617cdf09df3d3b1e0a8fcfb3d96910..8221ca3b82bacd73c9bdbc7c122730f0616b2c14 100644 GIT binary patch literal 19544 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
j)%bv&2*`D`8{(W$7=3&g* z8`Edz2h0xF)n*T9Psnw}*_XY*e5!Xc*UvlIWUxBxc5UP7YmDUlLvZVy_eN&@>da#w zW`}vGn*vRRj3rKgr-AKnYSm90ZDX@?O z+5h9g4*Rd}1n5M_ev7SJ8+ql4a}wA%*YB( z4eW0%b1{}Vr1z(T7t}iAsq=aCx8xaM>yeVnnPBDSQvQBB3%=}&xeP#N&CXfxY_R9b z-)ruBnddq1)~C*#OWnEfuBXl%qVtTL2k*I4=Xoi0=fk_l>fHC}JU}*EMMz9pQY{+c+aabJ!|T6A6*LeJ>8Kz=Q3n`s{2U&0C4Vw z%i)dZdU@Zu0?da~*l4Ja?Ui6-sZZ>yzb=i=m$EMl;nnMtcc!bs%B9Gz0W0s0 zPtNqU@aAN0J{#9DJA5|OT@NYqT!@q74Pfs%V|sRCUjq z_dDc#s{c3We1LbM_W4cqd$9W{>ErjwV_-g&kN4_1kam9G+1tm#?osyU2{4~(Z_HPH zY@Y6jqWZJ3AGxQ9YPgTGA_&*EQ-@eEH56JXa7ymzk`BeVupi>(u;jur;g8d+tYIYgU&U{sT@8t^?ar^T#myXcPNS zz_D*#^QY+a(Iz#22If=MtiB1{x@K$7{r@@Gz4NSlR_sS~Ux3}i);jzBC35?#&NcP( z-t36(D{v=f`Nf#OhF_d{7xt(vXlUoPy64)iTj_^yuC$~=U+9*$Mi-DDwxp{_M)A+7a^2+U@clP37 z{msD|we{Xvg4vOKXGwTv#uKOZ&S38lTw7}If=szRa^3hjyKXP!UBSJX%|&}-y6#fU z4%gLYX-Jvtiqqe2VE5ntx<+iAv&J&u-soLdd;MKwS!Rc8XtNxo%r(S$wz`8|V@+&R z@AAk}Z|r&?(^g%cw-vxXZ|d@_tO)*J&&o>Z^wB2I=gMIBR9&8*Rlta<=SO`L*yktr z*s9;*)3fkVAD^7L)sU6XLlb=YJoJLsMtRO$Z?JOn;q$HMp$`J}j`VHyT2_A7^hIVZ zexIoKog&{S{oswGO=9#1dq#7w`uku2GHun3um1+H23Wbaj?}p(va-&B@MWEY;I&bn zI@ba#w}*VbRp(#?>Xj$QwQE^f=Q_wzr~0zab>WSpP0rJLVC7{^L*SKL8=vYNuPfRWv{JZvd6Xfb0se98}R@OZnS?X3_*1Z|L zakNQ{&B5MB<^GO<*H&HjcO+Q3wvN=f1$dQK}g*T2i+28HJ%FCLzhgWWGe5yOm{dF(h)6_l&?AcE3k?(-a^P!&4 zxBQKh`+i5TYk5BOF}C+-?my?)js?4pcdCA6oN@3zU+O%Y>aveJf&D&o`ew}QK|6X9%k{q5jVRD&V>&i3vLu5IV>OGU=xF3zTH@y0gcdqh%khzxn#GV3H z?ucw^Ei-;((`uQfc~{&QMt|+q+l$mR9p3XLIa=4WU#%-^+8;dHU3e|4&!TcG^EV<;@o}n!TzR~@=mw_8Ej)+Xf4S6tt0P| z$AFb-mpI3QjioMj$APs|7rW!ZRV-#}N>0w{*$K6cb!uOULoei%pl(*GQIbI~R-&jl;DhV0>aU_w;COVxWutvz+0kIWuv zm;PJ;)<#+EE(F^nb^Z>vXBUC>R~NgB!G15;+t^(K)>fT8yOi0HXYw-m%OQIt&Rui` z*jSRoIuNz&&y}^lyo;_vrk%fC)t7hCLU{F|+(lO-b1n7R`)k0;9g$sI%Zwk{b+t^> zoXzWD^w(a!y>ySQ+ZwGk{x^W_k2R$KKZ4gsUHW<>cwb0e`g#*sUv*`C*YOOwM?VIe zkA2nNwa;h&{2sg+?7Ps~;QTG=7G&w4de2M#j(01(ve^EluvMpR>becyXNRffeEt+h z`8vq*H@e&5UC)1yFt+F8XUvW~Q$L4SX79vV;}>Ax`IuVn=wHGp&s`ip=iHUQ0{6zp zK51`E*S&+;;kw$~3Ec&`t~l%74bHlG&VG%|I=m~T^&o4@p56m?PqoYQ`WvvZ)TPJw zf^Q~JdVC*z_QRUg$954|ed65@w!i-SbKch;fY(l4dj25Tn8s5d|A)Zlq~7ynKdjvz z#O7hJYwU<`^7t)$bI#3wcYg#vy>JbEv`H@>1=|bl68CrD^^vJ7*EWaThrb7#qkEyf zdAVg7q_Z`=@R0#OJ_1(~`rSgz4?`Fs^TmYv`WvK;IP3l$-1=F31DUz`91ex-htFdA@g`V5?c4*`GOqDU zUU~N9EwFyB?Y~pS)>`y^ojK3f+hEuC@15SKS@#{Vy!y!A1)HOHY5d;<$6tAD{{hxs zefI31VB@Nb-TUATiK#AjAApq)LvD@9+c`CU2)0h^*4~=ji+?dYaxeZH{v*h`#kmLm z11|5bkKwJu?_$rf`q+K~R-b$7Q?N3>BhrV@!20W}-n`AlbzR@wK4*61EPVn0Wo;u) zPrd@*2ANA_U)QqoF8eRC@-F)Z-W)tj#?VKb#2|$#uJ6y0wW(!8kmdWKEwaSXM;qhh zxorpUIn^$4+JjwRowZx5wOe2M*a2PH^N#Rk&(+7a6IgwEz8F}UeM!$3hfmMdm+QKI z&dL(#%+DU{V@~D%xKFWN61{urS<%lpsj)M>de5Hw*#+##es+al3bJmoYiJ{{JaLu= z`}bh~%`dWU$dvocm45msw`IVdFJo(O%sey8g3V8x+&9aCTfY}}N2WY~yI3CVum*KK zpcNo<7AMwD^V3foZA)Hx@?8n6pK;7v+r(QL?7vY`Zc7cTAk#-(>{bQ)+l;BD zS3P0;?oyZjtp-+ZAJUU1urbx;Z1)0N!$3&Ad3l!eca7f2v5M79x>`uii(hEI!ZKrKVmB3q-D z87sbP)-v-#rW_1V95!5bq}pXYnM!d89mpCMp%p5fHGKD@t|9Emv;nYQX8+W@TGe6!~p zf|ZA|=fjX$o8&O>a?dwHu8scL^NqpUtNUMjz6m;Gc?Px5p6C5(Q)JrQi;>S({vQfw epDoYLaP*I$_y57r-sf&JPKjqs-Pf!z4EjGGG=W|K diff --git a/backend/src/glass/shaders/liquid_glass.fragment.hlsl b/backend/src/glass/shaders/liquid_glass.fragment.hlsl index 94140fc4a..559448a15 100644 --- a/backend/src/glass/shaders/liquid_glass.fragment.hlsl +++ b/backend/src/glass/shaders/liquid_glass.fragment.hlsl @@ -95,7 +95,7 @@ float4 main(PSInput input) : SV_Target0 float4 g3 = uData[3]; float4 g4 = uData[4]; float4 g5 = uData[5]; // dither; the lens: its band's share of the shape, its bend, its shade - float4 g6 = uData[6]; // opaque window; the lens: dispersion, a drop's diagonal light (0: a window's hairline), the band's cap + float4 g6 = uData[6]; // opaque window (+1) and the light turned to the top right (+2); the lens: dispersion, a drop's diagonal light (0: a window's hairline), the band's cap // Apple's lens (`glass_look.inApp`): the picture pulled inward from a band near the rim, // magnified and, past a bend of 1, folded there. Off, the earlier glass: pulled from outside. bool inward = g5.y > 0.0; @@ -125,13 +125,21 @@ float4 main(PSInput input) : SV_Target0 // is one more shape: a shape near the outline runs into it, a neck that joins it to the edge. // Its outline cuts the glass's coverage, so none of it crosses; the rim and the bend come from // the joined shape, so there is no rim along the cut, as if the glass carried on past it. + // Flagged 3, cut (`LiquidField.Edge.cut`): not joined, and the cut is an edge of the glass's + // own, its rim and bend along it. float inPlace = -1e9; + float2 placeOut = float2(0.0, 0.0); float4 e2 = uData[G + 3 * (MAX_SHAPES - 1) + 2]; + bool cutEdge = e2.w > 2.5; if (e2.w > 1.5) { float4 e0 = uData[G + 3 * (MAX_SHAPES - 1)]; float4 e1 = uData[G + 3 * (MAX_SHAPES - 1) + 1]; float2 ge; inPlace = sdRoundBox(p - e0.xy, e0.zw, min(e1, (float4)min(e0.z, e0.w)), ge); + placeOut = ge; + } + if (e2.w > 1.5 && !cutEdge) { + float2 ge = placeOut; float d = EDGE_GAP - inPlace; float hs = h1; if (d < d1) { d2 = d1; m2 = m1; h2 = h1; d1 = d; m1 = e2; h1 = hs; } else if (d < d2) { d2 = d; m2 = e2; h2 = hs; } @@ -142,12 +150,16 @@ float4 main(PSInput input) : SV_Target0 float wm; float wf; float D = smin(d1, d2, k, wm); float F = smin(f1, f2, k, wf); + // Cut: the glass's outline, and so its rim line, runs along the place's where that is nearer. + if (cutEdge) D = max(D, inPlace); float cov = clamp(0.5 - max(D, inPlace), 0.0, 1.0); if (cov <= 0.0) discard; if (input.color.r < 0.5) return float4(0.0, 0.0, 0.0, cov); float4 mat = lerp(m1, m2, wm); float2 outv = lerp(o1, o2, wf); + // Cut: where the place's outline is nearer than the shapes' own, the glass's edge is the cut. + if (cutEdge && inPlace > F) { F = inPlace; outv = placeOut; } float soft = max(0.0, -F); float steepR = exp(-soft / g1.z); float steepL = exp(-soft / g1.w); @@ -204,7 +216,7 @@ float4 main(PSInput input) : SV_Target0 // the tint, in place of what is behind it, as see-through as that colour is, so the desktop's // material shows through it as it does through the window round a card. bool window = g4.x > 1.5; - float under = window ? g3.a : (uData[6].x > 0.5 ? 1.0 : frost.a); + float under = window ? g3.a : (fmod(uData[6].x, 2.0) > 0.5 ? 1.0 : frost.a); frost = float4(frost.rgb / max(frost.a, 1e-4), 1.0); sharp = float4(sharp.rgb / max(sharp.a, 1e-4), 1.0); @@ -231,7 +243,11 @@ float4 main(PSInput input) : SV_Target0 c += frost * (1.0 - c.a); } // The way the edge faces, against a light from the top left. - float facing = dot(outv, float2(-0.70710678, -0.70710678)); + // From the top left, or turned a quarter to the top right (`LiquidField.light_across`, +2 on the + // opaque window's flag): on every look, a window's hairline as much as a drop's diagonal. + float2 lightDir = g6.x > 1.5 ? float2(0.70710678, -0.70710678) : float2(-0.70710678, -0.70710678); + float dg = g6.z; + float facing = dot(outv, lightDir); float toward = max(facing, 0.0); float away = max(-facing, 0.0); // Apple's lens has two edges, measured beside the OS's (`g6.z`, `glass_look.InApp.diagonal`): @@ -245,15 +261,15 @@ float4 main(PSInput input) : SV_Target0 // pixels (the rim line's width is 0.8 of one, `LiquidField.rim_line_width`). float px = g2.z * 1.25; float af = abs(facing); - bool diagonal = inward && g6.z > 0.0; - float edge_dark = diagonal ? g6.z * (1.0 - smoothstep(0.2, 0.7, af)) * exp(-max(0.0, -D) / (2.0 * px)) + bool diagonal = inward && dg > 0.0; + float edge_dark = diagonal ? dg * (1.0 - smoothstep(0.2, 0.7, af)) * exp(-max(0.0, -D) / (2.0 * px)) : 0.85 * (1.0 - smoothstep(0.0, 0.5 * px, -D)); // As much as the edge has formed (`lens`), as its bend, clearing and light are: glass that is // barely a lens yet — a drop sunk into its place's edge — draws no dark line round itself. float shade = inward ? max(g5.w * t * t, edge_dark) * min(lens, 1.0) : 0.0; float4 o = float4(c.rgb * (under * (1.0 - shade)), under * (1.0 - shade) + shade); // The lift and the rim's light (`drawLift`), brightest facing the top left. - float spec = inward ? (diagonal ? g6.z * smoothstep(0.75, 1.0, af) : 0.25) + float spec = inward ? (diagonal ? dg * smoothstep(0.75, 1.0, af) : 0.25) : 0.45 * (toward * sqrt(toward) + 0.75 * away * sqrt(away)) + 0.06; float line_ = diagonal ? exp(-max(0.0, -D) / px) : inward ? smoothstep(0.25 * px, 0.5 * px, -D) * (1.0 - smoothstep(1.2 * px, 1.6 * px, -D)) diff --git a/core/dialogs.zig b/core/dialogs.zig index feecd880a..98973c9b9 100644 --- a/core/dialogs.zig +++ b/core/dialogs.zig @@ -321,6 +321,9 @@ pub const BubblesOptions = struct { /// place of what is behind the glass, as see-through as the window is, so over a card the glass /// reads as the window's edge reaching into it. Over `tint`. window: bool = false, + /// A drop's rim lit from the top right (`LiquidField.light_across`): for glass whose edges that + /// show face across the usual light. + light_across: bool = false, }; /// A rounded box's smallest corner, in points, for its glass to bend along its own outline as a @@ -344,7 +347,7 @@ pub fn glassBubbles(id: dvui.Id, bubbles: []const Bubble, scale: f32, opts: Bubb }; if (widgets.LiquidField.ready()) { if (widgets.liquidFrost()) |frost| { - var field: widgets.LiquidField = .{ .merge_px = opts.merge * scale, .scale = scale, .edge = opts.edge, .solid = opts.solid }; + var field: widgets.LiquidField = .{ .merge_px = opts.merge * scale, .scale = scale, .edge = opts.edge, .solid = opts.solid, .light_across = opts.light_across }; for (bubbles) |b| { if (b.rect.w < 1 or b.rect.h < 1) continue; // Its edge bends along its own outline, as a drop's does, with a drop's rim of light: From 5a57b15247e56e6578f8daa3dfa272b844651fda Mon Sep 17 00:00:00 2001 From: foxnne Date: Mon, 19 Oct 2026 09:57:34 -0500 Subject: [PATCH 7/7] app: the vial takes only its glass, its liquid settles on slow frames and knocks on undo --- app/HistoryTransport.zig | 14 +-- app/Liquid.zig | 181 +++++++++++---------------------------- app/dome.zig | 23 ++--- src/editor/Editor.zig | 2 +- 4 files changed, 65 insertions(+), 155 deletions(-) diff --git a/app/HistoryTransport.zig b/app/HistoryTransport.zig index 000724303..225c468ed 100644 --- a/app/HistoryTransport.zig +++ b/app/HistoryTransport.zig @@ -234,7 +234,7 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica var reach = area; if (opts.edge) |y| reach.h = @max(reach.h, y - reach.y); const on_grip = opts.history and !core.dialogs.modalOverCurrent() and if (opts.corner) |c| - c.box(dvui.windowRectScale().s).contains(mouse) + c.hit(dvui.windowRectScale().s).contains(mouse) else reach.contains(mouse) and if (gripRect(area, dome.radius, opts.edge)) |g| dvui.windowRectScale().rectToPhysical(g.outsetAll(dome.reach)).contains(mouse) else false; self.tab = opts.history; @@ -267,15 +267,13 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica if (self.grip_steps) |was| if (opts.steps > was) { self.stepped_at = now; self.slosh.knock(now, 0.35); - // Into a vial, the new step splashes the surface, as a drop landing would. - self.liquid.splashIn(); + // Into a vial, the new step splashes the surface, as a drop landing would. Only into + // one: a liquid nothing steps would hold its splashes for when a vial came. + if (opts.corner != null) self.liquid.splashIn(); }; self.grip_steps = opts.steps; self.liquid.setLevel(fill); - if (opts.corner) |c| { - const s = dvui.windowRectScale().s; - self.liquid.step(now, c.point(), c.radiusPx(s), c.liquidDepth(s, self.liquid.shownLevel())); - } + if (opts.corner) |c| self.liquid.step(now, c.point()); const glow: f32 = if (self.stepped_at) |t| (if (now - t < step_glow_ns) 1 - @as(f32, @floatCast(@as(f64, @floatFromInt(now - t)) / @as(f64, @floatFromInt(step_glow_ns)))) else 0) else 0; if (glow == 0) self.stepped_at = null; const in_use = on_grip or self.stepped_at != null or !self.slosh.settled(now); @@ -463,6 +461,8 @@ pub fn draw(self: *HistoryTransport, doc: sdk.DocHandle, area: dvui.Rect.Physica if (self.slosh_at) |was| if (was != at) { const by = (@as(f32, @floatFromInt(at)) - @as(f32, @floatFromInt(was))) / @as(f32, @floatFromInt(@max(1, n))); self.slosh.knock(now, dome_rules.knockFor(by)); + // The vial's liquid too, a scrub's step tipping it as the dome's swing does. + if (opts.corner != null) self.liquid.knock(dome_rules.knockFor(by)); }; self.slosh_at = at; const tilt = self.slosh.tilt(now); diff --git a/app/Liquid.zig b/app/Liquid.zig index ab9f59143..70c4a9b61 100644 --- a/app/Liquid.zig +++ b/app/Liquid.zig @@ -1,18 +1,18 @@ //! The liquid in a history vial (`dome.paintCorner`): a surface that sloshes as the vial moves and -//! takes in drops that fall into it, as a liquid does. +//! splashes as steps come, as a liquid does. //! //! The surface is a row of columns tied to their neighbours by springs (the usual small model of //! water waves): pulled toward their neighbours' heights, toward rest, and losing energy as they //! go, with what the row holds kept the same, so a wave moves liquid about rather than making it. //! The vial moving tips it: its acceleration across the screen, from where the caller says it is //! each frame, pushes one side down and the other up, as a cup's liquid does when the cup is -//! moved. A new step drops a drop in from the top (`drip`): it falls, lands with a splash at the -//! column it hits, and only then is the level up by it. +//! moved. A new step splashes it (`splashIn`), an undo or redo knocks it (`knock`), and its level +//! eases to the history's fill (`setLevel`). //! -//! **It lets the app sleep.** `settled` says when nothing moves — the surface flat, no drop in the -//! air, the level where it is going — and the caller asks for frames only while it is not. A long -//! gap between frames (the app slept, and the vial moved in the frame that woke it) is a new start, -//! not an acceleration. +//! **It lets the app sleep.** `settled` says when nothing moves — the surface flat and still, the +//! level where it is going — and the caller asks for frames only while it is not. A long gap +//! between frames (the app slept, or its frames come slower than the liquid can follow) starts it +//! afresh, still, at its level: frames that never come quickly enough never keep it moving. //! //! Units are physical pixels and seconds; std-only, so it tests without a window //! (`build/app.zig`). @@ -21,34 +21,17 @@ const std = @import("std"); const Liquid = @This(); pub const columns = 16; -/// The most drops in the air at once; more wait their turn. -pub const max_drops = 4; - -pub const Drop = struct { - /// Across the vessel, 0 its left to 1 its right. - x: f32, - /// Down from the vessel's top, pixels. - y: f32, - vy: f32, -}; /// Each column's height over the level, pixels (up positive), and its speed. h: [columns]f32 = @splat(0), v: [columns]f32 = @splat(0), -drops: [max_drops]Drop = undefined, -drop_n: usize = 0, -/// Drops asked for and not yet falling (`drip` with the air full). -waiting: usize = 0, -/// The level, 0 to 1, as it is and where it is going (`setLevel`); a drop in the air or waiting -/// holds back its share until it lands. +/// The level, 0 to 1, as it is and where it is going (`setLevel`). level: f32 = 0, target: f32 = 0, -/// Each drop's share of the level, held back until it lands. -per_drop: f32 = 0, last_ns: i128 = 0, last_pos: [2]f32 = .{ 0, 0 }, last_vel: [2]f32 = .{ 0, 0 }, -/// Which drop comes next, for where it falls. +/// Which splash comes next, for where it lands. count: u32 = 0, /// How strongly a column is pulled toward its neighbours (wave speed), toward rest, and how fast @@ -58,40 +41,23 @@ const stiffness: f32 = 30; const damping: f32 = 2.6; /// Pixels of tip per pixel-per-second² of acceleration across, at the vessel's edges. const tip_gain: f32 = 0.00045; -/// Gravity on a drop, pixels per second². -const gravity: f32 = 2600; -/// How hard a landing drop pushes the column it hits down, pixels per second. +/// How hard a splash pushes the column it lands on down, pixels per second. const splash: f32 = 90; /// How fast the level comes to where it is going, per second. const level_rate: f32 = 6; -/// A frame gap past this is the app waking, not the vial moving. +/// A frame gap past this is a new start, not motion to follow. const max_gap_ns: i128 = 100 * std.time.ns_per_ms; -/// Steps the simulation takes per second at most. +/// The longest step the simulation takes, seconds. const step_s: f32 = 1.0 / 240.0; -/// Where the level is going, 0 to 1: the history's fill. A rise asked for by `drip` waits for its -/// drop to land. +/// Where the level is going, 0 to 1: the history's fill. pub fn setLevel(self: *Liquid, fill: f32) void { self.target = std.math.clamp(fill, 0, 1); if (self.last_ns == 0) self.level = self.target; } -/// A new step: a drop falls in from the top, and the level is up by `share` (0 to 1) once it lands. -pub fn drip(self: *Liquid, share: f32) void { - self.per_drop = share; - if (self.drop_n < max_drops) self.launch() else self.waiting += 1; -} - -fn launch(self: *Liquid) void { - // Somewhere across the middle of the vessel, a little differently each time. - const spread = [_]f32{ 0.5, 0.38, 0.62, 0.45, 0.55, 0.32 }; - self.drops[self.drop_n] = .{ .x = spread[self.count % spread.len], .y = 0, .vy = 0 }; - self.drop_n += 1; - self.count +%= 1; -} - -/// A new step without a drop: the surface splashes where one would have landed, somewhere across -/// the middle, a little differently each time, and the level comes up by itself (`setLevel`). +/// A new step: the surface splashes where a drop would land, somewhere across the middle, a little +/// differently each time. pub fn splashIn(self: *Liquid) void { const spread = [_]f32{ 0.5, 0.38, 0.62, 0.45, 0.55, 0.32 }; const c: usize = @intFromFloat(@round(spread[self.count % spread.len] * (columns - 1))); @@ -101,22 +67,23 @@ pub fn splashIn(self: *Liquid) void { if (c < columns - 1) self.v[c + 1] -= splash * 0.4; } -/// The level the surface is drawn at: where it is, short of what the drops still in the air (or -/// waiting) will add. -pub fn shownLevel(self: *const Liquid) f32 { - const pending = @as(f32, @floatFromInt(self.drop_n + self.waiting)) * self.per_drop; - return @max(0, @min(self.level, self.target - pending)); +/// A knock, -1 to 1: the surface tipped as by a sideways push (an undo, a redo). +pub fn knock(self: *Liquid, amount: f32) void { + for (&self.v, 0..) |*v, i| v.* += amount * 60 * across(i); } -/// Advance to `now_ns`, the vial at `pos` (pixels, any fixed point of it on screen), its vessel -/// `width` across and `depth` from its top to the liquid's level: tip the surface by how the -/// vial accelerated, run the waves, and let the drops fall. -pub fn step(self: *Liquid, now_ns: i128, pos: [2]f32, width: f32, depth: f32) void { +/// Advance to `now_ns`, the vial at `pos` (pixels, any fixed point of it on screen): tip the surface +/// by how the vial accelerated, run the waves, and ease the level. +pub fn step(self: *Liquid, now_ns: i128, pos: [2]f32) void { defer { self.last_ns = now_ns; self.last_pos = pos; } if (self.last_ns == 0 or now_ns - self.last_ns > max_gap_ns or now_ns <= self.last_ns) { + // A new start: still, at its level. + self.h = @splat(0); + self.v = @splat(0); + self.level = self.target; self.last_vel = .{ 0, 0 }; return; } @@ -133,16 +100,8 @@ pub fn step(self: *Liquid, now_ns: i128, pos: [2]f32, width: f32, depth: f32) vo const h = @min(left, step_s); left -= h; self.wave(h); - self.fall(h, width, depth); } - // The level eases to where it is going. - self.level += (self.shownTarget() - self.level) * @min(1, level_rate * dt); -} - -/// What the level eases to: the target, short of the drops still to land. -fn shownTarget(self: *const Liquid) f32 { - const pending = @as(f32, @floatFromInt(self.drop_n + self.waiting)) * self.per_drop; - return @max(0, self.target - pending); + self.level += (self.target - self.level) * @min(1, level_rate * dt); } /// Column `i` across the vessel, -1 at its left to 1 at its right. @@ -169,54 +128,20 @@ fn wave(self: *Liquid, dt: f32) void { for (&self.h) |*h| h.* -= mean; } -fn fall(self: *Liquid, dt: f32, width: f32, depth: f32) void { - _ = width; - var i: usize = 0; - while (i < self.drop_n) { - const d = &self.drops[i]; - d.vy += gravity * dt; - d.y += d.vy * dt; - const c: usize = @intFromFloat(std.math.clamp(@round(d.x * (columns - 1)), 0, columns - 1)); - // Near the surface, the surface reaches up for it: the two join, not a ball hitting a floor. - const gap = depth - self.h[c] - d.y; - if (gap < 6) self.v[c] += (6 - gap) * 40 * dt; - if (gap <= 0) { - // Landed: a splash where it hit, and its share in the level. - self.v[c] -= splash; - if (c > 0) self.v[c - 1] -= splash * 0.4; - if (c < columns - 1) self.v[c + 1] -= splash * 0.4; - self.drops[i] = self.drops[self.drop_n - 1]; - self.drop_n -= 1; - if (self.waiting > 0) { - self.waiting -= 1; - self.launch(); - } - continue; - } - i += 1; - } -} - -/// Nothing moves: the surface flat and still, no drop in the air or waiting, the level where it is -/// going. The caller asks for no frames while this holds. +/// Nothing moves: the surface flat and still, the level where it is going. The caller asks for no +/// frames while this holds. pub fn settled(self: *const Liquid) bool { - if (self.drop_n > 0 or self.waiting > 0) return false; if (@abs(self.level - self.target) > 0.002) return false; for (self.h, self.v) |h, v| if (@abs(h) > 0.05 or @abs(v) > 0.5) return false; return true; } -/// A knock, -1 to 1: the surface tipped as by a sideways push (an undo, a redo). -pub fn knock(self: *Liquid, amount: f32) void { - for (&self.v, 0..) |*v, i| v.* += amount * 60 * across(i); -} - test "a still vial settles and stays settled" { var l: Liquid = .{}; l.setLevel(0.5); var t: i128 = 1; for (0..200) |_| { - l.step(t, .{ 100, 100 }, 40, 30); + l.step(t, .{ 100, 100 }); t += 16 * std.time.ns_per_ms; } try std.testing.expect(l.settled()); @@ -228,52 +153,30 @@ test "moving the vial tips the surface, and it settles again" { var t: i128 = 1; var x: f32 = 100; for (0..10) |_| { - l.step(t, .{ x, 100 }, 40, 30); + l.step(t, .{ x, 100 }); t += 16 * std.time.ns_per_ms; x += 20; } try std.testing.expect(!l.settled()); for (0..600) |_| { - l.step(t, .{ x, 100 }, 40, 30); + l.step(t, .{ x, 100 }); t += 16 * std.time.ns_per_ms; } try std.testing.expect(l.settled()); } -test "a drop holds back its share until it lands, and the volume stays the same" { - var l: Liquid = .{}; - l.setLevel(0.2); - var t: i128 = 1; - l.step(t, .{ 0, 0 }, 40, 30); - l.setLevel(0.3); - l.drip(0.1); - try std.testing.expect(l.shownLevel() <= 0.2 + 1e-4); - var landed = false; - for (0..400) |_| { - t += 16 * std.time.ns_per_ms; - l.step(t, .{ 0, 0 }, 40, 30); - if (l.drop_n == 0) landed = true; - var sum: f32 = 0; - for (l.h) |h| sum += h; - try std.testing.expectApproxEqAbs(@as(f32, 0), sum, 0.01); - } - try std.testing.expect(landed); - try std.testing.expect(l.settled()); - try std.testing.expectApproxEqAbs(@as(f32, 0.3), l.level, 0.01); -} - test "a splash ripples the surface, keeps its volume, and settles" { var l: Liquid = .{}; l.setLevel(0.4); var t: i128 = 1; - l.step(t, .{ 0, 0 }, 40, 30); + l.step(t, .{ 0, 0 }); l.splashIn(); t += 16 * std.time.ns_per_ms; - l.step(t, .{ 0, 0 }, 40, 30); + l.step(t, .{ 0, 0 }); try std.testing.expect(!l.settled()); for (0..600) |_| { t += 16 * std.time.ns_per_ms; - l.step(t, .{ 0, 0 }, 40, 30); + l.step(t, .{ 0, 0 }); var sum: f32 = 0; for (l.h) |h| sum += h; try std.testing.expectApproxEqAbs(@as(f32, 0), sum, 0.01); @@ -285,8 +188,20 @@ test "a gap between frames is a new start, not a jolt" { var l: Liquid = .{}; l.setLevel(0.5); var t: i128 = 1; - l.step(t, .{ 0, 0 }, 40, 30); + l.step(t, .{ 0, 0 }); t += 5 * std.time.ns_per_s; - l.step(t, .{ 500, 0 }, 40, 30); + l.step(t, .{ 500, 0 }); + try std.testing.expect(l.settled()); +} + +test "frames always too slow to follow still settle, so the app can sleep" { + var l: Liquid = .{}; + l.setLevel(0.2); + var t: i128 = 1; + l.step(t, .{ 0, 0 }); + l.splashIn(); + l.setLevel(0.6); + t += 200 * std.time.ns_per_ms; + l.step(t, .{ 0, 0 }); try std.testing.expect(l.settled()); } diff --git a/app/dome.zig b/app/dome.zig index 8723d7aab..bbb8daf49 100644 --- a/app/dome.zig +++ b/app/dome.zig @@ -353,11 +353,13 @@ pub const Corner = struct { return .{ .rect = self.card, .radii = self.radii, .cut = true }; } - /// How far down from the vessel's top the liquid's level is at `level` (0 to 1), physical: - /// where a drop falling from the top meets it (`Liquid.step`). - pub fn liquidDepth(self: Corner, s: f32, level: f32) f32 { - const deep = self.radiusPx(s) - 2 * liquid_inset * s; - return deep * (1 - rules.fill_top * level); + /// Where the pointer is on it: the square from the corner that lies inside the quarter drawn + /// (its far corner on the circle), so nothing past the glass — the document's last columns, + /// a scroll bar's end — is taken for the grip's. + pub fn hit(self: Corner, s: f32) dvui.Rect.Physical { + const d = self.radiusPx(s) * std.math.sqrt1_2; + const p = self.point(); + return .{ .x = p[0] - d, .y = p[1] - d, .w = d, .h = d }; } /// Where the icon is centred: on the diagonal, in the middle of what of the quarter shows. @@ -374,7 +376,7 @@ pub const Corner = struct { pub fn drawCorner(c: Corner, id_extra: usize, look: Look, comptime anchor_fmt: []const u8, anchor_args: anytype) bool { const wrs = dvui.windowRectScale(); const s = wrs.s; - const pr = c.box(s); + const pr = c.hit(s); var float: dvui.FloatingWidget = undefined; float.init(@src(), .{}, .{ .rect = wrs.rectFromPhysical(pr), .id_extra = id_extra }); defer float.deinit(); @@ -443,8 +445,6 @@ pub fn paintCorner(id: dvui.Id, c: Corner, t: f32, look: Look) void { /// How far in from the glass and the card's edge the liquid keeps, points. const liquid_inset: f32 = 2.5; -/// A drop's radius, points. -const drop_r: f32 = 2.4; /// The liquid `liq` in `vessel` (physical, convex), its top `top` and its bottom `bottom` (y): the /// surface drawn column to column in strips, each cut by the vessel, with a lighter line along @@ -458,7 +458,7 @@ fn pourLiquid(vessel: []const rules.Pt, liq: *const Liquid, top: f32, bottom: f3 minx = @min(minx, q[0]); maxx = @max(maxx, q[0]); } - const level_y = std.math.lerp(bottom, top, rules.fill_top * liq.shownLevel()); + const level_y = std.math.lerp(bottom, top, rules.fill_top * liq.level); const dx = (maxx - minx) / @as(f32, Liquid.columns - 1); const shine = color.lerp(.white, 0.35); var out: [256]geometry.Pt = undefined; @@ -474,11 +474,6 @@ fn pourLiquid(vessel: []const rules.Pt, liq: *const Liquid, top: f32, bottom: f3 const line = [_]geometry.Pt{ .{ x0, y0 - 0.5 * s }, .{ x1, y1 - 0.5 * s }, .{ x1, y1 + 0.9 * s }, .{ x0, y0 + 0.9 * s } }; pour(geometry.cut(&line, vessel, &out, &scratch), shine); } - var circle: [48]geometry.Pt = undefined; - for (liq.drops[0..liq.drop_n]) |d| { - const c: geometry.Shape = .circle(.{ minx + d.x * (maxx - minx), top + d.y }, drop_r * s); - pour(geometry.cut(c.outline(12, &circle), vessel, &out, &scratch), color); - } } /// The stream poured from `lip` (the pool's surface) to `end` (the line's knob), physical, from diff --git a/src/editor/Editor.zig b/src/editor/Editor.zig index a2d7ff2ad..0fd65b242 100644 --- a/src/editor/Editor.zig +++ b/src/editor/Editor.zig @@ -3164,7 +3164,7 @@ fn drawDocSurface(ctx: ?*anyopaque) anyerror!dvui.App.Result { // And above the history's vial in the card's corner, which the feed's newest would sit on. if (claim_pill == null and ds.has_history) if (card_corner) |c| { const s = dvui.windowRectScale().s; - area.h = @min(area.h, c.point()[1] - c.radiusPx(s) - area.y + 6 * s); + area.h = @max(0, @min(area.h, c.point()[1] - c.radiusPx(s) - area.y + 6 * s)); }; if (ds.feed.draw(area, @truncate(ds.doc_id), now)) |pressed| editor.actFromFeed(doc, ds, pressed); }