Spaces:
Configuration error
Configuration error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,71 +1,104 @@
|
|
|
|
|
| 1 |
import os
|
| 2 |
-
os.environ["CUDA_VISIBLE_DEVICES"] = "" # 強制 CPU
|
| 3 |
-
os.environ["WORLDGEN_DISABLE_EVAL"] = "1" # 若程式支援,避免 KNN
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
|
| 8 |
-
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
from pathlib import Path
|
| 15 |
-
|
|
|
|
|
|
|
| 16 |
from worldgen import WorldGen
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
def _zip_directory(dir_path: Path) -> str:
|
| 19 |
-
zip_path = dir_path.with_suffix(
|
| 20 |
if zip_path.exists():
|
| 21 |
zip_path.unlink()
|
| 22 |
-
shutil.make_archive(str(dir_path),
|
| 23 |
return str(zip_path)
|
| 24 |
|
|
|
|
| 25 |
def _save_scene(generated, out_dir: Path) -> Path:
|
| 26 |
out_dir.mkdir(parents=True, exist_ok=True)
|
| 27 |
-
|
|
|
|
|
|
|
| 28 |
if hasattr(generated, meth):
|
| 29 |
-
getattr(generated, meth)(str(out_dir))
|
| 30 |
-
|
|
|
|
|
|
|
| 31 |
if hasattr(generated, meth):
|
| 32 |
-
fp = out_dir / (
|
| 33 |
-
getattr(generated, meth)(str(fp))
|
| 34 |
-
|
| 35 |
-
return out_dir
|
| 36 |
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
-
def _get_worldgen():
|
| 40 |
-
global _worldgen
|
| 41 |
-
if _worldgen is None:
|
| 42 |
-
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
| 43 |
-
_worldgen = WorldGen(mode='t2s', device=device, low_vram=True)
|
| 44 |
-
return _worldgen
|
| 45 |
|
| 46 |
-
def generate_from_text(prompt:str):
|
| 47 |
if not prompt or not prompt.strip():
|
| 48 |
-
return None,
|
| 49 |
-
|
| 50 |
try:
|
| 51 |
generated = wg.generate_world(prompt.strip())
|
| 52 |
except Exception as e:
|
| 53 |
-
return None, f
|
|
|
|
|
|
|
| 54 |
tmpdir = Path(tempfile.mkdtemp())
|
| 55 |
-
out_dir = tmpdir /
|
| 56 |
out_dir = _save_scene(generated, out_dir)
|
| 57 |
zip_path = _zip_directory(out_dir)
|
| 58 |
-
return zip_path, '生成完成!請下載 ZIP(含 3D 資產)。'
|
| 59 |
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
with gr.Row():
|
| 63 |
with gr.Column():
|
| 64 |
-
prompt = gr.Textbox(
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
with gr.Column():
|
| 67 |
-
out_zip = gr.File(label=
|
| 68 |
status = gr.Markdown()
|
| 69 |
-
btn.click(generate_from_text, inputs=[prompt], outputs=[out_zip, status])
|
| 70 |
|
| 71 |
-
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py —— 放在檔案最上方的環境設定(✦✦✦ 這段要在所有 import 之前 ✦✦✦)
|
| 2 |
import os
|
|
|
|
|
|
|
| 3 |
|
| 4 |
+
# 1) 禁用 CUDA(強制在 CPU 上跑,避免 CUDA 警告)
|
| 5 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = ""
|
| 6 |
|
| 7 |
+
# 2) 關閉評估(若 WorldGen 支援此旗標,可避免 KNN/自訂外掛)
|
| 8 |
+
os.environ["WORLDGEN_DISABLE_EVAL"] = "1"
|
| 9 |
|
| 10 |
+
# 3) 指定 Gradio 暫存目錄(選用;集中暫存防止亂堆)
|
| 11 |
+
os.environ["GRADIO_TEMP_DIR"] = "/tmp/gradio"
|
| 12 |
|
| 13 |
+
|
| 14 |
+
# ===== 下面才開始 import 其他套件 =====
|
| 15 |
+
import shutil
|
| 16 |
+
import tempfile
|
| 17 |
+
import threading
|
| 18 |
from pathlib import Path
|
| 19 |
+
|
| 20 |
+
import gradio as gr
|
| 21 |
+
import torch
|
| 22 |
from worldgen import WorldGen
|
| 23 |
|
| 24 |
+
|
| 25 |
+
# 初始化 WorldGen(✦ CPU + low_vram ✦)
|
| 26 |
+
wg = WorldGen(mode="t2s", device="cpu", low_vram=True)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
def _zip_directory(dir_path: Path) -> str:
|
| 30 |
+
zip_path = dir_path.with_suffix(".zip")
|
| 31 |
if zip_path.exists():
|
| 32 |
zip_path.unlink()
|
| 33 |
+
shutil.make_archive(str(dir_path), "zip", root_dir=str(dir_path))
|
| 34 |
return str(zip_path)
|
| 35 |
|
| 36 |
+
|
| 37 |
def _save_scene(generated, out_dir: Path) -> Path:
|
| 38 |
out_dir.mkdir(parents=True, exist_ok=True)
|
| 39 |
+
|
| 40 |
+
# 嘗試通用保存方法(不同版本 WorldGen 返回物件可能不同)
|
| 41 |
+
for meth in ("save", "export", "write", "to_disk"):
|
| 42 |
if hasattr(generated, meth):
|
| 43 |
+
getattr(generated, meth)(str(out_dir))
|
| 44 |
+
return out_dir
|
| 45 |
+
|
| 46 |
+
for meth in ("save_as_glb", "to_glb", "save_as_ply", "to_ply"):
|
| 47 |
if hasattr(generated, meth):
|
| 48 |
+
fp = out_dir / ("scene.glb" if "glb" in meth else "scene.ply")
|
| 49 |
+
getattr(generated, meth)(str(fp))
|
| 50 |
+
return out_dir
|
|
|
|
| 51 |
|
| 52 |
+
# 最後手段:提示需依你使用的 WorldGen 版本更新保存邏輯
|
| 53 |
+
(out_dir / "README.txt").write_text(
|
| 54 |
+
"未自動偵測到保存方法。請依 WorldGen 版本的 demo/README 更新保存流程。"
|
| 55 |
+
)
|
| 56 |
+
return out_dir
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
+
def generate_from_text(prompt: str):
|
| 60 |
if not prompt or not prompt.strip():
|
| 61 |
+
return None, "請輸入場景描述(英文更穩定)。"
|
| 62 |
+
|
| 63 |
try:
|
| 64 |
generated = wg.generate_world(prompt.strip())
|
| 65 |
except Exception as e:
|
| 66 |
+
return None, f"生成失敗:{e}"
|
| 67 |
+
|
| 68 |
+
# 臨時目錄寫入與壓縮
|
| 69 |
tmpdir = Path(tempfile.mkdtemp())
|
| 70 |
+
out_dir = tmpdir / "worldgen_output"
|
| 71 |
out_dir = _save_scene(generated, out_dir)
|
| 72 |
zip_path = _zip_directory(out_dir)
|
|
|
|
| 73 |
|
| 74 |
+
# ✦ 延時清理臨時目錄,避免磁碟爆掉(50GB 上限)
|
| 75 |
+
def _cleanup(p: Path):
|
| 76 |
+
try:
|
| 77 |
+
shutil.rmtree(p, ignore_errors=True)
|
| 78 |
+
except Exception:
|
| 79 |
+
pass
|
| 80 |
+
|
| 81 |
+
threading.Timer(300, _cleanup, args=(tmpdir,)).start() # 5 分鐘後清理
|
| 82 |
+
|
| 83 |
+
return zip_path, "✅ 生成完成!請下載 ZIP(含 3D 資產)。"
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
# ===== Gradio 介面 =====
|
| 87 |
+
with gr.Blocks(title="WorldGen • Text -> 3D Scene") as demo:
|
| 88 |
+
# ✦ 用「三重引號」寫多行 Markdown,避免未終止的字串錯誤
|
| 89 |
+
gr.Markdown("""# 🌍 WorldGen — 文字 -> 3D 場景- 僅啟用 Text -> 3D。- Free CPU 會很慢,建議升級 GPU(T4/A10G)。""")
|
| 90 |
with gr.Row():
|
| 91 |
with gr.Column():
|
| 92 |
+
prompt = gr.Textbox(
|
| 93 |
+
label="輸入英文場景描述",
|
| 94 |
+
placeholder="a neon-lit cyberpunk street with rain reflections, highly detailed",
|
| 95 |
+
lines=3,
|
| 96 |
+
)
|
| 97 |
+
run_btn = gr.Button("🚀 生成 3D 場景")
|
| 98 |
with gr.Column():
|
| 99 |
+
out_zip = gr.File(label="下載輸出(ZIP)")
|
| 100 |
status = gr.Markdown()
|
|
|
|
| 101 |
|
| 102 |
+
run_btn.click(generate_from_text, inputs=[prompt], outputs=[out_zip, status])
|
| 103 |
+
|
| 104 |
+
demo.launch()
|