AR Foundation演示项目,使用 AR Foundation 4.1.7 并围绕某些功能演示更高级功能。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

369 行
14 KiB

using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
#if UNITY_IOS
using UnityEngine.XR.ARKit;
#endif // UNITY_IOS && !UNITY_EDITOR
using Object = UnityEngine.Object;
namespace UnityEngine.XR.ARFoundation.Samples
{
public class MeshClassificationFracking : MonoBehaviour
{
/// <summary>
/// The number of mesh classifications detected.
/// </summary>
const int k_NumClassifications = 8;
/// <summary>
/// The mesh manager for the scene.
/// </summary>
public ARMeshManager m_MeshManager;
/// <summary>
/// The mesh prefab for the None classification.
/// </summary>
public MeshFilter m_NoneMeshPrefab;
/// <summary>
/// The mesh prefab for the Wall classification.
/// </summary>
public MeshFilter m_WallMeshPrefab;
/// <summary>
/// The mesh prefab for the Floor classification.
/// </summary>
public MeshFilter m_FloorMeshPrefab;
/// <summary>
/// The mesh prefab for the Ceiling classification.
/// </summary>
public MeshFilter m_CeilingMeshPrefab;
/// <summary>
/// The mesh prefab for the Table classification.
/// </summary>
public MeshFilter m_TableMeshPrefab;
/// <summary>
/// The mesh prefab for the Seat classification.
/// </summary>
public MeshFilter m_SeatMeshPrefab;
/// <summary>
/// The mesh prefab for the Window classification.
/// </summary>
public MeshFilter m_WindowMeshPrefab;
/// <summary>
/// The mesh prefab for the Door classification.
/// </summary>
public MeshFilter m_DoorMeshPrefab;
#if UNITY_IOS
/// <summary>
/// A mapping from tracking ID to instantiated mesh filters.
/// </summary>
readonly Dictionary<TrackableId, MeshFilter[]> m_MeshFrackingMap = new Dictionary<TrackableId, MeshFilter[]>();
/// <summary>
/// The delegate to call to breakup a mesh.
/// </summary>
Action<MeshFilter> m_BreakupMeshAction;
/// <summary>
/// The delegate to call to update a mesh.
/// </summary>
Action<MeshFilter> m_UpdateMeshAction;
/// <summary>
/// The delegate to call to remove a mesh.
/// </summary>
Action<MeshFilter> m_RemoveMeshAction;
/// <summary>
/// An array to store the triangle vertices of the base mesh.
/// </summary>
readonly List<int> m_BaseTriangles = new List<int>();
/// <summary>
/// An array to store the triangle vertices of the classified mesh.
/// </summary>
readonly List<int> m_ClassifiedTriangles = new List<int>();
readonly List<Material> m_SharedPrefabMats = new List<Material>();
const float k_StartAlpha = 0.3f;
bool m_ShowingMeshes = true;
/// <summary>
/// On awake, set up the mesh filter delegates.
/// </summary>
void Awake()
{
m_BreakupMeshAction = new Action<MeshFilter>(BreakupMesh);
m_UpdateMeshAction = new Action<MeshFilter>(UpdateMesh);
m_RemoveMeshAction = new Action<MeshFilter>(RemoveMesh);
m_SharedPrefabMats.Add(m_NoneMeshPrefab.GetComponent<MeshRenderer>().sharedMaterial);
m_SharedPrefabMats.Add(m_WallMeshPrefab.GetComponent<MeshRenderer>().sharedMaterial);
m_SharedPrefabMats.Add(m_FloorMeshPrefab.GetComponent<MeshRenderer>().sharedMaterial);
m_SharedPrefabMats.Add((m_CeilingMeshPrefab.GetComponent<MeshRenderer>().sharedMaterial));
m_SharedPrefabMats.Add(m_TableMeshPrefab.GetComponent<MeshRenderer>().sharedMaterial);
m_SharedPrefabMats.Add(m_SeatMeshPrefab.GetComponent<MeshRenderer>().sharedMaterial);
m_SharedPrefabMats.Add(m_WindowMeshPrefab.GetComponent<MeshRenderer>().sharedMaterial);
m_SharedPrefabMats.Add(m_DoorMeshPrefab.GetComponent<MeshRenderer>().sharedMaterial);
}
/// <summary>
/// On enable, subscribe to the meshes changed event.
/// </summary>
void OnEnable()
{
Debug.Assert(m_MeshManager != null, "mesh manager cannot be null");
m_MeshManager.meshesChanged += OnMeshesChanged;
}
/// <summary>
/// On disable, unsubscribe from the meshes changed event.
/// </summary>
void OnDisable()
{
Debug.Assert(m_MeshManager != null, "mesh manager cannot be null");
m_MeshManager.meshesChanged -= OnMeshesChanged;
}
/// <summary>
/// When the meshes change, update the scene meshes.
/// </summary>
void OnMeshesChanged(ARMeshesChangedEventArgs args)
{
if (args.added != null)
{
args.added.ForEach(m_BreakupMeshAction);
}
if (args.updated != null)
{
args.updated.ForEach(m_UpdateMeshAction);
}
if (args.removed != null)
{
args.removed.ForEach(m_RemoveMeshAction);
}
}
/// <summary>
/// Parse the trackable ID from the mesh filter name.
/// </summary>
/// <param name="meshFilterName">The mesh filter name containing the trackable ID.</param>
/// <returns>
/// The trackable ID parsed from the string.
/// </returns>
TrackableId ExtractTrackableId(string meshFilterName)
{
string[] nameSplit = meshFilterName.Split(' ');
return new TrackableId(nameSplit[1]);
}
/// <summary>
/// Given a base mesh, the face classifications for all faces in the mesh, and a single face classification to
/// extract, extract into a new mesh only the faces that have the selected face classification.
/// </summary>
/// <param name="baseMesh">The original base mesh.</param>
/// <param name="faceClassifications">The array of face classifications for each triangle in the
/// <paramref name="baseMesh"/></param>
/// <param name="selectedMeshClassification">A single classification to extract the faces from the
/// <paramref="baseMesh"/>into the <paramref name="classifiedMesh"/></param>
/// <param name="classifiedMesh">The output mesh to be updated with the extracted mesh.</param>
void ExtractClassifiedMesh(Mesh baseMesh, NativeArray<ARMeshClassification> faceClassifications, ARMeshClassification selectedMeshClassification, Mesh classifiedMesh)
{
// Count the number of faces matching the selected classification.
int classifiedFaceCount = 0;
for (int i = 0; i < faceClassifications.Length; ++i)
{
if (faceClassifications[i] == selectedMeshClassification)
{
++classifiedFaceCount;
}
}
// Clear the existing mesh.
classifiedMesh.Clear();
// If there were matching face classifications, build a new mesh from the base mesh.
if (classifiedFaceCount > 0)
{
baseMesh.GetTriangles(m_BaseTriangles, 0);
Debug.Assert(m_BaseTriangles.Count == (faceClassifications.Length * 3),
"unexpected mismatch between triangle count and face classification count");
m_ClassifiedTriangles.Clear();
m_ClassifiedTriangles.Capacity = classifiedFaceCount * 3;
for (int i = 0; i < faceClassifications.Length; ++i)
{
if (faceClassifications[i] == selectedMeshClassification)
{
int baseTriangleIndex = i * 3;
m_ClassifiedTriangles.Add(m_BaseTriangles[baseTriangleIndex + 0]);
m_ClassifiedTriangles.Add(m_BaseTriangles[baseTriangleIndex + 1]);
m_ClassifiedTriangles.Add(m_BaseTriangles[baseTriangleIndex + 2]);
}
}
classifiedMesh.vertices = baseMesh.vertices;
classifiedMesh.normals = baseMesh.normals;
classifiedMesh.SetTriangles(m_ClassifiedTriangles, 0);
}
}
/// <summary>
/// Break up a single mesh with multiple face classifications into submeshes, each with an unique and uniform mesh
/// classification.
/// </summary>
/// <param name="meshFilter">The mesh filter for the base mesh with multiple face classifications.</param>
void BreakupMesh(MeshFilter meshFilter)
{
XRMeshSubsystem meshSubsystem = m_MeshManager.subsystem as XRMeshSubsystem;
if (meshSubsystem == null)
{
return;
}
var meshId = ExtractTrackableId(meshFilter.name);
var faceClassifications = meshSubsystem.GetFaceClassifications(meshId, Allocator.Persistent);
if (!faceClassifications.IsCreated)
{
return;
}
using (faceClassifications)
{
if (faceClassifications.Length <= 0)
{
return;
}
var parent = meshFilter.transform.parent;
MeshFilter[] meshFilters = new MeshFilter[k_NumClassifications];
meshFilters[(int)ARMeshClassification.None] = (m_NoneMeshPrefab == null) ? null : Instantiate(m_NoneMeshPrefab, parent);
meshFilters[(int)ARMeshClassification.Wall] = (m_WallMeshPrefab == null) ? null : Instantiate(m_WallMeshPrefab, parent);
meshFilters[(int)ARMeshClassification.Floor] = (m_FloorMeshPrefab == null) ? null : Instantiate(m_FloorMeshPrefab, parent);
meshFilters[(int)ARMeshClassification.Ceiling] = (m_CeilingMeshPrefab == null) ? null : Instantiate(m_CeilingMeshPrefab, parent);
meshFilters[(int)ARMeshClassification.Table] = (m_TableMeshPrefab == null) ? null : Instantiate(m_TableMeshPrefab, parent);
meshFilters[(int)ARMeshClassification.Seat] = (m_SeatMeshPrefab == null) ? null : Instantiate(m_SeatMeshPrefab, parent);
meshFilters[(int)ARMeshClassification.Window] = (m_WindowMeshPrefab == null) ? null : Instantiate(m_WindowMeshPrefab, parent);
meshFilters[(int)ARMeshClassification.Door] = (m_DoorMeshPrefab == null) ? null : Instantiate(m_DoorMeshPrefab, parent);
m_MeshFrackingMap[meshId] = meshFilters;
var baseMesh = meshFilter.sharedMesh;
for (int i = 0; i < k_NumClassifications; ++i)
{
var classifiedMeshFilter = meshFilters[i];
if (classifiedMeshFilter != null)
{
var classifiedMesh = classifiedMeshFilter.mesh;
ExtractClassifiedMesh(baseMesh, faceClassifications, (ARMeshClassification)i, classifiedMesh);
meshFilters[i].mesh = classifiedMesh;
}
}
}
}
/// <summary>
/// Update the submeshes corresponding to the single mesh with multiple face classifications into submeshes.
/// </summary>
/// <param name="meshFilter">The mesh filter for the base mesh with multiple face classifications.</param>
void UpdateMesh(MeshFilter meshFilter)
{
XRMeshSubsystem meshSubsystem = m_MeshManager.subsystem as XRMeshSubsystem;
if (meshSubsystem == null)
{
return;
}
var meshId = ExtractTrackableId(meshFilter.name);
var faceClassifications = meshSubsystem.GetFaceClassifications(meshId, Allocator.Persistent);
if (!faceClassifications.IsCreated)
{
return;
}
using (faceClassifications)
{
if (faceClassifications.Length <= 0)
{
return;
}
var meshFilters = m_MeshFrackingMap[meshId];
var baseMesh = meshFilter.sharedMesh;
for (int i = 0; i < k_NumClassifications; ++i)
{
var classifiedMeshFilter = meshFilters[i];
if (classifiedMeshFilter != null)
{
var classifiedMesh = classifiedMeshFilter.mesh;
ExtractClassifiedMesh(baseMesh, faceClassifications, (ARMeshClassification)i, classifiedMesh);
meshFilters[i].mesh = classifiedMesh;
}
}
}
}
/// <summary>
/// Remove the submeshes corresponding to the single mesh.
/// </summary>
/// <param name="meshFilter">The mesh filter for the base mesh with multiple face classifications.</param>
void RemoveMesh(MeshFilter meshFilter)
{
var meshId = ExtractTrackableId(meshFilter.name);
var meshFilters = m_MeshFrackingMap[meshId];
for (int i = 0; i < k_NumClassifications; ++i)
{
var classifiedMeshFilter = meshFilters[i];
if (classifiedMeshFilter != null)
{
Object.Destroy(classifiedMeshFilter);
}
}
m_MeshFrackingMap.Remove(meshId);
}
public void ToggleVisability()
{
foreach (Material mat in m_SharedPrefabMats)
{
if (m_ShowingMeshes)
{
mat.color = new Color(mat.color.r, mat.color.g, mat.color.b, 0);
}
else
{
mat.color = new Color(mat.color.r, mat.color.g, mat.color.b, k_StartAlpha);
}
}
m_ShowingMeshes = !m_ShowingMeshes;
}
#endif // UNITY_IOS && !UNITY_EDITOR
}
}