Search code examples
3dthree.jsdrag-and-dropintersection

Drag a mesh over another and limit it within the sides three.js


I have to create a house like structure where user will be able to add windows on wall. The approach I am thinking is to first create a separate mesh for window which user can drag over the selected wall of the house mesh, and drops where they feel it suitable, but within the same wall or same side of the house mesh. After that I will create the whole scene again but draw the window in the wall of the house mesh instead of creating a separate window mesh.

Following is what it will look like when a window mesh is seen over a wall- window over wall

I am able to drag the window over wall using the DragControls.

dragControls = new THREE.DragControls( objects, camera, renderer.domElement );

but don't know how to limit the dragging so that window cannot go outside of the wall.

Its a working sample where I have created building mesh and window mesh which can be dragged - fiddle.


Solution

  • You can do it without THREE.DragControls(). Just check, if the ray of your raycaster is intersecting the wall/building and, if it is, set window's position at the point of intersection.

    There's a rough solution, which can be just the starting point for your creativity:

    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    
    var building = new THREE.Mesh(...); // wall/building
    var _window = new THREE.Mesh(...);  // window
    
    var raycaster = new THREE.Raycaster();
    var mouse = new THREE.Vector2();
    var intersects;
    var normalMatrix = new THREE.Matrix3();
    var worldNormal = new THREE.Vector3();
    var lookAtVector = new THREE.Vector3();
    var dragging = false;
    
    window.addEventListener("mousemove", onMouseMove, false);
    window.addEventListener("mousedown", onMouseDown, false);
    window.addEventListener("mouseup", onMouseUp, false);
    
    function onMouseDown(event) {
        if (intersects.length > 0) {
            controls.enableRotate = false;
            dragging = true;
        }
    }
    
    function onMouseUp(event) {
        controls.enableRotate = true;
        dragging = false;
    }
    
    function onMouseMove(event) {
        mouse.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1);
        raycaster.setFromCamera(mouse, camera);
        intersects = raycaster.intersectObjects([building]);
    
        if (intersects.length == 0 || !dragging) return;
    
        normalMatrix.getNormalMatrix(intersects[0].object.matrixWorld);
        worldNormal.copy(intersects[0].face.normal).applyMatrix3(normalMatrix).normalize();
        _window.position.copy(intersects[0].point);
        _window.lookAt(lookAtVector.copy(intersects[0].point).add(worldNormal));
    }
    

    jsfiddle example r87