/** * Create the array of branch choices and set the mode which forces the user to select from among * the branch choices. * * @param iBranchParent int Index which identifies the parent branch. * @param iBranchPoint int Index which identifies the point along the branch path where the * branching occurs for the choices. */ private void setupBranchChoices(int iBranchParent, int iBranchPoint) { // Get list of possible sub-branches for the parent. int[] aiBranchChildIndex = m_kFlyPathGraph.getBranchPointBranches(iBranchParent, iBranchPoint); // Get the information for the parent branch. BranchState kBranchStateParent = m_akBranchState[iBranchParent]; // Build the list of possible branches. // First branch choice is the parent branch, current direction // Last branch choice is the parent branch, reverse direction // Reset all of the sub-branches to their start state. m_akBranchChoice = new BranchState[2 + aiBranchChildIndex.length]; m_akBranchChoice[0] = kBranchStateParent.createCopy(); m_akBranchChoice[m_akBranchChoice.length - 1] = kBranchStateParent.createReverseCopy(); for (int i = 0; i < aiBranchChildIndex.length; i++) { int iBranchChild = aiBranchChildIndex[i]; m_akBranchState[iBranchChild].start(); m_akBranchChoice[i + 1] = m_akBranchState[iBranchChild].createCopy(); } m_bChooseBranch = true; m_iBranchChoiceIndex = -1; }
/** * Create a copy of this instance which has the same information except that the state of the * moving forward is inverted. * * @return BranchState New instance which is a copy of this instance except that the moving * forward flag is inverted. */ public BranchState createReverseCopy() { BranchState kCopy = createCopy(); kCopy.m_bMoveForward = !kCopy.m_bMoveForward; return kCopy; }
/** * Update the camera position along the path based on the specified distance from the beginning. * * @param fNormalizedDist normalized distance from the beginning of the path for the location of * the camera for viewing */ private void setPathDist(float fNormalizedDist) { // Make sure the distance is in the [0,1] range. fNormalizedDist = clampNormalizedPathDistance(fNormalizedDist); // Compute the actual distance along the curve. float fDist = fNormalizedDist * getPathLength(); // Save the current distance. m_kBranchState.m_fNormalizedPathDist = fNormalizedDist; // Get the path point (position and tangent) based on distance. // It needs to be double precision for the view to use. Curve3f kCurve = m_kBranchState.m_kBranchCurve; float fTime = kCurve.GetTime(fDist, 100, 1e-02f); Vector3f kViewPoint = kCurve.GetPosition(fTime); // If the gaze distance is zero, then use the tangent vector // to the curve. // If the path is being followed in the reverse direction, // then the direction of looking down the path needs to // be reversed (negated). Vector3f kLookatVector = new Vector3f(); boolean bLookatVectorUseTangent = true; if (m_fGazeDist > 0.0f) { float fTimeGazeDist = m_kBranchState.getForwardNormalizedTime(m_fGazeDist); if (fTime != fTimeGazeDist) { Vector3f kVec = kCurve.GetPosition(fTimeGazeDist); kLookatVector.Sub(kVec, kViewPoint); kLookatVector.Normalize(); bLookatVectorUseTangent = false; } } if (bLookatVectorUseTangent) { kLookatVector = kCurve.GetTangent(fTime); if (!m_kBranchState.m_bMoveForward) { kLookatVector.Neg(); } } // Update the view given the view position, view direction, // and a hint for the view up vector. setView(kViewPoint, kLookatVector); // Notify listener that we are updated. notifyCallback(EVENT_CHANGE_POSITION); }
/** * 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(); } }
/** * Set the annotation point along the path. * * @param iItem int */ private void setCurvePathAnnotateItem(int iItem) { // Select the curve and the position along the curve. // First set the sign of the path step to reflect // whether the movement down the path was forward or backward // when the annotation point was captured. FlyPathAnnotateList_WM.Item kItem = m_kAnnotateList.getItem(iItem); m_kBranchState.m_bMoveForward = kItem.isPathMoveForward(); setBranch(kItem.getBranchIndex()); m_kViewPoint.Copy(kItem.getCameraLocation()); m_kViewDirection.Copy(kItem.getCameraDirection()); m_kViewUp.Copy(kItem.getCameraUp()); notifyCallback(EVENT_CHANGE_POSITION); }
/** * Take the specified number of steps along the current path using the current path step size. If * the number of steps is negative, then the steps are taken in reverse. * * @param iNumSteps int Number of steps to take along the current path. */ private void doPathStep(int iNumSteps) { // If we make a step and there were branch choices defined, // then replace the current state for the branch with the // variation selected. boolean bFirstSelectedBranchStep = false; if (null != m_akBranchChoice) { m_akBranchState[m_kBranchState.m_iBranchIndex] = m_kBranchState; m_akBranchChoice = null; notifyCallback(EVENT_CHANGE_BRANCH); bFirstSelectedBranchStep = true; } // Note that iNumSteps may be negative! float fNormalizedPathStep = iNumSteps * getPathStep() / getPathLength(); // Reverse direction if moving backward. if (!m_kBranchState.m_bMoveForward) { fNormalizedPathStep = -fNormalizedPathStep; } // Compute the new normalized path distance. float fNewNormalizedPathDistance = getNormalizedPathDistance() + fNormalizedPathStep; // Note which branch we are currently on before we possibly change it. int iBranch = getBranchIndex(); // Determine which segment of the branch path, in relation to the // branch points, that we are currently in and that we are // stepping into. int iPathSegment = m_kBranchState.getBranchPointSegment(getNormalizedPathDistance()); int iNewPathSegment = m_kBranchState.getBranchPointSegment(fNewNormalizedPathDistance); // If the segments are different, then we will step into the branch. // The subbranch information is indexed by the minimum // of the two indexes. Don't check if this is the first step // being taken after selecting a branch at a branch point. if (((Math.abs(iPathSegment - iNewPathSegment) >= 1) && !bFirstSelectedBranchStep) || ((Math.abs(iPathSegment - iNewPathSegment) > 1) && bFirstSelectedBranchStep)) { beep(); // Access the branch point by its segment. int iSegment = Math.min(iPathSegment, iNewPathSegment); // Clamp the path distance to this branch point. fNewNormalizedPathDistance = m_kBranchState.m_afBranchPoint[iSegment]; m_kBranchState.updateDistUnvisited(fNewNormalizedPathDistance); setPathDist(fNewNormalizedPathDistance); // Build the list of possible branches. setupBranchChoices(iBranch, iSegment); } // Check for reaching the beginning of the branch in which // there is a parent. Then we will step back onto the parent. // Don't check if this is the first step being taken after // selecting a branch at a branch point. else if ((fNewNormalizedPathDistance < 0.0f) && (-1 != m_kBranchState.m_iParentBranchIndex) && !bFirstSelectedBranchStep) { beep(); // Clamp the path distance to this branch point. fNewNormalizedPathDistance = 0.0f; m_kBranchState.updateDistUnvisited(fNewNormalizedPathDistance); setPathDist(fNewNormalizedPathDistance); // Access the branch point by its segment. int iBranchParent = m_kBranchState.m_iParentBranchIndex; BranchState kBranchStateParent = m_akBranchState[iBranchParent]; int iSegment = 0; while (iSegment < kBranchStateParent.m_afBranchPoint.length) { if (m_kBranchState.m_fParentBranchPoint == kBranchStateParent.m_afBranchPoint[iSegment]) { break; } ++iSegment; } // Build the list of possible branches. setupBranchChoices(iBranchParent, iSegment); } // Remain on the same branch. else { // Make sure the distance is in the [0,1] range. fNewNormalizedPathDistance = clampNormalizedPathDistance(fNewNormalizedPathDistance); m_kBranchState.updateDistUnvisited(fNewNormalizedPathDistance); setPathDist(fNewNormalizedPathDistance); } }
/** * Call from the JPanelFlythruMove. * * @param command move command. */ public void move(String command) { if (command.equals("lookup")) { // pitch - look up Vector3f kRight = new Vector3f(); kRight.UnitCross(m_kViewDirection, m_kViewUp); Matrix3f kRotate = new Matrix3f(); kRotate.FromAxisAngle(kRight, (float) Math.toRadians(1)); kRotate.Mult(m_kViewDirection, m_kViewDirection); kRotate.Mult(m_kViewUp, m_kViewUp); // Notify listener that we are updated. notifyCallback(EVENT_CHANGE_POSITION); } else if (command.equals("lookdown")) { // pitch - look down Vector3f kRight = new Vector3f(); kRight.UnitCross(m_kViewDirection, m_kViewUp); Matrix3f kRotate = new Matrix3f(); kRotate.FromAxisAngle(kRight, (float) Math.toRadians(-1)); kRotate.Mult(m_kViewDirection, m_kViewDirection); kRotate.Mult(m_kViewUp, m_kViewUp); // Notify listener that we are updated. notifyCallback(EVENT_CHANGE_POSITION); } else if (command.equals("lookleft")) { // yaw - look left Matrix3f kRotate = new Matrix3f(); kRotate.FromAxisAngle(m_kViewUp, (float) Math.toRadians(1)); kRotate.Mult(m_kViewDirection, m_kViewDirection); // Notify listener that we are updated. notifyCallback(EVENT_CHANGE_POSITION); } else if (command.equals("lookright")) { // case KeyEvent.VK_RIGHT: // yaw - look right Matrix3f kRotate = new Matrix3f(); kRotate.FromAxisAngle(m_kViewUp, (float) Math.toRadians(-1)); kRotate.Mult(m_kViewDirection, m_kViewDirection); // Notify listener that we are updated. notifyCallback(EVENT_CHANGE_POSITION); } else if (command.equals("counterclockwise")) { // case KeyEvent.VK_F3: // roll - counterclockwise Matrix3f kRotate = new Matrix3f(); kRotate.FromAxisAngle(m_kViewDirection, (float) Math.toRadians(-1)); kRotate.Mult(m_kViewUp, m_kViewUp); // Notify listener that we are updated. notifyCallback(EVENT_CHANGE_POSITION); } else if (command.equals("clockwise")) { // roll - clockwise Matrix3f kRotate = new Matrix3f(); kRotate.FromAxisAngle(m_kViewDirection, (float) Math.toRadians(1)); kRotate.Mult(m_kViewUp, m_kViewUp); // Notify listener that we are updated. notifyCallback(EVENT_CHANGE_POSITION); } else if (command.equals("escape")) { // VK_ESCAPE setIdentityViewOrientation(); } else if (command.equals("home")) { // case KeyEvent.VK_HOME: // reset position to the beginning of the path if (!m_bChooseBranch && (null == m_akBranchChoice)) { setPathDist(0.0f); } else { beep(); } } else if (command.equals("end")) { // case KeyEvent.VK_END: // reset position to the end of the path if (!m_bChooseBranch && (null == m_akBranchChoice)) { setPathDist(1.0f); } else { beep(); } } else if (command.equals("forward")) { // case KeyEvent.VK_UP: // move forward along the path if (!m_bChooseBranch) { doPathStep(1); } else { beep(); } } else if (command.equals("backward")) { // case KeyEvent.VK_DOWN: // move backward along the path if (!m_bChooseBranch) { doPathStep(-1); } else { beep(); } } else if (command.equals("reverse")) { // case KeyEvent.VK_R: // follow path in reverse heading m_kBranchState.m_bMoveForward = !m_kBranchState.m_bMoveForward; setPathDist(m_kBranchState.m_fNormalizedPathDist); } else if (command.equals("prevAnnotatePt")) { // case KeyEvent.VK_F5: // go to previous annotate point if (!m_bChooseBranch && (m_kAnnotateList.getNumItems() > 0)) { if (--m_iAnnotateListItemSelected < 0) { m_iAnnotateListItemSelected = m_kAnnotateList.getNumItems() - 1; } setCurvePathAnnotateItem(m_iAnnotateListItemSelected); } else { beep(); } } else if (command.equals("nextAnnotatePt")) { // case KeyEvent.VK_F6: // go to next annotate point if (!m_bChooseBranch && (m_kAnnotateList.getNumItems() > 0)) { if (++m_iAnnotateListItemSelected >= m_kAnnotateList.getNumItems()) { m_iAnnotateListItemSelected = 0; } setCurvePathAnnotateItem(m_iAnnotateListItemSelected); } else { beep(); } } else if (command.equals("nextBranch")) { // case KeyEvent.VK_SPACE: // select next branch choice if (null != m_akBranchChoice) { setClosestChoiceBranch(); } else { beep(); } } else if (command.equals("stepDistanceIncrease")) { m_fPathStep += 0.1f; setPathDist(m_kBranchState.m_fNormalizedPathDist); } else if (command.equals("stepDistanceDecrease")) { m_fPathStep -= 0.1f; if (m_fPathStep < 0.1f) { m_fPathStep = 0.1f; beep(); } setPathDist(m_kBranchState.m_fNormalizedPathDist); } else if (command.equals("gazeDistanceDecrease")) { m_fGazeDist -= 1.0f; if (m_fGazeDist < 0.0f) { m_fGazeDist = 0.0f; beep(); } setPathDist(m_kBranchState.m_fNormalizedPathDist); } else if (command.equals("gazeDistanceIncrease")) { m_fGazeDist += 1.0f; setPathDist(m_kBranchState.m_fNormalizedPathDist); } }
/** * Handle the key pressed event from the text field. * * @param event key event to handle */ @Override public void keyPressed(KeyEvent event) { if (KeyEvent.KEY_PRESSED == event.getID()) { int iKeyCode = event.getKeyCode(); char iKeyChar = event.getKeyChar(); switch (iKeyCode) { case KeyEvent.VK_ESCAPE: setIdentityViewOrientation(); break; case KeyEvent.VK_HOME: // reset position to the beginning of the path if (!m_bChooseBranch && (null == m_akBranchChoice)) { setPathDist(0.0f); } else { beep(); } break; case KeyEvent.VK_END: // reset position to the end of the path if (!m_bChooseBranch && (null == m_akBranchChoice)) { setPathDist(1.0f); } else { beep(); } break; case KeyEvent.VK_UP: // move forward along the path if (!m_bChooseBranch) { doPathStep(1); } else { beep(); } break; case KeyEvent.VK_DOWN: // move backward along the path if (!m_bChooseBranch) { doPathStep(-1); } else { beep(); } break; case KeyEvent.VK_R: // follow path in reverse heading m_kBranchState.m_bMoveForward = !m_kBranchState.m_bMoveForward; setPathDist(m_kBranchState.m_fNormalizedPathDist); break; case KeyEvent.VK_F5: // go to previous annotate point if (!m_bChooseBranch && (m_kAnnotateList.getNumItems() > 0)) { if (--m_iAnnotateListItemSelected < 0) { m_iAnnotateListItemSelected = m_kAnnotateList.getNumItems() - 1; } setCurvePathAnnotateItem(m_iAnnotateListItemSelected); } else { beep(); } break; case KeyEvent.VK_F6: // go to next annotate point if (!m_bChooseBranch && (m_kAnnotateList.getNumItems() > 0)) { if (++m_iAnnotateListItemSelected >= m_kAnnotateList.getNumItems()) { m_iAnnotateListItemSelected = 0; } setCurvePathAnnotateItem(m_iAnnotateListItemSelected); } else { beep(); } break; case KeyEvent.VK_SPACE: // select next branch choice if (null != m_akBranchChoice) { setClosestChoiceBranch(); } else { beep(); } break; case KeyEvent.VK_S: // change the distance of a single step if ('s' == iKeyChar) { m_fPathStep -= 0.1f; if (m_fPathStep < 0.1f) { m_fPathStep = 0.1f; beep(); } } else { m_fPathStep += 0.1f; } setPathDist(m_kBranchState.m_fNormalizedPathDist); break; case KeyEvent.VK_G: // change the gaze distance if ('g' == iKeyChar) { m_fGazeDist -= 1.0f; if (m_fGazeDist < 0.0f) { m_fGazeDist = 0.0f; beep(); } } else { m_fGazeDist += 1.0f; } setPathDist(m_kBranchState.m_fNormalizedPathDist); break; } } }
/** * Get the current state of traversing. * * @return BranchState */ public BranchState getBranchState() { return m_kBranchState.createCopy(); }