Node Rotation and Quaternions

Anyone know of a simplied explanation of how to work with the DzQuat() to set nodes at different angles? I have a script I wrote long ago that adds lights to a scene and sets their rotations at increments of 90 degrees around the Y axis, and to do that defined a new DzQuat() for each light, did a setValue(1, 1.57) for example to get a 90 degree rotation, then used that DzQuat as the parameter for a setLocalRot() function, and all is fine. 

But I'm wondering if you want to get more complex and set, say, multiple rotations on different axes, I can't recall how to set the DzQuat parameters to implement that.

Anyone? 

Comments

  • Are you looking for general hints on how to conceptualize Quaternions themselves as a mathematical construct, or an explanation of the DS API?

  • ebergerlyebergerly Posts: 3,255

    Well I think it's more about how the D|S API is implementing them. For example, there's like 4 implementations of setValue to set quaternion angles and axes, but the descriptions on what they're doing aren't real clear. There's one where you can enter (axis1, axis2, axis3, vec3), where I *think* you can define the which axes you want to set rotation values for, and the vec3 are the rotation radian values. But that doesn't seem to do it. Now I know for example the w part of a quaternion is sin(theta/2) + cos(theta/2), it looks like they're simplifying that for the user in a few ways. 

    Anyway, quaternions are enough to explode your mind anyway, so figuring how D|S implements them is even more challenging. But I've looked all over for some examples of rotation scripts but came up empty. 

    Thanks. 

  • PraxisPraxis Posts: 252
    edited March 2021

    mcasual uses DzQuats a lot.  See e.g.: https://sites.google.com/site/mcasualsdazscripts6/mcjautolimb2015

    Some other mcasual scripts that use DzQuats:

    • mcjApplique
    • mcjBuilderA
    • mcjCollarToShldr
    • mcjMechAnim
    • mcjSlerp

    See also: https://www.daz3d.com/forums/discussion/293/list-of-free-daz-studio-script-and-plugins

     

    Post edited by Praxis on
  • ebergerly said:

    Well I think it's more about how the D|S API is implementing them. For example, there's like 4 implementations of setValue to set quaternion angles and axes, but the descriptions on what they're doing aren't real clear. There's one where you can enter (axis1, axis2, axis3, vec3), where I *think* you can define the which axes you want to set rotation values for, and the vec3 are the rotation radian values. But that doesn't seem to do it. Now I know for example the w part of a quaternion is sin(theta/2) + cos(theta/2), it looks like they're simplifying that for the user in a few ways. 

    Anyway, quaternions are enough to explode your mind anyway, so figuring how D|S implements them is even more challenging. But I've looked all over for some examples of rotation scripts but came up empty. 

    Thanks. 

    Yeah, I'm with you. Quaternions are difficult to wrap one's head around in the first place, and then there's the Daz API's documentation to contend with.

    But one thing: Q(x,y,x,w) describes a rotation about a single vector v(x,y,z) by an angle theta where cos(theta/2) is w. Where are you getting sin + cos from? As far as I know, it is just cos(theta/2).

  • ebergerlyebergerly Posts: 3,255

    Thanks for the mcasual references. I looked thru his long list of scripts a few times and didn't see anything that seemed obviously pertinent, so this helps. At some point I'll take a few hours and try to reverse engineer his stuff.

    I was hoping someone had a simple explanation of how the D|S script implementations work, or a description of how to implement the setValue (or other) tools to merely define node rotations.

    Thanks. 

  • ebergerlyebergerly Posts: 3,255

    Ahh, okay. I think I've found the magic bullet. laugh

    To find out how D|S handles the quaternions, I'm accessing the "get" methods of the quaternion. And if you just print those out, you can orient your object in the scene however you want, then run the "get" script and it prints out whatever rotation values you want. And that basically spits out the internal values it uses for each orientation. So if you want your script to orient an object with a certain rotation, put the object in that rotation in the scene, run the script, and it should tell you what parameters to use in the script. 

    So you can use the following get's, and just a print statement for each result, and it nicely prints and formats even vec3's with no special formatting. Cool. 

    Number getAngleOfRotation ()
    DzVec3 getAxisOfRotation ()
    DzVec3 getValue Number axis1, Number axis2, Number axis3 )
    DzVec3 getXAxis ()
    DzVec3 getYAxis ()
    DzVec3 getZAxis ()
  • ebergerlyebergerly Posts: 3,255

    Okay, and here's a bit of code I came up with to do just that. Select the object in the scene, run the script, and it will print out all the values
     

    function GetRotation(){	node = Scene.getSelectedNode(0)	print("Node Name = " + node.getLabel());	quat = new DzQuat();	quat = node.getLocalRot();	print("Angle of Rotation = " + quat.getAngleOfRotation());	print("Axis of Rotation = " + quat.getAxisOfRotation());	print("Value = " + quat.getValue(0,1,2));	print("X Axis = " + quat.getXAxis());	print("Y Axis = " + quat.getYAxis());	print("Z Axis = " + quat.getZAxis()); }

    And the output results are below. This is for a light that has 0 rotation in all axes except for X, which has a 10 degree rotation (0.17 radians).

     

    LightScript.JPG
    314 x 170 - 17K
  • ebergerlyebergerly Posts: 3,255

    Okay, well that wasn't too bad. My goal was to update a script I had to add 4 distant lights at 90 degree increments around the Y axis so the scene is lit from all sides. That's what I did previously, and it was pretty easy (as described in my first post here). But the challenge was to get each of those also rotated on the X axis so the lights are coming from slightly above. 

    So what I did is add the 4 lights manually, place them at 90 degree increments, then manually rotate each by 30 degrees on the local X axis. And based on the script I just posted, I extracted the "Angle of Rotation" and "Axis of Rotation" values for each light to see how D|S views those rotations, and set the quaternions for each of the 4 lights based on those values. So the resulting code includes the following:

    	angVec = new DzVec3(.25,-.935,-.25);	angVec1 = new DzVec3(1,0,0);	angVec2 = new DzVec3(0.233, 0.944,0.233);	angVec3 = new DzVec3(0,-.965, -.26);	quat1.setValue(angVec,1.637);	quat2.setValue(angVec1,0.524);	quat3.setValue(angVec2,1.628);	quat4.setValue(angVec3,3.14);

     

    A bit of a challenge since you need to approach it from the non-intuitive quaternion view, but I'm guessing there's also a way to enter the much easier and intuitive Euler angles. But that's for another day, since this solves the problem.

  • PraxisPraxis Posts: 252

    ebergerly said:

    Thanks for the mcasual references. I looked thru his long list of scripts a few times and didn't see anything that seemed obviously pertinent, so this helps. At some point I'll take a few hours and try to reverse engineer his stuff.

    I was hoping someone had a simple explanation of how the D|S script implementations work, or a description of how to implement the setValue (or other) tools to merely define node rotations.

    Thanks. 

    One of the many amazing things about mcasual's scripts is that they don't need to be "reverse engineered" - they are un-encrypted .dsa files.

    If you download the .zip file from this page https://sites.google.com/site/mcasualsdazscripts6/mcjautolimb2015 and extract the .dsa from it, you will find it contains a function spread( t, a, vecTarget, limbRoot ) that shows how to use DzQuats to apply a rotation of a radians about the axis defined by vecTarget to the Node limbRoot at time t (if I've understood it correctly).

    Note: His pages contain convenient links to donate to him via PayPal.

     

     

  • ebergerlyebergerly Posts: 3,255

    Yeah, he's pretty incredible. And you're right, around line 500 in a 658 line script there's the references to DzQuat's. I'm impressed you found it. And he even added some documentation. As a developer for many years I tend to avoid others' code if possible since it's often (almost always) undocumented and difficult/impossible to understand and reverse engineer. But he's definitely an exception. Good stuff. 

    I already came up with a solution for my particular application, but I'll definitely go thru his "spread" code and make sure I understand the DzQuat related stuff. Thanks again. 

  • Did you see this constructor?

    DzQuat( DzRotationOrder order, DzVec3 angles )

     

  • ebergerlyebergerly Posts: 3,255

    By the way, what I'm referring to when I say "reverse engineer" is trying to figure what the script, and each line of code, is doing. And in this case it's made quite a bit more difficult by using the generic "var" to declare the variables, rather than being explicit and defining exactly what type of variable they are (such as vec3, etc.). Unfortunately, for someone trying to understand the script who isn't familiar with it it now requires a whole lot of reverse engineering to figure out what's going on. "Is that variable a number, or a vec3, or a DzQuat", and so on. 

  • ebergerlyebergerly Posts: 3,255

    Also, for those Blender users out there who might also be trying to grapple with the Quaternion vs. Euler dilemma, keep in mind that Blender provides a way to see both Quaternion and Euler rotation values. In object mode, select the object, hit "n" to bring up the properties sidebar panel, then under the Item tab where it shows translation, rotation, and scale, there's a dropdown for Euler or Quaternion. So you can manipulate the object then jump between Quat and Euler and see what the relative values are. Of course that doesn't address how they're implemented in D|S scripting, but it should help. 

  • ebergerlyebergerly Posts: 3,255

    Okay, for those who might be interested into diving a bit deeper down the quaternion rabbit hole...

    I tweaked my script to extract the w,x,y,z components of the selected object's quaternion in D|S. And it gives the normalized (-1 to 1) values. Now what I can do is compare those with the values shown in Blender for a similar object, using Blender's quaternion display (also normalized). Which means you can play around with an object's rotation in Blender and see directly what the Euler (XYZ) and Quaternion (w,x,y,z) values are for any rotation. And you can compare those with each other, and with what you get in D|S using this script. Very cool. 

    And allowing for the fact that D|S and Blender have different axes (arrrggghhhh...) you'll see that the values correspond to each other very well. Which means D|S is using normalized values, as is Blender. 

    Now the challenge is there's an absolute ton of math going on in all of this, but at least you can play around with your object in Blender in realtime and watch the Euler and Quaternions change in real time. And that certainly should help to provide a much better intuitive understanding of how quaternions are working. 

    function GetRotation(){	node = Scene.getSelectedNode(0)	print("Node Name = " + node.getLabel());		quat = new DzQuat();	quat = node.getLocalRot();		print("Angle of Rotation = " + quat.getAngleOfRotation());	print("Axis of Rotation = " + quat.getAxisOfRotation());	print("Value = " + quat.getValue(1,1,1));	print("X Axis = " + quat.getXAxis());	print("Y Axis = " + quat.getYAxis());	print("Z Axis = " + quat.getZAxis());		print("Quat w = " + quat.w);	print("Quat x = " + quat.x);	print("Quat y = " + quat.y);	print("Quat z = " + quat.z);	}

     

  • ebergerlyebergerly Posts: 3,255

    And one more thing that might help (it helped me a lot...)

    I saw someone describe quaternions as this: each of the 4 values represent a "weight" for a different orientation of the object. When you load the object with zero rotations of any sort, the quaternion is (1,0,0,0) for (w,x,y,z). And each of the values for those numbers represent a weight for those orientations. So if you crank up the x to 1 it means it's flipped totally on the x axis, and a 1 for y (0,0,1,0) means it's flipped totally on the y axis, and so on. And values below that give different weights to that particular rotation. 

    As you can see if you play around with this in Blender and watch the quaternions, as you rotate the object in different ways it's adding more weight to some of the w,x,y,z values and less to others. So x,y,z aren't really a vector or an axis, they're just orientation weights, same as the w value. 

    Or something like that...

  • ebergerlyebergerly Posts: 3,255

    And even deeper down the rabbit hole...

    A quick and easy way to get a better handle on this quaternion stuff is to break open Excel (or your favorite spreadsheet), and enter some simple equations that allow you to take the intuitive Euler XYZ values and convert to quaternions. 

    Let's say you have a point P with an XYZ vector location, and a separate XYZ vector V = v1i + v2j + v3k describing an axis the point will rotate around. And a value theta that describes the angle that P will rotate around v. 

    The quaternion "h" for this is:

    quat  h = a + bi + cj + dk

    where

    a = w = cos(theta/2)

    b = x = v1*sin(theta/2)

    c = y = v2*sin(theta/2)

    d = z = v3*sin(theta/2)

    So you just take the axis of rotation and the angle and you can spit out the quaternion. And this also shows why, when you load an object into Blender with rotation of (0,0,0) you get a w value in the quaternion of 1, since cost(0/2) = 1. 

    Of course the rabbit hole is much deeper, but at least now we have at least 3 methods we can use to help understand what's going on.

     

Sign In or Register to comment.