/**
   * Set the camera to the specified be located at the specified view point and looking in the
   * specified direction.
   *
   * @param kViewPoint coordinates of the camera view point
   * @param kViewdirVector coordinates of the camera view direction vector. This vector must be
   *     normalized.
   */
  private void setView(Vector3f kViewPoint, Vector3f kViewdirVector) {
    // Use the view direction vector to create positive weights where more
    // weight is given to an axis that has less of a component in the
    // direction vector.  Use the weights to create an average of
    // two desired (orthogonal axis) up vectors.  Normalize this average
    // vector to create a combined view up vector to use.
    Vector3f kV = new Vector3f(kViewdirVector);

    kV.Set(Math.abs(kV.X), Math.abs(kV.Y), Math.abs(kV.Z));
    kV.Sub(Vector3f.ONE, kV);

    Vector3f kViewupVector = new Vector3f(0.0f, 0.0f, 0.0f);

    kViewupVector.ScaleAdd(m_kViewup1.Dot(kV), m_kViewup1, kViewupVector);
    kViewupVector.ScaleAdd(m_kViewup2.Dot(kV), m_kViewup2, kViewupVector);
    kViewupVector.Normalize();

    // Project the view-up vector onto the plane which is
    // perpendicular to the view direction vector.  By getting to
    // this point, we know that the view-up vector and the view
    // direction vectors are not aligned.  This projected vector is
    // normalized and becomes the new view-up vector.
    Vector3f kViewdirProjection = new Vector3f();

    kViewdirProjection.Scale(kViewdirVector.Dot(kViewupVector), kViewdirVector);
    kViewupVector.Sub(kViewdirProjection);
    kViewupVector.Normalize();

    Vector3f kViewleftVector = new Vector3f();

    kViewleftVector.Cross(kViewupVector, kViewdirVector);

    m_kViewPoint.Copy(kViewPoint);
    m_kViewDirection.Copy(kViewdirVector);
    m_kViewUp.Copy(kViewupVector);
  }
  /**
   * Loop through all of the possible branch choices and select the one that is the closest to the
   * current view direction vector. Take a vector from the current view point to a point along each
   * choice branch. Compute the dot-product between the current view direction vector and each of
   * these branch direction vectors and take the one with the largest positive value, i.e., most in
   * alignment.
   */
  private void setClosestChoiceBranch() {
    // Retrieve the current combined viewing direction vector.
    // Note that the sign of the view direction vector is negated
    // for the reasons described in the setView method.
    // Matrix4f kMatrixView = parentScene.getWVMatrix();
    // Vector3f kViewDirection = new Vector3f(-kMatrixView.M02, -kMatrixView.M12, -kMatrixView.M22);
    Vector3f kViewDirection = new Vector3f(parentScene.getCameraDirection());

    // Record the current view position and combined view orientation.
    // Vector3f kP0 = new Vector3f(kMatrixView.M03, kMatrixView.M13, kMatrixView.M23);
    Vector3f kP0 = new Vector3f(getViewPoint());

    // Check point down path which is maximum of the step distance
    // and the gaze distance.
    float fPointDist = Math.max(m_fGazeDist, m_fPathStep);
    float fBestAlign = -1.0f;
    int iBestAlignBranchChoiceIndex = -1;

    for (int iBranch = 0; iBranch < m_akBranchChoice.length; iBranch++) {

      // Get vector from current view point to point down branch path.
      BranchState kBranch = m_akBranchChoice[iBranch];
      Vector3f kV = new Vector3f();

      kV.Sub(kBranch.getForwardNormalizedPosition(fPointDist), kP0);
      kV.Normalize();

      // Only accept the best aligned branches we can supposedly see.
      float fAlign = kV.Dot(kViewDirection);

      if ((fAlign > 0.0f) && (fAlign > fBestAlign)) {
        fBestAlign = fAlign;
        iBestAlignBranchChoiceIndex = iBranch;
      }
    }

    // Select the "nearest" branch.
    if (iBestAlignBranchChoiceIndex >= 0) {

      // Select the new branch.
      m_iBranchChoiceIndex = iBestAlignBranchChoiceIndex;
      m_bChooseBranch = false;
      setBranch(m_akBranchChoice[m_iBranchChoiceIndex]);
    } else {
      beep();
    }
  }