• 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.