public override void OnInspectorGUI()
    {
        GUIStyle bold_wrap = EditorStyles.boldLabel;

        bold_wrap.wordWrap = true;
        GUILayout.Label("Navmesh Preprocessor for HTC Vive Locomotion", bold_wrap);
        GUILayout.Label("Adrian Biagioli 2017", EditorStyles.miniLabel);

        GUILayout.Label("Before Using", bold_wrap);
        GUIStyle wrap = EditorStyles.label;

        wrap.wordWrap = true;
        GUILayout.Label(
            "Make sure you bake a Navigation Mesh (NavMesh) in Unity before continuing (Window > Navigation).  When you " +
            "are done, click \"Update Navmesh Data\" below.  This will update the graphic of the playable area " +
            "that the player will see in-game.\n",
            wrap);

        VRNavMesh mesh = (VRNavMesh)target;

        serializedObject.Update();

        // Area Mask //
        string[] areaNames  = GameObjectUtility.GetNavMeshAreaNames();
        int[]    area_index = new int[areaNames.Length];
        int      temp_mask  = 0;

        for (int x = 0; x < areaNames.Length; x++)
        {
            area_index[x] = GameObjectUtility.GetNavMeshAreaFromName(areaNames[x]);
            temp_mask    |= ((p_area.intValue >> area_index[x]) & 1) << x;
        }
        EditorGUI.BeginChangeCheck();
        temp_mask = EditorGUILayout.MaskField("Area Mask", temp_mask, areaNames);
        if (EditorGUI.EndChangeCheck())
        {
            p_area.intValue = 0;
            for (int x = 0; x < areaNames.Length; x++)
            {
                p_area.intValue |= (((temp_mask >> x) & 1) == 1 ? 0 : 1) << area_index[x];
            }
            p_area.intValue = ~p_area.intValue;
        }
        serializedObject.ApplyModifiedProperties();

        // Sanity check for Null properties //
        bool HasMesh = (mesh.SelectableMesh != null && mesh.SelectableMesh.vertexCount != 0) || (mesh.SelectableMeshBorder != null && mesh.SelectableMeshBorder.Length != 0);

        // Fixes below error message popping up with prefabs.  Kind of hacky but gets the job done
        bool isPrefab = EditorUtility.IsPersistent(target);

        if (isPrefab && mesh.SelectableMesh == null)
        {
            mesh.SelectableMesh = new Mesh();
        }

        bool MeshNull   = mesh.SelectableMesh == null;
        bool BorderNull = mesh.SelectableMeshBorder == null;

        if (MeshNull || BorderNull)
        {
            string str = "Internal Error: ";
            if (MeshNull)
            {
                str += "Selectable Mesh == null.  ";
            }
            if (BorderNull)
            {
                str += "Border point array == null.  ";
            }
            str += "This may lead to strange behavior or serialization.  Try updating the mesh or delete and recreate the Navmesh object.  ";
            str += "If you are able to consistently get a Vive Nav Mesh object into this state, please submit a bug report.";
            EditorGUILayout.HelpBox(str, MessageType.Error);
        }

        // Update / Clear Navmesh Data //
        if (GUILayout.Button("Update Navmesh Data"))
        {
            Undo.RecordObject(mesh, "Update Navmesh Data");

            NavMeshTriangulation tri = NavMesh.CalculateTriangulation();

            Vector3[] verts = tri.vertices;
            int[]     tris  = tri.indices;
            int[]     areas = tri.areas;

            int vert_size = verts.Length;
            int tri_size  = tris.Length;
            RemoveMeshDuplicates(verts, tris, out vert_size, 0.01f);
            DewarpMesh(verts, mesh.DewarpingMethod, mesh.SampleRadius);
            CullNavmeshTriangulation(verts, tris, areas, p_area.intValue, mesh.IgnoreSlopedSurfaces, ref vert_size, ref tri_size);

            Mesh m = ConvertNavmeshToMesh(verts, tris, vert_size, tri_size);
            // Can't use SerializedProperties here because BorderPointSet doesn't derive from UnityEngine.Object
            mesh.SelectableMeshBorder = FindBorderEdges(m);

            serializedObject.Update();
            p_mesh.objectReferenceValue = m;
            serializedObject.ApplyModifiedPropertiesWithoutUndo();
            mesh.SelectableMesh = mesh.SelectableMesh; // Make sure that setter is called
        }

        GUI.enabled = HasMesh;
        if (GUILayout.Button("Clear Navmesh Data"))
        {
            Undo.RecordObject(mesh, "Clear Navmesh Data");

            // Note: Unity does not serialize "null" correctly so we set everything to empty objects
            Mesh m = new Mesh();

            serializedObject.Update();
            p_mesh.objectReferenceValue = m;
            serializedObject.ApplyModifiedPropertiesWithoutUndo();
            mesh.SelectableMesh = mesh.SelectableMesh; // Make sure setter is called

            mesh.SelectableMeshBorder = new BorderPointSet[0];
        }
        GUI.enabled = true;

        GUILayout.Label(HasMesh ? "Status: NavMesh Loaded" : "Status: No NavMesh Loaded");

        // Render Settings //
        EditorGUILayout.LabelField("Render Settings", EditorStyles.boldLabel);

        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(p_material);
        if (EditorGUI.EndChangeCheck())
        {
            Undo.RecordObject(mesh, "Change Ground Material");
            serializedObject.ApplyModifiedPropertiesWithoutUndo();
            mesh.GroundMaterial = new Material((Material)p_material.objectReferenceValue); // Reload material
        }

        EditorGUILayout.PropertyField(p_alpha);
        serializedObject.ApplyModifiedProperties();

        // Raycast Settings //
        EditorGUILayout.LabelField("Raycast Settings", EditorStyles.boldLabel);

        int  temp_layer_mask        = p_layer_mask.intValue;
        bool temp_ignore_layer_mask = p_ignore_layer_mask.boolValue;

        EditorGUI.BeginChangeCheck();
        temp_layer_mask = LayerMaskField("Layer Mask", temp_layer_mask);
        if (EditorGUI.EndChangeCheck())
        {
            p_layer_mask.intValue = temp_layer_mask;
        }
        serializedObject.ApplyModifiedProperties();
        EditorGUI.BeginChangeCheck();
        temp_ignore_layer_mask = EditorGUILayout.Toggle("Ignore Layer Mask", temp_ignore_layer_mask);
        if (EditorGUI.EndChangeCheck())
        {
            p_ignore_layer_mask.boolValue = temp_ignore_layer_mask;
        }
        serializedObject.ApplyModifiedProperties();

        QueryTriggerInteraction temp_query_trigger_interaction = (QueryTriggerInteraction)p_query_trigger_interaction.intValue;

        EditorGUI.BeginChangeCheck();
        temp_query_trigger_interaction = (QueryTriggerInteraction)EditorGUILayout.EnumPopup("Query Trigger Interaction", (QueryTriggerInteraction)temp_query_trigger_interaction);
        if (EditorGUI.EndChangeCheck())
        {
            p_query_trigger_interaction.intValue = (int)temp_query_trigger_interaction;
        }
        serializedObject.ApplyModifiedProperties();

        // Navmesh Settings //
        EditorGUILayout.LabelField("Navmesh Settings", EditorStyles.boldLabel);
        GUILayout.Label(
            "Make sure the sample radius below is equal to your Navmesh Voxel Size (see Advanced > Voxel Size " +
            "in the navigation window).  Increase this if the selection disk is not appearing.",
            wrap);
        EditorGUILayout.PropertyField(p_sample_radius);
        EditorGUILayout.PropertyField(p_ignore_sloped_surfaces);
        EditorGUILayout.PropertyField(p_dewarp_method);

        serializedObject.ApplyModifiedProperties();
    }
    // Sample a bunch of points along a parabolic curve until you hit gnd.  At that point, cut off the parabola
    // p0: starting point of parabola
    // v0: initial parabola velocity
    // a: initial acceleration
    // dist: distance between sample points
    // points: number of sample points
    // gnd: height of the ground, in meters above y=0
    // outPts: List that will be populated by new points
    // normal: normal of hit point

    /// <summary>
    ///
    /// </summary>
    /// <param name="p0">Origin point</param>
    /// <param name="v0">Initial velocity</param>
    /// <param name="a">Initial acceleration</param>
    /// <param name="dist">Distance between points</param>
    /// <param name="points">Number of points</param>
    /// <param name="nav">Navigation surface</param>
    /// <param name="outPts"></param>
    /// <param name="normal"></param>
    /// <returns></returns>
    private static bool CalculateParabolicCurve(Vector3 p0, Vector3 v0, Vector3 a, float dist, int points, VRNavMesh nav, List <Vector3> outPts, out Vector3 normal)
    {
        outPts.Clear();
        outPts.Add(p0);

        Vector3 last = p0;
        float   t    = 0;

        for (int i = 0; i < points; i++)
        {
            t += dist / ParabolicCurveDeriv(v0, a, t).magnitude;
            Vector3 next = ParabolicCurve(p0, v0, a, t);

            Vector3 castHit;
            Vector3 norm;
            bool    endOnNavmesh;
            bool    cast = nav.Linecast(last, next, out endOnNavmesh, out castHit, out norm);
            if (cast)
            {
                outPts.Add(castHit);
                normal = norm;
                return(endOnNavmesh);
            }
            else
            {
                outPts.Add(next);
            }

            last = next;
        }

        normal = Vector3.up;
        return(false);
    }