void mCoordinator_Tick()
        {
            #if DEBUG
            mStats.Begin();
            #endif
            mCore.Update(mCurrent.Position, mCurrent.Position - mPrev.Position, mCurrent.Orientation, mCurrent.Orientation - mPrev.Orientation);

            IncrementTime();
            mPrev = mCurrent;
            mCurrent = mEvents.CurrentEvent.Value;
            #if DEBUG
            mStats.End();
            #endif
        }
        private void FlythroughThread()
        {
            mFinished = false;
            mPlaying = true;
            #if DEBUG
            mStats.Begin();
            #endif
            while (mPlaying && mEvents.Length > 0) {
                IncrementTime();
                mPrev = mCurrent;
                mCurrent = mEvents.CurrentEvent.Value;
            #if DEBUG
                mStats.End();
            #endif

                //double wait = (mCore.TickLength * (1.0 / mSpeed)) - DateTime.Now.Subtract(mLastTick).TotalMilliseconds;
                //double wait = mCore.TickLength - DateTime.Now.Subtract(mLastTick).TotalMilliseconds;
            //Merge conflict - wait UTC or now? wait <= or < 0?
                //if (wait < 0)
                double wait = mCore.TickLength - DateTime.UtcNow.Subtract(mLastTick).TotalMilliseconds;
                if (wait <= 0)
                    Logger.Debug("Flythrough Tick overran by " + (wait * -1) + "ms.");
                else
                    System.Threading.Thread.Sleep((int)wait);
            #if DEBUG
                mStats.Begin();
            #endif
                mLastTick = DateTime.UtcNow;
                mCore.Update(mCurrent.Position, mCurrent.Position - mPrev.Position, mCurrent.Orientation, mCurrent.Orientation - mPrev.Orientation);
            }
            lock (mFinishLock) {
                mFinished = true;
                Monitor.PulseAll(mFinishLock);
            }
        }
        /// <summary>
        /// Initialise the flythrough transition an xml file.
        /// </summary>
        /// <param name="file">The file to load as a flythrough.</param>
        public void Load(string file)
        {
            if (!File.Exists(file)) {
                Logger.Warn("Unable to load " + file + ". Ignoring load request.");
                return;
            }

            if (FlythroughLoading != null)
                FlythroughLoading();

            mEvents = new EventSequence<Camera>();
            mEvents.LengthChange += new Action<EventSequence<Camera>, int>(mEvents_LengthChange);

            XmlDocument doc = new XmlDocument();
            doc.Load(file);
            int start = 0;
            XmlNode root = doc.GetElementsByTagName("Events")[0];

            XmlAttribute startPositionAttr = root.Attributes["StartPosition"];
            XmlAttribute startPitchAttr = root.Attributes["StartPitch"];
            XmlAttribute startYawAttr = root.Attributes["StartYaw"];
            Vector3 startPos = mCore.Position;
            double startPitch = mCore.Orientation.Pitch;
            double startYaw = mCore.Orientation.Yaw;
            if (startPositionAttr != null) Vector3.TryParse(startPositionAttr.Value, out startPos);
            if (startPitchAttr != null) double.TryParse(startPitchAttr.Value, out startPitch);
            if (startYawAttr != null) double.TryParse(startYawAttr.Value, out startYaw);
            Start = new Camera(startPos, new Rotation(startPitch, startYaw));

            foreach (XmlNode node in root.ChildNodes) {
                if (node is XmlElement) {
                    ComboEvent evt = new ComboEvent(this);
                    evt.Load(node);
                    mEvents.AddEvent(evt);
                    start = evt.SequenceStartTime + evt.Length;
                }
            }

            mCore.Update(Start.Position, Vector3.Zero, Start.Orientation, Rotation.Zero);

            if (FlythroughLoaded != null)
                FlythroughLoaded();
        }
        public void Play()
        {
            if (mPlaying)
                return;

            if (UnPaused != null)
                UnPaused();

            if (mEnabled && mEvents.Length > 0) {
                if (mEvents.CurrentEvent == null)
                    Time = 0;
                mCurrent = mEvents.CurrentEvent.Value;
                mCore.Update(mCurrent.Position, Vector3.Zero, mCurrent.Orientation, Rotation.Zero);
                mPrev = mCurrent;
                mLastTick = DateTime.UtcNow;
                if (mSeparateThread) {
                    Thread t = new Thread(FlythroughThread);
                    t.Name = "Flythrough thread";
                    //t.Priority = ThreadPriority.Highest;
                    t.Start();
                } else
                    mCore.Tick += mTickListener;
            }
        }
        public FlythroughPlugin()
        {
            Start = new Camera(new Vector3(128f, 128f, 60f), Rotation.Zero);
            mEvents.Start = Start;
            mEvents.LengthChange += new Action<EventSequence<Camera>, int>(mEvents_LengthChange);
            mTickListener = new Action(mCoordinator_Tick);

            FlythroughConfig cfg = new FlythroughConfig();
            mSynchLengths = cfg.SynchLengths;
        }