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

    • PortalCam P1

      • Przegląd produktu
      • Podstawowa obsługa
      • Korzystanie z aplikacji LCC Scan
      • Konserwacja i pielęgnacja
      • FAQ
  • Seria Lixel K

    • Lixel K1

      • Przegląd produktu
      • Podstawowa obsługa
      • Aktywacja i połączenie urządzenia
      • Przebieg skanowania
      • Pozyskiwanie danych chmury punktów ze współrzędnymi bezwzględnymi
      • Fuzja map
      • Zalecenia dotyczące planowania trasy dla typowych scen
      • Środki ostrożności
      • FAQ
    • Lixel K2

      • Przegląd produktu
      • Podstawowa obsługa
      • Aktywacja i połączenie urządzenia
      • Przebieg skanowania
      • Pozyskiwanie danych chmury punktów ze współrzędnymi bezwzględnymi
      • Fuzja map
      • Zalecenia dotyczące planowania trasy dla typowych scen
      • Środki ostrożności
      • FAQ
  • Seria Lixel L

    • Lixel L2 Pro

      • Przegląd produktu
      • Podstawowa obsługa
      • Aktywacja i połączenie urządzenia
      • Proces skanowania
      • Pozyskiwanie danych chmury punktów ze współrzędnymi bezwzględnymi
      • Punkt pomiarowy
      • Dodatek
      • FAQ
    • Lixel L3

      • Przegląd produktu
      • Podstawowe funkcje urządzenia
      • Aktywacja i połączenie urządzenia
      • Przechwytywanie danych
      • Przechwytywanie współrzędnych bezwzględnych
      • Dodatek
  • Akcesoria

    • Stick Light

      • Przewodnik instalacji
      • Podstawowa obsługa
      • Często zadawane pytania
  • Lixel Studio

    • Wersja i prawa autorskie
    • Instalacja i aktywacja
    • Interfejs oprogramowania
    • Operacje na plikach
    • Przetwarzanie projektu
    • Narzędzia
    • Rysowanie 2D
    • Zastosowania
    • Ustawienia
    • Połączenie z urządzeniem
  • 07-plugin-sdk

    • Unity

      • Wprowadzenie do Unity SDK
      • Funkcje techniczne
      • Wymagania środowiska deweloperskiego
      • Przewodnik szybkiego startu dla PC
      • Przewodnik optymalizacji ustawień projektu
      • Przewodnik deweloperski dla Pico 4 Ultra
      • Przewodnik strumieniowania PC dla Pico 4 Ultra
      • Przewodnik deweloperski dla Quest 3
      • Przewodnik strumieniowania PC dla Quest 3
      • Przewodnik deweloperski dla OpenXR
      • Przewodnik Business Streaming dla Pico 4 Ultra
      • Słownik
      • Często zadawane pytania (FAQ)
      • Skontaktuj się z nami
      • Przewodnik programowania funkcji

        • Przewodnik mieszanego renderowania wielu scen
        • Przewodnik odbierania cieni
        • Przewodnik oświetlenia w czasie rzeczywistym
        • Przewodnik renderowania wielokamerowego
        • Przewodnik renderowania obrazów wysokiej rozdzielczości
        • Przewodnik zgodności formatów danych LCC i LCC2
        • Przewodnik efektu ładowania
        • Przewodnik przełączania trybu renderowania Mip
        • Przewodnik przyciągania do krawędzi i narożników
        • Przewodnik dekala projekcyjnego
        • Przewodnik wideo dekala projekcyjnego
        • Przewodnik podświetlania
        • Przewodnik przycinania w czasie rzeczywistym
        • Przewodnik korekcji kolorów
        • Przewodnik pobierania danych siatki sceny
        • Przewodnik trybu edytora
        • Przewodnik konfiguracji parametrów wydajności renderowania
        • Przewodnik współpakowania aplikacji i danych
        • Przewodnik systemu kolizji
        • Przewodnik problemów z uprawnieniami w deweloperstwie na macOS
        • Przewodnik kompilacji i pakowania na urządzenia iOS
      • Dokumentacja API

        • LCCManager
        • Renderer
        • Typy danych
      • Dziennik zmian

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

Przewodnik pobierania danych siatki sceny

