XGRIDSDocumentation
  • 简体中文
  • English
  • 繁體中文
  • 日本語
  • Deutsch
  • Español
  • Italiano
  • Français
  • Русский
  • Polski
  • Nederlands
  • 简体中文
  • English
  • 繁體中文
  • 日本語
  • Deutsch
  • Español
  • Italiano
  • Français
  • Русский
  • Polski
  • Nederlands
  • Lixel CyberColor

    • LCC Studio

      • Prise en main
      • Version et mises à jour
      • Téléchargement et installation
      • Aperçu de l’interface et navigation
      • Avant la reconstruction
      • Reconstruction du modèle
      • Reconstruction d’un modèle unique
      • Fusion cartographique
      • Fusion aérienne-terrestre
      • Reconstruction aérienne
      • Mes modèles
      • Autres fonctionnalités
      • Paramètres et compte
      • Convertisseur
      • Reconstruction vidéo
      • Questions fréquentes
    • LCC Scene Editor

      • Version et mises à jour
      • Compte et connexion
      • Présentation du produit et accueil
      • Interface de l’éditeur
      • Modes de navigation de scène
      • Fichier
      • Paramètres
      • Opérations d’édition
      • Fenêtre
      • Barre d’outils globale
      • Ressources et propriétés
      • Barre d’outils gauche
      • Points de vue
      • Portail
      • Skybox
      • Annotations
      • Mesure
      • Parcours
      • Rapport de scène
      • 3D Layout
      • Mini-carte
      • Mode Prévisualisation, Viewer
      • Aide
      • FAQ
      • Point d’apparition
    • LCC Model Editor

      • Version et mises à jour
      • Guide de l’utilisateur
      • Aperçu et interface
      • Opérations sur les fichiers
      • Sélecteurs
      • Modification des modèles
      • Mesure
      • Réglage des couleurs
      • Gestion des ressources
      • Paramètres et aide
      • Questions fréquentes
    • Bonnes Pratiques

      • Fusion de plusieurs modèles 3DGS de grande taille
  • Plugin & SDK

    • Unreal

      • Introduction
      • Démarrage rapide - Windows
      • Démarrage rapide - Linux
      • Démarrage rapide - Quest3
      • Éditions et licences
      • Rendu
      • Rastérisation Tiled (expérimental)
      • Réglages visuels
      • Normales et éclairage
      • Édition de scène
      • Paramètres de performance
      • Guide d'optimisation des performances
      • Intégration avec les plugins tiers et moteur
      • Maillage proxy
      • Animation de chargement
      • Collision
      • Prise en charge du système de navigation
      • Prise en charge de l'eau à couche unique
      • Localisation
      • Questions fréquentes
      • Dépannage
      • Journaux et diagnostics
      • Nous contacter
      • Bonnes pratiques

        • Rééclairer un 3DGS avec un maillage LixelStudio
      • Référence API

        • ALCCActorBase
        • ULCCComponentBase
        • ULCCComponent
        • ULCC2Component
        • Actors SOG / SPZ / PLY
        • ALCC2ProxyMesh
        • ALCCClippingVolume
        • ALCCSectionPlane
        • ALCCLoadVolume
        • ULCCUtilLibrary
        • Enums
        • Structs
      • Journal des modifications

        • v3.4.0
        • v3.3.1
        • v3.0.0
        • v2.2.1
        • v1.0.0
        • v0.9.0
        • v0.8.0
        • v0.7.1
        • v0.6.1
        • v0.5.2
        • v0.4.1
        • v0.4.0
        • v0.3.0
        • v0.0.5
        • v0.0.4
        • v0.0.3
        • v0.0.2
        • v0.0.1
    • Unity

      • Introduction au SDK Unity
      • Fonctionnalités techniques
      • Exigences de l'environnement de développement
      • Guide de démarrage rapide PC
      • Guide d'optimisation des paramètres de projet
      • Guide de développement Pico 4 Ultra
      • Guide de streaming PC du Pico 4 Ultra
      • Guide de développement Quest 3
      • Guide de streaming PC du Quest 3
      • Guide de développement OpenXR
      • Guide Business Streaming du Pico 4 Ultra
      • Glossaire
      • FAQ
      • Nous contacter
      • Guide de développement des fonctionnalités

        • Guide de développement du rendu mixte multi-scènes
        • Guide de développement de la réception des ombres
        • Guide de développement de l'éclairage en temps réel
        • Guide de développement du rendu multi-caméras
        • Guide de développement du rendu d'images haute résolution
        • Guide de développement de la compatibilité des formats de données LCC et LCC2
        • Guide de développement de l'effet de chargement
        • Guide de développement du basculement du mode de rendu Mip
        • Guide de développement de l'accrochage aux arêtes et aux coins
        • Guide de développement du décalque projeté
        • Guide de développement du décalque projeté vidéo
        • Guide de développement du surlignage
        • Guide de développement de la découpe en temps réel
        • Guide de développement du réglage des couleurs
        • Guide de développement de l'obtention des données Mesh de la scène
        • Guide de développement du mode éditeur
        • Guide de configuration des paramètres de performance de rendu
        • Guide de développement du co-packageage de l'application et des données
        • Guide de développement du système de collision
        • Guide des problèmes d'autorisation de développement macOS
        • Guide de développement de la compilation et du packageage sur appareil iOS
      • Référence de l'API

        • LCCManager
        • Renderer
        • Types de données
      • Journal des modifications

        • v2.3.0
        • v2.2.10
        • v2.2.0
        • v2.1.19
        • v2.0.14

