2
votes

I would like to understand how to measure the distance between two 3D objects, let's call them a parent object and a child object. Think of the parent as the body of a car and the child being a wheel of the car.

I understand how to get the difference based on the objects position in world space but I would like to get the difference as a measurement based on the parents relative object space. 

E.g if the parent is facing East and the child is 2X, 3Y from the parent, measured in a relative sense. Such that if the parent rotated 60 degrees, the relative location of the child remains at a distance of 2x, 3y in the object space. Where as in a world space sense the child objects measurement as a Vector3 would be quite different. 

Basically I just want a predictable way to get the difference so that a child object which is on the right of the patent can always stay right of the parent object. 

This is the parent component, this update is run every frame:

[Serializable]
    public class Component_Parent : BaseComponentAutoSerialization<ISceneEntity>
    {
        public override void OnUpdate(GameTime gameTime)
        {
            PassThrough.ParentMatrix = ParentObject.World;
            PassThrough.ParentTranslation = ParentObject.World.Translation;


        }
    }

This next part is the child component:

[Serializable]
    public class Component_Child : BaseComponentAutoSerialization<ISceneEntity>
    {
        Vector3 _parentOffset;
        Quaternion _parentQuaternionOffset;

        public override void OnUpdate(GameTime gameTime)
        {
            // Get a sceneobject from the ParentObject
            SceneObject sceneobject = (SceneObject)ParentObject;

            // This relies on the position never being at 0,0,0 for setup, so please don't do that
            // or change it with more look ups so that you don't need to rely on a Zero Vector3 :-)
            if (PassThrough.GroupSetupMode || _parentOffset == Vector3.Zero)
            {
                if (PassThrough.ParentTranslation != Vector3.Zero)
                {
                    _parentOffset = sceneobject.World.Translation - PassThrough.ParentTranslation;

                    // Decompose World Matrix (Parent)
                    Quaternion parentQ = new Quaternion();
                    Vector3 parentSpot = new Vector3();
                    Vector3 parentScale = new Vector3();
                    PassThrough.ParentMatrix.Decompose(out parentScale, out parentQ, out parentSpot);

                    Matrix identity = Matrix.Identity;

                    // Decompose Identity Matrix (Parent)
                    Quaternion identityQ = new Quaternion();
                    Vector3 identitySpot = new Vector3();
                    Vector3 identityScale = new Vector3();
                    identity.Decompose(out identityScale, out identityQ, out identitySpot);

                    _parentQuaternionOffset = identityQ - parentQ;
                }
            }
            else
            {
                if (_parentOffset != Vector3.Zero)
                {

                    // Decompose World Matrix (Child)
                    Quaternion rotationQ = new Quaternion();
                    Vector3 spot = new Vector3();
                    Vector3 scale = new Vector3();
                    sceneobject.World.Decompose(out scale, out rotationQ, out spot);


                    // Decompose World Matrix (Parent)
                    Quaternion parentQ = new Quaternion();
                    Vector3 parentSpot = new Vector3();
                    Vector3 parentScale = new Vector3();
                    PassThrough.ParentMatrix.Decompose(out parentScale, out parentQ, out parentSpot);

                    Matrix location = Matrix.CreateTranslation(PassThrough.ParentTranslation);
                    Matrix rotation = Matrix.CreateFromQuaternion(parentQ);

                    Matrix rotation2 = Matrix.CreateFromQuaternion(_parentQuaternionOffset);

                    Matrix newWorld = rotation * location;

                    Vector3 testTranslation = newWorld.Translation + ((newWorld.Left * _parentOffset.X) + (newWorld.Up * _parentOffset.Y) + (newWorld.Forward * _parentOffset.Z));
                    Matrix scaleM = Matrix.CreateScale(scale);



                    //sceneobject.World = scaleM * (rotation * (Matrix.CreateTranslation(testTranslation)));
                    sceneobject.World = (Matrix.CreateTranslation(testTranslation));
                }
            }
        }
    }

I think it has something to do with keeping track of an offset rotation, from the identity matrix and I have started trying to add some code to that effect but really unsure of what next now.

Additional:

If I have the parent object facing the direction of the world space it all works, if it's facing a different direction then it's an issue and the child seems to rotate by the same amount when they are grouped together.

