prithivMLmods commited on
Commit
be8ef69
·
verified ·
1 Parent(s): 2bc4483

Upload 98 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +33 -0
  2. app.py +256 -0
  3. configs/infer_dit_refine.yaml +72 -0
  4. configs/train_dit_refine.yaml +138 -0
  5. configs/train_vae_refine.yaml +89 -0
  6. docs/assets/favicon.png +0 -0
  7. docs/assets/images/1.png +3 -0
  8. docs/assets/images/10.png +3 -0
  9. docs/assets/images/11.png +3 -0
  10. docs/assets/images/12.png +3 -0
  11. docs/assets/images/13.png +3 -0
  12. docs/assets/images/2.png +3 -0
  13. docs/assets/images/3.png +3 -0
  14. docs/assets/images/4.png +3 -0
  15. docs/assets/images/5.png +3 -0
  16. docs/assets/images/6.png +3 -0
  17. docs/assets/images/7.png +3 -0
  18. docs/assets/images/8.png +3 -0
  19. docs/assets/images/9.png +3 -0
  20. docs/assets/images/comp-close.png +3 -0
  21. docs/assets/images/comp-open1.png +3 -0
  22. docs/assets/images/comp-open2.png +3 -0
  23. docs/assets/images/pipeline.png +3 -0
  24. docs/assets/images/teaser.png +3 -0
  25. docs/assets/videos/1.mp4 +3 -0
  26. docs/assets/videos/10.mp4 +3 -0
  27. docs/assets/videos/11.mp4 +3 -0
  28. docs/assets/videos/12.mp4 +3 -0
  29. docs/assets/videos/13.mp4 +3 -0
  30. docs/assets/videos/2.mp4 +3 -0
  31. docs/assets/videos/3.mp4 +3 -0
  32. docs/assets/videos/4.mp4 +3 -0
  33. docs/assets/videos/5.mp4 +3 -0
  34. docs/assets/videos/6.mp4 +3 -0
  35. docs/assets/videos/7.mp4 +3 -0
  36. docs/assets/videos/8.mp4 +3 -0
  37. docs/assets/videos/9.mp4 +3 -0
  38. docs/carousel.css +102 -0
  39. docs/carousel.js +249 -0
  40. docs/index copy.html +788 -0
  41. docs/index.html +884 -0
  42. docs/main.css +626 -0
  43. docs/pv.css +66 -0
  44. docs/style.css +2 -0
  45. docs/stylesheet.css +3 -0
  46. docs/window.css +175 -0
  47. inputs/coarse_mesh/1.glb +3 -0
  48. inputs/image/1.png +3 -0
  49. requirements.txt +34 -0
  50. scripts/gradio_app.py +218 -0
.gitattributes CHANGED
@@ -33,3 +33,36 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ docs/assets/images/1.png filter=lfs diff=lfs merge=lfs -text
37
+ docs/assets/images/10.png filter=lfs diff=lfs merge=lfs -text
38
+ docs/assets/images/11.png filter=lfs diff=lfs merge=lfs -text
39
+ docs/assets/images/12.png filter=lfs diff=lfs merge=lfs -text
40
+ docs/assets/images/13.png filter=lfs diff=lfs merge=lfs -text
41
+ docs/assets/images/2.png filter=lfs diff=lfs merge=lfs -text
42
+ docs/assets/images/3.png filter=lfs diff=lfs merge=lfs -text
43
+ docs/assets/images/4.png filter=lfs diff=lfs merge=lfs -text
44
+ docs/assets/images/5.png filter=lfs diff=lfs merge=lfs -text
45
+ docs/assets/images/6.png filter=lfs diff=lfs merge=lfs -text
46
+ docs/assets/images/7.png filter=lfs diff=lfs merge=lfs -text
47
+ docs/assets/images/8.png filter=lfs diff=lfs merge=lfs -text
48
+ docs/assets/images/9.png filter=lfs diff=lfs merge=lfs -text
49
+ docs/assets/images/comp-close.png filter=lfs diff=lfs merge=lfs -text
50
+ docs/assets/images/comp-open1.png filter=lfs diff=lfs merge=lfs -text
51
+ docs/assets/images/comp-open2.png filter=lfs diff=lfs merge=lfs -text
52
+ docs/assets/images/pipeline.png filter=lfs diff=lfs merge=lfs -text
53
+ docs/assets/images/teaser.png filter=lfs diff=lfs merge=lfs -text
54
+ docs/assets/videos/1.mp4 filter=lfs diff=lfs merge=lfs -text
55
+ docs/assets/videos/10.mp4 filter=lfs diff=lfs merge=lfs -text
56
+ docs/assets/videos/11.mp4 filter=lfs diff=lfs merge=lfs -text
57
+ docs/assets/videos/12.mp4 filter=lfs diff=lfs merge=lfs -text
58
+ docs/assets/videos/13.mp4 filter=lfs diff=lfs merge=lfs -text
59
+ docs/assets/videos/2.mp4 filter=lfs diff=lfs merge=lfs -text
60
+ docs/assets/videos/3.mp4 filter=lfs diff=lfs merge=lfs -text
61
+ docs/assets/videos/4.mp4 filter=lfs diff=lfs merge=lfs -text
62
+ docs/assets/videos/5.mp4 filter=lfs diff=lfs merge=lfs -text
63
+ docs/assets/videos/6.mp4 filter=lfs diff=lfs merge=lfs -text
64
+ docs/assets/videos/7.mp4 filter=lfs diff=lfs merge=lfs -text
65
+ docs/assets/videos/8.mp4 filter=lfs diff=lfs merge=lfs -text
66
+ docs/assets/videos/9.mp4 filter=lfs diff=lfs merge=lfs -text
67
+ inputs/coarse_mesh/1.glb filter=lfs diff=lfs merge=lfs -text
68
+ inputs/image/1.png filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import gc
3
+ import os
4
+ import sys
5
+
6
+ import gradio as gr
7
+ import torch
8
+ import spaces
9
+ from omegaconf import OmegaConf
10
+
11
+ # Add project root to path so we can run from anywhere
12
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
13
+
14
+ from ultrashape.rembg import BackgroundRemover
15
+ from ultrashape.utils.misc import instantiate_from_config
16
+ from ultrashape.surface_loaders import SharpEdgeSurfaceLoader
17
+ from ultrashape.utils import voxelize_from_point
18
+ from ultrashape.pipelines import UltraShapePipeline
19
+
20
+ # Global variables to cache the model
21
+ MODEL_CACHE = {}
22
+
23
+
24
+ def get_pipeline_cached(config_path, ckpt_path, device='cuda', low_vram=False):
25
+ # Check if we have a valid cached pipeline for this checkpoint
26
+ if "pipeline" in MODEL_CACHE and MODEL_CACHE.get("ckpt_path") == ckpt_path:
27
+ print("Using cached pipeline...")
28
+ return MODEL_CACHE["pipeline"], MODEL_CACHE["config"]
29
+
30
+ # Clear old cache if it exists (e.g. different checkpoint)
31
+ if MODEL_CACHE:
32
+ print("Clearing old model cache...")
33
+ MODEL_CACHE.clear()
34
+ gc.collect()
35
+ torch.cuda.empty_cache()
36
+
37
+ print(f"Loading config from {config_path}...")
38
+ config = OmegaConf.load(config_path)
39
+
40
+ print("Instantiating VAE...")
41
+ vae = instantiate_from_config(config.model.params.vae_config)
42
+
43
+ print("Instantiating DiT...")
44
+ dit = instantiate_from_config(config.model.params.dit_cfg)
45
+
46
+ print("Instantiating Conditioner...")
47
+ conditioner = instantiate_from_config(config.model.params.conditioner_config)
48
+
49
+ print("Instantiating Scheduler & Processor...")
50
+ scheduler = instantiate_from_config(config.model.params.scheduler_cfg)
51
+ image_processor = instantiate_from_config(config.model.params.image_processor_cfg)
52
+
53
+ print(f"Loading weights from {ckpt_path}...")
54
+ weights = torch.load(ckpt_path, map_location='cpu')
55
+
56
+ vae.load_state_dict(weights['vae'], strict=True)
57
+ dit.load_state_dict(weights['dit'], strict=True)
58
+ conditioner.load_state_dict(weights['conditioner'], strict=True)
59
+
60
+ vae.eval().to(device)
61
+ dit.eval().to(device)
62
+ conditioner.eval().to(device)
63
+
64
+ if hasattr(vae, 'enable_flashvdm_decoder'):
65
+ vae.enable_flashvdm_decoder()
66
+
67
+ print("Creating Pipeline...")
68
+ pipeline = UltraShapePipeline(
69
+ vae=vae,
70
+ model=dit,
71
+ scheduler=scheduler,
72
+ conditioner=conditioner,
73
+ image_processor=image_processor
74
+ )
75
+
76
+ if low_vram:
77
+ pipeline.enable_model_cpu_offload()
78
+
79
+ MODEL_CACHE["pipeline"] = pipeline
80
+ MODEL_CACHE["config"] = config
81
+ MODEL_CACHE["ckpt_path"] = ckpt_path
82
+
83
+ return pipeline, config
84
+
85
+
86
+ @spaces.GPU
87
+ def predict(
88
+ image_input,
89
+ mesh_input,
90
+ steps,
91
+ scale,
92
+ octree_res,
93
+ num_latents,
94
+ chunk_size,
95
+ seed,
96
+ remove_bg,
97
+ ckpt_path,
98
+ low_vram
99
+ ):
100
+ # Aggressive memory cleanup at start
101
+ gc.collect()
102
+ torch.cuda.empty_cache()
103
+
104
+ try:
105
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
106
+
107
+ # Resolve config path relative to this script
108
+ base_dir = os.path.dirname(os.path.abspath(__file__))
109
+ config_path = os.path.join(base_dir, "configs", "infer_dit_refine.yaml")
110
+
111
+ if not os.path.exists(config_path):
112
+ raise gr.Error(f"Config not found at {config_path}")
113
+
114
+ if not os.path.exists(ckpt_path):
115
+ raise gr.Error(f"Checkpoint not found at '{ckpt_path}'. Please provide a valid checkpoint path or download the model weights.")
116
+
117
+ pipeline, config = get_pipeline_cached(config_path, ckpt_path, device, low_vram)
118
+
119
+ voxel_res = config.model.params.vae_config.params.voxel_query_res
120
+
121
+ print(f"Initializing Surface Loader (Token Num: {num_latents})...")
122
+ loader = SharpEdgeSurfaceLoader(
123
+ num_sharp_points=204800,
124
+ num_uniform_points=204800,
125
+ )
126
+
127
+ print(f"Processing inputs...")
128
+ if image_input is None:
129
+ raise gr.Error("Image input is required")
130
+ if mesh_input is None:
131
+ raise gr.Error("Mesh input is required")
132
+
133
+ image = image_input.convert("RGBA")
134
+
135
+ if remove_bg or image.mode != 'RGBA':
136
+ rembg = BackgroundRemover()
137
+ image = rembg(image)
138
+
139
+ # Handle mesh input - Gradio Model3D returns path to file
140
+ surface = loader(mesh_input, normalize_scale=scale).to(device, dtype=torch.float16)
141
+ pc = surface[:, :, :3] # [B, N, 3]
142
+
143
+ # Voxelize
144
+ _, voxel_idx = voxelize_from_point(pc, num_latents, resolution=voxel_res)
145
+
146
+ print("Running diffusion process...")
147
+ gen_device = "cpu" if low_vram else device
148
+ generator = torch.Generator(gen_device).manual_seed(int(seed))
149
+
150
+ with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
151
+ mesh_out_list, _ = pipeline(
152
+ image=image,
153
+ voxel_cond=voxel_idx,
154
+ generator=generator,
155
+ box_v=1.0,
156
+ mc_level=0.0,
157
+ octree_resolution=int(octree_res),
158
+ num_chunks=int(chunk_size),
159
+ num_inference_steps=int(steps)
160
+ )
161
+
162
+ # Save output
163
+ output_dir = os.path.join(base_dir, "outputs_gradio")
164
+ os.makedirs(output_dir, exist_ok=True)
165
+ base_name = "output"
166
+ save_path = os.path.join(output_dir, f"{base_name}_refined.glb")
167
+
168
+ mesh_out = mesh_out_list[0]
169
+ mesh_out.export(save_path)
170
+ print(f"Successfully saved to {save_path}")
171
+
172
+ return save_path
173
+
174
+ except Exception as e:
175
+ import traceback
176
+ traceback.print_exc()
177
+ raise gr.Error(str(e))
178
+ finally:
179
+ # Aggressive memory cleanup at end
180
+ gc.collect()
181
+ torch.cuda.empty_cache()
182
+
183
+
184
+ def main():
185
+ parser = argparse.ArgumentParser(description="UltraShape Gradio App")
186
+ parser.add_argument("--ckpt", type=str, default="checkpoints/ultrashape.pt", help="Path to split checkpoint (.pt)")
187
+ parser.add_argument("--share", action="store_true", help="Share the gradio app")
188
+ parser.add_argument("--low_vram", action="store_true", help="Optimize for low VRAM usage")
189
+
190
+ args = parser.parse_args()
191
+
192
+ # Define a clean & modern Gradio Theme
193
+ custom_theme = gr.themes.Soft(
194
+ primary_hue="blue",
195
+ secondary_hue="indigo",
196
+ ).set(
197
+ button_primary_background_fill="*primary_500",
198
+ button_primary_background_fill_hover="*primary_600",
199
+ )
200
+
201
+ # Define Gradio Interface
202
+ with gr.Blocks(title="UltraShape 1.0", theme=custom_theme) as demo:
203
+ gr.Markdown(
204
+ """
205
+ <div style="text-align: center;">
206
+ <h1>UltraShape 1.0</h1>
207
+ <h3>High-Fidelity 3D Shape Generation via Scalable Geometric Refinement</h3>
208
+ <p>Upload a reference image and a coarse mesh to generate a refined high-quality 3D shape.</p>
209
+ </div>
210
+ """
211
+ )
212
+
213
+ with gr.Row():
214
+ with gr.Column(scale=1):
215
+ gr.Markdown("### Input Parameters")
216
+ image_input = gr.Image(type="pil", label="Reference Image", image_mode="RGBA")
217
+ mesh_input = gr.Model3D(label="Coarse Mesh (.glb, .obj)")
218
+
219
+ with gr.Accordion("Advanced Options", open=False):
220
+ steps = gr.Slider(minimum=1, maximum=200, value=50, step=1, label="Inference Steps (12-50)")
221
+ scale = gr.Slider(minimum=0.1, maximum=2.0, value=0.99, label="Mesh Normalization Scale")
222
+ octree_res = gr.Slider(minimum=64, maximum=2048, value=1024, step=64, label="Octree Resolution")
223
+ num_latents = gr.Slider(minimum=1024, maximum=32768, value=32768, step=128,
224
+ label="Number of Latent Tokens (Decrease if OOM)")
225
+ chunk_size = gr.Slider(minimum=512, maximum=10000, value=2048, step=512,
226
+ label="Chunk Size (Decrease if OOM)")
227
+ seed = gr.Number(value=42, label="Random Seed")
228
+ remove_bg = gr.Checkbox(label="Remove Background from Image", value=False)
229
+
230
+ run_btn = gr.Button("Generate Refined Shape", variant="primary", size="lg")
231
+
232
+ with gr.Column(scale=1):
233
+ gr.Markdown("### Refined Output")
234
+ output_model = gr.Model3D(label="High-Fidelity Mesh", interactive=False)
235
+
236
+ gr.Markdown(
237
+ """
238
+ *Note: If you encounter Out-of-Memory (OOM) errors, try checking the 'Advanced Options' and lowering the `Number of Latent Tokens` (e.g., 8192) and `Chunk Size` (e.g., 2000), or run the app with the `--low_vram` flag.*
239
+ """
240
+ )
241
+
242
+ run_btn.click(
243
+ fn=lambda img, mesh, s, sc, oct, nml, chk, sd, rm: predict(
244
+ img, mesh, s, sc, oct, nml, chk, sd, rm, args.ckpt, args.low_vram
245
+ ),
246
+ inputs=[
247
+ image_input, mesh_input, steps, scale, octree_res,
248
+ num_latents, chunk_size, seed, remove_bg
249
+ ],
250
+ outputs=[output_model]
251
+ )
252
+
253
+ demo.launch(share=args.share, server_name='0.0.0.0', server_port=7860)
254
+
255
+ if __name__ == "__main__":
256
+ main()
configs/infer_dit_refine.yaml ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model:
2
+ target: ultrashape.pipelines.UltraShapePipeline
3
+ params:
4
+ # 1. VAE Config
5
+ vae_config:
6
+ target: ultrashape.models.autoencoders.ShapeVAE
7
+ params:
8
+ num_latents: &token_num 32768 # infer token_num
9
+ embed_dim: 64
10
+ num_freqs: 8
11
+ include_pi: false
12
+ heads: 16
13
+ width: 1024
14
+ point_feats: 4
15
+ num_encoder_layers: 8
16
+ num_decoder_layers: 16
17
+ pc_size: 409600 # num_s (204800) + num_u (204800)
18
+ pc_sharpedge_size: 0
19
+ downsample_ratio: 20
20
+ qkv_bias: false
21
+ qk_norm: true
22
+ scale_factor: 1.0039506158752403
23
+ geo_decoder_mlp_expand_ratio: 4
24
+ # geo_decoder_downsample_ratio: 1
25
+ geo_decoder_ln_post: true
26
+ enable_flashvdm: true
27
+ jitter_query: false
28
+ voxel_query: true
29
+ voxel_query_res: &voxel_query_res 128
30
+
31
+ # 2. DiT Denoiser Config
32
+ dit_cfg:
33
+ target: ultrashape.models.denoisers.dit_mask.RefineDiT
34
+ params:
35
+ input_size: *token_num
36
+ in_channels: 64
37
+ hidden_size: 2048
38
+ context_dim: 1024
39
+ depth: 21
40
+ num_heads: 16
41
+ qk_norm: true
42
+ text_len: 1370
43
+ qk_norm_type: 'rms'
44
+ qkv_bias: false
45
+ num_moe_layers: 6
46
+ num_experts: 8
47
+ moe_top_k: 2
48
+ voxel_query_res: *voxel_query_res
49
+
50
+ # 3. Image Encoder Config
51
+ conditioner_config:
52
+ target: ultrashape.models.conditioner_mask.SingleImageEncoder
53
+ params:
54
+ drop_ratio: 0.0
55
+ main_image_encoder:
56
+ type: DinoImageEncoder
57
+ kwargs:
58
+ version: 'facebook/dinov2-large'
59
+ image_size: 1022
60
+ use_cls_token: true
61
+
62
+ # 4. Scheduler Config
63
+ scheduler_cfg:
64
+ target: ultrashape.schedulers.FlowMatchEulerDiscreteScheduler
65
+ params:
66
+ num_train_timesteps: 1000
67
+
68
+ # 5. Image Processor
69
+ image_processor_cfg:
70
+ target: ultrashape.preprocessors.ImageProcessorV2
71
+ params:
72
+ size: 1024
configs/train_dit_refine.yaml ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: "UltraShape Refine DiT"
2
+
3
+ training:
4
+ # ckpt_path:
5
+ steps: 10_0000_0000
6
+ use_amp: true
7
+ amp_type: "bf16"
8
+ base_lr: 1e-5
9
+ gradient_clip_val: 1.0
10
+ gradient_clip_algorithm: "norm"
11
+ every_n_train_steps: 2500
12
+ val_check_interval: 1000
13
+ limit_val_batches: 16
14
+ accumulate_grad_batches: 4
15
+
16
+ dataset:
17
+ target: ultrashape.data.objaverse_dit.ObjaverseDataModule
18
+ params:
19
+ batch_size: 1
20
+ num_workers: 4
21
+ val_num_workers: 4
22
+
23
+ # data
24
+ training_data_list: data/data_list
25
+ sample_pcd_dir: data/sample
26
+ image_data_json: data/render.json
27
+
28
+ # image
29
+ image_size: &image_size 1022 # 518
30
+ mean: &mean [0.5, 0.5, 0.5]
31
+ std: &std [0.5, 0.5, 0.5]
32
+ padding: true
33
+
34
+ # input_pcd
35
+ pc_size: &pc_size 163840
36
+ pc_sharpedge_size: &pc_sharpedge_size 0
37
+ sharpedge_label: &sharpedge_label true
38
+ return_normal: true
39
+
40
+ model:
41
+ target: ultrashape.models.diffusion.flow_matching_dit_trainer.Diffuser
42
+ params:
43
+ ckpt_path: ckpt/dit_step=XXX.ckpt
44
+ scale_by_std: false
45
+ z_scale_factor: &z_scale_factor 1.0039506158752403
46
+ torch_compile: false
47
+
48
+ vae_config:
49
+ target: ultrashape.models.autoencoders.ShapeVAE
50
+ from_pretrained: ckpt/vae_step=XXX.ckpt
51
+ params:
52
+ num_latents: &num_latents 8192 # 4096
53
+ embed_dim: 64
54
+ num_freqs: 8
55
+ include_pi: false
56
+ heads: 16
57
+ width: 1024
58
+ point_feats: 4
59
+ num_encoder_layers: 8
60
+ num_decoder_layers: 16
61
+ pc_size: *pc_size
62
+ pc_sharpedge_size: *pc_sharpedge_size
63
+ downsample_ratio: 20
64
+ qkv_bias: false
65
+ qk_norm: true
66
+ scale_factor: *z_scale_factor
67
+ geo_decoder_mlp_expand_ratio: 4
68
+ geo_decoder_downsample_ratio: 1
69
+ geo_decoder_ln_post: true
70
+ enable_flashvdm: true
71
+ jitter_query: false
72
+ voxel_query: true
73
+ voxel_query_res: 128
74
+
75
+ cond_config:
76
+ target: ultrashape.models.conditioner_mask.SingleImageEncoder
77
+ params:
78
+ drop_ratio: 0.1
79
+ # disable_drop: false
80
+ main_image_encoder:
81
+ type: DinoImageEncoder
82
+ kwargs:
83
+ version: 'facebook/dinov2-large'
84
+ image_size: *image_size
85
+ use_cls_token: true
86
+
87
+ dit_cfg:
88
+ target: ultrashape.models.denoisers.dit_mask.RefineDiT
89
+ params:
90
+ input_size: *num_latents
91
+ in_channels: 64
92
+ hidden_size: 2048
93
+ context_dim: 1024
94
+ depth: 21
95
+ num_heads: 16
96
+ qk_norm: true
97
+ text_len: 5330 # 1370
98
+ qk_norm_type: 'rms'
99
+ qkv_bias: false
100
+ num_moe_layers: 6
101
+ num_experts: 8
102
+ moe_top_k: 2
103
+
104
+ scheduler_cfg:
105
+ transport:
106
+ target: ultrashape.models.diffusion.transport.create_transport
107
+ params:
108
+ path_type: Linear
109
+ prediction: velocity
110
+ sampler:
111
+ target: ultrashape.models.diffusion.transport.Sampler
112
+ params: {}
113
+ ode_params:
114
+ sampling_method: euler
115
+ num_steps: &num_steps 50
116
+
117
+ optimizer_cfg:
118
+ optimizer:
119
+ target: torch.optim.AdamW
120
+ params:
121
+ betas: [0.9, 0.99]
122
+ eps: 1.e-6
123
+ weight_decay: 1.e-2
124
+
125
+ scheduler:
126
+ target: ultrashape.utils.trainings.lr_scheduler.LambdaWarmUpCosineFactorScheduler
127
+ params:
128
+ warm_up_steps: 500 # 5000
129
+ f_start: 1.e-6
130
+ f_min: 1.e-3
131
+ f_max: 1.0
132
+
133
+ pipeline_cfg:
134
+ target: ultrashape.pipelines.UltraShapePipeline
135
+
136
+ image_processor_cfg:
137
+ target: ultrashape.preprocessors.ImageProcessorV2
138
+ params: {}
configs/train_vae_refine.yaml ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: "UltraShape Refine VAE"
2
+
3
+ training:
4
+ # ckpt_path:
5
+ steps: 10_0000_0000
6
+ use_amp: true
7
+ amp_type: "bf16"
8
+ base_lr: 1e-5
9
+ gradient_clip_val: 1.0
10
+ gradient_clip_algorithm: "norm"
11
+ every_n_train_steps: 2500
12
+ val_check_interval: 1000
13
+ limit_val_batches: 16
14
+
15
+ dataset:
16
+ target: ultrashape.data.objaverse_vae.ObjaverseDataModule
17
+ params:
18
+ batch_size: 4
19
+ num_workers: 4
20
+ val_num_workers: 4
21
+
22
+ # data
23
+ training_data_list: data/data_list
24
+ sample_pcd_dir: data/sample
25
+
26
+ # input_pcd
27
+ pc_size: &pc_size 163840
28
+ pc_sharpedge_size: &pc_sharpedge_size 0
29
+ sharpedge_label: &sharpedge_label true
30
+ return_normal: true
31
+
32
+ # sup_pcd
33
+ sup_near_uni_size: 100000
34
+ sup_near_sharp_size: 100000
35
+ sup_space_size: 100000
36
+ tsdf_threshold: 0.01
37
+
38
+ model:
39
+ target: ultrashape.models.autoencoders.VAETrainer
40
+ params:
41
+ ckpt_path: ckpt/vae_step=15000.ckpt
42
+ torch_compile: false
43
+ save_dir: outputs/vae_recon
44
+ mc_res: 512
45
+ vae_config:
46
+ target: ultrashape.models.autoencoders.ShapeVAE
47
+ params:
48
+ num_latents: &num_latents 8192 # 4096
49
+ embed_dim: 64
50
+ num_freqs: 8
51
+ include_pi: false
52
+ heads: 16
53
+ width: 1024
54
+ point_feats: 4
55
+ num_encoder_layers: 8
56
+ num_decoder_layers: 16
57
+ pc_size: *pc_size
58
+ pc_sharpedge_size: *pc_sharpedge_size
59
+ downsample_ratio: 20
60
+ qkv_bias: false
61
+ qk_norm: true
62
+ geo_decoder_mlp_expand_ratio: 4
63
+ geo_decoder_downsample_ratio: 1
64
+ geo_decoder_ln_post: true
65
+ enable_flashvdm: true
66
+ jitter_query: true
67
+
68
+ optimizer_cfg:
69
+ optimizer:
70
+ target: torch.optim.AdamW
71
+ params:
72
+ betas: [0.9, 0.99]
73
+ eps: 1.e-6
74
+ weight_decay: 1.e-2
75
+
76
+ scheduler:
77
+ target: ultrashape.utils.trainings.lr_scheduler.LambdaWarmUpCosineFactorScheduler
78
+ params:
79
+ warm_up_steps: 500 # 5000
80
+ f_start: 1.e-6
81
+ f_min: 1.e-3
82
+ f_max: 1.0
83
+
84
+ loss_cfg:
85
+ lambda_logits: 1.
86
+ lambda_kl: 0.001
87
+ # lambda_eik: -1.
88
+ # lambda_sn: -1.
89
+ # lambda_sign: -1.
docs/assets/favicon.png ADDED
docs/assets/images/1.png ADDED

