Possible to do load/save pose preset for cameras?

Issue #1875 resolved
Rakete created an issue

Seems like that would be possible in theory, but it is not implemented yet. How difficult would it be to implement that?

Comments (17)

  1. Alessandro Padovani

    Actually animations can be loaded for objects after Thomas fixed the code. The extra attributes for cameras and lights are not handled though.

  2. Rakete reporter

    Seems like not that much interest for this besides me, but I wanted to say that for things like this you can also just explain how it should be implemented, and then I can do it. I should know the source code well enough by now that I could make a PR for this, though I am not so sure what needs to happen to make the existing load/save pose code work for cameras and lights, so some pointers where to start would be helpful.

  3. Thomas Larsson repo owner

    Well, as you know the pose preset code is located in the file preset.py. The object animation of the active object corresponds to channels where the url starts with "name://@selection:”. Only one object animation can be stored in each file, so if you have several objects (figures, props, cameras, lights) you must save several pose presets. I don't know if this is a limitation of my code or of pose presets themselves. Hierarchical pose presets might store several objects in a single file.

    When I try to import the animation back into Blender the rotation channels seem to be missing, although they are stored in the duf file and load into DS. Maybe a bug.

    The object data are not saved for cameras and lights. You can see how the DAZ channels are mapped into Blender in camera.py and light.py. In principle it should be possible to make the inverse transformation for making pose presets, but something is of course always lost in translation.

  4. Rakete reporter
    I can make it so the rotation is applied to the camera, in fact it does apply the rotation for me in my branch because I might have tried to fix that problem a while ago. But the rotation is not applied correctly. When I import a pose, then the camera ends up pointing in the complete wrong direction. In daz my camera has this rotation:
    

    then in I export it to blender (I save the file with the camera and import, so not using a pose preset) and it ends up like that (these are the correct values that I want):
    

    and then when I try to save the same pose in daz and apply it in blender I get this (there are the wrong values that make it point in the wrong direction):
    

    So clearly different. However I noticed that for any other object, those values in the last screenshot are actually correct, meaning there must be something in the code that imports cameras that handles rotation differently then how the import pose code would handle the rotations for objects, here is an objects rotation in daz:
    

    and here is its rotation in blender after saving the above screenshots rotation as pose preset and importing that into blender:
    

  5. Rakete reporter

    I tried to get creative with the newlines there, now I can’t change it back so this has to do.

  6. Rakete reporter

    It must be this piece of code in camera.py:

    def postTransform(self):
            if GS.zup:
                ob = self.rna
                ob.rotation_euler[0] += math.pi/2
    

    actually kind of obvious, though I thought that GS.zup is only True when that Z-axis debugging thing is enabled, which it shouldn’t be, but I can’t spot anything other then that changing the rotation when importing a scene vs. importing a pose. How would you add something similar to that postTransform to the pose loading/saving code?

  7. Rakete reporter

    Though that alone isn’t enough, the numbers don’t work out, even before the postTransform the numbers are different in the import_daz code then in the import_pose code. Also obviously the postTransform only changes x value, but the import_pose code produces three completely different values for the rotation.

  8. Rakete reporter

    Ok, just hacking my way through, I got the pose preset for my camera to work by doing this (in transform.py):

    def evalRot(self):
            if self.rot is None:
                return Vector((0,0,0))
            else:
                print("evalRot", self.rot)
                lrot = Euler(self.rot*D, "XYZ").to_matrix().to_4x4()
                RXP = Matrix.Rotation(math.pi/2, 4, 'X')
                RXN = Matrix.Rotation(-math.pi/2, 4, 'X')
                wmat = RXP @ lrot @ RXN
                _, rot, _ = lrot.decompose()
                euler = rot.to_euler("XYZ")
                print("evalRot2", euler)
                return Vector((euler.x + math.pi/2, euler.y, euler.z))
    

    this is called from setObject which is called in makeObjectFrame which is called in animateBones which is where the animation loaded from the pose preset is applied to the object/bones. And that tells me the calcMatrices code that is called for imports, but not for pose presets, does something that needs to be applied when loading poses as well. Plus that postTransform thing (which is just the + math.pi/2 in the return) needs to also somehow happen in there somewhere.

  9. Rakete reporter

    Oh, my hacky code is so hacky, I don’t even understand it myself. The RXP RXN stuff does nothing there, so it’s just:

    lrot = Euler(self.rot*D, "XYZ").to_matrix().to_4x4()
    _, rot, _ = lrot.decompose()
    euler = rot.to_euler("XYZ")
    return Vector((euler.x + math.pi/2, euler.y, euler.z))
    

    so that code should only take the rotation_mode into account, we could just pass that in I guess.

  10. Rakete reporter

    Also, really ultra complex my euler → matrix → decompose → quaternion → euler → vector logic there, but hey, it works!

  11. Rakete reporter

    Oh, no, we don’t even need to do the rotation_mode thing, though it does help produce the exact same values as the ones we get for the import. But it is enough to do this:

    rotD = self.rot*D
    return Vector((rotD.x + math.pi/2, rotD.y, rotD.z))
    

    and while the values end up being different, the orientation of the camera ends up being correct (just euler angle things I guess).

  12. Rakete reporter

    Ok, so my “fix” for importing pose presets for cameras or lights is this:

    cameraOrLight = ob.type in ['CAMERA', 'LIGHT']
    rot = d2bu(self.evalRot(cameraOrLight))
    

    in setObject in transform.py and:

    def evalRot(self, cameraOrLight=False):
            if self.rot is None:
                return Vector((0,0,0))
            else:
                rotD = self.rot*D
                if cameraOrLight:
                    return Vector((rotD.x + math.pi/2, rotD.y, rotD.z))
                else:
                    return rotD
    

    in evalRot in transform.py.

  13. Rakete reporter

    Yep, saving pose presets for cameras can be fixed by doing this:

    def getRigMatrix(self, rig, frame):
            objkey = self.getDazObject(rig)
            rot = rig.rotation_euler.copy()
            if objkey in self.rots.keys():
                for idx,fcu in enumerate(self.rots[objkey]):
                    if fcu:
                        rot[idx] = fcu.evaluate(frame)
            # reverse postTransform for cameras and lights before saving pose preset
            if rig.type in ['CAMERA', 'LIGHT']:
                rot.x -= math.pi/2
            mat = rot.to_matrix().to_4x4()
            if self.useScale:
                scale = rig.scale.copy()
                if objkey in self.scales.keys():
                    for idx,fcu in enumerate(self.scales[objkey]):
                        if fcu:
                            scale[idx] = fcu.evaluate(frame)
                smat = Matrix.Diagonal(scale)
                mat = mat @ smat.to_4x4()
            loc = rig.location.copy()
            if objkey in self.locs.keys():
                for idx,fcu in enumerate(self.locs[objkey]):
                    if fcu:
                        loc[idx] = fcu.evaluate(frame)
            return Matrix.Translation(loc) @ mat
    

    in getRigMatrix in preset.py

  14. Thomas Larsson repo owner

    Thank you for this; your code has been added to the repo. I also fixed the double colons that prevented rotations from loading, and added an extra 90 degree rotation when loading poses for cameras and lights.

    The GS.zup variable is normally enabled, and only turned off for debugging. It is the global Z Up setting.

  15. Log in to comment