# 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 os.environ["GRADIO_APP"] = "texture_edit" import gradio as gr from common import ( MAX_SEED, VERSION, backproject_texture_v2, custom_theme, end_session, generate_condition, generate_texture_mvimages, get_seed, get_selected_image, image_css, lighting_css, render_result_video, start_session, ) def active_btn_by_content(mesh_content: gr.Model3D, text_content: gr.Textbox): if ( mesh_content is not None and text_content is not None and len(text_content) > 0 ): interactive = True else: interactive = False return gr.Button(interactive=interactive) with gr.Blocks(delete_cache=(43200, 43200), theme=custom_theme) as demo: gr.HTML(image_css, visible=False) # gr.HTML(lighting_css, visible=False) gr.Markdown( """ ## ***EmbodiedGen***: Texture Generation **🔖 Version**: {VERSION}

🌐 Project Page 📄 arXiv 💻 GitHub 🎥 Video

🎨 Generate visually rich textures for 3D mesh. """.format( VERSION=VERSION ), elem_classes=["header"], ) with gr.Row(): with gr.Column(scale=1): gr.Markdown( "You can select input in `Mesh Gallery` at page bottom." ) mesh_input = gr.Model3D( label="Upload Mesh File(.obj or .glb)", height=270 ) local_mesh = gr.Textbox(visible=False) text_prompt = gr.Textbox( label="Text Prompt (Chinese or English)", placeholder="Input text prompt here", ) gr.Markdown("
") ip_image = gr.Image( label="Reference Image(optional)", format="png", image_mode="RGB", type="filepath", height=250, elem_classes=["image_fit"], ) gr.Markdown( "Note: The `reference image` is optional. If provided, " "increase `Condition Scale` in Generation Settings." ) with gr.Accordion(label="Generation Settings", open=False): with gr.Row(): seed = gr.Slider( 0, MAX_SEED, label="Seed", value=0, step=1 ) randomize_seed = gr.Checkbox( label="Randomize Seed", value=False ) ip_adapt_scale = gr.Slider( 0, 1, label="IP-adapter Scale", value=0.7, step=0.05 ) cond_scale = gr.Slider( 0.0, 1.0, label="Geo Condition Scale", value=0.70, step=0.01, ) guidance_scale = gr.Slider( 1, 30, label="Text Guidance Scale", value=9, step=0.2 ) guidance_strength = gr.Slider( 0.0, 1.0, label="Strength", value=0.9, step=0.05, ) num_inference_steps = gr.Slider( 10, 100, label="Sampling Steps", value=50, step=5 ) texture_size = gr.Slider( 1024, 4096, label="UV texture size", value=2048, step=256 ) video_size = gr.Slider( 512, 2048, label="Video Resolution", value=512, step=256 ) with gr.Column(scale=3): with gr.Row(): image_sample1 = gr.Image( label="sample1", format="png", image_mode="RGBA", type="filepath", height=300, interactive=False, elem_classes=["image_fit"], ) image_sample2 = gr.Image( label="sample2", format="png", image_mode="RGBA", type="filepath", height=300, interactive=False, elem_classes=["image_fit"], ) image_sample3 = gr.Image( label="sample3", format="png", image_mode="RGBA", type="filepath", height=300, interactive=False, elem_classes=["image_fit"], ) usample1 = gr.Image( format="png", image_mode="RGBA", type="filepath", visible=False, ) usample2 = gr.Image( format="png", image_mode="RGBA", type="filepath", visible=False, ) usample3 = gr.Image( format="png", image_mode="RGBA", type="filepath", visible=False, ) # gr.Markdown( # "Note: Select samples with consistent textures from various " # "perspectives and no obvious reflections." # ) with gr.Row(): with gr.Column(scale=1): with gr.Row(): dropdown = gr.Radio( choices=["sample1", "sample2", "sample3"], value="sample1", label="Choose your favorite sample style.", ) select_img = gr.Image( visible=False, format="png", image_mode="RGBA", type="filepath", height=300, ) with gr.Row(): project_delight = gr.Checkbox( label="Back-project delight", value=True ) fix_mesh = gr.Checkbox( label="simplify mesh", value=False ) with gr.Column(scale=1): generate_mv_btn = gr.Button( "🎨 1. Generate MV Images(~1min)", variant="primary", interactive=False, ) texture_bake_btn = gr.Button( "🛠️ 2. Texture Baking(~2min)", variant="primary", interactive=False, ) download_btn = gr.DownloadButton( label="⬇️ 3. Download Mesh", variant="primary", interactive=False, ) with gr.Row(): mesh_output = gr.Model3D( label="Mesh Edit Result", clear_color=[0.8, 0.8, 0.8, 1], height=340, interactive=False, elem_id="lighter_mesh", ) mesh_outpath = gr.Textbox(visible=False) video_output = gr.Video( label="Mesh Edit Video", autoplay=True, loop=True, height=340, ) with gr.Row(): prompt_examples = [] with open("assets/example_texture/text_prompts.txt", "r") as f: for line in f: parts = line.strip().split("\\") prompt_examples.append([parts[0].strip(), parts[1].strip()]) examples = gr.Examples( label="Mesh Gallery", examples=prompt_examples, inputs=[mesh_input, text_prompt], examples_per_page=10, ) demo.load(start_session) demo.unload(end_session) mesh_input.change( lambda: tuple( [ None, None, None, gr.Button(interactive=False), gr.Button(interactive=False), None, None, None, ] ), outputs=[ mesh_outpath, mesh_output, video_output, texture_bake_btn, download_btn, image_sample1, image_sample2, image_sample3, ], ).success( active_btn_by_content, inputs=[mesh_input, text_prompt], outputs=[generate_mv_btn], ) text_prompt.change( active_btn_by_content, inputs=[mesh_input, text_prompt], outputs=[generate_mv_btn], ) generate_mv_btn.click( get_seed, inputs=[randomize_seed, seed], outputs=[seed], ).success( lambda: tuple( [ None, None, None, gr.Button(interactive=False), gr.Button(interactive=False), ] ), outputs=[ mesh_outpath, mesh_output, video_output, texture_bake_btn, download_btn, ], ).success( generate_condition, inputs=[mesh_input], outputs=[image_sample1, image_sample2, image_sample3], ).success( generate_texture_mvimages, inputs=[ text_prompt, cond_scale, guidance_scale, guidance_strength, num_inference_steps, seed, ip_adapt_scale, ip_image, ], outputs=[ image_sample1, image_sample2, image_sample3, usample1, usample2, usample3, ], ).success( lambda: gr.Button(interactive=True), outputs=[texture_bake_btn], ) texture_bake_btn.click( lambda: tuple([None, None, None, gr.Button(interactive=False)]), outputs=[mesh_outpath, mesh_output, video_output, download_btn], ).success( get_selected_image, inputs=[dropdown, usample1, usample2, usample3], outputs=select_img, ).success( backproject_texture_v2, inputs=[ mesh_input, select_img, texture_size, project_delight, fix_mesh, ], outputs=[mesh_output, mesh_outpath, download_btn], ).success( lambda: gr.DownloadButton(interactive=True), outputs=[download_btn], ).success( render_result_video, inputs=[mesh_outpath, video_size], outputs=[video_output], ) if __name__ == "__main__": demo.launch()