Skip to main content
Solution
Source Link

EDIT: The solution I ended up with is:

    public static (Vector3, Quaternion, Vector3) GetChildPosLeg(
    Transform bottomReference,
    Transform topReference,
    Transform topTarget,
    Transform bottomTarget,
    Vector3 parentScale,
    Vector3 itemScaleRatio
    )
{
    // World targets
    Vector3 targetAWorldPos = (Vector3.Scale(topTarget.position, parentScale));
    Vector3 targetBWorldPos = (Vector3.Scale(bottomTarget.position, parentScale));

    // Scale
    Vector3 scaledBottom = Vector3.Scale(bottomReference.position, itemScaleRatio);
    Vector3 scaledTop = Vector3.Scale(topReference.position, itemScaleRatio);
    float incomingDistance = Vector3.Distance(scaledBottom, scaledTop);
    float targetDistance = Vector3.Distance(targetAWorldPos, targetBWorldPos);

    float scaleRatio = targetDistance / incomingDistance;
    Vector3 finalScale = itemScaleRatio * scaleRatio;

    // Rotation
    Vector3 refDir = scaledBottom - scaledTop;
    Vector3 targetDir = topTarget.position - bottomTarget.position;

    // Local 'up' vector of the incoming object
    Vector3 refUp = refDir.normalized;

    // Local 'forward' vector of the incoming object (define this based on prefab orientation)
    Vector3 refForward = Vector3.Cross(refUp, bottomReference.forward).normalized; // assuming 'right' is orthogonal
    Quaternion refRotation = Quaternion.LookRotation(refForward, refUp);

    //Build a target rotation from the target node
    Vector3 targetUp = targetDir.normalized;
    Vector3 targetForward = topTarget.forward; // assuming the prefab is aligned the same way
    Quaternion targetRotation = Quaternion.LookRotation(targetForward, targetUp);

    Quaternion finalRotation = targetRotation * Quaternion.Inverse(refRotation);

    // Position
    // Scale it
    Vector3 scaledOffset = Vector3.Scale(scaledBottom, finalScale);

    // Rotate it
    Vector3 rotatedOffset = finalRotation * scaledOffset;

    //  Subtract that offset from the target position
    Vector3 finalPosition = targetAWorldPos - rotatedOffset;

    return (finalPosition, finalRotation, finalScale);
}

EDIT: The solution I ended up with is:

    public static (Vector3, Quaternion, Vector3) GetChildPosLeg(
    Transform bottomReference,
    Transform topReference,
    Transform topTarget,
    Transform bottomTarget,
    Vector3 parentScale,
    Vector3 itemScaleRatio
    )
{
    // World targets
    Vector3 targetAWorldPos = (Vector3.Scale(topTarget.position, parentScale));
    Vector3 targetBWorldPos = (Vector3.Scale(bottomTarget.position, parentScale));

    // Scale
    Vector3 scaledBottom = Vector3.Scale(bottomReference.position, itemScaleRatio);
    Vector3 scaledTop = Vector3.Scale(topReference.position, itemScaleRatio);
    float incomingDistance = Vector3.Distance(scaledBottom, scaledTop);
    float targetDistance = Vector3.Distance(targetAWorldPos, targetBWorldPos);

    float scaleRatio = targetDistance / incomingDistance;
    Vector3 finalScale = itemScaleRatio * scaleRatio;

    // Rotation
    Vector3 refDir = scaledBottom - scaledTop;
    Vector3 targetDir = topTarget.position - bottomTarget.position;

    // Local 'up' vector of the incoming object
    Vector3 refUp = refDir.normalized;

    // Local 'forward' vector of the incoming object (define this based on prefab orientation)
    Vector3 refForward = Vector3.Cross(refUp, bottomReference.forward).normalized; // assuming 'right' is orthogonal
    Quaternion refRotation = Quaternion.LookRotation(refForward, refUp);

    //Build a target rotation from the target node
    Vector3 targetUp = targetDir.normalized;
    Vector3 targetForward = topTarget.forward; // assuming the prefab is aligned the same way
    Quaternion targetRotation = Quaternion.LookRotation(targetForward, targetUp);

    Quaternion finalRotation = targetRotation * Quaternion.Inverse(refRotation);

    // Position
    // Scale it
    Vector3 scaledOffset = Vector3.Scale(scaledBottom, finalScale);

    // Rotate it
    Vector3 rotatedOffset = finalRotation * scaledOffset;

    //  Subtract that offset from the target position
    Vector3 finalPosition = targetAWorldPos - rotatedOffset;

    return (finalPosition, finalRotation, finalScale);
}
Source Link

Two Point Alignment Transformations

I'm trying to determine the transformation steps (position, scale, rotation) needed to be applied to an object so that two points, locally positioned in that object, line up with two world space positions.

The setup currently is:

  • E is a prefab with two transform children (A, B)
  • Transforms C and D are also within a prefab (F)
  • We have a world position, scale and rotation that F will instantiate with
  • Prior to any instantiation, calculate the transformation that needs to take place for E to end up in the right position.

Problem in parts

The solution I have at the moment is close but the rotation never seems to line up perfectly, affecting the positioning.

public static (Vector3, Quaternion, Vector3) GetChildPosLeg(
    Transform topReference, // A
    Transform bottomReference, // B
    Transform topTarget, // C
    Transform bottomTarget, // D
    Vector3 scale // Scale of root instantiated object
    )
{
    // World topTarget
    Vector3 topTargetPos = (Vector3.Scale(topTarget.position, scale));
    Vector3 bottomTargetPos = (Vector3.Scale(bottomTarget.position, scale));

    // Scale
    float refDist = Vector3.Distance(topReference.position, bottomReference.position);
    float targetDist = Vector3.Distance(topTargetPos, bottomTargetPos);

    float scaleRatio = targetDist / refDist;
    Vector3 finalScale = new Vector3(0.5f, 1f, 0.5f) * scaleRatio;

    // Rotation
    Vector3 targetDir = topTarget.position - bottomTarget.position;
    Vector3 refDir =  bottomReference.localPosition - topReference.localPosition;

    Quaternion objectRotation = Quaternion.FromToRotation(refDir, targetDir);

    // Position
    Vector3 childNodePos = Vector3.Scale(topReference.localPosition, finalScale);
    childNodePos = objectRotation * childNodePos;
    Vector3 finalPosition = topTargetPos + childNodePos;

    return (finalPosition, objectRotation, finalScale);
}