Author Topic: Math help; physically parenting objects  (Read 821 times)

I have two staticshapes, which i am trying to parent together so that when i rotate the parent, the child's position is updated, in 3d.

This is what i have:
When the child is set to be the child, it is given a position and rotation offsets, and a scale factor. Rotation offset is simply the parent's rotation minus the child's rotation. Position offset is just vectorSub(%parent.position, %child.position). Scale factor is just the fraction of the child's scale to the parent.
When i rotate the parent, I call a function which loops through all the children, and move, rotate and scale the child based on the three variables mentioned above. Currently, i have it rotating and scaling the child perfectly, however it doesn't move the child when the parent is rotated. This is easy enough using trig functions, but I need it to be in 3d.

Heres a picture to show you what i'm trying to do, but i need it in 3d instead of 2d. The parent is staying stationary, and only rotating.


Heres the relevant code:
Code: [Select]
function holo_addChild(%parent,%child)
{
if(%parent.class !$= "holo" || %child.class !$= "holo" || !isObject(%parent) || !isObject(%child) || isObject(%child.parent))
return;
holo_unChild(%parent, %child); //Remove it if it is already a child
%parent.children = trim(%parent.children SPC %child);

advVCEDebug("Set" SPC %parent SPC "as the parent of" SPC %child @ ". child list: \"" @ %parent.children @ "\"");

%child.posOffset = VectorSub(%child.getPosition(),%parent.getPosition());

%child.rotOffset = VectorSub(axisToEuler(getWords(%child.getTransform(),3,6)),axisToEuler(getWords(%parent.getTransform(),3,6)));

%pScale = %parent.getScale();
%pScaleX = getWord(%pScale,0);
%pScaleY = getWord(%pScale,1);
%pScaleZ = getWord(%pScale,2);

%cScale = %child.getScale();
%cScaleX = getWord(%cScale,0);
%cScaleY = getWord(%cScale,1);
%cScaleZ = getWord(%cScale,2);
%child.scaleFactor = %cScaleX/%pScaleX SPC %cScaleY/%pScaleY SPC %cScaleZ/%pScaleZ;

%child.parent = %parent;
}

// Gets called whenever a holo is moved, rotated or scaled
function holo_updateChildren(%parent)
{
advVCEDebug("Updating children on" SPC %parent @ ".");
if(%parent.class !$= "holo" || !isObject(%parent) || getWordCount(%parent.children) < 1)
return;

%parentPos = %parent.getPosition();
%parentPosX = getWord(%parentPos,0);
%parentPosY = getWord(%parentPos,1);
%parentPosZ = getWord(%parentPos,2);

%parentRot = axisToEuler(getWords(%parent.getTransform(),3,6));
%ParentRotX = getWord(%parentRot,0);
%ParentRotY = getWord(%parentRot,1);
%ParentRotZ = getWord(%parentRot,2);

%parentScale = %parent.getScale();
%parentScaleX = getWord(%parentScale, 0);
%parentScaleY = getWord(%parentScale, 1);
%parentScaleZ = getWord(%parentScale, 2);

for(%i=0; %i < getWordCount(%parent.children); %i++)
{
%child = getWord(%parent.children,%i);
// Check if the child exists, and if it is a Hologram
if(isObject(%child) && %child.class $= "holo")
{
advVCEDebug("Updating child" SPC %child @ ".");
%childPos = %child.posOffset;
%childPosX = getWord(%childPos,0);
%childPosY = getWord(%childPos,1);
%childPosZ = getWord(%childPos,2);

%childRot = axisToEuler(getWords(%child.getTransform(), 3, 6));
%childRotX = getWord(%childRot,0);
%childRotY = getWord(%childRot,1);
%childRotZ = getWord(%childRot,2);

%childPosOffset = %child.posOffset;
%childRotOffset = %child.rotOffset;

%childScale = %child.scaleFactor; //Gets set when parented
%childScaleX = getWord(%childScale, 0);
%childScaleY = getWord(%childScale, 1);
%childScaleZ = getWord(%childScale, 2);

%scale = %childScaleX*%parentScaleX SPC %childScaleY*%parentScaleY SPC %childScaleZ*%parentScaleZ;

%pos = vectorAdd(%parentPos, %childPosOffset); //This is a temporary. Need math help with this
%rot = vectorAdd(%parentRot, %childRotOffset);

%child.setTransform(%pos SPC eulerToAxis(%rot));
%child.setScale(%scale);
holo_updateChildren(%child); //Update any of the child's children
} else
%parent.unChild(%child);
}
}

Again, just to be clear, I have rotating and scaling the child done perfectly, I only need to find its position when the parent is rotated, in 3d.

It seems to me like the best way to do this is by using vector crossing
There are a bunch of threads with similar problems, just search for "vectorCross"
http://forum.blockland.us/index.php?topic=230530.0

I've skimmed through all the topics that search gave me, and read in detail any that seemed related, but I don't see how i can use vectorCross to solve this. It seems to be used more in getting rotations from positions instead of positions from rotations, although i'm sure its easy to reverse.
And yes, I have looked into vector crossing.