Guide de développement de l'obtention des données Mesh de la scène

Lors de la reconstruction des données LCC, le LCC Unity SDK génère également des données Mesh utilisées pour les collisions. En plus de la détection de collision, le SDK offre la capacité d'extraire ces données Mesh, de les convertir en objets Mesh Unity et de les visualiser, ce qui est pratique pour des scénarios tels que l'exportation et le baking de NavMesh.

Ce qui suit utilise un exemple complet pour décrire les étapes d'obtention synchrone de toutes les données Mesh et d'obtention à la demande des données Mesh d'une plage spécifiée.

Configuration de l'environnement

Créez une toute nouvelle scène dans Unity. Unity crée Caméra principale et Lumière directionnelle par défaut.

Configurer la Caméra principale

Sélectionnez Caméra principale dans la fenêtre Hiérarchie, et définissez ses propriétés dans la fenêtre Inspecteur comme illustré ci-dessous.

Définir les propriétés de la Caméra principale

Définir les propriétés de la Caméra principale

Créer LCCManager

Faites un clic droit dans une zone vide de la fenêtre Hiérarchie, sélectionnez Créer vide dans le menu contextuel pour créer un GameObject vide, nommez-le LCCManager, normalisez son Transform, et ajoutez-y le script LCCManager via Ajouter un composant.

Créer le GameObject LCCManager et ajouter le script LCCManager

Créer le GameObject LCCManager et ajouter le script LCCManager

Créer LCCRenderer

Faites un clic droit dans une zone vide de la fenêtre Hiérarchie, sélectionnez Créer vide pour créer un GameObject vide, nommez-le LCCRenderer, et définissez son Transform sur Rotation.x = -90 et Scale.x = -1.

Créer LCCRenderer et définir son Transform

Créer LCCRenderer et définir son Transform (Rotation.x = -90, Scale.x = -1)

Remarque :

Les réglages de Transform du GameObject LCCRenderer (Rotation.x = -90, Scale.x = -1) l'alignent avec le système de coordonnées des données LCC. Pour plus de détails sur l'alignement du système de coordonnées, voir « Système de coordonnées des données LCC » dans Introduction au SDK Unity.

Prérequis de données

Le prérequis pour que l'obtention des données Mesh prenne effet est que la scène porte les données Mesh de collision correspondantes (générées lorsque LCC Studio la reconstruit). De plus, cette capacité a les contraintes suivantes :

  • PC / Mac uniquement, et seules les sources de données locales sont prises en charge (les sources de données HTTP ne le sont pas).
  • Ne peut être appelé que sur le thread principal.
  • Les données au format natif (PLY / SOG / SPZ / splat / ksplat) ne portent pas de données Mesh de collision ; l'appel des méthodes associées retourne null.

