2026-01-22 15:23:57 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 3D 引擎管理器
|
|
|
|
|
|
* 负责管理 3D 渲染引擎的初始化、模型加载和测量功能
|
|
|
|
|
|
*/
|
2026-02-03 18:01:31 +08:00
|
|
|
|
import { Engine, type EngineOptions, type ModelLoadOptions, type EngineInfo } from '../components/engine';
|
2026-01-22 15:23:57 +08:00
|
|
|
|
import { BaseManager } from '../core/base-manager';
|
2025-12-10 09:42:05 +08:00
|
|
|
|
import { RightKeyManager } from './right-key-manager';
|
2026-01-15 14:13:13 +08:00
|
|
|
|
import type { MeasureMode } from '../types/measure';
|
2026-02-02 18:18:36 +08:00
|
|
|
|
import type { MeasureUnit, MeasurePrecision } from '../components/measure-panel/types';
|
2026-01-28 11:24:32 +08:00
|
|
|
|
import type { SectionBoxRange } from '../components/section-box-panel/types';
|
2026-01-28 11:57:20 +08:00
|
|
|
|
import type { MenuItemConfig } from '../components/menu/item';
|
|
|
|
|
|
import { ManagerRegistry } from '../core/manager-registry';
|
2025-12-04 18:41:11 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 3D 引擎管理器
|
2026-01-22 15:23:57 +08:00
|
|
|
|
* 封装底层 3D 引擎,提供模型加载、相机控制、测量等功能
|
2025-12-04 18:41:11 +08:00
|
|
|
|
*/
|
2026-01-22 15:23:57 +08:00
|
|
|
|
export class EngineManager extends BaseManager {
|
|
|
|
|
|
/** 容器元素 */
|
2025-12-04 18:41:11 +08:00
|
|
|
|
private container: HTMLElement;
|
2026-01-22 15:23:57 +08:00
|
|
|
|
/** 引擎实例 */
|
2025-12-08 10:02:24 +08:00
|
|
|
|
private engineInstance: Engine | null = null;
|
2026-01-22 15:23:57 +08:00
|
|
|
|
/** 右键菜单管理器 */
|
|
|
|
|
|
public rightKey: RightKeyManager | null = null;
|
2025-12-04 18:41:11 +08:00
|
|
|
|
|
2026-01-22 15:23:57 +08:00
|
|
|
|
constructor(container: HTMLElement) {
|
|
|
|
|
|
super();
|
2025-12-04 18:41:11 +08:00
|
|
|
|
this.container = container;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化 3D 引擎
|
2026-01-22 15:23:57 +08:00
|
|
|
|
* @param options 引擎配置选项
|
2025-12-04 18:41:11 +08:00
|
|
|
|
* @returns 是否初始化成功
|
|
|
|
|
|
*/
|
|
|
|
|
|
public initialize(options?: Omit<EngineOptions, 'container'>): boolean {
|
2025-12-08 10:02:24 +08:00
|
|
|
|
if (this.engineInstance && this.engineInstance.isInitialized()) {
|
2025-12-04 18:41:11 +08:00
|
|
|
|
console.warn('[EngineManager] 3D Engine already initialized. Destroying old instance...');
|
2025-12-08 10:02:24 +08:00
|
|
|
|
this.engineInstance.destroy();
|
|
|
|
|
|
this.engineInstance = null;
|
2025-12-04 18:41:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2025-12-08 10:02:24 +08:00
|
|
|
|
this.engineInstance = new Engine({
|
2025-12-04 18:41:11 +08:00
|
|
|
|
container: this.container,
|
2026-01-22 15:23:57 +08:00
|
|
|
|
...options,
|
2025-12-04 18:41:11 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-12-08 10:02:24 +08:00
|
|
|
|
this.engineInstance.init();
|
2025-12-04 18:41:11 +08:00
|
|
|
|
|
2026-01-22 15:23:57 +08:00
|
|
|
|
this.rightKey = new RightKeyManager(this.container);
|
2025-12-10 09:42:05 +08:00
|
|
|
|
|
|
|
|
|
|
this.rightKey.registerHandler((_e) => {
|
2026-01-28 11:57:20 +08:00
|
|
|
|
const selected = this.getSelectedComponent();
|
|
|
|
|
|
const items: MenuItemConfig[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (selected) {
|
2026-01-28 17:19:36 +08:00
|
|
|
|
// 1. 构件详情
|
2026-01-28 11:57:20 +08:00
|
|
|
|
items.push({
|
|
|
|
|
|
id: 'componentDetail',
|
|
|
|
|
|
label: 'menu.componentDetail',
|
|
|
|
|
|
group: 'component',
|
2026-01-28 17:19:36 +08:00
|
|
|
|
order: 1,
|
|
|
|
|
|
divider: true,
|
2026-01-28 11:57:20 +08:00
|
|
|
|
onClick: () => {
|
|
|
|
|
|
const registry = ManagerRegistry.getInstance();
|
2026-01-28 17:19:36 +08:00
|
|
|
|
registry.componentDetail?.show();
|
|
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 隐藏选中构件
|
|
|
|
|
|
items.push({
|
|
|
|
|
|
id: 'hideSelected',
|
|
|
|
|
|
label: 'menu.hideSelected',
|
|
|
|
|
|
group: 'component',
|
|
|
|
|
|
order: 2,
|
|
|
|
|
|
onClick: () => {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.hideSelectedModels();
|
2026-01-28 17:19:36 +08:00
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 半透明选中构件
|
|
|
|
|
|
items.push({
|
|
|
|
|
|
id: 'transparentSelected',
|
|
|
|
|
|
label: 'menu.transparentSelected',
|
|
|
|
|
|
group: 'component',
|
|
|
|
|
|
order: 3,
|
|
|
|
|
|
onClick: () => {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.translucentSelectedModels();
|
2026-01-28 17:19:36 +08:00
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-02-04 15:10:51 +08:00
|
|
|
|
// 4. 取消半透明
|
|
|
|
|
|
items.push({
|
|
|
|
|
|
id: 'cancelTranslucent',
|
|
|
|
|
|
label: 'menu.cancelTranslucent',
|
|
|
|
|
|
group: 'component',
|
|
|
|
|
|
order: 4,
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
this.unTranslucentModel();
|
|
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 隔离选中构件(带子菜单)
|
2026-01-28 17:19:36 +08:00
|
|
|
|
items.push({
|
|
|
|
|
|
id: 'isolateSelected',
|
|
|
|
|
|
label: 'menu.isolateSelected',
|
|
|
|
|
|
group: 'component',
|
2026-02-04 15:10:51 +08:00
|
|
|
|
order: 5,
|
2026-01-28 17:19:36 +08:00
|
|
|
|
divider: true,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'hideOthers',
|
|
|
|
|
|
label: 'menu.hideOthers',
|
|
|
|
|
|
onClick: () => {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.isolateSelectedModels();
|
2026-01-28 17:19:36 +08:00
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'transparentOthers',
|
|
|
|
|
|
label: 'menu.transparentOthers',
|
|
|
|
|
|
onClick: () => {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.translucentOtherModels();
|
2026-01-28 17:19:36 +08:00
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-02-04 15:10:51 +08:00
|
|
|
|
// 6. 快速选择(带子菜单)
|
2026-02-03 18:01:31 +08:00
|
|
|
|
items.push({
|
|
|
|
|
|
id: 'quickSelect',
|
|
|
|
|
|
label: 'menu.quickSelect',
|
|
|
|
|
|
group: 'component',
|
2026-02-04 15:10:51 +08:00
|
|
|
|
order: 6,
|
2026-02-03 18:01:31 +08:00
|
|
|
|
children: [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'selectSameType',
|
|
|
|
|
|
label: 'menu.selectSameType',
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
this.batchSelectSameTypeModel();
|
|
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'selectSameLevel',
|
|
|
|
|
|
label: 'menu.selectSameLevel',
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
this.batchSelectSameLevelModel();
|
|
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'selectSameLevelType',
|
|
|
|
|
|
label: 'menu.selectSameLevelType',
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
this.batchSelectSameLevelTypeModel();
|
|
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-02-04 15:10:51 +08:00
|
|
|
|
// 7. 剖切盒适应
|
2026-01-28 17:19:36 +08:00
|
|
|
|
items.push({
|
|
|
|
|
|
id: 'fitSectionBox',
|
|
|
|
|
|
label: 'menu.fitSectionBox',
|
|
|
|
|
|
group: 'component',
|
2026-02-04 15:10:51 +08:00
|
|
|
|
order: 7,
|
2026-01-28 17:19:36 +08:00
|
|
|
|
onClick: () => {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.fitSectionBoxToModel();
|
2026-01-28 11:57:20 +08:00
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 15:10:51 +08:00
|
|
|
|
// 8. 显示全部(始终显示)
|
2026-01-28 11:57:20 +08:00
|
|
|
|
items.push({
|
|
|
|
|
|
id: 'showAll',
|
|
|
|
|
|
label: 'menu.showAll',
|
|
|
|
|
|
group: 'component',
|
2026-02-04 15:10:51 +08:00
|
|
|
|
order: 8,
|
2026-01-28 11:57:20 +08:00
|
|
|
|
onClick: () => {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.showAllModels();
|
2026-01-28 11:57:20 +08:00
|
|
|
|
this.rightKey?.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return items;
|
2025-12-10 09:42:05 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-12-08 10:02:24 +08:00
|
|
|
|
return this.engineInstance.isInitialized();
|
2025-12-04 18:41:11 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[EngineManager] Failed to initialize 3D engine:', error);
|
2025-12-08 10:02:24 +08:00
|
|
|
|
this.engineInstance = null;
|
2025-12-04 18:41:11 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-22 15:23:57 +08:00
|
|
|
|
|
2025-12-04 18:41:11 +08:00
|
|
|
|
/**
|
2026-01-22 15:23:57 +08:00
|
|
|
|
* 检查引擎是否已初始化
|
|
|
|
|
|
* @returns 是否已初始化
|
2025-12-04 18:41:11 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public isInitialized(): boolean {
|
2025-12-08 10:02:24 +08:00
|
|
|
|
return this.engineInstance !== null && this.engineInstance.isInitialized();
|
2025-12-04 18:41:11 +08:00
|
|
|
|
}
|
2026-01-22 15:23:57 +08:00
|
|
|
|
|
2025-12-04 18:41:11 +08:00
|
|
|
|
/**
|
2026-01-22 15:23:57 +08:00
|
|
|
|
* 加载模型
|
|
|
|
|
|
* @param url 模型 URL
|
|
|
|
|
|
* @param options 加载选项
|
2025-12-04 18:41:11 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public loadModel(url: string, options?: ModelLoadOptions): void {
|
2026-01-15 14:13:13 +08:00
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
2025-12-04 18:41:11 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-12-08 10:02:24 +08:00
|
|
|
|
this.engineInstance.loadModel(url, options);
|
2025-12-04 18:41:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-22 15:23:57 +08:00
|
|
|
|
* 获取底层引擎实例
|
|
|
|
|
|
* @returns 引擎实例
|
2025-12-04 18:41:11 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public getEngine(): any {
|
2025-12-08 10:02:24 +08:00
|
|
|
|
if (!this.engineInstance) {
|
2025-12-04 18:41:11 +08:00
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2025-12-08 10:02:24 +08:00
|
|
|
|
return this.engineInstance.getEngine();
|
2025-12-04 18:41:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 15:23:57 +08:00
|
|
|
|
/** 相机回到初始位置 */
|
2026-01-15 14:13:13 +08:00
|
|
|
|
public CameraGoHome(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.CameraGoHome();
|
|
|
|
|
|
}
|
2026-01-22 15:23:57 +08:00
|
|
|
|
|
2026-01-23 16:27:04 +08:00
|
|
|
|
/** 暂停渲染 */
|
|
|
|
|
|
public pauseRendering(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.pauseRendering();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 恢复渲染 */
|
|
|
|
|
|
public resumeRendering(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.resumeRendering();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-15 14:13:13 +08:00
|
|
|
|
/**
|
2026-01-22 15:23:57 +08:00
|
|
|
|
* 激活测量模式
|
|
|
|
|
|
* @param mode 测量模式
|
2026-01-15 14:13:13 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public activateMeasure(mode: MeasureMode): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.activateMeasure(mode);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 15:23:57 +08:00
|
|
|
|
/** 停用测量模式 */
|
2026-01-15 14:13:13 +08:00
|
|
|
|
public deactivateMeasure(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.deactivateMeasure();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-22 15:23:57 +08:00
|
|
|
|
* 获取当前测量类型
|
|
|
|
|
|
* @returns 当前测量模式
|
2026-01-15 14:13:13 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public getCurrentMeasureType(): MeasureMode | null {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.engineInstance.getCurrentMeasureType();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 17:58:56 +08:00
|
|
|
|
public clearAllMeasures(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.clearAllMeasures();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 18:18:36 +08:00
|
|
|
|
public saveMeasureSetting(setting: { unit: MeasureUnit; precision: MeasurePrecision }): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.saveMeasureSetting(setting);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 16:25:27 +08:00
|
|
|
|
public activeSection(mode: 'x' | 'y' | 'z' | 'box' | 'face'): void {
|
2026-01-27 17:58:56 +08:00
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-02-02 16:25:27 +08:00
|
|
|
|
this.engineInstance.activeSection(mode);
|
2026-01-27 17:58:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 16:30:07 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前激活的剖切模式
|
|
|
|
|
|
* @returns 当前剖切模式,未激活时返回 null
|
|
|
|
|
|
*/
|
2026-02-02 16:25:27 +08:00
|
|
|
|
public getCurrentSectionMode(): 'x' | 'y' | 'z' | 'box' | 'face' | null {
|
2026-01-27 17:58:56 +08:00
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2026-02-02 16:25:27 +08:00
|
|
|
|
return this.engineInstance.getCurrentSectionMode();
|
2026-01-28 11:24:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 16:30:07 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 停用所有剖切功能
|
|
|
|
|
|
* @remarks 关闭剖切对话框时调用
|
|
|
|
|
|
*/
|
2026-02-02 16:25:27 +08:00
|
|
|
|
public deactivateSection(): void {
|
2026-01-28 11:24:32 +08:00
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-02-02 16:25:27 +08:00
|
|
|
|
this.engineInstance.deactivateSection();
|
2026-01-28 11:24:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 16:30:07 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 设置剖切盒范围
|
|
|
|
|
|
* @param range 各轴的剖切范围 (百分比 0-100)
|
|
|
|
|
|
* @remarks 仅在 'box' 模式下有效
|
|
|
|
|
|
*/
|
2026-01-28 11:24:32 +08:00
|
|
|
|
public setSectionBoxRange(range: SectionBoxRange): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.setSectionBoxRange(range);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 16:32:22 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 隐藏剖切面(临时隐藏,可恢复)
|
|
|
|
|
|
* @remarks 配合 recoverSection() 使用
|
|
|
|
|
|
*/
|
|
|
|
|
|
public hideSection(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.hideSection();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 恢复剖切面(从隐藏状态恢复)
|
|
|
|
|
|
* @remarks 恢复被 hideSection() 隐藏的剖切面
|
|
|
|
|
|
*/
|
|
|
|
|
|
public recoverSection(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.recoverSection();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 18:01:31 +08:00
|
|
|
|
public fitSectionBoxToModel(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.fitSectionBoxToModel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 17:58:56 +08:00
|
|
|
|
/** 激活框选放大功能 */
|
|
|
|
|
|
public activateZoomBox(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.activateZoomBox();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-28 11:24:32 +08:00
|
|
|
|
// ==================== 漫游功能 ====================
|
|
|
|
|
|
|
|
|
|
|
|
/** 激活第一人称漫游模式 */
|
|
|
|
|
|
public activateFirstPersonMode(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
console.warn('[EngineManager] 3D Engine not initialized.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.activateFirstPersonMode();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 停用第一人称漫游模式 */
|
|
|
|
|
|
public deactivateFirstPersonMode(): void {
|
|
|
|
|
|
if (!this.engineInstance) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.engineInstance.deactivateFirstPersonMode();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public setWalkSpeed(speed: number): void {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.engineInstance?.setWalkSpeed(speed);
|
2026-01-28 11:24:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public setWalkGravity(enabled: boolean): void {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.engineInstance?.setWalkGravity(enabled);
|
2026-01-28 11:24:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public setWalkCollision(enabled: boolean): void {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
this.engineInstance?.setWalkCollision(enabled);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public toggleMiniMap(): void {
|
|
|
|
|
|
this.engineInstance?.toggleMiniMap();
|
2026-01-28 11:24:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public isFirstPersonModeActive(): boolean {
|
2026-02-03 18:01:31 +08:00
|
|
|
|
return this.engineInstance?.isFirstPersonModeActive() ?? false;
|
2026-01-28 11:24:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 结束:漫游功能 ====================
|
|
|
|
|
|
|
2026-01-28 11:57:20 +08:00
|
|
|
|
// ==================== 构件选中 ====================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前选中的构件
|
|
|
|
|
|
* @returns 选中构件的 URL 和 ID,未选中时返回 null
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getSelectedComponent(): { url: string; id: string } | null {
|
|
|
|
|
|
return this.engineInstance?.getSelectedComponent() ?? null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取构件属性
|
|
|
|
|
|
* @param url 模型 URL
|
|
|
|
|
|
* @param id 构件 ID
|
|
|
|
|
|
* @param callback 回调函数,接收属性数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getComponentProperties(
|
|
|
|
|
|
url: string,
|
|
|
|
|
|
id: string,
|
|
|
|
|
|
callback: (data: any) => void
|
|
|
|
|
|
): void {
|
|
|
|
|
|
this.engineInstance?.getComponentProperties(url, id, callback);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 结束:构件选中 ====================
|
|
|
|
|
|
|
2026-01-28 17:19:36 +08:00
|
|
|
|
// ==================== 模型树 ====================
|
|
|
|
|
|
|
|
|
|
|
|
public getTypeTreeData(): any[] {
|
|
|
|
|
|
return this.engineInstance?.getTypeTreeData() ?? [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public getLevelTreeData(): any[] {
|
|
|
|
|
|
return this.engineInstance?.getLevelTreeData() ?? [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public getMajorTreeData(): any[] {
|
|
|
|
|
|
return this.engineInstance?.getMajorTreeData() ?? [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 18:01:31 +08:00
|
|
|
|
public getEngineInfo(): EngineInfo | null {
|
|
|
|
|
|
return this.engineInstance?.getEngineInfo() ?? null;
|
|
|
|
|
|
}
|
2026-01-28 17:19:36 +08:00
|
|
|
|
// ==================== 结束:模型树 ====================
|
|
|
|
|
|
|
2026-02-03 18:01:31 +08:00
|
|
|
|
// ==================== 路径漫游 ====================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 添加漫游点
|
|
|
|
|
|
* 将当前相机位置添加为一个漫游点
|
|
|
|
|
|
*/
|
|
|
|
|
|
public pathRoamingAddPoint(): void {
|
|
|
|
|
|
this.engineInstance?.pathRoamingAddPoint();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 删除指定索引的漫游点
|
|
|
|
|
|
* @param index 要删除的漫游点索引
|
|
|
|
|
|
*/
|
|
|
|
|
|
public pathRoamingRemovePoint(index: number): void {
|
|
|
|
|
|
this.engineInstance?.pathRoamingRemovePoint(index);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除所有漫游点
|
|
|
|
|
|
*/
|
|
|
|
|
|
public pathRoamingClearPoints(): void {
|
|
|
|
|
|
this.engineInstance?.pathRoamingClearPoints();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取所有漫游点
|
|
|
|
|
|
* @returns 漫游点数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
public pathRoamingGetPoints(): any[] {
|
|
|
|
|
|
return this.engineInstance?.pathRoamingGetPoints() ?? [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 跳转到指定漫游点
|
|
|
|
|
|
* @param index 目标漫游点索引
|
|
|
|
|
|
*/
|
|
|
|
|
|
public pathRoamingJumpToPoint(index: number): void {
|
|
|
|
|
|
this.engineInstance?.pathRoamingJumpToPoint(index);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 播放漫游
|
|
|
|
|
|
* @param options 播放选项,包含时长、循环、回调等配置
|
|
|
|
|
|
*/
|
|
|
|
|
|
public pathRoamingPlay(options?: {
|
|
|
|
|
|
duration?: number;
|
|
|
|
|
|
loop?: boolean;
|
|
|
|
|
|
onComplete?: () => void;
|
|
|
|
|
|
onPointComplete?: (pointIndex: number) => void;
|
|
|
|
|
|
}): void {
|
|
|
|
|
|
this.engineInstance?.pathRoamingPlay(options);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 停止漫游
|
|
|
|
|
|
*/
|
|
|
|
|
|
public pathRoamingStop(): void {
|
|
|
|
|
|
this.engineInstance?.pathRoamingStop();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 结束:路径漫游 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 构件操作 ====================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 隐藏选中构件
|
|
|
|
|
|
*/
|
|
|
|
|
|
public hideSelectedModels(): void {
|
|
|
|
|
|
this.engineInstance?.hideSelectedModels();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 半透明选中构件
|
|
|
|
|
|
*/
|
|
|
|
|
|
public translucentSelectedModels(): void {
|
|
|
|
|
|
this.engineInstance?.translucentSelectedModels();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 15:10:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 取消半透明
|
|
|
|
|
|
*/
|
|
|
|
|
|
public unTranslucentModel(): void {
|
|
|
|
|
|
this.engineInstance?.unTranslucentModel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 18:01:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 隔离选中构件(隐藏其他)
|
|
|
|
|
|
*/
|
|
|
|
|
|
public isolateSelectedModels(): void {
|
|
|
|
|
|
this.engineInstance?.isolateSelectedModels();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 半透明其他构件
|
|
|
|
|
|
*/
|
|
|
|
|
|
public translucentOtherModels(): void {
|
|
|
|
|
|
this.engineInstance?.translucentOtherModels();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 显示所有模型
|
|
|
|
|
|
*/
|
|
|
|
|
|
public showAllModels(): void {
|
|
|
|
|
|
this.engineInstance?.showAllModels();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量选择同类模型
|
|
|
|
|
|
*/
|
|
|
|
|
|
public batchSelectSameTypeModel(): void {
|
|
|
|
|
|
this.engineInstance?.batchSelectSameTypeModel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量选择同层模型
|
|
|
|
|
|
*/
|
|
|
|
|
|
public batchSelectSameLevelModel(): void {
|
|
|
|
|
|
this.engineInstance?.batchSelectSameLevelModel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量选择同层同类模型
|
|
|
|
|
|
*/
|
|
|
|
|
|
public batchSelectSameLevelTypeModel(): void {
|
|
|
|
|
|
this.engineInstance?.batchSelectSameLevelTypeModel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 结束:构件操作 ====================
|
|
|
|
|
|
|
2026-01-22 15:23:57 +08:00
|
|
|
|
/** 销毁引擎管理器 */
|
2025-12-04 18:41:11 +08:00
|
|
|
|
public destroy(): void {
|
2025-12-08 10:02:24 +08:00
|
|
|
|
if (this.engineInstance) {
|
|
|
|
|
|
this.engineInstance.destroy();
|
|
|
|
|
|
this.engineInstance = null;
|
2025-12-04 18:41:11 +08:00
|
|
|
|
}
|
2025-12-10 09:42:05 +08:00
|
|
|
|
if (this.rightKey) {
|
|
|
|
|
|
this.rightKey.destroy();
|
|
|
|
|
|
this.rightKey = null;
|
|
|
|
|
|
}
|
2026-01-22 15:23:57 +08:00
|
|
|
|
super.destroy();
|
2025-12-04 18:41:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|