提交Unity 联机Pro

This commit is contained in:
PC-20230316NUNE\Administrator
2024-08-17 14:27:18 +08:00
parent f00193b000
commit 894100ae37
7448 changed files with 854473 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6ba5c48ac57e41c4b498837ce26419cb
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2849d98e96f6407b89a8f0e701552aa6
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f910a46b20cc4261860037c31fba5c1a
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: abb7b7cb546b46df8d1364c0d6f31dc2
timeCreated: 1715335343

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8922873adf3048f9bbffeb83a88eaeff
timeCreated: 1715335343

View File

@@ -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 };
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ddccefd2cfb44e858c36ce544be031a4
timeCreated: 1715335343

View File

@@ -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>

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3946930b95f34dd7ace5692becf67638
timeCreated: 1715335343

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c626ab86d0764a7b8bd16576de7dd02b
timeCreated: 1715335342

View File

@@ -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;
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 071514546bb84f8bb0233a1ac5aee2a9
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e0e8bcf210ea4a3a8aec33e63564ce5c
timeCreated: 1715335342

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9fad519022e416ba038bd61e310c7cd
timeCreated: 1715335343

View File

@@ -0,0 +1,9 @@
//using DotRecast.Recast.Demo.Draw;
namespace DotRecast.Recast.Toolset.Gizmos
{
public interface IRcGizmoMeshFilter
{
//void Render(RecastDebugDraw debugDraw);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 826e29d9df714f8f87508c6ee870ed5f
timeCreated: 1715335343

View File

@@ -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;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: def9df06e44649748bbbf43b1b112f29
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b62a64e8a3fb4f4694c3dd0a0b5e3df2
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 53f7c76765ff4c0c88ec5998cd37b3a1
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 854d10a9699747fbb5bd897550f85196
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: beb914a0811c43c38ae4506df092c0ef
timeCreated: 1715335343

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 235008794faa49de9c3e8bd9cbe4212a
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cfc2fd59a92e453c95d0443df71f874b
timeCreated: 1715335343

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ac5867af32fd4b7d9d5679f17dd6aa60
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 92b619575a6e4b4eb0df8058246e23e7
timeCreated: 1715335343

View File

@@ -0,0 +1,7 @@
namespace DotRecast.Recast.Toolset
{
public interface IRcToolable
{
string GetName();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f160410c11d0475ca05a21fd4e159c41
timeCreated: 1715335343

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ecae0b7be48b41ad878f46474e87975e
timeCreated: 1715335343

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 722ad149982446f69458071c3e88a63f
timeCreated: 1715335343

View File

@@ -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,
};
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38041515b069400da717e8e11dfc6d0d
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e29114d81db34169a648361713f4b03b
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 12ffdc5a9b23438cb35434ed0012fb8a
timeCreated: 1715335343

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e96fe090b86f444cb250f7ab8bb69896
timeCreated: 1715335343

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 97d72af6d37d4440a149e5f73524c6c5
timeCreated: 1715335343

View File

@@ -0,0 +1,9 @@
namespace DotRecast.Recast.Toolset.Tools
{
public enum RcCrowdAgentType
{
VILLAGER,
TRAVELLER,
MOB,
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ba21b8baf51e4986bab9361c63189ffa
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 995c2e478cec460e81e3bbd7b380c6c7
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb6f1cde2fb84788bd4f4a11596436f5
timeCreated: 1715335343

View File

@@ -0,0 +1,14 @@
namespace DotRecast.Recast.Toolset.Tools
{
public enum RcDynamicColliderShape
{
SPHERE,
CAPSULE,
BOX,
CYLINDER,
COMPOSITE,
CONVEX,
TRIMESH_BRIDGE,
TRIMESH_HOUSE
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b7a63250b009403180f96ed08d9cf3d2
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 01b6d8138cd24d828ba4c1a4a6fe9869
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8633fd681b6645a185859363dd49eb85
timeCreated: 1715335343

View File

@@ -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;
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 066530f8c31e44aa8101611ae7c382eb
timeCreated: 1715335343

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3c5b517440744444a2f20dacc6338d3c
timeCreated: 1715335343

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ac22a9a64e9c487da2c9cfd548f78723
timeCreated: 1715335343

View File

@@ -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);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8faec19cd36a4e3486f0eb0de76d2883
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 805d68bb90da4a549eaf3e1051a94eaa
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4cc1deb471884a9d86a72ad13c7ddca5
timeCreated: 1715335343

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7a15a9b280014d299c6cb19f4f6a2dab
timeCreated: 1715335343