I have two Quaternion transforms, and a viewmatrix. These are custom transforms made by Dice, and I'm working on some custom modding for an older version of Battlefield.

Now, I don't have the entire context of these, as I've reversed engineered these, however I'm lost on how to calculate the Child's world location with the parent.

One is the parent transform, and here's what it looks like


translation: Vector4(-497.010f, 116.293f, -393.720f, 0.0f)

rotation: Quaternion(0.000f, 0.0707f, 0.000f, 0.707f)


translation: Vector4(0.018f, 1.547f, 0.030f, 0) // note how translation is simply an offset of the parent
rotation: Quaternion(0.000f, 0.0707f, 0.000f, 0.707f) // I made this value up, I can't remember what a good example was

How do I factor in the parents rotation? I've tried the following:

relativeRotation = inverse(parent.Rotation) * child.Rotation
var transformed = Vector4.Transform(childTranslation, relativeRotation) + parent.Translation

However it doesn't seem to be accurate. What am I missing here? Speficically I'm looking to calculate the 3d coordinate (with rotation applied) of the child, i.e. the world location.

There's also a scale for both, but its the same value for all parent and child:

var scale = Vector4(1, 1, 1, 0)

Current code:


childWorldLocation  = Vector4.Multiply(Vector4.Transform(child.Translation, parent.Rotation), parent.Scale) + parent.Translation;

screenCoordinate = Util.WorldToScreen(new Vector3(childWorldPosition.X, childWorldPosition.Y, childWorldPosition.Z), viewProjection); // this works fine for any vec3 origin, not the issue

Result is most of the child transforms are near the bottom where the parent translation is.


    public static Vector2 WorldToScreen(Vector3 position, Matrix viewProjection) {

        float w = viewProjection.M14 * position.X + viewProjection.M24 * position.Y + viewProjection.M34 * position.Z + viewProjection.M44;

        if (w < 0.01) {
            return Vector2.Zero;

        var screenPositionX = viewProjection.M11 * position.X + viewProjection.M21 * position.Y + viewProjection.M31 * position.Z + viewProjection.M41;
        var screenPositionY = viewProjection.M12 * position.X + viewProjection.M22 * position.Y + viewProjection.M32 * position.Z + viewProjection.M42;

        float invw = 1.0f / w;

        screenPositionX *= invw;
        screenPositionY *= invw;

        float x = (float)H.ViewPortX / 2; //viewport is the resolution x,y
        float y = (float)H.ViewPortY / 2;

        x += 0.5f * screenPositionX * (float)H.ViewPortX + 0.5f;
        y -= 0.5f * screenPositionY * (float)H.ViewPortY + 0.5f;

        return new Vector2(x, y);
  • \$\begingroup\$ This part is weird x += 0.5f * screenPositionX * (float)H.ViewPortX + 0.5f;, haven't you messed up the symbols? Shouldn't it be x += 0.5f * screenPositionX + (float)H.ViewPortX * 0.5f; or am I missing something? \$\endgroup\$
    – PentaKon
    Nov 23, 2021 at 16:24

1 Answer 1


The child's rotation does not change the child's position. Try it: do a pirouette - you stay in the same place, despite the fact that you are rotating through a full 360 degrees.

So, child rotation does not factor into this expression at all:

var childWorldPosition = Vector4.Scale(
                             Vector4.Transform(child.Translation, parent.Rotation),
                       ) + parent.Translation;

The child's rotation affects only the child's rotation:

var childWorldRotation = Quaternion.Compose(parent.Rotation, child.Rotation);

(Though you may need to flip the order of the arguments if your quaternion composition method takes the "more local" argument first and the "more global" argument second)

  • \$\begingroup\$ Hey Gregory! I believe this is mostly right, although I'm still having issues. The layout of the children looks correct, but they appear to be too low on the up and down axis. The game also has a character transform matrix where, M41, M42, M43 is it's world position, which IS THE SAME as the parent transform's translation. Do you think perhaps this needs to be applied to that somehow as well? \$\endgroup\$
    – Ben
    Nov 23, 2021 at 15:54
  • \$\begingroup\$ Just to confirm: you added + parent.Translation already as shown above? \$\endgroup\$
    – DMGregory
    Nov 23, 2021 at 15:59
  • \$\begingroup\$ Indeed, I've updated my post with my current code. \$\endgroup\$
    – Ben
    Nov 23, 2021 at 16:01
  • \$\begingroup\$ I'm 95% sure your post is correct, it appears that these values I'm reading from memory have some issue (odd things like a float FF FF FF A4), so I'm assuming they're just a bit garbled. \$\endgroup\$
    – Ben
    Nov 23, 2021 at 16:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .