Announcements

Between mid-October and November, the content on AREA will be relocated to the Autodesk Community M&E Hub and the Autodesk Community Gallery. Learn more HERE.

How to get 3D coordinates of a mouse click on object surfaces with raycasting?

How to get 3D coordinates of a mouse click on object surfaces with raycasting?

wojciech.selwa
Contributor Contributor
382 Views
5 Replies
Message 1 of 6

How to get 3D coordinates of a mouse click on object surfaces with raycasting?

wojciech.selwa
Contributor
Contributor

Hi everyone,

I’m trying to write a script that lets me click anywhere in the viewport and returns the exact 3D coordinates of the point where the click ray intersects with an object’s surface. If no object is under the click, it should fallback to returning the point on the grid.

So far, I’ve tried using pickPoint(), but it only returns points projected on the grid, ignoring objects. I also tried building a ray with mapScreenToWorldRay and using intersectRayScene, but I can’t get it to correctly detect the first surface hit by the click ray.
Any ideas how this can be achieved? Would really appreciate any suggestions...

0 Likes
Accepted solutions (1)
383 Views
5 Replies
Replies (5)
Message 2 of 6

MartinBeh
Advisor
Advisor

Weird - intersectRayScene() should do the trick.

Can you post some more details (code, .max scene for testing) of what is not working for you?

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

wojciech.selwa
Contributor
Contributor

Here is the simple code I have made to generate ray between camera and click point to give back any registered hit with surface on its way:

tool ClickPointCoordinates
(
on mousePoint click do
(
local mousePos = mouse.screenpos
local ray = mapScreenToWorldRay mousePos
local hit = intersectRayScene ray

if hit != undefined and hit.count == 2 then
(
local obj = hit[1]
local pos = hit[2]

print (" X: " + pos.x as string)
print (" Y: " + pos.y as string)
print (" Z: " + pos.z as string)
)
else
(
print "No hit."
)
)
)

startTool ClickPointCoordinates

 

Results are being displayed in Listener.
Feels like something is missing here, or I am not doing it right...

0 Likes
Message 4 of 6

MartinBeh
Advisor
Advisor

AFAIK intersectRayScene returns an array of arrays:

 

hit[1][1] -> the first hit object (in order of creation, not distance!), could also be hidden

hit[1][2] -> the ray, with hit position and hit normal direction

 

hit[2][1] -> the second hit object 

hit[2][2] -> the second ray, with hit position and hit normal direction

 

So your test

 

if hit != undefined and hit.count == 2 then

 

might be the culprit here. hit will always be an array, but count could be 0, 1, 2,... (i.e. the number of total hits)

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

MartinBeh
Advisor
Advisor
Accepted solution

Here is a little tool to place point helpers at the closest ray intersections:

tool PlaceRayPoints (
	struct hitData ( obj, pos, normal, dist )		-- struct to hold hit data

	-- compare function for qsort
	fn HitDistCompare a b = (
		-- expects a and b to be a hitData struct 
		if a.dist < b.dist then -1 else if a.dist > b.dist then 1 else 0
	)

	fn raycastPoint theRay = (
		-- raycast scene and place surface-aligned Point helper at closest visible intersection
		local intersections = intersectRayScene theRay		-- also includes intersections with hidden objects
		if intersections.count > 0 then (
			-- collect visible hit data with distances
			local intersectionData = for isect in intersections where (not isect[1].isHiddenInVpt) collect ( 
				p = isect[2].pos
				hitData obj:isect[1] pos:p dist:(distance theRay.pos p) normal:isect[2].dir
			)
			if intersectionData.count > 0 then (
				if intersectionData.count > 1 then qsort intersectionData HitDistCompare	-- sort all hits by distance
				local first = intersectionData[1]
				Point axistripod:true pos:first.pos dir:first.normal parent:first.obj size:2 wirecolor:green
			)
		)
	)

	on mousePoint clickNo do (
		raycastPoint (mapScreenToWorldRay mouse.pos)
	)
)

clearListener()
delete (for o in objects where (classOf o)==Point collect o)

startTool PlaceRayPoints prompt:"Click on objects to place points at intersection"

Performance is actually not completely bad - quick tests with 10k boxes or 1.5 Mio polygons were reasonably fast. But of course one should not expect miracles from MAXScript with very heavy scenes.

 

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

wojciech.selwa
Contributor
Contributor

Perfect, I wouldn`t even be able to get to that solution myself, thank you! For performance aspect, script will operate in rather light scenes so its does more than enough for me.

 

0 Likes