feat(hero): banner lettering, re-render the three staging shots; defer the rest to 0.9.0
Banner lettering (JIMMY KIMMEL LIVE): new Tools/gen/generate_banner_texture.py draws the exact title with Pillow + Liberation Sans Bold - an image model would mangle a trademark string, and Qwen-Image 2.1 is research-only. Gold JIMMY / KIMMEL / LIVE stacked on a crimson ground. ue_hero_facade_import.py generalises import_albedo to import_texture and wires the banner into M_Facade_Banner_Ref as Base Color (tiling 1.0), replacing the flat dark tint with a brighter red per the owner's 'less dark, more bright'. Re-rendered all three ReviewMap staging shots (front, oblique, portals) and updated their Screenshots/README.md status rows. The hero facade rework is now closed at 'surface detail that reads at 45 m'. The remaining elevation work - volute capital depth, lattice grille depth, and the full realism pass - is deferred to 0.9.0 per the owner, and the 0.9.0 hero item records the hand-off.
This commit is contained in:
1 parent
bc9fed8e11
commit
8c34eaece0
26 files changed
+171
-41
No files matched your search
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the JIMMY KIMMEL LIVE banner texture for the hero facade.
|
||||
|
||||
The three event banners are flat panels; the lettering is text and cannot be
|
||||
read as geometry at 45 m. An image model would mangle a specific trademark
|
||||
string, so the text is drawn here with Pillow and a system font instead:
|
||||
deterministic, exact, and (unlike Qwen-Image 2.1) not research-only.
|
||||
|
||||
The banner face is 1.35 m wide x 6.2 m tall (aspect 1:4.59). The texture is
|
||||
written at 512 x 2304 (aspect 1:4.5), a 2% stretch that is invisible at the
|
||||
review distance.
|
||||
|
||||
python3 Tools/gen/generate_banner_texture.py
|
||||
python3 Tools/gen/generate_banner_texture.py --out Art/textures/generated/T_Banner_KIMMEL.png
|
||||
|
||||
Requires Pillow (host-only tool, not a gate; standard library is not enough to
|
||||
rasterise a font). The text is a trademark reproduced for internal development
|
||||
review only - record it in THIRD_PARTY_NOTICES.md before any public build.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
# Banner face aspect: 1.35 m wide x 6.2 m tall. 512 x 2304 keeps that to within 2 %.
|
||||
WIDTH = 512
|
||||
HEIGHT = 2304
|
||||
|
||||
# sRGB colours. The banner is a deep event red, the lettering is gold; both match the
|
||||
# photographs (and the owner's "less dark, more bright" direction for the elevation).
|
||||
BANNER = (186, 24, 24)
|
||||
GOLD = (232, 198, 90)
|
||||
|
||||
FONT_BOLD = "/usr/share/fonts/liberation-sans-fonts/LiberationSans-Bold.ttf"
|
||||
FONT_REGULAR = "/usr/share/fonts/liberation-sans-fonts/LiberationSans-Regular.ttf"
|
||||
|
||||
# Three stacked words, as the real vertical banners arrange the title. `size` is the
|
||||
# font height in texture pixels; the block is centred vertically in the banner.
|
||||
LINES = [
|
||||
("JIMMY", 150, FONT_BOLD),
|
||||
("KIMMEL", 150, FONT_BOLD),
|
||||
("LIVE", 120, FONT_REGULAR),
|
||||
]
|
||||
LINE_GAP = 70 # pixels between baselines beyond the font ascent
|
||||
|
||||
|
||||
def _font(path: str, size: int) -> ImageFont.FreeTypeFont:
|
||||
if not os.path.exists(path):
|
||||
raise SystemExit(f"font missing: {path}")
|
||||
return ImageFont.truetype(path, size)
|
||||
|
||||
|
||||
def draw_banner(out: str) -> None:
|
||||
img = Image.new("RGB", (WIDTH, HEIGHT), BANNER)
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
fonts = [(text, size, _font(path, size)) for text, size, path in LINES]
|
||||
|
||||
# Measure the whole block so it sits centred vertically in the 6.2 m banner.
|
||||
ascents = [f.getbbox(text)[3] for text, _, f in fonts]
|
||||
block_h = sum(ascents) + LINE_GAP * (len(fonts) - 1)
|
||||
y = (HEIGHT - block_h) // 2
|
||||
|
||||
for text, size, font in fonts:
|
||||
bbox = font.getbbox(text)
|
||||
text_w = bbox[2] - bbox[0]
|
||||
x = (WIDTH - text_w) // 2 - bbox[0]
|
||||
draw.text((x, y), text, font=font, fill=GOLD)
|
||||
y += bbox[3] + LINE_GAP
|
||||
|
||||
os.makedirs(os.path.dirname(os.path.abspath(out)) or ".", exist_ok=True)
|
||||
img.save(out)
|
||||
print(f"wrote {out} ({WIDTH}x{HEIGHT})")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--out", default="Art/textures/generated/T_Banner_KIMMEL.png")
|
||||
args = parser.parse_args()
|
||||
draw_banner(args.out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user