Skip to content

LCC

参考サイト

LCCUnitySDKとMRTKを使ってQuest3でLCCモデルを表示する その1(プロジェクト設定とLCC Unity SDKのインポート) - MRが楽しい

試作1

1.LCCUnitySDKダウンロード・適応

下記サイトよりダウンロード、展開。

DEVELOPER

Package ManagerのInstall package from disk…を選択し、先ほど展開したフォルダ内package.jsonを指定。

2.シーン作成

シーン内にLCCManager(空のゲームオブジェクト)を作製し、LCCManager(スクリプト)をアタッチ。

image.png

新たなスクリプト(LCCQuestLoader )を作製 (公式でLCCRendererというスクリプトがあるが、それをそのまま使用するとQuest3で表示できなそう)

jsx
using UnityEngine;
using LCCCore;
using System.IO;
using System.Collections;
using UnityEngine.Networking;

#if UNITY_EDITOR
using UnityEditor;
#endif

/// <summary>
/// StreamingAssetsフォルダ内にあるLCCデータをQuest 3(Android)等の環境で適切にロードするためのスクリプトです。
/// エディタ上で meta.lcc 等のファイルをアタッチすることで自動的に相対パスを計算します。
/// </summary>
public class LCCQuestLoader : MonoBehaviour
{
    [Header("LCC Manager Reference")]
    public LCCManager lccManager;

    [Header("LCC Target File (meta.lcc or .bin)")]
    [Tooltip("StreamingAssetsフォルダ配下にある meta.lcc や data.bin などのファイルをドラッグ&ドロップしてください。")]
    public UnityEngine.Object targetLccFile;

    [Header("Computed Path (Read Only)")]
    [Tooltip("StreamingAssetsからの相対パスが自動計算されます。")]
    public string relativePath = "";

    private LCCCore.Renderer lccRenderer;
    private string targetAbsolutePath = "";

    void Start()
    {
        if (lccManager == null)
        {
            Debug.LogError("[LCCQuestLoader] LCCManager is not assigned!");
            return;
        }

        if (string.IsNullOrEmpty(relativePath))
        {
            Debug.LogError("[LCCQuestLoader] LCC relative path is empty. Please assign targetLccFile in the inspector.");
            return;
        }

        lccRenderer = lccManager.GetRender(this.transform);

        // Android環境では StreamingAssets 以下のファイルに直接ファイルパスでアクセスできないため、
        // Application.persistentDataPath にファイルをコピーしてから読み込みます。
#if UNITY_ANDROID && !UNITY_EDITOR
        StartCoroutine(PrepareAndLoadOnAndroid());
#else
        // PC / Editor の場合は StreamingAssets 内のファイルを直接指定可能
        // Windows環境でのスラッシュ/バックスラッシュ混在エラーを防ぐため、フルパスに正規化します
        targetAbsolutePath = Path.GetFullPath(Path.Combine(Application.streamingAssetsPath, relativePath));
        LoadLCCData(targetAbsolutePath, PlatformType.PC);
#endif
    }

    private void LoadLCCData(string filePath, PlatformType platform)
    {
        Debug.Log($"[LCCQuestLoader] Loading LCC data from: {filePath}");
        
        // 既存のSDKのLoadメソッドを呼び出し
        lccRenderer.Load(filePath, platform, OnLoadCallback);
        lccRenderer.SetDebugMode(true);
    }

    private void OnLoadCallback()
    {
        Debug.Log("[LCCQuestLoader] LCC data successfully loaded!");
    }

