The CS3Tools Custom User Interface implements several very useful tools. This tutorial will concentrate on the Custom Keys Utility, a custom keys mechanism that gives one-touch access to 6 key frame settings that you use most when animating a Biped. The Custom Keys Utility uses 13 icon buttons. The first 6 are "Set custom Biped Keys". The next 6 are "Change Preset to current key". The following one is "Launch Preset Floater".
Once you have stored your favorite custom keys, animation workflow is greatly improved, since you spend little or no time tuning default key frame attributes that don't fit your current character's situation or style. Simply pose your character and click the custom key icon that fits your current situation.
Custom Keys are implemented via a new dedicated 3ds max Tool Bar, called Character Studio Tool. This tool bar is the home base for a growing number of MAXScript-enabled character studio commands. All operations associated with Tool Bars are available. For example, individual icons can be relocated, deleted, and renamed. Tool tips can also be customized, as implied above, to let you annotate each custom key type for rapid recall. Custom Keys are displayed on the Character Studio Tool Bar as color-coded "set key" icons, similar to the standard Biped red set key icon in the Biped Motion panel. Each color indicates a specific custom key type. There are two icons for each key type û one for setting a key, and second for storing the preset value, based on the currently selected track and key. The second icon û used for storing the preset values û displays a small black arrow pointing to the center of the icon û indicating that a value will be stored. Once you have set your keys as you like them, you may choose to delete the storage icons entirely, to save space. You can also float or re-dock the Custom Key icons to customize access to suit your style and needs. Additionally, you can launch a floating rollout of the same set of custom keys.
To use Custom Keys, simply set a Biped key as you normally would using the Biped Motion Panel and set its Key Info settings as you prefer. Next store that key for the currently selected track at the current frame into one of the six preset custom key icons using the set key icons with the black arrows.
The file named CS3Tools.cui is found in the 3ds max UI directory.
Choose Customize -> Load Custom UIà -> CS3Tools.cui
The user interface is displayed on the left side of the screen made with bitmapped buttons icons. You can also build a Tab Panel from scratch and manually add these icons by selecting from the Character Studio Tool category of the Customize UI Dialog.
See Macro Scripts and Defining Macro Scripts for additional details regarding Macro Scripts.
The icon buttons of CS3Tools.cui are displayed in the above image.
The bitmaps for the custom ui are installed into the 3ds max UI directory. See Creating Icon Bitmap Files for more information on creating and customizing icon bitmaps.
For access to Custom User Interface (CUI) files using MAXScript, see Custom User Interface Files. Note that the *.cui file format specification is not currently provided.
character studio 3 ships with the following Character Studio Tool files:
..\ui\CS3Tools.cui
..\Scripts\Startup\CS3Customkeys.ms
..\Scripts\Startup\CS3CustomKeys-presets.ini
..\Scripts\Startup\CS3convertBips.ms
..\Scripts\Startup\CS3Bip2BonesFloater.ms
..\Stdplugs\stdscripts\CS3CustomKeysPresetFloater.ms
Tutorial_Filename_CS3CustomKeysPresetFloater_ms bitmap files:
\3ds3.1\Cstudio\ui\Cstudio_16a.bmp, Cstudio_16i.bmp, Cstudio_24a.bmp, and Cstudio_24i.bmp.
Custom Keys operate similar to channel buttons on a car radio. Anytime you "dial in" a set of parameters for a particular Biped key using the standard Biped "Key Info" rollout, you can store those values in one of the 6 custom key buttons available, by clicking on the associated tool bar icon.
All of the standard Biped Key Info parameters are supported, including: ease to, ease from, tension, continuity, bias, IK Blend value, IK body/space setting, and pivot point information. This can be seen by reviewing the struct Biped_key defined in ..\Scripts\Startup\CS3Customkeys.ms.
Custom Keys do not store transformation information (like a pose). Only Key Info attributes that affect the interpolation of a Biped's motion are stored. This allows stylistic settings to be easily transferred from frame to frame and even from Biped to Biped.
Each Custom Key is stored in a structure with the name Biped_key. Here is the definition of Biped_key:
struct Biped_key (type, ikblend, ikspace, easefrom, easeto, tension, continuity, bias, ikAnkleTension, balanceFactor, dynamicsBlend, ballisticTension)
Here is the initial Custom Key definition for the buffer "Preset Key A". Note that it is global and therefore visible in all running MAXScript code and will hold its value until 3ds max is exited.
Global keya_buffer = Biped_key type:#body ikblend:0.1 ikspace:1 easefrom:0.0 easeto:0.0 tension:25.0 continuity:0.0 bias:25.0 ikAnkleTension:.8 balanceFactor:1 dynamicsBlend:1 ballisticTension:0.5
The CS3Tools.cui has 13 icon buttons. The first 6 are "Set custom Biped Key *". The next 6 are "Change Preset * to current key". The last one is "Launch Preset Floater".
Pressing the 7th icon, "Change Preset A to current key", will change the value of the specified Preset A Key's keya_buffer to the value of the currently selected key. This will trigger the execution of the code:
macros.run "Character Studio Tool" "Change_Preset_Key_A"
The line above will be displayed in the MacroRecorder if the MacroRecorder is enabled.
This macro, "Change_Preset_Key_A", is defined in the file \3ds3.1\ui\macroscripts\Character Studio Tool-Change_Preset_Key_A.mcr and contains:
macroScript Change_Preset_Key_A
category:"Character Studio Tool"
toolTip:"Change Preset A to current key"
Icon:#("cstudio",7)
(
keya_buffer = CS3Change_Key_FN keya_buffer
CS3Save_presets()
)
See Macro Scripts and Defining Macro Scripts for additional details on creating and using Macro Scripts.
Select the last icon, "Launch Preset Floater", in the CS3Tools.cui. The Preset Floater dialog is launched and displayed. There are 6 "Change *" buttons and 6 "Show *" buttons. The 6 "Change *" buttons on the floating rollout perform the same actions as the 6 icon buttons on the CS Custom Key toolbar.
Pressing the "Change A" button executes the following code from the file ..\Scripts\Startup\CS3CustomKeys.ms
on ChangeA pressed do
(
keya_buffer = CS3Change_Key_FN keya_buffer
CS3Save_presets()
)
Pressing the "Show A" button will display the current contents of the keya_buffer as seen here:
Note that in both cases of Changing Preset A, the same function "CS3Change_Key_FN" was called. Additionally, notice that CS3Save_presets() is called anytime that a preset is changed. CS3Save_presets() will save all key values to a file called CS3CustomKeys-presets.ini that can be included in next launch of CS3Customkeys.ms. This is accomplished by Save_presets formating the presets as valid MAXScript code and using the include statement to include the code in CS3Customkeys.ms. The included code redefines the global key_buffers already defined.
The file \3ds3.1\Scripts\Startup\CS3Customkeys.ms Tutorial_File_name_CS3Customkeys_ms contains the following functions:
fn Set_One_Key_FN
fn Set_Key_FN
fn CS3Save_Presets
fn CS3Show_key_FN
fn CS3Change_Key_FN
fn ControllerOf
fn RootOf
The functions Change_Key_FN, Save_Presets and Set_Key_FN will be reviewed in more detail below.
As seen in the above examples of "ChangeA", the Change_Key_FN function is called with a single parameter, the particular local_key_buffer, and in the above examples with the keya_buffer. The local_key_buffer will either be returned unmodified or it will be returned modified.
The values for easeto, easeFrom, tension, continuity, bias, and ikAnkleTension are stored for all valid keytypes while ikblend and ikspace is stored for only #body keys. For #vertical keys, dynamicsblend and ballistictension are also stored. For #horizontal, balancefactor is also stored.
See BipedKey:MAXObject for more details.
The pseudo code for the Change_Key_FN is as follows:
function Change_Key_FN local_key_buffer
if nothing selected return local_key_buffer
if more than one selected return local_key_buffer
if not of class Biped_Object return local_key_buffer
get root node of the biped and controller for this body part
If body part is COM and trackselection not vertical, horizontal or turning then
print skipped and return local_key_buffer
else
save controller selection
if in figuremode return local_key_buffer
if in motion mode return local_key_buffer
if in footstepmode return local_key_buffer
if save controller selection not undefined then
set my_index to getkeyindex my_controller slidertime
if a key was found then
set my_key to biped.getKey my_controller my_index
if (my_key.type equals #body) then
(
set local_key_buffer.ikblend to my_key.ikblend
set local_key_buffer.ikspace to my_key.ikspace
set local_key_buffer.ikAnkleTensio to my_key.ikAnkleTension
)
if (my_key.type equals #vertical) then
(
set local_key_buffer.dynamicsblend to my_key.dynamicsblend
set local_key_buffer.ballistictension to my_key.ballistictension
)
if (my_key.type equals #horizontal) then
(
set local_key_buffer.balancefactor to my_key.balancefactor
)
set local_key_buffer.easefrom to my_key.easeto
set local_key_buffer.easeto to my_key.easefrom
set local_key_buffer.tension to my_key.tension
set local_key_buffer.continuity to my_key.continuity
set local_key_buffer.bias to my_key.bias
set return modified local_key_buffer
else
return local_key_buffer
else
return local_key_buffer
The actual function can be seen here:
FN CS3Change_Key_FN local_key_buffer =
(
if ($selection.count == 0 ) then
(
messagebox "No object selected.\nPlease select a Biped Object first.\n"
return local_key_buffer
)
if ($selection.count > 1) then
(
messagebox "There are multiple objects selected.\nPlease select a single Biped Object first."
return local_key_buffer
)
if ((classof $) != Biped_Object) then
(
messagebox "The selected object is not a Biped Object.\n Please select a Biped Object first\n."
return local_key_buffer
)
my_root = $.controller.rootnode
my_controller = $.controller
if (my_root == $) then -- user is working on COM
(
if (my_controller.trackselection == 2) then
(
my_controller = my_controller.vertical.controller
)
else if (my_controller.trackselection ==1) then
(
my_controller = my_controller.horizontal.controller
)
else if (my_controller.trackselection == 3) then
(
my_controller = my_controller.turning.controller
)
else
(
msgtext = "'" + $.name + "' skipped.\n"
messagebox msgtext
return local_key_buffer
)
)
if (my_root.transform.controller.figuremode == true) then
(
messagebox "Change Preset Key is not supported in Figure Mode.\n Please exit Figure Mode first.\n"
return local_key_buffer
)
if (my_root.transform.controller.motionmode == true) then
(
messagebox "Change Preset Key is not supported in Motion Flow Mode.\n Please exit Motion Flow Mode first.\n"
return local_key_buffer
)
if (my_root.transform.controller.footstepmode == true) then
(
messagebox "Change Preset Key is not supported in Footstep Mode.\n Please exit Footstep Mode first.\n"
return local_key_buffer
)
if (my_controller != undefined) then
(
-- format "Controller: %\n" my_controller
my_index = getkeyindex my_controller slidertime
-- format "Key Index = %\n" my_index
if (my_index>0) then
(
my_key = biped.getKey my_controller my_index
-- format "key type is % \n" my_key.type
if (my_key.type == #body) then
(
local_key_buffer.ikblend = my_key.ikblend
local_key_buffer.ikspace = my_key.ikspace
local_key_buffer.ikAnkleTension = my_key.ikAnkleTension
)
if (my_key.type == #vertical) then
(
local_key_buffer.dynamicsblend = my_key.dynamicsblend
local_key_buffer.ballistictension = my_key.balistictension
)
if (my_key.type == #horizontal) then
(
local_key_buffer.balancefactor = my_key.balancefactor
)
local_key_buffer.easefrom = my_key.easeto
local_key_buffer.easeto = my_key.easefrom
local_key_buffer.tension = my_key.tension
local_key_buffer.continuity = my_key.continuity
local_key_buffer.bias = my_key.bias
return (local_key_buffer)
)
else
(
messagebox "Cannot change this preset because there is no key for the selected track at the current frame.\n Please advance the time slider to a frame where a key exists.\nOr select a track and frame where a key exists.\nOr set a key first.\n"
return (local_key_buffer)
)
)
else
(
messagebox "There is no controller for the selected track.\nYou may need to enable separate tracks.\n"
return (local_key_buffer)
)
)
-- *************************************************************
-- CS3Save_Presets - save all key values to a .ms file that can be re-read at next launch
-- *************************************************************
FN CS3Save_Presets =
(
my_path = scriptspath + "startup\CS3CustomKeys-presets.ini"
outfile = createfile my_path
format "-- This file was created by the CS3 Custom Key Macro \"Save_Key_Presets\" to store user-customized presets for Biped custom key macro tools\n" to:outfile
format "-- These values can be editted by hand for existing fields\n\n" to:outfile
k = keya_buffer
format "Global keya_buffer = Biped_key type:% ikblend:% ikspace:% easefrom:% easeto:% tension:% continuity:% bias:%\n" k.type k.ikblend k.ikspace k.easefrom k.easeto k.tension k.continuity k.bias to:outfile
k = keyb_buffer
format "Global keyb_buffer = Biped_key type:% ikblend:% ikspace:% easefrom:% easeto:% tension:% continuity:% bias:%\n" k.type k.ikblend k.ikspace k.easefrom k.easeto k.tension k.continuity k.bias to:outfile
k = keyc_buffer
format "Global keyc_buffer = Biped_key type:% ikblend:% ikspace:% easefrom:% easeto:% tension:% continuity:% bias:%\n" k.type k.ikblend k.ikspace k.easefrom k.easeto k.tension k.continuity k.bias to:outfile
k = keyd_buffer
format "Global keyd_buffer = Biped_key type:% ikblend:% ikspace:% easefrom:% easeto:% tension:% continuity:% bias:%\n" k.type k.ikblend k.ikspace k.easefrom k.easeto k.tension k.continuity k.bias to:outfile
k = keye_buffer
format "Global keye_buffer = Biped_key type:% ikblend:% ikspace:% easefrom:% easeto:% tension:% continuity:% bias:%\n" k.type k.ikblend k.ikspace k.easefrom k.easeto k.tension k.continuity k.bias to:outfile
k = keyf_buffer
format "Global keyf_buffer = Biped_key type:% ikblend:% ikspace:% easefrom:% easeto:% tension:% continuity:% bias:%\n" k.type k.ikblend k.ikspace k.easefrom k.easeto k.tension k.continuity k.bias to:outfile
close outfile
)
The pseudo code for the Set_Key_FN is as follows:
function Set_Key_FN local_key_buffer
store state of shift key
if nothing selected then return false
if one object selected and not a Biped Object then return false
if local_key_buffer undefined then return false
-- process multiple selections independently so that we can
-- support key setting of multiple bipeds
else for each of the items in the selection
( if current item not a Biped Object then get next item
set my_root to the root of the hierarchy
if my_root is in figuremode then get the next item
if my_root is in motionmode then get the next item
if my_root is in footstepmode then get the next item
set my_controller to the item's controller
if hierarchy root is the current item then
set my_controller to the current track ( horizontal / vertical / turning)
else get the next item
if shift key was pressed then
(
for each key in my_controller
if key is selected set values for key
)
else
(
add a new key to my_controller at the current time and select the new key
get the index of the newly created key
if the index is greater than 0 then
get the newly created key
if (my_key.type == #body) then
(
set my_key.ikblend to local_key_buffer.ikblend
set my_key.ikspace to local_key_buffer.ikspace
)
set my_key.easeto to local_key_buffer.easefrom
set my_key.easefrom to local_key_buffer.easeto
set my_key.tension to local_key_buffer.tension
set my_key.continuity to local_key_buffer.continuity
set my_key.bias to local_key_buffer.bias
)
The actual function can be seen here:
FN Set_Key_FN local_key_buffer =
(
if (keyboard.shiftpressed) then
keyboardshiftpressed_atstart = true
else
keyboardshiftpressed_atstart = false
if ($selection.count == 0 ) then
(
messagebox "There are no objects selected.\nPlease select a Biped Object first.\n"
return false
)
-- Showclass "Biped_" reports Biped_Object:GeometryClass{9125,0}
if (($selection.count == 1) and ((classof $) != Biped_Object)) then
(
messagebox "The selected object is not a Biped Object.\nPlease select a Biped Object first\n."
return false
)
if (local_key_buffer == undefined) then
(
messagebox "This preset key has not been initialized.\nPlease select a key first, and set its preset\n."
return false
)
-- process multiple selections independently so that we can support key setting of multiple bipeds
else for i=1 to $selection.count do
(
if ((classof $selection[i]) != Biped_Object) then
(
msgtext = "The selected object '" + $selection[i].name + "' is not a Biped Object.\nObject Skipped.\n"
messagebox msgtext
continue
)
my_root = $selection[i].controller.rootnode
if (my_root.transform.controller.figuremode == true) then
(
msgtext = "'" + $selection[i].name + "' is in Figure Mode.\nSet Key is not supported in Figure Mode.\nPlease exit Figure Mode for this object first.\nObject Skipped.\n"
messagebox msgtext
continue
)
if (my_root.transform.controller.motionmode == true) then
(
msgtext = "'" + $selection[i].name + "' is in Motion Flow Mode.\nSet Key is not supported in Motion Flow Mode.\nPlease exit Motion Flow Mode for this object first.\nObject Skipped.\n"
messagebox msgtext
continue
)
if (my_root.transform.controller.footstepmode == true) then
(
msgtext = "'" + $selection[i].name + "' is in Footstep Mode.\nSet Key is not supported in Footstep Mode.\nPlease exit Footstep Mode for this object first.\nObject Skipped.\n"
messagebox msgtext
continue
)
-- find the root controller of this object
-- my_controller = (controllerof ($selection[i]))
my_controller = $selection[i].controller
if (my_root == selection[i) then -- user is working on COM
(
if (my_controller.trackselection == 1) then
(
my_controller = my_controller.horizontal.controller
)
else if (my_controller.trackselection == 2) then
(
my_controller = my_controller.vertical.controller
)
else if (my_controller.trackselection == 3) then
(
my_controller = my_controller.turning.controller
)
else
(
msgtext = "'" + $selection[i].name + "' skipped.\n"
messagebox msgtext
continue
)
)
-- loop through all keys and call only if a key is selected
if (keyboardshiftpressed_atstart == true) then -- user wants to process all selected keys in this track
(
found_selected_keys = 0
for temp_index=1 to (numkeys my_controller) do
(
if (iskeyselected my_controller temp_index) then
(
set_one_key_FN my_controller temp_index local_key_buffer -- set this key to the local key buffer
found_selected_keys += 1
)
)
if (found_selected_keys == 0 ) then
(
msgtext = "Warning: No keys selected.\nNo changes made to " + $selection[i].name + "."
messagebox (msgtext)
)
else
(
msgtext = "Changed " + (found_selected_keys as string) + " keys in " + $selection[i].name + "."
messagebox (msgtext)
)
)
else -- (default) user wants to process just the current key
(
temp_index = getkeyindex my_controller slidertime
-- format "index of key at this frame (temp_index) is %\n" temp_index
if (temp_index == 0) then -- no key at this frame yet, let's create one...
(
biped.addNewKey my_controller slidertime #select
temp_index = getkeyindex my_controller slidertime
)
-- temp_index should now indicate the ordinal index of the key for this track, 1st, 2nd, 3rd, etc.
-- if it is still zero, then the user has selected a track or mode that prevents adding a key, like footsteps
if (temp_index >0) then -- key was created ok
(
set_one_key_FN my_controller temp_index local_key_buffer
)
else -- addnewkey failed, probably because the footstep track is selected
(
msgtext = "The selected track '" + ($selection[i]).name + "' does not support keys.\n Object Skipped.\n"
messagebox msgtext
)
)
)
return true
)
You can name the key button accordingly to help you remember the purpose for that key type: "heel down", "finger point", "hand grab", etc.
To change the tooltip for the buttons on the CS3Tools.cui toolbar, right-click the button, choose "Edit Button Appearance", modify the text in the "Tooltip:" text box and click OK.
The number of different settings that CS3Tools.cui supports can be extended. Two new macro files (*.mcr) for each new additional custom key, one for "Set" and one for "Change Preset", need to be added to the ..\ui\macroscripts directory and identical macros need to be added to ..\Scripts\Startup\CS3Customkeys.ms as well. Global key*_buffer's that matched the number of pairs of new macros added to the CS3Tools.cui need to be inserted into the file ..\Scripts\Startup\CS3Customkeys.ms Tutorial_File_name_CS3Customkeys_ms. Finally, the new macro files (*.mcr) have to be incorprated into the CS3Tools.cui. See Macro Scripts and Defining Macro Scripts for additional details.
See also