1
This commit is contained in:
347
index copy.js
Normal file
347
index copy.js
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -336,7 +336,7 @@
|
|||||||
<button class="option-btn" data-option="louver-1">SPFPDS13FTW</button>
|
<button class="option-btn" data-option="louver-1">SPFPDS13FTW</button>
|
||||||
<button class="option-btn" data-option="louver-2">SPFPDS13FTC</button>
|
<button class="option-btn" data-option="louver-2">SPFPDS13FTC</button>
|
||||||
<button class="option-btn" data-option="louver-3">3m下拉帘</button>
|
<button class="option-btn" data-option="louver-3">3m下拉帘</button>
|
||||||
<button class="option-btn" data-option="louver-4">SPFSW13FTC</button>
|
<button class="option-btn" data-option="louver-4">SPFSW13FTW</button>
|
||||||
<button class="option-btn" data-option="louver-4">SPFSW10FTW</button>
|
<button class="option-btn" data-option="louver-4">SPFSW10FTW</button>
|
||||||
<button class="option-btn" data-option="louver-4">SPFSW10FTC</button>
|
<button class="option-btn" data-option="louver-4">SPFSW10FTC</button>
|
||||||
<button class="option-btn" data-option="louver-4">SPFPDS10FTW</button>
|
<button class="option-btn" data-option="louver-4">SPFPDS10FTW</button>
|
||||||
|
|||||||
16
index.js
16
index.js
@ -119,6 +119,8 @@ export const getPlacementZone = async (sku) => {
|
|||||||
|
|
||||||
//执行事件
|
//执行事件
|
||||||
export const getEvent = async (dropzone_data, sku) => {
|
export const getEvent = async (dropzone_data, sku) => {
|
||||||
|
console.log(sku);
|
||||||
|
|
||||||
// 将模型放置到该区域
|
// 将模型放置到该区域
|
||||||
try {
|
try {
|
||||||
const response = await fetch(apiConfig.getApiUrl(`/api/product-configs/by-sku/${sku}`));
|
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 { wallName, index, transform } = dropzone_data;
|
||||||
const { position, rotation } = transform;
|
const { position, rotation } = transform;
|
||||||
|
|
||||||
|
// 第一次循环:处理 change_model
|
||||||
for (const event of result.data.events) {
|
for (const event of result.data.events) {
|
||||||
if (event.event_type === 'change_model') {
|
if (event.event_type === 'change_model') {
|
||||||
console.log(event.target_data);
|
|
||||||
|
|
||||||
const { name, file_url, model_control_type, category } = event.target_data;
|
const { name, file_url, model_control_type, category } = event.target_data;
|
||||||
|
|
||||||
|
|
||||||
// 生成唯一的模型ID
|
// 生成唯一的模型ID
|
||||||
const modelId = Date.now();
|
const modelId = Date.now();
|
||||||
|
|
||||||
// 先记录模型放置(会自动处理替换逻辑)
|
|
||||||
kernel.dropZone.recordModelPlacement(wallName, index, name + '_' + modelId);
|
kernel.dropZone.recordModelPlacement(wallName, index, name + '_' + modelId);
|
||||||
console.log(name + '_' + modelId);
|
|
||||||
// 加载并放置模型
|
|
||||||
await kernel.model.add({
|
await kernel.model.add({
|
||||||
modelName: name,
|
modelName: name,
|
||||||
modelId: modelId,
|
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') {
|
if (event.event_type === 'change_color') {
|
||||||
const materialName = event.material_name;
|
const materialName = event.material_name;
|
||||||
const { color, color_map_url, normal_map_url, metallic, roughness } = event.target_data;
|
const { color, color_map_url, normal_map_url, metallic, roughness } = event.target_data;
|
||||||
console.log('替换百叶模型颜色:', event.target_data);
|
|
||||||
|
|
||||||
kernel.material.apply({
|
kernel.material.apply({
|
||||||
target: materialName,
|
target: materialName,
|
||||||
|
|||||||
@ -751,6 +751,7 @@ export class GameManager extends Monobehiver {
|
|||||||
*/
|
*/
|
||||||
applyMaterial(options: {
|
applyMaterial(options: {
|
||||||
target: string;
|
target: string;
|
||||||
|
modelId?: string;
|
||||||
albedoColor?: string;
|
albedoColor?: string;
|
||||||
albedoTexture?: string;
|
albedoTexture?: string;
|
||||||
normalMap?: string;
|
normalMap?: string;
|
||||||
@ -760,22 +761,44 @@ export class GameManager extends Monobehiver {
|
|||||||
}): void {
|
}): void {
|
||||||
this.updateDictionaries();
|
this.updateDictionaries();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 查找目标材质(支持精确匹配和前缀匹配)
|
// 查找目标材质(支持精确匹配和前缀匹配)
|
||||||
const targetMaterials: PBRMaterial[] = [];
|
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) {
|
// 如果提供了 modelId,只查找该模型的材质
|
||||||
console.warn(`Material not found: ${options.target}`);
|
if (options.modelId) {
|
||||||
return;
|
// 获取该模型的所有 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 => {
|
targetMaterials.forEach(material => {
|
||||||
|
|||||||
@ -93,6 +93,7 @@ export class KernelAdapter {
|
|||||||
*/
|
*/
|
||||||
apply: (options: {
|
apply: (options: {
|
||||||
target: string;
|
target: string;
|
||||||
|
modelId?: string;
|
||||||
albedoColor?: string;
|
albedoColor?: string;
|
||||||
albedoTexture?: string;
|
albedoTexture?: string;
|
||||||
normalMap?: string;
|
normalMap?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user