PDA

View Full Version : Sparse Keyframe script


Jimstein
10-03-2011, 07:30 PM
This script is an attempt to replicate the keying behavior I got used to in Lightwave3D, in combination with a hotkey the script or plugin becomes a very powerful workflow enhancement

This is a Python-”shortcut”-script for manually picking and setting a key with KeyFrameSelection on translate/rotate/scale. Instead of keying everything (like the Record button does by setting a key on every channel) this will only record the motion channels you've enabled; not dense keying. As an added bonus you dont have to move the time scrubber before generating the keys, this means that the current frame is the key generating source frame but you decide on what frame the keys will be inserted at, so you do not need to be afraid of overwriting the keys on the current frame just because you did forget to move the time slider to the location you want to insert the key.

Updated script and plugin on the 27th of October 2011




# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import c4d
from c4d import gui





# -------------------------------------------
# GLOBALS
# -------------------------------------------

# unique ID
PLUGIN_VERSION = 1.01
PLUGIN_NAME = "SpaKey"
PLUGIN_HELP = "Sparse Keyframe"
PLUGIN_ABOUT = """
(C) 2011 Jimmy Esbjörnsson jimstein3d@hotmail.com
This is a Python-”shortcut”-script for manually picking and setting a key with KeyFrameSelection on translate/rotate/scale. Instead of keying everything (like the Record button does by setting a key on every channel) this will only record the motion channels you've enabled; not dense keying. As an added bonus you dont have to move the time scrubber before generating the keys, this means that the current frame is the key generating source frame but you decide on what frame the keys will be inserted at, so you do not need to be afraid of overwriting the keys on the current frame just because you did forget to move the time slider to the location you want to insert the key.
This script is an attempt to replicate the keying behavior I got used to in Lightwave3D
I don't consider this script finished so suggestions and/or improvements are welcome.
"""



# Element IDs
GROUP_ID1=1000
FRAME=1001
KEY=1002
PX=1004
PY=1005
PZ=1006
RX=1007
RY=1008
RZ=1009
SX=1010
SY=1011
SZ=1012
# ------------------------------------------------------
# User Interface
# ------------------------------------------------------
class SpaKeysDialog(gui.GeDialog):
def CreateLayout(self):
#creat the layout of the dialog
self.GroupBegin(GROUP_ID1, c4d.BFH_SCALEFIT, 3, 1)
self.AddCheckbox(PX, c4d.BFH_SCALEFIT, initw=0, inith=0, name="P.X")
self.AddCheckbox(PY, c4d.BFH_SCALEFIT, initw=0, inith=0, name="P.Y")
self.AddCheckbox(PZ, c4d.BFH_SCALEFIT, initw=0, inith=0, name="P.Z")
self.AddCheckbox(RX, c4d.BFH_SCALEFIT, initw=0, inith=0, name="R.H")
self.AddCheckbox(RY, c4d.BFH_SCALEFIT, initw=0, inith=0, name="R.P")
self.AddCheckbox(RZ, c4d.BFH_SCALEFIT, initw=0, inith=0, name="R.B")
self.AddCheckbox(SX, c4d.BFH_SCALEFIT, initw=0, inith=0, name="S.X")
self.AddCheckbox(SY, c4d.BFH_SCALEFIT, initw=0, inith=0, name="S.Y")
self.AddCheckbox(SZ, c4d.BFH_SCALEFIT, initw=0, inith=0, name="S.Z")
self.AddButton(KEY, c4d.BFH_SCALEFIT, name="Create Key At")
self.AddEditNumberArrows(FRAME, c4d.BFH_SCALEFIT)
self.GroupEnd()

return True

def InitValues(self):
#initiate the gadgets with values
self.SetTitle("SpaKey")
self.SetReal(FRAME, doc.GetTime().GetFrame(doc.GetFps()))
self.SetBool(PX,False)
self.SetBool(PY,False)
self.SetBool(PZ,False)
self.SetBool(RX,False)
self.SetBool(RY,False)
self.SetBool(RZ,False)
self.SetBool(SX,False)
self.SetBool(SY,False)
self.SetBool(SZ,False)

return True

def Command(self, id, msg):
#handle user input
if id==KEY:
posOn = c4d.IsCommandChecked(12417) # Position
scaleOn = c4d.IsCommandChecked(12418) # Scale
rotOn = c4d.IsCommandChecked(12419) # Rotation

#turn on
if posOn is False: c4d.CallCommand(12417) # Position
if scaleOn is False: c4d.CallCommand(12418) # Scale
if rotOn is False: c4d.CallCommand(12419) # Rotation

frame = int(self.GetReal(FRAME)) #int(gui.RenameDialog(frame))
doc.SetTime(c4d.BaseTime(frame, doc.GetFps())) #sets time slider to frame 0