EDIT: Now that ive thought about it, i want to know how to rotate a 3d vector by a 3d euler angle, which is much easier to search for. Still, any help would be appreciated.
« Last Edit: June 26, 2013, 07:17:59 PM by boodals 2 »

Well if you're already going to the trouble to convert to euler angles, you might as well use trigonometry.

For reference:
%radius * mSin(%yaw) = %y;
%radius * mCos(%yaw) = %x;
%radius * mSin(%pitch) = %z;


Trig isn't the best solution to this problem, but it should make more sense to write than other methods

This code would would rotate the position offset based on the parent object's rotation:
Code: [Select]
function setchildpos(%parent, %child)
{
%curtransform = %parent.gettransform();
%u["x"] = getword(%curtransform,3);
%u["y"] = getword(%curtransform,4);
%u["z"] = getword(%curtransform,5);

%angl = getword(%curtransform,6) * -1;
%cos = mcos(%angl);
%sin = msin(%angl);

%a[1,1] = %cos + (%u["x"] * %u["x"] * (1 - %cos));
%a[1,2] = (%u["x"] * %u["y"] * (1 - %cos)) - (%u["z"] * %sin);
%a[1,3] = (%u["x"] * %u["z"] * (1 - %cos)) + (%u["y"] * %sin);

%a[2,1] = (%u["y"] * %u["x"] * (1 - %cos)) + (%u["z"] * %sin);
%a[2,2] = %cos + (%u["y"] * %u["y"] * (1 - %cos));
%a[2,3] = (%u["y"] * %u["z"] * (1 - %cos)) - (%u["x"] * %sin);

%a[3,1] = (%u["z"] * %u["x"] * (1 - %cos)) - (%u["y"] * %sin);
%a[3,2] = (%u["z"] * %u["y"] * (1 - %cos)) + (%u["x"] * %sin);
%a[3,3] = %cos + (%u["z"] * %u["z"] * (1 - %cos));

%pos = getwords(%curtransform,0,3);
%x = getword(%child.offset,0);
%y = getword(%child.offset,1);
%z = getword(%child.offset,2);

%newx = (%a[1,1] * %x) + (%a[1,2] * %y) + (%a[1,3] * %z);
%newy = (%a[2,1] * %x) + (%a[2,2] * %y) + (%a[2,3] * %z);
%newz = (%a[3,1] * %x) + (%a[3,2] * %y) + (%a[3,3] * %z);

%newoffset = %newx SPC %newy SPC %newz;
%totalpos = vectoradd(%pos, %newoffset);

%child.settransform(%totalpos SPC getwords(%curtransform, 3, 6));
}
Found at http://forum.blockland.us/index.php?topic=215330.15

I believe this was used as reference http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle

There might be a better way I don't know.
« Last Edit: June 27, 2013, 02:30:51 AM by Zeblote »

Oh come on, i read thought that topic, but didn't check the second page -_-

Huge thanks though, lemme give it a try..

Edit: Works beautifully, thanks <3

Edit 2: Okay, turns out its playing up when moving a child after parenting. What should happen is when the child is moved, it recalculates the position offset again, so that when the parent is updated, it doesn't jump back to where it was. I'm pretty sure i'm calculating the position offset incorrectly, even though it should be just parent's position - child position..

Another problem I found is when the parent is rotated by 0 1 0 in euler, the child is rotated the wrong direction. I could just invert it, but id like to know why it happens so i can patch it properly.