Git LFS Details

  • SHA256: ccfa824dc676397132973ede5799a04205a38c96cf021c73d450ad3b686c42d7
  • Pointer size: 131 Bytes
  • Size of remote file: 978 kB
docs/assets/images/10.png ADDED

Git LFS Details

  • SHA256: 8258e8abd84a97c530a9de4da8de012d6b30bdcf1def0e1b1d4eab8f4edeb9b6
  • Pointer size: 131 Bytes
  • Size of remote file: 1,000 kB
docs/assets/images/11.png ADDED

Git LFS Details

  • SHA256: eb4190984de781f5a9b669a511bb1ed507688ac4798a1d8a13a2ce2e7f1c6c67
  • Pointer size: 132 Bytes
  • Size of remote file: 1.05 MB
docs/assets/images/12.png ADDED

Git LFS Details

  • SHA256: f7c925eefcf85714df9fbf4d602e9ad604d7efc2a87b57d72741049737dc9747
  • Pointer size: 132 Bytes
  • Size of remote file: 2.48 MB
docs/assets/images/13.png ADDED

Git LFS Details

  • SHA256: c22e9e601fead00398634e083e2ed1e90d0c60f459757ae19f5909f3ee5481aa
  • Pointer size: 132 Bytes
  • Size of remote file: 1.03 MB
docs/assets/images/2.png ADDED

Git LFS Details

  • SHA256: ad466d732a170308b06751b59392902c35691bfdadc4de41a25f4cf83a8e3337
  • Pointer size: 131 Bytes
  • Size of remote file: 973 kB
docs/assets/images/3.png ADDED

Git LFS Details

  • SHA256: c8967f41a08a2f3ab4e40d60ecf8dd5a75aece4893e6166d14040605659707a8
  • Pointer size: 132 Bytes
  • Size of remote file: 4.13 MB
docs/assets/images/4.png ADDED

Git LFS Details

  • SHA256: 65d4f78003b1955086eef853a272cfa915cd4aecffd50842c14758458e02f72b
  • Pointer size: 131 Bytes
  • Size of remote file: 770 kB
docs/assets/images/5.png ADDED

Git LFS Details

  • SHA256: e9e4773f2d69a1a14e0d61766b78ed61a613dd622a465642a64c040e9c1e900d
  • Pointer size: 132 Bytes
  • Size of remote file: 1.06 MB
docs/assets/images/6.png ADDED

Git LFS Details

  • SHA256: a9de6b44485a0742681e1449e76d7db59375dde3b306f864c6ff86e98bac1bf4
  • Pointer size: 131 Bytes
  • Size of remote file: 618 kB
docs/assets/images/7.png ADDED

Git LFS Details

  • SHA256: 3c22f3f2f31dc94f8815103bd34faa5c862a6475f61168cf51412726592a79ac
  • Pointer size: 132 Bytes
  • Size of remote file: 1.9 MB
docs/assets/images/8.png ADDED

Git LFS Details

  • SHA256: 97f53090003015263b3b7e1e8cfeadc85382fee72b23024513932a6c1a41fdc6
  • Pointer size: 132 Bytes
  • Size of remote file: 1.14 MB
docs/assets/images/9.png ADDED

Git LFS Details

  • SHA256: 8367f2b5caa28c5cfa650620d598b43cead99e93b2bec0bd2d9f6e719c29acf8
  • Pointer size: 131 Bytes
  • Size of remote file: 452 kB
docs/assets/images/comp-close.png ADDED

Git LFS Details

  • SHA256: 4df711297a0a6179e5dbc0faa7e0929a634cc9d78d4fad0485e477de6870796d
  • Pointer size: 132 Bytes
  • Size of remote file: 7.65 MB
docs/assets/images/comp-open1.png ADDED

Git LFS Details

  • SHA256: f4d427ec26e52bf1c5f2d0bf32a509b8fbcd6e8d5638d9ecf0f79a4748bef272
  • Pointer size: 133 Bytes
  • Size of remote file: 11.1 MB
docs/assets/images/comp-open2.png ADDED

Git LFS Details

  • SHA256: 9f6dc0f1d54edcf543bfb77e6dc510e9d06d99a47b7ef18e7757f63d79258f9a
  • Pointer size: 133 Bytes
  • Size of remote file: 10.5 MB
docs/assets/images/pipeline.png ADDED

Git LFS Details

  • SHA256: d792037eedf9c62684b6098f32564aaca8c4f203c385a7b690106967e35422ef
  • Pointer size: 132 Bytes
  • Size of remote file: 1.36 MB
docs/assets/images/teaser.png ADDED

Git LFS Details

  • SHA256: e534cc21a26a7f276bff72a47cee55ae0e498cdf6dfdd213a62c8fc0a0cf0f7f
  • Pointer size: 133 Bytes
  • Size of remote file: 12.4 MB