Les données reconstruites dans des versions antérieures peuvent ne pas contenir de données de collision, auquel cas l'appel de GetMeshes() retourne null.

Obtenir synchroniquement toutes les données Mesh

Obtenir synchroniquement toutes les données Mesh signifie lire en une seule fois les données géométriques de tous les blocs Mesh de collision de la scène, implémenté via la méthode Renderer.GetMeshes().

Créez un nouveau script C# nommé Meshes.cs avec le code suivant :

using System.Collections.Generic;
using LCCCore;
using UnityEngine;
using UnityEngine.Rendering;

public class Meshes : MonoBehaviour
{
    public LCCManager m_manager;
    public string m_FilePath;
    private LCCCore.Renderer m_renderer;
    private bool m_isLoaded = false;
    private Camera m_camera;

    private const string kContainerName = "LCCVisualizedMeshes";

    void Start()
    {
        m_camera = Camera.main;
        m_isLoaded = false;
        m_manager.SetPlatformType(PlatformType.PC);

        m_renderer = m_manager.GetRender(this.transform);
        m_renderer.Load(m_FilePath,
            p => Debug.Log($"[Meshes] progression du chargement : {p:P1}"),
            () =>
            {
                Debug.Log($"[Meshes] fichier chargé : {m_FilePath}");
                m_isLoaded = true;
                m_manager.SetAutoFov(false);
            },
            e => Debug.LogError($"[Meshes] erreur de chargement du fichier : {e}")
        );
    }

    public void onGetAllMeshes()
    {
        if (!m_isLoaded || m_renderer == null)
        {
            Debug.LogWarning("[Meshes] Les données n'ont pas fini de charger ; impossible d'obtenir les Meshes.");
            return;
        }

        var meshes = m_renderer.GetMeshes();
        if (meshes == null || meshes.Count == 0)
        {
            Debug.LogWarning("[Meshes] Aucun Mesh obtenu (les données peuvent ne pas contenir de colliders).");
            return;
        }

        VisualizeMeshes(meshes);
    }

    private void VisualizeMeshes(List<LCCMeshBlock> meshes)
    {
        // Nettoyer le résultat de visualisation précédent pour éviter l'accumulation
        Transform root = m_renderer.Transform;
        Transform old = root.Find(kContainerName);
        if (old != null) Destroy(old.gameObject);

        var container = new GameObject(kContainerName).transform;
        container.SetParent(root, false);

        Material material = CreateVisualMaterial();

        for (int i = 0; i < meshes.Count; i++)
        {
            LCCMeshBlock block = meshes[i];
            if (block.vertices == null || block.vertices.Length == 0) continue;
            if (block.triangles == null || block.triangles.Length < 3) continue;

            var go = new GameObject(string.IsNullOrEmpty(block.id) ? $"MeshBlock_{i}" : block.id);
            go.transform.SetParent(container, false);

            var mesh = new Mesh { name = go.name };
            // Le nombre de sommets d'un seul bloc peut dépasser 65535, des indices 32 bits doivent donc être utilisés, sinon la géométrie sera corrompue
            mesh.indexFormat = IndexFormat.UInt32;
            mesh.vertices = block.vertices;
            mesh.triangles = block.triangles;
            mesh.RecalculateNormals();
            mesh.RecalculateBounds();

            var mf = go.AddComponent<MeshFilter>();
            mf.sharedMesh = mesh;

            var mr = go.AddComponent<MeshRenderer>();
            mr.sharedMaterial = material;
        }

        Debug.Log($"[Meshes] GameObjects créés et {meshes.Count} blocs Mesh visualisés.");
    }

