chahui commited on
Commit
a62d7b6
·
verified ·
1 Parent(s): 0455215

Upload 5 files

Browse files
Files changed (5) hide show
  1. README.md +20 -7
  2. app.py +37 -38
  3. pytorch3d/__init__.py +0 -0
  4. pytorch3d/transforms.py +24 -0
  5. requirements.txt +3 -3
README.md CHANGED
@@ -1,6 +1,5 @@
1
  ---
2
  title: "WorldGen Text-to-3D"
3
- emoji: "🌍"
4
  colorFrom: "blue"
5
  colorTo: "purple"
6
  sdk: "gradio"
@@ -12,10 +11,24 @@ pinned: false
12
 
13
  # WorldGen — Text -> 3D Scene (Hugging Face Spaces)
14
 
15
- 只做「文字 3D 場景」。請先在 Settings Hardware 升級到 GPU(T4/A10G)再正式生成;CPU 只適合驗證流程。
16
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- ## 使用方式
19
- 1. 在左側輸入英文場景描述。
20
- 2. 按「生成 3D 場景」。
21
- 3. 下載 ZIP 檔(內含 3D 資產)。
 
1
  ---
2
  title: "WorldGen Text-to-3D"
 
3
  colorFrom: "blue"
4
  colorTo: "purple"
5
  sdk: "gradio"
 
11
 
12
  # WorldGen — Text -> 3D Scene (Hugging Face Spaces)
13
 
14
+ This Space provides a minimal Text -> 3D demo using the open-source WorldGen project.
15
+
16
+ ## Quick start
17
+ 1) Open the Space and enter an English scene description.
18
+ 2) Click "Generate". A ZIP file with 3D outputs will appear for download.
19
+
20
+ > Tip: On free CPU it will be very slow and may timeout. Switch the Space hardware to a GPU (T4/A10G) for practical generation.
21
+
22
+ ## Environment
23
+ - Python 3.11 (set via the YAML block above)
24
+ - Gradio 5.x (set via sdk_version)
25
+
26
+ ## Dependencies
27
+ We keep requirements minimal so pip can resolve versions:
28
+ - torch>=2.7 (required by WorldGen)
29
+ - websockets>=13.1 (required by viser used inside WorldGen)
30
+ - WorldGen from GitHub
31
 
32
+ ## Notes
33
+ - This app disables CUDA and evaluation at runtime to avoid GPU-only/compiled ops when running on CPU.
34
+ - Temporary outputs are cleaned a few minutes after each run to avoid hitting the 50GB ephemeral disk limit.
 
app.py CHANGED
@@ -1,30 +1,26 @@
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")
@@ -33,72 +29,75 @@ def _zip_directory(dir_path: Path) -> str:
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()
 
 
1
  import os
 
 
 
 
 
 
 
 
 
 
 
 
2
  import shutil
3
  import tempfile
4
  import threading
5
  from pathlib import Path
6
 
7
+ # === Runtime environment tweaks (must be set before importing heavy libs) ===
8
+ os.environ.setdefault("CUDA_VISIBLE_DEVICES", "") # force CPU
9
+ os.environ.setdefault("WORLDGEN_DISABLE_EVAL", "1") # avoid KNN/eval paths if supported
10
+ os.environ.setdefault("GRADIO_TEMP_DIR", "/tmp/gradio") # centralize gradio cache
11
+
12
  import gradio as gr
13
  import torch
14
  from worldgen import WorldGen
15
 
16
+ # Lazy init to avoid long cold start
17
+ _wg = None
18
 
19
+ def get_worldgen():
20
+ global _wg
21
+ if _wg is None:
22
+ _wg = WorldGen(mode="t2s", device="cpu", low_vram=True)
23
+ return _wg
24
 
25
  def _zip_directory(dir_path: Path) -> str:
26
  zip_path = dir_path.with_suffix(".zip")
 
29
  shutil.make_archive(str(dir_path), "zip", root_dir=str(dir_path))
30
  return str(zip_path)
31
 
 
32
  def _save_scene(generated, out_dir: Path) -> Path:
33
  out_dir.mkdir(parents=True, exist_ok=True)
34
 
35
+ # 1) try common object methods
36
  for meth in ("save", "export", "write", "to_disk"):
37
  if hasattr(generated, meth):
38
  getattr(generated, meth)(str(out_dir))
39
  return out_dir
40
 
41
+ # 2) single-file export fallbacks
42
  for meth in ("save_as_glb", "to_glb", "save_as_ply", "to_ply"):
43
  if hasattr(generated, meth):
