增加地面,增加放置区域筛选逻辑

This commit is contained in:
2026-05-21 09:56:13 +08:00
parent b5b70251e2
commit c504fca3de
10 changed files with 377 additions and 35 deletions

View File

@ -31,8 +31,8 @@ export class AppCamera extends Monobehiver {
// 限制垂直角范围,实现上帝视角
this.object.upperBetaLimit = Tools.ToRadians(90); // 最大垂直角接近90度避免万向锁
this.object.position = new Vector3(-0, 10, 0);
this.setTarget(0, 2, 0);
this.object.position = new Vector3(0, 10, 0);
this.setTarget(0, 0.5, 0);
}
/** 设置相机目标点 */

View File

@ -123,29 +123,19 @@ export class AppDropZone {
'右': ['右', 'right', 'you']
};
// 匹配墙面名称(支持精确匹配和模糊匹配)
// 匹配墙面名称(精确匹配)
const matchWallName = (wallName: string): number | null => {
// 1. 优先精确匹配完整墙面名称
// 例如:wall.name = "80全铁3x6_前1"divisions 中有 "前1"
for (const [configName, divisionValue] of Object.entries(divisionsMap)) {
if (wallName.includes(configName)) {
return divisionValue;
}
}
// 2. 如果精确匹配失败,尝试方向模糊匹配(兼容旧版)
const lowerName = wallName.toLowerCase();
for (const [direction, keywords] of Object.entries(directionKeywords)) {
for (const keyword of keywords) {
if (lowerName.includes(keyword.toLowerCase()) || wallName.includes(keyword)) {
// 检查 divisionsMap 中是否有该方向的配置
if (divisionsMap[direction] !== undefined) {
return divisionsMap[direction];
}
}
}
// 提取墙面名称的最后部分(最后一个下划线之后)
// 例如:"SW10000070_10x20星空篷_前1" → "前1"
const wallShortName = wallName.split('_').pop() || wallName;
// 精确匹配提取出的简短名称
if (divisionsMap[wallShortName] !== undefined) {
console.log(`墙面 "${wallName}" 通过简短名称 "${wallShortName}" 精确匹配到分割数: ${divisionsMap[wallShortName]}`);
return divisionsMap[wallShortName];
}
console.log(`墙面 "${wallName}" 未匹配到任何分割数配置`);
return null;
};

220
src/babylonjs/AppGround.ts Normal file
View File

@ -0,0 +1,220 @@
import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
import { Mesh } from '@babylonjs/core/Meshes/mesh';
import { PBRMaterial } from '@babylonjs/core/Materials/PBR/pbrMaterial';
import { GridMaterial } from '@babylonjs/materials/grid/gridMaterial';
import { Texture } from '@babylonjs/core/Materials/Textures/texture';
import { Color3 } from '@babylonjs/core/Maths/math.color';
import { Vector3 } from '@babylonjs/core/Maths/math.vector';
import { Monobehiver } from '../base/Monobehiver';
/**
* 地面网格配置接口
*/
export interface GroundConfig {
/** 地面宽度 */
width?: number;
/** 地面深度 */
height?: number;
/** 网格细分数 */
subdivisions?: number;
/** 贴图URL */
textureUrl?: string;
/** 贴图平铺次数 (uScale, vScale) */
textureScale?: { u: number; v: number };
/** 地面颜色 (当没有贴图时使用) */
color?: Color3;
/** 是否接收阴影 */
receiveShadows?: boolean;
/** 地面位置 */
position?: Vector3;
/** 是否显示网格 */
showGrid?: boolean;
/** 网格线颜色 */
gridColor?: Color3;
/** 网格大小 */
gridRatio?: number;
}
/**
* 地面网格管理类 - 负责创建和管理地面网格
*/
export class AppGround extends Monobehiver {
ground: Mesh | null;
material: PBRMaterial | null;
gridGround: Mesh | null;
gridMaterial: GridMaterial | null;
private config: GroundConfig;
constructor(mainApp: any, config: GroundConfig = {}) {
super(mainApp);
this.ground = null;
this.material = null;
this.gridGround = null;
this.gridMaterial = null;
this.config = {
width: 100,
height: 100,
subdivisions: 10,
receiveShadows: true,
position: new Vector3(0, 0, 0),
textureScale: { u: 10, v: 10 },
color: new Color3(0.5, 0.5, 0.5),
textureUrl: "https://cdn.files.zguiy.com/zt/ground1.jpg", // 默认贴图
showGrid: true, // 默认显示网格
gridColor: new Color3(1,1,1),
gridRatio: 1.0,
...config
};
}
/** 初始化地面网格 */
Awake(): void {
this.createGround();
this.createMaterial();
if (this.config.showGrid) {
this.createGridGround();
}
}
/** 创建地面网格 */
private createGround(): void {
if (!this.mainApp.appScene.object) {
console.error('场景未初始化');
return;
}
this.ground = MeshBuilder.CreateGround(
'ground',
{
width: this.config.width,
height: this.config.height,
subdivisions: this.config.subdivisions
},
this.mainApp.appScene.object
);
if (this.config.position) {
this.ground.position = this.config.position;
}
if (this.config.receiveShadows) {
this.ground.receiveShadows = true;
}
}
/** 创建材质 */
private createMaterial(): void {
if (!this.ground || !this.mainApp.appScene.object) return;
this.material = new PBRMaterial('groundMaterial', this.mainApp.appScene.object);
// 如果有贴图URL加载贴图
if (this.config.textureUrl) {
const texture = new Texture(
this.config.textureUrl,
this.mainApp.appScene.object
);
// 设置贴图平铺
if (this.config.textureScale) {
texture.uScale = this.config.textureScale.u;
texture.vScale = this.config.textureScale.v;
}
this.material.albedoTexture = texture;
} else {
// 没有贴图时使用纯色
this.material.albedoColor = this.config.color || new Color3(1,1,1);
}
// PBR 材质属性设置
this.material.metallic = 0.0; // 非金属
this.material.roughness = 1.0; // 粗糙表面
this.ground.material = this.material;
}
/** 创建网格地面 */
private createGridGround(): void {
if (!this.mainApp.appScene.object) return;
// 创建网格地面位置稍微高一点避免z-fighting
this.gridGround = MeshBuilder.CreateGround(
'gridGround',
{
width: this.config.width,
height: this.config.height,
subdivisions: this.config.subdivisions
},
this.mainApp.appScene.object
);
// 设置位置,稍微高于贴图地面
const gridPosition = this.config.position ? this.config.position.clone() : new Vector3(0, 0, 0);
gridPosition.y += 0.01; // 抬高0.01单位避免z-fighting
this.gridGround.position = gridPosition;
// 创建网格材质
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);
this.gridMaterial.gridRatio = this.config.gridRatio || 1.0;
this.gridMaterial.opacity = 0.8;
this.gridMaterial.backFaceCulling = false;
this.gridGround.material = this.gridMaterial;
}
/** 更新贴图 */
setTexture(textureUrl: string, uScale: number = 10, vScale: number = 10): void {
if (!this.material || !this.mainApp.appScene.object) return;
const texture = new Texture(textureUrl, this.mainApp.appScene.object);
texture.uScale = uScale;
texture.vScale = vScale;
this.material.albedoTexture = texture;
}
/** 更新地面颜色 */
setColor(color: Color3): void {
if (!this.material) return;
this.material.albedoColor = color;
}
/** 显示/隐藏地面 */
setVisible(visible: boolean): void {
if (this.ground) {
this.ground.isVisible = visible;
}
if (this.gridGround) {
this.gridGround.isVisible = visible;
}
}
/** 显示/隐藏网格 */
setGridVisible(visible: boolean): void {
if (this.gridGround) {
this.gridGround.isVisible = visible;
}
}
/** 销毁地面 */
dispose(): void {
if (this.ground) {
this.ground.dispose();
this.ground = null;
}
if (this.material) {
this.material.dispose();
this.material = null;
}
if (this.gridGround) {
this.gridGround.dispose();
this.gridGround = null;
}
if (this.gridMaterial) {
this.gridMaterial.dispose();
this.gridMaterial = null;
}
}
}

