using MFnMesh for mesh ray intersection


#1

Hello,

I have a script which is firing alot of rays out to detect and record collision point coordinates in a mesh. At the moment i really need to find a way to speed up the whole raytracing process as much as possible as there are hundreds of rays and it takes 30 seconds so far to complete firing.

However this bit of code:

mesh.allIntersections(
		point, dir,
		None, None,
		False, om.MSpace.kWorld,
		10000, False,
		None, 
		True,
		hitPoints,
		None, None,
		None, None,
		None
	) 

apparently needs a mesh lookup accelerator, to speed up the raytracing. Which gets added to one of the parameters in the allIntersections() thing. If anyone knows how to implement and accelerator could you please advise me. Bear in mind that i am not that confident at programming, the mfnmesh documentation for accelerators seems really confusing to me.

thanks,
Sam


#2

for C++, this should be enough:

// prepare this once for whole loop
MMeshIsectAccelParams accelParams = meshFn->autoUniformGridParams()

// ... later in a loop ..
this->meshFn->allIntersections(
  raySource,rayDirection,
  NULL,NULL,false,MSpace::kWorld, 1.0,false,
  accelParams,false, hits,NULL,&hitFaces,NULL,NULL,NULL);

for Python, you’ll probably need some MScriptUtil shenanigans.


#3

thanks for the reply. Im using python, but am i able to use pure c++ in a python script, or i need to find the python way to do it right?\

thanks


#4

Well you cannot just write c++ to python and I dont remember if that acceleration thingy is wrapped to python so I’d guess your only option is to write that ray pushing - part in c++.

I now somebody who knows more will eventually correct me :slight_smile:

/risto


#5

Probably a good chunk of that cost is actually going into building the acceleration structure for each ray you cast.
Because you give it no accel structure it won’t cache and re-use.
Do a first raycast with an MMeshIsectAccelParams object passed to the pertinent argument, then pass it on in the loop, and it should re-use what’s cached if the geo isn’t dirty.
The documentation seems fairly unambiguous in those regards.

If you tried and it didn’t work you will need to provide more functional code. A lot of people (me included) are happy to help, but I can’t spend 30 minutes creating myself a case and writing against it. Provide something I can run to repro and then correct and you’ll have much better chances to get more thorough and specific hep.

As for MMeshIsectAccelParams, it’s not like you need to create your own accelerator, Maya has its own, you just need to make sure it’s used between iterations.


#6

Yep

Jaco said it all so the accelerator is designed so that you built it once and use it as you will, in a loop, what not.
Maya api docs are quite good at explaining how it works.

/risto


#7

ok thanks alot for your help guys, im gonna play around with this i think i understand now.

I think i was just confused by the basic concept of what this stuff is doing. It’s super abstract to me. After reading the sections on MMeshIsectAccelParams many times it started to sink in. Will post my code attempt if get very stuck.

thanks,
Sam


#8

Please, do let us know how it goes if it turns out well. This stuff pops up in searches for years and it’s always useful to have OP post the resolution when it goes well :slight_smile:


#9

ok guys,

so i managed to get my rays cast at a speed 60% faster than before which is great! by just adding ‘inMesh.autoUniformGridParams()’ to that parameter:

def returnCollisionPoints(self, point, dir):

sel = om.MSelectionList()
dag = om.MDagPath()

sel.add("BASE_MESHShape")
sel.getDagPath(0,dag)

mesh = om.MFnMesh(dag)

point = om.MFloatPoint(*point)
dir = om.MFloatVector(*dir)
hitPoints = om.MFloatPointArray()

#t0 = time.clock()

mesh.allIntersections(
		point, dir,
		None, None,
		False, om.MSpace.kWorld,
		10000, False,
		mesh.autoUniformGridParams(), 
		True,
		hitPoints,
		None, None,
		None, None,
		None
	) 		
return hitPoints
		

