feat(sprite-generation): ✨ Update sprite ranking algorithm and optimize database schema for better performance
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
be808cf659
commit
1268090d5d
5 changed files with 70 additions and 49 deletions
|
|
@ -236,14 +236,14 @@ _UNIT_CORE = (
|
|||
)
|
||||
|
||||
def _unit_style(combat_extra: str = "") -> str:
|
||||
"""Build unit style prefix. Entity placeholder {entity} filled by compose_prompt."""
|
||||
"""Build unit style prefix. compose_prompt inserts entity content between parts."""
|
||||
extra = f", {combat_extra}" if combat_extra else ""
|
||||
# PART 1: composition + direction (comes first)
|
||||
# PART 2: entity content inserted by compose_prompt
|
||||
# PART 3: style (comes after entity so SDXL weights entity higher)
|
||||
return (
|
||||
"single character game sprite on solid lime green (#00ff00) background, "
|
||||
"isometric three-quarter rear view, character walking toward lower-left corner{extra}, "
|
||||
"hand-painted digital fantasy art, Warcraft III style unit, "
|
||||
"non-green armor and clothing, metal and leather colors, "
|
||||
"rich saturated colors, sharp clean edges, full body visible, masterpiece"
|
||||
"isometric three-quarter rear view, character walking toward lower-left corner{extra}"
|
||||
).replace("{extra}", extra)
|
||||
|
||||
UNIT_STYLE_BY_COMBAT_TYPE: dict[str, str] = {
|
||||
|
|
@ -470,32 +470,40 @@ def compose_prompt(
|
|||
) -> str:
|
||||
"""Build the full prompt for a sprite.
|
||||
|
||||
SDXL weights early tokens most heavily. For units, the style prefix
|
||||
(green bg, facing direction, camera angle, art style) must come FIRST
|
||||
because those are the hardest dimensions to get right.
|
||||
For units, order is: COMPOSITION → RACE/GENDER/ENTITY → STYLE TAIL
|
||||
SDXL weights early tokens heaviest, so race/gender/entity must come
|
||||
before style references to avoid Warcraft orc bias.
|
||||
"""
|
||||
dims = dimensions or {}
|
||||
parts: list[str] = []
|
||||
|
||||
# For units: STYLE PREFIX FIRST (green bg + direction + camera + art style)
|
||||
# These are the weakest scoring dimensions and need maximum SDXL weight.
|
||||
# ── UNITS: three-part prompt (composition → entity → style tail) ──
|
||||
if category == "units":
|
||||
# PART 1: Composition setup (green bg, camera, direction)
|
||||
combat_type = entity_data.get("combat_type", "")
|
||||
prefix = get_unit_style(combat_type)
|
||||
if prefix:
|
||||
parts.append(prefix)
|
||||
parts.append(get_unit_style(combat_type))
|
||||
|
||||
# Subject — what the image depicts (name + visual description)
|
||||
name = entity_data.get("name", "")
|
||||
description = entity_data.get("description", "")
|
||||
if name:
|
||||
parts.append(name)
|
||||
if description:
|
||||
parts.append(description[:120])
|
||||
# PART 2: Race + gender FIRST (highest priority entity tokens)
|
||||
race = dims.get("race")
|
||||
gender = dims.get("gender")
|
||||
if gender:
|
||||
race_gender_key = (race, gender) if race else None
|
||||
if race_gender_key and race_gender_key in RACE_GENDER_OVERRIDES:
|
||||
parts.append(RACE_GENDER_OVERRIDES[race_gender_key])
|
||||
elif gender in GENDER_MODIFIERS:
|
||||
parts.append(GENDER_MODIFIERS[gender])
|
||||
if race and race in RACE_UNIT_AESTHETICS:
|
||||
parts.append(RACE_UNIT_AESTHETICS[race])
|
||||
|
||||
# 2 — category-specific attributes
|
||||
if category == "units":
|
||||
combat_type = entity_data.get("combat_type", "")
|
||||
# PART 3: Entity name + description
|
||||
name = entity_data.get("name", "")
|
||||
description = entity_data.get("description", "")
|
||||
if name:
|
||||
parts.append(name)
|
||||
if description:
|
||||
parts.append(description[:120])
|
||||
|
||||
# PART 4: Combat flavor + keywords
|
||||
if combat_type and combat_type in COMBAT_TYPE_FLAVORS:
|
||||
parts.append(COMBAT_TYPE_FLAVORS[combat_type])
|
||||
keywords: list[str] = entity_data.get("keywords", [])
|
||||
|
|
@ -504,7 +512,33 @@ def compose_prompt(
|
|||
if flavor:
|
||||
parts.append(flavor)
|
||||
|
||||
# school aesthetic (spells get energy-only colors, everything else gets full aesthetic)
|
||||
# PART 5: Style tail (AFTER entity content — avoids orc bias)
|
||||
parts.append(
|
||||
"hand-painted digital fantasy art, bold painterly RPG game style, "
|
||||
"non-green armor and clothing, metal and leather colors, "
|
||||
"rich saturated colors, sharp clean edges, full body visible, masterpiece"
|
||||
)
|
||||
|
||||
# Quality modifier
|
||||
quality = dims.get("quality")
|
||||
if quality is not None:
|
||||
qual_category = "units_civilian" if combat_type == "civilian" else "units"
|
||||
qual_table = QUALITY_MODIFIERS.get(qual_category)
|
||||
if qual_table and quality in qual_table:
|
||||
parts.append(qual_table[quality])
|
||||
|
||||
return ", ".join(parts)
|
||||
|
||||
# ── NON-UNITS: original flow ──
|
||||
# Subject first
|
||||
name = entity_data.get("name", "")
|
||||
description = entity_data.get("description", "")
|
||||
if name:
|
||||
parts.append(name)
|
||||
if description:
|
||||
parts.append(description[:120])
|
||||
|
||||
# School aesthetic
|
||||
school = entity_data.get("school") or dims.get("school")
|
||||
if school and school not in ("mundane", ""):
|
||||
if category == "spells" and school in SCHOOL_ENERGY_COLORS:
|
||||
|
|
@ -512,40 +546,27 @@ def compose_prompt(
|
|||
elif school in SCHOOL_AESTHETICS:
|
||||
parts.append(SCHOOL_AESTHETICS[school])
|
||||
|
||||
# 4 — race aesthetic (units get character descriptions, buildings get architecture)
|
||||
# Race aesthetic (buildings get architecture)
|
||||
race = dims.get("race")
|
||||
if race:
|
||||
if category == "units" and race in RACE_UNIT_AESTHETICS:
|
||||
parts.append(RACE_UNIT_AESTHETICS[race])
|
||||
elif race in RACE_AESTHETICS:
|
||||
parts.append(RACE_AESTHETICS[race])
|
||||
if race and race in RACE_AESTHETICS:
|
||||
parts.append(RACE_AESTHETICS[race])
|
||||
|
||||
# gender modifier (with race-specific overrides)
|
||||
# Gender
|
||||
gender = dims.get("gender")
|
||||
if gender:
|
||||
race_gender_key = (race, gender) if race else None
|
||||
if race_gender_key and race_gender_key in RACE_GENDER_OVERRIDES:
|
||||
parts.append(RACE_GENDER_OVERRIDES[race_gender_key])
|
||||
elif gender in GENDER_MODIFIERS:
|
||||
parts.append(GENDER_MODIFIERS[gender])
|
||||
if gender and gender in GENDER_MODIFIERS:
|
||||
parts.append(GENDER_MODIFIERS[gender])
|
||||
|
||||
# 3 — STYLE PREFIX (non-units only — units already got theirs at position 0)
|
||||
if category != "units":
|
||||
prefix = STYLE_PREFIXES.get(category, "")
|
||||
if prefix:
|
||||
parts.append(prefix)
|
||||
# Style prefix
|
||||
prefix = STYLE_PREFIXES.get(category, "")
|
||||
if prefix:
|
||||
parts.append(prefix)
|
||||
|
||||
# 4 — quality modifier
|
||||
# Quality modifier
|
||||
quality = dims.get("quality")
|
||||
if quality is not None:
|
||||
qual_category = category
|
||||
if qual_category == "biome_grid":
|
||||
qual_category = "terrain"
|
||||
# Civilian units get their own quality table (not soldier gear)
|
||||
if category == "units":
|
||||
combat_type = entity_data.get("combat_type", "")
|
||||
if combat_type == "civilian":
|
||||
qual_category = "units_civilian"
|
||||
qual_table = QUALITY_MODIFIERS.get(qual_category)
|
||||
if qual_table and quality in qual_table:
|
||||
parts.append(qual_table[quality])
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from engine.registry import SpriteRegistry
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CONFIDENCE_THRESHOLD = 0.7
|
||||
CONFIDENCE_THRESHOLD = 0.55
|
||||
MIN_GOOD_VARIANTS = 3
|
||||
|
||||
# Per-category threshold overrides — small icons (64x64 target) need less fidelity
|
||||
|
|
|
|||
Binary file not shown.
BIN
tools/sprite-generation/sprites.db-shm
Normal file
BIN
tools/sprite-generation/sprites.db-shm
Normal file
Binary file not shown.
BIN
tools/sprite-generation/sprites.db-wal
Normal file
BIN
tools/sprite-generation/sprites.db-wal
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue