Notifications
Clear all

[Closed] Get an object in Ray

Hi

is there any way to get the first object in ray casting path?

here is the code to cast rays as splines:


(
	fn drawRay pointA pointB type:#corner =
	(
		local sp = SplineShape pos:pointA
		addNewSpline sp
		addKnot sp 1 type #line PointA
		addKnot sp 1 type #line PointB
		updateShape sp
	)
	tool trackIntersection
	(
		on mousePoint clickno do
		(
			if clickno == 2 do
			(
				local viewPoint_ray = mapScreentoWorldRay viewPoint
				local p1 = viewPoint_ray.pos --viewport point
				local p2 = 
				(
					local dist = distance worldPoint p1
					viewPoint_ray.dir * dist*2 + viewPoint_ray.pos --scene point
				)
				drawRay p1 p2 --a straight line from the viewport passing through scene
			)
		)
	)
	starttool trackIntersection
)

thanks for any advice…

5 Replies
1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

the best way of getting the first object in ray casting path is to use intersectRayScene function. It returns an array of all hit nodes (including hidden) in order of nodes creation. You have sort this array by hit distance and exclude hidden nodes…


iray = ray <pos> <dir>
hit_nodes = for n in (intersectRayScene iray) where not n[1].isHidden collect #(n[1], n[2], distance iray.pos n[2].pos)
fn sortByHitDistance n1 n2 = if n1[3] < n2[3] then -1 else if n1[3] > n2[3] then 1 else 0 
qsort hit_nodes sortByHitDistance
hit_nodes[1] -- is closest

so , I know is not the best way to do this… but is working now…

To test it put some object in Max scene and run the script:


(
	struct object_data (obj, ray)
	fn getObjIntersection ray =
	(
		--collect all visible objects in scene
		local objs = for o in objects where not o.isHidden collect o
		local od = #()
		local ray_dist	= #()
		--collect objects shoted by ray
		for o in objs do
		(
			local i = intersectRay o ray
			if i!= undefined do 
			(
				od += #(object_data o i)
				ray_dist	+= #(distance ray.pos i.pos)
			)
		)
		if od.count	  == 0
		then return undefined
		else if od.count == 1
		then return od[1] 
		else --return the closest object to ray begin
		(
			copy_ray_dist = #()+ray_dist --4fun
			sort copy_ray_dist
			return od[findItem ray_dist copy_ray_dist[1]]
		)
	)
	tool getObjectFromRay
	(
		on mousePoint clickno do
		(
			if clickno != 1 do
			(
				local viewPoint_ray = mapScreentoWorldRay viewPoint
				local obj_data = getObjIntersection viewPoint_ray
				if obj_data != undefined
				then 
				(
					select obj_data.obj 
					format "picked obj:%
" obj_data
				)
				else 
				(
					max select none
					format "picked ground:worldPoint	ray:%
" (ray worldPoint viewPoint_ray.dir)
				)
			)
		)
	)
	starttool getObjectFromRay
)

updated version… now with interactive marker


  
  (
  	struct object_data (obj, ray)
  	fn getObjIntersection ray =
  	(
  		--collect all visible objects in scene
  		local objs = for o in objects where not o.isHidden collect o
  		local od = #()
  		local ray_dist	= #()
  		--collect objects shoted by ray
  		for o in objs do
  		(
  			local i = intersectRay o ray
  			if i!= undefined do 
  			(
  				od += #(object_data o i)
  				ray_dist	+= #(distance ray.pos i.pos)
  			)
  		)
  		if od.count	  == 0
  		then return undefined
  		else if od.count == 1
  		then return od[1] 
  		else --return the closest object to ray begin
  		(
  			copy_ray_dist = #()+ray_dist --4fun
  			sort copy_ray_dist
  			return od[findItem ray_dist copy_ray_dist[1]]
  		)
  	)
  	tool getObjectFromRay
  	(
  		local PaintMarker
  		on start do --create marker
  		(
  			max select none
  			PaintMarker = circle radius:10 pos:[0,0,0] wireColor:(color 80 250 80) displayRenderMesh:true name:"PaintMarker" thickness = 2
  		)
  		on mousemove clickno do
  		(
  			if clickno > 1 then --move marker ower surfaces or grid
  			(
  				local viewPoint_ray = mapScreentoWorldRay viewPoint
  				local obj_data = getObjIntersection viewPoint_ray
  				if obj_data != undefined
  				then 
  				(
  					--select obj_data.obj 
  					PaintMarker.pos = obj_data.ray.pos
  					PaintMarker.dir = obj_data.ray.dir
  					format "picked obj:%
" obj_data
  				)
  				else 
  				(
  					--max select none
  					PaintMarker.pos = worldPoint
 					PaintMarker.dir = [0,0,-1]
  					format "picked ground:worldPoint	ray:%
" (ray worldPoint viewPoint_ray.dir)
  				)
  				--#stop
  			)
  		)
  		on stop do
  		(
  			delete PaintMarker
  		)
  	)
  	starttool getObjectFromRay
  )
  

Alternatively, you could use the intersectRayScene function that was introduced in max 2008 (avguard extensions). From the manual:

intersectRayScene <ray>

NEW in 3ds Max 2008: [[color=blue]AVG][/color] Performs intersectRay on all nodes in the scene.

Returns an array of results, one entry per node hit, where each entry is a 2 element array containing the node and the intersectRay result for that node.

(
local r = ray [0,0,0] [1,0,0] --define a ray along X
for i = 0 to 4 do --loop 5 times
(
  box pos:[i * 30, 0, 0] --create a box
)
local hits = intersectRayScene r --intersect with scene
print hits --Misses box 1 because ray originates inside the box
)

#($Box:Box02 @ [30.000000,0.000000,0.000000], (ray [17.5,0,0] [-1,0,0]))
#($Box:Box03 @ [60.000000,0.000000,0.000000], (ray [47.5,0,0] [-1,0,0]))
#($Box:Box04 @ [90.000000,0.000000,0.000000], (ray [77.5,0,0] [-1,0,0]))
#($Box:Box05 @ [120.000000,0.000000,0.000000], (ray [107.5,0,0] [-1,0,0]))
OK

Martijn

Yes yes… that will be fastest way to do this.
Thank you magicm and you denisT