Jump to content

Blender To HKX Development Thread


Recommended Posts

I'm working on a script that generates HKX XML from Blender animations. I've taken the logic from Anton's scripts for Skyrim LE, and I'm extracting the pose information as translation vector/quaternion/scaling vector sets and building the animation data following a guide published recently on Nexus forums.

 

I can generate the XML fine. HKXPack packs the result without complaint. But when I try and link it to a pose and an idle, all I get is a T-Pose. I also tried to set my test pose up as a furniture animation, but when I try and generate metadata, the CK crashes.

 

I don't think it's the animation data. I think if it was, I'd see weird poses rather than the t-pose. Least I hope so, since working out where the calculations are wrong might be a bit harder.

 

If I can make this work, I'll release the script, and we'll have an alternative to animating with Max. Before that can happen however, I could use some help working out where I'm going wrong here.

 

I'm attaching the output generated XML, as well as the .blend file and script used to generate it.

 

Any help very much appreciated.

 

[edit]

 

Removed the downloads since A) they're hopelessly out of date and B) no one had looked at them anyway. Renamed the thread while I'm at it.

Link to comment

OK. A good night's sleep can work wonders.

 

First thing I notice: the HCT stack on Max lets you swap modules in and out. In particular, I can swap out the Spline Compression module. If I do that, it generates hkaInterleavedUncompressedAnimation files, which is what I'm trying for here.

 

More to the point, I get a file that lets me compare like for like.

 

Firstly the HCT generated code orders its sections a little differently. Probably that's not significant, XML being what it is.

 

Secondly, I'm not at all convinced the # numbers referencing the various blocks are correct in my version. I copied these blindly from example text, and I'm not at all sure they're right.

 

Thirdly, the HCT prints those animation tuples at three to a line. I don't need to jam everything on one line which is going to make debugging this stuff a lot easier.

 

Lastly, and this is the best, there are a couple of sections in the HCT file that just don't appear at all in the reference material I was using:

 

        <hkobject class="hkaDefaultAnimatedReferenceFrame" name="#94" signature="0x60f8e0b8">
            <!-- memSizeAndRefCount SERIALIZE_IGNORED -->
            <!-- frameType SERIALIZE_IGNORED -->
            <hkparam name="up">(0.0 0.0 1.0 0.0)</hkparam>
            <hkparam name="forward">(0.0 1.0 0.0 0.0)</hkparam>
            <hkparam name="duration">16.66666603088379</hkparam>
            <hkparam name="referenceFrameSamples" numelements="2">(0.0 0.0 0.0 0.0) (0.0 0.0 0.0 0.0)</hkparam>
        </hkobject>

 

That probably looks familiar to anyone who has looked at nif animation controllers. So the absence of that section is probably mt test pose just generates a t-pose. As opposed I suspect to crashing the game on account of the section pointers not pointing at anything.

 

I'll make those changes and see where it gets me.

 

[edit]

 

No, I tell a lie, my #numbers were consistent after all. Oh well, still plenty to be doing.

 

Link to comment

OK. Follow my reasoning here.

 

  1. The engine must be able to cope with animations that use fewer bones than the installed skeleton. Otherwise vanilla animations would crash around the extended ones we use.
  2. There are too many bones, and too many transforms, for me to do any meaningful analysis on the numbers
  3. Solution: delete some bones!

That needn't be as drastic as it sounds. I can edit the bone list in Max so HCT only takes account of root and COM. Liekwise, I can code a filter into my script to discard bones other than those two.

 

I chose root because the engine seems to need it, and because it's the literal root of the skeleton, and COM because I can move the whole body that way. Like straight up. That way I get a t-pose floating in mid air for the length of the pose.

 

Anyway, it works for Max.

 

image.png

 

So, if I can do that in Blender, I can compare the XML where each file will have 12 tuples of position data. And the only one that ought t count for anything is the translation vector that goes straight up. This way, I will know what the values should be, I'll know what I'm actually getting, and I'll have access to the raw data.

 

