Everything is done in Skulk:UpdateViewAngles(input) :
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1--> local angles = Angles(0,0,0) local coords = angles:GetCoords() coords.zAxis.x = math.cos(-input.pitch)*math.sin(input.yaw) // could be cos(input.pitch) coords.zAxis.y = math.sin(-input.pitch)*math.sin(input.yaw) // could be sin(input.pitch) coords.zAxis.z = math.cos(input.yaw)
local wallWalkingNormal = self.wallWalkingNormalCurrent coords.xAxis = wallWalkingNormal:CrossProduct(coords.zAxis) // could be (coords.zAxis,wallWalkingNormal) coords.yAxis = coords.zAxis:CrossProduct(coords.xAxis) // could be (coords.xAxis,coords.zAxis)
I will try it out once steam starts working again...
Could you elaborate on what works and what doesn't?
Tried it out. Something is wrong:
putting Print(ToString(coords.zAxis)) in your code right before the cross product, and Print(ToString(cameraCoords)) in skulk_client.lua. The camera coords for zAxis was [0.05,0,28,0,96]. In our code, the coords were [-0.64,0.28,0.71]. It is obvious that something is wrong with how we attain the coords.
It occurred to me this morning that the code could be simpler (more reliable?), because the function for getting the z axis is already built-in to the game? <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local angles = Angles(input.pitch,input.yaw,0) // this function local coords = angles:GetCoords()
local wallWalkingNormal = self.wallWalkingNormalCurrent coords.xAxis = wallWalkingNormal:CrossProduct(coords.zAxis) // could be (coords.zAxis,wallWalkingNormal) coords.yAxis = coords.zAxis:CrossProduct(coords.xAxis) // could be (coords.xAxis,coords.zAxis)
2) wallWalkingNormal = [0,1,0] input.pitch,input.yaw = 90,0 (degrees) => coords.zAxis = [0,1,0] coords.xAxis = wallWalkingNormal x coords.zAxis = [0,1,0]x[0,1,0] = [0,0,0] well... naturally that didn't work coords.yAxis = coords.zAxis x coords.xAxis = ---
Now, how do you deal with the situation where wallWalkingNormal is parallel with coords.zAxis?
Also the <u>length</u> of coords.xAxis and coords.yAxis change, which could be problematic - they should be unit vectors, no?
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local angles = Angles(input.pitch,input.yaw,0) // this function local coords = angles:GetCoords()
local wallWalkingNormal = self.wallWalkingNormalCurrent coords.xAxis = getNormalizedVector(wallWalkingNormal:CrossProduct(coords.zAxis)) // could be (coords.zAxis,wallWalkingNormal) coords.yAxis = getNormalizedVector(coords.zAxis:CrossProduct(coords.xAxis)) // could be (coords.xAxis,coords.zAxis)
angles:BuildFromCoords(coords) self:SetViewAngles(angles)<!--c2--></div><!--ec2--> I'm not sure that would actually fix anything. And we still need to fix it for the parallel case.
Tried out that out. Same problem. It seems like the only issue is with the x and z coordinates of the z-axis. Aiming the game so y=0 gave 0 in our implementation too, cameracoords zAxis: [0.00,0.00,1.00] while our was [0.706,0.00,0.707]. Don't worry about the lengthes, they get normalized.
I've been thinking about when they are parallel. The game handles it by... Trying to find the code for the implementation... Adding the codes I find that might help... From function Skulk:PreUpdateMove(input, runningPrediction) <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1--> coords.xAxis = coords.yAxis:CrossProduct( coords.zAxis ) if (coords.xAxis:Normalize() < 0.01) then // We have to choose the x-axis arbitrarily since we're // looking along the normal direction. coords.xAxis = coords.yAxis:GetPerpendicular() end<!--c2--></div><!--ec2-->
self:GetViewCoords() works to get the right vectors though. Trying to modify the code and see how it works. I realised that after like 15 min of trying to get it to work, I hadn't loaded the mod xD
<!--quoteo(post=1897809:date=Jan 28 2012, 03:22 AM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 28 2012, 03:22 AM) <a href="index.php?act=findpost&pid=1897809"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->How does it determine the perpendicular vector when there are an infinite number of perpendicular vectors?<!--QuoteEnd--></div><!--QuoteEEnd-->
Good question. I have no idea myself.
This is what I work with so far <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function Skulk:UpdateViewAngles(input)
local viewCoords = self:GetViewCoords() local coords = viewCoords
local wallWalkingNormal = self.wallWalkingNormalCurrent
coords.xAxis = wallWalkingNormal:CrossProduct(coords.zAxis) // could be (coords.zAxis,wallWalkingNormal) coords.yAxis = coords.zAxis:CrossProduct(coords.xAxis) // could be (coords.xAxis,coords.zAxis) Print(ToString(coords)) Angles:BuildFromCoords(coords) Print(ToString(Angles)) self:SetViewAngles(Angles)
//yuuki:for debug if math.random() < 0.1 then local startPoint = Vector(self:GetOrigin()) local extents = self:GetExtents() startPoint.y = startPoint.y + extents.y
// Update view offset from crouching local viewY = self:GetMaxViewOffsetHeight() viewY = viewY - viewY * self:GetCrouchShrinkAmount() * self:GetCrouchAmount()
// Don't set new view offset height unless needed (avoids Vector churn). local lastViewOffsetHeight = self:GetSmoothedViewOffset().y if math.abs(viewY - lastViewOffsetHeight) > kEpsilon then self:SetViewOffsetHeight(viewY) end
end<!--c2--></div><!--ec2-->
It's not working, cause I do the angles wrong; script error. I don't really get them.
Trying to find what is wrong in the code, I eventually turned the game to windowed mode and ran this code:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local angles = Angles(input.pitch,input.yaw,0) // this function local wallWalkingNormal = self.wallWalkingNormalCurrent
Going back to a previous approach. My goal was: - using a reference axes - taking a vector - finding the vector relative to the x-axis I somehow realised that the absolute reference axes correspond to an identity matrix [1 0 0] [0 1 0] [0 0 1] That is, multiplying a 1x3 vector [x,y,z] by that matrix would yield the exact same vector [x,y,z] - this was ideal. So I looked up matrix multiplication to get a refresher, did some tests, and confirmed my hypothesis that multiplying a vector by a matrix that represents a different reference axes WILL re-express that vector as relative to the reference axes*, like so: <img src="http://i.imgur.com/aXpdC.png" border="0" class="linked-image" /> *<!--coloro:gray--><span style="color:gray"><!--/coloro--><!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->I wonder if they taught this in math class but I just never paid attention...<!--sizec--></span><!--/sizec--><!--colorc--></span><!--/colorc--> I already knew that the reference axes would be built from the input.pitch and input.yaw angles, which was a simple task. However, I later realised that, given what I was trying to achieve (keeping the reference y-axis parallel with the absolute y-axis so that I could take the angle between the relative wallNormal and absolute "up"), I had to omit input.pitch when building the reference axis (yaw only moves the x and z axes).
After a bit of simplification (omitting needless steps), I ended up with the following code: <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceAngles = Angles(0,input.yaw,0) local referenceCoords = referenceAngles:getCoords()
local wallNormal = self.wallWalkingNormalCurrent ... // step(s) that transform wallNormal into appropriate 1x3 matrix, and referenceCoords into appropriate 3x3 matrix (see image) wallNormal = wallNormalMatrix * referenceCoordsMatrix
local wallNormalRoll = wallNormal wallNormalRoll.z = 0 wallNormalRoll = GetNormalizedVector(wallNormalRoll)
local wallNormalPitch = wallNormal wallNormalPitch.x = 0 wallNormalPitch = GetNormalizedVector(wallNormalPitch)
if wallNormalRoll.x < 0 wallRoll = -math.acos(wallNormalRoll.y) else wallRoll = math.acos(wallNormalRoll.y) end
if wallNormalPitch.z < 0 wallPitch = math.acos(wallNormalPitch.y) else wallPitch = -math.acos(wallNormalPitch.y) end
local viewAngles = Angles(wallPitch + input.pitch, input.yaw, wallRoll) self:SetViewAngles(viewAngles)<!--c2--></div><!--ec2--> <img src="http://i.imgur.com/wMTgR.png" border="0" class="linked-image" /> I'm hoping this works. If it doesn't, I think you have to restore the GetCoords(), GetRotation() and BuildFromCoords() functions. And if even that doesn't work, then... well, there's still hope, but... later^.
See the matrix multiplication as well as the reasoning, code and simplification in this excel spreadsheet (two sheets): <a href="http://www.mediafire.com/?yl2fc99kt7v39ic" target="_blank">mediafire</a> <!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->Note that the reference axes are hard-coded, and NOT determined by the roll/pitch/yaw angles. I didn't feel the need to implement that.<!--sizec--></span><!--/sizec-->
^I have a feeling I might be able to further simplify some of the intermediate steps (bypass the determination of the individual wallRoll and wallPitch angles) by keeping the reference axes idea but taking a different approach in getting the viewCoords, by making use of: - "coords"="angles":GetCoords() // converting a set of angles to a set of reference axes and/or - "angles":BuildFromCoords() // converting a set of reference axes to a set of angles; particularly this and/or - cross products, etc.
But when you do that, don't you just express the same vector in the new base? What we want to do is rotate the vectors so that they relate to the wallwalking plane the same way they related to the floor. Actually, not quite the same, as we want to keep aiming at the same point... I think I'll have to sleep on this for awhile. I should have payed more attention to my mathematic courses with matrixes and vectors, so that I would know what we needed to do.
Uh, I'm not sure what you mean, maybe you're misunderstanding something. I'm not using your idea, I'm going back to the previous idea, where pitch and roll are based on the angle between the wallNormal and "up", then you shift the view by pitch and roll (and input.pitch and input.yaw). So naturally I HAVE TO express the same vector in the new base.
Anyway, here's another alternative using the GetRotation() function:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceAngles = Angles(0,input.yaw,0) local referenceCoords = referenceAngles:getCoords()
local wallNormal = self.wallWalkingNormalCurrent ... // step(s) that transform wallNormal into appropriate 1x3 matrix, and referenceCoords into appropriate 3x3 matrix (see image) wallNormal = wallNormalMatrix * referenceCoordsMatrix
local wallNormalRoll = wallNormal wallNormalRoll.z = 0 wallNormalRoll = GetNormalizedVector(wallNormalRoll)
local wallNormalPitch = wallNormal wallNormalPitch.x = 0 wallNormalPitch = GetNormalizedVector(wallNormalPitch)
if wallNormalRoll.x < 0 wallRoll = -math.acos(wallNormalRoll.y) else wallRoll = math.acos(wallNormalRoll.y) end
if wallNormalPitch.z < 0 wallPitch = math.acos(wallNormalPitch.y) else wallPitch = -math.acos(wallNormalPitch.y) end
local viewAngles = Angles(0, 0, 0) local viewCoords = viewAngles:GetCoords()
Ahh, I realised there's an error. You can't really do <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceAngles = Angles(0,input.yaw,0) local referenceCoords = referenceAngles:getCoords()<!--c2--></div><!--ec2--> since it doesn't really mean what we want it to mean. replace those two lines with <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceCoords = self.lastCoords // last known axes, found in function Skulk:PreUpdateMove local referenceAngles = Angles(0,0,0) // initialise referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes referenceAngles.pitch = 0 // only interested in yaw, i.e. reversing effect of pitch referenceAngles.roll = 0 // only interested in yaw, i.e. reversing effect of roll referenceCoords = referenceAngles:getCoords() // new reference axes<!--c2--></div><!--ec2-->
local referenceCoords = self.lastCoords // last known axes, found in function Skulk:PreUpdateMove local referenceAngles = Angles(0,0,0) // initialise referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes referenceAngles.pitch = 0 // only interested in yaw, i.e. reversing effect of pitch referenceAngles.roll = 0 // only interested in yaw, i.e. reversing effect of roll referenceCoords = referenceAngles:getCoords() // new reference axes
local wallNormal = self.wallWalkingNormalCurrent ... // step(s) that transform wallNormal into appropriate 1x3 matrix, and referenceCoords into appropriate 3x3 matrix (see image) wallNormal = wallNormalMatrix * referenceCoordsMatrix
local wallNormalRoll = wallNormal wallNormalRoll.z = 0 wallNormalRoll = GetNormalizedVector(wallNormalRoll)
local wallNormalPitch = wallNormal wallNormalPitch.x = 0 wallNormalPitch = GetNormalizedVector(wallNormalPitch)
if wallNormalRoll.x < 0 wallRoll = -math.acos(wallNormalRoll.y) else wallRoll = math.acos(wallNormalRoll.y) end
if wallNormalPitch.z < 0 wallPitch = math.acos(wallNormalPitch.y) else wallPitch = -math.acos(wallNormalPitch.y) end
local viewAngles = Angles(0, 0, 0) local viewCoords = viewAngles:GetCoords()
Soul_RiderMod BeanJoin Date: 2004-06-19Member: 29388Members, Constellation, Squad Five Blue
edited January 2012
When using this code, I get the error - <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->Angles.BuildFromCoords(angles, nil) Correct overloads should be Angles.BuildfromCoords(Angles Self, Coords Coords)<!--c2--></div><!--ec2-->
This refers to the following line of code:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes<!--c2--></div><!--ec2-->
We need to supply angles self as well as coordinates. Currently the function is just taking the coords as angles.
Summary of the properties we want the mod to have: <ul><li>zAxis unchanged</li><li>xAxis to be completely in the wallwalking plane</li><li>yAxis to be right-handed perpendecular to zAxis and xAxis</li><li>Forward/backward movement to take place in the zAxis direction when flattened on the wallwalking plane</li><li>Strafing to take place along the xAxis</li><li>Pitch to rotate around the xAxis</li><li>Yaw to rotate around the wallwalking normal</li></ul> Keeping the zAxis unchanged is optional, but everything else is required to perserve the same behaviour as the ground. Why I include the zAxis: imagine as a marine, running up a slope would change your pitch the same angle as the slope. Thats how it would be otherrwise.
Soul_RiderMod BeanJoin Date: 2004-06-19Member: 29388Members, Constellation, Squad Five Blue
Slightly OT -
You gotta love some of the notes from the programmers :)
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function PowerManager:GetIsLocationPowered(entity, sendEvent) //$AS TODO: Use powerGrid instead of this horrible search method that makes me sad<!--c2--></div><!--ec2-->
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// Skulks do not respect ladders due to their wall walking superiority.<!--c2--></div><!--ec2-->
<!--quoteo(post=1897856:date=Jan 28 2012, 05:14 PM:name=Soul_Rider)--><div class='quotetop'>QUOTE (Soul_Rider @ Jan 28 2012, 05:14 PM) <a href="index.php?act=findpost&pid=1897856"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->When using this code, I get the error - <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->Angles.BuildFromCoords(angles, nil) Correct overloads should be Angles.BuildfromCoords(Angles Self, Coords Coords)<!--c2--></div><!--ec2-->
This refers to the following line of code:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes<!--c2--></div><!--ec2-->
We need to supply angles self as well as coordinates. Currently the function is just taking the coords as angles.<!--QuoteEnd--></div><!--QuoteEEnd--> Ummm... what do we need exactly? Are you saying there is a missing parameter? What is Angles Self? Or Coords Coords for that matter.
If you look at the other code: <!--quoteo--><div class='quotetop'>QUOTE </div><div class='quotemain'><!--quotec-->angles:BuildFromCoords(coords)<!--QuoteEnd--></div><!--QuoteEEnd--> This generally works fine, doesn't it?
Soul_RiderMod BeanJoin Date: 2004-06-19Member: 29388Members, Constellation, Squad Five Blue
Harimau, I'm experimenting with some code, but I'm not 100% certain what you are trying to do, as I couldn't follow the maths. Could you break it down into laymans terms, so I can convert it into code :)
Your notes are good, but not deep enough for me. I am looking at the following pieces of code but get confused by your statements.
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceCoords = self.lastCoords // last known axes, found in function Skulk:PreUpdateMove local referenceAngles = Angles(0,0,0) // initialise referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes referenceAngles.pitch = 0 // only interested in yaw, i.e. reversing effect of pitch referenceAngles.roll = 0 // only interested in yaw, i.e. reversing effect of roll referenceCoords = referenceAngles:getCoords() // new reference axes<!--c2--></div><!--ec2-->
Here you seem to be taking the current coordinates, turning them into angles, removing 2 axis (X&Y?) then turning them back into coordinates. The code I am looking at that is similar to this is :
From that you can see that the goals are: 1. Get player's (model's?) angles. 2. Set pitch and roll to zero, to line up the reference x-z plane with the absolute x-z plane. 3. Build coords (reference axes) from that. 4a. Transform those reference axes into a matrix. (columns: x-axis vector, y-axis vector, z-axis vector. rows: x-components; y-components; z-components) 4b. Transform wallnormal ("columns": x-component, y-component, z-component) 4c. Multiply the absolute wallnormal vector by the matrix, to get the relative wallnormal vector. 5. Flatten the wallnormal along the x-y plane and z-y plane for roll and pitch respectively (and normalise). 6. Find the pitch and roll angles of the wallnormal vector by taking the inverse cosine of the y-component of the wallnormal vector (which is the dot product of the y-axis and the wallnormal). 7. Shift the player's orientation (camera) to these angles [this is the player's movement plane]. 8. Shift the player's orientation (camera) by the input angles (input.yaw and input.pitch) [mouse-horizontal and mouse-vertical].
I may have overlooked some things, but hopefully those can be sorted out. A necessary consequence of this approach is that the z-axis DOES change when you change surface. However, perhaps the approach could be adapted (in step 8) so that it doesn't: I think it would involve calculating the angular difference (pitch and yaw) between the calculated z-axis and the preserved z-axis, then reversing the changes (compensation). One benefit of such an approach is that you could possibly add another key that would allow you to switch between the two modes at will (i.e. switching the compensation on and off).
Also, I was told earlier that self:GetCoords() wouldn't work, because there aren't yet self.coords when you call the function, which is why the game stores it as self.lastCoords in the function Skulk:PreUpdateMove? (On the other hand... self.lastCoords = self:GetCoords() in every instance)
<!--quoteo(post=1897869:date=Jan 28 2012, 07:00 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 28 2012, 07:00 PM) <a href="index.php?act=findpost&pid=1897869"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Summary of the properties we want the mod to have: <ul><li>zAxis unchanged</li><li>xAxis to be completely in the wallwalking plane</li><li>yAxis to be right-handed perpendecular to zAxis and xAxis</li><li>Forward/backward movement to take place in the zAxis direction when flattened on the wallwalking plane</li><li>Strafing to take place along the xAxis</li><li>Pitch to rotate around the xAxis</li><li>Yaw to rotate around the wallwalking normal</li></ul><!--QuoteEnd--></div><!--QuoteEEnd--> That's a comprehensive list, and I don't think there's anything to add. However, I would like to split the player movement bullet-points from the player view bullet-points, and perhaps stress the particular relevance of input.yaw and input.pitch (which are your last two bullet points, I believe).
Maybe we should take a step back and start again from first principles... My problem right now is that I don't understand how the camera angle links to the movement, I'm just going with it. So I can't write effective, working code, I can only discuss concepts. Looking at the mod - it looks like Yuuki has actually added the UpdateViewAngles function to Skulk.lua (at least I couldn't find it in the original 193 code): is that because then it overrides the UpdateViewAngles function in another lua file? And I'm not entirely sure whether we're specifying the "state" of the camera at any point in time or if we're modifying the state by a "change" (old state + difference = new state). The key difference is that the state exists at a single point in time and so is independent of the duration between frames, while the "change" depends on (is scaled by) the duration between frames and therefore the state depends on the previous frame. Like: the wallNormal doesn't (usually) change with time, but it seems to me that input.yaw and input.pitch would be dependent on the input time and therefore the previous state. But then it's also possible that input.yaw is simply stored as a number and then modified, kind of like the position of a cursor on the screen (then is it possible to rotate right a given number of times and hit a limit in the code because it can't store a bigger number than that?), and the same for input.pitch. And I'm not entirely sure whether or not some of the stuff in (for example) Skulk:PreUpdateMove might contradict the stuff we've added in Skulk:UpdateViewAngles. Because Skulk:PreUpdateMove does appear to do something with the camera (the view angles).
<!--quoteo(post=1897869:date=Jan 28 2012, 07:00 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 28 2012, 07:00 PM) <a href="index.php?act=findpost&pid=1897869"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Keeping the zAxis unchanged is optional, but everything else is required to perserve the same behaviour as the ground. Why I include the zAxis: imagine as a marine, running up a slope would change your pitch the same angle as the slope. Thats how it would be otherrwise.<!--QuoteEnd--></div><!--QuoteEEnd--> Yeah I'm not 100% convinced, I think if we can get it as an option (preferably an option that you can, at will, toggle or use as a modifier, while in-game) it would be ideal. Keeping the zAxis unchanged would be good for tracking targets and keeping from getting disoriented, but somewhat cumbersome for quicker movement in an environment you're already familiar with. On the other hand, letting the zAxis move as well would let the player move around more "cleanly" in an environment they're already familiar with and remove any strange dependencies.
One way to break it down, is, let's consider four situations inside a horizontal cylinder.
Your orientation (zAxis) starts parallel, and you're 1. facing the side of the cylinder and strafing along the circular... 2. facing the inside of the cylinder, and walking forward along the circular... a. ...with zAxis modified. b. ...with zAxis preserved.
1a: Player view would roll, and nothing else. * 1b: Player view would roll, and nothing else. * identical 2a: Player view would pitch, and nothing else. Kind of like a rollercoaster<u>^</u>. 2b: Player view would ...?** **At the bottom of the cylinder, the player would be facing parallel with the cylinder wall, essentially he's looking "directly ahead" in a non-wallwalking context. **At the front of the cylinder, the player would be facing into the cylinder, essentially he would be looking "down" in a non-wallwalking context. **->Now he's "stuck" because moving forward from this point would mean he'd either be ... standing still or turning around and around on the same spot to preserve the zAxis (yaw or roll), or ... we remove the upper and lower limits of the pitch so that he can pitch 360 degrees to preserve the zAxis (like a flight simulator<u>^</u>), or ... his orientation would shift so that he's always looking down into the cylinder (no longer preserving the z-axis).
<u>^</u><!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->gotta look at creating rollercoaster and flight simulator mods when we're done with this...<!--sizec--></span><!--/sizec-->
Soul_RiderMod BeanJoin Date: 2004-06-19Member: 29388Members, Constellation, Squad Five Blue
All the code that goes in Skulk:UpdateViewAngles() is just a replacement for the Skulk:AdjustPlayerCameraCoords.
Theoretically, the could be written in Skulk:AdjustPlayerCameraCoords, or we could create a whole new function to use it. At the moment the code is just replacing the updating camera and trying to fix the inputs. The code works with the camera, and changes the inputs, although not as we want, so essentially we have two pieces of code doing the same job :)
Whichever function we use is irrelevant, but we need to remove the other.
Taking the scalar of the right top image, you would get a negative number. Scalars doesn't produce vectors, but two vectors that are anti-parallel give negative scalars, while parallel vectors give positive.
For your top left figure: [wallnormal].[x-axis]=[1,1,0].[1,0,0]=1
For the top left figure: [wallnormal].[x-axis]=[-1,1,0].[1,0,0]=-1
<!--quoteo(post=1898004:date=Jan 29 2012, 06:29 AM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 29 2012, 06:29 AM) <a href="index.php?act=findpost&pid=1898004"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec--><u>^</u><!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->gotta look at creating rollercoaster and flight simulator mods when we're done with this...<!--sizec--></span><!--/sizec--><!--QuoteEnd--></div><!--QuoteEEnd--> Lerk flight-simulator. Strafing causes you to drift, yawing causes you to roll. Full view rotation and free movement, with loops and barrel-rolls. If you try to change direction while not gliding, then you would just drift and stall.
I agree with all of your post. Keeping the z-axis (absolutely) unchanged in simple geometry is not always good, but more complex geometry, such as walking over props and other kind of bumps is made much easier. The downside with a changing z-axis is that if you encounter a series of (small) angle changes, they can together build up making you end up walking in a different direction then you expected. With the z-axis being unchanged you may end up slightly of the target, but you will be going in the right direction at least.
We probably should take a step back and start from scratch with a more calculated approach. Instead of trying to change a function to fit our needs, I think it would be good if we finalized exactly what we need to do, then we just have to find where to insert the code. How do we make sure that another function doesn't change the camera and inputs again to something else though? I don't know.
Returning to the lerk flight-simulator, I'm fairly sure it is much easier to do, we just need to rotate the camera with your yaw instead in moving it sideways, pretty much just changing your yaw inputs to roll I guess? Then we make the strafing affect the yaw and we should have a working flight simulator. We don't need that to relate to wall normals and such.
<!--quoteo--><div class='quotetop'>QUOTE </div><div class='quotemain'><!--quotec-->We probably should take a step back and start from scratch with a more calculated approach. Instead of trying to change a function to fit our needs, I think it would be good if we finalized exactly what we need to do, then we just have to find where to insert the code. How do we make sure that another function doesn't change the camera and inputs again to something else though? I don't know.<!--QuoteEnd--></div><!--QuoteEEnd-->
If you comment self:SetViewAngles(angles) in Skulk:UpdateViewAngles(input) the camera doesn't response to mouse anymore, so I think we're good with that.
<!--quoteo(post=1898027:date=Jan 29 2012, 07:47 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 29 2012, 07:47 PM) <a href="index.php?act=findpost&pid=1898027"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->For your top left figure: [wallnormal].[x-axis]=[1,1,0].[1,0,0]=1
For the top left figure: [wallnormal].[x-axis]=[-1,1,0].[1,0,0]=-1<!--QuoteEnd--></div><!--QuoteEEnd--> We're taking the dot product of the y-axis, not the x-axis. 45 degrees right of y-axis: [wallnormal].[y-axis]=[b,b,0].[0,1,0]=b 45 degrees left of y-axis: [wallnormal].[y-axis]=[-b,b,0].[0,1,0]=b Different wallnormal, but same dot product: therefore, same calculated angle.
Also, you haven't normalised. Here, b = sqrt(1/2)
Even if you do take the angle for the x-axis, consider these instead: 45 degrees above x-axis: [wallnormal].[x-axis]=[b,b,0].[1,0,0]=b 45 degrees below x-axis: [wallnormal].[x-axis]=[b,-b,0].[1,0,0]=b Different wallnormal, but same dot product: therefore, same calculated angle.
Simply put, angles to the left and right side of the y-axis (when x is positive or negative) cannot be implicitly distinguished, and angles between the top and bottom side of the x-axis (when y is positive or negative) cannot be implicitly distinguished. That's why you cannot use a single function (at least you cannot use arccos), but you must explicitly distinguish between the different angles: and I do this by distinguishing between x negative (roll negative) and x positive (roll positive).
The reason that we get the x and z axes with respect to the player, is that distinguishing the -absolute- roll/pitch changes with the player's orientation. (We need to determine the -absolute- roll/pitch in order to determine the -absolute- movement plane). A wall to your left becomes a wall to your right if you simply turn around, so while in the former case you would roll right, in the latter case you would roll left.*
*Actually, I'm not so sure of this anymore. Is this step even necessary? It would certainly be necessary if you were dealing with changes (shift the player's current view), but not if you were dealing with states (build a new view based on given parameters).
Seriously, there's just too much we don't know. The first thing I want to know is: changes or states? The second thing I want to know is: are input.pitch and input.yaw changes (difference between frames) or states?
<!--quoteo(post=1898014:date=Jan 29 2012, 04:57 PM:name=Soul_Rider)--><div class='quotetop'>QUOTE (Soul_Rider @ Jan 29 2012, 04:57 PM) <a href="index.php?act=findpost&pid=1898014"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->All the code that goes in Skulk:UpdateViewAngles() is just a replacement for the Skulk:AdjustPlayerCameraCoords.<!--QuoteEnd--></div><!--QuoteEEnd--> I don't see either of those functions in Skulk.lua...
I have an idea. Both movement and camera are now related to the absolute coordinates. We should be able to transform this coordinate system using euler angles into the coordinate system for the wallwalking plane. Using the saame angles to transform the camera we should look in the same relative way compared to the wallwalking plane as we did for the absolute plane. Having the correct rotation on the wallwalking plane will make the camera change intuitive. This would make the z-axis change. To keep it we need to compensate the angle change somehow. We can make that a later project once we got a working model.
<!--quoteo(post=1898095:date=Jan 29 2012, 11:18 PM:name=Soul_Rider)--><div class='quotetop'>QUOTE (Soul_Rider @ Jan 29 2012, 11:18 PM) <a href="index.php?act=findpost&pid=1898095"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->@ Fluid - I posted that earlier :)
AngleVelocity.euler.y x Transformational matrix. It's in the code for AVP wall-walking which I posted.<!--QuoteEnd--></div><!--QuoteEEnd-->
Oh, I must have missed that then :) So any idea on how to get the math to do that right?
<!--quoteo(post=1898041:date=Jan 29 2012, 02:33 PM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 29 2012, 02:33 PM) <a href="index.php?act=findpost&pid=1898041"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->We're taking the dot product of the y-axis, not the x-axis.<!--QuoteEnd--></div><!--QuoteEEnd--> The picture sais when x is poistive, roll is positive, and when x is negative, roll is negative, so I assumed you meant the dot product with the x-axis. I must have misunderstood you, my bad.
Prefacing with some relevant links: <a href="http://damien.kodingen.com/ns2docs/index.html" target="_blank">NS2Docs</a> <a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Coords" target="_blank">Objects:Coords</a> <a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Angles" target="_blank">Objects:Angles</a> <a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Vector" target="_blank">Objects:Vector</a> <a href="http://www.unknownworlds.com/ns2/forums/index.php?showtopic=115926&view=findpost&p=1897270" target="_blank">AvP source code from Soul_Rider in original discussion thread</a> <a href="http://www.unknownworlds.com/ns2/forums/index.php?showtopic=115926&view=findpost&p=1897366" target="_blank">Yuuki's first prototype code in original discussion thread</a>
<!--quoteo(post=1898099:date=Jan 30 2012, 06:40 AM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 30 2012, 06:40 AM) <a href="index.php?act=findpost&pid=1898099"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->The picture sais when x is poistive, roll is positive, and when x is negative, roll is negative, so I assumed you meant the dot product with the x-axis. I must have misunderstood you, my bad.<!--QuoteEnd--></div><!--QuoteEEnd--> The size of the angle is dependent on the y-component (dot product of unit vector with y-axis); the range of the arccos function is only from 0 to 180 degrees, only half a circle. The orientation of the angle is dependent on the x-component (positive or negative, i.e. right side or left side), so you get two half circles to make a full circle.
So guys, input.yaw, input.pitch. Fill me in. :P How do they work?
Until I have a more concrete idea, from here on I'm going to assume that input.yaw and input.pitch are independent of the time, and the 'difference' is taken elsewhere. That is, there is a function where for example (if I were to prototype it, it would be something like this): <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->yawdifference(change in angle between frames) = f( input.mouse.x(distance moved between frames) * sensitivity * input.time(time between frames) ) pitchdifference(change in angle between frames) = f( input.mouse.y(distance moved between frames) * sensitivity * input.time(time between frames) ) input.yaw(currentframe) = input.yaw(previousframe) + yawdifference(change in angle between frames) input.pitch(currentframe) = input.pitch(previousframe) + pitchdifference(change in angle between frames) // if input.angle > (180 degrees) // then input.angle = input.angle - (360 degrees) // if input.angle < (-180 degrees) // then input.angle = input.angle + (360 degrees) // so that input.angle is constrained between -180 and 180 degrees // clamp pitch to +90, -90 degrees, so you can only look straight up and straight down, might take place in a different function<!--c2--></div><!--ec2--> Essentially, input.yaw and input.pitch describe your <b>current orientation</b> (with respect to a frame of reference, usually the absolute frame of reference), not the difference between your current orientation in the current frame and your previous orientation in the previous frame.
<!--quoteo(post=1898084:date=Jan 30 2012, 04:08 AM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 30 2012, 04:08 AM) <a href="index.php?act=findpost&pid=1898084"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->I have an idea. Both movement and camera are now related to the absolute coordinates. We should be able to transform this coordinate system using euler angles into the coordinate system for the wallwalking plane. Using the saame angles to transform the camera we should look in the same relative way compared to the wallwalking plane as we did for the absolute plane. Having the correct rotation on the wallwalking plane will make the camera change intuitive. This would make the z-axis change. To keep it we need to compensate the angle change somehow. We can make that a later project once we got a working model.<!--QuoteEnd--></div><!--QuoteEEnd--> This is essentially what I've been trying to do. The euler angles are pitch, yaw and roll. We don't need to change the yaw, but we want to change the pitch and roll in order to line up the wallnormal with the player's reference frame's y-axis. The rotation matrix is coords.GetRotation(axis, angle). I think that, mathematically, my most recent implementation is most sound. Code-wise I'm not sure of though.
The game already has some transformations that we know of: <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->angles = Angles(pitch,yaw,roll) // create a set of angles, from specified angles, that describe the orientation coords = angles:GetCoords() // create a set of axes, from a set of angles, that describe the orientation angles:BuildFromCoords(coords) // create a set of angles, from a set of axes, that describe the orientation coords*coords.GetRotation(axis, angle) // rotate a set of axes, around a specified axis, for a specified angle : this is a transformation matrix<!--c2--></div><!--ec2-->
So what we want to do: Approach A) 1. Get a wallnormal 2. Get the correct pitch and roll - but how? 3. Create a set of axes (or rotate a reference axes) corresponding to this pitch and roll 4. Build a camera corresponding to this set of axes. This is the reference frame. 5. Rotate the camera around the new y-axis by input.yaw, rotate the camera around the new x-axis by input.pitch
Approach B) 1. Get a wallnormal 2. Create a set of axes with wallnormal as the y-axis and the x-z plane parallel to the wall - but what orientation should the x and z axes be in? 3. Build a camera corresponding to this set of axes. This is the reference frame. 4. Rotate the camera around the new y-axis by input.yaw, rotate the camera around the new x-axis by input.pitch
input.yaw = 90 (facing "right" with respect to the reference frame) input.pitch = 0 (facing "directly ahead" with respect to the reference frame) wallnormal = [1,0,0] (pointing "right" with respect to the absolute reference frame) the player walks backwards into the wall (or, alternatively, the player is facing "forward", strafes left into the wall, THEN turns right while on the wall)
our goal: the player's orientation should be facing straight down, with: x-axis[0,0,-1] = 'behind' y-axis[1,0,0] = 'right' = wallnormal, no player pitch z-axis[0,-1,0] = 'down'
from the wallnormal, you get: wallpitch = 0 with respect to the absolute reference frame wallroll = +90 with respect to the absolute reference frame
you could build the reference frame now angles = Angles(wallpitch,0,wallroll) coords = angles:GetCoords()
this should lead to a reference frame: x-axis[1,0,0]->yaw(0),roll(90)->[0,-1,0] = 'down' y-axis[0,1,0]->pitch(0),roll(90)->[1,0,0] = 'right' = wallnormal z-axis[0,0,1]->pitch(0),yaw(0)->[0,0,1] = 'forward' Notice that in the case of the z-axis, it retains its orientation, because the angles it is changed by (pitch and yaw) are zero.
now you rotate the player with input.yaw and input.pitch pitch around x-axis, yaw around y-axis, roll around z-axis x-axis[0,-1,0]->yaw(90),roll(0)->[0,0,-1] = 'behind' y-axis[1,0,0]->pitch(0),roll(0)->[1,0,0] = 'right' = wallnormal, no player pitch z-axis[0,0,1]->pitch(0),yaw(90)->[0,-1,0] = 'down' This, in fact, corresponds to our goal.
2) reverse of the wallnormal from 1)
input.yaw = 90 (facing "right" with respect to the reference frame) input.pitch = 0 (facing "directly ahead" with respect to the reference frame) wallnormal = [-1,0,0] (pointing "left" with respect to the absolute reference frame) the player walks forwards into the wall (or, alternatively, the player is facing "forward", strafes right into the wall, THEN turns right while on the wall)
our goal: the player's orientation should be facing straight up, with: x-axis[0,0,-1] = 'behind' y-axis[-1,0,0] = 'left' = wallnormal, no player pitch z-axis[0,1,0] = 'up'
from the wallnormal, you get: wallpitch = 0 with respect to the absolute reference frame wallroll = -90 with respect to the absolute reference frame
you could build the reference frame now angles = Angles(wallpitch,0,wallroll) coords = angles:GetCoords()
this should lead to a reference frame: x-axis[1,0,0]->yaw(0),roll(-90)->[0,1,0] = 'up' y-axis[0,1,0]->pitch(0),roll(-90)->[-1,0,0] = 'left' = wallnormal z-axis[0,0,1]->pitch(0),yaw(0)->[0,0,1] = 'forward' Notice that in the case of the z-axis, it retains its orientation, because the angles it is changed by (pitch and yaw) are zero.
now you rotate the player with input.yaw and input.pitch pitch around x-axis, yaw around y-axis, roll around z-axis x-axis[0,1,0]->yaw(90),roll(0)->[0,0,-1] = 'behind' y-axis[-1,0,0]->pitch(0),roll(0)->[1,0,0] = 'right' = wallnormal, no player pitch z-axis[0,0,1]->pitch(0),yaw(90)->[0,1,0] = 'up' This, in fact, corresponds to our goal.
3) swapping input.pitch for input.yaw from 1)
input.yaw = 0 (facing "forward" with respect to the reference frame) input.pitch = 90 (facing "directly up" with respect to the reference frame) wallnormal = [1,0,0] (pointing "right" with respect to the absolute reference frame) the player strafes left into the wall while facing upward (or, alternatively, the player is facing "forward", strafes left into the wall, THEN tilts upward while on the wall)
our goal: the player's orientation should be facing directly right, with: x-axis[0,-1,0] = 'down' y-axis[0,0,-1] = 'behind' z-axis[1,0,0] = 'right'
from the wallnormal, you get: wallpitch = 0 with respect to the absolute reference frame wallroll = +90 with respect to the absolute reference frame
you could build the reference frame now angles = Angles(wallpitch,0,wallroll) coords = angles:GetCoords()
this should lead to a reference frame: x-axis[1,0,0]->yaw(0),roll(90)->[0,-1,0] = 'down' y-axis[0,1,0]->pitch(0),roll(90)->[1,0,0] = 'right' = wallnormal z-axis[0,0,1]->pitch(0),yaw(0)->[0,0,1] = 'forward' Notice that in the case of the z-axis, it retains its orientation, because the angles it is changed by (pitch and yaw) are zero.
now you rotate the player with input.yaw and input.pitch pitch around x-axis, yaw around y-axis, roll around z-axis x-axis[0,-1,0]->yaw(0),roll(0)->[0,-1,0] = 'down' y-axis[1,0,0]->pitch(90),roll(0)->[0,0,01] = 'behind' z-axis[0,0,1]->pitch(90),yaw(0)->[1,0,0] = 'right' This, in fact, corresponds to our goal.
4) perpendicular of the wallnormal from 1)
input.yaw = 90 (facing "right" with respect to the reference frame) input.pitch = 0 (facing "directly ahead" with respect to the reference frame) wallnormal = [0,0,-1] (pointing "behind" with respect to the absolute reference frame) the player strafes left into the wall (or, alternatively, the player is facing "forward", walks forward into the wall, THEN turns right while on the wall)
our goal: the player's orientation should be facing straight down, with: x-axis[0,-1,0] = 'down' y-axis[0,0,-1] = 'behind' = wallnormal, no player roll z-axis[1,0,0] = 'right'
from the wallnormal, you get: wallpitch = 90 with respect to the absolute reference frame wallroll = 0 with respect to the absolute reference frame
you could build the reference frame now angles = Angles(wallpitch,0,wallroll) coords = angles:GetCoords()
this should lead to a reference frame: x-axis[1,0,0]->yaw(0),roll(0)->[1,0,0] = 'right' y-axis[0,1,0]->pitch(90),roll(0)->[0,0,-1] = 'behind' = wallnormal z-axis[0,0,1]->pitch(90),yaw(0)->[0,1,0] = 'up' Notice that in the case of the x-axis, it retains its orientation, because the angles it is changed by (yaw and roll) are zero.
now you rotate the player with input.yaw and input.pitch pitch around x-axis, yaw around y-axis, roll around z-axis x-axis[1,0,0]->yaw(90),roll(0)->[0,-1,0] = 'down' y-axis[0,0,-1]->pitch(0),roll(0)->[0,0,-1] = 'behind' = wallnormal, no player roll z-axis[0,1,0]->pitch(0),yaw(90)->[1,0,0] = 'right' This, in fact, corresponds to our goal.
local wallNormal = self.wallWalkingNormalCurrent // get wall walking normal in absolute frame of reference
local wallNormalRoll = wallNormal // copy wallNormal, do you need to use a specific function for this? wallNormalRoll.z = 0 // flatten normal along x-y plane wallNormalRoll:Normalize() // get unit vector
local wallNormalPitch = wallNormal // copy wallNormal, do you need to use a specific function for this? wallNormalPitch.x = 0 // flatten normal along z-y plane wallNormalPitch:Normalize() // get unit vector
local wallRoll = 0 // initialise local wallPitch = 0 // initialise // all angles in radians
if wallNormalRoll.x < 0 // if x is negative (left) with respect to absolute frame of reference wallRoll = -math.acos(wallNormalRoll.y) // roll is negative (roll left) with respect to absolute frame of reference else // if x is positive (right) with respect to absolute frame of reference wallRoll = math.acos(wallNormalRoll.y) // roll is positive (roll right) with respect to absolute frame of reference end
if wallNormalPitch.z < 0 // if z is negative (behind) with respect to absolute frame of reference wallPitch = math.acos(wallNormalPitch.y) // pitch is positive (tilt backward) with respect to absolute frame of reference else // if z is positive (forward) with respect to absolute frame of reference wallPitch = -math.acos(wallNormalPitch.y) // pitch is negative (tilt forward) with respect to absolute frame of reference end
local angles = Angles(wallPitch, 0, wallRoll) // set reference angles, essentially shifting from the absolute frame of reference local coords = angles:GetCoords() // build reference axes from reference angles // this is the reference frame, i.e. x-z plane forms the movement plane, y-axis is the wallnormal.
// rotate reference frame around x-axis with player pitch, and y-axis with player yaw coords = coords*Coords.GetRotation(coords.xAxis, input.pitch)*Coords.GetRotation(coords.yAxis, input.yaw) // this is the camera axes
angles:BuildFromCoords(coords) // build camera angles from camera axes self:SetViewAngles(angles) // set camera to camera angles
local wallNormalRoll = wallNormal wallNormalRoll.z = 0 wallNormalRoll:Normalize()
local wallNormalPitch = wallNormal wallNormalPitch.x = 0 wallNormalPitch:Normalize()
local wallRoll = 0 local wallPitch = 0
if wallNormalRoll.x < 0 wallRoll = -math.acos(wallNormalRoll.y) else wallRoll = math.acos(wallNormalRoll.y) end
if wallNormalPitch.z < 0 wallPitch = math.acos(wallNormalPitch.y) else wallPitch = -math.acos(wallNormalPitch.y) end
local angles = Angles(wallPitch, 0, wallRoll) local coords = angles:GetCoords() // this is the reference frame, i.e. x-z plane forms the movement plane, y-axis is the wallnormal.
coords = coords*Coords.GetRotation(coords.xAxis, input.pitch)*Coords.GetRotation(coords.yAxis, input.yaw) // this is the camera axes
For comparison, here was Yuuki's first prototype: <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1--> local wallRoll = -math.acos(self.wallWalkingNormalCurrent.y)
local angles = Angles(0,0,0) local coords = angles:GetCoords()
Appending with some relevant links: <a href="http://damien.kodingen.com/ns2docs/index.html" target="_blank">NS2Docs</a> <a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Coords" target="_blank">Objects:Coords</a> <a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Angles" target="_blank">Objects:Angles</a> <a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Vector" target="_blank">Objects:Vector</a> <a href="http://www.unknownworlds.com/ns2/forums/index.php?showtopic=115926&view=findpost&p=1897270" target="_blank">AvP source code from Soul_Rider in original discussion thread</a> <a href="http://www.unknownworlds.com/ns2/forums/index.php?showtopic=115926&view=findpost&p=1897366" target="_blank">Yuuki's first prototype code in original discussion thread</a>
I'll give a more detailed run down of the actual issues later, suffice to say, when you spawn facing the hive every thing is correct. If you turn your view horizontally 90degrees, up down becomes roll.
The code flips you randomly from one side of the face to the other, and touching anything throws you off in all sorts of crazy directions. Mouse key inputs are still the wrong way around on the wall.
Comments
<a href="http://www.mediafire.com/?ho6bl7hfjd6obaa" target="_blank">http://www.mediafire.com/?ho6bl7hfjd6obaa</a>
Everything is done in Skulk:UpdateViewAngles(input) :
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->
local angles = Angles(0,0,0)
local coords = angles:GetCoords()
coords.zAxis.x = math.cos(-input.pitch)*math.sin(input.yaw) // could be cos(input.pitch)
coords.zAxis.y = math.sin(-input.pitch)*math.sin(input.yaw) // could be sin(input.pitch)
coords.zAxis.z = math.cos(input.yaw)
local wallWalkingNormal = self.wallWalkingNormalCurrent
coords.xAxis = wallWalkingNormal:CrossProduct(coords.zAxis) // could be (coords.zAxis,wallWalkingNormal)
coords.yAxis = coords.zAxis:CrossProduct(coords.xAxis) // could be (coords.xAxis,coords.zAxis)
angles:BuildFromCoords(coords)
self:SetViewAngles(angles)<!--c2--></div><!--ec2-->
I doesn't really works but it's not that bad :)
Could you elaborate on what works and what doesn't?
Tried it out. Something is wrong:
putting Print(ToString(coords.zAxis)) in your code right before the cross product, and Print(ToString(cameraCoords)) in skulk_client.lua. The camera coords for zAxis was [0.05,0,28,0,96]. In our code, the coords were [-0.64,0.28,0.71]. It is obvious that something is wrong with how we attain the coords.
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local angles = Angles(input.pitch,input.yaw,0) // this function
local coords = angles:GetCoords()
local wallWalkingNormal = self.wallWalkingNormalCurrent
coords.xAxis = wallWalkingNormal:CrossProduct(coords.zAxis) // could be (coords.zAxis,wallWalkingNormal)
coords.yAxis = coords.zAxis:CrossProduct(coords.xAxis) // could be (coords.xAxis,coords.zAxis)
angles:BuildFromCoords(coords)
self:SetViewAngles(angles)<!--c2--></div><!--ec2-->
Let's consider some scenarios:
1)
wallWalkingNormal = [0,1,0]
input.pitch,input.yaw = 0,0 (degrees)
=> coords.zAxis = [0,0,1]
coords.xAxis = wallWalkingNormal x coords.zAxis = [0,1,0]x[0,0,1] = [1,0,0] correct
coords.yAxis = coords.zAxis x coords.xAxis = [0,0,1]x[1,0,0] = [0,1,0] correct
2)
wallWalkingNormal = [0,1,0]
input.pitch,input.yaw = 90,0 (degrees)
=> coords.zAxis = [0,1,0]
coords.xAxis = wallWalkingNormal x coords.zAxis = [0,1,0]x[0,1,0] = [0,0,0] well... naturally that didn't work
coords.yAxis = coords.zAxis x coords.xAxis = ---
Now, how do you deal with the situation where wallWalkingNormal is parallel with coords.zAxis?
Also the <u>length</u> of coords.xAxis and coords.yAxis change, which could be problematic - they should be unit vectors, no?
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local angles = Angles(input.pitch,input.yaw,0) // this function
local coords = angles:GetCoords()
local wallWalkingNormal = self.wallWalkingNormalCurrent
coords.xAxis = getNormalizedVector(wallWalkingNormal:CrossProduct(coords.zAxis)) // could be (coords.zAxis,wallWalkingNormal)
coords.yAxis = getNormalizedVector(coords.zAxis:CrossProduct(coords.xAxis)) // could be (coords.xAxis,coords.zAxis)
angles:BuildFromCoords(coords)
self:SetViewAngles(angles)<!--c2--></div><!--ec2-->
I'm not sure that would actually fix anything. And we still need to fix it for the parallel case.
I've been thinking about when they are parallel. The game handles it by... Trying to find the code for the implementation... Adding the codes I find that might help...
From function Skulk:PreUpdateMove(input, runningPrediction)
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1--> coords.xAxis = coords.yAxis:CrossProduct( coords.zAxis )
if (coords.xAxis:Normalize() < 0.01) then
// We have to choose the x-axis arbitrarily since we're
// looking along the normal direction.
coords.xAxis = coords.yAxis:GetPerpendicular()
end<!--c2--></div><!--ec2-->
self:GetViewCoords() works to get the right vectors though. Trying to modify the code and see how it works. I realised that after like 15 min of trying to get it to work, I hadn't loaded the mod xD
Good question. I have no idea myself.
This is what I work with so far
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function Skulk:UpdateViewAngles(input)
local viewCoords = self:GetViewCoords()
local coords = viewCoords
local wallWalkingNormal = self.wallWalkingNormalCurrent
coords.xAxis = wallWalkingNormal:CrossProduct(coords.zAxis) // could be (coords.zAxis,wallWalkingNormal)
coords.yAxis = coords.zAxis:CrossProduct(coords.xAxis) // could be (coords.xAxis,coords.zAxis)
Print(ToString(coords))
Angles:BuildFromCoords(coords)
Print(ToString(Angles))
self:SetViewAngles(Angles)
//yuuki:for debug
if math.random() < 0.1 then
local startPoint = Vector(self:GetOrigin())
local extents = self:GetExtents()
startPoint.y = startPoint.y + extents.y
local endPoint = startPoint + 2*coords.zAxis
DebugLine(startPoint, endPoint, 1.1, 1, 0, 0, 1)
end
// Update view offset from crouching
local viewY = self:GetMaxViewOffsetHeight()
viewY = viewY - viewY * self:GetCrouchShrinkAmount() * self:GetCrouchAmount()
// Don't set new view offset height unless needed (avoids Vector churn).
local lastViewOffsetHeight = self:GetSmoothedViewOffset().y
if math.abs(viewY - lastViewOffsetHeight) > kEpsilon then
self:SetViewOffsetHeight(viewY)
end
end<!--c2--></div><!--ec2-->
It's not working, cause I do the angles wrong; script error. I don't really get them.
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local angles = Angles(input.pitch,input.yaw,0) // this function
local wallWalkingNormal = self.wallWalkingNormalCurrent
local coords = angles:GetCoords()
Print(ToString(coords))
coords.xAxis = wallWalkingNormal:CrossProduct(coords.zAxis)
coords.xAxis:Normalize()
coords.yAxis = coords.zAxis:CrossProduct(coords.xAxis)
coords.yAxis:Normalize()
Print(ToString(coords))
local viewCoords = self:GetViewCoords()
Print(ToString(viewCoords))
viewCoords.xAxis = wallWalkingNormal:CrossProduct(viewCoords.zAxis)
viewCoords.xAxis:Normalize()
viewCoords.yAxis = viewCoords.zAxis:CrossProduct(viewCoords.xAxis)
viewCoords.yAxis:Normalize()
Print(ToString(viewCoords))<!--c2--></div><!--ec2-->
<b>Standing on flat ground</b>
First print:
Coord: origin: (0.00,0.00,0.00) xAxis: (-0.42,0.00,0.91) yAxis: (-0.84,0.39,-0.38) zAxis: (-0.35,0.92,-0.16)
Second print:
Coord: origin: (0.00,0.00,0.00) xAxis: (-0.42,0.00,0.91) yAxis: (-0.84,0.39,-0.38) zAxis: (-0.35,0.92,-0.16)
Third print:
Coord: origin: (-37.43,4.85,65.21) xAxis: (-0.17,0.00,-0.99) yAxis: (-0.24,10.97,0.04) zAxis: (0.96,0.24,-0.16)
Forth print:
Coord: origin: (-37.43,4.85,65.21) xAxis: (-0.17,0.00,-0.99) yAxis: (-0.24,10.97,0.04) zAxis: (0.96,0.24,-0.16)
<b>
Standing on a right wall</b>:
First print:
Coord: origin: (0.00,0.00,0.00) xAxis: (-0.63,0.00,0.78) yAxis: (0.03,1.00,0.03) zAxis: (-0.78,0.04,-0.63)
Second print:
Coord: origin: (0.00,0.00,0.00) xAxis: (0.06,1.00,-0.00) yAxis: (0.63,-0.04,-0.78) zAxis: (-0.78,0.04,-0.63)
Third print:
Coord: origin: (-35.71,4.69,34.69) xAxis: (-0.17,0.00,-0.99) yAxis: (-0.24,0.97,0.04) zAxis: (0.96,0.24,-0.16)
Forth print:
Coord: origin: (-35.71,4.69,34.69) xAxis: (0.25,-0.97,-0.00) yAxis: (-0.16,-0.04,-0.99) zAxis: (0.96,0.24,-0.16)
(,,)
<b>Standing in the ceiling</b>:
First print:
Coord: origin: (0.00,0.00,0.00) xAxis: (-0.32,0.00,-0.95) yAxis: (0.06,1.00,-0.02) zAxis: (0.95,-0.06,-0.32)
Second print:
Coord: origin: (0.00,0.00,0.00) xAxis: (0.32,-0.00,0.95) yAxis: (-0.06,-1.00,0.02) zAxis: (0.95,-0.06,-0.32)
Third print:
Coord: origin: (-38.74,8.19,42.05) xAxis: (-0.51,0.00,-0.86) yAxis: (0.05,-1.00,-0.03) zAxis: (-0.86,-0.06,-0.51)
Forth print:
Coord: origin: (-38.74,8.19,42.05) xAxis: (-0.51,0.00,-0.86) yAxis: (0.05,-1.00,-0.03) zAxis: (-0.86,-0.06,-0.51)
If you for some reason find yourself typing out some coords, I found this helpful to copy:
Coord: origin: (,,) xAxis: (,,) yAxis: (,,) zAxis: (,,)
My goal was:
- using a reference axes
- taking a vector
- finding the vector relative to the x-axis
I somehow realised that the absolute reference axes correspond to an identity matrix
[1 0 0]
[0 1 0]
[0 0 1]
That is, multiplying a 1x3 vector [x,y,z] by that matrix would yield the exact same vector [x,y,z] - this was ideal.
So I looked up matrix multiplication to get a refresher, did some tests, and confirmed my hypothesis that multiplying a vector by a matrix that represents a different reference axes WILL re-express that vector as relative to the reference axes*, like so:
<img src="http://i.imgur.com/aXpdC.png" border="0" class="linked-image" />
*<!--coloro:gray--><span style="color:gray"><!--/coloro--><!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->I wonder if they taught this in math class but I just never paid attention...<!--sizec--></span><!--/sizec--><!--colorc--></span><!--/colorc-->
I already knew that the reference axes would be built from the input.pitch and input.yaw angles, which was a simple task.
However, I later realised that, given what I was trying to achieve (keeping the reference y-axis parallel with the absolute y-axis so that I could take the angle between the relative wallNormal and absolute "up"), I had to omit input.pitch when building the reference axis (yaw only moves the x and z axes).
After a bit of simplification (omitting needless steps), I ended up with the following code:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceAngles = Angles(0,input.yaw,0)
local referenceCoords = referenceAngles:getCoords()
local wallNormal = self.wallWalkingNormalCurrent
... // step(s) that transform wallNormal into appropriate 1x3 matrix, and referenceCoords into appropriate 3x3 matrix (see image)
wallNormal = wallNormalMatrix * referenceCoordsMatrix
local wallNormalRoll = wallNormal
wallNormalRoll.z = 0
wallNormalRoll = GetNormalizedVector(wallNormalRoll)
local wallNormalPitch = wallNormal
wallNormalPitch.x = 0
wallNormalPitch = GetNormalizedVector(wallNormalPitch)
if wallNormalRoll.x < 0
wallRoll = -math.acos(wallNormalRoll.y)
else
wallRoll = math.acos(wallNormalRoll.y)
end
if wallNormalPitch.z < 0
wallPitch = math.acos(wallNormalPitch.y)
else
wallPitch = -math.acos(wallNormalPitch.y)
end
local viewAngles = Angles(wallPitch + input.pitch, input.yaw, wallRoll)
self:SetViewAngles(viewAngles)<!--c2--></div><!--ec2-->
<img src="http://i.imgur.com/wMTgR.png" border="0" class="linked-image" />
I'm hoping this works. If it doesn't, I think you have to restore the GetCoords(), GetRotation() and BuildFromCoords() functions. And if even that doesn't work, then... well, there's still hope, but... later^.
See the matrix multiplication as well as the reasoning, code and simplification in this excel spreadsheet (two sheets): <a href="http://www.mediafire.com/?yl2fc99kt7v39ic" target="_blank">mediafire</a>
<!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->Note that the reference axes are hard-coded, and NOT determined by the roll/pitch/yaw angles. I didn't feel the need to implement that.<!--sizec--></span><!--/sizec-->
^I have a feeling I might be able to further simplify some of the intermediate steps (bypass the determination of the individual wallRoll and wallPitch angles) by keeping the reference axes idea but taking a different approach in getting the viewCoords, by making use of:
- "coords"="angles":GetCoords() // converting a set of angles to a set of reference axes
and/or
- "angles":BuildFromCoords() // converting a set of reference axes to a set of angles; particularly this
and/or
- cross products, etc.
Later, though.
Anyway, here's another alternative using the GetRotation() function:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceAngles = Angles(0,input.yaw,0)
local referenceCoords = referenceAngles:getCoords()
local wallNormal = self.wallWalkingNormalCurrent
... // step(s) that transform wallNormal into appropriate 1x3 matrix, and referenceCoords into appropriate 3x3 matrix (see image)
wallNormal = wallNormalMatrix * referenceCoordsMatrix
local wallNormalRoll = wallNormal
wallNormalRoll.z = 0
wallNormalRoll = GetNormalizedVector(wallNormalRoll)
local wallNormalPitch = wallNormal
wallNormalPitch.x = 0
wallNormalPitch = GetNormalizedVector(wallNormalPitch)
if wallNormalRoll.x < 0
wallRoll = -math.acos(wallNormalRoll.y)
else
wallRoll = math.acos(wallNormalRoll.y)
end
if wallNormalPitch.z < 0
wallPitch = math.acos(wallNormalPitch.y)
else
wallPitch = -math.acos(wallNormalPitch.y)
end
local viewAngles = Angles(0, 0, 0)
local viewCoords = viewAngles:GetCoords()
viewCoords = viewCoords*Coords.GetRotation(coords.zAxis, wallRoll)*Coords.GetRotation(coords.xAxis, wallPitch)*Coords.GetRotation(coords.yAxis, input.yaw)*Coords.GetRotation(coords.xAxis, input.pitch)
viewAngles:BuildFromCoords(viewCoords)
self:SetViewAngles(viewAngles)<!--c2--></div><!--ec2-->
Ahh, I realised there's an error. You can't really do
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceAngles = Angles(0,input.yaw,0)
local referenceCoords = referenceAngles:getCoords()<!--c2--></div><!--ec2-->
since it doesn't really mean what we want it to mean.
replace those two lines with
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceCoords = self.lastCoords // last known axes, found in function Skulk:PreUpdateMove
local referenceAngles = Angles(0,0,0) // initialise
referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes
referenceAngles.pitch = 0 // only interested in yaw, i.e. reversing effect of pitch
referenceAngles.roll = 0 // only interested in yaw, i.e. reversing effect of roll
referenceCoords = referenceAngles:getCoords() // new reference axes<!--c2--></div><!--ec2-->
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function Skulk:UpdateViewAngles(input)
local referenceCoords = self.lastCoords // last known axes, found in function Skulk:PreUpdateMove
local referenceAngles = Angles(0,0,0) // initialise
referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes
referenceAngles.pitch = 0 // only interested in yaw, i.e. reversing effect of pitch
referenceAngles.roll = 0 // only interested in yaw, i.e. reversing effect of roll
referenceCoords = referenceAngles:getCoords() // new reference axes
local wallNormal = self.wallWalkingNormalCurrent
... // step(s) that transform wallNormal into appropriate 1x3 matrix, and referenceCoords into appropriate 3x3 matrix (see image)
wallNormal = wallNormalMatrix * referenceCoordsMatrix
local wallNormalRoll = wallNormal
wallNormalRoll.z = 0
wallNormalRoll = GetNormalizedVector(wallNormalRoll)
local wallNormalPitch = wallNormal
wallNormalPitch.x = 0
wallNormalPitch = GetNormalizedVector(wallNormalPitch)
if wallNormalRoll.x < 0
wallRoll = -math.acos(wallNormalRoll.y)
else
wallRoll = math.acos(wallNormalRoll.y)
end
if wallNormalPitch.z < 0
wallPitch = math.acos(wallNormalPitch.y)
else
wallPitch = -math.acos(wallNormalPitch.y)
end
local viewAngles = Angles(0, 0, 0)
local viewCoords = viewAngles:GetCoords()
viewCoords = viewCoords*Coords.GetRotation(coords.xAxis, wallPitch+input.pitch)*Coords.GetRotation(coords.yAxis, input.yaw)*Coords.GetRotation(coords.zAxis, wallRoll)
viewAngles:BuildFromCoords(viewCoords)
self:SetViewAngles(viewAngles)
... // all the other stuff
end<!--c2--></div><!--ec2-->
This refers to the following line of code:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes<!--c2--></div><!--ec2-->
We need to supply angles self as well as coordinates. Currently the function is just taking the coords as angles.
<ul><li>zAxis unchanged</li><li>xAxis to be completely in the wallwalking plane</li><li>yAxis to be right-handed perpendecular to zAxis and xAxis</li><li>Forward/backward movement to take place in the zAxis direction when flattened on the wallwalking plane</li><li>Strafing to take place along the xAxis</li><li>Pitch to rotate around the xAxis</li><li>Yaw to rotate around the wallwalking normal</li></ul>
Keeping the zAxis unchanged is optional, but everything else is required to perserve the same behaviour as the ground. Why I include the zAxis: imagine as a marine, running up a slope would change your pitch the same angle as the slope. Thats how it would be otherrwise.
You gotta love some of the notes from the programmers :)
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function PowerManager:GetIsLocationPowered(entity, sendEvent)
//$AS TODO: Use powerGrid instead of this horrible search method that makes me sad<!--c2--></div><!--ec2-->
LoL
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// Skulks do not respect ladders due to their wall walking superiority.<!--c2--></div><!--ec2-->
and, for some reason,
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1--> // Skulks cannot crouch!<!--c2--></div><!--ec2-->
This refers to the following line of code:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes<!--c2--></div><!--ec2-->
We need to supply angles self as well as coordinates. Currently the function is just taking the coords as angles.<!--QuoteEnd--></div><!--QuoteEEnd-->
Ummm... what do we need exactly? Are you saying there is a missing parameter? What is Angles Self? Or Coords Coords for that matter.
If you look at the other code:
<!--quoteo--><div class='quotetop'>QUOTE </div><div class='quotemain'><!--quotec-->angles:BuildFromCoords(coords)<!--QuoteEnd--></div><!--QuoteEEnd-->
This generally works fine, doesn't it?
Your notes are good, but not deep enough for me. I am looking at the following pieces of code but get confused by your statements.
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local referenceCoords = self.lastCoords // last known axes, found in function Skulk:PreUpdateMove
local referenceAngles = Angles(0,0,0) // initialise
referenceAngles:BuildFromCoords(referenceCoords) // creating pitch, yaw, roll angles from axes
referenceAngles.pitch = 0 // only interested in yaw, i.e. reversing effect of pitch
referenceAngles.roll = 0 // only interested in yaw, i.e. reversing effect of roll
referenceCoords = referenceAngles:getCoords() // new reference axes<!--c2--></div><!--ec2-->
Here you seem to be taking the current coordinates, turning them into angles, removing 2 axis (X&Y?) then turning them back into coordinates. The code I am looking at that is similar to this is :
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function Skulk:UpdatePosition(velocity, time)
PROFILE("Skulk:UpdatePosition")
local yAxis = self:GetCoords().yAxis
local requestedVelocity = Vector(velocity)<!--c2--></div><!--ec2-->
If you just want the z-axis coordinates you could use:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1--> local zAxis = self:GetCoords().zAxis<!--c2--></div><!--ec2-->
To get angles we could look at:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function Skulk:PreUpdateMove(input, runningPrediction)
PROFILE("Skulk:PreUpdateMove")
local angles = Angles(self:GetAngles())<!--c2--></div><!--ec2-->
Once I can get a better understanding of exactly what you are trying to do, the easier it will be for me to work out the code :)
<img src="http://i.imgur.com/rX5m0.png" border="0" class="linked-image" />
From that you can see that the goals are:
1. Get player's (model's?) angles.
2. Set pitch and roll to zero, to line up the reference x-z plane with the absolute x-z plane.
3. Build coords (reference axes) from that.
4a. Transform those reference axes into a matrix. (columns: x-axis vector, y-axis vector, z-axis vector. rows: x-components; y-components; z-components)
4b. Transform wallnormal ("columns": x-component, y-component, z-component)
4c. Multiply the absolute wallnormal vector by the matrix, to get the relative wallnormal vector.
5. Flatten the wallnormal along the x-y plane and z-y plane for roll and pitch respectively (and normalise).
6. Find the pitch and roll angles of the wallnormal vector by taking the inverse cosine of the y-component of the wallnormal vector (which is the dot product of the y-axis and the wallnormal).
7. Shift the player's orientation (camera) to these angles [this is the player's movement plane].
8. Shift the player's orientation (camera) by the input angles (input.yaw and input.pitch) [mouse-horizontal and mouse-vertical].
I may have overlooked some things, but hopefully those can be sorted out.
A necessary consequence of this approach is that the z-axis DOES change when you change surface. However, perhaps the approach could be adapted (in step 8) so that it doesn't: I think it would involve calculating the angular difference (pitch and yaw) between the calculated z-axis and the preserved z-axis, then reversing the changes (compensation). One benefit of such an approach is that you could possibly add another key that would allow you to switch between the two modes at will (i.e. switching the compensation on and off).
Also, I was told earlier that self:GetCoords() wouldn't work, because there aren't yet self.coords when you call the function, which is why the game stores it as self.lastCoords in the function Skulk:PreUpdateMove?
(On the other hand... self.lastCoords = self:GetCoords() in every instance)
<!--quoteo(post=1897869:date=Jan 28 2012, 07:00 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 28 2012, 07:00 PM) <a href="index.php?act=findpost&pid=1897869"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Summary of the properties we want the mod to have:
<ul><li>zAxis unchanged</li><li>xAxis to be completely in the wallwalking plane</li><li>yAxis to be right-handed perpendecular to zAxis and xAxis</li><li>Forward/backward movement to take place in the zAxis direction when flattened on the wallwalking plane</li><li>Strafing to take place along the xAxis</li><li>Pitch to rotate around the xAxis</li><li>Yaw to rotate around the wallwalking normal</li></ul><!--QuoteEnd--></div><!--QuoteEEnd-->
That's a comprehensive list, and I don't think there's anything to add. However, I would like to split the player movement bullet-points from the player view bullet-points, and perhaps stress the particular relevance of input.yaw and input.pitch (which are your last two bullet points, I believe).
Maybe we should take a step back and start again from first principles...
My problem right now is that I don't understand how the camera angle links to the movement, I'm just going with it. So I can't write effective, working code, I can only discuss concepts.
Looking at the mod - it looks like Yuuki has actually added the UpdateViewAngles function to Skulk.lua (at least I couldn't find it in the original 193 code): is that because then it overrides the UpdateViewAngles function in another lua file?
And I'm not entirely sure whether we're specifying the "state" of the camera at any point in time or if we're modifying the state by a "change" (old state + difference = new state). The key difference is that the state exists at a single point in time and so is independent of the duration between frames, while the "change" depends on (is scaled by) the duration between frames and therefore the state depends on the previous frame.
Like: the wallNormal doesn't (usually) change with time, but it seems to me that input.yaw and input.pitch would be dependent on the input time and therefore the previous state. But then it's also possible that input.yaw is simply stored as a number and then modified, kind of like the position of a cursor on the screen (then is it possible to rotate right a given number of times and hit a limit in the code because it can't store a bigger number than that?), and the same for input.pitch.
And I'm not entirely sure whether or not some of the stuff in (for example) Skulk:PreUpdateMove might contradict the stuff we've added in Skulk:UpdateViewAngles. Because Skulk:PreUpdateMove does appear to do something with the camera (the view angles).
<!--quoteo(post=1897869:date=Jan 28 2012, 07:00 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 28 2012, 07:00 PM) <a href="index.php?act=findpost&pid=1897869"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Keeping the zAxis unchanged is optional, but everything else is required to perserve the same behaviour as the ground. Why I include the zAxis: imagine as a marine, running up a slope would change your pitch the same angle as the slope. Thats how it would be otherrwise.<!--QuoteEnd--></div><!--QuoteEEnd-->
Yeah I'm not 100% convinced, I think if we can get it as an option (preferably an option that you can, at will, toggle or use as a modifier, while in-game) it would be ideal.
Keeping the zAxis unchanged would be good for tracking targets and keeping from getting disoriented, but somewhat cumbersome for quicker movement in an environment you're already familiar with.
On the other hand, letting the zAxis move as well would let the player move around more "cleanly" in an environment they're already familiar with and remove any strange dependencies.
One way to break it down, is, let's consider four situations inside a horizontal cylinder.
Your orientation (zAxis) starts parallel, and you're
1. facing the side of the cylinder and strafing along the circular...
2. facing the inside of the cylinder, and walking forward along the circular...
a. ...with zAxis modified.
b. ...with zAxis preserved.
1a: Player view would roll, and nothing else. *
1b: Player view would roll, and nothing else. * identical
2a: Player view would pitch, and nothing else. Kind of like a rollercoaster<u>^</u>.
2b: Player view would ...?**
**At the bottom of the cylinder, the player would be facing parallel with the cylinder wall, essentially he's looking "directly ahead" in a non-wallwalking context.
**At the front of the cylinder, the player would be facing into the cylinder, essentially he would be looking "down" in a non-wallwalking context.
**->Now he's "stuck" because moving forward from this point would mean he'd either be
... standing still or turning around and around on the same spot to preserve the zAxis (yaw or roll), or
... we remove the upper and lower limits of the pitch so that he can pitch 360 degrees to preserve the zAxis (like a flight simulator<u>^</u>), or
... his orientation would shift so that he's always looking down into the cylinder (no longer preserving the z-axis).
<u>^</u><!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->gotta look at creating rollercoaster and flight simulator mods when we're done with this...<!--sizec--></span><!--/sizec-->
Lunch time.
Theoretically, the could be written in Skulk:AdjustPlayerCameraCoords, or we could create a whole new function to use it. At the moment the code is just replacing the updating camera and trying to fix the inputs. The code works with the camera, and changes the inputs, although not as we want, so essentially we have two pieces of code doing the same job :)
Whichever function we use is irrelevant, but we need to remove the other.
Taking the scalar of the right top image, you would get a negative number. Scalars doesn't produce vectors, but two vectors that are anti-parallel give negative scalars, while parallel vectors give positive.
For your top left figure:
[wallnormal].[x-axis]=[1,1,0].[1,0,0]=1
For the top left figure:
[wallnormal].[x-axis]=[-1,1,0].[1,0,0]=-1
<!--quoteo(post=1898004:date=Jan 29 2012, 06:29 AM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 29 2012, 06:29 AM) <a href="index.php?act=findpost&pid=1898004"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec--><u>^</u><!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->gotta look at creating rollercoaster and flight simulator mods when we're done with this...<!--sizec--></span><!--/sizec--><!--QuoteEnd--></div><!--QuoteEEnd-->
Lerk flight-simulator. Strafing causes you to drift, yawing causes you to roll. Full view rotation and free movement, with loops and barrel-rolls. If you try to change direction while not gliding, then you would just drift and stall.
I agree with all of your post. Keeping the z-axis (absolutely) unchanged in simple geometry is not always good, but more complex geometry, such as walking over props and other kind of bumps is made much easier. The downside with a changing z-axis is that if you encounter a series of (small) angle changes, they can together build up making you end up walking in a different direction then you expected. With the z-axis being unchanged you may end up slightly of the target, but you will be going in the right direction at least.
We probably should take a step back and start from scratch with a more calculated approach. Instead of trying to change a function to fit our needs, I think it would be good if we finalized exactly what we need to do, then we just have to find where to insert the code. How do we make sure that another function doesn't change the camera and inputs again to something else though? I don't know.
Returning to the lerk flight-simulator, I'm fairly sure it is much easier to do, we just need to rotate the camera with your yaw instead in moving it sideways, pretty much just changing your yaw inputs to roll I guess? Then we make the strafing affect the yaw and we should have a working flight simulator. We don't need that to relate to wall normals and such.
If you comment self:SetViewAngles(angles) in Skulk:UpdateViewAngles(input) the camera doesn't response to mouse anymore, so I think we're good with that.
[wallnormal].[x-axis]=[1,1,0].[1,0,0]=1
For the top left figure:
[wallnormal].[x-axis]=[-1,1,0].[1,0,0]=-1<!--QuoteEnd--></div><!--QuoteEEnd-->
We're taking the dot product of the y-axis, not the x-axis.
45 degrees right of y-axis: [wallnormal].[y-axis]=[b,b,0].[0,1,0]=b
45 degrees left of y-axis: [wallnormal].[y-axis]=[-b,b,0].[0,1,0]=b
Different wallnormal, but same dot product: therefore, same calculated angle.
Also, you haven't normalised.
Here, b = sqrt(1/2)
Even if you do take the angle for the x-axis, consider these instead:
45 degrees above x-axis: [wallnormal].[x-axis]=[b,b,0].[1,0,0]=b
45 degrees below x-axis: [wallnormal].[x-axis]=[b,-b,0].[1,0,0]=b
Different wallnormal, but same dot product: therefore, same calculated angle.
Simply put, angles to the left and right side of the y-axis (when x is positive or negative) cannot be implicitly distinguished, and angles between the top and bottom side of the x-axis (when y is positive or negative) cannot be implicitly distinguished. That's why you cannot use a single function (at least you cannot use arccos), but you must explicitly distinguish between the different angles: and I do this by distinguishing between x negative (roll negative) and x positive (roll positive).
The reason that we get the x and z axes with respect to the player, is that distinguishing the -absolute- roll/pitch changes with the player's orientation. (We need to determine the -absolute- roll/pitch in order to determine the -absolute- movement plane). A wall to your left becomes a wall to your right if you simply turn around, so while in the former case you would roll right, in the latter case you would roll left.*
*Actually, I'm not so sure of this anymore. Is this step even necessary? It would certainly be necessary if you were dealing with changes (shift the player's current view), but not if you were dealing with states (build a new view based on given parameters).
Seriously, there's just too much we don't know.
The first thing I want to know is: changes or states?
The second thing I want to know is: are input.pitch and input.yaw changes (difference between frames) or states?
<!--quoteo(post=1898014:date=Jan 29 2012, 04:57 PM:name=Soul_Rider)--><div class='quotetop'>QUOTE (Soul_Rider @ Jan 29 2012, 04:57 PM) <a href="index.php?act=findpost&pid=1898014"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->All the code that goes in Skulk:UpdateViewAngles() is just a replacement for the Skulk:AdjustPlayerCameraCoords.<!--QuoteEnd--></div><!--QuoteEEnd-->
I don't see either of those functions in Skulk.lua...
AngleVelocity.euler.y x Transformational matrix. It's in the code for AVP wall-walking which I posted.
AngleVelocity.euler.y x Transformational matrix. It's in the code for AVP wall-walking which I posted.<!--QuoteEnd--></div><!--QuoteEEnd-->
Oh, I must have missed that then :)
So any idea on how to get the math to do that right?
<!--quoteo(post=1898041:date=Jan 29 2012, 02:33 PM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 29 2012, 02:33 PM) <a href="index.php?act=findpost&pid=1898041"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->We're taking the dot product of the y-axis, not the x-axis.<!--QuoteEnd--></div><!--QuoteEEnd-->
The picture sais when x is poistive, roll is positive, and when x is negative, roll is negative, so I assumed you meant the dot product with the x-axis. I must have misunderstood you, my bad.
<a href="http://damien.kodingen.com/ns2docs/index.html" target="_blank">NS2Docs</a>
<a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Coords" target="_blank">Objects:Coords</a>
<a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Angles" target="_blank">Objects:Angles</a>
<a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Vector" target="_blank">Objects:Vector</a>
<a href="http://www.unknownworlds.com/ns2/forums/index.php?showtopic=115926&view=findpost&p=1897270" target="_blank">AvP source code from Soul_Rider in original discussion thread</a>
<a href="http://www.unknownworlds.com/ns2/forums/index.php?showtopic=115926&view=findpost&p=1897366" target="_blank">Yuuki's first prototype code in original discussion thread</a>
<!--quoteo(post=1898099:date=Jan 30 2012, 06:40 AM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 30 2012, 06:40 AM) <a href="index.php?act=findpost&pid=1898099"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->The picture sais when x is poistive, roll is positive, and when x is negative, roll is negative, so I assumed you meant the dot product with the x-axis. I must have misunderstood you, my bad.<!--QuoteEnd--></div><!--QuoteEEnd-->
The size of the angle is dependent on the y-component (dot product of unit vector with y-axis); the range of the arccos function is only from 0 to 180 degrees, only half a circle. The orientation of the angle is dependent on the x-component (positive or negative, i.e. right side or left side), so you get two half circles to make a full circle.
So guys, input.yaw, input.pitch. Fill me in. :P How do they work?
Until I have a more concrete idea, from here on I'm going to assume that input.yaw and input.pitch are independent of the time, and the 'difference' is taken elsewhere.
That is, there is a function where for example (if I were to prototype it, it would be something like this):
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->yawdifference(change in angle between frames) = f( input.mouse.x(distance moved between frames) * sensitivity * input.time(time between frames) )
pitchdifference(change in angle between frames) = f( input.mouse.y(distance moved between frames) * sensitivity * input.time(time between frames) )
input.yaw(currentframe) = input.yaw(previousframe) + yawdifference(change in angle between frames)
input.pitch(currentframe) = input.pitch(previousframe) + pitchdifference(change in angle between frames)
// if input.angle > (180 degrees)
// then input.angle = input.angle - (360 degrees)
// if input.angle < (-180 degrees)
// then input.angle = input.angle + (360 degrees)
// so that input.angle is constrained between -180 and 180 degrees
// clamp pitch to +90, -90 degrees, so you can only look straight up and straight down, might take place in a different function<!--c2--></div><!--ec2-->
Essentially, input.yaw and input.pitch describe your <b>current orientation</b> (with respect to a frame of reference, usually the absolute frame of reference), not the difference between your current orientation in the current frame and your previous orientation in the previous frame.
<!--quoteo(post=1898084:date=Jan 30 2012, 04:08 AM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 30 2012, 04:08 AM) <a href="index.php?act=findpost&pid=1898084"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->I have an idea. Both movement and camera are now related to the absolute coordinates. We should be able to transform this coordinate system using euler angles into the coordinate system for the wallwalking plane. Using the saame angles to transform the camera we should look in the same relative way compared to the wallwalking plane as we did for the absolute plane. Having the correct rotation on the wallwalking plane will make the camera change intuitive. This would make the z-axis change. To keep it we need to compensate the angle change somehow. We can make that a later project once we got a working model.<!--QuoteEnd--></div><!--QuoteEEnd-->
This is essentially what I've been trying to do. The euler angles are pitch, yaw and roll. We don't need to change the yaw, but we want to change the pitch and roll in order to line up the wallnormal with the player's reference frame's y-axis. The rotation matrix is coords.GetRotation(axis, angle). I think that, mathematically, my most recent implementation is most sound. Code-wise I'm not sure of though.
The game already has some transformations that we know of:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->angles = Angles(pitch,yaw,roll) // create a set of angles, from specified angles, that describe the orientation
coords = angles:GetCoords() // create a set of axes, from a set of angles, that describe the orientation
angles:BuildFromCoords(coords) // create a set of angles, from a set of axes, that describe the orientation
coords*coords.GetRotation(axis, angle) // rotate a set of axes, around a specified axis, for a specified angle : this is a transformation matrix<!--c2--></div><!--ec2-->
So what we want to do:
Approach A)
1. Get a wallnormal
2. Get the correct pitch and roll - but how?
3. Create a set of axes (or rotate a reference axes) corresponding to this pitch and roll
4. Build a camera corresponding to this set of axes.
This is the reference frame.
5. Rotate the camera around the new y-axis by input.yaw, rotate the camera around the new x-axis by input.pitch
Approach B)
1. Get a wallnormal
2. Create a set of axes with wallnormal as the y-axis and the x-z plane parallel to the wall - but what orientation should the x and z axes be in?
3. Build a camera corresponding to this set of axes.
This is the reference frame.
4. Rotate the camera around the new y-axis by input.yaw, rotate the camera around the new x-axis by input.pitch
1)
input.yaw = 90 (facing "right" with respect to the reference frame)
input.pitch = 0 (facing "directly ahead" with respect to the reference frame)
wallnormal = [1,0,0] (pointing "right" with respect to the absolute reference frame)
the player walks backwards into the wall (or, alternatively, the player is facing "forward", strafes left into the wall, THEN turns right while on the wall)
our goal:
the player's orientation should be facing straight down, with:
x-axis[0,0,-1] = 'behind'
y-axis[1,0,0] = 'right' = wallnormal, no player pitch
z-axis[0,-1,0] = 'down'
from the wallnormal, you get:
wallpitch = 0 with respect to the absolute reference frame
wallroll = +90 with respect to the absolute reference frame
you could build the reference frame now
angles = Angles(wallpitch,0,wallroll)
coords = angles:GetCoords()
this should lead to a reference frame:
x-axis[1,0,0]->yaw(0),roll(90)->[0,-1,0] = 'down'
y-axis[0,1,0]->pitch(0),roll(90)->[1,0,0] = 'right' = wallnormal
z-axis[0,0,1]->pitch(0),yaw(0)->[0,0,1] = 'forward'
Notice that in the case of the z-axis, it retains its orientation, because the angles it is changed by (pitch and yaw) are zero.
now you rotate the player with input.yaw and input.pitch
pitch around x-axis, yaw around y-axis, roll around z-axis
x-axis[0,-1,0]->yaw(90),roll(0)->[0,0,-1] = 'behind'
y-axis[1,0,0]->pitch(0),roll(0)->[1,0,0] = 'right' = wallnormal, no player pitch
z-axis[0,0,1]->pitch(0),yaw(90)->[0,-1,0] = 'down'
This, in fact, corresponds to our goal.
2) reverse of the wallnormal from 1)
input.yaw = 90 (facing "right" with respect to the reference frame)
input.pitch = 0 (facing "directly ahead" with respect to the reference frame)
wallnormal = [-1,0,0] (pointing "left" with respect to the absolute reference frame)
the player walks forwards into the wall (or, alternatively, the player is facing "forward", strafes right into the wall, THEN turns right while on the wall)
our goal:
the player's orientation should be facing straight up, with:
x-axis[0,0,-1] = 'behind'
y-axis[-1,0,0] = 'left' = wallnormal, no player pitch
z-axis[0,1,0] = 'up'
from the wallnormal, you get:
wallpitch = 0 with respect to the absolute reference frame
wallroll = -90 with respect to the absolute reference frame
you could build the reference frame now
angles = Angles(wallpitch,0,wallroll)
coords = angles:GetCoords()
this should lead to a reference frame:
x-axis[1,0,0]->yaw(0),roll(-90)->[0,1,0] = 'up'
y-axis[0,1,0]->pitch(0),roll(-90)->[-1,0,0] = 'left' = wallnormal
z-axis[0,0,1]->pitch(0),yaw(0)->[0,0,1] = 'forward'
Notice that in the case of the z-axis, it retains its orientation, because the angles it is changed by (pitch and yaw) are zero.
now you rotate the player with input.yaw and input.pitch
pitch around x-axis, yaw around y-axis, roll around z-axis
x-axis[0,1,0]->yaw(90),roll(0)->[0,0,-1] = 'behind'
y-axis[-1,0,0]->pitch(0),roll(0)->[1,0,0] = 'right' = wallnormal, no player pitch
z-axis[0,0,1]->pitch(0),yaw(90)->[0,1,0] = 'up'
This, in fact, corresponds to our goal.
3) swapping input.pitch for input.yaw from 1)
input.yaw = 0 (facing "forward" with respect to the reference frame)
input.pitch = 90 (facing "directly up" with respect to the reference frame)
wallnormal = [1,0,0] (pointing "right" with respect to the absolute reference frame)
the player strafes left into the wall while facing upward (or, alternatively, the player is facing "forward", strafes left into the wall, THEN tilts upward while on the wall)
our goal:
the player's orientation should be facing directly right, with:
x-axis[0,-1,0] = 'down'
y-axis[0,0,-1] = 'behind'
z-axis[1,0,0] = 'right'
from the wallnormal, you get:
wallpitch = 0 with respect to the absolute reference frame
wallroll = +90 with respect to the absolute reference frame
you could build the reference frame now
angles = Angles(wallpitch,0,wallroll)
coords = angles:GetCoords()
this should lead to a reference frame:
x-axis[1,0,0]->yaw(0),roll(90)->[0,-1,0] = 'down'
y-axis[0,1,0]->pitch(0),roll(90)->[1,0,0] = 'right' = wallnormal
z-axis[0,0,1]->pitch(0),yaw(0)->[0,0,1] = 'forward'
Notice that in the case of the z-axis, it retains its orientation, because the angles it is changed by (pitch and yaw) are zero.
now you rotate the player with input.yaw and input.pitch
pitch around x-axis, yaw around y-axis, roll around z-axis
x-axis[0,-1,0]->yaw(0),roll(0)->[0,-1,0] = 'down'
y-axis[1,0,0]->pitch(90),roll(0)->[0,0,01] = 'behind'
z-axis[0,0,1]->pitch(90),yaw(0)->[1,0,0] = 'right'
This, in fact, corresponds to our goal.
4) perpendicular of the wallnormal from 1)
input.yaw = 90 (facing "right" with respect to the reference frame)
input.pitch = 0 (facing "directly ahead" with respect to the reference frame)
wallnormal = [0,0,-1] (pointing "behind" with respect to the absolute reference frame)
the player strafes left into the wall (or, alternatively, the player is facing "forward", walks forward into the wall, THEN turns right while on the wall)
our goal:
the player's orientation should be facing straight down, with:
x-axis[0,-1,0] = 'down'
y-axis[0,0,-1] = 'behind' = wallnormal, no player roll
z-axis[1,0,0] = 'right'
from the wallnormal, you get:
wallpitch = 90 with respect to the absolute reference frame
wallroll = 0 with respect to the absolute reference frame
you could build the reference frame now
angles = Angles(wallpitch,0,wallroll)
coords = angles:GetCoords()
this should lead to a reference frame:
x-axis[1,0,0]->yaw(0),roll(0)->[1,0,0] = 'right'
y-axis[0,1,0]->pitch(90),roll(0)->[0,0,-1] = 'behind' = wallnormal
z-axis[0,0,1]->pitch(90),yaw(0)->[0,1,0] = 'up'
Notice that in the case of the x-axis, it retains its orientation, because the angles it is changed by (yaw and roll) are zero.
now you rotate the player with input.yaw and input.pitch
pitch around x-axis, yaw around y-axis, roll around z-axis
x-axis[1,0,0]->yaw(90),roll(0)->[0,-1,0] = 'down'
y-axis[0,0,-1]->pitch(0),roll(0)->[0,0,-1] = 'behind' = wallnormal, no player roll
z-axis[0,1,0]->pitch(0),yaw(90)->[1,0,0] = 'right'
This, in fact, corresponds to our goal.
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function Skulk:UpdateViewAngles(input)
local wallNormal = self.wallWalkingNormalCurrent // get wall walking normal in absolute frame of reference
local wallNormalRoll = wallNormal // copy wallNormal, do you need to use a specific function for this?
wallNormalRoll.z = 0 // flatten normal along x-y plane
wallNormalRoll:Normalize() // get unit vector
local wallNormalPitch = wallNormal // copy wallNormal, do you need to use a specific function for this?
wallNormalPitch.x = 0 // flatten normal along z-y plane
wallNormalPitch:Normalize() // get unit vector
local wallRoll = 0 // initialise
local wallPitch = 0 // initialise
// all angles in radians
if wallNormalRoll.x < 0 // if x is negative (left) with respect to absolute frame of reference
wallRoll = -math.acos(wallNormalRoll.y) // roll is negative (roll left) with respect to absolute frame of reference
else // if x is positive (right) with respect to absolute frame of reference
wallRoll = math.acos(wallNormalRoll.y) // roll is positive (roll right) with respect to absolute frame of reference
end
if wallNormalPitch.z < 0 // if z is negative (behind) with respect to absolute frame of reference
wallPitch = math.acos(wallNormalPitch.y) // pitch is positive (tilt backward) with respect to absolute frame of reference
else // if z is positive (forward) with respect to absolute frame of reference
wallPitch = -math.acos(wallNormalPitch.y) // pitch is negative (tilt forward) with respect to absolute frame of reference
end
local angles = Angles(wallPitch, 0, wallRoll) // set reference angles, essentially shifting from the absolute frame of reference
local coords = angles:GetCoords() // build reference axes from reference angles
// this is the reference frame, i.e. x-z plane forms the movement plane, y-axis is the wallnormal.
// rotate reference frame around x-axis with player pitch, and y-axis with player yaw
coords = coords*Coords.GetRotation(coords.xAxis, input.pitch)*Coords.GetRotation(coords.yAxis, input.yaw)
// this is the camera axes
angles:BuildFromCoords(coords) // build camera angles from camera axes
self:SetViewAngles(angles) // set camera to camera angles
... // all the other debug, offset stuff etc.
end<!--c2--></div><!--ec2-->
Clean:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function Skulk:UpdateViewAngles(input)
local wallNormal = self.wallWalkingNormalCurrent
local wallNormalRoll = wallNormal
wallNormalRoll.z = 0
wallNormalRoll:Normalize()
local wallNormalPitch = wallNormal
wallNormalPitch.x = 0
wallNormalPitch:Normalize()
local wallRoll = 0
local wallPitch = 0
if wallNormalRoll.x < 0
wallRoll = -math.acos(wallNormalRoll.y)
else
wallRoll = math.acos(wallNormalRoll.y)
end
if wallNormalPitch.z < 0
wallPitch = math.acos(wallNormalPitch.y)
else
wallPitch = -math.acos(wallNormalPitch.y)
end
local angles = Angles(wallPitch, 0, wallRoll)
local coords = angles:GetCoords()
// this is the reference frame, i.e. x-z plane forms the movement plane, y-axis is the wallnormal.
coords = coords*Coords.GetRotation(coords.xAxis, input.pitch)*Coords.GetRotation(coords.yAxis, input.yaw)
// this is the camera axes
angles:BuildFromCoords(coords)
self:SetViewAngles(angles)
... // all the other debug, offset stuff etc.
end<!--c2--></div><!--ec2-->
For comparison, here was Yuuki's first prototype:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1--> local wallRoll = -math.acos(self.wallWalkingNormalCurrent.y)
local angles = Angles(0,0,0)
local coords = angles:GetCoords()
coords = coords*Coords.GetRotation(coords.zAxis, wallRoll)*Coords.GetRotation(coords.yAxis, input.yaw)*Coords.GetRotation(coords.xAxis, input.pitch)
angles:BuildFromCoords(coords)
self:SetViewAngles(angles)<!--c2--></div><!--ec2-->
Appending with some relevant links:
<a href="http://damien.kodingen.com/ns2docs/index.html" target="_blank">NS2Docs</a>
<a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Coords" target="_blank">Objects:Coords</a>
<a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Angles" target="_blank">Objects:Angles</a>
<a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/Vector" target="_blank">Objects:Vector</a>
<a href="http://www.unknownworlds.com/ns2/forums/index.php?showtopic=115926&view=findpost&p=1897270" target="_blank">AvP source code from Soul_Rider in original discussion thread</a>
<a href="http://www.unknownworlds.com/ns2/forums/index.php?showtopic=115926&view=findpost&p=1897366" target="_blank">Yuuki's first prototype code in original discussion thread</a>
<center><object width="450" height="356"><param name="movie" value="http://www.youtube.com/v/EZphgsNTy8I"></param><embed src="http://www.youtube.com/v/EZphgsNTy8I" type="application/x-shockwave-flash" width="450" height="356"></embed></object></center>
I'll give a more detailed run down of the actual issues later, suffice to say, when you spawn facing the hive every thing is correct. If you turn your view horizontally 90degrees, up down becomes roll.
The code flips you randomly from one side of the face to the other, and touching anything throws you off in all sorts of crazy directions. Mouse key inputs are still the wrong way around on the wall.