Merge pull request #86 from Runware/feature-3DInference
Feature 3D inference
This commit is contained in:
@@ -72,6 +72,9 @@ from .modules.settings import RunwareSettings
|
||||
from .modules.videoAdvancedFeatureInputs import videoAdvancedFeatureInputs
|
||||
from .modules.advancedFeatureSettings.wanAnimateAdvancedFeatureSettings import RunwareWanAnimateAdvancedFeatureSettings
|
||||
from .modules.videoOutputs import RunwareVideoInferenceOutputs
|
||||
from .modules.threeDInference import threeDInference
|
||||
from .modules.threeDInferenceInputs import threeDInferenceInputs
|
||||
from .modules.save3D import RunwareSave3D
|
||||
|
||||
RUNWARE_COMFYUI_VERSION = "1.4.0 Beta"
|
||||
|
||||
@@ -158,6 +161,9 @@ NODE_CLASS_MAPPINGS = {
|
||||
"Runware Video Advanced Feature Inputs": videoAdvancedFeatureInputs,
|
||||
"Runware Wan Animate Advanced Feature Settings": RunwareWanAnimateAdvancedFeatureSettings,
|
||||
"Runware Video Inference Outputs": RunwareVideoInferenceOutputs,
|
||||
"Runware 3D Inference": threeDInference,
|
||||
"Runware 3D Inference Inputs": threeDInferenceInputs,
|
||||
"Runware Save 3D": RunwareSave3D,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
|
||||
+11
-1
@@ -1,6 +1,6 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler, captionNodeHandler, mediaUUIDHandler, videoTranscriptionHandler, videoOutputsHandler, handleCustomErrors, videoInferenceDimensionsHandler, videoModelSearchFilterHandler, audioModelSearchFilterHandler, useParameterToggleHandler, imageInferenceToggleHandler, upscalerToggleHandler, videoUpscalerToggleHandler, audioInferenceToggleHandler, acceleratorOptionsToggleHandler, bytedanceProviderSettingsToggleHandler, openaiProviderSettingsToggleHandler, lightricksProviderSettingsToggleHandler, klingProviderSettingsToggleHandler, lumaProviderSettingsToggleHandler, briaProviderSettingsToggleHandler, pixverseProviderSettingsToggleHandler, alibabaProviderSettingsToggleHandler, mireloProviderSettingsToggleHandler, googleProviderSettingsToggleHandler, syncProviderSettingsToggleHandler, syncSegmentToggleHandler, settingsToggleHandler, audioInputToggleHandler, speechInputToggleHandler, briaProviderMaskToggleHandler, wanAnimateAdvancedFeatureSettingsToggleHandler, videoAdvancedFeatureInputsToggleHandler, audioInferenceInputsToggleHandler } from "./utils.js";
|
||||
import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler, captionNodeHandler, mediaUUIDHandler, save3DFilepathHandler, videoTranscriptionHandler, videoOutputsHandler, handleCustomErrors, videoInferenceDimensionsHandler, videoModelSearchFilterHandler, audioModelSearchFilterHandler, useParameterToggleHandler, imageInferenceToggleHandler, upscalerToggleHandler, videoUpscalerToggleHandler, audioInferenceToggleHandler, acceleratorOptionsToggleHandler, bytedanceProviderSettingsToggleHandler, openaiProviderSettingsToggleHandler, lightricksProviderSettingsToggleHandler, klingProviderSettingsToggleHandler, lumaProviderSettingsToggleHandler, briaProviderSettingsToggleHandler, pixverseProviderSettingsToggleHandler, alibabaProviderSettingsToggleHandler, mireloProviderSettingsToggleHandler, googleProviderSettingsToggleHandler, syncProviderSettingsToggleHandler, syncSegmentToggleHandler, settingsToggleHandler, audioInputToggleHandler, speechInputToggleHandler, briaProviderMaskToggleHandler, wanAnimateAdvancedFeatureSettingsToggleHandler, videoAdvancedFeatureInputsToggleHandler, audioInferenceInputsToggleHandler } from "./utils.js";
|
||||
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
|
||||
|
||||
const nodeInitList = [];
|
||||
@@ -10,6 +10,7 @@ app.registerExtension({
|
||||
api.addEventListener('runwareError', handleCustomErrors);
|
||||
api.addEventListener('runwareImageCaption', captionNodeHandler);
|
||||
api.addEventListener('runwareMediaUUID', mediaUUIDHandler);
|
||||
api.addEventListener('runwareSave3DFilepath', save3DFilepathHandler);
|
||||
api.addEventListener('runwareVideoTranscription', videoTranscriptionHandler);
|
||||
api.addEventListener('runwareVideoOutputs', videoOutputsHandler);
|
||||
},
|
||||
@@ -52,6 +53,15 @@ app.registerExtension({
|
||||
w.inputEl.style.fontFamily = "monospace";
|
||||
}
|
||||
}
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.SAVE3D) {
|
||||
// Style the filepath widget to show full path (like Media Upload mediaUUID)
|
||||
const filepathWidget = node.widgets.find(widget => widget.name === "filepath");
|
||||
if(filepathWidget && filepathWidget.inputEl) {
|
||||
filepathWidget.inputEl.style.width = "100%";
|
||||
filepathWidget.inputEl.style.minWidth = "300px";
|
||||
filepathWidget.inputEl.style.fontSize = "11px";
|
||||
filepathWidget.inputEl.style.fontFamily = "monospace";
|
||||
}
|
||||
}
|
||||
|
||||
const nodeWidgets = node.widgets;
|
||||
|
||||
@@ -122,6 +122,9 @@ const RUNWARE_NODE_TYPES = {
|
||||
VIDEOADVANCEDFEATUREINPUTS: "Runware Video Advanced Feature Inputs",
|
||||
WANANIMATEADVANCEDFEATURESETTINGS: "Runware Wan Animate Advanced Feature Settings",
|
||||
VIDEOINFERENCEOUTPUTS: "Runware Video Inference Outputs",
|
||||
THREEDINFERENCE: "Runware 3D Inference",
|
||||
THREEDINFERENCEINPUTS: "Runware 3D Inference Inputs",
|
||||
SAVE3D: "Runware Save 3D",
|
||||
};
|
||||
|
||||
const RUNWARE_NODE_PROPS = {
|
||||
@@ -425,6 +428,18 @@ const RUNWARE_NODE_PROPS = {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
[RUNWARE_NODE_TYPES.THREEDINFERENCE]: {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
promptEnhancer: true,
|
||||
},
|
||||
[RUNWARE_NODE_TYPES.THREEDINFERENCEINPUTS]: {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
[RUNWARE_NODE_TYPES.SAVE3D]: {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
};
|
||||
|
||||
export {
|
||||
|
||||
@@ -70,6 +70,31 @@ function mediaUUIDHandler(msgEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function save3DFilepathHandler(msgEvent) {
|
||||
const data = msgEvent.detail;
|
||||
const filepath = data.filepath;
|
||||
const nodeID = parseInt(data.nodeID, 10);
|
||||
if (Number.isNaN(nodeID)) return false;
|
||||
|
||||
if(data.success && filepath) {
|
||||
const node = app.graph.getNodeById(nodeID);
|
||||
if(node !== null && node !== undefined) {
|
||||
const filepathWidget = node.widgets.find(widget => widget.name === "filepath");
|
||||
if(filepathWidget) {
|
||||
if(filepathWidget.value !== undefined) {
|
||||
filepathWidget.value = filepath;
|
||||
}
|
||||
if(filepathWidget.inputEl) {
|
||||
filepathWidget.inputEl.value = filepath;
|
||||
filepathWidget.inputEl.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
filepathWidget.inputEl.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function captionNodeHandler(msgEvent) {
|
||||
const captionData = msgEvent.detail;
|
||||
const captionText = captionData.captionText;
|
||||
@@ -3193,6 +3218,7 @@ export {
|
||||
syncDimensionsNodeHandler,
|
||||
searchNodeHandler,
|
||||
mediaUUIDHandler,
|
||||
save3DFilepathHandler,
|
||||
captionNodeHandler,
|
||||
videoTranscriptionHandler,
|
||||
videoOutputsHandler,
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
from .utils import runwareUtils as rwUtils
|
||||
import folder_paths
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class RunwareSave3D:
|
||||
"""Runware Save 3D node for saving 3D model files from URL"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"3dObject": ("STRING", {
|
||||
"tooltip": "3D file URL from Runware 3D Inference node."
|
||||
}),
|
||||
"filename_prefix": ("STRING", {
|
||||
"default": "ComfyUI",
|
||||
"tooltip": "Prefix for the filename."
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"filepath": ("STRING", {
|
||||
"placeholder": "Saved file path will appear here after execution.",
|
||||
"tooltip": "This field is automatically populated with the saved file path after save.",
|
||||
}),
|
||||
},
|
||||
"hidden": {"node_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("filepath",)
|
||||
FUNCTION = "save_3d"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "Runware"
|
||||
DESCRIPTION = "Save 3D model files (GLB/PLY) from Runware 3D Inference to the output folder."
|
||||
|
||||
def save_3d(self, **kwargs):
|
||||
"""Save 3D file from URL"""
|
||||
file_url = kwargs.get("3dObject", "")
|
||||
filename_prefix = kwargs.get("filename_prefix", "ComfyUI")
|
||||
|
||||
if not file_url or not file_url.strip():
|
||||
raise Exception("No 3D URL provided. Please connect the '3dObject' output from Runware 3D Inference node.")
|
||||
|
||||
file_url = file_url.strip()
|
||||
|
||||
# Get output directory
|
||||
output_dir = folder_paths.get_output_directory()
|
||||
threed_output_dir = os.path.join(output_dir, "3d_models")
|
||||
os.makedirs(threed_output_dir, exist_ok=True)
|
||||
|
||||
# Get extension from URL (handle query parameters)
|
||||
url_without_params = file_url.split('?')[0]
|
||||
extension = url_without_params.split('.')[-1].lower()
|
||||
|
||||
if extension not in ["glb", "ply"]:
|
||||
raise Exception(f"Unsupported 3D file extension '{extension}' from URL. Expected .glb or .ply.")
|
||||
|
||||
# Sanitize filename_prefix to prevent path traversal or arbitrary subdir creation
|
||||
prefix_basename = os.path.basename(
|
||||
filename_prefix.replace(os.sep, "_").replace(os.altsep or os.sep, "_")
|
||||
)
|
||||
safe_prefix = re.sub(r"[^\w\-]", "", prefix_basename) or "ComfyUI"
|
||||
|
||||
# Generate filename with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"{safe_prefix}_{timestamp}.{extension}"
|
||||
filepath = os.path.join(threed_output_dir, filename)
|
||||
|
||||
# Download with retry logic
|
||||
max_retries = 10
|
||||
retry_delays = [2, 5, 10, 15, 20]
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = requests.get(file_url, stream=True, timeout=60)
|
||||
print(f"[Runware Save 3D] Download attempt {attempt + 1}: {response.status_code}")
|
||||
|
||||
if response.status_code == 422:
|
||||
print(f"[Runware Save 3D] Server still processing, waiting...")
|
||||
rwUtils.time.sleep(retry_delays[min(attempt, len(retry_delays) - 1)])
|
||||
continue
|
||||
|
||||
if response.status_code == 502:
|
||||
print(f"[Runware Save 3D] Server error (502), retrying...")
|
||||
rwUtils.time.sleep(retry_delays[min(attempt, len(retry_delays) - 1)])
|
||||
continue
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
with open(filepath, 'wb') as f:
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
f.write(chunk)
|
||||
|
||||
print(f"[Runware Save 3D] Successfully saved to: {filepath}")
|
||||
|
||||
# Send filepath to UI (like media upload mediaUUID)
|
||||
rwUtils.sendSave3DFilepath(filepath, kwargs.get("node_id"))
|
||||
|
||||
return {"result": (filepath,), "ui": {"text": (f"Saved: {filename}",)}}
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"[Runware Save 3D] Attempt {attempt + 1} failed: {e}")
|
||||
if attempt < max_retries - 1:
|
||||
rwUtils.time.sleep(retry_delays[min(attempt, len(retry_delays) - 1)])
|
||||
continue
|
||||
else:
|
||||
raise Exception(f"Failed to download 3D file after {max_retries} attempts: {e}")
|
||||
except Exception as e:
|
||||
print(f"[Runware Save 3D] Unexpected error: {e}")
|
||||
raise Exception(f"Failed to save 3D file: {e}")
|
||||
|
||||
raise Exception("Failed to download 3D file: max retries exceeded")
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"RunwareSave3D": RunwareSave3D,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"RunwareSave3D": "Runware Save 3D",
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
from .utils import runwareUtils as rwUtils
|
||||
import comfy.model_management
|
||||
|
||||
|
||||
class threeDInference:
|
||||
"""Runware 3D Inference node for generating 3D models from images"""
|
||||
|
||||
# Available 3D models
|
||||
THREED_MODELS = {
|
||||
"Meta SAM 3D": "meta:sam@3d",
|
||||
}
|
||||
|
||||
# Output formats
|
||||
OUTPUT_FORMATS = ["GLB", "PLY"]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"Model": (list(cls.THREED_MODELS.keys()), {
|
||||
"tooltip": "Select the 3D generation model to use.",
|
||||
"default": "Meta SAM 3D",
|
||||
}),
|
||||
"positivePrompt": ("STRING", {
|
||||
"multiline": True,
|
||||
"placeholder": "Describe the 3D object you want to generate...",
|
||||
"tooltip": "A text prompt describing the 3D object to generate."
|
||||
}),
|
||||
"seed": ("INT", {
|
||||
"tooltip": "Seed for reproducibility. Use the same seed to get the same result.",
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 2147483647,
|
||||
}),
|
||||
"outputFormat": (cls.OUTPUT_FORMATS, {
|
||||
"tooltip": "Output format for the 3D model file.",
|
||||
"default": "GLB",
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"inputs": ("RUNWARE3DINFERENCEINPUTS", {
|
||||
"tooltip": "Connect a Runware 3D Inference Inputs node to provide image and mask inputs.",
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
DESCRIPTION = "Generate 3D models from images using Runware's 3D Inference API. Connect the output to 'Runware Save 3D' node to save the file."
|
||||
FUNCTION = "generate3D"
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("3dObject",)
|
||||
CATEGORY = "Runware"
|
||||
|
||||
def generate3D(self, **kwargs):
|
||||
"""Generate 3D model from inputs"""
|
||||
modelName = kwargs.get("Model", "Meta SAM 3D")
|
||||
positivePrompt = kwargs.get("positivePrompt", "")
|
||||
seed = kwargs.get("seed", 1)
|
||||
outputFormat = kwargs.get("outputFormat", "GLB")
|
||||
inputs = kwargs.get("inputs", None)
|
||||
|
||||
# Get model AIR code
|
||||
model = self.THREED_MODELS.get(modelName, "meta:sam@3d")
|
||||
|
||||
# Build generation config
|
||||
genConfig = [
|
||||
{
|
||||
"taskType": "3dInference",
|
||||
"taskUUID": rwUtils.genRandUUID(),
|
||||
"model": model,
|
||||
"numberResults": 1,
|
||||
"outputType": "URL",
|
||||
"includeCost": True,
|
||||
"deliveryMethod": "async",
|
||||
"outputFormat": outputFormat,
|
||||
}
|
||||
]
|
||||
|
||||
# Add positivePrompt if provided
|
||||
if positivePrompt and positivePrompt.strip() != "":
|
||||
genConfig[0]["positivePrompt"] = positivePrompt.strip()
|
||||
|
||||
# Add seed
|
||||
genConfig[0]["seed"] = seed
|
||||
|
||||
# Handle inputs from 3D Inference Inputs node
|
||||
if inputs is not None and isinstance(inputs, dict) and len(inputs) > 0:
|
||||
genConfig[0]["inputs"] = inputs
|
||||
|
||||
try:
|
||||
print(f"[DEBUG] Sending 3D Inference Request:")
|
||||
print(f"[DEBUG] Request Payload: {rwUtils.safe_json_dumps(genConfig, indent=2)}")
|
||||
|
||||
# Send initial request
|
||||
genResult = rwUtils.inferenecRequest(genConfig)
|
||||
|
||||
print(f"[DEBUG] Received 3D Inference Response:")
|
||||
print(f"[DEBUG] Response: {rwUtils.safe_json_dumps(genResult, indent=2)}")
|
||||
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
# Extract task UUID for polling
|
||||
taskUUID = genConfig[0]["taskUUID"]
|
||||
|
||||
# Poll for 3D generation completion
|
||||
while True:
|
||||
# Check for interrupt before each poll
|
||||
comfy.model_management.throw_exception_if_processing_interrupted()
|
||||
|
||||
# Poll for result
|
||||
pollResult = rwUtils.pollVideoResult(taskUUID) # Reuse existing poll function
|
||||
print(f"[Debugging] Poll result: {rwUtils.safe_json_dumps(pollResult, indent=2) if isinstance(pollResult, (dict, list)) else pollResult}")
|
||||
|
||||
# Check for errors
|
||||
if pollResult and "errors" in pollResult and len(pollResult["errors"]) > 0:
|
||||
error_info = pollResult["errors"][0]
|
||||
error_message = error_info.get("message", "Unknown error")
|
||||
|
||||
if "responseContent" in error_info:
|
||||
response_content = error_info["responseContent"]
|
||||
if isinstance(response_content, str):
|
||||
detailed_message = response_content
|
||||
elif isinstance(response_content, dict):
|
||||
detailed_message = response_content.get("message", str(response_content))
|
||||
else:
|
||||
detailed_message = str(response_content)
|
||||
|
||||
if detailed_message:
|
||||
error_message = f"{error_message}\nProvider Error: {detailed_message}"
|
||||
|
||||
task_uuid = error_info.get("taskUUID", "unknown")
|
||||
raise Exception(f"3D generation failed (Task: {task_uuid}): {error_message}")
|
||||
|
||||
# Check for successful completion
|
||||
if pollResult and "data" in pollResult and len(pollResult["data"]) > 0:
|
||||
data = pollResult["data"][0]
|
||||
|
||||
# Check if outputs.files exists (3D inference response format)
|
||||
outputs = data.get("outputs", {})
|
||||
files = outputs.get("files", [])
|
||||
|
||||
if len(files) > 0:
|
||||
file_info = files[0]
|
||||
file_url = file_info.get("url", "")
|
||||
|
||||
if file_url:
|
||||
print(f"[3D Inference] Generated 3D file URL: {file_url}")
|
||||
return (file_url,)
|
||||
|
||||
# Check status if available
|
||||
if "status" in data:
|
||||
status = data["status"]
|
||||
if status == "success":
|
||||
# Try alternative response format
|
||||
if "outputs" in data and "files" in data["outputs"]:
|
||||
files = data["outputs"]["files"]
|
||||
if len(files) > 0:
|
||||
file_url = files[0].get("url", "")
|
||||
if file_url:
|
||||
print(f"[3D Inference] Generated 3D file URL: {file_url}")
|
||||
return (file_url,)
|
||||
|
||||
# Check for interrupt before waiting
|
||||
comfy.model_management.throw_exception_if_processing_interrupted()
|
||||
|
||||
# Wait before next poll
|
||||
for _ in range(10): # 10 x 0.1 second = 1 second total
|
||||
comfy.model_management.throw_exception_if_processing_interrupted()
|
||||
rwUtils.time.sleep(0.1)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Runware3DInference": threeDInference,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Runware3DInference": "Runware 3D Inference",
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
from .utils import runwareUtils as rwUtils
|
||||
|
||||
|
||||
class threeDInferenceInputs:
|
||||
"""3D Inference Inputs node for configuring 3D generation inputs"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"image": ("IMAGE", {
|
||||
"tooltip": "Input image for 3D inference. This image will be used as the source for generating the 3D model."
|
||||
}),
|
||||
"mask": ("IMAGE", {
|
||||
"tooltip": "Optional mask image to specify a region of interest in the input image for 3D generation."
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
DESCRIPTION = "Configure custom inputs for Runware 3D Inference, including image and mask inputs."
|
||||
FUNCTION = "createInputs"
|
||||
RETURN_TYPES = ("RUNWARE3DINFERENCEINPUTS",)
|
||||
RETURN_NAMES = ("Inputs",)
|
||||
CATEGORY = "Runware"
|
||||
|
||||
def createInputs(self, **kwargs):
|
||||
"""Create 3D inference inputs from provided parameters"""
|
||||
image = kwargs.get("image", None)
|
||||
mask = kwargs.get("mask", None)
|
||||
|
||||
inputs = {}
|
||||
|
||||
if image is not None:
|
||||
inputs["image"] = rwUtils.convertTensor2IMG(image)
|
||||
|
||||
if mask is not None:
|
||||
inputs["mask"] = rwUtils.convertTensor2IMG(mask)
|
||||
|
||||
return (inputs,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Runware3DInferenceInputs": threeDInferenceInputs,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Runware3DInferenceInputs": "Runware 3D Inference Inputs",
|
||||
}
|
||||
@@ -295,6 +295,18 @@ def sendMediaUUID(mediaUUID, nodeID):
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def sendSave3DFilepath(filepath, nodeID):
|
||||
PromptServer.instance.send_sync(
|
||||
"runwareSave3DFilepath",
|
||||
{
|
||||
"success": True,
|
||||
"filepath": filepath,
|
||||
"nodeID": nodeID,
|
||||
"widgetName": "filepath",
|
||||
},
|
||||
)
|
||||
|
||||
def sendImageCaption(captionText, nodeID):
|
||||
PromptServer.instance.send_sync(
|
||||
"runwareImageCaption",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"id":"4f919008-d74f-4dcb-baaa-c9f3e0cfce69","revision":0,"last_node_id":10,"last_link_id":6,"nodes":[{"id":4,"type":"LoadImage","pos":[-102.60188293457031,287.9990234375],"size":[274.080078125,314],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"COMBO","widget":{"name":"image"},"link":null},{"localized_name":"choose file to upload","name":"upload","type":"IMAGEUPLOAD","widget":{"name":"upload"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[3]},{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"cnr_id":"comfy-core","ver":"0.3.46","Node name for S&R":"LoadImage"},"widgets_values":["hat.png","image"]},{"id":2,"type":"Runware 3D Inference Inputs","pos":[-49.6855354309082,709.256591796875],"size":[222.00253295898438,46],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"image","name":"image","shape":7,"type":"IMAGE","link":3},{"localized_name":"mask","name":"mask","shape":7,"type":"IMAGE","link":null}],"outputs":[{"localized_name":"Inputs","name":"Inputs","type":"RUNWARE3DINFERENCEINPUTS","links":[2]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"938185cd42fdc0a25095c8e72ba42fdcef10913e","Node name for S&R":"Runware 3D Inference Inputs"},"widgets_values":[],"bgcolor":"#5345bf"},{"id":7,"type":"Runware Save 3D","pos":[680.3914794921875,391.1221618652344],"size":[234.08380126953125,123.42192840576172],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"3dObject","name":"3dObject","type":"STRING","widget":{"name":"3dObject"},"link":4},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null},{"localized_name":"filepath","name":"filepath","shape":7,"type":"STRING","widget":{"name":"filepath"},"link":null}],"outputs":[{"localized_name":"filepath","name":"filepath","type":"STRING","links":[]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"ff6b7d8ad4471836cfbf1637e1ee1e24fe20bc48","Node name for S&R":"Runware Save 3D"},"widgets_values":["","ComfyUI","/Users/sirshenduganguly/Downloads/Runware-codebase/ComfyUI/output/3d_models/ComfyUI_20260130_101951.glb"],"bgcolor":"#5345bf"},{"id":1,"type":"Runware 3D Inference","pos":[215.56109619140625,360.20306396484375],"size":[400,200],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"inputs","name":"inputs","shape":7,"type":"RUNWARE3DINFERENCEINPUTS","link":2},{"localized_name":"Model","name":"Model","type":"COMBO","widget":{"name":"Model"},"link":null},{"localized_name":"positivePrompt","name":"positivePrompt","type":"STRING","widget":{"name":"positivePrompt"},"link":null},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"outputFormat","name":"outputFormat","type":"COMBO","widget":{"name":"outputFormat"},"link":null}],"outputs":[{"localized_name":"3dObject","name":"3dObject","type":"STRING","links":[4]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"938185cd42fdc0a25095c8e72ba42fdcef10913e","Node name for S&R":"Runware 3D Inference"},"widgets_values":["Meta SAM 3D","a hat",1383646106,"randomize","PLY"],"bgcolor":"#5345bf"}],"links":[[2,2,0,1,0,"RUNWARE3DINFERENCEINPUTS"],[3,4,0,2,0,"IMAGE"],[4,1,0,7,0,"STRING"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.6830134553650705,"offset":[565.2445846003944,-50.69034697604053]}},"version":0.4}
|
||||
Reference in New Issue
Block a user