From 8a427d3557303e4493c1777ca783ef6398408540 Mon Sep 17 00:00:00 2001 From: zguiy <1415466602@qq.com> Date: Tue, 19 May 2026 13:48:52 +0800 Subject: [PATCH] 1 --- index copy.js | 347 +++++++++++++++++++++++++++++++++++ index.html | 2 +- index.js | 16 +- src/babylonjs/GameManager.ts | 45 +++-- src/kernel/Adapter.ts | 1 + 5 files changed, 391 insertions(+), 20 deletions(-) create mode 100644 index copy.js diff --git a/index copy.js b/index copy.js new file mode 100644 index 0000000..c536224 --- /dev/null +++ b/index copy.js @@ -0,0 +1,347 @@ +import { EXRCubeTexture } from '@babylonjs/core'; +import apiConfig from './src/config.js'; + +// 存储 kernel 实例 +let kernelInstance = null; + +/** + * 初始化应用逻辑 - 注入 kernel 实例 + * @param {Object} kernel - SDK kernel 实例 + * @returns {Object} kernel 实例 + */ +export const initApp = (kernel) => { + if (!kernel) { + throw new Error('kernel 实例是必需的'); + } + kernelInstance = kernel; + console.log('应用逻辑已初始化,kernel 实例已注入'); + return kernelInstance; +}; + +/** + * 获取当前 kernel 实例 + */ +const getKernel = () => { + if (!kernelInstance) { + throw new Error('请先调用 initApp(kernel) 初始化 kernel 实例'); + } + return kernelInstance; +}; + +//初始化 +export const init = async (customConfig = {}) => { + const kernel = getKernel(); + + const defaultConfig = { + container: document.querySelector('#renderDom'), + modelUrlList: [], + env: { envPath: 'https://sdk.zguiy.com/resurces/hdr/hdr.env', intensity: 1.2, rotationY: 0.3, background: true }, + gizmo: { + position: false, + rotation: false, + scale: false + }, + outline: { + enable: true, + color: "#2196F3", + thickness: 1, + occlusionStrength: 0.1, + occlusionThreshold: 0.0002 + } + }; + + // 合并用户自定义配置 + const config = { ...defaultConfig, ...customConfig }; + kernel.init(config); +} + +//初始化加载模型 +export const getAutoLoadModelList = async () => { + const kernel = getKernel(); + + const url = apiConfig.getApiUrl('/api/models/auto-load/list') + console.log('API URL:', url) + console.log('apiConfig:', apiConfig) + const response = await fetch(url) + const data = await response.json() + const models = data.data // 这就是模型列表 + + models.forEach(model => { + + if (model.placement_zone) { + const { alpha, border_color, color, show_border, thickness, walls } = model.placement_zone + kernel.dropZone.setData({ + + color: color, + alpha: +alpha, + thickness: thickness, + showBorder: !show_border, + borderColor: border_color, + walls: walls + }); + } + + kernel.model.add({ + modelName: model.name + '_' + model.category, + modelId: model.category, + modelUrl: model.file_url, + modelControlType: model.model_control_type, + + }); + }) +} + +//获取放置区域 +export const getPlacementZone = async (sku) => { + const kernel = getKernel(); + + const response = await fetch(apiConfig.getApiUrl(`/api/product-configs/by-sku/${sku}`)); + const result = await response.json(); + if (result.code === 200) { + // await initPlacementZoneConfig(); + const { enable_placement_zone, wall_divisions } = result.data; + // const {position_x, position_y, position_z} = data; + if (enable_placement_zone && wall_divisions != undefined) { + + // 只清除旧的放置区域网格,不清除模型 + kernel.dropZone.clearZones(); + const divisions = wall_divisions.map(wall => ({ + name: wall.name, // 获取最后一个下划线后的部分 + divisions: wall.divisions + })) + + kernel.dropZone.updateDivisions(divisions); + // 显示放置区域 + kernel.dropZone.show(); + } + } +} + +//执行事件 +export const getEvent = async (dropzone_data, sku) => { + // 将模型放置到该区域 + try { + const response = await fetch(apiConfig.getApiUrl(`/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 + await executeEvent(dropzone_data, result) + } else { + console.log(`未查询到数据`); + } + } catch (error) { + console.error(`查询SKU配置或替换模型失败:`, error); + } +} + +//点击放置区域执行事件 一般是换配件 +export const executeEvent = async (dropzone_data, result) => { + const kernel = getKernel(); + + const { wallName, index, transform } = dropzone_data; + const { position, rotation } = transform; + +l + + for (const event of result.data.events) { + if (event.event_type === 'change_model') { + console.log(event.target_data); + + const { name, file_url, model_control_type, category } = event.target_data; + + + // 生成唯一的模型ID + const modelId = Date.now(); + + // 先记录模型放置(会自动处理替换逻辑) + kernel.dropZone.recordModelPlacement(wallName, index, name + '_' + modelId); + console.log(name + '_' + modelId); + // 加载并放置模型 + await kernel.model.add({ + modelName: name, + modelId: modelId, + modelUrl: file_url, + modelControlType: model_control_type, + drag: { + enable: true, + axis: rotation.y === 0 || rotation.y === 180 ? 'x' : 'z', + step: 0.1, + }, + transform: { + position: position, + rotation: rotation, + } + }); + + console.log(`百叶模型已放置为 ${name + '_' + category}`); + } + + if (event.event_type === 'change_color') { + const materialName = event.material_name; + const { color, color_map_url, normal_map_url, metallic, roughness } = event.target_data; + console.log('替换百叶模型颜色:', event.target_data); + + kernel.material.apply({ + target: materialName, + albedoColor: color, + albedoTexture: color_map_url, + normalMap: normal_map_url, + metallic: metallic, + roughness: roughness + }); + + console.log(`百叶模型颜色已替换为 ${color}`); + } + } +} + +//一般是换棚子/换颜色/设置放置区域 +export const executeEvent2 = async (result) => { + const kernel = getKernel(); + + // 检查是否有模型更换事件 + const hasModelChange = result.data.events.some(e => e.event_type === 'change_model'); + + // 检查新模型是否已经存在 + let modelAlreadyExists = false; + if (hasModelChange) { + const firstModelEvent = result.data.events.find(e => e.event_type === 'change_model'); + if (firstModelEvent && firstModelEvent.target_data) { + const { name, category } = firstModelEvent.target_data; + modelAlreadyExists = kernel.model.exists(name + '_' + category); + console.log(`检查模型 ${name + '_' + category} 是否存在:`, modelAlreadyExists); + } + } + + // 只有在需要更换模型且模型不存在时才清除 + if (hasModelChange && !modelAlreadyExists) { + console.log('模型不存在,执行清除操作'); + + kernel.model.removeAll(); + } else if (modelAlreadyExists) { + kernel.dropZone.hide(); + console.log('模型已存在,跳过清除操作,仅更新材质'); + } + + // 先处理所有 change_model 事件 + for (const event of result.data.events) { + if (event.event_type === 'change_model') { + const { target_data } = event; + console.log(event.target_data); + if (!target_data) { + console.error('change_model事件缺少target_data') + return; + }; + + const { id, name, file_url, model_control_type, category, placement_zone } = target_data; + // 如果模型已存在,跳过加载 + if (modelAlreadyExists) { + console.log(`模型 ${name + '_' + category} 已存在,跳过加载`); + continue; + } + + if (placement_zone) { + const { alpha, border_color, color, show_border, thickness, walls } = placement_zone + kernel.dropZone.setData({ + color: color, + alpha: +alpha, + thickness: thickness, + showBorder: !show_border, + borderColor: border_color, + walls: walls + }); + } + + // 加载并放置模型(使用 category 作为 modelId) + await kernel.model.add({ + modelName: name, + modelId: category, + modelUrl: file_url, + modelControlType: model_control_type, + }) + + console.log(`模型已放置为 ${name + '_' + category}`); + } + } + + // 等待模型加载完成后,再处理 change_color 事件 + for (const event of result.data.events) { + if (event.event_type === 'change_color') { + const materialName = event.material_name; + const { color, color_map_url, normal_map_url, metallic, roughness } = event.target_data; + console.log('替换百叶模型颜色:', event.target_data); + + kernel.material.apply({ + target: materialName, + albedoColor: color, + albedoTexture: color_map_url, + normalMap: normal_map_url, + metallic: metallic, + roughness: roughness + }); + + console.log(`百叶模型颜色已替换为 ${color}`); + } + } + +} + +//加载热点 +export const getHotspot = async () => { + const kernel = getKernel(); + + try { + // 从后端获取激活状态的热点列表 + const response = await fetch(apiConfig.getApiUrl('/api/hotspots?status=active&page=1&pageSize=100')); + const result = await response.json(); + + if (result.code === 200 && result.data.list.length > 0) { + // 将后端数据转换为 SDK 需要的格式 + const hotspots = result.data.list.map(item => ({ + id: item.id, + type: 'hotspot', + name: item.name, + meshName: item.name, // 可以根据实际情况调整 + icon: item.image_url, + position: [item.position_x, item.position_y, item.position_z], + radius: item.radius, + color: "#000000", + payload: { + skus: item.skus || [], + }, + })); + + // 渲染热点 + kernel.hotspot.render(hotspots); + console.log('热点渲染成功:', hotspots); + } else { + console.log('没有可用的热点数据'); + } + } catch (error) { + console.error('获取热点数据失败:', error); + } +} +//点击右侧按钮自动判断 +export const getProductConfig = async (sku) => { + try { + const response = await fetch(`${apiConfig.getApiUrl(`/api/product-configs/by-sku/${sku}`)}`); + const result = await response.json(); + if (result.code === 200) { + console.log(result.data); + const { enable_placement_zone } = result.data; + // await initPlacementZoneConfig(); + if (enable_placement_zone) { + getPlacementZone(sku) + } + else { + executeEvent2(result) + } + } + } catch (error) { + console.error('获取产品配置失败:', error); + } +} \ No newline at end of file diff --git a/index.html b/index.html index 92a51f5..5cb004c 100644 --- a/index.html +++ b/index.html @@ -336,7 +336,7 @@ - + diff --git a/index.js b/index.js index f5a8adf..bd1a5e6 100644 --- a/index.js +++ b/index.js @@ -119,6 +119,8 @@ export const getPlacementZone = async (sku) => { //执行事件 export const getEvent = async (dropzone_data, sku) => { + console.log(sku); + // 将模型放置到该区域 try { const response = await fetch(apiConfig.getApiUrl(`/api/product-configs/by-sku/${sku}`)); @@ -145,20 +147,16 @@ export const executeEvent = async (dropzone_data, result) => { const { wallName, index, transform } = dropzone_data; const { position, rotation } = transform; + // 第一次循环:处理 change_model for (const event of result.data.events) { if (event.event_type === 'change_model') { - console.log(event.target_data); - const { name, file_url, model_control_type, category } = event.target_data; - // 生成唯一的模型ID const modelId = Date.now(); - // 先记录模型放置(会自动处理替换逻辑) kernel.dropZone.recordModelPlacement(wallName, index, name + '_' + modelId); - console.log(name + '_' + modelId); - // 加载并放置模型 + await kernel.model.add({ modelName: name, modelId: modelId, @@ -175,13 +173,15 @@ export const executeEvent = async (dropzone_data, result) => { } }); - console.log(`百叶模型已放置为 ${name + '_' + category}`); + console.log(`百叶模型已放置为 ${name + '_' + modelId}`); } + } + // 第二次循环:处理 change_color(此时模型已加载完成) + for (const event of result.data.events) { if (event.event_type === 'change_color') { const materialName = event.material_name; const { color, color_map_url, normal_map_url, metallic, roughness } = event.target_data; - console.log('替换百叶模型颜色:', event.target_data); kernel.material.apply({ target: materialName, diff --git a/src/babylonjs/GameManager.ts b/src/babylonjs/GameManager.ts index 6187538..dbddba9 100644 --- a/src/babylonjs/GameManager.ts +++ b/src/babylonjs/GameManager.ts @@ -751,6 +751,7 @@ export class GameManager extends Monobehiver { */ applyMaterial(options: { target: string; + modelId?: string; albedoColor?: string; albedoTexture?: string; normalMap?: string; @@ -760,22 +761,44 @@ export class GameManager extends Monobehiver { }): void { this.updateDictionaries(); - - // 查找目标材质(支持精确匹配和前缀匹配) const targetMaterials: PBRMaterial[] = []; - this.materialDic.Values().forEach(material => { - if (material.name === options.target || material.name.startsWith(`${options.target}_`)) { - targetMaterials.push(material); - } - }); - if (targetMaterials.length === 0) { - console.warn(`Material not found: ${options.target}`); - return; + // 如果提供了 modelId,只查找该模型的材质 + if (options.modelId) { + // 获取该模型的所有 meshes + const modelMeshes = this.mainApp.appModel.modelDic.Get(options.modelId); + + if (!modelMeshes || modelMeshes.length === 0) { + console.warn(`Model not found: ${options.modelId}`); + return; + } + + // 遍历该模型的所有 mesh,查找匹配的材质 + modelMeshes.forEach((mesh: AbstractMesh) => { + if (mesh.material && mesh.material instanceof PBRMaterial) { + const material = mesh.material as PBRMaterial; + if (material.name === options.target || material.name.startsWith(`${options.target}_`)) { + // 避免重复添加 + if (!targetMaterials.includes(material)) { + targetMaterials.push(material); + } + } + } + }); + } else { + // 没有提供 modelId,全局查找(保持向后兼容) + this.materialDic.Values().forEach(material => { + if (material.name === options.target || material.name.startsWith(`${options.target}_`)) { + targetMaterials.push(material); + } + }); } - + if (targetMaterials.length === 0) { + console.warn(`Material not found: ${options.target}${options.modelId ? ` in model ${options.modelId}` : ''}`); + return; + } // 应用材质属性到目标材质 targetMaterials.forEach(material => { diff --git a/src/kernel/Adapter.ts b/src/kernel/Adapter.ts index a84af8b..a9d4161 100644 --- a/src/kernel/Adapter.ts +++ b/src/kernel/Adapter.ts @@ -93,6 +93,7 @@ export class KernelAdapter { */ apply: (options: { target: string; + modelId?: string; albedoColor?: string; albedoTexture?: string; normalMap?: string;