docs/assets/videos/1.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cdb3e1c44128f6845b68c80d957d940acd61974007ef97a32090ec6d47b25eee
3
+ size 1866665
docs/assets/videos/10.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:24c03137fbcde1b89954785570a1cfdde8ba98a2ea87895a144e250351aeb0a4
3
+ size 1239537
docs/assets/videos/11.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:992621f458ea10b81185d771406da796694464217ecff38ed0942d2b2bb6be8a
3
+ size 1760679
docs/assets/videos/12.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6a12fd3f3d8b27dba0c3224e9e6eb44505bb321155557eef1721b3061c2454f1
3
+ size 2014267
docs/assets/videos/13.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e6bad4d7050915cd2494b32dce84ce4263556c07fc89fe85dcec12e46fb958d1
3
+ size 1510265
docs/assets/videos/2.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:79affbfd31d1f628b213fde17ecfe325fb50a35637ea45e450fe5488a98197d7
3
+ size 1838655
docs/assets/videos/3.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e454e5e53f58c1143df86e652735f83f7ae5abf6091bee68a754e1ad40d70844
3
+ size 1766589
docs/assets/videos/4.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cb5185a53c54e3a824928dbfe74aad6ac539eb9399e4712c09a487d623a8e52e
3
+ size 2276016
docs/assets/videos/5.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b80db6768af638d3b95b4063323ade4eaf71d527eaca44112c6ce993acfb5775
3
+ size 2169303
docs/assets/videos/6.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b723d0886b0356fe22be77304c083186c8df73c0a9e2bdf1692eb1cbad5c823a
3
+ size 1446235
docs/assets/videos/7.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:92cd398305ebe8f576f0a959c870fd4545e5d237d81883d0a2b4215e6a3289cd
3
+ size 1599638
docs/assets/videos/8.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2248c2b1f97a47e33e49c287780ca57ba61f4804c07be55562da592bb783639a
3
+ size 1749235
docs/assets/videos/9.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7c7be53dec92c8511399d08c2979d30ef59885d0ab3a1d95d682be683c922f00
3
+ size 1119951
docs/carousel.css ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .x-carousel-tags {
2
+ width: 100%;
3
+ display: flex;
4
+ align-items: center;
5
+ justify-content: left;
6
+ flex-wrap: wrap;
7
+ }
8
+
9
+ .x-carousel-tag {
10
+ background-color: rgba(255, 255, 255, 0.9);
11
+ box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 4px;
12
+ border: 2px solid transparent;
13
+ border-radius: 8px;
14
+ cursor: pointer;
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: center;
18
+ justify-content: center;
19
+ transition: all 0.3s ease;
20
+ color: #2a2a2a;
21
+ margin: 4px;
22
+ padding: 8px 16px;
23
+ text-align: center;
24
+ }
25
+
26
+ .x-carousel-tag:hover {
27
+ background-color: rgba(255, 255, 255, 1);
28
+ transform: translateY(-2px);
29
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 4px 6px;
30
+ }
31
+
32
+ .x-carousel-tag.active { border-color: #666; background-color: rgba(255, 255, 255, 1); }
33
+
34
+ .x-carousel-slider {
35
+ width: 100%;
36
+ display: flex;
37
+ align-items: center;
38
+ justify-content: center;
39
+ flex-wrap: wrap;
40
+ }
41
+
42
+ .x-carousel-slider-item {
43
+ max-width: 100%;
44
+ width: 100%;
45
+ flex: 1 0 0;
46
+ }
47
+
48
+ .x-carousel-nav {
49
+ width: 100%;
50
+ height: 40px;
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: space-between;
54
+ }
55
+
56
+ .x-carousel-switch {
57
+ width: 50px;
58
+ height: 25px;
59
+ margin: 8px;
60
+ border-radius: 25px;
61
+ cursor: pointer;
62
+ user-select: none;
63
+ color: rgba(42, 42, 42, 0.4);
64
+ font-size: 24px;
65
+ font-weight: 500;
66
+ transition: all 0.25s ease;
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ }
71
+
72
+ .x-carousel-switch:hover {
73
+ color: rgba(42, 42, 42, 0.8);
74
+ transform: scale(1.05);
75
+ }
76
+
77
+ .x-carousel-pages {
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ flex-wrap: wrap;
82
+ }
83
+
84
+ .x-carousel-page {
85
+ width: 10px;
86
+ height: 10px;
87
+ border-radius: 10px;
88
+ background: rgba(42, 42, 42, 0.2);
89
+ /* background: linear-gradient(107.54deg, #0078d4 .39%, #8661c5 51.23%, #ff9349 100%) fixed; */
90
+ margin: 0 3px;
91
+ cursor: pointer;
92
+ transition: all 0.25s ease;
93
+ }
94
+
95
+ .x-carousel-page:hover {
96
+ background: rgba(42, 42, 42, 0.5);
97
+ }
98
+
99
+ .x-carousel-page.x-carousel-page-active {
100
+ width: 12px;
101
+ height: 13px;
102
+ }
docs/carousel.js ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Carousel functionality for research project page
3
+ * Handles navigation, filtering, and page indicators for carousel components
4
+ */
5
+
6
+ (function() {
7
+ 'use strict';
8
+
9
+ /**
10
+ * Initialize carousel functionality
11
+ * @param {string} carouselId - ID of the carousel container
12
+ */
13
+ function initCarousel(carouselId) {
14
+ const carousel = document.getElementById(carouselId);
15
+ if (!carousel) return;
16
+
17
+ const slider = carousel.querySelector('.x-carousel-slider');
18
+ const allItems = carousel.querySelectorAll('.x-carousel-slider-item');
19
+ const prevBtn = carousel.querySelector('.x-carousel-nav .x-carousel-switch:first-child');
20
+ const nextBtn = carousel.querySelector('.x-carousel-nav .x-carousel-switch:last-child');
21
+ const pages = carousel.querySelectorAll('.x-carousel-page');
22
+ const tags = carousel.querySelectorAll('.x-carousel-tag');
23
+
24
+ if (!slider || !allItems.length) return;
25
+
26
+ let currentIndex = 0;
27
+ let currentFilter = 'all'; // Current filter: 'all', 'class1', 'class2', 'class3'
28
+ let filteredItems = Array.from(allItems); // Currently visible items
29
+
30
+ /**
31
+ * Filter items by tag
32
+ * @param {string} filter - Filter value: 'all', 'class1', 'class2', 'class3'
33
+ */
34
+ function filterItems(filter) {
35
+ currentFilter = filter;
36
+
37
+ // Filter items based on data-tag attribute
38
+ if (filter === 'all') {
39
+ filteredItems = Array.from(allItems);
40
+ } else {
41
+ filteredItems = Array.from(allItems).filter(item => {
42
+ return item.getAttribute('data-tag') === filter;
43
+ });
44
+ }
45
+
46
+ // Reset to first item after filtering
47
+ currentIndex = 0;
48
+
49
+ // Update visibility of all items
50
+ allItems.forEach(item => {
51
+ if (filteredItems.includes(item)) {
52
+ item.style.display = 'block';
53
+ } else {
54
+ item.style.display = 'none';
55
+ }
56
+ });
57
+
58
+ // Show first filtered item
59
+ goToSlide(0);
60
+ updatePages();
61
+ }
62
+
63
+ /**
64
+ * Navigate to a specific slide (within filtered items)
65
+ * @param {number} index - Index of the slide to show
66
+ */
67
+ function goToSlide(index) {
68
+ const totalItems = filteredItems.length;
69
+ currentIndex = Math.max(0, Math.min(index, totalItems - 1));
70
+
71
+ // Hide all filtered items
72
+ filteredItems.forEach(item => {
73
+ item.style.display = 'none';
74
+ });
75
+
76
+ // Show current item - use block instead of flex to preserve card's flex layout
77
+ if (filteredItems[currentIndex]) {
78
+ filteredItems[currentIndex].style.display = 'block';
79
+ }
80
+
81
+ updatePages();
82
+ updateButtons();
83
+ }
84
+
85
+ /**
86
+ * Update page indicators to reflect current slide
87
+ */
88
+ function updatePages() {
89
+ const totalItems = filteredItems.length;
90
+ pages.forEach((page, index) => {
91
+ if (index === currentIndex && index < totalItems) {
92
+ page.classList.add('x-carousel-page-active');
93
+ } else {
94
+ page.classList.remove('x-carousel-page-active');
95
+ }
96
+ });
97
+
98
+ // Hide unused page indicators
99
+ pages.forEach((page, index) => {
100
+ if (index >= totalItems) {
101
+ page.style.display = 'none';
102
+ } else {
103
+ page.style.display = '';
104
+ }
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Update navigation buttons state (enable/disable at boundaries)
110
+ */
111
+ function updateButtons() {
112
+ const totalItems = filteredItems.length;
113
+ if (prevBtn) {
114
+ prevBtn.style.opacity = currentIndex === 0 ? '0.3' : '1';
115
+ prevBtn.style.cursor = currentIndex === 0 ? 'not-allowed' : 'pointer';
116
+ }
117
+ if (nextBtn) {
118
+ nextBtn.style.opacity = currentIndex === totalItems - 1 ? '0.3' : '1';
119
+ nextBtn.style.cursor = currentIndex === totalItems - 1 ? 'not-allowed' : 'pointer';
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Tag filtering (only for results-gen carousel)
125
+ */
126
+ if (tags.length && carouselId === 'results-gen') {
127
+ tags.forEach((tag) => {
128
+ tag.addEventListener('click', function() {
129
+ const filter = tag.getAttribute('data-filter');
130
+ if (!filter) return;
131
+
132
+ // Remove active class from all tags
133
+ tags.forEach(t => t.classList.remove('active'));
134
+ // Add active class to clicked tag
135
+ tag.classList.add('active');
136
+ // Filter items
137
+ filterItems(filter);
138
+ });
139
+ });
140
+ }
141
+
142
+ // Previous/Next buttons
143
+ if (prevBtn) {
144
+ prevBtn.addEventListener('click', function() {
145
+ if (currentIndex > 0) {
146
+ goToSlide(currentIndex - 1);
147
+ }
148
+ });
149
+ }
150
+
151
+ if (nextBtn) {
152
+ nextBtn.addEventListener('click', function() {
153
+ const totalItems = filteredItems.length;
154
+ if (currentIndex < totalItems - 1) {
155
+ goToSlide(currentIndex + 1);
156
+ }
157
+ });
158
+ }
159
+
160
+ // Page indicators - click to jump to specific slide
161
+ pages.forEach((page, index) => {
162
+ page.addEventListener('click', function() {
163
+ goToSlide(index);
164
+ });
165
+ });
166
+
167
+ // Initialize - filter to 'all' and show first item
168
+ filterItems('all');
169
+ }
170
+
171
+ // Initialize all carousels when DOM is ready
172
+ document.addEventListener('DOMContentLoaded', function() {
173
+ initCarousel('results-gen');
174
+ initCarousel('results-recon');
175
+ });
176
+ })();
177
+
178
+
179
+ document.addEventListener('DOMContentLoaded', function() {
180
+
181
+ // 1. 预先创建一个用于显示图片的容器(一开始隐藏)
182
+ const promptImgContainer = document.createElement('div');
183
+ promptImgContainer.id = 'glb-prompt-image-container';
184
+ promptImgContainer.style.cssText = `
185
+ position: fixed;
186
+ bottom: 20px;
187
+ right: 20px;
188
+ width: 200px;
189
+ height: 200px;
190
+ z-index: 10000; /* 保证在最上层 */
191
+ display: none; /* 默认隐藏 */
192
+ background-color: white;
193
+ padding: 5px;
194
+ border-radius: 8px;
195
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
196
+ cursor: pointer; /* 提示可点击关闭 */
197
+ `;
198
+
199
+ // 创建图片元素
200
+ const promptImg = document.createElement('img');
201
+ promptImg.style.cssText = `
202
+ width: 100%;
203
+ height: 100%;
204
+ object-fit: contain;
205
+ display: block;
206
+ `;
207
+ promptImgContainer.appendChild(promptImg);
208
+
209
+ // 添加关闭提示文字(可选)
210
+ const closeTip = document.createElement('div');
211
+ closeTip.innerText = "Click to close";
212
+ closeTip.style.cssText = "position:absolute; top:-25px; right:0; color:white; font-size:12px; background:rgba(0,0,0,0.5); padding:2px 5px; border-radius:4px;";
213
+ promptImgContainer.appendChild(closeTip);
214
+
215
+ document.body.appendChild(promptImgContainer);
216
+
217
+ // 点击图片容器时,自己隐藏
218
+ promptImgContainer.addEventListener('click', function() {
219
+ this.style.display = 'none';
220
+ });
221
+
222
+
223
+ // 2. 为所有的 View GLB 按钮添加点击事件
224
+ const buttons = document.querySelectorAll('.x-button');
225
+
226
+ buttons.forEach(btn => {
227
+ btn.addEventListener('click', function(e) {
228
+ // 获取 HTML 中定义的 data-prompt 属性 (assets/images/1.png)
229
+ const imgUrl = this.getAttribute('data-prompt');
230
+
231
+ if (imgUrl) {
232
+ promptImg.src = imgUrl;
233
+ promptImgContainer.style.display = 'block'; // 显示图片
234
+ }
235
+ });
236
+ });
237
+
238
+ // 3. (可选) 如果你的 GLB 查看器有“关闭”按钮(例如 class 为 .close-viewer),
239
+ // 你需要在这里添加逻辑,让点击关闭查看器时,图片也跟着消失。
240
+ // 假设关闭按钮的类名是 .close-btn (你需要确认实际类名)
241
+ /*
242
+ const closeGlbBtn = document.querySelector('.close-btn-class-name');
243
+ if(closeGlbBtn) {
244
+ closeGlbBtn.addEventListener('click', () => {
245
+ promptImgContainer.style.display = 'none';
246
+ });
247
+ }
248
+ */
249
+ });
docs/index copy.html ADDED
@@ -0,0 +1,788 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html lang="en"><head><style type="text/css">
2
+ .anticon {
3
+ display: inline-block;
4
+ color: inherit;
5
+ font-style: normal;
6
+ line-height: 0;
7
+ text-align: center;
8
+ text-transform: none;
9
+ vertical-align: -0.125em;
10
+ text-rendering: optimizeLegibility;
11
+ -webkit-font-smoothing: antialiased;
12
+ -moz-osx-font-smoothing: grayscale;
13
+ }
14
+
15
+ .anticon > * {
16
+ line-height: 1;
17
+ }
18
+
19
+ .anticon svg {
20
+ display: inline-block;
21
+ }
22
+
23
+ .anticon::before {
24
+ display: none;
25
+ }
26
+
27
+ .anticon .anticon-icon {
28
+ display: block;
29
+ }
30
+
31
+ .anticon[tabindex] {
32
+ cursor: pointer;
33
+ }
34
+
35
+ .anticon-spin::before,
36
+ .anticon-spin {
37
+ display: inline-block;
38
+ -webkit-animation: loadingCircle 1s infinite linear;
39
+ animation: loadingCircle 1s infinite linear;
40
+ }
41
+
42
+ @-webkit-keyframes loadingCircle {
43
+ 100% {
44
+ -webkit-transform: rotate(360deg);
45
+ transform: rotate(360deg);
46
+ }
47
+ }
48
+
49
+ @keyframes loadingCircle {
50
+ 100% {
51
+ -webkit-transform: rotate(360deg);
52
+ transform: rotate(360deg);
53
+ }
54
+ }
55
+ </style>
56
+ <meta charset="UTF-8">
57
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
58
+ <title>UltraShape 1.0: High-Fidelity 3D Shape Generation via Scalable Geometric Refinement</title>
59
+ <!-- TODO: Replace with UltraShape 1.0 favicon -->
60
+ <link rel="icon" href="assets/favicon.png">
61
+ <link rel="stylesheet" href="stylesheet.css">
62
+ <link rel="stylesheet" href="style.css">
63
+ <link rel="stylesheet" href="window.css">
64
+ <link rel="stylesheet" href="carousel.css">
65
+ <link rel="stylesheet" href="main.css">
66
+ <link rel="stylesheet" href="pv.css">
67
+ <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
68
+ <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.3.0/model-viewer.min.js"></script>
69
+ <script src="carousel.js" defer></script>
70
+ </head>
71
+ <body>
72
+ <div class="animated-gradient hero-expandable" style="width: 100%; height: 90vh; position: relative; overflow: hidden;">
73
+
74
+ <img
75
+ src="assets\images\teaser.png"
76
+ alt="Hero background"
77
+ style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.9;"
78
+ >
79
+ <div style="position: relative; z-index: 1; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; color: #ffffff; padding: 80px 20px; text-align: center;">
80
+
81
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;700&family=Inter:wght@300;400&display=swap" rel="stylesheet">
82
+
83
+ <div style="font-family: 'Outfit', sans-serif;">
84
+
85
+ <div style="
86
+ font-size: 100px;
87
+ font-weight: 700;
88
+ margin-bottom: 24px;
89
+ background: linear-gradient(135deg, #ffffff 0%, #c3ecff 100%);
90
+ -webkit-background-clip: text;
91
+ -webkit-text-fill-color: transparent;
92
+ letter-spacing: -2px;
93
+ text-shadow: 0px 10px 30px rgba(0,0,0,0.3);
94
+ ">
95
+ UltraShape 1.0
96
+ </div>
97
+
98
+ <div style="
99
+ font-family: 'Inter', sans-serif;
100
+ font-size: 60px;
101
+ font-weight: 600;
102
+ color: rgba(255, 255, 255, 0.9);
103
+ margin-bottom: 48px;
104
+ max-width: 1200px;
105
+ line-height: 1.6;
106
+ letter-spacing: 1px;
107
+ ">
108
+ High-Fidelity 3D Shape Generation via Scalable Geometric Refinement
109
+ </div>
110
+
111
+ </div>
112
+
113
+ <div style="font-size: 22px; font-weight: 400; margin-bottom: 48px; max-width: 1200px; line-height: 1.8; opacity: 0.95;">
114
+ We introduce UltraShape-1.0, a scalable two-stage diffusion framework for high-quality 3D geometry generation, enhanced by an advanced data processing pipeline that ensures geometric details through watertight processing and quality filtering.
115
+ </div>
116
+ <div id="links" style="display: flex; gap: 16px; flex-wrap: wrap; justify-content: center;">
117
+ <div><a id="paper" href="https://arxiv.org/pdf/2512.21185">Tech Report</a></div>
118
+ <div><a id="code" href="https://github.com/PKU-YuanGroup/UltraShape-1.0">Code</a></div>
119
+ <div><a id="demo" href="#demo">Demo</a></div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+
124
+ <div id="main" style="background: #faf9f7;">
125
+ <div class="x-section-title" style="text-align: center; margin-top: 80px;">Main Pipeline</div>
126
+
127
+ <div style="background: #f8f9fa; padding: 80px 20px;">
128
+ <div style="max-width: 1200px; margin: 40px auto; border-radius: 16px; overflow: hidden; box-shadow: 0 10px 40px rgba(0,0,0,0.15); position: relative; padding: 20px; background-color: #ffffff;">
129
+
130
+ <img src="assets/images/pipeline.png?v=2" alt="Video cover" style="width: 100%; height: auto; display: block;">
131
+
132
+
133
+ </div>
134
+ </div>
135
+
136
+ <div class="x-section-title" style="text-align: center; margin-top: 50px;">Image to 3D Shape Generation</div>
137
+ <div id="results-gen">
138
+ <div class="x-carousel-tags">
139
+ <!-- <div class="x-carousel-tag active" data-filter="all">All</div> -->
140
+ <!-- <div class="x-carousel-tag" data-filter="class1">Object</div> -->
141
+ <!-- <div class="x-carousel-tag" data-filter="class2">Multi-Object</div> -->
142
+ <!-- <div class="x-carousel-tag" data-filter="class3">Character</div> -->
143
+ <!-- <div class="x-carousel-tag" data-filter="class4">Car</div> -->
144
+ </div>
145
+ <div class="x-carousel-slider">
146
+ <!-- Item 1 -->
147
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
148
+ <div class="x-card" style="height: 70vh;">
149
+ <div class="x-button"
150
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
151
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
152
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/1.glb">
153
+ View GLB
154
+ </div>
155
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px; padding: 12px 24px; font-size: 24px;" data-glb="assets/meshs/1.glb" data-prompt="assets/images/1.png">View GLB</div> -->
156
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
157
+ <video
158
+ autoplay
159
+ loop
160
+ muted
161
+ playsinline
162
+ style="width: 100%; height: 100%; object-fit: contain;">
163
+ <source src="assets/videos/1.mp4" type="video/mp4">
164
+ </video>
165
+ </div>
166
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
167
+ <img
168
+ src="assets/images/1.png"
169
+ alt="Input Image"
170
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
171
+ >
172
+ </div>
173
+ </div>
174
+ </div>
175
+ <!-- Item 2 -->
176
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
177
+ <div class="x-card" style="height: 70vh;">
178
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/2.glb" data-prompt="Input Prompt 1">View GLB</div> -->
179
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
180
+ <video
181
+ autoplay
182
+ loop
183
+ muted
184
+ playsinline
185
+ style="width: 100%; height: 100%; object-fit: contain;">
186
+ <source src="assets/videos/2.mp4" type="video/mp4">
187
+ </video>
188
+ </div>
189
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
190
+ <img
191
+ src="assets/images/2.png"
192
+ alt="Input Image"
193
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
194
+ >
195
+ </div>
196
+ </div>
197
+ </div>
198
+ <!-- Item 3 -->
199
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
200
+ <div class="x-card" style="height: 70vh;">
201
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/3.glb" data-prompt="Input Prompt 1">View GLB</div> -->
202
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
203
+ <video
204
+ autoplay
205
+ loop
206
+ muted
207
+ playsinline
208
+ style="width: 100%; height: 100%; object-fit: contain;">
209
+ <source src="assets/videos/3.mp4" type="video/mp4">
210
+ </video>
211
+ </div>
212
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
213
+ <img
214
+ src="assets/images/3.png"
215
+ alt="Input Image"
216
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
217
+ >
218
+ </div>
219
+ </div>
220
+ </div>
221
+ <!-- Item 4 -->
222
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
223
+ <div class="x-card" style="height: 70vh;">
224
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/4.glb" data-prompt="Input Prompt 1">View GLB</div> -->
225
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
226
+ <video
227
+ autoplay
228
+ loop
229
+ muted
230
+ playsinline
231
+ style="width: 100%; height: 100%; object-fit: contain;">
232
+ <source src="assets/videos/4.mp4" type="video/mp4">
233
+ </video>
234
+ </div>
235
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
236
+ <img
237
+ src="assets/images/4.png"
238
+ alt="Input Image"
239
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
240
+ >
241
+ </div>
242
+ </div>
243
+ </div>
244
+ <!-- Item 5 -->
245
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
246
+ <div class="x-card" style="height: 70vh;">
247
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/5.glb" data-prompt="Input Prompt 1">View GLB</div> -->
248
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
249
+ <video
250
+ autoplay
251
+ loop
252
+ muted
253
+ playsinline
254
+ style="width: 100%; height: 100%; object-fit: contain;">
255
+ <source src="assets/videos/5.mp4" type="video/mp4">
256
+ </video>
257
+ </div>
258
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
259
+ <img
260
+ src="assets/images/5.png"
261
+ alt="Input Image"
262
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
263
+ >
264
+ </div>
265
+ </div>
266
+ </div>
267
+ <!-- Item 6 -->
268
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
269
+ <div class="x-card" style="height: 70vh;">
270
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/6.glb" data-prompt="Input Prompt 1">View GLB</div> -->
271
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
272
+ <video
273
+ autoplay
274
+ loop
275
+ muted
276
+ playsinline
277
+ style="width: 100%; height: 100%; object-fit: contain;">
278
+ <source src="assets/videos/6.mp4" type="video/mp4">
279
+ </video>
280
+ </div>
281
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
282
+ <img
283
+ src="assets/images/6.png"
284
+ alt="Input Image"
285
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
286
+ >
287
+ </div>
288
+ </div>
289
+ </div>
290
+ <!-- Item 7 -->
291
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
292
+ <div class="x-card" style="height: 70vh;">
293
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/7.glb" data-prompt="Input Prompt 1">View GLB</div> -->
294
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
295
+ <video
296
+ autoplay
297
+ loop
298
+ muted
299
+ playsinline
300
+ style="width: 100%; height: 100%; object-fit: contain;">
301
+ <source src="assets/videos/7.mp4" type="video/mp4">
302
+ </video>
303
+ </div>
304
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
305
+ <img
306
+ src="assets/images/7.png"
307
+ alt="Input Image"
308
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
309
+ >
310
+ </div>
311
+ </div>
312
+ </div>
313
+ <!-- Item 8 -->
314
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
315
+ <div class="x-card" style="height: 70vh;">
316
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/8.glb" data-prompt="Input Prompt 1">View GLB</div> -->
317
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
318
+ <video
319
+ autoplay
320
+ loop
321
+ muted
322
+ playsinline
323
+ style="width: 100%; height: 100%; object-fit: contain;">
324
+ <source src="assets/videos/8.mp4" type="video/mp4">
325
+ </video>
326
+ </div>
327
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
328
+ <img
329
+ src="assets/images/8.png"
330
+ alt="Input Image"
331
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
332
+ >
333
+ </div>
334
+ </div>
335
+ </div>
336
+ <!-- Item 9 -->
337
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
338
+ <div class="x-card" style="height: 70vh;">
339
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/9.glb" data-prompt="Input Prompt 1">View GLB</div> -->
340
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
341
+ <video
342
+ autoplay
343
+ loop
344
+ muted
345
+ playsinline
346
+ style="width: 100%; height: 100%; object-fit: contain;">
347
+ <source src="assets/videos/9.mp4" type="video/mp4">
348
+ </video>
349
+ </div>
350
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
351
+ <img
352
+ src="assets/images/9.png"
353
+ alt="Input Image"
354
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
355
+ >
356
+ </div>
357
+ </div>
358
+ </div>
359
+ <!-- Item 10 -->
360
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
361
+ <div class="x-card" style="height: 70vh;">
362
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/10.glb" data-prompt="Input Prompt 1">View GLB</div> -->
363
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
364
+ <video
365
+ autoplay
366
+ loop
367
+ muted
368
+ playsinline
369
+ style="width: 100%; height: 100%; object-fit: contain;">
370
+ <source src="assets/videos/10.mp4" type="video/mp4">
371
+ </video>
372
+ </div>
373
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
374
+ <img
375
+ src="assets/images/10.png"
376
+ alt="Input Image"
377
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
378
+ >
379
+ </div>
380
+ </div>
381
+ </div>
382
+ <!-- Item 11 -->
383
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
384
+ <div class="x-card" style="height: 70vh;">
385
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/11.glb" data-prompt="Input Prompt 1">View GLB</div> -->
386
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
387
+ <video
388
+ autoplay
389
+ loop
390
+ muted
391
+ playsinline
392
+ style="width: 100%; height: 100%; object-fit: contain;">
393
+ <source src="assets/videos/11.mp4" type="video/mp4">
394
+ </video>
395
+ </div>
396
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
397
+ <img
398
+ src="assets/images/11.png"
399
+ alt="Input Image"
400
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
401
+ >
402
+ </div>
403
+ </div>
404
+ </div>
405
+ <!-- Item 12 -->
406
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
407
+ <div class="x-card" style="height: 70vh;">
408
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/12.glb" data-prompt="Input Prompt 1">View GLB</div> -->
409
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
410
+ <video
411
+ autoplay
412
+ loop
413
+ muted
414
+ playsinline
415
+ style="width: 100%; height: 100%; object-fit: contain;">
416
+ <source src="assets/videos/12.mp4" type="video/mp4">
417
+ </video>
418
+ </div>
419
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
420
+ <img
421
+ src="assets/images/12.png"
422
+ alt="Input Image"
423
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
424
+ >
425
+ </div>
426
+ </div>
427
+ </div>
428
+ <!-- Item 13 -->
429
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
430
+ <div class="x-card" style="height: 70vh;">
431
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/13.glb" data-prompt="Input Prompt 1">View GLB</div> -->
432
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
433
+ <video
434
+ autoplay
435
+ loop
436
+ muted
437
+ playsinline
438
+ style="width: 100%; height: 100%; object-fit: contain;">
439
+ <source src="assets/videos/13.mp4" type="video/mp4">
440
+ </video>
441
+ </div>
442
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
443
+ <img
444
+ src="assets/images/13.png"
445
+ alt="Input Image"
446
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
447
+ >
448
+ </div>
449
+ </div>
450
+ </div>
451
+ </div>
452
+ <div class="x-carousel-nav">
453
+ <div class="x-carousel-switch">&lt;</div>
454
+ <div class="x-carousel-pages">
455
+ <div class="x-carousel-page x-carousel-page-active"></div>
456
+ <div class="x-carousel-page"></div>
457
+ <div class="x-carousel-page"></div>
458
+ <div class="x-carousel-page"></div>
459
+ <div class="x-carousel-page"></div>
460
+ <div class="x-carousel-page"></div>
461
+ <div class="x-carousel-page"></div>
462
+ <div class="x-carousel-page"></div>
463
+ <div class="x-carousel-page"></div>
464
+ <div class="x-carousel-page"></div>
465
+ <div class="x-carousel-page"></div>
466
+ <div class="x-carousel-page"></div>
467
+ <div class="x-carousel-page"></div>
468
+ </div>
469
+ <div class="x-carousel-switch">&gt;</div>
470
+ </div>
471
+ </div>
472
+
473
+ <div class="x-section-title" style="text-align: center; margin-top: 150px;">Comparison of 3D Shape Generation</div>
474
+
475
+ <div id="results-recon" style="max-width: 1200px; margin: 0 auto; padding: 0 20px;">
476
+ <div class="x-carousel-slider">
477
+
478
+ <!-- 第一张图 -->
479
+ <div class="x-carousel-slider-item" style="flex-basis: calc(100% / 1);">
480
+ <!-- 修改1:增加 display: flex; flex-direction: column; -->
481
+ <div class="x-card" style="height: 86vh; background-color: #faf9f7; border-radius: 4px; display: flex; flex-direction: column; padding: 0 0 40px 0;">
482
+ <div class="x-img-caption" style="padding: 10px; text-align: center; background: #faf9f7; font-size: 24px; color: #333; flex-shrink: 0;">
483
+ Comparison with Open-Source Models 1
484
+ </div>
485
+ <!-- 修改2:将图片高度改为 flex: 1 或 auto,去掉 height: 100% -->
486
+ <div style="flex: 1; min-height: 0; padding: 20px; background-color: #ffffff; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.15); margin: 0 20px 20px 20px; display: flex; align-items: center; justify-content: center;">
487
+ <img
488
+ src="assets/images/comp-open1.png"
489
+ alt="Comparison with Open-Source Models 1"
490
+ style="max-width: 100%; max-height: 100%; width: auto; height: auto; display: block;"
491
+ >
492
+ </div>
493
+ </div>
494
+ </div>
495
+
496
+ <!-- 第二张图 -->
497
+ <div class="x-carousel-slider-item" style="flex-basis: calc(100% / 1);">
498
+ <div class="x-card" style="height: 86vh; background-color: #faf9f7; border-radius: 4px; display: flex; flex-direction: column; padding: 0 0 40px 0;">
499
+ <div class="x-img-caption" style="padding: 10px; text-align: center; background: #faf9f7; font-size: 24px; color: #333; flex-shrink: 0;">
500
+ Comparison with Open-Source Models 2
501
+ </div>
502
+ <div style="flex: 1; min-height: 0; padding: 20px; background-color: #ffffff; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.15); margin: 0 20px 20px 20px; display: flex; align-items: center; justify-content: center;">
503
+ <img
504
+ src="assets/images/comp-open2.png"
505
+ alt="Comparison with Open-Source Models 2"
506
+ style="max-width: 100%; max-height: 100%; width: auto; height: auto; display: block;"
507
+ >
508
+ </div>
509
+ <!-- <div class="x-img-caption" style="padding: 10px; text-align: center; background: #faf9f7; font-size: 16px; color: #333;">
510
+ Comparison with Open-Source Models 2
511
+ </div> -->
512
+ </div>
513
+ </div>
514
+
515
+ <!-- 第三张图 -->
516
+ <div class="x-carousel-slider-item" style="flex-basis: calc(100% / 1);">
517
+ <div class="x-card" style="height: 86vh; background-color: #faf9f7; border-radius: 4px; display: flex; flex-direction: column; padding: 0 0 40px 0;">
518
+ <div class="x-img-caption" style="padding: 10px; text-align: center; background: #faf9f7; font-size: 24px; color: #333; flex-shrink: 0;">
519
+ Comparison with Commercial Models
520
+ </div>
521
+ <div style="flex: 1; min-height: 0; padding: 20px; background-color: #ffffff; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.15); margin: 0 20px 20px 20px; display: flex; align-items: center; justify-content: center;">
522
+ <img
523
+ src="assets/images/comp-close.png"
524
+ alt="Comparison with Close Models 1"
525
+ style="max-width: 100%; max-height: 100%; width: auto; height: auto; display: block;"
526
+ >
527
+ </div>
528
+ <!-- <div class="x-img-caption" style="padding: 15px; text-align: center; background: #faf9f7; font-size: 16px; color: #333;">
529
+ Comparison with Close Models 1
530
+ </div> -->
531
+ </div>
532
+ </div>
533
+
534
+ </div>
535
+
536
+ <div class="x-carousel-nav">
537
+ <div class="x-carousel-switch">&lt;</div>
538
+ <div class="x-carousel-pages">
539
+ <div class="x-carousel-page x-carousel-page-active"></div>
540
+ <div class="x-carousel-page"></div>
541
+ <div class="x-carousel-page"></div>
542
+ </div>
543
+ <div class="x-carousel-switch">&gt;</div>
544
+ </div>
545
+ </div>
546
+
547
+ </div>
548
+ <div class="x-small-header" style="text-align: center; margin-top: -100px;">Citation</div>
549
+ <div class="bibtex-entry" style="max-width: 800px; width: 90%; margin: 20px auto; box-sizing: border-box; background-color: #f5f5f5; padding: 15px; border-radius: 5px; overflow-x: auto;">
550
+ <div class="line">
551
+ <span class="value">@article{jia2025ultrashape,</span>
552
+ </div>
553
+ <div class="line">
554
+ <span class="key"> title={</span>
555
+ <span class="value">UltraShape 1.0: High-Fidelity 3D Shape Generation via Scalable Geometric Refinement},</span>
556
+ </div>
557
+ <div class="line">
558
+ <span class="key"> author={</span>
559
+ <span class="value">Jia, Tanghui and Yan, Dongyu and Hao, Dehao and Li, Yang and Zhang, Kaiyi and He, Xianyi and Li, Lanjiong and Chen, Jinnan and Jiang, Lutao and Yin, Qishen and Quan, Long and Chen, Ying-Cong and Yuan, Li},</span>
560
+ </div>
561
+ <div class="line">
562
+ <span class="key"> journal={</span>
563
+ <span class="value">arxiv preprint arXiv:2512.21185},</span>
564
+ </div>
565
+ <div class="line">
566
+ <span class="key"> year={</span>
567
+ <span class="value">2025}</span>
568
+ </div>
569
+ <div class="line">}</div>
570
+ </div>
571
+ <div style="height: 100px;"> </div>
572
+
573
+ </div>
574
+
575
+ <footer style="text-align: center; padding: 20px 0; color: #666; font-size: 14px;">
576
+ <p>
577
+ The website template was borrowed from <a href="https://microsoft.github.io/TRELLIS.2/" target="_blank">TRELLIS.2</a>.
578
+ </p>
579
+ </footer>
580
+
581
+ <!-- Scroll Down Indicator -->
582
+ <div id="scroll-indicator" class="scroll-indicator">
583
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
584
+ <path d="M7 13l5 5 5-5"></path>
585
+ </svg>
586
+ </div>
587
+
588
+ <div id="fullscreen">
589
+ <div id="window">
590
+ <div id="close">✕</div>
591
+ <div id="content"></div>
592
+ </div>
593
+ </div>
594
+ <script>
595
+ // Mouse-following gradient effect
596
+ document.addEventListener('DOMContentLoaded', function() {
597
+ const gradientEl = document.querySelector('.animated-gradient');
598
+ if (!gradientEl) return;
599
+
600
+ gradientEl.addEventListener('mousemove', function(e) {
601
+ const rect = gradientEl.getBoundingClientRect();
602
+ const x = ((e.clientX - rect.left) / rect.width) * 100;
603
+ const y = ((e.clientY - rect.top) / rect.height) * 100;
604
+
605
+ gradientEl.style.setProperty('--mouse-x', x + '%');
606
+ gradientEl.style.setProperty('--mouse-y', y + '%');
607
+ });
608
+
609
+ // Reset to center when mouse leaves
610
+ gradientEl.addEventListener('mouseleave', function() {
611
+ gradientEl.style.setProperty('--mouse-x', '50%');
612
+ gradientEl.style.setProperty('--mouse-y', '50%');
613
+ });
614
+ });
615
+
616
+ // Scroll expand effect - hero section expands on scroll
617
+ (function() {
618
+ const hero = document.querySelector('.hero-expandable');
619
+ if (!hero) return;
620
+
621
+ const windowHeight = window.innerHeight;
622
+ const expandThreshold = windowHeight * 0.5; // Start expanding after scrolling 50vh
623
+
624
+ function updateHero() {
625
+ const scrollY = window.scrollY;
626
+
627
+ if (scrollY === 0) {
628
+ // Initial state - hero is 90vh (positioned lower, showing content below)
629
+ hero.style.height = '90vh';
630
+ } else if (scrollY <= expandThreshold) {
631
+ // Expanding phase - hero grows from 90vh to 100vh
632
+ const progress = scrollY / expandThreshold;
633
+ const newHeight = 90 + (10 * progress);
634
+ hero.style.height = newHeight + 'vh';
635
+ } else {
636
+ // Fully expanded - hero is 100vh
637
+ hero.style.height = '100vh';
638
+ }
639
+ }
640
+
641
+ // Initial state
642
+ hero.style.height = '90vh';
643
+
644
+ // Throttle scroll for better performance
645
+ let ticking = false;
646
+ window.addEventListener('scroll', function() {
647
+ if (!ticking) {
648
+ window.requestAnimationFrame(function() {
649
+ updateHero();
650
+ ticking = false;
651
+ });
652
+ ticking = true;
653
+ }
654
+ }, { passive: true });
655
+
656
+ // Handle initial load
657
+ updateHero();
658
+ })();
659
+
660
+ // View GLB functionality
661
+ (function() {
662
+ const fullscreen = document.getElementById('fullscreen');
663
+ const windowEl = document.getElementById('window');
664
+ const closeBtn = document.getElementById('close');
665
+ const content = document.getElementById('content');
666
+
667
+ if (!fullscreen || !windowEl || !closeBtn || !content) return;
668
+
669
+ /**
670
+ * Open fullscreen window with GLB model
671
+ * @param {string} glbPath - Path to GLB file
672
+ * @param {string} prompt - Input prompt text
673
+ */
674
+ function openGLBViewer(glbPath, prompt) {
675
+ // Create model-viewer element
676
+ content.innerHTML = `
677
+ <div style="display: flex; flex-wrap: wrap; align-items: start; justify-content: center;">
678
+ <div class="modelviewer-container" style="width: 500px; height: 500px;">
679
+ <model-viewer
680
+ src="${glbPath}"
681
+ alt="3D Model"
682
+ camera-controls
683
+ auto-rotate
684
+ interaction-policy="allow-when-focused"
685
+ reveal="auto"
686
+ style="width: 100%; height: 100%; background-color: #f5f5f5; display: block;"
687
+ loading="eager"
688
+ shadow-intensity="1"
689
+ exposure="1"
690
+ tone-mapping="neutral"
691
+ >
692
+ </model-viewer>
693
+ </div>
694
+ <div class="modelviewer-panel">
695
+ <div class="modelviewer-panel-desc">
696
+ <div>Input Prompt</div>
697
+ </div>
698
+ <div class="modelviewer-panel-prompt">
699
+ ${prompt || '[PLACEHOLDER: Input prompt text]'}
700
+ </div>
701
+ </div>
702
+ </div>
703
+ `;
704
+
705
+ // Show fullscreen window
706
+ fullscreen.style.display = 'flex';
707
+ // Trigger opacity transition after a short delay to ensure model-viewer is ready
708
+ setTimeout(() => {
709
+ fullscreen.style.opacity = '1';
710
+ // Get the model-viewer element and add error handling
711
+ const modelViewer = content.querySelector('model-viewer');
712
+ if (modelViewer) {
713
+ // Add load success handler
714
+ modelViewer.addEventListener('load', () => {
715
+ console.log('Model loaded successfully:', glbPath);
716
+ // Force update after load
717
+ setTimeout(() => {
718
+ modelViewer.style.width = '100%';
719
+ modelViewer.style.height = '100%';
720
+ modelViewer.style.display = 'block';
721
+ }, 100);
722
+ });
723
+
724
+ // Add error handler
725
+ modelViewer.addEventListener('error', (e) => {
726
+ console.error('Model loading error:', e);
727
+ console.error('Failed path:', glbPath);
728
+ const container = content.querySelector('.modelviewer-container');
729
+ if (container) {
730
+ container.innerHTML = `
731
+ <div style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; color: #d32f2f; text-align: center; padding: 20px;">
732
+ <div>
733
+ <div style="font-size: 16px; margin-bottom: 8px; font-weight: 600;">Failed to load model</div>
734
+ <div style="font-size: 12px; color: #666; margin-bottom: 8px;">Path: ${glbPath}</div>
735
+ <div style="font-size: 11px; color: #999;">Please check the file path and ensure the file exists</div>
736
+ </div>
737
+ </div>
738
+ `;
739
+ }
740
+ });
741
+ } else {
742
+ console.error('model-viewer element not found after creation');
743
+ }
744
+ }, 100);
745
+ }
746
+
747
+ /**
748
+ * Close fullscreen window
749
+ */
750
+ function closeGLBViewer() {
751
+ fullscreen.style.opacity = '0';
752
+ setTimeout(() => {
753
+ fullscreen.style.display = 'none';
754
+ content.innerHTML = '';
755
+ }, 250); // Match transition duration
756
+ }
757
+
758
+ // Add click handlers to all View GLB buttons
759
+ document.addEventListener('click', function(e) {
760
+ const button = e.target.closest('.x-button');
761
+ if (button && button.textContent.trim() === 'View GLB') {
762
+ const glbPath = button.getAttribute('data-glb');
763
+ const prompt = button.getAttribute('data-prompt');
764
+ if (glbPath) {
765
+ openGLBViewer(glbPath, prompt);
766
+ }
767
+ }
768
+ });
769
+
770
+ // Close button handler
771
+ closeBtn.addEventListener('click', closeGLBViewer);
772
+
773
+ // Close on backdrop click
774
+ fullscreen.addEventListener('click', function(e) {
775
+ if (e.target === fullscreen) {
776
+ closeGLBViewer();
777
+ }
778
+ });
779
+
780
+ // Close on Escape key
781
+ document.addEventListener('keydown', function(e) {
782
+ if (e.key === 'Escape' && fullscreen.style.display === 'flex') {
783
+ closeGLBViewer();
784
+ }
785
+ });
786
+ })();
787
+ </script>
788
+ </body></html>
docs/index.html ADDED
@@ -0,0 +1,884 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html lang="en"><head><style type="text/css">
2
+ .anticon {
3
+ display: inline-block;
4
+ color: inherit;
5
+ font-style: normal;
6
+ line-height: 0;
7
+ text-align: center;
8
+ text-transform: none;
9
+ vertical-align: -0.125em;
10
+ text-rendering: optimizeLegibility;
11
+ -webkit-font-smoothing: antialiased;
12
+ -moz-osx-font-smoothing: grayscale;
13
+ }
14
+
15
+ .anticon > * {
16
+ line-height: 1;
17
+ }
18
+
19
+ .anticon svg {
20
+ display: inline-block;
21
+ }
22
+
23
+ .anticon::before {
24
+ display: none;
25
+ }
26
+
27
+ .anticon .anticon-icon {
28
+ display: block;
29
+ }
30
+
31
+ .anticon[tabindex] {
32
+ cursor: pointer;
33
+ }
34
+
35
+ .anticon-spin::before,
36
+ .anticon-spin {
37
+ display: inline-block;
38
+ -webkit-animation: loadingCircle 1s infinite linear;
39
+ animation: loadingCircle 1s infinite linear;
40
+ }
41
+
42
+ @-webkit-keyframes loadingCircle {
43
+ 100% {
44
+ -webkit-transform: rotate(360deg);
45
+ transform: rotate(360deg);
46
+ }
47
+ }
48
+
49
+ @keyframes loadingCircle {
50
+ 100% {
51
+ -webkit-transform: rotate(360deg);
52
+ transform: rotate(360deg);
53
+ }
54
+ }
55
+ </style>
56
+ <meta charset="UTF-8">
57
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
58
+ <title>UltraShape 1.0: High-Fidelity 3D Shape Generation via Scalable Geometric Refinement</title>
59
+ <!-- TODO: Replace with UltraShape 1.0 favicon -->
60
+ <link rel="icon" href="assets/favicon.png">
61
+ <link rel="stylesheet" href="stylesheet.css">
62
+ <link rel="stylesheet" href="style.css">
63
+ <link rel="stylesheet" href="window.css">
64
+ <link rel="stylesheet" href="carousel.css">
65
+ <link rel="stylesheet" href="main.css">
66
+ <link rel="stylesheet" href="pv.css">
67
+ <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
68
+ <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.3.0/model-viewer.min.js"></script>
69
+ <script src="carousel.js" defer></script>
70
+ </head>
71
+ <body>
72
+ <div class="animated-gradient hero-expandable" style="width: 100%; height: 90vh; position: relative; overflow: hidden;">
73
+
74
+ <img
75
+ src="assets\images\teaser.png"
76
+ alt="Hero background"
77
+ style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.9;"
78
+ >
79
+ <div style="position: relative; z-index: 1; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; color: #ffffff; padding: 80px 20px; text-align: center;">
80
+
81
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;700&family=Inter:wght@300;400&display=swap" rel="stylesheet">
82
+
83
+ <div style="font-family: 'Outfit', sans-serif;">
84
+
85
+ <div style="
86
+ font-size: 100px;
87
+ font-weight: 700;
88
+ margin-bottom: 24px;
89
+ background: linear-gradient(135deg, #ffffff 0%, #c3ecff 100%);
90
+ -webkit-background-clip: text;
91
+ -webkit-text-fill-color: transparent;
92
+ letter-spacing: -2px;
93
+ text-shadow: 0px 10px 30px rgba(0,0,0,0.3);
94
+ ">
95
+ UltraShape 1.0
96
+ </div>
97
+
98
+ <div style="
99
+ font-family: 'Inter', sans-serif;
100
+ font-size: 60px;
101
+ font-weight: 600;
102
+ color: rgba(255, 255, 255, 0.9);
103
+ margin-bottom: 48px;
104
+ max-width: 1200px;
105
+ line-height: 1.6;
106
+ letter-spacing: 1px;
107
+ ">
108
+ High-Fidelity 3D Shape Generation via Scalable Geometric Refinement
109
+ </div>
110
+
111
+ </div>
112
+
113
+ <div style="font-size: 22px; font-weight: 400; margin-bottom: 48px; max-width: 1200px; line-height: 1.8; opacity: 0.95;">
114
+ We introduce UltraShape-1.0, a scalable two-stage diffusion framework for high-quality 3D geometry generation, enhanced by an advanced data processing pipeline that ensures geometric details through watertight processing and quality filtering.
115
+ </div>
116
+ <div id="links" style="display: flex; gap: 16px; flex-wrap: wrap; justify-content: center;">
117
+ <div><a id="paper" href="https://arxiv.org/pdf/2512.21185">Tech Report</a></div>
118
+ <div><a id="code" href="https://github.com/PKU-YuanGroup/UltraShape-1.0">Code</a></div>
119
+ <div><a id="demo" href="#demo">Demo</a></div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+
124
+ <div id="main" style="background: #faf9f7;">
125
+ <div class="x-section-title" style="text-align: center; margin-top: 80px;">Main Pipeline</div>
126
+
127
+ <div style="background: #f8f9fa; padding: 80px 20px;">
128
+ <div style="max-width: 1200px; margin: 40px auto; border-radius: 16px; overflow: hidden; box-shadow: 0 10px 40px rgba(0,0,0,0.15); position: relative; padding: 20px; background-color: #ffffff;">
129
+
130
+ <img src="assets/images/pipeline.png?v=2" alt="Video cover" style="width: 100%; height: auto; display: block;">
131
+
132
+
133
+ </div>
134
+ </div>
135
+
136
+ <div class="x-section-title" style="text-align: center; margin-top: 50px;">Image to 3D Shape Generation</div>
137
+ <div id="results-gen">
138
+ <div class="x-carousel-tags">
139
+ <!-- <div class="x-carousel-tag active" data-filter="all">All</div> -->
140
+ <!-- <div class="x-carousel-tag" data-filter="class1">Object</div> -->
141
+ <!-- <div class="x-carousel-tag" data-filter="class2">Multi-Object</div> -->
142
+ <!-- <div class="x-carousel-tag" data-filter="class3">Character</div> -->
143
+ <!-- <div class="x-carousel-tag" data-filter="class4">Car</div> -->
144
+ </div>
145
+ <div class="x-carousel-slider">
146
+ <!-- Item 1 -->
147
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
148
+ <div class="x-card" style="height: 70vh;">
149
+ <div class="x-button"
150
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
151
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
152
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/1.glb"
153
+ data-prompt="assets/images/1.png">
154
+ View GLB
155
+ </div>
156
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px; padding: 12px 24px; font-size: 24px;" data-glb="assets/meshs/1.glb" data-prompt="assets/images/1.png">View GLB</div> -->
157
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
158
+ <video
159
+ autoplay
160
+ loop
161
+ muted
162
+ playsinline
163
+ style="width: 100%; height: 100%; object-fit: contain;">
164
+ <source src="assets/videos/1.mp4" type="video/mp4">
165
+ </video>
166
+ </div>
167
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
168
+ <img
169
+ src="assets/images/1.png"
170
+ alt="Input Image"
171
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
172
+ >
173
+ </div>
174
+ </div>
175
+ </div>
176
+ <!-- Item 2 -->
177
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
178
+ <div class="x-card" style="height: 70vh;">
179
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/2.glb" data-prompt="Input Prompt 1">View GLB</div> -->
180
+ <div class="x-button"
181
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
182
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
183
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/2.glb"
184
+ data-prompt="assets/images/2.png">
185
+ View GLB
186
+ </div>
187
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
188
+ <video
189
+ autoplay
190
+ loop
191
+ muted
192
+ playsinline
193
+ style="width: 100%; height: 100%; object-fit: contain;">
194
+ <source src="assets/videos/2.mp4" type="video/mp4">
195
+ </video>
196
+ </div>
197
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
198
+ <img
199
+ src="assets/images/2.png"
200
+ alt="Input Image"
201
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
202
+ >
203
+ </div>
204
+ </div>
205
+ </div>
206
+ <!-- Item 3 -->
207
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
208
+ <div class="x-card" style="height: 70vh;">
209
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/3.glb" data-prompt="Input Prompt 1">View GLB</div> -->
210
+ <div class="x-button"
211
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
212
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
213
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/3.glb"
214
+ data-prompt="assets/images/3.png">
215
+ View GLB
216
+ </div>
217
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
218
+ <video
219
+ autoplay
220
+ loop
221
+ muted
222
+ playsinline
223
+ style="width: 100%; height: 100%; object-fit: contain;">
224
+ <source src="assets/videos/3.mp4" type="video/mp4">
225
+ </video>
226
+ </div>
227
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
228
+ <img
229
+ src="assets/images/3.png"
230
+ alt="Input Image"
231
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
232
+ >
233
+ </div>
234
+ </div>
235
+ </div>
236
+ <!-- Item 4 -->
237
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
238
+ <div class="x-card" style="height: 70vh;">
239
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/4.glb" data-prompt="Input Prompt 1">View GLB</div> -->
240
+ <div class="x-button"
241
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
242
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
243
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/4.glb"
244
+ data-prompt="assets/images/4.png">
245
+ View GLB
246
+ </div>
247
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
248
+ <video
249
+ autoplay
250
+ loop
251
+ muted
252
+ playsinline
253
+ style="width: 100%; height: 100%; object-fit: contain;">
254
+ <source src="assets/videos/4.mp4" type="video/mp4">
255
+ </video>
256
+ </div>
257
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
258
+ <img
259
+ src="assets/images/4.png"
260
+ alt="Input Image"
261
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
262
+ >
263
+ </div>
264
+ </div>
265
+ </div>
266
+ <!-- Item 5 -->
267
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
268
+ <div class="x-card" style="height: 70vh;">
269
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/5.glb" data-prompt="Input Prompt 1">View GLB</div> -->
270
+ <div class="x-button"
271
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
272
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
273
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/5.glb"
274
+ data-prompt="assets/images/5.png">
275
+ View GLB
276
+ </div>
277
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
278
+ <video
279
+ autoplay
280
+ loop
281
+ muted
282
+ playsinline
283
+ style="width: 100%; height: 100%; object-fit: contain;">
284
+ <source src="assets/videos/5.mp4" type="video/mp4">
285
+ </video>
286
+ </div>
287
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
288
+ <img
289
+ src="assets/images/5.png"
290
+ alt="Input Image"
291
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
292
+ >
293
+ </div>
294
+ </div>
295
+ </div>
296
+ <!-- Item 6 -->
297
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
298
+ <div class="x-card" style="height: 70vh;">
299
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/6.glb" data-prompt="Input Prompt 1">View GLB</div> -->
300
+ <div class="x-button"
301
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
302
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
303
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/6.glb"
304
+ data-prompt="assets/images/6.png">
305
+ View GLB
306
+ </div>
307
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
308
+ <video
309
+ autoplay
310
+ loop
311
+ muted
312
+ playsinline
313
+ style="width: 100%; height: 100%; object-fit: contain;">
314
+ <source src="assets/videos/6.mp4" type="video/mp4">
315
+ </video>
316
+ </div>
317
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
318
+ <img
319
+ src="assets/images/6.png"
320
+ alt="Input Image"
321
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
322
+ >
323
+ </div>
324
+ </div>
325
+ </div>
326
+ <!-- Item 7 -->
327
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
328
+ <div class="x-card" style="height: 70vh;">
329
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/7.glb" data-prompt="Input Prompt 1">View GLB</div> -->
330
+ <div class="x-button"
331
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
332
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
333
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/7.glb"
334
+ data-prompt="assets/images/7.png">
335
+ View GLB
336
+ </div>
337
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
338
+ <video
339
+ autoplay
340
+ loop
341
+ muted
342
+ playsinline
343
+ style="width: 100%; height: 100%; object-fit: contain;">
344
+ <source src="assets/videos/7.mp4" type="video/mp4">
345
+ </video>
346
+ </div>
347
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
348
+ <img
349
+ src="assets/images/7.png"
350
+ alt="Input Image"
351
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
352
+ >
353
+ </div>
354
+ </div>
355
+ </div>
356
+ <!-- Item 8 -->
357
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
358
+ <div class="x-card" style="height: 70vh;">
359
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/8.glb" data-prompt="Input Prompt 1">View GLB</div> -->
360
+ <div class="x-button"
361
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
362
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
363
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/8.glb"
364
+ data-prompt="assets/images/8.png">
365
+ View GLB
366
+ </div>
367
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
368
+ <video
369
+ autoplay
370
+ loop
371
+ muted
372
+ playsinline
373
+ style="width: 100%; height: 100%; object-fit: contain;">
374
+ <source src="assets/videos/8.mp4" type="video/mp4">
375
+ </video>
376
+ </div>
377
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
378
+ <img
379
+ src="assets/images/8.png"
380
+ alt="Input Image"
381
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
382
+ >
383
+ </div>
384
+ </div>
385
+ </div>
386
+ <!-- Item 9 -->
387
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
388
+ <div class="x-card" style="height: 70vh;">
389
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/9.glb" data-prompt="Input Prompt 1">View GLB</div> -->
390
+ <div class="x-button"
391
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
392
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
393
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/9.glb"
394
+ data-prompt="assets/images/9.png">
395
+ View GLB
396
+ </div>
397
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
398
+ <video
399
+ autoplay
400
+ loop
401
+ muted
402
+ playsinline
403
+ style="width: 100%; height: 100%; object-fit: contain;">
404
+ <source src="assets/videos/9.mp4" type="video/mp4">
405
+ </video>
406
+ </div>
407
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
408
+ <img
409
+ src="assets/images/9.png"
410
+ alt="Input Image"
411
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
412
+ >
413
+ </div>
414
+ </div>
415
+ </div>
416
+ <!-- Item 10 -->
417
+ <div class="x-carousel-slider-item" data-tag="class3" style="flex-basis: calc(100% / 1);">
418
+ <div class="x-card" style="height: 70vh;">
419
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/10.glb" data-prompt="Input Prompt 1">View GLB</div> -->
420
+ <div class="x-button"
421
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
422
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
423
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/10.glb"
424
+ data-prompt="assets/images/10.png">
425
+ View GLB
426
+ </div>
427
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
428
+ <video
429
+ autoplay
430
+ loop
431
+ muted
432
+ playsinline
433
+ style="width: 100%; height: 100%; object-fit: contain;">
434
+ <source src="assets/videos/10.mp4" type="video/mp4">
435
+ </video>
436
+ </div>
437
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
438
+ <img
439
+ src="assets/images/10.png"
440
+ alt="Input Image"
441
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
442
+ >
443
+ </div>
444
+ </div>
445
+ </div>
446
+ <!-- Item 11 -->
447
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
448
+ <div class="x-card" style="height: 70vh;">
449
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/11.glb" data-prompt="Input Prompt 1">View GLB</div> -->
450
+ <div class="x-button"
451
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
452
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
453
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/11.glb"
454
+ data-prompt="assets/images/11.png">
455
+ View GLB
456
+ </div>
457
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
458
+ <video
459
+ autoplay
460
+ loop
461
+ muted
462
+ playsinline
463
+ style="width: 100%; height: 100%; object-fit: contain;">
464
+ <source src="assets/videos/11.mp4" type="video/mp4">
465
+ </video>
466
+ </div>
467
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
468
+ <img
469
+ src="assets/images/11.png"
470
+ alt="Input Image"
471
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
472
+ >
473
+ </div>
474
+ </div>
475
+ </div>
476
+ <!-- Item 12 -->
477
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
478
+ <div class="x-card" style="height: 70vh;">
479
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/12.glb" data-prompt="Input Prompt 1">View GLB</div> -->
480
+ <div class="x-button"
481
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
482
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
483
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/12.glb"
484
+ data-prompt="assets/images/12.png">
485
+ View GLB
486
+ </div>
487
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
488
+ <video
489
+ autoplay
490
+ loop
491
+ muted
492
+ playsinline
493
+ style="width: 100%; height: 100%; object-fit: contain;">
494
+ <source src="assets/videos/12.mp4" type="video/mp4">
495
+ </video>
496
+ </div>
497
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
498
+ <img
499
+ src="assets/images/12.png"
500
+ alt="Input Image"
501
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
502
+ >
503
+ </div>
504
+ </div>
505
+ </div>
506
+ <!-- Item 13 -->
507
+ <div class="x-carousel-slider-item" data-tag="class1" style="flex-basis: calc(100% / 1);">
508
+ <div class="x-card" style="height: 70vh;">
509
+ <!-- <div class="x-button" style="position: absolute; top: 10px; right: 10px" data-glb="assets/meshs/13.glb" data-prompt="Input Prompt 1">View GLB</div> -->
510
+ <div class="x-button"
511
+ style="position: absolute; top: 10px; right: 85px; padding: 15px 30px;
512
+ font-size: 22px; font-weight: bold; z-index: 10; cursor: pointer;"
513
+ data-glb="https://huggingface.co/datasets/infinith/ultrashape_page/resolve/main/13.glb"
514
+ data-prompt="assets/images/13.png">
515
+ View GLB
516
+ </div>
517
+ <div style="width: 85%; aspect-ratio: 1; background-color: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden;">
518
+ <video
519
+ autoplay
520
+ loop
521
+ muted
522
+ playsinline
523
+ style="width: 100%; height: 100%; object-fit: contain;">
524
+ <source src="assets/videos/13.mp4" type="video/mp4">
525
+ </video>
526
+ </div>
527
+ <div style="width: 200px; aspect-ratio: 1; background-color: transparent; border-radius: 4px; overflow: hidden;">
528
+ <img
529
+ src="assets/images/13.png"
530
+ alt="Input Image"
531
+ style="width: 100%; height: 100%; object-fit: contain; display: block;"
532
+ >
533
+ </div>
534
+ </div>
535
+ </div>
536
+ </div>
537
+ <div class="x-carousel-nav">
538
+ <div class="x-carousel-switch">&lt;</div>
539
+ <div class="x-carousel-pages">
540
+ <div class="x-carousel-page x-carousel-page-active"></div>
541
+ <div class="x-carousel-page"></div>
542
+ <div class="x-carousel-page"></div>
543
+ <div class="x-carousel-page"></div>
544
+ <div class="x-carousel-page"></div>
545
+ <div class="x-carousel-page"></div>
546
+ <div class="x-carousel-page"></div>
547
+ <div class="x-carousel-page"></div>
548
+ <div class="x-carousel-page"></div>
549
+ <div class="x-carousel-page"></div>
550
+ <div class="x-carousel-page"></div>
551
+ <div class="x-carousel-page"></div>
552
+ <div class="x-carousel-page"></div>
553
+ </div>
554
+ <div class="x-carousel-switch">&gt;</div>
555
+ </div>
556
+ </div>
557
+
558
+ <div class="x-section-title" style="text-align: center; margin-top: 150px;">Comparison of 3D Shape Generation</div>
559
+
560
+ <div id="results-recon" style="max-width: 1200px; margin: 0 auto; padding: 0 20px;">
561
+ <div class="x-carousel-slider">
562
+
563
+ <!-- 第一张图 -->
564
+ <div class="x-carousel-slider-item" style="flex-basis: calc(100% / 1);">
565
+ <!-- 修改1:增加 display: flex; flex-direction: column; -->
566
+ <div class="x-card" style="height: 86vh; background-color: #faf9f7; border-radius: 4px; display: flex; flex-direction: column; padding: 0 0 40px 0;">
567
+ <div class="x-img-caption" style="padding: 10px; text-align: center; background: #faf9f7; font-size: 24px; color: #333; flex-shrink: 0;">
568
+ Comparison with Open-Source Models 1
569
+ </div>
570
+ <!-- 修改2:将图片高度改为 flex: 1 或 auto,去掉 height: 100% -->
571
+ <div style="flex: 1; min-height: 0; padding: 20px; background-color: #ffffff; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.15); margin: 0 20px 20px 20px; display: flex; align-items: center; justify-content: center;">
572
+ <img
573
+ src="assets/images/comp-open1.png"
574
+ alt="Comparison with Open-Source Models 1"
575
+ style="max-width: 100%; max-height: 100%; width: auto; height: auto; display: block;"
576
+ >
577
+ </div>
578
+ </div>
579
+ </div>
580
+
581
+ <!-- 第二张图 -->
582
+ <div class="x-carousel-slider-item" style="flex-basis: calc(100% / 1);">
583
+ <div class="x-card" style="height: 86vh; background-color: #faf9f7; border-radius: 4px; display: flex; flex-direction: column; padding: 0 0 40px 0;">
584
+ <div class="x-img-caption" style="padding: 10px; text-align: center; background: #faf9f7; font-size: 24px; color: #333; flex-shrink: 0;">
585
+ Comparison with Open-Source Models 2
586
+ </div>
587
+ <div style="flex: 1; min-height: 0; padding: 20px; background-color: #ffffff; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.15); margin: 0 20px 20px 20px; display: flex; align-items: center; justify-content: center;">
588
+ <img
589
+ src="assets/images/comp-open2.png"
590
+ alt="Comparison with Open-Source Models 2"
591
+ style="max-width: 100%; max-height: 100%; width: auto; height: auto; display: block;"
592
+ >
593
+ </div>
594
+ <!-- <div class="x-img-caption" style="padding: 10px; text-align: center; background: #faf9f7; font-size: 16px; color: #333;">
595
+ Comparison with Open-Source Models 2
596
+ </div> -->
597
+ </div>
598
+ </div>
599
+
600
+ <!-- 第三张图 -->
601
+ <div class="x-carousel-slider-item" style="flex-basis: calc(100% / 1);">
602
+ <div class="x-card" style="height: 86vh; background-color: #faf9f7; border-radius: 4px; display: flex; flex-direction: column; padding: 0 0 40px 0;">
603
+ <div class="x-img-caption" style="padding: 10px; text-align: center; background: #faf9f7; font-size: 24px; color: #333; flex-shrink: 0;">
604
+ Comparison with Commercial Models
605
+ </div>
606
+ <div style="flex: 1; min-height: 0; padding: 20px; background-color: #ffffff; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.15); margin: 0 20px 20px 20px; display: flex; align-items: center; justify-content: center;">
607
+ <img
608
+ src="assets/images/comp-close.png"
609
+ alt="Comparison with Close Models 1"
610
+ style="max-width: 100%; max-height: 100%; width: auto; height: auto; display: block;"
611
+ >
612
+ </div>
613
+ <!-- <div class="x-img-caption" style="padding: 15px; text-align: center; background: #faf9f7; font-size: 16px; color: #333;">
614
+ Comparison with Close Models 1
615
+ </div> -->
616
+ </div>
617
+ </div>
618
+
619
+ </div>
620
+
621
+ <div class="x-carousel-nav">
622
+ <div class="x-carousel-switch">&lt;</div>
623
+ <div class="x-carousel-pages">
624
+ <div class="x-carousel-page x-carousel-page-active"></div>
625
+ <div class="x-carousel-page"></div>
626
+ <div class="x-carousel-page"></div>
627
+ </div>
628
+ <div class="x-carousel-switch">&gt;</div>
629
+ </div>
630
+ </div>
631
+
632
+ </div>
633
+ <div class="x-small-header" style="text-align: center; margin-top: -100px;">Citation</div>
634
+ <div class="bibtex-entry" style="max-width: 800px; width: 90%; margin: 20px auto; box-sizing: border-box; background-color: #f5f5f5; padding: 15px; border-radius: 5px; overflow-x: auto;">
635
+ <div class="line">
636
+ <span class="value">@article{jia2025ultrashape,</span>
637
+ </div>
638
+ <div class="line">
639
+ <span class="key"> title={</span>
640
+ <span class="value">UltraShape 1.0: High-Fidelity 3D Shape Generation via Scalable Geometric Refinement},</span>
641
+ </div>
642
+ <div class="line">
643
+ <span class="key"> author={</span>
644
+ <span class="value">Jia, Tanghui and Yan, Dongyu and Hao, Dehao and Li, Yang and Zhang, Kaiyi and He, Xianyi and Li, Lanjiong and Chen, Jinnan and Jiang, Lutao and Yin, Qishen and Quan, Long and Chen, Ying-Cong and Yuan, Li},</span>
645
+ </div>
646
+ <div class="line">
647
+ <span class="key"> journal={</span>
648
+ <span class="value">arxiv preprint arXiv:2512.21185},</span>
649
+ </div>
650
+ <div class="line">
651
+ <span class="key"> year={</span>
652
+ <span class="value">2025}</span>
653
+ </div>
654
+ <div class="line">}</div>
655
+ </div>
656
+ <div style="height: 100px;"> </div>
657
+
658
+ </div>
659
+
660
+ <footer style="text-align: center; padding: 20px 0; color: #666; font-size: 14px;">
661
+ <p>
662
+ The website template was borrowed from <a href="https://microsoft.github.io/TRELLIS.2/" target="_blank">TRELLIS.2</a>.
663
+ </p>
664
+ </footer>
665
+
666
+ <!-- Scroll Down Indicator -->
667
+ <div id="scroll-indicator" class="scroll-indicator">
668
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
669
+ <path d="M7 13l5 5 5-5"></path>
670
+ </svg>
671
+ </div>
672
+
673
+ <div id="fullscreen">
674
+ <div id="window">
675
+ <div id="close">✕</div>
676
+ <div id="content"></div>
677
+ </div>
678
+ </div>
679
+ <script>
680
+ // Mouse-following gradient effect
681
+ document.addEventListener('DOMContentLoaded', function() {
682
+ const gradientEl = document.querySelector('.animated-gradient');
683
+ if (!gradientEl) return;
684
+
685
+ gradientEl.addEventListener('mousemove', function(e) {
686
+ const rect = gradientEl.getBoundingClientRect();
687
+ const x = ((e.clientX - rect.left) / rect.width) * 100;
688
+ const y = ((e.clientY - rect.top) / rect.height) * 100;
689
+
690
+ gradientEl.style.setProperty('--mouse-x', x + '%');
691
+ gradientEl.style.setProperty('--mouse-y', y + '%');
692
+ });
693
+
694
+ // Reset to center when mouse leaves
695
+ gradientEl.addEventListener('mouseleave', function() {
696
+ gradientEl.style.setProperty('--mouse-x', '50%');
697
+ gradientEl.style.setProperty('--mouse-y', '50%');
698
+ });
699
+ });
700
+
701
+ // Scroll expand effect - hero section expands on scroll
702
+ (function() {
703
+ const hero = document.querySelector('.hero-expandable');
704
+ if (!hero) return;
705
+
706
+ const windowHeight = window.innerHeight;
707
+ const expandThreshold = windowHeight * 0.5; // Start expanding after scrolling 50vh
708
+
709
+ function updateHero() {
710
+ const scrollY = window.scrollY;
711
+
712
+ if (scrollY === 0) {
713
+ // Initial state - hero is 90vh (positioned lower, showing content below)
714
+ hero.style.height = '90vh';
715
+ } else if (scrollY <= expandThreshold) {
716
+ // Expanding phase - hero grows from 90vh to 100vh
717
+ const progress = scrollY / expandThreshold;
718
+ const newHeight = 90 + (10 * progress);
719
+ hero.style.height = newHeight + 'vh';
720
+ } else {
721
+ // Fully expanded - hero is 100vh
722
+ hero.style.height = '100vh';
723
+ }
724
+ }
725
+
726
+ // Initial state
727
+ hero.style.height = '90vh';
728
+
729
+ // Throttle scroll for better performance
730
+ let ticking = false;
731
+ window.addEventListener('scroll', function() {
732
+ if (!ticking) {
733
+ window.requestAnimationFrame(function() {
734
+ updateHero();
735
+ ticking = false;
736
+ });
737
+ ticking = true;
738
+ }
739
+ }, { passive: true });
740
+
741
+ // Handle initial load
742
+ updateHero();
743
+ })();
744
+
745
+ // View GLB functionality
746
+ (function() {
747
+ const fullscreen = document.getElementById('fullscreen');
748
+ const windowEl = document.getElementById('window');
749
+ const closeBtn = document.getElementById('close');
750
+ const content = document.getElementById('content');
751
+
752
+ if (!fullscreen || !windowEl || !closeBtn || !content) return;
753
+
754
+ /**
755
+ * Open fullscreen window with GLB model
756
+ * @param {string} glbPath - Path to GLB file
757
+ * @param {string} image - Input prompt image
758
+ */
759
+ function openGLBViewer(glbPath, imagePath) {
760
+ const overlay = document.createElement('div');
761
+ overlay.style.cssText = `
762
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
763
+ background: rgba(0, 0, 0, 0.85); z-index: 9999;
764
+ display: flex; align-items: center; justify-content: center;
765
+ backdrop-filter: blur(5px);
766
+ `;
767
+
768
+ const container = document.createElement('div');
769
+ container.style.cssText = `
770
+ width: 80%; height: 80%; background: #fff; border-radius: 12px;
771
+ display: flex; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.5);
772
+ position: relative;
773
+ `;
774
+
775
+ const imgContainer = document.createElement('div');
776
+ imgContainer.style.cssText = `
777
+ flex: 1; background: #f0f0f0; display: flex;
778
+ align-items: center; justify-content: center; border-right: 1px solid #ddd;
779
+ flex-direction: column;
780
+ `;
781
+ const imgTitle = document.createElement('div');
782
+ imgTitle.innerText = "Input Image";
783
+ imgTitle.style.cssText = "margin-bottom: 10px; font-weight: bold; color: #555;";
784
+ const img = document.createElement('img');
785
+ img.src = imagePath;
786
+ img.style.cssText = "max-width: 90%; max-height: 80%; object-fit: contain; border-radius: 4px;";
787
+ imgContainer.appendChild(imgTitle);
788
+ imgContainer.appendChild(img);
789
+
790
+ const modelContainer = document.createElement('div');
791
+ modelContainer.style.cssText = `
792
+ flex: 1; background: #e0e0e0; display: flex;
793
+ align-items: center; justify-content: center; flex-direction: column; position: relative;
794
+ `;
795
+ const modelTitle = document.createElement('div');
796
+ modelTitle.innerText = "Generated Mesh";
797
+ modelTitle.style.cssText = "position: absolute; top: 20px; font-weight: bold; color: #555; z-index: 10;";
798
+
799
+ const viewer = document.createElement('model-viewer');
800
+ viewer.src = glbPath;
801
+ viewer.setAttribute('auto-rotate', '');
802
+ viewer.setAttribute('camera-controls', '');
803
+ viewer.setAttribute('shadow-intensity', '1');
804
+ viewer.style.cssText = "width: 100%; height: 100%;";
805
+
806
+ viewer.addEventListener('load', () => {
807
+ if (viewer.model && viewer.model.materials.length > 0) {
808
+ const material = viewer.model.materials[0];
809
+
810
+ // [Red, Green, Blue, Alpha] (0.0 - 1.0)
811
+ material.pbrMetallicRoughness.setBaseColorFactor([0.8, 0.35, 0.2, 1.0]);
812
+ material.pbrMetallicRoughness.setBaseColorFactor([0.203, 0.374, 0.637, 1.0]);
813
+ // const r = Math.random();
814
+ // const g = Math.random();
815
+ // const b = Math.random();
816
+ // material.pbrMetallicRoughness.setBaseColorFactor([r, g, b, 1.0]);
817
+
818
+ material.pbrMetallicRoughness.setMetallicFactor(0.1);
819
+ material.pbrMetallicRoughness.setRoughnessFactor(0.7);
820
+ }
821
+ });
822
+
823
+ modelContainer.appendChild(modelTitle);
824
+ modelContainer.appendChild(viewer);
825
+
826
+ const closeBtn = document.createElement('button');
827
+ closeBtn.innerHTML = "&times;";
828
+ closeBtn.style.cssText = `
829
+ position: absolute; top: 15px; right: 20px;
830
+ background: none; border: none; color: #333; font-size: 30px;
831
+ cursor: pointer; z-index: 100; line-height: 1;
832
+ `;
833
+ closeBtn.onclick = () => document.body.removeChild(overlay);
834
+ overlay.onclick = (e) => { if (e.target === overlay) document.body.removeChild(overlay); };
835
+
836
+ container.appendChild(imgContainer);
837
+ container.appendChild(modelContainer);
838
+ container.appendChild(closeBtn);
839
+ overlay.appendChild(container);
840
+ document.body.appendChild(overlay);
841
+ }
842
+
843
+ /**
844
+ * Close fullscreen window
845
+ */
846
+ function closeGLBViewer() {
847
+ fullscreen.style.opacity = '0';
848
+ setTimeout(() => {
849
+ fullscreen.style.display = 'none';
850
+ content.innerHTML = '';
851
+ }, 250); // Match transition duration
852
+ }
853
+
854
+ // Add click handlers to all View GLB buttons
855
+ document.addEventListener('click', function(e) {
856
+ const button = e.target.closest('.x-button');
857
+ if (button && button.textContent.trim() === 'View GLB') {
858
+ const glbPath = button.getAttribute('data-glb');
859
+ const prompt = button.getAttribute('data-prompt');
860
+ if (glbPath) {
861
+ openGLBViewer(glbPath, prompt);
862
+ }
863
+ }
864
+ });
865
+
866
+ // Close button handler
867
+ closeBtn.addEventListener('click', closeGLBViewer);
868
+
869
+ // Close on backdrop click
870
+ fullscreen.addEventListener('click', function(e) {
871
+ if (e.target === fullscreen) {
872
+ closeGLBViewer();
873
+ }
874
+ });
875
+
876
+ // Close on Escape key
877
+ document.addEventListener('keydown', function(e) {
878
+ if (e.key === 'Escape' && fullscreen.style.display === 'flex') {
879
+ closeGLBViewer();
880
+ }
881
+ });
882
+ })();
883
+ </script>
884
+ </body></html>
docs/main.css ADDED
@@ -0,0 +1,626 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * {
2
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
3
+ }
4
+
5
+ a {
6
+ color: #5b6acf;
7
+ text-decoration: none;
8
+ }
9
+
10
+ a.link:focus, a.link:hover {
11
+ color: #8b5cf6;
12
+ text-decoration: none;
13
+ }
14
+
15
+ html {
16
+ scroll-behavior: smooth;
17
+ scroll-snap-type: y proximity;
18
+ }
19
+
20
+ body {
21
+ background: #faf9f7;
22
+ position: relative;
23
+ margin: 0px;
24
+ padding: 0px;
25
+ color: #2a2a2a;
26
+ overflow-x: hidden;
27
+ }
28
+
29
+ p {
30
+ position: relative;
31
+ margin: 16px;
32
+ font-size: 16px;
33
+ font-weight: 300;
34
+ text-align: justify;
35
+ }
36
+
37
+ p span {
38
+ font-weight: 500;
39
+ }
40
+
41
+ .x-row {
42
+ width: 100%;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ flex-wrap: nowrap;
47
+ }
48
+
49
+ .x-column {
50
+ display: flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ flex-wrap: nowrap;
54
+ flex-direction: column;
55
+ }
56
+
57
+ .x-center-text {
58
+ margin: 16px 32px;
59
+ text-align: center;
60
+ }
61
+
62
+ .x-left-align {
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: left;
66
+ flex-wrap: nowrap;
67
+ }
68
+
69
+ .x-right-align {
70
+ display: flex;
71
+ align-items: center;
72
+ justify-content: right;
73
+ flex-wrap: nowrap;
74
+ }
75
+
76
+ .x-flex-spacer {
77
+ flex: 1;
78
+ }
79
+
80
+ .x-labels {
81
+ position: absolute;
82
+ top: 8px;
83
+ right: 6px;
84
+ display: flex;
85
+ align-items: center;
86
+ justify-content: left;
87
+ flex-direction: row-reverse;
88
+ }
89
+
90
+ .x-label {
91
+ height: 20px;
92
+ padding: 0px 6px;
93
+ margin: 0px 2px;
94
+ color: #2a2a2a;
95
+ font-size: 12px;
96
+ font-weight: 600;
97
+ background: rgba(45, 45, 45, 0.1);
98
+ border-radius: 16px;
99
+ display: flex;
100
+ align-items: center;
101
+ justify-content: center;
102
+ }
103
+
104
+ .x-button {
105
+ height: 36px;
106
+ padding: 0px 14px;
107
+ background: rgba(45, 45, 45, 0.08);
108
+ color: #2a2a2a;
109
+ border-radius: 50px;
110
+ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
111
+ font-size: 16px;
112
+ font-weight: 300;
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ cursor: pointer;
117
+ transition: all 0.2s ease;
118
+ }
119
+
120
+ .x-button.small {
121
+ height: 32px;
122
+ padding: 0px 12px;
123
+ border-radius: 50px;
124
+ font-size: 14px;
125
+ font-weight: 600;
126
+ }
127
+
128
+ .x-button:hover {
129
+ background: rgba(45, 45, 45, 0.15);
130
+ transform: translateY(-2px);
131
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.15);
132
+ }
133
+
134
+ .x-button.disabled {
135
+ background: rgba(45, 45, 45, 0.05);
136
+ color: rgba(42, 42, 42, 0.4);
137
+ cursor: default;
138
+ }
139
+
140
+ .x-button.disabled:hover {
141
+ background: rgba(45, 45, 45, 0.05);
142
+ transform: none;
143
+ }
144
+
145
+ .x-gradient-font {
146
+ background: linear-gradient(270deg, #845ade 0%, #2e6ed6 25%, #ff7d4b 75%, #ec9b0b 100%);
147
+ background-clip: text;
148
+ -webkit-background-clip: text;
149
+ -webkit-text-fill-color: transparent;
150
+ }
151
+
152
+ .x-gradient-block {
153
+ color: #3f3f3f;
154
+ background: linear-gradient(270deg, #845ade2f 0%, #2e6ed62f 25%, #ff7d4b2f 75%, #ec9b0b2f 100%);
155
+ border-radius: 16px;
156
+ }
157
+
158
+ .x-gradient-border {
159
+ position: relative;
160
+ padding: 1px;
161
+ margin: 3px;
162
+ border: 3px;
163
+ background: white;
164
+ background-clip: padding-box;
165
+ border: solid border transparent;
166
+ border-radius: 16px;
167
+ }
168
+
169
+ .x-gradient-border::before {
170
+ content: '';
171
+ position: absolute;
172
+ top: 0; right: 0; bottom: 0; left: 0;
173
+ z-index: -1;
174
+ margin: -3px;
175
+ border-radius: 16px;
176
+ background: linear-gradient(270deg, #845ade 0%, #2e6ed6 25%, #ff7d4b 75%, #ec9b0b 100%);
177
+ }
178
+
179
+ .x-section-title {
180
+ text-align: center;
181
+ margin: 100px 0px 48px 0px;
182
+ font-size: 36px;
183
+ font-weight: 600;
184
+ letter-spacing: 2px;
185
+ text-transform: uppercase;
186
+ color: #333;
187
+ position: relative;
188
+ padding-bottom: 20px;
189
+ }
190
+
191
+ .x-section-title::after {
192
+ content: '';
193
+ position: absolute;
194
+ bottom: 0;
195
+ left: 50%;
196
+ transform: translateX(-50%);
197
+ width: 80px;
198
+ height: 3px;
199
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
200
+ border-radius: 2px;
201
+ }
202
+
203
+ .x-note {
204
+ color: rgba(42, 42, 42, 0.7);
205
+ font-size: 14px;
206
+ font-weight: 300;
207
+ }
208
+
209
+ .x-card {
210
+ position: relative;
211
+ display: flex;
212
+ align-items: center;
213
+ justify-content: center;
214
+ flex-wrap: wrap;
215
+ }
216
+
217
+ .x-card .caption {
218
+ height: 200px;
219
+ display: flex;
220
+ flex-direction: column;
221
+ align-items: center;
222
+ justify-content: center;
223
+ color: #2a2a2a;
224
+ font-size: 16px;
225
+ font-weight: 600;
226
+ width: 100%;
227
+ }
228
+
229
+ .x-handwriting {
230
+ width: 100%;
231
+ font-family: 'Segoe Print';
232
+ font-size: 12px;
233
+ font-weight: 600;
234
+ line-height: 1.5;
235
+ color: black;
236
+ text-align: justify;
237
+ }
238
+
239
+ .x-image-prompt {
240
+ position: relative;
241
+ height: calc(100% - 2px);
242
+ aspect-ratio: 1/1;
243
+ display: flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ }
247
+
248
+ .x-image-prompt img {
249
+ max-width: 100%;
250
+ max-height: 100%;
251
+ }
252
+
253
+ .x-small-header {
254
+ text-align: center;
255
+ margin-top: 64px;
256
+ margin-bottom: 24px;
257
+ margin-left: 4px;
258
+ font-size: 16px;
259
+ font-weight: 500;
260
+ letter-spacing: 4px;
261
+ text-transform: uppercase;
262
+ color: #666;
263
+ }
264
+
265
+ .x-dot-card {
266
+ background: rgba(255, 255, 255, 0.8);
267
+ border-radius: 16px;
268
+ padding: 24px;
269
+ display: flex;
270
+ flex-direction: column;
271
+ box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.08);
272
+ }
273
+
274
+ .x-dot-card-title {
275
+ margin-top: 0;
276
+ margin-bottom: 0px;
277
+ font-size: 20px;
278
+ color: #2a2a2a;
279
+ display: flex;
280
+ align-items: center;
281
+ gap: 10px;
282
+ }
283
+
284
+ #main {
285
+ max-width: 1000px;
286
+ margin: 0px auto;
287
+ padding-bottom: 200px;
288
+ }
289
+
290
+ .author-info {
291
+ display: flex;
292
+ justify-content: center;
293
+ align-items: center;
294
+ gap: 32px;
295
+ padding: 8px;
296
+ }
297
+
298
+ .author-link {
299
+ color: #2a2a2a;
300
+ text-decoration: none;
301
+ font-weight: 500;
302
+ }
303
+
304
+ .author-link:focus, .author-link:hover {
305
+ text-decoration: underline;
306
+ }
307
+
308
+ .affiliation-link {
309
+ font-size: 14px;
310
+ color: rgba(42, 42, 42, 0.7);
311
+ text-decoration: none;
312
+ font-weight: 300;
313
+ }
314
+
315
+ #links {
316
+ margin: 16px 0;
317
+ display: flex;
318
+ align-items: center;
319
+ justify-content: center;
320
+ flex-wrap: wrap;
321
+ }
322
+
323
+ #links div {
324
+ margin: 4px 8px;
325
+ height: 38px;
326
+ display: flex;
327
+ align-items: center;
328
+ justify-content: center;
329
+ }
330
+
331
+ #links a {
332
+ height: 20px;
333
+ padding: 8px 16px;
334
+ color: #2a2a2a;
335
+ font-size: 16px;
336
+ font-weight: 300;
337
+ background: rgba(255, 255, 255, 0.9);
338
+ border-radius: 50px;
339
+ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
340
+ display: flex;
341
+ align-items: center;
342
+ justify-content: center;
343
+ transition: all 0.2s ease;
344
+ }
345
+
346
+ #links a:hover {
347
+ background: rgba(255, 255, 255, 1);
348
+ transform: translateY(-2px);
349
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.15);
350
+ }
351
+
352
+ #links a.disabled {
353
+ background-color: rgba(200, 200, 200, 0.5);
354
+ color: rgba(42, 42, 42, 0.5);
355
+ }
356
+
357
+ #links a.disabled:hover {
358
+ background-color: rgba(200, 200, 200, 0.5);
359
+ }
360
+
361
+ #links a::before {
362
+ /* Material Icons */
363
+ font-family: 'Material Icons' !important;
364
+ font-style: normal;
365
+ font-weight: normal;
366
+ font-variant: normal;
367
+ text-transform: none;
368
+ line-height: 1;
369
+ letter-spacing: normal;
370
+ word-wrap: normal;
371
+ white-space: nowrap;
372
+ direction: ltr;
373
+
374
+ /* Better Font Rendering =========== */
375
+ -webkit-font-smoothing: antialiased;
376
+ -moz-osx-font-smoothing: grayscale;
377
+ text-rendering: optimizeLegibility;
378
+ font-feature-settings: 'liga';
379
+
380
+ margin-right: 8px;
381
+ font-size: 20px;
382
+ }
383
+
384
+ #links #paper::before {
385
+ content: "description";
386
+ }
387
+
388
+ #links #arxiv::before {
389
+ content: "article";
390
+ }
391
+
392
+ #links #code::before {
393
+ content: "code";
394
+ }
395
+
396
+ #links #poster::before {
397
+ content: "picture_as_pdf";
398
+ }
399
+
400
+ #links #video::before {
401
+ content: "play_circle";
402
+ }
403
+
404
+ #links #demo::before {
405
+ content: "rocket_launch";
406
+ }
407
+
408
+ .feature-container {
409
+ max-width: 1000px;
410
+ margin: 32px auto;
411
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
412
+ }
413
+
414
+ .feature-tabs {
415
+ display: grid;
416
+ grid-template-columns: repeat(4, 1fr);
417
+ gap: 16px;
418
+ }
419
+
420
+ .feature-tab {
421
+ aspect-ratio: 1 / 1;
422
+ background-color: rgba(255, 255, 255, 0.8);
423
+ border: 2px solid transparent;
424
+ border-radius: 12px;
425
+ cursor: pointer;
426
+ display: flex;
427
+ flex-direction: column;
428
+ align-items: center;
429
+ justify-content: center;
430
+ transition: all 0.3s ease;
431
+ color: #666;
432
+ padding: 10px;
433
+ text-align: center;
434
+ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
435
+ }
436
+
437
+ .feature-tab:hover {
438
+ background-color: rgba(255, 255, 255, 1);
439
+ transform: translateY(-2px);
440
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.12);
441
+ }
442
+
443
+ .feature-tab.active-yellow { border-color: #facd5c; color: #facd5c; background-color: rgba(251, 191, 36, 0.1); }
444
+ .feature-tab.active-red { border-color: #d17969; color: #d17969; background-color: rgba(209, 121, 105, 0.1); }
445
+ .feature-tab.active-blue { border-color: #60a5fa; color: #60a5fa; background-color: rgba(96, 165, 250, 0.1); }
446
+ .feature-tab.active-purple { border-color: #b7a5ff; color: #b7a5ff; background-color: rgba(167, 139, 250, 0.1); }
447
+
448
+ .feature-tab svg {
449
+ width: 64px;
450
+ height: 64px;
451
+ margin-bottom: 8px;
452
+ fill: currentColor;
453
+ }
454
+
455
+ .feature-tab span {
456
+ font-size: 15px;
457
+ font-weight: 400;
458
+ line-height: 1.2;
459
+ }
460
+
461
+ .feature-panel {
462
+ display: none;
463
+ padding: 32px;
464
+ animation: fadeIn 0.4s ease;
465
+ }
466
+
467
+ .feature-panel.active {
468
+ display: block;
469
+ }
470
+
471
+ @keyframes fadeIn {
472
+ from { opacity: 0; transform: translateY(10px); }
473
+ to { opacity: 1; transform: translateY(0); }
474
+ }
475
+
476
+ @media (max-width: 600px) {
477
+ .feature-tabs {
478
+ grid-template-columns: repeat(2, 1fr);
479
+ }
480
+ .feature-panel {
481
+ padding: 16px;
482
+ }
483
+ }
484
+
485
+ .bibtex-entry {
486
+ margin: 32px auto;
487
+ max-width: 900px;
488
+ padding: 0px 24px;
489
+ color: #2a2a2a;
490
+ font-family: consolas, monospace;
491
+ white-space: pre;
492
+ text-wrap: wrap;
493
+ font-size: 14px;
494
+ font-weight: 300;
495
+ display: flex;
496
+ flex-direction: column;
497
+ align-items: left;
498
+ justify-content: center;
499
+ }
500
+
501
+ .line {
502
+ display: grid;
503
+ grid-template-columns: max-content 1fr;
504
+ gap: 0;
505
+ }
506
+
507
+ .key {
508
+ font-family: consolas, monospace;
509
+ text-align: right;
510
+ padding-right: 0;
511
+ }
512
+
513
+ .value {
514
+ font-family: consolas, monospace;
515
+ word-break: break-word;
516
+ }
517
+
518
+ #bottombar {
519
+ position: absolute;
520
+ bottom: 0px;
521
+ height: 100px;
522
+ width: 100%;
523
+ display: flex;
524
+ flex-direction: column;
525
+ align-items: center;
526
+ justify-content: space-around;
527
+ user-select: none;
528
+ }
529
+
530
+ #bottombar .row {
531
+ width: 90%;
532
+ padding: 0px 5%;
533
+ display: flex;
534
+ align-items: center;
535
+ justify-content: space-between;
536
+ user-select: none;
537
+ }
538
+
539
+ #bottombar div {
540
+ color: rgba(42, 42, 42, 0.7);
541
+ font-size: 12px;
542
+ font-weight: 500;
543
+ }
544
+
545
+ #bottombar div a {
546
+ color: rgba(42, 42, 42, 0.7);
547
+ font-size: 12px;
548
+ font-weight: 500;
549
+ }
550
+
551
+ #bottombar div a:hover {
552
+ color: rgba(42, 42, 42, 1);
553
+ font-size: 12px;
554
+ }
555
+
556
+ #bottombar div span {
557
+ font-weight: 700;
558
+ }
559
+
560
+ .scroll-indicator {
561
+ position: fixed;
562
+ bottom: 4px;
563
+ left: 50%;
564
+ transform: translateX(-50%);
565
+ display: flex;
566
+ flex-direction: column;
567
+ align-items: center;
568
+ color: #666;
569
+ cursor: pointer;
570
+ z-index: 1000;
571
+
572
+ animation: scroll-bounce 2s infinite;
573
+ transition: opacity 0.5s ease, visibility 0.5s;
574
+ }
575
+
576
+ .scroll-indicator.hidden {
577
+ opacity: 0;
578
+ visibility: hidden;
579
+ }
580
+
581
+ @keyframes scroll-bounce {
582
+ 0%, 20%, 50%, 80%, 100% {
583
+ transform: translateX(-50%) translateY(0);
584
+ }
585
+ 40% {
586
+ transform: translateX(-50%) translateY(-10px);
587
+ }
588
+ 60% {
589
+ transform: translateX(-50%) translateY(-5px);
590
+ }
591
+ }
592
+
593
+ /* Animated Gradient Background with Mouse Follow */
594
+ .animated-gradient {
595
+ --mouse-x: 50%;
596
+ --mouse-y: 50%;
597
+ background:
598
+ radial-gradient(circle at var(--mouse-x) var(--mouse-y), rgba(102, 126, 234, 0.8) 0%, transparent 50%),
599
+ radial-gradient(circle at calc(100% - var(--mouse-x)) calc(100% - var(--mouse-y)), rgba(118, 75, 162, 0.8) 0%, transparent 50%),
600
+ radial-gradient(circle at var(--mouse-x) calc(100% - var(--mouse-y)), rgba(240, 147, 251, 0.6) 0%, transparent 50%),
601
+ linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
602
+ background-size: 200% 200%;
603
+ animation: gradient-shift 20s ease infinite;
604
+ transition: background 0.1s ease-out;
605
+ }
606
+
607
+ /* Hero expandable effect */
608
+ .hero-expandable {
609
+ position: sticky;
610
+ top: 0;
611
+ z-index: 10;
612
+ transition: height 0.6s cubic-bezier(0.4, 0, 0.2, 1);
613
+ margin-bottom: 0;
614
+ }
615
+
616
+ @keyframes gradient-shift {
617
+ 0% {
618
+ background-position: 0% 50%;
619
+ }
620
+ 50% {
621
+ background-position: 100% 50%;
622
+ }
623
+ 100% {
624
+ background-position: 0% 50%;
625
+ }
626
+ }
docs/pv.css ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .pv-video-wrapper {
2
+ position: relative;
3
+ width: 100%;
4
+ aspect-ratio: 16 / 9;
5
+ margin: 0 auto;
6
+ background-color: #2a2a2a;
7
+ overflow: hidden;
8
+ border-radius: 8px;
9
+ }
10
+
11
+ .pv-video-element {
12
+ width: 100%;
13
+ height: 100%;
14
+ display: block;
15
+ object-fit: contain;
16
+ }
17
+
18
+ .pv-poster-overlay {
19
+ position: absolute;
20
+ top: 0;
21
+ left: 0;
22
+ width: 100%;
23
+ height: 100%;
24
+ cursor: pointer;
25
+ z-index: 10;
26
+ display: flex;
27
+ justify-content: center;
28
+ align-items: center;
29
+ background-size: cover;
30
+ background-position: center;
31
+ transition: opacity 0.3s ease;
32
+ }
33
+
34
+ .pv-play-btn {
35
+ width: 64px;
36
+ height: 64px;
37
+ background-color: rgba(255, 255, 255, 0.9);
38
+ border: 2px solid #2a2a2a;
39
+ border-radius: 50%;
40
+ display: flex;
41
+ justify-content: center;
42
+ align-items: center;
43
+ transition: transform 0.2s ease, background-color 0.2s;
44
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
45
+ }
46
+
47
+ .pv-play-btn::after {
48
+ content: '';
49
+ display: block;
50
+ width: 0;
51
+ height: 0;
52
+ border-top: 10px solid transparent;
53
+ border-bottom: 10px solid transparent;
54
+ border-left: 16px solid #2a2a2a;
55
+ margin-left: 4px;
56
+ }
57
+
58
+ .pv-poster-overlay:hover .pv-play-btn {
59
+ transform: scale(1.1);
60
+ background-color: rgba(255, 255, 255, 1);
61
+ }
62
+
63
+ .pv-video-wrapper.is-playing .pv-poster-overlay {
64
+ opacity: 0;
65
+ pointer-events: none;
66
+ }
docs/style.css ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ /* Icomoon font removed - now using Material Icons via CDN */
2
+ /* Material Icons are loaded in index.html via Google Fonts CDN */
docs/stylesheet.css ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ /* Font definitions removed - using system Segoe UI font */
2
+ /* All font-face declarations for Avenir Next Cyr have been removed */
3
+ /* The site now uses 'Segoe UI' as defined in main.css and style.css */
docs/window.css ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #fullscreen {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ width: 100vw;
6
+ height: 100vh;
7
+ background: transparent;
8
+ display: none;
9
+ align-items: center;
10
+ justify-content: center;
11
+ z-index: 1000;
12
+ user-select: none;
13
+ backdrop-filter: blur(10px);
14
+ opacity: 0;
15
+ transition: opacity 0.25s ease;
16
+ }
17
+
18
+ #fullscreen #window {
19
+ position: relative;
20
+ min-width: 25vw;
21
+ min-height: 25vh;
22
+ max-width: 100vw;
23
+ max-height: 90vh;
24
+ background: #ffffff;
25
+ border-radius: 16px;
26
+ box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.2);
27
+ padding: 8px;
28
+
29
+ }
30
+
31
+ #fullscreen #window #close {
32
+ position: absolute;
33
+ top: 0px;
34
+ right: 0px;
35
+ width: 31px;
36
+ height: 30px;
37
+ padding: 0px 0px 2px 1px;
38
+ color: black;
39
+ font-size: 16px;
40
+ font-weight: 700;
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ cursor: pointer;
45
+ transition: all 0.2s ease;
46
+ z-index: 100;
47
+ }
48
+
49
+ #fullscreen #window #close {
50
+ color: #2a2a2a;
51
+ }
52
+
53
+ #fullscreen #window #close:hover {
54
+ color: #d32f2f;
55
+ }
56
+
57
+ #fullscreen #window #content {
58
+ max-width: calc(100vw - 16px);
59
+ max-height: calc(90vh - 16px);
60
+ overflow-x: hidden;
61
+ overflow-y: auto;
62
+ }
63
+
64
+ .modelviewer-container {
65
+ width: 500px;
66
+ height: 500px;
67
+ margin: 8px;
68
+ border-radius: 8px;
69
+ background: white;
70
+ box-shadow: inset 0px 0px 4px rgba(0, 0, 0, 0.25);
71
+ overflow: hidden;
72
+ position: relative;
73
+ }
74
+
75
+ .modelviewer-container model-viewer {
76
+ width: 100% !important;
77
+ height: 100% !important;
78
+ display: block !important;
79
+ background-color: #f5f5f5;
80
+ }
81
+
82
+ .modelviewer-container model-viewer button {
83
+ height: 16px;
84
+ padding: 0px 6px;
85
+ background: rgba(255, 255, 255, 0.75);
86
+ border-radius: 50px;
87
+ box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
88
+ border: none;
89
+ font-size: 12px;
90
+ font-weight: 300;
91
+ display: none;
92
+ opacity: 0;
93
+ align-items: center;
94
+ justify-content: center;
95
+ pointer-events: none;
96
+ }
97
+
98
+ .modelviewer-panel {
99
+ width: 300px;
100
+ margin: 8px;
101
+ margin-top: 0px;
102
+ display: flex;
103
+ flex-direction: column;
104
+ align-items: start;
105
+ justify-content: start;
106
+ }
107
+
108
+ .modelviewer-panel-desc {
109
+ width: 100%;
110
+ }
111
+
112
+ .modelviewer-panel-desc div {
113
+ font-size: 16px;
114
+ font-weight: 500;
115
+ margin: 4px;
116
+ }
117
+
118
+ .modelviewer-panel-prompt {
119
+ width: calc(100% - 16px);
120
+ height: 250px;
121
+ padding: 8px;
122
+ background: #f5f5f5;
123
+ border-radius: 8px;
124
+ box-shadow: inset 0px 0px 4px rgba(0, 0, 0, 0.1);
125
+ display: flex;
126
+ align-items: start;
127
+ justify-content: center;
128
+ overflow-y: auto;
129
+ user-select: text;
130
+ }
131
+
132
+ .modelviewer-panel-button {
133
+ height: 40px;
134
+ margin: 4px 4px;
135
+ padding: 0px 14px;
136
+ background: rgba(45, 45, 45, 0.08);
137
+ color: #2a2a2a;
138
+ border-radius: 50px;
139
+ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
140
+ font-size: 16px;
141
+ font-weight: 300;
142
+ display: flex;
143
+ align-items: center;
144
+ justify-content: center;
145
+ cursor: pointer;
146
+ transition: all 0.2s ease;
147
+ }
148
+
149
+ .modelviewer-panel-button.small {
150
+ height: 32px;
151
+ padding: 0px 12px;
152
+ border-radius: 50px;
153
+ font-size: 14px;
154
+ font-weight: 300;
155
+ }
156
+
157
+ .modelviewer-panel-button.tiny {
158
+ height: 24px;
159
+ padding: 0px 10px;
160
+ border-radius: 50px;
161
+ font-size: 12px;
162
+ font-weight: 300;
163
+ }
164
+
165
+ .modelviewer-panel-button:hover {
166
+ background: rgba(45, 45, 45, 0.15);
167
+ transform: translateY(-2px);
168
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.15);
169
+ }
170
+
171
+ .modelviewer-panel-button.checked {
172
+ border: 2px solid #2a2a2a;
173
+ background: rgba(45, 45, 45, 0.1);
174
+ color: #2a2a2a;
175
+ }
inputs/coarse_mesh/1.glb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e91d3836f418f03b7c43977b72dd2d2ed5ac282f4c2c245adf88ee0480e80dbb
3
+ size 26370512
inputs/image/1.png ADDED

