メッシュ化 (Meshing) サブシステムは、外部のプロバイダーからメッシュデータを抽出し、UnityEngine.Mesh に変換します。また、メインスレッドのストールを発生させることなく、任意の UnityEngine.MeshCollider を生成することができます。
メッシュ化サブシステムの主な使用例は、一般に深度カメラから生成されるような空間マッピングアルゴリズムから、プロシージャルに生成されたメッシュをサーフェス化することです。メッシュのサイズや更新頻度には制限がありません。
メッシュの生成は、バックグラウンドのスレッドで非同期的に行われます。そのため、外部のプロバイダーからデータを抽出しても、メインスレッドがブロックされることはなく、例えばメッシュコライダーのベイク処理を行うことができます。
メッシュ化サブシステムには 2 つの基本的なクエリがあります。
C# ユーザーは、 XRMeshSubsystem
インスタンスメソッドからメッシュの情報を得ることができます。
public bool TryGetMeshInfos(List<MeshInfo> meshInfosOut);
これは UnityXRMeshProvider::GetMeshInfos
への C 言語の呼び出しに直接対応しており、通常はフレームごとに 1 回呼び出され、追跡されたメッシュの現在のリストを取得します。
以下の C 言語の実装では、提供された allocator
オブジェクトを使用して、UnityXRMeshInfo
の配列を割り当て、それを埋めることができます。
UnitySubsystemErrorCode(UNITY_INTERFACE_API * GetMeshInfos)(
UnitySubsystemHandle handle, void* pluginData, UnityXRMeshInfoAllocator * allocator);
割り当てられたメモリは、Unity が所有します (通常はスタックアロケータを使用します。 そのため、割り当ては非常に高速です)。
typedef struct UnityXRMeshInfo
{
UnityXRMeshId meshId;
bool updated;
int priorityHint;
} UnityXRMeshInfo;
前回の TryGetMeshInfos の呼び出しから何も変更されていない場合は、false を返すことで、フレームごとに配列を埋めるのを避けることができます。
フィールド | 説明 |
---|---|
meshId | 128 ビットの一意の識別子です。プロバイダーが生成するこれらの値は、メッシュデータへのポインターになりますが、ID によって特定のメッシュを生成可能にする必要があります。 |
updated | Unity が必要とする唯一の状態は、メッシュが最後に生成されてから更新されたかどうかです。メッシュが追加されたか削除されたかの判断は自動的に行われ、Unity が知らないメッシュがサーフェス化され存在することが報告されると Added と表示され、以前報告されたメッシュの存在が報告されない場合は Removed と表示されます。 |
priorityHint | C# はこの値を解釈しますが、例えば、この値に基づいてどのメッシュの生成の優先順位を決定するような C# コンポーネントを提供したいと思うかもしれません。Unity はこの値を使いません。 |
C# では、TryGetMeshInfos は、mesh state を含む List<MeshInfo>
を生成します。
public enum MeshChangeState
{
Added,
Updated,
Removed,
Unchanged
}
C# コンポーネントは、メッシュの変更状態と優先順位を左右する値 (priorityHint) に基づいて、次にどのメッシュを生成するかを決定します。
C# からは、XRMeshSubsystem インスタンスメソッドを使って、特定のメッシュを非同期に生成することができます。
public extern void GenerateMeshAsync(
MeshId meshId,
Mesh mesh,
MeshCollider meshCollider,
MeshVertexAttributes attributes,
Action<MeshGenerationResult> onMeshGenerationComplete);
これは、生成のためにメッシュをキューに入れます。数に制限なくメッシュをキューに入れることができますが、同時に生成されるメッシュの数を数個に制限したほうが良い場合もあります。
Unity は、エラーが発生しても、提供された onMeshGenerationComplete
デリゲートを常に呼び出します。
メッシュの生成は、取得と解放の 2 つのフェーズで行われます。
UnitySubsystemErrorCode(UNITY_INTERFACE_API * AcquireMesh)(
UnitySubsystemHandle handle,
void* pluginData,
const UnityXRMeshId * meshId,
UnityXRMeshDataAllocator * allocator);
AcquireMesh
は、バックグラウンドのスレッドで呼び出されます。そのため、このメソッドでは、メッシュ自体を生成するような計算負荷の高い作業を含め、好きなだけ処理を行うことができます。この関数はすぐに返すことも、複数のフレームにわたって行うこともできます。
MeshCollider
を GenerateMeshAsync
に提供する場合、Unity はメッシュコライダーの加速構造も計算します (上図の “Bake Physics”)。これは大きなメッシュでは時間がかかります。そこで、この処理もワーカースレッドで行われます。
最後に、データの準備ができたら、Unity は、メインスレッド上の UnityEngine.Mesh
および/または UnityEngine.MeshCollider
にデータを書き込みます。その後、Unity は ReleaseMesh
を、同じくメインスレッドで呼び出します。
UnitySubsystemErrorCode(UNITY_INTERFACE_API * ReleaseMesh)(
UnitySubsystemHandle handle,
void* pluginData,
const UnityXRMeshId * meshId,
const UnityXRMeshDescriptor * mesh,
void* userData);
ReleaseMesh
はメインスレッドで呼び出されるため、それはすぐに返します。通常、これは AcquireMesh
で割り当てられたリソースを解放するために使用されます。
AcquireMesh
には、メッシュデータを Unity に提供するための 2 つの方法があります。Unity-managed (Unity マネージ) と provider-managed (プロバイダーマネージ) です。
Unity にメモリを管理させるには、以下を使用します。
UnityXRMeshDescriptor* (UNITY_INTERFACE_API * MeshDataAllocator_AllocateMesh)(
UnityXRMeshDataAllocator * allocator,
size_t vertexCount,
size_t indexCount,
UnityXRIndexFormat indexFormat,
UnityXRMeshVertexAttributeFlags attributes,
UnityXRMeshTopology topology);
これは、これらの 属性
と C# から要求された頂点属性の交点に基づくバッファへのポインターを持つ構造体を返します。プロバイダーは、適切なデータをバッファにコピーする必要があります。
このパラダイムを使うと、ReleaseMesh
を呼び出した後に Unity がメモリを再利用するので、メモリを解放する必要がありません。
メモリの管理を Unity に任せるのではなく、自身のデータで指し示すことができます。このデータは、 ReleaseMesh
が呼び出されるまで有効でなければなりません。
MeshDataAllocator_SetMesh
を使用して、null 以外のポインターが有効なデータを指す独自の UnityXRMeshDescriptor
を提供するには、以下のようにします。
void(UNITY_INTERFACE_API * MeshDataAllocator_SetMesh)(
UnityXRMeshDataAllocator * allocator, const UnityXRMeshDescriptor * meshDescriptor);
AcquireMesh
の実装は、以下を呼び出すことができます。
void(UNITY_INTERFACE_API * MeshDataAllocator_SetUserData)(
UnityXRMeshDataAllocator * allocator, void* userData);
Unity は、userData
ポインターを、ReleaseMesh
の実装に戻します。これは、プロバイダーマネージメモリを使用している場合に特に便利です。
void Update()
{
if (s_MeshSubsystem.TryGetMeshInfos(s_MeshInfos))
{
foreach (var meshInfo in s_MeshInfos)
{
switch (meshInfo.ChangeState)
{
case MeshChangeState.Added:
case MeshChangeState.Updated:
AddToQueueIfNecessary(meshInfo);
break;
case MeshChangeState.Removed:
RaiseMeshRemoved(meshInfo.MeshId);
// 処理キューから削除します
m_MeshesNeedingGeneration.Remove(meshInfo.MeshId);
// GameObject を破棄
GameObject meshGameObject;
if (meshIdToGameObjectMap.TryGetValue(meshInfo.MeshId, out meshGameObject))
{
Destroy(meshGameObject);
meshIdToGameObjectMap.Remove(meshInfo.MeshId);
}
break;
default:
break;
}
}
}
// ...
while (m_MeshesBeingGenerated.Count < meshQueueSize && m_MeshesNeedingGeneration.Count > 0)
{
// 生成する次のメッシュを取得します。
//メッシュの priorityHint、New か Updated かなどに基づく可能性があります。
var meshId = GetNextMeshToGenerate();
// Gather the necessary Unity objects for the generation request
var meshGameObject = GetOrCreateGameObjectForMesh(meshId);
var meshCollider = meshGameObject.GetComponent<MeshCollider>();
var mesh = meshGameObject.GetComponent<MeshFilter>().mesh;
var meshAttributes = shouldComputeNormals ? MeshVertexAttributes.Normals : MeshVertexAttributes.None;
// 生成を要求
s_MeshSubsystem.GenerateMeshAsync(meshId, mesh, meshCollider, meshAttributes, OnMeshGenerated);
// 内部の状態を更新
m_MeshesBeingGenerated.Add(meshId, m_MeshesNeedingGeneration[meshId]);
m_MeshesNeedingGeneration.Remove(meshId);
}
}
void OnMeshGenerated(MeshGenerationResult result)
{
if (result.Status != MeshGenerationStatus.Success)
{
// エラーの処理、再生成など。
}
m_MeshesBeingGenerated.Remove(result.MeshId);
}
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.