RayMeshGridIntersect can do anything you want , but it's very slow , without it , you can't done by only maxscript
if you can use c++ , then all methods in it (i can't do this)
if you can use c# , there are ways to do , i had take an example
public class IntersectRay
{
IGlobal global = GlobalInterface.Instance;
public Array Get(int handle, IPoint3 raycenter, IPoint3 raydir, int time)
{
IINode obj = global.COREInterface.GetINodeByHandle((uint)handle);
IObjectState state = obj.EvalWorldState(0, false);
IObject iobj = state.Obj;
if ((long)iobj.SuperClassID != 16)
{
object[] falseresult = new object[]
{
0,
0,
0,
new float[]{0,0,0},
new float[]{0,0,0}
};
return falseresult;
}
IObjectWrapper wobj = global.ObjectWrapper.Create();
wobj.Init(0, state, false, 0, 0);
ITab<float> tab = global.Tab.Create<float>();
IRay myRay = global.Ray.Create();
myRay.P = raycenter;
myRay.Dir = raydir;
float at = 0;
IPoint3 norm = global.Point3.Create();
norm.X = 0;
norm.Y = 0;
norm.Z = 0;
int fi = 0;
uint ufi = 11;
IPoint3 tritab = global.Point3.Create();
IMatrix3 worldTM = obj.GetObjectTM(time, global.Interval.Create());
IRay worldRay = global.Ray.Create();
worldRay.P = raycenter;
worldRay.Dir = raydir;
IMatrix3 invTM = worldTM;
invTM.Invert();
IRay localRay = TransformRay(worldRay, invTM);
string pos = "";
int re = -1;
float[] normal = new float[3];
float[] barycentric = new float[3];
bool isother = (iobj.ClassID.PartA == 1562457754 && iobj.ClassID.PartB == 0) || (iobj.ClassID.PartA == 469250957 && iobj.ClassID.PartB == 422535320) || (iobj.ClassID.PartA == 4144 && iobj.ClassID.PartB == 0);
if (isother)
{
re = wobj.IntersectRay(localRay, ref at, norm, ref fi, tab);
IPoint3 polytab = NormalizePoint3(tab);
barycentric[0] = polytab[0];
barycentric[1] = polytab[1];
barycentric[2] = polytab[2];
}
else
{
ITriObject triObj = iobj.ConvertToType(time, global.TriObjectClassID) as ITriObject;
IMatrix3 worldToNodeTM = obj.GetNodeTM(time, global.Interval.Create());
worldToNodeTM.Invert();
IRay meshlocalRay = global.Ray.Create();
meshlocalRay.P = worldToNodeTM.PointTransform(worldRay.P);
meshlocalRay.Dir = worldToNodeTM.VectorTransform(worldRay.Dir);
re = triObj.Mesh.IntersectRay(localRay, ref at, norm, ref ufi, tritab);
tritab = NormalizePoint3(tritab);
barycentric[0] = tritab.X;
barycentric[1] = tritab.Y;
barycentric[2] = tritab.Z;
float tolerance = 0.0001f;
for (int i = 0; i < triObj.Mesh.NumFaces; i++)
{
var face = triObj.Mesh.GetFace(i);
int v0Idx = (int)face.V[0];
int v1Idx = (int)face.V[1];
int v2Idx = (int)face.V[2];
IPoint3 p0 = triObj.Mesh.GetVert(v0Idx);
IPoint3 p1 = triObj.Mesh.GetVert(v1Idx);
IPoint3 p2 = triObj.Mesh.GetVert(v2Idx);
if (RayIntersectTriangle(localRay, p0, p1, p2, out float faceHitDist))
{
if (Math.Abs(faceHitDist - at) <= tolerance)
{
fi = i;
}
}
}
}
IPoint3 worldNorm = worldTM.VectorTransform(norm);
normal[0] = worldNorm[0];
normal[1] = worldNorm[1];
normal[2] = worldNorm[2];
object[] result = new object[]
{
re,
fi+1,
at,
barycentric,
normal
};
return result;
}
private static IRay TransformRay(IRay ray, IMatrix3 tm)
{
IGlobal g = GlobalInterface.Instance;
IRay result = g.Ray.Create();
result.P = tm.PointTransform(ray.P);
result.Dir = tm.VectorTransform(ray.Dir);
return result;
}
private static bool RayIntersectTriangle(IRay ray, IPoint3 v0, IPoint3 v1, IPoint3 v2, out float t)
{
t = 0.0f;
float EPSILON = 0.0000001f;
IPoint3 edge1 = v1.Subtract(v0);
IPoint3 edge2 = v2.Subtract(v0);
IPoint3 h = Cross(ray.Dir, edge2);
float a = Dot(edge1, h);
if (a > -EPSILON && a < EPSILON) return false;
float f = 1.0f / a;
IPoint3 s = ray.P.Subtract(v0);
float u = f * Dot(s, h);
if (u < 0.0f || u > 1.0f) return false;
IPoint3 q = Cross(s, edge1);
float v = f * Dot(ray.Dir, q);
if (v < 0.0f || u + v > 1.0f) return false;
t = f * Dot(edge2, q);
return t > EPSILON;
}
private static float Dot(IPoint3 a, IPoint3 b) => a.X * b.X + a.Y * b.Y + a.Z * b.Z;
private static IPoint3 Cross(IPoint3 a, IPoint3 b)
{
IGlobal global = GlobalInterface.Instance;
return global.Point3.Create(
a.Y * b.Z - a.Z * b.Y,
a.Z * b.X - a.X * b.Z,
a.X * b.Y - a.Y * b.X
);
}
IPoint3 NormalizePoint3(ITab<float> tab)
{
IPoint3 point3 = global.Point3.Create();
point3.X = tab[0];
point3.Y = tab[1];
point3.Z = tab[2];
return NormalizePoint3(point3);
}
IPoint3 NormalizePoint3(IPoint3 v)
{
float len = (float)Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z);
if (len < 1e-8f) return v;
IGlobal g = GlobalInterface.Instance;
return g.Point3.Create(v.X / len, v.Y / len, v.Z / len);
}
}
use script above , release a dll , this script based on max 2027 , other versions may need to change syntax as the api changed , must add reference Autodesk.Max.dll
a=(dotnetClass "System.Reflection.Assembly").Load ((dotnetClass "System.IO.File").ReadAllBytes @"c:\the path of released dll.dll")
b=(DotNetClass "System.Activator").CreateInstance (a.GetType("your_name_space.IntersectRay"))
iglobal = (dotnetclass "Autodesk.Max.GlobalInterface").Instance
center = iglobal.Point3.Create 0 0 0
dir = iglobal.Point3.Create 0 0 1
c=b.Get $.inode.handle center dir 0
the maxscript to use above , center and dir is your check ray , same as how you create ray current , last 0 is the time (ticks , not frame), you can set as slidertime.ticks to get current time , it will return a 5 items array , 1st is is intersected , 2nd is the face id , 3rd is distance between intersect point and ray center , 4th is the barycentric coordinate of intersect point in the intersect face , 5th is the normal of intersect point, example below
#(1, 5, 70.4428, #(0.0500463, 0.998747, 0.0), #(0.0272379, 0.100702, -0.994544))
4th may return a wrong value , its max api error , couldn't avoid , if needed , you can check 2 times , when wrong , it may return value as #(0,0,0) or #(0,0,1)
for a more fast way , you can check mesh with intersectRayEx and poly with i post