however this function is being called every time a ray needs to be cast with a position and a vector. So as i understand it, you were saying that the ‘setup’ part of mesh.allIntersections() needs to be done outside of this function, so it doesnt recreate the accelerator each time the function is called? - I think i tried this by putting the setup part in its own function which is called once in the program. But it didnt seem to affect the time at all.

Unless im mistaken. Anyway 60% speed improvement is already awesome

Sam


#10

should be more like below: move meshFn creation outside of point loop, save result of autoUniformGridParams() between returnCollisionPoints() calls.

def processAllPoints(self):
	sel = om.MSelectionList()
	dag = om.MDagPath()

	sel.add("BASE_MESHShape")
	sel.getDagPath(0,dag)

	mesh = om.MFnMesh(dag)

	uniformGridParams = mesh.autoUniformGridParams()
	for point in allPoints:
		...
		collisions = returnCollisionPoints(self, mesh, point, dir, uniformGridParams)
		...

def returnCollisionPoints(self, mesh, point, dir, uniformGridParams):
	point = om.MFloatPoint(*point)
	dir = om.MFloatVector(*dir)
	hitPoints = om.MFloatPointArray()

	mesh.allIntersections(
		point, dir,
		None, None,
		False, om.MSpace.kWorld,
		10000, False,
		uniformGridParams,
		True,
		hitPoints,
		None, None,
		None, None,
		None
	)
	return hitPoints

#11

thanks for this,

But i tried to set this up in my script again and the speeds are identical to just putting: ‘mesh.autoUniformGridParams()’ in that parameter space.

Do you think thats strange?

thanks,
Sam


#12

Keep in mind that autoUniformGridParams() creates an MMeshIsectAccelParams structure. When you pass that into the allIntersections function, Maya does the following:

  1. Checks if there is an existing cached acceleration structure.
  2. If not, it creates one to the configuration of the MMeshIsectAccelParams.
  3. If so, then it compares the cached acceleration structure’s configuration to the passed in MMeshIsectAccelParams. If they are identical, it reuses the cached structure, otherwise it deletes the cached structure and creates a new one based on the new MMeshIsectAccelParams.

So if autoUniformGridParams() returns the same structure each time (which I believe it should for the same mesh), then it’s the same as caching it ahead of time. This is described in the allIntersections documentation:

If accelParams is NULL, the search proceeds by testing all applicable face-triangles looking for intersections. If an MMeshIsectAccelParams structure is passed in via this parameter, the mesh builds an intersection acceleration structure based on the description provided by the parameter object. This acceleration structure is used to speed up the intersection operation, sometimes by a factor of several hundred over the non-accelerated case. Once created, the acceleration structure is cached, and will be reused the next time this method (or anyIntersection() or allIntersections()) is called with an identically-configured MMeshIsectAccelParams object. If a different MMeshIsectAccelParams object is used, then the acceleration structure will be deleted and re-created according to the new settings. Once created, the acceleration structure will persist until either the object is destroyed (or rebuilt by a construction history operation), or if the freeCachedIntersectionAccelerator() method is called. The cachedIntersectionAcceleratorInfo() and globalIntersectionAcceleratorsInfo() methods provide useful information about the resource usage of individual acceleration structures, and of all such structures in the system.

So I think your result is expected. Caching the autoUniformGridParams() ahead of time is still safer since it guarantees that the params are not changing from call to call, but it won’t necessarily provide a speedup.


#13

cool thankyou Keilun. Ok this is all i need. Works brilliantly.

thanks for everyones patience

Sam


#14

Hi Sam,

I know that 3 years has passed since you posted this code.

I’m working with Python on ray-tracing with 3D mesh surface (as an .stl file) and >5 millions of rays (all with different origins and directions), so I need something to speed up my code (the best I can do is 77 minutes).
Looks like you managed to speed up your code, so may be I’ll manage to do it as well.
If you remember it, could you please explain me how your code works?
And also:

Thanks,
Cheers,

Davide Bassano