[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…
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