From 7a391771ef980bf6af189f1d42637eec7d35b444 Mon Sep 17 00:00:00 2001 From: Xinjie Date: Thu, 6 Nov 2025 14:11:35 +0800 Subject: [PATCH] feat(docs): Add mkdocs to manager docs. (#50) --- .github/workflows/deploy_docs.yml | 30 ++ .gitignore | 4 + README.md | 49 +- apps/assets/example_image/sample_00.jpg | Bin 0 -> 83756 bytes apps/assets/example_image/sample_05.jpg | Bin 0 -> 73050 bytes apps/assets/example_layout/task_list.txt | 5 +- apps/visualize_asset.py | 507 ++++++++++++++---- docs/acknowledgement.md | 28 + docs/api/data.md | 25 + docs/api/envs.md | 7 + docs/api/index.md | 14 + docs/api/models.md | 33 ++ docs/api/trainer.md | 11 + docs/api/utils.md | 47 ++ docs/api/validators.md | 15 + {apps => docs}/assets/Iscene_demo1.gif | Bin {apps => docs}/assets/Iscene_demo2.gif | Bin {apps => docs}/assets/articulate.gif | Bin {apps => docs}/assets/image_to_3d.jpg | Bin {apps => docs}/assets/layout1.gif | Bin {apps => docs}/assets/layout2.gif | Bin {apps => docs}/assets/layout3.gif | Bin {apps => docs}/assets/layout4.gif | Bin {apps => docs}/assets/logo.png | Bin {apps => docs}/assets/overall.jpg | Bin {apps => docs}/assets/parallel_sim.gif | Bin {apps => docs}/assets/parallel_sim2.gif | Bin {apps => docs}/assets/real2sim_mujoco.gif | Bin {apps => docs}/assets/scene3d.gif | Bin .../assets/simulators_collision.jpg | Bin {apps => docs}/assets/text_to_3d.jpg | Bin {apps => docs}/assets/texture_gen.jpg | Bin docs/index.md | 23 + docs/install.md | 38 ++ docs/services/image_to_3d.md | 97 ++++ docs/services/index.md | 46 ++ docs/services/text_to_3d.md | 111 ++++ docs/services/texture_edit.md | 162 ++++++ docs/services/visualize_asset.md | 8 + docs/stylesheets/extra.css | 22 + docs/tutorials/any_simulators.md | 63 +++ docs/tutorials/articulated_gen.md | 0 docs/tutorials/digital_twin.md | 3 + docs/tutorials/gym_env.md | 22 + docs/tutorials/image_to_3d.md | 92 ++++ docs/tutorials/index.md | 197 +++++++ docs/tutorials/layout_gen.md | 90 ++++ docs/tutorials/scene_gen.md | 47 ++ docs/tutorials/text_to_3d.md | 137 +++++ docs/tutorials/texture_edit.md | 78 +++ embodied_gen/data/asset_converter.py | 61 ++- embodied_gen/models/texture_model.py | 50 ++ mkdocs.yml | 110 ++++ pyproject.toml | 4 + setup.cfg | 2 +- tests/test_examples/test_asset_converter.py | 29 +- 56 files changed, 2118 insertions(+), 149 deletions(-) create mode 100644 .github/workflows/deploy_docs.yml create mode 100644 apps/assets/example_image/sample_00.jpg create mode 100644 apps/assets/example_image/sample_05.jpg create mode 100644 docs/acknowledgement.md create mode 100644 docs/api/data.md create mode 100644 docs/api/envs.md create mode 100644 docs/api/index.md create mode 100644 docs/api/models.md create mode 100644 docs/api/trainer.md create mode 100644 docs/api/utils.md create mode 100644 docs/api/validators.md rename {apps => docs}/assets/Iscene_demo1.gif (100%) rename {apps => docs}/assets/Iscene_demo2.gif (100%) rename {apps => docs}/assets/articulate.gif (100%) rename {apps => docs}/assets/image_to_3d.jpg (100%) rename {apps => docs}/assets/layout1.gif (100%) rename {apps => docs}/assets/layout2.gif (100%) rename {apps => docs}/assets/layout3.gif (100%) rename {apps => docs}/assets/layout4.gif (100%) rename {apps => docs}/assets/logo.png (100%) rename {apps => docs}/assets/overall.jpg (100%) rename {apps => docs}/assets/parallel_sim.gif (100%) rename {apps => docs}/assets/parallel_sim2.gif (100%) rename {apps => docs}/assets/real2sim_mujoco.gif (100%) rename {apps => docs}/assets/scene3d.gif (100%) rename {apps => docs}/assets/simulators_collision.jpg (100%) rename {apps => docs}/assets/text_to_3d.jpg (100%) rename {apps => docs}/assets/texture_gen.jpg (100%) create mode 100644 docs/index.md create mode 100644 docs/install.md create mode 100644 docs/services/image_to_3d.md create mode 100644 docs/services/index.md create mode 100644 docs/services/text_to_3d.md create mode 100644 docs/services/texture_edit.md create mode 100644 docs/services/visualize_asset.md create mode 100644 docs/stylesheets/extra.css create mode 100644 docs/tutorials/any_simulators.md create mode 100644 docs/tutorials/articulated_gen.md create mode 100644 docs/tutorials/digital_twin.md create mode 100644 docs/tutorials/gym_env.md create mode 100644 docs/tutorials/image_to_3d.md create mode 100644 docs/tutorials/index.md create mode 100644 docs/tutorials/layout_gen.md create mode 100644 docs/tutorials/scene_gen.md create mode 100644 docs/tutorials/text_to_3d.md create mode 100644 docs/tutorials/texture_edit.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml new file mode 100644 index 0000000..0ee870f --- /dev/null +++ b/.github/workflows/deploy_docs.yml @@ -0,0 +1,30 @@ +name: Deploy MkDocs Documentation + +on: + push: + branches: + - master + +permissions: + contents: write + +jobs: + build-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + pip install mkdocs-material + pip install mkdocstrings[python] + pip install mkdocs-git-revision-date-localized-plugin + + - name: Deploy documentation + run: mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index ea1d498..ec1dc31 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,7 @@ scripts/tools/ weights apps/sessions/ apps/assets/ + +# Larger than 1MB +docs/assets/real2sim_mujoco.gif +docs/assets/scene3d.gif \ No newline at end of file diff --git a/README.md b/README.md index 6d3302b..3a38edc 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ > ***EmbodiedGen*** is a generative engine to create diverse and interactive 3D worlds composed of high-quality 3D assets(mesh & 3DGS) with plausible physics, leveraging generative AI to address the challenges of generalization in embodied intelligence related research. > It composed of six key modules: `Image-to-3D`, `Text-to-3D`, `Texture Generation`, `Articulated Object Generation`, `Scene Generation` and `Layout Generation`. -Overall Framework +Overall Framework --- @@ -76,7 +76,7 @@ Explore EmbodiedGen generated assets in [![๐Ÿค— Hugging Face](https://img.shield [![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Image_to_3D_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Image-to-3D) Generate physically plausible 3D asset URDF from single input image, offering high-quality support for digital twin systems. (HF space is a simplified demonstration. For the full functionality, please refer to `img3d-cli`.) -Image to 3D +Image to 3D ### โ˜๏ธ Service Run the image-to-3D generation service locally. @@ -91,8 +91,8 @@ CUDA_VISIBLE_DEVICES=0 nohup python apps/image_to_3d.py > /dev/null 2>&1 & ### โšก API Generate physically plausible 3D assets from image input via the command-line API. ```sh -img3d-cli --image_path apps/assets/example_image/sample_04.jpg apps/assets/example_image/sample_19.jpg \ - --n_retry 2 --output_root outputs/imageto3d +img3d-cli --image_path apps/assets/example_image/sample_00.jpg apps/assets/example_image/sample_01.jpg apps/assets/example_image/sample_19.jpg \ +--n_retry 1 --output_root outputs/imageto3d # See result(.urdf/mesh.obj/mesh.glb/gs.ply) in ${output_root}/sample_xx/result ``` @@ -104,7 +104,7 @@ img3d-cli --image_path apps/assets/example_image/sample_04.jpg apps/assets/examp [![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Text_to_3D_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Text-to-3D) Create 3D assets from text descriptions for a wide range of geometry and styles. (HF space is a simplified demonstration. For the full functionality, please refer to `text3d-cli`.) -Text to 3D +Text to 3D ### โ˜๏ธ Service Deploy the text-to-3D generation service locally. @@ -119,11 +119,11 @@ python apps/text_to_3d.py Text-to-image model based on SD3.5 Medium, English prompts only. Usage requires agreement to the [model license(click accept)](https://huggingface.co/stabilityai/stable-diffusion-3.5-medium), models downloaded automatically. -For large-scale 3D assets generation, set `--n_pipe_retry=2` to ensure high end-to-end 3D asset usability through automatic quality check and retries. For more diverse results, do not set `--seed_img`. +For large-scale 3D asset generation, set `--n_image_retry=4` `--n_asset_retry=3` `--n_pipe_retry=2`, slower but better, via automatic checking and retries. For more diverse results, omit `--seed_img`. ```sh text3d-cli --prompts "small bronze figurine of a lion" "A globe with wooden base" "wooden table with embroidery" \ - --n_image_retry 2 --n_asset_retry 2 --n_pipe_retry 1 --seed_img 0 \ + --n_image_retry 1 --n_asset_retry 1 --n_pipe_retry 1 --seed_img 0 \ --output_root outputs/textto3d ``` @@ -142,7 +142,7 @@ ps: models with more permissive licenses found in `embodied_gen/models/image_com [![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Texture_Gen_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Texture-Gen) Generate visually rich textures for 3D mesh. -Texture Gen +Texture Gen ### โ˜๏ธ Service @@ -167,7 +167,7 @@ texture-cli --mesh_path "apps/assets/example_texture/meshes/robot_text.obj" \

๐ŸŒ 3D Scene Generation

-scene3d +scene3d ### โšก API > Run `bash install.sh extra` to install additional requirements if you need to use `scene3d-cli`. @@ -190,7 +190,7 @@ CUDA_VISIBLE_DEVICES=0 scene3d-cli \ ๐Ÿšง *Coming Soon* -articulate +articulate --- @@ -202,12 +202,12 @@ CUDA_VISIBLE_DEVICES=0 scene3d-cli \ - - + + - - + +
layout1layout2layout1layout2
layout3layout4layout3layout4
@@ -225,8 +225,8 @@ layout-cli --task_descs "Place the pen in the mug on the desk" "Put the fruit on - - + +
Iscene_demo1Iscene_demo2Iscene_demo1Iscene_demo2
@@ -243,7 +243,8 @@ Using `compose_layout.py`, you can recompose the layout of the generated interac ```sh python embodied_gen/scripts/compose_layout.py \ --layout_path "outputs/layouts_gens/task_0000/layout.json" \ ---output_dir "outputs/layouts_gens/task_0000/recompose" --insert_robot +--output_dir "outputs/layouts_gens/task_0000/recompose" \ +--insert_robot ``` We provide `sim-cli`, that allows users to easily load generated layouts into an interactive 3D simulation using the SAPIEN engine (will support for more simulators in future updates). @@ -257,8 +258,8 @@ Example: generate multiple parallel simulation envs with `gym.make` and record s - - + +
parallel_sim1parallel_sim2parallel_sim1parallel_sim2
@@ -271,7 +272,7 @@ python embodied_gen/scripts/parallel_sim.py \ ### ๐Ÿ–ผ๏ธ Real-to-Sim Digital Twin -real2sim_mujoco +real2sim_mujoco --- @@ -284,11 +285,11 @@ Example in `tests/test_examples/test_asset_converter.py`. | Simulator | Conversion Class | |-----------|------------------| | [isaacsim](https://github.com/isaac-sim/IsaacSim) | MeshtoUSDConverter | -| [mujoco](https://github.com/google-deepmind/mujoco) | MeshtoMJCFConverter | -| [genesis](https://github.com/Genesis-Embodied-AI/Genesis) / [sapien](https://github.com/haosulab/SAPIEN) / [isaacgym](https://github.com/isaac-sim/IsaacGymEnvs) / [pybullet](https://github.com/bulletphysics/bullet3) | EmbodiedGen generated .urdf can be used directly | +| [mujoco](https://github.com/google-deepmind/mujoco) / [genesis](https://github.com/Genesis-Embodied-AI/Genesis) | MeshtoMJCFConverter | +| [sapien](https://github.com/haosulab/SAPIEN) / [isaacgym](https://github.com/isaac-sim/IsaacGymEnvs) / [pybullet](https://github.com/bulletphysics/bullet3) | EmbodiedGen generated .urdf can be used directly | -simulators_collision +simulators_collision --- @@ -296,6 +297,8 @@ Example in `tests/test_examples/test_asset_converter.py`. ```sh pip install -e .[dev] && pre-commit install python -m pytest # Pass all unit-test are required. +# mkdocs serve --dev-addr 0.0.0.0:8000 +# mkdocs gh-deploy --force ``` ## ๐Ÿ“š Citation diff --git a/apps/assets/example_image/sample_00.jpg b/apps/assets/example_image/sample_00.jpg new file mode 100644 index 0000000000000000000000000000000000000000..539fa027bc7e9465ed954eb43e73fd9eebcb5708 GIT binary patch literal 83756 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�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 literal 0 HcmV?d00001 diff --git a/apps/assets/example_layout/task_list.txt b/apps/assets/example_layout/task_list.txt index d33378b..e8d0b04 100644 --- a/apps/assets/example_layout/task_list.txt +++ b/apps/assets/example_layout/task_list.txt @@ -15,4 +15,7 @@ Pick up the marker from the table and put it in the bowl Pick up the charger and move it slightly to the left Move the jar to the left side of the desk Pick the rubik's cube on the top of the desk -Move the mug to the right \ No newline at end of file +Move the mug to the right +Put the apples from table to the basket +Put the oranges from table to the bowl +Put the red cup on the tray on the table \ No newline at end of file diff --git a/apps/visualize_asset.py b/apps/visualize_asset.py index 9712997..089e329 100644 --- a/apps/visualize_asset.py +++ b/apps/visualize_asset.py @@ -1,11 +1,52 @@ +# Project EmbodiedGen +# +# Copyright (c) 2025 Horizon Robotics. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + import os + +gradio_tmp_dir = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "gradio_cache" +) +os.makedirs(gradio_tmp_dir, exist_ok=True) +os.environ["GRADIO_TEMP_DIR"] = gradio_tmp_dir + import shutil +import uuid import xml.etree.ElementTree as ET +from pathlib import Path +from typing import Any, Dict, Tuple import gradio as gr import pandas as pd +import yaml from app_style import custom_theme, lighting_css +try: + from embodied_gen.utils.gpt_clients import GPT_CLIENT as gpt_client + + gpt_client.check_connection() + GPT_AVAILABLE = True +except Exception as e: + gpt_client = None + GPT_AVAILABLE = False + print( + f"Warning: GPT client could not be initialized. Search will be disabled. Error: {e}" + ) + + # --- Configuration & Data Loading --- VERSION = "v0.1.5" RUNNING_MODE = "local" # local or hf_remote @@ -36,6 +77,7 @@ TMP_DIR = os.path.join( ) os.makedirs(TMP_DIR, exist_ok=True) + # --- Custom CSS for Styling --- css = """ .gradio-container .gradio-group { box-shadow: 0 2px 4px rgba(0,0,0,0.05) !important; } @@ -44,14 +86,43 @@ css = """ lighting_css = """ """ +_prev_temp = {} -# --- Helper Functions --- + +def _unique_path( + src_path: str | None, session_hash: str, kind: str +) -> str | None: + """Link/copy src to GRADIO_TEMP_DIR/session_hash with random filename. Always return a fresh URL.""" + if not src_path: + return None + tmp_root = ( + Path(os.environ.get("GRADIO_TEMP_DIR", "/tmp")) + / "model3d-cache" + / session_hash + ) + tmp_root.mkdir(parents=True, exist_ok=True) + + # rolling cleanup for same kind + prev = _prev_temp.get(session_hash, {}) + old = prev.get(kind) + if old and old.exists(): + old.unlink() + + ext = Path(src_path).suffix or ".glb" + dst = tmp_root / f"{kind}-{uuid.uuid4().hex}{ext}" + shutil.copy2(src_path, dst) + + prev[kind] = dst + _prev_temp[session_hash] = prev + return str(dst) + + +# --- Helper Functions (data filtering) --- def get_primary_categories(): return sorted(df["primary_category"].dropna().unique()) @@ -81,7 +152,7 @@ def get_categories(primary, secondary): def get_assets(primary, secondary, category): if not primary or not secondary: - return [], gr.update(interactive=False) + return [], gr.update(interactive=False), pd.DataFrame() subset = df[ (df["primary_category"] == primary) @@ -105,79 +176,211 @@ def get_assets(primary, secondary, category): else "https://dummyimage.com/512x512/cccccc/000000&text=No+Preview" ) - return items, gr.update(interactive=True) + return items, gr.update(interactive=True), subset -def show_asset_from_gallery( - evt: gr.SelectData, primary: str, secondary: str, category: str -): - index = evt.index - subset = df[ - (df["primary_category"] == primary) - & (df["secondary_category"] == secondary) - ] - if category: - subset = subset[subset["category"] == category] +def search_assets(query: str, top_k: int): + if not GPT_AVAILABLE or not query: + gr.Warning( + "GPT client is not available or query is empty. Cannot perform search." + ) + return [], gr.update(interactive=False), pd.DataFrame() - est_type_text = "N/A" - est_height_text = "N/A" - est_mass_text = "N/A" - est_mu_text = "N/A" + gr.Info(f"Searching for assets matching: '{query}'...") - if index >= len(subset): - return ( - None, - "Error: Selection index is out of bounds.", - None, - None, - est_type_text, - est_height_text, - est_mass_text, - est_mu_text, + keywords = query.split() + keyword_filter = pd.Series([False] * len(df), index=df.index) + for keyword in keywords: + keyword_filter |= df['description'].str.contains( + keyword, case=False, na=False ) - row = subset.iloc[index] + candidates = df[keyword_filter] + + if len(candidates) > 100: + candidates = candidates.head(100) + + if candidates.empty: + gr.Warning("No assets found matching the keywords.") + return [], gr.update(interactive=True), pd.DataFrame() + + try: + descriptions = [ + f"{idx}: {desc}" for idx, desc in candidates['description'].items() + ] + descriptions_text = "\n".join(descriptions) + + prompt = f""" + A user is searching for 3D assets with the query: "{query}". + Below is a list of available assets, each with an ID and a description. + Please evaluate how well each asset description matches the user's query and rate them on a scale from 0 to 10, where 10 is a perfect match. + + Your task is to return a list of the top {top_k} asset IDs, sorted from the most relevant to the least relevant. + The output format must be a simple comma-separated list of IDs, for example: "123,45,678". Do not add any other text. + + Asset Descriptions: + {descriptions_text} + + User Query: "{query}" + + Top {top_k} sorted asset IDs: + """ + response = gpt_client.query(prompt) + sorted_ids_str = response.strip().split(',') + sorted_ids = [ + int(id_str.strip()) + for id_str in sorted_ids_str + if id_str.strip().isdigit() + ] + top_assets = df.loc[sorted_ids].head(top_k) + except Exception as e: + gr.Error(f"An error occurred while using GPT for ranking: {e}") + top_assets = candidates.head(top_k) + + items = [] + for row in top_assets.itertuples(): + asset_dir = os.path.join(DATA_ROOT, row.asset_dir) + video_path = None + if pd.notna(row.asset_dir) and os.path.exists(asset_dir): + for f in os.listdir(asset_dir): + if f.lower().endswith(".mp4"): + video_path = os.path.join(asset_dir, f) + break + items.append( + video_path + if video_path + else "https://dummyimage.com/512x512/cccccc/000000&text=No+Preview" + ) + + gr.Info(f"Found {len(items)} assets.") + return items, gr.update(interactive=True), top_assets + + +# --- Mesh extraction --- +def _extract_mesh_paths(row) -> Tuple[str | None, str | None, str]: desc = row["description"] urdf_path = os.path.join(DATA_ROOT, row["urdf_path"]) asset_dir = os.path.join(DATA_ROOT, row["asset_dir"]) - mesh_to_display = None + visual_mesh_path = None + collision_mesh_path = None + if pd.notna(urdf_path) and os.path.exists(urdf_path): try: tree = ET.parse(urdf_path) root = tree.getroot() - mesh_element = root.find('.//visual/geometry/mesh') - if mesh_element is not None: - mesh_filename = mesh_element.get('filename') - if mesh_filename: - glb_filename = os.path.splitext(mesh_filename)[0] + ".glb" + visual_mesh_element = root.find('.//visual/geometry/mesh') + if visual_mesh_element is not None: + visual_mesh_filename = visual_mesh_element.get('filename') + if visual_mesh_filename: + glb_filename = ( + os.path.splitext(visual_mesh_filename)[0] + ".glb" + ) potential_path = os.path.join(asset_dir, glb_filename) if os.path.exists(potential_path): - mesh_to_display = potential_path + visual_mesh_path = potential_path - category_elem = root.find('.//extra_info/category') - if category_elem is not None and category_elem.text: - est_type_text = category_elem.text.strip() - - height_elem = root.find('.//extra_info/real_height') - if height_elem is not None and height_elem.text: - est_height_text = height_elem.text.strip() - - mass_elem = root.find('.//extra_info/min_mass') - if mass_elem is not None and mass_elem.text: - est_mass_text = mass_elem.text.strip() - - mu_elem = root.find('.//collision/gazebo/mu2') - if mu_elem is not None and mu_elem.text: - est_mu_text = mu_elem.text.strip() + collision_mesh_element = root.find('.//collision/geometry/mesh') + if collision_mesh_element is not None: + collision_mesh_filename = collision_mesh_element.get( + 'filename' + ) + if collision_mesh_filename: + potential_collision_path = os.path.join( + asset_dir, collision_mesh_filename + ) + if os.path.exists(potential_collision_path): + collision_mesh_path = potential_collision_path except ET.ParseError: desc = f"Error: Failed to parse URDF at {urdf_path}. {desc}" except Exception as e: desc = f"An error occurred while processing URDF: {str(e)}. {desc}" + return visual_mesh_path, collision_mesh_path, desc + + +def show_asset_from_gallery( + evt: gr.SelectData, + primary: str, + secondary: str, + category: str, + search_query: str, + gallery_df: pd.DataFrame, +): + """Parse the selected asset and return raw paths + metadata.""" + index = evt.index + + if search_query and gallery_df is not None and not gallery_df.empty: + subset = gallery_df + else: + if not primary or not secondary: + return ( + None, # visual_path + None, # collision_path + "Error: Primary or secondary category not selected.", + None, # asset_dir + None, # urdf_path + "N/A", + "N/A", + "N/A", + "N/A", + ) + + subset = df[ + (df["primary_category"] == primary) + & (df["secondary_category"] == secondary) + ] + if category: + subset = subset[subset["category"] == category] + + if subset.empty or index >= len(subset): + return ( + None, + None, + "Error: Selection index is out of bounds or data is missing.", + None, + None, + "N/A", + "N/A", + "N/A", + "N/A", + ) + + row = subset.iloc[index] + visual_path, collision_path, desc = _extract_mesh_paths(row) + + urdf_path = os.path.join(DATA_ROOT, row["urdf_path"]) + asset_dir = os.path.join(DATA_ROOT, row["asset_dir"]) + + # read extra info + est_type_text = "N/A" + est_height_text = "N/A" + est_mass_text = "N/A" + est_mu_text = "N/A" + + if pd.notna(urdf_path) and os.path.exists(urdf_path): + try: + tree = ET.parse(urdf_path) + root = tree.getroot() + category_elem = root.find('.//extra_info/category') + if category_elem is not None and category_elem.text: + est_type_text = category_elem.text.strip() + height_elem = root.find('.//extra_info/real_height') + if height_elem is not None and height_elem.text: + est_height_text = height_elem.text.strip() + mass_elem = root.find('.//extra_info/min_mass') + if mass_elem is not None and mass_elem.text: + est_mass_text = mass_elem.text.strip() + mu_elem = root.find('.//collision/gazebo/mu2') + if mu_elem is not None and mu_elem.text: + est_mu_text = mu_elem.text.strip() + except Exception: + pass + return ( - gr.update(value=mesh_to_display), + visual_path, + collision_path, desc, asset_dir, urdf_path, @@ -188,6 +391,56 @@ def show_asset_from_gallery( ) +def render_meshes( + visual_path: str | None, + collision_path: str | None, + switch_viewer: bool, + req: gr.Request, +): + session_hash = getattr(req, "session_hash", "default") + + if switch_viewer: + yield ( + gr.update(value=None), + gr.update(value=None, visible=False), + gr.update(value=None, visible=True), + True, + ) + else: + yield ( + gr.update(value=None), + gr.update(value=None, visible=True), + gr.update(value=None, visible=False), + True, + ) + + visual_unique = ( + _unique_path(visual_path, session_hash, "visual") + if visual_path + else None + ) + collision_unique = ( + _unique_path(collision_path, session_hash, "collision") + if collision_path + else None + ) + + if switch_viewer: + yield ( + gr.update(value=visual_unique), + gr.update(value=None, visible=False), + gr.update(value=collision_unique, visible=True), + False, + ) + else: + yield ( + gr.update(value=visual_unique), + gr.update(value=collision_unique, visible=True), + gr.update(value=None, visible=False), + True, + ) + + def create_asset_zip(asset_dir: str, req: gr.Request): user_dir = os.path.join(TMP_DIR, str(req.session_hash)) os.makedirs(user_dir, exist_ok=True) @@ -214,7 +467,7 @@ def end_session(req: gr.Request) -> None: shutil.rmtree(user_dir) -# --- Gradio UI Definition --- +# --- UI --- with gr.Blocks( theme=custom_theme, css=css, @@ -256,9 +509,35 @@ with gr.Blocks( category_list = get_categories(primary_val, secondary_val) category_val = category_list[0] if category_list else None asset_folder = gr.State(value=None) + gallery_df_state = gr.State() + + switch_viewer_state = gr.State(value=False) with gr.Row(equal_height=False): with gr.Column(scale=1, min_width=350): + with gr.Group(): + gr.Markdown("### Search Asset with Descriptions") + search_box = gr.Textbox( + label="๐Ÿ”Ž Enter your search query", + placeholder="e.g., 'a red chair with four legs'", + interactive=GPT_AVAILABLE, + ) + top_k_slider = gr.Slider( + minimum=1, + maximum=50, + value=10, + step=1, + label="Number of results", + interactive=GPT_AVAILABLE, + ) + search_button = gr.Button( + "Search", variant="primary", interactive=GPT_AVAILABLE + ) + if not GPT_AVAILABLE: + gr.Markdown( + "

โš ๏ธ GPT client not available. Search is disabled.

" + ) + with gr.Group(): gr.Markdown("### Select Asset Category") primary = gr.Dropdown( @@ -278,10 +557,11 @@ with gr.Blocks( ) with gr.Group(): + initial_assets, _, initial_df = get_assets( + primary_val, secondary_val, category_val + ) gallery = gr.Gallery( - value=get_assets(primary_val, secondary_val, category_val)[ - 0 - ], + value=initial_assets, label="๐Ÿ–ผ๏ธ Asset Previews", columns=3, height="auto", @@ -292,14 +572,40 @@ with gr.Blocks( with gr.Column(scale=2, min_width=500): with gr.Group(): - viewer = gr.Model3D( - label="๐ŸงŠ 3D Model Viewer", - height=500, - clear_color=[0.95, 0.95, 0.95], - elem_id="lighter_mesh", + with gr.Tabs(): + with gr.TabItem("Visual Mesh") as t1: + viewer = gr.Model3D( + label="๐ŸงŠ 3D Model Viewer", + height=500, + clear_color=[0.95, 0.95, 0.95], + elem_id="visual_mesh", + ) + with gr.TabItem("Collision Mesh") as t2: + collision_viewer_a = gr.Model3D( + label="๐ŸงŠ Collision Mesh", + height=500, + clear_color=[0.95, 0.95, 0.95], + elem_id="collision_mesh_a", + visible=True, + ) + collision_viewer_b = gr.Model3D( + label="๐ŸงŠ Collision Mesh", + height=500, + clear_color=[0.95, 0.95, 0.95], + elem_id="collision_mesh_b", + visible=False, + ) + + t1.select( + fn=lambda: None, + js="() => { window.dispatchEvent(new Event('resize')); }", ) + t2.select( + fn=lambda: None, + js="() => { window.dispatchEvent(new Event('resize')); }", + ) + with gr.Row(): - # TODO: Add more asset details if needed est_type_text = gr.Textbox( label="Asset category", interactive=False ) @@ -312,10 +618,11 @@ with gr.Blocks( est_mu_text = gr.Textbox( label="Friction coefficient", interactive=False ) - with gr.Accordion(label="Asset Details", open=False): + with gr.Row(): desc_box = gr.Textbox( label="๐Ÿ“ Asset Description", interactive=False ) + with gr.Accordion(label="Asset Details", open=False): urdf_file = gr.Textbox( label="URDF File Path", interactive=False, lines=2 ) @@ -331,55 +638,64 @@ with gr.Blocks( interactive=False, ) + search_button.click( + fn=search_assets, + inputs=[search_box, top_k_slider], + outputs=[gallery, gallery, gallery_df_state], + ) + search_box.submit( + fn=search_assets, + inputs=[search_box, top_k_slider], + outputs=[gallery, gallery, gallery_df_state], + ) + def update_on_primary_change(p): s_choices = get_secondary_categories(p) + initial_assets, gallery_update, initial_df = get_assets(p, None, None) return ( gr.update(choices=s_choices, value=None), gr.update(choices=[], value=None), - [], - gr.update(interactive=False), + initial_assets, + gallery_update, + initial_df, ) def update_on_secondary_change(p, s): c_choices = get_categories(p, s) - return ( - gr.update(choices=c_choices, value=None), - [], - gr.update(interactive=False), - ) - - def update_on_secondary_change(p, s): - c_choices = get_categories(p, s) - asset_previews, gallery_update = get_assets(p, s, None) + asset_previews, gallery_update, gallery_df = get_assets(p, s, None) return ( gr.update(choices=c_choices, value=None), asset_previews, gallery_update, + gallery_df, ) + def update_assets(p, s, c): + asset_previews, gallery_update, gallery_df = get_assets(p, s, c) + return asset_previews, gallery_update, gallery_df + primary.change( fn=update_on_primary_change, inputs=[primary], - outputs=[secondary, category, gallery, gallery], + outputs=[secondary, category, gallery, gallery, gallery_df_state], ) - secondary.change( fn=update_on_secondary_change, inputs=[primary, secondary], - outputs=[category, gallery, gallery], + outputs=[category, gallery, gallery, gallery_df_state], ) - category.change( - fn=get_assets, + fn=update_assets, inputs=[primary, secondary, category], - outputs=[gallery, gallery], + outputs=[gallery, gallery, gallery_df_state], ) gallery.select( fn=show_asset_from_gallery, - inputs=[primary, secondary, category], + inputs=[primary, secondary, category, search_box, gallery_df_state], outputs=[ - viewer, + (visual_path_state := gr.State()), + (collision_path_state := gr.State()), desc_box, asset_folder, urdf_file, @@ -388,22 +704,23 @@ with gr.Blocks( est_mass_text, est_mu_text, ], + ).then( + fn=render_meshes, + inputs=[visual_path_state, collision_path_state, switch_viewer_state], + outputs=[ + viewer, + collision_viewer_a, + collision_viewer_b, + switch_viewer_state, + ], ).success( - lambda: tuple( - [ - gr.Button(interactive=True), - gr.Button(interactive=False), - ] - ), + lambda: (gr.Button(interactive=True), gr.Button(interactive=False)), outputs=[extract_btn, download_btn], ) extract_btn.click( fn=create_asset_zip, inputs=[asset_folder], outputs=[download_btn] - ).success( - fn=lambda: gr.update(interactive=True), - outputs=download_btn, - ) + ).success(fn=lambda: gr.update(interactive=True), outputs=download_btn) demo.load(start_session) demo.unload(end_session) @@ -411,7 +728,7 @@ with gr.Blocks( if __name__ == "__main__": demo.launch( - server_name="10.34.8.82", + server_name="10.34.8.77", server_port=8088, allowed_paths=[ "/horizon-bucket/robot_lab/datasets/embodiedgen/assets" diff --git a/docs/acknowledgement.md b/docs/acknowledgement.md new file mode 100644 index 0000000..d588194 --- /dev/null +++ b/docs/acknowledgement.md @@ -0,0 +1,28 @@ +# ๐Ÿ™Œ Acknowledgement + +EmbodiedGen builds upon the following amazing projects and models: +๐ŸŒŸ [Trellis](https://github.com/microsoft/TRELLIS) | ๐ŸŒŸ [Hunyuan-Delight](https://huggingface.co/tencent/Hunyuan3D-2/tree/main/hunyuan3d-delight-v2-0) | ๐ŸŒŸ [Segment Anything](https://github.com/facebookresearch/segment-anything) | ๐ŸŒŸ [Rembg](https://github.com/danielgatis/rembg) | ๐ŸŒŸ [RMBG-1.4](https://huggingface.co/briaai/RMBG-1.4) | ๐ŸŒŸ [Stable Diffusion x4](https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler) | ๐ŸŒŸ [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) | ๐ŸŒŸ [Kolors](https://github.com/Kwai-Kolors/Kolors) | ๐ŸŒŸ [ChatGLM3](https://github.com/THUDM/ChatGLM3) | ๐ŸŒŸ [Aesthetic Score](http://captions.christoph-schuhmann.de/aesthetic_viz_laion_sac+logos+ava1-l14-linearMSE-en-2.37B.html) | ๐ŸŒŸ [Pano2Room](https://github.com/TrickyGo/Pano2Room) | ๐ŸŒŸ [Diffusion360](https://github.com/ArcherFMY/SD-T2I-360PanoImage) | ๐ŸŒŸ [Kaolin](https://github.com/NVIDIAGameWorks/kaolin) | ๐ŸŒŸ [diffusers](https://github.com/huggingface/diffusers) | ๐ŸŒŸ [gsplat](https://github.com/nerfstudio-project/gsplat) | ๐ŸŒŸ [QWEN-2.5VL](https://github.com/QwenLM/Qwen2.5-VL) | ๐ŸŒŸ [GPT4o](https://platform.openai.com/docs/models/gpt-4o) | ๐ŸŒŸ [SD3.5](https://huggingface.co/stabilityai/stable-diffusion-3.5-medium) | ๐ŸŒŸ [ManiSkill](https://github.com/haosulab/ManiSkill) + +--- + +## ๐Ÿ“š Citation + +If you use EmbodiedGen in your research or projects, please cite: + +```bibtex +@misc{wang2025embodiedgengenerative3dworld, + title={EmbodiedGen: Towards a Generative 3D World Engine for Embodied Intelligence}, + author={Xinjie Wang and Liu Liu and Yu Cao and Ruiqi Wu and Wenkang Qin and Dehui Wang and Wei Sui and Zhizhong Su}, + year={2025}, + eprint={2506.10600}, + archivePrefix={arXiv}, + primaryClass={cs.RO}, + url={https://arxiv.org/abs/2506.10600}, +} +``` + +--- + +## โš–๏ธ License + +This project is licensed under the [Apache License 2.0](LICENSE). See the `LICENSE` file for details. diff --git a/docs/api/data.md b/docs/api/data.md new file mode 100644 index 0000000..4770d0a --- /dev/null +++ b/docs/api/data.md @@ -0,0 +1,25 @@ +# Data API + +::: embodied_gen.data.asset_converter + options: + heading_level: 3 + +::: embodied_gen.data.datasets + options: + heading_level: 3 + +::: embodied_gen.data.differentiable_render + options: + heading_level: 3 + +::: embodied_gen.data.mesh_operator + options: + heading_level: 3 + +::: embodied_gen.data.backproject_v2 + options: + heading_level: 3 + +::: embodied_gen.data.convex_decomposer + options: + heading_level: 3 \ No newline at end of file diff --git a/docs/api/envs.md b/docs/api/envs.md new file mode 100644 index 0000000..8b6d62b --- /dev/null +++ b/docs/api/envs.md @@ -0,0 +1,7 @@ +# Envs API + +Documentation for simulation environments and task definitions. + +::: embodied_gen.envs.pick_embodiedgen + options: + heading_level: 3 \ No newline at end of file diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..f354168 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,14 @@ +# API Reference + +Welcome to the API reference for EmbodiedGen. + +This section contains detailed documentation for all public modules, classes, +and functions. Use the navigation on the left (or the list below) to +browse the different components. + +* [**Data API**](data.md): Tools for data processing, conversion, and rendering. +* [**Envs API**](envs.md): Simulation environment definitions. +* [**Models API**](models.md): The core generative models (Texture, 3DGS, Layout, etc.). +* [**Trainer API**](trainer.md): PyTorch-Lightning style trainers for models. +* [**Utilities API**](utils.md): Helper functions and configuration. +* [**Validators API**](validators.md): Tools for checking and validating assets. diff --git a/docs/api/models.md b/docs/api/models.md new file mode 100644 index 0000000..034a816 --- /dev/null +++ b/docs/api/models.md @@ -0,0 +1,33 @@ +# Models API + +::: embodied_gen.models.texture_model + options: + heading_level: 3 + +::: embodied_gen.models.gs_model + options: + heading_level: 3 + +::: embodied_gen.models.layout + options: + heading_level: 3 + +::: embodied_gen.models.text_model + options: + heading_level: 3 + +::: embodied_gen.models.sr_model + options: + heading_level: 3 + +::: embodied_gen.models.segment_model + options: + heading_level: 3 + +::: embodied_gen.models.image_comm_model + options: + heading_level: 3 + +::: embodied_gen.models.delight_model + options: + heading_level: 3 \ No newline at end of file diff --git a/docs/api/trainer.md b/docs/api/trainer.md new file mode 100644 index 0000000..0a2bc89 --- /dev/null +++ b/docs/api/trainer.md @@ -0,0 +1,11 @@ +# Trainer API + +This section covers the training pipelines for various models. + +::: embodied_gen.trainer.gsplat_trainer + options: + heading_level: 3 + +::: embodied_gen.trainer.pono2mesh_trainer + options: + heading_level: 3 \ No newline at end of file diff --git a/docs/api/utils.md b/docs/api/utils.md new file mode 100644 index 0000000..e67f42f --- /dev/null +++ b/docs/api/utils.md @@ -0,0 +1,47 @@ +# Utilities API + +General-purpose utility functions, configuration, and helper classes. + +::: embodied_gen.utils.config + options: + heading_level: 3 + +::: embodied_gen.utils.log + options: + heading_level: 3 + +::: embodied_gen.utils.enum + options: + heading_level: 3 + +::: embodied_gen.utils.geometry + options: + heading_level: 3 + +::: embodied_gen.utils.gaussian + options: + heading_level: 3 + +::: embodied_gen.utils.gpt_clients + options: + heading_level: 3 + +::: embodied_gen.utils.process_media + options: + heading_level: 3 + +::: embodied_gen.utils.simulation + options: + heading_level: 3 + +::: embodied_gen.utils.tags + options: + heading_level: 3 + +::: embodied_gen.utils.trender + options: + heading_level: 3 + +::: embodied_gen.utils.monkey_patches + options: + heading_level: 3 \ No newline at end of file diff --git a/docs/api/validators.md b/docs/api/validators.md new file mode 100644 index 0000000..6dff6b4 --- /dev/null +++ b/docs/api/validators.md @@ -0,0 +1,15 @@ +# Validators API + +Tools for asset validation, quality control, and conversion. + +::: embodied_gen.validators.aesthetic_predictor + options: + heading_level: 3 + +::: embodied_gen.validators.quality_checkers + options: + heading_level: 3 + +::: embodied_gen.validators.urdf_convertor + options: + heading_level: 3 \ No newline at end of file diff --git a/apps/assets/Iscene_demo1.gif b/docs/assets/Iscene_demo1.gif similarity index 100% rename from apps/assets/Iscene_demo1.gif rename to docs/assets/Iscene_demo1.gif diff --git a/apps/assets/Iscene_demo2.gif b/docs/assets/Iscene_demo2.gif similarity index 100% rename from apps/assets/Iscene_demo2.gif rename to docs/assets/Iscene_demo2.gif diff --git a/apps/assets/articulate.gif b/docs/assets/articulate.gif similarity index 100% rename from apps/assets/articulate.gif rename to docs/assets/articulate.gif diff --git a/apps/assets/image_to_3d.jpg b/docs/assets/image_to_3d.jpg similarity index 100% rename from apps/assets/image_to_3d.jpg rename to docs/assets/image_to_3d.jpg diff --git a/apps/assets/layout1.gif b/docs/assets/layout1.gif similarity index 100% rename from apps/assets/layout1.gif rename to docs/assets/layout1.gif diff --git a/apps/assets/layout2.gif b/docs/assets/layout2.gif similarity index 100% rename from apps/assets/layout2.gif rename to docs/assets/layout2.gif diff --git a/apps/assets/layout3.gif b/docs/assets/layout3.gif similarity index 100% rename from apps/assets/layout3.gif rename to docs/assets/layout3.gif diff --git a/apps/assets/layout4.gif b/docs/assets/layout4.gif similarity index 100% rename from apps/assets/layout4.gif rename to docs/assets/layout4.gif diff --git a/apps/assets/logo.png b/docs/assets/logo.png similarity index 100% rename from apps/assets/logo.png rename to docs/assets/logo.png diff --git a/apps/assets/overall.jpg b/docs/assets/overall.jpg similarity index 100% rename from apps/assets/overall.jpg rename to docs/assets/overall.jpg diff --git a/apps/assets/parallel_sim.gif b/docs/assets/parallel_sim.gif similarity index 100% rename from apps/assets/parallel_sim.gif rename to docs/assets/parallel_sim.gif diff --git a/apps/assets/parallel_sim2.gif b/docs/assets/parallel_sim2.gif similarity index 100% rename from apps/assets/parallel_sim2.gif rename to docs/assets/parallel_sim2.gif diff --git a/apps/assets/real2sim_mujoco.gif b/docs/assets/real2sim_mujoco.gif similarity index 100% rename from apps/assets/real2sim_mujoco.gif rename to docs/assets/real2sim_mujoco.gif diff --git a/apps/assets/scene3d.gif b/docs/assets/scene3d.gif similarity index 100% rename from apps/assets/scene3d.gif rename to docs/assets/scene3d.gif diff --git a/apps/assets/simulators_collision.jpg b/docs/assets/simulators_collision.jpg similarity index 100% rename from apps/assets/simulators_collision.jpg rename to docs/assets/simulators_collision.jpg diff --git a/apps/assets/text_to_3d.jpg b/docs/assets/text_to_3d.jpg similarity index 100% rename from apps/assets/text_to_3d.jpg rename to docs/assets/text_to_3d.jpg diff --git a/apps/assets/texture_gen.jpg b/docs/assets/texture_gen.jpg similarity index 100% rename from apps/assets/texture_gen.jpg rename to docs/assets/texture_gen.jpg diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..d954540 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,23 @@ +--- +hide: + - navigation +--- + +# ๐Ÿ‘‹ Welcome to EmbodiedGen + +[![๐ŸŒ Project Page](https://img.shields.io/badge/๐ŸŒ-Project_Page-blue)](https://horizonrobotics.github.io/robot_lab/embodied_gen/index.html) +[![๐Ÿ“„ arXiv](https://img.shields.io/badge/๐Ÿ“„-arXiv-b31b1b)](https://arxiv.org/abs/2506.10600) +[![๐ŸŽฅ Video](https://img.shields.io/badge/๐ŸŽฅ-Video-red)](https://www.youtube.com/watch?v=rG4odybuJRk) +[![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Image_to_3D_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Image-to-3D) +[![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Text_to_3D_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Text-to-3D) +[![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Texture_Gen_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Texture-Gen) +[![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Asset_Gallery-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer) +[![ไธญๆ–‡ไป‹็ป](https://img.shields.io/badge/ไธญๆ–‡ไป‹็ป-07C160?logo=wechat&logoColor=white)](https://mp.weixin.qq.com/s/HH1cPBhK2xcDbyCK4BBTbw) + +*EmbodiedGen*: Towards a Generative 3D World Engine for Embodied Intelligence + +Overall Framework + +> ***EmbodiedGen*** is a generative engine to create diverse and interactive 3D worlds composed of high-quality 3D assets(mesh & 3DGS) with plausible physics, leveraging generative AI to address the challenges of generalization in embodied intelligence related research. + +--- diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..56d200f --- /dev/null +++ b/docs/install.md @@ -0,0 +1,38 @@ +--- +hide: + - navigation +--- + +## โœ… Setup Environment +```sh +git clone https://github.com/HorizonRobotics/EmbodiedGen.git +cd EmbodiedGen +git checkout v0.1.5 +git submodule update --init --recursive --progress +conda create -n embodiedgen python=3.10.13 -y # recommended to use a new env. +conda activate embodiedgen +bash install.sh basic +``` + +## โœ… Starting from Docker + +We provide a pre-built Docker image on [Docker Hub](https://hub.docker.com/repository/docker/wangxinjie/embodiedgen) with a configured environment for your convenience. For more details, please refer to [Docker documentation](https://github.com/HorizonRobotics/EmbodiedGen/tree/master/docker). + +> **Note:** Model checkpoints are not included in the image, they will be automatically downloaded on first run. You still need to set up the GPT Agent manually. + +```sh +IMAGE=wangxinjie/embodiedgen:env_v0.1.x +CONTAINER=EmbodiedGen-docker-${USER} +docker pull ${IMAGE} +docker run -itd --shm-size="64g" --gpus all --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged --net=host --name ${CONTAINER} ${IMAGE} +docker exec -it ${CONTAINER} bash +``` + +## โœ… Setup GPT Agent + +Update the API key in file: `embodied_gen/utils/gpt_config.yaml`. + +You can choose between two backends for the GPT agent: + +- **`gpt-4o`** (Recommended) โ€“ Use this if you have access to **Azure OpenAI**. +- **`qwen2.5-vl`** โ€“ An alternative with free usage via OpenRouter, apply a free key [here](https://openrouter.ai/settings/keys) and update `api_key` in `embodied_gen/utils/gpt_config.yaml` (50 free requests per day) diff --git a/docs/services/image_to_3d.md b/docs/services/image_to_3d.md new file mode 100644 index 0000000..fd07113 --- /dev/null +++ b/docs/services/image_to_3d.md @@ -0,0 +1,97 @@ + + + + + + +# ๐Ÿ–ผ๏ธ Image-to-3D Service +[![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Image_to_3D_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Image-to-3D) + +This service launches a web application to generate physically plausible 3D asset URDF from single input image, offering high-quality support for digital twin systems. + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +--- + +## โ˜๏ธ Run the App Service + +!!! note "Note" + Gradio servive is a simplified demonstration. For the full functionality, please refer to [img3d-cli](../tutorials/image_to_3d.md). + +Run the image-to-3D generation service locally. Models are automatically downloaded on first run, please be patient. + +```sh +# Run in foreground +python apps/image_to_3d.py + +# Or run in the background +CUDA_VISIBLE_DEVICES=0 nohup python apps/image_to_3d.py > /dev/null 2>&1 & +``` + +--- + +!!! tip "Getting Started" + - Try it directly online via our [Hugging Face Space](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Image-to-3D) โ€” no installation required. + - Explore EmbodiedGen generated sim-ready [Assets Gallery](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer). + - For instructions on using the generated asset in any simulator, see [Any Simulators Tutorial](tutorials/any_simulators.md). \ No newline at end of file diff --git a/docs/services/index.md b/docs/services/index.md new file mode 100644 index 0000000..92613af --- /dev/null +++ b/docs/services/index.md @@ -0,0 +1,46 @@ +# Interactive 3D Generation & Visualization Services + +EmbodiedGen provides a suite of **interactive services** that transform images and text into **physically plausible, simulator-ready 3D assets**. +Each service is optimized for visual quality, simulation compatibility, and scalability โ€” making it easy to create, edit, and explore assets for **digital twin**, **robotic simulation**, and **AI embodiment** scenarios. + +--- + +## โš™๏ธ Prerequisites + +!!! tip "Prerequisites" + Make sure to finish the [Installation Guide](../install.md) before launching any service. Missing dependencies will cause initialization errors. Model weights are automatically downloaded on first run. + +--- + +## ๐Ÿงฉ Overview of Available Services + +| Service | Description | +|----------|--------------| +| [๐Ÿ–ผ๏ธ **Image to 3D**](image_to_3d.md) | Generate physically plausible 3D asset URDF from single input image, offering high-quality support for digital twin systems. | +| [๐Ÿ“ **Text to 3D**](text_to_3d.md) | Generate physically plausible 3D assets from text descriptions for a wide range of geometry and styles. | +| [๐ŸŽจ **Texture Edit**](texture_edit.md) | Generate visually rich textures for existing 3D meshes. | +| [๐Ÿ“ธ **Asset Gallery**](visualize_asset.md) | Explore and download EmbodiedGen All-Simulators-Ready Assets. | + +--- + +## โš™๏ธ How to Run Locally + +!!! tip "Quick Start" + Each service can be launched directly as a local Gradio app: + ```bash + # Example: Run the Image-to-3D service + python apps/image_to_3d.py + ``` + + Models are automatically downloaded on first run. For full CLI usage, please check the corresponding [tutorials](../tutorials/index.md). + +--- + +## ๐Ÿงญ Next Steps + +- [๐Ÿ“˜ Tutorials](../tutorials/index.md) โ€“ Learn how to use EmbodiedGen in generating interactive 3D scenes for embodied intelligence. +- [๐Ÿงฑ API Reference](../api/index.md) โ€“ Integrate EmbodiedGen code programmatically. + +--- + +> ๐Ÿ’ก *EmbodiedGen bridges the gap between AI-driven 3D generation and physically grounded simulation, enabling true embodiment for intelligent agents.* diff --git a/docs/services/text_to_3d.md b/docs/services/text_to_3d.md new file mode 100644 index 0000000..cbeaaef --- /dev/null +++ b/docs/services/text_to_3d.md @@ -0,0 +1,111 @@ + + + + + + +# ๐Ÿ“ Text-to-3D Service + +[![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Text_to_3D_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Text-to-3D) + +This service launches a web application to generate physically plausible 3D assets from text descriptions for a wide range of geometry and styles. + +
+
+
+ + +

"Antique brass key, intricate filigree"

+
+
+ + +

"Rusty old wrench, peeling paint"

+
+
+ + +

"Sleek black drone, red sensors"

+
+
+ + +

"Miniature screwdriver with bright orange handle"

+
+
+ + +

"European style wooden dressing table"

+
+
+
+
+
+ +--- + +## โ˜๏ธ Run the App Service + +Create 3D assets from text descriptions for a wide range of geometry and styles. +!!! note "Note" + Gradio servive is a simplified demonstration. For the full functionality, please refer to [text3d-cli](../tutorials/text_to_3d.md). + + +Text-to-image model based on the Kolors model, supporting Chinese and English prompts. Models downloaded automatically on first run, please be patient. + +```sh +# Run in foreground +python apps/text_to_3d.py + +# Or run in the background +CUDA_VISIBLE_DEVICES=0 nohup python apps/text_to_3d.py > /dev/null 2>&1 & +``` + +--- + +!!! tip "Getting Started" + - You can also try Text-to-3D instantly online via our [Hugging Face Space](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Text-to-3D) โ€” no installation required. + - Explore EmbodiedGen generated sim-ready [Assets Gallery](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer). + - For instructions on using the generated asset in any simulator, see [Any Simulators Tutorial](tutorials/any_simulators.md). \ No newline at end of file diff --git a/docs/services/texture_edit.md b/docs/services/texture_edit.md new file mode 100644 index 0000000..7a7b212 --- /dev/null +++ b/docs/services/texture_edit.md @@ -0,0 +1,162 @@ + + + + + +# ๐ŸŽจ Texture Generation Service + +[![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-Texture_Gen_Demo-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Texture-Gen) + +This service launches a web application to generate visually rich textures for 3D mesh. + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +--- + +## โ˜๏ธ Run the App Service + +!!! note "Note" + Gradio servive is a simplified demonstration. For the full functionality, please refer to [texture-cli](../tutorials/texture_edit.md). + +Run the texture generation service locally. Models downloaded automatically on first run, see `download_kolors_weights`, `geo_cond_mv`. + + +```sh +# Run in foreground +python apps/texture_edit.py + +# Or run in the background +CUDA_VISIBLE_DEVICES=0 nohup python apps/texture_edit.py > /dev/null 2>&1 & +``` + +--- + +!!! tip "Getting Started" + - Try it directly online via our [Hugging Face Space](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Texture-Gen) โ€” no installation required. + - Explore EmbodiedGen generated sim-ready [Assets Gallery](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer). + - For instructions on using the generated asset in any simulator, see [Any Simulators Tutorial](tutorials/any_simulators.md). diff --git a/docs/services/visualize_asset.md b/docs/services/visualize_asset.md new file mode 100644 index 0000000..81a5553 --- /dev/null +++ b/docs/services/visualize_asset.md @@ -0,0 +1,8 @@ +# ๐Ÿ“ธ EmbodiedGen All-Simulators-Ready Assets Gallery + +[![๐Ÿค— Hugging Face](https://img.shields.io/badge/๐Ÿค—-EmbodiedGen_Asset_Gallery-blue)](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer) + + +!!! tip "Getting Started" + - Explore EmbodiedGen generated sim-ready [Assets Gallery](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer). + - For instructions on using the generated asset in any simulator, see [Any Simulators Tutorial](tutorials/any_simulators.md). \ No newline at end of file diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..0bbeb6f --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,22 @@ +/* Adjust the logo size */ +.md-header__button.md-logo { + height: 4rem; + padding: 0; + display: inline-flex; + align-items: center; +} +.md-header__button.md-logo img { + height: 4rem; + width: auto; +} + +.md-typeset pre code { + font-size: 0.7rem; + /* line-height: 1.5; */ + font-family: "Fira Code", "JetBrains Mono", monospace; +} + +.md-typeset .admonition, +.md-typeset details { + font-size: 0.77rem; +} \ No newline at end of file diff --git a/docs/tutorials/any_simulators.md b/docs/tutorials/any_simulators.md new file mode 100644 index 0000000..573e760 --- /dev/null +++ b/docs/tutorials/any_simulators.md @@ -0,0 +1,63 @@ +# ๐ŸŽฎ Use EmbodiedGen in Any Simulator + +Leverage **EmbodiedGen-generated assets** with *accurate physical collisions* and *consistent visual appearance* across major simulation engines โ€” **IsaacSim**, **MuJoCo**, **Genesis**, **PyBullet**, **IsaacGym**, and **SAPIEN**. + +!!! tip "Universal Compatibility" + EmbodiedGen assets follow **standardized URDF semantics** with **physically consistent collision meshes**, + enabling seamless loading across multiple simulation frameworks โ€” no manual editing needed. + +--- + +## ๐Ÿงฉ Supported Simulators + +| Simulator | Conversion Class | +|------------|------------------| +| [IsaacSim](https://github.com/isaac-sim/IsaacSim) | `MeshtoUSDConverter` | +| [MuJoCo](https://github.com/google-deepmind/mujoco) / [Genesis](https://github.com/Genesis-Embodied-AI/Genesis) | `MeshtoMJCFConverter` | +| [SAPIEN](https://github.com/haosulab/SAPIEN) / [IsaacGym](https://github.com/isaac-sim/IsaacGymEnvs) / [PyBullet](https://github.com/bulletphysics/bullet3) | `.urdf` generated by EmbodiedGen can be used **directly** | + +!!! note "Simulator Integration Overview" + + This table summarizes the compatibility of EmbodiedGen assets with various simulators: + + | Simulator | Supported Format | Notes | + |-----------|-----------------|-------| + | IsaacSim | USD / .usda | Use `MeshtoUSDConverter` to convert mesh to USD format. | + | MuJoCo | MJCF (.xml) | Use `MeshtoMJCFConverter` for physics-ready assets. | + | Genesis | MJCF (.xml) | Same as MuJoCo; fully compatible with Genesis scenes. | + | SAPIEN | URDF (.urdf) | Can directly load EmbodiedGen `.urdf` assets. | + | IsaacGym | URDF (.urdf) | Directly usable. | + | PyBullet | URDF (.urdf) | Directly usable. | + +--- + + +## ๐Ÿงฑ Example: Conversion to Target Simulator + +```python +from embodied_gen.data.asset_converter import SimAssetMapper, cvt_embodiedgen_asset_to_anysim +from typing import Literal + +simulator_name: Literal[ + "isaacsim", + "isaacgym", + "genesis", + "pybullet", + "sapien3", + "mujoco", +] = "mujoco" + +dst_asset_path = cvt_embodiedgen_asset_to_anysim( + urdf_files=[ + "path1_to_embodiedgen_asset/asset.urdf", + "path2_to_embodiedgen_asset/asset.urdf", + ], + target_type=SimAssetMapper[simulator_name], + source_type=AssetType.MESH, + overwrite=True, +) +``` + +simulators_collision + +Collision and visualization mesh across simulators, showing consistent geometry and material fidelity. diff --git a/docs/tutorials/articulated_gen.md b/docs/tutorials/articulated_gen.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/tutorials/digital_twin.md b/docs/tutorials/digital_twin.md new file mode 100644 index 0000000..9a742e2 --- /dev/null +++ b/docs/tutorials/digital_twin.md @@ -0,0 +1,3 @@ +# Real-to-Sim Digital Twin Creation + +real2sim_mujoco diff --git a/docs/tutorials/gym_env.md b/docs/tutorials/gym_env.md new file mode 100644 index 0000000..b01bbea --- /dev/null +++ b/docs/tutorials/gym_env.md @@ -0,0 +1,22 @@ +# Simulation in Parallel Envs + +Generate multiple parallel simulation environments with `gym.make` and record sensor and trajectory data. + +--- + +## โšก Command-Line Usage + +```sh +python embodied_gen/scripts/parallel_sim.py \ +--layout_file "outputs/layouts_gen/task_0000/layout.json" \ +--output_dir "outputs/parallel_sim/task_0000" \ +--num_envs 16 +``` + +
+ parallel_sim1 + parallel_sim2 +
+ diff --git a/docs/tutorials/image_to_3d.md b/docs/tutorials/image_to_3d.md new file mode 100644 index 0000000..d0b256e --- /dev/null +++ b/docs/tutorials/image_to_3d.md @@ -0,0 +1,92 @@ + + + + + +# ๐Ÿ–ผ๏ธ Image-to-3D: Physically Plausible 3D Asset Generation + +Generate **physically plausible 3D assets** from a single input image, supporting **digital twin** and **simulation environments**. + +--- + +## โšก Command-Line Usage + +```bash +img3d-cli --image_path apps/assets/example_image/sample_00.jpg \ +apps/assets/example_image/sample_01.jpg apps/assets/example_image/sample_19.jpg \ +--n_retry 1 --output_root outputs/imageto3d +``` + +You will get the following results: + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +The generated results are organized as follows: +```sh +outputs/imageto3d/sample_xx/result +โ”œโ”€โ”€ mesh +โ”‚ โ”œโ”€โ”€ material_0.png +โ”‚ โ”œโ”€โ”€ material.mtl +โ”‚ โ”œโ”€โ”€ sample_xx_collision.ply +โ”‚ โ”œโ”€โ”€ sample_xx.glb +โ”‚ โ”œโ”€โ”€ sample_xx_gs.ply +โ”‚ โ””โ”€โ”€ sample_xx.obj +โ”œโ”€โ”€ sample_xx.urdf +โ””โ”€โ”€ video.mp4 +``` + +- `mesh/` โ†’ Geometry and texture files, including visual mesh, collision mesh and 3DGS. +- `*.urdf` โ†’ Simulator-ready URDF with collision and visual meshes +- `video.mp4` โ†’ Preview of the generated 3D asset + + +!!! tip "Getting Started" + - Try it directly online via our [Hugging Face Space](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Image-to-3D) โ€” no installation required. + - Explore EmbodiedGen generated sim-ready [Assets Gallery](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer). + - For instructions on using the generated asset in any simulator, see [Any Simulators Tutorial](any_simulators.md). \ No newline at end of file diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md new file mode 100644 index 0000000..993bde6 --- /dev/null +++ b/docs/tutorials/index.md @@ -0,0 +1,197 @@ + + + + + +# Tutorials & Interface Usage + +Welcome to the tutorials for `EmbodiedGen`. `EmbodiedGen` is a powerful toolset for generating 3D assets, textures, scenes, and interactive layouts ready for simulators and digital twin environments. + +--- + +## โš™๏ธ Prerequisites + +!!! tip "Prerequisites" + Make sure to finish the [Installation Guide](../install.md) before starting tutorial. Missing dependencies will cause initialization errors. Model weights are automatically downloaded on first run. + +--- + +## [๐Ÿ–ผ๏ธ Image-to-3D](image_to_3d.md) + +Generate **physically plausible 3D assets** from a single input image, supporting digital twin and simulation environments. + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +--- + +## [๐Ÿ“ Text-to-3D](text_to_3d.md) + +Create **physically plausible 3D assets** from **text descriptions**, supporting a wide range of geometry, style, and material details. + + +
+
+
+ + +

"small bronze figurine of a lion"

+
+
+ + +

"A globe with wooden base"

+
+
+ + +

"wooden table with embroidery"

+
+
+
+
+
+ +--- + +## [๐ŸŽจ Texture Generation](texture_gen.md) + +Generate **high-quality textures** for 3D meshes using **text prompts**, supporting both Chinese and English, to enhance the visual appearance of existing 3D assets. + +
+
+
+ + +
+
+ + +
+
+
+
+
+ +--- + +## [๐ŸŒ 3D Scene Generation](scene_gen.md) + +Generate **physically consistent and visually coherent 3D environments** from text prompts. Typically used as **background** 3DGS scenes in simulators for efficient and photo-realistic rendering. + + + +--- + +## [๐Ÿž๏ธ Layout Generation](layout_gen.md) + +Generate diverse, physically realistic, and scalable **interactive 3D scenes** from natural language task descriptions, while also modeling the robot and manipulable objects. + +
+ layout1 + layout2 + layout3 + layout4 +
+ + +--- + +## [๐ŸŽ๏ธ Parallel Simulation](gym_env.md) + +Generate multiple **parallel simulation environments** with `gym.make` and record sensor and trajectory data. + +
+ parallel_sim1 + parallel_sim2 +
+ + +--- + +## [๐ŸŽฎ Use in Any Simulator](any_simulators.md) + +Seamlessly use EmbodiedGen-generated assets in major simulators like **IsaacSim**, **MuJoCo**, **Genesis**, **PyBullet**, **IsaacGym**, and **SAPIEN**, featuring **accurate physical collisions** and **consistent visual appearance**. + +
+ simulators_collision +
+ +## [๐Ÿ”ง Real-to-Sim Digital Twin Creation](digital_twin.md) + +
+ real2sim_mujoco +
diff --git a/docs/tutorials/layout_gen.md b/docs/tutorials/layout_gen.md new file mode 100644 index 0000000..f5a8e8a --- /dev/null +++ b/docs/tutorials/layout_gen.md @@ -0,0 +1,90 @@ +# ๐Ÿž๏ธ Layout Generation โ€” Interactive 3D Scenes + +Layout Generation enables the generation of diverse, physically realistic, and scalable **interactive 3D scenes** directly from natural language task descriptions, while also modeling the robot's pose and relationships with manipulable objects. Target objects are randomly placed within the robot's reachable range, making the scenes readily usable for downstream simulation and reinforcement learning tasks in any mainstream simulator. + +
+ layout1 + layout2 + layout3 + layout4 +
+ +!!! note "Model Requirement" + The text-to-image model is based on `SD3.5 Medium`. Usage requires agreement to the [model license](https://huggingface.co/stabilityai/stable-diffusion-3.5-medium). + +--- + +## Prerequisites โ€” Prepare Background 3D Scenes + +Before running `layout-cli`, you need to prepare background 3D scenes. +You can either **generate your own** using the [`scene3d-cli`](scene_gen.md), or **download pre-generated backgrounds** for convenience. + +Each scene takes approximately **30 minutes** to generate. For efficiency, we recommend pre-generating and listing them in `outputs/bg_scenes/scene_list.txt`. + +```bash +# Option 1: Download pre-generated backgrounds (~4 GB) +hf download xinjjj/scene3d-bg --repo-type dataset --local-dir outputs + +# Option 2: Download a larger background set (~14 GB) +hf download xinjjj/EmbodiedGenRLv2-BG --repo-type dataset --local-dir outputs +``` + +## Generate Interactive Layout Scenes + +Use the `layout-cli` to create interactive 3D scenes based on task descriptions. Each layout generation takes approximately 30 minutes. + +```sh +layout-cli \ + --task_descs "Place the pen in the mug on the desk" \ + "Put the fruit on the table on the plate" \ + --bg_list "outputs/bg_scenes/scene_list.txt" \ + --output_root "outputs/layouts_gen" \ + --insert_robot +``` + +You will get the following results: +
+ Iscene_demo1 + Iscene_demo2 +
+ + +### Batch Generation + +You can also run multiple tasks via a task list file in the backend. + +```sh +CUDA_VISIBLE_DEVICES=0 nohup layout-cli \ + --task_descs "apps/assets/example_layout/task_list.txt" \ + --bg_list "outputs/bg_scenes/scene_list.txt" \ + --output_root "outputs/layouts_gens" \ + --insert_robot > layouts_gens.log & +``` + +> ๐Ÿ’ก Remove `--insert_robot` if you donโ€™t need robot pose consideration in layout generation. + +### Layout Randomization + +Using `compose_layout.py`, you can **recompose the layout** of the generated interactive 3D scenes. + +```sh +python embodied_gen/scripts/compose_layout.py \ +--layout_path "outputs/layouts_gens/task_0000/layout.json" \ +--output_dir "outputs/layouts_gens/task_0000/recompose" \ +--insert_robot +``` + +### Load Interactive 3D Scenes in Simulators + +We provide `sim-cli`, that allows users to easily load generated layouts into an interactive 3D simulation using the SAPIEN engine. + +```sh +sim-cli --layout_path "outputs/layouts_gen/task_0000/layout.json" \ +--output_dir "outputs/layouts_gen/task_0000/sapien_render" --insert_robot +``` + +!!! tip "Recommended Workflow" + 1. Generate or download background scenes using `scene3d-cli`. + 2. Create interactive layouts from task descriptions using `layout-cli`. + 3. Optionally recompose them using `compose_layout.py`. + 4. Load the final layouts into simulators with `sim-cli`. \ No newline at end of file diff --git a/docs/tutorials/scene_gen.md b/docs/tutorials/scene_gen.md new file mode 100644 index 0000000..e08764d --- /dev/null +++ b/docs/tutorials/scene_gen.md @@ -0,0 +1,47 @@ +# ๐ŸŒ 3D Scene Generation + +Generate **physically consistent and visually coherent 3D environments** from text prompts. Typically used as **background** 3DGS scenes in simulators for efficient and photo-realistic rendering. + +--- + + + +--- + +## โšก Command-Line Usage + +> ๐Ÿ’ก Run `bash install.sh extra` to install additional dependencies if you plan to use `scene3d-cli`. + +It typically takes ~30 minutes per scene to generate both the colored mesh and 3D Gaussian Splat(3DGS) representation. + +```bash +CUDA_VISIBLE_DEVICES=0 scene3d-cli \ + --prompts "Art studio with easel and canvas" \ + --output_dir outputs/bg_scenes/ \ + --seed 0 \ + --gs3d.max_steps 4000 \ + --disable_pano_check +``` + +The generated results are organized as follows: +```sh +outputs/bg_scenes/scene_000 +โ”œโ”€โ”€ gs_model.ply +โ”œโ”€โ”€ gsplat_cfg.yml +โ”œโ”€โ”€ mesh_model.ply +โ”œโ”€โ”€ pano_image.png +โ”œโ”€โ”€ prompt.txt +โ””โ”€โ”€ video.mp4 +``` + +- `gs_model.ply` โ†’ Generated 3D scene in 3D Gaussian Splat representation. +- `mesh_model.ply` โ†’ Color mesh representation of the generated scene. +- `gsplat_cfg.yml` โ†’ Configuration file for 3DGS training and rendering parameters. +- `pano_image.png` โ†’ Generated panoramic view image. +- `prompt.txt` โ†’ Original scene generation prompt for traceability. +- `video.mp4` โ†’ Preview RGB and depth preview of the generated 3D scene. + +!!! note "Usage Notes" + - `3D Scene Generation` produces background 3DGS scenes optimized for efficient rendering in simulation environments. We also provide hybrid rendering examples combining background 3DGS with foreground interactive assets, see the [example]() + for details. + - In Layout Generation, we further demonstrate task-desc-driven interactive 3D scene generation, building complete 3D scenes based on natural language task descriptions. See the [Layout Generation Guide](layout_gen.md). diff --git a/docs/tutorials/text_to_3d.md b/docs/tutorials/text_to_3d.md new file mode 100644 index 0000000..8d96ffd --- /dev/null +++ b/docs/tutorials/text_to_3d.md @@ -0,0 +1,137 @@ + + + + + + +# ๐Ÿ“ Text-to-3D: Generate 3D Assets from Text + +Create **physically plausible 3D assets** from **text descriptions**, supporting a wide range of geometry, style, and material details. + +--- + +## โšก Command-Line Usage + +**Basic CLI(recommend)** + +Text-to-image model based on Stable Diffusion 3.5 Medium๏ผŒ English prompts only. Usage requires agreement to the [model license (click โ€œAcceptโ€)](https://huggingface.co/stabilityai/stable-diffusion-3.5-medium). + +```bash +text3d-cli \ + --prompts "small bronze figurine of a lion" "A globe with wooden base" "wooden table with embroidery" \ + --n_image_retry 1 \ + --n_asset_retry 1 \ + --n_pipe_retry 1 \ + --seed_img 0 \ + --output_root outputs/textto3d +``` + +- `--n_image_retry`: Number of retries per prompt for text-to-image generation +- `--n_asset_retry`: Retry attempts for image-to-3D assets generation +- `--n_pipe_retry`: Pipeline retry for end-to-end 3D asset quality check +- `--seed_img`: Optional initial seed image for style guidance +- `--output_root`: Directory to save generated assets + +For large-scale 3D asset generation, set `--n_image_retry=4` `--n_asset_retry=3` `--n_pipe_retry=2`, slower but better, via automatic checking and retries. For more diverse results, omit `--seed_img`. + +You will get the following results: + +
+
+
+ + +

"small bronze figurine of a lion"

+
+
+ + +

"A globe with wooden base"

+
+
+ + +

"wooden table with embroidery"

+
+
+
+
+
+ +--- + + +Kolors Model CLI (Supports Chinese & English Prompts): +```bash +bash embodied_gen/scripts/textto3d.sh \ + --prompts "small bronze figurine of a lion" "A globe with wooden base and latitude and longitude lines" "ๆฉ™่‰ฒ็”ตๅŠจๆ‰‹้’ป๏ผŒๆœ‰็ฃจๆŸ็ป†่Š‚" \ + --output_root outputs/textto3d_k +``` + +> Models with more permissive licenses can be found in `embodied_gen/models/image_comm_model.py`. + + +The generated results are organized as follows: +```sh +outputs/textto3d +โ”œโ”€โ”€ asset3d +โ”‚ โ”œโ”€โ”€ sample3d_xx +โ”‚ โ”‚ โ””โ”€โ”€ result +โ”‚ โ”‚ โ”œโ”€โ”€ mesh +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ material_0.png +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ material.mtl +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ sample3d_xx_collision.obj +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ sample3d_xx.glb +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ sample3d_xx_gs.ply +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ sample3d_xx.obj +โ”‚ โ”‚ โ”œโ”€โ”€ sample3d_xx.urdf +โ”‚ โ”‚ โ””โ”€โ”€ video.mp4 +โ””โ”€โ”€ images + โ”œโ”€โ”€ sample3d_xx.png + โ”œโ”€โ”€ sample3d_xx_raw.png +``` + +- `mesh/` โ†’ 3D geometry and texture files for the asset, including visual mesh, collision mesh and 3DGS +- `*.urdf` โ†’ Simulator-ready URDF including collision and visual meshes +- `video.mp4` โ†’ Preview video of the generated 3D asset +- `images/sample3d_xx.png` โ†’ Foreground-extracted image used for image-to-3D step +- `images/sample3d_xx_raw.png` โ†’ Original generated image from the text-to-image step + +--- + +!!! tip "Getting Started" + - You can also try Text-to-3D instantly online via our [Hugging Face Space](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Text-to-3D) โ€” no installation required. + - Explore EmbodiedGen generated sim-ready [Assets Gallery](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer). + - For instructions on using the generated asset in any simulator, see [Any Simulators Tutorial](any_simulators.md). \ No newline at end of file diff --git a/docs/tutorials/texture_edit.md b/docs/tutorials/texture_edit.md new file mode 100644 index 0000000..dcb763e --- /dev/null +++ b/docs/tutorials/texture_edit.md @@ -0,0 +1,78 @@ + + + + + +# ๐ŸŽจ Texture Generation: Create Visually Rich Textures for 3D Meshes + +Generate **high-quality textures** for 3D meshes using **text prompts**, supporting both **Chinese and English**. This allows you to enhance the visual appearance of existing 3D assets for simulation, visualization, or digital twin applications. + +--- + +## โšก Command-Line Usage + +```bash +texture-cli \ + --mesh_path "apps/assets/example_texture/meshes/robot_text.obj" \ + "apps/assets/example_texture/meshes/horse.obj" \ + --prompt "ไธพ็€็‰Œๅญ็š„ๅ†™ๅฎž้ฃŽๆ ผๆœบๅ™จไบบ๏ผŒๅคง็œผ็›๏ผŒ็‰ŒๅญไธŠๅ†™็€โ€œHelloโ€็š„ๆ–‡ๅญ—" \ + "A gray horse head with flying mane and brown eyes" \ + --output_root "outputs/texture_gen" \ + --seed 0 +``` + +- `--mesh_path` โ†’ Path(s) to input 3D mesh files +- `--prompt` โ†’ Text prompt(s) describing desired texture/style for each mesh +- `--output_root` โ†’ Directory to save textured meshes and related outputs +- `--seed` โ†’ Random seed for reproducible texture generation + + +You will get the following results: + +
+
+
+ + +
+
+ + +
+
+
+
+
+ +--- + +!!! tip "Getting Started" + - Try it directly online via our [Hugging Face Space](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Texture-Gen) โ€” no installation required. + - Explore EmbodiedGen generated sim-ready [Assets Gallery](https://huggingface.co/spaces/HorizonRobotics/EmbodiedGen-Gallery-Explorer). + - For instructions on using the generated asset in any simulator, see [Any Simulators Tutorial](any_simulators.md). \ No newline at end of file diff --git a/embodied_gen/data/asset_converter.py b/embodied_gen/data/asset_converter.py index 4e5ef7c..3b32c93 100644 --- a/embodied_gen/data/asset_converter.py +++ b/embodied_gen/data/asset_converter.py @@ -59,6 +59,7 @@ def cvt_embodiedgen_asset_to_anysim( urdf_files: list[str], target_type: AssetType, source_type: AssetType, + overwrite: bool = False, **kwargs, ) -> dict[str, str]: """Convert URDF files generated by EmbodiedGen into the format required by all simulators. @@ -76,6 +77,8 @@ def cvt_embodiedgen_asset_to_anysim( urdf_files (List[str]): List of URDF file paths to be converted. target_type (AssetType): The target asset type. source_type (AssetType): The source asset type. + overwrite (bool): Whether to overwrite existing converted files. + **kwargs: Additional keyword arguments for the converter. Returns: Dict[str, str]: A dictionary mapping the original URDF file path to the converted asset file path. @@ -424,7 +427,8 @@ class MeshtoUSDConverter(AssetConverterBase): DEFAULT_BIND_APIS = [ "MaterialBindingAPI", - "PhysicsMeshCollisionAPI", + # "PhysicsMeshCollisionAPI", + "PhysxDecompositionCollisionAPI", "PhysicsCollisionAPI", "PhysxCollisionAPI", "PhysicsMassAPI", @@ -554,7 +558,8 @@ class MeshtoUSDConverter(AssetConverterBase): class PhysicsUSDAdder(MeshtoUSDConverter): DEFAULT_BIND_APIS = [ "MaterialBindingAPI", - "PhysicsMeshCollisionAPI", + # "PhysicsMeshCollisionAPI", + "PhysxDecompositionCollisionAPI", "PhysicsCollisionAPI", "PhysxCollisionAPI", "PhysicsRigidBodyAPI", @@ -771,33 +776,33 @@ if __name__ == "__main__": for urdf_path, output_file in zip(urdf_paths, output_files): asset_converter.convert(urdf_path, output_file) - # urdf_path = "outputs/embodiedgen_assets/demo_assets/remote_control/result/remote_control.urdf" - # output_file = "outputs/embodiedgen_assets/demo_assets/remote_control/usd/remote_control.usd" + urdf_path = "outputs/embodiedgen_assets/demo_assets/remote_control/result/remote_control.urdf" + output_file = "outputs/embodiedgen_assets/demo_assets/remote_control/usd/remote_control.usd" - # asset_converter = AssetConverterFactory.create( - # target_type=AssetType.USD, - # source_type=AssetType.URDF, - # rotate_wxyz=(0.7071, 0.7071, 0, 0), # rotate 90 deg around the X-axis - # ) + asset_converter = AssetConverterFactory.create( + target_type=AssetType.USD, + source_type=AssetType.URDF, + rotate_wxyz=(0.7071, 0.7071, 0, 0), # rotate 90 deg around the X-axis + ) - # with asset_converter: - # asset_converter.convert(urdf_path, output_file) + with asset_converter: + asset_converter.convert(urdf_path, output_file) - # # Convert infinigen urdf to mjcf - # urdf_path = "/home/users/xinjie.wang/xinjie/infinigen/outputs/exports/kitchen_i_urdf/export_scene/scene.urdf" - # output_file = "/home/users/xinjie.wang/xinjie/infinigen/outputs/exports/kitchen_i_urdf/mjcf/scene.xml" - # asset_converter = AssetConverterFactory.create( - # target_type=AssetType.MJCF, - # source_type=AssetType.URDF, - # keep_materials=["diffuse"], - # ) - # with asset_converter: - # asset_converter.convert(urdf_path, output_file) + # Convert infinigen urdf to mjcf + urdf_path = "/home/users/xinjie.wang/xinjie/infinigen/outputs/exports/kitchen_i_urdf/export_scene/scene.urdf" + output_file = "/home/users/xinjie.wang/xinjie/infinigen/outputs/exports/kitchen_i_urdf/mjcf/scene.xml" + asset_converter = AssetConverterFactory.create( + target_type=AssetType.MJCF, + source_type=AssetType.URDF, + keep_materials=["diffuse"], + ) + with asset_converter: + asset_converter.convert(urdf_path, output_file) - # # Convert infinigen usdc to physics usdc - # converter = PhysicsUSDAdder() - # with converter: - # converter.convert( - # usd_path="/home/users/xinjie.wang/xinjie/infinigen/outputs/usdc/export_scene/export_scene.usdc", - # output_file="/home/users/xinjie.wang/xinjie/infinigen/outputs/usdc_p3/export_scene/export_scene.usdc", - # ) + # Convert infinigen usdc to physics usdc + converter = PhysicsUSDAdder() + with converter: + converter.convert( + usd_path="/home/users/xinjie.wang/xinjie/infinigen/outputs/usdc/export_scene/export_scene.usdc", + output_file="/home/users/xinjie.wang/xinjie/infinigen/outputs/usdc_p3/export_scene/export_scene.usdc", + ) diff --git a/embodied_gen/models/texture_model.py b/embodied_gen/models/texture_model.py index d29b06a..c8a12fc 100644 --- a/embodied_gen/models/texture_model.py +++ b/embodied_gen/models/texture_model.py @@ -42,6 +42,56 @@ def build_texture_gen_pipe( ip_adapt_scale: float = 0, device: str = "cuda", ) -> DiffusionPipeline: + """Build and initialize the Kolors + ControlNet (optional IP-Adapter) texture generation pipeline. + + Loads Kolors tokenizer, text encoder (ChatGLM), VAE, UNet, scheduler and (optionally) + a ControlNet checkpoint plus IP-Adapter vision encoder. If ``controlnet_ckpt`` is + not provided, the default multi-view texture ControlNet weights are downloaded + automatically from the hub. When ``ip_adapt_scale > 0`` an IP-Adapter vision + encoder and its weights are also loaded and activated. + + Args: + base_ckpt_dir (str): + Root directory where Kolors (and optionally Kolors-IP-Adapter-Plus) weights + are or will be stored. Required subfolders: ``Kolors/{text_encoder,vae,unet,scheduler}``. + controlnet_ckpt (str, optional): + Directory containing a ControlNet checkpoint (safetensors). If ``None``, + downloads the default ``texture_gen_mv_v1`` snapshot. + ip_adapt_scale (float, optional): + Strength (>=0) of IP-Adapter conditioning. Set >0 to enable IP-Adapter; + typical values: 0.4-0.8. Default: 0 (disabled). + device (str, optional): + Target device to move the pipeline to (e.g. ``"cuda"``, ``"cuda:0"``, ``"cpu"``). + Default: ``"cuda"``. + + Returns: + DiffusionPipeline: A configured + ``StableDiffusionXLControlNetImg2ImgPipeline`` ready for multi-view texture + generation (with optional IP-Adapter support). + + Example: + Initialize pipeline with IP-Adapter enabled. + ```python + from embodied_gen.models.texture_model import build_texture_gen_pipe + ip_adapt_scale = 0.7 + PIPELINE = build_texture_gen_pipe( + base_ckpt_dir="./weights", + ip_adapt_scale=ip_adapt_scale, + device="cuda", + ) + PIPELINE.set_ip_adapter_scale([ip_adapt_scale]) + ``` + Initialize pipeline without IP-Adapter. + ```python + from embodied_gen.models.texture_model import build_texture_gen_pipe + PIPELINE = build_texture_gen_pipe( + base_ckpt_dir="./weights", + ip_adapt_scale=0, + device="cuda", + ) + ``` + """ + download_kolors_weights(f"{base_ckpt_dir}/Kolors") logger.info(f"Load Kolors weights...") tokenizer = ChatGLMTokenizer.from_pretrained( diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..a280926 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,110 @@ +site_name: Documentation +site_url: https://horizonrobotics.github.io/EmbodiedGen/ +repo_name: "EmbodiedGen" +repo_url: https://github.com/HorizonRobotics/EmbodiedGen +copyright: "Copyright (c) 2025 Horizon Robotics" + +nav: + - ๐Ÿ  Home: index.md + - ๐Ÿš€ Installation: install.md + - ๐Ÿงฉ Services: + - Overview: services/index.md + - Image-to-3D: services/image_to_3d.md + - Text-to-3D: services/text_to_3d.md + - Texture Generation: services/texture_edit.md + - Asset Visualizer: services/visualize_asset.md + - ๐Ÿ“˜ Tutorials: + - Overview: tutorials/index.md + - Image-to-3D: tutorials/image_to_3d.md + - Text-to-3D: tutorials/text_to_3d.md + - Texture Generation: tutorials/texture_edit.md + # - Articulated Object Generation: tutorials/articulated_gen.md + - 3D Scene Generation: tutorials/scene_gen.md + - Interactive 3D Scenes: tutorials/layout_gen.md + - Gym Parallel Envs: tutorials/gym_env.md + - Any Simulators: tutorials/any_simulators.md + - Digital Twin Creation: tutorials/digital_twin.md + - ๐Ÿ“š API Reference: + - Overview: api/index.md + - Data: api/data.md + - Envs: api/envs.md + - Models: api/models.md + - Trainer: api/trainer.md + - Utilities: api/utils.md + - Validators: api/validators.md + - โœจ Acknowledgement: acknowledgement.md + +extra: + social: + - icon: simple/huggingface + link: https://huggingface.co/collections/HorizonRobotics/embodiedgen + - icon: fontawesome/brands/github + link: https://github.com/HorizonRobotics/EmbodiedGen + - icon: simple/arxiv + link: https://arxiv.org/abs/2506.10600 + - icon: fontawesome/solid/globe + link: https://horizonrobotics.github.io/robot_lab/embodied_gen/index.html + - icon: fontawesome/brands/youtube + link: https://www.youtube.com/watch?v=rG4odybuJRk + +theme: + name: material + language: en + logo: assets/logo.png + favicon: assets/logo.png + icon: + repo: fontawesome/brands/github + features: + - navigation.instant + - navigation.instant.prefetch + - navigation.instant.progress + - navigation.path + - navigation.tabs + - navigation.top + - search.highlight + - content.code.copy + - content.action.edit + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: brown + accent: red + toggle: + icon: material/weather-sunny + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: brown + accent: red + toggle: + icon: material/weather-night + name: Switch to light mode + +plugins: + - search + - mkdocstrings: + handlers: + python: + paths: [embodied_gen] + options: + show_signature_annotations: true + separate_signature: true + show_root_toc_entry: false + docstring_style: google + show_source: true + merge_init_into_class: true + show_inherited_members: true + show_root_heading: true + show_root_full_path: true + + - git-revision-date-localized: + enable_creation_date: true + +extra_css: + - stylesheets/extra.css + +markdown_extensions: + - pymdownx.highlight + - pymdownx.superfences + - admonition + diff --git a/pyproject.toml b/pyproject.toml index 81c23c2..4f8d645 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,10 @@ dev = [ "isort", "pytest", "pytest-mock", + "mkdocs", + "mkdocs-material", + "mkdocstrings[python]", + "mkdocs-git-revision-date-localized-plugin", ] [project.scripts] diff --git a/setup.cfg b/setup.cfg index 7ef6850..257629f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ [pycodestyle] -ignore = E203,W503,E402,E501 +ignore = E203,W503,E402,E501,E251 diff --git a/tests/test_examples/test_asset_converter.py b/tests/test_examples/test_asset_converter.py index bd46245..eeefdbb 100644 --- a/tests/test_examples/test_asset_converter.py +++ b/tests/test_examples/test_asset_converter.py @@ -1,6 +1,13 @@ +from typing import Literal + import pytest from huggingface_hub import snapshot_download -from embodied_gen.data.asset_converter import AssetConverterFactory, AssetType +from embodied_gen.data.asset_converter import ( + AssetConverterFactory, + AssetType, + SimAssetMapper, + cvt_embodiedgen_asset_to_anysim, +) @pytest.fixture(scope="session") @@ -56,3 +63,23 @@ def test_MeshtoUSDConverter(data_dir): assert output_file.exists(), f"Output not generated: {output_file}" assert output_file.stat().st_size > 0 + + +def test_cvt_embodiedgen_asset_to_anysim( + simulator_name: Literal[ + "isaacsim", + "isaacgym", + "genesis", + "pybullet", + "sapien3", + "mujoco", + ] = "mujoco", +): + dst_asset_path = cvt_embodiedgen_asset_to_anysim( + urdf_files=[ + "outputs/embodiedgen_assets/demo_assets/remote_control2/result/remote_control.urdf", + ], + target_type=SimAssetMapper[simulator_name], + source_type=AssetType.MESH, + overwrite=True, + )