221 lines
6.7 KiB
TypeScript
221 lines
6.7 KiB
TypeScript
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;
|
||
}
|
||
}
|
||
}
|