cavargas10 commited on
Commit
d0b7d87
·
verified ·
1 Parent(s): 0fd77e4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -146
app.py CHANGED
@@ -1,46 +1,45 @@
 
 
 
1
  import os
2
  import shlex
3
  import spaces
4
  import subprocess
 
5
 
6
- # --------------------------------------------------------------------------
7
- # 1. INSTALACIÓN DEL ENTORNO Y DEPENDENCIAS
8
- # Esta sección es crucial para que el Space de Hugging Face funcione.
9
- # Instala el toolkit de CUDA y compila las extensiones C++/CUDA necesarias.
10
- # --------------------------------------------------------------------------
11
- def install_cuda_toolkit():
12
- """Instala el toolkit de CUDA necesario para compilar las extensiones."""
13
- CUDA_TOOLKIT_URL = "https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.14_linux.run"
14
- CUDA_TOOLKIT_FILE = "/tmp/%s" % os.path.basename(CUDA_TOOLKIT_URL)
15
-
16
- print("Descargando CUDA Toolkit...")
17
- subprocess.call(["wget", "-q", CUDA_TOOLKIT_URL, "-O", CUDA_TOOLKIT_FILE])
18
- subprocess.call(["chmod", "+x", CUDA_TOOLKIT_FILE])
19
 
20
- print("Instalando CUDA Toolkit...")
21
- subprocess.call([CUDA_TOOLKIT_FILE, "--silent", "--toolkit"])
 
 
 
 
 
 
 
 
22
 
23
  os.environ["CUDA_HOME"] = "/usr/local/cuda"
24
- os.environ["PATH"] = "%s/bin:%s" % (os.environ["CUDA_HOME"], os.environ["PATH"])
25
- os.environ["LD_LIBRARY_PATH"] = "%s/lib:%s" % (
26
- os.environ["CUDA_HOME"],
27
- "" if "LD_LIBRARY_PATH" not in os.environ else os.environ["LD_LIBRARY_PATH"],
28
- )
29
  os.environ["TORCH_CUDA_ARCH_LIST"] = "8.0;8.6"
30
- print("Configuración de entorno de CUDA finalizada.")
31
-
32
- install_cuda_toolkit()
33
-
34
- print("Verificando instalación de PyTorch y versión de NVCC:")
35
- os.system("pip list | grep torch")
36
- os.system('nvcc -V')
37
-
38
- print("Compilando extensión de renderizador diferenciable...")
39
- os.system("cd /home/user/app/step1x3d_texture/differentiable_renderer/ && python setup.py install")
40
 
41
- print("Instalando rasterizador personalizado...")
42
- subprocess.run(shlex.split("pip install custom_rasterizer-0.1-cp310-cp310-linux_x86_64.whl"), check=True)
43
- print("Instalación y compilación completadas.")
44
 
45
  import uuid
46
  import torch
@@ -48,58 +47,88 @@ import trimesh
48
  import argparse
49
  import numpy as np
50
  import gradio as gr
 
 
51
  from step1x3d_geometry.models.pipelines.pipeline import Step1X3DGeometryPipeline
52
- from step1x3d_texture.pipelines.step1x_3d_texture_synthesis_pipeline import (
53
- Step1X3DTexturePipeline,
54
- )
55
  from step1x3d_geometry.models.pipelines.pipeline_utils import reduce_face, remove_degenerate_face
56
 
57
- # --------------------------------------------------------------------------
58
  # 2. CONFIGURACIÓN Y CARGA DE MODELOS
59
- # Aquí se definen los modelos a utilizar y se cargan en memoria.
60
- # --------------------------------------------------------------------------
61
  parser = argparse.ArgumentParser()
62
- parser.add_argument(
63
- "--geometry_model", type=str, default="Step1X-3D-Geometry-Label-1300m"
64
- )
65
- parser.add_argument(
66
- "--texture_model", type=str, default="Step1X-3D-Texture"
67
- )
68
  parser.add_argument("--cache_dir", type=str, default="cache")
69
  args = parser.parse_args()
70
 
71
  os.makedirs(args.cache_dir, exist_ok=True)
72
  device = "cuda" if torch.cuda.is_available() else "cpu"
 
73
 
74
- print(f"Cargando modelo de geometría: {args.geometry_model}...")
 
 
 
75
  geometry_model = Step1X3DGeometryPipeline.from_pretrained(
76
  "stepfun-ai/Step1X-3D", subfolder=args.geometry_model
77
  ).to(device)
