protected boolean recoverFromPenetration(CollisionWorld collisionWorld) {
    boolean penetration = false;

    collisionWorld
        .getDispatcher()
        .dispatchAllCollisionPairs(
            ghostObject.getOverlappingPairCache(),
            collisionWorld.getDispatchInfo(),
            collisionWorld.getDispatcher());

    currentPosition.set(ghostObject.getWorldTransform(new Transform()).origin);

    double maxPen = 0.0f;
    for (int i = 0; i < ghostObject.getOverlappingPairCache().getNumOverlappingPairs(); i++) {
      manifoldArray.clear();

      BroadphasePair collisionPair =
          ghostObject.getOverlappingPairCache().getOverlappingPairArray().getQuick(i);

      if (collisionPair.algorithm != null) {
        collisionPair.algorithm.getAllContactManifolds(manifoldArray);
      }

      for (int j = 0; j < manifoldArray.size(); j++) {
        PersistentManifold manifold = manifoldArray.getQuick(j);
        double directionSign = manifold.getBody0() == ghostObject ? -1.0f : 1.0f;
        for (int p = 0; p < manifold.getNumContacts(); p++) {
          ManifoldPoint pt = manifold.getContactPoint(p);

          double dist = pt.getDistance();
          if (dist < 0.0f) {
            if (dist < maxPen) {
              maxPen = dist;
              touchingNormal.set(pt.normalWorldOnB); // ??
              touchingNormal.scale(directionSign);
            }

            currentPosition.scaleAdd(
                directionSign * dist * 0.2f, pt.normalWorldOnB, currentPosition);

            penetration = true;
          } else {
            // printf("touching %f\n", dist);
          }
        }

        // manifold->clearManifold();
      }
    }

    Transform newTrans = ghostObject.getWorldTransform(new Transform());
    newTrans.origin.set(currentPosition);
    ghostObject.setWorldTransform(newTrans);
    // printf("m_touchingNormal =
    // %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);

    // System.out.println("recoverFromPenetration "+penetration+" "+touchingNormal);

    return penetration;
  }
  public void playerStep(CollisionWorld collisionWorld, double dt) {
    // printf("playerStep(): ");
    // printf("  dt = %f", dt);

    // quick check...
    if (!useWalkDirection && velocityTimeInterval <= 0.0f) {
      // printf("\n");
      return; // no motion
    }

    wasOnGround = onGround();

    // Update fall velocity.
    verticalVelocity -= gravity * dt;
    if (verticalVelocity > 0.0 && verticalVelocity > jumpSpeed) {
      verticalVelocity = jumpSpeed;
    }
    if (verticalVelocity < 0.0 && Math.abs(verticalVelocity) > Math.abs(fallSpeed)) {
      verticalVelocity = -Math.abs(fallSpeed);
    }
    verticalOffset = verticalVelocity * dt;

    Transform xform = ghostObject.getWorldTransform(new Transform());

    // printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
    // printf("walkSpeed=%f\n",walkSpeed);

    stepUp(collisionWorld);
    if (useWalkDirection) {
      // System.out.println("playerStep 3");
      stepForwardAndStrafe(collisionWorld, walkDirection);
    } else {
      System.out.println("playerStep 4");
      // printf("  time: %f", m_velocityTimeInterval);

      // still have some time left for moving!
      double dtMoving = (dt < velocityTimeInterval) ? dt : velocityTimeInterval;
      velocityTimeInterval -= dt;

      // how far will we move while we are moving?
      Vector3d move = new Vector3d();
      move.scale(dtMoving, walkDirection);

      // printf("  dtMoving: %f", dtMoving);

      // okay, step
      stepForwardAndStrafe(collisionWorld, move);
    }
    stepDown(collisionWorld, dt);

    // printf("\n");

    xform.origin.set(currentPosition);
    ghostObject.setWorldTransform(xform);
  }
  public void preStep(CollisionWorld collisionWorld) {
    int numPenetrationLoops = 0;
    touchingContact = false;
    while (recoverFromPenetration(collisionWorld)) {
      numPenetrationLoops++;
      touchingContact = true;
      if (numPenetrationLoops > 4) {
        // printf("character could not recover from penetration = %d\n", numPenetrationLoops);
        break;
      }
    }

    currentPosition.set(ghostObject.getWorldTransform(new Transform()).origin);
    targetPosition.set(currentPosition);
    // printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
  }