Mouse position to world vector from a custom origin

gui.ScreenToVector() and ply:GetCursorAimVector() does not work for what I’m doing because they assume the screen position is eyepos and eyeangles of the player. I want to trace from my screen to a world position from a different position.

Here’s a failed attempt my friend Mathias (Dr Magnusson) did.

function MouseToScreen()
local eP = LocalPlayer():EyePos()
local eA = LocalPlayer():EyeAngles()

local FoV = LocalPlayer():GetFOV()
local s = { W = ScrW(), H = ScrH() }
local x, y = gui.MousePos()
local m = { X = x, Y = y }

local Yaw = ( ( ( m.X / s.W ) * FoV ) - (FoV/2 ) )
local Pitch = ( ( ( m.Y / s.H ) * FoV ) - (FoV/2 ) ) * -1

local Dir = Angle( eA.p + Pitch, eA.y + Yaw, 0 ):Forward()

return util.TraceLine( { start = eP, endpos = eP + (Dir*2048) } ).HitPos


and here’s an attempt I did on converting a function from some XNA shit

local function MouseToVector(x_position, y_position)

local x = -(x_position - (ScrW()-ScrH()/2)) / (smooth_fov/100*ScrH()/2)
local y = (y_position - (ScrH()-ScrH()/2)) / (smooth_fov/100*ScrH()/2)

local z = 0
local mag = x*x + y*y
if mag > 1 then 
	local scale = 1 / math.sqrt(mag)
	x = x*scale
	y = y*scale
	z = math.sqrt(1-mag)

return Vector(x,-z,-y)



D3DXVECTOR3 CD3DArcBall::ScreenToVector( float fScreenPtX, float fScreenPtY )
// Scale to screen
FLOAT x = -(fScreenPtX - m_Offset.x - m_nWidth/2) / (m_fRadius* m_nWidth/2);
FLOAT y = (fScreenPtY - m_Offset.y - m_nHeight/2) / (m_fRadius* m_nHeight/2);

FLOAT z   = 0.0f;
FLOAT mag = x*x + y*y;

if( mag > 1.0f )
    FLOAT scale = 1.0f/sqrtf(mag);
    x *= scale;
    y *= scale;
    z = sqrtf( 1.0f - mag );

// Return vector
return D3DXVECTOR3( x, y, z );


Everything up to line 11 seemed to be working just fine, the angles were reasonable, so I’m thinking it might have something to do with what I put in the Angle() function.

This is what it looks like using gui.ScreenToVector()


	local trace_data = {}
	trace_data.start = smooth_position+smooth_trace
	trace_data.endpos = trace_data.start + gui.ScreenToVector(gui.MousePos()) * 10000
	local trace = util.TraceLine(trace_data).HitPos
	debugoverlay.Cross(trace, 100, 0)
	debugoverlay.Line(trace, ply:GetShootPos(), 0)


So you want to trace from a constant position to the place that your cursor is pointing to, right?


I’m probably misunderstanding, but did you guys try like:

[lua]local mpos = gui.ScreenToVector(gui.MouseX(), gui.MouseY())

local td = {}
td.start = Vector(0, 0, 0) – whatever your predetermined point was
td.endpos = mpos
td.filter = self.Owner

local trace = util.TraceLine(td)

I must be just not understanding what you want.

EDIT: Nevermind, just read your post over. I’ll think of something else, but I don’t understand why gui.ScreenToVector isn’t working for you. Would you mind explaining it a bit more?

I dunno why it doesn’t work.

I’m getting EyePos, EyeVector and EyeAngles returning 0 0 0 most of the time. Sometimes it changes to something else, but it doesn’t stay there.

Are they broken or is it like this for everyone? I tried printing what they were on my server on all clients and it was 0 0 0.

I was just thinking that this might be the problem, I dunno.

Nope, they work for me.

Are you using Panel:SetCursor() anywhere? I had this exact problem when I set my panel’s cursor to “blank”.

I am, but not doing so didn’t change anything.

Instead of using the Eye functions, why not do something like this:

res = hook.Call( “CalcView”, GAMEMODE, pl, pl:GetShootPos( ), pl:GetAimVector( ):Angle( ), desired_fov )
eP = res.origin
eA = res.angles
FoV = res.fov

