🦖 Evening Session: Items & Equipment Complete + Dino Valley 50%
Category 2: Items & Equipment - COMPLETE (105 assets) - Weapons: 30 - Armor: 20 - Consumables: 25 - Crafting Materials: 30 Dino Valley Biome: 55/109 assets (50%) - Vegetation: 15/15 (Style 30) ✅ - Dinosaurs: 12/12 (Style 33) ✅ - Items: 12/12 (Style 33) ✅ - Food: 16/16 (Style 33) ✅ Progress: 215/~1,500 assets (14.3%) Time: 23:45 CET, 02.01.2026 Production velocity: ~28 assets/hour
This commit is contained in:
340
scripts/mass_asset_generator.py
Executable file
340
scripts/mass_asset_generator.py
Executable file
@@ -0,0 +1,340 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
MASS ASSET GENERATOR - DolinaSmrti
|
||||
Generates all 14,000+ game assets automatically using Vertex AI Imagen.
|
||||
Follows the Dual-Style System and Production Workflow.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
import vertexai
|
||||
from vertexai.preview.vision_models import ImageGenerationModel
|
||||
|
||||
# === CONFIGURATION ===
|
||||
PROJECT_ID = "gen-lang-client-0428644398"
|
||||
LOCATION = "us-central1"
|
||||
BASE_PATH = Path("/Users/davidkotnik/repos/novafarma/assets/slike")
|
||||
PROGRESS_FILE = Path("/Users/davidkotnik/repos/novafarma/.asset_progress.json")
|
||||
|
||||
# Style definitions
|
||||
STYLE_30 = """Style 30: Garden Story cozy adventure. Soft rounded friendly shapes, pastel-vibrant balance, wholesome mature aesthetic, Link's Awakening meets modern indie. 2D game asset. Solid chroma green background (#00FF00)."""
|
||||
|
||||
STYLE_32 = """Style 32: Cult of the Lamb cute-dark aesthetic. Bold outlines, noir shadows, pastel-gothic colors (dusty pinks, muted purples, cream whites), chunky proportions, big expressive eyes, contradiction between cute and dark. 2D game sprite. Solid chroma green background (#00FF00)."""
|
||||
|
||||
# Asset database - complete production plan
|
||||
ASSET_DATABASE = {
|
||||
# PHASE 1: Plants (Style 30) - ALREADY COMPLETE
|
||||
"rastline": {
|
||||
"style": STYLE_30,
|
||||
"categories": {
|
||||
"sadje": [
|
||||
# Already generated - 30 assets
|
||||
],
|
||||
"zelenjava": [
|
||||
# Already generated - 30 assets
|
||||
],
|
||||
"ganja": [
|
||||
# Already generated - 15 assets
|
||||
],
|
||||
"drevesa": [
|
||||
# Already generated - 10 assets
|
||||
],
|
||||
"roze": [
|
||||
# Already generated - 8 assets
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
# PHASE 2: Creatures (Style 32)
|
||||
"kreature": {
|
||||
"style": STYLE_32,
|
||||
"categories": {
|
||||
"zombiji": [
|
||||
("zombie_basic", "Basic shambling zombie, rotting flesh"),
|
||||
("zombie_runner", "Fast running zombie, aggressive pose"),
|
||||
("zombie_tank", "Large bulky zombie, muscular"),
|
||||
("zombie_spitter", "Zombie with acidic spit attack"),
|
||||
("zombie_exploder", "Bloated zombie ready to explode"),
|
||||
("zombie_crawler", "Crawling zombie on ground"),
|
||||
("zombie_screamer", "Zombie with wide screaming mouth"),
|
||||
("zombie_bride", "Zombie in tattered wedding dress"),
|
||||
("zombie_cop", "Zombie police officer"),
|
||||
("zombie_doctor", "Zombie in medical scrubs"),
|
||||
],
|
||||
"dinozavri": [
|
||||
("dino_trex", "T-Rex dinosaur, fierce predator"),
|
||||
("dino_raptor", "Velociraptor, hunting pose"),
|
||||
("dino_trike", "Triceratops, herbivore with horns"),
|
||||
("dino_stego", "Stegosaurus with back plates"),
|
||||
("dino_bronto", "Brontosaurus, long neck"),
|
||||
("dino_ptero", "Pterodactyl flying"),
|
||||
("dino_ankylo", "Ankylosaurus with club tail"),
|
||||
("dino_parasaur", "Parasaurolophus with head crest"),
|
||||
("dino_carno", "Carnotaurus with horns"),
|
||||
("dino_spino", "Spinosaurus with sail"),
|
||||
],
|
||||
"zivali": [
|
||||
("animal_bear", "Brown bear, standing pose"),
|
||||
("animal_wolf", "Gray wolf, hunting stance"),
|
||||
("animal_deer", "Deer with antlers"),
|
||||
("animal_rabbit", "Cute rabbit"),
|
||||
("animal_fox", "Red fox, cunning"),
|
||||
("animal_boar", "Wild boar, aggressive"),
|
||||
("animal_crow", "Black crow perched"),
|
||||
("animal_owl", "Wise owl"),
|
||||
("animal_snake", "Coiled snake"),
|
||||
("animal_rat", "Large rat"),
|
||||
],
|
||||
"magicna_bitja": [
|
||||
("magic_demon", "Small demon creature"),
|
||||
("magic_imp", "Mischievous imp"),
|
||||
("magic_wraith", "Dark wraith spirit"),
|
||||
("magic_familiar", "Black cat familiar"),
|
||||
("magic_golem", "Stone golem"),
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
# PHASE 2: NPCs (Style 32)
|
||||
"npc": {
|
||||
"style": STYLE_32,
|
||||
"categories": {
|
||||
"policija": [
|
||||
("police_chief", "Police chief, stern expression"),
|
||||
("police_officer", "Regular police officer"),
|
||||
("police_corrupt", "Corrupt cop, suspicious"),
|
||||
],
|
||||
"zdravstvo": [
|
||||
("doctor_main", "Town doctor, friendly"),
|
||||
("nurse_helper", "Nurse assistant"),
|
||||
],
|
||||
"trgovci": [
|
||||
("merchant_general", "General store merchant"),
|
||||
("merchant_seeds", "Seed merchant"),
|
||||
("merchant_black_market", "Shady black market dealer"),
|
||||
],
|
||||
"mentorji": [
|
||||
("mentor_farming", "Old farmer mentor"),
|
||||
("mentor_magic", "Dark magic mentor"),
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
# PHASE 3: Main Characters (Style 32) - SAVE FOR LAST
|
||||
"glavni_karakterji": {
|
||||
"style": STYLE_32,
|
||||
"categories": {
|
||||
"kai": [
|
||||
("kai_front_idle", "Kai facing front, idle stance"),
|
||||
("kai_back_idle", "Kai facing back, idle stance"),
|
||||
("kai_left_idle", "Kai facing left, idle stance"),
|
||||
("kai_right_idle", "Kai facing right, idle stance"),
|
||||
("kai_front_walk_1", "Kai front walk frame 1"),
|
||||
("kai_front_walk_2", "Kai front walk frame 2"),
|
||||
("kai_back_walk_1", "Kai back walk frame 1"),
|
||||
("kai_back_walk_2", "Kai back walk frame 2"),
|
||||
("kai_left_walk_1", "Kai left walk frame 1"),
|
||||
("kai_left_walk_2", "Kai left walk frame 2"),
|
||||
("kai_right_walk_1", "Kai right walk frame 1"),
|
||||
("kai_right_walk_2", "Kai right walk frame 2"),
|
||||
],
|
||||
"ana": [
|
||||
("ana_front_idle", "Ana facing front, idle stance"),
|
||||
("ana_back_idle", "Ana facing back, idle stance"),
|
||||
("ana_left_idle", "Ana facing left, idle stance"),
|
||||
("ana_right_idle", "Ana facing right, idle stance"),
|
||||
("ana_front_walk_1", "Ana front walk frame 1"),
|
||||
("ana_front_walk_2", "Ana front walk frame 2"),
|
||||
("ana_back_walk_1", "Ana back walk frame 1"),
|
||||
("ana_back_walk_2", "Ana back walk frame 2"),
|
||||
("ana_left_walk_1", "Ana left walk frame 1"),
|
||||
("ana_left_walk_2", "Ana left walk frame 2"),
|
||||
("ana_right_walk_1", "Ana right walk frame 1"),
|
||||
("ana_right_walk_2", "Ana right walk frame 2"),
|
||||
],
|
||||
"gronk": [
|
||||
("gronk_front_idle", "Gronk facing front, idle stance"),
|
||||
("gronk_back_idle", "Gronk facing back, idle stance"),
|
||||
("gronk_left_idle", "Gronk facing left, idle stance"),
|
||||
("gronk_right_idle", "Gronk facing right, idle stance"),
|
||||
],
|
||||
"susi": [
|
||||
("susi_front_idle", "Susi the cat facing front"),
|
||||
("susi_back_idle", "Susi the cat facing back"),
|
||||
("susi_left_idle", "Susi the cat facing left"),
|
||||
("susi_right_idle", "Susi the cat facing right"),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MassAssetGenerator:
|
||||
"""Generates all game assets systematically."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the generator."""
|
||||
print("🚀 DolinaSmrti Mass Asset Generator")
|
||||
print("=" * 60)
|
||||
|
||||
# Initialize Vertex AI
|
||||
vertexai.init(project=PROJECT_ID, location=LOCATION)
|
||||
self.model = ImageGenerationModel.from_pretrained("imagen-3.0-generate-001")
|
||||
|
||||
# Load progress
|
||||
self.progress = self.load_progress()
|
||||
|
||||
# Stats
|
||||
self.total_generated = 0
|
||||
self.total_failed = 0
|
||||
self.session_generated = 0
|
||||
|
||||
def load_progress(self) -> Dict:
|
||||
"""Load generation progress from file."""
|
||||
if PROGRESS_FILE.exists():
|
||||
with open(PROGRESS_FILE, 'r') as f:
|
||||
return json.load(f)
|
||||
return {"completed": [], "failed": []}
|
||||
|
||||
def save_progress(self):
|
||||
"""Save current progress."""
|
||||
with open(PROGRESS_FILE, 'w') as f:
|
||||
json.dump(self.progress, f, indent=2)
|
||||
|
||||
def is_completed(self, asset_id: str) -> bool:
|
||||
"""Check if asset was already generated."""
|
||||
return asset_id in self.progress["completed"]
|
||||
|
||||
def generate_image(self, prompt: str, filename: str, output_path: Path) -> bool:
|
||||
"""Generate a single image using Vertex AI."""
|
||||
try:
|
||||
print(f" Generating: {filename}...")
|
||||
|
||||
response = self.model.generate_images(
|
||||
prompt=prompt,
|
||||
number_of_images=1,
|
||||
aspect_ratio="1:1",
|
||||
safety_filter_level="block_few",
|
||||
person_generation="allow_adult"
|
||||
)
|
||||
|
||||
if response.images:
|
||||
# Save image
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
response.images[0].save(location=str(output_path))
|
||||
print(f" ✅ Saved: {output_path.name}")
|
||||
return True
|
||||
else:
|
||||
print(f" ❌ No image generated")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Error: {str(e)}")
|
||||
return False
|
||||
|
||||
def generate_category(self, main_folder: str, category: str,
|
||||
assets: List[Tuple[str, str]], style: str):
|
||||
"""Generate all assets in a category."""
|
||||
print(f"\n📁 {main_folder}/{category}")
|
||||
print("-" * 60)
|
||||
|
||||
category_path = BASE_PATH / main_folder / category
|
||||
|
||||
for filename, description in assets:
|
||||
asset_id = f"{main_folder}/{category}/{filename}"
|
||||
|
||||
# Skip if already completed
|
||||
if self.is_completed(asset_id):
|
||||
print(f" ⏭️ Skipping: {filename} (already generated)")
|
||||
continue
|
||||
|
||||
# Build full prompt
|
||||
full_prompt = f"{style} {description}"
|
||||
|
||||
# Generate
|
||||
output_file = category_path / f"{filename}.png"
|
||||
success = self.generate_image(full_prompt, filename, output_file)
|
||||
|
||||
if success:
|
||||
self.progress["completed"].append(asset_id)
|
||||
self.total_generated += 1
|
||||
self.session_generated += 1
|
||||
else:
|
||||
self.progress["failed"].append(asset_id)
|
||||
self.total_failed += 1
|
||||
|
||||
# Save progress after each asset
|
||||
self.save_progress()
|
||||
|
||||
# Rate limiting - wait between generations
|
||||
time.sleep(1)
|
||||
|
||||
def generate_all(self):
|
||||
"""Generate all assets following the production workflow."""
|
||||
print("\n🎯 Starting mass generation...")
|
||||
print(f"Progress file: {PROGRESS_FILE}")
|
||||
print()
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
# Follow production workflow order:
|
||||
# 1. Plants (already done, skip)
|
||||
# 2. Creatures & NPCs
|
||||
# 3. Main characters (last)
|
||||
|
||||
# Phase 2: Creatures
|
||||
if "kreature" in ASSET_DATABASE:
|
||||
creatures = ASSET_DATABASE["kreature"]
|
||||
for category, assets in creatures["categories"].items():
|
||||
if assets: # Only if there are assets defined
|
||||
self.generate_category("kreature", category, assets, creatures["style"])
|
||||
|
||||
# Phase 2: NPCs
|
||||
if "npc" in ASSET_DATABASE:
|
||||
npcs = ASSET_DATABASE["npc"]
|
||||
for category, assets in npcs["categories"].items():
|
||||
if assets:
|
||||
self.generate_category("npc", category, assets, npcs["style"])
|
||||
|
||||
# Phase 3: Main Characters (generate last for highest quality)
|
||||
if "glavni_karakterji" in ASSET_DATABASE:
|
||||
print("\n" + "=" * 60)
|
||||
print("⚠️ FINAL PHASE: Main Characters (Highest Quality)")
|
||||
print("=" * 60)
|
||||
chars = ASSET_DATABASE["glavni_karakterji"]
|
||||
for category, assets in chars["categories"].items():
|
||||
if assets:
|
||||
self.generate_category("glavni_karakterji", category, assets, chars["style"])
|
||||
|
||||
# Summary
|
||||
elapsed = time.time() - start_time
|
||||
print("\n" + "=" * 60)
|
||||
print("🎉 GENERATION COMPLETE!")
|
||||
print("=" * 60)
|
||||
print(f"Session generated: {self.session_generated}")
|
||||
print(f"Total generated: {self.total_generated}")
|
||||
print(f"Total failed: {self.total_failed}")
|
||||
print(f"Time elapsed: {elapsed/60:.1f} minutes")
|
||||
print(f"Progress saved to: {PROGRESS_FILE}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
try:
|
||||
generator = MassAssetGenerator()
|
||||
generator.generate_all()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n⚠️ Generation interrupted by user")
|
||||
print("Progress has been saved. Run again to resume.")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Fatal error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
275
scripts/production_generator_v2.py
Executable file
275
scripts/production_generator_v2.py
Executable file
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DOLINASMRTI PRODUCTION ASSET GENERATOR V2
|
||||
Generates ALL game assets with proper Style 32/30 consistency.
|
||||
Uses detailed prompts following the Dual-Style System Guide.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
import vertexai
|
||||
from vertexai.preview.vision_models import ImageGenerationModel
|
||||
|
||||
# === CONFIGURATION ===
|
||||
PROJECT_ID = "gen-lang-client-0428644398"
|
||||
LOCATION = "us-central1"
|
||||
BASE_PATH = Path("/Users/davidkotnik/repos/novafarma/assets/slike")
|
||||
PROGRESS_FILE = Path("/Users/davidkotnik/repos/novafarma/.generation_progress.json")
|
||||
LOG_FILE = Path("/Users/davidkotnik/repos/novafarma/generation.log")
|
||||
|
||||
# === STYLE DEFINITIONS ===
|
||||
|
||||
STYLE_32_BASE = """Style 32: Cult of the Lamb cute-dark aesthetic. Bold black outlines, pastel-gothic color palette (dusty pinks, muted purples, cream whites, soft grays with noir shadows), chunky proportions, big expressive eyes, smooth gradients, contradiction between adorable and sinister. """
|
||||
|
||||
STYLE_30_BASE = """Style 30: Garden Story cozy adventure. Soft rounded friendly shapes, pastel-vibrant color balance (cheerful but not childish), wholesome mature aesthetic, Link's Awakening meets modern indie charm, clean vector look with smooth edges. """
|
||||
|
||||
COMMON_SUFFIX = """2D game asset, centered subject with 10px margin, consistent lighting. Solid chroma green background (#00FF00) for easy removal."""
|
||||
|
||||
# === DETAILED PROMPT DATABASE ===
|
||||
|
||||
PHASE_2_CREATURES = {
|
||||
"kreature/zombiji": {
|
||||
"style": "style_32",
|
||||
"assets": [
|
||||
("zombie_basic", f"{STYLE_32_BASE}Basic shambling zombie, rotting flesh with exposed ribs, tattered clothing, blank white eyes, slow stumbling pose, covered in dirt and blood splatters, cute-grotesque balance. {COMMON_SUFFIX}"),
|
||||
("zombie_runner", f"{STYLE_32_BASE}Fast running zombie, aggressive sprinting pose, lean athletic build, wild disheveled hair, crazed expression, torn sportswear, speed lines effect. {COMMON_SUFFIX}"),
|
||||
("zombie_tank", f"{STYLE_32_BASE}Large bulky zombie, massive muscular frame, cracked skin showing bones, heavy stomping stance, intimidating but cute chunky proportions, tattered vest. {COMMON_SUFFIX}"),
|
||||
("zombie_spitter", f"{STYLE_32_BASE}Zombie with acidic spit attack, bloated throat pouch, green acid dripping from mouth, hunched pose ready to spit, pustules on skin, toxic cute. {COMMON_SUFFIX}"),
|
||||
("zombie_exploder", f"{STYLE_32_BASE}Bloated zombie ready to explode, swollen round belly with glowing veins, worried expression, arms spread wide, pulsing red glow inside body, ticking time bomb cuteness. {COMMON_SUFFIX}"),
|
||||
("zombie_crawler", f"{STYLE_32_BASE}Crawling zombie on ground, dragging itself forward with arms, legless lower body, determined expression, dirty fingernails, trail of slime. {COMMON_SUFFIX}"),
|
||||
("zombie_screamer", f"{STYLE_32_BASE}Screaming zombie with wide open mouth, sound wave visual effect, hands clutching head, red glowing throat, ear-splitting pose, disturbing yet adorable. {COMMON_SUFFIX}"),
|
||||
("zombie_bride", f"{STYLE_32_BASE}Zombie in tattered wedding dress, decayed bride with veil, bouquet of dead flowers, melancholy expression, torn white gown with blood stains, tragic beauty. {COMMON_SUFFIX}"),
|
||||
("zombie_cop", f"{STYLE_32_BASE}Zombie police officer, torn blue uniform with badge, police cap askew, holding broken baton, deadpan law enforcement expression, duty-bound undead. {COMMON_SUFFIX}"),
|
||||
("zombie_doctor", f"{STYLE_32_BASE}Zombie in medical scrubs, stethoscope around neck, blood-stained white coat, surgical mask hanging loose, carrying clipboard, twisted healthcare worker. {COMMON_SUFFIX}"),
|
||||
]
|
||||
},
|
||||
|
||||
"kreature/dinozavri": {
|
||||
"style": "style_32",
|
||||
"assets": [
|
||||
("dino_trex", f"{STYLE_32_BASE}T-Rex dinosaur, fierce predator with tiny arms, massive head with sharp teeth showing, powerful legs in hunting stance, scaly texture, roaring pose, chunky cute-terrifying proportions. {COMMON_SUFFIX}"),
|
||||
("dino_raptor", f"{STYLE_32_BASE}Velociraptor, intelligent hunting pose, sickle claw prominently raised, sleek body with feathered accents, cunning big eyes, pack hunter stance. {COMMON_SUFFIX}"),
|
||||
("dino_trike", f"{STYLE_32_BASE}Triceratops, herbivore with three prominent horns and neck frill, sturdy body, gentle but powerful stance, armored head, grazing pose. {COMMON_SUFFIX}"),
|
||||
("dino_stego", f"{STYLE_32_BASE}Stegosaurus with distinctive back plates in row, spiked tail (thagomizer), long neck extended, plant-eater pose, armored gentle giant. {COMMON_SUFFIX}"),
|
||||
("dino_bronto", f"{STYLE_32_BASE}Brontosaurus, extremely long neck reaching upward, massive body on four thick legs, small head, gentle giant expression, eating tree leaves. {COMMON_SUFFIX}"),
|
||||
("dino_ankylo", f"{STYLE_32_BASE}Ankylosaurus with heavy armor plating covering body, club tail weapon raised defensively, low waddle stance, tank-like build, prehistoric tank. {COMMON_SUFFIX}"),
|
||||
("dino_ptero", f"{STYLE_32_BASE}Pterodactyl flying dinosaur, wings spread wide in flight, long beak with sharp teeth, soaring pose, leather wings, sky hunter. {COMMON_SUFFIX}"),
|
||||
("dino_spino", f"{STYLE_32_BASE}Spinosaurus with massive sail on back, crocodile-like snout, semi-aquatic stance, powerful arms, fishing predator pose. {COMMON_SUFFIX}"),
|
||||
("dino_dilo", f"{STYLE_32_BASE}Dilophosaurus with distinctive twin head crests, neck frill extended in threat display, venom-spitting pose, medium-sized predator. {COMMON_SUFFIX}"),
|
||||
("dino_pachy", f"{STYLE_32_BASE}Pachycephalosaurus with thick domed skull for head-butting, bipedal stance, charging pose, bony head visible, ram attack ready. {COMMON_SUFFIX}"),
|
||||
]
|
||||
},
|
||||
|
||||
"kreature/zivali": {
|
||||
"style": "style_32",
|
||||
"assets": [
|
||||
("animal_bear", f"{STYLE_32_BASE}Brown bear standing on hind legs, powerful build, thick fur, cute rounded ears, big paws with claws, intimidating but huggable stance. {COMMON_SUFFIX}"),
|
||||
("animal_wolf", f"{STYLE_32_BASE}Gray wolf in hunting stance, sleek body lowered ready to pounce, sharp teeth showing, pointed ears alert, pack leader aura, fierce yet cute. {COMMON_SUFFIX}"),
|
||||
("animal_deer", f"{STYLE_32_BASE}Deer with majestic antlers, gentle eyes, standing gracefully, spotted coat, alert ears, forest spirit vibe, serene but watchful. {COMMON_SUFFIX}"),
|
||||
("animal_rabbit", f"{STYLE_32_BASE}Cute rabbit sitting upright, long ears perked up, fluffy tail, whiskers twitching, holding carrot, innocent big eyes, forest cottontail. {COMMON_SUFFIX}"),
|
||||
("animal_fox", f"{STYLE_32_BASE}Red fox with bushy tail, cunning expression, sleek orange fur with white chest, pointed snout, sly sitting pose, forest trickster. {COMMON_SUFFIX}"),
|
||||
("animal_boar", f"{STYLE_32_BASE}Wild boar, aggressive stance with head lowered, sharp tusks protruding, bristly fur, muscular build, charging pose, dangerous but cute. {COMMON_SUFFIX}"),
|
||||
("animal_crow", f"{STYLE_32_BASE}Black crow perched on branch, glossy black feathers, intelligent beady eyes, sharp beak, one foot raised, ominous messenger. {COMMON_SUFFIX}"),
|
||||
("animal_owl", f"{STYLE_32_BASE}Wise owl perched, large round eyes staring forward, feathered ear tufts, soft brown plumage, nocturnal hunter pose, mysterious knowledge-keeper. {COMMON_SUFFIX}"),
|
||||
("animal_snake", f"{STYLE_32_BASE}Coiled snake ready to strike, forked tongue flicking out, scaled body in spiral, diamond head pattern, venomous but cute, danger noodle. {COMMON_SUFFIX}"),
|
||||
("animal_rat", f"{STYLE_32_BASE}Large rat on hind legs, long naked tail curled, whiskers prominent, beady eyes, tiny paws, city scavenger pose, plague cute. {COMMON_SUFFIX}"),
|
||||
]
|
||||
},
|
||||
|
||||
"kreature/magicna_bitja": {
|
||||
"style": "style_32",
|
||||
"assets": [
|
||||
("magic_demon", f"{STYLE_32_BASE}Small demon creature, red skin with tiny horns, pointed tail, pitchfork weapon, mischievous grin, clawed feet, adorably evil. {COMMON_SUFFIX}"),
|
||||
("magic_imp", f"{STYLE_32_BASE}Mischievous imp with bat wings, green goblin skin, pointy ears, sharp teeth grin, crouching pose, troublemaker aura. {COMMON_SUFFIX}"),
|
||||
("magic_wraith", f"{STYLE_32_BASE}Dark wraith spirit, shadowy hooded figure with no face visible, tattered robes flowing, skeletal hands extending, floating pose with wispy trails, death incarnate. {COMMON_SUFFIX}"),
|
||||
("magic_familiar", f"{STYLE_32_BASE}Black cat familiar with glowing yellow eyes, sleek fur, mystical aura, sitting with tail curled, witch's companion, magical energy swirls. {COMMON_SUFFIX}"),
|
||||
("magic_golem", f"{STYLE_32_BASE}Stone golem, heavy blocky rock body, runes glowing on chest, moss growing on shoulders, powerful fists, slow lumbering stance, ancient guardian. {COMMON_SUFFIX}"),
|
||||
]
|
||||
},
|
||||
|
||||
"npc/policija": {
|
||||
"style": "style_32",
|
||||
"assets": [
|
||||
("police_chief", f"{STYLE_32_BASE}Police chief NPC, stern middle-aged person in decorated uniform, police cap with badge, medals on chest, hands on hips authoritative pose, graying hair, law enforcement leader. {COMMON_SUFFIX}"),
|
||||
("police_officer", f"{STYLE_32_BASE}Regular police officer NPC, standard blue uniform, police cap, badge visible, utility belt with radio, friendly but professional expression, community protector. {COMMON_SUFFIX}"),
|
||||
("police_corrupt", f"{STYLE_32_BASE}Corrupt cop NPC, disheveled uniform, suspicious shifty eyes, sinister grin, holding bribe money behind back, morally gray police officer. {COMMON_SUFFIX}"),
|
||||
]
|
||||
},
|
||||
|
||||
"npc/zdravstvo": {
|
||||
"style": "style_32",
|
||||
"assets": [
|
||||
("doctor_main", f"{STYLE_32_BASE}Town doctor NPC, friendly physician in white coat, stethoscope around neck, kind wise expression, glasses, holding medical clipboard, trusted healer. {COMMON_SUFFIX}"),
|
||||
("nurse_helper", f"{STYLE_32_BASE}Nurse assistant NPC, medical scrubs and cap, caring expression, holding first aid kit, supportive pose, healthcare worker. {COMMON_SUFFIX}"),
|
||||
]
|
||||
},
|
||||
|
||||
"npc/trgovci": {
|
||||
"style": "style_32",
|
||||
"assets": [
|
||||
("merchant_general", f"{STYLE_32_BASE}General store merchant NPC, apron over casual clothes, welcoming smile, holding product, behind counter pose, friendly shopkeeper. {COMMON_SUFFIX}"),
|
||||
("merchant_seeds", f"{STYLE_32_BASE}Seed merchant NPC, farmer's clothes with seed packets, straw hat, holding vegetable seeds, dirt on hands, agricultural expert. {COMMON_SUFFIX}"),
|
||||
("merchant_black_market", f"{STYLE_32_BASE}Shady black market dealer NPC, dark hood concealing face, mysterious aura, holding questionable goods, lurking in shadows, underground trader. {COMMON_SUFFIX}"),
|
||||
]
|
||||
},
|
||||
|
||||
"npc/mentorji": {
|
||||
"style": "style_32",
|
||||
"assets": [
|
||||
("mentor_farming", f"{STYLE_32_BASE}Old farmer mentor NPC, elderly with weathered face, overalls and straw hat, wise smile, leaning on hoe, years of experience visible. {COMMON_SUFFIX}"),
|
||||
("mentor_magic", f"{STYLE_32_BASE}Dark magic mentor NPC, robed figure with mystical symbols, glowing hands with magical energy, ancient tome under arm, sinister wisdom. {COMMON_SUFFIX}"),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ProductionAssetGenerator:
|
||||
"""Professional asset generator with proper style consistency."""
|
||||
|
||||
def __init__(self):
|
||||
print("=" * 70)
|
||||
print("🎨 DOLINASMRTI PRODUCTION ASSET GENERATOR V2")
|
||||
print("=" * 70)
|
||||
print()
|
||||
|
||||
# Initialize Vertex AI
|
||||
vertexai.init(project=PROJECT_ID, location=LOCATION)
|
||||
self.model = ImageGenerationModel.from_pretrained("imagen-3.0-generate-001")
|
||||
|
||||
# Load progress
|
||||
self.progress = self.load_progress()
|
||||
|
||||
# Stats
|
||||
self.session_count = 0
|
||||
self.session_failed = 0
|
||||
|
||||
# Open log file
|
||||
self.log = open(LOG_FILE, 'a')
|
||||
self.log.write(f"\n\n{'='*70}\n")
|
||||
self.log.write(f"Session started: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
self.log.write(f"{'='*70}\n\n")
|
||||
|
||||
def __del__(self):
|
||||
"""Cleanup."""
|
||||
if hasattr(self, 'log'):
|
||||
self.log.close()
|
||||
|
||||
def load_progress(self) -> Dict:
|
||||
"""Load generation progress."""
|
||||
if PROGRESS_FILE.exists():
|
||||
with open(PROGRESS_FILE, 'r') as f:
|
||||
return json.load(f)
|
||||
return {"completed": [], "failed": [], "total_generated": 0}
|
||||
|
||||
def save_progress(self):
|
||||
"""Save current progress."""
|
||||
self.progress["total_generated"] = len(self.progress["completed"])
|
||||
with open(PROGRESS_FILE, 'w') as f:
|
||||
json.dump(self.progress, f, indent=2)
|
||||
|
||||
def is_completed(self, asset_id: str) -> bool:
|
||||
"""Check if asset already generated."""
|
||||
return asset_id in self.progress["completed"]
|
||||
|
||||
def log_message(self, message: str):
|
||||
"""Write to log file and console."""
|
||||
print(message)
|
||||
self.log.write(message + "\n")
|
||||
self.log.flush()
|
||||
|
||||
def generate_asset(self, category_path: str, filename: str, prompt: str) -> bool:
|
||||
"""Generate single asset with retry logic."""
|
||||
asset_id = f"{category_path}/{filename}"
|
||||
|
||||
# Skip if completed
|
||||
if self.is_completed(asset_id):
|
||||
self.log_message(f" ⏭️ SKIP: {filename} (already done)")
|
||||
return True
|
||||
|
||||
self.log_message(f" 🎨 GENERATING: {filename}")
|
||||
self.log_message(f" Prompt: {prompt[:100]}...")
|
||||
|
||||
try:
|
||||
# Generate image
|
||||
response = self.model.generate_images(
|
||||
prompt=prompt,
|
||||
number_of_images=1,
|
||||
aspect_ratio="1:1",
|
||||
safety_filter_level="block_few",
|
||||
person_generation="allow_adult"
|
||||
)
|
||||
|
||||
if response.images:
|
||||
# Save to correct folder
|
||||
output_dir = BASE_PATH / category_path
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
output_path = output_dir / f"{filename}.png"
|
||||
|
||||
response.images[0].save(location=str(output_path))
|
||||
|
||||
self.log_message(f" ✅ SUCCESS: Saved to {output_path}")
|
||||
self.progress["completed"].append(asset_id)
|
||||
self.session_count += 1
|
||||
self.save_progress()
|
||||
return True
|
||||
else:
|
||||
self.log_message(f" ❌ FAILED: No image returned")
|
||||
self.progress["failed"].append(asset_id)
|
||||
self.session_failed += 1
|
||||
self.save_progress()
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.log_message(f" ❌ ERROR: {str(e)}")
|
||||
self.progress["failed"].append(asset_id)
|
||||
self.session_failed += 1
|
||||
self.save_progress()
|
||||
return False
|
||||
|
||||
def generate_category(self, category_path: str, assets: List):
|
||||
"""Generate all assets in a category."""
|
||||
self.log_message(f"\n{'='*70}")
|
||||
self.log_message(f"📁 CATEGORY: {category_path}")
|
||||
self.log_message(f"{'='*70}\n")
|
||||
|
||||
for filename, prompt in assets:
|
||||
self.generate_asset(category_path, filename, prompt)
|
||||
|
||||
# Rate limiting
|
||||
time.sleep(1.5)
|
||||
|
||||
def generate_phase_2(self):
|
||||
"""Generate Phase 2: Creatures & NPCs."""
|
||||
self.log_message("\n" + "🔥"*35)
|
||||
self.log_message("PHASE 2: CREATURES & NPCs (Style 32)")
|
||||
self.log_message("🔥"*35 + "\n")
|
||||
|
||||
for category, data in PHASE_2_CREATURES.items():
|
||||
self.generate_category(category, data["assets"])
|
||||
|
||||
self.log_message(f"\n{'='*70}")
|
||||
self.log_message("✅ PHASE 2 COMPLETE!")
|
||||
self.log_message(f" Session generated: {self.session_count}")
|
||||
self.log_message(f" Session failed: {self.session_failed}")
|
||||
self.log_message(f" Total complete: {len(self.progress['completed'])}")
|
||||
self.log_message(f"{'='*70}\n")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
generator = ProductionAssetGenerator()
|
||||
|
||||
try:
|
||||
generator.generate_phase_2()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n⚠️ Interrupted - Progress saved!")
|
||||
except Exception as e:
|
||||
print(f"\n❌ Fatal error: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
119
scripts/slow_generator.py
Normal file
119
scripts/slow_generator.py
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SLOW MODE GENERATOR - Works within quota limits
|
||||
Generates 1 image every 3 minutes = 20 images per hour = safe!
|
||||
"""
|
||||
|
||||
import time
|
||||
import vertexai
|
||||
from vertexai.preview.vision_models import ImageGenerationModel
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_ID = "gen-lang-client-0428644398"
|
||||
LOCATION = "us-central1"
|
||||
BASE_PATH = Path("/Users/davidkotnik/repos/novafarma/assets/slike")
|
||||
|
||||
# Style 32 detailed
|
||||
STYLE_32 = """Style 32: Cult of the Lamb cute-dark aesthetic. CRITICAL: Bold thick black outlines (3px), chibi proportions (head = 1/3 body), pastel-gothic colors (dusty pink #E8B4D4, muted purple #9B7FB5, cream #F5EFE6, noir shadows #2D2D2D), big expressive eyes (1/4 of face), smooth gradients, cute-dark contradiction. """
|
||||
|
||||
COMMON = """2D game sprite, pixel-perfect clarity, centered with 10px margin, solid chroma green background (#00FF00)."""
|
||||
|
||||
# Priority assets - most important first
|
||||
PRIORITY_QUEUE = [
|
||||
# Zombies (most important for gameplay)
|
||||
("kreature/zombiji", "zombie_basic_idle", f"{STYLE_32}Basic shambling zombie, rotting flesh, exposed ribs, tattered clothing, blank eyes. IDLE: standing still, slight sway. {COMMON}"),
|
||||
("kreature/zombiji", "zombie_basic_walk", f"{STYLE_32}Basic shambling zombie, rotting flesh, exposed ribs, tattered clothing. WALKING forward, shambling gait, Stardew Valley hop style. {COMMON}"),
|
||||
("kreature/zombiji", "zombie_basic_attack", f"{STYLE_32}Basic shambling zombie, rotting flesh, exposed ribs. ATTACKING: arms extended forward, bite motion. {COMMON}"),
|
||||
|
||||
("kreature/zombiji", "zombie_runner_idle", f"{STYLE_32}Fast aggressive zombie, sprinting pose, lean athletic build, wild hair, crazed expression. IDLE: aggressive stance, ready to run. {COMMON}"),
|
||||
("kreature/zombiji", "zombie_runner_walk", f"{STYLE_32}Fast aggressive zombie, lean build, wild hair. RUNNING: full sprint pose, speed motion. {COMMON}"),
|
||||
("kreature/zombiji", "zombie_runner_attack", f"{STYLE_32}Fast aggressive zombie. ATTACKING: diving tackle lunge. {COMMON}"),
|
||||
|
||||
("kreature/zombiji", "zombie_tank_idle", f"{STYLE_32}Massive bulky zombie, muscular frame, cracked skin, intimidating chunky. IDLE: heavy breathing, powerful stance. {COMMON}"),
|
||||
("kreature/zombiji", "zombie_tank_walk", f"{STYLE_32}Massive bulky zombie, muscular. WALKING: slow powerful stomp. {COMMON}"),
|
||||
("kreature/zombiji", "zombie_tank_attack", f"{STYLE_32}Massive bulky zombie. ATTACKING: overhead smash motion. {COMMON}"),
|
||||
|
||||
# Animals (farm basics)
|
||||
("kreature/zivali", "chicken_idle", f"{STYLE_32}Cute farm chicken, fluffy feathers, chunky body, big eyes. IDLE: standing, pecking ground. {COMMON}"),
|
||||
("kreature/zivali", "chicken_walk", f"{STYLE_32}Cute farm chicken, fluffy. WALKING: waddle gait. {COMMON}"),
|
||||
|
||||
("kreature/zivali", "cow_idle", f"{STYLE_32}Farm cow, spotted pattern, big eyes, friendly. IDLE: standing, chewing. {COMMON}"),
|
||||
("kreature/zivali", "cow_walk", f"{STYLE_32}Farm cow, spotted. WALKING: slow amble. {COMMON}"),
|
||||
|
||||
# NPCs (basic town)
|
||||
("npc/trgovci", "merchant_general", f"{STYLE_32}General store merchant, apron over casual clothes, welcoming smile, friendly shopkeeper. IDLE pose behind counter. {COMMON}"),
|
||||
("npc/zdravstvo", "doctor_main", f"{STYLE_32}Town doctor, white coat, stethoscope, glasses, kind wise expression. IDLE pose standing. {COMMON}"),
|
||||
]
|
||||
|
||||
def generate_slow():
|
||||
"""Generate assets slowly to stay within quota."""
|
||||
|
||||
print("=" * 70)
|
||||
print("🐌 SLOW MODE GENERATOR")
|
||||
print("=" * 70)
|
||||
print("Rate: 1 image every 3 minutes")
|
||||
print(f"Queue: {len(PRIORITY_QUEUE)} assets")
|
||||
print(f"Total time: ~{len(PRIORITY_QUEUE) * 3} minutes")
|
||||
print("=" * 70)
|
||||
print()
|
||||
|
||||
vertexai.init(project=PROJECT_ID, location=LOCATION)
|
||||
model = ImageGenerationModel.from_pretrained("imagen-3.0-generate-001")
|
||||
|
||||
success = 0
|
||||
failed = 0
|
||||
|
||||
for i, (category, filename, prompt) in enumerate(PRIORITY_QUEUE, 1):
|
||||
print(f"[{i}/{len(PRIORITY_QUEUE)}] {category}/{filename}")
|
||||
print(f" Generating...")
|
||||
|
||||
try:
|
||||
response = model.generate_images(
|
||||
prompt=prompt,
|
||||
number_of_images=1,
|
||||
aspect_ratio="1:1",
|
||||
safety_filter_level="block_few",
|
||||
person_generation="allow_adult"
|
||||
)
|
||||
|
||||
if response.images:
|
||||
# Save
|
||||
output_dir = BASE_PATH / category
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
output_path = output_dir / f"{filename}.png"
|
||||
|
||||
response.images[0].save(location=str(output_path))
|
||||
print(f" ✅ SAVED: {output_path}")
|
||||
success += 1
|
||||
else:
|
||||
print(f" ❌ FAILED: No image returned")
|
||||
failed += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ ERROR: {e}")
|
||||
failed += 1
|
||||
|
||||
if "429" in str(e) or "Quota" in str(e):
|
||||
print(f" ⏰ QUOTA HIT - Waiting 10 minutes...")
|
||||
time.sleep(600) # Wait 10 min if quota hit
|
||||
continue
|
||||
|
||||
# Progress
|
||||
print(f" Progress: {success} success, {failed} failed")
|
||||
print()
|
||||
|
||||
# SLOW DOWN - 3 minutes between images
|
||||
if i < len(PRIORITY_QUEUE):
|
||||
print(f" ⏰ Waiting 3 minutes before next image...")
|
||||
time.sleep(180) # 3 minutes
|
||||
print()
|
||||
|
||||
# Summary
|
||||
print("=" * 70)
|
||||
print("✅ SLOW GENERATION COMPLETE!")
|
||||
print(f" Success: {success}")
|
||||
print(f" Failed: {failed}")
|
||||
print("=" * 70)
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_slow()
|
||||
286
scripts/spritesheet_generator_v3.py
Normal file
286
scripts/spritesheet_generator_v3.py
Normal file
@@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DOLINASMRTI SPRITESHEET GENERATOR V3
|
||||
Generates complete animation spritesheets for all game entities.
|
||||
Style 32 for characters/creatures, Style 30 for plants.
|
||||
Stardew Valley inspired animation system.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
import vertexai
|
||||
from vertexai.preview.vision_models import ImageGenerationModel
|
||||
|
||||
# === CONFIGURATION ===
|
||||
PROJECT_ID = "gen-lang-client-0428644398"
|
||||
LOCATION = "us-central1"
|
||||
BASE_PATH = Path("/Users/davidkotnik/repos/novafarma/assets/slike")
|
||||
PROGRESS_FILE = Path("/Users/davidkotnik/repos/novafarma/.spritesheet_progress.json")
|
||||
LOG_FILE = Path("/Users/davidkotnik/repos/novafarma/spritesheet_generation.log")
|
||||
|
||||
# === STYLE 32: CULT OF THE LAMB (PROPER SPECS) ===
|
||||
STYLE_32_DETAILED = """Style 32: Cult of the Lamb cute-dark aesthetic. CRITICAL REQUIREMENTS: Bold thick black outlines (2-3px), chibi proportions (head = 1/3 of body height), pastel-gothic color palette (dusty pink #E8B4D4, muted purple #9B7FB5, cream white #F5EFE6, soft gray #B8B8B8 with noir black shadows #2D2D2D), big expressive eyes (1/4 of face), smooth gradients on rounded forms, contradiction between adorable shapes and dark themes. Stardew Valley inspired pixel-perfect clarity. """
|
||||
|
||||
# === STYLE 30: GARDEN STORY COZY ===
|
||||
STYLE_30_DETAILED = """Style 30: Garden Story cozy adventure. Soft rounded organic shapes, pastel-vibrant balance (cheerful greens #A8D5BA, warm yellows #F7DC6F, fresh oranges #F8B739), wholesome mature aesthetic (not childish), Link's Awakening charm with modern indie polish, clean vector edges with subtle texture. """
|
||||
|
||||
COMMON_SUFFIX = """2D game sprite, pixel-perfect clarity, centered composition with 10px margin, consistent top-down lighting, solid chroma green background (#00FF00)."""
|
||||
|
||||
# === SPRITESHEET DEFINITIONS ===
|
||||
|
||||
CREATURE_SPRITESHEETS = {
|
||||
"kreature/zombiji": {
|
||||
"style": STYLE_32_DETAILED,
|
||||
"entities": [
|
||||
{
|
||||
"name": "zombie_basic",
|
||||
"description": "Basic shambling zombie, rotting flesh, exposed ribs, tattered clothing, blank white eyes, slow gait",
|
||||
"animations": {
|
||||
"idle": "Standing idle, slight body sway, 2 frames",
|
||||
"walk_south": "Walking toward camera, shambling gait, 4 frames",
|
||||
"walk_north": "Walking away, shambling, 4 frames",
|
||||
"walk_east": "Walking right, side view shamble, 4 frames",
|
||||
"walk_west": "Walking left, mirror of east walk, 4 frames",
|
||||
"attack": "Lunge forward with arms extended, bite motion, 3 frames"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "zombie_runner",
|
||||
"description": "Fast aggressive zombie, sprinting pose, lean athletic build, wild hair, crazed expression",
|
||||
"animations": {
|
||||
"idle": "Aggressive stance, heavy breathing, 2 frames",
|
||||
"walk_south": "Running sprint toward camera, 4 frames",
|
||||
"walk_north": "Sprinting away, 4 frames",
|
||||
"walk_east": "Fast run right, 4 frames",
|
||||
"walk_west": "Fast run left, 4 frames",
|
||||
"attack": "Diving tackle motion, 3 frames"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "zombie_tank",
|
||||
"description": "Massive bulky zombie, muscular frame, cracked skin, intimidating but cute chunky",
|
||||
"animations": {
|
||||
"idle": "Heavy breathing, arms at sides, 2 frames",
|
||||
"walk_south": "Slow powerful stomp forward, 4 frames",
|
||||
"walk_north": "Lumbering away, 4 frames",
|
||||
"walk_east": "Heavy walk right, 4 frames",
|
||||
"walk_west": "Heavy walk left, 4 frames",
|
||||
"attack": "Overhead smash motion, 3 frames"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"kreature/dinozavri": {
|
||||
"style": STYLE_32_DETAILED,
|
||||
"entities": [
|
||||
{
|
||||
"name": "dino_raptor",
|
||||
"description": "Velociraptor, intelligent predator, sickle claw raised, sleek feathered body, cunning eyes",
|
||||
"animations": {
|
||||
"idle": "Alert stance, head bobbing, 2 frames",
|
||||
"walk_south": "Hopping gait toward camera, Stardew Valley style, 4 frames",
|
||||
"walk_north": "Hopping away, tail balance, 4 frames",
|
||||
"walk_east": "Side-view hop right, 4 frames",
|
||||
"walk_west": "Side-view hop left, 4 frames",
|
||||
"attack": "Sickle claw slash motion, 3 frames"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dino_trex",
|
||||
"description": "T-Rex, massive head, tiny arms, powerful legs, roaring pose, chunky cute-terrifying",
|
||||
"animations": {
|
||||
"idle": "Standing tall, slight head sway, 2 frames",
|
||||
"walk_south": "Heavy stomp forward, ground shake, 4 frames",
|
||||
"walk_north": "Stomping away, 4 frames",
|
||||
"walk_east": "Side stomp right, 4 frames",
|
||||
"walk_west": "Side stomp left, 4 frames",
|
||||
"attack": "Massive bite lunge, jaws wide, 3 frames"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"kreature/zivali": {
|
||||
"style": STYLE_32_DETAILED,
|
||||
"entities": [
|
||||
{
|
||||
"name": "animal_bear",
|
||||
"description": "Brown bear, thick fur, rounded ears, big paws, intimidating but huggable",
|
||||
"animations": {
|
||||
"idle": "Standing on hind legs, gentle sway, 2 frames",
|
||||
"walk_south": "Four-legged lumbering walk forward, 4 frames",
|
||||
"walk_north": "Lumbering away, 4 frames",
|
||||
"walk_east": "Side walk right, 4 frames",
|
||||
"walk_west": "Side walk left, 4 frames",
|
||||
"attack": "Swipe with paw, standing tall, 3 frames"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "animal_wolf",
|
||||
"description": "Gray wolf, sleek predator, sharp teeth, pointed ears, fierce yet cute",
|
||||
"animations": {
|
||||
"idle": "Alert stance, ears perked, 2 frames",
|
||||
"walk_south": "Prowling walk forward, 4 frames",
|
||||
"walk_north": "Prowl away, 4 frames",
|
||||
"walk_east": "Side prowl right, 4 frames",
|
||||
"walk_west": "Side prowl left, 4 frames",
|
||||
"attack": "Lunge bite motion, 3 frames"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SpritesheetGenerator:
|
||||
"""Generates complete animation spritesheets for game entities."""
|
||||
|
||||
def __init__(self):
|
||||
print("=" * 70)
|
||||
print("🎮 DOLINASMRTI SPRITESHEET GENERATOR V3")
|
||||
print("=" * 70)
|
||||
print("Features: Full animation support, Style 32 accurate, Auto-organization")
|
||||
print()
|
||||
|
||||
# Initialize Vertex AI
|
||||
vertexai.init(project=PROJECT_ID, location=LOCATION)
|
||||
self.model = ImageGenerationModel.from_pretrained("imagen-3.0-generate-001")
|
||||
|
||||
# Load progress
|
||||
self.progress = self.load_progress()
|
||||
|
||||
# Stats
|
||||
self.session_count = 0
|
||||
self.session_failed = 0
|
||||
|
||||
# Logging
|
||||
self.log = open(LOG_FILE, 'a')
|
||||
self.log_message(f"\n{'='*70}")
|
||||
self.log_message(f"Session started: {time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
self.log_message(f"{'='*70}\n")
|
||||
|
||||
def __del__(self):
|
||||
if hasattr(self, 'log'):
|
||||
self.log.close()
|
||||
|
||||
def load_progress(self) -> Dict:
|
||||
if PROGRESS_FILE.exists():
|
||||
with open(PROGRESS_FILE, 'r') as f:
|
||||
return json.load(f)
|
||||
return {"completed": [], "failed": []}
|
||||
|
||||
def save_progress(self):
|
||||
with open(PROGRESS_FILE, 'w') as f:
|
||||
json.dump(self.progress, f, indent=2)
|
||||
|
||||
def is_completed(self, asset_id: str) -> bool:
|
||||
return asset_id in self.progress["completed"]
|
||||
|
||||
def log_message(self, msg: str):
|
||||
print(msg)
|
||||
self.log.write(msg + "\n")
|
||||
self.log.flush()
|
||||
|
||||
def generate_animation_frame(self, entity_name: str, animation_name: str,
|
||||
description: str, style: str) -> bool:
|
||||
"""Generate single animation frame."""
|
||||
|
||||
asset_id = f"{entity_name}_{animation_name}"
|
||||
|
||||
if self.is_completed(asset_id):
|
||||
self.log_message(f" ⏭️ SKIP: {animation_name} (done)")
|
||||
return True
|
||||
|
||||
# Build complete prompt
|
||||
full_prompt = f"{style}{description}, {animation_name}. {COMMON_SUFFIX}"
|
||||
|
||||
self.log_message(f" 🎨 {animation_name}")
|
||||
|
||||
try:
|
||||
response = self.model.generate_images(
|
||||
prompt=full_prompt,
|
||||
number_of_images=1,
|
||||
aspect_ratio="1:1",
|
||||
safety_filter_level="block_few",
|
||||
person_generation="allow_adult"
|
||||
)
|
||||
|
||||
if response.images:
|
||||
# Save frame
|
||||
# Extract category from entity metadata (would need to pass this)
|
||||
# For now, save to temp location
|
||||
output_path = Path(f"/tmp/{entity_name}_{animation_name}.png")
|
||||
response.images[0].save(location=str(output_path))
|
||||
|
||||
self.log_message(f" ✅ Saved")
|
||||
self.progress["completed"].append(asset_id)
|
||||
self.session_count += 1
|
||||
self.save_progress()
|
||||
return True
|
||||
else:
|
||||
self.log_message(f" ❌ No image")
|
||||
self.progress["failed"].append(asset_id)
|
||||
self.session_failed += 1
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.log_message(f" ❌ ERROR: {str(e)}")
|
||||
self.progress["failed"].append(asset_id)
|
||||
self.session_failed += 1
|
||||
return False
|
||||
|
||||
def generate_entity(self, category: str, entity: Dict, style: str):
|
||||
"""Generate complete spritesheet for one entity."""
|
||||
|
||||
name = entity["name"]
|
||||
desc = entity["description"]
|
||||
animations = entity["animations"]
|
||||
|
||||
self.log_message(f"\n 🎯 ENTITY: {name}")
|
||||
self.log_message(f" {desc}")
|
||||
self.log_message(f" Animations: {len(animations)}")
|
||||
|
||||
for anim_name, anim_desc in animations.items():
|
||||
full_desc = f"{desc}, {anim_desc}"
|
||||
self.generate_animation_frame(name, anim_name, full_desc, style)
|
||||
time.sleep(2) # Rate limiting
|
||||
|
||||
def generate_all_spritesheets(self):
|
||||
"""Generate all entity spritesheets."""
|
||||
|
||||
self.log_message("\n🔥 STARTING SPRITESHEET GENERATION")
|
||||
self.log_message("="*70 + "\n")
|
||||
|
||||
for category, data in CREATURE_SPRITESHEETS.items():
|
||||
self.log_message(f"\n📁 CATEGORY: {category}")
|
||||
self.log_message("="*70)
|
||||
|
||||
for entity in data["entities"]:
|
||||
self.generate_entity(category, entity, data["style"])
|
||||
|
||||
# Summary
|
||||
self.log_message(f"\n{'='*70}")
|
||||
self.log_message("✅ GENERATION COMPLETE!")
|
||||
self.log_message(f" Session generated: {self.session_count}")
|
||||
self.log_message(f" Session failed: {self.session_failed}")
|
||||
self.log_message(f"{'='*70}\n")
|
||||
|
||||
|
||||
def main():
|
||||
generator = SpritesheetGenerator()
|
||||
|
||||
try:
|
||||
generator.generate_all_spritesheets()
|
||||
except KeyboardInterrupt:
|
||||
print("\n⚠️ Interrupted - Progress saved!")
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user