Translate EyeAngles to plane local angles

This is the third time I’ve tried to make this thread after CloudFlare’s captcha has deleted my post.

http://zombine.me/ss/Photoshop-19.01.17340.png[/t]

I would like to translate the green arrow, LocalPlayer():EyeAngles(), into the space of one of the walls. All of the walls’ :Forward() directions are marked.

The end result I’m hoping for is to turn this:
[t]http://zombine.me/ss/Photoshop-19.01.17340.png[/t]

into this:
[t]http://zombine.me/ss/hl2-19.01.17337.jpg[/t]

I’d like to calculate, given the player’s EyeAngles in relation to Vector(0, 0, 1), what the equivalent view angles (with roll being corrected, though) would be in relation to a plane.

Here’s the code I use to rotate around a plane.


local localAngle = angle_zero;

hook.Add("InputMouseApply", "SimulateMouse", function(cmd, x, y, ang)
	local axis = LocalPlayer():GetNW2Vector("Up", vector_up);
	local wpos, wang = WorldToLocal(LocalPlayer():EyePos(), localAngle, LocalPlayer():EyePos(), vector_up:Angle());
	local lpos, lang = LocalToWorld(LocalPlayer():EyePos(), wang, LocalPlayer():EyePos(), axis:Angle());


	local xx = math.Clamp(localAngle.x + y * GetConVar("m_pitch"):GetFloat(), -89, 89); -- Handle looking up/down
	local yy = localAngle.y - x * GetConVar("m_yaw"):GetFloat(); -- Handle looking left/right


	localAngle = Angle(math.NormalizeAngle(xx), math.NormalizeAngle(yy), 0);


	cmd:SetViewAngles(lang);


	return true;
end);

The variable “localAngle” is the player’s rotation in terms of the wall’s local angles, so if I set it to Angle(0, 180, 0), no matter which wall I pick, I will always face the sky.

I’d like to change “localAngle” in an outside function when transitioning from one plane to another so that the player’s EyeAngles don’t reset to whatever localAngle was at the time, or even back to angle_zero.

It could be a case of me not using WorldToLocal and LocalToWorld right, but the above hook does work as you would expect.

Here is what I’m trying in an outside function when transitioning from default eye angles to being on the wall in the above before/after pictures.


local eyePos, eyeAng = LocalPlayer():EyePos(), LocalPlayer():EyeAngles();


-- oldVal/newVal are from a NWVarProxy. In this case, we'll say oldVal is Vector(0, 0, 1),
-- representing the default "Up" direction.
-- newVal is the surface normal of the wall/plane I want to make the new "Up" direction.
local localEyePos, localEyeAngle = WorldToLocal(eyePos, eyeAng, eyePos, oldVal:Angle());
local worldPos, worldAng = LocalToWorld(wallPos, eyeAng, eyePos, newVal:Angle());


LocalPlayer():SetEyeAngles(worldAng);

The code obviously doesn’t work, otherwise I wouldn’t be here…

Sorry for the long read.

[editline]19th January 2017[/editline]

Since Facepunch deletes the content of my message when I want to edit, I have to correct that the first image of the before/after is obviously wrong.

Here is the correct image sequence.

[t]http://zombine.me/ss/hl2-19.01.17336.jpg[/t]
[t]http://zombine.me/ss/hl2-19.01.17337.jpg

[lua]
local inverted_matrix = Matrix()
inverted_matrix:Rotate(vector_up:Angle())
inverted_matrix:InvertTR()
local output_angle = inverted_matrix * input_angle:Forward()
output_angle:Rotate(axis)
output_angle = output_angle:Angle()
[/lua]
If I remember what I learned about matrices, this might work.

After screwing around with it I managed to get it to work. Have some messy code if you’d like to see, or even use it for something.


	local localAngle = angle_zero;
	local lerping = false;

	LocalPlayer():SetNWVarProxy("Up", function(ent, var, oldVal, newVal)
		local eyeAng = LocalPlayer():EyeAngles();
		local eyePos = LocalPlayer():EyePos();
		local targetAng = angle_zero;
		
		if (oldVal == vector_up) then
			targetAng = Angle(0, 0, 0);
		end;

		local fixedpos, fixedang = WorldToLocal(eyePos, eyeAng, eyePos, newVal:Angle());

		fixedang:RotateAroundAxis(vector_origin:Angle():Right(), 90)
		
		local worldPos, worldAng = WorldToLocal(eyePos, fixedang, eyePos, vector_up:Angle());
		local epos, eang = LocalToWorld(worldPos, worldAng, eyePos, newVal:Angle());

		localAngle = fixedang;

		local start = CurTime();
		lerping = true;

		hook.Add("Think", "f", function()
			local frac = math.Clamp((CurTime() - start) / 0.3, 0, 1);
			local roll = Lerp(frac, eyeAng.z, eang.z)
			local trueAng = Angle(eyeAng.x, eyeAng.y, roll);
			
			LocalPlayer():SetEyeAngles(trueAng);

			if (frac >= 1) then
				hook.Remove("Think", "f");
				lerping = false;
			end
		end);
	end);

	hook.Add("InputMouseApply", "SimulateMouse", function(cmd, x, y, ang)
		if (lerping) then return; end;

		local axis = LocalPlayer():GetNW2Vector("Up", vector_up);
		local wpos, wang = WorldToLocal(LocalPlayer():EyePos(), localAngle, LocalPlayer():EyePos(), vector_up:Angle());
		local lpos, lang = LocalToWorld(LocalPlayer():EyePos(), wang, LocalPlayer():EyePos(), axis:Angle());
		local xx = math.Clamp(localAngle.x + y * GetConVar("m_pitch"):GetFloat(), -89, 89);
		local yy = localAngle.y - x * GetConVar("m_yaw"):GetFloat();

		localAngle = Angle(math.NormalizeAngle(xx), math.NormalizeAngle(yy), 0);

		cmd:SetViewAngles(lang);


		return true;
	end);

Well I restarted my game and the above solution no longer works perfectly.
The problem I’m having now is the “think” hook isn’t properly rolling the camera to what it should be.
In fact it’s not rolling at all.

EDIT:

Nevermind set ‘fixedang.z’ to 0 and it worked