跳转至

Manager 架构

本文档描述了现在支撑应用程序的基于 Manager 的架构。单一的单体 App 已被拆分为多个专注的 Manager,从而极大地提高了模块化、可测试性和可扩展性。

概览

基于 Manager 的架构将单体 App 类拆分为专注、可测试的 Manager:

App (编排者/外观)
├── ModelManager          # 模型存储和元数据
├── FileLoader            # 多格式文件加载 (Gaussian + FBX)
├── GaussianLoader        # 高级 GaussianModel 创建服务
├── ONNXManager           # ONNX 模型生命周期和推理
├── FBXLoaderManager      # FBX 网格文件加载
├── CameraManager         # 相机和控制器管理
├── AnimationManager      # 动态模型更新
└── RenderLoop            # 帧循环和渲染协调

每个 Manager 拥有特定的职责,而 App 通过依赖注入将它们连接起来。Manager 之间通过定义良好的接口和回调进行通信。

核心 Managers

ModelManager

职责:维护所有已加载的模型并暴露统一的接口。

亮点: - 存储元数据和引用。 - 跟踪可见性、变换和容量限制。 - 支持 PLY 和 ONNX 条目。 - 生成唯一名称并加速查找。

关键方法:

class ModelManager {
  addModel(model: Omit<ModelEntry, 'id'>): ModelEntry;
  removeModel(id: string): boolean;
  getModels(): ModelInfo[];
  getModelWithPointCloud(type: 'ply' | 'onnx', id?: string): ModelEntry | null;
  getFullModels(): ModelEntry[];
  getModelsByType(modelType: 'ply' | 'onnx'): ModelEntry[];
  getVisibleModels(): ModelEntry[];
  getDynamicModels(): ModelEntry[];
  setModelVisibility(id: string, visible: boolean): boolean;
  getModelCount(): number;
  getTotalPoints(): number;
  getTotalVisiblePoints(): number;
  isAtCapacity(): boolean;
  getRemainingCapacity(): number;
  clearAllModels(): void;
  findModelByName(name: string): ModelEntry | null;
  generateUniqueName(baseName: string): string;
  hasModelWithName(name: string): boolean;
  // 旧版变换助手
  setModelPosition(id: string, x: number, y: number, z: number): boolean;
  setModelRotation(id: string, x: number, y: number, z: number): boolean;
  setModelScale(id: string, scale: number | [number, number, number]): boolean;
  setModelTransform(id: string, transform: Float32Array | number[]): boolean;
  getModelPosition(id: string): [number, number, number] | null;
  getModelTransform(id: string): Float32Array | null;
}

FileLoader

职责:加载并解析多种高斯格式和 FBX 文件。

特性: - 内部使用 IO 模块的 defaultLoader 进行统一格式处理 - 支持所有高斯格式:PLY, SPZ, KSplat, SPLAT, SOG, Compressed PLY - 通过 FBXLoaderManager 支持 FBX 网格文件 - 通过 detectGaussianFormat()isGaussianFormat() 进行自动格式检测 - 支持带进度回调的文件和 URL 加载 - 处理带有格式检测的 Blob URL - 在加载前验证容量限制

class FileLoader {
  loadFile(file: File, device: GPUDevice): Promise<ModelEntry | null>;
  loadSample(filename: string, device: GPUDevice, expectedType?: string): Promise<ModelEntry | null>;
  isFileTypeSupported(filename: string): boolean;
  getSupportedExtensions(): string[];
  getFileType(filename: string): string | null;
  getGaussianFormat(filename: string): string | null;
}

加载流程: 1. 检查模型容量 2. 检测文件类型 (Gaussian, ONNX, FBX) 3. 路由到适当的加载器(Gaussian 使用 defaultLoader,FBX 使用 FBXLoaderManager) 4. 通过 createGaussianModel() 将加载的数据转换为 ModelEntry 5. 注册到 ModelManager

GaussianLoader

职责:用于创建 GaussianModel 实例的高级便捷服务。

特性: - 包装 FileLoaderONNXManager 以提供统一 API - 支持所有高斯格式(PLY, SPZ, KSplat, SPLAT, SOG, Compressed PLY) - 支持 ONNX 模型 - 自动检测文件格式 - 返回 GaussianModel 实例(Three.js Object3D 包装器)

