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

    • PortalCam P1

      • Product Overview
      • Basic Operation
      • Using the LCC Scan App
      • Maintenance and Care
      • FAQ
  • Lixel K Series

    • Lixel K1

      • Product Overview
      • Basic Operation
      • Device Activation and Connection
      • Scanning Workflow
      • Acquire Point Cloud Data with Absolute Coordinate
      • Map Fusion
      • Route Planning Suggestions for Typical Scenes
      • Precautions
      • FAQ
    • Lixel K2

      • Product Overview
      • Basic Operation
      • Device Activation and Connection
      • Scanning Workflow
      • Acquire Point Cloud Data with Absolute Coordinate
      • Map Fusion
      • Route Planning Suggestions for Typical Scenes
      • Precautions
      • FAQ
  • Lixel L Series

    • Lixel L2 Pro

      • Product Overview
      • Basic Operation
      • Device Activation and Connection
      • Scanning Workflow
      • Acquire Point Cloud Data with Absolute Coordinate
      • Measure Point
      • Appendix
      • FAQ
    • Lixel L3

      • Product Overview
      • Basic Device Functions
      • Device Activation and Connection
      • Data Capture
      • Capturing Absolute Coordinates
      • Appendix
  • Accessories

    • Stick Light

      • Installation Guide
      • Basic Operation
      • FAQ
  • Lixel Studio

    • Version and Copyright
    • Installation and Activation
    • Software Interface
    • File Operations
    • Project Processing
    • Tools
    • 2D Drawing
    • Applications
    • Settings
    • Device Connection
    • FAQ
  • Lixel CyberColor

    • LCC Studio

      • Getting Started
      • Version and Updates
      • Download and Installation
      • Interface Overview and Navigation
      • Before Reconstruction
      • Model Reconstruction
      • Single Model Reconstruction
      • Map Fusion
      • Aerial-Ground Fusion
      • Aerial Reconstruction
      • My Models
      • Other Features
      • Settings and Account
      • Converter
      • Video Reconstruction
      • FAQ
    • LCC Scene Editor

      • Version & Updates
      • Account & Login
      • Product Overview & Home
      • Editor Interface
      • Scene Navigation Modes
      • File
      • Settings
      • Edit Operations
      • Window
      • Global Toolbar
      • Assets & Properties
      • Left Toolbar
      • Viewpoints
      • Portal
      • Skybox
      • Annotations
      • Measurement
      • Flythrough
      • Scene Report
      • 3D Layout
      • Mini-Map
      • Preview Mode (Viewer)
      • Help
      • FAQ
      • Spawn Point
    • LCC Model Editor

      • Version and Updates
      • User Guide
      • Overview and Interface
      • File Operations
      • Selectors
      • Editing Models
      • Measurement
      • Color Grading
      • Asset Management
      • Settings and Help
      • FAQ
    • Capture Guide

      • Overview
      • Capture Devices Overview
      • General Capture Principles
      • Indoor Scene Capture
      • Outdoor Scene Capture
      • Large-Scale Capture (Map Fusion)
      • Aerial-Ground Map Fusion Capture
      • Object Capture
      • People Capture
      • Video Reconstruction Capture
      • HD Enhancement
      • Control Points (Lixel P1)
      • FAQ and Troubleshooting
    • LCC Studio Linux

      • Product Overview
      • Architecture Guide
      • Installation and Deployment Guide
      • Container Startup Parameters Guide
      • CodeMeter Offline Licensing Deployment Guide
      • API Reference
      • Appendix - Developer Data Guide
      • Appendix - Error Code List
      • Single-Machine Multi-GPU Cluster Deployment QuickStart
    • Best Practices

      • Merging Multiple Large 3DGS Models
    • Version History
  • Plugin & SDK

    • Unreal

      • Introduction
      • Quick Start - Windows
      • Quick Start - Linux
      • Quick Start - Quest3
      • Editions and Licensing
      • Rendering
      • Tiled Rasterization (Experimental)
      • Visual Settings
      • Normals and Lighting
      • Scene Editing
      • Performance Parameters
      • Performance Guide
      • Third-party and Engine Plugin Integration
      • Proxy Mesh
      • Loading Animation
      • Collision
      • Navigation System Support
      • Single Layer Water Support
      • Localization
      • FAQ
      • Troubleshooting
      • Logging and Diagnostics
      • Contact Us
      • Best Practices

        • Relighting 3DGS with a LixelStudio Mesh
      • API Reference

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

        • 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

      • Unity SDK Introduction
      • Technical Features
      • Development Environment Requirements
      • PC Quick Start Guide
      • Project Settings Optimization Guide
      • Pico 4 Ultra Development Guide
      • Pico 4 Ultra PC Streaming Guide
      • Quest 3 Development Guide
      • Quest 3 PC Streaming Guide
      • OpenXR Development Guide
      • Pico 4 Ultra Business Streaming Guide
      • Glossary
      • FAQ
      • Contact Us
      • Feature Development Guide

        • Multi-Scene Mixed Rendering Development Guide
        • Shadow Receiving Development Guide
        • Real-Time Lighting Development Guide
        • Multi-Camera Rendering Development Guide
        • High-Resolution Image Rendering Development Guide
        • LCC and LCC2 Data Format Compatibility Development Guide
        • Loading Effect Development Guide
        • Mip Rendering Mode Switching Development Guide
        • Edge and Corner Snapping Development Guide
        • Projected Decal Development Guide
        • Video Projected Decal Development Guide
        • Highlight Development Guide
        • Real-Time Clipping Development Guide
        • Color Adjustment Development Guide
        • Getting Scene Mesh Data Development Guide
        • Editor Mode Development Guide
        • Render Performance Parameter Configuration Guide
        • Application and Data Co-packaging Development Guide
        • Collision System Development Guide
        • macOS Development Permission Issue Guide
        • iOS Device Compilation and Packaging Development Guide
      • API Reference

        • LCCManager
        • Renderer
        • Data Types
      • Changelog

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

      • Introduction
      • Rendering Performance Optimization Guide
      • Graphics Configuration
      • FAQ
      • API Reference

        • LCCRender::clearIndexDB
        • LCCRender::dispose
        • LCCRender::load
        • LCCRender::setCamera
        • LCCRender::unload
        • LCCRender::update
        • LCCObject::checkRenderNextFrame
        • LCCObject::clearRenderNextFrame
        • LCCObject::ecef2Prj
        • LCCObject::getBounds
        • LCCObject::getEnvInstancedMesh
        • LCCObject::getInstancedMesh
        • LCCObject::getLodInfos
        • LCCObject::getOriginPosition
        • LCCObject::getProjectionCoordinateSystemInfos
        • LCCObject::hasCollision
        • LCCObject::hasEnvironment
        • LCCObject::hasShcoef
        • LCCObject::intersectsCapsule
        • LCCObject::intersectsRayExt
        • LCCObject::intersectsRayFromOriginExt
        • LCCObject::intersectsSphere
        • LCCObject::lowerToBottom
        • LCCObject::prj2Ecef
        • LCCObject::raiseToTop
        • LCCObject::raycast
        • LCCObject::raycastFromOrigin
        • LCCObject::setAlpha
        • LCCObject::setClipBox
        • LCCObject::setClipPlane
        • LCCObject::setEndLod
        • LCCObject::setLodAutoLevelUp
        • LCCObject::setMaxDistance
        • LCCObject::setMaxNodeSplats
        • LCCObject::setMaxSplats
        • LCCObject::setOriginPosition
        • LCCObject::setPointsColor
        • LCCObject::setRenderState
        • LCCObject::setRotation
        • LCCObject::setScale
        • LCCObject::setSemantic
        • LCCObject::setSemanticColor
        • LCCObject::setSmooth
        • LCCObject::setStartLod
        • LCCObject::setTranslation
        • LCCObject::setVisible
        • LCCObject::togglePointsDisplayMode
        • LCCObject::useEnvironment
        • LCCObject::useShcoef
      • Changelog

        • v0.6.3
        • v0.6.2
        • v0.6.1
        • v0.6.0
        • v0.5.5
        • v0.5.4
        • v0.5.3
        • v0.5.2
        • v0.5.1
        • v0.5.0
        • v0.4.1
        • v0.4.0
        • v0.3.1
        • v0.3.0
        • v0.2.0
    • Lixel OpenAPI

      • Overview
      • Connection & Authentication
      • Message Model & Codec
      • Interface Catalog
      • Real-time Data
      • Project File Access
      • Error Codes
      • Python

        • Quick Start
        • Client Wrapper & Tools
        • FAQ
      • C++

        • Quick Start
        • Client & Examples
        • FAQ

