Added Support for Draco Compression in 3D Inference

This commit is contained in:
Sirshendu Ganguly
2026-06-09 17:41:31 +05:30
parent 0bfe164c32
commit a793c18978
10 changed files with 316 additions and 26 deletions
+4 -20
View File
@@ -5,18 +5,6 @@ import comfy.model_management
class threeDInference:
"""Runware 3D Inference node for generating 3D models from images"""
# Available 3D models
THREED_MODELS = {
"Tencent Hunyuan 3D 3.1 Pro": "tencent:hunyuan-3d@3.1-pro",
"Tencent Hunyuan 3D 3.1 Rapid": "tencent:hunyuan-3d@3.1-rapid",
"Meta SAM 3D": "meta:sam@3d",
"TRELLIS.2": "microsoft:trellis-2@4b",
"Tripo 3D v3.1": "tripo:v3.1@0",
"Rodin Gen-1": "hyper3d:rodin@gen-1",
"Rodin Gen-2": "hyper3d:rodin@gen-2",
"Meshy 6": "meshy:meshy@6",
}
# Output formats
OUTPUT_FORMATS = ["GLB", "FBX", "PLY", "OBJ"]
@@ -24,9 +12,8 @@ class threeDInference:
def INPUT_TYPES(cls):
return {
"required": {
"Model": (list(cls.THREED_MODELS.keys()), {
"tooltip": "Select the 3D generation model to use.",
"default": "Meta SAM 3D",
"Model": ("RUNWARE3DMODEL", {
"tooltip": "Connect a Runware 3D Model from Runware 3D Model Search node.",
}),
"positivePrompt": ("STRING", {
"multiline": True,
@@ -66,7 +53,7 @@ class threeDInference:
"step": 1,
}),
"inputs": ("RUNWARE3DINFERENCEINPUTS", {
"tooltip": "Connect Runware 3D Inference Inputs for image, mask, meshFile, images (array from images_1…4), etc.",
"tooltip": "Connect Runware 3D Inference Inputs for image, mask, meshFile, images (array from Images 1…8), etc.",
}),
"settings": ("RUNWARE3DINFERENCESETTINGS", {
"tooltip": "Connect Runware 3D Inference Settings for textureSize, decimationTarget, remesh, resolution, imageAutoFix, faceLimit, texture, pbr, quad, Tripo options, sparseStructure, shapeSlat, texSlat, etc.",
@@ -82,7 +69,7 @@ class threeDInference:
def generate3D(self, **kwargs):
"""Generate 3D model from inputs"""
modelName = kwargs.get("Model", "Meta SAM 3D")
model = kwargs.get("Model", "meta:sam@3d")
positivePrompt = kwargs.get("positivePrompt", "")
negativePrompt = kwargs.get("negativePrompt", "")
seed = kwargs.get("seed", 1)
@@ -93,9 +80,6 @@ class threeDInference:
inputs = kwargs.get("inputs", None)
settings = kwargs.get("settings", None)
# Get model AIR code
model = self.THREED_MODELS.get(modelName, "meta:sam@3d")
# Build generation config
genConfig = [
{
+3 -3
View File
@@ -21,7 +21,7 @@ class threeDInferenceInputs:
}),
}
for i in range(1, _IMAGE_SLOTS + 1):
optional[f"images_{i}"] = ("IMAGE", {
optional[f"Images {i}"] = ("IMAGE", {
"tooltip": f"Multi-view image slot {i}; merged in order into inputs.images as a list of data URIs.",
})
return {
@@ -30,7 +30,7 @@ class threeDInferenceInputs:
}
DESCRIPTION = (
"Configure custom inputs for Runware 3D Inference: image, mask, meshFile, and images_1…images_8 "
"Configure custom inputs for Runware 3D Inference: image, mask, meshFile, and Images 1…Images 8 "
"(merged into inputs.images as an array)."
)
FUNCTION = "createInputs"
@@ -57,7 +57,7 @@ class threeDInferenceInputs:
images_list: list = []
for i in range(1, _IMAGE_SLOTS + 1):
slot = kwargs.get(f"images_{i}")
slot = kwargs.get(f"Images {i}")
if slot is not None:
images_list.append(rwUtils.convertTensor2IMG(slot))
if images_list:
+9 -2
View File
@@ -3,7 +3,7 @@ Runware 3D Inference Settings Node
Main settings for 3D inference: textureSize, decimationTarget, remesh, remeshBand, remeshProject,
resolution, imageAutoFix, faceLimit, texture, pbr, textureFormat, alphaMode, Tripo mesh/texture options, material/quality,
polyCount, taPose, boundingBox, meshMode, addons, hdTexture, and sub-configs
sparseStructure, shapeSlat, texSlat, meshCluster from connected nodes.
sparseStructure, shapeSlat, texSlat, meshCluster, dracoCompression from connected nodes.
"""
from typing import Dict, Any
@@ -466,6 +466,9 @@ class Runware3DInferenceSettings:
"tooltip": "Material alpha mode. Only used when 'Use Alpha Mode' is enabled.",
"default": "OPAQUE",
}),
"dracoCompression": ("RUNWARE3DINFERENCESETTINGSLAT", {
"tooltip": "Connect Runware 3D Inference Settings Draco Compression node.",
}),
}
}
@@ -478,7 +481,7 @@ class Runware3DInferenceSettings:
"resolution, imageAutoFix, faceLimit, texture, pbr, textureSeed, textureAlignment, textureQuality, "
"textureFormat, alphaMode, autoSize, orientation, quad, compress, "
"smartLowPoly, generateParts, exportUv, geometryQuality, originalAlpha, material, quality, polyCount, "
"taPose, boundingBox, meshMode, addons, hdTexture, lat configs, and meshCluster."
"taPose, boundingBox, meshMode, addons, hdTexture, lat configs, meshCluster, and dracoCompression."
)
def create(self, **kwargs) -> tuple[Dict[str, Any]]:
@@ -610,6 +613,10 @@ class Runware3DInferenceSettings:
if mesh_cluster is not None and isinstance(mesh_cluster, dict) and len(mesh_cluster) > 0:
settings["meshCluster"] = mesh_cluster
draco_compression = kwargs.get("dracoCompression", None)
if draco_compression is not None and isinstance(draco_compression, dict) and len(draco_compression) > 0:
settings["dracoCompression"] = draco_compression
return (settings,)
@@ -0,0 +1,109 @@
"""
Runware 3D Inference Settings Draco Compression Node
Outputs dracoCompression config: enabled, level, quantizationPosition,
quantizationNormal, quantizationTexCoord
"""
from typing import Dict, Any
class Runware3DInferenceSettingsDracoCompression:
"""Runware 3D Inference Settings Draco Compression Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useEnabled": ("BOOLEAN", {
"tooltip": "Enable to include enabled in dracoCompression",
"default": False,
}),
"enabled": ("BOOLEAN", {
"tooltip": (
"If true, applies KHR_draco_mesh_compression to the output GLB "
"(5-10x geometry size reduction with no triangle loss). "
"Only used when 'Use Enabled' is on."
),
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useLevel": ("BOOLEAN", {
"tooltip": "Enable to include level in dracoCompression",
"default": False,
}),
"level": ("INT", {
"tooltip": "Draco compression level (0-10). Higher = better compression but slower encoding. Only used when enabled.",
"default": 7,
"min": 0,
"max": 10,
"step": 1,
}),
"useQuantizationPosition": ("BOOLEAN", {
"tooltip": "Enable to include quantizationPosition in dracoCompression",
"default": False,
}),
"quantizationPosition": ("INT", {
"tooltip": "Quantization bits for vertex positions (8-30). Higher = better quality, larger file. Only used when enabled.",
"default": 16,
"min": 8,
"max": 30,
"step": 1,
}),
"useQuantizationNormal": ("BOOLEAN", {
"tooltip": "Enable to include quantizationNormal in dracoCompression",
"default": False,
}),
"quantizationNormal": ("INT", {
"tooltip": "Quantization bits for vertex normals (8-30). Higher = smoother shading, larger file. Only used when enabled.",
"default": 14,
"min": 8,
"max": 30,
"step": 1,
}),
"useQuantizationTexCoord": ("BOOLEAN", {
"tooltip": "Enable to include quantizationTexCoord in dracoCompression",
"default": False,
}),
"quantizationTexCoord": ("INT", {
"tooltip": "Quantization bits for texture coordinates (8-30). Higher = sharper textures at UV seams. Only used when enabled.",
"default": 14,
"min": 8,
"max": 30,
"step": 1,
}),
}
}
RETURN_TYPES = ("RUNWARE3DINFERENCESETTINGSLAT",)
RETURN_NAMES = ("Draco Compression",)
FUNCTION = "create"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure settings.dracoCompression for Runware 3D Inference "
"(enabled, level, quantizationPosition, quantizationNormal, quantizationTexCoord). "
"Connect to Runware 3D Inference Settings."
)
def create(self, **kwargs) -> tuple[Dict[str, Any]]:
out: Dict[str, Any] = {}
if kwargs.get("useEnabled", False):
out["enabled"] = bool(kwargs.get("enabled", False))
if kwargs.get("useLevel", False):
out["level"] = int(kwargs.get("level", 7))
if kwargs.get("useQuantizationPosition", False):
out["quantizationPosition"] = int(kwargs.get("quantizationPosition", 16))
if kwargs.get("useQuantizationNormal", False):
out["quantizationNormal"] = int(kwargs.get("quantizationNormal", 14))
if kwargs.get("useQuantizationTexCoord", False):
out["quantizationTexCoord"] = int(kwargs.get("quantizationTexCoord", 14))
return (out,)
NODE_CLASS_MAPPINGS = {
"Runware3DInferenceSettingsDracoCompression": Runware3DInferenceSettingsDracoCompression,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Runware3DInferenceSettingsDracoCompression": "Runware 3D Inference Settings Draco Compression",
}
+91
View File
@@ -0,0 +1,91 @@
class Runware3DModelSearch:
"""3D Model Search node for searching and selecting 3D generation models"""
THREED_MODELS = {
"Tencent": [
"tencent:hunyuan-3d@3.1-pro (Tencent Hunyuan 3D 3.1 Pro)",
"tencent:hunyuan-3d@3.1-rapid (Tencent Hunyuan 3D 3.1 Rapid)",
],
"Meta": [
"meta:sam@3d (Meta SAM 3D)",
],
"Microsoft": [
"microsoft:trellis-2@4b (TRELLIS.2)",
],
"Tripo": [
"tripo:v3.1@0 (Tripo 3D v3.1)",
],
"Hyper3D": [
"hyper3d:rodin@gen-1 (Rodin Gen-1)",
"hyper3d:rodin@gen-2 (Rodin Gen-2)",
],
"Meshy": [
"meshy:meshy@6 (Meshy 6)",
],
}
MODEL_ARCHITECTURES = [
"All",
"Tencent",
"Meta",
"Microsoft",
"Tripo",
"Hyper3D",
"Meshy",
]
@classmethod
def INPUT_TYPES(cls):
allModels = cls._getAllModels()
defaultModel = "meta:sam@3d (Meta SAM 3D)"
return {
"required": {
"Model Search": ("STRING", {
"tooltip": "Search for a specific 3D model by name or AIR code (e.g. hunyuan, trellis, tripo).",
}),
"Model Architecture": (cls.MODEL_ARCHITECTURES, {
"tooltip": "Choose 3D model architecture to filter results.",
"default": "Meta",
}),
"ThreeDList": (allModels, {
"tooltip": "3D model results will show up here so you can choose from.",
"default": defaultModel,
}),
"Use Search Value": ("BOOLEAN", {
"tooltip": "When enabled, the value in the search input is used instead of the selected list entry.\n\nUseful if you prefer to set the model manually.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
},
}
@classmethod
def _getAllModels(cls):
allModels = []
for models in cls.THREED_MODELS.values():
allModels.extend(models)
return allModels
DESCRIPTION = "Search and connect 3D models to Runware 3D Inference nodes in ComfyUI."
FUNCTION = "threeDModelSearch"
RETURN_TYPES = ("RUNWARE3DMODEL",)
RETURN_NAMES = ("Runware 3D Model",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, ThreeDList):
return True
def threeDModelSearch(self, **kwargs):
enableSearchValue = kwargs.get("Use Search Value", False)
searchInput = kwargs.get("Model Search")
if enableSearchValue:
modelAirCode = searchInput
else:
currentModel = kwargs.get("ThreeDList")
modelAirCode = currentModel.split(" (")[0]
return (modelAirCode,)