4

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.

Nakilon
  • 34,866
  • 14
  • 107
  • 142
d_z90
  • 1,213
  • 2
  • 19
  • 48
  • 1
    For sphere to follow mouse, you need to convert screen coordinates to threejs world position. [Check this link](http://stackoverflow.com/questions/13055214/mouse-canvas-x-y-to-three-js-world-x-y-z) . And updated [fiddle here](https://jsfiddle.net/obsg9e5w/1/), changed code starts at line 375. – uhura Mar 16 '16 at 21:29
  • Thanks man! This was part of an issue of [another question](http://stackoverflow.com/questions/36033879/three-js-object-follow-mouse-position) I have asked, if you want you can post the answer there and get rewarded. – d_z90 Mar 17 '16 at 08:10

3 Answers3

4

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.

Tero Tolonen
  • 4,144
  • 4
  • 27
  • 32
1

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;
gaitat
  • 12,449
  • 4
  • 52
  • 76
  • Cool man! But how should I store and reset the position? Because now every block on `hover` disappears. – d_z90 Mar 17 '16 at 13:25
  • I guess you would need another array/buffer that holds the `state` of your vertices – gaitat Mar 17 '16 at 13:36
  • That's a possibility, I think that the way to achieve this is to push the centroid of a `tessellated face` out whenever the distance to the mouse is lower than the radius of a sphere. Then when it is out, send it back to the original position. What do you think? – d_z90 Mar 17 '16 at 13:48
  • why use the centroid (which is extra computations) rather the vertices themselves? – gaitat Mar 17 '16 at 14:09
0

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

Zamboney
  • 2,002
  • 12
  • 22