Intersecting a poly

Intersecting a poly

e_bustaty3ATQ5
Participant Participant
189 Views
4 Replies
Message 1 of 5

Intersecting a poly

e_bustaty3ATQ5
Participant
Participant

Hi,

 

I'm trying to get the material ID of the face under the mouse, which works with meshes but not with polys.

 

The function intersectRayEx returns undefined if the node was mot a mesh, and RayMeshGridIntersect() works but the face index can be out of range, apparently it intersects with the underlying mesh and returns a face index from that mesh, not a "polygon" index if you will.

 

I tried using snapshotAsMesh, but intersectRayEx and RayMeshGridIntersect::AddNode only accept a node as the input.

 

Any pointers are appreciated.

 

Thanks

0 Likes
Accepted solutions (1)
190 Views
4 Replies
Replies (4)
Message 2 of 5

A娘
Advocate
Advocate

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

 

0 Likes
Message 3 of 5

MartinBeh
Advisor
Advisor
Accepted solution

On a sidenote:

If you use RayMeshGridIntersect, the getHitFace method returns the index of the TriMesh face. If you use that index on the poly.mesh (or snapshotAsMesh) data you should be able to query the material ID.

 

This likely is not very fast (especially when using snapshotAsMesh), but at least you should get some useful results.

Martin B   EESignature
→ please 'Like' posts that are helpful; if a post answers your question please click the "Accept Solution" button.
0 Likes
Message 4 of 5

e_bustaty3ATQ5
Participant
Participant

Thanks Martin,

 

I just didn't know that .mesh is a thing, all good now.

 

I'm now using RayMeshGridIntersect with the editable poly, but instead of doing polyop.getFaceMatID poly faceIndex , I'm now using getFaceMatID poly.mesh faceIndex.

 

I think that in terms of performance, RayMeshGridIntersect is slower if we build the grid for a single ray-cast, but it should be quicker if doing plenty of raycasts, the performance is adequate for my needs anyway so I'm not going to optimise unless I need to.

 

Thanks again

Message 5 of 5

e_bustaty3ATQ5
Participant
Participant

Hi A娘,

 

I appreciate the amount of effort you put into this, I will not use it this time because we're trying not to keep things in fewer languages at the moment, and because Martin's solution works.

 

I will definitely bookmark the page though so if I needed this at some point it will be there.

 

Thanks

0 Likes