Podczas rekonstrukcji danych LCC LCC Unity SDK generuje również dane siatki używane do kolizji. Oprócz wykrywania kolizji SDK udostępnia możliwość wyodrębnienia tych danych siatki, konwersji ich na obiekty Mesh Unity i wizualizacji, co jest wygodne w scenariuszach takich jak eksportowanie i wypiekanie NavMesh.

Poniżej na kompletnym przykładzie opisano kroki synchronicznego pobierania wszystkich danych siatki oraz pobierania danych siatki z określonego zakresu na żądanie.

Konfiguracja środowiska

Utwórz zupełnie nową scenę w Unity. Unity domyślnie tworzy Kamerę główną i Światło kierunkowe.

Skonfiguruj Kamerę główną

Wybierz Kamerę główną w oknie Hierarchia i ustaw jej właściwości w oknie Inspektor, jak pokazano poniżej.

Ustaw właściwości Kamery głównej

Ustaw właściwości Kamery głównej

Utwórz LCCManager

Kliknij prawym przyciskiem myszy pusty obszar w oknie Hierarchia, wybierz Utwórz pusty z menu podręcznego, aby utworzyć pusty GameObject, nazwij go LCCManager, znormalizuj jego Transform i dodaj do niego skrypt LCCManager przez Dodaj komponent.

Utwórz GameObject LCCManager i dodaj skrypt LCCManager

Utwórz GameObject LCCManager i dodaj skrypt LCCManager

Utwórz LCCRenderer

Kliknij prawym przyciskiem myszy pusty obszar w oknie Hierarchia, wybierz Utwórz pusty, aby utworzyć pusty GameObject, nazwij go LCCRenderer i ustaw jego Transform na Rotation.x = -90 i Scale.x = -1.

Utwórz LCCRenderer i ustaw jego Transform

Utwórz LCCRenderer i ustaw jego Transform (Rotation.x = -90, Scale.x = -1)

Uwaga:

Ustawienia Transform GameObject LCCRenderer (Rotation.x = -90, Scale.x = -1) wyrównują go z układem współrzędnych danych LCC. Szczegóły wyrównania układu współrzędnych znajdziesz w sekcji „Układ współrzędnych danych LCC" w Wprowadzenie do Unity SDK.

Wymagania wstępne danych

Warunkiem zadziałania pobierania danych siatki jest to, że scena przenosi odpowiednie dane siatki kolizji (generowane, gdy LCC Studio ją rekonstruuje). Ponadto ta możliwość ma następujące ograniczenia:

  • Tylko PC / Mac i obsługiwane są tylko lokalne źródła danych (źródła danych HTTP nie są obsługiwane).
  • Można wywoływać tylko na wątku głównym.
  • Dane w formacie natywnym (PLY / SOG / SPZ / splat / ksplat) nie przenoszą danych siatki kolizji; wywołanie powiązanych metod zwraca null.

Dane rekonstruowane we wcześniejszych wersjach mogą nie zawierać danych kolizji, w takim przypadku wywołanie GetMeshes() zwraca null.

Synchroniczne pobieranie wszystkich danych siatki

Synchroniczne pobieranie wszystkich danych siatki oznacza odczytanie naraz danych geometrii wszystkich bloków siatki kolizji w scenie, zaimplementowane przez metodę Renderer.GetMeshes().

Utwórz nowy skrypt C# o nazwie Meshes.cs z następującym kodem:

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] postęp ładowania: {p:P1}"),
            () =>
            {
                Debug.Log($"[Meshes] plik załadowany: {m_FilePath}");
                m_isLoaded = true;
                m_manager.SetAutoFov(false);
            },
            e => Debug.LogError($"[Meshes] błąd ładowania pliku: {e}")
        );
    }

    public void onGetAllMeshes()
    {
        if (!m_isLoaded || m_renderer == null)
        {
            Debug.LogWarning("[Meshes] Dane nie zostały jeszcze załadowane; nie można pobrać siatek.");
            return;
        }

        var meshes = m_renderer.GetMeshes();
        if (meshes == null || meshes.Count == 0)
        {
            Debug.LogWarning("[Meshes] Nie pobrano żadnych siatek (dane mogą nie zawierać koliderów).");
            return;
        }

        VisualizeMeshes(meshes);
    }

    private void VisualizeMeshes(List<LCCMeshBlock> meshes)
    {
        // Wyczyść poprzedni wynik wizualizacji, aby uniknąć gromadzenia
        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 };
            // Liczba wierzchołków pojedynczego bloku może przekroczyć 65535, więc muszą być użyte indeksy 32-bitowe, w przeciwnym razie geometria zostanie uszkodzona
            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] Utworzono GameObjects i zwizualizowano {meshes.Count} bloków siatki.");
    }

    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) };
        // Kolejność nawijania trójkątów siatki kolizji nie jest gwarantowana jako spójna; wyłącz odcinanie ścian tylnych dla pełnego wyświetlenia
        mat.SetFloat("_Cull", (float)CullMode.Off);
        return mat;
    }
}

