·
This commit is contained in:
parent
32fed2399c
commit
381fd5f99f
@ -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)
|
||||
|
||||
@ -1,179 +1,199 @@
|
||||
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, 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轴单元格大小
|
||||
|
||||
// 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 = ""
|
||||
constructor() {
|
||||
this.maps.set("SD_V2", {
|
||||
walkableSlopeAngle: 70, //可以步行的最大坡度。[限制:0 <= 值 < 90] [单位:度]
|
||||
walkableHeight: 10, //到达天花板最小可行走的距离
|
||||
walkableClimb: 5.6, //可以通过的最大岩架高度
|
||||
walkableRadius: 1.5, //距离障碍物的距离(半径)
|
||||
|
||||
borderSize: 2,//高度场周围不可导航边框的大小。
|
||||
cs: 0.11,//xz平面单元格大小
|
||||
ch: 0.01,//y轴单元格大小
|
||||
maxEdgeLen: 12, //沿网格边界的轮廓边的最大允许长度
|
||||
maxSimplificationError: 1.3, //简化轮廓的边界边缘应偏离原始原始轮廓的最大距离。
|
||||
minRegionArea: 8, //允许形成孤岛区域的最小单元 面积
|
||||
mergeRegionArea: 20, //如果可能,跨度计数小于此值的任何区域都将与更大的区域合并
|
||||
maxVertsPerPoly: 6, //过程中生成的多边形所允许的最大顶点数
|
||||
detailSampleDist: 6, //设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
|
||||
detailSampleMaxError: 1, //细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
|
||||
tileSize: 16,//瓦片尺寸
|
||||
});
|
||||
this.maps.set("Pyramid", {
|
||||
borderSize: 2, //高度场周围不可导航边框的大小。
|
||||
cs: 0.01, //xz平面单元格大小
|
||||
ch: 0.01, //y轴单元格大小
|
||||
|
||||
walkableSlopeAngle: 70,//可以步行的最大坡度。[限制:0 <= 值 < 90] [单位:度]
|
||||
walkableHeight: 10,//到达天花板最小可行走的距离
|
||||
walkableClimb: 5.6,//可以通过的最大岩架高度
|
||||
walkableRadius: 1.5,//距离障碍物的距离(半径)
|
||||
walkableSlopeAngle: 50, //可以步行的最大坡度。[限制:0 <= 值 < 90] [单位:度]
|
||||
walkableHeight: 10, //到达天花板最小可行走的距离
|
||||
walkableClimb: 2, //可以通过的最大岩架高度
|
||||
walkableRadius: 1.2, //距离障碍物的距离(半径)
|
||||
|
||||
maxEdgeLen: 12,//沿网格边界的轮廓边的最大允许长度
|
||||
maxSimplificationError: 1.3,//简化轮廓的边界边缘应偏离原始原始轮廓的最大距离。
|
||||
minRegionArea: 8,//允许形成孤岛区域的最小单元 面积
|
||||
mergeRegionArea: 20,//如果可能,跨度计数小于此值的任何区域都将与更大的区域合并
|
||||
maxVertsPerPoly: 6,//过程中生成的多边形所允许的最大顶点数
|
||||
detailSampleDist: 6,//设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
|
||||
detailSampleMaxError: 1,//细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
|
||||
})
|
||||
this.maps.set("Pyramid", {
|
||||
maxEdgeLen: 12, //沿网格边界的轮廓边的最大允许长度
|
||||
maxSimplificationError: 1.3, //简化轮廓的边界边缘应偏离原始原始轮廓的最大距离。
|
||||
minRegionArea: 8, //允许形成孤岛区域的最小单元 面积
|
||||
mergeRegionArea: 20, //如果可能,跨度计数小于此值的任何区域都将与更大的区域合并
|
||||
maxVertsPerPoly: 6, //过程中生成的多边形所允许的最大顶点数
|
||||
detailSampleDist: 6, //设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
|
||||
detailSampleMaxError: 1, //细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
|
||||
tileSize: 16,//瓦片尺寸
|
||||
});
|
||||
this.maps.set("Rock", {
|
||||
borderSize: 2, //高度场周围不可导航边框的大小。
|
||||
cs: 0.05, //xz平面单元格大小
|
||||
ch: 0.05, //y轴单元格大小
|
||||
|
||||
borderSize: 2,//高度场周围不可导航边框的大小。
|
||||
cs: 0.01,//xz平面单元格大小
|
||||
ch: 0.01,//y轴单元格大小
|
||||
walkableSlopeAngle: 50, //可以步行的最大坡度。[限制:0 <= 值 < 90] [单位:度]
|
||||
walkableHeight: 10, //到达天花板最小可行走的距离
|
||||
walkableClimb: 0.9, //可以通过的最大岩架高度
|
||||
walkableRadius: 1.5, //距离障碍物的距离(半径)
|
||||
|
||||
walkableSlopeAngle: 50,//可以步行的最大坡度。[限制:0 <= 值 < 90] [单位:度]
|
||||
walkableHeight: 10,//到达天花板最小可行走的距离
|
||||
walkableClimb: 2,//可以通过的最大岩架高度
|
||||
walkableRadius: 1.2,//距离障碍物的距离(半径)
|
||||
maxEdgeLen: 12, //沿网格边界的轮廓边的最大允许长度
|
||||
maxSimplificationError: 1.3, //简化轮廓的边界边缘应偏离原始原始轮廓的最大距离。
|
||||
minRegionArea: 8, //允许形成孤岛区域的最小单元 面积
|
||||
mergeRegionArea: 20, //如果可能,跨度计数小于此值的任何区域都将与更大的区域合并
|
||||
maxVertsPerPoly: 6, //过程中生成的多边形所允许的最大顶点数
|
||||
detailSampleDist: 6, //设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
|
||||
detailSampleMaxError: 1, //细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
|
||||
tileSize: 16,//瓦片尺寸
|
||||
});
|
||||
}
|
||||
|
||||
maxEdgeLen: 12,//沿网格边界的轮廓边的最大允许长度
|
||||
maxSimplificationError: 1.3,//简化轮廓的边界边缘应偏离原始原始轮廓的最大距离。
|
||||
minRegionArea: 8,//允许形成孤岛区域的最小单元 面积
|
||||
mergeRegionArea: 20,//如果可能,跨度计数小于此值的任何区域都将与更大的区域合并
|
||||
maxVertsPerPoly: 6,//过程中生成的多边形所允许的最大顶点数
|
||||
detailSampleDist: 6,//设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
|
||||
detailSampleMaxError: 1,//细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
|
||||
})
|
||||
this.maps.set("Rock", {
|
||||
public loadMap(
|
||||
modelPath: string,
|
||||
scene: BABYLON.Scene
|
||||
): Promise<Array<BABYLON.AbstractMesh>> {
|
||||
// scene.debugLayer.show({
|
||||
// embedMode: true,
|
||||
// showExplorer: true,
|
||||
// showInspector: true,
|
||||
// handleResize: true,
|
||||
// })//启用场景调试界面
|
||||
return new Promise((resolve) => {
|
||||
BABYLON.ImportMeshAsync(modelPath, scene).then((result) => {
|
||||
resolve(result.meshes);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
borderSize: 2,//高度场周围不可导航边框的大小。
|
||||
cs: 0.05,//xz平面单元格大小
|
||||
ch: 0.05,//y轴单元格大小
|
||||
|
||||
walkableSlopeAngle: 50,//可以步行的最大坡度。[限制:0 <= 值 < 90] [单位:度]
|
||||
walkableHeight: 10,//到达天花板最小可行走的距离
|
||||
walkableClimb: 0.9,//可以通过的最大岩架高度
|
||||
walkableRadius: 1.5,//距离障碍物的距离(半径)
|
||||
|
||||
maxEdgeLen: 12,//沿网格边界的轮廓边的最大允许长度
|
||||
maxSimplificationError: 1.3,//简化轮廓的边界边缘应偏离原始原始轮廓的最大距离。
|
||||
minRegionArea: 8,//允许形成孤岛区域的最小单元 面积
|
||||
mergeRegionArea: 20,//如果可能,跨度计数小于此值的任何区域都将与更大的区域合并
|
||||
maxVertsPerPoly: 6,//过程中生成的多边形所允许的最大顶点数
|
||||
detailSampleDist: 6,//设置生成细节网格时使用的采样距离(仅适用于高度详细信息。)
|
||||
detailSampleMaxError: 1,//细节网格表面应偏离高度场数据的最大距离。(仅适用于高度详细信息。)
|
||||
})
|
||||
public async createAgent(
|
||||
mapName: string,
|
||||
scene: BABYLON.Scene,
|
||||
meshes: Array<BABYLON.Mesh>
|
||||
) {
|
||||
if (!this.maps.has(mapName)) {
|
||||
return;
|
||||
}
|
||||
this.mapName = mapName;
|
||||
const recast = await Recast();
|
||||
this.navigationPlugin = new BABYLON.RecastJSPlugin(recast);
|
||||
const parameters = this.maps.get(mapName)!;
|
||||
this.navigationPlugin.createNavMesh(meshes, parameters);
|
||||
|
||||
public loadMap(modelPath: string, scene: BABYLON.Scene): Promise<Array<BABYLON.AbstractMesh>> {
|
||||
// scene.debugLayer.show({
|
||||
// embedMode: true,
|
||||
// showExplorer: true,
|
||||
// showInspector: true,
|
||||
// handleResize: true,
|
||||
// })//启用场景调试界面
|
||||
return new Promise((resolve) => {
|
||||
BABYLON.ImportMeshAsync(
|
||||
modelPath,
|
||||
scene
|
||||
).then(result => {
|
||||
resolve(result.meshes)
|
||||
})
|
||||
})
|
||||
}
|
||||
const extend = this.calculateTotalBound(meshes).extendSizeWorld;
|
||||
// this.meshInfo = {
|
||||
// extend: { x: extend.x, y: extend.z, z: extend.y }, //y,z 坐标对换,才和cocos里对的上
|
||||
// };
|
||||
|
||||
public async createAgent(mapName: string, scene: BABYLON.Scene, meshes: Array<BABYLON.Mesh>) {
|
||||
this.meshInfo = {
|
||||
extend: { x: extend.x, y: extend.y, z: extend.z }, //正常坐标
|
||||
};
|
||||
|
||||
if (!this.maps.has(mapName)) {
|
||||
return
|
||||
}
|
||||
this.mapName = mapName
|
||||
const recast = await Recast()
|
||||
this.navigationPlugin = new BABYLON.RecastJSPlugin(recast);
|
||||
const parameters = this.maps.get(mapName)
|
||||
this.navigationPlugin.createNavMesh(meshes, parameters);
|
||||
// navigationPlugin.buildFromNavmeshData(navigationPlugin.getNavmeshData())
|
||||
|
||||
const extend = this.calculateTotalBound(meshes).extendSizeWorld
|
||||
this.meshInfo = {
|
||||
extend: { x: extend.x, y: extend.z, z: extend.y }//y,z 坐标对换,才和cocos里对的上
|
||||
}
|
||||
// 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)
|
||||
|
||||
// navigationPlugin.buildFromNavmeshData(navigationPlugin.getNavmeshData())
|
||||
// const agentParams: BABYLON.IAgentParameters = {
|
||||
// radius: 0.5,
|
||||
// height: 1,
|
||||
// maxSpeed: 6,
|
||||
// maxAcceleration: 2,
|
||||
// collisionQueryRange: 2.5,
|
||||
// pathOptimizationRange: 1,
|
||||
// separationWeight: 1
|
||||
// };
|
||||
|
||||
// const playerAgentIndex = this.navMeshAgent.addAgent(new BABYLON.Vector3(0, 0, 0), agentParams, new BABYLON.TransformNode("agentPlayer"))
|
||||
|
||||
// // 手动指定路径
|
||||
// const pathPoints = navigationPlugin.getClosestPoint(new BABYLON.Vector3(1, 0, 1));
|
||||
// // 导航至目标点(自动寻找最近可行走点)
|
||||
// this.navMeshAgent.agentGoto(playerAgentIndex, new BABYLON.Vector3(1, 0, 1));
|
||||
|
||||
// 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)
|
||||
const navmeshdebug = this.navigationPlugin.createDebugNavMesh(scene);
|
||||
const matdebug = new BABYLON.StandardMaterial("matdebug", scene);
|
||||
matdebug.backFaceCulling = false; //启用双面材质
|
||||
matdebug.diffuseColor = new BABYLON.Color3(0.1, 0.2, 1);
|
||||
matdebug.alpha = 0.5;
|
||||
navmeshdebug.material = matdebug;
|
||||
|
||||
// const agentParams: BABYLON.IAgentParameters = {
|
||||
// radius: 0.5,
|
||||
// height: 1,
|
||||
// maxSpeed: 6,
|
||||
// maxAcceleration: 2,
|
||||
// collisionQueryRange: 2.5,
|
||||
// pathOptimizationRange: 1,
|
||||
// separationWeight: 1
|
||||
// };
|
||||
// scene.getEngine().stopRenderLoop()
|
||||
}
|
||||
|
||||
// const playerAgentIndex = this.navMeshAgent.addAgent(new BABYLON.Vector3(0, 0, 0), agentParams, new BABYLON.TransformNode("agentPlayer"))
|
||||
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 pathPoints = navigationPlugin.getClosestPoint(new BABYLON.Vector3(1, 0, 1));
|
||||
// // 导航至目标点(自动寻找最近可行走点)
|
||||
// this.navMeshAgent.agentGoto(playerAgentIndex, new BABYLON.Vector3(1, 0, 1));
|
||||
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) => {
|
||||
const loc = this.getMinAMaxPosExtend(mesh);
|
||||
min = BABYLON.Vector3.Minimize(min, loc[0]);
|
||||
max = BABYLON.Vector3.Maximize(max, loc[1]);
|
||||
});
|
||||
|
||||
return new BABYLON.BoundingBox(min, max);
|
||||
}
|
||||
|
||||
const navmeshdebug = this.navigationPlugin.createDebugNavMesh(scene);
|
||||
const matdebug = new BABYLON.StandardMaterial("matdebug", scene);
|
||||
matdebug.backFaceCulling = false;//启用双面材质
|
||||
matdebug.diffuseColor = new BABYLON.Color3(0.1, 0.2, 1);
|
||||
matdebug.alpha = 0.5;
|
||||
navmeshdebug.material = matdebug;
|
||||
public exportNavDataToFileDownload() {
|
||||
const data = this.navigationPlugin!.getNavmeshData();
|
||||
saveAs(
|
||||
new Blob([data as unknown as BlobPart], {
|
||||
type: "application/octet-stream",
|
||||
}),
|
||||
`${this.mapName}.txt`
|
||||
);
|
||||
|
||||
|
||||
|
||||
// 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]
|
||||
}
|
||||
|
||||
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 => {
|
||||
const loc = this.getMinAMaxPosExtend(mesh);
|
||||
min = BABYLON.Vector3.Minimize(min, loc[0]);
|
||||
max = BABYLON.Vector3.Maximize(max, loc[1]);
|
||||
});
|
||||
|
||||
return new BABYLON.BoundingBox(min, max);
|
||||
}
|
||||
|
||||
public exportNavDataToFileDownload() {
|
||||
|
||||
const data = this.navigationPlugin!.getNavmeshData()
|
||||
saveAs(new Blob([data], {
|
||||
type: "application/octet-stream",
|
||||
}), `${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`
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user