Getting Scene Mesh Data Development Guide

When reconstructing LCC data, the LCC Unity SDK also generates Mesh data used for collisions. Besides collision detection, the SDK provides the ability to extract this Mesh data, convert it into Unity Mesh objects, and visualize it, which is convenient for scenarios such as exporting and baking NavMesh.

The following uses a complete example to describe the steps for synchronously getting all Mesh data and for getting Mesh data of a specified range on demand.

Environment Setup

Create a brand-new scene in Unity. Unity creates Main Camera and Directional Light by default.

Set up Main Camera

Select Main Camera in the Hierarchy window, and set its properties in the Inspector window as shown below.

Set the Main Camera properties

Set the Main Camera properties

Create LCCManager

Right-click an empty area in the Hierarchy window, select Create Empty from the pop-up menu to create an empty GameObject, name it LCCManager, normalize its Transform, and add the LCCManager script to it via Add Component.

Create the LCCManager GameObject and add the LCCManager script

Create the LCCManager GameObject and add the LCCManager script

Create LCCRenderer

Right-click an empty area in the Hierarchy window, select Create Empty to create an empty GameObject, name it LCCRenderer, and set its Transform to Rotation.x = -90 and Scale.x = -1.

Create LCCRenderer and set its Transform

Create LCCRenderer and set its Transform (Rotation.x = -90, Scale.x = -1)

