Files
bim_engine/docs/MODULES/设置系统.md

27 KiB
Raw Blame History

设置系统模块文档

本文档基于 iflow-engine SDK 真实代码实现说明设置系统的架构、API 和第三方预设接入方式。

目录


1. 架构概览

设置系统采用分层架构:

┌─────────────────────────────────────────────────────────┐
│  UI 层: Toolbar Setting Button                          │
│    -> registry.setting.toggle()                         │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│  Manager 层: SettingDialogManager                       │
│    - 管理设置面板生命周期                                │
│    - 维护预设列表                                        │
│    - 编排设置应用流程                                    │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│  组件层: Engine (src/components/engine/index.ts)        │
│    - 封装底层引擎设置能力                                │
│    - 提供统一 getSettings/setSettings API               │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│  底层: iflow-engine-base                                │
│    - 实际 WebGL/Three.js 渲染设置                       │
│    - Setting 模块 (shadow, lighting, env, etc.)         │
└─────────────────────────────────────────────────────────┘

关键文件

文件 职责
src/managers/setting-dialog-manager.ts 设置对话框管理器UI 和预设管理
src/components/engine/index.ts 引擎组件,设置 API 实现
src/components/engine/types.ts 设置相关类型定义
src/types/events.ts 设置相关事件定义
src/locales/zh-CN.ts 设置面板文案国际化

2. 核心类型

2.1 EngineSettings - 完整设置

interface EngineSettings {
  render: RenderSettings;
  display: DisplaySettings;
  environment: EnvironmentSettings;
}

interface RenderSettings {
  mode: 'simple' | 'balance' | 'advanced';  // 渲染模式
  contrast: number;        // 对比度 0-100
  saturation: number;      // 饱和度 0-100
  shadowIntensity: number; // 阴影强度 0-100
  lightIntensity: number;  // 光照强度 0-100
  gtaoIntensity: number;   // GTAO 强度 0-100
}

interface DisplaySettings {
  showEdge: boolean;       // 显示边线
  edgeOpacity: number;     // 边线透明度 0-100
  showGrid: boolean;       // 显示轴网
  showLevel: boolean;      // 显示标高
  showGround: boolean;     // 显示地面
  groundId: string;        // 地面类型 ID
  groundHeight: number;    // 地面高度 (米)
}

interface EnvironmentSettings {
  type: 'none' | 'hdr' | 'sky';  // 环境类型
  hdrId: string;           // HDR 背景 ID
  hdrIntensity: number;    // HDR 强度 0-100
  skyPreset: string;       // 天空预设
  skyParams: SkyParams;    // 天空高级参数
  skyIntensity: number;    // 天空强度 0-100
}

interface SkyParams {
  turbidity?: number;
  rayleigh?: number;
  mieCoefficient?: number;
  mieDirectionalG?: number;
  elevation?: number;
  azimuth?: number;
  exposure?: number;
  orthoExposureScale?: number;
  cloudCoverage?: number;
  cloudDensity?: number;
  cloudElevation?: number;
  showSunDisc?: boolean;
}

2.2 EngineSettingPreset - 预设定义

interface EngineSettingPreset {
  id: string;              // 唯一标识
  presetName: string;      // 预设显示名称
  isDefault: boolean;      // 是否为默认预设
  settings: EngineSettings; // 预设包含的设置
  readonly?: boolean;      // 是否只读
  source?: 'sdk-default' | 'external' | 'user';  // 预设来源
  allowModify?: boolean;   // 是否允许修改(保存/删除),默认 true
}

字段说明:

字段 类型 说明
id string 预设唯一标识
presetName string 预设显示名称
isDefault boolean 是否为默认预设,打开面板时自动选中
settings EngineSettings 预设包含的完整设置
readonly boolean 是否只读,只读预设用户无法修改设置值(但仍可保存为新预设)
source 'sdk-default' | 'external' | 'user' 预设来源
allowModify boolean 是否允许修改当前预设false 时隐藏"保存"和"删除"按钮,默认 true