    /// <summary>
    /// Android (Quest) 環境で StreamingAssets のファイルを persistentDataPath にコピーするコルーチン
    /// </summary>
    private IEnumerator PrepareAndLoadOnAndroid()
    {
        string persistentDirPath = Path.Combine(Application.persistentDataPath, "LCC_Extracted");
        
        // 元の StreamingAssets 内のディレクトリ構造を再現するための相対ディレクトリパス
        string fileDirName = Path.GetDirectoryName(relativePath);
        string targetDirPath = Path.Combine(persistentDirPath, fileDirName);

        if (!Directory.Exists(targetDirPath))
        {
            Directory.CreateDirectory(targetDirPath);
            Debug.Log($"[LCCQuestLoader] Created directory: {targetDirPath}");
        }

        // --- 同梱されているすべての関連ファイル (bin, json, 等) をコピーする必要があります ---
        // LCCデータは meta.lcc だけでなく data.bin や shcoef.bin 等の複数ファイルから構成されるため、
        // フォルダ内の全構成ファイルをコピーする処理が必要ですが、UnityのWebRequestでは
        // StreamingAssets内の「ファイル一覧」を取得することができません。
        // 
        // 解決策として、LCCフォルダ以下で必要となる標準的なファイル名のリストを
        // コピー対象として試行します。
        
        string[] commonFilesToCopy = new string[]
        {
            relativePath, // アタッチされたメインのファイル (例: meta.lcc)
            Path.Combine(fileDirName, "data.bin").Replace("\\", "/"),
            Path.Combine(fileDirName, "shcoef.bin").Replace("\\", "/"),
            Path.Combine(fileDirName, "environment.bin").Replace("\\", "/"),
            Path.Combine(fileDirName, "index.bin").Replace("\\", "/"),
            Path.Combine(fileDirName, "attrs.lcp").Replace("\\", "/")
        };

        bool allSuccess = true;

        foreach (string fileRelativePath in commonFilesToCopy)
        {
            string sourcePath = Path.Combine(Application.streamingAssetsPath, fileRelativePath);
            string destPath = Path.Combine(Application.persistentDataPath, "LCC_Extracted", fileRelativePath);

            // 既に存在している場合はスキップ(上書き更新が必要な場合は要変更)
            if (File.Exists(destPath))
            {
                Debug.Log($"[LCCQuestLoader] File already exists, skipping copy: {destPath}");
                continue;
            }

            Debug.Log($"[LCCQuestLoader] Copying file: {sourcePath} to {destPath}");

            using (UnityWebRequest request = UnityWebRequest.Get(sourcePath))
            {
                yield return request.SendWebRequest();

                if (request.result == UnityWebRequest.Result.Success)
                {
                    File.WriteAllBytes(destPath, request.downloadHandler.data);
                    Debug.Log($"[LCCQuestLoader] Successfully copied to: {destPath}");
                }
                else
                {
                    // 一部の補助ファイルが存在しない場合はエラーとせずにスキップ
                    Debug.LogWarning($"[LCCQuestLoader] Failed to copy {sourcePath}: {request.error}");
                }
            }
        }

        // 対象ファイルの新しい(コピー先)絶対パスを決定
        string copiedMainFilePath = Path.Combine(Application.persistentDataPath, "LCC_Extracted", relativePath);
        
        if (File.Exists(copiedMainFilePath))
        {
            // SDK側にAndroid用プラットフォーム指定が存在するか不明なため、一旦 PlatformType.Android か PCか選択
            // SDKがAndroidをサポートしている場合は PlatformType.Android 等を指定
            // LCC SDK 1.4.7 の仕様に合わせて PlatformType.PC にしておくケースが多いです。
            LoadLCCData(copiedMainFilePath, PlatformType.PC);
        }
        else
        {
            Debug.LogError($"[LCCQuestLoader] Critical error: Main LCC file not found after extraction: {copiedMainFilePath}");
        }
    }

#if UNITY_EDITOR
    private void OnValidate()
    {
        if (targetLccFile != null)
        {
            // Unityの仕様上、OnValidate内で直ちにAssetDatabase等のAPIを呼ぶとエラーになることがあるため、遅延呼び出しを行います。
            EditorApplication.delayCall += () =>
            {
                if (this == null || targetLccFile == null) return; // 破棄されている場合は処理しない

                string assetPath = AssetDatabase.GetAssetPath(targetLccFile);
                
                // Assets/StreamingAssets/ が含まれているかチェック
                string streamingAssetsPrefix = "Assets/StreamingAssets/";
                if (assetPath.StartsWith(streamingAssetsPrefix))
                {
                    // StreamingAssets 以降の相対パスを切り出し
                    relativePath = assetPath.Substring(streamingAssetsPrefix.Length);
                }
                else
                {
                    Debug.LogWarning("[LCCQuestLoader] 選択されたファイルは StreamingAssets フォルダ内にありません。正しくロードするには StreamingAssets 配下に配置してください。");
                    relativePath = "";
                }
            };
        }
        else
        {
            relativePath = "";
        }
    }
#endif

    [ContextMenu("Render (need a little time to load data)")]
    public void Render()
    {    
        if(lccRenderer == null && lccManager != null)
            lccRenderer = lccManager.GetRender(this.transform);

        if(lccRenderer != null && !string.IsNullOrEmpty(targetAbsolutePath))
        {
            lccRenderer.Load(targetAbsolutePath, PlatformType.PC, OnLoadCallback);
        }
    }

    [ContextMenu("unRender")]
    public void unRender()
    {
        lccRenderer?.Dispose();
    }
}

LCCRenderer(空のゲームオブジェクト)を作製し、先ほど作成したスクリプトをアタッチ。

LCCManagerには同じシーンのLCCManagerを指定。

image.png

3.LCCサンプルデータ

下記サイトよりLCCのサンプルデータを保存。

DEVELOPER

保存したデータを Assets/StreamingAssets/LCC/HuanxiuGardenなどのようにStreamingAssets内に保存。

image.png

4.LCCデータの指定

LCCQuestLoader のLCCTargetFileに保存したLCCデータのmeta.lccを指定。

image.pngimage.png

この状態でエディター実行をするとLCC規格の3DGSを表示することができます。

ただしこの方法ではQuest3での表示がうまくいかなかった。

最初にサンプルの HuanxiuGardenを試してみたところ、アプリが落ちてしまった。 次に Big Mirrorを試してみたところ、多少のガウシアンが見えたが、なにかの景色という感じではなかった。


Author: 水上 | Source: 水上\LCC 30faba435ee780008c22d9df85a1b1e9.md