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;