diff --git a/renderer/README.md b/renderer/README.md index 337ccc4..3898ada 100644 --- a/renderer/README.md +++ b/renderer/README.md @@ -53,9 +53,11 @@ Authorization: Bearer change-me Renders a 6-second portrait wish: a collectible card pack from [pack-cards](https://github.com/paubineau/pack-cards) is torn open, its card spins out with a burst of sparkles, and lands showing the request's `label`, -its rank, and its stars under a polychrome rainbow foil. `rarity` (required) -picks the rank (`B` for 3★, `A` for 4★, `S` for 5★), the star count, the -card's material (rare, epic, legendary), and the colour (blue, purple, gold). +its rank, and its stars. `rarity` (required) picks the rank (`B` for 3★, `A` +for 4★, `S` for 5★), the star count, the card's material (rare, epic, +legendary), and the colour (blue, purple, gold). 4★ and 5★ cards are engraved +with pack-cards' rarity textures (contour on 4★; rings or facets on 5★, chosen +by the label) drawn in a still polychrome rainbow; 3★ cards are plain. `fps` is `24` or `30`. `width` is the long edge of the portrait frame: `640` renders 360×640 and `854` renders 480×854. Optional `seed` (integer, `0` to `2147483647`) seeds the page's randomness; when omitted the service @@ -72,8 +74,9 @@ server process in `--deterministic-mode`, steps virtual time one frame at a time, tears the pack with a scripted drag, captures each frame, and encodes them with Remotion's bundled ffmpeg. The page (`src/gacha/page/`) and the package are served from disk; the page has no network access. The first render -compiles the pack's WebGL shaders in software, which takes several seconds, so -server start-up runs one throwaway wish first. +compiles the pack's WebGL shaders in software and the first card of each +engraving rasterises its texture, which takes several seconds, so server +start-up renders a throwaway 5★ and 4★ wish first. pack-cards has no npm release, so it is installed from a GitHub tarball pinned to a commit. A moving branch URL would change the tarball's checksum and break diff --git a/renderer/src/gacha/page/page.css b/renderer/src/gacha/page/page.css index 9210b12..2a3e579 100644 --- a/renderer/src/gacha/page/page.css +++ b/renderer/src/gacha/page/page.css @@ -49,57 +49,24 @@ body { } /* - * Polychrome: a rainbow band pattern glazed over the card under the print, - * sliding diagonally so the colours shift. Only a transform animates, which - * the compositor moves without repainting the foil each frame. + * Polychrome engraving: the tooled lines (pack-cards' rarity-epic-contour, + * rarity-legendary, and rarity-legendary-facets textures) are filled with a + * fixed rainbow instead of the metallic glint, so the colours hold still. */ -.wish-polychrome { - border-radius: inherit; - inset: 0; - mix-blend-mode: screen; - opacity: var(--wish-polychrome); - overflow: hidden; - pointer-events: none; - position: absolute; -} - -.wish-polychrome::before { - animation: wish-polychrome 2.4s ease-in-out infinite alternate; - background: repeating-linear-gradient( +#stage + .recap-card.recap-collectible:is([data-effect='engraved'], [data-effect='engraved-lettering']) + .recap-effect-texture::before { + background: linear-gradient( 125deg, #ff5f9e 0%, - #ffb347 6%, - #fff275 12%, - #6dffb0 18%, - #5fd4ff 24%, - #9a7bff 30%, - #ff5f9e 36% + #ffb347 18%, + #fff275 34%, + #6dffb0 50%, + #5fd4ff 66%, + #9a7bff 82%, + #ff5f9e 100% ); - content: ''; - height: 300%; - left: -100%; - position: absolute; - top: -100%; - width: 300%; -} - -/* The band overhangs the card by a third of its size on each side, so it may slide at most that far. */ -@keyframes wish-polychrome { - to { - transform: translate(30%, 30%); - } -} - -.wish-card > :not(.wish-polychrome) { - position: relative; -} - -/* - * The spectrum foil sets --accent inline on the card to its own colour; the - * print inside keeps the rarity colour. - */ -.wish-card > * { - --accent: var(--wish-accent); + opacity: 1; } .wish-rank { diff --git a/renderer/src/gacha/page/page.js b/renderer/src/gacha/page/page.js index 00f5bee..9075c30 100644 --- a/renderer/src/gacha/page/page.js +++ b/renderer/src/gacha/page/page.js @@ -33,9 +33,6 @@ const el = (tag, className, text) => { return node; }; -/** @type {Record} */ -const polychromeStrength = {3: 0.3, 4: 0.42, 5: 0.55}; - const createFace = () => { const face = el('article', 'wish-card'); const header = el('header', 'recap-card-header'); @@ -46,10 +43,7 @@ const createFace = () => { content.append(el('span', 'wish-rank', wish.rank), title, el('p', 'wish-stars', '★'.repeat(wish.stars))); const footer = el('footer', 'recap-card-footer'); footer.append(el('span', '', `${wish.stars}★`), el('span', '', '01 / 01')); - // A rainbow sheen drifting across the whole face, stronger on rarer cards. - const sheen = el('span', 'wish-polychrome'); - sheen.style.setProperty('--wish-polychrome', String(polychromeStrength[wish.stars] ?? 0.3)); - face.append(sheen, header, content, footer); + face.append(header, content, footer); return face; }; @@ -121,15 +115,15 @@ const burstSparkles = (deck, delay, duration) => { }; /** - * Polychrome foil: every rarity gets pack-cards' rainbow holographic print, - * from a border on 3★ to the whole card on 4★ and 5★. + * Engravings carry the polychrome (page.css): 4★ cards are tooled with the + * epic contour, 5★ cards with the legendary rings or facets, picked by + * pack-cards from the card's label. 3★ cards stay plain. * * @type {import('pack-cards').AppearanceSettingsOptions['rarities']} */ -const polychrome = Object.freeze({ - rare: {foil: 'holographic', coverage: 'border', palette: 'spectrum', pattern: 'stardust'}, - epic: {foil: 'holographic', coverage: 'full', palette: 'spectrum', pattern: 'stardust'}, - legendary: {foil: 'holographic', coverage: 'full', palette: 'spectrum', pattern: 'facets'}, +const engravings = Object.freeze({ + epic: {engraving: 'contour'}, + legendary: {engraving: ['radial', 'facets']}, }); const animate = Element.prototype.animate; @@ -177,7 +171,7 @@ const view = createPackView(stage, { label: 'Gacha', artwork: wish.artwork, labels: {title: 'Gacha', swipe: '', tap: ''}, - appearance: {opening: 'animated', motion: 'interactive', glow: wish.glow, pack_zoom: true, rarities: polychrome}, + appearance: {opening: 'animated', motion: 'interactive', glow: wish.glow, pack_zoom: true, rarities: engravings}, }); view.showPack({ diff --git a/renderer/src/render/render-gacha.js b/renderer/src/render/render-gacha.js index ae3b2ca..e9de3f6 100644 --- a/renderer/src/render/render-gacha.js +++ b/renderer/src/render/render-gacha.js @@ -152,13 +152,17 @@ export const closeGachaBrowser = async () => { }; /** - * Pays the shared browser's one-time shader compilation with a throwaway - * wish, so the first real request renders as fast as later ones. + * Pays the shared browser's one-time costs with throwaway wishes, so the first + * real request renders as fast as later ones: the WebGL shader compilation, + * and the first raster of each card's engraving texture. 4★ and 5★ cards use + * different engravings, so both are warmed. * * @returns {Promise} */ export const warmGachaBrowser = async () => { - await renderGachaVideo({label: 'Warm-up', rarity: 5, fps: 24, width: 640, seed: 1}, {timeoutInMilliseconds: 120000}); + for (const rarity of /** @type {const} */ ([5, 4])) { + await renderGachaVideo({label: 'Warm-up', rarity, fps: 24, width: 640, seed: 1}, {timeoutInMilliseconds: 120000}); + } }; /**