class GaussianLoader {
  createFromGaussian(renderer, modelPath, options?, formatHint?): Promise<GaussianModel>;
  createFromPLY(renderer, modelPath, options?): Promise<GaussianModel>;
  createFromSPZ(renderer, modelPath, options?): Promise<GaussianModel>;
  createFromKSplat(renderer, modelPath, options?): Promise<GaussianModel>;
  createFromSplat(renderer, modelPath, options?): Promise<GaussianModel>;
  createFromSOG(renderer, modelPath, options?): Promise<GaussianModel>;
  createFromONNX(renderer, modelPath, camMat, projMat, options?): Promise<GaussianModel>;
  createFromFile(renderer, modelPath, cameraMatrices?, options?, fileType?): Promise<GaussianModel>;
  createFromEntry(entry: ModelEntry): GaussianModel;
  isFormatSupported(filename: string): boolean;
  getSupportedFormats(): string[];
  detectFormat(filename: string): string | null;
}

用例: 简化与 Three.js 集成时的模型加载,因为它返回可以直接添加到 Three.js 场景的 GaussianModel 实例。

ONNXManager

职责:管理 ONNX 模型加载和推理。

特性: - 创建专用的 ONNXGenerator + DynamicPointCloud 对。 - 检测颜色模式/维度(SH vs RGB)。 - 支持静态和动态推理。 - 管理仅 GPU 的管道和资源清理。

class ONNXManager {
  loadONNXModel(
    device: GPUDevice,
    modelPath: string,
    cameraMatrix: Float32Array,
    projectionMatrix: Float32Array,
    name?: string,
    options?: ONNXLoadOptions,
  ): Promise<ModelEntry>;

  loadONNXFromFile(
    device: GPUDevice,
    file: File,
    cameraMatrix?: Float32Array | null,
    projectionMatrix?: Float32Array | null,
  ): Promise<ModelEntry>;

  updateCameraMatrices(
    modelName: string,
    cameraMatrix: Float32Array,
    projectionMatrix: Float32Array,
  ): Promise<void>;

  disposeModel(modelId: string): void;
  dispose(): void;

  getGenerator(id: string): ONNXGenerator | undefined;
  getPointCloud(id: string): DynamicPointCloud | undefined;

  hasONNXModels(): boolean;
  getONNXModels(): string[];
  getONNXPerformanceStats(): { modelCount: number; totalGenerators: number; totalPointClouds: number };
}

CameraManager

职责:管理相机状态和控制器切换。

亮点: - 初始化相机并在每帧更新。 - 切换轨道/FPS 控制器并处理调整大小事件。 - 基于模型分布计算轨道中心。 - 暴露矩阵、视口、视锥体数据和调试快照。

class CameraManager {
  initCamera(canvas: HTMLCanvasElement): void;
  resetCamera(): void;
  setupCameraForPointCloud(pc: PointCloud): void;
  resize(canvas: HTMLCanvasElement): void;
  update(dt: number): void;

  switchController(type: 'orbit' | 'fps'): void;
  getControllerType(): ControllerType;
  getController(): IController;

  getCamera(): PerspectiveCamera | null;
  getCameraMatrix(): mat4;
  getProjectionMatrix(): mat4;
  getCameraPosition(): vec3 | null;
  getCameraRotation(): quat | null;
  setCameraPosition(position: vec3): void;
  setCameraRotation(rotation: quat): void;

  setOrbitCenter(center: vec3): void;
  getOrbitCenter(): vec3 | null;

  getViewportInfo(): { width: number; height: number; aspect: number } | null;
  getFrustumInfo(): { fov: number; near: number; far: number } | null;
  isInitialized(): boolean;
  getDebugInfo(): any;
}

AnimationManager

职责:更新动态模型(通常由 ONNX 驱动)。

class AnimationManager {
  updateDynamicPointClouds(view: mat4, proj: mat4, time: number): void;
  controlDynamicAnimation(action: 'start' | 'pause' | 'resume' | 'stop', speed?: number): void;
  setDynamicAnimationTime(time: number): void;
  getDynamicPerformanceStats(): Array<{ modelName: string; stats: any }>;
}

RenderLoop

职责:协调帧循环和渲染协同。

特性: - 拥有 requestAnimationFrame 生命周期 - 计算 Delta time 并跟踪 FPS - 管理渲染状态(背景颜色,高斯缩放) - 协调每帧更新(相机,动画,渲染) - 提供性能回调(FPS,点数)