selected = doc.GetActiveObjects(False) #Find the active objects

if len(selected) == 0:
gui.MessageDialog("You must select an Object!")
return False

doc.StartUndo()

for obj in selected:
obj.ClearKeyframeSelection() #Clears the Keyframe Selections


if self.GetBool(PX) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION), c4d.DescLevel(c4d.VECTOR_X))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection
if self.GetBool(PY) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION), c4d.DescLevel(c4d.VECTOR_Y))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection
if self.GetBool(PZ) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION), c4d.DescLevel(c4d.VECTOR_Z))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection

if self.GetBool(SX) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_SCALE), c4d.DescLevel(c4d.VECTOR_X))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection
if self.GetBool(SY) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_SCALE), c4d.DescLevel(c4d.VECTOR_Y))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection
if self.GetBool(SZ) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_SCALE), c4d.DescLevel(c4d.VECTOR_Z))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection

if self.GetBool(RX) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_ROTATION), c4d.DescLevel(c4d.VECTOR_X))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection
if self.GetBool(RY) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_ROTATION), c4d.DescLevel(c4d.VECTOR_Y))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection
if self.GetBool(RZ) is True:
id = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_ROTATION), c4d.DescLevel(c4d.VECTOR_Z))
obj.SetKeyframeSelection(id, 1) #Adds a Keyframe Selection

c4d.CallCommand(12410) #Record Button
obj.ClearKeyframeSelection() #Clears the Keyframe Selections

#turn back off
if posOn is False: c4d.CallCommand(12417) # Position
if scaleOn is False: c4d.CallCommand(12418) # Scale
if rotOn is False: c4d.CallCommand(12419) # Rotation

c4d.EventAdd()
doc.EndUndo()

if (c4d.GetC4DVersion() < 13000):
self.Close()
return True


# ----------------------------------------------------
# Main
# ----------------------------------------------------
if __name__=='__main__':
dlg = SpaKeysDialog()
if (c4d.GetC4DVersion() < 13000):
#R12 version...R12 doesn't support ASYNC dialogs
dlg.Open(c4d.DLG_TYPE_MODAL, defaultw=50, defaulth=50)
else:
dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=50, defaulth=50)
c4d.EventAdd()

Scott Ayers
10-03-2011, 08:00 PM
I haven't tested it very much. But there's one small problem at the end for R12 users.
Python dialogs can't be Asynchronous like Coffee and C++ dialogs in R12. It was something they added in R13.
So your dialog closes as soon as it opens if you use it in R12.

To fix that the last section should be this: if __name__=='__main__':
dlg = ExampleDlg()
dlg.Open(c4d.DLG_TYPE_MODAL, defaultw=50, defaulth=50)
c4d.EventAdd()


Here's another way to do it that automatically checks the version and runs the right code: if __name__=='__main__':
dlg = ExampleDlg()

if (c4d.GetC4DVersion()>=13000):
print "R13"
dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=50, defaulth=50) #R12 version...R12 doesn't support ASYNC dialogs
else:
print "R12"
dlg.Open(c4d.DLG_TYPE_MODAL, defaultw=50, defaulth=50)
c4d.EventAdd()

-ScottA

Focus3D
10-04-2011, 07:41 PM
Nice Work.
To avoid error message to the console, you could also add the code betwen ## ##:

selected = doc.GetActiveObjects(False) #Find the active objects
##
if len(selected) == 0:
gui.MessageDialog("You must select an Object!")
return False
##
for obj in selected:

NiklasR
10-05-2011, 01:43 PM
This is a very very dirty workaround actually.
But if you really don't want to write a plugin, you can make your dialog asynchronous like this:

from os.path import join # or any other function !
# ...
if __name__ == "__main__":
# ...
dlg = ExampleDlg()
dlg.Open(c4d.DLG_TYPE_ASYNC)
join.dlg = dlg # saves the dialog instance from the garbage collector

You should free the dialog after the dialog was closed.
class ExampleDlg(GeDialog):
#...
def DestroyWindow(self):
if hasattr(join, "dlg"):
del join.dlg

Jimstein
10-27-2011, 08:22 AM
Thanks for all the help boys! I have now done the suggested modifications so the script in the 1st post of this thread is comparable with R12. The original keyframe dialog in Lightwave3D was also modal so I added that the dialog need to close on each new key – like it did in Lightwave3D, but stays open in R13 so one can continue to keyframe on the fly.

I also did a plugin-version of the script – probably it will now behave the same in R12 as in R13?

I still think the GUI still sucks but at least it does the job I want.

CGTalk Moderation
10-27-2011, 08:22 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.