DocClox Posted May 11, 2021 Posted May 11, 2021 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. 5
DocClox Posted May 12, 2021 Author Posted May 12, 2021 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. 1
DocClox Posted May 12, 2021 Author Posted May 12, 2021 OK. Animation is in-game and doesn't crash anything. ... which isn't the same as it actually working, of course. It's progress of a sort. 1
DocClox Posted May 12, 2021 Author Posted May 12, 2021 OK. Follow my reasoning here. 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. There are too many bones, and too many transforms, for me to do any meaningful analysis on the numbers 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. 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. 2
DocClox Posted May 12, 2021 Author Posted May 12, 2021 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. So that's looking very probable. Let's try aligning that test model and try again. 2
Carabosse Posted May 12, 2021 Posted May 12, 2021 I have nothing valuable to add so I'll just say best of luck. ?
DocClox Posted May 12, 2021 Author Posted May 12, 2021 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. 1
DocClox Posted May 12, 2021 Author Posted May 12, 2021 Just now, Carabosse said: I have nothing valuable to add so I'll just say best of luck. ? The encouragement is appreciated 1
DocClox Posted May 13, 2021 Author Posted May 13, 2021 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 ,,,) This is the HCT rotation using the same site: So it looks like I have an unwanted rotation of -90 around the X-Axis. 1
DocClox Posted May 13, 2021 Author Posted May 13, 2021 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: 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 1
Bad Dog Posted May 13, 2021 Posted May 13, 2021 Haven't read this all yet, but ya, nif (all nifs that I've seen) are oriented differently from the rest of the world. You have to rotate them to "Y forward, Z up" on import. So that's likely independent of other issues.
DocClox Posted May 13, 2021 Author Posted May 13, 2021 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. 1
DocClox Posted May 13, 2021 Author Posted May 13, 2021 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 ... 1
DocClox Posted May 13, 2021 Author Posted May 13, 2021 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. 1
DocClox Posted May 13, 2021 Author Posted May 13, 2021 Well, as it happens ... this is the rotation HCT uses And this is the one my script generated. 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. 1
Bad Dog Posted May 13, 2021 Posted May 13, 2021 Nice work. Have some cake. ? (It makes me very happy to see someone *else* struggling with transforms. Gonna sift through in detail and see what I can take from it.)
DocClox Posted May 14, 2021 Author Posted May 14, 2021 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 1
DocClox Posted May 14, 2021 Author Posted May 14, 2021 So, apparently, what I needed was something like this: 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). 2
DocClox Posted May 14, 2021 Author Posted May 14, 2021 Well, what I was hoping for was something like this: What I got was this: 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. 3
DocClox Posted May 14, 2021 Author Posted May 14, 2021 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. 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... 2
DocClox Posted May 14, 2021 Author Posted May 14, 2021 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 1
DocClox Posted May 14, 2021 Author Posted May 14, 2021 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.
TheBottomhoodofSteel Posted May 14, 2021 Posted May 14, 2021 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 believe ZaZ had a base at one point, but that got lost to time.
DocClox Posted May 15, 2021 Author Posted May 15, 2021 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... 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 2
DocClox Posted May 15, 2021 Author Posted May 15, 2021 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.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now