I've uploaded a demo video to try and explain:

http://www.youtube.com/watch?v=BzAKW4WBWYs

I've also pasted up the complete code for the components, the static pass through and the scene entity.

http://pastebin.com/5hEmiVx9

Thanks

3
I don't understand your question. You want the distance between parent /child? Or child distance from the origin?namar0x0309
Distance between parent & child, relative to the direction of the parent.Sebastian Gray
Well whatever the direction is the child will have same distance. Matrix of parent will be applied, then matrix of child. As long as the translation part of the child's matrix isn't touched, the distance will remain the same.namar0x0309

3 Answers

2
votes

Think wheels on a car. I want the right wheel to always be in the same spot relative to the body of the car.

It sounds like you want to be able to locate the position of the wheel for any given orientation or position of the car. One built in method that XNA has to help here is Model.CopyAbsoluteBoneTransformsTo(Matrix[]); However, your code looks like you want to handle parent child relationship manually. So here is a way to do it without using the built in method. It assumes you do have offset information at load time:

Before the game loops starts (say, in the LoadContent method), after loading the car & wheel and assuming they load into the proper positions, you can then create your offset vector ( _parentOffset )

Vector3 _parentOffset = wheel.meshes[?].ParentBone.Transform.Translation - car.meshes[?].ParentBone.Transform.Translation;//where ? is the mesh index of the mesh you are setting up.

Save that vector and don't modify it.

Later, after the car's matrix has been rotationally and or positionally displaced, set the wheel's matrix like this:

Matrix wheelMatrix = carMatrix;
wheelMatrix.Translation += (wheelMatrix.Right * _parentOffset.X) +
                           (wheelMatrix.Up * _parentOffset.Y) +
                           (wheelMatrix.Backward * _parentOffset.Z);

This allows that the wheel matrix will inherit any rotational and translational information from the car but will displace the wheel's position appropriately regardless of car's orientation/position.

0
votes

The distance between two objects is NOT a function of either orientations.

What you basically want is the distance of the child object to the orientation line of the parent object. Assuming you have a global cartesian coordinate system this can be simply calculated as h=sqrt(x^2+y^2)*sin(Theta), x and y being the relative coordinates of the child with respect to the parent and Theta the orientation of the parent measured from x axis.

But still the question is a little bit confusing to me. If you only want to make sure that the child is on the right side of the parent why don't you simply check the relative x? If it's positive it's on the right and if it's negative it's on the left?

0
votes

The issue was the way I was trying to use the offset of the world space. Thanks to flashed from #XNA on EFnet, this code works perfectly:

 [Serializable]
    public class Component_Child_fromxna : BaseComponentAutoSerialization<ISceneEntity>
    {
        Vector3 _parentOffset;
        Matrix _ParentMatrixOffset;


        public override void OnUpdate(GameTime gameTime)
        {
            // Get a sceneobject from the ParentObject
            SceneObject sceneObject = (SceneObject)ParentObject;


            // This relies on the position never being at 0,0,0 for setup, so please don't do that
            // or change it with more look ups so that you don't need to rely on a Zero Vector3 :-)
            if (PassThrough.GroupSetupMode || _parentOffset == Vector3.Zero)
            {
                if (PassThrough.ParentTranslation != Vector3.Zero)
                {
                    // The old offset - This is just in world space though...
                    _parentOffset = sceneObject.World.Translation - PassThrough.ParentTranslation;

                    // Get the distance between the child and the parent which we keep as the offset
                    // Inversing the ParentMatrix and multiplying it by the childs matrix gives an offset
                    // The offset is stored as a relative xyz, based on the parents object space
                    _ParentMatrixOffset = sceneObject.World * Matrix.Invert(PassThrough.ParentMatrix);
                }
            }
            else
            {
                if (_parentOffset != Vector3.Zero)
                {

                    //Matrix pLocation = Matrix.CreateTranslation(_parentOffset);
                    //sceneObject.World = Matrix.Multiply(pLocation, PassThrough.ParentMatrix);

                    sceneObject.World = Matrix.Multiply(_ParentMatrixOffset, PassThrough.ParentMatrix);
                }
            }
        }
    }