Notifications
Clear all

[Closed] Useful mxs sdk extension functions

Wow, the last one is really cool find!

as i can see your function returns the first face that was intersected… but the built-in method returns the closest one. if you continue search intersected faces and finally find the closest hit, it will change the performance.

not so, it returns the closest, this line tests to see if it’s closer and if not continue searching


 if(first && a > at) 
 			continue;

just run this from the listener…


 outray = meshop.rayintersect $Plane01 (ray $Point01.pos $Point01.dir)
 point pos:outray.pos dir:outray.dir
 outray = meshop.rayintersect $Plane01 (ray $Point02.pos $Point02.dir)
 point pos:outray.pos dir:outray.dir

gives the following result

if you can do without the barycoords you can replace

bry = mesh->BaryCoords(f, p);
 if (bry.x < 0.0f || bry.x > 1.0f || bry.y < 0.0f || bry.y > 1.0f || bry.z < 0.0f || bry.z > 1.0f) 
			continue;
if (Fabs(bry.x + bry.y + bry.z - 1.0f) > EPSILON) 
			continue;

with a call to a PointInTriangle routine and it will improve the performance on the alway hitting from ~2-4x to 4~6x. I my test scene it was working out to 100 times on a 80,000 face object in about 0.5s which equates to 16000000 fps

sorry… i didn’t read the code well.

it is quite odd coding, it comes from the surfwrap project in the sdk (conform wsmod). As I understand it, the code tests the ray against each face as a plane, discards backfacing and non intersections, then if it is closer than previous test and the intersection pt is within the face bounds thats the new current face point and normal continue testing. Where it wins is it can invalidate and discard faces much more efficiently than the “native” max version.

i’ve found a time to play with new Ray Intersect method…
first of all i slightly modified the code to support Extended return data (ray, face index, bary).

here is a code:

#define EPSILON	0.0001f
    
 int RayIntersect(Ray& ray, float& at, Point3& norm, int& fi, Point3& bary, Mesh *mesh)
 {	
 	Face *face = mesh->faces;
 	Point3* verts = mesh->verts;
 	Point3 n, p, bry;
 	float d, rn, a;
 	BOOL first = FALSE;
 	int nfaces = mesh->getNumFaces();
    
 	for(int i = 0; i < nfaces ; ++i, ++face) 
 	{
 		   n = mesh->getFaceNormal(i);
 		   rn = DotProd(ray.dir,n);
 		   if (rn > -EPSILON) 
 			   continue;
 		   d = DotProd(mesh->verts[face->v[0]],n);
 		   a = (d - DotProd(ray.p,n)) / rn;
 		   if (a < 0.0f) 
 			   continue;
 		   if(first && a > at) 
 			   continue;
 		   p = ray.p + a*ray.dir;
 		   bry = mesh->BaryCoords(i,p);
 		   if (bry.x < 0.0f || bry.x > 1.0f || bry.y < 0.0f || bry.y > 1.0f || bry.z < 0.0f || bry.z > 1.0f) 
 			   continue;
 		   if (fabs(bry.x + bry.y + bry.z - 1.0f) > EPSILON) 
 			   continue;
 		   first = TRUE;		
 		   at = a;
 		   norm = n;
 		   fi = i;
 		   bary = bry;
 	}
 	return first;
 }
    
    
 def_visible_primitive(rayMeshIntersect, "rayMeshIntersect");   
 Value* rayMeshIntersect_cf(Value** arg_list, int count)
 {
 	float		at;
 	Point3		pt, normal, bary;
 	Ray			ray, os_ray;
 	GeomObject*	obj;	
 	int			fi;
    
 	check_arg_count_with_keys(rayintersect, 2, count);
 	INode* node = arg_list[0]->to_node();
 	BOOL dataArray = key_arg_or_default(dataArray, &false_value)->to_bool();
    
 	// Get the object from the node
 	ObjectState os = node->EvalWorldState(MAXScript_time());
 	if (os.obj->SuperClassID() == GEOMOBJECT_CLASS_ID) 
 	{
 		   obj = (GeomObject*)os.obj;
 		   if (obj->ClassID() != GetEditTriObjDesc()->ClassID() && obj->ClassID() != Class_ID(TRIOBJ_CLASS_ID, 0))		
 			   return &undefined;
 	}
 	else
 		   return &undefined;
 	   
 	// Back transform the ray into object space.		
 	Matrix3 obtm  = node->GetObjectTM(MAXScript_time());
 	Matrix3 iobtm = Inverse(obtm);
 	ray = arg_list[1]->to_ray();
 	os_ray.p   = iobtm * ray.p;
 	os_ray.dir = VectorTransform(iobtm, ray.dir);
 	   
 	// See if we hit the object
    
 	Mesh& mesh = ((TriObject*)obj)->GetMesh();
    
 	if(!mesh.normalsBuilt)
 		   mesh.buildNormals();
    
 	if(RayIntersect(os_ray, at, normal, fi, bary, &mesh))
 	{
 		   // Calculate the hit point
 		   pt = os_ray.p + os_ray.dir * at;
    
 		   // transform back into world space & build result ray
 		   pt = pt * obtm;
 		   normal = Normalize(VectorTransform(obtm, normal));
 		RayValue* ray = new RayValue(pt, normal);
 		   if (dataArray)
 		{
 			Array* data = new Array(3);
 			data->append(ray);
 			data->append(Integer::intern(fi+1));
 			data->append(new Point3Value(bary));
 			return data;
 		}
 		else
 			   return ray;
 	}
 	else
 		   return &undefined;
 }

