This commit is contained in:
2026-05-13 12:36:48 +08:00
parent 21255a701d
commit 066294e74f
11 changed files with 194 additions and 80 deletions

View File

@ -25,7 +25,7 @@ export class AppCamera extends Monobehiver {
this.object = new ArcRotateCamera('Camera', Tools.ToRadians(70), Tools.ToRadians(85), 5, new Vector3(0, 2, 0), scene);
this.object.attachControl(canvas, true);
this.object.minZ = 0.01; // 近裁剪面
// this.object.wheelPrecision =999999; // 滚轮缩放精度
this.object.wheelPrecision =200; // 滚轮缩放精度
this.object.panningSensibility = 0;
// 限制垂直角范围,实现上帝视角

View File

@ -54,6 +54,13 @@ export class AppDropZone {
return this.placementWall.getZone(wallName, index);
}
/**
* 设置点击回调
*/
setOnZoneClick(callback: (zoneInfo: PlacementZoneInfo) => void): void {
this.placementWall.setOnZoneClick(callback);
}
/**
* 显示所有放置区域
*/

View File

@ -1,4 +1,4 @@
import { Scene, Mesh, MeshBuilder, StandardMaterial, Color3, Vector3 } from '@babylonjs/core';
import { Scene, Mesh, MeshBuilder, StandardMaterial, Color3, Vector3, ActionManager, ExecuteCodeAction } from '@babylonjs/core';
/**
* 墙面配置 - 定义一个垂直面的起始和结束坐标
@ -9,6 +9,7 @@ export interface WallConfig {
endPoint: Vector3; // 结束点坐标(右下角)
height: number; // 墙面高度
divisions: number; // 分割数(将这个面分成几块)
offset?: number; // 偏移量正数向外负数向内默认0
}
/**
@ -40,6 +41,7 @@ export class AppPlacementWall {
private scene: Scene;
private placementZones: PlacementZoneInfo[] = [];
private borderLines: Mesh[] = [];
private onZoneClickCallback?: (zoneInfo: PlacementZoneInfo) => void;
constructor(scene: Scene) {
this.scene = scene;
@ -83,7 +85,7 @@ export class AppPlacementWall {
material: StandardMaterial,
thickness: number
): PlacementZoneInfo[] {
const { name, startPoint, endPoint, height, divisions } = wall;
const { name, startPoint, endPoint, height, divisions, offset = 0 } = wall;
// 计算墙面的方向向量和长度
const direction = endPoint.subtract(startPoint);
@ -96,15 +98,18 @@ export class AppPlacementWall {
// 计算墙面的法线方向(垂直于墙面,指向外侧)
const normal = new Vector3(-normalizedDir.z, 0, normalizedDir.x);
// 应用偏移量(沿着法线方向)
const offsetVector = normal.scale(offset);
const zones: PlacementZoneInfo[] = [];
for (let i = 0; i < divisions; i++) {
// 计算当前块的中心位置
const offset = blockWidth * (i + 0.5);
const centerX = startPoint.x + normalizedDir.x * offset;
const offset_i = blockWidth * (i + 0.5);
const centerX = startPoint.x + normalizedDir.x * offset_i;
const centerY = startPoint.y + height / 2;
const centerZ = startPoint.z + normalizedDir.z * offset;
const center = new Vector3(centerX, centerY, centerZ);
const centerZ = startPoint.z + normalizedDir.z * offset_i;
const center = new Vector3(centerX, centerY, centerZ).add(offsetVector);
// 创建放置区域平面
const plane = MeshBuilder.CreatePlane(
@ -127,6 +132,28 @@ export class AppPlacementWall {
// 启用拾取
plane.isPickable = true;
// 添加点击事件
plane.actionManager = new ActionManager(this.scene);
plane.actionManager.registerAction(
new ExecuteCodeAction(
ActionManager.OnPickTrigger,
() => {
const zoneInfo: PlacementZoneInfo = {
mesh: plane,
wallName: name,
index: i,
center: center.clone(),
width: blockWidth,
height: height,
normal: normal.clone()
};
if (this.onZoneClickCallback) {
this.onZoneClickCallback(zoneInfo);
}
}
)
);
// 为每个块创建边框
this.createBlockBorder(name, i, center, blockWidth, height, normalizedDir, normal);
@ -204,13 +231,21 @@ export class AppPlacementWall {
* 创建墙面边框
*/
private createWallBorder(wall: WallConfig, color: string): void {
const { name, startPoint, endPoint, height } = wall;
const { name, startPoint, endPoint, height, offset = 0 } = wall;
// 计算四个角点
const bottomLeft = startPoint.clone();
const bottomRight = endPoint.clone();
const topLeft = new Vector3(startPoint.x, startPoint.y + height, startPoint.z);
const topRight = new Vector3(endPoint.x, endPoint.y + height, endPoint.z);
// 计算法线方向
const direction = endPoint.subtract(startPoint);
const normalizedDir = direction.normalize();
const normal = new Vector3(-normalizedDir.z, 0, normalizedDir.x);
// 应用偏移量
const offsetVector = normal.scale(offset);
// 计算四个角点(应用偏移)
const bottomLeft = startPoint.clone().add(offsetVector);
const bottomRight = endPoint.clone().add(offsetVector);
const topLeft = new Vector3(startPoint.x, startPoint.y + height, startPoint.z).add(offsetVector);
const topRight = new Vector3(endPoint.x, endPoint.y + height, endPoint.z).add(offsetVector);
// 创建四条边
const edges = [
@ -240,7 +275,8 @@ export class AppPlacementWall {
private createMaterial(color: string, alpha: number): StandardMaterial {
const material = new StandardMaterial('placementMaterial', this.scene);
const rgb = this.hexToRgb(color);
material.diffuseColor = new Color3(rgb.r, rgb.g, rgb.b);
material.emissiveColor = new Color3(rgb.r, rgb.g, rgb.b);
material.disableLighting = true;
material.alpha = alpha;
material.backFaceCulling = false;
return material;
@ -283,6 +319,13 @@ export class AppPlacementWall {
);
}
/**
* 设置点击回调
*/
setOnZoneClick(callback: (zoneInfo: PlacementZoneInfo) => void): void {
this.onZoneClickCallback = callback;
}
/**
* 显示所有放置区域
*/

View File

@ -87,6 +87,24 @@ class AppRay extends Monobehiver {
return;
}
// 检查是否点击的是放置区域
if (pickInfo.pickedMesh.name.startsWith('placement_')) {
const zones = this.mainApp.appDropZone.getPlacementZones();
const clickedZone = zones.find(zone => zone.mesh === pickInfo.pickedMesh);
if (clickedZone) {
EventBridge.dropZoneClick({
wallName: clickedZone.wallName,
index: clickedZone.index,
center: clickedZone.center,
width: clickedZone.width,
height: clickedZone.height,
normal: clickedZone.normal,
mesh: clickedZone.mesh
});
return;
}
}
this.mainApp.appDomTo3D.hideAll()
const materialName = pickInfo.pickedMesh.material?.name || '';