SOG / SPZ / PLY Actors:单文件 3DGS 加载
ASogActor、ASpzActor、APlyActor 三个 Actor 分别对应 .sog、.spz、.ply 三种格式,用于加载单个 3DGS 文件,不需要 LCC 目录结构。它们都继承 ALCCActorBase。
| 模块 | LCC4UnrealRuntime |
| 头文件 | SogActor.h、SpzActor.h、PlyActor.h |
| 父类 | ALCCActorBase |
| 内部 Component | ULCC2Component |
#include "SogActor.h"
#include "SpzActor.h"
#include "PlyActor.h"
三者内部都通过构造虚拟的 FLCC2MetaInfo 复用完整的 LCC2 渲染管线。也就是说,LCC2 的能力对这三种格式同样可用,包括球谐阶数控制、光照法线模式、裁剪剖切、颜色调整。同理,LCC2 管线的限制也一并继承,比如多视口接口不生效。
Supported Formats
| Actor | 扩展名 | 适用场景 |
|---|---|---|
ASogActor | .sog | 压缩格式,体积小,加载快 |
ASpzActor | .spz | 压缩格式 |
APlyActor | .ply | 通用点云 / 3DGS 交换格式,多数训练工具的原生输出 |
各格式的支持范围见介绍 - 支持格式。
Comparison with LCC Directory Formats
.sog / .spz / .ply | LCC / LCC2 目录 | |
|---|---|---|
| 文件组织 | 一个文件 | 元信息文件 + 多个数据分块 |
| LOD 分层 | 无 | 有,按距离流式加载 |
| 适用规模 | 中小场景 | 大场景 |
| 碰撞数据 | 无 | 可有 |
| 分发便利性 | 高,单个文件 | 需要保持目录完整 |
选择依据:数据量不大、想快速验证效果、需要单文件分发时用这三种格式。大场景要靠 LOD 控性能,必须用 LCC / LCC2 目录格式。
Methods
三个 Actor 的公开接口全部继承自 ALCCActorBase,仅重写了下列方法。
SelectFile
// ASogActor
virtual void SelectFile() override; // 对话框只列 .sog
// ASpzActor
virtual void SelectFile() override; // 对话框只列 .spz
// APlyActor
virtual void SelectFile() override; // 对话框只列 .ply
弹出文件选择对话框,各自过滤对应扩展名。细节面板上显示为 Load 按钮。
仅编辑器可用,打包后的运行时请用 Load。
Load(仅 APlyActor 重写)
// APlyActor
virtual void Load(const FString& String) const override;
加载 .ply 文件。扩展名不是 .ply 的路径会被直接拒绝,不会尝试解析。
ASogActor 与 ASpzActor 没有重写 Load,使用基类实现。
Examples
加载单个 .sog 文件
#include "SogActor.h"
#include "LCC2Component.h"
void AMyLoader::LoadSogFile()
{
ASogActor* SogActor = GetWorld()->SpawnActor<ASogActor>(
ASogActor::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator);
if (!SogActor)
{
return;
}
SogActor->Load(TEXT("D:/Data/scene.sog"));
// 内部是 ULCC2Component,LCC2 的能力都能用
ULCC2Component* Comp = Cast<ULCC2Component>(SogActor->GetLCCComponent());
if (Comp)
{
Comp->SetSHBands(2);
Comp->SetSplatScale(0.9f);
}
}
按扩展名自动选择 Actor
每种格式各有专用 Actor,实际项目里通常需要按扩展名分发。
注意不要依赖 DetermineFileFormat 来做这件事,它只识别 .lcc、.splats、.las、.ply,对 .lcc2、.sog、.spz 一律返回 None。直接判断扩展名更可靠:
#include "Tools/LCCUtilLibrary.h"
#include "LCCActor.h"
#include "LCC2Actor.h"
#include "SogActor.h"
#include "SpzActor.h"
#include "PlyActor.h"
ALCCActorBase* AMyLoader::LoadAnyFormat(const FString& Path)
{
if (!ULCCUtilLibrary::CheckPathValid(Path))
{
UE_LOG(LogTemp, Error, TEXT("Path does not exist: %s"), *Path);
return nullptr;
}
const FString Ext = FPaths::GetExtension(Path).ToLower();
UClass* ActorClass = nullptr;
if (Ext == TEXT("lcc")) ActorClass = ALCCActor::StaticClass();
else if (Ext == TEXT("lcc2")) ActorClass = ALCC2Actor::StaticClass();
else if (Ext == TEXT("sog")) ActorClass = ASogActor::StaticClass();
else if (Ext == TEXT("spz")) ActorClass = ASpzActor::StaticClass();
else if (Ext == TEXT("ply")) ActorClass = APlyActor::StaticClass();
if (!ActorClass)
{
UE_LOG(LogTemp, Error, TEXT("Unsupported format: %s"), *Path);
return nullptr;
}
ALCCActorBase* Actor = GetWorld()->SpawnActor<ALCCActorBase>(
ActorClass, FVector::ZeroVector, FRotator::ZeroRotator);
if (Actor)
{
Actor->Load(Path);
}
return Actor;
}
蓝图里没有直接取扩展名的节点,可以用字符串判断:
[Custom Event: LoadAnyFormat]
Path (String)
│
▼
[Check Path Valid]
Path = Path
│ Return Value ──┐
▼ │
[Branch] ◀───────────────┘
│ True
▼
[Ends With]
Source String = Path
In Suffix = ".sog"
Search Case = Ignore Case
│ Return Value ──┐
▼ │
[Branch] ◀───────────────┘
│ True
▼
[Spawn Actor from Class]
Class = SogActor
│ Return Value ──┐
▼ │
[Load] ◀─────────────────┘
Target = (Return Value)
String = Path
(False 分支继续用 Ends With 判断 ".spz"、".ply"、".lcc2"、".lcc")
Notes
- 这三种格式没有 LOD 分层。加载时构造的是单层单节点的树(总层数为 1、根节点无子节点),因此
StartLevel、EndLevel、LevelFactor这些 LOD 参数虽然仍会被读取,但没有可供选择的层级,实际不产生效果。控制性能主要靠MaxSplatNum与SplatScale。 - 这三种格式不带碰撞数据,替代方案见碰撞 - 单文件格式的替代方案。
- 数据量大的文件会一次性全部加载,显存占用恒定,不随视角变化。加载上限与计算方式见渲染 - 单文件格式的加载上限。
- 走的是 LCC2 渲染管线,因此基类上的多视口接口不适用。
- 三者构造时都把 Component 的
bUseMipFilter设为false(基类默认true),并把 Actor 旋转设为(0, 0, 90)。 .ply有多种子格式,插件支持的是含 3DGS 属性的变体。普通几何网格.ply加载会失败。
See Also
- ALCCActorBase:继承来的全部接口
- ULCC2Component:这三个 Actor 内部使用的组件
- ULCCUtilLibrary:格式识别与路径校验