If I  can't work it out from that lot, I'm going to be very disappointed in myself.

Link to comment

OK: this is the blender version. Still not working, but it's not just a crumpled ball. Interestingly, that would probably be correct if the model was rotated 90 degrees. Which is itself interesting because there are some 0.707107 values in the quaternions for the blender output that don't show in the HCT version. That value is the sine (and cosine) of 45 degrees ... could my problems be down to my blender model using the wrong axes?

 

One way to find out :)

 

[edit]

 

I  imported a Fusion Girl body from an obj file I used for tattoo work. I know this one is oriented correctly.

 

image.png

 

So that's looking very probable. Let's try aligning that test model and try again.

Link to comment

Here's a comparison of the numbers for the two methods:

 

Blender
        Frame 1 Root
                Translation     ( 0.0  0.0 -0.0  0.0)
                Rotation        ( 0.0 -1.0  0.0 -0.0)
                Scale           ( 1.0  1.0  1.0  0.0)
        Frame 2 Root
                Translation     ( 0.0  0.0 -0.0  0.0)
                Rotation        ( 0.0 -1.0  0.0 -0.0)
                Scale           ( 1.0  1.0  1.0  0.0)

        Frame 1 COM
                Translation     ( 0.0 17.0 -0.0  0.0)
                Rotation        ( 1.0 -0.0 -0.0 -0.0)
                Scale           ( 1.0  1.0  1.0  0.0)
        Frame 2 COM
                Translation     ( 0.0 17.0 -0.0  0.0)
                Rotation        ( 1.0 -0.0 -0.0 -0.0)
                Scale           ( 1.0  1.0  1.0  0.0)

HCT:
        Frame 1 Root
                Translation     (0.0 0.0 0.0 0.0)
                Rotation        (0.0 0.0 0.0 1.0)
                Scale           (1.0 1.0 1.0 0.0)
        Frame 2 Root
                Translation     (0.0 0.0 0.0 0.0)
                Rotation        (0.0 0.0 0.0 1.0)
                Scale           (1.0 1.0 1.0 0.0)
        Frame 1 COM
                Translation     (0.0 0.0 140.8 0.0)
                Rotation        (0.0 -0.707106 0.0 0.707106)
                Scale           (1.0 1.0 1.0 0.0)
        Frame 2 COM
                Translation     (0.0 0.0 140.8 0.0)
                Rotation        (0.0 -0.707106 0.0 0.707106)
                Scale           (1.0 1.0 1.0 0.0)

 

 

First of all: in both cases, the two frames are identical. That's OK - I set them up like that because I read somewhere that this was what you should do if making a pose. No idea if it's needed, but it does mean we can discard one set of numbers. It also means I'm not introducing any weird side effects that mutate the numbers between frames. So that's good.

 

Root translation is the same in both cases barring some negative zeros which I suspect I can live with. Root rotation has different numbers ... and I think I need to know what the numbers mean here. I read up on quaternions years ago, but I've forgotten most of what I knew, and even if I remember right, I'm not sure of the order of the fields. T%hat said, the Blender UI gives the rotation as WXYZ with root and COM both as 1,0,0,0. That matches COM, but not root. And HCT (which we know has it right since it works) has 0,0,0,1. Which makes me wonder if HkX XML might not expect the rotation as XYZW. I can experiment.

 

The COM translation is the next surprise. The height of the movement is approximately equal in Max and Blender, but the numbers here are 17 vs 140. Which, allowing for one model being a few units higher than the other, looks like the old Blender out-by-a-factor-of-10 gotcha rearing its head once again. I can worry about that once I get the rotation right.

 

Speaking of which, the rotations are completely different. The HCT COM has some Sin54 and Cos45 numbers in there. Which makes me wonder if the F4AK rig doesn't have the COM bone rotated by 90 degrees. It might explain why so many of my attempts to load an animation onto the skeleton there end up with the body facing one way and the torso twisted 90 degrees clockwise.

 

