Here is how I did it:
1) I imported the base model only (no animation) into blender. I then created a keyframe map on paper describing what I wanted to do:
1 - 2 IDLE/STOP
2 - 6 SLOW FORWARD
6 - 236 FORWARD
236 - 242 SLOW FORWARD
242 - 243 IDLE/STOP
243 - 249 SLOW REVERSE
249 - 477 REVERSE
477 - 483 SLOW REVERSE
483 - 484 IDLE/STOP
2 - 6 SLOW FORWARD
6 - 236 FORWARD
236 - 242 SLOW FORWARD
242 - 243 IDLE/STOP
243 - 249 SLOW REVERSE
249 - 477 REVERSE
477 - 483 SLOW REVERSE
483 - 484 IDLE/STOP
2) I selected the valve, and added keyframes at each of the above points. Since this was a valve, and the stem was on the Y axis, I used LocRot Y.
3) The vertical axis in the ipo editor is the absolute angle for rotation. The further up you go, the more angular deflection you get. Obviously, it wraps at 360, but you get the idea. I wanted four full turns, so I chose an angular offset of 1440. To get the effect of slowing down at the ends, I used a bezier curve fit, and at the "slow" points, raised the angle about 10 degrees to give the curve a smoother beginning and finish.
4) MOST IMPORTANTLY, I added dead space at the end. 1 frame at the beginning, middle, and end. I can't stress this enough, you *MUST* put at least some (not necessarily a whole frame, but it's easier to do the calculations that way) dead space. This is where your idle animation will loop - and was why my original model spun freely at the end of the animation.
5) Export the model, and open both the nif, and kf (but not the xnif) in separate nifskope instances. You want to look at the keyframe data in the NiKeyFrameController in the nif, and the NiTextKeyExtraData in the kf.
6) Note that Blender added additional data points you don't need to worry about. You only need the key frames, and if you did it right in Blender, it should be obvious - the very first key is the beginning of the animation. The next key *should* be the last key in the first idle loop. In my case, since I wanted to smooth the curve for aesthetic reasons, the next two keys actually represent the inflection points - so I ignore them. However, when I get to the fourth key, I get to the end of the active portion of IDLE2. At this point, I need to start the IDLE2 LOOP. The next key is the end of the loop, and the start of IDLE3.
To summarize:
Each idle needs both an active region, and a loop, to accomplish the goal of moving, and then stopping automatically. The exception is IDLE. This is the starting state, and in my case, I wanted it to loop inactive. For the other animations, I needed an active region, and a loop region. The active region was where the valve rotated, and the loop region was where the valve was stopped. This implied for my model a total of 6 states.
{IDLE LOOP START, IDLE START}; {IDLE LOOP STOP, IDLE STOP, IDLE2 START}; {IDLE2 LOOP START}; {IDLE2 LOOP STOP, IDLE2 STOP, IDLE3 START}; {IDLE3 LOOP START}; {IDLE3 LOOP STOP, IDLE3 LOOP STOP}
http://img263.imageshack.us/i/41566530.jpg/
This will vary with the model, but you get the idea. You can set the loop start and stop independently of the start and stop.
7) Copy the relevant data to the NiTextKeyExtraData section of the nif (not the xnif).
Note, this is controlled in scripting using PlayGroup IDLE2 and PlayGroup IDLE3. The model will correctly respond to these commands, so it is up to the script to keep track of the valve state and issue the proper animation.
Add in a squeaky valve sound, and it's about perfect. I created a sound that is exactly timed to this particular animation, but you can also control a generic squeak with the loop sound player.
begin mvr_valve_anim_01_scriptshort initshort stateshort positionif ( MenuMode == 1 ) returnendifif ( init == 0 ); SetScale 0.25 set state to 0 set position to 0 set init to 1 returnendifif ( OnActivate == 1 ) if ( state == 1 ) return else set state to 1 PlaySound3DVP "mvr_squeaky_valve", 1.0, 1.0 if ( position == 0 ) Messagebox "Closing Valve" set position to 1 Playgroup Idle2 else Messagebox "Opening Valve" set position to 0 Playgroup Idle3 endif endifendifif ( state == 1 ) if ( GetSoundPlaying "mvr_squeaky_valve" == 0 ) set state to 0 endifendifend
Note that this depends on the sound being precisely tuned to the model.