private static void AssertExpectedPlane(BoundedPlane plane, Vector3 bounds, Matrix4x4 vertDataTransform, Matrix4x4 meshTransform) { float expectedArea = 4.0f * bounds.x * bounds.y; Vector3 expectedPlaneNormal = meshTransform.TransformDirection(vertDataTransform.TransformDirection(new Vector3(0, 0, 1))); Vector3 expectedPlaneCenter = meshTransform.TransformPoint(vertDataTransform.TransformPoint(Vector3.zero)); Assert.AreEqual(expectedArea, plane.Area, expectedArea * 0.01f); AssertAreEqual(expectedPlaneNormal, plane.Plane.normal, 0.01f, "Normal"); AssertAreEqual(expectedPlaneCenter, plane.Bounds.Center, bounds.Length() * 0.01f, "Center"); }
private List <GameObject> GetTablesInDescendingAreaOrder() { List <GameObject> floors = m_playspace_manager.GetTables(); floors.Sort((plane1, plane2) => { HoloToolkit.Unity.BoundedPlane bp1 = plane1.GetComponent <HoloToolkit.Unity.SurfacePlane>().Plane; HoloToolkit.Unity.BoundedPlane bp2 = plane2.GetComponent <HoloToolkit.Unity.SurfacePlane>().Plane; // Sort descending return(bp2.Area.CompareTo(bp1.Area)); }); return(floors); }
/// <summary> /// Marshals BoundedPlane data returned from a DLL API call into a managed BoundedPlane array /// and then frees the memory that was allocated within the DLL. /// </summary> /// <remarks>Disabling warning 618 when calling Marshal.SizeOf(), because /// Unity does not support .Net 4.5.1+ for using the preferred Marshal.SizeOf(T) method."/>, </remarks> private static BoundedPlane[] MarshalBoundedPlanesFromIntPtr(IntPtr outArray, int size) { BoundedPlane[] resArray = new BoundedPlane[size]; #pragma warning disable 618 int structsize = Marshal.SizeOf(typeof(BoundedPlane)); #pragma warning restore 618 IntPtr current = outArray; for (int i = 0; i < size; i++) { #pragma warning disable 618 resArray[i] = (BoundedPlane)Marshal.PtrToStructure(current, typeof(BoundedPlane)); #pragma warning restore 618 current = (IntPtr)((long)current + structsize); } Marshal.FreeCoTaskMem(outArray); return(resArray); }
/// <summary> /// Iterator block, analyzes surface meshes to find planes and create new 3D cubes to represent each plane. /// </summary> /// <returns>Yield result.</returns> private IEnumerator MakePlanesRoutine() { // Remove any previously existing planes, as they may no longer be valid. for (int index = 0; index < ActivePlanes.Count; index++) { Destroy(ActivePlanes[index]); } // Pause our work, and continue on the next frame. yield return(null); float start = Time.realtimeSinceStartup; ActivePlanes.Clear(); // Get the latest Mesh data from the Spatial Mapping Manager. List <PlaneFinding.MeshData> meshData = new List <PlaneFinding.MeshData>(); List <MeshFilter> filters = SpatialMappingManager.Instance.GetMeshFilters(); for (int index = 0; index < filters.Count; index++) { MeshFilter filter = filters[index]; if (filter != null && filter.sharedMesh != null) { // fix surface mesh normals so we can get correct plane orientation. filter.mesh.RecalculateNormals(); meshData.Add(new PlaneFinding.MeshData(filter)); } if ((Time.realtimeSinceStartup - start) > FrameTime) { // Pause our work, and continue to make more PlaneFinding objects on the next frame. yield return(null); start = Time.realtimeSinceStartup; } } // Pause our work, and continue on the next frame. yield return(null); #if !UNITY_EDITOR && UNITY_METRO // When not in the unity editor we can use a cool background task to help manage FindPlanes(). Task <BoundedPlane[]> planeTask = Task.Run(() => PlaneFinding.FindPlanes(meshData, snapToGravityThreshold, MinArea)); while (planeTask.IsCompleted == false) { yield return(null); } BoundedPlane[] planes = planeTask.Result; #else // In the unity editor, the task class isn't available, but perf is usually good, so we'll just wait for FindPlanes to complete. BoundedPlane[] planes = PlaneFinding.FindPlanes(meshData, snapToGravityThreshold, MinArea); #endif // Pause our work here, and continue on the next frame. yield return(null); start = Time.realtimeSinceStartup; float maxFloorArea = 0.0f; float maxCeilingArea = 0.0f; FloorYPosition = 0.0f; CeilingYPosition = 0.0f; float upNormalThreshold = 0.9f; if (SurfacePlanePrefab != null && SurfacePlanePrefab.GetComponent <SurfacePlane>() != null) { upNormalThreshold = SurfacePlanePrefab.GetComponent <SurfacePlane>().UpNormalThreshold; } // Find the floor and ceiling. // We classify the floor as the maximum horizontal surface below the user's head. // We classify the ceiling as the maximum horizontal surface above the user's head. for (int i = 0; i < planes.Length; i++) { BoundedPlane boundedPlane = planes[i]; if (boundedPlane.Bounds.Center.y < 0 && boundedPlane.Plane.normal.y >= upNormalThreshold) { maxFloorArea = Mathf.Max(maxFloorArea, boundedPlane.Area); if (maxFloorArea == boundedPlane.Area) { FloorYPosition = boundedPlane.Bounds.Center.y; } } else if (boundedPlane.Bounds.Center.y > 0 && boundedPlane.Plane.normal.y <= -(upNormalThreshold)) { maxCeilingArea = Mathf.Max(maxCeilingArea, boundedPlane.Area); if (maxCeilingArea == boundedPlane.Area) { CeilingYPosition = boundedPlane.Bounds.Center.y; } } } // Create SurfacePlane objects to represent each plane found in the Spatial Mapping mesh. for (int index = 0; index < planes.Length; index++) { GameObject destPlane; BoundedPlane boundedPlane = planes[index]; // Instantiate a SurfacePlane object, which will have the same bounds as our BoundedPlane object. if (SurfacePlanePrefab != null && SurfacePlanePrefab.GetComponent <SurfacePlane>() != null) { destPlane = Instantiate(SurfacePlanePrefab); } else { destPlane = GameObject.CreatePrimitive(PrimitiveType.Cube); destPlane.AddComponent <SurfacePlane>(); destPlane.GetComponent <Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; } destPlane.transform.parent = planesParent.transform; SurfacePlane surfacePlane = destPlane.GetComponent <SurfacePlane>(); // Set the Plane property to adjust transform position/scale/rotation and determine plane type. surfacePlane.Plane = boundedPlane; SetPlaneVisibility(surfacePlane); if ((destroyPlanesMask & surfacePlane.PlaneType) == surfacePlane.PlaneType) { DestroyImmediate(destPlane); } else { // Set the plane to use the same layer as the SpatialMapping mesh. destPlane.layer = SpatialMappingManager.Instance.PhysicsLayer; ActivePlanes.Add(destPlane); } // If too much time has passed, we need to return control to the main game loop. if ((Time.realtimeSinceStartup - start) > FrameTime) { // Pause our work here, and continue making additional planes on the next frame. yield return(null); start = Time.realtimeSinceStartup; } } Debug.Log("Finished making planes."); // We are done creating planes, trigger an event. EventHandler handler = MakePlanesComplete; if (handler != null) { handler(this, EventArgs.Empty); } makingPlanes = false; }
private void Awake() { plane = new BoundedPlane(transform); }
// Used for gameObject initialization. private void Start() { plane = new BoundedPlane(); }
/// <summary> /// Marshals BoundedPlane data returned from a DLL API call into a managed BoundedPlane array /// and then frees the memory that was allocated within the DLL. /// </summary> /// <remarks>Disabling warning 618 when calling Marshal.SizeOf(), because /// Unity does not support .Net 4.5.1+ for using the preferred Marshal.SizeOf(T) method."/>, </remarks> private static BoundedPlane[] MarshalBoundedPlanesFromIntPtr(IntPtr outArray, int size) { BoundedPlane[] resArray = new BoundedPlane[size]; #pragma warning disable 618 int structsize = Marshal.SizeOf(typeof(BoundedPlane)); #pragma warning restore 618 IntPtr current = outArray; for (int i = 0; i < size; i++) { #pragma warning disable 618 resArray[i] = (BoundedPlane)Marshal.PtrToStructure(current, typeof(BoundedPlane)); #pragma warning restore 618 current = (IntPtr)((long)current + structsize); } Marshal.FreeCoTaskMem(outArray); return resArray; }
/// <summary> /// Takes the subplanes returned by one or more previous calls to FindSubPlanes() and merges /// them together into larger planes that can potentially span across multiple meshes. /// Overlapping subplanes that have similar plane equations will be merged together to form /// larger planes. /// </summary> /// <param name="subPlanes"> /// The output from one or more previous calls to FindSubPlanes(). /// </param> /// <param name="snapToGravityThreshold"> /// Planes whose normal vectors are within this threshold (in degrees) from vertical/horizontal /// will be snapped to be perfectly gravity aligned. When set to something other than zero, the /// bounding boxes for each plane will be gravity aligned as well, rather than rotated for an /// optimally tight fit. Pass 0.0 for this parameter to completely disable the gravity alignment /// logic. /// </param> /// <param name="minArea"> /// While merging subplanes together, any candidate merged plane whose constituent mesh /// triangles have a total area less than this threshold are ignored. /// </param> public static BoundedPlane[] MergeSubPlanes(BoundedPlane[] subPlanes, float snapToGravityThreshold = 0.0f, float minArea = 0.0f) { StartPlaneFinding(); try { int planeCount; IntPtr planesPtr; DLLImports.MergeSubPlanes(subPlanes.Length, PinObject(subPlanes), minArea, snapToGravityThreshold, out planeCount, out planesPtr); return MarshalBoundedPlanesFromIntPtr(planesPtr, planeCount); } finally { FinishPlaneFinding(); } }