import * as THREE from 'https://unpkg.com/three@0.127.0/build/three.module.js'; import {OrbitControls} from 'https://unpkg.com/three@0.127.0/examples/jsm/controls/OrbitControls.js'; import {GLTFLoader} from 'https://unpkg.com/three@0.127.0/examples/jsm/loaders/GLTFLoader.js'; import {DragControls} from 'https://unpkg.com/three@0.127.0/examples/jsm/controls/DragControls.js' let scene, camera, renderer, cube, isDrawing, pointerDown, controls; let frameIndex = 0; let pointCloudData; const now = Date.now(); let pointCloud let quaternionStart, positionStart; let isGenerating = false; const allModelsUrl = ['background.glb', 'basin.glb', 'bed.glb', 'flower.glb', 'kitchen_chair_1.glb', 'kitchen_chair_2.glb', 'office_chair.glb', 'sofa.glb', 'table.glb', 'wc.glb']; // const allModelsUrl = ['table.glb'] const allModels_dict = {} const allModels_list = [] let initLoc = {} function init() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); // camera.position.set(9, 6, -9); camera.position.set(3, 13, 0); // var qm = new THREE.Quaternion(0.1, 0.2, 0.3, 0.4); // camera.quaternion.copy(qm) // camera.updateMatrixWorld(); scene.add(camera) renderer = new THREE.WebGLRenderer({alpha: true, antialias: true}); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap // document.body.appendChild(renderer.domElement); const canvasContainer = document.getElementById('canvas-container'); canvasContainer.appendChild(renderer.domElement); const planeGeometry = new THREE.PlaneGeometry(1000, 1000); const plane = new THREE.Mesh(planeGeometry, new THREE.MeshBasicMaterial({ color: 0x000000, opacity: 0.25, transparent: true })); plane.rotation.x = -Math.PI / 2; plane.visible = false; // Make the plane invisible scene.add(plane); let points = []; const line_geometry = new THREE.BufferGeometry().setFromPoints(points); const line_material = new THREE.LineBasicMaterial({ color: 0x0000ff, linewidth: 3 }); const line = new THREE.Line(line_geometry, line_material); scene.add(line); controls = new OrbitControls(camera, renderer.domElement); // controls.addEventListener('change', render); // const quaternion = new THREE.Quaternion(-0.707, 0, 0, 0.707) // camera.quaternion.copy(quaternion) // const light = new THREE.HemisphereLight(0xffffbb, 0x080820, 1); // scene.add(light); const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); // soft white light scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(0, 10, 5 ); directionalLight.castShadow = true; scene.add(directionalLight); const grid = new THREE.GridHelper(30, 30); scene.add(grid); // Handle dynamic point cloud const geometry = new THREE.BufferGeometry(); const points_frame = new Float32Array(1048 * 3); geometry.setAttribute('position', new THREE.BufferAttribute(points_frame, 3)); const material = new THREE.PointsMaterial({ size: 0.04, color: 0x2020e0 }); pointCloud = new THREE.Points(geometry, material); scene.add(pointCloud); for (let i = 0; i < allModelsUrl.length; i++) { const assetLoader = new GLTFLoader(); assetLoader.load('/static/'.concat(allModelsUrl[i]), function (gltf) { const model = gltf.scene; if (i !== 0) {allModels_dict[allModelsUrl[i]] = model; allModels_list.push(model); initLoc[allModelsUrl[i]] = model.children[0].position.clone() } scene.add(model); }, undefined, function (error) { console.error(error); }); } // for (let [key, value] of Object.entries(allModels_dict)) { // initLoc[key] = value.children[0].position // } const dragControls = new DragControls(allModels_list, camera, renderer.domElement); dragControls.addEventListener('dragstart', function (event) { if (isDrawing) {return} controls.enabled = false; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObject(plane); if (intersects.length > 0) { const point = intersects[0].point; event.object.position.set(point.x, 0, point.z); } }); dragControls.addEventListener('dragend', function (event) { if (isDrawing) {return} controls.enabled = true; }); dragControls.addEventListener('drag', function (event) { if (isDrawing) {return} raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObject(plane); if (intersects.length > 0) { const point = intersects[0].point; event.object.position.set(point.x, 0, point.z); } }); // Raycaster for constraining movement to the plane const raycaster = new THREE.Raycaster(); // Mouse vector for the raycaster const mouse = new THREE.Vector2(); // Update the mouse vector with the current mouse position document.addEventListener('pointermove', function (event) { const rect = renderer.domElement.getBoundingClientRect(); mouse.x = ((event.x - rect.left) / rect.width) * 2 - 1; mouse.y = -((event.y - rect.top) / rect.height) * 2 + 1; if (isDrawing && pointerDown && points.length < 150) { raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObject(plane); const point = intersects[0].point; point.y = 0.1 points.push(point); line.geometry.setFromPoints(points); }}, false); // pointerDown = false; document.addEventListener('pointerdown', function (event) { pointerDown = true; }, false); document.addEventListener('pointerup', function (event) { pointerDown = false; isDrawing = false; controls.enabled = true dragControls.enabled = true document.getElementById('toggleDraw').innerHTML = "Draw Trajectory"; }, false); document.getElementById('toggleDraw').addEventListener('click', toggleDrawing); isDrawing = false; function toggleDrawing() { controls.enabled = false quaternionStart = camera.quaternion.clone(); positionStart = camera.position.clone(); points = [] isDrawing = true; dragControls.enabled = false document.getElementById('toggleDraw').innerHTML = "Drawing"; } document.getElementById('reset').addEventListener('click', reset); function reset() { points = []; line_geometry.setFromPoints(points); for (let [key, value] of Object.entries(allModels_dict)) { value.children[0].position.set(initLoc[key].x, initLoc[key].y, initLoc[key].z); } document.getElementById('toggleDraw').innerHTML = "Draw Trajectory"; pointCloudData = null; pointCloud.geometry.attributes.position.array = new Float32Array(1048 * 3); pointCloud.geometry.attributes.position.needsUpdate = true; render() } document.getElementById('generateMotion').addEventListener('click', runGeneration); function runGeneration() { if (!points.length || isGenerating) {return} isGenerating = true; const userData = {} for (let [key, value] of Object.entries(allModels_dict)) { userData[key] = value.children[0].position } userData['trajectory'] = points fetch('/move_cube', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData), }) .then(response => response.json()) .then(data => { pointCloudData = data document.getElementById("generateMotion").innerHTML = "Generate Motion"; isGenerating = false; }) .catch((error) => { console.error('Error:', error); }); } document.addEventListener('keypress', function(event) { const key = event.key; // "a", "1", "Shift", etc. if (key === 'q') {if (isDrawing) {isDrawing = false;} else {isDrawing = true;}} }); // Resize Listener window.addEventListener('resize', onWindowResize, false); animate(); } function updatePointCloud() { if (!pointCloudData) { return; } const positions = pointCloud.geometry.attributes.position.array; const frameData = pointCloudData[frameIndex]; for (let i = 0, j = 0; i < frameData.length; i++, j += 3) { positions[j] = frameData[i][0]; positions[j + 1] = frameData[i][1]; positions[j + 2] = frameData[i][2]; } pointCloud.geometry.attributes.position.needsUpdate = true; frameIndex += 1; if (frameIndex >= pointCloudData.length) frameIndex = 0; // Loop back to the start } function animate() { // requestAnimationFrame(animate); setTimeout(() => { requestAnimationFrame(animate); }, 1000 / 25); updatePointCloud() if (isGenerating) { let dotNum = parseInt((Date.now() - now) / 500) % 8 const dot = Array(dotNum * 2).join('.') document.getElementById("generateMotion").innerHTML = "Loading" + dot; } renderer.render(scene, camera); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); render(); } // function onDocumentKeyDown(event) { // var keyCode = event.which; // // Move the cube with arrow keys // if (keyCode == 87) { cube.position.y += 0.1; } // W key // if (keyCode == 83) { cube.position.y -= 0.1; } // S key // if (keyCode == 65) { cube.position.x -= 0.1; } // A key // if (keyCode == 68) { cube.position.x += 0.1; } // D key // sendCubePosition(); // } // function runGeneration() { // const position = { // x: cube.position.x, // y: cube.position.y, // z: cube.position.z // }; // // fetch('/move_cube', { // method: 'POST', // headers: { // 'Content-Type': 'application/json', // }, // body: JSON.stringify(position), // }) // .then(response => response.json()) // .then(data => { // // Use the data to create a new cube // const newGeometry = new THREE.BoxGeometry(); // const newMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // const newCube = new THREE.Mesh(newGeometry, newMaterial); // newCube.position.set(data.x, data.y, data.z); // scene.add(newCube); // }) // .catch((error) => { // console.error('Error:', error); // }); // } function render() { renderer.render(scene, camera); } // document.addEventListener("keydown", onDocumentKeyDown, false); init();