    private Material CreateVisualMaterial()
    {
        Shader shader = Shader.Find("Universal Render Pipeline/Lit")
            ?? Shader.Find("Standard")
            ?? Shader.Find("Diffuse");
        if (shader == null) return null;

        var mat = new Material(shader) { color = new Color(0.7f, 0.7f, 0.7f, 1f) };
        // L'ordre d'enroulement des triangles du mesh de collision n'est pas garanti cohérent ; désactiver le culling des faces arrière pour un affichage complet
        mat.SetFloat("_Cull", (float)CullMode.Off);
        return mat;
    }
}

Remarque :

  • GetMeshes() est une opération lourde synchrone : elle lit de manière synchrone depuis le disque, analyse et alloue de la mémoire. Dans les grandes scènes, par ex. un jeu de données de 262 blocs, elle peut avoir besoin de lire une grande quantité de données disque et d'allouer de plusieurs Go à des dizaines de Go de mémoire managée. Appelez-la une fois et mettez vous-même le résultat en cache ; ne l'appelez jamais à chaque image.
  • vertices / boundsLocal sont dans le système de coordonnées local de l'objet, donc attacher le GameObject de visualisation sous Renderer.Transform l'aligne automatiquement.
  • Le nombre de sommets d'un seul bloc peut dépasser 65535 (maximum mesuré 390 656) ; avant de construire votre propre Mesh, vous devez définir mesh.indexFormat = IndexFormat.UInt32, sinon la géométrie sera corrompue.
  • L'ordre d'enroulement des triangles du mesh de collision n'est pas garanti cohérent ; l'exemple désactive le culling des faces arrière (_Cull = Off) pour un affichage complet.

Attachez le script au GameObject LCCRenderer, et dans l'Inspecteur, faites glisser le GameObject LCCManager sur la propriété Manager et renseignez le File Path.

Créez ensuite un Bouton et associez la méthode onGetAllMeshes() à son événement onClick.

Après l'exécution, cliquez sur le Bouton pour voir tous les Meshes de collision de la scène.

Obtenir synchroniquement toutes les données Mesh et les visualiser

Visualiser toutes les données Mesh de collision après avoir cliqué sur le bouton

Obtenir les données Mesh à la demande

La récupération à la demande signifie lire uniquement la partie spécifiée des données Mesh plutôt que de lire toutes les données Mesh en une fois, afin d'éviter un surcoût important d'E/S et de mémoire. Ce qui suit utilise la lecture des blocs Mesh dans une certaine plage autour de la caméra comme exemple.

Utilisez d'abord GetMeshBlockInfos() pour obtenir les métadonnées de tous les blocs (y compris les boîtes englobantes en espace mondial), calculez la distance du centre de la caméra de rendu à la surface BBOX du bloc en coordonnées mondiales, filtrez les ids de blocs dont la distance est inférieure à 100 m, puis utilisez la version paramétrée GetMeshes(ids) pour lire uniquement ces blocs.

Ajoutez les méthodes suivantes à Meshes.cs :

public void onRequireMeshes()
{
    if (!m_isLoaded || m_renderer == null)
    {
        Debug.LogWarning("[Meshes] Les données n'ont pas fini de charger ; impossible d'obtenir les Meshes.");
        return;
    }

    // 1. Lecture sans E/S de toutes les métadonnées de blocs, filtrage des blocs à moins de 100 m par la distance caméra-surface BBOX
    var infos = m_renderer.GetMeshBlockInfos();
    if (infos == null || infos.Count == 0)
    {
        Debug.LogWarning("[Meshes] Aucune info de Mesh obtenue (les données peuvent ne pas contenir de colliders).");
        return;
    }

    Vector3 camPos = m_camera.transform.position;
    var ids = new List<string>();
    foreach (var info in infos)
    {
        // Lorsque la caméra est à l'intérieur de la BBOX, ClosestPoint retourne la caméra elle-même et la distance est 0, elle est donc également sélectionnée
        if (calculateMinDistanceToAABB(info.boundsWorld, camPos) < 100f)
            ids.Add(info.id);
    }

    if (ids.Count == 0)
    {
        Debug.LogWarning("[Meshes] Aucun bloc Mesh à moins de 100 m de la caméra.");
        return;
    }

    // 2. Lire uniquement les blocs filtrés par id et les visualiser
    var meshes = m_renderer.GetMeshes(ids);
    if (meshes == null || meshes.Count == 0)
    {
        Debug.LogWarning("[Meshes] Échec de l'obtention des Meshes par id.");
        return;
    }

    VisualizeMeshes(meshes);
    Debug.Log($"[Meshes] {ids.Count} blocs sélectionnés à moins de 100 m de la caméra, {meshes.Count} visualisés.");
}

