Consultas espaciales

Las consultas espaciales son operaciones con las que puede preguntar al servicio de representación remoto qué objetos se encuentran en un área. Las consultas espaciales se usan con frecuencia para implementar interacciones, como averiguar a qué objeto apunta un usuario.

Todas las consultas espaciales se evalúan en el servidor. En consecuencia, las consultas son operaciones asincrónicas y los resultados llegan con un retraso que depende de la latencia de red.

Proyecciones de rayo

Una conversión de rayos es una consulta espacial en la que el tiempo de ejecución comprueba qué objetos intersecan un rayo, empezando por una posición determinada y apuntando a una determinada dirección. Como optimización, también se proporciona una distancia de rayo máxima, para no buscar objetos que estén demasiado lejos. Aunque realizar cientos de conversiones de rayos cada fotograma es computacionalmente factible en el lado servidor, cada consulta también genera tráfico de red, por lo que el número de consultas por fotograma debe mantenerse lo más bajo posible.

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
            }
        }
    });
}

Hay tres modos de recopilación de incidencias:

  • Closest: en este modo, solo se notifica el acierto más cercano.
  • Any: prefiere este modo cuando todo lo que quiera saber es si un rayo alcanzaría algo, pero no le importa lo que se golpeó exactamente. Esta consulta puede ser considerablemente más económica de evaluar, pero también tiene pocas aplicaciones.
  • All: en este modo, se notifican todos los golpes a lo largo del rayo, ordenados por distancia. No utilice este modo a menos que realmente necesite más que la primera incidencia. Limite el número de incidencias mostradas con la opción MaxHits.

Para excluir objetos de forma selectiva de la consideración de proyecciones de rayo, se puede usar el componente HierarchicalStateOverrideComponent.

Resultado de incidencias

El resultado de una consulta de proyección de rayo es una matriz de incidencias. La matriz está vacía, si no incide en ningún objeto.

Una incidencia tiene las siguientes propiedades:

  • HitEntity: la entidad a la que se ha alcanzado.
  • SubPartId: qué submesh se alcanzó en meshComponent. Puede usarse para la indexación en MeshComponent.UsedMaterials y para la búsqueda del material en ese punto.
  • HitPosition: posición del espacio mundial donde el rayo intersecó el objeto.
  • HitNormal: la superficie espacial mundial normal de la malla en la posición de la intersección.
  • DistanceToHit: distancia desde la posición inicial del rayo hasta el golpe.
  • HitType: lo que recibe el rayo: TriangleFrontFace, TriangleBackFace o Point. De forma predeterminada, ARR representa el doble lado , por lo que los triángulos que ve el usuario no son necesariamente frontales. Si quiere distinguir entre TriangleFrontFace y TriangleBackFace en el código, asegúrese de que los modelos se crean primero con las direcciones faciales correctas.

Consultas espaciales

Una consulta espacial permite que el tiempo de ejecución compruebe qué MeshComponents interseca con un volumen definido por el usuario. Esta comprobación es eficaz, ya que la comprobación individual se realiza en función de los límites de cada elemento de malla de la escena, no en un triángulo individual. Como optimización, se puede proporcionar un número máximo de componentes de malla de posicionamiento.
Aunque esta consulta se puede ejecutar manualmente en el lado cliente, para escenas grandes puede ser órdenes de magnitud más rápidas para que el servidor calcule esto.

En el código de ejemplo siguiente se muestra cómo realizar consultas en un rectángulo de límite alineado con el eje (AABB). Las variantes de la consulta también permiten volúmenes de cuadros de límite orientados (SpatialQueryObbAsync) y volúmenes de esfera (SpatialQuerySphereAsync).

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
                }
            }
        });
}

Documentación de la API

Pasos siguientes