The viewer JS/HTML/bundles are now committed in javascript/ (declared via pyproject [tool.comfy] web = "javascript") instead of being copied from the shared comfy-3d-viewers pip package at prestartup. Consequences: - requirements.txt is now just comfy-env (host env installs nothing else, per the never-touch-the-host-env principle) - prestartup_script.py no longer copies viewers or web_src; it keeps only setup_env() and the input-asset copies - WEB_DIRECTORY attribute removed; the pyproject declaration is the single source (served at /extensions/comfyui-geometrypack/, keyed by project name) - web_src/ deleted; its 6 widgets now live in javascript/js/ as the tracked source, edited directly - viewer_pbr.html hardcoded-fallback URL updated to the new mount name Iframe URLs auto-detect the mount from import.meta.url, so the folder rename and mount-name change are transparent at runtime.
219 lines
8.2 KiB
HTML
219 lines
8.2 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>GeomPack 3D Viewer</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
overflow: hidden;
|
|
background: #2a2a2a;
|
|
font-family: monospace;
|
|
}
|
|
#canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
}
|
|
#loading {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
color: white;
|
|
font-size: 14px;
|
|
text-align: center;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="canvas"></canvas>
|
|
<div id="loading">Ready to load mesh</div>
|
|
|
|
<script type="importmap">
|
|
{
|
|
"imports": {
|
|
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
|
|
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<script type="module">
|
|
import * as THREE from 'three';
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
|
|
|
|
console.log('[GeomPack Viewer] Initializing...');
|
|
|
|
const canvas = document.getElementById('canvas');
|
|
const loading = document.getElementById('loading');
|
|
|
|
// Scene
|
|
const scene = new THREE.Scene();
|
|
scene.background = new THREE.Color(0x2a2a2a);
|
|
|
|
// Camera
|
|
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|
camera.position.set(2, 2, 2);
|
|
|
|
// Renderer
|
|
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
|
|
// Controls
|
|
const controls = new OrbitControls(camera, canvas);
|
|
controls.enableDamping = true;
|
|
controls.dampingFactor = 0.05;
|
|
controls.zoomSpeed = 3.0; // 3x faster zoom
|
|
|
|
// Lighting
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
|
scene.add(ambientLight);
|
|
|
|
const dirLight1 = new THREE.DirectionalLight(0xffffff, 0.8);
|
|
dirLight1.position.set(5, 10, 7);
|
|
scene.add(dirLight1);
|
|
|
|
const dirLight2 = new THREE.DirectionalLight(0xffffff, 0.4);
|
|
dirLight2.position.set(-5, -5, -7);
|
|
scene.add(dirLight2);
|
|
|
|
// Grid
|
|
const grid = new THREE.GridHelper(10, 10, 0x444444, 0x222222);
|
|
scene.add(grid);
|
|
|
|
// Axes
|
|
const axes = new THREE.AxesHelper(1);
|
|
scene.add(axes);
|
|
|
|
// Current mesh
|
|
let currentMesh = null;
|
|
|
|
// Animation loop
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
controls.update();
|
|
renderer.render(scene, camera);
|
|
}
|
|
animate();
|
|
|
|
// Handle window resize
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
|
|
// Load mesh function
|
|
function loadMesh(filepath) {
|
|
console.log('[GeomPack Viewer] Loading:', filepath);
|
|
console.log('[GeomPack Viewer DEBUG] Full filepath URL:', filepath);
|
|
loading.textContent = 'Loading mesh...';
|
|
loading.style.display = 'block';
|
|
|
|
// Remove old mesh
|
|
if (currentMesh) {
|
|
scene.remove(currentMesh);
|
|
currentMesh = null;
|
|
}
|
|
|
|
// Check if filepath contains .glb or .gltf (even with query params)
|
|
const isGLB = filepath.includes('.glb') || filepath.includes('.gltf');
|
|
const loader = isGLB ? new GLTFLoader() : new OBJLoader();
|
|
|
|
console.log('[GeomPack Viewer] File type detected:', isGLB ? 'GLB/GLTF' : 'OBJ');
|
|
console.log('[GeomPack Viewer DEBUG] Using loader:', loader.constructor.name);
|
|
console.log('[GeomPack Viewer DEBUG] About to call loader.load()...');
|
|
|
|
loader.load(
|
|
filepath,
|
|
(result) => {
|
|
console.log('[GeomPack Viewer DEBUG] Loader success callback called');
|
|
console.log('[GeomPack Viewer DEBUG] Result:', result);
|
|
|
|
currentMesh = isGLB ? result.scene : result;
|
|
console.log('[GeomPack Viewer DEBUG] currentMesh:', currentMesh);
|
|
|
|
// Center mesh
|
|
const box = new THREE.Box3().setFromObject(currentMesh);
|
|
const center = box.getCenter(new THREE.Vector3());
|
|
currentMesh.position.sub(center);
|
|
|
|
console.log('[GeomPack Viewer DEBUG] Mesh bounds:', box);
|
|
console.log('[GeomPack Viewer DEBUG] Mesh center:', center);
|
|
|
|
// Add material if needed
|
|
let meshCount = 0;
|
|
currentMesh.traverse((child) => {
|
|
if (child.isMesh) {
|
|
meshCount++;
|
|
if (!child.material) {
|
|
child.material = new THREE.MeshStandardMaterial({
|
|
color: 0x88aacc,
|
|
metalness: 0.3,
|
|
roughness: 0.7
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log('[GeomPack Viewer DEBUG] Mesh count in scene:', meshCount);
|
|
|
|
// Add to scene
|
|
scene.add(currentMesh);
|
|
console.log('[GeomPack Viewer DEBUG] Mesh added to scene');
|
|
|
|
// Adjust camera
|
|
const size = box.getSize(new THREE.Vector3());
|
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
const distance = maxDim * 2.5;
|
|
camera.position.set(distance, distance, distance);
|
|
camera.lookAt(0, 0, 0);
|
|
controls.target.set(0, 0, 0);
|
|
controls.update();
|
|
|
|
console.log('[GeomPack Viewer DEBUG] Camera adjusted to distance:', distance);
|
|
|
|
loading.style.display = 'none';
|
|
console.log('[GeomPack Viewer] Mesh Loaded successfully');
|
|
},
|
|
(progress) => {
|
|
if (progress.lengthComputable) {
|
|
const percent = (progress.loaded / progress.total * 100).toFixed(0);
|
|
loading.textContent = `Loading mesh... ${percent}%`;
|
|
}
|
|
},
|
|
(error) => {
|
|
console.error('[GeomPack Viewer] Error loading mesh:', error);
|
|
console.error('[GeomPack Viewer DEBUG] Error type:', error?.constructor?.name);
|
|
console.error('[GeomPack Viewer DEBUG] Error message:', error?.message);
|
|
console.error('[GeomPack Viewer DEBUG] Error stack:', error?.stack);
|
|
loading.textContent = 'Error loading mesh - check console';
|
|
loading.style.color = '#ff6666';
|
|
}
|
|
);
|
|
}
|
|
|
|
// Listen for messages from parent window
|
|
window.addEventListener('message', (event) => {
|
|
console.log('[GeomPack Viewer DEBUG] Received postMessage:', event.data);
|
|
console.log('[GeomPack Viewer DEBUG] Message type:', event.data?.type);
|
|
console.log('[GeomPack Viewer DEBUG] Message filepath:', event.data?.filepath);
|
|
|
|
if (event.data.type === 'LOAD_MESH') {
|
|
console.log('[GeomPack Viewer] Received LOAD_MESH message:', event.data.filepath);
|
|
loadMesh(event.data.filepath);
|
|
} else {
|
|
console.warn('[GeomPack Viewer DEBUG] Unknown message type or missing type');
|
|
}
|
|
});
|
|
|
|
console.log('[GeomPack Viewer] Ready');
|
|
console.log('[GeomPack Viewer DEBUG] Waiting for LOAD_MESH postMessage...');
|
|
</script>
|
|
</body>
|
|
</html>
|