// Calculer la distance la plus proche d'un point à la surface AABB (retourne 0 lorsque le point est à l'intérieur de l'AABB)
private static float calculateMinDistanceToAABB(Bounds _aabb, Vector3 _point)
{
    Vector3 p = _aabb.ClosestPoint(_point);
    return Vector3.Distance(p, _point);
}

Remarque :

  • GetMeshBlockInfos() est une opération sans E/S et sans analyse qui ne lit que les métadonnées déjà en mémoire ; vous pouvez l'appeler librement.
  • Le filtre de distance utilise boundsWorld (boîte englobante en espace mondial), et le centre de la caméra prend Camera.transform.position.
  • Utilisez Bounds.ClosestPoint pour calculer la distance la plus proche d'un point à la surface AABB : lorsque la caméra est à l'intérieur de la BBOX, ClosestPoint retourne la caméra elle-même et la distance est 0, donc ce bloc est également sélectionné.
  • Le rayon de filtrage 100f (unité : mètres) de l'exemple peut être ajusté selon les besoins.

Créez un Bouton et associez onRequireMeshes() à son événement onClick.

Après l'exécution, cliquez sur le bouton pour voir les blocs Mesh à moins de 100 m de la caméra.

Obtenir à la demande les données Mesh à moins de 100 m de la caméra et les visualiser

Obtenir à la demande les données Mesh à moins de 100 m de la caméra

Étapes clés

Les étapes clés pour obtenir et visualiser les données Mesh de la scène :

  1. Terminez la pré-configuration telle que SetPlatformType, et chargez la scène LCC via GetRender + Load.
  2. Confirmez que les données portent des données Mesh de collision (les formats natifs ou les données reconstruites dans des versions antérieures peuvent ne pas en contenir).
  3. Récupération complète : appelez GetMeshes() pour lire tous les blocs en une fois et mettez vous-même le résultat en cache pour éviter les appels répétés.
  4. Récupération à la demande : utilisez d'abord GetMeshBlockInfos() pour obtenir les boîtes englobantes mondiales des blocs, filtrez les ids par distance de caméra, puis utilisez GetMeshes(ids) pour lire uniquement les blocs cibles.
  5. Lors de la visualisation, attachez le GameObject sous Renderer.Transform pour l'aligner avec les coordonnées locales, et construisez le Mesh avec IndexFormat.UInt32.

API de récupération de Mesh

Les méthodes suivantes se trouvent toutes sur Renderer ; elles doivent être appelées après la fin du chargement de la scène, et les données doivent porter des données Mesh de collision.

GetMeshBlockInfos

// Valeur de retour : List<LCCMeshBlockInfo> → métadonnées des blocs Mesh de collision ; null lorsqu'il n'y a pas de Mesh ou indisponible
public List<LCCMeshBlockInfo> GetMeshBlockInfos()

Retourne les métadonnées de tous les blocs Mesh de collision (id, boîte englobante mondiale/locale, nombre de sommets, nombre de faces). Sans E/S et sans analyse ; ne lit que les métadonnées déjà en mémoire.

Exemple d'utilisation :

var infos = renderer.GetMeshBlockInfos();
foreach (var info in infos)
{
    Debug.Log($"{info.id}: verts={info.vertexCount}, faces={info.faceCount}, bbox={info.boundsWorld.size}");
}

Remarques :

  • PC / Mac uniquement + source de données locale ; thread principal uniquement.
  • Utilisation typique : utilisez-la d'abord pour obtenir la BBox mondiale de chaque bloc, choisissez les ids nécessaires, puis appelez GetMeshes(ids) pour lire uniquement ces blocs.
  • Les formats natifs (PLY / SOG / SPZ / splat / ksplat) n'ont pas de Mesh et retournent null (ce qui est normal et non journalisé).

