1
This commit is contained in:
@ -209,6 +209,10 @@ const executeEvent = async (dropzone_data, result, sku) => {
|
||||
enable: true,
|
||||
axis: rotation.y === 0 || rotation.y === 180 ? 'x' : 'z',
|
||||
step: 0.1,
|
||||
snapToZone: true, // 拖拽吸附到最近的分割区域
|
||||
returnWhenOutOfBounds: true, // 拖拽到区域外时返回原位置
|
||||
handleOccupiedZone: true, // 处理已占用区域(false=允许重叠)
|
||||
occupiedZoneAction: 'replace' // 当开关3=true时的行为:'return'=返回原位置,'replace'=替换
|
||||
},
|
||||
transform: {
|
||||
position: position,
|
||||
@ -418,8 +422,8 @@ const getProductConfig = async (sku) => {
|
||||
}
|
||||
|
||||
// API 配置
|
||||
const API_BASE_URL = 'https://ztserver.zguiy.com';
|
||||
|
||||
//const API_BASE_URL = 'https://ztserver.zguiy.com';
|
||||
const API_BASE_URL = 'http://localhost:26517';
|
||||
const getApiUrl = (path) => {
|
||||
return `${API_BASE_URL}${path}`;
|
||||
};
|
||||
|
||||
@ -453,7 +453,7 @@
|
||||
<div class="category-content expanded">
|
||||
<div class="option-group">
|
||||
<button class="option-btn" data-option="color-1">SPFPDS13FTW</button>
|
||||
<button class="option-btn" data-option="color-2">SPFPDS13FTC</button>
|
||||
<button class="option-btn" data-option="color-2">SPFSW13FTC</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -870,10 +870,6 @@
|
||||
|
||||
console.log(kernel);
|
||||
|
||||
// 监听放置区域点击事件
|
||||
kernel.on('dropzone:click', function (dropzone_data) {
|
||||
window.AppLogic.getEvent(dropzone_data, sku);
|
||||
});
|
||||
|
||||
// 存储当前选中的材质名和网格
|
||||
var currentMaterialName = '';
|
||||
|
||||
@ -221,33 +221,40 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 进度条样式 */
|
||||
/* 加载遮罩样式 */
|
||||
#progress-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
width: 0%;
|
||||
height: 10px;
|
||||
background: linear-gradient(90deg, #4CAF50, #45a049);
|
||||
border-radius: 5px;
|
||||
transition: width 0.1s ease;
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
border-top-color: #4CAF50;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
#progress-text {
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@ -258,8 +265,8 @@
|
||||
<div id="canvas-container">
|
||||
<canvas id="renderDom"></canvas>
|
||||
<div id="progress-container" style="display: none;">
|
||||
<div id="progress-bar"></div>
|
||||
<div id="progress-text">0%</div>
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-text">加载中...</div>
|
||||
</div>
|
||||
|
||||
<!-- 生成放置区域按钮 -->
|
||||
@ -847,9 +854,25 @@
|
||||
|
||||
// 监听放置区域点击事件
|
||||
kernel.on('dropzone:click', async (dropzone_data) => {
|
||||
// 显示进度条
|
||||
const progressContainer = document.getElementById('progress-container');
|
||||
if (progressContainer) {
|
||||
progressContainer.style.display = 'flex';
|
||||
}
|
||||
|
||||
getEvent(dropzone_data, sku)
|
||||
});
|
||||
|
||||
// 监听模型加载完成事件
|
||||
kernel.on('model:loaded', (data) => {
|
||||
console.log('模型加载完成', data);
|
||||
// 隐藏进度条
|
||||
const progressContainer = document.getElementById('progress-container');
|
||||
if (progressContainer) {
|
||||
progressContainer.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -79877,10 +79877,8 @@ discard;}}
|
||||
}
|
||||
/** 初始化灯光并开启阴影 */
|
||||
Awake() {
|
||||
new DirectionalLight(
|
||||
"mainLight",
|
||||
new Vector3(0, -0.5, -1)
|
||||
);
|
||||
const light = new DirectionalLight("sun", new Vector3(-1, -2, -1), this.mainApp.appScene.object);
|
||||
light.intensity = 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -354748,6 +354746,22 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
}
|
||||
if (drag) {
|
||||
this.mainApp.appModelDrag?.configureDrag(modelName + "_" + modelId, drag);
|
||||
if (this.mainApp.appDropZone) {
|
||||
let modelWallName = null;
|
||||
const fullModelId = modelName + "_" + modelId;
|
||||
this.mainApp.appDropZone["zoneModelMap"]?.forEach((id, zoneKey) => {
|
||||
if (id === fullModelId) {
|
||||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||||
if (match) {
|
||||
modelWallName = match[1];
|
||||
}
|
||||
}
|
||||
});
|
||||
if (modelWallName && this.mainApp.appDropZone.isWallFull(modelWallName)) {
|
||||
console.log(`[拖拽控制] 墙面 ${modelWallName} 已满,禁用模型 ${fullModelId} 的拖拽`);
|
||||
this.mainApp.appModelDrag?.setDragEnabled(fullModelId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mainApp.gameManager?.updateDictionaries();
|
||||
EventBridge.modelLoaded({ urls: [modelUrl] });
|
||||
@ -354907,6 +354921,9 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
this.modelDic.Remove(modelName);
|
||||
this.modelMetadataDic.Remove(modelName);
|
||||
this.mainApp.gameManager?.updateDictionaries();
|
||||
if (this.mainApp.appDropZone && typeof this.mainApp.appDropZone.notifyModelRemoved === "function") {
|
||||
this.mainApp.appDropZone.notifyModelRemoved(modelName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
@ -355067,9 +355084,10 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
* 将模型放置到指定的放置区域
|
||||
* @param modelId 模型ID
|
||||
* @param zoneInfo 放置区域信息
|
||||
* @param offsetDistance 距离墙面的偏移距离(默认0.1,正数向外)
|
||||
* @param offsetDistance 距离墙面的偏移距离(默认0,正数向外)
|
||||
*/
|
||||
placeToZone(modelId, zoneInfo, offsetDistance = 0) {
|
||||
console.log(zoneInfo);
|
||||
const meshes = this.modelDic.Get(modelId);
|
||||
if (!meshes?.length) {
|
||||
console.warn(`Model not found: ${modelId}`);
|
||||
@ -355119,6 +355137,12 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
highlightLayer = null;
|
||||
originalMaterial = null;
|
||||
highlightedMesh = null;
|
||||
pointerDownTime = 0;
|
||||
pointerDownPickInfo = null;
|
||||
longPressTimer = null;
|
||||
longPressThreshold = 500;
|
||||
// 长按阈值(毫秒)
|
||||
isLongPress = false;
|
||||
constructor(mainApp) {
|
||||
super(mainApp);
|
||||
}
|
||||
@ -355143,29 +355167,85 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
}
|
||||
if (type === PointerEventTypes.POINTERDOWN) {
|
||||
this.oldPoint.set(pointerEvent.clientX, 0, pointerEvent.clientY);
|
||||
this.pointerDownTime = Date.now();
|
||||
this.pointerDownPickInfo = pickInfo;
|
||||
this.isLongPress = false;
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
}
|
||||
this.longPressTimer = setTimeout(() => {
|
||||
this.isLongPress = true;
|
||||
this.handleLongPress(pointerEvent, this.pointerDownPickInfo);
|
||||
}, this.longPressThreshold);
|
||||
} else if (type === PointerEventTypes.POINTERUP) {
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = null;
|
||||
}
|
||||
this.newPoint.set(pointerEvent.clientX, 0, pointerEvent.clientY);
|
||||
const distance = Vector3.Distance(this.oldPoint, this.newPoint);
|
||||
if (distance < 5) {
|
||||
if (this.isLongPress) {
|
||||
console.log("[长按] 松手,隐藏分割区域");
|
||||
this.mainApp.appDropZone.hide();
|
||||
}
|
||||
if (distance < 5 && !this.isLongPress) {
|
||||
this.handleSingleClick(pointerEvent, pickInfo);
|
||||
}
|
||||
this.isLongPress = false;
|
||||
} else if (type === PointerEventTypes.POINTERMOVE) {
|
||||
this.newPoint.set(pointerEvent.clientX, 0, pointerEvent.clientY);
|
||||
const distance = Vector3.Distance(this.oldPoint, this.newPoint);
|
||||
if (distance > 5 && this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// 处理长按
|
||||
handleLongPress(evt, pickInfo) {
|
||||
if (pickInfo && pickInfo.hit && pickInfo.pickedMesh) {
|
||||
if (pickInfo.pickedMesh.metadata?.type === "hotspot") {
|
||||
return;
|
||||
}
|
||||
if (pickInfo.pickedMesh.name.startsWith("placement_")) {
|
||||
return;
|
||||
}
|
||||
const modelName = this.mainApp.appModel.findModelNameByMesh(pickInfo.pickedMesh);
|
||||
if (modelName) {
|
||||
const wallName = this.findModelWallName(modelName);
|
||||
if (wallName) {
|
||||
console.log(`[长按] 模型 ${modelName} 位于墙面 ${wallName},显示该墙面的放置区域`);
|
||||
this.mainApp.appDropZone.showWall(wallName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 查找模型所在的墙面
|
||||
findModelWallName(modelId) {
|
||||
const zoneModelMap = this.mainApp.appDropZone["zoneModelMap"];
|
||||
if (!zoneModelMap) return null;
|
||||
for (const [zoneKey, id] of zoneModelMap.entries()) {
|
||||
if (id === modelId) {
|
||||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// 处理单击
|
||||
handleSingleClick(evt, pickInfo) {
|
||||
if (pickInfo && pickInfo.hit && pickInfo.pickedMesh && pickInfo.pickedPoint) {
|
||||
if (pickInfo.pickedMesh.metadata?.type === "hotspot") {
|
||||
return;
|
||||
}
|
||||
if (pickInfo.pickedMesh.name === "gridGround" || pickInfo.pickedMesh.name === "ground" || pickInfo.pickedMesh.name === "sphere_yundong_mesh") {
|
||||
return;
|
||||
}
|
||||
if (pickInfo.pickedMesh.name.startsWith("placement_")) {
|
||||
const zones = this.mainApp.appDropZone.getPlacementZones();
|
||||
const clickedZone = zones.find((zone) => zone.mesh === pickInfo.pickedMesh);
|
||||
if (clickedZone) {
|
||||
const offsetDistance = 0.05;
|
||||
const offsetDistance = 0;
|
||||
const targetPosition = clickedZone.center.add(clickedZone.normal.scale(offsetDistance));
|
||||
const targetDirection = clickedZone.normal.scale(-1);
|
||||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||||
@ -355186,7 +355266,6 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
rotation: {
|
||||
x: 0,
|
||||
y: angle * 180 / Math.PI,
|
||||
// 转换为角度
|
||||
z: 0
|
||||
},
|
||||
scale: {
|
||||
@ -355200,6 +355279,7 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
}
|
||||
}
|
||||
this.mainApp.appDomTo3D.hideAll();
|
||||
this.mainApp.appDropZone.hide();
|
||||
const materialName = pickInfo.pickedMesh.material?.name || "";
|
||||
const holdingShift = Boolean(evt.shiftKey);
|
||||
const modelMeshes = this.mainApp.appModel.getModelMeshesByMesh(pickInfo.pickedMesh);
|
||||
@ -355222,7 +355302,6 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
modelControlType: modelMetadata?.modelControlType
|
||||
});
|
||||
} else {
|
||||
console.log(1111);
|
||||
this.mainApp.appSelectionOutline.clear();
|
||||
this.mainApp.appPositionGizmo.detach();
|
||||
this.mainApp.appDomTo3D.hideAll();
|
||||
@ -356535,11 +356614,44 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
}
|
||||
const pointerDragBehavior = new PointerDragBehavior({ dragAxis });
|
||||
pointerDragBehavior.useObjectOrientationForDragging = false;
|
||||
let dragStartPosition = null;
|
||||
let hasShownZones = false;
|
||||
pointerDragBehavior.onDragStartObservable.add(() => {
|
||||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||||
if (meshes && meshes.length > 0) {
|
||||
dragStartPosition = meshes[0].position.clone();
|
||||
}
|
||||
this.disableCameraControl();
|
||||
hasShownZones = false;
|
||||
});
|
||||
pointerDragBehavior.onDragObservable.add((event) => {
|
||||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||||
if (meshes && meshes.length > 0 && dragStartPosition) {
|
||||
const distance = Vector3.Distance(dragStartPosition, meshes[0].position);
|
||||
if (distance > 0.01 && !hasShownZones && dragInfo.config.snapToZone) {
|
||||
this.showZonesForModel(modelId);
|
||||
hasShownZones = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
pointerDragBehavior.onDragEndObservable.add(() => {
|
||||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||||
let hasMoved = false;
|
||||
if (meshes && meshes.length > 0 && dragStartPosition) {
|
||||
const distance = Vector3.Distance(dragStartPosition, meshes[0].position);
|
||||
hasMoved = distance > 0.01;
|
||||
}
|
||||
this.enableCameraControl();
|
||||
if (hasMoved) {
|
||||
if (dragInfo.config.snapToZone && hasShownZones) {
|
||||
this.hideZonesForModel(modelId);
|
||||
this.snapModelToZone(modelId);
|
||||
} else {
|
||||
this.updateModelZoneMapping(modelId);
|
||||
}
|
||||
}
|
||||
dragStartPosition = null;
|
||||
hasShownZones = false;
|
||||
});
|
||||
return pointerDragBehavior;
|
||||
}
|
||||
@ -356632,6 +356744,259 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
camera.attachControl(canvas, true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 显示模型所在墙面的分割区域
|
||||
* @param modelId 模型ID
|
||||
*/
|
||||
showZonesForModel(modelId) {
|
||||
const appDropZone = this.mainApp.appDropZone;
|
||||
if (!appDropZone) return;
|
||||
let wallName = null;
|
||||
appDropZone["zoneModelMap"]?.forEach((id, zoneKey) => {
|
||||
if (id === modelId) {
|
||||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||||
if (match) {
|
||||
wallName = match[1];
|
||||
}
|
||||
}
|
||||
});
|
||||
if (wallName) {
|
||||
console.log(`[拖拽吸附] 显示墙面 ${wallName} 的分割区域`);
|
||||
appDropZone.showWall(wallName);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 隐藏分割区域
|
||||
* @param modelId 模型ID
|
||||
*/
|
||||
hideZonesForModel(modelId) {
|
||||
const appDropZone = this.mainApp.appDropZone;
|
||||
if (!appDropZone) return;
|
||||
console.log(`[拖拽吸附] 隐藏分割区域`);
|
||||
appDropZone.hide();
|
||||
}
|
||||
/**
|
||||
* 将模型吸附到最近的分割区域
|
||||
* @param modelId 模型ID
|
||||
*/
|
||||
snapModelToZone(modelId) {
|
||||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||||
if (!meshes || !meshes.length) return;
|
||||
const rootMesh = meshes[0];
|
||||
const appDropZone = this.mainApp.appDropZone;
|
||||
if (!appDropZone) return;
|
||||
let wallName = null;
|
||||
let originalZoneIndex = -1;
|
||||
appDropZone["zoneModelMap"]?.forEach((id, zoneKey) => {
|
||||
if (id === modelId) {
|
||||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||||
if (match) {
|
||||
wallName = match[1];
|
||||
originalZoneIndex = parseInt(match[2]);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!wallName) return;
|
||||
const wallZones = appDropZone.getZonesByWall(wallName);
|
||||
if (!wallZones.length) return;
|
||||
let closestZoneIndex = -1;
|
||||
let minDistance = Number.POSITIVE_INFINITY;
|
||||
wallZones.forEach((zone, index) => {
|
||||
const distance = rootMesh.position.subtract(zone.center).length();
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closestZoneIndex = index;
|
||||
}
|
||||
});
|
||||
if (closestZoneIndex === -1) return;
|
||||
const dragInfo = this.modelDragMap.get(modelId);
|
||||
const returnWhenOutOfBounds = dragInfo?.config.returnWhenOutOfBounds ?? false;
|
||||
const handleOccupiedZone = dragInfo?.config.handleOccupiedZone ?? false;
|
||||
const occupiedZoneAction = dragInfo?.config.occupiedZoneAction ?? "return";
|
||||
let isOutOfBounds = false;
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
let maxX = Number.NEGATIVE_INFINITY;
|
||||
let minZ = Number.POSITIVE_INFINITY;
|
||||
let maxZ = Number.NEGATIVE_INFINITY;
|
||||
wallZones.forEach((zone) => {
|
||||
const halfWidth = zone.width / 2;
|
||||
if (Math.abs(zone.normal.x) > 0.5) {
|
||||
minZ = Math.min(minZ, zone.center.z - halfWidth);
|
||||
maxZ = Math.max(maxZ, zone.center.z + halfWidth);
|
||||
} else if (Math.abs(zone.normal.z) > 0.5) {
|
||||
minX = Math.min(minX, zone.center.x - halfWidth);
|
||||
maxX = Math.max(maxX, zone.center.x + halfWidth);
|
||||
}
|
||||
});
|
||||
const currentPos = rootMesh.position;
|
||||
if (minX !== Number.POSITIVE_INFINITY && maxX !== Number.NEGATIVE_INFINITY) {
|
||||
if (currentPos.x < minX || currentPos.x > maxX) {
|
||||
isOutOfBounds = true;
|
||||
}
|
||||
}
|
||||
if (minZ !== Number.POSITIVE_INFINITY && maxZ !== Number.NEGATIVE_INFINITY) {
|
||||
if (currentPos.z < minZ || currentPos.z > maxZ) {
|
||||
isOutOfBounds = true;
|
||||
}
|
||||
}
|
||||
if (isOutOfBounds) {
|
||||
console.log(`[拖拽吸附] 模型 ${modelId} 超出边界`);
|
||||
if (returnWhenOutOfBounds) {
|
||||
console.log(`[拖拽吸附] 启用边界返回,回到原区域 ${originalZoneIndex}`);
|
||||
if (originalZoneIndex !== -1) {
|
||||
const originalZone = wallZones[originalZoneIndex];
|
||||
if (originalZone) {
|
||||
const offsetDistance2 = -0.05;
|
||||
const returnPosition = originalZone.center.add(originalZone.normal.scale(offsetDistance2));
|
||||
rootMesh.position.copyFrom(returnPosition);
|
||||
const targetDirection2 = originalZone.normal.scale(-1);
|
||||
const angle2 = Math.atan2(targetDirection2.x, targetDirection2.z);
|
||||
rootMesh.rotation.y = angle2;
|
||||
console.log(`[拖拽吸附] 模型 ${modelId} 已返回原区域 ${originalZoneIndex}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(`[拖拽吸附] 未启用边界返回,保持当前位置,不做吸附`);
|
||||
this.updateModelZoneMapping(modelId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const targetZone = wallZones[closestZoneIndex];
|
||||
const targetZoneKey = `${wallName}[${closestZoneIndex}]`;
|
||||
const occupyingModelId = appDropZone["zoneModelMap"]?.get(targetZoneKey);
|
||||
if (occupyingModelId && occupyingModelId !== modelId) {
|
||||
console.log(`[拖拽吸附] 目标区域 ${closestZoneIndex} 已被模型 ${occupyingModelId} 占用`);
|
||||
if (handleOccupiedZone) {
|
||||
if (occupiedZoneAction === "return") {
|
||||
console.log(`[拖拽吸附] 配置为返回原位置,回到区域 ${originalZoneIndex}`);
|
||||
if (originalZoneIndex !== -1) {
|
||||
const originalZone = wallZones[originalZoneIndex];
|
||||
if (originalZone) {
|
||||
const offsetDistance2 = -0.05;
|
||||
const returnPosition = originalZone.center.add(originalZone.normal.scale(offsetDistance2));
|
||||
rootMesh.position.copyFrom(returnPosition);
|
||||
const targetDirection2 = originalZone.normal.scale(-1);
|
||||
const angle2 = Math.atan2(targetDirection2.x, targetDirection2.z);
|
||||
rootMesh.rotation.y = angle2;
|
||||
console.log(`[拖拽吸附] 模型 ${modelId} 返回原区域 ${originalZoneIndex}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (occupiedZoneAction === "replace") {
|
||||
console.log(`[拖拽吸附] 配置为替换模型,将替换模型 ${occupyingModelId}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`[拖拽吸附] 未启用占用区域处理,允许重叠`);
|
||||
}
|
||||
}
|
||||
const offsetDistance = 0;
|
||||
const snapPosition = targetZone.center.add(targetZone.normal.scale(offsetDistance));
|
||||
rootMesh.position.copyFrom(snapPosition);
|
||||
const targetDirection = targetZone.normal.scale(-1);
|
||||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||||
rootMesh.rotation.y = angle;
|
||||
console.log(`[拖拽吸附] 模型 ${modelId} 吸附到区域 ${closestZoneIndex}`);
|
||||
this.updateModelZoneMapping(modelId);
|
||||
}
|
||||
/**
|
||||
* 更新模型所属的分割区域映射
|
||||
* @param modelId 模型ID
|
||||
*/
|
||||
updateModelZoneMapping(modelId) {
|
||||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||||
if (!meshes || !meshes.length) return;
|
||||
const rootMesh = meshes[0];
|
||||
const modelPosition = rootMesh.position;
|
||||
console.log(`[边界检测] 模型 ${modelId} 拖拽结束,当前位置:`, modelPosition);
|
||||
const appDropZone = this.mainApp.appDropZone;
|
||||
if (!appDropZone) return;
|
||||
let originalWallName = null;
|
||||
appDropZone["zoneModelMap"]?.forEach((id, zoneKey) => {
|
||||
if (id === modelId) {
|
||||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||||
if (match) {
|
||||
originalWallName = match[1];
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!originalWallName) {
|
||||
console.log(`[边界检测] 模型 ${modelId} 未找到原始墙面,跳过检测`);
|
||||
return;
|
||||
}
|
||||
console.log(`[边界检测] 模型 ${modelId} 原始墙面: ${originalWallName}`);
|
||||
const wallZones = appDropZone.getZonesByWall(originalWallName);
|
||||
if (!wallZones.length) return;
|
||||
console.log(`[边界检测] 墙面 ${originalWallName} 有 ${wallZones.length} 个分割区域`);
|
||||
let closestZoneIndex = -1;
|
||||
let minDistance = Number.POSITIVE_INFINITY;
|
||||
wallZones.forEach((zone, index) => {
|
||||
const distance = modelPosition.subtract(zone.center).length();
|
||||
console.log(`[边界检测] 区域 ${index} 中心:`, zone.center, `距离: ${distance.toFixed(3)}`);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closestZoneIndex = index;
|
||||
}
|
||||
});
|
||||
if (closestZoneIndex === -1) {
|
||||
console.log(`[边界检测] 未找到最近的区域`);
|
||||
return;
|
||||
}
|
||||
console.log(`[边界检测] 模型 ${modelId} 最接近区域 ${closestZoneIndex},距离: ${minDistance.toFixed(3)}`);
|
||||
let currentZoneIndex = -1;
|
||||
appDropZone["zoneModelMap"]?.forEach((id, zoneKey) => {
|
||||
if (id === modelId) {
|
||||
const match = zoneKey.match(/^.+\[(\d+)\]$/);
|
||||
if (match) {
|
||||
currentZoneIndex = parseInt(match[1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (currentZoneIndex !== closestZoneIndex) {
|
||||
console.log(`[边界检测] 模型 ${modelId} 从区域 ${currentZoneIndex} 移动到区域 ${closestZoneIndex}`);
|
||||
if (currentZoneIndex !== -1) {
|
||||
const oldKey = `${originalWallName}[${currentZoneIndex}]`;
|
||||
appDropZone["zoneModelMap"]?.delete(oldKey);
|
||||
console.log(`[边界检测] 删除旧映射: ${oldKey}`);
|
||||
}
|
||||
const newKey = `${originalWallName}[${closestZoneIndex}]`;
|
||||
const existingModelId = appDropZone["zoneModelMap"]?.get(newKey);
|
||||
const dragInfo = this.modelDragMap.get(modelId);
|
||||
const handleOccupiedZone = dragInfo?.config.handleOccupiedZone ?? false;
|
||||
const occupiedZoneAction = dragInfo?.config.occupiedZoneAction ?? "return";
|
||||
if (existingModelId && existingModelId !== modelId) {
|
||||
console.log(`[边界检测] 目标区域 ${closestZoneIndex} 已有模型 ${existingModelId}`);
|
||||
if (handleOccupiedZone && occupiedZoneAction === "replace") {
|
||||
console.log(`[边界检测] 配置为替换模式,交换位置`);
|
||||
if (currentZoneIndex !== -1) {
|
||||
const swapKey = `${originalWallName}[${currentZoneIndex}]`;
|
||||
appDropZone["zoneModelMap"]?.set(swapKey, existingModelId);
|
||||
console.log(`[边界检测] 模型 ${existingModelId} 移动到区域 ${currentZoneIndex}`);
|
||||
const existingMeshes = this.mainApp.appModel?.modelDic?.Get(existingModelId);
|
||||
if (existingMeshes && existingMeshes.length) {
|
||||
const existingRootMesh = existingMeshes[0];
|
||||
const swapZone = wallZones[currentZoneIndex];
|
||||
if (swapZone) {
|
||||
const offsetDistance = -0.05;
|
||||
const swapPosition = swapZone.center.add(swapZone.normal.scale(offsetDistance));
|
||||
existingRootMesh.position.copyFrom(swapPosition);
|
||||
const targetDirection = swapZone.normal.scale(-1);
|
||||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||||
existingRootMesh.rotation.y = angle;
|
||||
console.log(`[边界检测] 已将模型 ${existingModelId} 物理移动到区域 ${currentZoneIndex}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(`[边界检测] 未启用替换模式或未启用占用区域处理,允许重叠`);
|
||||
}
|
||||
}
|
||||
appDropZone["zoneModelMap"]?.set(newKey, modelId);
|
||||
console.log(`[边界检测] 添加新映射: ${newKey} -> ${modelId}`);
|
||||
} else {
|
||||
console.log(`[边界检测] 模型 ${modelId} 仍在区域 ${currentZoneIndex},无需更新映射`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 清理资源
|
||||
*/
|
||||
@ -356884,6 +357249,23 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
line.isVisible = true;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 只显示指定墙面的放置区域
|
||||
* @param wallName 墙面名称
|
||||
*/
|
||||
showWall(wallName) {
|
||||
this.hide();
|
||||
this.placementZones.forEach((zone) => {
|
||||
if (zone.wallName === wallName) {
|
||||
zone.mesh.isVisible = true;
|
||||
}
|
||||
});
|
||||
this.borderLines.forEach((line) => {
|
||||
if (line.name.includes(`_${wallName}_`)) {
|
||||
line.isVisible = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 隐藏所有放置区域
|
||||
*/
|
||||
@ -356920,6 +357302,7 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
scene;
|
||||
placementWall;
|
||||
appModel = null;
|
||||
mainApp = null;
|
||||
// 内部映射:放置区域 -> 模型ID
|
||||
zoneModelMap = /* @__PURE__ */ new Map();
|
||||
// 墙面 -> 当前分割数
|
||||
@ -356940,6 +357323,12 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
setModelManager(appModel) {
|
||||
this.appModel = appModel;
|
||||
}
|
||||
/**
|
||||
* 设置 MainApp 引用(内部使用)
|
||||
*/
|
||||
setMainApp(mainApp) {
|
||||
this.mainApp = mainApp;
|
||||
}
|
||||
/**
|
||||
* 设置放置区域数据
|
||||
* @param config 配置参数
|
||||
@ -357018,6 +357407,9 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
divisions: newDivisions
|
||||
};
|
||||
}).filter((wall) => wall !== null);
|
||||
this.dropZoneConfig.walls.forEach((wall) => {
|
||||
this.wallDivisionsMap.set(wall.name, wall.divisions);
|
||||
});
|
||||
this.clearZones();
|
||||
const zones = this.generateDropZones();
|
||||
this.show();
|
||||
@ -357071,6 +357463,176 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
}
|
||||
this.zoneModelMap.set(zoneKey, modelId);
|
||||
console.log(`已记录模型 ${modelId} 到区域 ${zoneKey}`);
|
||||
this.checkAndAutoArrange(wallName);
|
||||
}
|
||||
/**
|
||||
* 通知模型被删除(外部调用,用于更新映射和重新启用拖拽)
|
||||
* @param modelId 被删除的模型ID
|
||||
*/
|
||||
notifyModelRemoved(modelId) {
|
||||
console.log(`[模型删除通知] 模型 ${modelId} 被删除`);
|
||||
let removedWallName = null;
|
||||
let removedZoneKey = null;
|
||||
this.zoneModelMap.forEach((id, zoneKey) => {
|
||||
if (id === modelId) {
|
||||
removedZoneKey = zoneKey;
|
||||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||||
if (match) {
|
||||
removedWallName = match[1];
|
||||
}
|
||||
}
|
||||
});
|
||||
if (removedZoneKey) {
|
||||
this.zoneModelMap.delete(removedZoneKey);
|
||||
console.log(`[模型删除通知] 已从映射中删除: ${removedZoneKey}`);
|
||||
}
|
||||
if (removedWallName) {
|
||||
this.checkAndReenableDrag(removedWallName);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检查墙面是否不满,如果不满则重新启用该墙面所有模型的拖拽
|
||||
* @param wallName 墙面名称
|
||||
*/
|
||||
checkAndReenableDrag(wallName) {
|
||||
const currentDivisions = this.wallDivisionsMap.get(wallName);
|
||||
if (!currentDivisions) return;
|
||||
let placedCount = 0;
|
||||
const placedModelIds = [];
|
||||
this.zoneModelMap.forEach((modelId, zoneKey) => {
|
||||
if (zoneKey.startsWith(`${wallName}[`)) {
|
||||
placedCount++;
|
||||
placedModelIds.push(modelId);
|
||||
}
|
||||
});
|
||||
console.log(`[拖拽检查] 墙面 ${wallName} 当前模型数: ${placedCount}/${currentDivisions}`);
|
||||
if (placedCount < currentDivisions) {
|
||||
console.log(`[拖拽检查] 墙面 ${wallName} 未满,重新启用拖拽`);
|
||||
placedModelIds.forEach((modelId) => {
|
||||
if (this.mainApp && this.mainApp.appModelDrag && typeof this.mainApp.appModelDrag.setDragEnabled === "function") {
|
||||
this.mainApp.appModelDrag.setDragEnabled(modelId, true);
|
||||
console.log(`[拖拽检查] ✓ 已启用模型 ${modelId} 的拖拽功能`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检查墙面是否已满
|
||||
* @param wallName 墙面名称
|
||||
* @returns 是否已满
|
||||
*/
|
||||
isWallFull(wallName) {
|
||||
const currentDivisions = this.wallDivisionsMap.get(wallName);
|
||||
if (!currentDivisions) return false;
|
||||
let placedCount = 0;
|
||||
this.zoneModelMap.forEach((modelId, zoneKey) => {
|
||||
if (zoneKey.startsWith(`${wallName}[`)) {
|
||||
placedCount++;
|
||||
}
|
||||
});
|
||||
return placedCount >= currentDivisions;
|
||||
}
|
||||
/**
|
||||
* 检查墙面是否已满,如果满了则自动排列模型
|
||||
* @param wallName 墙面名称
|
||||
*/
|
||||
checkAndAutoArrange(wallName) {
|
||||
const currentDivisions = this.wallDivisionsMap.get(wallName);
|
||||
console.log(`[自动排列检查] 墙面: ${wallName}, 分割数: ${currentDivisions}`);
|
||||
if (!currentDivisions) {
|
||||
console.log(`[自动排列检查] 墙面 ${wallName} 没有分割数配置,跳过`);
|
||||
return;
|
||||
}
|
||||
let placedCount = 0;
|
||||
const placedModels = [];
|
||||
this.zoneModelMap.forEach((modelId, zoneKey) => {
|
||||
if (zoneKey.startsWith(`${wallName}[`)) {
|
||||
placedCount++;
|
||||
placedModels.push(`${zoneKey} -> ${modelId}`);
|
||||
}
|
||||
});
|
||||
console.log(`[自动排列检查] 墙面 ${wallName} 已放置模型数: ${placedCount}/${currentDivisions}`);
|
||||
console.log(`[自动排列检查] 已放置的模型:`, placedModels);
|
||||
if (placedCount === currentDivisions) {
|
||||
console.log(`[自动排列] 墙面 ${wallName} 已满(${placedCount}/${currentDivisions}),开始执行自动排列`);
|
||||
this.autoArrangeWall(wallName);
|
||||
} else {
|
||||
console.log(`[自动排列检查] 墙面 ${wallName} 未满,不执行自动排列`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 自动排列墙面上的所有模型
|
||||
* @param wallName 墙面名称
|
||||
*/
|
||||
autoArrangeWall(wallName) {
|
||||
console.log(`[自动排列] 开始排列墙面: ${wallName}`);
|
||||
const wallZones = this.getZonesByWall(wallName);
|
||||
console.log(`[自动排列] 墙面 ${wallName} 的放置区域数量: ${wallZones.length}`);
|
||||
if (!wallZones.length) {
|
||||
console.log(`[自动排列] 墙面 ${wallName} 没有放置区域,退出`);
|
||||
return;
|
||||
}
|
||||
const placedModels = [];
|
||||
this.zoneModelMap.forEach((modelId, zoneKey) => {
|
||||
const match = zoneKey.match(new RegExp(`^${wallName}\\[(\\d+)\\]$`));
|
||||
if (match) {
|
||||
const currentIndex = parseInt(match[1]);
|
||||
placedModels.push({
|
||||
modelId,
|
||||
currentIndex
|
||||
});
|
||||
console.log(`[自动排列] 找到模型: ${modelId}, 当前索引: ${currentIndex}`);
|
||||
}
|
||||
});
|
||||
console.log(`[自动排列] 收集到 ${placedModels.length} 个模型`);
|
||||
placedModels.sort((a, b) => a.currentIndex - b.currentIndex);
|
||||
console.log(`[自动排列] 排序后的模型顺序:`, placedModels.map((m) => `${m.modelId}(索引${m.currentIndex})`));
|
||||
placedModels.forEach((model, newIndex) => {
|
||||
console.log(`[自动排列] 处理模型 ${model.modelId}: 当前索引=${model.currentIndex}, 目标索引=${newIndex}`);
|
||||
const targetZone = wallZones[newIndex];
|
||||
if (!targetZone) {
|
||||
console.warn(`[自动排列] ✗ 找不到索引 ${newIndex} 的放置区域`);
|
||||
return;
|
||||
}
|
||||
if (this.appModel) {
|
||||
const offsetDistance = 0;
|
||||
const targetPosition = targetZone.center.add(targetZone.normal.scale(offsetDistance));
|
||||
const targetDirection = targetZone.normal.scale(-1);
|
||||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||||
console.log(`[自动排列] 目标区域 ${newIndex} 的位置:`, {
|
||||
center: targetZone.center,
|
||||
normal: targetZone.normal,
|
||||
targetPosition,
|
||||
rotation: angle * 180 / Math.PI
|
||||
});
|
||||
const meshes = this.appModel.getCachedMeshes(model.modelId);
|
||||
if (meshes && meshes.length > 0) {
|
||||
const rootMesh = meshes[0];
|
||||
rootMesh.position.copyFrom(targetPosition);
|
||||
rootMesh.rotation.y = angle;
|
||||
console.log(`[自动排列] ✓ 模型 ${model.modelId} 已移动到索引 ${newIndex} 的位置`);
|
||||
} else {
|
||||
console.warn(`[自动排列] ✗ 找不到模型 ${model.modelId} 的网格`);
|
||||
}
|
||||
if (model.currentIndex !== newIndex) {
|
||||
const oldKey = `${wallName}[${model.currentIndex}]`;
|
||||
const newKey = `${wallName}[${newIndex}]`;
|
||||
this.zoneModelMap.delete(oldKey);
|
||||
this.zoneModelMap.set(newKey, model.modelId);
|
||||
console.log(`[自动排列] 更新映射: ${oldKey} -> ${newKey}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log(`[自动排列] 开始禁用拖拽功能`);
|
||||
placedModels.forEach((model) => {
|
||||
if (this.mainApp && this.mainApp.appModelDrag && typeof this.mainApp.appModelDrag.setDragEnabled === "function") {
|
||||
this.mainApp.appModelDrag.setDragEnabled(model.modelId, false);
|
||||
console.log(`[自动排列] ✓ 已禁用模型 ${model.modelId} 的拖拽功能`);
|
||||
} else {
|
||||
console.warn(`[自动排列] ✗ 无法禁用模型 ${model.modelId} 的拖拽功能:appModelDrag 未初始化`);
|
||||
}
|
||||
});
|
||||
console.log(`[自动排列] 墙面 ${wallName} 自动排列完成`);
|
||||
}
|
||||
/**
|
||||
* 获取所有放置区域
|
||||
@ -357101,12 +357663,37 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
*/
|
||||
show() {
|
||||
this.placementWall.show();
|
||||
this.setModelsPickable(false);
|
||||
}
|
||||
/**
|
||||
* 只显示指定墙面的放置区域
|
||||
* @param wallName 墙面名称
|
||||
*/
|
||||
showWall(wallName) {
|
||||
this.placementWall.showWall(wallName);
|
||||
this.setModelsPickable(false);
|
||||
}
|
||||
/**
|
||||
* 隐藏所有放置区域
|
||||
*/
|
||||
hide() {
|
||||
this.placementWall.hide();
|
||||
this.setModelsPickable(true);
|
||||
}
|
||||
/**
|
||||
* 设置所有已放置模型的可拾取状态
|
||||
* @param pickable 是否可拾取
|
||||
*/
|
||||
setModelsPickable(pickable) {
|
||||
if (!this.appModel) return;
|
||||
this.zoneModelMap.forEach((modelId) => {
|
||||
const meshes = this.appModel.getCachedMeshes(modelId);
|
||||
if (meshes && meshes.length > 0) {
|
||||
meshes.forEach((mesh) => {
|
||||
mesh.isPickable = pickable;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 清除所有放置区域(只清除网格,不清除模型)
|
||||
@ -357501,6 +358088,7 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
if (this.config.receiveShadows) {
|
||||
this.ground.receiveShadows = true;
|
||||
}
|
||||
this.ground.isPickable = false;
|
||||
}
|
||||
/** 创建材质 */
|
||||
createMaterial() {
|
||||
@ -357539,6 +358127,7 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
const gridPosition = this.config.position ? this.config.position.clone() : new Vector3(0, 0, 0);
|
||||
gridPosition.y += 0.01;
|
||||
this.gridGround.position = gridPosition;
|
||||
this.gridGround.isPickable = false;
|
||||
this.gridMaterial = new GridMaterial("gridMaterial", this.mainApp.appScene.object);
|
||||
this.gridMaterial.mainColor = this.config.gridColor || new Color3(0.3, 0.3, 0.3);
|
||||
this.gridMaterial.lineColor = this.config.gridColor || new Color3(0.3, 0.3, 0.3);
|
||||
@ -357663,6 +358252,7 @@ clipPos=viewProjection*worldPos;previousClipPos=previousViewProjection*previousW
|
||||
this.appModel.initManagers();
|
||||
this.appDropZone = new AppDropZone(this.appScene.object);
|
||||
this.appDropZone.setModelManager(this.appModel);
|
||||
this.appDropZone.setMainApp(this);
|
||||
this.update();
|
||||
EventBridge.sceneReady({ scene: this.appScene.object });
|
||||
}
|
||||
|
||||
74
index.html
74
index.html
@ -275,33 +275,40 @@
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* 进度条样式 */
|
||||
/* 加载遮罩样式 */
|
||||
#progress-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
width: 0%;
|
||||
height: 10px;
|
||||
background: linear-gradient(90deg, #4CAF50, #45a049);
|
||||
border-radius: 5px;
|
||||
transition: width 0.1s ease;
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
border-top-color: #4CAF50;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
#progress-text {
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@ -312,8 +319,8 @@
|
||||
<div id="canvas-container">
|
||||
<canvas id="renderDom"></canvas>
|
||||
<div id="progress-container" style="display: none;">
|
||||
<div id="progress-bar"></div>
|
||||
<div id="progress-text">0%</div>
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-text">加载中...</div>
|
||||
</div>
|
||||
|
||||
<!-- 生成放置区域按钮 -->
|
||||
@ -892,6 +899,7 @@
|
||||
|
||||
// 移除按钮事件
|
||||
document.getElementById('remove-model-btn').addEventListener('click', () => {
|
||||
|
||||
const pickedMesh = window.getCurrentPickedMesh();
|
||||
if (pickedMesh) {
|
||||
const meshName = pickedMesh.name;
|
||||
@ -916,9 +924,25 @@
|
||||
|
||||
// 监听放置区域点击事件
|
||||
kernel.on('dropzone:click', async (dropzone_data) => {
|
||||
// 显示进度条
|
||||
const progressContainer = document.getElementById('progress-container');
|
||||
if (progressContainer) {
|
||||
progressContainer.style.display = 'flex';
|
||||
}
|
||||
|
||||
getEvent(dropzone_data, sku)
|
||||
});
|
||||
|
||||
// 监听模型加载完成事件
|
||||
kernel.on('model:loaded', (data) => {
|
||||
console.log('模型加载完成', data);
|
||||
// 隐藏进度条
|
||||
const progressContainer = document.getElementById('progress-container');
|
||||
if (progressContainer) {
|
||||
progressContainer.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 存储当前选中的材质名和网格
|
||||
let currentMaterialName = '';
|
||||
@ -994,6 +1018,8 @@
|
||||
|
||||
// 暴露 kernel 到全局,方便调试
|
||||
|
||||
kernel.on('model:loaded', (event) => {
|
||||
});
|
||||
|
||||
kernel.on('hotspot:click', (event) => {
|
||||
console.log('热点被点击:', event);
|
||||
|
||||
17
index.js
17
index.js
@ -44,7 +44,8 @@ export const init = async (customConfig = {}) => {
|
||||
container: document.querySelector('#renderDom'),
|
||||
modelUrlList: [],
|
||||
env: { envPath: 'https://cdn.files.zguiy.com/zt/environment.env', intensity: 1.2, rotationY: 0.3, background: false },
|
||||
gizmo: {
|
||||
gcamera:{},
|
||||
izmo: {
|
||||
position: false,
|
||||
rotation: false,
|
||||
scale: false
|
||||
@ -62,7 +63,7 @@ export const init = async (customConfig = {}) => {
|
||||
const config = { ...defaultConfig, ...customConfig };
|
||||
kernel.init(config);
|
||||
}
|
||||
|
||||
//
|
||||
//初始化加载模型
|
||||
export const getAutoLoadModelList = async () => {
|
||||
const kernel = getKernel();
|
||||
@ -201,6 +202,7 @@ export const executeEvent = async (dropzone_data, result, sku) => {
|
||||
// 第一次循环:处理 change_model
|
||||
for (const event of result.data.events) {
|
||||
if (event.event_type === 'change_model') {
|
||||
|
||||
const { name, file_url, model_control_type, category } = event.target_data;
|
||||
|
||||
// 生成唯一的模型ID
|
||||
@ -208,8 +210,7 @@ export const executeEvent = async (dropzone_data, result, sku) => {
|
||||
modelName = name;
|
||||
kernel.dropZone.recordModelPlacement(wallName, index, name + '_' + modelId);
|
||||
|
||||
// 记录模型ID到SKU的映射
|
||||
setSkuMapping(modelId, sku);
|
||||
|
||||
|
||||
await kernel.model.add({
|
||||
modelName: name,
|
||||
@ -220,10 +221,10 @@ export const executeEvent = async (dropzone_data, result, sku) => {
|
||||
enable: true,
|
||||
axis: rotation.y === 0 || rotation.y === 180 ? 'x' : 'z',
|
||||
step: 0.1,
|
||||
// snapToZone: true, // 开关1:拖拽吸附到最近的分割区域
|
||||
// returnWhenOutOfBounds: false, // 开关2:拖拽到区域外时返回原位置
|
||||
// handleOccupiedZone: true, // 开关3:处理已占用区域(false=允许重叠)
|
||||
// occupiedZoneAction: 'return' // 当开关3=true时的行为:'return'=返回原位置,'replace'=替换
|
||||
snapToZone: true, // 拖拽吸附到最近的分割区域
|
||||
returnWhenOutOfBounds: true, // 拖拽到区域外时返回原位置
|
||||
handleOccupiedZone: true, // 处理已占用区域(false=允许重叠)
|
||||
occupiedZoneAction: 'replace' // 当开关3=true时的行为:'return'=返回原位置,'replace'=替换
|
||||
},
|
||||
transform: {
|
||||
position: position,
|
||||
|
||||
Reference in New Issue
Block a user