78
- print("Modelo de geometría cargado.")
79
 
80
- print(f"Cargando modelo de textura: {args.texture_model}...")
81
  texture_model = Step1X3DTexturePipeline.from_pretrained("stepfun-ai/Step1X-3D", subfolder=args.texture_model)
82
- print("Modelo de textura cargado.")
83
 
 
 
 
 
 
 
84
 
85
- # --------------------------------------------------------------------------
86
- # 3. FUNCIONES DE GENERACIÓN SEPARADAS
87
- # Se divide la lógica en dos funciones: una para la geometría y otra para la textura.
88
- # --------------------------------------------------------------------------
89
 
90
- @spaces.GPU(duration=180)
91
- def generate_geometry(
92
- input_image_path, guidance_scale, inference_steps, max_facenum, symmetry, edge_type, progress=gr.Progress(track_tqdm=True)
93
- ):
94
  """
95
- Función que genera únicamente la geometría a partir de la imagen de entrada.
96
  """
97
- if input_image_path is None:
98
- raise gr.Error("Por favor, sube una imagen para empezar.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
- print("Iniciando generación de geometría...")
101
 
102
- # Elige el pipeline adecuado basado en el nombre del modelo
103
  if "Label" in args.geometry_model:
104
  symmetry_values = ["x", "asymmetry"]
105
  out = geometry_model(
@@ -118,135 +147,139 @@ def generate_geometry(
118
  max_facenum=int(max_facenum),
119
  )
120
 
121
- # Guarda el resultado en un fichero temporal
122
- save_name = str(uuid.uuid4())
123
- geometry_save_path = f"{args.cache_dir}/{save_name}.glb"
124
  geometry_mesh = out.mesh[0]
125
  geometry_mesh.export(geometry_save_path)
126
 
127
  torch.cuda.empty_cache()
128
- print(f"Geometría guardada en: {geometry_save_path}")
129
-
130
- # Devuelve la ruta para mostrarla en el visor y para guardarla en el estado
131
- return geometry_save_path, geometry_save_path
132
 
133
  @spaces.GPU(duration=120)
134
- def generate_texture(input_image_path, geometry_path, progress=gr.Progress(track_tqdm=True)):
135
- """
136
- Función que aplica la textura a una geometría ya generada.
137
- """
138
  if not geometry_path or not os.path.exists(geometry_path):
139
  raise gr.Error("Por favor, primero genera la geometría antes de texturizar.")
 
 
140
 
141
- print(f"Iniciando texturizado para la malla: {geometry_path}")
142
  geometry_mesh = trimesh.load(geometry_path)
143
 
144
- # Post-procesamiento opcional de la malla antes de texturizar
145
  geometry_mesh = remove_degenerate_face(geometry_mesh)
146
  geometry_mesh = reduce_face(geometry_mesh)
147
 
148
- # Llama al pipeline de texturizado
149
  textured_mesh = texture_model(input_image_path, geometry_mesh)
150
 
151
- # Guarda el resultado final
152
- save_name = os.path.basename(geometry_path).replace(".glb", "")
153
- textured_save_path = f"{args.cache_dir}/{save_name}-textured.glb"
154
  textured_mesh.export(textured_save_path)
155
 
156
  torch.cuda.empty_cache()
157
- print(f"Malla texturizada guardada en: {textured_save_path}")
158
-
159
  return textured_save_path
160
 
 
 
 
161
 
162
- # --------------------------------------------------------------------------
163
- # 4. INTERFAZ DE USUARIO CON GRADIO
164
- # Se define la apariencia y el comportamiento de la aplicación web.
165
- # --------------------------------------------------------------------------
166
-
167
- with gr.Blocks(title="Step1X-3D demo") as demo:
168
- gr.Markdown("# Step1X-3D")
169
- gr.Markdown("### Demostración de generación de modelos 3D a partir de una única imagen")
170
 
171
- # Componente de estado: guarda la ruta de la geometría entre los pasos
 
172
  geometry_path_state = gr.State()
173
 
174
  with gr.Row():
175
  with gr.Column(scale=2):
176
- input_image = gr.Image(label="Imagen de Entrada", type="filepath")
177
-
178
- with gr.Accordion(label="Parámetros de Generación", open=True):
179
- guidance_scale = gr.Number(label="Guidance Scale", value="7.5")
180
- inference_steps = gr.Slider(
181
- label="Pasos de Inferencia", minimum=1, maximum=100, value=50, step=1
182
- )
183
- max_facenum = gr.Number(label="Máx. Número de Caras", value="400000")
184
- symmetry = gr.Radio(
185
- choices=["symmetry", "asymmetry"],
186
- label="Tipo de Simetría",
187
- value="symmetry",
188
- type="index",
189
- )
190
- edge_type = gr.Radio(
191
- choices=["sharp", "normal", "smooth"],
192
- label="Tipo de Borde",
193
- value="sharp",
194
- type="value",
195
- )
196
-
197
  with gr.Row():
198
- btn_geo = gr.Button("1. Generar Geometría", variant="primary")
199
- btn_tex = gr.Button("2. Generar Textura", visible=False, variant="primary")
200
 
201
- with gr.Column(scale=4):
202
- textured_preview = gr.Model3D(label="Modelo con Textura", height=380, clear_color=[0.0, 0.0, 0.0, 0.0])
203
- geometry_preview = gr.Model3D(label="Modelo (solo geometría)", height=380, clear_color=[0.0, 0.0, 0.0, 0.0])
 
 
204
 
205
  with gr.Column(scale=1):
206
  gr.Examples(
207
  examples=[
208
- ["examples/images/000.png"],
209
- ["examples/images/001.png"],
210
- ["examples/images/004.png"],
211
- ["examples/images/008.png"],
212
- ["examples/images/028.png"],
213
- ["examples/images/032.png"],
214
- ["examples/images/061.png"],
215
- ["examples/images/107.png"],
216
  ],
217
- inputs=[input_image],
218
- cache_examples=False,
219
  )
220
 
221
- # --- Lógica de los botones y el flujo de la interfaz ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
- # 1. Cuando el usuario hace clic en "Generar Geometría"
224
  btn_geo.click(
225
  fn=generate_geometry,
226
- inputs=[
227
- input_image,
228
- guidance_scale,
229
- inference_steps,
230
- max_facenum,
231
- symmetry,
232
- edge_type,
233
- ],
234
- outputs=[geometry_preview, geometry_path_state]
235
  ).then(
236
- # 2. Cuando la geometría termina, se ejecuta esta parte
237
- fn=lambda: {
238
- btn_tex: gr.update(visible=True), # Hace visible el botón de texturizado
239
- textured_preview: gr.update(value=None) # Limpia la vista previa de texturas anterior
240
- },
241
- outputs=[btn_tex, textured_preview]
242
  )
243
 
244
- # 3. Cuando el usuario hace clic en "Generar Textura"
245
  btn_tex.click(
246
  fn=generate_texture,
247
- inputs=[input_image, geometry_path_state],
248
  outputs=[textured_preview],
249
  )
250
 
251
- # Lanza la aplicación
252
  demo.launch(ssr_mode=False)
 
1
+ # ==============================================================================
2
+ # 1. INSTALACIÓN DEL ENTORNO Y DEPENDENCIAS
3
+ # ==============================================================================
4
  import os
5
  import shlex
6
  import spaces
7
  import subprocess
8
+ import logging
9
 
10
+ # Configuración del logging para depuración
11
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - Step1X-3D - %(levelname)s - %(message)s')
12
+
13
+ def install_dependencies():
14
+ """Instala el toolkit de CUDA y compila las extensiones C++/CUDA necesarias."""
15
+ logging.info("Iniciando la instalación de dependencias...")
 
 
 
 
 
 
 
16
 
17
+ # Instalar CUDA Toolkit
18
+ CUDA_TOOLKIT_URL = "https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.14_linux.run"
19
+ CUDA_TOOLKIT_FILE = f"/tmp/{os.path.basename(CUDA_TOOLKIT_URL)}"
20
+ if not os.path.exists("/usr/local/cuda"):
21
+ logging.info("Descargando e instalando CUDA Toolkit...")
22
+ subprocess.call(["wget", "-q", CUDA_TOOLKIT_URL, "-O", CUDA_TOOLKIT_FILE])
23
+ subprocess.call(["chmod", "+x", CUDA_TOOLKIT_FILE])
24
+ subprocess.call([CUDA_TOOLKIT_FILE, "--silent", "--toolkit"])
25
+ else:
26
+ logging.info("CUDA Toolkit ya está instalado.")
27
 
28
  os.environ["CUDA_HOME"] = "/usr/local/cuda"
29
+ os.environ["PATH"] = f"{os.environ['CUDA_HOME']}/bin:{os.environ['PATH']}"
30
+ os.environ["LD_LIBRARY_PATH"] = f"{os.environ['CUDA_HOME']}/lib:{os.environ.get('LD_LIBRARY_PATH', '')}"
 
 
 
31
  os.environ["TORCH_CUDA_ARCH_LIST"] = "8.0;8.6"
32
+
33
+ # Compilar extensiones personalizadas
34
+ logging.info("Compilando extensiones de renderizado...")
35
+ renderer_path = "/home/user/app/step1x3d_texture/differentiable_renderer/"
36
+ subprocess.run(f"cd {renderer_path} && python setup.py install", shell=True, check=True)
37
+ subprocess.run(shlex.split("pip install custom_rasterizer-0.1-cp310-cp310-linux_x86_64.whl"), check=True)
38
+
39
+ logging.info("Instalación completada.")
40
+ os.system('nvcc -V')
 
41
 
42
+ install_dependencies()
 
 
43
 
44
  import uuid
45
  import torch
 
47
  import argparse
48
  import numpy as np
49
  import gradio as gr
50
+ from PIL import Image
51
+ from diffusers import StableDiffusionXLPipeline
52
  from step1x3d_geometry.models.pipelines.pipeline import Step1X3DGeometryPipeline
53
+ from step1x3d_texture.pipelines.step1x_3d_texture_synthesis_pipeline import Step1X3DTexturePipeline
 
 
54
  from step1x3d_geometry.models.pipelines.pipeline_utils import reduce_face, remove_degenerate_face
55
 
56
+ # ==============================================================================
57
  # 2. CONFIGURACIÓN Y CARGA DE MODELOS
58
+ # ==============================================================================
59
+
60
  parser = argparse.ArgumentParser()
61
+ parser.add_argument("--geometry_model", type=str, default="Step1X-3D-Geometry-Label-1300m")
62
+ parser.add_argument("--texture_model", type=str, default="Step1X-3D-Texture")
63
+ parser.add_argument("--text_to_image_model", type=str, default="stabilityai/stable-diffusion-xl-base-1.0")
 
 
 
64
  parser.add_argument("--cache_dir", type=str, default="cache")
65
  args = parser.parse_args()
66
 
67
  os.makedirs(args.cache_dir, exist_ok=True)
68
  device = "cuda" if torch.cuda.is_available() else "cpu"
69
+ torch_dtype = torch.float16
70
 
71
+ logging.info("Cargando modelos... Este proceso puede tardar varios minutos.")
72
+
73
+ # --- Carga de Modelos Step1X-3D ---
74
+ logging.info(f"Cargando modelo de geometría: {args.geometry_model}")
75
  geometry_model = Step1X3DGeometryPipeline.from_pretrained(
76
  "stepfun-ai/Step1X-3D", subfolder=args.geometry_model
77
  ).to(device)
 
78
 
79
+ logging.info(f"Cargando modelo de textura: {args.texture_model}")
80
  texture_model = Step1X3DTexturePipeline.from_pretrained("stepfun-ai/Step1X-3D", subfolder=args.texture_model)
 
81
 
82
+ # --- Carga de Modelo de Texto a Imagen ---
83
+ logging.info(f"Cargando modelo de texto a imagen: {args.text_to_image_model}")
84
+ text_to_image_pipe = StableDiffusionXLPipeline.from_pretrained(
85
+ args.text_to_image_model, torch_dtype=torch_dtype, variant="fp16", use_safetensors=True
86
+ ).to(device)
87
+ logging.info("Todos los modelos han sido cargados correctamente.")
88
 
89
+ # ==============================================================================
90
+ # 3. FUNCIONES DE GENERACIÓN POR PASOS
91
+ # ==============================================================================
 
92
 
93
+ @spaces.GPU(duration=60)
94
+ def generate_image_from_text(prompt, negative_prompt, guidance_scale, num_steps, seed):
 
 
95
  """
96
+ Paso 0: Genera una imagen a partir de un prompt de texto.
97
  """
98
+ if not prompt:
99
+ raise gr.Error("Por favor, introduce un prompt.")
100
+
101
+ logging.info(f"Generando imagen desde el prompt: '{prompt}'")
102
+
103
+ # Añadir modificadores para obtener un objeto aislado con fondo blanco
104
+ final_prompt = f"a 3d model of {prompt}, professional 3d model, octane render, highly detailed, volumetric, dramatic lighting, on a white background, studio lighting, 8k"
105
+ final_negative_prompt = f"text, watermark, blurry, low quality, deformed, ugly, {negative_prompt}"
106
+
107
+ generator = torch.Generator(device=device).manual_seed(int(seed))
108
+
109
+ image = text_to_image_pipe(
110
+ prompt=final_prompt,
111
+ negative_prompt=final_negative_prompt,
112
+ num_inference_steps=int(num_steps),
113
+ guidance_scale=float(guidance_scale),
114
+ generator=generator,
115
+ ).images[0]
116
+
117
+ save_name = str(uuid.uuid4())
118
+ image_path = f"{args.cache_dir}/{save_name}_generated_image.png"
119
+ image.save(image_path)
120
+
121
+ logging.info(f"Imagen generada y guardada en: {image_path}")
122
+ return image_path
123
+
124
+ @spaces.GPU(duration=180)
125
+ def generate_geometry(input_image_path, guidance_scale, inference_steps, max_facenum, symmetry, edge_type):
126
+ """Paso 1: Genera la geometría a partir de la imagen generada."""
127
+ if not input_image_path or not os.path.exists(input_image_path):
128
+ raise gr.Error("Primero debes generar una imagen desde el texto.")
129
 
130
+ logging.info(f"Iniciando generación de geometría desde: {os.path.basename(input_image_path)}")
131
 
 
132
  if "Label" in args.geometry_model:
133
  symmetry_values = ["x", "asymmetry"]
134
  out = geometry_model(
 
147
  max_facenum=int(max_facenum),
148
  )
149
 
150
+ save_name = os.path.basename(input_image_path).replace("_generated_image.png", "")
151
+ geometry_save_path = f"{args.cache_dir}/{save_name}_geometry.glb"
 
152
  geometry_mesh = out.mesh[0]
153
  geometry_mesh.export(geometry_save_path)
154
 
155
  torch.cuda.empty_cache()
156
+ logging.info(f"Geometría guardada en: {geometry_save_path}")
157
+ return geometry_save_path
 
 
158
 
159
  @spaces.GPU(duration=120)
160
+ def generate_texture(input_image_path, geometry_path):
161
+ """Paso 2: Aplica la textura a la geometría generada."""
 
 
162
  if not geometry_path or not os.path.exists(geometry_path):
163
  raise gr.Error("Por favor, primero genera la geometría antes de texturizar.")
164
+ if not input_image_path or not os.path.exists(input_image_path):
165
+ raise gr.Error("Se necesita la imagen generada para el texturizado.")
166
 
167
+ logging.info(f"Iniciando texturizado para la malla: {os.path.basename(geometry_path)}")
168
  geometry_mesh = trimesh.load(geometry_path)
169
 
170
+ # Post-procesamiento
171
  geometry_mesh = remove_degenerate_face(geometry_mesh)
172
  geometry_mesh = reduce_face(geometry_mesh)
173
 
 
174
  textured_mesh = texture_model(input_image_path, geometry_mesh)
175
 
176
+ save_name = os.path.basename(geometry_path).replace("_geometry.glb", "")
177
+ textured_save_path = f"{args.cache_dir}/{save_name}_textured.glb"
 
178
  textured_mesh.export(textured_save_path)
179
 
180
  torch.cuda.empty_cache()
181
+ logging.info(f"Malla texturizada guardada en: {textured_save_path}")
 
182
  return textured_save_path
183
 
184
+ # ==============================================================================
185
+ # 4. INTERFAZ DE GRADIO
186
+ # ==============================================================================
187
 
188
+ with gr.Blocks(title="Step1X-3D", css="footer {display: none !important;} a {text-decoration: none !important;}") as demo:
189
+ gr.Markdown("# Step1X-3D: Flujo de Texto a Malla 3D Texturizada")
190
+ gr.Markdown("Flujo de trabajo en 3 pasos: **0. Texto a Imagen 1. Generar Geometría → 2. Generar Textura**")
 
 
 
 
 
191
 
192
+ # Estados para mantener las rutas de los archivos
193
+ generated_image_path_state = gr.State()
194
  geometry_path_state = gr.State()
195
 
196
  with gr.Row():
197
  with gr.Column(scale=2):
198
+ # --- Panel de Entradas ---
199
+ prompt = gr.Textbox(label="Paso 0: Describe el objeto que quieres crear", value="a medieval fantasy sword")
200
+
201
+ with gr.Accordion(label="Opciones Avanzadas", open=False):
202
+ gr.Markdown("### Opciones de Texto a Imagen (Paso 0)")
203
+ neg_prompt_image = gr.Textbox(label="Negative Prompt (Imagen)", value="low quality, blurry, text, watermark")
204
+ guidance_image = gr.Slider(0.1, 15.0, label="Guidance Scale (Imagen)", value=7.5, step=0.1)
205
+ steps_image = gr.Slider(10, 100, label="Steps (Imagen)", value=25, step=1)
206
+ seed = gr.Number(label="Seed", value=42, precision=0)
207
+
208
+ gr.Markdown("---")
209
+ gr.Markdown("### Opciones de Generación 3D (Paso 1)")
210
+ guidance_3d = gr.Number(label="Guidance Scale (3D)", value="7.5")
211
+ steps_3d = gr.Slider(label="Inference Steps (3D)", minimum=20, maximum=100, value=50, step=1)
212
+ max_facenum = gr.Number(label="Max Face Num", value="200000")
213
+ symmetry = gr.Radio(choices=["symmetry", "asymmetry"], label="Symmetry", value="symmetry", type="index")
214
+ edge_type = gr.Radio(choices=["sharp", "normal", "smooth"], label="Edge Type", value="sharp", type="value")
215
+
216
+ with gr.Row():
217
+ btn_gen_image = gr.Button("0. Generar Imagen", variant="secondary")
 
218
  with gr.Row():
219
+ btn_geo = gr.Button("1. Generar Geometría", interactive=False)
220
+ btn_tex = gr.Button("2. Generar Textura", interactive=False)
221
 
222
+ with gr.Column(scale=3):
223
+ # --- Panel de Salidas ---
224
+ generated_image_preview = gr.Image(label="Resultado de Texto a Imagen", type="filepath", interactive=False, height=400)
225
+ geometry_preview = gr.Model3D(label="Vista Previa de la Geometría", height=400, clear_color=[0.0, 0.0, 0.0, 0.0])
226
+ textured_preview = gr.Model3D(label="Vista Previa del Modelo Texturizado", height=400, clear_color=[0.0, 0.0, 0.0, 0.0])
227
 
228
  with gr.Column(scale=1):
229
  gr.Examples(
230
  examples=[
231
+ ["a futuristic spaceship"],
232
+ ["a cartoon style monster"],
233
+ ["a red sports car"],
234
+ ["a delicious hamburger"],
235
+ ["a golden trophy"],
 
 
 
236
  ],
237
+ inputs=[prompt], cache_examples=False
 
238
  )
239
 
240
+ # --- Lógica de la Interfaz ---
241
+
242
+ def on_image_generated(path):
243
+ """Se ejecuta cuando la imagen 2D ha sido generada."""
244
+ return {
245
+ generated_image_path_state: path,
246
+ btn_geo: gr.update(interactive=True, variant="primary"),
247
+ btn_tex: gr.update(interactive=False),
248
+ geometry_preview: gr.update(value=None),
249
+ textured_preview: gr.update(value=None),
250
+ }
251
+
252
+ def on_geometry_generated(path):
253
+ """Se ejecuta cuando la geometría 3D ha sido generada."""
254
+ return {
255
+ geometry_path_state: path,
256
+ btn_tex: gr.update(interactive=True, variant="primary"),
257
+ }
258
+
259
+ btn_gen_image.click(
260
+ fn=generate_image_from_text,
261
+ inputs=[prompt, neg_prompt_image, guidance_image, steps_image, seed],
262
+ outputs=[generated_image_preview]
263
+ ).then(
264
+ fn=on_image_generated,
265
+ inputs=[generated_image_preview],
266
+ outputs=[generated_image_path_state, btn_geo, btn_tex, geometry_preview, textured_preview]
267
+ )
268
 
 
269
  btn_geo.click(
270
  fn=generate_geometry,
271
+ inputs=[generated_image_path_state, guidance_3d, steps_3d, max_facenum, symmetry, edge_type],
272
+ outputs=[geometry_preview]
 
 
 
 
 
 
 
273
  ).then(
274
+ fn=on_geometry_generated,
275
+ inputs=[geometry_preview],
276
+ outputs=[geometry_path_state, btn_tex]
 
 
 
277
  )
278
 
 
279
  btn_tex.click(
280
  fn=generate_texture,
281
+ inputs=[generated_image_path_state, geometry_path_state],
282
  outputs=[textured_preview],
283
  )
284
 
 
285
  demo.launch(ssr_mode=False)