Skip to content

Models Module API Reference

本模块暴露的模型数据契约和辅助工具的综合参考。

ModelEntry

代表运行时中已加载的一个模型。包含对 pointCloud/object3D 的完整引用以及所有元数据。

interface ModelEntry {
  id: string;                                     // 唯一标识符(自动生成)
  name: string;                                   // 显示名称
  visible: boolean;                               // 渲染可见性标志
  pointCloud: PointCloud | DynamicPointCloud | FBXModelWrapper;  // 特定格式的数据
  pointCount: number;                             // 总点数/顶点数
  isDynamic: boolean;                             // 对 ONNX 模型为 true
  modelType: ModelType;                           // 格式类型
  colorMode?: 'sh' | 'rgb';                       // 颜色编码模式
  colorChannels?: number;                         // 颜色通道数
}

说明

  • pointCloud 是必需的,可以是:
  • PointCloud 用于静态高斯格式 (PLY, SPZ, KSplat, SPLAT, SOG, Compressed PLY)
  • DynamicPointCloud 用于 ONNX 模型
  • FBXModelWrapper 用于 FBX 网格文件
  • isDynamic 区分流式点云 (ONNX) 和静态资产
  • modelType 支持:'ply' | 'spz' | 'ksplat' | 'splat' | 'sog' | 'compressed.ply' | 'onnx' | 'fbx'
  • colorModecolorChannels 允许 UI 一致地显示颜色元数据
  • idModelManager.addModel() 使用时间戳和随机字符串自动生成

ModelInfo

用于 UI 面板或摘要的轻量级、只读表示。不包含 pointCloud 引用,以避免持有大型对象图。

interface ModelInfo {
  id: string;
  name: string;
  visible: boolean;
  pointCount: number;
  isDynamic: boolean;
  modelType: ModelType;
  colorMode?: 'sh' | 'rgb';
  colorChannels?: number;
}

说明

  • React 安全: 无 pointCloud 引用,React 渲染安全
  • 可序列化: 可以安全地序列化为 JSON
  • UI 列表: 用于模型列表、面板和摘要
  • 创建方式: ModelManager.getModels() 返回 ModelInfo[]

FBXModelWrapper

Three.js FBX 对象的包装器,支持动画和 Timeline 集成。实现了 ITimelineTarget 接口。

class FBXModelWrapper implements ITimelineTarget {
  readonly object3D: THREE.Group;
  readonly mixer: THREE.AnimationMixer;
  readonly clips: THREE.AnimationClip[];
  transform: Float32Array;

  constructor(
    object3D: THREE.Group,
    clips: THREE.AnimationClip[],
    options?: FBXLoadOptions
  );

  // 变换
  setTransform(transform: Float32Array | number[]): void;

  // 可见性
  setVisible(visible: boolean): void;
  getVisible(): boolean;

  // 统计
  getVertexCount(): number;

  // ITimelineTarget 接口
  setAnimationTime(time: number): void;
  setAnimationSpeed(speed: number): void;
  getAnimationSpeed(): number;
  startAnimation(speed?: number): void;
  pauseAnimation(): void;
  resumeAnimation(): void;
  stopAnimation(): void;
  setTimeScale(scale: number): void;
  getTimeScale(): number;
  setTimeOffset(offset: number): void;
  getTimeOffset(): number;
  setTimeUpdateMode(mode: 'fixed_delta' | 'variable_delta'): void;
  getTimeUpdateMode(): 'fixed_delta' | 'variable_delta';
  getCurrentTime(): number;
  update(deltaTime: number): void;

  // 动画管理
  switchToClip(clipIndex: number): boolean;
  getClipInfo(): Array<{name: string, duration: number}>;
  supportsAnimation(): boolean;

  // 清理
  dispose(): void;
}

FBXLoadOptions

interface FBXLoadOptions {
  autoPlay?: boolean;        // 自动播放第一个动画 (默认: true)
  defaultSpeed?: number;      // 默认播放速度 (默认: 1.0)
  loop?: boolean;             // 循环动画 (默认: false)
  blendMode?: 'normal' | 'additive';  // 动画混合模式
}

主要特性

  • Three.js 集成: 包装 THREE.GroupTHREE.AnimationMixer
  • 动画控制: 对动画播放、速度和时间的完全控制
  • Timeline 集成: 实现 ITimelineTarget 以进行时间轴控制
  • 变换矩阵: 带有 setTransform() 方法的 4x4 变换矩阵
  • 多剪辑: 通过 switchToClip() 支持多个动画剪辑
  • 时间控制: 固定/可变 delta 时间模式、时间缩放和偏移

类型定义

ModelType

所有支持的模型格式的联合类型:

type ModelType = 
  | 'ply' | 'spz' | 'ksplat' | 'splat' | 'sog' | 'compressed.ply'  // 高斯格式
  | 'onnx'                                                          // 动态 ONNX
  | 'fbx';                                                          // FBX 网格

ColorMode

高斯模型的颜色编码模式:

type ColorMode = 'sh' | 'rgb';
  • 'sh': 球谐函数编码(通常 3 阶为 48 通道)
  • 'rgb': RGB 颜色编码(3 通道)

示例

创建一个 PLY Model Entry

import { ModelEntry } from 'src/models';
import { PointCloud } from 'src/point_cloud';

