import platform import sys import bpy from .runtime import runtime from .server import model_catalog as shared_model_catalog DEVICE_LABELS = { "cuda": ("CUDA", "NVIDIA CUDA"), "directml": ("DirectML", "Windows GPU (DirectML)"), "coreml": ("CoreML", "Apple CoreML"), "cpu": ("CPU", "CPU"), } def supports_coreml(): return sys.platform == "darwin" and platform.machine().lower() in { "arm64", "aarch64", } def supported_devices(): devices = runtime.environment_manifest().get("devices") if isinstance(devices, list) and devices: names = [ name for device in devices if (name := str(device).lower()) in DEVICE_LABELS ] if not supports_coreml(): names = [name for name in names if name != "coreml"] elif "cpu" in names: names = ["cpu", *(name for name in names if name != "cpu")] return names or ["cpu"] if sys.platform == "win32": return ["directml", "cpu"] return ["cpu", "coreml"] if supports_coreml() else ["cpu"] def device_enum_items(_owner, _context): return [ (name.upper(), *DEVICE_LABELS[name], index) for index, name in enumerate(supported_devices()) ] def models_status(refresh=False): status = runtime.server_status().get("resources", {}).get("model_manager", {}) return { "catalog": tuple( shared_model_catalog.model_record( model, _model_files_ready(model), ) for model in shared_model_catalog.DOWNLOADABLE_MODELS.values() ), "loaded": status.get("loaded", {}), } def _model_files_ready(model): directory = runtime.storage_root() / "models" / model.directory_name return all(any(directory.glob(pattern)) for pattern in model.ready_patterns) def model_ready(model_key): return any( model.get("key") == model_key and model.get("ready") for model in models_status().get("catalog", ()) ) def ai_status(): environment_ready = runtime.environment_ready() ready_models = {model["key"] for model in model_catalog() if model.get("ready")} missing_models = tuple( model_key for model_key in shared_model_catalog.DEFAULT_MODEL_KEYS if model_key not in ready_models ) return { "environment_ready": environment_ready, "missing_models": missing_models, "ready": environment_ready and not missing_models, } def ai_ready(): return ai_status()["ready"] def ai_setup_label(status=None): if status is None: status = ai_status() return ( "Download Required Models…" if status["environment_ready"] else "Set Up AI Server…" ) def model_download_size(model_key): model = shared_model_catalog.get_downloadable_model(model_key) return sum(size for _path, size, _checksum in model.r2_files) def model_catalog(): return models_status().get("catalog", ()) def model_record(model_key): return next( (model for model in model_catalog() if model.get("key") == model_key), None, ) def _model_items(models): numbers = {"MOGE2_VITS_NORMAL": 0, "MOGE2_VITB_NORMAL": 1, "MOGE3_VITL": 3} return [ ( model["key"], f"{tier} | {model['label']}", f"{model['description']}; license: {model['license']}", numbers[model["key"]], ) for model, tier in zip(models, ("Fast", "Base", "Pro")) ] def geometry_model_enum_items(_owner, _context): models = [model for model in model_catalog() if model.get("family") == "moge"] return _model_items(models) def upscale_model_label(model_key): model = shared_model_catalog.UPSCALE_MODELS.get(model_key) return model.label if model is not None else model_key def cutout_gesture_property(): return bpy.props.EnumProperty( name="Gesture", description="Shape used to select the Cutout area", items=( ("LASSO", "Lasso", "Draw a closed freehand area"), ("POLYLINE", "Polyline", "Click points to define a closed area"), ), default="LASSO", ) def mask_gesture_property(): return bpy.props.EnumProperty( name="Gesture", description="Shape used to edit image Alpha", items=( ("LASSO", "Lasso", "Draw a closed freehand area"), ("BRUSH", "Brush", "Paint with a circular brush"), ("POLYLINE", "Polyline", "Click points to define a closed area"), ), default="LASSO", ) def mask_mode_property(): return bpy.props.EnumProperty( name="Mode", description="Choose how the selected or painted area changes image visibility.", items=( ( "SET", "Set", "Keep visible content inside the selected or painted area and hide everything outside.", "SELECT_SET", 0, ), ( "EXTEND", "Extend", "Reveal the selected or painted area while keeping other areas unchanged.", "SELECT_EXTEND", 1, ), ( "SUBTRACT", "Subtract", "Hide the selected or painted area while keeping other areas unchanged.", "SELECT_SUBTRACT", 2, ), ), default="SET", ) def mask_radius_property(): return bpy.props.IntProperty( name="Radius", description="Brush radius in Viewport pixels", default=25, min=1, soft_max=500, subtype="PIXEL", ) def cutout_generate_normal_property(): return bpy.props.BoolProperty( name="Normal Map", description=( "Generate a normal map to add surface detail under lighting for Flat and Solid shapes. " "Depth Solid and Depth Balloon always generate normal maps." ), default=False, ) def cutout_alpha_threshold_property(): return bpy.props.FloatProperty( name="Alpha Threshold", description=( "Exclude areas below this opacity from the Cutout outline. " "Higher values exclude more semi-transparent edges." ), default=0.9, min=0.0, max=1.0, soft_min=0.1, soft_max=0.9, subtype="FACTOR", ) def cutout_fine_outline_property(): return bpy.props.BoolProperty( name="Fine Outline", description="Fit detailed outlines and support thin parts; disable for simpler, faster meshes", default=False, ) def cutout_mesh_detail_property(): return bpy.props.EnumProperty( name="Mesh Detail", description="Control mesh density. Higher levels use more vertices to represent finer shapes.", items=( ("LOW", "Low", "32 source image pixels between neighboring mesh points"), ("MEDIUM", "Medium", "16 source image pixels between neighboring mesh points"), ("HIGH", "High", "8 source image pixels between neighboring mesh points"), ("ULTRA", "Ultra", "4 source image pixels between neighboring mesh points"), ), default="LOW", ) class AnyImageSettings(bpy.types.PropertyGroup): cutout_gesture: cutout_gesture_property() cutout_alpha_threshold: cutout_alpha_threshold_property() cutout_mesh_detail: cutout_mesh_detail_property() cutout_fine_outline: cutout_fine_outline_property() cutout_generate_normal: cutout_generate_normal_property() mask_gesture: mask_gesture_property() mask_mode: mask_mode_property() mask_radius: mask_radius_property()