Note:

The Transform settings of the LCCRenderer GameObject (Rotation.x = -90, Scale.x = -1) align it with the LCC data coordinate system. For details on coordinate system alignment, see "LCC Data Coordinate System" in Unity SDK Introduction.

Data prerequisites

The prerequisite for getting Mesh data to take effect is that the scene carries the corresponding collision Mesh data (generated when LCC Studio reconstructs it). In addition, this capability has the following constraints:

  • PC / Mac only, and only local data sources are supported (HTTP data sources are not supported).
  • Can only be called on the main thread.
  • Native-format data (PLY / SOG / SPZ / splat / ksplat) does not carry collision Mesh data; calling the related methods returns null.

Data reconstructed in earlier versions may not contain collision data, in which case calling GetMeshes() returns null.

Synchronously Get All Mesh Data

Synchronously getting all Mesh data means reading the geometry data of all collision Mesh blocks in the scene at once, implemented through the Renderer.GetMeshes() method.

Create a new C# script named Meshes.cs with the following code:

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] load progress: {p:P1}"),
            () =>
            {
                Debug.Log($"[Meshes] file loaded: {m_FilePath}");
                m_isLoaded = true;
                m_manager.SetAutoFov(false);
            },
            e => Debug.LogError($"[Meshes] file load error: {e}")
        );
    }

    public void onGetAllMeshes()
    {
        if (!m_isLoaded || m_renderer == null)
        {
            Debug.LogWarning("[Meshes] Data has not finished loading; cannot get Meshes.");
            return;
        }

        var meshes = m_renderer.GetMeshes();
        if (meshes == null || meshes.Count == 0)
        {
            Debug.LogWarning("[Meshes] No Meshes obtained (data may not contain colliders).");
            return;
        }

        VisualizeMeshes(meshes);
    }

    private void VisualizeMeshes(List<LCCMeshBlock> meshes)
    {
        // Clean up the previous visualization result to avoid piling up
        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 };
            // A single block's vertex count may exceed 65535, so 32-bit indices must be used, otherwise the geometry will be corrupted
            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] Created GameObjects and visualized {meshes.Count} Mesh blocks.");
    }

    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) };
        // Collision mesh triangle winding order is not guaranteed consistent; disable back-face culling for complete display
        mat.SetFloat("_Cull", (float)CullMode.Off);
        return mat;
    }
}

