// =========================================================================== // Copyright 2018 Autodesk, Inc. All rights reserved. // // Use of this software is subject to the terms of the Autodesk license // agreement provided at the time of installation or download, or which // otherwise accompanies this software in either electronic or hard copy form. // =========================================================================== // // hikInputSourceUtils.mel // // Description: // Utility methods Related to the update of the input source. This should not contain // any UI code. All code that references UI or triggers UI // updates should live in the right *UI.mel or *Operations.mel // /////////////////////////////////////////////////////////////////////// // Local methods... /////////////////////////////////////////////////////////////////////// proc string createRetargeterNode() { string $ret = `createNode "HIKRetargeterNode"`; setAttr ($ret+".isHistoricallyInteresting") 0; return $ret; } proc cleanCharacterRetargetingLayers( string $pCharacter ) { if( !`optionVar -exists hikCleanRetargetingAnimLayers` ) { optionVar -intValue hikCleanRetargetingAnimLayers 1; } if( `optionVar -q hikCleanRetargetingAnimLayers` ) { string $layers[ ] = hikGetRetargetCharacters( $pCharacter, 1 ); string $layersToRemove[ ]; for( $i = 0; $i < size( $layers ); $i += 2 ) { string $layer = $layers[ $i + 1 ]; if( size( $layer ) > 0 ) $layersToRemove[ size( $layersToRemove ) ] = $layer; } if( size( $layersToRemove ) > 0 ) layerEditorDeleteAnimLayer( $layersToRemove ); } } proc string getDirectRetargeter( string $pDstCharacter, string $pSrcCharacter, int $pCreate ) { // Find the retargeter that is not driving the Rig string $lDstRetargeters[] = hikGetRetargeterFromCharacter($pDstCharacter); string $lDstRetargeter; for( $retargeter in $lDstRetargeters ) { string $connections[] = `listConnections -type HIKState2FK ($retargeter+".OutputCharacterState")`; if( size( $connections ) == 0 ) { $lDstRetargeter = $retargeter; break; } } // If we are being asked to create a retargeter but one already exists this can cause problems. // To avoid them, we explicitly delete any existing retargeters and assign the retargeter string // to an appropriate value. if ( $pCreate && ( size($lDstRetargeter)>0 ) ) { delete $lDstRetargeter; $lDstRetargeter = ""; } if( ($lDstRetargeter =="")&&($pCreate) ) { $lDstRetargeter = createRetargeterNode(); connectAttr ($pDstCharacter+".OutputCharacterDefinition") ($lDstRetargeter+".InputCharacterDefinitionDst"); // Connect Reference from destination character into retargeter node string $lDstReference = $pDstCharacter + ".Reference"; string $lReference[] = `listConnections $lDstReference`; if( size($lReference) > 0) { string $lInputReferenceGX = $lDstRetargeter + ".referenceGX"; string $lOutputReferenceGX = $lReference[0] + ".worldMatrix"; connectAttr -f $lOutputReferenceGX $lInputReferenceGX; } // Connect property state which contains retargeting parameters string $lDstPropState = hikGetProperty2StateFromCharacter($pDstCharacter); if( $lDstPropState != "" ) { connectAttr -f ($lDstPropState + ".OutputPropertySetState") ($lDstRetargeter + ".InputDstPropertySetState"); } } if( ($lDstRetargeter !="")&&($pSrcCharacter != "" ) ) { catchQuiet( `connectAttr -f ($pSrcCharacter+".OutputCharacterDefinition") ($lDstRetargeter+".InputCharacterDefinitionSrc")` ); string $lSrcPropState = hikGetProperty2StateFromCharacter($pSrcCharacter); if( $lSrcPropState != "" ) { catchQuiet( `connectAttr -f ($lSrcPropState + ".OutputPropertySetState") ($lDstRetargeter + ".InputSrcPropertySetState")` ); } string $lSrcSK2State = hikGetOrBuildStateFromSk($pSrcCharacter); hikConnectCharacterStateObject( $lSrcSK2State, $lDstRetargeter ); } return $lDstRetargeter; } proc string buildRetargetFromState(string $pCharacterDst, string $pCharacterSrc, string $StateObj) { string $retargeter = createRetargeterNode(); string $lCharacterSrc = hikGetCharacterDefinition($pCharacterSrc); string $lSrcPropState = hikGetProperty2StateFromCharacter($lCharacterSrc); string $lDstPropState = hikGetProperty2StateFromCharacter($pCharacterDst); if( $lSrcPropState == "" ) { $lSrcPropState = hikCreatePropertyState(); } if( $lDstPropState == "" ) { $lDstPropState = hikCreatePropertyState(); } //connect character definition string $OutputSrc = $lCharacterSrc + ".OutputCharacterDefinition"; string $InputSrc = $retargeter+ ".InputCharacterDefinitionSrc"; connectAttr -f $OutputSrc $InputSrc; connectAttr -f ($lSrcPropState + ".OutputPropertySetState") ($retargeter + ".InputSrcPropertySetState"); connectAttr -f ($lDstPropState + ".OutputPropertySetState") ($retargeter + ".InputDstPropertySetState"); if($pCharacterDst != "") { string $OutputDst = $pCharacterDst + ".OutputCharacterDefinition"; string $InputDst = $retargeter+ ".InputCharacterDefinitionDst"; connectAttr -f $OutputDst $InputDst; // Connect Reference from destination character into retargeter node string $lDstReference = $pCharacterDst + ".Reference"; string $lReference[] = `listConnections $lDstReference`; if( size($lReference) > 0) { string $lInputReferenceGX = $retargeter + ".referenceGX"; string $lOutputReferenceGX = $lReference[0] + ".worldMatrix"; connectAttr -f $lOutputReferenceGX $lInputReferenceGX; } } hikConnectCharacterStateObject($StateObj, $retargeter); return $retargeter; } proc string[] getStateToSk( string $pCharacter ) { string $list[]; if( $pCharacter != "" && objExists( $pCharacter ) ) { string $list1[] = `listConnections -type HIKState2GlobalSK ($pCharacter+".OutputCharacterDefinition")`; string $list2[] = `listConnections -type HIKState2SK ($pCharacter+".OutputCharacterDefinition")`; $list = stringArrayCatenate( $list1, $list2 ); } return $list; } proc setActiveSolver( string $pCharacter, string $pSolver ) { // safe fail if solver is invalid however character is expected to be valid if( $pSolver == "" ) { return; } // Connect the solver to the state2SK or/and state2SKGlobal string $state2Sks[] = getStateToSk( $pCharacter ); for( $state2Sk in $state2Sks ) { catchQuiet( `connectAttr -force ($pSolver+".OutputCharacterState") ($state2Sk+".InputCharacterState" )`); } } proc string getActiveSolver( string $pCharacter ) { // Will return the solver that is currently active for a character // This solver can be the the character solver itself // or a retargeter or a liveClient node string $state2Sks[] = getStateToSk( $pCharacter ); for( $state2Sk in $state2Sks ) { string $solvers[] = `listConnections -s 1 -d 0 ( $state2Sk+".InputCharacterState")`; if( size( $solvers ) > 0 ) { return $solvers[0]; } } return ""; } proc setDirectCharacterInput(string $pCharacter, string $pNewCharacterSrc ) { // TODO // Find retargeter or create it if it does not exist // set it as the active solver string $solver = getDirectRetargeter( $pCharacter, $pNewCharacterSrc, 1 ); setActiveSolver( $pCharacter, $solver ); hikEnableCharacter( $pCharacter, true ); } // Generic change Character Retarget Input Character proc changeCharacterInput(string $pCharacter, string $pNewCharacterSrc,string $layerName,string $controlRigLayerName ) { string $lSrcStateOut; string $lDstRetargeter; string $lSrcState; ////////////////// // SrcCharacter // ////////////////// { string $lSrcSK2State = hikGetOrBuildStateFromSk($pNewCharacterSrc); $lSrcStateOut = $lSrcSK2State + ".OutputCharacterState"; $lSrcState = $lSrcSK2State; } ////////////////// // DstCharacter // ////////////////// string $lDstControlRig = hikGetControlRig($pCharacter); if( size($lDstControlRig) ) // With Dst Rig { if($layerName == "") { string $errorMustUseLayersMsg = (uiRes("m_hikInputSourceUtils.kCreateLayer")); error($errorMustUseLayersMsg); return; } int $overrideLayer = `animLayer -q -override $layerName`; string $currAttrs[] = `animLayer -q -at $layerName`; if(!$overrideLayer && (size($currAttrs) > 0)) { string $warningLayerSetToOverrideMsg = (uiRes("m_hikInputSourceUtils.kNonEmptyLayer")); warning($warningLayerSetToOverrideMsg); } $lDstRetargeter = buildRetargetFromState($pCharacter, $lSrcState, $lSrcState); // Does everything, State and Definition string $lDstRetargeterOut = $lDstRetargeter + ".OutputCharacterState"; string $lDstState2IK = hikBuildEffectorFromCharacterState($lDstRetargeter, $lDstControlRig,$layerName); // Connects both ends string $lDstState2FK = hikBuildFKFromState($lDstControlRig,$layerName); string $lDstState2FKIn = $lDstState2FK + ".InputCharacterState"; connectAttr -f $lDstRetargeterOut $lDstState2FKIn; // Put the layer in override mode. This is important, otherwise the result may look like garbage as it // is added to the results from the layers below. if(!$overrideLayer) animLayer -edit -override 1 $layerName; if($controlRigLayerName != "") { // Legacy functionality, hikAddCharacterToLayer can be invoken anytime, no need to crate the // extra layer only when setting up a character pipe hikAddCharacterToLayer( $pCharacter, $controlRigLayerName, 1 ); } } else // No Dst Rig { if($layerName != "") { string $errorCantUseLayersMsg = (uiRes("m_hikInputSourceUtils.kCantUseLayer")); error($errorCantUseLayersMsg); return; } string $lDstRetargeters[] = hikGetRetargeterFromCharacter($pCharacter); if( size($lDstRetargeters) ) // With Dst Retargeter { // State $lDstRetargeter = $lDstRetargeters[0]; string $lDstRetargeterIn = $lDstRetargeter + ".InputCharacterState"; connectAttr -f $lSrcStateOut $lDstRetargeterIn; // Definition string $lSrcDefOut = $pNewCharacterSrc + ".OutputCharacterDefinition"; string $lDstDefIn = $lDstRetargeter + ".InputCharacterDefinitionSrc"; connectAttr -f $lSrcDefOut $lDstDefIn; } else // No Dst Retargeter { $lDstRetargeter = buildRetargetFromState($pCharacter, $lSrcState, $lSrcState); // Does everything, State and Definition string $lDstRetargeterOut = $lDstRetargeter + ".OutputCharacterState"; // State string $lDstState2SK = hikBuildSkFromState($pCharacter); // Does everything, State and Definition string $lDstState2SKIn = $lDstState2SK + ".InputCharacterState"; connectAttr -f $lDstRetargeterOut $lDstState2SKIn; } } } proc setLayeredCharacterInput(string $pCharacter, string $pNewCharacterSrc ) { // Ensure that we have a rig on the destination first // if( size( hikGetControlRig( $pCharacter ) ) == 0 ) { hikDoCreateControlRig( $pCharacter, hikGetCharacterScale( $pCharacter ) ); hikSetRigLookAndFeel( $pCharacter, hikGetLastSelectedRigStyle( ) ); } string $solver = hikGetSolverNodeFromCharacter( $pCharacter ); // Ensure that we arent in InputStance if( $solver != "" ) { setAttr ( $solver+".InputStance" ) 0; } if( !`optionVar -exists hikCleanRetargetingAnimLayers` ) { optionVar -intValue hikCleanRetargetingAnimLayers 1; } // Mute all the layers that are used for retargeting int $alreadyConnected = 0; if( `optionVar -q hikCleanRetargetingAnimLayers` ) cleanCharacterRetargetingLayers( $pCharacter ); else { string $charactersLayers[ ] = hikGetRetargetCharacters( $pCharacter, 1 ); for( $i = 0; $i< size($charactersLayers);$i+=2 ) { string $character = $charactersLayers[$i]; string $layer = $charactersLayers[$i+1]; if( $layer != "" ) { if( isSameObject( $pNewCharacterSrc, $character ) ) { animLayer -e -mute false -lock false -weight 1 $layer; $alreadyConnected = 1; } else { animLayer -e -mute true -lock true -weight 0 $layer; } } } } if( !$alreadyConnected ) { // Could not enable associated layer, create a retargeting pipeline // string $lDstControlRig = hikGetControlRig($pCharacter); string $layerName = `animLayer -override 1 -passthrough 1 "RetargetLayer"`; changeCharacterInput( $pCharacter, $pNewCharacterSrc, $layerName, "" ); } // ensure that the state2sk is connected hikEnableCharacter( $pCharacter, true ); hikSetLiveState( $pCharacter, 0 ); setActiveSolver( $pCharacter, $solver ); } /////////////////////////////////////////////////////////////////////// // Global methodes... /////////////////////////////////////////////////////////////////////// global proc hikSetCharacterInput(string $pCharacter, string $pNewCharacterSrc ) { string $lGlobal2SkNode = hikGetStateToGlobalSk( $pCharacter, 0 ); if( $lGlobal2SkNode != "" ) { // Custom Rig setup, do a direct retargeter setDirectCharacterInput($pCharacter, $pNewCharacterSrc ); } else { if( `optionVar -exists hikRetargetUsingRig` && (`optionVar -q hikRetargetUsingRig` != 0 ) ) { // Legacy mode setLayeredCharacterInput($pCharacter, $pNewCharacterSrc ); } else { setDirectCharacterInput($pCharacter, $pNewCharacterSrc ); } } } global proc hikSetDirectCharacterInputNoRetarget(string $pCharacter, string $pNewCharacterSrc ) { // ENCS-84 // We have to do a retargeting because the received new src definition may not match the // destination one. During the LiveBake process, this happens because extra shoulders arent // sent to Mobu setDirectCharacterInput( $pCharacter, $pNewCharacterSrc ); } global proc string[] hikGetRetargetCharacters( string $pCharacter, int $pGetLayers ) { // This returns an interleaved list of Characters or Character/Layer pair string $result[]; string $retargeters[] = `listConnections -type "HIKRetargeterNode" -s 0 -d 1 ($pCharacter+".OutputCharacterDefinition" )`; for( $retargeter in $retargeters ) { // Check if this retargeter is a source or a destination for the current node if( `isConnected ($pCharacter+".OutputCharacterDefinition" ) ($retargeter+".InputCharacterDefinitionDst")` ) { string $srcCharacter[] = `listConnections -s 1 -d 0 -type "HIKCharacterNode" ($retargeter+".InputCharacterDefinitionSrc")`; if( size( $srcCharacter ) > 0 ) { $result[ size($result) ] = $srcCharacter[0]; if( $pGetLayers ) { string $layer = ""; // Look downstream of the retargeter to find the layer node string $state2FK[] = `listConnections -s 0 -d 1 -type "HIKState2FK" ($retargeter+".OutputCharacterState" )`; if( size( $state2FK ) > 0 ) { string $computeLocal[] = `listConnections -s 0 -d 1 -type "ComputeLocal" ($state2FK[0]+".HipsGX")`; if( size( $computeLocal ) > 0 ) { string $blendNode[] = `listConnections -s 0 -d 1 -type "animBlendNodeAdditiveRotation" ($computeLocal[0]+".rotateX")`; if( size( $blendNode ) > 0 ) { string $layers[] = `listConnections -s 0 -d 1 -type "animLayer" $blendNode[0]`; if( size( $blendNode ) > 0 ) { $layer = $layers[0]; } } } } $result[ size($result)] = $layer; } } } } return $result; } // NOTE: The following method is also called from hikcharactertoolcmd.cpp, // if you are about to modify it's name make sure to update the // cpp file too. // global proc int hikGetInputType( string $pCharacter ) { // Potential values are // 0 stance // 1 rig // 2 character Layered // 3 Character Direct // 4 Character Live Src // -1 no input, usually meaning that character is unlocked string $solver = getActiveSolver( $pCharacter ); if( $solver == "" ) { // No solver for that character, return -1; } string $solverType = `nodeType $solver`; if( $solverType == "HIKRetargeterNode" ) { return 3; } if( $solverType == "HIKCharacterStateClient" ) { return 4; } if( $solverType != "HIKSolverNode" ) { // Unknown source return -1; } int $result = 0; // First check if in StanceInput if( `getAttr ($solver+".InputStance")` == 0 ) { // Not in stance, then assume a rig input $result = 1; // Then check if any retargeting layer has a wait > .5 string $charactersLayers[] = hikGetRetargetCharacters( $pCharacter, 1 ); for( $i = 0; $i< size($charactersLayers);$i+=2 ) { string $layer = $charactersLayers[$i+1]; if( $layer != "" && `getAttr ($layer+".weight")` > 0.5 ) { $result = 2; break; } } } return $result; } global proc string hikGetRetargetCharacterInput( string $pCharacter ) { string $result; // TODO: Check for direct character first string $charactersLayers[] = hikGetRetargetCharacters( $pCharacter, 1 ); for( $i = 0; $i< size($charactersLayers);$i+=2 ) { string $character = $charactersLayers[$i]; string $layer = $charactersLayers[$i+1]; if( $layer == "" || `getAttr ($layer+".weight")` > 0.5 ) { $result = $character; break; } } return $result; } global proc hikSetRigInput( string $pCharacter ) { string $solver = hikGetSolverNodeFromCharacter( $pCharacter ); // Ensure that we arent in InputStance if( $solver != "" ) { setAttr ($solver+".InputStance" ) 0; } // If this character does not have a control rig create one if( size( hikGetControlRig( $pCharacter ) ) == 0 ) { hikDoCreateControlRig( $pCharacter, hikGetCharacterScale( $pCharacter ) ); hikSetRigLookAndFeel( $pCharacter, hikGetLastSelectedRigStyle( ) ); } // Mute all the layers that are used for retargeting string $charactersLayers[] = hikGetRetargetCharacters( $pCharacter, 1 ); for( $i = 0; $i< size($charactersLayers);$i+=2 ) { string $layer = $charactersLayers[$i+1]; if( $layer != "" ) { animLayer -e -mute true -lock true -weight 0 $layer; } } setActiveSolver( $pCharacter, $solver ); hikEnableCharacter( $pCharacter, true ); hikSetLiveState( $pCharacter, 0 ); cleanCharacterRetargetingLayers( $pCharacter ); } global proc hikSetInactiveStanceInput( string $pCharacter ) { string $solver = hikGetSolverNodeFromCharacter( $pCharacter ); if( $solver != "" ) { setAttr ($solver+".InputStance" ) 1; } setActiveSolver( $pCharacter, $solver ); hikEnableCharacter( $pCharacter, 2 ); hikSetLiveState( $pCharacter, 0 ); cleanCharacterRetargetingLayers( $pCharacter ); } global proc hikSetStanceInput( string $pCharacter ) { string $solver = hikGetSolverNodeFromCharacter( $pCharacter ); if( $solver != "" ) { setAttr ($solver+".InputStance" ) 1; } setActiveSolver( $pCharacter, $solver ); hikEnableCharacter( $pCharacter, true ); hikSetLiveState( $pCharacter, 0 ); cleanCharacterRetargetingLayers( $pCharacter ); } global proc hikSetLiveCharacterInput( string $pCharacter ) { string $solver = hikGetLiveStateClient( $pCharacter, 1 ); hikSetLiveState( $pCharacter, 1 ); setActiveSolver( $pCharacter, $solver ); hikEnableCharacter( $pCharacter, true ); } /////////////////////////////////////////////////////////////////////// // Character Input Type Enum /////////////////////////////////////////////////////////////////////// // Stance global proc int hikIsCharacterInputTypeStance(string $character) { return (hikGetInputType($character) == 0); } // Rig global proc int hikIsCharacterInputTypeRig(string $character) { return (hikGetInputType($character) == 1); } // Layered global proc int hikIsCharacterInputTypeLayered(string $character) { return (hikGetInputType($character) == 2); } // Direct global proc int hikIsCharacterInputTypeDirect(string $character) { return (hikGetInputType($character) == 3); } // Live global proc int hikIsCharacterInputTypeLive(string $character) { return (hikGetInputType($character) == 4); } // No input, usually meaning that character is unlocked global proc int hikIsCharacterInputTypeNone(string $character) { return (hikGetInputType($character) == -1); }