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
`assets/branding/boot_splash.png`, 1024x1185, palette PNG with alpha
(256 colors, 95 alpha levels), about 159 KB. Subject: cartoon zombie head and bust,
bald scalp with a sparse hair fringe on the sides, unsettling grin, thumbs up, with
"MatGames" below (one word, no space) in a phosphor-terminal typeface.
`assets/branding/boot_splash.png`, 1024x1315, palette PNG with alpha
(256 colors, 95 alpha levels), about 171 KB. Subject: cartoon zombie head and bust,
bald scalp with a sparse hair fringe on the sides, thick nerd glasses, unsettling grin,
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`:
@@ -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
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
only the hair to a bald scalp with a side fringe; both files and their SHA256 are
kept in `tools/branding/raw/` and the edit is recorded under `mascot_derived_from`.
only the hair to a bald scalp with a side fringe, then a third pass added thick nerd
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
background is keyed out locally;
- 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;
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;
5. draws the title with VT323 in amber over a blurred glow layer. The title defaults
to the one-word form `MatGames`; pass `--title` to change it;
5. draws the title with VT323 in amber over a blurred glow layer, plus an optional
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.
```sh
@@ -71,11 +77,11 @@ ratio; the exported shell confirms the result with the classes
## Verification
- Exported `index.png` has the same 1024x1185 dimensions as the master. It is
re-encoded by the exporter to 32-bit RGBA (about 496 KB), so the bytes are not
identical; composited on black the two images differ by at most 6.4/255 on
0.13% 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.
- Exported `index.png` has the same 1024x1315 dimensions as the master. It is
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 5.8/255 on
0.11% of pixels, all with alpha below 20, i.e. invisible. The release keeps the
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
empty `alt`.
- `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
## 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
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",
"size": [
1024,
1185
1315
],
"mode": "P",
"palette_colors": 256,
"sha256": "4b4bab87b5038915d5a7493c6ee134cd654293036d093e71d210cbad6359bdb2"
"sha256": "715bd5f1dd3a651b1b5f6ee0560db232ddfdad676091f07ea34696787e27fbee"
},
"mascot_source": {
"file": "tools/branding/raw/agy_edit_20261010235510_0.jpg",
"sha256": "913184c2e2e3dd5357f7729988859c8e038e4561ba18af7cb60f12644f7f6994"
"file": "tools/branding/raw/agy_edit_20261011000309_0.jpg",
"sha256": "57bc1e46eb0876c02008038981c9b01a6bf4e02da78418de7b44f4fcbd93895e"
},
"generator": {
"tool": "agy_generate (Antigravity direct API)",
"model": "gemini-3.1-flash-image",
"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."
},
"chroma_key": {
"background_rgb": [
246,
1,
237
244,
0,
233
],
"transparent_below": 55,
"opaque_above": 130
},
"title": {
"text": "MatGames",
"subtitle": "\"...because he was bored...\"",
"font": "tools/branding/fonts/VT323-Regular.ttf",
"license": "SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)"
},
"bottom_fade_px": 50,
"mascot_derived_from": [
"mascot_provenance": [
{
"file": "tools/branding/raw/agy_generate_20261010234423_0.jpg",
"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"
"sha256": "5929495ff606fbcdd74f52a37304f3b569315bcd1fc0fef812b5fd7689dc1396"
},
{
"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.add_argument('raw', type=pathlib.Path, help='raw generated image (magenta background)')
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('--title-height', type=int, default=190)
parser.add_argument('--bottom-fade', type=int, default=50,
help='soften the generator frame cut at the bottom of the bust')
parser.add_argument('--colors', type=int, default=256,
help='palette colors for the shipped PNG; 0 keeps 32-bit RGBA')
parser.add_argument('--generated-from', type=pathlib.Path, default=None,
help='pre-edit generation this file was derived from, for provenance')
parser.add_argument('--provenance', action='append', type=pathlib.Path, default=[],
help='earlier files of the generation/edit chain, in order; repeatable')
args = parser.parse_args()
args.raw = args.raw.resolve()
root = pathlib.Path(__file__).resolve().parents[1]
@@ -70,27 +75,34 @@ if args.bottom_fade > 0:
# ---------------------------------------------------------------- composition
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
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.alpha_composite(mascot, ((canvas_width - mascot.width) // 2, 0))
font_size = int(title_area * 0.95)
font = ImageFont.truetype(str(font_path), font_size)
text = args.title
while font.getbbox(text)[2] - font.getbbox(text)[0] > canvas_width - 60 and font_size > 8:
font_size -= 2
font = ImageFont.truetype(str(font_path), font_size)
def _draw_line(text: str, block_top: int, block_height: int, size: int,
glow_color: tuple, text_color: tuple, blur: int) -> None:
font = ImageFont.truetype(str(font_path), size)
while font.getbbox(text)[2] - font.getbbox(text)[0] > canvas_width - 60 and size > 8:
size -= 2
font = ImageFont.truetype(str(font_path), size)
box = font.getbbox(text)
text_width = box[2] - box[0]
position = ((canvas_width - text_width) // 2 - box[0], mascot.height + (title_area - box[3]) // 2 - box[1])
position = ((canvas_width - (box[2] - box[0])) // 2 - box[0],
block_top + (block_height - box[3]) // 2 - box[1])
glow = Image.new('RGBA', canvas.size, (0, 0, 0, 0))
ImageDraw.Draw(glow).text(position, text, font=font, fill=(255, 150, 20, 190))
glow = glow.filter(ImageFilter.GaussianBlur(9))
canvas.alpha_composite(glow)
ImageDraw.Draw(canvas).text(position, text, font=font, fill=(255, 196, 64, 255))
ImageDraw.Draw(glow).text(position, text, font=font, fill=glow_color)
canvas.alpha_composite(glow.filter(ImageFilter.GaussianBlur(blur)))
ImageDraw.Draw(canvas).text(position, text, font=font, fill=text_color)
_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'
if args.colors > 0:
@@ -111,23 +123,22 @@ manifest.update({
'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'),
'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.'),
},
'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)'},
'bottom_fade_px': args.bottom_fade,
'bottom_padding_px': args.bottom_padding,
})
if args.generated_from is not None:
chain = manifest.get('mascot_derived_from', [])
manifest['mascot_derived_from'] = chain + [{
'file': str(args.generated_from.resolve().relative_to(root)),
'sha256': hashlib.sha256(args.generated_from.read_bytes()).hexdigest(),
'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',
}]
chain = list(args.provenance) + [args.raw]
manifest['mascot_provenance'] = [
{'file': str(path.resolve().relative_to(root)),
'sha256': hashlib.sha256(path.read_bytes()).hexdigest()} for path in chain]
manifest.pop('mascot_derived_from', None)
(source_tools / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n')
print(out, canvas.size, canvas.mode, 'mascot', mascot.size)