Add nerd glasses and the ...because he was bored... subtitle to the loading screen

This commit is contained in:
enne2
2026-10-11 02:08:20 +02:00
parent db2b57775c
commit e15deef84f
7 changed files with 133 additions and 53 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 171 KiB

+19 -13
View File
@@ -9,10 +9,11 @@ The stock Godot boot splash (logo plus "GODOT Game engine") is replaced by the
## Asset ## Asset
`assets/branding/boot_splash.png`, 1024x1185, palette PNG with alpha `assets/branding/boot_splash.png`, 1024x1315, palette PNG with alpha
(256 colors, 95 alpha levels), about 159 KB. Subject: cartoon zombie head and bust, (256 colors, 95 alpha levels), about 171 KB. Subject: cartoon zombie head and bust,
bald scalp with a sparse hair fringe on the sides, unsettling grin, thumbs up, with bald scalp with a sparse hair fringe on the sides, thick nerd glasses, unsettling grin,
"MatGames" below (one word, no space) in a phosphor-terminal typeface. thumbs up, with "MatGames" (one word, no space) in a phosphor-terminal typeface and
the subtitle `"...because he was bored..."` beneath it.
Provenance is recorded in `tools/branding/manifest.json`: Provenance is recorded in `tools/branding/manifest.json`:
@@ -20,8 +21,10 @@ Provenance is recorded in `tools/branding/manifest.json`:
`gemini-3.1-flash-image`) on 2026-10-11 from no third-party source material; the `gemini-3.1-flash-image`) on 2026-10-11 from no third-party source material; the
provider terms do not claim ownership of the output. A second AI pass (`agy_edit`) provider terms do not claim ownership of the output. A second AI pass (`agy_edit`)
kept the face, grin, thumbs-up hand, shirt, pose and flat background and changed kept the face, grin, thumbs-up hand, shirt, pose and flat background and changed
only the hair to a bald scalp with a side fringe; both files and their SHA256 are only the hair to a bald scalp with a side fringe, then a third pass added thick nerd
kept in `tools/branding/raw/` and the edit is recorded under `mascot_derived_from`. glasses while keeping the face, grin, hand, shirt, pose and flat background. All
files and their SHA256 are kept in `tools/branding/raw/` and the chain is recorded
under `mascot_provenance`.
Note that an image generator does not emit real alpha, so the flat magenta Note that an image generator does not emit real alpha, so the flat magenta
background is keyed out locally; background is keyed out locally;
- lettering: drawn by script, not by the generator, because generated text is - lettering: drawn by script, not by the generator, because generated text is
@@ -45,8 +48,11 @@ packed into the export (`tools/*` is already excluded by the export preset).
pink tongue or the blue shirt because their red and blue are not both above green; pink tongue or the blue shirt because their red and blue are not both above green;
4. crops to the subject, applies a 50 px fade at the bottom to soften the frame cut 4. crops to the subject, applies a 50 px fade at the bottom to soften the frame cut
of the generated bust, and scales to 1024 px wide with 40 px side padding; of the generated bust, and scales to 1024 px wide with 40 px side padding;
5. draws the title with VT323 in amber over a blurred glow layer. The title defaults 5. draws the title with VT323 in amber over a blurred glow layer, plus an optional
to the one-word form `MatGames`; pass `--title` to change it; second line. Defaults are the one-word `MatGames` and the subtitle
`"...because he was bored..."`; `--title`, `--subtitle` (empty to omit) and
`--subtitle-height` change them. A 30 px `--bottom-padding` band is left under the
text so the shell progress bar does not cross the lettering;
6. quantizes to 256 colors (no dither) and writes the PNG plus the manifest. 6. quantizes to 256 colors (no dither) and writes the PNG plus the manifest.
```sh ```sh
@@ -71,11 +77,11 @@ ratio; the exported shell confirms the result with the classes
## Verification ## Verification
- Exported `index.png` has the same 1024x1185 dimensions as the master. It is - Exported `index.png` has the same 1024x1315 dimensions as the master. It is
re-encoded by the exporter to 32-bit RGBA (about 496 KB), so the bytes are not re-encoded by the exporter to 32-bit RGBA (about 528 KB), so the bytes are not
identical; composited on black the two images differ by at most 6.4/255 on identical; composited on black the two images differ by at most 5.8/255 on
0.13% of pixels, all with alpha below 20, i.e. invisible. The release keeps the 0.11% of pixels, all with alpha below 20, i.e. invisible. The release keeps the
159 KB master in the repository and ships a gzip sidecar. 171 KB master in the repository and ships a gzip sidecar.
- The exported shell contains no "Game engine" text and the splash image has an - The exported shell contains no "Game engine" text and the splash image has an
empty `alt`. empty `alt`.
- `html/export_icon=false`, so no favicon is exported: the browser tab still shows - `html/export_icon=false`, so no favicon is exported: the browser tab still shows
+14
View File
@@ -1,5 +1,19 @@
# Web build and compressed deployment # Web build and compressed deployment
## Nerd glasses and subtitle release: f77b7ba7b89364dd
The mascot now wears thick nerd glasses (third AI edit pass) and carries the subtitle
`"...because he was bored..."` under the title, see `docs/BRANDING.md`. A 30 px band
was left under the lettering because the shell progress bar crossed the subtitle in the
first attempt, deployed minutes earlier as cd86c85387c1c9f3 and superseded by this one.
PCK 4,373,568 bytes; gzip payload total 12,821,256 bytes. Backup
`index-before-nerd-bar.html` (the earlier `index-before-nerd.html` holds the
cd86c85387c1c9f3 shell).
Obsolete releases after this deploy: 097e6c64b42640ad and 1194f239fcbd91d6, both
deletable under the retention policy once approved.
## Bald mascot release: 1194f239fcbd91d6 ## Bald mascot release: 1194f239fcbd91d6
Title is now the one-word `MatGames` and the mascot has a bald scalp with a side Title is now the one-word `MatGames` and the mascot has a bald scalp with a side
+21 -12
View File
@@ -3,43 +3,52 @@
"file": "assets/branding/boot_splash.png", "file": "assets/branding/boot_splash.png",
"size": [ "size": [
1024, 1024,
1185 1315
], ],
"mode": "P", "mode": "P",
"palette_colors": 256, "palette_colors": 256,
"sha256": "4b4bab87b5038915d5a7493c6ee134cd654293036d093e71d210cbad6359bdb2" "sha256": "715bd5f1dd3a651b1b5f6ee0560db232ddfdad676091f07ea34696787e27fbee"
}, },
"mascot_source": { "mascot_source": {
"file": "tools/branding/raw/agy_edit_20261010235510_0.jpg", "file": "tools/branding/raw/agy_edit_20261011000309_0.jpg",
"sha256": "913184c2e2e3dd5357f7729988859c8e038e4561ba18af7cb60f12644f7f6994" "sha256": "57bc1e46eb0876c02008038981c9b01a6bf4e02da78418de7b44f4fcbd93895e"
}, },
"generator": { "generator": {
"tool": "agy_generate (Antigravity direct API)", "tool": "agy_generate (Antigravity direct API)",
"model": "gemini-3.1-flash-image", "model": "gemini-3.1-flash-image",
"date": "2026-10-11", "date": "2026-10-11",
"prompt": "single cartoon zombie head-and-bust mascot, unsettling wide grin, thumbs up, 2D cartoon style with bold outlines, flat magenta chroma-key background, no text and no logo; then edited to a bald scalp with a sparse hair fringe on the sides only", "prompt": "single cartoon zombie head-and-bust mascot, unsettling wide grin, thumbs up, 2D cartoon style with bold outlines, flat magenta chroma-key background, no text and no logo; then edited to a bald scalp with a sparse hair fringe on the sides only, then edited again to wear thick nerd glasses",
"terms_note": "generated from no third-party source material; the provider terms do not claim ownership of the output. The generator emits no real alpha channel, so the background is keyed out locally by this script." "terms_note": "generated from no third-party source material; the provider terms do not claim ownership of the output. The generator emits no real alpha channel, so the background is keyed out locally by this script."
}, },
"chroma_key": { "chroma_key": {
"background_rgb": [ "background_rgb": [
246, 244,
1, 0,
237 233
], ],
"transparent_below": 55, "transparent_below": 55,
"opaque_above": 130 "opaque_above": 130
}, },
"title": { "title": {
"text": "MatGames", "text": "MatGames",
"subtitle": "\"...because he was bored...\"",
"font": "tools/branding/fonts/VT323-Regular.ttf", "font": "tools/branding/fonts/VT323-Regular.ttf",
"license": "SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)" "license": "SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)"
}, },
"bottom_fade_px": 50, "bottom_fade_px": 50,
"mascot_derived_from": [ "mascot_provenance": [
{ {
"file": "tools/branding/raw/agy_generate_20261010234423_0.jpg", "file": "tools/branding/raw/agy_generate_20261010234423_0.jpg",
"sha256": "5929495ff606fbcdd74f52a37304f3b569315bcd1fc0fef812b5fd7689dc1396", "sha256": "5929495ff606fbcdd74f52a37304f3b569315bcd1fc0fef812b5fd7689dc1396"
"edit": "AI edit (agy_edit): keep face, grin, thumbs up, shirt, pose and flat background; change the hair to a bald scalp with a sparse fringe on the sides only" },
{
"file": "tools/branding/raw/agy_edit_20261010235510_0.jpg",
"sha256": "913184c2e2e3dd5357f7729988859c8e038e4561ba18af7cb60f12644f7f6994"
},
{
"file": "tools/branding/raw/agy_edit_20261011000309_0.jpg",
"sha256": "57bc1e46eb0876c02008038981c9b01a6bf4e02da78418de7b44f4fcbd93895e"
} }
] ],
"bottom_padding_px": 30
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b5pj4ipovpbo"
path="res://.godot/imported/agy_edit_20261011000309_0.jpg-c4ed4776ca2aca40794a7d66a2e8befc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://tools/branding/raw/agy_edit_20261011000309_0.jpg"
dest_files=["res://.godot/imported/agy_edit_20261011000309_0.jpg-c4ed4776ca2aca40794a7d66a2e8befc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
+37 -26
View File
@@ -14,14 +14,19 @@ from PIL import Image, ImageDraw, ImageFilter, ImageFont
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('raw', type=pathlib.Path, help='raw generated image (magenta background)') parser.add_argument('raw', type=pathlib.Path, help='raw generated image (magenta background)')
parser.add_argument('--title', default='MatGames') parser.add_argument('--title', default='MatGames')
parser.add_argument('--subtitle', default='"...because he was bored..."',
help='second line under the title; empty string for none')
parser.add_argument('--subtitle-height', type=int, default=100)
parser.add_argument('--bottom-padding', type=int, default=30,
help='empty band under the text: the shell progress bar sits there')
parser.add_argument('--canvas', type=int, default=1024) parser.add_argument('--canvas', type=int, default=1024)
parser.add_argument('--title-height', type=int, default=190) parser.add_argument('--title-height', type=int, default=190)
parser.add_argument('--bottom-fade', type=int, default=50, parser.add_argument('--bottom-fade', type=int, default=50,
help='soften the generator frame cut at the bottom of the bust') help='soften the generator frame cut at the bottom of the bust')
parser.add_argument('--colors', type=int, default=256, parser.add_argument('--colors', type=int, default=256,
help='palette colors for the shipped PNG; 0 keeps 32-bit RGBA') help='palette colors for the shipped PNG; 0 keeps 32-bit RGBA')
parser.add_argument('--generated-from', type=pathlib.Path, default=None, parser.add_argument('--provenance', action='append', type=pathlib.Path, default=[],
help='pre-edit generation this file was derived from, for provenance') help='earlier files of the generation/edit chain, in order; repeatable')
args = parser.parse_args() args = parser.parse_args()
args.raw = args.raw.resolve() args.raw = args.raw.resolve()
root = pathlib.Path(__file__).resolve().parents[1] root = pathlib.Path(__file__).resolve().parents[1]
@@ -70,27 +75,34 @@ if args.bottom_fade > 0:
# ---------------------------------------------------------------- composition # ---------------------------------------------------------------- composition
canvas_width = args.canvas canvas_width = args.canvas
title_area = args.title_height title_area = args.title_height + (args.subtitle_height if args.subtitle else 0)
scale = (canvas_width - 2 * 40) / mascot.width scale = (canvas_width - 2 * 40) / mascot.width
mascot = mascot.resize((round(mascot.width * scale), round(mascot.height * scale)), Image.LANCZOS) mascot = mascot.resize((round(mascot.width * scale), round(mascot.height * scale)), Image.LANCZOS)
height = mascot.height + title_area height = mascot.height + title_area + args.bottom_padding
canvas = Image.new('RGBA', (canvas_width, height), (0, 0, 0, 0)) canvas = Image.new('RGBA', (canvas_width, height), (0, 0, 0, 0))
canvas.alpha_composite(mascot, ((canvas_width - mascot.width) // 2, 0)) canvas.alpha_composite(mascot, ((canvas_width - mascot.width) // 2, 0))
font_size = int(title_area * 0.95)
font = ImageFont.truetype(str(font_path), font_size) def _draw_line(text: str, block_top: int, block_height: int, size: int,
text = args.title glow_color: tuple, text_color: tuple, blur: int) -> None:
while font.getbbox(text)[2] - font.getbbox(text)[0] > canvas_width - 60 and font_size > 8: font = ImageFont.truetype(str(font_path), size)
font_size -= 2 while font.getbbox(text)[2] - font.getbbox(text)[0] > canvas_width - 60 and size > 8:
font = ImageFont.truetype(str(font_path), font_size) size -= 2
font = ImageFont.truetype(str(font_path), size)
box = font.getbbox(text) box = font.getbbox(text)
text_width = box[2] - box[0] position = ((canvas_width - (box[2] - box[0])) // 2 - box[0],
position = ((canvas_width - text_width) // 2 - box[0], mascot.height + (title_area - box[3]) // 2 - box[1]) block_top + (block_height - box[3]) // 2 - box[1])
glow = Image.new('RGBA', canvas.size, (0, 0, 0, 0)) glow = Image.new('RGBA', canvas.size, (0, 0, 0, 0))
ImageDraw.Draw(glow).text(position, text, font=font, fill=(255, 150, 20, 190)) ImageDraw.Draw(glow).text(position, text, font=font, fill=glow_color)
glow = glow.filter(ImageFilter.GaussianBlur(9)) canvas.alpha_composite(glow.filter(ImageFilter.GaussianBlur(blur)))
canvas.alpha_composite(glow) ImageDraw.Draw(canvas).text(position, text, font=font, fill=text_color)
ImageDraw.Draw(canvas).text(position, text, font=font, fill=(255, 196, 64, 255))
_draw_line(args.title, mascot.height, args.title_height, int(args.title_height * 0.95),
(255, 150, 20, 190), (255, 196, 64, 255), 9)
if args.subtitle:
_draw_line(args.subtitle, mascot.height + args.title_height, args.subtitle_height,
int(args.subtitle_height * 0.8), (255, 150, 20, 150), (255, 176, 58, 240), 6)
out = branding / 'boot_splash.png' out = branding / 'boot_splash.png'
if args.colors > 0: if args.colors > 0:
@@ -111,23 +123,22 @@ manifest.update({
'prompt': ('single cartoon zombie head-and-bust mascot, unsettling wide grin, thumbs up, ' 'prompt': ('single cartoon zombie head-and-bust mascot, unsettling wide grin, thumbs up, '
'2D cartoon style with bold outlines, flat magenta chroma-key background, ' '2D cartoon style with bold outlines, flat magenta chroma-key background, '
'no text and no logo; then edited to a bald scalp with a sparse hair fringe ' 'no text and no logo; then edited to a bald scalp with a sparse hair fringe '
'on the sides only'), 'on the sides only, then edited again to wear thick nerd glasses'),
'terms_note': ('generated from no third-party source material; the provider terms do not claim ' 'terms_note': ('generated from no third-party source material; the provider terms do not claim '
'ownership of the output. The generator emits no real alpha channel, so the ' 'ownership of the output. The generator emits no real alpha channel, so the '
'background is keyed out locally by this script.'), 'background is keyed out locally by this script.'),
}, },
'chroma_key': {'background_rgb': list(background), 'transparent_below': T0, 'opaque_above': T1}, 'chroma_key': {'background_rgb': list(background), 'transparent_below': T0, 'opaque_above': T1},
'title': {'text': args.title, 'font': 'tools/branding/fonts/VT323-Regular.ttf', 'title': {'text': args.title, 'subtitle': args.subtitle,
'font': 'tools/branding/fonts/VT323-Regular.ttf',
'license': 'SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)'}, 'license': 'SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)'},
'bottom_fade_px': args.bottom_fade, 'bottom_fade_px': args.bottom_fade,
'bottom_padding_px': args.bottom_padding,
}) })
if args.generated_from is not None: chain = list(args.provenance) + [args.raw]
chain = manifest.get('mascot_derived_from', []) manifest['mascot_provenance'] = [
manifest['mascot_derived_from'] = chain + [{ {'file': str(path.resolve().relative_to(root)),
'file': str(args.generated_from.resolve().relative_to(root)), 'sha256': hashlib.sha256(path.read_bytes()).hexdigest()} for path in chain]
'sha256': hashlib.sha256(args.generated_from.read_bytes()).hexdigest(), manifest.pop('mascot_derived_from', None)
'edit': 'AI edit (agy_edit): keep face, grin, thumbs up, shirt, pose and flat background; '
'change the hair to a bald scalp with a sparse fringe on the sides only',
}]
(source_tools / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n') (source_tools / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n')
print(out, canvas.size, canvas.mode, 'mascot', mascot.size) print(out, canvas.size, canvas.mode, 'mascot', mascot.size)