Upload 98 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +33 -0
- app.py +256 -0
- configs/infer_dit_refine.yaml +72 -0
- configs/train_dit_refine.yaml +138 -0
- configs/train_vae_refine.yaml +89 -0
- docs/assets/favicon.png +0 -0
- docs/assets/images/1.png +3 -0
- docs/assets/images/10.png +3 -0
- docs/assets/images/11.png +3 -0
- docs/assets/images/12.png +3 -0
- docs/assets/images/13.png +3 -0
- docs/assets/images/2.png +3 -0
- docs/assets/images/3.png +3 -0
- docs/assets/images/4.png +3 -0
- docs/assets/images/5.png +3 -0
- docs/assets/images/6.png +3 -0
- docs/assets/images/7.png +3 -0
- docs/assets/images/8.png +3 -0
- docs/assets/images/9.png +3 -0
- docs/assets/images/comp-close.png +3 -0
- docs/assets/images/comp-open1.png +3 -0
- docs/assets/images/comp-open2.png +3 -0
- docs/assets/images/pipeline.png +3 -0
- docs/assets/images/teaser.png +3 -0
- docs/assets/videos/1.mp4 +3 -0
- docs/assets/videos/10.mp4 +3 -0
- docs/assets/videos/11.mp4 +3 -0
- docs/assets/videos/12.mp4 +3 -0
- docs/assets/videos/13.mp4 +3 -0
- docs/assets/videos/2.mp4 +3 -0
- docs/assets/videos/3.mp4 +3 -0
- docs/assets/videos/4.mp4 +3 -0
- docs/assets/videos/5.mp4 +3 -0
- docs/assets/videos/6.mp4 +3 -0
- docs/assets/videos/7.mp4 +3 -0
- docs/assets/videos/8.mp4 +3 -0
- docs/assets/videos/9.mp4 +3 -0
- docs/carousel.css +102 -0
- docs/carousel.js +249 -0
- docs/index copy.html +788 -0
- docs/index.html +884 -0
- docs/main.css +626 -0
- docs/pv.css +66 -0
- docs/style.css +2 -0
- docs/stylesheet.css +3 -0
- docs/window.css +175 -0
- inputs/coarse_mesh/1.glb +3 -0
- inputs/image/1.png +3 -0
- requirements.txt +34 -0
- 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
|
docs/assets/images/10.png
ADDED
|
Git LFS Details
|
docs/assets/images/11.png
ADDED
|
Git LFS Details
|
docs/assets/images/12.png
ADDED
|
Git LFS Details
|
docs/assets/images/13.png
ADDED
|
Git LFS Details
|
docs/assets/images/2.png
ADDED
|
Git LFS Details
|
docs/assets/images/3.png
ADDED
|
Git LFS Details
|
docs/assets/images/4.png
ADDED
|
Git LFS Details
|
docs/assets/images/5.png
ADDED
|
Git LFS Details
|
docs/assets/images/6.png
ADDED
|
Git LFS Details
|
docs/assets/images/7.png
ADDED
|
Git LFS Details
|
docs/assets/images/8.png
ADDED
|
Git LFS Details
|
docs/assets/images/9.png
ADDED
|
Git LFS Details
|
docs/assets/images/comp-close.png
ADDED
|
Git LFS Details
|
docs/assets/images/comp-open1.png
ADDED
|
Git LFS Details
|
docs/assets/images/comp-open2.png
ADDED
|
Git LFS Details
|
docs/assets/images/pipeline.png
ADDED
|
Git LFS Details
|
docs/assets/images/teaser.png
ADDED
|
Git LFS Details
|
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"><</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">></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"><</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">></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"><</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">></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"><</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">></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 = "×";
|
| 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
|
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)
|