Koro rig (Caminandes) wrong in master, worked in 270(a) #39806

Closed
opened 2014-04-20 13:49:43 +02:00 by Thomas Dinges · 18 comments

System Information
Windows 7, Geforce 540M

Blender Version
Broken: f77207b
Worked: 2.69, 2.70(a)

Short description of error
The Koro rig is wrong in latest revisions.

Exact steps for others to reproduce the error
Open 11a_comp.blend from the Caminandes files (http://temp.dingto.org/caminandes_files.7z , ~480MB), reload trusted and go to frame 500.
2.70a is fine, in master the arms are wrong.

2.70a:
270a.jpg

Master:
Master.jpg

I want to show this file during my Cycles FMX presentation next week, with the latest Cycles speedups, so I would really appreciate a fix until Wednesday latest.. This is a bad regression anyways, so marking as unbreak now!

I know it's a large file, sorry for that. :)

**System Information** Windows 7, Geforce 540M **Blender Version** Broken: f77207b Worked: 2.69, 2.70(a) **Short description of error** The Koro rig is wrong in latest revisions. **Exact steps for others to reproduce the error** Open 11a_comp.blend from the Caminandes files (http://temp.dingto.org/caminandes_files.7z , ~480MB), reload trusted and go to frame 500. 2.70a is fine, in master the arms are wrong. 2.70a: ![270a.jpg](https://archive.blender.org/developer/F85393/270a.jpg) Master: ![Master.jpg](https://archive.blender.org/developer/F85395/Master.jpg) I want to show this file during my Cycles FMX presentation next week, *with* the latest Cycles speedups, so I would really appreciate a fix until Wednesday latest.. This is a bad regression anyways, so marking as unbreak now! I know it's a large file, sorry for that. :)
Author
Owner

Changed status to: 'Open'

Changed status to: 'Open'
Author
Owner

Added subscriber: @ThomasDinges

Added subscriber: @ThomasDinges
Author
Owner

Added subscribers: @JoshuaLeung, @ideasman42

Added subscribers: @JoshuaLeung, @ideasman42

Added subscriber: @nudelZ

Added subscriber: @nudelZ

confirmed :/

current master, ubuntu 13.10

confirmed :/ current master, ubuntu 13.10
Bastien Montagne self-assigned this 2014-04-20 14:28:10 +02:00

:/ Most likely another bone roll mess of my own… I’m starting to wonder if one should ever touche that ugly code.

:/ Most likely another bone roll mess of my own… I’m starting to wonder if one should ever touche that ugly code.

Ok, as expected, issue is caused by commit 07f8c5c…

Now, I just cannot investigate such a complex rig! We ask for simple blend files for reports, there’s a good reason for it…

Ok, as expected, issue is caused by commit 07f8c5c… Now, I just **cannot** investigate such a complex rig! We ask for **simple** blend files for reports, there’s a good reason for it…

To be more specific, with such a complex rig, I can’t determine where (as in, at which bone) happens the issue, I can’t make useful traces/prints in code, I can’t even say whether this is a real bug, or whether the rig had to compensate somehow a wrong behavior in previous code, that got fixed with this patch…

To be more specific, with such a complex rig, I can’t determine **where** (as in, at which bone) happens the issue, I can’t make useful traces/prints in code, I can’t even say whether this is a real bug, or whether the rig had to compensate somehow a wrong behavior in previous code, that got fixed with this patch…
Author
Owner

Added subscriber: @tippisum

Added subscriber: @tippisum
Author
Owner

I understand, but I don't see this as incomplete..

Maybe @tippisum has an idea.

I understand, but I don't see this as incomplete.. Maybe @tippisum has an idea.

Again, I need a simpler file demonstrating the issue, as stated in bug report rules!

Again, I need a simpler file demonstrating the issue, as stated in bug report rules!

Ok, so issue was actually caused by recent changes to Transform constraint (usual degrees to radians issue).

Here is a patch that adds an Update Animated Transform Constraints operator, which uses (a slightly modified) animsys_refactor to scan all actions and drivers, change RNA paths when needed, and alter FCurves keyframes and/or generator modifiers to convert from degrees values to radians ones.

Not sure we would like to extend this to all changes made in 2.70 release regarding deg2rad?

Ok, so issue was actually caused by recent changes to Transform constraint (usual degrees to radians issue). Here is a patch that adds an `Update Animated Transform Constraints` operator, which uses (a slightly modified) animsys_refactor to scan all actions and drivers, change RNA paths when needed, and alter FCurves keyframes and/or generator modifiers to convert from degrees values to radians ones. Not sure we would like to extend this to all changes made in 2.70 release regarding deg2rad?

Grrr…

P46: (An Untitled Masterwork)

diff --git a/release/scripts/modules/animsys_refactor.py b/release/scripts/modules/animsys_refactor.py
index 8899bbf..aaa06c3 100644
--- a/release/scripts/modules/animsys_refactor.py
+++ b/release/scripts/modules/animsys_refactor.py
@@ -25,6 +25,10 @@ rna values in fcurves and drivers.
 Currently unused, but might become useful later again.
 """
 
+import sys
+import bpy
+
+
 IS_TESTING = False
 
 
@@ -34,9 +38,21 @@ def drepr(string):
     return '"%s"' % repr(string)[1:-1].replace("\"", "\\\"").replace("\\'", "'")
 
 
+def classes_recursive(base_type, clss=None):
+    if clss is None:
+        clss = [base_type]
+    else:
+        clss.append(base_type)
+
+    for base_type_iter in base_type.__bases__:
+        if base_type_iter is not object:
+            classes_recursive(base_type_iter, clss)
+
+    return clss
+
+
 class DataPathBuilder(object):
-    """ Dummy class used to parse fcurve and driver data paths.
-    """
+    """Dummy class used to parse fcurve and driver data paths."""
     __slots__ = ("data_path", )
 
     def __init__(self, attrs):
@@ -55,38 +71,54 @@ class DataPathBuilder(object):
             raise Exception("unsupported accessor %r of type %r (internal error)" % (key, type(key)))
         return DataPathBuilder(self.data_path + (str_value, ))
 
-    def resolve(self, real_base, rna_update_from_map=None):
-        """ Return (attribute, value) pairs.
-        """
+    def resolve(self, real_base, rna_update_from_map, fcurve, log):
+        """Return (attribute, value) pairs."""
         pairs = []
         base = real_base
         for item in self.data_path:
             if base is not Ellipsis:
-                try:
-                    # this only works when running with an old blender
-                    # where the old path will resolve
-                    base = eval("base" + item)
-                except:
-                    base_new = Ellipsis
-                    # guess the new name
-                    if item.startswith("."):
-                        for item_new in rna_update_from_map.get(item[1:], ()):
-                            try:
-                                print("base." + item_new)
-                                base_new = eval("base." + item_new)
+                base_new = Ellipsis
+                # find the new name
+                if item.startswith("."):
+                    for class_name, item_new, options in rna_update_from_map.get(item[1:], - [ ]) + [(None, item[1:], None)]:
+                        if callable(item_new):
+                            # No type check here, callback is assumed to know what it's doing.
+                            base_new, item_new = item_new(base, class_name, item[1:], fcurve, options)
+                            if base_new is not Ellipsis:
                                 break  # found, don't keep looking
-                            except:
-                                pass
-
-                    if base_new is Ellipsis:
-                        print("Failed to resolve data path:", self.data_path)
-                    base = base_new
-
-            pairs.append((item, base))
+                        else:
+                            # Type check!
+                            type_ok = True
+                            if class_name is not None:
+                                type_ok = False
+                                for base_type in classes_recursive(type(base)):
+                                    if base_type.__name__ == class_name:
+                                        type_ok = True
+                                        break
+                            if type_ok:
+                                try:
+                                    #print("base." + item_new)
+                                    base_new = eval("base." + item_new)
+                                    break  # found, don't keep looking
+                                except:
+                                    pass
+                    item_new = "." + item_new
+                else:
+                    item_new = item
+                    try:
+                        base_new = eval("base" + item_new)
+                    except:
+                        pass
+
+                if base_new is Ellipsis:
+                    print("Failed to resolve data path:", self.data_path, file=log)
+                base = base_new
+            else:
+                item_new = item
+
+            pairs.append((item_new, base))
         return pairs
 
-import bpy
-
 
 def id_iter():
     type_iter = type(bpy.data.objects)
@@ -110,20 +142,7 @@ def anim_data_actions(anim_data):
     return [act for act in actions if act]
 
 
-def classes_recursive(base_type, clss=None):
-    if clss is None:
-        clss = [base_type]
-    else:
-        clss.append(base_type)
-
-    for base_type_iter in base_type.__bases__:
-        if base_type_iter is not object:
-            classes_recursive(base_type_iter, clss)
-
-    return clss
-
-
-def find_path_new(id_data, data_path, rna_update_dict, rna_update_from_map):
+def find_path_new(id_data, data_path, rna_update_from_map, fcurve, log):
     # note!, id_data can be ID type or a node tree
     # ignore ID props for now
     if data_path.startswith("["):
@@ -131,46 +150,28 @@ def find_path_new(id_data, data_path, rna_update_dict, rna_update_from_map):
 
     # recursive path fixing, likely will be one in most cases.
     data_path_builder = eval("DataPathBuilder(tuple())." + data_path)
-    data_resolve = data_path_builder.resolve(id_data, rna_update_from_map)
+    data_resolve = data_path_builder.resolve(id_data, rna_update_from_map, fcurve, log)
 
     path_new = [pair- [x] for pair in data_resolve]
 
-    # print(data_resolve)
-    data_base = id_data
-
-    for i, (attr, data) in enumerate(data_resolve):
-        if data is Ellipsis:
-            break
-
-        if attr.startswith("."):
-            # try all classes
-            for data_base_type in classes_recursive(type(data_base)):
-                attr_new = rna_update_dict.get(data_base_type.__name__, {}).get(attr[1:])
-                if attr_new:
-                    path_new- [x] = "." + attr_new
+    return "".join(path_new)[1:]  # skip the first "."
 
-        # set this as the base for further properties
-        data_base = data
 
-    data_path_new = "".join(path_new)[1:]  # skip the first "."
-    return data_path_new
-
-
-def update_data_paths(rna_update):
-    """ rna_update triple [(class_name, from, to), ...]
+def update_data_paths(rna_update, log=sys.stdout):
+    """
+    rna_update triple [(class_name, from, to or to_callback, callback options), ...]
+    to_callback is a function with this signature: update_cb(base, class_name, old_path, fcurve, options)
+                where base is current object, class_name is the expected type name of base (callback has to handle
+                this), old_path it the org name of base's property, fcurve is the affected fcurve (!),
+                and options is an opaque data.
+                class_name, fcurve and options may be None!
     """
-
-    # make a faster lookup dict
-    rna_update_dict = {}
-    for ren_class, ren_from, ren_to in rna_update:
-        rna_update_dict.setdefault(ren_class, {})[ren_from] = ren_to
 
     rna_update_from_map = {}
-    for ren_class, ren_from, ren_to in rna_update:
-        rna_update_from_map.setdefault(ren_from, - [ ]).append(ren_to)
+    for ren_class, ren_from, ren_to, options in rna_update:
+        rna_update_from_map.setdefault(ren_from, - [ ]).append((ren_class, ren_to, options))
 
     for id_data in id_iter():
-
         # check node-trees too
         anim_data_ls = [(id_data, getattr(id_data, "animation_data", None))]
         node_tree = getattr(id_data, "node_tree", None)
@@ -183,13 +184,13 @@ def update_data_paths(rna_update):
 
             for fcurve in anim_data.drivers:
                 data_path = fcurve.data_path
-                data_path_new = find_path_new(anim_data_base, data_path, rna_update_dict, rna_update_from_map)
+                data_path_new = find_path_new(anim_data_base, data_path, rna_update_from_map, fcurve, log)
                 # print(data_path_new)
                 if data_path_new != data_path:
                     if not IS_TESTING:
                         fcurve.data_path = data_path_new
                         fcurve.driver.is_valid = True  # reset to allow this to work again
-                    print("driver-fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new))
+                    print("driver-fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new), file=log)
 
                 for var in fcurve.driver.variables:
                     if var.type == 'SINGLE_PROP':
@@ -198,32 +199,33 @@ def update_data_paths(rna_update):
                             data_path = tar.data_path
 
                             if id_data_other and data_path:
-                                data_path_new = find_path_new(id_data_other, data_path, rna_update_dict, rna_update_from_map)
+                                data_path_new = find_path_new(id_data_other, data_path, rna_update_from_map, None, log)
                                 # print(data_path_new)
                                 if data_path_new != data_path:
                                     if not IS_TESTING:
                                         tar.data_path = data_path_new
-                                    print("driver (%s): %s -> %s" % (id_data_other.name, data_path, data_path_new))
+                                    print("driver (%s): %s -> %s" % (id_data_other.name, data_path, data_path_new),
+                                          file=log)
 
             for action in anim_data_actions(anim_data):
                 for fcu in action.fcurves:
                     data_path = fcu.data_path
-                    data_path_new = find_path_new(anim_data_base, data_path, rna_update_dict, rna_update_from_map)
+                    data_path_new = find_path_new(anim_data_base, data_path, rna_update_from_map, fcu, log)
                     # print(data_path_new)
                     if data_path_new != data_path:
                         if not IS_TESTING:
                             fcu.data_path = data_path_new
-                        print("fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new))
+                        print("fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new), file=log)
 
 
 if __name__ == "__main__":
 
     # Example, should be called externally
-    # (class, from, to)
+    # (class, from, to or to_callback, callback_options)
     replace_ls = [
-        ("AnimVizMotionPaths", "frame_after", "frame_after"),
-        ("AnimVizMotionPaths", "frame_before", "frame_before"),
-        ("AnimVizOnionSkinning", "frame_after", "frame_after"),
+        ("AnimVizMotionPaths", "frame_after", "frame_after", None),
+        ("AnimVizMotionPaths", "frame_before", "frame_before", None),
+        ("AnimVizOnionSkinning", "frame_after", "frame_after", None),
     ]
 
     update_data_paths(replace_ls)
diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py
index 41f39b9..0193454 100644
--- a/release/scripts/startup/bl_operators/anim.py
+++ b/release/scripts/startup/bl_operators/anim.py
@@ -23,6 +23,7 @@ if "bpy" in locals():
     if "anim_utils" in locals():
         imp.reload(anim_utils)
 
+
 import bpy
 from bpy.types import Operator
 from bpy.props import (IntProperty,
@@ -281,3 +282,87 @@ class ClearUselessActions(Operator):
         self.report({'INFO'}, "Removed %d empty and/or fake-user only Actions"
                               % removed)
         return {'FINISHED'}
+
+
+class UpdateAnimatedTransformConstraint(Operator):
+    """
+    Update fcurves/drivers affecting Transform constraints (use it with files from 2.70 and earlier)
+    """
+    bl_idname = "anim.update_animated_transform_constraints"
+    bl_label = "Update Animated Transform Constraints"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    use_convert_to_radians = BoolProperty(
+            name="Convert To Radians",
+            description="Convert fcurves/drivers affecting rotations to radians (Warning: use this only once!)",
+            default=True,
+            )
+
+    def execute(self, context):
+        import animsys_refactor
+        from math import radians
+        import io
+
+        from_paths = {"from_max_x", "from_max_y", "from_max_z", "from_min_x", "from_min_y", "from_min_z"}
+        to_paths = {"to_max_x", "to_max_y", "to_max_z", "to_min_x", "to_min_y", "to_min_z"}
+        paths = from_paths | to_paths
+
+        def update_cb(base, class_name, old_path, fcurve, options):
+            print(options)
+            def handle_deg2rad(fcurve):
+                if fcurve is not None:
+                    if hasattr(fcurve, "keyframes"):
+                        for k in fcurve.keyframes:
+                            k.co.y = radians(k.co.y)
+                    for mod in fcurve.modifiers:
+                        if mod.type == 'GENERATOR':
+                            if mod.mode == 'POLYNOMIAL':
+                                mod.coefficients- [x] = [radians(c) for c in mod.coefficients]
+                            else:  # if mod.type == 'POLYNOMIAL_FACTORISED':
+                                mod.coefficients[:2] = [radians(c) for c in mod.coefficients[:2]]
+                        elif mod.type == 'FNGENERATOR':
+                            mod.amplitude = radians(mod.amplitude)
+                    fcurve.update()
+
+            data = ...
+            try:
+                data = eval("base." + old_path)
+            except:
+                pass
+            ret = (data, old_path)
+            if isinstance(base, bpy.types.TransformConstraint) and data is not ...:
+                new_path = None
+                map_info = base.map_from if old_path in from_paths else base.map_to
+                if map_info == 'ROTATION':
+                    new_path = old_path + "_rot"
+                    if options is not None and options["use_convert_to_radians"]:
+                        handle_deg2rad(fcurve)
+                elif map_info == 'SCALE':
+                    new_path = old_path + "_scale"
+
+                if new_path is not None:
+                    data = ...
+                    try:
+                        data = eval("base." + new_path)
+                    except:
+                        pass
+                    ret = (data, new_path)
+                    #print(ret)
+
+            return ret
+
+        options = {"use_convert_to_radians": self.use_convert_to_radians}
+        replace_ls = [("TransformConstraint", p, update_cb, options) for p in paths]
+        log = io.StringIO()
+
+        animsys_refactor.update_data_paths(replace_ls, log)
+
+        context.scene.frame_set(context.scene.frame_current)
+
+        log = log.getvalue()
+        if log:
+            print(log)
+            text = bpy.data.texts.new("UpdateAnimatedTransformConstraint Report")
+            text.from_string(log)
+            self.report({'INFO'}, "Complete report available on '{}' text datablock".format(text.name))
+        return {'FINISHED'}

Grrr… [P46: (An Untitled Masterwork)](https://archive.blender.org/developer/P46.txt) ```diff diff --git a/release/scripts/modules/animsys_refactor.py b/release/scripts/modules/animsys_refactor.py index 8899bbf..aaa06c3 100644 --- a/release/scripts/modules/animsys_refactor.py +++ b/release/scripts/modules/animsys_refactor.py @@ -25,6 +25,10 @@ rna values in fcurves and drivers. Currently unused, but might become useful later again. """ +import sys +import bpy + + IS_TESTING = False @@ -34,9 +38,21 @@ def drepr(string): return '"%s"' % repr(string)[1:-1].replace("\"", "\\\"").replace("\\'", "'") +def classes_recursive(base_type, clss=None): + if clss is None: + clss = [base_type] + else: + clss.append(base_type) + + for base_type_iter in base_type.__bases__: + if base_type_iter is not object: + classes_recursive(base_type_iter, clss) + + return clss + + class DataPathBuilder(object): - """ Dummy class used to parse fcurve and driver data paths. - """ + """Dummy class used to parse fcurve and driver data paths.""" __slots__ = ("data_path", ) def __init__(self, attrs): @@ -55,38 +71,54 @@ class DataPathBuilder(object): raise Exception("unsupported accessor %r of type %r (internal error)" % (key, type(key))) return DataPathBuilder(self.data_path + (str_value, )) - def resolve(self, real_base, rna_update_from_map=None): - """ Return (attribute, value) pairs. - """ + def resolve(self, real_base, rna_update_from_map, fcurve, log): + """Return (attribute, value) pairs.""" pairs = [] base = real_base for item in self.data_path: if base is not Ellipsis: - try: - # this only works when running with an old blender - # where the old path will resolve - base = eval("base" + item) - except: - base_new = Ellipsis - # guess the new name - if item.startswith("."): - for item_new in rna_update_from_map.get(item[1:], ()): - try: - print("base." + item_new) - base_new = eval("base." + item_new) + base_new = Ellipsis + # find the new name + if item.startswith("."): + for class_name, item_new, options in rna_update_from_map.get(item[1:], - [ ]) + [(None, item[1:], None)]: + if callable(item_new): + # No type check here, callback is assumed to know what it's doing. + base_new, item_new = item_new(base, class_name, item[1:], fcurve, options) + if base_new is not Ellipsis: break # found, don't keep looking - except: - pass - - if base_new is Ellipsis: - print("Failed to resolve data path:", self.data_path) - base = base_new - - pairs.append((item, base)) + else: + # Type check! + type_ok = True + if class_name is not None: + type_ok = False + for base_type in classes_recursive(type(base)): + if base_type.__name__ == class_name: + type_ok = True + break + if type_ok: + try: + #print("base." + item_new) + base_new = eval("base." + item_new) + break # found, don't keep looking + except: + pass + item_new = "." + item_new + else: + item_new = item + try: + base_new = eval("base" + item_new) + except: + pass + + if base_new is Ellipsis: + print("Failed to resolve data path:", self.data_path, file=log) + base = base_new + else: + item_new = item + + pairs.append((item_new, base)) return pairs -import bpy - def id_iter(): type_iter = type(bpy.data.objects) @@ -110,20 +142,7 @@ def anim_data_actions(anim_data): return [act for act in actions if act] -def classes_recursive(base_type, clss=None): - if clss is None: - clss = [base_type] - else: - clss.append(base_type) - - for base_type_iter in base_type.__bases__: - if base_type_iter is not object: - classes_recursive(base_type_iter, clss) - - return clss - - -def find_path_new(id_data, data_path, rna_update_dict, rna_update_from_map): +def find_path_new(id_data, data_path, rna_update_from_map, fcurve, log): # note!, id_data can be ID type or a node tree # ignore ID props for now if data_path.startswith("["): @@ -131,46 +150,28 @@ def find_path_new(id_data, data_path, rna_update_dict, rna_update_from_map): # recursive path fixing, likely will be one in most cases. data_path_builder = eval("DataPathBuilder(tuple())." + data_path) - data_resolve = data_path_builder.resolve(id_data, rna_update_from_map) + data_resolve = data_path_builder.resolve(id_data, rna_update_from_map, fcurve, log) path_new = [pair- [x] for pair in data_resolve] - # print(data_resolve) - data_base = id_data - - for i, (attr, data) in enumerate(data_resolve): - if data is Ellipsis: - break - - if attr.startswith("."): - # try all classes - for data_base_type in classes_recursive(type(data_base)): - attr_new = rna_update_dict.get(data_base_type.__name__, {}).get(attr[1:]) - if attr_new: - path_new- [x] = "." + attr_new + return "".join(path_new)[1:] # skip the first "." - # set this as the base for further properties - data_base = data - data_path_new = "".join(path_new)[1:] # skip the first "." - return data_path_new - - -def update_data_paths(rna_update): - """ rna_update triple [(class_name, from, to), ...] +def update_data_paths(rna_update, log=sys.stdout): + """ + rna_update triple [(class_name, from, to or to_callback, callback options), ...] + to_callback is a function with this signature: update_cb(base, class_name, old_path, fcurve, options) + where base is current object, class_name is the expected type name of base (callback has to handle + this), old_path it the org name of base's property, fcurve is the affected fcurve (!), + and options is an opaque data. + class_name, fcurve and options may be None! """ - - # make a faster lookup dict - rna_update_dict = {} - for ren_class, ren_from, ren_to in rna_update: - rna_update_dict.setdefault(ren_class, {})[ren_from] = ren_to rna_update_from_map = {} - for ren_class, ren_from, ren_to in rna_update: - rna_update_from_map.setdefault(ren_from, - [ ]).append(ren_to) + for ren_class, ren_from, ren_to, options in rna_update: + rna_update_from_map.setdefault(ren_from, - [ ]).append((ren_class, ren_to, options)) for id_data in id_iter(): - # check node-trees too anim_data_ls = [(id_data, getattr(id_data, "animation_data", None))] node_tree = getattr(id_data, "node_tree", None) @@ -183,13 +184,13 @@ def update_data_paths(rna_update): for fcurve in anim_data.drivers: data_path = fcurve.data_path - data_path_new = find_path_new(anim_data_base, data_path, rna_update_dict, rna_update_from_map) + data_path_new = find_path_new(anim_data_base, data_path, rna_update_from_map, fcurve, log) # print(data_path_new) if data_path_new != data_path: if not IS_TESTING: fcurve.data_path = data_path_new fcurve.driver.is_valid = True # reset to allow this to work again - print("driver-fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new)) + print("driver-fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new), file=log) for var in fcurve.driver.variables: if var.type == 'SINGLE_PROP': @@ -198,32 +199,33 @@ def update_data_paths(rna_update): data_path = tar.data_path if id_data_other and data_path: - data_path_new = find_path_new(id_data_other, data_path, rna_update_dict, rna_update_from_map) + data_path_new = find_path_new(id_data_other, data_path, rna_update_from_map, None, log) # print(data_path_new) if data_path_new != data_path: if not IS_TESTING: tar.data_path = data_path_new - print("driver (%s): %s -> %s" % (id_data_other.name, data_path, data_path_new)) + print("driver (%s): %s -> %s" % (id_data_other.name, data_path, data_path_new), + file=log) for action in anim_data_actions(anim_data): for fcu in action.fcurves: data_path = fcu.data_path - data_path_new = find_path_new(anim_data_base, data_path, rna_update_dict, rna_update_from_map) + data_path_new = find_path_new(anim_data_base, data_path, rna_update_from_map, fcu, log) # print(data_path_new) if data_path_new != data_path: if not IS_TESTING: fcu.data_path = data_path_new - print("fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new)) + print("fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new), file=log) if __name__ == "__main__": # Example, should be called externally - # (class, from, to) + # (class, from, to or to_callback, callback_options) replace_ls = [ - ("AnimVizMotionPaths", "frame_after", "frame_after"), - ("AnimVizMotionPaths", "frame_before", "frame_before"), - ("AnimVizOnionSkinning", "frame_after", "frame_after"), + ("AnimVizMotionPaths", "frame_after", "frame_after", None), + ("AnimVizMotionPaths", "frame_before", "frame_before", None), + ("AnimVizOnionSkinning", "frame_after", "frame_after", None), ] update_data_paths(replace_ls) diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py index 41f39b9..0193454 100644 --- a/release/scripts/startup/bl_operators/anim.py +++ b/release/scripts/startup/bl_operators/anim.py @@ -23,6 +23,7 @@ if "bpy" in locals(): if "anim_utils" in locals(): imp.reload(anim_utils) + import bpy from bpy.types import Operator from bpy.props import (IntProperty, @@ -281,3 +282,87 @@ class ClearUselessActions(Operator): self.report({'INFO'}, "Removed %d empty and/or fake-user only Actions" % removed) return {'FINISHED'} + + +class UpdateAnimatedTransformConstraint(Operator): + """ + Update fcurves/drivers affecting Transform constraints (use it with files from 2.70 and earlier) + """ + bl_idname = "anim.update_animated_transform_constraints" + bl_label = "Update Animated Transform Constraints" + bl_options = {'REGISTER', 'UNDO'} + + use_convert_to_radians = BoolProperty( + name="Convert To Radians", + description="Convert fcurves/drivers affecting rotations to radians (Warning: use this only once!)", + default=True, + ) + + def execute(self, context): + import animsys_refactor + from math import radians + import io + + from_paths = {"from_max_x", "from_max_y", "from_max_z", "from_min_x", "from_min_y", "from_min_z"} + to_paths = {"to_max_x", "to_max_y", "to_max_z", "to_min_x", "to_min_y", "to_min_z"} + paths = from_paths | to_paths + + def update_cb(base, class_name, old_path, fcurve, options): + print(options) + def handle_deg2rad(fcurve): + if fcurve is not None: + if hasattr(fcurve, "keyframes"): + for k in fcurve.keyframes: + k.co.y = radians(k.co.y) + for mod in fcurve.modifiers: + if mod.type == 'GENERATOR': + if mod.mode == 'POLYNOMIAL': + mod.coefficients- [x] = [radians(c) for c in mod.coefficients] + else: # if mod.type == 'POLYNOMIAL_FACTORISED': + mod.coefficients[:2] = [radians(c) for c in mod.coefficients[:2]] + elif mod.type == 'FNGENERATOR': + mod.amplitude = radians(mod.amplitude) + fcurve.update() + + data = ... + try: + data = eval("base." + old_path) + except: + pass + ret = (data, old_path) + if isinstance(base, bpy.types.TransformConstraint) and data is not ...: + new_path = None + map_info = base.map_from if old_path in from_paths else base.map_to + if map_info == 'ROTATION': + new_path = old_path + "_rot" + if options is not None and options["use_convert_to_radians"]: + handle_deg2rad(fcurve) + elif map_info == 'SCALE': + new_path = old_path + "_scale" + + if new_path is not None: + data = ... + try: + data = eval("base." + new_path) + except: + pass + ret = (data, new_path) + #print(ret) + + return ret + + options = {"use_convert_to_radians": self.use_convert_to_radians} + replace_ls = [("TransformConstraint", p, update_cb, options) for p in paths] + log = io.StringIO() + + animsys_refactor.update_data_paths(replace_ls, log) + + context.scene.frame_set(context.scene.frame_current) + + log = log.getvalue() + if log: + print(log) + text = bpy.data.texts.new("UpdateAnimatedTransformConstraint Report") + text.from_string(log) + self.report({'INFO'}, "Complete report available on '{}' text datablock".format(text.name)) + return {'FINISHED'} ```

Also, as suggested by Ton, we should raise the minversion (to warn users when they re-open under previous versions).

P47: (An Untitled Masterwork)

diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h
index 2cc17f3..ebae9ff 100644
--- a/source/blender/blenkernel/BKE_blender.h
+++ b/source/blender/blenkernel/BKE_blender.h
@@ -43,9 +43,9 @@ extern "C" {
  * Use STRINGIFY() rather than defining with quotes */
 #define BLENDER_VERSION         270
 #define BLENDER_SUBVERSION      3
-/* 262 was the last editmesh release but it has compatibility code for bmesh data */
-#define BLENDER_MINVERSION      262
-#define BLENDER_MINSUBVERSION   0
+/* 270.1 Because of internal degrees to radians conversions. */
+#define BLENDER_MINVERSION      270
+#define BLENDER_MINSUBVERSION   1
 
 /* used by packaging tools */
 /* can be left blank, otherwise a,b,c... etc with no quotes */

Also, as suggested by Ton, we should raise the minversion (to warn users when they re-open under previous versions). [P47: (An Untitled Masterwork)](https://archive.blender.org/developer/P47.txt) ```diff diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 2cc17f3..ebae9ff 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -43,9 +43,9 @@ extern "C" { * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 270 #define BLENDER_SUBVERSION 3 -/* 262 was the last editmesh release but it has compatibility code for bmesh data */ -#define BLENDER_MINVERSION 262 -#define BLENDER_MINSUBVERSION 0 +/* 270.1 Because of internal degrees to radians conversions. */ +#define BLENDER_MINVERSION 270 +#define BLENDER_MINSUBVERSION 1 /* used by packaging tools */ /* can be left blank, otherwise a,b,c... etc with no quotes */ ```

This issue was referenced by blender/blender-addons-contrib@c089150426

This issue was referenced by blender/blender-addons-contrib@c08915042601c83022d352ba99026d13a18f73c2

This issue was referenced by c089150426

This issue was referenced by c08915042601c83022d352ba99026d13a18f73c2

Changed status from 'Open' to: 'Resolved'

Changed status from 'Open' to: 'Resolved'

Closed by commit c089150426.

Closed by commit c089150426.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
4 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#39806
No description provided.