PDA

View Full Version : Get a Ray in Local Coordinates


MerlinEl
11-07-2009, 08:06 PM
Hi

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

http://img405.imageshack.us/img405/1636/52243228.jpg


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
)

SyncViewS
11-07-2009, 09:33 PM
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

MerlinEl
11-08-2009, 07:26 AM
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

SyncViewS
11-08-2009, 08:54 AM
That's 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

MerlinEl
11-08-2009, 09:57 AM
Magnificently Enrico!
Thanks for this and you explanation , it helped me very much :-)

CGTalk Moderation
11-08-2009, 09:57 AM
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.