Consultas espaciais

As consultas espaciais são operações com as quais você pode perguntar ao serviço de renderização remoto quais objetos estão localizados em uma área. Consultas espaciais são usadas frequentemente para implementar interações, como descobrir para qual objeto um usuário está apontando.

Todas as consultas espaciais são avaliadas no servidor. Assim, as consultas são operações assíncronas e os resultados chegam com um atraso que depende da latência da rede.

Incidências de raio

Um ray cast é uma consulta espacial onde o tempo de execução verifica quais objetos cruzam um raio, começando em uma determinada posição e apontando para uma determinada direção. Como uma otimização, uma distância máxima de raios também é mostrada, para não procurar objetos que estejam muito distantes. Embora fazer centenas de ray casts em cada quadro seja computacionalmente viável no lado do servidor, cada consulta também gera tráfego de rede, portanto, o número de consultas por quadro deve ser mantido o mais baixo possível.

async void CastRay(RenderingSession session)
{
    // trace a line from the origin into the +z direction, over 10 units of distance.
    RayCast rayCast = new RayCast(new Double3(0, 0, 0), new Double3(0, 0, 1), 10);

    // only return the closest hit
    rayCast.HitCollection = HitCollectionPolicy.ClosestHit;

    RayCastQueryResult result = await session.Connection.RayCastQueryAsync(rayCast);
    RayCastHit[] hits = result.Hits;
    if (hits.Length > 0)
    {
        var hitObject = hits[0].HitObject;
        var hitPosition = hits[0].HitPosition;
        var hitNormal = hits[0].HitNormal;
        var hitType = hits[0].HitType;
        // do something with the hit information
    }
}
void CastRay(ApiHandle<RenderingSession> session)
{
    // trace a line from the origin into the +z direction, over 10 units of distance.
    RayCast rayCast;
    rayCast.StartPos = {0, 0, 0};
    rayCast.EndPos = {0, 0, 10};

    // only return the closest hit
    rayCast.HitCollection = HitCollectionPolicy::ClosestHit;

    session->Connection()->RayCastQueryAsync(rayCast, [](Status status, ApiHandle<RayCastQueryResult> result)
    {
        if (status == Status::OK)
        {
            std::vector<RayCastHit> hits;
            result->GetHits(hits);

            if (hits.size() > 0)
            {
                auto hitObject = hits[0].HitObject;
                auto hitPosition = hits[0].HitPosition;
                auto hitNormal = hits[0].HitNormal;
                auto hitType = hits[0].HitType;

                // do something with the hit information
            }
        }
    });
}

Há três modos de coleta de impactos:

  • Closest: Neste modo, apenas o acerto mais próximo é relatado.
  • Any: Prefira este modo quando tudo o que você quer saber é se um raio acertaria alguma coisa, mas não se importe com o que foi atingido exatamente. Essa consulta pode ser consideravelmente mais barata para avaliar, mas também tem poucas aplicações.
  • All: Neste modo, todos os acertos ao longo do raio são relatados, classificados por distância. Não use esse modo, a menos que você realmente precise saber mais do que o primeiro impacto. Limite o número de impactos relatados com a opção MaxHits.

Para excluir objetos de forma seletiva a serem considerados para incidências de raio, o componente HierarchicalStateOverrideComponent pode ser usado.

Resultado do impacto

O resultado de uma consulta de incidências de raio é uma matriz de impactos. A matriz estará vazia se nenhum objeto tiver sido atingido.

Um impacto tem as seguintes propriedades:

  • HitEntity: Qual entidade foi atingida.
  • SubPartId: Qual submalha foi atingida em um MeshComponent. Pode ser usado para indexar MeshComponent.UsedMaterials e procurar o material nesse ponto.
  • HitPosition: A posição do espaço mundial onde o raio cruzou o objeto.
  • HitNormal: A superfície espacial mundial normal da malha na posição da interseção.
  • DistanceToHit: A distância entre a posição inicial do raio e a batida.
  • HitType: O que é atingido pelo raio:TriangleFrontFace, TriangleBackFace ou Point. Por padrão, o ARR renderiza frente e verso para que os triângulos que o usuário vê não sejam necessariamente voltados para a frente. Se você quiser diferenciar entre TriangleFrontFace e TriangleBackFace em seu código, certifique-se de que seus modelos sejam criados com as direções de rosto corretas primeiro.

Consultas espaciais

Uma consulta espacial permite que o tempo de execução verifique quais MeshComponents se cruzam com um volume definido pelo usuário. Essa verificação é eficiente, pois a verificação individual é executada com base nos limites de cada parte da malha na cena, não em uma base de triângulo individual. Como otimização, um número máximo de componentes de malha de ocorrência pode ser fornecido.
Embora essa consulta possa ser executada manualmente no lado do cliente, para cenas grandes pode ser ordens de magnitude mais rápidas para o servidor computar isso.

O código de exemplo a seguir mostra como fazer consultas em uma caixa delimitadora alinhada ao eixo (AABB). As variantes da consulta também permitem volumes de caixa delimitadora orientados () e volumes de esfera (SpatialQueryObbAsyncSpatialQuerySphereAsync).

async void QueryAABB(RenderingSession session)
{
    // Query all mesh components in a 2x2x2m cube.
    SpatialQueryAabb query = new SpatialQueryAabb();
    query.Bounds = new Microsoft.Azure.RemoteRendering.Bounds(new Double3(-1, -1, -1), new Double3(1, 1, 1));
    query.MaxResults = 100;

    SpatialQueryResult result = await session.Connection.SpatialQueryAabbAsync(query);
    foreach (MeshComponent meshComponent in result.Overlaps)
    {
        Entity owner = meshComponent.Owner;
        // do something with the hit MeshComponent / Entity
    }
}
void QueryAABB(ApiHandle<RenderingSession> session)
{
    // Query all mesh components in a 2x2x2m cube.
    SpatialQueryAabb query;
    query.Bounds.Min = {-1, -1, -1};
    query.Bounds.Max = {1, 1, 1};
    query.MaxResults = 100;

    session->Connection()->SpatialQueryAabbAsync(query, [](Status status, ApiHandle<SpatialQueryResult> result)
        {
            if (status == Status::OK)
            {
                std::vector<ApiHandle<MeshComponent>> overlaps;
                result->GetOverlaps(overlaps);

                for (ApiHandle<MeshComponent> meshComponent : overlaps)
                {
                    ApiHandle<Entity> owner = meshComponent->GetOwner();
                    // do something with the hit MeshComponent / Entity
                }
            }
        });
}

Documentação da API

Próximas etapas