diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0859e33..1569f25 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -3,7 +3,8 @@ "allow": [ "Bash(cd /d/VscodeProject/zhengte.babylonjs-sdk && npm run build 2>&1 | head -50)", "Bash(find \"D:\\\\VscodeProject\\\\zhengte.babylonjs-sdk\\\\src\" -name \"*.ts\" -type f -exec grep -l \"class Dictionary\" {} \\\\;)", - "Bash(find \"D:\\\\VscodeProject\\\\zhengte.babylonjs-sdk\\\\src\" -name \"*.ts\" -type f -exec grep -l \"class.*Kernel\\\\|export.*kernel\" {} \\\\;)" + "Bash(find \"D:\\\\VscodeProject\\\\zhengte.babylonjs-sdk\\\\src\" -name \"*.ts\" -type f -exec grep -l \"class.*Kernel\\\\|export.*kernel\" {} \\\\;)", + "Bash(npm run *)" ] } } diff --git a/index.html b/index.html index 91bfccd..9a09715 100644 --- a/index.html +++ b/index.html @@ -856,9 +856,10 @@ // 监听放置区域点击事件 kernel.on('dropzone:click', async (dropzone_data) => { - console.log(dropzone_data); + console.log('点击了放置区域:', dropzone_data); - const { position, rotation } = dropzone_data.transform; + const { wallName, index, transform } = dropzone_data; + const { position, rotation } = transform; // 将模型放置到该区域 try { @@ -872,28 +873,35 @@ // 使用 for...of 循环以支持 await for (const event of result.data.events) { if (event.event_type === 'change_model') { - console.log( event.target_data); - - const {id, name,file_url, model_control_type, category } = event.target_data; + console.log(event.target_data); + + const {id, name, file_url, model_control_type, category } = event.target_data; console.log('替换百叶模型:', event); console.log('替换百叶模型类型:', category); + + // 生成唯一的模型ID + const modelId = id + '_' + Date.now(); + + // 先记录模型放置(会自动处理替换逻辑) + kernel.dropZone.recordModelPlacement(wallName, index, modelId); + + // 加载并放置模型 await kernel.model.add({ - modelId: id+Date.now(), + modelId: modelId, modelUrl: file_url, modelControlType: model_control_type, drag: { enable: true, axis: 'x', step: 0.1, - } - , + }, transform: { position: position, rotation: rotation, } }); - console.log(`百叶模型已替换为 ${name}`); + console.log(`百叶模型已放置为 ${name}`); } if (event.event_type === 'change_color') { diff --git a/src/babylonjs/AppDropZone.ts b/src/babylonjs/AppDropZone.ts index 6c8f6c3..9d38173 100644 --- a/src/babylonjs/AppDropZone.ts +++ b/src/babylonjs/AppDropZone.ts @@ -1,5 +1,6 @@ import { Scene, Vector3 } from '@babylonjs/core'; import { AppPlacementWall, WallConfig, PlacementZoneInfo } from './AppPlacementWall'; +import { AppModel } from './AppModel'; /** * 放置区域配置 @@ -19,20 +20,94 @@ export interface DropZoneConfig { export class AppDropZone { private scene: Scene; private placementWall: AppPlacementWall; + private appModel: AppModel | null = null; + + // 内部映射:放置区域 -> 模型ID + private zoneModelMap: Map = new Map(); + // 墙面 -> 当前分割数 + private wallDivisionsMap: Map = new Map(); constructor(scene: Scene) { this.scene = scene; this.placementWall = new AppPlacementWall(scene); } + /** + * 初始化模型管理器(内部使用) + */ + setModelManager(appModel: AppModel): void { + this.appModel = appModel; + } + /** * 生成放置区域 * @param config 配置参数 */ generateDropZones(config: DropZoneConfig): PlacementZoneInfo[] { + // 检查每个墙面的分割数是否改变,如果改变则卸载该墙面的所有模型 + config.walls.forEach(wall => { + const oldDivisions = this.wallDivisionsMap.get(wall.name); + if (oldDivisions !== undefined && oldDivisions !== wall.divisions) { + // 分割数改变,卸载该墙面的所有模型 + this.unloadWallModels(wall.name); + console.log(`墙面 ${wall.name} 分割数从 ${oldDivisions} 改为 ${wall.divisions},已卸载所有模型`); + } + // 更新分割数记录 + this.wallDivisionsMap.set(wall.name, wall.divisions); + }); + return this.placementWall.generatePlacementAreas(config); } + /** + * 卸载指定墙面的所有模型(内部方法) + */ + private unloadWallModels(wallName: string): void { + if (!this.appModel) return; + + const modelsToUnload: string[] = []; + + // 找出该墙面的所有模型 + this.zoneModelMap.forEach((modelId, zoneKey) => { + if (zoneKey.startsWith(`${wallName}[`)) { + modelsToUnload.push(modelId); + } + }); + + // 卸载模型并清除映射 + modelsToUnload.forEach(modelId => { + this.appModel!.removeByName(modelId); + // 清除该模型的映射 + this.zoneModelMap.forEach((value, key) => { + if (value === modelId) { + this.zoneModelMap.delete(key); + } + }); + }); + + if (modelsToUnload.length > 0) { + console.log(`已卸载墙面 ${wallName} 的 ${modelsToUnload.length} 个模型`); + } + } + + /** + * 记录模型放置到区域(内部方法,在点击事件中自动调用) + */ + recordModelPlacement(wallName: string, index: number, modelId: string): void { + const zoneKey = `${wallName}[${index}]`; + + // 检查该区域是否已有模型 + const existingModelId = this.zoneModelMap.get(zoneKey); + if (existingModelId && this.appModel) { + // 卸载旧模型 + console.log(`区域 ${zoneKey} 已有模型 ${existingModelId},将替换为 ${modelId}`); + this.appModel.removeByName(existingModelId); + } + + // 记录新模型 + this.zoneModelMap.set(zoneKey, modelId); + } + /** * 获取所有放置区域 */ @@ -79,6 +154,17 @@ export class AppDropZone { * 清除所有放置区域 */ clearAll(): void { + // 清除所有模型 + if (this.appModel) { + this.zoneModelMap.forEach(modelId => { + this.appModel!.removeByName(modelId); + }); + } + + // 清除映射 + this.zoneModelMap.clear(); + this.wallDivisionsMap.clear(); + this.placementWall.clearAll(); } diff --git a/src/babylonjs/MainApp.ts b/src/babylonjs/MainApp.ts index 20fa229..2d551e5 100644 --- a/src/babylonjs/MainApp.ts +++ b/src/babylonjs/MainApp.ts @@ -95,6 +95,8 @@ export class MainApp { this.appModel.initManagers(); // 在场景创建后初始化 AppDropZone this.appDropZone = new AppDropZone(this.appScene.object); + // 设置模型管理器引用 + this.appDropZone.setModelManager(this.appModel); this.update(); EventBridge.sceneReady({ scene: this.appScene.object }); } diff --git a/src/kernel/Adapter.ts b/src/kernel/Adapter.ts index 9c822c8..ef7c03d 100644 --- a/src/kernel/Adapter.ts +++ b/src/kernel/Adapter.ts @@ -374,6 +374,12 @@ export class KernelAdapter { inZone: false, zone: null }; + }, + /** + * 记录模型放置到区域(内部使用,自动处理替换) + */ + recordModelPlacement: (wallName: string, index: number, modelId: string): void => { + this.mainApp.appDropZone.recordModelPlacement(wallName, index, modelId); } };