This commit is contained in:
2026-05-13 11:28:49 +08:00
parent 223fa5dd4e
commit 21255a701d
12 changed files with 1765 additions and 537 deletions

View File

@ -0,0 +1,331 @@
import { Scene, Mesh, MeshBuilder, StandardMaterial, Color3, Vector3 } from '@babylonjs/core';
/**
* 墙面配置 - 定义一个垂直面的起始和结束坐标
*/
export interface WallConfig {
name: string; // 墙面名称(如 "front", "back", "left", "right"
startPoint: Vector3; // 起始点坐标(左下角)
endPoint: Vector3; // 结束点坐标(右下角)
height: number; // 墙面高度
divisions: number; // 分割数(将这个面分成几块)
}
/**
* 放置区域配置
*/
export interface PlacementAreaConfig {
walls: WallConfig[]; // 墙面数组可以定义1-N个面
color?: string; // 颜色(十六进制)
alpha?: number; // 透明度
thickness?: number; // 厚度
showBorder?: boolean; // 是否显示边框
borderColor?: string; // 边框颜色
}
/**
* 单个放置区域的信息
*/
export interface PlacementZoneInfo {
mesh: Mesh; // 放置区域的网格
wallName: string; // 所属墙面名称
index: number; // 在该墙面上的索引
center: Vector3; // 中心点坐标
width: number; // 宽度
height: number; // 高度
normal: Vector3; // 法线方向
}
export class AppPlacementWall {
private scene: Scene;
private placementZones: PlacementZoneInfo[] = [];
private borderLines: Mesh[] = [];
constructor(scene: Scene) {
this.scene = scene;
}
/**
* 根据墙面配置生成放置区域
*/
generatePlacementAreas(config: PlacementAreaConfig): PlacementZoneInfo[] {
const {
walls,
color = '#21c7ff',
alpha = 0.3,
thickness = 2,
showBorder = true,
borderColor = '#ffffff'
} = config;
// 清除之前的放置区域
this.clearAll();
const material = this.createMaterial(color, alpha);
walls.forEach(wall => {
const zones = this.generateWallZones(wall, material, thickness);
this.placementZones.push(...zones);
if (showBorder) {
this.createWallBorder(wall, borderColor);
}
});
return this.placementZones;
}
/**
* 为单个墙面生成放置区域
*/
private generateWallZones(
wall: WallConfig,
material: StandardMaterial,
thickness: number
): PlacementZoneInfo[] {
const { name, startPoint, endPoint, height, divisions } = wall;
// 计算墙面的方向向量和长度
const direction = endPoint.subtract(startPoint);
const wallLength = direction.length();
const normalizedDir = direction.normalize();
// 计算每块的宽度
const blockWidth = wallLength / divisions;
// 计算墙面的法线方向(垂直于墙面,指向外侧)
const normal = new Vector3(-normalizedDir.z, 0, normalizedDir.x);
const zones: PlacementZoneInfo[] = [];
for (let i = 0; i < divisions; i++) {
// 计算当前块的中心位置
const offset = blockWidth * (i + 0.5);
const centerX = startPoint.x + normalizedDir.x * offset;
const centerY = startPoint.y + height / 2;
const centerZ = startPoint.z + normalizedDir.z * offset;
const center = new Vector3(centerX, centerY, centerZ);
// 创建放置区域平面
const plane = MeshBuilder.CreatePlane(
`placement_${name}_${i}`,
{ width: blockWidth, height: height },
this.scene
);
plane.position = center;
plane.material = material;
// 让平面面向法线方向(垂直站立并朝外)
// 使用 lookAt 让平面面向外侧
const targetPoint = center.add(normal);
plane.lookAt(targetPoint);
// 设置厚度通过缩放Z轴
plane.scaling.z = thickness;
// 启用拾取
plane.isPickable = true;
// 为每个块创建边框
this.createBlockBorder(name, i, center, blockWidth, height, normalizedDir, normal);
zones.push({
mesh: plane,
wallName: name,
index: i,
center: center.clone(),
width: blockWidth,
height: height,
normal: normal.clone()
});
}
return zones;
}
/**
* 为单个块创建边框
*/
private createBlockBorder(
wallName: string,
index: number,
center: Vector3,
width: number,
height: number,
direction: Vector3,
normal: Vector3
): void {
const halfWidth = width / 2;
const halfHeight = height / 2;
// 计算四个角点
const bottomLeft = new Vector3(
center.x - direction.x * halfWidth,
center.y - halfHeight,
center.z - direction.z * halfWidth
);
const bottomRight = new Vector3(
center.x + direction.x * halfWidth,
center.y - halfHeight,
center.z + direction.z * halfWidth
);
const topLeft = new Vector3(
center.x - direction.x * halfWidth,
center.y + halfHeight,
center.z - direction.z * halfWidth
);
const topRight = new Vector3(
center.x + direction.x * halfWidth,
center.y + halfHeight,
center.z + direction.z * halfWidth
);
// 创建四条边
const edges = [
[bottomLeft, bottomRight], // 底边
[bottomRight, topRight], // 右边
[topRight, topLeft], // 顶边
[topLeft, bottomLeft] // 左边
];
edges.forEach((edge, edgeIndex) => {
const line = MeshBuilder.CreateLines(
`block_border_${wallName}_${index}_${edgeIndex}`,
{ points: edge },
this.scene
);
line.color = new Color3(1, 1, 1); // 白色
this.borderLines.push(line);
});
}
/**
* 创建墙面边框
*/
private createWallBorder(wall: WallConfig, color: string): void {
const { name, startPoint, endPoint, height } = 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 edges = [
[bottomLeft, bottomRight], // 底边
[bottomRight, topRight], // 右边
[topRight, topLeft], // 顶边
[topLeft, bottomLeft] // 左边
];
const rgb = this.hexToRgb(color);
const lineColor = new Color3(rgb.r, rgb.g, rgb.b);
edges.forEach((edge, index) => {
const line = MeshBuilder.CreateLines(
`border_${name}_${index}`,
{ points: edge },
this.scene
);
line.color = lineColor;
this.borderLines.push(line);
});
}
/**
* 创建材质
*/
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.alpha = alpha;
material.backFaceCulling = false;
return material;
}
/**
* 十六进制颜色转RGB
*/
private hexToRgb(hex: string): { r: number; g: number; b: number } {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16) / 255,
g: parseInt(result[2], 16) / 255,
b: parseInt(result[3], 16) / 255
}
: { r: 0, g: 0, b: 0 };
}
/**
* 获取所有放置区域
*/
getPlacementZones(): PlacementZoneInfo[] {
return this.placementZones;
}
/**
* 根据墙面名称获取放置区域
*/
getZonesByWall(wallName: string): PlacementZoneInfo[] {
return this.placementZones.filter(zone => zone.wallName === wallName);
}
/**
* 根据索引获取特定放置区域
*/
getZone(wallName: string, index: number): PlacementZoneInfo | undefined {
return this.placementZones.find(
zone => zone.wallName === wallName && zone.index === index
);
}
/**
* 显示所有放置区域
*/
show(): void {
this.placementZones.forEach(zone => {
zone.mesh.isVisible = true;
});
this.borderLines.forEach(line => {
line.isVisible = true;
});
}
/**
* 隐藏所有放置区域
*/
hide(): void {
this.placementZones.forEach(zone => {
zone.mesh.isVisible = false;
});
this.borderLines.forEach(line => {
line.isVisible = false;
});
}
/**
* 清除所有放置区域
*/
clearAll(): void {
this.placementZones.forEach(zone => {
zone.mesh.dispose();
});
this.placementZones = [];
this.borderLines.forEach(line => {
line.dispose();
});
this.borderLines = [];
}
/**
* 销毁
*/
dispose(): void {
this.clearAll();
}
}