Requêtes spatiales

Les requêtes spatiales sont des opérations grâce auxquelles vous pouvez demander au service de rendu distant les objets qui se trouvent dans une zone. Les requêtes spatiales sont souvent utilisées pour implémenter des interactions, telles que l’identification de l’objet vers lequel pointe l’utilisateur.

Toutes les requêtes spatiales sont évaluées sur le serveur. En conséquence, les requêtes sont des opérations asynchrones et les résultats arrivent avec un délai qui dépend de la latence de votre réseau.

Ray casts

Un cast de rayons est une requête spatiale où le runtime case activée les objets qui croisent un rayon, en commençant à une position donnée et pointant vers une certaine direction. En matière d’optimisation, une distance de rayon maximale est également donnée pour ne pas rechercher des objets trop éloignés. Bien que l’exécution de centaines de rayons caste chaque trame soit possible par calcul côté serveur, chaque requête génère également le trafic réseau, de sorte que le nombre de requêtes par image doit être conservé aussi bas que possible.

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

Il existe trois modes de collecte de hits :

  • Closest: dans ce mode, seul l’accès le plus proche est signalé.
  • Any: Préférez ce mode quand tout ce que vous voulez savoir est si un rayon frapperait quoi que ce soit, mais ne vous souciez pas exactement de ce qui a été atteint exactement. Cette requête peut être beaucoup moins coûteuse à évaluer, mais elle n’a que peu d’applications.
  • All: dans ce mode, tous les coups le long du rayon sont signalés, triés par distance. N’utilisez pas ce mode, à moins que le premier hit à lui seul ne soit pas suffisant. Limitez le nombre de hits signalés avec l’option MaxHits.

L’exclusion sélective d’objets, pour qu’ils ne soient pas pris en compte par les ray casts, peut se faire en utilisant le composant HierarchicalStateOverrideComponent.

Résultat des hits

Le résultat d’une requête de ray cast est un tableau de hits. Le tableau est vide si aucun objet n’a été touché.

Un hit présente les propriétés suivantes :

  • HitEntity: Quelle entité a été atteinte.
  • SubPartId: Quel sous-fichier a été frappé dans un MeshComponent. Peut être utilisé pour une indexation dans MeshComponent.UsedMaterials et une recherche des matériaux à ce stade.
  • HitPosition: position spatiale du monde où le rayon a croisé l’objet.
  • HitNormal: surface spatiale mondiale normale du maillage à la position de l’intersection.
  • DistanceToHit: distance entre la position de départ du rayon et la frappe.
  • HitType: Qu’est-ce qui est frappé par le rayon : TriangleFrontFaceou TriangleBackFacePoint. Par défaut, ARR restitue deux côtés afin que les triangles que l’utilisateur voit ne soient pas nécessairement à l’avant. Si vous souhaitez faire la différence entre TriangleFrontFace et TriangleBackFace dans votre code, assurez-vous que vos modèles sont créés avec des directions de visage correctes en premier.

Requêtes spatiales

Une requête spatiale permet au runtime de case activée laquelle MeshComponents se croise avec un volume défini par l’utilisateur. Cette case activée est performante, car la case activée individuelle est effectuée en fonction des limites de chaque partie de maillage dans la scène, et non sur une base de triangle individuelle. En guise d’optimisation, un nombre maximal de composants de maillage d’accès peut être fourni.
Bien qu’une telle requête puisse être exécutée manuellement côté client, pour les grandes scènes, il peut s’agir d’ordres de grandeur plus rapides pour que le serveur calcule cela.

L’exemple de code suivant montre comment effectuer des requêtes sur un cadre englobant aligné sur l’axe (AABB). Les variantes de la requête permettent également des volumes englobants orientés (SpatialQueryObbAsync) et des volumes de sphère (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
                }
            }
        });
}

Documentation de l’API

Étapes suivantes