Consultas espaciais

As consultas espaciais são operações com as quais você pode perguntar ao serviço de renderização remota quais objetos estão localizados em uma área. As consultas espaciais são frequentemente usadas 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.

Fundições 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 otimização, também é dada uma distância máxima de raios, para não procurar objetos que estão muito longe. Embora fazer centenas de ray casts 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
            }
        }
    });
}

Existem três modos de recolha de acertos:

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

Para excluir objetos seletivamente de serem considerados para moldes de raios, o componente HierarchicalStateOverrideComponent pode ser usado.

Resultado do acerto

O resultado de uma consulta de ray cast é uma matriz de acertos. A matriz está vazia, se nenhum objeto foi atingido.

Um Hit 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 do espaço mundial normal da malha na posição da intersecção.
  • DistanceToHit: A distância entre a posição inicial do raio e o acerto.
  • 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 são 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 é executante, pois a verificação individual é realizada com base nos limites de cada parte de malha na cena, não em uma base de triângulo individual. Como uma otimização, um número máximo de componentes de malha de acerto pode ser fornecido.
Embora essa consulta possa ser executada manualmente no lado do cliente, para cenas grandes pode ser ordens de grandeza mais rápidas para o servidor calcular 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óximos passos