HPB to Euler (yet again)



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 21/01/2007 at 01:20, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   8.2-10.0 
    Platform:   Windows  ; Mac  ;  Mac OSX  ; 
    Language(s) :     C++  ;

    ---------
    I'm going to frame the question differently in an attempt to arouse the interest of the developers. :)

    We all know that Ken Shoemake has a general solution for converting matrices to Euler angles. But, as admitted in the text itself, the solutions are not unique (there are multiple possible Euler angles for any matrix). This is not useful for my purposes. The results must be consistent with the Euler angle system in place and HPB results in discontinous results!

    The problem is the fact that Cinema 4D uses a hybrid rotation system - namely its version of HPB - so as to avoid gimbal lock. This introduces even more complexity into any solution whereby the HPB rotations are converted to Euler angles (just six possible permutations: XYZ, XZY, YXZ, YZX, ZXY, ZYX). Since HPB rotatons 'jump' to other values nonsequentially to avoid gimbal lock, simple procedural solutions require more information on the system. To date (several years) I have found no comparable system on which to base calculations!

    I have just been considering a tabular solution, but the storage required is ridiculous (over 3GB! even for 1 degree resolutions). Although I know that it is difficult to wrench yourselves from divulging the magical formulas for HPB angle calculations, it would be very helpful to understand them to solve the problem of converting these mysterious angle representations to a non-gimbal-lock-friendly system such as these ordered Euler angles.

    It is funny that Euler to HPB is as simple as rotation matrix concatentation, but vice versa is impossibly difficult - or, should I say, so inconsistent as to be not viable. The key here seems to be knowing when, why, and how HPB swaps values or changes values discontinuously to avoid gimbal lock - these make all standard calculations impossible.

    This conversion problem has caused me much grief. I cannot use any standard Cinema 4D features to advantage while my systems must remain unidirectional and closed to them (IK being the current concern). Now I'm beggging!

    Thank you very much,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 21/01/2007 at 10:56, xxxxxxxx wrote:

    Why not using Quaternions? They allow gimbel-lock free working.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 21/01/2007 at 11:55, xxxxxxxx wrote:

    Because I can't...

    The rotation values are from Poser - and they are ordered Euler angles and must remain that way. Doesn't matter if the C4D angles are converted to quaternions as Shoemake's method still converts to matrices with the non-unique solution to extract the Euler angles (matrix->Euler or quat->matrix->Euler). The reason that they must remain Euler angles is that these values (like all of the others) could be being used to control other values in a master-slave control system. What the values are determines the effect on the slave - any old value won't do.

    There is nothing about gimbal-lock in particular here, it's just the odd way that C4D HPB avoids it that makes conversion considerations difficult. I have yet to chance upon a rotation system that is similar - and with such a generalized name like 'HPB' (used by thousands of different rotation systems), wouldn't know it if I tripped over it. :) Could even be quite a proprietary solution (?).

    Therein lies the problem. A direction in 3D space can be described in many general coordinate systems (Cartesian, Spherical, ...) and with various units (degrees, radians, gradients, ...). On top of that, the way you rotate to get to the 3D direction is even more varied - Euler rotations (24 basic types!), Quaternions, Azimuth/Altitude, Heading-Pitch-Bank, Pitch-Yaw-Roll, Axis-Angle, R-Alpha (spherical), and so on. I need to go from one rotation system (C4D HPB) to another (Poser Euler) in a way that is consistent - or it is useless - since these rotations affect a hierarchical skeleton.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 21/01/2007 at 12:55, xxxxxxxx wrote:

    I am not sure I can follow you here Robert. What do you want to know, how Cinema calculates HPB rotations internally?

    cheers,
    Matthias



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 21/01/2007 at 16:40, xxxxxxxx wrote:

    Basically. :)

    The opposite process (going from Poser to Cinema 4D) involves having a 'rotation order' (as noted) which determines the concatenation order for the rotation matrices. With that, there are two parts to the concatenation - an initial orientation and the actual rotations.

    The code is like this - applies Poser-value rotations from sliders onto the object, sliders storing rotations in degrees:

    // - Modify IPPBase's Rotation  
    case CHAN_TYPE_ROTATE:  
    {  
         Vector     rotV =          obc->GetVector(IPP_ROTATION);  
         switch ((tsf & 0x0000FF00L) >> 8)  
         {  
              case CHAN_SUB_X:     rotV.x =     Rad(value);          break;  
              case CHAN_SUB_Y:     rotV.y =     Rad(value);          break;  
              case CHAN_SUB_Z:     rotV.z =     Rad(-value);     break;  
         }  
         // Store rotation vector  
         obc->SetVector(IPP_ROTATION, rotV);  
         if (root->IsInstanceOf(Opoint))  
         {  
              // orientation - precalculated rotation matrix  
              Matrix     rot =                         obc->GetMatrix(IPP_ORIENTMATRIX);  
              switch (obc->GetLong(IPP_RORDER))  
              {  
                   case ROTORDER_XYZ:     rot = rot * MatrixRotZ(rotV.z) * MatrixRotY(rotV.y) * MatrixRotX(rotV.x);     break;  
                   case ROTORDER_XZY:     rot = rot * MatrixRotY(rotV.y) * MatrixRotZ(rotV.z) * MatrixRotX(rotV.x);     break;  
                   case ROTORDER_YXZ:     rot = rot * MatrixRotZ(rotV.z) * MatrixRotX(rotV.x) * MatrixRotY(rotV.y);     break;  
                   case ROTORDER_YZX:     rot = rot * MatrixRotX(rotV.x) * MatrixRotZ(rotV.z) * MatrixRotY(rotV.y);     break;  
                   case ROTORDER_ZXY:     rot = rot * MatrixRotY(rotV.y) * MatrixRotX(rotV.x) * MatrixRotZ(rotV.z);     break;  
                   case ROTORDER_ZYX:     rot = rot * MatrixRotX(rotV.x) * MatrixRotY(rotV.y) * MatrixRotZ(rotV.z);     break;  
              }  
              obj->SetRot(MatrixToHPB(rot));  
              obj->Message(MSG_UPDATE);  
         }  
         break;  
    }
    

    As you can see, the concatenation is rmat = orientation * rot3 * rot2 * rot1. Then the matrix is converted to HPB and applied to the plugin bone object. This is done to avoid disruption to the matrix in general, noting that this is a local rotation.

    What I would like to do is go the exact opposite direction: HPB to Poser Euler angles. And this must be a consistent conversion - meaning that the slider values fed to set the HPB rotation must come back from HPB as the same values (within whatever small variances will naturally occur).

    The problem is that step where there is finally a matrix which requires matrix decomposition to extract ordered Euler angles. But there is never a singular solution to this - there are multiple solutions (See Graphics Gems IV, pp 224). Don't know how MatrixToHPB() works to retain a workable singular solution, but it does work in the Poser->HPB direction. The main instability occurs when the HPB anti-gimbal-lock mechanism comes into play and the HPB values swap or change radically - this makes Shoemake's results bad and unworkable.

    Any ideas on how to go backwards on the given code?

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 21/01/2007 at 23:00, xxxxxxxx wrote:

    To add: It'd be even better if there was an algorithmic way to go from HPB (Vector hpb = obj->GetRot()) to the Poser Euler angles (obc->SetVector(IPP_ROTATION, rotV)) without matrices at all (!). This is directly because of the problems with matrix decomposition. Concatenation is child's play, decomposition and I've got to call DeLaunay or Chebechev.

    One strategy that I have considered is a sampling method - with (0,0,0) orientations to remove this variable (for now). Start with 0 and 180 sets and divide to 90, 45, 22.5, etc. and hope to see a general conversion algorithm emerge. That's the painful way (as the sampling sets will increase in size as the angles are divided and included into the sampling sets).

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 22/01/2007 at 01:14, xxxxxxxx wrote:

    All the source code for the various conversions used in CINEMA (modules, core and plugins) is in c4d_tools.cpp in the api. So for example, when you call SetRot(hpb) this uses the HPBToMatrix() to cache the hpb matrix.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 22/01/2007 at 02:23, xxxxxxxx wrote:

    Hello everyone,
    although I am quite a newbie in this corner ;) I know about the problem. I tried to code a BHV-Export from C4d to Poser once but the results where not as excepted, although the code was mathematecally correct due to the fact that there ARE always several euler results for a hpb solution (Depeding on the startingrotation the result was any kind of strage yoga;) ). As I am not familiar with c++ I did not take a look at the c4d_tools.cpp yet, but I will try that. (Thanks for the tipp). I would be gratefull if you solve this difficult problem, if you could post your solution here, would be ery helpful.
    Thanks in advance,
    Coffejunkie



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 22/01/2007 at 09:55, xxxxxxxx wrote:

    I might need to see what variances appear in the code as well - I am already aware of c4d_tools.cpp.

    Since I cannot seem to figure a way to know when the user changes rotation (Rotation tool, Attributes Manager, or Coordinates) and distinguish that from animation, my sliders, etc., I think that my conversions will need to be specific to IK (check for Mkinematic). I know about the MSG_MOVE_FINISHED, but 1) this is only sent AFTER the user changes and 2) not availabe pre-R9. Nothing seems to be sent during the rotation changes (to my plugin object's Message()).

    I agree with Coffeejunkie. I spent last night doing the conversion code from David Eberly's PDF "Euler Angle Formulas" - which appears to be a simple derivation on Shoemake. Spaghetti IK chains on my figures. Now, I realized this morning that although the orientation was removed (!matrix), I was still dealing with the regular local matrix. The matrix probably needs to be a pure rotation matrix, so that will need to be accounted for. Still, the figure on which this was being tested has no scaling applied.

    As it was very late, the code will need to be double checked and the transfer process from Eberly's code to mine validated (there might need to be other adjustments). But the IK results seem to be that sometimes the rotation values overshoot, go in the opposite direction, or apply to the incorrect axis. That latter one is typical of the HPB->Euler problem that has continuously plagued my code - rotation axes switching roles indiscriminantly!

    Thank you very much,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 22/01/2007 at 10:59, xxxxxxxx wrote:

    Okay, here's an instance where your anti-gimbal-lock and/or other system oddities (unknown) screw me over (I'm being nice).

    The Body of my figure is the root plugin bone. The Polygon object to which it is parented has a unit matrix. This bone has no orientation (0,0,0) and a YXZ rotation order (this is, what, about as close to C4D's HPB as you can get).

    The following YXZ values are what is set on the sliders. The HPB values are what is shown in the object's Coord. tab. The method that you saw above (rmat = orient * rot3 * rot2 * rot1) is how I go from the YXZ to proper HPB equivalents through the matrix.

    Y   X   Z       H   P   B
    0   0   0       0   0   0
    90   0   0      90   0   0
    90 90   0      90   0 270 (?)
    0 90   0       0 90   0
    90 90 90       0 -90 180 (?)
    180 180 180       0   0 360 (?)
    0 180 180     180   0   0 (?)
    0 180   0     180   0 180 (?)

    And so on. Look, the *results* are equivalent - that is, the resulting rotation of the object is exactly proper. But the values are far from equivalent. Remember that these orders are not juxtaposed in any way - Y~=H, X~=P, Z~=B (supposedly).

    This is what I get going from YXZ to HPB. And I need to get from HPB to YXZ so that it looks like this (should look familiar - I just swapped the columnar sets) :

    H   P   B           Y   X   Z
    0   0   0           0   0   0
    90   0   0          90   0   0
    90   0 270 (?)      90 90   0
    0 90   0           0 90   0
    0 -90 180 (?)      90 90 90
    0   0 360 (?)     180 180 180
    180   0   0 (?)       0 180 180
    180   0 180 (?)       0 180   0

    If this cannot be achieved, I'm screwed. Variable equivalences wreak havoc over a skeletal hierarchy. It can't just go from (0, 180, 0) to (180, 0, 180) and whatever results in between without causing 'strange yoga'.

    For some reason, everyone keeps saying that C4D HPB is plain-old Euler - BS! Then the values above would be identical! Poser's rotations are plain-old Euler and I've used plain-old Euler code (Shoemake and Eberly - PhDs in mathematics both). Explain the differences and I might find a way...

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 22/01/2007 at 14:55, xxxxxxxx wrote:

    Okay, the IK solution is sort of working! There seems to be a 'jitter' as some of the rotation slider values toggle back and forth between positive and negative. Not sure why that is occuring yet, but no more spaghetti! What helped solve this is setting the slider values (allowing for full compliance - including master-slave changes) along with the IPP_ROTATION vector but without applying the values to the object (which would set the local matrix and cause an infinite loop or crash).

    Once the mystery jitter can be resolved, I'll post the code.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 23/01/2007 at 00:24, xxxxxxxx wrote:

    As promised, the code:

    // IPPBase.HPBToPoser -          Convert HPB to Poser ordered-Euler angles  
    //*---------------------------------------------------------------------------*  
    void IPPBase::HPBToPoser()  
    //*---------------------------------------------------------------------------*  
    {  
         BaseTag*          dialTag;  
         BaseContainer*     dialBC;  
         Matrix               mat;  
         Vector               v1, v2, v3;  
         BaseTime          docTime =     document->GetTime();  
         // Switch over to the dials' SetValueIK() method  
         ikEnabled =          TRUE;  
         // Check every body part for IK (Kinematic Tag)  
         for (LONG m = bpindex; m >= FIRST_BPINDEX; --m)  
         {  
              bone =          subBC->GetObjectLink(m, document);  
              if (!bone)     continue;  
              if (!bone->GetTag(Tkinematic))     continue;  
              bbc =          bone->GetDataInstance();  
              if (!bbc)     continue;  
              // Remove Orientation from Rotation Matrix  
              mat =          HPBToMatrix(bone->GetRot()) * !bbc->GetMatrix(IPP_ORIENTMATRIX);  
              v1 =          mat.v1;  
              v2 =          mat.v2;  
              v3 =          mat.v3;  
              // Take into account Rotation Order and extract Euler angles  
              switch (bbc->GetLong(IPP_RORDER))  
              {  
                   case ROTORDER_XYZ:  
                        y =               asin(v1.z);  
                        if (y < pi05)  
                        {  
                             if (y > -pi05)  
                             {  
                                  x = Angle(-v2.z, v3.z);  
                                  z = Angle(-v1.y, v1.x);  
                             }  
                             else  
                             {  
                                  x = -Angle(v2.x, v2.y);  
                                  z =     0.0;  
                             }  
                        }  
                        else  
                        {  
                             x = Angle(v2.x, v2.y);  
                             z =     0.0;  
                        }  
                        break;  
                   case ROTORDER_XZY:  
                        z =               asin(-v1.y);  
                        if (z < pi05)  
                        {  
                             if (z > -pi05)  
                             {  
                                  x = Angle(v3.y, v2.y);  
                                  y = Angle(v1.z, v1.x);  
                             }  
                             else  
                             {  
                                  x = -Angle(-v3.x, v3.z);  
                                  y =     0.0;  
                             }  
                        }  
                        else  
                        {  
                             x = Angle(-v3.x, v3.z);  
                             y =     0.0;  
                        }  
                        break;  
                   case ROTORDER_YXZ:  
                        x =               asin(-v2.z);  
                        if (x < pi05)  
                        {  
                             if (x > -pi05)  
                             {  
                                  y = Angle(v1.z, v3.z);  
                                  z = Angle(v2.x, v2.y);  
                             }  
                             else  
                             {  
                                  y = -Angle(-v1.y, v1.x);  
                                  z =     0.0;  
                             }  
                        }  
                        else  
                        {  
                             y = Angle(-v1.y, v1.x);  
                             z =     0.0;  
                        }  
                        break;  
                   case ROTORDER_YZX:  
                        z =               asin(v2.x);  
                        if (z < pi05)  
                        {  
                             if (z > -pi05)  
                             {  
                                  y = Angle(-v3.x, v1.x);  
                                  x = Angle(-v2.z, v2.y);  
                             }  
                             else  
                             {  
                                  y = -Angle(v3.y, v3.z);  
                                  x =     0.0;  
                             }  
                        }  
                        else  
                        {  
                             y = Angle(v3.y, v3.z);  
                             x =     0.0;  
                        }  
                        break;  
                   case ROTORDER_ZXY:  
                        x =               asin(v3.y);  
                        if (x < pi05)  
                        {  
                             if (x > -pi05)  
                             {  
                                  z = Angle(-v1.y, v2.y);  
                                  y = Angle(-v3.x, v3.z);  
                             }  
                             else  
                             {  
                                  z = -Angle(v1.z, v1.x);  
                                  y =     0.0;  
                             }  
                        }  
                        else  
                        {  
                             z = Angle(v1.z, v1.x);  
                             y =     0.0;  
                        }  
                        break;  
                   case ROTORDER_ZYX:  
                        y =               asin(-v3.x);  
                        if (y < pi05)  
                        {  
                             if (y > -pi05)  
                             {  
                                  z = Angle(v2.x, v1.x);  
                                  x = Angle(v3.y, v3.z);  
                             }  
                             else  
                             {  
                                  z = -Angle(-v1.y, v1.z);  
                                  x =     0.0;  
                             }  
                        }  
                        else  
                        {  
                             z = Angle(-v1.y, v1.z);  
                             x =     0.0;  
                        }  
                        break;  
              }  
              //v1 =     Vector(x,y,z);  
              //GePrint(bbc->GetString(IPP_INTERNALNAME)+" "+VectorToString(v1-bbc->GetVector(IPP_ROTATION), TRUE));  
              bbc->SetVector(IPP_ROTATION, Vector(x,y,z));  
              // Set Dial Sliders to reflect IK rotations  
              dialTag =                         (BaseTag* )bbc->GetLink(IPP_DIAL_ROTX, document);  
              if (dialTag)  
              {  
                   dialBC =                    dialTag->GetDataInstance();  
                   SetKey(document, docTime, bone, dialTag, dialBC, dialBC->GetLong(IPPDIAL_INDEX), ToDeg(x), TRUE);  
              }  
              dialTag =                         (BaseTag* )bbc->GetLink(IPP_DIAL_ROTY, document);  
              if (dialTag)  
              {  
                   dialBC =                    dialTag->GetDataInstance();  
                   SetKey(document, docTime, bone, dialTag, dialBC, dialBC->GetLong(IPPDIAL_INDEX), ToDeg(y), TRUE);  
              }  
              dialTag =                         (BaseTag* )bbc->GetLink(IPP_DIAL_ROTZ, document);  
              if (dialTag)  
              {  
                   dialBC =                    dialTag->GetDataInstance();  
                   SetKey(document, docTime, bone, dialTag, dialBC, dialBC->GetLong(IPPDIAL_INDEX), ToDeg(-z), TRUE);  
              }  
         }  
         // Switch back to the dials' SetValue() method  
         ikEnabled =          FALSE;  
    }  
    

    Some words on this code:

    1. ikEnabled is used to set the method which is used to set the slider values - don't want to actually set the object matrix or the result would be an infinite loop. This signals not to do that.

    2. My skeletal hierarchy is stored in a BaseContainer for fast retrieval (instead of recursive methods over the objects in the OM).

    3. IPP_ORIENTMATRIX is a rotation matrix for the plugin bone initial orientation (Poser). This is independent of the rotations of the bone.

    4. x,y,z are Real variables.

    5. The bottom section outside of the body part loop just sets the IPP_ROTATION vector (stored Poser Euler rotations in radians) and the sliders themselves. Most likely not of consequence for the general solution.

    6. Angle() is a wrapper for atan2() which considers certain special situations:

    // Return Real angle (in radians) whose tangent = y/x  
    //*---------------------------------------------------------------------------*  
    Real Angle(Real y, Real x)  
    //*---------------------------------------------------------------------------*  
    {  
         if (Abs(x) < 0.000001)  
         {  
              if (Abs(y) < 0.000001) return 0.0;  
              if (y > 0.0) return (RAD_90);  
              return (RAD_270);  
         }  
         return atan2(y,x);  
    }  
    

    Thanks!



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 23/01/2007 at 02:59, xxxxxxxx wrote:

    Hello kuroyume0161,
    that will help me very much! I will analyze the code and once I got it fully it will be very useful.
    Thansk a lot for your support,
     
    best wishes,
     
    Coffejunkie



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 29/01/2007 at 12:23, xxxxxxxx wrote:

    Looks that there are still inconsistencies. The three test figures all worked - a first still. Other figures show signs of the rotations varying though.

    Not as much jumping, but a chaotic wandering - probably a feedback situation as slight variances in the HPB to Poser conversion values are then set in the Poser values which are used for further rotations which are converted (etc.).

    Jumping does occur when certain rotation values go above 90d (on two axes).

    One problem here is that the HPBToPoser() conversion sets all three Euler rotations - there is no way around this.

    Gosh, could I use the collaboration of a mathematician in this (how to avoid the wandering) - or something. The jumping points to Cinema 4D-specific information that needs disclosing.

    Robert



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 19/04/2007 at 18:07, xxxxxxxx wrote:

    A final thought:

    This problem is intractable. This is not Cinema 4D's fault, just one where there is no singular solution to extraction of Euler angles from matrices or quaternions. If Cinema 4D didn't have the anti-gimballock mechanism, the Poser and Cinema 4D rotations might always match. But I'm not going to chide Maxon for employing the mechanism. So it is from-scratch wherever rotations are involved.

    I've emailed back and forth with Dave Eberly (yes, the man himself) and he agrees with this assessment.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 20/04/2007 at 10:12, xxxxxxxx wrote:

    Hello everybody,
    it seems to me, that this makes a BVH-export from cinema for other applications impossible. That's a pitty especially as someone less firm with these gimbal-lock-problems or the mathematical theory behind the HPB-to-Euler-Problem of course will wonder why cinema imports this format but does not export it. Wouldn't it be great to have a parameter inside cinema, that gives the user the possebility to choose between HPB or Euler-angles?
    It must be technically possible to make an animation-system work with Euler, poser proves this. So if this ist possible, why not making all cinema work with it? I know this will be a lot of work for Maxons's programmers , as they will have to change the programm in many ways. But it would be great for users.
    Of course then it will not be possible to change between these systems when there are already rotatable objects in a scene, but all the time before this point perhaps?
    Well HPB is a quite clever system, no doubt.
    Just one question to be sure I understood everything right here:
    When I got a poser import form BVH which is set to correct HPB-values on import in cinema and I try to store the original angles of any local bone additional.
    Will I get the problem to, tracking rotational changes in the HPB-system directly to the euler-angles? If I understood everything correct I would think so, just wanted to be sure.
    Because if not, I could export these Euler values back to poser afterwards. Although I would need some startvalues in Euler.
    Best whishes,
    Coffejunkie
    PS: Let's hope for Maxon's inovations on Cinema in future, dreaming is always possible ;)



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 20/04/2007 at 10:51, xxxxxxxx wrote:

    Yes.

    The BVH rotations cause correct HPB values by matrix concatenation of the rotations in proper Euler order. The problem is going the other way - extracting Euler rotation angles from a matrix always has multiple solutions (even if you know the order of the rotations).

    Further, assuming that the HPB rotations can be equivocated back to the same Euler rotations (even knowing the order of rotations) will eventually get you into trouble. The reason is that HPB does some shuffling of rotation values in order to avoid gimbal lock. If all of the HPB rotations are less than 90d, you will more than likely have an exact relationship between HPB and Euler. After that, the relationship most likely will not hold.

    With a small plugin, I rotated a single object through all three axes in six Euler orders (XYZ, XZY, YXZ, YZX, ZXY, ZYX) by increments of 90d (0,90,180,270). The original Euler rotations, the resulting HPB (from matrix concatenation), and the 'best converted' Euler rotations were tabulated. The results were that a large percentage of the Euler rotations (of the same Euler order) produced the same HPB rotations - that is, three/four/five different Euler rotations had the exact same HPB rotation (we're not even talking about conversion back to Euler yet!). At that point, there is no way to know which of the possible Euler rotations resulted in the particular HPB. Which one? Who knows...

    For animation and, especially, CA this is not acceptable. It sort of presents the same problem as MOCCA's Quaternion tag - there, any rotation beyond 180d is going to cause problems as this tag always looks for the shortest rotation path (which will always be 180d or less). So you end up with a completely different rotation result - which is similar to what I experienced when attempting to convert HPB changes back to similar changes in Poser's Euler ordered rotations. 180,0,180 may be equivalent to 0,180,0 in the direction vector but you can imagine how deformations applied according to individual axial rotations will differ between the two.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 23/04/2007 at 02:56, xxxxxxxx wrote:

    How can this result in the same rotation ??
    Y   X   Z       H   P   B
    90 90   0      90   0 270 (?)

    to my understanding
    XYZ should align the local Z axis parallel to world Y axis
    HPB should align the local Z axis parallel to world X axis



  • On 05/02/2017 at 02:45, xxxxxxxx wrote:

    I'm aware that this is a very old post, but 10 years later it is still a problem to get Poser figures to Cinema 4D and animate them there, because of the different rotation system.

    Unfortunately Robert is no longer around and so I wonder, if someone else has more information or if there was already an existing solution discussed, how to map XYZ rotation to HPB.


Log in to reply