First thing is to find out why the root bone rotation is different. There are a ton of special cases in Anton's code that I've largely copied since I didn't fully understand them, but some of which I suspect are not needed in this context. There's a 90 degree compensation for somthing in the NIF format for a start that probably doesn't belong there. It might also be instructive to paste some or all of the HCL animation data into the Blender XML and see if that gets the same result as the Max exported animation. It should - the files are close enough otherwise that I don't think there's anything left to go wrong. But it might be nice to confirm that.

 

Oh yeah, and one more test: the idles still play so the character lies on their side. So that hasn't changed.

 

Link to comment

Oh-kaaay! There's a fun site called Quaternions.online that helps visual the rotations, and also converts them into Euler angles. After some messing around, I've reset the rotation on all bones and my root bone now reports 0,0,0,0 rotation in blender UI. I've cleared any armature transformations, deleted all keyframes and applied any transformations on the body or armature in Object Mode. So my root bone should now have a rotation 0f 0,0,0,0

 

However, when I run hkx_extractor, i get  <Quaternion (w=0.0000, x=-0.0000, y=-0.7071, z=0.7071)>.

 

Why is that? Well, I'm guessing that the 0,0,0,0 is the rotation relative to where the bone has been placed in the editor, but the  value the script reports is showing the absolute rotation or the bone.

 

So I need to get my root bone to have the same rotation in absolute terms as that reported by havok. OK. Let's try that. This is what Quaternions Online says I'm doing with by bone at the moment (wait, let me rephrase that ,,,)

 

image.png

 

This is the HCT rotation using the same site:

 

image.png

 

So it looks like I have an unwanted rotation of -90 around the X-Axis.

 

Link to comment

All right. I rotated the whole mess through 90 around X ... and then when I applied that, Blender immediately applied a rotation of its own: 180 degrees around X, so my model is on her back rather than her front. That actually gets me to the same place in Euler terms - it's a net rotation of -180 around Z, as opposed to HCT's rotation of +180 around Z.

 

So I think I'll call that a win.

 

Now I need to sort out COM. Con starts our reporting the same rotation as root (which is now 0,0,0,1, oddly enough - what happened the the minius sign?) Anyway,. that doesn't last. Blender is reporting the total rotation of the bone, including all rotation due to parent bones. So the first thing we do is apply the inverse rotation of the parent. In this case, the parent rotation is the same, so that modifies the COM bone to a 1,0,0,0 rotation, which in Quaretnion terms is no rotation at all.

 

The trouble is, it needs to be like this:

 

image.png

 

So I need to flip COM on the X-Axis, and rotate it -90 degrees around the Y. And I need to be careful to make the changes in edit or object mode, so they don't get lost the first time I Clear All on the armature.

 

Also, I need to be careful not to rotate the scene root when I do the rotation. This seems to be an artifact inherited from the nif import, and I can't help wondering if shouldn't perhaps be the missing root bone, at least in Blender terms

Link to comment

Yeah, there was a comment in Anton's original script about that. I assumed that the adjustment was needed because he was baking the animation into the nif itself. Maybe it's necessary in the general case.

Link to comment

OK. It's becoming aparent that I'm never going to fix this thing by flailing around at random, so let's think about this.

 

Assuming the Quats are WXYZ (and experiments back that up) then the root bone is pointing flat on the floor. But the COM bone quat describes a rotation that would take that and align  it on the Z-Axis. So that makes sense, since without that rotation I get idles flat on the floor.

 

The trouble is, I need that rotation after the root bone's rotation has been removed from the bone. So I need to know what value I need so that when the parent bone is corrected for, the result is going to be 0, -0.707, 0, 0.707.

 

Luckily,  Blender's script window has a built-in Python interpreter. So I can construct the final quaternion I need like this:

 

 >>> final = Quaternion([0,-0.707107, 0, 0.707107])

 