Uwaga:

  • GetMeshes() to synchroniczna ciężka operacja: synchronicznie odczytuje z dysku, analizuje i alokuje pamięć. W dużych scenach, np. zestawie danych o 262 blokach, może wymagać odczytu dużej ilości danych z dysku i alokacji od kilku GB do kilkudziesięciu GB pamięci zarządzanej. Wywołaj raz i sam buforuj wynik; nigdy nie wywołuj co klatkę.
  • vertices / boundsLocal są w lokalnym układzie współrzędnych obiektu, więc dołączenie GameObject wizualizacji pod Renderer.Transform wyrównuje go automatycznie.
  • Liczba wierzchołków pojedynczego bloku może przekroczyć 65535 (zmierzone maksimum 390 656); przed zbudowaniem własnego Mesh musisz ustawić mesh.indexFormat = IndexFormat.UInt32, w przeciwnym razie geometria zostanie uszkodzona.
  • Kolejność nawijania trójkątów siatki kolizji nie jest gwarantowana jako spójna; przykład wyłącza odcinanie ścian tylnych (_Cull = Off) dla pełnego wyświetlenia.

Dołącz skrypt do GameObject LCCRenderer, a w Inspektorze przeciągnij GameObject LCCManager na właściwość Manager i wypełnij File Path.

Następnie utwórz Przycisk i powiąż metodę onGetAllMeshes() z jego zdarzeniem onClick.

Po uruchomieniu kliknij Przycisk, aby zobaczyć wszystkie siatki kolizji w scenie.

Synchronicznie pobierz wszystkie dane siatki i zwizualizuj je

Wizualizuj wszystkie dane siatki kolizji po kliknięciu przycisku

Pobieranie danych siatki na żądanie

Pobieranie na żądanie oznacza odczytywanie tylko określonej części danych siatki zamiast odczytywania wszystkich danych siatki naraz, aby uniknąć dużego narzutu I/O i pamięci. Poniżej jako przykład użyto odczytywania bloków siatki w pewnym zakresie wokół kamery.

Najpierw użyj GetMeshBlockInfos(), aby uzyskać metadane wszystkich bloków (w tym prostokąty ograniczające w przestrzeni świata), oblicz odległość od środka kamery renderującej do powierzchni BBOX bloku we współrzędnych świata, odfiltruj id bloków o odległości mniejszej niż 100 m, a następnie użyj sparametryzowanego GetMeshes(ids), aby odczytać tylko te bloki.

Dodaj następujące metody do Meshes.cs:

public void onRequireMeshes()
{
    if (!m_isLoaded || m_renderer == null)
    {
        Debug.LogWarning("[Meshes] Dane nie zostały jeszcze załadowane; nie można pobrać siatek.");
        return;
    }

    // 1. Odczyt zerowy I/O wszystkich metadanych bloków, filtrowanie bloków w promieniu 100 m według odległości kamera-powierzchnia BBOX
    var infos = m_renderer.GetMeshBlockInfos();
    if (infos == null || infos.Count == 0)
    {
        Debug.LogWarning("[Meshes] Nie pobrano informacji o siatkach (dane mogą nie zawierać koliderów).");
        return;
    }

    Vector3 camPos = m_camera.transform.position;
    var ids = new List<string>();
    foreach (var info in infos)
    {
        // Gdy kamera jest wewnątrz BBOX, ClosestPoint zwraca samą kamerę, a odległość wynosi 0, więc blok również jest wybierany
        if (calculateMinDistanceToAABB(info.boundsWorld, camPos) < 100f)
            ids.Add(info.id);
    }

    if (ids.Count == 0)
    {
        Debug.LogWarning("[Meshes] Brak bloków siatki w promieniu 100 m od kamery.");
        return;
    }

    // 2. Odczytaj tylko przefiltrowane bloki według id i zwizualizuj je
    var meshes = m_renderer.GetMeshes(ids);
    if (meshes == null || meshes.Count == 0)
    {
        Debug.LogWarning("[Meshes] Nie udało się pobrać siatek według id.");
        return;
    }

    VisualizeMeshes(meshes);
    Debug.Log($"[Meshes] Wybrano {ids.Count} bloków w promieniu 100 m od kamery, zwizualizowano {meshes.Count}.");
}

