1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
# SPDX-License-Identifier: Apache-2.0
# Copyright 2018-2021 The glTF-Blender-IO authors.
import functools
import bpy
from io_scene_gltf2.blender.exp import gltf2_blender_get
def cached_by_key(key):
"""
Decorates functions whose result should be cached. Use it like:
@cached_by_key(key=...)
def func(..., export_settings):
...
The decorated function, func, must always take an "export_settings" arg
(the cache is stored here).
The key argument to the decorator is a function that computes the key to
cache on. It is passed all the arguments to func.
"""
def inner(func):
@functools.wraps(func)
def wrapper_cached(*args, **kwargs):
if kwargs.get("export_settings"):
export_settings = kwargs["export_settings"]
else:
export_settings = args[-1]
cache_key = key(*args, **kwargs)
# invalidate cache if export settings have changed
if not hasattr(func, "__export_settings") or export_settings != func.__export_settings:
func.__cache = {}
func.__export_settings = export_settings
# use or fill cache
if cache_key in func.__cache:
return func.__cache[cache_key]
else:
result = func(*args, **kwargs)
func.__cache[cache_key] = result
return result
return wrapper_cached
return inner
def default_key(*args, **kwargs):
"""
Default cache key for @cached functions.
Cache on all arguments (except export_settings).
"""
assert len(args) >= 2 and 0 <= len(kwargs) <= 1, "Wrong signature for cached function"
cache_key_args = args
# make a shallow copy of the keyword arguments so that 'export_settings' can be removed
cache_key_kwargs = dict(kwargs)
if kwargs.get("export_settings"):
del cache_key_kwargs["export_settings"]
else:
cache_key_args = args[:-1]
cache_key = ()
for i in cache_key_args:
cache_key += (i,)
for i in cache_key_kwargs.values():
cache_key += (i,)
return cache_key
def cached(func):
return cached_by_key(key=default_key)(func)
def objectcache(func):
def reset_cache_objectcache():
func.__objectcache = {}
func.reset_cache = reset_cache_objectcache
@functools.wraps(func)
def wrapper_objectcache(*args, **kwargs):
cache_key_args = args
cache_key_args = args[:-1]
if not hasattr(func, "__objectcache"):
func.reset_cache()
# object is not cached yet
if cache_key_args[0] not in func.__objectcache.keys():
result = func(*args)
func.__objectcache = result
return result[cache_key_args[0]][cache_key_args[1]][cache_key_args[4]]
# object is in cache, but not this action
# We need to keep other actions
elif cache_key_args[1] not in func.__objectcache[cache_key_args[0]].keys():
result = func(*args)
func.__objectcache[cache_key_args[0]][cache_key_args[1]] = result[cache_key_args[0]][cache_key_args[1]]
return result[cache_key_args[0]][cache_key_args[1]][cache_key_args[4]]
# all is already cached
else:
return func.__objectcache[cache_key_args[0]][cache_key_args[1]][cache_key_args[4]]
return wrapper_objectcache
def bonecache(func):
def reset_cache_bonecache():
func.__current_action_name = None
func.__current_armature_uuid = None
func.__bonecache = {}
func.reset_cache = reset_cache_bonecache
@functools.wraps(func)
def wrapper_bonecache(*args, **kwargs):
armature = args[-1]['vtree'].nodes[args[0]].blender_object
cache_key_args = args
cache_key_args = args[:-1]
if cache_key_args[2] is None:
pose_bone_if_armature = gltf2_blender_get.get_object_from_datapath(armature,
cache_key_args[1][0].data_path)
else:
pose_bone_if_armature = armature.pose.bones[cache_key_args[2]]
if not hasattr(func, "__current_action_name"):
func.reset_cache()
if cache_key_args[6] != func.__current_action_name or cache_key_args[0] != func.__current_armature_uuid:
result = func(*args)
func.__bonecache = result
func.__current_action_name = cache_key_args[6]
func.__current_armature_uuid = cache_key_args[0]
return result[cache_key_args[7]][pose_bone_if_armature.name]
else:
return func.__bonecache[cache_key_args[7]][pose_bone_if_armature.name]
return wrapper_bonecache
# TODO: replace "cached" with "unique" in all cases where the caching is functional and not only for performance reasons
call_or_fetch = cached
unique = cached
def skdriverdiscovercache(func):
def reset_cache_skdriverdiscovercache():
func.__current_armature_uuid = None
func.__skdriverdiscover = {}
func.reset_cache = reset_cache_skdriverdiscovercache
@functools.wraps(func)
def wrapper_skdriverdiscover(*args, **kwargs):
cache_key_args = args
cache_key_args = args[:-1]
if not hasattr(func, "__current_armature_uuid") or func.__current_armature_uuid is None:
func.reset_cache()
if cache_key_args[0] != func.__current_armature_uuid:
result = func(*args)
func.__skdriverdiscover[cache_key_args[0]] = result
func.__current_armature_uuid = cache_key_args[0]
return result
else:
return func.__skdriverdiscover[cache_key_args[0]]
return wrapper_skdriverdiscover
def skdrivervalues(func):
def reset_cache_skdrivervalues():
func.__skdrivervalues = {}
func.reset_cache = reset_cache_skdrivervalues
@functools.wraps(func)
def wrapper_skdrivervalues(*args, **kwargs):
if not hasattr(func, "__skdrivervalues") or func.__skdrivervalues is None:
func.reset_cache()
armature = args[-1]['vtree'].nodes[args[0]].blender_object
cache_key_args = args
cache_key_args = args[:-1]
if armature.name not in func.__skdrivervalues.keys():
func.__skdrivervalues[armature.name] = {}
if cache_key_args[1] not in func.__skdrivervalues[armature.name]:
vals = func(*args)
func.__skdrivervalues[armature.name][cache_key_args[1]] = vals
return vals
else:
return func.__skdrivervalues[armature.name][cache_key_args[1]]
return wrapper_skdrivervalues
|