And the position of the root bone like this:

 

>>> root = Quaternion([0,-0, 0, 1])
>>> root
Quaternion((0.0, 0.0, 0.0, 1.0))

 

So if I multiply them together, the result should be the value that I need.

 

Let me put a  bit of structure around this ...

Link to comment

My calculations were all getting a bit big for the diddy little python window in Blender, so I made a proper script of it.

 

import bpy
import mathutils
from mathutils import Quaternion

#
# final rotation of COM as gleaned form HKXUnpack
#
com_final = Quaternion([0,-0.707107, 0, 0.707107])
root = Quaternion([0,-0, 0, 1])

#
# we need that value that when given the inverse rotation of root, stands out actor upright
# luckily we already know what the final value is. So to get the intermediate, we
# just need to rotate by root's non-inverse (which is to say normal) rotation.
#
# Obvious in hindsight.
#
com_initial = com_final.copy()
com_initial.rotate(root)

# prove it by inverting root and undoing the rotation
root_invert = root.copy()
root_invert.invert()

print("Needed COM rotation    : {}".format(com_final))
print("Initial Root Rotation  : {}".format(root))
print("Multiply COM final by the root rotation to get the initial value")
print("Initial COM rotation   : {}".format(com_initial))
print("We test this by generating the inverted root rotation matrix, and rotating by that")
print("... just like in the exporter")
print("Inverted root rotation : {}".format(root_invert))

print("lastly we copy COM inital, and then apply the inverted root roration.")
print("The result should match COM Final")
print("\n\n")
com_test = com_initial.copy()
com_test.rotate(root_invert)
print("COM Initial: {}".format(com_initial))
print("COM Test   : {}".format(com_test))
print("COM Final  : {}".format(com_final))

 

The prints go to the console window:

 



COM Final  : <Quaternion (w=0.0000, x=-0.7071, y=0.0000, z=0.7071)>
COM Initial: <Quaternion (w=0.7071, x=0.0000, y=0.7071, z=-0.0000)>
COM Test   : <Quaternion (w=0.0000, x=0.7071, y=0.0000, z=-0.7071)>
Needed COM rotation    : <Quaternion (w=0.0000, x=-0.7071, y=0.0000, z=0.7071)>
Initial Root Rotation  : <Quaternion (w=0.0000, x=0.0000, y=0.0000, z=1.0000)>
Multiply COM final by the root rotation to get the initial value
Initial COM rotation   : <Quaternion (w=0.7071, x=0.0000, y=0.7071, z=-0.0000)>
We test this by generating the inverted root rotation matrix, and rotating by that
... just like in the exporter
Inverted root rotation : <Quaternion (w=0.0000, x=-0.0000, y=-0.0000, z=-1.0000)>
lastly we copy COM inital, and then apply the inverted root roration.
The result should match COM Final



COM Initial: <Quaternion (w=0.7071, x=0.0000, y=0.7071, z=-0.0000)>
COM Test   : <Quaternion (w=0.0000, x=0.7071, y=0.0000, z=-0.7071)>
COM Final  : <Quaternion (w=0.0000, x=-0.7071, y=0.0000, z=0.7071)>

 

And they don't match! Arrrgh!

 

Still, at least I know about it now, and not after packing the animation, copying it over, starting up Fo4 and watching my test raider lying on her side, yet again.

 

Then again, there are only a couple of minus signs different. With Quaternions, that could mean we're rotation on the wrong axis altogether, or it could mean we overshot in our rotation by a millionth of a degree, mainly because 0.707107 is not the exact value of 1/sqrt(2).

 

Let's see what Quaternions Online thinks.

Link to comment

Well, as it happens ... this is the rotation HCT uses

 

image.png

 

And this is the one my script generated.

 

image.png

 

So they both get to the same place, with the actor the same way up at the end. They just rotate in different directions to get there.

 