const entry: ModelEntry = {
  id: 'museum-lobby',
  name: 'Museum Lobby',
  visible: true,
  pointCloud: new PointCloud(device, plyData),
  pointCount: plyData.numPoints(),
  isDynamic: false,
  modelType: 'ply',
  colorMode: 'sh',
  colorChannels: 48,
};

// 注册到 ModelManager
const modelManager = app.getModelManager();
modelManager.addModel(entry);

创建一个 ONNX Model Entry

import { ModelEntry } from 'src/models';
import { DynamicPointCloud } from 'src/point_cloud';

const entry: ModelEntry = {
  id: 'animated-gaussian',
  name: 'Animated Gaussian',
  visible: true,
  pointCloud: new DynamicPointCloud(device, onnxGenerator, buffers),
  pointCount: buffers.maxPoints,
  isDynamic: true,
  modelType: 'onnx',
  colorMode: 'rgb',
  colorChannels: 3,
};

// 注册到 ModelManager
modelManager.addModel(entry);

创建一个 FBX Model Entry

import { ModelEntry, FBXModelWrapper } from 'src/models';
import * as THREE from 'three/webgpu';

// 加载 FBX (通过 FBXLoaderManager)
const fbxGroup = await loadFBX('models/robot.fbx');
const clips = extractAnimationClips(fbxGroup);

// 创建包装器
const wrapper = new FBXModelWrapper(fbxGroup, clips, {
  autoPlay: true,
  defaultSpeed: 1.0,
  loop: true
});

// 创建条目
const entry: ModelEntry = {
  id: 'robot',
  name: 'Robot',
  visible: true,
  pointCloud: wrapper,
  pointCount: wrapper.getVertexCount(),
  isDynamic: false,
  modelType: 'fbx',
};

// 注册到 ModelManager
modelManager.addModel(entry);

创建不同的高斯格式

// SPZ 格式
const spzEntry: ModelEntry = {
  id: 'model-spz',
  name: 'SPZ Model',
  visible: true,
  pointCloud: new PointCloud(device, spzData),
  pointCount: spzData.numPoints(),
  isDynamic: false,
  modelType: 'spz',
  colorMode: 'sh',
  colorChannels: 48,
};

// KSplat 格式
const ksplatEntry: ModelEntry = {
  id: 'model-ksplat',
  name: 'KSplat Model',
  visible: true,
  pointCloud: new PointCloud(device, ksplatData),
  pointCount: ksplatData.numPoints(),
  isDynamic: false,
  modelType: 'ksplat',
  colorMode: 'sh',
  colorChannels: 48,
};

在 UI 中使用 ModelInfo

// 获取用于 UI 的轻量级信息
const models = modelManager.getModels(); // 返回 ModelInfo[]

// 在 React 组件中使用
function ModelList() {
  const models = modelManager.getModels();

  return (
    <ul>
      {models.map(model => (
        <li key={model.id}>
          {model.name} ({model.pointCount.toLocaleString()} points)
          {model.isDynamic && <span>Dynamic</span>}
        </li>
      ))}
    </ul>
  );
}

FBX 动画控制

// 获取 FBX 模型条目
const entry = modelManager.getModelWithPointCloud('fbx', 'robot-id');
if (entry && entry.modelType === 'fbx') {
  const wrapper = entry.pointCloud as FBXModelWrapper;

  // 控制动画
  wrapper.startAnimation(1.5);  // 以 1.5 倍速播放
  wrapper.pauseAnimation();
  wrapper.resumeAnimation();
  wrapper.stopAnimation();

  // Timeline 控制
  wrapper.setAnimationTime(5.0);  // 跳转到 5 秒
  wrapper.setAnimationSpeed(2.0);  // 2 倍速

  // 切换剪辑
  wrapper.switchToClip(1);  // 切换到第二个动画剪辑

  // 获取剪辑信息
  const clips = wrapper.getClipInfo();
  console.log('Available clips:', clips);
}

最佳实践

  1. 内存管理

    • 移除模型时,对包装器和动态点云调用 dispose()
    • 使用 ModelManager.removeModel(),它会处理清理工作
    • 对于 FBX:wrapper.dispose() 会清理 Three.js 资源
  2. 类型安全

    • 依赖 TypeScript 类型定义和 ModelType 联合类型
    • 在进行特定格式的操作前,使用类型守卫检查 modelType
    • 在访问动态特定功能前检查 isDynamic
  3. 动态模型

    • 确保 pointCount 反映最大容量
    • 对于 ONNX 模型,运行时点数可能会每帧变化
    • 使用 DynamicPointCloud.numPoints 获取当前点数
  4. UI 性能

    • UI 列表优先使用 ModelInfo,避免持有大型对象图
    • 使用返回 ModelInfo[]ModelManager.getModels()
    • 仅在需要 pointCloud 引用时使用 ModelEntry
  5. 格式检测

    • 使用 modelType 确定可用操作
    • 检查 colorModecolorChannels 以进行 UI 显示
    • 不同地处理 FBX 模型(使用 FBXModelWrapper 方法)
  6. 可扩展性

    • 通过联合类型扩展 ModelType 以添加新模型类型
    • 添加新格式时保留现有契约
    • 为需要特殊处理的格式创建包装器