Notifications
Clear all

[Closed] Get a Ray in Local Coordinates

Hi

The Ray which is returned from this function is in world coordinates.
Is posible to rotate it to node local coordinates?


	fn getHitNode =
	(
		
local view_mouse_pos = mouse.pos --get current mouse pos based on active view pos
		local view_size = getViewSize() --get active viewport size
		-- if mouse is out of viewport
		if view_mouse_pos.x < 0 or view_mouse_pos.x > view_size.x or
		view_mouse_pos.y < 0 or view_mouse_pos.y > view_size.y do return undefined
		-- cast ray from mouse point in to scene
		local iray = (mapScreentoWorldRay mouse.pos) --ray <pos> <dir>
		-- get visible nodes on ray path
local hit_nodes = for n in (intersectRayScene iray) where not n[1].isHidden collect #(n[1], n[2], distance iray.pos n[2].pos)
		if hit_nodes.count == 0 do return undefined -- if nothing found
		-- sort nodes by distance
 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
		return hit_nodes[1][1] --return node or undefined
	)

4 Replies

Here is the conversion:

-- Node Transform Matrix
m3NodeTM = theNode.transform

-- Node Rotation Matrix
m3NodeRM = m3NodeTM.rotationPart as Matrix3 

-- Ray in Node Local Coordinate System
rayInLocalCS = Ray (rayInWorldCS.pos * inverse(m3NodeTM)) (rayInWorldCS.dir * inverse(m3NodeRM))
  • Enrico

Hi Enrico ,
thanks for the reply.

Im tryed to implement this piece of code, but result is not so good.
If you want, try to put some boxes in you scene and run this function.
The points will be created in strange place (not below the mouse cursor)
and his axis will not be aligned to hited node local.


   	fn getHitNode =
   	(
   		
    local view_mouse_pos = mouse.pos --get current mouse pos based on active view pos
   		local view_size = getViewSize() --get active viewport size
   		-- if mouse is out of viewport
   		if view_mouse_pos.x < 0 or view_mouse_pos.x > view_size.x or
   		view_mouse_pos.y < 0 or view_mouse_pos.y > view_size.y do return undefined
   		-- cast ray from mouse point in to scene
   		local iray = (mapScreentoWorldRay mouse.pos) --ray <pos> <dir>
   		-- get visible nodes on ray path
    local hit_nodes = for n in (intersectRayScene iray) where not n[1].isHidden collect #(n[1], n[2], distance iray.pos n[2].pos)
   		if hit_nodes.count == 0 do return undefined -- if nothing found
   		-- sort nodes by distance
      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
   		--local conversion 
   		local node = hit_nodes[1][1]
   		local rayInWorldCS = hit_nodes[1][2]
   		local m3NodeTM = node.transform -- Node Transform Matrix
   		local m3NodeRM = m3NodeTM.rotationPart as Matrix3 -- Node Rotation Matrix
   		-- Ray in Node Local Coordinate Syste
   local rayInLocalCS = Ray (rayInWorldCS.pos * inverse(m3NodeTM)) (rayInWorldCS.dir * inverse(m3NodeRM))
   		return #(node, rayInLocalCS) --return an array #(node,ray)
   	)
   	
   	
   	hit_data = getHitNode()
   	point Pos:hit_data[2].pos Dir:hit_data[2].dir
   

That鈥檚 right, because Point() wants coordinates in WorldCS and not LocalCS, so your previous Ray was perfectly right.

When you specify .dir property in Point you are forcing the helper to have Z Axis aligned to that direction, but X and Y could be oriented in any other way. 3ds Max uses by default World Z Axis as Up Vector to build the full transform matrix. If you want to orient the point like the local pivot of the object you should pass a full matrix built from the Ray you got from Hit. This is the most flexible and tweakable solution (see code below).

The simpler and faster solution would be to take node transform matrix offset it to the hit position and place a point according to that new matrix.

function getMatrixFromRay rPlane p3UpVector:[0,0,1] theNodeTM:(Matrix3 1) epsi:1e-6 =
(
    local p3Axis_Z = rPlane.dir
    local p3Axis_X = cross p3UpVector p3Axis_Z

    if ((length p3Axis_X) < epsi) then
        p3Axis_X = normalize(cross theNodeTM.row2 p3Axis_Z)
    else
        p3Axis_X = normalize(p3Axis_X)

    local p3Axis_Y = normalize(cross p3Axis_Z p3Axis_X)

    return Matrix3 p3Axis_X p3Axis_Y p3Axis_Z rPlane.pos
)


fn getHitNode =
(
    local view_mouse_pos = mouse.pos --get current mouse pos based on active view pos
    local view_size = getViewSize() --get active viewport size

    -- if mouse is out of viewport
    if view_mouse_pos.x < 0 or view_mouse_pos.x > view_size.x or
    view_mouse_pos.y < 0 or view_mouse_pos.y > view_size.y do return undefined

    -- cast ray from mouse point in to scene
    local iray = (mapScreentoWorldRay mouse.pos) --ray <pos> <dir>

    -- get visible nodes on ray path
    local hit_nodes = for n in (intersectRayScene iray) where not n[1].isHidden collect #(n[1], n[2], distance iray.pos n[2].pos)
    if hit_nodes.count == 0 do return undefined -- if nothing found

    -- sort nodes by distance
      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

    -- local conversion
    local node = hit_nodes[1][1]
    local rayInWorldCS = hit_nodes[1][2]

    -- local m3NodeTM = node.transform -- Node Transform Matrix
    -- local m3NodeRM = m3NodeTM.rotationPart as Matrix3 -- Node Rotation Matrix

    -- Ray in Node Local Coordinate Syste
    -- local rayInLocalCS = Ray (rayInWorldCS.pos * inverse(m3NodeTM)) (rayInWorldCS.dir * inverse(m3NodeRM))

    local m3Hit = getMatrixFromRay rayInWorldCS p3UpVector:(node.transform.row3) theNodeTM:(node.transform)
    return #(node, m3Hit) --return an array #(node,ray)
)

hit_data = getHitNode()
-- point Pos:hit_data[2].pos Dir:hit_data[2].dir

Point transform:hit_data[2]
  • Enrico

Magnificently Enrico!
Thanks for this and you explanation , it helped me very much