OK. I'll take it. The next thing to do is to set the initial COM rotation to match COM Initial, above. And I think the best way to do that, and to be sure I have it right, is with the python interpreter window again. Though knowing me, it'll probably end up being a script.

 

But first, tea time and some telly, methinks. it's been a busy day.

 

Link to comment

Well, I said I'd make a script of it.

 

import bpy
from mathutils import Quaternion

#
# force edit mode
#
bpy.ops.object.mode_set(mode='EDIT')
edit_b = bpy.context.object.data.edit_bones

#
# this is the (editable) bone we want to change
#
bone = edit_b['NPC Root [Root]']

#
# we need its inverse matrix
#
inverse = bone.matrix.copy()
inverse.invert()

print("before: {}".format(bone.matrix.to_quaternion()))

bone.transform(inverse)

print("after: {}".format(bone.matrix.to_quaternion()))

 

long winder way of forcing any bone into Quaternion([1,0.0,0]) alignment. Which for bones, apparently, means lying on the Y-Axis, facing positive. As opposed to Quaterions Online, which tells me it ought to be facing along the X-Axis. Which explains why I've had trouble trying to line bones up based on the Q-O visualizations. Oh well...

 

Next up, I need to rotate COM to match the alignment used by HCT. Not an immediate problem since I can use the code above to zero the bone, and then just rotate it the desired position. More problematic is that I need to take the rest of the skeleton with me. This wouldn't be a problem in Pose Mode, but I need to do this in Edit Mode, and that means the bone will move on its own.

 

There are options to transform the entire armature, (and then transform root back) or if all else fails I can calculate the relative rotation needed by COM and iterate over the skeleton, rotating each bone in turn, but that sounds tedious beyond belief. So initially, I'm just going to work out what direction the bone needs to point in. Once I have that, I should be able to rotate the amature by hand.

 

So that's the plan.

 

9 hours ago, Bad Dog said:

Nice work. Have some cake. ?

 

 

Thank you :)

Link to comment

So, apparently, what I needed was something like this:

 

image.png

 

 

Big bone is COM, little one is Root, the other one is Anton's Controller_BASE, which I'm still not convinced I need, but it was convenient to test this on his dummy.blend.

 

it seems all i needed was for COM to point away from ROOT. Although that still leaves four possible rotations, depending on how the COM bone is rotated around the z axis. It's a pity we can't paint one side of the bone blue, or something.

 

Of course, there's always the option to transform the armature as a whole. And that should get around one side effect I've been seeing, which is to translate the bone to the World Origin. (I can fix that in code easy enough, but translating the armature might be simpler).

Link to comment

Well, what I was hoping for was something like this:

 

image.png

 

What I got was this:

 

image.png

 

Not my most stunningly successful foray ever, I feel.

 

Still, it's not a crumpled ball, and it's not lying on its side, so if not actual progress, it's definitely progress-adjacent.

 

Link to comment

I tried a few more things, but it's again becoming apparent that trying to position these things by hand just isn't going to work. Therefore let's transform the armature.

 

Quick Tip for anyone learning to script Blender with Python: the interactive Python window is your friend. The autocomplete in the interactive Python Window thinks particularly highly of you.

 

image.png

 

Basically, you can type partial object names and then hit TAB and get a list of everything matching. Tab on a function name with an open bracket and you'll get information about arguments, return types and even what it does. (And for those of you who just said "Yeah, but IntelliSense..." ?)

 

Anyhoo, that's the new plan. Transform the entire skeleton so it faces along the direction we need, and then rotate the root bone back. Of course, that's assuming that the COM bone isn't at some unexpected angle compared to the armature as a whole. And for that matter, if armatures can be rotated, it might be an idea to see what angle my current one is at, since I suspect it's been rotated more than once . Which might explain a few of the odder result I've been getting, thinking about it...

Link to comment

I said I'd probably make a script out of it...

 

Spoiler

import bpy
from mathutils import Quaternion

