Mouse position to world vector from a custom origin
36 replies, posted
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.
[lua]
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
end
[/lua]
and here's an attempt I did on converting a function from some XNA shit
[lua]
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
else
z = math.sqrt(1-mag)
end
return Vector(x,-z,-y)
end
[/lua]
C++
[cpp]
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;
}
else
z = sqrtf( 1.0f - mag );
// Return vector
return D3DXVECTOR3( x, y, z );
}
[/cpp]
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()
[img]http://errur.com/New/Uploads/254.png[/img]
[lua]
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)
[/lua]
So you want to trace from a constant position to the place that your cursor is pointing to, right?
Yeah.
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)
[/lua]
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".
[QUOTE=Sassafrass;20101309]Are you using Panel:SetCursor() anywhere? I had this exact problem when I set my panel's cursor to "blank".[/QUOTE]
I am, but not doing so didn't change anything.
Instead of using the Eye functions, why not do something like this:
[lua]
res = hook.Call( "CalcView", GAMEMODE, pl, pl:GetShootPos( ), pl:GetAimVector( ):Angle( ), desired_fov )
eP = res.origin
eA = res.angles
FoV = res.fov
[/lua]
That will give you the starting coordinates wherever your camera originates from.
[QUOTE=Kogitsune;20102775]Instead of using the Eye functions, why not do something like this:
[lua]
res = hook.Call( "CalcView", GAMEMODE, pl, pl:GetShootPos( ), pl:GetAimVector( ):Angle( ), desired_fov )
eP = res.origin
eA = res.angles
FoV = res.fov
[/lua]
That will give you the starting coordinates wherever your camera originates from.[/QUOTE]
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.
[lua]
util.QuickTrace(pos, gui.ScreenToVector(gui.MousePos()) * 2048, LocalPlayer())
[/lua]
[QUOTE=_Undefined;20103979]Not sure I understand completely, but I think this is what you are after.
[lua]
util.QuickTrace(pos, gui.ScreenToVector(gui.MousePos()) * 2048, LocalPlayer())
[/lua][/QUOTE]
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.
[lua]
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=fFoV*fMouseX
local fPitch=fFoV*fMouseY
--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=vForward*fX + vRight*-fY + vUp*fZ
[/lua]
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:
[code]
_
_--
o-_
--_
[/code]
And the other has a large FoV:
[code]
/
/
o
\
\
[/code]
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.
[QUOTE=theJ89;20104379]If my understanding of how projection works in computer graphics is correct, I may have a solution for you.[/QUOTE]
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:
[img]http://errur.com/New/Uploads/255.jpg[/img]
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.
[editline]12:01PM[/editline]
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.
I didn't read your code because it confused me a bit, so if I restate what you're talking about ignore this.
I'd say you should do some trig and work out the angles of the mouse based off the view angle and coords and then do a trace to figure out where it ends and do your stuff to there.
Hmm, here, lets try something different:
[lua]
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
--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()
--Build an aimvec in terms of world coordinates.
local vAimVec=( (vForward*(.5/math.tan(fFoV))) - (vRight*fMouseX) + (vUp*fMouseY)):Normalize()
[/lua]
Edit: Post #4600!
I have a similar problem, my view is a top down view and I'm using the player as a camera, the aim of the player is the center of the screen and when I click somewhere on the screen it always gives a world vector NEAR the player, if I click on the right side of the screen it'll be by the right of the player but always near the aim which is at center
this is using util.TraceLine( util.GetPlayerTrace( LocalPlayer(), gui.ScreenToVector( gui.MousePos() ) ) ).HitPos
theJ89's code didnt work too
any idea ???
[lua]local orig = CAMERA_POS
local dir = LocalPlayer():GetCursorAimVector();
local trace = util.QuickTrace(orig, dir*1e9, LocalPlayer());[/lua]
CAMERA_POS is the origin of the players camera that you set in CalcView.
That works for me.
works perfectly thank you
Alright... here goes! I know it's been a while but I finally got the code working!
[lua]
--[[
Give this function screen coordinates 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.
aCamRot is the angle your camera is at
fFoV is the Field of View (FOV) of your camera in ___radians___
]]--
local function LPCameraScreenToVector(iScreenX,iScreenY,iScreenW,iScreenH,aCamRot,fFoV)
--We use half screen widths/half screen heights in the code
local fHalfWidth = iScreenW*0.5;
local fHalfHeight = iScreenH*0.5;
--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 it's width is the screen width, and it's height is the screen height.
local d = fHalfWidth/math.tan(fFoV*0.5);
--Forward, right, and up vectors (need these to convert from local to world coordinates
local vForward=aCamRot:Forward();
local vRight=aCamRot:Right();
local vUp=aCamRot:Up();
--Then convert vec to proper world coordinates and return it
return (vForward*d + vRight*(iScreenX-fHalfWidth) + vUp*(fHalfHeight-iScreenY)):Normalize();
end
[/lua]
Related material: "Frustrum" of a pyramid:
[img]http://www.salmonsalvo.net/blog/wp-content/uploads/2008/10/frustrum_d1.png[/img]
Ok, you are awesome!
Wait, so would that code work if you had CalcView modifying the viewpoint to a fixed position, and wanted your player to look where you aimed the mouse?
I'd say yes since you specify all the camera and screen parameters. You'd have to pass the function the modified view angles.
[QUOTE=theJ89;22390574]I'd say yes since you specify all the camera and screen parameters. You'd have to pass the function the modified view angles.[/QUOTE]
Ok thanks, this will be useful for my Raamatukogu gamemode.
Sorry, you need to Log In to post a reply to this thread.