diff --git a/src/3d/load3d.ts b/src/3d/load3d.ts index 7eb63f1..b07b0cd 100644 --- a/src/3d/load3d.ts +++ b/src/3d/load3d.ts @@ -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) diff --git a/src/3d/renders/agent/PlayerAgent.ts b/src/3d/renders/agent/PlayerAgent.ts index 3140339..5d476de 100644 --- a/src/3d/renders/agent/PlayerAgent.ts +++ b/src/3d/renders/agent/PlayerAgent.ts @@ -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 = 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 = new Map() - 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> { + // 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 + ) { + 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> { - // 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) { + 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`) - } -} \ No newline at end of file + // const meshInfoJson = JSON.stringify(this.meshInfo); + // saveAs( + // new Blob([meshInfoJson], { + // type: "application/json", + // }), + // `${this.mapName}_info.json` + // ); + } +}