content: package thebetweenlands.client.render.model;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.function.Consumer;
  import java.util.function.Supplier;
  
  import net.minecraft.client.model.ModelRenderer;
  
  public class AnimationBlender<T extends MowzieModelBase> {
  	private class State {
  		private final Consumer<T> animator;
  		private final Supplier<Float> weight;
  
  		private float currentWeight;
  
  		private State(Consumer<T> animator, Supplier<Float> weight) {
  			this.animator = animator;
  			this.weight = weight;
  		}
  
  		private void update(AnimationBlender<T> blender, T model, boolean reset) {
  			if(reset) {
  				for(int i = 0; i < model.boxList.size(); i++) {
  					ModelRenderer part = model.boxList.get(i);
  					part.rotateAngleX = Float.NaN;
  					part.rotateAngleY = Float.NaN;
  					part.rotateAngleZ = Float.NaN;
  				}
  			}
  
  			this.animator.accept(model);
  
  			for(int i = 0; i < model.boxList.size(); i++) {
  				ModelRenderer part = model.boxList.get(i);
  
  				if(Float.isFinite(part.rotateAngleX)) {
  					blender.rotX[i] += this.currentWeight * part.rotateAngleX;
  				}
  
  				if(Float.isFinite(part.rotateAngleY)) {
  					blender.rotY[i] += this.currentWeight * part.rotateAngleY;
  				}
  
  				if(Float.isFinite(part.rotateAngleZ)) {
  					blender.rotZ[i] += this.currentWeight * part.rotateAngleZ;
  				}
  			}
  		}
  	}
  
  	private final T model;
  
  	private final State baseState;
  	private final List<State> states = new ArrayList<>();
  
  	private final float[] rotX;
  	private final float[] rotY;
  	private final float[] rotZ;
  
  	public AnimationBlender(T model) {
  		this.model = model;
  		this.states.add(this.baseState = new State(m -> {}, () -> 1.0f));
  		this.rotX = new float[this.model.boxList.size()];
  		this.rotY = new float[this.model.boxList.size()];
  		this.rotZ = new float[this.model.boxList.size()];
  	}
  
  	/**
  	 * @param animator The animator that set's the model part rotations for this state
  	 * @param weight The state's weight
  	 */
  	public void addState(Consumer<T> animator, Supplier<Float> weight) {
  		this.states.add(new State(animator, weight));
  	}
  
  	/**
  	 * Set's the angles as a weighted combination of each state's rotations.
  	 * If all state weights add up to 1 this is the same as an interpolation between all states.
  	 * @param useExistingAngles Whether currently already existing rotations should be added. If false all rotations are initialised with 0.
  	 */
  	public void setAngles(boolean useExistingAngles) {
  		for(int i = 0; i < this.model.boxList.size(); i++) {
  			this.rotX[i] = this.rotY[i] = this.rotZ[i] = 0;
  		}
  
  		for(State state : this.states) {
  			if(useExistingAngles || state != this.baseState) {
  				state.currentWeight = state.weight.get();
  				state.update(this, this.model, state != this.baseState);
  			}
  		}
  
  		for(int i = 0; i < this.model.boxList.size(); i++) {
  			ModelRenderer part = model.boxList.get(i);
  
  			part.rotateAngleX = this.rotX[i];
  			part.rotateAngleY = this.rotY[i];
  			part.rotateAngleZ = this.rotZ[i];
  		}
  	}
  }
  