Heres the updated code:
Code: [Select]
// Gets called whenever a holo is moved, rotated or scaled
function holo_updateChildren(%parent)
{
advVCEDebug("Updating children on" SPC %parent @ ".");
if(%parent.class !$= "holo" || !isObject(%parent) || getWordCount(%parent.children) < 1)
return;

%parentPos = %parent.getPosition();
%parentPosX = getWord(%parentPos,0);
%parentPosY = getWord(%parentPos,1);
%parentPosZ = getWord(%parentPos,2);

%parentRot = axisToEuler(getWords(%parent.getTransform(),3,6));
%ParentRotX = getWord(%parentRot,0);
%ParentRotY = getWord(%parentRot,1);
%ParentRotZ = getWord(%parentRot,2);

%parentScale = %parent.getScale();
%parentScaleX = getWord(%parentScale, 0);
%parentScaleY = getWord(%parentScale, 1);
%parentScaleZ = getWord(%parentScale, 2);

for(%i=0; %i < getWordCount(%parent.children); %i++)
{
%child = getWord(%parent.children,%i);
// Check if the child exists, and if it is a Hologram
if(isObject(%child) && %child.class $= "holo")
{
advVCEDebug("Updating child" SPC %child @ ".");
%childPos = %child.posOffset;
%childPosX = getWord(%childPos,0);
%childPosY = getWord(%childPos,1);
%childPosZ = getWord(%childPos,2);

%childRot = axisToEuler(getWords(%child.getTransform(), 3, 6));
%childRotX = getWord(%childRot,0);
%childRotY = getWord(%childRot,1);
%childRotZ = getWord(%childRot,2);

%childPosOffset = %child.posOffset;
%childRotOffset = %child.rotOffset;

%childScale = %child.scaleFactor; //Gets set when parented
%childScaleX = getWord(%childScale, 0);
%childScaleY = getWord(%childScale, 1);
%childScaleZ = getWord(%childScale, 2);

%scale = %childScaleX*%parentScaleX SPC %childScaleY*%parentScaleY SPC %childScaleZ*%parentScaleZ;

//Begin the awesome code which positions it!
%curtransform = %parent.gettransform();
%u["x"] = getword(%curtransform,3);
%u["y"] = getword(%curtransform,4);
%u["z"] = getword(%curtransform,5);

%angl = getword(%curtransform,6) * -1;
%cos = mcos(%angl);
%sin = msin(%angl);

%a[1,1] = %cos + (%u["x"] * %u["x"] * (1 - %cos));
%a[1,2] = (%u["x"] * %u["y"] * (1 - %cos)) - (%u["z"] * %sin);
%a[1,3] = (%u["x"] * %u["z"] * (1 - %cos)) + (%u["y"] * %sin);

%a[2,1] = (%u["y"] * %u["x"] * (1 - %cos)) + (%u["z"] * %sin);
%a[2,2] = %cos + (%u["y"] * %u["y"] * (1 - %cos));
%a[2,3] = (%u["y"] * %u["z"] * (1 - %cos)) - (%u["x"] * %sin);

%a[3,1] = (%u["z"] * %u["x"] * (1 - %cos)) - (%u["y"] * %sin);
%a[3,2] = (%u["z"] * %u["y"] * (1 - %cos)) + (%u["x"] * %sin);
%a[3,3] = %cos + (%u["z"] * %u["z"] * (1 - %cos));

%pos = getwords(%curtransform,0,3);
%x = getword(%child.posOffset,0);
%y = getword(%child.posOffset,1);
%z = getword(%child.posOffset,2);

%newx = (%a[1,1] * %x) + (%a[1,2] * %y) + (%a[1,3] * %z);
%newy = (%a[2,1] * %x) + (%a[2,2] * %y) + (%a[2,3] * %z);
%newz = (%a[3,1] * %x) + (%a[3,2] * %y) + (%a[3,3] * %z);

%newoffset = %newx SPC %newy SPC %newz;
%pos = vectoradd(%pos, %newoffset);
//Epic code done, back to my code

%rot = vectorAdd(%parentRot, %childRotOffset);

%child.setTransform(%pos SPC eulerToAxis(%rot));
%child.setScale(%scale);
holo_updateChildren(%child); //Update any of the child's children
} else
%parent.unChild(%child);
}
}

function fxDtsBrick::holo_Move(%brick,%type,%str,%client)
{
%holo = %brick.holo;
advVCEDebug("Moving holo:" SPC %holo @ ".");
if(!isObject(%holo))
return;

if(isFunction(filterVariableString)) //VCE support
%str = filterVariableString(%str,%brick,%client,%client.player);

%vec = vectorAdd(%str, "0 0 0"); //Forces it to be a 3d vector

switch(%type)
{
case 0: //Set
%pos = %vec;
case 1: //Move
%pos = vectorAdd(%vec,%holo.getPosition());
}
if(isObject(%holo.parent))
{
%holo.posOffset = vectorSub(%pos, %holo.parent.getPosition()); //Make sure to update the position offset
}

%rot = getWords(%holo.getTransform(),3,6);

%holo.setTransform(%pos SPC %rot);
holo_updateChildren(%holo);
}

function fxDtsBrick::holo_Rotate(%brick,%type,%str,%client)
{
%holo = %brick.holo;
advVCEDebug("Rotating holo:" SPC %holo @ ".");
if(!isObject(%holo))
return;

if(isFunction(filterVariableString)) //VCE support
%str = filterVariableString(%str,%brick,%client,%client.player);

%vec = vectorAdd(%str, "0 0 0"); //Forces it to be a 3d vector

%rot = axisToEuler(getWords(%holo.getTransform(), 3, 6));
switch(%type)
{
case 0: //Set
%rot = %vec;
case 1: //Add
%rot = vectorAdd(%vec,%rot);
}
if(isObject(%holo.parent))
%holo.rotOffset = vectorSub(%rot,axisToEuler(getWords(%holo.parent.getTransform(), 3, 6))); //Make sure to update the rotation offset

%pos = %holo.getPosition();

%holo.setTransform(%pos SPC eulerToAxis(%rot));
holo_updateChildren(%holo);
}
« Last Edit: June 27, 2013, 09:28:17 AM by boodals 2 »

Double post bump.

I also realised that when scaling the parent, the child should be positioned and scaled correctly against the parent, ive tried mathing it, but I cant get it right. This is a lot more complex that I thought it would be..