Подсистема создания сетки извлекает сеткуосновной графический примитив Unity. Меши составляют большую часть ваших 3D-миров. Unity поддерживает триангулированные или четырехугольные полигональные сетки. Поверхности Nurbs, Nurms, Subdiv должны быть преобразованы в полигоны. Подробнее
Просматривает в Словарь данные от внешнего поставщика и преобразует их в UnityEngine.Mesh. Он также может генерировать необязательный UnityEngine.MeshCollider без остановки основного потока.
Основным вариантом использования подсистемы Meshing является отображение процедурно сгенерированных сеток, как правило, из пространственного картографирования. отображение реальных поверхностей в виртуальный мир.
Посмотреть в Словарь алгоритм, аналогичный тому, который был сгенерирован из глубины камераКомпонент, который создает изображение определенной точки обзора в вашей сцене. Вывод либо рисуется на экране, либо фиксируется в виде текстуры. Подробнее
См. в Словарь. Размер сетки или частота обновлений не ограничены.
Создание сетки происходит асинхронно в фоновом потоке, поэтому извлечение данных из внешнего поставщика не блокирует основной поток, например, для запекания сетки коллайдер Невидимая форма, используемая для обработки физических столкновений объекта. Коллайдер не обязательно должен быть точно такой же формы, как сетка объекта — грубое приближение часто бывает более эффективным и неразличимым в игровом процессе. Подробнее
См. в Словарь.
Поток управления
Подсистема Meshing имеет два основных запроса:
- Получить состояние всех отслеживаемых сеток (например, "Новые", "Измененные", "Без изменений", "Удаленные").
- Создайте конкретную сетку. Меши идентифицируются с помощью идентификатора MeshId.
Получение MeshInfos
Пользователи C# могут получить информацию о сетке из метода экземпляра XRMeshSubsystem
:
public bool TryGetMeshInfos(List meshInfosOut);
Это сопоставляется непосредственно с вызовом C для UnityXRMeshProvider::GetMeshInfos
и обычно вызывается один раз за кадр для получения текущего списка отслеживаемых мешей.
Следующая реализация 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-битный уникальный идентификатор. Провайдер генерирует эти значения, которые могут быть указателем на данные меша, но вы должны иметь возможность генерировать конкретный меш по его идентификатору. |
updated | Единственное состояние, в котором нуждается Unity, — обновлялась ли сетка с момента последней генерации. Определение того, была ли сетка добавлена или удалена, выполняется автоматически; сообщение о существовании меша, о котором Unity не знает, отображается как добавленное, в то время как отсутствие сообщения о меше, о котором сообщалось ранее, помечает меш как удаленный. |
priorityHint | C# интерпретирует это значение, но вам может понадобиться, например, предоставить компонент C#, который отдает приоритет сетке, создаваемой на его основе. Unity не использует это значение. |
В C# TryGetMeshInfos заполняет List
, который включает состояние сетки:
public enum MeshChangeState
{
Added,
Updated,
Removed,
Unchanged
}
Основываясь на состоянии изменения сетки и значении подсказки приоритета, компонент C# может затем решить, какие сетки генерировать дальше.
Создание сетки
В C# можно асинхронно создать определенную сетку, используя метод экземпляра XRMeshSubsystem:
public extern void GenerateMeshAsync(
MeshId meshId,
Mesh mesh,
MeshCollider meshCollider,
MeshVertexAttributes attributes,
Action onMeshGenerationComplete);
Это помещает сетку в очередь для генерации. Вы можете поставить в очередь столько мешей, сколько вам нужно, но вы можете ограничить количество одновременно генерируемых мешей несколькими за раз.
Unity всегда вызывает предоставленный делегат onMeshGenerationComplete
, даже если возникает ошибка.
Сети создаются в два этапа в соответствии с моделью получения и выпуска:
UnitySubsystemErrorCode(UNITY_INTERFACE_API * AcquireMesh)(
UnitySubsystemHandle handle,
void* pluginData,
const UnityXRMeshId * meshId,
UnityXRMeshDataAllocator * allocator);
AcquireMesh
вызывается в фоновом потоке, поэтому вы можете выполнять в этом методе столько обработки, сколько хотите, включая работу, требующую больших вычислительных ресурсов, например создание самой сетки. Эта функция может возвращать значение немедленно или охватывать несколько кадров.
Если вы предоставляете MeshCollider
для GenerateMeshAsync
, Unity также вычисляет структуру ускорения MeshCollider ("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: под управлением Unity и под управлением поставщика.
Память, управляемая Unity
Чтобы позволить Unity управлять памятью, используйте:
UnityXRMeshDescriptor* (UNITY_INTERFACE_API * MeshDataAllocator_AllocateMesh)(
UnityXRMeshDataAllocator * allocator,
size_t vertexCount,
size_t indexCount,
UnityXRIndexFormat indexFormat,
UnityXRMeshVertexAttributeFlags attributes,
UnityXRMeshTopology topology);
Это возвращает структуру с указателями на буферы на основе пересечения этих атрибутов
и атрибутов вершин, запрошенных из C#. Затем провайдер должен скопировать соответствующие данные в буферы.
При использовании этой парадигмы вам не нужно освобождать память, так как Unity повторно использует память после вызова ReleaseMesh
.
Память, управляемая провайдером
Вместо того, чтобы позволять Unity управлять памятью, вы можете указать ему свои собственные данные. Данные должны оставаться действительными до тех пор, пока не будет вызван ReleaseMesh
.
Используйте MeshDataAllocator_SetMesh
, чтобы предоставить свой собственный 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
. Это особенно полезно при использовании памяти, управляемой поставщиком.
Пример компонента C#
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);
// Remove from processing queue
m_MeshesNeedingGeneration.Remove(meshInfo.MeshId);
// Destroy the 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)
{
// Get the next mesh to generate. Could be based on the mesh's
// priorityHint, whether it is new vs updated, etc.
var meshId = GetNextMeshToGenerate();
// Gather the necessary Unity objects for the generation request
var meshGameObject = GetOrCreateGameObjectForMesh(meshId);
var meshCollider = meshGameObject.GetComponent();
var mesh = meshGameObject.GetComponent().mesh;
var meshAttributes = shouldComputeNormals ? MeshVertexAttributes.Normals : MeshVertexAttributes.None;
// Request generation
s_MeshSubsystem.GenerateMeshAsync(meshId, mesh, meshCollider, meshAttributes, OnMeshGenerated);
// Update internal state
m_MeshesBeingGenerated.Add(meshId, m_MeshesNeedingGeneration[meshId]);
m_MeshesNeedingGeneration.Remove(meshId);
}
}
void OnMeshGenerated(MeshGenerationResult result)
{
if (result.Status != MeshGenerationStatus.Success)
{
// Handle error, regenerate, etc.
}
m_MeshesBeingGenerated.Remove(result.MeshId);
}