From 52b369737a843994a0eaab0ab6314e504d0d2471 Mon Sep 17 00:00:00 2001 From: zguiy <1415466602@qq.com> Date: Wed, 13 May 2026 13:08:42 +0800 Subject: [PATCH] 1 --- index copy.html | 418 ++++++++++++++++++++++++++++++++++---- index.html | 118 +++++------ src/babylonjs/AppModel.ts | 88 +++++++- src/babylonjs/AppRay.ts | 25 ++- src/event/types.ts | 17 ++ src/kernel/Adapter.ts | 16 ++ 6 files changed, 576 insertions(+), 106 deletions(-) diff --git a/index copy.html b/index copy.html index 0d052e5..ae60d4f 100644 --- a/index copy.html +++ b/index copy.html @@ -67,6 +67,40 @@ border-bottom: 2px solid rgba(255, 255, 255, 0.2); } + .click-info { + background: rgba(76, 175, 80, 0.2); + border: 1px solid rgba(76, 175, 80, 0.5); + border-radius: 8px; + padding: 12px; + margin-bottom: 15px; + color: #fff; + font-size: 13px; + line-height: 1.6; + } + + .click-info-title { + font-weight: bold; + color: #4caf50; + margin-bottom: 8px; + font-size: 14px; + } + + .click-info-item { + margin-bottom: 4px; + display: flex; + gap: 8px; + } + + .click-info-label { + color: rgba(255, 255, 255, 0.7); + min-width: 70px; + } + + .click-info-value { + color: #fff; + word-break: break-all; + } + .config-category { margin-bottom: 15px; border-radius: 8px; @@ -227,12 +261,34 @@
0%
+ + +
选装选配
+ + +
@@ -273,7 +329,7 @@
- + @@ -290,13 +346,16 @@
- +
+ + +
@@ -390,6 +449,10 @@ diff --git a/index.html b/index.html index 4114dcc..025c4c6 100644 --- a/index.html +++ b/index.html @@ -516,58 +516,10 @@ await hotspotRequest(); }) + let sku = "" const skuToFunc = async (currentText) => { - // 根据 SKU 查询配置和事件 - try { - const response = await fetch(`http://localhost:3000/api/product-configs/by-sku/${currentText}`); - const result = await response.json(); - - if (result.code === 200 && result.data) { - console.log('SKU配置数据:', result.data); - console.log('关联事件:', result.data.events); - placementWall(1); - // 使用 for...of 循环以支持 await - for (const event of result.data.events) { - if (event.event_type === 'change_model') { - const { file_url, model_control_type, category } = event.target_data; - console.log('替换百叶模型:', event); - - await kernel.model.replace({ - modelId: category, - modelUrl: file_url, - modelControlType: model_control_type, - drag: { - enable: true, - axis: 'x', - step: 0.1, - }, - }); - - console.log(`百叶模型已替换为 ${currentText}`); - } - - if (event.event_type === 'change_color') { - const materialName = event.material_name; - const { color, color_map_url, normal_map_url } = event.target_data; - console.log('替换百叶模型颜色:', event.target_data); - - kernel.material.apply({ - target: materialName, - albedoColor: color, - albedoTexture: color_map_url, - normalMap: normal_map_url, - }); - - console.log(`百叶模型颜色已替换为 ${color}`); - } - } - } else { - console.log(`未查询到数据`); - } - } catch (error) { - console.error(`查询SKU配置或替换模型失败:`, error); - } - + sku = currentText; + placementWall(1); } @@ -899,12 +851,64 @@ // 监听放置区域点击事件 - kernel.on('dropzone:click', (data) => { - console.log('点击了放置区域:', data); - console.log('中心点:', data.center); - console.log('宽度:', data.width); - console.log('高度:', data.height); - console.log('法线:', data.normal); + kernel.on('dropzone:click', async (dropzone_data) => { + const { position,rotation } = dropzone_data.transform; + + // 将模型放置到该区域 + try { + const response = await fetch(`http://localhost:3000/api/product-configs/by-sku/${sku}`); + const result = await response.json(); + + if (result.code === 200 && result.data) { + console.log('SKU配置数据:', result.data); + console.log('关联事件:', result.data.events); + + // 使用 for...of 循环以支持 await + for (const event of result.data.events) { + if (event.event_type === 'change_model') { + const { file_url, model_control_type, category } = event.target_data; + console.log('替换百叶模型:', event); + + await kernel.model.replace({ + modelId: category, + modelUrl: file_url, + modelControlType: model_control_type, + drag: { + enable: true, + axis: 'x', + step: 0.1, + } + , + transform: { + position: position, + rotation: rotation, + } + }); + + console.log(`百叶模型已替换为 ${currentText}`); + } + + if (event.event_type === 'change_color') { + const materialName = event.material_name; + const { color, color_map_url, normal_map_url } = event.target_data; + console.log('替换百叶模型颜色:', event.target_data); + + kernel.material.apply({ + target: materialName, + albedoColor: color, + albedoTexture: color_map_url, + normalMap: normal_map_url, + }); + + console.log(`百叶模型颜色已替换为 ${color}`); + } + } + } else { + console.log(`未查询到数据`); + } + } catch (error) { + console.error(`查询SKU配置或替换模型失败:`, error); + } }); diff --git a/src/babylonjs/AppModel.ts b/src/babylonjs/AppModel.ts index 89c9a00..bcf117e 100644 --- a/src/babylonjs/AppModel.ts +++ b/src/babylonjs/AppModel.ts @@ -24,11 +24,18 @@ type ModelConfig = { type ModelControlType = 'rotation' | 'color'; +type ModelTransform = { + position?: { x: number; y: number; z: number }; + rotation?: { x: number; y: number; z: number }; + scale?: { x: number; y: number; z: number }; +}; + type ModelMetadata = { modelId: string; modelUrl: string; modelControlType?: ModelControlType; drag?: DragConfig; + transform?: ModelTransform; }; /** @@ -224,14 +231,15 @@ export class AppModel extends Monobehiver { modelConfig.modelId, modelConfig.modelUrl, modelConfig.modelControlType, - modelConfig.drag + modelConfig.drag, + modelConfig.transform ); } /** * 添加单个模型 */ - private async addSingle(modelName: string, modelUrl: string, modelControlType?: ModelControlType, drag?: DragConfig): Promise { + private async addSingle(modelName: string, modelUrl: string, modelControlType?: ModelControlType, drag?: DragConfig, transform?: ModelTransform): Promise { // 检查是否已存在 const existingMeshes = this.modelDic.Get(modelName); if (existingMeshes?.length && !existingMeshes[0].isDisposed()) { @@ -254,9 +262,15 @@ export class AppModel extends Monobehiver { modelId: modelName, modelUrl: modelUrl, modelControlType: modelControlType, - drag: drag + drag: drag, + transform: transform }); + // 应用 transform + if (transform) { + this.applyTransform(modelName, transform); + } + // 配置拖拽功能 if (drag) { this.mainApp.appModelDrag?.configureDrag(modelName, drag); @@ -283,7 +297,7 @@ export class AppModel extends Monobehiver { EventBridge.modelLoadProgress({ loaded: 0, total, progress: 0, percentage: 0 }); for (let i = 0; i < models.length; i++) { - const { modelId, modelUrl, modelControlType, drag } = models[i]; + const { modelId, modelUrl, modelControlType, drag, transform } = models[i]; const result = await this.loadSingleModel(modelUrl, (event) => { this.emitProgress(i, total, modelUrl, event); @@ -298,9 +312,15 @@ export class AppModel extends Monobehiver { modelId: modelId, modelUrl: modelUrl, modelControlType: modelControlType, - drag: drag + drag: drag, + transform: transform }); + // 应用 transform + if (transform) { + this.applyTransform(modelId, transform); + } + // 配置拖拽功能 if (drag) { this.mainApp.appModelDrag?.configureDrag(modelId, drag); @@ -426,7 +446,8 @@ export class AppModel extends Monobehiver { modelConfig.modelId, modelConfig.modelUrl, modelConfig.modelControlType, - modelConfig.drag + modelConfig.drag, + modelConfig.transform ); } @@ -591,4 +612,59 @@ export class AppModel extends Monobehiver { mesh.scaling.z = scale.z; }); } + + /** + * 将模型放置到指定的放置区域 + * @param modelId 模型ID + * @param zoneInfo 放置区域信息 + * @param offsetDistance 距离墙面的偏移距离(默认0.1,正数向外) + */ + placeToZone(modelId: string, zoneInfo: any, offsetDistance: number = 0): void { + const meshes = this.modelDic.Get(modelId); + if (!meshes?.length) { + console.warn(`Model not found: ${modelId}`); + return; + } + + // 计算放置位置:中心点 + 法线方向的偏移 + const targetPosition = zoneInfo.center.add(zoneInfo.normal.scale(offsetDistance)); + + // 计算旋转角度:让模型面向墙面(法线的反方向) + const targetDirection = zoneInfo.normal.scale(-1); + const angle = Math.atan2(targetDirection.x, targetDirection.z); + + this.getModelTransformTargets(meshes).forEach(mesh => { + // 设置位置 + mesh.position.copyFrom(targetPosition); + + // 设置旋转(只旋转Y轴,让模型面向墙面) + if (mesh.rotationQuaternion) { + mesh.rotationQuaternion = Quaternion.FromEulerAngles(0, angle, 0); + } else { + mesh.rotation.set(0, angle, 0); + } + }); + } + + /** + * 应用 transform 到模型 + * @param modelId 模型ID + * @param transform 变换信息 + */ + private applyTransform(modelId: string, transform: ModelTransform): void { + // 应用位置 + if (transform.position) { + this.setPosition(modelId, transform.position); + } + + // 应用旋转(角度制) + if (transform.rotation) { + this.setRotation(modelId, transform.rotation, true); + } + + // 应用缩放 + if (transform.scale) { + this.setScale(modelId, transform.scale); + } + } } diff --git a/src/babylonjs/AppRay.ts b/src/babylonjs/AppRay.ts index ecb9507..6358180 100644 --- a/src/babylonjs/AppRay.ts +++ b/src/babylonjs/AppRay.ts @@ -92,6 +92,12 @@ class AppRay extends Monobehiver { const zones = this.mainApp.appDropZone.getPlacementZones(); const clickedZone = zones.find(zone => zone.mesh === pickInfo.pickedMesh); if (clickedZone) { + // 计算该放置区域的目标位置和旋转 + const offsetDistance = 0.1; + const targetPosition = clickedZone.center.add(clickedZone.normal.scale(offsetDistance)); + const targetDirection = clickedZone.normal.scale(-1); + const angle = Math.atan2(targetDirection.x, targetDirection.z); + EventBridge.dropZoneClick({ wallName: clickedZone.wallName, index: clickedZone.index, @@ -99,7 +105,24 @@ class AppRay extends Monobehiver { width: clickedZone.width, height: clickedZone.height, normal: clickedZone.normal, - mesh: clickedZone.mesh + mesh: clickedZone.mesh, + transform: { + position: { + x: targetPosition.x, + y: targetPosition.y, + z: targetPosition.z + }, + rotation: { + x: 0, + y: angle * 180 / Math.PI, // 转换为角度 + z: 0 + }, + scale: { + x: 1, + y: 1, + z: 1 + } + } }); return; } diff --git a/src/event/types.ts b/src/event/types.ts index fc3dd68..fc9ff1b 100644 --- a/src/event/types.ts +++ b/src/event/types.ts @@ -55,4 +55,21 @@ export type DropZoneClickPayload = { height: number; normal: any; mesh: any; + transform: { + position: { + x: number; + y: number; + z: number; + }; + rotation: { + x: number; + y: number; + z: number; + }; + scale: { + x: number; + y: number; + z: number; + }; + }; }; diff --git a/src/kernel/Adapter.ts b/src/kernel/Adapter.ts index 5a9dbd1..9c822c8 100644 --- a/src/kernel/Adapter.ts +++ b/src/kernel/Adapter.ts @@ -176,6 +176,22 @@ export class KernelAdapter { */ scale: (options: { modelId: string; vector3: { x: number; y: number; z: number } }): void => { this.mainApp.appModel.setScale(options.modelId, options.vector3); + }, + /** + * 将模型放置到指定的放置区域 + * @param options 放置配置 { modelId: string, zoneInfo: any, offsetDistance?: number } + * @example + * // 监听放置区域点击,将模型放置到该区域 + * kernel.on('dropzone:click', (zoneInfo) => { + * kernel.transform.placeToZone({ + * modelId: "myModel", + * zoneInfo: zoneInfo, + * offsetDistance: 0.1 // 可选,距离墙面的偏移距离 + * }); + * }); + */ + placeToZone: (options: { modelId: string; zoneInfo: any; offsetDistance?: number }): void => { + this.mainApp.appModel.placeToZone(options.modelId, options.zoneInfo, options.offsetDistance); } };