Stop known-failing rig / retarget combinations before any request
Every option was re-run on OV and CN production (2026-10-15). Rig v1.0 rigs bipeds only and fails with unity / unreal / vrm; v2.5 rejects humanoid presets; v3.0 rejects quadruped / hexapod / octopod presets. The Rig and Retarget nodes now stop with an explanation for those combinations (Rig passes its effective version downstream in MODEL_INFO), and new Rig nodes default to v3.0-20260909, which rigs every type and runs every humanoid preset. Remove the Generate option v2.0-20240919: API v3 rejects it. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+9
-5
@@ -8,8 +8,9 @@ The Tripo v2 API is shut down on 2026-10-31. This release runs every node on Tri
|
||||
### Compatibility
|
||||
|
||||
- Requires `tripo3d>=1.0.0,<2.0.0` (pinned in both `requirements.txt` and `pyproject.toml`).
|
||||
- Saved workflows keep loading: no input name, input order or existing option was changed or removed;
|
||||
new inputs were added at the end with defaults that keep the old behaviour. No node was removed.
|
||||
- Saved workflows keep loading: no node, input name or input order was changed or removed; new inputs
|
||||
were added at the end with defaults that keep the old behaviour. The only removed option is Generate
|
||||
model `v2.0-20240919`, which API v3 rejects (a saved workflow using it is refused before any request).
|
||||
- If an incompatible `tripo3d` is installed, ComfyUI still starts; the log and every Tripo node report
|
||||
the `pip install "tripo3d>=1.0.0,<2.0.0"` command to run.
|
||||
- 1.3.x of this extension also keeps working on `tripo3d` 1.x until you update it.
|
||||
@@ -20,7 +21,8 @@ The Tripo v2 API is shut down on 2026-10-31. This release runs every node on Tri
|
||||
- Generate model: `P1-20260311`, `P2-20260801`; `export_orientation`.
|
||||
- Texture model: `v3.5-20260815`, `texture_quality=fast`, `delight` (auto / on / off).
|
||||
- Rig model: `v2.5-20260210`, `v3.0-20260909`; specs `actorcore`, `unity`, `unreal`, `vrm`;
|
||||
optional `rig_type` (auto runs the rig check first, as before).
|
||||
optional `rig_type` (auto runs the rig check first, as before). `unity` / `unreal` / `vrm` need
|
||||
`v2.5-20260210` or `v3.0-20260909`.
|
||||
- Smart low poly: `P-v3.0-20260909` and `smart_uv`; `face_limit <= 0` uses the server default.
|
||||
- Complete mesh: `completion_mode` (`ai_completion`, `quick_cap`).
|
||||
- Convert model: `fbx_preset=bake_scale`.
|
||||
@@ -28,8 +30,10 @@ The Tripo v2 API is shut down on 2026-10-31. This release runs every node on Tri
|
||||
### Changed
|
||||
|
||||
- Import model sends `model_url` to the API instead of downloading it inside ComfyUI first.
|
||||
- Rig model `v2.0-20250506` runs as `v2.5-20260210` (same as API v2). v2.5 rigs only accept animal
|
||||
presets; new Rig nodes default to `v1.0-20240301`, which accepts the humanoid presets.
|
||||
- Rig model `v2.0-20250506` runs as `v2.5-20260210` (same as API v2). New Rig nodes default to
|
||||
`v3.0-20260909`. Combinations that fail upstream (v1.0 with a non-biped model; v2.5 with humanoid
|
||||
presets; v3.0 with quadruped / hexapod / octopod presets; unity / unreal / vrm on v1.0) now stop with
|
||||
an explanation before any request. Rig passes its version downstream in `MODEL_INFO`.
|
||||
- `bake_animation` on Retarget is kept for saved workflows but ignored by API v3.
|
||||
|
||||
### Deprecated
|
||||
|
||||
@@ -40,9 +40,16 @@ generated model is usually much larger, so connect it after **Tripo: Smart low p
|
||||
`Generate model → Smart low poly → Smart UV → Preview 3D / Preview Image`.
|
||||
|
||||
### Rigging notes
|
||||
`v2.0-20250506` runs as `v2.5-20260210`, exactly as on API v2. v2.5 rigs only accept the animal presets
|
||||
(`preset:quadruped:walk`, `preset:hexapod:walk`, `preset:octopod:walk`, `preset:serpentine:march`,
|
||||
`preset:aquatic:march`); use `v1.0-20240301` (the default for new nodes) for humanoid presets.
|
||||
Measured on the Tripo API (2026-10-15). New Rig nodes default to `v3.0-20260909`.
|
||||
|
||||
| Rig version | Skeleton types it can rig | Humanoid presets | serpentine / aquatic presets | quadruped / hexapod / octopod presets |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `v3.0-20260909` | all | ✓ | ✓ | ✗ |
|
||||
| `v2.5-20260210` (and `v2.0-20250506`) | all | ✗ | ✓ | ✓ |
|
||||
| `v1.0-20240301` | biped only | ✓ | — | — |
|
||||
|
||||
`unity`, `unreal` and `vrm` specs need `v2.5-20260210` or `v3.0-20260909`. The Rig and Retarget nodes stop
|
||||
with an explanation, before sending anything, when a combination in the table is known to fail.
|
||||
|
||||
## Installation
|
||||
### [method1] From Source
|
||||
|
||||
+53
-11
@@ -153,7 +153,7 @@ class TripoAPIDraft:
|
||||
"image_left": ("IMAGE",),
|
||||
"image_back": ("IMAGE",),
|
||||
"image_right": ("IMAGE",),
|
||||
"model_version": (["v1.4-20240625", "v2.0-20240919", "v2.5-20250123", "v3.0-20250812", "v3.1-20260211", "P1-20260311", "P2-20260801"], {"default": "v3.1-20260211", "tooltip": "v2.0-20240919 is no longer available on Tripo API v3."}),
|
||||
"model_version": (["v1.4-20240625", "v2.5-20250123", "v3.0-20250812", "v3.1-20260211", "P1-20260311", "P2-20260801"], {"default": "v3.1-20260211"}),
|
||||
"texture": ("BOOLEAN", {"default": True}),
|
||||
"pbr": ("BOOLEAN", {"default": True}),
|
||||
"image_seed": ("INT", {"default": 42}),
|
||||
@@ -394,15 +394,40 @@ class TripoRefineModel:
|
||||
async def generate_mesh(self, model_info):
|
||||
raise RuntimeError(RETIRED_NODE_MESSAGE.format(name="Tripo: Refine Draft model"))
|
||||
|
||||
# Rig / retarget combinations measured on the live API (OV + CN, 2026-10-15). Combinations listed
|
||||
# here fail upstream, so the nodes stop before sending anything; everything else is passed through.
|
||||
SPECS_NEEDING_NEWER_RIG = {"unity", "unreal", "vrm"} # fail on rig v1.0-20240301
|
||||
RIG_VERSIONS_FOR_NEWER_SPECS = {"v2.0-20250506", "v2.5-20260210", "v3.0-20260909"}
|
||||
RIG_V1_BIPED_ONLY = "v1.0-20240301" # v1.0 rigs every non-biped type with 1004
|
||||
EFFECTIVE_RIG_VERSION = {"v2.0-20250506": "v2.5-20260210"} # what the SDK actually sends
|
||||
RETARGET_PRESETS_FAILING_ON_RIG = {
|
||||
"v2.5-20260210": "humanoid", # every preset without a skeleton prefix
|
||||
"v3.0-20260909": {"preset:quadruped:walk", "preset:hexapod:walk", "preset:octopod:walk"},
|
||||
}
|
||||
|
||||
|
||||
def _is_humanoid_preset(animation):
|
||||
return animation.startswith("preset:") and animation.count(":") == 1
|
||||
|
||||
|
||||
def retarget_blocked_reason(rig_version, animation):
|
||||
rule = RETARGET_PRESETS_FAILING_ON_RIG.get(rig_version)
|
||||
if rule == "humanoid" and _is_humanoid_preset(animation):
|
||||
return f"humanoid preset '{animation}' does not work on a {rig_version} rig; rig with v3.0-20260909 or v1.0-20240301"
|
||||
if isinstance(rule, set) and animation in rule:
|
||||
return f"preset '{animation}' does not work on a {rig_version} rig; rig this model with v2.5-20260210"
|
||||
return None
|
||||
|
||||
|
||||
class TripoAnimateRigNode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"model_info": ("MODEL_INFO",),
|
||||
"model_version": (["v1.0-20240301", "v2.0-20250506", "v2.5-20260210", "v3.0-20260909"], {"default": "v1.0-20240301", "tooltip": "v2.0-20250506 runs as v2.5-20260210. v2.5 rigs only retarget animal presets (quadruped/hexapod/octopod/serpentine/aquatic); use v1.0-20240301 for humanoid presets."}),
|
||||
"model_version": (["v1.0-20240301", "v2.0-20250506", "v2.5-20260210", "v3.0-20260909"], {"default": "v3.0-20260909", "tooltip": "v3.0: every skeleton type and every preset except quadruped/hexapod/octopod walk. v2.5: every skeleton type, animal presets only. v1.0: biped only. v2.0-20250506 runs as v2.5-20260210."}),
|
||||
"out_format": (["glb", "fbx"], {"default": "glb"}),
|
||||
"spec": (["mixamo", "tripo", "actorcore", "unity", "unreal", "vrm"], {"default": "tripo"}),
|
||||
"spec": (["mixamo", "tripo", "actorcore", "unity", "unreal", "vrm"], {"default": "tripo", "tooltip": "unity / unreal / vrm need model_version v2.5-20260210 or v3.0-20260909."}),
|
||||
},
|
||||
"optional": {
|
||||
"rig_type": (["auto", "biped", "quadruped", "hexapod", "octopod", "avian", "serpentine", "aquatic", "others"], {"default": "auto", "tooltip": "auto detects the skeleton with a rig check first."}),
|
||||
@@ -415,6 +440,12 @@ class TripoAnimateRigNode:
|
||||
CATEGORY = "TripoAPI"
|
||||
|
||||
async def generate_mesh(self, model_info, model_version, out_format, spec, rig_type="auto"):
|
||||
if spec in SPECS_NEEDING_NEWER_RIG and model_version not in RIG_VERSIONS_FOR_NEWER_SPECS:
|
||||
raise RuntimeError(f"spec '{spec}' needs model_version v2.5-20260210 or v3.0-20260909 "
|
||||
f"(got {model_version}); no request was sent.")
|
||||
if model_version == RIG_V1_BIPED_ONLY and rig_type not in (None, "", "auto", "biped"):
|
||||
raise RuntimeError(f"rig {RIG_V1_BIPED_ONLY} only supports biped models (rig_type '{rig_type}'); "
|
||||
"use v3.0-20260909 or v2.5-20260210. No request was sent.")
|
||||
client, key = await GetTripoAPI(model_info["apikey"])
|
||||
|
||||
if not rig_type or rig_type == "auto":
|
||||
@@ -430,6 +461,10 @@ class TripoAnimateRigNode:
|
||||
if not rig_type:
|
||||
raise RuntimeError("No suitable rig type found for the model")
|
||||
|
||||
if model_version == RIG_V1_BIPED_ONLY and rig_type != "biped":
|
||||
raise RuntimeError(f"rig {RIG_V1_BIPED_ONLY} only supports biped models (this one is '{rig_type}'); "
|
||||
"use v3.0-20260909 or v2.5-20260210. No rig task was sent.")
|
||||
|
||||
task_id = await client.rig_model(
|
||||
original_model_task_id=model_info["task_id"],
|
||||
out_format=out_format,
|
||||
@@ -447,7 +482,8 @@ class TripoAnimateRigNode:
|
||||
"task_id": task_id,
|
||||
"apikey": key,
|
||||
"file_prefix": model_info["file_prefix"],
|
||||
"output_directory": model_info["output_directory"]
|
||||
"output_directory": model_info["output_directory"],
|
||||
"rig_model_version": EFFECTIVE_RIG_VERSION.get(model_version, model_version),
|
||||
}
|
||||
else:
|
||||
raise RuntimeError(f"Failed to generate mesh: {task.error_code} {task.error_msg if hasattr(task, 'error_msg') else ''}")
|
||||
@@ -490,6 +526,10 @@ class TripoAnimateRetargetNode:
|
||||
CATEGORY = "TripoAPI"
|
||||
|
||||
async def generate_mesh(self, model_info, animation, out_format, bake_animation=True, export_with_geometry=False):
|
||||
rig_version = model_info.get("rig_model_version")
|
||||
blocked = retarget_blocked_reason(rig_version, animation) if rig_version else None
|
||||
if blocked:
|
||||
raise RuntimeError(blocked + ". No request was sent.")
|
||||
client, key = await GetTripoAPI(model_info["apikey"])
|
||||
|
||||
task_id = await client.retarget_animation(
|
||||
@@ -504,13 +544,15 @@ class TripoAnimateRetargetNode:
|
||||
downloaded = await client.download_task_models(task, get_output_directory())
|
||||
model_file = next(iter(downloaded.values()))
|
||||
print(f"model_file: {model_file}")
|
||||
return rename_model(model_file, model_info["file_prefix"], model_info["output_directory"]), \
|
||||
{
|
||||
"task_id": task_id,
|
||||
"apikey": key,
|
||||
"file_prefix": model_info["file_prefix"],
|
||||
"output_directory": model_info["output_directory"]
|
||||
}
|
||||
info = {
|
||||
"task_id": task_id,
|
||||
"apikey": key,
|
||||
"file_prefix": model_info["file_prefix"],
|
||||
"output_directory": model_info["output_directory"],
|
||||
}
|
||||
if rig_version:
|
||||
info["rig_model_version"] = rig_version
|
||||
return rename_model(model_file, model_info["file_prefix"], model_info["output_directory"]), info
|
||||
else:
|
||||
raise RuntimeError(f"Failed to generate mesh: {task.error_code} {task.error_msg if hasattr(task, 'error_msg') else ''}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user