// --------------------------------------------------------------------------- // fixPhaseForward // //! Recompute phases of all Breakpoints on the closed range [pos, stopHere] //! so that the synthesized phases of those Breakpoints matches //! the stored phase, as long as the synthesized phase at pos //! matches the stored (not recomputed) phase. The phase at pos //! is modified only if pos is the position of a null Breakpoint //! and the Breakpoint that follows is non-null. //! //! Phase fixing is only applied to non-null (nonzero-amplitude) Breakpoints, //! because null Breakpoints are interpreted as phase reset points in //! Loris. If a null is encountered, its phase is corrected from its non-Null //! successor, if it has one, otherwise it is unmodified. //! //! \pre pos and stopHere are iterators on the same Partial, and //! pos must be not later than stopHere. //! \pre stopHere cannot be end of the Partial, it must be the postion //! of a valid Breakpoint. //! \param pos the position of the first Breakpoint whose phase might be //! recomputed. //! \param stopHere the position of the last Breakpoint whose phase might //! be modified. // void fixPhaseForward( Partial::iterator pos, Partial::iterator stopHere ) { while ( pos != stopHere ) { Partial::iterator posPrev = pos++; if ( NoNulls || BreakpointUtils::isNonNull( pos.breakpoint() ) ) { // pos is the position of a non-Null Breakpoint double travel = phaseTravel( posPrev, pos ); if ( NoNulls || BreakpointUtils::isNonNull( posPrev.breakpoint() ) ) { // if its predecessor of pos is non-Null, then fix // the phase of the Breakpoint at pos. pos.breakpoint().setPhase( wrapPi( posPrev.breakpoint().phase() + travel ) ); } else { // if the predecessor of pos is Null, then // correct the predecessor's phase so that // it correctly resets the synthesis phase // so that the phase of the Breakpoint at // pos is achieved in synthesis. posPrev.breakpoint().setPhase( wrapPi( pos.breakpoint().phase() - travel ) ); } } } }
// 变换为“限制集”欧拉角 void EulerAngles::cononize() { pitch = wrapPi(pitch); if (pitch < -kPiOver2) { pitch = -kPi - pitch; heading += kPi; bank += kPi; } else if (pitch>kPiOver2) { pitch = kPi - pitch; heading += kPi; bank += kPi; } if (fabs(pitch) > kPiOver2 - 1e-4) { heading += bank; bank = 0.0f; } else { bank = wrapPi(bank); } heading = wrapPi(heading); }
void EulerAngles::canonize() { pitch = wrapPi(pitch); if (pitch < -PI_OVER_2) { pitch = -PI - pitch; heading += PI; bank += PI; } else if (pitch > -PI_OVER_2) { pitch = PI - pitch; heading += PI; bank += PI; } //检测万向锁,即pitch是否等于pi/2,允许有误差 if (fabs(pitch) > PI_OVER_2 - EPSINON) { heading += bank; bank = 0.0f; } else { bank = wrapPi(bank); } heading = wrapPi(heading); }
void EulerAngles::canonize() { // 首先,回绕pitch到-pi到pi // First, wrap pitch in range -pi ... pi pitch = wrapPi(pitch); // 现在,检查矩阵的“后面”,pitch超出标准范围-pi/2到pi/2 // Now, check for "the back side" of the matrix, pitch outside // the canonical range of -pi/2 ... pi/2 if (pitch < -kPiOver2) { pitch = -kPi - pitch; heading += kPi; bank += kPi; } else if (pitch > kPiOver2) { pitch = kPi - pitch; heading += kPi; bank += kPi; } // 好了,现在检查万向锁情况(需要一点容忍度) // OK, now check for the gimbel lock case (within a slight // tolerance) if (fabs(pitch) > kPiOver2 - 1e-4) { // 我们遇到了万向锁。将所有竖直轴的旋转交给heading // We are in gimbel lock. Assign all rotation // about the vertical axis to heading heading += bank; bank = 0.0f; } else { // 没有遇到万向锁。回绕到标准范围的bank角度。 // Not in gimbel lock. Wrap the bank angle in // canonical range bank = wrapPi(bank); } // 将heading回绕到标准范围 // Wrap heading in canonical range heading = wrapPi(heading); }
// --------------------------------------------------------------------------- // fixPhaseBackward // //! Recompute phases of all Breakpoints on the half-open range [stopHere, pos) //! so that the synthesized phases of those Breakpoints matches //! the stored phase, as long as the synthesized phase at stopHere //! matches the stored (not recomputed) phase. //! //! The phase is corrected beginning at the end of the range, maintaining //! the stored phase in the Breakpoint at pos. //! //! Backward phase-fixing stops if a null (zero-amplitude) Breakpoint //! is encountered, because nulls are interpreted as phase reset points //! in Loris. If a null is encountered, the remainder of the range //! (the front part) is fixed in the forward direction, beginning at //! the start of the stopHere. //! //! \pre pos and stopHere are iterators on the same Partial, and //! pos must be not later than stopHere. //! \pre pos cannot be end of the Partial, it must be the postion //! of a valid Breakpoint. //! \param stopHere the position of the earliest Breakpoint whose phase might be //! recomputed. //! \param pos the position of a (later) Breakpoint whose phase is to be matched. //! The phase at pos is not modified. // void fixPhaseBackward( Partial::iterator stopHere, Partial::iterator pos ) { while ( pos != stopHere && ( NoNulls || BreakpointUtils::isNonNull( pos.breakpoint() ) ) ) { // pos is not the first Breakpoint in the Partial, // and pos is not a Null Breakpoint. // Compute the correct phase for the // predecessor of pos. Partial::iterator posFwd = pos--; double travel = phaseTravel( pos, posFwd ); pos.breakpoint().setPhase( wrapPi( posFwd.breakpoint().phase() - travel ) ); } // if a null was encountered, then stop fixing backwards, // and fix the front of the Partial in the forward direction: if ( pos != stopHere ) { // pos is not the first Breakpoint in the Partial, // and it is a Null Breakpoint (zero amplitude), // so it will be used to reset the phase during // synthesis. // The phase of all Breakpoints starting with pos // and ending with the Breakpoint nearest to time t // has been corrected. // Fix phases before pos starting at the beginning // of the Partial. // // Dont fix pos, it has already been fixed. fixPhaseForward( stopHere, --pos ); } }
/// Determines the canonical Euler angle triple for this /// set of angles and canonizes it. Note that while this /// doesn't affect the rotation represented by the triplet, /// if your code uses the angles for some other purpose /// (e.g., angular velocity), the results may be unexpected. /// \note See section 10.3 for more information. void EulerAngles::canonize() { // First, wrap pitch in range -pi ... pi pitch = wrapPi(pitch); // Now, check for "the back side" of the matrix, pitch outside // the canonical range of -pi/2 ... pi/2 if (pitch < -kPiOver2) { pitch = -kPi - pitch; heading += kPi; bank += kPi; } else if (pitch > kPiOver2) { pitch = kPi - pitch; heading += kPi; bank += kPi; } // OK, now check for the gimbal lock case (within a slight // tolerance) if (fabs(pitch) > kPiOver2 - 1e-4) { // We are in gimbal lock. Assign all rotation // about the vertical axis to heading heading += bank; bank = 0.0f; } else { // Not in gimbal lock. Wrap the bank angle in // canonical range bank = wrapPi(bank); } // Wrap heading in canonical range heading = wrapPi(heading); }
// processes movement and input /// \param elapsed time in seconds since the last call to this function void FreeCamera::process(float elapsed) { Matrix4x3 view; Vector3 movement = Vector3(0.0f,0.0f,0.0f); if (gInput.keyDown(DIK_UPARROW)) movement.z = 1.0f; if (gInput.keyDown(DIK_DOWNARROW)) movement.z = -1.0f; if (gInput.keyDown(DIK_LEFTARROW)) movement.x = -1.0f; if (gInput.keyDown(DIK_RIGHTARROW)) movement.x = 1.0f; if (gInput.keyDown(DIK_PGUP)) movement.y = 1.0f; if (gInput.keyDown(DIK_PGDN)) movement.y = -1.0f; // get mouse movement from input manager float lx = gInput.getMouseLX(); float ly = gInput.getMouseLY(); m_cameraOrientMoving.pitch += ly / 100.0f; m_cameraOrientMoving.heading += lx / 100.0f; // add 80% of cameraOrientMoving to cameraOrient // then remove 80% of cameraOrientMoving cameraOrient += m_cameraOrientMoving * 0.8f; m_cameraOrientMoving += m_cameraOrientMoving * -0.8f; // constrain pitch if (cameraOrient.pitch > kPiOver2) cameraOrient.pitch = kPiOver2; if (cameraOrient.pitch < -kPiOver2) cameraOrient.pitch = -kPiOver2; // wrap heading btw -pi and pi cameraOrient.heading = wrapPi(cameraOrient.heading); // create a matrix to transform the movement to world space view.setupParentToLocal(Vector3::kZeroVector, cameraOrient); view = view.inverse(); movement = movement*view; cameraPos += movement * elapsed * m_speed; }
// --------------------------------------------------------------------------- // matchPhaseFwd // //! Compute the target frequency that will affect the //! predicted (by the Breakpoint phases) amount of //! sinusoidal phase travel between two breakpoints, //! and assign that frequency to the target Breakpoint. //! After computing the new frequency, update the phase of //! the later Breakpoint. //! //! If the earlier Breakpoint is null and the later one //! is non-null, then update the phase of the earlier //! Breakpoint, and do not modify its frequency or the //! later Breakpoint. //! //! The most common kinds of errors are local (or burst) errors in //! frequency and phase. These errors are best corrected by correcting //! less than half the detected error at any time. Correcting more //! than that will produce frequency oscillations for the remainder of //! the Partial, in the case of a single bad frequency (as is common //! at the onset of a tone). Any damping factor less then one will //! converge eventually, .5 or less will converge without oscillating. //! Use the damping argument to control the damping of the correction. //! Specify 1 for no damping. //! //! \pre The two Breakpoints are assumed to be consecutive in //! a Partial. //! \param bp0 The earlier Breakpoint. //! \param bp1 The later Breakpoint. //! \param dt The time (in seconds) between bp0 and bp1. //! \param damping The fraction of the amount of phase error that will //! be corrected (.5 or less will prevent frequency oscilation //! due to burst errors in phase). //! \param maxFixPct The maximum amount of frequency adjustment //! that can be made to the frequency of bp1, expressed //! as a precentage of the unmodified frequency of bp1. //! If the necessary amount of frequency adjustment exceeds //! this amount, then the phase will not be matched, //! but will be updated as well to be consistent with //! the frequencies. (default is 0.2%) // void matchPhaseFwd( Breakpoint & bp0, Breakpoint & bp1, double dt, double damping, double maxFixPct ) { double travel = phaseTravel( bp0, bp1, dt ); if ( ! BreakpointUtils::isNonNull( bp1 ) ) { // if bp1 is null, just compute a new phase, // no need to match it. bp1.setPhase( wrapPi( bp0.phase() + travel ) ); } else if ( ! BreakpointUtils::isNonNull( bp0 ) ) { // if bp0 is null, and bp1 is not, then bp0 // should be a phase reset Breakpoint during // rendering, so compute a new phase for // bp0 that achieves bp1's phase. bp0.setPhase( wrapPi( bp1.phase() - travel ) ) ; } else { // invariant: // neither bp0 nor bp1 is null // // modify frequecies to match phases as nearly as possible double err = wrapPi( bp1.phase() - ( bp0.phase() + travel ) ); // The most common kinds of errors are local (or burst) errors in // frequency and phase. These errors are best corrected by correcting // less than half the detected error at any time. Correcting more // than that will produce frequency oscillations for the remainder of // the Partial, in the case of a single bad frequency (as is common // at the onset of a tone). Any damping factor less then one will // converge eventually, .5 or less will converge without oscillating. // #define DAMPING .5 travel += damping * err; double f0 = bp0.frequency(); double ftgt = ( travel / ( Pi * dt ) ) - f0; #ifdef Loris_Debug debugger << "matchPhaseFwd: correcting " << bp1.frequency() << " to " << ftgt << " (phase " << wrapPi( bp1.phase() ) << "), "; #endif // If the target is not a null breakpoint, may need to // clamp the amount of frequency modification. // // Actually, should probably always clamp the amount // of modulation, should never have arbitrarily large // frequency adjustments. // // Really, should never call this function if bp1 // is a null Breakpoint, because we don't care about // those phases in Loris. if ( true ) // bp1.amplitude() != 0. ) { if ( ftgt > bp1.frequency() * ( 1 + (maxFixPct*.01) ) ) { ftgt = bp1.frequency() * ( 1 + (maxFixPct*.01) ); } else if ( ftgt < bp1.frequency() * ( 1 - (maxFixPct*.01) ) ) { ftgt = bp1.frequency() * ( 1 - (maxFixPct*.01) ); } } bp1.setFrequency( ftgt ); // Recompute the phase according to the new frequency. double phi = wrapPi( bp0.phase() + phaseTravel( bp0, bp1, dt ) ); bp1.setPhase( phi ); #ifdef Loris_Debug debugger << "achieved " << ftgt << " (phase " << phi << ")" << endl; #endif } }
// --------------------------------------------------------------------------- // fixPhaseBetween // //! Fix the phase travel between two Breakpoints by adjusting the //! frequency and phase of Breakpoints between those two. //! //! This algorithm assumes that there is nothing interesting about the //! phases of the intervening Breakpoints, and modifies their frequencies //! as little as possible to achieve the correct amount of phase travel //! such that the frequencies and phases at the specified times //! match the stored values. The phases of all the Breakpoints between //! the specified times are recomputed. //! //! Null Breakpoints are treated the same as non-null Breakpoints. //! //! \pre b and e are iterators on the same Partials, and //! e must not preceed b in that Partial. //! \pre There must be at least one Breakpoint in the //! Partial between b and e. //! \post The phases and frequencies of the Breakpoints in the //! range have been recomputed such that an oscillator //! initialized to the parameters of the first Breakpoint //! will arrive at the parameters of the last Breakpoint, //! and all the intervening Breakpoints will be matched. //! \param b The phases and frequencies of Breakpoints later than //! this one may be modified. //! \param e The phases and frequencies of Breakpoints earlier than //! this one may be modified. // void fixPhaseBetween( Partial::iterator b, Partial::iterator e ) { if ( 1 < std::distance( b, e ) ) { // Accumulate the actual phase travel over the Breakpoint // span, and count the envelope segments. double travel = 0; Partial::iterator next = b; do { Partial::iterator prev = next++; travel += phaseTravel( prev, next ); } while( next != e ); // Compute the desired amount of phase travel: double deviation = wrapPi( e.breakpoint().phase() - ( b.breakpoint().phase() + travel ) ); double desired = travel + deviation; /* debugger << "---------- fixing breakpoint frequencies over time ( " << b.time() << " , " << e.time() << " )" << endl; debugger << "desired travel: " << desired << endl; debugger << "actual travel: " << travel << endl; */ // Compute the amount by which to perturb the frequencies of // all the null Breakpoints between b and e. // // The accumulated phase travel is the sum of the average frequency // (in radians) of each segment times the duration of each segment // (the actual phase travel is computed this way). If this sum is // computed with each Breakpoint frequency perturbed (additively) // by delta, and set equal to the desired phase travel, then it // can be simplified to: // delta = 2 * ( phase error ) / ( tN + tN-1 - t1 - t0 ) // where tN is the time of e, tN-1 is the time of its predecessor, // t0 is the time of b, and t1 is the time of b's successor. // Partial::iterator iter = b; double t0 = iter.time(); ++iter; double t1 = iter.time(); iter = e; double tN = iter.time(); --iter; double tNm1 = iter.time(); Assert( t1 < tN ); // else there were no Breakpoints in between double delta = ( 2 * ( desired - travel ) / ( tN + tNm1 - t1 - t0 ) ) / ( 2 * Pi ); // Perturb the Breakpoint frequencies. double DEBUGtravel = 0; next = b; Partial::iterator prev = next++; while ( next != e ) { //debugger << "changing frequency from " << next.breakpoint().frequency(); next.breakpoint().setFrequency( next.breakpoint().frequency() + delta ); //debugger << " to " << next.breakpoint().frequency() << endl; double newtravel = phaseTravel( prev, next ); DEBUGtravel += newtravel; //debugger << "changing phase from " << wrapPi( next.breakpoint().phase() ); next.breakpoint().setPhase( wrapPi( prev.breakpoint().phase() + newtravel ) ); //debugger << " to " << next.breakpoint().phase() << endl; prev = next++; } DEBUGtravel += phaseTravel( prev, next ); /* debugger << "travel: " << DEBUGtravel << endl; debugger << "desired: " << e.breakpoint().phase() << endl; debugger << "got: " << wrapPi( prev.breakpoint().phase() + phaseTravel( prev, next ) ) << endl; debugger << "---------- done." << endl; */ } else { // Preconditions not met, cannot fix the phase travel. // Should raise exception? debugger << "cannot fix phase between " << b.time() << " and " << e.time() << ", there are no Breakpoints between those times" << endl; } }