Git LFS Details

  • SHA256: ccfa824dc676397132973ede5799a04205a38c96cf021c73d450ad3b686c42d7
  • Pointer size: 131 Bytes
  • Size of remote file: 978 kB
requirements.txt ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ accelerate==1.1.1
2
+ diffusers==0.30.0
3
+ deepspeed
4
+ diso==0.1.4
5
+ einops==0.8.1
6
+ flash_attn==2.8.3
7
+ huggingface_hub==0.36.0
8
+ imageio==2.36.0
9
+ ipywidgets==8.1.7
10
+ jaxtyping==0.3.4
11
+ matplotlib==3.10.8
12
+ numpy==1.24.4
13
+ omegaconf==2.3.0
14
+ opencv_python==4.10.0.84
15
+ opencv_python_headless==4.11.0.86
16
+ pandas==2.3.3
17
+ Pillow==12.0.0
18
+ pymeshlab==2022.2.post3
19
+ pythreejs==2.4.2
20
+ pytorch_lightning==1.9.5
21
+ PyYAML==6.0.2
22
+ safetensors==0.7.0
23
+ sageattention==1.0.6
24
+ scikit-image==0.24.0
25
+ onnxruntime
26
+ rembg
27
+ tensorboard
28
+ timm==1.0.22
29
+ torchdiffeq==0.2.5
30
+ tqdm==4.66.5
31
+ transformers==4.37.2
32
+ trimesh==4.4.7
33
+ typeguard==4.3.0
34
+ wandb==0.23.1
scripts/gradio_app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import gc
3
+ import os
4
+ import sys
5
+
6
+ import gradio as gr
7
+ import torch
8
+ from omegaconf import OmegaConf
9
+
10
+ # Add project root to path
11
+ sys.path.append(os.getcwd())
12
+
13
+ from ultrashape.rembg import BackgroundRemover
14
+ from ultrashape.utils.misc import instantiate_from_config
15
+ from ultrashape.surface_loaders import SharpEdgeSurfaceLoader
16
+ from ultrashape.utils import voxelize_from_point
17
+ from ultrashape.pipelines import UltraShapePipeline
18
+
19
+ # Global variables to cache the model
20
+ MODEL_CACHE = {}
21
+
22
+
23
+ def get_pipeline_cached(config_path, ckpt_path, device='cuda', low_vram=False):
24
+ # Check if we have a valid cached pipeline for this checkpoint
25
+ if "pipeline" in MODEL_CACHE and MODEL_CACHE.get("ckpt_path") == ckpt_path:
26
+ print("Using cached pipeline...")
27
+ return MODEL_CACHE["pipeline"], MODEL_CACHE["config"]
28
+
29
+ # Clear old cache if it exists (e.g. different checkpoint)
30
+ if MODEL_CACHE:
31
+ print("Clearing old model cache...")
32
+ MODEL_CACHE.clear()
33
+ gc.collect()
34
+ torch.cuda.empty_cache()
35
+
36
+ print(f"Loading config from {config_path}...")
37
+ config = OmegaConf.load(config_path)
38
+
39
+ print("Instantiating VAE...")
40
+ vae = instantiate_from_config(config.model.params.vae_config)
41
+
42
+ print("Instantiating DiT...")
43
+ dit = instantiate_from_config(config.model.params.dit_cfg)
44
+
45
+ print("Instantiating Conditioner...")
46
+ conditioner = instantiate_from_config(config.model.params.conditioner_config)
47
+
48
+ print("Instantiating Scheduler & Processor...")
49
+ scheduler = instantiate_from_config(config.model.params.scheduler_cfg)
50
+ image_processor = instantiate_from_config(config.model.params.image_processor_cfg)
51
+
52
+ print(f"Loading weights from {ckpt_path}...")
53
+ weights = torch.load(ckpt_path, map_location='cpu')
54
+
55
+ vae.load_state_dict(weights['vae'], strict=True)
56
+ dit.load_state_dict(weights['dit'], strict=True)
57
+ conditioner.load_state_dict(weights['conditioner'], strict=True)
58
+
59
+ vae.eval().to(device)
60
+ dit.eval().to(device)
61
+ conditioner.eval().to(device)
62
+
63
+ if hasattr(vae, 'enable_flashvdm_decoder'):
64
+ vae.enable_flashvdm_decoder()
65
+
66
+ print("Creating Pipeline...")
67
+ pipeline = UltraShapePipeline(
68
+ vae=vae,
69
+ model=dit,
70
+ scheduler=scheduler,
71
+ conditioner=conditioner,
72
+ image_processor=image_processor
73
+ )
74
+
75
+ if low_vram:
76
+ pipeline.enable_model_cpu_offload()
77
+
78
+ MODEL_CACHE["pipeline"] = pipeline
79
+ MODEL_CACHE["config"] = config
80
+ MODEL_CACHE["ckpt_path"] = ckpt_path
81
+
82
+ return pipeline, config
83
+
84
+
85
+ def predict(
86
+ image_input,
87
+ mesh_input,
88
+ steps,
89
+ scale,
90
+ octree_res,
91
+ num_latents,
92
+ chunk_size,
93
+ seed,
94
+ remove_bg,
95
+ ckpt_path,
96
+ low_vram
97
+ ):
98
+ # Aggressive memory cleanup at start
99
+ gc.collect()
100
+ torch.cuda.empty_cache()
101
+
102
+ try:
103
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
104
+ config_path = "configs/infer_dit_refine.yaml"
105
+
106
+ if not os.path.exists(config_path):
107
+ raise FileNotFoundError(f"Config not found at {config_path}")
108
+
109
+ pipeline, config = get_pipeline_cached(config_path, ckpt_path, device, low_vram)
110
+
111
+ voxel_res = config.model.params.vae_config.params.voxel_query_res
112
+
113
+ print(f"Initializing Surface Loader (Token Num: {num_latents})...")
114
+ loader = SharpEdgeSurfaceLoader(
115
+ num_sharp_points=204800,
116
+ num_uniform_points=204800,
117
+ )
118
+
119
+ print(f"Processing inputs...")
120
+ if image_input is None:
121
+ raise ValueError("Image input is required")
122
+ if mesh_input is None:
123
+ raise ValueError("Mesh input is required")
124
+
125
+ # Handle image input
126
+ if isinstance(image_input, dict):
127
+ # In newer gradio versions Image component might return a dict for mask etc, but usually just PIL/numpy
128
+ # if type='pil' it is PIL.Image
129
+ pass
130
+
131
+ image = image_input.convert("RGBA")
132
+
133
+ if remove_bg or image.mode != 'RGBA':
134
+ rembg = BackgroundRemover()
135
+ image = rembg(image)
136
+
137
+ # Handle mesh input - Gradio Model3D returns path to file
138
+ surface = loader(mesh_input, normalize_scale=scale).to(device, dtype=torch.float16)
139
+ pc = surface[:, :, :3] # [B, N, 3]
140
+
141
+ # Voxelize
142
+ _, voxel_idx = voxelize_from_point(pc, num_latents, resolution=voxel_res)
143
+
144
+ print("Running diffusion process...")
145
+ gen_device = "cpu" if low_vram else device
146
+ generator = torch.Generator(gen_device).manual_seed(int(seed))
147
+
148
+ with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
149
+ mesh_out_list, _ = pipeline(
150
+ image=image,
151
+ voxel_cond=voxel_idx,
152
+ generator=generator,
153
+ box_v=1.0,
154
+ mc_level=0.0,
155
+ octree_resolution=int(octree_res),
156
+ num_chunks=int(chunk_size),
157
+ num_inference_steps=int(steps)
158
+ )
159
+
160
+ # Save output
161
+ output_dir = "outputs_gradio"
162
+ os.makedirs(output_dir, exist_ok=True)
163
+ base_name = "output"
164
+ save_path = os.path.join(output_dir, f"{base_name}_refined.glb")
165
+
166
+ mesh_out = mesh_out_list[0]
167
+ mesh_out.export(save_path)
168
+ print(f"Successfully saved to {save_path}")
169
+
170
+ return save_path
171
+
172
+ finally:
173
+ # Aggressive memory cleanup at end
174
+ gc.collect()
175
+ torch.cuda.empty_cache()
176
+
177
+
178
+ if __name__ == "__main__":
179
+ parser = argparse.ArgumentParser(description="UltraShape Gradio App")
180
+ parser.add_argument("--ckpt", type=str, required=True, help="Path to split checkpoint (.pt)")
181
+ parser.add_argument("--share", action="store_true", help="Share the gradio app")
182
+ parser.add_argument("--low_vram", action="store_true", help="Optimize for low VRAM usage")
183
+
184
+ args = parser.parse_args()
185
+
186
+ # Define Gradio Interface
187
+ with gr.Blocks(title="UltraShape Inference") as demo:
188
+ gr.Markdown("# UltraShape Inference: Mesh & Image Refinement")
189
+
190
+ with gr.Row():
191
+ with gr.Column():
192
+ image_input = gr.Image(type="pil", label="Input Image", image_mode="RGBA")
193
+ mesh_input = gr.Model3D(label="Input Coarse Mesh (.glb, .obj)")
194
+
195
+ with gr.Accordion("Advanced Parameters", open=True):
196
+ steps = gr.Slider(minimum=1, maximum=200, value=50, step=1, label="Inference Steps")
197
+ scale = gr.Slider(minimum=0.1, maximum=2.0, value=0.99, label="Mesh Normalization Scale")
198
+ octree_res = gr.Slider(minimum=64, maximum=2048, value=1024, step=64, label="Octree Resolution")
199
+ num_latents = gr.Slider(minimum=1024, maximum=32768, value=32768, step=128,
200
+ label="Number of Latent Tokens (Use 8192 if OOM)")
201
+ chunk_size = gr.Slider(minimum=512, maximum=10000, value=2048, step=512,
202
+ label="Chunk Size (Use 2000 if OOM)")
203
+ seed = gr.Number(value=42, label="Random Seed")
204
+ remove_bg = gr.Checkbox(label="Remove Background", value=False)
205
+
206
+ run_btn = gr.Button("Run Inference", variant="primary")
207
+
208
+ with gr.Column():
209
+ output_model = gr.Model3D(label="Refined Output Mesh")
210
+
211
+ run_btn.click(
212
+ fn=lambda img, mesh, s, sc, oct, nml, chk, sd, rm: predict(img, mesh, s, sc, oct, nml, chk, sd, rm, args.ckpt,
213
+ args.low_vram),
214
+ inputs=[image_input, mesh_input, steps, scale, octree_res, num_latents, chunk_size, seed, remove_bg],
215
+ outputs=[output_model]
216
+ )
217
+
218
+ demo.launch(share=args.share, server_name='0.0.0.0', server_port=7860)