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); }