#
# we want com to be at (0.0 -0.707106 0.0 0.707106)
#
# say com is currently at WXYZ
#
# we need the quat Q, such that WXYZ + Q =  (0.0 -0.707106 0.0 0.707106)
#
# Therefore
#
# Q = WXYZ -  (0.0 -0.707106 0.0 0.707106)
#
# Then, since I haven't actually rotated com yet, I can rotate the armature by Q
# and then fix the root bone.
#
#

print("\n")
print("********")
print("******** THE FUN STARTS HERE!!")
print("********")

#
# edit mode ON
#
bpy.ops.object.mode_set(mode='EDIT')

#
# this is where we need to go
#
end_q = Quaternion([0.0, -0.707106, 0.0, 0.707106])

#
# get COM
#
com = bpy.context.object.data.edit_bones["COM"]


#
# this is where we are
#
start_matrix = com.matrix.copy()
start_q = start_matrix.to_quaternion().copy()
print("Start rotation: {}".format(start_q))

#
# end - start gives us the rotation we need
#
# I thought I'd need to mess about with inverse rotateions
#
invert_matrix = start_matrix.copy()
invert_matrix.invert()
print("Inverted COM Matrix:")
print(invert_matrix)

delta_q = start_q.rotation_difference(end_q)
print("Delta Q: {}".format(delta_q))

test_q = start_q @ delta_q
print("these should be equal:")
print(test_q)
print(end_q)

 

 

 

Output is:

 

Spoiler

********
******** THE FUN STARTS HERE!!
********
Start rotation: <Quaternion (w=0.4384, x=0.1369, y=0.0000, z=0.8883)>
Inverted COM Matrix:
<Matrix 4x4 (-0.5780,  0.7789,  0.2432, -0.0000)
            (-0.7789, -0.6155,  0.1200,  0.0000)
            ( 0.2432, -0.1200,  0.9625, -0.0000)
            (-0.0000,  0.0000, -0.0000,  1.0000)>
Delta Q: <Quaternion (w=0.5313, x=-0.3100, y=0.7249, z=0.3100)>
these should be equal:
<Quaternion (w=0.0000, x=-0.7071, y=0.0000, z=0.7071)>
<Quaternion (w=0.0000, x=-0.7071, y=0.0000, z=0.7071)>

 

 

Feel free to tell me I have a strange idea of what constitutes fun.

 

That just calculates the delta rotation needed. This one just wouldn't work however I tried, until I finally found the proper google terms. Turns multiplying by an inverted quat is not the way to subtract the buggers, and you should in fact use the rotation_difference() function. Which explains a certain amount.

 

Next thing is to rotate the armature, now that I know I know how to reckon the delta. And probably go back and fix that last script which wrongly calculates the angle difference.

 

I'm beginning to feel as if I know what I'm doing. This is frequently a Bad Sign :)

 

Link to comment
48 minutes ago, DocClox said:

This is frequently a Bad Sign

 

And so it proved to be. I thought, just to be thorough, I'd apply that delta to the COM bone and check the rotation of the resulting position.

 

Well, it didn't work, did it? So back to square one I thought. Anyway, wasn't as b ad as I'd thought: I needed to take the end position and subtract the start. Which is kind of obvious, really. Only it didn't work when I first tried it, so I reversed the order and it seemed to, so I didn't dig any deeper. Anyway, not only does the revised version work properly, it also avoids the minus sign on the resulting quat (which I didn't notice before) probably caused by having to rotate the long way around to get to the desired point.

 

Time to try this out for real.

Link to comment
13 hours ago, TheBottomhoodofSteel said:

If this works out, let me know

Also just in any case give you 100% permission to convert the ZeX rigs and Rig TXT over to Blender (and upload those).

 

I'll talk to you about that once things are working a little better.

 

Speaking of getting better...

 

image.png

 