// Oblicz najbliższą odległość od punktu do powierzchni AABB (zwraca 0, gdy punkt jest wewnątrz AABB)
private static float calculateMinDistanceToAABB(Bounds _aabb, Vector3 _point)
{
    Vector3 p = _aabb.ClosestPoint(_point);
    return Vector3.Distance(p, _point);
}

Uwaga:

  • GetMeshBlockInfos() to operacja zero-I/O, zero-analizy, która odczytuje tylko metadane już w pamięci; możesz ją wywoływać swobodnie.
  • Filtr odległości używa boundsWorld (prostokąt ograniczający w przestrzeni świata), a środek kamery przyjmuje Camera.transform.position.
  • Użyj Bounds.ClosestPoint, aby obliczyć najbliższą odległość od punktu do powierzchni AABB: gdy kamera jest wewnątrz BBOX, ClosestPoint zwraca samą kamerę, a odległość wynosi 0, więc ten blok również jest wybierany.
  • Promień filtra 100f (jednostka: metry) w przykładzie można dostosować w razie potrzeby.

Utwórz Przycisk i powiąż onRequireMeshes() z jego zdarzeniem onClick.

Po uruchomieniu kliknij przycisk, aby zobaczyć bloki siatki w promieniu 100 m od kamery.

Pobierz dane siatki w promieniu 100 m od kamery na żądanie i zwizualizuj je

Pobierz dane siatki w promieniu 100 m od kamery na żądanie

Kluczowe kroki

Kluczowe kroki pobierania i wizualizacji danych siatki sceny:

  1. Ukończ wstępną konfigurację, taką jak SetPlatformType, i załaduj scenę LCC przez GetRender + Load.
  2. Potwierdź, że dane przenoszą dane siatki kolizji (formaty natywne lub dane rekonstruowane we wcześniejszych wersjach mogą ich nie zawierać).
  3. Pełne pobieranie: wywołaj GetMeshes(), aby odczytać wszystkie bloki naraz, i sam buforuj wynik, aby uniknąć powtarzanych wywołań.
  4. Pobieranie na żądanie: najpierw użyj GetMeshBlockInfos(), aby uzyskać prostokąty ograniczające bloków w świecie, odfiltruj id według odległości kamery, a następnie użyj GetMeshes(ids), aby odczytać tylko docelowe bloki.
  5. Przy wizualizacji dołącz GameObject pod Renderer.Transform, aby wyrównać z lokalnymi współrzędnymi, i zbuduj Mesh z IndexFormat.UInt32.

API pobierania siatki

Poniższe metody znajdują się wszystkie w Renderer; należy je wywoływać po zakończeniu ładowania sceny, a dane muszą przenosić dane siatki kolizji.

GetMeshBlockInfos

// Wartość zwracana: List<LCCMeshBlockInfo> → metadane bloków siatki kolizji; null, gdy nie ma siatki lub jest niedostępna
public List<LCCMeshBlockInfo> GetMeshBlockInfos()

Zwraca metadane wszystkich bloków siatki kolizji (id, prostokąt ograniczający świata/lokalny, liczba wierzchołków, liczba ścian). Zero-I/O i zero-analizy; odczytuje tylko metadane już w pamięci.

Przykład użycia:

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

Uwagi:

  • Tylko PC / Mac + lokalne źródło danych; tylko wątek główny.
  • Typowe użycie: najpierw użyj jej, aby uzyskać BBox świata każdego bloku, wybierz potrzebne id, a następnie wywołaj GetMeshes(ids), aby odczytać tylko te bloki.
  • Format natywny (PLY / SOG / SPZ / splat / ksplat) nie ma siatki i zwraca null (co jest normalne i nie jest rejestrowane).