44
  fp = out_dir / ("scene.glb" if "glb" in meth else "scene.ply")
45
  getattr(generated, meth)(str(fp))
46
  return out_dir
47
 
48
+ # 3) last resort: leave a note
49
  (out_dir / "README.txt").write_text(
50
+ "No known save/export method detected. Please adjust _save_scene according to your WorldGen version."
51
  )
52
  return out_dir
53
 
 
54
  def generate_from_text(prompt: str):
55
  if not prompt or not prompt.strip():
56
+ return None, "Please enter a scene description (English works best)."
57
+
58
+ wg = get_worldgen()
59
 
60
  try:
61
+ result = wg.generate_world(prompt.strip())
62
  except Exception as e:
63
+ return None, f"Generation failed: {e}"
64
 
 
65
  tmpdir = Path(tempfile.mkdtemp())
66
  out_dir = tmpdir / "worldgen_output"
67
+ out_dir = _save_scene(result, out_dir)
68
  zip_path = _zip_directory(out_dir)
69
 
70
+ # delayed cleanup to avoid filling the 50GB ephemeral disk
71
  def _cleanup(p: Path):
72
  try:
73
  shutil.rmtree(p, ignore_errors=True)
74
  except Exception:
75
  pass
76
 
77
+ threading.Timer(300, _cleanup, args=(tmpdir,)).start() # 5 minutes later
78
 
79
+ return zip_path, "Done. Download the ZIP (contains 3D assets)."
80
 
 
 
81
  with gr.Blocks(title="WorldGen • Text -> 3D Scene") as demo:
82
+ gr.Markdown(
83
+ """# WorldGen - Text -> 3D
84
+ - CPU-only fallback is enabled. For real runs, switch the Space to a GPU (T4/A10G).
85
+ - Outputs are zipped for download; temp files are cleaned shortly after each run.
86
+
87
+ """
88
+ )
89
  with gr.Row():
90
  with gr.Column():
91
  prompt = gr.Textbox(
92
+ label="Scene prompt (English)",
93
  placeholder="a neon-lit cyberpunk street with rain reflections, highly detailed",
94
  lines=3,
95
  )
96
+ btn = gr.Button("Generate 3D Scene")
97
  with gr.Column():
98
+ out_zip = gr.File(label="Download output (ZIP)")
99
  status = gr.Markdown()
100
 
101
+ btn.click(generate_from_text, inputs=[prompt], outputs=[out_zip, status])
102
 
103
+ demo.launch()
pytorch3d/__init__.py ADDED
File without changes
pytorch3d/transforms.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ def matrix_to_quaternion(R: torch.Tensor) -> torch.Tensor:
4
+ """Minimal CPU-only implementation supporting [..., 3, 3] rotation matrices.
5
+ Returns quaternion in (x, y, z, w) order.
6
+ """
7
+ R = R.float()
8
+ single = False
9
+ if R.dim() == 2:
10
+ R = R.unsqueeze(0)
11
+ single = True
12
+ m00 = R[..., 0, 0]; m01 = R[..., 0, 1]; m02 = R[..., 0, 2]
13
+ m10 = R[..., 1, 0]; m11 = R[..., 1, 1]; m12 = R[..., 1, 2]
14
+ m20 = R[..., 2, 0]; m21 = R[..., 2, 1]; m22 = R[..., 2, 2]
15
+ trace = m00 + m11 + m22
16
+
17
+ qw = torch.sqrt(torch.clamp(trace + 1.0, min=1e-8)) / 2.0
18
+ qx = torch.sign(m21 - m12) * torch.sqrt(torch.clamp(1.0 + m00 - m11 - m22, min=1e-8)) / 2.0
19
+ qy = torch.sign(m02 - m20) * torch.sqrt(torch.clamp(1.0 - m00 + m11 - m22, min=1e-8)) / 2.0
20
+ qz = torch.sign(m10 - m01) * torch.sqrt(torch.clamp(1.0 - m00 - m11 + m22, min=1e-8)) / 2.0
21
+ q = torch.stack([qx, qy, qz, qw], dim=-1)
22
+ if single:
23
+ q = q.squeeze(0)
24
+ return q
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
  torch>=2.7
2
- # torchvision>=0.22 # WorldGen 實際需要再開
3
- websockets>=13.1 # 給 viser 相容
4
- git+https://github.com/ZiYang-xie/WorldGen.git
 
1
  torch>=2.7
2
+ # torchvision>=0.22 # uncomment if needed by your WorldGen setup
3
+ websockets>=13.1
4
+ git+https://github.com/ZiYang-xie/WorldGen.git