Three.js - Push away and then restore elements pos

2020-04-07 19:09发布

Sample of what I want to achieve

Hi all, I am developing a project in Three.js where the user can hover a tessellated face, and each mesh should be pushed away whenever it intersects and invisible sphere and, go back to its original position when its outside from its area. I am using this and this example as reference, but I got stuck since I don't know how to make it work in Three.js.

In my code, I am able to hover each face of a mesh, make it red and restore its original colour after that the mouse is gone. But I can't do something similar with the position. This is the part of the code where I think the issue is:

if (intersects.length > 0) {

    // if the closest object intersected is not the currently stored intersection object
    if (intersects[0].object != INTERSECTED) {

        // Restore previous intersection objects (if they exist) to their original color
        if (INTERSECTED) {
            INTERSECTED.face.color.setHex(INTERSECTED.currentHex);
        }

        // Intersected elements
        INTERSECTED = intersects[0];
        var intGeometry = INTERSECTED.object.geometry;
        var intFace     = INTERSECTED.face;

        // Difference between intersected faces and geometry
        INTERSECTED.currentHex = intFace.color.getHex();
        intFace.color.setHex(0xc0392b);
        intGeometry.colorsNeedUpdate = true;

        // Identify the vertices of each face
        var intVertices = intGeometry.vertices;
        var v1 = intVertices[intFace.a],
            v2 = intVertices[intFace.a],
            v3 = intVertices[intFace.a];

        // Calculate the centroid of the selected face
        var intPosition = new THREE.Vector3();
        intPosition.x = (v1.x + v2.x + v3.x) / 3;
        intPosition.y = (v1.y + v2.y + v3.y) / 3;
        intPosition.z = (v1.z + v2.z + v3.z) / 3;
        console.log(intPosition);
    }

}

Via console.log() I can see that the faces are correctly recognised together with also their position, but the sphere is not tracking the mouse properly and I need help with the position. This is a JSFiddle with the full code.

3条回答
【Aperson】
2楼-- · 2020-04-07 19:59

You are not assigning something back to your geometry. Something to the effect:

intVertices[intFace.a].copy( intPosition );
intVertices[intFace.b].copy( intPosition );
intVertices[intFace.c].copy( intPosition );

intGeometry.verticesNeedUpdate = true;
intGeometry.normalsNeedUpdate = true;
查看更多
\"骚年 ilove
3楼-- · 2020-04-07 20:04

Here is a small test about the subject: https://jsfiddle.net/77xvepp7/

It does not work properly yet, I moved the camera a bit further so the idea can be seen.

The basic idea is to pick one vertex from the surface as the "base point", it could be the center of the surface as you did in your example, and keep that point as the reference point to move all other points in the triangle by same amount. If you do not save the original value, then the next round may have invalid values and the triangles will move a bit randomly.

Then you calculate the distance of the mouse to the "base point", go through all the vertices and move them to the direction of the base point some amount. If the distance is greater than some defined amount, restore the original value from the vertex.

You might try to do that using vertex shader, but the problem is that when the mouse is over the surface each vertex would go to opposite direction and the triangle which is under the mouse would scale to fill the void. Selecting one point as the "base point" removes this problem.

  // Modify the geometry
  var geometry = backgroundMesh.geometry;
    for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {

        var face = geometry.faces[ i ];

        if ( face instanceof THREE.Face3 ) {

            var a = geometry.vertices[face.a];
            var b = geometry.vertices[face.b];
            var c = geometry.vertices[face.c];
            var vList = [a,b,c];

            // The trick is to save the original face positions
            if(!a.origXSet) {
               a.origX = a.x;
               a.origY = a.y;
               a.origZ = a.z;
               a.origXSet = true;
            }

            var vect = a;
            var dx = (vect.origX - pos.x), 
                dy = (vect.origY - pos.y), 
                dz = (vect.origZ - pos.z);

            // distance to the mouse
            var dist = Math.sqrt( dx*dx + dy*dy);

            // magic constant, react if distance smaller than 4
            if(dist < 4) {
              vList.forEach(function(v) {
              if(!v.origXSet) {
                   v.origX = v.x;
                   v.origY = v.y;
                   v.origZ = v.z;
                   v.origXSet = true;
               }
               var len = Math.sqrt(dx*dx + dy*dy + dz*dz);
               if(len==0) return;
               var ndx = dx / len,
                   ndy = dy / len,
                   ndz = dz / len;
               v.x = v.origX + ndx * 2; // move 2 pixels
               v.y = v.origY + ndy * 2; 
               v.z = v.origZ + ndz * 2;
      });

      } else {
          // otherwise, reset coordinates
          vList.forEach(function(v) {
            if(v.origXSet) {
             v.x = v.origX;
             v.y = v.origY;   
             v.z = v.origZ;
             }
           });
      }

       geometry.verticesNeedUpdate = true;
       geometry.normalsNeedUpdate = true;    

    }
 }

The main problems are: the position of the mouse pointer in the World Coordinates is not correctly calculated. Right now I don't have the energy to think about how to correct that, but decided to post this answer so that you can continue from that.

查看更多
够拽才男人
4楼-- · 2020-04-07 20:08

the problem you having it that the targetList that you try ray.intersectObjects is not a THREE.Mesh instance. because of that when you try to access to intersectedFace.acc it's throw an error

查看更多
登录 后发表回答