Files
lo-th-Comfyui_three_js/js/three_view.js
T
AlekPet ee351ea6ba Inplemented select model form list
Support saves selected model in list when reloaded and loaded page
browser.
Write backend code get models in json format, and create file json default models for local server.
Add buttons refresh model list
2024-12-06 18:55:48 +03:00

290 lines
9.8 KiB
JavaScript

import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
import { addStylesheet } from "../../scripts/utils.js";
import { $el } from "../../scripts/ui.js";
import { ThreeCanvas } from "./ThreeCanvas.js";
const DEBUG = true;
// Function create widget
async function widgetThreeJS(node, nodeData, inputData, app, params = {}) {
const d = new Date();
const canvasNames = [
{name:"image", color: "black"},
{name:"lines", color: "red"},
{name:"depth", color: "yellow"},
{name:"normal", color: "#8080ff"}
];
const base_filenames = canvasNames.map((data)=>`${nodeData.name}${node.id}-${d.getUTCFullYear()}_${d.getUTCMonth() + 1}_${d.getUTCDate()}_${data.name}.png`)
// Find widget stored image filename for threejs, and hide him.
const widgeImageThree = node.widgets.find((w) => w.name === "imageThreejs");
widgeImageThree.value = base_filenames.join()
widgeImageThree.type = "converted-widget";
widgeImageThree.computeSize = () => [0, -4];
if (widgeImageThree.linkedWidgets) {
for (const link of widgeImageThree.linkedWidgets) {
link.type = "converted-widget";
link.computeSize = () => [0, -4];
}
}
let widget = {};
const threeCanvas = new ThreeCanvas(node, widgeImageThree, params);
threeCanvas.setApi(api);
threeCanvas.init(canvasNames);
const panelWidget = node.addDOMWidget(
nodeData.name,
"threeCanvasPanel",
threeCanvas.panelWrapper,
{
getValue() {
return "panelWidgetValue";
},
}
);
panelWidget.computeSize = () => {
return [node.size[0], 45];
};
// end - Panel
// Add widget threeCanvas
widget = node.addDOMWidget("threeCanvas", "custom_widget", threeCanvas.threeWrapper, {
getValue() {
return threeCanvas.getSavedOptions();
},
});
const origDraw = widget.draw;
widget.draw = function () {
origDraw?.apply(this, arguments);
const [ctx, nodeThree, widgetWidth, posY] = arguments;
const w = widgetWidth - 25;
const aspect_ratio = threeCanvas.size.h / threeCanvas.size.w;
Object.assign(threeCanvas.threeWrapper.style, {
width: w + "px",
height: w * aspect_ratio + "px",
});
// Update renderer
// threeCanvas.update(node.size[0]);
//app.graph.setDirtyCanvas(true, false);
};
widget.threeCanvas = threeCanvas;
widget.threeWrapper = threeCanvas.threeWrapper;
document.body.addEventListener("threeview_model_added", ({detail})=>{
// console.log("Detail:", detail?.currentModel)
})
widget.callback = () => {};
// Note: If the node is collapsed, the draw method does not work and the canvas will not update.
// const animator = () => {
// threeCanvas.update(node.size[0]);
// app.graph.setDirtyCanvas(true, false);
// requestAnimationFrame(animator);
// };
// requestAnimationFrame(animator);
node.onResize = function () {
let [w, h] = this.size;
let aspect_ratio = 1;
aspect_ratio = threeCanvas.size.h / threeCanvas.size.w;
const buffer = 140;
if (w > 1024) w = w - (w - 1024);
if (w < 200) w = 200;
h = w * aspect_ratio + buffer;
if (h < 200) h = 200 + h / 2;
this.size = [w, h];
};
// Serialize node data, you can save the image here too
node.onSerialize = (n) => {
//console.log('YOOOOO')
};
node.onDragOver = function (e) {
return false;
};
node.onDragDrop = function (e) {
return false;
};
// https://docs.comfy.org/essentials/comms_messages
// api.addEventListener("execution_start", async () => {
// console.log("execution_start...");
// await threeCanvas.sendFileToServer(widgeImageThree.value);
// });
// Custom event save image
api.addEventListener("lth_save_image", async ({ detail }) => {
const { unique_id } = detail;
if (+unique_id !== node.id) return;
function reflect(promise){
return promise.then(function(v){ return {v:v, status: "fulfilled" }},
function(e){ return {e:e, status: "rejected" }});
}
const promises = base_filenames.map((v, idx)=>threeCanvas.sendFileToServer(v, idx))
Promise.all(promises.map(reflect)).then(async function (results) {
const success = results.filter(x => x.status === "fulfilled");
if (success.length === base_filenames.length) {
console.log("All images saved successfully. Notifying server...");
await api.fetchApi("/lth/save_complete", {
method: "POST",
body: JSON.stringify({
unique_id: node.id.toString(),
}),
});
} else {
console.error("Some images failed to save. Skipping server notification.");
}
});
});
return widget;
}
app.registerExtension({
name: "Three View",
async init(){
// Add css styles
addStylesheet("css/styles.css", import.meta.url);
},
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeType.comfyClass === "ThreeView") {
nodeType.prototype.onExecuted = async function (message) {
console.log("yooooo is running !!!!");
};
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = async function () {
const r = onNodeCreated
? onNodeCreated.apply(this, arguments)
: undefined;
const titleNode = await this.getTitle(); // need to load properties node
let parameters = {}
try {
parameters = {savedData: this?.widgets_values[2]}
} catch (error) {
}
// Create widget
const widget = await widgetThreeJS(
this,
nodeData,
nodeData,
app,
parameters
);
this.title = `${this.type} [${widget.threeCanvas.size.w}x${widget.threeCanvas.size.h}]`;
this?.onResize()
console.log("onCreate")
return r;
};
// onConfigure, load serialized values
const onConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = async function (w) {
onConfigure?.apply(this, arguments);
await this.getTitle(); // wait loaded node
if (w?.widgets_values?.length) {
const threeCanvasId = this.widgets.findIndex(
(wi) => wi.name === "threeCanvas"
);
if (threeCanvasId !== -1 && w?.widgets_values[threeCanvasId]) {
// Get widget threeCanvas by Index
const threeCnavasWidget = this.widgets[threeCanvasId].threeCanvas
// Get value size and options
const {size: sizeData, camera: cameraData, currentModel} = w.widgets_values[threeCanvasId];
// Set size
if(sizeData){
const { w: width, h: height } = sizeData;
if(width && height){
threeCnavasWidget.setCanvasSize(width, height);
this.title = `${this.type} [${width}x${height}]`;
}
}
// Loading last model added
if(currentModel){
let urlData = ""
if(currentModel.indexOf("assets/") === -1){
const [subfolder, name] = currentModel.split("/")
urlData = await api.apiURL(
`/view?filename=${encodeURIComponent(
name
)}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}`
);
} else {
urlData = new URL(currentModel, import.meta.url).href
}
await threeCnavasWidget.loaderGltf.load(urlData, ( glb ) => {
threeCnavasWidget.addModel( glb );
// Load camera data
if( cameraData ) threeCnavasWidget.loadCameraState( cameraData )
},
( data ) => {/* console.log( `Loaded data: ${data.loaded}/${data.total}` */},
( err ) => {
console.log( err );
threeCnavasWidget.addHeadTest();
})
}
console.log("onConfigure")
}
}
};
// When the node is executed we will be sent the input text, display this in the widget
/*const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (message) {
console.log("yooooo");
//onExecuted?.apply(this, arguments);
//populate.call(this, message.text);
};*/
}
},
});