Note:

  • GetMeshes() is a synchronous heavy operation: it synchronously reads from disk, parses, and allocates memory. In large scenes, e.g. a dataset of 262 blocks, it may need to read a large amount of disk data and allocate several GB to tens of GB of managed memory. Call it once and cache the result yourself; never call it every frame.
  • vertices / boundsLocal are in the object local coordinate system, so attaching the visualization GameObject under Renderer.Transform aligns it automatically.
  • A single block's vertex count may exceed 65535 (measured maximum 390,656); before building your own Mesh, you must set mesh.indexFormat = IndexFormat.UInt32, otherwise the geometry will be corrupted.
  • Collision mesh triangle winding order is not guaranteed consistent; the example disables back-face culling (_Cull = Off) for complete display.

Attach the script to the LCCRenderer GameObject, and in the Inspector drag the LCCManager GameObject onto the Manager property and fill in the File Path.

Then create a Button and associate the onGetAllMeshes() method with its onClick event.

After running, click the Button to see all the collision Meshes in the scene.

Synchronously get all Mesh data and visualize it

Visualize all collision Mesh data after clicking the button

Get Mesh Data on Demand

On-demand retrieval means reading only the specified part of the Mesh data rather than reading all Mesh data at once, to avoid large I/O and memory overhead. The following uses reading the Mesh blocks within a certain range around the camera as an example.

First use GetMeshBlockInfos() to get the metadata of all blocks (including world-space bounding boxes), compute the distance from the rendering camera center to the block BBOX surface in world coordinates, filter out the block ids with a distance less than 100 m, and then use the parameterized GetMeshes(ids) to read only those blocks.

Add the following methods to Meshes.cs:

public void onRequireMeshes()
{
    if (!m_isLoaded || m_renderer == null)
    {
        Debug.LogWarning("[Meshes] Data has not finished loading; cannot get Meshes.");
        return;
    }

    // 1. Zero-I/O read of all block metadata, filtering blocks within 100 m by the camera-to-BBOX surface distance
    var infos = m_renderer.GetMeshBlockInfos();
    if (infos == null || infos.Count == 0)
    {
        Debug.LogWarning("[Meshes] No Mesh info obtained (data may not contain colliders).");
        return;
    }

    Vector3 camPos = m_camera.transform.position;
    var ids = new List<string>();
    foreach (var info in infos)
    {
        // When the camera is inside the BBOX, ClosestPoint returns the camera itself and the distance is 0, so it is also selected
        if (calculateMinDistanceToAABB(info.boundsWorld, camPos) < 100f)
            ids.Add(info.id);
    }

    if (ids.Count == 0)
    {
        Debug.LogWarning("[Meshes] No Mesh blocks within 100 m of the camera.");
        return;
    }

    // 2. Read only the filtered blocks by id and visualize them
    var meshes = m_renderer.GetMeshes(ids);
    if (meshes == null || meshes.Count == 0)
    {
        Debug.LogWarning("[Meshes] Failed to get Meshes by id.");
        return;
    }

    VisualizeMeshes(meshes);
    Debug.Log($"[Meshes] Selected {ids.Count} blocks within 100 m of the camera, visualized {meshes.Count}.");
}

// Compute the closest distance from a point to the AABB surface (returns 0 when the point is inside the AABB)
private static float calculateMinDistanceToAABB(Bounds _aabb, Vector3 _point)
{
    Vector3 p = _aabb.ClosestPoint(_point);
    return Vector3.Distance(p, _point);
}

Note:

  • GetMeshBlockInfos() is a zero-I/O, zero-parsing operation that only reads the metadata already in memory; you can call it freely.
  • The distance filter uses boundsWorld (world-space bounding box), and the camera center takes Camera.transform.position.
  • Use Bounds.ClosestPoint to compute the closest distance from a point to the AABB surface: when the camera is inside the BBOX, ClosestPoint returns the camera itself and the distance is 0, so that block is also selected.
  • The filter radius 100f (unit: meters) in the example can be adjusted as needed.

Create a Button and associate onRequireMeshes() with its onClick event.

After running, click the button to see the Mesh blocks within 100 m of the camera.

Get Mesh data within 100 m of the camera on demand and visualize it

Get Mesh data within 100 m of the camera on demand

Key Steps

