private void UpdateDragPlane()
        {
            Refl.Invoke(EditorLogic.fetch, "CenterDragPlane", EditorLogic.SelectedPart.transform.position + EditorLogic.fetch.selPartGrabOffset);
            var args = new object[] { new Vector3() };

            Refl.Invoke(EditorLogic.fetch, "dragOverPlane", args);
            EditorLogic.SelectedPart.transform.position = (Vector3)args [0];
        }
        public void LateUpdate()
        {
            if (Input.GetKeyDown(config.keySwitchMode))
            {
                SwitchMode();
            }
            if (selectedPart && EditorLogic.SelectedPart == null)
            {
                SwitchMode(false, false);
            }
            if (!cameraEnabled)
            {
                return;
            }
            if (EditorLogic.SelectedPart != null)
            {
                SwitchMode(false, true);
                return;
            }

            cursorLocker.LockUpdate();

            bool isDown   = Input.GetKey(KeyCode.Mouse1);
            bool goneDown = isDown && !mouseWasDown;
            bool goneUp   = !isDown && mouseWasDown;

            mouseWasDown = isDown;

            bool shiftIsDown = Input.GetKey(KeyCode.LeftShift);

            var isTweaking = (Input.GetMouseButton(0) &&
                              (editorFSM.currentStateName == "st_offset_tweak" || editorFSM.currentStateName == "st_rotate_tweak"));

            if (isTweaking)
            {
                return;
            }

            float dx = 0, dy = 0;

            if (isDown)
            {
                dx     = Input.GetAxis("Mouse X");
                dy     = Input.GetAxis("Mouse Y");
                yaw   += 0.22f * dx * config.sensitivity;
                yaw   %= 360;
                pitch += -0.22f * dy * config.sensitivity;
                pitch  = Mathf.Clamp(pitch, -90, 90);
            }

            if (goneDown)
            {
                // Locks part rotation with WASD keys. Doesn't actually seem to affect gizmos.
                InputLockManager.SetControlLock(ControlTypes.EDITOR_GIZMO_TOOLS, this.GetType().Name);

                if (EditorLogic.SelectedPart != null)
                {
                    partOffset  = EditorLogic.SelectedPart.transform.position - EditorLogic.fetch.editorCamera.transform.position;
                    partOffset += EditorLogic.fetch.selPartGrabOffset;
                    partOffset  = EditorLogic.fetch.editorCamera.transform.InverseTransformVector(partOffset);
                }

                Screen.showCursor = false;
                cursorLocker.PrepareLock();
                cursorLocker.LockCursor();
            }

            if (goneUp)
            {
                InputLockManager.RemoveControlLock(this.GetType().Name);

                Screen.showCursor = true;
                cursorLocker.UnlockCursor();

                if (movePart)
                {
                    EditorBounds.Instance.constructionBounds = movementBounds;
                    UpdateDragPlane();
                }
                movePart = false;
            }

            if (EditorLogic.SelectedPart == null)
            {
                movePart = false;
            }

            // WASD in place mode is part rotation.
            var simpleMove = EditorLogic.SelectedPart == null || EditorLogic.fetch.EditorConstructionMode != ConstructionMode.Place;

            if (isDown || simpleMove)
            {
                var rot  = Quaternion.AngleAxis(yaw, Vector3.up) * Quaternion.AngleAxis(pitch, Vector3.right);
                var fwd  = rot * Vector3.forward;
                var side = rot * Vector3.right;

                Vector3 acc = Vector3.zero;
                if (Input.GetKey(config.keyForward))
                {
                    acc += fwd;
                }
                if (config.mouseWheelActive && Input.GetAxis("Mouse ScrollWheel") > 0 && shiftIsDown)
                {
                    acc += fwd * config.mouseWheelAcceleration;
                }
                if (Input.GetKey(config.keyBack))
                {
                    acc -= fwd;
                }
                if (config.mouseWheelActive && Input.GetAxis("Mouse ScrollWheel") < 0 && shiftIsDown)
                {
                    acc -= fwd * config.mouseWheelAcceleration;
                }
                if (Input.GetKey(config.keyRight))
                {
                    acc += side;
                }
                if (Input.GetKey(config.keyLeft))
                {
                    acc -= side;
                }
                if (Input.GetKey(config.keyDown))
                {
                    acc -= Vector3.up;
                }
                if (config.mouseWheelActive && Input.GetAxis("Mouse ScrollWheel") < 0 && !shiftIsDown)
                {
                    acc -= Vector3.up * config.mouseWheelAcceleration;
                }
                if (Input.GetKey(config.keyUp))
                {
                    acc += Vector3.up;
                }
                if (config.mouseWheelActive && Input.GetAxis("Mouse ScrollWheel") > 0 && !shiftIsDown)
                {
                    acc += Vector3.up * config.mouseWheelAcceleration;
                }
                if (Input.GetKey(config.keyRun))
                {
                    acc *= config.runMultiplier;
                }
                if (Input.GetKey(config.keySneak))
                {
                    acc *= config.sneakMultiplier;
                }
                acc *= config.acceleration;
                vel += (acc - config.friction * vel) * Time.deltaTime;
                var delta = vel * Time.deltaTime;

                EditorLogic.fetch.editorCamera.transform.rotation = rot;

                if (!movePart && EditorLogic.SelectedPart != null && EditorLogic.fetch.EditorConstructionMode == ConstructionMode.Place &&
                    (delta != Vector3.zero || dx != 0 || dy != 0))
                {
                    movePart = true;
                    // A convenient trick to prevent EditorLogic::dragOverPlane from updating the part position.
                    EditorBounds.Instance.constructionBounds = ZERO_BOUNDS;
                }

                if (movePart || simpleMove)
                {
                    pos += delta;

                    float distance = 0;
                    movementBounds.IntersectRay(new Ray(pos, -delta), out distance);
                    //distance = -1;
                    //log.Debug("POS {0} {1} {2}", pos, yaw, pitch);
                    if (distance < 0)
                    {
                        EditorLogic.fetch.editorCamera.transform.position = pos;
                    }
                    else
                    {
                        EditorLogic.fetch.editorCamera.transform.position = GetClosestPointOnBounds(movementBounds, pos);
                        pos = EditorLogic.fetch.editorCamera.transform.position;
                    }

                    if (movePart)
                    {
                        Refl.Invoke(EditorLogic.fetch, "displayAttachNodeIcons", false, false, false);

                        var offset = EditorLogic.fetch.editorCamera.transform.TransformPoint(partOffset);
                        EditorLogic.SelectedPart.transform.position = offset - EditorLogic.fetch.selPartGrabOffset;
                    }
                }
            }
        }