That will give you the starting coordinates wherever your camera originates from.

gui.ScreenToVector() and ply:GetCursorAimVector() will still originate from LocalPlayer():EyeAngles() or whatever it uses.

I was mentioning that they were weird cause I thought maybe gui.ScreenToVector() and ply:GetCursorAimVector() used them somehow.

I’m frustrated cause I was having so much fun coding and then I bumped into this which I have wasted my entire day on.

Not sure I understand completely, but I think this is what you are after.

util.QuickTrace(pos, gui.ScreenToVector(gui.MousePos()) * 2048, LocalPlayer())

I’ve tried this. It doesn’t work because my view origin is not the players eye angles.

I think you’re after a simple linear projection.

If my understanding of how projection works in computer graphics is correct, I may have a solution for you.

Basically, think of it like this. If, for example, the FoV of the camera is 90 degrees, then

  • The far left side of screen displays everything 45 degrees to the left of the camera’s facing direction.
  • The far right side of the screen displays whatever is 45 degrees to the right of the camera’s facing direction.
  • The far up side of the screen displays whatever is 45 degrees up from the camera’s facing direction,
  • The far down side of the screen displays whatever is 45 degrees down from the camera’s facing direction.

So whenever the cursor is all the way over to the left, the forward vector of the camera is rotated about the up vector 45 degrees, or -45 degrees if it’s all the way over to the right.
Whenever the cursor is all the way on the top of the screen, the forward vector of the camera is rotated about the right vector 45 degrees, or -45 degrees if it’s all the way on the bottom.

With this basic example in mind, I’ve written some code that can be used to get the aim vector of a camera with any FoV oriented in any direction.

local aCam=Angle(0,0,0); --The angle your camera is at
local fFoV= math.pi * .5; --The FoV of your camera in radians (math.pi * .5 = 90 degrees)

–Position of the cursor on the screen
local fMouseX,fMouseY=gui.MousePos();

–Change those coordinates so they’re factors that range from 0.5 to -0.5 instead
fMouseX=.5-(fMouseX/ScrW()); – .5 when cursor is to the left, -.5 when cursor is to the right
fMouseY=.5-(fMouseY/ScrH()); – .5 when cursor is up, -.5 when cursor is down

–Calculate the pitch and yaw angular offset of the cursor relative to the camera’s forward
local fYaw=fFoVfMouseX
local fPitch=fFoV

–We calculate the coordinates of where the player’s cursor is relative to the camera
local fZ=math.sin(fPitch)
local fScalar=math.cos(fPitch)
local fY=math.sin(fYaw)*fScalar
local fX=math.cos(fYaw)*fScalar

–Forward, right, and up vectors (need these to convert from local to world coordinates)
local vForward=aCam:Forward()
local vRight=aCam:Right()
local vUp=aCam:Up()

–Then convert aimvec to proper world coordinates
local vAimVec=vForwardfX + vRight-fY + vUp*fZ

Hope this helps!
NOTE: I haven’t actually tested this code. You’ll have to give it a try for yourself to see if it’s accurate. I’m not entirely sure how the linear projection (which takes world geometry and projects it upon a flat surface like your screen) works.

Also, would you mind clarifying a bit what you’re doing that results in the camera leaving the player’s view? Are you looking through a gmod camera, or using cam.Start3D or something?

I’m making it for my thirdperson camera. I want to make a point and click RTS style movement system for it. :smile:

Thanks, but I didn’t get the FOV part, I have a variable with the fov the camera is at, but I’m not sure what to do there.

Well, basically what this is about, say you have two cameras that are exactly the same except one has a small FoV:


And the other has a large FoV:


Obviously the camera with the larger FoV would result in the aimvec being shifted more, right?

theJ89, that is exactly what I’m trying to do in the first example, but it doesn’t seem to be working properly.

I studied this a while ago and the outer angles were indeed around fov/2 degrees, but any angle in between was inaccurate when comparing with gui.ScreenToVector.

The problem with that code is this:

Top down image.

The black dot is the player, the blue dots is where the cursor is at, the red dots is where the code think the cursor is.


If you have the cursror at the center it will be centered. It becomes more and more offset the longer the distance from the player and the cursor is.