ALCCActorBase:3DGS Actor 基類
所有 LCC Actor 的基類,負責把 3DGS 數據放進關卡。ALCCActor、ALCC2Actor、ASogActor、ASpzActor、APlyActor 都繼承自它。
| 模塊 | LCC4UnrealRuntime |
| 頭文件 | LCCActorBase.h |
| 父類 | AActor |
| 藍圖 | 可繼承(BlueprintType) |
#include "LCCActorBase.h"
Actor 本身不做渲染,它持有一個 ULCCComponentBase 並把加載類操作轉發過去。渲染參數、顏色、性能、碰撞全部在 Component 上,通過 GetLCCComponent 獲取。
縮放限制:LCC 系列 Actor 只支持等比縮放。 不要使用帶負值的縮放(如
(-1, 1, 1))或各軸不等的縮放(如(2, 1, 3)),否則會出現渲染異常,畫面上通常表現為一道線。
Properties
| 屬性 | 類型 | 訪問 | 說明 |
|---|---|---|---|
DefaultSceneRoot | ULCCFocusRootComponent* | 只讀 | 不可見的根組件。存在的唯一目的是給編輯器視口一個固定的小包圍盒,按 F 聚焦時不會因為場景巨大而失效。 |
LCCComponent | ULCCComponentBase* | 只讀 | 真正幹活的組件。子類構造時決定它的具體類型。 |
兩個屬性都是 VisibleAnywhere + BlueprintReadOnly,不能在細節面板或藍圖裏替換,只能讀。
Methods
Load
UFUNCTION(BlueprintCallable, Category = "XGrids")
virtual void Load(const FString& String) const;
按路徑加載 3DGS 數據。這是運行時加載場景的主入口。
參數:
| 參數 | 類型 | 說明 |
|---|---|---|
String | const FString& | 數據文件路徑。支持絕對路徑(D:/Data/Tower/Tower.lcc)和相對路徑。相對路徑以工程 Content 目錄為基準,例如 Tower/Tower.lcc 指向 Content/Tower/Tower.lcc。 |
不同子類接受的路徑形態不同:
| Actor | 傳入什麼 |
|---|---|
ALCCActor | LCC1 的 .lcc 文件路徑。文件名不固定,同目錄必須有 data.bin 與 index.bin |
ALCC2Actor | LCC2 的 .lcc2 文件路徑。文件名不固定 |
ASogActor | .sog 文件路徑 |
ASpzActor | .spz 文件路徑 |
APlyActor | .ply 文件路徑,擴展名不是 .ply 會被直接拒絕 |
注:
.lcc與.lcc2的文件名由數據生產端決定,不要假設它一定叫meta.lcc。判斷格式請看擴展名,或用 ULCCUtilLibrary::DetermineFileFormat。
用法要點:
- 方法是
const,因為狀態變更發生在LCCComponent內部,不修改 Actor 自身。 - 加載是異步的。調用返回時數據還沒就緒,當前版本需要用
GetLCCComponent()->CheckIfLoaded()輪詢。後續版本會提供加載完成回調,屆時不必再輪詢。 CheckIfLoaded()返回true只代表元信息與索引已建立、可以安全讀參數配參數,Splat 數據仍在按視角流式加載,畫面會繼續補齊。- 想在關卡裏預設路徑而不寫代碼,直接在細節面板設 Component 的
DefaultLoadPath,運行時會自動加載。 - 傳入空路徑等價於卸載當前數據。
- 傳入的路徑與當前已加載的路徑相同時,調用會直接返回,不會重新加載。
- 路徑可疑時先用 ULCCUtilLibrary::CheckLCCValid 校驗,避免加載失敗只在日誌裏報錯。
C++ 示例:
#include "LCCActor.h"
#include "LCCComponentBase.h"
void AMyGameMode::SpawnLCCScene()
{
// 生成 Actor
ALCCActor* LCCActor = GetWorld()->SpawnActor<ALCCActor>(
ALCCActor::StaticClass(),
FVector::ZeroVector,
FRotator::ZeroRotator);
if (!LCCActor)
{
return;
}
// 絕對路徑加載
LCCActor->Load(TEXT("D:/Data/Tower/Tower.lcc"));
// 或相對 Content 目錄加載
// LCCActor->Load(TEXT("Tower/Tower.lcc"));
}
等待加載完成後再操作。當前版本沒有回調,只能輪詢:
void AMyActor::LoadAndConfigure()
{
LCCActor->Load(TEXT("D:/Data/Tower/Tower.lcc"));
// 每 0.2 秒檢查一次是否就緒
GetWorld()->GetTimerManager().SetTimer(
LoadCheckTimer, this, &AMyActor::OnCheckLoaded, 0.2f, true);
}
void AMyActor::OnCheckLoaded()
{
ULCCComponentBase* Component = LCCActor->GetLCCComponent();
if (!Component || !Component->CheckIfLoaded())
{
return;
}
GetWorld()->GetTimerManager().ClearTimer(LoadCheckTimer);
// 元信息已就緒,可以安全配參數(Splat 數據仍在流式加載)
Component->SetRenderMode(ERenderMode::Splatting);
UE_LOG(LogTemp, Log, TEXT("Total splats: %d"), Component->GetSplatNumber());
}
文本藍圖:
[Event BeginPlay]
│
▼
[Spawn Actor from Class]
Class = LCCActor
Spawn Transform = (默認)
│ Return Value ──┐
▼ │
[Load] ◀────────────────┘
Target = (上一步的 Return Value)
String = "D:/Data/Tower/Tower.lcc"
UnLoad
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Actions",
meta = (DisplayName = "UnLoad", DisplayPriority = 2))
virtual void UnLoad();
卸載當前數據並釋放資源,包括 GPU 緩衝、節點緩存和碰撞體。
用法要點:
- 帶
CallInEditor,細節面板 Actions 分類下有一個 UnLoad 按鈕,編輯器裏可直接點。 - 卸載後 Actor 還在關卡裏,只是不再渲染任何內容。再調
Load可以加載別的數據。 - 切換大場景時先
UnLoad再Load,避免兩份數據同時佔用顯存。 - 不需要手動在
EndPlay裏調用,Component 銷毀時會自行清理。
C++ 示例,切換場景:
void AMyManager::SwitchScene(const FString& NewPath)
{
// 先釋放舊數據,防止顯存峰值疊加
LCCActor->UnLoad();
LCCActor->Load(NewPath);
}
Refresh
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Actions",
meta = (DisplayPriority = 3))
virtual void Refresh();
重新加載當前數據。內部實現是先 UnLoad() 再 Load(),所以開銷與一次完整重新加載相當,不是輕量操作。
之所以能重新加載「當前」數據,是因為加載成功時會把路徑同步寫入 DefaultLoadPath,Refresh 取的就是這個值。
用法要點:
- 磁盤上的數據文件被替換後,用它重新讀盤。
- 想讓渲染結果重新計算一遍,不要用它。改屬性走 Setter 會自動觸發更新;確實需要推一幀的話用 Component 的
ForceUpdate(),那個才是輕量的。 - 重新加載後需要重新等待就緒,之前對 Component 設置的運行時參數需要視情況重設。
- 編輯器細節面板 Actions 分類下有對應按鈕。
GetLCCComponent
UFUNCTION(BlueprintCallable, Category = "XGrids")
ULCCComponentBase* GetLCCComponent() const;
返回 Actor 內部的 Component 指針。這是訪問全部渲染能力的入口。
返回值:ULCCComponentBase*。正常情況下非空,Actor 構造時就已創建。
用法要點:
- 需要子類專屬能力時向下轉型。
ALCCActor拿到的是ULCCComponent,ALCC2Actor與三種單文件 Actor 拿到的是ULCC2Component。 - 轉型一律用
Cast<>,不要用 C 風格強轉。類型不匹配時Cast返回nullptr,方便早退。
C++ 示例,改基類通用參數:
ULCCComponentBase* Component = LCCActor->GetLCCComponent();
if (Component)
{
Component->SetSplatScale(0.8f);
Component->SetGlobalAlpha(1.0f);
}
向下轉型訪問 LCC2 專屬參數:
#include "LCC2Component.h"
ULCC2Component* LCC2Comp = Cast<ULCC2Component>(LCC2Actor->GetLCCComponent());
if (LCC2Comp)
{
LCC2Comp->SetSHBands(2);
LCC2Comp->NormalMode = ELCC2NormalGenerationMode::Hemispherical;
}
文本藍圖,藍圖裏轉型用 Cast To 節點:
[Get LCC Component]
Target = LCCActor
│ Return Value ──┐
▼ │
[Cast To LCC2Component] ◀┘
Object = (上一步的 Return Value)
│ Cast Succeeded
▼
[Set SH Bands]
Target = (Cast 輸出的 As LCC2 Component)
In SH Bands = 2
SelectFile
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Actions",
meta = (DisplayName = "Load", DisplayPriority = 1))
virtual void SelectFile();
彈出系統文件選擇對話框,選中後立即加載。
用法要點:
- 細節面板上顯示為 Load 按鈕,是編輯器裏加載數據最常用的入口。
- 各子類重寫了它,用於過濾對應擴展名:
ASogActor只列.sog,ASpzActor只列.spz,APlyActor只列.ply。 - 依賴編輯器的對話框能力,打包後的運行時不要用它,運行時請用 Load。
DebugNodeBound
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Actions",
meta = (DisplayName = "Debug Node Bound", DisplayPriority = 4))
void DebugNodeBound();
開關八叉樹節點邊界的可視化,用線框盒畫出當前加載的節點。
盒體顏色對應節點 Level:紅、橙、黃、綠、藍、紫依次遞進,紅色是最低 Level(細節最高),白色是最高 Level(細節最低)。
用法要點:
- 排查 LOD 問題時用。看到遠處還在渲染紅色盒子,說明
LevelFactor偏小或StartLevel設得太低,性能被浪費了。 - 反過來,近處全是高 Level 的冷色盒子,說明細節被壓得過狠,畫面會發糊。
- 再調用一次關閉,是開關式的。
- 只在編輯器與開發構建下有效。
Stats
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Actions",
meta = (DisplayPriority = 5))
void Stats();
開關渲染統計面板,等價於控制台執行 stat XGrids。
面板給出當前渲染的 Splat 數量、加載中的節點數、各階段耗時。調性能參數時對照它看效果,比憑感覺調靠譜。
ShowCollision
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Actions",
meta = (DisplayName = "Show Collision", DisplayPriority = 6))
void ShowCollision();
開關碰撞線框可視化,等價於控制台執行 r.xgrids.DrawCollision。
用法要點:
- 前提是數據本身帶碰撞,且 Component 的
bEnableCollision已打開,否則什麼都不會顯示。 - 角色穿模、射線打不中的時候先開這個,確認碰撞體到底有沒有加載出來。
- 碰撞是按距離流式加載的,遠處沒有線框屬於正常表現,受
Performance.CollisionLoadMaxDistance控制。
ShowFPS
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Actions",
meta = (DisplayName = "Show FPS", DisplayPriority = 7))
void ShowFPS();
開關幀率顯示,等價於控制台執行 stat fps。配合 Stats 一起用,一邊看幀率一邊看 Splat 數量。
Complete Example
運行時加載一個 LCC 場景,就緒後配置渲染參數:
// MyLCCLoader.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyLCCLoader.generated.h"
UCLASS()
class AMyLCCLoader : public AActor
{
GENERATED_BODY()
public:
/** 要加載的數據路徑,絕對路徑或相對 Content 目錄 */
UPROPERTY(EditAnywhere, Category = "MyLCC")
FString ScenePath = TEXT("Tower/Tower.lcc");
protected:
virtual void BeginPlay() override;
private:
void OnLoadCheck();
UPROPERTY()
TObjectPtr<class ALCCActor> LCCActor;
FTimerHandle LoadCheckTimer;
};
// MyLCCLoader.cpp
#include "MyLCCLoader.h"
#include "LCCActor.h"
#include "LCCComponentBase.h"
void AMyLCCLoader::BeginPlay()
{
Super::BeginPlay();
LCCActor = GetWorld()->SpawnActor<ALCCActor>(
ALCCActor::StaticClass(), GetActorTransform());
if (!LCCActor)
{
return;
}
LCCActor->Load(ScenePath);
GetWorld()->GetTimerManager().SetTimer(
LoadCheckTimer, this, &AMyLCCLoader::OnLoadCheck, 0.2f, true);
}
void AMyLCCLoader::OnLoadCheck()
{
ULCCComponentBase* Component = LCCActor ? LCCActor->GetLCCComponent() : nullptr;
if (!Component || !Component->CheckIfLoaded())
{
return;
}
GetWorld()->GetTimerManager().ClearTimer(LoadCheckTimer);
// 元信息已就緒,按需要配置
Component->SetRenderMode(ERenderMode::Splatting);
Component->SetSplatScale(0.9f);
Component->SetMaxDistance(200); // 最遠渲染 200 米
Component->SetMaxSplatNum(1500); // 每幀最多 1500 萬個 Splat
Component->SetLCCCollisionEnable(true);
UE_LOG(LogTemp, Log, TEXT("LCC ready, splat number: %d"),
Component->GetSplatNumber());
}
See Also
- ULCCComponentBase:渲染與性能參數都在這裏
- SOG / SPZ / PLY Actors:三種單文件格式的專用 Actor
- ULCCUtilLibrary:加載前的路徑與格式校驗