# detect inside of object

#1

Hi all,
Can anyone help me find a way to check if a point is inside an object or outside? I need better accuracy than coincident to the bounding box, and I strongly prefer mel. I’m basically trying to voxel-ize an object, if that makes sense, imagine a very pixelated image but in 3d.
Dave.

#2

This example should work, though it was written to check one point at a time so it’ll be very slow. But you can use the concept.

{
proc int isPointInsideMesh(float \$ptPos[], string \$meshObj) {
// Create a closestPointOnMesh node
// We get all of our information about the relationship
// between the mesh and the point in space from this node
string \$cpomNode = `createNode closestPointOnMesh`;
// Connect the mesh to the closestPointOnMesh node
connectAttr (\$meshObj + ".worldMesh") (\$cpomNode + ".inMesh");
// Set the closestPointOnMesh node input value to the point's position
setAttr (\$cpomNode + ".inPositionX") \$ptPos[0];
setAttr (\$cpomNode + ".inPositionY") \$ptPos[1];
setAttr (\$cpomNode + ".inPositionZ") \$ptPos[2];
// Put the point's position values into a vector so we can do math on it
vector \$queryPoint = <<\$ptPos[0], \$ptPos[1], \$ptPos[2]>>;
// Get the closest point on the mesh to the point we are checking
float \$cPosX = `getAttr (\$cpomNode + ".positionX")`;
float \$cPosY = `getAttr (\$cpomNode + ".positionY")`;
float \$cPosZ = `getAttr (\$cpomNode + ".positionZ")`;
// Subtrect the point's position and the closest point on the mesh
// Normalize the resulting vector using the unit function
vector \$pos = unit(\$queryPoint - <<\$cPosX, \$cPosY, \$cPosZ>>);
// Get the surface normal at the closest point on the mesh
float \$nX = `getAttr (\$cpomNode + ".normalX")`;
float \$nY = `getAttr (\$cpomNode + ".normalY")`;
float \$nZ = `getAttr (\$cpomNode + ".normalZ")`;
// Make the normal into a normalized vector
vector \$normal = unit(<<\$nX, \$nY, \$nZ>>);
// Delete the closestPointOnMesh node because we're done with it
delete \$cpomNode;
// If the dot product of the normal and the normalized difference in positions
// is greater than zero, then the point is inside the mesh
// This assumes that the mesh normals are pointing outward
if (dot(\$normal, \$pos) <= 0) {
// If the dot product is less than zero, then we're inside
return true;
} else {
// If the dot product is greater than zero, then we're outside
return false;
}
}
string \$newSphere[] = `polySphere -r 10.0`;
float \$x, \$y, \$z;
for (\$x=-10; \$x<10; \$x++) {
for (\$y=-10; \$y<10; \$y++) {
for (\$z=-10; \$z<10; \$z++) {
if (`isPointInsideMesh {\$x, \$y, \$z} \$newSphere[0]` == 1) {
spaceLocator -p \$x \$y \$z;
}
}
}
}
}

Based on the dot product of the surface normal compared to the difference between the position and the closest point on surface, you can tell if you are inside the mesh.

You could speed this up by creating one closestPointOnMesh node and just change the inPosition depending on what you are checking.

Stev

#3

Thanks Stev,

I’m hoping to implement something like this in a shatter script. Have a look at the shatter help thread in dynamics.

Dave.

#4

Here’s one using ray casting. I’m basing it on the fact that if a point’s inside a mesh, a raycast from that point will hit an odd number of faces to get outside. If it hits an even number, we’re already on the outside.

It’s not dependant on normals, only volume based manifold meshes including multiple shells and non-convex hulls.

