/// <summary> /// Finds a good position to set the anchor. /// 1. If we have an anchor stored in the player prefs/ anchor store, use that /// 2. If we don't have spatial mapping, just use where the object happens to be /// 3. if we do have spatial mapping, anchor at a vertex dense portion of spatial mapping /// </summary> private void FindAnchorPosition() { // 1. recover a stored anchor if we can if (PlayerPrefs.HasKey(SavedAnchorKey) && AttachToCachedAnchor(PlayerPrefs.GetString(SavedAnchorKey))) { exportingAnchorName = PlayerPrefs.GetString(SavedAnchorKey); Debug.Log("found " + exportingAnchorName + " again"); ExportAnchor(); } // 2. just use the current object position if we don't have access to spatial mapping else if (spatialMapping == null) { if (UseSpatialMapping) { Debug.Log("No spatial mapping..."); } ExportAnchorAtPosition(objectToAnchor.transform.position); } // 3. seek a vertex dense portion of spatial mapping else { ReadOnlyCollection <SpatialMappingSource.SurfaceObject> surfaces = spatialMapping.GetSurfaceObjects(); if (surfaces == null || surfaces.Count == 0) { // If we aren't getting surfaces we may need to start the observer. if (spatialMapping.IsObserverRunning() == false) { spatialMapping.StartObserver(); StartedObserver = true; } // And try again after the observer has a chance to get an update. Invoke("FindAnchorPosition", spatialMapping.GetComponent <SpatialMappingObserver>().TimeBetweenUpdates); } else { float startTime = Time.realtimeSinceStartup; // If we have surfaces, we need to iterate through them to find a dense area // of geometry, which should provide a good spot for an anchor. Mesh bestMesh = null; MeshFilter bestFilter = null; int mostVerts = 0; for (int index = 0; index < surfaces.Count; index++) { // If the current surface doesn't have a filter or a mesh, skip to the next one // This happens as a surface is being processed. We need to track both the mesh // and the filter because the mesh has the verts in local space and the filter has the transform to // world space. MeshFilter currentFilter = surfaces[index].Filter; if (currentFilter == null) { continue; } Mesh currentMesh = currentFilter.sharedMesh; if (currentMesh == null) { continue; } // If we have a collider we can use the extents to estimate the volume. MeshCollider currentCollider = surfaces[index].Collider; float volume = currentCollider == null ? 1.0f : currentCollider.bounds.extents.magnitude; // get th verts divided by the volume if any int meshVerts = (int)(currentMesh.vertexCount / volume); // and if this is most verts/volume we've seen, record this mesh as the current best. mostVerts = Mathf.Max(meshVerts, mostVerts); if (mostVerts == meshVerts) { bestMesh = currentMesh; bestFilter = currentFilter; } } // If we have a good area to use, then use it. if (bestMesh != null && mostVerts > 100) { // Get the average of the vertices Vector3[] verts = bestMesh.vertices; Vector3 avgVert = verts.Average(); // transform the average into world space. Vector3 center = bestFilter.transform.TransformPoint(avgVert); Debug.LogFormat("found a good mesh mostVerts = {0} processed {1} meshes in {2} ms", mostVerts, surfaces.Count, 1000 * (Time.realtimeSinceStartup - startTime)); // then export the anchor where we've calculated. ExportAnchorAtPosition(center); } else { // If we didn't find a good mesh, try again a little later. Debug.LogFormat("Failed to find a good mesh mostVerts = {0} processed {1} meshes in {2} ms", mostVerts, surfaces.Count, 1000 * (Time.realtimeSinceStartup - startTime)); Invoke("FindAnchorPosition", spatialMapping.GetComponent <SpatialMappingObserver>().TimeBetweenUpdates); } } } }