source 字段说明:

  • 'sdk-default': SDK 内置的默认预设(allowModify: false
  • 'external': 第三方注入的预设
  • 'user': 用户自行保存的预设

allowModify 字段说明:

  • true(默认): 显示所有操作按钮("保存预设"、"存为新预设"、"删除"
  • false: 隐藏"保存当前预设"和"删除"按钮,但保留"另存为新预设"按钮

注意: "另存为新预设"始终可用,因为创建新预设不是在修改当前预设。下拉框宽度始终保持固定。

2.3 设置补丁 (局部更新)

interface EngineSettingsPatch {
  render?: Partial<RenderSettings>;
  display?: Partial<DisplaySettings>;
  environment?: Partial<EnvironmentSettings>;
}

2.4 预设资源列表

interface SettingPresetLists {
  ground: PresetListItem[];  // 地面类型列表
  hdr: PresetListItem[];     // HDR 背景列表
  sky: PresetListItem[];     // 天空预设列表
}

interface PresetListItem {
  id: string;
  names: string[];  // [中文名, 繁体名, 英文名]
}

3. 设置面板UI

3.1 面板结构

设置对话框宽度 420px,包含以下区域:

┌───────────────────────────────────────────┐
│  设置                         [×]        │  ← 标题栏
├───────────────────────────────────────────┤
│                                           │
│  ▓▓ 渲染设置 ▓▓                           │
│  渲染模式: [性能] [平衡] [效果]           │
│  阴影强度: [══════════════●══] 75         │
│  光照强度: [════════●═══════] 50          │
│  对比度:   [════════●═══════] 50  (效果模式可见)
│  饱和度:   [════════●═══════] 50  (效果模式可见)
│  GTAO:     [════════●═══════] 50  (效果模式可见)
│                                           │
│  ▓▓ 显示设置 ▓▓                           │
│  边线       [◉] 透明度 [═══●═══] 50       │
│  轴网       [◉]                           │
│  标高       [◉]                           │
│  地面       [◉] 类型 [地面一 ▼] 高度 [0]  │
│                                           │
│  ▓▓ 环境背景 ▓▓                           │
│  [无] [HDR背景] [天空盒]                  │
│  HDR: [hdr-001 ▼] 强度 [══●═══] 40        │
│  或                                     │
│  天空: [晴朗 ▼] 强度 [══●═══] 40          │
│                                           │
├───────────────────────────────────────────┤
│  [撤销修改]                               │  ← 修改时显示
│  [预设选择 ▼] [🗑] [保存预设]            │  ← 底部操作栏
└───────────────────────────────────────────┘

3.2 UI 交互规则

  1. 渲染模式条件显示

    • contrast, saturation, gtaoIntensity 仅在 advanced 模式下显示
    • 切换模式时面板不抖动,控件平滑显示/隐藏
  2. 环境类型互斥

    • none: 无环境背景
    • hdr: 显示 HDR 选择器和强度滑块
    • sky: 显示天空预设选择器和强度滑块
  3. 显示设置联动

    • 边线开启后才显示透明度滑块
    • 地面开启后才显示地面类型和高度输入

4. API 参考

4.1 SettingDialogManager

文件: src/managers/setting-dialog-manager.ts

生命周期

// 初始化设置管理器
init(): void

// 销毁设置管理器
destroy(): void

对话框控制

// 显示设置对话框
show(): void

// 隐藏设置对话框
hide(): void

// 切换显示状态
toggle(): void

// 获取对话框是否打开
isOpen(): boolean

预设管理

/**
 * 设置/注入预设列表
 * @param presets 预设数组,会合并到现有预设列表
 */
setPresetList(presets: EngineSettingPreset[]): void

// 获取当前预设列表
getPresetList(): EngineSettingPreset[]

// 获取当前选中的预设
getSelectedPreset(): EngineSettingPreset | null

// 应用指定 ID 的预设
applyPresetById(id: string): Promise<void>

4.2 Engine 组件设置 API

文件: src/components/engine/index.ts

批量设置

/**
 * 获取当前完整设置
 * @returns 当前 EngineSettings 的深拷贝
 */
getSettings(): EngineSettings

/**
 * 应用设置补丁
 * @param patch 部分设置,会合并到当前设置
 * @returns Promise设置应用完成后 resolve
 */
setSettings(patch: EngineSettingsPatch): Promise<void>

/**
 * 重置为默认设置
 * @returns Promise
 */
resetToDefault(): Promise<void>

预设资源

/**
 * 获取预设资源列表(地面/HDR/天空)
 * @returns 各类资源的可用选项
 */
getPresetLists(): SettingPresetLists

渲染模式

// 获取当前渲染模式
getRenderMode(): 'simple' | 'balance' | 'advanced'

// 设置渲染模式
setRenderMode(mode: 'simple' | 'balance' | 'advanced'): void

光照与渲染

// 环境光强度
setAmbientLightIntensity(intensity: number): void  // 0-100
getAmbientLightIntensity(): number

// 对比度
setSceneContrast(contrast: number): void  // 0-100
getSceneContrast(): number

// 饱和度
setSceneSaturation(saturation: number): void  // 0-100
getSceneSaturation(): number

// 阴影强度
setShadowIntensity(intensity: number): void  // 0-100

边线

// 启用边线
activeModelEdge(): void

// 禁用边线
disActiveModelEdge(): void

// 获取边线是否启用
getModelEdgeActive(): boolean

// 设置边线透明度
setEdgeOpacity(opacity: number): void  // 0-100

HDR 背景

// 获取 HDR 列表
getHDRBackgroundList(): { name: string; id: string }[]

// 设置 HDR ID
setHDRBackgroundId(id: string): void

// 获取当前 HDR ID
getHDRBackgroundId(): string

// 设置 HDR 可见性
setHDRBackgroundVisibility(visible: boolean): void

// 设置 HDR 强度
setHDRIntensity(intensity: number): void  // 0-100

地面

// 获取地面列表
getGroundList(): { name: string; id: string }[]

// 设置地面 ID
setGroundId(id: string): void

// 获取当前地面 ID
getGroundId(): string

// 设置地面高度
setGroundElevation(elevation: number): void  // 米

// 获取地面高度
getGroundElevation(): number

// 设置地面可见性
setGroundVisible(visible: boolean): void

天空

// 获取天空预设列表
getSkyPresetList(): { name: string; id: string }[]

// 设置天空预设
setSkyPreset(presetId: string): void

// 设置天空强度
setSkyIntensity(intensity: number): void  // 0-100

// 设置天空参数
setSkyParams(params: Partial<SkyParams>): void

4.3 BimEngine 快捷访问

const bimEngine = new BimEngine(container, options);

// 初始化设置管理器
bimEngine.initSetting();

// 访问设置管理器
bimEngine.setting?.show();
bimEngine.setting?.hide();
bimEngine.setting?.toggle();

// 通过引擎组件访问设置 API
bimEngine.engine?.getSettings();
bimEngine.engine?.setSettings({ render: { mode: 'advanced' } });

5. 第三方预设接入

5.1 注入预设

第三方可通过 setPresetList 方法注入自定义预设:

import { BimEngine } from 'iflow-engine';

// 创建引擎
const bimEngine = new BimEngine(container, {
  locale: 'zh-CN',
  theme: 'dark'
});

// 初始化必要组件
bimEngine.initToolbar();
bimEngine.initButtonGroup();
bimEngine.initDialog();
bimEngine.initEngine();
bimEngine.initSetting();  // 初始化设置管理器

// 注入第三方预设
const externalPresets = [
  {
    id: 'vendor-indoor',
    presetName: '室内展厅',
    isDefault: false,
    source: 'external',  // 标记为外部来源
    settings: {
      render: {
        mode: 'advanced',
        contrast: 55,
        saturation: 60,
        shadowIntensity: 70,
        lightIntensity: 80,
        gtaoIntensity: 60,
      },
      display: {
        showEdge: true,
        edgeOpacity: 40,
        showGrid: false,
        showLevel: true,
        showGround: true,
        groundId: 'marble-01',
        groundHeight: 0,
      },
      environment: {
        type: 'hdr',
        hdrId: 'interior-01',
        hdrIntensity: 30,
        skyPreset: 'sunrise_clear',
        skyParams: {},
        skyIntensity: 20,
      },
    },
  },
  {
    id: 'vendor-outdoor',
    presetName: '室外建筑',
    isDefault: true,  // 设为默认预设
    source: 'external',
    settings: {
      render: {
        mode: 'balance',
        contrast: 50,
        saturation: 50,
        shadowIntensity: 50,
        lightIntensity: 50,
        gtaoIntensity: 50,
      },
      display: {
        showEdge: false,
        edgeOpacity: 30,
        showGrid: true,
        showLevel: false,
        showGround: true,
        groundId: 'grass-01',
        groundHeight: 0,
      },
      environment: {
        type: 'sky',
        hdrId: '0',
        hdrIntensity: 20,
        skyPreset: 'sunny_clear',
        skyParams: {
          turbidity: 3,
          cloudCoverage: 0.2,
        },
        skyIntensity: 25,
      },
    },
  },
];

bimEngine.setting?.setPresetList(externalPresets);

5.2 预设合并规则

调用 setPresetList 时的处理逻辑:

  1. 保留内置预设__internal-default-preset__ 始终保留
  2. 去重:相同 id 的预设会被新传入的替换
  3. 来源标记:建议第三方预设标记 source: 'external'
  4. 默认预设
    • 若传入的预设中有 isDefault: true,则选中该预设
    • 若有多个 isDefault: true,取第一个
    • 若没有指定默认,选中内置默认预设

5.3 禁止修改预设

使用 allowModify: false 可以禁止用户保存或删除某个预设:

bimEngine.setting?.setPresetList([
  {
    id: 'vendor-locked',
    presetName: '供应商锁定预设',
    isDefault: false,
    source: 'external',
    allowModify: false,  // ← 禁止修改
    settings: {
      render: { mode: 'advanced', contrast: 60, ... },
      display: { showEdge: true, ... },
      environment: { type: 'hdr', hdrId: 'hdr-01', ... }
    }
  }
]);

效果:

  • 不显示"保存当前预设"按钮(无法覆盖原预设)
  • 不显示删除按钮
  • 保留"另存为新预设"按钮(可基于此预设创建新预设)
  • 预设选择下拉框宽度固定不变
  • 用户仍可修改设置值(如需禁止修改设置值,使用 readonly: true

适用场景:

  • 供应商提供的标准预设,不希望用户覆盖或删除,但允许基于此创建新预设
  • 项目规定的固定展示模式
  • 内置默认预设SDK 默认 allowModify: false

为什么保留"另存为新预设"

"另存为新预设"是创建一个新的预设,不是在修改当前预设。即使用户不能修改供应商预设,也应该可以基于它创建自己的版本。> 如果你希望完全禁止用户基于此预设创建新预设,需要额外的前端逻辑控制。

5.4 动态更新预设

// 随时可以更新预设列表
bimEngine.setting?.setPresetList([
  {
    id: 'dynamic-preset',
    presetName: '动态预设',
    isDefault: false,
    source: 'external',
    settings: { /* ... */ },
  },
]);

// 获取当前预设列表
const presets = bimEngine.setting?.getPresetList();

5.5 完整示例

import { BimEngine } from 'iflow-engine';
import type { EngineSettingPreset } from 'iflow-engine';

class VendorPresetManager {
  private engine: BimEngine;

  constructor(engine: BimEngine) {
    this.engine = engine;
  }

  // 注册供应商预设
  registerVendorPresets() {
    const presets: EngineSettingPreset[] = [
      this.createReviewPreset(),
      this.createPresentationPreset(),
      this.createPerformancePreset(),
    ];

    this.engine.setting?.setPresetList(presets);
  }

  // 评审模式 - 高对比度,显示边线
  private createReviewPreset(): EngineSettingPreset {
    return {
      id: 'vendor-review',
      presetName: '评审模式',
      isDefault: false,
      source: 'external',
      settings: {
        render: {
          mode: 'advanced',
          contrast: 70,
          saturation: 55,
          shadowIntensity: 65,
          lightIntensity: 55,
          gtaoIntensity: 50,
        },
        display: {
          showEdge: true,
          edgeOpacity: 50,
          showGrid: true,
          showLevel: true,
          showGround: true,
          groundId: 'grid-01',
          groundHeight: 0,
        },
        environment: {
          type: 'hdr',
          hdrId: 'studio-01',
          hdrIntensity: 35,
          skyPreset: 'sunrise_clear',
          skyParams: {},
          skyIntensity: 20,
        },
      },
    };
  }

  // 展示模式 - 高饱和度HDR环境
  private createPresentationPreset(): EngineSettingPreset {
    return {
      id: 'vendor-presentation',
      presetName: '展示模式',
      isDefault: true,  // 设为默认
      source: 'external',
      settings: {
        render: {
          mode: 'advanced',
          contrast: 55,
          saturation: 75,
          shadowIntensity: 80,
          lightIntensity: 70,
          gtaoIntensity: 60,
        },
        display: {
          showEdge: false,
          edgeOpacity: 30,
          showGrid: false,
          showLevel: false,
          showGround: true,
          groundId: 'pavement-01',
          groundHeight: -0.5,
        },
        environment: {
          type: 'hdr',
          hdrId: 'outdoor-afternoon',
          hdrIntensity: 45,
          skyPreset: 'sunset_glow',
          skyParams: {},
          skyIntensity: 25,
        },
      },
    };
  }

  // 性能模式 - 低画质,高帧率
  private createPerformancePreset(): EngineSettingPreset {
    return {
      id: 'vendor-performance',
      presetName: '性能模式',
      isDefault: false,
      source: 'external',
      settings: {
        render: {
          mode: 'simple',
          contrast: 50,
          saturation: 50,
          shadowIntensity: 20,
          lightIntensity: 50,
          gtaoIntensity: 0,
        },
        display: {
          showEdge: false,
          edgeOpacity: 30,
          showGrid: false,
          showLevel: false,
          showGround: false,
          groundId: '0',
          groundHeight: 0,
        },
        environment: {
          type: 'none',
          hdrId: '0',
          hdrIntensity: 20,
          skyPreset: 'sunrise_clear',
          skyParams: {},
          skyIntensity: 20,
        },
      },
    };
  }
}

// 使用
const bimEngine = new BimEngine(container);
bimEngine.initToolbar();
bimEngine.initButtonGroup();
bimEngine.initDialog();
bimEngine.initEngine();
bimEngine.initSetting();

const presetManager = new VendorPresetManager(bimEngine);
presetManager.registerVendorPresets();

// 监听预设变更
bimEngine.on('setting:preset-changed', ({ preset }) => {
  console.log(`已切换到: ${preset.presetName}`);
  
  // 可以在这里做自定义逻辑
  if (preset.source === 'external') {
    console.log('使用第三方预设');
  }
});

6. 事件系统

6.1 设置相关事件

文件: src/types/events.ts

// 预设保存事件
'setting:preset-saved': {
  preset: EngineSettingPreset;      // 被保存的预设
  currentSettings: EngineSettings;  // 当前完整设置
  timestamp: number;
}

// 预设切换事件
'setting:preset-changed': {
  preset: EngineSettingPreset;      // 切换后的目标预设
  timestamp: number;
}

// 预设删除事件
'setting:preset-deleted': EngineSettingPreset;  // 被删除的预设

6.2 事件监听示例

const bimEngine = new BimEngine(container);

// 监听预设保存
const offSaved = bimEngine.on('setting:preset-saved', ({ preset, currentSettings, timestamp }) => {
  console.log('用户保存预设:', preset.presetName);
  console.log('预设来源:', preset.source);
  console.log('保存时间:', new Date(timestamp));
  
  // 可以持久化到本地存储或后端
  localStorage.setItem(`preset_${preset.id}`, JSON.stringify(preset));
});

// 监听预设切换
const offChanged = bimEngine.on('setting:preset-changed', ({ preset, timestamp }) => {
  console.log('预设已切换:', preset.presetName);
  
  // 根据来源做不同处理
  switch (preset.source) {
    case 'sdk-default':
      console.log('使用 SDK 默认预设');
      break;
    case 'external':
      console.log('使用第三方预设');
      break;
    case 'user':
      console.log('使用用户自定义预设');
      break;
  }
});

// 监听预设删除
const offDeleted = bimEngine.on('setting:preset-deleted', (preset) => {
  console.log('预设已删除:', preset.presetName);
  
  // 清理本地存储
  localStorage.removeItem(`preset_${preset.id}`);
});

// 取消监听
// offSaved();
// offChanged();
// offDeleted();

6.3 与第三方系统集成

// 示例:将用户保存的预设同步到后端
class PresetSyncService {
  constructor(private engine: BimEngine) {
    this.setupListeners();
  }

  private setupListeners() {
    // 保存时同步到后端
    this.engine.on('setting:preset-saved', async ({ preset }) => {
      if (preset.source === 'user') {
        await this.saveToServer(preset);
      }
    });

    // 切换时上报埋点
    this.engine.on('setting:preset-changed', ({ preset }) => {
      this.trackPresetUsage(preset);
    });
  }

  private async saveToServer(preset: EngineSettingPreset) {
    try {
      await fetch('/api/presets', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(preset),
      });
    } catch (error) {
      console.error('同步预设失败:', error);
    }
  }

  private trackPresetUsage(preset: EngineSettingPreset) {
    // 埋点上报
    analytics.track('Preset Changed', {
      presetId: preset.id,
      presetName: preset.presetName,
      source: preset.source,
    });
  }
}

7. 默认值参考

const DEFAULT_SETTINGS: EngineSettings = {
  render: {
    mode: 'advanced',
    contrast: 50,
    saturation: 50,
    shadowIntensity: 50,
    lightIntensity: 50,
    gtaoIntensity: 50,
  },
  display: {
    showEdge: false,
    edgeOpacity: 30,
    showGrid: false,
    showLevel: false,
    showGround: false,
    groundId: '0',
    groundHeight: 0,
  },
  environment: {
    type: 'none',
    hdrId: '0',
    hdrIntensity: 20,
    skyPreset: 'sunrise_clear',
    skyParams: {},
    skyIntensity: 20,
  },
};

8. 常见问题

Q1: 如何获取当前生效的设置?

const currentSettings = bimEngine.engine?.getSettings();
console.log(currentSettings.render.mode);

Q2: 如何只修改部分设置?

// 只修改渲染模式,其他保持不变
await bimEngine.engine?.setSettings({
  render: { mode: 'simple' }
});

Q3: 如何禁止用户修改(保存/删除)某个预设?

使用 allowModify: false 可以禁止用户覆盖或删除某个预设:

{
  id: 'vendor-locked-preset',
  presetName: '供应商锁定预设',
  source: 'external',
  allowModify: false,  // 禁止修改当前预设
  settings: { ... }
}

效果:

  • 隐藏"保存当前预设"按钮(无法覆盖原预设)
  • 隐藏"删除"按钮
  • 保留"另存为新预设"按钮(可基于此创建新预设)
  • 下拉框宽度保持不变

注意:

  • allowModify: false 时,用户仍可以切换到这个预设
  • 用户仍可以修改设置值,只是无法保存到当前预设
  • 如需完全禁止修改设置值,需配合 readonly: true

Q4: readonlyallowModify 有什么区别?

字段 作用 对用户的影响
readonly 禁止修改设置值 禁用所有设置控件(滑块、开关等)
allowModify 禁止覆盖/删除当前预设 隐藏"保存当前预设"和"删除"按钮,但保留"另存为新预设"

内置默认预设(__internal-default-preset__)的 allowModify: false

Q5: 第三方预设会被用户删除吗?

如果设置了 allowModify: false,用户无法删除该预设。建议第三方预设设置 allowModify: false

{
  id: 'vendor-preset',
  presetName: '供应商预设',
  source: 'external',
  allowModify: false,  // 用户不可删除/保存该预设
  settings: { ... }
}

Q6: 如何清空所有第三方预设?

// 获取当前列表
const currentList = bimEngine.setting?.getPresetList() || [];

// 过滤掉外部预设
const filteredList = currentList.filter(p => p.source !== 'external');

// 重新设置
bimEngine.setting?.setPresetList(filteredList);

Q7: 设置修改后立即生效吗?

setSettings 返回 Promise设置应用完成后 resolve

await bimEngine.engine?.setSettings({
  environment: { type: 'hdr', hdrId: 'hdr-01' }
});
// 到这里设置已生效

文档版本: 1.1.0
更新时间: 2026-03-30
适用 SDK 版本: iflow-engine >= 2.2.1