GetMeshes

// Valeur de retour : List<LCCMeshBlock> → toutes les données géométriques des Mesh de collision ; null lorsqu'indisponible
public List<LCCMeshBlock> GetMeshes()

Retourne de manière synchrone les données géométriques de tous les blocs Mesh de collision (sommets + indices de triangles, dans le système de coordonnées local). C'est une opération lourde (lecture disque synchrone + analyse + allocation) ; appelez-la une fois et mettez-la vous-même en cache, jamais à chaque image.

Exemple d'utilisation :

var meshes = renderer.GetMeshes();
foreach (var block in meshes)
{
    // block.vertices / block.triangles sont dans le système de coordonnées local de l'objet
}

Remarques :

  • PC / Mac uniquement + source de données locale ; thread principal uniquement.
  • Pour les grandes scènes, utilisez GetMeshBlockInfos() pour choisir les ids + GetMeshes(ids) à la place, afin d'éviter d'allouer une énorme quantité de mémoire managée en une fois.
  • Le nombre de sommets d'un seul bloc peut dépasser 65535 ; avant de construire votre propre Mesh, vous devez définir mesh.indexFormat = IndexFormat.UInt32.

GetMeshes (par id)

// Valeur de retour : List<LCCMeshBlock> → données géométriques des Mesh de collision pour les ids spécifiés ; null lorsqu'il n'y a pas d'impact
public List<LCCMeshBlock> GetMeshes(IEnumerable<string> _ids)

Retourne de manière synchrone les données géométriques des blocs Mesh de collision avec les ids spécifiés (système de coordonnées local). Les ids sont fournis par GetMeshBlockInfos().

ParamètreTypeDescription
_idsIEnumerable<string>La collection d'ids de blocs cibles (fournie par GetMeshBlockInfos())

Exemple d'utilisation :

var ids = new List<string> { "block-0", "block-3" };
var meshes = renderer.GetMeshes(ids);

Remarques :

  • Les ids inconnus sont ignorés avec un avertissement ; les ids dupliqués sont dédupliqués (pas de lecture disque répétée) ; l'ordre de retour est l'ordre de parcours des ids (après déduplication).
  • Passer null, une entrée vide ou aucun impact retourne null avec un avertissement.
  • Les autres contraintes sont identiques à GetMeshes().

Structure de données LCCMeshBlockInfo

public struct LCCMeshBlockInfo {
    public string id;           // id de nœud, correspondant à LCCMeshBlock.id
    public Bounds boundsWorld;  // AABB en espace mondial, utilisée pour sélectionner les ids
    public Bounds boundsLocal;  // AABB en espace local
    public int vertexCount;     // nombre de sommets
    public int faceCount;       // nombre de faces
}
ChampTypeDescription
idstringId de nœud, correspondant à LCCMeshBlock.id
boundsWorldBoundsAABB en espace mondial, utilisée pour sélectionner les ids
boundsLocalBoundsAABB en espace local
vertexCountintNombre de sommets
faceCountintNombre de faces

Structure de données LCCMeshBlock

public struct LCCMeshBlock {
    public string id;           // id de nœud
    public Vector3[] vertices;  // sommets, système de coordonnées local
    public int[] triangles;     // indices de triangles, 3 par triangle
    public Bounds boundsLocal;  // bornes dans le même espace que les sommets
}
ChampTypeDescription
idstringId de nœud
verticesVector3[]Tableau de sommets, système de coordonnées local de l'objet
trianglesint[]Indices de triangles, 3 par triangle
boundsLocalBoundsBornes dans le même espace que les sommets

Remarque :

vertices / boundsLocal sont tous deux dans le système de coordonnées local de l'objet. Pour les visualiser dans Unity, attachez le GameObject qui porte le Mesh sous Renderer.Transform pour qu'il s'aligne automatiquement. Le nombre de sommets d'un seul bloc peut dépasser 65535 ; avant de construire votre propre Mesh, vous devez définir mesh.indexFormat = IndexFormat.UInt32.