global proc int enigmaMachine.cipher(int $input) { int $i; int $rotor1_cipher[] = {5, 11, 13, 6, 12, 7, 4, 17, 22, 26, 14, 20, 15, 23, 25, 8, 24, 21, 19, 16, 1, 9, 2, 18, 3, 10}; int $rotor2_cipher[] = {1, 10, 4, 11, 19, 9, 18, 21, 24, 2, 12, 8, 23, 20, 13, 3, 17, 7, 26, 14, 16, 25, 6, 22, 15, 5}; int $rotor3_cipher[] = {2, 4, 6, 8, 10, 12, 3, 16, 18, 20, 24, 22, 26, 14, 25, 5, 9, 23, 7, 1, 11, 13, 21, 19, 17, 15}; // rotors are 1-based for readability - decrement them all by 1 to make them 0-based for($i = 0; $i < 26; $i++) { $rotor1_cipher[$i] -= 1; $rotor2_cipher[$i] -= 1; $rotor3_cipher[$i] -= 1; } int $rotor1_inverse[26]; int $rotor2_inverse[26]; int $rotor3_inverse[26]; // now invert rotors so we can go backwards for($i = 0; $i < 26; $i++) { $rotor1_inverse[ ($rotor1_cipher[$i]) ] = $i; $rotor2_inverse[ ($rotor2_cipher[$i]) ] = $i; $rotor3_inverse[ ($rotor3_cipher[$i]) ] = $i; } // finally, the reflector (0-based) int $reflector[] = {24, 17, 20, 7, 16, 18, 11, 3, 15, 23, 13, 6, 14, 10, 12, 8, 4, 1, 5, 25, 2, 22, 21, 9, 0, 19}; // now get the rotor states int $offset1 = (`getAttr rotor1.offset`) % 26; int $offset2 = (`getAttr rotor2.offset`) % 26; int $offset3 = (`getAttr rotor3.offset`) % 26; // now do it! // it's coming out of this pin on rotor1 int $output = $rotor1_cipher[($input + $offset1) % 26]; // that pin's absolute position is this $output = ($output + (26 - $offset1)) % 26; // repeat for rotors 2 and 3 $output = $rotor2_cipher[($output + $offset2) % 26]; $output = ($output + (26 - $offset2)) % 26; $output = $rotor3_cipher[($output + $offset3) % 26]; $output = ($output + (26 - $offset3)) % 26; // now do the reflection $output = $reflector[$output]; // now go backwards $output = $rotor3_inverse[($output + $offset3) % 26]; $output = ($output + (26 - $offset3)) % 26; $output = $rotor2_inverse[($output + $offset2) % 26]; $output = ($output + (26 - $offset2)) % 26; $output = $rotor1_inverse[($output + $offset1) % 26]; $output = ($output + (26 - $offset1)) % 26; // DONE! return $output; } global proc enigmaMachine.resetAnimation() { // reset all animation select "key*" "light*" "rotor*" "pawl1" "pawl2" "pawl3" "pawl_axle" "light_material*"; cutKey -t ":"; currentTime 1; // make sure that the keys are all up select "key*"; move 0 0 0; select -cl; } proc int enigmaMachine.charToInt( string $char ){ switch($char) { case "A": return 0; case "B": return 1; case "C": return 2; case "D": return 3; case "E": return 4; case "F": return 5; case "G": return 6; case "H": return 7; case "I": return 8; case "J": return 9; case "K": return 10; case "L": return 11; case "M": return 12; case "N": return 13; case "O": return 14; case "P": return 15; case "Q": return 16; case "R": return 17; case "S": return 18; case "T": return 19; case "U": return 20; case "V": return 21; case "W": return 22; case "X": return 23; case "Y": return 24; case "Z": return 25; } return -1; } global proc enigmaMachine.enterString( string $text ) { enigmaMachine.resetAnimation(); // setup is now done - now go through each letter of the input string for($i = 1; $i <= size($text); $i++) { // need to convert the i-th char of the string to a number string $char = `substring $text $i $i`; int $input = `enigmaMachine.charToInt($char)`; int $startTime = `currentTime -q`; // first, plug in the pawl axle rotation for this whole step copyKey -t ":" pawl_axle_forward; pasteKey -t $startTime pawl_axle; ///////////////////////////////////////////////////////////////////// // // ADVANCE ROTORS // ///////////////////////////////////////////////////////////////////// // make sure the rotors are keyed for their current position setKeyframe "rotor1.offset" "rotor2.offset" "rotor3.offset"; // next, advance all of the necessary rotors for this step currentTime ($startTime + 29); int $rotorStart[3]; $rotorStart[0] = `getAttr rotor1.offset`; $rotorStart[1] = `getAttr rotor2.offset`; $rotorStart[2] = `getAttr rotor3.offset`; // if rotor 2 is in the step position, move up rotor #3 if( `getAttr rotor2.offset` == 4) setAttr rotor3.offset ($rotorStart[2] + 1); // rotor 2 advances if rotor1 is in step position, OR if rotor3 just advanced (this is "double-stepping") if( `getAttr rotor1.offset` == 16 || `getAttr rotor3.offset` == $rotorStart[2] + 1) setAttr rotor2.offset ($rotorStart[1] + 1); // and rotor 1 always advances setAttr rotor1.offset ($rotorStart[0] + 1); // now set keys for all rotors at these new positions setKeyframe "rotor1.offset" "rotor2.offset" "rotor3.offset"; ///////////////////////////////////////////////////////////////////// // // MOVE PAWLS // ///////////////////////////////////////////////////////////////////// currentTime $startTime; // first, move all the pawls up // pawl1 always advances notch to notch copyKey -t ":" -attribute "rotateX" pawl_notch_forward_notch; pasteKey -t $startTime pawl1; // pawl2 advances notch to notch only if rotor1 is in step position if( `getAttr rotor1.offset` == 16) copyKey -t ":" -attribute "rotateX" pawl_notch_forward_notch; else copyKey -t ":" -attribute "rotateX" pawl_ring_forward_ring; pasteKey -t $startTime pawl2; // similarly, pawl3 advances notch to notch only if rotor2 is in step position if( `getAttr rotor2.offset` == 4) copyKey -t ":" -attribute "rotateX" pawl_notch_forward_notch; else copyKey -t ":" -attribute "rotateX" pawl_ring_forward_ring; pasteKey -t $startTime pawl3; // now advance to pawls moving back currentTime ($startTime + 60); // pawl1 always moves back notch to notch copyKey -t ":" -attribute "rotateX" pawl_notch_backward_notch; pasteKey -t ($startTime + 60) pawl1; // pawl2 depends on rotor1 if(`getAttr rotor1.offset` == 16 + 1) copyKey -t ":" -attribute "rotateX" pawl_notch_backward_ring; else if(`getAttr rotor1.offset` == 16) copyKey -t ":" -attribute "rotateX" pawl_ring_backward_notch; else copyKey -t ":" -attribute "rotateX" pawl_ring_backward_ring; pasteKey -t ($startTime + 60) pawl2; // pawl3 depends on rotor2 if(`getAttr rotor2.offset` == 4 + 1) copyKey -t ":" -attribute "rotateX" pawl_notch_backward_ring; else if(`getAttr rotor2.offset` == 4) copyKey -t ":" -attribute "rotateX" pawl_ring_backward_notch; else copyKey -t ":" -attribute "rotateX" pawl_ring_backward_ring; pasteKey -t ($startTime + 60) pawl3; ///////////////////////////////////////////////////////////////////// // // MOVE KEYS // ///////////////////////////////////////////////////////////////////// currentTime $startTime; string $keyObj = ("key" + ($input + 1)); setKeyframe ($keyObj+".translateY"); currentTime ($startTime + 30); move 0 -0.5 0 $keyObj; setKeyframe ($keyObj+".translateY"); currentTime ($startTime + 61); setKeyframe ($keyObj+".translateY"); currentTime ($startTime + 90); move 0 00 0 $keyObj; setKeyframe ($keyObj+".translateY"); ///////////////////////////////////////////////////////////////////// // // CIPHER/LIGHTS // ///////////////////////////////////////////////////////////////////// // once rotors have advanced, get their state and do the cipher! currentTime ($startTime + 30); int $output = enigmaMachine.cipher($input) + 1; string $incan = ("light_material"+$output+".incandescence"); currentTime $startTime; setAttr $incan -type double3 0 0 0; setKeyframe $incan; currentTime ($startTime + 30); setKeyframe $incan; currentTime ($startTime + 34); setAttr $incan -type double3 1 1 1; setKeyframe $incan; currentTime ($startTime + 63); setKeyframe $incan; currentTime ($startTime + 67); setAttr $incan -type double3 0 0 0; setKeyframe $incan; keyTangent -itt flat -ott flat -t ":" ("light_material"+$output); currentTime ($startTime + 90); } // set all rotor keys to linear tangents keyTangent -inTangentType linear -outTangentType linear -time ":" rotor1 rotor2 rotor3; // set all key, uh...keys to flat tangents select "key*"; keyTangent -inTangentType flat -outTangentType flat -time ":"; }