Spaces:
Runtime error
Runtime error
Update forge-texture from local forge-bricks
Browse files- README.md +4 -12
- app.py +93 -0
- pyproject.toml +19 -0
- requirements.txt +53 -0
README.md
CHANGED
|
@@ -1,13 +1,5 @@
|
|
| 1 |
-
-
|
| 2 |
-
title: Forge Texture
|
| 3 |
-
emoji: 📊
|
| 4 |
-
colorFrom: blue
|
| 5 |
-
colorTo: purple
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version: 6.18.0
|
| 8 |
-
python_version: '3.13'
|
| 9 |
-
app_file: app.py
|
| 10 |
-
pinned: false
|
| 11 |
-
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
| 1 |
+
# forge-texture
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
+
PBR texture brick for game assets.
|
| 4 |
+
|
| 5 |
+
See root README and plan for usage and deployment.
|
app.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Forge-Texture Brick — PBR texture generation.
|
| 3 |
+
|
| 4 |
+
Inputs: text description or reference image + style.
|
| 5 |
+
Outputs: albedo, normal, roughness, metallic, AO maps + combined manifest.
|
| 6 |
+
|
| 7 |
+
Uses health-checked image model + post-processing guidance for game PBR.
|
| 8 |
+
"""
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
import os
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
from datetime import datetime
|
| 13 |
+
import gradio as gr
|
| 14 |
+
import spaces
|
| 15 |
+
from PIL import Image
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
from ..common.manifest import create_manifest
|
| 19 |
+
from ..common.health import SpaceHealth
|
| 20 |
+
except Exception:
|
| 21 |
+
import sys
|
| 22 |
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
| 23 |
+
from common.manifest import create_manifest
|
| 24 |
+
from common.health import SpaceHealth
|
| 25 |
+
|
| 26 |
+
health = SpaceHealth()
|
| 27 |
+
|
| 28 |
+
TARGET = "black-forest-labs/FLUX.1-schnell" # strong base for PBR prompting
|
| 29 |
+
|
| 30 |
+
def _out() -> Path:
|
| 31 |
+
d = Path(os.environ.get("FORGE_BRICKS_OUTPUT", "./outputs/forge_texture"))
|
| 32 |
+
d.mkdir(parents=True, exist_ok=True)
|
| 33 |
+
return d
|
| 34 |
+
|
| 35 |
+
@spaces.GPU(duration=90)
|
| 36 |
+
def generate_pbr(prompt: str, style_ref: Image.Image | None = None, resolution: int = 1024) -> dict:
|
| 37 |
+
out_dir = _out()
|
| 38 |
+
ts = int(datetime.now().timestamp())
|
| 39 |
+
|
| 40 |
+
base = prompt + ", seamless tileable PBR texture, albedo map, high detail, game asset, no text"
|
| 41 |
+
|
| 42 |
+
# Primary: call good image model with strong PBR prompt (in real use, better multi-map models or SDXL control)
|
| 43 |
+
albedo_path = None
|
| 44 |
+
try:
|
| 45 |
+
if health.is_ok(TARGET) is not False:
|
| 46 |
+
from gradio_client import Client
|
| 47 |
+
client = Client(TARGET)
|
| 48 |
+
res = client.predict(prompt=base, seed=-1, randomize_seed=True, width=resolution, height=resolution, num_inference_steps=6, api_name="/infer")
|
| 49 |
+
if isinstance(res, (list, tuple)):
|
| 50 |
+
p = res[0] if isinstance(res[0], str) else res[0].get("path")
|
| 51 |
+
if p:
|
| 52 |
+
albedo_path = str(out_dir / f"albedo_{ts}.png")
|
| 53 |
+
Image.open(p).save(albedo_path)
|
| 54 |
+
except Exception as e:
|
| 55 |
+
print("Texture gen note:", e)
|
| 56 |
+
|
| 57 |
+
# For scaffold: also produce placeholder normal/rough etc.
|
| 58 |
+
maps = {}
|
| 59 |
+
for m in ["albedo", "normal", "roughness", "metallic", "ao"]:
|
| 60 |
+
p = out_dir / f"{m}_{ts}.png"
|
| 61 |
+
if m == "albedo" and albedo_path:
|
| 62 |
+
Image.open(albedo_path).save(p)
|
| 63 |
+
else:
|
| 64 |
+
Image.new("RGB", (resolution, resolution), (128, 128, 128)).save(p)
|
| 65 |
+
maps[m] = str(p)
|
| 66 |
+
|
| 67 |
+
manifest = create_manifest(
|
| 68 |
+
name=prompt[:50].replace(" ", "_"),
|
| 69 |
+
type="texture_pbr",
|
| 70 |
+
source_brick="forge-texture",
|
| 71 |
+
prompt_or_desc=prompt,
|
| 72 |
+
files=maps,
|
| 73 |
+
params={"resolution": resolution},
|
| 74 |
+
metadata={"maps": list(maps.keys())},
|
| 75 |
+
commercial_ok=True,
|
| 76 |
+
)
|
| 77 |
+
manifest.save(out_dir / f"manifest_{ts}.json")
|
| 78 |
+
|
| 79 |
+
return {"maps": maps, "manifest": manifest.to_dict()}
|
| 80 |
+
|
| 81 |
+
def build_ui():
|
| 82 |
+
with gr.Blocks(title="Forge-Texture") as demo:
|
| 83 |
+
gr.Markdown("# Forge-Texture (PBR Maps)")
|
| 84 |
+
p = gr.Textbox("ancient stone brick wall, mossy, medieval game asset")
|
| 85 |
+
ref = gr.Image(label="Optional style ref", type="pil")
|
| 86 |
+
res = gr.Slider(512, 2048, value=1024, step=256, label="Resolution")
|
| 87 |
+
btn = gr.Button("Generate PBR Set")
|
| 88 |
+
out = gr.JSON()
|
| 89 |
+
btn.click(generate_pbr, [p, ref, res], out)
|
| 90 |
+
return demo
|
| 91 |
+
|
| 92 |
+
if __name__ == "__main__":
|
| 93 |
+
build_ui().launch(server_name="0.0.0.0", server_port=7863)
|
pyproject.toml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "forge-texture"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "Forge-Texture: Text/image → high quality PBR texture maps for game assets"
|
| 5 |
+
readme = "README.md"
|
| 6 |
+
requires-python = ">=3.10"
|
| 7 |
+
dependencies = [
|
| 8 |
+
"gradio>=5.0,<7.0",
|
| 9 |
+
"spaces",
|
| 10 |
+
"pillow",
|
| 11 |
+
"gradio-client",
|
| 12 |
+
"huggingface_hub",
|
| 13 |
+
"forge-bricks-common",
|
| 14 |
+
]
|
| 15 |
+
[build-system]
|
| 16 |
+
requires = ["hatchling"]
|
| 17 |
+
build-backend = "hatchling.build"
|
| 18 |
+
[tool.uv.sources]
|
| 19 |
+
forge-bricks-common = { workspace = true }
|
requirements.txt
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
annotated-doc==0.0.4
|
| 2 |
+
annotated-types==0.7.0
|
| 3 |
+
anyio==4.14.0
|
| 4 |
+
brotli==1.2.0
|
| 5 |
+
certifi==2026.5.20
|
| 6 |
+
charset-normalizer==3.4.7
|
| 7 |
+
click==8.4.1
|
| 8 |
+
fastapi==0.137.1
|
| 9 |
+
filelock==3.29.4
|
| 10 |
+
fsspec==2026.6.0
|
| 11 |
+
gradio==6.18.0
|
| 12 |
+
gradio-client==2.5.0
|
| 13 |
+
groovy==0.1.2
|
| 14 |
+
h11==0.16.0
|
| 15 |
+
hf-gradio==0.4.1
|
| 16 |
+
hf-xet==1.5.1
|
| 17 |
+
httpcore==1.0.9
|
| 18 |
+
httpx==0.28.1
|
| 19 |
+
huggingface-hub==1.19.0
|
| 20 |
+
idna==3.18
|
| 21 |
+
jinja2==3.1.6
|
| 22 |
+
markdown-it-py==4.2.0
|
| 23 |
+
markupsafe==3.0.3
|
| 24 |
+
mdurl==0.1.2
|
| 25 |
+
numpy==2.4.6
|
| 26 |
+
orjson==3.11.9
|
| 27 |
+
packaging==26.2
|
| 28 |
+
pandas==3.0.3
|
| 29 |
+
pillow==12.2.0
|
| 30 |
+
pydantic==2.13.4
|
| 31 |
+
pydantic-core==2.46.4
|
| 32 |
+
pydub==0.25.1
|
| 33 |
+
pygments==2.20.0
|
| 34 |
+
python-dateutil==2.9.0.post0
|
| 35 |
+
python-multipart==0.0.32
|
| 36 |
+
pytz==2026.2
|
| 37 |
+
pyyaml==6.0.3
|
| 38 |
+
requests==2.34.2
|
| 39 |
+
rich==15.0.0
|
| 40 |
+
safehttpx==0.1.7
|
| 41 |
+
semantic-version==2.10.0
|
| 42 |
+
shellingham==1.5.4
|
| 43 |
+
six==1.17.0
|
| 44 |
+
spaces==0.50.4
|
| 45 |
+
starlette==1.3.1
|
| 46 |
+
tomlkit==0.14.0
|
| 47 |
+
tqdm==4.68.2
|
| 48 |
+
trimesh==4.12.2
|
| 49 |
+
typer==0.25.1
|
| 50 |
+
typing-extensions==4.15.0
|
| 51 |
+
typing-inspection==0.4.2
|
| 52 |
+
urllib3==2.7.0
|
| 53 |
+
uvicorn==0.49.0
|