The key steps for getting and visualizing scene Mesh data:

  1. Complete the pre-configuration such as SetPlatformType, and load the LCC scene via GetRender + Load.
  2. Confirm the data carries collision Mesh data (native formats or data reconstructed in earlier versions may not).
  3. Full retrieval: call GetMeshes() to read all blocks at once and cache the result yourself to avoid repeated calls.
  4. On-demand retrieval: first use GetMeshBlockInfos() to get block world bounding boxes, filter ids by camera distance, then use GetMeshes(ids) to read only the target blocks.
  5. When visualizing, attach the GameObject under Renderer.Transform to align with local coordinates, and build the Mesh with IndexFormat.UInt32.

Mesh-retrieval APIs

The following methods are all on Renderer; they need to be called after the scene finishes loading, and the data must carry collision Mesh data.

GetMeshBlockInfos

// Return value: List<LCCMeshBlockInfo> → collision Mesh block metadata; null when there is no Mesh or unavailable
public List<LCCMeshBlockInfo> GetMeshBlockInfos()

Returns the metadata of all collision Mesh blocks (id, world/local bounding box, vertex count, face count). Zero-I/O and zero-parsing; only reads the metadata already in memory.

Usage example:

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

Notes:

  • PC / Mac only + local data source; main thread only.
  • Typical usage: first use it to get each block's world BBox, pick out the needed ids, then call GetMeshes(ids) to read only those blocks.
  • Native formats (PLY / SOG / SPZ / splat / ksplat) have no Mesh and return null (which is normal and not logged).

GetMeshes

// Return value: List<LCCMeshBlock> → all collision Mesh geometry data; null when unavailable
public List<LCCMeshBlock> GetMeshes()

Synchronously returns the geometry data of all collision Mesh blocks (vertices + triangle indices, in the local coordinate system). This is a heavy operation (synchronous disk read + parse + allocation); call it once and cache it yourself, never every frame.

Usage example:

var meshes = renderer.GetMeshes();
foreach (var block in meshes)
{
    // block.vertices / block.triangles are in the object local coordinate system
}

Notes:

  • PC / Mac only + local data source; main thread only.
  • For large scenes, use GetMeshBlockInfos() to pick ids + GetMeshes(ids) instead, to avoid allocating a huge amount of managed memory at once.
  • A single block's vertex count can exceed 65535; before building your own Mesh, you must set mesh.indexFormat = IndexFormat.UInt32.

GetMeshes (by id)

// Return value: List<LCCMeshBlock> → collision Mesh geometry data for the specified ids; null when no hits
public List<LCCMeshBlock> GetMeshes(IEnumerable<string> _ids)

Synchronously returns the geometry data of the collision Mesh blocks with the specified ids (local coordinate system). The ids are provided by GetMeshBlockInfos().

ParameterTypeDescription
_idsIEnumerable<string>The target block id collection (provided by GetMeshBlockInfos())

Usage example:

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

Notes:

  • Unknown ids are skipped with a warning; duplicate ids are deduplicated (not read from disk repeatedly); the return order is the traversal order of ids (after deduplication).
  • Passing null, an empty input, or no hits returns null with a warning.
  • Other constraints are the same as GetMeshes().

LCCMeshBlockInfo data structure

public struct LCCMeshBlockInfo {
    public string id;           // node id, corresponding to LCCMeshBlock.id
    public Bounds boundsWorld;  // world-space AABB, used to select ids
    public Bounds boundsLocal;  // local-space AABB
    public int vertexCount;     // vertex count
    public int faceCount;       // face count
}
FieldTypeDescription
idstringNode id, corresponding to LCCMeshBlock.id
boundsWorldBoundsWorld-space AABB, used to select ids
boundsLocalBoundsLocal-space AABB
vertexCountintVertex count
faceCountintFace count

LCCMeshBlock data structure

public struct LCCMeshBlock {
    public string id;           // node id
    public Vector3[] vertices;  // vertices, local coordinate system
    public int[] triangles;     // triangle indices, 3 per triangle
    public Bounds boundsLocal;  // bounds in the same space as vertices
}
FieldTypeDescription
idstringNode id
verticesVector3[]Vertex array, object local coordinate system
trianglesint[]Triangle indices, 3 per triangle
boundsLocalBoundsBounds in the same space as vertices

Note:

vertices / boundsLocal are both in the object local coordinate system. To visualize them in Unity, attach the GameObject that holds the Mesh under Renderer.Transform to align automatically. A single block's vertex count can exceed 65535; before building your own Mesh, you must set mesh.indexFormat = IndexFormat.UInt32.