global proc int isPointInsideOfMesh(vector \$point, string \$mesh)
{
string \$triNode[] = `polyTriangulate -ch 1 \$mesh`; //we can't assume there's no n-sided faces which 'fold'
float \$tolerance = 0.0001;
int \$count = 0;

if (`rayIntersect -inPosition (\$point.x) (\$point.y) (\$point.z) -direction 0 1 0 -q -i1 \$mesh`) //we've hit something
{
float \$n[]= (`rayIntersect -inPosition (\$point.x) (\$point.y) (\$point.z) -direction 0 1 0 -q -i2 \$mesh`);
if (\$n[0]) //we've hit n something's
{
int \$lastFaceID = `rayIntersect -inPosition (\$point.x) (\$point.y) (\$point.z) -direction 0 1 0 -q -f2 \$mesh`;
int \$faceID = `rayIntersect -inPosition (\$point.x) (\$point.y) (\$point.z) -direction 0 1 0 -q -f1 \$mesh`;
//get the location of the last 'hit' \$newPoint
vector \$newPoint = 	`rayIntersect -inPosition (\$point.x) (\$point.y) (\$point.z) -direction 0 1 0 -q -p1 \$mesh`;

while ( \$lastFaceID != \$faceID) //step through the face until we hit the last.
{
\$faceID = `rayIntersect -inPosition (\$newPoint.x) (\$newPoint.y + \$tolerance) (\$newPoint.z) -direction 0 1 0 -q -f1 \$mesh`;
\$newPoint =  `rayIntersect -inPosition (\$newPoint.x) (\$newPoint.y + \$tolerance) (\$newPoint.z) -direction 0 1 0 -q -p1 \$mesh`;
\$count++;
}
}
\$count++;
}
delete \$triNode[0]; //cleanup
return (`fmod \$count 2`);
}

int \$insideOutside = isPointInsideOfMesh(<<0,0,0>> ,"pCube1");
if (\$insideOutside)
{
print "
Is Inside";
}
else
{
print "
Is Outside";
}

The triangulation is there incase there’s some iffy n sided faces that are non planar, and we don’t really want to treat them as one face.

The only thing nasty is the tolerance, used to just nudge the previously found intersection along the direction vector and is a limitation of the plugin I’m using (I’ve used +y for this, but it could be any vector really).

The rayIntersect plugin is originaly from Highend3D.

#5

Me too!

I posted this earlier!

#6

Hi all, yep compiling the rayIntersect plug from Highend3D is the way to go with this. But, I did wonder if anyone knows how to use that API command using a bit of pyhon? I’d seen an example using pymel… but again that’s something that’s not in a standard installation. Is it possible in the clunky maya python implementation?

Cheers
Dave.

#7

Hmmmm interesting! This will come in handy with the shatter script! Nice job Paul!

#8

Heres a little wrapper function for the allIntersections api call in python.
It just returns whether your point is inside the mesh or not, the wrapper could be made to return any of the possible informtion provided by the api function.

#A simple wrapper for the maya api function allIntersections to test if  point lies inside of a mesh or not
#returns true or false if the point is inside the mesh or not
#
#point is the position of interest in world space
#direction is the ray direction to test in
#mesh is the geometry to test against
import maya.OpenMaya as OM
from math import fmod

def rayIntersect(mesh, point, direction=(0.0, 1.0, 0.0)):
OM.MGlobal.selectByName(mesh)
sList = OM.MSelectionList()
#Assign current selection to the selection list object
OM.MGlobal.getActiveSelectionList(sList)

item = OM.MDagPath()
sList.getDagPath(0, item)
item.extendToShape()

fnMesh = OM.MFnMesh(item)

raySource = OM.MFloatPoint(point[0], point[1], point[2], 1.0)
rayDir = OM.MFloatVector(direction[0], direction[1], direction[2])
faceIds = None
triIds = None
idsSorted = False
testBothDirections = False
worldSpace = OM.MSpace.kWorld
maxParam = 999999
accelParams = None
sortHits = True
hitPoints = OM.MFloatPointArray()
#hitRayParams = OM.MScriptUtil().asFloatPtr()
hitRayParams = OM.MFloatArray()
hitFaces = OM.MIntArray()
hitTris = None
hitBarys1 = None
hitBarys2 = None
tolerance = 0.0001
hit = fnMesh.allIntersections(raySource, rayDir, faceIds, triIds, idsSorted, worldSpace, maxParam, testBothDirections, accelParams, sortHits, hitPoints, hitRayParams, hitFaces, hitTris, hitBarys1, hitBarys2, tolerance)

result = int(fmod(len(hitFaces), 2))

#clear selection as may cause problem if the function is called multiple times in succession
OM.MGlobal.clearSelectionList()
return result

Maybe someone finds it useful.

Ta

#9

Octupe, nice work! I tried your code and passed the name of a mesh and a vector for the position of a locator and it worked great. Although I think i broke something now because it keeps giving me an error whenever i try to pass a vector to it again . . . :-\ Not sure what I changed / did wrong.

#10

Nevermind, haha, just figured it out. I was passing the name of an object like:

mesh = “pSphere1”

when it should be:

mesh = ‘pSphere1’

Awesome script!!

#11

very nice wrapper indeed. Thanks for sharing!:applause:

#12

:love: Joy! thank U!

#13

This is great, thanks very much:)

#14

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.