class RenderLoop {
  init(gpu: WebGPUContext, renderer: GaussianRenderer, canvas: HTMLCanvasElement): void;
  start(): void;
  stop(): void;
  setGaussianScale(scale: number): void;
  getGaussianScale(): number;
  setBackgroundColor(color: [number, number, number, number]): void;
  getBackgroundColor(): [number, number, number, number];
  setCallbacks(callbacks: FPSCallbacks): void;
  getState(): AppState;
  getPerformanceInfo(): object;
  getFPS(): number;
  getFrameCount(): number;
  getLastFrameTime(): number;
  getAverageFrameTime(): number;
  isRunning(): boolean;
  setTargetFPS(fps: number): void;
  getTargetFPS(): number;
  getDebugInfo(): object;
}

帧周期:

  1. 计算 delta time
  2. CameraManager.update(dt)
  3. AnimationManager.updateDynamicPointClouds(view, proj, time)
  4. 更新 FPS 计数器
  5. GaussianRenderer.prepareMulti(...) / renderMulti(...)
  6. 调用回调(FPS, 点数)

FBXLoaderManager

职责:加载 FBX 网格文件并将其注册到 ModelManager。

特性: - 包装 Three.js FBXLoader - 提供进度和错误回调 - 与 ModelManager 集成以实现统一的模型跟踪

class FBXLoaderManager {
  loadFromFile(file: File, options?): Promise<ModelEntry>;
  loadFromUrl(url: string, options?): Promise<ModelEntry>;
  dispose(): void;
}

注意: FBX + WebGPU 兼容性有限;为了更好的 WebGPU 支持,推荐使用 GLTF/GLB。

数据流

初始化

1. DOM / 画布验证 (DOMElements)
2. WebGPU 初始化 (`initWebGPU_onnx`)
3. GaussianRenderer 构造
4. CameraManager 初始化
5. RenderLoop 初始化 + 启动

事件/数据流

UI → UIController → App 回调 → Managers → 模型状态 → RenderLoop → 渲染器
示例链: - 文件拖放 → onFileLoadFileLoader.loadFileModelManager.addModel → RenderLoop 获取新模型。 - ONNX 加载 → ONNXManager.loadONNXModelDynamicPointCloud 输出 → 预处理 → 排序 → 渲染。 - 控制器切换 → CameraManager.switchController → UI 更新活动控制方案。

Manager 通信

依赖注入

class App {
  constructor() {
    this.modelManager = new ModelManager(MAX_MODELS);
    this.cameraManager = new CameraManager();
    this.animationManager = new AnimationManager(this.modelManager);
    this.renderLoop = new RenderLoop(this.modelManager, this.animationManager, this.cameraManager);

    this.fileLoader = new FileLoader(this.modelManager, loadingCallbacks);
    this.onnxManager = new ONNXManager(this.modelManager);
  }
}

回调契约

interface LoadingCallbacks {
  onProgress: (show: boolean, text?: string, pct?: number) => void;
  onError: (msg: string) => void;
}

interface UICallbacks {
  onFileLoad(file: File): Promise<void>;
  onSampleLoad(filename: string): void;
  onResetCamera(): void;
  onGaussianScaleChange(scale: number): void;
  onBackgroundColorChange(color: [number, number, number, number]): void;
  onControllerSwitch(type: 'orbit' | 'fps'): void;
}

优势

  • 模块化 – 清晰的关注点分离。
  • 可扩展性 – 可以通过引入新的 Manager 来添加新功能。
  • 可测试性 – Manager 可以通过模拟依赖项独立测试。
  • 性能 – 职责可以隔离优化,减少竞争。

Manager 通信模式

依赖注入

Manager 通过构造函数接收依赖: - FileLoader 需要 ModelManagerLoadingCallbacks - AnimationManager 需要 ModelManager - RenderLoop 需要 ModelManagerAnimationManagerCameraManager - GaussianLoader 需要 FileLoaderONNXManager

回调契约

Manager 使用回调处理横切关注点:

LoadingCallbacks:

interface LoadingCallbacks {
  onProgress?: (show: boolean, text?: string, pct?: number) => void;
  onError?: (message: string) => void;
}

FPSCallbacks:

interface FPSCallbacks {
  onFPSUpdate?: (fps: number) => void;
  onPointCountUpdate?: (count: number) => void;
}

数据流

  • 加载: FileLoaderModelManager.addModel()RenderLoop 获取新模型
  • 动态更新: ONNXManagerDynamicPointCloudAnimationManager.update()RenderLoop
  • 相机: CameraManagerRenderLoop 使用相机矩阵进行渲染