She's actually upright now! I kept trying to lie her down, thinking that was the orientation the engine needed. Turns out, I wasn't taking into account that Qaternions Online uses different axes to Fallout/Havok. So eventually, I gave up trying to set the Armature in pose mode and just wrote the needed rotation directly onto the Pose Bones. Turns out all she needed was to face in a different direction.

 

Still not there, however. She's sunk halfway into the floor now, and the further I raise the COM bone, the further away the pose moves her from me. What's really odd is that her collision is still where she was when I applied the pose. If I walk towards her in that scene, she moves away from be because I'm pushing her collision around. Probably, I'm thinking, because I'm only exporting two bones: Root and COM.  There's a CharacterBumper bone in the skeleton that's not linked to anything, and I suspect it's still sitting where she started out.

 

Doesn't explain why she gets displaced away rather than up though. Hmm...

 


Frame 1, Bone 1 - Root

raw translation: <Vector (-0.0000, -0.0000, 0.0000)>
raw rotation: <Quaternion (w=0.7071, x=0.0000, y=0.0000, z=0.7071)>
bone  = <Vector (-0.0000, -0.0000, 0.0000)>
(-9.094946475628196e-13, -6.911788849595091e-19, 3.2526065174565133e-19)
scale: (1.0, 1.0, 1.0, 0.0)

Frame 1, Bone 7 - COM

raw translation: <Vector (-0.0000, -0.0000, 210.7344)>
raw rotation: <Quaternion (w=0.5000, x=0.5000, y=-0.5000, z=-0.5000)>
translation after subtracting parent: <Vector (-0.0000, -0.0000, 210.7344)>
translation after reverting parent rotation: <Vector (-0.0000, 0.0000, 210.7344)>
bone  = <Vector (-0.0000, 0.0000, 210.7344)>
(-4.7946636186679825e-05, 2.440339812892489e-05, 210.73443603515625)
after reverting parent : <Quaternion (w=0.0000, x=0.0000, y=0.7071, z=0.7071)>
scale: (1.0, 1.0, 1.0, 0.0)

 

Those quats still aren't right. The gremlins are out in force today :)

Link to comment

This is a good one.

 

I've been setting the angle of pose bones by script. I know this does something because the bones do in fact rotate in the model window.  And if I run the script again, they stay put because they already have the rotation I assigned to them. So that's solid, right?

 

Except when I run the exporter script, without having done anything else in the mean time, the report a different rotation. So what's happening?

 

I wondered if some weird side-effect but wasn't altering the values before I could print them. So I though I'd write a function to print out a bone's rotation and call it from different places in the code.

 

def print_bone(name):
        b = bpy.context.active_object.pose.bones[name]
        print("Bone: {}".format(b.name))                       # let's not assume
        print("Rotation: {}".format( b.rotation_quaternion ))  # this gets and sets the bone rotation

 

Now the rotation_quaternion property is how I set the rotation, but it's not how I get it for the exporter. So, just to be sure, I added another, redundant line:

 

        print("Rotation: {}".format( b.matrix.to_quaternion().copy()))          # should be the same

 

That's how I'm getting the numbers when I'm doing the export. So what does it print?

 

Bone: Root
Rotation: <Quaternion (w=0.0000, x=0.0000, y=0.0000, z=1.0000)>
Rotation: <Quaternion (w=1.0000, x=-0.0000, y=0.0000, z=0.0000)>

 

I mean, what the High Holy Fuckety-Fucking Fuck is that all about? I know no one can explain the Matrix to me, but I've experienced it for myself and I still don't get it. I'd say Anton's code was wrong, except for the simple fact that it generated working animations. Then again, a lot of Anton's processing was hidden away behind .exe files for which we have no source, so we don't know what post-processing want on behind the scenes.

 

So I'm going to try an rewrite the transforms loop using the method that gives sensible results. That will give me the rotations Max and HCT generate for a model that isn't rotating. And then we'll see where takes us.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. For more information, see our Privacy Policy & Terms of Use