Detecting position on surface of mesh after moving a point in mesh on a certain direction by a certain amount

@JohnK lets see if I can explain well what I need here,

  • say that I have a point selected on top of a mesh in babylon
  • now I calculate a direction, a vector in whose direction I want to move from that point, and an amount to move in that direction
  • now I need to move the point in that direction by that amount
  • however if I just add the direction multiplied by the amount to the position of the point, I will end up somewhere above or below the surface of the mesh
  • I then need to calculate what is the point in the surface of the mesh that corresponds to that position where i ended up after adding the direction vector to the position of the point?

that is what i need, how do I calculate the position on top of the surface of the mesh that corresponds to: originalPointOnMesh + amount * directionvector

thank you for any help :slight_smile:

That will work provided the directionvector is an unit vector that lies in the plane of the top. If the top of the mesh is parallel to the XZ plane then any vector of the form (x, 0, z).normalize() will be an appropriate directionvector. If the top is not parallel to the XZ plane then it gets more complicated and all about how you determine the direction of travel of the point.

@JohnK thank you John, I have to explain better what I mean, I didnt explain it well,

  • imagine a valley separated by 2 hills
  • I am in the part that goes down from one the hills towards the valley
  • The direction gradient vector (negative of gradient) is pointing down towards the valley
  • now imagine I want to move x amount in that direction (the gradient value multiplied by x units)
  • if i just go straight in the direction of the gradient i will go through the surface to below the valley
  • instead i want the point to move through the valley floor and start climbing the hill after the valley
  • so say that from the initial point in down part of hill 1, i need to push for 100 positions in the direction of gradient, and i need to keep the point on the surface of mesh at all times
  • thats why i was thinking that one way is, ok i push 100 units in the direction of the gradient, and im gonna go through the floor below the valley but if i can then i could calculate what is the point in surface that matches vertically with where the point is at each moment, then i could keep the point visually always on the surface of the mesh.

what do u think. To do this I cannot use a physical system because i dont want to have gravity or any physical behaviour, i control the movement myself always at all times. However now i am thinking maybe some kind of collision detection could be an alternative to force the point or sphere to always be on top of the surface as i push it in the gradient direction, not sure

Maybe something like this would work for you. At point A (x, y, z )rotate theta around the y axis from x axis to give horizontal direction. Move forward a distance d horizontally to a point B (x + dcos(theta), y, z + dsin(theta)). From B cast a ray and find the point C where it hits the mesh surface. The vector AC will give the direction of travel and the distance.

1 Like