View File

@ -19,6 +19,7 @@ import { AppSelectionOutline } from './AppSelectionOutline';
import { AppPositionGizmo } from './AppPositionGizmo';
import { AppModelDrag } from './AppModelDrag';
import { AppDropZone } from './AppDropZone';
import { AppGround } from './AppGround';
/**
* 主应用类 - 3D场景的核心控制器
@ -38,6 +39,7 @@ export class MainApp {
appPositionGizmo: AppPositionGizmo;
appModelDrag: AppModelDrag;
appDropZone: AppDropZone;
appGround: AppGround;
gameManager: GameManager;
@ -54,6 +56,7 @@ export class MainApp {
this.appSelectionOutline = new AppSelectionOutline(this);
this.appPositionGizmo = new AppPositionGizmo(this);
this.appModelDrag = new AppModelDrag(this);
this.appGround = new AppGround(this);
this.gameManager = new GameManager(this);
window.addEventListener("resize", () => this.appEngin.handleResize());
@ -76,7 +79,6 @@ export class MainApp {
async loadModel(): Promise<void> {
await this.appModel.loadModel();
await this.gameManager.Awake();
console.log(1111111111111111111111);
EventBridge.allReady({ scene: this.appScene.object });
}
@ -87,6 +89,7 @@ export class MainApp {
this.appCamera.Awake();
this.appLight.Awake();
this.appEnv.Awake();
this.appGround.Awake();
this.appRay.Awake();
this.appSelectionOutline.init();
this.appPositionGizmo.Awake();
@ -117,6 +120,7 @@ export class MainApp {
this.appEnv?.clean();
this.appPositionGizmo?.dispose();
this.appModelDrag?.dispose();
this.appGround?.dispose();
// this.appHotspot?.clear();
}
}