This commit is contained in:
wuyanchen 2025-12-11 23:53:43 +08:00
parent 32fed2399c
commit 381fd5f99f
2 changed files with 174 additions and 154 deletions

View File

@ -130,7 +130,7 @@ export class Load3D {
engine = this.engine!
const mapName = "Rock"
scene.useRightHandedSystem = true;//计算导航使用右手坐标系与cocos匹配
// scene.useRightHandedSystem = true;//计算导航使用右手坐标系与cocos匹配
const meshes = await this.agent.loadMap(`./3d/model/${mapName}.glb`, scene)
this.envLight = this.light.createEnvLight(scene)
this.topLight = this.light.createDirectionalLight(scene)

View File

@ -1,17 +1,18 @@
import * as BABYLON from '@babylonjs/core/Legacy/legacy'
import Recast from 'recast-detour'
import { saveAs } from 'file-saver';
import * as BABYLON from "@babylonjs/core/Legacy/legacy";
import Recast from "recast-detour";
import { saveAs } from "file-saver";
export class PlayerAgent {
// private navMeshAgent: BABYLON.ICrowd | null = null
private navigationPlugin: BABYLON.RecastJSPlugin | null = null
private meshInfo: any = null
private maps: Map<string, any> = new Map<string, any>()
private mapName: string = ""
private navigationPlugin: BABYLON.RecastJSPlugin | null = null;
private meshInfo: any = null;
private maps: Map<string, BABYLON.INavMeshParameters> = new Map<
string,
BABYLON.INavMeshParameters
>();
private mapName: string = "";
constructor() {
this.maps.set("SD_V2", {
borderSize: 2, //高度场周围不可导航边框的大小。
cs: 0.11, //xz平面单元格大小
ch: 0.01, //y轴单元格大小
@ -28,9 +29,9 @@ export class PlayerAgent {
maxVertsPerPoly: 6, //过程中生成的多边形所允许的最大顶点数
detailSampleDist: 6, //设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
detailSampleMaxError: 1, //细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
})
tileSize: 16,//瓦片尺寸
});
this.maps.set("Pyramid", {
borderSize: 2, //高度场周围不可导航边框的大小。
cs: 0.01, //xz平面单元格大小
ch: 0.01, //y轴单元格大小
@ -47,9 +48,9 @@ export class PlayerAgent {
maxVertsPerPoly: 6, //过程中生成的多边形所允许的最大顶点数
detailSampleDist: 6, //设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
detailSampleMaxError: 1, //细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
})
tileSize: 16,//瓦片尺寸
});
this.maps.set("Rock", {
borderSize: 2, //高度场周围不可导航边框的大小。
cs: 0.05, //xz平面单元格大小
ch: 0.05, //y轴单元格大小
@ -66,10 +67,14 @@ export class PlayerAgent {
maxVertsPerPoly: 6, //过程中生成的多边形所允许的最大顶点数
detailSampleDist: 6, //设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
detailSampleMaxError: 1, //细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
})
tileSize: 16,//瓦片尺寸
});
}
public loadMap(modelPath: string, scene: BABYLON.Scene): Promise<Array<BABYLON.AbstractMesh>> {
public loadMap(
modelPath: string,
scene: BABYLON.Scene
): Promise<Array<BABYLON.AbstractMesh>> {
// scene.debugLayer.show({
// embedMode: true,
// showExplorer: true,
@ -77,35 +82,37 @@ export class PlayerAgent {
// handleResize: true,
// })//启用场景调试界面
return new Promise((resolve) => {
BABYLON.ImportMeshAsync(
modelPath,
scene
).then(result => {
resolve(result.meshes)
})
})
BABYLON.ImportMeshAsync(modelPath, scene).then((result) => {
resolve(result.meshes);
});
});
}
public async createAgent(mapName: string, scene: BABYLON.Scene, meshes: Array<BABYLON.Mesh>) {
public async createAgent(
mapName: string,
scene: BABYLON.Scene,
meshes: Array<BABYLON.Mesh>
) {
if (!this.maps.has(mapName)) {
return
return;
}
this.mapName = mapName
const recast = await Recast()
this.mapName = mapName;
const recast = await Recast();
this.navigationPlugin = new BABYLON.RecastJSPlugin(recast);
const parameters = this.maps.get(mapName)
const parameters = this.maps.get(mapName)!;
this.navigationPlugin.createNavMesh(meshes, parameters);
const extend = this.calculateTotalBound(meshes).extendSizeWorld
const extend = this.calculateTotalBound(meshes).extendSizeWorld;
// this.meshInfo = {
// extend: { x: extend.x, y: extend.z, z: extend.y }, //y,z 坐标对换才和cocos里对的上
// };
this.meshInfo = {
extend: { x: extend.x, y: extend.z, z: extend.y }//y,z 坐标对换才和cocos里对的上
}
extend: { x: extend.x, y: extend.y, z: extend.z }, //正常坐标
};
// navigationPlugin.buildFromNavmeshData(navigationPlugin.getNavmeshData())
// const poses = this.navigationPlugin.computePathSmooth(new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(1, 0, 1))
// console.log(poses)
// this.navMeshAgent = navigationPlugin.createCrowd(10, 0.5, scene)
@ -127,8 +134,6 @@ export class PlayerAgent {
// // 导航至目标点(自动寻找最近可行走点)
// this.navMeshAgent.agentGoto(playerAgentIndex, new BABYLON.Vector3(1, 0, 1));
const navmeshdebug = this.navigationPlugin.createDebugNavMesh(scene);
const matdebug = new BABYLON.StandardMaterial("matdebug", scene);
matdebug.backFaceCulling = false; //启用双面材质
@ -136,26 +141,36 @@ export class PlayerAgent {
matdebug.alpha = 0.5;
navmeshdebug.material = matdebug;
// scene.getEngine().stopRenderLoop()
}
private getMinAMaxPosExtend(mesh: BABYLON.Mesh): BABYLON.Vector3[] {
const bound = mesh.getBoundingInfo()
const absPos = mesh.absolutePosition
const extendSize = bound.boundingBox.extendSize
extendSize.set(extendSize.x * mesh.scaling.x, extendSize.y * mesh.scaling.y, extendSize.z * mesh.scaling.z)
const min = new BABYLON.Vector3(absPos.x - extendSize.x, absPos.y - extendSize.y, absPos.z - extendSize.z)
const max = new BABYLON.Vector3(absPos.x + extendSize.x, absPos.y + extendSize.y, absPos.z + extendSize.z)
return [min, max]
const bound = mesh.getBoundingInfo();
const absPos = mesh.absolutePosition;
const extendSize = bound.boundingBox.extendSize;
extendSize.set(
extendSize.x * mesh.scaling.x,
extendSize.y * mesh.scaling.y,
extendSize.z * mesh.scaling.z
);
const min = new BABYLON.Vector3(
absPos.x - extendSize.x,
absPos.y - extendSize.y,
absPos.z - extendSize.z
);
const max = new BABYLON.Vector3(
absPos.x + extendSize.x,
absPos.y + extendSize.y,
absPos.z + extendSize.z
);
return [min, max];
}
private calculateTotalBound(meshes: BABYLON.Mesh[]): BABYLON.BoundingBox {
let min = new BABYLON.Vector3(Infinity, Infinity, Infinity);
let max = new BABYLON.Vector3(-Infinity, -Infinity, -Infinity);
meshes.forEach(mesh => {
meshes.forEach((mesh) => {
const loc = this.getMinAMaxPosExtend(mesh);
min = BABYLON.Vector3.Minimize(min, loc[0]);
max = BABYLON.Vector3.Maximize(max, loc[1]);
@ -165,15 +180,20 @@ export class PlayerAgent {
}
public exportNavDataToFileDownload() {
const data = this.navigationPlugin!.getNavmeshData()
saveAs(new Blob([data], {
const data = this.navigationPlugin!.getNavmeshData();
saveAs(
new Blob([data as unknown as BlobPart], {
type: "application/octet-stream",
}), `${this.mapName}.txt`)
}),
`${this.mapName}.txt`
);
const meshInfoJson = JSON.stringify(this.meshInfo)
saveAs(new Blob([meshInfoJson], {
type: "application/json",
}), `${this.mapName}_info.json`)
// const meshInfoJson = JSON.stringify(this.meshInfo);
// saveAs(
// new Blob([meshInfoJson], {
// type: "application/json",
// }),
// `${this.mapName}_info.json`
// );
}
}