champions-of-luneraria/tools/port/png_stats.py
Martin Stickelbroeck 07fa4572e0 U5.3: Gebäude, Boss und Ringborke auf PBR; Bildfehler aus U5 behoben
Fortsetzung des angefangenen U5.3-Stands vom 2. Oktober.

- Fünf neue Sätze aus bake_pbr.py (metal, cloth, hide, bone, wood) für
  Türme, Ancients, Grotte und die Schlundkrone. Ringborkes Rinde (RB_Bark)
  auf ihren eigenen UVs mit dem 4k-Satz (Rauigkeit zu ORM gepackt),
  Normalen im Tangentenraum des Meshes. Aufnahmen aller drei Lanes.
- Befund: Seit U5 war die Welt halb so hell und ohne Grün (Mid-Lane
  Helligkeit 0,27, Grün 0 %). Ursachen und Korrekturen:
  - Die PBR-Albedos wurden über die Godot-Farben multipliziert (Rasen
    etwa x0,05). Jetzt teilt der Shader jede Karte durch ihren Mittel-
    wert; die Godot-Farbe bleibt der Durchschnitt, die Karte liefert
    Struktur. Ebenso die Rauigkeit, wo das Material eine eigene hat.
  - Normal- und ORM-Karten waren als sRGB-Farbe importiert. Jetzt
    Normalkarte bzw. linear.
  - bake_pbr.py verzerrte alle Muster 10:1 (Regenstreifen). Stein, Erde,
    Laub, Wasser und die neuen Sätze ohne Maserung neu gebacken.
  - Der Treffer-Flash ersetzte die Grundfarbe aller Materialplätze; im
    Nahkampf waren die Armeen weiß und der Boss gelb. Jetzt Tönung der
    eigenen Farbe je Platz, wenig Emission, 16 Stufen.
  - Nebel 0,0085 verschleierte die Kartenansicht zu 35-50 %; jetzt 0,0045.
  Ergebnis Mid-Lane: Helligkeit 0,41-0,44, Grün 31-39 % (U3: 0,45 / 27 %).
- Tests: EditMode 29/29, PlayMode 13/13, Verify 718/0, Kerntests .NET
  457/457, Fingerabdrücke unverändert (DCE95C96556F3A1A, 7B8E43AEFC0D60C5).
- Leistung: A/B mit einem U5.2-Player unter gleicher Last, kein messbarer
  Unterschied (91,4 gegen 93,8 fps Karte, 124,9 gegen 125,3 fps Mid).
- Messwerkzeug tools/port/png_stats.py; Doku U5-VISUAL (U5.3),
  PORT-STATUS, PORT-QA; Bilder screenshots/u5.3/ als JPEG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 19:34:02 +02:00

90 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Brightness and colour of screenshots (U5.3), without external libraries.
Prints for each PNG (8-bit RGB/RGBA, every 4th pixel in both directions):
luma mean Rec. 709 luma of the sRGB values (0 black … 1 white)
sat mean HSV saturation
green share of pixels with a green hue (saturation > .15, hue 61°–162°)
blue share of pixels with a blue hue (saturation > .15, hue 180°–259°)
Usage: python3 tools/port/png_stats.py file.png [file.png …] [--crop x0 y0 x1 y1]
"""
import colorsys
import struct
import sys
import zlib
def read_png(path):
data = open(path, 'rb').read()
if data[:8] != b'\x89PNG\r\n\x1a\n':
raise ValueError(path + ': not a PNG')
pos, idat = 8, []
while pos < len(data):
length, kind = struct.unpack('>I4s', data[pos:pos + 8])
body = data[pos + 8:pos + 8 + length]
if kind == b'IHDR':
width, height, depth, color, _, _, interlace = struct.unpack('>IIBBBBB', body)
elif kind == b'IDAT':
idat.append(body)
pos += 12 + length
if depth != 8 or interlace != 0 or color not in (2, 6):
raise ValueError('%s: needs 8-bit RGB/RGBA without interlace' % path)
bpp = 3 if color == 2 else 4
raw = zlib.decompress(b''.join(idat))
stride = width * bpp
rows, prev, i = [], bytearray(stride), 0
for _ in range(height):
kind, line = raw[i], bytearray(raw[i + 1:i + 1 + stride])
i += 1 + stride
for x in range(stride):
a = line[x - bpp] if x >= bpp else 0
b = prev[x]
c = prev[x - bpp] if x >= bpp else 0
if kind == 1:
line[x] = (line[x] + a) & 255
elif kind == 2:
line[x] = (line[x] + b) & 255
elif kind == 3:
line[x] = (line[x] + ((a + b) >> 1)) & 255
elif kind == 4:
p = a + b - c
pa, pb, pc = abs(p - a), abs(p - b), abs(p - c)
line[x] = (line[x] + (a if pa <= pb and pa <= pc else b if pb <= pc else c)) & 255
rows.append(bytes(line))
prev = line
return width, height, bpp, rows
def stats(path, crop=None):
width, height, bpp, rows = read_png(path)
x0, y0, x1, y1 = crop if crop else (0, 0, width, height)
luma = sat = green = blue = n = 0
for y in range(y0, y1, 4):
row = rows[y]
for x in range(x0, x1, 4):
r, g, b = row[x * bpp] / 255, row[x * bpp + 1] / 255, row[x * bpp + 2] / 255
hue = colorsys.rgb_to_hls(r, g, b)[0]
top = max(r, g, b)
s = 0 if top == 0 else (top - min(r, g, b)) / top
luma += 0.2126 * r + 0.7152 * g + 0.0722 * b
sat += s
n += 1
if s > .15 and .17 < hue < .45:
green += 1
if s > .15 and .5 < hue < .72:
blue += 1
return luma / n, sat / n, green / n, blue / n
def main(argv):
crop = None
if '--crop' in argv:
at = argv.index('--crop')
crop = tuple(int(v) for v in argv[at + 1:at + 5])
argv = argv[:at] + argv[at + 5:]
for path in argv:
luma, sat, green, blue = stats(path, crop)
print('%-48s luma %.3f sat %.3f green %.2f blue %.2f' % (path.split('/')[-1], luma, sat, green, blue))
if __name__ == '__main__':
main(sys.argv[1:])