Files
if-ai-ComfyUI-IF_Trellis/web/visualization.js
T
2024-12-13 19:15:11 +00:00

134 lines
4.4 KiB
JavaScript

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
class Visualizer {
constructor(node, container, visualSrc) {
this.node = node;
this.container = container;
// Create main container div
this.mainContainer = document.createElement('div');
this.mainContainer.style.position = 'relative';
this.mainContainer.style.width = '100%';
this.mainContainer.style.height = '100%';
container.appendChild(this.mainContainer);
// Setup Three.js scene
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, this.mainContainer.clientWidth / this.mainContainer.clientHeight, 0.1, 1000);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(this.mainContainer.clientWidth, this.mainContainer.clientHeight);
this.mainContainer.appendChild(this.renderer.domElement);
// Setup lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
this.scene.add(directionalLight);
// Setup camera and controls
this.camera.position.z = 5;
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
this.controls.dampingFactor = 0.05;
// Start animation loop
this.animate();
// Handle window resize
window.addEventListener('resize', () => this.onResize());
}
animate() {
requestAnimationFrame(() => this.animate());
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
onResize() {
const width = this.mainContainer.clientWidth;
const height = this.mainContainer.clientHeight;
this.camera.aspect = width / height;
this.camera.updateProjectionMatrix();
this.renderer.setSize(width, height);
}
updateVisual(params) {
if (params?.filename) {
// Load the GLB model
const loader = new GLTFLoader();
loader.load(params.filename, (gltf) => {
// Clear existing model
while(this.scene.children.length > 0) {
const obj = this.scene.children[0];
if (obj.type === "Mesh") {
obj.geometry.dispose();
obj.material.dispose();
}
this.scene.remove(obj);
}
// Add new model
this.scene.add(gltf.scene);
// Center and scale model
const box = new THREE.Box3().setFromObject(gltf.scene);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
const maxDim = Math.max(size.x, size.y, size.z);
const scale = 2.0 / maxDim;
gltf.scene.scale.multiplyScalar(scale);
gltf.scene.position.sub(center.multiplyScalar(scale));
});
}
}
remove() {
// Clean up Three.js resources
this.renderer.dispose();
this.controls.dispose();
this.container.remove();
}
}
// Update the onExecuted handler in registerVisualizer
nodeType.prototype.onExecuted = async function(message) {
const params = {};
// Handle mesh/3D model
if (message?.mesh) {
params.filename = message.mesh[0];
}
this.updateParameters(params);
};
// Add this to the createVisualizer function
node.onResize = function() {
let [w, h] = this.size;
const minWidth = 400;
const minHeight = 300;
// Enforce minimum dimensions
w = Math.max(w, minWidth);
h = Math.max(h, minHeight);
// Maintain aspect ratio
if (w > minWidth) {
h = Math.max(minHeight, w * 0.75); // 4:3 aspect ratio
}
this.size = [w, h];
// Update visualizer size if it exists
if (this.visualizer) {
this.visualizer.onResize();
}
};