mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-11-11 08:38:45 +00:00
提交Unity 联机Pro
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ba5c48ac57e41c4b498837ce26419cb
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,102 @@
|
||||
using DotRecast.Detour;
|
||||
using DotRecast.Recast.Geom;
|
||||
using DotRecast.Recast.Toolset.Geom;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Builder
|
||||
{
|
||||
public static class DemoNavMeshBuilder
|
||||
{
|
||||
public static DtNavMeshCreateParams GetNavMeshCreateParams(IInputGeomProvider geom, LFloat cellSize,
|
||||
LFloat cellHeight, LFloat agentHeight, LFloat agentRadius, LFloat agentMaxClimb,
|
||||
RcBuilderResult rcResult)
|
||||
{
|
||||
RcPolyMesh pmesh = rcResult.Mesh;
|
||||
RcPolyMeshDetail dmesh = rcResult.MeshDetail;
|
||||
DtNavMeshCreateParams option = new DtNavMeshCreateParams();
|
||||
for (int i = 0; i < pmesh.npolys; ++i)
|
||||
{
|
||||
pmesh.flags[i] = 1;
|
||||
}
|
||||
|
||||
option.verts = pmesh.verts;
|
||||
option.vertCount = pmesh.nverts;
|
||||
option.polys = pmesh.polys;
|
||||
option.polyAreas = pmesh.areas;
|
||||
option.polyFlags = pmesh.flags;
|
||||
option.polyCount = pmesh.npolys;
|
||||
option.nvp = pmesh.nvp;
|
||||
if (dmesh != null)
|
||||
{
|
||||
option.detailMeshes = dmesh.meshes;
|
||||
option.detailVerts = dmesh.verts;
|
||||
option.detailVertsCount = dmesh.nverts;
|
||||
option.detailTris = dmesh.tris;
|
||||
option.detailTriCount = dmesh.ntris;
|
||||
}
|
||||
|
||||
option.walkableHeight = agentHeight;
|
||||
option.walkableRadius = agentRadius;
|
||||
option.walkableClimb = agentMaxClimb;
|
||||
option.bmin = pmesh.bmin;
|
||||
option.bmax = pmesh.bmax;
|
||||
option.cs = cellSize;
|
||||
option.ch = cellHeight;
|
||||
option.buildBvTree = true;
|
||||
|
||||
var offMeshConnections = geom.GetOffMeshConnections();
|
||||
option.offMeshConCount = offMeshConnections.Count;
|
||||
option.offMeshConVerts = new LFloat[option.offMeshConCount * 6];
|
||||
option.offMeshConRad = new LFloat[option.offMeshConCount];
|
||||
option.offMeshConDir = new int[option.offMeshConCount];
|
||||
option.offMeshConAreas = new int[option.offMeshConCount];
|
||||
option.offMeshConFlags = new int[option.offMeshConCount];
|
||||
option.offMeshConUserID = new int[option.offMeshConCount];
|
||||
for (int i = 0; i < option.offMeshConCount; i++)
|
||||
{
|
||||
RcOffMeshConnection offMeshCon = offMeshConnections[i];
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
option.offMeshConVerts[6 * i + j] = offMeshCon.verts[j];
|
||||
}
|
||||
|
||||
option.offMeshConRad[i] = offMeshCon.radius;
|
||||
option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0;
|
||||
option.offMeshConAreas[i] = offMeshCon.area;
|
||||
option.offMeshConFlags[i] = offMeshCon.flags;
|
||||
// option.offMeshConUserID[i] = offMeshCon.userId;
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
public static DtMeshData UpdateAreaAndFlags(DtMeshData meshData)
|
||||
{
|
||||
// Update poly flags from areas.
|
||||
for (int i = 0; i < meshData.polys.Length; ++i)
|
||||
{
|
||||
if (meshData.polys[i].GetArea() == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WALKABLE)
|
||||
{
|
||||
meshData.polys[i].SetArea(SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND);
|
||||
}
|
||||
|
||||
if (meshData.polys[i].GetArea() == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND
|
||||
|| meshData.polys[i].GetArea() == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GRASS
|
||||
|| meshData.polys[i].GetArea() == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD)
|
||||
{
|
||||
meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_WALK;
|
||||
}
|
||||
else if (meshData.polys[i].GetArea() == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER)
|
||||
{
|
||||
meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM;
|
||||
}
|
||||
else if (meshData.polys[i].GetArea() == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_DOOR)
|
||||
{
|
||||
meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR;
|
||||
}
|
||||
}
|
||||
|
||||
return meshData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2849d98e96f6407b89a8f0e701552aa6
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DotRecast.Detour;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Builder
|
||||
{
|
||||
public class NavMeshBuildResult
|
||||
{
|
||||
public readonly bool Success;
|
||||
public readonly IList<RcBuilderResult> RecastBuilderResults;
|
||||
public readonly DtNavMesh NavMesh;
|
||||
|
||||
public NavMeshBuildResult()
|
||||
{
|
||||
Success = false;
|
||||
RecastBuilderResults = Array.Empty<RcBuilderResult>();
|
||||
NavMesh = null;
|
||||
}
|
||||
|
||||
public NavMeshBuildResult(IList<RcBuilderResult> recastBuilderResults, DtNavMesh navMesh)
|
||||
{
|
||||
Success = true;
|
||||
RecastBuilderResults = recastBuilderResults;
|
||||
NavMesh = navMesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f910a46b20cc4261860037c31fba5c1a
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org
|
||||
DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using DotRecast.Core.Collections;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Builder
|
||||
{
|
||||
public class SampleAreaModifications
|
||||
{
|
||||
public const int SAMPLE_POLYAREA_TYPE_GROUND = 0x0;
|
||||
public const int SAMPLE_POLYAREA_TYPE_WATER = 0x1;
|
||||
public const int SAMPLE_POLYAREA_TYPE_ROAD = 0x2;
|
||||
public const int SAMPLE_POLYAREA_TYPE_DOOR = 0x3;
|
||||
public const int SAMPLE_POLYAREA_TYPE_GRASS = 0x4;
|
||||
public const int SAMPLE_POLYAREA_TYPE_JUMP = 0x5;
|
||||
public const int SAMPLE_POLYAREA_TYPE_JUMP_AUTO = 0x6;
|
||||
public const int SAMPLE_POLYAREA_TYPE_WALKABLE = 0x3f;
|
||||
|
||||
public static readonly int SAMPLE_POLYFLAGS_WALK = 0x01; // Ability to walk (ground, grass, road)
|
||||
public static readonly int SAMPLE_POLYFLAGS_SWIM = 0x02; // Ability to swim (water).
|
||||
public static readonly int SAMPLE_POLYFLAGS_DOOR = 0x04; // Ability to move through doors.
|
||||
public static readonly int SAMPLE_POLYFLAGS_JUMP = 0x08; // Ability to jump.
|
||||
public static readonly int SAMPLE_POLYFLAGS_DISABLED = 0x10; // Disabled polygon
|
||||
public static readonly int SAMPLE_POLYFLAGS_ALL = 0xffff; // All abilities.
|
||||
|
||||
public static readonly RcAreaModification SAMPLE_AREAMOD_WALKABLE = new RcAreaModification(SAMPLE_POLYAREA_TYPE_WALKABLE);
|
||||
public static readonly RcAreaModification SAMPLE_AREAMOD_GROUND = new RcAreaModification(SAMPLE_POLYAREA_TYPE_GROUND);
|
||||
public static readonly RcAreaModification SAMPLE_AREAMOD_WATER = new RcAreaModification(SAMPLE_POLYAREA_TYPE_WATER);
|
||||
public static readonly RcAreaModification SAMPLE_AREAMOD_ROAD = new RcAreaModification(SAMPLE_POLYAREA_TYPE_ROAD);
|
||||
public static readonly RcAreaModification SAMPLE_AREAMOD_GRASS = new RcAreaModification(SAMPLE_POLYAREA_TYPE_GRASS);
|
||||
public static readonly RcAreaModification SAMPLE_AREAMOD_DOOR = new RcAreaModification(SAMPLE_POLYAREA_TYPE_DOOR);
|
||||
public static readonly RcAreaModification SAMPLE_AREAMOD_JUMP = new RcAreaModification(SAMPLE_POLYAREA_TYPE_JUMP);
|
||||
|
||||
public static readonly RcImmutableArray<RcAreaModification> Values = RcImmutableArray.Create(
|
||||
SAMPLE_AREAMOD_WALKABLE,
|
||||
SAMPLE_AREAMOD_GROUND,
|
||||
SAMPLE_AREAMOD_WATER,
|
||||
SAMPLE_AREAMOD_ROAD,
|
||||
SAMPLE_AREAMOD_GRASS,
|
||||
SAMPLE_AREAMOD_DOOR,
|
||||
SAMPLE_AREAMOD_JUMP
|
||||
);
|
||||
|
||||
public static RcAreaModification OfValue(int value)
|
||||
{
|
||||
foreach (var v in Values)
|
||||
{
|
||||
if (v.Value == value)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
return SAMPLE_AREAMOD_GRASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abb7b7cb546b46df8d1364c0d6f31dc2
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
using DotRecast.Core.Collections;
|
||||
using DotRecast.Detour;
|
||||
using DotRecast.Recast.Toolset.Geom;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Builder
|
||||
{
|
||||
public class SoloNavMeshBuilder
|
||||
{
|
||||
public NavMeshBuildResult Build(DemoInputGeomProvider geom, RcNavMeshBuildSettings settings)
|
||||
{
|
||||
return Build(geom,
|
||||
RcPartitionType.OfValue(settings.partitioning),
|
||||
settings.cellSize, settings.cellHeight,
|
||||
settings.agentMaxSlope, settings.agentHeight, settings.agentRadius, settings.agentMaxClimb,
|
||||
settings.minRegionSize, settings.mergedRegionSize,
|
||||
settings.edgeMaxLen, settings.edgeMaxError,
|
||||
settings.vertsPerPoly,
|
||||
settings.detailSampleDist, settings.detailSampleMaxError,
|
||||
settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans,
|
||||
settings.keepInterResults);
|
||||
}
|
||||
|
||||
public NavMeshBuildResult Build(DemoInputGeomProvider geom,
|
||||
RcPartition partitionType,
|
||||
LFloat cellSize, LFloat cellHeight,
|
||||
LFloat agentMaxSlope, LFloat agentHeight, LFloat agentRadius, LFloat agentMaxClimb,
|
||||
int regionMinSize, int regionMergeSize,
|
||||
LFloat edgeMaxLen, LFloat edgeMaxError,
|
||||
int vertsPerPoly,
|
||||
LFloat detailSampleDist, LFloat detailSampleMaxError,
|
||||
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
|
||||
bool keepInterResults)
|
||||
{
|
||||
RcConfig cfg = new RcConfig(
|
||||
partitionType,
|
||||
cellSize, cellHeight,
|
||||
agentMaxSlope, agentHeight, agentRadius, agentMaxClimb,
|
||||
regionMinSize, regionMergeSize,
|
||||
edgeMaxLen, edgeMaxError,
|
||||
vertsPerPoly,
|
||||
detailSampleDist, detailSampleMaxError,
|
||||
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
|
||||
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
|
||||
|
||||
RcBuilderResult rcResult = BuildRecastResult(geom, cfg, keepInterResults);
|
||||
var meshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, rcResult);
|
||||
if (null == meshData)
|
||||
{
|
||||
return new NavMeshBuildResult();
|
||||
}
|
||||
|
||||
var navMesh = BuildNavMesh(meshData, vertsPerPoly);
|
||||
return new NavMeshBuildResult(RcImmutableArray.Create(rcResult), navMesh);
|
||||
}
|
||||
|
||||
private DtNavMesh BuildNavMesh(DtMeshData meshData, int vertsPerPoly)
|
||||
{
|
||||
return new DtNavMesh(meshData, vertsPerPoly, 0);
|
||||
}
|
||||
|
||||
private RcBuilderResult BuildRecastResult(DemoInputGeomProvider geom, RcConfig cfg, bool keepInterResults)
|
||||
{
|
||||
RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax());
|
||||
RcBuilder rcBuilder = new RcBuilder();
|
||||
return rcBuilder.Build(geom, bcfg, keepInterResults);
|
||||
}
|
||||
|
||||
public DtMeshData BuildMeshData(DemoInputGeomProvider geom,
|
||||
LFloat cellSize, LFloat cellHeight,
|
||||
LFloat agentHeight, LFloat agentRadius, LFloat agentMaxClimb,
|
||||
RcBuilderResult result)
|
||||
{
|
||||
DtNavMeshCreateParams option = DemoNavMeshBuilder
|
||||
.GetNavMeshCreateParams(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, result);
|
||||
var meshData = DtNavMeshBuilder.CreateNavMeshData(option);
|
||||
if (null == meshData)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return DemoNavMeshBuilder.UpdateAreaAndFlags(meshData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8922873adf3048f9bbffeb83a88eaeff
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DotRecast.Detour;
|
||||
using DotRecast.Recast.Geom;
|
||||
using DotRecast.Recast.Toolset.Geom;
|
||||
using JNGame.Math;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Builder
|
||||
{
|
||||
public class TileNavMeshBuilder
|
||||
{
|
||||
public TileNavMeshBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
public NavMeshBuildResult Build(IInputGeomProvider geom, RcNavMeshBuildSettings settings)
|
||||
{
|
||||
return Build(geom,
|
||||
settings.tileSize,
|
||||
RcPartitionType.OfValue(settings.partitioning),
|
||||
settings.cellSize, settings.cellHeight,
|
||||
settings.agentMaxSlope, settings.agentHeight, settings.agentRadius, settings.agentMaxClimb,
|
||||
settings.minRegionSize, settings.mergedRegionSize,
|
||||
settings.edgeMaxLen, settings.edgeMaxError,
|
||||
settings.vertsPerPoly, settings.detailSampleDist, settings.detailSampleMaxError,
|
||||
settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans,
|
||||
settings.keepInterResults, settings.buildAll);
|
||||
}
|
||||
|
||||
public NavMeshBuildResult Build(IInputGeomProvider geom,
|
||||
int tileSize,
|
||||
RcPartition partitionType,
|
||||
LFloat cellSize, LFloat cellHeight,
|
||||
LFloat agentMaxSlope, LFloat agentHeight, LFloat agentRadius, LFloat agentMaxClimb,
|
||||
int regionMinSize, int regionMergeSize,
|
||||
LFloat edgeMaxLen, LFloat edgeMaxError,
|
||||
int vertsPerPoly,
|
||||
LFloat detailSampleDist, LFloat detailSampleMaxError,
|
||||
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
|
||||
bool keepInterResults, bool buildAll)
|
||||
{
|
||||
// string text = "";
|
||||
//
|
||||
// text += $"\n{JsonConvert.SerializeObject(geom)}";
|
||||
// text += $"\n{tileSize}";
|
||||
// text += $"\n{JsonConvert.SerializeObject(partitionType)}";
|
||||
// text += $"\n{cellSize}";
|
||||
// text += $"\n{cellHeight}";
|
||||
// text += $"\n{agentMaxSlope}";
|
||||
// text += $"\n{agentHeight}";
|
||||
// text += $"\n{agentRadius}";
|
||||
// text += $"\n{agentMaxClimb}";
|
||||
// text += $"\n{regionMinSize}";
|
||||
// text += $"\n{regionMergeSize}";
|
||||
// text += $"\n{edgeMaxLen}";
|
||||
// text += $"\n{edgeMaxError}";
|
||||
// text += $"\n{vertsPerPoly}";
|
||||
// text += $"\n{detailSampleDist}";
|
||||
// text += $"\n{detailSampleMaxError}";
|
||||
// text += $"\n{filterLowHangingObstacles}";
|
||||
// text += $"\n{filterLedgeSpans}";
|
||||
// text += $"\n{filterWalkableLowHeightSpans}";
|
||||
// text += $"\n{keepInterResults}";
|
||||
// text += $"\n{buildAll}";
|
||||
|
||||
List<RcBuilderResult> results = BuildRecastResult(
|
||||
geom,
|
||||
tileSize,
|
||||
partitionType,
|
||||
cellSize, cellHeight,
|
||||
agentMaxSlope, agentHeight, agentRadius, agentMaxClimb,
|
||||
regionMinSize, regionMergeSize,
|
||||
edgeMaxLen, edgeMaxError,
|
||||
vertsPerPoly,
|
||||
detailSampleDist, detailSampleMaxError,
|
||||
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
|
||||
keepInterResults, buildAll
|
||||
);
|
||||
|
||||
// foreach (var rcBuilderResult in results)
|
||||
// {
|
||||
// text += $"\n{JsonConvert.SerializeObject(rcBuilderResult)}";
|
||||
// }
|
||||
|
||||
var tileMeshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, results);
|
||||
var tileNavMesh = BuildNavMesh(geom, tileMeshData, cellSize, tileSize, vertsPerPoly);
|
||||
return new NavMeshBuildResult(results, tileNavMesh);
|
||||
}
|
||||
|
||||
public List<RcBuilderResult> BuildRecastResult(IInputGeomProvider geom,
|
||||
int tileSize,
|
||||
RcPartition partitionType,
|
||||
LFloat cellSize, LFloat cellHeight,
|
||||
LFloat agentMaxSlope, LFloat agentHeight, LFloat agentRadius, LFloat agentMaxClimb,
|
||||
int regionMinSize, int regionMergeSize,
|
||||
LFloat edgeMaxLen, LFloat edgeMaxError,
|
||||
int vertsPerPoly,
|
||||
LFloat detailSampleDist, LFloat detailSampleMaxError,
|
||||
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
|
||||
bool keepInterResults, bool buildAll)
|
||||
|
||||
{
|
||||
RcConfig cfg = new RcConfig(true, tileSize, tileSize,
|
||||
RcConfig.CalcBorder(agentRadius, cellSize),
|
||||
partitionType,
|
||||
cellSize, cellHeight,
|
||||
agentMaxSlope, agentHeight, agentRadius, agentMaxClimb,
|
||||
regionMinSize * regionMinSize * cellSize * cellSize, regionMergeSize * regionMergeSize * cellSize * cellSize,
|
||||
edgeMaxLen, edgeMaxError,
|
||||
vertsPerPoly,
|
||||
detailSampleDist, detailSampleMaxError,
|
||||
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
|
||||
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
|
||||
RcBuilder rcBuilder = new RcBuilder();
|
||||
List<RcBuilderResult> results = rcBuilder.BuildTiles(geom, cfg, keepInterResults, buildAll, Environment.ProcessorCount + 1);
|
||||
return results;
|
||||
}
|
||||
|
||||
public DtNavMesh BuildNavMesh(IInputGeomProvider geom, List<DtMeshData> meshData, LFloat cellSize, int tileSize, int vertsPerPoly)
|
||||
{
|
||||
DtNavMeshParams navMeshParams = new DtNavMeshParams();
|
||||
navMeshParams.orig = geom.GetMeshBoundsMin();
|
||||
navMeshParams.tileWidth = tileSize * cellSize;
|
||||
navMeshParams.tileHeight = tileSize * cellSize;
|
||||
|
||||
// Snprintf(text, 64, "Tiles %d x %d", tw, th);
|
||||
|
||||
navMeshParams.maxTiles = GetMaxTiles(geom, cellSize, tileSize);
|
||||
navMeshParams.maxPolys = GetMaxPolysPerTile(geom, cellSize, tileSize);
|
||||
DtNavMesh navMesh = new DtNavMesh(navMeshParams, vertsPerPoly);
|
||||
meshData.ForEach(md => navMesh.AddTile(md, 0, 0));
|
||||
return navMesh;
|
||||
}
|
||||
|
||||
public List<DtMeshData> BuildMeshData(IInputGeomProvider geom, LFloat cellSize, LFloat cellHeight, LFloat agentHeight,
|
||||
LFloat agentRadius, LFloat agentMaxClimb, IList<RcBuilderResult> results)
|
||||
{
|
||||
// Add tiles to nav mesh
|
||||
List<DtMeshData> meshData = new List<DtMeshData>();
|
||||
foreach (RcBuilderResult result in results)
|
||||
{
|
||||
int x = result.TileX;
|
||||
int z = result.TileZ;
|
||||
DtNavMeshCreateParams option = DemoNavMeshBuilder.GetNavMeshCreateParams(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, result);
|
||||
|
||||
option.tileX = x;
|
||||
option.tileZ = z;
|
||||
DtMeshData md = DtNavMeshBuilder.CreateNavMeshData(option);
|
||||
if (md != null)
|
||||
{
|
||||
meshData.Add(DemoNavMeshBuilder.UpdateAreaAndFlags(md));
|
||||
}
|
||||
}
|
||||
|
||||
return meshData;
|
||||
}
|
||||
|
||||
|
||||
public int GetMaxTiles(IInputGeomProvider geom, LFloat cellSize, int tileSize)
|
||||
{
|
||||
int tileBits = GetTileBits(geom, cellSize, tileSize);
|
||||
return 1 << tileBits;
|
||||
}
|
||||
|
||||
public int GetMaxPolysPerTile(IInputGeomProvider geom, LFloat cellSize, int tileSize)
|
||||
{
|
||||
int polyBits = 22 - GetTileBits(geom, cellSize, tileSize);
|
||||
return 1 << polyBits;
|
||||
}
|
||||
|
||||
private int GetTileBits(IInputGeomProvider geom, LFloat cellSize, int tileSize)
|
||||
{
|
||||
RcCommons.CalcGridSize(geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), cellSize, out var gw, out var gh);
|
||||
int tw = (gw + tileSize - 1) / tileSize;
|
||||
int th = (gh + tileSize - 1) / tileSize;
|
||||
int tileBits = LMath.Min(DtUtils.Ilog2(DtUtils.NextPow2(tw * th)), 14);
|
||||
return tileBits;
|
||||
}
|
||||
|
||||
public int[] GetTiles(DemoInputGeomProvider geom, LFloat cellSize, int tileSize)
|
||||
{
|
||||
RcCommons.CalcGridSize(geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), cellSize, out var gw, out var gh);
|
||||
int tw = (gw + tileSize - 1) / tileSize;
|
||||
int th = (gh + tileSize - 1) / tileSize;
|
||||
return new int[] { tw, th };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddccefd2cfb44e858c36ce544be031a4
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<PackageId>DotRecast.Recast.Toolset</PackageId>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<Authors>ikpil</Authors>
|
||||
<Description>DotRecast - a port of Recast Detour, Industry-standard navigation mesh toolset for .NET, C#, Unity3D, games, servers</Description>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageProjectUrl>https://github.com/ikpil/DotRecast</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/ikpil/DotRecast</RepositoryUrl>
|
||||
<PackageTags>game gamedev ai csharp server unity navigation game-development unity3d pathfinding pathfinder recast detour navmesh crowd-simulation recastnavigation</PackageTags>
|
||||
<PackageReleaseNotes>https://github.com/ikpil/DotRecast/blob/main/CHANGELOG.md</PackageReleaseNotes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../../README.md" Pack="true" PackagePath="\"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DotRecast.Core\DotRecast.Core.csproj"/>
|
||||
<ProjectReference Include="..\DotRecast.Recast\DotRecast.Recast.csproj"/>
|
||||
<ProjectReference Include="..\DotRecast.Detour\DotRecast.Detour.csproj"/>
|
||||
<ProjectReference Include="..\DotRecast.Detour.Crowd\DotRecast.Detour.Crowd.csproj"/>
|
||||
<ProjectReference Include="..\DotRecast.Detour.Dynamic\DotRecast.Detour.Dynamic.csproj"/>
|
||||
<ProjectReference Include="..\DotRecast.Detour.Extras\DotRecast.Detour.Extras.csproj"/>
|
||||
<ProjectReference Include="..\DotRecast.Detour.TileCache\DotRecast.Detour.TileCache.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3946930b95f34dd7ace5692becf67638
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c626ab86d0764a7b8bd16576de7dd02b
|
||||
timeCreated: 1715335342
|
||||
@@ -0,0 +1,74 @@
|
||||
using DotRecast.Detour;
|
||||
using DotRecast.Detour.TileCache;
|
||||
using DotRecast.Recast.Geom;
|
||||
using DotRecast.Recast.Toolset.Builder;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Geom
|
||||
{
|
||||
public class DemoDtTileCacheMeshProcess : IDtTileCacheMeshProcess
|
||||
{
|
||||
private IInputGeomProvider _geom;
|
||||
|
||||
public DemoDtTileCacheMeshProcess()
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(IInputGeomProvider geom)
|
||||
{
|
||||
_geom = geom;
|
||||
}
|
||||
|
||||
public void Process(DtNavMeshCreateParams option)
|
||||
{
|
||||
// Update poly flags from areas.
|
||||
for (int i = 0; i < option.polyCount; ++i)
|
||||
{
|
||||
if (option.polyAreas[i] == DtTileCacheBuilder.DT_TILECACHE_WALKABLE_AREA)
|
||||
option.polyAreas[i] = SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND;
|
||||
|
||||
if (option.polyAreas[i] == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND ||
|
||||
option.polyAreas[i] == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GRASS ||
|
||||
option.polyAreas[i] == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD)
|
||||
{
|
||||
option.polyFlags[i] = SampleAreaModifications.SAMPLE_POLYFLAGS_WALK;
|
||||
}
|
||||
else if (option.polyAreas[i] == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER)
|
||||
{
|
||||
option.polyFlags[i] = SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM;
|
||||
}
|
||||
else if (option.polyAreas[i] == SampleAreaModifications.SAMPLE_POLYAREA_TYPE_DOOR)
|
||||
{
|
||||
option.polyFlags[i] = SampleAreaModifications.SAMPLE_POLYFLAGS_WALK | SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass in off-mesh connections.
|
||||
if (null != _geom)
|
||||
{
|
||||
var offMeshConnections = _geom.GetOffMeshConnections();
|
||||
option.offMeshConVerts = new LFloat[option.offMeshConCount * 6];
|
||||
option.offMeshConRad = new LFloat[option.offMeshConCount];
|
||||
option.offMeshConDir = new int[option.offMeshConCount];
|
||||
option.offMeshConAreas = new int[option.offMeshConCount];
|
||||
option.offMeshConFlags = new int[option.offMeshConCount];
|
||||
option.offMeshConUserID = new int[option.offMeshConCount];
|
||||
option.offMeshConCount = offMeshConnections.Count;
|
||||
for (int i = 0; i < option.offMeshConCount; i++)
|
||||
{
|
||||
RcOffMeshConnection offMeshCon = offMeshConnections[i];
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
option.offMeshConVerts[6 * i + j] = offMeshCon.verts[j];
|
||||
}
|
||||
|
||||
option.offMeshConRad[i] = offMeshCon.radius;
|
||||
option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0;
|
||||
option.offMeshConAreas[i] = offMeshCon.area;
|
||||
option.offMeshConFlags[i] = offMeshCon.flags;
|
||||
// option.offMeshConUserID[i] = offMeshCon.userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 071514546bb84f8bb0233a1ac5aee2a9
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org
|
||||
DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DotRecast.Core;
|
||||
using DotRecast.Core.Collections;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Recast.Geom;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Geom
|
||||
{
|
||||
public class DemoInputGeomProvider : IInputGeomProvider
|
||||
{
|
||||
public readonly LFloat[] vertices;
|
||||
public readonly int[] faces;
|
||||
public readonly LFloat[] normals;
|
||||
private readonly RcVec3f bmin;
|
||||
private readonly RcVec3f bmax;
|
||||
|
||||
private readonly List<RcConvexVolume> _convexVolumes = new List<RcConvexVolume>();
|
||||
private readonly List<RcOffMeshConnection> _offMeshConnections = new List<RcOffMeshConnection>();
|
||||
private readonly RcTriMesh _mesh;
|
||||
|
||||
public static DemoInputGeomProvider LoadFile(string objFilePath)
|
||||
{
|
||||
byte[] chunk = RcResources.Load(objFilePath);
|
||||
var context = RcObjImporter.LoadContext(chunk);
|
||||
return new DemoInputGeomProvider(context.vertexPositions, context.meshFaces);
|
||||
}
|
||||
|
||||
public DemoInputGeomProvider(List<LFloat> vertexPositions, List<int> meshFaces) :
|
||||
this(MapVertices(vertexPositions), MapFaces(meshFaces))
|
||||
{
|
||||
}
|
||||
|
||||
public DemoInputGeomProvider(LFloat[] vertices, int[] faces)
|
||||
{
|
||||
this.vertices = vertices;
|
||||
this.faces = faces;
|
||||
normals = new LFloat[faces.Length];
|
||||
CalculateNormals();
|
||||
bmin = RcVecUtils.Create(vertices);
|
||||
bmax = RcVecUtils.Create(vertices);
|
||||
for (int i = 1; i < vertices.Length / 3; i++)
|
||||
{
|
||||
bmin = RcVecUtils.Min(bmin, vertices, i * 3);
|
||||
bmax = RcVecUtils.Max(bmax, vertices, i * 3);
|
||||
}
|
||||
|
||||
_mesh = new RcTriMesh(vertices, faces);
|
||||
}
|
||||
|
||||
public RcTriMesh GetMesh()
|
||||
{
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
public RcVec3f GetMeshBoundsMin()
|
||||
{
|
||||
return bmin;
|
||||
}
|
||||
|
||||
public RcVec3f GetMeshBoundsMax()
|
||||
{
|
||||
return bmax;
|
||||
}
|
||||
|
||||
public void CalculateNormals()
|
||||
{
|
||||
for (int i = 0; i < faces.Length; i += 3)
|
||||
{
|
||||
int v0 = faces[i] * 3;
|
||||
int v1 = faces[i + 1] * 3;
|
||||
int v2 = faces[i + 2] * 3;
|
||||
var e0 = RcVecUtils.Subtract(vertices, v1, v0);
|
||||
var e1 = RcVecUtils.Subtract(vertices, v2, v0);
|
||||
|
||||
normals[i] = e0.Y * e1.Z - e0.Z * e1.Y;
|
||||
normals[i + 1] = e0.Z * e1.X - e0.X * e1.Z;
|
||||
normals[i + 2] = e0.X * e1.Y - e0.Y * e1.X;
|
||||
LFloat d = LMath.Sqrt(normals[i] * normals[i] + normals[i + 1] * normals[i + 1] + normals[i + 2] * normals[i + 2]);
|
||||
if (d > 0)
|
||||
{
|
||||
d = (LFloat)1.0f / d;
|
||||
normals[i] *= d;
|
||||
normals[i + 1] *= d;
|
||||
normals[i + 2] *= d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IList<RcConvexVolume> ConvexVolumes()
|
||||
{
|
||||
return _convexVolumes;
|
||||
}
|
||||
|
||||
public IEnumerable<RcTriMesh> Meshes()
|
||||
{
|
||||
return RcImmutableArray.Create(_mesh);
|
||||
}
|
||||
|
||||
public List<RcOffMeshConnection> GetOffMeshConnections()
|
||||
{
|
||||
return _offMeshConnections;
|
||||
}
|
||||
|
||||
public void AddOffMeshConnection(RcVec3f start, RcVec3f end, LFloat radius, bool bidir, int area, int flags)
|
||||
{
|
||||
_offMeshConnections.Add(new RcOffMeshConnection(start, end, radius, bidir, area, flags));
|
||||
}
|
||||
|
||||
public void RemoveOffMeshConnections(Predicate<RcOffMeshConnection> filter)
|
||||
{
|
||||
//offMeshConnections.RetainAll(offMeshConnections.Stream().Filter(c -> !filter.Test(c)).Collect(ToList()));
|
||||
_offMeshConnections.RemoveAll(filter); // TODO : 확인 필요
|
||||
}
|
||||
|
||||
public bool RaycastMesh(RcVec3f src, RcVec3f dst, out LFloat tmin)
|
||||
{
|
||||
tmin = (LFloat)1.0f;
|
||||
|
||||
// Prune hit ray.
|
||||
if (!RcIntersections.IsectSegAABB(src, dst, bmin, bmax, out var btmin, out var btmax))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var p = new RcVec2f();
|
||||
var q = new RcVec2f();
|
||||
p.X = src.X + (dst.X - src.X) * btmin;
|
||||
p.Y = src.Z + (dst.Z - src.Z) * btmin;
|
||||
q.X = src.X + (dst.X - src.X) * btmax;
|
||||
q.Y = src.Z + (dst.Z - src.Z) * btmax;
|
||||
|
||||
List<RcChunkyTriMeshNode> chunks = _mesh.chunkyTriMesh.GetChunksOverlappingSegment(p, q);
|
||||
if (0 == chunks.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
tmin = (LFloat)1.0f;
|
||||
bool hit = false;
|
||||
foreach (RcChunkyTriMeshNode chunk in chunks)
|
||||
{
|
||||
int[] tris = chunk.tris;
|
||||
for (int j = 0; j < chunk.tris.Length; j += 3)
|
||||
{
|
||||
RcVec3f v1 = new RcVec3f(
|
||||
vertices[tris[j] * 3],
|
||||
vertices[tris[j] * 3 + 1],
|
||||
vertices[tris[j] * 3 + 2]
|
||||
);
|
||||
RcVec3f v2 = new RcVec3f(
|
||||
vertices[tris[j + 1] * 3],
|
||||
vertices[tris[j + 1] * 3 + 1],
|
||||
vertices[tris[j + 1] * 3 + 2]
|
||||
);
|
||||
RcVec3f v3 = new RcVec3f(
|
||||
vertices[tris[j + 2] * 3],
|
||||
vertices[tris[j + 2] * 3 + 1],
|
||||
vertices[tris[j + 2] * 3 + 2]
|
||||
);
|
||||
if (RcIntersections.IntersectSegmentTriangle(src, dst, v1, v2, v3, out var t))
|
||||
{
|
||||
if (t < tmin)
|
||||
{
|
||||
tmin = t;
|
||||
}
|
||||
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
||||
public void AddConvexVolume(LFloat[] verts, LFloat minh, LFloat maxh, RcAreaModification areaMod)
|
||||
{
|
||||
RcConvexVolume volume = new RcConvexVolume();
|
||||
volume.verts = verts;
|
||||
volume.hmin = minh;
|
||||
volume.hmax = maxh;
|
||||
volume.areaMod = areaMod;
|
||||
AddConvexVolume(volume);
|
||||
}
|
||||
|
||||
public void AddConvexVolume(RcConvexVolume volume)
|
||||
{
|
||||
_convexVolumes.Add(volume);
|
||||
}
|
||||
|
||||
public void ClearConvexVolumes()
|
||||
{
|
||||
_convexVolumes.Clear();
|
||||
}
|
||||
|
||||
private static int[] MapFaces(List<int> meshFaces)
|
||||
{
|
||||
int[] faces = new int[meshFaces.Count];
|
||||
for (int i = 0; i < faces.Length; i++)
|
||||
{
|
||||
faces[i] = meshFaces[i];
|
||||
}
|
||||
|
||||
return faces;
|
||||
}
|
||||
|
||||
private static LFloat[] MapVertices(List<LFloat> vertexPositions)
|
||||
{
|
||||
LFloat[] vertices = new LFloat[vertexPositions.Count];
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
vertices[i] = vertexPositions[i];
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0e8bcf210ea4a3a8aec33e63564ce5c
|
||||
timeCreated: 1715335342
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9fad519022e416ba038bd61e310c7cd
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,9 @@
|
||||
//using DotRecast.Recast.Demo.Draw;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public interface IRcGizmoMeshFilter
|
||||
{
|
||||
//void Render(RecastDebugDraw debugDraw);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 826e29d9df714f8f87508c6ee870ed5f
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,51 @@
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Detour.Dynamic.Colliders;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public class RcBoxGizmo : IRcGizmoMeshFilter
|
||||
{
|
||||
public static readonly int[] TRIANLGES =
|
||||
{
|
||||
0, 1, 2, 0, 2, 3, 4, 7, 6, 4, 6, 5, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2,
|
||||
2, 6, 7, 2, 7, 3, 4, 0, 3, 4, 3, 7
|
||||
};
|
||||
|
||||
public static readonly RcVec3f[] VERTS =
|
||||
{
|
||||
new RcVec3f(-(LFloat)1f, -(LFloat)1f, -(LFloat)1f),
|
||||
new RcVec3f((LFloat)1f, -(LFloat)1f, -(LFloat)1f),
|
||||
new RcVec3f((LFloat)1f, -(LFloat)1f, (LFloat)1f),
|
||||
new RcVec3f(-(LFloat)1f, -(LFloat)1f, (LFloat)1f),
|
||||
new RcVec3f(-(LFloat)1f, (LFloat)1f, -(LFloat)1f),
|
||||
new RcVec3f((LFloat)1f, (LFloat)1f, -(LFloat)1f),
|
||||
new RcVec3f((LFloat)1f, (LFloat)1f, (LFloat)1f),
|
||||
new RcVec3f(-(LFloat)1f, (LFloat)1f, (LFloat)1f),
|
||||
};
|
||||
|
||||
public readonly LFloat[] vertices = new LFloat[8 * 3];
|
||||
public readonly RcVec3f center;
|
||||
public readonly RcVec3f[] halfEdges;
|
||||
|
||||
public RcBoxGizmo(RcVec3f center, RcVec3f extent, RcVec3f forward, RcVec3f up) :
|
||||
this(center, DtBoxCollider.GetHalfEdges(up, forward, extent))
|
||||
{
|
||||
}
|
||||
|
||||
public RcBoxGizmo(RcVec3f center, RcVec3f[] halfEdges)
|
||||
{
|
||||
this.center = center;
|
||||
this.halfEdges = halfEdges;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
LFloat s0 = (i & 1) != 0 ? (LFloat)1f : -(LFloat)1f;
|
||||
LFloat s1 = (i & 2) != 0 ? (LFloat)1f : -(LFloat)1f;
|
||||
LFloat s2 = (i & 4) != 0 ? (LFloat)1f : -(LFloat)1f;
|
||||
vertices[i * 3 + 0] = center.X + s0 * halfEdges[0].X + s1 * halfEdges[1].X + s2 * halfEdges[2].X;
|
||||
vertices[i * 3 + 1] = center.Y + s0 * halfEdges[0].Y + s1 * halfEdges[1].Y + s2 * halfEdges[2].Y;
|
||||
vertices[i * 3 + 2] = center.Z + s0 * halfEdges[0].Z + s1 * halfEdges[1].Z + s2 * halfEdges[2].Z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: def9df06e44649748bbbf43b1b112f29
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using DotRecast.Core.Numerics;
|
||||
using JNGame.Math;
|
||||
using static DotRecast.Recast.Toolset.Gizmos.RcGizmoHelper;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public class RcCapsuleGizmo : IRcGizmoMeshFilter
|
||||
{
|
||||
public readonly LFloat[] vertices;
|
||||
public readonly int[] triangles;
|
||||
public readonly LFloat[] center;
|
||||
public readonly LFloat[] gradient;
|
||||
|
||||
public RcCapsuleGizmo(RcVec3f start, RcVec3f end, LFloat radius)
|
||||
{
|
||||
center = new LFloat[]
|
||||
{
|
||||
(LFloat)0.5f * (start.X + end.X), (LFloat)0.5f * (start.Y + end.Y),
|
||||
(LFloat)0.5f * (start.Z + end.Z)
|
||||
};
|
||||
RcVec3f axis = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
|
||||
RcVec3f[] normals = new RcVec3f[3];
|
||||
normals[1] = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
|
||||
normals[1] = RcVec3f.Normalize(normals[1]);
|
||||
normals[0] = GetSideVector(axis);
|
||||
normals[2] = RcVec3f.Zero;
|
||||
normals[2] = RcVec3f.Cross(normals[0], normals[1]);
|
||||
normals[2] = RcVec3f.Normalize(normals[2]);
|
||||
triangles = GenerateSphericalTriangles();
|
||||
var trX = new RcVec3f(normals[0].X, normals[1].X, normals[2].X);
|
||||
var trY = new RcVec3f(normals[0].Y, normals[1].Y, normals[2].Y);
|
||||
var trZ = new RcVec3f(normals[0].Z, normals[1].Z, normals[2].Z);
|
||||
LFloat[] spVertices = GenerateSphericalVertices();
|
||||
LFloat halfLength = (LFloat)0.5f * axis.Length();
|
||||
vertices = new LFloat[spVertices.Length];
|
||||
gradient = new LFloat[spVertices.Length / 3];
|
||||
RcVec3f v = new RcVec3f();
|
||||
for (int i = 0; i < spVertices.Length; i += 3)
|
||||
{
|
||||
LFloat offset = (i >= spVertices.Length / 2) ? -halfLength : halfLength;
|
||||
LFloat x = radius * spVertices[i];
|
||||
LFloat y = radius * spVertices[i + 1] + offset;
|
||||
LFloat z = radius * spVertices[i + 2];
|
||||
vertices[i] = x * trX.X + y * trX.Y + z * trX.Z + center[0];
|
||||
vertices[i + 1] = x * trY.X + y * trY.Y + z * trY.Z + center[1];
|
||||
vertices[i + 2] = x * trZ.X + y * trZ.Y + z * trZ.Z + center[2];
|
||||
v.X = vertices[i] - center[0];
|
||||
v.Y = vertices[i + 1] - center[1];
|
||||
v.Z = vertices[i + 2] - center[2];
|
||||
v = RcVec3f.Normalize(v);
|
||||
gradient[i / 3] = LMath.Clamp((LFloat)0.57735026f * (v.X + v.Y + v.Z), -(LFloat)1, (LFloat)1);
|
||||
}
|
||||
}
|
||||
|
||||
private RcVec3f GetSideVector(RcVec3f axis)
|
||||
{
|
||||
var side = new RcVec3f(1, 0, 0);
|
||||
if (axis.X > 0.8)
|
||||
{
|
||||
side = new RcVec3f(0, 0, 1);
|
||||
}
|
||||
|
||||
var forward = RcVec3f.Cross(side, axis);
|
||||
side = RcVec3f.Cross(axis, forward);
|
||||
side = RcVec3f.Normalize(side);
|
||||
return side;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b62a64e8a3fb4f4694c3dd0a0b5e3df2
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public class RcCompositeGizmo : IRcGizmoMeshFilter
|
||||
{
|
||||
public readonly IRcGizmoMeshFilter[] gizmoMeshes;
|
||||
|
||||
public RcCompositeGizmo(params IRcGizmoMeshFilter[] gizmoMeshes)
|
||||
{
|
||||
this.gizmoMeshes = gizmoMeshes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53f7c76765ff4c0c88ec5998cd37b3a1
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using DotRecast.Core.Numerics;
|
||||
using JNGame.Math;
|
||||
using static DotRecast.Recast.Toolset.Gizmos.RcGizmoHelper;
|
||||
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public class RcCylinderGizmo : IRcGizmoMeshFilter
|
||||
{
|
||||
public readonly LFloat[] vertices;
|
||||
public readonly int[] triangles;
|
||||
public readonly RcVec3f center;
|
||||
public readonly LFloat[] gradient;
|
||||
|
||||
public RcCylinderGizmo(RcVec3f start, RcVec3f end, LFloat radius)
|
||||
{
|
||||
center = new RcVec3f(
|
||||
(LFloat)0.5f * (start.X + end.X), (LFloat)0.5f * (start.Y + end.Y),
|
||||
(LFloat)0.5f * (start.Z + end.Z)
|
||||
);
|
||||
RcVec3f axis = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
|
||||
RcVec3f[] normals = new RcVec3f[3];
|
||||
normals[1] = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
|
||||
normals[1] = RcVec3f.Normalize(normals[1]);
|
||||
normals[0] = GetSideVector(axis);
|
||||
normals[2] = RcVec3f.Zero;
|
||||
normals[2] = RcVec3f.Cross(normals[0], normals[1]);
|
||||
normals[2] = RcVec3f.Normalize(normals[2]);
|
||||
triangles = GenerateCylindricalTriangles();
|
||||
RcVec3f trX = new RcVec3f(normals[0].X, normals[1].X, normals[2].X);
|
||||
RcVec3f trY = new RcVec3f(normals[0].Y, normals[1].Y, normals[2].Y);
|
||||
RcVec3f trZ = new RcVec3f(normals[0].Z, normals[1].Z, normals[2].Z);
|
||||
vertices = GenerateCylindricalVertices();
|
||||
LFloat halfLength = (LFloat)0.5f * axis.Length();
|
||||
gradient = new LFloat[vertices.Length / 3];
|
||||
RcVec3f v = new RcVec3f();
|
||||
for (int i = 0; i < vertices.Length; i += 3)
|
||||
{
|
||||
LFloat offset = (i >= vertices.Length / 2) ? -halfLength : halfLength;
|
||||
LFloat x = radius * vertices[i];
|
||||
LFloat y = vertices[i + 1] + offset;
|
||||
LFloat z = radius * vertices[i + 2];
|
||||
vertices[i] = x * trX.X + y * trX.Y + z * trX.Z + center.X;
|
||||
vertices[i + 1] = x * trY.X + y * trY.Y + z * trY.Z + center.Y;
|
||||
vertices[i + 2] = x * trZ.X + y * trZ.Y + z * trZ.Z + center.Z;
|
||||
if (i < vertices.Length / 4 || i >= 3 * vertices.Length / 4)
|
||||
{
|
||||
gradient[i / 3] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.X = vertices[i] - center.X;
|
||||
v.Y = vertices[i + 1] - center.Y;
|
||||
v.Z = vertices[i + 2] - center.Z;
|
||||
v = RcVec3f.Normalize(v);
|
||||
gradient[i / 3] = LMath.Clamp((LFloat)0.57735026f * (v.X + v.Y + v.Z), -(LFloat)1, (LFloat)1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RcVec3f GetSideVector(RcVec3f axis)
|
||||
{
|
||||
RcVec3f side = new RcVec3f(1, 0, 0);
|
||||
if (axis.X > 0.8)
|
||||
{
|
||||
side = new RcVec3f(0, 0, 1);
|
||||
}
|
||||
|
||||
var forward = RcVec3f.Cross(side, axis);
|
||||
side = RcVec3f.Cross(axis, forward);
|
||||
side = RcVec3f.Normalize(side);
|
||||
return side;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 854d10a9699747fbb5bd897550f85196
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,17 @@
|
||||
using DotRecast.Detour.Dynamic.Colliders;
|
||||
using DotRecast.Recast.Toolset.Gizmos;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public class RcGizmo
|
||||
{
|
||||
public readonly IRcGizmoMeshFilter Gizmo;
|
||||
public readonly IDtCollider Collider;
|
||||
|
||||
public RcGizmo(IDtCollider collider, IRcGizmoMeshFilter gizmo)
|
||||
{
|
||||
Collider = collider;
|
||||
Gizmo = gizmo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: beb914a0811c43c38ae4506df092c0ef
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,38 @@
|
||||
using DotRecast.Core.Numerics;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public static class RcGizmoFactory
|
||||
{
|
||||
public static RcBoxGizmo Box(RcVec3f center, RcVec3f[] halfEdges)
|
||||
{
|
||||
return new RcBoxGizmo(center, halfEdges);
|
||||
}
|
||||
|
||||
public static RcSphereGizmo Sphere(RcVec3f center, LFloat radius)
|
||||
{
|
||||
return new RcSphereGizmo(center, radius);
|
||||
}
|
||||
|
||||
public static RcCapsuleGizmo Capsule(RcVec3f start, RcVec3f end, LFloat radius)
|
||||
{
|
||||
return new RcCapsuleGizmo(start, end, radius);
|
||||
}
|
||||
|
||||
public static RcCylinderGizmo Cylinder(RcVec3f start, RcVec3f end, LFloat radius)
|
||||
{
|
||||
return new RcCylinderGizmo(start, end, radius);
|
||||
}
|
||||
|
||||
public static RcTrimeshGizmo Trimesh(LFloat[] verts, int[] faces)
|
||||
{
|
||||
return new RcTrimeshGizmo(verts, faces);
|
||||
}
|
||||
|
||||
public static RcCompositeGizmo Composite(params IRcGizmoMeshFilter[] gizmos)
|
||||
{
|
||||
return new RcCompositeGizmo(gizmos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 235008794faa49de9c3e8bd9cbe4212a
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public static class RcGizmoHelper
|
||||
{
|
||||
private static readonly int SEGMENTS = 16;
|
||||
private static readonly int RINGS = 8;
|
||||
|
||||
private static LFloat[] sphericalVertices;
|
||||
|
||||
public static LFloat[] GenerateSphericalVertices()
|
||||
{
|
||||
if (sphericalVertices == null)
|
||||
{
|
||||
sphericalVertices = GenerateSphericalVertices(SEGMENTS, RINGS);
|
||||
}
|
||||
|
||||
return sphericalVertices;
|
||||
}
|
||||
|
||||
private static LFloat[] GenerateSphericalVertices(int segments, int rings)
|
||||
{
|
||||
LFloat[] vertices = new LFloat[6 + 3 * (segments + 1) * (rings + 1)];
|
||||
// top
|
||||
int vi = 0;
|
||||
vertices[vi++] = 0;
|
||||
vertices[vi++] = 1;
|
||||
vertices[vi++] = 0;
|
||||
for (int r = 0; r <= rings; r++)
|
||||
{
|
||||
LFloat theta = LMath.PI * (r + 1) / (rings + 2);
|
||||
vi = GenerateRingVertices(segments, vertices, vi, theta);
|
||||
}
|
||||
|
||||
// bottom
|
||||
vertices[vi++] = 0;
|
||||
vertices[vi++] = -1;
|
||||
vertices[vi++] = 0;
|
||||
return vertices;
|
||||
}
|
||||
|
||||
public static LFloat[] GenerateCylindricalVertices()
|
||||
{
|
||||
return GenerateCylindricalVertices(SEGMENTS);
|
||||
}
|
||||
|
||||
private static LFloat[] GenerateCylindricalVertices(int segments)
|
||||
{
|
||||
LFloat[] vertices = new LFloat[3 * (segments + 1) * 4];
|
||||
int vi = 0;
|
||||
for (int r = 0; r < 4; r++)
|
||||
{
|
||||
vi = GenerateRingVertices(segments, vertices, vi, LMath.PI * (LFloat)0.5f);
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
private static int GenerateRingVertices(int segments, LFloat[] vertices, int vi, LFloat theta)
|
||||
{
|
||||
LFloat cosTheta = LMath.Cos(theta);
|
||||
LFloat sinTheta = LMath.Sin(theta);
|
||||
for (int p = 0; p <= segments; p++)
|
||||
{
|
||||
LFloat phi = 2 * LMath.PI * p / segments;
|
||||
LFloat cosPhi = LMath.Cos(phi);
|
||||
LFloat sinPhi = LMath.Sin(phi);
|
||||
vertices[vi++] = (LFloat)(sinTheta * cosPhi);
|
||||
vertices[vi++] = (LFloat)cosTheta;
|
||||
vertices[vi++] = (LFloat)(sinTheta * sinPhi);
|
||||
}
|
||||
|
||||
return vi;
|
||||
}
|
||||
|
||||
public static int[] GenerateSphericalTriangles()
|
||||
{
|
||||
return GenerateSphericalTriangles(SEGMENTS, RINGS);
|
||||
}
|
||||
|
||||
private static int[] GenerateSphericalTriangles(int segments, int rings)
|
||||
{
|
||||
int[] triangles = new int[6 * (segments + rings * (segments + 1))];
|
||||
int ti = GenerateSphereUpperCapTriangles(segments, triangles, 0);
|
||||
ti = GenerateRingTriangles(segments, rings, triangles, 1, ti);
|
||||
GenerateSphereLowerCapTriangles(segments, rings, triangles, ti);
|
||||
return triangles;
|
||||
}
|
||||
|
||||
public static int GenerateRingTriangles(int segments, int rings, int[] triangles, int vertexOffset, int ti)
|
||||
{
|
||||
for (int r = 0; r < rings; r++)
|
||||
{
|
||||
for (int p = 0; p < segments; p++)
|
||||
{
|
||||
int current = p + r * (segments + 1) + vertexOffset;
|
||||
int next = p + 1 + r * (segments + 1) + vertexOffset;
|
||||
int currentBottom = p + (r + 1) * (segments + 1) + vertexOffset;
|
||||
int nextBottom = p + 1 + (r + 1) * (segments + 1) + vertexOffset;
|
||||
triangles[ti++] = current;
|
||||
triangles[ti++] = next;
|
||||
triangles[ti++] = nextBottom;
|
||||
triangles[ti++] = current;
|
||||
triangles[ti++] = nextBottom;
|
||||
triangles[ti++] = currentBottom;
|
||||
}
|
||||
}
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
private static int GenerateSphereUpperCapTriangles(int segments, int[] triangles, int ti)
|
||||
{
|
||||
for (int p = 0; p < segments; p++)
|
||||
{
|
||||
triangles[ti++] = p + 2;
|
||||
triangles[ti++] = p + 1;
|
||||
triangles[ti++] = 0;
|
||||
}
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
private static void GenerateSphereLowerCapTriangles(int segments, int rings, int[] triangles, int ti)
|
||||
{
|
||||
int lastVertex = 1 + (segments + 1) * (rings + 1);
|
||||
for (int p = 0; p < segments; p++)
|
||||
{
|
||||
triangles[ti++] = lastVertex;
|
||||
triangles[ti++] = lastVertex - (p + 2);
|
||||
triangles[ti++] = lastVertex - (p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] GenerateCylindricalTriangles()
|
||||
{
|
||||
return GenerateCylindricalTriangles(SEGMENTS);
|
||||
}
|
||||
|
||||
private static int[] GenerateCylindricalTriangles(int segments)
|
||||
{
|
||||
int circleTriangles = segments - 2;
|
||||
int[] triangles = new int[6 * (circleTriangles + (segments + 1))];
|
||||
int vi = 0;
|
||||
int ti = GenerateCircleTriangles(segments, triangles, vi, 0, false);
|
||||
ti = GenerateRingTriangles(segments, 1, triangles, segments + 1, ti);
|
||||
int vertexCount = (segments + 1) * 4;
|
||||
ti = GenerateCircleTriangles(segments, triangles, vertexCount - segments, ti, true);
|
||||
return triangles;
|
||||
}
|
||||
|
||||
private static int GenerateCircleTriangles(int segments, int[] triangles, int vi, int ti, bool invert)
|
||||
{
|
||||
for (int p = 0; p < segments - 2; p++)
|
||||
{
|
||||
if (invert)
|
||||
{
|
||||
triangles[ti++] = vi;
|
||||
triangles[ti++] = vi + p + 1;
|
||||
triangles[ti++] = vi + p + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
triangles[ti++] = vi + p + 2;
|
||||
triangles[ti++] = vi + p + 1;
|
||||
triangles[ti++] = vi;
|
||||
}
|
||||
}
|
||||
|
||||
return ti;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cfc2fd59a92e453c95d0443df71f874b
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,23 @@
|
||||
using DotRecast.Core.Numerics;
|
||||
using JNGame.Math;
|
||||
using static DotRecast.Recast.Toolset.Gizmos.RcGizmoHelper;
|
||||
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public class RcSphereGizmo : IRcGizmoMeshFilter
|
||||
{
|
||||
public readonly LFloat[] vertices;
|
||||
public readonly int[] triangles;
|
||||
public readonly LFloat radius;
|
||||
public readonly RcVec3f center;
|
||||
|
||||
public RcSphereGizmo(RcVec3f center, LFloat radius)
|
||||
{
|
||||
this.center = center;
|
||||
this.radius = radius;
|
||||
vertices = GenerateSphericalVertices();
|
||||
triangles = GenerateSphericalTriangles();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac5867af32fd4b7d9d5679f17dd6aa60
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,16 @@
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Gizmos
|
||||
{
|
||||
public class RcTrimeshGizmo : IRcGizmoMeshFilter
|
||||
{
|
||||
public readonly LFloat[] vertices;
|
||||
public readonly int[] triangles;
|
||||
|
||||
public RcTrimeshGizmo(LFloat[] vertices, int[] triangles)
|
||||
{
|
||||
this.vertices = vertices;
|
||||
this.triangles = triangles;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92b619575a6e4b4eb0df8058246e23e7
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace DotRecast.Recast.Toolset
|
||||
{
|
||||
public interface IRcToolable
|
||||
{
|
||||
string GetName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f160410c11d0475ca05a21fd4e159c41
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,40 @@
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset
|
||||
{
|
||||
public class RcNavMeshBuildSettings
|
||||
{
|
||||
public LFloat cellSize = (LFloat)0.3f;
|
||||
public LFloat cellHeight = (LFloat)0.2f;
|
||||
|
||||
public LFloat agentHeight = (LFloat)2.0f;
|
||||
public LFloat agentRadius = (LFloat)0.6f;
|
||||
public LFloat agentMaxClimb = (LFloat)0.9f;
|
||||
public LFloat agentMaxSlope = (LFloat)45f;
|
||||
|
||||
public LFloat agentMaxAcceleration = (LFloat)8.0f;
|
||||
public LFloat agentMaxSpeed = (LFloat)3.5f;
|
||||
|
||||
public int minRegionSize = 8;
|
||||
public int mergedRegionSize = 20;
|
||||
|
||||
public int partitioning = RcPartitionType.WATERSHED.Value;
|
||||
|
||||
public bool filterLowHangingObstacles = true;
|
||||
public bool filterLedgeSpans = true;
|
||||
public bool filterWalkableLowHeightSpans = true;
|
||||
|
||||
public LFloat edgeMaxLen = (LFloat)12f;
|
||||
public LFloat edgeMaxError = (LFloat)1.3f;
|
||||
public int vertsPerPoly = 6;
|
||||
|
||||
public LFloat detailSampleDist = (LFloat)6f;
|
||||
public LFloat detailSampleMaxError = (LFloat)1f;
|
||||
|
||||
public bool tiled = false;
|
||||
public int tileSize = 32;
|
||||
|
||||
public bool keepInterResults = false;
|
||||
public bool buildAll = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecae0b7be48b41ad878f46474e87975e
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 722ad149982446f69458071c3e88a63f
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DotRecast.Core;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Recast.Geom;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcConvexVolumeTool : IRcToolable
|
||||
{
|
||||
private readonly List<RcVec3f> _pts;
|
||||
private readonly List<int> _hull;
|
||||
|
||||
public RcConvexVolumeTool()
|
||||
{
|
||||
_pts = new List<RcVec3f>();
|
||||
_hull = new List<int>();
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return "Convex Volumes";
|
||||
}
|
||||
|
||||
public List<RcVec3f> GetShapePoint()
|
||||
{
|
||||
return _pts;
|
||||
}
|
||||
|
||||
public List<int> GetShapeHull()
|
||||
{
|
||||
return _hull;
|
||||
}
|
||||
|
||||
public void ClearShape()
|
||||
{
|
||||
_pts.Clear();
|
||||
_hull.Clear();
|
||||
}
|
||||
|
||||
public bool PlottingShape(RcVec3f p, out List<RcVec3f> pts, out List<int> hull)
|
||||
{
|
||||
pts = null;
|
||||
hull = null;
|
||||
|
||||
// Create
|
||||
// If clicked on that last pt, create the shape.
|
||||
if (_pts.Count > 0 && RcVec3f.DistanceSquared(p, _pts[_pts.Count - 1]) < 0.2f * 0.2f)
|
||||
{
|
||||
pts = new List<RcVec3f>(_pts);
|
||||
hull = new List<int>(_hull);
|
||||
|
||||
_pts.Clear();
|
||||
_hull.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add new point
|
||||
_pts.Add(p);
|
||||
|
||||
// Update hull.
|
||||
if (_pts.Count > 3)
|
||||
{
|
||||
_hull.Clear();
|
||||
_hull.AddRange(RcConvexUtils.Convexhull(_pts));
|
||||
}
|
||||
else
|
||||
{
|
||||
_hull.Clear();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public bool TryRemove(IInputGeomProvider geom, RcVec3f pos, out RcConvexVolume volume)
|
||||
{
|
||||
// Delete
|
||||
int nearestIndex = -1;
|
||||
IList<RcConvexVolume> vols = geom.ConvexVolumes();
|
||||
for (int i = 0; i < vols.Count; ++i)
|
||||
{
|
||||
if (RcAreas.PointInPoly(vols[i].verts, pos) && pos.Y >= vols[i].hmin
|
||||
&& pos.Y <= vols[i].hmax)
|
||||
{
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// If end point close enough, delete it.
|
||||
if (nearestIndex == -1)
|
||||
{
|
||||
volume = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var removal = geom.ConvexVolumes()[nearestIndex];
|
||||
geom.ConvexVolumes().RemoveAt(nearestIndex);
|
||||
volume = removal;
|
||||
return null != volume;
|
||||
}
|
||||
|
||||
public bool TryAdd(IInputGeomProvider geom, RcVec3f p, RcAreaModification areaType, LFloat boxDescent, LFloat boxHeight, LFloat polyOffset, out RcConvexVolume volume)
|
||||
{
|
||||
// Create
|
||||
|
||||
// If clicked on that last pt, create the shape.
|
||||
if (_pts.Count > 0 && RcVec3f.DistanceSquared(p, _pts[^1]) < 0.2f * 0.2f)
|
||||
{
|
||||
//
|
||||
if (_hull.Count > 2)
|
||||
{
|
||||
volume = CreateConvexVolume(_pts, _hull, areaType, boxDescent, boxHeight, polyOffset);
|
||||
geom.AddConvexVolume(volume);
|
||||
}
|
||||
|
||||
_pts.Clear();
|
||||
_hull.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new point
|
||||
_pts.Add(p);
|
||||
|
||||
// Update hull.
|
||||
if (_pts.Count > 1)
|
||||
{
|
||||
_hull.Clear();
|
||||
_hull.AddRange(RcConvexUtils.Convexhull(_pts));
|
||||
}
|
||||
else
|
||||
{
|
||||
_hull.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
volume = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static RcConvexVolume CreateConvexVolume(List<RcVec3f> pts, List<int> hull, RcAreaModification areaType, LFloat boxDescent, LFloat boxHeight, LFloat polyOffset)
|
||||
{
|
||||
// Create shape.
|
||||
LFloat[] verts = new LFloat[hull.Count * 3];
|
||||
for (int i = 0; i < hull.Count; ++i)
|
||||
{
|
||||
verts[i * 3] = pts[hull[i]].X;
|
||||
verts[i * 3 + 1] = pts[hull[i]].Y;
|
||||
verts[i * 3 + 2] = pts[hull[i]].Z;
|
||||
}
|
||||
|
||||
LFloat minh = LFloat.MaxValue, maxh = 0;
|
||||
for (int i = 0; i < hull.Count; ++i)
|
||||
{
|
||||
minh = LMath.Min(minh, verts[i * 3 + 1]);
|
||||
}
|
||||
|
||||
minh -= boxDescent;
|
||||
maxh = minh + boxHeight;
|
||||
|
||||
if (polyOffset > 0.01f)
|
||||
{
|
||||
LFloat[] offset = new LFloat[verts.Length * 2];
|
||||
int noffset = RcAreas.OffsetPoly(verts, hull.Count, polyOffset, offset, offset.Length);
|
||||
if (noffset > 0)
|
||||
{
|
||||
verts = RcArrays.CopyOf(offset, 0, noffset * 3);
|
||||
}
|
||||
}
|
||||
|
||||
return new RcConvexVolume()
|
||||
{
|
||||
verts = verts,
|
||||
hmin = minh,
|
||||
hmax = maxh,
|
||||
areaMod = areaType,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38041515b069400da717e8e11dfc6d0d
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,16 @@
|
||||
using DotRecast.Core.Numerics;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcCrowdAgentData
|
||||
{
|
||||
public readonly RcCrowdAgentType type;
|
||||
public readonly RcVec3f home = new RcVec3f();
|
||||
|
||||
public RcCrowdAgentData(RcCrowdAgentType type, RcVec3f home)
|
||||
{
|
||||
this.type = type;
|
||||
this.home = home;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e29114d81db34169a648361713f4b03b
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,403 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using DotRecast.Core;
|
||||
using DotRecast.Core.Buffers;
|
||||
using DotRecast.Core.Collections;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Detour;
|
||||
using DotRecast.Detour.Crowd;
|
||||
using DotRecast.Recast.Toolset.Builder;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcCrowdAgentProfilingTool : IRcToolable
|
||||
{
|
||||
private RcCrowdAgentProfilingToolConfig _cfg;
|
||||
|
||||
private DtCrowdConfig _crowdCfg;
|
||||
private DtCrowd _crowd;
|
||||
private readonly DtCrowdAgentConfig _agCfg;
|
||||
|
||||
private DtNavMesh _navMesh;
|
||||
|
||||
private IRcRand _rand;
|
||||
private readonly List<DtPolyPoint> _polyPoints;
|
||||
|
||||
private const int SamplingCount = 500;
|
||||
private long _samplingUpdateTime;
|
||||
private readonly RcCyclicBuffer<long> _updateTimes;
|
||||
private long _curUpdateTime;
|
||||
private long _avgUpdateTime;
|
||||
private long _minUpdateTime;
|
||||
private long _maxUpdateTime;
|
||||
|
||||
public RcCrowdAgentProfilingTool()
|
||||
{
|
||||
_cfg = new RcCrowdAgentProfilingToolConfig();
|
||||
_agCfg = new DtCrowdAgentConfig();
|
||||
_polyPoints = new List<DtPolyPoint>();
|
||||
_updateTimes = new RcCyclicBuffer<long>(SamplingCount);
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return "Crowd Agent Profiling";
|
||||
}
|
||||
|
||||
public RcCrowdAgentProfilingToolConfig GetToolConfig()
|
||||
{
|
||||
return _cfg;
|
||||
}
|
||||
|
||||
public DtCrowdAgentConfig GetCrowdConfig()
|
||||
{
|
||||
return _agCfg;
|
||||
}
|
||||
|
||||
public DtCrowd GetCrowd()
|
||||
{
|
||||
return _crowd;
|
||||
}
|
||||
|
||||
public void Setup(LFloat maxAgentRadius, DtNavMesh nav)
|
||||
{
|
||||
_navMesh = nav;
|
||||
if (nav != null)
|
||||
{
|
||||
_crowdCfg = new DtCrowdConfig(maxAgentRadius);
|
||||
}
|
||||
}
|
||||
|
||||
private DtCrowdAgentParams GetAgentParams(LFloat agentRadius, LFloat agentHeight, LFloat agentMaxAcceleration, LFloat agentMaxSpeed)
|
||||
{
|
||||
DtCrowdAgentParams ap = new DtCrowdAgentParams();
|
||||
ap.radius = agentRadius;
|
||||
ap.height = agentHeight;
|
||||
ap.maxAcceleration = agentMaxAcceleration;
|
||||
ap.maxSpeed = agentMaxSpeed;
|
||||
ap.collisionQueryRange = ap.radius * (LFloat)12.0f;
|
||||
ap.pathOptimizationRange = ap.radius * (LFloat)30.0f;
|
||||
ap.updateFlags = _agCfg.GetUpdateFlags();
|
||||
ap.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
||||
ap.separationWeight = _agCfg.separationWeight;
|
||||
return ap;
|
||||
}
|
||||
|
||||
private DtStatus GetMobPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt)
|
||||
{
|
||||
return navquery.FindRandomPoint(filter, _rand, out var randomRef, out randomPt);
|
||||
}
|
||||
|
||||
private DtStatus GetVillagerPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt)
|
||||
{
|
||||
randomPt = RcVec3f.Zero;
|
||||
|
||||
if (0 >= _polyPoints.Count)
|
||||
return DtStatus.DT_FAILURE;
|
||||
|
||||
int zone = (int)(_rand.Next() * _polyPoints.Count);
|
||||
return navquery.FindRandomPointWithinCircle(_polyPoints[zone].refs, _polyPoints[zone].pt, _cfg.zoneRadius, filter, _rand,
|
||||
out var randomRef, out randomPt);
|
||||
}
|
||||
|
||||
private void CreateZones()
|
||||
{
|
||||
_polyPoints.Clear();
|
||||
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
||||
DtNavMeshQuery navquery = new DtNavMeshQuery(_navMesh);
|
||||
for (int i = 0; i < _cfg.numberOfZones; i++)
|
||||
{
|
||||
LFloat zoneSeparation = _cfg.zoneRadius * _cfg.zoneRadius * 16;
|
||||
for (int k = 0; k < 100; k++)
|
||||
{
|
||||
var status = navquery.FindRandomPoint(filter, _rand, out var randomRef, out var randomPt);
|
||||
if (status.Succeeded())
|
||||
{
|
||||
bool valid = true;
|
||||
foreach (var zone in _polyPoints)
|
||||
{
|
||||
if (RcVec3f.DistanceSquared(zone.pt, randomPt) < zoneSeparation)
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
_polyPoints.Add(new DtPolyPoint(randomRef, randomPt));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCrowd()
|
||||
{
|
||||
_crowd = new DtCrowd(_crowdCfg, _navMesh, __ => new DtQueryDefaultFilter(
|
||||
SampleAreaModifications.SAMPLE_POLYFLAGS_ALL,
|
||||
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED,
|
||||
new LFloat[] { (LFloat)1f, (LFloat)10f, (LFloat)1f, (LFloat)1f, (LFloat)2f, (LFloat)1.5f })
|
||||
);
|
||||
|
||||
DtObstacleAvoidanceParams option = new DtObstacleAvoidanceParams(_crowd.GetObstacleAvoidanceParams(0));
|
||||
// Low (11)
|
||||
option.velBias = (LFloat)0.5f;
|
||||
option.adaptiveDivs = 5;
|
||||
option.adaptiveRings = 2;
|
||||
option.adaptiveDepth = 1;
|
||||
_crowd.SetObstacleAvoidanceParams(0, option);
|
||||
// Medium (22)
|
||||
option.velBias = (LFloat)0.5f;
|
||||
option.adaptiveDivs = 5;
|
||||
option.adaptiveRings = 2;
|
||||
option.adaptiveDepth = 2;
|
||||
_crowd.SetObstacleAvoidanceParams(1, option);
|
||||
// Good (45)
|
||||
option.velBias = (LFloat)0.5f;
|
||||
option.adaptiveDivs = 7;
|
||||
option.adaptiveRings = 2;
|
||||
option.adaptiveDepth = 3;
|
||||
_crowd.SetObstacleAvoidanceParams(2, option);
|
||||
// High (66)
|
||||
option.velBias = (LFloat)0.5f;
|
||||
option.adaptiveDivs = 7;
|
||||
option.adaptiveRings = 3;
|
||||
option.adaptiveDepth = 3;
|
||||
_crowd.SetObstacleAvoidanceParams(3, option);
|
||||
}
|
||||
|
||||
public void StartProfiling(LFloat agentRadius, LFloat agentHeight, LFloat agentMaxAcceleration, LFloat agentMaxSpeed)
|
||||
{
|
||||
if (null == _navMesh)
|
||||
return;
|
||||
|
||||
// for benchmark
|
||||
_updateTimes.Clear();
|
||||
_samplingUpdateTime = 0;
|
||||
_curUpdateTime = 0;
|
||||
_avgUpdateTime = 0;
|
||||
_minUpdateTime = 0;
|
||||
_maxUpdateTime = 0;
|
||||
|
||||
_rand = new RcRand(_cfg.randomSeed);
|
||||
CreateCrowd();
|
||||
CreateZones();
|
||||
DtNavMeshQuery navquery = new DtNavMeshQuery(_navMesh);
|
||||
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
||||
for (int i = 0; i < _cfg.agents; i++)
|
||||
{
|
||||
LFloat tr = _rand.Next();
|
||||
RcCrowdAgentType type = RcCrowdAgentType.MOB;
|
||||
LFloat mobsPcnt = _cfg.percentMobs / (LFloat)100f;
|
||||
if (tr > mobsPcnt)
|
||||
{
|
||||
tr = _rand.Next();
|
||||
LFloat travellerPcnt = _cfg.percentTravellers / (LFloat)100f;
|
||||
if (tr > travellerPcnt)
|
||||
{
|
||||
type = RcCrowdAgentType.VILLAGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = RcCrowdAgentType.TRAVELLER;
|
||||
}
|
||||
}
|
||||
|
||||
var status = DtStatus.DT_FAILURE;
|
||||
var randomPt = RcVec3f.Zero;
|
||||
switch (type)
|
||||
{
|
||||
case RcCrowdAgentType.MOB:
|
||||
status = GetMobPosition(navquery, filter, out randomPt);
|
||||
break;
|
||||
case RcCrowdAgentType.VILLAGER:
|
||||
status = GetVillagerPosition(navquery, filter, out randomPt);
|
||||
break;
|
||||
case RcCrowdAgentType.TRAVELLER:
|
||||
status = GetVillagerPosition(navquery, filter, out randomPt);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status.Succeeded())
|
||||
{
|
||||
AddAgent(randomPt, type, agentRadius, agentHeight, agentMaxAcceleration, agentMaxSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(LFloat dt)
|
||||
{
|
||||
long startTime = RcFrequency.Ticks;
|
||||
if (_crowd != null)
|
||||
{
|
||||
_crowd.Config().pathQueueSize = _cfg.pathQueueSize;
|
||||
_crowd.Config().maxFindPathIterations = _cfg.maxIterations;
|
||||
_crowd.Update(dt, null);
|
||||
}
|
||||
|
||||
long endTime = RcFrequency.Ticks;
|
||||
if (_crowd != null)
|
||||
{
|
||||
DtNavMeshQuery navquery = new DtNavMeshQuery(_navMesh);
|
||||
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
||||
foreach (DtCrowdAgent ag in _crowd.GetActiveAgents())
|
||||
{
|
||||
if (NeedsNewTarget(ag))
|
||||
{
|
||||
RcCrowdAgentData crowAgentData = (RcCrowdAgentData)ag.option.userData;
|
||||
switch (crowAgentData.type)
|
||||
{
|
||||
case RcCrowdAgentType.MOB:
|
||||
MoveMob(navquery, filter, ag, crowAgentData);
|
||||
break;
|
||||
case RcCrowdAgentType.VILLAGER:
|
||||
MoveVillager(navquery, filter, ag, crowAgentData);
|
||||
break;
|
||||
case RcCrowdAgentType.TRAVELLER:
|
||||
MoveTraveller(navquery, filter, ag, crowAgentData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentTime = endTime - startTime;
|
||||
_updateTimes.PushBack(currentTime);
|
||||
|
||||
// for benchmark
|
||||
_samplingUpdateTime = _updateTimes.Sum() / TimeSpan.TicksPerMillisecond;
|
||||
_curUpdateTime = currentTime / TimeSpan.TicksPerMillisecond;
|
||||
_avgUpdateTime = (long)(_updateTimes.Average() / TimeSpan.TicksPerMillisecond);
|
||||
_minUpdateTime = _updateTimes.Min() / TimeSpan.TicksPerMillisecond;
|
||||
_maxUpdateTime = _updateTimes.Max() / TimeSpan.TicksPerMillisecond;
|
||||
}
|
||||
|
||||
private void MoveMob(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData)
|
||||
{
|
||||
// Move somewhere
|
||||
var status = navquery.FindNearestPoly(ag.npos, _crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _);
|
||||
if (status.Succeeded())
|
||||
{
|
||||
status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, _cfg.zoneRadius * (LFloat)2f, filter, _rand,
|
||||
out var randomRef, out var randomPt);
|
||||
if (status.Succeeded())
|
||||
{
|
||||
_crowd.RequestMoveTarget(ag, randomRef, randomPt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveVillager(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData)
|
||||
{
|
||||
// Move somewhere close
|
||||
var status = navquery.FindNearestPoly(ag.npos, _crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _);
|
||||
if (status.Succeeded())
|
||||
{
|
||||
status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, _cfg.zoneRadius * (LFloat)0.2f, filter, _rand,
|
||||
out var randomRef, out var randomPt);
|
||||
if (status.Succeeded())
|
||||
{
|
||||
_crowd.RequestMoveTarget(ag, randomRef, randomPt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveTraveller(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData)
|
||||
{
|
||||
// Move to another zone
|
||||
List<DtPolyPoint> potentialTargets = new List<DtPolyPoint>();
|
||||
foreach (var zone in _polyPoints)
|
||||
{
|
||||
if (RcVec3f.DistanceSquared(zone.pt, ag.npos) > _cfg.zoneRadius * _cfg.zoneRadius)
|
||||
{
|
||||
potentialTargets.Add(zone);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < potentialTargets.Count)
|
||||
{
|
||||
// potentialTargets.Shuffle();
|
||||
_crowd.RequestMoveTarget(ag, potentialTargets[0].refs, potentialTargets[0].pt);
|
||||
}
|
||||
}
|
||||
|
||||
private bool NeedsNewTarget(DtCrowdAgent ag)
|
||||
{
|
||||
if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE
|
||||
|| ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_FAILED)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VALID)
|
||||
{
|
||||
LFloat dx = ag.targetPos.X - ag.npos.X;
|
||||
LFloat dy = ag.targetPos.Y - ag.npos.Y;
|
||||
LFloat dz = ag.targetPos.Z - ag.npos.Z;
|
||||
return dx * dx + dy * dy + dz * dz < 0.3f;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private DtCrowdAgent AddAgent(RcVec3f p, RcCrowdAgentType type, LFloat agentRadius, LFloat agentHeight, LFloat agentMaxAcceleration, LFloat agentMaxSpeed)
|
||||
{
|
||||
DtCrowdAgentParams ap = GetAgentParams(agentRadius, agentHeight, agentMaxAcceleration, agentMaxSpeed);
|
||||
ap.userData = new RcCrowdAgentData(type, p);
|
||||
return _crowd.AddAgent(p, ap);
|
||||
}
|
||||
|
||||
public void UpdateAgentParams()
|
||||
{
|
||||
if (_crowd != null)
|
||||
{
|
||||
foreach (DtCrowdAgent ag in _crowd.GetActiveAgents())
|
||||
{
|
||||
DtCrowdAgentParams option = new DtCrowdAgentParams();
|
||||
option.radius = ag.option.radius;
|
||||
option.height = ag.option.height;
|
||||
option.maxAcceleration = ag.option.maxAcceleration;
|
||||
option.maxSpeed = ag.option.maxSpeed;
|
||||
option.collisionQueryRange = ag.option.collisionQueryRange;
|
||||
option.pathOptimizationRange = ag.option.pathOptimizationRange;
|
||||
option.queryFilterType = ag.option.queryFilterType;
|
||||
option.userData = ag.option.userData;
|
||||
option.updateFlags = _agCfg.GetUpdateFlags();
|
||||
option.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
||||
option.separationWeight = _agCfg.separationWeight;
|
||||
_crowd.UpdateAgentParameters(ag, option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long GetCrowdUpdateSamplingTime()
|
||||
{
|
||||
return _samplingUpdateTime;
|
||||
}
|
||||
|
||||
public long GetCrowdUpdateTime()
|
||||
{
|
||||
return _curUpdateTime;
|
||||
}
|
||||
|
||||
public long GetCrowdUpdateAvgTime()
|
||||
{
|
||||
return _avgUpdateTime;
|
||||
}
|
||||
|
||||
public long GetCrowdUpdateMinTime()
|
||||
{
|
||||
return _minUpdateTime;
|
||||
}
|
||||
|
||||
public long GetCrowdUpdateMaxTime()
|
||||
{
|
||||
return _maxUpdateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12ffdc5a9b23438cb35434ed0012fb8a
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,18 @@
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcCrowdAgentProfilingToolConfig
|
||||
{
|
||||
public int expandSimOptions = 1;
|
||||
public int expandCrowdOptions = 1;
|
||||
public int agents = 1000;
|
||||
public int randomSeed = 270;
|
||||
public int numberOfZones = 4;
|
||||
public LFloat zoneRadius = (LFloat)20f;
|
||||
public LFloat percentMobs = (LFloat)80f;
|
||||
public LFloat percentTravellers = (LFloat)15f;
|
||||
public int pathQueueSize = 32;
|
||||
public int maxIterations = 300;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e96fe090b86f444cb250f7ab8bb69896
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,11 @@
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcCrowdAgentTrail
|
||||
{
|
||||
public const int AGENT_MAX_TRAIL = 64;
|
||||
public LFloat[] trail = new LFloat[AGENT_MAX_TRAIL * 3];
|
||||
public int htrail;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97d72af6d37d4440a149e5f73524c6c5
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public enum RcCrowdAgentType
|
||||
{
|
||||
VILLAGER,
|
||||
TRAVELLER,
|
||||
MOB,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba21b8baf51e4986bab9361c63189ffa
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,314 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DotRecast.Core;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Detour;
|
||||
using DotRecast.Detour.Crowd;
|
||||
using DotRecast.Recast.Toolset.Builder;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcCrowdTool : IRcToolable
|
||||
{
|
||||
private readonly DtCrowdAgentConfig _agCfg;
|
||||
private DtCrowd crowd;
|
||||
private readonly DtCrowdAgentDebugInfo _agentDebug;
|
||||
private long crowdUpdateTime;
|
||||
private readonly Dictionary<long, RcCrowdAgentTrail> _trails;
|
||||
private long _moveTargetRef;
|
||||
private RcVec3f _moveTargetPos;
|
||||
|
||||
public RcCrowdTool()
|
||||
{
|
||||
_agCfg = new DtCrowdAgentConfig();
|
||||
_agentDebug = new DtCrowdAgentDebugInfo();
|
||||
_agentDebug.vod = new DtObstacleAvoidanceDebugData(2048);
|
||||
_trails = new Dictionary<long, RcCrowdAgentTrail>();
|
||||
}
|
||||
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return "Crowd Control";
|
||||
}
|
||||
|
||||
public DtCrowdAgentConfig GetCrowdConfig()
|
||||
{
|
||||
return _agCfg;
|
||||
}
|
||||
|
||||
public DtCrowdAgentDebugInfo GetCrowdAgentDebugInfo()
|
||||
{
|
||||
return _agentDebug;
|
||||
}
|
||||
|
||||
public Dictionary<long, RcCrowdAgentTrail> GetCrowdAgentTrails()
|
||||
{
|
||||
return _trails;
|
||||
}
|
||||
|
||||
public long GetMoveTargetRef()
|
||||
{
|
||||
return _moveTargetRef;
|
||||
}
|
||||
|
||||
public RcVec3f GetMoveTargetPos()
|
||||
{
|
||||
return _moveTargetPos;
|
||||
}
|
||||
|
||||
public void Setup(LFloat agentRadius, DtNavMesh navMesh)
|
||||
{
|
||||
DtCrowdConfig config = new DtCrowdConfig(agentRadius);
|
||||
crowd = new DtCrowd(config, navMesh, __ => new DtQueryDefaultFilter(
|
||||
SampleAreaModifications.SAMPLE_POLYFLAGS_ALL,
|
||||
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED,
|
||||
new LFloat[] { (LFloat)1f, (LFloat)10f, (LFloat)1f, (LFloat)1f, (LFloat)2f, (LFloat)1.5f })
|
||||
);
|
||||
|
||||
// Setup local avoidance option to different qualities.
|
||||
// Use mostly default settings, copy from dtCrowd.
|
||||
DtObstacleAvoidanceParams option = new DtObstacleAvoidanceParams(crowd.GetObstacleAvoidanceParams(0));
|
||||
|
||||
// Low (11)
|
||||
option.velBias = (LFloat)0.5f;
|
||||
option.adaptiveDivs = 5;
|
||||
option.adaptiveRings = 2;
|
||||
option.adaptiveDepth = 1;
|
||||
crowd.SetObstacleAvoidanceParams(0, option);
|
||||
|
||||
// Medium (22)
|
||||
option.velBias = (LFloat)0.5f;
|
||||
option.adaptiveDivs = 5;
|
||||
option.adaptiveRings = 2;
|
||||
option.adaptiveDepth = 2;
|
||||
crowd.SetObstacleAvoidanceParams(1, option);
|
||||
|
||||
// Good (45)
|
||||
option.velBias = (LFloat)0.5f;
|
||||
option.adaptiveDivs = 7;
|
||||
option.adaptiveRings = 2;
|
||||
option.adaptiveDepth = 3;
|
||||
crowd.SetObstacleAvoidanceParams(2, option);
|
||||
|
||||
// High (66)
|
||||
option.velBias = (LFloat)0.5f;
|
||||
option.adaptiveDivs = 7;
|
||||
option.adaptiveRings = 3;
|
||||
option.adaptiveDepth = 3;
|
||||
|
||||
crowd.SetObstacleAvoidanceParams(3, option);
|
||||
}
|
||||
|
||||
public void UpdateAgentParams()
|
||||
{
|
||||
if (crowd == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
||||
{
|
||||
DtCrowdAgentParams agOption = new DtCrowdAgentParams();
|
||||
agOption.radius = ag.option.radius;
|
||||
agOption.height = ag.option.height;
|
||||
agOption.maxAcceleration = ag.option.maxAcceleration;
|
||||
agOption.maxSpeed = ag.option.maxSpeed;
|
||||
agOption.collisionQueryRange = ag.option.collisionQueryRange;
|
||||
agOption.pathOptimizationRange = ag.option.pathOptimizationRange;
|
||||
agOption.obstacleAvoidanceType = ag.option.obstacleAvoidanceType;
|
||||
agOption.queryFilterType = ag.option.queryFilterType;
|
||||
agOption.userData = ag.option.userData;
|
||||
agOption.updateFlags = _agCfg.GetUpdateFlags();
|
||||
agOption.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
||||
agOption.separationWeight = _agCfg.separationWeight;
|
||||
crowd.UpdateAgentParameters(ag, agOption);
|
||||
}
|
||||
}
|
||||
|
||||
public DtCrowd GetCrowd()
|
||||
{
|
||||
return crowd;
|
||||
}
|
||||
|
||||
public void Update(LFloat dt)
|
||||
{
|
||||
if (crowd == null)
|
||||
return;
|
||||
|
||||
DtNavMesh nav = crowd.GetNavMesh();
|
||||
if (nav == null)
|
||||
return;
|
||||
|
||||
long startTime = RcFrequency.Ticks;
|
||||
crowd.Update(dt, _agentDebug);
|
||||
long endTime = RcFrequency.Ticks;
|
||||
|
||||
// Update agent trails
|
||||
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
||||
{
|
||||
RcCrowdAgentTrail trail = _trails[ag.idx];
|
||||
// Update agent movement trail.
|
||||
trail.htrail = (trail.htrail + 1) % RcCrowdAgentTrail.AGENT_MAX_TRAIL;
|
||||
trail.trail[trail.htrail * 3] = ag.npos.X;
|
||||
trail.trail[trail.htrail * 3 + 1] = ag.npos.Y;
|
||||
trail.trail[trail.htrail * 3 + 2] = ag.npos.Z;
|
||||
}
|
||||
|
||||
_agentDebug.vod.NormalizeSamples();
|
||||
|
||||
// m_crowdSampleCount.addSample((LFloat) crowd.GetVelocitySampleCount());
|
||||
crowdUpdateTime = (endTime - startTime) / TimeSpan.TicksPerMillisecond;
|
||||
}
|
||||
|
||||
public void RemoveAgent(DtCrowdAgent agent)
|
||||
{
|
||||
crowd.RemoveAgent(agent);
|
||||
if (agent == _agentDebug.agent)
|
||||
{
|
||||
_agentDebug.agent = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddAgent(RcVec3f p, LFloat agentRadius, LFloat agentHeight, LFloat agentMaxAcceleration, LFloat agentMaxSpeed)
|
||||
{
|
||||
DtCrowdAgentParams ap = CreateAgentParams(agentRadius, agentHeight, agentMaxAcceleration, agentMaxSpeed);
|
||||
DtCrowdAgent ag = crowd.AddAgent(p, ap);
|
||||
if (ag != null)
|
||||
{
|
||||
if (_moveTargetRef != 0)
|
||||
crowd.RequestMoveTarget(ag, _moveTargetRef, _moveTargetPos);
|
||||
|
||||
// Init trail
|
||||
if (!_trails.TryGetValue(ag.idx, out var trail))
|
||||
{
|
||||
trail = new RcCrowdAgentTrail();
|
||||
_trails.Add(ag.idx, trail);
|
||||
}
|
||||
|
||||
for (int i = 0; i < RcCrowdAgentTrail.AGENT_MAX_TRAIL; ++i)
|
||||
{
|
||||
trail.trail[i * 3] = p.X;
|
||||
trail.trail[i * 3 + 1] = p.Y;
|
||||
trail.trail[i * 3 + 2] = p.Z;
|
||||
}
|
||||
|
||||
trail.htrail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private DtCrowdAgentParams CreateAgentParams(LFloat agentRadius, LFloat agentHeight, LFloat agentMaxAcceleration, LFloat agentMaxSpeed)
|
||||
{
|
||||
DtCrowdAgentParams ap = new DtCrowdAgentParams();
|
||||
ap.radius = agentRadius;
|
||||
ap.height = agentHeight;
|
||||
ap.maxAcceleration = agentMaxAcceleration;
|
||||
ap.maxSpeed = agentMaxSpeed;
|
||||
ap.collisionQueryRange = ap.radius * (LFloat)12.0f;
|
||||
ap.pathOptimizationRange = ap.radius * (LFloat)30.0f;
|
||||
ap.updateFlags = _agCfg.GetUpdateFlags();
|
||||
ap.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
||||
ap.separationWeight = _agCfg.separationWeight;
|
||||
return ap;
|
||||
}
|
||||
|
||||
public DtCrowdAgent HitTestAgents(RcVec3f s, RcVec3f p)
|
||||
{
|
||||
DtCrowdAgent isel = null;
|
||||
LFloat tsel = LFloat.MaxValue;
|
||||
|
||||
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
||||
{
|
||||
RcVec3f bmin = new RcVec3f();
|
||||
RcVec3f bmax = new RcVec3f();
|
||||
GetAgentBounds(ag, ref bmin, ref bmax);
|
||||
if (RcIntersections.IsectSegAABB(s, p, bmin, bmax, out var tmin, out var tmax))
|
||||
{
|
||||
if (tmin > 0 && tmin < tsel)
|
||||
{
|
||||
isel = ag;
|
||||
tsel = tmin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isel;
|
||||
}
|
||||
|
||||
private void GetAgentBounds(DtCrowdAgent ag, ref RcVec3f bmin, ref RcVec3f bmax)
|
||||
{
|
||||
RcVec3f p = ag.npos;
|
||||
LFloat r = ag.option.radius;
|
||||
LFloat h = ag.option.height;
|
||||
bmin.X = p.X - r;
|
||||
bmin.Y = p.Y;
|
||||
bmin.Z = p.Z - r;
|
||||
bmax.X = p.X + r;
|
||||
bmax.Y = p.Y + h;
|
||||
bmax.Z = p.Z + r;
|
||||
}
|
||||
|
||||
public void SetMoveTarget(RcVec3f p, bool adjust)
|
||||
{
|
||||
if (crowd == null)
|
||||
return;
|
||||
|
||||
// Find nearest point on navmesh and set move request to that location.
|
||||
DtNavMeshQuery navquery = crowd.GetNavMeshQuery();
|
||||
IDtQueryFilter filter = crowd.GetFilter(0);
|
||||
RcVec3f halfExtents = crowd.GetQueryExtents();
|
||||
|
||||
if (adjust)
|
||||
{
|
||||
// Request velocity
|
||||
if (_agentDebug.agent != null)
|
||||
{
|
||||
RcVec3f vel = CalcVel(_agentDebug.agent.npos, p, _agentDebug.agent.option.maxSpeed);
|
||||
crowd.RequestMoveVelocity(_agentDebug.agent, vel);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
||||
{
|
||||
RcVec3f vel = CalcVel(ag.npos, p, ag.option.maxSpeed);
|
||||
crowd.RequestMoveVelocity(ag, vel);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
navquery.FindNearestPoly(p, halfExtents, filter, out _moveTargetRef, out _moveTargetPos, out var _);
|
||||
if (_agentDebug.agent != null)
|
||||
{
|
||||
crowd.RequestMoveTarget(_agentDebug.agent, _moveTargetRef, _moveTargetPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
||||
{
|
||||
crowd.RequestMoveTarget(ag, _moveTargetRef, _moveTargetPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RcVec3f CalcVel(RcVec3f pos, RcVec3f tgt, LFloat speed)
|
||||
{
|
||||
RcVec3f vel = RcVec3f.Subtract(tgt, pos);
|
||||
vel.Y = (LFloat)0.0f;
|
||||
vel = RcVec3f.Normalize(vel);
|
||||
return vel.Scale(speed);
|
||||
}
|
||||
|
||||
public long GetCrowdUpdateTime()
|
||||
{
|
||||
return crowdUpdateTime;
|
||||
}
|
||||
|
||||
public void HighlightAgent(DtCrowdAgent agent)
|
||||
{
|
||||
_agentDebug.agent = agent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 995c2e478cec460e81e3bbd7b380c6c7
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,28 @@
|
||||
using DotRecast.Core.Collections;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcCrowdToolMode
|
||||
{
|
||||
public static readonly RcCrowdToolMode CREATE = new RcCrowdToolMode(0, "Create Agents");
|
||||
public static readonly RcCrowdToolMode MOVE_TARGET = new RcCrowdToolMode(1, "Move Target");
|
||||
public static readonly RcCrowdToolMode SELECT = new RcCrowdToolMode(2, "Select Agent");
|
||||
public static readonly RcCrowdToolMode TOGGLE_POLYS = new RcCrowdToolMode(3, "Toggle Polys");
|
||||
|
||||
public static readonly RcImmutableArray<RcCrowdToolMode> Values = RcImmutableArray.Create(
|
||||
CREATE,
|
||||
MOVE_TARGET,
|
||||
SELECT,
|
||||
TOGGLE_POLYS
|
||||
);
|
||||
|
||||
public readonly int Idx;
|
||||
public readonly string Label;
|
||||
|
||||
private RcCrowdToolMode(int idx, string label)
|
||||
{
|
||||
Idx = idx;
|
||||
Label = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb6f1cde2fb84788bd4f4a11596436f5
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public enum RcDynamicColliderShape
|
||||
{
|
||||
SPHERE,
|
||||
CAPSULE,
|
||||
BOX,
|
||||
CYLINDER,
|
||||
COMPOSITE,
|
||||
CONVEX,
|
||||
TRIMESH_BRIDGE,
|
||||
TRIMESH_HOUSE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7a63250b009403180f96ed08d9cf3d2
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,365 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using DotRecast.Core;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Detour.Dynamic;
|
||||
using DotRecast.Detour.Dynamic.Colliders;
|
||||
using DotRecast.Detour.Dynamic.Io;
|
||||
using DotRecast.Recast.Toolset.Builder;
|
||||
using DotRecast.Recast.Toolset.Geom;
|
||||
using DotRecast.Recast.Toolset.Gizmos;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcDynamicUpdateTool : IRcToolable
|
||||
{
|
||||
private DtDynamicNavMesh dynaMesh;
|
||||
private readonly Dictionary<long, RcGizmo> colliderGizmos;
|
||||
|
||||
private readonly IRcRand random;
|
||||
private readonly DemoInputGeomProvider bridgeGeom;
|
||||
private readonly DemoInputGeomProvider houseGeom;
|
||||
private readonly DemoInputGeomProvider convexGeom;
|
||||
|
||||
public RcDynamicUpdateTool(IRcRand rand, DemoInputGeomProvider bridgeGeom, DemoInputGeomProvider houseGeom, DemoInputGeomProvider convexGeom)
|
||||
{
|
||||
this.colliderGizmos = new Dictionary<long, RcGizmo>();
|
||||
this.random = rand;
|
||||
this.bridgeGeom = bridgeGeom;
|
||||
this.houseGeom = houseGeom;
|
||||
this.convexGeom = convexGeom;
|
||||
}
|
||||
|
||||
public IEnumerable<RcGizmo> GetGizmos()
|
||||
{
|
||||
return colliderGizmos.Values;
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return "Dynamic Updates";
|
||||
}
|
||||
|
||||
public DtDynamicNavMesh GetDynamicNavMesh()
|
||||
{
|
||||
return dynaMesh;
|
||||
}
|
||||
|
||||
public void RemoveShape(RcVec3f start, RcVec3f dir)
|
||||
{
|
||||
foreach (var e in colliderGizmos)
|
||||
{
|
||||
if (Hit(start, dir, e.Value.Collider.Bounds()))
|
||||
{
|
||||
dynaMesh.RemoveCollider(e.Key);
|
||||
colliderGizmos.Remove(e.Key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool Hit(RcVec3f point, RcVec3f dir, LFloat[] bounds)
|
||||
{
|
||||
LFloat cx = (LFloat)0.5f * (bounds[0] + bounds[3]);
|
||||
LFloat cy = (LFloat)0.5f * (bounds[1] + bounds[4]);
|
||||
LFloat cz = (LFloat)0.5f * (bounds[2] + bounds[5]);
|
||||
LFloat dx = (LFloat)0.5f * (bounds[3] - bounds[0]);
|
||||
LFloat dy = (LFloat)0.5f * (bounds[4] - bounds[1]);
|
||||
LFloat dz = (LFloat)0.5f * (bounds[5] - bounds[2]);
|
||||
LFloat rSqr = dx * dx + dy * dy + dz * dz;
|
||||
LFloat mx = point.X - cx;
|
||||
LFloat my = point.Y - cy;
|
||||
LFloat mz = point.Z - cz;
|
||||
LFloat c = mx * mx + my * my + mz * mz - rSqr;
|
||||
if (c <= 0.0f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
LFloat b = mx * dir.X + my * dir.Y + mz * dir.Z;
|
||||
if (b > 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LFloat disc = b * b - c;
|
||||
return disc >= 0.0f;
|
||||
}
|
||||
|
||||
|
||||
public RcGizmo AddShape(RcDynamicColliderShape colliderShape, RcVec3f p)
|
||||
{
|
||||
if (dynaMesh == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
RcGizmo colliderWithGizmo = null;
|
||||
{
|
||||
if (colliderShape == RcDynamicColliderShape.SPHERE)
|
||||
{
|
||||
colliderWithGizmo = SphereCollider(p, dynaMesh.config.walkableClimb);
|
||||
}
|
||||
else if (colliderShape == RcDynamicColliderShape.CAPSULE)
|
||||
{
|
||||
colliderWithGizmo = CapsuleCollider(p, dynaMesh.config.walkableClimb);
|
||||
}
|
||||
else if (colliderShape == RcDynamicColliderShape.BOX)
|
||||
{
|
||||
colliderWithGizmo = BoxCollider(p, dynaMesh.config.walkableClimb);
|
||||
}
|
||||
else if (colliderShape == RcDynamicColliderShape.CYLINDER)
|
||||
{
|
||||
colliderWithGizmo = CylinderCollider(p, dynaMesh.config.walkableClimb);
|
||||
}
|
||||
else if (colliderShape == RcDynamicColliderShape.COMPOSITE)
|
||||
{
|
||||
colliderWithGizmo = CompositeCollider(p, dynaMesh.config.walkableClimb);
|
||||
}
|
||||
else if (colliderShape == RcDynamicColliderShape.TRIMESH_BRIDGE)
|
||||
{
|
||||
colliderWithGizmo = TrimeshBridge(p, dynaMesh.config.walkableClimb);
|
||||
}
|
||||
else if (colliderShape == RcDynamicColliderShape.TRIMESH_HOUSE)
|
||||
{
|
||||
colliderWithGizmo = TrimeshHouse(p, dynaMesh.config.walkableClimb);
|
||||
}
|
||||
else if (colliderShape == RcDynamicColliderShape.CONVEX)
|
||||
{
|
||||
colliderWithGizmo = ConvexTrimesh(p, dynaMesh.config.walkableClimb);
|
||||
}
|
||||
}
|
||||
|
||||
if (colliderWithGizmo != null)
|
||||
{
|
||||
long id = dynaMesh.AddCollider(colliderWithGizmo.Collider);
|
||||
colliderGizmos.Add(id, colliderWithGizmo);
|
||||
}
|
||||
|
||||
return colliderWithGizmo;
|
||||
}
|
||||
|
||||
public DtDynamicNavMesh Load(string filename, IRcCompressor compressor)
|
||||
{
|
||||
using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
using var br = new BinaryReader(fs);
|
||||
DtVoxelFileReader reader = new DtVoxelFileReader(compressor);
|
||||
DtVoxelFile voxelFile = reader.Read(br);
|
||||
|
||||
dynaMesh = new DtDynamicNavMesh(voxelFile);
|
||||
dynaMesh.config.keepIntermediateResults = true;
|
||||
|
||||
colliderGizmos.Clear();
|
||||
|
||||
return dynaMesh;
|
||||
}
|
||||
|
||||
public void Save(string filename, bool compression, IRcCompressor compressor)
|
||||
{
|
||||
DtVoxelFile voxelFile = DtVoxelFile.From(dynaMesh);
|
||||
using var fs = new FileStream(filename, FileMode.CreateNew, FileAccess.Write);
|
||||
using var bw = new BinaryWriter(fs);
|
||||
DtVoxelFileWriter writer = new DtVoxelFileWriter(compressor);
|
||||
writer.Write(bw, voxelFile, compression);
|
||||
}
|
||||
|
||||
|
||||
public RcGizmo SphereCollider(RcVec3f p, LFloat walkableClimb)
|
||||
{
|
||||
LFloat radius = 1 + (LFloat)random.NextDouble() * 10;
|
||||
var collider = new DtSphereCollider(p, radius, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER, walkableClimb);
|
||||
var gizmo = RcGizmoFactory.Sphere(p, radius);
|
||||
|
||||
return new RcGizmo(collider, gizmo);
|
||||
}
|
||||
|
||||
public RcGizmo CapsuleCollider(RcVec3f p, LFloat walkableClimb)
|
||||
{
|
||||
LFloat radius = (LFloat)0.4f + random.NextDouble() * (LFloat)4f;
|
||||
RcVec3f a = new RcVec3f(
|
||||
((LFloat)1f - (LFloat)2f * random.NextDouble()),
|
||||
(LFloat)0.01f + random.NextDouble(),
|
||||
((LFloat)1f - 2 * random.NextDouble())
|
||||
);
|
||||
a = RcVec3f.Normalize(a);
|
||||
|
||||
LFloat len = (LFloat)1f + random.NextDouble() * (LFloat)20f;
|
||||
a.X *= len;
|
||||
a.Y *= len;
|
||||
a.Z *= len;
|
||||
RcVec3f start = new RcVec3f(p.X, p.Y, p.Z);
|
||||
RcVec3f end = new RcVec3f(p.X + a.X, p.Y + a.Y, p.Z + a.Z);
|
||||
var collider = new DtCapsuleCollider(start, end, radius, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER, walkableClimb);
|
||||
var gizmo = RcGizmoFactory.Capsule(start, end, radius);
|
||||
return new RcGizmo(collider, gizmo);
|
||||
}
|
||||
|
||||
public RcGizmo BoxCollider(RcVec3f p, LFloat walkableClimb)
|
||||
{
|
||||
RcVec3f extent = new RcVec3f(
|
||||
(LFloat)0.5f + random.NextDouble() * (LFloat)6f,
|
||||
(LFloat)0.5f + random.NextDouble() * (LFloat)6f,
|
||||
(LFloat)0.5f + random.NextDouble() * (LFloat)6f
|
||||
);
|
||||
RcVec3f forward = new RcVec3f(((LFloat)1f - 2 * (LFloat)random.NextDouble()), 0, ((LFloat)1f - 2 * (LFloat)random.NextDouble()));
|
||||
RcVec3f up = new RcVec3f(((LFloat)1f - 2 * (LFloat)random.NextDouble()), (LFloat)0.01f + (LFloat)random.NextDouble(), ((LFloat)1f - 2 * (LFloat)random.NextDouble()));
|
||||
RcVec3f[] halfEdges = Detour.Dynamic.Colliders.DtBoxCollider.GetHalfEdges(up, forward, extent);
|
||||
var collider = new DtBoxCollider(p, halfEdges, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER, walkableClimb);
|
||||
var gizmo = RcGizmoFactory.Box(p, halfEdges);
|
||||
return new RcGizmo(collider, gizmo);
|
||||
}
|
||||
|
||||
public RcGizmo CylinderCollider(RcVec3f p, LFloat walkableClimb)
|
||||
{
|
||||
LFloat radius = (LFloat)0.7f + (LFloat)random.NextDouble() * (LFloat)4f;
|
||||
RcVec3f a = new RcVec3f((LFloat)1f - 2 * (LFloat)random.NextDouble(), (LFloat)0.01f + (LFloat)random.NextDouble(), (LFloat)1f - 2 * (LFloat)random.NextDouble());
|
||||
a = RcVec3f.Normalize(a);
|
||||
LFloat len = (LFloat)2f + (LFloat)random.NextDouble() * (LFloat)20f;
|
||||
a.X *= len;
|
||||
a.Y *= len;
|
||||
a.Z *= len;
|
||||
RcVec3f start = new RcVec3f(p.X, p.Y, p.Z);
|
||||
RcVec3f end = new RcVec3f(p.X + a.X, p.Y + a.Y, p.Z + a.Z);
|
||||
var collider = new DtCylinderCollider(start, end, radius, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER, walkableClimb);
|
||||
var gizmo = RcGizmoFactory.Cylinder(start, end, radius);
|
||||
|
||||
return new RcGizmo(collider, gizmo);
|
||||
}
|
||||
|
||||
public RcGizmo CompositeCollider(RcVec3f p, LFloat walkableClimb)
|
||||
{
|
||||
RcVec3f baseExtent = new RcVec3f(5, 3, 8);
|
||||
RcVec3f baseCenter = new RcVec3f(p.X, p.Y + 3, p.Z);
|
||||
RcVec3f baseUp = new RcVec3f(0, 1, 0);
|
||||
RcVec3f forward = new RcVec3f(((LFloat)1f - 2 * random.NextDouble()), 0, ((LFloat)1f - 2 * random.NextDouble()));
|
||||
forward = RcVec3f.Normalize(forward);
|
||||
|
||||
RcVec3f side = RcVec3f.Cross(forward, baseUp);
|
||||
DtBoxCollider @base = new DtBoxCollider(baseCenter, Detour.Dynamic.Colliders.DtBoxCollider.GetHalfEdges(baseUp, forward, baseExtent),
|
||||
SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD, walkableClimb);
|
||||
var roofUp = RcVec3f.Zero;
|
||||
RcVec3f roofExtent = new RcVec3f((LFloat)4.5f, (LFloat)4.5f, (LFloat)8f);
|
||||
var rx = RcMatrix4x4f.CreateFromRotate(45, forward.X, forward.Y, forward.Z);
|
||||
roofUp = MulMatrixVector(ref roofUp, rx, baseUp);
|
||||
RcVec3f roofCenter = new RcVec3f(p.X, p.Y + 6, p.Z);
|
||||
DtBoxCollider roof = new DtBoxCollider(roofCenter, Detour.Dynamic.Colliders.DtBoxCollider.GetHalfEdges(roofUp, forward, roofExtent),
|
||||
SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD, walkableClimb);
|
||||
RcVec3f trunkStart = new RcVec3f(
|
||||
baseCenter.X - forward.X * 15 + side.X * 6,
|
||||
p.Y,
|
||||
baseCenter.Z - forward.Z * 15 + side.Z * 6
|
||||
);
|
||||
RcVec3f trunkEnd = new RcVec3f(trunkStart.X, trunkStart.Y + 10, trunkStart.Z);
|
||||
DtCapsuleCollider trunk = new DtCapsuleCollider(trunkStart, trunkEnd, (LFloat)0.5f, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD,
|
||||
walkableClimb);
|
||||
RcVec3f crownCenter = new RcVec3f(
|
||||
baseCenter.X - forward.X * 15 + side.X * 6, p.Y + 10,
|
||||
baseCenter.Z - forward.Z * 15 + side.Z * 6
|
||||
);
|
||||
DtSphereCollider crown = new DtSphereCollider(crownCenter, (LFloat)4f, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GRASS,
|
||||
walkableClimb);
|
||||
DtCompositeCollider collider = new DtCompositeCollider(@base, roof, trunk, crown);
|
||||
IRcGizmoMeshFilter baseGizmo = RcGizmoFactory.Box(baseCenter, Detour.Dynamic.Colliders.DtBoxCollider.GetHalfEdges(baseUp, forward, baseExtent));
|
||||
IRcGizmoMeshFilter roofGizmo = RcGizmoFactory.Box(roofCenter, Detour.Dynamic.Colliders.DtBoxCollider.GetHalfEdges(roofUp, forward, roofExtent));
|
||||
IRcGizmoMeshFilter trunkGizmo = RcGizmoFactory.Capsule(trunkStart, trunkEnd, (LFloat)0.5f);
|
||||
IRcGizmoMeshFilter crownGizmo = RcGizmoFactory.Sphere(crownCenter, (LFloat)4f);
|
||||
IRcGizmoMeshFilter gizmo = RcGizmoFactory.Composite(baseGizmo, roofGizmo, trunkGizmo, crownGizmo);
|
||||
return new RcGizmo(collider, gizmo);
|
||||
}
|
||||
|
||||
public RcGizmo TrimeshBridge(RcVec3f p, LFloat walkableClimb)
|
||||
{
|
||||
return TrimeshCollider(p, bridgeGeom, walkableClimb);
|
||||
}
|
||||
|
||||
public RcGizmo TrimeshHouse(RcVec3f p, LFloat walkableClimb)
|
||||
{
|
||||
return TrimeshCollider(p, houseGeom, walkableClimb);
|
||||
}
|
||||
|
||||
public RcGizmo ConvexTrimesh(RcVec3f p, LFloat walkableClimb)
|
||||
{
|
||||
LFloat[] verts = TransformVertices(p, convexGeom, 360);
|
||||
var collider = new DtConvexTrimeshCollider(verts, convexGeom.faces,
|
||||
SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD, walkableClimb * 10);
|
||||
var gizmo = RcGizmoFactory.Trimesh(verts, convexGeom.faces);
|
||||
return new RcGizmo(collider, gizmo);
|
||||
}
|
||||
|
||||
private RcGizmo TrimeshCollider(RcVec3f p, DemoInputGeomProvider geom, LFloat walkableClimb)
|
||||
{
|
||||
LFloat[] verts = TransformVertices(p, geom, 0);
|
||||
var collider = new DtTrimeshCollider(verts, geom.faces, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD,
|
||||
walkableClimb * 10);
|
||||
var gizmo = RcGizmoFactory.Trimesh(verts, geom.faces);
|
||||
|
||||
return new RcGizmo(collider, gizmo);
|
||||
}
|
||||
|
||||
private LFloat[] TransformVertices(RcVec3f p, DemoInputGeomProvider geom, LFloat ax)
|
||||
{
|
||||
var rx = RcMatrix4x4f.CreateFromRotate((LFloat)random.NextDouble() * ax, 1, 0, 0);
|
||||
var ry = RcMatrix4x4f.CreateFromRotate((LFloat)random.NextDouble() * 360, 0, 1, 0);
|
||||
var m = RcMatrix4x4f.Mul(ref rx, ref ry);
|
||||
LFloat[] verts = new LFloat[geom.vertices.Length];
|
||||
RcVec3f v = new RcVec3f();
|
||||
RcVec3f vr = new RcVec3f();
|
||||
for (int i = 0; i < geom.vertices.Length; i += 3)
|
||||
{
|
||||
v.X = geom.vertices[i];
|
||||
v.Y = geom.vertices[i + 1];
|
||||
v.Z = geom.vertices[i + 2];
|
||||
MulMatrixVector(ref vr, m, v);
|
||||
vr.X += p.X;
|
||||
vr.Y += p.Y - (LFloat)0.1f;
|
||||
vr.Z += p.Z;
|
||||
verts[i] = vr.X;
|
||||
verts[i + 1] = vr.Y;
|
||||
verts[i + 2] = vr.Z;
|
||||
}
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private static LFloat[] MulMatrixVector(LFloat[] resultvector, LFloat[] matrix, LFloat[] pvector)
|
||||
{
|
||||
resultvector[0] = matrix[0] * pvector[0] + matrix[4] * pvector[1] + matrix[8] * pvector[2];
|
||||
resultvector[1] = matrix[1] * pvector[0] + matrix[5] * pvector[1] + matrix[9] * pvector[2];
|
||||
resultvector[2] = matrix[2] * pvector[0] + matrix[6] * pvector[1] + matrix[10] * pvector[2];
|
||||
return resultvector;
|
||||
}
|
||||
|
||||
private static RcVec3f MulMatrixVector(ref RcVec3f resultvector, RcMatrix4x4f matrix, RcVec3f pvector)
|
||||
{
|
||||
resultvector.X = matrix.M11 * pvector.X + matrix.M21 * pvector.Y + matrix.M31 * pvector.Z;
|
||||
resultvector.Y = matrix.M12 * pvector.X + matrix.M22 * pvector.Y + matrix.M32 * pvector.Z;
|
||||
resultvector.Z = matrix.M13 * pvector.X + matrix.M23 * pvector.Y + matrix.M33 * pvector.Z;
|
||||
return resultvector;
|
||||
}
|
||||
|
||||
// public bool Update(TaskFactory executor)
|
||||
// {
|
||||
// if (dynaMesh == null)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return dynaMesh.Update(executor);
|
||||
// }
|
||||
|
||||
public bool Raycast(RcVec3f spos, RcVec3f epos, out LFloat hitPos, out RcVec3f raycastHitPos)
|
||||
{
|
||||
RcVec3f sp = new RcVec3f(spos.X, spos.Y + (LFloat)1.3f, spos.Z);
|
||||
RcVec3f ep = new RcVec3f(epos.X, epos.Y + (LFloat)1.3f, epos.Z);
|
||||
|
||||
bool hasHit = dynaMesh.VoxelQuery().Raycast(sp, ep, out hitPos);
|
||||
raycastHitPos = hasHit
|
||||
? new RcVec3f(sp.X + hitPos * (ep.X - sp.X), sp.Y + hitPos * (ep.Y - sp.Y), sp.Z + hitPos * (ep.Z - sp.Z))
|
||||
: ep;
|
||||
|
||||
return hasHit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01b6d8138cd24d828ba4c1a4a6fe9869
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,24 @@
|
||||
using DotRecast.Core.Collections;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcDynamicUpdateToolMode
|
||||
{
|
||||
public static readonly RcDynamicUpdateToolMode BUILD = new RcDynamicUpdateToolMode(0, "Build");
|
||||
public static readonly RcDynamicUpdateToolMode COLLIDERS = new RcDynamicUpdateToolMode(1, "Colliders");
|
||||
public static readonly RcDynamicUpdateToolMode RAYCAST = new RcDynamicUpdateToolMode(2, "Raycast");
|
||||
|
||||
public static readonly RcImmutableArray<RcDynamicUpdateToolMode> Values = RcImmutableArray.Create(
|
||||
BUILD, COLLIDERS, RAYCAST
|
||||
);
|
||||
|
||||
public readonly int Idx;
|
||||
public readonly string Label;
|
||||
|
||||
private RcDynamicUpdateToolMode(int idx, string label)
|
||||
{
|
||||
Idx = idx;
|
||||
Label = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8633fd681b6645a185859363dd49eb85
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,128 @@
|
||||
using System.Collections.Generic;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Detour.Extras.Jumplink;
|
||||
using DotRecast.Recast.Geom;
|
||||
using DotRecast.Recast.Toolset.Builder;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcJumpLinkBuilderTool : IRcToolable
|
||||
{
|
||||
private readonly List<JumpLink> _links;
|
||||
private JumpLinkBuilder _annotationBuilder;
|
||||
private readonly int _selEdge = -1;
|
||||
|
||||
public RcJumpLinkBuilderTool()
|
||||
{
|
||||
_links = new List<JumpLink>();
|
||||
}
|
||||
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return "Annotation Builder";
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_annotationBuilder = null;
|
||||
}
|
||||
|
||||
public JumpLinkBuilder GetAnnotationBuilder()
|
||||
{
|
||||
return _annotationBuilder;
|
||||
}
|
||||
|
||||
public int GetSelEdge()
|
||||
{
|
||||
return _selEdge;
|
||||
}
|
||||
|
||||
public List<JumpLink> GetLinks()
|
||||
{
|
||||
return _links;
|
||||
}
|
||||
|
||||
public void Build(IInputGeomProvider geom, RcNavMeshBuildSettings settings, IList<RcBuilderResult> results, RcJumpLinkBuilderToolConfig cfg)
|
||||
{
|
||||
if (_annotationBuilder == null)
|
||||
{
|
||||
if (0 < results.Count)
|
||||
{
|
||||
_annotationBuilder = new JumpLinkBuilder(results);
|
||||
}
|
||||
}
|
||||
|
||||
_links.Clear();
|
||||
if (_annotationBuilder != null)
|
||||
{
|
||||
LFloat cellSize = settings.cellSize;
|
||||
LFloat agentHeight = settings.agentHeight;
|
||||
LFloat agentRadius = settings.agentRadius;
|
||||
LFloat agentClimb = settings.agentMaxClimb;
|
||||
LFloat cellHeight = settings.cellHeight;
|
||||
|
||||
if ((cfg.buildTypes & JumpLinkType.EDGE_CLIMB_DOWN.Bit) != 0)
|
||||
{
|
||||
JumpLinkBuilderConfig config = new JumpLinkBuilderConfig(
|
||||
cellSize,
|
||||
cellHeight,
|
||||
agentRadius,
|
||||
agentHeight,
|
||||
agentClimb,
|
||||
cfg.groundTolerance,
|
||||
-agentRadius * (LFloat)0.2f,
|
||||
cellSize + 2 * agentRadius + cfg.climbDownDistance,
|
||||
-cfg.climbDownMaxHeight,
|
||||
-cfg.climbDownMinHeight,
|
||||
0
|
||||
);
|
||||
_links.AddRange(_annotationBuilder.Build(config, JumpLinkType.EDGE_CLIMB_DOWN));
|
||||
}
|
||||
|
||||
if ((cfg.buildTypes & JumpLinkType.EDGE_JUMP.Bit) != 0)
|
||||
{
|
||||
JumpLinkBuilderConfig config = new JumpLinkBuilderConfig(
|
||||
cellSize,
|
||||
cellHeight,
|
||||
agentRadius,
|
||||
agentHeight,
|
||||
agentClimb,
|
||||
cfg.groundTolerance,
|
||||
-agentRadius * (LFloat)0.2f,
|
||||
cfg.edgeJumpEndDistance,
|
||||
-cfg.edgeJumpDownMaxHeight,
|
||||
cfg.edgeJumpUpMaxHeight,
|
||||
cfg.edgeJumpHeight
|
||||
);
|
||||
_links.AddRange(_annotationBuilder.Build(config, JumpLinkType.EDGE_JUMP));
|
||||
}
|
||||
|
||||
if (cfg.buildOffMeshConnections)
|
||||
{
|
||||
int area = SampleAreaModifications.SAMPLE_POLYAREA_TYPE_JUMP_AUTO;
|
||||
geom.RemoveOffMeshConnections(c => c.area == area);
|
||||
_links.ForEach(l => AddOffMeshLink(l, geom, agentRadius));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOffMeshLink(JumpLink link, IInputGeomProvider geom, LFloat agentRadius)
|
||||
{
|
||||
int area = SampleAreaModifications.SAMPLE_POLYAREA_TYPE_JUMP_AUTO;
|
||||
int flags = SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP;
|
||||
RcVec3f prev = new RcVec3f();
|
||||
for (int i = 0; i < link.startSamples.Length; i++)
|
||||
{
|
||||
RcVec3f p = link.startSamples[i].p;
|
||||
RcVec3f q = link.endSamples[i].p;
|
||||
if (i == 0 || RcVecUtils.Dist2D(prev, p) > agentRadius)
|
||||
{
|
||||
geom.AddOffMeshConnection(p, q, agentRadius, false, area, flags);
|
||||
prev = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 066530f8c31e44aa8101611ae7c382eb
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
recast4j copyright (c) 2020-2021 Piotr Piastucki piotr@jtilia.org
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
using DotRecast.Detour.Extras.Jumplink;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcJumpLinkBuilderToolConfig
|
||||
{
|
||||
public int buildTypes = JumpLinkType.EDGE_CLIMB_DOWN.Bit | JumpLinkType.EDGE_JUMP.Bit;
|
||||
public bool buildOffMeshConnections = false;
|
||||
|
||||
public LFloat groundTolerance = (LFloat)0.3f;
|
||||
public LFloat climbDownDistance = (LFloat)0.4f;
|
||||
public LFloat climbDownMaxHeight = (LFloat)3.2f;
|
||||
public LFloat climbDownMinHeight = (LFloat)1.5f;
|
||||
public LFloat edgeJumpEndDistance = (LFloat)2f;
|
||||
public LFloat edgeJumpHeight = (LFloat)0.4f;
|
||||
public LFloat edgeJumpDownMaxHeight = (LFloat)2.5f;
|
||||
public LFloat edgeJumpUpMaxHeight = (LFloat)0.3f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c5b517440744444a2f20dacc6338d3c
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using DotRecast.Core;
|
||||
using DotRecast.Core.Collections;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Detour;
|
||||
using DotRecast.Detour.TileCache;
|
||||
using DotRecast.Detour.TileCache.Io.Compress;
|
||||
using DotRecast.Recast.Geom;
|
||||
using DotRecast.Recast.Toolset.Builder;
|
||||
using DotRecast.Recast.Toolset.Geom;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcObstacleTool : IRcToolable
|
||||
{
|
||||
private readonly IDtTileCacheCompressorFactory _comp;
|
||||
private readonly DemoDtTileCacheMeshProcess _proc;
|
||||
private DtTileCache _tc;
|
||||
|
||||
public RcObstacleTool(IDtTileCacheCompressorFactory comp)
|
||||
{
|
||||
_comp = comp;
|
||||
_proc = new DemoDtTileCacheMeshProcess();
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return "Temp Obstacles";
|
||||
}
|
||||
|
||||
public NavMeshBuildResult Build(IInputGeomProvider geom, RcNavMeshBuildSettings setting, RcByteOrder order, bool cCompatibility)
|
||||
{
|
||||
if (null == geom || null == geom.GetMesh())
|
||||
{
|
||||
//m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: No vertices and triangles.");
|
||||
return new NavMeshBuildResult();
|
||||
}
|
||||
|
||||
_proc.Init(geom);
|
||||
|
||||
// Init cache
|
||||
var bmin = geom.GetMeshBoundsMin();
|
||||
var bmax = geom.GetMeshBoundsMax();
|
||||
RcCommons.CalcGridSize(bmin, bmax, setting.cellSize, out var gw, out var gh);
|
||||
int ts = setting.tileSize;
|
||||
int tw = (gw + ts - 1) / ts;
|
||||
int th = (gh + ts - 1) / ts;
|
||||
|
||||
// Generation params.
|
||||
var walkableRadius = (int)LMath.Ceiling(setting.agentRadius / setting.cellSize); // Reserve enough padding.
|
||||
RcConfig cfg = new RcConfig(
|
||||
true, setting.tileSize, setting.tileSize,
|
||||
walkableRadius + 3,
|
||||
RcPartitionType.OfValue(setting.partitioning),
|
||||
setting.cellSize, setting.cellHeight,
|
||||
setting.agentMaxSlope, setting.agentHeight, setting.agentRadius, setting.agentMaxClimb,
|
||||
(int)RcMath.Sqr(setting.minRegionSize), (int)RcMath.Sqr(setting.mergedRegionSize), // Note: area = size*size
|
||||
(int)(setting.edgeMaxLen / setting.cellSize), setting.edgeMaxError,
|
||||
setting.vertsPerPoly,
|
||||
setting.detailSampleDist, setting.detailSampleMaxError,
|
||||
true, true, true,
|
||||
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
|
||||
|
||||
var builder = new DtTileCacheLayerBuilder(DtTileCacheCompressorFactory.Shared);
|
||||
var storageParams = new DtTileCacheStorageParams(order, cCompatibility);
|
||||
var results = builder.Build(geom, cfg, storageParams, 8, tw, th);
|
||||
var layers = results
|
||||
.SelectMany(x => x.layers)
|
||||
.ToList();
|
||||
|
||||
_tc = CreateTileCache(geom, setting, tw, th, order, cCompatibility);
|
||||
|
||||
for (int i = 0; i < layers.Count; ++i)
|
||||
{
|
||||
var layer = layers[i];
|
||||
var refs = _tc.AddTile(layer, 0);
|
||||
_tc.BuildNavMeshTile(refs);
|
||||
}
|
||||
|
||||
return new NavMeshBuildResult(RcImmutableArray<RcBuilderResult>.Empty, _tc.GetNavMesh());
|
||||
}
|
||||
|
||||
public void ClearAllTempObstacles()
|
||||
{
|
||||
if (null == _tc)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _tc.GetObstacleCount(); ++i)
|
||||
{
|
||||
DtTileCacheObstacle ob = _tc.GetObstacle(i);
|
||||
if (ob.state == DtObstacleState.DT_OBSTACLE_EMPTY)
|
||||
continue;
|
||||
|
||||
_tc.RemoveObstacle(_tc.GetObstacleRef(ob));
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTempObstacle(RcVec3f sp, RcVec3f sq)
|
||||
{
|
||||
if (null == _tc)
|
||||
return;
|
||||
|
||||
long refs = HitTestObstacle(sp, sq);
|
||||
_tc.RemoveObstacle(refs);
|
||||
}
|
||||
|
||||
public long AddTempObstacle(RcVec3f p)
|
||||
{
|
||||
if (null == _tc)
|
||||
return 0;
|
||||
|
||||
p.Y -= (LFloat)0.5f;
|
||||
return _tc.AddObstacle(p, (LFloat)1.0f, (LFloat)2.0f);
|
||||
}
|
||||
|
||||
public DtTileCache GetTileCache()
|
||||
{
|
||||
return _tc;
|
||||
}
|
||||
|
||||
public DtTileCache CreateTileCache(IInputGeomProvider geom, RcNavMeshBuildSettings setting, int tw, int th, RcByteOrder order, bool cCompatibility)
|
||||
{
|
||||
DtTileCacheParams option = new DtTileCacheParams();
|
||||
option.ch = setting.cellHeight;
|
||||
option.cs = setting.cellSize;
|
||||
option.orig = geom.GetMeshBoundsMin();
|
||||
option.height = setting.tileSize;
|
||||
option.width = setting.tileSize;
|
||||
option.walkableHeight = setting.agentHeight;
|
||||
option.walkableRadius = setting.agentRadius;
|
||||
option.walkableClimb = setting.agentMaxClimb;
|
||||
option.maxSimplificationError = setting.edgeMaxError;
|
||||
option.maxTiles = tw * th * 4; // for test EXPECTED_LAYERS_PER_TILE;
|
||||
option.maxObstacles = 128;
|
||||
|
||||
DtNavMeshParams navMeshParams = new DtNavMeshParams();
|
||||
navMeshParams.orig = geom.GetMeshBoundsMin();
|
||||
navMeshParams.tileWidth = setting.tileSize * setting.cellSize;
|
||||
navMeshParams.tileHeight = setting.tileSize * setting.cellSize;
|
||||
navMeshParams.maxTiles = 256; // ..
|
||||
navMeshParams.maxPolys = 16384;
|
||||
|
||||
var navMesh = new DtNavMesh(navMeshParams, 6);
|
||||
var comp = _comp.Create(cCompatibility ? 0 : 1);
|
||||
var storageParams = new DtTileCacheStorageParams(order, cCompatibility);
|
||||
DtTileCache tc = new DtTileCache(option, storageParams, navMesh, comp, _proc);
|
||||
return tc;
|
||||
}
|
||||
|
||||
public long HitTestObstacle(RcVec3f sp, RcVec3f sq)
|
||||
{
|
||||
LFloat tmin = LFloat.MaxValue;
|
||||
DtTileCacheObstacle obmin = null;
|
||||
|
||||
for (int i = 0; i < _tc.GetObstacleCount(); ++i)
|
||||
{
|
||||
DtTileCacheObstacle ob = _tc.GetObstacle(i);
|
||||
if (ob.state == DtObstacleState.DT_OBSTACLE_EMPTY)
|
||||
continue;
|
||||
|
||||
RcVec3f bmin = RcVec3f.Zero;
|
||||
RcVec3f bmax = RcVec3f.Zero;
|
||||
_tc.GetObstacleBounds(ob, ref bmin, ref bmax);
|
||||
|
||||
if (RcIntersections.IsectSegAABB(sp, sq, bmin, bmax, out var t0, out var t1))
|
||||
{
|
||||
if (t0 < tmin)
|
||||
{
|
||||
tmin = t0;
|
||||
obmin = ob;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _tc.GetObstacleRef(obmin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac22a9a64e9c487da2c9cfd548f78723
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Recast.Geom;
|
||||
using DotRecast.Recast.Toolset.Builder;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcOffMeshConnectionTool : IRcToolable
|
||||
{
|
||||
public RcOffMeshConnectionTool()
|
||||
{
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return "Off-Mesh Links";
|
||||
}
|
||||
|
||||
public void Add(IInputGeomProvider geom, RcNavMeshBuildSettings settings, RcVec3f start, RcVec3f end, bool bidir)
|
||||
{
|
||||
if (null == geom)
|
||||
return;
|
||||
|
||||
int area = SampleAreaModifications.SAMPLE_POLYAREA_TYPE_JUMP;
|
||||
int flags = SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP;
|
||||
geom.AddOffMeshConnection(start, end, settings.agentRadius, bidir, area, flags);
|
||||
}
|
||||
|
||||
public void Remove(IInputGeomProvider geom, RcNavMeshBuildSettings settings, RcVec3f p)
|
||||
{
|
||||
// Delete
|
||||
// Find nearest link end-point
|
||||
LFloat nearestDist = LFloat.MaxValue;
|
||||
RcOffMeshConnection nearestConnection = null;
|
||||
foreach (RcOffMeshConnection offMeshCon in geom.GetOffMeshConnections())
|
||||
{
|
||||
LFloat d = LMath.Min(RcVecUtils.DistanceSquared(p, offMeshCon.verts, 0), RcVecUtils.DistanceSquared(p, offMeshCon.verts, 3));
|
||||
if (d < nearestDist && LMath.Sqrt(d) < settings.agentRadius)
|
||||
{
|
||||
nearestDist = d;
|
||||
nearestConnection = offMeshCon;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearestConnection != null)
|
||||
{
|
||||
geom.GetOffMeshConnections().Remove(nearestConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8faec19cd36a4e3486f0eb0de76d2883
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,454 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DotRecast.Core;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Detour;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcTestNavMeshTool : IRcToolable
|
||||
{
|
||||
public const int MAX_POLYS = 256;
|
||||
public const int MAX_SMOOTH = 2048;
|
||||
|
||||
|
||||
public RcTestNavMeshTool()
|
||||
{
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return "Test Navmesh";
|
||||
}
|
||||
|
||||
public DtStatus FindFollowPath(DtNavMesh navMesh, DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPt, RcVec3f endPt, IDtQueryFilter filter, bool enableRaycast,
|
||||
ref List<long> pathIterPolys, int pathIterPolyCount, ref List<RcVec3f> smoothPath)
|
||||
{
|
||||
if (startRef == 0 || endRef == 0)
|
||||
{
|
||||
pathIterPolys?.Clear();
|
||||
smoothPath?.Clear();
|
||||
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
pathIterPolys ??= new List<long>();
|
||||
smoothPath ??= new List<RcVec3f>();
|
||||
|
||||
pathIterPolys.Clear();
|
||||
pathIterPolyCount = 0;
|
||||
|
||||
smoothPath.Clear();
|
||||
|
||||
var opt = new DtFindPathOption(enableRaycast ? DtFindPathOptions.DT_FINDPATH_ANY_ANGLE : 0, LFloat.MaxValue);
|
||||
navQuery.FindPath(startRef, endRef, startPt, endPt, filter, ref pathIterPolys, opt);
|
||||
if (0 >= pathIterPolys.Count)
|
||||
return DtStatus.DT_FAILURE;
|
||||
|
||||
pathIterPolyCount = pathIterPolys.Count;
|
||||
|
||||
// Iterate over the path to find smooth path on the detail mesh surface.
|
||||
navQuery.ClosestPointOnPoly(startRef, startPt, out var iterPos, out var _);
|
||||
navQuery.ClosestPointOnPoly(pathIterPolys[pathIterPolys.Count - 1], endPt, out var targetPos, out var _);
|
||||
|
||||
LFloat STEP_SIZE = (LFloat)0.5f;
|
||||
LFloat SLOP = (LFloat)0.01f;
|
||||
|
||||
smoothPath.Clear();
|
||||
smoothPath.Add(iterPos);
|
||||
|
||||
Span<long> visited = stackalloc long[16];
|
||||
int nvisited = 0;
|
||||
|
||||
|
||||
// Move towards target a small advancement at a time until target reached or
|
||||
// when ran out of memory to store the path.
|
||||
while (0 < pathIterPolyCount && smoothPath.Count < MAX_SMOOTH)
|
||||
{
|
||||
// Find location to steer towards.
|
||||
if (!DtPathUtils.GetSteerTarget(navQuery, iterPos, targetPos, SLOP,
|
||||
pathIterPolys, pathIterPolyCount, out var steerPos, out var steerPosFlag, out var steerPosRef))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bool endOfPath = (steerPosFlag & DtStraightPathFlags.DT_STRAIGHTPATH_END) != 0
|
||||
? true
|
||||
: false;
|
||||
bool offMeshConnection = (steerPosFlag & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0
|
||||
? true
|
||||
: false;
|
||||
|
||||
// Find movement delta.
|
||||
RcVec3f delta = RcVec3f.Subtract(steerPos, iterPos);
|
||||
LFloat len = LMath.Sqrt(RcVec3f.Dot(delta, delta));
|
||||
// If the steer target is end of path or off-mesh link, do not move past the location.
|
||||
if ((endOfPath || offMeshConnection) && len < STEP_SIZE)
|
||||
{
|
||||
len = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = STEP_SIZE / len;
|
||||
}
|
||||
|
||||
RcVec3f moveTgt = RcVecUtils.Mad(iterPos, delta, len);
|
||||
|
||||
// Move
|
||||
navQuery.MoveAlongSurface(pathIterPolys[0], iterPos, moveTgt, filter, out var result, visited, out nvisited, 16);
|
||||
|
||||
iterPos = result;
|
||||
|
||||
pathIterPolyCount = DtPathUtils.MergeCorridorStartMoved(ref pathIterPolys, pathIterPolyCount, MAX_POLYS, visited, nvisited);
|
||||
pathIterPolyCount = DtPathUtils.FixupShortcuts(ref pathIterPolys, pathIterPolyCount, navQuery);
|
||||
|
||||
var status = navQuery.GetPolyHeight(pathIterPolys[0], result, out var h);
|
||||
if (status.Succeeded())
|
||||
{
|
||||
iterPos.Y = h;
|
||||
}
|
||||
|
||||
// Handle end of path and off-mesh links when close enough.
|
||||
if (endOfPath && DtPathUtils.InRange(iterPos, steerPos, SLOP, (LFloat)1.0f))
|
||||
{
|
||||
// Reached end of path.
|
||||
iterPos = targetPos;
|
||||
if (smoothPath.Count < MAX_SMOOTH)
|
||||
{
|
||||
smoothPath.Add(iterPos);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (offMeshConnection && DtPathUtils.InRange(iterPos, steerPos, SLOP, (LFloat)1.0f))
|
||||
{
|
||||
// Reached off-mesh connection.
|
||||
RcVec3f startPos = RcVec3f.Zero;
|
||||
RcVec3f endPos = RcVec3f.Zero;
|
||||
|
||||
// Advance the path up to and over the off-mesh connection.
|
||||
long prevRef = 0;
|
||||
long polyRef = pathIterPolys[0];
|
||||
int npos = 0;
|
||||
while (npos < pathIterPolyCount && polyRef != steerPosRef)
|
||||
{
|
||||
prevRef = polyRef;
|
||||
polyRef = pathIterPolys[npos];
|
||||
npos++;
|
||||
}
|
||||
|
||||
pathIterPolys = pathIterPolys.GetRange(npos, pathIterPolys.Count - npos);
|
||||
pathIterPolyCount -= npos;
|
||||
|
||||
// Handle the connection.
|
||||
var status2 = navMesh.GetOffMeshConnectionPolyEndPoints(prevRef, polyRef, ref startPos, ref endPos);
|
||||
if (status2.Succeeded())
|
||||
{
|
||||
if (smoothPath.Count < MAX_SMOOTH)
|
||||
{
|
||||
smoothPath.Add(startPos);
|
||||
// Hack to make the dotted path not visible during off-mesh connection.
|
||||
if ((smoothPath.Count & 1) != 0)
|
||||
{
|
||||
smoothPath.Add(startPos);
|
||||
}
|
||||
}
|
||||
|
||||
// Move position at the other side of the off-mesh link.
|
||||
iterPos = endPos;
|
||||
navQuery.GetPolyHeight(pathIterPolys[0], iterPos, out var eh);
|
||||
iterPos.Y = eh;
|
||||
}
|
||||
}
|
||||
|
||||
// Store results.
|
||||
if (smoothPath.Count < MAX_SMOOTH)
|
||||
{
|
||||
smoothPath.Add(iterPos);
|
||||
}
|
||||
}
|
||||
|
||||
return DtStatus.DT_SUCCESS;
|
||||
}
|
||||
|
||||
public DtStatus FindStraightPath(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPt, RcVec3f endPt, IDtQueryFilter filter, bool enableRaycast,
|
||||
ref List<long> polys, ref List<DtStraightPath> straightPath, int straightPathOptions)
|
||||
{
|
||||
if (startRef == 0 || endRef == 0)
|
||||
{
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
polys ??= new List<long>();
|
||||
straightPath ??= new List<DtStraightPath>();
|
||||
|
||||
polys.Clear();
|
||||
straightPath.Clear();
|
||||
|
||||
var opt = new DtFindPathOption(enableRaycast ? DtFindPathOptions.DT_FINDPATH_ANY_ANGLE : 0, LFloat.MaxValue);
|
||||
navQuery.FindPath(startRef, endRef, startPt, endPt, filter, ref polys, opt);
|
||||
|
||||
if (0 >= polys.Count)
|
||||
return DtStatus.DT_FAILURE;
|
||||
|
||||
// In case of partial path, make sure the end point is clamped to the last polygon.
|
||||
var epos = new RcVec3f(endPt.X, endPt.Y, endPt.Z);
|
||||
if (polys[polys.Count - 1] != endRef)
|
||||
{
|
||||
var result = navQuery.ClosestPointOnPoly(polys[polys.Count - 1], endPt, out var closest, out var _);
|
||||
if (result.Succeeded())
|
||||
{
|
||||
epos = closest;
|
||||
}
|
||||
}
|
||||
|
||||
navQuery.FindStraightPath(startPt, epos, polys, polys.Count, ref straightPath, MAX_POLYS, straightPathOptions);
|
||||
|
||||
return DtStatus.DT_SUCCESS;
|
||||
}
|
||||
|
||||
public DtStatus InitSlicedFindPath(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter, bool enableRaycast)
|
||||
{
|
||||
if (startRef == 0 || endRef == 0)
|
||||
{
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
return navQuery.InitSlicedFindPath(startRef, endRef, startPos, endPos, filter,
|
||||
enableRaycast ? DtFindPathOptions.DT_FINDPATH_ANY_ANGLE : 0,
|
||||
LFloat.MaxValue
|
||||
);
|
||||
}
|
||||
|
||||
public DtStatus UpdateSlicedFindPath(DtNavMeshQuery navQuery, int maxIter, long endRef, RcVec3f startPos, RcVec3f endPos,
|
||||
ref List<long> path, ref List<DtStraightPath> straightPath)
|
||||
{
|
||||
var status = navQuery.UpdateSlicedFindPath(maxIter, out _);
|
||||
|
||||
if (!status.Succeeded())
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
navQuery.FinalizeSlicedFindPath(ref path);
|
||||
|
||||
straightPath?.Clear();
|
||||
if (path != null)
|
||||
{
|
||||
// In case of partial path, make sure the end point is clamped to the last polygon.
|
||||
RcVec3f epos = endPos;
|
||||
if (path[path.Count - 1] != endRef)
|
||||
{
|
||||
var result = navQuery.ClosestPointOnPoly(path[path.Count - 1], endPos, out var closest, out var _);
|
||||
if (result.Succeeded())
|
||||
{
|
||||
epos = closest;
|
||||
}
|
||||
}
|
||||
|
||||
straightPath = new List<DtStraightPath>(MAX_POLYS);
|
||||
navQuery.FindStraightPath(startPos, epos, path, path.Count, ref straightPath, MAX_POLYS, DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS);
|
||||
}
|
||||
|
||||
return DtStatus.DT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
public DtStatus Raycast(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter,
|
||||
ref List<long> polys, ref List<DtStraightPath> straightPath, ref RcVec3f hitPos, ref RcVec3f hitNormal, ref bool hitResult)
|
||||
{
|
||||
if (startRef == 0 || endRef == 0)
|
||||
{
|
||||
polys?.Clear();
|
||||
straightPath?.Clear();
|
||||
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
var path = new List<long>();
|
||||
var status = navQuery.Raycast(startRef, startPos, endPos, filter, out var t, out var hitNormal2, ref path);
|
||||
if (!status.Succeeded())
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
// results ...
|
||||
polys = path;
|
||||
|
||||
if (t > 1)
|
||||
{
|
||||
// No hit
|
||||
hitPos = endPos;
|
||||
hitResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hit
|
||||
hitPos = RcVec3f.Lerp(startPos, endPos, t);
|
||||
hitNormal = hitNormal2;
|
||||
hitResult = true;
|
||||
}
|
||||
|
||||
// Adjust height.
|
||||
if (path.Count > 0)
|
||||
{
|
||||
var result = navQuery.GetPolyHeight(path[path.Count - 1], hitPos, out var h);
|
||||
if (result.Succeeded())
|
||||
{
|
||||
hitPos.Y = h;
|
||||
}
|
||||
}
|
||||
|
||||
straightPath ??= new List<DtStraightPath>();
|
||||
straightPath.Clear();
|
||||
straightPath.Add(new DtStraightPath(startPos, 0, 0));
|
||||
straightPath.Add(new DtStraightPath(hitPos, 0, 0));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public DtStatus FindDistanceToWall(DtNavMeshQuery navQuery, long startRef, RcVec3f spos, LFloat maxRadius, IDtQueryFilter filter,
|
||||
ref LFloat hitDist, ref RcVec3f hitPos, ref RcVec3f hitNormal)
|
||||
{
|
||||
if (0 == startRef)
|
||||
{
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
var status = navQuery.FindDistanceToWall(startRef, spos, maxRadius, filter,
|
||||
out var tempHitDist, out var tempHitPos, out var tempHitNormal);
|
||||
|
||||
if (status.Succeeded())
|
||||
{
|
||||
hitDist = tempHitDist;
|
||||
hitPos = tempHitPos;
|
||||
hitNormal = tempHitNormal;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
public DtStatus FindPolysAroundCircle(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f spos, RcVec3f epos, IDtQueryFilter filter, ref List<long> resultRef, ref List<long> resultParent)
|
||||
{
|
||||
if (startRef == 0 || endRef == 0)
|
||||
{
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
LFloat dx = epos.X - spos.X;
|
||||
LFloat dz = epos.Z - spos.Z;
|
||||
LFloat dist = LMath.Sqrt(dx * dx + dz * dz);
|
||||
|
||||
List<long> tempResultRefs = new List<long>();
|
||||
List<long> tempParentRefs = new List<long>();
|
||||
List<LFloat> tempCosts = new List<LFloat>();
|
||||
var status = navQuery.FindPolysAroundCircle(startRef, spos, dist, filter, ref tempResultRefs, ref tempParentRefs, ref tempCosts);
|
||||
if (status.Succeeded())
|
||||
{
|
||||
resultRef = tempResultRefs;
|
||||
resultParent = tempParentRefs;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public DtStatus FindLocalNeighbourhood(DtNavMeshQuery navQuery, long startRef, RcVec3f spos, LFloat radius, IDtQueryFilter filter,
|
||||
ref List<long> resultRef, ref List<long> resultParent)
|
||||
{
|
||||
if (startRef == 0)
|
||||
{
|
||||
resultRef?.Clear();
|
||||
resultParent?.Clear();
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
resultRef ??= new List<long>();
|
||||
resultParent ??= new List<long>();
|
||||
|
||||
resultRef.Clear();
|
||||
resultParent.Clear();
|
||||
|
||||
var status = navQuery.FindLocalNeighbourhood(startRef, spos, radius, filter, ref resultRef, ref resultParent);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
public DtStatus FindPolysAroundShape(DtNavMeshQuery navQuery, LFloat agentHeight, long startRef, long endRef, RcVec3f spos, RcVec3f epos, IDtQueryFilter filter,
|
||||
ref List<long> resultRefs, ref List<long> resultParents, ref RcVec3f[] queryPoly)
|
||||
{
|
||||
if (startRef == 0 || endRef == 0)
|
||||
{
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
LFloat nx = (epos.Z - spos.Z) * (LFloat)0.25f;
|
||||
LFloat nz = -(epos.X - spos.X) * (LFloat)0.25f;
|
||||
|
||||
var tempQueryPoly = new RcVec3f[4];
|
||||
tempQueryPoly[0].X = spos.X + nx * (LFloat)1.2f;
|
||||
tempQueryPoly[0].Y = spos.Y + agentHeight / 2;
|
||||
tempQueryPoly[0].Z = spos.Z + nz * (LFloat)1.2f;
|
||||
|
||||
tempQueryPoly[1].X = spos.X - nx * (LFloat)1.3f;
|
||||
tempQueryPoly[1].Y = spos.Y + agentHeight / 2;
|
||||
tempQueryPoly[1].Z = spos.Z - nz * (LFloat)1.3f;
|
||||
|
||||
tempQueryPoly[2].X = epos.X - nx * (LFloat)0.8f;
|
||||
tempQueryPoly[2].Y = epos.Y + agentHeight / 2;
|
||||
tempQueryPoly[2].Z = epos.Z - nz * (LFloat)0.8f;
|
||||
|
||||
tempQueryPoly[3].X = epos.X + nx;
|
||||
tempQueryPoly[3].Y = epos.Y + agentHeight / 2;
|
||||
tempQueryPoly[3].Z = epos.Z + nz;
|
||||
|
||||
var tempResultRefs = new List<long>();
|
||||
var tempResultParents = new List<long>();
|
||||
var tempCosts = new List<LFloat>();
|
||||
var status = navQuery.FindPolysAroundShape(startRef, tempQueryPoly, filter, ref tempResultRefs, ref tempResultParents, ref tempCosts);
|
||||
if (status.Succeeded())
|
||||
{
|
||||
resultRefs = tempResultRefs;
|
||||
resultParents = tempResultParents;
|
||||
queryPoly = tempQueryPoly;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public DtStatus FindRandomPointAroundCircle(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f spos, RcVec3f epos, IDtQueryFilter filter, bool constrainByCircle, int count,
|
||||
ref List<RcVec3f> points)
|
||||
{
|
||||
if (startRef == 0 || endRef == 0)
|
||||
{
|
||||
return DtStatus.DT_FAILURE;
|
||||
}
|
||||
|
||||
LFloat dx = epos.X - spos.X;
|
||||
LFloat dz = epos.Z - spos.Z;
|
||||
LFloat dist = LMath.Sqrt(dx * dx + dz * dz);
|
||||
|
||||
IDtPolygonByCircleConstraint constraint = constrainByCircle
|
||||
? DtStrictDtPolygonByCircleConstraint.Shared
|
||||
: DtNoOpDtPolygonByCircleConstraint.Shared;
|
||||
|
||||
var frand = new RcRand();
|
||||
int prevCnt = points.Count;
|
||||
|
||||
points = new List<RcVec3f>();
|
||||
while (0 < count && points.Count < prevCnt + count)
|
||||
{
|
||||
var status = navQuery.FindRandomPointAroundCircle(startRef, spos, dist, filter, frand, constraint,
|
||||
out var randomRef, out var randomPt);
|
||||
|
||||
if (status.Succeeded())
|
||||
{
|
||||
points.Add(randomPt);
|
||||
}
|
||||
}
|
||||
|
||||
return DtStatus.DT_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 805d68bb90da4a549eaf3e1051a94eaa
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,39 @@
|
||||
using DotRecast.Core.Collections;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcTestNavmeshToolMode
|
||||
{
|
||||
public static readonly RcTestNavmeshToolMode PATHFIND_FOLLOW = new RcTestNavmeshToolMode(0, "Pathfind Follow");
|
||||
public static readonly RcTestNavmeshToolMode PATHFIND_STRAIGHT = new RcTestNavmeshToolMode(1, "Pathfind Straight");
|
||||
public static readonly RcTestNavmeshToolMode PATHFIND_SLICED = new RcTestNavmeshToolMode(2, "Pathfind Sliced");
|
||||
public static readonly RcTestNavmeshToolMode DISTANCE_TO_WALL = new RcTestNavmeshToolMode(3, "Distance to Wall");
|
||||
public static readonly RcTestNavmeshToolMode RAYCAST = new RcTestNavmeshToolMode(4, "Raycast");
|
||||
public static readonly RcTestNavmeshToolMode FIND_POLYS_IN_CIRCLE = new RcTestNavmeshToolMode(5, "Find Polys in Circle");
|
||||
public static readonly RcTestNavmeshToolMode FIND_POLYS_IN_SHAPE = new RcTestNavmeshToolMode(6, "Find Polys in Shape");
|
||||
public static readonly RcTestNavmeshToolMode FIND_LOCAL_NEIGHBOURHOOD = new RcTestNavmeshToolMode(7, "Find Local Neighbourhood");
|
||||
public static readonly RcTestNavmeshToolMode RANDOM_POINTS_IN_CIRCLE = new RcTestNavmeshToolMode(8, "Random Points in Circle");
|
||||
|
||||
public static readonly RcImmutableArray<RcTestNavmeshToolMode> Values = RcImmutableArray.Create(
|
||||
PATHFIND_FOLLOW,
|
||||
PATHFIND_STRAIGHT,
|
||||
PATHFIND_SLICED,
|
||||
DISTANCE_TO_WALL,
|
||||
RAYCAST,
|
||||
FIND_POLYS_IN_CIRCLE,
|
||||
FIND_POLYS_IN_SHAPE,
|
||||
FIND_LOCAL_NEIGHBOURHOOD,
|
||||
RANDOM_POINTS_IN_CIRCLE
|
||||
);
|
||||
|
||||
|
||||
public readonly int Idx;
|
||||
public readonly string Label;
|
||||
|
||||
private RcTestNavmeshToolMode(int idx, string label)
|
||||
{
|
||||
Idx = idx;
|
||||
Label = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4cc1deb471884a9d86a72ad13c7ddca5
|
||||
timeCreated: 1715335343
|
||||
@@ -0,0 +1,154 @@
|
||||
using System.Linq;
|
||||
using DotRecast.Core;
|
||||
using DotRecast.Core.Collections;
|
||||
using DotRecast.Core.Numerics;
|
||||
using DotRecast.Detour;
|
||||
using DotRecast.Recast.Geom;
|
||||
using DotRecast.Recast.Toolset.Builder;
|
||||
using JNGame.Math;
|
||||
|
||||
namespace DotRecast.Recast.Toolset.Tools
|
||||
{
|
||||
public class RcTileTool : IRcToolable
|
||||
{
|
||||
public string GetName()
|
||||
{
|
||||
return "Tiles";
|
||||
}
|
||||
|
||||
public void RemoveAllTiles(IInputGeomProvider geom, RcNavMeshBuildSettings settings, DtNavMesh navMesh)
|
||||
{
|
||||
if (null == settings || null == geom || navMesh == null)
|
||||
return;
|
||||
|
||||
var bmin = geom.GetMeshBoundsMin();
|
||||
var bmax = geom.GetMeshBoundsMax();
|
||||
int gw = 0, gh = 0;
|
||||
RcCommons.CalcGridSize(bmin, bmax, settings.cellSize, out gw, out gh);
|
||||
|
||||
int ts = settings.tileSize;
|
||||
int tw = (gw + ts - 1) / ts;
|
||||
int th = (gh + ts - 1) / ts;
|
||||
|
||||
for (int y = 0; y < th; ++y)
|
||||
{
|
||||
for (int x = 0; x < tw; ++x)
|
||||
{
|
||||
var tileRef = navMesh.GetTileRefAt(x, y, 0);
|
||||
navMesh.RemoveTile(tileRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildAllTiles(IInputGeomProvider geom, RcNavMeshBuildSettings settings, DtNavMesh navMesh)
|
||||
{
|
||||
if (null == settings || null == geom || navMesh == null)
|
||||
return;
|
||||
|
||||
var bmin = geom.GetMeshBoundsMin();
|
||||
var bmax = geom.GetMeshBoundsMax();
|
||||
int gw = 0, gh = 0;
|
||||
RcCommons.CalcGridSize(bmin, bmax, settings.cellSize, out gw, out gh);
|
||||
|
||||
int ts = settings.tileSize;
|
||||
int tw = (gw + ts - 1) / ts;
|
||||
int th = (gh + ts - 1) / ts;
|
||||
|
||||
for (int y = 0; y < th; ++y)
|
||||
{
|
||||
for (int x = 0; x < tw; ++x)
|
||||
{
|
||||
BuildTile(geom, settings, navMesh, x, y, out var tileBuildTicks, out var tileTriCount, out var tileMemUsage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool BuildTile(IInputGeomProvider geom, RcNavMeshBuildSettings settings, DtNavMesh navMesh, int tx, int ty, out long tileBuildTicks, out int tileTriCount, out int tileMemUsage)
|
||||
{
|
||||
tileBuildTicks = 0;
|
||||
tileTriCount = 0; // ...
|
||||
tileMemUsage = 0; // ...
|
||||
|
||||
var availableTileCount = navMesh.GetAvailableTileCount();
|
||||
if (0 >= availableTileCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RcVec3f bmin = geom.GetMeshBoundsMin();
|
||||
RcVec3f bmax = geom.GetMeshBoundsMax();
|
||||
|
||||
RcConfig cfg = new RcConfig(
|
||||
true, settings.tileSize, settings.tileSize,
|
||||
RcConfig.CalcBorder(settings.agentRadius, settings.cellSize),
|
||||
RcPartitionType.OfValue(settings.partitioning),
|
||||
settings.cellSize, settings.cellHeight,
|
||||
settings.agentMaxSlope, settings.agentHeight, settings.agentRadius, settings.agentMaxClimb,
|
||||
settings.minRegionSize * settings.minRegionSize * settings.cellSize * settings.cellSize,
|
||||
settings.mergedRegionSize * settings.mergedRegionSize * settings.cellSize * settings.cellSize,
|
||||
settings.edgeMaxLen, settings.edgeMaxError,
|
||||
settings.vertsPerPoly,
|
||||
settings.detailSampleDist, settings.detailSampleMaxError,
|
||||
settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans,
|
||||
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true
|
||||
);
|
||||
|
||||
var beginTick = RcFrequency.Ticks;
|
||||
var rb = new RcBuilder();
|
||||
var result = rb.BuildTile(geom, cfg, bmin, bmax, tx, ty, new RcAtomicInteger(0), 1, settings.keepInterResults);
|
||||
|
||||
var tb = new TileNavMeshBuilder();
|
||||
var meshData = tb.BuildMeshData(geom, settings.cellSize, settings.cellHeight, settings.agentHeight, settings.agentRadius, settings.agentMaxClimb, RcImmutableArray.Create(result)
|
||||
).FirstOrDefault();
|
||||
|
||||
if (null == meshData)
|
||||
return false;
|
||||
|
||||
navMesh.UpdateTile(meshData, 0);
|
||||
|
||||
tileBuildTicks = RcFrequency.Ticks - beginTick;
|
||||
tileTriCount = 0; // ...
|
||||
tileMemUsage = 0; // ...
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool BuildTile(IInputGeomProvider geom, RcNavMeshBuildSettings settings, DtNavMesh navMesh, RcVec3f pos, out long tileBuildTicks, out int tileTriCount, out int tileMemUsage)
|
||||
{
|
||||
tileBuildTicks = 0;
|
||||
tileTriCount = 0;
|
||||
tileMemUsage = 0;
|
||||
|
||||
if (null == settings || null == geom || navMesh == null)
|
||||
return false;
|
||||
|
||||
LFloat ts = settings.tileSize * settings.cellSize;
|
||||
|
||||
RcVec3f bmin = geom.GetMeshBoundsMin();
|
||||
RcVec3f bmax = geom.GetMeshBoundsMax();
|
||||
|
||||
int tx = (int)((pos.X - bmin.X) / ts);
|
||||
int ty = (int)((pos.Z - bmin.Z) / ts);
|
||||
|
||||
return BuildTile(geom, settings, navMesh, tx, ty, out tileBuildTicks, out tileTriCount, out tileMemUsage);
|
||||
}
|
||||
|
||||
public bool RemoveTile(IInputGeomProvider geom, RcNavMeshBuildSettings settings, DtNavMesh navMesh, RcVec3f pos)
|
||||
{
|
||||
if (null == settings || null == geom || navMesh == null)
|
||||
return false;
|
||||
|
||||
LFloat ts = settings.tileSize * settings.cellSize;
|
||||
|
||||
var bmin = geom.GetMeshBoundsMin();
|
||||
|
||||
int tx = (int)((pos.X - bmin.X) / ts);
|
||||
int ty = (int)((pos.Z - bmin.Z) / ts);
|
||||
|
||||
var tileRef = navMesh.GetTileRefAt(tx, ty, 0);
|
||||
navMesh.RemoveTile(tileRef);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a15a9b280014d299c6cb19f4f6a2dab
|
||||
timeCreated: 1715335343
|
||||
Reference in New Issue
Block a user