here is a test scene and comparison methods:

delete objects
 (
 	s = plane name:"source" width:100 length:100 widthsegs:40 lengthsegs:40 pos:[0,0,10]
 	t = teapot name:"target" radius:50 segsments:100 scale:[1,1,0.1]
 	converttomesh #(s, t)
 	--converttomesh #(s)
 	
 	num = 10
 	
 	gc()
 	data = undefined
 	missed = undefined
 	t0 = timestamp()
 	for k=1 to num do
 	(
 		missed = 0
 		data = for v=1 to s.numverts collect
 		(
 			p = getvert s v
 			r = ray [p.x,p.y,100] -z_axis
 			d = intersectRay t r 	
 			if d == undefined do missed += 1
 			d
 		)
 	)
 	format "intersect ray >> time:% count:% missed:%
	>> %
" (timestamp() - t0) s.numverts missed data
 
 	gc()
 	data = undefined
 	missed = undefined
 	t0 = timestamp()
 	m0 = heapfree
 	for k=1 to num do
 	(
 		missed = 0
 		data = for v=1 to s.numverts collect
 		(
 			p = getvert s v
 			r = ray [p.x,p.y,100] -z_axis
 			d = rayMeshIntersect t r dataArray:off
 			if d == undefined do missed += 1
 			d
 		)
 	)
 	format "ray intersect >> time:% count:% missed:%
	>> %
" (timestamp() - t0) s.numverts missed data
 	
 	gc()
 	data = undefined
 	missed = undefined
 	t0 = timestamp()
 	for k=1 to num do
 	(
 		missed = 0
 		data = for v=1 to s.numverts collect
 		(
 			p = getvert s v
 			r = ray [p.x,p.y,100] -z_axis
 			d = intersectRayEx t r 	
 			if d == undefined do missed += 1
 			d
 		)
 	)
 	format "intersect ray +> time:% count:% missed:%
	>> %
" (timestamp() - t0) s.numverts missed data
 
 	gc()
 	data = undefined
 	missed = undefined
 	t0 = timestamp()
 	m0 = heapfree
 	for k=1 to num do
 	(
 		missed = 0
 		data = for v=1 to s.numverts collect
 		(
 			p = getvert s v
 			r = ray [p.x,p.y,100] -z_axis
 			d = rayMeshIntersect t r dataArray:on
 			if d == undefined do missed += 1
 			d
 		)
 	)
 	format "ray intersect +> time:% count:% missed:%
	>> %
" (timestamp() - t0) s.numverts missed data
 )

you can see no big difference. the only real improvement in the way how new Ray Intersect method takes Mesh.

native max intersectRay method does do it the bad way. this issue almost fixed in intersectRayEx, but as the comparison shows could be solved better

you can change the line

converttomesh #(s, t)

to

converttomesh #(s)

and see what i mean.

seems pretty conclusive to me running your script on my function gives the following

 intersect ray &gt;&gt; time:5715 count:1681 missed:496
 ray intersect &gt;&gt; time:886 count:1681 missed:468
 intersect ray +&gt; time:5702 count:1681 missed:496

with the converttomesh #(s) ?

intersect ray >> time:5688 count:1681 missed:496
ray intersect >> time:838 count:1681 missed:468
intersect ray +> time:5756 count:1681 missed:496

 ~6.5 times faster ! not sure whats happening with the missed discrepancy but when you use the routine in anger it doesn't seem to error.

… i’m probably going off my head. i have absolutely different numbers. absolutely… i will show them tomorrow

i’m not quiet sure but this morning for both methods i had time == ~90

ok. i had to reset xform for teapot to make methods work right. these are the numbers:

intersect ray >> time:1052 count:1681 missed:492
ray intersect >> time:879 count:1681 missed:492
intersect ray +> time:1061 count:1681 missed:492
ray intersect +> time:893 count:1681 missed:492

we can see the difference, but my thought it’s only because of using a better way of getting the mesh.

for most of my functions where i need to do any not deform operations (bbox, intersection, search) i pass already a mesh (instead of node) to avoid conversion on every method’s call.

but my thought it's only because of using a better way of getting the mesh.

the get mesh method was ripped straight out of the code for intersectRayEx function so there’s no difference. I have to ask, as I get much greater performance differential have you compiled in release ?

yes. i have a release.
max 2014 (machine is not good)

please add to my compare code

resetxform t

before conversion teapot to mesh
it seems like native max methods doesn’t take into account node scale

Page 11 / 18