Skulk View Rotation

24

Comments

  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    That looks good, I think it would work! Where do we implement this code now? I lost track of that detail somewhere along the way :)
  • YuukiYuuki Join Date: 2010-11-20 Member: 75079Members
    edited January 2012
    Here you go :

    <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 :)
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    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.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    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)

    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.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    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
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    How does it determine the perpendicular vector when there are an infinite number of perpendicular vectors?
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    <!--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
          
            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.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    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

    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: (,,)
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    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.

    Later, though.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    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.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    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()

    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-->
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    So... something like this...

    <!--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-->
  • Soul_RiderSoul_Rider Mod Bean Join Date: 2004-06-19 Member: 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.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    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_RiderSoul_Rider Mod Bean Join Date: 2004-06-19 Member: 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-->

    LoL
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    My personal favorites are;

    <!--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-->
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    <!--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_RiderSoul_Rider Mod Bean Join Date: 2004-06-19 Member: 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 :

    <!--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 :)
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    Uh, it's not that exactly.

    <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.
  • Soul_RiderSoul_Rider Mod Bean Join Date: 2004-06-19 Member: 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.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    <!--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--><img src="http://i.imgur.com/rX5m0.png" border="0" class="linked-image" /><!--QuoteEnd--></div><!--QuoteEEnd-->

    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.
  • YuukiYuuki Join Date: 2010-11-20 Member: 75079Members
    <!--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.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    <!--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...
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    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.
  • Soul_RiderSoul_Rider Mod Bean Join Date: 2004-06-19 Member: 29388Members, Constellation, Squad Five Blue
    @ Fluid - I posted that earlier :)

    AngleVelocity.euler.y x Transformational matrix. It's in the code for AVP wall-walking which I posted.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    <!--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.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    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
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    So, examples... (using Approach A)

    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.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    The code to make this happen pretty much already existed in one of my previous attempts:

    <!--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>
  • Soul_RiderSoul_Rider Mod Bean Join Date: 2004-06-19 Member: 29388Members, Constellation, Squad Five Blue
    edited August 2012
    The results are hilarious :)

    <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.
Sign In or Register to comment.