Welcome to Babylon! If I’m understanding what you’re asking correctly, then part of why this math may be confusing is that, without additional parameters beyond just the
dragPlaneNormal, the problem is under-constrained, meaning there are infinite solutions. The idea of a plane with a grid on it needs more than just a normal; it needs an orientation to prevent the gridded plane from being allowed to “spin” around its
dragPlaneNormal, which would cause each grid location to trace a circle of places where it’s mathematically allowed to be. If you don’t have a specific grid orientation you’re looking for, then we can just pick one arbitrarily, at which point the problem becomes more straightforward linear algebra.
With orientation in hand, the way I would think about this problem would be to transform the problem into an unorientated space, solve it there, then transform the solution back. Essentially what you want to do is take whatever your
dragPlaneNormal is and “move it” to be something obvious like
(0, 0, 1). In such a space, as you mentioned, the problem is quite straightforward to solve, and you can then move the solution back into the original space, where you can use it.
To implement this, the first thing you need is to get the orientation associated with your
dragPlaneNormal. You could do this any number of ways, but I’d personally recommend constructing a basis (a set of three axes) using your
dragPlaneNormal, then using those axes to create a
Quaternion. You can construct an arbitrary using cross products:
dragPlaneNormal is your third axis; you can find your second axis by taking the cross product of
dragPlaneNormal with an arbitrary other vector like
(1, 0, 0) (note that, if you do this, you’ll need one fallback to avoid singularities if
dragPlaneNormal ever happens to be
(1, 0, 0)); and you can find your second axis as the cross product of your first and third. Note that you need your axes to be constructed sanely and you probably want them to resemble Babylon’s standard basis as closely as possible (especially regarding handedness) and that the order of arguments with cross products matters deeply, so I’d definitely recommend displaying your calculated axes in a debug view to check that you’ve got the math right. If one axis happens to be pointing in the wrong direction, reverse the order of the arguments in the cross product that created it. Once you’ve got this looking correct for one
dragPlaneNormal, you should never have to fiddle with that math again.
Once you have your basis and orientation
Quaternion, the rest should be pretty straightforward. Get the translation your user moved the object in world space by subtracting the original position from the new position:
trans := newPos - pos. Move this translation into unoriented space by transforming it by the inverse of your
Quaternion; if this worked, the Z value (or whichever axis of your basis was
dragPlaneNormal) of your transformed translation should be zero. Your grid is now effectively axis-aligned, which should make it easy to “snap” the translation to a gridded value in this space. Once you have that, all you have to do is transform the snapped translation back into oriented space using your
Quaternion, then add back the original position. The result should be the “gridded” drop point in your arbitrarily oriented drag plane.
(NOTE: A lot of this math depends on normals, so you need to make sure all vectors are correctly normalized before sending them into cross products and basis math. I only mention this because
(1, 2, 3) is not a unit vector and would need to be normalized before it could be used in this context.)
Hope this helps, and best of luck!