Mouse position to world vector from a custom origin
36 replies, posted
[QUOTE=thomasfn;22390939]Ok thanks, this will be useful for my Raamatukogu gamemode.[/QUOTE]
What is that??
[url]http://www.google.com/search?sourceid=chrome&ie=UTF-8&q=Raamatukogu[/url]
[QUOTE=commander204;22394096]What is that??
[url]http://www.google.com/search?sourceid=chrome&ie=UTF-8&q=Raamatukogu[/url][/QUOTE]
[url]http://www.google.com/search?q=Raamatukogu+gmod[/url]
Thanks.
That is awesome theJ89, you should add it to the Wiki's "useful snippets" section. I'm sure many people will find this useful.
Here's an update guys!
The previously posted code did not work with widescreen.
I have discovered why this is and fixed it.
I've also created a function that does the opposite thing:
You can find where something in the world will appear when viewed with a camera with the given angles / field of view, on a screen of the given width / height.
[lua]
--[[
Give this function the coordinates of a pixel on your screen, and it will return a unit vector pointing
in the direction that the camera would project that pixel in.
Useful for converting mouse positions to aim vectors for traces.
iScreenX is the x position of your cursor on the screen, in pixels.
iScreenY is the y position of your cursor on the screen, in pixels.
iScreenW is the width of the screen, in pixels.
iScreenH is the height of the screen, in pixels.
angCamRot is the angle your camera is at
fFoV is the Field of View (FOV) of your camera in ___radians___
Note: This must be nonzero or you will get a divide by zero error.
]]
function LPCameraScreenToVector( iScreenX, iScreenY, iScreenW, iScreenH, angCamRot, fFoV )
--This code works by basically treating the camera like a frustrum of a pyramid.
--We slice this frustrum at a distance "d" from the camera, where the slice will be a rectangle whose width equals the "4:3" width corresponding to the given screen height.
local d = 4 * iScreenH / ( 6 * math.tan( 0.5 * fFoV ) ) ;
--Forward, right, and up vectors (need these to convert from local to world coordinates
local vForward = angCamRot:Forward();
local vRight = angCamRot:Right();
local vUp = angCamRot:Up();
--Then convert vec to proper world coordinates and return it
return ( d * vForward + ( iScreenX - 0.5 * iScreenW ) * vRight + ( 0.5 * iScreenH - iScreenY ) * vUp ):Normalize();
end
--[[
Give this function a vector, pointing from the camera to a position in the world,
and it will return the coordinates of a pixel on your screen - this is where the world position would be projected onto your screen.
Useful for finding where things in the world are on your screen (if they are at all).
vDir is a direction vector pointing from the camera to a position in the world
iScreenW is the width of the screen, in pixels.
iScreenH is the height of the screen, in pixels.
angCamRot is the angle your camera is at
fFoV is the Field of View (FOV) of your camera in ___radians___
Note: This must be nonzero or you will get a divide by zero error.
Returns x, y, iVisibility.
x and y are screen coordinates.
iVisibility will be:
1 if the point is visible
0 if the point is in front of the camera, but is not visible
-1 if the point is behind the camera
]]
function VectorToLPCameraScreen( vDir, iScreenW, iScreenH, angCamRot, fFoV )
--Same as we did above, we found distance the camera to a rectangular slice of the camera's frustrum, whose width equals the "4:3" width corresponding to the given screen height.
local d = 4 * iScreenH / ( 6 * math.tan( 0.5 * fFoV ) );
local fdp = angCamRot:Forward():Dot( vDir );
--fdp must be nonzero ( in other words, vDir must not be perpendicular to angCamRot:Forward() )
--or we will get a divide by zero error when calculating vProj below.
if fdp == 0 then
return 0, 0, -1
end
--Using linear projection, project this vector onto the plane of the slice
local vProj = ( d / fdp ) * vDir;
--Dotting the projected vector onto the right and up vectors gives us screen positions relative to the center of the screen.
--We add half-widths / half-heights to these coordinates to give us screen positions relative to the upper-left corner of the screen.
--We have to subtract from the "up" instead of adding, since screen coordinates decrease as they go upwards.
local x = 0.5 * iScreenW + angCamRot:Right():Dot( vProj );
local y = 0.5 * iScreenH - angCamRot:Up():Dot( vProj );
--Lastly we have to ensure these screen positions are actually on the screen.
local iVisibility
if fdp < 0 then --Simple check to see if the object is in front of the camera
iVisibility = -1;
elseif x < 0 || x > iScreenW || y < 0 || y > iScreenH then --We've already determined the object is in front of us, but it may be lurking just outside our field of vision.
iVisibility = 0;
else
iVisibility = 1;
end
return x, y, iVisibility;
end
[/lua]
[QUOTE=theJ89;32941555]code[/QUOTE]
Very useful indeed, I've been fighting with this for years.
I had this same problem a while back.
Here's the code that I used (granted its not optimized at all)
[lua]
local view = { angles = Angle(45,180,0) }
local trace = {}
local angFixTrace = {}
function CLASS:CalcView( ply, pos, angles, fov )
gui.EnableScreenClicker(true)
local x, y, ang, sw, sh, hw, hh, mposx, mposy, ratio
mposx = gui.MouseX()
mposy = gui.MouseY()
sw = ScrW()
sh = ScrH()
ratio = sw/sh
hw = sw / 2
hh = sh / 2
x = hw - mposx
y = hh - mposy
ang = Angle( 0, math.deg( math.atan2( -y, x ) ) - 90, 0 )
ang:RotateAroundAxis(ang:Forward(), 45)
ply:SetEyeAngles( ang )
local tx, ty
tx = -x / hw * 45 * ratio
ty = 250 - y / hh * 45 * ratio
view.origin = pos + Vector( ty, tx, 210 )
view.fov = fov
angFixTrace.start = ply:EyePos()
angFixTrace.endpos = view.origin
angFixTrace.filter = ply
local atr = util.TraceLine(angFixTrace)
if atr.HitWorld then
view.origin = Vector(atr.HitPos.x,atr.HitPos.y,view.origin.z)
else
view.angles = Angle(45,180,0)
end
local fovFix = (math.atan(math.tan((fov * math.pi) / 360) * (ratio / (4/3) )) * 360) / math.pi
trace.start = view.origin
trace.endpos = trace.start + MPosAimVec(mposx, mposy, sw, sh, view.angles, math.rad(fovFix)) * 5000
trace.filter = ply
local tr = util.TraceLine(trace)
if not tr.Hit then return end
CLASS:SetData(ply, "MPos", tostring(tr.HitPos)) --The hitpos of the trace on the world
CLASS:SetData(ply, "MAimVec", tostring(tr.HitPos-ply:EyePos())) --AimVector from the player to the trace
return view
end
[/lua]
This was in a class base for one of my old gamemodes, so it may need to be edited for your needs, but should be able to do so easily
[editline]25th October 2011[/editline]
Woops, forgot to add the function to adjust for widescreen:
[lua]
--This function is needed to adjust the angle for CalcView for widescreen resolutions
local function MPosAimVec(iScreenX,iScreenY,iScreenW,iScreenH,aCamRot,fFoV)
local fHalfWidth = iScreenW*0.5;
local fHalfHeight = iScreenH*0.5;
local d = fHalfWidth/math.tan(fFoV*0.5);
local vForward=aCamRot:Forward();
local vRight=aCamRot:Right();
local vUp=aCamRot:Up();
return (vForward*d + vRight*(iScreenX-fHalfWidth) + vUp*(fHalfHeight-iScreenY)):Normalize();
end
[/lua]
Sorry, you need to Log In to post a reply to this thread.