A controller to smoothly transition a spatial between colors

This is a class I've been using to control fading a MaterialState between a set of different colors (for example, to cause a button to highlight smoothly when a cursor moves over it). Thought I'd post it here in case anyone else wants to use it.

When constructing, define two pairs of arrays. One array is a set of ColorRGBAs, the other is a set of floats representing the length of time the transition to the corresponding color in the color array should take. The third parameter is the material state that will be effected by the color changes, so just apply it to any Spatials that you want to change colors.

To initiate a fade to one of the colors in the array, call "transition(index of color)."

One neat thing about this is that it will make sure the transition is smooth even if it is interrupted part way through.



import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.scene.state.MaterialState;

public class ColorTransitionController extends Controller {
   private static final long serialVersionUID =1;

   private ColorRGBA currentColor;

   private float[] times;
   private ColorRGBA[] colors;
   
   private MaterialState material;


   /**
    *    
    * Creates a transition controller capable of controlling color changes between an arbitrary number of colors.   
    * @param colors An array of colors to transition between.
    * @param times An array of times governing how long the transition to each corresponding color in colors should take.
    * @param toControl The MaterialState to apply color transitions to. Any spatial with this MaterialState as a render state will have its colours controlled by this controller.
    */
   public ColorTransitionController(ColorRGBA[] colors, float[] times, MaterialState toControl) {
      
      this.colors = colors;
      this.times = times;
      this.material = toControl;
      
      this.currentColor = new ColorRGBA();
      currentColor.set(colors[0]);         
   }

   /**
    * Call to cause a transition from the present color of the MaterialState to the color in colors with index 'state.'
    * This transition can be initiated at any time, and will smoothly transition from whatever color the material state is currently in to the requested color.
    * @param state The index of the color to change to, and the corresponding length of time that transition should take.
    */
   public void transition(int state)
   {
      float length;
      endColor = colors[state];
      length = times[state];
         
      if (length <= 0)
      {         
         this.setActive(false);   //this controller no longer needs to be updated      
         currentColor.set(endColor);
      
         material.setAmbient(currentColor);
         material.setDiffuse(currentColor);

               
      }else
      {
         rate[0] = (endColor.r - currentColor.r)/length;
         rate[1] = (endColor.g - currentColor.g)/length;
         rate[2] = (endColor.b - currentColor.b)/length;
         rate[3] = (endColor.a - currentColor.a)/length;
         this.setActive(true);//now that a new transition is required, indicate that this needs to be updated.
      }      
   }
   private ColorRGBA endColor;   
   private float[] rate = new float[4];   

   @Override
   public void update(float time)
   {

      currentColor.r += rate[0]* time;
      currentColor.g += rate[1]* time;
      currentColor.b += rate[2]* time;
      currentColor.a += rate[3]* time;

      if (((rate[0]>0?-1:1 )*(currentColor.r - endColor.r) <= 0)&&
            ((rate[1]>0?-1:1 )*(currentColor.g - endColor.g) <= 0 )&&   
            ((rate[2]>0?-1:1 )*(currentColor.b - endColor.b )<= 0 )&&   
            ((rate[3]>0?-1:1 )*(currentColor.a - endColor.a ) <= 0))         
      {            
         currentColor.set(endColor);

         material.setAmbient(currentColor);
         material.setDiffuse(currentColor);
         this.setActive(false);//stop updating this controller now that the end color is achieved.
      }else
      {   
         if ((Math.abs( currentColor.r)>1 )||
            (Math.abs( currentColor.g)>1 )||
            (Math.abs( currentColor.b)>1 )||
            (Math.abs( currentColor.a)>1 ))
            {
               currentColor.set(endColor);      
            }
         
         material.setAmbient(currentColor);
         material.setDiffuse(currentColor);
      }

   }

      public float[] getTimes() {
         return times;
      }

      public void setTimes(float[] times) {
         this.times = times;
      }

      public ColorRGBA[] getColors() {
         return colors;
      }

      public void setColors(ColorRGBA[] colors) {
         this.colors = colors;
      }   
}

Smooth :smiley:



Thanks for sharing