@JohnK mmm not sure if I understand well, let me try to break it down to see if i understand:

  • so a ball, sphere is in point A (x,y,z)
  • I want to push it in the direction of the negative gradient by an amount F
  • what is theta, the gradient angle? the gradient actually is a direction vector (a,b,c)
  • not clear what theta is for me yet, but then you say I take theta and mmm locate a point B with coordinates (x, d x cos (theta), y, z+d x sin(theta)
  • then from B mmm you say that I do something like this:
    var ray = new BABYLON.Ray(origin, direction, length);
    var hit = scene.pickWithRay(ray);
    if (hit.pickedMesh){hit.pickedMesh.position?}

and then maybe at hit.pickedMesh.position I would get the place in the mesh where that ray hits?

  • and then I can position the ball at that point

I think I still don’t understand what you propose to be honest,
but this ray thing seems interesting, would this be possible?

  • so a ball, sphere is in point A (x,y,z)
  • I want to push it in the direction of the negative gradient by an amount F
  • So I do newPosition = originalPointOnMesh + F * gradient
  • that new position may end up below or above the mesh surface
  • and then I throw a ray from that point vertically in the Y axis to try to find where that point collides with the surface: new BABYLON.Ray(origin, direction, length); (I guess I would have to try both in direction vertical up and then vertical down because I dont know if I am above or below the surface
  • when I detect a hit, either with ray that I throw vertically in Y axis or with the one that I throw vertically down the Y axist, then I can place the ball in that position

would that work?

of course that would make the ball jump from A to B, if I wanted to show the gradua move from A to B, I guess I could simply divide that F amount in a number of subdivisions and calculate all the in betweens?

In my example you are moving from point A to point C. B is an intermediate calculation point to find C. Take a horizontal plane through A, consider the usual X, Y, Z axes with A as the origin. Consider a line of length d from A along the X axis. Rotate d in the XZ plane until it lies over the gradient vector, theta is the angle d has rotated about Y axis. The end of line d is B, raycast from B.

Your method should also work.

@JohnK I think I now understand it better, you are just taking a distance d horizontally in X and projecting that onto the gradient plane first, etc

mmm, if the raycasting of the ray works, I think it may just be easier to just follow the gradient vector till wherever it goes and then just raycast from there till finding a hit, seems easier in theory, the only difficulty I see is when creating the ray, I would have to create two rays, one that points straight up and another that points straight down:
var ray = new BABYLON.Ray(origin, direction, length);

what should I set as direction and lenght? would direction be (0,1,0) to point up and (0,-1,0) to point down? and length a large value to make it go really far? like 1000?

I guess then the thing would be:

  • if ray to 0,1,0 finds a hit and 0,-1,0 doesnt then I am below surface, move ball to that hit point
  • if ray to 0,1,0 doesnt find a hit and 0,-1,0 does then I am above surface, move ball to that hit point
  • if both rays find a hit then maybe i am on the surface? so do nothing
  • if none of the rays finds a hit: hopefully this should never happen :slight_smile:

length a large value to make it go really far? like 1000?

length is optional if you leave it out it is practically infinite.

Best thing to do is a simple example in a PG, here is a surface you could try with Babylon.js Playground

@JohnK thats a great idea John, Im gonna put a sphere in that playground and push it towards some direction vector and then see if this ray thing would work, will report back here :wink:

for the ray helper to display you do need a length for the ray apparently. https://www.babylonjs-playground.com/#HALPDM#10

@JohnK John it works now
https://www.babylonjs-playground.com/#HALPDM#18

it was a mistake in the calculations of the coordinates, now it seems to work, so thats with 1 ray,
now I would have to use 2 rays in case its down instead of up etc, and also check along the way to do the path part by part, will give it a try

thank you very much for the help :slight_smile:

1 Like

Well done on solving it. I’ve been working on my version https://www.babylonjs-playground.com/#HALPDM#20

Not having === ground on line 65 caused me problems for a while.

2 Likes

@JohnK thank you very much John, love your version as that’s the next thing I had to check, the continuity stuff, great really, thank you again for such a superb help and support :slight_smile:

@JohnK its really good, question, you use only 1 ray
ray = new BABYLON.Ray(B, new BABYLON.Vector3(0, -1, 0), 1000);

pointing down, I guess because you are moving up with the gradient direction,

in my case I should throw 2 rays, because sometimes I could end up below the surface and other times above the surface, depending on the direction of gradient

mmm no your gradient points down, and the ray also down, or maybe because the ray is infinite doesnt matter if it points up or down it will just go all the way vertical?

update: now I understand exactly what you do, you push the Y up and throw the ray from way above towards down, very clever, that way you only need 1 ray yeah

@JohnK
Im trying it in combo with the real gradient, and whats happening is that I have created a button and every time I click the button I want the sphere to do one more jump, but the ray hit is hitting the ball not the mesh, thats the issue, I have to see how to solve it without having to put a continous loop

This is the issue I had, as I mentioned see line 65 of my PG which means only the surface picked point is returned.

@JohnK , here is the function that gets executed everytime I click on a button but yeah its hitting the sphere the ray, not the mesh, mmm, i guess with a continuous loop maybe it hits first one and then the other, but mmm isnt there a way to skip the sphere hit straight away i wonder

const stepNow = (scene) =>{
const dir=setArrow3(desPick); // gets gradient vector
let desDir = dir;
var f= 0.1;
var B = dsph.position.clone();
var radius = 0.1;
B.y = -400;
B.x += f * desDir.x;
B.z += f * desDir.z;
ray = new Ray(B, new Vector3(0, 1, 0), 1000);
hit = scene.pickWithRay(ray);
if (hit.pickedMesh === mhr[mcur]){
dsph.position.x = B.x;
dsph.position.y = hit.pickedPoint.y + radius;
dsph.position.z = B.z;
}
desPick=hit;
};

@JohnK I know, I saw your line, and I wrote something similar, but it doesnt work for me in this standalone function

const stepNow = (scene) =>{
const dir=setArrow3(desPick); // gets gradient vector
let desDir = dir;
var f= 0.1;
var B = dsph.position.clone();
var radius = 0.1;
B.y = -400;
B.x += f * desDir.x;
B.z += f * desDir.z;
ray = new Ray(B, new Vector3(0, 1, 0), 1000);
hit = scene.pickWithRay(ray);
if (hit.pickedMesh === mhr[mcur]){
dsph.position.x = B.x;
dsph.position.y = hit.pickedPoint.y + radius;
dsph.position.z = B.z;
}
desPick=hit;
};

basically it never completes the condition if (hit.pickedMesh === mhr[mcur]){, because its hitting the sphere. When i do a console.log right before that condition arrives i can see that hit always has the sphere mesh inside

@JohnK so mmm now I added exactly what you have the observable function like I show below, but mmm not getting the hit with the mesh yet; and the weird thing is that if I put a
console.log(hit.pickedMesh); inside the onAfterRenderObservable function, it constantly hits the sphere, it never hits the mesh

const stepNow = (scene) =>{
const dir=setArrow3(desPick); // get gradient vector
let desDir = dir;
var f= 0.1;
var B = dsph.position.clone();
var radius = 0.1;
B.y = 400;
B.x += f * desDir.x;
B.z += f * desDir.z;
scene.onAfterRenderObservable.add(() => {
ray = new Ray(B, new Vector3(0, -1, 0), 1000);
hit = scene.pickWithRay(ray);
if (hit.pickedMesh === mhr[mcur]){
console.log(“mesh hit”);
}
});

@JohnK so im having a couple of issues here
im using the derObs=scene.onAfterRenderObservable.add(() => {
and the problem is that I have the sphere, the arrow etc, and then the mesh,
and I keep getting hits with sometimes the arrow, sometimes the sphere, sometimes the mesh,
but when I get the arrow or sphere, I just get them and I dont get the mesh at all, thats the odd thing, its not that first it hits the sphere, then the arrow and then the mesh, it just hits the first of them and doesnt keep on hitting the others

the other issue I have is that I want to deactivate the onAfterRenderObservable.in between button clicks, so once I hit the mesh I do:
scene.onBeforeRenderObservable.remove(derObs);
no errors, but it doesnt work, the event keeps on running happily, wont stop