diff --git a/bakeAllAnim_v01.py b/bakeAllAnim_v01.py
index 7062bd6..2e0f04f 100644
--- a/bakeAllAnim_v01.py
+++ b/bakeAllAnim_v01.py
@@ -114,7 +114,13 @@ def __init__(self, ui_file, pymxs, parent=MaxPlus.GetQMaxMainWindow()):
self._options = {'start': 0,
'end': 100,
'nth': 1,
- 'pad': False}
+ 'pad': False,
+ 'tracks': []}
+
+ # Label color vars
+ self._err = "color=#e82309"
+ self._wrn = "color=#f7bd0e"
+ self._grn = "color=#3cc103"
# ---------------------------------------------------
# End of Init
@@ -124,22 +130,38 @@ def __init__(self, ui_file, pymxs, parent=MaxPlus.GetQMaxMainWindow()):
# ---------------------------------------------------
def _update_range(self):
+ """
+ Update the frame range spinners with the Max Time Slider range.
+ """
_rt = self._pymxs.runtime
self._spn_start.setValue(_rt.animationRange.start)
self._spn_end.setValue(_rt.animationRange.end)
+ # Update status label
+ self._lbl_status.setText("Updated: Frame Range" % self._grn)
+
def _update_tracks(self):
"""
- Updates the Track Selection box with a list of unique track names
+ Updates the Track Selection box with a list of unique track names.
"""
_rt = self._pymxs.runtime
tracks = []
layout = self._box_tracks.layout()
+ selection = _rt.getCurrentSelection()
+ self._bar_progress.setMaximum(len(selection))
+ self._lbl_status.setText("Finding animated tracks...")
+
# Get track list from current selection
- for obj in _rt.getCurrentSelection():
- tracks = self._get_keyed_subtracks(obj, tracks)
+ for obj in selection:
+ tracks = self._get_keyed_subtracks(obj, tracks, namesOnly=True)
+ self._bar_progress.setValue(self._bar_progress.value()+1)
+
+ # Update status label and progress bar
+ self._lbl_status.setText("Updating Track Selection list...")
+ self._bar_progress.setValue(0)
+ self._bar_progress.setMaximum(len(tracks))
# Clear the UI track list, repopulate with tracks we found
for i in range(layout.count()):
@@ -150,70 +172,118 @@ def _update_tracks(self):
index = 0
for track in tracks:
- layout.addWidget(QtW.QCheckBox(track.name))
+ layout.addWidget(QtW.QCheckBox(track))
widget = layout.itemAt(index).widget()
widget.setChecked(True)
+ self._bar_progress.setValue(self._bar_progress.value()+1)
index += 1
layout.addStretch()
+
+ self._lbl_status.setText("Found: %d Tracks in %d Objects" % (self._grn,
+ len(tracks),
+ len(_rt.getCurrentSelection())))
def _bake(self):
- print "Bake"
-
-
- def _get_keyed_subtracks(self, track, list=[]):
+ """
+ Update options, validate frame range, and bake selected tracks.
+ """
+ _rt = self._pymxs.runtime
+ _at = self._pymxs.attime
+ _animate = self._pymxs.animate
+
+ # Update options from GUI
+ self._options['start'] = self._spn_start.value()
+ self._options['end'] = self._spn_end.value()
+ self._options['nth'] = self._spn_nth.value()
+ self._options['pad'] = self._chk_pad.isChecked()
+ self._options['tracks'] = []
+ # Get selected tracks
+ layout = self._box_tracks.layout()
+ for i in range(layout.count()):
+ widget = layout.itemAt(i).widget()
+ if widget and widget.isChecked():
+ self._options['tracks'].append(str(widget.text()))
+
+ # Validate options
+ if self._options['start'] >= self._options['end']:
+ self._lbl_status.setText("ERROR: Start frame is after End!" % self._err)
+ return
+
+ # Apply padding if needed
+ if self._options['pad'] and ((self._options['end'] - self._options['start']) % self._options['nth']) > 0:
+ self._options['end'] += ((self._options['end'] - self._options['start']) % self._options['nth'])
+
+ # Bake selected objects / tracks
+ with self._pymxs.undo(True, 'Bake Selection'):
+ selection = _rt.getCurrentSelection()
+
+ # Update status label and progress bar
+ self._lbl_status.setText("Baking %d Objects..." % len(selection))
+ self._bar_progress.setValue(0)
+ self._bar_progress.setMaximum(len(selection))
+
+ for obj in selection:
+ tracks = self._get_keyed_subtracks(obj)
+ if len(tracks) == 0:
+ self._bar_progress.setValue(self._bar_progress.value()+1)
+ continue
+
+ for track in tracks:
+ # Skip track if it's not selected
+ if track.name not in self._options['tracks']:
+ continue
+
+ # Cache track values _at each frame, every n'th frame
+ anim = []
+ for t in range(self._options['start'], self._options['end']+1, self._options['nth']):
+ with _at(t):
+ anim.append(track.value)
+
+ # Now clear the track and write cached keys
+ _rt.deleteKeys(track)
+
+ anim_index = 0
+ with _animate(True):
+ for t in range(self._options['start'], self._options['end']+1, self._options['nth']):
+ with _at(t):
+ track.controller.value = anim[anim_index]
+ anim_index += 1
+
+ self._bar_progress.setValue(self._bar_progress.value()+1)
+
+ self._lbl_status.setText("Baked: %d tracks on %d objects" % (self._grn,
+ len(self._options['tracks']),
+ len(selection)))
+
+
+ def _get_keyed_subtracks(self, track, list=[], namesOnly=False):
"""
Crawl through track heirarchy, building a list of animated tracks. Exclude Position and Rotation parent tracks.
:param track: The track to start crawling from - also used when called recursively
:param list: The list of animated tracks, should start blank.
+ :param namesOnly: If true, only consider the track names for uniqueness.
:return: A list of all (unique) animated track objects below the initial track.
"""
_rt = self._pymxs.runtime
-
ignore = ['Transform', 'Position', 'Rotation']
# Only add this track to the list if it's a SubAnim, has a controller, and has been animated
if _rt.iskindof(track, _rt.SubAnim) and _rt.iscontroller(track.controller) and track.isanimated:
- if track not in list and track.name not in ignore:
+ if not namesOnly and track not in list and track.name not in ignore:
list.append(track)
+ elif namesOnly and track.name not in list and track.name not in ignore:
+ list.append(track.name)
# Always call self recursively on all children of this track
# Note: SubAnim list is 1-indexed. Thanks, Autodesk.
for i in range(1, track.numsubs + 1):
- list = self._get_keyed_subtracks(_rt.getSubAnim(track, i), list)
+ list = self._get_keyed_subtracks(_rt.getSubAnim(track, i), list, namesOnly)
return list
-def bake_anim(obj, start, end, n):
- tracks = get_keyed_subtracks(obj)
- print "%s has %d keyed tracks" % (obj.name, len(tracks))
- if len(tracks) == 0:
- return
-
- for track in tracks:
- print "Caching %s..." % track.name
- # Cache track values _at each frame, every n'th frame
- anim = []
- for t in range(start, end, n):
- with _at(t):
- anim.append(track.value)
-
- # Now clear the track and write cached keys
- _rt.deleteKeys(track)
-
- anim_index = 0
- with _animate(True):
- for t in range(start, end, n):
- # print anim[anim_index]
- with _at(t):
- track.controller.value = anim[anim_index]
- anim_index += 1
-
- print "Baked %s" % track.name
-
-
# --------------------
# Dialog Setup
# --------------------
diff --git a/bakeAllAnim_v01.ui b/bakeAllAnim_v01.ui
index 7fa27ad..8bff8cd 100644
--- a/bakeAllAnim_v01.ui
+++ b/bakeAllAnim_v01.ui
@@ -88,7 +88,7 @@
-
- If checked, and Nth Frame is set, we'll make sure to put a key on the End frame
+ If checked, make sure we place a keyframe on or after the End frame.
Pad Range?
@@ -338,7 +338,7 @@
Status Update
- Qt::AutoText
+ Qt::RichText