GetMeshes

// Wartość zwracana: List<LCCMeshBlock> → wszystkie dane geometrii siatki kolizji; null, gdy niedostępna
public List<LCCMeshBlock> GetMeshes()

Synchronicznie zwraca dane geometrii wszystkich bloków siatki kolizji (wierzchołki + indeksy trójkątów, w lokalnym układzie współrzędnych). To ciężka operacja (synchroniczny odczyt z dysku + analiza + alokacja); wywołaj raz i sam buforuj wynik, nigdy co klatkę.

Przykład użycia:

var meshes = renderer.GetMeshes();
foreach (var block in meshes)
{
    // block.vertices / block.triangles są w lokalnym układzie współrzędnych obiektu
}

Uwagi:

  • Tylko PC / Mac + lokalne źródło danych; tylko wątek główny.
  • Dla dużych scen użyj GetMeshBlockInfos() do wyboru id + GetMeshes(ids) zamiast tego, aby uniknąć alokacji ogromnej ilości pamięci zarządzanej naraz.
  • Liczba wierzchołków pojedynczego bloku może przekroczyć 65535; przed zbudowaniem własnego Mesh musisz ustawić mesh.indexFormat = IndexFormat.UInt32.

GetMeshes (według id)

// Wartość zwracana: List<LCCMeshBlock> → dane geometrii siatki kolizji dla określonych id; null, gdy brak trafień
public List<LCCMeshBlock> GetMeshes(IEnumerable<string> _ids)

Synchronicznie zwraca dane geometrii bloków siatki kolizji o określonych id (lokalny układ współrzędnych). Id są dostarczane przez GetMeshBlockInfos().

ParametrTypOpis
_idsIEnumerable<string>Kolekcja docelowych id bloków (dostarczona przez GetMeshBlockInfos())

Przykład użycia:

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

Uwagi:

  • Nieznane id są pomijane z ostrzeżeniem; zduplikowane id są deduplikowane (nie są wielokrotnie odczytywane z dysku); kolejność zwracania to kolejność przechodzenia id (po deduplikacji).
  • Przekazanie null, pustego wejścia lub brak trafień zwraca null z ostrzeżeniem.
  • Pozostałe ograniczenia są takie same jak w GetMeshes().

Struktura danych LCCMeshBlockInfo

public struct LCCMeshBlockInfo {
    public string id;           // id węzła, odpowiadające LCCMeshBlock.id
    public Bounds boundsWorld;  // AABB w przestrzeni świata, używany do wyboru id
    public Bounds boundsLocal;  // AABB w przestrzeni lokalnej
    public int vertexCount;     // liczba wierzchołków
    public int faceCount;       // liczba ścian
}
PoleTypOpis
idstringId węzła, odpowiadające LCCMeshBlock.id
boundsWorldBoundsAABB w przestrzeni świata, używany do wyboru id
boundsLocalBoundsAABB w przestrzeni lokalnej
vertexCountintLiczba wierzchołków
faceCountintLiczba ścian

Struktura danych LCCMeshBlock

public struct LCCMeshBlock {
    public string id;           // id węzła
    public Vector3[] vertices;  // wierzchołki, lokalny układ współrzędnych
    public int[] triangles;     // indeksy trójkątów, 3 na trójkąt
    public Bounds boundsLocal;  // granice w tej samej przestrzeni co wierzchołki
}
PoleTypOpis
idstringId węzła
verticesVector3[]Tablica wierzchołków, lokalny układ współrzędnych obiektu
trianglesint[]Indeksy trójkątów, 3 na trójkąt
boundsLocalBoundsGranice w tej samej przestrzeni co wierzchołki

Uwaga:

vertices / boundsLocal są oba w lokalnym układzie współrzędnych obiektu. Aby je zwizualizować w Unity, dołącz GameObject zawierający Mesh pod Renderer.Transform, aby wyrównać automatycznie. Liczba wierzchołków pojedynczego bloku może przekroczyć 65535; przed zbudowaniem własnego Mesh musisz ustawić mesh.indexFormat = IndexFormat.UInt32.