/// <summary> /// Set the grid index as having been observed from the given direction. /// </summary> /// <param name="gridIndex">Grid index to observe.</param> private void _ViewGridIndex(Tango3DReconstruction.GridIndex gridIndex) { // This update may occur somewhat later than the actual time of the camera pose observation. Vector3 dir = Camera.main.transform.forward; dir = new Vector3(dir.x, 0.0f, dir.z).normalized; Vector3[] directions = new Vector3[] { new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(1, 0, 0), new Vector3(-1, 0, 0) }; for (int i = 0; i < 4; i++) { // If the camera is facing one of 4 directions (every 90 degrees) within a 45 degree spread, // set that direction as seen. float dot = Vector3.Dot(dir, directions[i]); if (dot > m_minDirectionCheck) { // Bitwise OR new and old directions to show that the mesh has been observed from the new direction. byte direction = (byte)(1 << i); m_meshes[gridIndex].m_directions = (byte)(m_meshes[gridIndex].m_directions | direction); break; } } }
/// <summary> /// Update the bounding box. /// </summary> /// <param name="gridIndex">Grid index to include in bounds.</param> private void _UpdateBounds(Tango3DReconstruction.GridIndex gridIndex) { float gridIndexSize = m_tangoApplication.m_3drResolutionMeters * 16; Vector3 pointToCompare = gridIndexSize * new Vector3(gridIndex.x, gridIndex.y, gridIndex.z); Vector3 min = m_bounds.min; Vector3 max = m_bounds.max; if (m_bounds.min.x > pointToCompare.x) { min.x = pointToCompare.x; } if (m_bounds.min.y > pointToCompare.y) { min.y = pointToCompare.y; } if (m_bounds.min.z > pointToCompare.z) { min.z = pointToCompare.z; } if (m_bounds.max.x < pointToCompare.x) { max.x = pointToCompare.x; } if (m_bounds.max.y < pointToCompare.y) { max.y = pointToCompare.y; } if (m_bounds.max.z < pointToCompare.z) { max.z = pointToCompare.z; } m_bounds.SetMinMax(min, max); }
/// <summary> /// Extract and update (or create, if it doesn't exist) the mesh at the given grid index. /// </summary> /// <param name="gridIndex">Grid index.</param> /// <param name="needsResize">List to which indices needing a future resize will be added.</param> private void _UpdateMeshAtGridIndex(Tango3DReconstruction.GridIndex gridIndex, List<Tango3DReconstruction.GridIndex> needsResize) { TangoSingleDynamicMesh dynamicMesh; if (!m_meshes.TryGetValue(gridIndex, out dynamicMesh)) { // build a dynamic mesh as a child of this game object. GameObject newObj = new GameObject(); newObj.transform.parent = transform; newObj.name = string.Format("{0},{1},{2}", gridIndex.x, gridIndex.y, gridIndex.z); newObj.layer = gameObject.layer; dynamicMesh = newObj.AddComponent<TangoSingleDynamicMesh>(); dynamicMesh.m_vertices = new Vector3[INITIAL_VERTEX_COUNT]; if (m_tangoApplication.m_3drGenerateTexCoord) { dynamicMesh.m_uv = new Vector2[INITIAL_VERTEX_COUNT]; } if (m_tangoApplication.m_3drGenerateColor) { dynamicMesh.m_colors = new Color32[INITIAL_VERTEX_COUNT]; } dynamicMesh.m_triangles = new int[INITIAL_INDEX_COUNT]; // Update debug info too. m_debugTotalVertices = dynamicMesh.m_vertices.Length; m_debugTotalTriangles = dynamicMesh.m_triangles.Length; // Add the other necessary objects MeshFilter meshFilter = newObj.AddComponent<MeshFilter>(); dynamicMesh.m_mesh = meshFilter.mesh; if (m_meshRenderer != null) { MeshRenderer meshRenderer = newObj.AddComponent<MeshRenderer>(); #if UNITY_5 meshRenderer.shadowCastingMode = m_meshRenderer.shadowCastingMode; meshRenderer.receiveShadows = m_meshRenderer.receiveShadows; meshRenderer.sharedMaterials = m_meshRenderer.sharedMaterials; meshRenderer.useLightProbes = m_meshRenderer.useLightProbes; meshRenderer.reflectionProbeUsage = m_meshRenderer.reflectionProbeUsage; meshRenderer.probeAnchor = m_meshRenderer.probeAnchor; #elif UNITY_4_6 meshRenderer.castShadows = m_meshRenderer.castShadows; meshRenderer.receiveShadows = m_meshRenderer.receiveShadows; meshRenderer.sharedMaterials = m_meshRenderer.sharedMaterials; meshRenderer.useLightProbes = m_meshRenderer.useLightProbes; meshRenderer.lightProbeAnchor = m_meshRenderer.lightProbeAnchor; #endif } if (m_meshCollider != null) { MeshCollider meshCollider = newObj.AddComponent<MeshCollider>(); meshCollider.convex = m_meshCollider.convex; meshCollider.isTrigger = m_meshCollider.isTrigger; meshCollider.sharedMaterial = m_meshCollider.sharedMaterial; meshCollider.sharedMesh = dynamicMesh.m_mesh; dynamicMesh.m_meshCollider = meshCollider; } m_meshes.Add(gridIndex, dynamicMesh); _UpdateBounds(gridIndex); } // Skip updating this grid index if it is considered completed. if (m_enableSelectiveMeshing) { if (dynamicMesh.m_completed) { return; } _ObserveGridIndex(gridIndex, dynamicMesh); } // Last frame the mesh needed more space. Give it more room now. if (dynamicMesh.m_needsToGrow) { int newVertexSize = (int)(dynamicMesh.m_vertices.Length * GROWTH_FACTOR); int newTriangleSize = (int)(dynamicMesh.m_triangles.Length * GROWTH_FACTOR); newTriangleSize -= newTriangleSize % 3; // Remove the old size, add the new size. m_debugTotalVertices += newVertexSize - dynamicMesh.m_vertices.Length; m_debugTotalTriangles += newTriangleSize - dynamicMesh.m_triangles.Length; dynamicMesh.m_vertices = new Vector3[newVertexSize]; if (m_tangoApplication.m_3drGenerateTexCoord) { dynamicMesh.m_uv = new Vector2[newVertexSize]; } if (m_tangoApplication.m_3drGenerateColor) { dynamicMesh.m_colors = new Color32[newVertexSize]; } dynamicMesh.m_triangles = new int[newTriangleSize]; dynamicMesh.m_needsToGrow = false; } int numVertices; int numTriangles; Tango3DReconstruction.Status status = m_tangoApplication.Tango3DRExtractMeshSegment( gridIndex, dynamicMesh.m_vertices, null, dynamicMesh.m_colors, dynamicMesh.m_triangles, out numVertices, out numTriangles); if (status != Tango3DReconstruction.Status.INSUFFICIENT_SPACE && status != Tango3DReconstruction.Status.SUCCESS) { Debug.Log("Tango3DR extraction failed, status code = " + status + Environment.StackTrace); return; } else if (status == Tango3DReconstruction.Status.INSUFFICIENT_SPACE) { // We already spent the time extracting this mesh, let's not spend any more time this frame // to extract the mesh. Debug.Log(string.Format( "TangoDynamicMesh.Update() extraction ran out of space with room for {0} vertexes, {1} indexes.", dynamicMesh.m_vertices.Length, dynamicMesh.m_triangles.Length)); dynamicMesh.m_needsToGrow = true; needsResize.Add(gridIndex); } // Make any leftover triangles degenerate. for (int triangleIt = numTriangles * 3; triangleIt < dynamicMesh.m_triangles.Length; ++triangleIt) { dynamicMesh.m_triangles[triangleIt] = 0; } if (dynamicMesh.m_uv != null) { // Add texture coordinates. for (int vertexIt = 0; vertexIt < numVertices; ++vertexIt) { Vector3 vertex = dynamicMesh.m_vertices[vertexIt]; dynamicMesh.m_uv[vertexIt].x = vertex.x * UV_PER_METERS; dynamicMesh.m_uv[vertexIt].y = (vertex.z + vertex.y) * UV_PER_METERS; } } dynamicMesh.m_mesh.Clear(); dynamicMesh.m_mesh.vertices = dynamicMesh.m_vertices; dynamicMesh.m_mesh.uv = dynamicMesh.m_uv; dynamicMesh.m_mesh.colors32 = dynamicMesh.m_colors; dynamicMesh.m_mesh.triangles = dynamicMesh.m_triangles; if (m_tangoApplication.m_3drGenerateNormal) { dynamicMesh.m_mesh.RecalculateNormals(); } if (dynamicMesh.m_meshCollider != null) { // Force the mesh collider to update too. dynamicMesh.m_meshCollider.sharedMesh = null; dynamicMesh.m_meshCollider.sharedMesh = dynamicMesh.m_mesh; } }
/// <summary> /// When the grid index has been updated, also determine whether it should be considered completed /// based on its neighboring grid indices, number of observations, and mesh completeness. /// /// When checking a grid index for completeness, the observation count of neighboring grid indices is checked. /// If all grid indices contained in one of the configurations have a sufficient number of observations, /// the grid index is considered complete. /// </summary> /// <param name="gridIndex">Grid index to observe.</param> /// <param name="singleMesh">TangoSingleDynamicMesh to update and observe.</param> private void _ObserveGridIndex(Tango3DReconstruction.GridIndex gridIndex, TangoSingleDynamicMesh singleMesh) { // Increment the observations made for this grid index. singleMesh.m_observations++; // Add observation based on the direction of the observation. _ViewGridIndex(gridIndex); // Exit if the grid index has not been observed from all 8 directions. if (singleMesh.m_directions != DIRECTIONS_COMPLETE) { return; } // Run through each grid index configuration and check if the grid index is complete. for (int i = 0; i < m_gridIndexConfigs.Length; i++) { Vector3[] config = m_gridIndexConfigs[i]; bool neighborsObserved = true; foreach (Vector3 nPosition in config) { Tango3DReconstruction.GridIndex neighbor = new Tango3DReconstruction.GridIndex(); neighbor.x = (Int32)(nPosition.x + gridIndex.x); neighbor.y = (Int32)(nPosition.y + gridIndex.y); neighbor.z = (Int32)(nPosition.z + gridIndex.z); TangoSingleDynamicMesh nSingleMesh; if (m_meshes.TryGetValue(neighbor, out nSingleMesh)) { if (nSingleMesh.m_observations < NUM_OBSERVATIONS_TO_COMPLETE) { neighborsObserved = false; break; } } } // Complete using this configurations of the neighbors with sufficient observations. if (neighborsObserved) { // Add the grid index to the completed list, so it will be skipped during next mesh update. singleMesh.m_completed = true; return; } } }
/// <summary> /// Gets each single dynamic mesh and fills out arrays with properties. Each mesh corresponds to the same index in each array. /// </summary> /// <param name="gridIndices">Filled out with grid index of each mesh.</param> /// <param name="completed">Filled out with completion state of each mesh.</param> /// <param name="completionScale">Filled out with amount that each mesh has been completed.</param> /// <param name="directions">Filled out with a byte representation of the observed directions of each mesh.</param> public void GetSingleMeshProperties(out Tango3DReconstruction.GridIndex[] gridIndices, out bool[] completed, out float[] completionScale, out byte[] directions) { int numIndices = m_meshes.Count; gridIndices = new Tango3DReconstruction.GridIndex[numIndices]; completed = new bool[numIndices]; completionScale = new float[numIndices]; directions = new byte[numIndices]; // Assign mesh properties to each index of the arrays. m_meshes.Keys.CopyTo(gridIndices, 0); for (int i = 0; i < numIndices; i++) { TangoSingleDynamicMesh mesh = m_meshes[gridIndices[i]]; completed[i] = mesh.m_completed; completionScale[i] = 1.0f * mesh.m_observations / NUM_OBSERVATIONS_TO_COMPLETE; directions[i] = mesh.m_directions; } }
/// <summary> /// Awake this instance. /// </summary> private void Awake() { AndroidHelper.RegisterPauseEvent(_androidOnPause); AndroidHelper.RegisterResumeEvent(_androidOnResume); AndroidHelper.RegisterOnActivityResultEvent(_androidOnActivityResult); AndroidHelper.RegisterOnScreenOrientationChangedEvent(_androidOnScreenOrientationChanged); // Setup listeners. m_tangoEventListener = new TangoEventListener(); m_areaDescriptionEventListener = new AreaDescriptionEventListener(); if (m_enableCloudADF) { m_tangoCloudEventListener = new TangoCloudEventListener(); } if (m_enableMotionTracking) { m_poseListener = new PoseListener(); } if (m_enableDepth) { m_depthListener = new DepthListener(); } if (m_enableVideoOverlay) { int yTextureWidth = 0; int yTextureHeight = 0; int uvTextureWidth = 0; int uvTextureHeight = 0; m_yuvTexture = new YUVTexture(yTextureWidth, yTextureHeight, uvTextureWidth, uvTextureHeight, TextureFormat.RGBA32, false); m_videoOverlayListener = new VideoOverlayListener(); } if (m_enable3DReconstruction) { m_tango3DReconstruction = new Tango3DReconstruction(m_3drResolutionMeters, m_3drGenerateColor, m_3drSpaceClearing); m_tango3DReconstruction.m_useAreaDescriptionPose = m_3drUseAreaDescriptionPose; m_tango3DReconstruction.m_sendColorToUpdate = m_3drGenerateColor; } // Setup configs. m_tangoConfig = new TangoConfig(TangoEnums.TangoConfigType.TANGO_CONFIG_DEFAULT); m_tangoRuntimeConfig = new TangoConfig(TangoEnums.TangoConfigType.TANGO_CONFIG_RUNTIME); TangoSupport.UpdateCurrentRotationIndex(); }
/// <summary> /// Extract a single grid cell's mesh. /// </summary> /// <returns>Status of the extraction.</returns> /// <param name="gridIndex">Grid index to extract.</param> /// <param name="vertices">Filled out with extracted vertices.</param> /// <param name="normals">Filled out with extracted normals.</param> /// <param name="colors">Filled out with extracted colors.</param> /// <param name="triangles">Filled out with extracted triangle indices.</param> /// <param name="numVertices">Filled out with the number of valid vertices.</param> /// <param name="numTriangles">Filled out with the number of valid triangles.</param> public Tango3DReconstruction.Status Tango3DRExtractMeshSegment( Tango3DReconstruction.GridIndex gridIndex, Vector3[] vertices, Vector3[] normals, Color32[] colors, int[] triangles, out int numVertices, out int numTriangles) { if (m_tango3DReconstruction != null) { return m_tango3DReconstruction.ExtractMeshSegment(gridIndex, vertices, normals, colors, triangles, out numVertices, out numTriangles); } numVertices = 0; numTriangles = 0; return Tango3DReconstruction.Status.INVALID; }
/// <summary> /// Extract an array of <c>SignedDistanceVoxel</c> objects. /// </summary> /// <returns> /// Returns Status.SUCCESS if the voxels are fully extracted and stared in the array. In this case, <c>numVoxels</c> /// will say how many voxels are used, the rest of the array is untouched. /// /// Returns Status.INVALID if the array length does not exactly equal the number of voxels in a single grid /// index. By default, the number of voxels in a grid index is 16*16*16. /// /// Returns Status.INVALID if some other error occurs. /// </returns> /// <param name="gridIndex">Grid index to extract.</param> /// <param name="voxels"> /// On successful extraction this will get filled out with the signed distance voxels. /// </param> /// <param name="numVoxels">Number of voxels filled out.</param> public Tango3DReconstruction.Status Tango3DRExtractSignedDistanceVoxel( Tango3DReconstruction.GridIndex gridIndex, Tango3DReconstruction.SignedDistanceVoxel[] voxels, out int numVoxels) { if (m_tango3DReconstruction != null) { return m_tango3DReconstruction.ExtractSignedDistanceVoxel(gridIndex, voxels, out numVoxels); } numVoxels = 0; return Tango3DReconstruction.Status.INVALID; }
/// <summary> /// Awake this instance. /// </summary> private void Awake() { if (!AndroidHelper.LoadTangoLibrary()) { Debug.Log("Unable to load Tango library. Things may not work."); return; } AndroidHelper.RegisterPauseEvent(_androidOnPause); AndroidHelper.RegisterResumeEvent(_androidOnResume); AndroidHelper.RegisterOnActivityResultEvent(_androidOnActivityResult); AndroidHelper.RegisterOnDisplayChangedEvent(_androidOnDisplayChanged); AndroidHelper.RegisterOnTangoServiceConnected(_androidOnTangoServiceConnected); AndroidHelper.RegisterOnTangoServiceDisconnected(_androidOnTangoServiceDisconnected); if (m_enableDepth) { DepthListener.SetPointCloudLimit(m_initialPointCloudMaxPoints); } if (m_enableVideoOverlay) { int yTextureWidth = 0; int yTextureHeight = 0; int uvTextureWidth = 0; int uvTextureHeight = 0; m_yuvTexture = new YUVTexture(yTextureWidth, yTextureHeight, uvTextureWidth, uvTextureHeight, TextureFormat.RGBA32, false); } if (m_enable3DReconstruction) { m_tango3DReconstruction = new Tango3DReconstruction( resolution: m_3drResolutionMeters, generateColor: m_3drGenerateColor, spaceClearing: m_3drSpaceClearing, minNumVertices: m_3drMinNumVertices, updateMethod: m_3drUpdateMethod); m_tango3DReconstruction.m_useAreaDescriptionPose = m_3drUseAreaDescriptionPose; m_tango3DReconstruction.m_sendColorToUpdate = m_3drGenerateColor; } TangoSupport.UpdatePoseMatrixFromDeviceRotation(AndroidHelper.GetDisplayRotation(), AndroidHelper.GetColorCameraRotation()); if (m_adjustScreenResolution) { _ChangeResolutionForPerformance(); } // Importing and exporting Area Descriptions can be done before you connect. We must // propogate those events if they happen. AreaDescriptionEventListener.SetCallback(); #if UNITY_EDITOR if (m_doSlowEmulation && (m_enableDepth || m_enableVideoOverlay)) { if (m_emulationEnvironment == null) { Debug.LogError("No Mesh for Emulation assigned on the Tango Application (commonly in the Tango Manager prefab)." + " Expect blank camera and/or depth frames."); } EmulatedEnvironmentRenderHelper.InitForEnvironment(m_emulationEnvironment, m_emulationEnvironmentTexture, m_emulationVideoOverlaySimpleLighting); } else { EmulatedEnvironmentRenderHelper.Clear(); } #endif }