From 7bba1dd89aea3b00c0a2954c504d2187debd7ab5 Mon Sep 17 00:00:00 2001 From: kijai <40791699+kijai@users.noreply.github.com> Date: Sun, 2 Feb 2025 13:57:03 +0200 Subject: [PATCH] Add upload mesh node --- nodes.py | 37 ++++++++++++++++++ web/js/jsnodes.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/nodes.py b/nodes.py index 448cbc5..ee55f54 100644 --- a/nodes.py +++ b/nodes.py @@ -958,6 +958,41 @@ class Hy3DLoadMesh: return (mesh,) +class Hy3DUploadMesh: + @classmethod + def INPUT_TYPES(s): + mesh_extensions = ['glb', 'gltf'] + input_dir = folder_paths.get_input_directory() + files = [] + for f in os.listdir(input_dir): + if os.path.isfile(os.path.join(input_dir, f)): + file_parts = f.split('.') + if len(file_parts) > 1 and (file_parts[-1] in mesh_extensions): + files.append(f) + return { + "required": { + "mesh": (sorted(files),), + } + } + RETURN_TYPES = ("HY3DMESH",) + RETURN_NAMES = ("mesh",) + OUTPUT_TOOLTIPS = ("The glb model with mesh to texturize.",) + + FUNCTION = "load" + CATEGORY = "Hunyuan3DWrapper" + DESCRIPTION = "Loads a glb model from the given path." + + def load(self, mesh): + path = mesh.strip() + if path.startswith("\""): + path = path[1:] + if path.endswith("\""): + path = path[:-1] + mesh_file = folder_paths.get_annotated_filepath(path) + loaded_mesh = trimesh.load(mesh_file, force="mesh") + + return (loaded_mesh,) + class Hy3DGenerateMesh: @classmethod @@ -1504,6 +1539,7 @@ NODE_CLASS_MAPPINGS = { "Hy3DTorchCompileSettings": Hy3DTorchCompileSettings, "Hy3DPostprocessMesh": Hy3DPostprocessMesh, "Hy3DLoadMesh": Hy3DLoadMesh, + "Hy3DUploadMesh": Hy3DUploadMesh, "Hy3DCameraConfig": Hy3DCameraConfig, "Hy3DMeshUVWrap": Hy3DMeshUVWrap, "Hy3DSampleMultiView": Hy3DSampleMultiView, @@ -1534,6 +1570,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "Hy3DTorchCompileSettings": "Hy3D Torch Compile Settings", "Hy3DPostprocessMesh": "Hy3D Postprocess Mesh", "Hy3DLoadMesh": "Hy3D Load Mesh", + "Hy3DUploadMesh": "Hy3D Upload Mesh", "Hy3DCameraConfig": "Hy3D Camera Config", "Hy3DMeshUVWrap": "Hy3D Mesh UV Wrap", "Hy3DSampleMultiView": "Hy3D Sample MultiView", diff --git a/web/js/jsnodes.js b/web/js/jsnodes.js index a8a4d98..d5444dd 100644 --- a/web/js/jsnodes.js +++ b/web/js/jsnodes.js @@ -1,4 +1,5 @@ import { app } from "../../../scripts/app.js"; +import { api } from "../../../scripts/api.js"; app.registerExtension({ name: "HY3D.jsnodes", @@ -25,8 +26,100 @@ app.registerExtension({ this.outputs[2]["name"] = values[1] + " faces" return r } - break; + break; + case "Hy3DUploadMesh": + addUploadWidget(nodeType, nodeData, "mesh"); + break; } }, -}); \ No newline at end of file +}); + +//file upload code from VHS nodes: https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite +async function uploadFile(file) { + //TODO: Add uploaded file to cache with Cache.put()? + try { + // Wrap file in formdata so it includes filename + const body = new FormData(); + const i = file.webkitRelativePath.lastIndexOf('/'); + const subfolder = file.webkitRelativePath.slice(0,i+1) + const new_file = new File([file], file.name, { + type: file.type, + lastModified: file.lastModified, + }); + body.append("image", new_file); + if (i > 0) { + body.append("subfolder", subfolder); + } + const resp = await api.fetchApi("/upload/image", { + method: "POST", + body, + }); + + if (resp.status === 200) { + return resp + } else { + alert(resp.status + " - " + resp.statusText); + } + } catch (error) { + alert(error); + } +} + +function addUploadWidget(nodeType, nodeData, widgetName) { + chainCallback(nodeType.prototype, "onNodeCreated", function() { + const pathWidget = this.widgets.find((w) => w.name === widgetName); + const fileInput = document.createElement("input"); + chainCallback(this, "onRemoved", () => { + fileInput?.remove(); + }); + + Object.assign(fileInput, { + type: "file", + accept: ".glb,.gltf,model/gltf-binary,model/gltf+json", + style: "display: none", + onchange: async () => { + if (fileInput.files.length) { + let resp = await uploadFile(fileInput.files[0]) + if (resp.status != 200) { + //upload failed and file can not be added to options + return; + } + const filename = (await resp.json()).name; + pathWidget.options.values.push(filename); + pathWidget.value = filename; + if (pathWidget.callback) { + pathWidget.callback(filename) + } + } + }, + }); + console.log(this) + document.body.append(fileInput); + let uploadWidget = this.addWidget("button", "choose glb file to upload", "image", () => { + //clear the active click event + app.canvas.node_widget = null + + fileInput.click(); + }); + uploadWidget.options.serialize = false; + }); +} + +function chainCallback(object, property, callback) { + if (object == undefined) { + //This should not happen. + console.error("Tried to add callback to non-existant object") + return; + } + if (property in object && object[property]) { + const callback_orig = object[property] + object[property] = function () { + const r = callback_orig.apply(this, arguments); + callback.apply(this, arguments); + return r + }; + } else { + object[property] = callback; + } +} \ No newline at end of file