Search Unity

How to rotate around localAxis?

Discussion in 'Data Oriented Technology Stack' started by Buretto, Apr 20, 2019.

  1. Buretto

    Buretto

    Joined:
    Mar 23, 2015
    Posts:
    31
    I am really bad at math(s), and just had a unsuccessful day trying to recreate the
    transform.RotateAround 
    functionality in ECS. Through my research I found this thread, which includes the function for the translation; but I couldn't figure out how to make it rotate (for my needs) as well.

    What I want is:
    • player can rotate about local y axis using mouse
    • players rotation stays "in sync" with position around sphere
    Maybe this gif can better illustrate:

    In this case, the rotation veers "off course" (while just holding down one button, no mouse input) as soon as you start moving in any second axis (here is image of the path in a separate run).
    I suspect the solution will be embarrassingly easy, just my poor brain could not get with it ;(. It's the end of the day for me, thank you all in advanced; here is the code I left off with:

    Code (CSharp):
    1.  protected override unsafe void OnUpdate()
    2.         {
    3.             Entities.ForEach(
    4.                 (ref PlayerController playerController,
    5.                 ref LocalToWorld toWorld,
    6.                 ref Translation position,
    7.                 ref Rotation rotation) =>
    8.                 {
    9.                     float deltaTime = Time.deltaTime;
    10.  
    11.                     float velX = Input.GetAxisRaw("Horizontal");
    12.                     float velZ = Input.GetAxisRaw("Vertical");
    13.  
    14.                     float rotY = Input.GetAxis("Mouse X");
    15.                     bool changedRot = false;
    16.                     Quaternion quat = rotation.Value;
    17.                     if (math.abs(rotY) > float.Epsilon)
    18.                     {
    19.                         float delta = rotY * playerController.RotateSpeed * deltaTime;
    20.                         quat = quat * quaternion.AxisAngle(toWorld.Up, delta);
    21.                         changedRot = true;
    22.                         Debug.DrawRay(position.Value, toWorld.Up, Color.green, 255);
    23.                     }
    24.  
    25.                     if (math.abs(velX) > float.Epsilon)
    26.                     {
    27.                         float delta = -velX * playerController.MoveSpeed * deltaTime;
    28.                         Quaternion rot = quaternion.AxisAngle(toWorld.Forward, delta);
    29.                         Debug.DrawRay(position.Value, toWorld.Forward, Color.red, 255);
    30.                         position.Value = rot * position.Value;
    31.                         quat = quat * rot;
    32.                         changedRot = true;
    33.                     }
    34.  
    35.                     if (math.abs(velZ) > float.Epsilon)
    36.                     {
    37.                         float delta = velZ * playerController.MoveSpeed * deltaTime;
    38.                         Quaternion rot = quaternion.AxisAngle(toWorld.Right, delta);
    39.                         position.Value = rot * position.Value;
    40.                         quat = quat * rot;
    41.                         changedRot = true;
    42.                         Debug.DrawRay(position.Value, toWorld.Right, Color.blue, 255);
    43.  
    44.                     }
    45.                     if (changedRot)
    46.                         rotation.Value = quat;
    47.                 });
    48.         }
    49.     }
     
  2. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    417
    First of all you should do all that in a Bursted Job so you can take advantage of multi-threading and burst compiler.
    This is what i use to rotate around a center point in a certain axis.
    Code (CSharp):
    1.   public static float4x4 RotateAround(LocalToWorld localToWorld, float3 center, float3 axis, float angle) {
    2.     var initialRot = quaternion.LookRotationSafe(localToWorld.Forward, localToWorld.Up);
    3.     var rotAmount = quaternion.AxisAngle(axis, angle);
    4.     var finalPos = center + math.mul(rotAmount, localToWorld.Position - center);
    5.     var finalRot = math.mul(math.mul(initialRot, math.mul(math.inverse(initialRot), rotAmount)), initialRot);
    6.     return new float4x4(finalRot, finalPos);
    7.   }
    In my case use mouse input to rotate the camera around another object.
    Hope this helps you going forward.
     
  3. Buretto

    Buretto

    Joined:
    Mar 23, 2015
    Posts:
    31
    Hi GilCat,
    In order to rotate this in two dimensions, how do you get the
    float3 axis
    to rotate about? Or do you call this function twice in in separate axis?

    I'm still not sure how to rotate about "local y axis" while keeping orientation with the sphere.

    Also I am unsure if this functionality deserves to be put in a job, since it will only be applying to one entity I think it would be overkill. I will probably use
    PostUpdateCommands
    once I get it working.

    Anyways, I'll be sure to keep your function in mind while experimenting today, hopefully more luck this morning.
    Thanks :)
     
  4. Buretto

    Buretto

    Joined:
    Mar 23, 2015
    Posts:
    31
    Actually you got me interested in:
    var finalRot = math.mul(math.mul(initialRot, math.mul(math.inverse(initialRot), rotAmount)), initialRot);

    Perhaps this is the key, could you explain how it works or maybe point to where I can read more about it?
    Unfortunately I'm really bad a math so I can't figure it out haha.
    It's got me interested since I'm just used to seeing (using) something like:
    Quaternion rot = quaternion.AxisAngle(toWorld.Right, delta);
     
  5. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    417
    You can read about quaternion rotation here.
    You can also look in here where there is some implementation of it using legacy unity math API.
     
  6. Buretto

    Buretto

    Joined:
    Mar 23, 2015
    Posts:
    31
    Ahh nevermind.. that's exactly what I needed! Thank you very much GilCat it works perfect :). If you dont mind I've got a couple of questions I would like to ask:
    1. Is there a way to directly convert a
      float4
      to a
      float3
      , right now I am using a
      Matrix4x4
      and a
      Vector3
      to convert so I assume it must be possible?
    2. Do you know a way to get up, forward, right etc.. directions from a
      float4x4
      representing rotation and translation? Right now I am using a "temp"
      LocalToWorld.Value
    Here is working code for anyone who wants it in future, probably will need fixing:

    Code (CSharp):
    1. public class RotateCharacterController : ComponentSystem
    2.     {
    3.         protected override unsafe void OnUpdate()
    4.         {
    5.             Entities.ForEach(
    6.                 (
    7.                 ref PlayerController playerController,
    8.                 ref LocalToWorld toWorld,
    9.                 ref Translation position,
    10.                 ref Rotation rotation) =>
    11.                 {
    12.                     float deltaTime = Time.deltaTime;
    13.  
    14.                     float velX = Input.GetAxisRaw("Horizontal");
    15.                     float velZ = Input.GetAxisRaw("Vertical");
    16.  
    17.                     float rotY = Input.GetAxis("Mouse X");
    18.                     LocalToWorld tempWorld = new LocalToWorld { Value = toWorld.Value};
    19.                     Quaternion tempRot = rotation.Value;
    20.                     float3 tempPos = position.Value;
    21.                     bool rotated = false;
    22.                     bool translated = false;
    23.                     if (math.abs(rotY) > float.Epsilon)
    24.                     {
    25.                         rotated = true;
    26.                         float delta1 = math.mul(math.mul(rotY,playerController.RotateSpeed),  deltaTime);
    27.                         tempRot = math.mul(tempRot, quaternion.AxisAngle(math.up(), delta1));
    28.                         tempWorld.Value = new float4x4(tempRot, position.Value);
    29.                         //Debug.DrawRay(position.Value, math.up(), Color.green, 255);
    30.                     }
    31.  
    32.                     if (math.abs(velX) > float.Epsilon)
    33.                     {
    34.                         rotated = true;
    35.                         translated = true;
    36.                         float delta = math.mul(math.mul(-velX, playerController.MoveSpeed), deltaTime);
    37.                         float3 dir = tempWorld.Forward;
    38.                         Matrix4x4 matrix = Common.RotationFunctions.RotateAround(tempWorld, float3.zero, dir, delta);
    39.                         Vector3 pos = matrix.GetColumn(3);
    40.                         tempWorld.Value = matrix;
    41.                         tempPos = pos;
    42.                         tempRot = Quaternion.LookRotation(matrix.GetColumn(2), matrix.GetColumn(1));
    43.                     }
    44.                     if (math.abs(velZ) > float.Epsilon)
    45.                     {
    46.                         rotated = true;
    47.                         translated = true;
    48.                         float delta = math.mul(math.mul(velZ, playerController.MoveSpeed),  deltaTime);
    49.                         float3 dir = tempWorld.Right;
    50.                         Matrix4x4 matrix = Common.RotationFunctions.RotateAround(tempWorld, float3.zero, dir, delta);
    51.                         Vector3 pos = matrix.GetColumn(3);
    52.                         tempWorld.Value = matrix;
    53.                         tempPos = pos;
    54.                         tempRot = Quaternion.LookRotation(matrix.GetColumn(2), matrix.GetColumn(1));
    55.                     }
    56.                     if (rotated)
    57.                     {
    58.                         //Rotation rot = new Rotation { Value = tempRot };
    59.                         //PostUpdateCommands.SetComponent<Rotation>(entity, rot);
    60.                         rotation.Value = tempRot;
    61.                         if (translated)
    62.                         {
    63.                             //Translation pos = new Translation { Value = tempPos };
    64.                             //PostUpdateCommands.SetComponent<Translation>(entity, pos);
    65.                             position.Value = tempPos;
    66.                         }
    67.                     }
    68.                 });
    69.         }
    70.     }
     
  7. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    417
    No problem. Glad I could help.
    About your questions:
    1. Yes. float4 has a xyz property that gives you a float3.
    2. LocalToWorld has a Position, Forward and Up properties but you can also get that from float4x4.
    In float4x4, c0 is Right vector, c1 is Up vector, c2 is Forward vector and c3 is Position.

    Sorry for any typo as I'm on the phone right now.
     
    Buretto likes this.
  8. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    417
    You should use JobComponentSystem instead alongside with bursted jobs.
    The way i would do it would be to have a system to collect player input and another to process that input.
    In DOD concerns should be separated as much as possible.