mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-30 19:07:25 +08:00
[Partner Nodes] feat: add new nodes for Tripo3D P1 model
Signed-off-by: bigcat88 <bigcat88@icloud.com>
This commit is contained in:
parent
4af9a47227
commit
45ebdb2efa
@ -1,25 +1,25 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Optional, Any
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, RootModel
|
from pydantic import BaseModel, Field, RootModel
|
||||||
|
|
||||||
|
|
||||||
class TripoModelVersion(str, Enum):
|
class TripoModelVersion(str, Enum):
|
||||||
v3_1_20260211 = 'v3.1-20260211'
|
v3_1_20260211 = "v3.1-20260211"
|
||||||
v3_0_20250812 = 'v3.0-20250812'
|
v3_0_20250812 = "v3.0-20250812"
|
||||||
v2_5_20250123 = 'v2.5-20250123'
|
v2_5_20250123 = "v2.5-20250123"
|
||||||
v2_0_20240919 = 'v2.0-20240919'
|
v2_0_20240919 = "v2.0-20240919"
|
||||||
v1_4_20240625 = 'v1.4-20240625'
|
v1_4_20240625 = "v1.4-20240625"
|
||||||
|
|
||||||
|
|
||||||
class TripoGeometryQuality(str, Enum):
|
class TripoGeometryQuality(str, Enum):
|
||||||
standard = 'standard'
|
standard = "standard"
|
||||||
detailed = 'detailed'
|
detailed = "detailed"
|
||||||
|
|
||||||
|
|
||||||
class TripoTextureQuality(str, Enum):
|
class TripoTextureQuality(str, Enum):
|
||||||
standard = 'standard'
|
standard = "standard"
|
||||||
detailed = 'detailed'
|
detailed = "detailed"
|
||||||
|
|
||||||
|
|
||||||
class TripoStyle(str, Enum):
|
class TripoStyle(str, Enum):
|
||||||
@ -33,6 +33,7 @@ class TripoStyle(str, Enum):
|
|||||||
ANCIENT_BRONZE = "ancient_bronze"
|
ANCIENT_BRONZE = "ancient_bronze"
|
||||||
NONE = "None"
|
NONE = "None"
|
||||||
|
|
||||||
|
|
||||||
class TripoTaskType(str, Enum):
|
class TripoTaskType(str, Enum):
|
||||||
TEXT_TO_MODEL = "text_to_model"
|
TEXT_TO_MODEL = "text_to_model"
|
||||||
IMAGE_TO_MODEL = "image_to_model"
|
IMAGE_TO_MODEL = "image_to_model"
|
||||||
@ -45,26 +46,27 @@ class TripoTaskType(str, Enum):
|
|||||||
STYLIZE_MODEL = "stylize_model"
|
STYLIZE_MODEL = "stylize_model"
|
||||||
CONVERT_MODEL = "convert_model"
|
CONVERT_MODEL = "convert_model"
|
||||||
|
|
||||||
|
|
||||||
class TripoTextureAlignment(str, Enum):
|
class TripoTextureAlignment(str, Enum):
|
||||||
ORIGINAL_IMAGE = "original_image"
|
ORIGINAL_IMAGE = "original_image"
|
||||||
GEOMETRY = "geometry"
|
GEOMETRY = "geometry"
|
||||||
|
|
||||||
|
|
||||||
class TripoOrientation(str, Enum):
|
class TripoOrientation(str, Enum):
|
||||||
ALIGN_IMAGE = "align_image"
|
ALIGN_IMAGE = "align_image"
|
||||||
DEFAULT = "default"
|
DEFAULT = "default"
|
||||||
|
|
||||||
|
|
||||||
class TripoOutFormat(str, Enum):
|
class TripoOutFormat(str, Enum):
|
||||||
GLB = "glb"
|
GLB = "glb"
|
||||||
FBX = "fbx"
|
FBX = "fbx"
|
||||||
|
|
||||||
class TripoTopology(str, Enum):
|
|
||||||
BIP = "bip"
|
|
||||||
QUAD = "quad"
|
|
||||||
|
|
||||||
class TripoSpec(str, Enum):
|
class TripoSpec(str, Enum):
|
||||||
MIXAMO = "mixamo"
|
MIXAMO = "mixamo"
|
||||||
TRIPO = "tripo"
|
TRIPO = "tripo"
|
||||||
|
|
||||||
|
|
||||||
class TripoAnimation(str, Enum):
|
class TripoAnimation(str, Enum):
|
||||||
IDLE = "preset:idle"
|
IDLE = "preset:idle"
|
||||||
WALK = "preset:walk"
|
WALK = "preset:walk"
|
||||||
@ -83,11 +85,6 @@ class TripoAnimation(str, Enum):
|
|||||||
SERPENTINE_MARCH = "preset:serpentine:march"
|
SERPENTINE_MARCH = "preset:serpentine:march"
|
||||||
AQUATIC_MARCH = "preset:aquatic:march"
|
AQUATIC_MARCH = "preset:aquatic:march"
|
||||||
|
|
||||||
class TripoStylizeStyle(str, Enum):
|
|
||||||
LEGO = "lego"
|
|
||||||
VOXEL = "voxel"
|
|
||||||
VORONOI = "voronoi"
|
|
||||||
MINECRAFT = "minecraft"
|
|
||||||
|
|
||||||
class TripoConvertFormat(str, Enum):
|
class TripoConvertFormat(str, Enum):
|
||||||
GLTF = "GLTF"
|
GLTF = "GLTF"
|
||||||
@ -97,6 +94,7 @@ class TripoConvertFormat(str, Enum):
|
|||||||
STL = "STL"
|
STL = "STL"
|
||||||
_3MF = "3MF"
|
_3MF = "3MF"
|
||||||
|
|
||||||
|
|
||||||
class TripoTextureFormat(str, Enum):
|
class TripoTextureFormat(str, Enum):
|
||||||
BMP = "BMP"
|
BMP = "BMP"
|
||||||
DPX = "DPX"
|
DPX = "DPX"
|
||||||
@ -108,6 +106,7 @@ class TripoTextureFormat(str, Enum):
|
|||||||
TIFF = "TIFF"
|
TIFF = "TIFF"
|
||||||
WEBP = "WEBP"
|
WEBP = "WEBP"
|
||||||
|
|
||||||
|
|
||||||
class TripoTaskStatus(str, Enum):
|
class TripoTaskStatus(str, Enum):
|
||||||
QUEUED = "queued"
|
QUEUED = "queued"
|
||||||
RUNNING = "running"
|
RUNNING = "running"
|
||||||
@ -118,183 +117,223 @@ class TripoTaskStatus(str, Enum):
|
|||||||
BANNED = "banned"
|
BANNED = "banned"
|
||||||
EXPIRED = "expired"
|
EXPIRED = "expired"
|
||||||
|
|
||||||
|
|
||||||
class TripoFbxPreset(str, Enum):
|
class TripoFbxPreset(str, Enum):
|
||||||
BLENDER = "blender"
|
BLENDER = "blender"
|
||||||
MIXAMO = "mixamo"
|
MIXAMO = "mixamo"
|
||||||
_3DSMAX = "3dsmax"
|
_3DSMAX = "3dsmax"
|
||||||
|
|
||||||
|
|
||||||
class TripoFileTokenReference(BaseModel):
|
class TripoFileTokenReference(BaseModel):
|
||||||
type: Optional[str] = Field(None, description='The type of the reference')
|
type: str | None = Field(None, description="The type of the reference")
|
||||||
file_token: str
|
file_token: str
|
||||||
|
|
||||||
|
|
||||||
class TripoUrlReference(BaseModel):
|
class TripoUrlReference(BaseModel):
|
||||||
type: Optional[str] = Field(None, description='The type of the reference')
|
type: str | None = Field(None, description="The type of the reference")
|
||||||
url: str
|
url: str
|
||||||
|
|
||||||
|
|
||||||
class TripoObjectStorage(BaseModel):
|
class TripoObjectStorage(BaseModel):
|
||||||
bucket: str
|
bucket: str
|
||||||
key: str
|
key: str
|
||||||
|
|
||||||
|
|
||||||
class TripoObjectReference(BaseModel):
|
class TripoObjectReference(BaseModel):
|
||||||
type: str
|
type: str
|
||||||
object: TripoObjectStorage
|
object: TripoObjectStorage
|
||||||
|
|
||||||
|
|
||||||
class TripoFileEmptyReference(BaseModel):
|
class TripoFileEmptyReference(BaseModel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TripoFileReference(RootModel):
|
class TripoFileReference(RootModel):
|
||||||
root: TripoFileTokenReference | TripoUrlReference | TripoObjectReference | TripoFileEmptyReference
|
root: TripoFileTokenReference | TripoUrlReference | TripoObjectReference | TripoFileEmptyReference
|
||||||
|
|
||||||
class TripoGetStsTokenRequest(BaseModel):
|
|
||||||
format: str = Field(..., description='The format of the image')
|
|
||||||
|
|
||||||
class TripoTextToModelRequest(BaseModel):
|
class TripoTextToModelRequest(BaseModel):
|
||||||
type: TripoTaskType = Field(TripoTaskType.TEXT_TO_MODEL, description='Type of task')
|
type: TripoTaskType = Field(TripoTaskType.TEXT_TO_MODEL, description="Type of task")
|
||||||
prompt: str = Field(..., description='The text prompt describing the model to generate', max_length=1024)
|
prompt: str = Field(..., description="The text prompt describing the model to generate", max_length=1024)
|
||||||
negative_prompt: Optional[str] = Field(None, description='The negative text prompt', max_length=1024)
|
negative_prompt: str | None = Field(None, description="The negative text prompt", max_length=1024)
|
||||||
model_version: Optional[TripoModelVersion] = TripoModelVersion.v2_5_20250123
|
model_version: TripoModelVersion | None = TripoModelVersion.v2_5_20250123
|
||||||
face_limit: Optional[int] = Field(None, description='The number of faces to limit the generation to')
|
face_limit: int | None = Field(None, description="The number of faces to limit the generation to")
|
||||||
texture: Optional[bool] = Field(True, description='Whether to apply texture to the generated model')
|
texture: bool | None = Field(True, description="Whether to apply texture to the generated model")
|
||||||
pbr: Optional[bool] = Field(True, description='Whether to apply PBR to the generated model')
|
pbr: bool | None = Field(True, description="Whether to apply PBR to the generated model")
|
||||||
image_seed: Optional[int] = Field(None, description='The seed for the text')
|
image_seed: int | None = Field(None, description="The seed for the text")
|
||||||
model_seed: Optional[int] = Field(None, description='The seed for the model')
|
model_seed: int | None = Field(None, description="The seed for the model")
|
||||||
texture_seed: Optional[int] = Field(None, description='The seed for the texture')
|
texture_seed: int | None = Field(None, description="The seed for the texture")
|
||||||
texture_quality: Optional[TripoTextureQuality] = TripoTextureQuality.standard
|
texture_quality: TripoTextureQuality | None = TripoTextureQuality.standard
|
||||||
geometry_quality: Optional[TripoGeometryQuality] = TripoGeometryQuality.standard
|
geometry_quality: TripoGeometryQuality | None = TripoGeometryQuality.standard
|
||||||
style: Optional[TripoStyle] = None
|
style: TripoStyle | None = None
|
||||||
auto_size: Optional[bool] = Field(False, description='Whether to auto-size the model')
|
auto_size: bool | None = Field(False, description="Whether to auto-size the model")
|
||||||
quad: Optional[bool] = Field(False, description='Whether to apply quad to the generated model')
|
quad: bool | None = Field(False, description="Whether to apply quad to the generated model")
|
||||||
|
|
||||||
|
|
||||||
class TripoImageToModelRequest(BaseModel):
|
class TripoImageToModelRequest(BaseModel):
|
||||||
type: TripoTaskType = Field(TripoTaskType.IMAGE_TO_MODEL, description='Type of task')
|
type: TripoTaskType = Field(TripoTaskType.IMAGE_TO_MODEL, description="Type of task")
|
||||||
file: TripoFileReference = Field(..., description='The file reference to convert to a model')
|
file: TripoFileReference = Field(..., description="The file reference to convert to a model")
|
||||||
model_version: Optional[TripoModelVersion] = Field(None, description='The model version to use for generation')
|
model_version: TripoModelVersion | None = Field(None, description="The model version to use for generation")
|
||||||
face_limit: Optional[int] = Field(None, description='The number of faces to limit the generation to')
|
face_limit: int | None = Field(None, description="The number of faces to limit the generation to")
|
||||||
texture: Optional[bool] = Field(True, description='Whether to apply texture to the generated model')
|
texture: bool | None = Field(True, description="Whether to apply texture to the generated model")
|
||||||
pbr: Optional[bool] = Field(True, description='Whether to apply PBR to the generated model')
|
pbr: bool | None = Field(True, description="Whether to apply PBR to the generated model")
|
||||||
model_seed: Optional[int] = Field(None, description='The seed for the model')
|
model_seed: int | None = Field(None, description="The seed for the model")
|
||||||
texture_seed: Optional[int] = Field(None, description='The seed for the texture')
|
texture_seed: int | None = Field(None, description="The seed for the texture")
|
||||||
texture_quality: Optional[TripoTextureQuality] = TripoTextureQuality.standard
|
texture_quality: TripoTextureQuality | None = TripoTextureQuality.standard
|
||||||
geometry_quality: Optional[TripoGeometryQuality] = TripoGeometryQuality.standard
|
geometry_quality: TripoGeometryQuality | None = TripoGeometryQuality.standard
|
||||||
texture_alignment: Optional[TripoTextureAlignment] = Field(TripoTextureAlignment.ORIGINAL_IMAGE, description='The texture alignment method')
|
texture_alignment: TripoTextureAlignment | None = Field(
|
||||||
style: Optional[TripoStyle] = Field(None, description='The style to apply to the generated model')
|
TripoTextureAlignment.ORIGINAL_IMAGE, description="The texture alignment method"
|
||||||
auto_size: Optional[bool] = Field(False, description='Whether to auto-size the model')
|
)
|
||||||
orientation: Optional[TripoOrientation] = TripoOrientation.DEFAULT
|
style: TripoStyle | None = Field(None, description="The style to apply to the generated model")
|
||||||
quad: Optional[bool] = Field(False, description='Whether to apply quad to the generated model')
|
auto_size: bool | None = Field(False, description="Whether to auto-size the model")
|
||||||
|
orientation: TripoOrientation | None = TripoOrientation.DEFAULT
|
||||||
|
quad: bool | None = Field(False, description="Whether to apply quad to the generated model")
|
||||||
|
|
||||||
|
|
||||||
class TripoMultiviewToModelRequest(BaseModel):
|
class TripoMultiviewToModelRequest(BaseModel):
|
||||||
type: TripoTaskType = TripoTaskType.MULTIVIEW_TO_MODEL
|
type: TripoTaskType = TripoTaskType.MULTIVIEW_TO_MODEL
|
||||||
files: list[TripoFileReference] = Field(..., description='The file references to convert to a model')
|
files: list[TripoFileReference] = Field(..., description="The file references to convert to a model")
|
||||||
model_version: Optional[TripoModelVersion] = Field(None, description='The model version to use for generation')
|
model_version: TripoModelVersion | None = Field(None, description="The model version to use for generation")
|
||||||
orthographic_projection: Optional[bool] = Field(False, description='Whether to use orthographic projection')
|
orthographic_projection: bool | None = Field(False, description="Whether to use orthographic projection")
|
||||||
face_limit: Optional[int] = Field(None, description='The number of faces to limit the generation to')
|
face_limit: int | None = Field(None, description="The number of faces to limit the generation to")
|
||||||
texture: Optional[bool] = Field(True, description='Whether to apply texture to the generated model')
|
texture: bool | None = Field(True, description="Whether to apply texture to the generated model")
|
||||||
pbr: Optional[bool] = Field(True, description='Whether to apply PBR to the generated model')
|
pbr: bool | None = Field(True, description="Whether to apply PBR to the generated model")
|
||||||
model_seed: Optional[int] = Field(None, description='The seed for the model')
|
model_seed: int | None = Field(None, description="The seed for the model")
|
||||||
texture_seed: Optional[int] = Field(None, description='The seed for the texture')
|
texture_seed: int | None = Field(None, description="The seed for the texture")
|
||||||
texture_quality: Optional[TripoTextureQuality] = TripoTextureQuality.standard
|
texture_quality: TripoTextureQuality | None = TripoTextureQuality.standard
|
||||||
geometry_quality: Optional[TripoGeometryQuality] = TripoGeometryQuality.standard
|
geometry_quality: TripoGeometryQuality | None = TripoGeometryQuality.standard
|
||||||
texture_alignment: Optional[TripoTextureAlignment] = TripoTextureAlignment.ORIGINAL_IMAGE
|
texture_alignment: TripoTextureAlignment | None = TripoTextureAlignment.ORIGINAL_IMAGE
|
||||||
auto_size: Optional[bool] = Field(False, description='Whether to auto-size the model')
|
auto_size: bool | None = Field(False, description="Whether to auto-size the model")
|
||||||
orientation: Optional[TripoOrientation] = Field(TripoOrientation.DEFAULT, description='The orientation for the model')
|
orientation: TripoOrientation | None = Field(TripoOrientation.DEFAULT, description="The orientation for the model")
|
||||||
quad: Optional[bool] = Field(False, description='Whether to apply quad to the generated model')
|
quad: bool | None = Field(False, description="Whether to apply quad to the generated model")
|
||||||
|
|
||||||
|
|
||||||
class TripoTextureModelRequest(BaseModel):
|
class TripoTextureModelRequest(BaseModel):
|
||||||
type: TripoTaskType = Field(TripoTaskType.TEXTURE_MODEL, description='Type of task')
|
type: TripoTaskType = Field(TripoTaskType.TEXTURE_MODEL, description="Type of task")
|
||||||
original_model_task_id: str = Field(..., description='The task ID of the original model')
|
original_model_task_id: str = Field(..., description="The task ID of the original model")
|
||||||
texture: Optional[bool] = Field(True, description='Whether to apply texture to the model')
|
texture: bool | None = Field(True, description="Whether to apply texture to the model")
|
||||||
pbr: Optional[bool] = Field(True, description='Whether to apply PBR to the model')
|
pbr: bool | None = Field(True, description="Whether to apply PBR to the model")
|
||||||
model_seed: Optional[int] = Field(None, description='The seed for the model')
|
model_seed: int | None = Field(None, description="The seed for the model")
|
||||||
texture_seed: Optional[int] = Field(None, description='The seed for the texture')
|
texture_seed: int | None = Field(None, description="The seed for the texture")
|
||||||
texture_quality: Optional[TripoTextureQuality] = Field(None, description='The quality of the texture')
|
texture_quality: TripoTextureQuality | None = Field(None, description="The quality of the texture")
|
||||||
texture_alignment: Optional[TripoTextureAlignment] = Field(TripoTextureAlignment.ORIGINAL_IMAGE, description='The texture alignment method')
|
texture_alignment: TripoTextureAlignment | None = Field(
|
||||||
|
TripoTextureAlignment.ORIGINAL_IMAGE, description="The texture alignment method"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TripoRefineModelRequest(BaseModel):
|
class TripoRefineModelRequest(BaseModel):
|
||||||
type: TripoTaskType = Field(TripoTaskType.REFINE_MODEL, description='Type of task')
|
type: TripoTaskType = Field(TripoTaskType.REFINE_MODEL, description="Type of task")
|
||||||
draft_model_task_id: str = Field(..., description='The task ID of the draft model')
|
draft_model_task_id: str = Field(..., description="The task ID of the draft model")
|
||||||
|
|
||||||
class TripoAnimatePrerigcheckRequest(BaseModel):
|
|
||||||
type: TripoTaskType = Field(TripoTaskType.ANIMATE_PRERIGCHECK, description='Type of task')
|
|
||||||
original_model_task_id: str = Field(..., description='The task ID of the original model')
|
|
||||||
|
|
||||||
class TripoAnimateRigRequest(BaseModel):
|
class TripoAnimateRigRequest(BaseModel):
|
||||||
type: TripoTaskType = Field(TripoTaskType.ANIMATE_RIG, description='Type of task')
|
type: TripoTaskType = Field(TripoTaskType.ANIMATE_RIG, description="Type of task")
|
||||||
original_model_task_id: str = Field(..., description='The task ID of the original model')
|
original_model_task_id: str = Field(..., description="The task ID of the original model")
|
||||||
out_format: Optional[TripoOutFormat] = Field(TripoOutFormat.GLB, description='The output format')
|
out_format: TripoOutFormat | None = Field(TripoOutFormat.GLB, description="The output format")
|
||||||
spec: Optional[TripoSpec] = Field(TripoSpec.TRIPO, description='The specification for rigging')
|
spec: TripoSpec | None = Field(TripoSpec.TRIPO, description="The specification for rigging")
|
||||||
|
|
||||||
|
|
||||||
class TripoAnimateRetargetRequest(BaseModel):
|
class TripoAnimateRetargetRequest(BaseModel):
|
||||||
type: TripoTaskType = Field(TripoTaskType.ANIMATE_RETARGET, description='Type of task')
|
type: TripoTaskType = Field(TripoTaskType.ANIMATE_RETARGET, description="Type of task")
|
||||||
original_model_task_id: str = Field(..., description='The task ID of the original model')
|
original_model_task_id: str = Field(..., description="The task ID of the original model")
|
||||||
animation: TripoAnimation = Field(..., description='The animation to apply')
|
animation: TripoAnimation = Field(..., description="The animation to apply")
|
||||||
out_format: Optional[TripoOutFormat] = Field(TripoOutFormat.GLB, description='The output format')
|
out_format: TripoOutFormat | None = Field(TripoOutFormat.GLB, description="The output format")
|
||||||
bake_animation: Optional[bool] = Field(True, description='Whether to bake the animation')
|
bake_animation: bool | None = Field(True, description="Whether to bake the animation")
|
||||||
|
|
||||||
class TripoStylizeModelRequest(BaseModel):
|
|
||||||
type: TripoTaskType = Field(TripoTaskType.STYLIZE_MODEL, description='Type of task')
|
|
||||||
style: TripoStylizeStyle = Field(..., description='The style to apply to the model')
|
|
||||||
original_model_task_id: str = Field(..., description='The task ID of the original model')
|
|
||||||
block_size: Optional[int] = Field(80, description='The block size for stylization')
|
|
||||||
|
|
||||||
class TripoConvertModelRequest(BaseModel):
|
class TripoConvertModelRequest(BaseModel):
|
||||||
type: TripoTaskType = Field(TripoTaskType.CONVERT_MODEL, description='Type of task')
|
type: TripoTaskType = Field(TripoTaskType.CONVERT_MODEL, description="Type of task")
|
||||||
format: TripoConvertFormat = Field(..., description='The format to convert to')
|
format: TripoConvertFormat = Field(..., description="The format to convert to")
|
||||||
original_model_task_id: str = Field(..., description='The task ID of the original model')
|
original_model_task_id: str = Field(..., description="The task ID of the original model")
|
||||||
quad: Optional[bool] = Field(None, description='Whether to apply quad to the model')
|
quad: bool | None = Field(None, description="Whether to apply quad to the model")
|
||||||
force_symmetry: Optional[bool] = Field(None, description='Whether to force symmetry')
|
force_symmetry: bool | None = Field(None, description="Whether to force symmetry")
|
||||||
face_limit: Optional[int] = Field(None, description='The number of faces to limit the conversion to')
|
face_limit: int | None = Field(None, description="The number of faces to limit the conversion to")
|
||||||
flatten_bottom: Optional[bool] = Field(None, description='Whether to flatten the bottom of the model')
|
flatten_bottom: bool | None = Field(None, description="Whether to flatten the bottom of the model")
|
||||||
flatten_bottom_threshold: Optional[float] = Field(None, description='The threshold for flattening the bottom')
|
flatten_bottom_threshold: float | None = Field(None, description="The threshold for flattening the bottom")
|
||||||
texture_size: Optional[int] = Field(None, description='The size of the texture')
|
texture_size: int | None = Field(None, description="The size of the texture")
|
||||||
texture_format: Optional[TripoTextureFormat] = Field(TripoTextureFormat.JPEG, description='The format of the texture')
|
texture_format: TripoTextureFormat | None = Field(TripoTextureFormat.JPEG, description="The format of the texture")
|
||||||
pivot_to_center_bottom: Optional[bool] = Field(None, description='Whether to pivot to the center bottom')
|
pivot_to_center_bottom: bool | None = Field(None, description="Whether to pivot to the center bottom")
|
||||||
scale_factor: Optional[float] = Field(None, description='The scale factor for the model')
|
scale_factor: float | None = Field(None, description="The scale factor for the model")
|
||||||
with_animation: Optional[bool] = Field(None, description='Whether to include animations')
|
with_animation: bool | None = Field(None, description="Whether to include animations")
|
||||||
pack_uv: Optional[bool] = Field(None, description='Whether to pack the UVs')
|
pack_uv: bool | None = Field(None, description="Whether to pack the UVs")
|
||||||
bake: Optional[bool] = Field(None, description='Whether to bake the model')
|
bake: bool | None = Field(None, description="Whether to bake the model")
|
||||||
part_names: Optional[list[str]] = Field(None, description='The names of the parts to include')
|
part_names: list[str] | None = Field(None, description="The names of the parts to include")
|
||||||
fbx_preset: Optional[TripoFbxPreset] = Field(None, description='The preset for the FBX export')
|
fbx_preset: TripoFbxPreset | None = Field(None, description="The preset for the FBX export")
|
||||||
export_vertex_colors: Optional[bool] = Field(None, description='Whether to export the vertex colors')
|
export_vertex_colors: bool | None = Field(None, description="Whether to export the vertex colors")
|
||||||
export_orientation: Optional[TripoOrientation] = Field(None, description='The orientation for the export')
|
export_orientation: TripoOrientation | None = Field(None, description="The orientation for the export")
|
||||||
animate_in_place: Optional[bool] = Field(None, description='Whether to animate in place')
|
animate_in_place: bool | None = Field(None, description="Whether to animate in place")
|
||||||
|
|
||||||
|
|
||||||
|
class TripoP1CommonRequest(BaseModel):
|
||||||
|
"""Fields supported by Tripo P1 across all input types."""
|
||||||
|
|
||||||
|
model_version: str = Field("P1-20260311")
|
||||||
|
model_seed: int | None = Field(None, description="Random seed for geometry generation")
|
||||||
|
face_limit: int | None = Field(None, ge=48, le=20000, description="Target face count (48-20000)")
|
||||||
|
texture: bool | None = Field(None, description="Enable texturing; pbr=True forces this true")
|
||||||
|
pbr: bool | None = Field(None, description="Enable PBR maps; when true, texture is also enabled")
|
||||||
|
texture_seed: int | None = Field(None, description="Random seed for texture generation")
|
||||||
|
texture_quality: str | None = Field(None, description='"standard" or "detailed"')
|
||||||
|
auto_size: bool | None = Field(None, description="Scale to real-world meters")
|
||||||
|
compress: str | None = Field(None, description='Only "geometry" is supported')
|
||||||
|
export_uv: bool | None = Field(None, description="Perform UV unwrapping during generation")
|
||||||
|
|
||||||
|
|
||||||
|
class TripoP1TextToModelRequest(TripoP1CommonRequest):
|
||||||
|
type: str = "text_to_model"
|
||||||
|
prompt: str = Field(..., max_length=1024)
|
||||||
|
negative_prompt: str | None = Field(None, max_length=255)
|
||||||
|
image_seed: int | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class TripoP1ImageToModelRequest(TripoP1CommonRequest):
|
||||||
|
type: str = "image_to_model"
|
||||||
|
file: TripoFileReference
|
||||||
|
enable_image_autofix: bool | None = None
|
||||||
|
texture_alignment: str | None = Field(None, description='"original_image" or "geometry"')
|
||||||
|
orientation: str | None = Field(None, description='"default" or "align_image"; needs texture=true')
|
||||||
|
|
||||||
|
|
||||||
|
class TripoP1MultiviewToModelRequest(TripoP1CommonRequest):
|
||||||
|
"""P1 multiview generation.
|
||||||
|
|
||||||
|
Tripo requires `files` to be exactly four entries in [front, left, back, right] order with `{}`
|
||||||
|
(TripoFileEmptyReference) for omitted slots; front is required and at least two images total must be provided.
|
||||||
|
"""
|
||||||
|
|
||||||
|
type: str = "multiview_to_model"
|
||||||
|
files: list[TripoFileReference]
|
||||||
|
texture_alignment: str | None = None
|
||||||
|
orientation: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class TripoTaskOutput(BaseModel):
|
class TripoTaskOutput(BaseModel):
|
||||||
model: Optional[str] = Field(None, description='URL to the model')
|
model: str | None = Field(None, description="URL to the model")
|
||||||
base_model: Optional[str] = Field(None, description='URL to the base model')
|
base_model: str | None = Field(None, description="URL to the base model")
|
||||||
pbr_model: Optional[str] = Field(None, description='URL to the PBR model')
|
pbr_model: str | None = Field(None, description="URL to the PBR model")
|
||||||
rendered_image: Optional[str] = Field(None, description='URL to the rendered image')
|
rendered_image: str | None = Field(None, description="URL to the rendered image")
|
||||||
riggable: Optional[bool] = Field(None, description='Whether the model is riggable')
|
riggable: bool | None = Field(None, description="Whether the model is riggable")
|
||||||
|
|
||||||
|
|
||||||
class TripoTask(BaseModel):
|
class TripoTask(BaseModel):
|
||||||
task_id: str = Field(..., description='The task ID')
|
task_id: str = Field(..., description="The task ID")
|
||||||
type: Optional[str] = Field(None, description='The type of task')
|
type: str | None = Field(None, description="The type of task")
|
||||||
status: Optional[TripoTaskStatus] = Field(None, description='The status of the task')
|
status: TripoTaskStatus | None = Field(None, description="The status of the task")
|
||||||
input: Optional[dict[str, Any]] = Field(None, description='The input parameters for the task')
|
input: dict[str, Any] | None = Field(None, description="The input parameters for the task")
|
||||||
output: Optional[TripoTaskOutput] = Field(None, description='The output of the task')
|
output: TripoTaskOutput | None = Field(None, description="The output of the task")
|
||||||
progress: Optional[int] = Field(None, description='The progress of the task', ge=0, le=100)
|
progress: int | None = Field(None, description="The progress of the task", ge=0, le=100)
|
||||||
create_time: Optional[int] = Field(None, description='The creation time of the task')
|
create_time: int | None = Field(None, description="The creation time of the task")
|
||||||
running_left_time: Optional[int] = Field(None, description='The estimated time left for the task')
|
running_left_time: int | None = Field(None, description="The estimated time left for the task")
|
||||||
queue_position: Optional[int] = Field(None, description='The position in the queue')
|
queue_position: int | None = Field(None, description="The position in the queue")
|
||||||
consumed_credit: int | None = Field(None)
|
consumed_credit: int | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
class TripoTaskResponse(BaseModel):
|
class TripoTaskResponse(BaseModel):
|
||||||
code: int = Field(0, description='The response code')
|
code: int = Field(0, description="The response code")
|
||||||
data: TripoTask = Field(..., description='The task data')
|
data: TripoTask = Field(..., description="The task data")
|
||||||
|
|
||||||
class TripoGeneralResponse(BaseModel):
|
|
||||||
code: int = Field(0, description='The response code')
|
|
||||||
data: dict[str, str] = Field(..., description='The task ID data')
|
|
||||||
|
|
||||||
class TripoBalanceData(BaseModel):
|
|
||||||
balance: float = Field(..., description='The account balance')
|
|
||||||
frozen: float = Field(..., description='The frozen balance')
|
|
||||||
|
|
||||||
class TripoBalanceResponse(BaseModel):
|
|
||||||
code: int = Field(0, description='The response code')
|
|
||||||
data: TripoBalanceData = Field(..., description='The balance data')
|
|
||||||
|
|
||||||
class TripoErrorResponse(BaseModel):
|
class TripoErrorResponse(BaseModel):
|
||||||
code: int = Field(..., description='The error code')
|
code: int = Field(..., description="The error code")
|
||||||
message: str = Field(..., description='The error message')
|
message: str = Field(..., description="The error message")
|
||||||
suggestion: str = Field(..., description='The suggestion for fixing the error')
|
suggestion: str = Field(..., description="The suggestion for fixing the error")
|
||||||
|
|||||||
@ -11,6 +11,9 @@ from comfy_api_nodes.apis.tripo import (
|
|||||||
TripoModelVersion,
|
TripoModelVersion,
|
||||||
TripoMultiviewToModelRequest,
|
TripoMultiviewToModelRequest,
|
||||||
TripoOrientation,
|
TripoOrientation,
|
||||||
|
TripoP1ImageToModelRequest,
|
||||||
|
TripoP1MultiviewToModelRequest,
|
||||||
|
TripoP1TextToModelRequest,
|
||||||
TripoRefineModelRequest,
|
TripoRefineModelRequest,
|
||||||
TripoStyle,
|
TripoStyle,
|
||||||
TripoTaskResponse,
|
TripoTaskResponse,
|
||||||
@ -93,10 +96,22 @@ class TripoTextToModelNode(IO.ComfyNode):
|
|||||||
IO.Int.Input("image_seed", default=42, optional=True, advanced=True),
|
IO.Int.Input("image_seed", default=42, optional=True, advanced=True),
|
||||||
IO.Int.Input("model_seed", default=42, optional=True, advanced=True),
|
IO.Int.Input("model_seed", default=42, optional=True, advanced=True),
|
||||||
IO.Int.Input("texture_seed", default=42, optional=True, advanced=True),
|
IO.Int.Input("texture_seed", default=42, optional=True, advanced=True),
|
||||||
IO.Combo.Input("texture_quality", default="standard", options=["standard", "detailed"], optional=True, advanced=True),
|
IO.Combo.Input(
|
||||||
|
"texture_quality",
|
||||||
|
default="standard",
|
||||||
|
options=["standard", "detailed"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
IO.Int.Input("face_limit", default=-1, min=-1, max=2000000, optional=True, advanced=True),
|
IO.Int.Input("face_limit", default=-1, min=-1, max=2000000, optional=True, advanced=True),
|
||||||
IO.Boolean.Input("quad", default=False, optional=True, advanced=True),
|
IO.Boolean.Input("quad", default=False, optional=True, advanced=True),
|
||||||
IO.Combo.Input("geometry_quality", default="standard", options=["standard", "detailed"], optional=True, advanced=True),
|
IO.Combo.Input(
|
||||||
|
"geometry_quality",
|
||||||
|
default="standard",
|
||||||
|
options=["standard", "detailed"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
||||||
@ -209,16 +224,36 @@ class TripoImageToModelNode(IO.ComfyNode):
|
|||||||
IO.Boolean.Input("pbr", default=True, optional=True),
|
IO.Boolean.Input("pbr", default=True, optional=True),
|
||||||
IO.Int.Input("model_seed", default=42, optional=True, advanced=True),
|
IO.Int.Input("model_seed", default=42, optional=True, advanced=True),
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"orientation", options=TripoOrientation, default=TripoOrientation.DEFAULT, optional=True, advanced=True
|
"orientation",
|
||||||
|
options=TripoOrientation,
|
||||||
|
default=TripoOrientation.DEFAULT,
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
),
|
),
|
||||||
IO.Int.Input("texture_seed", default=42, optional=True, advanced=True),
|
IO.Int.Input("texture_seed", default=42, optional=True, advanced=True),
|
||||||
IO.Combo.Input("texture_quality", default="standard", options=["standard", "detailed"], optional=True, advanced=True),
|
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"texture_alignment", default="original_image", options=["original_image", "geometry"], optional=True, advanced=True
|
"texture_quality",
|
||||||
|
default="standard",
|
||||||
|
options=["standard", "detailed"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"texture_alignment",
|
||||||
|
default="original_image",
|
||||||
|
options=["original_image", "geometry"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
),
|
),
|
||||||
IO.Int.Input("face_limit", default=-1, min=-1, max=500000, optional=True, advanced=True),
|
IO.Int.Input("face_limit", default=-1, min=-1, max=500000, optional=True, advanced=True),
|
||||||
IO.Boolean.Input("quad", default=False, optional=True, advanced=True),
|
IO.Boolean.Input("quad", default=False, optional=True, advanced=True),
|
||||||
IO.Combo.Input("geometry_quality", default="standard", options=["standard", "detailed"], optional=True, advanced=True),
|
IO.Combo.Input(
|
||||||
|
"geometry_quality",
|
||||||
|
default="standard",
|
||||||
|
options=["standard", "detailed"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
||||||
@ -346,13 +381,35 @@ class TripoMultiviewToModelNode(IO.ComfyNode):
|
|||||||
IO.Boolean.Input("pbr", default=True, optional=True),
|
IO.Boolean.Input("pbr", default=True, optional=True),
|
||||||
IO.Int.Input("model_seed", default=42, optional=True, advanced=True),
|
IO.Int.Input("model_seed", default=42, optional=True, advanced=True),
|
||||||
IO.Int.Input("texture_seed", default=42, optional=True, advanced=True),
|
IO.Int.Input("texture_seed", default=42, optional=True, advanced=True),
|
||||||
IO.Combo.Input("texture_quality", default="standard", options=["standard", "detailed"], optional=True, advanced=True),
|
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"texture_alignment", default="original_image", options=["original_image", "geometry"], optional=True, advanced=True
|
"texture_quality",
|
||||||
|
default="standard",
|
||||||
|
options=["standard", "detailed"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"texture_alignment",
|
||||||
|
default="original_image",
|
||||||
|
options=["original_image", "geometry"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
),
|
),
|
||||||
IO.Int.Input("face_limit", default=-1, min=-1, max=500000, optional=True, advanced=True),
|
IO.Int.Input("face_limit", default=-1, min=-1, max=500000, optional=True, advanced=True),
|
||||||
IO.Boolean.Input("quad", default=False, optional=True, advanced=True, tooltip="This parameter is deprecated and does nothing."),
|
IO.Boolean.Input(
|
||||||
IO.Combo.Input("geometry_quality", default="standard", options=["standard", "detailed"], optional=True, advanced=True),
|
"quad",
|
||||||
|
default=False,
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
tooltip="This parameter is deprecated and does nothing.",
|
||||||
|
),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"geometry_quality",
|
||||||
|
default="standard",
|
||||||
|
options=["standard", "detailed"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
||||||
@ -467,9 +524,19 @@ class TripoTextureNode(IO.ComfyNode):
|
|||||||
IO.Boolean.Input("texture", default=True, optional=True),
|
IO.Boolean.Input("texture", default=True, optional=True),
|
||||||
IO.Boolean.Input("pbr", default=True, optional=True),
|
IO.Boolean.Input("pbr", default=True, optional=True),
|
||||||
IO.Int.Input("texture_seed", default=42, optional=True, advanced=True),
|
IO.Int.Input("texture_seed", default=42, optional=True, advanced=True),
|
||||||
IO.Combo.Input("texture_quality", default="standard", options=["standard", "detailed"], optional=True, advanced=True),
|
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"texture_alignment", default="original_image", options=["original_image", "geometry"], optional=True, advanced=True
|
"texture_quality",
|
||||||
|
default="standard",
|
||||||
|
options=["standard", "detailed"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"texture_alignment",
|
||||||
|
default="original_image",
|
||||||
|
options=["original_image", "geometry"],
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
@ -626,7 +693,7 @@ class TripoRetargetNode(IO.ComfyNode):
|
|||||||
"preset:hexapod:walk",
|
"preset:hexapod:walk",
|
||||||
"preset:octopod:walk",
|
"preset:octopod:walk",
|
||||||
"preset:serpentine:march",
|
"preset:serpentine:march",
|
||||||
"preset:aquatic:march"
|
"preset:aquatic:march",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -817,7 +884,7 @@ class TripoConversionNode(IO.ComfyNode):
|
|||||||
# Parse part_names from comma-separated string to list
|
# Parse part_names from comma-separated string to list
|
||||||
part_names_list = None
|
part_names_list = None
|
||||||
if part_names and part_names.strip():
|
if part_names and part_names.strip():
|
||||||
part_names_list = [name.strip() for name in part_names.split(',') if name.strip()]
|
part_names_list = [name.strip() for name in part_names.split(",") if name.strip()]
|
||||||
|
|
||||||
response = await sync_op(
|
response = await sync_op(
|
||||||
cls,
|
cls,
|
||||||
@ -848,6 +915,373 @@ class TripoConversionNode(IO.ComfyNode):
|
|||||||
return await poll_until_finished(cls, response, average_duration=30)
|
return await poll_until_finished(cls, response, average_duration=30)
|
||||||
|
|
||||||
|
|
||||||
|
def _p1_price_expr(*, geometry_credits: int, textured_credits: int, detailed_credits: int) -> str:
|
||||||
|
return (
|
||||||
|
"("
|
||||||
|
" $mode := widgets.output_mode;"
|
||||||
|
' $detailed := $lookup(widgets, "output_mode.texture_quality") = "detailed";'
|
||||||
|
f' $credits := $mode = "geometry only" ? {geometry_credits} : ($detailed ? {detailed_credits} : {textured_credits});'
|
||||||
|
' {"type":"usd","usd": $credits * 0.01, "format": {"approximate": true}}'
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _p1_textured_inputs(*, include_image_alignment: bool) -> list:
|
||||||
|
"""Inputs shown inside the 'Textured' branch of the P1 output_mode DynamicCombo."""
|
||||||
|
inputs: list = [
|
||||||
|
IO.Boolean.Input("pbr", default=True, tooltip="Include PBR maps. When on, base texture is forced on too."),
|
||||||
|
IO.Combo.Input("texture_quality", options=["standard", "detailed"], default="standard"),
|
||||||
|
]
|
||||||
|
if include_image_alignment:
|
||||||
|
inputs.extend(
|
||||||
|
[
|
||||||
|
IO.Combo.Input(
|
||||||
|
"texture_alignment",
|
||||||
|
options=["original_image", "geometry"],
|
||||||
|
default="original_image",
|
||||||
|
tooltip="Prioritize visual fidelity to the source image, or alignment to the mesh geometry.",
|
||||||
|
),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"orientation",
|
||||||
|
options=["default", "align_image"],
|
||||||
|
default="default",
|
||||||
|
tooltip="Rotate the output to match the source image. Only applies when textured.",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
inputs.append(IO.Int.Input("texture_seed", default=42, advanced=True))
|
||||||
|
return inputs
|
||||||
|
|
||||||
|
|
||||||
|
def _build_p1_output_mode(*, include_image_alignment: bool) -> IO.DynamicCombo.Input:
|
||||||
|
return IO.DynamicCombo.Input(
|
||||||
|
"output_mode",
|
||||||
|
options=[
|
||||||
|
IO.DynamicCombo.Option("Geometry only", []),
|
||||||
|
IO.DynamicCombo.Option("Textured", _p1_textured_inputs(include_image_alignment=include_image_alignment)),
|
||||||
|
],
|
||||||
|
tooltip='"Geometry only" returns an untextured mesh. "Textured" adds color/PBR maps.',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_p1_texture_fields(output_mode: dict) -> dict:
|
||||||
|
"""Translate the output_mode DynamicCombo payload into P1 request fields.
|
||||||
|
|
||||||
|
pbr=true forces texture=true server-side, but we send both explicitly so the
|
||||||
|
intent is visible in the request body and logs.
|
||||||
|
"""
|
||||||
|
mode = output_mode["output_mode"]
|
||||||
|
if mode == "Geometry only":
|
||||||
|
return {"texture": False, "pbr": False}
|
||||||
|
out = {
|
||||||
|
"texture": True,
|
||||||
|
"pbr": bool(output_mode.get("pbr", True)),
|
||||||
|
"texture_quality": output_mode.get("texture_quality", "standard"),
|
||||||
|
"texture_seed": output_mode.get("texture_seed"),
|
||||||
|
}
|
||||||
|
if "texture_alignment" in output_mode:
|
||||||
|
out["texture_alignment"] = output_mode["texture_alignment"]
|
||||||
|
if "orientation" in output_mode:
|
||||||
|
out["orientation"] = output_mode["orientation"]
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _p1_common_inputs() -> list:
|
||||||
|
"""Inputs shared by all P1 nodes (placed after output_mode)."""
|
||||||
|
return [
|
||||||
|
IO.Int.Input(
|
||||||
|
"face_limit",
|
||||||
|
default=-1,
|
||||||
|
min=-1,
|
||||||
|
max=20000,
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
tooltip="Target face count, 48-20000. -1 lets Tripo pick adaptively.",
|
||||||
|
),
|
||||||
|
IO.Int.Input("model_seed", default=42, optional=True, advanced=True),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"auto_size",
|
||||||
|
default=False,
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
tooltip="Scale the output to approximate real-world meters.",
|
||||||
|
),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"export_uv",
|
||||||
|
default=True,
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
tooltip="UV unwrap during generation. Turn off for faster geometry-only runs.",
|
||||||
|
),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"compress_geometry",
|
||||||
|
default=False,
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
tooltip="Apply geometry-based compression. Decompress before editing.",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _build_p1_request_kwargs(
|
||||||
|
*,
|
||||||
|
output_mode: dict,
|
||||||
|
face_limit: int,
|
||||||
|
model_seed: int,
|
||||||
|
auto_size: bool,
|
||||||
|
export_uv: bool,
|
||||||
|
compress_geometry: bool,
|
||||||
|
) -> dict:
|
||||||
|
"""Common P1 request fields shared by all three node types."""
|
||||||
|
kwargs: dict = {
|
||||||
|
"model_seed": model_seed,
|
||||||
|
"face_limit": face_limit if face_limit != -1 else None,
|
||||||
|
"auto_size": auto_size,
|
||||||
|
"export_uv": export_uv,
|
||||||
|
"compress": "geometry" if compress_geometry else None,
|
||||||
|
}
|
||||||
|
kwargs.update(_resolve_p1_texture_fields(output_mode))
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class TripoP1TextToModelNode(IO.ComfyNode):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="TripoP1TextToModelNode",
|
||||||
|
display_name="Tripo P1: Text to Model",
|
||||||
|
category="3d/partner/Tripo",
|
||||||
|
description="Tripo P1 text-to-3D. Optimized for low-poly, game-ready meshes with stable topology.",
|
||||||
|
inputs=[
|
||||||
|
IO.String.Input("prompt", multiline=True, tooltip="Up to 1024 characters."),
|
||||||
|
IO.String.Input("negative_prompt", multiline=True, optional=True, tooltip="Up to 255 characters."),
|
||||||
|
_build_p1_output_mode(include_image_alignment=False),
|
||||||
|
IO.Int.Input("image_seed", default=42, optional=True, advanced=True),
|
||||||
|
*_p1_common_inputs(),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
||||||
|
IO.Custom("MODEL_TASK_ID").Output(display_name="model task_id"),
|
||||||
|
IO.File3DGLB.Output(display_name="GLB"),
|
||||||
|
],
|
||||||
|
hidden=[
|
||||||
|
IO.Hidden.auth_token_comfy_org,
|
||||||
|
IO.Hidden.api_key_comfy_org,
|
||||||
|
IO.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["output_mode", "output_mode.texture_quality"]),
|
||||||
|
expr=_p1_price_expr(geometry_credits=30, textured_credits=40, detailed_credits=50),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
prompt: str,
|
||||||
|
output_mode: dict,
|
||||||
|
negative_prompt: str | None = None,
|
||||||
|
image_seed: int | None = None,
|
||||||
|
face_limit: int = -1,
|
||||||
|
model_seed: int | None = None,
|
||||||
|
auto_size: bool = False,
|
||||||
|
export_uv: bool = True,
|
||||||
|
compress_geometry: bool = False,
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
if not prompt:
|
||||||
|
raise RuntimeError("Prompt is required")
|
||||||
|
common = _build_p1_request_kwargs(
|
||||||
|
output_mode=output_mode,
|
||||||
|
face_limit=face_limit,
|
||||||
|
model_seed=model_seed,
|
||||||
|
auto_size=auto_size,
|
||||||
|
export_uv=export_uv,
|
||||||
|
compress_geometry=compress_geometry,
|
||||||
|
)
|
||||||
|
request = TripoP1TextToModelRequest(
|
||||||
|
prompt=prompt,
|
||||||
|
negative_prompt=negative_prompt or None,
|
||||||
|
image_seed=image_seed,
|
||||||
|
**common,
|
||||||
|
)
|
||||||
|
response = await sync_op(
|
||||||
|
cls,
|
||||||
|
endpoint=ApiEndpoint(path="/proxy/tripo/v2/openapi/task", method="POST"),
|
||||||
|
response_model=TripoTaskResponse,
|
||||||
|
data=request,
|
||||||
|
)
|
||||||
|
return await poll_until_finished(cls, response, average_duration=60)
|
||||||
|
|
||||||
|
|
||||||
|
class TripoP1ImageToModelNode(IO.ComfyNode):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="TripoP1ImageToModelNode",
|
||||||
|
display_name="Tripo P1: Image to Model",
|
||||||
|
category="3d/partner/Tripo",
|
||||||
|
description="Tripo P1 image-to-3D. Optimized for low-poly, game-ready meshes.",
|
||||||
|
inputs=[
|
||||||
|
IO.Image.Input("image"),
|
||||||
|
_build_p1_output_mode(include_image_alignment=True),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"enable_image_autofix",
|
||||||
|
default=False,
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
tooltip="Pre-process the input image for better generation quality.",
|
||||||
|
),
|
||||||
|
*_p1_common_inputs(),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
||||||
|
IO.Custom("MODEL_TASK_ID").Output(display_name="model task_id"),
|
||||||
|
IO.File3DGLB.Output(display_name="GLB"),
|
||||||
|
],
|
||||||
|
hidden=[
|
||||||
|
IO.Hidden.auth_token_comfy_org,
|
||||||
|
IO.Hidden.api_key_comfy_org,
|
||||||
|
IO.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["output_mode", "output_mode.texture_quality"]),
|
||||||
|
expr=_p1_price_expr(geometry_credits=40, textured_credits=50, detailed_credits=60),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
image: Input.Image,
|
||||||
|
output_mode: dict,
|
||||||
|
enable_image_autofix: bool = False,
|
||||||
|
face_limit: int = -1,
|
||||||
|
model_seed: int | None = None,
|
||||||
|
auto_size: bool = False,
|
||||||
|
export_uv: bool = True,
|
||||||
|
compress_geometry: bool = False,
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
if image is None:
|
||||||
|
raise RuntimeError("Image is required")
|
||||||
|
tripo_file = TripoFileReference(
|
||||||
|
root=TripoUrlReference(
|
||||||
|
url=(await upload_images_to_comfyapi(cls, image, max_images=1))[0],
|
||||||
|
type="jpeg",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
common = _build_p1_request_kwargs(
|
||||||
|
output_mode=output_mode,
|
||||||
|
face_limit=face_limit,
|
||||||
|
model_seed=model_seed,
|
||||||
|
auto_size=auto_size,
|
||||||
|
export_uv=export_uv,
|
||||||
|
compress_geometry=compress_geometry,
|
||||||
|
)
|
||||||
|
request = TripoP1ImageToModelRequest(
|
||||||
|
file=tripo_file,
|
||||||
|
enable_image_autofix=enable_image_autofix,
|
||||||
|
**common,
|
||||||
|
)
|
||||||
|
response = await sync_op(
|
||||||
|
cls,
|
||||||
|
endpoint=ApiEndpoint(path="/proxy/tripo/v2/openapi/task", method="POST"),
|
||||||
|
response_model=TripoTaskResponse,
|
||||||
|
data=request,
|
||||||
|
)
|
||||||
|
return await poll_until_finished(cls, response, average_duration=60)
|
||||||
|
|
||||||
|
|
||||||
|
class TripoP1MultiviewToModelNode(IO.ComfyNode):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="TripoP1MultiviewToModelNode",
|
||||||
|
display_name="Tripo P1: Multiview to Model",
|
||||||
|
category="3d/partner/Tripo",
|
||||||
|
description="Tripo P1 multiview-to-3D from 2-4 reference images in [front, left, back, right] order. "
|
||||||
|
"Front is required; any combination of the other three may be omitted.",
|
||||||
|
inputs=[
|
||||||
|
IO.Image.Input("image", tooltip="Front view (0°). Required."),
|
||||||
|
IO.Image.Input(
|
||||||
|
"image_left",
|
||||||
|
optional=True,
|
||||||
|
tooltip="Left view (90°), i.e. the subject's left side.",
|
||||||
|
),
|
||||||
|
IO.Image.Input("image_back", optional=True, tooltip="Back view (180°)."),
|
||||||
|
IO.Image.Input(
|
||||||
|
"image_right",
|
||||||
|
optional=True,
|
||||||
|
tooltip="Right view (270°), i.e. the subject's right side.",
|
||||||
|
),
|
||||||
|
_build_p1_output_mode(include_image_alignment=True),
|
||||||
|
*_p1_common_inputs(),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
IO.String.Output(display_name="model_file"), # for backward compatibility only
|
||||||
|
IO.Custom("MODEL_TASK_ID").Output(display_name="model task_id"),
|
||||||
|
IO.File3DGLB.Output(display_name="GLB"),
|
||||||
|
],
|
||||||
|
hidden=[
|
||||||
|
IO.Hidden.auth_token_comfy_org,
|
||||||
|
IO.Hidden.api_key_comfy_org,
|
||||||
|
IO.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["output_mode", "output_mode.texture_quality"]),
|
||||||
|
expr=_p1_price_expr(geometry_credits=40, textured_credits=50, detailed_credits=60),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
image: Input.Image,
|
||||||
|
output_mode: dict,
|
||||||
|
image_left: Input.Image | None = None,
|
||||||
|
image_back: Input.Image | None = None,
|
||||||
|
image_right: Input.Image | None = None,
|
||||||
|
face_limit: int = -1,
|
||||||
|
model_seed: int | None = None,
|
||||||
|
auto_size: bool = False,
|
||||||
|
export_uv: bool = True,
|
||||||
|
compress_geometry: bool = False,
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
views = [image, image_left, image_back, image_right]
|
||||||
|
if sum(1 for v in views if v is not None) < 2:
|
||||||
|
raise RuntimeError("Tripo P1 multiview requires at least 2 images (front plus one of left/back/right).")
|
||||||
|
|
||||||
|
files: list[TripoFileReference] = []
|
||||||
|
for view in views:
|
||||||
|
if view is None:
|
||||||
|
files.append(TripoFileReference(root=TripoFileEmptyReference()))
|
||||||
|
continue
|
||||||
|
url = (await upload_images_to_comfyapi(cls, view, max_images=1))[0]
|
||||||
|
files.append(TripoFileReference(root=TripoUrlReference(url=url, type="jpeg")))
|
||||||
|
|
||||||
|
common = _build_p1_request_kwargs(
|
||||||
|
output_mode=output_mode,
|
||||||
|
face_limit=face_limit,
|
||||||
|
model_seed=model_seed,
|
||||||
|
auto_size=auto_size,
|
||||||
|
export_uv=export_uv,
|
||||||
|
compress_geometry=compress_geometry,
|
||||||
|
)
|
||||||
|
request = TripoP1MultiviewToModelRequest(files=files, **common)
|
||||||
|
response = await sync_op(
|
||||||
|
cls,
|
||||||
|
endpoint=ApiEndpoint(path="/proxy/tripo/v2/openapi/task", method="POST"),
|
||||||
|
response_model=TripoTaskResponse,
|
||||||
|
data=request,
|
||||||
|
)
|
||||||
|
return await poll_until_finished(cls, response, average_duration=80)
|
||||||
|
|
||||||
|
|
||||||
class TripoExtension(ComfyExtension):
|
class TripoExtension(ComfyExtension):
|
||||||
@override
|
@override
|
||||||
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
||||||
@ -855,6 +1289,9 @@ class TripoExtension(ComfyExtension):
|
|||||||
TripoTextToModelNode,
|
TripoTextToModelNode,
|
||||||
TripoImageToModelNode,
|
TripoImageToModelNode,
|
||||||
TripoMultiviewToModelNode,
|
TripoMultiviewToModelNode,
|
||||||
|
TripoP1TextToModelNode,
|
||||||
|
TripoP1ImageToModelNode,
|
||||||
|
TripoP1MultiviewToModelNode,
|
||||||
TripoTextureNode,
|
TripoTextureNode,
|
||||||
TripoRefineNode,
|
TripoRefineNode,
|
||||||
TripoRigNode,
|
TripoRigNode,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user