private void climb(
      final CharacterStateEvent state, CharacterMoveInputEvent input, Vector3f desiredVelocity) {
    if (state.getClimbDirection() == null) {
      return;
    }
    Vector3f tmp;

    Vector3i climbDir3i = state.getClimbDirection();
    Vector3f climbDir3f = climbDir3i.toVector3f();

    Quat4f rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), 0, 0);
    tmp = new Vector3f(0.0f, 0.0f, -1.0f);
    QuaternionUtil.quatRotate(rotation, tmp, tmp);
    float angleToClimbDirection = tmp.angle(climbDir3f);

    boolean clearMovementToDirection = !state.isGrounded();

    // facing the ladder or looking down or up
    if (angleToClimbDirection < Math.PI / 4.0 || Math.abs(input.getPitch()) > 60f) {
      float pitchAmount = state.isGrounded() ? 45f : 90f;
      float pitch = input.getPitch() > 30f ? pitchAmount : -pitchAmount;
      rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), TeraMath.DEG_TO_RAD * pitch, 0);
      QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity);

      // looking sidewards from ladder
    } else if (angleToClimbDirection < Math.PI * 3.0 / 4.0) {
      float rollAmount = state.isGrounded() ? 45f : 90f;
      tmp = new Vector3f();
      QuaternionUtil.quatRotate(rotation, climbDir3f, tmp);
      float leftOrRight = tmp.x;
      float plusOrMinus = (leftOrRight < 0f ? -1.0f : 1.0f) * (climbDir3i.x != 0 ? -1.0f : 1.0f);
      rotation =
          new Quat4f(
              TeraMath.DEG_TO_RAD * input.getYaw(),
              0f,
              TeraMath.DEG_TO_RAD * rollAmount * plusOrMinus);
      QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity);

      // facing away from ladder
    } else {
      rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), 0, 0);
      QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity);
      clearMovementToDirection = false;
    }

    // clear out movement towards or away from the ladder
    if (clearMovementToDirection) {
      if (climbDir3i.x != 0) {
        desiredVelocity.x = 0f;
      }
      if (climbDir3i.z != 0) {
        desiredVelocity.z = 0f;
      }
    }
  }
 /**
  * Sets the state of the given entity to the state represented by the CharacterStateEvent. The
  * state of the entity is determined by its LocationComponent (location and orientation of physics
  * body), CharacterMovementComponent (velocity and various movement state variables) and
  * CharacterComponent for pitch and yaw (used for the camera).
  *
  * @param entity
  * @param state
  */
 public void setToState(EntityRef entity, CharacterStateEvent state) {
   LocationComponent location = entity.getComponent(LocationComponent.class);
   CharacterMovementComponent movementComp = entity.getComponent(CharacterMovementComponent.class);
   CharacterComponent characterComponent = entity.getComponent(CharacterComponent.class);
   if (location == null || movementComp == null || characterComponent == null) {
     return;
   }
   location.setWorldPosition(state.getPosition());
   location.setWorldRotation(state.getRotation());
   entity.saveComponent(location);
   movementComp.mode = state.getMode();
   movementComp.setVelocity(state.getVelocity());
   movementComp.grounded = state.isGrounded();
   movementComp.footstepDelta = state.getFootstepDelta();
   entity.saveComponent(movementComp);
   characterComponent.pitch = state.getPitch();
   characterComponent.yaw = state.getYaw();
   entity.saveComponent(characterComponent);
   setPhysicsLocation(entity, state.getPosition());
 }
 private void extrapolateCharacterComponent(EntityRef entity, CharacterStateEvent state) {
   CharacterComponent characterComponent = entity.getComponent(CharacterComponent.class);
   characterComponent.pitch = state.getPitch();
   characterComponent.yaw = state.getYaw();
   entity.saveComponent(characterComponent);
 }