简体   繁体   中英

Three.js performance very slow using onMouseMove with RayCaster

I'm building an application in three.js, however I'm having real problems with performance. This part of the application is based upon the Voxel Painter example . In my version, the user clicks on a cell to begin placement, drags the cursor to where they wish to end placement, and clicks to end.

function onDocumentMouseMove(event) {
    //set up mouse and raycaster
    event.preventDefault();
    mouse.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1);
    raycaster.setFromCamera(mouse, camera);

    switch (buildMode) {
        case buildModes.CORRIDOR:
            scene.add(rollOverFloor);

            var intersects = raycaster.intersectObjects(gridObject);
            if (intersects.length > 0) {

                var intersect = intersects[0];

                if (beginPlace == true) {
                    //store the intersection position
                    var endPlace = new THREE.Vector3(0, 0, 0);
                    endPlace.copy(intersect.point).add(intersect.face.normal);
                    endPlace.divideScalar(step).floor().multiplyScalar(step).addScalar(step / step);
                    endPlace.set(endPlace.x, 0, endPlace.z);

                    corridorDrag(endPlace);

                }
                //if user hasn't begun to place the wall
                else {
                    //show temporary wall on grid
                    rollOverFloor.position.copy(intersect.point).add(intersect.face.normal);
                    rollOverFloor.position.divideScalar(step).floor().multiplyScalar(step).addScalar(step / step);
                    rollOverFloor.position.set(rollOverFloor.position.x, 0, rollOverFloor.position.z);
                }
            }
            break;

    }
    render();
}

The code above is called when the user moves the mouse (there are many buildmodes in the main application, but I have not included them here). This function simply gets a start and end point, the corridorDrag() function fills in the cells between the start and end points:

function corridorDrag(endPlace) {
    deleteFromScene(stateType.CORRIDOR_DRAG);

    var startPoint = startPlace;
    var endPoint = endPlace;
    var zIntersect = new THREE.Vector3(startPoint.x, 0, endPoint.z);
    var xIntersect = new THREE.Vector3(endPoint.x, 0, startPoint.z);

    var differenceZ = Math.abs(startPlace.z - zIntersect.z);
    var differenceX = Math.abs(startPlace.x - xIntersect.x);

    var mergedGeometry = new THREE.Geometry();

    for (var i = 0; i <= (differenceZ / step); i++) {
        for (var j = 0; j <= (differenceX / step); j++) {
            var x = startPlace.x;
            var y = startPlace.y;
            var z = startPlace.z;

            if (endPoint.x <= (startPlace.x )) {
                if (endPoint.z <= (startPlace.z)) {
                    x = x - (step * j);
                    z = z - (step * i);
                }
                else if (endPoint.z >= (startPlace.z)) {
                    x = x - (step * j);
                    z = z + (step * i);
                }
            } else if (endPoint.x >= (startPlace.x)) {
                if (endPoint.z <= (startPlace.z)) {
                    x = x + (step * j);
                    z = z - (step * i);
                }
                else if (endPoint.z >= (startPlace.z)) {
                    x = x + (step * j);
                    z = z + (step * i);
                }
            }

            floorGeometry.translate(x, y, z);
            mergedGeometry.merge(floorGeometry);
            floorGeometry.translate(-x, -y, -z);
        }
    }

    var voxel = new THREE.Mesh(mergedGeometry, tempMaterial);
    voxel.state = stateType.CORRIDOR_DRAG;

    scene.add(voxel);
    tempObjects.push(voxel);
}

Firstly, the deleteFromScene() function removes all current highlighted cells from the scene (see below). The code then (I believe), should create a number of meshes, depending on the start and end points, and add them to the scene.

 function deleteFromScene(state) {
    tempObjects = [];
    var i = scene.children.length;
    while (i--) {
        if (scene.children[i].state != undefined)
            if (scene.children[i].state == state)
                scene.children.splice(i, 1);
    }
}

As I said, it is very, very slow. It also appears to be adding an obscene amount of vertices to the renderer, as seen in the WebGLRenderer stats window. I have no idea why it's adding so many vertices, but I'm assuming that's why it's rendering so slowly.

The application can be viewed here - the problem can be seen by clicking on one cell, dragging the cursor to the other end of the grid, and observing the time taken to fill in the cells.

Thank you in advance, this really is a last resort.

A few years ago Twitter put out an update. In this update they had just introduced infinite scrolling and on the day of its release the update was crashing users browsers. Twitter engineers did some investigating and found that the crashes were the result of the scroll event firing hundreds of times a second.

Mouse events can fire many MANY times a second and can cause your code to execute too often, which slows down the browser and (in many cases) crashes it. The solution for Twitter (and hopefully you) was simple: Poll your event.

Inside your mousemove event handler check that it has been some number of milliseconds since the last move event.

var lastMove = Date.now();

function onDocumentMouseMove(event) {
    if (Date.now() - lastMove < 31) { // 32 frames a second
        return;
    } else {
        lastMove = Date.now();
    }

    // your code here
}

I hope that helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM