3

've been working on a game engine using the WebGL library Three.js. I have basic terrain collision working to the point where I can find the face the user is standing on. Using the average or maximum Y position of the faces vertices is not sufficient.

What formula can I use to find the exact Y position of an object relative to the terrain face that is "standing" on? Assuming I know the faces four vertex positions and the objects vector position.

enter image description here

The plane is a THREE.PlaneGeometry(2000,2000,128,128) augmented by a heightmap. The image above is one of it's faces.

Hobbes
  • 781
  • 10
  • 29
  • There is a geometry.computeCentroids() function you can use and then use the centroid of the face. – gaitat Apr 18 '13 at 09:07
  • I've done geo.computeCentroids() but the problem is the character is not always standing in the center of the face. – Hobbes Apr 18 '13 at 09:25
  • Since you are doing collision, you are already sending rays (I assume). When you succeed finding a face why dont you do ray-plane intersection to find the exact spot. Dont know if three.js has this build in. – gaitat Apr 18 '13 at 10:40
  • I'm using an alternate method to find the face. Ray projection performance was not as good as I'd liked it to be. Either way I'm left finding one face. I need to know the precise y position of the part of the face i am standing on. Since there are no vertices within this face (only on the corners), one could only extrapolate this data using some kind of math that i just don't know heh. – Hobbes Apr 18 '13 at 10:44

2 Answers2

5

I cannot put code in the comment so I am putting it here. You dont need to do the math. Let three.js do it for you. You can do something like the following:

var plane = new THREE.Plane();
plane.setFromCoplanarPoints (v1, v2, v3);

// x, y, z: position of your object
var ray = new THREE.Ray (new THREE.Vector3(x, y, z), new THREE.Vector3(0, 1, 0));
var where = ray.intersectPlane (plane);

and from the Vector3 'where' you use the y component to change the position of your object.

gaitat
  • 12,449
  • 4
  • 52
  • 76
  • Hmm this didn't work with my plane ( which is a THREE.PlaneGeometry(2000,2000,128,128) ), which has been augmented by a heightmap. ray.intersectPlane (mesh) gives the error "cannot call method 'dot' of undefined". Same thing happens when passing the geometry to the intersectPlane function. So, this function returns a vector not a face? If it returns the true vector of intersection (not the closest vertex of the planes geometry) I'd alter my terrain code to accommodate. I'm using r58. – Hobbes Apr 18 '13 at 11:52
  • v1, v2, v3 correspond to the vertices in your image. The 'plane' in the code has nothing to do with planeGeometry. And ray.intersectPlane does not take a mesh. Please look at the manual. – gaitat Apr 18 '13 at 12:03
  • I'm hesitant to rework the code to use the THREE.Plane object without knowing for sure that a plane created in this method can be subdivided and displaced by a height-map like THREE.PlaneGeometry can, and that using planeIntercept() returns a vector of the precise point of intersection, not just geometric data from the plane it hits. I know interceptObjects() returns the mesh, geometric data, distance and some other info but not the exact point of intersection which wont work here since the user could be (and often is) in-between vertices. – Hobbes Apr 18 '13 at 12:14
  • 1
    You will only create the plane for the intersection. Nothing more. Once you have the v1, v2, v3 vectors, create it, do the intersection and then discard it. – gaitat Apr 18 '13 at 12:23
  • Ok, instead of creating the plane every frame, I'm reusing the same plane for every detection and just re-positioning it. Moving now takes 1ms instead of 0ms, which i can live with. This method is not perfect, but it is much better. No longer are my characters floating on the faces max vertex positions =] Occasionally when i'm moving my feet will sink below the surface on steep inclines/declines, but nonetheless thanks. Voting up. – Hobbes Apr 18 '13 at 19:43
  • plane.setFromNormalAndCoplanarPoint(normal,centroid) works a little better since the the face has 4 vertices, and setFromCoplanarPoints(v1,v2,v3) only takes 3 points. Is there a better way to do this? – Hobbes Apr 18 '13 at 20:15
  • If you look at the code of setFromCoplanarPoints() it calls internally setFromNormalAndCoplanarPoint(). But if your v1,v2,v3,v4 were all coplanar, then it should not matter. Since you say it works better, that only means that the four vertices are not coplanar and you should intersect against triangle v1,v2,v4 and then also against v1,v4,v3. – gaitat Apr 18 '13 at 20:30
  • If I test it against the 2 vertex groups, do I take the average of the results? Sorry I slept through most of geometry class. I really appreciate your help. Edit - I just looked up the definition of coplanar (lol i know...), and the 4 points are coplanar, so im not sure why setting by normal and coplanar point is more accurate. Maybe its not and I'm just seeing things (happens... a lot...). – Hobbes Apr 18 '13 at 20:53
  • In the case that you wanted to check both triangles, the intersection would succeed only for one of them. Even if you were on their sharing edge (v1,v4) still no averaging would be required. – gaitat Apr 18 '13 at 20:57
  • Nice, that works quite well. Testing both vector groups both return a value, so i just take the max. On steep inclines my feet are still a little underground but to fix that I'd have to adjust x/z rotation of the model and I'm not sure i want to do that since people climbing a mountain are not perpendicular to the surface (well in cartoons sometimes). I wonder if I have the vectors assigned correctly. I'm doing v1 = face.a, v2 = face.b, v3 = face.c, v4 = face.d. – Hobbes Apr 18 '13 at 21:29
  • This has been working great! Works fine post r60 with some minor modifications. I was wondering (and I may need to do this as a separate question) if this can be done with PlaneBufferGeometry ? – Hobbes Nov 17 '14 at 17:54
  • To answer my own last question, yes it can be done with buffer geometry, but getting it setup was a bit complex (but totally worth it). – Hobbes Oct 09 '17 at 21:11
0

You can easily find the height of the face your object is standing on

const down = new THREE.Vector3(0, -1, 0);
const raycaster = new THREE.Raycaster();

raycaster.set(obj.position, down);
const collisionResults = raycaster.intersectObject(terrain);

if (collisionResults.length > 0 && collisionResults[0].distance > 0)
   const pointHeight = collisionResults[0].point.y;

at this point just subtract pointHeight from the y coordinate of your object, to get the relative height.

const relativeHeight = obj.position.y - pointHeight;
Pierfrancesco Soffritti
  • 1,678
  • 1
  • 18
  • 22
  • While this is most definitely the easiest way to pull this off, there is a caveat. My terrain for example is roughly 30K poly's, using this method is much slower than the answer from Gaitat. – Hobbes Oct 09 '17 at 21:11